Skip to content

Commit

Permalink
Caching of payoff/strategy values for MixedStrategyProfile
Browse files Browse the repository at this point in the history
  • Loading branch information
rahulsavani authored and tturocy committed Mar 18, 2024
1 parent da89fc0 commit 14df07a
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 43 deletions.
5 changes: 3 additions & 2 deletions ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
- `append_move`/`append_infoset` now resolves either a singleton node reference or any
iterable set of node references
- Additional regret-related functions added to `MixedBehaviorProfile` and `MixedStrategyProfile`
in both C++ and Python.
in both C++ and Python
- Some caching added to payoff/strategy value calculations in `MixedStrategyProfile`

### Changed
- Gambit now requires a compiler that supports C++17.
Expand All @@ -37,7 +38,7 @@
- Improved __repr__ methods in pygambit for game-related classes
- Further extension of test suite for mixed behavior profiles to cover new indexing and profile
order consistency for payoff-related calculations
- Overhaul of caching in MixedBehaviorProfile to use maps (std::map)
- Overhaul of caching in `MixedBehaviorProfile` to use maps (`std::map`)
- Creation of random mixed profiles in pygambit is done with new `Game.random_strategy_profile` and
`Game.random_behavior_profile` methods; these accept `numpy.random.Generator` objects for
reproducible state.
Expand Down
49 changes: 23 additions & 26 deletions src/games/game.cc
Original file line number Diff line number Diff line change
Expand Up @@ -428,13 +428,15 @@ template <class T>
MixedStrategyProfile<T>::MixedStrategyProfile(const MixedStrategyProfile<T> &p_profile)
: m_rep(p_profile.m_rep->Copy())
{
InvalidateCache();
}

