SGSolve
sgsolver_maxminmax_grb.hpp
1 // This file is part of the SGSolve library for stochastic games
2 // Copyright (C) 2019 Benjamin A. Brooks
3 //
4 // SGSolve free software: you can redistribute it and/or modify it
5 // under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // SGSolve is distributed in the hope that it will be useful, but
10 // WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 // General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see
16 // <http://www.gnu.org/licenses/>.
17 //
18 // Benjamin A. Brooks
19 // ben@benjaminbrooks.net
20 // Chicago, IL
21 
22 #ifndef _SGSOLVER_MAXMINMAX_GRB_HPP
23 #define _SGSOLVER_MAXMINMAX_GRB_HPP
24 
25 #include "sgcommon.hpp"
26 #include "sgutilities.hpp"
27 #include "sggame.hpp"
28 #include "sgexception.hpp"
29 #include "gurobi_c++.h"
30 
32 
38 {
39 private:
41  const SGGame & game;
42 
44  double minRotation = -3.14159*1e-4;
45 
47  double payoffBound = 0;
48 
50  vector< vector<bool> > eqActions;
51  vector<bool> feasibleActions;
52  int numFeasibleActions;
53 
54  int numActions_grandTotal;
55  vector<int> gaToS;
56  vector<int> gaToA;
57 
59  list< vector<double> > bounds;
60 
62  list<SGPoint> directions;
63 
64  SGTuple threatTuple;
65 
68 
70  const vector< vector< SGPoint> > & payoffs;
71  const vector< vector< vector<double> > > & prob;
72  const vector< vector<int> > & numActions;
73  const vector< int > & numActions_total;
74  const int numStates;
75  const double delta;
76  const int numPlayers;
77 
78  // Parameters
79  const double regimeChangeTol = 1e-9;
80  const double pseudoConstrTol = 1e-3;
81  const double convTol = 1e-6;
82  const int maxIter = 1e3;
83 
84 
85 public:
86  enum SGRegimeStatus
87  {
88  SG_RECURSIVE,
89  SG_FIXED
90  };
91  enum SGQuadrant
92  {
93  SG_NORTHEAST,
94  SG_SOUTHEAST,
95  SG_SOUTHWEST,
96  SG_NORTHWEST
97  };
98  enum SGSolverMode
99  {
100  SG_FEASIBLE,
101  SG_MAXMINMAX,
102  SG_APS
103  };
104 
105  enum SGICStatus
106  {
107  SG_NONE,
108  SG_BINDING0,
109  SG_BINDING1,
110  SG_BINDING01
111  };
112 
115  game(_game),
116  payoffs(_game.getPayoffs()),
117  prob(_game.getProbabilities()),
118  numActions(_game.getNumActions()),
119  numActions_total(_game.getNumActions_total()),
120  numStates(_game.getNumStates()),
121  delta(_game.getDelta()),
122  numPlayers(2),
123  bounds(),
124  directions(),
125  numDirections()
126  {
127  }
128 
130  const SGGame & getGame() const { return game; }
131  // //! Returns the environment
132  // GRBEnv & getEnv() {return env; }
134  const list< vector<double> > & getBounds() const { return bounds; }
136  const list<SGPoint> & getDirections() const { return directions; }
138  int getNumDirections() const { return numDirections; }
139 
141  void solve();
142 
144  void initialize();
145 
147  double iterate(const SGSolverMode mode,
148  int & steps);
149 
150  void addBoundingHyperplane(SGPoint & currDir,
151  GRBConstr & xConstr,
152  GRBConstr & yConstr,
153  GRBVar * valueFn,
154  int numStates,
155  list<SGPoint> & newDirections,
156  list< vector<double> > & newBounds,
157  GRBModel & model,
158  const bool addDirection);
159  void printIteration(ofstream & ofs, int numIter);
160 
161 };
162 
163 
165 {
166  double movement = 1.0;
167  int numIter = 0;
168  int steps = 0;
169 
170  SGSolverMode mode = SG_MAXMINMAX;
171  // mode = SG_FEASIBLE;
172  // mode = SG_APS;
173 
174  initialize();
175 
176  threatTuple = SGTuple (game.getNumStates(),-GRB_INFINITY);
177 
179 
180  ofstream ofs;
181  ofs.open("sgsolver_v3.log");
182 
183  try
184  {
185  // First round to compute the feasible set
186  iterate(SG_FEASIBLE,steps);
187  printIteration(ofs, numIter);
188 
189  // Now implement the ABS operator
190  while (movement > convTol
191  && numIter < maxIter)
192  {
193  movement = iterate(mode,steps);
194 
195  cout << "Iteration: " << numIter
196  << ", directions: " << directions.size()
197  << ", steps: " << steps
198  << ", movement: " << movement
199  << endl;
200 
201  numIter++;
202 
203  // Save the bounds in a log file
204  printIteration(ofs, numIter);
205  } // while
206  }
207  catch (GRBException & e)
208  {
209  cout << "GRB Exception caught: " << e.getMessage() << endl;
210  }
211 
212  ofs.close();
213 
214  cout << "Converged!" << endl;
215 }
216 
218 {
219  payoffBound = 0;
220  for (int s = 0; s < numStates; s++)
221  {
222  for (int a = 0; a < numActions_total[s]; a++)
223  {
224  for (int p = 0; p < numPlayers; p++)
225  {
226  payoffBound = max(payoffBound,abs(payoffs[s][a][p]));
227  } // for p
228  } // for a
229  } // for s
230 
231  payoffBound *= 1e2;
232 }
233 
234 double SGSolver_MaxMinMax_GRB::iterate(const SGSolverMode mode, int & steps)
235 {
236  GRBEnv env;
237  // env.set(GRB_IntParam_Method,0); // primal simplex
238  // env.set(GRB_IntParam_Method,1); // dual simplex
239  env.set(GRB_IntParam_Method,5); // deterministic concurrent simplex; needs newer version of gurobi
240  // env.set(GRB_IntParam_Crossover,0); // disable crossover
241  // env.set(GRB_DoubleParam_BarConvTol,1e-12);
242  env.set(GRB_DoubleParam_OptimalityTol,1e-9);
243  env.set(GRB_DoubleParam_MarkowitzTol,0.999);
244  env.set(GRB_DoubleParam_FeasibilityTol,1e-9);
245  // env.set(GRB_IntParam_DualReductions,0);
246  env.set(GRB_IntParam_OutputFlag,0);
247  GRBModel model(env);
248 
249  vector<int> numEqActions(numStates,0);
250 
251  vector<int> actions, deviations;
252  int deviation;
253 
254  numActions_grandTotal = 0;
255  for (int s = 0; s < game.getNumStates(); s++)
256  {
257  if (eqActions[s].empty())
258  {
259  numActions_grandTotal += numActions_total[s];
260  numEqActions[s] = numActions_total[s];
261  }
262  else
263  {
264  for (int a = 0; a < eqActions[s].size(); a++)
265  {
266  if (eqActions[s][a])
267  {
268  numActions_grandTotal ++;
269  numEqActions[s] ++;
270  }
271  }
272  }
273  }
274  feasibleActions = vector<bool>(numActions_grandTotal,true);
275  numFeasibleActions = numActions_grandTotal;
276 
277  gaToS = vector<int> (numActions_grandTotal,0);
278  gaToA = vector<int> (numActions_grandTotal,0);
279  {
280  int ga = 0;
281  for (int s = 0; s < numStates; s++)
282  {
283  for (int a = 0; a < numActions_total[s]; a++)
284  {
285  if (eqActions[s][a])
286  {
287  gaToS[ga] = s;
288  gaToA[ga] = a;
289  ++ga;
290  }
291  } // for a
292  } // for s
293  }
294 
295  const int numDirections = directions.size();
296  if (mode != SG_FEASIBLE)
297  assert(numDirections>0);
298 
299  GRBVar * valueFn = model.addVars(numStates);
300  GRBVar * contVals = model.addVars(numActions_grandTotal);
301  GRBVar * valueFunSlacks = model.addVars(numActions_grandTotal);
302  GRBVar * pseudoContVals = model.addVars(numActions_grandTotal);
303  GRBVar * recursiveContValSlacks = model.addVars(numActions_grandTotal);
304  GRBVar * APSContValSlacks = model.addVars(numActions_grandTotal);
305  GRBVar * APSContValVar = model.addVars(numActions_grandTotal);
306  GRBVar * feasMult = model.addVars(numActions_grandTotal*numDirections);
307  GRBVar * ICMult = model.addVars(numActions_grandTotal*numPlayers);
308  GRBVar * currDirVar = model.addVars(2);
309 
310  model.update();
311 
312  SGPoint currDir(0.0,1.0);
313  currDirVar[0].set(GRB_DoubleAttr_LB,-GRB_INFINITY);
314  currDirVar[1].set(GRB_DoubleAttr_LB,-GRB_INFINITY);
315 
316  GRBConstr xConstr = model.addConstr(currDirVar[0] == 0.0);
317  GRBConstr yConstr = model.addConstr(currDirVar[1] == 1.0);
318  vector< vector<GRBConstr> > valueFnConstr (numStates);
319  for (int s = 0; s < numStates; s++)
320  {
321  valueFnConstr[s] = vector<GRBConstr> (numEqActions[s]);
322  }
323 
324  vector<GRBLinExpr> recursiveContVal(numActions_grandTotal,0);
325  vector<SGRegimeStatus> regimeStatus(numActions_grandTotal,SG_RECURSIVE);
326  vector<int> optActions(numStates,-1);
327 
328  // This tracks which IC constraints are binding.
329  vector<SGICStatus> optICStatuses(numStates,SG_NONE);
330  vector<SGPoint> optPayoffs(numStates,0);
331 
332  for (int ga = 0; ga < numActions_grandTotal; ga++)
333  {
334  contVals[ga].set(GRB_DoubleAttr_LB,-2*payoffBound);
335  pseudoContVals[ga].set(GRB_DoubleAttr_LB,-2*payoffBound);
336  APSContValVar[ga].set(GRB_DoubleAttr_LB,-2*payoffBound);
337  }
338  for (int s = 0; s < numStates; s++)
339  valueFn[s].set(GRB_DoubleAttr_LB,-GRB_INFINITY);
340 
341  GRBLinExpr objective = 0;
342  {
343  int ga = 0; // grandAction
344  for (int s = 0; s < numStates; s++)
345  {
346  objective += 1e10*valueFn[s];
347 
348  int actr = 0;
349  // Add feasibility constraints
350  for (int a = 0; a < numActions_total[s]; a++)
351  {
352  if (!eqActions[s].empty() && !eqActions[s][a])
353  continue;
354 
355  for (int sp = 0; sp < numStates; sp++)
356  recursiveContVal[ga] += prob[s][a][sp]*valueFn[sp];
357 
358  GRBLinExpr APSContVal = 0;
359  if (mode==SG_MAXMINMAX || mode==SG_APS)
360  {
361  // Calculate minIC for each player.
362  for (int p = 0; p < numPlayers; p++)
363  {
364  double minIC = SGAction_PencilSharpening::calculateMinIC(a,s,p,
365  game,threatTuple);
366  APSContVal -= ICMult[p+2*ga]*minIC;
367  }
368 
369  {
370  list< vector<double> >::const_iterator bnd;
371  int dirCtr;
372  for (bnd = bounds.begin(),
373  dirCtr = 0;
374  bnd != bounds.end();
375  ++bnd,++dirCtr)
376  {
377  double expBnd = 0;
378  for (int sp = 0; sp < numStates; sp++)
379  expBnd += (*bnd)[sp] * prob[s][a][sp];
380 
381  APSContVal += expBnd * feasMult[ga+dirCtr*numActions_grandTotal];
382  } // for bnd
383  }
384 
385  {
386  list<SGPoint>::const_iterator dir;
387  int dirCtr;
388  for (int p = 0; p < numPlayers; p++)
389  {
390  GRBLinExpr dualConstrLHS = 0;
391 
392  dualConstrLHS += ICMult[p+2*ga];
393  dualConstrLHS += currDirVar[p];
394  for (dir = directions.begin(),
395  dirCtr = 0;
396  dir != directions.end();
397  ++dir,++dirCtr)
398  {
399  dualConstrLHS -= (*dir)[p] * feasMult[ga+dirCtr*numActions_grandTotal];
400  } // for dir
401 
402  model.addConstr(dualConstrLHS==0);
403  } // for p
404  }
405 
406  model.addConstr(pseudoContVals[ga] >= APSContVal);
407  model.addConstr(contVals[ga] == APSContVal+APSContValSlacks[ga]);
408  model.addConstr(APSContValVar[ga]==APSContVal);
409  } // if calculating subgame perfect
410  model.addConstr(pseudoContVals[ga]
411  >= recursiveContVal[ga]*(1.0+pseudoConstrTol));
412  model.addConstr(contVals[ga] == recursiveContVal[ga]+recursiveContValSlacks[ga]);
413 
414  GRBLinExpr valueFnConstrRHS = 0;
415  for (int p = 0; p < numPlayers; p++)
416  valueFnConstrRHS += (1-delta)*payoffs[s][a][p]*currDirVar[p];
417 
418  valueFnConstrRHS += delta*contVals[ga];
419 
420  valueFnConstr[s][actr] = model.addConstr(valueFn[s] == valueFnConstrRHS
421  +valueFunSlacks[ga]);
422 
423  actr++;
424 
425  ++ga;
426  } // for a
427  } // for s
428  } // ga
429 
430  for (int ga = 0; ga < numActions_grandTotal; ga++)
431  {
432  if (mode == SG_APS)
433  {
434  // Start with fixed constraints
435  regimeStatus[ga] = SG_FIXED;
436  APSContValSlacks[ga].set(GRB_DoubleAttr_LB,0);
437  recursiveContValSlacks[ga].set(GRB_DoubleAttr_LB,-GRB_INFINITY);
438  }
439  else
440  {
441  // Start with recursive constraints
442  APSContValSlacks[ga].set(GRB_DoubleAttr_LB,-GRB_INFINITY);
443  recursiveContValSlacks[ga].set(GRB_DoubleAttr_LB,0);
444  }
445  } // for ga
446 
447  // Finish setting up objective
448  for (int ga = 0; ga < numActions_grandTotal; ga++)
449  {
450  objective += pseudoContVals[ga];
451  // objective += contVals[ga];
452  objective += APSContValVar[ga];
453  } // for ga
454 
455  model.setObjective(objective,GRB_MINIMIZE);
456 
457  list< vector<double> > newBounds(0);
458  list<SGPoint> newDirections(0);
459  SGTuple newThreatTuple(numStates);
460 
461  bool passNorth = false;
462  bool newQuadrant = true;
463  SGQuadrant quadrant = SG_NORTHEAST;
464 
465  // Main loop to find new directions/bounds
466  steps = 0;
467  do
468  {
469  // On each iteration, need to accomplish two main tasks.
470 
471  // (i) optimize the objective in the current direction. This
472  // includes changing any regimes as needed.
473  bool regimesSubOptimal = true;
474  int regimeChangeIters = 0;
475 
476  vector<SGRegimeStatus> oldRegimeStatus(regimeStatus);
477  vector<int> oldOptActions(optActions);
478  vector<SGICStatus> oldOptICStatuses(optICStatuses);
479  vector<SGPoint> oldOptPayoffs(optPayoffs);
480 
481  while (regimesSubOptimal
482  && regimeChangeIters < 10*numActions_grandTotal)
483  {
484  model.optimize();
485 
486  if (model.get(GRB_IntAttr_Status)!=GRB_OPTIMAL)
487  {
488  cout << "Warning: model not optimal. Code is: "
489  << model.get(GRB_IntAttr_Status) << endl;
490  }
491  if (model.get(GRB_IntAttr_Status)==GRB_UNBOUNDED)
492  {
493  cout << "Warning: Unbounded model" << endl;
494  }
495  if (model.get(GRB_IntAttr_Status)==GRB_INFEASIBLE)
496  {
497  cout << "Warning: Infeasible model" << endl;
498  }
499 
500  // If just computing the feasible set, skip the next step of
501  // updating regimes
502  if (mode!=SG_MAXMINMAX)
503  break;
504 
505  // Calculate the maximum slack in the continuation values
506  double maxSlack = 0.0;
507  for (int ga = 0; ga < numActions_grandTotal; ga++)
508  {
509  double tmp = 0;
510  tmp = recursiveContVal[ga].getValue()
511  -APSContValVar[ga].get(GRB_DoubleAttr_X);
512  if (regimeStatus[ga]==SG_FIXED)
513  {
514  tmp = -tmp;
515  }
516 
517  if (tmp > maxSlack)
518  maxSlack = tmp;
519  } // for ga
520 
521  //cout << endl << currDir << endl;
522  // If no slack found, skip the regime changes
523  regimesSubOptimal = (maxSlack > regimeChangeTol);
524  if (!regimesSubOptimal)
525  {
526  // Change fixed regimes to recursive if optimal action
527  // has non-binding constraints
528  int ga = 0;
529  for (int s = 0; s < numStates; s++)
530  {
531  int numOptA = 0;
532  // cout << endl << "s = " << s;
533  for (int a = 0; a < numActions_total[s]; a++)
534  {
535  if (!eqActions[s].empty() && !eqActions[s][a])
536  continue;
537  // cout << " (a,vbasis)=(" << a
538  // << "," << valueFunSlacks[ga].get(GRB_IntAttr_VBasis) << ")";
539  if (valueFunSlacks[ga].get(GRB_IntAttr_VBasis) == -1)
540  {
541  numOptA++;
542  optActions[s]=ga;
543  if (regimeStatus[ga] == SG_FIXED
544  && ICMult[2*ga].get(GRB_IntAttr_VBasis)==-1
545  && ICMult[1+2*ga].get(GRB_IntAttr_VBasis)==-1)
546  {
547  regimeStatus[ga] = SG_RECURSIVE;
548  optICStatuses[s] = SG_NONE;
549  APSContValSlacks[ga].set(GRB_DoubleAttr_LB,
550  -GRB_INFINITY);
551  recursiveContValSlacks[ga].set(GRB_DoubleAttr_LB,0.0);
552  } // if
553  else if (regimeStatus[ga] == SG_FIXED)
554  {
555  // Update the number of IC statuses
556  if (ICMult[1+2*ga].get(GRB_IntAttr_VBasis)==-1)
557  optICStatuses[s] = SG_BINDING0;
558  else if (ICMult[2*ga].get(GRB_IntAttr_VBasis)==-1)
559  optICStatuses[s] = SG_BINDING1;
560  else
561  optICStatuses[s] = SG_BINDING01;
562  } // else if
563  else
564  optICStatuses[s] = SG_NONE;
565  } // if
566  ++ga;
567  } // for a
568  //assert(numOptA>=1);
569  } // for s
570  // Optimize one last time with the correct regimes.
571  model.optimize();
572  break;
573  } // if
574  else
575  {
576  // Switch regimes where the slack is greater than
577  // delta*maxSlack (since for these actions, changing all of
578  // the other regimes is insufficient to take up all the slack).
579  for (int ga = 0; ga < numActions_grandTotal; ga++)
580  {
581  switch (regimeStatus[ga])
582  {
583  case SG_RECURSIVE:
584  if (recursiveContVal[ga].getValue()
585  -APSContValVar[ga].get(GRB_DoubleAttr_X)
586  >delta*maxSlack)
587  {
588  regimeStatus[ga] = SG_FIXED;
589  APSContValSlacks[ga].set(GRB_DoubleAttr_LB,0.0);
590  recursiveContValSlacks[ga].set(GRB_DoubleAttr_LB,
591  -GRB_INFINITY);
592  }
593 
594  break;
595 
596  case SG_FIXED:
597  // If we are in the fixed regime, have to change
598  // regime if there's any slack
599 
600  // If this is the optimal action but no IC
601  // constraint binds, switch to recursive
602  if ( (-recursiveContVal[ga].getValue()
603  +APSContValVar[ga].get(GRB_DoubleAttr_X)
604  > regimeChangeTol))
605  {
606  regimeStatus[ga] = SG_RECURSIVE;
607  APSContValSlacks[ga].set(GRB_DoubleAttr_LB,
608  -GRB_INFINITY);
609  recursiveContValSlacks[ga].set(GRB_DoubleAttr_LB,0.0);
610  }
611  break;
612  } // switch
613  } // for ga
614  } // else
615  regimeChangeIters++;
616  } // while
617 
618  // We want to add a new direction only if the optimum changes
619  // for the value function. This means either (a) changing which
620  // constraint binds in some state s or (b) changing a regime for
621  // a binding action. If either of these conditions is met,
622  // change the following flag to true. This will hopefully help
623  // cut down on spurious constraints.
624  bool addDirection = newQuadrant;
625  for (int s = 0; s < numStates; s++)
626  {
627  // Check if the optimal policy changed, and if so, trip the
628  // addDirection flag and update the optimal action
629  if (optActions[s] != oldOptActions[s]
630  || regimeStatus[optActions[s]] != oldRegimeStatus[optActions[s]]
631  || optICStatuses[s] != oldOptICStatuses[s])
632  addDirection = true;
633  } // for s
634  addDirection = true;
635 
636  if (regimeChangeIters >= numActions_grandTotal)
637  cout << "Warning: Too many regime changes. " << regimeChangeIters
638  << " changes but only " << numActions_grandTotal
639  << " action profiles." << endl;
640 
641  // (ii) Find how far we can rotate clockwise without violating
642  // optimality. Add a new hyperplane for this face. Rotate the
643  // objective and continue.
644  double tmp = 0;
645  switch (quadrant)
646  {
647  case SG_NORTHEAST:
648  // Increasing currDirVar[0] and decreasing currDirVar[1]
649  tmp = xConstr.get(GRB_DoubleAttr_SARHSUp);
650  currDir[0] = tmp;
651 
652  addBoundingHyperplane(currDir,xConstr,yConstr,valueFn,
653  numStates,newDirections,newBounds,model,
654  addDirection);
655  newQuadrant = false;
656 
657  if (currDir[1]<0)
658  {
659  quadrant=SG_SOUTHEAST;
660  newQuadrant = true;
661  // cout << "Starting southeast..." << endl;
662  }
663 
664  break;
665 
666  case SG_SOUTHEAST:
667  // Decreasing currDirVar[0] and decreasing currDirVar[1]
668  tmp = yConstr.get(GRB_DoubleAttr_SARHSLow);
669  currDir[1] = tmp;
670 
671  addBoundingHyperplane(currDir,xConstr,yConstr,valueFn,
672  numStates,newDirections,newBounds,model,
673  addDirection);
674 
675  newQuadrant = false;
676  if (currDir[0]<0)
677  {
678  quadrant=SG_SOUTHWEST;
679  newQuadrant = true;
680 
681  // Last direction must have been due south... update
682  // threats for player 2
683  for (int s = 0; s < numStates; s++)
684  newThreatTuple[s][1]=-newBounds.back()[s];
685 
686  // cout << "Starting southwest..." << endl;
687  }
688 
689  break;
690 
691  case SG_SOUTHWEST:
692  // Decreasing currDirVar[0] and increasing currDirVar[1]
693  tmp = xConstr.get(GRB_DoubleAttr_SARHSLow);
694  currDir[0] = tmp;
695 
696  addBoundingHyperplane(currDir,xConstr,yConstr,valueFn,
697  numStates,newDirections,newBounds,model,
698  addDirection);
699 
700  newQuadrant = false;
701  // cout << "Maximum RHS " << tmp << endl;
702  if (currDir[1]>0)
703  {
704  quadrant=SG_NORTHWEST;
705  newQuadrant = true;
706 
707  // Last direction must have been due west... update
708  // threats for player 1
709  for (int s = 0; s < numStates; s++)
710  newThreatTuple[s][0]=-newBounds.back()[s];
711 
712  // cout << "Starting northwest..." << endl;
713  }
714 
715  break;
716 
717  case SG_NORTHWEST:
718  // Increasing currDirVar[0] and increasing currDirVar[1]
719  tmp = yConstr.get(GRB_DoubleAttr_SARHSUp);
720  currDir[1] = tmp;
721 
722  addBoundingHyperplane(currDir,xConstr,yConstr,valueFn,
723  numStates,newDirections,newBounds,model,
724  addDirection);
725 
726  newQuadrant = false;
727 
728  // cout << "Maximum RHS " << tmp << endl;
729  if (currDir[0]>0)
730  {
731  quadrant=SG_NORTHEAST;
732  newQuadrant = true;
733  // cout << "Passing north!" << endl;
734  passNorth = true;
735  }
736  break;
737 
738  } // switch
739 
740 
741  ++ steps;
742  } while (!passNorth);
743  // cout << "Done with iteration!" << endl;
744  // cout << "New threat tuple: " << newThreatTuple << endl;
745 
746  delete [] valueFn;
747  delete [] contVals;
748  delete [] valueFunSlacks;
749  delete [] pseudoContVals;
750  delete [] recursiveContValSlacks;
751  delete [] APSContValSlacks;
752  delete [] APSContValVar;
753  delete [] feasMult;
754  delete [] ICMult;
755  delete [] currDirVar;
756 
757  double dist = 1.0;
758  if (directions.size() == newDirections.size())
759  {
760  dist = 0.0;
761  list<SGPoint>::const_iterator dir;
762  list< vector<double> >::const_iterator bnd;
763  list<SGPoint>::const_iterator newDir;
764  list< vector<double> >::const_iterator newBnd;
765  for (dir = directions.begin(),
766  bnd = bounds.begin(),
767  newDir = newDirections.begin(),
768  newBnd = newBounds.begin();
769  dir != directions.end();
770  ++dir,++bnd,++newDir,++newBnd)
771  {
772  dist = std::max(dist,SGPoint::distance(*dir,*newDir));
773 
774  for (int s = 0; s < numStates; s++)
775  dist = std::max(dist,abs((*bnd)[s]-(*newBnd)[s]));
776  }
777  } // if
778  bounds = newBounds;
779  directions = newDirections;
780 
781  // for (int s = 0; s < numStates; s++)
782  // threatTuple[s].max(newThreatTuple[s]);
783  threatTuple = newThreatTuple;
784 
785  return dist;
786 } // iterate
787 
788 void SGSolver_MaxMinMax_GRB::addBoundingHyperplane(SGPoint & currDir,
789  GRBConstr & xConstr,
790  GRBConstr & yConstr,
791  GRBVar * valueFn,
792  int numStates,
793  list<SGPoint> & newDirections,
794  list< vector<double> > & newBounds,
795  GRBModel & model,
796  const bool addDirection)
797 {
798  int roundScale = 1e7;
799 
800  currDir.normalize();
801  if (addDirection)
802  {
803  xConstr.set(GRB_DoubleAttr_RHS,currDir[0]);
804  yConstr.set(GRB_DoubleAttr_RHS,currDir[1]);
805 
806  // const double defaultLimit = model.getEnv().get(GRB_DoubleParam_IterationLimit);
807  // model.getEnv().set(GRB_DoubleParam_IterationLimit,1);
808  // model.optimize();
809  // model.getEnv().set(GRB_DoubleParam_IterationLimit,defaultLimit);
810  model.optimize();
811 
812  // currDir.roundPoint(1.0/roundScale);
813 
814  if (newDirections.size() > 2)
815  {
816  // Check if colinear with last two hyperplanes Want to know if
817  // there exist a, b such that a*(d,h)+b*(d',h')=(d'',h''). We
818  // can solve the matrix equation a*d+b*d' = d'', and then check
819  // if a*h+b*h'=h''.
820  std::list<SGPoint>::const_reverse_iterator d0 = newDirections.rbegin();
821  std::list<SGPoint>::const_reverse_iterator d1 = d0++;
822  double det = (*d0)[0]*(*d1)[1]-(*d0)[1]*(*d1)[0];
823 
824  if (det == 0 && SGPoint::distance(*d0,*d1)==0)
825  {
826  cout << "Warning: Repeated direction. " << endl
827  << (*d0) << " " << (*d1) << " " << currDir << endl;
828  }
829 
830  double a = (currDir[0]*(*d1)[1]-currDir[1]*(*d1)[0])/det;
831  double b = ((*d0)[0]*currDir[1]-(*d0)[1]*currDir[0])/det;
832 
833  std::list<vector<double> >::const_reverse_iterator h0 = newBounds.rbegin();
834  std::list<vector<double> >::const_reverse_iterator h1 = h0++;
835  double distSum = 0;
836  for (int s = 0; s < numStates; s++)
837  distSum += abs(a*(*h0)[s]+b*(*h1)[s] - valueFn[s].get(GRB_DoubleAttr_X));
838  // cout << "Checking for colinearity" << endl;
839  // cout << distSum << endl;
840  if (distSum < 1e-14)
841  {
842  // If colinear, drop the previous hyperplane/bound.
843  newBounds.pop_back();
844  newDirections.pop_back();
845  // cout << "Colinear hyperplane found." << endl;
846  }
847  }
848 
849  newDirections.push_back(currDir);
850  newBounds.push_back(vector<double>(numStates,0));
851  for (int s = 0; s < numStates; s++)
852  {
853  double tmp = valueFn[s].get(GRB_DoubleAttr_X);
854  // tmp = round(tmp*roundScale)/roundScale;
855  assert(!isnan(tmp));
856  newBounds.back()[s] = tmp;
857  }
858  }
859 
860  // SGPoint oldDir = currDir;
861  currDir.rotateCW(-minRotation);
862  // cout << "Distance from rotation: " << setprecision(15) << SGPoint::distance(oldDir,currDir) << endl;
863  xConstr.set(GRB_DoubleAttr_RHS,currDir[0]);
864  yConstr.set(GRB_DoubleAttr_RHS,currDir[1]);
865 } // addBoundingHyperlpane
866 
867 void SGSolver_MaxMinMax_GRB::printIteration(ofstream & ofs, int numIter)
868 {
869  const int numStates = game.getNumStates();
870  ofs << numIter << " " << directions.size();
871  for (int s = 0; s < numStates; s++)
872  ofs << " " << 0;
873  ofs << endl;
874 
875  list<SGPoint>::const_iterator dir;
876  list< vector<double> >::const_iterator bnd;
877  for (dir = directions.begin(),
878  bnd = bounds.begin();
879  dir != directions.end();
880  ++dir,++bnd)
881  {
882  ofs << setprecision(12) << (*dir)[0] << " " << (*dir)[1];
883  for (int s = 0; s < numStates; s++)
884  ofs << " " << (*bnd)[s];
885  ofs << endl;
886  }
887 
888 } // printIteration
889 
890 
891 #endif
SGPoint::distance
static double distance(const SGPoint &p0, const SGPoint &p1)
Calculates distance between p0 and p1 in the sup-norm.
Definition: sgpoint.cpp:96
SGSolver_MaxMinMax_GRB::getNumDirections
int getNumDirections() const
Return numDirections.
Definition: sgsolver_maxminmax_grb.hpp:138
SGSolver_MaxMinMax_GRB::getBounds
const list< vector< double > > & getBounds() const
Returns payoff bounds.
Definition: sgsolver_maxminmax_grb.hpp:134
SGGame
Describes a stochastic game.
Definition: sggame.hpp:40
SGSolver_MaxMinMax_GRB::iterate
double iterate(const SGSolverMode mode, int &steps)
Runs one iteration.
Definition: sgsolver_maxminmax_grb.hpp:234
SGSolver_MaxMinMax_GRB::payoffs
const vector< vector< SGPoint > > & payoffs
Game elements.
Definition: sgsolver_maxminmax_grb.hpp:70
SGSolver_MaxMinMax_GRB::eqActions
vector< vector< bool > > eqActions
Feasible actions.
Definition: sgsolver_maxminmax_grb.hpp:50
SGSolver_MaxMinMax_GRB::numDirections
int numDirections
Number of gradients.
Definition: sgsolver_maxminmax_grb.hpp:67
SGSolver_MaxMinMax_GRB::initialize
void initialize()
Initializes the solver.
Definition: sgsolver_maxminmax_grb.hpp:217
SGSolver_MaxMinMax_GRB::getGame
const SGGame & getGame() const
Returns the current game.
Definition: sgsolver_maxminmax_grb.hpp:130
sgutilities.hpp
SGPoint::rotateCW
bool rotateCW(double pi)
Rotates the point clockwise by pi radians.
Definition: sgpoint.hpp:77
std::vector< SGPoint >
Definition: sgstl.cpp:25
SGSolver_MaxMinMax_GRB::minRotation
double minRotation
Minimum rotation.
Definition: sgsolver_maxminmax_grb.hpp:44
SGGame::getEquilibriumActions
const vector< vector< bool > > & getEquilibriumActions() const
Returns a const reference to the equilibrium actions.
Definition: sggame.hpp:194
SGSolver_MaxMinMax_GRB
Class that implements a version of the max-min-max algorithm using Gurobi.
Definition: sgsolver_maxminmax_grb.hpp:38
SGSolver_MaxMinMax_GRB::game
const SGGame & game
Const reference to the game being solved.
Definition: sgsolver_maxminmax_grb.hpp:41
SGPoint::normalize
bool normalize()
Normalizes so that the norm is one.
Definition: sgpoint.cpp:82
SGPoint
A vector in .
Definition: sgpoint.hpp:35
SGSolver_MaxMinMax_GRB::directions
list< SGPoint > directions
List of gradients in which to bound the correspondence.
Definition: sgsolver_maxminmax_grb.hpp:62
SGAction_PencilSharpening::calculateMinIC
void calculateMinIC(const SGGame &game, const vector< bool > &update, const SGTuple &threatTuple)
Calculates the minimum incentive compatible continuation payoff.
Definition: sgaction_pencilsharpening.cpp:134
SGSolver_MaxMinMax_GRB::bounds
list< vector< double > > bounds
Payoff bounds.
Definition: sgsolver_maxminmax_grb.hpp:59
SGSolver_MaxMinMax_GRB::payoffBound
double payoffBound
Payoff bound.
Definition: sgsolver_maxminmax_grb.hpp:47
SGGame::getNumStates
int getNumStates() const
Returns the number of states.
Definition: sggame.hpp:179
SGSolver_MaxMinMax_GRB::getDirections
const list< SGPoint > & getDirections() const
Return directions.
Definition: sgsolver_maxminmax_grb.hpp:136
SGSolver_MaxMinMax_GRB::SGSolver_MaxMinMax_GRB
SGSolver_MaxMinMax_GRB(const SGGame &_game)
Constructor.
Definition: sgsolver_maxminmax_grb.hpp:114
SGSolver_MaxMinMax_GRB::solve
void solve()
Solve routine.
Definition: sgsolver_maxminmax_grb.hpp:164
SGTuple
Definition: sgtuple.hpp:52