⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 nfgqre.cc

📁 Gambit 是一个游戏库理论软件
💻 CC
字号:
//// $Source: /home/gambit/CVS/gambit/sources/nash/nfgqre.cc,v $// $Date: 2002/09/10 14:27:43 $// $Revision: 1.10.2.1 $//// DESCRIPTION:// Computation of quantal response equilibrium correspondence for// normal form games.//// This file is part of Gambit// Copyright (c) 2002, The Gambit Project//// This program is free software; you can redistribute it and/or modify// it under the terms of the GNU General Public License as published by// the Free Software Foundation; either version 2 of the License, or// (at your option) any later version.//// This program is distributed in the hope that it will be useful,// but WITHOUT ANY WARRANTY; without even the implied warranty of// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the// GNU General Public License for more details.//// You should have received a copy of the GNU General Public License// along with this program; if not, write to the Free Software// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.//#include <math.h>#include "nfgqre.h"#include "base/gnullstatus.h"#include "math/gmath.h"#include "math/gmatrix.h"#include "math/gsmatrix.h"//=========================================================================//             QRE Correspondence Computation via Homotopy//=========================================================================//// The following code implements a homotopy approach to computing// the logistic QRE correspondence.  This implementation is a basic// Euler-Newton approach with adaptive step size, based upon the// ideas and codes presented in Allgower and Georg's// _Numerical Continuation Methods_.//inline double sqr(double x) { return x*x; }static void Givens(gMatrix<double> &b, gMatrix<double> &q,		   double &c1, double &c2, int l1, int l2, int l3){  if (fabs(c1) + fabs(c2) == 0.0) {    return;  }  double sn;  if (fabs(c2) >= fabs(c1)) {    sn = sqrt(1.0 + sqr(c1/c2)) * fabs(c2);  }  else {    sn = sqrt(1.0 + sqr(c2/c1)) * fabs(c1);  }  double s1 = c1/sn;  double s2 = c2/sn;  for (int k = 1; k <= q.NumColumns(); k++) {    double sv1 = q(l1, k);    double sv2 = q(l2, k);    q(l1, k) = s1 * sv1 + s2 * sv2;    q(l2, k) = -s2 * sv1 + s1 * sv2;  }  for (int k = l3; k <= b.NumColumns(); k++) {    double sv1 = b(l1, k);    double sv2 = b(l2, k);    b(l1, k) = s1 * sv1 + s2 * sv2;    b(l2, k) = -s2 * sv1 + s1 * sv2;  }  c1 = sn;  c2 = 0.0;}static void QRDecomp(gMatrix<double> &b, gMatrix<double> &q){  q.MakeIdent();  for (int m = 1; m <= b.NumColumns(); m++) {    for (int k = m + 1; k <= b.NumRows(); k++) {      Givens(b, q, b(m, m), b(k, m), m, k, m + 1);    }  }}static void NewtonStep(gMatrix<double> &q, gMatrix<double> &b,		       gVector<double> &u, gVector<double> &y,		       double &d){  for (int k = 1; k <= b.NumColumns(); k++) {    for (int l = 1; l <= k - 1; l++) {      y[k] -= b(l, k) * y[l];    }    y[k] /= b(k, k);  }  d = 0.0;  for (int k = 1; k <= b.NumRows(); k++) {    double s = 0.0;    for (int l = 1; l <= b.NumColumns(); l++) {      s += q(l, k) * y[l];    }    u[k] -= s;    d += s * s;  }  d = sqrt(d);}static void QreLHS(const NFSupport &p_support, const gVector<double> &p_point,		   gVector<double> &p_lhs){  MixedProfile<double> profile(p_support);  for (int i = 1; i <= profile.Length(); i++) {    profile[i] = p_point[i];  }  double lambda = p_point[p_point.Length()];    p_lhs = 0.0;  int rowno = 0;  for (int pl = 1; pl <= p_support.Game().NumPlayers(); pl++) {    rowno++;    for (int st = 1; st <= p_support.NumStrats(pl); st++) {      p_lhs[rowno] += profile(pl, st);    }    p_lhs[rowno] -= 1.0;    for (int st = 2; st <= p_support.NumStrats(pl); st++) {      p_lhs[++rowno] = log(profile(pl, st) / profile(pl, 1));      p_lhs[rowno] -= (lambda * 		       (profile.Payoff(pl, pl, st) -			profile.Payoff(pl, pl, 1)));      p_lhs[rowno] *= profile(pl, 1) * profile(pl, st);    }  }}static void QreJacobian(const NFSupport &p_support,			const gVector<double> &p_point,			gMatrix<double> &p_matrix){  const Nfg &nfg = p_support.Game();  MixedProfile<double> profile(p_support);  for (int i = 1; i <= profile.Length(); i++) {    profile[i] = p_point[i];  }  double lambda = p_point[p_point.Length()];  int rowno = 0;  for (int pl1 = 1; pl1 <= nfg.NumPlayers(); pl1++) {    rowno++;    // First, do the "sum to one" equation    int colno = 0;    for (int pl2 = 1; pl2 <= nfg.NumPlayers(); pl2++) {      for (int st2 = 1; st2 <= p_support.NumStrats(pl2); st2++) {	colno++;	if (pl1 == pl2) {	  p_matrix(colno, rowno) = 1.0;	}	else {	  p_matrix(colno, rowno) = 0.0;	}      }    }    p_matrix(p_matrix.NumRows(), rowno) = 0.0;    for (int st1 = 2; st1 <= p_support.NumStrats(pl1); st1++) {      rowno++;      int colno = 0;      for (int pl2 = 1; pl2 <= nfg.NumPlayers(); pl2++) {	for (int st2 = 1; st2 <= p_support.NumStrats(pl2); st2++) {	  colno++;	  if (pl1 == pl2) {	    if (st2 == 1) {	      p_matrix(colno, rowno) = -profile(pl1, st1);	    }	    else if (st1 == st2) {	      p_matrix(colno, rowno) = profile(pl1, 1);	    }	    else {	      p_matrix(colno, rowno) = 0.0;	    }	  }	  else {	    p_matrix(colno, rowno) = -lambda * profile(pl1, 1) * profile(pl1, st1) * (profile.Payoff(pl1, pl1, st1, pl2, st2) - profile.Payoff(pl1, pl1, 1, pl2, st2));	  }	}      }      p_matrix(p_matrix.NumRows(), rowno) = -profile(pl1, 1) * profile(pl1, st1) * (profile.Payoff(pl1, pl1, st1) - profile.Payoff(pl1, pl1, 1));    }  }}//// TracePath does the real work of tracing a branch of the correspondence//// Strategy:// This is the standard simple PC continuation method outlined in// Allgower & Georg, _Numerical Continuation Methods_.// The only modification is to deal with the machine limitation// in computing the log in the homotopy equations (in QreLHS() above).// When the probability for a strategy drops below 10^-10, it is// removed from the support, and TracePath() is called recursively from// that point.  //// In a quantal response equilibrium (for finite lambda, at least),// no strategy is ever truly played with zero probability, as this method// approximates.  However, strictly inferior strategies are played// with probability that is exponentially decreasing in lambda.  So,// approximating these as zero gives a good approximation to the true// equilibrium, at least to floating point precision.  If one is// using this method to compute Nash equilibria, this should be harmless;// if one is actually interested in the quantal response equilibria,// then this should be happening at lambda that are so large that they// will not be observed in lab data.//// The algorithm could be improved to approximate the exponential decay// of dropped strategies explicitly, but, again, these probabilities are// already going to display as zero on plots or in floating point output// with standard precisions, so it isn't a priority.//// Really, the algorithm should check that omitted strategies remain// truly inferior after they drop out; in practice, this is so unlikely// to happen that the code should only be rewritten once a reasonable// example of a strategy that drops out by this criterion winding up with// positive probability.//static void TracePath(const MixedProfile<double> &p_start,		      double p_startLambda, double p_maxLambda, double p_omega,		      gStatus &p_status,		      gList<MixedSolution> &p_solutions){  const double c_tol = 1.0e-4;     // tolerance for corrector iteration  const double c_maxDecel = 1.1;   // maximal deceleration factor  const double c_maxDist = 0.4;    // maximal distance to curve  const double c_maxContr = 0.6;   // maximal contraction rate in corrector  const double c_eta = 0.1;        // perturbation to avoid cancellation                                   // in calculating contraction rate  double h = .03;                  // initial stepsize  const double c_hmin = 1.0e-5;    // minimal stepsize  gVector<double> x(p_start.Length() + 1), u(p_start.Length() + 1);  for (int i = 1; i <= p_start.Length(); i++) {    x[i] = p_start[i];  }  x[x.Length()] = p_startLambda;  gVector<double> t(p_start.Length() + 1);  gVector<double> y(p_start.Length());  gMatrix<double> b(p_start.Length() + 1, p_start.Length());  gSquareMatrix<double> q(p_start.Length() + 1);  QreJacobian(p_start.Support(), x, b);  QRDecomp(b, q);  q.GetRow(q.NumRows(), t);    int niters = 0;  while (x[x.Length()] >= 0.0 && x[x.Length()] < p_maxLambda) {    if (niters++ % 25 == 0) {      p_status.Get();      p_status.SetProgress(x[x.Length()] / p_maxLambda,			   gText("Lambda = ") + ToText(x[x.Length()]));    }    bool accept = true;    if (fabs(h) <= c_hmin) {      return;    }    // Predictor step    for (int k = 1; k <= x.Length(); k++) {      u[k] = x[k] + h * p_omega * t[k];      if (k < x.Length() && u[k] < 0.0) {	accept = false;	break;      }    }    if (!accept) {      h *= 0.5;      continue;    }    double decel = 1.0 / c_maxDecel;  // initialize deceleration factor    QreJacobian(p_start.Support(), u, b);    QRDecomp(b, q);    int iter = 1;    double disto = 0.0;    while (true) {      double dist;      QreLHS(p_start.Support(), u, y);      NewtonStep(q, b, u, y, dist);       if (dist >= c_maxDist) {	accept = false;	break;      }      for (int i = 1; i < u.Length(); i++) {	if (u[i] < 0.0) {	  // don't go negative	  accept = false;	  break;	}      }      if (!accept) {	break;      }            decel = gmax(decel, sqrt(dist / c_maxDist) * c_maxDecel);      if (iter >= 2) {	double contr = dist / (disto + c_tol * c_eta);	if (contr > c_maxContr) {	  accept = false;	  break;	}	decel = gmax(decel, sqrt(contr / c_maxContr) * c_maxDecel);      }      if (dist <= c_tol) {	// Success; break out of iteration	break;      }      disto = dist;      iter++;    }    if (!accept) {      h /= c_maxDecel;   // PC not accepted; change stepsize and retry      if (fabs(h) <= c_hmin) {	return;      }      continue;    }    // Determine new stepsize    if (decel > c_maxDecel) {      decel = c_maxDecel;    }    h = fabs(h / decel);    // PC step was successful; update and iterate    for (int i = 1; i < x.Length(); i++) {      if (u[i] < 1.0e-10) {	// Drop this strategy from the support, then recursively call	// to continue tracing	NFSupport newSupport(p_start.Support());	int index = 1;	for (int pl = 1; pl <= newSupport.Game().NumPlayers(); pl++) {	  for (int st = 1; st <= newSupport.NumStrats(pl); st++) {	    if (index++ == i) {	      newSupport.RemoveStrategy(newSupport.GetStrategy(pl, st));	    }	  }	}	MixedProfile<double> newProfile(newSupport);	for (int j = 1; j <= newProfile.Length(); j++) {	  if (j < i) {	    newProfile[j] = u[j];	  }	  else if (j >= i) {	    newProfile[j] = u[j+1];	  }	}	TracePath(newProfile, u[u.Length()], p_maxLambda, p_omega,		  p_status, p_solutions);	return;      }      else {	x[i] = u[i];      }    }    x[x.Length()] = u[u.Length()];    MixedProfile<double> foo(p_start);    for (int i = 1; i <= foo.Length(); i++) {      foo[i] = x[i];    }    p_solutions.Append(MixedSolution(foo, "Qre[NFG]"));    p_solutions[p_solutions.Length()].SetQre(x[x.Last()], 0);        gVector<double> newT(t);    q.GetRow(q.NumRows(), newT);  // new tangent    if (t * newT < 0.0) {      // Bifurcation detected; for now, just "jump over" and continue,      // taking into account the change in orientation of the curve.      // Someday, we need to do more here! :)      p_omega = -p_omega;    }    t = newT;  }}nfgQre::nfgQre(void)  : m_maxLam(30.0), m_stepSize(0.0001), m_fullGraph(false){ }gList<MixedSolution> nfgQre::Solve(const NFSupport &p_support,				   gStatus &p_status){  gList<MixedSolution> solutions;  MixedProfile<double> start(p_support);  try {    TracePath(start, 0.0, m_maxLam, 1.0, p_status, solutions);  }  catch (...) { }  if (!m_fullGraph) {     while (solutions.Length() > 1) {      solutions.Remove(1);    }  }  return solutions;}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -