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):
"""
- 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.
+ 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
+ --------
+
+ >>> 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}'
- 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)