]>
gitweb.michael.orlitzky.com - dunshire.git/blob - dunshire/cones.py
2 Class definitions for all of the symmetric cones (and their superclass,
3 :class:`SymmetricCone`) supported by CVXOPT.
6 from cvxopt
import matrix
8 from .matrices
import eigenvalues
, norm
13 An instance of a symmetric (self-dual and homogeneous) cone.
15 There are three types of symmetric cones supported by CVXOPT:
17 1. The nonnegative orthant in the real n-space.
18 2. The Lorentz "ice cream" cone, or the second-order cone.
19 3. The cone of symmetric positive-semidefinite matrices.
21 This class is intended to encompass them all.
23 When constructing a single symmetric cone (i.e. not a
24 :class:`CartesianProduct` of them), the only information that we
25 need is its dimension. We take that dimension as a parameter, and
32 The dimension of this cone.
38 If you try to create a cone with dimension zero or less.
41 def __init__(self
, dimension
):
43 A generic constructor for symmetric cones.
46 raise ValueError('cones must have dimension greater than zero')
48 self
._dimension
= dimension
51 def __contains__(self
, point
):
53 Return whether or not ``point`` belongs to this cone.
59 The point to test for membership in this cone.
65 Always, this method must be implemented in subclasses.
70 >>> K = SymmetricCone(5)
71 >>> matrix([1,2]) in K
72 Traceback (most recent call last):
77 raise NotImplementedError
80 def ball_radius(self
, point
):
82 Return the radius of a ball around ``point`` in this cone.
84 Since a radius cannot be negative, the ``point`` must be
85 contained in this cone; otherwise, an error is raised.
91 A point contained in this cone.
97 A radius ``r`` such that the ball of radius ``r`` centered at
98 ``point`` is contained entirely within this cone.
104 Always, this method must be implemented in subclasses.
109 >>> K = SymmetricCone(5)
110 >>> K.ball_radius(matrix([1,1,1,1,1]))
111 Traceback (most recent call last):
116 raise NotImplementedError
121 Return the dimension of this symmetric cone.
123 The dimension of this symmetric cone is recorded during its
124 creation. This method simply returns the recorded value, and
125 should not need to be overridden in subclasses. We prefer to do
126 any special computation in ``__init__()`` and record the result
127 in ``self._dimension``.
133 The stored dimension (from when this cone was constructed)
139 >>> K = SymmetricCone(5)
144 return self
._dimension
147 class NonnegativeOrthant(SymmetricCone
):
149 The nonnegative orthant in the given number of dimensions.
154 >>> K = NonnegativeOrthant(3)
156 Nonnegative orthant in the real 3-space
161 Output a human-readable description of myself.
163 tpl
= 'Nonnegative orthant in the real {:d}-space'
164 return tpl
.format(self
.dimension())
167 def __contains__(self
, point
):
169 Return whether or not ``point`` belongs to this cone.
171 Since this test is expected to work on points whose components
172 are floating point numbers, it doesn't make any sense to
173 distinguish between strict and non-strict containment -- the
174 test uses a tolerance parameter.
180 A :class:`cvxopt.base.matrix` having dimensions ``(n,1)``
181 where ``n`` is the :meth:`dimension` of this cone.
188 ``True`` if ``point`` belongs to this cone, ``False`` otherwise.
194 If ``point`` is not a :class:`cvxopt.base.matrix`.
197 If ``point`` has the wrong dimensions.
202 All of these coordinates are positive enough:
204 >>> K = NonnegativeOrthant(3)
205 >>> matrix([1,2,3]) in K
208 The one negative coordinate pushes this point outside of ``K``:
210 >>> K = NonnegativeOrthant(3)
211 >>> matrix([1,-0.1,3]) in K
214 A boundary point is considered inside of ``K``:
215 >>> K = NonnegativeOrthant(3)
216 >>> matrix([1,0,3]) in K
219 Junk arguments don't work:
221 >>> K = NonnegativeOrthant(3)
223 Traceback (most recent call last):
225 TypeError: the given point is not a cvxopt.base.matrix
227 >>> K = NonnegativeOrthant(3)
228 >>> matrix([1,2]) in K
229 Traceback (most recent call last):
231 TypeError: the given point has the wrong dimensions
234 if not isinstance(point
, matrix
):
235 raise TypeError('the given point is not a cvxopt.base.matrix')
236 if not point
.size
== (self
.dimension(), 1):
237 raise TypeError('the given point has the wrong dimensions')
239 return all([x
> -options
.ABS_TOL
for x
in point
])
242 def ball_radius(self
, point
):
244 Return the radius of a ball around ``point`` in this cone.
246 Since a radius cannot be negative, the ``point`` must be
247 contained in this cone; otherwise, an error is raised.
249 The minimum distance from ``point`` to the complement of this
250 cone will always occur at its projection onto that set. It is
251 not hard to see that the projection is directly along one of the
252 coordinates, and so the minimum distance from ``point`` to the
253 boundary of this cone is the smallest coordinate of
254 ``point``. Therefore any radius less than that will work; we
255 divide it in half somewhat arbitrarily.
261 A point contained in this cone.
267 A radius ``r`` such that the ball of radius ``r`` centered at
268 ``point`` is contained entirely within this cone.
274 If ``point`` is not a :class:`cvxopt.base.matrix`.
277 If ``point`` has the wrong dimensions.
280 if ``point`` is not contained in this cone.
285 >>> K = NonnegativeOrthant(5)
286 >>> K.ball_radius(matrix([1,2,3,4,5]))
290 if not isinstance(point
, matrix
):
291 raise TypeError('the given point is not a cvxopt.base.matrix')
292 if not point
.size
== (self
.dimension(), 1):
293 raise TypeError('the given point has the wrong dimensions')
294 if not point
in self
:
295 raise ValueError('the given point does not lie in the cone')
297 return min(list(point
)) / 2.0
301 class IceCream(SymmetricCone
):
303 The Lorentz "ice cream" cone in the given number of dimensions.
310 Lorentz "ice cream" cone in the real 3-space
315 Output a human-readable description of myself.
317 tpl
= 'Lorentz "ice cream" cone in the real {:d}-space'
318 return tpl
.format(self
.dimension())
321 def __contains__(self
, point
):
323 Return whether or not ``point`` belongs to this cone.
325 Since this test is expected to work on points whose components
326 are floating point numbers, it doesn't make any sense to
327 distinguish between strict and non-strict containment -- the
328 test uses a tolerance parameter.
334 A :class:`cvxopt.base.matrix` having dimensions ``(n,1)``
335 where ``n`` is the :meth:`dimension` of this cone.
342 ``True`` if ``point`` belongs to this cone, ``False`` otherwise.
348 If ``point`` is not a :class:`cvxopt.base.matrix`.
351 If ``point`` has the wrong dimensions.
356 This point lies well within the ice cream cone:
359 >>> matrix([1,0.5,0.5]) in K
362 This one lies on its boundary:
365 >>> matrix([1,0,1]) in K
368 This point lies entirely outside of the ice cream cone:
371 >>> matrix([1,1,1]) in K
374 Junk arguments don't work:
378 Traceback (most recent call last):
380 TypeError: the given point is not a cvxopt.base.matrix
383 >>> matrix([1,2]) in K
384 Traceback (most recent call last):
386 TypeError: the given point has the wrong dimensions
389 if not isinstance(point
, matrix
):
390 raise TypeError('the given point is not a cvxopt.base.matrix')
391 if not point
.size
== (self
.dimension(), 1):
392 raise TypeError('the given point has the wrong dimensions')
395 if self
.dimension() == 1:
396 # In one dimension, the ice cream cone is the nonnegative
398 return height
> -options
.ABS_TOL
401 return norm(radius
) < (height
+ options
.ABS_TOL
)
404 def ball_radius(self
, point
):
406 Return the radius of a ball around ``point`` in this cone.
408 Since a radius cannot be negative, the ``point`` must be
409 contained in this cone; otherwise, an error is raised.
411 The minimum distance from ``point`` to the complement of this
412 cone will always occur at its projection onto that set. It is
413 not hard to see that the projection is at a "down and out" angle
414 of ``pi/4`` towards the outside of the cone. If one draws a
415 right triangle involving the ``point`` and that projection, it
416 becomes clear that the distance between ``point`` and its
417 projection is a factor of ``1/sqrt(2)`` times the "horizontal"
418 distance from ``point`` to boundary of this cone. For simplicity
419 we take ``1/2`` instead.
425 A point contained in this cone.
431 A radius ``r`` such that the ball of radius ``r`` centered at
432 ``point`` is contained entirely within this cone.
438 If ``point`` is not a :class:`cvxopt.base.matrix`.
441 If ``point`` has the wrong dimensions.
444 if ``point`` is not contained in this cone.
449 The height of ``x`` is one (its first coordinate), and so the
450 radius of the circle obtained from a height-one cross section is
451 also one. Note that the last two coordinates of ``x`` are half
452 of the way to the boundary of the cone, and in the direction of
453 a 30-60-90 triangle. If one follows those coordinates, they hit
454 at ``(1, sqrt(3)/2, 1/2)`` having unit norm. Thus the
455 "horizontal" distance to the boundary of the cone is ``(1 -
456 norm(x)``, which simplifies to ``1/2``. And rather than involve
457 a square root, we divide by two for a final safe radius of
460 >>> from math import sqrt
462 >>> x = matrix([1, sqrt(3)/4.0, 1/4.0])
467 if not isinstance(point
, matrix
):
468 raise TypeError('the given point is not a cvxopt.base.matrix')
469 if not point
.size
== (self
.dimension(), 1):
470 raise TypeError('the given point has the wrong dimensions')
471 if not point
in self
:
472 raise ValueError('the given point does not lie in the cone')
475 radius
= norm(point
[1:])
476 return (height
- radius
) / 2.0
479 class SymmetricPSD(SymmetricCone
):
481 The cone of real symmetric positive-semidefinite matrices.
483 This cone has a dimension ``n`` associated with it, but we let ``n``
484 refer to the dimension of the domain of our matrices and not the
485 dimension of the (much larger) space in which the matrices
486 themselves live. In other words, our ``n`` is the ``n`` that appears
487 in the usual notation :math:`S^{n}` for symmetric matrices.
489 As a result, the cone ``SymmetricPSD(n)`` lives in a space of dimension
490 :math:`\left(n^{2} + n\right)/2)`.
495 >>> K = SymmetricPSD(3)
497 Cone of symmetric positive-semidefinite matrices on the real 3-space
504 Output a human-readable description of myself.
506 tpl
= 'Cone of symmetric positive-semidefinite matrices ' \
507 'on the real {:d}-space'
508 return tpl
.format(self
.dimension())
511 def __contains__(self
, point
):
513 Return whether or not ``point`` belongs to this cone.
515 Since this test is expected to work on points whose components
516 are floating point numbers, it doesn't make any sense to
517 distinguish between strict and non-strict containment -- the
518 test uses a tolerance parameter.
524 A :class:`cvxopt.base.matrix` having dimensions ``(n,n)``
525 where ``n`` is the :meth:`dimension` of this cone.
532 ``True`` if ``point`` belongs to this cone, ``False`` otherwise.
538 If ``point`` is not a :class:`cvxopt.base.matrix`.
541 If ``point`` has the wrong dimensions.
546 These all lie in the interior of the Symmetric PSD cone:
548 >>> K = SymmetricPSD(2)
549 >>> matrix([[1,0],[0,1]]) in K
552 >>> K = SymmetricPSD(3)
553 >>> matrix([[2,-1,0],[-1,2,-1],[0,-1,2]]) in K
556 >>> K = SymmetricPSD(5)
557 >>> A = matrix([[5,4,3,2,1],
565 Boundary points lie in the cone as well:
567 >>> K = SymmetricPSD(2)
568 >>> matrix([[0,0],[0,0]]) in K
571 >>> K = SymmetricPSD(5)
572 >>> A = matrix([[1,0,0,0,0],
580 However, this matrix has a negative eigenvalue:
582 >>> K = SymmetricPSD(2)
583 >>> A = matrix([[ 1, -2],
588 An asymmetric cone with positive eigenvalues is not in the cone:
590 >>> K = SymmetricPSD(2)
591 >>> A = matrix([[10, 2],
596 Junk arguments don't work:
598 >>> K = SymmetricPSD(2)
599 >>> [[1,2],[2,3]] in K
600 Traceback (most recent call last):
602 TypeError: the given point is not a cvxopt.base.matrix
604 >>> K = SymmetricPSD(3)
605 >>> matrix([[1,2],[3,4]]) in K
606 Traceback (most recent call last):
608 TypeError: the given point has the wrong dimensions
611 if not isinstance(point
, matrix
):
612 raise TypeError('the given point is not a cvxopt.base.matrix')
613 if not point
.size
== (self
.dimension(), self
.dimension()):
614 raise TypeError('the given point has the wrong dimensions')
615 if not point
.typecode
== 'd':
616 point
= matrix(point
, (self
.dimension(), self
.dimension()), 'd')
617 if not norm(point
- point
.trans()) < options
.ABS_TOL
:
618 # It's not symmetric.
620 return all([e
> -options
.ABS_TOL
for e
in eigenvalues(point
)])
624 class CartesianProduct(SymmetricCone
):
626 A cartesian product of symmetric cones, which is itself a symmetric
632 >>> K = CartesianProduct(NonnegativeOrthant(3), IceCream(2))
634 Cartesian product of dimension 5 with 2 factors:
635 * Nonnegative orthant in the real 3-space
636 * Lorentz "ice cream" cone in the real 2-space
639 def __init__(self
, *factors
):
640 my_dimension
= sum([f
.dimension() for f
in factors
])
641 super().__init
__(my_dimension
)
642 self
._factors
= factors
647 Output a human-readable description of myself.
649 tpl
= 'Cartesian product of dimension {:d} with {:d} factors:'
650 tpl
+= '\n * {!s}' * len(self
.factors())
651 format_args
= [self
.dimension(), len(self
.factors())]
652 format_args
+= list(self
.factors())
653 return tpl
.format(*format_args
)
656 def __contains__(self
, point
):
658 Return whether or not ``point`` belongs to this cone.
660 The ``point`` is expected to be a tuple of points which will be
661 tested for membership in this cone's factors. If each point in
662 the tuple belongs to its corresponding factor, then the whole
663 point belongs to this cone. Otherwise, it doesn't.
668 point : tuple of matrix
669 A tuple of :class:`cvxopt.base.matrix` corresponding to the
670 :meth:`factors` of this cartesian product.
677 ``True`` if ``point`` belongs to this cone, ``False`` otherwise.
683 If ``point`` is not a tuple of :class:`cvxopt.base.matrix`.
686 If any element of ``point`` has the wrong dimensions.
691 The result depends on how containment is defined for our factors:
693 >>> K = CartesianProduct(NonnegativeOrthant(3), IceCream(3))
694 >>> (matrix([1,2,3]), matrix([1,0.5,0.5])) in K
697 >>> K = CartesianProduct(NonnegativeOrthant(3), IceCream(3))
698 >>> (matrix([0,0,0]), matrix([1,0,1])) in K
701 >>> K = CartesianProduct(NonnegativeOrthant(3), IceCream(3))
702 >>> (matrix([1,1,1]), matrix([1,1,1])) in K
705 >>> K = CartesianProduct(NonnegativeOrthant(3), IceCream(3))
706 >>> (matrix([1,-1,1]), matrix([1,0,1])) in K
709 Junk arguments don't work:
711 >>> K = CartesianProduct(NonnegativeOrthant(3), IceCream(3))
712 >>> [[1,2,3],[4,5,6]] in K
713 Traceback (most recent call last):
715 TypeError: the given point is not a cvxopt.base.matrix
717 >>> K = CartesianProduct(NonnegativeOrthant(3), IceCream(3))
718 >>> (matrix([1,2]), matrix([3,4,5,6])) in K
719 Traceback (most recent call last):
721 TypeError: the given point has the wrong dimensions
724 return all([p
in f
for (p
, f
) in zip(point
, self
.factors())])
730 Return a tuple containing the factors (in order) of this
736 tuple of :class:`SymmetricCone`.
737 The factors of this cartesian product.
742 >>> K = CartesianProduct(NonnegativeOrthant(3), IceCream(2))
750 def cvxopt_dims(self
):
752 Return a dictionary of dimensions corresponding to the factors
753 of this cartesian product. The format of this dictionary is
754 described in the CVXOPT user's guide:
756 http://cvxopt.org/userguide/coneprog.html#linear-cone-programs
762 A dimension dictionary suitable to feed to CVXOPT.
767 >>> K = CartesianProduct(NonnegativeOrthant(3),
770 >>> d = K.cvxopt_dims()
771 >>> (d['l'], d['q'], d['s'])
775 dims
= {'l':0, 'q':[], 's':[]}
776 dims
['l'] += sum([K
.dimension()
777 for K
in self
.factors()
778 if isinstance(K
, NonnegativeOrthant
)])
779 dims
['q'] = [K
.dimension()
780 for K
in self
.factors()
781 if isinstance(K
, IceCream
)]
782 dims
['s'] = [K
.dimension()
783 for K
in self
.factors()
784 if isinstance(K
, SymmetricPSD
)]