From cdc4a67c3dc0e4f38f6a2d2486761b5159d0f7cc Mon Sep 17 00:00:00 2001 From: jClugstor Date: Mon, 27 Oct 2025 21:27:02 -0400 Subject: [PATCH 1/6] use solve_up for SciMLSensitivity integration --- lib/OptimizationBase/src/OptimizationBase.jl | 5 +- lib/OptimizationBase/src/solve.jl | 133 +++++++++++++++++-- 2 files changed, 127 insertions(+), 11 deletions(-) diff --git a/lib/OptimizationBase/src/OptimizationBase.jl b/lib/OptimizationBase/src/OptimizationBase.jl index 230f5bca8..b36035fb7 100644 --- a/lib/OptimizationBase/src/OptimizationBase.jl +++ b/lib/OptimizationBase/src/OptimizationBase.jl @@ -13,7 +13,10 @@ import SciMLBase: solve, init, solve!, __init, __solve, allowsconstraints, requiresconstraints, allowscallback, requiresgradient, requireshessian, requiresconsjac, - requiresconshess + requiresconshess, wrap_sol, has_kwargs, + get_root_indp, get_updated_symbolic_problem, + get_concrete_p, get_concrete_u0, promote_u0, + promote_p, KeywordArgError, checkkwargs export ObjSense, MaxSense, MinSense export allowsbounds, requiresbounds, allowsconstraints, requiresconstraints, diff --git a/lib/OptimizationBase/src/solve.jl b/lib/OptimizationBase/src/solve.jl index 777ca1d8f..7c581aace 100644 --- a/lib/OptimizationBase/src/solve.jl +++ b/lib/OptimizationBase/src/solve.jl @@ -90,18 +90,33 @@ from NLopt for an example. The common local optimizer arguments are: - `local_reltol`: relative tolerance in changes of the objective value - `local_options`: `NamedTuple` of keyword arguments for local optimizer """ -function solve(prob::SciMLBase.OptimizationProblem, alg, args...; - kwargs...)::SciMLBase.AbstractOptimizationSolution - if SciMLBase.has_init(alg) - solve!(init(prob, alg, args...; kwargs...)) +function solve(prob::SciMLBase.OptimizationProblem, args...; sensealg = nothing, + u0 = nothing, p = nothing, wrap = Val(true), kwargs...)::SciMLBase.AbstractOptimizationSolution + if sensealg === nothing && haskey(prob.kwargs, :sensealg) + sensealg = prob.kwargs[:sensealg] + end + + u0 = u0 !== nothing ? u0 : prob.u0 + p = p !== nothing ? p : prob.p + + if wrap isa Val{true} + wrap_sol(solve_up(prob, + sensealg, + u0, + p, + args...; + originator = SciMLBase.ChainRulesOriginator(), + kwargs...)) else - if prob.u0 !== nothing && !isconcretetype(eltype(prob.u0)) - throw(SciMLBase.NonConcreteEltypeError(eltype(prob.u0))) - end - _check_opt_alg(prob, alg; kwargs...) - __solve(prob, alg, args...; kwargs...) + solve_up(prob, + sensealg, + u0, + p, + args...; + originator = SciMLBase.ChainRulesOrginator(), + kwargs...) end -end +end function solve( prob::SciMLBase.EnsembleProblem{T}, args...; kwargs...) where {T <: @@ -216,3 +231,101 @@ end function __solve(prob::SciMLBase.OptimizationProblem, alg, args...; kwargs...) throw(OptimizerMissingError(alg)) end + +function solve_up(prob::SciMLBase.OptimizationProblem, sensealg, u0, p, args...; originator = SciMLBase.ChainRulesOriginator(), + kwargs...) + alg = extract_opt_alg(args, kwargs, has_kwargs(prob) ? prob.kwargs : kwargs) + _prob = get_concrete_problem(prob; u0 = u0, p = p, kwargs...) + if length(args) < 1 + solve_call(_prob, alg, Base.tails(args)..., kwargs...) + else + solve_call(_prob, alg; kwargs...) + end +end + +function solve_call(_prob, alg, args...; merge_callbacks = true, kwargshandle = nothing, + kwargs...) + kwargshandle = kwargshandle === nothing ? KeywordArgError : kwargshandle + kwargshandle = has_kwargs(_prob) && haskey(_prob.kwargs, :kwargshandle) ? + _prob.kwargs[:kwargshandle] : kwargshandle + + if has_kwargs(_prob) + kwargs = isempty(_prob.kwargs) ? kwargs : merge(values(_prob.kwargs), kwargs) + end + + checkkwargs(kwargshandle; kwargs...) + + if SciMLBase.has_init(alg) + solve!(init(_prob, alg, args...; kwargs...)) + else + if _prob.u0 !== nothing && !isconcretetype(eltype(_prob.u0)) + throw(SciMLBase.NonConcreteEltypeError(eltype(_prob.u0))) + end + _check_opt_alg(prob, alg; kwargs...) + __solve(_prob, alg, args...; kwargs...) + end +end + +function get_concrete_problem(prob::OptimizationProblem; kwargs...) + oldprob = prob + prob = get_updated_symbolic_problem(get_root_indp(prob), prob; kwargs...) + if prob !== oldprob + kwargs = (;kwargs..., u0 = SII.state_values(prob), p = SII.parameter_values(prob)) + end + p = get_concrete_p(prob, kwargs) + u0 = get_concrete_u0(prob, false, nothing, kwargs) + u0 = promote_u0(u0, p, nothing) + remake(prob; u0 = u0, p = p) + +end + + +@inline function extract_opt_alg(solve_args, solve_kwargs, prob_kwargs) + if isempty(solve_args) || isnothing(first(solve_args)) + if haskey(solve_kwargs, :alg) + solve_kwargs[:alg] + elseif haskey(prob_kwargs, :alg) + prob_kwargs[:alg] + else + nothing + end + else + first(solve_args) + end +end + + +function _solve_forward(prob, sensealg, u0, p, originator, args...; merge_callbacks = true, + kwargs...) + alg = extract_opt_alg(args, kwargs, prob.kwargs) + _prob = get_concrete_problem(prob; u0 = u0, p = p, kwargs...) + + if has_kwargs(_prob) + kwargs = isempty(_porb.kwargs) ? kwargs : merge(values(_prob.kwargs), kwargs) + end + + if length(args) > 1 + _concrete_solve_forward(_prob, alg, sensealg, u0, p, originator, + Base.tail(args)...; kwargs...) + else + _concrete_solve_forward(_prob, alg, sensealg, u0, p, originator; kwargs...) + end +end + +function _solve_adjoint(_prob, sensealg, u0, p, originator, args...; merge_callbacks = true, + kwargs...) + alg = extract_alg(args, kwargs, prob.kwargs) + + _prob = get_concrete_problem(prob; u0 = u0, p = p, kwargs...) + + if has_kwargs(_prob) + kwargs = isempty(_prob.kwargs) ? kwargs : merge(values(_prob.kwargs), kwargs) + end + + if length(args) > 1 + _concrete_solve_adjoint(_prob, alg, sensealg, u0, p, originator, + Base.tail(args)...; kwargs...) + else + _concrete_solve_adjoint(_prob, alg, sensealg, u0, p, originator; kwargs...) + end +end \ No newline at end of file From 986f9461373c541b2d07e0dba845b5d87375778d Mon Sep 17 00:00:00 2001 From: jClugstor Date: Tue, 28 Oct 2025 11:41:51 -0400 Subject: [PATCH 2/6] remove kwarg checking --- lib/OptimizationBase/src/solve.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/OptimizationBase/src/solve.jl b/lib/OptimizationBase/src/solve.jl index 7c581aace..67356f0bd 100644 --- a/lib/OptimizationBase/src/solve.jl +++ b/lib/OptimizationBase/src/solve.jl @@ -253,7 +253,7 @@ function solve_call(_prob, alg, args...; merge_callbacks = true, kwargshandle = kwargs = isempty(_prob.kwargs) ? kwargs : merge(values(_prob.kwargs), kwargs) end - checkkwargs(kwargshandle; kwargs...) + #checkkwargs(kwargshandle; kwargs...) if SciMLBase.has_init(alg) solve!(init(_prob, alg, args...; kwargs...)) From 09c3311a899c16036ae7e30f6aa86c8bfd1b5911 Mon Sep 17 00:00:00 2001 From: jClugstor Date: Tue, 28 Oct 2025 12:33:02 -0400 Subject: [PATCH 3/6] fix prob typo --- lib/OptimizationBase/src/solve.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/OptimizationBase/src/solve.jl b/lib/OptimizationBase/src/solve.jl index 67356f0bd..daef12d0b 100644 --- a/lib/OptimizationBase/src/solve.jl +++ b/lib/OptimizationBase/src/solve.jl @@ -261,7 +261,7 @@ function solve_call(_prob, alg, args...; merge_callbacks = true, kwargshandle = if _prob.u0 !== nothing && !isconcretetype(eltype(_prob.u0)) throw(SciMLBase.NonConcreteEltypeError(eltype(_prob.u0))) end - _check_opt_alg(prob, alg; kwargs...) + _check_opt_alg(_prob, alg; kwargs...) __solve(_prob, alg, args...; kwargs...) end end From 3f94e9cbdb7ce1b368cddcf3359095aa02dfb17c Mon Sep 17 00:00:00 2001 From: jClugstor Date: Thu, 30 Oct 2025 12:45:43 -0400 Subject: [PATCH 4/6] SciMLBase.promote_p doesn't exist --- lib/OptimizationBase/src/OptimizationBase.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/OptimizationBase/src/OptimizationBase.jl b/lib/OptimizationBase/src/OptimizationBase.jl index b36035fb7..6d3d56e6c 100644 --- a/lib/OptimizationBase/src/OptimizationBase.jl +++ b/lib/OptimizationBase/src/OptimizationBase.jl @@ -16,7 +16,7 @@ import SciMLBase: solve, init, solve!, __init, __solve, requiresconshess, wrap_sol, has_kwargs, get_root_indp, get_updated_symbolic_problem, get_concrete_p, get_concrete_u0, promote_u0, - promote_p, KeywordArgError, checkkwargs + KeywordArgError, checkkwargs export ObjSense, MaxSense, MinSense export allowsbounds, requiresbounds, allowsconstraints, requiresconstraints, From 949b8c6dca1ae24abaef36558f3752f827a8c588 Mon Sep 17 00:00:00 2001 From: jClugstor Date: Thu, 30 Oct 2025 14:12:53 -0400 Subject: [PATCH 5/6] add SII --- Project.toml | 9 +++------ lib/OptimizationBase/Project.toml | 3 +++ lib/OptimizationBase/src/OptimizationBase.jl | 2 ++ lib/OptimizationBase/src/solve.jl | 2 +- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/Project.toml b/Project.toml index 809611303..daac7440a 100644 --- a/Project.toml +++ b/Project.toml @@ -86,9 +86,10 @@ IterTools = "c8e1da08-722c-5040-9ed9-7db0dc04731e" Lux = "b2108857-7c20-44ae-9111-449ecde12c47" MLUtils = "f1d291b0-491e-4a28-83b9-f70985020b54" ModelingToolkit = "961ee093-0014-501f-94e3-6117800e7a78" +Mooncake = "da2b9cff-9c12-43a0-ae48-6db2b0edb7d6" Optim = "429524aa-4258-5aef-a3af-852621145aeb" -OptimizationLBFGSB = "22f7324a-a79d-40f2-bebe-3af60c77bd15" Optimisers = "3bd65402-5787-11e9-1adc-39752487f4e2" +OptimizationLBFGSB = "22f7324a-a79d-40f2-bebe-3af60c77bd15" OptimizationMOI = "fd9f6733-72f4-499f-8506-86b2bdd0dea1" OptimizationOptimJL = "36348300-93cb-4f02-beb5-3c3902f8871e" OptimizationOptimisers = "42dfb2eb-d2b4-4451-abcd-913932933ac1" @@ -103,10 +104,6 @@ Symbolics = "0c5d862f-8b57-4792-8d23-62f2024744c7" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" Tracker = "9f7883ad-71c0-57eb-9f7f-b5c9e6d3789c" Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f" -Mooncake = "da2b9cff-9c12-43a0-ae48-6db2b0edb7d6" [targets] -test = ["Aqua", "BenchmarkTools", "Boltz", "ComponentArrays", "DiffEqFlux", "Enzyme", "FiniteDiff", "Flux", "ForwardDiff", - "Ipopt", "IterTools", "Lux", "MLUtils", "ModelingToolkit", "Optim", "OptimizationLBFGSB", "OptimizationMOI", "OptimizationOptimJL", "OptimizationOptimisers", - "OrdinaryDiffEqTsit5", "Pkg", "Random", "ReverseDiff", "SafeTestsets", "SciMLSensitivity", "SparseArrays", - "Symbolics", "Test", "Tracker", "Zygote", "Mooncake"] +test = ["Aqua", "BenchmarkTools", "Boltz", "ComponentArrays", "DiffEqFlux", "Enzyme", "FiniteDiff", "Flux", "ForwardDiff", "Ipopt", "IterTools", "Lux", "MLUtils", "ModelingToolkit", "Optim", "OptimizationLBFGSB", "OptimizationMOI", "OptimizationOptimJL", "OptimizationOptimisers", "OrdinaryDiffEqTsit5", "Pkg", "Random", "ReverseDiff", "SafeTestsets", "SciMLSensitivity", "SparseArrays", "Symbolics", "Test", "Tracker", "Zygote", "Mooncake"] diff --git a/lib/OptimizationBase/Project.toml b/lib/OptimizationBase/Project.toml index a90d299ea..cfa148634 100644 --- a/lib/OptimizationBase/Project.toml +++ b/lib/OptimizationBase/Project.toml @@ -1,5 +1,6 @@ name = "OptimizationBase" uuid = "bca83a33-5cc9-4baa-983d-23429ab6bcbb" +version = "4.0.1" authors = ["Vaibhav Dixit and contributors"] version = "4.0.2" @@ -16,6 +17,7 @@ SciMLBase = "0bca4576-84f4-4d90-8ffe-ffa030f20462" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" SparseConnectivityTracer = "9f842d2f-2579-4b1d-911e-f412cf18a3f5" SparseMatrixColorings = "0a514795-09f3-496d-8182-132a7b665d35" +SymbolicIndexingInterface = "2efcf032-c050-4f8e-a9bb-153293bab1f5" [weakdeps] Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9" @@ -59,6 +61,7 @@ SciMLBase = "2.122.1" SparseConnectivityTracer = "0.6, 1" SparseMatrixColorings = "0.4" SymbolicAnalysis = "0.3" +SymbolicIndexingInterface = "0.3.46" Zygote = "0.6.67, 0.7" julia = "1.10" diff --git a/lib/OptimizationBase/src/OptimizationBase.jl b/lib/OptimizationBase/src/OptimizationBase.jl index 6d3d56e6c..b6de9be3c 100644 --- a/lib/OptimizationBase/src/OptimizationBase.jl +++ b/lib/OptimizationBase/src/OptimizationBase.jl @@ -18,6 +18,8 @@ import SciMLBase: solve, init, solve!, __init, __solve, get_concrete_p, get_concrete_u0, promote_u0, KeywordArgError, checkkwargs +using SymbolicIndexingInterface: SymbolicIndexingInterface + export ObjSense, MaxSense, MinSense export allowsbounds, requiresbounds, allowsconstraints, requiresconstraints, allowscallback, requiresgradient, requireshessian, diff --git a/lib/OptimizationBase/src/solve.jl b/lib/OptimizationBase/src/solve.jl index daef12d0b..77d179e93 100644 --- a/lib/OptimizationBase/src/solve.jl +++ b/lib/OptimizationBase/src/solve.jl @@ -270,7 +270,7 @@ function get_concrete_problem(prob::OptimizationProblem; kwargs...) oldprob = prob prob = get_updated_symbolic_problem(get_root_indp(prob), prob; kwargs...) if prob !== oldprob - kwargs = (;kwargs..., u0 = SII.state_values(prob), p = SII.parameter_values(prob)) + kwargs = (;kwargs..., u0 = SymbolicIndexingInterface.state_values(prob), p = SymbolicIndexingInterface.parameter_values(prob)) end p = get_concrete_p(prob, kwargs) u0 = get_concrete_u0(prob, false, nothing, kwargs) From 71fa9e17ba1802c0f47e4673853c60ca3577a010 Mon Sep 17 00:00:00 2001 From: jClugstor Date: Thu, 30 Oct 2025 14:41:36 -0400 Subject: [PATCH 6/6] fix solveup typos --- lib/OptimizationBase/src/solve.jl | 4 ++-- .../test/runtests.jl | 10 ++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/lib/OptimizationBase/src/solve.jl b/lib/OptimizationBase/src/solve.jl index 77d179e93..47bb929cf 100644 --- a/lib/OptimizationBase/src/solve.jl +++ b/lib/OptimizationBase/src/solve.jl @@ -236,8 +236,8 @@ function solve_up(prob::SciMLBase.OptimizationProblem, sensealg, u0, p, args...; kwargs...) alg = extract_opt_alg(args, kwargs, has_kwargs(prob) ? prob.kwargs : kwargs) _prob = get_concrete_problem(prob; u0 = u0, p = p, kwargs...) - if length(args) < 1 - solve_call(_prob, alg, Base.tails(args)..., kwargs...) + if length(args) > 1 + solve_call(_prob, alg, Base.tail(args)..., kwargs...) else solve_call(_prob, alg; kwargs...) end diff --git a/lib/OptimizationMultistartOptimization/test/runtests.jl b/lib/OptimizationMultistartOptimization/test/runtests.jl index b321cea1f..e7190f4a9 100644 --- a/lib/OptimizationMultistartOptimization/test/runtests.jl +++ b/lib/OptimizationMultistartOptimization/test/runtests.jl @@ -12,3 +12,13 @@ using Test, ReverseDiff OptimizationNLopt.Opt(:LD_LBFGS, 2)) @test 10 * sol.objective < l1 end + +rosenbrock(x, p) = (p[1] - x[1])^2 + p[2] * (x[2] - x[1]^2)^2 +x0 = zeros(2) +_p = [1.0, 100.0] +l1 = rosenbrock(x0, _p) +f = OptimizationFunction(rosenbrock, OptimizationBase.AutoForwardDiff()) +prob = OptimizationBase.OptimizationProblem(f, x0, _p, lb = [-1.0, -1.0], ub = [1.5, 1.5]) +sol = solve(prob, OptimizationMultistartOptimization.TikTak(100), + OptimizationNLopt.Opt(:LD_LBFGS, 2)) +@test 10 * sol.objective < l1 \ No newline at end of file