Skip to content

Constraints for AutoFiniteDiff #318

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Jul 9, 2022
Merged

Constraints for AutoFiniteDiff #318

merged 12 commits into from
Jul 9, 2022

Conversation

DanielVandH
Copy link
Member

@DanielVandH DanielVandH commented Jul 7, 2022

This defines a method for generating Jacobians and Hessians of the constraints when using AutoFiniteDiff (see also #130). Hopefully it's not too bad, I've left some comments in the code where I was unsure. I also have some comments below about issues I haven't been able to fix, that should probably be fixed before any action is taken on this pull request.

  • Line 50 of finitediff.jl: I had to take args... outside of the f.f call or else things were breaking. Any ideas why? You can run the AutoFiniteDiff section of the tests with args included to see the errors, e.g.
prob = OptimizationProblem(optprob, x0)
f = Optimization.instantiate_function(prob.f, prob.u0, prob.f.adtype, prob.p)
_f = (θ, args...) -> first(prob.f.f(θ, prob.p, args...))
_f(θ, args...)
ERROR: MethodError: no method matching rosenbrock(::Vector{Float64}, ::SciMLBase.NullParameters, ::SciMLBase.NullParameters)
Closest candidates are:
  rosenbrock(::Any, ::Any) at c:\Users\licer\.julia\dev\Optimization\test\ADtests.jl:6
  rosenbrock(::Any) at c:\Users\licer\.julia\dev\Optimization\test\ADtests.jl:6
Stacktrace:
 [1] (::OptimizationFunction{true, Main.Optimization.AutoFiniteDiff{Val{:forward}, Val{:forward}, Val{:hcentral}}, typeof(rosenbrock), Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing})(::Vector{Float64}, ::Vararg{Any})
   @ SciMLBase C:\Users\licer\.julia\packages\SciMLBase\IJbT7\src\scimlfunctions.jl:2887
 [2] (::var"#75#76")(θ::Vector{Float64}, args::SciMLBase.NullParameters)
   @ Main c:\Users\licer\.julia\dev\Optimization\test\ADtests.jl:167
 [3] top-level scope
   @ c:\Users\licer\.julia\dev\Optimization\test\ADtests.jl:168
  • Line 86 of finitediff.jl: I'm not sure I understand the way I should be using FiniteDiff.JacobianCache here. It was for some reason completely incompatible with the rectangular Jacobian tested in Line 166--180 of ADtests.jl, giving either an issue with m, n = size(J) not working for a vector J, or some parent error with _color = reshape(colorvec, axes(x)...).

  • Finally, I tried solving some problems, e.g. at the end of ADtests.jl I have

## Solving some problems 
#cons = (x, p) -> [x[1]^2 + x[2]^2]
#optf = OptimizationFunction(rosenbrock, Optimization.AutoFiniteDiff(); cons = cons)
#prob = OptimizationProblem(optf, x0, lcons = [0.2], rcons = [0.2])
#sol = solve(prob, Optim.BFGS()) ## not recognising gradients?

but optf was for some reason not recognising that the gradient is now defined. I figured I shouldn't tinker any further with this last problem since it's defined outside of Optimization.jl. Maybe there's a syntax error I just haven't recognised for whatever reason.

@codecov
Copy link

codecov bot commented Jul 7, 2022

Codecov Report

Merging #318 (9ed5aae) into master (a9be183) will increase coverage by 4.68%.
The diff coverage is 100.00%.

@@            Coverage Diff             @@
##           master     #318      +/-   ##
==========================================
+ Coverage   74.68%   79.37%   +4.68%     
==========================================
  Files           9        9              
  Lines         241      257      +16     
==========================================
+ Hits          180      204      +24     
+ Misses         61       53       -8     
Impacted Files Coverage Δ
src/function/finitediff.jl 100.00% <100.00%> (ø)
src/function/forwarddiff.jl 100.00% <0.00%> (+20.51%) ⬆️

📣 Codecov can now indicate which changes are the most critical in Pull Requests. Learn more

@ChrisRackauckas
Copy link
Member

For a non-square Jacobian, you just need to provide the cache with the output arrays so it knows how they are sized.

FiniteDiff.JacobianCache(
              x1 ,
              fx ,
              fx1,
              fdtype     :: Type{T1} = Val{:central},
              returntype :: Type{T2} = eltype(fx),
              colorvec = 1:length(x),
              sparsity = nothing)

provide it with the fx and fx1 sized as out.

@ChrisRackauckas
Copy link
Member

Line 50 of finitediff.jl: I had to take args... outside of the f.f call or else things were breaking. Any ideas why? You can run the AutoFiniteDiff section of the tests with args included to see the errors, e.g.

It needs access to the batch data. Dig through one step at a time, why does it get batch data as null parameters instead of the empty tuple?

@ChrisRackauckas
Copy link
Member

BFGS doesn't support constraints. Try IPNewton.

@DanielVandH
Copy link
Member Author

DanielVandH commented Jul 7, 2022

Line 50 of finitediff.jl: I had to take args... outside of the f.f call or else things were breaking. Any ideas why? You can run the AutoFiniteDiff section of the tests with args included to see the errors, e.g.

It needs access to the batch data. Dig through one step at a time, why does it get batch data as null parameters instead of the empty tuple?

This, for a reason that I'm not sure of, seems to just a problem with how I'm working with this repo I think. When I use ] test from the REPL and keep args..., it seems to work, so no problems here it seems.

