Skip to content

Commit db75171

Browse files
format and bump
1 parent acc57e0 commit db75171

4 files changed

Lines changed: 90 additions & 36 deletions

File tree

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "DiffOpt"
22
uuid = "930fe3bc-9c6b-11ea-2d94-6184641e85e7"
33
authors = ["Akshay Sharma", "Mathieu Besançon", "Joaquim Dias Garcia", "Benoît Legat", "Oscar Dowson", "Andrew Rosemberg"]
4-
version = "0.5.4"
4+
version = "0.5.6"
55

66
[deps]
77
BlockDiagonals = "0a1fb500-61f7-11e9-3c65-f5ef3456f9f0"

src/NonLinearProgram/vno_bridge.jl

Lines changed: 80 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,16 @@ function _unwrap_to_form(m)
3131
elseif hasfield(T, :optimizer)
3232
return _unwrap_to_form(getfield(m, :optimizer))
3333
end
34-
error("VectorNonlinearOracle bridge could not unwrap optimizer to NonLinearProgram.Form; got $(typeof(m))")
34+
return error(
35+
"VectorNonlinearOracle bridge could not unwrap optimizer to NonLinearProgram.Form; got $(typeof(m))",
36+
)
3537
end
3638

3739
_nl_model(m) = _unwrap_to_form(m).model # this is the MOI.Nonlinear.Model
3840

39-
_vno_op_symbol(base_id::Int, row::Int) = Symbol(:_diffopt_vno_, base_id, :_, row)
41+
function _vno_op_symbol(base_id::Int, row::Int)
42+
return Symbol(:_diffopt_vno_, base_id, :_, row)
43+
end
4044

4145
# --------------------------------------------------------------------------
4246
# Register a scalar operator for one row of the vector oracle:
@@ -86,7 +90,7 @@ function _register_univariate_vno_row_operator!(
8690
row::Int,
8791
) where {T<:Real}
8892
m = s.output_dimension
89-
93+
9094
f = function (x::T)
9195
ret = Vector{T}(undef, m)
9296
s.eval_f(ret, [x])
@@ -192,9 +196,13 @@ end
192196
struct VNOToScalarNLBridge{T<:Real} <: _B.Constraint.AbstractBridge
193197
f::MOI.VectorOfVariables
194198
s::MOI.VectorNonlinearOracle{T}
195-
leq::Vector{MOI.ConstraintIndex{MOI.ScalarNonlinearFunction, MOI.LessThan{T}}}
196-
geq::Vector{MOI.ConstraintIndex{MOI.ScalarNonlinearFunction, MOI.GreaterThan{T}}}
197-
eq::Vector{MOI.ConstraintIndex{MOI.ScalarNonlinearFunction, MOI.EqualTo{T}}}
199+
leq::Vector{
200+
MOI.ConstraintIndex{MOI.ScalarNonlinearFunction,MOI.LessThan{T}},
201+
}
202+
geq::Vector{
203+
MOI.ConstraintIndex{MOI.ScalarNonlinearFunction,MOI.GreaterThan{T}},
204+
}
205+
eq::Vector{MOI.ConstraintIndex{MOI.ScalarNonlinearFunction,MOI.EqualTo{T}}}
198206
end
199207

