// // $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 &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 void SolveSubgames(const BehavSupport &p_support, const DVector &p_templateSolution, SolverType p_solver, GameNode n, List > &solns, List &values) { Game efg = p_support.GetGame(); List > thissolns; thissolns.Append(p_templateSolution); ((Vector &) thissolns[1]).operator=(T(0)); List subroots; for (int i = 1; i <= n->NumChildren(); i++) { ChildSubgames(n->GetChild(i), subroots); } List > subrootvalues; subrootvalues.Append(Array(subroots.Length())); for (int i = 1; i <= subroots.Length(); i++) { //printf("Looking at subgame %d of %d\n", i, subroots.Length()); List > subsolns; List subvalues; SolveSubgames(p_support, p_templateSolution, p_solver, subroots[i], subsolns, subvalues); if (subsolns.Length() == 0) { solns = List >(); return; } //printf("Found %d subsolutions for subgame %d of %d\n", //subsolns.Length(), i, subroots.Length()); List > newsolns; List > 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 bp(thissolns[soln]); DVector 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 > sol = (*p_solver)(subsupport); if (sol.Length() == 0) { solns = List >(); //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 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(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 List > 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 > vectors; List values; SolveSubgames(support, DVector(support.NumActions()), p_solver, efg->GetRoot(), vectors, values); List > solutions; for (int i = 1; i <= vectors.Length(); i++) { solutions.Append(MixedBehavProfile(p_support)); for (int j = 1; j <= vectors[i].Length(); j++) { solutions[i][j] = vectors[i][j]; } } return solutions; } // // Explicit instantiations follow // template List > SolveBySubgames(const BehavSupport &p_support, DoubleSolver p_solver); template List > SolveBySubgames(const BehavSupport &p_support, RationalSolver p_solver); } // end namespace Gambit