For a non-square Jacobian, you just need to provide the cache with the output arrays so it knows how they are sized.

FiniteDiff.JacobianCache(
              x1 ,
              fx ,
              fx1,
              fdtype     :: Type{T1} = Val{:central},
              returntype :: Type{T2} = eltype(fx),
              colorvec = 1:length(x),
              sparsity = nothing)

provide it with the fx and fx1 sized as out.

Fixed now - thank you, I didn't realise fx and fx1 should be the same.

BFGS doesn't support constraints. Try IPNewton.

Doesn't seem to work, still. (The extra lb and ub were just to test if it's related to that at all, doesn't seem to be.)

julia> cons = (x, p) -> [x[1]^2 + x[2]^2]
       optf = OptimizationFunction(rosenbrock, Optimization.AutoFiniteDiff(); cons = cons)
       prob = OptimizationProblem(optf, x0, lcons = [0.2], rcons = [0.2], lb = [0.0, 0.0], ub = [1.0, 1.0])
       sol = solve(prob, IPNewton())
ERROR: This optimizer requires derivative definitions for nonlinear constraints. If the problem does not have nonlinear constraints, choose a different optimizer. Otherwise define the derivative for cons using OptimizationFunction either directly or automatically generate them with one of the autodiff backends
Stacktrace:
 [1] error(s::String)
   @ Base .\error.jl:35
 [2] ___solve(prob::OptimizationProblem{true, OptimizationFunction{true, Main.Optimization.AutoFiniteDiff{Val{:forward}, Val{:forward}, Val{:hcentral}}, typeof(rosenbrock), Nothing, Nothing, Nothing, var"#56#57", Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing}, Vector{Float64}, SciMLBase.NullParameters, Vector{Float64}, Vector{Float64}, Nothing, Nothing, Base.Pairs{Symbol, Vector{Float64}, Tuple{Symbol}, NamedTuple{(:rcons,), Tuple{Vector{Float64}}}}}, opt::IPNewton{typeof(Optim.backtrack_constrained_grad), Symbol}, data::Base.Iterators.Cycle{Tuple{Optimization.NullData}}; callback::Function, maxiters::Nothing, maxtime::Nothing, abstol::Nothing, reltol::Nothing, progress::Bool, kwargs::Base.Pairs{Symbol, Union{}, Tuple{}, NamedTuple{(), Tuple{}}})
   @ Main.OptimizationOptimJL c:\Users\licer\.julia\dev\Optimization\lib\OptimizationOptimJL\src\OptimizationOptimJL.jl:261
 [3] ___solve(prob::OptimizationProblem{true, OptimizationFunction{true, Main.Optimization.AutoFiniteDiff{Val{:forward}, Val{:forward}, Val{:hcentral}}, typeof(rosenbrock), Nothing, Nothing, Nothing, var"#56#57", Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing}, Vector{Float64}, SciMLBase.NullParameters, Vector{Float64}, Vector{Float64}, Nothing, Nothing, Base.Pairs{Symbol, Vector{Float64}, Tuple{Symbol}, NamedTuple{(:rcons,), Tuple{Vector{Float64}}}}}, opt::IPNewton{typeof(Optim.backtrack_constrained_grad), Symbol}, data::Base.Iterators.Cycle{Tuple{Optimization.NullData}})
   @ Main.OptimizationOptimJL c:\Users\licer\.julia\dev\Optimization\lib\OptimizationOptimJL\src\OptimizationOptimJL.jl:224
 [4] __solve(prob::OptimizationProblem{true, OptimizationFunction{true, Main.Optimization.AutoFiniteDiff{Val{:forward}, Val{:forward}, Val{:hcentral}}, typeof(rosenbrock), Nothing, Nothing, Nothing, var"#56#57", Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing}, Vector{Float64}, SciMLBase.NullParameters, Vector{Float64}, Vector{Float64}, Nothing, Nothing, Base.Pairs{Symbol, Vector{Float64}, Tuple{Symbol}, NamedTuple{(:rcons,), Tuple{Vector{Float64}}}}}, opt::IPNewton{typeof(Optim.backtrack_constrained_grad), Symbol}, data::Base.Iterators.Cycle{Tuple{Optimization.NullData}}; kwargs::Base.Pairs{Symbol, 
Union{}, Tuple{}, NamedTuple{(), Tuple{}}})
   @ Main.OptimizationOptimJL c:\Users\licer\.julia\dev\Optimization\lib\OptimizationOptimJL\src\OptimizationOptimJL.jl:56
 [5] __solve(prob::OptimizationProblem{true, OptimizationFunction{true, Main.Optimization.AutoFiniteDiff{Val{:forward}, Val{:forward}, Val{:hcentral}}, typeof(rosenbrock), Nothing, Nothing, Nothing, var"#56#57", Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing}, Vector{Float64}, SciMLBase.NullParameters, Vector{Float64}, Vector{Float64}, Nothing, Nothing, Base.Pairs{Symbol, Vector{Float64}, Tuple{Symbol}, NamedTuple{(:rcons,), Tuple{Vector{Float64}}}}}, opt::IPNewton{typeof(Optim.backtrack_constrained_grad), Symbol}, data::Base.Iterators.Cycle{Tuple{Optimization.NullData}}) (repeats 2 times)
   @ Main.OptimizationOptimJL c:\Users\licer\.julia\dev\Optimization\lib\OptimizationOptimJL\src\OptimizationOptimJL.jl:40
 [6] solve(::OptimizationProblem{true, OptimizationFunction{true, Main.Optimization.AutoFiniteDiff{Val{:forward}, Val{:forward}, Val{:hcentral}}, typeof(rosenbrock), Nothing, Nothing, Nothing, var"#56#57", Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing}, Vector{Float64}, SciMLBase.NullParameters, Vector{Float64}, Vector{Float64}, Nothing, Nothing, Base.Pairs{Symbol, Vector{Float64}, Tuple{Symbol}, NamedTuple{(:rcons,), Tuple{Vector{Float64}}}}}, ::IPNewton{typeof(Optim.backtrack_constrained_grad), Symbol}; kwargs::Base.Pairs{Symbol, Union{}, Tuple{}, NamedTuple{(), Tuple{}}})
   @ SciMLBase C:\Users\licer\.julia\packages\SciMLBase\IJbT7\src\solve.jl:71
 [7] solve(::OptimizationProblem{true, OptimizationFunction{true, Main.Optimization.AutoFiniteDiff{Val{:forward}, Val{:forward}, Val{:hcentral}}, typeof(rosenbrock), Nothing, Nothing, Nothing, var"#56#57", Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing}, Vector{Float64}, SciMLBase.NullParameters, Vector{Float64}, Vector{Float64}, Nothing, Nothing, Base.Pairs{Symbol, Vector{Float64}, Tuple{Symbol}, NamedTuple{(:rcons,), Tuple{Vector{Float64}}}}}, ::IPNewton{typeof(Optim.backtrack_constrained_grad), Symbol})
   @ SciMLBase C:\Users\licer\.julia\packages\SciMLBase\IJbT7\src\solve.jl:70
 [8] top-level scope
   @ c:\Users\licer\.julia\dev\Optimization\test\ADtests.jl:204

