2011-02-11 14 views
7

Si quiero encontrar todas las cantidades posibles a partir de dos listas list1 y list2, utilizo la función Outer[] con la especificación de Plus como el operador de la combinación de:En Mathematica, ¿cómo compilo la función Externa [] para un número arbitrario de argumentos?

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

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

si quiero ser capaz de manejar un número arbitrario de listas, por ejemplo, una lista de listas,

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

entonces la única manera que sé cómo encontrar todas las sumas posibles es utilizar la función Apply[] (que tiene la mano corta @@) junto con 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}}

o simplemente

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

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

El problema viene cuando intento compilar:

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

Compile::cpapot: "Compilation of [email protected]@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. "

La cuestión es que am usando un s función upported, es decir, Plus. El problema parece ser únicamente con la función Apply[]. Porque si le dan un número fijo de listas al exterior de más juntos, funciona bien

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

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

pero tan pronto como yo uso Apply, se rompe

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

Out[8]= Compile::cpapot: "Compilation of [email protected]@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."

Así que mis preguntas son: ¿Hay alguna forma de eludir este error o, alternativamente, una forma de calcular todas las sumas posibles de elementos extraídos de un número arbitrario de listas en una función compilada?

(Además, no estoy seguro de si "compilación" es una etiqueta adecuada. Por favor avise.)

Muchas gracias.

+0

¿Cuántas listas espera y por cuánto tiempo son las listas? Según la respuesta, Compilar puede no ser la forma más rápida de realizar esta operación. – joebolte

Respuesta

10

Una forma es utilizar With, para crear una función de programación compilado:

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}]]]]; 

Probablemente se puede hacer más bonito, pero funciona. Por ejemplo:

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

Editar:

puede ocultar la función compilado detrás de otra, de manera que se crea en tiempo de ejecución y que en realidad no verlo:

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}} 

En este caso se enfrenta a una sobrecarga de compilación, ya que crea una función compilada nuevamente cada vez. Se puede luchar contra esta sobrecarga en lugar elegantemente con memoization, utilizando Module las variables de persistencia, para localizar sus definiciones memoized:

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}} 
+0

¿Pero no tendría que volver a compilar para todas las listas posibles? Quiero una única función compilada que, dependiendo de las entradas, pueda manejar diferentes números de listas. –

+1

La última función (computeSumsMemoized) hace exactamente eso. Cuando por primera vez ve una serie de listas (por ejemplo, 3) que no vio antes, sí compila. Pero todas las otras veces que suministre 3 listas, no se volverá a compilar; utilizará la definición memorada (función ya compilada). Pero tenga en cuenta que para Outer, aunque puede obtener un poco de aceleración a partir de la compilación (especialmente si compila a C), probablemente no sea dramático. –

+2

Ok, lo siento, me perdí el punto un poco.Esta no es una única función compilada que maneja cualquier combinación de listas, de hecho. Más bien, esta es una colección (tabla hash) de funciones compiladas, creadas automáticamente a pedido. Para una sola función compilada, de hecho tienes un problema con 'Apply' y' Outer', y no sé cómo resolver eso. Pero, en la práctica, ¿hace mucha diferencia? Te enfrentarás a pequeños éxitos de rendimiento debido a la compilación cada vez que proporciones una nueva cantidad de listas, pero esto solo ocurrirá una vez para un número dado de listas. –

3

Este es mi primer post. Espero que entienda esto bien.

Si las entradas son listas de números enteros, soy escéptico del valor de compilar esta función, al menos en Mathematica 7.

Por ejemplo:

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 

Los dos horarios no son significativamente diferentes para mí, pero por supuesto esta es solo una muestra. El punto es simplemente que Outer ya es bastante rápido en comparación con la versión compilada.

Si tiene otros motivos que la velocidad de compilación, puede encontrar algún uso en Tuples en lugar de Outer, pero todavía tiene la restricción de las funciones compiladas que requieren entrada de tensor.

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

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

Si sus entradas son grandes, entonces un enfoque diferente puede estar en orden. Dada una lista de listas de números enteros, esta función devolverá las sumas posibles y el número de maneras de obtener cada suma:

f3 = [email protected][Sum[x^i, {i, p}], {p, #}] &; 

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

Esto debe llegar a ser mucho más eficiente de la memoria en muchos casos.

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

f3[a2]; // Timing 

MaxMemoryUsed[] 

Esto tomó 3 segundos y mínimo de memoria, pero intentar la aplicación de exterior a a2 terminado el kernel con "No hay más memoria disponible."

Cuestiones relacionadas