]> gitweb.michael.orlitzky.com - dunshire.git/commitdiff
Get rid of the contains_strict() methods and compare against ABS_TOL.
authorMichael Orlitzky <michael@orlitzky.com>
Tue, 11 Oct 2016 14:33:56 +0000 (10:33 -0400)
committerMichael Orlitzky <michael@orlitzky.com>
Tue, 11 Oct 2016 14:33:56 +0000 (10:33 -0400)
Since we're comparing floating point numbers in the containment tests,
we should be doing it against a tolerance. But then, it becomes
pointless to distinguish between strict and non-strict containment.
So, this commit leaves only the __contains__() methods and checks
against options.ABS_TOL.

src/dunshire/cones.py
src/dunshire/games.py

index 905dd8edf7092b83ef7c1275b2a09430f18c10e1..d7c9b62e02314e1b637a3c1caf00f1d68612b050 100644 (file)
@@ -5,6 +5,7 @@ Class definitions for all of the symmetric cones (and their superclass,
 
 from cvxopt import matrix
 from matrices import eigenvalues, norm
+import options
 
 class SymmetricCone:
     """
@@ -75,34 +76,6 @@ class SymmetricCone:
         raise NotImplementedError
 
 
-    def contains_strict(self, point):
-        """
-        Return whether or not ``point`` belongs to the interior
-        of this cone.
-
-        Parameters
-        ----------
-
-        point : matrix
-            The point to test for strict membership in this cone.
-
-        Raises
-        ------
-
-        NotImplementedError
-            Always, this method must be implemented in subclasses.
-
-        Examples
-        --------
-
-            >>> K = SymmetricCone(5)
-            >>> K.contains_strict(matrix([1,2]))
-            Traceback (most recent call last):
-            ...
-            NotImplementedError
-        """
-        raise NotImplementedError
-
 
     def dimension(self):
         """
@@ -156,6 +129,17 @@ class NonnegativeOrthant(SymmetricCone):
         """
         Return whether or not ``point`` belongs to this cone.
 
+        Since this test is expected to work on points whose components
+        are floating point numbers, it doesn't make any sense to
+        distinguish between strict and non-strict containment -- the
+        test uses a tolerance parameter.
+
+        This test will err on the side of caution, and return ``True``
+        only when the ``point`` lies inside this cone *past* the
+        tolerance guarantee. That means if you ask whether or not a
+        boundary point lies in this cone, you will get ``False`` as your
+        answer.
+
         Parameters
         ----------
 
@@ -182,87 +166,33 @@ class NonnegativeOrthant(SymmetricCone):
         Examples
         --------
 
+        All of these coordinates are positive enough:
+
             >>> K = NonnegativeOrthant(3)
             >>> matrix([1,2,3]) in K
             True
 
+        The one negative coordinate pushes this point outside of ``K``:
+
             >>> K = NonnegativeOrthant(3)
             >>> matrix([1,-0.1,3]) in K
             False
 
+        However, not even a boundary point is considered inside of ``K``:
             >>> K = NonnegativeOrthant(3)
-            >>> [1,2,3] in K
-            Traceback (most recent call last):
-            ...
-            TypeError: the given point is not a cvxopt.base.matrix
-
-            >>> K = NonnegativeOrthant(3)
-            >>> matrix([1,2]) in K
-            Traceback (most recent call last):
-            ...
-            TypeError: the given point has the wrong dimensions
-
-        """
-        if not isinstance(point, matrix):
-            raise TypeError('the given point is not a cvxopt.base.matrix')
-        if not point.size == (self.dimension(), 1):
-            raise TypeError('the given point has the wrong dimensions')
-
-        return all([x >= 0 for x in point])
-
-
-    def contains_strict(self, point):
-        """
-        Return whether or not ``point`` belongs to the interior of this
-        cone.
-
-        Parameters
-        ----------
-
-        point : matrix
-            A :class:`cvxopt.base.matrix` having dimensions ``(n,1)``
-            where ``n`` is the :meth:`dimension` of this cone.
-
-        Returns
-        -------
-
-        bool
-
-           ``True`` if ``point`` belongs to the interior of this cone,
-           ``False`` otherwise.
-
-        Raises
-        ------
-
-        TypeError
-            If ``point`` is not a :class:`cvxopt.base.matrix`.
-
-        TypeError
-            If ``point`` has the wrong dimensions.
-
-        Examples
-        --------
-
-            >>> K = NonnegativeOrthant(3)
-            >>> K.contains_strict(matrix([1,2,3]))
-            True
-
-            >>> K = NonnegativeOrthant(3)
-            >>> K.contains_strict(matrix([1,0,1]))
+            >>> matrix([1,0,3]) in K
             False
 
-            >>> K = NonnegativeOrthant(3)
-            >>> K.contains_strict(matrix([1,-0.1,3]))
-            False
+        Junk arguments don't work:
 
             >>> K = NonnegativeOrthant(3)
-            >>> K.contains_strict([1,2,3])
+            >>> [1,2,3] in K
             Traceback (most recent call last):
             ...
             TypeError: the given point is not a cvxopt.base.matrix
 
             >>> K = NonnegativeOrthant(3)
-            >>> K.contains_strict(matrix([1,2]))
+            >>> matrix([1,2]) in K
             Traceback (most recent call last):
             ...
             TypeError: the given point has the wrong dimensions
@@ -273,7 +203,7 @@ class NonnegativeOrthant(SymmetricCone):
         if not point.size == (self.dimension(), 1):
             raise TypeError('the given point has the wrong dimensions')
 
-        return all([x > 0 for x in point])
+        return all([x > options.ABS_TOL for x in point])
 
 
 
@@ -301,6 +231,17 @@ class IceCream(SymmetricCone):
         """
         Return whether or not ``point`` belongs to this cone.
 
+        Since this test is expected to work on points whose components
+        are floating point numbers, it doesn't make any sense to
+        distinguish between strict and non-strict containment -- the
+        test uses a tolerance parameter.
+
+        This test will err on the side of caution, and return ``True``
+        only when the ``point`` lies inside this cone *past* the
+        tolerance guarantee. That means if you ask whether or not a
+        boundary point lies in this cone, you will get ``False`` as your
+        answer.
+
         Parameters
         ----------
 
@@ -327,18 +268,26 @@ class IceCream(SymmetricCone):
         Examples
         --------
 
+        This point lies well within the ice cream cone:
+
             >>> K = IceCream(3)
             >>> matrix([1,0.5,0.5]) in K
             True
 
+        But this one lies on its boundary and runs foul of the tolerance:
+
             >>> K = IceCream(3)
             >>> matrix([1,0,1]) in K
-            True
+            False
+
+        This point lies entirely outside of the ice cream cone:
 
             >>> K = IceCream(3)
             >>> matrix([1,1,1]) in K
             False
 
+        Junk arguments don't work:
+
             >>> K = IceCream(3)
             >>> [1,2,3] in K
             Traceback (most recent call last):
@@ -361,82 +310,11 @@ class IceCream(SymmetricCone):
         if self.dimension() == 1:
             # In one dimension, the ice cream cone is the nonnegative
             # orthant.
-            return height >= 0
+            return height > options.ABS_TOL
         else:
             radius = point[1:]
-            return height >= norm(radius)
-
-
-    def contains_strict(self, point):
-        """
-        Return whether or not ``point`` belongs to the interior
-        of this cone.
+            return norm(radius) < (height - options.ABS_TOL)
 
-        Parameters
-        ----------
-
-        point : matrix
-            A :class:`cvxopt.base.matrix` having dimensions ``(n,1)``
-            where ``n`` is the :meth:`dimension` of this cone.
-
-        Returns
-        -------
-
-        bool
-
-           ``True`` if ``point`` belongs to the interior of this cone,
-           ``False`` otherwise.
-
-        Raises
-        ------
-
-        TypeError
-            If ``point`` is not a :class:`cvxopt.base.matrix`.
-
-        TypeError
-            If ``point`` has the wrong dimensions.
-
-        Examples
-        --------
-
-            >>> K = IceCream(3)
-            >>> K.contains_strict(matrix([1,0.5,0.5]))
-            True
-
-            >>> K = IceCream(3)
-            >>> K.contains_strict(matrix([1,0,1]))
-            False
-
-            >>> K = IceCream(3)
-            >>> K.contains_strict(matrix([1,1,1]))
-            False
-
-            >>> K = IceCream(3)
-            >>> K.contains_strict([1,2,3])
-            Traceback (most recent call last):
-            ...
-            TypeError: the given point is not a cvxopt.base.matrix
-
-            >>> K = IceCream(3)
-            >>> K.contains_strict(matrix([1,2]))
-            Traceback (most recent call last):
-            ...
-            TypeError: the given point has the wrong dimensions
-
-        """
-        if not isinstance(point, matrix):
-            raise TypeError('the given point is not a cvxopt.base.matrix')
-        if not point.size == (self.dimension(), 1):
-            raise TypeError('the given point has the wrong dimensions')
-
-        height = point[0]
-        if self.dimension() == 1:
-            # In one dimension, the ice cream cone is the nonnegative
-            # orthant.
-            return height > 0
-        else:
-            radius = point[1:]
-            return height > norm(radius)
 
 
 class SymmetricPSD(SymmetricCone):
@@ -475,6 +353,17 @@ class SymmetricPSD(SymmetricCone):
         """
         Return whether or not ``point`` belongs to this cone.
 
+        Since this test is expected to work on points whose components
+        are floating point numbers, it doesn't make any sense to
+        distinguish between strict and non-strict containment -- the
+        test uses a tolerance parameter.
+
+        This test will err on the side of caution, and return ``True``
+        only when the ``point`` lies inside this cone *past* the
+        tolerance guarantee. That means if you ask whether or not a
+        boundary point lies in this cone, you will get ``False`` as your
+        answer.
+
         Parameters
         ----------
 
@@ -501,12 +390,10 @@ class SymmetricPSD(SymmetricCone):
         Examples
         --------
 
-            >>> K = SymmetricPSD(2)
-            >>> matrix([[1,0],[0,1]]) in K
-            True
+        These all lie in the interior of the Symmetric PSD cone:
 
             >>> K = SymmetricPSD(2)
-            >>> matrix([[0,0],[0,0]]) in K
+            >>> matrix([[1,0],[0,1]]) in K
             True
 
             >>> K = SymmetricPSD(3)
@@ -522,107 +409,32 @@ class SymmetricPSD(SymmetricCone):
             >>> A in K
             True
 
-            >>> K = SymmetricPSD(5)
-            >>> A = matrix([[1,0,0,0,0],
-            ...            [0,1,0,0,0],
-            ...            [0,0,0,0,0],
-            ...            [0,0,0,1,0],
-            ...            [0,0,0,0,1]])
-            >>> A in K
-            True
+        But any matrix with a zero eigenvalue will lie on the boundary
+        of the Symmetric PSD cone and run foul of our tolerance:
 
             >>> K = SymmetricPSD(2)
-            >>> [[1,2],[2,3]] in K
-            Traceback (most recent call last):
-            ...
-            TypeError: the given point is not a cvxopt.base.matrix
-
-            >>> K = SymmetricPSD(3)
-            >>> matrix([[1,2],[3,4]]) in K
-            Traceback (most recent call last):
-            ...
-            TypeError: the given point has the wrong dimensions
-
-        """
-        if not isinstance(point, matrix):
-            raise TypeError('the given point is not a cvxopt.base.matrix')
-        if not point.size == (self.dimension(), self.dimension()):
-            raise TypeError('the given point has the wrong dimensions')
-        if not point.typecode == 'd':
-            point = matrix(point, (self.dimension(), self.dimension()), 'd')
-        return all([e >= 0 for e in eigenvalues(point)])
-
-
-    def contains_strict(self, point):
-        """
-        Return whether or not ``point`` belongs to the interior
-        of this cone.
-
-        Parameters
-        ----------
-
-        point : matrix
-            A :class:`cvxopt.base.matrix` having dimensions ``(n,n)``
-            where ``n`` is the :meth:`dimension` of this cone.
-
-        Returns
-        -------
-
-        bool
-
-           ``True`` if ``point`` belongs to the interior of this cone,
-           ``False`` otherwise.
-
-        Raises
-        ------
-
-        TypeError
-            If ``point`` is not a :class:`cvxopt.base.matrix`.
-
-        TypeError
-            If ``point`` has the wrong dimensions.
-
-        Examples
-        --------
-
-            >>> K = SymmetricPSD(2)
-            >>> K.contains_strict(matrix([[1,0],[0,1]]))
-            True
-
-            >>> K = SymmetricPSD(2)
-            >>> K.contains_strict(matrix([[0,0],[0,0]]))
+            >>> matrix([[0,0],[0,0]]) in K
             False
 
-            >>> K = SymmetricPSD(3)
-            >>> matrix([[2,-1,0],[-1,2,-1],[0,-1,2]]) in K
-            True
-
-            >>> K = SymmetricPSD(5)
-            >>> A = matrix([[5,4,3,2,1],
-            ...            [4,5,4,3,2],
-            ...            [3,4,5,4,3],
-            ...            [2,3,4,5,4],
-            ...            [1,2,3,4,5]])
-            >>> A in K
-            True
-
             >>> K = SymmetricPSD(5)
             >>> A = matrix([[1,0,0,0,0],
             ...            [0,1,0,0,0],
             ...            [0,0,0,0,0],
             ...            [0,0,0,1,0],
             ...            [0,0,0,0,1]])
-            >>> K.contains_strict(A)
+            >>> A in K
             False
 
+        Junk arguments don't work:
+
             >>> K = SymmetricPSD(2)
-            >>> K.contains_strict([[1,2],[2,3]])
+            >>> [[1,2],[2,3]] in K
             Traceback (most recent call last):
             ...
             TypeError: the given point is not a cvxopt.base.matrix
 
             >>> K = SymmetricPSD(3)
-            >>> K.contains_strict(matrix([[1,2],[3,4]]))
+            >>> matrix([[1,2],[3,4]]) in K
             Traceback (most recent call last):
             ...
             TypeError: the given point has the wrong dimensions
@@ -634,7 +446,8 @@ class SymmetricPSD(SymmetricCone):
             raise TypeError('the given point has the wrong dimensions')
         if not point.typecode == 'd':
             point = matrix(point, (self.dimension(), self.dimension()), 'd')
-        return all([e > 0 for e in eigenvalues(point)])
+        return all([e > options.ABS_TOL for e in eigenvalues(point)])
+
 
 
 class CartesianProduct(SymmetricCone):
@@ -704,13 +517,15 @@ class CartesianProduct(SymmetricCone):
         Examples
         --------
 
+        The result depends on how containment is defined for our factors:
+
             >>> K = CartesianProduct(NonnegativeOrthant(3), IceCream(3))
             >>> (matrix([1,2,3]), matrix([1,0.5,0.5])) in K
             True
 
             >>> K = CartesianProduct(NonnegativeOrthant(3), IceCream(3))
             >>> (matrix([0,0,0]), matrix([1,0,1])) in K
-            True
+            False
 
             >>> K = CartesianProduct(NonnegativeOrthant(3), IceCream(3))
             >>> (matrix([1,1,1]), matrix([1,1,1])) in K
@@ -720,6 +535,8 @@ class CartesianProduct(SymmetricCone):
             >>> (matrix([1,-1,1]), matrix([1,0,1])) in K
             False
 
+        Junk arguments don't work:
+
             >>> K = CartesianProduct(NonnegativeOrthant(3), IceCream(3))
             >>> [[1,2,3],[4,5,6]] in K
             Traceback (most recent call last):
@@ -736,77 +553,6 @@ class CartesianProduct(SymmetricCone):
         return all([p in f for (p,f) in zip(point, self.factors())])
 
 
-    def contains_strict(self, point):
-        """
-        Return whether or not ``point`` belongs to the interior
-        of this cone.
-
-        The ``point`` is expected to be a tuple of points which will be
-        tested for membership in this cone's factors. If each point in
-        the tuple belongs to the interior of its corresponding factor,
-        then the whole point belongs to the interior of this
-        cone. Otherwise, it doesn't.
-
-        Parameters
-        ----------
-
-        point : tuple of matrix
-            A tuple of :class:`cvxopt.base.matrix` corresponding to the
-            :meth:`factors` of this cartesian product.
-
-        Returns
-        -------
-
-        bool
-
-            ``True`` if ``point`` belongs to the interior of this cone,
-            ``False`` otherwise.
-
-        Raises
-        ------
-
-        TypeError
-            If ``point`` is not a tuple of :class:`cvxopt.base.matrix`.
-
-        TypeError
-            If any element of ``point`` has the wrong dimensions.
-
-        Examples
-        --------
-
-            >>> K = CartesianProduct(NonnegativeOrthant(3), IceCream(3))
-            >>> K.contains_strict((matrix([1,2,3]), matrix([1,0.5,0.5])))
-            True
-
-            >>> K = CartesianProduct(NonnegativeOrthant(3), IceCream(3))
-            >>> K.contains_strict((matrix([0,0,0]), matrix([1,0,1])))
-            False
-
-            >>> K = CartesianProduct(NonnegativeOrthant(3), IceCream(3))
-            >>> K.contains_strict((matrix([1,1,1]), matrix([1,1,1])))
-            False
-
-            >>> K = CartesianProduct(NonnegativeOrthant(3), IceCream(3))
-            >>> K.contains_strict((matrix([1,-1,1]), matrix([1,0,1])))
-            False
-
-            >>> K = CartesianProduct(NonnegativeOrthant(3), IceCream(3))
-            >>> K.contains_strict([[1,2,3],[4,5,6]])
-            Traceback (most recent call last):
-            ...
-            TypeError: the given point is not a cvxopt.base.matrix
-
-            >>> K = CartesianProduct(NonnegativeOrthant(3), IceCream(3))
-            >>> K.contains_strict((matrix([1,2]), matrix([3,4,5,6])))
-            Traceback (most recent call last):
-            ...
-            TypeError: the given point has the wrong dimensions
-
-        """
-        return all([f.contains_strict(p)
-                    for (p,f) in zip(point, self.factors())])
-
-
 
     def factors(self):
         """
index f3d8100665d88c3690d75605ed177988fef6f54e..4b5383dd2f12c1fe2d6de250760e5009639bfd5d 100644 (file)
@@ -319,10 +319,10 @@ class SymmetricLinearGame:
         # feeding it to CVXOPT.
         self._L = matrix(L, (K.dimension(), K.dimension())).trans()
 
-        if not K.contains_strict(self._e1):
+        if not self._e1 in K:
             raise ValueError('the point e1 must lie in the interior of K')
 
-        if not K.contains_strict(self._e2):
+        if not self._e2 in K:
             raise ValueError('the point e2 must lie in the interior of K')
 
     def __str__(self):