Coverage for /usr/lib/python3/dist-packages/sympy/sets/conditionset.py: 22%

125 statements  

« prev     ^ index     » next       coverage.py v7.9.1, created at 2025-06-14 15:55 +0200

1from sympy.core.singleton import S 

2from sympy.core.basic import Basic 

3from sympy.core.containers import Tuple 

4from sympy.core.function import Lambda, BadSignatureError 

5from sympy.core.logic import fuzzy_bool 

6from sympy.core.relational import Eq 

7from sympy.core.symbol import Dummy 

8from sympy.core.sympify import _sympify 

9from sympy.logic.boolalg import And, as_Boolean 

10from sympy.utilities.iterables import sift, flatten, has_dups 

11from sympy.utilities.exceptions import sympy_deprecation_warning 

12from .contains import Contains 

13from .sets import Set, Union, FiniteSet, SetKind 

14 

15 

16adummy = Dummy('conditionset') 

17 

18 

19class ConditionSet(Set): 

20 r""" 

21 Set of elements which satisfies a given condition. 

22 

23 .. math:: \{x \mid \textrm{condition}(x) = \texttt{True}, x \in S\} 

24 

25 Examples 

26 ======== 

27 

28 >>> from sympy import Symbol, S, ConditionSet, pi, Eq, sin, Interval 

29 >>> from sympy.abc import x, y, z 

30 

31 >>> sin_sols = ConditionSet(x, Eq(sin(x), 0), Interval(0, 2*pi)) 

32 >>> 2*pi in sin_sols 

33 True 

34 >>> pi/2 in sin_sols 

35 False 

36 >>> 3*pi in sin_sols 

37 False 

38 >>> 5 in ConditionSet(x, x**2 > 4, S.Reals) 

39 True 

40 

41 If the value is not in the base set, the result is false: 

42 

43 >>> 5 in ConditionSet(x, x**2 > 4, Interval(2, 4)) 

44 False 

45 

46 Notes 

47 ===== 

48 

49 Symbols with assumptions should be avoided or else the 

50 condition may evaluate without consideration of the set: 

51 

52 >>> n = Symbol('n', negative=True) 

53 >>> cond = (n > 0); cond 

54 False 

55 >>> ConditionSet(n, cond, S.Integers) 

56 EmptySet 

57 

58 Only free symbols can be changed by using `subs`: 

59 

60 >>> c = ConditionSet(x, x < 1, {x, z}) 

61 >>> c.subs(x, y) 

62 ConditionSet(x, x < 1, {y, z}) 

63 

64 To check if ``pi`` is in ``c`` use: 

65 

66 >>> pi in c 

67 False 

68 

69 If no base set is specified, the universal set is implied: 

70 

71 >>> ConditionSet(x, x < 1).base_set 

72 UniversalSet 

73 

74 Only symbols or symbol-like expressions can be used: 

75 

76 >>> ConditionSet(x + 1, x + 1 < 1, S.Integers) 

77 Traceback (most recent call last): 

78 ... 

79 ValueError: non-symbol dummy not recognized in condition 

80 

81 When the base set is a ConditionSet, the symbols will be 

82 unified if possible with preference for the outermost symbols: 

83 

84 >>> ConditionSet(x, x < y, ConditionSet(z, z + y < 2, S.Integers)) 

85 ConditionSet(x, (x < y) & (x + y < 2), Integers) 

86 

87 """ 

88 def __new__(cls, sym, condition, base_set=S.UniversalSet): 

89 sym = _sympify(sym) 

90 flat = flatten([sym]) 

91 if has_dups(flat): 

92 raise BadSignatureError("Duplicate symbols detected") 

93 base_set = _sympify(base_set) 

94 if not isinstance(base_set, Set): 

95 raise TypeError( 

96 'base set should be a Set object, not %s' % base_set) 

97 condition = _sympify(condition) 

98 

99 if isinstance(condition, FiniteSet): 

100 condition_orig = condition 

101 temp = (Eq(lhs, 0) for lhs in condition) 

102 condition = And(*temp) 

103 sympy_deprecation_warning( 

104 f""" 

105Using a set for the condition in ConditionSet is deprecated. Use a boolean 

106instead. 

107 

108In this case, replace 

109 

110 {condition_orig} 

111 

112with 

113 

114 {condition} 

115""", 

116 deprecated_since_version='1.5', 

117 active_deprecations_target="deprecated-conditionset-set", 

118 ) 

119 

120 condition = as_Boolean(condition) 

121 

122 if condition is S.true: 

123 return base_set 

124 

125 if condition is S.false: 

126 return S.EmptySet 

