Skip to content

Commit

Permalink
Merge pull request #106 from joaquimg/jg/mods
Browse files Browse the repository at this point in the history
Add more modification methods
  • Loading branch information
joaquimg authored May 13, 2021
2 parents 1da97cd + 02b147d commit 8a8e5ba
Show file tree
Hide file tree
Showing 11 changed files with 481 additions and 138 deletions.
4 changes: 2 additions & 2 deletions src/BilevelJuMP.jl
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ using IntervalArithmetic


export
BilevelModel, UpperToLower, LowerToUpper,
BilevelModel,
Upper, Lower, UpperOnly, LowerOnly,
DualOf
DualOf, BilevelAffExpr, BilevelQuadExpr

include("moi.jl")
include("moi_utilities.jl")
Expand Down
115 changes: 68 additions & 47 deletions src/jump.jl
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ mutable struct BilevelModel <: AbstractBilevelModel
var_upper::Dict{Int, JuMP.AbstractVariableRef}
var_lower::Dict{Int, JuMP.AbstractVariableRef} #
var_info::Dict{Int, VariableInfo}
var_upper_rev::Union{Nothing, Dict{JuMP.AbstractVariableRef, JuMP.AbstractVariableRef}} # bilevel ref no defined
var_lower_rev::Union{Nothing, Dict{JuMP.AbstractVariableRef, JuMP.AbstractVariableRef}} # bilevel ref no defined

# upper level decisions that are "parameters" of the second level
upper_to_lower_link::Dict{JuMP.AbstractVariableRef, JuMP.AbstractVariableRef}
Expand All @@ -34,17 +36,13 @@ mutable struct BilevelModel <: AbstractBilevelModel
nextconidx::Int # Next constraint index is nextconidx+1
constraints::Dict{Int, JuMP.AbstractConstraint} # Map conidx -> variable
connames::Dict{Int, String} # Map varidx -> name
connames_rev::Dict{String, Int} # Map varidx -> name
connames_rev::Dict{String, Int} # Map varidx -> name
ctr_level::Dict{Int, Level}
ctr_upper::Dict{Int, JuMP.ConstraintRef}
ctr_lower::Dict{Int, JuMP.ConstraintRef}
ctr_info::Dict{Int, ConstraintInfo}

upper_objective_sense::MOI.OptimizationSense
upper_objective_function::JuMP.AbstractJuMPScalar

lower_objective_sense::MOI.OptimizationSense
lower_objective_function::JuMP.AbstractJuMPScalar
ctr_upper_rev::Union{Nothing, Dict{JuMP.ConstraintRef, JuMP.ConstraintRef}} # bilevel ref no defined
ctr_lower_rev::Union{Nothing, Dict{JuMP.ConstraintRef, JuMP.ConstraintRef}} # bilevel ref no defined

# solution data
need_rebuild::Bool
Expand Down Expand Up @@ -78,6 +76,8 @@ mutable struct BilevelModel <: AbstractBilevelModel
Dict{Int, String}(),Dict{String, Int}(),
Dict{Int, Level}(), Dict{Int, JuMP.AbstractVariable}(), Dict{Int, JuMP.AbstractVariable}(),
Dict{Int, VariableInfo}(),
nothing,
nothing,
# links
Dict{JuMP.AbstractVariable, JuMP.AbstractVariable}(), Dict{JuMP.AbstractVariable, JuMP.AbstractVariable}(),
Dict{JuMP.AbstractVariable, JuMP.ConstraintRef}(),
Expand All @@ -87,11 +87,8 @@ mutable struct BilevelModel <: AbstractBilevelModel
Dict{Int, String}(),Dict{String, Int}(),
Dict{Int, Level}(), Dict{Int, JuMP.AbstractConstraint}(), Dict{Int, JuMP.AbstractConstraint}(),
Dict{Int, ConstraintInfo}(),
#obj
MOI.FEASIBILITY_SENSE,
zero(JuMP.GenericAffExpr{Float64, BilevelVariableRef}), # Model objective
MOI.FEASIBILITY_SENSE,
zero(JuMP.GenericAffExpr{Float64, BilevelVariableRef}), # Model objective
nothing,
nothing,

