Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
### Changed
- Refined the perturbation approach in `path.cc` (logit and hp solvers) to better handle paths on games
with bifurcations. (#492)
- Nash solver algorithms now return a method-specific `Result` class, rather than a generic class,
with potentially richer information about the performance of the algorithm.

## [17.0.0-beta.1] - 2026-09-08

Expand Down
20 changes: 19 additions & 1 deletion doc/pygambit.api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,6 @@ Computation of Nash equilibria
.. autosummary::
:toctree: api/

NashComputationResult
enumpure_solve
enumpure_agent_solve
enummixed_solve
Expand All @@ -343,6 +342,25 @@ Computation of Nash equilibria
gnm_solve
hp_solve

Each of these returns its own result type, documented below:

.. autosummary::
:toctree: api/

NashResultBase
EnumPureResult
EnumMixedResult
EnumPolyResult
LpResult
LcpStrategyResult
LcpBehaviorResult
LiapResult
LogitResult
SimpdivResult
IPAResult
GNMResult
HPResult


Computation of quantal response equilibria
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand Down
2 changes: 1 addition & 1 deletion doc/tutorials/01_quickstart.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@
"We can use Gambit to compute the Nash equilibria for our Prisoner's Dilemma game in a single line of code. A Nash equilibrium describes a profile of strategies, one for each player, such that each player is maximizing their payoff given the strategies the other players are adopting.\n",
"\n",
"For a two-player Normal-form game, let's use `enumpure_solve` to search for a pure-strategy Nash equilibria.\n",
"The returned object will be a `NashComputationResult`."
"The returned object will be an `EnumPureResult`."
]
},
{
Expand Down
18 changes: 9 additions & 9 deletions doc/tutorials/03_stripped_down_poker.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -278,9 +278,9 @@
"id": "e5946077",
"metadata": {},
"source": [
"The result of the calculation is returned as a `NashComputationResult` object.\n",
"The result of the calculation is returned as an `LcpBehaviorResult` object.\n",
"\n",
"The set of equilibria found is reported in `NashComputationResult.equilibria`; in this case, this is a list of `MixedBehaviorProfile`'s.\n",
"The equilibrium found is reported in `LcpBehaviorResult.equilibrium`; in this case, this is a single `MixedBehaviorProfile`.\n",
"\n",
"For one-card poker, we expect to find a single equilibrium (one `MixedBehaviorProfile`):"
]
Expand All @@ -292,8 +292,8 @@
"metadata": {},
"outputs": [],
"source": [
"print(\"Number of equilibria found:\", len(result.equilibria))\n",
"eqm = result.equilibria[0]"
"print(\"Equilibrium found:\", result.equilibrium is not None)\n",
"eqm = result.equilibrium"
]
},
{
Expand Down Expand Up @@ -650,7 +650,7 @@
"outputs": [],
"source": [
"logit_solve_result = gbt.nash.logit_solve(g, maxregret=1e-8)\n",
"len(logit_solve_result.equilibria)"
"logit_solve_result.success"
]
},
{
Expand All @@ -660,7 +660,7 @@
"metadata": {},
"outputs": [],
"source": [
"ls_eqm = logit_solve_result.equilibria[0]\n",
"ls_eqm = logit_solve_result.equilibrium\n",
"ls_eqm.max_regret()"
]
},
Expand Down Expand Up @@ -701,7 +701,7 @@
"outputs": [],
"source": [
"(\n",
" gbt.nash.logit_solve(g, maxregret=1e-4).equilibria[0]\n",
" gbt.nash.logit_solve(g, maxregret=1e-4).equilibrium\n",
" .max_regret() / (g.max_payoff - g.min_payoff)\n",
")"
]
Expand Down Expand Up @@ -755,7 +755,7 @@
"source": [
"(\n",
" gbt.nash.liap_solve(g.mixed_strategy_profile(), maxregret=1e-1)\n",
" .equilibria[0].max_regret() / (g.max_payoff - g.min_payoff)\n",
" .equilibrium.max_regret() / (g.max_payoff - g.min_payoff)\n",
")"
]
},
Expand All @@ -773,7 +773,7 @@
"id": "a892dc2b",
"metadata": {},
"outputs": [],
"source": "for label in g.get_outcomes():\n payoffs = g.get_outcome_payoffs(label)\n g.set_outcome_payoffs(label, {player: value * 2 for player, value in payoffs})\n\n(\n gbt.nash.liap_solve(g.mixed_strategy_profile(), maxregret=1e-1)\n .equilibria[0].max_regret() / (g.max_payoff - g.min_payoff)\n)"
"source": "for label in g.get_outcomes():\n payoffs = g.get_outcome_payoffs(label)\n g.set_outcome_payoffs(label, {player: value * 2 for player, value in payoffs})\n\n(\n gbt.nash.liap_solve(g.mixed_strategy_profile(), maxregret=1e-1)\n .equilibrium.max_regret() / (g.max_payoff - g.min_payoff)\n)"
},
{
"cell_type": "markdown",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@
"metadata": {},
"outputs": [],
"source": [
"candidate_eq = gbt.nash.liap_solve(start=starting_profile_double).equilibria[0]\n",
"candidate_eq = gbt.nash.liap_solve(start=starting_profile_double).equilibrium\n",
"print(candidate_eq)"
]
},
Expand Down
2 changes: 1 addition & 1 deletion doc/tutorials/advanced_tutorials/quantal_response.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
" [[0, 1.1141], [1.1141, 0]],\n",
" title=\"Ochs (1995) asymmetric matching pennies as transformed in McKelvey-Palfrey (1995)\"\n",
")\n",
"gbt.nash.logit_solve(g).equilibria[0]"
"gbt.nash.logit_solve(g).equilibrium"
]
},
{
Expand Down
12 changes: 6 additions & 6 deletions doc/tutorials/advanced_tutorials/starting_points.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
"metadata": {},
"outputs": [],
"source": [
"gbt.nash.liap_solve(centroid_start).equilibria[0]"
"gbt.nash.liap_solve(centroid_start).equilibrium"
]
},
{
Expand Down Expand Up @@ -97,7 +97,7 @@
"metadata": {},
"outputs": [],
"source": [
"gbt.nash.liap_solve(new_start).equilibria[0]"
"gbt.nash.liap_solve(new_start).equilibrium"
]
},
{
Expand Down Expand Up @@ -126,7 +126,7 @@
"metadata": {},
"outputs": [],
"source": [
"gbt.nash.liap_solve(random_start).equilibria[0]"
"gbt.nash.liap_solve(random_start).equilibrium"
]
},
{
Expand Down Expand Up @@ -187,7 +187,7 @@
"metadata": {},
"outputs": [],
"source": [
"gbt.nash.simpdiv_solve(rsp).equilibria[0]"
"gbt.nash.simpdiv_solve(rsp).equilibrium"
]
},
{
Expand All @@ -208,7 +208,7 @@
"metadata": {},
"outputs": [],
"source": [
"gbt.nash.simpdiv_solve(rsp1).equilibria[0]"
"gbt.nash.simpdiv_solve(rsp1).equilibrium"
]
},
{
Expand All @@ -229,7 +229,7 @@
"metadata": {},
"outputs": [],
"source": [
"gbt.nash.simpdiv_solve(rsp2).equilibria[0]"
"gbt.nash.simpdiv_solve(rsp2).equilibrium"
]
}
],
Expand Down
2 changes: 1 addition & 1 deletion doc/tutorials/interoperability_tutorials/openspiel.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -438,7 +438,7 @@
"outputs": [],
"source": [
"gbt_hanabi_game = gbt.catalog.generate_openspiel(\"tiny_hanabi\")\n",
"eqm = gbt.nash.lcp_solve(gbt_hanabi_game).equilibria[0]"
"eqm = gbt.nash.lcp_solve(gbt_hanabi_game).equilibrium"
]
},
{
Expand Down
2 changes: 1 addition & 1 deletion src/pygambit/cli/enummixed.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ def render(profile) -> None:
cliques=cliques,
)
if cliques:
for index, clique in enumerate(result.parameters["cliques"], start=1):
for index, clique in enumerate(result.cliques, start=1):
label = f"convex-{index}"
for profile in clique:
click.echo(render_profile_csv(profile, label, decimals or 0))
Expand Down
4 changes: 2 additions & 2 deletions src/pygambit/cli/hp.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,8 @@ def render_event(event: gbt.HPStepEvent) -> None:
if verbose:
click.echo(render_profile_csv(prior, "prior", decimals))
result = gbt.nash.hp_solve(prior, maxregret=maxregret, event_callback=render_event)
for eq in result.equilibria:
click.echo(render_profile_csv(eq, "NE", decimals))
if result.equilibrium is not None:
click.echo(render_profile_csv(result.equilibrium, "NE", decimals))


if __name__ == "__main__":
Expand Down
4 changes: 2 additions & 2 deletions src/pygambit/cli/logit.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ def stream(qre) -> None:
first_step=first_step,
max_accel=max_accel,
)
click.echo(render_profile_csv(result.equilibria[-1], "NE", decimals, fixed=False))
click.echo(render_profile_csv(result.equilibrium, "NE", decimals, fixed=False))
return

result = gbt.nash.logit_solve(
Expand All @@ -204,7 +204,7 @@ def stream(qre) -> None:
max_accel=max_accel,
event_callback=stream,
)
click.echo(render_profile_csv(result.equilibria[-1], "NE", decimals, fixed=False))
click.echo(render_profile_csv(result.equilibrium, "NE", decimals, fixed=False))


if __name__ == "__main__":
Expand Down
Loading
Loading