Я запускаю функцию таблицы, которая займет слишком много времени.
Я хотел знать, есть ли способ получить результаты, рассчитанные до сих пор.
Я запускаю функцию таблицы, которая займет слишком много времени.
Я хотел знать, есть ли способ получить результаты, рассчитанные до сих пор.
Вот версия Table
, которая Abort
-able, и сохранит промежуточные результаты, собранные до сих пор. Это измененная версия размещенного решения здесь.
ClearAll[abortableTable];
SetAttributes[abortableTable, HoldAll];
abortableTable[expr_, iter__List] :=
Module[{indices, indexedRes, sowTag},
SetDelayed @@
Prepend[Thread[Map[Take[#, 1] &, List @@ Hold @@@ Hold[iter]],
Hold], indices];
indexedRes =
If[# === {}, #, [email protected]#] &@[email protected][
CheckAbort[Do[Sow[{expr, indices}, sowTag], iter], {}], sowTag];
AbortProtect[
Map[First,
SplitBy[indexedRes,
Table[
With[{i = i}, Function[Slot[1][[2, i]]]],
{i, Length[Hold[iter]] - 1}]],
{-3}]]];
Он должен иметь возможность использовать те же спецификации итератора, что и Table
.
Вот как это работает. Первое утверждение (SetDelayed @@...
) "анализирует" итераторы, предполагая, что они являются каждой из форм {iteratorSymbol_,bounds__}
, и присваивает список переменных итератора переменной indices
. Конструкция с Hold
необходима для предотвращения возможной оценки переменных итератора. Есть много способов сделать это, я использовал только один из них. Вот как это работает:
In[44]:=
{i, j, k} = {1, 2, 3};
Prepend[Thread[Map[Take[#, 1] &, List @@ Hold @@@
Hold[{i, 1, 10}, {j, 1, 5}, {k, 1, 3}]], Hold], indices]
Out[45]= Hold[indices, {i, j, k}]
Используя SetDelayed @@ the-above
, естественно, вы получите отложенное определение формы indices:={i,j,k}
. Я присвоил значения индексам i,j,k
, чтобы продемонстрировать, что при использовании этой конструкции не возникает никакой нежелательной оценки.
Следующий оператор создает список собранных результатов, где каждый результат группируется в списке со списком индексов, используемых для его создания. Поскольку переменная indices
определяется заданием с задержкой, она будет оценивать каждый раз заново, для новой комбинации индексов. Еще одна важная функция, используемая здесь, заключается в том, что цикл Do
принимает тот же синтаксис итератора, что и Table
(а также динамически локализует переменные итератора), будучи последовательной (постоянной памятью). Для сбора промежуточных результатов использовались Reap
и Sow
. Поскольку expr
может быть любой частью кода и может, в частности, также использовать Sow
, пользовательский тег с уникальным именем необходим только для Reap
тех значений, которые были Sown
по нашей функции, но не кода он выполняется. Поскольку Module
естественно создает (временные) символы с уникальным именем, я просто использовал переменную Module
сгенерированную без значения в качестве тега. Это обычно полезный метод.
Чтобы иметь возможность собирать результаты в случае Abort[]
, выпущенные пользователем интерактивно или в коде, мы завершаем цикл Do
в CheckAbort
. Код, который выполняется в Abort[]
({}
здесь), во многом подобен этому подходу, поскольку сбор результатов в любом случае выполняется с помощью Sow
и Reap
, хотя может быть полезен в более сложной версии, которая сохраните результат в некоторой переменной, предоставленной пользователем, а затем повторно опубликуйте Abort[]
(функциональность, которая в настоящее время не реализована).
В результате мы перейдем к переменной indexedRes
плоскому списку формы
{{expr1, {ind11,ind21,...indn1}},...,{exprk, {ind1k,ind2k,...indnk}}
где результаты группируются с соответствующей комбинацией индексов. Нам нужны эти индексные комбинации для восстановления многомерного результирующего списка из плоского списка. Способ сделать это состоит в том, чтобы повторно разбить список в соответствии со значением i
-th index. Функция SplitBy
имеет эту функциональность, но нам нужно предоставить список функций, которые будут использоваться для разделения шагов. Так как индекс i
-го индекса итератора в подслове {expr,{ind1,...,indn}}
равен 2,i
, то функция, выполняющая расщепление на i
-th step, равна #[[2, i]]&
, и нам нужно построить список таких функций динамически, чтобы подать его на SplitBy
. Вот пример:
In[46]:= Table[With[{i = i}, Function[Slot[1][[2, i]]]], {i, 5}]
Out[46]= {#1[[2, 1]] &, #1[[2, 2]] &, #1[[2, 3]] &, #1[[2, 4]] &, #1[[2, 5]] &}
Конструкция With[{i=i},body]
использовалась для ввода конкретных значений i
внутри чистых функций. Альтернативы для ввода значения i
в Function
существуют, например, например:
In[75]:=
Function[Slot[1][[2, i]]] /. Map[List, Thread[HoldPattern[i] -> Range[5]]]
Out[75]= {#1[[2, 1]] &, #1[[2, 2]] &, #1[[2, 3]] &, #1[[2, 4]] &, #1[[2, 5]] &}
или
In[80]:= Block[{Part}, Function /@ Thread[Slot[1][[2, Range[5]]]]]
Out[80]= {#1[[2, 1]] &, #1[[2, 2]] &, #1[[2, 3]] &, #1[[2, 4]] &, #1[[ 2, 5]] &}
или
In[86]:= Replace[Table[{2, i}, {i, 5}], {inds__} :> (#[[inds]] &), 1]
Out[86]= {#1[[2, 1]] &, #1[[2, 2]] &, #1[[2, 3]] &, #1[[2, 4]] &, #1[[ 2, 5]] &}
но, вероятно, еще более неясны (возможно, кроме последнего).
Полученный вложенный список имеет правильную структуру, а подсети {expr,{ind1,...,indn}}
находятся на уровне -3
(третий уровень снизу). Используя Map[First,lst,{-3}]
, мы удаляем комбинации индексов, так как вложенный список уже реконструирован, и они больше не нужны. Остается наш результат - вложенный список полученных выражений, структура которых соответствует структуре аналогичного вложенного списка, созданного Table
. Последний оператор завернут в AbortProtect
- на всякий случай, чтобы убедиться, что результат возвращается до возможного пожара Abort[]
.
Вот пример, когда я нажал Alt+.
(Abort[]
) вскоре после вычисления команды:
In[133]:= abortableTable[N[(1+1/i)^i],{i,20000}]//Short
Out[133]//Short= {2.,2.25,2.37037,2.44141,<<6496>>,2.71807,2.71807,2.71807}
Это почти так же быстро, как Table
:
In[132]:= abortableTable[N[(1+1/i)^i,20],{i,10000}]//Short//Timing
Out[132]= {1.515,{2.0000000000000000000,2.2500000000000000000,<<9997>>,2.7181459268252248640}}
In[131]:= Table[N[(1+1/i)^i,20],{i,10000}]//Short//Timing
Out[131]= {1.5,{2.0000000000000000000,2.2500000000000000000,<<9997>>,2.7181459268252248640}}
Но он не автокомпилируется, а Table
делает:
In[134]:= Table[N[(1+1/i)^i],{i,10000}]//Short//Timing
Out[134]= {0.,{2.,2.25,2.37037,2.44141,<<9993>>,2.71815,2.71815,2.71815}}
Можно запрограммировать автоматическую компиляцию и добавить ее к вышеуказанному решению, я просто не делал этого, так как будет очень много работы, чтобы сделать это правильно.
ИЗМЕНИТЬ
Я переписал функцию, чтобы сделать некоторые части более краткими и понятными. Также, это примерно на 25% быстрее, чем первая версия, в больших списках.
ClearAll[abortableTableAlt];
SetAttributes[abortableTableAlt, HoldAll];
abortableTableAlt[expr_, iter : {_Symbol, __} ..] :=
Module[{indices, indexedRes, sowTag, depth = Length[Hold[iter]] - 1},
Hold[iter] /. {sym_Symbol, __} :> sym /. Hold[syms__] :> (indices := {syms});
indexedRes = Replace[#, {x_} :> x] &@ [email protected]Reap[
CheckAbort[Do[Sow[{expr, indices}, sowTag], iter], Null],sowTag];
AbortProtect[
SplitBy[indexedRes, Array[Function[x, #[[2, x]] &], {depth}]][[##,1]] & @@
Table[All, {depth + 1}]
]];
К сожалению нет. Если вы хотите сделать что-то вроде lst=Table[f[i],{i,1,10000}]
, чтобы, если вы прервали вас, у вас все еще есть результаты, вы можете сделать
Clear[lst2];
lst2 = {};
(Do[lst2 = {lst2, f[i]}, {i, 1, 10000}];
lst2=Flatten[lst2];) // Timing
который для undefined f
занимает 0.173066 с на моей машине, а lst = Table[f[i], {i, 1, 100000}]
занимает примерно 0,06 с (т.е. Table
он в 3 раза быстрее за счет того, что он не прерывается).
Обратите внимание, что очевидное "прерывистое" решение lst = {};
Do[AppendTo[lst, f[i]], {i, 1, 100000}]
занимает около 40 секунд, поэтому не делайте этого: используйте связанные списки и сгладьте в конце, как в моем первом примере (однако, это сломается, если f[i]
возвращает список, и тогда необходимо больше внимания).
Другим решением является экспорт результатов промежуточных вычислений в исполняемый файл журнала, как описано в этом ответе WReach (см. "Подход, основанный на файлах в памяти", раздел). С помощью этого вы получите более свежие результаты промежуточных вычислений и всегда сможете исследовать то, что вычисляется до сих пор.
P.S. Я думаю, что использование Monitor
, как было предложено в this недавний отзыв Mathematica на twitter также полезен в таких случаях:
Monitor[Table[Integrate[1/(x^n + 1), x], {n, 20}],
ProgressIndicator[n, {1, 20}]]