Skip to content
94 changes: 94 additions & 0 deletions src/core/util.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include <iomanip>
#include <cmath>
#include <algorithm>
#include <numeric>
#include <map>

namespace Gambit {
Expand Down Expand Up @@ -63,6 +64,99 @@ template <class Key, class T> bool contains(const std::map<Key, T> &map, const K
{
return map.find(key) != map.end();
}

/// @brief A container adaptor which skips over a given value when iterating
template <typename Container, typename T> class exclude_value {
public:
using Iter = decltype(std::begin(std::declval<Container &>()));

class iterator {
public:
using iterator_category = std::forward_iterator_tag;
using value_type = typename std::iterator_traits<Iter>::value_type;
using difference_type = typename std::iterator_traits<Iter>::difference_type;
using reference = typename std::iterator_traits<Iter>::reference;
using pointer = typename std::iterator_traits<Iter>::pointer;

iterator(Iter current, Iter end, const T &value)
: m_current(current), m_end(end), m_value(value)
{
skip_if_value();
}

value_type operator*() const { return *m_current; }
pointer operator->() const { return std::addressof(*m_current); }

iterator &operator++()
{
++m_current;
skip_if_value();
return *this;
}

iterator operator++(int)
{
auto tmp = *this;
++(*this);
return tmp;
}

friend bool operator==(const iterator &a, const iterator &b)
{
return a.m_current == b.m_current;
}

friend bool operator!=(const iterator &a, const iterator &b)
{
return a.m_current != b.m_current;
}

private:
Iter m_current, m_end;
T m_value;

void skip_if_value()
{
while (m_current != m_end && *m_current == m_value) {
++m_current;
}
}
};

exclude_value(const Container &c, const T &value)
: m_begin(c.begin(), c.end(), value), m_end(c.end(), c.end(), value)
{
}

iterator begin() const { return m_begin; }
iterator end() const { return m_end; }

private:
iterator m_begin, m_end;
};

/// @brief Returns the maximum value of the function over the *non-empty* container
template <class Container, class Func>
auto maximize_function(const Container &p_container, const Func &p_function)
{
auto it = p_container.begin();
using T = decltype(p_function(*it));
return std::transform_reduce(
std::next(it), p_container.end(), p_function(*it),
[](const T &a, const T &b) -> T { return std::max(a, b); }, p_function);
}

/// @brief Returns the minimum value of the function over the *non-empty* container
template <class Container, class Func>
auto minimize_function(const Container &p_container, const Func &p_function)
{
auto it = p_container.begin();
using T = decltype(p_function(*it));
return std::transform_reduce(
std::next(it), p_container.end(), p_function(*it),
[](const T &a, const T &b) -> T { return std::min(a, b); }, p_function);
}

//========================================================================
// Exception classes
//========================================================================
Expand Down
30 changes: 14 additions & 16 deletions src/games/behavmixed.cc
Original file line number Diff line number Diff line change
Expand Up @@ -266,12 +266,11 @@ template <class T> T MixedBehaviorProfile<T>::GetLiapValue() const
CheckVersion();
ComputeSolutionData();

auto value = T(0);
for (auto player : m_support.GetGame()->GetPlayers()) {
for (auto infoset : player->GetInfosets()) {
for (auto action : m_support.GetActions(infoset)) {
value += sqr(std::max(GetPayoff(action) - GetPayoff(infoset), T(0)));
}
auto value = static_cast<T>(0);
for (auto infoset : m_support.GetGame()->GetInfosets()) {
for (auto action : m_support.GetActions(infoset)) {
value +=
sqr(std::max(map_actionValues[action] - map_infosetValues[infoset], static_cast<T>(0)));
}
}
return value;
Expand Down Expand Up @@ -352,24 +351,23 @@ template <class T> const T &MixedBehaviorProfile<T>::GetRegret(const GameAction
{
CheckVersion();
ComputeSolutionData();
return map_regret[act];
return map_regret.at(act);
}

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

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

template <class T>
Expand Down
3 changes: 1 addition & 2 deletions src/games/file.cc
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,13 @@
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//

#include <cstdlib>
#include <cctype>
#include <iostream>
#include <fstream>
#include <map>
#include <algorithm>

#include "gambit.h"
#include "gameagg.h"

namespace {
// This anonymous namespace encapsulates the file-parsing code
Expand Down
88 changes: 43 additions & 45 deletions src/games/game.cc
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ MixedStrategyProfileRep<T>::MixedStrategyProfileRep(const StrategySupportProfile
template <class T> void MixedStrategyProfileRep<T>::SetCentroid()
{
for (auto player : m_support.GetGame()->GetPlayers()) {
T center = T(1) / T(m_support.GetStrategies(player).size());
T center = static_cast<T>(1) / static_cast<T>(m_support.GetStrategies(player).size());
for (auto strategy : m_support.GetStrategies(player)) {
(*this)[strategy] = center;
}
Expand All @@ -277,39 +277,6 @@ std::unique_ptr<MixedStrategyProfileRep<T>> MixedStrategyProfileRep<T>::Normaliz
return norm;
}

template <class T> T MixedStrategyProfileRep<T>::GetRegret(const GameStrategy &p_strategy) const
{
const GamePlayer player = p_strategy->GetPlayer();
T payoff = GetPayoffDeriv(player->GetNumber(), p_strategy);
T brpayoff = payoff;
for (auto strategy : player->GetStrategies()) {
if (strategy != p_strategy) {
brpayoff = std::max(brpayoff, GetPayoffDeriv(player->GetNumber(), strategy));
}
}
return brpayoff - payoff;
}

template <class T> T MixedStrategyProfileRep<T>::GetRegret(const GamePlayer &p_player) const
{
auto strategies = p_player->GetStrategies();
T br_payoff = std::accumulate(std::next(strategies.begin()), strategies.end(),
GetPayoff(*strategies.begin()),
[this](const T &x, const GameStrategy &strategy) {
return std::max(x, GetPayoff(strategy));
});
return br_payoff - GetPayoff(p_player);
}

template <class T> T MixedStrategyProfileRep<T>::GetMaxRegret() const
{
auto players = m_support.GetGame()->GetPlayers();
return std::accumulate(players.begin(), players.end(), T(0),
[this](const T &x, const GamePlayer &player) {
return std::max(x, this->GetRegret(player));
});
}

//========================================================================
// MixedStrategyProfile<T>: Lifecycle
//========================================================================
Expand All @@ -333,8 +300,7 @@ MixedStrategyProfile<T>::MixedStrategyProfile(const MixedBehaviorProfile<T> &p_p
}

template <class T>
MixedStrategyProfile<T> &
MixedStrategyProfile<T>::operator=(const MixedStrategyProfile<T> &p_profile)
MixedStrategyProfile<T> &MixedStrategyProfile<T>::operator=(const MixedStrategyProfile &p_profile)
{
if (this != &p_profile) {
InvalidateCache();
Expand Down Expand Up @@ -377,16 +343,15 @@ template <class T> MixedStrategyProfile<T> MixedStrategyProfile<T>::ToFullSuppor

template <class T> void MixedStrategyProfile<T>::ComputePayoffs() const
{
if (!map_profile_payoffs.empty()) {
// caches (map_profile_payoffs and map_strategy_payoffs) are valid,
if (!m_payoffs.empty()) {
// caches (m_payoffs and m_strategyValues) are valid,
// so don't compute anything, simply return
return;
}
for (const auto &player : m_rep->m_support.GetPlayers()) {
map_profile_payoffs[player] = GetPayoff(player);
// values of the player's strategies
m_payoffs[player] = GetPayoff(player);
for (const auto &strategy : m_rep->m_support.GetStrategies(player)) {
map_strategy_payoffs[player][strategy] = GetPayoff(strategy);
m_strategyValues[player][strategy] = GetPayoff(strategy);
}
}
};
Expand All @@ -397,14 +362,47 @@ template <class T> T MixedStrategyProfile<T>::GetLiapValue() const
ComputePayoffs();

auto liapValue = static_cast<T>(0);
for (auto [player, payoff] : map_profile_payoffs) {
for (auto v : map_strategy_payoffs[player]) {
liapValue += sqr(std::max(v.second - payoff, static_cast<T>(0)));
}
for (auto p : m_payoffs) {
liapValue += std::transform_reduce(
m_strategyValues.at(p.first).begin(), m_strategyValues.at(p.first).end(),
static_cast<T>(0), std::plus<T>(), [&p](const auto &v) -> T {
return sqr(std::max(v.second - p.second, static_cast<T>(0)));
});
}
return liapValue;
}

template <class T> T MixedStrategyProfile<T>::GetRegret(const GameStrategy &p_strategy) const
{
CheckVersion();
ComputePayoffs();
auto player = p_strategy->GetPlayer();
T best_other_payoff = maximize_function(exclude_value(player->GetStrategies(), p_strategy),
[this, &player](const auto &strategy) -> T {
return m_strategyValues.at(player).at(strategy);
});
return std::max(best_other_payoff - m_strategyValues.at(player).at(p_strategy),
static_cast<T>(0));
}

template <class T> T MixedStrategyProfile<T>::GetRegret(const GamePlayer &p_player) const
{
CheckVersion();
ComputePayoffs();
auto br_payoff =
maximize_function(p_player->GetStrategies(), [this, p_player](const auto &strategy) -> T {
return m_strategyValues.at(p_player).at(strategy);
});
return br_payoff - m_payoffs.at(p_player);
}

template <class T> T MixedStrategyProfile<T>::GetMaxRegret() const
{
CheckVersion();
return maximize_function(GetGame()->GetPlayers(),
[this](const auto &player) -> T { return this->GetRegret(player); });
}

template class MixedStrategyProfileRep<double>;
template class MixedStrategyProfileRep<Rational>;

Expand Down
2 changes: 1 addition & 1 deletion src/games/gameobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ template <class P, class T> class ElementCollection {
}
value_type operator*() const { return m_container->at(m_index); }

inline const P &GetOwner() const { return m_owner; }
const P &GetOwner() const { return m_owner; }
};

ElementCollection() = default;
Expand Down
Loading
Loading