""" Errors that can occur when solving a linear game. """ from cvxopt import matrix def _pretty_format_dict(dictionary): """ Return a pretty-formatted string representation of a dictionary containing CVXOPT matrices. The dictionary is also sorted so that it can be tested repeatably. Examples -------- >>> d = {'foo': 1.234, 'bar': matrix([1,2,3])} >>> print(_pretty_format_dict(d)) bar: [ 1] [ 2] [ 3] foo: 1.234 """ result = '' for (key, value) in sorted(dictionary.items()): if isinstance(value, matrix): # Display matrices on their own lines, indented. result += '{:s}:'.format(key) colvec = '\n{!s}'.format(value) result += '\n '.join(colvec.splitlines()) result += '\n' else: result += '{:s}: {!s}\n'.format(key, value) return result.rstrip('\n') # Kills trailing newlines on matrices. class GameUnsolvableException(Exception): """ An exception raised when a game cannot be solved. Every linear game has a solution. If we can't solve the conic program associated with a linear game, then something is wrong with either the model or the input, and this exception should be raised. Parameters ---------- solution_dict : dict The solution dictionary returned from the failed cone program. Examples -------- >>> from dunshire import * >>> K = IceCream(2) >>> L = [[1,2],[3,4]] >>> e1 = [1, 0.1] >>> e2 = [3, 0.1] >>> G = SymmetricLinearGame(L,K,e1,e2) >>> d = {'residual as dual infeasibility certificate': None, ... 'y': matrix([1,1]), ... 'dual slack': 8.779496368228267e-10, ... 'z': matrix([1,1,0,0]), ... 's': None, ... 'primal infeasibility': None, ... 'status': 'primal infeasible', ... 'dual infeasibility': None, ... 'relative gap': None, ... 'iterations': 5, ... 'primal slack': None, ... 'x': None, ... 'dual objective': 1.0, ... 'primal objective': None, ... 'gap': None, ... 'residual as primal infeasibility certificate': ... 3.986246886102996e-09} >>> print(GameUnsolvableException(G,d)) Solution failed with result "primal infeasible." The linear game (L, K, e1, e2) where L = [ 1 2] [ 3 4], K = Lorentz "ice cream" cone in the real 2-space, e1 = [1.0000000] [0.1000000], e2 = [3.0000000] [0.1000000]. CVXOPT returned: dual infeasibility: None dual objective: 1.0 dual slack: 8.779496368228267e-10 gap: None iterations: 5 primal infeasibility: None primal objective: None primal slack: None relative gap: None residual as dual infeasibility certificate: None residual as primal infeasibility certificate: 3.986246886102996e-09 s: None status: primal infeasible x: None y: [ 1] [ 1] z: [ 1] [ 1] [ 0] [ 0] """ def __init__(self, game, solution_dict): """ Create a new GameUnsolvableException object. """ super().__init__() self._game = game self._solution_dict = solution_dict def __str__(self): """ Return a string representation of this exception. The returned representation highlights the "status" field of the CVXOPT dictionary, since that should explain what went wrong. The game details and full CVXOPT solution dictionary is included after the status. """ tpl = 'Solution failed with result "{:s}."\n' \ '{!s}\n' \ 'CVXOPT returned:\n {!s}' cvx_lines = _pretty_format_dict(self._solution_dict).splitlines() # Indent the whole dict by two spaces. cvx_str = '\n '.join(cvx_lines) return tpl.format(self._solution_dict['status'], self._game, cvx_str)