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