API
This section documents the BilevelJuMP API.
As a JuMP extension, most JuMP functions should just work. Some JuMP functions will return an error saying they are not implemented for BilevelJuMP structures such as BilevelModel. If that happens and you consider that function should be implemented, please, open an issue.
Constructors
BilevelJuMP.BilevelModel — TypeBilevelModel()Create an empty BilevelModel with default settings, no solver and no solve mode.
Example
julia> model = BilevelModel()BilevelModel(solver::Function; mode = BilevelJuMP.SOS1Mode(), add_bridges::Bool = true)Create a BilevelModel with the given solver and solve mode.
solver: is a function that takes no arguments and returns a JuMP solver object.mode: is a solve mode object that defines how the model is solved.add_bridges: iftrue(default) then bridges are added to the model. Iffalsethen bridges are not added and the model is not modified.
Example
julia> model = BilevelModel(
HiGHS.Optimizer,
mode = BilevelJuMP.FortunyAmatMcCarlMode(primal_big_M = 1e6, dual_big_M = 1e6))which is equivalent to
julia> model = BilevelModel(
()->HiGHS.Optimizer(),
mode = BilevelJuMP.FortunyAmatMcCarlMode(primal_big_M = 1e6, dual_big_M = 1e6))and equivalent to
julia> model = BilevelModel()
julia> set_optimizer(model, HiGHS.Optimizer)
julia> BilevelJuMP.set_mode(model, BilevelJuMP.FortunyAmatMcCarlMode(primal_big_M = 1e6, dual_big_M = 1e6))BilevelJuMP.Upper — FunctionUpper(model::BilevelModel)Create a reference to the upper level of a bilevel model.
Example
julia> model = BilevelModel();
julia> @variable(Upper(model), x >= 0)BilevelJuMP.Lower — FunctionLower(model::BilevelModel)Create a reference to the lower level of a bilevel model.
Example
julia> model = BilevelModel();
julia> @variable(Lower(model), x >= 0)BilevelJuMP.DualOf — TypeDualOf(constraint::ConstraintRef)Get the dual variable associated with a constraint. This is only valid for constraints in the upper level of a bilevel model.
Examples
julia> m = BilevelModel();
julia> @variable(Lower(m), x >= 0);
julia> @constraint(Lower(m), c, x <= 1);
julia> @variable(Upper(m), y, DualOf(c));Advanced constructors
BilevelJuMP.UpperOnly — FunctionUpperOnly(model::BilevelModel)Create a special reference to the upper level of a bilevel model. Variables created with this reference will not be shared with the lower level.
BilevelJuMP.LowerOnly — FunctionLowerOnly(model::BilevelModel)Create a special reference to the lower level of a bilevel model. Variables created with this reference will not be shared with the upper level.
Enums
BilevelJuMP.Level — TypeLevelThe level of a variable in a bilevel problem.
BilevelJuMP.LOWER_BOTH — ConstantIndicates an object that is part of the lower level problem, but is shared with the upper level.
BilevelJuMP.UPPER_BOTH — ConstantIndicates an object that is part of the upper level problem, but is shared with the lower level.
BilevelJuMP.LOWER_ONLY — ConstantIndicates an object that is part of the lower level problem, but is not shared with the upper level.
BilevelJuMP.UPPER_ONLY — ConstantIndicates an object that is part of the upper level problem, but is not shared with the lower level.
BilevelJuMP.DUAL_OF_LOWER — ConstantIndicates an object that is part of the dual of the lower level problem, and is shared with the upper level.
BilevelJuMP.IndicatorSetting — TypeIndicatorSettingThe type of indicator function to use in the IndicatorMode mode.
BilevelJuMP.ZERO_ONE — ConstantActivates the indicator constraint on the primal constraint if the auxiliary binary is one and activates the indicator constraint on the dual variable if the auxiliary binary is zero.
BilevelJuMP.ZERO_ZERO — ConstantActivates the indicator constraint on the primal constraint if the auxiliary binary is zero and activates the indicator constraint on the dual variable if the auxiliary binary is zero.
BilevelJuMP.ONE_ONE — ConstantActivates the indicator constraint on the primal constraint if the auxiliary binary is one and activates the indicator constraint on the dual variable if the auxiliary binary is one.
Structs
BilevelJuMP.BilevelVariableRef — TypeBilevelVariableRefHolds a reference to a variable in a bilevel model.
BilevelJuMP.BilevelAffExpr — TypeBilevelAffExprAlias for GenericAffExpr{Float64,BilevelVariableRef}.
BilevelJuMP.BilevelQuadExpr — TypeBilevelQuadExprAlias for GenericQuadExpr{Float64,BilevelVariableRef}.
Modes
BilevelJuMP.SOS1Mode — TypeSOS1Mode()Used to solve a bilevel problem with the MPEC reformulation using SOS1 constraints to convert complementarity constraints into mixed-integer constraints.
BilevelJuMP.BigMMode — TypeBigMMode(; with_slack = false, primal_big_M = Inf, dual_big_M = Inf)Used to solve a bilevel problem with the MPEC reformulation using Fortuny-Amat and McCarl's big-M method to convert complementarity constraints to a mixed integer formulation.
with_slackindicates whether to use slack variables to convert the complementarity constraints to a mixed integer formulation. Iffalse, the reformulation of a constraint likeexpr <= 0isexpr <= big_M * (1 - binary)andvar <= big_M * binary, wherevaris the associated dual variable. Iftrue, the reformulation isexpr == slack,slack <= big_M * (1 - binary)andvar <= big_M * binary.primal_big_Mis a big-M used for primal variables that have no bounds so we can compute the big-M for the primal constraint.dual_big_Mis a big-M used for dual variables that have no bounds so we can compute the big-M for the dual constraint.
Also known as FortunyAmatMcCarlMode (which can be used interchangeably).
BilevelJuMP.FortunyAmatMcCarlMode — TypeFortunyAmatMcCarlModeSee BigMMode for more details.
BilevelJuMP.IndicatorMode — TypeIndicatorMode(method::IndicatorSetting = BilevelJuMP.ONE_ONE)Used to solve a bilevel problem with the MPEC reformulation using indicator constraints to convert complementarity constraints to a mixed integer formulation.
methodindicates how the indicator constraints are activated for primal constraints and dual variables. SeeIndicatorSettingfor more details.
BilevelJuMP.ProductMode — TypeProductMode(epsilon = 0.0; with_slack = false, aggregation_group = nothing)Used to solve a bilevel problem with the MPEC reformulation using products to convert complementarity constraints into non-convex quadratic constraints.
epsilonis the tolerance used to relax the products. Given a pairexprandvar, the reformulation isexpr * var <= epsiloninstead ofexpr * var == 0. Defaults to0.0.with_slackindicates whether to use slack variables to reformulate the complementarity constraints. Given a pairexprandvar, the reformulation isexpr == slackandvar * slack == 0instead ofexpr * slack == 0.aggregation_groupindicates whether to aggregate the products into a single quadratic constraint. Ifaggregation_groupisnothing, then each product is converted into a quadratic constraint. Ifaggregation_groupis a positive integer, then products with the sameaggregation_groupare aggregated into a single quadratic constraint.
BilevelJuMP.StrongDualityMode — TypeStrongDualityMode(eps = 0.0; inequality = true)A mode that adds a strong duality constraint of the lower level problem instead of reformulating the complementarity constraints.
eps: The tolerance for the strong duality constraint. Defaults to0.0.inequality: Iftruethe strong duality constraint is added as two inequality constraints. Iffalsethe strong duality constraint is added as an equality constraint. Defaults totrue.
BilevelJuMP.ComplementMode — TypeComplementMode(; with_slack = false)Used to solve a bilevel problem with the MPEC reformulation using actual complementarity constraints. A limited number of solvers support this mode. One example is Knitro.
with_slackindicates whether to use slack variables to reformulate the complementarity constraints. Given a pairexprandvar, the reformulation isexpr == slackandvar ⟂ slackinstead ofexpr ⟂ slack.
BilevelJuMP.MixedMode — TypeMixedMode(; default = SOS1Mode())A mode that allows to mix different modes for different constraints and variables.
defaultis the default mode to use for all constraints and variables that are not explicitly mapped to a mode.
BilevelJuMP.MibSMode — TypeMibSMode(mibs_call; verbose = false, debug_dir = "", check_integrality = true)Solve the bilevel problem with MibS, an external mixed integer bilevel solver.
Unlike the other modes, MibS does not reformulate the problem into a single optimization problem that is handed to a MathOptInterface solver. It is a standalone executable that reads the problem from a file, so there is no optimizer to attach and set_optimizer must not be called.
mibs_callis the MibS executable. It is obtained from theMibS_jllpackage, which is deliberately not a dependency of BilevelJuMP: install and load it yourself, then passMibS_jll.mibs.verboseprints the MibS log.debug_diris a directory to copy the files given to MibS into, for inspection. The default keeps them only when the solve fails, in which case the error message reports where they were saved.check_integralityerrors if the model has a continuous variable. MibS may run forever instead of reporting an error on such a model, so this check is on by default. Set it tofalseto attempt the solve anyway.
MibS requires both levels to be linear and, in practice, every variable to be integer. Models it cannot represent are rejected with an error by optimize!.
Results are queried with the usual JuMP functions: termination_status, primal_status, objective_value, and value. Dual solutions are not available, because MibS never forms the dual of the lower level.
Example
using BilevelJuMP, MibS_jll
model = BilevelModel()
BilevelJuMP.set_mode(model, BilevelJuMP.MibSMode(MibS_jll.mibs))
@variable(Upper(model), x, Int)
@variable(Lower(model), y, Int)
@objective(Upper(model), Min, -3x - 7y)
@constraint(Upper(model), u1, x <= 10)
@objective(Lower(model), Min, y)
@constraint(Lower(model), l1, 2x - y <= 7)
optimize!(model)
termination_status(model)
objective_value(model)
value(x)
value(y)Bound hints
BilevelJuMP.set_dual_upper_bound_hint — Functionset_dual_upper_bound_hint(cref, value)Set an upper bound to the dual variable of the constraint cref to value. This bound will not be dualized. The dual upper bound hint is used to help the solution method.
Solution modes can be benefitted from this hint:
BigMModewill use this information to compute a tighter bound for the dual variable.Other modes will be stabilized by the existence of the bounds on variables that would otherwise not be bounded.
Bounds that are not dualized are also useful for binary expansions of products of variables that can be done with
QuadraticToBinary.jl.
BilevelJuMP.get_dual_upper_bound_hint — Functionget_dual_upper_bound_hint(cref)Get the upper bound to the dual variable of the constraint cref that was set with set_dual_upper_bound_hint.
BilevelJuMP.set_dual_lower_bound_hint — Functionset_dual_lower_bound_hint(cref, value)Set a lower bound to the dual variable of the constraint cref to value. This bound will not be dualized. The dual lower bound hint is used to help the solution method.
Solution modes can be benefitted from this hint:
BigMModewill use this information to compute a tighter bound for the dual variable.Other modes will be stabilized by the existence of the bounds on variables that would otherwise not be bounded.
Bounds that are not dualized are also useful for binary expansions of products of variables that can be done with
QuadraticToBinary.jl.
BilevelJuMP.get_dual_lower_bound_hint — Functionget_dual_lower_bound_hint(cref)Get the lower bound to the dual variable of the constraint cref that was set with set_dual_lower_bound_hint.
BilevelJuMP.set_primal_upper_bound_hint — Functionset_primal_upper_bound_hint(vref, value)Set an upper bound to the primal variable vref to value. This bound will not be dualized. The upper bound hint is used to help the solution method.
Solution modes can be benefitted from this hint:
BigMModewill use this information to compute a tighter bound for the primal constraint variable.Other modes will be stabilized by the existence of the bounds on variables that would otherwise not be bounded.
Bounds that are not dualized are also useful for binary expansions of products of variables that can be done with
QuadraticToBinary.jl.
BilevelJuMP.get_primal_upper_bound_hint — Functionget_primal_upper_bound_hint(vref)Get the upper bound to the primal variable vref that was set with set_primal_upper_bound_hint.
BilevelJuMP.set_primal_lower_bound_hint — Functionset_primal_lower_bound_hint(vref, value)Set a lower bound to the primal variable vref to value. This bound will not be dualized. The lower bound hint is used to help the solution method.
Solution modes can be benefitted from this hint:
BigMModewill use this information to compute a tighter bound for the primal constraint variable.Other modes will be stabilized by the existence of the bounds on variables that would otherwise not be bounded.
Bounds that are not dualized are also useful for binary expansions of products of variables that can be done with
QuadraticToBinary.jl.
BilevelJuMP.get_primal_lower_bound_hint — Functionget_primal_lower_bound_hint(vref)Get the lower bound to the primal variable vref that was set with set_primal_lower_bound_hint.
Attributes getters and setters
BilevelJuMP.lower_objective_value — Functionlower_objective_value(model::BilevelModel; result::Int = 1)Return the value of the objective function of the lower level problem.
BilevelJuMP.build_time — Functionbuild_time(model::BilevelModel)Return the time it took to build the model.
BilevelJuMP.set_mode — Functionset_mode(bm::BilevelModel, mode::AbstractBilevelSolverMode)Set the mode of a bilevel model.
set_mode(ci::BilevelConstraintRef, mode::AbstractBilevelSolverMode)Set the mode of a constraint. This is used in MixedMode reformulations.
set_mode(vi::BilevelVariableRef, mode::AbstractBilevelSolverMode)Set the mode of the bounds of a variable. This is used in MixedMode reformulations.
BilevelJuMP.get_mode — Functionget_mode(ci::BilevelConstraintRef)Get the mode of a constraint. This is used in MixedMode reformulations.
get_mode(vi::BilevelVariableRef)Get the mode of the bounds of a variable. This is used in MixedMode reformulations.
BilevelJuMP.unset_mode — Functionunset_mode(ci::BilevelConstraintRef)Unset the mode of a constraint. This will use the default mode for the constraint. This is used in MixedMode reformulations.
unset_mode(vi::BilevelVariableRef)Unset the mode of the bounds of a variable. This will use the default mode for the bounds. This is used in MixedMode reformulations.
BilevelJuMP.set_copy_names — Functionset_copy_names(model::BilevelModel)Set the copy_names attribute of the solver to true.
BilevelJuMP.get_copy_names — Functionget_copy_names(model::BilevelModel)Return the value of the copy_names attribute of the solver.
BilevelJuMP.unset_copy_names — Functionunset_copy_names(model::BilevelModel)Set the copy_names attribute of the solver to false.
BilevelJuMP.set_pass_start — Functionset_pass_start(model::BilevelModel)Activate passing start values (both primal and dual) to the solver.
BilevelJuMP.get_pass_start — Functionget_pass_start(model::BilevelModel)Checks if passing start values (both primal and dual) to the solver is activated.
BilevelJuMP.unset_pass_start — Functionunset_pass_start(model::BilevelModel)Deactivate passing start values (both primal and dual) to the solver.