""" 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): """ 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. EXAMPLES: >>> 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(d)) Solution failed with result "primal infeasible." 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, solution_dict): """ Create a new GameUnsolvableException object. INPUT: - ``solution_dict`` -- the solution dictionary returned from the cone program. """ super().__init__() 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 full CVXOPT solution dictionary is included after the status. """ tpl = 'Solution failed with result "{:s}."\n' \ 'CVXOPT returned:\n {!s}' cvx_lines = _pretty_format_dict(self._solution_dict).splitlines() cvx_str = '\n '.join(cvx_lines) # Indent the whole dict by two spaces. return tpl.format(self._solution_dict['status'], cvx_str)