true,
true,
Expand Down Expand Up @@ -138,34 +135,22 @@ struct UpperModel <: InnerBilevelModel
m::BilevelModel
end
Upper(m::BilevelModel) = UpperModel(m)
UpperToLower(m::BilevelModel) = UpperModel(m)
struct LowerModel <: InnerBilevelModel
m::BilevelModel
end
Lower(m::BilevelModel) = LowerModel(m)
LowerToUpper(m::BilevelModel) = LowerModel(m)
bilevel_model(m::InnerBilevelModel) = m.m
mylevel_model(m::UpperModel) = bilevel_model(m).upper
mylevel_model(m::LowerModel) = bilevel_model(m).lower
level(m::LowerModel) = LOWER_ONLY
level(m::UpperModel) = UPPER_ONLY
level(::LowerModel) = LOWER_ONLY
level(::UpperModel) = UPPER_ONLY
mylevel_ctr_list(m::LowerModel) = bilevel_model(m).ctr_lower
mylevel_ctr_list(m::UpperModel) = bilevel_model(m).ctr_upper
mylevel_var_list(m::LowerModel) = bilevel_model(m).var_lower
mylevel_var_list(m::UpperModel) = bilevel_model(m).var_upper

# obj

mylevel_obj_sense(m::LowerModel) = bilevel_model(m).lower_objective_sense
mylevel_obj_function(m::LowerModel) = bilevel_model(m).lower_objective_function
mylevel_obj_sense(m::UpperModel) = bilevel_model(m).upper_objective_sense
mylevel_obj_function(m::UpperModel) = bilevel_model(m).upper_objective_function

set_mylevel_obj_sense(m::LowerModel, val) = bilevel_model(m).lower_objective_sense = val
set_mylevel_obj_function(m::LowerModel, val) = bilevel_model(m).lower_objective_function = val
set_mylevel_obj_sense(m::UpperModel, val) = bilevel_model(m).upper_objective_sense = val
set_mylevel_obj_function(m::UpperModel, val) = bilevel_model(m).upper_objective_function = val

function set_link!(m::UpperModel, upper::JuMP.AbstractVariableRef, lower::JuMP.AbstractVariableRef)
bilevel_model(m).upper_to_lower_link[upper] = lower
bilevel_model(m).link[upper] = lower
Expand Down Expand Up @@ -207,7 +192,7 @@ struct BilevelVariableRef <: JuMP.AbstractVariableRef
idx::Int # Index in `model.variables`
level::Level
end
function BilevelVariableRef(model, idx)
function BilevelVariableRef(model::BilevelModel, idx)
return BilevelVariableRef(model, idx, model.var_level[idx])
end

Expand Down Expand Up @@ -294,11 +279,39 @@ end

# Replace variables

replace_var_type(::Type{BilevelModel}) = JuMP.VariableRef
replace_var_type(::Type{M}) where {M<:JuMP.AbstractModel} = BilevelVariableRef
function build_reverse_var_map!(um::UpperModel)
m = bilevel_model(um)
m.var_upper_rev = Dict{JuMP.AbstractVariableRef, BilevelVariableRef}()
for (idx, ref) in m.var_upper
m.var_upper_rev[ref] = BilevelVariableRef(m, idx)
end
end
function build_reverse_var_map!(lm::LowerModel)
m = bilevel_model(lm)
m.var_lower_rev = Dict{JuMP.AbstractVariableRef, BilevelVariableRef}()
for (idx, ref) in m.var_lower
m.var_lower_rev[ref] = BilevelVariableRef(m, idx)
end
return nothing
end
get_reverse_var_map(m::UpperModel) = m.m.var_upper_rev
get_reverse_var_map(m::LowerModel) = m.m.var_lower_rev
function reverse_replace_variable(f, m::InnerBilevelModel)
build_reverse_var_map!(m)
return replace_variables(f, mylevel_model(m), get_reverse_var_map(m), level(m))
end
function replace_variables(var::VV, # JuMP.VariableRef
model::M,
variable_map::Dict{I, V},
level::Level) where {I,V<:JuMP.AbstractVariableRef, M, VV<:JuMP.AbstractVariableRef}
return variable_map[var]
end
function replace_variables(var::BilevelVariableRef,
model::BilevelModel,
inner::JuMP.AbstractModel,
variable_map::Dict{Int, V},
level::Level) where {V<:JuMP.AbstractVariableRef}
model::M,
variable_map::Dict{I, V},
level::Level) where {I,V<:JuMP.AbstractVariableRef, M<:BilevelModel}
if var.model === model && in_level(var, level)
return variable_map[var.idx]
elseif var.model === model
Expand All @@ -307,32 +320,30 @@ function replace_variables(var::BilevelVariableRef,
error("A BilevelModel cannot have expression using variables of a BilevelModel different from itself")
end
end
function replace_variables(aff::JuMP.GenericAffExpr{C, BilevelVariableRef},
model::BilevelModel,
inner::JuMP.AbstractModel,
variable_map::Dict{Int, V},
level::Level) where {C,V<:JuMP.AbstractVariableRef}
result = JuMP.GenericAffExpr{C, JuMP.VariableRef}(0.0)#zero(aff)
function replace_variables(aff::JuMP.GenericAffExpr{C, VV},
model::M,
variable_map::Dict{I, V},
level::Level) where {I,C,V<:JuMP.AbstractVariableRef, M, VV}
result = JuMP.GenericAffExpr{C, replace_var_type(M)}(0.0)#zero(aff)
result.constant = aff.constant
for (coef, var) in JuMP.linear_terms(aff)
JuMP.add_to_expression!(result,
coef,
replace_variables(var, model, model, variable_map, level))
replace_variables(var, model, variable_map, level))
end
return result
end
function replace_variables(quad::JuMP.GenericQuadExpr{C, BilevelVariableRef},
model::BilevelModel,
inner::JuMP.AbstractModel,
variable_map::Dict{Int, V},
level::Level) where {C,V<:JuMP.AbstractVariableRef}
aff = replace_variables(quad.aff, model, model, variable_map, level)
quadv = JuMP.GenericQuadExpr{C, JuMP.VariableRef}(aff)
function replace_variables(quad::JuMP.GenericQuadExpr{C, VV},
model::M,
variable_map::Dict{I, V},
level::Level) where {I,C,V<:JuMP.AbstractVariableRef, M, VV}
aff = replace_variables(quad.aff, model, variable_map, level)
quadv = JuMP.GenericQuadExpr{C, replace_var_type(M)}(aff)
for (coef, var1, var2) in JuMP.quad_terms(quad)
JuMP.add_to_expression!(quadv,
coef,
replace_variables(var1, model, model, variable_map, level),
replace_variables(var2, model, model, variable_map, level))
replace_variables(var1, model, variable_map, level),
replace_variables(var2, model, variable_map, level))
end
return quadv
end
Expand Down Expand Up @@ -706,4 +717,14 @@ function set_mode(::BilevelVariableRef, ::MixedMode{T}) where T
end
function set_mode(::BilevelVariableRef, ::StrongDualityMode{T}) where T
error("Cant set StrongDualityMode in a specific variable")
end

