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