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 @@ -7,6 +7,8 @@
are always drawn and indicators are always drawn if an information set spans multiple levels.
- In `pygambit`, indexing the children of a node by a string inteprets the string as an action label,
not a label of a child node. In addition, indexing by an action object is now supported. (#587)
- In `pygambit`, `min_payoff` and `max_payoff` (for both games and players) now refers to payoffs in
any play of the game; previously this referred only to the set of outcomes. (#498)

### Added
- Tests for EFG Nash solvers -- `enumpoly_solve`, `lp_solve`, `lcp_solve` -- in behavior stratgegies
Expand Down
12 changes: 6 additions & 6 deletions src/games/game.h
Original file line number Diff line number Diff line change
Expand Up @@ -738,14 +738,14 @@ class GameRep : public std::enable_shared_from_this<GameRep> {

/// Returns true if the game is constant-sum
virtual bool IsConstSum() const = 0;
/// Returns the smallest payoff to any player in any outcome of the game
/// Returns the smallest payoff to any player in any play of the game
virtual Rational GetMinPayoff() const = 0;
/// Returns the smallest payoff to the player in any outcome of the game
virtual Rational GetMinPayoff(const GamePlayer &p_player) const = 0;
/// Returns the largest payoff to any player in any outcome of the game
/// Returns the smallest payoff to the player in any play of the game
virtual Rational GetPlayerMinPayoff(const GamePlayer &p_player) const = 0;
/// Returns the largest payoff to any player in any play of the game
virtual Rational GetMaxPayoff() const = 0;
/// Returns the largest payoff to the player in any outcome of the game
virtual Rational GetMaxPayoff(const GamePlayer &p_player) const = 0;
/// Returns the largest payoff to the player in any play of the game
virtual Rational GetPlayerMaxPayoff(const GamePlayer &p_player) const = 0;

/// Returns the set of terminal nodes which are descendants of node
virtual std::vector<GameNode> GetPlays(GameNode node) const { throw UndefinedException(); }
Expand Down
4 changes: 2 additions & 2 deletions src/games/gameagg.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,11 @@ class GameAGGRep : public GameRep {
/// Returns the smallest payoff to any player in any outcome of the game
Rational GetMinPayoff() const override { return Rational(aggPtr->getMinPayoff()); }
/// Returns the smallest payoff to the player in any outcome of the game
Rational GetMinPayoff(const GamePlayer &) const override { throw UndefinedException(); }
Rational GetPlayerMinPayoff(const GamePlayer &) const override { throw UndefinedException(); }
/// Returns the largest payoff to any player in any outcome of the game
Rational GetMaxPayoff() const override { return Rational(aggPtr->getMaxPayoff()); }
/// Returns the largest payoff to the player in any outcome of the game
Rational GetMaxPayoff(const GamePlayer &) const override { throw UndefinedException(); }
Rational GetPlayerMaxPayoff(const GamePlayer &) const override { throw UndefinedException(); }
//@}

/// @name Modification
Expand Down
4 changes: 2 additions & 2 deletions src/games/gamebagg.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,11 +96,11 @@ class GameBAGGRep : public GameRep {
/// Returns the smallest payoff to any player in any outcome of the game
Rational GetMinPayoff() const override { return Rational(baggPtr->getMinPayoff()); }
/// Returns the smallest payoff to the player in any outcome of the game
Rational GetMinPayoff(const GamePlayer &) const override { throw UndefinedException(); }
Rational GetPlayerMinPayoff(const GamePlayer &) const override { throw UndefinedException(); }
/// Returns the largest payoff to any player in any outcome of the game
Rational GetMaxPayoff() const override { return Rational(baggPtr->getMaxPayoff()); }
/// Returns the largest payoff to the player in any outcome of the game
Rational GetMaxPayoff(const GamePlayer &) const override { throw UndefinedException(); }
Rational GetPlayerMaxPayoff(const GamePlayer &) const override { throw UndefinedException(); }
//@}

/// @name Writing data files
Expand Down
36 changes: 8 additions & 28 deletions src/games/gameexpl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -38,39 +38,19 @@ namespace Gambit {

Rational GameExplicitRep::GetMinPayoff() const
{
return std::accumulate(
std::next(m_players.begin()), m_players.end(), GetMinPayoff(m_players.front()),
[this](const Rational &r, const GamePlayer &p) { return std::min(r, GetMinPayoff(p)); });
}

Rational GameExplicitRep::GetMinPayoff(const GamePlayer &p_player) const
{
if (m_outcomes.empty()) {
return Rational(0);
}
return std::accumulate(std::next(m_outcomes.begin()), m_outcomes.end(),
m_outcomes.front()->GetPayoff<Rational>(p_player),
[&p_player](const Rational &r, const std::shared_ptr<GameOutcomeRep> &c) {
return std::min(r, c->GetPayoff<Rational>(p_player));
return std::accumulate(std::next(m_players.begin()), m_players.end(),
GetPlayerMinPayoff(m_players.front()),
[this](const Rational &r, const GamePlayer &p) {
return std::min(r, GetPlayerMinPayoff(p));
});
}

Rational GameExplicitRep::GetMaxPayoff() const
{
return std::accumulate(
std::next(m_players.begin()), m_players.end(), GetMaxPayoff(m_players.front()),
[this](const Rational &r, const GamePlayer &p) { return std::max(r, GetMaxPayoff(p)); });
}

Rational GameExplicitRep::GetMaxPayoff(const GamePlayer &p_player) const
{
if (m_outcomes.empty()) {
return Rational(0);
}
return std::accumulate(std::next(m_outcomes.begin()), m_outcomes.end(),
m_outcomes.front()->GetPayoff<Rational>(p_player),
[&p_player](const Rational &r, const std::shared_ptr<GameOutcomeRep> &c) {
return std::max(r, c->GetPayoff<Rational>(p_player));
return std::accumulate(std::next(m_players.begin()), m_players.end(),
GetPlayerMaxPayoff(m_players.front()),
[this](const Rational &r, const GamePlayer &p) {
return std::max(r, GetPlayerMaxPayoff(p));
});
}

Expand Down
8 changes: 2 additions & 6 deletions src/games/gameexpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,10 @@ class GameExplicitRep : public GameRep {
public:
/// @name General data access
//@{
/// Returns the smallest payoff to any player in any outcome of the game
/// Returns the smallest payoff to any player in any play of the game
Rational GetMinPayoff() const override;
/// Returns the smallest payoff to the player in any outcome of the game
Rational GetMinPayoff(const GamePlayer &) const override;
/// Returns the largest payoff to any player in any outcome of the game
/// Returns the largest payoff to any player in any play of the game
Rational GetMaxPayoff() const override;
/// Returns the largest payoff to the player in any outcome of the game
Rational GetMaxPayoff(const GamePlayer &) const override;
//@}

/// @name Dimensions of the game
Expand Down
23 changes: 22 additions & 1 deletion src/games/gametable.cc
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,8 @@ bool GameTableRep::IsConstSum() const
sum += profile->GetPayoff(player);
}

for (auto iter : StrategyContingencies(std::const_pointer_cast<GameRep>(shared_from_this()))) {
for (const auto iter :
StrategyContingencies(std::const_pointer_cast<GameRep>(shared_from_this()))) {
Rational newsum(0);
for (const auto &player : m_players) {
newsum += iter->GetPayoff(player);
Expand All @@ -319,6 +320,26 @@ bool GameTableRep::IsConstSum() const
return true;
}

Rational GameTableRep::GetPlayerMinPayoff(const GamePlayer &p_player) const
{
Rational minpay = NewPureStrategyProfile()->GetPayoff(p_player);
for (const auto &profile :
StrategyContingencies(std::const_pointer_cast<GameRep>(shared_from_this()))) {
minpay = std::min(minpay, profile->GetPayoff(p_player));
}
return minpay;
}

Rational GameTableRep::GetPlayerMaxPayoff(const GamePlayer &p_player) const
{
Rational maxpay = NewPureStrategyProfile()->GetPayoff(p_player);
for (const auto &profile :
StrategyContingencies(std::const_pointer_cast<GameRep>(shared_from_this()))) {
maxpay = std::max(maxpay, profile->GetPayoff(p_player));
}
return maxpay;
}

//------------------------------------------------------------------------
// GameTableRep: Writing data files
//------------------------------------------------------------------------
Expand Down
6 changes: 6 additions & 0 deletions src/games/gametable.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,12 @@ class GameTableRep : public GameExplicitRep {
//@{
bool IsTree() const override { return false; }
bool IsConstSum() const override;

/// Returns the smallest payoff to the player in any play of the game
Rational GetPlayerMinPayoff(const GamePlayer &) const override;
/// Returns the largest payoff to the player in any play of the game
Rational GetPlayerMaxPayoff(const GamePlayer &) const override;

bool IsPerfectRecall() const override { return true; }
//@}

Expand Down
40 changes: 38 additions & 2 deletions src/games/gametree.cc
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

#include <iostream>
#include <algorithm>
#include <functional>
#include <numeric>
#include <stack>
#include <set>
Expand Down Expand Up @@ -719,10 +720,10 @@ Rational SubtreeSum(GameNode p_node)
Rational sum(0);

if (!p_node->IsTerminal()) {
auto children = p_node->GetChildren();
const auto children = p_node->GetChildren();
sum = SubtreeSum(children.front());
if (std::any_of(std::next(children.begin()), children.end(),
[sum](GameNode n) { return SubtreeSum(n) != sum; })) {
[sum](const GameNode &n) { return SubtreeSum(n) != sum; })) {
throw NotZeroSumException();
}
}
Expand All @@ -735,6 +736,29 @@ Rational SubtreeSum(GameNode p_node)
return sum;
}

Rational
AggregateSubtreePayoff(const GamePlayer &p_player, const GameNode &p_node,
std::function<Rational(const Rational &, const Rational &)> p_aggregator)
{
if (p_node->IsTerminal()) {
if (p_node->GetOutcome()) {
return p_node->GetOutcome()->GetPayoff<Rational>(p_player);
}
return Rational(0);
}
const auto &children = p_node->GetChildren();
auto subtree =
std::accumulate(std::next(children.begin()), children.end(),
AggregateSubtreePayoff(p_player, children.front(), p_aggregator),
[&p_aggregator, &p_player](const Rational &r, const GameNode &c) {
return p_aggregator(r, AggregateSubtreePayoff(p_player, c, p_aggregator));
});
if (p_node->GetOutcome()) {
return subtree + p_node->GetOutcome()->GetPayoff<Rational>(p_player);
}
return subtree;
}

} // end anonymous namespace

bool GameTreeRep::IsConstSum() const
Expand All @@ -748,6 +772,18 @@ bool GameTreeRep::IsConstSum() const
}
}

Rational GameTreeRep::GetPlayerMinPayoff(const GamePlayer &p_player) const
{
return AggregateSubtreePayoff(
p_player, m_root, [](const Rational &a, const Rational &b) { return std::min(a, b); });
}

Rational GameTreeRep::GetPlayerMaxPayoff(const GamePlayer &p_player) const
{
return AggregateSubtreePayoff(
p_player, m_root, [](const Rational &a, const Rational &b) { return std::max(a, b); });
}

bool GameTreeRep::IsPerfectRecall() const
{
if (m_infosetParents.empty() && !m_root->IsTerminal()) {
Expand Down
5 changes: 5 additions & 0 deletions src/games/gametree.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,11 @@ class GameTreeRep : public GameExplicitRep {
bool IsTree() const override { return true; }
bool IsConstSum() const override;
bool IsPerfectRecall() const override;

/// Returns the smallest payoff to the player in any play of the game
Rational GetPlayerMinPayoff(const GamePlayer &) const override;
/// Returns the largest payoff to the player in any play of the game
Rational GetPlayerMaxPayoff(const GamePlayer &) const override;
//@}

/// @name Players
Expand Down
4 changes: 2 additions & 2 deletions src/pygambit/gambit.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -290,9 +290,9 @@ cdef extern from "games/game.h":

bool IsConstSum() except +
c_Rational GetMinPayoff() except +
c_Rational GetMinPayoff(c_GamePlayer) except +
c_Rational GetPlayerMinPayoff(c_GamePlayer) except +
c_Rational GetMaxPayoff() except +
c_Rational GetMaxPayoff(c_GamePlayer) except +
c_Rational GetPlayerMaxPayoff(c_GamePlayer) except +
stdvector[c_GameNode] GetPlays(c_GameNode) except +
stdvector[c_GameNode] GetPlays(c_GameInfoset) except +
stdvector[c_GameNode] GetPlays(c_GameAction) except +
Expand Down
24 changes: 22 additions & 2 deletions src/pygambit/game.pxi
Original file line number Diff line number Diff line change
Expand Up @@ -790,12 +790,32 @@ class Game:

@property
def min_payoff(self) -> typing.Union[decimal.Decimal, Rational]:
"""The minimum payoff in the game."""
"""The minimum payoff to any player in any play of the game.

.. versionchanged:: 16.5.0
Changed from reporting minimum payoff in any (non-null) outcome to the minimum
payoff in any play of the game.

See also
--------
Game.max_payoff
Player.min_payoff
"""
return rat_to_py(self.game.deref().GetMinPayoff())

@property
def max_payoff(self) -> typing.Union[decimal.Decimal, Rational]:
"""The maximum payoff in the game."""
"""The maximum payoff to any player in any play of the game.

.. versionchanged:: 16.5.0
Changed from reporting maximum payoff in any (non-null) outcome to the maximum
payoff in any play of the game.

See also
--------
Game.min_payoff
Player.max_payoff
"""
return rat_to_py(self.game.deref().GetMaxPayoff())

def set_chance_probs(self, infoset: typing.Union[Infoset, str], probs: typing.Sequence):
Expand Down
28 changes: 24 additions & 4 deletions src/pygambit/player.pxi
Original file line number Diff line number Diff line change
Expand Up @@ -241,10 +241,30 @@ class Player:

@property
def min_payoff(self) -> Rational:
"""Returns the smallest payoff for the player in any outcome of the game."""
return rat_to_py(self.player.deref().GetGame().deref().GetMinPayoff(self.player))
"""Returns the smallest payoff for the player in any play of the game.

.. versionchanged:: 16.5.0
Changed from reporting minimum payoff in any (non-null) outcome to the minimum
payoff in any play of the game.

See also
--------
Player.max_payoff
Game.min_payoff
"""
return rat_to_py(self.player.deref().GetGame().deref().GetPlayerMinPayoff(self.player))

@property
def max_payoff(self) -> Rational:
"""Returns the largest payoff for the player in any outcome of the game."""
return rat_to_py(self.player.deref().GetGame().deref().GetMaxPayoff(self.player))
"""Returns the largest payoff for the player in any play of the game.

.. versionchanged:: 16.5.0
Changed from reporting maximum payoff in any (non-null) outcome to the maximum
payoff in any play of the game.

See also
--------
Player.min_payoff
Game.max_payoff
"""
return rat_to_py(self.player.deref().GetGame().deref().GetPlayerMaxPayoff(self.player))
Loading
Loading