Optimale Möglichkeit, eine Funktion über viele Punkte hinweg zu bewerten

Dec 12 2020

Dies huckepack auf meine vorherige Frage: Die Parameterraum-Suchroutine ist zu schnell?

Ich suche nach einer schnellen Möglichkeit, eine symbolische Liste über viele Punkte hinweg zu bewerten. Nehmen wir also an, ich habe eine Liste symbolischer Ausdrücke wie

ListA={a*b*c>0, a*b*(c+1)>0, a*b*(c-1)>0, etc.}

und eine Liste von Tupeln des Formulars

ListB={{1,1,1}, {1,1,2}, {1,2,1}, {1,2,2}< etc.}

und ich möchte ListA über jedes Tupel von ListB wie bewerten

ListA/.Thread[{a,b,c} -> ListB[[1]]]
ListA /.Thread[{a,b,c} -> ListB[[2]]]

Jetzt kann meine Liste A mehr als Zehntausende Punkte haben und jeder Ausdruck kann mehr als hundert Zeilen umfassen. Mein ListB kann auch gigantisch sein, wie z. B. mehrere zehn Millionen Punkte, aber jedes Tupel enthält nur ~ 5 Elemente, und ich habe es in Größen von ungefähr 100-1000 Tupeln aufgeteilt. Meine Frage ist dann, was der beste Weg wäre, um diese Art von Ersetzungen / Assoziationen schnell durchzuführen.

Mein erster Versuch benutzte, ParallelMapaber das dauerte noch Ewigkeiten. Dann habe ich Associationsnachgesehen und dies hat die Zeit verkürzt, aber jeder Austausch eines Elements von ListB dauert immer noch 1,5 - 2 Sekunden, was ich erheblich verkürzen muss. Hier ist ein MWE als Referenz:

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*)

Nun Auswertung ListAüber ListBwürde gehen wie

(AllTrue[ListA /. Thread[parameters -> #], TrueQ]) & /@ ListB
(*Careful running this, it will probably take a few months :( *)

Mein Problem ist, dass sogar eine einzige Assoziation mag

ListA/.Thread[parameters->{1,1,1,1,1,1,1}]

dauert ca. 2 Sekunden. Es würde also ein Jahrhundert dauern, dies über eine Liste von ~ 2 Millionen Punkten zu wiederholen.

Wäre eine kompilierte Funktion nützlich? Ich habe nicht viel Erfahrung mit der Kompilierungsfunktion, daher frage ich mich, ob es vorteilhaft wäre, dies zu untersuchen. Ich freue mich über jeden Einblick!

Aktualisieren

Dank des Vorschlags von @flinty Withscheint die Verwendung die Zuweisung erheblich zu beschleunigen. Hier ist ein kurzes Timing-Experiment:

Hier QuantifieroverSpaceentspricht ListAin der MWE oben.

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}*)

Die Verwendung von Withanstelle von ReplaceAllist also um viele Größenordnungen schneller, was interessant ist. Ich werde dies in meine Suchroutine implementieren und sehen, um wie viel es es verbessert.

Update 2

Mein nächstes Problem ist also, dass das erste Argument Withmodular zur Anzahl der Argumente sein muss, dh es muss in der Lage sein, entweder eine 3-Variablen-Menge wie {a = 1, b = 1, c = 1} oder aufzunehmen eine andere Zahl wie {a = 1}. Mein erstes wäre jedoch, so etwas zu tun

With[
     {Thread[SymbolList = ArrayofValues]}, 
     ...
     ]

aber mathematica weist ArrayofValuesden Symbolen in die Werte zu, SymbolListso dass die Variable azum Beispiel den Wert 1 hat. Ich habe es dann versucht

init = MapThread[HoldForm[#1=#2]&, {SymbolList, ArrayofValues}];
With[
     Evaluate@ReleaseHold[init],
     ...
     ]

Dies geschieht jedoch genauso, indem den Symbolen die Werte zugewiesen werden. Interessanterweise führt mathematica den withAusdruck immer noch mit den Werten im ersten Argument aus, weist jedoch den Wert dem Symbol zu, was die Ausführung meiner Suchroutine verlangsamen würde, wenn ich die Zuordnung rückgängig machen möchte. Ich muss die SetZuweisung irgendwie anhalten, aber das Formular trotzdem a=1dynamisch für die Anzahl der Variablen beibehalten .

Update 3

Nun, bei weiterer Betrachtung fand ich heraus, warum Withes so viel schneller zu sein scheint. Dies liegt daran, dass die Werte des ersten Arguments nicht tatsächlich in den Ausdruck eingefügt werden. Beispielsweise,

a = {l, s};
With[{l = 3, s = 12},
  Print[Evaluate[a]]
  ];

(*{l,s}*)

Ich bin also wohl wieder auf dem ersten Platz und versuche, einen schnelleren Weg zu finden, um Parametern in einem großen symbolischen Array Werte zuzuweisen.

Antworten

1 SimonWoods Dec 13 2020 at 02:19

Dies ist nur eine teilweise Antwort, aber ...

Ihre Funktion ist ziemlich kompliziert, und es ist wahrscheinlich wichtiger, diese zu beschleunigen, als wie Sie ihr Werte zuführen. Compileist dein Freund hier.

cfunc = Compile @@ {Join[variables, parameters], func, 
   CompilationTarget -> "C", "RuntimeOptions" -> "Speed", 
   RuntimeAttributes -> {Listable}}

RepeatedTiming[AllTrue[cfunc @@ Join[SpaceA // Transpose, ListB[[1]]], Positive]]
{0.0051, False}

Bei 5 ms für eine einzelne Reihe ListBwird es immer noch lange dauern, obwohl es realistischer wird.