Repository navigation
Add Game.is_degenerate for two-player games - #1201
Draft
UveysAlsalehi wants to merge 7 commits into
Draft
UveysAlsalehi wants to merge 7 commits into
UveysAlsalehi wants to merge 7 commits into
Conversation
Replace scipy's floating-point linprog with exact rational LP (pycddlib/cdd.gmp) so ties are detected exactly, per maintainer guidance. Pure-strategy ties are checked directly; LPs handle supports of size >= 2. - Raise NotImplementedError for games without exactly two players - Return the witness strategy as a numpy array of Rational - Add pycddlib>=3.0 as a dependency - Check certificates in tests with exact Fraction arithmetic; add mixed-only, rational-witness and near-tie tests - List Game.is_degenerate in the API docs; document the cddlib/GMP build requirement - Install cddlib/GMP in Linux and macOS CI
…tproject#961) Restructure tests/test_degeneracy.py to follow the style of the existing suite: a parametrized table of degenerate and nondegenerate games (from SageMath, the catalog and test_games) run with and without a certificate, dataclass cases pinning unique witnesses, and tests for every exit of the method, including faked LP statuses. Import cdd inside is_degenerate so that importing pygambit does not require pycddlib, clarify the docstring on the witness's support, and update examples to StrategicGame.
pycddlib has no macOS wheels, so it is compiled from source in CI. Add Homebrew's include and library directories to the compiler and linker search paths, and build for arm64 only, since Homebrew's cddlib and GMP on the macOS runners are arm64-only.
The job installs pygambit, which builds pycddlib from source, so it needs the cddlib and GMP development libraries like the Linux test job.
Give each game in the parametrized table a pytest id instead of a trailing comment, build test games with gbt.Rational as the rest of the test suite does, and store certificate test games directly rather than through factory functions.
There was a problem hiding this comment.
🟡 Changes recommended
The mandatory dependency leaves supported Windows installations and documentation and wheel-test environments without a working installation path.
2 open issues
What changed in this PR
Adds exact degeneracy detection for two-player games to the Python API, with an optional mixed-strategy witness.
Changes:
- Implements pure-strategy checks and exhaustive rational linear-programming checks using pycddlib.
- Adds tests for degeneracy, exact witnesses, near-ties, and error handling.
- Documents the API and native dependencies, and updates Linux/macOS CI setup.
| File | Description |
|---|---|
tests/test_degeneracy.py |
Tests results, witnesses, exact arithmetic, and error paths. |
src/pygambit/game.pxi |
Implements Game.is_degenerate and documents its behavior. |
pyproject.toml |
Adds pycddlib as a required dependency. |
doc/pygambit.api.rst |
Lists the new method in the API reference. |
doc/developer.build.rst |
Documents native build prerequisites. |
.github/workflows/python.yml |
Adds Linux/macOS native dependencies and macOS build flags. |
.github/workflows/lint.yml |
Installs native prerequisites for catalog validation. |
Review effort: Balanced
| "scipy", | ||
| "pandas", | ||
| "click", | ||
| "pycddlib>=3.0", |
Comment on lines
+1125
to
+1135
| for S in itertools.combinations(range(m), k): | ||
| for T in itertools.combinations(range(n), k + 1): | ||
| strategy = tied_best_response_strategy(M2, S, T) | ||
| if strategy is not None: | ||
| if not certificate: | ||
| return True | ||
| return True, (0, strategy) | ||
|
|
||
| # Player 1 mixes over k columns; can player 0 have k + 1 best responses? | ||
| for S in itertools.combinations(range(n), k): | ||
| for T in itertools.combinations(range(m), k + 1): |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


Follows discussion #961
Description of the changes in this PR
Added$k$ for which the opponent has more than $k$ pure best responses.
Game.is_degenerate(certificate=False)to check whether a two-player game is degenerate. A game is degenerate if there exists a mixed strategy with support sizeThe function only works for two-player games; for other games it raises
NotImplementedError.If
certificateisTrue, it also returns a witness: the player and an exact mixed strategy that make the game degenerate.As in SageMath's implementation, we first check every pure strategy to see whether the opponent has more than one pure best response to it. If so, the game is degenerate.
Then, for every support size$k$ from 2 to $\min(m, n)$ , we check every pair consisting of a support of size $k$ and a candidate set of $k+1$ pure best responses for the opponent. To check whether some mixed strategy on that support makes every strategy in the candidate set a best response, we solve a linear program (LP). To solve it exactly, in rational arithmetic, I used the pycddlib library.
I tested 22 degenerate and nondegenerate games from SageMath, the Gambit catalog and some of my own. The tests cover every exit of the function.
How to review this PR
Game.is_degenerateinsrc/pygambit/game.pxi.tests/test_degeneracy.py.pycddlib>=3.0topyproject.toml,Game.is_degenerateto the API docs indoc/pygambit.api.rst,doc/developer.build.rst,Questions
This was my first time adding a dependency and changing the CI, so I am not sure I handled it correctly. My questions are:
certificate=True, the witness is returned as(player, strategy). Should it be(strategy, player), like in SageMath?SolveLP<Rational>insrc/solvers/linalg/lpsolve.h. Would you prefer using it instead of pycddlib? It would avoid the new dependency and the installation problems.Sorry for the late response. I hope this is close to what you had in mind, and I am happy to change anything. Thank you for the guidance in #961 and for the opportunity.