Navigator
Benders decomposition
Consider the following general LP, where x x x is a real valued variable and y y y is a variable whose domain is defined by polyhedron Y \mathbb{Y} Y. y y y is a complicating variable:
min c T x + f T y s . t . A x + B y ≥ b x ≥ 0 , y ∈ Y \begin{aligned} &\min & c^Tx+f^Ty\\ &s.t. & Ax+By\geq b\\ &&x\geq 0, y\in \mathbb{Y} \end{aligned} mins.t.cTx+fTyAx+By≥bx≥0,y∈Y
The Benders decomposition method partitions the problem in two: a master problem containing the y y y variables and a sub-problem containing the x x x variables. With q ( y ) q(y) q(y) as the incumbent value for the x x x part, thus, we can define a LP using only variable y y y:
min f T y + q ( y ) s . t . y ∈ Y \begin{aligned} &\min &f^Ty+q(y)\\ &s.t.&y\in\mathbb{Y} \end{aligned} mins.t.fTy+q(y)y∈Y
Then, we have the sub-problem in terms of x x x. Note that if the sub-problem is unbounded, the original problem is unbounded as well. Under the assumption of bounded, we calculate the value of q ( y ) q(y) q(y) by solving the following LP:
min c T x s . t . A x ≥ b − B y x ≥ 0 (Sub) \begin{aligned} &\min &c^Tx\\ &s.t. & Ax\geq b-By\\ &&x\geq 0 \end{aligned}\tag{Sub} mins.t.cTxAx≥b−Byx≥0(Sub)
Considering the dual variable, α \alpha α associated with the sub-problem:
max α T ( b − B y ) s . t . A T α ≤ c α ≥ 0 (Dual) \begin{aligned} &\max &\alpha^T(b-By)\\ &s.t.& A^T\alpha\leq c\\ &&\alpha\geq 0 \end{aligned}\tag{Dual} maxs.t.αT(b−By)ATα≤cα≥0(Dual)
When the solution space is not empty, we can enumerate all extreme rays ρ i , i ∈ [ 1 , I ] \rho_i, i\in[1, I] ρi,i∈[1,I] and extreme points π j , j ∈ [ 1 , J ] \pi_j, j\in[1, J] πj,j∈[1,J] of the feasible region. Given a solution vector y ∗ y^* y∗, we can solve the dual problem by checking if we can find:
- ρ i \rho_i ρi such that: ρ i T ( b − B y ∗ ) > 0 \rho_i^T(b-By^*)>0 ρiT(b−By∗)>0, in which case the dual is unbounded;
- π i \pi_i πi maximizing: π j T ( b − B y ∗ ) \pi_j^T(b-By^*) πjT(b−By∗), in which case both the primal and dual have infinite solutions.
Rewriting the sub-problem in terms of a single variable q q q. We have the master problem in terms of y y y and q q q only, and the constraints are called cuts in Benders:
min f T y + q s . t . ρ i T ( b − B y ) ≤ 0 ∀ i ∈ [ 1 , I ] ( 1 ) π j T ( b − B y ) ≤ q ∀ j ∈ [ 1 , J ] ( 2 ) q ∈ R , y ∈ Y \begin{aligned} &\min & f^Ty+q\\ &s.t.& \rho_i^T(b-By)\leq 0 & \forall i\in [1, I] & (1)\\ &&\pi_j^T(b-By)\leq q & \forall j \in [1, J] & (2)\\ &&q\in\mathbb{R}, y\in \mathbb{Y} \end{aligned} mins.t.fTy+qρiT(b−By)≤0πjT(b−By)≤qq∈R,y∈Y∀i∈[1,I]∀j∈[1,J](1)(2)
Since there is an exponential number of extreme points and extreme rays, and because their enumeration is N P NP NP-hard, Benders starts without any cut and solves a relaxed master problem which gives a candidate solution ( y ∗ , q ∗ ) (y^*, q^*) (y∗,q∗). Using this solution, it solves the sub-problem to obtain a value q ( y ∗ ) q(y^*) q(y∗). If q ∗ = q ( y ∗ ) q^*=q(y^*) q∗=q(y∗) then the candidate solution is optimal for the original problem. Otherwise we have two cases:
- the dual is unbounded, then we select an extreme ray to generate a constraint of type ( 4 ) (4) (4), which is called a feasibility cut.
- we have q ( y ∗ ) > q ∗ q(y^*)>q^* q(y∗)>q∗, then we select and exteren point to add a constraint of type ( 5 ) (5) (5), which is called an optimality cut as it gives a lower bound for the incumbent.
The algorithm then repeats the previous steps: it solves the master problem, now with an additional constraint, and checks wheter the solution is optimal or if a new cut is needed.
Xpress demo
Problem:
min c 1 ∗ x + c 2 ∗ y s . t . A 1 ∗ x + A 2 ∗ y ≤ b \begin{aligned} &\min & c_1*x+c_2*y\\ &s.t.&A_1*x+A_2*y\leq b \end{aligned} mins.t.c1∗x+c2∗yA1∗x+A2∗y≤b
x x x binary, n 1 n_1 n1-dimensional vector
y ≥ 0 y\geq 0 y≥0 continuous, n 2 n_2 n2 dimensional vector
import xpress as xp
def demo_1():
x1 = xp.var(vartype=xp.integer, name='x1', lb=-10, ub=10)
x2 = xp.var(name='x2')
p = xp.problem(x1, x2, # variables of the problem
x1**2+2*x2, # objective function
x1+3*x2>=4, # constraints
name='myexample')
p.solve()
print('solution: {0}={1}; {2}={3}'.format(x1.name, p.getSolution(x1), x2.name, p.getSolution(x2)))
import sys
def demo_2():
c1 = [1, 6, 5, 7] # n1 * 1
c2 = [9, 3, 0, 2, 3] # n2 * 1
b = [-3, -4, 1, 4, 5] # m * 1
A1 = [[0, -2, 3, 2],
[-5, 0, -3, 1],
[1, 0, 4, -2],
[0, -3, 4, -1],
[-5, -4, 3, 0]]
A2 = [[3, 4, 2, 0, -5],
[0, 2, 3, -2, 1],
[2, 0, 1, -3, -5],
[-5, 3, -2, -3, 0],
[-2, 3, -1, 2, -4]]
m, n1, n2 = len(b), len(c1), len(c2)
## Define variables
p = xp.problem()
x = [xp.var(vartype=xp.binary) for _ in range(n1)]
y = [xp.var() for _ in range(n2)] # positive real variable
p.addVariable(x, y)
## Define constraints
cons = [xp.Sum(A1[i][j]*x[j] for j in range(n1)) + xp.Sum(A2[i][j]*y[j] for j in range(n2))<=b[i] for i in range(m)]
p.addConstraint(cons)
## Define objective
p.setObjective(xp.Sum(c1[j]*x[j] for j in range(n1))+xp.Sum(c2[j]*y[j] for j in range(n2)),sense=xp.minimize)
# solve & retrieve solution
p.solve()
if p.getProbStatus() != xp.mip_optimal:
raise RuntimeError('Problem could not be solved to MIP optimality')
y_opt = p.getSolution(y)
print('The optimal value is: ', p.getObjVal())
print('The first stage variables are: ', p.getSolution(x))
print('The second stage variables are: ', y_opt)
print('The objective of the second stage is:', sum(c2[j]*y_opt[j] for j in range(n2)))
def main():
# demo_1()
demo_2()
if __name__ == '__main__':
main()
Reference
Benders blog
Gurobi列生成和Benders分解
Sparse portfolio selection with uncertain probability distribution