@@ -2,40 +2,270 @@ module DiffOptExt
22
33import MadDiff
44import DiffOpt
5+ import MadNLP
56const MOI = DiffOpt. MOI
7+ const POI = DiffOpt. POI
68
79MOIExt = Base. get_extension (MadDiff, :MathOptInterfaceExt )
8- Optimizer = MOIExt. Optimizer
9-
10- DiffOpt. forward_differentiate! (model:: Optimizer ) = MadDiff. forward_differentiate! (model)
11- DiffOpt. reverse_differentiate! (model:: Optimizer ) = MadDiff. reverse_differentiate! (model)
12- DiffOpt. empty_input_sensitivities! (model:: Optimizer ) = MadDiff. empty_input_sensitivities! (model)
13- MadDiff. nonlinear_diff_model (optimizer_constructor; kwargs... ) = DiffOpt. JuMP. Model (MadDiff. diff_optimizer (optimizer_constructor; kwargs... ))
14-
15- MOI. set (m:: Optimizer , :: DiffOpt.ForwardConstraintSet , ci:: MOI.ConstraintIndex , set) =
16- MOI. set (m, MadDiff. ForwardConstraintSet (), ci, set)
17- MOI. get (m:: Optimizer , :: DiffOpt.ForwardVariablePrimal , vi:: MOI.VariableIndex ) =
18- MOI. get (m, MadDiff. ForwardVariablePrimal (), vi)
19- MOI. get (m:: Optimizer , :: DiffOpt.ForwardConstraintDual , ci:: MOI.ConstraintIndex ) =
20- MOI. get (m, MadDiff. ForwardConstraintDual (), ci)
21- MOI. get (m:: Optimizer , :: DiffOpt.ForwardObjectiveSensitivity ) =
22- MOI. get (m, MadDiff. ForwardObjectiveSensitivity ())
23-
24- MOI. set (m:: Optimizer , :: DiffOpt.ReverseVariablePrimal , vi:: MOI.VariableIndex , value) =
25- MOI. set (m, MadDiff. ReverseVariablePrimal (), vi, value)
26- MOI. set (m:: Optimizer , :: DiffOpt.ReverseConstraintDual , ci:: MOI.ConstraintIndex , value) =
27- MOI. set (m, MadDiff. ReverseConstraintDual (), ci, value)
28- MOI. set (m:: Optimizer , :: DiffOpt.ReverseObjectiveSensitivity , value) =
29- MOI. set (m, MadDiff. ReverseObjectiveSensitivity (), value)
30- MOI. get (m:: Optimizer , :: DiffOpt.ReverseConstraintSet , ci:: MOI.ConstraintIndex ) =
31- MOI. get (m, MadDiff. ReverseConstraintSet (), ci)
32-
33- MOI. get (m:: Optimizer , :: DiffOpt.DifferentiateTimeSec ) =
34- MOI. get (m, MadDiff. DifferentiateTimeSec ())
10+
11+ mutable struct DiffOptModel <: MOI.AbstractOptimizer
12+ wrapper:: Union{Nothing,MOIExt.DiffOptWrapper}
13+ source_to_inner:: MOI.Utilities.IndexMap
14+ sensitivity_config:: MadDiff.MadDiffConfig
15+ end
16+
17+ function DiffOptModel (; sensitivity_config:: MadDiff.MadDiffConfig = MadDiff. MadDiffConfig ())
18+ return DiffOptModel (nothing , MOI. Utilities. IndexMap (), sensitivity_config)
19+ end
20+
21+ _backend (model:: DiffOptModel ) = model. wrapper:: MOIExt.DiffOptWrapper
22+
23+ MOI. supports_incremental_interface (:: DiffOptModel ) = true
24+ MOI. supports_add_constrained_variable (:: DiffOptModel , :: Type{<:MOI.AbstractScalarSet} ) = true
25+ MOI. supports_add_constrained_variables (:: DiffOptModel , :: Type{<:MOI.AbstractVectorSet} ) = true
26+ MOI. supports_add_constrained_variables (:: DiffOptModel , :: Type{MOI.Reals} ) = true
27+ MOI. supports_constraint (:: DiffOptModel , :: Type{<:MOI.AbstractFunction} , :: Type{<:MOI.AbstractSet} ) = true
28+ MOI. is_empty (model:: DiffOptModel ) = isnothing (model. wrapper)
29+
30+ function MOI. empty! (model:: DiffOptModel )
31+ model. wrapper = nothing
32+ model. source_to_inner = MOI. Utilities. IndexMap ()
33+ return
34+ end
35+
36+ function _madnlp_optimizer_type ()
37+ if isdefined (MadNLP, :Optimizer )
38+ return getproperty (MadNLP, :Optimizer )
39+ end
40+ ext = Base. get_extension (MadNLP, :MathOptInterfaceExt )
41+ if isnothing (ext)
42+ return nothing
43+ end
44+ return getproperty (ext, :Optimizer )
45+ end
46+
47+ function _is_madnlp_optimizer (optimizer)
48+ optimizer_type = _madnlp_optimizer_type ()
49+ return ! isnothing (optimizer_type) && optimizer isa optimizer_type
50+ end
51+
52+ function _compose_index_maps (
53+ source_to_mid:: MOI.Utilities.IndexMap ,
54+ mid_to_dest:: MOI.Utilities.IndexMap ,
55+ )
56+ output = MOI. Utilities. IndexMap ()
57+ for (source, mid) in source_to_mid
58+ output[source] = mid_to_dest[mid]
59+ end
60+ return output
61+ end
62+
63+ function _has_active_bridges (model:: MOI.Bridges.LazyBridgeOptimizer )
64+ return ! isempty (MOI. Bridges. Variable. bridges (model)) ||
65+ ! isempty (MOI. Bridges. Constraint. bridges (model)) ||
66+ ! isempty (MOI. Bridges. Objective. bridges (model))
67+ end
68+
69+ function _unwrap_to_madnlp (
70+ root_optimizer,
71+ optimizer,
72+ source_to_optimizer:: Union{Nothing,MOI.Utilities.IndexMap} ,
73+ )
74+ _is_madnlp_optimizer (optimizer) &&
75+ return optimizer, something (source_to_optimizer, MOI. Utilities. identity_index_map (root_optimizer))
76+ error (" MadDiff requires a wrapper chain ending in MadNLP. Got $(typeof (optimizer)) ." )
77+ end
78+
79+ function _unwrap_to_madnlp (
80+ root_optimizer,
81+ optimizer:: MOI.Utilities.CachingOptimizer ,
82+ source_to_optimizer:: Union{Nothing,MOI.Utilities.IndexMap} ,
83+ )
84+ map_step = deepcopy (optimizer. model_to_optimizer_map)
85+ source_to_inner = isnothing (source_to_optimizer) ?
86+ map_step : _compose_index_maps (source_to_optimizer, map_step)
87+ return _unwrap_to_madnlp (root_optimizer, optimizer. optimizer, source_to_inner)
88+ end
89+
90+ function _unwrap_to_madnlp (
91+ root_optimizer,
92+ optimizer:: MOI.Bridges.LazyBridgeOptimizer ,
93+ source_to_optimizer:: Union{Nothing,MOI.Utilities.IndexMap} ,
94+ )
95+ _has_active_bridges (optimizer) &&
96+ error (" MadDiff does not support active MOI bridges in the DiffOpt chain." )
97+ return _unwrap_to_madnlp (root_optimizer, optimizer. model, source_to_optimizer)
98+ end
99+
100+ function _unwrap_to_madnlp (
101+ root_optimizer,
102+ optimizer:: POI.Optimizer ,
103+ source_to_optimizer:: Union{Nothing,MOI.Utilities.IndexMap} ,
104+ )
105+ return _unwrap_to_madnlp (root_optimizer, optimizer. optimizer, source_to_optimizer)
106+ end
107+
108+ _unwrap_to_madnlp (root_optimizer) = _unwrap_to_madnlp (root_optimizer, root_optimizer, nothing )
109+
110+ function _refresh_parameter_map! (
111+ optimizer:: MOIExt.DiffOptWrapper ,
112+ source_optimizer,
113+ source_to_madnlp:: MOI.Utilities.IndexMap ,
114+ )
115+ empty! (optimizer. param_ci_to_vi)
116+ for source_ci in MOI. get (
117+ source_optimizer,
118+ MOI. ListOfConstraintIndices{
119+ MOI. VariableIndex,
120+ MOI. Parameter{Float64},
121+ }(),
122+ )
123+ source_vi = MOI. get (source_optimizer, MOI. ConstraintFunction (), source_ci)
124+ optimizer. param_ci_to_vi[source_to_madnlp[source_ci]] =
125+ source_to_madnlp[source_vi]
126+ end
127+ return optimizer
128+ end
129+
130+ function MOI. copy_to (model:: DiffOptModel , src:: MOI.ModelLike )
131+ madnlp_optimizer, source_to_madnlp = _unwrap_to_madnlp (src)
132+ backend = MOIExt. DiffOptWrapper (madnlp_optimizer)
133+ backend. sensitivity_config = deepcopy (model. sensitivity_config)
134+ _refresh_parameter_map! (backend, src, source_to_madnlp)
135+ model. wrapper = backend
136+ model. source_to_inner = source_to_madnlp
137+ return MOI. Utilities. identity_index_map (src)
138+ end
139+
140+ function MOI. copy_to (
141+ model:: MOI.Bridges.LazyBridgeOptimizer{<:DiffOptModel} ,
142+ src:: MOI.ModelLike ,
143+ )
144+ return MOI. copy_to (model. model, src)
145+ end
146+
147+ _map_source_to_inner (model:: DiffOptModel , idx) = model. source_to_inner[idx]
148+
149+ DiffOpt. forward_differentiate! (model:: DiffOptModel ) =
150+ MadDiff. forward_differentiate! (_backend (model))
151+ DiffOpt. reverse_differentiate! (model:: DiffOptModel ) =
152+ MadDiff. reverse_differentiate! (_backend (model))
153+ DiffOpt. empty_input_sensitivities! (model:: DiffOptModel ) =
154+ MadDiff. empty_input_sensitivities! (_backend (model))
155+
156+ MOI. supports (:: DiffOptModel , :: DiffOpt.NonLinearKKTJacobianFactorization ) = true
157+ MOI. supports (:: DiffOptModel , :: DiffOpt.AllowObjectiveAndSolutionInput ) = true
158+ MOI. set (:: DiffOptModel , :: DiffOpt.NonLinearKKTJacobianFactorization , _) = nothing
159+ MOI. set (:: DiffOptModel , :: DiffOpt.AllowObjectiveAndSolutionInput , _) = nothing
160+
161+ function MOI. set (
162+ model:: DiffOptModel ,
163+ :: DiffOpt.ForwardConstraintSet ,
164+ ci:: MOI.ConstraintIndex{MOI.VariableIndex,MOI.Parameter{T}} ,
165+ set:: MOI.Parameter{T} ,
166+ ) where {T}
167+ inner_ci = _map_source_to_inner (model, ci)
168+ return MOI. set (_backend (model), MadDiff. ForwardConstraintSet (), inner_ci, set)
169+ end
170+
171+ function MOI. get (
172+ model:: DiffOptModel ,
173+ :: DiffOpt.ForwardVariablePrimal ,
174+ vi:: MOI.VariableIndex ,
175+ )
176+ inner_vi = _map_source_to_inner (model, vi)
177+ return MOI. get (_backend (model), MadDiff. ForwardVariablePrimal (), inner_vi)
178+ end
179+
180+ function MOI. get (
181+ model:: DiffOptModel ,
182+ :: DiffOpt.ForwardConstraintDual ,
183+ ci:: MOI.ConstraintIndex ,
184+ )
185+ inner_ci = _map_source_to_inner (model, ci)
186+ return MOI. get (_backend (model), MadDiff. ForwardConstraintDual (), inner_ci)
187+ end
188+
189+ MOI. get (model:: DiffOptModel , :: DiffOpt.ForwardObjectiveSensitivity ) =
190+ MOI. get (_backend (model), MadDiff. ForwardObjectiveSensitivity ())
191+
192+ function MOI. set (
193+ model:: DiffOptModel ,
194+ :: DiffOpt.ReverseVariablePrimal ,
195+ vi:: MOI.VariableIndex ,
196+ value,
197+ )
198+ inner_vi = _map_source_to_inner (model, vi)
199+ return MOI. set (_backend (model), MadDiff. ReverseVariablePrimal (), inner_vi, value)
200+ end
201+
202+ function MOI. set (
203+ model:: DiffOptModel ,
204+ :: DiffOpt.ReverseConstraintDual ,
205+ ci:: MOI.ConstraintIndex ,
206+ value,
207+ )
208+ inner_ci = _map_source_to_inner (model, ci)
209+ return MOI. set (_backend (model), MadDiff. ReverseConstraintDual (), inner_ci, value)
210+ end
211+
212+ MOI. set (model:: DiffOptModel , :: DiffOpt.ReverseObjectiveSensitivity , value) =
213+ MOI. set (_backend (model), MadDiff. ReverseObjectiveSensitivity (), value)
214+
215+ function MOI. get (
216+ model:: DiffOptModel ,
217+ :: DiffOpt.ReverseConstraintSet ,
218+ ci:: MOI.ConstraintIndex{MOI.VariableIndex,MOI.Parameter{T}} ,
219+ ) where {T}
220+ inner_ci = _map_source_to_inner (model, ci)
221+ return MOI. get (_backend (model), MadDiff. ReverseConstraintSet (), inner_ci)
222+ end
223+
224+ MOI. get (model:: DiffOptModel , :: DiffOpt.DifferentiateTimeSec ) =
225+ MOI. get (_backend (model), MadDiff. DifferentiateTimeSec ())
35226
36227DiffOpt. get_reverse_parameter (
37- model:: Optimizer ,
38- ci:: MOI.ConstraintIndex{MOI.VariableIndex, MOI.Parameter{T}} ,
39- ) where {T} = MadDiff. get_reverse_parameter (model, ci)
228+ model:: DiffOptModel ,
229+ ci:: MOI.ConstraintIndex{MOI.VariableIndex,MOI.Parameter{T}} ,
230+ ) where {T} = MadDiff. get_reverse_parameter (
231+ _backend (model),
232+ _map_source_to_inner (model, ci),
233+ )
234+
235+ MOI. get (model:: DiffOptModel , :: MOI.SolverName ) = " MadDiff[MadNLP]"
236+
237+ DiffOpt. _copy_dual (:: DiffOptModel , :: MOI.ModelLike , _) = nothing
238+ DiffOpt. _copy_dual (
239+ :: MOI.Bridges.LazyBridgeOptimizer{<:DiffOptModel} ,
240+ :: MOI.ModelLike ,
241+ _,
242+ ) = nothing
243+
244+ """
245+ MadDiff.diffopt_model_constructor(; config = MadDiff.MadDiffConfig())
246+
247+ Return a DiffOpt `ModelConstructor` callable that reuses a solved MadNLP
248+ optimizer for differentiation.
249+ """
250+ function MadDiff. diffopt_model_constructor (;
251+ config:: MadDiff.MadDiffConfig = MadDiff. MadDiffConfig (),
252+ )
253+ return () -> DiffOptModel (; sensitivity_config = config)
254+ end
255+
256+ function MadDiff. diff_model (
257+ optimizer_constructor;
258+ config:: MadDiff.MadDiffConfig = MadDiff. MadDiffConfig (),
259+ kwargs... ,
260+ )
261+ model = DiffOpt. diff_model (optimizer_constructor; kwargs... )
262+ MOI. set (model, DiffOpt. AllowObjectiveAndSolutionInput (), true )
263+ MOI. set (
264+ model,
265+ DiffOpt. ModelConstructor (),
266+ MadDiff. diffopt_model_constructor (config = config),
267+ )
268+ return model
269+ end
40270
41- end # module
271+ end # module DiffOptExt
0 commit comments