//
// $Source: /cvsroot/gambit/gambit/sources/libgambit/subgame.cc,v $
// $Date: 2006/11/28 18:01:57 $
// $Revision: 1.5 $
//
// DESCRIPTION:
// Utilities for computing and verifying subgame-perfection
//
// This file is part of Gambit
// Copyright (c) 2006, 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"
#include "subgame.h"
namespace Gambit {
// A nested anonymous namespace to privatize these functions
namespace {
///
/// Returns a copy of the game 'p_efg', including only the subtree rooted
/// at 'p_node'.
///
Game CopyGame(const Game &p_efg, const GameNode &p_node)
{
std::ostringstream os;
p_efg->WriteEfgFile(os, p_node);
std::istringstream is(os.str());
return ReadGame(is);
}
///
/// Returns a list of the root nodes of all the immediate proper subgames
/// in the subtree rooted at 'p_node'.
///
void ChildSubgames(const GameNode &p_node, List<GameNode> &p_list)
{
if (p_node->IsSubgameRoot()) {
p_list.Append(p_node);
}
else {
for (int i = 1; i <= p_node->NumChildren();
ChildSubgames(p_node->GetChild(i++), p_list));
}
}
} // end nested anonymous namespace
//
// Some general notes on the strategy for solving by subgames:
//
// * We work with a *copy* of the original game, which is destroyed
// as we go.
// * Before solving, information set labels on the copy game are
// set to unique IDs. These are used to match up information
// sets in the subgames (which are themselves copies) to the
// original game.
// * We only carry around DVectors instead of full MixedBehavProfiles,
// because MixedBehavProfiles allocate space several times the
// size of the tree to carry around useful quantities. These
// quantities are irrelevant for this calculation, so we only
// store the probabilities, and convert to MixedBehavProfiles
// at the end of the computation
//
template <class T, class SolverType>
void SolveSubgames(const BehavSupport &p_support,
const DVector<T> &p_templateSolution,
SolverType p_solver,
GameNode n,
List<DVector<T> > &solns,
List<GameOutcome> &values)
{
Game efg = p_support.GetGame();
List<DVector<T> > thissolns;
thissolns.Append(p_templateSolution);
((Vector<T> &) thissolns[1]).operator=(T(0));
List<GameNode> subroots;
for (int i = 1; i <= n->NumChildren(); i++) {
ChildSubgames(n->GetChild(i), subroots);
}
List<Array<GameOutcome> > subrootvalues;
subrootvalues.Append(Array<GameOutcome>(subroots.Length()));
for (int i = 1; i <= subroots.Length(); i++) {
//printf("Looking at subgame %d of %d\n", i, subroots.Length());
List<DVector<T> > subsolns;
List<GameOutcome> subvalues;
SolveSubgames(p_support, p_templateSolution, p_solver,
subroots[i], subsolns, subvalues);
if (subsolns.Length() == 0) {
solns = List<DVector<T> >();
return;
}
//printf("Found %d subsolutions for subgame %d of %d\n",
//subsolns.Length(), i, subroots.Length());
List<DVector<T> > newsolns;
List<Array<GameOutcome> > newsubrootvalues;
//printf("Merging with %d existing solutions\n", thissolns.Length());
for (int soln = 1; soln <= thissolns.Length(); soln++) {
for (int subsoln = 1; subsoln <= subsolns.Length(); subsoln++) {
//printf("Merging existing %d with new %d\n", soln, subsoln);
DVector<T> bp(thissolns[soln]);
DVector<T> tmp(subsolns[subsoln]);
for (int j = 1; j <= bp.Length(); j++) {
bp[j] += tmp[j];
}
newsolns.Append(bp);
newsubrootvalues.Append(subrootvalues[soln]);
newsubrootvalues[newsubrootvalues.Length()][i] = subvalues[subsoln];
}
}
thissolns = newsolns;
subrootvalues = newsubrootvalues;
//printf("Finished solving subgame %d\n", i);
}
for (int soln = 1; soln <= thissolns.Length(); soln++) {
//printf("Analyzing scenario %d of %d\n", soln, thissolns.Length());
for (int i = 1; i <= subroots.Length(); i++) {
subroots[i]->SetOutcome(subrootvalues[soln][i]);
}
Game subgame = CopyGame(efg, n);
// this prevents double-counting of outcomes at roots of subgames
// by convention, we will just put the payoffs in the parent subgame
subgame->GetRoot()->SetOutcome(0);
BehavSupport subsupport(subgame);
// Here, we build the support for the subgame
/*
for (int pl = 1; pl <= subgame->NumPlayers(); pl++) {
GamePlayer subplayer = subgame->GetPlayer(pl);
GamePlayer player = p_support.GetGame()->GetPlayer(pl);
for (int iset = 1; iset <= subplayer->NumInfosets(); iset++) {
GameInfoset subinfoset = subplayer->GetInfoset(iset);
for (int j = 1; j <= player->NumInfosets(); j++) {
GameInfoset infoset = player->GetInfoset(j);
if (subinfoset->GetLabel() == infoset->GetLabel()) {
for (int act = 1; act <= infoset->NumActions(); act++) {
if (!p_support.Contains(infoset->GetAction(act))) {
subsupport.RemoveAction(subinfoset->GetAction(act));
}
}
break;
}
}
}
}
*/
List<MixedBehavProfile<T> > sol = (*p_solver)(subsupport);
if (sol.Length() == 0) {
solns = List<DVector<T> >();
//printf("No solutions found\n");
return;
}
// Put behavior profile in "total" solution here...
for (int solno = 1; solno <= sol.Length(); solno++) {
solns.Append(thissolns[soln]);
for (int pl = 1; pl <= subgame->NumPlayers(); pl++) {
GamePlayer subplayer = subgame->GetPlayer(pl);
GamePlayer player = p_support.GetGame()->GetPlayer(pl);
for (int iset = 1; iset <= subplayer->NumInfosets(); iset++) {
GameInfoset subinfoset = subplayer->GetInfoset(iset);
for (int j = 1; j <= player->NumInfosets(); j++) {
if (subinfoset->GetLabel() == player->GetInfoset(j)->GetLabel()) {
int id = atoi(subinfoset->GetLabel().c_str());
for (int act = 1; act <= subsupport.NumActions(pl, iset); act++) {
int actno = subsupport.GetAction(pl, iset, act)->GetNumber();
solns[solns.Length()](pl, id, actno) = sol[solno](pl, iset, act);
}
break;
}
}
}
}
Vector<T> subval(subgame->NumPlayers());
GameOutcome outcome = n->GetOutcome();
for (int pl = 1; pl <= subgame->NumPlayers(); pl++) {
subval[pl] = sol[solno].GetPayoff(pl);
if (outcome) {
subval[pl] += outcome->GetPayoff<T>(pl);
}
}
GameOutcome ov = efg->NewOutcome();
for (int pl = 1; pl <= efg->NumPlayers(); pl++) {
ov->SetPayoff(pl, ToText(subval[pl]));
}
values.Append(ov);
}
//printf("Finished with scenario %d of %d; total solutions so far = %d\n",
//soln, thissolns.Length(), solns.Length());
}
n->DeleteTree();
}
template <class T, typename SolverType>
List<MixedBehavProfile<T> >
SolveBySubgames(const BehavSupport &p_support,
SolverType p_solver)
{
Game efg = CopyGame(p_support.GetGame(), p_support.GetGame()->GetRoot());
for (int pl = 1; pl <= efg->NumPlayers(); pl++) {
for (int iset = 1; iset <= efg->GetPlayer(pl)->NumInfosets(); iset++) {
efg->GetPlayer(pl)->GetInfoset(iset)->SetLabel(ToText(iset));
}
}
BehavSupport support(efg);
for (int pl = 1; pl <= efg->NumPlayers(); pl++) {
GamePlayer player = p_support.GetGame()->GetPlayer(pl);
for (int iset = 1; iset <= player->NumInfosets(); iset++) {
GameInfoset infoset = player->GetInfoset(iset);
for (int act = 1; act <= infoset->NumActions(); act++) {
if (!p_support.Contains(infoset->GetAction(act))) {
support.RemoveAction(efg->GetPlayer(pl)->GetInfoset(iset)->GetAction(act));
}
}
}
}
List<DVector<T> > vectors;
List<GameOutcome> values;
SolveSubgames(support, DVector<T>(support.NumActions()),
p_solver, efg->GetRoot(), vectors, values);
List<MixedBehavProfile<T> > solutions;
for (int i = 1; i <= vectors.Length(); i++) {
solutions.Append(MixedBehavProfile<T>(p_support));
for (int j = 1; j <= vectors[i].Length(); j++) {
solutions[i][j] = vectors[i][j];
}
}
return solutions;
}
//
// Explicit instantiations follow
//
template List<MixedBehavProfile<double> >
SolveBySubgames(const BehavSupport &p_support, DoubleSolver p_solver);
template List<MixedBehavProfile<Rational> >
SolveBySubgames(const BehavSupport &p_support, RationalSolver p_solver);
} // end namespace Gambit
syntax highlighted by Code2HTML, v. 0.9.1