@@ -405,30 +405,22 @@ function MOI.set(
405405 b:: VNOToScalarNLBridge{T} ,
406406 value:: AbstractVector ,
407407) where {T}
408- # For VectorOfVariables in VectorNonlinearOracle, the ConstraintDual from solvers
409- # like Ipopt has length = input_dimension (variables), not output_dimension.
410- # This is because it represents the dual contribution per variable: J' * λ
411- # where J is the Jacobian and λ is the vector of constraint duals.
412- #
413- # For our bridged scalar constraints, we need the dual per output (λ).
414- # We can recover λ from the Jacobian: λ = (J * J')^{-1} * J * dual_per_var
408+ # The dual for the VNO constraint is λ (length n = input_dimension), one
409+ # multiplier per variable. The bridged scalar constraints have duals μ
410+ # (length m = output_dimension). The Lagrangian gradient identity
411+ # `λ = J(x)' * μ` determines the relationship, so we always solve
412+ # J(x)' * μ = λ regardless of whether m == n.
415413
416- m = b. s. output_dimension
417414 n = b. s. input_dimension
418415
419- if length (value) == m
420- # Direct mapping: value[i] is dual for output i
421- _set_duals_from_output_duals! (model, b, value)
422- elseif length (value) == n
423- # The dual is per-input-variable (J' * λ), need to recover λ
424- _set_duals_from_input_duals! (model, b, value)
425- else
416+ if length (value) != n
426417 error (
427418 " ConstraintDualStart for VNOToScalarNLBridge expects a vector of " *
428- " length equal to the output dimension ( $m ) or input dimension ($n ), " *
419+ " length equal to the input dimension ($n ), " *
429420 " but got length $(length (value)) ." ,
430421 )
431422 end
423+ _set_duals_from_input_duals! (model, b, value)
432424 return
433425end
434426
@@ -456,14 +448,13 @@ function _set_duals_from_input_duals!(
456448 J[r, c] = vals[k]
457449 end
458450
459- # Compute λ from dual_per_var = J' * λ
460- # λ = (J * J')^{-1} * J * dual_per_var
461- # For numerical stability, use least squares: λ = J' \ dual_per_var
462- # which solves min ||J' * λ - dual_per_var||
463- λ = J' \ dual_per_var
451+ # Solve J(x)' * μ = dual_per_var (= λ) for the output duals μ.
452+ # J' is (n×m), so this is an n-equation system in m unknowns.
453+ # Julia's \ uses the minimum-norm / least-squares solution as appropriate.
454+ μ = J' \ dual_per_var
464455
465- # Now set the scalar constraint duals
466- _set_duals_from_output_duals! (model, b, λ )
456+ # Set the scalar constraint dual starts to μ.
457+ _set_duals_from_output_duals! (model, b, μ )
467458 return
468459end
469460
0 commit comments