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

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# ------------------------------------------------------------------------------ 

19 

20"""Implements :class:`SecondOrderCone`.""" 

21 

22import operator 

23from collections import namedtuple 

24 

25from .. import glyphs 

26from ..apidoc import api_end, api_start 

27from ..constraints import SOCConstraint 

28from .cone import Cone 

29from .exp_affine import AffineExpression 

30 

31_API_START = api_start(globals()) 

32# ------------------------------- 

33 

34 

35class SecondOrderCone(Cone): 

36 r"""The second order cone. 

37 

38 .. _lorentz: 

39 

40 Also known as the quadratic, :math:`2`-norm, Lorentz, or ice cream cone. 

41 

42 For :math:`n \in \mathbb{Z}_{\geq 2}`, represents the convex cone 

43 

44 .. math:: 

45 

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\}. 

51 

52 :Dual cone: 

53 

54 The second order cone as defined above is self-dual. 

55 """ 

56 

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

62 

63 typeStr = "Second Order Cone" 

64 symbStr = glyphs.set(glyphs.sep( 

65 glyphs.col_vectorize("t", "x"), glyphs.le(glyphs.norm("x"), "t"))) 

66 

67 Cone.__init__(self, dim, typeStr, symbStr) 

68 

69 def _get_mutables(self): 

70 return frozenset() 

71 

72 def _replace_mutables(self): 

73 return self 

74 

75 Subtype = namedtuple("Subtype", ("dim",)) 

76 

77 def _get_subtype(self): 

78 return self.Subtype(self.dim) 

79 

80 @classmethod 

81 def _predict(cls, subtype, relation, other): 

82 from .uncertain import UncertainAffineExpression 

83 

84 assert isinstance(subtype, cls.Subtype) 

85 

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.") 

95 

96 return SOCConstraint.make_type(other.subtype.dim - 1) 

97 

98 return Cone._predict_base(cls, subtype, relation, other) 

99 

100 def _rshift_implementation(self, element): 

101 from .uncertain import ConicPerturbationSet, UncertainAffineExpression 

102 

103 if isinstance(element, (AffineExpression, UncertainAffineExpression)): 

104 self._check_dimension(element) 

105 

106 if len(element) < 2: 

107 raise TypeError("Elements of the second order cone must be " 

108 "at least two-dimensional.") 

109 

110 element = element.vec 

111 

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] 

119 

120 # Handle scenario uncertainty for all cones. 

121 return Cone._rshift_base(self, element) 

122 

123 @property 

124 def dual_cone(self): 

125 """Implement :attr:`.cone.Cone.dual_cone`.""" 

126 return self 

127 

128 

129# -------------------------------------- 

130__all__ = api_end(_API_START, globals())