Optymalny sposób oceny funkcji w wielu punktach

Dec 12 2020

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, ParallelMapale trwało to jeszcze wieki. Potem zajrzałem Associationsi 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 ListAzakończyłaby się ListBtak, 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 Withwydaje się znacznie przyspieszyć zadanie. Oto krótki eksperyment czasowy:

Tutaj QuantifieroverSpaceodpowiada ListAw 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 Withzamiast ReplaceAlljest 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 Withbył 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 ArrayofValuessymbolach w SymbolListtak, że ana 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 withwyraż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ć Setprzypisanie, ale nadal utrzymywać formę a=1w sposób dynamiczny w stosunku do liczby zmiennych.

Zaktualizuj 3

Cóż, po dalszej inspekcji doszedłem do wniosku, dlaczego Withwydaje 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

1 SimonWoods Dec 13 2020 at 02:19

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 ListBnadal zajmie to dużo czasu, chociaż robi się bardziej realistyczne.