|
| 1 | +# Copyright (c) 2017: Guilherme Bodin, and contributors |
| 2 | +# |
| 3 | +# Use of this source code is governed by an MIT-style license that can be found |
| 4 | +# in the LICENSE.md file or at https://opensource.org/licenses/MIT. |
| 5 | + |
| 6 | +module DualizationJuMPExt |
| 7 | + |
| 8 | +import Dualization |
| 9 | +import JuMP |
| 10 | +import MathOptInterface as MOI |
| 11 | + |
| 12 | +function Dualization.dualize( |
| 13 | + model::JuMP.Model, |
| 14 | + optimizer_constructor = nothing; |
| 15 | + kwargs..., |
| 16 | +) |
| 17 | + mode = JuMP.mode(model) |
| 18 | + if mode != JuMP.AUTOMATIC |
| 19 | + error("Dualization does not support solvers in $(mode) mode") |
| 20 | + end |
| 21 | + dual_model = JuMP.Model() |
| 22 | + dual_problem = Dualization.DualProblem(JuMP.backend(dual_model)) |
| 23 | + Dualization.dualize(JuMP.backend(model), dual_problem; kwargs...) |
| 24 | + _fill_obj_dict_with_variables!(dual_model) |
| 25 | + _fill_obj_dict_with_constraints!(dual_model) |
| 26 | + if optimizer_constructor !== nothing |
| 27 | + JuMP.set_optimizer(dual_model, optimizer_constructor) |
| 28 | + end |
| 29 | + dual_model.ext[:_Dualization_jl_PrimalDualMap] = |
| 30 | + dual_problem.primal_dual_map |
| 31 | + return dual_model |
| 32 | +end |
| 33 | + |
| 34 | +function _fill_obj_dict_with_variables!(model::JuMP.Model) |
| 35 | + list = MOI.get(model, MOI.ListOfVariableAttributesSet()) |
| 36 | + if !(MOI.VariableName() in list) |
| 37 | + return |
| 38 | + end |
| 39 | + for vi in MOI.get(model, MOI.ListOfVariableIndices()) |
| 40 | + name = MOI.get(JuMP.backend(model), MOI.VariableName(), vi) |
| 41 | + if !isempty(name) |
| 42 | + model[Symbol(name)] = JuMP.VariableRef(model, vi) |
| 43 | + end |
| 44 | + end |
| 45 | + return |
| 46 | +end |
| 47 | + |
| 48 | +function _fill_obj_dict_with_constraints!(model::JuMP.Model) |
| 49 | + con_types = MOI.get(model, MOI.ListOfConstraintTypesPresent()) |
| 50 | + for (F, S) in con_types |
| 51 | + _fill_obj_dict_with_constraints!(model, F, S) |
| 52 | + end |
| 53 | + return |
| 54 | +end |
| 55 | + |
| 56 | +function _fill_obj_dict_with_constraints!( |
| 57 | + model::JuMP.Model, |
| 58 | + ::Type{F}, |
| 59 | + ::Type{S}, |
| 60 | +) where {F,S} |
| 61 | + list = MOI.get(model, MOI.ListOfConstraintAttributesSet{F,S}()) |
| 62 | + if !(MOI.ConstraintName() in list) |
| 63 | + return |
| 64 | + end |
| 65 | + for ci in MOI.get(JuMP.backend(model), MOI.ListOfConstraintIndices{F,S}()) |
| 66 | + name = MOI.get(JuMP.backend(model), MOI.ConstraintName(), ci) |
| 67 | + if !isempty(name) |
| 68 | + model[Symbol(name)] = JuMP.constraint_ref_with_index(model, ci) |
| 69 | + end |
| 70 | + end |
| 71 | + return |
| 72 | +end |
| 73 | + |
| 74 | +function _get_primal_dual_map(model::JuMP.Model) |
| 75 | + return model.ext[:_Dualization_jl_PrimalDualMap] |
| 76 | +end |
| 77 | + |
| 78 | +function Dualization._get_dual_constraint( |
| 79 | + dual_model, |
| 80 | + primal_ref::JuMP.VariableRef, |
| 81 | +) |
| 82 | + map = _get_primal_dual_map(dual_model) |
| 83 | + moi_primal_vi = JuMP.index(primal_ref) |
| 84 | + moi_dual_ci, idx = Dualization._get_dual_constraint(map, moi_primal_vi) |
| 85 | + # dual_model = nothing # TODO |
| 86 | + if idx === nothing |
| 87 | + # variables fixed at zero |
| 88 | + return nothing, idx |
| 89 | + end |
| 90 | + return JuMP.constraint_ref_with_index(dual_model, moi_dual_ci), idx |
| 91 | +end |
| 92 | + |
| 93 | +function Dualization._get_primal_constraint( |
| 94 | + dual_model::JuMP.Model, |
| 95 | + primal_vi::JuMP.VariableRef, |
| 96 | +) |
| 97 | + primal_model = JuMP.owner_model(primal_vi) |
| 98 | + map = _get_primal_dual_map(dual_model) |
| 99 | + moi_primal_vi = JuMP.index(primal_vi) |
| 100 | + primal_ci, idx = Dualization._get_primal_constraint(map, moi_primal_vi) |
| 101 | + if primal_ci === nothing |
| 102 | + return nothing, idx |
| 103 | + end |
| 104 | + return JuMP.constraint_ref_with_index(primal_model, primal_ci), idx |
| 105 | +end |
| 106 | + |
| 107 | +function Dualization._get_dual_variables( |
| 108 | + dual_model::JuMP.Model, |
| 109 | + primal_ref::JuMP.ConstraintRef, |
| 110 | +) |
| 111 | + map = _get_primal_dual_map(dual_model) |
| 112 | + moi_primal_ci = JuMP.index(primal_ref) |
| 113 | + moi_dual_vis = Dualization._get_dual_variables(map, moi_primal_ci) |
| 114 | + if moi_dual_vis === nothing |
| 115 | + # main constraint of a constrained variable |
| 116 | + return nothing |
| 117 | + end |
| 118 | + return [JuMP.VariableRef(dual_model, vi) for vi in moi_dual_vis] |
| 119 | +end |
| 120 | + |
| 121 | +# this is a constrained variable constraint |
| 122 | +function Dualization._get_dual_constraint( |
| 123 | + dual_model::JuMP.Model, |
| 124 | + primal_ref::JuMP.ConstraintRef, |
| 125 | +) |
| 126 | + map = _get_primal_dual_map(dual_model) |
| 127 | + moi_primal_ci = JuMP.index(primal_ref) |
| 128 | + moi_dual_ci = Dualization._get_dual_constraint(map, moi_primal_ci) |
| 129 | + if moi_dual_ci === nothing |
| 130 | + # main constraint of a constrained variable |
| 131 | + # or |
| 132 | + # primal constraint is equality |
| 133 | + return nothing |
| 134 | + end |
| 135 | + return JuMP.constraint_ref_with_index(dual_model, moi_dual_ci) |
| 136 | +end |
| 137 | + |
| 138 | +function Dualization._get_dual_parameter( |
| 139 | + dual_model::JuMP.Model, |
| 140 | + primal_ref::JuMP.VariableRef, |
| 141 | +) |
| 142 | + map = _get_primal_dual_map(dual_model) |
| 143 | + moi_primal_vi = JuMP.index(primal_ref) |
| 144 | + moi_dual_vi = Dualization._get_dual_parameter(map, moi_primal_vi) |
| 145 | + # the above line might error |
| 146 | + return JuMP.VariableRef(dual_model, moi_dual_vi) |
| 147 | +end |
| 148 | + |
| 149 | +end # module DualizationJuMPExt |
0 commit comments