X-Git-Url: https://gitweb.michael.orlitzky.com/?a=blobdiff_plain;f=src%2Fdunshire%2Ferrors.py;h=322479f0d39320ede35977969d7e95577bdce79e;hb=e5b29b4c2e4dabb438d77f7f623c8b098353f5b0;hp=bb52cac66b194b67aedc5d1f8fc95514fd040cb8;hpb=56ea961887d507114174af5f92b8c3c77b0b7a50;p=dunshire.git diff --git a/src/dunshire/errors.py b/src/dunshire/errors.py index bb52cac..322479f 100644 --- a/src/dunshire/errors.py +++ b/src/dunshire/errors.py @@ -5,23 +5,36 @@ Errors that can occur when solving a linear game. from cvxopt import matrix -def _pretty_print_dict(dictionary): +def _pretty_format_dict(dictionary): """ - Return a pretty-printed string representation of a 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 dictionary.items(): + for (key, value) in sorted(dictionary.items()): if isinstance(value, matrix): # Display matrices on their own lines, indented. - result += ' {:s}:'.format(key) + result += '{:s}:'.format(key) colvec = '\n{!s}'.format(value) - result += '\n '.join(colvec.splitlines()) + result += '\n '.join(colvec.splitlines()) result += '\n' else: - result += ' {:s}: {!s}\n'.format(key, value) + result += '{:s}: {!s}\n'.format(key, value) - return result + return result.rstrip('\n') # Kills trailing newlines on matrices. class GameUnsolvableException(Exception): @@ -30,6 +43,50 @@ class GameUnsolvableException(Exception): 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): """ @@ -46,7 +103,16 @@ class GameUnsolvableException(Exception): 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}' - return tpl.format(self._solution_dict['status'], - _pretty_print_dict(self._solution_dict)) + '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)