Skip to content

Commit

Permalink
Implement MixedBehaviorProfile.realiz_prob to return node realization…
Browse files Browse the repository at this point in the history
… probabilities.
  • Loading branch information
Konstantinos Varsos authored and tturocy committed Nov 2, 2023
1 parent 98b8a06 commit 4467bf0
Show file tree
Hide file tree
Showing 7 changed files with 95 additions and 50 deletions.
4 changes: 2 additions & 2 deletions src/games/behav.h
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ template <class T> class MixedBehaviorProfile : public DVector<T> {
T GetLiapValue(bool p_definedOnly = false) const;

const T &GetRealizProb(const GameNode &node) const;
T GetRealizProb(const GameInfoset &iset) const;
T GetInfosetProb(const GameInfoset &iset) const;
const T &GetBeliefProb(const GameNode &node) const;
Vector<T> GetPayoff(const GameNode &node) const;
const T &GetPayoff(const GamePlayer &player, const GameNode &node) const;
Expand All @@ -195,4 +195,4 @@ template <class T> class MixedBehaviorProfile : public DVector<T> {

} // end namespace Gambit

#endif // LIBGAMBIT_BEHAV_H
#endif // LIBGAMBIT_BEHAV_H
8 changes: 4 additions & 4 deletions src/games/behav.imp
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ void MixedBehaviorProfile<T>::UndefinedToCentroid()
GamePlayer player = efg->GetPlayer(pl);
for (int iset = 1; iset <= player->NumInfosets(); iset++) {
GameInfoset infoset = player->GetInfoset(iset);
if (GetRealizProb(infoset) > (T) 0) {
if (GetInfosetProb(infoset) > (T) 0) {
continue;
}
T total = (T) 0;
Expand All @@ -270,7 +270,7 @@ MixedBehaviorProfile<T> MixedBehaviorProfile<T>::Normalize() const
GamePlayer player = efg->GetPlayer(pl);
for (int iset = 1; iset <= player->NumInfosets(); iset++) {
GameInfoset infoset = player->GetInfoset(iset);
if (GetRealizProb(infoset) == (T) 0) {
if (GetInfosetProb(infoset) == (T) 0) {
continue;
}
T total = (T) 0;
Expand Down Expand Up @@ -416,7 +416,7 @@ const T &MixedBehaviorProfile<T>::GetRealizProb(const GameNode &node) const
}

template <class T>
T MixedBehaviorProfile<T>::GetRealizProb(const GameInfoset &iset) const
T MixedBehaviorProfile<T>::GetInfosetProb(const GameInfoset &iset) const
{
ComputeSolutionData();
T prob = (T) 0;
Expand Down Expand Up @@ -555,7 +555,7 @@ T MixedBehaviorProfile<T>::DiffActionValue(const GameAction &p_action,
DiffNodeValue(member->GetChild(p_action->GetNumber()), player, p_oppAction);
}

return deriv / GetRealizProb(p_action->GetInfoset());
return deriv / GetInfosetProb(p_action->GetInfoset());
}

template <class T>
Expand Down
8 changes: 4 additions & 4 deletions src/gui/analysis.cc
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ gbtAnalysisProfileList<T>::GetBeliefProb(const GameNode &p_node,
if (!p_node->GetPlayer()) return "";

try {
if (m_behavProfiles[index].GetRealizProb(p_node->GetInfoset()) > Rational(0)) {
if (m_behavProfiles[index].GetInfosetProb(p_node->GetInfoset()) > Rational(0)) {
return lexical_cast<std::string>(m_behavProfiles[index].GetBeliefProb(p_node),
m_doc->GetStyle().NumDecimals());
}
Expand Down Expand Up @@ -298,7 +298,7 @@ gbtAnalysisProfileList<T>::GetInfosetProb(const GameNode &p_node,
if (!p_node->GetPlayer()) return "";

try {
return lexical_cast<std::string>(m_behavProfiles[index].GetRealizProb(p_node->GetInfoset()),
return lexical_cast<std::string>(m_behavProfiles[index].GetInfosetProb(p_node->GetInfoset()),
m_doc->GetStyle().NumDecimals());
}
catch (IndexException &) {
Expand All @@ -315,7 +315,7 @@ gbtAnalysisProfileList<T>::GetInfosetValue(const GameNode &p_node,
if (!p_node->GetPlayer() || p_node->GetPlayer()->IsChance()) return "";

try {
if (m_behavProfiles[index].GetRealizProb(p_node->GetInfoset()) > Rational(0)) {
if (m_behavProfiles[index].GetInfosetProb(p_node->GetInfoset()) > Rational(0)) {
return lexical_cast<std::string>(m_behavProfiles[index].GetPayoff(p_node->GetInfoset()),
m_doc->GetStyle().NumDecimals());
}
Expand Down Expand Up @@ -385,7 +385,7 @@ gbtAnalysisProfileList<T>::GetActionValue(const GameNode &p_node, int p_act,
if (!p_node->GetPlayer() || p_node->GetPlayer()->IsChance()) return "";

try {
if (m_behavProfiles[index].GetRealizProb(p_node->GetInfoset()) > Rational(0)) {
if (m_behavProfiles[index].GetInfosetProb(p_node->GetInfoset()) > Rational(0)) {
return lexical_cast<std::string>(m_behavProfiles[index].GetPayoff(p_node->GetInfoset()->GetAction(p_act)),
m_doc->GetStyle().NumDecimals());
}
Expand Down
28 changes: 26 additions & 2 deletions src/pygambit/behav.pxi
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,24 @@ class MixedBehaviorProfile:
raise ValueError("action_value() is not defined for the chance player")
return self._action_value(resolved_action)

def realiz_prob(self, node: typing.Union[Node, str]):
"""Returns the probability with which an node is reached.
Parameters
----------
node : Node or str
The node to get the payoff for. If a string is passed, the
node is determined by finding the node with that label, if any.
Raises
------
MismatchError
If `node` is an `Node` from a different game.
KeyError
If `node` is a string and no node in the game has that label.
"""
return self._realiz_prob(self.game._resolve_node(node, 'realiz_prob'))

def infoset_prob(self, infoset: typing.Union[Infoset, str]):
"""Returns the probability with which an information set is reached.
Expand Down Expand Up @@ -477,8 +495,11 @@ class MixedBehaviorProfileDouble(MixedBehaviorProfile):
def _belief(self, node: Node) -> float:
return deref(self.profile).GetBeliefProb(node.node)

def _realiz_prob(self, node: Node) -> float:
return deref(self.profile).GetRealizProb(node.node)

def _infoset_prob(self, infoset: Infoset) -> float:
return deref(self.profile).GetRealizProb(infoset.infoset)
return deref(self.profile).GetInfosetProb(infoset.infoset)

def _infoset_value(self, infoset: Infoset) -> float:
return deref(self.profile).GetPayoff(infoset.infoset)
Expand Down Expand Up @@ -555,8 +576,11 @@ class MixedBehaviorProfileRational(MixedBehaviorProfile):
def _belief(self, node: Node) -> Rational:
return rat_to_py(deref(self.profile).GetBeliefProb(node.node))

def _realiz_prob(self, node: Node) -> Rational:
return rat_to_py(deref(self.profile).GetRealizProb(node.node))

def _infoset_prob(self, infoset: Infoset) -> Rational:
return rat_to_py(deref(self.profile).GetRealizProb(infoset.infoset))
return rat_to_py(deref(self.profile).GetInfosetProb(infoset.infoset))

def _infoset_value(self, infoset: Infoset) -> Rational:
return rat_to_py(deref(self.profile).GetPayoff(infoset.infoset))
Expand Down
6 changes: 4 additions & 2 deletions src/pygambit/gambit.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,8 @@ cdef extern from "games/behav.h":
double getaction "operator()"(c_GameAction) except +IndexError
double GetPayoff(int)
double GetBeliefProb(c_GameNode)
double GetRealizProb(c_GameInfoset)
double GetRealizProb(c_GameNode)
double GetInfosetProb(c_GameInfoset)
double GetPayoff(c_GameInfoset)
double GetPayoff(c_GamePlayer, c_GameNode)
double GetPayoff(c_GameAction)
Expand All @@ -308,7 +309,8 @@ cdef extern from "games/behav.h":
c_Rational getaction "operator()"(c_GameAction) except +IndexError
c_Rational GetPayoff(int)
c_Rational GetBeliefProb(c_GameNode)
c_Rational GetRealizProb(c_GameInfoset)
c_Rational GetRealizProb(c_GameNode)
c_Rational GetInfosetProb(c_GameInfoset)
c_Rational GetPayoff(c_GameInfoset)
c_Rational GetPayoff(c_GamePlayer, c_GameNode)
c_Rational GetPayoff(c_GameAction)
Expand Down
52 changes: 46 additions & 6 deletions src/pygambit/game.pxi
Original file line number Diff line number Diff line change
Expand Up @@ -672,16 +672,15 @@ class Game:
mspr[s] = Rational(v)
return mspr

def mixed_behavior_profile(self, rational=False) -> MixedBehaviorProfile:
"""Create a behavior strategy profile over the game.

The profile is initialized to uniform randomization for each player
over their actions at each information set.
def mixed_behavior_profile(self, data=None, rational=False) -> MixedBehaviorProfile:
"""Returns a behavior strategy profile `MixedBehaviorProfile` over the game,
initialized to uniform randomization for each player over his actions at each
specifying the probabilities of the strategies.

Parameters
----------
rational
If True, probabilities are represented using rational numbers; otherwise
If 'True', probabilities are represented using rational numbers; otherwise
double-precision floating point numbers are used.

Raises
Expand All @@ -693,10 +692,50 @@ class Game:
if not rational:
mbpd = MixedBehaviorProfileDouble()
mbpd.profile = make_shared[c_MixedBehaviorProfileDouble](self.game)
if data is None:
return mbpd
if len(data) != len(self.players):
raise ValueError(
"Number of elements does not match number of players"
)
for (p, d) in zip(self.players, data):
if len(p.infosets) != len(d):
raise ValueError(
f"Number of elements does not match number of "
f"infosets for {p}"
)
for (i, v) in zip(p.infosets, d):
if len(i.actions) != len(v):
raise ValueError(
f"Number of elements does not match number of "
f"actions for the infoset {i} for {p}"
)
for (a, u) in zip(i.actions, v):
mbpd[a] = float(u)
return mbpd
else:
mbpr = MixedBehaviorProfileRational()
mbpr.profile = make_shared[c_MixedBehaviorProfileRational](self.game)
if data is None:
return mbpr
if len(data) != len(self.players):
raise ValueError(
"Number of elements does not match number of players"
)
for (p, d) in zip(self.players, data):
if len(p.infosets) != len(d):
raise ValueError(
f"Number of elements does not match number of "
f"infosets for {p}"
)
for (i, v) in zip(p.infosets, d):
if len(i.actions) != len(v):
raise ValueError(
f"Number of elements does not match number of "
f"actions for the infoset {i} for {p}"
)
for (a, u) in zip(i.actions, v):
mbpr[a] = float(u)
return mbpr
else:
raise UndefinedOperationError(
Expand Down Expand Up @@ -1447,3 +1486,4 @@ class Game:
if len(resolved_strategy.player.strategies) == 1:
raise UndefinedOperationError("Cannot delete the only strategy for a player")
resolved_strategy.strategy.deref().DeleteStrategy()

39 changes: 9 additions & 30 deletions src/pygambit/tests/test_behav.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ def setUp(self):
self.game = gbt.Game.read_game(
"test_games/mixed_behavior_game.efg"
)
self.profile_double = self.game.mixed_behavior_profile()
self.profile_rational = self.game.mixed_behavior_profile(True)
self.profile_double = self.game.mixed_behavior_profile(None, False)
self.profile_rational = self.game.mixed_behavior_profile(None, True)

self.game_with_chance = gbt.Game.read_game(
"test_games/complicated_extensive_game.efg"
)
self.profile_double_with_chance = self.game_with_chance.mixed_behavior_profile()
self.profile_rational_with_chance = self.game_with_chance.mixed_behavior_profile(True)
self.profile_double_w_chance = self.game_with_chance.mixed_behavior_profile(None, False)
self.profile_rational_w_chance = self.game_with_chance.mixed_behavior_profile(None, True)

def tearDown(self):
del self.game
Expand Down Expand Up @@ -430,6 +430,11 @@ def test_set_probabilities_player_by_string(self):
[[gbt.Rational("1/98"), gbt.Rational("97/98")]]
)

def test_realiz_prob(self):
"""Test realization probability on node."""
assert self.profile_double.realiz_prob(self.game.root) == 1
assert self.profile_rational.realiz_prob(self.game.root) == 1

def test_infoset_prob(self):
"""Test to retrieve the probability an information set is reached."""
assert (self.profile_double.infoset_prob(self.game.players[0].infosets[0]) == 1.0)
Expand Down Expand Up @@ -670,29 +675,3 @@ def test_infoset_belief(self):
self.profile_rational.belief(self.game.infosets[0].members[0]) ==
gbt.Rational(1, 1)
)

def test_payoff_with_chance_player(self):
"""Test to ensure that payoff called with the chance player raises a ValueError"""
chance_player = self.game_with_chance.players.chance
self.assertRaises(ValueError, self.profile_double_with_chance.payoff, chance_player)
self.assertRaises(ValueError, self.profile_rational_with_chance.payoff, chance_player)

def test_infoset_value_with_chance_player_infoset(self):
"""Test to ensure that infoset_value called with an infoset of the chance player
raises a ValueError
"""
chance_infoset = self.game_with_chance.players.chance.infosets[0]
self.assertRaises(ValueError, self.profile_double_with_chance.infoset_value,
chance_infoset)
self.assertRaises(ValueError, self.profile_rational_with_chance.infoset_value,
chance_infoset)

def test_action_value_with_chance_player_action(self):
"""Test to ensure that action_value called with an action of the chance player
raises a ValueError
"""
chance_action = self.game_with_chance.players.chance.infosets[0].actions[0]
self.assertRaises(ValueError, self.profile_double_with_chance.action_value,
chance_action)
self.assertRaises(ValueError, self.profile_rational_with_chance.action_value,
chance_action)

0 comments on commit 4467bf0

Please sign in to comment.