//
// $Source: /cvsroot/gambit/gambit/sources/libgambit/behavspt.cc,v $
// $Date: 2006/08/17 02:37:01 $
// $Revision: 1.13 $
//
// DESCRIPTION:
// Implementation of supports for extensive forms
//
// This file is part of Gambit
// Copyright (c) 2002, The Gambit Project
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//
#include "libgambit.h"
namespace Gambit {
template <class T> void RemoveRedundancies(List<T> &p_list)
{
int i = 1; int j = 2;
while (i < p_list.Length()) {
if (p_list[i] == p_list[j])
p_list.Remove(j);
else
j++;
if (j > p_list.Length()) { i++; j = i+1; }
}
}
//========================================================================
// BehavSupport: Lifecycle
//========================================================================
BehavSupport::BehavSupport(const Game &p_efg)
: m_efg(p_efg),
m_infosetActive(0, p_efg->NumPlayers()),
m_nonterminalActive(0, p_efg->NumPlayers())
{
for (int pl = 1; pl <= p_efg->NumPlayers(); pl++) {
m_actions.Append(Array<Array<GameAction> >());
GamePlayer player = p_efg->GetPlayer(pl);
for (int iset = 1; iset <= player->NumInfosets(); iset++) {
GameInfoset infoset = player->GetInfoset(iset);
m_actions[pl].Append(Array<GameAction>());
for (int act = 1; act <= infoset->NumActions(); act++) {
m_actions[pl][iset].Append(infoset->GetAction(act));
}
}
}
// Initialize the list of reachable information sets and nodes
for (int pl = 0; pl <= GetGame()->NumPlayers(); pl++) {
GamePlayer player = (pl == 0) ? GetGame()->GetChance() : GetGame()->GetPlayer(pl);
List<bool> is_players_infoset_active;
List<List<bool> > is_players_node_active;
for (int iset = 1; iset <= player->NumInfosets(); iset++) {
is_players_infoset_active.Append(false);
List<bool> is_infosets_node_active;
for (int n = 1; n <= player->GetInfoset(iset)->NumMembers()
; n++)
is_infosets_node_active.Append(false);
is_players_node_active.Append(is_infosets_node_active);
}
m_infosetActive[pl] = is_players_infoset_active;
m_nonterminalActive[pl] = is_players_node_active;
}
ActivateSubtree(GetGame()->GetRoot());
}
//========================================================================
// BehavSupport: Operator overloading
//========================================================================
bool BehavSupport::operator==(const BehavSupport &p_support) const
{
return (m_actions == p_support.m_actions);
}
//========================================================================
// BehavSupport: General information
//========================================================================
PVector<int> BehavSupport::NumActions(void) const
{
PVector<int> answer(m_efg->NumInfosets());
for (int pl = 1; pl <= m_efg->NumPlayers(); pl++) {
for (int iset = 1; iset <= m_efg->GetPlayer(pl)->NumInfosets(); iset++) {
answer(pl, iset) = NumActions(pl, iset);
}
}
return answer;
}
int BehavSupport::GetIndex(const GameAction &a) const
{
if (a->GetInfoset()->GetGame() != m_efg) throw MismatchException();
int pl = a->GetInfoset()->GetPlayer()->GetNumber();
if (pl == 0) {
// chance action; all chance actions are always in the support
return a->GetNumber();
}
else {
return m_actions[pl][a->GetInfoset()->GetNumber()].Find(a);
}
}
int BehavSupport::NumDegreesOfFreedom(void) const
{
int answer = 0;
List<GameInfoset> active_infosets = ReachableInfosets(GetGame()->GetRoot());
for (int i = 1; i <= active_infosets.Length(); i++)
answer += NumActions(active_infosets[i]->GetPlayer()->GetNumber(),
active_infosets[i]->GetNumber()) - 1;
return answer;
}
bool BehavSupport::HasActiveActionAt(const GameInfoset &infoset) const
{
return (m_actions[infoset->GetPlayer()->GetNumber()][infoset->GetNumber()].Length() > 0);
}
bool BehavSupport::HasActiveActionsAtAllInfosets(void) const
{
for (int pl = 1; pl <= m_actions.Length(); pl++) {
for (int iset = 1; iset <= m_actions[pl].Length(); iset++) {
if (m_actions[pl][iset].Length() == 0) return false;
}
}
return true;
}
bool BehavSupport::RemoveAction(const GameAction &s)
{
List<GameNode> startlist(ReachableMembers(s->GetInfoset()));
for (int i = 1; i <= startlist.Length(); i++)
DeactivateSubtree(startlist[i]->GetChild(s->GetNumber()));
GameInfoset infoset = s->GetInfoset();
GamePlayer player = infoset->GetPlayer();
Array<GameAction> &actions = m_actions[player->GetNumber()][infoset->GetNumber()];
if (!actions.Contains(s)) {
return false;
}
else if (actions.Length() == 1) {
actions.Remove(actions.Find(s));
return false;
}
else {
actions.Remove(actions.Find(s));
return true;
}
}
bool BehavSupport::RemoveAction(const GameAction &s, List<GameInfoset> &list)
{
List<GameNode> startlist(ReachableMembers(s->GetInfoset()));
for (int i = 1; i <= startlist.Length(); i++) {
DeactivateSubtree(startlist[i]->GetChild(s->GetNumber()), list);
}
// the following returns false if s was not in the support
return RemoveAction(s);
}
void BehavSupport::AddAction(const GameAction &s)
{
GameInfoset infoset = s->GetInfoset();
GamePlayer player = infoset->GetPlayer();
Array<GameAction> &actions = m_actions[player->GetNumber()][infoset->GetNumber()];
int act = 1;
while (act <= actions.Length()) {
if (actions[act] == s) {
break;
}
else if (actions[act]->GetNumber() > s->GetNumber()) {
actions.Insert(s, act);
break;
}
act++;
}
if (act > actions.Length()) {
actions.Append(s);
}
List<GameNode> startlist(ReachableMembers(s->GetInfoset()));
for (int i = 1; i <= startlist.Length(); i++)
DeactivateSubtree(startlist[i]);
}
int BehavSupport::NumSequences(int j) const
{
if (j < 1 || j > m_efg->NumPlayers()) return 1;
List<GameInfoset> isets = ReachableInfosets(m_efg->GetPlayer(j));
int num = 1;
for(int i = 1; i <= isets.Length(); i++)
num+=NumActions(isets[i]->GetPlayer()->GetNumber(),
isets[i]->GetNumber());
return num;
}
int BehavSupport::NumSequences(void) const
{
int total = 0;
for (int i = 1 ; i <= m_efg->NumPlayers(); i++)
total += NumSequences(i);
return total;
}
List<GameNode> BehavSupport::ReachableNonterminalNodes(const GameNode &n) const
{
List<GameNode> answer;
if (!n->IsTerminal()) {
int pl = n->GetInfoset()->GetPlayer()->GetNumber();
int iset = n->GetInfoset()->GetNumber();
for (int i = 1; i <= NumActions(pl, iset); i++) {
GameNode nn = n->GetChild(GetAction(pl, iset, i)->GetNumber());
if (!nn->IsTerminal()) {
answer.Append(nn);
answer += ReachableNonterminalNodes(nn);
}
}
}
return answer;
}
List<GameNode>
BehavSupport::ReachableNonterminalNodes(const GameNode &n,
const GameAction &a) const
{
List<GameNode> answer;
GameNode nn = n->GetChild(a->GetNumber());
if (!nn->IsTerminal()) {
answer.Append(nn);
answer += ReachableNonterminalNodes(nn);
}
return answer;
}
List<GameInfoset>
BehavSupport::ReachableInfosets(const GamePlayer &p) const
{
Array<GameInfoset> isets;
for (int iset = 1; iset <= p->NumInfosets(); iset++) {
isets.Append(p->GetInfoset(iset));
}
List<GameInfoset> answer;
for (int i = isets.First(); i <= isets.Last(); i++)
if (MayReach(isets[i]))
answer.Append(isets[i]);
return answer;
}
List<GameInfoset> BehavSupport::ReachableInfosets(const GameNode &n) const
{
List<GameInfoset> answer;
List<GameNode> nodelist = ReachableNonterminalNodes(n);
for (int i = 1; i <= nodelist.Length(); i++)
answer.Append(nodelist[i]->GetInfoset());
RemoveRedundancies(answer);
return answer;
}
List<GameInfoset>
BehavSupport::ReachableInfosets(const GameNode &n,
const GameAction &a) const
{
List<GameInfoset> answer;
List<GameNode> nodelist = ReachableNonterminalNodes(n,a);
for (int i = 1; i <= nodelist.Length(); i++)
answer.Append(nodelist[i]->GetInfoset());
RemoveRedundancies(answer);
return answer;
}
bool BehavSupport::MayReach(const GameInfoset &i) const
{
for (int j = 1; j <= i->NumMembers(); j++)
if (MayReach(i->GetMember(j)))
return true;
return false;
}
bool BehavSupport::MayReach(const GameNode &n) const
{
if (n == m_efg->GetRoot())
return true;
else {
if (!Contains(n->GetPriorAction()))
return false;
else
return MayReach(n->GetParent());
}
}
// This class iterates
// over contingencies that are relevant once a particular node
// has been reached.
class BehavConditionalIterator {
private:
Game _efg;
BehavSupport _support;
PureBehavProfile _profile;
PVector<int> _current;
Array<Array<bool> > _is_active;
Array<int> _num_active_infosets;
mutable Vector<Rational> _payoff;
public:
BehavConditionalIterator(const BehavSupport &);
BehavConditionalIterator(const BehavSupport &, const List<GameInfoset> &);
~BehavConditionalIterator();
void First(void); // Sets each infoset's action to the first in the support
void Set(int pl, int iset, int act);
void Set(const GameAction &a);
const PureBehavProfile &GetProfile(void) const { return _profile; }
int NextContingency(void); // Needs rewriting
Rational GetPayoff(int pl) const;
Rational GetPayoff(const GameNode &, int pl) const;
};
BehavConditionalIterator::BehavConditionalIterator(const BehavSupport &s)
: _efg(s.GetGame()), _support(s),
_profile(s.GetGame()), _current(s.GetGame()->NumInfosets()),
_is_active(),
_num_active_infosets(_efg->NumPlayers()),
_payoff(_efg->NumPlayers())
{
for (int pl = 1; pl <= _efg->NumPlayers(); pl++) {
_num_active_infosets[pl] = 0;
Array<bool> active_for_pl(_efg->GetPlayer(pl)->NumInfosets());
for (int iset = 1; iset <= _efg->GetPlayer(pl)->NumInfosets(); iset++) {
active_for_pl[iset] = true;
_num_active_infosets[pl]++;
}
_is_active.Append(active_for_pl);
}
First();
}
BehavConditionalIterator::BehavConditionalIterator(const BehavSupport &s,
const List<GameInfoset>& active)
: _efg(s.GetGame()), _support(s),
_profile(s.GetGame()), _current(s.GetGame()->NumInfosets()),
_is_active(),
_num_active_infosets(_efg->NumPlayers()),
_payoff(_efg->NumPlayers())
{
for (int pl = 1; pl <= _efg->NumPlayers(); pl++) {
_num_active_infosets[pl] = 0;
Array<bool> active_for_pl(_efg->GetPlayer(pl)->NumInfosets());
for (int iset = 1; iset <= _efg->GetPlayer(pl)->NumInfosets(); iset++) {
if ( active.Contains(_efg->GetPlayer(pl)->GetInfoset(iset)) ) {
active_for_pl[iset] = true;
_num_active_infosets[pl]++;
}
else
active_for_pl[iset] = false;
}
_is_active.Append(active_for_pl);
}
First();
}
BehavConditionalIterator::~BehavConditionalIterator()
{ }
void BehavConditionalIterator::First(void)
{
for (int pl = 1; pl <= _efg->NumPlayers(); pl++) {
for (int iset = 1; iset <= _efg->GetPlayer(pl)->NumInfosets(); iset++) {
_current(pl, iset) = 1;
if (_is_active[pl][iset])
_profile.SetAction(_support.GetAction(pl, iset, 1));
}
}
}
void BehavConditionalIterator::Set(int pl, int iset, int act)
{
_current(pl, iset) = act;
_profile.SetAction(_support.GetAction(pl, iset, act));
}
void BehavConditionalIterator::Set(const GameAction &a)
{
_profile.SetAction(a);
}
int BehavConditionalIterator::NextContingency(void)
{
int pl = _efg->NumPlayers();
while (pl > 0 && _num_active_infosets[pl] == 0)
--pl;
if (pl == 0) return 0;
int iset = _efg->GetPlayer(pl)->NumInfosets();
while (true) {
if (_is_active[pl][iset])
if (_current(pl, iset) < _support.NumActions(pl, iset)) {
_current(pl, iset) += 1;
_profile.SetAction(_support.GetAction(pl, iset, _current(pl, iset)));
return 1;
}
else {
_current(pl, iset) = 1;
_profile.SetAction(_support.GetAction(pl, iset, 1));
}
iset--;
if (iset == 0) {
do {
--pl;
} while (pl > 0 && _num_active_infosets[pl] == 0);
if (pl == 0) return 0;
iset = _efg->GetPlayer(pl)->NumInfosets();
}
}
}
Rational BehavConditionalIterator::GetPayoff(int pl) const
{
return _profile.GetPayoff<Rational>(pl);
}
Rational BehavConditionalIterator::GetPayoff(const GameNode &n, int pl) const
{
return _profile.GetNodeValue<Rational>(n, pl);
}
bool BehavSupport::Dominates(const GameAction &a, const GameAction &b,
bool strong, bool conditional) const
{
GameInfoset infoset = a->GetInfoset();
if (infoset != b->GetInfoset()) {
throw UndefinedException();
}
const BehavSupport SAct(*this);
GamePlayer player = infoset->GetPlayer();
int pl = player->GetNumber();
bool equal = true;
if (!conditional) {
for (BehavIterator iter(*this, a); !iter.AtEnd(); iter++) {
Rational ap = iter->GetActionValue<Rational>(a);
Rational bp = iter->GetActionValue<Rational>(b);
if (strong)
{ if (ap <= bp) return false; }
else
if (ap < bp) return false;
else if (ap > bp) equal = false;
}
}
else {
List<GameNode> nodelist = SAct.ReachableMembers(infoset);
if (nodelist.Length() == 0) {
// This may not be a good idea; I suggest checking for this
// prior to entry
for (int i = 1; i <= infoset->NumMembers(); i++) {
nodelist.Append(infoset->GetMember(i));
}
}
for (int n = 1; n <= nodelist.Length(); n++) {
List<GameInfoset> L;
L += ReachableInfosets(nodelist[n], a);
L += ReachableInfosets(nodelist[n], b);
RemoveRedundancies(L);
BehavConditionalIterator A(*this,L), B(*this,L);
A.Set(a);
B.Set(b);
do {
Rational ap = A.GetPayoff(nodelist[n],pl);
Rational bp = B.GetPayoff(nodelist[n],pl);
if (strong)
{ if (ap <= bp) return false; }
else
if (ap < bp) return false;
else if (ap > bp) equal = false;
} while (A.NextContingency() && B.NextContingency());
}
}
if (strong) return true;
else return (!equal);
/*
return ::Dominates(*this,player->GetNumber(),infoset->GetNumber(),
a->GetNumber(),b->GetNumber(),
strong, conditional);
*/
}
bool SomeElementDominates(const BehavSupport &S,
const Array<GameAction> &array,
const GameAction &a,
const bool strong,
const bool conditional)
{
for (int i = 1; i <= array.Length(); i++)
if (array[i] != a)
if (S.Dominates(array[i],a,strong,conditional)) {
return true;
}
return false;
}
bool BehavSupport::IsDominated(const GameAction &a,
bool strong, bool conditional) const
{
int pl = a->GetInfoset()->GetPlayer()->GetNumber();
int iset = a->GetInfoset()->GetNumber();
Array<GameAction> array(m_actions[pl][iset]);
return SomeElementDominates(*this,array,a,strong,conditional);
}
bool InfosetHasDominatedElement(const BehavSupport &S,
const GameInfoset &p_infoset,
bool strong,
bool conditional)
{
int pl = p_infoset->GetPlayer()->GetNumber();
int iset = p_infoset->GetNumber();
Array<GameAction> actions;
for (int act = 1; act <= S.NumActions(pl, iset); act++) {
actions.Append(S.GetAction(pl, iset, act));
}
for (int i = 1; i <= actions.Length(); i++)
if (SomeElementDominates(S,actions,actions[i],
strong,conditional))
return true;
return false;
}
bool ElimDominatedInInfoset(const BehavSupport &S, BehavSupport &T,
int pl, int iset,
bool strong, bool conditional)
{
Array<GameAction> actions;
for (int act = 1; act <= S.NumActions(pl, iset); act++) {
actions.Append(S.GetAction(pl, iset, act));
}
Array<bool> is_dominated(actions.Length());
for (int k = 1; k <= actions.Length(); k++)
is_dominated[k] = false;
for (int i = 1; i <= actions.Length(); i++)
for (int j = 1; j <= actions.Length(); j++)
if (i != j && !is_dominated[i] && !is_dominated[j])
if (S.Dominates(actions[i], actions[j], strong, conditional)) {
is_dominated[j] = true;
}
bool action_was_eliminated = false;
int k = 1;
while (k <= actions.Length() && !action_was_eliminated) {
if (is_dominated[k]) action_was_eliminated = true;
else k++;
}
while (k <= actions.Length()) {
if (is_dominated[k])
T.RemoveAction(actions[k]);
k++;
}
return action_was_eliminated;
}
bool ElimDominatedForPlayer(const BehavSupport &S, BehavSupport &T,
const int pl, int &cumiset,
const bool strong,
const bool conditional)
{
bool action_was_eliminated = false;
for (int iset = 1; iset <= S.GetGame()->GetPlayer(pl)->NumInfosets();
iset++, cumiset++) {
if (ElimDominatedInInfoset(S, T, pl, iset, strong, conditional))
action_was_eliminated = true;
}
return action_was_eliminated;
}
BehavSupport BehavSupport::Undominated(bool strong, bool conditional,
const Array<int> &players,
std::ostream &) const
{
BehavSupport T(*this);
int cumiset = 0;
for (int i = 1; i <= players.Length(); i++) {
int pl = players[i];
ElimDominatedForPlayer(*this, T, pl, cumiset,
strong, conditional);
}
return T;
}
// Utilities
bool BehavSupport::HasActiveMembers(int pl, int iset) const
{
for (int i = 1; i <= m_nonterminalActive[pl][iset].Length(); i++) {
if (m_nonterminalActive[pl][iset][i]) {
return true;
}
}
return false;
}
void BehavSupport::activate(const GameNode &n)
{
m_nonterminalActive[n->GetPlayer()->GetNumber()]
[n->GetInfoset()->GetNumber()]
[n->NumberInInfoset()] = true;
}
void BehavSupport::deactivate(const GameNode &n)
{
m_nonterminalActive[n->GetPlayer()->GetNumber()]
[n->GetInfoset()->GetNumber()]
[n->NumberInInfoset()] = false;
}
void BehavSupport::activate(const GameInfoset &i)
{
m_infosetActive[i->GetPlayer()->GetNumber()][i->GetNumber()] = true;
}
void BehavSupport::deactivate(const GameInfoset &i)
{
m_infosetActive[i->GetPlayer()->GetNumber()][i->GetNumber()] = false;
}
void BehavSupport::ActivateSubtree(const GameNode &n)
{
if (!n->IsTerminal()) {
activate(n);
activate(n->GetInfoset());
if (n->GetInfoset()->GetPlayer()->IsChance()) {
for (int i = 1; i <= n->NumChildren(); i++) {
ActivateSubtree(n->GetChild(i));
}
}
else {
const Array<GameAction> &actions(m_actions[n->GetInfoset()->GetPlayer()->GetNumber()][n->GetInfoset()->GetNumber()]);
for (int i = 1; i <= actions.Length(); i++) {
ActivateSubtree(n->GetChild(actions[i]->GetNumber()));
}
}
}
}
void BehavSupport::DeactivateSubtree(const GameNode &n)
{
if (!n->IsTerminal()) { // THIS ALL LOOKS FISHY
deactivate(n);
if ( !HasActiveMembers(n->GetInfoset()->GetPlayer()->GetNumber(),
n->GetInfoset()->GetNumber())) {
deactivate(n->GetInfoset());
}
Array<GameAction> actions(m_actions[n->GetInfoset()->GetPlayer()->GetNumber()][n->GetInfoset()->GetNumber()]);
for (int i = 1; i <= actions.Length(); i++) {
DeactivateSubtree(n->GetChild(actions[i]->GetNumber()));
}
}
}
void
BehavSupport::DeactivateSubtree(const GameNode &n, List<GameInfoset> &list)
{
if (!n->IsTerminal()) {
deactivate(n);
if (!HasActiveMembers(n->GetInfoset()->GetPlayer()->GetNumber(),
n->GetInfoset()->GetNumber())) {
list.Append(n->GetInfoset());
deactivate(n->GetInfoset());
}
Array<GameAction> actions(m_actions[n->GetInfoset()->GetPlayer()->GetNumber()][n->GetInfoset()->GetNumber()]);
for (int i = 1; i <= actions.Length(); i++) {
DeactivateSubtree(n->GetChild(actions[i]->GetNumber()), list);
}
}
}
List<GameNode>
BehavSupport::ReachableMembers(const GameInfoset &i) const
{
List<GameNode> answer;
int pl = i->GetPlayer()->GetNumber();
int iset = i->GetNumber();
for (int j = 1; j <= i->NumMembers(); j++)
if (m_nonterminalActive[pl][iset][j])
answer.Append(i->GetMember(j));
return answer;
}
List<GameNode>
BehavSupport::ReachableNonterminalNodes(void) const
{
List<GameNode> answer;
for (int pl = 1; pl <= GetGame()->NumPlayers(); pl++) {
GamePlayer p = GetGame()->GetPlayer(pl);
for (int iset = 1; iset <= p->NumInfosets(); iset++)
answer += ReachableMembers(p->GetInfoset(iset));
}
return answer;
}
int BehavSupport::NumActiveMembers(const GameInfoset &p_infoset) const
{
int answer = 0;
int pl = p_infoset->GetPlayer()->GetNumber();
int iset = p_infoset->GetNumber();
for (int i = 1; i <= m_nonterminalActive[pl][iset].Length(); i++) {
if (m_nonterminalActive[pl][iset][i]) {
answer++;
}
}
return answer;
}
bool BehavSupport::IsActive(const GameInfoset &i) const
{
return m_infosetActive[i->GetPlayer()->GetNumber()][i->GetNumber()];
}
bool BehavSupport::IsActive(const GameNode &n) const
{
return m_nonterminalActive[n->GetInfoset()->GetPlayer()->GetNumber()]
[n->GetInfoset()->GetNumber()][n->NumberInInfoset()];
}
bool BehavSupport::HasActiveActionsAtActiveInfosets(void) const
{
for (int pl = 1; pl <= GetGame()->NumPlayers(); pl++) {
for (int iset = 1; iset <= GetGame()->GetPlayer(pl)->NumInfosets(); iset++) {
if (m_infosetActive[pl][iset] && NumActions(pl, iset) == 0) {
return false;
}
}
}
return true;
}
bool BehavSupport::HasActiveActionsAtActiveInfosetsAndNoOthers(void) const
{
for (int pl = 1; pl <= GetGame()->NumPlayers(); pl++) {
for (int iset = 1; iset <= GetGame()->GetPlayer(pl)->NumInfosets(); iset++) {
if (m_infosetActive[pl][iset] && NumActions(pl, iset) == 0) {
return false;
}
if (!m_infosetActive[pl][iset] && NumActions(pl, iset) > 0) {
return false;
}
}
}
return true;
}
} // end namespace Gambit
syntax highlighted by Code2HTML, v. 0.9.1