I will have to come back to this a bit later. All the tests do pass now though, except for this error.

@DanielVandH
Copy link
Member Author

DanielVandH commented Jul 8, 2022

Also: Should we be using the prototype/colorvec options from f in these caches? I initially chose not to because they were set back to nothing in ForwardDiff, so I kept that here, but thinking about it some more that seems wrong. edit: Working on this now to see.

@DanielVandH
Copy link
Member Author

Tested some problems now with IPNewton. I think this is all working now, no more changes incoming :).

test/ADtests.jl Outdated
@test sol1.u ≈ sol2.u

optf1 = OptimizationFunction(rosenbrock, Optimization.AutoFiniteDiff(); cons = consf)
prob1 = OptimizationProblem(optf, [0.3, 0.5], lb = [0.2, 0.4], ub = [0.6, 0.8], lcons = [0.2], ucons = [0.55])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The constraint's bounds here need to be different for the two elements, this is the failure on CI currently

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll fix that. Forgot that con2_c has two constraints in it when making this loop.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I see.

Co-authored-by: Vaibhav Kumar Dixit <[email protected]>
@ChrisRackauckas
Copy link
Member

JuliaRegistries/General#63877 inc.


if cons !== nothing && f.cons_h === nothing
cons_h = function (res, θ)
for i in 1:num_cons#note: colorvecs not yet supported by FiniteDiff for Hessians
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should start integrating/testing JuliaDiff/SparseDiffTools.jl#190 and then do similar to the FiniteDiff side (cc @ElOceanografo)

