2011-11-18 12 views
8

Me pregunto si esto es un error o un comportamiento documentado?Do/Return se comporta de manera diferente en Compile - ¿por qué?

f1 = Function[v, 
    Do[If[v[[i]] < 0, Return[v[[i]]]], {i, 1, Length[v]}]] 

c1 = Compile[{{v, _Integer, 1}}, 
    Do[If[v[[i]] < 0, Return[v[[i]]]], {i, 1, Length[v]}]] 

Al aplicarlos a una lista que no contiene números negativos, se obtienen resultados diferentes:

In[66]:= Through[{f1, c1}[{1, 2, 3}]] 

Out[66]= {Null, 3} 

Esto ha causado un error cuando traté de compilar una short function (en realidad una versión modificada de la misma) .

Do sola no muestra el problema:

c2 = Compile[{}, Do[i, {i, 5}]] 

c2[] (* returns nothing, as expected *) 

Respuesta

3

Como se señala en las respuestas de @Pillsy y @Leonid, el problema es que la función original a veces devuelve Null y, a veces, un número entero. Por el contrario, la función compilada siempre devuelve un número entero. En V8, podemos ver esto utilizando CompilePrint:

Needs["CompiledFunctionTools`"] 
CompilePrint @ 
    Compile[{{v,_Integer,1}},Do[If[v[[i]]<0,Return[v[[i]]]],{i,1,Length[v]}]] 

que, bajo V8.0.4, produce este resultado:

 1 argument 
     1 Boolean register 
     6 Integer registers 
     1 Tensor register 
     Underflow checking off 
     Overflow checking off 
     Integer overflow checking on 
     RuntimeAttributes -> {} 

     T(I1)0 = A1 
     I3 = 0 
     I0 = 1 
     Result = I5 

1 I2 = Length[ T(I1)0] 
2 I4 = I3 
3 goto 10 
4 I5 = Part[ T(I1)0, I4] 
5 B0 = I5 < I3 
6 if[ !B0] goto 10 
7 I5 = Part[ T(I1)0, I4] 
8 goto 11 
9 goto 10 
10 if[ ++ I4 < I2] goto 4 
11 goto 12 
12 Return 

podemos ver que el resultado de la función compilado es lo que termina en registro entero I5.Siguiendo el flujo de las instrucciones descompiladas, vemos que si no hay coincidencia, entonces I5 terminará conteniendo el último elemento de la lista.

El comportamiento del compilador puede variar entre las versiones de Mathematica. Creo que es razonable argumentar que el compilador debe emitir al menos una advertencia en circunstancias en las que el tipo de resultado de retorno es ambiguo.

+0

+1 Esto es exactamente lo que también miré para ver qué se devuelve. –

5

que diría esto un error con la forma Compile está trabajando, pero no es muy sorprendente que no está funcionando bien. Compile hace suposiciones bastante específicas sobre no solo sus entradas (aquí, que v será una lista de enteros) sino también sus salidas. Se supone que las funciones compiladas devuelven valores de un único tipo específico, y ese tipo debe ser uno de los tipos aceptables como entrada para una función compilada: True|False, Integer, etc., y matrices de la misma. Obviamente, sería mejor si la función se quejó con un mensaje y luego devolvió Null, pero para ser una función de buen comportamiento para la compilación, debe proporcionar un valor de retorno entero apropiado como un defalult.

EDIT para aclarar sobre los tipos de salida, por Szabolcs 'comentario a continuación.

+0

No devuelve valores del mismo tipo que la entrada, pero devuelve el mismo tipo de valor independientemente de la entrada específica (es decir, no puede devolver tanto Nulo como un Entero). Sí, lo que dices es razonable. – Szabolcs

4

No diría que esto es un error. Como señaló @Pillsy, la función Compile -d es más restringida ya que siempre debe devolver el mismo tipo. Dado que Do es una construcción de ámbito, Return dentro de Do solo se rompe en Do, no en Function. Por lo tanto, en algunos casos devuelve un elemento vectorial, en otros devuelve Null. Estrictamente hablando, tal como está escrito, la función no debería compilarse en absoluto. Sin embargo, uno puede ser más flexible y asumir que el escritor de la función sabe mejor, y descartará la respuesta en ese caso particular. Con esta interpretación, Compile es libre de producir cualquier respuesta en ese caso. Lo que hace aquí es producir el último elemento en la lista. Y creo que esto no es más ad hoc que producir un número fijo cada vez. También creo que estos casos de esquina no se pueden evitar cuando se compila un código simbólico mucho más flexible. Compile podría haber tenido reglas más estrictas en este caso y requiere alguna devolución significativa (del mismo tipo) en todos los casos, pero no está claro si esto realmente sería beneficioso. En cierto sentido, todo C es así: el compilador supone que usted sabe lo que está haciendo, pero le permite crear un comportamiento indefinido si no tiene cuidado.

3

Alguna información adicional que pueda resultarte útil. Considere esto:

In[26]:= f1 = 
Function[v, Do[If[v[[i]] < 0, Return[v[[i]]]], {i, 1, Length[v]}]; 
    last = 1;]; 

In[27]:= last 

Out[27]= last 

In[28]:= f1[{-1, 2, 3}] 

In[29]:= last 

Out[29]= 1 

Apesar de la función debería haber vuelto en el primer elemento que tiene que durar = 1, por lo tanto, como otros han señalado, vuelta se rompe. Esto no será solucionado ya que hay demasiado código que depende de este comportamiento.

Ahora, su puede usar:

In[30]:= f2 = Function[v, Module[{}, 
    Do[If[v[[i]] < 0, Return[v[[i]], Module]], {i, 1, Length[v]}]; 
    last2 = 1;]]; 

In[31]:= f2[{-1, 2, 3}] 

Out[31]= -1 

In[32]:= last2 

Out[32]= last2 

que se comporta como se esperaba. Desafortunadamente, sin embargo,

In[33]:= c1 = Compile[{{v, _Integer, 1}}, 
    Module[{}, 
    Do[If[v[[i]] < 0, Return[v[[i]], Module]], {i, 1, Length[v]}]; 
    ] 
    ]; 

no se compilará.

Aquí hay una manera de hacer que funcione.

In[137]:= c1=Compile[{{v,_Integer,1}}, 
Module[{res=1}, 
Do[If[v[[i]]<0,res=v[[i]];Break[]],{i,1,Length[v]}]; 
If[res==1,Internal`CompileError[]]; 
res 
] 
,"RuntimeOptions"->{"RuntimeErrorHandler"->Function[Null]}] 

In[140]:= c1[{1,2,3,1}] 

In[141]:= c1[{1,2,3,-1}] 

Out[141]= -1 

Compruebe la salida.

In[139]:= CompilePrint[c1] 

más notas: "RuntimeErrorHandler" -> Función [Null] Esta es una función! Piense en eso por un segundo. Puedes Thow, ¡Mensaje cualquier cosa!

Así que algo como esto funciona.

cfquietfail = 
    Compile[{{x, _Real, 1}}, Exp[x], 
    "RuntimeOptions" -> {"WarningMessages" -> False, 
    "RuntimeErrorHandler" -> 
     Function[Message[MyFunctionName::"I can complain here!"]; 
     Throw[$Failed]]}]; 
Catch[ cfquietfail[{1000.}]] 

Espero que esto sea útil.

Cuestiones relacionadas