]> gitweb.michael.orlitzky.com - dunshire.git/blob - src/dunshire/cones.py
Switch the docs style of cones.py and update cartesian product containment.
[dunshire.git] / src / dunshire / cones.py
1 """
2 Class definitions for all of the symmetric cones (and their superclass,
3 class:`SymmetricCone`) supported by CVXOPT.
4 """
5
6 from cvxopt import matrix
7 from matrices import eigenvalues, norm
8
9 class SymmetricCone:
10 """
11 An instance of a symmetric (self-dual and homogeneous) cone.
12
13 There are three types of symmetric cones supported by CVXOPT:
14
15 1. The nonnegative orthant in the real n-space.
16 2. The Lorentz "ice cream" cone, or the second-order cone.
17 3. The cone of symmetric positive-semidefinite matrices.
18
19 This class is intended to encompass them all.
20
21 When constructing a single symmetric cone (i.e. not a
22 :class:`CartesianProduct` of them), the only information that we
23 need is its dimension. We take that dimension as a parameter, and
24 store it for later.
25
26 Parameters
27 ----------
28
29 dimension : int
30 The dimension of this cone.
31
32 """
33 def __init__(self, dimension):
34 """
35 A generic constructor for symmetric cones.
36 """
37 if dimension <= 0:
38 raise ValueError('cones must have dimension greater than zero')
39
40 self._dimension = dimension
41
42
43 def __contains__(self, point):
44 """
45 Return whether or not ``point`` belongs to this cone.
46
47 Parameters
48 ----------
49
50 point : matrix
51 The point to test for membership in this cone.
52
53 Raises
54 ------
55
56 NotImplementedError
57 Always, this method must be implemented in subclasses.
58
59 Examples
60 --------
61
62 >>> K = SymmetricCone(5)
63 >>> matrix([1,2]) in K
64 Traceback (most recent call last):
65 ...
66 NotImplementedError
67
68 """
69 raise NotImplementedError
70
71
72 def contains_strict(self, point):
73 """
74 Return whether or not ``point`` belongs to the interior
75 of this cone.
76
77 Parameters
78 ----------
79
80 point : matrix
81 The point to test for strict membership in this cone.
82
83 Raises
84 ------
85
86 NotImplementedError
87 Always, this method must be implemented in subclasses.
88
89 Examples
90 --------
91
92 >>> K = SymmetricCone(5)
93 >>> K.contains_strict(matrix([1,2]))
94 Traceback (most recent call last):
95 ...
96 NotImplementedError
97 """
98 raise NotImplementedError
99
100
101 def dimension(self):
102 """
103 Return the dimension of this symmetric cone.
104
105 The dimension of this symmetric cone is recorded during its
106 creation. This method simply returns the recorded value, and
107 should not need to be overridden in subclasses. We prefer to do
108 any special computation in ``__init__()`` and record the result
109 in ``self._dimension``.
110
111 Returns
112 -------
113
114 int
115 The stored dimension (from when this cone was constructed)
116 of this cone.
117
118 Examples
119 --------
120
121 >>> K = SymmetricCone(5)
122 >>> K.dimension()
123 5
124
125 """
126 return self._dimension
127
128
129 class NonnegativeOrthant(SymmetricCone):
130 """
131 The nonnegative orthant in the given number of dimensions.
132
133 Examples
134 --------
135
136 >>> K = NonnegativeOrthant(3)
137 >>> print(K)
138 Nonnegative orthant in the real 3-space
139
140 """
141 def __str__(self):
142 """
143 Output a human-readable description of myself.
144 """
145 tpl = 'Nonnegative orthant in the real {:d}-space'
146 return tpl.format(self.dimension())
147
148
149 def __contains__(self, point):
150 """
151 Return whether or not ``point`` belongs to this cone.
152
153 Parameters
154 ----------
155
156 point : matrix
157 A :class:`cvxopt.base.matrix` having dimensions ``(n,1)``
158 where ``n`` is the :meth:`dimension` of this cone.
159
160 Returns
161 -------
162
163 bool
164
165 ``True`` if ``point`` belongs to this cone, ``False`` otherwise.
166
167 Raises
168 ------
169
170 TypeError
171 If ``point`` is not a :class:`cvxopt.base.matrix`.
172
173 TypeError
174 If ``point`` has the wrong dimensions.
175
176 Examples
177 --------
178
179 >>> K = NonnegativeOrthant(3)
180 >>> matrix([1,2,3]) in K
181 True
182
183 >>> K = NonnegativeOrthant(3)
184 >>> matrix([1,-0.1,3]) in K
185 False
186
187 >>> K = NonnegativeOrthant(3)
188 >>> [1,2,3] in K
189 Traceback (most recent call last):
190 ...
191 TypeError: the given point is not a cvxopt.base.matrix
192
193 >>> K = NonnegativeOrthant(3)
194 >>> matrix([1,2]) in K
195 Traceback (most recent call last):
196 ...
197 TypeError: the given point has the wrong dimensions
198
199 """
200 if not isinstance(point, matrix):
201 raise TypeError('the given point is not a cvxopt.base.matrix')
202 if not point.size == (self.dimension(), 1):
203 raise TypeError('the given point has the wrong dimensions')
204
205 return all([x >= 0 for x in point])
206
207
208 def contains_strict(self, point):
209 """
210 Return whether or not ``point`` belongs to the interior of this
211 cone.
212
213 Parameters
214 ----------
215
216 point : matrix
217 A :class:`cvxopt.base.matrix` having dimensions ``(n,1)``
218 where ``n`` is the :meth:`dimension` of this cone.
219
220 Returns
221 -------
222
223 bool
224
225 ``True`` if ``point`` belongs to the interior of this cone,
226 ``False`` otherwise.
227
228 Raises
229 ------
230
231 TypeError
232 If ``point`` is not a :class:`cvxopt.base.matrix`.
233
234 TypeError
235 If ``point`` has the wrong dimensions.
236
237 Examples
238 --------
239
240 >>> K = NonnegativeOrthant(3)
241 >>> K.contains_strict(matrix([1,2,3]))
242 True
243
244 >>> K = NonnegativeOrthant(3)
245 >>> K.contains_strict(matrix([1,0,1]))
246 False
247
248 >>> K = NonnegativeOrthant(3)
249 >>> K.contains_strict(matrix([1,-0.1,3]))
250 False
251
252 >>> K = NonnegativeOrthant(3)
253 >>> K.contains_strict([1,2,3])
254 Traceback (most recent call last):
255 ...
256 TypeError: the given point is not a cvxopt.base.matrix
257
258 >>> K = NonnegativeOrthant(3)
259 >>> K.contains_strict(matrix([1,2]))
260 Traceback (most recent call last):
261 ...
262 TypeError: the given point has the wrong dimensions
263
264 """
265 if not isinstance(point, matrix):
266 raise TypeError('the given point is not a cvxopt.base.matrix')
267 if not point.size == (self.dimension(), 1):
268 raise TypeError('the given point has the wrong dimensions')
269
270 return all([x > 0 for x in point])
271
272
273
274 class IceCream(SymmetricCone):
275 """
276 The Lorentz "ice cream" cone in the given number of dimensions.
277
278 Examples
279 --------
280
281 >>> K = IceCream(3)
282 >>> print(K)
283 Lorentz "ice cream" cone in the real 3-space
284
285 """
286 def __str__(self):
287 """
288 Output a human-readable description of myself.
289 """
290 tpl = 'Lorentz "ice cream" cone in the real {:d}-space'
291 return tpl.format(self.dimension())
292
293
294 def __contains__(self, point):
295 """
296 Return whether or not ``point`` belongs to this cone.
297
298 Parameters
299 ----------
300
301 point : matrix
302 A :class:`cvxopt.base.matrix` having dimensions ``(n,1)``
303 where ``n`` is the :meth:`dimension` of this cone.
304
305 Returns
306 -------
307
308 bool
309
310 ``True`` if ``point`` belongs to this cone, ``False`` otherwise.
311
312 Raises
313 ------
314
315 TypeError
316 If ``point`` is not a :class:`cvxopt.base.matrix`.
317
318 TypeError
319 If ``point`` has the wrong dimensions.
320
321 Examples
322 --------
323
324 >>> K = IceCream(3)
325 >>> matrix([1,0.5,0.5]) in K
326 True
327
328 >>> K = IceCream(3)
329 >>> matrix([1,0,1]) in K
330 True
331
332 >>> K = IceCream(3)
333 >>> matrix([1,1,1]) in K
334 False
335
336 >>> K = IceCream(3)
337 >>> [1,2,3] in K
338 Traceback (most recent call last):
339 ...
340 TypeError: the given point is not a cvxopt.base.matrix
341
342 >>> K = IceCream(3)
343 >>> matrix([1,2]) in K
344 Traceback (most recent call last):
345 ...
346 TypeError: the given point has the wrong dimensions
347
348 """
349 if not isinstance(point, matrix):
350 raise TypeError('the given point is not a cvxopt.base.matrix')
351 if not point.size == (self.dimension(), 1):
352 raise TypeError('the given point has the wrong dimensions')
353
354 height = point[0]
355 if self.dimension() == 1:
356 # In one dimension, the ice cream cone is the nonnegative
357 # orthant.
358 return height >= 0
359 else:
360 radius = point[1:]
361 return height >= norm(radius)
362
363
364 def contains_strict(self, point):
365 """
366 Return whether or not ``point`` belongs to the interior
367 of this cone.
368
369 Parameters
370 ----------
371
372 point : matrix
373 A :class:`cvxopt.base.matrix` having dimensions ``(n,1)``
374 where ``n`` is the :meth:`dimension` of this cone.
375
376 Returns
377 -------
378
379 bool
380
381 ``True`` if ``point`` belongs to the interior of this cone,
382 ``False`` otherwise.
383
384 Raises
385 ------
386
387 TypeError
388 If ``point`` is not a :class:`cvxopt.base.matrix`.
389
390 TypeError
391 If ``point`` has the wrong dimensions.
392
393 Examples
394 --------
395
396 >>> K = IceCream(3)
397 >>> K.contains_strict(matrix([1,0.5,0.5]))
398 True
399
400 >>> K = IceCream(3)
401 >>> K.contains_strict(matrix([1,0,1]))
402 False
403
404 >>> K = IceCream(3)
405 >>> K.contains_strict(matrix([1,1,1]))
406 False
407
408 >>> K = IceCream(3)
409 >>> K.contains_strict([1,2,3])
410 Traceback (most recent call last):
411 ...
412 TypeError: the given point is not a cvxopt.base.matrix
413
414 >>> K = IceCream(3)
415 >>> K.contains_strict(matrix([1,2]))
416 Traceback (most recent call last):
417 ...
418 TypeError: the given point has the wrong dimensions
419
420 """
421 if not isinstance(point, matrix):
422 raise TypeError('the given point is not a cvxopt.base.matrix')
423 if not point.size == (self.dimension(), 1):
424 raise TypeError('the given point has the wrong dimensions')
425
426 height = point[0]
427 if self.dimension() == 1:
428 # In one dimension, the ice cream cone is the nonnegative
429 # orthant.
430 return height > 0
431 else:
432 radius = point[1:]
433 return height > norm(radius)
434
435
436 class SymmetricPSD(SymmetricCone):
437 """
438 The cone of real symmetric positive-semidefinite matrices.
439
440 This cone has a dimension ``n`` associated with it, but we let ``n``
441 refer to the dimension of the domain of our matrices and not the
442 dimension of the (much larger) space in which the matrices
443 themselves live. In other words, our ``n`` is the ``n`` that appears
444 in the usual notation `S^{n}` for symmetric matrices.
445
446 As a result, the cone ``SymmetricPSD(n)`` lives in a space of dimension
447 ``(n**2 + n)/2)``.
448
449 Examples
450 --------
451
452 >>> K = SymmetricPSD(3)
453 >>> print(K)
454 Cone of symmetric positive-semidefinite matrices on the real 3-space
455 >>> K.dimension()
456 3
457
458 """
459 def __str__(self):
460 """
461 Output a human-readable description of myself.
462 """
463 tpl = 'Cone of symmetric positive-semidefinite matrices ' \
464 'on the real {:d}-space'
465 return tpl.format(self.dimension())
466
467
468 def __contains__(self, point):
469 """
470 Return whether or not ``point`` belongs to this cone.
471
472 Parameters
473 ----------
474
475 point : matrix
476 A :class:`cvxopt.base.matrix` having dimensions ``(n,n)``
477 where ``n`` is the :meth:`dimension` of this cone.
478
479 Returns
480 -------
481
482 bool
483
484 ``True`` if ``point`` belongs to this cone, ``False`` otherwise.
485
486 Raises
487 ------
488
489 TypeError
490 If ``point`` is not a :class:`cvxopt.base.matrix`.
491
492 TypeError
493 If ``point`` has the wrong dimensions.
494
495 Examples
496 --------
497
498 >>> K = SymmetricPSD(2)
499 >>> matrix([[1,0],[0,1]]) in K
500 True
501
502 >>> K = SymmetricPSD(2)
503 >>> matrix([[0,0],[0,0]]) in K
504 True
505
506 >>> K = SymmetricPSD(3)
507 >>> matrix([[2,-1,0],[-1,2,-1],[0,-1,2]]) in K
508 True
509
510 >>> K = SymmetricPSD(5)
511 >>> A = matrix([[5,4,3,2,1],
512 ... [4,5,4,3,2],
513 ... [3,4,5,4,3],
514 ... [2,3,4,5,4],
515 ... [1,2,3,4,5]])
516 >>> A in K
517 True
518
519 >>> K = SymmetricPSD(5)
520 >>> A = matrix([[1,0,0,0,0],
521 ... [0,1,0,0,0],
522 ... [0,0,0,0,0],
523 ... [0,0,0,1,0],
524 ... [0,0,0,0,1]])
525 >>> A in K
526 True
527
528 >>> K = SymmetricPSD(2)
529 >>> [[1,2],[2,3]] in K
530 Traceback (most recent call last):
531 ...
532 TypeError: the given point is not a cvxopt.base.matrix
533
534 >>> K = SymmetricPSD(3)
535 >>> matrix([[1,2],[3,4]]) in K
536 Traceback (most recent call last):
537 ...
538 TypeError: the given point has the wrong dimensions
539
540 """
541 if not isinstance(point, matrix):
542 raise TypeError('the given point is not a cvxopt.base.matrix')
543 if not point.size == (self.dimension(), self.dimension()):
544 raise TypeError('the given point has the wrong dimensions')
545 if not point.typecode == 'd':
546 point = matrix(point, (self.dimension(), self.dimension()), 'd')
547 return all([e >= 0 for e in eigenvalues(point)])
548
549
550 def contains_strict(self, point):
551 """
552 Return whether or not ``point`` belongs to the interior
553 of this cone.
554
555 Parameters
556 ----------
557
558 point : matrix
559 A :class:`cvxopt.base.matrix` having dimensions ``(n,n)``
560 where ``n`` is the :meth:`dimension` of this cone.
561
562 Returns
563 -------
564
565 bool
566
567 ``True`` if ``point`` belongs to the interior of this cone,
568 ``False`` otherwise.
569
570 Raises
571 ------
572
573 TypeError
574 If ``point`` is not a :class:`cvxopt.base.matrix`.
575
576 TypeError
577 If ``point`` has the wrong dimensions.
578
579 Examples
580 --------
581
582 >>> K = SymmetricPSD(2)
583 >>> K.contains_strict(matrix([[1,0],[0,1]]))
584 True
585
586 >>> K = SymmetricPSD(2)
587 >>> K.contains_strict(matrix([[0,0],[0,0]]))
588 False
589
590 >>> K = SymmetricPSD(3)
591 >>> matrix([[2,-1,0],[-1,2,-1],[0,-1,2]]) in K
592 True
593
594 >>> K = SymmetricPSD(5)
595 >>> A = matrix([[5,4,3,2,1],
596 ... [4,5,4,3,2],
597 ... [3,4,5,4,3],
598 ... [2,3,4,5,4],
599 ... [1,2,3,4,5]])
600 >>> A in K
601 True
602
603 >>> K = SymmetricPSD(5)
604 >>> A = matrix([[1,0,0,0,0],
605 ... [0,1,0,0,0],
606 ... [0,0,0,0,0],
607 ... [0,0,0,1,0],
608 ... [0,0,0,0,1]])
609 >>> K.contains_strict(A)
610 False
611
612 >>> K = SymmetricPSD(2)
613 >>> K.contains_strict([[1,2],[2,3]])
614 Traceback (most recent call last):
615 ...
616 TypeError: the given point is not a cvxopt.base.matrix
617
618 >>> K = SymmetricPSD(3)
619 >>> K.contains_strict(matrix([[1,2],[3,4]]))
620 Traceback (most recent call last):
621 ...
622 TypeError: the given point has the wrong dimensions
623
624 """
625 if not isinstance(point, matrix):
626 raise TypeError('the given point is not a cvxopt.base.matrix')
627 if not point.size == (self.dimension(), self.dimension()):
628 raise TypeError('the given point has the wrong dimensions')
629 if not point.typecode == 'd':
630 point = matrix(point, (self.dimension(), self.dimension()), 'd')
631 return all([e > 0 for e in eigenvalues(point)])
632
633
634 class CartesianProduct(SymmetricCone):
635 """
636 A cartesian product of symmetric cones, which is itself a symmetric
637 cone.
638
639 Examples
640 --------
641
642 >>> K = CartesianProduct(NonnegativeOrthant(3), IceCream(2))
643 >>> print(K)
644 Cartesian product of dimension 5 with 2 factors:
645 * Nonnegative orthant in the real 3-space
646 * Lorentz "ice cream" cone in the real 2-space
647
648 """
649 def __init__(self, *factors):
650 my_dimension = sum([f.dimension() for f in factors])
651 super().__init__(my_dimension)
652 self._factors = factors
653
654
655 def __str__(self):
656 """
657 Output a human-readable description of myself.
658 """
659 tpl = 'Cartesian product of dimension {:d} with {:d} factors:'
660 tpl += '\n * {!s}' * len(self.factors())
661 format_args = [self.dimension(), len(self.factors())]
662 format_args += list(self.factors())
663 return tpl.format(*format_args)
664
665
666 def __contains__(self, point):
667 """
668 Return whether or not ``point`` belongs to this cone.
669
670 The ``point`` is expected to be a tuple of points which will be
671 tested for membership in this cone's factors. If each point in
672 the tuple belongs to its corresponding factor, then the whole
673 point belongs to this cone. Otherwise, it doesn't.
674
675 Parameters
676 ----------
677
678 point : tuple of matrix
679 A tuple of :class:`cvxopt.base.matrix` corresponding to the
680 :meth:`factors` of this cartesian product.
681
682 Returns
683 -------
684
685 bool
686
687 ``True`` if ``point`` belongs to this cone, ``False`` otherwise.
688
689 Raises
690 ------
691
692 TypeError
693 If ``point`` is not a tuple of :class:`cvxopt.base.matrix`.
694
695 TypeError
696 If any element of ``point`` has the wrong dimensions.
697
698 Examples
699 --------
700
701 >>> K = CartesianProduct(NonnegativeOrthant(3), IceCream(3))
702 >>> (matrix([1,2,3]), matrix([1,0.5,0.5])) in K
703 True
704
705 >>> K = CartesianProduct(NonnegativeOrthant(3), IceCream(3))
706 >>> (matrix([0,0,0]), matrix([1,0,1])) in K
707 True
708
709 >>> K = CartesianProduct(NonnegativeOrthant(3), IceCream(3))
710 >>> (matrix([1,1,1]), matrix([1,1,1])) in K
711 False
712
713 >>> K = CartesianProduct(NonnegativeOrthant(3), IceCream(3))
714 >>> (matrix([1,-1,1]), matrix([1,0,1])) in K
715 False
716
717 >>> K = CartesianProduct(NonnegativeOrthant(3), IceCream(3))
718 >>> [[1,2,3],[4,5,6]] in K
719 Traceback (most recent call last):
720 ...
721 TypeError: the given point is not a cvxopt.base.matrix
722
723 >>> K = CartesianProduct(NonnegativeOrthant(3), IceCream(3))
724 >>> (matrix([1,2]), matrix([3,4,5,6])) in K
725 Traceback (most recent call last):
726 ...
727 TypeError: the given point has the wrong dimensions
728
729 """
730 return all([p in f for (p,f) in zip(point, self.factors())])
731
732
733 def contains_strict(self, point):
734 """
735 Return whether or not ``point`` belongs to the interior
736 of this cone.
737
738 The ``point`` is expected to be a tuple of points which will be
739 tested for membership in this cone's factors. If each point in
740 the tuple belongs to the interior of its corresponding factor,
741 then the whole point belongs to the interior of this
742 cone. Otherwise, it doesn't.
743
744 Parameters
745 ----------
746
747 point : tuple of matrix
748 A tuple of :class:`cvxopt.base.matrix` corresponding to the
749 :meth:`factors` of this cartesian product.
750
751 Returns
752 -------
753
754 bool
755
756 ``True`` if ``point`` belongs to the interior of this cone,
757 ``False`` otherwise.
758
759 Raises
760 ------
761
762 TypeError
763 If ``point`` is not a tuple of :class:`cvxopt.base.matrix`.
764
765 TypeError
766 If any element of ``point`` has the wrong dimensions.
767
768 Examples
769 --------
770
771 >>> K = CartesianProduct(NonnegativeOrthant(3), IceCream(3))
772 >>> K.contains_strict((matrix([1,2,3]), matrix([1,0.5,0.5])))
773 True
774
775 >>> K = CartesianProduct(NonnegativeOrthant(3), IceCream(3))
776 >>> K.contains_strict((matrix([0,0,0]), matrix([1,0,1])))
777 False
778
779 >>> K = CartesianProduct(NonnegativeOrthant(3), IceCream(3))
780 >>> K.contains_strict((matrix([1,1,1]), matrix([1,1,1])))
781 False
782
783 >>> K = CartesianProduct(NonnegativeOrthant(3), IceCream(3))
784 >>> K.contains_strict((matrix([1,-1,1]), matrix([1,0,1])))
785 False
786
787 >>> K = CartesianProduct(NonnegativeOrthant(3), IceCream(3))
788 >>> K.contains_strict([[1,2,3],[4,5,6]])
789 Traceback (most recent call last):
790 ...
791 TypeError: the given point is not a cvxopt.base.matrix
792
793 >>> K = CartesianProduct(NonnegativeOrthant(3), IceCream(3))
794 >>> K.contains_strict((matrix([1,2]), matrix([3,4,5,6])))
795 Traceback (most recent call last):
796 ...
797 TypeError: the given point has the wrong dimensions
798
799 """
800 return all([f.contains_strict(p)
801 for (p,f) in zip(point, self.factors())])
802
803
804
805 def factors(self):
806 """
807 Return a tuple containing the factors (in order) of this
808 cartesian product.
809
810 Returns
811 -------
812
813 tuple of :class:`SymmetricCone`.
814 The factors of this cartesian product.
815
816 Examples
817 --------
818
819 >>> K = CartesianProduct(NonnegativeOrthant(3), IceCream(2))
820 >>> len(K.factors())
821 2
822
823 """
824 return self._factors
825
826
827 def cvxopt_dims(self):
828 """
829 Return a dictionary of dimensions corresponding to the factors
830 of this cartesian product. The format of this dictionary is
831 described in the CVXOPT user's guide:
832
833 http://cvxopt.org/userguide/coneprog.html#linear-cone-programs
834
835 Returns
836 -------
837
838 dict
839 A dimension dictionary suitable to feed to CVXOPT.
840
841 Examples
842 --------
843
844 >>> K = CartesianProduct(NonnegativeOrthant(3),
845 ... IceCream(2),
846 ... IceCream(3))
847 >>> d = K.cvxopt_dims()
848 >>> (d['l'], d['q'], d['s'])
849 (3, [2, 3], [])
850
851 """
852 dims = {'l':0, 'q':[], 's':[]}
853 dims['l'] += sum([K.dimension()
854 for K in self.factors()
855 if isinstance(K, NonnegativeOrthant)])
856 dims['q'] = [K.dimension()
857 for K in self.factors()
858 if isinstance(K, IceCream)]
859 dims['s'] = [K.dimension()
860 for K in self.factors()
861 if isinstance(K, SymmetricPSD)]
862 return dims
863
864