Skip to content

Commit

Permalink
Prefer simplicial subdivision as recommended method for some Nash.
Browse files Browse the repository at this point in the history
* Changes the "recommended method" in the graphical interface for computing
  some equilibria of a game with more than two players, from `gambit-liap`
  to `gambit-simpdiv`
* Adds an option to report equilibria found by `gambit-simpdiv` on the command line as floating-point
  probabilities rather than rationals.

The latter closes #296.
  • Loading branch information
tturocy committed Mar 28, 2024
1 parent 0acdf85 commit 1e444c5
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 13 deletions.
5 changes: 4 additions & 1 deletion ChangeLog
Expand Up @@ -22,6 +22,8 @@
- Additional regret-related functions added to `MixedBehaviorProfile` and `MixedStrategyProfile`
in both C++ and Python
- Some caching added to payoff/strategy value calculations in `MixedStrategyProfile`
- `gambit-simpdiv` now supports expressing output as floating-point with a specified number of
digits (#296)

### Changed
- Gambit now requires a compiler that supports C++17.
Expand All @@ -45,7 +47,8 @@
Creation of random mixed profiles in C++ is done with new `Game::NewRandomStrategyProfile` and
`Game::NewRandomBehaviorProfile` methods; these accept STL `Generator` objects for reproducible state.
The Python implementation is no longer just a wrapper around the C++ one.

- Graphical interface now uses simplicial subdivision as the recommended method for finding some
equilibria in games with more than two players, instead of Lyapunov function minimisation

## [16.1.1] - 2024-01-10

Expand Down
12 changes: 6 additions & 6 deletions src/gui/dlnash.cc
Expand Up @@ -49,14 +49,14 @@ gbtNashChoiceDialog::gbtNashChoiceDialog(wxWindow *p_parent, gbtGameDocument *p_

if (m_doc->GetGame()->NumPlayers() == 2) {
wxString countChoices[] = {wxT("Compute one Nash equilibrium"),
wxT("Compute as many Nash equilibria as possible"),
wxT("Compute some Nash equilibria"),
wxT("Compute all Nash equilibria")};
m_countChoice =
new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 3, countChoices);
}
else {
wxString countChoices[] = {wxT("Compute one Nash equilibrium"),
wxT("Compute as many Nash equilibria as possible")};
wxT("Compute some Nash equilibria")};
m_countChoice =
new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 2, countChoices);
}
Expand Down Expand Up @@ -213,9 +213,9 @@ gbtAnalysisOutput *gbtNashChoiceDialog::GetCommand() const
wxT("Some equilibria by solving a linear ") wxT("complementarity program ") + game);
}
else {
cmd = new gbtAnalysisProfileList<double>(m_doc, useEfg);
cmd->SetCommand(prefix + wxT("liap -d 10") + options);
cmd->SetDescription(wxT("Some equilibria by function minimization ") + game);
cmd = new gbtAnalysisProfileList<double>(m_doc, false);
cmd->SetCommand(prefix + wxT("simpdiv -d 10 -n 20 -r 100") + options);
cmd->SetDescription(wxT("Some equilibria by simplicial subdivision ") + game);
}
}
else {
Expand Down Expand Up @@ -285,7 +285,7 @@ gbtAnalysisOutput *gbtNashChoiceDialog::GetCommand() const
}
else if (method == s_simpdiv) {
cmd = new gbtAnalysisProfileList<double>(m_doc, false);
cmd->SetCommand(prefix + wxT("simpdiv") + options);
cmd->SetCommand(prefix + wxT("simpdiv -d 10 -n 20 -r 100") + options);
cmd->SetDescription(count + wxT(" by simplicial subdivision ") wxT("in strategic game"));
}
else {
Expand Down
48 changes: 42 additions & 6 deletions src/tools/simpdiv/nfgsimpdiv.cc
Expand Up @@ -66,6 +66,31 @@ List<MixedStrategyProfile<Rational>> RandomProfiles(const Game &p_game, int p_co
return profiles;
}

class MixedStrategyCSVAsFloatRenderer : public MixedStrategyRenderer<Rational> {
public:
explicit MixedStrategyCSVAsFloatRenderer(std::ostream &p_stream, int p_numDecimals = 6)
: m_stream(p_stream), m_numDecimals(p_numDecimals)
{
}
~MixedStrategyCSVAsFloatRenderer() override = default;
void Render(const MixedStrategyProfile<Rational> &p_profile,
const std::string &p_label = "NE") const override;

private:
std::ostream &m_stream;
int m_numDecimals;
};

void MixedStrategyCSVAsFloatRenderer::Render(const MixedStrategyProfile<Rational> &p_profile,
const std::string &p_label) const
{
m_stream << p_label;
for (size_t i = 1; i <= p_profile.MixedProfileLength(); i++) {
m_stream << "," << lexical_cast<std::string>(double(p_profile[i]), m_numDecimals);
}
m_stream << std::endl;
}

void PrintBanner(std::ostream &p_stream)
{
p_stream << "Compute Nash equilibria using simplicial subdivision\n";
Expand All @@ -86,6 +111,8 @@ void PrintHelp(char *progname)
std::cerr << " -r DENOM generate random starting points with denominator DENOM\n";
std::cerr << " -n COUNT number of starting points to generate (requires -r)\n";
std::cerr << " -s FILE file containing starting points\n";
std::cerr << " -d DECIMALS show profiles as floating point with DECIMALS digits\n";
std::cerr << " (default is to display rational numbers)\n";
std::cerr << " -m MAXREGRET maximum regret acceptable as a proportion of range of\n";
std::cerr << " payoffs in the game\n";
std::cerr << " -q quiet mode (suppresses banner)\n";
Expand All @@ -100,7 +127,7 @@ int main(int argc, char *argv[])
opterr = 0;
std::string startFile;
bool useRandom = false;
int randDenom = 1, gridResize = 2, stopAfter = 1;
int randDenom = 1, gridResize = 2, stopAfter = 1, decimals = 0;
bool verbose = false, quiet = false;
Rational maxregret(1, 10000000);

Expand All @@ -110,7 +137,7 @@ int main(int argc, char *argv[])
{"verbose", 0, nullptr, 'V'},
{nullptr, 0, nullptr, 0}};
int c;
while ((c = getopt_long(argc, argv, "g:hVvn:r:s:m:qS", long_options, &long_opt_index)) != -1) {
while ((c = getopt_long(argc, argv, "g:hVvn:r:s:m:d:qS", long_options, &long_opt_index)) != -1) {
switch (c) {
case 'v':
PrintBanner(std::cerr);
Expand All @@ -131,6 +158,9 @@ int main(int argc, char *argv[])
case 'm':
maxregret = lexical_cast<Rational>(std::string(optarg));
break;
case 'd':
decimals = atoi(optarg);
break;
case 's':
startFile = optarg;
break;
Expand Down Expand Up @@ -190,10 +220,16 @@ int main(int argc, char *argv[])
}
}
for (auto start : starts) {
std::shared_ptr<StrategyProfileRenderer<Rational>> renderer(
new MixedStrategyCSVRenderer<Rational>(std::cout));
NashSimpdivStrategySolver algorithm(gridResize, 0, maxregret, verbose, renderer);
algorithm.Solve(start);
if (decimals > 0) {
auto renderer = std::make_shared<MixedStrategyCSVAsFloatRenderer>(std::cout, decimals);
NashSimpdivStrategySolver algorithm(gridResize, 0, maxregret, verbose, renderer);
algorithm.Solve(start);
}
else {
auto renderer = std::make_shared<MixedStrategyCSVRenderer<Rational>>(std::cout);
NashSimpdivStrategySolver algorithm(gridResize, 0, maxregret, verbose, renderer);
algorithm.Solve(start);
}
}
return 0;
}
Expand Down

0 comments on commit 1e444c5

Please sign in to comment.