Skip to content

Commit 788ed67

Browse files
authored
Modernisation and consistency of regret and liap values (#672)
* For mixed strategy profiles, be consistent in using the cached payoffs for both liap value and regret * Refactor some common code (e.g. maximising a function over a container of values) into a suitable template * Make mixed behaviour profiles parallel with mixed strategy profiles in the implementation style of liap value and regret.
1 parent 1cb87a4 commit 788ed67

7 files changed

Lines changed: 172 additions & 102 deletions

File tree

src/core/util.h

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include <iomanip>
2929
#include <cmath>
3030
#include <algorithm>
31+
#include <numeric>
3132
#include <map>
3233

3334
namespace Gambit {
@@ -63,6 +64,99 @@ template <class Key, class T> bool contains(const std::map<Key, T> &map, const K
6364
{
6465
return map.find(key) != map.end();
6566
}
67+
68+
/// @brief A container adaptor which skips over a given value when iterating
69+
template <typename Container, typename T> class exclude_value {
70+
public:
71+
using Iter = decltype(std::begin(std::declval<Container &>()));
72+
73+
class iterator {
74+
public:
75+
using iterator_category = std::forward_iterator_tag;
76+
using value_type = typename std::iterator_traits<Iter>::value_type;
77+
using difference_type = typename std::iterator_traits<Iter>::difference_type;
78+
using reference = typename std::iterator_traits<Iter>::reference;
79+
using pointer = typename std::iterator_traits<Iter>::pointer;
80+
81+
iterator(Iter current, Iter end, const T &value)
82+
: m_current(current), m_end(end), m_value(value)
83+
{
84+
skip_if_value();
85+
}
86+
87+
value_type operator*() const { return *m_current; }
88+
pointer operator->() const { return std::addressof(*m_current); }
89+
90+
iterator &operator++()
91+
{
92+
++m_current;
93+
skip_if_value();
94+
return *this;
95+
}
96+
97+
iterator operator++(int)
98+
{
99+
auto tmp = *this;
100+
++(*this);
101+
return tmp;
102+
}
103+
104+
friend bool operator==(const iterator &a, const iterator &b)
105+
{
106+
return a.m_current == b.m_current;
107+
}
108+
109+
friend bool operator!=(const iterator &a, const iterator &b)
110+
{
111+
return a.m_current != b.m_current;
112+
}
113+
114+
private:
115+
Iter m_current, m_end;
116+
T m_value;
117+
118+
void skip_if_value()
119+
{
120+
while (m_current != m_end && *m_current == m_value) {
121+
++m_current;
122+
}
123+
}
124+
};
125+
126+
exclude_value(const Container &c, const T &value)
127+
: m_begin(c.begin(), c.end(), value), m_end(c.end(), c.end(), value)
128+
{
129+
}
130+
131+
iterator begin() const { return m_begin; }
132+
iterator end() const { return m_end; }
133+
134+
private:
135+
iterator m_begin, m_end;
136+
};
137+
138+
/// @brief Returns the maximum value of the function over the *non-empty* container
139+
template <class Container, class Func>
140+
auto maximize_function(const Container &p_container, const Func &p_function)
141+
{
142+
auto it = p_container.begin();
143+
using T = decltype(p_function(*it));
144+
return std::transform_reduce(
145+
std::next(it), p_container.end(), p_function(*it),
146+
[](const T &a, const T &b) -> T { return std::max(a, b); }, p_function);
147+
}
148+
149+
/// @brief Returns the minimum value of the function over the *non-empty* container
150+
template <class Container, class Func>
151+
auto minimize_function(const Container &p_container, const Func &p_function)
152+
{
153+
auto it = p_container.begin();
154+
using T = decltype(p_function(*it));
155+
return std::transform_reduce(
156+
std::next(it), p_container.end(), p_function(*it),
157+
[](const T &a, const T &b) -> T { return std::min(a, b); }, p_function);
158+
}
159+
66160
//========================================================================
67161
// Exception classes
68162
//========================================================================

src/games/behavmixed.cc

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -266,12 +266,11 @@ template <class T> T MixedBehaviorProfile<T>::GetLiapValue() const
266266
CheckVersion();
267267
ComputeSolutionData();
268268

269-
auto value = T(0);
270-
for (auto player : m_support.GetGame()->GetPlayers()) {
271-
for (auto infoset : player->GetInfosets()) {
272-
for (auto action : m_support.GetActions(infoset)) {
273-
value += sqr(std::max(GetPayoff(action) - GetPayoff(infoset), T(0)));
274-
}
269+
auto value = static_cast<T>(0);
270+
for (auto infoset : m_support.GetGame()->GetInfosets()) {
271+
for (auto action : m_support.GetActions(infoset)) {
272+
value +=
273+
sqr(std::max(map_actionValues[action] - map_infosetValues[infoset], static_cast<T>(0)));
275274
}
276275
}
277276
return value;
@@ -352,24 +351,23 @@ template <class T> const T &MixedBehaviorProfile<T>::GetRegret(const GameAction
352351
{
353352
CheckVersion();
354353
ComputeSolutionData();
355-
return map_regret[act];
354+
return map_regret.at(act);
356355
}
357356

358357
template <class T> T MixedBehaviorProfile<T>::GetRegret(const GameInfoset &p_infoset) const
359358
{
360-
auto actions = p_infoset->GetActions();
361-
T br_payoff = std::accumulate(
362-
std::next(actions.begin()), actions.end(), GetPayoff(*actions.begin()),
363-
[this](const T &x, const GameAction &action) { return std::max(x, GetPayoff(action)); });
364-
return br_payoff - GetPayoff(p_infoset);
359+
CheckVersion();
360+
ComputeSolutionData();
361+
T br_payoff = maximize_function(p_infoset->GetActions(), [this](const auto &action) -> T {
362+
return map_actionValues.at(action);
363+
});
364+
return br_payoff - map_infosetValues[p_infoset];
365365
}
366366

367367
template <class T> T MixedBehaviorProfile<T>::GetMaxRegret() const
368368
{
369-
auto infosets = m_support.GetGame()->GetInfosets();
370-
return std::accumulate(
371-
infosets.begin(), infosets.end(), T(0),
372-
[this](const T &x, const GameInfoset &infoset) { return std::max(x, GetRegret(infoset)); });
369+
return maximize_function(m_support.GetGame()->GetInfosets(),
370+
[this](const auto &infoset) -> T { return this->GetRegret(infoset); });
373371
}
374372

375373
template <class T>

src/games/file.cc

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,13 @@
2020
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
2121
//
2222

23-
#include <cstdlib>
24-
#include <cctype>
2523
#include <iostream>
2624
#include <fstream>
2725
#include <map>
2826
#include <algorithm>
2927

3028
#include "gambit.h"
29+
#include "gameagg.h"
3130

3231
namespace {
3332
// This anonymous namespace encapsulates the file-parsing code

src/games/game.cc

Lines changed: 43 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ MixedStrategyProfileRep<T>::MixedStrategyProfileRep(const StrategySupportProfile
251251
template <class T> void MixedStrategyProfileRep<T>::SetCentroid()
252252
{
253253
for (auto player : m_support.GetGame()->GetPlayers()) {
254-
T center = T(1) / T(m_support.GetStrategies(player).size());
254+
T center = static_cast<T>(1) / static_cast<T>(m_support.GetStrategies(player).size());
255255
for (auto strategy : m_support.GetStrategies(player)) {
256256
(*this)[strategy] = center;
257257
}
@@ -277,39 +277,6 @@ std::unique_ptr<MixedStrategyProfileRep<T>> MixedStrategyProfileRep<T>::Normaliz
277277
return norm;
278278
}
279279

280-
template <class T> T MixedStrategyProfileRep<T>::GetRegret(const GameStrategy &p_strategy) const
281-
{
282-
const GamePlayer player = p_strategy->GetPlayer();
283-
T payoff = GetPayoffDeriv(player->GetNumber(), p_strategy);
284-
T brpayoff = payoff;
285-
for (auto strategy : player->GetStrategies()) {
286-
if (strategy != p_strategy) {
287-
brpayoff = std::max(brpayoff, GetPayoffDeriv(player->GetNumber(), strategy));
288-
}
289-
}
290-
return brpayoff - payoff;
291-
}
292-
293-
template <class T> T MixedStrategyProfileRep<T>::GetRegret(const GamePlayer &p_player) const
294-
{
295-
auto strategies = p_player->GetStrategies();
296-
T br_payoff = std::accumulate(std::next(strategies.begin()), strategies.end(),
297-
GetPayoff(*strategies.begin()),
298-
[this](const T &x, const GameStrategy &strategy) {
299-
return std::max(x, GetPayoff(strategy));
300-
});
301-
return br_payoff - GetPayoff(p_player);
302-
}
303-
304-
template <class T> T MixedStrategyProfileRep<T>::GetMaxRegret() const
305-
{
306-
auto players = m_support.GetGame()->GetPlayers();
307-
return std::accumulate(players.begin(), players.end(), T(0),
308-
[this](const T &x, const GamePlayer &player) {
309-
return std::max(x, this->GetRegret(player));
310-
});
311-
}
312-
313280
//========================================================================
314281
// MixedStrategyProfile<T>: Lifecycle
315282
//========================================================================
@@ -333,8 +300,7 @@ MixedStrategyProfile<T>::MixedStrategyProfile(const MixedBehaviorProfile<T> &p_p
333300
}
334301

335302
template <class T>
336-
MixedStrategyProfile<T> &
337-
MixedStrategyProfile<T>::operator=(const MixedStrategyProfile<T> &p_profile)
303+
MixedStrategyProfile<T> &MixedStrategyProfile<T>::operator=(const MixedStrategyProfile &p_profile)
338304
{
339305
if (this != &p_profile) {
340306
InvalidateCache();
@@ -377,16 +343,15 @@ template <class T> MixedStrategyProfile<T> MixedStrategyProfile<T>::ToFullSuppor
377343

378344
template <class T> void MixedStrategyProfile<T>::ComputePayoffs() const
379345
{
380-
if (!map_profile_payoffs.empty()) {
381-
// caches (map_profile_payoffs and map_strategy_payoffs) are valid,
346+
if (!m_payoffs.empty()) {
347+
// caches (m_payoffs and m_strategyValues) are valid,
382348
// so don't compute anything, simply return
383349
return;
384350
}
385351
for (const auto &player : m_rep->m_support.GetPlayers()) {
386-
map_profile_payoffs[player] = GetPayoff(player);
387-
// values of the player's strategies
352+
m_payoffs[player] = GetPayoff(player);
388353
for (const auto &strategy : m_rep->m_support.GetStrategies(player)) {
389-
map_strategy_payoffs[player][strategy] = GetPayoff(strategy);
354+
m_strategyValues[player][strategy] = GetPayoff(strategy);
390355
}
391356
}
392357
};
@@ -397,14 +362,47 @@ template <class T> T MixedStrategyProfile<T>::GetLiapValue() const
397362
ComputePayoffs();
398363

399364
auto liapValue = static_cast<T>(0);
400-
for (auto [player, payoff] : map_profile_payoffs) {
401-
for (auto v : map_strategy_payoffs[player]) {
402-
liapValue += sqr(std::max(v.second - payoff, static_cast<T>(0)));
403-
}
365+
for (auto p : m_payoffs) {
366+
liapValue += std::transform_reduce(
367+
m_strategyValues.at(p.first).begin(), m_strategyValues.at(p.first).end(),
368+
static_cast<T>(0), std::plus<T>(), [&p](const auto &v) -> T {
369+
return sqr(std::max(v.second - p.second, static_cast<T>(0)));
370+
});
404371
}
405372
return liapValue;
406373
}
407374

375+
template <class T> T MixedStrategyProfile<T>::GetRegret(const GameStrategy &p_strategy) const
376+
{
377+
CheckVersion();
378+
ComputePayoffs();
379+
auto player = p_strategy->GetPlayer();
380+
T best_other_payoff = maximize_function(exclude_value(player->GetStrategies(), p_strategy),
381+
[this, &player](const auto &strategy) -> T {
382+
return m_strategyValues.at(player).at(strategy);
383+
});
384+
return std::max(best_other_payoff - m_strategyValues.at(player).at(p_strategy),
385+
static_cast<T>(0));
386+
}
387+
388+
template <class T> T MixedStrategyProfile<T>::GetRegret(const GamePlayer &p_player) const
389+
{
390+
CheckVersion();
391+
ComputePayoffs();
392+
auto br_payoff =
393+
maximize_function(p_player->GetStrategies(), [this, p_player](const auto &strategy) -> T {
394+
return m_strategyValues.at(p_player).at(strategy);
395+
});
396+
return br_payoff - m_payoffs.at(p_player);
397+
}
398+
399+
template <class T> T MixedStrategyProfile<T>::GetMaxRegret() const
400+
{
401+
CheckVersion();
402+
return maximize_function(GetGame()->GetPlayers(),
403+
[this](const auto &player) -> T { return this->GetRegret(player); });
404+
}
405+
408406
template class MixedStrategyProfileRep<double>;
409407
template class MixedStrategyProfileRep<Rational>;
410408

src/games/gameobject.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ template <class P, class T> class ElementCollection {
188188
}
189189
value_type operator*() const { return m_container->at(m_index); }
190190

191-
inline const P &GetOwner() const { return m_owner; }
191+
const P &GetOwner() const { return m_owner; }
192192
};
193193

194194
ElementCollection() = default;

0 commit comments

Comments
 (0)