diff --git a/src/games/behavspt.cc b/src/games/behavspt.cc index 6b9004acc..be017148e 100644 --- a/src/games/behavspt.cc +++ b/src/games/behavspt.cc @@ -66,6 +66,7 @@ size_t BehaviorSupportProfile::BehaviorProfileLength() const void BehaviorSupportProfile::AddAction(const GameAction &p_action) { + m_reachableInfosets = nullptr; auto &support = m_actions.at(p_action->GetInfoset()); auto pos = std::find_if(support.begin(), support.end(), [p_action](const GameAction &a) { return a->GetNumber() >= p_action->GetNumber(); @@ -81,6 +82,7 @@ void BehaviorSupportProfile::AddAction(const GameAction &p_action) bool BehaviorSupportProfile::RemoveAction(const GameAction &p_action) { + m_reachableInfosets = nullptr; auto &support = m_actions.at(p_action->GetInfoset()); auto pos = std::find(support.begin(), support.end(), p_action); if (pos != support.end()) { @@ -272,14 +274,12 @@ std::shared_ptr BehaviorSupportProfile::GetSequenceForm() cons return m_sequenceForm; } -SequencesWrapper BehaviorSupportProfile::GetSequences() const -{ - return SequencesWrapper(GetSequenceForm()->GetSequences()); -} +BehaviorSupportProfile::Sequences BehaviorSupportProfile::GetSequences() const { return {this}; } -PlayerSequencesWrapper BehaviorSupportProfile::GetSequences(GamePlayer &p_player) const +BehaviorSupportProfile::PlayerSequences +BehaviorSupportProfile::GetSequences(GamePlayer &p_player) const { - return PlayerSequencesWrapper(GetSequenceForm()->GetSequences(p_player)); + return {this, p_player}; } int BehaviorSupportProfile::GetConstraintEntry(const GameInfoset &p_infoset, @@ -288,27 +288,190 @@ int BehaviorSupportProfile::GetConstraintEntry(const GameInfoset &p_infoset, return GetSequenceForm()->GetConstraintEntry(p_infoset, p_action); } -const Rational &BehaviorSupportProfile::GetPayoff( - const std::map> &p_profile, - const GamePlayer &p_player) const +const Rational & +BehaviorSupportProfile::GetPayoff(const std::map &p_profile, + const GamePlayer &p_player) const { return GetSequenceForm()->GetPayoff(p_profile, p_player); } -MixedBehaviorProfile BehaviorSupportProfile::ToMixedBehaviorProfile( - const std::map, double> &p_profile) const +BehaviorSupportProfile::SequenceContingencies +BehaviorSupportProfile::GetSequenceContingencies() const +{ + return {this}; +} + +MixedBehaviorProfile +BehaviorSupportProfile::ToMixedBehaviorProfile(const std::map &x) const { - return GetSequenceForm()->ToMixedBehaviorProfile(p_profile); + MixedBehaviorProfile b(*this); + for (auto sequence : GetSequences()) { + if (sequence->action == nullptr) { + continue; + } + const double parent_prob = x.at(sequence->parent.lock()); + if (parent_prob > 0) { + b[sequence->action] = x.at(sequence) / parent_prob; + } + else { + b[sequence->action] = 0; + } + } + return b; } -InfosetsWrapper BehaviorSupportProfile::GetInfosets() const +size_t BehaviorSupportProfile::Sequences::size() const { - return InfosetsWrapper(GetSequenceForm()->GetInfosets()); + return std::accumulate(m_support->GetSequenceForm()->m_sequences.cbegin(), + m_support->GetSequenceForm()->m_sequences.cend(), 0, + [](int acc, const std::pair> &seq) { + return acc + seq.second.size(); + }); } -ContingenciesWrapper BehaviorSupportProfile::GetContingencies() const +BehaviorSupportProfile::Sequences::iterator BehaviorSupportProfile::Sequences::begin() const { - return ContingenciesWrapper(GetSequenceForm()->GetContingencies()); + return {m_support->GetSequenceForm(), false}; +} +BehaviorSupportProfile::Sequences::iterator BehaviorSupportProfile::Sequences::end() const +{ + return {m_support->GetSequenceForm(), true}; +} + +BehaviorSupportProfile::Sequences::iterator::iterator( + const std::shared_ptr p_sfg, bool p_end) + : m_sfg(p_sfg) +{ + if (p_end) { + m_currentPlayer = m_sfg->m_sequences.cend(); + } + else { + m_currentPlayer = m_sfg->m_sequences.cbegin(); + m_currentSequence = m_currentPlayer->second.cbegin(); + } +} + +BehaviorSupportProfile::Sequences::iterator & +BehaviorSupportProfile::Sequences::iterator::operator++() +{ + if (m_currentPlayer == m_sfg->m_sequences.cend()) { + return *this; + } + m_currentSequence++; + if (m_currentSequence != m_currentPlayer->second.cend()) { + return *this; + } + m_currentPlayer++; + if (m_currentPlayer != m_sfg->m_sequences.cend()) { + m_currentSequence = m_currentPlayer->second.cbegin(); + } + return *this; +} + +bool BehaviorSupportProfile::Sequences::iterator::operator==(const iterator &it) const +{ + if (m_sfg != it.m_sfg || m_currentPlayer != it.m_currentPlayer) { + return false; + } + if (m_currentPlayer == m_sfg->m_sequences.end()) { + return true; + } + return (m_currentSequence == it.m_currentSequence); +} + +std::vector::const_iterator BehaviorSupportProfile::PlayerSequences::begin() const +{ + return m_support->GetSequenceForm()->m_sequences.at(m_player).begin(); +} + +std::vector::const_iterator BehaviorSupportProfile::PlayerSequences::end() const +{ + return m_support->GetSequenceForm()->m_sequences.at(m_player).end(); +} + +size_t BehaviorSupportProfile::PlayerSequences::size() const +{ + return m_support->GetSequenceForm()->m_sequences.at(m_player).size(); +} + +BehaviorSupportProfile::SequenceContingencies::iterator::iterator( + const std::shared_ptr p_sfg, bool p_end) + : m_sfg(p_sfg), m_end(p_end) +{ + for (auto [player, sequences] : m_sfg->m_sequences) { + m_indices[player] = 0; + } +} + +std::map +BehaviorSupportProfile::SequenceContingencies::iterator::operator*() const +{ + std::map ret; + for (auto [player, index] : m_indices) { + ret[player] = m_sfg->m_sequences.at(player)[index]; + } + return ret; +} + +std::map +BehaviorSupportProfile::SequenceContingencies::iterator::operator->() const +{ + std::map ret; + for (auto [player, index] : m_indices) { + ret[player] = m_sfg->m_sequences.at(player)[index]; + } + return ret; +} + +BehaviorSupportProfile::SequenceContingencies::iterator & +BehaviorSupportProfile::SequenceContingencies::iterator::operator++() +{ + for (auto [player, index] : m_indices) { + if (index < m_sfg->m_sequences.at(player).size() - 1) { + m_indices[player]++; + return *this; + } + m_indices[player] = 0; + } + m_end = true; + return *this; +} + +//======================================================================== +// BehaviorSupportProfile: Reachable Information Sets +//======================================================================== + +void BehaviorSupportProfile::FindReachableInfosets(GameNode p_node) const +{ + if (!p_node->IsTerminal()) { + auto infoset = p_node->GetInfoset(); + (*m_reachableInfosets)[infoset] = true; + if (p_node->GetPlayer()->IsChance()) { + for (auto action : infoset->GetActions()) { + FindReachableInfosets(p_node->GetChild(action)); + } + } + else { + for (auto action : GetActions(infoset)) { + FindReachableInfosets(p_node->GetChild(action)); + } + } + } +} + +std::shared_ptr> BehaviorSupportProfile::GetReachableInfosets() const +{ + if (!m_reachableInfosets) { + m_reachableInfosets = std::make_shared>(); + for (size_t pl = 0; pl <= GetGame()->NumPlayers(); pl++) { + const GamePlayer player = (pl == 0) ? GetGame()->GetChance() : GetGame()->GetPlayer(pl); + for (const auto &infoset : player->GetInfosets()) { + (*m_reachableInfosets)[infoset] = false; + } + } + FindReachableInfosets(GetGame()->GetRoot()); + } + return m_reachableInfosets; } } // end namespace Gambit diff --git a/src/games/behavspt.h b/src/games/behavspt.h index 099234ba0..59e89fbbc 100644 --- a/src/games/behavspt.h +++ b/src/games/behavspt.h @@ -30,7 +30,6 @@ namespace Gambit { class GameSequenceForm; -class GameSequenceRep; class SequencesWrapper; class PlayerSequencesWrapper; class InfosetsWrapper; @@ -44,9 +43,10 @@ class ContingenciesWrapper; /// computational approaches that enumerate possible equilibrium /// supports. class BehaviorSupportProfile { -protected: Game m_efg; std::map> m_actions; + mutable std::shared_ptr m_sequenceForm; + mutable std::shared_ptr> m_reachableInfosets; std::map m_infosetReachable; std::map m_nonterminalReachable; @@ -57,7 +57,6 @@ class BehaviorSupportProfile { public: class Support { - private: const BehaviorSupportProfile *m_profile; GameInfoset m_infoset; @@ -147,19 +146,117 @@ class BehaviorSupportProfile { BehaviorSupportProfile Undominated(bool p_strict) const; //@} - mutable std::shared_ptr m_sequenceForm; + class Infosets { + const BehaviorSupportProfile *m_support; + + public: + Infosets(const BehaviorSupportProfile *p_support) : m_support(p_support) {} + + size_t size() const + { + auto reachable_infosets = m_support->GetReachableInfosets(); + size_t count = 0; + for (auto [infoset, is_reachable] : *reachable_infosets) { + if (is_reachable && !infoset->GetPlayer()->IsChance()) { + ++count; + } + } + return count; + } + }; + + class Sequences { + const BehaviorSupportProfile *m_support; + + public: + class iterator { + const std::shared_ptr m_sfg; + std::map>::const_iterator m_currentPlayer; + std::vector::const_iterator m_currentSequence; + + public: + iterator(const std::shared_ptr p_sfg, bool p_end); + + GameSequence operator*() const { return *m_currentSequence; } + GameSequence operator->() const { return *m_currentSequence; } + + iterator &operator++(); + + bool operator==(const iterator &it) const; + bool operator!=(const iterator &it) const { return !(*this == it); } + }; + + Sequences(const BehaviorSupportProfile *p_support) : m_support(p_support) {} + + size_t size() const; + + iterator begin() const; + iterator end() const; + }; + + class PlayerSequences { + const BehaviorSupportProfile *m_support; + GamePlayer m_player; + + public: + PlayerSequences(const BehaviorSupportProfile *p_support, const GamePlayer &p_player) + : m_support(p_support), m_player(p_player) + { + } + + size_t size() const; + std::vector::const_iterator begin() const; + std::vector::const_iterator end() const; + }; + + class SequenceContingencies { + const BehaviorSupportProfile *m_support; + + public: + SequenceContingencies(const BehaviorSupportProfile *p_support) : m_support(p_support) {} + + class iterator { + private: + const std::shared_ptr m_sfg; + bool m_end{false}; + std::map m_indices; + + public: + using iterator_category = std::input_iterator_tag; + + iterator(const std::shared_ptr p_sfg, bool p_end = false); + + std::map operator*() const; + + std::map operator->() const; + + iterator &operator++(); + + bool operator==(const iterator &it) const + { + return (m_end == it.m_end && m_sfg == it.m_sfg && m_indices == it.m_indices); + } + bool operator!=(const iterator &it) const { return !(*this == it); } + }; + + iterator begin() { return {m_support->GetSequenceForm()}; } + iterator end() { return {m_support->GetSequenceForm(), true}; } + }; + std::shared_ptr GetSequenceForm() const; - SequencesWrapper GetSequences() const; - PlayerSequencesWrapper GetSequences(GamePlayer &p_player) const; + Sequences GetSequences() const; + PlayerSequences GetSequences(GamePlayer &p_player) const; int GetConstraintEntry(const GameInfoset &p_infoset, const GameAction &p_action) const; - const Rational & - GetPayoff(const std::map> &p_profile, - const GamePlayer &p_player) const; + const Rational &GetPayoff(const std::map &p_profile, + const GamePlayer &p_player) const; GameRep::Players GetPlayers() const { return GetGame()->GetPlayers(); } MixedBehaviorProfile - ToMixedBehaviorProfile(const std::map, double> &) const; - InfosetsWrapper GetInfosets() const; - ContingenciesWrapper GetContingencies() const; + ToMixedBehaviorProfile(const std::map &) const; + Infosets GetInfosets() const { return {this}; }; + SequenceContingencies GetSequenceContingencies() const; + + void FindReachableInfosets(GameNode p_node) const; + std::shared_ptr> GetReachableInfosets() const; }; } // end namespace Gambit diff --git a/src/games/game.h b/src/games/game.h index 53cea45f8..2af71a604 100644 --- a/src/games/game.h +++ b/src/games/game.h @@ -48,6 +48,9 @@ using GameInfoset = GameObjectPtr; class GameStrategyRep; using GameStrategy = GameObjectPtr; +class GameSequenceRep; +using GameSequence = GameObjectPtr; + class GamePlayerRep; using GamePlayer = GameObjectPtr; @@ -316,6 +319,36 @@ class GameStrategyRep : public std::enable_shared_from_this { //@} }; +class GameSequenceRep : public std::enable_shared_from_this { +public: + bool m_valid{true}; + GamePlayer player; + GameAction action; + size_t number; + std::weak_ptr parent; + + explicit GameSequenceRep(const GamePlayer &p_player, const GameAction &p_action, size_t p_number, + std::weak_ptr p_parent) + : player(p_player), action(p_action), number(p_number), parent(p_parent) + { + } + + bool IsValid() const { return m_valid; } + void Invalidate() { m_valid = false; } + + Game GetGame() const; + GameInfoset GetInfoset() const { return (action) ? action->GetInfoset() : nullptr; } + + bool operator<(const GameSequenceRep &other) const + { + return player < other.player || (player == other.player && action < other.action); + } + bool operator==(const GameSequenceRep &other) const + { + return player == other.player && action == other.action; + } +}; + /// A player in a game class GamePlayerRep : public std::enable_shared_from_this { friend class GameRep; @@ -945,6 +978,8 @@ inline void GameOutcomeRep::SetPayoff(const GamePlayer &p_player, const Number & inline GamePlayer GameStrategyRep::GetPlayer() const { return m_player->shared_from_this(); } inline Game GameStrategyRep::GetGame() const { return m_player->GetGame(); } +inline Game GameSequenceRep::GetGame() const { return player->GetGame(); } + inline Game GameActionRep::GetGame() const { return m_infoset->GetGame(); } inline Game GameInfosetRep::GetGame() const { return m_game->shared_from_this(); } diff --git a/src/games/gameseq.cc b/src/games/gameseq.cc index 1205c459f..367a7ec7c 100644 --- a/src/games/gameseq.cc +++ b/src/games/gameseq.cc @@ -43,8 +43,9 @@ void GameSequenceForm::BuildSequences(const GameNode &n, auto tmp_sequence = p_currentSequences.at(n->GetPlayer()); for (auto action : m_support.GetActions(n->GetInfoset())) { if (m_correspondence.find(action) == m_correspondence.end()) { - m_sequences[n->GetPlayer()].push_back(std::make_shared( - n->GetPlayer(), action, m_sequences[n->GetPlayer()].size() + 1, tmp_sequence)); + m_sequences[n->GetPlayer()].emplace_back(std::make_shared( + n->GetPlayer(), action, m_sequences[n->GetPlayer()].size() + 1, + tmp_sequence.get_shared())); m_correspondence[action] = m_sequences[n->GetPlayer()].back(); } p_currentSequences[n->GetPlayer()] = m_correspondence[action]; @@ -111,23 +112,4 @@ void GameSequenceForm::FillTableau() FillTableau(m_support.GetGame()->GetRoot(), Rational(1), currentSequence); } -MixedBehaviorProfile -GameSequenceForm::ToMixedBehaviorProfile(const std::map &x) const -{ - MixedBehaviorProfile b(m_support); - for (auto sequence : GetSequences()) { - if (sequence->action == nullptr) { - continue; - } - const double parent_prob = x.at(sequence->parent.lock()); - if (parent_prob > 0) { - b[sequence->action] = x.at(sequence) / parent_prob; - } - else { - b[sequence->action] = 0; - } - } - return b; -} - } // end namespace Gambit diff --git a/src/games/gameseq.h b/src/games/gameseq.h index d5718599f..bcf4fec70 100644 --- a/src/games/gameseq.h +++ b/src/games/gameseq.h @@ -28,35 +28,9 @@ namespace Gambit { -class GameSequenceRep { -public: - GamePlayer player; - GameAction action; - size_t number; - std::weak_ptr parent; - - explicit GameSequenceRep(const GamePlayer &p_player, const GameAction &p_action, size_t p_number, - std::weak_ptr p_parent) - : player(p_player), action(p_action), number(p_number), parent(p_parent) - { - } - - GameInfoset GetInfoset() const { return (action) ? action->GetInfoset() : nullptr; } - - bool operator<(const GameSequenceRep &other) const - { - return player < other.player || (player == other.player && action < other.action); - } - bool operator==(const GameSequenceRep &other) const - { - return player == other.player && action == other.action; - } -}; - -using GameSequence = std::shared_ptr; - class GameSequenceForm { -private: + friend class BehaviorSupportProfile; + BehaviorSupportProfile m_support; std::map> m_sequences; NDArray m_payoffs; @@ -86,7 +60,6 @@ class GameSequenceForm { public: class Infosets { - private: const GameSequenceForm *m_sfg; public: @@ -95,164 +68,6 @@ class GameSequenceForm { size_t size() const { return m_sfg->m_infosets.size(); } }; - class Sequences { - private: - const GameSequenceForm *m_sfg; - - public: - class iterator { - private: - const GameSequenceForm *m_sfg; - std::map>::const_iterator m_currentPlayer; - std::vector::const_iterator m_currentSequence; - - public: - iterator(const GameSequenceForm *p_sfg, bool p_end) : m_sfg(p_sfg) - { - if (p_end) { - m_currentPlayer = m_sfg->m_sequences.cend(); - } - else { - m_currentPlayer = m_sfg->m_sequences.cbegin(); - m_currentSequence = m_currentPlayer->second.cbegin(); - } - } - - GameSequence operator*() const { return *m_currentSequence; } - GameSequence operator->() const { return *m_currentSequence; } - - iterator &operator++() - { - if (m_currentPlayer == m_sfg->m_sequences.cend()) { - return *this; - } - m_currentSequence++; - if (m_currentSequence != m_currentPlayer->second.cend()) { - return *this; - } - m_currentPlayer++; - if (m_currentPlayer != m_sfg->m_sequences.cend()) { - m_currentSequence = m_currentPlayer->second.cbegin(); - } - return *this; - } - - bool operator==(const iterator &it) const - { - if (m_sfg != it.m_sfg || m_currentPlayer != it.m_currentPlayer) { - return false; - } - if (m_currentPlayer == m_sfg->m_sequences.end()) { - return true; - } - return (m_currentSequence == it.m_currentSequence); - } - bool operator!=(const iterator &it) const { return !(*this == it); } - }; - - Sequences(const GameSequenceForm *p_sfg) : m_sfg(p_sfg) {} - - size_t size() const - { - return std::accumulate( - m_sfg->m_sequences.cbegin(), m_sfg->m_sequences.cend(), 0, - [](int acc, const std::pair> &seq) { - return acc + seq.second.size(); - }); - } - - iterator begin() const { return {m_sfg, false}; } - iterator end() const { return {m_sfg, true}; } - }; - - class PlayerSequences { - private: - const GameSequenceForm *m_sfg; - GamePlayer m_player; - - public: - PlayerSequences(const GameSequenceForm *p_sfg, const GamePlayer &p_player) - : m_sfg(p_sfg), m_player(p_player) - { - } - - size_t size() const { return m_sfg->m_sequences.at(m_player).size(); } - - std::vector::const_iterator begin() const - { - return m_sfg->m_sequences.at(m_player).begin(); - } - std::vector::const_iterator end() const - { - return m_sfg->m_sequences.at(m_player).end(); - } - }; - - class Contingencies { - private: - const GameSequenceForm *m_sfg; - - public: - Contingencies(const GameSequenceForm *p_sfg) : m_sfg(p_sfg) {} - - class iterator { - private: - const GameSequenceForm *m_sfg; - bool m_end{false}; - std::map m_indices; - - public: - using iterator_category = std::input_iterator_tag; - - iterator(const GameSequenceForm *p_sfg, bool p_end = false) : m_sfg(p_sfg), m_end(p_end) - { - for (auto [player, sequences] : m_sfg->m_sequences) { - m_indices[player] = 0; - } - } - - std::map operator*() const - { - std::map ret; - for (auto [player, index] : m_indices) { - ret[player] = m_sfg->m_sequences.at(player)[index]; - } - return ret; - } - - std::map operator->() const - { - std::map ret; - for (auto [player, index] : m_indices) { - ret[player] = m_sfg->m_sequences.at(player)[index]; - } - return ret; - } - - iterator &operator++() - { - for (auto [player, index] : m_indices) { - if (index < m_sfg->m_sequences.at(player).size() - 1) { - m_indices[player]++; - return *this; - } - m_indices[player] = 0; - } - m_end = true; - return *this; - } - - bool operator==(const iterator &it) const - { - return (m_end == it.m_end && m_sfg == it.m_sfg && m_indices == it.m_indices); - } - bool operator!=(const iterator &it) const { return !(*this == it); } - }; - - iterator begin() { return {m_sfg}; } - iterator end() { return {m_sfg, true}; } - }; - explicit GameSequenceForm(const BehaviorSupportProfile &p_support) : m_support(p_support) { BuildSequences(); @@ -263,16 +78,8 @@ class GameSequenceForm { const BehaviorSupportProfile &GetSupport() const { return m_support; } - Sequences GetSequences() const { return {this}; } - - PlayerSequences GetSequences(const GamePlayer &p_player) const { return {this, p_player}; } - - Contingencies GetContingencies() const { return {this}; } - GameRep::Players GetPlayers() const { return m_support.GetGame()->GetPlayers(); } - Infosets GetInfosets() const { return {this}; } - const Rational &GetPayoff(const std::map &p_profile, const GamePlayer &p_player) const { @@ -288,64 +95,6 @@ class GameSequenceForm { return 0; } } - - MixedBehaviorProfile - ToMixedBehaviorProfile(const std::map &) const; -}; - -class SequencesWrapper { -public: - explicit SequencesWrapper(const GameSequenceForm::Sequences &sequences) : m_sequences(sequences) - { - } - - auto begin() const { return m_sequences.begin(); } - auto end() const { return m_sequences.end(); } - - std::size_t size() const { return m_sequences.size(); } - -private: - GameSequenceForm::Sequences m_sequences; -}; - -class PlayerSequencesWrapper { -public: - explicit PlayerSequencesWrapper(const GameSequenceForm::PlayerSequences &sequences) - : m_sequences(sequences) - { - } - - auto begin() const { return m_sequences.begin(); } - auto end() const { return m_sequences.end(); } - - std::size_t size() const { return m_sequences.size(); } - -private: - GameSequenceForm::PlayerSequences m_sequences; -}; - -class InfosetsWrapper { -public: - explicit InfosetsWrapper(const GameSequenceForm::Infosets &infosets) : m_infosets(infosets) {} - - std::size_t size() const { return m_infosets.size(); } - -private: - GameSequenceForm::Infosets m_infosets; -}; - -class ContingenciesWrapper { -public: - explicit ContingenciesWrapper(const GameSequenceForm::Contingencies &contingencies) - : m_contingencies(contingencies) - { - } - - auto begin() { return m_contingencies.begin(); } - auto end() { return m_contingencies.end(); } - -private: - GameSequenceForm::Contingencies m_contingencies; }; } // end namespace Gambit diff --git a/src/solvers/enumpoly/efgpoly.cc b/src/solvers/enumpoly/efgpoly.cc index a6775ca82..e88cca353 100644 --- a/src/solvers/enumpoly/efgpoly.cc +++ b/src/solvers/enumpoly/efgpoly.cc @@ -100,7 +100,7 @@ Polynomial GetPayoff(ProblemData &p_data, const GamePlayer &p_player) { Polynomial equation(p_data.space); - for (auto profile : p_data.m_support.GetContingencies()) { + for (auto profile : p_data.m_support.GetSequenceContingencies()) { auto pay = p_data.m_support.GetPayoff(profile, p_player); if (pay != Rational(0)) { Polynomial term(p_data.space, double(pay));