Setup decomposition with BlockDecomposition
BlockDecomposition allows the user to perform two types of decomposition using BlockDecomposition.@dantzig_wolfe_decomposition
and BlockDecomposition.@benders_decomposition
.
For both decompositions, the index-set of the subproblems is declared through an BlockDecomposition.@axis
. It returns an array. Each value of the array is a subproblem index wrapped into a BlockDecomposition.AxisId
. Each time BlockDecomposition finds an AxisId
in the indices of a variable and a constraint, it knows to which subproblem the variable or the constraint belongs.
The macro creates a decomposition tree where the root is the master and the depth is the number of nested decompositions. A classic Dantzig-Wolfe or Benders decomposition produces a decomposition tree of depth 1. At the moment, nested decomposition is not supported.
You can get the subproblem membership of all variables and constraints using the method BlockDecomposition.annotation
.
BlockDecomposition does not change the JuMP model. It decorates the model with additional information. All this information is stored in the ext
field of the JuMP model.
Errors and warnings
BlockDecomposition.MasterVarInDwSp
— TypeError thrown when a master variable is in a constraint that belongs to a Dantzig-Wolfe subproblem.
You can retrieve the JuMP variable and the JuMP constraint where the error occurs:
error.variable
error.constraint
BlockDecomposition.VarsOfSameDwSpInMaster
— TypeWarning when a master constraint involves variables that belong to the same Dantzig-Wolfe subproblem. It means you can move the constraint in a subproblem.
References
BlockDecomposition.BlockModel
— FunctionBlockModel(optimizer [, direct_model = false])
Return a JuMP model which BlockDecomposition will decompose using instructions given by the user.
If you define direct_model = true
, the method creates the model with JuMP.direct_model
, otherwise it uses JuMP.Model
.
These are the methods to decompose a JuMP model :
BlockDecomposition.@axis
— Macro@axis(name, collection)
Declare collection
as an index-set of subproblems. You can access the axis using the variable name
.
Examples
Consider a formulation that has a decomposition which gives raise to 5 subproblems. Let {1,2,3,4,5} be the index-set of the subproblems.
To perform this decomposition with BlockDecomposition, we must declare an axis that contains the index-set of the subproblems :
julia> L = 1:5
1:5
julia> @axis(K, L)
BlockDecomposition.Axis{:K, Int64}(:K, BlockDecomposition.AxisId{:K, Int64}[1, 2, 3, 4, 5])
julia> K[1]
1
julia> typeof(K[1])
BlockDecomposition.AxisId{:K, Int64}
The elements of the axis are AxisId
. You must use AxisId
in the indices of the variables and the constraints that you declare otherwise BlockDecomposition assign them to the master problem.
@variable(model, x[l in L]) # x[l] belongs to the master for any l ∈ L
@variable(model, y[k in K]) # y[k], k ∈ K, belongs to subproblem k (because K is an axis)
BlockDecomposition.@benders_decomposition
— Macro@benders_decomposition(model, name, axis)
Register a Benders decomposition on the JuMP model model
where the index-set of the subproblems is defined by the axis axis
.
Create a variable name
from which the user can access decomposition tree.
BlockDecomposition.@dantzig_wolfe_decomposition
— Macro@dantzig_wolfe_decomposition(model, name, axis)
Register a Dantzig-Wolfe decomposition on the JuMP model model
where the index-set of the subproblems is defined by the axis axis
.
Create a variable name
from which the user can access the decomposition tree.
These are the methods to set additional information to the decomposition (multiplicity and optimizers) :
BlockDecomposition.getmaster
— Functiongetmaster(node) -> MasterForm
Return an object that wraps the annotation that describes the master formulation of a decomposition stored at the node
of the decomposition tree.
This method is not defined if the node is a leaf of the decomposition tree.
BlockDecomposition.getsubproblems
— Functiongetsubproblems(node) -> Vector{SubproblemForm}
Return a vector of objects that wrap the annotations that describe subproblem formulations of a decomposition stored at node
of the decomposition tree.
This method is not defined if the node is a leaf of the decomposition tree.
BlockDecomposition.specify!
— Functionspecify!(
subproblem,
lower_multiplicity = 1,
upper_multiplicity = 1,
solver = nothing,
branching_priority = 1
)
Method that allows the user to specify additional property of the subproblems.
The multiplicity of subproblem
is the number of times that the same independent block shaped by the subproblem in the coefficient matrix appears in the model. It is equivalent to the number of solutions to the subproblem that can appear in the solution of the original problem.
Branching priority of a subproblem is equal to the branching priority of the associated integer variable (the number of columns from this subproblem in the global solution). It also determines the default branching priority of columns generated by this subproblem. Branching priority is also used in rounding and diving heuristics to prioritize which variables and columns to fix the first.
The solver of the subproblem is the way the subproblem will be optimized. It can be either a function (pricing callback), an optimizer of MathOptInterface (e.g. Gurobi.Optimizer
, CPLEX.Optimizer
, Glpk.Optimizer
... with attributes), or nothing
. In the latter case, the solver will use a default optimizer that should be defined in the parameters of the main solver.
Advanced usage : The user can use several solvers to optimize a subproblem :
specify!(subproblem, solver = [Gurobi.Optimizer, my_callback, my_second_callback])
Coluna always uses the first solver by default. Be cautious because changes are always buffered to all solvers. So you may degrade performances if you use a lot of solvers.
This method helps you to check your decomposition :
BlockDecomposition.annotation
— Functionannotation(node)
Return the annotation that describes the master/subproblem of a given node of the decomposition tree.
annotation(model, variable)
annotation(model, constraint)
Return the subproblem to which a variable or a constraint belongs.