Skip to content

Commit f9ba831

Browse files
authored
Implement non-recursive expected payoff calculations for mixed strategy profiles (#743)
This removes the recursive calculation of expected payoffs for mixed strategy profiles on the strategic form, in favour of a one-pass scan that computes the relevant outer products and sums the inner product on the fly. A version of computing strategy values is also provided where all strategy values for one player are computed at once. This results in about a 2x performance gain on using `logic-solve` on 4-player x 10-strategy games, as a benchmark.
1 parent b0da540 commit f9ba831

File tree

8 files changed

+267
-111
lines changed

8 files changed

+267
-111
lines changed

src/core/segment.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ template <class Storage> class Segmented {
9999

100100
void SetFlattened(const Storage &v) { m_values = v; }
101101
const Storage &GetFlattened() const { return m_values; }
102+
Storage &GetFlattened() { return m_values; }
102103
};
103104

104105
template <class T> using SegmentedArray = Segmented<Array<T>>;

src/games/game.cc

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -249,14 +249,16 @@ void GameRep::WriteNfgFile(std::ostream &p_file) const
249249

250250
template <class T>
251251
MixedStrategyProfileRep<T>::MixedStrategyProfileRep(const StrategySupportProfile &p_support)
252-
: m_probs(p_support.MixedProfileLength()), m_support(p_support),
252+
: m_probs(p_support.GetShape()), m_offsets(p_support.GetShape()), m_support(p_support),
253253
m_gameversion(p_support.GetGame()->GetVersion())
254254
{
255255
int index = 1;
256256
for (auto player : p_support.GetGame()->GetPlayers()) {
257257
for (auto strategy : player->GetStrategies()) {
258258
if (p_support.Contains(strategy)) {
259-
m_profileIndex[strategy] = index++;
259+
m_offsets.GetFlattened()[index] = StrategyOffset(strategy);
260+
m_profileIndex[strategy] = index;
261+
index++;
260262
}
261263
else {
262264
m_profileIndex[strategy] = -1;
@@ -367,8 +369,20 @@ template <class T> void MixedStrategyProfile<T>::ComputePayoffs() const
367369
Cache newCache;
368370
for (const auto &player : m_rep->GetSupport().GetPlayers()) {
369371
newCache.m_payoffs[player] = GetPayoff(player);
370-
for (const auto &strategy : m_rep->GetSupport().GetStrategies(player)) {
371-
newCache.m_strategyValues[player][strategy] = GetPayoff(strategy);
372+
const auto &strategies = m_rep->GetSupport().GetStrategies(player);
373+
Vector<T> values(strategies.size());
374+
if (m_rep->GetPayoffDerivs(player->GetNumber(), values)) {
375+
auto value_it = values.begin();
376+
for (const auto &strategy : strategies) {
377+
newCache.m_strategyValues[player][strategy] = *value_it;
378+
++value_it;
379+
;
380+
}
381+
}
382+
else {
383+
for (const auto &strategy : m_rep->GetSupport().GetStrategies(player)) {
384+
newCache.m_strategyValues[player][strategy] = GetPayoff(strategy);
385+
}
372386
}
373387
}
374388
newCache.m_valid = true;

src/games/game.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -624,6 +624,7 @@ class GameRep : public std::enable_shared_from_this<GameRep> {
624624
friend class StrategySupportProfile;
625625
template <class T> friend class MixedBehaviorProfile;
626626
template <class T> friend class MixedStrategyProfile;
627+
template <class T> friend class MixedStrategyProfileRep;
627628
template <class T> friend class TableMixedStrategyProfileRep;
628629

629630
protected:

src/games/gameagg.cc

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ template <class T> T AGGMixedStrategyProfileRep<T>::GetPayoff(int pl) const
102102
const GameStrategy strategy =
103103
this->m_support.GetGame()->GetPlayer(i + 1)->GetStrategy(j + 1);
104104
const int ind = this->m_profileIndex.at(strategy);
105-
s[g.aggPtr->firstAction(i) + j] = (ind == -1) ? (T)0 : this->m_probs[ind];
105+
s[g.aggPtr->firstAction(i) + j] = (ind == -1) ? (T)0 : this->m_probs.GetFlattened()[ind];
106106
}
107107
}
108108
return (T)g.aggPtr->getMixedPayoff(pl - 1, s);
@@ -125,7 +125,7 @@ T AGGMixedStrategyProfileRep<T>::GetPayoffDeriv(int pl, const GameStrategy &ps)
125125
const GameStrategy strategy =
126126
this->m_support.GetGame()->GetPlayer(i + 1)->GetStrategy(j + 1);
127127
const int &ind = this->m_profileIndex.at(strategy);
128-
s[g.aggPtr->firstAction(i) + j] = (ind == -1) ? (T)0 : this->m_probs[ind];
128+
s[g.aggPtr->firstAction(i) + j] = (ind == -1) ? (T)0 : this->m_probs.GetFlattened()[ind];
129129
}
130130
}
131131
}
@@ -162,7 +162,7 @@ T AGGMixedStrategyProfileRep<T>::GetPayoffDeriv(int pl, const GameStrategy &ps1,
162162
const GameStrategy strategy =
163163
this->m_support.GetGame()->GetPlayer(i + 1)->GetStrategy(j + 1);
164164
const int ind = this->m_profileIndex.at(strategy);
165-
s[g.aggPtr->firstAction(i) + j] = (ind == -1) ? (T)0 : this->m_probs[ind];
165+
s[g.aggPtr->firstAction(i) + j] = (ind == -1) ? (T)0 : this->m_probs.GetFlattened()[ind];
166166
}
167167
}
168168
}

src/games/gamebagg.cc

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ template <class T> T BAGGMixedStrategyProfileRep<T>::GetPayoff(int pl) const
113113
->GetPlayer(g.baggPtr->typeOffset[i] + tp + 1)
114114
->GetStrategy(j + 1);
115115
const int ind = this->m_profileIndex.at(strategy);
116-
s.at(offs) = (ind == -1) ? (T)0 : this->m_probs[ind];
116+
s.at(offs) = (ind == -1) ? (T)0 : this->m_probs.GetFlattened()[ind];
117117
}
118118
}
119119
}
@@ -144,7 +144,8 @@ T BAGGMixedStrategyProfileRep<T>::GetPayoffDeriv(int pl, const GameStrategy &ps)
144144
->GetPlayer(g.baggPtr->typeOffset[i] + tp + 1)
145145
->GetStrategy(j + 1);
146146
const int ind = this->m_profileIndex.at(strategy);
147-
s.at(g.baggPtr->firstAction(i, tp) + j) = (ind == -1) ? Rational(0) : this->m_probs[ind];
147+
s.at(g.baggPtr->firstAction(i, tp) + j) =
148+
(ind == -1) ? Rational(0) : this->m_probs.GetFlattened()[ind];
148149
}
149150
}
150151
}
@@ -191,7 +192,7 @@ T BAGGMixedStrategyProfileRep<T>::GetPayoffDeriv(int pl, const GameStrategy &ps1
191192
->GetStrategy(j + 1);
192193
const int ind = this->m_profileIndex.at(strategy);
193194
s.at(g.baggPtr->firstAction(i, tp) + j) =
194-
static_cast<T>((ind == -1) ? T(0) : this->m_probs[ind]);
195+
static_cast<T>((ind == -1) ? T(0) : this->m_probs.GetFlattened()[ind]);
195196
}
196197
}
197198
}

0 commit comments

Comments
 (0)