5

I am aware that one can compile Which, e.g.:

ff = Compile[x, Which @@ {x < 0, 2, x >= 0, 3}
ff[1.]

3

A drawback of Which is that it evaluates x repeatedly until one of the conditions is satisfied. This is time consuming when x is a complicated expression or/and when the set of conditions is large. As a far as I know, the same issue arises with Piecewise.

Switch evaluates x only once, but seems to be not compilable. My try was as follows:

gg = Compile[x, Switch[x, x < 0, 3, x >= 0, 3]]
gg[1.]

CompiledFunction::cfse: Compiled expression Null should be a machine-size real number. >> CompiledFunction::cfex: Could not complete external evaluation at instruction 1; proceeding with uncompiled evaluation. >>

Does anyone know how to compile Switch correctly?

J. M.'s missing motivation
  • 124,525
  • 11
  • 401
  • 574
Breugem
  • 785
  • 3
  • 12
  • 2
    Notice your first line of code isn't compiled actually because Apply will be compiled only if its first argument is Times or Plus. – xzczd Sep 28 '16 at 12:02
  • 3
    Also, as a member for 3 years, please learn to format your code properly. And, you've totally misunderstand Switch in Mathematica, please check the document of Switch carefully. – xzczd Sep 28 '16 at 12:10
  • 4
    -1 for poor formatting; format your code, and I will remove -1. – QuantumDot Sep 28 '16 at 13:20
  • 1
    Sorry, I earned the -1 indeed. I will invest more time in posting an answer next time. Apologies again – Breugem Sep 28 '16 at 15:28

1 Answers1

14

A drawback of the Which command is that it evaluates x repeatedly until one of the conditions of Which is satisfied. This is time consuming when x is a complicated expression or/and when the set of conditions is large.

This is not true in Compile as x will always be a number or an array. Multiple uses of x won't slow anything down.

It isn't even true outside of Compile in most practical cases unless you defined x using := instead of =.

The command "Switch" only evaluates x once, but seems to be not compilable.

Simple forms of Switch are compilable. In your question, you are misusing Switch. It takes patterns, not true/false conditions. Please read the documentation.

When using it in Compile, only literal patterns seem to be accepted. An exception is the last pattern, which must be _ to handle the default case.

Example:

cf = Compile[{{x, _Integer}},
  Switch[x,
   1, 10,
   2, 20,
   _, 0
   ]
  ]

"
        1 argument
        2 Boolean registers
        8 Integer registers
        Underflow checking off
        Overflow checking off
        Integer overflow checking on
        RuntimeAttributes -> {}

        I0 = A1
        I1 = 1
        I4 = 20
        I2 = 10
        I3 = 2
        I5 = 0
        Result = I7

1   B0 = I0 == I1
2   if[ !B0] goto 5
3   I7 = I2
4   goto 11
5   B1 = I0 == I3
6   if[ !B1] goto 9
7   I6 = I4
8   goto 10
9   I6 = I5
10  I7 = I6
11  Return
"

If you try to use a more general pattern, such as _Integer or x_ /; x > 0, it will trigger a call to MainEvaluate (i.e. it won't be compiled) for that particular test.

Warning: When testing with literal patterns, it is important to be aware that 1. and 1 are considered to be different. We needed to explicitly specify that the input is an integer, otherwise it would be converted to a real (floating point) number, which never matches the integer 1. Thanks to @MichaelE2 for pointing this out!


In summary:

Inside Compile,

  • use Switch if you need an equivalent of switch from C

  • use Which if you need the equivalent of a sequence of if ... else if ... else if ...

Szabolcs
  • 234,956
  • 30
  • 623
  • 1,263
  • 2
    Try Compile[{x}, Switch[x, 1., 10, 2., 20, _, 0]]. Your input x is Real by default. Not sure I completely understand yet...-- I guess the compiler decides that a real x cannot match an integer 1 or 2, so it pre-evaluates the boolean condition to False. – Michael E2 Sep 28 '16 at 12:33
  • 2
    Compare with Compile[{{x, _Integer}}, Switch[x, 1, 10, 2, 20, _, 0]]. – Michael E2 Sep 28 '16 at 12:36
  • @MichaelE2 You are right, not a bug. – Szabolcs Sep 28 '16 at 12:37
  • 2
    FWIW, it's consistent with the regular Mathematica value of Switch[1., 1, 10, 2, 20, _, 0]. – Michael E2 Sep 28 '16 at 12:41
  • @MichaelE2 That's strange: both with version 8.0.4 and 11.0.0 I get 10 as output for Compile[{x}, Switch[x, 1., 10, 2., 20, _, 0]][1] and Compile[{{x, _Integer}}, Switch[x, 1, 10, 2, 20, _, 0]][1]. But Switch[1., 1, 10, 2, 20, _, 0] returns 0. I'm on Windows 7 x64. Do you get different results? – Alexey Popkov Sep 28 '16 at 17:18
  • 1
    @AlexeyPopkov Yes, I get the same, and it seems quite normal, no? I mean in the first case, the type of x is by default _Real, so the input 1 is first converted to the real 1. before being passed to the compiled function. So it matches the case that returns 10. In the uncompiled Switch[..], the 1. does not match 1, since a Real is not an Integer. – Michael E2 Sep 28 '16 at 22:16
  • @MichaelE2 Thank you, I've got the point. – Alexey Popkov Sep 29 '16 at 04:57