Skip to content

Commit 90060bc

Browse files
authored
Implement GameRep::GetInfosets range class (#674)
This replaces the previous implementation of `GameRep::GetInfosets`, which created an explicit container of `GameInfoset` objects, with a new range class that iterates over all personal player information sets on demand. This is now used in the agent version of liap. Closes #520.
1 parent b66d630 commit 90060bc

File tree

6 files changed

+108
-46
lines changed

6 files changed

+108
-46
lines changed

src/core/array.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ template <class T> class Array {
4242
public:
4343
using iterator = typename std::vector<T>::iterator;
4444
using const_iterator = typename std::vector<T>::const_iterator;
45+
using value_type = typename std::vector<T>::value_type;
4546
using reference = typename std::vector<T>::reference;
4647
using const_reference = typename std::vector<T>::const_reference;
4748

src/core/util.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,15 @@ auto minimize_function(const Container &p_container, const Func &p_function)
157157
[](const T &a, const T &b) -> T { return std::min(a, b); }, p_function);
158158
}
159159

160+
/// @brief Returns the sum of the function over the container
161+
template <class Container, class Func>
162+
auto sum_function(const Container &p_container, const Func &p_function)
163+
{
164+
using T = decltype(p_function(*(p_container.begin())));
165+
return std::transform_reduce(p_container.begin(), p_container.end(), static_cast<T>(0),
166+
std::plus<>{}, p_function);
167+
}
168+
160169
//========================================================================
161170
// Exception classes
162171
//========================================================================

src/games/game.h

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -901,10 +901,12 @@ class GameRep : public std::enable_shared_from_this<GameRep> {
901901

902902
/// @name Information sets
903903
//@{
904+
class Infosets;
905+
904906
/// Returns the iset'th information set in the game (numbered globally)
905907
virtual GameInfoset GetInfoset(int iset) const { throw UndefinedException(); }
906908
/// Returns the set of information sets in the game
907-
virtual std::vector<GameInfoset> GetInfosets() const { throw UndefinedException(); }
909+
virtual Infosets GetInfosets() const;
908910
/// Sort the information sets for each player in a canonical order
909911
virtual void SortInfosets() {}
910912
/// Returns the set of actions taken by the infoset's owner before reaching this infoset
@@ -956,6 +958,83 @@ class GameRep : public std::enable_shared_from_this<GameRep> {
956958
virtual void BuildComputedValues() const {}
957959
};
958960

961+
class GameRep::Infosets {
962+
Players m_players;
963+
964+
public:
965+
explicit Infosets(const Players &outer) : m_players(outer) {}
966+
967+
class iterator {
968+
using OuterIter = Players::iterator;
969+
using InnerIter = GamePlayerRep::Infosets::iterator;
970+
971+
OuterIter m_playerIterator, m_playerEnd;
972+
InnerIter m_infosetIterator, m_infosetEnd;
973+
974+
void next()
975+
{
976+
while (m_playerIterator != m_playerEnd && m_infosetIterator == m_infosetEnd) {
977+
++m_playerIterator;
978+
if (m_playerIterator != m_playerEnd) {
979+
auto infosets = (*m_playerIterator)->GetInfosets();
980+
m_infosetIterator = infosets.begin();
981+
m_infosetEnd = infosets.end();
982+
}
983+
}
984+
}
985+
986+
public:
987+
using iterator_category = std::forward_iterator_tag;
988+
using value_type = GameInfoset;
989+
using reference = GameInfoset;
990+
using pointer = GameInfoset;
991+
using difference_type = std::ptrdiff_t;
992+
993+
iterator() = default;
994+
995+
iterator(const OuterIter &p_playerIterator, const OuterIter &p_playerEnd)
996+
: m_playerIterator(p_playerIterator), m_playerEnd(p_playerEnd)
997+
{
998+
if (m_playerIterator != m_playerEnd) {
999+
const auto infosets = (*m_playerIterator)->GetInfosets();
1000+
m_infosetIterator = infosets.begin();
1001+
m_infosetEnd = infosets.end();
1002+
}
1003+
next();
1004+
}
1005+
1006+
reference operator*() const { return *m_infosetIterator; }
1007+
pointer operator->() const { return *m_infosetIterator; }
1008+
1009+
iterator &operator++()
1010+
{
1011+
++m_infosetIterator;
1012+
next();
1013+
return *this;
1014+
}
1015+
1016+
iterator operator++(int)
1017+
{
1018+
iterator tmp = *this;
1019+
++(*this);
1020+
return tmp;
1021+
}
1022+
1023+
friend bool operator==(const iterator &a, const iterator &b)
1024+
{
1025+
return a.m_playerIterator == b.m_playerIterator &&
1026+
(a.m_playerIterator == a.m_playerEnd || a.m_infosetIterator == b.m_infosetIterator);
1027+
}
1028+
1029+
friend bool operator!=(const iterator &a, const iterator &b) { return !(a == b); }
1030+
};
1031+
1032+
iterator begin() const { return {m_players.begin(), m_players.end()}; }
1033+
iterator end() const { return {m_players.end(), m_players.end()}; }
1034+
};
1035+
1036+
inline GameRep::Infosets GameRep::GetInfosets() const { return Infosets(GetPlayers()); }
1037+
9591038
//=======================================================================
9601039
// Inline members of game representation classes
9611040
//=======================================================================

src/games/gametree.cc

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1193,15 +1193,6 @@ GameInfoset GameTreeRep::GetInfoset(int p_index) const
11931193
throw std::out_of_range("Infoset index out of range");
11941194
}
11951195

