Skip to content

Commit 4d669e7

Browse files
Refactoring of sequence form as implementation detail of behavior support profile
* Migrates collection classes from being members of `GameSequenceForm` to `BehaviorSupportProfile` * Promotes `GameSequenceRep` to be a full-fledged `GameObject` (instead of a simple `std::shared_ptr`) --------- Co-authored-by: Theodore Turocy <ted.turocy@gmail.com>
1 parent ecc2577 commit 4d669e7

File tree

6 files changed

+329
-303
lines changed

6 files changed

+329
-303
lines changed

src/games/behavspt.cc

Lines changed: 179 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ size_t BehaviorSupportProfile::BehaviorProfileLength() const
6666

6767
void BehaviorSupportProfile::AddAction(const GameAction &p_action)
6868
{
69+
m_reachableInfosets = nullptr;
6970
auto &support = m_actions.at(p_action->GetInfoset());
7071
auto pos = std::find_if(support.begin(), support.end(), [p_action](const GameAction &a) {
7172
return a->GetNumber() >= p_action->GetNumber();
@@ -81,6 +82,7 @@ void BehaviorSupportProfile::AddAction(const GameAction &p_action)
8182

8283
bool BehaviorSupportProfile::RemoveAction(const GameAction &p_action)
8384
{
85+
m_reachableInfosets = nullptr;
8486
auto &support = m_actions.at(p_action->GetInfoset());
8587
auto pos = std::find(support.begin(), support.end(), p_action);
8688
if (pos != support.end()) {
@@ -272,14 +274,12 @@ std::shared_ptr<GameSequenceForm> BehaviorSupportProfile::GetSequenceForm() cons
272274
return m_sequenceForm;
273275
}
274276

275-
SequencesWrapper BehaviorSupportProfile::GetSequences() const
276-
{
277-
return SequencesWrapper(GetSequenceForm()->GetSequences());
278-
}
277+
BehaviorSupportProfile::Sequences BehaviorSupportProfile::GetSequences() const { return {this}; }
279278

280-
PlayerSequencesWrapper BehaviorSupportProfile::GetSequences(GamePlayer &p_player) const
279+
BehaviorSupportProfile::PlayerSequences
280+
BehaviorSupportProfile::GetSequences(GamePlayer &p_player) const
281281
{
282-
return PlayerSequencesWrapper(GetSequenceForm()->GetSequences(p_player));
282+
return {this, p_player};
283283
}
284284

285285
int BehaviorSupportProfile::GetConstraintEntry(const GameInfoset &p_infoset,
@@ -288,27 +288,190 @@ int BehaviorSupportProfile::GetConstraintEntry(const GameInfoset &p_infoset,
288288
return GetSequenceForm()->GetConstraintEntry(p_infoset, p_action);
289289
}
290290

291-
const Rational &BehaviorSupportProfile::GetPayoff(
292-
const std::map<GamePlayer, std::shared_ptr<GameSequenceRep>> &p_profile,
293-
const GamePlayer &p_player) const
291+
const Rational &
292+
BehaviorSupportProfile::GetPayoff(const std::map<GamePlayer, GameSequence> &p_profile,
293+
const GamePlayer &p_player) const
294294
{
295295
return GetSequenceForm()->GetPayoff(p_profile, p_player);
296296
}
297297

298-
MixedBehaviorProfile<double> BehaviorSupportProfile::ToMixedBehaviorProfile(
299-
const std::map<std::shared_ptr<GameSequenceRep>, double> &p_profile) const
298+
BehaviorSupportProfile::SequenceContingencies
299+
BehaviorSupportProfile::GetSequenceContingencies() const
300+
{
301+
return {this};
302+
}
303+
304+
MixedBehaviorProfile<double>
305+
BehaviorSupportProfile::ToMixedBehaviorProfile(const std::map<GameSequence, double> &x) const
300306
{
301-
return GetSequenceForm()->ToMixedBehaviorProfile(p_profile);
307+
MixedBehaviorProfile<double> b(*this);
308+
for (auto sequence : GetSequences()) {
309+
if (sequence->action == nullptr) {
310+
continue;
311+
}
312+
const double parent_prob = x.at(sequence->parent.lock());
313+
if (parent_prob > 0) {
314+
b[sequence->action] = x.at(sequence) / parent_prob;
315+
}
316+
else {
317+
b[sequence->action] = 0;
318+
}
319+
}
320+
return b;
302321
}
303322

304-
InfosetsWrapper BehaviorSupportProfile::GetInfosets() const
323+
size_t BehaviorSupportProfile::Sequences::size() const
305324
{
306-
return InfosetsWrapper(GetSequenceForm()->GetInfosets());
325+
return std::accumulate(m_support->GetSequenceForm()->m_sequences.cbegin(),
326+
m_support->GetSequenceForm()->m_sequences.cend(), 0,
327+
[](int acc, const std::pair<GamePlayer, std::vector<GameSequence>> &seq) {
328+
return acc + seq.second.size();
329+
});
307330
}
308331

309-
ContingenciesWrapper BehaviorSupportProfile::GetContingencies() const
332+
BehaviorSupportProfile::Sequences::iterator BehaviorSupportProfile::Sequences::begin() const
310333
{
311-
return ContingenciesWrapper(GetSequenceForm()->GetContingencies());
334+
return {m_support->GetSequenceForm(), false};
335+
}
336+
BehaviorSupportProfile::Sequences::iterator BehaviorSupportProfile::Sequences::end() const
337+
{
338+
return {m_support->GetSequenceForm(), true};
339+
}
340+
341+
BehaviorSupportProfile::Sequences::iterator::iterator(
342+
const std::shared_ptr<GameSequenceForm> p_sfg, bool p_end)
343+
: m_sfg(p_sfg)
344+
{
345+
if (p_end) {
346+
m_currentPlayer = m_sfg->m_sequences.cend();
347+
}
348+
else {
349+
m_currentPlayer = m_sfg->m_sequences.cbegin();
350+
m_currentSequence = m_currentPlayer->second.cbegin();
351+
}
352+
}
353+
354+
BehaviorSupportProfile::Sequences::iterator &
355+
BehaviorSupportProfile::Sequences::iterator::operator++()
356+
{
357+
if (m_currentPlayer == m_sfg->m_sequences.cend()) {
358+
return *this;
359+
}
360+
m_currentSequence++;
361+
if (m_currentSequence != m_currentPlayer->second.cend()) {
362+
return *this;
363+
}
364+
m_currentPlayer++;
365+
if (m_currentPlayer != m_sfg->m_sequences.cend()) {
366+
m_currentSequence = m_currentPlayer->second.cbegin();
367+
}
368+
return *this;
369+
}
370+
371+
bool BehaviorSupportProfile::Sequences::iterator::operator==(const iterator &it) const
372+
{
373+
if (m_sfg != it.m_sfg || m_currentPlayer != it.m_currentPlayer) {
374+
return false;
375+
}
376+
if (m_currentPlayer == m_sfg->m_sequences.end()) {
377+
return true;
378+
}
379+
return (m_currentSequence == it.m_currentSequence);
380+
}
381+
382+
std::vector<GameSequence>::const_iterator BehaviorSupportProfile::PlayerSequences::begin() const
383+
{
384+
return m_support->GetSequenceForm()->m_sequences.at(m_player).begin();
385+
}
386+
387+
std::vector<GameSequence>::const_iterator BehaviorSupportProfile::PlayerSequences::end() const
388+
{
389+
return m_support->GetSequenceForm()->m_sequences.at(m_player).end();
390+
}
391+
392+
size_t BehaviorSupportProfile::PlayerSequences::size() const
393+
{
394+
return m_support->GetSequenceForm()->m_sequences.at(m_player).size();
395+
}
396+
397+
BehaviorSupportProfile::SequenceContingencies::iterator::iterator(
398+
const std::shared_ptr<GameSequenceForm> p_sfg, bool p_end)
399+
: m_sfg(p_sfg), m_end(p_end)
400+
{
401+
for (auto [player, sequences] : m_sfg->m_sequences) {
402+
m_indices[player] = 0;
403+
}
404+
}
405+
406+
std::map<GamePlayer, GameSequence>
407+
BehaviorSupportProfile::SequenceContingencies::iterator::operator*() const
408+
{
409+
std::map<GamePlayer, GameSequence> ret;
410+
for (auto [player, index] : m_indices) {
411+
ret[player] = m_sfg->m_sequences.at(player)[index];
412+
}
413+
return ret;
414+
}
415+
416+
std::map<GamePlayer, GameSequence>
417+
BehaviorSupportProfile::SequenceContingencies::iterator::operator->() const
418+
{
419+
std::map<GamePlayer, GameSequence> ret;
420+
for (auto [player, index] : m_indices) {
421+
ret[player] = m_sfg->m_sequences.at(player)[index];
422+
}
423+
return ret;
424+
}
425+
426+
BehaviorSupportProfile::SequenceContingencies::iterator &
427+
BehaviorSupportProfile::SequenceContingencies::iterator::operator++()
428+
{
429+
for (auto [player, index] : m_indices) {
430+
if (index < m_sfg->m_sequences.at(player).size() - 1) {
431+
m_indices[player]++;
432+
return *this;
433+
}
434+
m_indices[player] = 0;
435+
}
436+
m_end = true;
437+
return *this;
438+
}
439+
440+
//========================================================================
441+
// BehaviorSupportProfile: Reachable Information Sets
442+
//========================================================================
443+
444+
void BehaviorSupportProfile::FindReachableInfosets(GameNode p_node) const
445+
{
446+
if (!p_node->IsTerminal()) {
447+
auto infoset = p_node->GetInfoset();
448+
(*m_reachableInfosets)[infoset] = true;
449+
if (p_node->GetPlayer()->IsChance()) {
450+
for (auto action : infoset->GetActions()) {
451+
FindReachableInfosets(p_node->GetChild(action));
452+
}
453+
}
454+
else {
455+
for (auto action : GetActions(infoset)) {
456+
FindReachableInfosets(p_node->GetChild(action));
457+
}
458+
}
459+
}
460+
}
461+
462+
std::shared_ptr<std::map<GameInfoset, bool>> BehaviorSupportProfile::GetReachableInfosets() const
463+
{
464+
if (!m_reachableInfosets) {
465+
m_reachableInfosets = std::make_shared<std::map<GameInfoset, bool>>();
466+
for (size_t pl = 0; pl <= GetGame()->NumPlayers(); pl++) {
467+
const GamePlayer player = (pl == 0) ? GetGame()->GetChance() : GetGame()->GetPlayer(pl);
468+
for (const auto &infoset : player->GetInfosets()) {
469+
(*m_reachableInfosets)[infoset] = false;
470+
}
471+
}
472+
FindReachableInfosets(GetGame()->GetRoot());
473+
}
474+
return m_reachableInfosets;
312475
}
313476

314477
} // end namespace Gambit

src/games/behavspt.h

Lines changed: 109 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
namespace Gambit {
3131

3232
class GameSequenceForm;
33-
class GameSequenceRep;
3433
class SequencesWrapper;
3534
class PlayerSequencesWrapper;
3635
class InfosetsWrapper;
@@ -44,9 +43,10 @@ class ContingenciesWrapper;
4443
/// computational approaches that enumerate possible equilibrium
4544
/// supports.
4645
class BehaviorSupportProfile {
47-
protected:
4846
Game m_efg;
4947
std::map<GameInfoset, std::vector<GameAction>> m_actions;
48+
mutable std::shared_ptr<GameSequenceForm> m_sequenceForm;
49+
mutable std::shared_ptr<std::map<GameInfoset, bool>> m_reachableInfosets;
5050

5151
std::map<GameInfoset, bool> m_infosetReachable;
5252
std::map<GameNode, bool> m_nonterminalReachable;
@@ -57,7 +57,6 @@ class BehaviorSupportProfile {
5757

5858
public:
5959
class Support {
60-
private:
6160
const BehaviorSupportProfile *m_profile;
6261
GameInfoset m_infoset;
6362

@@ -147,19 +146,117 @@ class BehaviorSupportProfile {
147146
BehaviorSupportProfile Undominated(bool p_strict) const;
148147
//@}
149148

150-
mutable std::shared_ptr<GameSequenceForm> m_sequenceForm;
149+
class Infosets {
150+
const BehaviorSupportProfile *m_support;
151+
152+
public:
153+
Infosets(const BehaviorSupportProfile *p_support) : m_support(p_support) {}
154+
155+
size_t size() const
156+
{
157+
auto reachable_infosets = m_support->GetReachableInfosets();
158+
size_t count = 0;
159+
for (auto [infoset, is_reachable] : *reachable_infosets) {
160+
if (is_reachable && !infoset->GetPlayer()->IsChance()) {
161+
++count;
162+
}
163+
}
164+
return count;
165+
}
166+
};
167+
168+
class Sequences {
169+
const BehaviorSupportProfile *m_support;
170+
171+
public:
172+
class iterator {
173+
const std::shared_ptr<GameSequenceForm> m_sfg;
174+
std::map<GamePlayer, std::vector<GameSequence>>::const_iterator m_currentPlayer;
175+
std::vector<GameSequence>::const_iterator m_currentSequence;
176+
177+
public:
178+
iterator(const std::shared_ptr<GameSequenceForm> p_sfg, bool p_end);
179+
180+
GameSequence operator*() const { return *m_currentSequence; }
181+
GameSequence operator->() const { return *m_currentSequence; }
182+
183+
iterator &operator++();
184+
185+
bool operator==(const iterator &it) const;
186+
bool operator!=(const iterator &it) const { return !(*this == it); }
187+
};
188+
189+
Sequences(const BehaviorSupportProfile *p_support) : m_support(p_support) {}
190+
191+
size_t size() const;
192+
193+
iterator begin() const;
194+
iterator end() const;
195+
};
196+
197+
class PlayerSequences {
198+
const BehaviorSupportProfile *m_support;
199+
GamePlayer m_player;
200+
201+
public:
202+
PlayerSequences(const BehaviorSupportProfile *p_support, const GamePlayer &p_player)
203+
: m_support(p_support), m_player(p_player)
204+
{
205+
}
206+
207+
size_t size() const;
208+
std::vector<GameSequence>::const_iterator begin() const;
209+
std::vector<GameSequence>::const_iterator end() const;
210+
};
211+
212+
class SequenceContingencies {
213+
const BehaviorSupportProfile *m_support;
214+
215+
public:
216+
SequenceContingencies(const BehaviorSupportProfile *p_support) : m_support(p_support) {}
217+
218+
class iterator {
219+
private:
220+
const std::shared_ptr<GameSequenceForm> m_sfg;
221+
bool m_end{false};
222+
std::map<GamePlayer, size_t> m_indices;
223+
224+
public:
225+
using iterator_category = std::input_iterator_tag;
226+
227+
iterator(const std::shared_ptr<GameSequenceForm> p_sfg, bool p_end = false);
228+
229+
std::map<GamePlayer, GameSequence> operator*() const;
230+
231+
std::map<GamePlayer, GameSequence> operator->() const;
232+
233+
iterator &operator++();
234+
235+
bool operator==(const iterator &it) const
236+
{
237+
return (m_end == it.m_end && m_sfg == it.m_sfg && m_indices == it.m_indices);
238+
}
239+
bool operator!=(const iterator &it) const { return !(*this == it); }
240+
};
241+
242+
iterator begin() { return {m_support->GetSequenceForm()}; }
243+
iterator end() { return {m_support->GetSequenceForm(), true}; }
244+
};
245+
151246
std::shared_ptr<GameSequenceForm> GetSequenceForm() const;
152-
SequencesWrapper GetSequences() const;
153-
PlayerSequencesWrapper GetSequences(GamePlayer &p_player) const;
247+
Sequences GetSequences() const;
248+
PlayerSequences GetSequences(GamePlayer &p_player) const;
154249
int GetConstraintEntry(const GameInfoset &p_infoset, const GameAction &p_action) const;
155-
const Rational &
156-
GetPayoff(const std::map<GamePlayer, std::shared_ptr<GameSequenceRep>> &p_profile,
157-
const GamePlayer &p_player) const;
250+
const Rational &GetPayoff(const std::map<GamePlayer, GameSequence> &p_profile,
251+
const GamePlayer &p_player) const;
158252
GameRep::Players GetPlayers() const { return GetGame()->GetPlayers(); }
159253
MixedBehaviorProfile<double>
160-
ToMixedBehaviorProfile(const std::map<std::shared_ptr<GameSequenceRep>, double> &) const;
161-
InfosetsWrapper GetInfosets() const;
162-
ContingenciesWrapper GetContingencies() const;
254+
ToMixedBehaviorProfile(const std::map<GameSequence, double> &) const;
255+
Infosets GetInfosets() const { return {this}; };
256+
SequenceContingencies GetSequenceContingencies() const;
257+
258+
void FindReachableInfosets(GameNode p_node) const;
259+
std::shared_ptr<std::map<GameInfoset, bool>> GetReachableInfosets() const;
163260
};
164261

165262
} // end namespace Gambit

0 commit comments

Comments
 (0)