Skip to content
Closed
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
1 change: 1 addition & 0 deletions build_support/catalog/catalog.am
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ CATALOG_FILES = \
catalog/conf/itcs/jakobsen2016/fig1c.efg \
catalog/conf/itcs/jakobsen2016/fig3.efg \
catalog/journals/dcg/vonstengel1999/6x6_game_with_75_eq.nfg \
catalog/journals/dcg/vonstengel1999/6x6_game_with_75_eq_small_payoffs.nfg \
catalog/journals/geb/bagwell1995.efg \
catalog/journals/geb/gilboa1997/fig1.efg \
catalog/journals/geb/gilboa1997/fig2.efg \
Expand Down
11 changes: 11 additions & 0 deletions doc/references.bib
Original file line number Diff line number Diff line change
Expand Up @@ -443,3 +443,14 @@ @techreport{vS97
note = {Technical Report 264, Departement Informatik},
category = {articles_equilibria}
}

@article{SvS06,
year = {2006},
author = {Rahul Savani and Bernhard von Stengel},
title = {Hard-to-Solve Bimatrix Games},
journal = {Econometrica},
volume = {74},
number = {2},
pages = {397--429},
category = {articles_equilibria}
}
26 changes: 13 additions & 13 deletions src/games/behavspt.cc
Original file line number Diff line number Diff line change
Expand Up @@ -223,39 +223,39 @@ size_t BehaviorSupportProfile::Sequences::size() const

BehaviorSupportProfile::Sequences::iterator BehaviorSupportProfile::Sequences::begin() const
{
return {m_support->GetSequenceMap(), false};
return {m_support->GetSequenceMap(), m_support->GetPlayers(), false};
}
BehaviorSupportProfile::Sequences::iterator BehaviorSupportProfile::Sequences::end() const
{
return {m_support->GetSequenceMap(), true};
return {m_support->GetSequenceMap(), m_support->GetPlayers(), true};
}

BehaviorSupportProfile::Sequences::iterator::iterator(
const std::shared_ptr<SequenceMap> p_sequences, bool p_end)
: m_sequences(p_sequences)
const std::shared_ptr<SequenceMap> p_sequences, const GameRep::Players &p_players, bool p_end)
: m_sequences(p_sequences), m_players(p_players)
{
if (p_end) {
m_currentPlayer = m_sequences->cend();
m_currentPlayer = m_players.end();
}
else {
m_currentPlayer = m_sequences->cbegin();
m_currentSequence = m_currentPlayer->second.cbegin();
m_currentPlayer = m_players.begin();
m_currentSequence = m_sequences->at(*m_currentPlayer).cbegin();
}
}

