Loading...
Searching...
No Matches
benders2stage.m
1function benders2stage(varargin)
2
3 % check workspace info from arguments
4 if nargin > 0
5 wsInfo = gams.control.WorkspaceInfo();
6 wsInfo.systemDirectory = varargin{1};
7 ws = gams.control.Workspace(wsInfo);
8 else
9 ws = gams.control.Workspace();
10 end
11
12 data = {
13 'Sets '
14 'i factories /f1*f3/ '
15 'j distribution centers /d1*d5/ '
16 ' '
17 'Parameter '
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/ '
25 ' '
26 'Table transcost(i,j) unit transportation cost '
27 ' d1 d2 d3 d4 d5 '
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; '
31 ' '
32 '$ifthen not set useBig '
33 'Set '
34 ' s scenarios /lo,mid,hi/ '
35 ' '
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; '
41 '$else '
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;'
45 'option seed=1234; '
46 'ScenarioData(s,''prob'') = 1/card(s); '
47 'ScenarioData(s,j) = demand(j)*uniform(0.6,1.4); '
48 '$endif '};
49 data = sprintf('%s\n', data{:});
50
51 masterModel = {
52 'Sets '
53 'i factories '
54 'j distribution centers '
55 ' '
56 'Parameter '
57 ' capacity(i) unit capacity at factories '
58 ' prodcost unit production cost '
59 ' transcost(i,j) unit transportation cost '
60 ' '
61 '$if not set datain $abort ''datain not set'' '
62 '$gdxin %datain% '
63 '$load i j capacity prodcost transcost '
64 ' '
65 '* Benders master problem '
66 '$if not set maxiter $set maxiter 25 '
67 'Set '
68 ' iter max Benders iterations /1*%maxiter%/ '
69 ' '
70 'Parameter '
71 ' cutconst(iter) constants in optimality cuts '
72 ' cutcoeff(iter,j) coefficients in optimality cuts '
73 ' '
74 'Variables '
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; '
81 ' '
82 'Equations '
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; '
87 ' '
88 'masterobj.. '
89 ' zmaster =e= theta -sum((i,j), transcost(i,j)*ship(i,j)) '
90 ' - sum(i,prodcost*product(i)); '
91 ' '
92 'receive(j).. received(j) =e= sum(i, ship(i,j)); '
93 ' '
94 'production(i).. product(i) =e= sum(j, ship(i,j)); '
95 'product.up(i) = capacity(i); '
96 ' '
97 'optcut(iter).. theta =l= cutconst(iter) + '
98 ' sum(j, cutcoeff(iter,j)*received(j)); '
99 ' '
100 'model masterproblem /all/; '
101 ' '
102 '* Initialize cut to be non-binding '
103 'cutconst(iter) = 1e15; '
104 'cutcoeff(iter,j) = eps; '};
105 masterModel = sprintf('%s\n', masterModel{:});
106
107 subModel = {
108 'Sets '
109 ' i factories '
110 ' j distribution centers '
111 ' '
112 'Parameter '
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 '
117 ' '
118 '$if not set datain $abort ''datain not set'' '
119 '$gdxin %datain% '
120 '$load i j demand price wastecost '
121 ' '
122 '* Benders'' subproblem '
123 ' '
124 'Variables '
125 ' sales(j) sales (actually sold) '
126 ' waste(j) overstocked products '
127 ' zsub objective variable of sub problem '
128 'Positive variables sales, waste '
129 ' '
130 'Equations '
131 ' subobj subproblem objective function '
132 ' selling(j) part of received is sold '
133 ' market(j) upperbound on sales '
134 '; '
135 ' '
136 'subobj.. '
137 ' zsub =e= sum(j, price*sales(j)) - sum(j, wastecost*waste(j)); '
138 ' '
139 'selling(j).. sales(j) + waste(j) =e= received(j); '
140 ' '
141 'market(j).. sales(j) =l= demand(j); '
142 ' '
143 'model subproblem /subobj,selling,market/; '
144 ' '
145 '* Initialize received '
146 'received(j) = demand(j); '};
147 subModel = sprintf('%s\n', subModel{:});
148
149 dataJob = ws.addJobFromString(data);
150
151 optData = ws.addOptions();
152 optData.defines('useBig', '1');
153 optData.defines('nrScen', '100');
154 dataJob.run(optData);
155 optData.dispose();
156
157 scenarioData = dataJob.outDB.getParameter('ScenarioData');
158 opt = ws.addOptions();
159 opt.defines('datain', dataJob.outDB.name);
160 maxiter = 40;
161 opt.defines('maxiter', int2str(maxiter));
162 opt.setAllModelTypes('cplex');
163
164 cpMaster = ws.addCheckpoint();
165 cpSub = ws.addCheckpoint();
166
167 ws.addJobFromString(masterModel).run(opt, cpMaster, dataJob.outDB);
168
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)});
178
179 ws.addJobFromString(subModel).run(opt, cpSub, dataJob.outDB);
180
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)});
186
187 opt.dispose();
188
189 lowerbound = -Inf;
190 upperbound = Inf;
191 objmaster = Inf;
192 iter = 1;
193
194 while true
195 fprintf('Iteration: %d\n', iter);
196
197 % Solve master
198 if iter == 1 % fix theta for first iteration
199 thetaFix.addRecord().value = 0;
200 else
201 thetaFix.clear();
202 end
203 masteri.solve(gams.control.globals.SymbolUpdateType.BASECASE);
204
205 fprintf(' Master %s : obj=%g\n', masteri.modelStatus.select, masteri.syncDB.getVariable('zmaster').record.level);
206 if iter > 1
207 upperbound = masteri.syncDB.getVariable('zmaster').record.level;
208 end
209 objmaster = masteri.syncDB.getVariable('zmaster').record.level - theta.record.level;
210
211 % Set received from master
212 received.clear();
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));
216 end
217
218 cutconst.addRecord(int2str(iter));
219 objsub = 0.0;
220 for s = dataJob.outDB.getSet('s').records
221 demand.clear();
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;
224 end
225
226 subi.solve(gams.control.globals.SymbolUpdateType.BASECASE);
227 fprintf(' Sub %s : obj=%g\n', subi.modelStatus.select, subi.syncDB.getVariable('zsub').record.level);
228
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
232 keys = j{1}.keys;
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;
237 end
238 end
239
240 lowerbound = max(lowerbound, objmaster + objsub);
241 iter = iter + 1;
242 if iter > maxiter
243 error('Benders out of iterations');
244 end
245
246 fprintf(' lowerbound: %g upperbound: %g objmaster: %g\n', lowerbound, upperbound, objmaster);
247
248 if upperbound - lowerbound < 0.001 * (1 + abs(upperbound))
249 break;
250 end
251 end
252
253 masteri.dispose();
254 subi.dispose();
255end