1function benders2stage(varargin)
3 % check workspace info from arguments
5 wsInfo = gams.control.WorkspaceInfo();
6 wsInfo.systemDirectory = varargin{1};
7 ws = gams.control.Workspace(wsInfo);
9 ws = gams.control.Workspace();
14 'i factories /f1*f3/ '
15 'j distribution centers /d1*d5/ '
18 'capacity(i) unit capacity at factories '
19 ' /f1 500, f2 450, f3 650/ '
20 'demand(j) unit demand at distribution centers '
21 ' /d1 160, d2 120, d3 270, d4 325, d5 700 / '
22 'prodcost unit production cost /14/ '
23 'price sales price /24/ '
24 'wastecost cost of removal of overstocked products /4/ '
26 'Table transcost(i,j) unit transportation cost '
28 ' f1 2.49 5.21 3.76 4.85 2.07 '
29 ' f2 1.46 2.54 1.83 1.86 4.76 '
30 ' f3 3.26 3.08 2.60 3.76 4.45; '
32 '$ifthen not set useBig '
34 ' s scenarios /lo,mid,hi/ '
36 'Table ScenarioData(s,*) possible outcomes for demand plus probabilities '
37 ' d1 d2 d3 d4 d5 prob '
38 ' lo 150 100 250 300 600 0.25 '
39 ' mid 160 120 270 325 700 0.50 '
40 ' hi 170 135 300 350 800 0.25; '
42 '$if not set nrScen $set nrScen 10 '
43 'Set s scenarios /s1*s%nrScen%/;'
44 'parameter ScenarioData(s,*) possible outcomes for demand plus probabilities;'
46 'ScenarioData(s,''prob'') = 1/card(s); '
47 'ScenarioData(s,j) = demand(j)*uniform(0.6,1.4); '
49 data = sprintf(
'%s\n', data{:});
54 'j distribution centers '
57 ' capacity(i) unit capacity at factories '
58 ' prodcost unit production cost '
59 ' transcost(i,j) unit transportation cost '
61 '$if not set datain $abort ''datain not set'' '
63 '$load i j capacity prodcost transcost '
65 '* Benders master problem '
66 '$if not set maxiter $set maxiter 25 '
68 ' iter max Benders iterations /1*%maxiter%/ '
71 ' cutconst(iter) constants in optimality cuts '
72 ' cutcoeff(iter,j) coefficients in optimality cuts '
75 ' ship(i,j) shipments '
76 ' product(i) production '
77 ' received(j) quantity sent to market '
78 ' zmaster objective variable of master problem '
79 ' theta future profit '
80 'Positive Variables ship; '
83 ' masterobj master objective function '
84 ' production(i) calculate production in each factory '
85 ' receive(j) calculate quantity to be send to markets '
86 ' optcut(iter) Benders optimality cuts; '
89 ' zmaster =e= theta -sum((i,j), transcost(i,j)*ship(i,j)) '
90 ' - sum(i,prodcost*product(i)); '
92 'receive(j).. received(j) =e= sum(i, ship(i,j)); '
94 'production(i).. product(i) =e= sum(j, ship(i,j)); '
95 'product.up(i) = capacity(i); '
97 'optcut(iter).. theta =l= cutconst(iter) + '
98 ' sum(j, cutcoeff(iter,j)*received(j)); '
100 'model masterproblem /all/; '
102 '* Initialize cut to be non-binding '
103 'cutconst(iter) = 1e15; '
104 'cutcoeff(iter,j) = eps; '};
105 masterModel = sprintf(
'%s\n', masterModel{:});
110 ' j distribution centers '
113 ' demand(j) unit demand at distribution centers '
114 ' price sales price '
115 ' wastecost cost of removal of overstocked products '
116 ' received(j) first stage decision units received '
118 '$if not set datain $abort ''datain not set'' '
120 '$load i j demand price wastecost '
122 '* Benders'' subproblem '
125 ' sales(j) sales (actually sold) '
126 ' waste(j) overstocked products '
127 ' zsub objective variable of sub problem '
128 'Positive variables sales, waste '
131 ' subobj subproblem objective function '
132 ' selling(j) part of received is sold '
133 ' market(j) upperbound on sales '
137 ' zsub =e= sum(j, price*sales(j)) - sum(j, wastecost*waste(j)); '
139 'selling(j).. sales(j) + waste(j) =e= received(j); '
141 'market(j).. sales(j) =l= demand(j); '
143 'model subproblem /subobj,selling,market/; '
145 '* Initialize received '
146 'received(j) = demand(j); '};
147 subModel = sprintf(
'%s\n', subModel{:});
149 dataJob = ws.addJobFromString(data);
151 optData = ws.addOptions();
152 optData.defines(
'useBig',
'1');
153 optData.defines(
'nrScen',
'100');
154 dataJob.run(optData);
157 scenarioData = dataJob.outDB.getParameter(
'ScenarioData');
158 opt = ws.addOptions();
159 opt.defines(
'datain', dataJob.outDB.name);
161 opt.defines(
'maxiter', int2str(maxiter));
162 opt.setAllModelTypes(
'cplex');
164 cpMaster = ws.addCheckpoint();
165 cpSub = ws.addCheckpoint();
167 ws.addJobFromString(masterModel).run(opt, cpMaster, dataJob.outDB);
169 masteri = cpMaster.addModelInstance();
170 cutconst = masteri.syncDB.addParameter(
'cutconst', 1,
'Benders optimality cut constant');
171 cutcoeff = masteri.syncDB.addParameter(
'cutcoeff', 2,
'Benders optimality coefficients');
172 theta = masteri.syncDB.addVariable(
'theta', 0, gams.control.globals.VarType.FREE, ...
173 'Future profit function variable');
174 thetaFix = masteri.syncDB.addParameter(
'thetaFix', 0,
'');
175 masteri.instantiate(
'masterproblem max zmaster using lp', opt, {...
176 gams.control.Modifier(cutconst), gams.control.Modifier(cutcoeff), ...
177 gams.control.Modifier(theta, gams.control.globals.UpdateAction.FIXED, thetaFix)});
179 ws.addJobFromString(subModel).run(opt, cpSub, dataJob.outDB);
181 subi = cpSub.addModelInstance();
182 received = subi.syncDB.addParameter(
'received', 1,
'units received from master');
183 demand = subi.syncDB.addParameter(
'demand', 1,
'stochastic demand');
184 subi.instantiate(
'subproblem max zsub using lp', opt, {...
185 gams.control.Modifier(received), gams.control.Modifier(demand)});
195 fprintf(
'Iteration: %d\n', iter);
198 if iter == 1 % fix theta
for first iteration
199 thetaFix.addRecord().value = 0;
203 masteri.solve(gams.control.globals.SymbolUpdateType.BASECASE);
205 fprintf(
' Master %s : obj=%g\n', masteri.modelStatus.select, masteri.syncDB.getVariable(
'zmaster').record.level);
207 upperbound = masteri.syncDB.getVariable(
'zmaster').record.level;
209 objmaster = masteri.syncDB.getVariable(
'zmaster').record.level - theta.record.level;
211 % Set received from master
213 for r = masteri.syncDB.getVariable(
'received').records
214 received.addRecord(r{1}.keys).value = r{1}.level;
215 cutcoeff.addRecord(int2str(iter), r{1}.key(1));
218 cutconst.addRecord(int2str(iter));
220 for s = dataJob.outDB.getSet(
's').records
222 for j = dataJob.outDB.getSet(
'j').records
223 demand.addRecord(j{1}.keys).value = scenarioData.findRecord({s{1}.key(1), j{1}.key(1)}).value;
226 subi.solve(gams.control.globals.SymbolUpdateType.BASECASE);
227 fprintf(
' Sub %s : obj=%g\n', subi.modelStatus.select, subi.syncDB.getVariable(
'zsub').record.level);
229 probability = scenarioData.findRecord({s{1}.key(1),
'prob'}).value;
230 objsub = objsub + probability * subi.syncDB.getVariable('zsub').record.level;
231 for j = dataJob.outDB.getSet(
'j').records
233 record = cutconst.findRecord(int2str(iter));
234 record.value = record.value + probability * subi.syncDB.getEquation('market').findRecord(keys).marginal * demand.findRecord(keys).value;
235 record = cutcoeff.findRecord({int2str(iter), j{1}.key(1)});
236 record.value = record.value + probability * subi.syncDB.getEquation('selling').findRecord(keys).marginal;
240 lowerbound = max(lowerbound, objmaster + objsub);
243 error(
'Benders out of iterations');
246 fprintf(
' lowerbound: %g upperbound: %g objmaster: %g\n', lowerbound, upperbound, objmaster);
248 if upperbound - lowerbound < 0.001 * (1 + abs(upperbound))