//
// $Source: /cvsroot/gambit/gambit/sources/tools/logit/nfgdyn.cc,v $
// $Date: 2006/11/17 19:58:22 $
// $Revision: 1.12 $
//
// DESCRIPTION:
// Computation of stable quantal response equilibria via perturbed
// best-reply dynamics
//
// This file is part of Gambit
// Copyright (c) 2005, 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 <unistd.h>
#include <math.h>
#include <iostream>
#include <fstream>
#include <iomanip>
#include "libgambit/libgambit.h"
//
// This program attempts to identify the quantal response equilibria
// which are stable under the perturbed best-reply dynamics for a given
// value of the logit precision parameter 'lambda'.
//
// The program takes a normal form game on standard input. The following
// command-line parameters modify its operation:
//
// -f #: Number of decimal places to display in outputting profiles
// (Default is 6)
// -n #: Number of random starting points to generate (default is 100)
// -l #: Value of lambda (required)
// -t #: Tolerance for stopping criterion (parameter is exponent to
// 10^(-k), default is 6).
//
int g_numDecimals = 6;
void LogitBR(const Gambit::MixedStrategyProfile<double> &p_profile, double p_lambda,
Gambit::MixedStrategyProfile<double> &p_br)
{
Gambit::Game nfg = p_profile.GetGame();
for (int pl = 1; pl <= nfg->NumPlayers(); pl++) {
Gambit::Array<double> lval(nfg->GetPlayer(pl)->NumStrategies());
double sum = 0.0;
for (int st = 1; st <= nfg->GetPlayer(pl)->NumStrategies(); st++) {
lval[st] = exp(p_lambda * p_profile.GetStrategyValue(nfg->GetPlayer(pl)->GetStrategy(st)));
sum += lval[st];
}
for (int st = 1; st <= nfg->GetPlayer(pl)->NumStrategies(); st++) {
p_br[nfg->GetPlayer(pl)->GetStrategy(st)] = lval[st] / sum;
}
}
}
void Randomize(Gambit::MixedStrategyProfile<double> &p_profile)
{
Gambit::Game nfg = p_profile.GetGame();
((Gambit::Vector<double> &) p_profile) = 0.0;
for (int pl = 1; pl <= nfg->NumPlayers(); pl++) {
double sum = 0.0;
for (int st = 1; st < nfg->GetPlayer(pl)->NumStrategies(); st++) {
p_profile[nfg->GetPlayer(pl)->GetStrategy(st)] = (1.0 - sum) * (double) rand() / (double) RAND_MAX;
sum += p_profile[nfg->GetPlayer(pl)->GetStrategy(st)];
}
p_profile[nfg->GetPlayer(pl)->GetStrategy(nfg->GetPlayer(pl)->NumStrategies())] = 1.0 - sum;
}
}
void PrintProfile(std::ostream &p_stream,
const std::string &p_label,
const Gambit::MixedStrategyProfile<double> &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;
}
double Distance(const Gambit::MixedStrategyProfile<double> &a, const Gambit::MixedStrategyProfile<double> &b)
{
double dist = 0.0;
for (int i = 1; i <= a.Length(); i++) {
if (fabs(a[i] - b[i]) > dist) {
dist = fabs(a[i] - b[i]);
}
}
return dist;
}
bool ReadProfile(std::istream &p_stream, Gambit::Array<double> &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;
}
int main(int argc, char *argv[])
{
int stopAfter = 100;
double lambda;
bool lambdaSpec = false;
double tol = 1.0e-6;
std::string startFile = "";
int c;
while ((c = getopt(argc, argv, "f:n:l:t:p:")) != -1) {
switch (c) {
case 'f':
g_numDecimals = atoi(optarg);
break;
case 'n':
stopAfter = atoi(optarg);
break;
case 'l':
lambda = atof(optarg);
lambdaSpec = true;
break;
case 't':
tol = pow(10.0, (double) -atoi(optarg));
break;
case 'p':
startFile = optarg;
break;
case '?':
if (isprint(optopt)) {
std::cerr << argv[0] << ": Unknown option `-" << ((char) optopt) << "'.\n";
}
else {
std::cerr << argv[0] << ": Unknown option character `\\x" << optopt << "`.\n";
}
return 1;
default:
abort();
}
}
if (!lambdaSpec) {
std::cerr << argv[0] << ": Required parameter -l missing.\n";
exit(1);
}
Gambit::Game nfg;
try {
nfg = Gambit::ReadGame(std::cin);
}
catch (...) {
return 1;
}
if (startFile == "") {
for (int i = 1; i <= stopAfter; i++) {
Gambit::MixedStrategyProfile<double> profile(nfg);
Randomize(profile);
PrintProfile(std::cout, "start", profile);
Gambit::MixedStrategyProfile<double> br(nfg);
double c_delta = .001;
do {
LogitBR(profile, lambda, br);
(Gambit::Vector<double> &) profile =
(((Gambit::Vector<double> &) profile) * (1.0 - c_delta) +
((Gambit::Vector<double> &) br) * c_delta);
} while (Distance(profile, br) > tol);
PrintProfile(std::cout, "QRE", profile);
}
}
else {
Gambit::Array<double> x(nfg->MixedProfileLength() + 1);
std::ifstream startData(startFile.c_str());
ReadProfile(startData, x);
Gambit::MixedStrategyProfile<double> profile(nfg);
for (int i = 1; i <= profile.Length(); i++) {
profile[i] = x[i+1];
if (profile[i] == 0.0) {
profile[i] = 0.0001;
}
}
lambda = x[1];
double c_delta = .001;
Gambit::MixedStrategyProfile<double> br(nfg);
do {
PrintProfile(std::cout, "step", profile);
LogitBR(profile, lambda, br);
(Gambit::Vector<double> &) profile =
(((Gambit::Vector<double> &) profile) * (1.0 - c_delta) +
((Gambit::Vector<double> &) br) * c_delta);
} while (Distance(profile, br) > tol);
PrintProfile(std::cout, "QRE", profile);
}
}
syntax highlighted by Code2HTML, v. 0.9.1