Skip to content

Commit

Permalink
probs of actions at chance node normalized after an action is deleted
Browse files Browse the repository at this point in the history
  • Loading branch information
rahulsavani committed Oct 25, 2023
1 parent d401f7c commit a8a3574
Show file tree
Hide file tree
Showing 9 changed files with 97 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/games/game.h
Original file line number Diff line number Diff line change
Expand Up @@ -847,6 +847,8 @@ class GameRep : public BaseGameRep {
//@{
/// Set the probability distribution of actions at a chance node
virtual Game SetChanceProbs(const GameInfoset &, const Array<Number> &) = 0;
/// Normalize the probability distribution of actions at a chance node
virtual Game NormalizeChanceProbs(const GameInfoset &) = 0;
//@}
};

Expand Down
1 change: 1 addition & 0 deletions src/games/gameagg.h
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ class GameAggRep : public GameRep {
/// @name Modification
//@{
Game SetChanceProbs(const GameInfoset &, const Array<Number> &) override { throw UndefinedException(); }
Game NormalizeChanceProbs(const GameInfoset &) override { throw UndefinedException(); }
//@}

/// @name Writing data files
Expand Down
1 change: 1 addition & 0 deletions src/games/gamebagg.h
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ class GameBagentRep : public GameRep {
/// @name Modification
//@{
Game SetChanceProbs(const GameInfoset &, const Array<Number> &) override { throw UndefinedException(); }
Game NormalizeChanceProbs(const GameInfoset &) override { throw UndefinedException(); }
//@}
};

Expand Down
1 change: 1 addition & 0 deletions src/games/gametable.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ class GameTableRep : public GameExplicitRep {
/// @name Modification
//@{
Game SetChanceProbs(const GameInfoset &, const Array<Number> &) override { throw UndefinedException(); }
Game NormalizeChanceProbs(const GameInfoset &) override { throw UndefinedException(); }
//@}

PureStrategyProfile NewPureStrategyProfile() const override;
Expand Down
43 changes: 43 additions & 0 deletions src/games/gametree.cc
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ void GameTreeActionRep::DeleteAction()
member->children[where]->DeleteTree();
member->children.Remove(where)->Invalidate();
}

if (m_infoset->IsChanceInfoset()) {
m_infoset->m_efg->NormalizeChanceProbs(m_infoset);
}
m_infoset->m_efg->ClearComputedValues();
m_infoset->m_efg->Canonicalize();
}
Expand Down Expand Up @@ -1054,6 +1058,45 @@ Game GameTreeRep::SetChanceProbs(const GameInfoset &p_infoset, const Array<Numbe
return this;
}

Game GameTreeRep::NormalizeChanceProbs(const GameInfoset &m_infoset) {
if (m_infoset->GetGame() != this) {
throw MismatchException();
}
if (!m_infoset->IsChanceInfoset()) {
throw UndefinedException("Action probabilities can only be normalized for chance information sets");
}
Rational sum(0);
for (int act = 1; act <= m_infoset->NumActions(); act++) {
Rational action_prob(m_infoset->GetActionProb(act));
std::cout << "action prob BEFORE NORM: " << action_prob << '\n';
sum += action_prob;
}
Array <Number> m_probs(m_infoset->NumActions());
std::cout << "COMPUTED SUM IS: " << sum << '\n';
if (sum == Rational(0)) {
// all remaining moves have prob zero; split prob 1 equally among them
for (int act = 1; act <= m_infoset->NumActions(); act++) {
std::cout << "SUM 0\n";
m_probs[act] = Rational(1, m_infoset->NumActions());
std::cout << "action prob SET UNIFORM: " << (std::string) m_probs[act] << '\n';
}
}
else {
for (int act = 1; act <= m_infoset->NumActions(); act++) {
Rational prob(m_infoset->GetActionProb(act));
m_probs[act] = prob / sum;
std::cout << "action prob SET NORMALIZE: " << (std::string) m_probs[act] << '\n';
}
}
m_infoset->GetGame()->SetChanceProbs(m_infoset, m_probs);

for (int act = 1; act <= m_infoset->NumActions(); act++) {
Rational prob(m_infoset->GetActionProb(act));
std::cout << "action prob NORMALIZED: " << prob << '\n';
}

return this;
}

//------------------------------------------------------------------------
// GameTreeRep: Factory functions
Expand Down
1 change: 1 addition & 0 deletions src/games/gametree.h
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,7 @@ class GameTreeRep : public GameExplicitRep {
/// @name Modification
//@{
Game SetChanceProbs(const GameInfoset &, const Array<Number> &) override;
Game NormalizeChanceProbs(const GameInfoset &) override;
//@}

PureStrategyProfile NewPureStrategyProfile() const override;
Expand Down
30 changes: 30 additions & 0 deletions src/pygambit/tests/test_actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,33 @@ def test_action_delete_last(game: gbt.Game):
game.delete_action(node.infoset.actions[0])
with pytest.raises(gbt.UndefinedOperationError):
game.delete_action(node.infoset.actions[0])


@pytest.mark.parametrize(
"game",
[gbt.Game.read_game("test_games/chance_root_3_moves_only_one_nonzero_prob.efg"),]
)
# gbt.Game.read_game("test_games/complicated_extensive_game.efg"),
# gbt.Game.read_game("test_games/chance_root_5_moves_no_nonterm_player_nodes.efg")]
def test_action_delete_chance(game: gbt.Game):
""" """
chance_iset = game.players.chance.infosets[0]
while len(chance_iset.actions) > 1:
check_flag = False
if chance_iset.actions[0].prob == 0:
print("CHECK FLAG IS TRUE")
# the move we are about to delete has 0 prob so nothing should change
check_flag = True
# keep copy of old probs to check that nothing changed
old_probs = [a.prob for a in chance_iset.actions]
print("old_probs", old_probs)
print("chance_iset.actions[0]).prob", chance_iset.actions[0].prob)
game.delete_action(chance_iset.actions[0])
# always check that new prob sum is 1
assert sum([a.prob for a in chance_iset.actions]) == 1
if check_flag:
new_probs = [a.prob for a in chance_iset.actions]
for p1, p2 in zip(old_probs[1:], new_probs):
assert p1 == p2
with pytest.raises(gbt.UndefinedOperationError):
game.delete_action(chance_iset.actions[0])
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
EFG 2 R "A chance node with 3 moves, 2 with zero prob; one non-terminal player node" { "Player" }
""

c "" 1 "(0,1)" { "A" 0 "B" 0 "C" 1 } 0
p "" 1 1 "(1,1)" { "LEFT" "RIGHT" } 0
t "" 1 "Outcome 1" { 0 }
t "" 2 "Outcome 2" { 1 }
t "" 3 "Outcome 3" { 2 }
t "" 4 "Outcome 4" { 3 }
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
EFG 2 R "A chance node with 5 moves; no non-terminal player node" { "Player" }
""

c "" 1 "" { "A" 1/3 "B" 0 "C" 1/3 "D" 1/3 "E" 0 } 0
t "" 1 "A" { 0 }
t "" 2 "B" { 1 }
t "" 3 "C" { 3 }
t "" 4 "D" { 3 }
t "" 5 "E" { 4 }

0 comments on commit a8a3574

Please sign in to comment.