Функция таблицы Mathematica

Я запускаю функцию таблицы, которая займет слишком много времени.

Я хотел знать, есть ли способ получить результаты, рассчитанные до сих пор.

Ответ 1

Предлагаемое решение

Вот версия 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}]
   ]];

Ответ 2

К сожалению нет. Если вы хотите сделать что-то вроде 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] возвращает список, и тогда необходимо больше внимания).

Ответ 3

Другим решением является экспорт результатов промежуточных вычислений в исполняемый файл журнала, как описано в этом ответе WReach (см. "Подход, основанный на файлах в памяти", раздел). С помощью этого вы получите более свежие результаты промежуточных вычислений и всегда сможете исследовать то, что вычисляется до сих пор.

P.S. Я думаю, что использование Monitor, как было предложено в this недавний отзыв Mathematica на twitter также полезен в таких случаях:

Monitor[Table[Integrate[1/(x^n + 1), x], {n, 20}], 
 ProgressIndicator[n, {1, 20}]]