Skip to content

Commit 2ff522b

Browse files
authored
Develop concept of a Cartesian product space and apply to games in strategic form. (#740)
This creates an abstraction, CartesianProductSpace, which aims to separate out the computations related to representing a Cartesian product. This has always been implicit in the strategic form, but the core ideas apply equally to other situations (sequence form, some applications of pure behavior profiles). Underpinning this is that any element of a cartesian product space can be represented by an integer computed using mixed radix arithmetic. This then leads to simplifications in the internal representation of several objects: * PureStrategyProfile, which is now be characterised simply by an integer index. * StrategySupportProfile, which is characterised by the set of "digits" which are valid for each dimension. * StrategyContingencies, which iterates over the valid digits for each dimension.
1 parent a7b5c2c commit 2ff522b

File tree

18 files changed

+394
-346
lines changed

18 files changed

+394
-346
lines changed

Makefile.am

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,6 @@ game_SOURCES = \
299299
src/games/behavmixed.h \
300300
src/games/stratspt.cc \
301301
src/games/stratspt.h \
302-
src/games/stratpure.cc \
303302
src/games/stratpure.h \
304303
src/games/stratmixed.h \
305304
src/games/file.cc \

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/file.cc

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -411,23 +411,21 @@ void ReadOutcomeList(GameFileLexer &p_parser, Game &p_nfg)
411411
void ParseOutcomeBody(GameFileLexer &p_parser, Game &p_nfg)
412412
{
413413
ReadOutcomeList(p_parser, p_nfg);
414-
const StrategySupportProfile profile(p_nfg);
415-
for (auto iter : StrategyContingencies(profile)) {
414+
for (const auto &profile : StrategyContingencies(p_nfg)) {
416415
p_parser.ExpectCurrentToken(TOKEN_NUMBER, "outcome index");
417416
if (const int outcomeId = std::stoi(p_parser.GetLastText())) {
418-
iter->SetOutcome(p_nfg->GetOutcome(outcomeId));
417+
profile->SetOutcome(p_nfg->GetOutcome(outcomeId));
419418
}
420419
p_parser.GetNextToken();
421420
}
422421
}
423422

424423
void ParsePayoffBody(GameFileLexer &p_parser, Game &p_nfg)
425424
{
426-
const StrategySupportProfile profile(p_nfg);
427-
for (auto iter : StrategyContingencies(profile)) {
425+
for (const auto &profile : StrategyContingencies(p_nfg)) {
428426
for (const auto &player : p_nfg->GetPlayers()) {
429427
p_parser.ExpectCurrentToken(TOKEN_NUMBER, "numerical payoff");
430-
iter->GetOutcome()->SetPayoff(player, Number(p_parser.GetLastText()));
428+
profile->GetOutcome()->SetPayoff(player, Number(p_parser.GetLastText()));
431429
p_parser.GetNextToken();
432430
}
433431
}

src/games/game.cc

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,24 @@ GameRep::~GameRep()
182182
}
183183
}
184184

185+
void GameRep::IndexStrategies() const
186+
{
187+
const size_t n = m_players.size();
188+
m_pureStrategies.m_radices.resize(n);
189+
m_pureStrategies.m_strides.resize(n);
190+
191+
long stride = 1L;
192+
for (size_t i = 0; i < n; ++i) {
193+
const auto &player = m_players[i];
194+
m_pureStrategies.m_strides[i] = stride;
195+
m_pureStrategies.m_radices[i] = player->m_strategies.size();
196+
for (auto [st, strategy] : enumerate(player->m_strategies)) {
197+
strategy->m_number = st + 1;
198+
}
199+
stride *= m_pureStrategies.m_radices[i];
200+
}
201+
}
202+
185203
//------------------------------------------------------------------------
186204
// GameRep: Writing data files
187205
//------------------------------------------------------------------------
@@ -405,6 +423,16 @@ template <class T> T MixedStrategyProfile<T>::GetMaxRegret() const
405423
[this](const auto &player) -> T { return this->GetRegret(player); });
406424
}
407425

426+
MixedStrategyProfile<Rational> PureStrategyProfileRep::ToMixedStrategyProfile() const
427+
{
428+
auto temp = m_game->NewMixedStrategyProfile(Rational(0));
429+
temp = Rational(0);
430+
for (const auto &player : m_game->GetPlayers()) {
431+
temp[GetStrategy(player)] = Rational(1);
432+
}
433+
return temp;
434+
}
435+
408436
template class MixedStrategyProfileRep<double>;
409437
template class MixedStrategyProfileRep<Rational>;
410438

