Skip to content

Commit c90bb09

Browse files
committed
Implement StrategySupportProfile as vectors of legal "digits" in indexing the Cartesian product space.
1 parent f075ce3 commit c90bb09

5 files changed

Lines changed: 142 additions & 72 deletions

File tree

src/games/behavmixed.cc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,7 @@ template <class T> MixedBehaviorProfile<T> MixedBehaviorProfile<T>::ToFullSuppor
252252

253253
template <class T> T MixedBehaviorProfile<T>::GetLiapValue() const
254254
{
255+
m_support.GetGame()->BuildComputedValues();
255256
return MixedStrategyProfile<T>(*this).GetLiapValue();
256257
}
257258

@@ -356,6 +357,7 @@ template <class T> T MixedBehaviorProfile<T>::GetRegret(const GameInfoset &p_inf
356357

357358
template <class T> T MixedBehaviorProfile<T>::GetMaxRegret() const
358359
{
360+
m_support.GetGame()->BuildComputedValues();
359361
return MixedStrategyProfile<T>(*this).GetMaxRegret();
360362
}
361363

@@ -553,6 +555,7 @@ template <class T> bool MixedBehaviorProfile<T>::IsDefinedAt(GameInfoset p_infos
553555
template <class T> MixedStrategyProfile<T> MixedBehaviorProfile<T>::ToMixedProfile() const
554556
{
555557
CheckVersion();
558+
m_support.GetGame()->BuildComputedValues();
556559
return MixedStrategyProfile<T>(*this);
557560
}
558561

src/games/game.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -600,6 +600,12 @@ class CartesianProductSpace {
600600
std::vector<long> m_strides;
601601
};
602602

603+
class CartesianSubset {
604+
public:
605+
const CartesianProductSpace *m_space;
606+
std::vector<std::vector<int>> m_allowedDigits;
607+
};
608+
603609
template <class T> class CartesianTensor {
604610
public:
605611
const CartesianProductSpace *m_space;
@@ -614,6 +620,7 @@ class GameRep : public std::enable_shared_from_this<GameRep> {
614620
friend class GamePlayerRep;
615621
friend class PureStrategyProfileRep;
616622
friend class TablePureStrategyProfileRep;
623+
friend class StrategySupportProfile;
617624
template <class T> friend class MixedBehaviorProfile;
618625
template <class T> friend class MixedStrategyProfile;
619626
template <class T> friend class TableMixedStrategyProfileRep;

src/games/gametree.cc

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1356,7 +1356,7 @@ MixedStrategyProfile<double> GameTreeRep::NewMixedStrategyProfile(double) const
13561356
if (!IsPerfectRecall()) {
13571357
throw UndefinedException("Mixed strategies not supported for games with imperfect recall.");
13581358
}
1359-
EnsureInfosetOrdering();
1359+
BuildComputedValues();
13601360
return StrategySupportProfile(std::const_pointer_cast<GameRep>(shared_from_this()))
13611361
.NewMixedStrategyProfile<double>();
13621362
}
@@ -1366,7 +1366,7 @@ MixedStrategyProfile<Rational> GameTreeRep::NewMixedStrategyProfile(const Ration
13661366
if (!IsPerfectRecall()) {
13671367
throw UndefinedException("Mixed strategies not supported for games with imperfect recall.");
13681368
}
1369-
EnsureInfosetOrdering();
1369+
BuildComputedValues();
13701370
return StrategySupportProfile(std::const_pointer_cast<GameRep>(shared_from_this()))
13711371
.NewMixedStrategyProfile<Rational>();
13721372
}
@@ -1377,7 +1377,7 @@ GameTreeRep::NewMixedStrategyProfile(double, const StrategySupportProfile &spt)
13771377
if (!IsPerfectRecall()) {
13781378
throw UndefinedException("Mixed strategies not supported for games with imperfect recall.");
13791379
}
1380-
EnsureInfosetOrdering();
1380+
BuildComputedValues();
13811381
return MixedStrategyProfile<double>(std::make_unique<TreeMixedStrategyProfileRep<double>>(spt));
13821382
}
13831383

@@ -1387,7 +1387,7 @@ GameTreeRep::NewMixedStrategyProfile(const Rational &, const StrategySupportProf
13871387
if (!IsPerfectRecall()) {
13881388
throw UndefinedException("Mixed strategies not supported for games with imperfect recall.");
13891389
}
1390-
EnsureInfosetOrdering();
1390+
BuildComputedValues();
13911391
return MixedStrategyProfile<Rational>(
13921392
std::make_unique<TreeMixedStrategyProfileRep<Rational>>(spt));
13931393
}
@@ -1417,7 +1417,7 @@ class TreePureStrategyProfileRep : public PureStrategyProfileRep {
14171417

14181418
PureStrategyProfile GameTreeRep::NewPureStrategyProfile() const
14191419
{
1420-
EnsureInfosetOrdering();
1420+
BuildComputedValues();
14211421
return PureStrategyProfile(std::make_shared<TreePureStrategyProfileRep>(
14221422
std::const_pointer_cast<GameRep>(shared_from_this())));
14231423
}

src/games/stratspt.cc

Lines changed: 48 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -32,51 +32,48 @@ namespace Gambit {
3232
// class StrategySupportProfile
3333
//===========================================================================
3434

35-
StrategySupportProfile::StrategySupportProfile(const Game &p_nfg) : m_nfg(p_nfg)
35+
StrategySupportProfile::StrategySupportProfile(const Game &p_game) : m_game(p_game)
3636
{
37-
for (auto player : m_nfg->GetPlayers()) {
38-
for (auto strategy : player->GetStrategies()) {
39-
m_support[player].push_back(strategy);
40-
}
37+
m_game->BuildComputedValues();
38+
m_strategies.m_space = &p_game->m_pureStrategies;
39+
m_strategies.m_allowedDigits.resize(p_game->NumPlayers());
40+
41+
for (size_t i = 0; i < p_game->NumPlayers(); ++i) {
42+
const int radix = p_game->m_pureStrategies.m_radices[i];
43+
auto &digits = m_strategies.m_allowedDigits[i];
44+
digits.resize(radix);
45+
std::iota(digits.begin(), digits.end(), 0);
4146
}
4247
}
4348

44-
Array<int> StrategySupportProfile::NumStrategies() const
45-
{
46-
Array<int> dim(m_support.size());
47-
std::transform(
48-
m_support.cbegin(), m_support.cend(), dim.begin(),
49-
[](std::pair<GamePlayer, std::vector<GameStrategy>> a) { return a.second.size(); });
50-
return dim;
51-
}
52-
5349
int StrategySupportProfile::MixedProfileLength() const
5450
{
55-
return std::accumulate(m_support.cbegin(), m_support.cend(), 0,
56-
[](size_t tot, std::pair<GamePlayer, std::vector<GameStrategy>> a) {
57-
return tot + a.second.size();
58-
});
51+
return sum_function(m_strategies.m_allowedDigits, [](const std::vector<int> &digits) {
52+
return static_cast<int>(digits.size());
53+
});
5954
}
6055

6156
template <> MixedStrategyProfile<double> StrategySupportProfile::NewMixedStrategyProfile() const
6257
{
63-
return m_nfg->NewMixedStrategyProfile(0.0, *this);
58+
return m_game->NewMixedStrategyProfile(0.0, *this);
6459
}
6560

6661
template <> MixedStrategyProfile<Rational> StrategySupportProfile::NewMixedStrategyProfile() const
6762
{
68-
return m_nfg->NewMixedStrategyProfile(Rational(0), *this);
63+
return m_game->NewMixedStrategyProfile(Rational(0), *this);
6964
}
7065

71-
bool StrategySupportProfile::IsSubsetOf(const StrategySupportProfile &p_support) const
66+
bool StrategySupportProfile::IsSubsetOf(const StrategySupportProfile &p_other) const
7267
{
73-
for (auto player : m_nfg->GetPlayers()) {
74-
if (!std::includes(p_support.m_support.at(player).begin(),
75-
p_support.m_support.at(player).end(), m_support.at(player).begin(),
76-
m_support.at(player).end(),
77-
[](const GameStrategy &s, const GameStrategy &t) {
78-
return s->GetNumber() < t->GetNumber();
79-
})) {
68+
if (m_game != p_other.m_game) {
69+
return false;
70+
}
71+
const auto &A = m_strategies.m_allowedDigits;
72+
const auto &B = p_other.m_strategies.m_allowedDigits;
73+
const size_t n = A.size();
74+
75+
for (size_t i = 0; i < n; ++i) {
76+
if (!std::includes(B[i].begin(), B[i].end(), A[i].begin(), A[i].end())) {
8077
return false;
8178
}
8279
}
@@ -85,8 +82,8 @@ bool StrategySupportProfile::IsSubsetOf(const StrategySupportProfile &p_support)
8582

8683
void StrategySupportProfile::WriteNfgFile(std::ostream &p_file) const
8784
{
88-
auto players = m_nfg->GetPlayers();
89-
p_file << "NFG 1 R " << std::quoted(m_nfg->GetTitle()) << ' '
85+
auto players = m_game->GetPlayers();
86+
p_file << "NFG 1 R " << std::quoted(m_game->GetTitle()) << ' '
9087
<< FormatList(players, [](const GamePlayer &p) { return QuoteString(p->GetLabel()); })
9188
<< std::endl
9289
<< std::endl;
@@ -97,7 +94,7 @@ void StrategySupportProfile::WriteNfgFile(std::ostream &p_file) const
9794
}) << std::endl;
9895
}
9996
p_file << "}" << std::endl;
100-
p_file << std::quoted(m_nfg->GetComment()) << std::endl << std::endl;
97+
p_file << std::quoted(m_game->GetComment()) << std::endl << std::endl;
10198

10299
for (auto iter : StrategyContingencies(*this)) {
103100
p_file << FormatList(
@@ -112,25 +109,32 @@ void StrategySupportProfile::WriteNfgFile(std::ostream &p_file) const
112109

113110
void StrategySupportProfile::AddStrategy(const GameStrategy &p_strategy)
114111
{
115-
auto &support = m_support[p_strategy->GetPlayer()];
116-
auto pos = std::find_if(support.begin(), support.end(), [p_strategy](const GameStrategy &s) {
117-
return s->GetNumber() >= p_strategy->GetNumber();
118-
});
119-
if (pos == support.end() || *pos != p_strategy) {
120-
// Strategy is not in the support for the player; add at this location to keep sorted by number
121-
support.insert(pos, p_strategy);
112+
if (p_strategy->GetGame() != m_game) {
113+
throw MismatchException();
114+
}
115+
const size_t index = p_strategy->GetPlayer()->GetNumber() - 1;
116+
const int digit = p_strategy->GetNumber() - 1;
117+
auto &digits = m_strategies.m_allowedDigits[index];
118+
auto pos = std::lower_bound(digits.begin(), digits.end(), digit);
119+
if (pos == digits.end() || *pos != digit) {
120+
digits.insert(pos, digit);
122121
}
123122
}
124123

125124
bool StrategySupportProfile::RemoveStrategy(const GameStrategy &p_strategy)
126125
{
127-
auto &support = m_support[p_strategy->GetPlayer()];
128-
if (support.size() == 1) {
126+
if (p_strategy->GetGame() != m_game) {
127+
throw MismatchException();
128+
}
129+
const size_t index = p_strategy->GetPlayer()->GetNumber() - 1;
130+
const int digit = p_strategy->GetNumber() - 1;
131+
auto &digits = m_strategies.m_allowedDigits[index];
132+
if (digits.size() == 1) {
129133
return false;
130134
}
131-
auto pos = std::find(support.begin(), support.end(), p_strategy);
132-
if (pos != support.end()) {
133-
support.erase(pos);
135+
auto pos = std::lower_bound(digits.begin(), digits.end(), digit);
136+
if (pos != digits.end() && *pos == digit) {
137+
digits.erase(pos);
134138
return true;
135139
}
136140
return false;
@@ -263,7 +267,7 @@ bool UndominatedForPlayer(const StrategySupportProfile &p_support,
263267
StrategySupportProfile StrategySupportProfile::Undominated(bool p_strict, bool p_external) const
264268
{
265269
StrategySupportProfile newSupport(*this);
266-
for (auto player : m_nfg->GetPlayers()) {
270+
for (auto player : m_game->GetPlayers()) {
267271
UndominatedForPlayer(*this, newSupport, player, p_strict, p_external);
268272
}
269273
return newSupport;

src/games/stratspt.h

Lines changed: 79 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -39,31 +39,83 @@ namespace Gambit {
3939
/// Within the support, strategies are maintained in the same order
4040
/// in which they appear in the underlying game.
4141
class StrategySupportProfile {
42-
protected:
43-
Game m_nfg;
44-
std::map<GamePlayer, std::vector<GameStrategy>> m_support;
42+
Game m_game;
43+
CartesianSubset m_strategies;
4544

4645
public:
4746
class Support {
48-
private:
4947
const StrategySupportProfile *m_profile;
50-
GamePlayer m_player;
48+
size_t m_playerIndex;
5149

5250
public:
53-
using const_iterator = std::vector<GameStrategy>::const_iterator;
51+
class const_iterator {
52+
const StrategySupportProfile *m_profile{nullptr};
53+
size_t m_playerIndex{0};
54+
std::vector<int>::const_iterator m_it;
55+
56+
public:
57+
using value_type = GameStrategy;
58+
using reference = GameStrategy;
59+
using pointer = void;
60+
using difference_type = std::ptrdiff_t;
61+
using iterator_category = std::forward_iterator_tag;
62+
63+
const_iterator() = default;
64+
const_iterator(const StrategySupportProfile *profile, const size_t playerIndex,
65+
const std::vector<int>::const_iterator it)
66+
: m_profile(profile), m_playerIndex(playerIndex), m_it(it)
67+
{
68+
}
69+
70+
GameStrategy operator*() const
71+
{
72+
const auto &player = m_profile->m_game->GetPlayer(m_playerIndex + 1);
73+
return player->GetStrategy(*m_it + 1);
74+
}
75+
76+
const_iterator &operator++()
77+
{
78+
++m_it;
79+
return *this;
80+
}
81+
82+
bool operator==(const const_iterator &other) const { return m_it == other.m_it; }
83+
84+
bool operator!=(const const_iterator &other) const { return !(*this == other); }
85+
};
86+
87+
Support() : m_profile(nullptr), m_playerIndex(0) {}
88+
89+
Support(const StrategySupportProfile *profile, GamePlayer player)
90+
: m_profile(profile), m_playerIndex(player->GetNumber() - 1)
91+
{
92+
}
5493

55-
Support() : m_profile(nullptr), m_player(nullptr) {}
56-
Support(const StrategySupportProfile *p_profile, GamePlayer p_player)
57-
: m_profile(p_profile), m_player(p_player)
94+
size_t size() const { return m_profile->m_strategies.m_allowedDigits[m_playerIndex].size(); }
95+
96+
GameStrategy front() const
5897
{
98+
const int digit = m_profile->m_strategies.m_allowedDigits[m_playerIndex].front();
99+
return m_profile->m_game->GetPlayer(m_playerIndex + 1)->GetStrategy(digit + 1);
59100
}
60101

61-
size_t size() const { return m_profile->m_support.at(m_player).size(); }
62-
GameStrategy front() const { return m_profile->m_support.at(m_player).front(); }
63-
GameStrategy back() const { return m_profile->m_support.at(m_player).back(); }
102+
GameStrategy back() const
103+
{
104+
const int digit = m_profile->m_strategies.m_allowedDigits[m_playerIndex].back();
105+
return m_profile->m_game->GetPlayer(m_playerIndex + 1)->GetStrategy(digit + 1);
106+
}
64107

65-
const_iterator begin() const { return m_profile->m_support.at(m_player).begin(); }
66-
const_iterator end() const { return m_profile->m_support.at(m_player).end(); }
108+
const_iterator begin() const
109+
{
110+
const auto &digits = m_profile->m_strategies.m_allowedDigits[m_playerIndex];
111+
return {m_profile, m_playerIndex, digits.begin()};
112+
}
113+
114+
const_iterator end() const
115+
{
116+
const auto &digits = m_profile->m_strategies.m_allowedDigits[m_playerIndex];
117+
return {m_profile, m_playerIndex, digits.end()};
118+
}
67119
};
68120

69121
/// @name Lifecycle
@@ -77,37 +129,41 @@ class StrategySupportProfile {
77129
/// Test for the equality of two supports (same strategies for all players)
78130
bool operator==(const StrategySupportProfile &p_support) const
79131
{
80-
return (m_support == p_support.m_support);
132+
return m_game == p_support.m_game &&
133+
m_strategies.m_allowedDigits == p_support.m_strategies.m_allowedDigits;
81134
}
82135
/// Test for the inequality of two supports
83136
bool operator!=(const StrategySupportProfile &p_support) const
84137
{
85-
return (m_support != p_support.m_support);
138+
return m_game != p_support.m_game ||
139+
m_strategies.m_allowedDigits != p_support.m_strategies.m_allowedDigits;
86140
}
87141
//@}
88142

89143
/// @name General information
90144
//@{
91145
/// Returns the game on which the support is defined.
92-
Game GetGame() const { return m_nfg; }
93-
94-
/// Returns the number of strategies in the support for all players.
95-
Array<int> NumStrategies() const;
146+
Game GetGame() const { return m_game; }
96147

97148
/// Returns the total number of strategies in the support.
98149
int MixedProfileLength() const;
99150

100151
template <class T> MixedStrategyProfile<T> NewMixedStrategyProfile() const;
101152

102153
/// Returns the number of players in the game
103-
int NumPlayers() const { return m_nfg->NumPlayers(); }
154+
int NumPlayers() const { return m_game->NumPlayers(); }
104155
/// Returns the set of players in the game
105-
GameRep::Players GetPlayers() const { return m_nfg->GetPlayers(); }
156+
GameRep::Players GetPlayers() const { return m_game->GetPlayers(); }
106157
/// Returns the set of strategies in the support for a player
107158
Support GetStrategies(const GamePlayer &p_player) const { return {this, p_player}; }
108159

109160
/// Returns true exactly when the strategy is in the support.
110-
bool Contains(const GameStrategy &s) const { return contains(m_support.at(s->GetPlayer()), s); }
161+
bool Contains(const GameStrategy &s) const
162+
{
163+
const auto &digits = m_strategies.m_allowedDigits[s->GetPlayer()->GetNumber() - 1];
164+
const int digit = s->GetNumber() - 1;
165+
return std::binary_search(digits.begin(), digits.end(), digit);
166+
}
111167

112168
/// Returns true iff this support is a (weak) subset of the specified support
113169
bool IsSubsetOf(const StrategySupportProfile &) const;

0 commit comments

Comments
 (0)