127 

128 if base_set is S.EmptySet: 

129 return S.EmptySet 

130 

131 # no simple answers, so now check syms 

132 for i in flat: 

133 if not getattr(i, '_diff_wrt', False): 

134 raise ValueError('`%s` is not symbol-like' % i) 

135 

136 if base_set.contains(sym) is S.false: 

137 raise TypeError('sym `%s` is not in base_set `%s`' % (sym, base_set)) 

138 

139 know = None 

140 if isinstance(base_set, FiniteSet): 

141 sifted = sift( 

142 base_set, lambda _: fuzzy_bool(condition.subs(sym, _))) 

143 if sifted[None]: 

144 know = FiniteSet(*sifted[True]) 

145 base_set = FiniteSet(*sifted[None]) 

146 else: 

147 return FiniteSet(*sifted[True]) 

148 

149 if isinstance(base_set, cls): 

150 s, c, b = base_set.args 

151 def sig(s): 

152 return cls(s, Eq(adummy, 0)).as_dummy().sym 

153 sa, sb = map(sig, (sym, s)) 

154 if sa != sb: 

155 raise BadSignatureError('sym does not match sym of base set') 

156 reps = dict(zip(flatten([sym]), flatten([s]))) 

157 if s == sym: 

158 condition = And(condition, c) 

159 base_set = b 

160 elif not c.free_symbols & sym.free_symbols: 

161 reps = {v: k for k, v in reps.items()} 

162 condition = And(condition, c.xreplace(reps)) 

163 base_set = b 

164 elif not condition.free_symbols & s.free_symbols: 

165 sym = sym.xreplace(reps) 

166 condition = And(condition.xreplace(reps), c) 

167 base_set = b 

168 

169 # flatten ConditionSet(Contains(ConditionSet())) expressions 

170 if isinstance(condition, Contains) and (sym == condition.args[0]): 

171 if isinstance(condition.args[1], Set): 

172 return condition.args[1].intersect(base_set) 

173 

174 rv = Basic.__new__(cls, sym, condition, base_set) 

175 return rv if know is None else Union(know, rv) 

176 

177 sym = property(lambda self: self.args[0]) 

178 condition = property(lambda self: self.args[1]) 

179 base_set = property(lambda self: self.args[2]) 

180 

181 @property 

182 def free_symbols(self): 

183 cond_syms = self.condition.free_symbols - self.sym.free_symbols 

184 return cond_syms | self.base_set.free_symbols 

185 

186 @property 

187 def bound_symbols(self): 

188 return flatten([self.sym]) 

189 

190 def _contains(self, other): 

191 def ok_sig(a, b): 

192 tuples = [isinstance(i, Tuple) for i in (a, b)] 

193 c = tuples.count(True) 

194 if c == 1: 

195 return False 

196 if c == 0: 

197 return True 

198 return len(a) == len(b) and all( 

199 ok_sig(i, j) for i, j in zip(a, b)) 

200 if not ok_sig(self.sym, other): 

201 return S.false 

202 

203 # try doing base_cond first and return 

204 # False immediately if it is False 

205 base_cond = Contains(other, self.base_set) 

206 if base_cond is S.false: 

207 return S.false 

208 

209 # Substitute other into condition. This could raise e.g. for 

210 # ConditionSet(x, 1/x >= 0, Reals).contains(0) 

211 lamda = Lambda((self.sym,), self.condition) 

212 try: 

213 lambda_cond = lamda(other) 

214 except TypeError: 

215 return Contains(other, self, evaluate=False) 

216 else: 

217 return And(base_cond, lambda_cond) 

218 

219 def as_relational(self, other): 

220 f = Lambda(self.sym, self.condition) 

221 if isinstance(self.sym, Tuple): 

222 f = f(*other) 

223 else: 

224 f = f(other) 

225 return And(f, self.base_set.contains(other)) 

226 

227 def _eval_subs(self, old, new): 

228 sym, cond, base = self.args 

229 dsym = sym.subs(old, adummy) 

230 insym = dsym.has(adummy) 

231 # prioritize changing a symbol in the base 

232 newbase = base.subs(old, new) 

233 if newbase != base: 

234 if not insym: 

235 cond = cond.subs(old, new) 

236 return self.func(sym, cond, newbase) 

237 if insym: 

238 pass # no change of bound symbols via subs 

239 elif getattr(new, '_diff_wrt', False): 

240 cond = cond.subs(old, new) 

241 else: 

242 pass # let error about the symbol raise from __new__ 

243 return self.func(sym, cond, base) 

244 

245 def _kind(self): 

246 return SetKind(self.sym.kind)