From 739eca7c95ef38a38ddefd45c974560e67a16fd8 Mon Sep 17 00:00:00 2001 From: StephenPasteris Date: Mon, 3 Nov 2025 17:04:29 +0000 Subject: [PATCH 1/4] Issue #593 --- Makefile.am | 8 +-- src/games/behavspt.cc | 56 +++++++++++++++++++ src/games/behavspt.h | 21 +++++++ src/{solvers/enumpoly => games}/gameseq.cc | 0 src/{solvers/enumpoly => games}/gameseq.h | 55 ++++++++++++++++++ .../enumpoly => games}/indexproduct.h | 0 src/{solvers/enumpoly => games}/ndarray.h | 0 src/solvers/enumpoly/efgpoly.cc | 38 ++++++------- src/solvers/enumpoly/polypartial.imp | 2 +- src/solvers/enumpoly/polysolver.cc | 2 +- 10 files changed, 157 insertions(+), 25 deletions(-) rename src/{solvers/enumpoly => games}/gameseq.cc (100%) rename src/{solvers/enumpoly => games}/gameseq.h (86%) rename src/{solvers/enumpoly => games}/indexproduct.h (100%) rename src/{solvers/enumpoly => games}/ndarray.h (100%) diff --git a/Makefile.am b/Makefile.am index 2a19386a9..5f7151100 100644 --- a/Makefile.am +++ b/Makefile.am @@ -285,6 +285,10 @@ agg_SOURCES = \ game_SOURCES = \ src/gambit.h \ + src/games/gameseq.cc \ + src/games/gameseq.h \ + src/games/indexproduct.h \ + src/games/ndarray.h \ src/games/number.h \ src/games/gameobject.h \ src/games/game.cc \ @@ -398,10 +402,6 @@ gambit_nashsupport_SOURCES = \ gambit_enumpoly_SOURCES = \ ${core_SOURCES} ${game_SOURCES} ${gambit_nashsupport_SOURCES} \ - src/solvers/enumpoly/ndarray.h \ - src/solvers/enumpoly/gameseq.cc \ - src/solvers/enumpoly/gameseq.h \ - src/solvers/enumpoly/indexproduct.h \ src/solvers/enumpoly/rectangle.h \ src/solvers/enumpoly/poly.cc \ src/solvers/enumpoly/poly.h \ diff --git a/src/games/behavspt.cc b/src/games/behavspt.cc index 71eec0931..e98352bbe 100644 --- a/src/games/behavspt.cc +++ b/src/games/behavspt.cc @@ -21,6 +21,7 @@ // #include "gambit.h" +#include "gameseq.h" namespace Gambit { @@ -259,4 +260,59 @@ std::list BehaviorSupportProfile::GetMembers(const GameInfoset &p_info return answer; } +//======================================================================== +// BehaviorSupportProfile: Sequence form +//======================================================================== + +std::shared_ptr BehaviorSupportProfile::GetSequenceForm() const +{ + if (!m_sequenceForm) { + m_sequenceForm = std::make_shared(*this); + } + return m_sequenceForm; +} + +SequencesWrapper BehaviorSupportProfile::GetSequences() const +{ + auto sequences = GetSequenceForm()->GetSequences(); + return SequencesWrapper(sequences); +} + +PlayerSequencesWrapper BehaviorSupportProfile::GetSequences(GamePlayer &p_player) const +{ + auto sequences = GetSequenceForm()->GetSequences(p_player); + return PlayerSequencesWrapper(sequences); +} + +int BehaviorSupportProfile::GetConstraintEntry(const GameInfoset &p_infoset, + const GameAction &p_action) const +{ + return GetSequenceForm()->GetConstraintEntry(p_infoset, p_action); +} + +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 +{ + return GetSequenceForm()->ToMixedBehaviorProfile(p_profile); +} + +InfosetsWrapper BehaviorSupportProfile::GetInfosets() const +{ + auto infosets = GetSequenceForm()->GetInfosets(); + return InfosetsWrapper(infosets); +} + +ContingenciesWrapper BehaviorSupportProfile::GetContingencies() const +{ + auto contingencies = GetSequenceForm()->GetContingencies(); + return ContingenciesWrapper(contingencies); +} + } // end namespace Gambit diff --git a/src/games/behavspt.h b/src/games/behavspt.h index bf15cccb5..4018c4c10 100644 --- a/src/games/behavspt.h +++ b/src/games/behavspt.h @@ -29,6 +29,13 @@ namespace Gambit { +class GameSequenceForm; +struct GameSequenceRep; +class SequencesWrapper; +class PlayerSequencesWrapper; +class InfosetsWrapper; +class ContingenciesWrapper; + /// This class represents a subset of the actions in an extensive game. /// It is enforced that each player has at least one action at each /// information set; thus, the actions in a support can be viewed as @@ -139,6 +146,20 @@ class BehaviorSupportProfile { /// Returns a copy of the support with dominated actions eliminated BehaviorSupportProfile Undominated(bool p_strict) const; //@} + + mutable std::shared_ptr m_sequenceForm; + std::shared_ptr GetSequenceForm() const; + SequencesWrapper GetSequences() const; + PlayerSequencesWrapper 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; + GameRep::Players GetPlayers() const { return GetGame()->GetPlayers(); } + MixedBehaviorProfile + ToMixedBehaviorProfile(const std::map, double> &) const; + InfosetsWrapper GetInfosets() const; + ContingenciesWrapper GetContingencies() const; }; } // end namespace Gambit diff --git a/src/solvers/enumpoly/gameseq.cc b/src/games/gameseq.cc similarity index 100% rename from src/solvers/enumpoly/gameseq.cc rename to src/games/gameseq.cc diff --git a/src/solvers/enumpoly/gameseq.h b/src/games/gameseq.h similarity index 86% rename from src/solvers/enumpoly/gameseq.h rename to src/games/gameseq.h index 54f43c0eb..7c3ef858f 100644 --- a/src/solvers/enumpoly/gameseq.h +++ b/src/games/gameseq.h @@ -293,6 +293,61 @@ class GameSequenceForm { 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 #endif // GAMESEQ_H diff --git a/src/solvers/enumpoly/indexproduct.h b/src/games/indexproduct.h similarity index 100% rename from src/solvers/enumpoly/indexproduct.h rename to src/games/indexproduct.h diff --git a/src/solvers/enumpoly/ndarray.h b/src/games/ndarray.h similarity index 100% rename from src/solvers/enumpoly/ndarray.h rename to src/games/ndarray.h diff --git a/src/solvers/enumpoly/efgpoly.cc b/src/solvers/enumpoly/efgpoly.cc index 8ca2ac2fd..a6775ca82 100644 --- a/src/solvers/enumpoly/efgpoly.cc +++ b/src/solvers/enumpoly/efgpoly.cc @@ -22,7 +22,7 @@ #include "enumpoly.h" #include "solvers/nashsupport/nashsupport.h" -#include "gameseq.h" +#include "games/gameseq.h" #include "polysystem.h" #include "polysolver.h" #include "behavextend.h" @@ -48,7 +48,7 @@ namespace { class ProblemData { public: - GameSequenceForm sfg; + BehaviorSupportProfile m_support; std::shared_ptr space; std::map var; std::map> variables; @@ -62,17 +62,17 @@ Polynomial BuildSequenceVariable(ProblemData &p_data, const GameSequence if (!p_sequence->action) { return {p_data.space, 1}; } - if (p_sequence->action != p_data.sfg.GetSupport().GetActions(p_sequence->GetInfoset()).back()) { + if (p_sequence->action != p_data.m_support.GetActions(p_sequence->GetInfoset()).back()) { return {p_data.space, var.at(p_sequence), 1}; } Polynomial equation(p_data.space); - for (auto seq : p_data.sfg.GetSequences(p_sequence->player)) { + for (auto seq : p_data.m_support.GetSequences(p_sequence->player)) { if (seq == p_sequence) { continue; } if (const int constraint_coef = - p_data.sfg.GetConstraintEntry(p_sequence->GetInfoset(), seq->action)) { + p_data.m_support.GetConstraintEntry(p_sequence->GetInfoset(), seq->action)) { equation += BuildSequenceVariable(p_data, seq, var) * double(constraint_coef); } } @@ -80,18 +80,18 @@ Polynomial BuildSequenceVariable(ProblemData &p_data, const GameSequence } ProblemData::ProblemData(const BehaviorSupportProfile &p_support) - : sfg(p_support), - space(std::make_shared(sfg.GetSequences().size() - sfg.GetInfosets().size() - - sfg.GetPlayers().size())) + : m_support(p_support), space(std::make_shared(m_support.GetSequences().size() - + m_support.GetInfosets().size() - + m_support.GetPlayers().size())) { - for (auto sequence : sfg.GetSequences()) { + for (auto sequence : m_support.GetSequences()) { if (sequence->action && (sequence->action != p_support.GetActions(sequence->GetInfoset()).back())) { var[sequence] = var.size() + 1; } } - for (auto sequence : sfg.GetSequences()) { + for (auto sequence : m_support.GetSequences()) { variables.emplace(sequence, BuildSequenceVariable(*this, sequence, var)); } } @@ -100,11 +100,11 @@ Polynomial GetPayoff(ProblemData &p_data, const GamePlayer &p_player) { Polynomial equation(p_data.space); - for (auto profile : p_data.sfg.GetContingencies()) { - auto pay = p_data.sfg.GetPayoff(profile, p_player); + for (auto profile : p_data.m_support.GetContingencies()) { + auto pay = p_data.m_support.GetPayoff(profile, p_player); if (pay != Rational(0)) { Polynomial term(p_data.space, double(pay)); - for (auto player : p_data.sfg.GetPlayers()) { + for (auto player : p_data.m_support.GetPlayers()) { term *= p_data.variables.at(profile[player]); } equation += term; @@ -115,9 +115,9 @@ Polynomial GetPayoff(ProblemData &p_data, const GamePlayer &p_player) void IndifferenceEquations(ProblemData &p_data, PolynomialSystem &p_equations) { - for (auto player : p_data.sfg.GetPlayers()) { + for (auto player : p_data.m_support.GetPlayers()) { const Polynomial payoff = GetPayoff(p_data, player); - for (auto sequence : p_data.sfg.GetSequences(player)) { + for (auto sequence : p_data.m_support.GetSequences(player)) { try { p_equations.push_back(payoff.PartialDerivative(p_data.var.at(sequence))); } @@ -131,11 +131,11 @@ void IndifferenceEquations(ProblemData &p_data, PolynomialSystem &p_equa void LastActionProbPositiveInequalities(ProblemData &p_data, PolynomialSystem &p_equations) { - for (auto sequence : p_data.sfg.GetSequences()) { + for (auto sequence : p_data.m_support.GetSequences()) { if (!sequence->action) { continue; } - const auto &actions = p_data.sfg.GetSupport().GetActions(sequence->action->GetInfoset()); + const auto &actions = p_data.m_support.GetActions(sequence->action->GetInfoset()); if (actions.size() > 1 && sequence->action == actions.back()) { p_equations.push_back(p_data.variables.at(sequence)); } @@ -145,7 +145,7 @@ void LastActionProbPositiveInequalities(ProblemData &p_data, PolynomialSystem ToSequenceProbs(const ProblemData &p_data, const Vector &v) { std::map x; - for (auto sequence : p_data.sfg.GetSequences()) { + for (auto sequence : p_data.m_support.GetSequences()) { x[sequence] = p_data.variables.at(sequence).Evaluate(v); } return x; @@ -181,7 +181,7 @@ std::list> SolveSupport(const BehaviorSupportProfil std::list> solutions; for (auto root : roots) { const MixedBehaviorProfile sol( - data.sfg.ToMixedBehaviorProfile(ToSequenceProbs(data, root))); + data.m_support.ToMixedBehaviorProfile(ToSequenceProbs(data, root))); if (ExtendsToNash(sol, BehaviorSupportProfile(sol.GetGame()), BehaviorSupportProfile(sol.GetGame()))) { solutions.push_back(sol); diff --git a/src/solvers/enumpoly/polypartial.imp b/src/solvers/enumpoly/polypartial.imp index 9248c5251..9bf84f466 100644 --- a/src/solvers/enumpoly/polypartial.imp +++ b/src/solvers/enumpoly/polypartial.imp @@ -21,7 +21,7 @@ // #include "polypartial.h" -#include "indexproduct.h" +#include "games/indexproduct.h" namespace Gambit { diff --git a/src/solvers/enumpoly/polysolver.cc b/src/solvers/enumpoly/polysolver.cc index 7a59fd5c9..d7b5d15a7 100644 --- a/src/solvers/enumpoly/polysolver.cc +++ b/src/solvers/enumpoly/polysolver.cc @@ -21,7 +21,7 @@ // #include "polysolver.h" -#include "indexproduct.h" +#include "games/indexproduct.h" namespace Gambit { From 5072b7f91335f336b4fd5ff0a2a028f7248fd6f1 Mon Sep 17 00:00:00 2001 From: StephenPasteris Date: Tue, 11 Nov 2025 11:35:09 +0000 Subject: [PATCH 2/4] Moved indexproduct.h back to enumpoly --- Makefile.am | 2 +- src/{games => solvers/enumpoly}/indexproduct.h | 0 src/solvers/enumpoly/polypartial.imp | 2 +- src/solvers/enumpoly/polysolver.cc | 2 +- 4 files changed, 3 insertions(+), 3 deletions(-) rename src/{games => solvers/enumpoly}/indexproduct.h (100%) diff --git a/Makefile.am b/Makefile.am index 5f7151100..4325353ef 100644 --- a/Makefile.am +++ b/Makefile.am @@ -287,7 +287,6 @@ game_SOURCES = \ src/gambit.h \ src/games/gameseq.cc \ src/games/gameseq.h \ - src/games/indexproduct.h \ src/games/ndarray.h \ src/games/number.h \ src/games/gameobject.h \ @@ -402,6 +401,7 @@ gambit_nashsupport_SOURCES = \ gambit_enumpoly_SOURCES = \ ${core_SOURCES} ${game_SOURCES} ${gambit_nashsupport_SOURCES} \ + src/solvers/enumpoly/indexproduct.h \ src/solvers/enumpoly/rectangle.h \ src/solvers/enumpoly/poly.cc \ src/solvers/enumpoly/poly.h \ diff --git a/src/games/indexproduct.h b/src/solvers/enumpoly/indexproduct.h similarity index 100% rename from src/games/indexproduct.h rename to src/solvers/enumpoly/indexproduct.h diff --git a/src/solvers/enumpoly/polypartial.imp b/src/solvers/enumpoly/polypartial.imp index 9bf84f466..9248c5251 100644 --- a/src/solvers/enumpoly/polypartial.imp +++ b/src/solvers/enumpoly/polypartial.imp @@ -21,7 +21,7 @@ // #include "polypartial.h" -#include "games/indexproduct.h" +#include "indexproduct.h" namespace Gambit { diff --git a/src/solvers/enumpoly/polysolver.cc b/src/solvers/enumpoly/polysolver.cc index d7b5d15a7..7a59fd5c9 100644 --- a/src/solvers/enumpoly/polysolver.cc +++ b/src/solvers/enumpoly/polysolver.cc @@ -21,7 +21,7 @@ // #include "polysolver.h" -#include "games/indexproduct.h" +#include "indexproduct.h" namespace Gambit { From 69e204e590adbd4b15e75e8ab7e8e6aaca43ece8 Mon Sep 17 00:00:00 2001 From: StephenPasteris Date: Tue, 11 Nov 2025 11:45:48 +0000 Subject: [PATCH 3/4] Made one-liners in wrapper methods --- src/games/behavspt.cc | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/games/behavspt.cc b/src/games/behavspt.cc index e98352bbe..6b9004acc 100644 --- a/src/games/behavspt.cc +++ b/src/games/behavspt.cc @@ -274,14 +274,12 @@ std::shared_ptr BehaviorSupportProfile::GetSequenceForm() cons SequencesWrapper BehaviorSupportProfile::GetSequences() const { - auto sequences = GetSequenceForm()->GetSequences(); - return SequencesWrapper(sequences); + return SequencesWrapper(GetSequenceForm()->GetSequences()); } PlayerSequencesWrapper BehaviorSupportProfile::GetSequences(GamePlayer &p_player) const { - auto sequences = GetSequenceForm()->GetSequences(p_player); - return PlayerSequencesWrapper(sequences); + return PlayerSequencesWrapper(GetSequenceForm()->GetSequences(p_player)); } int BehaviorSupportProfile::GetConstraintEntry(const GameInfoset &p_infoset, @@ -305,14 +303,12 @@ MixedBehaviorProfile BehaviorSupportProfile::ToMixedBehaviorProfile( InfosetsWrapper BehaviorSupportProfile::GetInfosets() const { - auto infosets = GetSequenceForm()->GetInfosets(); - return InfosetsWrapper(infosets); + return InfosetsWrapper(GetSequenceForm()->GetInfosets()); } ContingenciesWrapper BehaviorSupportProfile::GetContingencies() const { - auto contingencies = GetSequenceForm()->GetContingencies(); - return ContingenciesWrapper(contingencies); + return ContingenciesWrapper(GetSequenceForm()->GetContingencies()); } } // end namespace Gambit From f8f6f1b3b91aba811516e2ae46891d03bdea92f3 Mon Sep 17 00:00:00 2001 From: StephenPasteris Date: Tue, 11 Nov 2025 11:52:11 +0000 Subject: [PATCH 4/4] Changed GameSequenceRep from struct to class --- src/games/behavspt.h | 2 +- src/games/gameseq.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/games/behavspt.h b/src/games/behavspt.h index 4018c4c10..099234ba0 100644 --- a/src/games/behavspt.h +++ b/src/games/behavspt.h @@ -30,7 +30,7 @@ namespace Gambit { class GameSequenceForm; -struct GameSequenceRep; +class GameSequenceRep; class SequencesWrapper; class PlayerSequencesWrapper; class InfosetsWrapper; diff --git a/src/games/gameseq.h b/src/games/gameseq.h index 7c3ef858f..d5718599f 100644 --- a/src/games/gameseq.h +++ b/src/games/gameseq.h @@ -28,7 +28,7 @@ namespace Gambit { -struct GameSequenceRep { +class GameSequenceRep { public: GamePlayer player; GameAction action;