From: Michael Orlitzky Date: Tue, 11 Oct 2016 14:33:56 +0000 (-0400) Subject: Get rid of the contains_strict() methods and compare against ABS_TOL. X-Git-Tag: 0.1.0~163 X-Git-Url: https://gitweb.michael.orlitzky.com/?a=commitdiff_plain;h=4f0bc24d81ecbce9b2220d042b407529917ef5c6;p=dunshire.git Get rid of the contains_strict() methods and compare against ABS_TOL. 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. --- diff --git a/src/dunshire/cones.py b/src/dunshire/cones.py index 905dd8e..d7c9b62 100644 --- a/src/dunshire/cones.py +++ b/src/dunshire/cones.py @@ -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): """ diff --git a/src/dunshire/games.py b/src/dunshire/games.py index f3d8100..4b5383d 100644 --- a/src/dunshire/games.py +++ b/src/dunshire/games.py @@ -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):