// // $Source: /cvsroot/gambit/gambit/sources/tools/liap/nfgliap.cc,v $ // $Date: 2006/02/08 21:12:04 $ // $Revision: 1.14 $ // // DESCRIPTION: // Compute Nash equilibria by minimizing Liapunov function // // 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 #include #include #include "libgambit/libgambit.h" #include "funcmin.h" extern int m_stopAfter; extern int m_numTries; extern int m_maxits1; extern int m_maxitsN; extern double m_tol1; extern double m_tolN; extern std::string startFile; extern bool useRandom; extern int g_numDecimals; extern bool verbose; //--------------------------------------------------------------------- // class NFLiapFunc //--------------------------------------------------------------------- class NFLiapFunc : public gC1Function { private: mutable long _nevals; Gambit::Game _nfg; mutable Gambit::MixedStrategyProfile _p; double Value(const Gambit::Vector &) const; bool Gradient(const Gambit::Vector &, Gambit::Vector &) const; double LiapDerivValue(int, int, const Gambit::MixedStrategyProfile &) const; public: NFLiapFunc(const Gambit::Game &, const Gambit::MixedStrategyProfile &); virtual ~NFLiapFunc(); long NumEvals(void) const { return _nevals; } }; NFLiapFunc::NFLiapFunc(const Gambit::Game &N, const Gambit::MixedStrategyProfile &start) : _nevals(0L), _nfg(N), _p(start) { } NFLiapFunc::~NFLiapFunc() { } double NFLiapFunc::LiapDerivValue(int i1, int j1, const Gambit::MixedStrategyProfile &p) const { int i, j; double x, x1, psum; x = 0.0; for (i = 1; i <= _nfg->NumPlayers(); i++) { psum = 0.0; for (j = 1; j <= p.GetSupport().NumStrategies(i); j++) { psum += p[p.GetSupport().GetStrategy(i,j)]; x1 = p.GetStrategyValue(p.GetSupport().GetStrategy(i, j)) - p.GetPayoff(i); if (i1 == i) { if (x1 > 0.0) x -= x1 * p.GetPayoffDeriv(i, p.GetSupport().GetStrategy(i1, j1)); } else { if (x1> 0.0) x += x1 * (p.GetPayoffDeriv(i, p.GetSupport().GetStrategy(i, j), p.GetSupport().GetStrategy(i1, j1)) - p.GetPayoffDeriv(i, p.GetSupport().GetStrategy(i1, j1))); } } if (i == i1) x += 100.0 * (psum - 1.0); } if (p[p.GetSupport().GetStrategy(i1, j1)] < 0.0) { x += p[p.GetSupport().GetStrategy(i1, j1)]; } return 2.0 * x; } // // This function projects a gradient into the plane of the simplex. // (Actually, it works by computing the projection of 'x' onto the // vector perpendicular to the plane, then subtracting to compute the // component parallel to the plane.) // static void Project(Gambit::Vector &x, const Gambit::Array &lengths) { int index = 1; for (int part = 1; part <= lengths.Length(); part++) { double avg = 0.0; int j; for (j = 1; j <= lengths[part]; j++, index++) { avg += x[index]; } avg /= (double) lengths[part]; index -= lengths[part]; for (j = 1; j <= lengths[part]; j++, index++) { x[index] -= avg; } } } bool NFLiapFunc::Gradient(const Gambit::Vector &v, Gambit::Vector &d) const { ((Gambit::Vector &) _p).operator=(v); int i1, j1, ii; for (i1 = 1, ii = 1; i1 <= _nfg->NumPlayers(); i1++) { for (j1 = 1; j1 <= _p.GetSupport().NumStrategies(i1); j1++) { d[ii++] = LiapDerivValue(i1, j1, _p); } } Project(d, _p.GetSupport().NumStrategies()); return true; } double NFLiapFunc::Value(const Gambit::Vector &v) const { _nevals++; ((Gambit::Vector &) _p).operator=(v); return _p.GetLiapValue(); } static void PickRandomProfile(Gambit::MixedStrategyProfile &p) { double sum, tmp; for (int pl = 1; pl <= p.GetGame()->NumPlayers(); pl++) { sum = 0.0; int st; for (st = 1; st < p.GetSupport().NumStrategies(pl); st++) { do tmp = ((double) rand()) / ((double) RAND_MAX); while (tmp + sum > 1.0); p[p.GetSupport().GetStrategy(pl, st)] = tmp; sum += tmp; } p[p.GetSupport().GetStrategy(pl, st)] = 1.0 - sum; } } void PrintProfile(std::ostream &p_stream, const std::string &p_label, const Gambit::MixedStrategyProfile &p_profile) { p_stream << p_label; for (int i = 1; i <= p_profile.Length(); i++) { p_stream.setf(std::ios::fixed); p_stream << ", " << std::setprecision(g_numDecimals) << p_profile[i]; } p_stream << std::endl; } bool ReadProfile(std::istream &p_stream, Gambit::MixedStrategyProfile &p_profile) { for (int i = 1; i <= p_profile.Length(); i++) { if (p_stream.eof() || p_stream.bad()) { return false; } p_stream >> p_profile[i]; if (i < p_profile.Length()) { char comma; p_stream >> comma; } } // Read in the rest of the line and discard std::string foo; std::getline(p_stream, foo); return true; } extern std::string startFile; void SolveStrategic(const Gambit::Game &p_game) { Gambit::List > starts; if (startFile != "") { std::ifstream startPoints(startFile.c_str()); while (!startPoints.eof() && !startPoints.bad()) { Gambit::MixedStrategyProfile start(p_game); if (ReadProfile(startPoints, start)) { starts.Append(start); } } } else { // Generate the desired number of points randomly for (int i = 1; i <= m_numTries; i++) { Gambit::MixedStrategyProfile start(p_game); PickRandomProfile(start); starts.Append(start); } } static const double ALPHA = .00000001; for (int i = 1; i <= starts.Length(); i++) { Gambit::MixedStrategyProfile p(starts[i]); if (verbose) { PrintProfile(std::cout, "start", p); } NFLiapFunc F(p.GetGame(), p); // if starting vector not interior, perturb it towards centroid int kk; for (kk = 1; kk <= p.Length() && p[kk] > ALPHA; kk++); if (kk <= p.Length()) { Gambit::MixedStrategyProfile centroid(p.GetSupport()); for (int k = 1; k <= p.Length(); k++) { p[k] = centroid[k] * ALPHA + p[k] * (1.0-ALPHA); } } gConjugatePR minimizer(p.Length()); Gambit::Vector gradient(p.Length()), dx(p.Length()); double fval; minimizer.Set(F, p, fval, gradient, .01, .0001); try { for (int iter = 1; iter <= m_maxitsN; iter++) { if (!minimizer.Iterate(F, p, fval, gradient, dx)) { break; } if (sqrt(gradient.NormSquared()) < .001) { PrintProfile(std::cout, "NE", p); break; } } if (verbose && sqrt(gradient.NormSquared()) >= .001) { PrintProfile(std::cout, "end", p); } } catch (gFuncMinException &) { } } }