6

The smallest sample I can find to reproduce this problem is:

test = Compile[{}, If[True, 1]];
<< CompiledFunctionTools`
CompilePrint@test
…………
 1    R0 = MainEvaluate[ Function[{}, If[True, 1]][ ]]
 2    Return

The problem disappears when the If[] is followed by a semicolon:

test = Compile[{}, If[True, 1];];
CompilePrint@test
…………
1     if[ !B0] goto 3
2     goto 3
3     Return

or enclosed by a redundant Do[]:

test = Compile[{}, Do[If[True, 1], {1}]];
CompilePrint@test
…………
1   I1 = I0
2   I3 = I2
3   goto 6
4   if[ !B0] goto 6
5   goto 6
6   if[ ++ I3 < I1] goto 4
7   Return

while a redundant Table[] won't help:

test = Compile[{}, Table[If[True, 1], {1}]];
CompilePrint@test
…………

1 I1 = I0 2 I4 = I3 3 T(R1)0 = Table[ I1] 4 I2 = I3 5 goto 8 6 R1 = MainEvaluate[ Function[{}, If[True, 1]][ ]] 7 Element[ T(R1)0, I4] = R1 8 if[ ++ I2 < I1] goto 6 9 Return

This phenomenon may lead to a total failure of compilation when codes become more complicated:

ie = 200;
ez = ConstantArray[0., {ie + 1}];
hy = ConstantArray[0., {ie}];

f1 = Compile[{{steps}}, 
   Module[{ie = ie, ez = ez, hy = hy}, 
    Do[ez[[2 ;; ie]] = ez[[2 ;; ie]] + (hy[[2 ;; ie]] - hy[[1 ;; ie - 1]]);
     ez[[1]] = Sin[n/10];
     hy[[1 ;; ie]] = hy[[1 ;; ie]] + (ez[[2 ;; ie + 1]] - ez[[1 ;; ie]]), {n, steps}];
    ez]];

f2 = Compile[{{steps, _Integer}, {test, True | False}}, 
   Module[{ie = ie, ez = ez, hy = hy}, 
    Do[ez[[2 ;; ie]] = ez[[2 ;; ie]] + (hy[[2 ;; ie]] - hy[[1 ;; ie - 1]]);
     ez[[1]] = Sin[n/10];
     hy[[1 ;; ie]] = hy[[1 ;; ie]] + (ez[[2 ;; ie + 1]] - ez[[1 ;; ie]]), {n, steps}];
    If[test, ez]]];

f1[1500]; // AbsoluteTiming
{0.0250000, Null}
f2[1500, True]; // AbsoluteTiming

Warnings……

{0.5730000, Null}

So, as the title said, If won't compile when its output is the output of Compile?

Of course it can be easily circumvented, but I wonder if there's a universal rule behind this, or it's just another unreasonable behavior of Compile?

xzczd
  • 65,995
  • 9
  • 163
  • 468
  • 3
    Identical issue, and solution, to those discussed in this question earlier today. In fact I'd be tempted to close this as a duplicate, except that I think it shows the issue a bit more clearly than the other question. – Oleksandr R. Apr 18 '14 at 13:26
  • @OleksandrR. In fact it's that question that motivate me to ask this one (I've noticed this behavior for some time), I read it but still failed to notice it's an identical issue 囧. – xzczd Apr 18 '14 at 13:33

1 Answers1

8

This is a type incompatibility problem. The compiler must make an assignment to the output of If in order to return a value. If[True, 1] is equivalent to If[True, 1, Null], and there is no type that can be assigned to both 1 and Null. You will get the same problem if you make an assignment to the If result when it isn't the final output of Compile:

CompilePrint @ Compile[{}, Block[{x}, x = If[True, 1]]; 2]
      I0 = 1
      I1 = 2
      B0 = True
      Result = I1

1 R0 = MainEvaluate[ Function[{}, If[True, 1]][ ]] 2 Return

I'm not sure why you don't get a message, in other cases you do:

CompilePrint @ Compile[{}, If[True, 1, {2, 3}]]

Compile::cif: The types of the two results in If[True,1,{2,3}] are incompatible because their ranks are different. Evaluation will use the uncompiled function. >>

      I0 = 1
      T(I1)0 = {2, 3}
      B0 = True
      Result = R0

1 R0 = MainEvaluate[ Function[{}, If[True, 1, {2, 3}]][ ]]
2 Return

Note that if both returns from If are Null there is no problem:

CompilePrint @ Compile[{}, If[True, Null]]
      B0 = True
      Result = V17

1 if[ !B0] goto 3 2 goto 3 3 Return

Simon Woods
  • 84,945
  • 8
  • 175
  • 324