@ChrisRackauckas
Copy link
Member

I think the test issue is the only thing left. If with those bounds it passes then I think this is good.

@DanielVandH
Copy link
Member Author

I don't really understand why it's still failing? It works for me locally...

@ChrisRackauckas
Copy link
Member

Check that the dependencies are the same. It looks like it got FiniteDiff patch this time, but was there another patch you had somewhere?

@DanielVandH
Copy link
Member Author

DanielVandH commented Jul 8, 2022

Check that the dependencies are the same. It looks like it got FiniteDiff patch this time, but was there another patch you had somewhere?

Seemed to be related - it keeps giving me the old version without any of my changes and I didn't realise. Still learning how to use other packages like this 😅 Also forgot to use the correct optf's when I changed to the for loop and it seems to work now... hopefully the CI does too.

Well, it fails but at least it's not erroring. I'll have to come back to this a bit later and see why.

@DanielVandH
Copy link
Member Author

Should be all good now, finally.

@ChrisRackauckas
Copy link
Member

I'm not waiting on the DiffEqFlux downstream since it doesn't use AutoFiniteDiff anyways.

🎉

@ChrisRackauckas ChrisRackauckas merged commit fd343ab into SciML:master Jul 9, 2022
optf2 = OptimizationFunction(rosenbrock, Optimization.AutoForwardDiff(); cons = consf)
prob2 = OptimizationProblem(optf2, [0.3, 0.5], lb = [0.2, 0.4], ub = [0.6, 0.8], lcons = lcons, ucons = ucons)
sol2 = solve(prob2,IPNewton())
@test sol1.minimum ≈ sol2.minimum
sol2 = solve(prob2,Optim.SAMIN(), maxiters = 10000)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SAMIN doesn't use the constraints though?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But it doesn't error if constraints are given? Optim just lets them through?

Copy link
Member Author

@DanielVandH DanielVandH Jul 9, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought it did? Based on what is given here http://optimization.sciml.ai/stable/optimization_packages/optim/#With-Constraint-Equations "The following method in Optim is performing global optimization on problems with constraint equation"

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did miss that SAMIN is derivative-free though (PSO too), so we should probably mark that in the docs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants