]> gitweb.michael.orlitzky.com - dunshire.git/blob - src/dunshire/errors.py
b63344a0311e9c843cdeb189c903902de4a2179b
[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 >>> from dunshire import *
59 >>> K = IceCream(2)
60 >>> L = [[1,2],[3,4]]
61 >>> e1 = [1, 0.1]
62 >>> e2 = [3, 0.1]
63 >>> G = SymmetricLinearGame(L,K,e1,e2)
64 >>> d = {'residual as dual infeasibility certificate': None,
65 ... 'y': matrix([1,1]),
66 ... 'dual slack': 8.779496368228267e-10,
67 ... 'z': matrix([1,1,0,0]),
68 ... 's': None,
69 ... 'primal infeasibility': None,
70 ... 'status': 'primal infeasible',
71 ... 'dual infeasibility': None,
72 ... 'relative gap': None,
73 ... 'iterations': 5,
74 ... 'primal slack': None,
75 ... 'x': None,
76 ... 'dual objective': 1.0,
77 ... 'primal objective': None,
78 ... 'gap': None,
79 ... 'residual as primal infeasibility certificate':
80 ... 3.986246886102996e-09}
81 >>> print(GameUnsolvableException(G,d))
82 Solution failed with result "primal infeasible."
83 The linear game (L, K, e1, e2) where
84 L = [ 1 2]
85 [ 3 4],
86 K = Lorentz "ice cream" cone in the real 2-space,
87 e1 = [1.0000000]
88 [0.1000000],
89 e2 = [3.0000000]
90 [0.1000000].
91 CVXOPT returned:
92 dual infeasibility: None
93 dual objective: 1.0
94 dual slack: 8.779496368228267e-10
95 gap: None
96 iterations: 5
97 primal infeasibility: None
98 primal objective: None
99 primal slack: None
100 relative gap: None
101 residual as dual infeasibility certificate: None
102 residual as primal infeasibility certificate: 3.986246886102996e-09
103 s: None
104 status: primal infeasible
105 x: None
106 y:
107 [ 1]
108 [ 1]
109 z:
110 [ 1]
111 [ 1]
112 [ 0]
113 [ 0]
114 """
115 def __init__(self, game, solution_dict):
116 """
117 Create a new GameUnsolvableException object.
118 """
119 super().__init__()
120 self._game = game
121 self._solution_dict = solution_dict
122
123
124 def __str__(self):
125 """
126 Return a string representation of this exception.
127
128 The returned representation highlights the "status" field of the
129 CVXOPT dictionary, since that should explain what went
130 wrong. The game details and full CVXOPT solution dictionary is
131 included after the status.
132 """
133 tpl = 'Solution failed with result "{:s}."\n' \
134 '{!s}\n' \
135 'CVXOPT returned:\n {!s}'
136 cvx_lines = _pretty_format_dict(self._solution_dict).splitlines()
137 # Indent the whole dict by two spaces.
138 cvx_str = '\n '.join(cvx_lines)
139 return tpl.format(self._solution_dict['status'], self._game, cvx_str)