Skip to content

Commit

Permalink
Check that perturbation vector for IPA is generic.
Browse files Browse the repository at this point in the history
This adds a check that the perturbation vector specified for IPA
is generic.  Specifically, it requires that the perturbation
vector has a unique maximiser for each player.  This ensures
that the game perturbed along this ray has a unique equilibrium
in pure strategies for a sufficiently large perturbation.

This also changes the perturbation vector used by the default call to IPA
to perturb the game such that the perturbed game's equilibrium is
for each player to play their first-numbered strategy.

A consequence of using non-generic perturbation vectors was that
IPA could return non-Nash output.

Closes #406.
  • Loading branch information
tturocy committed Jan 26, 2024
1 parent 8456e0f commit 85470ae
Show file tree
Hide file tree
Showing 8 changed files with 188 additions and 195 deletions.
1 change: 1 addition & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,7 @@ gtracer_SOURCES = \
src/solvers/gtracer/aggame.h \
src/solvers/gtracer/aggame.cc \
src/solvers/gtracer/gtracer.h \
src/solvers/gtracer/gtracer.cc \
src/solvers/gtracer/gnm.cc \
src/solvers/gtracer/ipa.cc

Expand Down
47 changes: 2 additions & 45 deletions src/solvers/gnm/gnm.cc
Original file line number Diff line number Diff line change
Expand Up @@ -29,49 +29,6 @@
using namespace Gambit;
using namespace Gambit::gametracer;

namespace {

std::shared_ptr<gnmgame> BuildRepresentation(const Game &p_game)
{
if (p_game->IsAgg()) {
return std::shared_ptr<gnmgame>(new aggame(dynamic_cast<GameAGGRep &>(*p_game)));
}
Rational maxPay = p_game->GetMaxPayoff();
Rational minPay = p_game->GetMinPayoff();
double scale = (maxPay > minPay) ? 1.0 / (maxPay - minPay) : 1.0;

auto players = p_game->GetPlayers();
std::vector<int> actions(players.size());
std::transform(players.cbegin(), players.cend(), actions.begin(),
[](const GamePlayer &p) { return p->NumStrategies(); });
std::shared_ptr<gnmgame> A(new nfgame(actions));

std::vector<int> profile(players.size());
for (StrategyProfileIterator iter(p_game); !iter.AtEnd(); iter++) {
std::transform(players.cbegin(), players.cend(), profile.begin(), [iter](const GamePlayer &p) {
return (*iter)->GetStrategy(p)->GetNumber() - 1;
});
for (auto player : players) {
A->setPurePayoff(player->GetNumber() - 1, profile,
scale * ((*iter)->GetPayoff(player) - minPay));
}
}
return A;
}

MixedStrategyProfile<double> ToProfile(const Game &p_game, const cvector &p_profile)
{
MixedStrategyProfile<double> msp = p_game->NewMixedStrategyProfile(0.0);
auto value = p_profile.cbegin();
for (auto strategy : p_game->GetStrategies()) {
msp[strategy] = *value;
++value;
}
return msp;
}

} // namespace

