# Indent the whole dict by two spaces.
cvx_str = '\n '.join(cvx_lines)
return tpl.format(self._solution_dict['status'], self._game, cvx_str)
+
+
+class PoorScalingException(Exception):
+ """
+ An exception raised when poor scaling leads to solution errors.
+
+ Under certain circumstances, a problem that should be solvable can
+ trigger errors in CVXOPT. The end result is the following
+ :class:`ValueError`::
+
+ Traceback (most recent call last):
+ ...
+ return math.sqrt(x[offset] - a) * math.sqrt(x[offset] + a)
+ ValueError: math domain error
+
+ This happens when one of the arguments to :func:`math.sqrt` is
+ negative, but the underlying cause is elusive. We're blaming it on
+ "poor scaling," whatever that means.
+
+ Similar issues have been discussed a few times on the CVXOPT mailing
+ list; for example,
+
+ 1. https://groups.google.com/forum/#!msg/cvxopt/TeQGdc2b4Xc/j5_mQME_rvUJ
+ 2. https://groups.google.com/forum/#!topic/cvxopt/HZrRfaoM0pk
+ 3. https://groups.google.com/forum/#!topic/cvxopt/riFSxB31zU4
+
+ Parameters
+ ----------
+
+ game : SymmetricLinearGame
+ A copy of the game whose solution failed.
+
+ Examples
+ --------
+
+ >>> from dunshire import *
+ >>> K = IceCream(2)
+ >>> L = [[1,2],[3,4]]
+ >>> e1 = [1, 0.1]
+ >>> e2 = [3, 0.1]
+ >>> G = SymmetricLinearGame(L,K,e1,e2)
+ >>> print(PoorScalingException(G))
+ Solution failed due to poor scaling.
+ The linear game (L, K, e1, e2) where
+ L = [ 1 2]
+ [ 3 4],
+ K = Lorentz "ice cream" cone in the real 2-space,
+ e1 = [1.0000000]
+ [0.1000000],
+ e2 = [3.0000000]
+ [0.1000000].
+ <BLANKLINE>
+ """
+ def __init__(self, game):
+ """
+ Create a new :class:`PoorScalingException` object.
+ """
+ super().__init__()
+ self._game = game
+
+
+ def __str__(self):
+ """
+ Return a string representation of this exception.
+
+ Pretty much all we can say is that there was poor scaling; that
+ is, that CVXOPT failed. The game details are included after
+ that.
+ """
+ tpl = 'Solution failed due to poor scaling.\n' \
+ '{!s}\n'
+ return tpl.format(self._game)
from cvxopt import matrix, printing, solvers
from .cones import CartesianProduct
-from .errors import GameUnsolvableException
+from .errors import GameUnsolvableException, PoorScalingException
from .matrices import append_col, append_row, identity
from . import options
If the game could not be solved (if an optimal solution to its
associated cone program was not found).
+ PoorScalingException
+ If the game could not be solved because CVXOPT crashed while
+ trying to take the square root of a negative number.
+
Examples
--------
# Actually solve the thing and obtain a dictionary describing
# what happened.
- soln_dict = solvers.conelp(c, G, h, C.cvxopt_dims(), A, b)
+ try:
+ soln_dict = solvers.conelp(c, G, h, C.cvxopt_dims(), A, b)
+ except ValueError as e:
+ if str(e) == 'math domain error':
+ # Oops, CVXOPT tried to take the square root of a
+ # negative number. Report some details about the game
+ # rather than just the underlying CVXOPT crash.
+ raise PoorScalingException(self)
+ else:
+ raise e
# The optimal strategies are named ``p`` and ``q`` in the
# background documentation, and we need to extract them from