BehaviorSupportProfile::Sequences::iterator &
BehaviorSupportProfile::Sequences::iterator::operator++()
{
if (m_currentPlayer == m_sequences->cend()) {
if (m_currentPlayer == m_players.end()) {
return *this;
}
m_currentSequence++;
if (m_currentSequence != m_currentPlayer->second.cend()) {
if (m_currentSequence != m_sequences->at(*m_currentPlayer).cend()) {
return *this;
}
m_currentPlayer++;
if (m_currentPlayer != m_sequences->cend()) {
m_currentSequence = m_currentPlayer->second.cbegin();
++m_currentPlayer;
if (m_currentPlayer != m_players.end()) {
m_currentSequence = m_sequences->at(*m_currentPlayer).cbegin();
}
return *this;
}
Expand All @@ -265,7 +265,7 @@ bool BehaviorSupportProfile::Sequences::iterator::operator==(const iterator &it)
if (m_sequences != it.m_sequences || m_currentPlayer != it.m_currentPlayer) {
return false;
}
if (m_currentPlayer == m_sequences->end()) {
if (m_currentPlayer == m_players.end()) {
return true;
}
return (m_currentSequence == it.m_currentSequence);
Expand Down
12 changes: 10 additions & 2 deletions src/games/behavspt.h
Original file line number Diff line number Diff line change
Expand Up @@ -156,12 +156,20 @@ class BehaviorSupportProfile {

public:
class iterator {
// Sequences are visited grouped by player, in the game's canonical player order
// (GameRep::Players), rather than in SequenceMap's own key order. SequenceMap is
// keyed by GamePlayer, whose ordering is based on object addresses; iterating the
// map directly would therefore make the visitation order (and hence, downstream,
// any polynomial variable numbering derived from it) depend on heap layout, which
// can differ from run to run.
const std::shared_ptr<SequenceMap> m_sequences;
SequenceMap::const_iterator m_currentPlayer;
GameRep::Players m_players;
GameRep::Players::iterator m_currentPlayer;
std::vector<GameSequence>::const_iterator m_currentSequence;

public:
iterator(const std::shared_ptr<SequenceMap> p_sequences, bool p_end);
iterator(const std::shared_ptr<SequenceMap> p_sequences, const GameRep::Players &p_players,
bool p_end);

GameSequence operator*() const { return *m_currentSequence; }
GameSequence operator->() const { return *m_currentSequence; }
Expand Down
11 changes: 11 additions & 0 deletions src/solvers/enumpoly/polysolver.cc
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ bool PolynomialSystemSolver::NewtonRootInRectangle(const Rectangle<double> &r,
newpoint = NewtonStep(point);
}
catch (SingularMatrixException &) {
bool perturbed = false;
for (int i = 1; i <= point.size(); i++) {
Vector<double> perturbed_point(point);
if (r.Side(i).UpperBound() > point[i]) {
Expand All @@ -82,11 +83,21 @@ bool PolynomialSystemSolver::NewtonRootInRectangle(const Rectangle<double> &r,
}
try {
newpoint = point + (NewtonStep(perturbed_point) - perturbed_point);
perturbed = true;
break;
}
catch (SingularMatrixException &) {
}
}
if (!perturbed) {
// The Jacobian is singular at this point, and remains singular under every
// single-coordinate perturbation tried. This is a local degeneracy of the
// search at this point/rectangle, not evidence that the system of equations
// for the support as a whole is degenerate -- so we treat it the same as any
// other failure to confirm a root here, leaving it to the caller to subdivide
// the rectangle further.
return false;
}
}

if (!r.SameCenterDoubleLengths().Contains(newpoint)) {
Expand Down
66 changes: 44 additions & 22 deletions tests/games.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,20 @@
UNICODE_LABELS = ["é", "naïve", "日本語", "😀"]
VALID_LABELS = ["x", "a b", "a b", "a b c", *UNICODE_LABELS]
INVALID_LABELS = [
" x", "x ", " ", "a b", "a\tb", "a\nb", "a\x01b", "a\x7fb", "a…b",
" x", "x ", " ", "a  b", "a
b",
" x",
"x ",
" ",
"a b",
"a\tb",
"a\nb",
"a\x01b",
"a\x7fb",
"a…b",
" x",
"x ",
" ",
"a  b",
"a
b",
]


Expand All @@ -43,7 +55,7 @@ def read_from_file(fn: str) -> gbt.Game:
raise ValueError(f"Unknown file extension in {fn}")


def create_efg_corresponding_to_bimatrix_game(
def create_efg_corresponding_to_bimatrix_game_as_ndarrays(
A: np.ndarray, B: np.ndarray, title: str
) -> gbt.Game:
"""
Expand All @@ -61,11 +73,27 @@ def create_efg_corresponding_to_bimatrix_game(
for i, j in itertools.product(range(m), range(n)):
g.set_outcome(
g.root.children[str(i)].children[str(j)],
g.add_outcome(f"({i},{j})", [A[i, j], B[i, j]])
g.add_outcome(f"({i},{j})", [A[i, j], B[i, j]]),
)
return g


def create_efg_corresponding_to_bimatrix_game(g: gbt.Game) -> gbt.Game:
"""
There is no direct pygambit method to create an EFG from a stategic-form game.
Here we create an EFG corresponding to a bimatrix game, given as a Gambit game.
"""
assert len(g.players) == 2
A, B = g.to_arrays()
title = f"EFG for: {g.title}"
return create_efg_corresponding_to_bimatrix_game_as_ndarrays(A, B, title)


def create_efg_corresponding_to_catalog_bimatrix_game(slug: str) -> gbt.Game:
g = gbt.catalog.load(slug)
return create_efg_corresponding_to_bimatrix_game(g)


################################################################################################
# Extensive-form games (efg)

Expand All @@ -85,7 +113,7 @@ def create_2x2_zero_sum_efg(variant: None | str = None) -> gbt.Game:

A = np.eye(2)
B = -A
g = create_efg_corresponding_to_bimatrix_game(A, B, title)
g = create_efg_corresponding_to_bimatrix_game_as_ndarrays(A, B, title)

if variant == "missing term outcome":
g.delete_outcome(g.root.children["0"].children["1"].outcome)
Expand Down Expand Up @@ -406,32 +434,24 @@ def create_one_shot_trust_efg(unique_NE_variant: bool = False) -> gbt.Game:
)
g.append_move(g.root, "Buyer", ["Trust", "Not trust"])
g.append_move(g.root.children["Trust"], "Seller", ["Honor", "Abuse"])
g.set_outcome(
g.root.children["Trust"].children["Honor"],
g.add_outcome("Trustworthy", [1, 1])
)
g.set_outcome(g.root.children["Trust"].children["Honor"], g.add_outcome("Trustworthy", [1, 1]))
if unique_NE_variant:
g.set_outcome(
g.root.children["Trust"].children["Abuse"],
g.add_outcome("Untrustworthy", ["1/2", 2])
g.root.children["Trust"].children["Abuse"], g.add_outcome("Untrustworthy", ["1/2", 2])
)
else:
g.set_outcome(
g.root.children["Trust"].children["Abuse"],
g.add_outcome("Untrustworthy", [-1, 2])
)
g.set_outcome(
g.root.children["Not trust"],
g.add_outcome("Opt-out", [0, 0])
g.root.children["Trust"].children["Abuse"], g.add_outcome("Untrustworthy", [-1, 2])
)
g.set_outcome(g.root.children["Not trust"], g.add_outcome("Opt-out", [0, 0]))
return g


def create_EFG_for_nxn_bimatrix_coordination_game(n: int) -> gbt.Game:
A = np.eye(n, dtype=int)
B = A
title = f"{n}x{n} coordination game, {2**n - 1} equilibria"
return create_efg_corresponding_to_bimatrix_game(A, B, title)
return create_efg_corresponding_to_bimatrix_game_as_ndarrays(A, B, title)


def create_EFG_for_6x6_bimatrix_with_long_LH_paths_and_unique_eq() -> gbt.Game:
Expand All @@ -456,7 +476,7 @@ def create_EFG_for_6x6_bimatrix_with_long_LH_paths_and_unique_eq() -> gbt.Game:
A = np.array(A)
B = np.array(B)
title = "6x6 Long Lemke-Howson Paths, unique eq"
return create_efg_corresponding_to_bimatrix_game(A, B, title)
return create_efg_corresponding_to_bimatrix_game_as_ndarrays(A, B, title)


class EfgFamilyForReducedStrategicFormTests(ABC):
Expand Down Expand Up @@ -686,9 +706,11 @@ def gbt_game(self):
for n in g.nodes:
if not n.is_terminal and not n.children["L"].is_terminal:
left = n.children["L"]
g.make_infoset(list(left.infoset.members) + [n.children["R"]],
left.infoset.player.label,
left.infoset.label or None)
g.make_infoset(
list(left.infoset.members) + [n.children["R"]],
left.infoset.player.label,
left.infoset.label or None,
)
return g

def reduced_strategic_form(self):
Expand Down
Loading
Loading