Skip to content

Commit 3788d4c

Browse files
tturocyrahulsavani
andauthored
Return full profiles from enumpoly_solve (#791)
Previously, for extensive games `enumpoly` returned "stub" profiles. These profiles were in principle extendable to Nash equilibria (although the check for doing so was not fully reliable). However, having incomplete profiles raised a large number of practical issues computationally and in terms of interpretation. This change clarifies the operation of this method on extensive games. Specifically, a given support of actions is equivalent to specifying a given set of terminal nodes which may have positive probability. A solution to the indifference conditions (in the sequence form) maps onto a probability distribution over terminal nodes. In order to confirm that such a probability distribution is implementable by a Nash equilibrium, the method searches over all pure-action completions of the partial profile, and returns the first such profile found. In other words, for a given set of realisation-equivalent Nash equilibria, the method returns a sample from that set. Closes #660. --------- Co-authored-by: Rahul Savani <rahul.savani@gmail.com>
1 parent 7264eff commit 3788d4c

14 files changed

Lines changed: 309 additions & 794 deletions

Makefile.am

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -406,9 +406,6 @@ gambit_enumpoly_SOURCES = \
406406
src/solvers/enumpoly/polypartial.imp \
407407
src/solvers/enumpoly/polysolver.cc \
408408
src/solvers/enumpoly/polysolver.h \
409-
src/solvers/enumpoly/polyfeasible.h \
410-
src/solvers/enumpoly/behavextend.cc \
411-
src/solvers/enumpoly/behavextend.h \
412409
src/solvers/enumpoly/efgpoly.cc \
413410
src/solvers/enumpoly/nfgpoly.cc \
414411
src/solvers/enumpoly/enumpoly.h \

doc/tools.enumpoly.rst

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,19 @@ supports which have the fewest strategies in total. For many classes
2424
of games, this will tend to lower the average time until finding one equilibrium,
2525
as well as finding the second equilibrium (if one exists).
2626

27+
For extensive games, a support of actions equates to allowing positive
28+
probabilities over a subset of terminal nodes. The indifference conditions
29+
used are those for the sequence form defined on the projection of the game
30+
to that support of actions. A solution to these equations implies a probability
31+
distribution over terminal nodes. The algorithm then searches for
32+
a profile that is a Nash equilibrium that implements that probability
33+
distribution. If there exists at least one such profile, a sample one is returned.
34+
Note that for probability distributions which assign zero probability to some terminal
35+
nodes, it is generally the case that there are (infinitely) many such profiles.
36+
Subsequent analysis of unreached information sets can yield alternative
37+
profiles that specify different choices at unreached information sets
38+
while still satisfying the Nash equilibrium conditions.
39+
2740
When the verbose switch `-v` is used, the program outputs each support
2841
as it is considered. The supports are presented as a comma-separated
2942
list of binary strings, where each entry represents one player. The

src/games/behavmixed.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -234,12 +234,12 @@ template <class T> MixedBehaviorProfile<T> MixedBehaviorProfile<T>::Normalize()
234234
template <class T> MixedBehaviorProfile<T> MixedBehaviorProfile<T>::ToFullSupport() const
235235
{
236236
CheckVersion();
237-
MixedBehaviorProfile<T> full(GetGame());
237+
MixedBehaviorProfile full(GetGame());
238238

239239
for (auto player : m_support.GetGame()->GetPlayers()) {
240240
for (auto infoset : player->GetInfosets()) {
241241
for (auto action : infoset->GetActions()) {
242-
full[action] = (m_support.Contains(action)) ? (*this)[action] : T(0);
242+
full[action] = (m_support.Contains(action)) ? (*this)[action] : T{0};
243243
}
244244
}
245245
}

src/games/behavspt.cc

Lines changed: 8 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,10 @@ void BehaviorSupportProfile::AddAction(const GameAction &p_action)
7373
if (pos == support.end() || *pos != p_action) {
7474
// Action is not in the support at the infoset; add at this location to keep sorted by number
7575
support.insert(pos, p_action);
76-
for (const auto &node : GetMembers(p_action->GetInfoset())) {
77-
ActivateSubtree(node->GetChild(p_action));
76+
for (const auto &node : p_action->GetInfoset()->GetMembers()) {
77+
if (m_nonterminalReachable[node]) {
78+
ActivateSubtree(node->GetChild(p_action));
79+
}
7880
}
7981
}
8082
}
@@ -86,25 +88,16 @@ bool BehaviorSupportProfile::RemoveAction(const GameAction &p_action)
8688
auto pos = std::find(support.begin(), support.end(), p_action);
8789
if (pos != support.end()) {
8890
support.erase(pos);
89-
for (const auto &node : GetMembers(p_action->GetInfoset())) {
90-
DeactivateSubtree(node->GetChild(p_action));
91+
for (const auto &node : p_action->GetInfoset()->GetMembers()) {
92+
if (m_nonterminalReachable[node]) {
93+
DeactivateSubtree(node->GetChild(p_action));
94+
}
9195
}
9296
return !support.empty();
9397
}
9498
return false;
9599
}
96100

97-
std::list<GameInfoset> BehaviorSupportProfile::GetInfosets(const GamePlayer &p_player) const
98-
{
99-
std::list<GameInfoset> answer;
100-
for (const auto &infoset : p_player->GetInfosets()) {
101-
if (m_infosetReachable.at(infoset)) {
102-
answer.push_back(infoset);
103-
}
104-
}
105-
return answer;
106-
}
107-
108101
bool BehaviorSupportProfile::HasReachableMembers(const GameInfoset &p_infoset) const
109102
{
110103
const auto &members = p_infoset->GetMembers();
@@ -150,17 +143,6 @@ void BehaviorSupportProfile::DeactivateSubtree(const GameNode &n)
150143
}
151144
}
152145

153-
std::list<GameNode> BehaviorSupportProfile::GetMembers(const GameInfoset &p_infoset) const
154-
{
155-
std::list<GameNode> answer;
156-
for (const auto &member : p_infoset->GetMembers()) {
157-
if (m_nonterminalReachable.at(member)) {
158-
answer.push_back(member);
159-
}
160-
}
161-
return answer;
162-
}
163-
164146
//========================================================================
165147
// BehaviorSupportProfile: Sequence form
166148
//========================================================================

src/games/behavspt.h

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -130,20 +130,6 @@ class BehaviorSupportProfile {
130130
//@{
131131
/// Can the information set be reached under this support?
132132
bool IsReachable(const GameInfoset &p_infoset) const { return m_infosetReachable.at(p_infoset); }
133-
/// Get the information sets for the player reachable under the support
134-
std::list<GameInfoset> GetInfosets(const GamePlayer &) const;
135-
/// Get the members of the information set reachable under the support
136-
std::list<GameNode> GetMembers(const GameInfoset &) const;
137-
//@}
138-
139-
/// @name Identification of dominated actions
140-
//@{
141-
/// Returns true if action 'a' is dominated by action 'b'
142-
bool Dominates(const GameAction &a, const GameAction &b, bool p_strict) const;
143-
/// Returns true if the action is dominated by some other action
144-
bool IsDominated(const GameAction &a, bool p_strict) const;
145-
/// Returns a copy of the support with dominated actions eliminated
146-
BehaviorSupportProfile Undominated(bool p_strict) const;
147133
//@}
148134

149135
class Infosets {

0 commit comments

Comments
 (0)