Skip to content

Error differentiating through complex power due to isinteger #637

Open
@tom-plaa

Description

@tom-plaa

I have a weird case where a complex power is giving me an inexact error because the _cpow helper function determines my object to be an integer.

Truncated error message:

julia> ForwardDiff.gradient(test_func, ptest)
ERROR: InexactError: Int(Int64, Dual{ForwardDiff.Tag{typeof(test_func), Float64}}(1.0,-0.0,-0.8333333333333333,-0.0,-0.0,-0.17599538919114094))
Stacktrace:
  [1] Int64
    @ ~/.julia/packages/ForwardDiff/QdStj/src/dual.jl:364 [inlined]
  [2] convert(#unused#::Type{Int64}, x::ForwardDiff.Dual{ForwardDiff.Tag{typeof(test_func), Float64}, Float64, 5})                                  
    @ Base ./number.jl:7
  [3] _cpow(z::Complex{ForwardDiff.Dual{ForwardDiff.Tag{typeof(test_func), Float64}, Float64, 5}}, p::ForwardDiff.Dual{ForwardDiff.Tag{typeof(test_func), Float64}, Float64, 5})
    @ Base ./complex.jl:782
  [4] ^
    @ ./complex.jl:851 [inlined]
  [5] ^
    @ ./complex.jl:866 [inlined]

So given that

julia> typeof(test_func)
typeof(test_func) (singleton type of function test_func, subtype of Function)

It seems that we can get the following MWE:

julia> isinteger(Dual{ForwardDiff.Tag{Function, Float64}}(1.0,-0.0,-0.8333333333333333,-0.0,-0.0,-0.17599538919114094))
true

This is the associated culprit line in complex.jl that tries to do this conversion to Int (only the relevant part is shown):

# _cpow helper function to avoid method ambiguity with ^(::Complex,::Real)
function _cpow(z::Union{T,Complex{T}}, p::Union{T,Complex{T}}) where T
    z = float(z)
    p = float(p)
    Tf = float(T)
    if isreal(p)
        pᵣ = real(p)
        if isinteger(pᵣ) && abs(pᵣ) < typemax(Int32) 
            # |p| < typemax(Int32) serves two purposes: it prevents overflow
            # when converting p to Int, and it also turns out to be roughly
            # the crossover point for exp(p*log(z)) or similar to be faster.
            if iszero(pᵣ) # fix signs of imaginary part for z^0
                zer = flipsign(copysign(zero(Tf),pᵣ), imag(z))
                return Complex(one(Tf), zer)
            end
            ip = convert(Int, pᵣ) # <--- HERE!

Admittedly, I am not sure if the partials should be considered for determining if it's an integer or not.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions