Bug with Compile and Log2?

12

1

It seems that Log2 in Compile gives numerical errors (CompiledFunction::cfn) if the input is not a power of 2.

For instance:

test1 = Compile[{{in, _Integer}}, Log2[in]];
test1[3]

gives an error and proceeds with an uncompiled evaluation.

However:

test2 = Compile[{{in, _Integer}}, Log[2, in]];
test2[3]

works correctly.

Is this intentional or a bug?

JungHwan Min

Posted 2016-07-23T20:58:21.153

Reputation: 4 484

1Log10[] is also affected. – J. M.'s ennui – 2016-07-23T21:05:42.823

1Just for noting it: Log2[N[in]] works. – István Zachar – 2016-07-23T21:07:40.157

Log2[N[in]] adds a integer to real conversion. – Karsten 7. – 2016-07-23T21:28:15.950

3Compile[{{in, _Real}}, Log2[in]] works. – Karsten 7. – 2016-07-23T21:30:11.083

test2 does two integer to real conversions. One for 2 and one for the argument in. – Karsten 7. – 2016-07-23T21:51:18.240

5If you look at CompilePrint@test1 and CompilePrint@test2, I think one can infer that Log2[integer] is intentionally a special-case integer function. (Apparently undocumented?) – Michael E2 – 2016-07-23T22:45:37.493

Answers

9

Looking at the internal structure of the compiled functions, we can see a distinct call to an integer-valued function Log2 of an integer argument. There is also one for a real-valued function Log2 of a real argument. The first example I think shows that the integer Log2 is intentional. There are similar special integer functions for Log10, Power, Gamma and perhaps others. It is easy to imagine reasons why one would want to do integer calculations with such functions. As far as I know, these special cases are not documented.

Needs["CompiledFunctionTools`"];

test1 = Compile[{{in, _Integer}}, Log2[in]];
CompilePrint@test1

(*
        1 argument
        2 Integer registers
        Underflow checking off
        Overflow checking off
        Integer overflow checking on
        RuntimeAttributes -> {}

        I0 = A1
        Result = I1

1   I1 = Log2[ I0]
2   Return
*)

test3 = Compile[{{re, _Real}}, Log2[re]];
CompilePrint@test3

(*
        1 argument
        2 Real registers
        Underflow checking off
        Overflow checking off
        Integer overflow checking on
        RuntimeAttributes -> {}

        R0 = A1
        Result = R1

1   R1 = Log2[ R0]
2   Return
*)

The byte codes (from List @@ test1, List @@ test3) for the Log2 instructions are respectively

{40, 36, 2, 0, 0, 2, 0, 1}    (* I1 = Log2[ I0] *)
{40, 36, 3, 0, 0, 3, 0, 1}    (* R1 = Log2[ R0] *)

The 40, 36 identifies Log2; the 2, 0, x and 3, 0, x specify the register types of the input and output, _Integer (2) and _Real (3), of rank 0 (scalar), and of register numbers 0, 1 resp.

I tried constructing a CompiledFunction with the "instruction"

{40, 36, 2, 0, 0, 3, 0, 0}    (* R0 = Log2[ I0] *)

but it crashed the kernel. (I tried doing it by hand imitating the output of List @@ test1 and other compiled functions.) I concluded that such a function is not implemented.

Michael E2

Posted 2016-07-23T20:58:21.153

Reputation: 190 928

Nice analysis, very insightful! – sebhofer – 2016-07-26T07:42:04.810

1@sebhofer Thanks! -- Side note: "Type conversion" can be accomplished with Compile[{{in, _Integer}}, Log2[N[in]]]. (There is no call to N per se; it just converts to Real via R0 = I0 and then calls Log2[ R0]. If in is type _Real to begin with, then N is ignored.) – Michael E2 – 2016-07-26T12:55:07.307