Coverage for picos/expressions/set.py: 76.19%

84 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"""Backend for mathematical set type implementations.""" 

21 

22from abc import ABC, abstractmethod 

23 

24from .. import glyphs 

25from ..apidoc import api_end, api_start 

26from ..caching import cached_property 

27from .expression import (ExpressionType, convert_operands, refine_operands, 

28 validate_prediction) 

29 

30_API_START = api_start(globals()) 

31# ------------------------------- 

32 

33 

34class SetType(ExpressionType): 

35 """:class:`~picos.expressions.ExpressionType` for sets.""" 

36 

37 pass 

38 

39 

40class Set(ABC): 

41 """Abstract base class for mathematical set expressions.""" 

42 

43 def __init__(self, typeStr, symbStr): 

44 """Perform basic initialization for :class:`Set` instances. 

45 

46 :param str typeStr: Short string denoting the set type. 

47 :param str symbStr: Algebraic string description of the set. 

48 """ 

49 self._typeStr = typeStr 

50 self._symbStr = symbStr 

51 

52 @property 

53 def string(self): 

54 """Symbolic string representation of the set.""" 

55 return self._symbStr 

56 

57 @property 

58 @abstractmethod 

59 def Subtype(self): 

60 """Analog to :meth:`.expression.Expression.Subtype`.""" 

61 pass 

62 

63 @property 

64 def type(self): 

65 """Analog to :meth:`.expression.Expression.type`.""" 

66 return ExpressionType(self.__class__, self._get_subtype()) 

67 

68 @classmethod 

69 def make_type(cls, *args, **kwargs): 

70 """Analog to :meth:`.expression.Expression.make_type`.""" 

71 return ExpressionType(cls, cls.Subtype(*args, **kwargs)) 

72 

73 @property 

74 def subtype(self): 

75 """Analog to :meth:`.expression.Expression.subtype`.""" 

76 return self._get_subtype() 

77 

78 @property 

79 def refined(self): 

80 """The set itself, as sets do not support refinement. 

81 

82 This exists for compatibility with expressions. 

83 """ 

84 return self 

85 

86 def __repr__(self): 

87 return str(glyphs.repr2(self._typeStr, self._symbStr)) 

88 

89 def __str__(self): 

90 return str(self._symbStr) 

91 

92 def __format__(self, format_spec): 

93 return self._symbStr.__format__(format_spec) 

94 

95 @abstractmethod 

96 def _get_subtype(self): 

97 """:meth:`picos.expressions.Expression._get_subtype`.""" 

98 pass 

99 

100 @classmethod 

101 @abstractmethod 

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

103 """See :meth:`picos.expressions.Expression._predict`.""" 

104 pass 

105 

106 @abstractmethod 

107 def _get_mutables(self): 

108 """Return a Python set of mutables that are involved in the set.""" 

109 pass 

110 

111 mutables = property( 

112 lambda self: self._get_mutables(), 

113 doc=_get_mutables.__doc__) 

114 

115 @cached_property 

116 def variables(self): 

117 """The set of decision variables that are involved in the set.""" 

118 from .variables import BaseVariable 

119 

120 return frozenset(mutable for mutable in self._get_mutables() 

121 if isinstance(mutable, BaseVariable)) 

122 

123 @cached_property 

124 def parameters(self): 

125 """The set of parameters that are involved in the set.""" 

126 from .variables import BaseVariable 

127 

128 return frozenset(mutable for mutable in self._get_mutables() 

129 if not isinstance(mutable, BaseVariable)) 

130 

131 @abstractmethod 

132 def _replace_mutables(self, mapping): 

133 """See :meth:`~.expression.Expression._replace_mutables`.""" 

134 pass 

135 

136 # HACK: Borrow Expression.replace_mutables. 

137 # TODO: Common base class ExpressionOrSet. 

138 def replace_mutables(self, new_mutables): 

139 """See :meth:`~.expression.Expression.replace_mutables`.""" 

140 from .expression import Expression 

141 return Expression.replace_mutables(self, new_mutables) 

142 

143 # -------------------------------------------------------------------------- 

144 # Turn __lshift__ and __rshift__ into a single binary relation. 

145 # This is used for both Loewner order (defining LMIs) and set membership. 

146 # TODO: Define this in a common base class of Expression and Set. 

147 # -------------------------------------------------------------------------- 

148 

149 def _lshift_implementation(self, other): 

150 return NotImplemented 

151 

152 def _rshift_implementation(self, other): 

153 return NotImplemented 

154 

155 @convert_operands() 

156 @validate_prediction 

157 @refine_operands() 

158 def __lshift__(self, other): 

159 result = self._lshift_implementation(other) 

160 

161 if result is NotImplemented: 

162 result = other._rshift_implementation(self) 

163 

164 return result 

165 

166 @convert_operands() 

167 @validate_prediction 

168 @refine_operands() 

169 def __rshift__(self, other): 

170 result = self._rshift_implementation(other) 

171 

172 if result is NotImplemented: 

173 result = other._lshift_implementation(self) 

174 

175 return result 

176 

177 

178# -------------------------------------- 

179__all__ = api_end(_API_START, globals())