-
Notifications
You must be signed in to change notification settings - Fork 151
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Remove integrated lrslib and call lrsnash as external tool.
This removes the very dated implementation of `lrslib` previously embedded, and instead allows for calls to `lrsnash` as an external program.
- Loading branch information
Showing
20 changed files
with
166 additions
and
9,587 deletions.
There are no files selected for viewing
This file contains 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
This file contains 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
This file contains 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
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -39,6 +39,7 @@ documentation. | |
|
||
tools.enumpure | ||
tools.enummixed | ||
tools.enumpoly | ||
tools.lcp | ||
tools.lp | ||
tools.liap | ||
|
This file contains 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
This file contains 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
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
"""Interface to external standalone tool lrsnash. | ||
""" | ||
|
||
from __future__ import annotations | ||
|
||
import itertools | ||
import pathlib | ||
import subprocess | ||
import sys | ||
|
||
import pygambit as gbt | ||
import pygambit.util as util | ||
|
||
|
||
def _generate_lrs_input(game: gbt.Game) -> str: | ||
s = f"{len(game.players[0].strategies)} {len(game.players[1].strategies)}\n\n" | ||
for st1 in game.players[0].strategies: | ||
s += " ".join(str(gbt.Rational(game[st1, st2][game.players[0]])) | ||
for st2 in game.players[1].strategies) + "\n" | ||
s += "\n" | ||
for st1 in game.players[0].strategies: | ||
s += " ".join(str(gbt.Rational(game[st1, st2][game.players[1]])) | ||
for st2 in game.players[1].strategies) + "\n" | ||
return s | ||
|
||
|
||
def _parse_lrs_output(game: gbt.Game, txt: str) -> list[gbt.MixedStrategyProfileRational]: | ||
data = "\n".join([x for x in txt.splitlines() if not x.startswith("*")]).strip() | ||
eqa = [] | ||
for component in data.split("\n\n"): | ||
profiles = {"1": [], "2": []} | ||
for line in component.strip().splitlines(): | ||
profiles[line[0]].append([gbt.Rational(x) for x in line[1:].strip().split()[:-1]]) | ||
for p1, p2 in itertools.product(profiles["1"], profiles["2"]): | ||
eqa.append(game.mixed_strategy_profile([p1, p2], rational=True)) | ||
return eqa | ||
|
||
|
||
def lrsnash_solve(game: gbt.Game, | ||
lrsnash_path: pathlib.Path | str) -> list[gbt.MixedStrategyProfileRational]: | ||
if len(game.players) != 2: | ||
raise RuntimeError("Method only valid for two-player games.") | ||
with util.make_temporary(_generate_lrs_input(game)) as infn: | ||
result = subprocess.run([lrsnash_path, infn], encoding="utf-8", | ||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT) | ||
if result.returncode != 0: | ||
raise ValueError(f"PHC run failed with return code {result.returncode}") | ||
return _parse_lrs_output(game, result.stdout) | ||
|
||
|
||
def main(): | ||
game = gbt.Game.parse_game(sys.stdin.read()) | ||
eqa = lrsnash_solve(game, "./lrsnash") | ||
for eqm in eqa: | ||
print("NE," + | ||
",".join(str(eqm[strat]) for player in game.players for strat in player.strategies)) | ||
|
||
|
||
if __name__ == "__main__": | ||
main() |
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import contextlib | ||
import pathlib | ||
import tempfile | ||
import typing | ||
|
||
|
||
@contextlib.contextmanager | ||
def make_temporary(content: typing.Optional[str] = None) -> pathlib.Path: | ||
"""Context manager to create a temporary file containing `content', and | ||
provide the path to the temporary file. | ||
If `content' is none, the temporary file is created and then deleted, while | ||
returning the filename, for another process then to write to that file | ||
(under the assumption that it is extremely unlikely that another program | ||
will try to write to that same tempfile name). | ||
""" | ||
with tempfile.NamedTemporaryFile("w", delete=(content is None)) as f: | ||
if content: | ||
f.write(content) | ||
filepath = pathlib.Path(f.name) | ||
try: | ||
yield filepath | ||
finally: | ||
filepath.unlink(missing_ok=True) |
Oops, something went wrong.