template <class T>
MixedStrategyProfile<T> &
MixedStrategyProfile<T>::operator=(const MixedStrategyProfile<T> &p_profile)
{
if (this != &p_profile) {
InvalidateCache();
delete m_rep;
m_rep = p_profile.m_rep->Copy();
}
Expand Down Expand Up @@ -462,7 +464,7 @@ template <class T> Vector<T> MixedStrategyProfile<T>::operator[](const GamePlaye
template <class T> MixedStrategyProfile<T> MixedStrategyProfile<T>::ToFullSupport() const
{
CheckVersion();
MixedStrategyProfile<T> full(m_rep->m_support.GetGame()->NewMixedStrategyProfile((T)0));
MixedStrategyProfile<T> full(m_rep->m_support.GetGame()->NewMixedStrategyProfile(T(0)));

for (int pl = 1; pl <= m_rep->m_support.GetGame()->NumPlayers(); pl++) {
GamePlayer player = m_rep->m_support.GetGame()->GetPlayer(pl);
Expand All @@ -482,41 +484,36 @@ template <class T> MixedStrategyProfile<T> MixedStrategyProfile<T>::ToFullSuppor
//========================================================================
// MixedStrategyProfile<T>: Computation of interesting quantities
//========================================================================

template <class T> T MixedStrategyProfile<T>::GetLiapValue() const
template <class T> void MixedStrategyProfile<T>::ComputePayoffs() const
{
CheckVersion();
static const T BIG1 = (T)100;
static const T BIG2 = (T)100;

T liapValue = (T)0;

if (!map_profile_payoffs.empty()) {
// caches (map_profile_payoffs and map_strategy_payoffs) are valid,
// so don't compute anything, simply return
return;
}
for (auto player : m_rep->m_support.GetPlayers()) {
map_profile_payoffs[player] = GetPayoff(player);
// values of the player's strategies
std::map<GameStrategy, T> values;

T avg = (T)0, sum = (T)0;
for (auto strategy : m_rep->m_support.GetStrategies(player)) {
const T &prob = (*this)[strategy];
values[strategy] = GetPayoff(strategy);
avg += prob * values.at(strategy);
sum += prob;
if (prob < (T)0) {
liapValue += BIG1 * prob * prob; // penalty for negative probabilities
}
map_strategy_payoffs[player][strategy] = GetPayoff(strategy);
}
}
};

template <class T> T MixedStrategyProfile<T>::GetLiapValue() const
{
CheckVersion();
ComputePayoffs();

for (auto v : values) {
T regret = v.second - avg;
if (regret > (T)0) {
T liapValue = T(0);
for (auto player : m_rep->m_support.GetPlayers()) {
for (auto v : map_strategy_payoffs[player]) {
T regret = v.second - map_profile_payoffs[player];
if (regret > T(0)) {
liapValue += regret * regret; // penalty if not best response
}
}

// penalty if sum does not equal to one
liapValue += BIG2 * (sum - (T)1.0) * (sum - (T)1.0);
}

return liapValue;
}

Expand Down
16 changes: 8 additions & 8 deletions src/games/gametable.cc
Original file line number Diff line number Diff line change
Expand Up @@ -165,13 +165,13 @@ T TableMixedStrategyProfileRep<T>::GetPayoff(int pl, int index, int current) con
return static_cast<T>(outcome->GetPayoff(pl));
}
else {
return (T)0;
return T(0);
}
}

T sum = static_cast<T>(0);
for (auto s : this->m_support.GetStrategies(this->m_support.GetGame()->GetPlayer(current))) {
if ((*this)[s] != (T)0) {
if ((*this)[s] != T(0)) {
sum += ((*this)[s] * GetPayoff(pl, index + s->m_offset, current + 1));
}
}
Expand Down Expand Up @@ -200,7 +200,7 @@ void TableMixedStrategyProfileRep<T>::GetPayoffDeriv(int pl, int const_pl, int c
}
else {
for (auto s : this->m_support.GetStrategies(this->m_support.GetGame()->GetPlayer(cur_pl))) {
if ((*this)[s] > (T)0) {
if ((*this)[s] > T(0)) {
GetPayoffDeriv(pl, const_pl, cur_pl + 1, index + s->m_offset, prob * (*this)[s], value);
}
}
Expand All @@ -210,8 +210,8 @@ void TableMixedStrategyProfileRep<T>::GetPayoffDeriv(int pl, int const_pl, int c
template <class T>
T TableMixedStrategyProfileRep<T>::GetPayoffDeriv(int pl, const GameStrategy &strategy) const
{
T value = (T)0;
GetPayoffDeriv(pl, strategy->GetPlayer()->GetNumber(), 1, strategy->m_offset + 1, (T)1, value);
T value = T(0);
GetPayoffDeriv(pl, strategy->GetPlayer()->GetNumber(), 1, strategy->m_offset + 1, T(1), value);
return value;
}

Expand Down Expand Up @@ -248,12 +248,12 @@ T TableMixedStrategyProfileRep<T>::GetPayoffDeriv(int pl, const GameStrategy &st
GamePlayerRep *player1 = strategy1->GetPlayer();
GamePlayerRep *player2 = strategy2->GetPlayer();
if (player1 == player2) {
return (T)0;
return T(0);
}

T value = (T)0;
T value = T(0);
GetPayoffDeriv(pl, player1->GetNumber(), player2->GetNumber(), 1,
strategy1->m_offset + strategy2->m_offset + 1, (T)1, value);
strategy1->m_offset + strategy2->m_offset + 1, T(1), value);
return value;
}

Expand Down
22 changes: 15 additions & 7 deletions src/games/gametree.cc
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,16 @@ template <class T> MixedStrategyProfileRep<T> *TreeMixedStrategyProfileRep<T>::C

template <class T> T TreeMixedStrategyProfileRep<T>::GetPayoff(int pl) const
{
MixedStrategyProfile<T> profile(Copy());
return MixedBehaviorProfile<T>(profile).GetPayoff(pl);
if (mixed_behav_profile_sptr.get() == nullptr) {
MixedStrategyProfile<T> tmp(Copy());
mixed_behav_profile_sptr = std::make_shared<MixedBehaviorProfile<T>>(tmp);
}
return mixed_behav_profile_sptr->GetPayoff(pl);
}

template <class T> void TreeMixedStrategyProfileRep<T>::InvalidateCache() const
{
mixed_behav_profile_sptr = nullptr;
}

template <class T>
Expand All @@ -73,19 +81,19 @@ T TreeMixedStrategyProfileRep<T>::GetPayoffDeriv(int pl, const GameStrategy &str
GamePlayerRep *player1 = strategy1->GetPlayer();
GamePlayerRep *player2 = strategy2->GetPlayer();
if (player1 == player2) {
return (T)0;
return T(0);
}

MixedStrategyProfile<T> foo(Copy());
for (auto strategy : this->m_support.GetStrategies(player1)) {
foo[strategy] = (T)0;
foo[strategy] = T(0);
}
foo[strategy1] = (T)1;
foo[strategy1] = T(1);

for (auto strategy : this->m_support.GetStrategies(player2)) {
foo[strategy] = (T)0;
foo[strategy] = T(0);
}
foo[strategy2] = (T)1;
foo[strategy2] = T(1);

return foo.GetPayoff(pl);
}
Expand Down
5 changes: 5 additions & 0 deletions src/games/gametree.h
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,11 @@ template <class T> class TreeMixedStrategyProfileRep : public MixedStrategyProfi
T GetPayoff(int pl) const override;
T GetPayoffDeriv(int pl, const GameStrategy &) const override;
T GetPayoffDeriv(int pl, const GameStrategy &, const GameStrategy &) const override;

void InvalidateCache() const override;

protected:
mutable std::shared_ptr<MixedBehaviorProfile<T>> mixed_behav_profile_sptr;
};

} // namespace Gambit
Expand Down
18 changes: 18 additions & 0 deletions src/games/stratmixed.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ template <class T> class MixedStrategyProfileRep {
T GetRegret(const GameStrategy &) const;
T GetRegret(const GamePlayer &) const;
T GetMaxRegret() const;

virtual void InvalidateCache() const {};
};

/// \brief A probability distribution over strategies in a game
Expand Down Expand Up @@ -102,11 +104,13 @@ template <class T> class MixedStrategyProfile {
MixedStrategyProfile<T> &operator=(const MixedStrategyProfile<T> &);
MixedStrategyProfile<T> &operator=(const Vector<T> &v)
{
InvalidateCache();
m_rep->m_probs = v;
return *this;
}
MixedStrategyProfile<T> &operator=(const T &c)
{
InvalidateCache();
m_rep->m_probs = c;
return *this;
}
Expand Down Expand Up @@ -137,6 +141,7 @@ template <class T> class MixedStrategyProfile {
T &operator[](int i)
{
CheckVersion();
InvalidateCache();
return m_rep->m_probs[i];
}

Expand All @@ -150,6 +155,7 @@ template <class T> class MixedStrategyProfile {
T &operator[](const GameStrategy &p_strategy)
{
CheckVersion();
InvalidateCache(); // NEW
return m_rep->operator[](p_strategy);
}

Expand Down Expand Up @@ -204,6 +210,18 @@ template <class T> class MixedStrategyProfile {

/// @name Computation of interesting quantities
//@{
/// Used to read payoffs from cache or compute them and cache them if needed
void ComputePayoffs() const;
mutable std::map<GamePlayer, std::map<GameStrategy, T>> map_strategy_payoffs;
mutable std::map<GamePlayer, T> map_profile_payoffs;
/// Reset cache for payoffs and strategy values
virtual void InvalidateCache() const
{
map_strategy_payoffs.clear();
map_profile_payoffs.clear();
m_rep->InvalidateCache();
}

/// Computes the payoff of the profile to player 'pl'
T GetPayoff(int pl) const
{
Expand Down

0 comments on commit 14df07a

Please sign in to comment.