// // $Source: /cvsroot/gambit/gambit/sources/tools/lcp/efglcp.cc,v $ // $Date: 2006/11/13 06:06:44 $ // $Revision: 1.23 $ // // DESCRIPTION: // Implementation of algorithm to solve extensive forms using linear // complementarity program from sequence form // // 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 #include #include "libgambit/libgambit.h" using namespace Gambit; #include "lhtab.h" #include "lemketab.h" extern int g_numDecimals; void PrintProfile(std::ostream &p_stream, const std::string &p_label, const MixedBehavProfile &p_profile) { p_stream << p_label; for (int i = 1; i <= p_profile.Length(); i++) { p_stream << "," << p_profile[i]; } p_stream << std::endl; } void PrintProfile(std::ostream &p_stream, const std::string &p_label, const MixedBehavProfile &p_profile) { p_stream << p_label; for (int i = 1; i <= p_profile.Length(); i++) { p_stream.setf(std::ios::fixed); p_stream << "," << std::setprecision(g_numDecimals) << p_profile[i]; } p_stream << std::endl; } template class SolveEfgLcp { private: int m_stopAfter, m_maxDepth; int ns1,ns2,ni1,ni2; T maxpay,eps; List > m_list; List isets1, isets2; void FillTableau(const BehavSupport &, Matrix &, const GameNode &, T, int, int, int, int); int AddBFS(const LTableau &tab); int AllLemke(const BehavSupport &, int dup, LTableau &B, int depth, Matrix &, bool p_print, List > &); void GetProfile(const BehavSupport &, const LTableau &tab, MixedBehavProfile &, const Vector &, const GameNode &n, int,int); public: SolveEfgLcp(void) : m_stopAfter(0), m_maxDepth(0) { } int StopAfter(void) const { return m_stopAfter; } void SetStopAfter(int p_stopAfter) { m_stopAfter = p_stopAfter; } int MaxDepth(void) const { return m_maxDepth; } void SetMaxDepth(int p_maxDepth) { m_maxDepth = p_maxDepth; } List > Solve(const BehavSupport &, bool p_print = true); }; //--------------------------------------------------------------------------- // SolveEfgLcp: member functions //--------------------------------------------------------------------------- // // Sets the action probabilities at unreached information sets // which are left undefined by the sequence form method to // the centroid. This helps IsNash and LiapValue work correctly. // template void UndefinedToCentroid(MixedBehavProfile &p_profile) { Game efg = p_profile.GetGame(); for (int pl = 1; pl <= efg->NumPlayers(); pl++) { GamePlayer player = efg->GetPlayer(pl); for (int iset = 1; iset <= player->NumInfosets(); iset++) { GameInfoset infoset = player->GetInfoset(iset); if (p_profile.GetInfosetProb(infoset) > (T) 0) { continue; } T total = (T) 0; for (int act = 1; act <= infoset->NumActions(); act++) { total += p_profile.GetActionProb(infoset->GetAction(act)); } if (total == (T) 0) { for (int act = 1; act <= infoset->NumActions(); act++) { p_profile(pl, iset, act) = (T) 1.0 / (T) infoset->NumActions(); } } } } } // // Lemke implements the Lemke's algorithm (as refined by Eaves // for degenerate problems) for Linear Complementarity // problems, starting from the primary ray. // template List > SolveEfgLcp::Solve(const BehavSupport &p_support, bool p_print /*= true*/) { BFS cbfs((T) 0); int i, j; isets1 = p_support.ReachableInfosets(p_support.GetGame()->GetPlayer(1)); isets2 = p_support.ReachableInfosets(p_support.GetGame()->GetPlayer(2)); m_list = List >(); int ntot; ns1 = p_support.NumSequences(1); ns2 = p_support.NumSequences(2); ni1 = p_support.GetGame()->GetPlayer(1)->NumInfosets()+1; ni2 = p_support.GetGame()->GetPlayer(2)->NumInfosets()+1; ntot = ns1+ns2+ni1+ni2; Matrix A(1,ntot,0,ntot); Vector b(1,ntot); maxpay = p_support.GetGame()->GetMaxPayoff() + Rational(1); T prob = (T)1; for (i = A.MinRow(); i <= A.MaxRow(); i++) { b[i] = (T) 0; for (j = A.MinCol(); j <= A.MaxCol(); j++) { A(i,j) = (T) 0; } } FillTableau(p_support, A, p_support.GetGame()->GetRoot(), prob, 1, 1, 0, 0); for (i = A.MinRow(); i <= A.MaxRow(); i++) { A(i,0) = -(T) 1; } A(1,ns1+ns2+1) = (T)1; A(ns1+ns2+1,1) = -(T)1; A(ns1+1,ns1+ns2+ni1+1) = (T)1; A(ns1+ns2+ni1+1,ns1+1) = -(T)1; b[ns1+ns2+1] = -(T)1; b[ns1+ns2+ni1+1] = -(T)1; LTableau tab(A,b); eps = tab.Epsilon(); MixedBehavProfile profile(p_support); Vector sol(tab.MinRow(),tab.MaxRow()); List > solutions; try { if (m_stopAfter != 1) { AllLemke(p_support, ns1+ns2+1, tab, 0, A, p_print, solutions); } else { tab.Pivot(ns1+ns2+1,0); tab.SF_LCPPath(ns1+ns2+1); AddBFS(tab); tab.BasisVector(sol); GetProfile(p_support, tab, profile,sol,p_support.GetGame()->GetRoot(),1,1); UndefinedToCentroid(profile); PrintProfile(std::cout, "NE", profile); } } catch (...) { // catch exception; return solutions computed (if any) } return solutions; } template int SolveEfgLcp::AddBFS(const LTableau &tableau) { BFS cbfs((T) 0); Vector v(tableau.MinRow(), tableau.MaxRow()); tableau.BasisVector(v); for (int i = tableau.MinCol(); i <= tableau.MaxCol(); i++) if (tableau.Member(i)) { cbfs.Define(i, v[tableau.Find(i)]); } if (m_list.Contains(cbfs)) return 0; m_list.Append(cbfs); return 1; } // // All_Lemke finds all accessible Nash equilibria by recursively // calling itself. List maintains the list of basic variables // for the equilibria that have already been found. // From each new accessible equilibrium, it follows // all possible paths, adding any new equilibria to the List. // template int SolveEfgLcp::AllLemke(const BehavSupport &p_support, int j, LTableau &B, int depth, Matrix &A, bool p_print, List > &p_solutions) { if (m_maxDepth != 0 && depth > m_maxDepth) { return 1; } int i,newsol,missing; T small_num = (T)1/(T)1000; Vector sol(B.MinRow(),B.MaxRow()); MixedBehavProfile profile(p_support); newsol =0; for (i = B.MinRow(); i <= B.MaxRow() && newsol == 0; i++) { if (i != j) { LTableau BCopy(B); A(i,0) = -small_num; BCopy.Refactor(); if (depth == 0) { BCopy.Pivot(j, 0); missing = -j; } else { missing = BCopy.SF_PivotIn(0); } newsol = 0; if (BCopy.SF_LCPPath(-missing) == 1) { newsol = AddBFS(BCopy); BCopy.BasisVector(sol); GetProfile(p_support, BCopy, profile, sol, p_support.GetGame()->GetRoot(), 1, 1); UndefinedToCentroid(profile); if (newsol) { if (p_print) { PrintProfile(std::cout, "NE", profile); } p_solutions.Append(profile); } } else { // gout << ": Dead End"; } A(i,0) = (T) -1; if (newsol) { BCopy.Refactor(); AllLemke(p_support, i, BCopy, depth+1, A, p_print, p_solutions); } } } return 1; } template void SolveEfgLcp::FillTableau(const BehavSupport &p_support, Matrix &A, const GameNode &n, T prob, int s1, int s2, int i1, int i2) { int snew; GameOutcome outcome = n->GetOutcome(); if (outcome) { A(s1,ns1+s2) = Rational(A(s1,ns1+s2)) + Rational(prob) * (outcome->GetPayoff(1) - Rational(maxpay)); A(ns1+s2,s1) = Rational(A(ns1+s2,s1)) + Rational(prob) * (outcome->GetPayoff(2) - Rational(maxpay)); } if (n->GetInfoset()) { if (n->GetPlayer()->IsChance()) { GameInfoset infoset = n->GetInfoset(); for (int i = 1; i <= n->NumChildren(); i++) { FillTableau(p_support, A, n->GetChild(i), Rational(prob) * infoset->GetActionProb(i), s1,s2,i1,i2); } } int pl = n->GetPlayer()->GetNumber(); if (pl==1) { i1=isets1.Find(n->GetInfoset()); snew=1; for (int i = 1; i < i1; i++) { snew+=p_support.NumActions(isets1[i]->GetPlayer()->GetNumber(), isets1[i]->GetNumber()); } A(s1,ns1+ns2+i1+1) = -(T)1; A(ns1+ns2+i1+1,s1) = (T)1; for (int i = 1; i <= p_support.NumActions(n->GetInfoset()->GetPlayer()->GetNumber(), n->GetInfoset()->GetNumber()); i++) { A(snew+i,ns1+ns2+i1+1) = (T)1; A(ns1+ns2+i1+1,snew+i) = -(T)1; FillTableau(p_support, A, n->GetChild(p_support.GetAction(n->GetInfoset()->GetPlayer()->GetNumber(), n->GetInfoset()->GetNumber(), i)->GetNumber()),prob,snew+i,s2,i1,i2); } } if(pl==2) { i2=isets2.Find(n->GetInfoset()); snew=1; for (int i = 1; i < i2; i++) { snew+=p_support.NumActions(isets2[i]->GetPlayer()->GetNumber(), isets2[i]->GetNumber()); } A(ns1+s2,ns1+ns2+ni1+i2+1) = -(T)1; A(ns1+ns2+ni1+i2+1,ns1+s2) = (T)1; for (int i = 1; i <= p_support.NumActions(n->GetInfoset()->GetPlayer()->GetNumber(), n->GetInfoset()->GetNumber()); i++) { A(ns1+snew+i,ns1+ns2+ni1+i2+1) = (T)1; A(ns1+ns2+ni1+i2+1,ns1+snew+i) = -(T)1; FillTableau(p_support, A, n->GetChild(p_support.GetAction(n->GetInfoset()->GetPlayer()->GetNumber(), n->GetInfoset()->GetNumber(), i)->GetNumber()),prob,s1,snew+i,i1,i2); } } } } template void SolveEfgLcp::GetProfile(const BehavSupport &p_support, const LTableau &tab, MixedBehavProfile &v, const Vector &sol, const GameNode &n, int s1,int s2) { if (n->GetInfoset()) { int pl = n->GetPlayer()->GetNumber(); int iset = n->GetInfoset()->GetNumber(); if (n->GetPlayer()->IsChance()) { for (int i = 1; i <= n->NumChildren(); i++) { GetProfile(p_support, tab, v, sol, n->GetChild(i), s1, s2); } } else if (pl == 1) { int inf = isets1.Find(n->GetInfoset()); int snew = 1; for (int i = 1; i < inf; i++) { snew += p_support.NumActions(1, isets1[i]->GetNumber()); } for (int i = 1; i <= p_support.NumActions(pl, iset); i++) { v(pl,inf,i) = (T) 0; if (tab.Member(s1)) { int ind = tab.Find(s1); if (sol[ind] > eps) { if (tab.Member(snew+i)) { int ind2 = tab.Find(snew+i); if (sol[ind2] > eps) { v(pl,inf,i) = sol[ind2] / sol[ind]; } } } } GetProfile(p_support, tab, v, sol, n->GetChild(p_support.GetAction(pl, iset, i)->GetNumber()), snew+i, s2); } } else if (pl == 2) { int inf = isets2.Find(n->GetInfoset()); int snew = 1; for (int i = 1; i < inf; i++) { snew += p_support.NumActions(2, isets2[i]->GetNumber()); } for (int i = 1; i<= p_support.NumActions(pl, iset); i++) { v(pl,inf,i) = (T) 0; if (tab.Member(ns1+s2)) { int ind = tab.Find(ns1+s2); if (sol[ind] > eps) { if (tab.Member(ns1+snew+i)) { int ind2 = tab.Find(ns1+snew+i); if (sol[ind2] > eps) { v(pl,inf,i) = sol[ind2] / sol[ind]; } } } } GetProfile(p_support, tab, v, sol, n->GetChild(p_support.GetAction(pl, iset, i)->GetNumber()), s1, snew+i); } } } } template List > SolveExtensive(const BehavSupport &p_support) { SolveEfgLcp algorithm; return algorithm.Solve(p_support); } template List > SolveExtensive(const BehavSupport &); template List > SolveExtensive(const BehavSupport &); template List > SolveExtensiveSilent(const BehavSupport &p_support) { SolveEfgLcp algorithm; return algorithm.Solve(p_support, false); } template List > SolveExtensiveSilent(const BehavSupport &); template List > SolveExtensiveSilent(const BehavSupport &);