From b2d7efd34422331bdfeb91c114b5b2dd0ab82bb9 Mon Sep 17 00:00:00 2001 From: Guillaume Marques Date: Wed, 28 Jun 2023 11:06:28 +0200 Subject: [PATCH 1/4] add Benders --- docs/src/api/benders.md | 180 +++++++++++++++++++++++++++++++++++----- docs/src/api/colgen.md | 4 +- src/Benders/Benders.jl | 5 +- 3 files changed, 166 insertions(+), 23 deletions(-) diff --git a/docs/src/api/benders.md b/docs/src/api/benders.md index 3b12b1c09..87b6cf4f9 100644 --- a/docs/src/api/benders.md +++ b/docs/src/api/benders.md @@ -2,16 +2,71 @@ CurrentModule = Coluna ``` -# Benders API +# Benders cut generation Coluna provides an interface and generic functions to implement a Benders cut generation algorithm. -Here is an overview of the main concepts and a description of the default implementation. -## Problem information +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. +The default implementation is based on the paper of -The following methods provide information about the reformulation that the Benders cut -generation algorithm will solve: +You can find the generic functions and the interface in the `Benders` submodule and the +default implementation in the `Algorithm` submodule at `src/Algorithm/benders`. + +## Context + +The `Benders` submodule provides an interface and generic functions to implement a benders cut generation algorithm. The implementation depends on an object called `context`. + +```@docs +Coluna.Benders.AbstractBendersContext +``` + +Benders provides two types of context: + +```@docs +Coluna.Algorithm.BendersContext +Coluna.Algorithm.BendersPrinterContext +``` + +## Generic functions + +Generic functions are the core of the Benders cut generation algorithm. +There are three generic functions: + +```@docs +Coluna.Benders.run_benders_loop! +``` +See ... + +```@docs +Coluna.Benders.run_benders_iteration! +``` +See ... + +These functions are independant of any other submodule of Coluna. +You can use them to implement your own Benders cut 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: + +```math +\begin{aligned} +\min \quad& obj & \\ + +\end{aligned} +``` + +The subproblems have the following form: + +```math + +``` + +**References**: ```@docs Coluna.Benders.is_minimization @@ -20,32 +75,60 @@ Coluna.Benders.get_master Coluna.Benders.get_benders_subprobs ``` -## Benders cut generation algorithm +## Main loop -The following generic function contains all the logic of the algorithm: +This is a description of how the `Coluna.Benders.run_benders_loop!` generic function behaves with the default implementation. -```@docs -Coluna.Benders.run_benders_loop! -``` +The loop stops if one of the following conditions is met: +- the master is infeasible +- a separation subproblem is infeasible +- the time limit is reached +- the maximum number of iterations is reached +- no new cut generated at the last iteration -The latter method calls the following methods: +**References**: ```@docs Coluna.Benders.setup_reformulation! Coluna.Benders.stop_benders Coluna.Benders.after_benders_iteration +Coluna.Benders.AbstractBendersOutput Coluna.Benders.benders_output_type Coluna.Benders.new_output ``` -and the generic function: - -```@docs -Coluna.Benders.run_benders_iteration! +## Benders cut generation iteration + + +```mermaid +flowchart TB; + id1(Optimize master) + id2(Treat unbounded master) + id3(Setup separation subproblems) + id4(Separation subproblem iterator) + id5(Optimize separation subproblem) + id6(Push cut into set) + id7(Insert cut) + id8(Iteration output) + id1 --unbounded--> id2 + id2 --certificate--> id3 + id1 -- optimal --> id3 + id3 --> id4 + id4 -- subproblem --> id5 + id5 --> id6 + id6 --> id4 + id4 -- end --> id7 + id7 --> id8 + click id1 href "#Master-optimization" "Link to doc" + click id2 href "#Unbounded-master-case" "Link to doc" + click id3 href "#Setup-separation-subproblems" "Link to doc" + click id4 href "#Subproblem-iterator" "Link to doc" + click id5 href "#Separation-subproblem-optimization" "Link to doc" + click id6 href "#Set-of-generated-cuts" "Link to doc" + click id7 href "#Cuts-insertion" "Link to doc" + click id8 href "#Iteration-output" "Link to doc" ``` -## Benders cut generation algorithm iteration - The `run_benders_iteration!` generic function calls the following method: ```@docs @@ -59,7 +142,7 @@ Coluna.Benders.build_primal_solution Coluna.Benders.new_iteration_output ``` -## Optimization of the Master +### Master optimization The Benders cut generation algorithm is an iterative algorithm that consists in fixing a part of the variable @@ -78,20 +161,73 @@ If the master is unbounded... Coluna.Benders.treat_unbounded_master_problem_case! ``` -## Separation Problem Optimization +### Unbounded master case + +Lorem ipsum: + +**References**: + +```@docs + +``` + +### Setup separation subproblems +Lorem ipsum. +**References**: ```@docs Coluna.Benders.setup_separation_for_unbounded_master_case! Coluna.Benders.update_sp_rhs! ``` +### Subproblem iterator + +Not implemented yet. + +### Separation subproblem optimization + +Lorem ipsum + +**References**: + ```@docs Coluna.Benders.optimize_separation_problem! Coluna.Benders.treat_infeasible_separation_problem_case! ``` -## Optimization Results +### Set of generated cuts + +Lorem ipsum: + +**References**: + +```@docs + +``` + +### Cuts insertion + +Lorem ipsum: + +**References**: + +```@docs + +``` + +### Iteration output + +Lorem ipsum: + +**References**: + +```@docs + +``` + + +### Getters for Result data structures | Method name | Master | Separation | | ---------------- | ------ | ---------- | @@ -110,3 +246,7 @@ Coluna.Benders.get_primal_sol Coluna.Benders.get_dual_sol Coluna.Benders.get_obj_val ``` + +## Stabilization + +Not implemented yet. \ No newline at end of file diff --git a/docs/src/api/colgen.md b/docs/src/api/colgen.md index a3dad33d0..5b01400c0 100644 --- a/docs/src/api/colgen.md +++ b/docs/src/api/colgen.md @@ -106,7 +106,7 @@ Coluna.ColGen.is_minimization 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 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 @@ -578,7 +578,7 @@ Coluna.ColGen.get_master_dual_sol Coluna.ColGen.get_master_lp_primal_bound ``` -### Stabilization +## Stabilization Coluna provides a default implementation of the smoothing stabilization with a self-adjusted $\alpha$ parameter, $0 \leq \alpha < 1$. diff --git a/src/Benders/Benders.jl b/src/Benders/Benders.jl index 9147a8daa..5eb184b6a 100644 --- a/src/Benders/Benders.jl +++ b/src/Benders/Benders.jl @@ -3,6 +3,10 @@ module Benders include("../MustImplement/MustImplement.jl") using .MustImplement +""" +Supertype for the objects to which belongs the implemntation of the Benders cut generation and +that stores any kind of information during the execution of the Bender cut generation algorithm. +""" abstract type AbstractBendersContext end struct UnboundedError <: Exception end @@ -20,7 +24,6 @@ struct UnboundedError <: Exception end @mustimplement "BendersProbInfo" get_benders_subprobs(context) = nothing - """ optimize_master_problem!(master, context, env) -> MasterResult From e59230f3b059057aeb9308dbb746f151d726264a Mon Sep 17 00:00:00 2001 From: Guillaume Marques Date: Wed, 28 Jun 2023 12:09:22 +0200 Subject: [PATCH 2/4] wip --- docs/src/api/benders.md | 36 ++++++- src/Benders/Benders.jl | 187 +----------------------------------- src/Benders/interface.jl | 201 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 235 insertions(+), 189 deletions(-) create mode 100644 src/Benders/interface.jl diff --git a/docs/src/api/benders.md b/docs/src/api/benders.md index 87b6cf4f9..077983534 100644 --- a/docs/src/api/benders.md +++ b/docs/src/api/benders.md @@ -55,17 +55,41 @@ The master has the following form: ```math \begin{aligned} -\min \quad& obj & \\ - +\min \quad& cx + \sum_{k \in K} \eta_k & &\\ +\text{s.t.} \quad& Ax \geq a & & (1) \\ + & \text{< benders cuts>} & & (2) \\ + & l_1 \leq x \leq u_1 & & (3) \\ + & \eta_k \in \mathbb{R} & \forall k \in K \quad& (4) \end{aligned} ``` +where $x$ are first-stage variables, +$\eta_k$ is the second-stage cost variable for the subproblem $k$, +constraints $(1)$ are the first-stage constraints, +constraints $(2)$ are the Benders cuts, +constraints $(3)$ are the bounds on the first-stage variables, +and expression $(4)$ shows that second-stage variables are free. + The subproblems have the following form: ```math - +$$ +\begin{aligned} +\min \quad& fy + \mathbf{1}z' + \mathbf{1}z'' &&& \\ +\text{s.t.} \quad& Dy + z' \geq d - B\bar{x} && (5) \quad& {\color{blue}(\pi)} \\ + & Ey + z'' \geq e && (6) \quad& {\color{blue}(\rho)} \\ + & l_2 \leq y \leq u_2 && (7) \quad& {\color{blue}(\sigma)} +\end{aligned} +$$ ``` +where $y$ are second-stage variables, $z'$ and $z''$ are artificial variables, +constraints (5) are the reformulation of linking constraints using the first-stage solution $\bar{x}$, +constraints (6) are the second-stage constraints, +and constraints (7) are the bounds on the second-stage variables. +In blue, we define the dual variables associated to these constraints. + + **References**: ```@docs @@ -86,6 +110,12 @@ The loop stops if one of the following conditions is met: - the maximum number of iterations is reached - no new cut generated at the last iteration +The default implementation returns: + +```@docs + +``` + **References**: ```@docs diff --git a/src/Benders/Benders.jl b/src/Benders/Benders.jl index 5eb184b6a..361ae944f 100644 --- a/src/Benders/Benders.jl +++ b/src/Benders/Benders.jl @@ -11,192 +11,7 @@ abstract type AbstractBendersContext end struct UnboundedError <: Exception end -"Returns `true` if the objective sense is minimization, `false` otherwise." -@mustimplement "BendersProbInfo" is_minimization(context::AbstractBendersContext) = nothing - -"Returns Benders reformulation." -@mustimplement "BendersProbInfo" get_reform(context::AbstractBendersContext) = nothing - -"Returns the master problem." -@mustimplement "BendersProbInfo" get_master(context::AbstractBendersContext) = nothing - -"Returns the separation subproblems." -@mustimplement "BendersProbInfo" get_benders_subprobs(context) = nothing - - -""" - optimize_master_problem!(master, context, env) -> MasterResult - -Returns an instance of a custom object `MasterResult` that implements the following methods: -- `is_unbounded(res::MasterResult) -> Bool` -- `is_infeasible(res::MasterResult) -> Bool` -- `is_certificate(res::MasterResult) -> Bool` -- `get_primal_sol(res::MasterResult) -> Union{Nothing, PrimalSolution}` -""" -@mustimplement "Benders" optimize_master_problem!(master, context, env) = nothing - -""" - treat_unbounded_master_problem_case!(master, context, env) -> MasterResult - -When after a call to `optimize_master_problem!`, the master is unbounded, this method is called. -Returns an instance of a custom object `MasterResult`. -""" -@mustimplement "Benders" treat_unbounded_master_problem_case!(master, context, env) = nothing - -# Master solution -""" - is_unbounded(master_res) -> Bool - is_unbounded(sep_res) -> Bool - -Returns `true` if the problem is unbounded, `false` otherwise. -""" -@mustimplement "Benders" is_unbounded(res) = nothing - -""" - is_infeasible(master_res) -> Bool - is_infeasible(sep_res) -> Bool - -Returns `true` if the master is infeasible, `false` otherwise. -""" -@mustimplement "Benders" is_infeasible(res) = nothing - -"Returns the certificate of dual infeasibility if the master is unbounded, `nothing` otherwise." -@mustimplement "Benders" is_certificate(res) = nothing - -"Returns the primal solution of the master problem if it exists, `nothing` otherwise." -@mustimplement "Benders" get_primal_sol(res) = nothing - -# second stage variable costs -""" - update_sp_rhs!(context, sp, mast_primal_sol) - -Updates the right-hand side of the separation problem `sp` by fixing the first-level solution -obtained by solving the master problem `mast_primal_sol`. -""" -@mustimplement "Benders" update_sp_rhs!(context, sp, mast_primal_sol) = nothing - -""" - setup_separation_for_unbounded_master_case!(context, sp, mast_primal_sol) - -Updates the separation problem to derive a cut when the master problem is unbounded. -""" -@mustimplement "Benders" setup_separation_for_unbounded_master_case!(context, sp, mast_primal_sol) = nothing - -""" -Returns an empty container that will store all the cuts generated by the separation problems -during an iteration of the Benders cut generation algorithm. -One must be able to iterate on this container to insert the cuts in the master problem. -""" -@mustimplement "Benders" set_of_cuts(context) = nothing - -""" -Returns an empty container that will store the primal solutions to the separation problems -at a given iteration of the Benders cut generation algorithm. -""" -@mustimplement "Benders" set_of_sep_sols(context) = nothing - -""" - optimize_separation_problem!(context, sp_to_solve, env, unbounded_master) -> SeparationResult - -Returns an instance of a custom object `SeparationResult` that implements the following methods: -- `is_unbounded(res::SeparationResult) -> Bool` -- `is_infeasible(res::SeparationResult) -> Bool` -- `get_obj_val(res::SeparationResult) -> Float64` -- `get_primal_sol(res::SeparationResult) -> Union{Nothing, PrimalSolution}` -- `get_dual_sp_sol(res::SeparationResult) -> Union{Nothing, DualSolution}` -""" -@mustimplement "Benders" optimize_separation_problem!(context, sp_to_solve, env, unbounded_master) = nothing - -""" - treat_infeasible_separation_problem_case!(context, sp_to_solve, env, unbounded_master) -> SeparationResult - -When after a call to `optimize_separation_problem!`, the separation problem is infeasible, this method is called. -Returns an instance of a custom object `SeparationResult`. -""" -@mustimplement "Benders" treat_infeasible_separation_problem_case!(context, sp_to_solve, env, unbounded_master_case) = nothing - -""" - get_dual_sol(master_result) -> Union{Nothing, DualSolution} - get_dual_sol(sep_result) -> Union{Nothing, DualSolution} - -Returns the dual solution of the separation problem if it exists; `nothing` otherwise. -""" -@mustimplement "Benders" get_dual_sol(res) = nothing - -""" - push_in_set!(context, cut_pool, sep_result) -> Bool - -Inserts a cut in the set of cuts generated at a given iteration of the Benders cut generation algorithm. -The `cut_pool` structure is generated by `set_of_cuts(context)`. - - push_in_set!(context, sep_sp_sols, sep_result) -> Bool - -Inserts a primal solution to a separation problem in the set of primal solutions generated at a given iteration of the Benders cut generation algorithm. -The `sep_sp_sols` structure is generated by `set_of_sep_sols(context)`. - -Returns `true` if the cut or the primal solution was inserted in the set, `false` otherwise. -""" -@mustimplement "Benders" push_in_set!(context, pool, elem) = nothing - -""" - insert_cuts!(reform, context, generated_cuts) - -Inserts the cuts into the master. -""" -@mustimplement "Benders" insert_cuts!(reform, context, generated_cuts) = nothing - -"Supertype for the custom objects that will store the output of a Benders iteration." -abstract type AbstractBendersIterationOutput end - -""" - benders_iteration_output_type(context) -> Type{<:AbstractBendersIterationOutput} - -Returns the type of the custom object that will store the output of a Benders iteration. -""" -@mustimplement "Benders" benders_iteration_output_type(::AbstractBendersContext) = nothing - -"Returns a new instance of the custom object that stores the output of a Benders iteration." -@mustimplement "Benders" new_iteration_output(::Type{<:AbstractBendersIterationOutput}, is_min_sense, nb_cuts_inserted, ip_primal_sol, infeasible, time_limit_reached, master_obj_val) = nothing - -"Placeholder method called after each iteration of the Benders cut generation algorithm." -@mustimplement "Benders" after_benders_iteration(::AbstractBendersContext, phase, env, iteration, benders_iter_output) = nothing - -"Returns `true` if the Benders cut generation algorithm must stop, `false` otherwise." -@mustimplement "Benders" stop_benders(::AbstractBendersContext, benders_iter_output, iteration) = nothing - -"Supertype for the custom objects that will store the output of the Benders cut generation algorithm." -abstract type AbstractBendersOutput end - -""" - benders_output_type(context) -> Type{<:AbstractBendersOutput} - -Returns the type of the custom object that will store the output of the Benders cut generation -algorithm. -""" -@mustimplement "Benders" benders_output_type(::AbstractBendersContext) = nothing - -"Returns a new instance of the custom object that stores the output of the Benders cut generation algorithm." -@mustimplement "Benders" new_output(::Type{<:AbstractBendersOutput}, benders_iter_output) = nothing - -""" - get_obj_val(master_res) -> Float64 - get_obj_val(sep_res) -> Float64 - -Returns the objective value of the master or separation problem. -""" -@mustimplement "BendersMasterResult" get_obj_val(master_res) = nothing - -"Prepares the reformulation before starting the Benders cut generation algorithm." -@mustimplement "Benders" setup_reformulation!(reform, env) = nothing - -""" -Builds a primal solution to the original problem from the primal solution to the master -problem and the primal solutions to the separation problems. -""" -@mustimplement "Benders" build_primal_solution(context, mast_primal_sol, sep_sp_sols) = nothing - -"Returns `true` if the master has been proven unbounded, `false` otherwise." -@mustimplement "Benders" master_is_unbounded(context, second_stage_cost, unbounded_master_case) = nothing +include("interface.jl") "Main loop of the Benders cut generation algorithm." function run_benders_loop!(context, env; iter = 1) diff --git a/src/Benders/interface.jl b/src/Benders/interface.jl new file mode 100644 index 000000000..4b22bba17 --- /dev/null +++ b/src/Benders/interface.jl @@ -0,0 +1,201 @@ +############################################################################################ +# Reformulation getters +############################################################################################ +"Returns `true` if the objective sense is minimization, `false` otherwise." +@mustimplement "BendersProbInfo" is_minimization(context::AbstractBendersContext) = nothing + +"Returns Benders reformulation." +@mustimplement "BendersProbInfo" get_reform(context::AbstractBendersContext) = nothing + +"Returns the master problem." +@mustimplement "BendersProbInfo" get_master(context::AbstractBendersContext) = nothing + +"Returns the separation subproblems." +@mustimplement "BendersProbInfo" get_benders_subprobs(context) = nothing + +############################################################################################ +# Main loop +############################################################################################ +"Prepares the reformulation before starting the Benders cut generation algorithm." +@mustimplement "Benders" setup_reformulation!(reform, env) = nothing + +"Returns `true` if the Benders cut generation algorithm must stop, `false` otherwise." +@mustimplement "Benders" stop_benders(::AbstractBendersContext, benders_iter_output, iteration) = nothing + +"Placeholder method called after each iteration of the Benders cut generation algorithm." +@mustimplement "Benders" after_benders_iteration(::AbstractBendersContext, phase, env, iteration, benders_iter_output) = nothing + +############################################################################################ +# Benders output +############################################################################################ +"Supertype for the custom objects that will store the output of the Benders cut generation algorithm." +abstract type AbstractBendersOutput end + +""" + benders_output_type(context) -> Type{<:AbstractBendersOutput} + +Returns the type of the custom object that will store the output of the Benders cut generation +algorithm. +""" +@mustimplement "Benders" benders_output_type(::AbstractBendersContext) = nothing + +"Returns a new instance of the custom object that stores the output of the Benders cut generation algorithm." +@mustimplement "Benders" new_output(::Type{<:AbstractBendersOutput}, benders_iter_output) = nothing + +############################################################################################ +# Master optimization +############################################################################################ +""" + optimize_master_problem!(master, context, env) -> MasterResult + +Returns an instance of a custom object `MasterResult` that implements the following methods: +- `is_unbounded(res::MasterResult) -> Bool` +- `is_infeasible(res::MasterResult) -> Bool` +- `is_certificate(res::MasterResult) -> Bool` +- `get_primal_sol(res::MasterResult) -> Union{Nothing, PrimalSolution}` +""" +@mustimplement "Benders" optimize_master_problem!(master, context, env) = nothing + +############################################################################################ +# Unbounded master case +############################################################################################ +""" + treat_unbounded_master_problem_case!(master, context, env) -> MasterResult + +When after a call to `optimize_master_problem!`, the master is unbounded, this method is called. +Returns an instance of a custom object `MasterResult`. +""" +@mustimplement "Benders" treat_unbounded_master_problem_case!(master, context, env) = nothing + +############################################################################################ +# Update separation subproblems +############################################################################################ +""" + update_sp_rhs!(context, sp, mast_primal_sol) + +Updates the right-hand side of the separation problem `sp` by fixing the first-level solution +obtained by solving the master problem `mast_primal_sol`. +""" +@mustimplement "Benders" update_sp_rhs!(context, sp, mast_primal_sol) = nothing + +""" + setup_separation_for_unbounded_master_case!(context, sp, mast_primal_sol) + +Updates the separation problem to derive a cut when the master problem is unbounded. +""" +@mustimplement "Benders" setup_separation_for_unbounded_master_case!(context, sp, mast_primal_sol) = nothing + +############################################################################################ +# Separation problem optimization +############################################################################################ +""" + optimize_separation_problem!(context, sp_to_solve, env, unbounded_master) -> SeparationResult + +Returns an instance of a custom object `SeparationResult` that implements the following methods: +- `is_unbounded(res::SeparationResult) -> Bool` +- `is_infeasible(res::SeparationResult) -> Bool` +- `get_obj_val(res::SeparationResult) -> Float64` +- `get_primal_sol(res::SeparationResult) -> Union{Nothing, PrimalSolution}` +- `get_dual_sp_sol(res::SeparationResult) -> Union{Nothing, DualSolution}` +""" +@mustimplement "Benders" optimize_separation_problem!(context, sp_to_solve, env, unbounded_master) = nothing + +""" + treat_infeasible_separation_problem_case!(context, sp_to_solve, env, unbounded_master) -> SeparationResult + +When after a call to `optimize_separation_problem!`, the separation problem is infeasible, this method is called. +Returns an instance of a custom object `SeparationResult`. +""" +@mustimplement "Benders" treat_infeasible_separation_problem_case!(context, sp_to_solve, env, unbounded_master_case) = nothing + +############################################################################################ +# Cuts and primal solutions +############################################################################################ +""" +Returns an empty container that will store all the cuts generated by the separation problems +during an iteration of the Benders cut generation algorithm. +One must be able to iterate on this container to insert the cuts in the master problem. +""" +@mustimplement "Benders" set_of_cuts(context) = nothing + +""" +Returns an empty container that will store the primal solutions to the separation problems +at a given iteration of the Benders cut generation algorithm. +""" +@mustimplement "Benders" set_of_sep_sols(context) = nothing + + +""" + push_in_set!(context, cut_pool, sep_result) -> Bool + +Inserts a cut in the set of cuts generated at a given iteration of the Benders cut generation algorithm. +The `cut_pool` structure is generated by `set_of_cuts(context)`. + + push_in_set!(context, sep_sp_sols, sep_result) -> Bool + +Inserts a primal solution to a separation problem in the set of primal solutions generated at a given iteration of the Benders cut generation algorithm. +The `sep_sp_sols` structure is generated by `set_of_sep_sols(context)`. + +Returns `true` if the cut or the primal solution was inserted in the set, `false` otherwise. +""" +@mustimplement "Benders" push_in_set!(context, pool, elem) = nothing + +############################################################################################ +# Cuts insertion +############################################################################################ +"Inserts the cuts into the master." +@mustimplement "Benders" insert_cuts!(reform, context, generated_cuts) = nothing + +############################################################################################ +# Benders iteration output +############################################################################################ + +"Supertype for the custom objects that will store the output of a Benders iteration." +abstract type AbstractBendersIterationOutput end + +""" + benders_iteration_output_type(context) -> Type{<:AbstractBendersIterationOutput} + +Returns the type of the custom object that will store the output of a Benders iteration. +""" +@mustimplement "Benders" benders_iteration_output_type(::AbstractBendersContext) = nothing + +"Returns a new instance of the custom object that stores the output of a Benders iteration." +@mustimplement "Benders" new_iteration_output(::Type{<:AbstractBendersIterationOutput}, is_min_sense, nb_cuts_inserted, ip_primal_sol, infeasible, time_limit_reached, master_obj_val) = nothing + +############################################################################################ +# Optimization result getters +############################################################################################ +"Returns `true` if the problem is unbounded, `false` otherwise." +@mustimplement "Benders" is_unbounded(res) = nothing + +"Returns `true` if the master is infeasible, `false` otherwise." +@mustimplement "Benders" is_infeasible(res) = nothing + +"Returns the certificate of dual infeasibility if the master is unbounded, `nothing` otherwise." +@mustimplement "Benders" is_certificate(res) = nothing + +"Returns the primal solution of the master problem if it exists, `nothing` otherwise." +@mustimplement "Benders" get_primal_sol(res) = nothing + +"Returns the dual solution of the separation problem if it exists; `nothing` otherwise." +@mustimplement "Benders" get_dual_sol(res) = nothing + +"Returns the objective value of the master or separation problem." +@mustimplement "BendersMasterResult" get_obj_val(master_res) = nothing + +############################################################################################ +# Build primal solution +############################################################################################ +""" +Builds a primal solution to the original problem from the primal solution to the master +problem and the primal solutions to the separation problems. +""" +@mustimplement "Benders" build_primal_solution(context, mast_primal_sol, sep_sp_sols) = nothing + +############################################################################################ +# Master unboundedness +############################################################################################ + +"Returns `true` if the master has been proven unbounded, `false` otherwise." +@mustimplement "Benders" master_is_unbounded(context, second_stage_cost, unbounded_master_case) = nothing From e6df0e47fe925e20b10a338bf5e04da939cf2340 Mon Sep 17 00:00:00 2001 From: Guillaume Marques Date: Wed, 28 Jun 2023 12:11:34 +0200 Subject: [PATCH 3/4] ez --- docs/src/api/benders.md | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/docs/src/api/benders.md b/docs/src/api/benders.md index 077983534..5c5b32ac1 100644 --- a/docs/src/api/benders.md +++ b/docs/src/api/benders.md @@ -185,20 +185,14 @@ It returns a primal solution. Coluna.Benders.optimize_master_problem! ``` -If the master is unbounded... - -```@docs -Coluna.Benders.treat_unbounded_master_problem_case! -``` - ### Unbounded master case -Lorem ipsum: +If the master is unbounded... **References**: ```@docs - +Coluna.Benders.treat_unbounded_master_problem_case! ``` ### Setup separation subproblems @@ -233,7 +227,9 @@ Lorem ipsum: **References**: ```@docs - +Coluna.Benders.set_of_cuts +Coluna.Benders.set_of_sep_sols +Coluna.Benders.push_in_set! ``` ### Cuts insertion @@ -243,7 +239,7 @@ Lorem ipsum: **References**: ```@docs - +Coluna.Benders.insert_cuts! ``` ### Iteration output @@ -253,7 +249,9 @@ Lorem ipsum: **References**: ```@docs - +Coluna.Benders.AbstractBendersIterationOutput +Coluna.Benders.benders_iteration_output_type +Coluna.Benders.new_iteration_output ``` From d7b5a9fd3f01040c1b0425e3caf32d892dd2d930 Mon Sep 17 00:00:00 2001 From: Guillaume Marques Date: Wed, 28 Jun 2023 12:17:07 +0200 Subject: [PATCH 4/4] ok --- docs/src/api/benders.md | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/docs/src/api/benders.md b/docs/src/api/benders.md index 5c5b32ac1..442476d12 100644 --- a/docs/src/api/benders.md +++ b/docs/src/api/benders.md @@ -73,14 +73,12 @@ and expression $(4)$ shows that second-stage variables are free. The subproblems have the following form: ```math -$$ \begin{aligned} \min \quad& fy + \mathbf{1}z' + \mathbf{1}z'' &&& \\ \text{s.t.} \quad& Dy + z' \geq d - B\bar{x} && (5) \quad& {\color{blue}(\pi)} \\ & Ey + z'' \geq e && (6) \quad& {\color{blue}(\rho)} \\ & l_2 \leq y \leq u_2 && (7) \quad& {\color{blue}(\sigma)} \end{aligned} -$$ ``` where $y$ are second-stage variables, $z'$ and $z''$ are artificial variables, @@ -159,19 +157,6 @@ flowchart TB; click id8 href "#Iteration-output" "Link to doc" ``` -The `run_benders_iteration!` generic function calls the following method: - -```@docs -Coluna.Benders.benders_iteration_output_type -Coluna.Benders.set_of_cuts -Coluna.Benders.set_of_sep_sols -Coluna.Benders.push_in_set! -Coluna.Benders.master_is_unbounded -Coluna.Benders.insert_cuts! -Coluna.Benders.build_primal_solution -Coluna.Benders.new_iteration_output -``` - ### Master optimization The Benders cut generation algorithm is an iterative algorithm that consists in fixing a part of the variable @@ -254,7 +239,6 @@ Coluna.Benders.benders_iteration_output_type Coluna.Benders.new_iteration_output ``` - ### Getters for Result data structures | Method name | Master | Separation |