Permalink
Browse files

Implement Solution base class for all solution profiles.

* Solution base class provides common features for all strategy
  profiles satisfying some solution concept of a game, most notably
  that the profile raises an exception when attempting to set a
  probability (solutions are intended to be non-mutable).
* Solution classes created for Nash equilibria, logit QRE,
  and cognitive hierarchy solutions.
* Interaction with external Nash solvers simplified to support
  creation of Nash equilibrium solution classes.
  • Loading branch information...
tturocy committed Aug 3, 2011
1 parent 502b1e8 commit 19870e52a1ec1125bec6cae5c931fb318152fc2c
@@ -4,6 +4,7 @@
import math
import scipy.stats
+from gambit.profiles import Solution
def logit_br(game, profile, lam):
br = game.mixed_profile()
@@ -13,21 +14,21 @@ def logit_br(game, profile, lam):
br[i] = payoffs[i] / s
return br
-
-class CognitiveHierarchyProfile(object):
+class CognitiveHierarchyProfile(Solution):
"""
Container class representing a CH solution.
"""
def __init__(self, tau, lam, profile):
- self.tau = tau
- self.lam = lam
- self.profile = profile
- def __len__(self): return len(self.profile)
- def __getitem__(self, i): return self.profile[i]
- def __getattr__(self, attr): return getattr(self.profile, attr)
+ Solution.__init__(self, profile)
+ self._tau = tau
+ self._lam = lam
def __repr__(self):
return "<CognitiveHierarchyProfile for tau=%f, lam=%f: %s>" % \
- (self.tau, self.lam, self.profile)
+ (self._tau, self._lam, self._profile)
+ @property
+ def tau(self): return self._tau
+ @property
+ def lam(self): return self._lam
def compute_coghier(game, tau, lam):
"""
@@ -141,6 +141,7 @@ cdef extern from "libgambit/game.h":
cdef extern from "libgambit/mixed.h":
ctypedef struct c_MixedStrategyProfileDouble "MixedStrategyProfile<double>":
+ c_Game GetGame()
int MixedProfileLength()
double getitem_int "operator[]"(int) except +IndexError
double getitem_Strategy "operator[]"(c_GameStrategy)
@@ -43,6 +43,13 @@ cdef class MixedStrategyProfileDouble:
else:
return list(self) >= list(other)
+ property game:
+ def __get__(self):
+ cdef Game g
+ g = Game()
+ g.game = self.profile.GetGame()
+ return g
+
def payoff(self, Player player):
return self.profile.GetPayoff(player.player)
View
@@ -3,7 +3,16 @@
"""
import subprocess
+from gambit.profiles import Solution
+class NashSolution(Solution):
+ def __init__(self, profile):
+ Solution.__init__(self, profile)
+ def __repr__(self):
+ return "<NashProfile for '%s': %s>" % (self._profile.game.title,
+ self._profile)
+
+
class ExternalSolver(object):
"""
Base class for managing calls to external programs.
@@ -28,39 +37,36 @@ def launch(self, prog, game):
child_stdin.close()
return child_stdout
-class ExternalEnumPureSolver(ExternalSolver):
- """
- Algorithm class to manage calls to external gambit-enumpure solver
- for computing pure-strategy equilibria.
- """
- def solve(self, game, rational=False):
+ def _parse_output(self, stream, game, rational):
profiles = [ ]
- command_line = "gambit-enumpure"
- for line in self.launch(command_line, game):
+ for line in stream:
entries = line.strip().split(",")
if entries[0] != "NE": continue
profile = game.mixed_profile()
for (i, p) in enumerate(entries[1:]):
profile[i] = float(p)
- profiles.append(profile)
+ profiles.append(NashSolution(profile))
return profiles
+class ExternalEnumPureSolver(ExternalSolver):
+ """
+ Algorithm class to manage calls to external gambit-enumpure solver
+ for computing pure-strategy equilibria.
+ """
+ def solve(self, game, rational=False):
+ command_line = "gambit-enumpure"
+ return self._parse_output(self.launch(command_line, game),
+ game, rational)
+
class ExternalLPSolver(ExternalSolver):
"""
Algorithm class to manage calls to external gambit-lp solver
for computing equilibria in two-player games using linear programming.
"""
def solve(self, game, rational=False):
- profiles = [ ]
command_line = "gambit-lp -d 10"
- for line in self.launch(command_line, game):
- entries = line.strip().split(",")
- if entries[0] != "NE": continue
- profile = game.mixed_profile()
- for (i, p) in enumerate(entries[1:]):
- profile[i] = float(p)
- profiles.append(profile)
- return profiles
+ return self._parse_output(self.launch(command_line, game),
+ game, rational)
class ExternalLCPSolver(ExternalSolver):
"""
@@ -69,119 +75,69 @@ class ExternalLCPSolver(ExternalSolver):
programming.
"""
def solve(self, game, rational=False):
- profiles = [ ]
command_line = "gambit-lcp -d 10"
- for line in self.launch(command_line, game):
- entries = line.strip().split(",")
- if entries[0] != "NE": continue
- profile = game.mixed_profile()
- for (i, p) in enumerate(entries[1:]):
- profile[i] = float(p)
- profiles.append(profile)
- return profiles
+ return self._parse_output(self.launch(command_line, game),
+ game, rational)
class ExternalEnumMixedSolver(ExternalSolver):
"""
Algorithm class to manage calls to external gambit-enummixed solver
for computing equilibria in two-player games using enumeration of extreme points.
"""
def solve(self, game, rational=False):
- profiles = [ ]
command_line = "gambit-enummixed -d 10"
- for line in self.launch(command_line, game):
- entries = line.strip().split(",")
- if entries[0] != "NE": continue
- profile = game.mixed_profile()
- for (i, p) in enumerate(entries[1:]):
- profile[i] = float(p)
- profiles.append(profile)
- return profiles
+ return self._parse_output(self.launch(command_line, game),
+ game, rational)
class ExternalSimpdivSolver(ExternalSolver):
"""
Algorithm class to manage calls to external gambit-simpdiv solver
for computing equilibria in N-player games using simpicial subdivision.
"""
def solve(self, game):
- profiles = [ ]
command_line = "gambit-simpdiv -d 10"
- for line in self.launch(command_line, game):
- entries = line.strip().split(",")
- if entries[0] != "NE": continue
- profile = game.mixed_profile()
- for (i, p) in enumerate(entries[1:]):
- profile[i] = float(p)
- profiles.append(profile)
- return profiles
-
+ return self._parse_output(self.launch(command_line, game),
+ game, rational)
+
class ExternalGlobalNewtonSolver(ExternalSolver):
"""
Algorithm class to manage calls to external gambit-gnm solver
for computing equilibria in N-player games using the global Newton method.
"""
def solve(self, game):
- profiles = [ ]
command_line = "gambit-gnm -d 10"
- for line in self.launch(command_line, game):
- entries = line.strip().split(",")
- if entries[0] != "NE": continue
- profile = game.mixed_profile()
- for (i, p) in enumerate(entries[1:]):
- profile[i] = float(p)
- profiles.append(profile)
- return profiles
+ return self._parse_output(self.launch(command_line, game),
+ game, rational)
class ExternalEnumPolySolver(ExternalSolver):
"""
Algorithm class to manage calls to external gambit-enumpoly solver
for computing equilibria in N-player games systems of polynomial equations.
"""
def solve(self, game):
- profiles = [ ]
command_line = "gambit-enumpoly -d 10"
- for line in self.launch(command_line, game):
- entries = line.strip().split(",")
- if entries[0] != "NE": continue
- profile = game.mixed_profile()
- for (i, p) in enumerate(entries[1:]):
- profile[i] = float(p)
- profiles.append(profile)
- return profiles
+ return self._parse_output(self.launch(command_line, game),
+ game, rational)
class ExternalLyapunovSolver(ExternalSolver):
"""
Algorithm class to manage calls to external gambit-liap solver
for computing equilibria in N-player games using Lyapunov function minimization.
"""
def solve(self, game):
- profiles = [ ]
command_line = "gambit-liap -d 10"
- for line in self.launch(command_line, game):
- entries = line.strip().split(",")
- if entries[0] != "NE": continue
- profile = game.mixed_profile()
- for (i, p) in enumerate(entries[1:]):
- profile[i] = float(p)
- profiles.append(profile)
- return profiles
+ return self._parse_output(self.launch(command_line, game),
+ game, rational)
class ExternalIteratedPolymatrixSolver(ExternalSolver):
"""
Algorithm class to manage calls to external gambit-ipa solver
for computing equilibria in N-player games using iterated polymatrix approximation.
"""
def solve(self, game):
- profiles = [ ]
command_line = "gambit-ipa -d 10"
- for line in self.launch(command_line, game):
- entries = line.strip().split(",")
- if entries[0] != "NE": continue
- profile = game.mixed_profile()
- for (i, p) in enumerate(entries[1:]):
- profile[i] = float(p)
- profiles.append(profile)
- return profiles
-
+ return self._parse_output(self.launch(command_line, game),
+ game, rational)
class ExternalLogitSolver(ExternalSolver):
"""
@@ -191,12 +147,5 @@ class ExternalLogitSolver(ExternalSolver):
def solve(self, game):
profiles = [ ]
command_line = "gambit-logit -d 20 -e"
- for line in self.launch(command_line, game):
- entries = line.strip().split(",")
- if entries[0] != "NE": continue
- profile = game.mixed_profile()
- for (i, p) in enumerate(entries[1:]):
- profile[i] = float(p)
- profiles.append(profile)
- return profiles
-
+ return self._parse_output(self.launch(command_line, game),
+ game, rational)
@@ -0,0 +1,15 @@
+"""
+Base classes for strategy profiles.
+"""
+
+class Solution(object):
+ """
+ Generic object representing a strategy profile which is
+ (part of) a solution of a game.
+ """
+ def __init__(self, profile): self._profile = profile
+ def __len__(self): return len(self._profile)
+ def __getitem__(self, i): return self._profile[i]
+ def __setitem__(self, i, v):
+ raise TypeError, "solution profile object does not support probability assignment"
+ def __getattr__(self, attr): return getattr(self._profile, attr)
View
@@ -6,6 +6,8 @@
import numpy
import pctrace
+from gambit.profiles import Solution
+
def sym_compute_lhs(game, point):
"""
Compute the LHS for the set of equations for a symmetric logit QRE
@@ -72,18 +74,19 @@ def printer(game, point):
-class LogitQRE(object):
+class LogitQRE(Solution):
"""
Container class representing a logit QRE
"""
def __init__(self, lam, profile):
- self.lam = lam
- self.profile = profile
- def __len__(self): return len(self.profile)
- def __getitem__(self, i): return self.profile[i]
- def __getattr__(self, attr): return getattr(self.profile, attr)
+ Solution.__init__(self, profile)
+ self._lam = lam
def __repr__(self):
- return "<LogitQRE at lam=%f: %s>" % (self.lam, self.profile)
+ return "<LogitQRE at lam=%f: %s>" % (self._lam, self._profile)
+ @property
+ def lam(self): return self._lam
+ @property
+ def mu(self): return 1.0 / self._lam
class StrategicQREPathTracer(object):
"""

0 comments on commit 19870e5

Please sign in to comment.