Skip to content

Commit 16265b0

Browse files
Use live parameters in sparse constraint Jacobians
Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
1 parent be6fbd2 commit 16265b0

3 files changed

Lines changed: 115 additions & 12 deletions

File tree

lib/OptimizationBase/src/OptimizationDISparseExt.jl

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ function instantiate_function(
2020
lag_h = false
2121
)
2222
adtype, soadtype = generate_sparse_adtype(adtype)
23+
Tx0 = typeof(x)
24+
Tp0 = typeof(p)
2325

2426
if g == true && f.grad === nothing
2527
prep_grad = prepare_gradient(f.f, adtype.dense_ad, x, Constant(p))
@@ -167,17 +169,31 @@ function instantiate_function(
167169
cons_jac_prototype = f.cons_jac_prototype
168170
cons_jac_colorvec = f.cons_jac_colorvec
169171
if f.cons !== nothing && cons_j == true && f.cons_j === nothing
170-
prep_jac = prepare_jacobian(cons_oop, adtype, x)
171-
function cons_j!(J, θ)
172-
jacobian!(cons_oop, J, prep_jac, adtype, θ)
173-
return if size(J, 1) == 1
174-
J = vec(J)
172+
cons_oop_p = let f = f, num_cons = num_cons
173+
function (x, p)
174+
res = Vector{_cons_out_eltype(x, p)}(undef, num_cons)
175+
f.cons(res, x, p)
176+
return res
177+
end
178+
end
179+
prep_jac = prepare_jacobian(cons_oop_p, adtype, x, Constant(p))
180+
cons_j! = let cons_oop_p = cons_oop_p, prep_jac = prep_jac,
181+
adtype = adtype, p = p, Tx0 = Tx0, Tp0 = Tp0
182+
function (J, θ, p = p)
183+
if _prep_valid(Tx0, θ) && _prep_valid(Tp0, p)
184+
jacobian!(cons_oop_p, J, prep_jac, adtype, θ, Constant(p))
185+
else
186+
jacobian!(cons_oop_p, J, adtype, θ, Constant(p))
187+
end
188+
return size(J, 1) == 1 ? vec(J) : J
175189
end
176190
end
177191
cons_jac_prototype = prep_jac.coloring_result.A
178192
cons_jac_colorvec = prep_jac.coloring_result.color
179193
elseif cons_j === true && f.cons !== nothing
180-
cons_j! = (J, θ) -> f.cons_j(J, θ, p)
194+
cons_j! = let f = f, p = p
195+
(J, θ, p = p) -> f.cons_j(J, θ, p)
196+
end
181197
else
182198
cons_j! = nothing
183199
end
@@ -339,6 +355,8 @@ function instantiate_function(
339355
lag_h = false
340356
)
341357
adtype, soadtype = generate_sparse_adtype(adtype)
358+
Tx0 = typeof(x)
359+
Tp0 = typeof(p)
342360

343361
if g == true && f.grad === nothing
344362
prep_grad = prepare_gradient(f.f, adtype.dense_ad, x, Constant(p))
@@ -462,17 +480,23 @@ function instantiate_function(
462480
cons_jac_colorvec = f.cons_jac_colorvec
463481
if f.cons !== nothing && cons_j == true && f.cons_j === nothing
464482
prep_jac = prepare_jacobian(f.cons, adtype, x, Constant(p))
465-
function cons_j!(θ)
466-
J = jacobian(f.cons, prep_jac, adtype, θ, Constant(p))
467-
if size(J, 1) == 1
468-
J = vec(J)
483+
cons_j! = let f = f, prep_jac = prep_jac, adtype = adtype,
484+
p = p, Tx0 = Tx0, Tp0 = Tp0
485+
function (θ, p = p)
486+
J = if _prep_valid(Tx0, θ) && _prep_valid(Tp0, p)
487+
jacobian(f.cons, prep_jac, adtype, θ, Constant(p))
488+
else
489+
jacobian(f.cons, adtype, θ, Constant(p))
490+
end
491+
return size(J, 1) == 1 ? vec(J) : J
469492
end
470-
return J
471493
end
472494
cons_jac_prototype = prep_jac.coloring_result.A
473495
cons_jac_colorvec = prep_jac.coloring_result.color
474496
elseif cons_j === true && f.cons !== nothing
475-
cons_j! = (θ) -> f.cons_j(θ, p)
497+
cons_j! = let f = f, p = p
498+
(θ, p = p) -> f.cons_j(θ, p)
499+
end
476500
else
477501
cons_j! = nothing
478502
end

lib/OptimizationBase/test/AD/adtests.jl

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -911,6 +911,48 @@ end
911911
@test nnz(lag_H) == 5
912912
end
913913

914+
@testset "sparse constraint Jacobians use live parameters" begin
915+
objective(x, p) = sum(abs2, x)
916+
constraints(x, p) = [p[1] * x[1]]
917+
function constraints!(res, x, p)
918+
res[1] = p[1] * x[1]
919+
return nothing
920+
end
921+
constraint_jacobian(x, p) = sparse([1], [1], [p[1]], 1, 2)
922+
function constraint_jacobian!(J, x, p)
923+
J[1, 1] = p[1]
924+
return nothing
925+
end
926+
927+
x = [1.0, 1.0]
928+
initial_p = [2.0]
929+
live_p = [3.0]
930+
931+
@testset "in-place = $iip, analytic = $analytic" for iip in (false, true),
932+
analytic in (false, true)
933+
jacobian_kwargs = analytic ?
934+
(;
935+
cons_j = iip ? constraint_jacobian! : constraint_jacobian,
936+
cons_jac_prototype = sparse([1], [1], [1.0], 1, 2),
937+
) : (;)
938+
optf = OptimizationFunction{iip}(
939+
objective, AutoSparse(AutoForwardDiff());
940+
cons = iip ? constraints! : constraints, jacobian_kwargs...
941+
)
942+
optprob = OptimizationBase.instantiate_function(
943+
optf, x, optf.adtype, initial_p, 1; cons_j = true
944+
)
945+
946+
if iip
947+
J = similar(optprob.cons_jac_prototype, Float64)
948+
optprob.cons_j(J, x, live_p)
949+
@test vec(Array(J)) == [3.0, 0.0]
950+
else
951+
@test vec(Array(optprob.cons_j(x, live_p))) == [3.0, 0.0]
952+
end
953+
end
954+
end
955+
914956
@testset "OOP" begin
915957
cons = (x, p) -> [x[1]^2 + x[2]^2]
916958
optf = OptimizationFunction{false}(

lib/OptimizationIpopt/test/core_tests.jl

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,43 @@ end
7171
end
7272
end
7373

74+
@testset "sparse constraint Jacobian with parameters" begin
75+
objective(x, p) = sum(abs2, x)
76+
function constraints!(res, x, p)
77+
res[1] = p[1] * x[1]
78+
return nothing
79+
end
80+
function constraint_jacobian!(J, x, p)
81+
J[1, 1] = p[1]
82+
return nothing
83+
end
84+
85+
@testset "analytic = $analytic" for analytic in (false, true)
86+
jacobian_kwargs = analytic ?
87+
(;
88+
cons_j = constraint_jacobian!,
89+
cons_jac_prototype = sparse([1], [1], [1.0], 1, 2),
90+
) : (;)
91+
f = OptimizationFunction{true}(
92+
objective, AutoSparse(AutoForwardDiff());
93+
cons = constraints!, jacobian_kwargs...
94+
)
95+
prob = OptimizationProblem(
96+
f, [1.0, 1.0], [2.0]; lcons = [0.0], ucons = [0.0]
97+
)
98+
sol = solve(
99+
prob,
100+
IpoptOptimizer(;
101+
hessian_approximation = "limited-memory",
102+
additional_options = Dict{String, Any}("print_level" => 0)
103+
)
104+
)
105+
106+
@test SciMLBase.successful_retcode(sol)
107+
@test isapprox(sol.u[1], 0.0; atol = 1.0e-8)
108+
end
109+
end
110+
74111
include("additional_tests.jl")
75112
include("advanced_features.jl")
76113
include("problem_types.jl")

0 commit comments

Comments
 (0)