Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 13 additions & 7 deletions src/gui/dlnash.cc
Original file line number Diff line number Diff line change
Expand Up @@ -157,10 +157,12 @@ wxString ExternalCommand(const NashComputationSpec &p_spec)
return prefix + wxT("enummixed");
}
else if constexpr (std::is_same_v<Method, EnumPolyNashSpec>) {
return prefix +
wxString::Format("enumpoly -d 10 -e %d -m %.17g", method.stopAfter,
method.maxRegret) +
strategic;
wxString command = wxString::Format("enumpoly -d 10 -m %.17g", method.maxRegret);
if (method.stopAfter.has_value()) {
command += wxString::Format(" -e %llu",
static_cast<unsigned long long>(method.stopAfter.value()));
}
return prefix + command + strategic;
}
else if constexpr (std::is_same_v<Method, GNMNashSpec>) {
return prefix + wxString::Format("gnm -d 10 -n %d -m %.17g -c %d -f %d -i %d",
Expand All @@ -175,8 +177,12 @@ wxString ExternalCommand(const NashComputationSpec &p_spec)
return prefix + wxT("lp") + strategic;
}
else if constexpr (std::is_same_v<Method, LCPNashSpec>) {
return prefix + wxString::Format("lcp -e %d -r %d", method.stopAfter, method.maxDepth) +
strategic;
wxString command = wxString::Format("lcp -r %d", method.maxDepth);
if (method.stopAfter.has_value()) {
command += wxString::Format(" -e %llu",
static_cast<unsigned long long>(method.stopAfter.value()));
}
return prefix + command + strategic;
}
else if constexpr (std::is_same_v<Method, LiapNashSpec>) {
return prefix + wxString::Format("liap -d 10 -n %d -i %d -m %.17g",
Expand Down Expand Up @@ -244,7 +250,7 @@ wxString ParameterDescription(const NashMethodSpec &p_method)
return std::visit(
[]<typename Method>(const Method &method) {
if constexpr (std::is_same_v<Method, EnumPolyNashSpec>) {
if (method.stopAfter == 1) {
if (method.stopAfter.has_value() && method.stopAfter.value() == 1) {
return wxString::Format(" (stop after one equilibrium; maximum regret %.4g)",
method.maxRegret);
}
Expand Down
4 changes: 2 additions & 2 deletions src/gui/nashspec.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ struct EnumMixedNashSpec {
};

struct EnumPolyNashSpec {
int stopAfter{0};
std::optional<size_t> stopAfter{std::nullopt};
double maxRegret{1.0e-4};

std::optional<SolverFunction> MakeSolver(NashRepresentation p_representation) const;
Expand All @@ -99,7 +99,7 @@ struct LPNashSpec {
};

struct LCPNashSpec {
int stopAfter{0};
std::optional<size_t> stopAfter{std::nullopt};
int maxDepth{0};

std::optional<SolverFunction> MakeSolver(NashRepresentation p_representation) const;
Expand Down
6 changes: 3 additions & 3 deletions src/pygambit/gambit.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -554,7 +554,7 @@ cdef extern from "solvers/enummixed/enummixed.h":

cdef extern from "solvers/lcp/lcp.h":
stdlist[c_MixedStrategyProfile[T]] LcpStrategySolve[T](
c_Game, int p_stopAfter, int p_maxDepth
c_Game, optional[size_t] p_stopAfter, int p_maxDepth
) except +RuntimeError
stdlist[c_MixedBehaviorProfile[T]] LcpBehaviorSolve[T](c_Game) except +RuntimeError

Expand Down Expand Up @@ -594,10 +594,10 @@ cdef extern from "solvers/nashsupport/nashsupport.h":

cdef extern from "solvers/enumpoly/enumpoly.h":
stdlist[c_MixedStrategyProfile[double]] EnumPolyStrategySolve(
c_Game, int, float, size_t
c_Game, optional[size_t], float, size_t
) except +RuntimeError
stdlist[c_MixedBehaviorProfile[double]] EnumPolyBehaviorSolve(
c_Game, int, float, size_t
c_Game, optional[size_t], float, size_t
) except +RuntimeError

cdef extern from "solvers/logit/logit.h":
Expand Down
28 changes: 20 additions & 8 deletions src/pygambit/nash.pxi
Original file line number Diff line number Diff line change
Expand Up @@ -86,15 +86,21 @@ def _lcp_behavior_solve_rational(


def _lcp_strategy_solve_double(
game: Game, stop_after: int, max_depth: int
game: Game, stop_after, max_depth: int
) -> list[MixedStrategyProfileDouble]:
return _convert_mspd(LcpStrategySolve[double](game.game, stop_after, max_depth))
cdef optional[size_t] c_stop_after
if stop_after is not None:
c_stop_after = <size_t>stop_after
return _convert_mspd(LcpStrategySolve[double](game.game, c_stop_after, max_depth))


def _lcp_strategy_solve_rational(
game: Game, stop_after: int, max_depth: int
game: Game, stop_after, max_depth: int
) -> list[MixedStrategyProfileRational]:
return _convert_mspr(LcpStrategySolve[c_Rational](game.game, stop_after, max_depth))
cdef optional[size_t] c_stop_after
if stop_after is not None:
c_stop_after = <size_t>stop_after
return _convert_mspr(LcpStrategySolve[c_Rational](game.game, c_stop_after, max_depth))


def _lp_behavior_solve_double(game: Game) -> list[MixedBehaviorProfileDouble]:
Expand Down Expand Up @@ -178,20 +184,26 @@ def _nashsupport_strategy_solve(

def _enumpoly_strategy_solve(
game: Game,
stop_after: int,
stop_after,
maxregret: float,
max_rectangles: int,
) -> list[MixedStrategyProfileDouble]:
return _convert_mspd(EnumPolyStrategySolve(game.game, stop_after, maxregret, max_rectangles))
cdef optional[size_t] c_stop_after
if stop_after is not None:
c_stop_after = <size_t>stop_after
return _convert_mspd(EnumPolyStrategySolve(game.game, c_stop_after, maxregret, max_rectangles))


def _enumpoly_behavior_solve(
game: Game,
stop_after: int,
stop_after,
maxregret: float,
max_rectangles: int,
) -> list[MixedBehaviorProfileDouble]:
return _convert_mbpd(EnumPolyBehaviorSolve(game.game, stop_after, maxregret, max_rectangles))
cdef optional[size_t] c_stop_after
if stop_after is not None:
c_stop_after = <size_t>stop_after
return _convert_mbpd(EnumPolyBehaviorSolve(game.game, c_stop_after, maxregret, max_rectangles))


def _logit_strategy_solve(
Expand Down
28 changes: 20 additions & 8 deletions src/pygambit/nash.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
from __future__ import annotations

import dataclasses
import math
import pathlib
from collections.abc import Iterator

Expand Down Expand Up @@ -242,15 +243,21 @@ def lcp_solve(
raise ValueError(
"lcp_solve(): max_depth can only be used on the strategic representation"
)
if stop_after is not None and stop_after < 0:
if stop_after is not None and stop_after <= 0:
raise ValueError(
f"lcp_solve(): stop_after argument must be a non-negative number; got {stop_after}"
f"lcp_solve(): stop_after argument must be a positive integer; got {stop_after}"
)
if stop_after is not None and (
not math.isfinite(stop_after) or int(stop_after) != stop_after
):
raise ValueError(
f"lcp_solve(): stop_after argument must be a positive integer; got {stop_after}"
)
if not game.is_tree or use_strategic:
if rational:
equilibria = libgbt._lcp_strategy_solve_rational(game, stop_after or 0, max_depth or 0)
equilibria = libgbt._lcp_strategy_solve_rational(game, stop_after, max_depth or 0)
else:
equilibria = libgbt._lcp_strategy_solve_double(game, stop_after or 0, max_depth or 0)
equilibria = libgbt._lcp_strategy_solve_double(game, stop_after, max_depth or 0)
elif rational:
equilibria = libgbt._lcp_behavior_solve_rational(game)
else:
Expand Down Expand Up @@ -709,12 +716,17 @@ def enumpoly_solve(
-----
PHCpack is available at https://homepages.math.uic.edu/~jan/PHCpack/phcpack.html
"""
if stop_after is None:
stop_after = 0
elif stop_after < 0:
if stop_after is not None and stop_after <= 0:
raise ValueError(
f"enumpoly_solve(): "
f"stop_after argument must be a positive integer; got {stop_after}"
)
if stop_after is not None and (
not math.isfinite(stop_after) or int(stop_after) != stop_after
):
raise ValueError(
f"enumpoly_solve(): "
f"stop_after argument must be a non-negative number; got {stop_after}"
f"stop_after argument must be a positive integer; got {stop_after}"
)
if maxregret <= 0.0:
raise ValueError(
Expand Down
21 changes: 12 additions & 9 deletions src/solvers/enumpoly/efgpoly.cc
Original file line number Diff line number Diff line change
Expand Up @@ -183,8 +183,8 @@ FindNashExtension(const MixedBehaviorProfile<double> &p_baseProfile, double p_ma

std::list<MixedBehaviorProfile<double>> SolveSupport(const BehaviorSupportProfile &p_support,
bool &p_isSingular, bool &p_budgetExceeded,
int p_stopAfter, double p_maxRegret,
size_t p_maxRectangles,
std::optional<size_t> p_stopAfter,
double p_maxRegret, size_t p_maxRectangles,
const CancelToken &p_cancel)
{
ProblemData data(p_support);
Expand All @@ -200,9 +200,9 @@ std::list<MixedBehaviorProfile<double>> SolveSupport(const BehaviorSupportProfil
PolynomialSystemSolver solver(equations);
std::list<Vector<double>> roots;
try {
roots = solver.FindRoots({bottoms, tops},
(p_stopAfter > 0) ? p_stopAfter : std::numeric_limits<int>::max(),
p_maxRectangles, p_budgetExceeded, p_cancel);
roots =
solver.FindRoots({bottoms, tops}, p_stopAfter.value_or(std::numeric_limits<size_t>::max()),
p_maxRectangles, p_budgetExceeded, p_cancel);
}
catch (const SingularMatrixException &) {
p_isSingular = true;
Expand All @@ -228,7 +228,7 @@ std::list<MixedBehaviorProfile<double>> SolveSupport(const BehaviorSupportProfil
namespace Gambit::Nash {

std::list<MixedBehaviorProfile<double>>
EnumPolyBehaviorSolve(const Game &p_game, int p_stopAfter, double p_maxregret,
EnumPolyBehaviorSolve(const Game &p_game, std::optional<size_t> p_stopAfter, double p_maxregret,
size_t p_maxRectangles, BehaviorCallbackType<double> p_onEquilibrium,
EnumPolyEventCallbackType<BehaviorSupportProfile> p_onEvent,
const CancelToken &p_cancel)
Expand All @@ -253,8 +253,11 @@ EnumPolyBehaviorSolve(const Game &p_game, int p_stopAfter, double p_maxregret,
bool budgetExceeded = false;
for (const auto &solution :
SolveSupport(support, isSingular, budgetExceeded,
std::max(p_stopAfter - static_cast<int>(ret.size()), 0), p_maxregret,
p_maxRectangles, p_cancel)) {
p_stopAfter.has_value()
? std::optional<size_t>(p_stopAfter.value() -
std::min(ret.size(), p_stopAfter.value()))
: std::nullopt,
p_maxregret, p_maxRectangles, p_cancel)) {
p_onEquilibrium(solution);
ret.push_back(solution);
}
Expand All @@ -264,7 +267,7 @@ EnumPolyBehaviorSolve(const Game &p_game, int p_stopAfter, double p_maxregret,
if (budgetExceeded) {
p_onEvent(EnumPolyBudgetExceededSupportEvent<BehaviorSupportProfile>{support});
}
if (p_stopAfter > 0 && static_cast<int>(ret.size()) >= p_stopAfter) {
if (p_stopAfter.has_value() && ret.size() >= p_stopAfter.value()) {
break;
}
}
Expand Down
5 changes: 3 additions & 2 deletions src/solvers/enumpoly/enumpoly.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#ifndef GAMBIT_SOLVERS_ENUMPOLY_ENUMPOLY_H
#define GAMBIT_SOLVERS_ENUMPOLY_ENUMPOLY_H

#include <optional>
#include <variant>

#include "solvers/nash.h"
Expand Down Expand Up @@ -62,15 +63,15 @@ template <class Support> void NullEnumPolyEventCallback(const EnumPolyEvent<Supp
constexpr size_t kDefaultEnumPolyMaxRectangles = 20'000;

std::list<MixedStrategyProfile<double>>
EnumPolyStrategySolve(const Game &p_game, int p_stopAfter, double p_maxregret,
EnumPolyStrategySolve(const Game &p_game, std::optional<size_t> p_stopAfter, double p_maxregret,
size_t p_maxRectangles = kDefaultEnumPolyMaxRectangles,
StrategyCallbackType<double> p_onEquilibrium = NullStrategyCallback<double>,
EnumPolyEventCallbackType<StrategySupportProfile> p_onEvent =
NullEnumPolyEventCallback<StrategySupportProfile>,
const CancelToken &p_cancel = CancelToken());

std::list<MixedBehaviorProfile<double>>
EnumPolyBehaviorSolve(const Game &, int p_stopAfter, double p_maxregret,
EnumPolyBehaviorSolve(const Game &, std::optional<size_t> p_stopAfter, double p_maxregret,
size_t p_maxRectangles = kDefaultEnumPolyMaxRectangles,
BehaviorCallbackType<double> p_onEquilibrium = NullBehaviorCallback<double>,
EnumPolyEventCallbackType<BehaviorSupportProfile> p_onEvent =
Expand Down
20 changes: 12 additions & 8 deletions src/solvers/enumpoly/nfgpoly.cc
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,8 @@ namespace Gambit::Nash {

std::list<MixedStrategyProfile<double>>
EnumPolyStrategySupportSolve(const StrategySupportProfile &support, bool &is_singular,
bool &p_budgetExceeded, int p_stopAfter, size_t p_maxRectangles,
const CancelToken &p_cancel)
bool &p_budgetExceeded, std::optional<size_t> p_stopAfter,
size_t p_maxRectangles, const CancelToken &p_cancel)
{
auto space = std::make_shared<VariableSpace>(support.MixedProfileLength() -
support.GetGame()->NumPlayers());
Expand All @@ -119,9 +119,9 @@ EnumPolyStrategySupportSolve(const StrategySupportProfile &support, bool &is_sin
is_singular = false;
std::list<Vector<double>> roots;
try {
roots = solver.FindRoots({bottoms, tops},
(p_stopAfter > 0) ? p_stopAfter : std::numeric_limits<int>::max(),
p_maxRectangles, p_budgetExceeded, p_cancel);
roots =
solver.FindRoots({bottoms, tops}, p_stopAfter.value_or(std::numeric_limits<size_t>::max()),
p_maxRectangles, p_budgetExceeded, p_cancel);
}
catch (const SingularMatrixException &) {
is_singular = true;
Expand All @@ -142,7 +142,7 @@ EnumPolyStrategySupportSolve(const StrategySupportProfile &support, bool &is_sin
}

std::list<MixedStrategyProfile<double>>
EnumPolyStrategySolve(const Game &p_game, int p_stopAfter, double p_maxregret,
EnumPolyStrategySolve(const Game &p_game, std::optional<size_t> p_stopAfter, double p_maxregret,
size_t p_maxRectangles, StrategyCallbackType<double> p_onEquilibrium,
EnumPolyEventCallbackType<StrategySupportProfile> p_onEvent,
const CancelToken &p_cancel)
Expand All @@ -167,7 +167,11 @@ EnumPolyStrategySolve(const Game &p_game, int p_stopAfter, double p_maxregret,
bool budget_exceeded;
for (auto solution : EnumPolyStrategySupportSolve(
support, is_singular, budget_exceeded,
std::max(p_stopAfter - static_cast<int>(ret.size()), 0), p_maxRectangles, p_cancel)) {
p_stopAfter.has_value()
? std::optional<size_t>(p_stopAfter.value() -
std::min(ret.size(), p_stopAfter.value()))
: std::nullopt,
p_maxRectangles, p_cancel)) {
const MixedStrategyProfile<double> fullProfile = solution.ToFullSupport();
if (fullProfile.GetMaxRegret() < p_maxregret) {
p_onEquilibrium(fullProfile);
Expand All @@ -181,7 +185,7 @@ EnumPolyStrategySolve(const Game &p_game, int p_stopAfter, double p_maxregret,
if (budget_exceeded) {
p_onEvent(EnumPolyBudgetExceededSupportEvent<StrategySupportProfile>{support});
}
if (p_stopAfter > 0 && static_cast<int>(ret.size()) >= p_stopAfter) {
if (p_stopAfter.has_value() && ret.size() >= p_stopAfter.value()) {
break;
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/solvers/enumpoly/polysolver.cc
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ Vector<double> PolynomialSystemSolver::ImprovingNewtonStep(const Vector<double>
}

std::list<Vector<double>> PolynomialSystemSolver::FindRoots(const Rectangle<double> &r,
const int max_roots,
const size_t max_roots,
const size_t max_rectangles,
bool &p_budgetExceeded,
const CancelToken &p_cancel)
Expand Down
2 changes: 1 addition & 1 deletion src/solvers/enumpoly/polysolver.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ class PolynomialSystemSolver {
// Find up to `max_roots` roots inside rectangle `r`, examining at most
// `max_rectangles` subdivisions. Sets `p_budgetExceeded` if that budget
// runs out first, in which case the roots returned may be incomplete.
std::list<Vector<double>> FindRoots(const Rectangle<double> &r, int max_roots,
std::list<Vector<double>> FindRoots(const Rectangle<double> &r, size_t max_roots,
size_t max_rectangles, bool &p_budgetExceeded,
const CancelToken &p_cancel = CancelToken());
};
Expand Down
4 changes: 3 additions & 1 deletion src/solvers/lcp/lcp.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,15 @@
#ifndef GAMBIT_SOLVERS_LCP_LCP_H
#define GAMBIT_SOLVERS_LCP_LCP_H

#include <optional>

#include "solvers/nash.h"

namespace Gambit::Nash {

template <class T>
std::list<MixedStrategyProfile<T>>
LcpStrategySolve(const Game &p_game, int p_stopAfter, int p_maxDepth,
LcpStrategySolve(const Game &p_game, std::optional<size_t> p_stopAfter, int p_maxDepth,
StrategyCallbackType<T> p_onEquilibrium = NullStrategyCallback<T>,
const CancelToken &p_cancel = CancelToken());

Expand Down
Loading
Loading