]>
gitweb.michael.orlitzky.com - dunshire.git/blob - src/dunshire/symmetric_linear_game.py
2 Symmetric linear games and their solutions.
4 This module contains the main SymmetricLinearGame class that knows how
5 to solve a linear game.
8 from cvxopt
import matrix
, printing
, solvers
10 from cones
import CartesianProduct
11 from errors
import GameUnsolvableException
12 from matrices
import append_col
, append_row
, identity
14 printing
.options
['dformat'] = '%.7f'
15 solvers
.options
['show_progress'] = False
20 A representation of the solution of a linear game. It should contain
21 the value of the game, and both players' strategies.
23 def __init__(self
, game_value
, p1_optimal
, p2_optimal
):
25 Create a new Solution object from a game value and two optimal
26 strategies for the players.
28 self
._game
_value
= game_value
29 self
._player
1_optimal
= p1_optimal
30 self
._player
2_optimal
= p2_optimal
34 Return a string describing the solution of a linear game.
36 The three data that are described are,
38 * The value of the game.
39 * The optimal strategy of player one.
40 * The optimal strategy of player two.
44 >>> print(Solution(10, matrix([1,2]), matrix([3,4])))
45 Game value: 10.0000000
54 tpl
= 'Game value: {:.7f}\n' \
55 'Player 1 optimal:{:s}\n' \
56 'Player 2 optimal:{:s}'
58 p1_str
= '\n{!s}'.format(self
.player1_optimal())
59 p1_str
= '\n '.join(p1_str
.splitlines())
60 p2_str
= '\n{!s}'.format(self
.player2_optimal())
61 p2_str
= '\n '.join(p2_str
.splitlines())
63 return tpl
.format(self
.game_value(), p1_str
, p2_str
)
68 Return the game value for this solution.
70 return self
._game
_value
73 def player1_optimal(self
):
75 Return player one's optimal strategy in this solution.
77 return self
._player
1_optimal
80 def player2_optimal(self
):
82 Return player two's optimal strategy in this solution.
84 return self
._player
2_optimal
87 class SymmetricLinearGame
:
89 A representation of a symmetric linear game.
91 The data for a linear game are,
93 * A "payoff" operator ``L``.
95 * A point ``e`` in the interior of ``K``.
96 * A point ``f`` in the interior of the dual of ``K``.
98 In a symmetric game, the cone ``K`` is be self-dual. We therefore
99 name the two interior points ``e1`` and ``e2`` to indicate that
100 they come from the same cone but are "chosen" by players one and
103 The ambient space is assumed to be the span of ``K``.
105 def __init__(self
, L
, K
, e1
, e2
):
109 - ``L`` -- an square matrix represented as a list of lists
110 of real numbers. ``L`` itself is interpreted as a list of
111 ROWS, which agrees with (for example) SageMath and NumPy,
112 but not with CVXOPT (whose matrix constructor accepts a
115 - ``K`` -- a SymmetricCone instance.
117 - ``e1`` -- the interior point of ``K`` belonging to player one;
118 it can be of any enumerable type having the correct length.
120 - ``e2`` -- the interior point of ``K`` belonging to player two;
121 it can be of any enumerable type having the correct length.
125 Lists can (and probably should) be used for every argument:
127 >>> from cones import NonnegativeOrthant
128 >>> K = NonnegativeOrthant(2)
129 >>> L = [[1,0],[0,1]]
132 >>> G = SymmetricLinearGame(L, K, e1, e2)
134 The linear game (L, K, e1, e2) where
137 K = Nonnegative orthant in the real 2-space,
143 The points ``e1`` and ``e2`` can also be passed as some other
144 enumerable type (of the correct length) without much harm, since
145 there is no row/column ambiguity:
149 >>> from cones import NonnegativeOrthant
150 >>> K = NonnegativeOrthant(2)
151 >>> L = [[1,0],[0,1]]
152 >>> e1 = cvxopt.matrix([1,1])
153 >>> e2 = numpy.matrix([1,1])
154 >>> G = SymmetricLinearGame(L, K, e1, e2)
156 The linear game (L, K, e1, e2) where
159 K = Nonnegative orthant in the real 2-space,
165 However, ``L`` will always be intepreted as a list of rows, even
166 if it is passed as a ``cvxopt.base.matrix`` which is otherwise
170 >>> from cones import NonnegativeOrthant
171 >>> K = NonnegativeOrthant(2)
172 >>> L = [[1,2],[3,4]]
175 >>> G = SymmetricLinearGame(L, K, e1, e2)
177 The linear game (L, K, e1, e2) where
180 K = Nonnegative orthant in the real 2-space,
185 >>> L = cvxopt.matrix(L)
190 >>> G = SymmetricLinearGame(L, K, e1, e2)
192 The linear game (L, K, e1, e2) where
195 K = Nonnegative orthant in the real 2-space,
202 self
._e
1 = matrix(e1
, (K
.dimension(), 1))
203 self
._e
2 = matrix(e2
, (K
.dimension(), 1))
205 # Our input ``L`` is indexed by rows but CVXOPT matrices are
206 # indexed by columns, so we need to transpose the input before
207 # feeding it to CVXOPT.
208 self
._L = matrix(L
, (K
.dimension(), K
.dimension())).trans()
210 if not K
.contains_strict(self
._e
1):
211 raise ValueError('the point e1 must lie in the interior of K')
213 if not K
.contains_strict(self
._e
2):
214 raise ValueError('the point e2 must lie in the interior of K')
218 Return a string representatoin of this game.
222 >>> from cones import NonnegativeOrthant
223 >>> K = NonnegativeOrthant(3)
224 >>> L = [[1,-5,-15],[-1,2,-3],[-12,-15,1]]
227 >>> SLG = SymmetricLinearGame(L, K, e1, e2)
229 The linear game (L, K, e1, e2) where
233 K = Nonnegative orthant in the real 3-space,
242 tpl
= 'The linear game (L, K, e1, e2) where\n' \
247 indented_L
= '\n '.join(str(self
._L).splitlines())
248 indented_e1
= '\n '.join(str(self
._e
1).splitlines())
249 indented_e2
= '\n '.join(str(self
._e
2).splitlines())
250 return tpl
.format(indented_L
, str(self
._K
), indented_e1
, indented_e2
)
255 Solve this linear game and return a Solution object.
259 If the cone program associated with this game could be
260 successfully solved, then a Solution object containing the
261 game's value and optimal strategies is returned. If the game
262 could *not* be solved -- which should never happen -- then a
263 GameUnsolvableException is raised. It can be printed to get the
264 raw output from CVXOPT.
268 This example is computed in Gowda and Ravindran in the section
269 "The value of a Z-transformation":
271 >>> from cones import NonnegativeOrthant
272 >>> K = NonnegativeOrthant(3)
273 >>> L = [[1,-5,-15],[-1,2,-3],[-12,-15,1]]
276 >>> SLG = SymmetricLinearGame(L, K, e1, e2)
277 >>> print(SLG.solution())
278 Game value: -6.1724138
288 The value of the following game can be computed using the fact
289 that the identity is invertible:
291 >>> from cones import NonnegativeOrthant
292 >>> K = NonnegativeOrthant(3)
293 >>> L = [[1,0,0],[0,1,0],[0,0,1]]
296 >>> SLG = SymmetricLinearGame(L, K, e1, e2)
297 >>> print(SLG.solution())
298 Game value: 0.0312500
309 # The cone "C" that appears in the statement of the CVXOPT
311 C
= CartesianProduct(self
._K
, self
._K
)
313 # The column vector "b" that appears on the right-hand side of
314 # Ax = b in the statement of the CVXOPT conelp program.
315 b
= matrix([1], tc
='d')
317 # A column of zeros that fits K.
318 zero
= matrix(0, (self
._K
.dimension(), 1), tc
='d')
320 # The column vector "h" that appears on the right-hand side of
321 # Gx + s = h in the statement of the CVXOPT conelp program.
322 h
= matrix([zero
, zero
])
324 # The column vector "c" that appears in the objective function
325 # value <c,x> in the statement of the CVXOPT conelp program.
326 c
= matrix([-1, zero
])
328 # The matrix "G" that appears on the left-hand side of Gx + s = h
329 # in the statement of the CVXOPT conelp program.
330 G
= append_row(append_col(zero
, -identity(self
._K
.dimension())),
331 append_col(self
._e
1, -self
._L))
333 # The matrix "A" that appears on the right-hand side of Ax = b
334 # in the statement of the CVXOPT conelp program.
335 A
= matrix([0, self
._e
2], (1, self
._K
.dimension() + 1), 'd')
337 # Actually solve the thing and obtain a dictionary describing
339 soln_dict
= solvers
.conelp(c
, G
, h
, C
.cvxopt_dims(), A
, b
)
341 # The "status" field contains "optimal" if everything went
342 # according to plan. Other possible values are "primal
343 # infeasible", "dual infeasible", "unknown", all of which
344 # mean we didn't get a solution. That should never happen,
345 # because by construction our game has a solution, and thus
346 # the cone program should too.
347 if soln_dict
['status'] != 'optimal':
348 raise GameUnsolvableException(soln_dict
)
350 p1_value
= soln_dict
['x'][0]
351 p1_optimal
= soln_dict
['x'][1:]
352 p2_optimal
= soln_dict
['z'][self
._K
.dimension():]
354 return Solution(p1_value
, p1_optimal
, p2_optimal
)
358 Return the dual game to this game.
362 >>> from cones import NonnegativeOrthant
363 >>> K = NonnegativeOrthant(3)
364 >>> L = [[1,-5,-15],[-1,2,-3],[-12,-15,1]]
367 >>> SLG = SymmetricLinearGame(L, K, e1, e2)
368 >>> print(SLG.dual())
369 The linear game (L, K, e1, e2) where
373 K = Nonnegative orthant in the real 3-space,
382 return SymmetricLinearGame(self
._L, # It will be transposed in __init__().
383 self
._K
, # Since "K" is symmetric.