Табличная функция Mathematica - PullRequest
11 голосов
/ 24 июня 2011

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

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

Ответы [ 3 ]

19 голосов
/ 24 июня 2011

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

Вот версия Table, которая Abort -поддерживается и будет сохранять промежуточные результаты, собранные до сих пор. Это модифицированная версия решения, размещенная здесь .

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[# === {}, #, First@#] &@Last@Reap[
        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 -го индекса. Функция SplitBy имеет эту функцию, но нам нужно предоставить список функций, которые будут использоваться для разделения шагов. Поскольку индекс i -го итератора в подсписке {expr,{ind1,...,indn}} равен 2,i, функция, которая выполняет разбиение на i -м шаге, равна #[[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 существуют, например, например ::1010*

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}

Это почти так же быстроas 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] &@ Last@Reap[
      CheckAbort[Do[Sow[{expr, indices}, sowTag], iter], Null],sowTag];
   AbortProtect[
      SplitBy[indexedRes, Array[Function[x, #[[2, x]] &], {depth}]][[##,1]] & @@ 
      Table[All, {depth + 1}]
   ]];
6 голосов
/ 24 июня 2011

К сожалению нет. Если вы хотите сделать что-то вроде lst=Table[f[i],{i,1,10000}], чтобы при прерывании у вас все еще были результаты, вы можете сделать

Clear[lst2];
lst2 = {};
(Do[lst2 = {lst2, f[i]}, {i, 1, 10000}];
lst2=Flatten[lst2];) // Timing

, что для неопределенного 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 голосов
/ 26 июня 2011

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

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

Monitor[Table[Integrate[1/(x^n + 1), x], {n, 20}], 
 ProgressIndicator[n, {1, 20}]]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...