1196-
std::vector<GameInfoset> GameTreeRep::GetInfosets() const
1197-
{
1198-
std::vector<GameInfoset> infosets;
1199-
for (const auto &player : m_players) {
1200-
std::copy(player->m_infosets.begin(), player->m_infosets.end(), std::back_inserter(infosets));
1201-
}
1202-
return infosets;
1203-
}
1204-
12051196
//------------------------------------------------------------------------
12061197
// GameTreeRep: Outcomes
12071198
//------------------------------------------------------------------------

src/games/gametree.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,8 +126,6 @@ class GameTreeRep : public GameExplicitRep {
126126
//@{
127127
/// Returns the iset'th information set in the game (numbered globally)
128128
GameInfoset GetInfoset(int iset) const override;
129-
/// Returns the set of information sets in the game
130-
std::vector<GameInfoset> GetInfosets() const override;
131129
/// Sort the information sets for each player in a canonical order
132130
void SortInfosets() override;
133131
/// Returns the set of actions taken by the infoset's owner before reaching this infoset

src/solvers/liap/efgliap.cc

Lines changed: 18 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -38,18 +38,10 @@ class AgentLyapunovFunction : public FunctionOnSimplices {
3838
: m_game(p_start.GetGame()), m_profile(p_start)
3939
{
4040
m_scale = m_game->GetMaxPayoff() - m_game->GetMinPayoff();
41-
if (m_scale == 0.0) {
42-
m_scale = 1.0;
43-
}
44-
else {
45-
m_scale = 1.0 / m_scale;
46-
}
47-
48-
for (const auto &player : m_game->GetPlayers()) {
49-
for (const auto &infoset : player->GetInfosets()) {
50-
m_shape.push_back(infoset->GetActions().size());
51-
}
52-
}
41+
m_scale = (m_scale == 0.0) ? 1.0 : 1.0 / m_scale;
42+
std::transform(
43+
m_game->GetInfosets().begin(), m_game->GetInfosets().end(), std::back_inserter(m_shape),
44+
[](const auto &infoset) -> std::size_t { return infoset->GetActions().size(); });
5345
}
5446

5547
~AgentLyapunovFunction() override = default;
@@ -72,35 +64,29 @@ class AgentLyapunovFunction : public FunctionOnSimplices {
7264
inline double sum_infoset_probs(const MixedBehaviorProfile<double> &p_profile,
7365
const GameInfoset &p_infoset)
7466
{
75-
auto actions = p_infoset->GetActions();
76-
return std::accumulate(actions.begin(), actions.end(), 0.0,
77-
[p_profile](double t, const GameAction &a) { return t + p_profile[a]; });
67+
return sum_function(p_infoset->GetActions(),
68+
[&p_profile](const auto &action) { return p_profile[action]; });
7869
}
7970

8071
double
8172
AgentLyapunovFunction::PenalizedLiapValue(const MixedBehaviorProfile<double> &p_profile) const
8273
{
8374
double value = 0.0;
84-
// Liapunov function proper - should be replaced with call to profile once
85-
// the penalty is removed from that implementation.
86-
for (auto player : p_profile.GetGame()->GetPlayers()) {
87-
for (auto infoset : player->GetInfosets()) {
88-
for (auto action : infoset->GetActions()) {
89-
value += sqr(
90-
std::max(m_scale * (p_profile.GetPayoff(action) - p_profile.GetPayoff(infoset)), 0.0));
91-
}
92-
}
75+
// Liapunov function proper.
76+
for (const auto &infoset : p_profile.GetGame()->GetInfosets()) {
77+
double infosetValue = p_profile.GetPayoff(infoset);
78+
value += sum_function(infoset->GetActions(), [&](const auto &action) -> double {
79+
return sqr(std::max(m_scale * (p_profile.GetPayoff(action) - infosetValue), 0.0));
80+
});
9381
}
9482
// Penalty function for non-negativity constraint for each action
9583
for (auto element : static_cast<const Vector<double> &>(p_profile)) {
9684
value += m_penalty * sqr(std::min(element, 0.0));
9785
}
9886
// Penalty function for sum-to-one constraint for each action
99-
for (auto player : p_profile.GetGame()->GetPlayers()) {
100-
for (auto infoset : player->GetInfosets()) {
101-
value += m_penalty * sqr(sum_infoset_probs(m_profile, infoset) - 1.0);
102-
}
103-
}
87+
value += sum_function(p_profile.GetGame()->GetInfosets(), [&](const auto &infoset) -> double {
88+
return m_penalty * sqr(sum_infoset_probs(m_profile, infoset) - 1.0);
89+
});
10490
return value;
10591
}
10692

@@ -131,11 +117,9 @@ namespace {
131117
MixedBehaviorProfile<double> EnforceNonnegativity(const MixedBehaviorProfile<double> &p_profile)
132118
{
133119
auto profile = p_profile;
134-
for (auto player : p_profile.GetGame()->GetPlayers()) {
135-
for (auto infoset : player->GetInfosets()) {
136-
for (auto action : infoset->GetActions()) {
137-
profile[action] = std::max(profile[action], 0.0);
138-
}
120+
for (const auto &infoset : p_profile.GetGame()->GetInfosets()) {
121+
for (const auto &action : infoset->GetActions()) {
122+
profile[action] = std::max(profile[action], 0.0);
139123
}
140124
}
141125
return profile.Normalize();

0 commit comments

Comments
 (0)