]> gitweb.michael.orlitzky.com - dunshire.git/blob - src/dunshire/errors.py
Fiddle with the pylint settings.
[dunshire.git] / src / dunshire / errors.py
1 """
2 Errors that can occur when solving a linear game.
3 """
4
5 from cvxopt import matrix
6
7
8 def _pretty_format_dict(dictionary):
9 """
10 Return a pretty-formatted string representation of a dictionary
11 containing CVXOPT matrices.
12
13 The dictionary is also sorted so that it can be tested repeatably.
14
15 Examples
16 --------
17
18 >>> d = {'foo': 1.234, 'bar': matrix([1,2,3])}
19 >>> print(_pretty_format_dict(d))
20 bar:
21 [ 1]
22 [ 2]
23 [ 3]
24 foo: 1.234
25
26 """
27 result = ''
28 for (key, value) in sorted(dictionary.items()):
29 if isinstance(value, matrix):
30 # Display matrices on their own lines, indented.
31 result += '{:s}:'.format(key)
32 colvec = '\n{!s}'.format(value)
33 result += '\n '.join(colvec.splitlines())
34 result += '\n'
35 else:
36 result += '{:s}: {!s}\n'.format(key, value)
37
38 return result.rstrip('\n') # Kills trailing newlines on matrices.
39
40
41 class GameUnsolvableException(Exception):
42 """
43 An exception raised when a game cannot be solved.
44
45 Every linear game has a solution. If we can't solve the conic
46 program associated with a linear game, then something is wrong with
47 either the model or the input, and this exception should be raised.
48
49 Parameters
50 ----------
51
52 solution_dict : dict
53 The solution dictionary returned from the failed cone program.
54
55 Examples
56 --------
57
58 >>> d = {'residual as dual infeasibility certificate': None,
59 ... 'y': matrix([1,1]),
60 ... 'dual slack': 8.779496368228267e-10,
61 ... 'z': matrix([1,1,0,0]),
62 ... 's': None,
63 ... 'primal infeasibility': None,
64 ... 'status': 'primal infeasible',
65 ... 'dual infeasibility': None,
66 ... 'relative gap': None,
67 ... 'iterations': 5,
68 ... 'primal slack': None,
69 ... 'x': None,
70 ... 'dual objective': 1.0,
71 ... 'primal objective': None,
72 ... 'gap': None,
73 ... 'residual as primal infeasibility certificate':
74 ... 3.986246886102996e-09}
75 >>> print(GameUnsolvableException(d))
76 Solution failed with result "primal infeasible."
77 CVXOPT returned:
78 dual infeasibility: None
79 dual objective: 1.0
80 dual slack: 8.779496368228267e-10
81 gap: None
82 iterations: 5
83 primal infeasibility: None
84 primal objective: None
85 primal slack: None
86 relative gap: None
87 residual as dual infeasibility certificate: None
88 residual as primal infeasibility certificate: 3.986246886102996e-09
89 s: None
90 status: primal infeasible
91 x: None
92 y:
93 [ 1]
94 [ 1]
95 z:
96 [ 1]
97 [ 1]
98 [ 0]
99 [ 0]
100 """
101 def __init__(self, solution_dict):
102 """
103 Create a new GameUnsolvableException object.
104 """
105 super().__init__()
106 self._solution_dict = solution_dict
107
108
109 def __str__(self):
110 """
111 Return a string representation of this exception.
112
113 The returned representation highlights the "status" field of the
114 CVXOPT dictionary, since that should explain what went
115 wrong. The full CVXOPT solution dictionary is included after the
116 status.
117 """
118 tpl = 'Solution failed with result "{:s}."\n' \
119 'CVXOPT returned:\n {!s}'
120 cvx_lines = _pretty_format_dict(self._solution_dict).splitlines()
121
122 # Indent the whole dict by two spaces.
123 cvx_str = '\n '.join(cvx_lines)
124
125 return tpl.format(self._solution_dict['status'], cvx_str)