200208
function MOI.supports_constraint(
@@ -213,7 +221,9 @@ function _B.Constraint.concrete_bridge_type(
213221
return VNOToScalarNLBridge{T}
214222
end
215223

216-
function _B.added_constrained_variable_types(::Type{VNOToScalarNLBridge{T}}) where {T}
224+
function _B.added_constrained_variable_types(
225+
::Type{VNOToScalarNLBridge{T}},
226+
) where {T}
217227
return Tuple{Type}[]
218228
end
219229

@@ -239,9 +249,9 @@ function _B.Constraint.bridge_constraint(
239249

240250
nlm = _nl_model(model)
241251

242-
leq = MOI.ConstraintIndex{MOI.ScalarNonlinearFunction, MOI.LessThan{T}}[]
243-
geq = MOI.ConstraintIndex{MOI.ScalarNonlinearFunction, MOI.GreaterThan{T}}[]
244-
eq = MOI.ConstraintIndex{MOI.ScalarNonlinearFunction, MOI.EqualTo{T}}[]
252+
leq = MOI.ConstraintIndex{MOI.ScalarNonlinearFunction,MOI.LessThan{T}}[]
253+
geq = MOI.ConstraintIndex{MOI.ScalarNonlinearFunction,MOI.GreaterThan{T}}[]
254+
eq = MOI.ConstraintIndex{MOI.ScalarNonlinearFunction,MOI.EqualTo{T}}[]
245255

246256
base_id = (_vno_op_counter[] += 1)
247257

@@ -258,7 +268,10 @@ function _B.Constraint.bridge_constraint(
258268
push!(eq, MOI.add_constraint(model, sf, MOI.EqualTo{T}(li)))
259269
else
260270
if isfinite(li)
261-
push!(geq, MOI.add_constraint(model, sf, MOI.GreaterThan{T}(li)))
271+
push!(
272+
geq,
273+
MOI.add_constraint(model, sf, MOI.GreaterThan{T}(li)),
274+
)
262275
end
263276
if isfinite(ui)
264277
push!(leq, MOI.add_constraint(model, sf, MOI.LessThan{T}(ui)))
@@ -270,7 +283,11 @@ function _B.Constraint.bridge_constraint(
270283
end
271284

272285
# Bridge transparency (optional but nice)
273-
function MOI.get(::MOI.ModelLike, ::MOI.ConstraintFunction, b::VNOToScalarNLBridge)
286+
function MOI.get(
287+
::MOI.ModelLike,
288+
::MOI.ConstraintFunction,
289+
b::VNOToScalarNLBridge,
290+
)
274291
return b.f
275292
end
276293
function MOI.get(::MOI.ModelLike, ::MOI.ConstraintSet, b::VNOToScalarNLBridge)
@@ -311,34 +328,49 @@ function MOI.set(
311328
m = b.s.output_dimension
312329
n = b.s.input_dimension
313330
@assert length(value) == n # value is x, not f(x)
314-
331+
315332
# Evaluate f(x)
316333
f_x = Vector{T}(undef, m)
317334
b.s.eval_f(f_x, value)
318-
335+
319336
# Now set the constraint primal for each bridged constraint
320337
# The constraints are created in order of output dimensions
321338
leq_idx = 1
322339
geq_idx = 1
323340
eq_idx = 1
324-
341+
325342
for i in 1:m
326343
li = b.s.l[i]
327344
ui = b.s.u[i]
328-
345+
329346
if isfinite(li) && isfinite(ui) && li == ui
330347
# Equality constraint
331348
if eq_idx <= length(b.eq)
332-
MOI.set(model, MOI.ConstraintPrimalStart(), b.eq[eq_idx], f_x[i])
349+
MOI.set(
350+
model,
351+
MOI.ConstraintPrimalStart(),
352+
b.eq[eq_idx],
353+
f_x[i],
354+
)
333355
eq_idx += 1
334356
end
335357
else
336358
if isfinite(li) && geq_idx <= length(b.geq)
337-
MOI.set(model, MOI.ConstraintPrimalStart(), b.geq[geq_idx], f_x[i])
359+
MOI.set(
360+
model,
361+
MOI.ConstraintPrimalStart(),
362+
b.geq[geq_idx],
363+
f_x[i],
364+
)
338365
geq_idx += 1
339366
end
340367
if isfinite(ui) && leq_idx <= length(b.leq)
341-
MOI.set(model, MOI.ConstraintPrimalStart(), b.leq[leq_idx], f_x[i])
368+
MOI.set(
369+
model,
370+
MOI.ConstraintPrimalStart(),
371+
b.leq[leq_idx],
372+
f_x[i],
373+
)
342374
leq_idx += 1
343375
end
344376
end
@@ -368,10 +400,10 @@ function MOI.set(
368400
#
369401
# For our bridged scalar constraints, we need the dual per output (λ).
370402
# We can recover λ from the Jacobian: λ = (J * J')^{-1} * J * dual_per_var
371-
403+
372404
m = b.s.output_dimension
373405
n = b.s.input_dimension
374-
406+
375407
if length(value) == m
376408
# Direct mapping: value[i] is dual for output i
377409
_set_duals_from_output_duals!(model, b, value)
@@ -392,29 +424,29 @@ function _set_duals_from_input_duals!(
392424
) where {T}
393425
m = b.s.output_dimension
394426
n = b.s.input_dimension
395-
427+
396428
# Get the current primal values to compute Jacobian
397429
x = zeros(T, n)
398430
for (i, vi) in enumerate(b.f.variables)
399431
x[i] = MOI.get(model, MOI.VariablePrimalStart(), vi)
400432
end
401-
433+
402434
# Compute the Jacobian at current point
403435
vals = Vector{T}(undef, length(b.s.jacobian_structure))
404436
b.s.eval_jacobian(vals, x)
405-
437+
406438
# Build the sparse Jacobian matrix J (m x n)
407439
J = zeros(T, m, n)
408440
for (k, (r, c)) in enumerate(b.s.jacobian_structure)
409441
J[r, c] = vals[k]
410442
end
411-
443+
412444
# Compute λ from dual_per_var = J' * λ
413445
# λ = (J * J')^{-1} * J * dual_per_var
414446
# For numerical stability, use least squares: λ = J' \ dual_per_var
415447
# which solves min ||J' * λ - dual_per_var||
416448
λ = J' \ dual_per_var
417-
449+
418450
# Now set the scalar constraint duals
419451
_set_duals_from_output_duals!(model, b, λ)
420452
return
@@ -426,35 +458,50 @@ function _set_duals_from_output_duals!(
426458
value::AbstractVector,
427459
) where {T}
428460
m = b.s.output_dimension
429-
461+
430462
leq_idx = 1
431463
geq_idx = 1
432464
eq_idx = 1
433-
465+
434466
for i in 1:m
435467
li = b.s.l[i]
436468
ui = b.s.u[i]
437-
469+
438470
if isfinite(li) && isfinite(ui) && li == ui
439471
# Equality constraint
440472
if eq_idx <= length(b.eq)
441-
MOI.set(model, MOI.ConstraintDualStart(), b.eq[eq_idx], value[i])
473+
MOI.set(
474+
model,
475+
MOI.ConstraintDualStart(),
476+
b.eq[eq_idx],
477+
value[i],
478+
)
442479
eq_idx += 1
443480
end
444481
else
445482
if isfinite(li) && geq_idx <= length(b.geq)
446-
MOI.set(model, MOI.ConstraintDualStart(), b.geq[geq_idx], value[i])
483+
MOI.set(
484+
model,
485+
MOI.ConstraintDualStart(),
486+
b.geq[geq_idx],
487+
value[i],
488+
)
447489
geq_idx += 1
448490
end
449491
if isfinite(ui) && leq_idx <= length(b.leq)
450492
# Note: For an interval constraint, the dual might need to be split
451493
# For now, we only set if there's no corresponding geq (i.e., only upper bound)
452494
if !isfinite(li)
453-
MOI.set(model, MOI.ConstraintDualStart(), b.leq[leq_idx], value[i])
495+
MOI.set(
496+
model,
497+
MOI.ConstraintDualStart(),
498+
b.leq[leq_idx],
499+
value[i],
500+
)
454501
end
455502
leq_idx += 1
456503
end
457504
end
458505
end
459506
return
460-
end
507+
end

src/moi_wrapper.jl

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -677,7 +677,10 @@ function _add_bridges(instantiated_model)
677677
MOI.Bridges.Objective.add_all_bridges(model, Float64)
678678
# Add the VectorNonlinearOracle bridge for NonLinearProgram
679679
if instantiated_model isa NonLinearProgram.Model
680-
MOI.Bridges.add_bridge(model, NonLinearProgram.VNOToScalarNLBridge{Float64})
680+
MOI.Bridges.add_bridge(
681+
model,
682+
NonLinearProgram.VNOToScalarNLBridge{Float64},
683+
)
681684
end
682685
return model
683686
end

test/nlp_program.jl

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1038,7 +1038,11 @@ function test_VectorNonlinearOracle_univariate()
10381038
return
10391039
end
10401040
hessian_lagrangian_structure = [(1, 1)]
1041-
function eval_hessian_lagrangian(ret::AbstractVector, z::AbstractVector, μ::AbstractVector)
1041+
function eval_hessian_lagrangian(
1042+
ret::AbstractVector,
1043+
z::AbstractVector,
1044+
μ::AbstractVector,
1045+
)
10421046
# Hessian of μ1 * z1^2 is 2*μ1
10431047
ret[1] = 2.0 * μ[1]
10441048
return

0 commit comments

Comments
 (0)