src/games/game.h

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,7 @@ class GameInfosetRep : public std::enable_shared_from_this<GameInfosetRep> {
267267
/// relatively efficient.
268268
class GameStrategyRep : public std::enable_shared_from_this<GameStrategyRep> {
269269
friend class GameExplicitRep;
270+
friend class GameRep;
270271
friend class GameTreeRep;
271272
friend class GameTableRep;
272273
friend class GameAGGRep;
@@ -283,7 +284,6 @@ class GameStrategyRep : public std::enable_shared_from_this<GameStrategyRep> {
283284
bool m_valid{true};
284285
GamePlayerRep *m_player;
285286
int m_number;
286-
long m_offset{-1L};
287287
std::string m_label;
288288
std::map<GameInfosetRep *, int> m_behav;
289289

@@ -360,6 +360,7 @@ class GamePlayerRep : public std::enable_shared_from_this<GamePlayerRep> {
360360
friend class GameInfosetRep;
361361
friend class GameNodeRep;
362362
friend class StrategySupportProfile;
363+
friend class PureStrategyProfileRep;
363364
template <class T> friend class MixedBehaviorProfile;
364365
template <class T> friend class MixedStrategyProfile;
365366

@@ -594,6 +595,24 @@ inline GameNode GameNodeRep::Actions::iterator::GetOwner() const { return m_chil
594595

595596
enum class TraversalOrder { Preorder, Postorder };
596597

598+
class CartesianProductSpace {
599+
public:
600+
std::vector<int> m_radices;
601+
std::vector<long> m_strides;
602+
};
603+
604+
class CartesianSubset {
605+
public:
606+
const CartesianProductSpace *m_space{nullptr};
607+
std::vector<std::vector<int>> m_allowedDigits;
608+
};
609+
610+
template <class T> class CartesianTensor {
611+
public:
612+
const CartesianProductSpace *m_space{nullptr};
613+
std::vector<T> m_data;
614+
};
615+
597616
/// This is the class for representing an arbitrary finite game.
598617
class GameRep : public std::enable_shared_from_this<GameRep> {
599618
friend class GameOutcomeRep;
@@ -602,13 +621,15 @@ class GameRep : public std::enable_shared_from_this<GameRep> {
602621
friend class GamePlayerRep;
603622
friend class PureStrategyProfileRep;
604623
friend class TablePureStrategyProfileRep;
624+
friend class StrategySupportProfile;
605625
template <class T> friend class MixedBehaviorProfile;
606626
template <class T> friend class MixedStrategyProfile;
607627
template <class T> friend class TableMixedStrategyProfileRep;
608628

609629
protected:
610630
std::vector<std::shared_ptr<GamePlayerRep>> m_players;
611631
std::vector<std::shared_ptr<GameOutcomeRep>> m_outcomes;
632+
mutable CartesianProductSpace m_pureStrategies;
612633
std::string m_title, m_comment;
613634
unsigned int m_version{0};
614635

@@ -618,6 +639,7 @@ class GameRep : public std::enable_shared_from_this<GameRep> {
618639
//@{
619640
/// Mark that the content of the game has changed
620641
void IncrementVersion() { m_version++; }
642+
void IndexStrategies() const;
621643
//@}
622644

623645
/// Hooks for derived classes to update lazily-computed orderings if required

src/games/gameagg.cc

Lines changed: 12 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -51,21 +51,21 @@ class AGGPureStrategyProfileRep : public PureStrategyProfileRep {
5151

5252
Rational AGGPureStrategyProfileRep::GetPayoff(const GamePlayer &p_player) const
5353
{
54-
const std::shared_ptr<agg::AGG> aggPtr = dynamic_cast<GameAGGRep &>(*m_nfg).aggPtr;
54+
const std::shared_ptr<agg::AGG> aggPtr = dynamic_cast<GameAGGRep &>(*m_game).aggPtr;
5555
std::vector<int> s(aggPtr->getNumPlayers());
5656
for (int i = 1; i <= aggPtr->getNumPlayers(); i++) {
57-
s[i - 1] = m_profile.at(m_nfg->GetPlayer(i))->GetNumber() - 1;
57+
s[i - 1] = GetStrategy(m_game->GetPlayer(i))->GetNumber() - 1;
5858
}
5959
return Rational(aggPtr->getPurePayoff(p_player->GetNumber() - 1, s));
6060
}
6161

6262
Rational AGGPureStrategyProfileRep::GetStrategyValue(const GameStrategy &p_strategy) const
6363
{
6464
const int player = p_strategy->GetPlayer()->GetNumber();
65-
const std::shared_ptr<agg::AGG> aggPtr = dynamic_cast<GameAGGRep &>(*m_nfg).aggPtr;
65+
const std::shared_ptr<agg::AGG> aggPtr = dynamic_cast<GameAGGRep &>(*m_game).aggPtr;
6666
std::vector<int> s(aggPtr->getNumPlayers());
6767
for (int i = 1; i <= aggPtr->getNumPlayers(); i++) {
68-
s[i - 1] = m_profile.at(m_nfg->GetPlayer(i))->GetNumber() - 1;
68+
s[i - 1] = GetStrategy(m_game->GetPlayer(i))->GetNumber() - 1;
6969
}
7070
s[player - 1] = p_strategy->GetNumber() - 1;
7171
return Rational(aggPtr->getPurePayoff(player - 1, s));
@@ -236,23 +236,14 @@ GameAGGRep::NewMixedStrategyProfile(const Rational &, const StrategySupportProfi
236236

237237
bool GameAGGRep::IsConstSum() const
238238
{
239-
auto profile = NewPureStrategyProfile();
240-
Rational sum(0);
241-
for (const auto &player : m_players) {
242-
sum += profile->GetPayoff(player);
243-
}
244-
245-
for (auto iter : StrategyContingencies(std::const_pointer_cast<GameRep>(shared_from_this()))) {
246-
Rational newsum(0);
247-
for (const auto &player : m_players) {
248-
newsum += iter->GetPayoff(player);
249-
}
250-
if (newsum != sum) {
251-
return false;
252-
}
253-
}
254-
255-
return true;
239+
auto payoff_sum = [&](const PureStrategyProfile &p) {
240+
return sum_function(m_players, [&](const auto &player) { return p->GetPayoff(player); });
241+
};
242+
const Rational sum = payoff_sum(NewPureStrategyProfile());
243+
244+
auto contingencies = StrategyContingencies(std::const_pointer_cast<GameRep>(shared_from_this()));
245+
return std::all_of(contingencies.begin(), contingencies.end(),
246+
[&](const PureStrategyProfile &p) { return payoff_sum(p) == sum; });
256247
}
257248

258249
//------------------------------------------------------------------------

src/games/gamebagg.cc

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -50,26 +50,26 @@ class BAGGPureStrategyProfileRep : public PureStrategyProfileRep {
5050

5151
Rational BAGGPureStrategyProfileRep::GetPayoff(const GamePlayer &p_player) const
5252
{
53-
const std::shared_ptr<agg::BAGG> baggPtr = dynamic_cast<GameBAGGRep &>(*m_nfg).baggPtr;
54-
std::vector<int> s(m_nfg->NumPlayers());
55-
for (size_t i = 1; i <= m_nfg->NumPlayers(); i++) {
56-
s[i - 1] = m_profile.at(m_nfg->GetPlayer(i))->GetNumber() - 1;
53+
const std::shared_ptr<agg::BAGG> baggPtr = dynamic_cast<GameBAGGRep &>(*m_game).baggPtr;
54+
std::vector<int> s(m_game->NumPlayers());
55+
for (size_t i = 1; i <= m_game->NumPlayers(); i++) {
56+
s[i - 1] = GetStrategy(m_game->GetPlayer(i))->GetNumber() - 1;
5757
}
58-
const int bp = dynamic_cast<GameBAGGRep &>(*m_nfg).agent2baggPlayer[p_player->GetNumber()];
58+
const int bp = dynamic_cast<GameBAGGRep &>(*m_game).agent2baggPlayer[p_player->GetNumber()];
5959
const int tp = p_player->GetNumber() - 1 - baggPtr->typeOffset[bp - 1];
6060
return Rational(baggPtr->getPurePayoff(bp - 1, tp, s));
6161
}
6262

6363
Rational BAGGPureStrategyProfileRep::GetStrategyValue(const GameStrategy &p_strategy) const
6464
{
6565
const int player = p_strategy->GetPlayer()->GetNumber();
66-
const std::shared_ptr<agg::BAGG> baggPtr = dynamic_cast<GameBAGGRep &>(*m_nfg).baggPtr;
67-
std::vector<int> s(m_nfg->NumPlayers());
68-
for (size_t i = 1; i <= m_nfg->NumPlayers(); i++) {
69-
s[i - 1] = m_profile.at(m_nfg->GetPlayer(i))->GetNumber() - 1;
66+
const std::shared_ptr<agg::BAGG> baggPtr = dynamic_cast<GameBAGGRep &>(*m_game).baggPtr;
67+
std::vector<int> s(m_game->NumPlayers());
68+
for (size_t i = 1; i <= m_game->NumPlayers(); i++) {
69+
s[i - 1] = GetStrategy(m_game->GetPlayer(i))->GetNumber() - 1;
7070
}
7171
s[player - 1] = p_strategy->GetNumber() - 1;
72-
const int bp = dynamic_cast<GameBAGGRep &>(*m_nfg).agent2baggPlayer[player];
72+
const int bp = dynamic_cast<GameBAGGRep &>(*m_game).agent2baggPlayer[player];
7373
const int tp = player - 1 - baggPtr->typeOffset[bp - 1];
7474
return Rational(baggPtr->getPurePayoff(bp - 1, tp, s));
7575
}

0 commit comments

Comments
 (0)