├── Benders decomposition.py ├── CCGExample.py └── README.md /Benders decomposition.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun Apr 2 17:21:33 2023 4 | 5 | @author: wyx 6 | """ 7 | from gurobipy import * 8 | import numpy as np 9 | 10 | # Parameters 11 | Ay = np.array([[800., 0., 0., -1., 0., 0.], 12 | [ 0., 800., 0., 0., -1., 0.], 13 | [ 0., 0., 800., 0., 0., -1.], 14 | [ 0., 0., 0., 1., 1., 1.]]) 15 | by = np.array([ 0., 0., 0., 772.]) 16 | G = np.array([[-1., -1., -1., 0., 0., 0., 0., 0., 0.], 17 | [ 0., 0., 0., -1., -1., -1., 0., 0., 0.], 18 | [ 0., 0., 0., 0., 0., 0., -1., -1., -1.], 19 | [ 1., 0., 0., 1., 0., 0., 1., 0., 0.], 20 | [ 0., 1., 0., 0., 1., 0., 0., 1., 0.], 21 | [ 0., 0., 1., 0., 0., 1., 0., 0., 1.]]) 22 | E = np.array([[0., 0., 0., 1., 0., 0.], 23 | [0., 0., 0., 0., 1., 0.], 24 | [0., 0., 0., 0., 0., 1.], 25 | [0., 0., 0., 0., 0., 0.], 26 | [0., 0., 0., 0., 0., 0.], 27 | [0., 0., 0., 0., 0., 0.]]) 28 | M = np.array([[ 0., 0., 0.], 29 | [ 0., 0., 0.], 30 | [ 0., 0., 0.], 31 | [-40., 0., 0.], 32 | [ 0., -40., 0.], 33 | [ 0., 0., -40.]]) 34 | h = np.array([ 0., 0., 0., 206., 274., 220.]) 35 | 36 | MP = Model('MP') 37 | 38 | # construct main problem 39 | c = np.array([400, 414, 326]) 40 | a = np.array([18, 25, 20]) 41 | b = np.array([22, 33, 24, 33, 23, 30, 20, 25, 27]) 42 | bigM = 10**3 43 | LB = -GRB.INFINITY 44 | UB = GRB.INFINITY 45 | epsilon = 1e-5 46 | k = 1 47 | u = [] 48 | l = [] 49 | y = MP.addMVar((3,), obj=c, vtype=GRB.BINARY) 50 | z = MP.addMVar((3,), obj=a, vtype=GRB.CONTINUOUS) 51 | d = MP.addMVar((3,), lb=0, name='d') 52 | eta = MP.addMVar((1,), obj=1, vtype=GRB.CONTINUOUS) 53 | 54 | # construct the MP 55 | MP.addConstr(Ay[:, :3]@y+Ay[:, 3:]@z >= by) 56 | MP.optimize() 57 | MP_obj = MP.ObjVal 58 | LB = max(MP_obj, LB) 59 | 60 | SP = Model('SP') 61 | x = SP.addMVar((9,), vtype=GRB.CONTINUOUS, name='x') 62 | pi = SP.addMVar(G.shape[0], vtype=GRB.CONTINUOUS, name='pi') 63 | g = SP.addMVar((3,), ub=1, vtype=GRB.CONTINUOUS, name='g') 64 | v = SP.addMVar((G.shape[0],), vtype=GRB.BINARY, name='v') 65 | w = SP.addMVar((G.shape[1],), vtype=GRB.BINARY, name='w') 66 | 67 | G1 = SP.addConstr(G@x >= h-M@g-E@np.concatenate([y.x, z.x]), name="G1") 68 | SP.addConstr(G.T@pi <= b, name='pi') 69 | SP.addConstr(pi <= bigM*v, name='v') 70 | G2 = SP.addConstr( 71 | G@x-h+E@np.concatenate([y.x, z.x])+M@g <= bigM*(1-v), name='G2') 72 | SP.addConstr(x <= bigM*w, name='w1') 73 | SP.addConstr(b-G.T@pi <= bigM*(1-w), name='w2') 74 | SP.addConstr(g[:2].sum() <= 1.2, name='g1') 75 | SP.addConstr(g.sum() <= 1.8, name='g2') 76 | SP.setObjective(b@x, GRB.MAXIMIZE) 77 | SP.optimize() 78 | SP_obj = SP.ObjVal 79 | UB = min(UB, c@y.x+a@z.x+SP_obj) 80 | MP.reset() 81 | while abs(UB-LB) >= epsilon: 82 | # add x^{k+1} 83 | x_new = MP.addMVar((9,), vtype=GRB.CONTINUOUS) 84 | # eta>=bTx^{k+1} 85 | MP.addConstr(eta >= (h-E[:, :3]@y-E[:, 3:]@z-M@g.x)@pi.x) 86 | # Ey+Gx^{k+1}>=h-Mu_{k+1} 87 | # MP.addConstr(E[:,:3]@y+E[:,3:]@z+G@x_new>=h-M@g.x) 88 | MP.optimize() 89 | SP.reset() 90 | LB = max(LB, MP.objval) 91 | # update the SP constrs according to the MP solution 92 | SP.remove(G1) 93 | SP.remove(G2) 94 | G1 = SP.addConstr(G@x >= h-M@g-E@np.concatenate([y.x, z.x]), name="G1") 95 | G2 = SP.addConstr(G@x-h+E@np.concatenate([y.x, z.x])+M@g <= bigM*(1-v), name='G2') 96 | SP.optimize() 97 | # obtain the optimal y^{k+1} 98 | SP_obj = SP.ObjVal 99 | UB = min(UB, c@y.x+a@z.x+SP_obj) 100 | MP.reset() 101 | k += 1 102 | u.append(UB) 103 | l.append(LB) 104 | # go back to the MP 105 | print("经过{}次迭代".format(k)) 106 | print("上界为:{}".format(UB)) 107 | print("下界为:{}".format(LB)) 108 | -------------------------------------------------------------------------------- /CCGExample.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun Apr 2 17:21:33 2023 4 | @author: wyx 5 | """ 6 | from gurobipy import * 7 | import numpy as np 8 | 9 | # Parameters 10 | Ay = np.array([[800., 0., 0., -1., 0., 0.], 11 | [ 0., 800., 0., 0., -1., 0.], 12 | [ 0., 0., 800., 0., 0., -1.], 13 | [ 0., 0., 0., 1., 1., 1.]]) 14 | by = np.array([ 0., 0., 0., 772.]) 15 | G = np.array([[-1., -1., -1., 0., 0., 0., 0., 0., 0.], 16 | [ 0., 0., 0., -1., -1., -1., 0., 0., 0.], 17 | [ 0., 0., 0., 0., 0., 0., -1., -1., -1.], 18 | [ 1., 0., 0., 1., 0., 0., 1., 0., 0.], 19 | [ 0., 1., 0., 0., 1., 0., 0., 1., 0.], 20 | [ 0., 0., 1., 0., 0., 1., 0., 0., 1.]]) 21 | E = np.array([[0., 0., 0., 1., 0., 0.], 22 | [0., 0., 0., 0., 1., 0.], 23 | [0., 0., 0., 0., 0., 1.], 24 | [0., 0., 0., 0., 0., 0.], 25 | [0., 0., 0., 0., 0., 0.], 26 | [0., 0., 0., 0., 0., 0.]]) 27 | M = np.array([[ 0., 0., 0.], 28 | [ 0., 0., 0.], 29 | [ 0., 0., 0.], 30 | [-40., 0., 0.], 31 | [ 0., -40., 0.], 32 | [ 0., 0., -40.]]) 33 | h = np.array([ 0., 0., 0., 206., 274., 220.]) 34 | 35 | MP = Model('MP') 36 | 37 | # construct main problem 38 | c = np.array([400, 414, 326]) 39 | a = np.array([18, 25, 20]) 40 | b = np.array([22, 33, 24, 33, 23, 30, 20, 25, 27]) 41 | bigM = 10**5 42 | LB = -GRB.INFINITY 43 | UB = GRB.INFINITY 44 | epsilon = 1e-5 45 | k = 1 46 | 47 | y = MP.addMVar((3,), obj=c, vtype=GRB.BINARY) 48 | z = MP.addMVar((3,), obj=a, vtype=GRB.CONTINUOUS) 49 | d = MP.addMVar((3,), lb=0, name='d') 50 | eta = MP.addMVar((1,), obj=1, vtype=GRB.CONTINUOUS) 51 | 52 | # construct the MP 53 | MP.addConstr(Ay[:, :3]@y+Ay[:, 3:]@z >= by) 54 | MP.optimize() 55 | MP_obj = MP.ObjVal 56 | LB = max(MP_obj, LB) 57 | 58 | 59 | SP = Model('SP') 60 | x = SP.addMVar((9,), vtype=GRB.CONTINUOUS, name='x') 61 | pi = SP.addMVar(G.shape[0], vtype=GRB.CONTINUOUS, name='pi') 62 | g = SP.addMVar((3,), ub=1, vtype=GRB.CONTINUOUS, name='g') 63 | v = SP.addMVar((G.shape[0],), vtype=GRB.BINARY, name='v') 64 | w = SP.addMVar((G.shape[1],), vtype=GRB.BINARY, name='w') 65 | 66 | G1 = SP.addConstr(G@x >= h-M@g-E@np.concatenate([y.x, z.x]), name="G1") 67 | SP.addConstr(G.T@pi <= b, name='pi') 68 | SP.addConstr(pi <= bigM*v, name='v') 69 | G2 = SP.addConstr( 70 | G@x-h+E@np.concatenate([y.x, z.x])+M@g <= bigM*(1-v), name='G2') 71 | SP.addConstr(x <= bigM*w, name='w1') 72 | SP.addConstr(b-G.T@pi <= bigM*(1-w), name='w2') 73 | SP.addConstr(g[:2].sum() <= 1.2, name='g1') 74 | SP.addConstr(g.sum() <= 1.8, name='g2') 75 | SP.setObjective(b@x, GRB.MAXIMIZE) 76 | SP.optimize() 77 | SP_obj = SP.ObjVal 78 | UB = min(UB, c@y.x+a@z.x+SP_obj) 79 | MP.reset() 80 | while abs(UB-LB) >= epsilon: 81 | if SP_obj < GRB.INFINITY: 82 | MP.reset() 83 | # add x^{k+1} 84 | x_new = MP.addMVar((9,), vtype=GRB.CONTINUOUS) 85 | # eta>=bTx^{k+1} 86 | MP.addConstr(eta >= b.T@x_new) 87 | # Ey+Gx^{k+1}>=h-Mu_{k+1} 88 | MP.addConstr(E[:, :3]@y+E[:, 3:]@z+G@x_new >= h-M@g.x) 89 | SP.reset() 90 | MP.optimize() 91 | MP_obj = MP.objval 92 | LB = max(LB, MP_obj) 93 | else: 94 | x_new = MP.addMVar((9,), vtype=GRB.CONTINUOUS) 95 | MP.addConstr(E[:, :3]@y+E[:, 3:]@z+G@x_new >= h-M@g.x) 96 | # update the SP constrs according to the MP solution 97 | SP.remove(G1) 98 | SP.remove(G2) 99 | G1 = SP.addConstr(G@x >= h-M@g-E@np.concatenate([y.x, z.x]), name="G1") 100 | G2 = SP.addConstr( 101 | G@x-h+E@np.concatenate([y.x, z.x])+M@g <= bigM*(1-v), name='G2') 102 | SP.optimize() 103 | # obtain the optimal y^{k+1} 104 | SP_obj = SP.ObjVal 105 | UB = min(UB, c@y.x+a@z.x+SP_obj) 106 | k += 1 107 | # go back to the MP 108 | print("经过{}次迭代".format(k)) 109 | print("上界为:{}".format(UB)) 110 | print("下界为:{}".format(LB)) 111 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Two-Stage-Robust-Optimization 2 | Two stage robust optimization using a column-and-constraint generation (C&CG) method and Benders Decomposition. 3 | This is an example code for the classical C&CG algorithm for two stage robust optimization, which is programmed by Python and solved by Gurobi solver. 4 | 5 | please refer to 6 | 7 | "Zeng B, Zhao L. Solving two-stage robust optimization problems using a column-and-constraint generation method[J]. Operations Research Letters, 2013, 41(5): 457-461." 8 | 9 | for more details. 10 | 11 | All the constraints were transformed into Matrix formulations. 12 | 13 | C&CG iteration process 14 | 15 | | interations | LB |UB| 16 | | ---- | ---- |---- | 17 | | 1 | 14296 | 35238 | 18 | | 2 | 33680 | 33680 | 19 | 20 | Benders decomposition iteration process 21 | 22 | | interations | LB |UB| 23 | | ---- | ---- |---- | 24 | | 1 | 14296 | 35238 | 25 | | 2 | 14665 | 35238 | 26 | | 3 | 14860 | 35238 | 27 | | 4 | 15227 | 35238 | 28 | | 5 | 30532 | 34556 | 29 | | 6 | 30956 | 34556 | 30 | | 7 | 30958 | 34556 | 31 | | 8 | 31383 | 34556 | 32 | | 9 | 33127 | 33680 | 33 | | 10 | 33543 | 33680 | 34 | | 11 | 33680 | 33680 | 35 | --------------------------------------------------------------------------------