]> gitweb.michael.orlitzky.com - dunshire.git/blobdiff - dunshire/errors.py
Use "..." for floating point output in doctests.
[dunshire.git] / dunshire / errors.py
index b63344a0311e9c843cdeb189c903902de4a2179b..424f3e62352848c7f673375e401da18c25cdbb98 100644 (file)
@@ -49,6 +49,9 @@ class GameUnsolvableException(Exception):
     Parameters
     ----------
 
+    game : SymmetricLinearGame
+        A copy of the game whose solution failed.
+
     solution_dict : dict
         The solution dictionary returned from the failed cone program.
 
@@ -87,7 +90,8 @@ class GameUnsolvableException(Exception):
          e1 = [1.0000000]
               [0.1000000],
          e2 = [3.0000000]
-              [0.1000000].
+              [0.1000000],
+         Condition((L, K, e1, e2)) = 4.155...
        CVXOPT returned:
          dual infeasibility: None
          dual objective: 1.0
@@ -114,7 +118,7 @@ class GameUnsolvableException(Exception):
     """
     def __init__(self, game, solution_dict):
         """
-        Create a new GameUnsolvableException object.
+        Create a new :class:`GameUnsolvableException` object.
         """
         super().__init__()
         self._game = game
@@ -127,7 +131,7 @@ class GameUnsolvableException(Exception):
 
         The returned representation highlights the "status" field of the
         CVXOPT dictionary, since that should explain what went
-        wrong. The game details and full CVXOPT solution dictionary is
+        wrong. The game details and full CVXOPT solution dictionary are
         included after the status.
         """
         tpl = 'Solution failed with result "{:s}."\n' \
@@ -137,3 +141,76 @@ class GameUnsolvableException(Exception):
         # 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],
+         Condition((L, K, e1, e2)) = 4.155...
+       <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)