Column generation
Coluna provides an interface and generic functions to implement a multi-stage column generation algorithm together with a default implementation of this algorithm.
In this section, we are first going to present the generic functions, the implementation with some theory backgrounds and then give the references of the interface.
You can find the generic functions and the interface in the ColGen
submodule and the default implementation in the Algorithm
submodule at src/Algorithm/colgen
.
Context
The ColGen
submodule provides an interface and generic functions to implement a column generation algorithm. The implementation depends on an object called context
.
Coluna.ColGen.AbstractColGenContext
— TypeSupertype for the objects to which belongs the implementation of the column generation and that stores any kind of information during the execution of the column generation algorithm.
IMPORTANT: implementation of the column generation mainly depends on the context type.
Coluna provides two types of context:
Coluna.Algorithm.ColGenContext
— TypeColGenContext(reformulation, algo_params) -> ColGenContext
Creates a context to run the default implementation of the column generation algorithm.
Coluna.Algorithm.ColGenPrinterContext
— TypeColGenPrinterContext(reformulation, algo_params) -> ColGenPrinterContext
Creates a context to run the default implementation of the column generation algorithm together with a printer that prints information about the algorithm execution.
Generic functions
Generic functions are the core of the column generation algorithm. There are three generic functions:
Coluna.ColGen.run!
— Functionrun!(ctx, env, ip_primal_sol; iter = 1) -> AbstractColGenOutput
Runs the column generation algorithm.
Arguments are:
ctx
: column generation contextenv
: Coluna environmentip_primal_sol
: current best primal solution to the master problemiter
: iteration number (default: 1)
This function is responsible for initializing the column generation context, the reformulation, and the stabilization. We iterate on the loop each time the phase or the stage changes.
See the main loop section for more details.
Coluna.ColGen.run_colgen_phase!
— Functionrun_colgen_phase!(ctx, phase, stage, env, ip_primal_sol, stab; iter = 1) -> AbstractColGenPhaseOutput
Runs a phase of the column generation algorithm.
Arguments are:
ctx
: column generation contextphase
: current column generation phasestage
: current column generation stageenv
: Coluna environmentip_primal_sol
: current best primal solution to the master problemstab
: stabilizationiter
: iteration number (default: 1)
This function is responsible for running the column generation iterations until the phase is finished.
See the phase loop section for more details.
Coluna.ColGen.run_colgen_iteration!
— Functionrun_colgen_iteration!(context, phase, stage, env, ip_primal_sol, stab) -> AbstractColGenIterationOutput
Runs an iteration of column generation.
Arguments are:
context
: column generation contextphase
: current column generation phasestage
: current column generation stageenv
: Coluna environmentip_primal_sol
: current best primal solution to the master problemstab
: stabilization
See the column generation iteration section for more details.
They are independent of any other submodule of Coluna. You can use them to implement your own column generation algorithm.
Reformulation
The default implementation works with a reformulated problem contained in MathProg.Reformulation
where master and subproblems are MathProg.Formulation
objects.
The master has the following form:
\[\begin{aligned} \min \quad& \sum_{k \in K} c^k \lambda^k+\bar{c} y & \\ \text{s.t.} \quad& \sum_{k \in K} A^k \lambda^k+\bar{A} y \geq a & (1)\\ & l_k \leq \mathbf{1} \lambda^k \leq u_k & (2) \\ & \bar{l} \leq y \leq \bar{u} & (3) \end{aligned}\]
where $\lambda$ are the master columns, $y$ are the pure master variables, constraints (1) are the linking constraints, constraints (2) are the convexity constraints that depend on $l_k$ and $u_k$ (e.g. the lower and upper multiplicity of the subproblem $k$ respectively), and constraints (3) are the bounds on the pure master variables.
The subproblems have the following form:
\[\begin{aligned} \min \quad& cx + 0z \\ \text{s.t.} \quad& Bx \geq b \\ & 1 \leq z \leq 1 \end{aligned}\]
where $x$ are the subproblem variables, $z$ is a setup variable that always takes the value one in a solution to the subproblem.
The coefficients of the columns in constraints (1) and (2) of the master are computed using representative variables of the subproblems. You can read this section (TODO Natacha) to understand how we map the subproblem solutions into master columns.
References:
Coluna.ColGen.get_reform
— FunctionReturns Dantzig-Wolfe reformulation.
Coluna.ColGen.get_master
— FunctionReturns master formulation.
Coluna.ColGen.get_pricing_subprobs
— Functionget_pricing_subprobs(ctx) -> Vector{Tuple{SuproblemId, SpFormulation}}
Returns subproblem formulations.
Coluna.ColGen.is_minimization
— FunctionReturns true
if the objective sense is minimization; false
otherwise.
Main loop
This is a description of how the Coluna.ColGen.run!
generic function behaves in the default implementation.
The main loop stops when the Coluna.ColGen.stop_colgen
method returns true
. This is the case when one of the following conditions holds:
- the master or a pricing subproblem is infeasible
- the time limit is reached
- the maximum number of iterations is reached
Otherwise, the main loop runs until there is no more phase or stage to execute.
The method returns:
Coluna.Algorithm.ColGenOutput
— TypeOutput of the default implementation of the column generation algorithm.
References:
Coluna.ColGen.stop_colgen
— FunctionReturns true
when the column generation algorithm must stop; false
otherwise.
Coluna.ColGen.setup_reformulation!
— FunctionSetup the reformulation for the given phase.
Coluna.ColGen.setup_context!
— FunctionSetup the context for the given phase.
Coluna.ColGen.AbstractColGenOutput
— TypeSupertype for the objects that contains the output of the column generation algorithm.
Coluna.ColGen.colgen_output_type
— Functioncolgen_output_type(ctx) -> Type{<:AbstractColGenOutput}
Returns the type of the column generation output associated to the context.
Coluna.ColGen.new_output
— Functionnew_output(OutputType, colgen_phase_output) -> OutputType
Returns the column generation output where colgen_phase_output
is the output of the last column generation phase executed.
Phase loop
This is a description of how the Coluna.ColGen.run_colgen_phase!
generic function behaves in the default implementation.
This function is responsible for maintaining the incumbent dual bound and the incumbent master IP primal solution.
The phase loop stops when the Coluna.ColGen.stop_colgen_phase
method returns true
. This is the case when one of the following conditions holds:
- the maximum number of iterations is reached
- the time limit is reached
- the master is infeasible
- the master is unbounded
- a pricing subproblem is infeasible
- a pricing subproblem is unbounded
- there is no new column generated at the last iteration
- there is a new constraint or valid inequality in the master
- the incumbent dual bound and the primal master LP solution value converged
The method returns:
Coluna.Algorithm.ColGenPhaseOutput
— TypeOutput of the default implementation of a phase of the column generation algorithm.
References:
Coluna.ColGen.stop_colgen_phase
— FunctionReturns true
if the column generation phase must stop.
Coluna.ColGen.before_colgen_iteration
— FunctionPlaceholder method called before the column generation iteration. Does nothing by default but can be redefined to print some informations for instance. We strongly advise users against the use of this method to modify the context or the reformulation.
Coluna.ColGen.after_colgen_iteration
— FunctionPlaceholder method called after the column generation iteration. Does nothing by default but can be redefined to print some informations for instance. We strongly advise users against the use of this method to modify the context or the reformulation.
Coluna.ColGen.is_better_dual_bound
— FunctionReturns true
if new_dual_bound
is better than dual_bound
; false
otherwise.
Phase iterator
In the first iterations, the restricted master LP contains a few columns and may be infeasible. To prevent this, we introduced artificial variables $v$ and we activate/deactivate these variables depending on whether we want to prove the infeasibility of the master LP or find the optimal LP solution. The default implementation provides three phases:
Coluna.Algorithm.ColGenPhase0
— TypePhase 0 is a mix of phase 1 and phase 2. It sets a very large cost to artifical variables to force them to be removed from the master LP solution. If the final master LP solution contains artifical variables either the master is infeasible or the cost of artificial variables is not large enough. Phase 1 must be run.
Coluna.Algorithm.ColGenPhase1
— TypePhase 1 sets the cost of variables to 0 except for artifical variables. The goal is to find a solution to the master LP problem that has no artificial variables.
Coluna.Algorithm.ColGenPhase2
— TypePhase 2 solves the master LP without artificial variables. To start, it requires a set of columns that forms a feasible solution to the LP master. This set is found with phase 1.
Column generation always starts with Phase 0.
The default implementation of the phase iterator belongs to the following type:
Coluna.Algorithm.ColunaColGenPhaseIterator
— TypeType for the default implementation of the sequence of phases.
Transitions between the phases depend on four conditions:
- (A) the presence of artificial variables in the master LP solution
- (B) the generation of new essential constraints (may happen when a new master IP solution is found)
- (C) the current stage is exact
- (D) column generation converged
Transitions are the following:
References:
Coluna.ColGen.AbstractColGenPhase
— TypeA phase of the column generation. Each phase is associated with a specific set up of the reformulation.
Coluna.ColGen.AbstractColGenPhaseIterator
— TypeAn iterator that indicates how phases follow each other.
Coluna.ColGen.new_phase_iterator
— FunctionReturns a new phase iterator.
Coluna.ColGen.initial_phase
— FunctionReturns the phase with which the column generation algorithm must start.
Coluna.ColGen.decrease_stage
— FunctionReturns the next stage involving a "more exact solver" than the current one. Returns nothing
if the algorithm has already reached the exact phase (last phase).
Coluna.ColGen.next_phase
— FunctionReturns the next phase of the column generation algorithm. Returns nothing
if the algorithm must stop.
Phase output
Coluna.ColGen.AbstractColGenPhaseOutput
— TypeSupertype for the objects that contains the output of a column generation phase.
Coluna.ColGen.colgen_phase_output_type
— Functioncolgen_phase_outputype(ctx) -> Type{<:AbstractColGenPhaseOutput}
Returns the type of the column generation phase output associated to the context.
Coluna.ColGen.new_phase_output
— Functionnew_phase_output(OutputType, min_sense, phase, stage, colgen_iter_output, iter, inc_dual_bound) -> OutputType
Returns the column generation phase output.
Arguments of this function are:
OutputType
: the type of the column generation phase outputmin_sense
:true
if it is a minimization problem;false
otherwisephase
: the current column generation phasestage
: the current column generation stagecol_gen_iter_output
: the last column generation iteration outputiter
: the last iteration numberinc_dual_bound
: the current incumbent dual bound
Stages
A stage is a set of consecutive iterations in which we use a given pricing solver. The aim is to speed up the resolution of the pricing problem by first using an approximate but fast pricing algorithm and then switching to increasingly less heuristic algorithms until the last stage where an exact solver is used. and an exact solver at the last stage. Given a pricing solver, when the column generation does not progress anymore or the pricing solver does not return any new column, the default implementation switch to a more exact pricing solver. Stages are created using the stages_pricing_solver_ids
of the ColumnGenerationAlgorithm
parameter object. The default implementation implements the interface around the following object:
Coluna.Algorithm.ColGenStageIterator
— TypeDefault implementation of the column generation stages works as follows.
Consider a set {A,B,C} of subproblems each of them associated to the following sets of pricing solvers: {a1, a2, a3}, {b1, b2}, {c1, c2, c3, c4}. Pricing solvers a1, b1, c1 are exact solvers; others are heuristic.
The column generation algorithm will run the following stages:
- stage 4 with pricing solvers {a3, b2, c4}
- stage 3 with pricing solvers {a2, b1, c3}
- stage 2 with pricing solvers {a1, b1, c2}
- stage 1 with pricing solvers {a1, b1, c1} (exact stage)
Column generation moves from one stage to another when all solvers find no column.
References:
Coluna.ColGen.AbstractColGenStage
— TypeA stage of the column generation algorithm. Each stage is associated to a specific solver for each pricing subproblem.
Coluna.ColGen.AbstractColGenStageIterator
— TypeAn iterator that indicates how stages follow each other.
Coluna.ColGen.new_stage_iterator
— FunctionReturns a new stage iterator.
Coluna.ColGen.initial_stage
— FunctionReturns the stage at which the column generation algorithm must start.
Coluna.ColGen.next_stage
— FunctionReturns the next stage that column generation must use.
Coluna.ColGen.get_pricing_subprob_optimizer
— FunctionReturns the optimizer for the pricing subproblem associated to the given stage.
Coluna.ColGen.stage_id
— FunctionReturns the id of the stage.
Coluna.ColGen.is_exact_stage
— FunctionReturns true
if the stage uses an exact solver for the pricing subproblem; false
otherwise.
Column generation iteration
This is a description of how the Coluna.ColGen.run_colgen_iteration!
generic function behaves in the default implementation.
These are the main steps of a column generation iteration without stabilization. Click on the step to go to the relevant section.
Optimize master LP
At each iteration, the algorithm requires a dual solution to the master LP to compute the reduced cost of subproblem variables.
The default implementation optimizes the master with an LP solver through MathOptInterface. It returns a primal and a dual solution.
In the default implementation, the master LP output is in the following data structure:
Coluna.Algorithm.ColGenMasterResult
— TypeOutput of the ColGen.optimize_master_lp_problem!
method.
Contains result
, an OptimizationState
object that is the output of the SolveLpForm
algorithm called to optimize the master LP problem.
References:
Coluna.ColGen.optimize_master_lp_problem!
— Functionoptimize_master_lp_problem!(master, context, env) -> MasterResult
Returns an instance of a custom object MasterResult
that implements the following methods:
get_obj_val
: objective value of the master (mandatory)get_primal_sol
: primal solution to the master (optional)get_dual_sol
: dual solution to the master (mandatory otherwise column generation stops)
It should at least return a dual solution (obtained with LP optimization or subgradient) otherwise column generation cannot continue.
You can see the additional methods to implement in the result data structures section.
Go back to the column generation iteration overview.
Check the integrality of the master LP solution
The algorithm checks the integrality of the primal solution to the master LP to improve the global primal bound of the branch-cut-price algorithm.
By default, the integrality check is done using the MathProg.proj_cols_is_integer
method. It implements the mapping procedure from the paper "F. Vanderbeck, Branching in branch-and-price: a generic scheme, Math.Prog. (2011)". Basically, it sorts the column used in the master LP primal solution in lexicographic order. It assigns a weight to each column equal to the value of the column in the master LP solution. It then forms columns of weight one by accumulating the columns of the fractional solution. If columns are integral, the solution is integral. This is a heuristic procedure so it can miss some integer solutions.
In the case the pricing subproblems are solved by a callback, and some subproblem integer variables are "hidden" from Coluna (values of these variables are usually stored in CustomData
associated with the pricing problem solution), the mapping procedure may not be valid. In this case, the integrality should be checked in the "strict" way, i.e., by explicitly verifying that all columns are integer.
Integrality check procedure is set using parameter strict_integrality_check
(false
by default) of the ColumnGenerationAlgorithm
.
If the solution is integral, the essential cut callback is called to make sure it is feasible.
References:
Coluna.ColGen.check_primal_ip_feasibility!
— FunctionReturns a primal solution expressed in the original problem variables if the current master LP solution is integer feasible; nothing
otherwise.
Coluna.ColGen.is_better_primal_sol
— FunctionReturns true
if the new master IP primal solution is better than the current; false
otherwise.
Go back to the column generation iteration overview.
Update incumbent primal solution
If the solution to master LP is integral and better than the current best one, we need to update the incumbent. This solution is then used by the tree-search algorithm in the bounding mechanism that prunes the nodes.
References:
Coluna.ColGen.update_inc_primal_sol!
— FunctionUpdates the current master IP primal solution.
Go back to the column generation iteration overview.
Reduced costs calculation
Reduced costs calculation is written as a math operation in the run_colgen_iteration!
generic function. As a consequence, the dual solution to the master LP and the implementation of the two following methods must return data structures that support math operations.
To speed up this operation, we cache data in the following data structure:
Coluna.Algorithm.ReducedCostsCalculationHelper
— TypeExtracted information to speed-up calculation of reduced costs of subproblem representatives and pure master variables. We extract from the master the information we need to compute the reduced cost of DW subproblem variables:
dw_subprob_c
contains the perenial cost of DW subproblem representative variablesdw_subprob_A
is a submatrix of the master coefficient matrix that involves only DW subproblem representative variables.
We also extract from the master the information we need to compute the reduced cost of pure master variables:
pure_master_c
contains the perenial cost of pure master variablespure_master_A
is a submatrix of the master coefficient matrix that involves only pure master variables.
Calculation is c - transpose(A) * master_lp_dual_solution
.
This information is given to the generic implementation of the column generation algorithm through methods:
- ColGen.getsubprobvarorigcosts
- ColGen.getorigcoefmatrix
Reduced costs calculation also requires the implementation of the two following methods:
Coluna.ColGen.update_master_constrs_dual_vals!
— FunctionUpdates dual value of the master constraints. Dual values of the constraints can be used when the pricing solver supports non-robust cuts.
Coluna.ColGen.update_reduced_costs!
— FunctionMethod that you can implement if you want to store the reduced cost of subproblem variables in the context.
Coluna.ColGen.get_subprob_var_orig_costs
— FunctionReturns the original cost c
of subproblems variables. to compute reduced cost ̄c = c - transpose(A) * π
.
Coluna.ColGen.get_subprob_var_coef_matrix
— FunctionReturns the coefficient matrix A
of subproblem variables in the master to compute reduced cost ̄c = c - transpose(A) * π
.
Coluna.ColGen.update_sp_vars_red_costs!
— FunctionUpdates reduced costs of variables of a given subproblem.
Go back to the column generation iteration overview.
Pricing subproblem iterator
The pricing strategy is basically an iterator used to iterate over the pricing subproblems to optimize at each iteration of the column generation. The context can serve as a memory of the pricing strategy to change the way we iterate over subproblems between each column generation iteration.
The default implementation iterates over all subproblems.
Here are the references for the interface:
Coluna.ColGen.AbstractPricingStrategy
— TypeA pricing strategy defines how we iterate on pricing subproblems. A default pricing strategy consists in iterating on all pricing subproblems.
Basically, this object is used like this:
pricing_strategy = ColGen.get_pricing_strategy(ctx, phase)
next = ColGen.pricing_strategy_iterate(pricing_strategy)
while !isnothing(next)
(sp_id, sp_to_solve), state = next
# Solve the subproblem `sp_to_solve`.
next = ColGen.pricing_strategy_iterate(pricing_strategy, state)
end
Coluna.ColGen.get_pricing_strategy
— Functionget_pricing_strategy(ctx, phase) -> AbstractPricingStrategy
Returns the pricing strategy object.
Coluna.ColGen.pricing_strategy_iterate
— Functionpricing_strategy_iterate(pricing_strategy) -> ((sp_id, sp_to_solve), state)
pricing_strategy_iterate(pricing_strategy, state) -> ((sp_id, sp_to_solve), state)
Returns an iterator with the first pricing subproblem that must be optimized. The next subproblem is returned by a call to Base.iterate
using the information provided by this method.
Go back to the column generation iteration overview.
Pricing subproblem optimization
At each iteration, the algorithm requires primal solutions to the pricing subproblems. The generic function supports multi-column generation so you can return any number of solutions.
The default implementation supports optimization of the pricing subproblems using a MILP solver or a pricing callback. Non-robust valid inequalities are not supported by MILP solvers as they change the structure of the subproblems. When using a pricing callback, you must be aware of how Coluna calculates the reduced cost of a column:
The reduced cost of a column is split into three contributions:
- the contribution of the subproblem variables that is the primal solution cost given the reduced cost of subproblem variables
- the contribution of the non-robust constraints (i.e. master constraints that cannot be expressed using subproblem variables except the convexity constraint) that is not supported by MILP solver but that you must take into account in the pricing callback
- the contribution of the master convexity constraint that is automatically taken into account by Coluna once the primal solution is returned.
Therefore, when you use a pricing callback, you must not discard some columns based only on the primal solution cost because you don't know the contribution of the convexity constraint.
Coluna.Algorithm.GeneratedColumn
— TypeSolution to a pricing subproblem after a given optimization.
It contains:
column
: the solution stored as aPrimalSolution
objectred_cost
: the reduced cost of the columnmin_obj
: a boolean indicating if the objective is to minimize or maximize
Coluna.Algorithm.ColGenPricingResult
— TypeOutput of the default implementation of ColGen.optimize_pricing_problem!
.
It contains:
result
: the output of theSolveIpForm
algorithm called to optimize the pricing subproblemcolumns
: a vector ofGeneratedColumn
objects obtained by processing of the output of pricing subproblem optimization, it stores the reduced cost of each columnbest_red_cost
: the best reduced cost of the columns
References:
Coluna.ColGen.optimize_pricing_problem!
— Functionoptimize_pricing_problem!(ctx, sp, env, optimizer, mast_dual_sol) -> PricingResult
Returns a custom object PricingResult
that must implement the following functions:
get_primal_sols
: array of primal solution to the pricing subproblemget_primal_bound
: best reduced cost (optional ?)get_dual_bound
: dual bound of the pricing subproblem (used to compute the master dual bound)master_dual_sol
: dual solution $\pi^{\text{out}}$ to the master problem used to compute the real reduced cost of the column when stabilization is active
You can see the additional methods to implement in the result data structures section.
Go back to the column generation iteration overview.
Set of generated columns
You can define your data structure to manage the columns generated at a given iteration. Columns are inserted after the optimization of all pricing subproblems to allow the parallelization of the latter.
In the default implementation, we use the following data structure:
Coluna.Algorithm.ColumnsSet
— TypeStores a collection of columns.
It contains:
columns
: a vector ofGeneratedColumn
objects by all pricing subproblems that will be inserted into the mastersubprob_primal_solutions
: an object that stores the best columns generated by each pricing subproblem at this iteration.
Coluna.Algorithm.SubprobPrimalSolsSet
— TypeColumns generated at the current iteration that forms the "current primal solution". This is used to compute the Lagragian dual bound.
It contains:
primal_sols
a dictionary that maps a formulation id to the best primal solution found by the pricing subproblem associated to this formulationimprove_master
a dictionary that maps a formulation id to a boolean indicating if the best primal solution found by the pricing subproblem associated to this formulation has negative reduced cost
This structure also helps to compute the subgradient of the Lagrangian function.
In the default implementation, push_in_set!
is responsible for checking if the column has improving reduced cost. Only columns with improving reduced cost are inserted in the set. The push_in_set!
is also responsible to insert he best primal solution to each pricing problem into the SubprobPrimalSolsSet
object.
References:
Coluna.ColGen.set_of_columns
— FunctionReturns an empty container that will store all the columns generated by the pricing problems during an iteration of the column generation algorithm. One must be able to iterate on this container to insert the columns in the master problem.
Coluna.ColGen.push_in_set!
— FunctionPushes the column in the set of columns generated at a given iteration of the column generation algorithm. Columns stored in the set will then be considered for insertion in the master problem. Returns true
if column was inserted in the set, false
otherwise.
Go back to the column generation iteration overview.
Dual bound calculation
In the default implementation, given a vector $\pi \geq 0$ of dual values to the master constraints (1), the Lagrangian dual function is given by:
\[L(\pi) = \pi a + \sum_{k \in K} \max_{l_k \leq \mathbf{1} \lambda^k \leq u^k} (c^k - \pi A^k)\lambda^k + \max_{ \bar{l} \leq y \leq \bar{u}} (\bar{c} - \pi \bar{A})y\]
Let:
- element $z_k(\pi) \leq \min_i (c^k_i - \pi A^k_i)$ be a lower bound on the solution value of the pricing problem
- element $\bar{z}_j(\pi) = \bar{c} - \pi \bar{A}$ be the reduced cost of pure master variable $y_j$
Then, the Lagrangian dual function can be lower bounded by:
\[L(\pi) \geq \pi a + \sum_{k \in K} \max\{ z_k(\pi) \cdot l_k, z_k(\pi) \cdot u_k \} + \sum_{j \in J} \max\{ \bar{z}_j(\pi) \cdot \bar{l}_j, \bar{z}_j(\pi) \cdot \bar{u}_j\}\]
More precisely:
- the first term is the contribution of the master obtained by removing the contribution of the convexity constraints (computed by
ColGen.Algorithm._convexity_contrib
), and the pure master variables (but you should see the third term) from the master LP solution value - the second term is the contribution of the subproblem variables which is the sum of the best solution value of each pricing subproblem multiplied by the lower and upper multiplicity of the subproblem depending on whether the reduced cost is negative or positive (this is computed by
ColGen.Algorithm._subprob_contrib
) - the third term is the contribution of the pure master variables which is taken into account by master LP value.
Therefore, we can compute the Lagrangian dual bound as follows:
master_lp_obj_val - convexity_contrib + sp_contrib
However, if the smoothing stabilization is active, we compute the dual bound at the sep-point. As a consequence, we can't use the master LP value because it corresponds to the dual solution at the out-point. We therefore need to compute the lagrangian dual bound by strictly applying the above formula.
References:
Coluna.ColGen.compute_sp_init_pb
— FunctionReturns an initial primal bound for a pricing subproblem. Default value should be +/- infinite depending on the optimization sense.
Coluna.ColGen.compute_sp_init_db
— FunctionReturns an initial dual bound for a pricing subproblem. Default value should be +/- infinite depending on the optimization sense.
Coluna.ColGen.compute_dual_bound
— Functioncompute_dual_bound(ctx, phase, master_lp_obj_val, master_dbs, generated_columns, mast_dual_sol) -> Float64
Caculates the dual bound at a given iteration of column generation. The dual bound is composed of:
master_lp_obj_val
: objective value of the master LP problemmaster_dbs
: dual values of the pricing subproblems- the contribution of the master convexity constraints that you should compute from
mast_dual_sol
.
Go back to the column generation iteration overview.
Columns insertion
The default implementation inserts into the master all the columns stored in the ColumnsSet
object.
Reference:
Coluna.ColGen.insert_columns!
— FunctionInserts columns into the master. Returns the number of columns inserted. Implementation is responsible for checking if the column must be inserted and warn the user if something unexpected happens.
Go back to the column generation iteration overview.
Iteration output
Coluna.Algorithm.ColGenIterationOutput
— TypeObject for the output of an iteration of the column generation default implementation.
References:
Coluna.ColGen.AbstractColGenIterationOutput
— TypeSupertype for the objects that contains the output of a column generation iteration.
Coluna.ColGen.colgen_iteration_output_type
— Functioncolgen_iteration_output_type(ctx) -> Type{<:AbstractColGenIterationOutput}
Returns the type of the column generation iteration output associated to the context.
Coluna.ColGen.new_iteration_output
— Functionnew_iteration_output(::Type{<:AbstractColGenIterationOutput}, args...) -> AbstractColGenIterationOutput
Arguments (i.e. arg...
) of this function are the following:
min_sense
:true
if the objective is a minimization function;false
otherwisemlp
: the optimal solution value of the master LPdb
: the Lagrangian dual boundnb_new_cols
: the number of columns inserted into the masternew_cut_in_master
:true
if valid inequalities or new constraints added into the master;false
otherwiseinfeasible_master
:true
if the master is proven infeasible;false
otherwiseunbounded_master
:true
if the master is unbounded;false
otherwiseinfeasible_subproblem
:true
if a pricing subproblem is proven infeasible;false
otherwiseunbounded_subproblem
:true
if a pricing subproblem is unbounded;false
otherwisetime_limit_reached
:true
if time limit is reached;false
otherwisemaster_primal_sol
: the primal master LP solutionip_primal_sol
: the incumbent primal master solutiondual_sol
: the dual master LP solution
Go back to the column generation iteration overview.
Getters for Result data structures
Method name | Master | Pricing |
---|---|---|
is_unbounded | X | X |
is_infeasible | X | X |
get_primal_sol | X | |
get_primal_sols | X | |
get_dual_sol | X | |
get_obj_val | X | |
get_primal_bound | X | |
get_dual_bound | X |
References:
Coluna.ColGen.is_unbounded
— FunctionReturns true if a master or pricing problem result is unbounded; false otherwise.
Coluna.ColGen.is_infeasible
— FunctionReturns true if a master or pricing problem result is infeasible; false otherwise.
Coluna.ColGen.get_primal_sol
— FunctionReturns primal solution to the master LP problem.
Coluna.ColGen.get_primal_sols
— FunctionArray of primal solutions to the pricing subproblem
Coluna.ColGen.get_dual_sol
— FunctionReturns dual solution to the master optimization problem.
Coluna.ColGen.get_obj_val
— FunctionReturns the optimal objective value of the master LP problem." See optimize_master_lp_problem!
.
Coluna.ColGen.get_primal_bound
— FunctionReturns primal bound of the pricing subproblem; nothing
if no primal bound is available and the initial dual bound returned by compute_sp_init_pb
will be used to compute the pseudo dual bound.
Go back to the column generation iteration overview.
Getters for Output data structures
Method name | ColGen | Phase | Iteration |
---|---|---|---|
get_nb_new_cols | X | ||
get_master_ip_primal_sol | X | X | X |
get_master_lp_primal_sol | X | ||
get_master_dual_sol | X | ||
get_dual_bound | X | X | |
get_master_lp_primal_bound | X | ||
is_infeasible | X |
References:
Coluna.ColGen.get_nb_new_cols
— FunctionReturns the number of new columns inserted into the master at the end of an iteration.
Coluna.ColGen.get_master_ip_primal_sol
— FunctionReturns the incumbent primal master IP solution at the end of an iteration or a phase.
Coluna.ColGen.get_master_lp_primal_sol
— FunctionReturns the primal master LP solution found at the last iteration of the column generation algorithm.
Coluna.ColGen.get_master_dual_sol
— FunctionReturns the dual master LP solution found at the last iteration of the column generation algorithm.
Coluna.ColGen.get_master_lp_primal_bound
— FunctionReturns the master LP solution value at the last iteration of the column generation algorithm.
Go back to the column generation iteration overview.
Stabilization
Coluna provides a default implementation of the smoothing stabilization with a self-adjusted $\alpha$ parameter, $0 \leq \alpha < 1$.
At each iteration of the column generation algorithm, instead of generating columns for the dual solution to the master LP, we generate columns for a perturbed dual solution defined as follows:
\[\pi^{\text{sep}} = \alpha \pi^{\text{in}} + (1-\alpha) \pi^{\text{out}}\]
where $\pi^{\text{in}}$ is the dual solution that gives the best Lagrangian dual bound so far (also called stabilization center) and $\pi^{\text{out}}$ is the dual solution to the master LP at the current iteration. This solution is returned by the default implementation of Coluna.ColGen.get_stab_dual_sol
.
Some elements of the column generation change when using stabilization.
- Columns are generated using the smoothed dual solution $\pi^{\text{sep}}$ but we still need to compute the reduced cost of the columns using the original dual solution $\pi^{\text{out}}$.
- The dual bound is computed using the smoothed dual solution $\pi^{\text{sep}}$.
- The pseudo bound is computed using the smoothed dual solution $\pi^{\text{sep}}$.
- The smoothed dual bound can result in the generation of no improving columns. This is called a misprice. In that case, we need to move away from the stabilization center $\pi^{\text{in}}$ by decreasing $\alpha$.
When using self-adjusted stabilization, the smoothing coefficient $\alpha$ is adjusted to make the smoothed dual solution $\pi^{\text{sep}}$ closer to the best possible dual solution on the line between $\pi^{\text{in}}$ and $\pi^{\text{out}}$ (i.e. where the subgradient of the current primal solution is perpendicular to the latter line). To compute the subgradient, we use the following data structure:
Coluna.Algorithm.SubgradientCalculationHelper
— TypePrecompute information to speed-up calculation of subgradient of master variables. We extract from the master follwowing information:
a
contains the perenial rhs of all master constraints except convexity constraints;A
is a submatrix of the master coefficient matrix that involves only representative of original variables (pure master vars + DW subproblem represtative vars)
Calculation is a - A * (m .* z)
where :
m
contains a multiplicity factor for each variable involved in the calculation (lower or upper sp multiplicity depending on variable reduced cost);z
is the concatenation of the solution to the master (for pure master vars) and pricing subproblems (for DW subproblem represtative vars).
Operation m .* z
"mimics" a solution in the original space.
References:
Coluna.ColGen.setup_stabilization!
— FunctionReturns an instance of a data structure that contain information about the stabilization used in the column generation algorithm.
Coluna.ColGen.update_stabilization_after_master_optim!
— Functionupdate_stabilization_after_master_optim!(stab, phase, mast_dual_sol) -> Bool
Update stabilization after master optimization where mast_dual_sol
is the dual solution to the master problem. Returns true
if the stabilization will change the dual solution used for the pricing in the current column generation iteration, false
otherwise.
Coluna.ColGen.get_stab_dual_sol
— FunctionReturns the dual solution used for the pricing in the current column generation iteration (stabilized dual solution).
Coluna.ColGen.check_misprice
— FunctionReturns true
if the stabilized dual solution leads to a misprice, false
otherwise.
Coluna.ColGen.update_stabilization_after_pricing_optim!
— FunctionUpdates stabilization after pricing optimization where:
mast_dual_sol
is the dual solution to the master problempseudo_db
is the pseudo dual bound of the problem after optimization of the pricing problemssmooth_dual_sol
is the current smoothed dual solution
Coluna.ColGen.update_stabilization_after_misprice!
— FunctionUpdates stabilization after a misprice. Argument mast_dual_sol
is the dual solution to the master problem.
Coluna.ColGen.update_stabilization_after_iter!
— FunctionUpdates stabilization after an iteration of the column generation algorithm. Arguments:
stab
is the stabilization data structurectx
is the column generation contextmaster
is the master problemgenerated_columns
is the set of generated columnsmast_dual_sol
is the dual solution to the master problem
Coluna.ColGen.get_output_str
— FunctionReturns a string with a short information about the stabilization.