namespace Gambit {
namespace Nash {

Expand Down Expand Up @@ -114,7 +71,7 @@ List<MixedStrategyProfile<double>> NashGNMStrategySolver::Solve(const Game &p_ga
throw UndefinedException(
"Computing equilibria of games with imperfect recall is not supported.");
}
std::shared_ptr<gnmgame> A = BuildRepresentation(p_game);
std::shared_ptr<gnmgame> A = BuildGame(p_game, true);
cvector g(A->getNumActions());
g = 0.0;
*g.begin() = 1.0;
Expand All @@ -128,7 +85,7 @@ NashGNMStrategySolver::Solve(const MixedStrategyProfile<double> &p_pert) const
throw UndefinedException(
"Computing equilibria of games with imperfect recall is not supported.");
}
std::shared_ptr<gnmgame> A = BuildRepresentation(p_pert.GetGame());
std::shared_ptr<gnmgame> A = BuildGame(p_pert.GetGame(), true);
auto strategies = p_pert.GetGame()->GetStrategies();
cvector g(strategies.size());
std::transform(strategies.cbegin(), strategies.cend(), g.begin(),
Expand Down
72 changes: 72 additions & 0 deletions src/solvers/gtracer/gtracer.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
//
// This file is part of Gambit
// Copyright (c) 1994-2024, The Gambit Project (http://www.gambit-project.org)
//
// FILE: library/include/gtracer/gtracer.cc
// Top-level include file for Gametracer embedding in Gambit
// This file is based on GameTracer v0.2, which is
// Copyright (c) 2002, Ben Blum and Christian Shelton
//
// 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 <algorithm>
#include "gtracer.h"
#include "gambit.h"

namespace Gambit {
namespace gametracer {

std::shared_ptr<gnmgame> BuildGame(const Game &p_game, bool p_scaled)
{
if (p_game->IsAgg()) {
return std::shared_ptr<gnmgame>(new aggame(dynamic_cast<GameAGGRep &>(*p_game)));
}
Rational maxPay = p_game->GetMaxPayoff();
Rational minPay = p_game->GetMinPayoff();
double scale = (p_scaled && maxPay > minPay) ? 1.0 / (maxPay - minPay) : 1.0;

auto players = p_game->GetPlayers();
std::vector<int> actions(players.size());
std::transform(players.cbegin(), players.cend(), actions.begin(),
[](const GamePlayer &p) { return p->NumStrategies(); });
std::shared_ptr<gnmgame> A(new nfgame(actions));

std::vector<int> profile(players.size());
for (StrategyProfileIterator iter(p_game); !iter.AtEnd(); iter++) {
std::transform(players.cbegin(), players.cend(), profile.begin(), [iter](const GamePlayer &p) {
return (*iter)->GetStrategy(p)->GetNumber() - 1;
});
for (auto player : players) {
A->setPurePayoff(player->GetNumber() - 1, profile,
scale * ((*iter)->GetPayoff(player) - minPay));
}
}
return A;
}

MixedStrategyProfile<double> ToProfile(const Game &p_game, const cvector &p_profile)
{
MixedStrategyProfile<double> msp = p_game->NewMixedStrategyProfile(0.0);
auto value = p_profile.cbegin();
for (auto strategy : p_game->GetStrategies()) {
msp[strategy] = *value;
++value;
}
return msp;
}

} // namespace gametracer
} // namespace Gambit
13 changes: 11 additions & 2 deletions src/solvers/gtracer/gtracer.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include "cmatrix.h"
#include "nfgame.h"
#include "aggame.h"
#include "gambit.h"

namespace Gambit {
namespace gametracer {
Expand All @@ -36,8 +37,16 @@ int GNM(gnmgame &A, cvector &g, std::list<cvector> &Eq, int steps, double fuzz,
int LNMMax, double LambdaMin, bool wobble, double threshold, bool verbose,
std::string &returnMessage);

int IPA(gnmgame &A, cvector &g, cvector &zh, double alpha, double fuzz, cvector &ans,
int maxiter = -1);
int IPA(gnmgame &A, const cvector &g, cvector &zh, double alpha, double fuzz, cvector &ans,
unsigned int maxiter = 100, bool p_verbose = false);

/// @brief Build a Gametracer representation based on a Gambit game
/// @param p_game The game to convert to Gametracer's representation
/// @param p_scaled Whether to rescale the payoffs to be on [0, 1]
std::shared_ptr<gnmgame> BuildGame(const Game &p_game, bool p_scaled);

/// @brief Convert a Gametracer vector to a mixed strategy profile on a Gambit game
MixedStrategyProfile<double> ToProfile(const Game &p_game, const cvector &p_profile);

} // namespace gametracer
} // end namespace Gambit
Expand Down
Loading

0 comments on commit 85470ae

Please sign in to comment.