Coverage for picos/expressions/cone_soc.py: 91.84%
49 statements
« prev ^ index » next coverage.py v6.5.0, created at 2023-03-26 07:46 +0000
« prev ^ index » next coverage.py v6.5.0, created at 2023-03-26 07:46 +0000
1# ------------------------------------------------------------------------------
2# Copyright (C) 2019 Maximilian Stahlberg
3# Based on the original picos.expressions module by Guillaume Sagnol.
4#
5# This file is part of PICOS.
6#
7# PICOS is free software: you can redistribute it and/or modify it under the
8# terms of the GNU General Public License as published by the Free Software
9# Foundation, either version 3 of the License, or (at your option) any later
10# version.
11#
12# PICOS is distributed in the hope that it will be useful, but WITHOUT ANY
13# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License along with
17# this program. If not, see <http://www.gnu.org/licenses/>.
18# ------------------------------------------------------------------------------
20"""Implements :class:`SecondOrderCone`."""
22import operator
23from collections import namedtuple
25from .. import glyphs
26from ..apidoc import api_end, api_start
27from ..constraints import SOCConstraint
28from .cone import Cone
29from .exp_affine import AffineExpression
31_API_START = api_start(globals())
32# -------------------------------
35class SecondOrderCone(Cone):
36 r"""The second order cone.
38 .. _lorentz:
40 Also known as the quadratic, :math:`2`-norm, Lorentz, or ice cream cone.
42 For :math:`n \in \mathbb{Z}_{\geq 2}`, represents the convex cone
44 .. math::
46 \mathcal{Q}^n = \left\{
47 x \in \mathbb{R}^n
48 ~\middle|~
49 x_1 \geq \sqrt{\sum_{i = 2}^n x_i^2}
50 \right\}.
52 :Dual cone:
54 The second order cone as defined above is self-dual.
55 """
57 def __init__(self, dim=None):
58 """Construct a second order cone."""
59 if dim and dim < 2:
60 raise ValueError("The minimal dimension for {} is {}."
61 .format(self.__class__.__name__, 2))
63 typeStr = "Second Order Cone"
64 symbStr = glyphs.set(glyphs.sep(
65 glyphs.col_vectorize("t", "x"), glyphs.le(glyphs.norm("x"), "t")))
67 Cone.__init__(self, dim, typeStr, symbStr)
69 def _get_mutables(self):
70 return frozenset()
72 def _replace_mutables(self):
73 return self
75 Subtype = namedtuple("Subtype", ("dim",))
77 def _get_subtype(self):
78 return self.Subtype(self.dim)
80 @classmethod
81 def _predict(cls, subtype, relation, other):
82 from .uncertain import UncertainAffineExpression
84 assert isinstance(subtype, cls.Subtype)
86 if relation == operator.__rshift__:
87 if issubclass(other.clstype,
88 (AffineExpression, UncertainAffineExpression)) \
89 and not subtype.dim or subtype.dim == other.subtype.dim \
90 and other.subtype.dim >= 2:
91 if issubclass(other.clstype, UncertainAffineExpression):
92 raise NotImplementedError("Cannot predict the outcome "
93 "of constraining an uncertain affine expression to the "
94 "second order cone.")
96 return SOCConstraint.make_type(other.subtype.dim - 1)
98 return Cone._predict_base(cls, subtype, relation, other)
100 def _rshift_implementation(self, element):
101 from .uncertain import ConicPerturbationSet, UncertainAffineExpression
103 if isinstance(element, (AffineExpression, UncertainAffineExpression)):
104 self._check_dimension(element)
106 if len(element) < 2:
107 raise TypeError("Elements of the second order cone must be "
108 "at least two-dimensional.")
110 element = element.vec
112 if isinstance(element, AffineExpression):
113 return SOCConstraint(element[1:], element[0])
114 else:
115 if isinstance(element.universe, ConicPerturbationSet):
116 # Unpredictable case: Outcome depends on whether slices of
117 # the element remain uncertain.
118 return abs(element[1:]) <= element[0]
120 # Handle scenario uncertainty for all cones.
121 return Cone._rshift_base(self, element)
123 @property
124 def dual_cone(self):
125 """Implement :attr:`.cone.Cone.dual_cone`."""
126 return self
129# --------------------------------------
130__all__ = api_end(_API_START, globals())