Skip to content

Closures capture storage in extras #252

@timholy

Description

@timholy

If you prepare a project with both DifferentiationInterface and ForwardDiff:

using DifferentiationInterface
using ForwardDiff

struct FC{F,C,LT}
    f::F
    c::C
    Lx::LT

    function FC(f, c)
        # Define the Lagrange function.
        # Compared to defining it locally in downstream "consumer" functions,
        # this ensures it's the same function-type each time. Aside from
        # reducing useless re-compilation, the main impact is that we can use
        # DifferentiationInterface's `prepare_*` functions to get better autodiff
        # performance.
        function Lx(λ)
            x -> f(x) - λ'*c(x)
        end
        new{typeof(f),typeof(c),typeof(Lx)}(f, c, Lx)
    end
end

# Powell's Maratos-effect test problem
f(x) = 2 * (x[1]^2 + x[2]^2 - 1) - x[1]
c(x) = [x[1]^2 + x[2]^2 - 1]
fc = FC(f, c)

θ = π/12
x = [cos(θ), sin(θ)]
λ = [3/2]
λprep = [0.2]

backend = SecondOrder(AutoForwardDiff(), AutoForwardDiff())
extrasL = prepare_hessian(fc.Lx(λprep), backend, x)
H1 = hessian(fc.Lx(λ), backend, x)
H2 = hessian(fc.Lx(λ), backend, x, extrasL)
copyto!(λprep, λ)
H3 = hessian(fc.Lx(λ), backend, x, extrasL)
@assert H1  H2
@assert H1  H3

yields


julia> H1 = hessian(fc.Lx(λ), backend, x)
2×2 Matrix{Float64}:
 1.0  0.0
 0.0  1.0

julia> H2 = hessian(fc.Lx(λ), backend, x, extrasL)
2×2 Matrix{Float64}:
 3.6  0.0
 0.0  3.6

julia> copyto!(λprep, λ)
1-element Vector{Float64}:
 1.5

julia> H3 = hessian(fc.Lx(λ), backend, x, extrasL)
2×2 Matrix{Float64}:
 1.0  0.0
 0.0  1.0

julia> @assert H1  H2
ERROR: AssertionError: H1  H2
Stacktrace:
 [1] top-level scope
   @ REPL[55]:1

julia> @assert H1  H3

The issue seems to be that the specific storage in λprep somehow gets captured in extrasL, corrupting the answer.

I've also tried the following:

    # backend = SecondOrder(AutoEnzyme(Enzyme.Forward), AutoEnzyme(Enzyme.Reverse))
    # backend = SecondOrder(AutoForwardDiff(), AutoEnzyme(Enzyme.Reverse))
    # backend = SecondOrder(AutoForwardDiff(), AutoZygote())

Enzyme fails outright (ERROR: Attempting to call an indirect active function whose runtime value is inactive...). Zygote gives the same numeric error.

Metadata

Metadata

Assignees

No one assigned

    Labels

    coreRelated to the core utilities of the packagewontfixThis will not be worked on

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions