В Compile ведут себя по-разному - почему? - PullRequest
8 голосов
/ 18 ноября 2011

Мне интересно, если это ошибка или задокументированное поведение?

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

При применении их к списку, не содержащему отрицательных чисел, мы получаем разные результаты:

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

Out[66]= {Null, 3}

Это вызвало ошибку, когда я попытался скомпилировать короткую функцию (на самом деле ее модифицированную версию).

Do сам по себе не показывает проблему:

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

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

Ответы [ 4 ]

5 голосов
/ 18 ноября 2011

Я бы сказал, что это ошибка, связанная с тем, как Compile работает, но неудивительно, что она работает неправильно. Compile делает довольно специфические предположения не только о его входных данных (здесь v будет список целых чисел), но и о его выходных данных. Предполагается, что скомпилированные функции должны возвращать значения одного определенного типа, и этот тип должен быть одним из типов, допустимых в качестве входных данных для скомпилированной функции: True|False, Integer и т. Д., А также массивы так же. Очевидно, было бы лучше, если бы функция жаловалась на сообщение, а затем возвращала Null, но для того, чтобы быть хорошо работающей функцией для компиляции, вам нужно предоставить соответствующее целочисленное возвращаемое значение в качестве значения по умолчанию.

РЕДАКТИРОВАТЬ , чтобы уточнить о типах вывода, для Сабольч 'комментарий ниже.

4 голосов
/ 18 ноября 2011

Я бы не сказал, что это ошибка. Как заметил @Pillsy, функция Compile -d более ограничена, поскольку она всегда должна возвращать один и тот же тип. Поскольку Do является областью видимости, Return внутри Do выходит только из Do, а не Function. Следовательно, в некоторых случаях он возвращает элемент вектора, в других он возвращает Null. Строго говоря, как написано, функция вообще не должна компилироваться. Тем не менее, можно быть более гибким и предполагать, что автор функции знает лучше, и в этом конкретном случае откажется от ответа. При такой интерпретации Compile может дать любой ответ в этом случае. Здесь он производит последний элемент в списке. И я думаю, что это не более случайный, чем создание фиксированного числа каждый раз. Я также думаю, что таких угловых случаев нельзя избежать, когда компилируется гораздо более гибкий символический код. Compile мог бы иметь более строгие правила в этом случае, а требует некоторого значимого возврата (того же типа) во всех случаях, но мне не ясно, будет ли это действительно выгодно. В некотором смысле, все C таково - компилятор предполагает, что вы знаете, что делаете, но позволяет вам создать много неопределенного поведения, если вы не будете осторожны.

3 голосов
/ 19 ноября 2011

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

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

Несмотря на то, что функция должна была вернуться к первому элементу, который она получила до last = 1, следовательно, как уже отмечали другие, Return не работает.Это не будет исправлено, так как от этого поведения зависит слишком много кода.

Теперь вы можете использовать:

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

, который ведет себя как ожидалось.К сожалению, однако,

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

не скомпилируется.

Вот способ заставить это работать.

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

Проверьте вывод.

In[139]:= CompilePrint[c1]

Некоторые дополнительные примечания: «RuntimeErrorHandler» -> Function [Null] это функция!Подумайте об этом на секунду.Вы можете Отослать что угодно!

Так что-то вроде этого работает.

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

Надеюсь, это полезно.

3 голосов
/ 18 ноября 2011

Как отмечается в ответах @Pillsy и @Leonid, проблема в том, что исходная функция иногда возвращает Null, а иногда и целое число. Напротив, скомпилированная функция всегда возвращает целое число. В V8 мы можем увидеть это, используя CompilePrint:

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

, который под V8.0.4 дает такой результат:

        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

Мы можем видеть, что результатом скомпилированной функции является то, что заканчивается в целочисленном регистре I5. Следуя потоку декомпилированных инструкций, мы видим, что если совпадений нет, то I5 будет содержать последний элемент списка.

Поведение компилятора может меняться между выпусками Mathematica. Я думаю, что разумно утверждать, что компилятор должен выдавать хотя бы предупреждение при обстоятельствах, когда тип возвращаемого результата неоднозначен.

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