Оптимальный способ оценить функцию по многим точкам
Это ответ на мой предыдущий вопрос: процедура поиска в пространстве параметров работает слишком быстро?
Я ищу быстрый способ оценить символический список по многим пунктам. Итак, допустим, у меня есть список символических выражений, таких как
ListA={a*b*c>0, a*b*(c+1)>0, a*b*(c-1)>0, etc.}
и список кортежей вида
ListB={{1,1,1}, {1,1,2}, {1,2,1}, {1,2,2}< etc.}
и я хочу оценить ListA для каждого кортежа ListB, например
ListA/.Thread[{a,b,c} -> ListB[[1]]]
ListA /.Thread[{a,b,c} -> ListB[[2]]]
Теперь мой listA может иметь более десятков тысяч точек, а каждое выражение может содержать более сотни строк. Мой ListB также может быть огромным, например, до десятков миллионов точек, но каждый кортеж имеет только ~ 5 элементов, и я разбил его на размеры примерно по 100-1000 кортежей. Тогда у меня вопрос, как лучше всего быстро выполнить этот тип замен / ассоциаций?
Моя первая попытка использовалась, ParallelMap
но это все еще заняло много времени. Затем я посмотрел, Associations
и это сократило время, но каждая замена элемента ListB по-прежнему занимает 1,5–2 секунды, что мне нужно значительно сократить. Вот MWE для справки:
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*)
Теперь, оценивая ListA
более ListB
будет продолжаться как
(AllTrue[ListA /. Thread[parameters -> #], TrueQ]) & /@ ListB
(*Careful running this, it will probably take a few months :( *)
Моя проблема в том, что даже одна ассоциация вроде
ListA/.Thread[parameters->{1,1,1,1,1,1,1}]
занимает около 2 секунд. Таким образом, повторение этого над списком из ~ 2 миллионов точек заняло бы столетие.
Будет ли полезна скомпилированная функция? У меня нет большого опыта использования функций компиляции, поэтому мне интересно, было бы полезно изучить это. Я ценю любую проницательность!
Обновлять
Благодаря предложению @flinty использование, With
похоже, значительно ускоряет назначение. Вот небольшой эксперимент по времени:
Здесь QuantifieroverSpace
соответствует ListA
в приведенном выше MWE.
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}*)
Так что использование With
вместо на ReplaceAll
много порядков быстрее, что интересно. Я добавлю это в свою процедуру поиска и посмотрю, насколько это улучшит.
Обновление 2
Итак, моя следующая проблема заключается в том, что мне нужно, чтобы первый аргумент With
был модульным по отношению к количеству аргументов, то есть он должен иметь возможность принимать либо набор из трех переменных, например {a = 1, b = 1, c = 1}, либо другое число, например {a = 1}. Моя первая мысль была бы сделать что-нибудь вроде
With[
{Thread[SymbolList = ArrayofValues]},
...
]
но mathematica присваивает значения в ArrayofValues
символах в SymbolList
так, чтобы переменная, a
например, имела значение 1. Затем я попробовал
init = MapThread[HoldForm[#1=#2]&, {SymbolList, ArrayofValues}];
With[
Evaluate@ReleaseHold[init],
...
]
но это то же самое, присваивая значения символам. Интересно, что mathematica по-прежнему выполняет with
выражение, используя значения в первом аргументе, но по-прежнему присваивает значение символу, что замедлит выполнение моей процедуры поиска, если я захочу отменить присвоение. Мне нужно как-то остановить Set
присвоение ing, но при этом сохранить форму a=1
динамически в зависимости от количества переменных.
Обновление 3
При дальнейшем осмотре я понял, почему With
он работает намного быстрее. Это потому, что на самом деле в выражение не подставляются значения первого аргумента. Например,
a = {l, s};
With[{l = 3, s = 12},
Print[Evaluate[a]]
];
(*{l,s}*)
Итак, я думаю, что я вернулся на круги своя, пытаясь найти более быстрый способ присвоить значения параметрам внутри большого символьного массива.
Ответы
Это лишь частичный ответ, но ...
Ваша функция довольно сложна, и ее ускорение, вероятно, важнее, чем то, как вы вводите в нее значения. Compileтвой друг здесь.
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 мс для одной строки ListB
это все равно займет много времени, хотя это становится более реалистичным.