ZhengyangZhang commited on
Commit
e42f0f9
·
verified ·
1 Parent(s): 58c9c89

Add files using upload-large-folder tool

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. lib/python3.12/site-packages/sympy/__pycache__/__init__.cpython-312.pyc +0 -0
  2. lib/python3.12/site-packages/sympy/__pycache__/abc.cpython-312.pyc +0 -0
  3. lib/python3.12/site-packages/sympy/__pycache__/conftest.cpython-312.pyc +0 -0
  4. lib/python3.12/site-packages/sympy/__pycache__/galgebra.cpython-312.pyc +0 -0
  5. lib/python3.12/site-packages/sympy/__pycache__/release.cpython-312.pyc +0 -0
  6. lib/python3.12/site-packages/sympy/__pycache__/this.cpython-312.pyc +0 -0
  7. lib/python3.12/site-packages/sympy/assumptions/__init__.py +18 -0
  8. lib/python3.12/site-packages/sympy/assumptions/ask.py +651 -0
  9. lib/python3.12/site-packages/sympy/assumptions/ask_generated.py +352 -0
  10. lib/python3.12/site-packages/sympy/assumptions/assume.py +485 -0
  11. lib/python3.12/site-packages/sympy/assumptions/cnf.py +445 -0
  12. lib/python3.12/site-packages/sympy/assumptions/facts.py +270 -0
  13. lib/python3.12/site-packages/sympy/assumptions/handlers/calculus.py +258 -0
  14. lib/python3.12/site-packages/sympy/assumptions/handlers/matrices.py +716 -0
  15. lib/python3.12/site-packages/sympy/assumptions/handlers/ntheory.py +269 -0
  16. lib/python3.12/site-packages/sympy/assumptions/handlers/order.py +436 -0
  17. lib/python3.12/site-packages/sympy/assumptions/handlers/sets.py +772 -0
  18. lib/python3.12/site-packages/sympy/assumptions/lra_satask.py +286 -0
  19. lib/python3.12/site-packages/sympy/assumptions/refine.py +405 -0
  20. lib/python3.12/site-packages/sympy/assumptions/satask.py +369 -0
  21. lib/python3.12/site-packages/sympy/assumptions/sathandlers.py +322 -0
  22. lib/python3.12/site-packages/sympy/assumptions/tests/__init__.py +0 -0
  23. lib/python3.12/site-packages/sympy/assumptions/tests/__pycache__/__init__.cpython-312.pyc +0 -0
  24. lib/python3.12/site-packages/sympy/assumptions/tests/__pycache__/test_assumptions_2.cpython-312.pyc +0 -0
  25. lib/python3.12/site-packages/sympy/assumptions/tests/__pycache__/test_context.cpython-312.pyc +0 -0
  26. lib/python3.12/site-packages/sympy/assumptions/tests/__pycache__/test_matrices.cpython-312.pyc +0 -0
  27. lib/python3.12/site-packages/sympy/assumptions/tests/__pycache__/test_refine.cpython-312.pyc +0 -0
  28. lib/python3.12/site-packages/sympy/assumptions/tests/__pycache__/test_rel_queries.cpython-312.pyc +0 -0
  29. lib/python3.12/site-packages/sympy/assumptions/tests/__pycache__/test_satask.cpython-312.pyc +0 -0
  30. lib/python3.12/site-packages/sympy/assumptions/tests/__pycache__/test_sathandlers.cpython-312.pyc +0 -0
  31. lib/python3.12/site-packages/sympy/assumptions/tests/__pycache__/test_wrapper.cpython-312.pyc +0 -0
  32. lib/python3.12/site-packages/sympy/assumptions/tests/test_assumptions_2.py +35 -0
  33. lib/python3.12/site-packages/sympy/assumptions/tests/test_context.py +39 -0
  34. lib/python3.12/site-packages/sympy/assumptions/tests/test_matrices.py +283 -0
  35. lib/python3.12/site-packages/sympy/assumptions/tests/test_query.py +0 -0
  36. lib/python3.12/site-packages/sympy/assumptions/tests/test_refine.py +227 -0
  37. lib/python3.12/site-packages/sympy/assumptions/tests/test_rel_queries.py +172 -0
  38. lib/python3.12/site-packages/sympy/assumptions/tests/test_satask.py +378 -0
  39. lib/python3.12/site-packages/sympy/assumptions/tests/test_sathandlers.py +50 -0
  40. lib/python3.12/site-packages/sympy/assumptions/tests/test_wrapper.py +39 -0
  41. lib/python3.12/site-packages/sympy/assumptions/wrapper.py +164 -0
  42. lib/python3.12/site-packages/sympy/ntheory/__pycache__/__init__.cpython-312.pyc +0 -0
  43. lib/python3.12/site-packages/sympy/ntheory/__pycache__/bbp_pi.cpython-312.pyc +0 -0
  44. lib/python3.12/site-packages/sympy/ntheory/__pycache__/continued_fraction.cpython-312.pyc +0 -0
  45. lib/python3.12/site-packages/sympy/ntheory/__pycache__/digits.cpython-312.pyc +0 -0
  46. lib/python3.12/site-packages/sympy/ntheory/__pycache__/ecm.cpython-312.pyc +0 -0
  47. lib/python3.12/site-packages/sympy/ntheory/__pycache__/egyptian_fraction.cpython-312.pyc +0 -0
  48. lib/python3.12/site-packages/sympy/ntheory/__pycache__/elliptic_curve.cpython-312.pyc +0 -0
  49. lib/python3.12/site-packages/sympy/ntheory/__pycache__/factor_.cpython-312.pyc +0 -0
  50. lib/python3.12/site-packages/sympy/ntheory/__pycache__/generate.cpython-312.pyc +0 -0
lib/python3.12/site-packages/sympy/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (31 kB). View file
 
lib/python3.12/site-packages/sympy/__pycache__/abc.cpython-312.pyc ADDED
Binary file (4.37 kB). View file
 
lib/python3.12/site-packages/sympy/__pycache__/conftest.cpython-312.pyc ADDED
Binary file (4.41 kB). View file
 
lib/python3.12/site-packages/sympy/__pycache__/galgebra.cpython-312.pyc ADDED
Binary file (318 Bytes). View file
 
lib/python3.12/site-packages/sympy/__pycache__/release.cpython-312.pyc ADDED
Binary file (210 Bytes). View file
 
lib/python3.12/site-packages/sympy/__pycache__/this.cpython-312.pyc ADDED
Binary file (769 Bytes). View file
 
lib/python3.12/site-packages/sympy/assumptions/__init__.py ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ A module to implement logical predicates and assumption system.
3
+ """
4
+
5
+ from .assume import (
6
+ AppliedPredicate, Predicate, AssumptionsContext, assuming,
7
+ global_assumptions
8
+ )
9
+ from .ask import Q, ask, register_handler, remove_handler
10
+ from .refine import refine
11
+ from .relation import BinaryRelation, AppliedBinaryRelation
12
+
13
+ __all__ = [
14
+ 'AppliedPredicate', 'Predicate', 'AssumptionsContext', 'assuming',
15
+ 'global_assumptions', 'Q', 'ask', 'register_handler', 'remove_handler',
16
+ 'refine',
17
+ 'BinaryRelation', 'AppliedBinaryRelation'
18
+ ]
lib/python3.12/site-packages/sympy/assumptions/ask.py ADDED
@@ -0,0 +1,651 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Module for querying SymPy objects about assumptions."""
2
+
3
+ from sympy.assumptions.assume import (global_assumptions, Predicate,
4
+ AppliedPredicate)
5
+ from sympy.assumptions.cnf import CNF, EncodedCNF, Literal
6
+ from sympy.core import sympify
7
+ from sympy.core.kind import BooleanKind
8
+ from sympy.core.relational import Eq, Ne, Gt, Lt, Ge, Le
9
+ from sympy.logic.inference import satisfiable
10
+ from sympy.utilities.decorator import memoize_property
11
+ from sympy.utilities.exceptions import (sympy_deprecation_warning,
12
+ SymPyDeprecationWarning,
13
+ ignore_warnings)
14
+
15
+
16
+ # Memoization is necessary for the properties of AssumptionKeys to
17
+ # ensure that only one object of Predicate objects are created.
18
+ # This is because assumption handlers are registered on those objects.
19
+
20
+
21
+ class AssumptionKeys:
22
+ """
23
+ This class contains all the supported keys by ``ask``.
24
+ It should be accessed via the instance ``sympy.Q``.
25
+
26
+ """
27
+
28
+ # DO NOT add methods or properties other than predicate keys.
29
+ # SAT solver checks the properties of Q and use them to compute the
30
+ # fact system. Non-predicate attributes will break this.
31
+
32
+ @memoize_property
33
+ def hermitian(self):
34
+ from .handlers.sets import HermitianPredicate
35
+ return HermitianPredicate()
36
+
37
+ @memoize_property
38
+ def antihermitian(self):
39
+ from .handlers.sets import AntihermitianPredicate
40
+ return AntihermitianPredicate()
41
+
42
+ @memoize_property
43
+ def real(self):
44
+ from .handlers.sets import RealPredicate
45
+ return RealPredicate()
46
+
47
+ @memoize_property
48
+ def extended_real(self):
49
+ from .handlers.sets import ExtendedRealPredicate
50
+ return ExtendedRealPredicate()
51
+
52
+ @memoize_property
53
+ def imaginary(self):
54
+ from .handlers.sets import ImaginaryPredicate
55
+ return ImaginaryPredicate()
56
+
57
+ @memoize_property
58
+ def complex(self):
59
+ from .handlers.sets import ComplexPredicate
60
+ return ComplexPredicate()
61
+
62
+ @memoize_property
63
+ def algebraic(self):
64
+ from .handlers.sets import AlgebraicPredicate
65
+ return AlgebraicPredicate()
66
+
67
+ @memoize_property
68
+ def transcendental(self):
69
+ from .predicates.sets import TranscendentalPredicate
70
+ return TranscendentalPredicate()
71
+
72
+ @memoize_property
73
+ def integer(self):
74
+ from .handlers.sets import IntegerPredicate
75
+ return IntegerPredicate()
76
+
77
+ @memoize_property
78
+ def noninteger(self):
79
+ from .predicates.sets import NonIntegerPredicate
80
+ return NonIntegerPredicate()
81
+
82
+ @memoize_property
83
+ def rational(self):
84
+ from .handlers.sets import RationalPredicate
85
+ return RationalPredicate()
86
+
87
+ @memoize_property
88
+ def irrational(self):
89
+ from .handlers.sets import IrrationalPredicate
90
+ return IrrationalPredicate()
91
+
92
+ @memoize_property
93
+ def finite(self):
94
+ from .handlers.calculus import FinitePredicate
95
+ return FinitePredicate()
96
+
97
+ @memoize_property
98
+ def infinite(self):
99
+ from .handlers.calculus import InfinitePredicate
100
+ return InfinitePredicate()
101
+
102
+ @memoize_property
103
+ def positive_infinite(self):
104
+ from .handlers.calculus import PositiveInfinitePredicate
105
+ return PositiveInfinitePredicate()
106
+
107
+ @memoize_property
108
+ def negative_infinite(self):
109
+ from .handlers.calculus import NegativeInfinitePredicate
110
+ return NegativeInfinitePredicate()
111
+
112
+ @memoize_property
113
+ def positive(self):
114
+ from .handlers.order import PositivePredicate
115
+ return PositivePredicate()
116
+
117
+ @memoize_property
118
+ def negative(self):
119
+ from .handlers.order import NegativePredicate
120
+ return NegativePredicate()
121
+
122
+ @memoize_property
123
+ def zero(self):
124
+ from .handlers.order import ZeroPredicate
125
+ return ZeroPredicate()
126
+
127
+ @memoize_property
128
+ def extended_positive(self):
129
+ from .handlers.order import ExtendedPositivePredicate
130
+ return ExtendedPositivePredicate()
131
+
132
+ @memoize_property
133
+ def extended_negative(self):
134
+ from .handlers.order import ExtendedNegativePredicate
135
+ return ExtendedNegativePredicate()
136
+
137
+ @memoize_property
138
+ def nonzero(self):
139
+ from .handlers.order import NonZeroPredicate
140
+ return NonZeroPredicate()
141
+
142
+ @memoize_property
143
+ def nonpositive(self):
144
+ from .handlers.order import NonPositivePredicate
145
+ return NonPositivePredicate()
146
+
147
+ @memoize_property
148
+ def nonnegative(self):
149
+ from .handlers.order import NonNegativePredicate
150
+ return NonNegativePredicate()
151
+
152
+ @memoize_property
153
+ def extended_nonzero(self):
154
+ from .handlers.order import ExtendedNonZeroPredicate
155
+ return ExtendedNonZeroPredicate()
156
+
157
+ @memoize_property
158
+ def extended_nonpositive(self):
159
+ from .handlers.order import ExtendedNonPositivePredicate
160
+ return ExtendedNonPositivePredicate()
161
+
162
+ @memoize_property
163
+ def extended_nonnegative(self):
164
+ from .handlers.order import ExtendedNonNegativePredicate
165
+ return ExtendedNonNegativePredicate()
166
+
167
+ @memoize_property
168
+ def even(self):
169
+ from .handlers.ntheory import EvenPredicate
170
+ return EvenPredicate()
171
+
172
+ @memoize_property
173
+ def odd(self):
174
+ from .handlers.ntheory import OddPredicate
175
+ return OddPredicate()
176
+
177
+ @memoize_property
178
+ def prime(self):
179
+ from .handlers.ntheory import PrimePredicate
180
+ return PrimePredicate()
181
+
182
+ @memoize_property
183
+ def composite(self):
184
+ from .handlers.ntheory import CompositePredicate
185
+ return CompositePredicate()
186
+
187
+ @memoize_property
188
+ def commutative(self):
189
+ from .handlers.common import CommutativePredicate
190
+ return CommutativePredicate()
191
+
192
+ @memoize_property
193
+ def is_true(self):
194
+ from .handlers.common import IsTruePredicate
195
+ return IsTruePredicate()
196
+
197
+ @memoize_property
198
+ def symmetric(self):
199
+ from .handlers.matrices import SymmetricPredicate
200
+ return SymmetricPredicate()
201
+
202
+ @memoize_property
203
+ def invertible(self):
204
+ from .handlers.matrices import InvertiblePredicate
205
+ return InvertiblePredicate()
206
+
207
+ @memoize_property
208
+ def orthogonal(self):
209
+ from .handlers.matrices import OrthogonalPredicate
210
+ return OrthogonalPredicate()
211
+
212
+ @memoize_property
213
+ def unitary(self):
214
+ from .handlers.matrices import UnitaryPredicate
215
+ return UnitaryPredicate()
216
+
217
+ @memoize_property
218
+ def positive_definite(self):
219
+ from .handlers.matrices import PositiveDefinitePredicate
220
+ return PositiveDefinitePredicate()
221
+
222
+ @memoize_property
223
+ def upper_triangular(self):
224
+ from .handlers.matrices import UpperTriangularPredicate
225
+ return UpperTriangularPredicate()
226
+
227
+ @memoize_property
228
+ def lower_triangular(self):
229
+ from .handlers.matrices import LowerTriangularPredicate
230
+ return LowerTriangularPredicate()
231
+
232
+ @memoize_property
233
+ def diagonal(self):
234
+ from .handlers.matrices import DiagonalPredicate
235
+ return DiagonalPredicate()
236
+
237
+ @memoize_property
238
+ def fullrank(self):
239
+ from .handlers.matrices import FullRankPredicate
240
+ return FullRankPredicate()
241
+
242
+ @memoize_property
243
+ def square(self):
244
+ from .handlers.matrices import SquarePredicate
245
+ return SquarePredicate()
246
+
247
+ @memoize_property
248
+ def integer_elements(self):
249
+ from .handlers.matrices import IntegerElementsPredicate
250
+ return IntegerElementsPredicate()
251
+
252
+ @memoize_property
253
+ def real_elements(self):
254
+ from .handlers.matrices import RealElementsPredicate
255
+ return RealElementsPredicate()
256
+
257
+ @memoize_property
258
+ def complex_elements(self):
259
+ from .handlers.matrices import ComplexElementsPredicate
260
+ return ComplexElementsPredicate()
261
+
262
+ @memoize_property
263
+ def singular(self):
264
+ from .predicates.matrices import SingularPredicate
265
+ return SingularPredicate()
266
+
267
+ @memoize_property
268
+ def normal(self):
269
+ from .predicates.matrices import NormalPredicate
270
+ return NormalPredicate()
271
+
272
+ @memoize_property
273
+ def triangular(self):
274
+ from .predicates.matrices import TriangularPredicate
275
+ return TriangularPredicate()
276
+
277
+ @memoize_property
278
+ def unit_triangular(self):
279
+ from .predicates.matrices import UnitTriangularPredicate
280
+ return UnitTriangularPredicate()
281
+
282
+ @memoize_property
283
+ def eq(self):
284
+ from .relation.equality import EqualityPredicate
285
+ return EqualityPredicate()
286
+
287
+ @memoize_property
288
+ def ne(self):
289
+ from .relation.equality import UnequalityPredicate
290
+ return UnequalityPredicate()
291
+
292
+ @memoize_property
293
+ def gt(self):
294
+ from .relation.equality import StrictGreaterThanPredicate
295
+ return StrictGreaterThanPredicate()
296
+
297
+ @memoize_property
298
+ def ge(self):
299
+ from .relation.equality import GreaterThanPredicate
300
+ return GreaterThanPredicate()
301
+
302
+ @memoize_property
303
+ def lt(self):
304
+ from .relation.equality import StrictLessThanPredicate
305
+ return StrictLessThanPredicate()
306
+
307
+ @memoize_property
308
+ def le(self):
309
+ from .relation.equality import LessThanPredicate
310
+ return LessThanPredicate()
311
+
312
+
313
+ Q = AssumptionKeys()
314
+
315
+ def _extract_all_facts(assump, exprs):
316
+ """
317
+ Extract all relevant assumptions from *assump* with respect to given *exprs*.
318
+
319
+ Parameters
320
+ ==========
321
+
322
+ assump : sympy.assumptions.cnf.CNF
323
+
324
+ exprs : tuple of expressions
325
+
326
+ Returns
327
+ =======
328
+
329
+ sympy.assumptions.cnf.CNF
330
+
331
+ Examples
332
+ ========
333
+
334
+ >>> from sympy import Q
335
+ >>> from sympy.assumptions.cnf import CNF
336
+ >>> from sympy.assumptions.ask import _extract_all_facts
337
+ >>> from sympy.abc import x, y
338
+ >>> assump = CNF.from_prop(Q.positive(x) & Q.integer(y))
339
+ >>> exprs = (x,)
340
+ >>> cnf = _extract_all_facts(assump, exprs)
341
+ >>> cnf.clauses
342
+ {frozenset({Literal(Q.positive, False)})}
343
+
344
+ """
345
+ facts = set()
346
+
347
+ for clause in assump.clauses:
348
+ args = []
349
+ for literal in clause:
350
+ if isinstance(literal.lit, AppliedPredicate) and len(literal.lit.arguments) == 1:
351
+ if literal.lit.arg in exprs:
352
+ # Add literal if it has matching in it
353
+ args.append(Literal(literal.lit.function, literal.is_Not))
354
+ else:
355
+ # If any of the literals doesn't have matching expr don't add the whole clause.
356
+ break
357
+ else:
358
+ # If any of the literals aren't unary predicate don't add the whole clause.
359
+ break
360
+
361
+ else:
362
+ if args:
363
+ facts.add(frozenset(args))
364
+ return CNF(facts)
365
+
366
+
367
+ def ask(proposition, assumptions=True, context=global_assumptions):
368
+ """
369
+ Function to evaluate the proposition with assumptions.
370
+
371
+ Explanation
372
+ ===========
373
+
374
+ This function evaluates the proposition to ``True`` or ``False`` if
375
+ the truth value can be determined. If not, it returns ``None``.
376
+
377
+ It should be discerned from :func:`~.refine()` which, when applied to a
378
+ proposition, simplifies the argument to symbolic ``Boolean`` instead of
379
+ Python built-in ``True``, ``False`` or ``None``.
380
+
381
+ **Syntax**
382
+
383
+ * ask(proposition)
384
+ Evaluate the *proposition* in global assumption context.
385
+
386
+ * ask(proposition, assumptions)
387
+ Evaluate the *proposition* with respect to *assumptions* in
388
+ global assumption context.
389
+
390
+ Parameters
391
+ ==========
392
+
393
+ proposition : Boolean
394
+ Proposition which will be evaluated to boolean value. If this is
395
+ not ``AppliedPredicate``, it will be wrapped by ``Q.is_true``.
396
+
397
+ assumptions : Boolean, optional
398
+ Local assumptions to evaluate the *proposition*.
399
+
400
+ context : AssumptionsContext, optional
401
+ Default assumptions to evaluate the *proposition*. By default,
402
+ this is ``sympy.assumptions.global_assumptions`` variable.
403
+
404
+ Returns
405
+ =======
406
+
407
+ ``True``, ``False``, or ``None``
408
+
409
+ Raises
410
+ ======
411
+
412
+ TypeError : *proposition* or *assumptions* is not valid logical expression.
413
+
414
+ ValueError : assumptions are inconsistent.
415
+
416
+ Examples
417
+ ========
418
+
419
+ >>> from sympy import ask, Q, pi
420
+ >>> from sympy.abc import x, y
421
+ >>> ask(Q.rational(pi))
422
+ False
423
+ >>> ask(Q.even(x*y), Q.even(x) & Q.integer(y))
424
+ True
425
+ >>> ask(Q.prime(4*x), Q.integer(x))
426
+ False
427
+
428
+ If the truth value cannot be determined, ``None`` will be returned.
429
+
430
+ >>> print(ask(Q.odd(3*x))) # cannot determine unless we know x
431
+ None
432
+
433
+ ``ValueError`` is raised if assumptions are inconsistent.
434
+
435
+ >>> ask(Q.integer(x), Q.even(x) & Q.odd(x))
436
+ Traceback (most recent call last):
437
+ ...
438
+ ValueError: inconsistent assumptions Q.even(x) & Q.odd(x)
439
+
440
+ Notes
441
+ =====
442
+
443
+ Relations in assumptions are not implemented (yet), so the following
444
+ will not give a meaningful result.
445
+
446
+ >>> ask(Q.positive(x), x > 0)
447
+
448
+ It is however a work in progress.
449
+
450
+ See Also
451
+ ========
452
+
453
+ sympy.assumptions.refine.refine : Simplification using assumptions.
454
+ Proposition is not reduced to ``None`` if the truth value cannot
455
+ be determined.
456
+ """
457
+ from sympy.assumptions.satask import satask
458
+ from sympy.assumptions.lra_satask import lra_satask
459
+ from sympy.logic.algorithms.lra_theory import UnhandledInput
460
+
461
+ proposition = sympify(proposition)
462
+ assumptions = sympify(assumptions)
463
+
464
+ if isinstance(proposition, Predicate) or proposition.kind is not BooleanKind:
465
+ raise TypeError("proposition must be a valid logical expression")
466
+
467
+ if isinstance(assumptions, Predicate) or assumptions.kind is not BooleanKind:
468
+ raise TypeError("assumptions must be a valid logical expression")
469
+
470
+ binrelpreds = {Eq: Q.eq, Ne: Q.ne, Gt: Q.gt, Lt: Q.lt, Ge: Q.ge, Le: Q.le}
471
+ if isinstance(proposition, AppliedPredicate):
472
+ key, args = proposition.function, proposition.arguments
473
+ elif proposition.func in binrelpreds:
474
+ key, args = binrelpreds[type(proposition)], proposition.args
475
+ else:
476
+ key, args = Q.is_true, (proposition,)
477
+
478
+ # convert local and global assumptions to CNF
479
+ assump_cnf = CNF.from_prop(assumptions)
480
+ assump_cnf.extend(context)
481
+
482
+ # extract the relevant facts from assumptions with respect to args
483
+ local_facts = _extract_all_facts(assump_cnf, args)
484
+
485
+ # convert default facts and assumed facts to encoded CNF
486
+ known_facts_cnf = get_all_known_facts()
487
+ enc_cnf = EncodedCNF()
488
+ enc_cnf.from_cnf(CNF(known_facts_cnf))
489
+ enc_cnf.add_from_cnf(local_facts)
490
+
491
+ # check the satisfiability of given assumptions
492
+ if local_facts.clauses and satisfiable(enc_cnf) is False:
493
+ raise ValueError("inconsistent assumptions %s" % assumptions)
494
+
495
+ # quick computation for single fact
496
+ res = _ask_single_fact(key, local_facts)
497
+ if res is not None:
498
+ return res
499
+
500
+ # direct resolution method, no logic
501
+ res = key(*args)._eval_ask(assumptions)
502
+ if res is not None:
503
+ return bool(res)
504
+
505
+ # using satask (still costly)
506
+ res = satask(proposition, assumptions=assumptions, context=context)
507
+ if res is not None:
508
+ return res
509
+
510
+ try:
511
+ res = lra_satask(proposition, assumptions=assumptions, context=context)
512
+ except UnhandledInput:
513
+ return None
514
+
515
+ return res
516
+
517
+
518
+ def _ask_single_fact(key, local_facts):
519
+ """
520
+ Compute the truth value of single predicate using assumptions.
521
+
522
+ Parameters
523
+ ==========
524
+
525
+ key : sympy.assumptions.assume.Predicate
526
+ Proposition predicate.
527
+
528
+ local_facts : sympy.assumptions.cnf.CNF
529
+ Local assumption in CNF form.
530
+
531
+ Returns
532
+ =======
533
+
534
+ ``True``, ``False`` or ``None``
535
+
536
+ Examples
537
+ ========
538
+
539
+ >>> from sympy import Q
540
+ >>> from sympy.assumptions.cnf import CNF
541
+ >>> from sympy.assumptions.ask import _ask_single_fact
542
+
543
+ If prerequisite of proposition is rejected by the assumption,
544
+ return ``False``.
545
+
546
+ >>> key, assump = Q.zero, ~Q.zero
547
+ >>> local_facts = CNF.from_prop(assump)
548
+ >>> _ask_single_fact(key, local_facts)
549
+ False
550
+ >>> key, assump = Q.zero, ~Q.even
551
+ >>> local_facts = CNF.from_prop(assump)
552
+ >>> _ask_single_fact(key, local_facts)
553
+ False
554
+
555
+ If assumption implies the proposition, return ``True``.
556
+
557
+ >>> key, assump = Q.even, Q.zero
558
+ >>> local_facts = CNF.from_prop(assump)
559
+ >>> _ask_single_fact(key, local_facts)
560
+ True
561
+
562
+ If proposition rejects the assumption, return ``False``.
563
+
564
+ >>> key, assump = Q.even, Q.odd
565
+ >>> local_facts = CNF.from_prop(assump)
566
+ >>> _ask_single_fact(key, local_facts)
567
+ False
568
+ """
569
+ if local_facts.clauses:
570
+
571
+ known_facts_dict = get_known_facts_dict()
572
+
573
+ if len(local_facts.clauses) == 1:
574
+ cl, = local_facts.clauses
575
+ if len(cl) == 1:
576
+ f, = cl
577
+ prop_facts = known_facts_dict.get(key, None)
578
+ prop_req = prop_facts[0] if prop_facts is not None else set()
579
+ if f.is_Not and f.arg in prop_req:
580
+ # the prerequisite of proposition is rejected
581
+ return False
582
+
583
+ for clause in local_facts.clauses:
584
+ if len(clause) == 1:
585
+ f, = clause
586
+ prop_facts = known_facts_dict.get(f.arg, None) if not f.is_Not else None
587
+ if prop_facts is None:
588
+ continue
589
+
590
+ prop_req, prop_rej = prop_facts
591
+ if key in prop_req:
592
+ # assumption implies the proposition
593
+ return True
594
+ elif key in prop_rej:
595
+ # proposition rejects the assumption
596
+ return False
597
+
598
+ return None
599
+
600
+
601
+ def register_handler(key, handler):
602
+ """
603
+ Register a handler in the ask system. key must be a string and handler a
604
+ class inheriting from AskHandler.
605
+
606
+ .. deprecated:: 1.8.
607
+ Use multipledispatch handler instead. See :obj:`~.Predicate`.
608
+
609
+ """
610
+ sympy_deprecation_warning(
611
+ """
612
+ The AskHandler system is deprecated. The register_handler() function
613
+ should be replaced with the multipledispatch handler of Predicate.
614
+ """,
615
+ deprecated_since_version="1.8",
616
+ active_deprecations_target='deprecated-askhandler',
617
+ )
618
+ if isinstance(key, Predicate):
619
+ key = key.name.name
620
+ Qkey = getattr(Q, key, None)
621
+ if Qkey is not None:
622
+ Qkey.add_handler(handler)
623
+ else:
624
+ setattr(Q, key, Predicate(key, handlers=[handler]))
625
+
626
+
627
+ def remove_handler(key, handler):
628
+ """
629
+ Removes a handler from the ask system.
630
+
631
+ .. deprecated:: 1.8.
632
+ Use multipledispatch handler instead. See :obj:`~.Predicate`.
633
+
634
+ """
635
+ sympy_deprecation_warning(
636
+ """
637
+ The AskHandler system is deprecated. The remove_handler() function
638
+ should be replaced with the multipledispatch handler of Predicate.
639
+ """,
640
+ deprecated_since_version="1.8",
641
+ active_deprecations_target='deprecated-askhandler',
642
+ )
643
+ if isinstance(key, Predicate):
644
+ key = key.name.name
645
+ # Don't show the same warning again recursively
646
+ with ignore_warnings(SymPyDeprecationWarning):
647
+ getattr(Q, key).remove_handler(handler)
648
+
649
+
650
+ from sympy.assumptions.ask_generated import (get_all_known_facts,
651
+ get_known_facts_dict)
lib/python3.12/site-packages/sympy/assumptions/ask_generated.py ADDED
@@ -0,0 +1,352 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Do NOT manually edit this file.
3
+ Instead, run ./bin/ask_update.py.
4
+ """
5
+
6
+ from sympy.assumptions.ask import Q
7
+ from sympy.assumptions.cnf import Literal
8
+ from sympy.core.cache import cacheit
9
+
10
+ @cacheit
11
+ def get_all_known_facts():
12
+ """
13
+ Known facts between unary predicates as CNF clauses.
14
+ """
15
+ return {
16
+ frozenset((Literal(Q.algebraic, False), Literal(Q.imaginary, True), Literal(Q.transcendental, False))),
17
+ frozenset((Literal(Q.algebraic, False), Literal(Q.negative, True), Literal(Q.transcendental, False))),
18
+ frozenset((Literal(Q.algebraic, False), Literal(Q.positive, True), Literal(Q.transcendental, False))),
19
+ frozenset((Literal(Q.algebraic, False), Literal(Q.rational, True))),
20
+ frozenset((Literal(Q.algebraic, False), Literal(Q.transcendental, False), Literal(Q.zero, True))),
21
+ frozenset((Literal(Q.algebraic, True), Literal(Q.finite, False))),
22
+ frozenset((Literal(Q.algebraic, True), Literal(Q.transcendental, True))),
23
+ frozenset((Literal(Q.antihermitian, False), Literal(Q.hermitian, False), Literal(Q.zero, True))),
24
+ frozenset((Literal(Q.antihermitian, False), Literal(Q.imaginary, True))),
25
+ frozenset((Literal(Q.commutative, False), Literal(Q.finite, True))),
26
+ frozenset((Literal(Q.commutative, False), Literal(Q.infinite, True))),
27
+ frozenset((Literal(Q.complex_elements, False), Literal(Q.real_elements, True))),
28
+ frozenset((Literal(Q.composite, False), Literal(Q.even, True), Literal(Q.positive, True), Literal(Q.prime, False))),
29
+ frozenset((Literal(Q.composite, True), Literal(Q.even, False), Literal(Q.odd, False))),
30
+ frozenset((Literal(Q.composite, True), Literal(Q.positive, False))),
31
+ frozenset((Literal(Q.composite, True), Literal(Q.prime, True))),
32
+ frozenset((Literal(Q.diagonal, False), Literal(Q.lower_triangular, True), Literal(Q.upper_triangular, True))),
33
+ frozenset((Literal(Q.diagonal, True), Literal(Q.lower_triangular, False))),
34
+ frozenset((Literal(Q.diagonal, True), Literal(Q.normal, False))),
35
+ frozenset((Literal(Q.diagonal, True), Literal(Q.symmetric, False))),
36
+ frozenset((Literal(Q.diagonal, True), Literal(Q.upper_triangular, False))),
37
+ frozenset((Literal(Q.even, False), Literal(Q.odd, False), Literal(Q.prime, True))),
38
+ frozenset((Literal(Q.even, False), Literal(Q.zero, True))),
39
+ frozenset((Literal(Q.even, True), Literal(Q.odd, True))),
40
+ frozenset((Literal(Q.even, True), Literal(Q.rational, False))),
41
+ frozenset((Literal(Q.finite, False), Literal(Q.transcendental, True))),
42
+ frozenset((Literal(Q.finite, True), Literal(Q.infinite, True))),
43
+ frozenset((Literal(Q.fullrank, False), Literal(Q.invertible, True))),
44
+ frozenset((Literal(Q.fullrank, True), Literal(Q.invertible, False), Literal(Q.square, True))),
45
+ frozenset((Literal(Q.hermitian, False), Literal(Q.negative, True))),
46
+ frozenset((Literal(Q.hermitian, False), Literal(Q.positive, True))),
47
+ frozenset((Literal(Q.hermitian, False), Literal(Q.zero, True))),
48
+ frozenset((Literal(Q.imaginary, True), Literal(Q.negative, True))),
49
+ frozenset((Literal(Q.imaginary, True), Literal(Q.positive, True))),
50
+ frozenset((Literal(Q.imaginary, True), Literal(Q.zero, True))),
51
+ frozenset((Literal(Q.infinite, False), Literal(Q.negative_infinite, True))),
52
+ frozenset((Literal(Q.infinite, False), Literal(Q.positive_infinite, True))),
53
+ frozenset((Literal(Q.integer_elements, True), Literal(Q.real_elements, False))),
54
+ frozenset((Literal(Q.invertible, False), Literal(Q.positive_definite, True))),
55
+ frozenset((Literal(Q.invertible, False), Literal(Q.singular, False))),
56
+ frozenset((Literal(Q.invertible, False), Literal(Q.unitary, True))),
57
+ frozenset((Literal(Q.invertible, True), Literal(Q.singular, True))),
58
+ frozenset((Literal(Q.invertible, True), Literal(Q.square, False))),
59
+ frozenset((Literal(Q.irrational, False), Literal(Q.negative, True), Literal(Q.rational, False))),
60
+ frozenset((Literal(Q.irrational, False), Literal(Q.positive, True), Literal(Q.rational, False))),
61
+ frozenset((Literal(Q.irrational, False), Literal(Q.rational, False), Literal(Q.zero, True))),
62
+ frozenset((Literal(Q.irrational, True), Literal(Q.negative, False), Literal(Q.positive, False), Literal(Q.zero, False))),
63
+ frozenset((Literal(Q.irrational, True), Literal(Q.rational, True))),
64
+ frozenset((Literal(Q.lower_triangular, False), Literal(Q.triangular, True), Literal(Q.upper_triangular, False))),
65
+ frozenset((Literal(Q.lower_triangular, True), Literal(Q.triangular, False))),
66
+ frozenset((Literal(Q.negative, False), Literal(Q.positive, False), Literal(Q.rational, True), Literal(Q.zero, False))),
67
+ frozenset((Literal(Q.negative, True), Literal(Q.negative_infinite, True))),
68
+ frozenset((Literal(Q.negative, True), Literal(Q.positive, True))),
69
+ frozenset((Literal(Q.negative, True), Literal(Q.positive_infinite, True))),
70
+ frozenset((Literal(Q.negative, True), Literal(Q.zero, True))),
71
+ frozenset((Literal(Q.negative_infinite, True), Literal(Q.positive, True))),
72
+ frozenset((Literal(Q.negative_infinite, True), Literal(Q.positive_infinite, True))),
73
+ frozenset((Literal(Q.negative_infinite, True), Literal(Q.zero, True))),
74
+ frozenset((Literal(Q.normal, False), Literal(Q.unitary, True))),
75
+ frozenset((Literal(Q.normal, True), Literal(Q.square, False))),
76
+ frozenset((Literal(Q.odd, True), Literal(Q.rational, False))),
77
+ frozenset((Literal(Q.orthogonal, False), Literal(Q.real_elements, True), Literal(Q.unitary, True))),
78
+ frozenset((Literal(Q.orthogonal, True), Literal(Q.positive_definite, False))),
79
+ frozenset((Literal(Q.orthogonal, True), Literal(Q.unitary, False))),
80
+ frozenset((Literal(Q.positive, False), Literal(Q.prime, True))),
81
+ frozenset((Literal(Q.positive, True), Literal(Q.positive_infinite, True))),
82
+ frozenset((Literal(Q.positive, True), Literal(Q.zero, True))),
83
+ frozenset((Literal(Q.positive_infinite, True), Literal(Q.zero, True))),
84
+ frozenset((Literal(Q.square, False), Literal(Q.symmetric, True))),
85
+ frozenset((Literal(Q.triangular, False), Literal(Q.unit_triangular, True))),
86
+ frozenset((Literal(Q.triangular, False), Literal(Q.upper_triangular, True)))
87
+ }
88
+
89
+ @cacheit
90
+ def get_all_known_matrix_facts():
91
+ """
92
+ Known facts between unary predicates for matrices as CNF clauses.
93
+ """
94
+ return {
95
+ frozenset((Literal(Q.complex_elements, False), Literal(Q.real_elements, True))),
96
+ frozenset((Literal(Q.diagonal, False), Literal(Q.lower_triangular, True), Literal(Q.upper_triangular, True))),
97
+ frozenset((Literal(Q.diagonal, True), Literal(Q.lower_triangular, False))),
98
+ frozenset((Literal(Q.diagonal, True), Literal(Q.normal, False))),
99
+ frozenset((Literal(Q.diagonal, True), Literal(Q.symmetric, False))),
100
+ frozenset((Literal(Q.diagonal, True), Literal(Q.upper_triangular, False))),
101
+ frozenset((Literal(Q.fullrank, False), Literal(Q.invertible, True))),
102
+ frozenset((Literal(Q.fullrank, True), Literal(Q.invertible, False), Literal(Q.square, True))),
103
+ frozenset((Literal(Q.integer_elements, True), Literal(Q.real_elements, False))),
104
+ frozenset((Literal(Q.invertible, False), Literal(Q.positive_definite, True))),
105
+ frozenset((Literal(Q.invertible, False), Literal(Q.singular, False))),
106
+ frozenset((Literal(Q.invertible, False), Literal(Q.unitary, True))),
107
+ frozenset((Literal(Q.invertible, True), Literal(Q.singular, True))),
108
+ frozenset((Literal(Q.invertible, True), Literal(Q.square, False))),
109
+ frozenset((Literal(Q.lower_triangular, False), Literal(Q.triangular, True), Literal(Q.upper_triangular, False))),
110
+ frozenset((Literal(Q.lower_triangular, True), Literal(Q.triangular, False))),
111
+ frozenset((Literal(Q.normal, False), Literal(Q.unitary, True))),
112
+ frozenset((Literal(Q.normal, True), Literal(Q.square, False))),
113
+ frozenset((Literal(Q.orthogonal, False), Literal(Q.real_elements, True), Literal(Q.unitary, True))),
114
+ frozenset((Literal(Q.orthogonal, True), Literal(Q.positive_definite, False))),
115
+ frozenset((Literal(Q.orthogonal, True), Literal(Q.unitary, False))),
116
+ frozenset((Literal(Q.square, False), Literal(Q.symmetric, True))),
117
+ frozenset((Literal(Q.triangular, False), Literal(Q.unit_triangular, True))),
118
+ frozenset((Literal(Q.triangular, False), Literal(Q.upper_triangular, True)))
119
+ }
120
+
121
+ @cacheit
122
+ def get_all_known_number_facts():
123
+ """
124
+ Known facts between unary predicates for numbers as CNF clauses.
125
+ """
126
+ return {
127
+ frozenset((Literal(Q.algebraic, False), Literal(Q.imaginary, True), Literal(Q.transcendental, False))),
128
+ frozenset((Literal(Q.algebraic, False), Literal(Q.negative, True), Literal(Q.transcendental, False))),
129
+ frozenset((Literal(Q.algebraic, False), Literal(Q.positive, True), Literal(Q.transcendental, False))),
130
+ frozenset((Literal(Q.algebraic, False), Literal(Q.rational, True))),
131
+ frozenset((Literal(Q.algebraic, False), Literal(Q.transcendental, False), Literal(Q.zero, True))),
132
+ frozenset((Literal(Q.algebraic, True), Literal(Q.finite, False))),
133
+ frozenset((Literal(Q.algebraic, True), Literal(Q.transcendental, True))),
134
+ frozenset((Literal(Q.antihermitian, False), Literal(Q.hermitian, False), Literal(Q.zero, True))),
135
+ frozenset((Literal(Q.antihermitian, False), Literal(Q.imaginary, True))),
136
+ frozenset((Literal(Q.commutative, False), Literal(Q.finite, True))),
137
+ frozenset((Literal(Q.commutative, False), Literal(Q.infinite, True))),
138
+ frozenset((Literal(Q.composite, False), Literal(Q.even, True), Literal(Q.positive, True), Literal(Q.prime, False))),
139
+ frozenset((Literal(Q.composite, True), Literal(Q.even, False), Literal(Q.odd, False))),
140
+ frozenset((Literal(Q.composite, True), Literal(Q.positive, False))),
141
+ frozenset((Literal(Q.composite, True), Literal(Q.prime, True))),
142
+ frozenset((Literal(Q.even, False), Literal(Q.odd, False), Literal(Q.prime, True))),
143
+ frozenset((Literal(Q.even, False), Literal(Q.zero, True))),
144
+ frozenset((Literal(Q.even, True), Literal(Q.odd, True))),
145
+ frozenset((Literal(Q.even, True), Literal(Q.rational, False))),
146
+ frozenset((Literal(Q.finite, False), Literal(Q.transcendental, True))),
147
+ frozenset((Literal(Q.finite, True), Literal(Q.infinite, True))),
148
+ frozenset((Literal(Q.hermitian, False), Literal(Q.negative, True))),
149
+ frozenset((Literal(Q.hermitian, False), Literal(Q.positive, True))),
150
+ frozenset((Literal(Q.hermitian, False), Literal(Q.zero, True))),
151
+ frozenset((Literal(Q.imaginary, True), Literal(Q.negative, True))),
152
+ frozenset((Literal(Q.imaginary, True), Literal(Q.positive, True))),
153
+ frozenset((Literal(Q.imaginary, True), Literal(Q.zero, True))),
154
+ frozenset((Literal(Q.infinite, False), Literal(Q.negative_infinite, True))),
155
+ frozenset((Literal(Q.infinite, False), Literal(Q.positive_infinite, True))),
156
+ frozenset((Literal(Q.irrational, False), Literal(Q.negative, True), Literal(Q.rational, False))),
157
+ frozenset((Literal(Q.irrational, False), Literal(Q.positive, True), Literal(Q.rational, False))),
158
+ frozenset((Literal(Q.irrational, False), Literal(Q.rational, False), Literal(Q.zero, True))),
159
+ frozenset((Literal(Q.irrational, True), Literal(Q.negative, False), Literal(Q.positive, False), Literal(Q.zero, False))),
160
+ frozenset((Literal(Q.irrational, True), Literal(Q.rational, True))),
161
+ frozenset((Literal(Q.negative, False), Literal(Q.positive, False), Literal(Q.rational, True), Literal(Q.zero, False))),
162
+ frozenset((Literal(Q.negative, True), Literal(Q.negative_infinite, True))),
163
+ frozenset((Literal(Q.negative, True), Literal(Q.positive, True))),
164
+ frozenset((Literal(Q.negative, True), Literal(Q.positive_infinite, True))),
165
+ frozenset((Literal(Q.negative, True), Literal(Q.zero, True))),
166
+ frozenset((Literal(Q.negative_infinite, True), Literal(Q.positive, True))),
167
+ frozenset((Literal(Q.negative_infinite, True), Literal(Q.positive_infinite, True))),
168
+ frozenset((Literal(Q.negative_infinite, True), Literal(Q.zero, True))),
169
+ frozenset((Literal(Q.odd, True), Literal(Q.rational, False))),
170
+ frozenset((Literal(Q.positive, False), Literal(Q.prime, True))),
171
+ frozenset((Literal(Q.positive, True), Literal(Q.positive_infinite, True))),
172
+ frozenset((Literal(Q.positive, True), Literal(Q.zero, True))),
173
+ frozenset((Literal(Q.positive_infinite, True), Literal(Q.zero, True)))
174
+ }
175
+
176
+ @cacheit
177
+ def get_known_facts_dict():
178
+ """
179
+ Logical relations between unary predicates as dictionary.
180
+
181
+ Each key is a predicate, and item is two groups of predicates.
182
+ First group contains the predicates which are implied by the key, and
183
+ second group contains the predicates which are rejected by the key.
184
+
185
+ """
186
+ return {
187
+ Q.algebraic: (set([Q.algebraic, Q.commutative, Q.complex, Q.finite]),
188
+ set([Q.infinite, Q.negative_infinite, Q.positive_infinite,
189
+ Q.transcendental])),
190
+ Q.antihermitian: (set([Q.antihermitian]), set([])),
191
+ Q.commutative: (set([Q.commutative]), set([])),
192
+ Q.complex: (set([Q.commutative, Q.complex, Q.finite]),
193
+ set([Q.infinite, Q.negative_infinite, Q.positive_infinite])),
194
+ Q.complex_elements: (set([Q.complex_elements]), set([])),
195
+ Q.composite: (set([Q.algebraic, Q.commutative, Q.complex, Q.composite,
196
+ Q.extended_nonnegative, Q.extended_nonzero,
197
+ Q.extended_positive, Q.extended_real, Q.finite, Q.hermitian,
198
+ Q.integer, Q.nonnegative, Q.nonzero, Q.positive, Q.rational,
199
+ Q.real]), set([Q.extended_negative, Q.extended_nonpositive,
200
+ Q.imaginary, Q.infinite, Q.irrational, Q.negative,
201
+ Q.negative_infinite, Q.nonpositive, Q.positive_infinite,
202
+ Q.prime, Q.transcendental, Q.zero])),
203
+ Q.diagonal: (set([Q.diagonal, Q.lower_triangular, Q.normal, Q.square,
204
+ Q.symmetric, Q.triangular, Q.upper_triangular]), set([])),
205
+ Q.even: (set([Q.algebraic, Q.commutative, Q.complex, Q.even,
206
+ Q.extended_real, Q.finite, Q.hermitian, Q.integer, Q.rational,
207
+ Q.real]), set([Q.imaginary, Q.infinite, Q.irrational,
208
+ Q.negative_infinite, Q.odd, Q.positive_infinite,
209
+ Q.transcendental])),
210
+ Q.extended_negative: (set([Q.commutative, Q.extended_negative,
211
+ Q.extended_nonpositive, Q.extended_nonzero, Q.extended_real]),
212
+ set([Q.composite, Q.extended_nonnegative, Q.extended_positive,
213
+ Q.imaginary, Q.nonnegative, Q.positive, Q.positive_infinite,
214
+ Q.prime, Q.zero])),
215
+ Q.extended_nonnegative: (set([Q.commutative, Q.extended_nonnegative,
216
+ Q.extended_real]), set([Q.extended_negative, Q.imaginary,
217
+ Q.negative, Q.negative_infinite])),
218
+ Q.extended_nonpositive: (set([Q.commutative, Q.extended_nonpositive,
219
+ Q.extended_real]), set([Q.composite, Q.extended_positive,
220
+ Q.imaginary, Q.positive, Q.positive_infinite, Q.prime])),
221
+ Q.extended_nonzero: (set([Q.commutative, Q.extended_nonzero,
222
+ Q.extended_real]), set([Q.imaginary, Q.zero])),
223
+ Q.extended_positive: (set([Q.commutative, Q.extended_nonnegative,
224
+ Q.extended_nonzero, Q.extended_positive, Q.extended_real]),
225
+ set([Q.extended_negative, Q.extended_nonpositive, Q.imaginary,
226
+ Q.negative, Q.negative_infinite, Q.nonpositive, Q.zero])),
227
+ Q.extended_real: (set([Q.commutative, Q.extended_real]),
228
+ set([Q.imaginary])),
229
+ Q.finite: (set([Q.commutative, Q.finite]), set([Q.infinite,
230
+ Q.negative_infinite, Q.positive_infinite])),
231
+ Q.fullrank: (set([Q.fullrank]), set([])),
232
+ Q.hermitian: (set([Q.hermitian]), set([])),
233
+ Q.imaginary: (set([Q.antihermitian, Q.commutative, Q.complex,
234
+ Q.finite, Q.imaginary]), set([Q.composite, Q.even,
235
+ Q.extended_negative, Q.extended_nonnegative,
236
+ Q.extended_nonpositive, Q.extended_nonzero,
237
+ Q.extended_positive, Q.extended_real, Q.infinite, Q.integer,
238
+ Q.irrational, Q.negative, Q.negative_infinite, Q.nonnegative,
239
+ Q.nonpositive, Q.nonzero, Q.odd, Q.positive,
240
+ Q.positive_infinite, Q.prime, Q.rational, Q.real, Q.zero])),
241
+ Q.infinite: (set([Q.commutative, Q.infinite]), set([Q.algebraic,
242
+ Q.complex, Q.composite, Q.even, Q.finite, Q.imaginary,
243
+ Q.integer, Q.irrational, Q.negative, Q.nonnegative,
244
+ Q.nonpositive, Q.nonzero, Q.odd, Q.positive, Q.prime,
245
+ Q.rational, Q.real, Q.transcendental, Q.zero])),
246
+ Q.integer: (set([Q.algebraic, Q.commutative, Q.complex,
247
+ Q.extended_real, Q.finite, Q.hermitian, Q.integer, Q.rational,
248
+ Q.real]), set([Q.imaginary, Q.infinite, Q.irrational,
249
+ Q.negative_infinite, Q.positive_infinite, Q.transcendental])),
250
+ Q.integer_elements: (set([Q.complex_elements, Q.integer_elements,
251
+ Q.real_elements]), set([])),
252
+ Q.invertible: (set([Q.fullrank, Q.invertible, Q.square]),
253
+ set([Q.singular])),
254
+ Q.irrational: (set([Q.commutative, Q.complex, Q.extended_nonzero,
255
+ Q.extended_real, Q.finite, Q.hermitian, Q.irrational,
256
+ Q.nonzero, Q.real]), set([Q.composite, Q.even, Q.imaginary,
257
+ Q.infinite, Q.integer, Q.negative_infinite, Q.odd,
258
+ Q.positive_infinite, Q.prime, Q.rational, Q.zero])),
259
+ Q.is_true: (set([Q.is_true]), set([])),
260
+ Q.lower_triangular: (set([Q.lower_triangular, Q.triangular]), set([])),
261
+ Q.negative: (set([Q.commutative, Q.complex, Q.extended_negative,
262
+ Q.extended_nonpositive, Q.extended_nonzero, Q.extended_real,
263
+ Q.finite, Q.hermitian, Q.negative, Q.nonpositive, Q.nonzero,
264
+ Q.real]), set([Q.composite, Q.extended_nonnegative,
265
+ Q.extended_positive, Q.imaginary, Q.infinite,
266
+ Q.negative_infinite, Q.nonnegative, Q.positive,
267
+ Q.positive_infinite, Q.prime, Q.zero])),
268
+ Q.negative_infinite: (set([Q.commutative, Q.extended_negative,
269
+ Q.extended_nonpositive, Q.extended_nonzero, Q.extended_real,
270
+ Q.infinite, Q.negative_infinite]), set([Q.algebraic,
271
+ Q.complex, Q.composite, Q.even, Q.extended_nonnegative,
272
+ Q.extended_positive, Q.finite, Q.imaginary, Q.integer,
273
+ Q.irrational, Q.negative, Q.nonnegative, Q.nonpositive,
274
+ Q.nonzero, Q.odd, Q.positive, Q.positive_infinite, Q.prime,
275
+ Q.rational, Q.real, Q.transcendental, Q.zero])),
276
+ Q.noninteger: (set([Q.noninteger]), set([])),
277
+ Q.nonnegative: (set([Q.commutative, Q.complex, Q.extended_nonnegative,
278
+ Q.extended_real, Q.finite, Q.hermitian, Q.nonnegative,
279
+ Q.real]), set([Q.extended_negative, Q.imaginary, Q.infinite,
280
+ Q.negative, Q.negative_infinite, Q.positive_infinite])),
281
+ Q.nonpositive: (set([Q.commutative, Q.complex, Q.extended_nonpositive,
282
+ Q.extended_real, Q.finite, Q.hermitian, Q.nonpositive,
283
+ Q.real]), set([Q.composite, Q.extended_positive, Q.imaginary,
284
+ Q.infinite, Q.negative_infinite, Q.positive,
285
+ Q.positive_infinite, Q.prime])),
286
+ Q.nonzero: (set([Q.commutative, Q.complex, Q.extended_nonzero,
287
+ Q.extended_real, Q.finite, Q.hermitian, Q.nonzero, Q.real]),
288
+ set([Q.imaginary, Q.infinite, Q.negative_infinite,
289
+ Q.positive_infinite, Q.zero])),
290
+ Q.normal: (set([Q.normal, Q.square]), set([])),
291
+ Q.odd: (set([Q.algebraic, Q.commutative, Q.complex,
292
+ Q.extended_nonzero, Q.extended_real, Q.finite, Q.hermitian,
293
+ Q.integer, Q.nonzero, Q.odd, Q.rational, Q.real]),
294
+ set([Q.even, Q.imaginary, Q.infinite, Q.irrational,
295
+ Q.negative_infinite, Q.positive_infinite, Q.transcendental,
296
+ Q.zero])),
297
+ Q.orthogonal: (set([Q.fullrank, Q.invertible, Q.normal, Q.orthogonal,
298
+ Q.positive_definite, Q.square, Q.unitary]), set([Q.singular])),
299
+ Q.positive: (set([Q.commutative, Q.complex, Q.extended_nonnegative,
300
+ Q.extended_nonzero, Q.extended_positive, Q.extended_real,
301
+ Q.finite, Q.hermitian, Q.nonnegative, Q.nonzero, Q.positive,
302
+ Q.real]), set([Q.extended_negative, Q.extended_nonpositive,
303
+ Q.imaginary, Q.infinite, Q.negative, Q.negative_infinite,
304
+ Q.nonpositive, Q.positive_infinite, Q.zero])),
305
+ Q.positive_definite: (set([Q.fullrank, Q.invertible,
306
+ Q.positive_definite, Q.square]), set([Q.singular])),
307
+ Q.positive_infinite: (set([Q.commutative, Q.extended_nonnegative,
308
+ Q.extended_nonzero, Q.extended_positive, Q.extended_real,
309
+ Q.infinite, Q.positive_infinite]), set([Q.algebraic,
310
+ Q.complex, Q.composite, Q.even, Q.extended_negative,
311
+ Q.extended_nonpositive, Q.finite, Q.imaginary, Q.integer,
312
+ Q.irrational, Q.negative, Q.negative_infinite, Q.nonnegative,
313
+ Q.nonpositive, Q.nonzero, Q.odd, Q.positive, Q.prime,
314
+ Q.rational, Q.real, Q.transcendental, Q.zero])),
315
+ Q.prime: (set([Q.algebraic, Q.commutative, Q.complex,
316
+ Q.extended_nonnegative, Q.extended_nonzero,
317
+ Q.extended_positive, Q.extended_real, Q.finite, Q.hermitian,
318
+ Q.integer, Q.nonnegative, Q.nonzero, Q.positive, Q.prime,
319
+ Q.rational, Q.real]), set([Q.composite, Q.extended_negative,
320
+ Q.extended_nonpositive, Q.imaginary, Q.infinite, Q.irrational,
321
+ Q.negative, Q.negative_infinite, Q.nonpositive,
322
+ Q.positive_infinite, Q.transcendental, Q.zero])),
323
+ Q.rational: (set([Q.algebraic, Q.commutative, Q.complex,
324
+ Q.extended_real, Q.finite, Q.hermitian, Q.rational, Q.real]),
325
+ set([Q.imaginary, Q.infinite, Q.irrational,
326
+ Q.negative_infinite, Q.positive_infinite, Q.transcendental])),
327
+ Q.real: (set([Q.commutative, Q.complex, Q.extended_real, Q.finite,
328
+ Q.hermitian, Q.real]), set([Q.imaginary, Q.infinite,
329
+ Q.negative_infinite, Q.positive_infinite])),
330
+ Q.real_elements: (set([Q.complex_elements, Q.real_elements]), set([])),
331
+ Q.singular: (set([Q.singular]), set([Q.invertible, Q.orthogonal,
332
+ Q.positive_definite, Q.unitary])),
333
+ Q.square: (set([Q.square]), set([])),
334
+ Q.symmetric: (set([Q.square, Q.symmetric]), set([])),
335
+ Q.transcendental: (set([Q.commutative, Q.complex, Q.finite,
336
+ Q.transcendental]), set([Q.algebraic, Q.composite, Q.even,
337
+ Q.infinite, Q.integer, Q.negative_infinite, Q.odd,
338
+ Q.positive_infinite, Q.prime, Q.rational, Q.zero])),
339
+ Q.triangular: (set([Q.triangular]), set([])),
340
+ Q.unit_triangular: (set([Q.triangular, Q.unit_triangular]), set([])),
341
+ Q.unitary: (set([Q.fullrank, Q.invertible, Q.normal, Q.square,
342
+ Q.unitary]), set([Q.singular])),
343
+ Q.upper_triangular: (set([Q.triangular, Q.upper_triangular]), set([])),
344
+ Q.zero: (set([Q.algebraic, Q.commutative, Q.complex, Q.even,
345
+ Q.extended_nonnegative, Q.extended_nonpositive,
346
+ Q.extended_real, Q.finite, Q.hermitian, Q.integer,
347
+ Q.nonnegative, Q.nonpositive, Q.rational, Q.real, Q.zero]),
348
+ set([Q.composite, Q.extended_negative, Q.extended_nonzero,
349
+ Q.extended_positive, Q.imaginary, Q.infinite, Q.irrational,
350
+ Q.negative, Q.negative_infinite, Q.nonzero, Q.odd, Q.positive,
351
+ Q.positive_infinite, Q.prime, Q.transcendental])),
352
+ }
lib/python3.12/site-packages/sympy/assumptions/assume.py ADDED
@@ -0,0 +1,485 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """A module which implements predicates and assumption context."""
2
+
3
+ from contextlib import contextmanager
4
+ import inspect
5
+ from sympy.core.symbol import Str
6
+ from sympy.core.sympify import _sympify
7
+ from sympy.logic.boolalg import Boolean, false, true
8
+ from sympy.multipledispatch.dispatcher import Dispatcher, str_signature
9
+ from sympy.utilities.exceptions import sympy_deprecation_warning
10
+ from sympy.utilities.iterables import is_sequence
11
+ from sympy.utilities.source import get_class
12
+
13
+
14
+ class AssumptionsContext(set):
15
+ """
16
+ Set containing default assumptions which are applied to the ``ask()``
17
+ function.
18
+
19
+ Explanation
20
+ ===========
21
+
22
+ This is used to represent global assumptions, but you can also use this
23
+ class to create your own local assumptions contexts. It is basically a thin
24
+ wrapper to Python's set, so see its documentation for advanced usage.
25
+
26
+ Examples
27
+ ========
28
+
29
+ The default assumption context is ``global_assumptions``, which is initially empty:
30
+
31
+ >>> from sympy import ask, Q
32
+ >>> from sympy.assumptions import global_assumptions
33
+ >>> global_assumptions
34
+ AssumptionsContext()
35
+
36
+ You can add default assumptions:
37
+
38
+ >>> from sympy.abc import x
39
+ >>> global_assumptions.add(Q.real(x))
40
+ >>> global_assumptions
41
+ AssumptionsContext({Q.real(x)})
42
+ >>> ask(Q.real(x))
43
+ True
44
+
45
+ And remove them:
46
+
47
+ >>> global_assumptions.remove(Q.real(x))
48
+ >>> print(ask(Q.real(x)))
49
+ None
50
+
51
+ The ``clear()`` method removes every assumption:
52
+
53
+ >>> global_assumptions.add(Q.positive(x))
54
+ >>> global_assumptions
55
+ AssumptionsContext({Q.positive(x)})
56
+ >>> global_assumptions.clear()
57
+ >>> global_assumptions
58
+ AssumptionsContext()
59
+
60
+ See Also
61
+ ========
62
+
63
+ assuming
64
+
65
+ """
66
+
67
+ def add(self, *assumptions):
68
+ """Add assumptions."""
69
+ for a in assumptions:
70
+ super().add(a)
71
+
72
+ def _sympystr(self, printer):
73
+ if not self:
74
+ return "%s()" % self.__class__.__name__
75
+ return "{}({})".format(self.__class__.__name__, printer._print_set(self))
76
+
77
+ global_assumptions = AssumptionsContext()
78
+
79
+
80
+ class AppliedPredicate(Boolean):
81
+ """
82
+ The class of expressions resulting from applying ``Predicate`` to
83
+ the arguments. ``AppliedPredicate`` merely wraps its argument and
84
+ remain unevaluated. To evaluate it, use the ``ask()`` function.
85
+
86
+ Examples
87
+ ========
88
+
89
+ >>> from sympy import Q, ask
90
+ >>> Q.integer(1)
91
+ Q.integer(1)
92
+
93
+ The ``function`` attribute returns the predicate, and the ``arguments``
94
+ attribute returns the tuple of arguments.
95
+
96
+ >>> type(Q.integer(1))
97
+ <class 'sympy.assumptions.assume.AppliedPredicate'>
98
+ >>> Q.integer(1).function
99
+ Q.integer
100
+ >>> Q.integer(1).arguments
101
+ (1,)
102
+
103
+ Applied predicates can be evaluated to a boolean value with ``ask``:
104
+
105
+ >>> ask(Q.integer(1))
106
+ True
107
+
108
+ """
109
+ __slots__ = ()
110
+
111
+ def __new__(cls, predicate, *args):
112
+ if not isinstance(predicate, Predicate):
113
+ raise TypeError("%s is not a Predicate." % predicate)
114
+ args = map(_sympify, args)
115
+ return super().__new__(cls, predicate, *args)
116
+
117
+ @property
118
+ def arg(self):
119
+ """
120
+ Return the expression used by this assumption.
121
+
122
+ Examples
123
+ ========
124
+
125
+ >>> from sympy import Q, Symbol
126
+ >>> x = Symbol('x')
127
+ >>> a = Q.integer(x + 1)
128
+ >>> a.arg
129
+ x + 1
130
+
131
+ """
132
+ # Will be deprecated
133
+ args = self._args
134
+ if len(args) == 2:
135
+ # backwards compatibility
136
+ return args[1]
137
+ raise TypeError("'arg' property is allowed only for unary predicates.")
138
+
139
+ @property
140
+ def function(self):
141
+ """
142
+ Return the predicate.
143
+ """
144
+ # Will be changed to self.args[0] after args overriding is removed
145
+ return self._args[0]
146
+
147
+ @property
148
+ def arguments(self):
149
+ """
150
+ Return the arguments which are applied to the predicate.
151
+ """
152
+ # Will be changed to self.args[1:] after args overriding is removed
153
+ return self._args[1:]
154
+
155
+ def _eval_ask(self, assumptions):
156
+ return self.function.eval(self.arguments, assumptions)
157
+
158
+ @property
159
+ def binary_symbols(self):
160
+ from .ask import Q
161
+ if self.function == Q.is_true:
162
+ i = self.arguments[0]
163
+ if i.is_Boolean or i.is_Symbol:
164
+ return i.binary_symbols
165
+ if self.function in (Q.eq, Q.ne):
166
+ if true in self.arguments or false in self.arguments:
167
+ if self.arguments[0].is_Symbol:
168
+ return {self.arguments[0]}
169
+ elif self.arguments[1].is_Symbol:
170
+ return {self.arguments[1]}
171
+ return set()
172
+
173
+
174
+ class PredicateMeta(type):
175
+ def __new__(cls, clsname, bases, dct):
176
+ # If handler is not defined, assign empty dispatcher.
177
+ if "handler" not in dct:
178
+ name = f"Ask{clsname.capitalize()}Handler"
179
+ handler = Dispatcher(name, doc="Handler for key %s" % name)
180
+ dct["handler"] = handler
181
+
182
+ dct["_orig_doc"] = dct.get("__doc__", "")
183
+
184
+ return super().__new__(cls, clsname, bases, dct)
185
+
186
+ @property
187
+ def __doc__(cls):
188
+ handler = cls.handler
189
+ doc = cls._orig_doc
190
+ if cls is not Predicate and handler is not None:
191
+ doc += "Handler\n"
192
+ doc += " =======\n\n"
193
+
194
+ # Append the handler's doc without breaking sphinx documentation.
195
+ docs = [" Multiply dispatched method: %s" % handler.name]
196
+ if handler.doc:
197
+ for line in handler.doc.splitlines():
198
+ if not line:
199
+ continue
200
+ docs.append(" %s" % line)
201
+ other = []
202
+ for sig in handler.ordering[::-1]:
203
+ func = handler.funcs[sig]
204
+ if func.__doc__:
205
+ s = ' Inputs: <%s>' % str_signature(sig)
206
+ lines = []
207
+ for line in func.__doc__.splitlines():
208
+ lines.append(" %s" % line)
209
+ s += "\n".join(lines)
210
+ docs.append(s)
211
+ else:
212
+ other.append(str_signature(sig))
213
+ if other:
214
+ othersig = " Other signatures:"
215
+ for line in other:
216
+ othersig += "\n * %s" % line
217
+ docs.append(othersig)
218
+
219
+ doc += '\n\n'.join(docs)
220
+
221
+ return doc
222
+
223
+
224
+ class Predicate(Boolean, metaclass=PredicateMeta):
225
+ """
226
+ Base class for mathematical predicates. It also serves as a
227
+ constructor for undefined predicate objects.
228
+
229
+ Explanation
230
+ ===========
231
+
232
+ Predicate is a function that returns a boolean value [1].
233
+
234
+ Predicate function is object, and it is instance of predicate class.
235
+ When a predicate is applied to arguments, ``AppliedPredicate``
236
+ instance is returned. This merely wraps the argument and remain
237
+ unevaluated. To obtain the truth value of applied predicate, use the
238
+ function ``ask``.
239
+
240
+ Evaluation of predicate is done by multiple dispatching. You can
241
+ register new handler to the predicate to support new types.
242
+
243
+ Every predicate in SymPy can be accessed via the property of ``Q``.
244
+ For example, ``Q.even`` returns the predicate which checks if the
245
+ argument is even number.
246
+
247
+ To define a predicate which can be evaluated, you must subclass this
248
+ class, make an instance of it, and register it to ``Q``. After then,
249
+ dispatch the handler by argument types.
250
+
251
+ If you directly construct predicate using this class, you will get
252
+ ``UndefinedPredicate`` which cannot be dispatched. This is useful
253
+ when you are building boolean expressions which do not need to be
254
+ evaluated.
255
+
256
+ Examples
257
+ ========
258
+
259
+ Applying and evaluating to boolean value:
260
+
261
+ >>> from sympy import Q, ask
262
+ >>> ask(Q.prime(7))
263
+ True
264
+
265
+ You can define a new predicate by subclassing and dispatching. Here,
266
+ we define a predicate for sexy primes [2] as an example.
267
+
268
+ >>> from sympy import Predicate, Integer
269
+ >>> class SexyPrimePredicate(Predicate):
270
+ ... name = "sexyprime"
271
+ >>> Q.sexyprime = SexyPrimePredicate()
272
+ >>> @Q.sexyprime.register(Integer, Integer)
273
+ ... def _(int1, int2, assumptions):
274
+ ... args = sorted([int1, int2])
275
+ ... if not all(ask(Q.prime(a), assumptions) for a in args):
276
+ ... return False
277
+ ... return args[1] - args[0] == 6
278
+ >>> ask(Q.sexyprime(5, 11))
279
+ True
280
+
281
+ Direct constructing returns ``UndefinedPredicate``, which can be
282
+ applied but cannot be dispatched.
283
+
284
+ >>> from sympy import Predicate, Integer
285
+ >>> Q.P = Predicate("P")
286
+ >>> type(Q.P)
287
+ <class 'sympy.assumptions.assume.UndefinedPredicate'>
288
+ >>> Q.P(1)
289
+ Q.P(1)
290
+ >>> Q.P.register(Integer)(lambda expr, assump: True)
291
+ Traceback (most recent call last):
292
+ ...
293
+ TypeError: <class 'sympy.assumptions.assume.UndefinedPredicate'> cannot be dispatched.
294
+
295
+ References
296
+ ==========
297
+
298
+ .. [1] https://en.wikipedia.org/wiki/Predicate_%28mathematical_logic%29
299
+ .. [2] https://en.wikipedia.org/wiki/Sexy_prime
300
+
301
+ """
302
+
303
+ is_Atom = True
304
+
305
+ def __new__(cls, *args, **kwargs):
306
+ if cls is Predicate:
307
+ return UndefinedPredicate(*args, **kwargs)
308
+ obj = super().__new__(cls, *args)
309
+ return obj
310
+
311
+ @property
312
+ def name(self):
313
+ # May be overridden
314
+ return type(self).__name__
315
+
316
+ @classmethod
317
+ def register(cls, *types, **kwargs):
318
+ """
319
+ Register the signature to the handler.
320
+ """
321
+ if cls.handler is None:
322
+ raise TypeError("%s cannot be dispatched." % type(cls))
323
+ return cls.handler.register(*types, **kwargs)
324
+
325
+ @classmethod
326
+ def register_many(cls, *types, **kwargs):
327
+ """
328
+ Register multiple signatures to same handler.
329
+ """
330
+ def _(func):
331
+ for t in types:
332
+ if not is_sequence(t):
333
+ t = (t,) # for convenience, allow passing `type` to mean `(type,)`
334
+ cls.register(*t, **kwargs)(func)
335
+ return _
336
+
337
+ def __call__(self, *args):
338
+ return AppliedPredicate(self, *args)
339
+
340
+ def eval(self, args, assumptions=True):
341
+ """
342
+ Evaluate ``self(*args)`` under the given assumptions.
343
+
344
+ This uses only direct resolution methods, not logical inference.
345
+ """
346
+ result = None
347
+ try:
348
+ result = self.handler(*args, assumptions=assumptions)
349
+ except NotImplementedError:
350
+ pass
351
+ return result
352
+
353
+ def _eval_refine(self, assumptions):
354
+ # When Predicate is no longer Boolean, delete this method
355
+ return self
356
+
357
+
358
+ class UndefinedPredicate(Predicate):
359
+ """
360
+ Predicate without handler.
361
+
362
+ Explanation
363
+ ===========
364
+
365
+ This predicate is generated by using ``Predicate`` directly for
366
+ construction. It does not have a handler, and evaluating this with
367
+ arguments is done by SAT solver.
368
+
369
+ Examples
370
+ ========
371
+
372
+ >>> from sympy import Predicate, Q
373
+ >>> Q.P = Predicate('P')
374
+ >>> Q.P.func
375
+ <class 'sympy.assumptions.assume.UndefinedPredicate'>
376
+ >>> Q.P.name
377
+ Str('P')
378
+
379
+ """
380
+
381
+ handler = None
382
+
383
+ def __new__(cls, name, handlers=None):
384
+ # "handlers" parameter supports old design
385
+ if not isinstance(name, Str):
386
+ name = Str(name)
387
+ obj = super(Boolean, cls).__new__(cls, name)
388
+ obj.handlers = handlers or []
389
+ return obj
390
+
391
+ @property
392
+ def name(self):
393
+ return self.args[0]
394
+
395
+ def _hashable_content(self):
396
+ return (self.name,)
397
+
398
+ def __getnewargs__(self):
399
+ return (self.name,)
400
+
401
+ def __call__(self, expr):
402
+ return AppliedPredicate(self, expr)
403
+
404
+ def add_handler(self, handler):
405
+ sympy_deprecation_warning(
406
+ """
407
+ The AskHandler system is deprecated. Predicate.add_handler()
408
+ should be replaced with the multipledispatch handler of Predicate.
409
+ """,
410
+ deprecated_since_version="1.8",
411
+ active_deprecations_target='deprecated-askhandler',
412
+ )
413
+ self.handlers.append(handler)
414
+
415
+ def remove_handler(self, handler):
416
+ sympy_deprecation_warning(
417
+ """
418
+ The AskHandler system is deprecated. Predicate.remove_handler()
419
+ should be replaced with the multipledispatch handler of Predicate.
420
+ """,
421
+ deprecated_since_version="1.8",
422
+ active_deprecations_target='deprecated-askhandler',
423
+ )
424
+ self.handlers.remove(handler)
425
+
426
+ def eval(self, args, assumptions=True):
427
+ # Support for deprecated design
428
+ # When old design is removed, this will always return None
429
+ sympy_deprecation_warning(
430
+ """
431
+ The AskHandler system is deprecated. Evaluating UndefinedPredicate
432
+ objects should be replaced with the multipledispatch handler of
433
+ Predicate.
434
+ """,
435
+ deprecated_since_version="1.8",
436
+ active_deprecations_target='deprecated-askhandler',
437
+ stacklevel=5,
438
+ )
439
+ expr, = args
440
+ res, _res = None, None
441
+ mro = inspect.getmro(type(expr))
442
+ for handler in self.handlers:
443
+ cls = get_class(handler)
444
+ for subclass in mro:
445
+ eval_ = getattr(cls, subclass.__name__, None)
446
+ if eval_ is None:
447
+ continue
448
+ res = eval_(expr, assumptions)
449
+ # Do not stop if value returned is None
450
+ # Try to check for higher classes
451
+ if res is None:
452
+ continue
453
+ if _res is None:
454
+ _res = res
455
+ else:
456
+ # only check consistency if both resolutors have concluded
457
+ if _res != res:
458
+ raise ValueError('incompatible resolutors')
459
+ break
460
+ return res
461
+
462
+
463
+ @contextmanager
464
+ def assuming(*assumptions):
465
+ """
466
+ Context manager for assumptions.
467
+
468
+ Examples
469
+ ========
470
+
471
+ >>> from sympy import assuming, Q, ask
472
+ >>> from sympy.abc import x, y
473
+ >>> print(ask(Q.integer(x + y)))
474
+ None
475
+ >>> with assuming(Q.integer(x), Q.integer(y)):
476
+ ... print(ask(Q.integer(x + y)))
477
+ True
478
+ """
479
+ old_global_assumptions = global_assumptions.copy()
480
+ global_assumptions.update(assumptions)
481
+ try:
482
+ yield
483
+ finally:
484
+ global_assumptions.clear()
485
+ global_assumptions.update(old_global_assumptions)
lib/python3.12/site-packages/sympy/assumptions/cnf.py ADDED
@@ -0,0 +1,445 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ The classes used here are for the internal use of assumptions system
3
+ only and should not be used anywhere else as these do not possess the
4
+ signatures common to SymPy objects. For general use of logic constructs
5
+ please refer to sympy.logic classes And, Or, Not, etc.
6
+ """
7
+ from itertools import combinations, product, zip_longest
8
+ from sympy.assumptions.assume import AppliedPredicate, Predicate
9
+ from sympy.core.relational import Eq, Ne, Gt, Lt, Ge, Le
10
+ from sympy.core.singleton import S
11
+ from sympy.logic.boolalg import Or, And, Not, Xnor
12
+ from sympy.logic.boolalg import (Equivalent, ITE, Implies, Nand, Nor, Xor)
13
+
14
+
15
+ class Literal:
16
+ """
17
+ The smallest element of a CNF object.
18
+
19
+ Parameters
20
+ ==========
21
+
22
+ lit : Boolean expression
23
+
24
+ is_Not : bool
25
+
26
+ Examples
27
+ ========
28
+
29
+ >>> from sympy import Q
30
+ >>> from sympy.assumptions.cnf import Literal
31
+ >>> from sympy.abc import x
32
+ >>> Literal(Q.even(x))
33
+ Literal(Q.even(x), False)
34
+ >>> Literal(~Q.even(x))
35
+ Literal(Q.even(x), True)
36
+ """
37
+
38
+ def __new__(cls, lit, is_Not=False):
39
+ if isinstance(lit, Not):
40
+ lit = lit.args[0]
41
+ is_Not = True
42
+ elif isinstance(lit, (AND, OR, Literal)):
43
+ return ~lit if is_Not else lit
44
+ obj = super().__new__(cls)
45
+ obj.lit = lit
46
+ obj.is_Not = is_Not
47
+ return obj
48
+
49
+ @property
50
+ def arg(self):
51
+ return self.lit
52
+
53
+ def rcall(self, expr):
54
+ if callable(self.lit):
55
+ lit = self.lit(expr)
56
+ else:
57
+ lit = self.lit.apply(expr)
58
+ return type(self)(lit, self.is_Not)
59
+
60
+ def __invert__(self):
61
+ is_Not = not self.is_Not
62
+ return Literal(self.lit, is_Not)
63
+
64
+ def __str__(self):
65
+ return '{}({}, {})'.format(type(self).__name__, self.lit, self.is_Not)
66
+
67
+ __repr__ = __str__
68
+
69
+ def __eq__(self, other):
70
+ return self.arg == other.arg and self.is_Not == other.is_Not
71
+
72
+ def __hash__(self):
73
+ h = hash((type(self).__name__, self.arg, self.is_Not))
74
+ return h
75
+
76
+
77
+ class OR:
78
+ """
79
+ A low-level implementation for Or
80
+ """
81
+ def __init__(self, *args):
82
+ self._args = args
83
+
84
+ @property
85
+ def args(self):
86
+ return sorted(self._args, key=str)
87
+
88
+ def rcall(self, expr):
89
+ return type(self)(*[arg.rcall(expr)
90
+ for arg in self._args
91
+ ])
92
+
93
+ def __invert__(self):
94
+ return AND(*[~arg for arg in self._args])
95
+
96
+ def __hash__(self):
97
+ return hash((type(self).__name__,) + tuple(self.args))
98
+
99
+ def __eq__(self, other):
100
+ return self.args == other.args
101
+
102
+ def __str__(self):
103
+ s = '(' + ' | '.join([str(arg) for arg in self.args]) + ')'
104
+ return s
105
+
106
+ __repr__ = __str__
107
+
108
+
109
+ class AND:
110
+ """
111
+ A low-level implementation for And
112
+ """
113
+ def __init__(self, *args):
114
+ self._args = args
115
+
116
+ def __invert__(self):
117
+ return OR(*[~arg for arg in self._args])
118
+
119
+ @property
120
+ def args(self):
121
+ return sorted(self._args, key=str)
122
+
123
+ def rcall(self, expr):
124
+ return type(self)(*[arg.rcall(expr)
125
+ for arg in self._args
126
+ ])
127
+
128
+ def __hash__(self):
129
+ return hash((type(self).__name__,) + tuple(self.args))
130
+
131
+ def __eq__(self, other):
132
+ return self.args == other.args
133
+
134
+ def __str__(self):
135
+ s = '('+' & '.join([str(arg) for arg in self.args])+')'
136
+ return s
137
+
138
+ __repr__ = __str__
139
+
140
+
141
+ def to_NNF(expr, composite_map=None):
142
+ """
143
+ Generates the Negation Normal Form of any boolean expression in terms
144
+ of AND, OR, and Literal objects.
145
+
146
+ Examples
147
+ ========
148
+
149
+ >>> from sympy import Q, Eq
150
+ >>> from sympy.assumptions.cnf import to_NNF
151
+ >>> from sympy.abc import x, y
152
+ >>> expr = Q.even(x) & ~Q.positive(x)
153
+ >>> to_NNF(expr)
154
+ (Literal(Q.even(x), False) & Literal(Q.positive(x), True))
155
+
156
+ Supported boolean objects are converted to corresponding predicates.
157
+
158
+ >>> to_NNF(Eq(x, y))
159
+ Literal(Q.eq(x, y), False)
160
+
161
+ If ``composite_map`` argument is given, ``to_NNF`` decomposes the
162
+ specified predicate into a combination of primitive predicates.
163
+
164
+ >>> cmap = {Q.nonpositive: Q.negative | Q.zero}
165
+ >>> to_NNF(Q.nonpositive, cmap)
166
+ (Literal(Q.negative, False) | Literal(Q.zero, False))
167
+ >>> to_NNF(Q.nonpositive(x), cmap)
168
+ (Literal(Q.negative(x), False) | Literal(Q.zero(x), False))
169
+ """
170
+ from sympy.assumptions.ask import Q
171
+
172
+ if composite_map is None:
173
+ composite_map = {}
174
+
175
+
176
+ binrelpreds = {Eq: Q.eq, Ne: Q.ne, Gt: Q.gt, Lt: Q.lt, Ge: Q.ge, Le: Q.le}
177
+ if type(expr) in binrelpreds:
178
+ pred = binrelpreds[type(expr)]
179
+ expr = pred(*expr.args)
180
+
181
+ if isinstance(expr, Not):
182
+ arg = expr.args[0]
183
+ tmp = to_NNF(arg, composite_map) # Strategy: negate the NNF of expr
184
+ return ~tmp
185
+
186
+ if isinstance(expr, Or):
187
+ return OR(*[to_NNF(x, composite_map) for x in Or.make_args(expr)])
188
+
189
+ if isinstance(expr, And):
190
+ return AND(*[to_NNF(x, composite_map) for x in And.make_args(expr)])
191
+
192
+ if isinstance(expr, Nand):
193
+ tmp = AND(*[to_NNF(x, composite_map) for x in expr.args])
194
+ return ~tmp
195
+
196
+ if isinstance(expr, Nor):
197
+ tmp = OR(*[to_NNF(x, composite_map) for x in expr.args])
198
+ return ~tmp
199
+
200
+ if isinstance(expr, Xor):
201
+ cnfs = []
202
+ for i in range(0, len(expr.args) + 1, 2):
203
+ for neg in combinations(expr.args, i):
204
+ clause = [~to_NNF(s, composite_map) if s in neg else to_NNF(s, composite_map)
205
+ for s in expr.args]
206
+ cnfs.append(OR(*clause))
207
+ return AND(*cnfs)
208
+
209
+ if isinstance(expr, Xnor):
210
+ cnfs = []
211
+ for i in range(0, len(expr.args) + 1, 2):
212
+ for neg in combinations(expr.args, i):
213
+ clause = [~to_NNF(s, composite_map) if s in neg else to_NNF(s, composite_map)
214
+ for s in expr.args]
215
+ cnfs.append(OR(*clause))
216
+ return ~AND(*cnfs)
217
+
218
+ if isinstance(expr, Implies):
219
+ L, R = to_NNF(expr.args[0], composite_map), to_NNF(expr.args[1], composite_map)
220
+ return OR(~L, R)
221
+
222
+ if isinstance(expr, Equivalent):
223
+ cnfs = []
224
+ for a, b in zip_longest(expr.args, expr.args[1:], fillvalue=expr.args[0]):
225
+ a = to_NNF(a, composite_map)
226
+ b = to_NNF(b, composite_map)
227
+ cnfs.append(OR(~a, b))
228
+ return AND(*cnfs)
229
+
230
+ if isinstance(expr, ITE):
231
+ L = to_NNF(expr.args[0], composite_map)
232
+ M = to_NNF(expr.args[1], composite_map)
233
+ R = to_NNF(expr.args[2], composite_map)
234
+ return AND(OR(~L, M), OR(L, R))
235
+
236
+ if isinstance(expr, AppliedPredicate):
237
+ pred, args = expr.function, expr.arguments
238
+ newpred = composite_map.get(pred, None)
239
+ if newpred is not None:
240
+ return to_NNF(newpred.rcall(*args), composite_map)
241
+
242
+ if isinstance(expr, Predicate):
243
+ newpred = composite_map.get(expr, None)
244
+ if newpred is not None:
245
+ return to_NNF(newpred, composite_map)
246
+
247
+ return Literal(expr)
248
+
249
+
250
+ def distribute_AND_over_OR(expr):
251
+ """
252
+ Distributes AND over OR in the NNF expression.
253
+ Returns the result( Conjunctive Normal Form of expression)
254
+ as a CNF object.
255
+ """
256
+ if not isinstance(expr, (AND, OR)):
257
+ tmp = set()
258
+ tmp.add(frozenset((expr,)))
259
+ return CNF(tmp)
260
+
261
+ if isinstance(expr, OR):
262
+ return CNF.all_or(*[distribute_AND_over_OR(arg)
263
+ for arg in expr._args])
264
+
265
+ if isinstance(expr, AND):
266
+ return CNF.all_and(*[distribute_AND_over_OR(arg)
267
+ for arg in expr._args])
268
+
269
+
270
+ class CNF:
271
+ """
272
+ Class to represent CNF of a Boolean expression.
273
+ Consists of set of clauses, which themselves are stored as
274
+ frozenset of Literal objects.
275
+
276
+ Examples
277
+ ========
278
+
279
+ >>> from sympy import Q
280
+ >>> from sympy.assumptions.cnf import CNF
281
+ >>> from sympy.abc import x
282
+ >>> cnf = CNF.from_prop(Q.real(x) & ~Q.zero(x))
283
+ >>> cnf.clauses
284
+ {frozenset({Literal(Q.zero(x), True)}),
285
+ frozenset({Literal(Q.negative(x), False),
286
+ Literal(Q.positive(x), False), Literal(Q.zero(x), False)})}
287
+ """
288
+ def __init__(self, clauses=None):
289
+ if not clauses:
290
+ clauses = set()
291
+ self.clauses = clauses
292
+
293
+ def add(self, prop):
294
+ clauses = CNF.to_CNF(prop).clauses
295
+ self.add_clauses(clauses)
296
+
297
+ def __str__(self):
298
+ s = ' & '.join(
299
+ ['(' + ' | '.join([str(lit) for lit in clause]) +')'
300
+ for clause in self.clauses]
301
+ )
302
+ return s
303
+
304
+ def extend(self, props):
305
+ for p in props:
306
+ self.add(p)
307
+ return self
308
+
309
+ def copy(self):
310
+ return CNF(set(self.clauses))
311
+
312
+ def add_clauses(self, clauses):
313
+ self.clauses |= clauses
314
+
315
+ @classmethod
316
+ def from_prop(cls, prop):
317
+ res = cls()
318
+ res.add(prop)
319
+ return res
320
+
321
+ def __iand__(self, other):
322
+ self.add_clauses(other.clauses)
323
+ return self
324
+
325
+ def all_predicates(self):
326
+ predicates = set()
327
+ for c in self.clauses:
328
+ predicates |= {arg.lit for arg in c}
329
+ return predicates
330
+
331
+ def _or(self, cnf):
332
+ clauses = set()
333
+ for a, b in product(self.clauses, cnf.clauses):
334
+ tmp = set(a)
335
+ tmp.update(b)
336
+ clauses.add(frozenset(tmp))
337
+ return CNF(clauses)
338
+
339
+ def _and(self, cnf):
340
+ clauses = self.clauses.union(cnf.clauses)
341
+ return CNF(clauses)
342
+
343
+ def _not(self):
344
+ clss = list(self.clauses)
345
+ ll = {frozenset((~x,)) for x in clss[-1]}
346
+ ll = CNF(ll)
347
+
348
+ for rest in clss[:-1]:
349
+ p = {frozenset((~x,)) for x in rest}
350
+ ll = ll._or(CNF(p))
351
+ return ll
352
+
353
+ def rcall(self, expr):
354
+ clause_list = []
355
+ for clause in self.clauses:
356
+ lits = [arg.rcall(expr) for arg in clause]
357
+ clause_list.append(OR(*lits))
358
+ expr = AND(*clause_list)
359
+ return distribute_AND_over_OR(expr)
360
+
361
+ @classmethod
362
+ def all_or(cls, *cnfs):
363
+ b = cnfs[0].copy()
364
+ for rest in cnfs[1:]:
365
+ b = b._or(rest)
366
+ return b
367
+
368
+ @classmethod
369
+ def all_and(cls, *cnfs):
370
+ b = cnfs[0].copy()
371
+ for rest in cnfs[1:]:
372
+ b = b._and(rest)
373
+ return b
374
+
375
+ @classmethod
376
+ def to_CNF(cls, expr):
377
+ from sympy.assumptions.facts import get_composite_predicates
378
+ expr = to_NNF(expr, get_composite_predicates())
379
+ expr = distribute_AND_over_OR(expr)
380
+ return expr
381
+
382
+ @classmethod
383
+ def CNF_to_cnf(cls, cnf):
384
+ """
385
+ Converts CNF object to SymPy's boolean expression
386
+ retaining the form of expression.
387
+ """
388
+ def remove_literal(arg):
389
+ return Not(arg.lit) if arg.is_Not else arg.lit
390
+
391
+ return And(*(Or(*(remove_literal(arg) for arg in clause)) for clause in cnf.clauses))
392
+
393
+
394
+ class EncodedCNF:
395
+ """
396
+ Class for encoding the CNF expression.
397
+ """
398
+ def __init__(self, data=None, encoding=None):
399
+ if not data and not encoding:
400
+ data = []
401
+ encoding = {}
402
+ self.data = data
403
+ self.encoding = encoding
404
+ self._symbols = list(encoding.keys())
405
+
406
+ def from_cnf(self, cnf):
407
+ self._symbols = list(cnf.all_predicates())
408
+ n = len(self._symbols)
409
+ self.encoding = dict(zip(self._symbols, range(1, n + 1)))
410
+ self.data = [self.encode(clause) for clause in cnf.clauses]
411
+
412
+ @property
413
+ def symbols(self):
414
+ return self._symbols
415
+
416
+ @property
417
+ def variables(self):
418
+ return range(1, len(self._symbols) + 1)
419
+
420
+ def copy(self):
421
+ new_data = [set(clause) for clause in self.data]
422
+ return EncodedCNF(new_data, dict(self.encoding))
423
+
424
+ def add_prop(self, prop):
425
+ cnf = CNF.from_prop(prop)
426
+ self.add_from_cnf(cnf)
427
+
428
+ def add_from_cnf(self, cnf):
429
+ clauses = [self.encode(clause) for clause in cnf.clauses]
430
+ self.data += clauses
431
+
432
+ def encode_arg(self, arg):
433
+ literal = arg.lit
434
+ value = self.encoding.get(literal, None)
435
+ if value is None:
436
+ n = len(self._symbols)
437
+ self._symbols.append(literal)
438
+ value = self.encoding[literal] = n + 1
439
+ if arg.is_Not:
440
+ return -value
441
+ else:
442
+ return value
443
+
444
+ def encode(self, clause):
445
+ return {self.encode_arg(arg) if not arg.lit == S.false else 0 for arg in clause}
lib/python3.12/site-packages/sympy/assumptions/facts.py ADDED
@@ -0,0 +1,270 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Known facts in assumptions module.
3
+
4
+ This module defines the facts between unary predicates in ``get_known_facts()``,
5
+ and supports functions to generate the contents in
6
+ ``sympy.assumptions.ask_generated`` file.
7
+ """
8
+
9
+ from sympy.assumptions.ask import Q
10
+ from sympy.assumptions.assume import AppliedPredicate
11
+ from sympy.core.cache import cacheit
12
+ from sympy.core.symbol import Symbol
13
+ from sympy.logic.boolalg import (to_cnf, And, Not, Implies, Equivalent,
14
+ Exclusive,)
15
+ from sympy.logic.inference import satisfiable
16
+
17
+
18
+ @cacheit
19
+ def get_composite_predicates():
20
+ # To reduce the complexity of sat solver, these predicates are
21
+ # transformed into the combination of primitive predicates.
22
+ return {
23
+ Q.real : Q.negative | Q.zero | Q.positive,
24
+ Q.integer : Q.even | Q.odd,
25
+ Q.nonpositive : Q.negative | Q.zero,
26
+ Q.nonzero : Q.negative | Q.positive,
27
+ Q.nonnegative : Q.zero | Q.positive,
28
+ Q.extended_real : Q.negative_infinite | Q.negative | Q.zero | Q.positive | Q.positive_infinite,
29
+ Q.extended_positive: Q.positive | Q.positive_infinite,
30
+ Q.extended_negative: Q.negative | Q.negative_infinite,
31
+ Q.extended_nonzero: Q.negative_infinite | Q.negative | Q.positive | Q.positive_infinite,
32
+ Q.extended_nonpositive: Q.negative_infinite | Q.negative | Q.zero,
33
+ Q.extended_nonnegative: Q.zero | Q.positive | Q.positive_infinite,
34
+ Q.complex : Q.algebraic | Q.transcendental
35
+ }
36
+
37
+
38
+ @cacheit
39
+ def get_known_facts(x=None):
40
+ """
41
+ Facts between unary predicates.
42
+
43
+ Parameters
44
+ ==========
45
+
46
+ x : Symbol, optional
47
+ Placeholder symbol for unary facts. Default is ``Symbol('x')``.
48
+
49
+ Returns
50
+ =======
51
+
52
+ fact : Known facts in conjugated normal form.
53
+
54
+ """
55
+ if x is None:
56
+ x = Symbol('x')
57
+
58
+ fact = And(
59
+ get_number_facts(x),
60
+ get_matrix_facts(x)
61
+ )
62
+ return fact
63
+
64
+
65
+ @cacheit
66
+ def get_number_facts(x = None):
67
+ """
68
+ Facts between unary number predicates.
69
+
70
+ Parameters
71
+ ==========
72
+
73
+ x : Symbol, optional
74
+ Placeholder symbol for unary facts. Default is ``Symbol('x')``.
75
+
76
+ Returns
77
+ =======
78
+
79
+ fact : Known facts in conjugated normal form.
80
+
81
+ """
82
+ if x is None:
83
+ x = Symbol('x')
84
+
85
+ fact = And(
86
+ # primitive predicates for extended real exclude each other.
87
+ Exclusive(Q.negative_infinite(x), Q.negative(x), Q.zero(x),
88
+ Q.positive(x), Q.positive_infinite(x)),
89
+
90
+ # build complex plane
91
+ Exclusive(Q.real(x), Q.imaginary(x)),
92
+ Implies(Q.real(x) | Q.imaginary(x), Q.complex(x)),
93
+
94
+ # other subsets of complex
95
+ Exclusive(Q.transcendental(x), Q.algebraic(x)),
96
+ Equivalent(Q.real(x), Q.rational(x) | Q.irrational(x)),
97
+ Exclusive(Q.irrational(x), Q.rational(x)),
98
+ Implies(Q.rational(x), Q.algebraic(x)),
99
+
100
+ # integers
101
+ Exclusive(Q.even(x), Q.odd(x)),
102
+ Implies(Q.integer(x), Q.rational(x)),
103
+ Implies(Q.zero(x), Q.even(x)),
104
+ Exclusive(Q.composite(x), Q.prime(x)),
105
+ Implies(Q.composite(x) | Q.prime(x), Q.integer(x) & Q.positive(x)),
106
+ Implies(Q.even(x) & Q.positive(x) & ~Q.prime(x), Q.composite(x)),
107
+
108
+ # hermitian and antihermitian
109
+ Implies(Q.real(x), Q.hermitian(x)),
110
+ Implies(Q.imaginary(x), Q.antihermitian(x)),
111
+ Implies(Q.zero(x), Q.hermitian(x) | Q.antihermitian(x)),
112
+
113
+ # define finity and infinity, and build extended real line
114
+ Exclusive(Q.infinite(x), Q.finite(x)),
115
+ Implies(Q.complex(x), Q.finite(x)),
116
+ Implies(Q.negative_infinite(x) | Q.positive_infinite(x), Q.infinite(x)),
117
+
118
+ # commutativity
119
+ Implies(Q.finite(x) | Q.infinite(x), Q.commutative(x)),
120
+ )
121
+ return fact
122
+
123
+
124
+ @cacheit
125
+ def get_matrix_facts(x = None):
126
+ """
127
+ Facts between unary matrix predicates.
128
+
129
+ Parameters
130
+ ==========
131
+
132
+ x : Symbol, optional
133
+ Placeholder symbol for unary facts. Default is ``Symbol('x')``.
134
+
135
+ Returns
136
+ =======
137
+
138
+ fact : Known facts in conjugated normal form.
139
+
140
+ """
141
+ if x is None:
142
+ x = Symbol('x')
143
+
144
+ fact = And(
145
+ # matrices
146
+ Implies(Q.orthogonal(x), Q.positive_definite(x)),
147
+ Implies(Q.orthogonal(x), Q.unitary(x)),
148
+ Implies(Q.unitary(x) & Q.real_elements(x), Q.orthogonal(x)),
149
+ Implies(Q.unitary(x), Q.normal(x)),
150
+ Implies(Q.unitary(x), Q.invertible(x)),
151
+ Implies(Q.normal(x), Q.square(x)),
152
+ Implies(Q.diagonal(x), Q.normal(x)),
153
+ Implies(Q.positive_definite(x), Q.invertible(x)),
154
+ Implies(Q.diagonal(x), Q.upper_triangular(x)),
155
+ Implies(Q.diagonal(x), Q.lower_triangular(x)),
156
+ Implies(Q.lower_triangular(x), Q.triangular(x)),
157
+ Implies(Q.upper_triangular(x), Q.triangular(x)),
158
+ Implies(Q.triangular(x), Q.upper_triangular(x) | Q.lower_triangular(x)),
159
+ Implies(Q.upper_triangular(x) & Q.lower_triangular(x), Q.diagonal(x)),
160
+ Implies(Q.diagonal(x), Q.symmetric(x)),
161
+ Implies(Q.unit_triangular(x), Q.triangular(x)),
162
+ Implies(Q.invertible(x), Q.fullrank(x)),
163
+ Implies(Q.invertible(x), Q.square(x)),
164
+ Implies(Q.symmetric(x), Q.square(x)),
165
+ Implies(Q.fullrank(x) & Q.square(x), Q.invertible(x)),
166
+ Equivalent(Q.invertible(x), ~Q.singular(x)),
167
+ Implies(Q.integer_elements(x), Q.real_elements(x)),
168
+ Implies(Q.real_elements(x), Q.complex_elements(x)),
169
+ )
170
+ return fact
171
+
172
+
173
+
174
+ def generate_known_facts_dict(keys, fact):
175
+ """
176
+ Computes and returns a dictionary which contains the relations between
177
+ unary predicates.
178
+
179
+ Each key is a predicate, and item is two groups of predicates.
180
+ First group contains the predicates which are implied by the key, and
181
+ second group contains the predicates which are rejected by the key.
182
+
183
+ All predicates in *keys* and *fact* must be unary and have same placeholder
184
+ symbol.
185
+
186
+ Parameters
187
+ ==========
188
+
189
+ keys : list of AppliedPredicate instances.
190
+
191
+ fact : Fact between predicates in conjugated normal form.
192
+
193
+ Examples
194
+ ========
195
+
196
+ >>> from sympy import Q, And, Implies
197
+ >>> from sympy.assumptions.facts import generate_known_facts_dict
198
+ >>> from sympy.abc import x
199
+ >>> keys = [Q.even(x), Q.odd(x), Q.zero(x)]
200
+ >>> fact = And(Implies(Q.even(x), ~Q.odd(x)),
201
+ ... Implies(Q.zero(x), Q.even(x)))
202
+ >>> generate_known_facts_dict(keys, fact)
203
+ {Q.even: ({Q.even}, {Q.odd}),
204
+ Q.odd: ({Q.odd}, {Q.even, Q.zero}),
205
+ Q.zero: ({Q.even, Q.zero}, {Q.odd})}
206
+ """
207
+ fact_cnf = to_cnf(fact)
208
+ mapping = single_fact_lookup(keys, fact_cnf)
209
+
210
+ ret = {}
211
+ for key, value in mapping.items():
212
+ implied = set()
213
+ rejected = set()
214
+ for expr in value:
215
+ if isinstance(expr, AppliedPredicate):
216
+ implied.add(expr.function)
217
+ elif isinstance(expr, Not):
218
+ pred = expr.args[0]
219
+ rejected.add(pred.function)
220
+ ret[key.function] = (implied, rejected)
221
+ return ret
222
+
223
+
224
+ @cacheit
225
+ def get_known_facts_keys():
226
+ """
227
+ Return every unary predicates registered to ``Q``.
228
+
229
+ This function is used to generate the keys for
230
+ ``generate_known_facts_dict``.
231
+
232
+ """
233
+ # exclude polyadic predicates
234
+ exclude = {Q.eq, Q.ne, Q.gt, Q.lt, Q.ge, Q.le}
235
+
236
+ result = []
237
+ for attr in Q.__class__.__dict__:
238
+ if attr.startswith('__'):
239
+ continue
240
+ pred = getattr(Q, attr)
241
+ if pred in exclude:
242
+ continue
243
+ result.append(pred)
244
+ return result
245
+
246
+
247
+ def single_fact_lookup(known_facts_keys, known_facts_cnf):
248
+ # Return the dictionary for quick lookup of single fact
249
+ mapping = {}
250
+ for key in known_facts_keys:
251
+ mapping[key] = {key}
252
+ for other_key in known_facts_keys:
253
+ if other_key != key:
254
+ if ask_full_inference(other_key, key, known_facts_cnf):
255
+ mapping[key].add(other_key)
256
+ if ask_full_inference(~other_key, key, known_facts_cnf):
257
+ mapping[key].add(~other_key)
258
+ return mapping
259
+
260
+
261
+ def ask_full_inference(proposition, assumptions, known_facts_cnf):
262
+ """
263
+ Method for inferring properties about objects.
264
+
265
+ """
266
+ if not satisfiable(And(known_facts_cnf, assumptions, proposition)):
267
+ return False
268
+ if not satisfiable(And(known_facts_cnf, assumptions, Not(proposition))):
269
+ return True
270
+ return None
lib/python3.12/site-packages/sympy/assumptions/handlers/calculus.py ADDED
@@ -0,0 +1,258 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ This module contains query handlers responsible for calculus queries:
3
+ infinitesimal, finite, etc.
4
+ """
5
+
6
+ from sympy.assumptions import Q, ask
7
+ from sympy.core import Add, Mul, Pow, Symbol
8
+ from sympy.core.numbers import (NegativeInfinity, GoldenRatio,
9
+ Infinity, Exp1, ComplexInfinity, ImaginaryUnit, NaN, Number, Pi, E,
10
+ TribonacciConstant)
11
+ from sympy.functions import cos, exp, log, sign, sin
12
+ from sympy.logic.boolalg import conjuncts
13
+
14
+ from ..predicates.calculus import (FinitePredicate, InfinitePredicate,
15
+ PositiveInfinitePredicate, NegativeInfinitePredicate)
16
+
17
+
18
+ # FinitePredicate
19
+
20
+
21
+ @FinitePredicate.register(Symbol)
22
+ def _(expr, assumptions):
23
+ """
24
+ Handles Symbol.
25
+ """
26
+ if expr.is_finite is not None:
27
+ return expr.is_finite
28
+ if Q.finite(expr) in conjuncts(assumptions):
29
+ return True
30
+ return None
31
+
32
+ @FinitePredicate.register(Add)
33
+ def _(expr, assumptions):
34
+ """
35
+ Return True if expr is bounded, False if not and None if unknown.
36
+
37
+ Truth Table:
38
+
39
+ +-------+-----+-----------+-----------+
40
+ | | | | |
41
+ | | B | U | ? |
42
+ | | | | |
43
+ +-------+-----+---+---+---+---+---+---+
44
+ | | | | | | | | |
45
+ | | |'+'|'-'|'x'|'+'|'-'|'x'|
46
+ | | | | | | | | |
47
+ +-------+-----+---+---+---+---+---+---+
48
+ | | | | |
49
+ | B | B | U | ? |
50
+ | | | | |
51
+ +---+---+-----+---+---+---+---+---+---+
52
+ | | | | | | | | | |
53
+ | |'+'| | U | ? | ? | U | ? | ? |
54
+ | | | | | | | | | |
55
+ | +---+-----+---+---+---+---+---+---+
56
+ | | | | | | | | | |
57
+ | U |'-'| | ? | U | ? | ? | U | ? |
58
+ | | | | | | | | | |
59
+ | +---+-----+---+---+---+---+---+---+
60
+ | | | | | |
61
+ | |'x'| | ? | ? |
62
+ | | | | | |
63
+ +---+---+-----+---+---+---+---+---+---+
64
+ | | | | |
65
+ | ? | | | ? |
66
+ | | | | |
67
+ +-------+-----+-----------+---+---+---+
68
+
69
+ * 'B' = Bounded
70
+
71
+ * 'U' = Unbounded
72
+
73
+ * '?' = unknown boundedness
74
+
75
+ * '+' = positive sign
76
+
77
+ * '-' = negative sign
78
+
79
+ * 'x' = sign unknown
80
+
81
+ * All Bounded -> True
82
+
83
+ * 1 Unbounded and the rest Bounded -> False
84
+
85
+ * >1 Unbounded, all with same known sign -> False
86
+
87
+ * Any Unknown and unknown sign -> None
88
+
89
+ * Else -> None
90
+
91
+ When the signs are not the same you can have an undefined
92
+ result as in oo - oo, hence 'bounded' is also undefined.
93
+ """
94
+ sign = -1 # sign of unknown or infinite
95
+ result = True
96
+ for arg in expr.args:
97
+ _bounded = ask(Q.finite(arg), assumptions)
98
+ if _bounded:
99
+ continue
100
+ s = ask(Q.extended_positive(arg), assumptions)
101
+ # if there has been more than one sign or if the sign of this arg
102
+ # is None and Bounded is None or there was already
103
+ # an unknown sign, return None
104
+ if sign != -1 and s != sign or \
105
+ s is None and None in (_bounded, sign):
106
+ return None
107
+ else:
108
+ sign = s
109
+ # once False, do not change
110
+ if result is not False:
111
+ result = _bounded
112
+ return result
113
+
114
+ @FinitePredicate.register(Mul)
115
+ def _(expr, assumptions):
116
+ """
117
+ Return True if expr is bounded, False if not and None if unknown.
118
+
119
+ Truth Table:
120
+
121
+ +---+---+---+--------+
122
+ | | | | |
123
+ | | B | U | ? |
124
+ | | | | |
125
+ +---+---+---+---+----+
126
+ | | | | | |
127
+ | | | | s | /s |
128
+ | | | | | |
129
+ +---+---+---+---+----+
130
+ | | | | |
131
+ | B | B | U | ? |
132
+ | | | | |
133
+ +---+---+---+---+----+
134
+ | | | | | |
135
+ | U | | U | U | ? |
136
+ | | | | | |
137
+ +---+---+---+---+----+
138
+ | | | | |
139
+ | ? | | | ? |
140
+ | | | | |
141
+ +---+---+---+---+----+
142
+
143
+ * B = Bounded
144
+
145
+ * U = Unbounded
146
+
147
+ * ? = unknown boundedness
148
+
149
+ * s = signed (hence nonzero)
150
+
151
+ * /s = not signed
152
+ """
153
+ result = True
154
+ for arg in expr.args:
155
+ _bounded = ask(Q.finite(arg), assumptions)
156
+ if _bounded:
157
+ continue
158
+ elif _bounded is None:
159
+ if result is None:
160
+ return None
161
+ if ask(Q.extended_nonzero(arg), assumptions) is None:
162
+ return None
163
+ if result is not False:
164
+ result = None
165
+ else:
166
+ result = False
167
+ return result
168
+
169
+ @FinitePredicate.register(Pow)
170
+ def _(expr, assumptions):
171
+ """
172
+ * Unbounded ** NonZero -> Unbounded
173
+
174
+ * Bounded ** Bounded -> Bounded
175
+
176
+ * Abs()<=1 ** Positive -> Bounded
177
+
178
+ * Abs()>=1 ** Negative -> Bounded
179
+
180
+ * Otherwise unknown
181
+ """
182
+ if expr.base == E:
183
+ return ask(Q.finite(expr.exp), assumptions)
184
+
185
+ base_bounded = ask(Q.finite(expr.base), assumptions)
186
+ exp_bounded = ask(Q.finite(expr.exp), assumptions)
187
+ if base_bounded is None and exp_bounded is None: # Common Case
188
+ return None
189
+ if base_bounded is False and ask(Q.extended_nonzero(expr.exp), assumptions):
190
+ return False
191
+ if base_bounded and exp_bounded:
192
+ return True
193
+ if (abs(expr.base) <= 1) == True and ask(Q.extended_positive(expr.exp), assumptions):
194
+ return True
195
+ if (abs(expr.base) >= 1) == True and ask(Q.extended_negative(expr.exp), assumptions):
196
+ return True
197
+ if (abs(expr.base) >= 1) == True and exp_bounded is False:
198
+ return False
199
+ return None
200
+
201
+ @FinitePredicate.register(exp)
202
+ def _(expr, assumptions):
203
+ return ask(Q.finite(expr.exp), assumptions)
204
+
205
+ @FinitePredicate.register(log)
206
+ def _(expr, assumptions):
207
+ # After complex -> finite fact is registered to new assumption system,
208
+ # querying Q.infinite may be removed.
209
+ if ask(Q.infinite(expr.args[0]), assumptions):
210
+ return False
211
+ return ask(~Q.zero(expr.args[0]), assumptions)
212
+
213
+ @FinitePredicate.register_many(cos, sin, Number, Pi, Exp1, GoldenRatio,
214
+ TribonacciConstant, ImaginaryUnit, sign)
215
+ def _(expr, assumptions):
216
+ return True
217
+
218
+ @FinitePredicate.register_many(ComplexInfinity, Infinity, NegativeInfinity)
219
+ def _(expr, assumptions):
220
+ return False
221
+
222
+ @FinitePredicate.register(NaN)
223
+ def _(expr, assumptions):
224
+ return None
225
+
226
+
227
+ # InfinitePredicate
228
+
229
+
230
+ @InfinitePredicate.register_many(ComplexInfinity, Infinity, NegativeInfinity)
231
+ def _(expr, assumptions):
232
+ return True
233
+
234
+
235
+ # PositiveInfinitePredicate
236
+
237
+
238
+ @PositiveInfinitePredicate.register(Infinity)
239
+ def _(expr, assumptions):
240
+ return True
241
+
242
+
243
+ @PositiveInfinitePredicate.register_many(NegativeInfinity, ComplexInfinity)
244
+ def _(expr, assumptions):
245
+ return False
246
+
247
+
248
+ # NegativeInfinitePredicate
249
+
250
+
251
+ @NegativeInfinitePredicate.register(NegativeInfinity)
252
+ def _(expr, assumptions):
253
+ return True
254
+
255
+
256
+ @NegativeInfinitePredicate.register_many(Infinity, ComplexInfinity)
257
+ def _(expr, assumptions):
258
+ return False
lib/python3.12/site-packages/sympy/assumptions/handlers/matrices.py ADDED
@@ -0,0 +1,716 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ This module contains query handlers responsible for Matrices queries:
3
+ Square, Symmetric, Invertible etc.
4
+ """
5
+
6
+ from sympy.logic.boolalg import conjuncts
7
+ from sympy.assumptions import Q, ask
8
+ from sympy.assumptions.handlers import test_closed_group
9
+ from sympy.matrices import MatrixBase
10
+ from sympy.matrices.expressions import (BlockMatrix, BlockDiagMatrix, Determinant,
11
+ DiagMatrix, DiagonalMatrix, HadamardProduct, Identity, Inverse, MatAdd, MatMul,
12
+ MatPow, MatrixExpr, MatrixSlice, MatrixSymbol, OneMatrix, Trace, Transpose,
13
+ ZeroMatrix)
14
+ from sympy.matrices.expressions.blockmatrix import reblock_2x2
15
+ from sympy.matrices.expressions.factorizations import Factorization
16
+ from sympy.matrices.expressions.fourier import DFT
17
+ from sympy.core.logic import fuzzy_and
18
+ from sympy.utilities.iterables import sift
19
+ from sympy.core import Basic
20
+
21
+ from ..predicates.matrices import (SquarePredicate, SymmetricPredicate,
22
+ InvertiblePredicate, OrthogonalPredicate, UnitaryPredicate,
23
+ FullRankPredicate, PositiveDefinitePredicate, UpperTriangularPredicate,
24
+ LowerTriangularPredicate, DiagonalPredicate, IntegerElementsPredicate,
25
+ RealElementsPredicate, ComplexElementsPredicate)
26
+
27
+
28
+ def _Factorization(predicate, expr, assumptions):
29
+ if predicate in expr.predicates:
30
+ return True
31
+
32
+
33
+ # SquarePredicate
34
+
35
+ @SquarePredicate.register(MatrixExpr)
36
+ def _(expr, assumptions):
37
+ return expr.shape[0] == expr.shape[1]
38
+
39
+
40
+ # SymmetricPredicate
41
+
42
+ @SymmetricPredicate.register(MatMul)
43
+ def _(expr, assumptions):
44
+ factor, mmul = expr.as_coeff_mmul()
45
+ if all(ask(Q.symmetric(arg), assumptions) for arg in mmul.args):
46
+ return True
47
+ # TODO: implement sathandlers system for the matrices.
48
+ # Now it duplicates the general fact: Implies(Q.diagonal, Q.symmetric).
49
+ if ask(Q.diagonal(expr), assumptions):
50
+ return True
51
+ if len(mmul.args) >= 2 and mmul.args[0] == mmul.args[-1].T:
52
+ if len(mmul.args) == 2:
53
+ return True
54
+ return ask(Q.symmetric(MatMul(*mmul.args[1:-1])), assumptions)
55
+
56
+ @SymmetricPredicate.register(MatPow)
57
+ def _(expr, assumptions):
58
+ # only for integer powers
59
+ base, exp = expr.args
60
+ int_exp = ask(Q.integer(exp), assumptions)
61
+ if not int_exp:
62
+ return None
63
+ non_negative = ask(~Q.negative(exp), assumptions)
64
+ if (non_negative or non_negative == False
65
+ and ask(Q.invertible(base), assumptions)):
66
+ return ask(Q.symmetric(base), assumptions)
67
+ return None
68
+
69
+ @SymmetricPredicate.register(MatAdd)
70
+ def _(expr, assumptions):
71
+ return all(ask(Q.symmetric(arg), assumptions) for arg in expr.args)
72
+
73
+ @SymmetricPredicate.register(MatrixSymbol)
74
+ def _(expr, assumptions):
75
+ if not expr.is_square:
76
+ return False
77
+ # TODO: implement sathandlers system for the matrices.
78
+ # Now it duplicates the general fact: Implies(Q.diagonal, Q.symmetric).
79
+ if ask(Q.diagonal(expr), assumptions):
80
+ return True
81
+ if Q.symmetric(expr) in conjuncts(assumptions):
82
+ return True
83
+
84
+ @SymmetricPredicate.register_many(OneMatrix, ZeroMatrix)
85
+ def _(expr, assumptions):
86
+ return ask(Q.square(expr), assumptions)
87
+
88
+ @SymmetricPredicate.register_many(Inverse, Transpose)
89
+ def _(expr, assumptions):
90
+ return ask(Q.symmetric(expr.arg), assumptions)
91
+
92
+ @SymmetricPredicate.register(MatrixSlice)
93
+ def _(expr, assumptions):
94
+ # TODO: implement sathandlers system for the matrices.
95
+ # Now it duplicates the general fact: Implies(Q.diagonal, Q.symmetric).
96
+ if ask(Q.diagonal(expr), assumptions):
97
+ return True
98
+ if not expr.on_diag:
99
+ return None
100
+ else:
101
+ return ask(Q.symmetric(expr.parent), assumptions)
102
+
103
+ @SymmetricPredicate.register(Identity)
104
+ def _(expr, assumptions):
105
+ return True
106
+
107
+
108
+ # InvertiblePredicate
109
+
110
+ @InvertiblePredicate.register(MatMul)
111
+ def _(expr, assumptions):
112
+ factor, mmul = expr.as_coeff_mmul()
113
+ if all(ask(Q.invertible(arg), assumptions) for arg in mmul.args):
114
+ return True
115
+ if any(ask(Q.invertible(arg), assumptions) is False
116
+ for arg in mmul.args):
117
+ return False
118
+
119
+ @InvertiblePredicate.register(MatPow)
120
+ def _(expr, assumptions):
121
+ # only for integer powers
122
+ base, exp = expr.args
123
+ int_exp = ask(Q.integer(exp), assumptions)
124
+ if not int_exp:
125
+ return None
126
+ if exp.is_negative == False:
127
+ return ask(Q.invertible(base), assumptions)
128
+ return None
129
+
130
+ @InvertiblePredicate.register(MatAdd)
131
+ def _(expr, assumptions):
132
+ return None
133
+
134
+ @InvertiblePredicate.register(MatrixSymbol)
135
+ def _(expr, assumptions):
136
+ if not expr.is_square:
137
+ return False
138
+ if Q.invertible(expr) in conjuncts(assumptions):
139
+ return True
140
+
141
+ @InvertiblePredicate.register_many(Identity, Inverse)
142
+ def _(expr, assumptions):
143
+ return True
144
+
145
+ @InvertiblePredicate.register(ZeroMatrix)
146
+ def _(expr, assumptions):
147
+ return False
148
+
149
+ @InvertiblePredicate.register(OneMatrix)
150
+ def _(expr, assumptions):
151
+ return expr.shape[0] == 1 and expr.shape[1] == 1
152
+
153
+ @InvertiblePredicate.register(Transpose)
154
+ def _(expr, assumptions):
155
+ return ask(Q.invertible(expr.arg), assumptions)
156
+
157
+ @InvertiblePredicate.register(MatrixSlice)
158
+ def _(expr, assumptions):
159
+ if not expr.on_diag:
160
+ return None
161
+ else:
162
+ return ask(Q.invertible(expr.parent), assumptions)
163
+
164
+ @InvertiblePredicate.register(MatrixBase)
165
+ def _(expr, assumptions):
166
+ if not expr.is_square:
167
+ return False
168
+ return expr.rank() == expr.rows
169
+
170
+ @InvertiblePredicate.register(MatrixExpr)
171
+ def _(expr, assumptions):
172
+ if not expr.is_square:
173
+ return False
174
+ return None
175
+
176
+ @InvertiblePredicate.register(BlockMatrix)
177
+ def _(expr, assumptions):
178
+ if not expr.is_square:
179
+ return False
180
+ if expr.blockshape == (1, 1):
181
+ return ask(Q.invertible(expr.blocks[0, 0]), assumptions)
182
+ expr = reblock_2x2(expr)
183
+ if expr.blockshape == (2, 2):
184
+ [[A, B], [C, D]] = expr.blocks.tolist()
185
+ if ask(Q.invertible(A), assumptions) == True:
186
+ invertible = ask(Q.invertible(D - C * A.I * B), assumptions)
187
+ if invertible is not None:
188
+ return invertible
189
+ if ask(Q.invertible(B), assumptions) == True:
190
+ invertible = ask(Q.invertible(C - D * B.I * A), assumptions)
191
+ if invertible is not None:
192
+ return invertible
193
+ if ask(Q.invertible(C), assumptions) == True:
194
+ invertible = ask(Q.invertible(B - A * C.I * D), assumptions)
195
+ if invertible is not None:
196
+ return invertible
197
+ if ask(Q.invertible(D), assumptions) == True:
198
+ invertible = ask(Q.invertible(A - B * D.I * C), assumptions)
199
+ if invertible is not None:
200
+ return invertible
201
+ return None
202
+
203
+ @InvertiblePredicate.register(BlockDiagMatrix)
204
+ def _(expr, assumptions):
205
+ if expr.rowblocksizes != expr.colblocksizes:
206
+ return None
207
+ return fuzzy_and([ask(Q.invertible(a), assumptions) for a in expr.diag])
208
+
209
+
210
+ # OrthogonalPredicate
211
+
212
+ @OrthogonalPredicate.register(MatMul)
213
+ def _(expr, assumptions):
214
+ factor, mmul = expr.as_coeff_mmul()
215
+ if (all(ask(Q.orthogonal(arg), assumptions) for arg in mmul.args) and
216
+ factor == 1):
217
+ return True
218
+ if any(ask(Q.invertible(arg), assumptions) is False
219
+ for arg in mmul.args):
220
+ return False
221
+
222
+ @OrthogonalPredicate.register(MatPow)
223
+ def _(expr, assumptions):
224
+ # only for integer powers
225
+ base, exp = expr.args
226
+ int_exp = ask(Q.integer(exp), assumptions)
227
+ if int_exp:
228
+ return ask(Q.orthogonal(base), assumptions)
229
+ return None
230
+
231
+ @OrthogonalPredicate.register(MatAdd)
232
+ def _(expr, assumptions):
233
+ if (len(expr.args) == 1 and
234
+ ask(Q.orthogonal(expr.args[0]), assumptions)):
235
+ return True
236
+
237
+ @OrthogonalPredicate.register(MatrixSymbol)
238
+ def _(expr, assumptions):
239
+ if (not expr.is_square or
240
+ ask(Q.invertible(expr), assumptions) is False):
241
+ return False
242
+ if Q.orthogonal(expr) in conjuncts(assumptions):
243
+ return True
244
+
245
+ @OrthogonalPredicate.register(Identity)
246
+ def _(expr, assumptions):
247
+ return True
248
+
249
+ @OrthogonalPredicate.register(ZeroMatrix)
250
+ def _(expr, assumptions):
251
+ return False
252
+
253
+ @OrthogonalPredicate.register_many(Inverse, Transpose)
254
+ def _(expr, assumptions):
255
+ return ask(Q.orthogonal(expr.arg), assumptions)
256
+
257
+ @OrthogonalPredicate.register(MatrixSlice)
258
+ def _(expr, assumptions):
259
+ if not expr.on_diag:
260
+ return None
261
+ else:
262
+ return ask(Q.orthogonal(expr.parent), assumptions)
263
+
264
+ @OrthogonalPredicate.register(Factorization)
265
+ def _(expr, assumptions):
266
+ return _Factorization(Q.orthogonal, expr, assumptions)
267
+
268
+
269
+ # UnitaryPredicate
270
+
271
+ @UnitaryPredicate.register(MatMul)
272
+ def _(expr, assumptions):
273
+ factor, mmul = expr.as_coeff_mmul()
274
+ if (all(ask(Q.unitary(arg), assumptions) for arg in mmul.args) and
275
+ abs(factor) == 1):
276
+ return True
277
+ if any(ask(Q.invertible(arg), assumptions) is False
278
+ for arg in mmul.args):
279
+ return False
280
+
281
+ @UnitaryPredicate.register(MatPow)
282
+ def _(expr, assumptions):
283
+ # only for integer powers
284
+ base, exp = expr.args
285
+ int_exp = ask(Q.integer(exp), assumptions)
286
+ if int_exp:
287
+ return ask(Q.unitary(base), assumptions)
288
+ return None
289
+
290
+ @UnitaryPredicate.register(MatrixSymbol)
291
+ def _(expr, assumptions):
292
+ if (not expr.is_square or
293
+ ask(Q.invertible(expr), assumptions) is False):
294
+ return False
295
+ if Q.unitary(expr) in conjuncts(assumptions):
296
+ return True
297
+
298
+ @UnitaryPredicate.register_many(Inverse, Transpose)
299
+ def _(expr, assumptions):
300
+ return ask(Q.unitary(expr.arg), assumptions)
301
+
302
+ @UnitaryPredicate.register(MatrixSlice)
303
+ def _(expr, assumptions):
304
+ if not expr.on_diag:
305
+ return None
306
+ else:
307
+ return ask(Q.unitary(expr.parent), assumptions)
308
+
309
+ @UnitaryPredicate.register_many(DFT, Identity)
310
+ def _(expr, assumptions):
311
+ return True
312
+
313
+ @UnitaryPredicate.register(ZeroMatrix)
314
+ def _(expr, assumptions):
315
+ return False
316
+
317
+ @UnitaryPredicate.register(Factorization)
318
+ def _(expr, assumptions):
319
+ return _Factorization(Q.unitary, expr, assumptions)
320
+
321
+
322
+ # FullRankPredicate
323
+
324
+ @FullRankPredicate.register(MatMul)
325
+ def _(expr, assumptions):
326
+ if all(ask(Q.fullrank(arg), assumptions) for arg in expr.args):
327
+ return True
328
+
329
+ @FullRankPredicate.register(MatPow)
330
+ def _(expr, assumptions):
331
+ # only for integer powers
332
+ base, exp = expr.args
333
+ int_exp = ask(Q.integer(exp), assumptions)
334
+ if int_exp and ask(~Q.negative(exp), assumptions):
335
+ return ask(Q.fullrank(base), assumptions)
336
+ return None
337
+
338
+ @FullRankPredicate.register(Identity)
339
+ def _(expr, assumptions):
340
+ return True
341
+
342
+ @FullRankPredicate.register(ZeroMatrix)
343
+ def _(expr, assumptions):
344
+ return False
345
+
346
+ @FullRankPredicate.register(OneMatrix)
347
+ def _(expr, assumptions):
348
+ return expr.shape[0] == 1 and expr.shape[1] == 1
349
+
350
+ @FullRankPredicate.register_many(Inverse, Transpose)
351
+ def _(expr, assumptions):
352
+ return ask(Q.fullrank(expr.arg), assumptions)
353
+
354
+ @FullRankPredicate.register(MatrixSlice)
355
+ def _(expr, assumptions):
356
+ if ask(Q.orthogonal(expr.parent), assumptions):
357
+ return True
358
+
359
+
360
+ # PositiveDefinitePredicate
361
+
362
+ @PositiveDefinitePredicate.register(MatMul)
363
+ def _(expr, assumptions):
364
+ factor, mmul = expr.as_coeff_mmul()
365
+ if (all(ask(Q.positive_definite(arg), assumptions)
366
+ for arg in mmul.args) and factor > 0):
367
+ return True
368
+ if (len(mmul.args) >= 2
369
+ and mmul.args[0] == mmul.args[-1].T
370
+ and ask(Q.fullrank(mmul.args[0]), assumptions)):
371
+ return ask(Q.positive_definite(
372
+ MatMul(*mmul.args[1:-1])), assumptions)
373
+
374
+ @PositiveDefinitePredicate.register(MatPow)
375
+ def _(expr, assumptions):
376
+ # a power of a positive definite matrix is positive definite
377
+ if ask(Q.positive_definite(expr.args[0]), assumptions):
378
+ return True
379
+
380
+ @PositiveDefinitePredicate.register(MatAdd)
381
+ def _(expr, assumptions):
382
+ if all(ask(Q.positive_definite(arg), assumptions)
383
+ for arg in expr.args):
384
+ return True
385
+
386
+ @PositiveDefinitePredicate.register(MatrixSymbol)
387
+ def _(expr, assumptions):
388
+ if not expr.is_square:
389
+ return False
390
+ if Q.positive_definite(expr) in conjuncts(assumptions):
391
+ return True
392
+
393
+ @PositiveDefinitePredicate.register(Identity)
394
+ def _(expr, assumptions):
395
+ return True
396
+
397
+ @PositiveDefinitePredicate.register(ZeroMatrix)
398
+ def _(expr, assumptions):
399
+ return False
400
+
401
+ @PositiveDefinitePredicate.register(OneMatrix)
402
+ def _(expr, assumptions):
403
+ return expr.shape[0] == 1 and expr.shape[1] == 1
404
+
405
+ @PositiveDefinitePredicate.register_many(Inverse, Transpose)
406
+ def _(expr, assumptions):
407
+ return ask(Q.positive_definite(expr.arg), assumptions)
408
+
409
+ @PositiveDefinitePredicate.register(MatrixSlice)
410
+ def _(expr, assumptions):
411
+ if not expr.on_diag:
412
+ return None
413
+ else:
414
+ return ask(Q.positive_definite(expr.parent), assumptions)
415
+
416
+
417
+ # UpperTriangularPredicate
418
+
419
+ @UpperTriangularPredicate.register(MatMul)
420
+ def _(expr, assumptions):
421
+ factor, matrices = expr.as_coeff_matrices()
422
+ if all(ask(Q.upper_triangular(m), assumptions) for m in matrices):
423
+ return True
424
+
425
+ @UpperTriangularPredicate.register(MatAdd)
426
+ def _(expr, assumptions):
427
+ if all(ask(Q.upper_triangular(arg), assumptions) for arg in expr.args):
428
+ return True
429
+
430
+ @UpperTriangularPredicate.register(MatPow)
431
+ def _(expr, assumptions):
432
+ # only for integer powers
433
+ base, exp = expr.args
434
+ int_exp = ask(Q.integer(exp), assumptions)
435
+ if not int_exp:
436
+ return None
437
+ non_negative = ask(~Q.negative(exp), assumptions)
438
+ if (non_negative or non_negative == False
439
+ and ask(Q.invertible(base), assumptions)):
440
+ return ask(Q.upper_triangular(base), assumptions)
441
+ return None
442
+
443
+ @UpperTriangularPredicate.register(MatrixSymbol)
444
+ def _(expr, assumptions):
445
+ if Q.upper_triangular(expr) in conjuncts(assumptions):
446
+ return True
447
+
448
+ @UpperTriangularPredicate.register_many(Identity, ZeroMatrix)
449
+ def _(expr, assumptions):
450
+ return True
451
+
452
+ @UpperTriangularPredicate.register(OneMatrix)
453
+ def _(expr, assumptions):
454
+ return expr.shape[0] == 1 and expr.shape[1] == 1
455
+
456
+ @UpperTriangularPredicate.register(Transpose)
457
+ def _(expr, assumptions):
458
+ return ask(Q.lower_triangular(expr.arg), assumptions)
459
+
460
+ @UpperTriangularPredicate.register(Inverse)
461
+ def _(expr, assumptions):
462
+ return ask(Q.upper_triangular(expr.arg), assumptions)
463
+
464
+ @UpperTriangularPredicate.register(MatrixSlice)
465
+ def _(expr, assumptions):
466
+ if not expr.on_diag:
467
+ return None
468
+ else:
469
+ return ask(Q.upper_triangular(expr.parent), assumptions)
470
+
471
+ @UpperTriangularPredicate.register(Factorization)
472
+ def _(expr, assumptions):
473
+ return _Factorization(Q.upper_triangular, expr, assumptions)
474
+
475
+ # LowerTriangularPredicate
476
+
477
+ @LowerTriangularPredicate.register(MatMul)
478
+ def _(expr, assumptions):
479
+ factor, matrices = expr.as_coeff_matrices()
480
+ if all(ask(Q.lower_triangular(m), assumptions) for m in matrices):
481
+ return True
482
+
483
+ @LowerTriangularPredicate.register(MatAdd)
484
+ def _(expr, assumptions):
485
+ if all(ask(Q.lower_triangular(arg), assumptions) for arg in expr.args):
486
+ return True
487
+
488
+ @LowerTriangularPredicate.register(MatPow)
489
+ def _(expr, assumptions):
490
+ # only for integer powers
491
+ base, exp = expr.args
492
+ int_exp = ask(Q.integer(exp), assumptions)
493
+ if not int_exp:
494
+ return None
495
+ non_negative = ask(~Q.negative(exp), assumptions)
496
+ if (non_negative or non_negative == False
497
+ and ask(Q.invertible(base), assumptions)):
498
+ return ask(Q.lower_triangular(base), assumptions)
499
+ return None
500
+
501
+ @LowerTriangularPredicate.register(MatrixSymbol)
502
+ def _(expr, assumptions):
503
+ if Q.lower_triangular(expr) in conjuncts(assumptions):
504
+ return True
505
+
506
+ @LowerTriangularPredicate.register_many(Identity, ZeroMatrix)
507
+ def _(expr, assumptions):
508
+ return True
509
+
510
+ @LowerTriangularPredicate.register(OneMatrix)
511
+ def _(expr, assumptions):
512
+ return expr.shape[0] == 1 and expr.shape[1] == 1
513
+
514
+ @LowerTriangularPredicate.register(Transpose)
515
+ def _(expr, assumptions):
516
+ return ask(Q.upper_triangular(expr.arg), assumptions)
517
+
518
+ @LowerTriangularPredicate.register(Inverse)
519
+ def _(expr, assumptions):
520
+ return ask(Q.lower_triangular(expr.arg), assumptions)
521
+
522
+ @LowerTriangularPredicate.register(MatrixSlice)
523
+ def _(expr, assumptions):
524
+ if not expr.on_diag:
525
+ return None
526
+ else:
527
+ return ask(Q.lower_triangular(expr.parent), assumptions)
528
+
529
+ @LowerTriangularPredicate.register(Factorization)
530
+ def _(expr, assumptions):
531
+ return _Factorization(Q.lower_triangular, expr, assumptions)
532
+
533
+
534
+ # DiagonalPredicate
535
+
536
+ def _is_empty_or_1x1(expr):
537
+ return expr.shape in ((0, 0), (1, 1))
538
+
539
+ @DiagonalPredicate.register(MatMul)
540
+ def _(expr, assumptions):
541
+ if _is_empty_or_1x1(expr):
542
+ return True
543
+ factor, matrices = expr.as_coeff_matrices()
544
+ if all(ask(Q.diagonal(m), assumptions) for m in matrices):
545
+ return True
546
+
547
+ @DiagonalPredicate.register(MatPow)
548
+ def _(expr, assumptions):
549
+ # only for integer powers
550
+ base, exp = expr.args
551
+ int_exp = ask(Q.integer(exp), assumptions)
552
+ if not int_exp:
553
+ return None
554
+ non_negative = ask(~Q.negative(exp), assumptions)
555
+ if (non_negative or non_negative == False
556
+ and ask(Q.invertible(base), assumptions)):
557
+ return ask(Q.diagonal(base), assumptions)
558
+ return None
559
+
560
+ @DiagonalPredicate.register(MatAdd)
561
+ def _(expr, assumptions):
562
+ if all(ask(Q.diagonal(arg), assumptions) for arg in expr.args):
563
+ return True
564
+
565
+ @DiagonalPredicate.register(MatrixSymbol)
566
+ def _(expr, assumptions):
567
+ if _is_empty_or_1x1(expr):
568
+ return True
569
+ if Q.diagonal(expr) in conjuncts(assumptions):
570
+ return True
571
+
572
+ @DiagonalPredicate.register(OneMatrix)
573
+ def _(expr, assumptions):
574
+ return expr.shape[0] == 1 and expr.shape[1] == 1
575
+
576
+ @DiagonalPredicate.register_many(Inverse, Transpose)
577
+ def _(expr, assumptions):
578
+ return ask(Q.diagonal(expr.arg), assumptions)
579
+
580
+ @DiagonalPredicate.register(MatrixSlice)
581
+ def _(expr, assumptions):
582
+ if _is_empty_or_1x1(expr):
583
+ return True
584
+ if not expr.on_diag:
585
+ return None
586
+ else:
587
+ return ask(Q.diagonal(expr.parent), assumptions)
588
+
589
+ @DiagonalPredicate.register_many(DiagonalMatrix, DiagMatrix, Identity, ZeroMatrix)
590
+ def _(expr, assumptions):
591
+ return True
592
+
593
+ @DiagonalPredicate.register(Factorization)
594
+ def _(expr, assumptions):
595
+ return _Factorization(Q.diagonal, expr, assumptions)
596
+
597
+
598
+ # IntegerElementsPredicate
599
+
600
+ def BM_elements(predicate, expr, assumptions):
601
+ """ Block Matrix elements. """
602
+ return all(ask(predicate(b), assumptions) for b in expr.blocks)
603
+
604
+ def MS_elements(predicate, expr, assumptions):
605
+ """ Matrix Slice elements. """
606
+ return ask(predicate(expr.parent), assumptions)
607
+
608
+ def MatMul_elements(matrix_predicate, scalar_predicate, expr, assumptions):
609
+ d = sift(expr.args, lambda x: isinstance(x, MatrixExpr))
610
+ factors, matrices = d[False], d[True]
611
+ return fuzzy_and([
612
+ test_closed_group(Basic(*factors), assumptions, scalar_predicate),
613
+ test_closed_group(Basic(*matrices), assumptions, matrix_predicate)])
614
+
615
+
616
+ @IntegerElementsPredicate.register_many(Determinant, HadamardProduct, MatAdd,
617
+ Trace, Transpose)
618
+ def _(expr, assumptions):
619
+ return test_closed_group(expr, assumptions, Q.integer_elements)
620
+
621
+ @IntegerElementsPredicate.register(MatPow)
622
+ def _(expr, assumptions):
623
+ # only for integer powers
624
+ base, exp = expr.args
625
+ int_exp = ask(Q.integer(exp), assumptions)
626
+ if not int_exp:
627
+ return None
628
+ if exp.is_negative == False:
629
+ return ask(Q.integer_elements(base), assumptions)
630
+ return None
631
+
632
+ @IntegerElementsPredicate.register_many(Identity, OneMatrix, ZeroMatrix)
633
+ def _(expr, assumptions):
634
+ return True
635
+
636
+ @IntegerElementsPredicate.register(MatMul)
637
+ def _(expr, assumptions):
638
+ return MatMul_elements(Q.integer_elements, Q.integer, expr, assumptions)
639
+
640
+ @IntegerElementsPredicate.register(MatrixSlice)
641
+ def _(expr, assumptions):
642
+ return MS_elements(Q.integer_elements, expr, assumptions)
643
+
644
+ @IntegerElementsPredicate.register(BlockMatrix)
645
+ def _(expr, assumptions):
646
+ return BM_elements(Q.integer_elements, expr, assumptions)
647
+
648
+
649
+ # RealElementsPredicate
650
+
651
+ @RealElementsPredicate.register_many(Determinant, Factorization, HadamardProduct,
652
+ MatAdd, Trace, Transpose)
653
+ def _(expr, assumptions):
654
+ return test_closed_group(expr, assumptions, Q.real_elements)
655
+
656
+ @RealElementsPredicate.register(MatPow)
657
+ def _(expr, assumptions):
658
+ # only for integer powers
659
+ base, exp = expr.args
660
+ int_exp = ask(Q.integer(exp), assumptions)
661
+ if not int_exp:
662
+ return None
663
+ non_negative = ask(~Q.negative(exp), assumptions)
664
+ if (non_negative or non_negative == False
665
+ and ask(Q.invertible(base), assumptions)):
666
+ return ask(Q.real_elements(base), assumptions)
667
+ return None
668
+
669
+ @RealElementsPredicate.register(MatMul)
670
+ def _(expr, assumptions):
671
+ return MatMul_elements(Q.real_elements, Q.real, expr, assumptions)
672
+
673
+ @RealElementsPredicate.register(MatrixSlice)
674
+ def _(expr, assumptions):
675
+ return MS_elements(Q.real_elements, expr, assumptions)
676
+
677
+ @RealElementsPredicate.register(BlockMatrix)
678
+ def _(expr, assumptions):
679
+ return BM_elements(Q.real_elements, expr, assumptions)
680
+
681
+
682
+ # ComplexElementsPredicate
683
+
684
+ @ComplexElementsPredicate.register_many(Determinant, Factorization, HadamardProduct,
685
+ Inverse, MatAdd, Trace, Transpose)
686
+ def _(expr, assumptions):
687
+ return test_closed_group(expr, assumptions, Q.complex_elements)
688
+
689
+ @ComplexElementsPredicate.register(MatPow)
690
+ def _(expr, assumptions):
691
+ # only for integer powers
692
+ base, exp = expr.args
693
+ int_exp = ask(Q.integer(exp), assumptions)
694
+ if not int_exp:
695
+ return None
696
+ non_negative = ask(~Q.negative(exp), assumptions)
697
+ if (non_negative or non_negative == False
698
+ and ask(Q.invertible(base), assumptions)):
699
+ return ask(Q.complex_elements(base), assumptions)
700
+ return None
701
+
702
+ @ComplexElementsPredicate.register(MatMul)
703
+ def _(expr, assumptions):
704
+ return MatMul_elements(Q.complex_elements, Q.complex, expr, assumptions)
705
+
706
+ @ComplexElementsPredicate.register(MatrixSlice)
707
+ def _(expr, assumptions):
708
+ return MS_elements(Q.complex_elements, expr, assumptions)
709
+
710
+ @ComplexElementsPredicate.register(BlockMatrix)
711
+ def _(expr, assumptions):
712
+ return BM_elements(Q.complex_elements, expr, assumptions)
713
+
714
+ @ComplexElementsPredicate.register(DFT)
715
+ def _(expr, assumptions):
716
+ return True
lib/python3.12/site-packages/sympy/assumptions/handlers/ntheory.py ADDED
@@ -0,0 +1,269 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Handlers for keys related to number theory: prime, even, odd, etc.
3
+ """
4
+
5
+ from sympy.assumptions import Q, ask
6
+ from sympy.core import Add, Basic, Expr, Float, Mul, Pow, S
7
+ from sympy.core.numbers import (ImaginaryUnit, Infinity, Integer, NaN,
8
+ NegativeInfinity, NumberSymbol, Rational, int_valued)
9
+ from sympy.functions import Abs, im, re
10
+ from sympy.ntheory import isprime
11
+
12
+ from sympy.multipledispatch import MDNotImplementedError
13
+
14
+ from ..predicates.ntheory import (PrimePredicate, CompositePredicate,
15
+ EvenPredicate, OddPredicate)
16
+
17
+
18
+ # PrimePredicate
19
+
20
+ def _PrimePredicate_number(expr, assumptions):
21
+ # helper method
22
+ exact = not expr.atoms(Float)
23
+ try:
24
+ i = int(expr.round())
25
+ if (expr - i).equals(0) is False:
26
+ raise TypeError
27
+ except TypeError:
28
+ return False
29
+ if exact:
30
+ return isprime(i)
31
+ # when not exact, we won't give a True or False
32
+ # since the number represents an approximate value
33
+
34
+ @PrimePredicate.register(Expr)
35
+ def _(expr, assumptions):
36
+ ret = expr.is_prime
37
+ if ret is None:
38
+ raise MDNotImplementedError
39
+ return ret
40
+
41
+ @PrimePredicate.register(Basic)
42
+ def _(expr, assumptions):
43
+ if expr.is_number:
44
+ return _PrimePredicate_number(expr, assumptions)
45
+
46
+ @PrimePredicate.register(Mul)
47
+ def _(expr, assumptions):
48
+ if expr.is_number:
49
+ return _PrimePredicate_number(expr, assumptions)
50
+ for arg in expr.args:
51
+ if not ask(Q.integer(arg), assumptions):
52
+ return None
53
+ for arg in expr.args:
54
+ if arg.is_number and arg.is_composite:
55
+ return False
56
+
57
+ @PrimePredicate.register(Pow)
58
+ def _(expr, assumptions):
59
+ """
60
+ Integer**Integer -> !Prime
61
+ """
62
+ if expr.is_number:
63
+ return _PrimePredicate_number(expr, assumptions)
64
+ if ask(Q.integer(expr.exp), assumptions) and \
65
+ ask(Q.integer(expr.base), assumptions):
66
+ return False
67
+
68
+ @PrimePredicate.register(Integer)
69
+ def _(expr, assumptions):
70
+ return isprime(expr)
71
+
72
+ @PrimePredicate.register_many(Rational, Infinity, NegativeInfinity, ImaginaryUnit)
73
+ def _(expr, assumptions):
74
+ return False
75
+
76
+ @PrimePredicate.register(Float)
77
+ def _(expr, assumptions):
78
+ return _PrimePredicate_number(expr, assumptions)
79
+
80
+ @PrimePredicate.register(NumberSymbol)
81
+ def _(expr, assumptions):
82
+ return _PrimePredicate_number(expr, assumptions)
83
+
84
+ @PrimePredicate.register(NaN)
85
+ def _(expr, assumptions):
86
+ return None
87
+
88
+
89
+ # CompositePredicate
90
+
91
+ @CompositePredicate.register(Expr)
92
+ def _(expr, assumptions):
93
+ ret = expr.is_composite
94
+ if ret is None:
95
+ raise MDNotImplementedError
96
+ return ret
97
+
98
+ @CompositePredicate.register(Basic)
99
+ def _(expr, assumptions):
100
+ _positive = ask(Q.positive(expr), assumptions)
101
+ if _positive:
102
+ _integer = ask(Q.integer(expr), assumptions)
103
+ if _integer:
104
+ _prime = ask(Q.prime(expr), assumptions)
105
+ if _prime is None:
106
+ return
107
+ # Positive integer which is not prime is not
108
+ # necessarily composite
109
+ if expr.equals(1):
110
+ return False
111
+ return not _prime
112
+ else:
113
+ return _integer
114
+ else:
115
+ return _positive
116
+
117
+
118
+ # EvenPredicate
119
+
120
+ def _EvenPredicate_number(expr, assumptions):
121
+ # helper method
122
+ if isinstance(expr, (float, Float)):
123
+ if int_valued(expr):
124
+ return None
125
+ return False
126
+ try:
127
+ i = int(expr.round())
128
+ except TypeError:
129
+ return False
130
+ if not (expr - i).equals(0):
131
+ return False
132
+ return i % 2 == 0
133
+
134
+ @EvenPredicate.register(Expr)
135
+ def _(expr, assumptions):
136
+ ret = expr.is_even
137
+ if ret is None:
138
+ raise MDNotImplementedError
139
+ return ret
140
+
141
+ @EvenPredicate.register(Basic)
142
+ def _(expr, assumptions):
143
+ if expr.is_number:
144
+ return _EvenPredicate_number(expr, assumptions)
145
+
146
+ @EvenPredicate.register(Mul)
147
+ def _(expr, assumptions):
148
+ """
149
+ Even * Integer -> Even
150
+ Even * Odd -> Even
151
+ Integer * Odd -> ?
152
+ Odd * Odd -> Odd
153
+ Even * Even -> Even
154
+ Integer * Integer -> Even if Integer + Integer = Odd
155
+ otherwise -> ?
156
+ """
157
+ if expr.is_number:
158
+ return _EvenPredicate_number(expr, assumptions)
159
+ even, odd, irrational, acc = False, 0, False, 1
160
+ for arg in expr.args:
161
+ # check for all integers and at least one even
162
+ if ask(Q.integer(arg), assumptions):
163
+ if ask(Q.even(arg), assumptions):
164
+ even = True
165
+ elif ask(Q.odd(arg), assumptions):
166
+ odd += 1
167
+ elif not even and acc != 1:
168
+ if ask(Q.odd(acc + arg), assumptions):
169
+ even = True
170
+ elif ask(Q.irrational(arg), assumptions):
171
+ # one irrational makes the result False
172
+ # two makes it undefined
173
+ if irrational:
174
+ break
175
+ irrational = True
176
+ else:
177
+ break
178
+ acc = arg
179
+ else:
180
+ if irrational:
181
+ return False
182
+ if even:
183
+ return True
184
+ if odd == len(expr.args):
185
+ return False
186
+
187
+ @EvenPredicate.register(Add)
188
+ def _(expr, assumptions):
189
+ """
190
+ Even + Odd -> Odd
191
+ Even + Even -> Even
192
+ Odd + Odd -> Even
193
+
194
+ """
195
+ if expr.is_number:
196
+ return _EvenPredicate_number(expr, assumptions)
197
+ _result = True
198
+ for arg in expr.args:
199
+ if ask(Q.even(arg), assumptions):
200
+ pass
201
+ elif ask(Q.odd(arg), assumptions):
202
+ _result = not _result
203
+ else:
204
+ break
205
+ else:
206
+ return _result
207
+
208
+ @EvenPredicate.register(Pow)
209
+ def _(expr, assumptions):
210
+ if expr.is_number:
211
+ return _EvenPredicate_number(expr, assumptions)
212
+ if ask(Q.integer(expr.exp), assumptions):
213
+ if ask(Q.positive(expr.exp), assumptions):
214
+ return ask(Q.even(expr.base), assumptions)
215
+ elif ask(~Q.negative(expr.exp) & Q.odd(expr.base), assumptions):
216
+ return False
217
+ elif expr.base is S.NegativeOne:
218
+ return False
219
+
220
+ @EvenPredicate.register(Integer)
221
+ def _(expr, assumptions):
222
+ return not bool(expr.p & 1)
223
+
224
+ @EvenPredicate.register_many(Rational, Infinity, NegativeInfinity, ImaginaryUnit)
225
+ def _(expr, assumptions):
226
+ return False
227
+
228
+ @EvenPredicate.register(NumberSymbol)
229
+ def _(expr, assumptions):
230
+ return _EvenPredicate_number(expr, assumptions)
231
+
232
+ @EvenPredicate.register(Abs)
233
+ def _(expr, assumptions):
234
+ if ask(Q.real(expr.args[0]), assumptions):
235
+ return ask(Q.even(expr.args[0]), assumptions)
236
+
237
+ @EvenPredicate.register(re)
238
+ def _(expr, assumptions):
239
+ if ask(Q.real(expr.args[0]), assumptions):
240
+ return ask(Q.even(expr.args[0]), assumptions)
241
+
242
+ @EvenPredicate.register(im)
243
+ def _(expr, assumptions):
244
+ if ask(Q.real(expr.args[0]), assumptions):
245
+ return True
246
+
247
+ @EvenPredicate.register(NaN)
248
+ def _(expr, assumptions):
249
+ return None
250
+
251
+
252
+ # OddPredicate
253
+
254
+ @OddPredicate.register(Expr)
255
+ def _(expr, assumptions):
256
+ ret = expr.is_odd
257
+ if ret is None:
258
+ raise MDNotImplementedError
259
+ return ret
260
+
261
+ @OddPredicate.register(Basic)
262
+ def _(expr, assumptions):
263
+ _integer = ask(Q.integer(expr), assumptions)
264
+ if _integer:
265
+ _even = ask(Q.even(expr), assumptions)
266
+ if _even is None:
267
+ return None
268
+ return not _even
269
+ return _integer
lib/python3.12/site-packages/sympy/assumptions/handlers/order.py ADDED
@@ -0,0 +1,436 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Handlers related to order relations: positive, negative, etc.
3
+ """
4
+
5
+ from sympy.assumptions import Q, ask
6
+ from sympy.core import Add, Basic, Expr, Mul, Pow
7
+ from sympy.core.logic import fuzzy_not, fuzzy_and, fuzzy_or
8
+ from sympy.core.numbers import E, ImaginaryUnit, NaN, I, pi
9
+ from sympy.functions import Abs, acos, acot, asin, atan, exp, factorial, log
10
+ from sympy.matrices import Determinant, Trace
11
+ from sympy.matrices.expressions.matexpr import MatrixElement
12
+
13
+ from sympy.multipledispatch import MDNotImplementedError
14
+
15
+ from ..predicates.order import (NegativePredicate, NonNegativePredicate,
16
+ NonZeroPredicate, ZeroPredicate, NonPositivePredicate, PositivePredicate,
17
+ ExtendedNegativePredicate, ExtendedNonNegativePredicate,
18
+ ExtendedNonPositivePredicate, ExtendedNonZeroPredicate,
19
+ ExtendedPositivePredicate,)
20
+
21
+
22
+ # NegativePredicate
23
+
24
+ def _NegativePredicate_number(expr, assumptions):
25
+ r, i = expr.as_real_imag()
26
+ # If the imaginary part can symbolically be shown to be zero then
27
+ # we just evaluate the real part; otherwise we evaluate the imaginary
28
+ # part to see if it actually evaluates to zero and if it does then
29
+ # we make the comparison between the real part and zero.
30
+ if not i:
31
+ r = r.evalf(2)
32
+ if r._prec != 1:
33
+ return r < 0
34
+ else:
35
+ i = i.evalf(2)
36
+ if i._prec != 1:
37
+ if i != 0:
38
+ return False
39
+ r = r.evalf(2)
40
+ if r._prec != 1:
41
+ return r < 0
42
+
43
+ @NegativePredicate.register(Basic)
44
+ def _(expr, assumptions):
45
+ if expr.is_number:
46
+ return _NegativePredicate_number(expr, assumptions)
47
+
48
+ @NegativePredicate.register(Expr)
49
+ def _(expr, assumptions):
50
+ ret = expr.is_negative
51
+ if ret is None:
52
+ raise MDNotImplementedError
53
+ return ret
54
+
55
+ @NegativePredicate.register(Add)
56
+ def _(expr, assumptions):
57
+ """
58
+ Positive + Positive -> Positive,
59
+ Negative + Negative -> Negative
60
+ """
61
+ if expr.is_number:
62
+ return _NegativePredicate_number(expr, assumptions)
63
+
64
+ r = ask(Q.real(expr), assumptions)
65
+ if r is not True:
66
+ return r
67
+
68
+ nonpos = 0
69
+ for arg in expr.args:
70
+ if ask(Q.negative(arg), assumptions) is not True:
71
+ if ask(Q.positive(arg), assumptions) is False:
72
+ nonpos += 1
73
+ else:
74
+ break
75
+ else:
76
+ if nonpos < len(expr.args):
77
+ return True
78
+
79
+ @NegativePredicate.register(Mul)
80
+ def _(expr, assumptions):
81
+ if expr.is_number:
82
+ return _NegativePredicate_number(expr, assumptions)
83
+ result = None
84
+ for arg in expr.args:
85
+ if result is None:
86
+ result = False
87
+ if ask(Q.negative(arg), assumptions):
88
+ result = not result
89
+ elif ask(Q.positive(arg), assumptions):
90
+ pass
91
+ else:
92
+ return
93
+ return result
94
+
95
+ @NegativePredicate.register(Pow)
96
+ def _(expr, assumptions):
97
+ """
98
+ Real ** Even -> NonNegative
99
+ Real ** Odd -> same_as_base
100
+ NonNegative ** Positive -> NonNegative
101
+ """
102
+ if expr.base == E:
103
+ # Exponential is always positive:
104
+ if ask(Q.real(expr.exp), assumptions):
105
+ return False
106
+ return
107
+
108
+ if expr.is_number:
109
+ return _NegativePredicate_number(expr, assumptions)
110
+ if ask(Q.real(expr.base), assumptions):
111
+ if ask(Q.positive(expr.base), assumptions):
112
+ if ask(Q.real(expr.exp), assumptions):
113
+ return False
114
+ if ask(Q.even(expr.exp), assumptions):
115
+ return False
116
+ if ask(Q.odd(expr.exp), assumptions):
117
+ return ask(Q.negative(expr.base), assumptions)
118
+
119
+ @NegativePredicate.register_many(Abs, ImaginaryUnit)
120
+ def _(expr, assumptions):
121
+ return False
122
+
123
+ @NegativePredicate.register(exp)
124
+ def _(expr, assumptions):
125
+ if ask(Q.real(expr.exp), assumptions):
126
+ return False
127
+ raise MDNotImplementedError
128
+
129
+
130
+ # NonNegativePredicate
131
+
132
+ @NonNegativePredicate.register(Basic)
133
+ def _(expr, assumptions):
134
+ if expr.is_number:
135
+ notnegative = fuzzy_not(_NegativePredicate_number(expr, assumptions))
136
+ if notnegative:
137
+ return ask(Q.real(expr), assumptions)
138
+ else:
139
+ return notnegative
140
+
141
+ @NonNegativePredicate.register(Expr)
142
+ def _(expr, assumptions):
143
+ ret = expr.is_nonnegative
144
+ if ret is None:
145
+ raise MDNotImplementedError
146
+ return ret
147
+
148
+
149
+ # NonZeroPredicate
150
+
151
+ @NonZeroPredicate.register(Expr)
152
+ def _(expr, assumptions):
153
+ ret = expr.is_nonzero
154
+ if ret is None:
155
+ raise MDNotImplementedError
156
+ return ret
157
+
158
+ @NonZeroPredicate.register(Basic)
159
+ def _(expr, assumptions):
160
+ if ask(Q.real(expr)) is False:
161
+ return False
162
+ if expr.is_number:
163
+ # if there are no symbols just evalf
164
+ i = expr.evalf(2)
165
+ def nonz(i):
166
+ if i._prec != 1:
167
+ return i != 0
168
+ return fuzzy_or(nonz(i) for i in i.as_real_imag())
169
+
170
+ @NonZeroPredicate.register(Add)
171
+ def _(expr, assumptions):
172
+ if all(ask(Q.positive(x), assumptions) for x in expr.args) \
173
+ or all(ask(Q.negative(x), assumptions) for x in expr.args):
174
+ return True
175
+
176
+ @NonZeroPredicate.register(Mul)
177
+ def _(expr, assumptions):
178
+ for arg in expr.args:
179
+ result = ask(Q.nonzero(arg), assumptions)
180
+ if result:
181
+ continue
182
+ return result
183
+ return True
184
+
185
+ @NonZeroPredicate.register(Pow)
186
+ def _(expr, assumptions):
187
+ return ask(Q.nonzero(expr.base), assumptions)
188
+
189
+ @NonZeroPredicate.register(Abs)
190
+ def _(expr, assumptions):
191
+ return ask(Q.nonzero(expr.args[0]), assumptions)
192
+
193
+ @NonZeroPredicate.register(NaN)
194
+ def _(expr, assumptions):
195
+ return None
196
+
197
+
198
+ # ZeroPredicate
199
+
200
+ @ZeroPredicate.register(Expr)
201
+ def _(expr, assumptions):
202
+ ret = expr.is_zero
203
+ if ret is None:
204
+ raise MDNotImplementedError
205
+ return ret
206
+
207
+ @ZeroPredicate.register(Basic)
208
+ def _(expr, assumptions):
209
+ return fuzzy_and([fuzzy_not(ask(Q.nonzero(expr), assumptions)),
210
+ ask(Q.real(expr), assumptions)])
211
+
212
+ @ZeroPredicate.register(Mul)
213
+ def _(expr, assumptions):
214
+ # TODO: This should be deducible from the nonzero handler
215
+ return fuzzy_or(ask(Q.zero(arg), assumptions) for arg in expr.args)
216
+
217
+
218
+ # NonPositivePredicate
219
+
220
+ @NonPositivePredicate.register(Expr)
221
+ def _(expr, assumptions):
222
+ ret = expr.is_nonpositive
223
+ if ret is None:
224
+ raise MDNotImplementedError
225
+ return ret
226
+
227
+ @NonPositivePredicate.register(Basic)
228
+ def _(expr, assumptions):
229
+ if expr.is_number:
230
+ notpositive = fuzzy_not(_PositivePredicate_number(expr, assumptions))
231
+ if notpositive:
232
+ return ask(Q.real(expr), assumptions)
233
+ else:
234
+ return notpositive
235
+
236
+
237
+ # PositivePredicate
238
+
239
+ def _PositivePredicate_number(expr, assumptions):
240
+ r, i = expr.as_real_imag()
241
+ # If the imaginary part can symbolically be shown to be zero then
242
+ # we just evaluate the real part; otherwise we evaluate the imaginary
243
+ # part to see if it actually evaluates to zero and if it does then
244
+ # we make the comparison between the real part and zero.
245
+ if not i:
246
+ r = r.evalf(2)
247
+ if r._prec != 1:
248
+ return r > 0
249
+ else:
250
+ i = i.evalf(2)
251
+ if i._prec != 1:
252
+ if i != 0:
253
+ return False
254
+ r = r.evalf(2)
255
+ if r._prec != 1:
256
+ return r > 0
257
+
258
+ @PositivePredicate.register(Expr)
259
+ def _(expr, assumptions):
260
+ ret = expr.is_positive
261
+ if ret is None:
262
+ raise MDNotImplementedError
263
+ return ret
264
+
265
+ @PositivePredicate.register(Basic)
266
+ def _(expr, assumptions):
267
+ if expr.is_number:
268
+ return _PositivePredicate_number(expr, assumptions)
269
+
270
+ @PositivePredicate.register(Mul)
271
+ def _(expr, assumptions):
272
+ if expr.is_number:
273
+ return _PositivePredicate_number(expr, assumptions)
274
+ result = True
275
+ for arg in expr.args:
276
+ if ask(Q.positive(arg), assumptions):
277
+ continue
278
+ elif ask(Q.negative(arg), assumptions):
279
+ result = result ^ True
280
+ else:
281
+ return
282
+ return result
283
+
284
+ @PositivePredicate.register(Add)
285
+ def _(expr, assumptions):
286
+ if expr.is_number:
287
+ return _PositivePredicate_number(expr, assumptions)
288
+
289
+ r = ask(Q.real(expr), assumptions)
290
+ if r is not True:
291
+ return r
292
+
293
+ nonneg = 0
294
+ for arg in expr.args:
295
+ if ask(Q.positive(arg), assumptions) is not True:
296
+ if ask(Q.negative(arg), assumptions) is False:
297
+ nonneg += 1
298
+ else:
299
+ break
300
+ else:
301
+ if nonneg < len(expr.args):
302
+ return True
303
+
304
+ @PositivePredicate.register(Pow)
305
+ def _(expr, assumptions):
306
+ if expr.base == E:
307
+ if ask(Q.real(expr.exp), assumptions):
308
+ return True
309
+ if ask(Q.imaginary(expr.exp), assumptions):
310
+ return ask(Q.even(expr.exp/(I*pi)), assumptions)
311
+ return
312
+
313
+ if expr.is_number:
314
+ return _PositivePredicate_number(expr, assumptions)
315
+ if ask(Q.positive(expr.base), assumptions):
316
+ if ask(Q.real(expr.exp), assumptions):
317
+ return True
318
+ if ask(Q.negative(expr.base), assumptions):
319
+ if ask(Q.even(expr.exp), assumptions):
320
+ return True
321
+ if ask(Q.odd(expr.exp), assumptions):
322
+ return False
323
+
324
+ @PositivePredicate.register(exp)
325
+ def _(expr, assumptions):
326
+ if ask(Q.real(expr.exp), assumptions):
327
+ return True
328
+ if ask(Q.imaginary(expr.exp), assumptions):
329
+ return ask(Q.even(expr.exp/(I*pi)), assumptions)
330
+
331
+ @PositivePredicate.register(log)
332
+ def _(expr, assumptions):
333
+ r = ask(Q.real(expr.args[0]), assumptions)
334
+ if r is not True:
335
+ return r
336
+ if ask(Q.positive(expr.args[0] - 1), assumptions):
337
+ return True
338
+ if ask(Q.negative(expr.args[0] - 1), assumptions):
339
+ return False
340
+
341
+ @PositivePredicate.register(factorial)
342
+ def _(expr, assumptions):
343
+ x = expr.args[0]
344
+ if ask(Q.integer(x) & Q.positive(x), assumptions):
345
+ return True
346
+
347
+ @PositivePredicate.register(ImaginaryUnit)
348
+ def _(expr, assumptions):
349
+ return False
350
+
351
+ @PositivePredicate.register(Abs)
352
+ def _(expr, assumptions):
353
+ return ask(Q.nonzero(expr), assumptions)
354
+
355
+ @PositivePredicate.register(Trace)
356
+ def _(expr, assumptions):
357
+ if ask(Q.positive_definite(expr.arg), assumptions):
358
+ return True
359
+
360
+ @PositivePredicate.register(Determinant)
361
+ def _(expr, assumptions):
362
+ if ask(Q.positive_definite(expr.arg), assumptions):
363
+ return True
364
+
365
+ @PositivePredicate.register(MatrixElement)
366
+ def _(expr, assumptions):
367
+ if (expr.i == expr.j
368
+ and ask(Q.positive_definite(expr.parent), assumptions)):
369
+ return True
370
+
371
+ @PositivePredicate.register(atan)
372
+ def _(expr, assumptions):
373
+ return ask(Q.positive(expr.args[0]), assumptions)
374
+
375
+ @PositivePredicate.register(asin)
376
+ def _(expr, assumptions):
377
+ x = expr.args[0]
378
+ if ask(Q.positive(x) & Q.nonpositive(x - 1), assumptions):
379
+ return True
380
+ if ask(Q.negative(x) & Q.nonnegative(x + 1), assumptions):
381
+ return False
382
+
383
+ @PositivePredicate.register(acos)
384
+ def _(expr, assumptions):
385
+ x = expr.args[0]
386
+ if ask(Q.nonpositive(x - 1) & Q.nonnegative(x + 1), assumptions):
387
+ return True
388
+
389
+ @PositivePredicate.register(acot)
390
+ def _(expr, assumptions):
391
+ return ask(Q.real(expr.args[0]), assumptions)
392
+
393
+ @PositivePredicate.register(NaN)
394
+ def _(expr, assumptions):
395
+ return None
396
+
397
+
398
+ # ExtendedNegativePredicate
399
+
400
+ @ExtendedNegativePredicate.register(object)
401
+ def _(expr, assumptions):
402
+ return ask(Q.negative(expr) | Q.negative_infinite(expr), assumptions)
403
+
404
+
405
+ # ExtendedPositivePredicate
406
+
407
+ @ExtendedPositivePredicate.register(object)
408
+ def _(expr, assumptions):
409
+ return ask(Q.positive(expr) | Q.positive_infinite(expr), assumptions)
410
+
411
+
412
+ # ExtendedNonZeroPredicate
413
+
414
+ @ExtendedNonZeroPredicate.register(object)
415
+ def _(expr, assumptions):
416
+ return ask(
417
+ Q.negative_infinite(expr) | Q.negative(expr) | Q.positive(expr) | Q.positive_infinite(expr),
418
+ assumptions)
419
+
420
+
421
+ # ExtendedNonPositivePredicate
422
+
423
+ @ExtendedNonPositivePredicate.register(object)
424
+ def _(expr, assumptions):
425
+ return ask(
426
+ Q.negative_infinite(expr) | Q.negative(expr) | Q.zero(expr),
427
+ assumptions)
428
+
429
+
430
+ # ExtendedNonNegativePredicate
431
+
432
+ @ExtendedNonNegativePredicate.register(object)
433
+ def _(expr, assumptions):
434
+ return ask(
435
+ Q.zero(expr) | Q.positive(expr) | Q.positive_infinite(expr),
436
+ assumptions)
lib/python3.12/site-packages/sympy/assumptions/handlers/sets.py ADDED
@@ -0,0 +1,772 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Handlers for predicates related to set membership: integer, rational, etc.
3
+ """
4
+
5
+ from sympy.assumptions import Q, ask
6
+ from sympy.core import Add, Basic, Expr, Mul, Pow, S
7
+ from sympy.core.numbers import (AlgebraicNumber, ComplexInfinity, Exp1, Float,
8
+ GoldenRatio, ImaginaryUnit, Infinity, Integer, NaN, NegativeInfinity,
9
+ Number, NumberSymbol, Pi, pi, Rational, TribonacciConstant, E)
10
+ from sympy.core.logic import fuzzy_bool
11
+ from sympy.functions import (Abs, acos, acot, asin, atan, cos, cot, exp, im,
12
+ log, re, sin, tan)
13
+ from sympy.core.numbers import I
14
+ from sympy.core.relational import Eq
15
+ from sympy.functions.elementary.complexes import conjugate
16
+ from sympy.matrices import Determinant, MatrixBase, Trace
17
+ from sympy.matrices.expressions.matexpr import MatrixElement
18
+
19
+ from sympy.multipledispatch import MDNotImplementedError
20
+
21
+ from .common import test_closed_group
22
+ from ..predicates.sets import (IntegerPredicate, RationalPredicate,
23
+ IrrationalPredicate, RealPredicate, ExtendedRealPredicate,
24
+ HermitianPredicate, ComplexPredicate, ImaginaryPredicate,
25
+ AntihermitianPredicate, AlgebraicPredicate)
26
+
27
+
28
+ # IntegerPredicate
29
+
30
+ def _IntegerPredicate_number(expr, assumptions):
31
+ # helper function
32
+ try:
33
+ i = int(expr.round())
34
+ if not (expr - i).equals(0):
35
+ raise TypeError
36
+ return True
37
+ except TypeError:
38
+ return False
39
+
40
+ @IntegerPredicate.register_many(int, Integer) # type:ignore
41
+ def _(expr, assumptions):
42
+ return True
43
+
44
+ @IntegerPredicate.register_many(Exp1, GoldenRatio, ImaginaryUnit, Infinity,
45
+ NegativeInfinity, Pi, Rational, TribonacciConstant)
46
+ def _(expr, assumptions):
47
+ return False
48
+
49
+ @IntegerPredicate.register(Expr)
50
+ def _(expr, assumptions):
51
+ ret = expr.is_integer
52
+ if ret is None:
53
+ raise MDNotImplementedError
54
+ return ret
55
+
56
+ @IntegerPredicate.register_many(Add, Pow)
57
+ def _(expr, assumptions):
58
+ """
59
+ * Integer + Integer -> Integer
60
+ * Integer + !Integer -> !Integer
61
+ * !Integer + !Integer -> ?
62
+ """
63
+ if expr.is_number:
64
+ return _IntegerPredicate_number(expr, assumptions)
65
+ return test_closed_group(expr, assumptions, Q.integer)
66
+
67
+ @IntegerPredicate.register(Mul)
68
+ def _(expr, assumptions):
69
+ """
70
+ * Integer*Integer -> Integer
71
+ * Integer*Irrational -> !Integer
72
+ * Odd/Even -> !Integer
73
+ * Integer*Rational -> ?
74
+ """
75
+ if expr.is_number:
76
+ return _IntegerPredicate_number(expr, assumptions)
77
+ _output = True
78
+ for arg in expr.args:
79
+ if not ask(Q.integer(arg), assumptions):
80
+ if arg.is_Rational:
81
+ if arg.q == 2:
82
+ return ask(Q.even(2*expr), assumptions)
83
+ if ~(arg.q & 1):
84
+ return None
85
+ elif ask(Q.irrational(arg), assumptions):
86
+ if _output:
87
+ _output = False
88
+ else:
89
+ return
90
+ else:
91
+ return
92
+
93
+ return _output
94
+
95
+ @IntegerPredicate.register(Abs)
96
+ def _(expr, assumptions):
97
+ return ask(Q.integer(expr.args[0]), assumptions)
98
+
99
+ @IntegerPredicate.register_many(Determinant, MatrixElement, Trace)
100
+ def _(expr, assumptions):
101
+ return ask(Q.integer_elements(expr.args[0]), assumptions)
102
+
103
+
104
+ # RationalPredicate
105
+
106
+ @RationalPredicate.register(Rational)
107
+ def _(expr, assumptions):
108
+ return True
109
+
110
+ @RationalPredicate.register(Float)
111
+ def _(expr, assumptions):
112
+ return None
113
+
114
+ @RationalPredicate.register_many(Exp1, GoldenRatio, ImaginaryUnit, Infinity,
115
+ NegativeInfinity, Pi, TribonacciConstant)
116
+ def _(expr, assumptions):
117
+ return False
118
+
119
+ @RationalPredicate.register(Expr)
120
+ def _(expr, assumptions):
121
+ ret = expr.is_rational
122
+ if ret is None:
123
+ raise MDNotImplementedError
124
+ return ret
125
+
126
+ @RationalPredicate.register_many(Add, Mul)
127
+ def _(expr, assumptions):
128
+ """
129
+ * Rational + Rational -> Rational
130
+ * Rational + !Rational -> !Rational
131
+ * !Rational + !Rational -> ?
132
+ """
133
+ if expr.is_number:
134
+ if expr.as_real_imag()[1]:
135
+ return False
136
+ return test_closed_group(expr, assumptions, Q.rational)
137
+
138
+ @RationalPredicate.register(Pow)
139
+ def _(expr, assumptions):
140
+ """
141
+ * Rational ** Integer -> Rational
142
+ * Irrational ** Rational -> Irrational
143
+ * Rational ** Irrational -> ?
144
+ """
145
+ if expr.base == E:
146
+ x = expr.exp
147
+ if ask(Q.rational(x), assumptions):
148
+ return ask(~Q.nonzero(x), assumptions)
149
+ return
150
+
151
+ if ask(Q.integer(expr.exp), assumptions):
152
+ return ask(Q.rational(expr.base), assumptions)
153
+ elif ask(Q.rational(expr.exp), assumptions):
154
+ if ask(Q.prime(expr.base), assumptions):
155
+ return False
156
+
157
+ @RationalPredicate.register_many(asin, atan, cos, sin, tan)
158
+ def _(expr, assumptions):
159
+ x = expr.args[0]
160
+ if ask(Q.rational(x), assumptions):
161
+ return ask(~Q.nonzero(x), assumptions)
162
+
163
+ @RationalPredicate.register(exp)
164
+ def _(expr, assumptions):
165
+ x = expr.exp
166
+ if ask(Q.rational(x), assumptions):
167
+ return ask(~Q.nonzero(x), assumptions)
168
+
169
+ @RationalPredicate.register_many(acot, cot)
170
+ def _(expr, assumptions):
171
+ x = expr.args[0]
172
+ if ask(Q.rational(x), assumptions):
173
+ return False
174
+
175
+ @RationalPredicate.register_many(acos, log)
176
+ def _(expr, assumptions):
177
+ x = expr.args[0]
178
+ if ask(Q.rational(x), assumptions):
179
+ return ask(~Q.nonzero(x - 1), assumptions)
180
+
181
+
182
+ # IrrationalPredicate
183
+
184
+ @IrrationalPredicate.register(Expr)
185
+ def _(expr, assumptions):
186
+ ret = expr.is_irrational
187
+ if ret is None:
188
+ raise MDNotImplementedError
189
+ return ret
190
+
191
+ @IrrationalPredicate.register(Basic)
192
+ def _(expr, assumptions):
193
+ _real = ask(Q.real(expr), assumptions)
194
+ if _real:
195
+ _rational = ask(Q.rational(expr), assumptions)
196
+ if _rational is None:
197
+ return None
198
+ return not _rational
199
+ else:
200
+ return _real
201
+
202
+
203
+ # RealPredicate
204
+
205
+ def _RealPredicate_number(expr, assumptions):
206
+ # let as_real_imag() work first since the expression may
207
+ # be simpler to evaluate
208
+ i = expr.as_real_imag()[1].evalf(2)
209
+ if i._prec != 1:
210
+ return not i
211
+ # allow None to be returned if we couldn't show for sure
212
+ # that i was 0
213
+
214
+ @RealPredicate.register_many(Abs, Exp1, Float, GoldenRatio, im, Pi, Rational,
215
+ re, TribonacciConstant)
216
+ def _(expr, assumptions):
217
+ return True
218
+
219
+ @RealPredicate.register_many(ImaginaryUnit, Infinity, NegativeInfinity)
220
+ def _(expr, assumptions):
221
+ return False
222
+
223
+ @RealPredicate.register(Expr)
224
+ def _(expr, assumptions):
225
+ ret = expr.is_real
226
+ if ret is None:
227
+ raise MDNotImplementedError
228
+ return ret
229
+
230
+ @RealPredicate.register(Add)
231
+ def _(expr, assumptions):
232
+ """
233
+ * Real + Real -> Real
234
+ * Real + (Complex & !Real) -> !Real
235
+ """
236
+ if expr.is_number:
237
+ return _RealPredicate_number(expr, assumptions)
238
+ return test_closed_group(expr, assumptions, Q.real)
239
+
240
+ @RealPredicate.register(Mul)
241
+ def _(expr, assumptions):
242
+ """
243
+ * Real*Real -> Real
244
+ * Real*Imaginary -> !Real
245
+ * Imaginary*Imaginary -> Real
246
+ """
247
+ if expr.is_number:
248
+ return _RealPredicate_number(expr, assumptions)
249
+ result = True
250
+ for arg in expr.args:
251
+ if ask(Q.real(arg), assumptions):
252
+ pass
253
+ elif ask(Q.imaginary(arg), assumptions):
254
+ result = result ^ True
255
+ else:
256
+ break
257
+ else:
258
+ return result
259
+
260
+ @RealPredicate.register(Pow)
261
+ def _(expr, assumptions):
262
+ """
263
+ * Real**Integer -> Real
264
+ * Positive**Real -> Real
265
+ * Real**(Integer/Even) -> Real if base is nonnegative
266
+ * Real**(Integer/Odd) -> Real
267
+ * Imaginary**(Integer/Even) -> Real
268
+ * Imaginary**(Integer/Odd) -> not Real
269
+ * Imaginary**Real -> ? since Real could be 0 (giving real)
270
+ or 1 (giving imaginary)
271
+ * b**Imaginary -> Real if log(b) is imaginary and b != 0
272
+ and exponent != integer multiple of
273
+ I*pi/log(b)
274
+ * Real**Real -> ? e.g. sqrt(-1) is imaginary and
275
+ sqrt(2) is not
276
+ """
277
+ if expr.is_number:
278
+ return _RealPredicate_number(expr, assumptions)
279
+
280
+ if expr.base == E:
281
+ return ask(
282
+ Q.integer(expr.exp/I/pi) | Q.real(expr.exp), assumptions
283
+ )
284
+
285
+ if expr.base.func == exp or (expr.base.is_Pow and expr.base.base == E):
286
+ if ask(Q.imaginary(expr.base.exp), assumptions):
287
+ if ask(Q.imaginary(expr.exp), assumptions):
288
+ return True
289
+ # If the i = (exp's arg)/(I*pi) is an integer or half-integer
290
+ # multiple of I*pi then 2*i will be an integer. In addition,
291
+ # exp(i*I*pi) = (-1)**i so the overall realness of the expr
292
+ # can be determined by replacing exp(i*I*pi) with (-1)**i.
293
+ i = expr.base.exp/I/pi
294
+ if ask(Q.integer(2*i), assumptions):
295
+ return ask(Q.real((S.NegativeOne**i)**expr.exp), assumptions)
296
+ return
297
+
298
+ if ask(Q.imaginary(expr.base), assumptions):
299
+ if ask(Q.integer(expr.exp), assumptions):
300
+ odd = ask(Q.odd(expr.exp), assumptions)
301
+ if odd is not None:
302
+ return not odd
303
+ return
304
+
305
+ if ask(Q.imaginary(expr.exp), assumptions):
306
+ imlog = ask(Q.imaginary(log(expr.base)), assumptions)
307
+ if imlog is not None:
308
+ # I**i -> real, log(I) is imag;
309
+ # (2*I)**i -> complex, log(2*I) is not imag
310
+ return imlog
311
+
312
+ if ask(Q.real(expr.base), assumptions):
313
+ if ask(Q.real(expr.exp), assumptions):
314
+ if expr.exp.is_Rational and \
315
+ ask(Q.even(expr.exp.q), assumptions):
316
+ return ask(Q.positive(expr.base), assumptions)
317
+ elif ask(Q.integer(expr.exp), assumptions):
318
+ return True
319
+ elif ask(Q.positive(expr.base), assumptions):
320
+ return True
321
+ elif ask(Q.negative(expr.base), assumptions):
322
+ return False
323
+
324
+ @RealPredicate.register_many(cos, sin)
325
+ def _(expr, assumptions):
326
+ if ask(Q.real(expr.args[0]), assumptions):
327
+ return True
328
+
329
+ @RealPredicate.register(exp)
330
+ def _(expr, assumptions):
331
+ return ask(
332
+ Q.integer(expr.exp/I/pi) | Q.real(expr.exp), assumptions
333
+ )
334
+
335
+ @RealPredicate.register(log)
336
+ def _(expr, assumptions):
337
+ return ask(Q.positive(expr.args[0]), assumptions)
338
+
339
+ @RealPredicate.register_many(Determinant, MatrixElement, Trace)
340
+ def _(expr, assumptions):
341
+ return ask(Q.real_elements(expr.args[0]), assumptions)
342
+
343
+
344
+ # ExtendedRealPredicate
345
+
346
+ @ExtendedRealPredicate.register(object)
347
+ def _(expr, assumptions):
348
+ return ask(Q.negative_infinite(expr)
349
+ | Q.negative(expr)
350
+ | Q.zero(expr)
351
+ | Q.positive(expr)
352
+ | Q.positive_infinite(expr),
353
+ assumptions)
354
+
355
+ @ExtendedRealPredicate.register_many(Infinity, NegativeInfinity)
356
+ def _(expr, assumptions):
357
+ return True
358
+
359
+ @ExtendedRealPredicate.register_many(Add, Mul, Pow) # type:ignore
360
+ def _(expr, assumptions):
361
+ return test_closed_group(expr, assumptions, Q.extended_real)
362
+
363
+
364
+ # HermitianPredicate
365
+
366
+ @HermitianPredicate.register(object) # type:ignore
367
+ def _(expr, assumptions):
368
+ if isinstance(expr, MatrixBase):
369
+ return None
370
+ return ask(Q.real(expr), assumptions)
371
+
372
+ @HermitianPredicate.register(Add) # type:ignore
373
+ def _(expr, assumptions):
374
+ """
375
+ * Hermitian + Hermitian -> Hermitian
376
+ * Hermitian + !Hermitian -> !Hermitian
377
+ """
378
+ if expr.is_number:
379
+ raise MDNotImplementedError
380
+ return test_closed_group(expr, assumptions, Q.hermitian)
381
+
382
+ @HermitianPredicate.register(Mul) # type:ignore
383
+ def _(expr, assumptions):
384
+ """
385
+ As long as there is at most only one noncommutative term:
386
+
387
+ * Hermitian*Hermitian -> Hermitian
388
+ * Hermitian*Antihermitian -> !Hermitian
389
+ * Antihermitian*Antihermitian -> Hermitian
390
+ """
391
+ if expr.is_number:
392
+ raise MDNotImplementedError
393
+ nccount = 0
394
+ result = True
395
+ for arg in expr.args:
396
+ if ask(Q.antihermitian(arg), assumptions):
397
+ result = result ^ True
398
+ elif not ask(Q.hermitian(arg), assumptions):
399
+ break
400
+ if ask(~Q.commutative(arg), assumptions):
401
+ nccount += 1
402
+ if nccount > 1:
403
+ break
404
+ else:
405
+ return result
406
+
407
+ @HermitianPredicate.register(Pow) # type:ignore
408
+ def _(expr, assumptions):
409
+ """
410
+ * Hermitian**Integer -> Hermitian
411
+ """
412
+ if expr.is_number:
413
+ raise MDNotImplementedError
414
+ if expr.base == E:
415
+ if ask(Q.hermitian(expr.exp), assumptions):
416
+ return True
417
+ raise MDNotImplementedError
418
+ if ask(Q.hermitian(expr.base), assumptions):
419
+ if ask(Q.integer(expr.exp), assumptions):
420
+ return True
421
+ raise MDNotImplementedError
422
+
423
+ @HermitianPredicate.register_many(cos, sin) # type:ignore
424
+ def _(expr, assumptions):
425
+ if ask(Q.hermitian(expr.args[0]), assumptions):
426
+ return True
427
+ raise MDNotImplementedError
428
+
429
+ @HermitianPredicate.register(exp) # type:ignore
430
+ def _(expr, assumptions):
431
+ if ask(Q.hermitian(expr.exp), assumptions):
432
+ return True
433
+ raise MDNotImplementedError
434
+
435
+ @HermitianPredicate.register(MatrixBase) # type:ignore
436
+ def _(mat, assumptions):
437
+ rows, cols = mat.shape
438
+ ret_val = True
439
+ for i in range(rows):
440
+ for j in range(i, cols):
441
+ cond = fuzzy_bool(Eq(mat[i, j], conjugate(mat[j, i])))
442
+ if cond is None:
443
+ ret_val = None
444
+ if cond == False:
445
+ return False
446
+ if ret_val is None:
447
+ raise MDNotImplementedError
448
+ return ret_val
449
+
450
+
451
+ # ComplexPredicate
452
+
453
+ @ComplexPredicate.register_many(Abs, cos, exp, im, ImaginaryUnit, log, Number, # type:ignore
454
+ NumberSymbol, re, sin)
455
+ def _(expr, assumptions):
456
+ return True
457
+
458
+ @ComplexPredicate.register_many(Infinity, NegativeInfinity) # type:ignore
459
+ def _(expr, assumptions):
460
+ return False
461
+
462
+ @ComplexPredicate.register(Expr) # type:ignore
463
+ def _(expr, assumptions):
464
+ ret = expr.is_complex
465
+ if ret is None:
466
+ raise MDNotImplementedError
467
+ return ret
468
+
469
+ @ComplexPredicate.register_many(Add, Mul) # type:ignore
470
+ def _(expr, assumptions):
471
+ return test_closed_group(expr, assumptions, Q.complex)
472
+
473
+ @ComplexPredicate.register(Pow) # type:ignore
474
+ def _(expr, assumptions):
475
+ if expr.base == E:
476
+ return True
477
+ return test_closed_group(expr, assumptions, Q.complex)
478
+
479
+ @ComplexPredicate.register_many(Determinant, MatrixElement, Trace) # type:ignore
480
+ def _(expr, assumptions):
481
+ return ask(Q.complex_elements(expr.args[0]), assumptions)
482
+
483
+ @ComplexPredicate.register(NaN) # type:ignore
484
+ def _(expr, assumptions):
485
+ return None
486
+
487
+
488
+ # ImaginaryPredicate
489
+
490
+ def _Imaginary_number(expr, assumptions):
491
+ # let as_real_imag() work first since the expression may
492
+ # be simpler to evaluate
493
+ r = expr.as_real_imag()[0].evalf(2)
494
+ if r._prec != 1:
495
+ return not r
496
+ # allow None to be returned if we couldn't show for sure
497
+ # that r was 0
498
+
499
+ @ImaginaryPredicate.register(ImaginaryUnit) # type:ignore
500
+ def _(expr, assumptions):
501
+ return True
502
+
503
+ @ImaginaryPredicate.register(Expr) # type:ignore
504
+ def _(expr, assumptions):
505
+ ret = expr.is_imaginary
506
+ if ret is None:
507
+ raise MDNotImplementedError
508
+ return ret
509
+
510
+ @ImaginaryPredicate.register(Add) # type:ignore
511
+ def _(expr, assumptions):
512
+ """
513
+ * Imaginary + Imaginary -> Imaginary
514
+ * Imaginary + Complex -> ?
515
+ * Imaginary + Real -> !Imaginary
516
+ """
517
+ if expr.is_number:
518
+ return _Imaginary_number(expr, assumptions)
519
+
520
+ reals = 0
521
+ for arg in expr.args:
522
+ if ask(Q.imaginary(arg), assumptions):
523
+ pass
524
+ elif ask(Q.real(arg), assumptions):
525
+ reals += 1
526
+ else:
527
+ break
528
+ else:
529
+ if reals == 0:
530
+ return True
531
+ if reals in (1, len(expr.args)):
532
+ # two reals could sum 0 thus giving an imaginary
533
+ return False
534
+
535
+ @ImaginaryPredicate.register(Mul) # type:ignore
536
+ def _(expr, assumptions):
537
+ """
538
+ * Real*Imaginary -> Imaginary
539
+ * Imaginary*Imaginary -> Real
540
+ """
541
+ if expr.is_number:
542
+ return _Imaginary_number(expr, assumptions)
543
+ result = False
544
+ reals = 0
545
+ for arg in expr.args:
546
+ if ask(Q.imaginary(arg), assumptions):
547
+ result = result ^ True
548
+ elif not ask(Q.real(arg), assumptions):
549
+ break
550
+ else:
551
+ if reals == len(expr.args):
552
+ return False
553
+ return result
554
+
555
+ @ImaginaryPredicate.register(Pow) # type:ignore
556
+ def _(expr, assumptions):
557
+ """
558
+ * Imaginary**Odd -> Imaginary
559
+ * Imaginary**Even -> Real
560
+ * b**Imaginary -> !Imaginary if exponent is an integer
561
+ multiple of I*pi/log(b)
562
+ * Imaginary**Real -> ?
563
+ * Positive**Real -> Real
564
+ * Negative**Integer -> Real
565
+ * Negative**(Integer/2) -> Imaginary
566
+ * Negative**Real -> not Imaginary if exponent is not Rational
567
+ """
568
+ if expr.is_number:
569
+ return _Imaginary_number(expr, assumptions)
570
+
571
+ if expr.base == E:
572
+ a = expr.exp/I/pi
573
+ return ask(Q.integer(2*a) & ~Q.integer(a), assumptions)
574
+
575
+ if expr.base.func == exp or (expr.base.is_Pow and expr.base.base == E):
576
+ if ask(Q.imaginary(expr.base.exp), assumptions):
577
+ if ask(Q.imaginary(expr.exp), assumptions):
578
+ return False
579
+ i = expr.base.exp/I/pi
580
+ if ask(Q.integer(2*i), assumptions):
581
+ return ask(Q.imaginary((S.NegativeOne**i)**expr.exp), assumptions)
582
+
583
+ if ask(Q.imaginary(expr.base), assumptions):
584
+ if ask(Q.integer(expr.exp), assumptions):
585
+ odd = ask(Q.odd(expr.exp), assumptions)
586
+ if odd is not None:
587
+ return odd
588
+ return
589
+
590
+ if ask(Q.imaginary(expr.exp), assumptions):
591
+ imlog = ask(Q.imaginary(log(expr.base)), assumptions)
592
+ if imlog is not None:
593
+ # I**i -> real; (2*I)**i -> complex ==> not imaginary
594
+ return False
595
+
596
+ if ask(Q.real(expr.base) & Q.real(expr.exp), assumptions):
597
+ if ask(Q.positive(expr.base), assumptions):
598
+ return False
599
+ else:
600
+ rat = ask(Q.rational(expr.exp), assumptions)
601
+ if not rat:
602
+ return rat
603
+ if ask(Q.integer(expr.exp), assumptions):
604
+ return False
605
+ else:
606
+ half = ask(Q.integer(2*expr.exp), assumptions)
607
+ if half:
608
+ return ask(Q.negative(expr.base), assumptions)
609
+ return half
610
+
611
+ @ImaginaryPredicate.register(log) # type:ignore
612
+ def _(expr, assumptions):
613
+ if ask(Q.real(expr.args[0]), assumptions):
614
+ if ask(Q.positive(expr.args[0]), assumptions):
615
+ return False
616
+ return
617
+ # XXX it should be enough to do
618
+ # return ask(Q.nonpositive(expr.args[0]), assumptions)
619
+ # but ask(Q.nonpositive(exp(x)), Q.imaginary(x)) -> None;
620
+ # it should return True since exp(x) will be either 0 or complex
621
+ if expr.args[0].func == exp or (expr.args[0].is_Pow and expr.args[0].base == E):
622
+ if expr.args[0].exp in [I, -I]:
623
+ return True
624
+ im = ask(Q.imaginary(expr.args[0]), assumptions)
625
+ if im is False:
626
+ return False
627
+
628
+ @ImaginaryPredicate.register(exp) # type:ignore
629
+ def _(expr, assumptions):
630
+ a = expr.exp/I/pi
631
+ return ask(Q.integer(2*a) & ~Q.integer(a), assumptions)
632
+
633
+ @ImaginaryPredicate.register_many(Number, NumberSymbol) # type:ignore
634
+ def _(expr, assumptions):
635
+ return not (expr.as_real_imag()[1] == 0)
636
+
637
+ @ImaginaryPredicate.register(NaN) # type:ignore
638
+ def _(expr, assumptions):
639
+ return None
640
+
641
+
642
+ # AntihermitianPredicate
643
+
644
+ @AntihermitianPredicate.register(object) # type:ignore
645
+ def _(expr, assumptions):
646
+ if isinstance(expr, MatrixBase):
647
+ return None
648
+ if ask(Q.zero(expr), assumptions):
649
+ return True
650
+ return ask(Q.imaginary(expr), assumptions)
651
+
652
+ @AntihermitianPredicate.register(Add) # type:ignore
653
+ def _(expr, assumptions):
654
+ """
655
+ * Antihermitian + Antihermitian -> Antihermitian
656
+ * Antihermitian + !Antihermitian -> !Antihermitian
657
+ """
658
+ if expr.is_number:
659
+ raise MDNotImplementedError
660
+ return test_closed_group(expr, assumptions, Q.antihermitian)
661
+
662
+ @AntihermitianPredicate.register(Mul) # type:ignore
663
+ def _(expr, assumptions):
664
+ """
665
+ As long as there is at most only one noncommutative term:
666
+
667
+ * Hermitian*Hermitian -> !Antihermitian
668
+ * Hermitian*Antihermitian -> Antihermitian
669
+ * Antihermitian*Antihermitian -> !Antihermitian
670
+ """
671
+ if expr.is_number:
672
+ raise MDNotImplementedError
673
+ nccount = 0
674
+ result = False
675
+ for arg in expr.args:
676
+ if ask(Q.antihermitian(arg), assumptions):
677
+ result = result ^ True
678
+ elif not ask(Q.hermitian(arg), assumptions):
679
+ break
680
+ if ask(~Q.commutative(arg), assumptions):
681
+ nccount += 1
682
+ if nccount > 1:
683
+ break
684
+ else:
685
+ return result
686
+
687
+ @AntihermitianPredicate.register(Pow) # type:ignore
688
+ def _(expr, assumptions):
689
+ """
690
+ * Hermitian**Integer -> !Antihermitian
691
+ * Antihermitian**Even -> !Antihermitian
692
+ * Antihermitian**Odd -> Antihermitian
693
+ """
694
+ if expr.is_number:
695
+ raise MDNotImplementedError
696
+ if ask(Q.hermitian(expr.base), assumptions):
697
+ if ask(Q.integer(expr.exp), assumptions):
698
+ return False
699
+ elif ask(Q.antihermitian(expr.base), assumptions):
700
+ if ask(Q.even(expr.exp), assumptions):
701
+ return False
702
+ elif ask(Q.odd(expr.exp), assumptions):
703
+ return True
704
+ raise MDNotImplementedError
705
+
706
+ @AntihermitianPredicate.register(MatrixBase) # type:ignore
707
+ def _(mat, assumptions):
708
+ rows, cols = mat.shape
709
+ ret_val = True
710
+ for i in range(rows):
711
+ for j in range(i, cols):
712
+ cond = fuzzy_bool(Eq(mat[i, j], -conjugate(mat[j, i])))
713
+ if cond is None:
714
+ ret_val = None
715
+ if cond == False:
716
+ return False
717
+ if ret_val is None:
718
+ raise MDNotImplementedError
719
+ return ret_val
720
+
721
+
722
+ # AlgebraicPredicate
723
+
724
+ @AlgebraicPredicate.register_many(AlgebraicNumber, Float, GoldenRatio, # type:ignore
725
+ ImaginaryUnit, TribonacciConstant)
726
+ def _(expr, assumptions):
727
+ return True
728
+
729
+ @AlgebraicPredicate.register_many(ComplexInfinity, Exp1, Infinity, # type:ignore
730
+ NegativeInfinity, Pi)
731
+ def _(expr, assumptions):
732
+ return False
733
+
734
+ @AlgebraicPredicate.register_many(Add, Mul) # type:ignore
735
+ def _(expr, assumptions):
736
+ return test_closed_group(expr, assumptions, Q.algebraic)
737
+
738
+ @AlgebraicPredicate.register(Pow) # type:ignore
739
+ def _(expr, assumptions):
740
+ if expr.base == E:
741
+ if ask(Q.algebraic(expr.exp), assumptions):
742
+ return ask(~Q.nonzero(expr.exp), assumptions)
743
+ return
744
+ return expr.exp.is_Rational and ask(Q.algebraic(expr.base), assumptions)
745
+
746
+ @AlgebraicPredicate.register(Rational) # type:ignore
747
+ def _(expr, assumptions):
748
+ return expr.q != 0
749
+
750
+ @AlgebraicPredicate.register_many(asin, atan, cos, sin, tan) # type:ignore
751
+ def _(expr, assumptions):
752
+ x = expr.args[0]
753
+ if ask(Q.algebraic(x), assumptions):
754
+ return ask(~Q.nonzero(x), assumptions)
755
+
756
+ @AlgebraicPredicate.register(exp) # type:ignore
757
+ def _(expr, assumptions):
758
+ x = expr.exp
759
+ if ask(Q.algebraic(x), assumptions):
760
+ return ask(~Q.nonzero(x), assumptions)
761
+
762
+ @AlgebraicPredicate.register_many(acot, cot) # type:ignore
763
+ def _(expr, assumptions):
764
+ x = expr.args[0]
765
+ if ask(Q.algebraic(x), assumptions):
766
+ return False
767
+
768
+ @AlgebraicPredicate.register_many(acos, log) # type:ignore
769
+ def _(expr, assumptions):
770
+ x = expr.args[0]
771
+ if ask(Q.algebraic(x), assumptions):
772
+ return ask(~Q.nonzero(x - 1), assumptions)
lib/python3.12/site-packages/sympy/assumptions/lra_satask.py ADDED
@@ -0,0 +1,286 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.assumptions.assume import global_assumptions
2
+ from sympy.assumptions.cnf import CNF, EncodedCNF
3
+ from sympy.assumptions.ask import Q
4
+ from sympy.logic.inference import satisfiable
5
+ from sympy.logic.algorithms.lra_theory import UnhandledInput, ALLOWED_PRED
6
+ from sympy.matrices.kind import MatrixKind
7
+ from sympy.core.kind import NumberKind
8
+ from sympy.assumptions.assume import AppliedPredicate
9
+ from sympy.core.mul import Mul
10
+ from sympy.core.singleton import S
11
+
12
+
13
+ def lra_satask(proposition, assumptions=True, context=global_assumptions):
14
+ """
15
+ Function to evaluate the proposition with assumptions using SAT algorithm
16
+ in conjunction with an Linear Real Arithmetic theory solver.
17
+
18
+ Used to handle inequalities. Should eventually be depreciated and combined
19
+ into satask, but infinity handling and other things need to be implemented
20
+ before that can happen.
21
+ """
22
+ props = CNF.from_prop(proposition)
23
+ _props = CNF.from_prop(~proposition)
24
+
25
+ cnf = CNF.from_prop(assumptions)
26
+ assumptions = EncodedCNF()
27
+ assumptions.from_cnf(cnf)
28
+
29
+ context_cnf = CNF()
30
+ if context:
31
+ context_cnf = context_cnf.extend(context)
32
+
33
+ assumptions.add_from_cnf(context_cnf)
34
+
35
+ return check_satisfiability(props, _props, assumptions)
36
+
37
+ # Some predicates such as Q.prime can't be handled by lra_satask.
38
+ # For example, (x > 0) & (x < 1) & Q.prime(x) is unsat but lra_satask would think it was sat.
39
+ # WHITE_LIST is a list of predicates that can always be handled.
40
+ WHITE_LIST = ALLOWED_PRED | {Q.positive, Q.negative, Q.zero, Q.nonzero, Q.nonpositive, Q.nonnegative,
41
+ Q.extended_positive, Q.extended_negative, Q.extended_nonpositive,
42
+ Q.extended_negative, Q.extended_nonzero, Q.negative_infinite,
43
+ Q.positive_infinite}
44
+
45
+
46
+ def check_satisfiability(prop, _prop, factbase):
47
+ sat_true = factbase.copy()
48
+ sat_false = factbase.copy()
49
+ sat_true.add_from_cnf(prop)
50
+ sat_false.add_from_cnf(_prop)
51
+
52
+ all_pred, all_exprs = get_all_pred_and_expr_from_enc_cnf(sat_true)
53
+
54
+ for pred in all_pred:
55
+ if pred.function not in WHITE_LIST and pred.function != Q.ne:
56
+ raise UnhandledInput(f"LRASolver: {pred} is an unhandled predicate")
57
+ for expr in all_exprs:
58
+ if expr.kind == MatrixKind(NumberKind):
59
+ raise UnhandledInput(f"LRASolver: {expr} is of MatrixKind")
60
+ if expr == S.NaN:
61
+ raise UnhandledInput("LRASolver: nan")
62
+
63
+ # convert old assumptions into predicates and add them to sat_true and sat_false
64
+ # also check for unhandled predicates
65
+ for assm in extract_pred_from_old_assum(all_exprs):
66
+ n = len(sat_true.encoding)
67
+ if assm not in sat_true.encoding:
68
+ sat_true.encoding[assm] = n+1
69
+ sat_true.data.append([sat_true.encoding[assm]])
70
+
71
+ n = len(sat_false.encoding)
72
+ if assm not in sat_false.encoding:
73
+ sat_false.encoding[assm] = n+1
74
+ sat_false.data.append([sat_false.encoding[assm]])
75
+
76
+
77
+ sat_true = _preprocess(sat_true)
78
+ sat_false = _preprocess(sat_false)
79
+
80
+ can_be_true = satisfiable(sat_true, use_lra_theory=True) is not False
81
+ can_be_false = satisfiable(sat_false, use_lra_theory=True) is not False
82
+
83
+ if can_be_true and can_be_false:
84
+ return None
85
+
86
+ if can_be_true and not can_be_false:
87
+ return True
88
+
89
+ if not can_be_true and can_be_false:
90
+ return False
91
+
92
+ if not can_be_true and not can_be_false:
93
+ raise ValueError("Inconsistent assumptions")
94
+
95
+
96
+ def _preprocess(enc_cnf):
97
+ """
98
+ Returns an encoded cnf with only Q.eq, Q.gt, Q.lt,
99
+ Q.ge, and Q.le predicate.
100
+
101
+ Converts every unequality into a disjunction of strict
102
+ inequalities. For example, x != 3 would become
103
+ x < 3 OR x > 3.
104
+
105
+ Also converts all negated Q.ne predicates into
106
+ equalities.
107
+ """
108
+
109
+ # loops through each literal in each clause
110
+ # to construct a new, preprocessed encodedCNF
111
+
112
+ enc_cnf = enc_cnf.copy()
113
+ cur_enc = 1
114
+ rev_encoding = {value: key for key, value in enc_cnf.encoding.items()}
115
+
116
+ new_encoding = {}
117
+ new_data = []
118
+ for clause in enc_cnf.data:
119
+ new_clause = []
120
+ for lit in clause:
121
+ if lit == 0:
122
+ new_clause.append(lit)
123
+ new_encoding[lit] = False
124
+ continue
125
+ prop = rev_encoding[abs(lit)]
126
+ negated = lit < 0
127
+ sign = (lit > 0) - (lit < 0)
128
+
129
+ prop = _pred_to_binrel(prop)
130
+
131
+ if not isinstance(prop, AppliedPredicate):
132
+ if prop not in new_encoding:
133
+ new_encoding[prop] = cur_enc
134
+ cur_enc += 1
135
+ lit = new_encoding[prop]
136
+ new_clause.append(sign*lit)
137
+ continue
138
+
139
+
140
+ if negated and prop.function == Q.eq:
141
+ negated = False
142
+ prop = Q.ne(*prop.arguments)
143
+
144
+ if prop.function == Q.ne:
145
+ arg1, arg2 = prop.arguments
146
+ if negated:
147
+ new_prop = Q.eq(arg1, arg2)
148
+ if new_prop not in new_encoding:
149
+ new_encoding[new_prop] = cur_enc
150
+ cur_enc += 1
151
+
152
+ new_enc = new_encoding[new_prop]
153
+ new_clause.append(new_enc)
154
+ continue
155
+ else:
156
+ new_props = (Q.gt(arg1, arg2), Q.lt(arg1, arg2))
157
+ for new_prop in new_props:
158
+ if new_prop not in new_encoding:
159
+ new_encoding[new_prop] = cur_enc
160
+ cur_enc += 1
161
+
162
+ new_enc = new_encoding[new_prop]
163
+ new_clause.append(new_enc)
164
+ continue
165
+
166
+ if prop.function == Q.eq and negated:
167
+ assert False
168
+
169
+ if prop not in new_encoding:
170
+ new_encoding[prop] = cur_enc
171
+ cur_enc += 1
172
+ new_clause.append(new_encoding[prop]*sign)
173
+ new_data.append(new_clause)
174
+
175
+ assert len(new_encoding) >= cur_enc - 1
176
+
177
+ enc_cnf = EncodedCNF(new_data, new_encoding)
178
+ return enc_cnf
179
+
180
+
181
+ def _pred_to_binrel(pred):
182
+ if not isinstance(pred, AppliedPredicate):
183
+ return pred
184
+
185
+ if pred.function in pred_to_pos_neg_zero:
186
+ f = pred_to_pos_neg_zero[pred.function]
187
+ if f is False:
188
+ return False
189
+ pred = f(pred.arguments[0])
190
+
191
+ if pred.function == Q.positive:
192
+ pred = Q.gt(pred.arguments[0], 0)
193
+ elif pred.function == Q.negative:
194
+ pred = Q.lt(pred.arguments[0], 0)
195
+ elif pred.function == Q.zero:
196
+ pred = Q.eq(pred.arguments[0], 0)
197
+ elif pred.function == Q.nonpositive:
198
+ pred = Q.le(pred.arguments[0], 0)
199
+ elif pred.function == Q.nonnegative:
200
+ pred = Q.ge(pred.arguments[0], 0)
201
+ elif pred.function == Q.nonzero:
202
+ pred = Q.ne(pred.arguments[0], 0)
203
+
204
+ return pred
205
+
206
+ pred_to_pos_neg_zero = {
207
+ Q.extended_positive: Q.positive,
208
+ Q.extended_negative: Q.negative,
209
+ Q.extended_nonpositive: Q.nonpositive,
210
+ Q.extended_negative: Q.negative,
211
+ Q.extended_nonzero: Q.nonzero,
212
+ Q.negative_infinite: False,
213
+ Q.positive_infinite: False
214
+ }
215
+
216
+ def get_all_pred_and_expr_from_enc_cnf(enc_cnf):
217
+ all_exprs = set()
218
+ all_pred = set()
219
+ for pred in enc_cnf.encoding.keys():
220
+ if isinstance(pred, AppliedPredicate):
221
+ all_pred.add(pred)
222
+ all_exprs.update(pred.arguments)
223
+
224
+ return all_pred, all_exprs
225
+
226
+ def extract_pred_from_old_assum(all_exprs):
227
+ """
228
+ Returns a list of relevant new assumption predicate
229
+ based on any old assumptions.
230
+
231
+ Raises an UnhandledInput exception if any of the assumptions are
232
+ unhandled.
233
+
234
+ Ignored predicate:
235
+ - commutative
236
+ - complex
237
+ - algebraic
238
+ - transcendental
239
+ - extended_real
240
+ - real
241
+ - all matrix predicate
242
+ - rational
243
+ - irrational
244
+
245
+ Example
246
+ =======
247
+ >>> from sympy.assumptions.lra_satask import extract_pred_from_old_assum
248
+ >>> from sympy import symbols
249
+ >>> x, y = symbols("x y", positive=True)
250
+ >>> extract_pred_from_old_assum([x, y, 2])
251
+ [Q.positive(x), Q.positive(y)]
252
+ """
253
+ ret = []
254
+ for expr in all_exprs:
255
+ if not hasattr(expr, "free_symbols"):
256
+ continue
257
+ if len(expr.free_symbols) == 0:
258
+ continue
259
+
260
+ if expr.is_real is not True:
261
+ raise UnhandledInput(f"LRASolver: {expr} must be real")
262
+ # test for I times imaginary variable; such expressions are considered real
263
+ if isinstance(expr, Mul) and any(arg.is_real is not True for arg in expr.args):
264
+ raise UnhandledInput(f"LRASolver: {expr} must be real")
265
+
266
+ if expr.is_integer == True and expr.is_zero != True:
267
+ raise UnhandledInput(f"LRASolver: {expr} is an integer")
268
+ if expr.is_integer == False:
269
+ raise UnhandledInput(f"LRASolver: {expr} can't be an integer")
270
+ if expr.is_rational == False:
271
+ raise UnhandledInput(f"LRASolver: {expr} is irational")
272
+
273
+ if expr.is_zero:
274
+ ret.append(Q.zero(expr))
275
+ elif expr.is_positive:
276
+ ret.append(Q.positive(expr))
277
+ elif expr.is_negative:
278
+ ret.append(Q.negative(expr))
279
+ elif expr.is_nonzero:
280
+ ret.append(Q.nonzero(expr))
281
+ elif expr.is_nonpositive:
282
+ ret.append(Q.nonpositive(expr))
283
+ elif expr.is_nonnegative:
284
+ ret.append(Q.nonnegative(expr))
285
+
286
+ return ret
lib/python3.12/site-packages/sympy/assumptions/refine.py ADDED
@@ -0,0 +1,405 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+ from typing import Callable
3
+
4
+ from sympy.core import S, Add, Expr, Basic, Mul, Pow, Rational
5
+ from sympy.core.logic import fuzzy_not
6
+ from sympy.logic.boolalg import Boolean
7
+
8
+ from sympy.assumptions import ask, Q # type: ignore
9
+
10
+
11
+ def refine(expr, assumptions=True):
12
+ """
13
+ Simplify an expression using assumptions.
14
+
15
+ Explanation
16
+ ===========
17
+
18
+ Unlike :func:`~.simplify()` which performs structural simplification
19
+ without any assumption, this function transforms the expression into
20
+ the form which is only valid under certain assumptions. Note that
21
+ ``simplify()`` is generally not done in refining process.
22
+
23
+ Refining boolean expression involves reducing it to ``S.true`` or
24
+ ``S.false``. Unlike :func:`~.ask()`, the expression will not be reduced
25
+ if the truth value cannot be determined.
26
+
27
+ Examples
28
+ ========
29
+
30
+ >>> from sympy import refine, sqrt, Q
31
+ >>> from sympy.abc import x
32
+ >>> refine(sqrt(x**2), Q.real(x))
33
+ Abs(x)
34
+ >>> refine(sqrt(x**2), Q.positive(x))
35
+ x
36
+
37
+ >>> refine(Q.real(x), Q.positive(x))
38
+ True
39
+ >>> refine(Q.positive(x), Q.real(x))
40
+ Q.positive(x)
41
+
42
+ See Also
43
+ ========
44
+
45
+ sympy.simplify.simplify.simplify : Structural simplification without assumptions.
46
+ sympy.assumptions.ask.ask : Query for boolean expressions using assumptions.
47
+ """
48
+ if not isinstance(expr, Basic):
49
+ return expr
50
+
51
+ if not expr.is_Atom:
52
+ args = [refine(arg, assumptions) for arg in expr.args]
53
+ # TODO: this will probably not work with Integral or Polynomial
54
+ expr = expr.func(*args)
55
+ if hasattr(expr, '_eval_refine'):
56
+ ref_expr = expr._eval_refine(assumptions)
57
+ if ref_expr is not None:
58
+ return ref_expr
59
+ name = expr.__class__.__name__
60
+ handler = handlers_dict.get(name, None)
61
+ if handler is None:
62
+ return expr
63
+ new_expr = handler(expr, assumptions)
64
+ if (new_expr is None) or (expr == new_expr):
65
+ return expr
66
+ if not isinstance(new_expr, Expr):
67
+ return new_expr
68
+ return refine(new_expr, assumptions)
69
+
70
+
71
+ def refine_abs(expr, assumptions):
72
+ """
73
+ Handler for the absolute value.
74
+
75
+ Examples
76
+ ========
77
+
78
+ >>> from sympy import Q, Abs
79
+ >>> from sympy.assumptions.refine import refine_abs
80
+ >>> from sympy.abc import x
81
+ >>> refine_abs(Abs(x), Q.real(x))
82
+ >>> refine_abs(Abs(x), Q.positive(x))
83
+ x
84
+ >>> refine_abs(Abs(x), Q.negative(x))
85
+ -x
86
+
87
+ """
88
+ from sympy.functions.elementary.complexes import Abs
89
+ arg = expr.args[0]
90
+ if ask(Q.real(arg), assumptions) and \
91
+ fuzzy_not(ask(Q.negative(arg), assumptions)):
92
+ # if it's nonnegative
93
+ return arg
94
+ if ask(Q.negative(arg), assumptions):
95
+ return -arg
96
+ # arg is Mul
97
+ if isinstance(arg, Mul):
98
+ r = [refine(abs(a), assumptions) for a in arg.args]
99
+ non_abs = []
100
+ in_abs = []
101
+ for i in r:
102
+ if isinstance(i, Abs):
103
+ in_abs.append(i.args[0])
104
+ else:
105
+ non_abs.append(i)
106
+ return Mul(*non_abs) * Abs(Mul(*in_abs))
107
+
108
+
109
+ def refine_Pow(expr, assumptions):
110
+ """
111
+ Handler for instances of Pow.
112
+
113
+ Examples
114
+ ========
115
+
116
+ >>> from sympy import Q
117
+ >>> from sympy.assumptions.refine import refine_Pow
118
+ >>> from sympy.abc import x,y,z
119
+ >>> refine_Pow((-1)**x, Q.real(x))
120
+ >>> refine_Pow((-1)**x, Q.even(x))
121
+ 1
122
+ >>> refine_Pow((-1)**x, Q.odd(x))
123
+ -1
124
+
125
+ For powers of -1, even parts of the exponent can be simplified:
126
+
127
+ >>> refine_Pow((-1)**(x+y), Q.even(x))
128
+ (-1)**y
129
+ >>> refine_Pow((-1)**(x+y+z), Q.odd(x) & Q.odd(z))
130
+ (-1)**y
131
+ >>> refine_Pow((-1)**(x+y+2), Q.odd(x))
132
+ (-1)**(y + 1)
133
+ >>> refine_Pow((-1)**(x+3), True)
134
+ (-1)**(x + 1)
135
+
136
+ """
137
+ from sympy.functions.elementary.complexes import Abs
138
+ from sympy.functions import sign
139
+ if isinstance(expr.base, Abs):
140
+ if ask(Q.real(expr.base.args[0]), assumptions) and \
141
+ ask(Q.even(expr.exp), assumptions):
142
+ return expr.base.args[0] ** expr.exp
143
+ if ask(Q.real(expr.base), assumptions):
144
+ if expr.base.is_number:
145
+ if ask(Q.even(expr.exp), assumptions):
146
+ return abs(expr.base) ** expr.exp
147
+ if ask(Q.odd(expr.exp), assumptions):
148
+ return sign(expr.base) * abs(expr.base) ** expr.exp
149
+ if isinstance(expr.exp, Rational):
150
+ if isinstance(expr.base, Pow):
151
+ return abs(expr.base.base) ** (expr.base.exp * expr.exp)
152
+
153
+ if expr.base is S.NegativeOne:
154
+ if expr.exp.is_Add:
155
+
156
+ old = expr
157
+
158
+ # For powers of (-1) we can remove
159
+ # - even terms
160
+ # - pairs of odd terms
161
+ # - a single odd term + 1
162
+ # - A numerical constant N can be replaced with mod(N,2)
163
+
164
+ coeff, terms = expr.exp.as_coeff_add()
165
+ terms = set(terms)
166
+ even_terms = set()
167
+ odd_terms = set()
168
+ initial_number_of_terms = len(terms)
169
+
170
+ for t in terms:
171
+ if ask(Q.even(t), assumptions):
172
+ even_terms.add(t)
173
+ elif ask(Q.odd(t), assumptions):
174
+ odd_terms.add(t)
175
+
176
+ terms -= even_terms
177
+ if len(odd_terms) % 2:
178
+ terms -= odd_terms
179
+ new_coeff = (coeff + S.One) % 2
180
+ else:
181
+ terms -= odd_terms
182
+ new_coeff = coeff % 2
183
+
184
+ if new_coeff != coeff or len(terms) < initial_number_of_terms:
185
+ terms.add(new_coeff)
186
+ expr = expr.base**(Add(*terms))
187
+
188
+ # Handle (-1)**((-1)**n/2 + m/2)
189
+ e2 = 2*expr.exp
190
+ if ask(Q.even(e2), assumptions):
191
+ if e2.could_extract_minus_sign():
192
+ e2 *= expr.base
193
+ if e2.is_Add:
194
+ i, p = e2.as_two_terms()
195
+ if p.is_Pow and p.base is S.NegativeOne:
196
+ if ask(Q.integer(p.exp), assumptions):
197
+ i = (i + 1)/2
198
+ if ask(Q.even(i), assumptions):
199
+ return expr.base**p.exp
200
+ elif ask(Q.odd(i), assumptions):
201
+ return expr.base**(p.exp + 1)
202
+ else:
203
+ return expr.base**(p.exp + i)
204
+
205
+ if old != expr:
206
+ return expr
207
+
208
+
209
+ def refine_atan2(expr, assumptions):
210
+ """
211
+ Handler for the atan2 function.
212
+
213
+ Examples
214
+ ========
215
+
216
+ >>> from sympy import Q, atan2
217
+ >>> from sympy.assumptions.refine import refine_atan2
218
+ >>> from sympy.abc import x, y
219
+ >>> refine_atan2(atan2(y,x), Q.real(y) & Q.positive(x))
220
+ atan(y/x)
221
+ >>> refine_atan2(atan2(y,x), Q.negative(y) & Q.negative(x))
222
+ atan(y/x) - pi
223
+ >>> refine_atan2(atan2(y,x), Q.positive(y) & Q.negative(x))
224
+ atan(y/x) + pi
225
+ >>> refine_atan2(atan2(y,x), Q.zero(y) & Q.negative(x))
226
+ pi
227
+ >>> refine_atan2(atan2(y,x), Q.positive(y) & Q.zero(x))
228
+ pi/2
229
+ >>> refine_atan2(atan2(y,x), Q.negative(y) & Q.zero(x))
230
+ -pi/2
231
+ >>> refine_atan2(atan2(y,x), Q.zero(y) & Q.zero(x))
232
+ nan
233
+ """
234
+ from sympy.functions.elementary.trigonometric import atan
235
+ y, x = expr.args
236
+ if ask(Q.real(y) & Q.positive(x), assumptions):
237
+ return atan(y / x)
238
+ elif ask(Q.negative(y) & Q.negative(x), assumptions):
239
+ return atan(y / x) - S.Pi
240
+ elif ask(Q.positive(y) & Q.negative(x), assumptions):
241
+ return atan(y / x) + S.Pi
242
+ elif ask(Q.zero(y) & Q.negative(x), assumptions):
243
+ return S.Pi
244
+ elif ask(Q.positive(y) & Q.zero(x), assumptions):
245
+ return S.Pi/2
246
+ elif ask(Q.negative(y) & Q.zero(x), assumptions):
247
+ return -S.Pi/2
248
+ elif ask(Q.zero(y) & Q.zero(x), assumptions):
249
+ return S.NaN
250
+ else:
251
+ return expr
252
+
253
+
254
+ def refine_re(expr, assumptions):
255
+ """
256
+ Handler for real part.
257
+
258
+ Examples
259
+ ========
260
+
261
+ >>> from sympy.assumptions.refine import refine_re
262
+ >>> from sympy import Q, re
263
+ >>> from sympy.abc import x
264
+ >>> refine_re(re(x), Q.real(x))
265
+ x
266
+ >>> refine_re(re(x), Q.imaginary(x))
267
+ 0
268
+ """
269
+ arg = expr.args[0]
270
+ if ask(Q.real(arg), assumptions):
271
+ return arg
272
+ if ask(Q.imaginary(arg), assumptions):
273
+ return S.Zero
274
+ return _refine_reim(expr, assumptions)
275
+
276
+
277
+ def refine_im(expr, assumptions):
278
+ """
279
+ Handler for imaginary part.
280
+
281
+ Explanation
282
+ ===========
283
+
284
+ >>> from sympy.assumptions.refine import refine_im
285
+ >>> from sympy import Q, im
286
+ >>> from sympy.abc import x
287
+ >>> refine_im(im(x), Q.real(x))
288
+ 0
289
+ >>> refine_im(im(x), Q.imaginary(x))
290
+ -I*x
291
+ """
292
+ arg = expr.args[0]
293
+ if ask(Q.real(arg), assumptions):
294
+ return S.Zero
295
+ if ask(Q.imaginary(arg), assumptions):
296
+ return - S.ImaginaryUnit * arg
297
+ return _refine_reim(expr, assumptions)
298
+
299
+ def refine_arg(expr, assumptions):
300
+ """
301
+ Handler for complex argument
302
+
303
+ Explanation
304
+ ===========
305
+
306
+ >>> from sympy.assumptions.refine import refine_arg
307
+ >>> from sympy import Q, arg
308
+ >>> from sympy.abc import x
309
+ >>> refine_arg(arg(x), Q.positive(x))
310
+ 0
311
+ >>> refine_arg(arg(x), Q.negative(x))
312
+ pi
313
+ """
314
+ rg = expr.args[0]
315
+ if ask(Q.positive(rg), assumptions):
316
+ return S.Zero
317
+ if ask(Q.negative(rg), assumptions):
318
+ return S.Pi
319
+ return None
320
+
321
+
322
+ def _refine_reim(expr, assumptions):
323
+ # Helper function for refine_re & refine_im
324
+ expanded = expr.expand(complex = True)
325
+ if expanded != expr:
326
+ refined = refine(expanded, assumptions)
327
+ if refined != expanded:
328
+ return refined
329
+ # Best to leave the expression as is
330
+ return None
331
+
332
+
333
+ def refine_sign(expr, assumptions):
334
+ """
335
+ Handler for sign.
336
+
337
+ Examples
338
+ ========
339
+
340
+ >>> from sympy.assumptions.refine import refine_sign
341
+ >>> from sympy import Symbol, Q, sign, im
342
+ >>> x = Symbol('x', real = True)
343
+ >>> expr = sign(x)
344
+ >>> refine_sign(expr, Q.positive(x) & Q.nonzero(x))
345
+ 1
346
+ >>> refine_sign(expr, Q.negative(x) & Q.nonzero(x))
347
+ -1
348
+ >>> refine_sign(expr, Q.zero(x))
349
+ 0
350
+ >>> y = Symbol('y', imaginary = True)
351
+ >>> expr = sign(y)
352
+ >>> refine_sign(expr, Q.positive(im(y)))
353
+ I
354
+ >>> refine_sign(expr, Q.negative(im(y)))
355
+ -I
356
+ """
357
+ arg = expr.args[0]
358
+ if ask(Q.zero(arg), assumptions):
359
+ return S.Zero
360
+ if ask(Q.real(arg)):
361
+ if ask(Q.positive(arg), assumptions):
362
+ return S.One
363
+ if ask(Q.negative(arg), assumptions):
364
+ return S.NegativeOne
365
+ if ask(Q.imaginary(arg)):
366
+ arg_re, arg_im = arg.as_real_imag()
367
+ if ask(Q.positive(arg_im), assumptions):
368
+ return S.ImaginaryUnit
369
+ if ask(Q.negative(arg_im), assumptions):
370
+ return -S.ImaginaryUnit
371
+ return expr
372
+
373
+
374
+ def refine_matrixelement(expr, assumptions):
375
+ """
376
+ Handler for symmetric part.
377
+
378
+ Examples
379
+ ========
380
+
381
+ >>> from sympy.assumptions.refine import refine_matrixelement
382
+ >>> from sympy import MatrixSymbol, Q
383
+ >>> X = MatrixSymbol('X', 3, 3)
384
+ >>> refine_matrixelement(X[0, 1], Q.symmetric(X))
385
+ X[0, 1]
386
+ >>> refine_matrixelement(X[1, 0], Q.symmetric(X))
387
+ X[0, 1]
388
+ """
389
+ from sympy.matrices.expressions.matexpr import MatrixElement
390
+ matrix, i, j = expr.args
391
+ if ask(Q.symmetric(matrix), assumptions):
392
+ if (i - j).could_extract_minus_sign():
393
+ return expr
394
+ return MatrixElement(matrix, j, i)
395
+
396
+ handlers_dict: dict[str, Callable[[Expr, Boolean], Expr]] = {
397
+ 'Abs': refine_abs,
398
+ 'Pow': refine_Pow,
399
+ 'atan2': refine_atan2,
400
+ 're': refine_re,
401
+ 'im': refine_im,
402
+ 'arg': refine_arg,
403
+ 'sign': refine_sign,
404
+ 'MatrixElement': refine_matrixelement
405
+ }
lib/python3.12/site-packages/sympy/assumptions/satask.py ADDED
@@ -0,0 +1,369 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Module to evaluate the proposition with assumptions using SAT algorithm.
3
+ """
4
+
5
+ from sympy.core.singleton import S
6
+ from sympy.core.symbol import Symbol
7
+ from sympy.core.kind import NumberKind, UndefinedKind
8
+ from sympy.assumptions.ask_generated import get_all_known_matrix_facts, get_all_known_number_facts
9
+ from sympy.assumptions.assume import global_assumptions, AppliedPredicate
10
+ from sympy.assumptions.sathandlers import class_fact_registry
11
+ from sympy.core import oo
12
+ from sympy.logic.inference import satisfiable
13
+ from sympy.assumptions.cnf import CNF, EncodedCNF
14
+ from sympy.matrices.kind import MatrixKind
15
+
16
+
17
+ def satask(proposition, assumptions=True, context=global_assumptions,
18
+ use_known_facts=True, iterations=oo):
19
+ """
20
+ Function to evaluate the proposition with assumptions using SAT algorithm.
21
+
22
+ This function extracts every fact relevant to the expressions composing
23
+ proposition and assumptions. For example, if a predicate containing
24
+ ``Abs(x)`` is proposed, then ``Q.zero(Abs(x)) | Q.positive(Abs(x))``
25
+ will be found and passed to SAT solver because ``Q.nonnegative`` is
26
+ registered as a fact for ``Abs``.
27
+
28
+ Proposition is evaluated to ``True`` or ``False`` if the truth value can be
29
+ determined. If not, ``None`` is returned.
30
+
31
+ Parameters
32
+ ==========
33
+
34
+ proposition : Any boolean expression.
35
+ Proposition which will be evaluated to boolean value.
36
+
37
+ assumptions : Any boolean expression, optional.
38
+ Local assumptions to evaluate the *proposition*.
39
+
40
+ context : AssumptionsContext, optional.
41
+ Default assumptions to evaluate the *proposition*. By default,
42
+ this is ``sympy.assumptions.global_assumptions`` variable.
43
+
44
+ use_known_facts : bool, optional.
45
+ If ``True``, facts from ``sympy.assumptions.ask_generated``
46
+ module are passed to SAT solver as well.
47
+
48
+ iterations : int, optional.
49
+ Number of times that relevant facts are recursively extracted.
50
+ Default is infinite times until no new fact is found.
51
+
52
+ Returns
53
+ =======
54
+
55
+ ``True``, ``False``, or ``None``
56
+
57
+ Examples
58
+ ========
59
+
60
+ >>> from sympy import Abs, Q
61
+ >>> from sympy.assumptions.satask import satask
62
+ >>> from sympy.abc import x
63
+ >>> satask(Q.zero(Abs(x)), Q.zero(x))
64
+ True
65
+
66
+ """
67
+ props = CNF.from_prop(proposition)
68
+ _props = CNF.from_prop(~proposition)
69
+
70
+ assumptions = CNF.from_prop(assumptions)
71
+
72
+ context_cnf = CNF()
73
+ if context:
74
+ context_cnf = context_cnf.extend(context)
75
+
76
+ sat = get_all_relevant_facts(props, assumptions, context_cnf,
77
+ use_known_facts=use_known_facts, iterations=iterations)
78
+ sat.add_from_cnf(assumptions)
79
+ if context:
80
+ sat.add_from_cnf(context_cnf)
81
+
82
+ return check_satisfiability(props, _props, sat)
83
+
84
+
85
+ def check_satisfiability(prop, _prop, factbase):
86
+ sat_true = factbase.copy()
87
+ sat_false = factbase.copy()
88
+ sat_true.add_from_cnf(prop)
89
+ sat_false.add_from_cnf(_prop)
90
+ can_be_true = satisfiable(sat_true)
91
+ can_be_false = satisfiable(sat_false)
92
+
93
+ if can_be_true and can_be_false:
94
+ return None
95
+
96
+ if can_be_true and not can_be_false:
97
+ return True
98
+
99
+ if not can_be_true and can_be_false:
100
+ return False
101
+
102
+ if not can_be_true and not can_be_false:
103
+ # TODO: Run additional checks to see which combination of the
104
+ # assumptions, global_assumptions, and relevant_facts are
105
+ # inconsistent.
106
+ raise ValueError("Inconsistent assumptions")
107
+
108
+
109
+ def extract_predargs(proposition, assumptions=None, context=None):
110
+ """
111
+ Extract every expression in the argument of predicates from *proposition*,
112
+ *assumptions* and *context*.
113
+
114
+ Parameters
115
+ ==========
116
+
117
+ proposition : sympy.assumptions.cnf.CNF
118
+
119
+ assumptions : sympy.assumptions.cnf.CNF, optional.
120
+
121
+ context : sympy.assumptions.cnf.CNF, optional.
122
+ CNF generated from assumptions context.
123
+
124
+ Examples
125
+ ========
126
+
127
+ >>> from sympy import Q, Abs
128
+ >>> from sympy.assumptions.cnf import CNF
129
+ >>> from sympy.assumptions.satask import extract_predargs
130
+ >>> from sympy.abc import x, y
131
+ >>> props = CNF.from_prop(Q.zero(Abs(x*y)))
132
+ >>> assump = CNF.from_prop(Q.zero(x) & Q.zero(y))
133
+ >>> extract_predargs(props, assump)
134
+ {x, y, Abs(x*y)}
135
+
136
+ """
137
+ req_keys = find_symbols(proposition)
138
+ keys = proposition.all_predicates()
139
+ # XXX: We need this since True/False are not Basic
140
+ lkeys = set()
141
+ if assumptions:
142
+ lkeys |= assumptions.all_predicates()
143
+ if context:
144
+ lkeys |= context.all_predicates()
145
+
146
+ lkeys = lkeys - {S.true, S.false}
147
+ tmp_keys = None
148
+ while tmp_keys != set():
149
+ tmp = set()
150
+ for l in lkeys:
151
+ syms = find_symbols(l)
152
+ if (syms & req_keys) != set():
153
+ tmp |= syms
154
+ tmp_keys = tmp - req_keys
155
+ req_keys |= tmp_keys
156
+ keys |= {l for l in lkeys if find_symbols(l) & req_keys != set()}
157
+
158
+ exprs = set()
159
+ for key in keys:
160
+ if isinstance(key, AppliedPredicate):
161
+ exprs |= set(key.arguments)
162
+ else:
163
+ exprs.add(key)
164
+ return exprs
165
+
166
+ def find_symbols(pred):
167
+ """
168
+ Find every :obj:`~.Symbol` in *pred*.
169
+
170
+ Parameters
171
+ ==========
172
+
173
+ pred : sympy.assumptions.cnf.CNF, or any Expr.
174
+
175
+ """
176
+ if isinstance(pred, CNF):
177
+ symbols = set()
178
+ for a in pred.all_predicates():
179
+ symbols |= find_symbols(a)
180
+ return symbols
181
+ return pred.atoms(Symbol)
182
+
183
+
184
+ def get_relevant_clsfacts(exprs, relevant_facts=None):
185
+ """
186
+ Extract relevant facts from the items in *exprs*. Facts are defined in
187
+ ``assumptions.sathandlers`` module.
188
+
189
+ This function is recursively called by ``get_all_relevant_facts()``.
190
+
191
+ Parameters
192
+ ==========
193
+
194
+ exprs : set
195
+ Expressions whose relevant facts are searched.
196
+
197
+ relevant_facts : sympy.assumptions.cnf.CNF, optional.
198
+ Pre-discovered relevant facts.
199
+
200
+ Returns
201
+ =======
202
+
203
+ exprs : set
204
+ Candidates for next relevant fact searching.
205
+
206
+ relevant_facts : sympy.assumptions.cnf.CNF
207
+ Updated relevant facts.
208
+
209
+ Examples
210
+ ========
211
+
212
+ Here, we will see how facts relevant to ``Abs(x*y)`` are recursively
213
+ extracted. On the first run, set containing the expression is passed
214
+ without pre-discovered relevant facts. The result is a set containing
215
+ candidates for next run, and ``CNF()`` instance containing facts
216
+ which are relevant to ``Abs`` and its argument.
217
+
218
+ >>> from sympy import Abs
219
+ >>> from sympy.assumptions.satask import get_relevant_clsfacts
220
+ >>> from sympy.abc import x, y
221
+ >>> exprs = {Abs(x*y)}
222
+ >>> exprs, facts = get_relevant_clsfacts(exprs)
223
+ >>> exprs
224
+ {x*y}
225
+ >>> facts.clauses #doctest: +SKIP
226
+ {frozenset({Literal(Q.odd(Abs(x*y)), False), Literal(Q.odd(x*y), True)}),
227
+ frozenset({Literal(Q.zero(Abs(x*y)), False), Literal(Q.zero(x*y), True)}),
228
+ frozenset({Literal(Q.even(Abs(x*y)), False), Literal(Q.even(x*y), True)}),
229
+ frozenset({Literal(Q.zero(Abs(x*y)), True), Literal(Q.zero(x*y), False)}),
230
+ frozenset({Literal(Q.even(Abs(x*y)), False),
231
+ Literal(Q.odd(Abs(x*y)), False),
232
+ Literal(Q.odd(x*y), True)}),
233
+ frozenset({Literal(Q.even(Abs(x*y)), False),
234
+ Literal(Q.even(x*y), True),
235
+ Literal(Q.odd(Abs(x*y)), False)}),
236
+ frozenset({Literal(Q.positive(Abs(x*y)), False),
237
+ Literal(Q.zero(Abs(x*y)), False)})}
238
+
239
+ We pass the first run's results to the second run, and get the expressions
240
+ for next run and updated facts.
241
+
242
+ >>> exprs, facts = get_relevant_clsfacts(exprs, relevant_facts=facts)
243
+ >>> exprs
244
+ {x, y}
245
+
246
+ On final run, no more candidate is returned thus we know that all
247
+ relevant facts are successfully retrieved.
248
+
249
+ >>> exprs, facts = get_relevant_clsfacts(exprs, relevant_facts=facts)
250
+ >>> exprs
251
+ set()
252
+
253
+ """
254
+ if not relevant_facts:
255
+ relevant_facts = CNF()
256
+
257
+ newexprs = set()
258
+ for expr in exprs:
259
+ for fact in class_fact_registry(expr):
260
+ newfact = CNF.to_CNF(fact)
261
+ relevant_facts = relevant_facts._and(newfact)
262
+ for key in newfact.all_predicates():
263
+ if isinstance(key, AppliedPredicate):
264
+ newexprs |= set(key.arguments)
265
+
266
+ return newexprs - exprs, relevant_facts
267
+
268
+
269
+ def get_all_relevant_facts(proposition, assumptions, context,
270
+ use_known_facts=True, iterations=oo):
271
+ """
272
+ Extract all relevant facts from *proposition* and *assumptions*.
273
+
274
+ This function extracts the facts by recursively calling
275
+ ``get_relevant_clsfacts()``. Extracted facts are converted to
276
+ ``EncodedCNF`` and returned.
277
+
278
+ Parameters
279
+ ==========
280
+
281
+ proposition : sympy.assumptions.cnf.CNF
282
+ CNF generated from proposition expression.
283
+
284
+ assumptions : sympy.assumptions.cnf.CNF
285
+ CNF generated from assumption expression.
286
+
287
+ context : sympy.assumptions.cnf.CNF
288
+ CNF generated from assumptions context.
289
+
290
+ use_known_facts : bool, optional.
291
+ If ``True``, facts from ``sympy.assumptions.ask_generated``
292
+ module are encoded as well.
293
+
294
+ iterations : int, optional.
295
+ Number of times that relevant facts are recursively extracted.
296
+ Default is infinite times until no new fact is found.
297
+
298
+ Returns
299
+ =======
300
+
301
+ sympy.assumptions.cnf.EncodedCNF
302
+
303
+ Examples
304
+ ========
305
+
306
+ >>> from sympy import Q
307
+ >>> from sympy.assumptions.cnf import CNF
308
+ >>> from sympy.assumptions.satask import get_all_relevant_facts
309
+ >>> from sympy.abc import x, y
310
+ >>> props = CNF.from_prop(Q.nonzero(x*y))
311
+ >>> assump = CNF.from_prop(Q.nonzero(x))
312
+ >>> context = CNF.from_prop(Q.nonzero(y))
313
+ >>> get_all_relevant_facts(props, assump, context) #doctest: +SKIP
314
+ <sympy.assumptions.cnf.EncodedCNF at 0x7f09faa6ccd0>
315
+
316
+ """
317
+ # The relevant facts might introduce new keys, e.g., Q.zero(x*y) will
318
+ # introduce the keys Q.zero(x) and Q.zero(y), so we need to run it until
319
+ # we stop getting new things. Hopefully this strategy won't lead to an
320
+ # infinite loop in the future.
321
+ i = 0
322
+ relevant_facts = CNF()
323
+ all_exprs = set()
324
+ while True:
325
+ if i == 0:
326
+ exprs = extract_predargs(proposition, assumptions, context)
327
+ all_exprs |= exprs
328
+ exprs, relevant_facts = get_relevant_clsfacts(exprs, relevant_facts)
329
+ i += 1
330
+ if i >= iterations:
331
+ break
332
+ if not exprs:
333
+ break
334
+
335
+ if use_known_facts:
336
+ known_facts_CNF = CNF()
337
+
338
+ if any(expr.kind == MatrixKind(NumberKind) for expr in all_exprs):
339
+ known_facts_CNF.add_clauses(get_all_known_matrix_facts())
340
+ # check for undefinedKind since kind system isn't fully implemented
341
+ if any(((expr.kind == NumberKind) or (expr.kind == UndefinedKind)) for expr in all_exprs):
342
+ known_facts_CNF.add_clauses(get_all_known_number_facts())
343
+
344
+ kf_encoded = EncodedCNF()
345
+ kf_encoded.from_cnf(known_facts_CNF)
346
+
347
+ def translate_literal(lit, delta):
348
+ if lit > 0:
349
+ return lit + delta
350
+ else:
351
+ return lit - delta
352
+
353
+ def translate_data(data, delta):
354
+ return [{translate_literal(i, delta) for i in clause} for clause in data]
355
+ data = []
356
+ symbols = []
357
+ n_lit = len(kf_encoded.symbols)
358
+ for i, expr in enumerate(all_exprs):
359
+ symbols += [pred(expr) for pred in kf_encoded.symbols]
360
+ data += translate_data(kf_encoded.data, i * n_lit)
361
+
362
+ encoding = dict(list(zip(symbols, range(1, len(symbols)+1))))
363
+ ctx = EncodedCNF(data, encoding)
364
+ else:
365
+ ctx = EncodedCNF()
366
+
367
+ ctx.add_from_cnf(relevant_facts)
368
+
369
+ return ctx
lib/python3.12/site-packages/sympy/assumptions/sathandlers.py ADDED
@@ -0,0 +1,322 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from collections import defaultdict
2
+
3
+ from sympy.assumptions.ask import Q
4
+ from sympy.core import (Add, Mul, Pow, Number, NumberSymbol, Symbol)
5
+ from sympy.core.numbers import ImaginaryUnit
6
+ from sympy.functions.elementary.complexes import Abs
7
+ from sympy.logic.boolalg import (Equivalent, And, Or, Implies)
8
+ from sympy.matrices.expressions import MatMul
9
+
10
+ # APIs here may be subject to change
11
+
12
+
13
+ ### Helper functions ###
14
+
15
+ def allargs(symbol, fact, expr):
16
+ """
17
+ Apply all arguments of the expression to the fact structure.
18
+
19
+ Parameters
20
+ ==========
21
+
22
+ symbol : Symbol
23
+ A placeholder symbol.
24
+
25
+ fact : Boolean
26
+ Resulting ``Boolean`` expression.
27
+
28
+ expr : Expr
29
+
30
+ Examples
31
+ ========
32
+
33
+ >>> from sympy import Q
34
+ >>> from sympy.assumptions.sathandlers import allargs
35
+ >>> from sympy.abc import x, y
36
+ >>> allargs(x, Q.negative(x) | Q.positive(x), x*y)
37
+ (Q.negative(x) | Q.positive(x)) & (Q.negative(y) | Q.positive(y))
38
+
39
+ """
40
+ return And(*[fact.subs(symbol, arg) for arg in expr.args])
41
+
42
+
43
+ def anyarg(symbol, fact, expr):
44
+ """
45
+ Apply any argument of the expression to the fact structure.
46
+
47
+ Parameters
48
+ ==========
49
+
50
+ symbol : Symbol
51
+ A placeholder symbol.
52
+
53
+ fact : Boolean
54
+ Resulting ``Boolean`` expression.
55
+
56
+ expr : Expr
57
+
58
+ Examples
59
+ ========
60
+
61
+ >>> from sympy import Q
62
+ >>> from sympy.assumptions.sathandlers import anyarg
63
+ >>> from sympy.abc import x, y
64
+ >>> anyarg(x, Q.negative(x) & Q.positive(x), x*y)
65
+ (Q.negative(x) & Q.positive(x)) | (Q.negative(y) & Q.positive(y))
66
+
67
+ """
68
+ return Or(*[fact.subs(symbol, arg) for arg in expr.args])
69
+
70
+
71
+ def exactlyonearg(symbol, fact, expr):
72
+ """
73
+ Apply exactly one argument of the expression to the fact structure.
74
+
75
+ Parameters
76
+ ==========
77
+
78
+ symbol : Symbol
79
+ A placeholder symbol.
80
+
81
+ fact : Boolean
82
+ Resulting ``Boolean`` expression.
83
+
84
+ expr : Expr
85
+
86
+ Examples
87
+ ========
88
+
89
+ >>> from sympy import Q
90
+ >>> from sympy.assumptions.sathandlers import exactlyonearg
91
+ >>> from sympy.abc import x, y
92
+ >>> exactlyonearg(x, Q.positive(x), x*y)
93
+ (Q.positive(x) & ~Q.positive(y)) | (Q.positive(y) & ~Q.positive(x))
94
+
95
+ """
96
+ pred_args = [fact.subs(symbol, arg) for arg in expr.args]
97
+ res = Or(*[And(pred_args[i], *[~lit for lit in pred_args[:i] +
98
+ pred_args[i+1:]]) for i in range(len(pred_args))])
99
+ return res
100
+
101
+
102
+ ### Fact registry ###
103
+
104
+ class ClassFactRegistry:
105
+ """
106
+ Register handlers against classes.
107
+
108
+ Explanation
109
+ ===========
110
+
111
+ ``register`` method registers the handler function for a class. Here,
112
+ handler function should return a single fact. ``multiregister`` method
113
+ registers the handler function for multiple classes. Here, handler function
114
+ should return a container of multiple facts.
115
+
116
+ ``registry(expr)`` returns a set of facts for *expr*.
117
+
118
+ Examples
119
+ ========
120
+
121
+ Here, we register the facts for ``Abs``.
122
+
123
+ >>> from sympy import Abs, Equivalent, Q
124
+ >>> from sympy.assumptions.sathandlers import ClassFactRegistry
125
+ >>> reg = ClassFactRegistry()
126
+ >>> @reg.register(Abs)
127
+ ... def f1(expr):
128
+ ... return Q.nonnegative(expr)
129
+ >>> @reg.register(Abs)
130
+ ... def f2(expr):
131
+ ... arg = expr.args[0]
132
+ ... return Equivalent(~Q.zero(arg), ~Q.zero(expr))
133
+
134
+ Calling the registry with expression returns the defined facts for the
135
+ expression.
136
+
137
+ >>> from sympy.abc import x
138
+ >>> reg(Abs(x))
139
+ {Q.nonnegative(Abs(x)), Equivalent(~Q.zero(x), ~Q.zero(Abs(x)))}
140
+
141
+ Multiple facts can be registered at once by ``multiregister`` method.
142
+
143
+ >>> reg2 = ClassFactRegistry()
144
+ >>> @reg2.multiregister(Abs)
145
+ ... def _(expr):
146
+ ... arg = expr.args[0]
147
+ ... return [Q.even(arg) >> Q.even(expr), Q.odd(arg) >> Q.odd(expr)]
148
+ >>> reg2(Abs(x))
149
+ {Implies(Q.even(x), Q.even(Abs(x))), Implies(Q.odd(x), Q.odd(Abs(x)))}
150
+
151
+ """
152
+ def __init__(self):
153
+ self.singlefacts = defaultdict(frozenset)
154
+ self.multifacts = defaultdict(frozenset)
155
+
156
+ def register(self, cls):
157
+ def _(func):
158
+ self.singlefacts[cls] |= {func}
159
+ return func
160
+ return _
161
+
162
+ def multiregister(self, *classes):
163
+ def _(func):
164
+ for cls in classes:
165
+ self.multifacts[cls] |= {func}
166
+ return func
167
+ return _
168
+
169
+ def __getitem__(self, key):
170
+ ret1 = self.singlefacts[key]
171
+ for k in self.singlefacts:
172
+ if issubclass(key, k):
173
+ ret1 |= self.singlefacts[k]
174
+
175
+ ret2 = self.multifacts[key]
176
+ for k in self.multifacts:
177
+ if issubclass(key, k):
178
+ ret2 |= self.multifacts[k]
179
+
180
+ return ret1, ret2
181
+
182
+ def __call__(self, expr):
183
+ ret = set()
184
+
185
+ handlers1, handlers2 = self[type(expr)]
186
+
187
+ ret.update(h(expr) for h in handlers1)
188
+ for h in handlers2:
189
+ ret.update(h(expr))
190
+ return ret
191
+
192
+ class_fact_registry = ClassFactRegistry()
193
+
194
+
195
+
196
+ ### Class fact registration ###
197
+
198
+ x = Symbol('x')
199
+
200
+ ## Abs ##
201
+
202
+ @class_fact_registry.multiregister(Abs)
203
+ def _(expr):
204
+ arg = expr.args[0]
205
+ return [Q.nonnegative(expr),
206
+ Equivalent(~Q.zero(arg), ~Q.zero(expr)),
207
+ Q.even(arg) >> Q.even(expr),
208
+ Q.odd(arg) >> Q.odd(expr),
209
+ Q.integer(arg) >> Q.integer(expr),
210
+ ]
211
+
212
+
213
+ ### Add ##
214
+
215
+ @class_fact_registry.multiregister(Add)
216
+ def _(expr):
217
+ return [allargs(x, Q.positive(x), expr) >> Q.positive(expr),
218
+ allargs(x, Q.negative(x), expr) >> Q.negative(expr),
219
+ allargs(x, Q.real(x), expr) >> Q.real(expr),
220
+ allargs(x, Q.rational(x), expr) >> Q.rational(expr),
221
+ allargs(x, Q.integer(x), expr) >> Q.integer(expr),
222
+ exactlyonearg(x, ~Q.integer(x), expr) >> ~Q.integer(expr),
223
+ ]
224
+
225
+ @class_fact_registry.register(Add)
226
+ def _(expr):
227
+ allargs_real = allargs(x, Q.real(x), expr)
228
+ onearg_irrational = exactlyonearg(x, Q.irrational(x), expr)
229
+ return Implies(allargs_real, Implies(onearg_irrational, Q.irrational(expr)))
230
+
231
+
232
+ ### Mul ###
233
+
234
+ @class_fact_registry.multiregister(Mul)
235
+ def _(expr):
236
+ return [Equivalent(Q.zero(expr), anyarg(x, Q.zero(x), expr)),
237
+ allargs(x, Q.positive(x), expr) >> Q.positive(expr),
238
+ allargs(x, Q.real(x), expr) >> Q.real(expr),
239
+ allargs(x, Q.rational(x), expr) >> Q.rational(expr),
240
+ allargs(x, Q.integer(x), expr) >> Q.integer(expr),
241
+ exactlyonearg(x, ~Q.rational(x), expr) >> ~Q.integer(expr),
242
+ allargs(x, Q.commutative(x), expr) >> Q.commutative(expr),
243
+ ]
244
+
245
+ @class_fact_registry.register(Mul)
246
+ def _(expr):
247
+ # Implicitly assumes Mul has more than one arg
248
+ # Would be allargs(x, Q.prime(x) | Q.composite(x)) except 1 is composite
249
+ # More advanced prime assumptions will require inequalities, as 1 provides
250
+ # a corner case.
251
+ allargs_prime = allargs(x, Q.prime(x), expr)
252
+ return Implies(allargs_prime, ~Q.prime(expr))
253
+
254
+ @class_fact_registry.register(Mul)
255
+ def _(expr):
256
+ # General Case: Odd number of imaginary args implies mul is imaginary(To be implemented)
257
+ allargs_imag_or_real = allargs(x, Q.imaginary(x) | Q.real(x), expr)
258
+ onearg_imaginary = exactlyonearg(x, Q.imaginary(x), expr)
259
+ return Implies(allargs_imag_or_real, Implies(onearg_imaginary, Q.imaginary(expr)))
260
+
261
+ @class_fact_registry.register(Mul)
262
+ def _(expr):
263
+ allargs_real = allargs(x, Q.real(x), expr)
264
+ onearg_irrational = exactlyonearg(x, Q.irrational(x), expr)
265
+ return Implies(allargs_real, Implies(onearg_irrational, Q.irrational(expr)))
266
+
267
+ @class_fact_registry.register(Mul)
268
+ def _(expr):
269
+ # Including the integer qualification means we don't need to add any facts
270
+ # for odd, since the assumptions already know that every integer is
271
+ # exactly one of even or odd.
272
+ allargs_integer = allargs(x, Q.integer(x), expr)
273
+ anyarg_even = anyarg(x, Q.even(x), expr)
274
+ return Implies(allargs_integer, Equivalent(anyarg_even, Q.even(expr)))
275
+
276
+
277
+ ### MatMul ###
278
+
279
+ @class_fact_registry.register(MatMul)
280
+ def _(expr):
281
+ allargs_square = allargs(x, Q.square(x), expr)
282
+ allargs_invertible = allargs(x, Q.invertible(x), expr)
283
+ return Implies(allargs_square, Equivalent(Q.invertible(expr), allargs_invertible))
284
+
285
+
286
+ ### Pow ###
287
+
288
+ @class_fact_registry.multiregister(Pow)
289
+ def _(expr):
290
+ base, exp = expr.base, expr.exp
291
+ return [
292
+ (Q.real(base) & Q.even(exp) & Q.nonnegative(exp)) >> Q.nonnegative(expr),
293
+ (Q.nonnegative(base) & Q.odd(exp) & Q.nonnegative(exp)) >> Q.nonnegative(expr),
294
+ (Q.nonpositive(base) & Q.odd(exp) & Q.nonnegative(exp)) >> Q.nonpositive(expr),
295
+ Equivalent(Q.zero(expr), Q.zero(base) & Q.positive(exp))
296
+ ]
297
+
298
+
299
+ ### Numbers ###
300
+
301
+ _old_assump_getters = {
302
+ Q.positive: lambda o: o.is_positive,
303
+ Q.zero: lambda o: o.is_zero,
304
+ Q.negative: lambda o: o.is_negative,
305
+ Q.rational: lambda o: o.is_rational,
306
+ Q.irrational: lambda o: o.is_irrational,
307
+ Q.even: lambda o: o.is_even,
308
+ Q.odd: lambda o: o.is_odd,
309
+ Q.imaginary: lambda o: o.is_imaginary,
310
+ Q.prime: lambda o: o.is_prime,
311
+ Q.composite: lambda o: o.is_composite,
312
+ }
313
+
314
+ @class_fact_registry.multiregister(Number, NumberSymbol, ImaginaryUnit)
315
+ def _(expr):
316
+ ret = []
317
+ for p, getter in _old_assump_getters.items():
318
+ pred = p(expr)
319
+ prop = getter(expr)
320
+ if prop is not None:
321
+ ret.append(Equivalent(pred, prop))
322
+ return ret
lib/python3.12/site-packages/sympy/assumptions/tests/__init__.py ADDED
File without changes
lib/python3.12/site-packages/sympy/assumptions/tests/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (202 Bytes). View file
 
lib/python3.12/site-packages/sympy/assumptions/tests/__pycache__/test_assumptions_2.cpython-312.pyc ADDED
Binary file (2.27 kB). View file
 
lib/python3.12/site-packages/sympy/assumptions/tests/__pycache__/test_context.cpython-312.pyc ADDED
Binary file (3.6 kB). View file
 
lib/python3.12/site-packages/sympy/assumptions/tests/__pycache__/test_matrices.cpython-312.pyc ADDED
Binary file (32.2 kB). View file
 
lib/python3.12/site-packages/sympy/assumptions/tests/__pycache__/test_refine.cpython-312.pyc ADDED
Binary file (25.3 kB). View file
 
lib/python3.12/site-packages/sympy/assumptions/tests/__pycache__/test_rel_queries.cpython-312.pyc ADDED
Binary file (14.9 kB). View file
 
lib/python3.12/site-packages/sympy/assumptions/tests/__pycache__/test_satask.cpython-312.pyc ADDED
Binary file (46.2 kB). View file
 
lib/python3.12/site-packages/sympy/assumptions/tests/__pycache__/test_sathandlers.cpython-312.pyc ADDED
Binary file (5.64 kB). View file
 
lib/python3.12/site-packages/sympy/assumptions/tests/__pycache__/test_wrapper.cpython-312.pyc ADDED
Binary file (2.22 kB). View file
 
lib/python3.12/site-packages/sympy/assumptions/tests/test_assumptions_2.py ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ rename this to test_assumptions.py when the old assumptions system is deleted
3
+ """
4
+ from sympy.abc import x, y
5
+ from sympy.assumptions.assume import global_assumptions
6
+ from sympy.assumptions.ask import Q
7
+ from sympy.printing import pretty
8
+
9
+
10
+ def test_equal():
11
+ """Test for equality"""
12
+ assert Q.positive(x) == Q.positive(x)
13
+ assert Q.positive(x) != ~Q.positive(x)
14
+ assert ~Q.positive(x) == ~Q.positive(x)
15
+
16
+
17
+ def test_pretty():
18
+ assert pretty(Q.positive(x)) == "Q.positive(x)"
19
+ assert pretty(
20
+ {Q.positive, Q.integer}) == "{Q.integer, Q.positive}"
21
+
22
+
23
+ def test_global():
24
+ """Test for global assumptions"""
25
+ global_assumptions.add(x > 0)
26
+ assert (x > 0) in global_assumptions
27
+ global_assumptions.remove(x > 0)
28
+ assert not (x > 0) in global_assumptions
29
+ # same with multiple of assumptions
30
+ global_assumptions.add(x > 0, y > 0)
31
+ assert (x > 0) in global_assumptions
32
+ assert (y > 0) in global_assumptions
33
+ global_assumptions.clear()
34
+ assert not (x > 0) in global_assumptions
35
+ assert not (y > 0) in global_assumptions
lib/python3.12/site-packages/sympy/assumptions/tests/test_context.py ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.assumptions import ask, Q
2
+ from sympy.assumptions.assume import assuming, global_assumptions
3
+ from sympy.abc import x, y
4
+
5
+ def test_assuming():
6
+ with assuming(Q.integer(x)):
7
+ assert ask(Q.integer(x))
8
+ assert not ask(Q.integer(x))
9
+
10
+ def test_assuming_nested():
11
+ assert not ask(Q.integer(x))
12
+ assert not ask(Q.integer(y))
13
+ with assuming(Q.integer(x)):
14
+ assert ask(Q.integer(x))
15
+ assert not ask(Q.integer(y))
16
+ with assuming(Q.integer(y)):
17
+ assert ask(Q.integer(x))
18
+ assert ask(Q.integer(y))
19
+ assert ask(Q.integer(x))
20
+ assert not ask(Q.integer(y))
21
+ assert not ask(Q.integer(x))
22
+ assert not ask(Q.integer(y))
23
+
24
+ def test_finally():
25
+ try:
26
+ with assuming(Q.integer(x)):
27
+ 1/0
28
+ except ZeroDivisionError:
29
+ pass
30
+ assert not ask(Q.integer(x))
31
+
32
+ def test_remove_safe():
33
+ global_assumptions.add(Q.integer(x))
34
+ with assuming():
35
+ assert ask(Q.integer(x))
36
+ global_assumptions.remove(Q.integer(x))
37
+ assert not ask(Q.integer(x))
38
+ assert ask(Q.integer(x))
39
+ global_assumptions.clear() # for the benefit of other tests
lib/python3.12/site-packages/sympy/assumptions/tests/test_matrices.py ADDED
@@ -0,0 +1,283 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.assumptions.ask import (Q, ask)
2
+ from sympy.core.symbol import Symbol
3
+ from sympy.matrices.expressions.diagonal import (DiagMatrix, DiagonalMatrix)
4
+ from sympy.matrices.dense import Matrix
5
+ from sympy.matrices.expressions import (MatrixSymbol, Identity, ZeroMatrix,
6
+ OneMatrix, Trace, MatrixSlice, Determinant, BlockMatrix, BlockDiagMatrix)
7
+ from sympy.matrices.expressions.factorizations import LofLU
8
+ from sympy.testing.pytest import XFAIL
9
+
10
+ X = MatrixSymbol('X', 2, 2)
11
+ Y = MatrixSymbol('Y', 2, 3)
12
+ Z = MatrixSymbol('Z', 2, 2)
13
+ A1x1 = MatrixSymbol('A1x1', 1, 1)
14
+ B1x1 = MatrixSymbol('B1x1', 1, 1)
15
+ C0x0 = MatrixSymbol('C0x0', 0, 0)
16
+ V1 = MatrixSymbol('V1', 2, 1)
17
+ V2 = MatrixSymbol('V2', 2, 1)
18
+
19
+ def test_square():
20
+ assert ask(Q.square(X))
21
+ assert not ask(Q.square(Y))
22
+ assert ask(Q.square(Y*Y.T))
23
+
24
+ def test_invertible():
25
+ assert ask(Q.invertible(X), Q.invertible(X))
26
+ assert ask(Q.invertible(Y)) is False
27
+ assert ask(Q.invertible(X*Y), Q.invertible(X)) is False
28
+ assert ask(Q.invertible(X*Z), Q.invertible(X)) is None
29
+ assert ask(Q.invertible(X*Z), Q.invertible(X) & Q.invertible(Z)) is True
30
+ assert ask(Q.invertible(X.T)) is None
31
+ assert ask(Q.invertible(X.T), Q.invertible(X)) is True
32
+ assert ask(Q.invertible(X.I)) is True
33
+ assert ask(Q.invertible(Identity(3))) is True
34
+ assert ask(Q.invertible(ZeroMatrix(3, 3))) is False
35
+ assert ask(Q.invertible(OneMatrix(1, 1))) is True
36
+ assert ask(Q.invertible(OneMatrix(3, 3))) is False
37
+ assert ask(Q.invertible(X), Q.fullrank(X) & Q.square(X))
38
+
39
+ def test_singular():
40
+ assert ask(Q.singular(X)) is None
41
+ assert ask(Q.singular(X), Q.invertible(X)) is False
42
+ assert ask(Q.singular(X), ~Q.invertible(X)) is True
43
+
44
+ @XFAIL
45
+ def test_invertible_fullrank():
46
+ assert ask(Q.invertible(X), Q.fullrank(X)) is True
47
+
48
+
49
+ def test_invertible_BlockMatrix():
50
+ assert ask(Q.invertible(BlockMatrix([Identity(3)]))) == True
51
+ assert ask(Q.invertible(BlockMatrix([ZeroMatrix(3, 3)]))) == False
52
+
53
+ X = Matrix([[1, 2, 3], [3, 5, 4]])
54
+ Y = Matrix([[4, 2, 7], [2, 3, 5]])
55
+ # non-invertible A block
56
+ assert ask(Q.invertible(BlockMatrix([
57
+ [Matrix.ones(3, 3), Y.T],
58
+ [X, Matrix.eye(2)],
59
+ ]))) == True
60
+ # non-invertible B block
61
+ assert ask(Q.invertible(BlockMatrix([
62
+ [Y.T, Matrix.ones(3, 3)],
63
+ [Matrix.eye(2), X],
64
+ ]))) == True
65
+ # non-invertible C block
66
+ assert ask(Q.invertible(BlockMatrix([
67
+ [X, Matrix.eye(2)],
68
+ [Matrix.ones(3, 3), Y.T],
69
+ ]))) == True
70
+ # non-invertible D block
71
+ assert ask(Q.invertible(BlockMatrix([
72
+ [Matrix.eye(2), X],
73
+ [Y.T, Matrix.ones(3, 3)],
74
+ ]))) == True
75
+
76
+
77
+ def test_invertible_BlockDiagMatrix():
78
+ assert ask(Q.invertible(BlockDiagMatrix(Identity(3), Identity(5)))) == True
79
+ assert ask(Q.invertible(BlockDiagMatrix(ZeroMatrix(3, 3), Identity(5)))) == False
80
+ assert ask(Q.invertible(BlockDiagMatrix(Identity(3), OneMatrix(5, 5)))) == False
81
+
82
+
83
+ def test_symmetric():
84
+ assert ask(Q.symmetric(X), Q.symmetric(X))
85
+ assert ask(Q.symmetric(X*Z), Q.symmetric(X)) is None
86
+ assert ask(Q.symmetric(X*Z), Q.symmetric(X) & Q.symmetric(Z)) is True
87
+ assert ask(Q.symmetric(X + Z), Q.symmetric(X) & Q.symmetric(Z)) is True
88
+ assert ask(Q.symmetric(Y)) is False
89
+ assert ask(Q.symmetric(Y*Y.T)) is True
90
+ assert ask(Q.symmetric(Y.T*X*Y)) is None
91
+ assert ask(Q.symmetric(Y.T*X*Y), Q.symmetric(X)) is True
92
+ assert ask(Q.symmetric(X**10), Q.symmetric(X)) is True
93
+ assert ask(Q.symmetric(A1x1)) is True
94
+ assert ask(Q.symmetric(A1x1 + B1x1)) is True
95
+ assert ask(Q.symmetric(A1x1 * B1x1)) is True
96
+ assert ask(Q.symmetric(V1.T*V1)) is True
97
+ assert ask(Q.symmetric(V1.T*(V1 + V2))) is True
98
+ assert ask(Q.symmetric(V1.T*(V1 + V2) + A1x1)) is True
99
+ assert ask(Q.symmetric(MatrixSlice(Y, (0, 1), (1, 2)))) is True
100
+ assert ask(Q.symmetric(Identity(3))) is True
101
+ assert ask(Q.symmetric(ZeroMatrix(3, 3))) is True
102
+ assert ask(Q.symmetric(OneMatrix(3, 3))) is True
103
+
104
+ def _test_orthogonal_unitary(predicate):
105
+ assert ask(predicate(X), predicate(X))
106
+ assert ask(predicate(X.T), predicate(X)) is True
107
+ assert ask(predicate(X.I), predicate(X)) is True
108
+ assert ask(predicate(X**2), predicate(X))
109
+ assert ask(predicate(Y)) is False
110
+ assert ask(predicate(X)) is None
111
+ assert ask(predicate(X), ~Q.invertible(X)) is False
112
+ assert ask(predicate(X*Z*X), predicate(X) & predicate(Z)) is True
113
+ assert ask(predicate(Identity(3))) is True
114
+ assert ask(predicate(ZeroMatrix(3, 3))) is False
115
+ assert ask(Q.invertible(X), predicate(X))
116
+ assert not ask(predicate(X + Z), predicate(X) & predicate(Z))
117
+
118
+ def test_orthogonal():
119
+ _test_orthogonal_unitary(Q.orthogonal)
120
+
121
+ def test_unitary():
122
+ _test_orthogonal_unitary(Q.unitary)
123
+ assert ask(Q.unitary(X), Q.orthogonal(X))
124
+
125
+ def test_fullrank():
126
+ assert ask(Q.fullrank(X), Q.fullrank(X))
127
+ assert ask(Q.fullrank(X**2), Q.fullrank(X))
128
+ assert ask(Q.fullrank(X.T), Q.fullrank(X)) is True
129
+ assert ask(Q.fullrank(X)) is None
130
+ assert ask(Q.fullrank(Y)) is None
131
+ assert ask(Q.fullrank(X*Z), Q.fullrank(X) & Q.fullrank(Z)) is True
132
+ assert ask(Q.fullrank(Identity(3))) is True
133
+ assert ask(Q.fullrank(ZeroMatrix(3, 3))) is False
134
+ assert ask(Q.fullrank(OneMatrix(1, 1))) is True
135
+ assert ask(Q.fullrank(OneMatrix(3, 3))) is False
136
+ assert ask(Q.invertible(X), ~Q.fullrank(X)) == False
137
+
138
+
139
+ def test_positive_definite():
140
+ assert ask(Q.positive_definite(X), Q.positive_definite(X))
141
+ assert ask(Q.positive_definite(X.T), Q.positive_definite(X)) is True
142
+ assert ask(Q.positive_definite(X.I), Q.positive_definite(X)) is True
143
+ assert ask(Q.positive_definite(Y)) is False
144
+ assert ask(Q.positive_definite(X)) is None
145
+ assert ask(Q.positive_definite(X**3), Q.positive_definite(X))
146
+ assert ask(Q.positive_definite(X*Z*X),
147
+ Q.positive_definite(X) & Q.positive_definite(Z)) is True
148
+ assert ask(Q.positive_definite(X), Q.orthogonal(X))
149
+ assert ask(Q.positive_definite(Y.T*X*Y),
150
+ Q.positive_definite(X) & Q.fullrank(Y)) is True
151
+ assert not ask(Q.positive_definite(Y.T*X*Y), Q.positive_definite(X))
152
+ assert ask(Q.positive_definite(Identity(3))) is True
153
+ assert ask(Q.positive_definite(ZeroMatrix(3, 3))) is False
154
+ assert ask(Q.positive_definite(OneMatrix(1, 1))) is True
155
+ assert ask(Q.positive_definite(OneMatrix(3, 3))) is False
156
+ assert ask(Q.positive_definite(X + Z), Q.positive_definite(X) &
157
+ Q.positive_definite(Z)) is True
158
+ assert not ask(Q.positive_definite(-X), Q.positive_definite(X))
159
+ assert ask(Q.positive(X[1, 1]), Q.positive_definite(X))
160
+
161
+ def test_triangular():
162
+ assert ask(Q.upper_triangular(X + Z.T + Identity(2)), Q.upper_triangular(X) &
163
+ Q.lower_triangular(Z)) is True
164
+ assert ask(Q.upper_triangular(X*Z.T), Q.upper_triangular(X) &
165
+ Q.lower_triangular(Z)) is True
166
+ assert ask(Q.lower_triangular(Identity(3))) is True
167
+ assert ask(Q.lower_triangular(ZeroMatrix(3, 3))) is True
168
+ assert ask(Q.upper_triangular(ZeroMatrix(3, 3))) is True
169
+ assert ask(Q.lower_triangular(OneMatrix(1, 1))) is True
170
+ assert ask(Q.upper_triangular(OneMatrix(1, 1))) is True
171
+ assert ask(Q.lower_triangular(OneMatrix(3, 3))) is False
172
+ assert ask(Q.upper_triangular(OneMatrix(3, 3))) is False
173
+ assert ask(Q.triangular(X), Q.unit_triangular(X))
174
+ assert ask(Q.upper_triangular(X**3), Q.upper_triangular(X))
175
+ assert ask(Q.lower_triangular(X**3), Q.lower_triangular(X))
176
+
177
+
178
+ def test_diagonal():
179
+ assert ask(Q.diagonal(X + Z.T + Identity(2)), Q.diagonal(X) &
180
+ Q.diagonal(Z)) is True
181
+ assert ask(Q.diagonal(ZeroMatrix(3, 3)))
182
+ assert ask(Q.diagonal(OneMatrix(1, 1))) is True
183
+ assert ask(Q.diagonal(OneMatrix(3, 3))) is False
184
+ assert ask(Q.lower_triangular(X) & Q.upper_triangular(X), Q.diagonal(X))
185
+ assert ask(Q.diagonal(X), Q.lower_triangular(X) & Q.upper_triangular(X))
186
+ assert ask(Q.symmetric(X), Q.diagonal(X))
187
+ assert ask(Q.triangular(X), Q.diagonal(X))
188
+ assert ask(Q.diagonal(C0x0))
189
+ assert ask(Q.diagonal(A1x1))
190
+ assert ask(Q.diagonal(A1x1 + B1x1))
191
+ assert ask(Q.diagonal(A1x1*B1x1))
192
+ assert ask(Q.diagonal(V1.T*V2))
193
+ assert ask(Q.diagonal(V1.T*(X + Z)*V1))
194
+ assert ask(Q.diagonal(MatrixSlice(Y, (0, 1), (1, 2)))) is True
195
+ assert ask(Q.diagonal(V1.T*(V1 + V2))) is True
196
+ assert ask(Q.diagonal(X**3), Q.diagonal(X))
197
+ assert ask(Q.diagonal(Identity(3)))
198
+ assert ask(Q.diagonal(DiagMatrix(V1)))
199
+ assert ask(Q.diagonal(DiagonalMatrix(X)))
200
+
201
+
202
+ def test_non_atoms():
203
+ assert ask(Q.real(Trace(X)), Q.positive(Trace(X)))
204
+
205
+ @XFAIL
206
+ def test_non_trivial_implies():
207
+ X = MatrixSymbol('X', 3, 3)
208
+ Y = MatrixSymbol('Y', 3, 3)
209
+ assert ask(Q.lower_triangular(X+Y), Q.lower_triangular(X) &
210
+ Q.lower_triangular(Y)) is True
211
+ assert ask(Q.triangular(X), Q.lower_triangular(X)) is True
212
+ assert ask(Q.triangular(X+Y), Q.lower_triangular(X) &
213
+ Q.lower_triangular(Y)) is True
214
+
215
+ def test_MatrixSlice():
216
+ X = MatrixSymbol('X', 4, 4)
217
+ B = MatrixSlice(X, (1, 3), (1, 3))
218
+ C = MatrixSlice(X, (0, 3), (1, 3))
219
+ assert ask(Q.symmetric(B), Q.symmetric(X))
220
+ assert ask(Q.invertible(B), Q.invertible(X))
221
+ assert ask(Q.diagonal(B), Q.diagonal(X))
222
+ assert ask(Q.orthogonal(B), Q.orthogonal(X))
223
+ assert ask(Q.upper_triangular(B), Q.upper_triangular(X))
224
+
225
+ assert not ask(Q.symmetric(C), Q.symmetric(X))
226
+ assert not ask(Q.invertible(C), Q.invertible(X))
227
+ assert not ask(Q.diagonal(C), Q.diagonal(X))
228
+ assert not ask(Q.orthogonal(C), Q.orthogonal(X))
229
+ assert not ask(Q.upper_triangular(C), Q.upper_triangular(X))
230
+
231
+ def test_det_trace_positive():
232
+ X = MatrixSymbol('X', 4, 4)
233
+ assert ask(Q.positive(Trace(X)), Q.positive_definite(X))
234
+ assert ask(Q.positive(Determinant(X)), Q.positive_definite(X))
235
+
236
+ def test_field_assumptions():
237
+ X = MatrixSymbol('X', 4, 4)
238
+ Y = MatrixSymbol('Y', 4, 4)
239
+ assert ask(Q.real_elements(X), Q.real_elements(X))
240
+ assert not ask(Q.integer_elements(X), Q.real_elements(X))
241
+ assert ask(Q.complex_elements(X), Q.real_elements(X))
242
+ assert ask(Q.complex_elements(X**2), Q.real_elements(X))
243
+ assert ask(Q.real_elements(X**2), Q.integer_elements(X))
244
+ assert ask(Q.real_elements(X+Y), Q.real_elements(X)) is None
245
+ assert ask(Q.real_elements(X+Y), Q.real_elements(X) & Q.real_elements(Y))
246
+ from sympy.matrices.expressions.hadamard import HadamardProduct
247
+ assert ask(Q.real_elements(HadamardProduct(X, Y)),
248
+ Q.real_elements(X) & Q.real_elements(Y))
249
+ assert ask(Q.complex_elements(X+Y), Q.real_elements(X) & Q.complex_elements(Y))
250
+
251
+ assert ask(Q.real_elements(X.T), Q.real_elements(X))
252
+ assert ask(Q.real_elements(X.I), Q.real_elements(X) & Q.invertible(X))
253
+ assert ask(Q.real_elements(Trace(X)), Q.real_elements(X))
254
+ assert ask(Q.integer_elements(Determinant(X)), Q.integer_elements(X))
255
+ assert not ask(Q.integer_elements(X.I), Q.integer_elements(X))
256
+ alpha = Symbol('alpha')
257
+ assert ask(Q.real_elements(alpha*X), Q.real_elements(X) & Q.real(alpha))
258
+ assert ask(Q.real_elements(LofLU(X)), Q.real_elements(X))
259
+ e = Symbol('e', integer=True, negative=True)
260
+ assert ask(Q.real_elements(X**e), Q.real_elements(X) & Q.invertible(X))
261
+ assert ask(Q.real_elements(X**e), Q.real_elements(X)) is None
262
+
263
+ def test_matrix_element_sets():
264
+ X = MatrixSymbol('X', 4, 4)
265
+ assert ask(Q.real(X[1, 2]), Q.real_elements(X))
266
+ assert ask(Q.integer(X[1, 2]), Q.integer_elements(X))
267
+ assert ask(Q.complex(X[1, 2]), Q.complex_elements(X))
268
+ assert ask(Q.integer_elements(Identity(3)))
269
+ assert ask(Q.integer_elements(ZeroMatrix(3, 3)))
270
+ assert ask(Q.integer_elements(OneMatrix(3, 3)))
271
+ from sympy.matrices.expressions.fourier import DFT
272
+ assert ask(Q.complex_elements(DFT(3)))
273
+
274
+
275
+ def test_matrix_element_sets_slices_blocks():
276
+ X = MatrixSymbol('X', 4, 4)
277
+ assert ask(Q.integer_elements(X[:, 3]), Q.integer_elements(X))
278
+ assert ask(Q.integer_elements(BlockMatrix([[X], [X]])),
279
+ Q.integer_elements(X))
280
+
281
+ def test_matrix_element_sets_determinant_trace():
282
+ assert ask(Q.integer(Determinant(X)), Q.integer_elements(X))
283
+ assert ask(Q.integer(Trace(X)), Q.integer_elements(X))
lib/python3.12/site-packages/sympy/assumptions/tests/test_query.py ADDED
The diff for this file is too large to render. See raw diff
 
lib/python3.12/site-packages/sympy/assumptions/tests/test_refine.py ADDED
@@ -0,0 +1,227 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.assumptions.ask import Q
2
+ from sympy.assumptions.refine import refine
3
+ from sympy.core.expr import Expr
4
+ from sympy.core.numbers import (I, Rational, nan, pi)
5
+ from sympy.core.singleton import S
6
+ from sympy.core.symbol import Symbol
7
+ from sympy.functions.elementary.complexes import (Abs, arg, im, re, sign)
8
+ from sympy.functions.elementary.exponential import exp
9
+ from sympy.functions.elementary.miscellaneous import sqrt
10
+ from sympy.functions.elementary.trigonometric import (atan, atan2)
11
+ from sympy.abc import w, x, y, z
12
+ from sympy.core.relational import Eq, Ne
13
+ from sympy.functions.elementary.piecewise import Piecewise
14
+ from sympy.matrices.expressions.matexpr import MatrixSymbol
15
+
16
+
17
+ def test_Abs():
18
+ assert refine(Abs(x), Q.positive(x)) == x
19
+ assert refine(1 + Abs(x), Q.positive(x)) == 1 + x
20
+ assert refine(Abs(x), Q.negative(x)) == -x
21
+ assert refine(1 + Abs(x), Q.negative(x)) == 1 - x
22
+
23
+ assert refine(Abs(x**2)) != x**2
24
+ assert refine(Abs(x**2), Q.real(x)) == x**2
25
+
26
+
27
+ def test_pow1():
28
+ assert refine((-1)**x, Q.even(x)) == 1
29
+ assert refine((-1)**x, Q.odd(x)) == -1
30
+ assert refine((-2)**x, Q.even(x)) == 2**x
31
+
32
+ # nested powers
33
+ assert refine(sqrt(x**2)) != Abs(x)
34
+ assert refine(sqrt(x**2), Q.complex(x)) != Abs(x)
35
+ assert refine(sqrt(x**2), Q.real(x)) == Abs(x)
36
+ assert refine(sqrt(x**2), Q.positive(x)) == x
37
+ assert refine((x**3)**Rational(1, 3)) != x
38
+
39
+ assert refine((x**3)**Rational(1, 3), Q.real(x)) != x
40
+ assert refine((x**3)**Rational(1, 3), Q.positive(x)) == x
41
+
42
+ assert refine(sqrt(1/x), Q.real(x)) != 1/sqrt(x)
43
+ assert refine(sqrt(1/x), Q.positive(x)) == 1/sqrt(x)
44
+
45
+ # powers of (-1)
46
+ assert refine((-1)**(x + y), Q.even(x)) == (-1)**y
47
+ assert refine((-1)**(x + y + z), Q.odd(x) & Q.odd(z)) == (-1)**y
48
+ assert refine((-1)**(x + y + 1), Q.odd(x)) == (-1)**y
49
+ assert refine((-1)**(x + y + 2), Q.odd(x)) == (-1)**(y + 1)
50
+ assert refine((-1)**(x + 3)) == (-1)**(x + 1)
51
+
52
+ # continuation
53
+ assert refine((-1)**((-1)**x/2 - S.Half), Q.integer(x)) == (-1)**x
54
+ assert refine((-1)**((-1)**x/2 + S.Half), Q.integer(x)) == (-1)**(x + 1)
55
+ assert refine((-1)**((-1)**x/2 + 5*S.Half), Q.integer(x)) == (-1)**(x + 1)
56
+
57
+
58
+ def test_pow2():
59
+ assert refine((-1)**((-1)**x/2 - 7*S.Half), Q.integer(x)) == (-1)**(x + 1)
60
+ assert refine((-1)**((-1)**x/2 - 9*S.Half), Q.integer(x)) == (-1)**x
61
+
62
+ # powers of Abs
63
+ assert refine(Abs(x)**2, Q.real(x)) == x**2
64
+ assert refine(Abs(x)**3, Q.real(x)) == Abs(x)**3
65
+ assert refine(Abs(x)**2) == Abs(x)**2
66
+
67
+
68
+ def test_exp():
69
+ x = Symbol('x', integer=True)
70
+ assert refine(exp(pi*I*2*x)) == 1
71
+ assert refine(exp(pi*I*2*(x + S.Half))) == -1
72
+ assert refine(exp(pi*I*2*(x + Rational(1, 4)))) == I
73
+ assert refine(exp(pi*I*2*(x + Rational(3, 4)))) == -I
74
+
75
+
76
+ def test_Piecewise():
77
+ assert refine(Piecewise((1, x < 0), (3, True)), (x < 0)) == 1
78
+ assert refine(Piecewise((1, x < 0), (3, True)), ~(x < 0)) == 3
79
+ assert refine(Piecewise((1, x < 0), (3, True)), (y < 0)) == \
80
+ Piecewise((1, x < 0), (3, True))
81
+ assert refine(Piecewise((1, x > 0), (3, True)), (x > 0)) == 1
82
+ assert refine(Piecewise((1, x > 0), (3, True)), ~(x > 0)) == 3
83
+ assert refine(Piecewise((1, x > 0), (3, True)), (y > 0)) == \
84
+ Piecewise((1, x > 0), (3, True))
85
+ assert refine(Piecewise((1, x <= 0), (3, True)), (x <= 0)) == 1
86
+ assert refine(Piecewise((1, x <= 0), (3, True)), ~(x <= 0)) == 3
87
+ assert refine(Piecewise((1, x <= 0), (3, True)), (y <= 0)) == \
88
+ Piecewise((1, x <= 0), (3, True))
89
+ assert refine(Piecewise((1, x >= 0), (3, True)), (x >= 0)) == 1
90
+ assert refine(Piecewise((1, x >= 0), (3, True)), ~(x >= 0)) == 3
91
+ assert refine(Piecewise((1, x >= 0), (3, True)), (y >= 0)) == \
92
+ Piecewise((1, x >= 0), (3, True))
93
+ assert refine(Piecewise((1, Eq(x, 0)), (3, True)), (Eq(x, 0)))\
94
+ == 1
95
+ assert refine(Piecewise((1, Eq(x, 0)), (3, True)), (Eq(0, x)))\
96
+ == 1
97
+ assert refine(Piecewise((1, Eq(x, 0)), (3, True)), ~(Eq(x, 0)))\
98
+ == 3
99
+ assert refine(Piecewise((1, Eq(x, 0)), (3, True)), ~(Eq(0, x)))\
100
+ == 3
101
+ assert refine(Piecewise((1, Eq(x, 0)), (3, True)), (Eq(y, 0)))\
102
+ == Piecewise((1, Eq(x, 0)), (3, True))
103
+ assert refine(Piecewise((1, Ne(x, 0)), (3, True)), (Ne(x, 0)))\
104
+ == 1
105
+ assert refine(Piecewise((1, Ne(x, 0)), (3, True)), ~(Ne(x, 0)))\
106
+ == 3
107
+ assert refine(Piecewise((1, Ne(x, 0)), (3, True)), (Ne(y, 0)))\
108
+ == Piecewise((1, Ne(x, 0)), (3, True))
109
+
110
+
111
+ def test_atan2():
112
+ assert refine(atan2(y, x), Q.real(y) & Q.positive(x)) == atan(y/x)
113
+ assert refine(atan2(y, x), Q.negative(y) & Q.positive(x)) == atan(y/x)
114
+ assert refine(atan2(y, x), Q.negative(y) & Q.negative(x)) == atan(y/x) - pi
115
+ assert refine(atan2(y, x), Q.positive(y) & Q.negative(x)) == atan(y/x) + pi
116
+ assert refine(atan2(y, x), Q.zero(y) & Q.negative(x)) == pi
117
+ assert refine(atan2(y, x), Q.positive(y) & Q.zero(x)) == pi/2
118
+ assert refine(atan2(y, x), Q.negative(y) & Q.zero(x)) == -pi/2
119
+ assert refine(atan2(y, x), Q.zero(y) & Q.zero(x)) is nan
120
+
121
+
122
+ def test_re():
123
+ assert refine(re(x), Q.real(x)) == x
124
+ assert refine(re(x), Q.imaginary(x)) is S.Zero
125
+ assert refine(re(x+y), Q.real(x) & Q.real(y)) == x + y
126
+ assert refine(re(x+y), Q.real(x) & Q.imaginary(y)) == x
127
+ assert refine(re(x*y), Q.real(x) & Q.real(y)) == x * y
128
+ assert refine(re(x*y), Q.real(x) & Q.imaginary(y)) == 0
129
+ assert refine(re(x*y*z), Q.real(x) & Q.real(y) & Q.real(z)) == x * y * z
130
+
131
+
132
+ def test_im():
133
+ assert refine(im(x), Q.imaginary(x)) == -I*x
134
+ assert refine(im(x), Q.real(x)) is S.Zero
135
+ assert refine(im(x+y), Q.imaginary(x) & Q.imaginary(y)) == -I*x - I*y
136
+ assert refine(im(x+y), Q.real(x) & Q.imaginary(y)) == -I*y
137
+ assert refine(im(x*y), Q.imaginary(x) & Q.real(y)) == -I*x*y
138
+ assert refine(im(x*y), Q.imaginary(x) & Q.imaginary(y)) == 0
139
+ assert refine(im(1/x), Q.imaginary(x)) == -I/x
140
+ assert refine(im(x*y*z), Q.imaginary(x) & Q.imaginary(y)
141
+ & Q.imaginary(z)) == -I*x*y*z
142
+
143
+
144
+ def test_complex():
145
+ assert refine(re(1/(x + I*y)), Q.real(x) & Q.real(y)) == \
146
+ x/(x**2 + y**2)
147
+ assert refine(im(1/(x + I*y)), Q.real(x) & Q.real(y)) == \
148
+ -y/(x**2 + y**2)
149
+ assert refine(re((w + I*x) * (y + I*z)), Q.real(w) & Q.real(x) & Q.real(y)
150
+ & Q.real(z)) == w*y - x*z
151
+ assert refine(im((w + I*x) * (y + I*z)), Q.real(w) & Q.real(x) & Q.real(y)
152
+ & Q.real(z)) == w*z + x*y
153
+
154
+
155
+ def test_sign():
156
+ x = Symbol('x', real = True)
157
+ assert refine(sign(x), Q.positive(x)) == 1
158
+ assert refine(sign(x), Q.negative(x)) == -1
159
+ assert refine(sign(x), Q.zero(x)) == 0
160
+ assert refine(sign(x), True) == sign(x)
161
+ assert refine(sign(Abs(x)), Q.nonzero(x)) == 1
162
+
163
+ x = Symbol('x', imaginary=True)
164
+ assert refine(sign(x), Q.positive(im(x))) == S.ImaginaryUnit
165
+ assert refine(sign(x), Q.negative(im(x))) == -S.ImaginaryUnit
166
+ assert refine(sign(x), True) == sign(x)
167
+
168
+ x = Symbol('x', complex=True)
169
+ assert refine(sign(x), Q.zero(x)) == 0
170
+
171
+ def test_arg():
172
+ x = Symbol('x', complex = True)
173
+ assert refine(arg(x), Q.positive(x)) == 0
174
+ assert refine(arg(x), Q.negative(x)) == pi
175
+
176
+ def test_func_args():
177
+ class MyClass(Expr):
178
+ # A class with nontrivial .func
179
+
180
+ def __init__(self, *args):
181
+ self.my_member = ""
182
+
183
+ @property
184
+ def func(self):
185
+ def my_func(*args):
186
+ obj = MyClass(*args)
187
+ obj.my_member = self.my_member
188
+ return obj
189
+ return my_func
190
+
191
+ x = MyClass()
192
+ x.my_member = "A very important value"
193
+ assert x.my_member == refine(x).my_member
194
+
195
+ def test_issue_refine_9384():
196
+ assert refine(Piecewise((1, x < 0), (0, True)), Q.positive(x)) == 0
197
+ assert refine(Piecewise((1, x < 0), (0, True)), Q.negative(x)) == 1
198
+ assert refine(Piecewise((1, x > 0), (0, True)), Q.positive(x)) == 1
199
+ assert refine(Piecewise((1, x > 0), (0, True)), Q.negative(x)) == 0
200
+
201
+
202
+ def test_eval_refine():
203
+ class MockExpr(Expr):
204
+ def _eval_refine(self, assumptions):
205
+ return True
206
+
207
+ mock_obj = MockExpr()
208
+ assert refine(mock_obj)
209
+
210
+ def test_refine_issue_12724():
211
+ expr1 = refine(Abs(x * y), Q.positive(x))
212
+ expr2 = refine(Abs(x * y * z), Q.positive(x))
213
+ assert expr1 == x * Abs(y)
214
+ assert expr2 == x * Abs(y * z)
215
+ y1 = Symbol('y1', real = True)
216
+ expr3 = refine(Abs(x * y1**2 * z), Q.positive(x))
217
+ assert expr3 == x * y1**2 * Abs(z)
218
+
219
+
220
+ def test_matrixelement():
221
+ x = MatrixSymbol('x', 3, 3)
222
+ i = Symbol('i', positive = True)
223
+ j = Symbol('j', positive = True)
224
+ assert refine(x[0, 1], Q.symmetric(x)) == x[0, 1]
225
+ assert refine(x[1, 0], Q.symmetric(x)) == x[0, 1]
226
+ assert refine(x[i, j], Q.symmetric(x)) == x[j, i]
227
+ assert refine(x[j, i], Q.symmetric(x)) == x[j, i]
lib/python3.12/site-packages/sympy/assumptions/tests/test_rel_queries.py ADDED
@@ -0,0 +1,172 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.assumptions.lra_satask import lra_satask
2
+ from sympy.logic.algorithms.lra_theory import UnhandledInput
3
+ from sympy.assumptions.ask import Q, ask
4
+
5
+ from sympy.core import symbols, Symbol
6
+ from sympy.matrices.expressions.matexpr import MatrixSymbol
7
+ from sympy.core.numbers import I
8
+
9
+ from sympy.testing.pytest import raises, XFAIL
10
+ x, y, z = symbols("x y z", real=True)
11
+
12
+ def test_lra_satask():
13
+ im = Symbol('im', imaginary=True)
14
+
15
+ # test preprocessing of unequalities is working correctly
16
+ assert lra_satask(Q.eq(x, 1), ~Q.ne(x, 0)) is False
17
+ assert lra_satask(Q.eq(x, 0), ~Q.ne(x, 0)) is True
18
+ assert lra_satask(~Q.ne(x, 0), Q.eq(x, 0)) is True
19
+ assert lra_satask(~Q.eq(x, 0), Q.eq(x, 0)) is False
20
+ assert lra_satask(Q.ne(x, 0), Q.eq(x, 0)) is False
21
+
22
+ # basic tests
23
+ assert lra_satask(Q.ne(x, x)) is False
24
+ assert lra_satask(Q.eq(x, x)) is True
25
+ assert lra_satask(Q.gt(x, 0), Q.gt(x, 1)) is True
26
+
27
+ # check that True/False are handled
28
+ assert lra_satask(Q.gt(x, 0), True) is None
29
+ assert raises(ValueError, lambda: lra_satask(Q.gt(x, 0), False))
30
+
31
+ # check imaginary numbers are correctly handled
32
+ # (im * I).is_real returns True so this is an edge case
33
+ raises(UnhandledInput, lambda: lra_satask(Q.gt(im * I, 0), Q.gt(im * I, 0)))
34
+
35
+ # check matrix inputs
36
+ X = MatrixSymbol("X", 2, 2)
37
+ raises(UnhandledInput, lambda: lra_satask(Q.lt(X, 2) & Q.gt(X, 3)))
38
+
39
+
40
+ def test_old_assumptions():
41
+ # test unhandled old assumptions
42
+ w = symbols("w")
43
+ raises(UnhandledInput, lambda: lra_satask(Q.lt(w, 2) & Q.gt(w, 3)))
44
+ w = symbols("w", rational=False, real=True)
45
+ raises(UnhandledInput, lambda: lra_satask(Q.lt(w, 2) & Q.gt(w, 3)))
46
+ w = symbols("w", odd=True, real=True)
47
+ raises(UnhandledInput, lambda: lra_satask(Q.lt(w, 2) & Q.gt(w, 3)))
48
+ w = symbols("w", even=True, real=True)
49
+ raises(UnhandledInput, lambda: lra_satask(Q.lt(w, 2) & Q.gt(w, 3)))
50
+ w = symbols("w", prime=True, real=True)
51
+ raises(UnhandledInput, lambda: lra_satask(Q.lt(w, 2) & Q.gt(w, 3)))
52
+ w = symbols("w", composite=True, real=True)
53
+ raises(UnhandledInput, lambda: lra_satask(Q.lt(w, 2) & Q.gt(w, 3)))
54
+ w = symbols("w", integer=True, real=True)
55
+ raises(UnhandledInput, lambda: lra_satask(Q.lt(w, 2) & Q.gt(w, 3)))
56
+ w = symbols("w", integer=False, real=True)
57
+ raises(UnhandledInput, lambda: lra_satask(Q.lt(w, 2) & Q.gt(w, 3)))
58
+
59
+ # test handled
60
+ w = symbols("w", positive=True, real=True)
61
+ assert lra_satask(Q.le(w, 0)) is False
62
+ assert lra_satask(Q.gt(w, 0)) is True
63
+ w = symbols("w", negative=True, real=True)
64
+ assert lra_satask(Q.lt(w, 0)) is True
65
+ assert lra_satask(Q.ge(w, 0)) is False
66
+ w = symbols("w", zero=True, real=True)
67
+ assert lra_satask(Q.eq(w, 0)) is True
68
+ assert lra_satask(Q.ne(w, 0)) is False
69
+ w = symbols("w", nonzero=True, real=True)
70
+ assert lra_satask(Q.ne(w, 0)) is True
71
+ assert lra_satask(Q.eq(w, 1)) is None
72
+ w = symbols("w", nonpositive=True, real=True)
73
+ assert lra_satask(Q.le(w, 0)) is True
74
+ assert lra_satask(Q.gt(w, 0)) is False
75
+ w = symbols("w", nonnegative=True, real=True)
76
+ assert lra_satask(Q.ge(w, 0)) is True
77
+ assert lra_satask(Q.lt(w, 0)) is False
78
+
79
+
80
+ def test_rel_queries():
81
+ assert ask(Q.lt(x, 2) & Q.gt(x, 3)) is False
82
+ assert ask(Q.positive(x - z), (x > y) & (y > z)) is True
83
+ assert ask(x + y > 2, (x < 0) & (y <0)) is False
84
+ assert ask(x > z, (x > y) & (y > z)) is True
85
+
86
+
87
+ def test_unhandled_queries():
88
+ X = MatrixSymbol("X", 2, 2)
89
+ assert ask(Q.lt(X, 2) & Q.gt(X, 3)) is None
90
+
91
+
92
+ def test_all_pred():
93
+ # test usable pred
94
+ assert lra_satask(Q.extended_positive(x), (x > 2)) is True
95
+ assert lra_satask(Q.positive_infinite(x)) is False
96
+ assert lra_satask(Q.negative_infinite(x)) is False
97
+
98
+ # test disallowed pred
99
+ raises(UnhandledInput, lambda: lra_satask((x > 0), (x > 2) & Q.prime(x)))
100
+ raises(UnhandledInput, lambda: lra_satask((x > 0), (x > 2) & Q.composite(x)))
101
+ raises(UnhandledInput, lambda: lra_satask((x > 0), (x > 2) & Q.odd(x)))
102
+ raises(UnhandledInput, lambda: lra_satask((x > 0), (x > 2) & Q.even(x)))
103
+ raises(UnhandledInput, lambda: lra_satask((x > 0), (x > 2) & Q.integer(x)))
104
+
105
+
106
+ def test_number_line_properties():
107
+ # From:
108
+ # https://en.wikipedia.org/wiki/Inequality_(mathematics)#Properties_on_the_number_line
109
+
110
+ a, b, c = symbols("a b c", real=True)
111
+
112
+ # Transitivity
113
+ # If a <= b and b <= c, then a <= c.
114
+ assert ask(a <= c, (a <= b) & (b <= c)) is True
115
+ # If a <= b and b < c, then a < c.
116
+ assert ask(a < c, (a <= b) & (b < c)) is True
117
+ # If a < b and b <= c, then a < c.
118
+ assert ask(a < c, (a < b) & (b <= c)) is True
119
+
120
+ # Addition and subtraction
121
+ # If a <= b, then a + c <= b + c and a - c <= b - c.
122
+ assert ask(a + c <= b + c, a <= b) is True
123
+ assert ask(a - c <= b - c, a <= b) is True
124
+
125
+
126
+ @XFAIL
127
+ def test_failing_number_line_properties():
128
+ # From:
129
+ # https://en.wikipedia.org/wiki/Inequality_(mathematics)#Properties_on_the_number_line
130
+
131
+ a, b, c = symbols("a b c", real=True)
132
+
133
+ # Multiplication and division
134
+ # If a <= b and c > 0, then ac <= bc and a/c <= b/c. (True for non-zero c)
135
+ assert ask(a*c <= b*c, (a <= b) & (c > 0) & ~ Q.zero(c)) is True
136
+ assert ask(a/c <= b/c, (a <= b) & (c > 0) & ~ Q.zero(c)) is True
137
+ # If a <= b and c < 0, then ac >= bc and a/c >= b/c. (True for non-zero c)
138
+ assert ask(a*c >= b*c, (a <= b) & (c < 0) & ~ Q.zero(c)) is True
139
+ assert ask(a/c >= b/c, (a <= b) & (c < 0) & ~ Q.zero(c)) is True
140
+
141
+ # Additive inverse
142
+ # If a <= b, then -a >= -b.
143
+ assert ask(-a >= -b, a <= b) is True
144
+
145
+ # Multiplicative inverse
146
+ # For a, b that are both negative or both positive:
147
+ # If a <= b, then 1/a >= 1/b .
148
+ assert ask(1/a >= 1/b, (a <= b) & Q.positive(x) & Q.positive(b)) is True
149
+ assert ask(1/a >= 1/b, (a <= b) & Q.negative(x) & Q.negative(b)) is True
150
+
151
+
152
+ def test_equality():
153
+ # test symetry and reflexivity
154
+ assert ask(Q.eq(x, x)) is True
155
+ assert ask(Q.eq(y, x), Q.eq(x, y)) is True
156
+ assert ask(Q.eq(y, x), ~Q.eq(z, z) | Q.eq(x, y)) is True
157
+
158
+ # test transitivity
159
+ assert ask(Q.eq(x,z), Q.eq(x,y) & Q.eq(y,z)) is True
160
+
161
+
162
+ @XFAIL
163
+ def test_equality_failing():
164
+ # Note that implementing the substitution property of equality
165
+ # most likely requires a redesign of the new assumptions.
166
+ # See issue #25485 for why this is the case and general ideas
167
+ # about how things could be redesigned.
168
+
169
+ # test substitution property
170
+ assert ask(Q.prime(x), Q.eq(x, y) & Q.prime(y)) is True
171
+ assert ask(Q.real(x), Q.eq(x, y) & Q.real(y)) is True
172
+ assert ask(Q.imaginary(x), Q.eq(x, y) & Q.imaginary(y)) is True
lib/python3.12/site-packages/sympy/assumptions/tests/test_satask.py ADDED
@@ -0,0 +1,378 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.assumptions.ask import Q
2
+ from sympy.assumptions.assume import assuming
3
+ from sympy.core.numbers import (I, pi)
4
+ from sympy.core.relational import (Eq, Gt)
5
+ from sympy.core.singleton import S
6
+ from sympy.core.symbol import symbols
7
+ from sympy.functions.elementary.complexes import Abs
8
+ from sympy.logic.boolalg import Implies
9
+ from sympy.matrices.expressions.matexpr import MatrixSymbol
10
+ from sympy.assumptions.cnf import CNF, Literal
11
+ from sympy.assumptions.satask import (satask, extract_predargs,
12
+ get_relevant_clsfacts)
13
+
14
+ from sympy.testing.pytest import raises, XFAIL
15
+
16
+
17
+ x, y, z = symbols('x y z')
18
+
19
+
20
+ def test_satask():
21
+ # No relevant facts
22
+ assert satask(Q.real(x), Q.real(x)) is True
23
+ assert satask(Q.real(x), ~Q.real(x)) is False
24
+ assert satask(Q.real(x)) is None
25
+
26
+ assert satask(Q.real(x), Q.positive(x)) is True
27
+ assert satask(Q.positive(x), Q.real(x)) is None
28
+ assert satask(Q.real(x), ~Q.positive(x)) is None
29
+ assert satask(Q.positive(x), ~Q.real(x)) is False
30
+
31
+ raises(ValueError, lambda: satask(Q.real(x), Q.real(x) & ~Q.real(x)))
32
+
33
+ with assuming(Q.positive(x)):
34
+ assert satask(Q.real(x)) is True
35
+ assert satask(~Q.positive(x)) is False
36
+ raises(ValueError, lambda: satask(Q.real(x), ~Q.positive(x)))
37
+
38
+ assert satask(Q.zero(x), Q.nonzero(x)) is False
39
+ assert satask(Q.positive(x), Q.zero(x)) is False
40
+ assert satask(Q.real(x), Q.zero(x)) is True
41
+ assert satask(Q.zero(x), Q.zero(x*y)) is None
42
+ assert satask(Q.zero(x*y), Q.zero(x))
43
+
44
+
45
+ def test_zero():
46
+ """
47
+ Everything in this test doesn't work with the ask handlers, and most
48
+ things would be very difficult or impossible to make work under that
49
+ model.
50
+
51
+ """
52
+ assert satask(Q.zero(x) | Q.zero(y), Q.zero(x*y)) is True
53
+ assert satask(Q.zero(x*y), Q.zero(x) | Q.zero(y)) is True
54
+
55
+ assert satask(Implies(Q.zero(x), Q.zero(x*y))) is True
56
+
57
+ # This one in particular requires computing the fixed-point of the
58
+ # relevant facts, because going from Q.nonzero(x*y) -> ~Q.zero(x*y) and
59
+ # Q.zero(x*y) -> Equivalent(Q.zero(x*y), Q.zero(x) | Q.zero(y)) takes two
60
+ # steps.
61
+ assert satask(Q.zero(x) | Q.zero(y), Q.nonzero(x*y)) is False
62
+
63
+ assert satask(Q.zero(x), Q.zero(x**2)) is True
64
+
65
+
66
+ def test_zero_positive():
67
+ assert satask(Q.zero(x + y), Q.positive(x) & Q.positive(y)) is False
68
+ assert satask(Q.positive(x) & Q.positive(y), Q.zero(x + y)) is False
69
+ assert satask(Q.nonzero(x + y), Q.positive(x) & Q.positive(y)) is True
70
+ assert satask(Q.positive(x) & Q.positive(y), Q.nonzero(x + y)) is None
71
+
72
+ # This one requires several levels of forward chaining
73
+ assert satask(Q.zero(x*(x + y)), Q.positive(x) & Q.positive(y)) is False
74
+
75
+ assert satask(Q.positive(pi*x*y + 1), Q.positive(x) & Q.positive(y)) is True
76
+ assert satask(Q.positive(pi*x*y - 5), Q.positive(x) & Q.positive(y)) is None
77
+
78
+
79
+ def test_zero_pow():
80
+ assert satask(Q.zero(x**y), Q.zero(x) & Q.positive(y)) is True
81
+ assert satask(Q.zero(x**y), Q.nonzero(x) & Q.zero(y)) is False
82
+
83
+ assert satask(Q.zero(x), Q.zero(x**y)) is True
84
+
85
+ assert satask(Q.zero(x**y), Q.zero(x)) is None
86
+
87
+
88
+ @XFAIL
89
+ # Requires correct Q.square calculation first
90
+ def test_invertible():
91
+ A = MatrixSymbol('A', 5, 5)
92
+ B = MatrixSymbol('B', 5, 5)
93
+ assert satask(Q.invertible(A*B), Q.invertible(A) & Q.invertible(B)) is True
94
+ assert satask(Q.invertible(A), Q.invertible(A*B)) is True
95
+ assert satask(Q.invertible(A) & Q.invertible(B), Q.invertible(A*B)) is True
96
+
97
+
98
+ def test_prime():
99
+ assert satask(Q.prime(5)) is True
100
+ assert satask(Q.prime(6)) is False
101
+ assert satask(Q.prime(-5)) is False
102
+
103
+ assert satask(Q.prime(x*y), Q.integer(x) & Q.integer(y)) is None
104
+ assert satask(Q.prime(x*y), Q.prime(x) & Q.prime(y)) is False
105
+
106
+
107
+ def test_old_assump():
108
+ assert satask(Q.positive(1)) is True
109
+ assert satask(Q.positive(-1)) is False
110
+ assert satask(Q.positive(0)) is False
111
+ assert satask(Q.positive(I)) is False
112
+ assert satask(Q.positive(pi)) is True
113
+
114
+ assert satask(Q.negative(1)) is False
115
+ assert satask(Q.negative(-1)) is True
116
+ assert satask(Q.negative(0)) is False
117
+ assert satask(Q.negative(I)) is False
118
+ assert satask(Q.negative(pi)) is False
119
+
120
+ assert satask(Q.zero(1)) is False
121
+ assert satask(Q.zero(-1)) is False
122
+ assert satask(Q.zero(0)) is True
123
+ assert satask(Q.zero(I)) is False
124
+ assert satask(Q.zero(pi)) is False
125
+
126
+ assert satask(Q.nonzero(1)) is True
127
+ assert satask(Q.nonzero(-1)) is True
128
+ assert satask(Q.nonzero(0)) is False
129
+ assert satask(Q.nonzero(I)) is False
130
+ assert satask(Q.nonzero(pi)) is True
131
+
132
+ assert satask(Q.nonpositive(1)) is False
133
+ assert satask(Q.nonpositive(-1)) is True
134
+ assert satask(Q.nonpositive(0)) is True
135
+ assert satask(Q.nonpositive(I)) is False
136
+ assert satask(Q.nonpositive(pi)) is False
137
+
138
+ assert satask(Q.nonnegative(1)) is True
139
+ assert satask(Q.nonnegative(-1)) is False
140
+ assert satask(Q.nonnegative(0)) is True
141
+ assert satask(Q.nonnegative(I)) is False
142
+ assert satask(Q.nonnegative(pi)) is True
143
+
144
+
145
+ def test_rational_irrational():
146
+ assert satask(Q.irrational(2)) is False
147
+ assert satask(Q.rational(2)) is True
148
+ assert satask(Q.irrational(pi)) is True
149
+ assert satask(Q.rational(pi)) is False
150
+ assert satask(Q.irrational(I)) is False
151
+ assert satask(Q.rational(I)) is False
152
+
153
+ assert satask(Q.irrational(x*y*z), Q.irrational(x) & Q.irrational(y) &
154
+ Q.rational(z)) is None
155
+ assert satask(Q.irrational(x*y*z), Q.irrational(x) & Q.rational(y) &
156
+ Q.rational(z)) is True
157
+ assert satask(Q.irrational(pi*x*y), Q.rational(x) & Q.rational(y)) is True
158
+
159
+ assert satask(Q.irrational(x + y + z), Q.irrational(x) & Q.irrational(y) &
160
+ Q.rational(z)) is None
161
+ assert satask(Q.irrational(x + y + z), Q.irrational(x) & Q.rational(y) &
162
+ Q.rational(z)) is True
163
+ assert satask(Q.irrational(pi + x + y), Q.rational(x) & Q.rational(y)) is True
164
+
165
+ assert satask(Q.irrational(x*y*z), Q.rational(x) & Q.rational(y) &
166
+ Q.rational(z)) is False
167
+ assert satask(Q.rational(x*y*z), Q.rational(x) & Q.rational(y) &
168
+ Q.rational(z)) is True
169
+
170
+ assert satask(Q.irrational(x + y + z), Q.rational(x) & Q.rational(y) &
171
+ Q.rational(z)) is False
172
+ assert satask(Q.rational(x + y + z), Q.rational(x) & Q.rational(y) &
173
+ Q.rational(z)) is True
174
+
175
+
176
+ def test_even_satask():
177
+ assert satask(Q.even(2)) is True
178
+ assert satask(Q.even(3)) is False
179
+
180
+ assert satask(Q.even(x*y), Q.even(x) & Q.odd(y)) is True
181
+ assert satask(Q.even(x*y), Q.even(x) & Q.integer(y)) is True
182
+ assert satask(Q.even(x*y), Q.even(x) & Q.even(y)) is True
183
+ assert satask(Q.even(x*y), Q.odd(x) & Q.odd(y)) is False
184
+ assert satask(Q.even(x*y), Q.even(x)) is None
185
+ assert satask(Q.even(x*y), Q.odd(x) & Q.integer(y)) is None
186
+ assert satask(Q.even(x*y), Q.odd(x) & Q.odd(y)) is False
187
+
188
+ assert satask(Q.even(abs(x)), Q.even(x)) is True
189
+ assert satask(Q.even(abs(x)), Q.odd(x)) is False
190
+ assert satask(Q.even(x), Q.even(abs(x))) is None # x could be complex
191
+
192
+
193
+ def test_odd_satask():
194
+ assert satask(Q.odd(2)) is False
195
+ assert satask(Q.odd(3)) is True
196
+
197
+ assert satask(Q.odd(x*y), Q.even(x) & Q.odd(y)) is False
198
+ assert satask(Q.odd(x*y), Q.even(x) & Q.integer(y)) is False
199
+ assert satask(Q.odd(x*y), Q.even(x) & Q.even(y)) is False
200
+ assert satask(Q.odd(x*y), Q.odd(x) & Q.odd(y)) is True
201
+ assert satask(Q.odd(x*y), Q.even(x)) is None
202
+ assert satask(Q.odd(x*y), Q.odd(x) & Q.integer(y)) is None
203
+ assert satask(Q.odd(x*y), Q.odd(x) & Q.odd(y)) is True
204
+
205
+ assert satask(Q.odd(abs(x)), Q.even(x)) is False
206
+ assert satask(Q.odd(abs(x)), Q.odd(x)) is True
207
+ assert satask(Q.odd(x), Q.odd(abs(x))) is None # x could be complex
208
+
209
+
210
+ def test_integer():
211
+ assert satask(Q.integer(1)) is True
212
+ assert satask(Q.integer(S.Half)) is False
213
+
214
+ assert satask(Q.integer(x + y), Q.integer(x) & Q.integer(y)) is True
215
+ assert satask(Q.integer(x + y), Q.integer(x)) is None
216
+
217
+ assert satask(Q.integer(x + y), Q.integer(x) & ~Q.integer(y)) is False
218
+ assert satask(Q.integer(x + y + z), Q.integer(x) & Q.integer(y) &
219
+ ~Q.integer(z)) is False
220
+ assert satask(Q.integer(x + y + z), Q.integer(x) & ~Q.integer(y) &
221
+ ~Q.integer(z)) is None
222
+ assert satask(Q.integer(x + y + z), Q.integer(x) & ~Q.integer(y)) is None
223
+ assert satask(Q.integer(x + y), Q.integer(x) & Q.irrational(y)) is False
224
+
225
+ assert satask(Q.integer(x*y), Q.integer(x) & Q.integer(y)) is True
226
+ assert satask(Q.integer(x*y), Q.integer(x)) is None
227
+
228
+ assert satask(Q.integer(x*y), Q.integer(x) & ~Q.integer(y)) is None
229
+ assert satask(Q.integer(x*y), Q.integer(x) & ~Q.rational(y)) is False
230
+ assert satask(Q.integer(x*y*z), Q.integer(x) & Q.integer(y) &
231
+ ~Q.rational(z)) is False
232
+ assert satask(Q.integer(x*y*z), Q.integer(x) & ~Q.rational(y) &
233
+ ~Q.rational(z)) is None
234
+ assert satask(Q.integer(x*y*z), Q.integer(x) & ~Q.rational(y)) is None
235
+ assert satask(Q.integer(x*y), Q.integer(x) & Q.irrational(y)) is False
236
+
237
+
238
+ def test_abs():
239
+ assert satask(Q.nonnegative(abs(x))) is True
240
+ assert satask(Q.positive(abs(x)), ~Q.zero(x)) is True
241
+ assert satask(Q.zero(x), ~Q.zero(abs(x))) is False
242
+ assert satask(Q.zero(x), Q.zero(abs(x))) is True
243
+ assert satask(Q.nonzero(x), ~Q.zero(abs(x))) is None # x could be complex
244
+ assert satask(Q.zero(abs(x)), Q.zero(x)) is True
245
+
246
+
247
+ def test_imaginary():
248
+ assert satask(Q.imaginary(2*I)) is True
249
+ assert satask(Q.imaginary(x*y), Q.imaginary(x)) is None
250
+ assert satask(Q.imaginary(x*y), Q.imaginary(x) & Q.real(y)) is True
251
+ assert satask(Q.imaginary(x), Q.real(x)) is False
252
+ assert satask(Q.imaginary(1)) is False
253
+ assert satask(Q.imaginary(x*y), Q.real(x) & Q.real(y)) is False
254
+ assert satask(Q.imaginary(x + y), Q.real(x) & Q.real(y)) is False
255
+
256
+
257
+ def test_real():
258
+ assert satask(Q.real(x*y), Q.real(x) & Q.real(y)) is True
259
+ assert satask(Q.real(x + y), Q.real(x) & Q.real(y)) is True
260
+ assert satask(Q.real(x*y*z), Q.real(x) & Q.real(y) & Q.real(z)) is True
261
+ assert satask(Q.real(x*y*z), Q.real(x) & Q.real(y)) is None
262
+ assert satask(Q.real(x*y*z), Q.real(x) & Q.real(y) & Q.imaginary(z)) is False
263
+ assert satask(Q.real(x + y + z), Q.real(x) & Q.real(y) & Q.real(z)) is True
264
+ assert satask(Q.real(x + y + z), Q.real(x) & Q.real(y)) is None
265
+
266
+
267
+ def test_pos_neg():
268
+ assert satask(~Q.positive(x), Q.negative(x)) is True
269
+ assert satask(~Q.negative(x), Q.positive(x)) is True
270
+ assert satask(Q.positive(x + y), Q.positive(x) & Q.positive(y)) is True
271
+ assert satask(Q.negative(x + y), Q.negative(x) & Q.negative(y)) is True
272
+ assert satask(Q.positive(x + y), Q.negative(x) & Q.negative(y)) is False
273
+ assert satask(Q.negative(x + y), Q.positive(x) & Q.positive(y)) is False
274
+
275
+
276
+ def test_pow_pos_neg():
277
+ assert satask(Q.nonnegative(x**2), Q.positive(x)) is True
278
+ assert satask(Q.nonpositive(x**2), Q.positive(x)) is False
279
+ assert satask(Q.positive(x**2), Q.positive(x)) is True
280
+ assert satask(Q.negative(x**2), Q.positive(x)) is False
281
+ assert satask(Q.real(x**2), Q.positive(x)) is True
282
+
283
+ assert satask(Q.nonnegative(x**2), Q.negative(x)) is True
284
+ assert satask(Q.nonpositive(x**2), Q.negative(x)) is False
285
+ assert satask(Q.positive(x**2), Q.negative(x)) is True
286
+ assert satask(Q.negative(x**2), Q.negative(x)) is False
287
+ assert satask(Q.real(x**2), Q.negative(x)) is True
288
+
289
+ assert satask(Q.nonnegative(x**2), Q.nonnegative(x)) is True
290
+ assert satask(Q.nonpositive(x**2), Q.nonnegative(x)) is None
291
+ assert satask(Q.positive(x**2), Q.nonnegative(x)) is None
292
+ assert satask(Q.negative(x**2), Q.nonnegative(x)) is False
293
+ assert satask(Q.real(x**2), Q.nonnegative(x)) is True
294
+
295
+ assert satask(Q.nonnegative(x**2), Q.nonpositive(x)) is True
296
+ assert satask(Q.nonpositive(x**2), Q.nonpositive(x)) is None
297
+ assert satask(Q.positive(x**2), Q.nonpositive(x)) is None
298
+ assert satask(Q.negative(x**2), Q.nonpositive(x)) is False
299
+ assert satask(Q.real(x**2), Q.nonpositive(x)) is True
300
+
301
+ assert satask(Q.nonnegative(x**3), Q.positive(x)) is True
302
+ assert satask(Q.nonpositive(x**3), Q.positive(x)) is False
303
+ assert satask(Q.positive(x**3), Q.positive(x)) is True
304
+ assert satask(Q.negative(x**3), Q.positive(x)) is False
305
+ assert satask(Q.real(x**3), Q.positive(x)) is True
306
+
307
+ assert satask(Q.nonnegative(x**3), Q.negative(x)) is False
308
+ assert satask(Q.nonpositive(x**3), Q.negative(x)) is True
309
+ assert satask(Q.positive(x**3), Q.negative(x)) is False
310
+ assert satask(Q.negative(x**3), Q.negative(x)) is True
311
+ assert satask(Q.real(x**3), Q.negative(x)) is True
312
+
313
+ assert satask(Q.nonnegative(x**3), Q.nonnegative(x)) is True
314
+ assert satask(Q.nonpositive(x**3), Q.nonnegative(x)) is None
315
+ assert satask(Q.positive(x**3), Q.nonnegative(x)) is None
316
+ assert satask(Q.negative(x**3), Q.nonnegative(x)) is False
317
+ assert satask(Q.real(x**3), Q.nonnegative(x)) is True
318
+
319
+ assert satask(Q.nonnegative(x**3), Q.nonpositive(x)) is None
320
+ assert satask(Q.nonpositive(x**3), Q.nonpositive(x)) is True
321
+ assert satask(Q.positive(x**3), Q.nonpositive(x)) is False
322
+ assert satask(Q.negative(x**3), Q.nonpositive(x)) is None
323
+ assert satask(Q.real(x**3), Q.nonpositive(x)) is True
324
+
325
+ # If x is zero, x**negative is not real.
326
+ assert satask(Q.nonnegative(x**-2), Q.nonpositive(x)) is None
327
+ assert satask(Q.nonpositive(x**-2), Q.nonpositive(x)) is None
328
+ assert satask(Q.positive(x**-2), Q.nonpositive(x)) is None
329
+ assert satask(Q.negative(x**-2), Q.nonpositive(x)) is None
330
+ assert satask(Q.real(x**-2), Q.nonpositive(x)) is None
331
+
332
+ # We could deduce things for negative powers if x is nonzero, but it
333
+ # isn't implemented yet.
334
+
335
+
336
+ def test_prime_composite():
337
+ assert satask(Q.prime(x), Q.composite(x)) is False
338
+ assert satask(Q.composite(x), Q.prime(x)) is False
339
+ assert satask(Q.composite(x), ~Q.prime(x)) is None
340
+ assert satask(Q.prime(x), ~Q.composite(x)) is None
341
+ # since 1 is neither prime nor composite the following should hold
342
+ assert satask(Q.prime(x), Q.integer(x) & Q.positive(x) & ~Q.composite(x)) is None
343
+ assert satask(Q.prime(2)) is True
344
+ assert satask(Q.prime(4)) is False
345
+ assert satask(Q.prime(1)) is False
346
+ assert satask(Q.composite(1)) is False
347
+
348
+
349
+ def test_extract_predargs():
350
+ props = CNF.from_prop(Q.zero(Abs(x*y)) & Q.zero(x*y))
351
+ assump = CNF.from_prop(Q.zero(x))
352
+ context = CNF.from_prop(Q.zero(y))
353
+ assert extract_predargs(props) == {Abs(x*y), x*y}
354
+ assert extract_predargs(props, assump) == {Abs(x*y), x*y, x}
355
+ assert extract_predargs(props, assump, context) == {Abs(x*y), x*y, x, y}
356
+
357
+ props = CNF.from_prop(Eq(x, y))
358
+ assump = CNF.from_prop(Gt(y, z))
359
+ assert extract_predargs(props, assump) == {x, y, z}
360
+
361
+
362
+ def test_get_relevant_clsfacts():
363
+ exprs = {Abs(x*y)}
364
+ exprs, facts = get_relevant_clsfacts(exprs)
365
+ assert exprs == {x*y}
366
+ assert facts.clauses == \
367
+ {frozenset({Literal(Q.odd(Abs(x*y)), False), Literal(Q.odd(x*y), True)}),
368
+ frozenset({Literal(Q.zero(Abs(x*y)), False), Literal(Q.zero(x*y), True)}),
369
+ frozenset({Literal(Q.even(Abs(x*y)), False), Literal(Q.even(x*y), True)}),
370
+ frozenset({Literal(Q.zero(Abs(x*y)), True), Literal(Q.zero(x*y), False)}),
371
+ frozenset({Literal(Q.even(Abs(x*y)), False),
372
+ Literal(Q.odd(Abs(x*y)), False),
373
+ Literal(Q.odd(x*y), True)}),
374
+ frozenset({Literal(Q.even(Abs(x*y)), False),
375
+ Literal(Q.even(x*y), True),
376
+ Literal(Q.odd(Abs(x*y)), False)}),
377
+ frozenset({Literal(Q.positive(Abs(x*y)), False),
378
+ Literal(Q.zero(Abs(x*y)), False)})}
lib/python3.12/site-packages/sympy/assumptions/tests/test_sathandlers.py ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.assumptions.ask import Q
2
+ from sympy.core.basic import Basic
3
+ from sympy.core.expr import Expr
4
+ from sympy.core.mul import Mul
5
+ from sympy.core.symbol import symbols
6
+ from sympy.logic.boolalg import (And, Or)
7
+
8
+ from sympy.assumptions.sathandlers import (ClassFactRegistry, allargs,
9
+ anyarg, exactlyonearg,)
10
+
11
+ x, y, z = symbols('x y z')
12
+
13
+
14
+ def test_class_handler_registry():
15
+ my_handler_registry = ClassFactRegistry()
16
+
17
+ # The predicate doesn't matter here, so just pass
18
+ @my_handler_registry.register(Mul)
19
+ def fact1(expr):
20
+ pass
21
+ @my_handler_registry.multiregister(Expr)
22
+ def fact2(expr):
23
+ pass
24
+
25
+ assert my_handler_registry[Basic] == (frozenset(), frozenset())
26
+ assert my_handler_registry[Expr] == (frozenset(), frozenset({fact2}))
27
+ assert my_handler_registry[Mul] == (frozenset({fact1}), frozenset({fact2}))
28
+
29
+
30
+ def test_allargs():
31
+ assert allargs(x, Q.zero(x), x*y) == And(Q.zero(x), Q.zero(y))
32
+ assert allargs(x, Q.positive(x) | Q.negative(x), x*y) == And(Q.positive(x) | Q.negative(x), Q.positive(y) | Q.negative(y))
33
+
34
+
35
+ def test_anyarg():
36
+ assert anyarg(x, Q.zero(x), x*y) == Or(Q.zero(x), Q.zero(y))
37
+ assert anyarg(x, Q.positive(x) & Q.negative(x), x*y) == \
38
+ Or(Q.positive(x) & Q.negative(x), Q.positive(y) & Q.negative(y))
39
+
40
+
41
+ def test_exactlyonearg():
42
+ assert exactlyonearg(x, Q.zero(x), x*y) == \
43
+ Or(Q.zero(x) & ~Q.zero(y), Q.zero(y) & ~Q.zero(x))
44
+ assert exactlyonearg(x, Q.zero(x), x*y*z) == \
45
+ Or(Q.zero(x) & ~Q.zero(y) & ~Q.zero(z), Q.zero(y)
46
+ & ~Q.zero(x) & ~Q.zero(z), Q.zero(z) & ~Q.zero(x) & ~Q.zero(y))
47
+ assert exactlyonearg(x, Q.positive(x) | Q.negative(x), x*y) == \
48
+ Or((Q.positive(x) | Q.negative(x)) &
49
+ ~(Q.positive(y) | Q.negative(y)), (Q.positive(y) | Q.negative(y)) &
50
+ ~(Q.positive(x) | Q.negative(x)))
lib/python3.12/site-packages/sympy/assumptions/tests/test_wrapper.py ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.assumptions.ask import Q
2
+ from sympy.assumptions.wrapper import (AssumptionsWrapper, is_infinite,
3
+ is_extended_real)
4
+ from sympy.core.symbol import Symbol
5
+ from sympy.core.assumptions import _assume_defined
6
+
7
+
8
+ def test_all_predicates():
9
+ for fact in _assume_defined:
10
+ method_name = f'_eval_is_{fact}'
11
+ assert hasattr(AssumptionsWrapper, method_name)
12
+
13
+
14
+ def test_AssumptionsWrapper():
15
+ x = Symbol('x', positive=True)
16
+ y = Symbol('y')
17
+ assert AssumptionsWrapper(x).is_positive
18
+ assert AssumptionsWrapper(y).is_positive is None
19
+ assert AssumptionsWrapper(y, Q.positive(y)).is_positive
20
+
21
+
22
+ def test_is_infinite():
23
+ x = Symbol('x', infinite=True)
24
+ y = Symbol('y', infinite=False)
25
+ z = Symbol('z')
26
+ assert is_infinite(x)
27
+ assert not is_infinite(y)
28
+ assert is_infinite(z) is None
29
+ assert is_infinite(z, Q.infinite(z))
30
+
31
+
32
+ def test_is_extended_real():
33
+ x = Symbol('x', extended_real=True)
34
+ y = Symbol('y', extended_real=False)
35
+ z = Symbol('z')
36
+ assert is_extended_real(x)
37
+ assert not is_extended_real(y)
38
+ assert is_extended_real(z) is None
39
+ assert is_extended_real(z, Q.extended_real(z))
lib/python3.12/site-packages/sympy/assumptions/wrapper.py ADDED
@@ -0,0 +1,164 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Functions and wrapper object to call assumption property and predicate
3
+ query with same syntax.
4
+
5
+ In SymPy, there are two assumption systems. Old assumption system is
6
+ defined in sympy/core/assumptions, and it can be accessed by attribute
7
+ such as ``x.is_even``. New assumption system is defined in
8
+ sympy/assumptions, and it can be accessed by predicates such as
9
+ ``Q.even(x)``.
10
+
11
+ Old assumption is fast, while new assumptions can freely take local facts.
12
+ In general, old assumption is used in evaluation method and new assumption
13
+ is used in refinement method.
14
+
15
+ In most cases, both evaluation and refinement follow the same process, and
16
+ the only difference is which assumption system is used. This module provides
17
+ ``is_[...]()`` functions and ``AssumptionsWrapper()`` class which allows
18
+ using two systems with same syntax so that parallel code implementation can be
19
+ avoided.
20
+
21
+ Examples
22
+ ========
23
+
24
+ For multiple use, use ``AssumptionsWrapper()``.
25
+
26
+ >>> from sympy import Q, Symbol
27
+ >>> from sympy.assumptions.wrapper import AssumptionsWrapper
28
+ >>> x = Symbol('x')
29
+ >>> _x = AssumptionsWrapper(x, Q.even(x))
30
+ >>> _x.is_integer
31
+ True
32
+ >>> _x.is_odd
33
+ False
34
+
35
+ For single use, use ``is_[...]()`` functions.
36
+
37
+ >>> from sympy.assumptions.wrapper import is_infinite
38
+ >>> a = Symbol('a')
39
+ >>> print(is_infinite(a))
40
+ None
41
+ >>> is_infinite(a, Q.finite(a))
42
+ False
43
+
44
+ """
45
+
46
+ from sympy.assumptions import ask, Q
47
+ from sympy.core.basic import Basic
48
+ from sympy.core.sympify import _sympify
49
+
50
+
51
+ def make_eval_method(fact):
52
+ def getit(self):
53
+ pred = getattr(Q, fact)
54
+ ret = ask(pred(self.expr), self.assumptions)
55
+ return ret
56
+ return getit
57
+
58
+
59
+ # we subclass Basic to use the fact deduction and caching
60
+ class AssumptionsWrapper(Basic):
61
+ """
62
+ Wrapper over ``Basic`` instances to call predicate query by
63
+ ``.is_[...]`` property
64
+
65
+ Parameters
66
+ ==========
67
+
68
+ expr : Basic
69
+
70
+ assumptions : Boolean, optional
71
+
72
+ Examples
73
+ ========
74
+
75
+ >>> from sympy import Q, Symbol
76
+ >>> from sympy.assumptions.wrapper import AssumptionsWrapper
77
+ >>> x = Symbol('x', even=True)
78
+ >>> AssumptionsWrapper(x).is_integer
79
+ True
80
+ >>> y = Symbol('y')
81
+ >>> AssumptionsWrapper(y, Q.even(y)).is_integer
82
+ True
83
+
84
+ With ``AssumptionsWrapper``, both evaluation and refinement can be supported
85
+ by single implementation.
86
+
87
+ >>> from sympy import Function
88
+ >>> class MyAbs(Function):
89
+ ... @classmethod
90
+ ... def eval(cls, x, assumptions=True):
91
+ ... _x = AssumptionsWrapper(x, assumptions)
92
+ ... if _x.is_nonnegative:
93
+ ... return x
94
+ ... if _x.is_negative:
95
+ ... return -x
96
+ ... def _eval_refine(self, assumptions):
97
+ ... return MyAbs.eval(self.args[0], assumptions)
98
+ >>> MyAbs(x)
99
+ MyAbs(x)
100
+ >>> MyAbs(x).refine(Q.positive(x))
101
+ x
102
+ >>> MyAbs(Symbol('y', negative=True))
103
+ -y
104
+
105
+ """
106
+ def __new__(cls, expr, assumptions=None):
107
+ if assumptions is None:
108
+ return expr
109
+ obj = super().__new__(cls, expr, _sympify(assumptions))
110
+ obj.expr = expr
111
+ obj.assumptions = assumptions
112
+ return obj
113
+
114
+ _eval_is_algebraic = make_eval_method("algebraic")
115
+ _eval_is_antihermitian = make_eval_method("antihermitian")
116
+ _eval_is_commutative = make_eval_method("commutative")
117
+ _eval_is_complex = make_eval_method("complex")
118
+ _eval_is_composite = make_eval_method("composite")
119
+ _eval_is_even = make_eval_method("even")
120
+ _eval_is_extended_negative = make_eval_method("extended_negative")
121
+ _eval_is_extended_nonnegative = make_eval_method("extended_nonnegative")
122
+ _eval_is_extended_nonpositive = make_eval_method("extended_nonpositive")
123
+ _eval_is_extended_nonzero = make_eval_method("extended_nonzero")
124
+ _eval_is_extended_positive = make_eval_method("extended_positive")
125
+ _eval_is_extended_real = make_eval_method("extended_real")
126
+ _eval_is_finite = make_eval_method("finite")
127
+ _eval_is_hermitian = make_eval_method("hermitian")
128
+ _eval_is_imaginary = make_eval_method("imaginary")
129
+ _eval_is_infinite = make_eval_method("infinite")
130
+ _eval_is_integer = make_eval_method("integer")
131
+ _eval_is_irrational = make_eval_method("irrational")
132
+ _eval_is_negative = make_eval_method("negative")
133
+ _eval_is_noninteger = make_eval_method("noninteger")
134
+ _eval_is_nonnegative = make_eval_method("nonnegative")
135
+ _eval_is_nonpositive = make_eval_method("nonpositive")
136
+ _eval_is_nonzero = make_eval_method("nonzero")
137
+ _eval_is_odd = make_eval_method("odd")
138
+ _eval_is_polar = make_eval_method("polar")
139
+ _eval_is_positive = make_eval_method("positive")
140
+ _eval_is_prime = make_eval_method("prime")
141
+ _eval_is_rational = make_eval_method("rational")
142
+ _eval_is_real = make_eval_method("real")
143
+ _eval_is_transcendental = make_eval_method("transcendental")
144
+ _eval_is_zero = make_eval_method("zero")
145
+
146
+
147
+ # one shot functions which are faster than AssumptionsWrapper
148
+
149
+ def is_infinite(obj, assumptions=None):
150
+ if assumptions is None:
151
+ return obj.is_infinite
152
+ return ask(Q.infinite(obj), assumptions)
153
+
154
+
155
+ def is_extended_real(obj, assumptions=None):
156
+ if assumptions is None:
157
+ return obj.is_extended_real
158
+ return ask(Q.extended_real(obj), assumptions)
159
+
160
+
161
+ def is_extended_nonnegative(obj, assumptions=None):
162
+ if assumptions is None:
163
+ return obj.is_extended_nonnegative
164
+ return ask(Q.extended_nonnegative(obj), assumptions)
lib/python3.12/site-packages/sympy/ntheory/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (2.64 kB). View file
 
lib/python3.12/site-packages/sympy/ntheory/__pycache__/bbp_pi.cpython-312.pyc ADDED
Binary file (6.51 kB). View file
 
lib/python3.12/site-packages/sympy/ntheory/__pycache__/continued_fraction.cpython-312.pyc ADDED
Binary file (13.1 kB). View file
 
lib/python3.12/site-packages/sympy/ntheory/__pycache__/digits.cpython-312.pyc ADDED
Binary file (4.69 kB). View file
 
lib/python3.12/site-packages/sympy/ntheory/__pycache__/ecm.cpython-312.pyc ADDED
Binary file (13.9 kB). View file
 
lib/python3.12/site-packages/sympy/ntheory/__pycache__/egyptian_fraction.cpython-312.pyc ADDED
Binary file (8.48 kB). View file
 
lib/python3.12/site-packages/sympy/ntheory/__pycache__/elliptic_curve.cpython-312.pyc ADDED
Binary file (19.3 kB). View file
 
lib/python3.12/site-packages/sympy/ntheory/__pycache__/factor_.cpython-312.pyc ADDED
Binary file (84.2 kB). View file
 
lib/python3.12/site-packages/sympy/ntheory/__pycache__/generate.cpython-312.pyc ADDED
Binary file (39.4 kB). View file