function MOI.set(::BilevelModel, ::MOI.LazyConstraintCallback, func)
error("Callbacks are not available in BilevelJuMP Models")
end
function MOI.set(::BilevelModel, ::MOI.UserCutCallback, func)
error("Callbacks are not available in BilevelJuMP Models")
end
function MOI.set(::BilevelModel, ::MOI.HeuristicCallback, func)
error("Callbacks are not available in BilevelJuMP Models")
end
122 changes: 114 additions & 8 deletions src/jump_constraints.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ raw_ref(cref::BilevelConstraintRef) = raw_ref(cref.model, cref.index)

function BilevelConstraintRef(model, idx)
raw = raw_ref(model, idx)
JuMP.ConstraintRef(model, idx, raw.shape)
return JuMP.ConstraintRef(model, idx, raw.shape)
end
JuMP.constraint_type(::AbstractBilevelModel) = BilevelConstraintRef
level(cref::BilevelConstraintRef) = cref.model.ctr_level[cref.index]
Expand All @@ -32,7 +32,7 @@ function JuMP.add_constraint(m::InnerBilevelModel, c::Union{JuMP.ScalarConstrain
blm.nextconidx += 1
cref = JuMP.ConstraintRef(blm, blm.nextconidx, JuMP.shape(c))
func = JuMP.jump_function(c)
level_func = replace_variables(func, bilevel_model(m), mylevel_model(m), mylevel_var_list(m), level(m))
level_func = replace_variables(func, bilevel_model(m), mylevel_var_list(m), level(m))
level_c = JuMP.build_constraint(error, level_func, c.set)
level_cref = JuMP.add_constraint(mylevel_model(m), level_c, name)
blm.ctr_level[cref.index] = level(m)
Expand All @@ -42,12 +42,7 @@ function JuMP.add_constraint(m::InnerBilevelModel, c::Union{JuMP.ScalarConstrain
JuMP.set_name(cref, name)
cref
end
function JuMP.delete(m::AbstractBilevelModel, cref::BilevelConstraintRef)
error("can't delete")
# m.need_rebuild = true
# delete!(m.constraints, cref.index)
# delete!(m.connames, cref.index)
end

JuMP.is_valid(m::BilevelModel, cref::BilevelConstraintRef) = cref.index in keys(m.constraints)
JuMP.is_valid(m::InnerBilevelModel, cref::BilevelConstraintRef) =
JuMP.is_valid(bilevel_model(m), cref) && level(cref) == level(m)
Expand Down Expand Up @@ -142,6 +137,13 @@ end
function JuMP.num_constraints(model::UpperModel)
return length(model.m.ctr_upper)
end
function JuMP.num_constraints(model::InnerBilevelModel, f, s)
return JuMP.num_constraints(mylevel_model(model), f, s)
end
function JuMP.num_constraints(model::BilevelModel, f, s)
return JuMP.num_constraints(Upper(model), f, s) +
JuMP.num_constraints(Lower(model), f, s)
end

struct DualOf
ci::BilevelConstraintRef
Expand Down Expand Up @@ -263,4 +265,108 @@ end

function JuMP.normalized_rhs(cref::BilevelConstraintRef)
return JuMP.normalized_rhs(raw_ref(cref))
end

function JuMP.set_normalized_rhs(cref::BilevelConstraintRef, val)
return JuMP.set_normalized_rhs(raw_ref(cref), val)
end

function JuMP.add_to_function_constant(cref::BilevelConstraintRef, val)
return JuMP.add_to_function_constant(raw_ref(cref), val)
end

function JuMP.normalized_coefficient(cref::BilevelConstraintRef, var::BilevelVariableRef)
cidx = cref.index
model = cref.model
level = model.ctr_level[cidx]
vidx = var.idx
level_var = if level == UPPER_ONLY
model.var_upper[vidx]
else
model.var_lower[vidx]
end
return JuMP.normalized_coefficient(raw_ref(cref), level_var)
end

function JuMP.set_normalized_coefficient(
cref::BilevelConstraintRef, var::BilevelVariableRef, val)
cidx = cref.index
model = cref.model
level = model.ctr_level[cidx]
vidx = var.idx
level_var = if level == UPPER_ONLY
model.var_upper[vidx]
else
model.var_lower[vidx]
end
return JuMP.set_normalized_coefficient(raw_ref(cref), level_var, val)
end

function JuMP.list_of_constraint_types(
model::InnerBilevelModel,
)::Vector{Tuple{DataType,DataType}}
return JuMP.list_of_constraint_types(mylevel_model(model))
end
function JuMP.list_of_constraint_types(
model::BilevelModel,
)::Vector{Tuple{DataType,DataType}}
return unique!(vcat(
JuMP.list_of_constraint_types(Upper(model)),
JuMP.list_of_constraint_types(Lower(model)),
))
end

function JuMP.all_constraints(model::BilevelModel, f, s)
return unique!(vcat(
JuMP.all_constraints(Upper(model), f, s),
JuMP.all_constraints(Lower(model), f, s),
))
end

function JuMP.all_constraints(model::InnerBilevelModel, f, s)
build_reverse_ctr_map!(model)
m = mylevel_model(model)
list = JuMP.all_constraints(m, f, s)
get_reverse_ctr_map.(model, list)
end
function build_reverse_ctr_map!(um::UpperModel)
m = bilevel_model(um)
m.ctr_upper_rev = Dict{JuMP.ConstraintRef, JuMP.ConstraintRef}()
for (idx, ref) in m.ctr_upper
m.ctr_upper_rev[ref] = BilevelConstraintRef(m, idx)
end
end
function build_reverse_ctr_map!(lm::LowerModel)
m = bilevel_model(lm)
m.ctr_lower_rev = Dict{JuMP.ConstraintRef, JuMP.ConstraintRef}()
for (idx, ref) in m.ctr_lower
m.ctr_lower_rev[ref] = BilevelConstraintRef(m, idx)
end
return nothing
end
get_reverse_ctr_map(m::UpperModel, idx) = m.m.ctr_upper_rev[idx]
get_reverse_ctr_map(m::LowerModel, idx) = m.m.ctr_lower_rev[idx]

function JuMP.delete(mod::BilevelModel, cref::BilevelConstraintRef)
model = cref.model
@assert model === mod
idx = cref.index
delete!(model.constraints, idx)
delete!(model.connames, idx)
model.need_rebuild_names_ctr = true
delete!(model.ctr_level, idx)
if haskey(model.ctr_upper, idx)
c_up = model.ctr_upper[idx]
delete!(model.ctr_upper, idx)
JuMP.delete(model.upper, c_up)
end
if haskey(model.ctr_lower, idx)
c_lo = model.ctr_lower[idx]
delete!(model.ctr_lower, idx)
JuMP.delete(model.lower, c_lo)
end
delete!(model.ctr_info, idx)
model.ctr_upper_rev = nothing
model.ctr_lower_rev = nothing
return nothing
end
Loading

0 comments on commit 8a8e5ba

Please sign in to comment.