В Mathematica, как мне скомпилировать функцию Outer [] для произвольного числа аргументов? - PullRequest
8 голосов
/ 11 февраля 2011

Если я хочу найти все возможные суммы из двух списков list1 и list2, я использую функцию Outer[] со спецификацией Plus в качестве оператора объединения:

In[1]= list1 = {a, b}; list2 = {c, d}; Outer[Plus, list1, list2]

Out[1]= {{a + c, a + d}, {b + c, b + d}}

Если я хочу обрабатывать произвольное количество списков, скажем, список списков,

In[2]= listOfLists={list1, list2};

тогда единственный способ узнать все возможные суммы - использовать функцию Apply[] (которая имеет сокращенную комбинацию @@) вместе с Join:

In[3]= argumentsToPass=Join[{Plus},listOfLists]

Out[3]= {Plus, {a, b}, {c, d}}

In[4]= Outer @@ argumentsToPass

Out[4]= {{a + c, a + d}, {b + c, b + d}}

или просто

In[5]= Outer @@ Join[{Plus},listOfLists]

Out[5]= {{a + c, a + d}, {b + c, b + d}}

Проблема возникает, когда я пытаюсь скомпилировать:

In[6]= Compile[ ..... Outer @@ Join[{Plus},listOfLists] .... ]

Compile::cpapot: "Compilation of Outer@@Join[{Plus},listOfLists]] is not supported for the function argument Outer. The only function arguments supported are Times, Plus, or List. Evaluation will use the uncompiled function. "

Дело в том, что я использую поддерживаемую функцию, а именно Plus. Проблема, похоже, связана только с функцией Apply[]. Потому что, если я дам ему фиксированное количество списков вместе с external-plus, он будет работать нормально

In[7]= Compile[{{bob, _Integer, 1}, {joe, _Integer, 1}}, Outer[Plus, bob, joe]]

Out[7]= CompiledFunction[{bob, joe}, Outer[Plus, bob, joe],-CompiledCode-]

но как только я использую Apply, он ломается

In[8]= Compile[{{bob, _Integer, 1}, {joe, _Integer, 1}}, Outer @@ Join[{Plus}, {bob, joe}]]

Out[8]= Compile::cpapot: "Compilation of Outer@@Join[{Plus},{bob,joe}] is not supported for the function argument Outer. The only function arguments supported are Times, Plus, or List. Evaluation will use the uncompiled function."

Итак, мои вопросы : есть ли способ обойти эту ошибку или, альтернативно, способ вычислить все возможные суммы элементов, извлеченных из произвольного числа списков в скомпилированной функции?

(Кроме того, я не уверен, является ли тег "compilation" подходящим. Пожалуйста, сообщите.)

Большое спасибо.

Ответы [ 2 ]

12 голосов
/ 11 февраля 2011

Один из способов использовать With для программного создания скомпилированной функции:

Clear[makeCompiled];
makeCompiled[lnum_Integer] :=
 With[{listNames = Table[Unique["list"], {lnum}]},
   With[{compileArgs = {#, _Integer, 1} & /@ listNames},
      Compile @@ Join[Hold[compileArgs],
        Replace[Hold[Outer[Plus, listNames]], 
          Hold[Outer[Plus, {x__}]] :> Hold[Outer[Plus, x]], {0}]]]];

Возможно, это можно сделать красивее, но это работает.Например:

In[22]:= p2 = makeCompiled[2]
Out[22]= CompiledFunction[{list13,list14},Outer[Plus,list13,list14],-CompiledCode-]

In[23]:= p2[{1,2,3},{4,5}]
Out[23]= {{5,6},{6,7},{7,8}}

In[24]:= p3 = makeCompiled[3]
Out[24]= CompiledFunction[{list15,list16,list17},Outer[Plus,list15,list16,list17],-CompiledCode-]

In[25]:= p3[{1,2},{3,4},{5,6}]
Out[25]= {{{9,10},{10,11}},{{10,11},{11,12}}}

HTH

Редактировать:

Вы можете скрыть скомпилированную функцию за другой, чтобы она создавалась во время выполнения, и вы неНа самом деле это не так:

In[33]:= 
Clear[computeSums]
computeSums[lists : {__?NumberQ} ..] := makeCompiled[Length[{lists}]][lists];

In[35]:= computeSums[{1, 2, 3}, {4, 5}]

Out[35]= {{5, 6}, {6, 7}, {7, 8}}

В этом случае вы сталкиваетесь с накладными расходами на компиляцию, поскольку вы каждый раз создаете скомпилированную функцию заново.Вы можете довольно элегантно справиться с этими затратами, используя памятку, используя Module переменные для сохранения, чтобы локализовать ваши памятные определения:

In[44]:= 
Clear[computeSumsMemoized];
Module[{compiled},
  compiled[n_] := compiled[n] = makeCompiled[n];
  computeSumsMemoized[lists : {__?NumberQ} ..] := compiled[Length[{lists}]][lists]];

In[46]:= computeSumsMemoized[{1, 2, 3}, {4, 5}]

Out[46]= {{5, 6}, {6, 7}, {7, 8}}
4 голосов
/ 16 февраля 2011

Это мой первый пост.Надеюсь, я правильно понял.

Если ваши входные данные представляют собой списки целых чисел, я скептически отношусь к значению компиляции этой функции, по крайней мере, в Mathematica 7.

Например:

f = Compile[{{a, _Integer, 1}, {b, _Integer, 1}, {c, _Integer, 1}, {d, _Integer, 1}, {e, _Integer, 1}}, 
        Outer[Plus, a, b, c, d, e]
    ];

a = RandomInteger[{1, 99}, #] & /@ {12, 32, 19, 17, 43};

Do[f @@ a, {50}] // Timing

Do[Outer[Plus, ##] & @@ a, {50}] // Timing

Два тайминга для меня существенно не отличаются, но, конечно, это только один пример.Дело лишь в том, что Outer уже довольно быстр по сравнению с скомпилированной версией.

Если у вас есть причины, отличные от скорости компиляции, вы можете найти применение в Tuples вместо Outer, но у вас все еще есть ограничение:скомпилированные функции, требующие тензорного ввода.

f2 = Compile[{{array, _Integer, 2}}, 
      Plus @@@ Tuples@array
    ];

f2[{{1, 3, 7}, {13, 25, 41}}]

Если ваши входные данные большие, тогда может потребоваться другой подход.Имея список списков целых чисел, эта функция будет возвращать возможные суммы и количество способов получить каждую сумму:

f3 = CoefficientRules@Product[Sum[x^i, {i, p}], {p, #}] &;

f3[{{1, 3, 7}, {13, 25, 41}}]

Во многих случаях это должно оказаться намного более эффективным с точки зрения памяти.

a2 = RandomInteger[{1, 999}, #] & /@ {50, 74, 55, 55, 90, 57, 47, 79, 87, 36};

f3[a2]; // Timing

MaxMemoryUsed[]

Это заняло 3 секунды и потребовало минимального объема памяти, но попытка применения Outer к a2 завершила работу ядра с «Нет больше доступной памяти».

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...