Optymalny sposób oceny funkcji w wielu punktach
To nawiązuje do mojego poprzedniego pytania: Procedura wyszukiwania w przestrzeni parametrów jest zbyt szybka?
Szukam szybkiego sposobu oceny symbolicznej listy w wielu punktach. Powiedzmy więc, że mam listę wyrażeń symbolicznych, takich jak
ListA={a*b*c>0, a*b*(c+1)>0, a*b*(c-1)>0, etc.}
i lista krotek formularza
ListB={{1,1,1}, {1,1,2}, {1,2,1}, {1,2,2}< etc.}
i chcę ocenić ListA na każdej krotce ListB, jak
ListA/.Thread[{a,b,c} -> ListB[[1]]]
ListA /.Thread[{a,b,c} -> ListB[[2]]]
Teraz moja listaA może mieć dziesiątki tysięcy punktów w górę, a każde wyrażenie może mieć ponad sto linii. Moje ListB może być również gigantyczne, na przykład kilkadziesiąt milionów punktów, ale każda krotka ma tylko ~ 5 elementów i podzieliłem ją na rozmiary około 100-1000 krotek. Moje pytanie brzmi zatem, jaki byłby najlepszy sposób na szybkie wykonanie tego typu zastępstw / skojarzeń?
Moja pierwsza próba była używana, ParallelMap
ale trwało to jeszcze wieki. Potem zajrzałem Associations
i to skróciło czas, ale każda wymiana elementu ListB nadal trwa około 1,5 - 2 sekund, które muszę znacznie skrócić. Oto MWE w celach informacyjnych:
func = (-2^(1 - px) (-1 + px) px Coth[
rx sx]^2 (-2 sx y Sech[sx (-rx + x^2 + y^2)]^2 +
2 sx y Sech[sx (rx + x^2 + y^2)]^2)^2 (Coth[
rx sx] (-Tanh[sx (-rx + x^2 + y^2)] +
Tanh[sx (rx + x^2 + y^2)]))^(-2 + px) -
2^(1 - px) px Coth[
rx sx] (Coth[
rx sx] (-Tanh[sx (-rx + x^2 + y^2)] +
Tanh[sx (rx + x^2 + y^2)]))^(-1 +
px) (-2 sx Sech[sx (-rx + x^2 + y^2)]^2 +
2 sx Sech[sx (rx + x^2 + y^2)]^2 +
8 sx^2 y^2 Sech[sx (-rx + x^2 + y^2)]^2 Tanh[
sx (-rx + x^2 + y^2)] -
8 sx^2 y^2 Sech[sx (rx + x^2 + y^2)]^2 Tanh[
sx (rx + x^2 + y^2)]) +
2^-px (-1 + px) px Coth[
rx sx]^2 (-2 sx y Sech[sx (-R - rx + x^2 + y^2)]^2 +
2 sx y Sech[sx (-R + rx + x^2 + y^2)]^2)^2 (Coth[
rx sx] (-Tanh[sx (-R - rx + x^2 + y^2)] +
Tanh[sx (-R + rx + x^2 + y^2)]))^(-2 + px) +
2^-px px Coth[
rx sx] (Coth[
rx sx] (-Tanh[sx (-R - rx + x^2 + y^2)] +
Tanh[sx (-R + rx + x^2 + y^2)]))^(-1 +
px) (-2 sx Sech[sx (-R - rx + x^2 + y^2)]^2 +
2 sx Sech[sx (-R + rx + x^2 + y^2)]^2 +
8 sx^2 y^2 Sech[sx (-R - rx + x^2 + y^2)]^2 Tanh[
sx (-R - rx + x^2 + y^2)] -
8 sx^2 y^2 Sech[sx (-R + rx + x^2 + y^2)]^2 Tanh[
sx (-R + rx + x^2 + y^2)]) +
2^-px (-1 + px) px Coth[
rx sx]^2 (-2 sx y Sech[sx (R - rx + x^2 + y^2)]^2 +
2 sx y Sech[sx (R + rx + x^2 + y^2)]^2)^2 (Coth[
rx sx] (-Tanh[sx (R - rx + x^2 + y^2)] +
Tanh[sx (R + rx + x^2 + y^2)]))^(-2 + px) +
2^-px px Coth[
rx sx] (Coth[
rx sx] (-Tanh[sx (R - rx + x^2 + y^2)] +
Tanh[sx (R + rx + x^2 + y^2)]))^(-1 +
px) (-2 sx Sech[sx (R - rx + x^2 + y^2)]^2 +
2 sx Sech[sx (R + rx + x^2 + y^2)]^2 +
8 sx^2 y^2 Sech[sx (R - rx + x^2 + y^2)]^2 Tanh[
sx (R - rx + x^2 + y^2)] -
8 sx^2 y^2 Sech[sx (R + rx + x^2 + y^2)]^2 Tanh[
sx (R + rx + x^2 + y^2)]));
parameters = {px, pz, R, rx, rz, sx, sz}
variables = {x, y, z}
Quantifier[coords_, params_] :=
Function[Evaluate@Join[variables, parameters], Evaluate@(func > 0)][
Sequence @@ Join[coords, params]]
SpaceA = Tuples[Range[-2, 2, 0.2], 3];
ListA = Quantifier[#1, parameters] & /@ SpaceA;
ListB = Tuples[Range[1, 4, 0.4], 7];
(*ListB contains~2 million elements*)
Teraz ocena ListA
zakończyłaby się ListB
tak, jak
(AllTrue[ListA /. Thread[parameters -> #], TrueQ]) & /@ ListB
(*Careful running this, it will probably take a few months :( *)
Mój problem polega na tym, że choćby jedno takie skojarzenie
ListA/.Thread[parameters->{1,1,1,1,1,1,1}]
trwa około 2 sekund. Zatem powtórzenie tego na liście ~ 2 milionów punktów zajęłoby sto lat.
Czy skompilowana funkcja byłaby przydatna? Nie mam dużego doświadczenia w korzystaniu z funkcji kompilacji, więc zastanawiam się, czy warto byłoby to zbadać. Doceniam każdy wgląd!
Aktualizacja
Dzięki sugestii @flinty użycie With
wydaje się znacznie przyspieszyć zadanie. Oto krótki eksperyment czasowy:
Tutaj QuantifieroverSpace
odpowiada ListA
w MWE powyżej.
ClearAll[\[Epsilon], px, pz, R, rx, rz, sx, sz]
ByteCount[QuantifieroverSpace]
With[{\[Epsilon] = 2, px = 1, pz = 5, R = 1, rx = 2, rz = 2, sx = 2,
sz = 2},
Evaluate@AllTrue[QuantifieroverSpace, TrueQ]] // Timing
AllTrue[QuantifieroverSpace /.
Thread[{\[Epsilon], px, pz, R, rx, rz, sx, sz} -> {2, 1, 5, 1, 2,
2, 2, 2}], TrueQ] // Timing
(*126992696*)
(*{0.000026, False}*)
(*{2.08846, False}*)
Tak więc użycie With
zamiast ReplaceAll
jest o wiele rzędów wielkości szybsze, co jest interesujące. Zaimplementuję to w mojej rutynie wyszukiwania i zobaczę, jak bardzo to poprawi.
Zaktualizuj 2
Więc moim następnym problemem jest to, że potrzebuję, aby pierwszy argument z programu With
był modularny w stosunku do liczby argumentów, tj. Musi być w stanie przyjąć zestaw 3 zmiennych, taki jak {a = 1, b = 1, c = 1} lub inną liczbę, na przykład {a = 1}. Najpierw jednak chciałbym zrobić coś takiego
With[
{Thread[SymbolList = ArrayofValues]},
...
]
ale mathematica przypisuje wartości w ArrayofValues
symbolach w SymbolList
tak, że a
na przykład zmienna ma wartość 1. Następnie spróbowałem
init = MapThread[HoldForm[#1=#2]&, {SymbolList, ArrayofValues}];
With[
Evaluate@ReleaseHold[init],
...
]
ale to robi to samo, przypisując wartości do symboli. Co ciekawe, mathematica nadal wykonuje with
wyrażenie, używając wartości z pierwszego argumentu, ale nadal przypisuje wartość do symbolu, co spowolniłoby wykonywanie mojej procedury wyszukiwania, gdybym chciał cofnąć przypisanie. Muszę jakoś zatrzymać Set
przypisanie, ale nadal utrzymywać formę a=1
w sposób dynamiczny w stosunku do liczby zmiennych.
Zaktualizuj 3
Cóż, po dalszej inspekcji doszedłem do wniosku, dlaczego With
wydaje się być o wiele szybszy. Dzieje się tak, ponieważ w rzeczywistości nie zastępuje wartości pierwszego argumentu w wyrażeniu. Na przykład,
a = {l, s};
With[{l = 3, s = 12},
Print[Evaluate[a]]
];
(*{l,s}*)
Myślę więc, że wróciłem do punktu wyjścia, próbując znaleźć szybszy sposób przypisywania wartości do parametrów wewnątrz dużej tablicy symbolicznej.
Odpowiedzi
To tylko częściowa odpowiedź, ale ...
Twoja funkcja jest dość skomplikowana, a przyspieszenie jej jest prawdopodobnie ważniejsze niż sposób podawania do niej wartości. Compilejest tu twoim przyjacielem.
cfunc = Compile @@ {Join[variables, parameters], func,
CompilationTarget -> "C", "RuntimeOptions" -> "Speed",
RuntimeAttributes -> {Listable}}
RepeatedTiming[AllTrue[cfunc @@ Join[SpaceA // Transpose, ListB[[1]]], Positive]]
{0.0051, False}
Przy 5 ms dla pojedynczego rzędu ListB
nadal zajmie to dużo czasu, chociaż robi się bardziej realistyczne.