Skip to content

Commit 842b0ec

Browse files
committed
Node values are well-defined!
1 parent e6e57f7 commit 842b0ec

6 files changed

Lines changed: 11 additions & 36 deletions

File tree

ChangeLog

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
### Changed
66
- `Game.comment` has been renamed to `Game.description`
77
- With behaviour profiles that reach some information sets with probability zero, beliefs, action
8-
values, infoset values, and node values are not well-defined. These functions now return a
8+
values, and infoset values are not well-defined. These functions now return a
99
`std::optional` in C++ and type or `None` in Python, where nulls indicate these quantities
1010
are not defined. (#446)
1111

src/games/behavmixed.cc

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -311,15 +311,12 @@ template <class T> Vector<T> MixedBehaviorProfile<T>::GetPayoff(const GameNode &
311311
}
312312

313313
template <class T>
314-
std::optional<T> MixedBehaviorProfile<T>::GetPayoff(const GamePlayer &p_player,
315-
const GameNode &p_node) const
314+
const T &MixedBehaviorProfile<T>::GetPayoff(const GamePlayer &p_player,
315+
const GameNode &p_node) const
316316
{
317317
CheckVersion();
318318
EnsureNodeValues();
319-
if (p_node->GetInfoset() && GetInfosetProb(p_node->GetInfoset()) == T{0}) {
320-
return std::nullopt;
321-
}
322-
return m_cache.m_nodeValues[p_node][p_player];
319+
return m_cache.m_nodeValues.at(p_node).at(p_player);
323320
}
324321

325322
template <class T>

src/games/behavmixed.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ template <class T> class MixedBehaviorProfile {
243243
T GetInfosetProb(const GameInfoset &p_infoset) const;
244244
std::optional<T> GetBeliefProb(const GameNode &node) const;
245245
Vector<T> GetPayoff(const GameNode &node) const;
246-
std::optional<T> GetPayoff(const GamePlayer &player, const GameNode &node) const;
246+
const T &GetPayoff(const GamePlayer &p_player, const GameNode &p_node) const;
247247
std::optional<T> GetPayoff(const GameInfoset &p_infoset) const;
248248
std::optional<T> GetPayoff(const GameAction &act) const;
249249
T GetActionProb(const GameAction &act) const;

src/pygambit/behavmixed.pxi

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -637,13 +637,10 @@ class MixedBehaviorProfile:
637637
return self._payoff(resolved_player)
638638

639639
def node_value(self, player: PlayerReference,
640-
node: NodeReference) -> ProfileDType | None:
640+
node: NodeReference) -> ProfileDType:
641641
"""Returns the expected payoff to `player` conditional on play reaching `node`,
642642
if all players play according to the profile.
643643

644-
If the node's information set is not reachable, in general the node value
645-
is not well-defined. In this case, the function returns `None`.
646-
647644
Parameters
648645
----------
649646
player : Player or str
@@ -663,10 +660,6 @@ class MixedBehaviorProfile:
663660
`node` is a string and no node in the game has that label.
664661
ValueError
665662
If `player` resolves to the chance player
666-
667-
See Also
668-
--------
669-
MixedBehaviorProfile.infoset_prob
670663
"""
671664
self._check_validity()
672665
resolved_player = self.game._resolve_player(player, "node_value")
@@ -990,11 +983,8 @@ class MixedBehaviorProfileDouble(MixedBehaviorProfile):
990983
return value.value()
991984
return None
992985

993-
def _node_value(self, player: Player, node: Node) -> float | None:
994-
cdef optional[double] value = deref(self.profile).GetPayoff(player.player, node.node)
995-
if value.has_value():
996-
return value.value()
997-
return None
986+
def _node_value(self, player: Player, node: Node) -> float:
987+
return deref(self.profile).GetPayoff(player.player, node.node)
998988

999989
def _action_value(self, action: Action) -> float | None:
1000990
cdef optional[double] value = deref(self.profile).GetPayoff(action.action)
@@ -1104,11 +1094,8 @@ class MixedBehaviorProfileRational(MixedBehaviorProfile):
11041094
return rat_to_py(value.value())
11051095
return None
11061096

1107-
def _node_value(self, player: Player, node: Node) -> Rational | None:
1108-
cdef optional[c_Rational] value = deref(self.profile).GetPayoff(player.player, node.node)
1109-
if value.has_value():
1110-
return rat_to_py(value.value())
1111-
return None
1097+
def _node_value(self, player: Player, node: Node) -> Rational:
1098+
return rat_to_py(deref(self.profile).GetPayoff(player.player, node.node))
11121099

11131100
def _action_value(self, action: Action) -> Rational | None:
11141101
cdef optional[c_Rational] value = deref(self.profile).GetPayoff(action.action)

src/pygambit/gambit.pxd

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,7 @@ cdef extern from "games/behavmixed.h" namespace "Gambit":
367367
T GetRealizProb(c_GameNode) except +
368368
T GetInfosetProb(c_GameInfoset) except +
369369
optional[T] GetPayoff(c_GameInfoset) except +
370-
optional[T] GetPayoff(c_GamePlayer, c_GameNode) except +
370+
T GetPayoff(c_GamePlayer, c_GameNode) except +
371371
optional[T] GetPayoff(c_GameAction) except +
372372
T GetRegret(c_GameAction) except +
373373
T GetRegret(c_GameInfoset) except +

tests/test_behav.py

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2117,15 +2117,6 @@ def test_undefined_belief():
21172117
assert profile.belief(node) is None
21182118

21192119

2120-
def test_undefined_node_value():
2121-
"""Test that undefined node values return `None`."""
2122-
game = gbt.catalog.load("selten1975/fig1")
2123-
node = game.players[2].infosets[0].members[0]
2124-
for rat in [False, True]:
2125-
profile = game.mixed_behavior_profile([[[1, 0]], [[1, 0]], [[1, 0]]], rational=rat)
2126-
assert profile.node_value(node.player, node) is None
2127-
2128-
21292120
def test_undefined_infoset_value():
21302121
"""Test that undefined infoset values return `None`."""
21312122
game = gbt.catalog.load("selten1975/fig1")

0 commit comments

Comments
 (0)