Coverage for picos/expressions/cone_soc.py: 91.84%

49 statements

, 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

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())