diff --git a/src/games/nash.h b/src/games/nash.h index 88d7eb71e5..322c4a2438 100644 --- a/src/games/nash.h +++ b/src/games/nash.h @@ -29,22 +29,16 @@ namespace Gambit::Nash { template -using StrategyCallbackType = - std::function &, const std::string &)>; +using StrategyCallbackType = std::function &)>; /// @brief A fallback callback function for mixed strategy profiles that does nothing -template void NullStrategyCallback(const MixedStrategyProfile &, const std::string &) -{ -} +template void NullStrategyCallback(const MixedStrategyProfile &) {} template -using BehaviorCallbackType = - std::function &, const std::string &)>; +using BehaviorCallbackType = std::function &)>; /// @brief A fallback callback function for mixed behavior profiles that does nothing -template void NullBehaviorCallback(const MixedBehaviorProfile &, const std::string &) -{ -} +template void NullBehaviorCallback(const MixedBehaviorProfile &) {} template std::list> diff --git a/src/solvers/enummixed/enummixed.cc b/src/solvers/enummixed/enummixed.cc index 92c1ff5e50..aa0da145c0 100644 --- a/src/solvers/enummixed/enummixed.cc +++ b/src/solvers/enummixed/enummixed.cc @@ -176,7 +176,7 @@ EnumMixedStrategySolveDetailed(const Game &p_game, StrategyCallbackType p_onE } eqm = eqm.Normalize(); solution->m_extremeEquilibria.push_back(eqm); - p_onEquilibrium(eqm, "NE"); + p_onEquilibrium(eqm); // note: The keys give the mixed strategy associated with each node. // The keys should also keep track of the basis diff --git a/src/solvers/enumpoly/efgpoly.cc b/src/solvers/enumpoly/efgpoly.cc index bc82ac051b..99cd21e359 100644 --- a/src/solvers/enumpoly/efgpoly.cc +++ b/src/solvers/enumpoly/efgpoly.cc @@ -224,8 +224,8 @@ namespace Gambit::Nash { std::list> EnumPolyBehaviorSolve(const Game &p_game, int p_stopAfter, double p_maxregret, - EnumPolyMixedBehaviorObserverFunctionType p_onEquilibrium, - EnumPolyBehaviorSupportObserverFunctionType p_onSupport) + BehaviorCallbackType p_onEquilibrium, + EnumPolyEventCallbackType p_onEvent) { const double scale = p_game->GetMaxPayoff() - p_game->GetMinPayoff(); if (scale != 0.0) { @@ -236,7 +236,7 @@ EnumPolyBehaviorSolve(const Game &p_game, int p_stopAfter, double p_maxregret, auto possible_supports = PossibleNashBehaviorSupports(p_game); for (auto support : possible_supports->m_supports) { - p_onSupport("candidate", support); + p_onEvent(EnumPolyCandidateSupportEvent{support}); bool isSingular = false; for (const auto &solution : SolveSupport(support, isSingular, std::max(p_stopAfter - static_cast(ret.size()), 0), @@ -245,7 +245,7 @@ EnumPolyBehaviorSolve(const Game &p_game, int p_stopAfter, double p_maxregret, ret.push_back(solution); } if (isSingular) { - p_onSupport("singular", support); + p_onEvent(EnumPolySingularSupportEvent{support}); } if (p_stopAfter > 0 && static_cast(ret.size()) >= p_stopAfter) { break; diff --git a/src/solvers/enumpoly/enumpoly.h b/src/solvers/enumpoly/enumpoly.h index d468ad6135..fd09858ac8 100644 --- a/src/solvers/enumpoly/enumpoly.h +++ b/src/solvers/enumpoly/enumpoly.h @@ -24,46 +24,41 @@ #ifndef GAMBIT_SOLVERS_ENUMPOLY_ENUMPOLY_H #define GAMBIT_SOLVERS_ENUMPOLY_ENUMPOLY_H +#include + #include "games/nash.h" #include "solvers/nashsupport/nashsupport.h" namespace Gambit::Nash { -using EnumPolyMixedStrategyObserverFunctionType = - std::function &)>; - -inline void EnumPolyNullMixedStrategyObserver(const MixedStrategyProfile &) {} - -using EnumPolyStrategySupportObserverFunctionType = - std::function; - -inline void EnumPolyNullStrategySupportObserver(const std::string &, - const StrategySupportProfile &) -{ -} +template struct EnumPolyCandidateSupportEvent { + const Support &support; +}; -std::list> EnumPolyStrategySolve( - const Game &p_game, int p_stopAfter, double p_maxregret, - EnumPolyMixedStrategyObserverFunctionType p_onEquilibrium = EnumPolyNullMixedStrategyObserver, - EnumPolyStrategySupportObserverFunctionType p_onSupport = EnumPolyNullStrategySupportObserver); +template struct EnumPolySingularSupportEvent { + const Support &support; +}; -using EnumPolyMixedBehaviorObserverFunctionType = - std::function &)>; +template +using EnumPolyEvent = + std::variant, EnumPolySingularSupportEvent>; -inline void EnumPolyNullMixedBehaviorObserver(const MixedBehaviorProfile &) {} +template +using EnumPolyEventCallbackType = std::function &)>; -using EnumPolyBehaviorSupportObserverFunctionType = - std::function; +template void NullEnumPolyEventCallback(const EnumPolyEvent &) {} -inline void EnumPolyNullBehaviorSupportObserver(const std::string &, - const BehaviorSupportProfile &) -{ -} +std::list> +EnumPolyStrategySolve(const Game &p_game, int p_stopAfter, double p_maxregret, + StrategyCallbackType p_onEquilibrium = NullStrategyCallback, + EnumPolyEventCallbackType p_onEvent = + NullEnumPolyEventCallback); -std::list> EnumPolyBehaviorSolve( - const Game &, int p_stopAfter, double p_maxregret, - EnumPolyMixedBehaviorObserverFunctionType p_onEquilibrium = EnumPolyNullMixedBehaviorObserver, - EnumPolyBehaviorSupportObserverFunctionType p_onSupport = EnumPolyNullBehaviorSupportObserver); +std::list> +EnumPolyBehaviorSolve(const Game &, int p_stopAfter, double p_maxregret, + BehaviorCallbackType p_onEquilibrium = NullBehaviorCallback, + EnumPolyEventCallbackType p_onEvent = + NullEnumPolyEventCallback); } // namespace Gambit::Nash diff --git a/src/solvers/enumpoly/nfgpoly.cc b/src/solvers/enumpoly/nfgpoly.cc index a77d730f53..3eff45f54b 100644 --- a/src/solvers/enumpoly/nfgpoly.cc +++ b/src/solvers/enumpoly/nfgpoly.cc @@ -140,8 +140,8 @@ EnumPolyStrategySupportSolve(const StrategySupportProfile &support, bool &is_sin std::list> EnumPolyStrategySolve(const Game &p_game, int p_stopAfter, double p_maxregret, - EnumPolyMixedStrategyObserverFunctionType p_onEquilibrium, - EnumPolyStrategySupportObserverFunctionType p_onSupport) + StrategyCallbackType p_onEquilibrium, + EnumPolyEventCallbackType p_onEvent) { const double scale = p_game->GetMaxPayoff() - p_game->GetMinPayoff(); if (scale != 0.0) { @@ -152,7 +152,7 @@ EnumPolyStrategySolve(const Game &p_game, int p_stopAfter, double p_maxregret, auto possible_supports = PossibleNashStrategySupports(p_game); for (auto support : possible_supports->m_supports) { - p_onSupport("candidate", support); + p_onEvent(EnumPolyCandidateSupportEvent{support}); bool is_singular; for (auto solution : EnumPolyStrategySupportSolve( support, is_singular, std::max(p_stopAfter - static_cast(ret.size()), 0))) { @@ -164,7 +164,7 @@ EnumPolyStrategySolve(const Game &p_game, int p_stopAfter, double p_maxregret, } if (is_singular) { - p_onSupport("singular", support); + p_onEvent(EnumPolySingularSupportEvent{support}); } if (p_stopAfter > 0 && static_cast(ret.size()) >= p_stopAfter) { break; diff --git a/src/solvers/enumpure/enumpure.h b/src/solvers/enumpure/enumpure.h index 52927dd0fd..a0eee8b4d4 100644 --- a/src/solvers/enumpure/enumpure.h +++ b/src/solvers/enumpure/enumpure.h @@ -56,7 +56,7 @@ inline std::list> EnumPureStrategySolve( for (const auto &profile : StrategyContingencies(p_game)) { if (IsNash(profile)) { solutions.push_back(profile->ToMixedStrategyProfile()); - p_onEquilibrium(solutions.back(), "NE"); + p_onEquilibrium(solutions.back()); } } return solutions; @@ -95,7 +95,7 @@ EnumPureAgentSolve(const Game &p_game, for (auto citer : BehaviorContingencies(BehaviorSupportProfile(p_game))) { if (IsAgentNash(citer)) { solutions.push_back(citer.ToMixedBehaviorProfile()); - p_onEquilibrium(solutions.back(), "NE"); + p_onEquilibrium(solutions.back()); } } return solutions; diff --git a/src/solvers/gnm/gnm.cc b/src/solvers/gnm/gnm.cc index a208b6db1e..abd9fcc404 100644 --- a/src/solvers/gnm/gnm.cc +++ b/src/solvers/gnm/gnm.cc @@ -33,7 +33,7 @@ namespace { std::list> Solve(const Game &p_game, const std::shared_ptr &p_rep, const cvector &p_pert, double p_lambdaEnd, int p_steps, int p_localNewtonInterval, int p_localNewtonMaxits, - std::function &, const std::string &)> &p_callback) + Nash::StrategyCallbackType p_onEquilibrium, Nash::GNMEventCallbackType p_onEvent) { const double FUZZ = 1e-12; const bool WOBBLE = false; @@ -49,12 +49,23 @@ Solve(const Game &p_game, const std::shared_ptr &p_rep, const cvector & throw UndefinedException("Perturbation vector must have at least one nonzero component."); } std::list> eqa; - p_callback(ToProfile(p_game, p_pert), "pert"); + const auto perturbation = ToProfile(p_game, p_pert); + p_onEvent(Nash::GNMPerturbationEvent{perturbation}); cvector norm_pert = p_pert / p_pert.norm(); std::list answers; std::string return_message; - auto callback = [p_game, p_callback](const std::string &label, const cvector &sigma) { - p_callback(ToProfile(p_game, sigma), label); + auto callback = [p_game, p_onEquilibrium, p_onEvent](const std::string &label, + const cvector &sigma) { + const auto profile = ToProfile(p_game, sigma); + if (label == "NE") { + p_onEquilibrium(profile); + } + else if (label == "start") { + p_onEvent(Nash::GNMStartEvent{profile}); + } + else { + p_onEvent(Nash::GNMStepEvent{profile, std::stod(label)}); + } }; GNM(*p_rep, norm_pert, answers, p_steps, FUZZ, p_localNewtonInterval, p_localNewtonMaxits, p_lambdaEnd, WOBBLE, THRESHOLD, callback, return_message); @@ -68,10 +79,10 @@ Solve(const Game &p_game, const std::shared_ptr &p_rep, const cvector & namespace Gambit::Nash { -std::list> GNMStrategySolve(const Game &p_game, double p_lambdaEnd, - int p_steps, int p_localNewtonInterval, - int p_localNewtonMaxits, - StrategyCallbackType p_callback) +std::list> +GNMStrategySolve(const Game &p_game, double p_lambdaEnd, int p_steps, int p_localNewtonInterval, + int p_localNewtonMaxits, StrategyCallbackType p_onEquilibrium, + GNMEventCallbackType p_onEvent) { if (!p_game->IsPerfectRecall()) { throw UndefinedException( @@ -88,13 +99,13 @@ std::list> GNMStrategySolve(const Game &p_game, dou pert[player->GetStrategies().front()] = 1.0; } return Solve(p_game, A, ToPerturbation(pert), p_lambdaEnd, p_steps, p_localNewtonInterval, - p_localNewtonMaxits, p_callback); + p_localNewtonMaxits, p_onEquilibrium, p_onEvent); } std::list> GNMStrategySolve(const MixedStrategyProfile &p_pert, double p_lambdaEnd, int p_steps, int p_localNewtonInterval, int p_localNewtonMaxits, - StrategyCallbackType p_callback) + StrategyCallbackType p_onEquilibrium, GNMEventCallbackType p_onEvent) { if (!p_pert.GetGame()->IsPerfectRecall()) { throw UndefinedException( @@ -102,7 +113,7 @@ GNMStrategySolve(const MixedStrategyProfile &p_pert, double p_lambdaEnd, } const std::shared_ptr A = BuildGame(p_pert.GetGame(), true); return Solve(p_pert.GetGame(), A, ToPerturbation(p_pert), p_lambdaEnd, p_steps, - p_localNewtonInterval, p_localNewtonMaxits, p_callback); + p_localNewtonInterval, p_localNewtonMaxits, p_onEquilibrium, p_onEvent); } } // end namespace Gambit::Nash diff --git a/src/solvers/gnm/gnm.h b/src/solvers/gnm/gnm.h index fa38b4646b..182ff4d1e7 100644 --- a/src/solvers/gnm/gnm.h +++ b/src/solvers/gnm/gnm.h @@ -23,6 +23,8 @@ #ifndef GAMBIT_NASH_GNM_H #define GAMBIT_NASH_GNM_H +#include + #include "games/nash.h" #include "solvers/gtracer/gtracer.h" @@ -33,17 +35,37 @@ const int GNM_LOCAL_NEWTON_INTERVAL_DEFAULT = 3; const int GNM_LOCAL_NEWTON_MAXITS_DEFAULT = 10; const int GNM_STEPS_DEFAULT = 100; +struct GNMPerturbationEvent { + const MixedStrategyProfile &profile; +}; + +struct GNMStartEvent { + const MixedStrategyProfile &profile; +}; + +struct GNMStepEvent { + const MixedStrategyProfile &profile; + double lambda; +}; + +using GNMEvent = std::variant; +using GNMEventCallbackType = std::function; + +inline void NullGNMEventCallback(const GNMEvent &) {} + std::list> GNMStrategySolve(const Game &p_game, double p_lambdaEnd, int p_steps, int p_localNewtonInterval, int p_localNewtonMaxits, - StrategyCallbackType p_callback = NullStrategyCallback); + StrategyCallbackType p_onEquilibrium = NullStrategyCallback, + GNMEventCallbackType p_onEvent = NullGNMEventCallback); /// @brief Compute the mixed strategy equilibria accessible via the initial ray determined /// by \p p_profile using the Global Newton method std::list> GNMStrategySolve(const MixedStrategyProfile &p_profile, double p_lambdaEnd, int p_steps, int p_localNewtonInterval, int p_localNewtonMaxits, - StrategyCallbackType p_callback = NullStrategyCallback); + StrategyCallbackType p_onEquilibrium = NullStrategyCallback, + GNMEventCallbackType p_onEvent = NullGNMEventCallback); } // end namespace Gambit::Nash diff --git a/src/solvers/ipa/ipa.cc b/src/solvers/ipa/ipa.cc index 3b32ca09a5..27aefaa24e 100644 --- a/src/solvers/ipa/ipa.cc +++ b/src/solvers/ipa/ipa.cc @@ -29,8 +29,8 @@ using namespace Gambit::gametracer; namespace Gambit::Nash { -std::list> IPAStrategySolve(const Game &p_game, - StrategyCallbackType p_callback) +std::list> +IPAStrategySolve(const Game &p_game, StrategyCallbackType p_onEquilibrium) { MixedStrategyProfile pert = p_game->NewMixedStrategyProfile(0.0); for (const auto &player : p_game->GetPlayers()) { @@ -41,12 +41,12 @@ std::list> IPAStrategySolve(const Game &p_game, for (auto player : p_game->GetPlayers()) { pert[player->GetStrategies().front()] = 1.0; } - return IPAStrategySolve(pert, p_callback); + return IPAStrategySolve(pert, p_onEquilibrium); } std::list> IPAStrategySolve(const MixedStrategyProfile &p_pert, - StrategyCallbackType p_callback) + StrategyCallbackType p_onEquilibrium) { if (!p_pert.GetGame()->IsPerfectRecall()) { throw UndefinedException( @@ -67,7 +67,7 @@ IPAStrategySolve(const MixedStrategyProfile &p_pert, std::list> solutions; solutions.push_back(ToProfile(p_pert.GetGame(), ans)); - p_callback(solutions.back(), "NE"); + p_onEquilibrium(solutions.back()); return solutions; } diff --git a/src/solvers/ipa/ipa.h b/src/solvers/ipa/ipa.h index fb0982b862..e310ace056 100644 --- a/src/solvers/ipa/ipa.h +++ b/src/solvers/ipa/ipa.h @@ -29,11 +29,11 @@ namespace Gambit::Nash { std::list> IPAStrategySolve(const Game &p_game, - StrategyCallbackType p_callback = NullStrategyCallback); + StrategyCallbackType p_onEquilibrium = NullStrategyCallback); std::list> IPAStrategySolve(const MixedStrategyProfile &p_pert, - StrategyCallbackType p_callback = NullStrategyCallback); + StrategyCallbackType p_onEquilibrium = NullStrategyCallback); } // namespace Gambit::Nash diff --git a/src/solvers/lcp/efglcp.cc b/src/solvers/lcp/efglcp.cc index 73b85d7390..94d7a01fe5 100644 --- a/src/solvers/lcp/efglcp.cc +++ b/src/solvers/lcp/efglcp.cc @@ -152,7 +152,7 @@ std::list> NashLcpBehaviorSolver::Solve(const Game &p GetProfile(tab, profile, sol, p_game->GetRoot(), 1, 1, solution); profile.UndefinedToCentroid(); solution.m_equilibria.push_back(profile); - this->m_onEquilibrium(profile, "NE"); + this->m_onEquilibrium(profile); return solution.m_equilibria; } diff --git a/src/solvers/lcp/nfglcp.cc b/src/solvers/lcp/nfglcp.cc index cfa502aa8a..472fb72f42 100644 --- a/src/solvers/lcp/nfglcp.cc +++ b/src/solvers/lcp/nfglcp.cc @@ -203,7 +203,7 @@ NashLcpStrategySolver::OnBFS(const Game &p_game, linalg::LHTableau &p_tabl } } - m_onEquilibrium(profile, "NE"); + m_onEquilibrium(profile); p_solution.m_equilibria.push_back(profile); if (m_stopAfter > 0 && p_solution.EquilibriumCount() >= m_stopAfter) { diff --git a/src/solvers/liap/efgliap.cc b/src/solvers/liap/efgliap.cc index 8b827db40e..b9b67d9aff 100644 --- a/src/solvers/liap/efgliap.cc +++ b/src/solvers/liap/efgliap.cc @@ -127,9 +127,10 @@ MixedBehaviorProfile EnforceNonnegativity(const MixedBehaviorProfile> LiapAgentSolve(const MixedBehaviorProfile &p_start, - double p_maxregret, int p_maxitsN, - BehaviorCallbackType p_callback) +std::list> +LiapAgentSolve(const MixedBehaviorProfile &p_start, double p_maxregret, int p_maxitsN, + BehaviorCallbackType p_onEquilibrium, + LiapEventCallbackType> p_onEvent) { if (!p_start.GetGame()->IsPerfectRecall()) { throw UndefinedException( @@ -139,7 +140,7 @@ std::list> LiapAgentSolve(const MixedBehaviorProfil std::list> solutions; MixedBehaviorProfile p(p_start); - p_callback(p, "start"); + p_onEvent(LiapStartEvent>{p}); const AgentLyapunovFunction F(p); const Matrix xi(p.BehaviorProfileLength(), p.BehaviorProfileLength()); @@ -160,12 +161,13 @@ std::list> LiapAgentSolve(const MixedBehaviorProfil } auto p2 = EnforceNonnegativity(p); - if (p2.GetAgentMaxRegret() * F.GetScale() < p_maxregret) { - p_callback(p2, "NE"); + const double regret = p2.GetAgentMaxRegret() * F.GetScale(); + if (regret < p_maxregret) { + p_onEquilibrium(p2); solutions.push_back(p2); } else { - p_callback(p2, "end"); + p_onEvent(LiapEndEvent>{p2, regret}); } return solutions; } diff --git a/src/solvers/liap/liap.h b/src/solvers/liap/liap.h index 7cd36e2e11..16cc649c2f 100644 --- a/src/solvers/liap/liap.h +++ b/src/solvers/liap/liap.h @@ -23,17 +23,39 @@ #ifndef GAMBIT_NASH_LIAP_H #define GAMBIT_NASH_LIAP_H +#include #include "games/nash.h" namespace Gambit::Nash { +template struct LiapStartEvent { + const Profile &profile; +}; + +template struct LiapEndEvent { + const Profile &profile; + double regret; +}; + +template +using LiapEvent = std::variant, LiapEndEvent>; + +template +using LiapEventCallbackType = std::function &)>; + +template void NullLiapEventCallback(const LiapEvent &) {} + std::list> LiapAgentSolve(const MixedBehaviorProfile &p_start, double p_maxregret, int p_maxitsN, - BehaviorCallbackType p_callback = NullBehaviorCallback); + BehaviorCallbackType p_onEquilibrium = NullBehaviorCallback, + LiapEventCallbackType> p_onEvent = + NullLiapEventCallback>); std::list> LiapStrategySolve(const MixedStrategyProfile &p_start, double p_maxregret, int p_maxitsN, - StrategyCallbackType p_callback = NullStrategyCallback); + StrategyCallbackType p_onEquilibrium = NullStrategyCallback, + LiapEventCallbackType> p_onEvent = + NullLiapEventCallback>); } // namespace Gambit::Nash diff --git a/src/solvers/liap/nfgliap.cc b/src/solvers/liap/nfgliap.cc index 0ae78ec495..c5ecf0488c 100644 --- a/src/solvers/liap/nfgliap.cc +++ b/src/solvers/liap/nfgliap.cc @@ -135,7 +135,8 @@ MixedStrategyProfile EnforceNonnegativity(const MixedStrategyProfile> LiapStrategySolve(const MixedStrategyProfile &p_start, double p_maxregret, int p_maxitsN, - StrategyCallbackType p_callback) + StrategyCallbackType p_onEquilibrium, + LiapEventCallbackType> p_onEvent) { if (!p_start.GetGame()->IsPerfectRecall()) { throw UndefinedException( @@ -145,7 +146,7 @@ LiapStrategySolve(const MixedStrategyProfile &p_start, double p_maxregre std::list> solutions; MixedStrategyProfile p(p_start); - p_callback(p, "start"); + p_onEvent(LiapStartEvent>{p}); const StrategicLyapunovFunction F(p); ConjugatePRMinimizer minimizer(p.MixedProfileLength()); @@ -165,12 +166,13 @@ LiapStrategySolve(const MixedStrategyProfile &p_start, double p_maxregre } p = EnforceNonnegativity(p); - if (p.GetMaxRegret() * F.GetScale() < p_maxregret) { - p_callback(p, "NE"); + const double regret = p.GetMaxRegret() * F.GetScale(); + if (regret < p_maxregret) { + p_onEquilibrium(p); solutions.push_back(p); } else { - p_callback(p, "end"); + p_onEvent(LiapEndEvent>{p, regret}); } return solutions; diff --git a/src/solvers/logit/efglogit.cc b/src/solvers/logit/efglogit.cc index cae2f769d6..5a589a8a8c 100644 --- a/src/solvers/logit/efglogit.cc +++ b/src/solvers/logit/efglogit.cc @@ -244,8 +244,9 @@ void EquationSystem::GetJacobian(const Vector &p_point, Matrix & class TracingCallbackFunction { public: - TracingCallbackFunction(const Game &p_game, MixedBehaviorObserverFunctionType p_observer) - : m_game(p_game), m_observer(p_observer) + TracingCallbackFunction(const Game &p_game, + LogitEventCallbackType p_onEvent) + : m_game(p_game), m_onEvent(p_onEvent) { } ~TracingCallbackFunction() = default; @@ -255,7 +256,7 @@ class TracingCallbackFunction { private: Game m_game; - MixedBehaviorObserverFunctionType m_observer; + LogitEventCallbackType m_onEvent; std::list m_profiles; }; @@ -263,13 +264,13 @@ void TracingCallbackFunction::AppendPoint(const Vector &p_point) { const MixedBehaviorProfile profile(PointToProfile(m_game, p_point)); m_profiles.emplace_back(profile, p_point.back(), 1.0); - m_observer(m_profiles.back()); + m_onEvent(LogitPathEvent{m_profiles.back()}); } class EstimatorCallbackFunction { public: EstimatorCallbackFunction(const Game &p_game, const Vector &p_frequencies, - MixedBehaviorObserverFunctionType p_observer); + LogitEventCallbackType p_onEvent); ~EstimatorCallbackFunction() = default; void EvaluatePoint(const Vector &p_point); @@ -278,14 +279,14 @@ class EstimatorCallbackFunction { private: Game m_game; const Vector &m_frequencies; - MixedBehaviorObserverFunctionType m_observer; + LogitEventCallbackType m_onEvent; LogitQREMixedBehaviorProfile m_bestProfile; }; -EstimatorCallbackFunction::EstimatorCallbackFunction(const Game &p_game, - const Vector &p_frequencies, - MixedBehaviorObserverFunctionType p_observer) - : m_game(p_game), m_frequencies(p_frequencies), m_observer(p_observer), +EstimatorCallbackFunction::EstimatorCallbackFunction( + const Game &p_game, const Vector &p_frequencies, + LogitEventCallbackType p_onEvent) + : m_game(p_game), m_frequencies(p_frequencies), m_onEvent(p_onEvent), m_bestProfile(MixedBehaviorProfile(p_game), 0.0, LogLike(p_frequencies, static_cast &>( MixedBehaviorProfile(p_game)))) @@ -298,7 +299,7 @@ void EstimatorCallbackFunction::EvaluatePoint(const Vector &p_point) auto qre = LogitQREMixedBehaviorProfile( profile, p_point.back(), LogLike(m_frequencies, static_cast &>(profile))); - m_observer(qre); + m_onEvent(LogitPathEvent{qre}); if (qre.GetLogLike() > m_bestProfile.GetLogLike()) { m_bestProfile = qre; } @@ -311,7 +312,8 @@ namespace Gambit { std::list LogitBehaviorSolve(const LogitQREMixedBehaviorProfile &p_start, double p_regret, double p_omega, double p_firstStep, double p_maxAccel, - MixedBehaviorObserverFunctionType p_observer) + Nash::BehaviorCallbackType p_onEquilibrium, + LogitEventCallbackType p_onEvent) { if (p_start.size() == 0) { return {p_start}; @@ -326,7 +328,7 @@ LogitBehaviorSolve(const LogitQREMixedBehaviorProfile &p_start, double p_regret, } const Game game = p_start.GetGame(); Vector x(ProfileToPoint(p_start)); - TracingCallbackFunction callback(game, p_observer); + TracingCallbackFunction callback(game, p_onEvent); EquationSystem system(game); tracer.TracePath( [&system](const Vector &p_point, Vector &p_lhs) { @@ -340,14 +342,16 @@ LogitBehaviorSolve(const LogitQREMixedBehaviorProfile &p_start, double p_regret, return RegretTerminationFunction(game, p_point, p_regret); }, [&callback](const Vector &p_point) -> void { callback.AppendPoint(p_point); }); - return callback.GetProfiles(); + const auto &profiles = callback.GetProfiles(); + p_onEquilibrium(profiles.back().GetProfile()); + return profiles; } std::list LogitBehaviorSolveLambda(const LogitQREMixedBehaviorProfile &p_start, const std::list &p_targetLambda, double p_omega, double p_firstStep, double p_maxAccel, - MixedBehaviorObserverFunctionType p_observer) + LogitEventCallbackType p_onEvent) { if (p_start.size() == 0) { return {p_start}; @@ -358,7 +362,7 @@ LogitBehaviorSolveLambda(const LogitQREMixedBehaviorProfile &p_start, const Game game = p_start.GetGame(); Vector x(ProfileToPoint(p_start)); - TracingCallbackFunction callback(game, p_observer); + TracingCallbackFunction callback(game, p_onEvent); EquationSystem system(game); std::list ret; for (auto lam : p_targetLambda) { @@ -382,7 +386,7 @@ LogitBehaviorSolveLambda(const LogitQREMixedBehaviorProfile &p_start, LogitQREMixedBehaviorProfile LogitBehaviorEstimate(const MixedBehaviorProfile &p_frequencies, double p_maxLambda, double p_omega, double p_stopAtLocal, double p_firstStep, double p_maxAccel, - MixedBehaviorObserverFunctionType p_observer) + LogitEventCallbackType p_onEvent) { const LogitQREMixedBehaviorProfile start(p_frequencies.GetGame()); if (start.size() == 0) { @@ -395,7 +399,7 @@ LogitBehaviorEstimate(const MixedBehaviorProfile &p_frequencies, double Vector x(ProfileToPoint(start)), restart(x); const Vector freq_vector(static_cast &>(p_frequencies)); EstimatorCallbackFunction callback( - start.GetGame(), static_cast &>(p_frequencies), p_observer); + start.GetGame(), static_cast &>(p_frequencies), p_onEvent); EquationSystem system(start.GetGame()); while (true) { tracer.TracePath( diff --git a/src/solvers/logit/logit.h b/src/solvers/logit/logit.h index 8d619d44e0..926e3778e7 100644 --- a/src/solvers/logit/logit.h +++ b/src/solvers/logit/logit.h @@ -24,6 +24,9 @@ #define SOLVERS_LOGIT_H #include +#include + +#include "games/nash.h" namespace Gambit { @@ -79,48 +82,56 @@ template <> inline size_t LogitQRE>::size() const using LogitQREMixedStrategyProfile = LogitQRE>; -using MixedStrategyObserverFunctionType = - std::function; +template struct LogitPathEvent { + const QRE &qre; +}; + +template using LogitEvent = std::variant>; +template using LogitEventCallbackType = std::function &)>; -inline void NullMixedStrategyObserver(const LogitQREMixedStrategyProfile &) {} +template void NullLogitEventCallback(const LogitEvent &) {} std::list LogitStrategySolve( const LogitQREMixedStrategyProfile &p_start, double p_regret, double p_omega, double p_firstStep, double p_maxAccel, - const MixedStrategyObserverFunctionType &p_observer = NullMixedStrategyObserver); + Nash::StrategyCallbackType p_onEquilibrium = Nash::NullStrategyCallback, + LogitEventCallbackType p_onEvent = + NullLogitEventCallback); -std::list LogitStrategySolveLambda( - const LogitQREMixedStrategyProfile &p_start, const std::list &p_targetLambda, - double p_omega, double p_firstStep, double p_maxAccel, - const MixedStrategyObserverFunctionType &p_observer = NullMixedStrategyObserver); +std::list +LogitStrategySolveLambda(const LogitQREMixedStrategyProfile &p_start, + const std::list &p_targetLambda, double p_omega, + double p_firstStep, double p_maxAccel, + LogitEventCallbackType p_onEvent = + NullLogitEventCallback); LogitQREMixedStrategyProfile LogitStrategyEstimate(const MixedStrategyProfile &p_frequencies, double p_maxLambda, double p_omega, double p_stopAtLocal, double p_firstStep, double p_maxAccel, - MixedStrategyObserverFunctionType p_observer = NullMixedStrategyObserver); + LogitEventCallbackType p_onEvent = + NullLogitEventCallback); using LogitQREMixedBehaviorProfile = LogitQRE>; -using MixedBehaviorObserverFunctionType = - std::function; - -inline void NullMixedBehaviorObserver(const LogitQREMixedBehaviorProfile &) {} - -std::list -LogitBehaviorSolve(const LogitQREMixedBehaviorProfile &p_start, double p_regret, double p_omega, - double p_firstStep, double p_maxAccel, - MixedBehaviorObserverFunctionType p_observer = NullMixedBehaviorObserver); +std::list LogitBehaviorSolve( + const LogitQREMixedBehaviorProfile &p_start, double p_regret, double p_omega, + double p_firstStep, double p_maxAccel, + Nash::BehaviorCallbackType p_onEquilibrium = Nash::NullBehaviorCallback, + LogitEventCallbackType p_onEvent = + NullLogitEventCallback); std::list LogitBehaviorSolveLambda(const LogitQREMixedBehaviorProfile &p_start, const std::list &p_targetLambda, double p_omega, double p_firstStep, double p_maxAccel, - MixedBehaviorObserverFunctionType p_observer = NullMixedBehaviorObserver); + LogitEventCallbackType p_onEvent = + NullLogitEventCallback); LogitQREMixedBehaviorProfile LogitBehaviorEstimate(const MixedBehaviorProfile &p_frequencies, double p_maxLambda, double p_omega, double p_stopAtLocal, double p_firstStep, double p_maxAccel, - MixedBehaviorObserverFunctionType p_observer = NullMixedBehaviorObserver); + LogitEventCallbackType p_onEvent = + NullLogitEventCallback); } // namespace Gambit diff --git a/src/solvers/logit/nfglogit.cc b/src/solvers/logit/nfglogit.cc index e8e9f81679..f421a368c3 100644 --- a/src/solvers/logit/nfglogit.cc +++ b/src/solvers/logit/nfglogit.cc @@ -283,8 +283,9 @@ void EquationSystem::GetJacobian(const Vector &p_point, Matrix & class TracingCallbackFunction { public: - TracingCallbackFunction(const Game &p_game, const MixedStrategyObserverFunctionType &p_observer) - : m_game(p_game), m_observer(p_observer) + TracingCallbackFunction(const Game &p_game, + LogitEventCallbackType p_onEvent) + : m_game(p_game), m_onEvent(p_onEvent) { } ~TracingCallbackFunction() = default; @@ -294,7 +295,7 @@ class TracingCallbackFunction { private: Game m_game; - MixedStrategyObserverFunctionType m_observer; + LogitEventCallbackType m_onEvent; std::list m_profiles; }; @@ -303,13 +304,13 @@ void TracingCallbackFunction::AppendPoint(const Vector &p_point) MixedStrategyProfile profile(m_game->NewMixedStrategyProfile(0.0)); PointToProfile(profile, p_point); m_profiles.emplace_back(profile, p_point.back(), 1.0); - m_observer(m_profiles.back()); + m_onEvent(LogitPathEvent{m_profiles.back()}); } class EstimatorCallbackFunction { public: EstimatorCallbackFunction(const Game &p_game, const Vector &p_frequencies, - const MixedStrategyObserverFunctionType &p_observer); + LogitEventCallbackType p_onEvent); ~EstimatorCallbackFunction() = default; void EvaluatePoint(const Vector &p_point); @@ -319,14 +320,14 @@ class EstimatorCallbackFunction { private: Game m_game; const Vector &m_frequencies; - MixedStrategyObserverFunctionType m_observer; + LogitEventCallbackType m_onEvent; LogitQREMixedStrategyProfile m_bestProfile; }; EstimatorCallbackFunction::EstimatorCallbackFunction( const Game &p_game, const Vector &p_frequencies, - const MixedStrategyObserverFunctionType &p_observer) - : m_game(p_game), m_frequencies(p_frequencies), m_observer(p_observer), + LogitEventCallbackType p_onEvent) + : m_game(p_game), m_frequencies(p_frequencies), m_onEvent(p_onEvent), m_bestProfile(p_game->NewMixedStrategyProfile(0.0), 0.0, LogLike(p_frequencies, p_game->NewMixedStrategyProfile(0.0).GetProbVector())) { @@ -338,7 +339,7 @@ void EstimatorCallbackFunction::EvaluatePoint(const Vector &p_point) PointToProfile(profile, p_point); auto qre = LogitQREMixedStrategyProfile(profile, p_point.back(), LogLike(m_frequencies, profile.GetProbVector())); - m_observer(qre); + m_onEvent(LogitPathEvent{qre}); if (qre.GetLogLike() > m_bestProfile.GetLogLike()) { m_bestProfile = qre; } @@ -349,7 +350,8 @@ void EstimatorCallbackFunction::EvaluatePoint(const Vector &p_point) std::list LogitStrategySolve(const LogitQREMixedStrategyProfile &p_start, double p_regret, double p_omega, double p_firstStep, double p_maxAccel, - const MixedStrategyObserverFunctionType &p_observer) + Nash::StrategyCallbackType p_onEquilibrium, + LogitEventCallbackType p_onEvent) { if (p_start.size() == 0) { return {p_start}; @@ -365,7 +367,7 @@ LogitStrategySolve(const LogitQREMixedStrategyProfile &p_start, double p_regret, const Game game = p_start.GetGame(); Vector x(ProfileToPoint(p_start)); - TracingCallbackFunction callback(game, p_observer); + TracingCallbackFunction callback(game, p_onEvent); EquationSystem system(game); tracer.TracePath( [&system](const Vector &p_point, Vector &p_lhs) { @@ -379,14 +381,16 @@ LogitStrategySolve(const LogitQREMixedStrategyProfile &p_start, double p_regret, return RegretTerminationFunction(p_start.GetGame(), p_point, p_regret); }, [&callback](const Vector &p_point) -> void { callback.AppendPoint(p_point); }); - return callback.GetProfiles(); + const auto &profiles = callback.GetProfiles(); + p_onEquilibrium(profiles.back().GetProfile()); + return profiles; } std::list LogitStrategySolveLambda(const LogitQREMixedStrategyProfile &p_start, const std::list &p_targetLambda, double p_omega, double p_firstStep, double p_maxAccel, - const MixedStrategyObserverFunctionType &p_observer) + LogitEventCallbackType p_onEvent) { if (p_start.size() == 0) { return {p_start}; @@ -397,7 +401,7 @@ LogitStrategySolveLambda(const LogitQREMixedStrategyProfile &p_start, const Game game = p_start.GetGame(); Vector x(ProfileToPoint(p_start)); - TracingCallbackFunction callback(game, p_observer); + TracingCallbackFunction callback(game, p_onEvent); std::list ret; EquationSystem system(game); for (auto lam : p_targetLambda) { @@ -421,7 +425,7 @@ LogitStrategySolveLambda(const LogitQREMixedStrategyProfile &p_start, LogitQREMixedStrategyProfile LogitStrategyEstimate(const MixedStrategyProfile &p_frequencies, double p_maxLambda, double p_omega, double p_stopAtLocal, double p_firstStep, double p_maxAccel, - MixedStrategyObserverFunctionType p_observer) + LogitEventCallbackType p_onEvent) { const LogitQREMixedStrategyProfile start(p_frequencies.GetGame()); if (start.size() == 0) { @@ -434,7 +438,7 @@ LogitStrategyEstimate(const MixedStrategyProfile &p_frequencies, double const Game game = start.GetGame(); Vector x(ProfileToPoint(start)), restart(x); const Vector freq_vector(p_frequencies.GetProbVector()); - EstimatorCallbackFunction callback(game, p_frequencies.GetProbVector(), p_observer); + EstimatorCallbackFunction callback(game, p_frequencies.GetProbVector(), p_onEvent); EquationSystem system(game); while (true) { tracer.TracePath( diff --git a/src/solvers/lp/lp.cc b/src/solvers/lp/lp.cc index 278e9d521c..eb741af5cf 100644 --- a/src/solvers/lp/lp.cc +++ b/src/solvers/lp/lp.cc @@ -201,7 +201,7 @@ std::list> LpBehaviorSolve(const Game &p_game, MixedBehaviorProfile profile(p_game); data.GetBehavior(profile, primal, dual, p_game->GetRoot(), 1, 1); profile.UndefinedToCentroid(); - p_onEquilibrium(profile, "NE"); + p_onEquilibrium(profile); solution.push_back(profile); return solution; } @@ -264,7 +264,7 @@ std::list> LpStrategySolve(const Game &p_game, for (int j = 1; j <= k; j++) { eqm[p_game->GetPlayer(2)->GetStrategy(j)] = dual[j]; } - p_onEquilibrium(eqm, "NE"); + p_onEquilibrium(eqm); std::list> solution; solution.push_back(eqm); return solution; diff --git a/src/solvers/simpdiv/simpdiv.cc b/src/solvers/simpdiv/simpdiv.cc index b9fc15f27f..ebd55ff209 100644 --- a/src/solvers/simpdiv/simpdiv.cc +++ b/src/solvers/simpdiv/simpdiv.cc @@ -31,9 +31,10 @@ class NashSimpdivStrategySolver { explicit NashSimpdivStrategySolver( int p_gridResize = 2, int p_leashLength = 0, const Rational &p_maxregret = Rational(1, 1000000), - StrategyCallbackType p_onEquilibrium = NullStrategyCallback) + StrategyCallbackType p_onEquilibrium = NullStrategyCallback, + SimpdivEventCallbackType p_onEvent = NullSimpdivEventCallback) : m_gridResize(p_gridResize), m_leashLength((p_leashLength > 0) ? p_leashLength : 32000), - m_maxregret(p_maxregret), m_onEquilibrium(p_onEquilibrium) + m_maxregret(p_maxregret), m_onEquilibrium(p_onEquilibrium), m_onEvent(p_onEvent) { } ~NashSimpdivStrategySolver() = default; @@ -46,6 +47,7 @@ class NashSimpdivStrategySolver { int m_gridResize, m_leashLength; Rational m_maxregret; StrategyCallbackType m_onEquilibrium; + SimpdivEventCallbackType m_onEvent; class State; @@ -506,18 +508,18 @@ NashSimpdivStrategySolver::Solve(const MixedStrategyProfile &p_start) const Rational scale = p_start.GetGame()->GetMaxPayoff() - p_start.GetGame()->GetMinPayoff(); MixedStrategyProfile y(p_start); - m_onEquilibrium(y, "start"); + m_onEvent(SimpdivStartEvent{y}); while (true) { d /= Rational(m_gridResize); const Rational regret = Simplex(y, d); - m_onEquilibrium(y, std::to_string(d)); + m_onEvent(SimpdivRefinementEvent{y, d, regret}); if (regret <= m_maxregret * scale) { break; } } - m_onEquilibrium(y, "NE"); + m_onEquilibrium(y); std::list> sol; sol.push_back(y); return sol; @@ -549,9 +551,11 @@ NashSimpdivStrategySolver::Solve(const Game &p_game) const std::list> SimpdivStrategySolve(const MixedStrategyProfile &p_start, const Rational &p_maxregret, int p_gridResize, int p_leashLength, - StrategyCallbackType p_onEquilibrium) + StrategyCallbackType p_onEquilibrium, + SimpdivEventCallbackType p_onEvent) { - return NashSimpdivStrategySolver(p_gridResize, p_leashLength, p_maxregret, p_onEquilibrium) + return NashSimpdivStrategySolver(p_gridResize, p_leashLength, p_maxregret, p_onEquilibrium, + p_onEvent) .Solve(p_start); } diff --git a/src/solvers/simpdiv/simpdiv.h b/src/solvers/simpdiv/simpdiv.h index c246ccac13..7ffc2784fa 100644 --- a/src/solvers/simpdiv/simpdiv.h +++ b/src/solvers/simpdiv/simpdiv.h @@ -23,10 +23,26 @@ #ifndef GAMBIT_NASH_SIMPDIV_H #define GAMBIT_NASH_SIMPDIV_H +#include #include "games/nash.h" namespace Gambit::Nash { +struct SimpdivStartEvent { + const MixedStrategyProfile &profile; +}; + +struct SimpdivRefinementEvent { + const MixedStrategyProfile &profile; + Rational gridSize; + Rational regret; +}; + +using SimpdivEvent = std::variant; +using SimpdivEventCallbackType = std::function; + +inline void NullSimpdivEventCallback(const SimpdivEvent &) {} + /// /// This is a simplicial subdivision algorithm with restart, for finding /// mixed strategy solutions to general finite n-person games. It is based on @@ -36,7 +52,8 @@ std::list> SimpdivStrategySolve( const MixedStrategyProfile &p_start, const Rational &p_maxregret = Rational(1, 1000000), int p_gridResize = 2, int p_leashLength = 0, - StrategyCallbackType p_onEquilibrium = NullStrategyCallback); + StrategyCallbackType p_onEquilibrium = NullStrategyCallback, + SimpdivEventCallbackType p_onEvent = NullSimpdivEventCallback); } // end namespace Gambit::Nash diff --git a/src/tools/enummixed/enummixed.cc b/src/tools/enummixed/enummixed.cc index 54ba260bf7..c55306cb71 100644 --- a/src/tools/enummixed/enummixed.cc +++ b/src/tools/enummixed/enummixed.cc @@ -132,9 +132,7 @@ int main(int argc, char *argv[]) if (useFloat) { auto renderer = MakeMixedStrategyProfileRenderer(std::cout, numDecimals, false); auto solution = EnumMixedStrategySolveDetailed( - game, [&](const MixedStrategyProfile &p, const std::string &label) { - renderer->Render(p, label); - }); + game, [&](const MixedStrategyProfile &p) { renderer->Render(p); }); if (showConnect) { PrintCliques(solution->GetCliques(), renderer); } @@ -142,9 +140,7 @@ int main(int argc, char *argv[]) else { auto renderer = MakeMixedStrategyProfileRenderer(std::cout, numDecimals, false); auto solution = EnumMixedStrategySolveDetailed( - game, [&](const MixedStrategyProfile &p, const std::string &label) { - renderer->Render(p, label); - }); + game, [&](const MixedStrategyProfile &p) { renderer->Render(p); }); if (showConnect) { PrintCliques(solution->GetCliques(), renderer); } diff --git a/src/tools/enumpoly/enumpoly.cc b/src/tools/enumpoly/enumpoly.cc index 8c14baf4bb..b6a0b70f79 100644 --- a/src/tools/enumpoly/enumpoly.cc +++ b/src/tools/enumpoly/enumpoly.cc @@ -23,6 +23,7 @@ #include #include #include +#include #include "gambit.h" #include "solvers/enumpoly/enumpoly.h" @@ -117,6 +118,21 @@ void PrintSupport(std::ostream &p_stream, const std::string &p_label, p_stream << std::endl; } +template void PrintEnumPolyEvent(const EnumPolyEvent &p_event) +{ + std::visit( + [](const auto &event) { + using Event = std::decay_t; + if constexpr (std::is_same_v>) { + PrintSupport(std::cout, "candidate", event.support); + } + else if constexpr (std::is_same_v>) { + PrintSupport(std::cout, "singular", event.support); + } + }, + p_event); +} + int main(int argc, char *argv[]) { opterr = 0; @@ -199,17 +215,13 @@ int main(int argc, char *argv[]) EnumPolyStrategySolve( game, stopAfter, maxregret, [](const MixedStrategyProfile &eqm) { PrintProfile(std::cout, "NE", eqm); }, - [](const std::string &label, const StrategySupportProfile &support) { - PrintSupport(std::cout, label, support); - }); + [](const EnumPolyEvent &event) { PrintEnumPolyEvent(event); }); } else { EnumPolyBehaviorSolve( game, stopAfter, maxregret, [](const MixedBehaviorProfile &eqm) { PrintProfile(std::cout, "NE", eqm); }, - [](const std::string &label, const BehaviorSupportProfile &support) { - PrintSupport(std::cout, label, support); - }); + [](const EnumPolyEvent &event) { PrintEnumPolyEvent(event); }); } return 0; } diff --git a/src/tools/enumpure/enumpure.cc b/src/tools/enumpure/enumpure.cc index f0e5eb2431..f00d1a48b2 100644 --- a/src/tools/enumpure/enumpure.cc +++ b/src/tools/enumpure/enumpure.cc @@ -125,12 +125,12 @@ int main(int argc, char *argv[]) } if (game->IsTree() && solveAgent) { - EnumPureAgentSolve(game, [&](const MixedBehaviorProfile &p, - const std::string &label) { renderer->Render(p, label); }); + EnumPureAgentSolve(game, + [&](const MixedBehaviorProfile &p) { renderer->Render(p); }); } else { - EnumPureStrategySolve(game, [&](const MixedStrategyProfile &p, - const std::string &label) { renderer->Render(p, label); }); + EnumPureStrategySolve(game, + [&](const MixedStrategyProfile &p) { renderer->Render(p); }); } return 0; } diff --git a/src/tools/gt/nfggnm.cc b/src/tools/gt/nfggnm.cc index be3b6abd4b..51bbe40546 100644 --- a/src/tools/gt/nfggnm.cc +++ b/src/tools/gt/nfggnm.cc @@ -24,6 +24,7 @@ #include #include #include +#include #include "gambit.h" #include "tools/util.h" #include "solvers/gnm/gnm.h" @@ -31,6 +32,29 @@ using namespace Gambit; using namespace Gambit::Nash; +namespace { + +void RenderGNMEvent(const GNMEvent &p_event, + const std::shared_ptr> &p_renderer) +{ + std::visit( + [p_renderer](const auto &event) { + using Event = std::decay_t; + if constexpr (std::is_same_v) { + p_renderer->Render(event.profile, "pert"); + } + else if constexpr (std::is_same_v) { + p_renderer->Render(event.profile, "start"); + } + else if constexpr (std::is_same_v) { + p_renderer->Render(event.profile, lexical_cast(event.lambda)); + } + }, + p_event); +} + +} // namespace + extern Array> ReadStrategyPerturbations(const Game &p_game, std::istream &p_stream); extern Array> RandomStrategyPerturbations(const Game &p_game, @@ -184,13 +208,16 @@ int main(int argc, char *argv[]) perts = RandomStrategyPerturbations(game, numVectors); } for (auto pert : perts) { - GNMStrategySolve(pert, lambdaEnd, steps, localNewtonInterval, localNewtonMaxits, - [renderer, verbose](const MixedStrategyProfile &p_profile, - const std::string &p_label) { - if (p_label == "NE" || verbose) { - renderer->Render(p_profile, p_label); - } - }); + GNMStrategySolve( + pert, lambdaEnd, steps, localNewtonInterval, localNewtonMaxits, + [renderer](const MixedStrategyProfile &p_profile) { + renderer->Render(p_profile); + }, + [renderer, verbose](const GNMEvent &p_event) { + if (verbose) { + RenderGNMEvent(p_event, renderer); + } + }); } return 0; } diff --git a/src/tools/gt/nfgipa.cc b/src/tools/gt/nfgipa.cc index ed0de1f4a7..4b4031f15f 100644 --- a/src/tools/gt/nfgipa.cc +++ b/src/tools/gt/nfgipa.cc @@ -132,11 +132,8 @@ int main(int argc, char *argv[]) } for (auto pert : perts) { - IPAStrategySolve(pert, [renderer](const MixedStrategyProfile &p_profile, - const std::string &p_label) { - if (p_label == "NE") { - renderer->Render(p_profile, p_label); - } + IPAStrategySolve(pert, [renderer](const MixedStrategyProfile &p_profile) { + renderer->Render(p_profile); }); } return 0; diff --git a/src/tools/lcp/lcp.cc b/src/tools/lcp/lcp.cc index a7ffa24a21..aed1325d5f 100644 --- a/src/tools/lcp/lcp.cc +++ b/src/tools/lcp/lcp.cc @@ -133,32 +133,30 @@ int main(int argc, char *argv[]) if (useFloat) { auto renderer = MakeMixedStrategyProfileRenderer(std::cout, numDecimals, printDetail); - LcpStrategySolve(game, stopAfter, maxDepth, - [&](const MixedStrategyProfile &p, - const std::string &label) { renderer->Render(p, label); }); + LcpStrategySolve( + game, stopAfter, maxDepth, + [&](const MixedStrategyProfile &p) { renderer->Render(p); }); } else { auto renderer = MakeMixedStrategyProfileRenderer(std::cout, numDecimals, printDetail); - LcpStrategySolve(game, stopAfter, maxDepth, - [&](const MixedStrategyProfile &p, - const std::string &label) { renderer->Render(p, label); }); + LcpStrategySolve( + game, stopAfter, maxDepth, + [&](const MixedStrategyProfile &p) { renderer->Render(p); }); } } else { if (useFloat) { auto renderer = MakeMixedBehaviorProfileRenderer(std::cout, numDecimals, printDetail); - LcpBehaviorSolve(game, - [&](const MixedBehaviorProfile &p, - const std::string &label) { renderer->Render(p, label); }); + LcpBehaviorSolve( + game, [&](const MixedBehaviorProfile &p) { renderer->Render(p); }); } else { auto renderer = MakeMixedBehaviorProfileRenderer(std::cout, numDecimals, printDetail); - LcpBehaviorSolve(game, - [&](const MixedBehaviorProfile &p, - const std::string &label) { renderer->Render(p, label); }); + LcpBehaviorSolve( + game, [&](const MixedBehaviorProfile &p) { renderer->Render(p); }); } } return 0; diff --git a/src/tools/liap/liap.cc b/src/tools/liap/liap.cc index 9ad653e202..ee9b78c7bb 100644 --- a/src/tools/liap/liap.cc +++ b/src/tools/liap/liap.cc @@ -22,6 +22,7 @@ #include #include +#include #include #include "gambit.h" #include "tools/util.h" @@ -30,6 +31,22 @@ using namespace Gambit; using namespace Gambit::Nash; +template +void RenderLiapEvent(const Renderer &p_renderer, const LiapEvent &p_event) +{ + std::visit( + [&](const auto &event) { + using EventType = std::decay_t; + if constexpr (std::is_same_v>) { + p_renderer->Render(event.profile, "start"); + } + else if constexpr (std::is_same_v>) { + p_renderer->Render(event.profile, "end"); + } + }, + p_event); +} + void PrintBanner(std::ostream &p_stream) { p_stream << "Compute Nash equilibria by minimizing the Lyapunov function\n"; @@ -221,13 +238,16 @@ int main(int argc, char *argv[]) for (size_t i = 1; i <= starts.size(); i++) { auto renderer = MakeMixedStrategyProfileRenderer(std::cout, numDecimals, false); - LiapStrategySolve(starts[i], maxregret, maxitsN, - [renderer, verbose](const MixedStrategyProfile &p_profile, - const std::string &p_label) { - if (p_label == "NE" || verbose) { - renderer->Render(p_profile, p_label); - } - }); + LiapStrategySolve( + starts[i], maxregret, maxitsN, + [renderer](const MixedStrategyProfile &p_profile) { + renderer->Render(p_profile); + }, + [renderer, verbose](const LiapEvent> &event) { + if (verbose) { + RenderLiapEvent(renderer, event); + } + }); } } else { @@ -243,13 +263,16 @@ int main(int argc, char *argv[]) for (size_t i = 1; i <= starts.size(); i++) { auto renderer = MakeMixedBehaviorProfileRenderer(std::cout, numDecimals, false); - LiapAgentSolve(starts[i], maxregret, maxitsN, - [renderer, verbose](const MixedBehaviorProfile &p_profile, - const std::string &p_label) { - if (p_label == "NE" || verbose) { - renderer->Render(p_profile, p_label); - } - }); + LiapAgentSolve( + starts[i], maxregret, maxitsN, + [renderer](const MixedBehaviorProfile &p_profile) { + renderer->Render(p_profile); + }, + [renderer, verbose](const LiapEvent> &event) { + if (verbose) { + RenderLiapEvent(renderer, event); + } + }); } } return 0; diff --git a/src/tools/logit/logit.cc b/src/tools/logit/logit.cc index a10fbebd2d..17143fc350 100644 --- a/src/tools/logit/logit.cc +++ b/src/tools/logit/logit.cc @@ -200,9 +200,11 @@ int main(int argc, char *argv[]) std::ifstream mleData(mleFile.c_str()); ReadProfile(mleData, frequencies); - auto printer = [fullGraph, decimals](const LogitQREMixedStrategyProfile &p) { + auto printer = [fullGraph, + decimals](const LogitEvent &p_event) { if (fullGraph) { - PrintProfile(std::cout, decimals, p); + PrintProfile(std::cout, decimals, + std::get>(p_event).qre); } }; auto result = @@ -212,9 +214,11 @@ int main(int argc, char *argv[]) } if (!game->IsTree() || useStrategic) { - auto printer = [fullGraph, decimals](const LogitQREMixedStrategyProfile &p) { + auto printer = [fullGraph, + decimals](const LogitEvent &p_event) { if (fullGraph) { - PrintProfile(std::cout, decimals, p); + PrintProfile(std::cout, decimals, + std::get>(p_event).qre); } }; const LogitQREMixedStrategyProfile start(game); @@ -226,14 +230,17 @@ int main(int argc, char *argv[]) } } else { - auto result = LogitStrategySolve(start, maxregret, 1.0, hStart, maxDecel, printer); + auto result = LogitStrategySolve(start, maxregret, 1.0, hStart, maxDecel, + Nash::NullStrategyCallback, printer); PrintProfile(std::cout, decimals, result.back(), true); } } else { - auto printer = [fullGraph, decimals](const LogitQREMixedBehaviorProfile &p) { + auto printer = [fullGraph, + decimals](const LogitEvent &p_event) { if (fullGraph) { - PrintProfile(std::cout, decimals, p); + PrintProfile(std::cout, decimals, + std::get>(p_event).qre); } }; const LogitQREMixedBehaviorProfile start(game); @@ -245,7 +252,8 @@ int main(int argc, char *argv[]) } } else { - auto result = LogitBehaviorSolve(start, maxregret, 1.0, hStart, maxDecel, printer); + auto result = LogitBehaviorSolve(start, maxregret, 1.0, hStart, maxDecel, + Nash::NullBehaviorCallback, printer); PrintProfile(std::cout, decimals, result.back(), true); } } diff --git a/src/tools/lp/lp.cc b/src/tools/lp/lp.cc index 89b50dd917..4ab836e3ed 100644 --- a/src/tools/lp/lp.cc +++ b/src/tools/lp/lp.cc @@ -123,32 +123,28 @@ int main(int argc, char *argv[]) if (useFloat) { auto renderer = MakeMixedStrategyProfileRenderer(std::cout, numDecimals, printDetail); - LpStrategySolve(game, - [&](const MixedStrategyProfile &p, - const std::string &label) { renderer->Render(p, label); }); + LpStrategySolve( + game, [&](const MixedStrategyProfile &p) { renderer->Render(p); }); } else { auto renderer = MakeMixedStrategyProfileRenderer(std::cout, numDecimals, printDetail); - LpStrategySolve(game, - [&](const MixedStrategyProfile &p, - const std::string &label) { renderer->Render(p, label); }); + LpStrategySolve( + game, [&](const MixedStrategyProfile &p) { renderer->Render(p); }); } } else { if (useFloat) { auto renderer = MakeMixedBehaviorProfileRenderer(std::cout, numDecimals, printDetail); - LpBehaviorSolve(game, - [&](const MixedBehaviorProfile &p, - const std::string &label) { renderer->Render(p, label); }); + LpBehaviorSolve( + game, [&](const MixedBehaviorProfile &p) { renderer->Render(p); }); } else { auto renderer = MakeMixedBehaviorProfileRenderer(std::cout, numDecimals, printDetail); - LpBehaviorSolve(game, - [&](const MixedBehaviorProfile &p, - const std::string &label) { renderer->Render(p, label); }); + LpBehaviorSolve( + game, [&](const MixedBehaviorProfile &p) { renderer->Render(p); }); } } return 0; diff --git a/src/tools/simpdiv/nfgsimpdiv.cc b/src/tools/simpdiv/nfgsimpdiv.cc index 88fea2f57c..ae863b530c 100644 --- a/src/tools/simpdiv/nfgsimpdiv.cc +++ b/src/tools/simpdiv/nfgsimpdiv.cc @@ -23,6 +23,7 @@ #include #include #include +#include #include "gambit.h" #include "tools/util.h" #include "solvers/simpdiv/simpdiv.h" @@ -30,6 +31,22 @@ using namespace Gambit; using namespace Gambit::Nash; +template +void RenderSimpdivEvent(const Renderer &p_renderer, const SimpdivEvent &p_event) +{ + std::visit( + [&](const auto &event) { + using EventType = std::decay_t; + if constexpr (std::is_same_v) { + p_renderer->Render(event.profile, "start"); + } + else if constexpr (std::is_same_v) { + p_renderer->Render(event.profile, std::to_string(event.gridSize)); + } + }, + p_event); +} + Array> ReadProfiles(const Game &p_game, std::istream &p_stream) { Array> profiles; @@ -222,9 +239,10 @@ int main(int argc, char *argv[]) auto renderer = std::make_shared(std::cout, decimals); SimpdivStrategySolve( start, maxregret, gridResize, 0, - [&](const MixedStrategyProfile &p, const std::string &label) { - if (label == "NE" || verbose) { - renderer->Render(p, label); + [&](const MixedStrategyProfile &p) { renderer->Render(p); }, + [&](const SimpdivEvent &event) { + if (verbose) { + RenderSimpdivEvent(renderer, event); } }); } @@ -232,9 +250,10 @@ int main(int argc, char *argv[]) auto renderer = std::make_shared>(std::cout); SimpdivStrategySolve( start, maxregret, gridResize, 0, - [&](const MixedStrategyProfile &p, const std::string &label) { - if (label == "NE" || verbose) { - renderer->Render(p, label); + [&](const MixedStrategyProfile &p) { renderer->Render(p); }, + [&](const SimpdivEvent &event) { + if (verbose) { + RenderSimpdivEvent(renderer, event); } }); }