/
dual_equality_constraints.jl
144 lines (128 loc) · 7.4 KB
/
dual_equality_constraints.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
function add_dual_equality_constraints(dual_model::MOI.ModelLike, primal_model::MOI.ModelLike,
primal_dual_map::PrimalDualMap, dual_names::DualNames,
primal_objective::PrimalObjective{T},
con_types::Vector{Tuple{DataType, DataType}}) where T
dual_sense = MOI.get(dual_model, MOI.ObjectiveSense()) # Get dual model sense
num_objective_terms = MOIU.number_of_affine_terms(T, get_saf(primal_objective)) # This is used to update the scalar_term_index
scalar_term_index::Int = 1
for primal_vi in MOI.get(primal_model, MOI.ListOfVariableIndices())
# Loop at every constraint to get the scalar affine terms
scalar_affine_terms = get_scalar_affine_terms(primal_model, primal_dual_map.primal_con_dual_var,
primal_vi, con_types, T)
# Add constraint, the sense of a0 depends on the dual_model ObjectiveSense
# If max sense scalar term is -a0 and if min sense sacalar term is a0
if primal_vi == primal_objective.saf.terms[scalar_term_index].variable_index
scalar_term_value = MOI.coefficient(primal_objective.saf.terms[scalar_term_index])
# This ternary is important for the last scalar_term_index
# If the last term of the objective is not the last primal variable we don't update
# the scalar_term_index
scalar_term_index == num_objective_terms ? scalar_term_index : scalar_term_index += 1
# Update the coeficient with the problem sense
scalar_term = (dual_sense == MOI.MAX_SENSE ? 1 : -1) * scalar_term_value
else # In this case this variable is not on the objective function
scalar_term = zero(T)
end
# Add equality constraint
dual_ci = MOI.add_constraint(dual_model, MOI.ScalarAffineFunction(scalar_affine_terms, zero(T)), MOI.EqualTo(scalar_term))
#Set constraint name with the name of the associated priaml variable
set_dual_constraint_name(dual_model, primal_model, primal_vi, dual_ci,
dual_names.dual_constraint_name_prefix)
# Add primal variable to dual contraint to the link dictionary
push!(primal_dual_map.primal_var_dual_con, primal_vi => dual_ci)
end
return
end
function set_dual_constraint_name(dual_model::MOI.ModelLike, primal_model::MOI.ModelLike,
primal_vi::VI, dual_ci::CI, prefix::String)
MOI.set(dual_model, MOI.ConstraintName(), dual_ci,
prefix*MOI.get(primal_model, MOI.VariableName(), primal_vi))
return
end
function get_scalar_affine_terms(primal_model::MOI.ModelLike,
primal_con_dual_var::Dict{CI, Vector{VI}}, primal_vi::VI,
con_types::Vector{Tuple{DataType, DataType}}, T::DataType)
scalar_affine_terms = Vector{MOI.ScalarAffineTerm{T}}(undef, 0)
for (F, S) in con_types
primal_cis = MOI.get(primal_model, MOI.ListOfConstraintIndices{F,S}()) # Constraints of type {F, S}
for ci in primal_cis
fill_scalar_affine_terms!(scalar_affine_terms, primal_con_dual_var,
primal_model, ci, primal_vi)
end
end
return scalar_affine_terms
end
function push_to_scalar_affine_terms!(scalar_affine_terms::Vector{MOI.ScalarAffineTerm{T}},
affine_term::T, vi::VI) where T
if !iszero(affine_term) # if term is different than 0 add to the scalar affine terms vector
push!(scalar_affine_terms, MOI.ScalarAffineTerm(affine_term, vi))
end
return
end
function fill_scalar_affine_terms!(scalar_affine_terms::Vector{MOI.ScalarAffineTerm{T}},
primal_con_dual_var::Dict{CI, Vector{VI}},
primal_model::MOI.ModelLike, ci::CI{SAF{T}, S},
primal_vi::VI) where {T,
S <: Union{MOI.GreaterThan{T},
MOI.LessThan{T},
MOI.EqualTo{T}}}
moi_function = get_function(primal_model, ci)
for term in moi_function.terms
if term.variable_index == primal_vi
dual_vi = primal_con_dual_var[ci][1] # In this case we only have one vi
push_to_scalar_affine_terms!(scalar_affine_terms, MOI.coefficient(term), dual_vi)
end
end
return
end
function fill_scalar_affine_terms!(scalar_affine_terms::Vector{MOI.ScalarAffineTerm{T}},
primal_con_dual_var::Dict{CI, Vector{VI}},
primal_model::MOI.ModelLike, ci::CI{SVF, S},
primal_vi::VI) where {T,
S <: Union{MOI.GreaterThan{T},
MOI.LessThan{T},
MOI.EqualTo{T}}}
moi_function = get_function(primal_model, ci)
if moi_function.variable == primal_vi
dual_vi = primal_con_dual_var[ci][1] # In this case we only have one vi
push_to_scalar_affine_terms!(scalar_affine_terms, one(T), dual_vi)
end
return
end
function fill_scalar_affine_terms!(scalar_affine_terms::Vector{MOI.ScalarAffineTerm{T}},
primal_con_dual_var::Dict{CI, Vector{VI}},
primal_model::MOI.ModelLike, ci::CI{VAF{T}, S},
primal_vi::VI) where {T, S <: MOI.AbstractVectorSet}
moi_function = get_function(primal_model, ci)
set = get_set(primal_model, ci)
for term in moi_function.terms
if term.scalar_term.variable_index == primal_vi
dual_vi = primal_con_dual_var[ci][term.output_index] # term.output_index is the row of the VAF,
# it corresponds to the dual variable associated with
# this constraint
push_to_scalar_affine_terms!(scalar_affine_terms,
set_dot(term.output_index, set, T)*MOI.coefficient(term), dual_vi)
end
end
return
end
function fill_scalar_affine_terms!(scalar_affine_terms::Vector{MOI.ScalarAffineTerm{T}},
primal_con_dual_var::Dict{CI, Vector{VI}},
primal_model::MOI.ModelLike, ci::CI{VVF, S},
primal_vi::VI) where {T, S <: MOI.AbstractVectorSet}
moi_function = get_function(primal_model, ci)
set = get_set(primal_model, ci)
for (i, variable) in enumerate(moi_function.variables)
if variable == primal_vi
dual_vi = primal_con_dual_var[ci][i]
push_to_scalar_affine_terms!(scalar_affine_terms,
set_dot(i, set, T)*one(T), dual_vi)
end
end
return
end
# Change to spzeros once https://github.com/JuliaOpt/MathOptInterface.jl/pull/805 is merged
function set_dot(i::Int, s::MOI.AbstractVectorSet, T::DataType)
vec = zeros(T, MOI.dimension(s))
vec[i] = one(T)
return MOIU.set_dot(vec, vec, s)
end