Can constant propagation transform integer powers of -1 to an if/else?

julia> @btime (n -> [iseven(k) ? 1 : (-1) for k in 1:n])(500);
  890.571 ns (1 allocation: 4.06 KiB)

julia> @btime (n -> [(-1)^k for k in 1:n])(500);
  6.170 μs (1 allocation: 4.06 KiB)

It would be great if the latter could be transformed into the former through constant propagation. I wonder if there are edge cases where this won’t hold?

1 Like

I think you should be able to add a special case here:

But, optimizing for special cases like this is detrimental for the performance of the general case, so I’m not sure this would be a good idea.

Another possibility would be using parser tricks like with literal_pow, but that has its own issues.

1 Like

The FastPow.jl package will do this transformation.

2 Likes

k is not a constant here so it’s something other than constant propagation. I’m not sure where a -1 branch should be put if that’s the way to do it. It also occurs to me, the branch shouldn’t just be for -1, right; any negative value to the power of a k::Integer can check k for evenness to calculate the sign and compute the absolute value to that power, and so (-1)^k should boil down to 1^k with its own 1 branch.

I sometimes like to define ^ on the function - so that I can write (-)^n instead of (-1)^n and have it be efficient

julia> Base.:(^)(::typeof(-), n::Int) = iseven(n) ? 1 : (-1);

julia> Base.literal_pow(::typeof(^), ::typeof(-), ::Val{n}) where {n} = (-)^n;

julia> (-)^-1
-1

julia> (-)^-2
1

julia> (-)^100
1
Base.:(^)(::Val{-1},n)=(-1)^mod(n,2)
Base.:(^)(::Val{-1},n)=(-1)^(n & 1)