Méthode optimale pour évaluer une fonction sur de nombreux points
Ce piggy-backs sur ma question précédente: la routine de recherche d'espace de paramètres est trop rapide?
Je recherche un moyen rapide d'évaluer une liste symbolique sur de nombreux points. Alors disons que j'ai une liste d'expressions symboliques comme
ListA={a*b*c>0, a*b*(c+1)>0, a*b*(c-1)>0, etc.}
et une liste de tuples de la forme
ListB={{1,1,1}, {1,1,2}, {1,2,1}, {1,2,2}< etc.}
et je veux évaluer ListA sur chaque tuple de ListB comme
ListA/.Thread[{a,b,c} -> ListB[[1]]]
ListA /.Thread[{a,b,c} -> ListB[[2]]]
Maintenant, ma listeA peut avoir plus de dizaines de milliers de points et chaque expression peut dépasser une centaine de lignes. Ma ListB peut aussi être gigantesque, comme plus de dizaines de millions de points, mais chaque tuple n'a que ~ 5 éléments et je l'ai découpé en tailles d'environ 100-1000 tuples. Ma question est alors quelle serait la meilleure façon d'effectuer rapidement ce type de remplacements / d'association?
Ma première tentative a été utilisée ParallelMap
mais cela a encore pris des années. Ensuite, j'ai regardé Associations
et cela a réduit le temps, mais chaque remplacement d'un élément de ListB prend encore 1,5 à 2 secondes, ce que je dois réduire considérablement. Voici un MWE pour référence:
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*)
Maintenant, évaluer ListA
plus ListB
se déroulerait comme
(AllTrue[ListA /. Thread[parameters -> #], TrueQ]) & /@ ListB
(*Careful running this, it will probably take a few months :( *)
Mon problème est que même une seule association comme
ListA/.Thread[parameters->{1,1,1,1,1,1,1}]
prend environ 2 secondes. Donc, répéter cela sur une liste d'environ 2 millions de points prendrait un siècle.
Une fonction compilée serait-elle utile? Je n'ai pas beaucoup d'expérience dans l'utilisation de la fonctionnalité de compilation, donc je me demande s'il serait avantageux d'explorer cela. J'apprécie toute perspicacité!
Mettre à jour
Grâce à la suggestion @flinty, l'utilisation With
semble accélérer considérablement la tâche. Voici une courte expérience de synchronisation:
Ici, QuantifieroverSpace
correspond à ListA
dans le MWE ci-dessus.
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}*)
Donc, utiliser With
au lieu de ReplaceAll
est beaucoup plus rapide de plusieurs ordres de grandeur, ce qui est intéressant. Je vais l'implémenter dans ma routine de recherche et voir à quel point cela l'améliore.
Mise à jour 2
Donc, mon prochain problème est que j'ai besoin que le premier argument de With
soit modulaire au nombre d'arguments, c'est-à-dire qu'il doit être capable de prendre soit un ensemble de 3 variables comme {a = 1, b = 1, c = 1} ou un nombre différent comme {a = 1}. Mon premier serait de faire quelque chose comme
With[
{Thread[SymbolList = ArrayofValues]},
...
]
mais Mathematica attribue les valeurs ArrayofValues
aux symboles SymbolList
afin que la variable a
par exemple ait la valeur 1. J'ai ensuite essayé
init = MapThread[HoldForm[#1=#2]&, {SymbolList, ArrayofValues}];
With[
Evaluate@ReleaseHold[init],
...
]
mais cela fait la même chose, en attribuant les valeurs aux symboles. Fait intéressant, Mathematica exécute toujours l' with
expression en utilisant les valeurs du premier argument, mais il affecte toujours la valeur au symbole, ce qui ralentirait l'exécution de ma routine de recherche si je voulais annuler l'affectation. Je dois en quelque sorte arrêter l' Set
affectation d'ingénierie tout en conservant le formulaire a=1
d'une manière dynamique par rapport au nombre de variables.
Mise à jour 3
Eh bien, après une inspection plus approfondie, j'ai compris pourquoi With
semble être beaucoup plus rapide. C'est parce qu'il ne remplace pas réellement les valeurs du premier argument dans l'expression. Par example,
a = {l, s};
With[{l = 3, s = 12},
Print[Evaluate[a]]
];
(*{l,s}*)
Je suppose donc que je suis de retour à la case départ en essayant de trouver un moyen plus rapide d'attribuer des valeurs aux paramètres à l'intérieur d'un grand tableau symbolique.
Réponses
Ce n'est qu'une réponse partielle, mais ...
Votre fonction est assez compliquée, et l'accélérer est probablement plus important que la façon dont vous lui donnez des valeurs. Compileest votre ami ici.
cfunc = Compile @@ {Join[variables, parameters], func,
CompilationTarget -> "C", "RuntimeOptions" -> "Speed",
RuntimeAttributes -> {Listable}}
RepeatedTiming[AllTrue[cfunc @@ Join[SpaceA // Transpose, ListB[[1]]], Positive]]
{0.0051, False}
À 5 ms pour une seule rangée de ListB
, cela prendra encore beaucoup de temps, même si cela devient plus réaliste.