From a448c7849ff1af10a8e0ecd6f9d143b5f58e49d8 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Thu, 6 Oct 2016 09:11:49 -0400 Subject: [PATCH] Play with the error/solution formatting some more. --- errors.py | 63 ++++++++++++++++++---------------------- symmetric_linear_game.py | 43 ++++++++++++++------------- 2 files changed, 50 insertions(+), 56 deletions(-) diff --git a/errors.py b/errors.py index 028f5e7..f46c4a4 100644 --- a/errors.py +++ b/errors.py @@ -4,53 +4,48 @@ Errors that can occur when solving a linear game. from cvxopt import matrix -class GameException(Exception): - """ - The base class for all exceptions that can occur during the solution - of a linear game. - """ - def pretty_print_dict(self, solution_dict): - """ - Return a pretty-printed string representation of a CVXOPT - solution dictionary. - """ - result = '' - for (k,v) in solution_dict.items(): - if isinstance(v, matrix): - # Try to display vectors as rows on one line. - result += ' {:s}: {!s}'.format(k,v.trans()) - else: - result += ' {:s}: {!s}\n'.format(k,v) - - return result - -class GameUnsolvableException(GameException): +class GameUnsolvableException(Exception): """ Every linear game has a solution (this follows from a general min-max theorem). If we can't solve the conic program associated with a linear game, then something is wrong with either the model of the input. """ - def __init__(self, status, solution, solution_dict): + def __str__(self): + tpl = 'Solution failed with result "{:s}."\n' \ + 'CVXOPT returned:\n{!s}' + return tpl.format(self._solution_dict['status'], + self._pretty_print_dict(self._solution_dict)) + + + def __init__(self, solution_dict): """ Create a new GameUnsolvableException object. INPUT: - - ``status`` -- the failure status code returned by CVXOPT. - - - ``solution`` -- a Solution object. - - ``solution_dict`` -- the solution dictionary returned from the cone program. """ - tpl = 'Solution failed with error "{:s}".\n' \ - '{!s}\n' \ - 'CVXOPT returned:\n{!s}' - # TODO: dont convert the solution to a string, we need - # to output the two values as well. - self.message = tpl.format(status, - solution, - self.pretty_print_dict(solution_dict)) + self._solution_dict = solution_dict + + + def _pretty_print_dict(self, solution_dict): + """ + Return a pretty-printed string representation of a CVXOPT + solution dictionary. + """ + result = '' + for (k,v) in solution_dict.items(): + if isinstance(v, matrix): + # Display matrices on their own lines, indented. + result += ' {:s}:'.format(k) + colvec = '\n{!s}'.format(v) + result += '\n '.join(colvec.splitlines()) + result += '\n' + else: + result += ' {:s}: {!s}\n'.format(k,v) + + return result diff --git a/symmetric_linear_game.py b/symmetric_linear_game.py index 8c37c66..694e09f 100644 --- a/symmetric_linear_game.py +++ b/symmetric_linear_game.py @@ -7,14 +7,14 @@ from matrices import append_col, append_row, identity printing.options['dformat'] = '%.7f' solvers.options['show_progress'] = False + class Solution: """ A representation of the solution of a linear game. It should contain the value of the game, and both players' strategies. """ - def __init__(self, p1_value, p2_value, p1_optimal, p2_optimal): - self._player1_value = p1_value - self._player2_value = p2_value + def __init__(self, game_value, p1_optimal, p2_optimal): + self._game_value = game_value self._player1_optimal = p1_optimal self._player2_optimal = p2_optimal @@ -29,27 +29,27 @@ class Solution: * The optimal strategy of player two. """ - # The string representations of the player strategy matrices - # already contain trailing newlines. + tpl = 'Game value: {:.7f}\n' \ - 'Player 1 optimal: {!s}' \ - 'Player 2 optimal: {!s}' - return tpl.format(self.game_value(), - self.player1_optimal().trans(), - self.player2_optimal().trans()) + 'Player 1 optimal:{:s}\n' \ + 'Player 2 optimal:{:s}\n' + + p1 = '\n{!s}'.format(self.player1_optimal()) + p1 = '\n '.join(p1.splitlines()) + p2 = '\n{!s}'.format(self.player2_optimal()) + p2 = '\n '.join(p2.splitlines()) + + return tpl.format(self.game_value(), p1, p2) - def game_value(self): - return ((self.player1_value() + self.player2_value()) / 2.0) - def player1_value(self): - return self._player1_value + def game_value(self): + return self._game_value - def player2_value(self): - return self._player2_value def player1_optimal(self): return self._player1_optimal + def player2_optimal(self): return self._player2_optimal @@ -113,13 +113,12 @@ class SymmetricLinearGame: A = matrix([0, self._e1], (1, K.dimension() + 1), 'd') soln_dict = solvers.conelp(c, G, h, C.cvxopt_dims(), A, b) + + if soln_dict['status'] != 'optimal': + raise GameUnsolvableException(soln_dict) + p1_value = soln_dict['x'][0] - p2_value = soln_dict['y'][0] p1_optimal = soln_dict['x'][1:] p2_optimal = soln_dict['z'][self._K.dimension():] - soln = Solution(p1_value, p2_value, p1_optimal, p2_optimal) - - #if soln_dict['status'] != 'optimal': - raise GameUnsolvableException(soln_dict['status'], soln, soln_dict) - return soln + return Solution(p1_value, p1_optimal, p2_optimal) -- 2.44.2