Skip to content

Commit 51f5041

Browse files
authored
Improve JuMP API (#186)
* [WIP] Improve JuMP API * add tests and fixes * make JuMP a weakdep * bump julia version * Update ci.yml
1 parent 06f4eaf commit 51f5041

10 files changed

Lines changed: 260 additions & 88 deletions

File tree

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@ jobs:
2121
- version: '1'
2222
os: ubuntu-latest
2323
arch: x64
24-
- version: '1.6'
24+
- version: '1.9'
2525
os: ubuntu-latest
2626
arch: x64
27-
- version: '1.6'
27+
- version: '1.9'
2828
os: ubuntu-latest
2929
arch: x86
3030
steps:

Project.toml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,16 @@ authors = ["guilhermebodin <guilherme.b.moraes@gmail.com>"]
44
version = "0.5.9"
55

66
[deps]
7-
JuMP = "4076af6c-e467-56ae-b986-b466b2749572"
87
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
98
MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee"
109

10+
[weakdeps]
11+
JuMP = "4076af6c-e467-56ae-b986-b466b2749572"
12+
13+
[extensions]
14+
DualizationJuMPExt = "JuMP"
15+
1116
[compat]
1217
JuMP = "0.23, 1"
1318
MathOptInterface = "1"
14-
julia = "1.6"
19+
julia = "1.9"

docs/src/reference.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
Dualization.supported_constraints
55
Dualization.supported_objective
66
Dualization.DualNames
7-
Dualization.VariableData
8-
Dualization.ConstraintData
7+
Dualization.PrimalVariableData
8+
Dualization.PrimalConstraintData
99
Dualization.PrimalDualMap
1010
```
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
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

src/Dualization.jl

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,8 @@
55

66
module Dualization
77

8-
import JuMP
98
import LinearAlgebra
10-
import MathOptInterface
11-
12-
const MOI = MathOptInterface
9+
import MathOptInterface as MOI
1310

1411
include("structures.jl")
1512
include("utils.jl")

src/dual_equality_constraints.jl

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ function _add_dual_equality_constraints(
113113
end
114114
# Add primal variable to dual contraint to the link dictionary
115115
primal_dual_map.primal_variable_data[primal_vi] =
116-
VariableData{T}(nothing, -1, dual_ci, nothing)
116+
PrimalVariableData{T}(nothing, -1, dual_ci, nothing)
117117
end
118118
return scalar_affine_terms
119119
end
@@ -139,7 +139,7 @@ function _add_constrained_variable_constraint(
139139
sense_change * get(scalar_terms, vi, zero(T)),
140140
)
141141
primal_dual_map.primal_variable_data[vi] =
142-
VariableData{T}(ci, i, nothing, dual_function)
142+
PrimalVariableData{T}(ci, i, nothing, dual_function)
143143
end
144144
return
145145
end
@@ -172,7 +172,7 @@ function _add_constrained_variable_constraint(
172172
dual_ci = MOI.add_constraint(dual_model, func_dual, set_dual)
173173
for (i, vi) in enumerate(vis)
174174
primal_dual_map.primal_variable_data[vi] =
175-
VariableData{T}(ci, i, dual_ci, nothing)
175+
PrimalVariableData{T}(ci, i, dual_ci, nothing)
176176
end
177177
if !is_empty(dual_names)
178178
@warn(
@@ -199,7 +199,7 @@ function _add_constrained_variable_constraint(
199199
sense_change * get(scalar_terms, vi, zero(T)),
200200
)
201201
primal_dual_map.primal_variable_data[vi] =
202-
VariableData{T}(ci, 0, nothing, dual_function)
202+
PrimalVariableData{T}(ci, 0, nothing, dual_function)
203203
return
204204
end
205205

@@ -226,7 +226,7 @@ function _add_constrained_variable_constraint(
226226
set_dual,
227227
)
228228
primal_dual_map.primal_variable_data[vi] =
229-
VariableData{T}(ci, 0, dual_ci, nothing)
229+
PrimalVariableData{T}(ci, 0, dual_ci, nothing)
230230
if !is_empty(dual_names)
231231
_set_dual_constraint_name(
232232
dual_model,

src/dual_model_variables.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ function _add_dual_variable(
8686
T[]
8787
end
8888
primal_dual_map.primal_constraint_data[ci] =
89-
ConstraintData(set_constant, vis, con_index)
89+
PrimalConstraintData(set_constant, vis, con_index)
9090
# Get constraint name
9191
ci_name = MOI.get(primal_model, MOI.ConstraintName(), ci)
9292
# Add each vi to the dictionary

src/dualize.jl

Lines changed: 0 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -219,62 +219,3 @@ function dualize(
219219
end
220220
return dual_problem
221221
end
222-
223-
function dualize(model::JuMP.Model, optimizer_constructor = nothing; kwargs...)
224-
mode = JuMP.mode(model)
225-
if mode != JuMP.AUTOMATIC
226-
error("Dualization does not support solvers in $(mode) mode")
227-
end
228-
dual_model = JuMP.Model()
229-
dualize(
230-
JuMP.backend(model),
231-
DualProblem(JuMP.backend(dual_model));
232-
kwargs...,
233-
)
234-
_fill_obj_dict_with_variables!(dual_model)
235-
_fill_obj_dict_with_constraints!(dual_model)
236-
if optimizer_constructor !== nothing
237-
JuMP.set_optimizer(dual_model, optimizer_constructor)
238-
end
239-
return dual_model
240-
end
241-
242-
function _fill_obj_dict_with_variables!(model::JuMP.Model)
243-
list = MOI.get(model, MOI.ListOfVariableAttributesSet())
244-
if !(MOI.VariableName() in list)
245-
return
246-
end
247-
for vi in MOI.get(model, MOI.ListOfVariableIndices())
248-
name = MOI.get(JuMP.backend(model), MOI.VariableName(), vi)
249-
if !isempty(name)
250-
model[Symbol(name)] = JuMP.VariableRef(model, vi)
251-
end
252-
end
253-
return
254-
end
255-
256-
function _fill_obj_dict_with_constraints!(model::JuMP.Model)
257-
con_types = MOI.get(model, MOI.ListOfConstraintTypesPresent())
258-
for (F, S) in con_types
259-
_fill_obj_dict_with_constraints!(model, F, S)
260-
end
261-
return
262-
end
263-
264-
function _fill_obj_dict_with_constraints!(
265-
model::JuMP.Model,
266-
::Type{F},
267-
::Type{S},
268-
) where {F,S}
269-
list = MOI.get(model, MOI.ListOfConstraintAttributesSet{F,S}())
270-
if !(MOI.ConstraintName() in list)
271-
return
272-
end
273-
for ci in MOI.get(JuMP.backend(model), MOI.ListOfConstraintIndices{F,S}())
274-
name = MOI.get(JuMP.backend(model), MOI.ConstraintName(), ci)
275-
if !isempty(name)
276-
model[Symbol(name)] = JuMP.constraint_ref_with_index(model, ci)
277-
end
278-
end
279-
return
280-
end

0 commit comments

Comments
 (0)