SGSolve
sgsolver_jyc.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_JYC_HPP
23 #define _SGSOLVER_JYC_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 
43 {
44 private:
46  const SGGame & game;
47 
49  GRBEnv env;
51  GRBModel model;
52 
54  vector< vector<double> > levels;
55 
58 
61 
62 public:
64  SGSolver_JYC(const SGGame & _game, int _numDirections):
65  env(),
66  model(env),
67  game(_game),
68  levels(vector< vector<double> > (game.getNumStates(),
69  vector<double>(_numDirections,0))),
70  directions(_numDirections),
71  numDirections(_numDirections)
72  {
73  env.set(GRB_IntParam_Method,2); // barrier
74  env.set(GRB_IntParam_Crossover,0); // disable crossover
75  env.set(GRB_DoubleParam_BarConvTol,1e-12);
76  env.set(GRB_DoubleParam_OptimalityTol,1e-9);
77  env.set(GRB_DoubleParam_MarkowitzTol,0.999);
78  env.set(GRB_DoubleParam_FeasibilityTol,1e-9);
79  env.set(GRB_IntParam_OutputFlag,0);
80  }
81 
83  const SGGame & getGame() const { return game; }
85  GRBEnv & getEnv() {return env; }
87  const vector< vector<double> > & getLevels() const { return levels; }
89  const vector<SGPoint> & getDirections() const { return directions; }
91  int getNumDirections() const { return numDirections; }
92 
94  void solve();
95 
97  void initialize();
98 
100  double iterate();
101 };
102 
104 {
105  double errorTol = 1e-8;
106  double error = 1.0;
107  int numIterations = 0;
108 
109  initialize();
110 
111  while (error > errorTol)
112  {
113  error = iterate();
114  cout << "Iteration: " << numIterations
115  << ", error: " << error << endl;
116 
117  numIterations++;
118  }
119 }
120 
122 {
123  const int numPlayers = game.getNumPlayers();
124 
125  // Set directions to be equally spaced
126  if (numPlayers==2)
127  {
128  for (int dir = 0; dir < numDirections; dir++)
129  {
130  double theta = 2.0*dir/numDirections*PI;
131  directions[dir] = SGPoint(std::cos(theta),
132  std::sin(theta));
133  }
134  }
135  else if (numPlayers==3)
136  {
137  int numDirsApprox = numDirections;
138  numDirections = 0;
139  directions.clear();
140 
141  double a = 4.0*PI/static_cast<double>(numDirsApprox);
142  double d = sqrt(a);
143  int Mpsi = round(PI/d);
144  double dpsi = PI/static_cast<double>(Mpsi);
145  double dphi = a/dpsi;
146 
147  // Initialize directions - approximately evenly spaced around the
148  // sphere, with three negative coordinate directions at the end.
149  vector< list<vector< double> > :: const_iterator> playerMinLvls(numPlayers);
150  for (int mpsi = 0; mpsi < Mpsi; mpsi++)
151  {
152  double psi = PI*(static_cast<double>(mpsi)+0.5)/static_cast<double>(Mpsi);
153  int Mphi = round(2.0*PI*sin(psi)/dpsi);
154 
155  for (int mphi = 0; mphi < Mphi; mphi++)
156  {
157  double phi = 2.0*PI*static_cast<double>(mphi)/static_cast<double>(Mphi);
158 
159  SGPoint newDir(numPlayers,0.0);
160  newDir[0]=sin(psi)*cos(phi);
161  newDir[1]=sin(psi)*sin(phi);
162  newDir[2]=cos(psi);
163  directions.push_back(newDir);
164 
165  numDirections++;
166  }
167  }
168 
169  levels= vector< vector<double> > (game.getNumStates(),
170  vector<double>(numDirections,0));
171  } // 3 players
172 
173  // Variables
174  GRBVar * var = model.addVars(2*numPlayers*game.getNumStates()); // One variable for each
175  // player/state in
176  // equilibrium and one
177  // variable for each
178  // player/state as the
179  // threat
180  for (int varCtr = 0; varCtr < model.get(GRB_IntAttr_NumVars); varCtr++)
181  {
182  var[varCtr].set(GRB_DoubleAttr_LB,-1e20);
183  }
184 
185  // First numPlayers*numStates variables correspond to eq payoffs, second
186  // numPlayers*numStates are threats.
187  model.update();
188 
189  // Calculate initial levels.
190  SGPoint NE, SW;
191  game.getPayoffBounds(NE,SW);
192  SGPoint NW(SW[0],NE[1]), SE(NE[0],SW[1]);
193 
194  for (int dir = 0; dir < numDirections; dir++)
195  {
196  levels[0][dir] = NE*directions[dir];
197  levels[0][dir] = std::max(levels[0][dir],
198  SE*directions[dir]);
199  levels[0][dir] = std::max(levels[0][dir],
200  SW*directions[dir]);
201  levels[0][dir] = std::max(levels[0][dir],
202  NW*directions[dir]);
203 
204  for (int state = 1; state < game.getNumStates(); state++)
205  levels[state][dir] = levels[0][dir];
206  } // direction
207 
208  // Add feasibility constraints.
209  for (int state = 0; state < game.getNumStates(); state++)
210  {
211  // Equilibrium payoffs
212  for (int dir = 0; dir < numDirections; dir++)
213  {
214  GRBLinExpr lhs = 0;
215  for (int p = 0; p < numPlayers; p++)
216  {
217  lhs += var[numPlayers*state+p]*directions[dir][p];
218  }
219  model.addConstr(lhs <= levels[state][dir]);
220  } // direction
221 
222  // Threat points
223  for (int dir = 0; dir < numDirections; dir++)
224  {
225  GRBLinExpr lhs = 0;
226  for (int p = 0; p < numPlayers; p++)
227  {
228  lhs += var[numPlayers*game.getNumStates()
229  +numPlayers*state+p]*directions[dir][p];
230  }
231  model.addConstr(lhs <= levels[state][dir]);
232  } // direction
233  } // state
234  model.update();
235 
236  delete[] var;
237 }
238 
240 {
241  const int numPlayers = game.getNumPlayers();
242 
243  vector<int> actions, deviations;
244  int deviation;
245 
246  const vector< vector< SGPoint> > & payoffs = game.getPayoffs();
247  const vector< vector< vector<double> > > & prob = game.getProbabilities();
248  const vector< vector<int> > & numActions = game.getNumActions();
249  const vector< int > & numActions_total = game.getNumActions_total();
250  const vector< vector<bool> > & eqActions = game.getEquilibriumActions();
251  int numStates = game.getNumStates();
252  double delta = game.getDelta();
253 
254  GRBVar * var = model.getVars();
255 
256  GRBConstr * constr = model.getConstrs();
257 
258  // Update feasibility constraints.
259  for (int state = 0; state < numStates; state++)
260  {
261  // Equilibrium payoffs
262  for (int dir = 0; dir < numDirections; dir++)
263  {
264  constr[2*state*numDirections+dir].set(GRB_DoubleAttr_RHS,levels[state][dir]);
265  constr[(2*state+1)*numDirections+dir].set(GRB_DoubleAttr_RHS,levels[state][dir]);
266  } // direction
267  } // state
268 
269  vector< vector<double> >
270  newLevels(numStates,
271  vector<double>(numDirections,
272  -numeric_limits<double>::max()));
273  for (int state = 0; state < numStates; state++)
274  {
275  for (int action = 0; action < numActions_total[state]; action++)
276  {
277 
278  if (!eqActions[state].empty() && !eqActions[state][action])
279  continue;
280 
281  indexToVector(action,actions,numActions[state]);
282 
283  deviations = actions;
284 
285  // Implement incentive constraints for this action
286  for (int player = 0; player < numPlayers; player ++)
287  {
288 
289  GRBLinExpr lhs = 0;
290  lhs += (1-delta)*payoffs[state][action][player];
291  for (int sp = 0; sp < numStates; sp++)
292  lhs += delta*prob[state][action][sp]*var[numPlayers*sp+player];
293 
294  for (int dev = 0; dev < numActions[state][player]; dev++)
295  {
296  if (dev == actions[player])
297  continue;
298 
299  deviations[player] = dev;
300  deviation = vectorToIndex(deviations,
301  numActions[state]);
302 
303  GRBLinExpr rhs = 0; // deviation payoff
304  rhs += (1-delta)*payoffs[state][deviation][player];
305  for (int sp = 0; sp < numStates; sp++)
306  {
307  rhs += delta*prob[state][deviation][sp]*var[numPlayers*numStates
308  +numPlayers*sp+player];
309  }
310 
311  model.addConstr(lhs >= rhs);
312  } // dev
313 
314  deviations[player] = actions[player];
315  } // player
316 
317  model.update();
318 
319  for (int dir = 0; dir < numDirections; dir++)
320  {
321  GRBLinExpr obj = 0;
322  for (int sp = 0; sp < numStates; sp++)
323  {
324  for (int player = 0; player < numPlayers; player++)
325  obj += directions[dir][player]*prob[state][action][sp]
326  *var[numPlayers*sp+player];
327 
328  }
329 
330  model.setObjective(obj);
331  model.getEnv().set(GRB_IntParam_OutputFlag,0);
332  model.set(GRB_IntAttr_ModelSense,-1); // maximize
333  model.optimize();
334 
335  if (model.get(GRB_IntAttr_Status)==GRB_OPTIMAL)
336  {
337  double val = (1-delta)*payoffs[state][action]*directions[dir]
338  + delta * model.get(GRB_DoubleAttr_ObjVal);
339 
340  if (val > newLevels[state][dir])
341  newLevels[state][dir] = val;
342  }
343  else
344  break;
345  } // direction
346 
347  // Remove IC constraints.
348  constr = model.getConstrs();
349  for (int constrCtr = numPlayers*numStates*numDirections;
350  constrCtr < model.get(GRB_IntAttr_NumConstrs);
351  constrCtr++)
352  model.remove(constr[constrCtr]);
353 
354  model.update();
355  // delete[] constr;
356  } // action
357  } // state
358 
359  delete[] var;
360 
361  double dist = 0.0;
362  for (int state = 0; state < numStates; state++)
363  {
364  for (int dir = 0; dir < numDirections; dir++)
365  dist = std::max(dist,abs(levels[state][dir]-newLevels[state][dir]));
366  } // state
367  levels = newLevels;
368 
369  return dist;
370 } // iterate
371 
372 #endif
SGGame::getPayoffBounds
void getPayoffBounds(SGPoint &UB, SGPoint &LB) const
Definition: sggame.cpp:167
SGSolver_JYC::getNumDirections
int getNumDirections() const
Return numDirections.
Definition: sgsolver_jyc.hpp:91
SGGame::getNumPlayers
int getNumPlayers() const
Returns SGGame::numPlayers, the number of players.
Definition: sggame.hpp:177
SGGame::getPayoffs
const vector< vector< SGPoint > > & getPayoffs() const
Returns a const reference to the payoffs.
Definition: sggame.hpp:191
SGSolver_JYC::solve
void solve()
Solve routine.
Definition: sgsolver_jyc.hpp:103
SGSolver_JYC::env
GRBEnv env
The Gurobi environment.
Definition: sgsolver_jyc.hpp:49
SGGame::getDelta
double getDelta() const
Returns SGGame::delta, the discount factor.
Definition: sggame.hpp:175
SGGame
Describes a stochastic game.
Definition: sggame.hpp:40
SGSolver_JYC::levels
vector< vector< double > > levels
Payoff levels.
Definition: sgsolver_jyc.hpp:54
SGGame::getNumActions_total
const vector< int > & getNumActions_total() const
Definition: sggame.hpp:185
SGSolver_JYC::model
GRBModel model
The Gurobi model.
Definition: sgsolver_jyc.hpp:51
SGGame::getNumActions
const vector< vector< int > > & getNumActions() const
Sets the argument _numActions equal to SGGame::numActions.
Definition: sggame.hpp:181
SGSolver_JYC::initialize
void initialize()
Initializes the solver.
Definition: sgsolver_jyc.hpp:121
SGSolver_JYC
Class that implements the JYC algorithm using Gurobi.
Definition: sgsolver_jyc.hpp:43
sgutilities.hpp
SGSolver_JYC::iterate
double iterate()
Runs one iteration.
Definition: sgsolver_jyc.hpp:239
SGSolver_JYC::getLevels
const vector< vector< double > > & getLevels() const
Returns payoff levels.
Definition: sgsolver_jyc.hpp:87
vectorToIndex
int vectorToIndex(const vector< int > &v, const vector< int > &sizes)
Maps a multi-index into a linear index over an array with the given dimension.
Definition: sgutilities.cpp:47
SGSolver_JYC::numDirections
int numDirections
Number of gradients.
Definition: sgsolver_jyc.hpp:60
std::vector< SGPoint >
Definition: sgstl.cpp:25
SGGame::getEquilibriumActions
const vector< vector< bool > > & getEquilibriumActions() const
Returns a const reference to the equilibrium actions.
Definition: sggame.hpp:194
SGSolver_JYC::game
const SGGame & game
Const reference to the game being solved.
Definition: sgsolver_jyc.hpp:46
SGGame::getProbabilities
const vector< vector< vector< double > > > & getProbabilities() const
Returns a const reference to probabilities.
Definition: sggame.hpp:188
SGSolver_JYC::getGame
const SGGame & getGame() const
Returns the current game.
Definition: sgsolver_jyc.hpp:83
SGPoint
A vector in .
Definition: sgpoint.hpp:35
SGSolver_JYC::getDirections
const vector< SGPoint > & getDirections() const
Return directions.
Definition: sgsolver_jyc.hpp:89
SGSolver_JYC::getEnv
GRBEnv & getEnv()
Returns the environment.
Definition: sgsolver_jyc.hpp:85
SGGame::getNumStates
int getNumStates() const
Returns the number of states.
Definition: sggame.hpp:179
SGSolver_JYC::directions
vector< SGPoint > directions
List of gradients in which to bound the correspondence.
Definition: sgsolver_jyc.hpp:57
SGSolver_JYC::SGSolver_JYC
SGSolver_JYC(const SGGame &_game, int _numDirections)
Constructor.
Definition: sgsolver_jyc.hpp:64
indexToVector
int indexToVector(int index, vector< int > &v, const vector< int > &sizes)
Maps a linear index into a multi-index over an array with the given dimension.
Definition: sgutilities.cpp:25