diff --git a/lib/python3.12/site-packages/sympy/core/__init__.py b/lib/python3.12/site-packages/sympy/core/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..4c03c9fc11479bb6d93a3bff3dfd0992ef994a19 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/core/__init__.py @@ -0,0 +1,103 @@ +"""Core module. Provides the basic operations needed in sympy. +""" + +from .sympify import sympify, SympifyError +from .cache import cacheit +from .assumptions import assumptions, check_assumptions, failing_assumptions, common_assumptions +from .basic import Basic, Atom +from .singleton import S +from .expr import Expr, AtomicExpr, UnevaluatedExpr +from .symbol import Symbol, Wild, Dummy, symbols, var +from .numbers import Number, Float, Rational, Integer, NumberSymbol, \ + RealNumber, igcd, ilcm, seterr, E, I, nan, oo, pi, zoo, \ + AlgebraicNumber, comp, mod_inverse +from .power import Pow +from .intfunc import integer_nthroot, integer_log, num_digits, trailing +from .mul import Mul, prod +from .add import Add +from .mod import Mod +from .relational import ( Rel, Eq, Ne, Lt, Le, Gt, Ge, + Equality, GreaterThan, LessThan, Unequality, StrictGreaterThan, + StrictLessThan ) +from .multidimensional import vectorize +from .function import Lambda, WildFunction, Derivative, diff, FunctionClass, \ + Function, Subs, expand, PoleError, count_ops, \ + expand_mul, expand_log, expand_func, \ + expand_trig, expand_complex, expand_multinomial, nfloat, \ + expand_power_base, expand_power_exp, arity +from .evalf import PrecisionExhausted, N +from .containers import Tuple, Dict +from .exprtools import gcd_terms, factor_terms, factor_nc +from .parameters import evaluate +from .kind import UndefinedKind, NumberKind, BooleanKind +from .traversal import preorder_traversal, bottom_up, use, postorder_traversal +from .sorting import default_sort_key, ordered + +# expose singletons +Catalan = S.Catalan +EulerGamma = S.EulerGamma +GoldenRatio = S.GoldenRatio +TribonacciConstant = S.TribonacciConstant + +__all__ = [ + 'sympify', 'SympifyError', + + 'cacheit', + + 'assumptions', 'check_assumptions', 'failing_assumptions', + 'common_assumptions', + + 'Basic', 'Atom', + + 'S', + + 'Expr', 'AtomicExpr', 'UnevaluatedExpr', + + 'Symbol', 'Wild', 'Dummy', 'symbols', 'var', + + 'Number', 'Float', 'Rational', 'Integer', 'NumberSymbol', 'RealNumber', + 'igcd', 'ilcm', 'seterr', 'E', 'I', 'nan', 'oo', 'pi', 'zoo', + 'AlgebraicNumber', 'comp', 'mod_inverse', + + 'Pow', + + 'integer_nthroot', 'integer_log', 'num_digits', 'trailing', + + 'Mul', 'prod', + + 'Add', + + 'Mod', + + 'Rel', 'Eq', 'Ne', 'Lt', 'Le', 'Gt', 'Ge', 'Equality', 'GreaterThan', + 'LessThan', 'Unequality', 'StrictGreaterThan', 'StrictLessThan', + + 'vectorize', + + 'Lambda', 'WildFunction', 'Derivative', 'diff', 'FunctionClass', + 'Function', 'Subs', 'expand', 'PoleError', 'count_ops', 'expand_mul', + 'expand_log', 'expand_func', 'expand_trig', 'expand_complex', + 'expand_multinomial', 'nfloat', 'expand_power_base', 'expand_power_exp', + 'arity', + + 'PrecisionExhausted', 'N', + + 'evalf', # The module? + + 'Tuple', 'Dict', + + 'gcd_terms', 'factor_terms', 'factor_nc', + + 'evaluate', + + 'Catalan', + 'EulerGamma', + 'GoldenRatio', + 'TribonacciConstant', + + 'UndefinedKind', 'NumberKind', 'BooleanKind', + + 'preorder_traversal', 'bottom_up', 'use', 'postorder_traversal', + + 'default_sort_key', 'ordered', +] diff --git a/lib/python3.12/site-packages/sympy/core/__pycache__/__init__.cpython-312.pyc b/lib/python3.12/site-packages/sympy/core/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9802ab2aad97217f60857d5608a2c79d255a734c Binary files /dev/null and b/lib/python3.12/site-packages/sympy/core/__pycache__/__init__.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/core/__pycache__/_print_helpers.cpython-312.pyc b/lib/python3.12/site-packages/sympy/core/__pycache__/_print_helpers.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6f178a739ecdc6fb14c3a5d5918999282fa6f154 Binary files /dev/null and b/lib/python3.12/site-packages/sympy/core/__pycache__/_print_helpers.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/core/__pycache__/add.cpython-312.pyc b/lib/python3.12/site-packages/sympy/core/__pycache__/add.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9bd640b43dd8231b8f8a7776d2df9c285448b7a2 Binary files /dev/null and b/lib/python3.12/site-packages/sympy/core/__pycache__/add.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/core/__pycache__/alphabets.cpython-312.pyc b/lib/python3.12/site-packages/sympy/core/__pycache__/alphabets.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9f4b3ce35030d4fce63a75ee98532a5c785ed0c2 Binary files /dev/null and b/lib/python3.12/site-packages/sympy/core/__pycache__/alphabets.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/core/__pycache__/assumptions.cpython-312.pyc b/lib/python3.12/site-packages/sympy/core/__pycache__/assumptions.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ab1c5a3465e6a7887f7e154aeb64aa98911bc0ca Binary files /dev/null and b/lib/python3.12/site-packages/sympy/core/__pycache__/assumptions.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/core/__pycache__/assumptions_generated.cpython-312.pyc b/lib/python3.12/site-packages/sympy/core/__pycache__/assumptions_generated.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3dbdd51a386b1ba5399629480f15b89f661fb8b5 Binary files /dev/null and b/lib/python3.12/site-packages/sympy/core/__pycache__/assumptions_generated.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/core/__pycache__/backend.cpython-312.pyc b/lib/python3.12/site-packages/sympy/core/__pycache__/backend.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6ce93a38ef07538eb0054a08c2ebfb56ca2a20b0 Binary files /dev/null and b/lib/python3.12/site-packages/sympy/core/__pycache__/backend.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/core/__pycache__/basic.cpython-312.pyc b/lib/python3.12/site-packages/sympy/core/__pycache__/basic.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e7bc4301ed40809bdbb9b3505c3800df0c578b74 Binary files /dev/null and b/lib/python3.12/site-packages/sympy/core/__pycache__/basic.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/core/__pycache__/cache.cpython-312.pyc b/lib/python3.12/site-packages/sympy/core/__pycache__/cache.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4095f475e5c1fe064ebf81b4d1b37d3a855b68f5 Binary files /dev/null and b/lib/python3.12/site-packages/sympy/core/__pycache__/cache.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/core/__pycache__/compatibility.cpython-312.pyc b/lib/python3.12/site-packages/sympy/core/__pycache__/compatibility.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1a109cdeaa859bd6c9b2f35873cdbba247fe757e Binary files /dev/null and b/lib/python3.12/site-packages/sympy/core/__pycache__/compatibility.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/core/__pycache__/containers.cpython-312.pyc b/lib/python3.12/site-packages/sympy/core/__pycache__/containers.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..42d273b2eee3b75066cedf183394a2fe077ad209 Binary files /dev/null and b/lib/python3.12/site-packages/sympy/core/__pycache__/containers.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/core/__pycache__/core.cpython-312.pyc b/lib/python3.12/site-packages/sympy/core/__pycache__/core.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..115a6121616c80d0b2e116a748b145d8cea4a53f Binary files /dev/null and b/lib/python3.12/site-packages/sympy/core/__pycache__/core.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/core/__pycache__/coreerrors.cpython-312.pyc b/lib/python3.12/site-packages/sympy/core/__pycache__/coreerrors.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ea8a98d7137899b7ac8e1b1bc55db8a22ff28af5 Binary files /dev/null and b/lib/python3.12/site-packages/sympy/core/__pycache__/coreerrors.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/core/__pycache__/decorators.cpython-312.pyc b/lib/python3.12/site-packages/sympy/core/__pycache__/decorators.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0538216c821b54d47dec4b178f4076bad3097cd6 Binary files /dev/null and b/lib/python3.12/site-packages/sympy/core/__pycache__/decorators.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/core/__pycache__/evalf.cpython-312.pyc b/lib/python3.12/site-packages/sympy/core/__pycache__/evalf.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0913ed29c2142c768701d52d2fef4ca1b2b60e12 Binary files /dev/null and b/lib/python3.12/site-packages/sympy/core/__pycache__/evalf.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/core/__pycache__/exprtools.cpython-312.pyc b/lib/python3.12/site-packages/sympy/core/__pycache__/exprtools.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dfeb021368a6d9fe6795fa30173af317b0f3d1b8 Binary files /dev/null and b/lib/python3.12/site-packages/sympy/core/__pycache__/exprtools.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/core/__pycache__/facts.cpython-312.pyc b/lib/python3.12/site-packages/sympy/core/__pycache__/facts.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..362975b300ba30379f3ce82286a7859cb637f8dd Binary files /dev/null and b/lib/python3.12/site-packages/sympy/core/__pycache__/facts.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/core/__pycache__/intfunc.cpython-312.pyc b/lib/python3.12/site-packages/sympy/core/__pycache__/intfunc.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..193321d54caeb59057b2a406db8d98f07afc0a07 Binary files /dev/null and b/lib/python3.12/site-packages/sympy/core/__pycache__/intfunc.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/core/__pycache__/kind.cpython-312.pyc b/lib/python3.12/site-packages/sympy/core/__pycache__/kind.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a1b5dafed1dfdc13adeec8fe1aebe8a8e787aa45 Binary files /dev/null and b/lib/python3.12/site-packages/sympy/core/__pycache__/kind.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/core/__pycache__/logic.cpython-312.pyc b/lib/python3.12/site-packages/sympy/core/__pycache__/logic.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..04a841fb98adcb5303087effbf2345b3378200e9 Binary files /dev/null and b/lib/python3.12/site-packages/sympy/core/__pycache__/logic.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/core/__pycache__/mod.cpython-312.pyc b/lib/python3.12/site-packages/sympy/core/__pycache__/mod.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1a1321dae04e2ccc45a5ebccb4707ba3e7a5ca9d Binary files /dev/null and b/lib/python3.12/site-packages/sympy/core/__pycache__/mod.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/core/__pycache__/mul.cpython-312.pyc b/lib/python3.12/site-packages/sympy/core/__pycache__/mul.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d03300aed29f757c98eb5ab260c0ec003d680d6a Binary files /dev/null and b/lib/python3.12/site-packages/sympy/core/__pycache__/mul.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/core/__pycache__/multidimensional.cpython-312.pyc b/lib/python3.12/site-packages/sympy/core/__pycache__/multidimensional.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5b384a9d68df9495c54f4a930efc68057c821273 Binary files /dev/null and b/lib/python3.12/site-packages/sympy/core/__pycache__/multidimensional.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/core/__pycache__/operations.cpython-312.pyc b/lib/python3.12/site-packages/sympy/core/__pycache__/operations.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..95b62f3ecd4e6956b24d39cd13c2197a8f6c2455 Binary files /dev/null and b/lib/python3.12/site-packages/sympy/core/__pycache__/operations.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/core/__pycache__/parameters.cpython-312.pyc b/lib/python3.12/site-packages/sympy/core/__pycache__/parameters.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..64ada8eeb33bd8265940c1fd22a858b833c9576e Binary files /dev/null and b/lib/python3.12/site-packages/sympy/core/__pycache__/parameters.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/core/__pycache__/power.cpython-312.pyc b/lib/python3.12/site-packages/sympy/core/__pycache__/power.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2e27c702e4898b6c8fd262938ce64d63191cd3f6 Binary files /dev/null and b/lib/python3.12/site-packages/sympy/core/__pycache__/power.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/core/__pycache__/random.cpython-312.pyc b/lib/python3.12/site-packages/sympy/core/__pycache__/random.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..186c472a7f7df25796ddac7c67df01aa3bc1bc94 Binary files /dev/null and b/lib/python3.12/site-packages/sympy/core/__pycache__/random.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/core/__pycache__/relational.cpython-312.pyc b/lib/python3.12/site-packages/sympy/core/__pycache__/relational.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e21380a8798e93c8f3743d376e7c8b4767c045ac Binary files /dev/null and b/lib/python3.12/site-packages/sympy/core/__pycache__/relational.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/core/__pycache__/rules.cpython-312.pyc b/lib/python3.12/site-packages/sympy/core/__pycache__/rules.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..89734fab1affaef0185b7391d3597578fac77ed5 Binary files /dev/null and b/lib/python3.12/site-packages/sympy/core/__pycache__/rules.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/core/__pycache__/singleton.cpython-312.pyc b/lib/python3.12/site-packages/sympy/core/__pycache__/singleton.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fe30b14ce462dd11fa7c917b849e63ddf62ccb0d Binary files /dev/null and b/lib/python3.12/site-packages/sympy/core/__pycache__/singleton.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/core/__pycache__/sorting.cpython-312.pyc b/lib/python3.12/site-packages/sympy/core/__pycache__/sorting.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..979fd1d4e19df8a29ddd45959f38eaa49eed3e62 Binary files /dev/null and b/lib/python3.12/site-packages/sympy/core/__pycache__/sorting.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/core/__pycache__/symbol.cpython-312.pyc b/lib/python3.12/site-packages/sympy/core/__pycache__/symbol.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9eb2ce535d7342a0f8be487c3f2ebafd326ef23c Binary files /dev/null and b/lib/python3.12/site-packages/sympy/core/__pycache__/symbol.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/core/__pycache__/sympify.cpython-312.pyc b/lib/python3.12/site-packages/sympy/core/__pycache__/sympify.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..68a8f85f34c2df151b8496d32f3b26cc72f30900 Binary files /dev/null and b/lib/python3.12/site-packages/sympy/core/__pycache__/sympify.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/core/__pycache__/trace.cpython-312.pyc b/lib/python3.12/site-packages/sympy/core/__pycache__/trace.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..87be4bdc4e525b974b3a5c4c3255281d948381d1 Binary files /dev/null and b/lib/python3.12/site-packages/sympy/core/__pycache__/trace.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/core/__pycache__/traversal.cpython-312.pyc b/lib/python3.12/site-packages/sympy/core/__pycache__/traversal.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b3c9e51dd2874e1f34a30b30daa4da98d596f87a Binary files /dev/null and b/lib/python3.12/site-packages/sympy/core/__pycache__/traversal.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/core/_print_helpers.py b/lib/python3.12/site-packages/sympy/core/_print_helpers.py new file mode 100644 index 0000000000000000000000000000000000000000..d704ed220d444e2d8510b280dca85c8ae6149d4c --- /dev/null +++ b/lib/python3.12/site-packages/sympy/core/_print_helpers.py @@ -0,0 +1,65 @@ +""" +Base class to provide str and repr hooks that `init_printing` can overwrite. + +This is exposed publicly in the `printing.defaults` module, +but cannot be defined there without causing circular imports. +""" + +class Printable: + """ + The default implementation of printing for SymPy classes. + + This implements a hack that allows us to print elements of built-in + Python containers in a readable way. Natively Python uses ``repr()`` + even if ``str()`` was explicitly requested. Mix in this trait into + a class to get proper default printing. + + This also adds support for LaTeX printing in jupyter notebooks. + """ + + # Since this class is used as a mixin we set empty slots. That means that + # instances of any subclasses that use slots will not need to have a + # __dict__. + __slots__ = () + + # Note, we always use the default ordering (lex) in __str__ and __repr__, + # regardless of the global setting. See issue 5487. + def __str__(self): + from sympy.printing.str import sstr + return sstr(self, order=None) + + __repr__ = __str__ + + def _repr_disabled(self): + """ + No-op repr function used to disable jupyter display hooks. + + When :func:`sympy.init_printing` is used to disable certain display + formats, this function is copied into the appropriate ``_repr_*_`` + attributes. + + While we could just set the attributes to `None``, doing it this way + allows derived classes to call `super()`. + """ + return None + + # We don't implement _repr_png_ here because it would add a large amount of + # data to any notebook containing SymPy expressions, without adding + # anything useful to the notebook. It can still enabled manually, e.g., + # for the qtconsole, with init_printing(). + _repr_png_ = _repr_disabled + + _repr_svg_ = _repr_disabled + + def _repr_latex_(self): + """ + IPython/Jupyter LaTeX printing + + To change the behavior of this (e.g., pass in some settings to LaTeX), + use init_printing(). init_printing() will also enable LaTeX printing + for built in numeric types like ints and container types that contain + SymPy objects, like lists and dictionaries of expressions. + """ + from sympy.printing.latex import latex + s = latex(self, mode='plain') + return "$\\displaystyle %s$" % s diff --git a/lib/python3.12/site-packages/sympy/core/add.py b/lib/python3.12/site-packages/sympy/core/add.py new file mode 100644 index 0000000000000000000000000000000000000000..43d7fd72c73f1cf9d01e8467f0af2c2cc9947ec7 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/core/add.py @@ -0,0 +1,1284 @@ +from typing import Tuple as tTuple +from collections import defaultdict +from functools import reduce +from operator import attrgetter +from .basic import _args_sortkey +from .parameters import global_parameters +from .logic import _fuzzy_group, fuzzy_or, fuzzy_not +from .singleton import S +from .operations import AssocOp, AssocOpDispatcher +from .cache import cacheit +from .numbers import equal_valued +from .intfunc import ilcm, igcd +from .expr import Expr +from .kind import UndefinedKind +from sympy.utilities.iterables import is_sequence, sift + +def _could_extract_minus_sign(expr): + # assume expr is Add-like + # We choose the one with less arguments with minus signs + negative_args = sum(1 for i in expr.args + if i.could_extract_minus_sign()) + positive_args = len(expr.args) - negative_args + if positive_args > negative_args: + return False + elif positive_args < negative_args: + return True + # choose based on .sort_key() to prefer + # x - 1 instead of 1 - x and + # 3 - sqrt(2) instead of -3 + sqrt(2) + return bool(expr.sort_key() < (-expr).sort_key()) + + +def _addsort(args): + # in-place sorting of args + args.sort(key=_args_sortkey) + + +def _unevaluated_Add(*args): + """Return a well-formed unevaluated Add: Numbers are collected and + put in slot 0 and args are sorted. Use this when args have changed + but you still want to return an unevaluated Add. + + Examples + ======== + + >>> from sympy.core.add import _unevaluated_Add as uAdd + >>> from sympy import S, Add + >>> from sympy.abc import x, y + >>> a = uAdd(*[S(1.0), x, S(2)]) + >>> a.args[0] + 3.00000000000000 + >>> a.args[1] + x + + Beyond the Number being in slot 0, there is no other assurance of + order for the arguments since they are hash sorted. So, for testing + purposes, output produced by this in some other function can only + be tested against the output of this function or as one of several + options: + + >>> opts = (Add(x, y, evaluate=False), Add(y, x, evaluate=False)) + >>> a = uAdd(x, y) + >>> assert a in opts and a == uAdd(x, y) + >>> uAdd(x + 1, x + 2) + x + x + 3 + """ + args = list(args) + newargs = [] + co = S.Zero + while args: + a = args.pop() + if a.is_Add: + # this will keep nesting from building up + # so that x + (x + 1) -> x + x + 1 (3 args) + args.extend(a.args) + elif a.is_Number: + co += a + else: + newargs.append(a) + _addsort(newargs) + if co: + newargs.insert(0, co) + return Add._from_args(newargs) + + +class Add(Expr, AssocOp): + """ + Expression representing addition operation for algebraic group. + + .. deprecated:: 1.7 + + Using arguments that aren't subclasses of :class:`~.Expr` in core + operators (:class:`~.Mul`, :class:`~.Add`, and :class:`~.Pow`) is + deprecated. See :ref:`non-expr-args-deprecated` for details. + + Every argument of ``Add()`` must be ``Expr``. Infix operator ``+`` + on most scalar objects in SymPy calls this class. + + Another use of ``Add()`` is to represent the structure of abstract + addition so that its arguments can be substituted to return different + class. Refer to examples section for this. + + ``Add()`` evaluates the argument unless ``evaluate=False`` is passed. + The evaluation logic includes: + + 1. Flattening + ``Add(x, Add(y, z))`` -> ``Add(x, y, z)`` + + 2. Identity removing + ``Add(x, 0, y)`` -> ``Add(x, y)`` + + 3. Coefficient collecting by ``.as_coeff_Mul()`` + ``Add(x, 2*x)`` -> ``Mul(3, x)`` + + 4. Term sorting + ``Add(y, x, 2)`` -> ``Add(2, x, y)`` + + If no argument is passed, identity element 0 is returned. If single + element is passed, that element is returned. + + Note that ``Add(*args)`` is more efficient than ``sum(args)`` because + it flattens the arguments. ``sum(a, b, c, ...)`` recursively adds the + arguments as ``a + (b + (c + ...))``, which has quadratic complexity. + On the other hand, ``Add(a, b, c, d)`` does not assume nested + structure, making the complexity linear. + + Since addition is group operation, every argument should have the + same :obj:`sympy.core.kind.Kind()`. + + Examples + ======== + + >>> from sympy import Add, I + >>> from sympy.abc import x, y + >>> Add(x, 1) + x + 1 + >>> Add(x, x) + 2*x + >>> 2*x**2 + 3*x + I*y + 2*y + 2*x/5 + 1.0*y + 1 + 2*x**2 + 17*x/5 + 3.0*y + I*y + 1 + + If ``evaluate=False`` is passed, result is not evaluated. + + >>> Add(1, 2, evaluate=False) + 1 + 2 + >>> Add(x, x, evaluate=False) + x + x + + ``Add()`` also represents the general structure of addition operation. + + >>> from sympy import MatrixSymbol + >>> A,B = MatrixSymbol('A', 2,2), MatrixSymbol('B', 2,2) + >>> expr = Add(x,y).subs({x:A, y:B}) + >>> expr + A + B + >>> type(expr) + + + Note that the printers do not display in args order. + + >>> Add(x, 1) + x + 1 + >>> Add(x, 1).args + (1, x) + + See Also + ======== + + MatAdd + + """ + + __slots__ = () + + args: tTuple[Expr, ...] + + is_Add = True + + _args_type = Expr + + @classmethod + def flatten(cls, seq): + """ + Takes the sequence "seq" of nested Adds and returns a flatten list. + + Returns: (commutative_part, noncommutative_part, order_symbols) + + Applies associativity, all terms are commutable with respect to + addition. + + NB: the removal of 0 is already handled by AssocOp.__new__ + + See Also + ======== + + sympy.core.mul.Mul.flatten + + """ + from sympy.calculus.accumulationbounds import AccumBounds + from sympy.matrices.expressions import MatrixExpr + from sympy.tensor.tensor import TensExpr + rv = None + if len(seq) == 2: + a, b = seq + if b.is_Rational: + a, b = b, a + if a.is_Rational: + if b.is_Mul: + rv = [a, b], [], None + if rv: + if all(s.is_commutative for s in rv[0]): + return rv + return [], rv[0], None + + terms = {} # term -> coeff + # e.g. x**2 -> 5 for ... + 5*x**2 + ... + + coeff = S.Zero # coefficient (Number or zoo) to always be in slot 0 + # e.g. 3 + ... + order_factors = [] + + extra = [] + + for o in seq: + + # O(x) + if o.is_Order: + if o.expr.is_zero: + continue + for o1 in order_factors: + if o1.contains(o): + o = None + break + if o is None: + continue + order_factors = [o] + [ + o1 for o1 in order_factors if not o.contains(o1)] + continue + + # 3 or NaN + elif o.is_Number: + if (o is S.NaN or coeff is S.ComplexInfinity and + o.is_finite is False) and not extra: + # we know for sure the result will be nan + return [S.NaN], [], None + if coeff.is_Number or isinstance(coeff, AccumBounds): + coeff += o + if coeff is S.NaN and not extra: + # we know for sure the result will be nan + return [S.NaN], [], None + continue + + elif isinstance(o, AccumBounds): + coeff = o.__add__(coeff) + continue + + elif isinstance(o, MatrixExpr): + # can't add 0 to Matrix so make sure coeff is not 0 + extra.append(o) + continue + + elif isinstance(o, TensExpr): + coeff = o.__add__(coeff) if coeff else o + continue + + elif o is S.ComplexInfinity: + if coeff.is_finite is False and not extra: + # we know for sure the result will be nan + return [S.NaN], [], None + coeff = S.ComplexInfinity + continue + + # Add([...]) + elif o.is_Add: + # NB: here we assume Add is always commutative + seq.extend(o.args) # TODO zerocopy? + continue + + # Mul([...]) + elif o.is_Mul: + c, s = o.as_coeff_Mul() + + # check for unevaluated Pow, e.g. 2**3 or 2**(-1/2) + elif o.is_Pow: + b, e = o.as_base_exp() + if b.is_Number and (e.is_Integer or + (e.is_Rational and e.is_negative)): + seq.append(b**e) + continue + c, s = S.One, o + + else: + # everything else + c = S.One + s = o + + # now we have: + # o = c*s, where + # + # c is a Number + # s is an expression with number factor extracted + # let's collect terms with the same s, so e.g. + # 2*x**2 + 3*x**2 -> 5*x**2 + if s in terms: + terms[s] += c + if terms[s] is S.NaN and not extra: + # we know for sure the result will be nan + return [S.NaN], [], None + else: + terms[s] = c + + # now let's construct new args: + # [2*x**2, x**3, 7*x**4, pi, ...] + newseq = [] + noncommutative = False + for s, c in terms.items(): + # 0*s + if c.is_zero: + continue + # 1*s + elif c is S.One: + newseq.append(s) + # c*s + else: + if s.is_Mul: + # Mul, already keeps its arguments in perfect order. + # so we can simply put c in slot0 and go the fast way. + cs = s._new_rawargs(*((c,) + s.args)) + newseq.append(cs) + elif s.is_Add: + # we just re-create the unevaluated Mul + newseq.append(Mul(c, s, evaluate=False)) + else: + # alternatively we have to call all Mul's machinery (slow) + newseq.append(Mul(c, s)) + + noncommutative = noncommutative or not s.is_commutative + + # oo, -oo + if coeff is S.Infinity: + newseq = [f for f in newseq if not (f.is_extended_nonnegative or f.is_real)] + + elif coeff is S.NegativeInfinity: + newseq = [f for f in newseq if not (f.is_extended_nonpositive or f.is_real)] + + if coeff is S.ComplexInfinity: + # zoo might be + # infinite_real + finite_im + # finite_real + infinite_im + # infinite_real + infinite_im + # addition of a finite real or imaginary number won't be able to + # change the zoo nature; adding an infinite qualtity would result + # in a NaN condition if it had sign opposite of the infinite + # portion of zoo, e.g., infinite_real - infinite_real. + newseq = [c for c in newseq if not (c.is_finite and + c.is_extended_real is not None)] + + # process O(x) + if order_factors: + newseq2 = [] + for t in newseq: + for o in order_factors: + # x + O(x) -> O(x) + if o.contains(t): + t = None + break + # x + O(x**2) -> x + O(x**2) + if t is not None: + newseq2.append(t) + newseq = newseq2 + order_factors + # 1 + O(1) -> O(1) + for o in order_factors: + if o.contains(coeff): + coeff = S.Zero + break + + # order args canonically + _addsort(newseq) + + # current code expects coeff to be first + if coeff is not S.Zero: + newseq.insert(0, coeff) + + if extra: + newseq += extra + noncommutative = True + + # we are done + if noncommutative: + return [], newseq, None + else: + return newseq, [], None + + @classmethod + def class_key(cls): + return 3, 1, cls.__name__ + + @property + def kind(self): + k = attrgetter('kind') + kinds = map(k, self.args) + kinds = frozenset(kinds) + if len(kinds) != 1: + # Since addition is group operator, kind must be same. + # We know that this is unexpected signature, so return this. + result = UndefinedKind + else: + result, = kinds + return result + + def could_extract_minus_sign(self): + return _could_extract_minus_sign(self) + + @cacheit + def as_coeff_add(self, *deps): + """ + Returns a tuple (coeff, args) where self is treated as an Add and coeff + is the Number term and args is a tuple of all other terms. + + Examples + ======== + + >>> from sympy.abc import x + >>> (7 + 3*x).as_coeff_add() + (7, (3*x,)) + >>> (7*x).as_coeff_add() + (0, (7*x,)) + """ + if deps: + l1, l2 = sift(self.args, lambda x: x.has_free(*deps), binary=True) + return self._new_rawargs(*l2), tuple(l1) + coeff, notrat = self.args[0].as_coeff_add() + if coeff is not S.Zero: + return coeff, notrat + self.args[1:] + return S.Zero, self.args + + def as_coeff_Add(self, rational=False, deps=None): + """ + Efficiently extract the coefficient of a summation. + """ + coeff, args = self.args[0], self.args[1:] + + if coeff.is_Number and not rational or coeff.is_Rational: + return coeff, self._new_rawargs(*args) + return S.Zero, self + + # Note, we intentionally do not implement Add.as_coeff_mul(). Rather, we + # let Expr.as_coeff_mul() just always return (S.One, self) for an Add. See + # issue 5524. + + def _eval_power(self, e): + from .evalf import pure_complex + from .relational import is_eq + if len(self.args) == 2 and any(_.is_infinite for _ in self.args): + if e.is_zero is False and is_eq(e, S.One) is False: + # looking for literal a + I*b + a, b = self.args + if a.coeff(S.ImaginaryUnit): + a, b = b, a + ico = b.coeff(S.ImaginaryUnit) + if ico and ico.is_extended_real and a.is_extended_real: + if e.is_extended_negative: + return S.Zero + if e.is_extended_positive: + return S.ComplexInfinity + return + if e.is_Rational and self.is_number: + ri = pure_complex(self) + if ri: + r, i = ri + if e.q == 2: + from sympy.functions.elementary.miscellaneous import sqrt + D = sqrt(r**2 + i**2) + if D.is_Rational: + from .exprtools import factor_terms + from sympy.functions.elementary.complexes import sign + from .function import expand_multinomial + # (r, i, D) is a Pythagorean triple + root = sqrt(factor_terms((D - r)/2))**e.p + return root*expand_multinomial(( + # principle value + (D + r)/abs(i) + sign(i)*S.ImaginaryUnit)**e.p) + elif e == -1: + return _unevaluated_Mul( + r - i*S.ImaginaryUnit, + 1/(r**2 + i**2)) + elif e.is_Number and abs(e) != 1: + # handle the Float case: (2.0 + 4*x)**e -> 4**e*(0.5 + x)**e + c, m = zip(*[i.as_coeff_Mul() for i in self.args]) + if any(i.is_Float for i in c): # XXX should this always be done? + big = -1 + for i in c: + if abs(i) >= big: + big = abs(i) + if big > 0 and not equal_valued(big, 1): + from sympy.functions.elementary.complexes import sign + bigs = (big, -big) + c = [sign(i) if i in bigs else i/big for i in c] + addpow = Add(*[c*m for c, m in zip(c, m)])**e + return big**e*addpow + + @cacheit + def _eval_derivative(self, s): + return self.func(*[a.diff(s) for a in self.args]) + + def _eval_nseries(self, x, n, logx, cdir=0): + terms = [t.nseries(x, n=n, logx=logx, cdir=cdir) for t in self.args] + return self.func(*terms) + + def _matches_simple(self, expr, repl_dict): + # handle (w+3).matches('x+5') -> {w: x+2} + coeff, terms = self.as_coeff_add() + if len(terms) == 1: + return terms[0].matches(expr - coeff, repl_dict) + return + + def matches(self, expr, repl_dict=None, old=False): + return self._matches_commutative(expr, repl_dict, old) + + @staticmethod + def _combine_inverse(lhs, rhs): + """ + Returns lhs - rhs, but treats oo like a symbol so oo - oo + returns 0, instead of a nan. + """ + from sympy.simplify.simplify import signsimp + inf = (S.Infinity, S.NegativeInfinity) + if lhs.has(*inf) or rhs.has(*inf): + from .symbol import Dummy + oo = Dummy('oo') + reps = { + S.Infinity: oo, + S.NegativeInfinity: -oo} + ireps = {v: k for k, v in reps.items()} + eq = lhs.xreplace(reps) - rhs.xreplace(reps) + if eq.has(oo): + eq = eq.replace( + lambda x: x.is_Pow and x.base is oo, + lambda x: x.base) + rv = eq.xreplace(ireps) + else: + rv = lhs - rhs + srv = signsimp(rv) + return srv if srv.is_Number else rv + + @cacheit + def as_two_terms(self): + """Return head and tail of self. + + This is the most efficient way to get the head and tail of an + expression. + + - if you want only the head, use self.args[0]; + - if you want to process the arguments of the tail then use + self.as_coef_add() which gives the head and a tuple containing + the arguments of the tail when treated as an Add. + - if you want the coefficient when self is treated as a Mul + then use self.as_coeff_mul()[0] + + >>> from sympy.abc import x, y + >>> (3*x - 2*y + 5).as_two_terms() + (5, 3*x - 2*y) + """ + return self.args[0], self._new_rawargs(*self.args[1:]) + + def as_numer_denom(self): + """ + Decomposes an expression to its numerator part and its + denominator part. + + Examples + ======== + + >>> from sympy.abc import x, y, z + >>> (x*y/z).as_numer_denom() + (x*y, z) + >>> (x*(y + 1)/y**7).as_numer_denom() + (x*(y + 1), y**7) + + See Also + ======== + + sympy.core.expr.Expr.as_numer_denom + """ + # clear rational denominator + content, expr = self.primitive() + if not isinstance(expr, Add): + return Mul(content, expr, evaluate=False).as_numer_denom() + ncon, dcon = content.as_numer_denom() + + # collect numerators and denominators of the terms + nd = defaultdict(list) + for f in expr.args: + ni, di = f.as_numer_denom() + nd[di].append(ni) + + # check for quick exit + if len(nd) == 1: + d, n = nd.popitem() + return self.func( + *[_keep_coeff(ncon, ni) for ni in n]), _keep_coeff(dcon, d) + + # sum up the terms having a common denominator + for d, n in nd.items(): + if len(n) == 1: + nd[d] = n[0] + else: + nd[d] = self.func(*n) + + # assemble single numerator and denominator + denoms, numers = [list(i) for i in zip(*iter(nd.items()))] + n, d = self.func(*[Mul(*(denoms[:i] + [numers[i]] + denoms[i + 1:])) + for i in range(len(numers))]), Mul(*denoms) + + return _keep_coeff(ncon, n), _keep_coeff(dcon, d) + + def _eval_is_polynomial(self, syms): + return all(term._eval_is_polynomial(syms) for term in self.args) + + def _eval_is_rational_function(self, syms): + return all(term._eval_is_rational_function(syms) for term in self.args) + + def _eval_is_meromorphic(self, x, a): + return _fuzzy_group((arg.is_meromorphic(x, a) for arg in self.args), + quick_exit=True) + + def _eval_is_algebraic_expr(self, syms): + return all(term._eval_is_algebraic_expr(syms) for term in self.args) + + # assumption methods + _eval_is_real = lambda self: _fuzzy_group( + (a.is_real for a in self.args), quick_exit=True) + _eval_is_extended_real = lambda self: _fuzzy_group( + (a.is_extended_real for a in self.args), quick_exit=True) + _eval_is_complex = lambda self: _fuzzy_group( + (a.is_complex for a in self.args), quick_exit=True) + _eval_is_antihermitian = lambda self: _fuzzy_group( + (a.is_antihermitian for a in self.args), quick_exit=True) + _eval_is_finite = lambda self: _fuzzy_group( + (a.is_finite for a in self.args), quick_exit=True) + _eval_is_hermitian = lambda self: _fuzzy_group( + (a.is_hermitian for a in self.args), quick_exit=True) + _eval_is_integer = lambda self: _fuzzy_group( + (a.is_integer for a in self.args), quick_exit=True) + _eval_is_rational = lambda self: _fuzzy_group( + (a.is_rational for a in self.args), quick_exit=True) + _eval_is_algebraic = lambda self: _fuzzy_group( + (a.is_algebraic for a in self.args), quick_exit=True) + _eval_is_commutative = lambda self: _fuzzy_group( + a.is_commutative for a in self.args) + + def _eval_is_infinite(self): + sawinf = False + for a in self.args: + ainf = a.is_infinite + if ainf is None: + return None + elif ainf is True: + # infinite+infinite might not be infinite + if sawinf is True: + return None + sawinf = True + return sawinf + + def _eval_is_imaginary(self): + nz = [] + im_I = [] + for a in self.args: + if a.is_extended_real: + if a.is_zero: + pass + elif a.is_zero is False: + nz.append(a) + else: + return + elif a.is_imaginary: + im_I.append(a*S.ImaginaryUnit) + elif a.is_Mul and S.ImaginaryUnit in a.args: + coeff, ai = a.as_coeff_mul(S.ImaginaryUnit) + if ai == (S.ImaginaryUnit,) and coeff.is_extended_real: + im_I.append(-coeff) + else: + return + else: + return + b = self.func(*nz) + if b != self: + if b.is_zero: + return fuzzy_not(self.func(*im_I).is_zero) + elif b.is_zero is False: + return False + + def _eval_is_zero(self): + if self.is_commutative is False: + # issue 10528: there is no way to know if a nc symbol + # is zero or not + return + nz = [] + z = 0 + im_or_z = False + im = 0 + for a in self.args: + if a.is_extended_real: + if a.is_zero: + z += 1 + elif a.is_zero is False: + nz.append(a) + else: + return + elif a.is_imaginary: + im += 1 + elif a.is_Mul and S.ImaginaryUnit in a.args: + coeff, ai = a.as_coeff_mul(S.ImaginaryUnit) + if ai == (S.ImaginaryUnit,) and coeff.is_extended_real: + im_or_z = True + else: + return + else: + return + if z == len(self.args): + return True + if len(nz) in [0, len(self.args)]: + return None + b = self.func(*nz) + if b.is_zero: + if not im_or_z: + if im == 0: + return True + elif im == 1: + return False + if b.is_zero is False: + return False + + def _eval_is_odd(self): + l = [f for f in self.args if not (f.is_even is True)] + if not l: + return False + if l[0].is_odd: + return self._new_rawargs(*l[1:]).is_even + + def _eval_is_irrational(self): + for t in self.args: + a = t.is_irrational + if a: + others = list(self.args) + others.remove(t) + if all(x.is_rational is True for x in others): + return True + return None + if a is None: + return + return False + + def _all_nonneg_or_nonppos(self): + nn = np = 0 + for a in self.args: + if a.is_nonnegative: + if np: + return False + nn = 1 + elif a.is_nonpositive: + if nn: + return False + np = 1 + else: + break + else: + return True + + def _eval_is_extended_positive(self): + if self.is_number: + return super()._eval_is_extended_positive() + c, a = self.as_coeff_Add() + if not c.is_zero: + from .exprtools import _monotonic_sign + v = _monotonic_sign(a) + if v is not None: + s = v + c + if s != self and s.is_extended_positive and a.is_extended_nonnegative: + return True + if len(self.free_symbols) == 1: + v = _monotonic_sign(self) + if v is not None and v != self and v.is_extended_positive: + return True + pos = nonneg = nonpos = unknown_sign = False + saw_INF = set() + args = [a for a in self.args if not a.is_zero] + if not args: + return False + for a in args: + ispos = a.is_extended_positive + infinite = a.is_infinite + if infinite: + saw_INF.add(fuzzy_or((ispos, a.is_extended_nonnegative))) + if True in saw_INF and False in saw_INF: + return + if ispos: + pos = True + continue + elif a.is_extended_nonnegative: + nonneg = True + continue + elif a.is_extended_nonpositive: + nonpos = True + continue + + if infinite is None: + return + unknown_sign = True + + if saw_INF: + if len(saw_INF) > 1: + return + return saw_INF.pop() + elif unknown_sign: + return + elif not nonpos and not nonneg and pos: + return True + elif not nonpos and pos: + return True + elif not pos and not nonneg: + return False + + def _eval_is_extended_nonnegative(self): + if not self.is_number: + c, a = self.as_coeff_Add() + if not c.is_zero and a.is_extended_nonnegative: + from .exprtools import _monotonic_sign + v = _monotonic_sign(a) + if v is not None: + s = v + c + if s != self and s.is_extended_nonnegative: + return True + if len(self.free_symbols) == 1: + v = _monotonic_sign(self) + if v is not None and v != self and v.is_extended_nonnegative: + return True + + def _eval_is_extended_nonpositive(self): + if not self.is_number: + c, a = self.as_coeff_Add() + if not c.is_zero and a.is_extended_nonpositive: + from .exprtools import _monotonic_sign + v = _monotonic_sign(a) + if v is not None: + s = v + c + if s != self and s.is_extended_nonpositive: + return True + if len(self.free_symbols) == 1: + v = _monotonic_sign(self) + if v is not None and v != self and v.is_extended_nonpositive: + return True + + def _eval_is_extended_negative(self): + if self.is_number: + return super()._eval_is_extended_negative() + c, a = self.as_coeff_Add() + if not c.is_zero: + from .exprtools import _monotonic_sign + v = _monotonic_sign(a) + if v is not None: + s = v + c + if s != self and s.is_extended_negative and a.is_extended_nonpositive: + return True + if len(self.free_symbols) == 1: + v = _monotonic_sign(self) + if v is not None and v != self and v.is_extended_negative: + return True + neg = nonpos = nonneg = unknown_sign = False + saw_INF = set() + args = [a for a in self.args if not a.is_zero] + if not args: + return False + for a in args: + isneg = a.is_extended_negative + infinite = a.is_infinite + if infinite: + saw_INF.add(fuzzy_or((isneg, a.is_extended_nonpositive))) + if True in saw_INF and False in saw_INF: + return + if isneg: + neg = True + continue + elif a.is_extended_nonpositive: + nonpos = True + continue + elif a.is_extended_nonnegative: + nonneg = True + continue + + if infinite is None: + return + unknown_sign = True + + if saw_INF: + if len(saw_INF) > 1: + return + return saw_INF.pop() + elif unknown_sign: + return + elif not nonneg and not nonpos and neg: + return True + elif not nonneg and neg: + return True + elif not neg and not nonpos: + return False + + def _eval_subs(self, old, new): + if not old.is_Add: + if old is S.Infinity and -old in self.args: + # foo - oo is foo + (-oo) internally + return self.xreplace({-old: -new}) + return None + + coeff_self, terms_self = self.as_coeff_Add() + coeff_old, terms_old = old.as_coeff_Add() + + if coeff_self.is_Rational and coeff_old.is_Rational: + if terms_self == terms_old: # (2 + a).subs( 3 + a, y) -> -1 + y + return self.func(new, coeff_self, -coeff_old) + if terms_self == -terms_old: # (2 + a).subs(-3 - a, y) -> -1 - y + return self.func(-new, coeff_self, coeff_old) + + if coeff_self.is_Rational and coeff_old.is_Rational \ + or coeff_self == coeff_old: + args_old, args_self = self.func.make_args( + terms_old), self.func.make_args(terms_self) + if len(args_old) < len(args_self): # (a+b+c).subs(b+c,x) -> a+x + self_set = set(args_self) + old_set = set(args_old) + + if old_set < self_set: + ret_set = self_set - old_set + return self.func(new, coeff_self, -coeff_old, + *[s._subs(old, new) for s in ret_set]) + + args_old = self.func.make_args( + -terms_old) # (a+b+c+d).subs(-b-c,x) -> a-x+d + old_set = set(args_old) + if old_set < self_set: + ret_set = self_set - old_set + return self.func(-new, coeff_self, coeff_old, + *[s._subs(old, new) for s in ret_set]) + + def removeO(self): + args = [a for a in self.args if not a.is_Order] + return self._new_rawargs(*args) + + def getO(self): + args = [a for a in self.args if a.is_Order] + if args: + return self._new_rawargs(*args) + + @cacheit + def extract_leading_order(self, symbols, point=None): + """ + Returns the leading term and its order. + + Examples + ======== + + >>> from sympy.abc import x + >>> (x + 1 + 1/x**5).extract_leading_order(x) + ((x**(-5), O(x**(-5))),) + >>> (1 + x).extract_leading_order(x) + ((1, O(1)),) + >>> (x + x**2).extract_leading_order(x) + ((x, O(x)),) + + """ + from sympy.series.order import Order + lst = [] + symbols = list(symbols if is_sequence(symbols) else [symbols]) + if not point: + point = [0]*len(symbols) + seq = [(f, Order(f, *zip(symbols, point))) for f in self.args] + for ef, of in seq: + for e, o in lst: + if o.contains(of) and o != of: + of = None + break + if of is None: + continue + new_lst = [(ef, of)] + for e, o in lst: + if of.contains(o) and o != of: + continue + new_lst.append((e, o)) + lst = new_lst + return tuple(lst) + + def as_real_imag(self, deep=True, **hints): + """ + Return a tuple representing a complex number. + + Examples + ======== + + >>> from sympy import I + >>> (7 + 9*I).as_real_imag() + (7, 9) + >>> ((1 + I)/(1 - I)).as_real_imag() + (0, 1) + >>> ((1 + 2*I)*(1 + 3*I)).as_real_imag() + (-5, 5) + """ + sargs = self.args + re_part, im_part = [], [] + for term in sargs: + re, im = term.as_real_imag(deep=deep) + re_part.append(re) + im_part.append(im) + return (self.func(*re_part), self.func(*im_part)) + + def _eval_as_leading_term(self, x, logx=None, cdir=0): + from sympy.core.symbol import Dummy, Symbol + from sympy.series.order import Order + from sympy.functions.elementary.exponential import log + from sympy.functions.elementary.piecewise import Piecewise, piecewise_fold + from .function import expand_mul + + o = self.getO() + if o is None: + o = Order(0) + old = self.removeO() + + if old.has(Piecewise): + old = piecewise_fold(old) + + # This expansion is the last part of expand_log. expand_log also calls + # expand_mul with factor=True, which would be more expensive + if any(isinstance(a, log) for a in self.args): + logflags = {"deep": True, "log": True, "mul": False, "power_exp": False, + "power_base": False, "multinomial": False, "basic": False, "force": False, + "factor": False} + old = old.expand(**logflags) + expr = expand_mul(old) + + if not expr.is_Add: + return expr.as_leading_term(x, logx=logx, cdir=cdir) + + infinite = [t for t in expr.args if t.is_infinite] + + _logx = Dummy('logx') if logx is None else logx + leading_terms = [t.as_leading_term(x, logx=_logx, cdir=cdir) for t in expr.args] + + min, new_expr = Order(0), 0 + + try: + for term in leading_terms: + order = Order(term, x) + if not min or order not in min: + min = order + new_expr = term + elif min in order: + new_expr += term + + except TypeError: + return expr + + if logx is None: + new_expr = new_expr.subs(_logx, log(x)) + + is_zero = new_expr.is_zero + if is_zero is None: + new_expr = new_expr.trigsimp().cancel() + is_zero = new_expr.is_zero + if is_zero is True: + # simple leading term analysis gave us cancelled terms but we have to send + # back a term, so compute the leading term (via series) + try: + n0 = min.getn() + except NotImplementedError: + n0 = S.One + if n0.has(Symbol): + n0 = S.One + res = Order(1) + incr = S.One + while res.is_Order: + res = old._eval_nseries(x, n=n0+incr, logx=logx, cdir=cdir).cancel().powsimp().trigsimp() + incr *= 2 + return res.as_leading_term(x, logx=logx, cdir=cdir) + + elif new_expr is S.NaN: + return old.func._from_args(infinite) + o + + else: + return new_expr + + def _eval_adjoint(self): + return self.func(*[t.adjoint() for t in self.args]) + + def _eval_conjugate(self): + return self.func(*[t.conjugate() for t in self.args]) + + def _eval_transpose(self): + return self.func(*[t.transpose() for t in self.args]) + + def primitive(self): + """ + Return ``(R, self/R)`` where ``R``` is the Rational GCD of ``self```. + + ``R`` is collected only from the leading coefficient of each term. + + Examples + ======== + + >>> from sympy.abc import x, y + + >>> (2*x + 4*y).primitive() + (2, x + 2*y) + + >>> (2*x/3 + 4*y/9).primitive() + (2/9, 3*x + 2*y) + + >>> (2*x/3 + 4.2*y).primitive() + (1/3, 2*x + 12.6*y) + + No subprocessing of term factors is performed: + + >>> ((2 + 2*x)*x + 2).primitive() + (1, x*(2*x + 2) + 2) + + Recursive processing can be done with the ``as_content_primitive()`` + method: + + >>> ((2 + 2*x)*x + 2).as_content_primitive() + (2, x*(x + 1) + 1) + + See also: primitive() function in polytools.py + + """ + + terms = [] + inf = False + for a in self.args: + c, m = a.as_coeff_Mul() + if not c.is_Rational: + c = S.One + m = a + inf = inf or m is S.ComplexInfinity + terms.append((c.p, c.q, m)) + + if not inf: + ngcd = reduce(igcd, [t[0] for t in terms], 0) + dlcm = reduce(ilcm, [t[1] for t in terms], 1) + else: + ngcd = reduce(igcd, [t[0] for t in terms if t[1]], 0) + dlcm = reduce(ilcm, [t[1] for t in terms if t[1]], 1) + + if ngcd == dlcm == 1: + return S.One, self + if not inf: + for i, (p, q, term) in enumerate(terms): + terms[i] = _keep_coeff(Rational((p//ngcd)*(dlcm//q)), term) + else: + for i, (p, q, term) in enumerate(terms): + if q: + terms[i] = _keep_coeff(Rational((p//ngcd)*(dlcm//q)), term) + else: + terms[i] = _keep_coeff(Rational(p, q), term) + + # we don't need a complete re-flattening since no new terms will join + # so we just use the same sort as is used in Add.flatten. When the + # coefficient changes, the ordering of terms may change, e.g. + # (3*x, 6*y) -> (2*y, x) + # + # We do need to make sure that term[0] stays in position 0, however. + # + if terms[0].is_Number or terms[0] is S.ComplexInfinity: + c = terms.pop(0) + else: + c = None + _addsort(terms) + if c: + terms.insert(0, c) + return Rational(ngcd, dlcm), self._new_rawargs(*terms) + + def as_content_primitive(self, radical=False, clear=True): + """Return the tuple (R, self/R) where R is the positive Rational + extracted from self. If radical is True (default is False) then + common radicals will be removed and included as a factor of the + primitive expression. + + Examples + ======== + + >>> from sympy import sqrt + >>> (3 + 3*sqrt(2)).as_content_primitive() + (3, 1 + sqrt(2)) + + Radical content can also be factored out of the primitive: + + >>> (2*sqrt(2) + 4*sqrt(10)).as_content_primitive(radical=True) + (2, sqrt(2)*(1 + 2*sqrt(5))) + + See docstring of Expr.as_content_primitive for more examples. + """ + con, prim = self.func(*[_keep_coeff(*a.as_content_primitive( + radical=radical, clear=clear)) for a in self.args]).primitive() + if not clear and not con.is_Integer and prim.is_Add: + con, d = con.as_numer_denom() + _p = prim/d + if any(a.as_coeff_Mul()[0].is_Integer for a in _p.args): + prim = _p + else: + con /= d + if radical and prim.is_Add: + # look for common radicals that can be removed + args = prim.args + rads = [] + common_q = None + for m in args: + term_rads = defaultdict(list) + for ai in Mul.make_args(m): + if ai.is_Pow: + b, e = ai.as_base_exp() + if e.is_Rational and b.is_Integer: + term_rads[e.q].append(abs(int(b))**e.p) + if not term_rads: + break + if common_q is None: + common_q = set(term_rads.keys()) + else: + common_q = common_q & set(term_rads.keys()) + if not common_q: + break + rads.append(term_rads) + else: + # process rads + # keep only those in common_q + for r in rads: + for q in list(r.keys()): + if q not in common_q: + r.pop(q) + for q in r: + r[q] = Mul(*r[q]) + # find the gcd of bases for each q + G = [] + for q in common_q: + g = reduce(igcd, [r[q] for r in rads], 0) + if g != 1: + G.append(g**Rational(1, q)) + if G: + G = Mul(*G) + args = [ai/G for ai in args] + prim = G*prim.func(*args) + + return con, prim + + @property + def _sorted_args(self): + from .sorting import default_sort_key + return tuple(sorted(self.args, key=default_sort_key)) + + def _eval_difference_delta(self, n, step): + from sympy.series.limitseq import difference_delta as dd + return self.func(*[dd(a, n, step) for a in self.args]) + + @property + def _mpc_(self): + """ + Convert self to an mpmath mpc if possible + """ + from .numbers import Float + re_part, rest = self.as_coeff_Add() + im_part, imag_unit = rest.as_coeff_Mul() + if not imag_unit == S.ImaginaryUnit: + # ValueError may seem more reasonable but since it's a @property, + # we need to use AttributeError to keep from confusing things like + # hasattr. + raise AttributeError("Cannot convert Add to mpc. Must be of the form Number + Number*I") + + return (Float(re_part)._mpf_, Float(im_part)._mpf_) + + def __neg__(self): + if not global_parameters.distribute: + return super().__neg__() + return Mul(S.NegativeOne, self) + +add = AssocOpDispatcher('add') + +from .mul import Mul, _keep_coeff, _unevaluated_Mul +from .numbers import Rational diff --git a/lib/python3.12/site-packages/sympy/core/alphabets.py b/lib/python3.12/site-packages/sympy/core/alphabets.py new file mode 100644 index 0000000000000000000000000000000000000000..1ea2ae1c410ccd30e7ec9551f4cd8b19a36cdba1 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/core/alphabets.py @@ -0,0 +1,4 @@ +greeks = ('alpha', 'beta', 'gamma', 'delta', 'epsilon', 'zeta', + 'eta', 'theta', 'iota', 'kappa', 'lambda', 'mu', 'nu', + 'xi', 'omicron', 'pi', 'rho', 'sigma', 'tau', 'upsilon', + 'phi', 'chi', 'psi', 'omega') diff --git a/lib/python3.12/site-packages/sympy/core/assumptions.py b/lib/python3.12/site-packages/sympy/core/assumptions.py new file mode 100644 index 0000000000000000000000000000000000000000..677e86c5e39390b0b188a5158dd2fabfbac4c760 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/core/assumptions.py @@ -0,0 +1,692 @@ +""" +This module contains the machinery handling assumptions. +Do also consider the guide :ref:`assumptions-guide`. + +All symbolic objects have assumption attributes that can be accessed via +``.is_`` attribute. + +Assumptions determine certain properties of symbolic objects and can +have 3 possible values: ``True``, ``False``, ``None``. ``True`` is returned if the +object has the property and ``False`` is returned if it does not or cannot +(i.e. does not make sense): + + >>> from sympy import I + >>> I.is_algebraic + True + >>> I.is_real + False + >>> I.is_prime + False + +When the property cannot be determined (or when a method is not +implemented) ``None`` will be returned. For example, a generic symbol, ``x``, +may or may not be positive so a value of ``None`` is returned for ``x.is_positive``. + +By default, all symbolic values are in the largest set in the given context +without specifying the property. For example, a symbol that has a property +being integer, is also real, complex, etc. + +Here follows a list of possible assumption names: + +.. glossary:: + + commutative + object commutes with any other object with + respect to multiplication operation. See [12]_. + + complex + object can have only values from the set + of complex numbers. See [13]_. + + imaginary + object value is a number that can be written as a real + number multiplied by the imaginary unit ``I``. See + [3]_. Please note that ``0`` is not considered to be an + imaginary number, see + `issue #7649 `_. + + real + object can have only values from the set + of real numbers. + + extended_real + object can have only values from the set + of real numbers, ``oo`` and ``-oo``. + + integer + object can have only values from the set + of integers. + + odd + even + object can have only values from the set of + odd (even) integers [2]_. + + prime + object is a natural number greater than 1 that has + no positive divisors other than 1 and itself. See [6]_. + + composite + object is a positive integer that has at least one positive + divisor other than 1 or the number itself. See [4]_. + + zero + object has the value of 0. + + nonzero + object is a real number that is not zero. + + rational + object can have only values from the set + of rationals. + + algebraic + object can have only values from the set + of algebraic numbers [11]_. + + transcendental + object can have only values from the set + of transcendental numbers [10]_. + + irrational + object value cannot be represented exactly by :class:`~.Rational`, see [5]_. + + finite + infinite + object absolute value is bounded (arbitrarily large). + See [7]_, [8]_, [9]_. + + negative + nonnegative + object can have only negative (nonnegative) + values [1]_. + + positive + nonpositive + object can have only positive (nonpositive) values. + + extended_negative + extended_nonnegative + extended_positive + extended_nonpositive + extended_nonzero + as without the extended part, but also including infinity with + corresponding sign, e.g., extended_positive includes ``oo`` + + hermitian + antihermitian + object belongs to the field of Hermitian + (antihermitian) operators. + +Examples +======== + + >>> from sympy import Symbol + >>> x = Symbol('x', real=True); x + x + >>> x.is_real + True + >>> x.is_complex + True + +See Also +======== + +.. seealso:: + + :py:class:`sympy.core.numbers.ImaginaryUnit` + :py:class:`sympy.core.numbers.Zero` + :py:class:`sympy.core.numbers.One` + :py:class:`sympy.core.numbers.Infinity` + :py:class:`sympy.core.numbers.NegativeInfinity` + :py:class:`sympy.core.numbers.ComplexInfinity` + +Notes +===== + +The fully-resolved assumptions for any SymPy expression +can be obtained as follows: + + >>> from sympy.core.assumptions import assumptions + >>> x = Symbol('x',positive=True) + >>> assumptions(x + I) + {'commutative': True, 'complex': True, 'composite': False, 'even': + False, 'extended_negative': False, 'extended_nonnegative': False, + 'extended_nonpositive': False, 'extended_nonzero': False, + 'extended_positive': False, 'extended_real': False, 'finite': True, + 'imaginary': False, 'infinite': False, 'integer': False, 'irrational': + False, 'negative': False, 'noninteger': False, 'nonnegative': False, + 'nonpositive': False, 'nonzero': False, 'odd': False, 'positive': + False, 'prime': False, 'rational': False, 'real': False, 'zero': + False} + +Developers Notes +================ + +The current (and possibly incomplete) values are stored +in the ``obj._assumptions dictionary``; queries to getter methods +(with property decorators) or attributes of objects/classes +will return values and update the dictionary. + + >>> eq = x**2 + I + >>> eq._assumptions + {} + >>> eq.is_finite + True + >>> eq._assumptions + {'finite': True, 'infinite': False} + +For a :class:`~.Symbol`, there are two locations for assumptions that may +be of interest. The ``assumptions0`` attribute gives the full set of +assumptions derived from a given set of initial assumptions. The +latter assumptions are stored as ``Symbol._assumptions_orig`` + + >>> Symbol('x', prime=True, even=True)._assumptions_orig + {'even': True, 'prime': True} + +The ``_assumptions_orig`` are not necessarily canonical nor are they filtered +in any way: they records the assumptions used to instantiate a Symbol and (for +storage purposes) represent a more compact representation of the assumptions +needed to recreate the full set in ``Symbol.assumptions0``. + + +References +========== + +.. [1] https://en.wikipedia.org/wiki/Negative_number +.. [2] https://en.wikipedia.org/wiki/Parity_%28mathematics%29 +.. [3] https://en.wikipedia.org/wiki/Imaginary_number +.. [4] https://en.wikipedia.org/wiki/Composite_number +.. [5] https://en.wikipedia.org/wiki/Irrational_number +.. [6] https://en.wikipedia.org/wiki/Prime_number +.. [7] https://en.wikipedia.org/wiki/Finite +.. [8] https://docs.python.org/3/library/math.html#math.isfinite +.. [9] https://numpy.org/doc/stable/reference/generated/numpy.isfinite.html +.. [10] https://en.wikipedia.org/wiki/Transcendental_number +.. [11] https://en.wikipedia.org/wiki/Algebraic_number +.. [12] https://en.wikipedia.org/wiki/Commutative_property +.. [13] https://en.wikipedia.org/wiki/Complex_number + +""" + +from sympy.utilities.exceptions import sympy_deprecation_warning + +from .facts import FactRules, FactKB +from .sympify import sympify + +from sympy.core.random import _assumptions_shuffle as shuffle +from sympy.core.assumptions_generated import generated_assumptions as _assumptions + +def _load_pre_generated_assumption_rules() -> FactRules: + """ Load the assumption rules from pre-generated data + + To update the pre-generated data, see :method::`_generate_assumption_rules` + """ + _assume_rules=FactRules._from_python(_assumptions) + return _assume_rules + +def _generate_assumption_rules(): + """ Generate the default assumption rules + + This method should only be called to update the pre-generated + assumption rules. + + To update the pre-generated assumptions run: bin/ask_update.py + + """ + _assume_rules = FactRules([ + + 'integer -> rational', + 'rational -> real', + 'rational -> algebraic', + 'algebraic -> complex', + 'transcendental == complex & !algebraic', + 'real -> hermitian', + 'imaginary -> complex', + 'imaginary -> antihermitian', + 'extended_real -> commutative', + 'complex -> commutative', + 'complex -> finite', + + 'odd == integer & !even', + 'even == integer & !odd', + + 'real -> complex', + 'extended_real -> real | infinite', + 'real == extended_real & finite', + + 'extended_real == extended_negative | zero | extended_positive', + 'extended_negative == extended_nonpositive & extended_nonzero', + 'extended_positive == extended_nonnegative & extended_nonzero', + + 'extended_nonpositive == extended_real & !extended_positive', + 'extended_nonnegative == extended_real & !extended_negative', + + 'real == negative | zero | positive', + 'negative == nonpositive & nonzero', + 'positive == nonnegative & nonzero', + + 'nonpositive == real & !positive', + 'nonnegative == real & !negative', + + 'positive == extended_positive & finite', + 'negative == extended_negative & finite', + 'nonpositive == extended_nonpositive & finite', + 'nonnegative == extended_nonnegative & finite', + 'nonzero == extended_nonzero & finite', + + 'zero -> even & finite', + 'zero == extended_nonnegative & extended_nonpositive', + 'zero == nonnegative & nonpositive', + 'nonzero -> real', + + 'prime -> integer & positive', + 'composite -> integer & positive & !prime', + '!composite -> !positive | !even | prime', + + 'irrational == real & !rational', + + 'imaginary -> !extended_real', + + 'infinite == !finite', + 'noninteger == extended_real & !integer', + 'extended_nonzero == extended_real & !zero', + ]) + return _assume_rules + + +_assume_rules = _load_pre_generated_assumption_rules() +_assume_defined = _assume_rules.defined_facts.copy() +_assume_defined.add('polar') +_assume_defined = frozenset(_assume_defined) + + +def assumptions(expr, _check=None): + """return the T/F assumptions of ``expr``""" + n = sympify(expr) + if n.is_Symbol: + rv = n.assumptions0 # are any important ones missing? + if _check is not None: + rv = {k: rv[k] for k in set(rv) & set(_check)} + return rv + rv = {} + for k in _assume_defined if _check is None else _check: + v = getattr(n, 'is_{}'.format(k)) + if v is not None: + rv[k] = v + return rv + + +def common_assumptions(exprs, check=None): + """return those assumptions which have the same True or False + value for all the given expressions. + + Examples + ======== + + >>> from sympy.core import common_assumptions + >>> from sympy import oo, pi, sqrt + >>> common_assumptions([-4, 0, sqrt(2), 2, pi, oo]) + {'commutative': True, 'composite': False, + 'extended_real': True, 'imaginary': False, 'odd': False} + + By default, all assumptions are tested; pass an iterable of the + assumptions to limit those that are reported: + + >>> common_assumptions([0, 1, 2], ['positive', 'integer']) + {'integer': True} + """ + check = _assume_defined if check is None else set(check) + if not check or not exprs: + return {} + + # get all assumptions for each + assume = [assumptions(i, _check=check) for i in sympify(exprs)] + # focus on those of interest that are True + for i, e in enumerate(assume): + assume[i] = {k: e[k] for k in set(e) & check} + # what assumptions are in common? + common = set.intersection(*[set(i) for i in assume]) + # which ones hold the same value + a = assume[0] + return {k: a[k] for k in common if all(a[k] == b[k] + for b in assume)} + + +def failing_assumptions(expr, **assumptions): + """ + Return a dictionary containing assumptions with values not + matching those of the passed assumptions. + + Examples + ======== + + >>> from sympy import failing_assumptions, Symbol + + >>> x = Symbol('x', positive=True) + >>> y = Symbol('y') + >>> failing_assumptions(6*x + y, positive=True) + {'positive': None} + + >>> failing_assumptions(x**2 - 1, positive=True) + {'positive': None} + + If *expr* satisfies all of the assumptions, an empty dictionary is returned. + + >>> failing_assumptions(x**2, positive=True) + {} + + """ + expr = sympify(expr) + failed = {} + for k in assumptions: + test = getattr(expr, 'is_%s' % k, None) + if test is not assumptions[k]: + failed[k] = test + return failed # {} or {assumption: value != desired} + + +def check_assumptions(expr, against=None, **assume): + """ + Checks whether assumptions of ``expr`` match the T/F assumptions + given (or possessed by ``against``). True is returned if all + assumptions match; False is returned if there is a mismatch and + the assumption in ``expr`` is not None; else None is returned. + + Explanation + =========== + + *assume* is a dict of assumptions with True or False values + + Examples + ======== + + >>> from sympy import Symbol, pi, I, exp, check_assumptions + >>> check_assumptions(-5, integer=True) + True + >>> check_assumptions(pi, real=True, integer=False) + True + >>> check_assumptions(pi, negative=True) + False + >>> check_assumptions(exp(I*pi/7), real=False) + True + >>> x = Symbol('x', positive=True) + >>> check_assumptions(2*x + 1, positive=True) + True + >>> check_assumptions(-2*x - 5, positive=True) + False + + To check assumptions of *expr* against another variable or expression, + pass the expression or variable as ``against``. + + >>> check_assumptions(2*x + 1, x) + True + + To see if a number matches the assumptions of an expression, pass + the number as the first argument, else its specific assumptions + may not have a non-None value in the expression: + + >>> check_assumptions(x, 3) + >>> check_assumptions(3, x) + True + + ``None`` is returned if ``check_assumptions()`` could not conclude. + + >>> check_assumptions(2*x - 1, x) + + >>> z = Symbol('z') + >>> check_assumptions(z, real=True) + + See Also + ======== + + failing_assumptions + + """ + expr = sympify(expr) + if against is not None: + if assume: + raise ValueError( + 'Expecting `against` or `assume`, not both.') + assume = assumptions(against) + known = True + for k, v in assume.items(): + if v is None: + continue + e = getattr(expr, 'is_' + k, None) + if e is None: + known = None + elif v != e: + return False + return known + + +class StdFactKB(FactKB): + """A FactKB specialized for the built-in rules + + This is the only kind of FactKB that Basic objects should use. + """ + def __init__(self, facts=None): + super().__init__(_assume_rules) + # save a copy of the facts dict + if not facts: + self._generator = {} + elif not isinstance(facts, FactKB): + self._generator = facts.copy() + else: + self._generator = facts.generator + if facts: + self.deduce_all_facts(facts) + + def copy(self): + return self.__class__(self) + + @property + def generator(self): + return self._generator.copy() + + +def as_property(fact): + """Convert a fact name to the name of the corresponding property""" + return 'is_%s' % fact + + +def make_property(fact): + """Create the automagic property corresponding to a fact.""" + + def getit(self): + try: + return self._assumptions[fact] + except KeyError: + if self._assumptions is self.default_assumptions: + self._assumptions = self.default_assumptions.copy() + return _ask(fact, self) + + getit.func_name = as_property(fact) + return property(getit) + + +def _ask(fact, obj): + """ + Find the truth value for a property of an object. + + This function is called when a request is made to see what a fact + value is. + + For this we use several techniques: + + First, the fact-evaluation function is tried, if it exists (for + example _eval_is_integer). Then we try related facts. For example + + rational --> integer + + another example is joined rule: + + integer & !odd --> even + + so in the latter case if we are looking at what 'even' value is, + 'integer' and 'odd' facts will be asked. + + In all cases, when we settle on some fact value, its implications are + deduced, and the result is cached in ._assumptions. + """ + # FactKB which is dict-like and maps facts to their known values: + assumptions = obj._assumptions + + # A dict that maps facts to their handlers: + handler_map = obj._prop_handler + + # This is our queue of facts to check: + facts_to_check = [fact] + facts_queued = {fact} + + # Loop over the queue as it extends + for fact_i in facts_to_check: + + # If fact_i has already been determined then we don't need to rerun the + # handler. There is a potential race condition for multithreaded code + # though because it's possible that fact_i was checked in another + # thread. The main logic of the loop below would potentially skip + # checking assumptions[fact] in this case so we check it once after the + # loop to be sure. + if fact_i in assumptions: + continue + + # Now we call the associated handler for fact_i if it exists. + fact_i_value = None + handler_i = handler_map.get(fact_i) + if handler_i is not None: + fact_i_value = handler_i(obj) + + # If we get a new value for fact_i then we should update our knowledge + # of fact_i as well as any related facts that can be inferred using the + # inference rules connecting the fact_i and any other fact values that + # are already known. + if fact_i_value is not None: + assumptions.deduce_all_facts(((fact_i, fact_i_value),)) + + # Usually if assumptions[fact] is now not None then that is because of + # the call to deduce_all_facts above. The handler for fact_i returned + # True or False and knowing fact_i (which is equal to fact in the first + # iteration) implies knowing a value for fact. It is also possible + # though that independent code e.g. called indirectly by the handler or + # called in another thread in a multithreaded context might have + # resulted in assumptions[fact] being set. Either way we return it. + fact_value = assumptions.get(fact) + if fact_value is not None: + return fact_value + + # Extend the queue with other facts that might determine fact_i. Here + # we randomise the order of the facts that are checked. This should not + # lead to any non-determinism if all handlers are logically consistent + # with the inference rules for the facts. Non-deterministic assumptions + # queries can result from bugs in the handlers that are exposed by this + # call to shuffle. These are pushed to the back of the queue meaning + # that the inference graph is traversed in breadth-first order. + new_facts_to_check = list(_assume_rules.prereq[fact_i] - facts_queued) + shuffle(new_facts_to_check) + facts_to_check.extend(new_facts_to_check) + facts_queued.update(new_facts_to_check) + + # The above loop should be able to handle everything fine in a + # single-threaded context but in multithreaded code it is possible that + # this thread skipped computing a particular fact that was computed in + # another thread (due to the continue). In that case it is possible that + # fact was inferred and is now stored in the assumptions dict but it wasn't + # checked for in the body of the loop. This is an obscure case but to make + # sure we catch it we check once here at the end of the loop. + if fact in assumptions: + return assumptions[fact] + + # This query can not be answered. It's possible that e.g. another thread + # has already stored None for fact but assumptions._tell does not mind if + # we call _tell twice setting the same value. If this raises + # InconsistentAssumptions then it probably means that another thread + # attempted to compute this and got a value of True or False rather than + # None. In that case there must be a bug in at least one of the handlers. + # If the handlers are all deterministic and are consistent with the + # inference rules then the same value should be computed for fact in all + # threads. + assumptions._tell(fact, None) + return None + + +def _prepare_class_assumptions(cls): + """Precompute class level assumptions and generate handlers. + + This is called by Basic.__init_subclass__ each time a Basic subclass is + defined. + """ + + local_defs = {} + for k in _assume_defined: + attrname = as_property(k) + v = cls.__dict__.get(attrname, '') + if isinstance(v, (bool, int, type(None))): + if v is not None: + v = bool(v) + local_defs[k] = v + + defs = {} + for base in reversed(cls.__bases__): + assumptions = getattr(base, '_explicit_class_assumptions', None) + if assumptions is not None: + defs.update(assumptions) + defs.update(local_defs) + + cls._explicit_class_assumptions = defs + cls.default_assumptions = StdFactKB(defs) + + cls._prop_handler = {} + for k in _assume_defined: + eval_is_meth = getattr(cls, '_eval_is_%s' % k, None) + if eval_is_meth is not None: + cls._prop_handler[k] = eval_is_meth + + # Put definite results directly into the class dict, for speed + for k, v in cls.default_assumptions.items(): + setattr(cls, as_property(k), v) + + # protection e.g. for Integer.is_even=F <- (Rational.is_integer=F) + derived_from_bases = set() + for base in cls.__bases__: + default_assumptions = getattr(base, 'default_assumptions', None) + # is an assumption-aware class + if default_assumptions is not None: + derived_from_bases.update(default_assumptions) + + for fact in derived_from_bases - set(cls.default_assumptions): + pname = as_property(fact) + if pname not in cls.__dict__: + setattr(cls, pname, make_property(fact)) + + # Finally, add any missing automagic property (e.g. for Basic) + for fact in _assume_defined: + pname = as_property(fact) + if not hasattr(cls, pname): + setattr(cls, pname, make_property(fact)) + + +# XXX: ManagedProperties used to be the metaclass for Basic but now Basic does +# not use a metaclass. We leave this here for backwards compatibility for now +# in case someone has been using the ManagedProperties class in downstream +# code. The reason that it might have been used is that when subclassing a +# class and wanting to use a metaclass the metaclass must be a subclass of the +# metaclass for the class that is being subclassed. Anyone wanting to subclass +# Basic and use a metaclass in their subclass would have needed to subclass +# ManagedProperties. Here ManagedProperties is not the metaclass for Basic any +# more but it should still be usable as a metaclass for Basic subclasses since +# it is a subclass of type which is now the metaclass for Basic. +class ManagedProperties(type): + def __init__(cls, *args, **kwargs): + msg = ("The ManagedProperties metaclass. " + "Basic does not use metaclasses any more") + sympy_deprecation_warning(msg, + deprecated_since_version="1.12", + active_deprecations_target='managedproperties') + + # Here we still call this function in case someone is using + # ManagedProperties for something that is not a Basic subclass. For + # Basic subclasses this function is now called by __init_subclass__ and + # so this metaclass is not needed any more. + _prepare_class_assumptions(cls) diff --git a/lib/python3.12/site-packages/sympy/core/assumptions_generated.py b/lib/python3.12/site-packages/sympy/core/assumptions_generated.py new file mode 100644 index 0000000000000000000000000000000000000000..b4b2597a72b500155370db385b58e61f0f951984 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/core/assumptions_generated.py @@ -0,0 +1,1615 @@ +""" +Do NOT manually edit this file. +Instead, run ./bin/ask_update.py. +""" + +defined_facts = [ + 'algebraic', + 'antihermitian', + 'commutative', + 'complex', + 'composite', + 'even', + 'extended_negative', + 'extended_nonnegative', + 'extended_nonpositive', + 'extended_nonzero', + 'extended_positive', + 'extended_real', + 'finite', + 'hermitian', + 'imaginary', + 'infinite', + 'integer', + 'irrational', + 'negative', + 'noninteger', + 'nonnegative', + 'nonpositive', + 'nonzero', + 'odd', + 'positive', + 'prime', + 'rational', + 'real', + 'transcendental', + 'zero', +] # defined_facts + + +full_implications = dict( [ + # Implications of algebraic = True: + (('algebraic', True), set( ( + ('commutative', True), + ('complex', True), + ('finite', True), + ('infinite', False), + ('transcendental', False), + ) ), + ), + # Implications of algebraic = False: + (('algebraic', False), set( ( + ('composite', False), + ('even', False), + ('integer', False), + ('odd', False), + ('prime', False), + ('rational', False), + ('zero', False), + ) ), + ), + # Implications of antihermitian = True: + (('antihermitian', True), set( ( + ) ), + ), + # Implications of antihermitian = False: + (('antihermitian', False), set( ( + ('imaginary', False), + ) ), + ), + # Implications of commutative = True: + (('commutative', True), set( ( + ) ), + ), + # Implications of commutative = False: + (('commutative', False), set( ( + ('algebraic', False), + ('complex', False), + ('composite', False), + ('even', False), + ('extended_negative', False), + ('extended_nonnegative', False), + ('extended_nonpositive', False), + ('extended_nonzero', False), + ('extended_positive', False), + ('extended_real', False), + ('imaginary', False), + ('integer', False), + ('irrational', False), + ('negative', False), + ('noninteger', False), + ('nonnegative', False), + ('nonpositive', False), + ('nonzero', False), + ('odd', False), + ('positive', False), + ('prime', False), + ('rational', False), + ('real', False), + ('transcendental', False), + ('zero', False), + ) ), + ), + # Implications of complex = True: + (('complex', True), set( ( + ('commutative', True), + ('finite', True), + ('infinite', False), + ) ), + ), + # Implications of complex = False: + (('complex', False), set( ( + ('algebraic', False), + ('composite', False), + ('even', False), + ('imaginary', False), + ('integer', False), + ('irrational', False), + ('negative', False), + ('nonnegative', False), + ('nonpositive', False), + ('nonzero', False), + ('odd', False), + ('positive', False), + ('prime', False), + ('rational', False), + ('real', False), + ('transcendental', False), + ('zero', False), + ) ), + ), + # Implications of composite = True: + (('composite', True), set( ( + ('algebraic', True), + ('commutative', True), + ('complex', True), + ('extended_negative', False), + ('extended_nonnegative', True), + ('extended_nonpositive', False), + ('extended_nonzero', True), + ('extended_positive', True), + ('extended_real', True), + ('finite', True), + ('hermitian', True), + ('imaginary', False), + ('infinite', False), + ('integer', True), + ('irrational', False), + ('negative', False), + ('noninteger', False), + ('nonnegative', True), + ('nonpositive', False), + ('nonzero', True), + ('positive', True), + ('prime', False), + ('rational', True), + ('real', True), + ('transcendental', False), + ('zero', False), + ) ), + ), + # Implications of composite = False: + (('composite', False), set( ( + ) ), + ), + # Implications of even = True: + (('even', True), set( ( + ('algebraic', True), + ('commutative', True), + ('complex', True), + ('extended_real', True), + ('finite', True), + ('hermitian', True), + ('imaginary', False), + ('infinite', False), + ('integer', True), + ('irrational', False), + ('noninteger', False), + ('odd', False), + ('rational', True), + ('real', True), + ('transcendental', False), + ) ), + ), + # Implications of even = False: + (('even', False), set( ( + ('zero', False), + ) ), + ), + # Implications of extended_negative = True: + (('extended_negative', True), set( ( + ('commutative', True), + ('composite', False), + ('extended_nonnegative', False), + ('extended_nonpositive', True), + ('extended_nonzero', True), + ('extended_positive', False), + ('extended_real', True), + ('imaginary', False), + ('nonnegative', False), + ('positive', False), + ('prime', False), + ('zero', False), + ) ), + ), + # Implications of extended_negative = False: + (('extended_negative', False), set( ( + ('negative', False), + ) ), + ), + # Implications of extended_nonnegative = True: + (('extended_nonnegative', True), set( ( + ('commutative', True), + ('extended_negative', False), + ('extended_real', True), + ('imaginary', False), + ('negative', False), + ) ), + ), + # Implications of extended_nonnegative = False: + (('extended_nonnegative', False), set( ( + ('composite', False), + ('extended_positive', False), + ('nonnegative', False), + ('positive', False), + ('prime', False), + ('zero', False), + ) ), + ), + # Implications of extended_nonpositive = True: + (('extended_nonpositive', True), set( ( + ('commutative', True), + ('composite', False), + ('extended_positive', False), + ('extended_real', True), + ('imaginary', False), + ('positive', False), + ('prime', False), + ) ), + ), + # Implications of extended_nonpositive = False: + (('extended_nonpositive', False), set( ( + ('extended_negative', False), + ('negative', False), + ('nonpositive', False), + ('zero', False), + ) ), + ), + # Implications of extended_nonzero = True: + (('extended_nonzero', True), set( ( + ('commutative', True), + ('extended_real', True), + ('imaginary', False), + ('zero', False), + ) ), + ), + # Implications of extended_nonzero = False: + (('extended_nonzero', False), set( ( + ('composite', False), + ('extended_negative', False), + ('extended_positive', False), + ('negative', False), + ('nonzero', False), + ('positive', False), + ('prime', False), + ) ), + ), + # Implications of extended_positive = True: + (('extended_positive', True), set( ( + ('commutative', True), + ('extended_negative', False), + ('extended_nonnegative', True), + ('extended_nonpositive', False), + ('extended_nonzero', True), + ('extended_real', True), + ('imaginary', False), + ('negative', False), + ('nonpositive', False), + ('zero', False), + ) ), + ), + # Implications of extended_positive = False: + (('extended_positive', False), set( ( + ('composite', False), + ('positive', False), + ('prime', False), + ) ), + ), + # Implications of extended_real = True: + (('extended_real', True), set( ( + ('commutative', True), + ('imaginary', False), + ) ), + ), + # Implications of extended_real = False: + (('extended_real', False), set( ( + ('composite', False), + ('even', False), + ('extended_negative', False), + ('extended_nonnegative', False), + ('extended_nonpositive', False), + ('extended_nonzero', False), + ('extended_positive', False), + ('integer', False), + ('irrational', False), + ('negative', False), + ('noninteger', False), + ('nonnegative', False), + ('nonpositive', False), + ('nonzero', False), + ('odd', False), + ('positive', False), + ('prime', False), + ('rational', False), + ('real', False), + ('zero', False), + ) ), + ), + # Implications of finite = True: + (('finite', True), set( ( + ('infinite', False), + ) ), + ), + # Implications of finite = False: + (('finite', False), set( ( + ('algebraic', False), + ('complex', False), + ('composite', False), + ('even', False), + ('imaginary', False), + ('infinite', True), + ('integer', False), + ('irrational', False), + ('negative', False), + ('nonnegative', False), + ('nonpositive', False), + ('nonzero', False), + ('odd', False), + ('positive', False), + ('prime', False), + ('rational', False), + ('real', False), + ('transcendental', False), + ('zero', False), + ) ), + ), + # Implications of hermitian = True: + (('hermitian', True), set( ( + ) ), + ), + # Implications of hermitian = False: + (('hermitian', False), set( ( + ('composite', False), + ('even', False), + ('integer', False), + ('irrational', False), + ('negative', False), + ('nonnegative', False), + ('nonpositive', False), + ('nonzero', False), + ('odd', False), + ('positive', False), + ('prime', False), + ('rational', False), + ('real', False), + ('zero', False), + ) ), + ), + # Implications of imaginary = True: + (('imaginary', True), set( ( + ('antihermitian', True), + ('commutative', True), + ('complex', True), + ('composite', False), + ('even', False), + ('extended_negative', False), + ('extended_nonnegative', False), + ('extended_nonpositive', False), + ('extended_nonzero', False), + ('extended_positive', False), + ('extended_real', False), + ('finite', True), + ('infinite', False), + ('integer', False), + ('irrational', False), + ('negative', False), + ('noninteger', False), + ('nonnegative', False), + ('nonpositive', False), + ('nonzero', False), + ('odd', False), + ('positive', False), + ('prime', False), + ('rational', False), + ('real', False), + ('zero', False), + ) ), + ), + # Implications of imaginary = False: + (('imaginary', False), set( ( + ) ), + ), + # Implications of infinite = True: + (('infinite', True), set( ( + ('algebraic', False), + ('complex', False), + ('composite', False), + ('even', False), + ('finite', False), + ('imaginary', False), + ('integer', False), + ('irrational', False), + ('negative', False), + ('nonnegative', False), + ('nonpositive', False), + ('nonzero', False), + ('odd', False), + ('positive', False), + ('prime', False), + ('rational', False), + ('real', False), + ('transcendental', False), + ('zero', False), + ) ), + ), + # Implications of infinite = False: + (('infinite', False), set( ( + ('finite', True), + ) ), + ), + # Implications of integer = True: + (('integer', True), set( ( + ('algebraic', True), + ('commutative', True), + ('complex', True), + ('extended_real', True), + ('finite', True), + ('hermitian', True), + ('imaginary', False), + ('infinite', False), + ('irrational', False), + ('noninteger', False), + ('rational', True), + ('real', True), + ('transcendental', False), + ) ), + ), + # Implications of integer = False: + (('integer', False), set( ( + ('composite', False), + ('even', False), + ('odd', False), + ('prime', False), + ('zero', False), + ) ), + ), + # Implications of irrational = True: + (('irrational', True), set( ( + ('commutative', True), + ('complex', True), + ('composite', False), + ('even', False), + ('extended_nonzero', True), + ('extended_real', True), + ('finite', True), + ('hermitian', True), + ('imaginary', False), + ('infinite', False), + ('integer', False), + ('noninteger', True), + ('nonzero', True), + ('odd', False), + ('prime', False), + ('rational', False), + ('real', True), + ('zero', False), + ) ), + ), + # Implications of irrational = False: + (('irrational', False), set( ( + ) ), + ), + # Implications of negative = True: + (('negative', True), set( ( + ('commutative', True), + ('complex', True), + ('composite', False), + ('extended_negative', True), + ('extended_nonnegative', False), + ('extended_nonpositive', True), + ('extended_nonzero', True), + ('extended_positive', False), + ('extended_real', True), + ('finite', True), + ('hermitian', True), + ('imaginary', False), + ('infinite', False), + ('nonnegative', False), + ('nonpositive', True), + ('nonzero', True), + ('positive', False), + ('prime', False), + ('real', True), + ('zero', False), + ) ), + ), + # Implications of negative = False: + (('negative', False), set( ( + ) ), + ), + # Implications of noninteger = True: + (('noninteger', True), set( ( + ('commutative', True), + ('composite', False), + ('even', False), + ('extended_nonzero', True), + ('extended_real', True), + ('imaginary', False), + ('integer', False), + ('odd', False), + ('prime', False), + ('zero', False), + ) ), + ), + # Implications of noninteger = False: + (('noninteger', False), set( ( + ) ), + ), + # Implications of nonnegative = True: + (('nonnegative', True), set( ( + ('commutative', True), + ('complex', True), + ('extended_negative', False), + ('extended_nonnegative', True), + ('extended_real', True), + ('finite', True), + ('hermitian', True), + ('imaginary', False), + ('infinite', False), + ('negative', False), + ('real', True), + ) ), + ), + # Implications of nonnegative = False: + (('nonnegative', False), set( ( + ('composite', False), + ('positive', False), + ('prime', False), + ('zero', False), + ) ), + ), + # Implications of nonpositive = True: + (('nonpositive', True), set( ( + ('commutative', True), + ('complex', True), + ('composite', False), + ('extended_nonpositive', True), + ('extended_positive', False), + ('extended_real', True), + ('finite', True), + ('hermitian', True), + ('imaginary', False), + ('infinite', False), + ('positive', False), + ('prime', False), + ('real', True), + ) ), + ), + # Implications of nonpositive = False: + (('nonpositive', False), set( ( + ('negative', False), + ('zero', False), + ) ), + ), + # Implications of nonzero = True: + (('nonzero', True), set( ( + ('commutative', True), + ('complex', True), + ('extended_nonzero', True), + ('extended_real', True), + ('finite', True), + ('hermitian', True), + ('imaginary', False), + ('infinite', False), + ('real', True), + ('zero', False), + ) ), + ), + # Implications of nonzero = False: + (('nonzero', False), set( ( + ('composite', False), + ('negative', False), + ('positive', False), + ('prime', False), + ) ), + ), + # Implications of odd = True: + (('odd', True), set( ( + ('algebraic', True), + ('commutative', True), + ('complex', True), + ('even', False), + ('extended_nonzero', True), + ('extended_real', True), + ('finite', True), + ('hermitian', True), + ('imaginary', False), + ('infinite', False), + ('integer', True), + ('irrational', False), + ('noninteger', False), + ('nonzero', True), + ('rational', True), + ('real', True), + ('transcendental', False), + ('zero', False), + ) ), + ), + # Implications of odd = False: + (('odd', False), set( ( + ) ), + ), + # Implications of positive = True: + (('positive', True), set( ( + ('commutative', True), + ('complex', True), + ('extended_negative', False), + ('extended_nonnegative', True), + ('extended_nonpositive', False), + ('extended_nonzero', True), + ('extended_positive', True), + ('extended_real', True), + ('finite', True), + ('hermitian', True), + ('imaginary', False), + ('infinite', False), + ('negative', False), + ('nonnegative', True), + ('nonpositive', False), + ('nonzero', True), + ('real', True), + ('zero', False), + ) ), + ), + # Implications of positive = False: + (('positive', False), set( ( + ('composite', False), + ('prime', False), + ) ), + ), + # Implications of prime = True: + (('prime', True), set( ( + ('algebraic', True), + ('commutative', True), + ('complex', True), + ('composite', False), + ('extended_negative', False), + ('extended_nonnegative', True), + ('extended_nonpositive', False), + ('extended_nonzero', True), + ('extended_positive', True), + ('extended_real', True), + ('finite', True), + ('hermitian', True), + ('imaginary', False), + ('infinite', False), + ('integer', True), + ('irrational', False), + ('negative', False), + ('noninteger', False), + ('nonnegative', True), + ('nonpositive', False), + ('nonzero', True), + ('positive', True), + ('rational', True), + ('real', True), + ('transcendental', False), + ('zero', False), + ) ), + ), + # Implications of prime = False: + (('prime', False), set( ( + ) ), + ), + # Implications of rational = True: + (('rational', True), set( ( + ('algebraic', True), + ('commutative', True), + ('complex', True), + ('extended_real', True), + ('finite', True), + ('hermitian', True), + ('imaginary', False), + ('infinite', False), + ('irrational', False), + ('real', True), + ('transcendental', False), + ) ), + ), + # Implications of rational = False: + (('rational', False), set( ( + ('composite', False), + ('even', False), + ('integer', False), + ('odd', False), + ('prime', False), + ('zero', False), + ) ), + ), + # Implications of real = True: + (('real', True), set( ( + ('commutative', True), + ('complex', True), + ('extended_real', True), + ('finite', True), + ('hermitian', True), + ('imaginary', False), + ('infinite', False), + ) ), + ), + # Implications of real = False: + (('real', False), set( ( + ('composite', False), + ('even', False), + ('integer', False), + ('irrational', False), + ('negative', False), + ('nonnegative', False), + ('nonpositive', False), + ('nonzero', False), + ('odd', False), + ('positive', False), + ('prime', False), + ('rational', False), + ('zero', False), + ) ), + ), + # Implications of transcendental = True: + (('transcendental', True), set( ( + ('algebraic', False), + ('commutative', True), + ('complex', True), + ('composite', False), + ('even', False), + ('finite', True), + ('infinite', False), + ('integer', False), + ('odd', False), + ('prime', False), + ('rational', False), + ('zero', False), + ) ), + ), + # Implications of transcendental = False: + (('transcendental', False), set( ( + ) ), + ), + # Implications of zero = True: + (('zero', True), set( ( + ('algebraic', True), + ('commutative', True), + ('complex', True), + ('composite', False), + ('even', True), + ('extended_negative', False), + ('extended_nonnegative', True), + ('extended_nonpositive', True), + ('extended_nonzero', False), + ('extended_positive', False), + ('extended_real', True), + ('finite', True), + ('hermitian', True), + ('imaginary', False), + ('infinite', False), + ('integer', True), + ('irrational', False), + ('negative', False), + ('noninteger', False), + ('nonnegative', True), + ('nonpositive', True), + ('nonzero', False), + ('odd', False), + ('positive', False), + ('prime', False), + ('rational', True), + ('real', True), + ('transcendental', False), + ) ), + ), + # Implications of zero = False: + (('zero', False), set( ( + ) ), + ), + ] ) # full_implications + + +prereq = { + + # facts that could determine the value of algebraic + 'algebraic': { + 'commutative', + 'complex', + 'composite', + 'even', + 'finite', + 'infinite', + 'integer', + 'odd', + 'prime', + 'rational', + 'transcendental', + 'zero', + }, + + # facts that could determine the value of antihermitian + 'antihermitian': { + 'imaginary', + }, + + # facts that could determine the value of commutative + 'commutative': { + 'algebraic', + 'complex', + 'composite', + 'even', + 'extended_negative', + 'extended_nonnegative', + 'extended_nonpositive', + 'extended_nonzero', + 'extended_positive', + 'extended_real', + 'imaginary', + 'integer', + 'irrational', + 'negative', + 'noninteger', + 'nonnegative', + 'nonpositive', + 'nonzero', + 'odd', + 'positive', + 'prime', + 'rational', + 'real', + 'transcendental', + 'zero', + }, + + # facts that could determine the value of complex + 'complex': { + 'algebraic', + 'commutative', + 'composite', + 'even', + 'finite', + 'imaginary', + 'infinite', + 'integer', + 'irrational', + 'negative', + 'nonnegative', + 'nonpositive', + 'nonzero', + 'odd', + 'positive', + 'prime', + 'rational', + 'real', + 'transcendental', + 'zero', + }, + + # facts that could determine the value of composite + 'composite': { + 'algebraic', + 'commutative', + 'complex', + 'extended_negative', + 'extended_nonnegative', + 'extended_nonpositive', + 'extended_nonzero', + 'extended_positive', + 'extended_real', + 'finite', + 'hermitian', + 'imaginary', + 'infinite', + 'integer', + 'irrational', + 'negative', + 'noninteger', + 'nonnegative', + 'nonpositive', + 'nonzero', + 'positive', + 'prime', + 'rational', + 'real', + 'transcendental', + 'zero', + }, + + # facts that could determine the value of even + 'even': { + 'algebraic', + 'commutative', + 'complex', + 'extended_real', + 'finite', + 'hermitian', + 'imaginary', + 'infinite', + 'integer', + 'irrational', + 'noninteger', + 'odd', + 'rational', + 'real', + 'transcendental', + 'zero', + }, + + # facts that could determine the value of extended_negative + 'extended_negative': { + 'commutative', + 'composite', + 'extended_nonnegative', + 'extended_nonpositive', + 'extended_nonzero', + 'extended_positive', + 'extended_real', + 'imaginary', + 'negative', + 'nonnegative', + 'positive', + 'prime', + 'zero', + }, + + # facts that could determine the value of extended_nonnegative + 'extended_nonnegative': { + 'commutative', + 'composite', + 'extended_negative', + 'extended_positive', + 'extended_real', + 'imaginary', + 'negative', + 'nonnegative', + 'positive', + 'prime', + 'zero', + }, + + # facts that could determine the value of extended_nonpositive + 'extended_nonpositive': { + 'commutative', + 'composite', + 'extended_negative', + 'extended_positive', + 'extended_real', + 'imaginary', + 'negative', + 'nonpositive', + 'positive', + 'prime', + 'zero', + }, + + # facts that could determine the value of extended_nonzero + 'extended_nonzero': { + 'commutative', + 'composite', + 'extended_negative', + 'extended_positive', + 'extended_real', + 'imaginary', + 'irrational', + 'negative', + 'noninteger', + 'nonzero', + 'odd', + 'positive', + 'prime', + 'zero', + }, + + # facts that could determine the value of extended_positive + 'extended_positive': { + 'commutative', + 'composite', + 'extended_negative', + 'extended_nonnegative', + 'extended_nonpositive', + 'extended_nonzero', + 'extended_real', + 'imaginary', + 'negative', + 'nonpositive', + 'positive', + 'prime', + 'zero', + }, + + # facts that could determine the value of extended_real + 'extended_real': { + 'commutative', + 'composite', + 'even', + 'extended_negative', + 'extended_nonnegative', + 'extended_nonpositive', + 'extended_nonzero', + 'extended_positive', + 'imaginary', + 'integer', + 'irrational', + 'negative', + 'noninteger', + 'nonnegative', + 'nonpositive', + 'nonzero', + 'odd', + 'positive', + 'prime', + 'rational', + 'real', + 'zero', + }, + + # facts that could determine the value of finite + 'finite': { + 'algebraic', + 'complex', + 'composite', + 'even', + 'imaginary', + 'infinite', + 'integer', + 'irrational', + 'negative', + 'nonnegative', + 'nonpositive', + 'nonzero', + 'odd', + 'positive', + 'prime', + 'rational', + 'real', + 'transcendental', + 'zero', + }, + + # facts that could determine the value of hermitian + 'hermitian': { + 'composite', + 'even', + 'integer', + 'irrational', + 'negative', + 'nonnegative', + 'nonpositive', + 'nonzero', + 'odd', + 'positive', + 'prime', + 'rational', + 'real', + 'zero', + }, + + # facts that could determine the value of imaginary + 'imaginary': { + 'antihermitian', + 'commutative', + 'complex', + 'composite', + 'even', + 'extended_negative', + 'extended_nonnegative', + 'extended_nonpositive', + 'extended_nonzero', + 'extended_positive', + 'extended_real', + 'finite', + 'infinite', + 'integer', + 'irrational', + 'negative', + 'noninteger', + 'nonnegative', + 'nonpositive', + 'nonzero', + 'odd', + 'positive', + 'prime', + 'rational', + 'real', + 'zero', + }, + + # facts that could determine the value of infinite + 'infinite': { + 'algebraic', + 'complex', + 'composite', + 'even', + 'finite', + 'imaginary', + 'integer', + 'irrational', + 'negative', + 'nonnegative', + 'nonpositive', + 'nonzero', + 'odd', + 'positive', + 'prime', + 'rational', + 'real', + 'transcendental', + 'zero', + }, + + # facts that could determine the value of integer + 'integer': { + 'algebraic', + 'commutative', + 'complex', + 'composite', + 'even', + 'extended_real', + 'finite', + 'hermitian', + 'imaginary', + 'infinite', + 'irrational', + 'noninteger', + 'odd', + 'prime', + 'rational', + 'real', + 'transcendental', + 'zero', + }, + + # facts that could determine the value of irrational + 'irrational': { + 'commutative', + 'complex', + 'composite', + 'even', + 'extended_real', + 'finite', + 'hermitian', + 'imaginary', + 'infinite', + 'integer', + 'odd', + 'prime', + 'rational', + 'real', + 'zero', + }, + + # facts that could determine the value of negative + 'negative': { + 'commutative', + 'complex', + 'composite', + 'extended_negative', + 'extended_nonnegative', + 'extended_nonpositive', + 'extended_nonzero', + 'extended_positive', + 'extended_real', + 'finite', + 'hermitian', + 'imaginary', + 'infinite', + 'nonnegative', + 'nonpositive', + 'nonzero', + 'positive', + 'prime', + 'real', + 'zero', + }, + + # facts that could determine the value of noninteger + 'noninteger': { + 'commutative', + 'composite', + 'even', + 'extended_real', + 'imaginary', + 'integer', + 'irrational', + 'odd', + 'prime', + 'zero', + }, + + # facts that could determine the value of nonnegative + 'nonnegative': { + 'commutative', + 'complex', + 'composite', + 'extended_negative', + 'extended_nonnegative', + 'extended_real', + 'finite', + 'hermitian', + 'imaginary', + 'infinite', + 'negative', + 'positive', + 'prime', + 'real', + 'zero', + }, + + # facts that could determine the value of nonpositive + 'nonpositive': { + 'commutative', + 'complex', + 'composite', + 'extended_nonpositive', + 'extended_positive', + 'extended_real', + 'finite', + 'hermitian', + 'imaginary', + 'infinite', + 'negative', + 'positive', + 'prime', + 'real', + 'zero', + }, + + # facts that could determine the value of nonzero + 'nonzero': { + 'commutative', + 'complex', + 'composite', + 'extended_nonzero', + 'extended_real', + 'finite', + 'hermitian', + 'imaginary', + 'infinite', + 'irrational', + 'negative', + 'odd', + 'positive', + 'prime', + 'real', + 'zero', + }, + + # facts that could determine the value of odd + 'odd': { + 'algebraic', + 'commutative', + 'complex', + 'even', + 'extended_real', + 'finite', + 'hermitian', + 'imaginary', + 'infinite', + 'integer', + 'irrational', + 'noninteger', + 'rational', + 'real', + 'transcendental', + 'zero', + }, + + # facts that could determine the value of positive + 'positive': { + 'commutative', + 'complex', + 'composite', + 'extended_negative', + 'extended_nonnegative', + 'extended_nonpositive', + 'extended_nonzero', + 'extended_positive', + 'extended_real', + 'finite', + 'hermitian', + 'imaginary', + 'infinite', + 'negative', + 'nonnegative', + 'nonpositive', + 'nonzero', + 'prime', + 'real', + 'zero', + }, + + # facts that could determine the value of prime + 'prime': { + 'algebraic', + 'commutative', + 'complex', + 'composite', + 'extended_negative', + 'extended_nonnegative', + 'extended_nonpositive', + 'extended_nonzero', + 'extended_positive', + 'extended_real', + 'finite', + 'hermitian', + 'imaginary', + 'infinite', + 'integer', + 'irrational', + 'negative', + 'noninteger', + 'nonnegative', + 'nonpositive', + 'nonzero', + 'positive', + 'rational', + 'real', + 'transcendental', + 'zero', + }, + + # facts that could determine the value of rational + 'rational': { + 'algebraic', + 'commutative', + 'complex', + 'composite', + 'even', + 'extended_real', + 'finite', + 'hermitian', + 'imaginary', + 'infinite', + 'integer', + 'irrational', + 'odd', + 'prime', + 'real', + 'transcendental', + 'zero', + }, + + # facts that could determine the value of real + 'real': { + 'commutative', + 'complex', + 'composite', + 'even', + 'extended_real', + 'finite', + 'hermitian', + 'imaginary', + 'infinite', + 'integer', + 'irrational', + 'negative', + 'nonnegative', + 'nonpositive', + 'nonzero', + 'odd', + 'positive', + 'prime', + 'rational', + 'zero', + }, + + # facts that could determine the value of transcendental + 'transcendental': { + 'algebraic', + 'commutative', + 'complex', + 'composite', + 'even', + 'finite', + 'infinite', + 'integer', + 'odd', + 'prime', + 'rational', + 'zero', + }, + + # facts that could determine the value of zero + 'zero': { + 'algebraic', + 'commutative', + 'complex', + 'composite', + 'even', + 'extended_negative', + 'extended_nonnegative', + 'extended_nonpositive', + 'extended_nonzero', + 'extended_positive', + 'extended_real', + 'finite', + 'hermitian', + 'imaginary', + 'infinite', + 'integer', + 'irrational', + 'negative', + 'noninteger', + 'nonnegative', + 'nonpositive', + 'nonzero', + 'odd', + 'positive', + 'prime', + 'rational', + 'real', + 'transcendental', + }, + +} # prereq + + +# Note: the order of the beta rules is used in the beta_triggers +beta_rules = [ + + # Rules implying composite = True + ({('even', True), ('positive', True), ('prime', False)}, + ('composite', True)), + + # Rules implying even = False + ({('composite', False), ('positive', True), ('prime', False)}, + ('even', False)), + + # Rules implying even = True + ({('integer', True), ('odd', False)}, + ('even', True)), + + # Rules implying extended_negative = True + ({('extended_positive', False), ('extended_real', True), ('zero', False)}, + ('extended_negative', True)), + ({('extended_nonpositive', True), ('extended_nonzero', True)}, + ('extended_negative', True)), + + # Rules implying extended_nonnegative = True + ({('extended_negative', False), ('extended_real', True)}, + ('extended_nonnegative', True)), + + # Rules implying extended_nonpositive = True + ({('extended_positive', False), ('extended_real', True)}, + ('extended_nonpositive', True)), + + # Rules implying extended_nonzero = True + ({('extended_real', True), ('zero', False)}, + ('extended_nonzero', True)), + + # Rules implying extended_positive = True + ({('extended_negative', False), ('extended_real', True), ('zero', False)}, + ('extended_positive', True)), + ({('extended_nonnegative', True), ('extended_nonzero', True)}, + ('extended_positive', True)), + + # Rules implying extended_real = False + ({('infinite', False), ('real', False)}, + ('extended_real', False)), + ({('extended_negative', False), ('extended_positive', False), ('zero', False)}, + ('extended_real', False)), + + # Rules implying infinite = True + ({('extended_real', True), ('real', False)}, + ('infinite', True)), + + # Rules implying irrational = True + ({('rational', False), ('real', True)}, + ('irrational', True)), + + # Rules implying negative = True + ({('positive', False), ('real', True), ('zero', False)}, + ('negative', True)), + ({('nonpositive', True), ('nonzero', True)}, + ('negative', True)), + ({('extended_negative', True), ('finite', True)}, + ('negative', True)), + + # Rules implying noninteger = True + ({('extended_real', True), ('integer', False)}, + ('noninteger', True)), + + # Rules implying nonnegative = True + ({('negative', False), ('real', True)}, + ('nonnegative', True)), + ({('extended_nonnegative', True), ('finite', True)}, + ('nonnegative', True)), + + # Rules implying nonpositive = True + ({('positive', False), ('real', True)}, + ('nonpositive', True)), + ({('extended_nonpositive', True), ('finite', True)}, + ('nonpositive', True)), + + # Rules implying nonzero = True + ({('extended_nonzero', True), ('finite', True)}, + ('nonzero', True)), + + # Rules implying odd = True + ({('even', False), ('integer', True)}, + ('odd', True)), + + # Rules implying positive = False + ({('composite', False), ('even', True), ('prime', False)}, + ('positive', False)), + + # Rules implying positive = True + ({('negative', False), ('real', True), ('zero', False)}, + ('positive', True)), + ({('nonnegative', True), ('nonzero', True)}, + ('positive', True)), + ({('extended_positive', True), ('finite', True)}, + ('positive', True)), + + # Rules implying prime = True + ({('composite', False), ('even', True), ('positive', True)}, + ('prime', True)), + + # Rules implying real = False + ({('negative', False), ('positive', False), ('zero', False)}, + ('real', False)), + + # Rules implying real = True + ({('extended_real', True), ('infinite', False)}, + ('real', True)), + ({('extended_real', True), ('finite', True)}, + ('real', True)), + + # Rules implying transcendental = True + ({('algebraic', False), ('complex', True)}, + ('transcendental', True)), + + # Rules implying zero = True + ({('extended_negative', False), ('extended_positive', False), ('extended_real', True)}, + ('zero', True)), + ({('negative', False), ('positive', False), ('real', True)}, + ('zero', True)), + ({('extended_nonnegative', True), ('extended_nonpositive', True)}, + ('zero', True)), + ({('nonnegative', True), ('nonpositive', True)}, + ('zero', True)), + +] # beta_rules +beta_triggers = { + ('algebraic', False): [32, 11, 3, 8, 29, 14, 25, 13, 17, 7], + ('algebraic', True): [10, 30, 31, 27, 16, 21, 19, 22], + ('antihermitian', False): [], + ('commutative', False): [], + ('complex', False): [10, 12, 11, 3, 8, 17, 7], + ('complex', True): [32, 10, 30, 31, 27, 16, 21, 19, 22], + ('composite', False): [1, 28, 24], + ('composite', True): [23, 2], + ('even', False): [23, 11, 3, 8, 29, 14, 25, 7], + ('even', True): [3, 33, 8, 6, 5, 14, 34, 25, 20, 18, 27, 16, 21, 19, 22, 0, 28, 24, 7], + ('extended_negative', False): [11, 33, 8, 5, 29, 34, 25, 18], + ('extended_negative', True): [30, 12, 31, 29, 14, 20, 16, 21, 22, 17], + ('extended_nonnegative', False): [11, 3, 6, 29, 14, 20, 7], + ('extended_nonnegative', True): [30, 12, 31, 33, 8, 9, 6, 29, 34, 25, 18, 19, 35, 17, 7], + ('extended_nonpositive', False): [11, 8, 5, 29, 25, 18, 7], + ('extended_nonpositive', True): [30, 12, 31, 3, 33, 4, 5, 29, 14, 34, 20, 21, 35, 17, 7], + ('extended_nonzero', False): [11, 33, 6, 5, 29, 34, 20, 18], + ('extended_nonzero', True): [30, 12, 31, 3, 8, 4, 9, 6, 5, 29, 14, 25, 22, 17], + ('extended_positive', False): [11, 3, 33, 6, 29, 14, 34, 20], + ('extended_positive', True): [30, 12, 31, 29, 25, 18, 27, 19, 22, 17], + ('extended_real', False): [], + ('extended_real', True): [30, 12, 31, 3, 33, 8, 6, 5, 17, 7], + ('finite', False): [11, 3, 8, 17, 7], + ('finite', True): [10, 30, 31, 27, 16, 21, 19, 22], + ('hermitian', False): [10, 12, 11, 3, 8, 17, 7], + ('imaginary', True): [32], + ('infinite', False): [10, 30, 31, 27, 16, 21, 19, 22], + ('infinite', True): [11, 3, 8, 17, 7], + ('integer', False): [11, 3, 8, 29, 14, 25, 17, 7], + ('integer', True): [23, 2, 3, 33, 8, 6, 5, 14, 34, 25, 20, 18, 27, 16, 21, 19, 22, 7], + ('irrational', True): [32, 3, 8, 4, 9, 6, 5, 14, 25, 15, 26, 20, 18, 27, 16, 21, 19], + ('negative', False): [29, 34, 25, 18], + ('negative', True): [32, 13, 17], + ('noninteger', True): [30, 12, 31, 3, 8, 4, 9, 6, 5, 29, 14, 25, 22], + ('nonnegative', False): [11, 3, 8, 29, 14, 20, 7], + ('nonnegative', True): [32, 33, 8, 9, 6, 34, 25, 26, 20, 27, 21, 22, 35, 36, 13, 17, 7], + ('nonpositive', False): [11, 3, 8, 29, 25, 18, 7], + ('nonpositive', True): [32, 3, 33, 4, 5, 14, 34, 15, 18, 16, 19, 22, 35, 36, 13, 17, 7], + ('nonzero', False): [29, 34, 20, 18], + ('nonzero', True): [32, 3, 8, 4, 9, 6, 5, 14, 25, 15, 26, 20, 18, 27, 16, 21, 19, 13, 17], + ('odd', False): [2], + ('odd', True): [3, 8, 4, 9, 6, 5, 14, 25, 15, 26, 20, 18, 27, 16, 21, 19], + ('positive', False): [29, 14, 34, 20], + ('positive', True): [32, 0, 1, 28, 13, 17], + ('prime', False): [0, 1, 24], + ('prime', True): [23, 2], + ('rational', False): [11, 3, 8, 29, 14, 25, 13, 17, 7], + ('rational', True): [3, 33, 8, 6, 5, 14, 34, 25, 20, 18, 27, 16, 21, 19, 22, 17, 7], + ('real', False): [10, 12, 11, 3, 8, 17, 7], + ('real', True): [32, 3, 33, 8, 6, 5, 14, 34, 25, 20, 18, 27, 16, 21, 19, 22, 13, 17, 7], + ('transcendental', True): [10, 30, 31, 11, 3, 8, 29, 14, 25, 27, 16, 21, 19, 22, 13, 17, 7], + ('zero', False): [11, 3, 8, 29, 14, 25, 7], + ('zero', True): [], +} # beta_triggers + + +generated_assumptions = {'defined_facts': defined_facts, 'full_implications': full_implications, + 'prereq': prereq, 'beta_rules': beta_rules, 'beta_triggers': beta_triggers} diff --git a/lib/python3.12/site-packages/sympy/core/backend.py b/lib/python3.12/site-packages/sympy/core/backend.py new file mode 100644 index 0000000000000000000000000000000000000000..ea801b74df3822d274d4e7f287c68d51619d5889 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/core/backend.py @@ -0,0 +1,120 @@ +import os +USE_SYMENGINE = os.getenv('USE_SYMENGINE', '0') +USE_SYMENGINE = USE_SYMENGINE.lower() in ('1', 't', 'true') # type: ignore + +if USE_SYMENGINE: + from symengine import (Symbol, Integer, sympify as sympify_symengine, S, + SympifyError, exp, log, gamma, sqrt, I, E, pi, Matrix, + sin, cos, tan, cot, csc, sec, asin, acos, atan, acot, acsc, asec, + sinh, cosh, tanh, coth, asinh, acosh, atanh, acoth, + lambdify, symarray, diff, zeros, eye, diag, ones, + expand, Function, symbols, var, Add, Mul, Derivative, + ImmutableMatrix, MatrixBase, Rational, Basic) + from symengine.lib.symengine_wrapper import gcd as igcd + from symengine import AppliedUndef + + def sympify(a, *, strict=False): + """ + Notes + ===== + + SymEngine's ``sympify`` does not accept keyword arguments and is + therefore not compatible with SymPy's ``sympify`` with ``strict=True`` + (which ensures that only the types for which an explicit conversion has + been defined are converted). This wrapper adds an addiotional parameter + ``strict`` (with default ``False``) that will raise a ``SympifyError`` + if ``strict=True`` and the argument passed to the parameter ``a`` is a + string. + + See Also + ======== + + sympify: Converts an arbitrary expression to a type that can be used + inside SymPy. + + """ + # The parameter ``a`` is used for this function to keep compatibility + # with the SymEngine docstring. + if strict and isinstance(a, str): + raise SympifyError(a) + return sympify_symengine(a) + + # Keep the SymEngine docstring and append the additional "Notes" and "See + # Also" sections. Replacement of spaces is required to correctly format the + # indentation of the combined docstring. + sympify.__doc__ = ( + sympify_symengine.__doc__ + + sympify.__doc__.replace(' ', ' ') # type: ignore + ) +else: + from sympy.core.add import Add + from sympy.core.basic import Basic + from sympy.core.function import (diff, Function, AppliedUndef, + expand, Derivative) + from sympy.core.mul import Mul + from sympy.core.intfunc import igcd + from sympy.core.numbers import pi, I, Integer, Rational, E + from sympy.core.singleton import S + from sympy.core.symbol import Symbol, var, symbols + from sympy.core.sympify import SympifyError, sympify + from sympy.functions.elementary.exponential import log, exp + from sympy.functions.elementary.hyperbolic import (coth, sinh, + acosh, acoth, tanh, asinh, atanh, cosh) + from sympy.functions.elementary.miscellaneous import sqrt + from sympy.functions.elementary.trigonometric import (csc, + asec, cos, atan, sec, acot, asin, tan, sin, cot, acsc, acos) + from sympy.functions.special.gamma_functions import gamma + from sympy.matrices.dense import (eye, zeros, diag, Matrix, + ones, symarray) + from sympy.matrices.immutable import ImmutableMatrix + from sympy.matrices.matrixbase import MatrixBase + from sympy.utilities.lambdify import lambdify + + +# +# XXX: Handling of immutable and mutable matrices in SymEngine is inconsistent +# with SymPy's matrix classes in at least SymEngine version 0.7.0. Until that +# is fixed the function below is needed for consistent behaviour when +# attempting to simplify a matrix. +# +# Expected behaviour of a SymPy mutable/immutable matrix .simplify() method: +# +# Matrix.simplify() : works in place, returns None +# ImmutableMatrix.simplify() : returns a simplified copy +# +# In SymEngine both mutable and immutable matrices simplify in place and return +# None. This is inconsistent with the matrix being "immutable" and also the +# returned None leads to problems in the mechanics module. +# +# The simplify function should not be used because simplify(M) sympifies the +# matrix M and the SymEngine matrices all sympify to SymPy matrices. If we want +# to work with SymEngine matrices then we need to use their .simplify() method +# but that method does not work correctly with immutable matrices. +# +# The _simplify_matrix function can be removed when the SymEngine bug is fixed. +# Since this should be a temporary problem we do not make this function part of +# the public API. +# +# SymEngine issue: https://github.com/symengine/symengine.py/issues/363 +# + +def _simplify_matrix(M): + """Return a simplified copy of the matrix M""" + if not isinstance(M, (Matrix, ImmutableMatrix)): + raise TypeError("The matrix M must be an instance of Matrix or ImmutableMatrix") + Mnew = M.as_mutable() # makes a copy if mutable + Mnew.simplify() + if isinstance(M, ImmutableMatrix): + Mnew = Mnew.as_immutable() + return Mnew + + +__all__ = [ + 'Symbol', 'Integer', 'sympify', 'S', 'SympifyError', 'exp', 'log', + 'gamma', 'sqrt', 'I', 'E', 'pi', 'Matrix', 'sin', 'cos', 'tan', 'cot', + 'csc', 'sec', 'asin', 'acos', 'atan', 'acot', 'acsc', 'asec', 'sinh', + 'cosh', 'tanh', 'coth', 'asinh', 'acosh', 'atanh', 'acoth', 'lambdify', + 'symarray', 'diff', 'zeros', 'eye', 'diag', 'ones', 'expand', 'Function', + 'symbols', 'var', 'Add', 'Mul', 'Derivative', 'ImmutableMatrix', + 'MatrixBase', 'Rational', 'Basic', 'igcd', 'AppliedUndef', +] diff --git a/lib/python3.12/site-packages/sympy/core/basic.py b/lib/python3.12/site-packages/sympy/core/basic.py new file mode 100644 index 0000000000000000000000000000000000000000..caab088cf2663c2ff55f807e085ae95f93a5a5e3 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/core/basic.py @@ -0,0 +1,2339 @@ +"""Base class for all the objects in SymPy""" +from __future__ import annotations + +from collections import defaultdict +from collections.abc import Mapping +from itertools import chain, zip_longest +from functools import cmp_to_key + +from .assumptions import _prepare_class_assumptions +from .cache import cacheit +from .sympify import _sympify, sympify, SympifyError, _external_converter +from .sorting import ordered +from .kind import Kind, UndefinedKind +from ._print_helpers import Printable + +from sympy.utilities.decorator import deprecated +from sympy.utilities.exceptions import sympy_deprecation_warning +from sympy.utilities.iterables import iterable, numbered_symbols +from sympy.utilities.misc import filldedent, func_name + +from inspect import getmro + + +def as_Basic(expr): + """Return expr as a Basic instance using strict sympify + or raise a TypeError; this is just a wrapper to _sympify, + raising a TypeError instead of a SympifyError.""" + try: + return _sympify(expr) + except SympifyError: + raise TypeError( + 'Argument must be a Basic object, not `%s`' % func_name( + expr)) + + +# Key for sorting commutative args in canonical order +# by name. This is used for canonical ordering of the +# args for Add and Mul *if* the names of both classes +# being compared appear here. Some things in this list +# are not spelled the same as their name so they do not, +# in effect, appear here. See Basic.compare. +ordering_of_classes = [ + # singleton numbers + 'Zero', 'One', 'Half', 'Infinity', 'NaN', 'NegativeOne', 'NegativeInfinity', + # numbers + 'Integer', 'Rational', 'Float', + # singleton symbols + 'Exp1', 'Pi', 'ImaginaryUnit', + # symbols + 'Symbol', 'Wild', + # arithmetic operations + 'Pow', 'Mul', 'Add', + # function values + 'Derivative', 'Integral', + # defined singleton functions + 'Abs', 'Sign', 'Sqrt', + 'Floor', 'Ceiling', + 'Re', 'Im', 'Arg', + 'Conjugate', + 'Exp', 'Log', + 'Sin', 'Cos', 'Tan', 'Cot', 'ASin', 'ACos', 'ATan', 'ACot', + 'Sinh', 'Cosh', 'Tanh', 'Coth', 'ASinh', 'ACosh', 'ATanh', 'ACoth', + 'RisingFactorial', 'FallingFactorial', + 'factorial', 'binomial', + 'Gamma', 'LowerGamma', 'UpperGamma', 'PolyGamma', + 'Erf', + # special polynomials + 'Chebyshev', 'Chebyshev2', + # undefined functions + 'Function', 'WildFunction', + # anonymous functions + 'Lambda', + # Landau O symbol + 'Order', + # relational operations + 'Equality', 'Unequality', 'StrictGreaterThan', 'StrictLessThan', + 'GreaterThan', 'LessThan', +] + +def _cmp_name(x: type, y: type) -> int: + """return -1, 0, 1 if the name of x is before that of y. + A string comparison is done if either name does not appear + in `ordering_of_classes`. This is the helper for + ``Basic.compare`` + + Examples + ======== + + >>> from sympy import cos, tan, sin + >>> from sympy.core import basic + >>> save = basic.ordering_of_classes + >>> basic.ordering_of_classes = () + >>> basic._cmp_name(cos, tan) + -1 + >>> basic.ordering_of_classes = ["tan", "sin", "cos"] + >>> basic._cmp_name(cos, tan) + 1 + >>> basic._cmp_name(sin, cos) + -1 + >>> basic.ordering_of_classes = save + + """ + # If the other object is not a Basic subclass, then we are not equal to it. + if not issubclass(y, Basic): + return -1 + + n1 = x.__name__ + n2 = y.__name__ + if n1 == n2: + return 0 + + UNKNOWN = len(ordering_of_classes) + 1 + try: + i1 = ordering_of_classes.index(n1) + except ValueError: + i1 = UNKNOWN + try: + i2 = ordering_of_classes.index(n2) + except ValueError: + i2 = UNKNOWN + if i1 == UNKNOWN and i2 == UNKNOWN: + return (n1 > n2) - (n1 < n2) + return (i1 > i2) - (i1 < i2) + + +class Basic(Printable): + """ + Base class for all SymPy objects. + + Notes and conventions + ===================== + + 1) Always use ``.args``, when accessing parameters of some instance: + + >>> from sympy import cot + >>> from sympy.abc import x, y + + >>> cot(x).args + (x,) + + >>> cot(x).args[0] + x + + >>> (x*y).args + (x, y) + + >>> (x*y).args[1] + y + + + 2) Never use internal methods or variables (the ones prefixed with ``_``): + + >>> cot(x)._args # do not use this, use cot(x).args instead + (x,) + + + 3) By "SymPy object" we mean something that can be returned by + ``sympify``. But not all objects one encounters using SymPy are + subclasses of Basic. For example, mutable objects are not: + + >>> from sympy import Basic, Matrix, sympify + >>> A = Matrix([[1, 2], [3, 4]]).as_mutable() + >>> isinstance(A, Basic) + False + + >>> B = sympify(A) + >>> isinstance(B, Basic) + True + """ + __slots__ = ('_mhash', # hash value + '_args', # arguments + '_assumptions' + ) + + _args: tuple[Basic, ...] + _mhash: int | None + + @property + def __sympy__(self): + return True + + def __init_subclass__(cls): + # Initialize the default_assumptions FactKB and also any assumptions + # property methods. This method will only be called for subclasses of + # Basic but not for Basic itself so we call + # _prepare_class_assumptions(Basic) below the class definition. + super().__init_subclass__() + _prepare_class_assumptions(cls) + + # To be overridden with True in the appropriate subclasses + is_number = False + is_Atom = False + is_Symbol = False + is_symbol = False + is_Indexed = False + is_Dummy = False + is_Wild = False + is_Function = False + is_Add = False + is_Mul = False + is_Pow = False + is_Number = False + is_Float = False + is_Rational = False + is_Integer = False + is_NumberSymbol = False + is_Order = False + is_Derivative = False + is_Piecewise = False + is_Poly = False + is_AlgebraicNumber = False + is_Relational = False + is_Equality = False + is_Boolean = False + is_Not = False + is_Matrix = False + is_Vector = False + is_Point = False + is_MatAdd = False + is_MatMul = False + is_real: bool | None + is_extended_real: bool | None + is_zero: bool | None + is_negative: bool | None + is_commutative: bool | None + + kind: Kind = UndefinedKind + + def __new__(cls, *args): + obj = object.__new__(cls) + obj._assumptions = cls.default_assumptions + obj._mhash = None # will be set by __hash__ method. + + obj._args = args # all items in args must be Basic objects + return obj + + def copy(self): + return self.func(*self.args) + + def __getnewargs__(self): + return self.args + + def __getstate__(self): + return None + + def __setstate__(self, state): + for name, value in state.items(): + setattr(self, name, value) + + def __reduce_ex__(self, protocol): + if protocol < 2: + msg = "Only pickle protocol 2 or higher is supported by SymPy" + raise NotImplementedError(msg) + return super().__reduce_ex__(protocol) + + def __hash__(self) -> int: + # hash cannot be cached using cache_it because infinite recurrence + # occurs as hash is needed for setting cache dictionary keys + h = self._mhash + if h is None: + h = hash((type(self).__name__,) + self._hashable_content()) + self._mhash = h + return h + + def _hashable_content(self): + """Return a tuple of information about self that can be used to + compute the hash. If a class defines additional attributes, + like ``name`` in Symbol, then this method should be updated + accordingly to return such relevant attributes. + + Defining more than _hashable_content is necessary if __eq__ has + been defined by a class. See note about this in Basic.__eq__.""" + return self._args + + @property + def assumptions0(self): + """ + Return object `type` assumptions. + + For example: + + Symbol('x', real=True) + Symbol('x', integer=True) + + are different objects. In other words, besides Python type (Symbol in + this case), the initial assumptions are also forming their typeinfo. + + Examples + ======== + + >>> from sympy import Symbol + >>> from sympy.abc import x + >>> x.assumptions0 + {'commutative': True} + >>> x = Symbol("x", positive=True) + >>> x.assumptions0 + {'commutative': True, 'complex': True, 'extended_negative': False, + 'extended_nonnegative': True, 'extended_nonpositive': False, + 'extended_nonzero': True, 'extended_positive': True, 'extended_real': + True, 'finite': True, 'hermitian': True, 'imaginary': False, + 'infinite': False, 'negative': False, 'nonnegative': True, + 'nonpositive': False, 'nonzero': True, 'positive': True, 'real': + True, 'zero': False} + """ + return {} + + def compare(self, other): + """ + Return -1, 0, 1 if the object is less than, equal, + or greater than other in a canonical sense. + Non-Basic are always greater than Basic. + If both names of the classes being compared appear + in the `ordering_of_classes` then the ordering will + depend on the appearance of the names there. + If either does not appear in that list, then the + comparison is based on the class name. + If the names are the same then a comparison is made + on the length of the hashable content. + Items of the equal-lengthed contents are then + successively compared using the same rules. If there + is never a difference then 0 is returned. + + Examples + ======== + + >>> from sympy.abc import x, y + >>> x.compare(y) + -1 + >>> x.compare(x) + 0 + >>> y.compare(x) + 1 + + """ + # all redefinitions of __cmp__ method should start with the + # following lines: + if self is other: + return 0 + n1 = self.__class__ + n2 = other.__class__ + c = _cmp_name(n1, n2) + if c: + return c + # + st = self._hashable_content() + ot = other._hashable_content() + c = (len(st) > len(ot)) - (len(st) < len(ot)) + if c: + return c + for l, r in zip(st, ot): + l = Basic(*l) if isinstance(l, frozenset) else l + r = Basic(*r) if isinstance(r, frozenset) else r + if isinstance(l, Basic): + c = l.compare(r) + else: + c = (l > r) - (l < r) + if c: + return c + return 0 + + @staticmethod + def _compare_pretty(a, b): + """return -1, 0, 1 if a is canonically less, equal or + greater than b. This is used when 'order=old' is selected + for printing. This puts Order last, orders Rationals + according to value, puts terms in order wrt the power of + the last power appearing in a term. Ties are broken using + Basic.compare. + """ + from sympy.series.order import Order + if isinstance(a, Order) and not isinstance(b, Order): + return 1 + if not isinstance(a, Order) and isinstance(b, Order): + return -1 + + if a.is_Rational and b.is_Rational: + l = a.p * b.q + r = b.p * a.q + return (l > r) - (l < r) + else: + from .symbol import Wild + p1, p2, p3 = Wild("p1"), Wild("p2"), Wild("p3") + r_a = a.match(p1 * p2**p3) + if r_a and p3 in r_a: + a3 = r_a[p3] + r_b = b.match(p1 * p2**p3) + if r_b and p3 in r_b: + b3 = r_b[p3] + c = Basic.compare(a3, b3) + if c != 0: + return c + + # break ties + return Basic.compare(a, b) + + @classmethod + def fromiter(cls, args, **assumptions): + """ + Create a new object from an iterable. + + This is a convenience function that allows one to create objects from + any iterable, without having to convert to a list or tuple first. + + Examples + ======== + + >>> from sympy import Tuple + >>> Tuple.fromiter(i for i in range(5)) + (0, 1, 2, 3, 4) + + """ + return cls(*tuple(args), **assumptions) + + @classmethod + def class_key(cls): + """Nice order of classes.""" + return 5, 0, cls.__name__ + + @cacheit + def sort_key(self, order=None): + """ + Return a sort key. + + Examples + ======== + + >>> from sympy import S, I + + >>> sorted([S(1)/2, I, -I], key=lambda x: x.sort_key()) + [1/2, -I, I] + + >>> S("[x, 1/x, 1/x**2, x**2, x**(1/2), x**(1/4), x**(3/2)]") + [x, 1/x, x**(-2), x**2, sqrt(x), x**(1/4), x**(3/2)] + >>> sorted(_, key=lambda x: x.sort_key()) + [x**(-2), 1/x, x**(1/4), sqrt(x), x, x**(3/2), x**2] + + """ + + # XXX: remove this when issue 5169 is fixed + def inner_key(arg): + if isinstance(arg, Basic): + return arg.sort_key(order) + else: + return arg + + args = self._sorted_args + args = len(args), tuple([inner_key(arg) for arg in args]) + return self.class_key(), args, S.One.sort_key(), S.One + + def _do_eq_sympify(self, other): + """Returns a boolean indicating whether a == b when either a + or b is not a Basic. This is only done for types that were either + added to `converter` by a 3rd party or when the object has `_sympy_` + defined. This essentially reuses the code in `_sympify` that is + specific for this use case. Non-user defined types that are meant + to work with SymPy should be handled directly in the __eq__ methods + of the `Basic` classes it could equate to and not be converted. Note + that after conversion, `==` is used again since it is not + necessarily clear whether `self` or `other`'s __eq__ method needs + to be used.""" + for superclass in type(other).__mro__: + conv = _external_converter.get(superclass) + if conv is not None: + return self == conv(other) + if hasattr(other, '_sympy_'): + return self == other._sympy_() + return NotImplemented + + def __eq__(self, other): + """Return a boolean indicating whether a == b on the basis of + their symbolic trees. + + This is the same as a.compare(b) == 0 but faster. + + Notes + ===== + + If a class that overrides __eq__() needs to retain the + implementation of __hash__() from a parent class, the + interpreter must be told this explicitly by setting + __hash__ : Callable[[object], int] = .__hash__. + Otherwise the inheritance of __hash__() will be blocked, + just as if __hash__ had been explicitly set to None. + + References + ========== + + from https://docs.python.org/dev/reference/datamodel.html#object.__hash__ + """ + if self is other: + return True + + if not isinstance(other, Basic): + return self._do_eq_sympify(other) + + # check for pure number expr + if not (self.is_Number and other.is_Number) and ( + type(self) != type(other)): + return False + a, b = self._hashable_content(), other._hashable_content() + if a != b: + return False + # check number *in* an expression + for a, b in zip(a, b): + if not isinstance(a, Basic): + continue + if a.is_Number and type(a) != type(b): + return False + return True + + def __ne__(self, other): + """``a != b`` -> Compare two symbolic trees and see whether they are different + + this is the same as: + + ``a.compare(b) != 0`` + + but faster + """ + return not self == other + + def dummy_eq(self, other, symbol=None): + """ + Compare two expressions and handle dummy symbols. + + Examples + ======== + + >>> from sympy import Dummy + >>> from sympy.abc import x, y + + >>> u = Dummy('u') + + >>> (u**2 + 1).dummy_eq(x**2 + 1) + True + >>> (u**2 + 1) == (x**2 + 1) + False + + >>> (u**2 + y).dummy_eq(x**2 + y, x) + True + >>> (u**2 + y).dummy_eq(x**2 + y, y) + False + + """ + s = self.as_dummy() + o = _sympify(other) + o = o.as_dummy() + + dummy_symbols = [i for i in s.free_symbols if i.is_Dummy] + + if len(dummy_symbols) == 1: + dummy = dummy_symbols.pop() + else: + return s == o + + if symbol is None: + symbols = o.free_symbols + + if len(symbols) == 1: + symbol = symbols.pop() + else: + return s == o + + tmp = dummy.__class__() + + return s.xreplace({dummy: tmp}) == o.xreplace({symbol: tmp}) + + def atoms(self, *types): + """Returns the atoms that form the current object. + + By default, only objects that are truly atomic and cannot + be divided into smaller pieces are returned: symbols, numbers, + and number symbols like I and pi. It is possible to request + atoms of any type, however, as demonstrated below. + + Examples + ======== + + >>> from sympy import I, pi, sin + >>> from sympy.abc import x, y + >>> (1 + x + 2*sin(y + I*pi)).atoms() + {1, 2, I, pi, x, y} + + If one or more types are given, the results will contain only + those types of atoms. + + >>> from sympy import Number, NumberSymbol, Symbol + >>> (1 + x + 2*sin(y + I*pi)).atoms(Symbol) + {x, y} + + >>> (1 + x + 2*sin(y + I*pi)).atoms(Number) + {1, 2} + + >>> (1 + x + 2*sin(y + I*pi)).atoms(Number, NumberSymbol) + {1, 2, pi} + + >>> (1 + x + 2*sin(y + I*pi)).atoms(Number, NumberSymbol, I) + {1, 2, I, pi} + + Note that I (imaginary unit) and zoo (complex infinity) are special + types of number symbols and are not part of the NumberSymbol class. + + The type can be given implicitly, too: + + >>> (1 + x + 2*sin(y + I*pi)).atoms(x) # x is a Symbol + {x, y} + + Be careful to check your assumptions when using the implicit option + since ``S(1).is_Integer = True`` but ``type(S(1))`` is ``One``, a special type + of SymPy atom, while ``type(S(2))`` is type ``Integer`` and will find all + integers in an expression: + + >>> from sympy import S + >>> (1 + x + 2*sin(y + I*pi)).atoms(S(1)) + {1} + + >>> (1 + x + 2*sin(y + I*pi)).atoms(S(2)) + {1, 2} + + Finally, arguments to atoms() can select more than atomic atoms: any + SymPy type (loaded in core/__init__.py) can be listed as an argument + and those types of "atoms" as found in scanning the arguments of the + expression recursively: + + >>> from sympy import Function, Mul + >>> from sympy.core.function import AppliedUndef + >>> f = Function('f') + >>> (1 + f(x) + 2*sin(y + I*pi)).atoms(Function) + {f(x), sin(y + I*pi)} + >>> (1 + f(x) + 2*sin(y + I*pi)).atoms(AppliedUndef) + {f(x)} + + >>> (1 + x + 2*sin(y + I*pi)).atoms(Mul) + {I*pi, 2*sin(y + I*pi)} + + """ + if types: + types = tuple( + [t if isinstance(t, type) else type(t) for t in types]) + nodes = _preorder_traversal(self) + if types: + result = {node for node in nodes if isinstance(node, types)} + else: + result = {node for node in nodes if not node.args} + return result + + @property + def free_symbols(self) -> set[Basic]: + """Return from the atoms of self those which are free symbols. + + Not all free symbols are ``Symbol``. Eg: IndexedBase('I')[0].free_symbols + + For most expressions, all symbols are free symbols. For some classes + this is not true. e.g. Integrals use Symbols for the dummy variables + which are bound variables, so Integral has a method to return all + symbols except those. Derivative keeps track of symbols with respect + to which it will perform a derivative; those are + bound variables, too, so it has its own free_symbols method. + + Any other method that uses bound variables should implement a + free_symbols method.""" + empty: set[Basic] = set() + return empty.union(*(a.free_symbols for a in self.args)) + + @property + def expr_free_symbols(self): + sympy_deprecation_warning(""" + The expr_free_symbols property is deprecated. Use free_symbols to get + the free symbols of an expression. + """, + deprecated_since_version="1.9", + active_deprecations_target="deprecated-expr-free-symbols") + return set() + + def as_dummy(self): + """Return the expression with any objects having structurally + bound symbols replaced with unique, canonical symbols within + the object in which they appear and having only the default + assumption for commutativity being True. When applied to a + symbol a new symbol having only the same commutativity will be + returned. + + Examples + ======== + + >>> from sympy import Integral, Symbol + >>> from sympy.abc import x + >>> r = Symbol('r', real=True) + >>> Integral(r, (r, x)).as_dummy() + Integral(_0, (_0, x)) + >>> _.variables[0].is_real is None + True + >>> r.as_dummy() + _r + + Notes + ===== + + Any object that has structurally bound variables should have + a property, `bound_symbols` that returns those symbols + appearing in the object. + """ + from .symbol import Dummy, Symbol + def can(x): + # mask free that shadow bound + free = x.free_symbols + bound = set(x.bound_symbols) + d = {i: Dummy() for i in bound & free} + x = x.subs(d) + # replace bound with canonical names + x = x.xreplace(x.canonical_variables) + # return after undoing masking + return x.xreplace({v: k for k, v in d.items()}) + if not self.has(Symbol): + return self + return self.replace( + lambda x: hasattr(x, 'bound_symbols'), + can, + simultaneous=False) + + @property + def canonical_variables(self): + """Return a dictionary mapping any variable defined in + ``self.bound_symbols`` to Symbols that do not clash + with any free symbols in the expression. + + Examples + ======== + + >>> from sympy import Lambda + >>> from sympy.abc import x + >>> Lambda(x, 2*x).canonical_variables + {x: _0} + """ + if not hasattr(self, 'bound_symbols'): + return {} + dums = numbered_symbols('_') + reps = {} + # watch out for free symbol that are not in bound symbols; + # those that are in bound symbols are about to get changed + bound = self.bound_symbols + names = {i.name for i in self.free_symbols - set(bound)} + for b in bound: + d = next(dums) + if b.is_Symbol: + while d.name in names: + d = next(dums) + reps[b] = d + return reps + + def rcall(self, *args): + """Apply on the argument recursively through the expression tree. + + This method is used to simulate a common abuse of notation for + operators. For instance, in SymPy the following will not work: + + ``(x+Lambda(y, 2*y))(z) == x+2*z``, + + however, you can use: + + >>> from sympy import Lambda + >>> from sympy.abc import x, y, z + >>> (x + Lambda(y, 2*y)).rcall(z) + x + 2*z + """ + return Basic._recursive_call(self, args) + + @staticmethod + def _recursive_call(expr_to_call, on_args): + """Helper for rcall method.""" + from .symbol import Symbol + def the_call_method_is_overridden(expr): + for cls in getmro(type(expr)): + if '__call__' in cls.__dict__: + return cls != Basic + + if callable(expr_to_call) and the_call_method_is_overridden(expr_to_call): + if isinstance(expr_to_call, Symbol): # XXX When you call a Symbol it is + return expr_to_call # transformed into an UndefFunction + else: + return expr_to_call(*on_args) + elif expr_to_call.args: + args = [Basic._recursive_call( + sub, on_args) for sub in expr_to_call.args] + return type(expr_to_call)(*args) + else: + return expr_to_call + + def is_hypergeometric(self, k): + from sympy.simplify.simplify import hypersimp + from sympy.functions.elementary.piecewise import Piecewise + if self.has(Piecewise): + return None + return hypersimp(self, k) is not None + + @property + def is_comparable(self): + """Return True if self can be computed to a real number + (or already is a real number) with precision, else False. + + Examples + ======== + + >>> from sympy import exp_polar, pi, I + >>> (I*exp_polar(I*pi/2)).is_comparable + True + >>> (I*exp_polar(I*pi*2)).is_comparable + False + + A False result does not mean that `self` cannot be rewritten + into a form that would be comparable. For example, the + difference computed below is zero but without simplification + it does not evaluate to a zero with precision: + + >>> e = 2**pi*(1 + 2**pi) + >>> dif = e - e.expand() + >>> dif.is_comparable + False + >>> dif.n(2)._prec + 1 + + """ + is_extended_real = self.is_extended_real + if is_extended_real is False: + return False + if not self.is_number: + return False + # don't re-eval numbers that are already evaluated since + # this will create spurious precision + n, i = [p.evalf(2) if not p.is_Number else p + for p in self.as_real_imag()] + if not (i.is_Number and n.is_Number): + return False + if i: + # if _prec = 1 we can't decide and if not, + # the answer is False because numbers with + # imaginary parts can't be compared + # so return False + return False + else: + return n._prec != 1 + + @property + def func(self): + """ + The top-level function in an expression. + + The following should hold for all objects:: + + >> x == x.func(*x.args) + + Examples + ======== + + >>> from sympy.abc import x + >>> a = 2*x + >>> a.func + + >>> a.args + (2, x) + >>> a.func(*a.args) + 2*x + >>> a == a.func(*a.args) + True + + """ + return self.__class__ + + @property + def args(self) -> tuple[Basic, ...]: + """Returns a tuple of arguments of 'self'. + + Examples + ======== + + >>> from sympy import cot + >>> from sympy.abc import x, y + + >>> cot(x).args + (x,) + + >>> cot(x).args[0] + x + + >>> (x*y).args + (x, y) + + >>> (x*y).args[1] + y + + Notes + ===== + + Never use self._args, always use self.args. + Only use _args in __new__ when creating a new function. + Do not override .args() from Basic (so that it is easy to + change the interface in the future if needed). + """ + return self._args + + @property + def _sorted_args(self): + """ + The same as ``args``. Derived classes which do not fix an + order on their arguments should override this method to + produce the sorted representation. + """ + return self.args + + def as_content_primitive(self, radical=False, clear=True): + """A stub to allow Basic args (like Tuple) to be skipped when computing + the content and primitive components of an expression. + + See Also + ======== + + sympy.core.expr.Expr.as_content_primitive + """ + return S.One, self + + def subs(self, *args, **kwargs): + """ + Substitutes old for new in an expression after sympifying args. + + `args` is either: + - two arguments, e.g. foo.subs(old, new) + - one iterable argument, e.g. foo.subs(iterable). The iterable may be + o an iterable container with (old, new) pairs. In this case the + replacements are processed in the order given with successive + patterns possibly affecting replacements already made. + o a dict or set whose key/value items correspond to old/new pairs. + In this case the old/new pairs will be sorted by op count and in + case of a tie, by number of args and the default_sort_key. The + resulting sorted list is then processed as an iterable container + (see previous). + + If the keyword ``simultaneous`` is True, the subexpressions will not be + evaluated until all the substitutions have been made. + + Examples + ======== + + >>> from sympy import pi, exp, limit, oo + >>> from sympy.abc import x, y + >>> (1 + x*y).subs(x, pi) + pi*y + 1 + >>> (1 + x*y).subs({x:pi, y:2}) + 1 + 2*pi + >>> (1 + x*y).subs([(x, pi), (y, 2)]) + 1 + 2*pi + >>> reps = [(y, x**2), (x, 2)] + >>> (x + y).subs(reps) + 6 + >>> (x + y).subs(reversed(reps)) + x**2 + 2 + + >>> (x**2 + x**4).subs(x**2, y) + y**2 + y + + To replace only the x**2 but not the x**4, use xreplace: + + >>> (x**2 + x**4).xreplace({x**2: y}) + x**4 + y + + To delay evaluation until all substitutions have been made, + set the keyword ``simultaneous`` to True: + + >>> (x/y).subs([(x, 0), (y, 0)]) + 0 + >>> (x/y).subs([(x, 0), (y, 0)], simultaneous=True) + nan + + This has the added feature of not allowing subsequent substitutions + to affect those already made: + + >>> ((x + y)/y).subs({x + y: y, y: x + y}) + 1 + >>> ((x + y)/y).subs({x + y: y, y: x + y}, simultaneous=True) + y/(x + y) + + In order to obtain a canonical result, unordered iterables are + sorted by count_op length, number of arguments and by the + default_sort_key to break any ties. All other iterables are left + unsorted. + + >>> from sympy import sqrt, sin, cos + >>> from sympy.abc import a, b, c, d, e + + >>> A = (sqrt(sin(2*x)), a) + >>> B = (sin(2*x), b) + >>> C = (cos(2*x), c) + >>> D = (x, d) + >>> E = (exp(x), e) + + >>> expr = sqrt(sin(2*x))*sin(exp(x)*x)*cos(2*x) + sin(2*x) + + >>> expr.subs(dict([A, B, C, D, E])) + a*c*sin(d*e) + b + + The resulting expression represents a literal replacement of the + old arguments with the new arguments. This may not reflect the + limiting behavior of the expression: + + >>> (x**3 - 3*x).subs({x: oo}) + nan + + >>> limit(x**3 - 3*x, x, oo) + oo + + If the substitution will be followed by numerical + evaluation, it is better to pass the substitution to + evalf as + + >>> (1/x).evalf(subs={x: 3.0}, n=21) + 0.333333333333333333333 + + rather than + + >>> (1/x).subs({x: 3.0}).evalf(21) + 0.333333333333333314830 + + as the former will ensure that the desired level of precision is + obtained. + + See Also + ======== + replace: replacement capable of doing wildcard-like matching, + parsing of match, and conditional replacements + xreplace: exact node replacement in expr tree; also capable of + using matching rules + sympy.core.evalf.EvalfMixin.evalf: calculates the given formula to a desired level of precision + + """ + from .containers import Dict + from .symbol import Dummy, Symbol + from .numbers import _illegal + + unordered = False + if len(args) == 1: + + sequence = args[0] + if isinstance(sequence, set): + unordered = True + elif isinstance(sequence, (Dict, Mapping)): + unordered = True + sequence = sequence.items() + elif not iterable(sequence): + raise ValueError(filldedent(""" + When a single argument is passed to subs + it should be a dictionary of old: new pairs or an iterable + of (old, new) tuples.""")) + elif len(args) == 2: + sequence = [args] + else: + raise ValueError("subs accepts either 1 or 2 arguments") + + def sympify_old(old): + if isinstance(old, str): + # Use Symbol rather than parse_expr for old + return Symbol(old) + elif isinstance(old, type): + # Allow a type e.g. Function('f') or sin + return sympify(old, strict=False) + else: + return sympify(old, strict=True) + + def sympify_new(new): + if isinstance(new, (str, type)): + # Allow a type or parse a string input + return sympify(new, strict=False) + else: + return sympify(new, strict=True) + + sequence = [(sympify_old(s1), sympify_new(s2)) for s1, s2 in sequence] + + # skip if there is no change + sequence = [(s1, s2) for s1, s2 in sequence if not _aresame(s1, s2)] + + simultaneous = kwargs.pop('simultaneous', False) + + if unordered: + from .sorting import _nodes, default_sort_key + sequence = dict(sequence) + # order so more complex items are first and items + # of identical complexity are ordered so + # f(x) < f(y) < x < y + # \___ 2 __/ \_1_/ <- number of nodes + # + # For more complex ordering use an unordered sequence. + k = list(ordered(sequence, default=False, keys=( + lambda x: -_nodes(x), + default_sort_key, + ))) + sequence = [(k, sequence[k]) for k in k] + # do infinities first + if not simultaneous: + redo = [i for i, seq in enumerate(sequence) if seq[1] in _illegal] + for i in reversed(redo): + sequence.insert(0, sequence.pop(i)) + + if simultaneous: # XXX should this be the default for dict subs? + reps = {} + rv = self + kwargs['hack2'] = True + m = Dummy('subs_m') + for old, new in sequence: + com = new.is_commutative + if com is None: + com = True + d = Dummy('subs_d', commutative=com) + # using d*m so Subs will be used on dummy variables + # in things like Derivative(f(x, y), x) in which x + # is both free and bound + rv = rv._subs(old, d*m, **kwargs) + if not isinstance(rv, Basic): + break + reps[d] = new + reps[m] = S.One # get rid of m + return rv.xreplace(reps) + else: + rv = self + for old, new in sequence: + rv = rv._subs(old, new, **kwargs) + if not isinstance(rv, Basic): + break + return rv + + @cacheit + def _subs(self, old, new, **hints): + """Substitutes an expression old -> new. + + If self is not equal to old then _eval_subs is called. + If _eval_subs does not want to make any special replacement + then a None is received which indicates that the fallback + should be applied wherein a search for replacements is made + amongst the arguments of self. + + >>> from sympy import Add + >>> from sympy.abc import x, y, z + + Examples + ======== + + Add's _eval_subs knows how to target x + y in the following + so it makes the change: + + >>> (x + y + z).subs(x + y, 1) + z + 1 + + Add's _eval_subs does not need to know how to find x + y in + the following: + + >>> Add._eval_subs(z*(x + y) + 3, x + y, 1) is None + True + + The returned None will cause the fallback routine to traverse the args and + pass the z*(x + y) arg to Mul where the change will take place and the + substitution will succeed: + + >>> (z*(x + y) + 3).subs(x + y, 1) + z + 3 + + ** Developers Notes ** + + An _eval_subs routine for a class should be written if: + + 1) any arguments are not instances of Basic (e.g. bool, tuple); + + 2) some arguments should not be targeted (as in integration + variables); + + 3) if there is something other than a literal replacement + that should be attempted (as in Piecewise where the condition + may be updated without doing a replacement). + + If it is overridden, here are some special cases that might arise: + + 1) If it turns out that no special change was made and all + the original sub-arguments should be checked for + replacements then None should be returned. + + 2) If it is necessary to do substitutions on a portion of + the expression then _subs should be called. _subs will + handle the case of any sub-expression being equal to old + (which usually would not be the case) while its fallback + will handle the recursion into the sub-arguments. For + example, after Add's _eval_subs removes some matching terms + it must process the remaining terms so it calls _subs + on each of the un-matched terms and then adds them + onto the terms previously obtained. + + 3) If the initial expression should remain unchanged then + the original expression should be returned. (Whenever an + expression is returned, modified or not, no further + substitution of old -> new is attempted.) Sum's _eval_subs + routine uses this strategy when a substitution is attempted + on any of its summation variables. + """ + + def fallback(self, old, new): + """ + Try to replace old with new in any of self's arguments. + """ + hit = False + args = list(self.args) + for i, arg in enumerate(args): + if not hasattr(arg, '_eval_subs'): + continue + arg = arg._subs(old, new, **hints) + if not _aresame(arg, args[i]): + hit = True + args[i] = arg + if hit: + rv = self.func(*args) + hack2 = hints.get('hack2', False) + if hack2 and self.is_Mul and not rv.is_Mul: # 2-arg hack + coeff = S.One + nonnumber = [] + for i in args: + if i.is_Number: + coeff *= i + else: + nonnumber.append(i) + nonnumber = self.func(*nonnumber) + if coeff is S.One: + return nonnumber + else: + return self.func(coeff, nonnumber, evaluate=False) + return rv + return self + + if _aresame(self, old): + return new + + rv = self._eval_subs(old, new) + if rv is None: + rv = fallback(self, old, new) + return rv + + def _eval_subs(self, old, new): + """Override this stub if you want to do anything more than + attempt a replacement of old with new in the arguments of self. + + See also + ======== + + _subs + """ + return None + + def xreplace(self, rule): + """ + Replace occurrences of objects within the expression. + + Parameters + ========== + + rule : dict-like + Expresses a replacement rule + + Returns + ======= + + xreplace : the result of the replacement + + Examples + ======== + + >>> from sympy import symbols, pi, exp + >>> x, y, z = symbols('x y z') + >>> (1 + x*y).xreplace({x: pi}) + pi*y + 1 + >>> (1 + x*y).xreplace({x: pi, y: 2}) + 1 + 2*pi + + Replacements occur only if an entire node in the expression tree is + matched: + + >>> (x*y + z).xreplace({x*y: pi}) + z + pi + >>> (x*y*z).xreplace({x*y: pi}) + x*y*z + >>> (2*x).xreplace({2*x: y, x: z}) + y + >>> (2*2*x).xreplace({2*x: y, x: z}) + 4*z + >>> (x + y + 2).xreplace({x + y: 2}) + x + y + 2 + >>> (x + 2 + exp(x + 2)).xreplace({x + 2: y}) + x + exp(y) + 2 + + xreplace does not differentiate between free and bound symbols. In the + following, subs(x, y) would not change x since it is a bound symbol, + but xreplace does: + + >>> from sympy import Integral + >>> Integral(x, (x, 1, 2*x)).xreplace({x: y}) + Integral(y, (y, 1, 2*y)) + + Trying to replace x with an expression raises an error: + + >>> Integral(x, (x, 1, 2*x)).xreplace({x: 2*y}) # doctest: +SKIP + ValueError: Invalid limits given: ((2*y, 1, 4*y),) + + See Also + ======== + replace: replacement capable of doing wildcard-like matching, + parsing of match, and conditional replacements + subs: substitution of subexpressions as defined by the objects + themselves. + + """ + value, _ = self._xreplace(rule) + return value + + def _xreplace(self, rule): + """ + Helper for xreplace. Tracks whether a replacement actually occurred. + """ + if self in rule: + return rule[self], True + elif rule: + args = [] + changed = False + for a in self.args: + _xreplace = getattr(a, '_xreplace', None) + if _xreplace is not None: + a_xr = _xreplace(rule) + args.append(a_xr[0]) + changed |= a_xr[1] + else: + args.append(a) + args = tuple(args) + if changed: + return self.func(*args), True + return self, False + + @cacheit + def has(self, *patterns): + """ + Test whether any subexpression matches any of the patterns. + + Examples + ======== + + >>> from sympy import sin + >>> from sympy.abc import x, y, z + >>> (x**2 + sin(x*y)).has(z) + False + >>> (x**2 + sin(x*y)).has(x, y, z) + True + >>> x.has(x) + True + + Note ``has`` is a structural algorithm with no knowledge of + mathematics. Consider the following half-open interval: + + >>> from sympy import Interval + >>> i = Interval.Lopen(0, 5); i + Interval.Lopen(0, 5) + >>> i.args + (0, 5, True, False) + >>> i.has(4) # there is no "4" in the arguments + False + >>> i.has(0) # there *is* a "0" in the arguments + True + + Instead, use ``contains`` to determine whether a number is in the + interval or not: + + >>> i.contains(4) + True + >>> i.contains(0) + False + + + Note that ``expr.has(*patterns)`` is exactly equivalent to + ``any(expr.has(p) for p in patterns)``. In particular, ``False`` is + returned when the list of patterns is empty. + + >>> x.has() + False + + """ + return self._has(iterargs, *patterns) + + def has_xfree(self, s: set[Basic]): + """Return True if self has any of the patterns in s as a + free argument, else False. This is like `Basic.has_free` + but this will only report exact argument matches. + + Examples + ======== + + >>> from sympy import Function + >>> from sympy.abc import x, y + >>> f = Function('f') + >>> f(x).has_xfree({f}) + False + >>> f(x).has_xfree({f(x)}) + True + >>> f(x + 1).has_xfree({x}) + True + >>> f(x + 1).has_xfree({x + 1}) + True + >>> f(x + y + 1).has_xfree({x + 1}) + False + """ + # protect O(1) containment check by requiring: + if type(s) is not set: + raise TypeError('expecting set argument') + return any(a in s for a in iterfreeargs(self)) + + @cacheit + def has_free(self, *patterns): + """Return True if self has object(s) ``x`` as a free expression + else False. + + Examples + ======== + + >>> from sympy import Integral, Function + >>> from sympy.abc import x, y + >>> f = Function('f') + >>> g = Function('g') + >>> expr = Integral(f(x), (f(x), 1, g(y))) + >>> expr.free_symbols + {y} + >>> expr.has_free(g(y)) + True + >>> expr.has_free(*(x, f(x))) + False + + This works for subexpressions and types, too: + + >>> expr.has_free(g) + True + >>> (x + y + 1).has_free(y + 1) + True + """ + if not patterns: + return False + p0 = patterns[0] + if len(patterns) == 1 and iterable(p0) and not isinstance(p0, Basic): + # Basic can contain iterables (though not non-Basic, ideally) + # but don't encourage mixed passing patterns + raise TypeError(filldedent(''' + Expecting 1 or more Basic args, not a single + non-Basic iterable. Don't forget to unpack + iterables: `eq.has_free(*patterns)`''')) + # try quick test first + s = set(patterns) + rv = self.has_xfree(s) + if rv: + return rv + # now try matching through slower _has + return self._has(iterfreeargs, *patterns) + + def _has(self, iterargs, *patterns): + # separate out types and unhashable objects + type_set = set() # only types + p_set = set() # hashable non-types + for p in patterns: + if isinstance(p, type) and issubclass(p, Basic): + type_set.add(p) + continue + if not isinstance(p, Basic): + try: + p = _sympify(p) + except SympifyError: + continue # Basic won't have this in it + p_set.add(p) # fails if object defines __eq__ but + # doesn't define __hash__ + types = tuple(type_set) # + for i in iterargs(self): # + if i in p_set: # <--- here, too + return True + if isinstance(i, types): + return True + + # use matcher if defined, e.g. operations defines + # matcher that checks for exact subset containment, + # (x + y + 1).has(x + 1) -> True + for i in p_set - type_set: # types don't have matchers + if not hasattr(i, '_has_matcher'): + continue + match = i._has_matcher() + if any(match(arg) for arg in iterargs(self)): + return True + + # no success + return False + + def replace(self, query, value, map=False, simultaneous=True, exact=None): + """ + Replace matching subexpressions of ``self`` with ``value``. + + If ``map = True`` then also return the mapping {old: new} where ``old`` + was a sub-expression found with query and ``new`` is the replacement + value for it. If the expression itself does not match the query, then + the returned value will be ``self.xreplace(map)`` otherwise it should + be ``self.subs(ordered(map.items()))``. + + Traverses an expression tree and performs replacement of matching + subexpressions from the bottom to the top of the tree. The default + approach is to do the replacement in a simultaneous fashion so + changes made are targeted only once. If this is not desired or causes + problems, ``simultaneous`` can be set to False. + + In addition, if an expression containing more than one Wild symbol + is being used to match subexpressions and the ``exact`` flag is None + it will be set to True so the match will only succeed if all non-zero + values are received for each Wild that appears in the match pattern. + Setting this to False accepts a match of 0; while setting it True + accepts all matches that have a 0 in them. See example below for + cautions. + + The list of possible combinations of queries and replacement values + is listed below: + + Examples + ======== + + Initial setup + + >>> from sympy import log, sin, cos, tan, Wild, Mul, Add + >>> from sympy.abc import x, y + >>> f = log(sin(x)) + tan(sin(x**2)) + + 1.1. type -> type + obj.replace(type, newtype) + + When object of type ``type`` is found, replace it with the + result of passing its argument(s) to ``newtype``. + + >>> f.replace(sin, cos) + log(cos(x)) + tan(cos(x**2)) + >>> sin(x).replace(sin, cos, map=True) + (cos(x), {sin(x): cos(x)}) + >>> (x*y).replace(Mul, Add) + x + y + + 1.2. type -> func + obj.replace(type, func) + + When object of type ``type`` is found, apply ``func`` to its + argument(s). ``func`` must be written to handle the number + of arguments of ``type``. + + >>> f.replace(sin, lambda arg: sin(2*arg)) + log(sin(2*x)) + tan(sin(2*x**2)) + >>> (x*y).replace(Mul, lambda *args: sin(2*Mul(*args))) + sin(2*x*y) + + 2.1. pattern -> expr + obj.replace(pattern(wild), expr(wild)) + + Replace subexpressions matching ``pattern`` with the expression + written in terms of the Wild symbols in ``pattern``. + + >>> a, b = map(Wild, 'ab') + >>> f.replace(sin(a), tan(a)) + log(tan(x)) + tan(tan(x**2)) + >>> f.replace(sin(a), tan(a/2)) + log(tan(x/2)) + tan(tan(x**2/2)) + >>> f.replace(sin(a), a) + log(x) + tan(x**2) + >>> (x*y).replace(a*x, a) + y + + Matching is exact by default when more than one Wild symbol + is used: matching fails unless the match gives non-zero + values for all Wild symbols: + + >>> (2*x + y).replace(a*x + b, b - a) + y - 2 + >>> (2*x).replace(a*x + b, b - a) + 2*x + + When set to False, the results may be non-intuitive: + + >>> (2*x).replace(a*x + b, b - a, exact=False) + 2/x + + 2.2. pattern -> func + obj.replace(pattern(wild), lambda wild: expr(wild)) + + All behavior is the same as in 2.1 but now a function in terms of + pattern variables is used rather than an expression: + + >>> f.replace(sin(a), lambda a: sin(2*a)) + log(sin(2*x)) + tan(sin(2*x**2)) + + 3.1. func -> func + obj.replace(filter, func) + + Replace subexpression ``e`` with ``func(e)`` if ``filter(e)`` + is True. + + >>> g = 2*sin(x**3) + >>> g.replace(lambda expr: expr.is_Number, lambda expr: expr**2) + 4*sin(x**9) + + The expression itself is also targeted by the query but is done in + such a fashion that changes are not made twice. + + >>> e = x*(x*y + 1) + >>> e.replace(lambda x: x.is_Mul, lambda x: 2*x) + 2*x*(2*x*y + 1) + + When matching a single symbol, `exact` will default to True, but + this may or may not be the behavior that is desired: + + Here, we want `exact=False`: + + >>> from sympy import Function + >>> f = Function('f') + >>> e = f(1) + f(0) + >>> q = f(a), lambda a: f(a + 1) + >>> e.replace(*q, exact=False) + f(1) + f(2) + >>> e.replace(*q, exact=True) + f(0) + f(2) + + But here, the nature of matching makes selecting + the right setting tricky: + + >>> e = x**(1 + y) + >>> (x**(1 + y)).replace(x**(1 + a), lambda a: x**-a, exact=False) + x + >>> (x**(1 + y)).replace(x**(1 + a), lambda a: x**-a, exact=True) + x**(-x - y + 1) + >>> (x**y).replace(x**(1 + a), lambda a: x**-a, exact=False) + x + >>> (x**y).replace(x**(1 + a), lambda a: x**-a, exact=True) + x**(1 - y) + + It is probably better to use a different form of the query + that describes the target expression more precisely: + + >>> (1 + x**(1 + y)).replace( + ... lambda x: x.is_Pow and x.exp.is_Add and x.exp.args[0] == 1, + ... lambda x: x.base**(1 - (x.exp - 1))) + ... + x**(1 - y) + 1 + + See Also + ======== + + subs: substitution of subexpressions as defined by the objects + themselves. + xreplace: exact node replacement in expr tree; also capable of + using matching rules + + """ + + try: + query = _sympify(query) + except SympifyError: + pass + try: + value = _sympify(value) + except SympifyError: + pass + if isinstance(query, type): + _query = lambda expr: isinstance(expr, query) + + if isinstance(value, type): + _value = lambda expr, result: value(*expr.args) + elif callable(value): + _value = lambda expr, result: value(*expr.args) + else: + raise TypeError( + "given a type, replace() expects another " + "type or a callable") + elif isinstance(query, Basic): + _query = lambda expr: expr.match(query) + if exact is None: + from .symbol import Wild + exact = (len(query.atoms(Wild)) > 1) + + if isinstance(value, Basic): + if exact: + _value = lambda expr, result: (value.subs(result) + if all(result.values()) else expr) + else: + _value = lambda expr, result: value.subs(result) + elif callable(value): + # match dictionary keys get the trailing underscore stripped + # from them and are then passed as keywords to the callable; + # if ``exact`` is True, only accept match if there are no null + # values amongst those matched. + if exact: + _value = lambda expr, result: (value(** + {str(k)[:-1]: v for k, v in result.items()}) + if all(val for val in result.values()) else expr) + else: + _value = lambda expr, result: value(** + {str(k)[:-1]: v for k, v in result.items()}) + else: + raise TypeError( + "given an expression, replace() expects " + "another expression or a callable") + elif callable(query): + _query = query + + if callable(value): + _value = lambda expr, result: value(expr) + else: + raise TypeError( + "given a callable, replace() expects " + "another callable") + else: + raise TypeError( + "first argument to replace() must be a " + "type, an expression or a callable") + + def walk(rv, F): + """Apply ``F`` to args and then to result. + """ + args = getattr(rv, 'args', None) + if args is not None: + if args: + newargs = tuple([walk(a, F) for a in args]) + if args != newargs: + rv = rv.func(*newargs) + if simultaneous: + # if rv is something that was already + # matched (that was changed) then skip + # applying F again + for i, e in enumerate(args): + if rv == e and e != newargs[i]: + return rv + rv = F(rv) + return rv + + mapping = {} # changes that took place + + def rec_replace(expr): + result = _query(expr) + if result or result == {}: + v = _value(expr, result) + if v is not None and v != expr: + if map: + mapping[expr] = v + expr = v + return expr + + rv = walk(self, rec_replace) + return (rv, mapping) if map else rv + + def find(self, query, group=False): + """Find all subexpressions matching a query.""" + query = _make_find_query(query) + results = list(filter(query, _preorder_traversal(self))) + + if not group: + return set(results) + else: + groups = {} + + for result in results: + if result in groups: + groups[result] += 1 + else: + groups[result] = 1 + + return groups + + def count(self, query): + """Count the number of matching subexpressions.""" + query = _make_find_query(query) + return sum(bool(query(sub)) for sub in _preorder_traversal(self)) + + def matches(self, expr, repl_dict=None, old=False): + """ + Helper method for match() that looks for a match between Wild symbols + in self and expressions in expr. + + Examples + ======== + + >>> from sympy import symbols, Wild, Basic + >>> a, b, c = symbols('a b c') + >>> x = Wild('x') + >>> Basic(a + x, x).matches(Basic(a + b, c)) is None + True + >>> Basic(a + x, x).matches(Basic(a + b + c, b + c)) + {x_: b + c} + """ + expr = sympify(expr) + if not isinstance(expr, self.__class__): + return None + + if repl_dict is None: + repl_dict = {} + else: + repl_dict = repl_dict.copy() + + if self == expr: + return repl_dict + + if len(self.args) != len(expr.args): + return None + + d = repl_dict # already a copy + for arg, other_arg in zip(self.args, expr.args): + if arg == other_arg: + continue + if arg.is_Relational: + try: + d = arg.xreplace(d).matches(other_arg, d, old=old) + except TypeError: # Should be InvalidComparisonError when introduced + d = None + else: + d = arg.xreplace(d).matches(other_arg, d, old=old) + if d is None: + return None + return d + + def match(self, pattern, old=False): + """ + Pattern matching. + + Wild symbols match all. + + Return ``None`` when expression (self) does not match + with pattern. Otherwise return a dictionary such that:: + + pattern.xreplace(self.match(pattern)) == self + + Examples + ======== + + >>> from sympy import Wild, Sum + >>> from sympy.abc import x, y + >>> p = Wild("p") + >>> q = Wild("q") + >>> r = Wild("r") + >>> e = (x+y)**(x+y) + >>> e.match(p**p) + {p_: x + y} + >>> e.match(p**q) + {p_: x + y, q_: x + y} + >>> e = (2*x)**2 + >>> e.match(p*q**r) + {p_: 4, q_: x, r_: 2} + >>> (p*q**r).xreplace(e.match(p*q**r)) + 4*x**2 + + Structurally bound symbols are ignored during matching: + + >>> Sum(x, (x, 1, 2)).match(Sum(y, (y, 1, p))) + {p_: 2} + + But they can be identified if desired: + + >>> Sum(x, (x, 1, 2)).match(Sum(q, (q, 1, p))) + {p_: 2, q_: x} + + The ``old`` flag will give the old-style pattern matching where + expressions and patterns are essentially solved to give the + match. Both of the following give None unless ``old=True``: + + >>> (x - 2).match(p - x, old=True) + {p_: 2*x - 2} + >>> (2/x).match(p*x, old=True) + {p_: 2/x**2} + + """ + pattern = sympify(pattern) + # match non-bound symbols + canonical = lambda x: x if x.is_Symbol else x.as_dummy() + m = canonical(pattern).matches(canonical(self), old=old) + if m is None: + return m + from .symbol import Wild + from .function import WildFunction + from ..tensor.tensor import WildTensor, WildTensorIndex, WildTensorHead + wild = pattern.atoms(Wild, WildFunction, WildTensor, WildTensorIndex, WildTensorHead) + # sanity check + if set(m) - wild: + raise ValueError(filldedent(''' + Some `matches` routine did not use a copy of repl_dict + and injected unexpected symbols. Report this as an + error at https://github.com/sympy/sympy/issues''')) + # now see if bound symbols were requested + bwild = wild - set(m) + if not bwild: + return m + # replace free-Wild symbols in pattern with match result + # so they will match but not be in the next match + wpat = pattern.xreplace(m) + # identify remaining bound wild + w = wpat.matches(self, old=old) + # add them to m + if w: + m.update(w) + # done + return m + + def count_ops(self, visual=None): + """Wrapper for count_ops that returns the operation count.""" + from .function import count_ops + return count_ops(self, visual) + + def doit(self, **hints): + """Evaluate objects that are not evaluated by default like limits, + integrals, sums and products. All objects of this kind will be + evaluated recursively, unless some species were excluded via 'hints' + or unless the 'deep' hint was set to 'False'. + + >>> from sympy import Integral + >>> from sympy.abc import x + + >>> 2*Integral(x, x) + 2*Integral(x, x) + + >>> (2*Integral(x, x)).doit() + x**2 + + >>> (2*Integral(x, x)).doit(deep=False) + 2*Integral(x, x) + + """ + if hints.get('deep', True): + terms = [term.doit(**hints) if isinstance(term, Basic) else term + for term in self.args] + return self.func(*terms) + else: + return self + + def simplify(self, **kwargs): + """See the simplify function in sympy.simplify""" + from sympy.simplify.simplify import simplify + return simplify(self, **kwargs) + + def refine(self, assumption=True): + """See the refine function in sympy.assumptions""" + from sympy.assumptions.refine import refine + return refine(self, assumption) + + def _eval_derivative_n_times(self, s, n): + # This is the default evaluator for derivatives (as called by `diff` + # and `Derivative`), it will attempt a loop to derive the expression + # `n` times by calling the corresponding `_eval_derivative` method, + # while leaving the derivative unevaluated if `n` is symbolic. This + # method should be overridden if the object has a closed form for its + # symbolic n-th derivative. + from .numbers import Integer + if isinstance(n, (int, Integer)): + obj = self + for i in range(n): + obj2 = obj._eval_derivative(s) + if obj == obj2 or obj2 is None: + break + obj = obj2 + return obj2 + else: + return None + + def rewrite(self, *args, deep=True, **hints): + """ + Rewrite *self* using a defined rule. + + Rewriting transforms an expression to another, which is mathematically + equivalent but structurally different. For example you can rewrite + trigonometric functions as complex exponentials or combinatorial + functions as gamma function. + + This method takes a *pattern* and a *rule* as positional arguments. + *pattern* is optional parameter which defines the types of expressions + that will be transformed. If it is not passed, all possible expressions + will be rewritten. *rule* defines how the expression will be rewritten. + + Parameters + ========== + + args : Expr + A *rule*, or *pattern* and *rule*. + - *pattern* is a type or an iterable of types. + - *rule* can be any object. + + deep : bool, optional + If ``True``, subexpressions are recursively transformed. Default is + ``True``. + + Examples + ======== + + If *pattern* is unspecified, all possible expressions are transformed. + + >>> from sympy import cos, sin, exp, I + >>> from sympy.abc import x + >>> expr = cos(x) + I*sin(x) + >>> expr.rewrite(exp) + exp(I*x) + + Pattern can be a type or an iterable of types. + + >>> expr.rewrite(sin, exp) + exp(I*x)/2 + cos(x) - exp(-I*x)/2 + >>> expr.rewrite([cos,], exp) + exp(I*x)/2 + I*sin(x) + exp(-I*x)/2 + >>> expr.rewrite([cos, sin], exp) + exp(I*x) + + Rewriting behavior can be implemented by defining ``_eval_rewrite()`` + method. + + >>> from sympy import Expr, sqrt, pi + >>> class MySin(Expr): + ... def _eval_rewrite(self, rule, args, **hints): + ... x, = args + ... if rule == cos: + ... return cos(pi/2 - x, evaluate=False) + ... if rule == sqrt: + ... return sqrt(1 - cos(x)**2) + >>> MySin(MySin(x)).rewrite(cos) + cos(-cos(-x + pi/2) + pi/2) + >>> MySin(x).rewrite(sqrt) + sqrt(1 - cos(x)**2) + + Defining ``_eval_rewrite_as_[...]()`` method is supported for backwards + compatibility reason. This may be removed in the future and using it is + discouraged. + + >>> class MySin(Expr): + ... def _eval_rewrite_as_cos(self, *args, **hints): + ... x, = args + ... return cos(pi/2 - x, evaluate=False) + >>> MySin(x).rewrite(cos) + cos(-x + pi/2) + + """ + if not args: + return self + + hints.update(deep=deep) + + pattern = args[:-1] + rule = args[-1] + + # support old design by _eval_rewrite_as_[...] method + if isinstance(rule, str): + method = "_eval_rewrite_as_%s" % rule + elif hasattr(rule, "__name__"): + # rule is class or function + clsname = rule.__name__ + method = "_eval_rewrite_as_%s" % clsname + else: + # rule is instance + clsname = rule.__class__.__name__ + method = "_eval_rewrite_as_%s" % clsname + + if pattern: + if iterable(pattern[0]): + pattern = pattern[0] + pattern = tuple(p for p in pattern if self.has(p)) + if not pattern: + return self + # hereafter, empty pattern is interpreted as all pattern. + + return self._rewrite(pattern, rule, method, **hints) + + def _rewrite(self, pattern, rule, method, **hints): + deep = hints.pop('deep', True) + if deep: + args = [a._rewrite(pattern, rule, method, **hints) + for a in self.args] + else: + args = self.args + if not pattern or any(isinstance(self, p) for p in pattern): + meth = getattr(self, method, None) + if meth is not None: + rewritten = meth(*args, **hints) + else: + rewritten = self._eval_rewrite(rule, args, **hints) + if rewritten is not None: + return rewritten + if not args: + return self + return self.func(*args) + + def _eval_rewrite(self, rule, args, **hints): + return None + + _constructor_postprocessor_mapping = {} # type: ignore + + @classmethod + def _exec_constructor_postprocessors(cls, obj): + # WARNING: This API is experimental. + + # This is an experimental API that introduces constructor + # postprosessors for SymPy Core elements. If an argument of a SymPy + # expression has a `_constructor_postprocessor_mapping` attribute, it will + # be interpreted as a dictionary containing lists of postprocessing + # functions for matching expression node names. + + clsname = obj.__class__.__name__ + postprocessors = defaultdict(list) + for i in obj.args: + try: + postprocessor_mappings = ( + Basic._constructor_postprocessor_mapping[cls].items() + for cls in type(i).mro() + if cls in Basic._constructor_postprocessor_mapping + ) + for k, v in chain.from_iterable(postprocessor_mappings): + postprocessors[k].extend([j for j in v if j not in postprocessors[k]]) + except TypeError: + pass + + for f in postprocessors.get(clsname, []): + obj = f(obj) + + return obj + + def _sage_(self): + """ + Convert *self* to a symbolic expression of SageMath. + + This version of the method is merely a placeholder. + """ + old_method = self._sage_ + from sage.interfaces.sympy import sympy_init + sympy_init() # may monkey-patch _sage_ method into self's class or superclasses + if old_method == self._sage_: + raise NotImplementedError('conversion to SageMath is not implemented') + else: + # call the freshly monkey-patched method + return self._sage_() + + def could_extract_minus_sign(self): + return False # see Expr.could_extract_minus_sign + + def is_same(a, b, approx=None): + """Return True if a and b are structurally the same, else False. + If `approx` is supplied, it will be used to test whether two + numbers are the same or not. By default, only numbers of the + same type will compare equal, so S.Half != Float(0.5). + + Examples + ======== + + In SymPy (unlike Python) two numbers do not compare the same if they are + not of the same type: + + >>> from sympy import S + >>> 2.0 == S(2) + False + >>> 0.5 == S.Half + False + + By supplying a function with which to compare two numbers, such + differences can be ignored. e.g. `equal_valued` will return True + for decimal numbers having a denominator that is a power of 2, + regardless of precision. + + >>> from sympy import Float + >>> from sympy.core.numbers import equal_valued + >>> (S.Half/4).is_same(Float(0.125, 1), equal_valued) + True + >>> Float(1, 2).is_same(Float(1, 10), equal_valued) + True + + But decimals without a power of 2 denominator will compare + as not being the same. + + >>> Float(0.1, 9).is_same(Float(0.1, 10), equal_valued) + False + + But arbitrary differences can be ignored by supplying a function + to test the equivalence of two numbers: + + >>> import math + >>> Float(0.1, 9).is_same(Float(0.1, 10), math.isclose) + True + + Other objects might compare the same even though types are not the + same. This routine will only return True if two expressions are + identical in terms of class types. + + >>> from sympy import eye, Basic + >>> eye(1) == S(eye(1)) # mutable vs immutable + True + >>> Basic.is_same(eye(1), S(eye(1))) + False + + """ + from .numbers import Number + from .traversal import postorder_traversal as pot + for t in zip_longest(pot(a), pot(b)): + if None in t: + return False + a, b = t + if isinstance(a, Number): + if not isinstance(b, Number): + return False + if approx: + return approx(a, b) + if not (a == b and a.__class__ == b.__class__): + return False + return True + +_aresame = Basic.is_same # for sake of others importing this + +# key used by Mul and Add to make canonical args +_args_sortkey = cmp_to_key(Basic.compare) + +# For all Basic subclasses _prepare_class_assumptions is called by +# Basic.__init_subclass__ but that method is not called for Basic itself so we +# call the function here instead. +_prepare_class_assumptions(Basic) + + +class Atom(Basic): + """ + A parent class for atomic things. An atom is an expression with no subexpressions. + + Examples + ======== + + Symbol, Number, Rational, Integer, ... + But not: Add, Mul, Pow, ... + """ + + is_Atom = True + + __slots__ = () + + def matches(self, expr, repl_dict=None, old=False): + if self == expr: + if repl_dict is None: + return {} + return repl_dict.copy() + + def xreplace(self, rule, hack2=False): + return rule.get(self, self) + + def doit(self, **hints): + return self + + @classmethod + def class_key(cls): + return 2, 0, cls.__name__ + + @cacheit + def sort_key(self, order=None): + return self.class_key(), (1, (str(self),)), S.One.sort_key(), S.One + + def _eval_simplify(self, **kwargs): + return self + + @property + def _sorted_args(self): + # this is here as a safeguard against accidentally using _sorted_args + # on Atoms -- they cannot be rebuilt as atom.func(*atom._sorted_args) + # since there are no args. So the calling routine should be checking + # to see that this property is not called for Atoms. + raise AttributeError('Atoms have no args. It might be necessary' + ' to make a check for Atoms in the calling code.') + + +def _atomic(e, recursive=False): + """Return atom-like quantities as far as substitution is + concerned: Derivatives, Functions and Symbols. Do not + return any 'atoms' that are inside such quantities unless + they also appear outside, too, unless `recursive` is True. + + Examples + ======== + + >>> from sympy import Derivative, Function, cos + >>> from sympy.abc import x, y + >>> from sympy.core.basic import _atomic + >>> f = Function('f') + >>> _atomic(x + y) + {x, y} + >>> _atomic(x + f(y)) + {x, f(y)} + >>> _atomic(Derivative(f(x), x) + cos(x) + y) + {y, cos(x), Derivative(f(x), x)} + + """ + pot = _preorder_traversal(e) + seen = set() + if isinstance(e, Basic): + free = getattr(e, "free_symbols", None) + if free is None: + return {e} + else: + return set() + from .symbol import Symbol + from .function import Derivative, Function + atoms = set() + for p in pot: + if p in seen: + pot.skip() + continue + seen.add(p) + if isinstance(p, Symbol) and p in free: + atoms.add(p) + elif isinstance(p, (Derivative, Function)): + if not recursive: + pot.skip() + atoms.add(p) + return atoms + + +def _make_find_query(query): + """Convert the argument of Basic.find() into a callable""" + try: + query = _sympify(query) + except SympifyError: + pass + if isinstance(query, type): + return lambda expr: isinstance(expr, query) + elif isinstance(query, Basic): + return lambda expr: expr.match(query) is not None + return query + +# Delayed to avoid cyclic import +from .singleton import S +from .traversal import (preorder_traversal as _preorder_traversal, + iterargs, iterfreeargs) + +preorder_traversal = deprecated( + """ + Using preorder_traversal from the sympy.core.basic submodule is + deprecated. + + Instead, use preorder_traversal from the top-level sympy namespace, like + + sympy.preorder_traversal + """, + deprecated_since_version="1.10", + active_deprecations_target="deprecated-traversal-functions-moved", +)(_preorder_traversal) diff --git a/lib/python3.12/site-packages/sympy/core/cache.py b/lib/python3.12/site-packages/sympy/core/cache.py new file mode 100644 index 0000000000000000000000000000000000000000..ec11600a5e40ad446a6e5dde8820d46ea915b06a --- /dev/null +++ b/lib/python3.12/site-packages/sympy/core/cache.py @@ -0,0 +1,210 @@ +""" Caching facility for SymPy """ +from importlib import import_module +from typing import Callable + +class _cache(list): + """ List of cached functions """ + + def print_cache(self): + """print cache info""" + + for item in self: + name = item.__name__ + myfunc = item + while hasattr(myfunc, '__wrapped__'): + if hasattr(myfunc, 'cache_info'): + info = myfunc.cache_info() + break + else: + myfunc = myfunc.__wrapped__ + else: + info = None + + print(name, info) + + def clear_cache(self): + """clear cache content""" + for item in self: + myfunc = item + while hasattr(myfunc, '__wrapped__'): + if hasattr(myfunc, 'cache_clear'): + myfunc.cache_clear() + break + else: + myfunc = myfunc.__wrapped__ + + +# global cache registry: +CACHE = _cache() +# make clear and print methods available +print_cache = CACHE.print_cache +clear_cache = CACHE.clear_cache + +from functools import lru_cache, wraps + +def __cacheit(maxsize): + """caching decorator. + + important: the result of cached function must be *immutable* + + + Examples + ======== + + >>> from sympy import cacheit + >>> @cacheit + ... def f(a, b): + ... return a+b + + >>> @cacheit + ... def f(a, b): # noqa: F811 + ... return [a, b] # <-- WRONG, returns mutable object + + to force cacheit to check returned results mutability and consistency, + set environment variable SYMPY_USE_CACHE to 'debug' + """ + def func_wrapper(func): + cfunc = lru_cache(maxsize, typed=True)(func) + + @wraps(func) + def wrapper(*args, **kwargs): + try: + retval = cfunc(*args, **kwargs) + except TypeError as e: + if not e.args or not e.args[0].startswith('unhashable type:'): + raise + retval = func(*args, **kwargs) + return retval + + wrapper.cache_info = cfunc.cache_info + wrapper.cache_clear = cfunc.cache_clear + + CACHE.append(wrapper) + return wrapper + + return func_wrapper +######################################## + + +def __cacheit_nocache(func): + return func + + +def __cacheit_debug(maxsize): + """cacheit + code to check cache consistency""" + def func_wrapper(func): + cfunc = __cacheit(maxsize)(func) + + @wraps(func) + def wrapper(*args, **kw_args): + # always call function itself and compare it with cached version + r1 = func(*args, **kw_args) + r2 = cfunc(*args, **kw_args) + + # try to see if the result is immutable + # + # this works because: + # + # hash([1,2,3]) -> raise TypeError + # hash({'a':1, 'b':2}) -> raise TypeError + # hash((1,[2,3])) -> raise TypeError + # + # hash((1,2,3)) -> just computes the hash + hash(r1), hash(r2) + + # also see if returned values are the same + if r1 != r2: + raise RuntimeError("Returned values are not the same") + return r1 + return wrapper + return func_wrapper + + +def _getenv(key, default=None): + from os import getenv + return getenv(key, default) + +# SYMPY_USE_CACHE=yes/no/debug +USE_CACHE = _getenv('SYMPY_USE_CACHE', 'yes').lower() +# SYMPY_CACHE_SIZE=some_integer/None +# special cases : +# SYMPY_CACHE_SIZE=0 -> No caching +# SYMPY_CACHE_SIZE=None -> Unbounded caching +scs = _getenv('SYMPY_CACHE_SIZE', '1000') +if scs.lower() == 'none': + SYMPY_CACHE_SIZE = None +else: + try: + SYMPY_CACHE_SIZE = int(scs) + except ValueError: + raise RuntimeError( + 'SYMPY_CACHE_SIZE must be a valid integer or None. ' + \ + 'Got: %s' % SYMPY_CACHE_SIZE) + +if USE_CACHE == 'no': + cacheit = __cacheit_nocache +elif USE_CACHE == 'yes': + cacheit = __cacheit(SYMPY_CACHE_SIZE) +elif USE_CACHE == 'debug': + cacheit = __cacheit_debug(SYMPY_CACHE_SIZE) # a lot slower +else: + raise RuntimeError( + 'unrecognized value for SYMPY_USE_CACHE: %s' % USE_CACHE) + + +def cached_property(func): + '''Decorator to cache property method''' + attrname = '__' + func.__name__ + _cached_property_sentinel = object() + def propfunc(self): + val = getattr(self, attrname, _cached_property_sentinel) + if val is _cached_property_sentinel: + val = func(self) + setattr(self, attrname, val) + return val + return property(propfunc) + + +def lazy_function(module : str, name : str) -> Callable: + """Create a lazy proxy for a function in a module. + + The module containing the function is not imported until the function is used. + + """ + func = None + + def _get_function(): + nonlocal func + if func is None: + func = getattr(import_module(module), name) + return func + + # The metaclass is needed so that help() shows the docstring + class LazyFunctionMeta(type): + @property + def __doc__(self): + docstring = _get_function().__doc__ + docstring += f"\n\nNote: this is a {self.__class__.__name__} wrapper of '{module}.{name}'" + return docstring + + class LazyFunction(metaclass=LazyFunctionMeta): + def __call__(self, *args, **kwargs): + # inline get of function for performance gh-23832 + nonlocal func + if func is None: + func = getattr(import_module(module), name) + return func(*args, **kwargs) + + @property + def __doc__(self): + docstring = _get_function().__doc__ + docstring += f"\n\nNote: this is a {self.__class__.__name__} wrapper of '{module}.{name}'" + return docstring + + def __str__(self): + return _get_function().__str__() + + def __repr__(self): + return f"<{__class__.__name__} object at 0x{id(self):x}>: wrapping '{module}.{name}'" + + return LazyFunction() diff --git a/lib/python3.12/site-packages/sympy/core/compatibility.py b/lib/python3.12/site-packages/sympy/core/compatibility.py new file mode 100644 index 0000000000000000000000000000000000000000..637a2698dbb39a042d3d664404bb0a4cba7fd004 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/core/compatibility.py @@ -0,0 +1,35 @@ +""" +.. deprecated:: 1.10 + + ``sympy.core.compatibility`` is deprecated. See + :ref:`sympy-core-compatibility`. + +Reimplementations of constructs introduced in later versions of Python than +we support. Also some functions that are needed SymPy-wide and are located +here for easy import. + +""" + + +from sympy.utilities.exceptions import sympy_deprecation_warning + +sympy_deprecation_warning(""" +The sympy.core.compatibility submodule is deprecated. + +This module was only ever intended for internal use. Some of the functions +that were in this module are available from the top-level SymPy namespace, +i.e., + + from sympy import ordered, default_sort_key + +The remaining were only intended for internal SymPy use and should not be used +by user code. +""", + deprecated_since_version="1.10", + active_deprecations_target="deprecated-sympy-core-compatibility", + ) + + +from .sorting import ordered, _nodes, default_sort_key # noqa:F401 +from sympy.utilities.misc import as_int as _as_int # noqa:F401 +from sympy.utilities.iterables import iterable, is_sequence, NotIterable # noqa:F401 diff --git a/lib/python3.12/site-packages/sympy/core/containers.py b/lib/python3.12/site-packages/sympy/core/containers.py new file mode 100644 index 0000000000000000000000000000000000000000..bb03b61869f6929552e614d3fa6b2bfc272f270c --- /dev/null +++ b/lib/python3.12/site-packages/sympy/core/containers.py @@ -0,0 +1,410 @@ +"""Module for SymPy containers + + (SymPy objects that store other SymPy objects) + + The containers implemented in this module are subclassed to Basic. + They are supposed to work seamlessly within the SymPy framework. +""" + +from collections import OrderedDict +from collections.abc import MutableSet +from typing import Any, Callable + +from .basic import Basic +from .sorting import default_sort_key, ordered +from .sympify import _sympify, sympify, _sympy_converter, SympifyError +from sympy.core.kind import Kind +from sympy.utilities.iterables import iterable +from sympy.utilities.misc import as_int + + +class Tuple(Basic): + """ + Wrapper around the builtin tuple object. + + Explanation + =========== + + The Tuple is a subclass of Basic, so that it works well in the + SymPy framework. The wrapped tuple is available as self.args, but + you can also access elements or slices with [:] syntax. + + Parameters + ========== + + sympify : bool + If ``False``, ``sympify`` is not called on ``args``. This + can be used for speedups for very large tuples where the + elements are known to already be SymPy objects. + + Examples + ======== + + >>> from sympy import Tuple, symbols + >>> a, b, c, d = symbols('a b c d') + >>> Tuple(a, b, c)[1:] + (b, c) + >>> Tuple(a, b, c).subs(a, d) + (d, b, c) + + """ + + def __new__(cls, *args, **kwargs): + if kwargs.get('sympify', True): + args = (sympify(arg) for arg in args) + obj = Basic.__new__(cls, *args) + return obj + + def __getitem__(self, i): + if isinstance(i, slice): + indices = i.indices(len(self)) + return Tuple(*(self.args[j] for j in range(*indices))) + return self.args[i] + + def __len__(self): + return len(self.args) + + def __contains__(self, item): + return item in self.args + + def __iter__(self): + return iter(self.args) + + def __add__(self, other): + if isinstance(other, Tuple): + return Tuple(*(self.args + other.args)) + elif isinstance(other, tuple): + return Tuple(*(self.args + other)) + else: + return NotImplemented + + def __radd__(self, other): + if isinstance(other, Tuple): + return Tuple(*(other.args + self.args)) + elif isinstance(other, tuple): + return Tuple(*(other + self.args)) + else: + return NotImplemented + + def __mul__(self, other): + try: + n = as_int(other) + except ValueError: + raise TypeError("Can't multiply sequence by non-integer of type '%s'" % type(other)) + return self.func(*(self.args*n)) + + __rmul__ = __mul__ + + def __eq__(self, other): + if isinstance(other, Basic): + return super().__eq__(other) + return self.args == other + + def __ne__(self, other): + if isinstance(other, Basic): + return super().__ne__(other) + return self.args != other + + def __hash__(self): + return hash(self.args) + + def _to_mpmath(self, prec): + return tuple(a._to_mpmath(prec) for a in self.args) + + def __lt__(self, other): + return _sympify(self.args < other.args) + + def __le__(self, other): + return _sympify(self.args <= other.args) + + # XXX: Basic defines count() as something different, so we can't + # redefine it here. Originally this lead to cse() test failure. + def tuple_count(self, value) -> int: + """Return number of occurrences of value.""" + return self.args.count(value) + + def index(self, value, start=None, stop=None): + """Searches and returns the first index of the value.""" + # XXX: One would expect: + # + # return self.args.index(value, start, stop) + # + # here. Any trouble with that? Yes: + # + # >>> (1,).index(1, None, None) + # Traceback (most recent call last): + # File "", line 1, in + # TypeError: slice indices must be integers or None or have an __index__ method + # + # See: http://bugs.python.org/issue13340 + + if start is None and stop is None: + return self.args.index(value) + elif stop is None: + return self.args.index(value, start) + else: + return self.args.index(value, start, stop) + + @property + def kind(self): + """ + The kind of a Tuple instance. + + The kind of a Tuple is always of :class:`TupleKind` but + parametrised by the number of elements and the kind of each element. + + Examples + ======== + + >>> from sympy import Tuple, Matrix + >>> Tuple(1, 2).kind + TupleKind(NumberKind, NumberKind) + >>> Tuple(Matrix([1, 2]), 1).kind + TupleKind(MatrixKind(NumberKind), NumberKind) + >>> Tuple(1, 2).kind.element_kind + (NumberKind, NumberKind) + + See Also + ======== + + sympy.matrices.kind.MatrixKind + sympy.core.kind.NumberKind + """ + return TupleKind(*(i.kind for i in self.args)) + +_sympy_converter[tuple] = lambda tup: Tuple(*tup) + + + + + +def tuple_wrapper(method): + """ + Decorator that converts any tuple in the function arguments into a Tuple. + + Explanation + =========== + + The motivation for this is to provide simple user interfaces. The user can + call a function with regular tuples in the argument, and the wrapper will + convert them to Tuples before handing them to the function. + + Explanation + =========== + + >>> from sympy.core.containers import tuple_wrapper + >>> def f(*args): + ... return args + >>> g = tuple_wrapper(f) + + The decorated function g sees only the Tuple argument: + + >>> g(0, (1, 2), 3) + (0, (1, 2), 3) + + """ + def wrap_tuples(*args, **kw_args): + newargs = [] + for arg in args: + if isinstance(arg, tuple): + newargs.append(Tuple(*arg)) + else: + newargs.append(arg) + return method(*newargs, **kw_args) + return wrap_tuples + + +class Dict(Basic): + """ + Wrapper around the builtin dict object. + + Explanation + =========== + + The Dict is a subclass of Basic, so that it works well in the + SymPy framework. Because it is immutable, it may be included + in sets, but its values must all be given at instantiation and + cannot be changed afterwards. Otherwise it behaves identically + to the Python dict. + + Examples + ======== + + >>> from sympy import Dict, Symbol + + >>> D = Dict({1: 'one', 2: 'two'}) + >>> for key in D: + ... if key == 1: + ... print('%s %s' % (key, D[key])) + 1 one + + The args are sympified so the 1 and 2 are Integers and the values + are Symbols. Queries automatically sympify args so the following work: + + >>> 1 in D + True + >>> D.has(Symbol('one')) # searches keys and values + True + >>> 'one' in D # not in the keys + False + >>> D[1] + one + + """ + + def __new__(cls, *args): + if len(args) == 1 and isinstance(args[0], (dict, Dict)): + items = [Tuple(k, v) for k, v in args[0].items()] + elif iterable(args) and all(len(arg) == 2 for arg in args): + items = [Tuple(k, v) for k, v in args] + else: + raise TypeError('Pass Dict args as Dict((k1, v1), ...) or Dict({k1: v1, ...})') + elements = frozenset(items) + obj = Basic.__new__(cls, *ordered(items)) + obj.elements = elements + obj._dict = dict(items) # In case Tuple decides it wants to sympify + return obj + + def __getitem__(self, key): + """x.__getitem__(y) <==> x[y]""" + try: + key = _sympify(key) + except SympifyError: + raise KeyError(key) + + return self._dict[key] + + def __setitem__(self, key, value): + raise NotImplementedError("SymPy Dicts are Immutable") + + def items(self): + '''Returns a set-like object providing a view on dict's items. + ''' + return self._dict.items() + + def keys(self): + '''Returns the list of the dict's keys.''' + return self._dict.keys() + + def values(self): + '''Returns the list of the dict's values.''' + return self._dict.values() + + def __iter__(self): + '''x.__iter__() <==> iter(x)''' + return iter(self._dict) + + def __len__(self): + '''x.__len__() <==> len(x)''' + return self._dict.__len__() + + def get(self, key, default=None): + '''Returns the value for key if the key is in the dictionary.''' + try: + key = _sympify(key) + except SympifyError: + return default + return self._dict.get(key, default) + + def __contains__(self, key): + '''D.__contains__(k) -> True if D has a key k, else False''' + try: + key = _sympify(key) + except SympifyError: + return False + return key in self._dict + + def __lt__(self, other): + return _sympify(self.args < other.args) + + @property + def _sorted_args(self): + return tuple(sorted(self.args, key=default_sort_key)) + + def __eq__(self, other): + if isinstance(other, dict): + return self == Dict(other) + return super().__eq__(other) + + __hash__ : Callable[[Basic], Any] = Basic.__hash__ + +# this handles dict, defaultdict, OrderedDict +_sympy_converter[dict] = lambda d: Dict(*d.items()) + +class OrderedSet(MutableSet): + def __init__(self, iterable=None): + if iterable: + self.map = OrderedDict((item, None) for item in iterable) + else: + self.map = OrderedDict() + + def __len__(self): + return len(self.map) + + def __contains__(self, key): + return key in self.map + + def add(self, key): + self.map[key] = None + + def discard(self, key): + self.map.pop(key) + + def pop(self, last=True): + return self.map.popitem(last=last)[0] + + def __iter__(self): + yield from self.map.keys() + + def __repr__(self): + if not self.map: + return '%s()' % (self.__class__.__name__,) + return '%s(%r)' % (self.__class__.__name__, list(self.map.keys())) + + def intersection(self, other): + return self.__class__([val for val in self if val in other]) + + def difference(self, other): + return self.__class__([val for val in self if val not in other]) + + def update(self, iterable): + for val in iterable: + self.add(val) + +class TupleKind(Kind): + """ + TupleKind is a subclass of Kind, which is used to define Kind of ``Tuple``. + + Parameters of TupleKind will be kinds of all the arguments in Tuples, for + example + + Parameters + ========== + + args : tuple(element_kind) + element_kind is kind of element. + args is tuple of kinds of element + + Examples + ======== + + >>> from sympy import Tuple + >>> Tuple(1, 2).kind + TupleKind(NumberKind, NumberKind) + >>> Tuple(1, 2).kind.element_kind + (NumberKind, NumberKind) + + See Also + ======== + + sympy.core.kind.NumberKind + MatrixKind + sympy.sets.sets.SetKind + """ + def __new__(cls, *args): + obj = super().__new__(cls, *args) + obj.element_kind = args + return obj + + def __repr__(self): + return "TupleKind{}".format(self.element_kind) diff --git a/lib/python3.12/site-packages/sympy/core/core.py b/lib/python3.12/site-packages/sympy/core/core.py new file mode 100644 index 0000000000000000000000000000000000000000..8a45bb06919d7a8ef88e2c9958decac705c0b8ee --- /dev/null +++ b/lib/python3.12/site-packages/sympy/core/core.py @@ -0,0 +1,21 @@ +""" The core's core. """ +from __future__ import annotations + + +class Registry: + """ + Base class for registry objects. + + Registries map a name to an object using attribute notation. Registry + classes behave singletonically: all their instances share the same state, + which is stored in the class object. + + All subclasses should set `__slots__ = ()`. + """ + __slots__ = () + + def __setattr__(self, name, obj): + setattr(self.__class__, name, obj) + + def __delattr__(self, name): + delattr(self.__class__, name) diff --git a/lib/python3.12/site-packages/sympy/core/coreerrors.py b/lib/python3.12/site-packages/sympy/core/coreerrors.py new file mode 100644 index 0000000000000000000000000000000000000000..e1af0729533a7ce2ae2a4564d225a03eaed6e333 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/core/coreerrors.py @@ -0,0 +1,9 @@ +"""Definitions of common exceptions for :mod:`sympy.core` module. """ + + +class BaseCoreError(Exception): + """Base class for core related exceptions. """ + + +class NonCommutativeExpression(BaseCoreError): + """Raised when expression didn't have commutative property. """ diff --git a/lib/python3.12/site-packages/sympy/core/decorators.py b/lib/python3.12/site-packages/sympy/core/decorators.py new file mode 100644 index 0000000000000000000000000000000000000000..1da77d2be6b8351ad16ef142f7add51f316e58ef --- /dev/null +++ b/lib/python3.12/site-packages/sympy/core/decorators.py @@ -0,0 +1,238 @@ +""" +SymPy core decorators. + +The purpose of this module is to expose decorators without any other +dependencies, so that they can be easily imported anywhere in sympy/core. +""" + +from functools import wraps +from .sympify import SympifyError, sympify + + +def _sympifyit(arg, retval=None): + """ + decorator to smartly _sympify function arguments + + Explanation + =========== + + @_sympifyit('other', NotImplemented) + def add(self, other): + ... + + In add, other can be thought of as already being a SymPy object. + + If it is not, the code is likely to catch an exception, then other will + be explicitly _sympified, and the whole code restarted. + + if _sympify(arg) fails, NotImplemented will be returned + + See also + ======== + + __sympifyit + """ + def deco(func): + return __sympifyit(func, arg, retval) + + return deco + + +def __sympifyit(func, arg, retval=None): + """Decorator to _sympify `arg` argument for function `func`. + + Do not use directly -- use _sympifyit instead. + """ + + # we support f(a,b) only + if not func.__code__.co_argcount: + raise LookupError("func not found") + # only b is _sympified + assert func.__code__.co_varnames[1] == arg + if retval is None: + @wraps(func) + def __sympifyit_wrapper(a, b): + return func(a, sympify(b, strict=True)) + + else: + @wraps(func) + def __sympifyit_wrapper(a, b): + try: + # If an external class has _op_priority, it knows how to deal + # with SymPy objects. Otherwise, it must be converted. + if not hasattr(b, '_op_priority'): + b = sympify(b, strict=True) + return func(a, b) + except SympifyError: + return retval + + return __sympifyit_wrapper + + +def call_highest_priority(method_name): + """A decorator for binary special methods to handle _op_priority. + + Explanation + =========== + + Binary special methods in Expr and its subclasses use a special attribute + '_op_priority' to determine whose special method will be called to + handle the operation. In general, the object having the highest value of + '_op_priority' will handle the operation. Expr and subclasses that define + custom binary special methods (__mul__, etc.) should decorate those + methods with this decorator to add the priority logic. + + The ``method_name`` argument is the name of the method of the other class + that will be called. Use this decorator in the following manner:: + + # Call other.__rmul__ if other._op_priority > self._op_priority + @call_highest_priority('__rmul__') + def __mul__(self, other): + ... + + # Call other.__mul__ if other._op_priority > self._op_priority + @call_highest_priority('__mul__') + def __rmul__(self, other): + ... + """ + def priority_decorator(func): + @wraps(func) + def binary_op_wrapper(self, other): + if hasattr(other, '_op_priority'): + if other._op_priority > self._op_priority: + f = getattr(other, method_name, None) + if f is not None: + return f(self) + return func(self, other) + return binary_op_wrapper + return priority_decorator + + +def sympify_method_args(cls): + '''Decorator for a class with methods that sympify arguments. + + Explanation + =========== + + The sympify_method_args decorator is to be used with the sympify_return + decorator for automatic sympification of method arguments. This is + intended for the common idiom of writing a class like : + + Examples + ======== + + >>> from sympy import Basic, SympifyError, S + >>> from sympy.core.sympify import _sympify + + >>> class MyTuple(Basic): + ... def __add__(self, other): + ... try: + ... other = _sympify(other) + ... except SympifyError: + ... return NotImplemented + ... if not isinstance(other, MyTuple): + ... return NotImplemented + ... return MyTuple(*(self.args + other.args)) + + >>> MyTuple(S(1), S(2)) + MyTuple(S(3), S(4)) + MyTuple(1, 2, 3, 4) + + In the above it is important that we return NotImplemented when other is + not sympifiable and also when the sympified result is not of the expected + type. This allows the MyTuple class to be used cooperatively with other + classes that overload __add__ and want to do something else in combination + with instance of Tuple. + + Using this decorator the above can be written as + + >>> from sympy.core.decorators import sympify_method_args, sympify_return + + >>> @sympify_method_args + ... class MyTuple(Basic): + ... @sympify_return([('other', 'MyTuple')], NotImplemented) + ... def __add__(self, other): + ... return MyTuple(*(self.args + other.args)) + + >>> MyTuple(S(1), S(2)) + MyTuple(S(3), S(4)) + MyTuple(1, 2, 3, 4) + + The idea here is that the decorators take care of the boiler-plate code + for making this happen in each method that potentially needs to accept + unsympified arguments. Then the body of e.g. the __add__ method can be + written without needing to worry about calling _sympify or checking the + type of the resulting object. + + The parameters for sympify_return are a list of tuples of the form + (parameter_name, expected_type) and the value to return (e.g. + NotImplemented). The expected_type parameter can be a type e.g. Tuple or a + string 'Tuple'. Using a string is useful for specifying a Type within its + class body (as in the above example). + + Notes: Currently sympify_return only works for methods that take a single + argument (not including self). Specifying an expected_type as a string + only works for the class in which the method is defined. + ''' + # Extract the wrapped methods from each of the wrapper objects created by + # the sympify_return decorator. Doing this here allows us to provide the + # cls argument which is used for forward string referencing. + for attrname, obj in cls.__dict__.items(): + if isinstance(obj, _SympifyWrapper): + setattr(cls, attrname, obj.make_wrapped(cls)) + return cls + + +def sympify_return(*args): + '''Function/method decorator to sympify arguments automatically + + See the docstring of sympify_method_args for explanation. + ''' + # Store a wrapper object for the decorated method + def wrapper(func): + return _SympifyWrapper(func, args) + return wrapper + + +class _SympifyWrapper: + '''Internal class used by sympify_return and sympify_method_args''' + + def __init__(self, func, args): + self.func = func + self.args = args + + def make_wrapped(self, cls): + func = self.func + parameters, retval = self.args + + # XXX: Handle more than one parameter? + [(parameter, expectedcls)] = parameters + + # Handle forward references to the current class using strings + if expectedcls == cls.__name__: + expectedcls = cls + + # Raise RuntimeError since this is a failure at import time and should + # not be recoverable. + nargs = func.__code__.co_argcount + # we support f(a, b) only + if nargs != 2: + raise RuntimeError('sympify_return can only be used with 2 argument functions') + # only b is _sympified + if func.__code__.co_varnames[1] != parameter: + raise RuntimeError('parameter name mismatch "%s" in %s' % + (parameter, func.__name__)) + + @wraps(func) + def _func(self, other): + # XXX: The check for _op_priority here should be removed. It is + # needed to stop mutable matrices from being sympified to + # immutable matrices which breaks things in quantum... + if not hasattr(other, '_op_priority'): + try: + other = sympify(other, strict=True) + except SympifyError: + return retval + if not isinstance(other, expectedcls): + return retval + return func(self, other) + + return _func diff --git a/lib/python3.12/site-packages/sympy/core/evalf.py b/lib/python3.12/site-packages/sympy/core/evalf.py new file mode 100644 index 0000000000000000000000000000000000000000..c5337c446be737f079c6dccf7cef08d720c0bde7 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/core/evalf.py @@ -0,0 +1,1802 @@ +""" +Adaptive numerical evaluation of SymPy expressions, using mpmath +for mathematical functions. +""" +from __future__ import annotations +from typing import Tuple as tTuple, Optional, Union as tUnion, Callable, List, Dict as tDict, Type, TYPE_CHECKING, \ + Any, overload + +import math + +import mpmath.libmp as libmp +from mpmath import ( + make_mpc, make_mpf, mp, mpc, mpf, nsum, quadts, quadosc, workprec) +from mpmath import inf as mpmath_inf +from mpmath.libmp import (from_int, from_man_exp, from_rational, fhalf, + fnan, finf, fninf, fnone, fone, fzero, mpf_abs, mpf_add, + mpf_atan, mpf_atan2, mpf_cmp, mpf_cos, mpf_e, mpf_exp, mpf_log, mpf_lt, + mpf_mul, mpf_neg, mpf_pi, mpf_pow, mpf_pow_int, mpf_shift, mpf_sin, + mpf_sqrt, normalize, round_nearest, to_int, to_str) +from mpmath.libmp import bitcount as mpmath_bitcount +from mpmath.libmp.backend import MPZ +from mpmath.libmp.libmpc import _infs_nan +from mpmath.libmp.libmpf import dps_to_prec, prec_to_dps + +from .sympify import sympify +from .singleton import S +from sympy.external.gmpy import SYMPY_INTS +from sympy.utilities.iterables import is_sequence +from sympy.utilities.lambdify import lambdify +from sympy.utilities.misc import as_int + +if TYPE_CHECKING: + from sympy.core.expr import Expr + from sympy.core.add import Add + from sympy.core.mul import Mul + from sympy.core.power import Pow + from sympy.core.symbol import Symbol + from sympy.integrals.integrals import Integral + from sympy.concrete.summations import Sum + from sympy.concrete.products import Product + from sympy.functions.elementary.exponential import exp, log + from sympy.functions.elementary.complexes import Abs, re, im + from sympy.functions.elementary.integers import ceiling, floor + from sympy.functions.elementary.trigonometric import atan + from .numbers import Float, Rational, Integer, AlgebraicNumber, Number + +LG10 = math.log2(10) +rnd = round_nearest + + +def bitcount(n): + """Return smallest integer, b, such that |n|/2**b < 1. + """ + return mpmath_bitcount(abs(int(n))) + +# Used in a few places as placeholder values to denote exponents and +# precision levels, e.g. of exact numbers. Must be careful to avoid +# passing these to mpmath functions or returning them in final results. +INF = float(mpmath_inf) +MINUS_INF = float(-mpmath_inf) + +# ~= 100 digits. Real men set this to INF. +DEFAULT_MAXPREC = 333 + + +class PrecisionExhausted(ArithmeticError): + pass + +#----------------------------------------------------------------------------# +# # +# Helper functions for arithmetic and complex parts # +# # +#----------------------------------------------------------------------------# + +""" +An mpf value tuple is a tuple of integers (sign, man, exp, bc) +representing a floating-point number: [1, -1][sign]*man*2**exp where +sign is 0 or 1 and bc should correspond to the number of bits used to +represent the mantissa (man) in binary notation, e.g. +""" +MPF_TUP = tTuple[int, int, int, int] # mpf value tuple + +""" +Explanation +=========== + +>>> from sympy.core.evalf import bitcount +>>> sign, man, exp, bc = 0, 5, 1, 3 +>>> n = [1, -1][sign]*man*2**exp +>>> n, bitcount(man) +(10, 3) + +A temporary result is a tuple (re, im, re_acc, im_acc) where +re and im are nonzero mpf value tuples representing approximate +numbers, or None to denote exact zeros. + +re_acc, im_acc are integers denoting log2(e) where e is the estimated +relative accuracy of the respective complex part, but may be anything +if the corresponding complex part is None. + +""" +TMP_RES = Any # temporary result, should be some variant of +# tUnion[tTuple[Optional[MPF_TUP], Optional[MPF_TUP], +# Optional[int], Optional[int]], +# 'ComplexInfinity'] +# but mypy reports error because it doesn't know as we know +# 1. re and re_acc are either both None or both MPF_TUP +# 2. sometimes the result can't be zoo + +# type of the "options" parameter in internal evalf functions +OPT_DICT = tDict[str, Any] + + +def fastlog(x: Optional[MPF_TUP]) -> tUnion[int, Any]: + """Fast approximation of log2(x) for an mpf value tuple x. + + Explanation + =========== + + Calculated as exponent + width of mantissa. This is an + approximation for two reasons: 1) it gives the ceil(log2(abs(x))) + value and 2) it is too high by 1 in the case that x is an exact + power of 2. Although this is easy to remedy by testing to see if + the odd mpf mantissa is 1 (indicating that one was dealing with + an exact power of 2) that would decrease the speed and is not + necessary as this is only being used as an approximation for the + number of bits in x. The correct return value could be written as + "x[2] + (x[3] if x[1] != 1 else 0)". + Since mpf tuples always have an odd mantissa, no check is done + to see if the mantissa is a multiple of 2 (in which case the + result would be too large by 1). + + Examples + ======== + + >>> from sympy import log + >>> from sympy.core.evalf import fastlog, bitcount + >>> s, m, e = 0, 5, 1 + >>> bc = bitcount(m) + >>> n = [1, -1][s]*m*2**e + >>> n, (log(n)/log(2)).evalf(2), fastlog((s, m, e, bc)) + (10, 3.3, 4) + """ + + if not x or x == fzero: + return MINUS_INF + return x[2] + x[3] + + +def pure_complex(v: 'Expr', or_real=False) -> tuple['Number', 'Number'] | None: + """Return a and b if v matches a + I*b where b is not zero and + a and b are Numbers, else None. If `or_real` is True then 0 will + be returned for `b` if `v` is a real number. + + Examples + ======== + + >>> from sympy.core.evalf import pure_complex + >>> from sympy import sqrt, I, S + >>> a, b, surd = S(2), S(3), sqrt(2) + >>> pure_complex(a) + >>> pure_complex(a, or_real=True) + (2, 0) + >>> pure_complex(surd) + >>> pure_complex(a + b*I) + (2, 3) + >>> pure_complex(I) + (0, 1) + """ + h, t = v.as_coeff_Add() + if t: + c, i = t.as_coeff_Mul() + if i is S.ImaginaryUnit: + return h, c + elif or_real: + return h, S.Zero + return None + + +# I don't know what this is, see function scaled_zero below +SCALED_ZERO_TUP = tTuple[List[int], int, int, int] + + +@overload +def scaled_zero(mag: SCALED_ZERO_TUP, sign=1) -> MPF_TUP: + ... +@overload +def scaled_zero(mag: int, sign=1) -> tTuple[SCALED_ZERO_TUP, int]: + ... +def scaled_zero(mag: tUnion[SCALED_ZERO_TUP, int], sign=1) -> \ + tUnion[MPF_TUP, tTuple[SCALED_ZERO_TUP, int]]: + """Return an mpf representing a power of two with magnitude ``mag`` + and -1 for precision. Or, if ``mag`` is a scaled_zero tuple, then just + remove the sign from within the list that it was initially wrapped + in. + + Examples + ======== + + >>> from sympy.core.evalf import scaled_zero + >>> from sympy import Float + >>> z, p = scaled_zero(100) + >>> z, p + (([0], 1, 100, 1), -1) + >>> ok = scaled_zero(z) + >>> ok + (0, 1, 100, 1) + >>> Float(ok) + 1.26765060022823e+30 + >>> Float(ok, p) + 0.e+30 + >>> ok, p = scaled_zero(100, -1) + >>> Float(scaled_zero(ok), p) + -0.e+30 + """ + if isinstance(mag, tuple) and len(mag) == 4 and iszero(mag, scaled=True): + return (mag[0][0],) + mag[1:] + elif isinstance(mag, SYMPY_INTS): + if sign not in [-1, 1]: + raise ValueError('sign must be +/-1') + rv, p = mpf_shift(fone, mag), -1 + s = 0 if sign == 1 else 1 + rv = ([s],) + rv[1:] + return rv, p + else: + raise ValueError('scaled zero expects int or scaled_zero tuple.') + + +def iszero(mpf: tUnion[MPF_TUP, SCALED_ZERO_TUP, None], scaled=False) -> Optional[bool]: + if not scaled: + return not mpf or not mpf[1] and not mpf[-1] + return mpf and isinstance(mpf[0], list) and mpf[1] == mpf[-1] == 1 + + +def complex_accuracy(result: TMP_RES) -> tUnion[int, Any]: + """ + Returns relative accuracy of a complex number with given accuracies + for the real and imaginary parts. The relative accuracy is defined + in the complex norm sense as ||z|+|error|| / |z| where error + is equal to (real absolute error) + (imag absolute error)*i. + + The full expression for the (logarithmic) error can be approximated + easily by using the max norm to approximate the complex norm. + + In the worst case (re and im equal), this is wrong by a factor + sqrt(2), or by log2(sqrt(2)) = 0.5 bit. + """ + if result is S.ComplexInfinity: + return INF + re, im, re_acc, im_acc = result + if not im: + if not re: + return INF + return re_acc + if not re: + return im_acc + re_size = fastlog(re) + im_size = fastlog(im) + absolute_error = max(re_size - re_acc, im_size - im_acc) + relative_error = absolute_error - max(re_size, im_size) + return -relative_error + + +def get_abs(expr: 'Expr', prec: int, options: OPT_DICT) -> TMP_RES: + result = evalf(expr, prec + 2, options) + if result is S.ComplexInfinity: + return finf, None, prec, None + re, im, re_acc, im_acc = result + if not re: + re, re_acc, im, im_acc = im, im_acc, re, re_acc + if im: + if expr.is_number: + abs_expr, _, acc, _ = evalf(abs(N(expr, prec + 2)), + prec + 2, options) + return abs_expr, None, acc, None + else: + if 'subs' in options: + return libmp.mpc_abs((re, im), prec), None, re_acc, None + return abs(expr), None, prec, None + elif re: + return mpf_abs(re), None, re_acc, None + else: + return None, None, None, None + + +def get_complex_part(expr: 'Expr', no: int, prec: int, options: OPT_DICT) -> TMP_RES: + """no = 0 for real part, no = 1 for imaginary part""" + workprec = prec + i = 0 + while 1: + res = evalf(expr, workprec, options) + if res is S.ComplexInfinity: + return fnan, None, prec, None + value, accuracy = res[no::2] + # XXX is the last one correct? Consider re((1+I)**2).n() + if (not value) or accuracy >= prec or -value[2] > prec: + return value, None, accuracy, None + workprec += max(30, 2**i) + i += 1 + + +def evalf_abs(expr: 'Abs', prec: int, options: OPT_DICT) -> TMP_RES: + return get_abs(expr.args[0], prec, options) + + +def evalf_re(expr: 're', prec: int, options: OPT_DICT) -> TMP_RES: + return get_complex_part(expr.args[0], 0, prec, options) + + +def evalf_im(expr: 'im', prec: int, options: OPT_DICT) -> TMP_RES: + return get_complex_part(expr.args[0], 1, prec, options) + + +def finalize_complex(re: MPF_TUP, im: MPF_TUP, prec: int) -> TMP_RES: + if re == fzero and im == fzero: + raise ValueError("got complex zero with unknown accuracy") + elif re == fzero: + return None, im, None, prec + elif im == fzero: + return re, None, prec, None + + size_re = fastlog(re) + size_im = fastlog(im) + if size_re > size_im: + re_acc = prec + im_acc = prec + min(-(size_re - size_im), 0) + else: + im_acc = prec + re_acc = prec + min(-(size_im - size_re), 0) + return re, im, re_acc, im_acc + + +def chop_parts(value: TMP_RES, prec: int) -> TMP_RES: + """ + Chop off tiny real or complex parts. + """ + if value is S.ComplexInfinity: + return value + re, im, re_acc, im_acc = value + # Method 1: chop based on absolute value + if re and re not in _infs_nan and (fastlog(re) < -prec + 4): + re, re_acc = None, None + if im and im not in _infs_nan and (fastlog(im) < -prec + 4): + im, im_acc = None, None + # Method 2: chop if inaccurate and relatively small + if re and im: + delta = fastlog(re) - fastlog(im) + if re_acc < 2 and (delta - re_acc <= -prec + 4): + re, re_acc = None, None + if im_acc < 2 and (delta - im_acc >= prec - 4): + im, im_acc = None, None + return re, im, re_acc, im_acc + + +def check_target(expr: 'Expr', result: TMP_RES, prec: int): + a = complex_accuracy(result) + if a < prec: + raise PrecisionExhausted("Failed to distinguish the expression: \n\n%s\n\n" + "from zero. Try simplifying the input, using chop=True, or providing " + "a higher maxn for evalf" % (expr)) + + +def get_integer_part(expr: 'Expr', no: int, options: OPT_DICT, return_ints=False) -> \ + tUnion[TMP_RES, tTuple[int, int]]: + """ + With no = 1, computes ceiling(expr) + With no = -1, computes floor(expr) + + Note: this function either gives the exact result or signals failure. + """ + from sympy.functions.elementary.complexes import re, im + # The expression is likely less than 2^30 or so + assumed_size = 30 + result = evalf(expr, assumed_size, options) + if result is S.ComplexInfinity: + raise ValueError("Cannot get integer part of Complex Infinity") + ire, iim, ire_acc, iim_acc = result + + # We now know the size, so we can calculate how much extra precision + # (if any) is needed to get within the nearest integer + if ire and iim: + gap = max(fastlog(ire) - ire_acc, fastlog(iim) - iim_acc) + elif ire: + gap = fastlog(ire) - ire_acc + elif iim: + gap = fastlog(iim) - iim_acc + else: + # ... or maybe the expression was exactly zero + if return_ints: + return 0, 0 + else: + return None, None, None, None + + margin = 10 + + if gap >= -margin: + prec = margin + assumed_size + gap + ire, iim, ire_acc, iim_acc = evalf( + expr, prec, options) + else: + prec = assumed_size + + # We can now easily find the nearest integer, but to find floor/ceil, we + # must also calculate whether the difference to the nearest integer is + # positive or negative (which may fail if very close). + def calc_part(re_im: 'Expr', nexpr: MPF_TUP): + from .add import Add + _, _, exponent, _ = nexpr + is_int = exponent == 0 + nint = int(to_int(nexpr, rnd)) + if is_int: + # make sure that we had enough precision to distinguish + # between nint and the re or im part (re_im) of expr that + # was passed to calc_part + ire, iim, ire_acc, iim_acc = evalf( + re_im - nint, 10, options) # don't need much precision + assert not iim + size = -fastlog(ire) + 2 # -ve b/c ire is less than 1 + if size > prec: + ire, iim, ire_acc, iim_acc = evalf( + re_im, size, options) + assert not iim + nexpr = ire + nint = int(to_int(nexpr, rnd)) + _, _, new_exp, _ = ire + is_int = new_exp == 0 + if not is_int: + # if there are subs and they all contain integer re/im parts + # then we can (hopefully) safely substitute them into the + # expression + s = options.get('subs', False) + if s: + # use strict=False with as_int because we take + # 2.0 == 2 + def is_int_reim(x): + """Check for integer or integer + I*integer.""" + try: + as_int(x, strict=False) + return True + except ValueError: + try: + [as_int(i, strict=False) for i in x.as_real_imag()] + return True + except ValueError: + return False + + if all(is_int_reim(v) for v in s.values()): + re_im = re_im.subs(s) + + re_im = Add(re_im, -nint, evaluate=False) + x, _, x_acc, _ = evalf(re_im, 10, options) + try: + check_target(re_im, (x, None, x_acc, None), 3) + except PrecisionExhausted: + if not re_im.equals(0): + raise PrecisionExhausted + x = fzero + nint += int(no*(mpf_cmp(x or fzero, fzero) == no)) + nint = from_int(nint) + return nint, INF + + re_, im_, re_acc, im_acc = None, None, None, None + + if ire: + re_, re_acc = calc_part(re(expr, evaluate=False), ire) + if iim: + im_, im_acc = calc_part(im(expr, evaluate=False), iim) + + if return_ints: + return int(to_int(re_ or fzero)), int(to_int(im_ or fzero)) + return re_, im_, re_acc, im_acc + + +def evalf_ceiling(expr: 'ceiling', prec: int, options: OPT_DICT) -> TMP_RES: + return get_integer_part(expr.args[0], 1, options) + + +def evalf_floor(expr: 'floor', prec: int, options: OPT_DICT) -> TMP_RES: + return get_integer_part(expr.args[0], -1, options) + + +def evalf_float(expr: 'Float', prec: int, options: OPT_DICT) -> TMP_RES: + return expr._mpf_, None, prec, None + + +def evalf_rational(expr: 'Rational', prec: int, options: OPT_DICT) -> TMP_RES: + return from_rational(expr.p, expr.q, prec), None, prec, None + + +def evalf_integer(expr: 'Integer', prec: int, options: OPT_DICT) -> TMP_RES: + return from_int(expr.p, prec), None, prec, None + +#----------------------------------------------------------------------------# +# # +# Arithmetic operations # +# # +#----------------------------------------------------------------------------# + + +def add_terms(terms: list, prec: int, target_prec: int) -> \ + tTuple[tUnion[MPF_TUP, SCALED_ZERO_TUP, None], Optional[int]]: + """ + Helper for evalf_add. Adds a list of (mpfval, accuracy) terms. + + Returns + ======= + + - None, None if there are no non-zero terms; + - terms[0] if there is only 1 term; + - scaled_zero if the sum of the terms produces a zero by cancellation + e.g. mpfs representing 1 and -1 would produce a scaled zero which need + special handling since they are not actually zero and they are purposely + malformed to ensure that they cannot be used in anything but accuracy + calculations; + - a tuple that is scaled to target_prec that corresponds to the + sum of the terms. + + The returned mpf tuple will be normalized to target_prec; the input + prec is used to define the working precision. + + XXX explain why this is needed and why one cannot just loop using mpf_add + """ + + terms = [t for t in terms if not iszero(t[0])] + if not terms: + return None, None + elif len(terms) == 1: + return terms[0] + + # see if any argument is NaN or oo and thus warrants a special return + special = [] + from .numbers import Float + for t in terms: + arg = Float._new(t[0], 1) + if arg is S.NaN or arg.is_infinite: + special.append(arg) + if special: + from .add import Add + rv = evalf(Add(*special), prec + 4, {}) + return rv[0], rv[2] + + working_prec = 2*prec + sum_man, sum_exp = 0, 0 + absolute_err: List[int] = [] + + for x, accuracy in terms: + sign, man, exp, bc = x + if sign: + man = -man + absolute_err.append(bc + exp - accuracy) + delta = exp - sum_exp + if exp >= sum_exp: + # x much larger than existing sum? + # first: quick test + if ((delta > working_prec) and + ((not sum_man) or + delta - bitcount(abs(sum_man)) > working_prec)): + sum_man = man + sum_exp = exp + else: + sum_man += (man << delta) + else: + delta = -delta + # x much smaller than existing sum? + if delta - bc > working_prec: + if not sum_man: + sum_man, sum_exp = man, exp + else: + sum_man = (sum_man << delta) + man + sum_exp = exp + absolute_error = max(absolute_err) + if not sum_man: + return scaled_zero(absolute_error) + if sum_man < 0: + sum_sign = 1 + sum_man = -sum_man + else: + sum_sign = 0 + sum_bc = bitcount(sum_man) + sum_accuracy = sum_exp + sum_bc - absolute_error + r = normalize(sum_sign, sum_man, sum_exp, sum_bc, target_prec, + rnd), sum_accuracy + return r + + +def evalf_add(v: 'Add', prec: int, options: OPT_DICT) -> TMP_RES: + res = pure_complex(v) + if res: + h, c = res + re, _, re_acc, _ = evalf(h, prec, options) + im, _, im_acc, _ = evalf(c, prec, options) + return re, im, re_acc, im_acc + + oldmaxprec = options.get('maxprec', DEFAULT_MAXPREC) + + i = 0 + target_prec = prec + while 1: + options['maxprec'] = min(oldmaxprec, 2*prec) + + terms = [evalf(arg, prec + 10, options) for arg in v.args] + n = terms.count(S.ComplexInfinity) + if n >= 2: + return fnan, None, prec, None + re, re_acc = add_terms( + [a[0::2] for a in terms if isinstance(a, tuple) and a[0]], prec, target_prec) + im, im_acc = add_terms( + [a[1::2] for a in terms if isinstance(a, tuple) and a[1]], prec, target_prec) + if n == 1: + if re in (finf, fninf, fnan) or im in (finf, fninf, fnan): + return fnan, None, prec, None + return S.ComplexInfinity + acc = complex_accuracy((re, im, re_acc, im_acc)) + if acc >= target_prec: + if options.get('verbose'): + print("ADD: wanted", target_prec, "accurate bits, got", re_acc, im_acc) + break + else: + if (prec - target_prec) > options['maxprec']: + break + + prec = prec + max(10 + 2**i, target_prec - acc) + i += 1 + if options.get('verbose'): + print("ADD: restarting with prec", prec) + + options['maxprec'] = oldmaxprec + if iszero(re, scaled=True): + re = scaled_zero(re) + if iszero(im, scaled=True): + im = scaled_zero(im) + return re, im, re_acc, im_acc + + +def evalf_mul(v: 'Mul', prec: int, options: OPT_DICT) -> TMP_RES: + res = pure_complex(v) + if res: + # the only pure complex that is a mul is h*I + _, h = res + im, _, im_acc, _ = evalf(h, prec, options) + return None, im, None, im_acc + args = list(v.args) + + # see if any argument is NaN or oo and thus warrants a special return + has_zero = False + special = [] + from .numbers import Float + for arg in args: + result = evalf(arg, prec, options) + if result is S.ComplexInfinity: + special.append(result) + continue + if result[0] is None: + if result[1] is None: + has_zero = True + continue + num = Float._new(result[0], 1) + if num is S.NaN: + return fnan, None, prec, None + if num.is_infinite: + special.append(num) + if special: + if has_zero: + return fnan, None, prec, None + from .mul import Mul + return evalf(Mul(*special), prec + 4, {}) + if has_zero: + return None, None, None, None + + # With guard digits, multiplication in the real case does not destroy + # accuracy. This is also true in the complex case when considering the + # total accuracy; however accuracy for the real or imaginary parts + # separately may be lower. + acc = prec + + # XXX: big overestimate + working_prec = prec + len(args) + 5 + + # Empty product is 1 + start = man, exp, bc = MPZ(1), 0, 1 + + # First, we multiply all pure real or pure imaginary numbers. + # direction tells us that the result should be multiplied by + # I**direction; all other numbers get put into complex_factors + # to be multiplied out after the first phase. + last = len(args) + direction = 0 + args.append(S.One) + complex_factors = [] + + for i, arg in enumerate(args): + if i != last and pure_complex(arg): + args[-1] = (args[-1]*arg).expand() + continue + elif i == last and arg is S.One: + continue + re, im, re_acc, im_acc = evalf(arg, working_prec, options) + if re and im: + complex_factors.append((re, im, re_acc, im_acc)) + continue + elif re: + (s, m, e, b), w_acc = re, re_acc + elif im: + (s, m, e, b), w_acc = im, im_acc + direction += 1 + else: + return None, None, None, None + direction += 2*s + man *= m + exp += e + bc += b + while bc > 3*working_prec: + man >>= working_prec + exp += working_prec + bc -= working_prec + acc = min(acc, w_acc) + sign = (direction & 2) >> 1 + if not complex_factors: + v = normalize(sign, man, exp, bitcount(man), prec, rnd) + # multiply by i + if direction & 1: + return None, v, None, acc + else: + return v, None, acc, None + else: + # initialize with the first term + if (man, exp, bc) != start: + # there was a real part; give it an imaginary part + re, im = (sign, man, exp, bitcount(man)), (0, MPZ(0), 0, 0) + i0 = 0 + else: + # there is no real part to start (other than the starting 1) + wre, wim, wre_acc, wim_acc = complex_factors[0] + acc = min(acc, + complex_accuracy((wre, wim, wre_acc, wim_acc))) + re = wre + im = wim + i0 = 1 + + for wre, wim, wre_acc, wim_acc in complex_factors[i0:]: + # acc is the overall accuracy of the product; we aren't + # computing exact accuracies of the product. + acc = min(acc, + complex_accuracy((wre, wim, wre_acc, wim_acc))) + + use_prec = working_prec + A = mpf_mul(re, wre, use_prec) + B = mpf_mul(mpf_neg(im), wim, use_prec) + C = mpf_mul(re, wim, use_prec) + D = mpf_mul(im, wre, use_prec) + re = mpf_add(A, B, use_prec) + im = mpf_add(C, D, use_prec) + if options.get('verbose'): + print("MUL: wanted", prec, "accurate bits, got", acc) + # multiply by I + if direction & 1: + re, im = mpf_neg(im), re + return re, im, acc, acc + + +def evalf_pow(v: 'Pow', prec: int, options) -> TMP_RES: + + target_prec = prec + base, exp = v.args + + # We handle x**n separately. This has two purposes: 1) it is much + # faster, because we avoid calling evalf on the exponent, and 2) it + # allows better handling of real/imaginary parts that are exactly zero + if exp.is_Integer: + p: int = exp.p # type: ignore + # Exact + if not p: + return fone, None, prec, None + # Exponentiation by p magnifies relative error by |p|, so the + # base must be evaluated with increased precision if p is large + prec += int(math.log2(abs(p))) + result = evalf(base, prec + 5, options) + if result is S.ComplexInfinity: + if p < 0: + return None, None, None, None + return result + re, im, re_acc, im_acc = result + # Real to integer power + if re and not im: + return mpf_pow_int(re, p, target_prec), None, target_prec, None + # (x*I)**n = I**n * x**n + if im and not re: + z = mpf_pow_int(im, p, target_prec) + case = p % 4 + if case == 0: + return z, None, target_prec, None + if case == 1: + return None, z, None, target_prec + if case == 2: + return mpf_neg(z), None, target_prec, None + if case == 3: + return None, mpf_neg(z), None, target_prec + # Zero raised to an integer power + if not re: + if p < 0: + return S.ComplexInfinity + return None, None, None, None + # General complex number to arbitrary integer power + re, im = libmp.mpc_pow_int((re, im), p, prec) + # Assumes full accuracy in input + return finalize_complex(re, im, target_prec) + + result = evalf(base, prec + 5, options) + if result is S.ComplexInfinity: + if exp.is_Rational: + if exp < 0: + return None, None, None, None + return result + raise NotImplementedError + + # Pure square root + if exp is S.Half: + xre, xim, _, _ = result + # General complex square root + if xim: + re, im = libmp.mpc_sqrt((xre or fzero, xim), prec) + return finalize_complex(re, im, prec) + if not xre: + return None, None, None, None + # Square root of a negative real number + if mpf_lt(xre, fzero): + return None, mpf_sqrt(mpf_neg(xre), prec), None, prec + # Positive square root + return mpf_sqrt(xre, prec), None, prec, None + + # We first evaluate the exponent to find its magnitude + # This determines the working precision that must be used + prec += 10 + result = evalf(exp, prec, options) + if result is S.ComplexInfinity: + return fnan, None, prec, None + yre, yim, _, _ = result + # Special cases: x**0 + if not (yre or yim): + return fone, None, prec, None + + ysize = fastlog(yre) + # Restart if too big + # XXX: prec + ysize might exceed maxprec + if ysize > 5: + prec += ysize + yre, yim, _, _ = evalf(exp, prec, options) + + # Pure exponential function; no need to evalf the base + if base is S.Exp1: + if yim: + re, im = libmp.mpc_exp((yre or fzero, yim), prec) + return finalize_complex(re, im, target_prec) + return mpf_exp(yre, target_prec), None, target_prec, None + + xre, xim, _, _ = evalf(base, prec + 5, options) + # 0**y + if not (xre or xim): + if yim: + return fnan, None, prec, None + if yre[0] == 1: # y < 0 + return S.ComplexInfinity + return None, None, None, None + + # (real ** complex) or (complex ** complex) + if yim: + re, im = libmp.mpc_pow( + (xre or fzero, xim or fzero), (yre or fzero, yim), + target_prec) + return finalize_complex(re, im, target_prec) + # complex ** real + if xim: + re, im = libmp.mpc_pow_mpf((xre or fzero, xim), yre, target_prec) + return finalize_complex(re, im, target_prec) + # negative ** real + elif mpf_lt(xre, fzero): + re, im = libmp.mpc_pow_mpf((xre, fzero), yre, target_prec) + return finalize_complex(re, im, target_prec) + # positive ** real + else: + return mpf_pow(xre, yre, target_prec), None, target_prec, None + + +#----------------------------------------------------------------------------# +# # +# Special functions # +# # +#----------------------------------------------------------------------------# + + +def evalf_exp(expr: 'exp', prec: int, options: OPT_DICT) -> TMP_RES: + from .power import Pow + return evalf_pow(Pow(S.Exp1, expr.exp, evaluate=False), prec, options) + + +def evalf_trig(v: 'Expr', prec: int, options: OPT_DICT) -> TMP_RES: + """ + This function handles sin and cos of complex arguments. + + TODO: should also handle tan of complex arguments. + """ + from sympy.functions.elementary.trigonometric import cos, sin + if isinstance(v, cos): + func = mpf_cos + elif isinstance(v, sin): + func = mpf_sin + else: + raise NotImplementedError + arg = v.args[0] + # 20 extra bits is possibly overkill. It does make the need + # to restart very unlikely + xprec = prec + 20 + re, im, re_acc, im_acc = evalf(arg, xprec, options) + if im: + if 'subs' in options: + v = v.subs(options['subs']) + return evalf(v._eval_evalf(prec), prec, options) + if not re: + if isinstance(v, cos): + return fone, None, prec, None + elif isinstance(v, sin): + return None, None, None, None + else: + raise NotImplementedError + # For trigonometric functions, we are interested in the + # fixed-point (absolute) accuracy of the argument. + xsize = fastlog(re) + # Magnitude <= 1.0. OK to compute directly, because there is no + # danger of hitting the first root of cos (with sin, magnitude + # <= 2.0 would actually be ok) + if xsize < 1: + return func(re, prec, rnd), None, prec, None + # Very large + if xsize >= 10: + xprec = prec + xsize + re, im, re_acc, im_acc = evalf(arg, xprec, options) + # Need to repeat in case the argument is very close to a + # multiple of pi (or pi/2), hitting close to a root + while 1: + y = func(re, prec, rnd) + ysize = fastlog(y) + gap = -ysize + accuracy = (xprec - xsize) - gap + if accuracy < prec: + if options.get('verbose'): + print("SIN/COS", accuracy, "wanted", prec, "gap", gap) + print(to_str(y, 10)) + if xprec > options.get('maxprec', DEFAULT_MAXPREC): + return y, None, accuracy, None + xprec += gap + re, im, re_acc, im_acc = evalf(arg, xprec, options) + continue + else: + return y, None, prec, None + + +def evalf_log(expr: 'log', prec: int, options: OPT_DICT) -> TMP_RES: + if len(expr.args)>1: + expr = expr.doit() + return evalf(expr, prec, options) + arg = expr.args[0] + workprec = prec + 10 + result = evalf(arg, workprec, options) + if result is S.ComplexInfinity: + return result + xre, xim, xacc, _ = result + + # evalf can return NoneTypes if chop=True + # issue 18516, 19623 + if xre is xim is None: + # Dear reviewer, I do not know what -inf is; + # it looks to be (1, 0, -789, -3) + # but I'm not sure in general, + # so we just let mpmath figure + # it out by taking log of 0 directly. + # It would be better to return -inf instead. + xre = fzero + + if xim: + from sympy.functions.elementary.complexes import Abs + from sympy.functions.elementary.exponential import log + + # XXX: use get_abs etc instead + re = evalf_log( + log(Abs(arg, evaluate=False), evaluate=False), prec, options) + im = mpf_atan2(xim, xre or fzero, prec) + return re[0], im, re[2], prec + + imaginary_term = (mpf_cmp(xre, fzero) < 0) + + re = mpf_log(mpf_abs(xre), prec, rnd) + size = fastlog(re) + if prec - size > workprec and re != fzero: + from .add import Add + # We actually need to compute 1+x accurately, not x + add = Add(S.NegativeOne, arg, evaluate=False) + xre, xim, _, _ = evalf_add(add, prec, options) + prec2 = workprec - fastlog(xre) + # xre is now x - 1 so we add 1 back here to calculate x + re = mpf_log(mpf_abs(mpf_add(xre, fone, prec2)), prec, rnd) + + re_acc = prec + + if imaginary_term: + return re, mpf_pi(prec), re_acc, prec + else: + return re, None, re_acc, None + + +def evalf_atan(v: 'atan', prec: int, options: OPT_DICT) -> TMP_RES: + arg = v.args[0] + xre, xim, reacc, imacc = evalf(arg, prec + 5, options) + if xre is xim is None: + return (None,)*4 + if xim: + raise NotImplementedError + return mpf_atan(xre, prec, rnd), None, prec, None + + +def evalf_subs(prec: int, subs: dict) -> dict: + """ Change all Float entries in `subs` to have precision prec. """ + newsubs = {} + for a, b in subs.items(): + b = S(b) + if b.is_Float: + b = b._eval_evalf(prec) + newsubs[a] = b + return newsubs + + +def evalf_piecewise(expr: 'Expr', prec: int, options: OPT_DICT) -> TMP_RES: + from .numbers import Float, Integer + if 'subs' in options: + expr = expr.subs(evalf_subs(prec, options['subs'])) + newopts = options.copy() + del newopts['subs'] + if hasattr(expr, 'func'): + return evalf(expr, prec, newopts) + if isinstance(expr, float): + return evalf(Float(expr), prec, newopts) + if isinstance(expr, int): + return evalf(Integer(expr), prec, newopts) + + # We still have undefined symbols + raise NotImplementedError + + +def evalf_alg_num(a: 'AlgebraicNumber', prec: int, options: OPT_DICT) -> TMP_RES: + return evalf(a.to_root(), prec, options) + +#----------------------------------------------------------------------------# +# # +# High-level operations # +# # +#----------------------------------------------------------------------------# + + +def as_mpmath(x: Any, prec: int, options: OPT_DICT) -> tUnion[mpc, mpf]: + from .numbers import Infinity, NegativeInfinity, Zero + x = sympify(x) + if isinstance(x, Zero) or x == 0.0: + return mpf(0) + if isinstance(x, Infinity): + return mpf('inf') + if isinstance(x, NegativeInfinity): + return mpf('-inf') + # XXX + result = evalf(x, prec, options) + return quad_to_mpmath(result) + + +def do_integral(expr: 'Integral', prec: int, options: OPT_DICT) -> TMP_RES: + func = expr.args[0] + x, xlow, xhigh = expr.args[1] + if xlow == xhigh: + xlow = xhigh = 0 + elif x not in func.free_symbols: + # only the difference in limits matters in this case + # so if there is a symbol in common that will cancel + # out when taking the difference, then use that + # difference + if xhigh.free_symbols & xlow.free_symbols: + diff = xhigh - xlow + if diff.is_number: + xlow, xhigh = 0, diff + + oldmaxprec = options.get('maxprec', DEFAULT_MAXPREC) + options['maxprec'] = min(oldmaxprec, 2*prec) + + with workprec(prec + 5): + xlow = as_mpmath(xlow, prec + 15, options) + xhigh = as_mpmath(xhigh, prec + 15, options) + + # Integration is like summation, and we can phone home from + # the integrand function to update accuracy summation style + # Note that this accuracy is inaccurate, since it fails + # to account for the variable quadrature weights, + # but it is better than nothing + + from sympy.functions.elementary.trigonometric import cos, sin + from .symbol import Wild + + have_part = [False, False] + max_real_term: tUnion[float, int] = MINUS_INF + max_imag_term: tUnion[float, int] = MINUS_INF + + def f(t: 'Expr') -> tUnion[mpc, mpf]: + nonlocal max_real_term, max_imag_term + re, im, re_acc, im_acc = evalf(func, mp.prec, {'subs': {x: t}}) + + have_part[0] = re or have_part[0] + have_part[1] = im or have_part[1] + + max_real_term = max(max_real_term, fastlog(re)) + max_imag_term = max(max_imag_term, fastlog(im)) + + if im: + return mpc(re or fzero, im) + return mpf(re or fzero) + + if options.get('quad') == 'osc': + A = Wild('A', exclude=[x]) + B = Wild('B', exclude=[x]) + D = Wild('D') + m = func.match(cos(A*x + B)*D) + if not m: + m = func.match(sin(A*x + B)*D) + if not m: + raise ValueError("An integrand of the form sin(A*x+B)*f(x) " + "or cos(A*x+B)*f(x) is required for oscillatory quadrature") + period = as_mpmath(2*S.Pi/m[A], prec + 15, options) + result = quadosc(f, [xlow, xhigh], period=period) + # XXX: quadosc does not do error detection yet + quadrature_error = MINUS_INF + else: + result, quadrature_err = quadts(f, [xlow, xhigh], error=1) + quadrature_error = fastlog(quadrature_err._mpf_) + + options['maxprec'] = oldmaxprec + + if have_part[0]: + re: Optional[MPF_TUP] = result.real._mpf_ + re_acc: Optional[int] + if re == fzero: + re_s, re_acc = scaled_zero(int(-max(prec, max_real_term, quadrature_error))) + re = scaled_zero(re_s) # handled ok in evalf_integral + else: + re_acc = int(-max(max_real_term - fastlog(re) - prec, quadrature_error)) + else: + re, re_acc = None, None + + if have_part[1]: + im: Optional[MPF_TUP] = result.imag._mpf_ + im_acc: Optional[int] + if im == fzero: + im_s, im_acc = scaled_zero(int(-max(prec, max_imag_term, quadrature_error))) + im = scaled_zero(im_s) # handled ok in evalf_integral + else: + im_acc = int(-max(max_imag_term - fastlog(im) - prec, quadrature_error)) + else: + im, im_acc = None, None + + result = re, im, re_acc, im_acc + return result + + +def evalf_integral(expr: 'Integral', prec: int, options: OPT_DICT) -> TMP_RES: + limits = expr.limits + if len(limits) != 1 or len(limits[0]) != 3: + raise NotImplementedError + workprec = prec + i = 0 + maxprec = options.get('maxprec', INF) + while 1: + result = do_integral(expr, workprec, options) + accuracy = complex_accuracy(result) + if accuracy >= prec: # achieved desired precision + break + if workprec >= maxprec: # can't increase accuracy any more + break + if accuracy == -1: + # maybe the answer really is zero and maybe we just haven't increased + # the precision enough. So increase by doubling to not take too long + # to get to maxprec. + workprec *= 2 + else: + workprec += max(prec, 2**i) + workprec = min(workprec, maxprec) + i += 1 + return result + + +def check_convergence(numer: 'Expr', denom: 'Expr', n: 'Symbol') -> tTuple[int, Any, Any]: + """ + Returns + ======= + + (h, g, p) where + -- h is: + > 0 for convergence of rate 1/factorial(n)**h + < 0 for divergence of rate factorial(n)**(-h) + = 0 for geometric or polynomial convergence or divergence + + -- abs(g) is: + > 1 for geometric convergence of rate 1/h**n + < 1 for geometric divergence of rate h**n + = 1 for polynomial convergence or divergence + + (g < 0 indicates an alternating series) + + -- p is: + > 1 for polynomial convergence of rate 1/n**h + <= 1 for polynomial divergence of rate n**(-h) + + """ + from sympy.polys.polytools import Poly + npol = Poly(numer, n) + dpol = Poly(denom, n) + p = npol.degree() + q = dpol.degree() + rate = q - p + if rate: + return rate, None, None + constant = dpol.LC() / npol.LC() + from .numbers import equal_valued + if not equal_valued(abs(constant), 1): + return rate, constant, None + if npol.degree() == dpol.degree() == 0: + return rate, constant, 0 + pc = npol.all_coeffs()[1] + qc = dpol.all_coeffs()[1] + return rate, constant, (qc - pc)/dpol.LC() + + +def hypsum(expr: 'Expr', n: 'Symbol', start: int, prec: int) -> mpf: + """ + Sum a rapidly convergent infinite hypergeometric series with + given general term, e.g. e = hypsum(1/factorial(n), n). The + quotient between successive terms must be a quotient of integer + polynomials. + """ + from .numbers import Float, equal_valued + from sympy.simplify.simplify import hypersimp + + if prec == float('inf'): + raise NotImplementedError('does not support inf prec') + + if start: + expr = expr.subs(n, n + start) + hs = hypersimp(expr, n) + if hs is None: + raise NotImplementedError("a hypergeometric series is required") + num, den = hs.as_numer_denom() + + func1 = lambdify(n, num) + func2 = lambdify(n, den) + + h, g, p = check_convergence(num, den, n) + + if h < 0: + raise ValueError("Sum diverges like (n!)^%i" % (-h)) + + term = expr.subs(n, 0) + if not term.is_Rational: + raise NotImplementedError("Non rational term functionality is not implemented.") + + # Direct summation if geometric or faster + if h > 0 or (h == 0 and abs(g) > 1): + term = (MPZ(term.p) << prec) // term.q + s = term + k = 1 + while abs(term) > 5: + term *= MPZ(func1(k - 1)) + term //= MPZ(func2(k - 1)) + s += term + k += 1 + return from_man_exp(s, -prec) + else: + alt = g < 0 + if abs(g) < 1: + raise ValueError("Sum diverges like (%i)^n" % abs(1/g)) + if p < 1 or (equal_valued(p, 1) and not alt): + raise ValueError("Sum diverges like n^%i" % (-p)) + # We have polynomial convergence: use Richardson extrapolation + vold = None + ndig = prec_to_dps(prec) + while True: + # Need to use at least quad precision because a lot of cancellation + # might occur in the extrapolation process; we check the answer to + # make sure that the desired precision has been reached, too. + prec2 = 4*prec + term0 = (MPZ(term.p) << prec2) // term.q + + def summand(k, _term=[term0]): + if k: + k = int(k) + _term[0] *= MPZ(func1(k - 1)) + _term[0] //= MPZ(func2(k - 1)) + return make_mpf(from_man_exp(_term[0], -prec2)) + + with workprec(prec): + v = nsum(summand, [0, mpmath_inf], method='richardson') + vf = Float(v, ndig) + if vold is not None and vold == vf: + break + prec += prec # double precision each time + vold = vf + + return v._mpf_ + + +def evalf_prod(expr: 'Product', prec: int, options: OPT_DICT) -> TMP_RES: + if all((l[1] - l[2]).is_Integer for l in expr.limits): + result = evalf(expr.doit(), prec=prec, options=options) + else: + from sympy.concrete.summations import Sum + result = evalf(expr.rewrite(Sum), prec=prec, options=options) + return result + + +def evalf_sum(expr: 'Sum', prec: int, options: OPT_DICT) -> TMP_RES: + from .numbers import Float + if 'subs' in options: + expr = expr.subs(options['subs']) + func = expr.function + limits = expr.limits + if len(limits) != 1 or len(limits[0]) != 3: + raise NotImplementedError + if func.is_zero: + return None, None, prec, None + prec2 = prec + 10 + try: + n, a, b = limits[0] + if b is not S.Infinity or a is S.NegativeInfinity or a != int(a): + raise NotImplementedError + # Use fast hypergeometric summation if possible + v = hypsum(func, n, int(a), prec2) + delta = prec - fastlog(v) + if fastlog(v) < -10: + v = hypsum(func, n, int(a), delta) + return v, None, min(prec, delta), None + except NotImplementedError: + # Euler-Maclaurin summation for general series + eps = Float(2.0)**(-prec) + for i in range(1, 5): + m = n = 2**i * prec + s, err = expr.euler_maclaurin(m=m, n=n, eps=eps, + eval_integral=False) + err = err.evalf() + if err is S.NaN: + raise NotImplementedError + if err <= eps: + break + err = fastlog(evalf(abs(err), 20, options)[0]) + re, im, re_acc, im_acc = evalf(s, prec2, options) + if re_acc is None: + re_acc = -err + if im_acc is None: + im_acc = -err + return re, im, re_acc, im_acc + + +#----------------------------------------------------------------------------# +# # +# Symbolic interface # +# # +#----------------------------------------------------------------------------# + +def evalf_symbol(x: 'Expr', prec: int, options: OPT_DICT) -> TMP_RES: + val = options['subs'][x] + if isinstance(val, mpf): + if not val: + return None, None, None, None + return val._mpf_, None, prec, None + else: + if '_cache' not in options: + options['_cache'] = {} + cache = options['_cache'] + cached, cached_prec = cache.get(x, (None, MINUS_INF)) + if cached_prec >= prec: + return cached + v = evalf(sympify(val), prec, options) + cache[x] = (v, prec) + return v + + +evalf_table: tDict[Type['Expr'], Callable[['Expr', int, OPT_DICT], TMP_RES]] = {} + + +def _create_evalf_table(): + global evalf_table + from sympy.concrete.products import Product + from sympy.concrete.summations import Sum + from .add import Add + from .mul import Mul + from .numbers import Exp1, Float, Half, ImaginaryUnit, Integer, NaN, NegativeOne, One, Pi, Rational, \ + Zero, ComplexInfinity, AlgebraicNumber + from .power import Pow + from .symbol import Dummy, Symbol + from sympy.functions.elementary.complexes import Abs, im, re + from sympy.functions.elementary.exponential import exp, log + from sympy.functions.elementary.integers import ceiling, floor + from sympy.functions.elementary.piecewise import Piecewise + from sympy.functions.elementary.trigonometric import atan, cos, sin + from sympy.integrals.integrals import Integral + evalf_table = { + Symbol: evalf_symbol, + Dummy: evalf_symbol, + Float: evalf_float, + Rational: evalf_rational, + Integer: evalf_integer, + Zero: lambda x, prec, options: (None, None, prec, None), + One: lambda x, prec, options: (fone, None, prec, None), + Half: lambda x, prec, options: (fhalf, None, prec, None), + Pi: lambda x, prec, options: (mpf_pi(prec), None, prec, None), + Exp1: lambda x, prec, options: (mpf_e(prec), None, prec, None), + ImaginaryUnit: lambda x, prec, options: (None, fone, None, prec), + NegativeOne: lambda x, prec, options: (fnone, None, prec, None), + ComplexInfinity: lambda x, prec, options: S.ComplexInfinity, + NaN: lambda x, prec, options: (fnan, None, prec, None), + + exp: evalf_exp, + + cos: evalf_trig, + sin: evalf_trig, + + Add: evalf_add, + Mul: evalf_mul, + Pow: evalf_pow, + + log: evalf_log, + atan: evalf_atan, + Abs: evalf_abs, + + re: evalf_re, + im: evalf_im, + floor: evalf_floor, + ceiling: evalf_ceiling, + + Integral: evalf_integral, + Sum: evalf_sum, + Product: evalf_prod, + Piecewise: evalf_piecewise, + + AlgebraicNumber: evalf_alg_num, + } + + +def evalf(x: 'Expr', prec: int, options: OPT_DICT) -> TMP_RES: + """ + Evaluate the ``Expr`` instance, ``x`` + to a binary precision of ``prec``. This + function is supposed to be used internally. + + Parameters + ========== + + x : Expr + The formula to evaluate to a float. + prec : int + The binary precision that the output should have. + options : dict + A dictionary with the same entries as + ``EvalfMixin.evalf`` and in addition, + ``maxprec`` which is the maximum working precision. + + Returns + ======= + + An optional tuple, ``(re, im, re_acc, im_acc)`` + which are the real, imaginary, real accuracy + and imaginary accuracy respectively. ``re`` is + an mpf value tuple and so is ``im``. ``re_acc`` + and ``im_acc`` are ints. + + NB: all these return values can be ``None``. + If all values are ``None``, then that represents 0. + Note that 0 is also represented as ``fzero = (0, 0, 0, 0)``. + """ + from sympy.functions.elementary.complexes import re as re_, im as im_ + try: + rf = evalf_table[type(x)] + r = rf(x, prec, options) + except KeyError: + # Fall back to ordinary evalf if possible + if 'subs' in options: + x = x.subs(evalf_subs(prec, options['subs'])) + xe = x._eval_evalf(prec) + if xe is None: + raise NotImplementedError + as_real_imag = getattr(xe, "as_real_imag", None) + if as_real_imag is None: + raise NotImplementedError # e.g. FiniteSet(-1.0, 1.0).evalf() + re, im = as_real_imag() + if re.has(re_) or im.has(im_): + raise NotImplementedError + if re == 0.0: + re = None + reprec = None + elif re.is_number: + re = re._to_mpmath(prec, allow_ints=False)._mpf_ + reprec = prec + else: + raise NotImplementedError + if im == 0.0: + im = None + imprec = None + elif im.is_number: + im = im._to_mpmath(prec, allow_ints=False)._mpf_ + imprec = prec + else: + raise NotImplementedError + r = re, im, reprec, imprec + + if options.get("verbose"): + print("### input", x) + print("### output", to_str(r[0] or fzero, 50) if isinstance(r, tuple) else r) + print("### raw", r) # r[0], r[2] + print() + chop = options.get('chop', False) + if chop: + if chop is True: + chop_prec = prec + else: + # convert (approximately) from given tolerance; + # the formula here will will make 1e-i rounds to 0 for + # i in the range +/-27 while 2e-i will not be chopped + chop_prec = int(round(-3.321*math.log10(chop) + 2.5)) + if chop_prec == 3: + chop_prec -= 1 + r = chop_parts(r, chop_prec) + if options.get("strict"): + check_target(x, r, prec) + return r + + +def quad_to_mpmath(q, ctx=None): + """Turn the quad returned by ``evalf`` into an ``mpf`` or ``mpc``. """ + mpc = make_mpc if ctx is None else ctx.make_mpc + mpf = make_mpf if ctx is None else ctx.make_mpf + if q is S.ComplexInfinity: + raise NotImplementedError + re, im, _, _ = q + if im: + if not re: + re = fzero + return mpc((re, im)) + elif re: + return mpf(re) + else: + return mpf(fzero) + + +class EvalfMixin: + """Mixin class adding evalf capability.""" + + __slots__ = () # type: tTuple[str, ...] + + def evalf(self, n=15, subs=None, maxn=100, chop=False, strict=False, quad=None, verbose=False): + """ + Evaluate the given formula to an accuracy of *n* digits. + + Parameters + ========== + + subs : dict, optional + Substitute numerical values for symbols, e.g. + ``subs={x:3, y:1+pi}``. The substitutions must be given as a + dictionary. + + maxn : int, optional + Allow a maximum temporary working precision of maxn digits. + + chop : bool or number, optional + Specifies how to replace tiny real or imaginary parts in + subresults by exact zeros. + + When ``True`` the chop value defaults to standard precision. + + Otherwise the chop value is used to determine the + magnitude of "small" for purposes of chopping. + + >>> from sympy import N + >>> x = 1e-4 + >>> N(x, chop=True) + 0.000100000000000000 + >>> N(x, chop=1e-5) + 0.000100000000000000 + >>> N(x, chop=1e-4) + 0 + + strict : bool, optional + Raise ``PrecisionExhausted`` if any subresult fails to + evaluate to full accuracy, given the available maxprec. + + quad : str, optional + Choose algorithm for numerical quadrature. By default, + tanh-sinh quadrature is used. For oscillatory + integrals on an infinite interval, try ``quad='osc'``. + + verbose : bool, optional + Print debug information. + + Notes + ===== + + When Floats are naively substituted into an expression, + precision errors may adversely affect the result. For example, + adding 1e16 (a Float) to 1 will truncate to 1e16; if 1e16 is + then subtracted, the result will be 0. + That is exactly what happens in the following: + + >>> from sympy.abc import x, y, z + >>> values = {x: 1e16, y: 1, z: 1e16} + >>> (x + y - z).subs(values) + 0 + + Using the subs argument for evalf is the accurate way to + evaluate such an expression: + + >>> (x + y - z).evalf(subs=values) + 1.00000000000000 + """ + from .numbers import Float, Number + n = n if n is not None else 15 + + if subs and is_sequence(subs): + raise TypeError('subs must be given as a dictionary') + + # for sake of sage that doesn't like evalf(1) + if n == 1 and isinstance(self, Number): + from .expr import _mag + rv = self.evalf(2, subs, maxn, chop, strict, quad, verbose) + m = _mag(rv) + rv = rv.round(1 - m) + return rv + + if not evalf_table: + _create_evalf_table() + prec = dps_to_prec(n) + options = {'maxprec': max(prec, int(maxn*LG10)), 'chop': chop, + 'strict': strict, 'verbose': verbose} + if subs is not None: + options['subs'] = subs + if quad is not None: + options['quad'] = quad + try: + result = evalf(self, prec + 4, options) + except NotImplementedError: + # Fall back to the ordinary evalf + if hasattr(self, 'subs') and subs is not None: # issue 20291 + v = self.subs(subs)._eval_evalf(prec) + else: + v = self._eval_evalf(prec) + if v is None: + return self + elif not v.is_number: + return v + try: + # If the result is numerical, normalize it + result = evalf(v, prec, options) + except NotImplementedError: + # Probably contains symbols or unknown functions + return v + if result is S.ComplexInfinity: + return result + re, im, re_acc, im_acc = result + if re is S.NaN or im is S.NaN: + return S.NaN + if re: + p = max(min(prec, re_acc), 1) + re = Float._new(re, p) + else: + re = S.Zero + if im: + p = max(min(prec, im_acc), 1) + im = Float._new(im, p) + return re + im*S.ImaginaryUnit + else: + return re + + n = evalf + + def _evalf(self, prec): + """Helper for evalf. Does the same thing but takes binary precision""" + r = self._eval_evalf(prec) + if r is None: + r = self + return r + + def _eval_evalf(self, prec): + return + + def _to_mpmath(self, prec, allow_ints=True): + # mpmath functions accept ints as input + errmsg = "cannot convert to mpmath number" + if allow_ints and self.is_Integer: + return self.p + if hasattr(self, '_as_mpf_val'): + return make_mpf(self._as_mpf_val(prec)) + try: + result = evalf(self, prec, {}) + return quad_to_mpmath(result) + except NotImplementedError: + v = self._eval_evalf(prec) + if v is None: + raise ValueError(errmsg) + if v.is_Float: + return make_mpf(v._mpf_) + # Number + Number*I is also fine + re, im = v.as_real_imag() + if allow_ints and re.is_Integer: + re = from_int(re.p) + elif re.is_Float: + re = re._mpf_ + else: + raise ValueError(errmsg) + if allow_ints and im.is_Integer: + im = from_int(im.p) + elif im.is_Float: + im = im._mpf_ + else: + raise ValueError(errmsg) + return make_mpc((re, im)) + + +def N(x, n=15, **options): + r""" + Calls x.evalf(n, \*\*options). + + Explanations + ============ + + Both .n() and N() are equivalent to .evalf(); use the one that you like better. + See also the docstring of .evalf() for information on the options. + + Examples + ======== + + >>> from sympy import Sum, oo, N + >>> from sympy.abc import k + >>> Sum(1/k**k, (k, 1, oo)) + Sum(k**(-k), (k, 1, oo)) + >>> N(_, 4) + 1.291 + + """ + # by using rational=True, any evaluation of a string + # will be done using exact values for the Floats + return sympify(x, rational=True).evalf(n, **options) + + +def _evalf_with_bounded_error(x: 'Expr', eps: 'Optional[Expr]' = None, + m: int = 0, + options: Optional[OPT_DICT] = None) -> TMP_RES: + """ + Evaluate *x* to within a bounded absolute error. + + Parameters + ========== + + x : Expr + The quantity to be evaluated. + eps : Expr, None, optional (default=None) + Positive real upper bound on the acceptable error. + m : int, optional (default=0) + If *eps* is None, then use 2**(-m) as the upper bound on the error. + options: OPT_DICT + As in the ``evalf`` function. + + Returns + ======= + + A tuple ``(re, im, re_acc, im_acc)``, as returned by ``evalf``. + + See Also + ======== + + evalf + + """ + if eps is not None: + if not (eps.is_Rational or eps.is_Float) or not eps > 0: + raise ValueError("eps must be positive") + r, _, _, _ = evalf(1/eps, 1, {}) + m = fastlog(r) + + c, d, _, _ = evalf(x, 1, {}) + # Note: If x = a + b*I, then |a| <= 2|c| and |b| <= 2|d|, with equality + # only in the zero case. + # If a is non-zero, then |c| = 2**nc for some integer nc, and c has + # bitcount 1. Therefore 2**fastlog(c) = 2**(nc+1) = 2|c| is an upper bound + # on |a|. Likewise for b and d. + nr, ni = fastlog(c), fastlog(d) + n = max(nr, ni) + 1 + # If x is 0, then n is MINUS_INF, and p will be 1. Otherwise, + # n - 1 bits get us past the integer parts of a and b, and +1 accounts for + # the factor of <= sqrt(2) that is |x|/max(|a|, |b|). + p = max(1, m + n + 1) + + options = options or {} + return evalf(x, p, options) diff --git a/lib/python3.12/site-packages/sympy/core/expr.py b/lib/python3.12/site-packages/sympy/core/expr.py new file mode 100644 index 0000000000000000000000000000000000000000..c6be21344a60ef7521ee75a5b8e453aaf682f315 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/core/expr.py @@ -0,0 +1,4163 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING +from collections.abc import Iterable +from functools import reduce +import re + +from .sympify import sympify, _sympify +from .basic import Basic, Atom +from .singleton import S +from .evalf import EvalfMixin, pure_complex, DEFAULT_MAXPREC +from .decorators import call_highest_priority, sympify_method_args, sympify_return +from .cache import cacheit +from .intfunc import mod_inverse +from .sorting import default_sort_key +from .kind import NumberKind +from sympy.utilities.exceptions import sympy_deprecation_warning +from sympy.utilities.misc import as_int, func_name, filldedent +from sympy.utilities.iterables import has_variety, sift +from mpmath.libmp import mpf_log, prec_to_dps +from mpmath.libmp.libintmath import giant_steps + + +if TYPE_CHECKING: + from .numbers import Number + +from collections import defaultdict + + +def _corem(eq, c): # helper for extract_additively + # return co, diff from co*c + diff + co = [] + non = [] + for i in Add.make_args(eq): + ci = i.coeff(c) + if not ci: + non.append(i) + else: + co.append(ci) + return Add(*co), Add(*non) + + +@sympify_method_args +class Expr(Basic, EvalfMixin): + """ + Base class for algebraic expressions. + + Explanation + =========== + + Everything that requires arithmetic operations to be defined + should subclass this class, instead of Basic (which should be + used only for argument storage and expression manipulation, i.e. + pattern matching, substitutions, etc). + + If you want to override the comparisons of expressions: + Should use _eval_is_ge for inequality, or _eval_is_eq, with multiple dispatch. + _eval_is_ge return true if x >= y, false if x < y, and None if the two types + are not comparable or the comparison is indeterminate + + See Also + ======== + + sympy.core.basic.Basic + """ + + __slots__: tuple[str, ...] = () + + is_scalar = True # self derivative is 1 + + @property + def _diff_wrt(self): + """Return True if one can differentiate with respect to this + object, else False. + + Explanation + =========== + + Subclasses such as Symbol, Function and Derivative return True + to enable derivatives wrt them. The implementation in Derivative + separates the Symbol and non-Symbol (_diff_wrt=True) variables and + temporarily converts the non-Symbols into Symbols when performing + the differentiation. By default, any object deriving from Expr + will behave like a scalar with self.diff(self) == 1. If this is + not desired then the object must also set `is_scalar = False` or + else define an _eval_derivative routine. + + Note, see the docstring of Derivative for how this should work + mathematically. In particular, note that expr.subs(yourclass, Symbol) + should be well-defined on a structural level, or this will lead to + inconsistent results. + + Examples + ======== + + >>> from sympy import Expr + >>> e = Expr() + >>> e._diff_wrt + False + >>> class MyScalar(Expr): + ... _diff_wrt = True + ... + >>> MyScalar().diff(MyScalar()) + 1 + >>> class MySymbol(Expr): + ... _diff_wrt = True + ... is_scalar = False + ... + >>> MySymbol().diff(MySymbol()) + Derivative(MySymbol(), MySymbol()) + """ + return False + + @cacheit + def sort_key(self, order=None): + + coeff, expr = self.as_coeff_Mul() + + if expr.is_Pow: + if expr.base is S.Exp1: + # If we remove this, many doctests will go crazy: + # (keeps E**x sorted like the exp(x) function, + # part of exp(x) to E**x transition) + expr, exp = Function("exp")(expr.exp), S.One + else: + expr, exp = expr.args + else: + exp = S.One + + if expr.is_Dummy: + args = (expr.sort_key(),) + elif expr.is_Atom: + args = (str(expr),) + else: + if expr.is_Add: + args = expr.as_ordered_terms(order=order) + elif expr.is_Mul: + args = expr.as_ordered_factors(order=order) + else: + args = expr.args + + args = tuple( + [ default_sort_key(arg, order=order) for arg in args ]) + + args = (len(args), tuple(args)) + exp = exp.sort_key(order=order) + + return expr.class_key(), args, exp, coeff + + def _hashable_content(self): + """Return a tuple of information about self that can be used to + compute the hash. If a class defines additional attributes, + like ``name`` in Symbol, then this method should be updated + accordingly to return such relevant attributes. + Defining more than _hashable_content is necessary if __eq__ has + been defined by a class. See note about this in Basic.__eq__.""" + return self._args + + # *************** + # * Arithmetics * + # *************** + # Expr and its subclasses use _op_priority to determine which object + # passed to a binary special method (__mul__, etc.) will handle the + # operation. In general, the 'call_highest_priority' decorator will choose + # the object with the highest _op_priority to handle the call. + # Custom subclasses that want to define their own binary special methods + # should set an _op_priority value that is higher than the default. + # + # **NOTE**: + # This is a temporary fix, and will eventually be replaced with + # something better and more powerful. See issue 5510. + _op_priority = 10.0 + + @property + def _add_handler(self): + return Add + + @property + def _mul_handler(self): + return Mul + + def __pos__(self): + return self + + def __neg__(self): + # Mul has its own __neg__ routine, so we just + # create a 2-args Mul with the -1 in the canonical + # slot 0. + c = self.is_commutative + return Mul._from_args((S.NegativeOne, self), c) + + def __abs__(self) -> Expr: + from sympy.functions.elementary.complexes import Abs + return Abs(self) + + @sympify_return([('other', 'Expr')], NotImplemented) + @call_highest_priority('__radd__') + def __add__(self, other): + return Add(self, other) + + @sympify_return([('other', 'Expr')], NotImplemented) + @call_highest_priority('__add__') + def __radd__(self, other): + return Add(other, self) + + @sympify_return([('other', 'Expr')], NotImplemented) + @call_highest_priority('__rsub__') + def __sub__(self, other): + return Add(self, -other) + + @sympify_return([('other', 'Expr')], NotImplemented) + @call_highest_priority('__sub__') + def __rsub__(self, other): + return Add(other, -self) + + @sympify_return([('other', 'Expr')], NotImplemented) + @call_highest_priority('__rmul__') + def __mul__(self, other): + return Mul(self, other) + + @sympify_return([('other', 'Expr')], NotImplemented) + @call_highest_priority('__mul__') + def __rmul__(self, other): + return Mul(other, self) + + @sympify_return([('other', 'Expr')], NotImplemented) + @call_highest_priority('__rpow__') + def _pow(self, other): + return Pow(self, other) + + def __pow__(self, other, mod=None) -> Expr: + if mod is None: + return self._pow(other) + try: + _self, other, mod = as_int(self), as_int(other), as_int(mod) + if other >= 0: + return _sympify(pow(_self, other, mod)) + else: + return _sympify(mod_inverse(pow(_self, -other, mod), mod)) + except ValueError: + power = self._pow(other) + try: + return power%mod + except TypeError: + return NotImplemented + + @sympify_return([('other', 'Expr')], NotImplemented) + @call_highest_priority('__pow__') + def __rpow__(self, other): + return Pow(other, self) + + @sympify_return([('other', 'Expr')], NotImplemented) + @call_highest_priority('__rtruediv__') + def __truediv__(self, other): + denom = Pow(other, S.NegativeOne) + if self is S.One: + return denom + else: + return Mul(self, denom) + + @sympify_return([('other', 'Expr')], NotImplemented) + @call_highest_priority('__truediv__') + def __rtruediv__(self, other): + denom = Pow(self, S.NegativeOne) + if other is S.One: + return denom + else: + return Mul(other, denom) + + @sympify_return([('other', 'Expr')], NotImplemented) + @call_highest_priority('__rmod__') + def __mod__(self, other): + return Mod(self, other) + + @sympify_return([('other', 'Expr')], NotImplemented) + @call_highest_priority('__mod__') + def __rmod__(self, other): + return Mod(other, self) + + @sympify_return([('other', 'Expr')], NotImplemented) + @call_highest_priority('__rfloordiv__') + def __floordiv__(self, other): + from sympy.functions.elementary.integers import floor + return floor(self / other) + + @sympify_return([('other', 'Expr')], NotImplemented) + @call_highest_priority('__floordiv__') + def __rfloordiv__(self, other): + from sympy.functions.elementary.integers import floor + return floor(other / self) + + + @sympify_return([('other', 'Expr')], NotImplemented) + @call_highest_priority('__rdivmod__') + def __divmod__(self, other): + from sympy.functions.elementary.integers import floor + return floor(self / other), Mod(self, other) + + @sympify_return([('other', 'Expr')], NotImplemented) + @call_highest_priority('__divmod__') + def __rdivmod__(self, other): + from sympy.functions.elementary.integers import floor + return floor(other / self), Mod(other, self) + + def __int__(self): + if not self.is_number: + raise TypeError("Cannot convert symbols to int") + r = self.round(2) + if not r.is_Number: + raise TypeError("Cannot convert complex to int") + if r in (S.NaN, S.Infinity, S.NegativeInfinity): + raise TypeError("Cannot convert %s to int" % r) + i = int(r) + if not i: + return i + if int_valued(r): + # non-integer self should pass one of these tests + if (self > i) is S.true: + return i + if (self < i) is S.true: + return i - 1 + ok = self.equals(i) + if ok is None: + raise TypeError('cannot compute int value accurately') + if ok: + return i + # off by one + return i - (1 if i > 0 else -1) + return i + + def __float__(self): + # Don't bother testing if it's a number; if it's not this is going + # to fail, and if it is we still need to check that it evalf'ed to + # a number. + result = self.evalf() + if result.is_Number: + return float(result) + if result.is_number and result.as_real_imag()[1]: + raise TypeError("Cannot convert complex to float") + raise TypeError("Cannot convert expression to float") + + def __complex__(self): + result = self.evalf() + re, im = result.as_real_imag() + return complex(float(re), float(im)) + + @sympify_return([('other', 'Expr')], NotImplemented) + def __ge__(self, other): + from .relational import GreaterThan + return GreaterThan(self, other) + + @sympify_return([('other', 'Expr')], NotImplemented) + def __le__(self, other): + from .relational import LessThan + return LessThan(self, other) + + @sympify_return([('other', 'Expr')], NotImplemented) + def __gt__(self, other): + from .relational import StrictGreaterThan + return StrictGreaterThan(self, other) + + @sympify_return([('other', 'Expr')], NotImplemented) + def __lt__(self, other): + from .relational import StrictLessThan + return StrictLessThan(self, other) + + def __trunc__(self): + if not self.is_number: + raise TypeError("Cannot truncate symbols and expressions") + else: + return Integer(self) + + def __format__(self, format_spec: str): + if self.is_number: + mt = re.match(r'\+?\d*\.(\d+)f', format_spec) + if mt: + prec = int(mt.group(1)) + rounded = self.round(prec) + if rounded.is_Integer: + return format(int(rounded), format_spec) + if rounded.is_Float: + return format(rounded, format_spec) + return super().__format__(format_spec) + + @staticmethod + def _from_mpmath(x, prec): + if hasattr(x, "_mpf_"): + return Float._new(x._mpf_, prec) + elif hasattr(x, "_mpc_"): + re, im = x._mpc_ + re = Float._new(re, prec) + im = Float._new(im, prec)*S.ImaginaryUnit + return re + im + else: + raise TypeError("expected mpmath number (mpf or mpc)") + + @property + def is_number(self): + """Returns True if ``self`` has no free symbols and no + undefined functions (AppliedUndef, to be precise). It will be + faster than ``if not self.free_symbols``, however, since + ``is_number`` will fail as soon as it hits a free symbol + or undefined function. + + Examples + ======== + + >>> from sympy import Function, Integral, cos, sin, pi + >>> from sympy.abc import x + >>> f = Function('f') + + >>> x.is_number + False + >>> f(1).is_number + False + >>> (2*x).is_number + False + >>> (2 + Integral(2, x)).is_number + False + >>> (2 + Integral(2, (x, 1, 2))).is_number + True + + Not all numbers are Numbers in the SymPy sense: + + >>> pi.is_number, pi.is_Number + (True, False) + + If something is a number it should evaluate to a number with + real and imaginary parts that are Numbers; the result may not + be comparable, however, since the real and/or imaginary part + of the result may not have precision. + + >>> cos(1).is_number and cos(1).is_comparable + True + + >>> z = cos(1)**2 + sin(1)**2 - 1 + >>> z.is_number + True + >>> z.is_comparable + False + + See Also + ======== + + sympy.core.basic.Basic.is_comparable + """ + return all(obj.is_number for obj in self.args) + + def _random(self, n=None, re_min=-1, im_min=-1, re_max=1, im_max=1): + """Return self evaluated, if possible, replacing free symbols with + random complex values, if necessary. + + Explanation + =========== + + The random complex value for each free symbol is generated + by the random_complex_number routine giving real and imaginary + parts in the range given by the re_min, re_max, im_min, and im_max + values. The returned value is evaluated to a precision of n + (if given) else the maximum of 15 and the precision needed + to get more than 1 digit of precision. If the expression + could not be evaluated to a number, or could not be evaluated + to more than 1 digit of precision, then None is returned. + + Examples + ======== + + >>> from sympy import sqrt + >>> from sympy.abc import x, y + >>> x._random() # doctest: +SKIP + 0.0392918155679172 + 0.916050214307199*I + >>> x._random(2) # doctest: +SKIP + -0.77 - 0.87*I + >>> (x + y/2)._random(2) # doctest: +SKIP + -0.57 + 0.16*I + >>> sqrt(2)._random(2) + 1.4 + + See Also + ======== + + sympy.core.random.random_complex_number + """ + + free = self.free_symbols + prec = 1 + if free: + from sympy.core.random import random_complex_number + a, c, b, d = re_min, re_max, im_min, im_max + reps = dict(list(zip(free, [random_complex_number(a, b, c, d, rational=True) + for zi in free]))) + try: + nmag = abs(self.evalf(2, subs=reps)) + except (ValueError, TypeError): + # if an out of range value resulted in evalf problems + # then return None -- XXX is there a way to know how to + # select a good random number for a given expression? + # e.g. when calculating n! negative values for n should not + # be used + return None + else: + reps = {} + nmag = abs(self.evalf(2)) + + if not hasattr(nmag, '_prec'): + # e.g. exp_polar(2*I*pi) doesn't evaluate but is_number is True + return None + + if nmag._prec == 1: + # increase the precision up to the default maximum + # precision to see if we can get any significance + + # evaluate + for prec in giant_steps(2, DEFAULT_MAXPREC): + nmag = abs(self.evalf(prec, subs=reps)) + if nmag._prec != 1: + break + + if nmag._prec != 1: + if n is None: + n = max(prec, 15) + return self.evalf(n, subs=reps) + + # never got any significance + return None + + def is_constant(self, *wrt, **flags): + """Return True if self is constant, False if not, or None if + the constancy could not be determined conclusively. + + Explanation + =========== + + If an expression has no free symbols then it is a constant. If + there are free symbols it is possible that the expression is a + constant, perhaps (but not necessarily) zero. To test such + expressions, a few strategies are tried: + + 1) numerical evaluation at two random points. If two such evaluations + give two different values and the values have a precision greater than + 1 then self is not constant. If the evaluations agree or could not be + obtained with any precision, no decision is made. The numerical testing + is done only if ``wrt`` is different than the free symbols. + + 2) differentiation with respect to variables in 'wrt' (or all free + symbols if omitted) to see if the expression is constant or not. This + will not always lead to an expression that is zero even though an + expression is constant (see added test in test_expr.py). If + all derivatives are zero then self is constant with respect to the + given symbols. + + 3) finding out zeros of denominator expression with free_symbols. + It will not be constant if there are zeros. It gives more negative + answers for expression that are not constant. + + If neither evaluation nor differentiation can prove the expression is + constant, None is returned unless two numerical values happened to be + the same and the flag ``failing_number`` is True -- in that case the + numerical value will be returned. + + If flag simplify=False is passed, self will not be simplified; + the default is True since self should be simplified before testing. + + Examples + ======== + + >>> from sympy import cos, sin, Sum, S, pi + >>> from sympy.abc import a, n, x, y + >>> x.is_constant() + False + >>> S(2).is_constant() + True + >>> Sum(x, (x, 1, 10)).is_constant() + True + >>> Sum(x, (x, 1, n)).is_constant() + False + >>> Sum(x, (x, 1, n)).is_constant(y) + True + >>> Sum(x, (x, 1, n)).is_constant(n) + False + >>> Sum(x, (x, 1, n)).is_constant(x) + True + >>> eq = a*cos(x)**2 + a*sin(x)**2 - a + >>> eq.is_constant() + True + >>> eq.subs({x: pi, a: 2}) == eq.subs({x: pi, a: 3}) == 0 + True + + >>> (0**x).is_constant() + False + >>> x.is_constant() + False + >>> (x**x).is_constant() + False + >>> one = cos(x)**2 + sin(x)**2 + >>> one.is_constant() + True + >>> ((one - 1)**(x + 1)).is_constant() in (True, False) # could be 0 or 1 + True + """ + + def check_denominator_zeros(expression): + from sympy.solvers.solvers import denoms + + retNone = False + for den in denoms(expression): + z = den.is_zero + if z is True: + return True + if z is None: + retNone = True + if retNone: + return None + return False + + simplify = flags.get('simplify', True) + + if self.is_number: + return True + free = self.free_symbols + if not free: + return True # assume f(1) is some constant + + # if we are only interested in some symbols and they are not in the + # free symbols then this expression is constant wrt those symbols + wrt = set(wrt) + if wrt and not wrt & free: + return True + wrt = wrt or free + + # simplify unless this has already been done + expr = self + if simplify: + expr = expr.simplify() + + # is_zero should be a quick assumptions check; it can be wrong for + # numbers (see test_is_not_constant test), giving False when it + # shouldn't, but hopefully it will never give True unless it is sure. + if expr.is_zero: + return True + + # Don't attempt substitution or differentiation with non-number symbols + wrt_number = {sym for sym in wrt if sym.kind is NumberKind} + + # try numerical evaluation to see if we get two different values + failing_number = None + if wrt_number == free: + # try 0 (for a) and 1 (for b) + try: + a = expr.subs(list(zip(free, [0]*len(free))), + simultaneous=True) + if a is S.NaN: + # evaluation may succeed when substitution fails + a = expr._random(None, 0, 0, 0, 0) + except ZeroDivisionError: + a = None + if a is not None and a is not S.NaN: + try: + b = expr.subs(list(zip(free, [1]*len(free))), + simultaneous=True) + if b is S.NaN: + # evaluation may succeed when substitution fails + b = expr._random(None, 1, 0, 1, 0) + except ZeroDivisionError: + b = None + if b is not None and b is not S.NaN and b.equals(a) is False: + return False + # try random real + b = expr._random(None, -1, 0, 1, 0) + if b is not None and b is not S.NaN and b.equals(a) is False: + return False + # try random complex + b = expr._random() + if b is not None and b is not S.NaN: + if b.equals(a) is False: + return False + failing_number = a if a.is_number else b + + # now we will test each wrt symbol (or all free symbols) to see if the + # expression depends on them or not using differentiation. This is + # not sufficient for all expressions, however, so we don't return + # False if we get a derivative other than 0 with free symbols. + for w in wrt_number: + deriv = expr.diff(w) + if simplify: + deriv = deriv.simplify() + if deriv != 0: + if not (pure_complex(deriv, or_real=True)): + if flags.get('failing_number', False): + return failing_number + return False + cd = check_denominator_zeros(self) + if cd is True: + return False + elif cd is None: + return None + return True + + def equals(self, other, failing_expression=False): + """Return True if self == other, False if it does not, or None. If + failing_expression is True then the expression which did not simplify + to a 0 will be returned instead of None. + + Explanation + =========== + + If ``self`` is a Number (or complex number) that is not zero, then + the result is False. + + If ``self`` is a number and has not evaluated to zero, evalf will be + used to test whether the expression evaluates to zero. If it does so + and the result has significance (i.e. the precision is either -1, for + a Rational result, or is greater than 1) then the evalf value will be + used to return True or False. + + """ + from sympy.simplify.simplify import nsimplify, simplify + from sympy.solvers.solvers import solve + from sympy.polys.polyerrors import NotAlgebraic + from sympy.polys.numberfields import minimal_polynomial + + other = sympify(other) + if self == other: + return True + + # they aren't the same so see if we can make the difference 0; + # don't worry about doing simplification steps one at a time + # because if the expression ever goes to 0 then the subsequent + # simplification steps that are done will be very fast. + diff = factor_terms(simplify(self - other), radical=True) + + if not diff: + return True + + if not diff.has(Add, Mod): + # if there is no expanding to be done after simplifying + # then this can't be a zero + return False + + factors = diff.as_coeff_mul()[1] + if len(factors) > 1: # avoid infinity recursion + fac_zero = [fac.equals(0) for fac in factors] + if None not in fac_zero: # every part can be decided + return any(fac_zero) + + constant = diff.is_constant(simplify=False, failing_number=True) + + if constant is False: + return False + + if not diff.is_number: + if constant is None: + # e.g. unless the right simplification is done, a symbolic + # zero is possible (see expression of issue 6829: without + # simplification constant will be None). + return + + if constant is True: + # this gives a number whether there are free symbols or not + ndiff = diff._random() + # is_comparable will work whether the result is real + # or complex; it could be None, however. + if ndiff and ndiff.is_comparable: + return False + + # sometimes we can use a simplified result to give a clue as to + # what the expression should be; if the expression is *not* zero + # then we should have been able to compute that and so now + # we can just consider the cases where the approximation appears + # to be zero -- we try to prove it via minimal_polynomial. + # + # removed + # ns = nsimplify(diff) + # if diff.is_number and (not ns or ns == diff): + # + # The thought was that if it nsimplifies to 0 that's a sure sign + # to try the following to prove it; or if it changed but wasn't + # zero that might be a sign that it's not going to be easy to + # prove. But tests seem to be working without that logic. + # + if diff.is_number: + # try to prove via self-consistency + surds = [s for s in diff.atoms(Pow) if s.args[0].is_Integer] + # it seems to work better to try big ones first + surds.sort(key=lambda x: -x.args[0]) + for s in surds: + try: + # simplify is False here -- this expression has already + # been identified as being hard to identify as zero; + # we will handle the checking ourselves using nsimplify + # to see if we are in the right ballpark or not and if so + # *then* the simplification will be attempted. + sol = solve(diff, s, simplify=False) + if sol: + if s in sol: + # the self-consistent result is present + return True + if all(si.is_Integer for si in sol): + # perfect powers are removed at instantiation + # so surd s cannot be an integer + return False + if all(i.is_algebraic is False for i in sol): + # a surd is algebraic + return False + if any(si in surds for si in sol): + # it wasn't equal to s but it is in surds + # and different surds are not equal + return False + if any(nsimplify(s - si) == 0 and + simplify(s - si) == 0 for si in sol): + return True + if s.is_real: + if any(nsimplify(si, [s]) == s and simplify(si) == s + for si in sol): + return True + except NotImplementedError: + pass + + # try to prove with minimal_polynomial but know when + # *not* to use this or else it can take a long time. e.g. issue 8354 + if True: # change True to condition that assures non-hang + try: + mp = minimal_polynomial(diff) + if mp.is_Symbol: + return True + return False + except (NotAlgebraic, NotImplementedError): + pass + + # diff has not simplified to zero; constant is either None, True + # or the number with significance (is_comparable) that was randomly + # calculated twice as the same value. + if constant not in (True, None) and constant != 0: + return False + + if failing_expression: + return diff + return None + + def _eval_is_extended_positive_negative(self, positive): + from sympy.polys.numberfields import minimal_polynomial + from sympy.polys.polyerrors import NotAlgebraic + if self.is_number: + # check to see that we can get a value + try: + n2 = self._eval_evalf(2) + # XXX: This shouldn't be caught here + # Catches ValueError: hypsum() failed to converge to the requested + # 34 bits of accuracy + except ValueError: + return None + if n2 is None: + return None + if getattr(n2, '_prec', 1) == 1: # no significance + return None + if n2 is S.NaN: + return None + + f = self.evalf(2) + if f.is_Float: + match = f, S.Zero + else: + match = pure_complex(f) + if match is None: + return False + r, i = match + if not (i.is_Number and r.is_Number): + return False + if r._prec != 1 and i._prec != 1: + return bool(not i and ((r > 0) if positive else (r < 0))) + elif r._prec == 1 and (not i or i._prec == 1) and \ + self._eval_is_algebraic() and not self.has(Function): + try: + if minimal_polynomial(self).is_Symbol: + return False + except (NotAlgebraic, NotImplementedError): + pass + + def _eval_is_extended_positive(self): + return self._eval_is_extended_positive_negative(positive=True) + + def _eval_is_extended_negative(self): + return self._eval_is_extended_positive_negative(positive=False) + + def _eval_interval(self, x, a, b): + """ + Returns evaluation over an interval. For most functions this is: + + self.subs(x, b) - self.subs(x, a), + + possibly using limit() if NaN is returned from subs, or if + singularities are found between a and b. + + If b or a is None, it only evaluates -self.subs(x, a) or self.subs(b, x), + respectively. + + """ + from sympy.calculus.accumulationbounds import AccumBounds + from sympy.functions.elementary.exponential import log + from sympy.series.limits import limit, Limit + from sympy.sets.sets import Interval + from sympy.solvers.solveset import solveset + + if (a is None and b is None): + raise ValueError('Both interval ends cannot be None.') + + def _eval_endpoint(left): + c = a if left else b + if c is None: + return S.Zero + else: + C = self.subs(x, c) + if C.has(S.NaN, S.Infinity, S.NegativeInfinity, + S.ComplexInfinity, AccumBounds): + if (a < b) != False: + C = limit(self, x, c, "+" if left else "-") + else: + C = limit(self, x, c, "-" if left else "+") + + if isinstance(C, Limit): + raise NotImplementedError("Could not compute limit") + return C + + if a == b: + return S.Zero + + A = _eval_endpoint(left=True) + if A is S.NaN: + return A + + B = _eval_endpoint(left=False) + + if (a and b) is None: + return B - A + + value = B - A + + if a.is_comparable and b.is_comparable: + if a < b: + domain = Interval(a, b) + else: + domain = Interval(b, a) + # check the singularities of self within the interval + # if singularities is a ConditionSet (not iterable), catch the exception and pass + singularities = solveset(self.cancel().as_numer_denom()[1], x, + domain=domain) + for logterm in self.atoms(log): + singularities = singularities | solveset(logterm.args[0], x, + domain=domain) + try: + for s in singularities: + if value is S.NaN: + # no need to keep adding, it will stay NaN + break + if not s.is_comparable: + continue + if (a < s) == (s < b) == True: + value += -limit(self, x, s, "+") + limit(self, x, s, "-") + elif (b < s) == (s < a) == True: + value += limit(self, x, s, "+") - limit(self, x, s, "-") + except TypeError: + pass + + return value + + def _eval_power(self, other): + # subclass to compute self**other for cases when + # other is not NaN, 0, or 1 + return None + + def _eval_conjugate(self): + if self.is_extended_real: + return self + elif self.is_imaginary: + return -self + + def conjugate(self): + """Returns the complex conjugate of 'self'.""" + from sympy.functions.elementary.complexes import conjugate as c + return c(self) + + def dir(self, x, cdir): + if self.is_zero: + return S.Zero + from sympy.functions.elementary.exponential import log + minexp = S.Zero + arg = self + while arg: + minexp += S.One + arg = arg.diff(x) + coeff = arg.subs(x, 0) + if coeff is S.NaN: + coeff = arg.limit(x, 0) + if coeff is S.ComplexInfinity: + try: + coeff, _ = arg.leadterm(x) + if coeff.has(log(x)): + raise ValueError() + except ValueError: + coeff = arg.limit(x, 0) + if coeff != S.Zero: + break + return coeff*cdir**minexp + + def _eval_transpose(self): + from sympy.functions.elementary.complexes import conjugate + if (self.is_complex or self.is_infinite): + return self + elif self.is_hermitian: + return conjugate(self) + elif self.is_antihermitian: + return -conjugate(self) + + def transpose(self): + from sympy.functions.elementary.complexes import transpose + return transpose(self) + + def _eval_adjoint(self): + from sympy.functions.elementary.complexes import conjugate, transpose + if self.is_hermitian: + return self + elif self.is_antihermitian: + return -self + obj = self._eval_conjugate() + if obj is not None: + return transpose(obj) + obj = self._eval_transpose() + if obj is not None: + return conjugate(obj) + + def adjoint(self): + from sympy.functions.elementary.complexes import adjoint + return adjoint(self) + + @classmethod + def _parse_order(cls, order): + """Parse and configure the ordering of terms. """ + from sympy.polys.orderings import monomial_key + + startswith = getattr(order, "startswith", None) + if startswith is None: + reverse = False + else: + reverse = startswith('rev-') + if reverse: + order = order[4:] + + monom_key = monomial_key(order) + + def neg(monom): + return tuple([neg(m) if isinstance(m, tuple) else -m for m in monom]) + + def key(term): + _, ((re, im), monom, ncpart) = term + + monom = neg(monom_key(monom)) + ncpart = tuple([e.sort_key(order=order) for e in ncpart]) + coeff = ((bool(im), im), (re, im)) + + return monom, ncpart, coeff + + return key, reverse + + def as_ordered_factors(self, order=None): + """Return list of ordered factors (if Mul) else [self].""" + return [self] + + def as_poly(self, *gens, **args): + """Converts ``self`` to a polynomial or returns ``None``. + + Explanation + =========== + + >>> from sympy import sin + >>> from sympy.abc import x, y + + >>> print((x**2 + x*y).as_poly()) + Poly(x**2 + x*y, x, y, domain='ZZ') + + >>> print((x**2 + x*y).as_poly(x, y)) + Poly(x**2 + x*y, x, y, domain='ZZ') + + >>> print((x**2 + sin(y)).as_poly(x, y)) + None + + """ + from sympy.polys.polyerrors import PolynomialError, GeneratorsNeeded + from sympy.polys.polytools import Poly + + try: + poly = Poly(self, *gens, **args) + + if not poly.is_Poly: + return None + else: + return poly + except (PolynomialError, GeneratorsNeeded): + # PolynomialError is caught for e.g. exp(x).as_poly(x) + # GeneratorsNeeded is caught for e.g. S(2).as_poly() + return None + + def as_ordered_terms(self, order=None, data=False): + """ + Transform an expression to an ordered list of terms. + + Examples + ======== + + >>> from sympy import sin, cos + >>> from sympy.abc import x + + >>> (sin(x)**2*cos(x) + sin(x)**2 + 1).as_ordered_terms() + [sin(x)**2*cos(x), sin(x)**2, 1] + + """ + + from .numbers import Number, NumberSymbol + + if order is None and self.is_Add: + # Spot the special case of Add(Number, Mul(Number, expr)) with the + # first number positive and the second number negative + key = lambda x:not isinstance(x, (Number, NumberSymbol)) + add_args = sorted(Add.make_args(self), key=key) + if (len(add_args) == 2 + and isinstance(add_args[0], (Number, NumberSymbol)) + and isinstance(add_args[1], Mul)): + mul_args = sorted(Mul.make_args(add_args[1]), key=key) + if (len(mul_args) == 2 + and isinstance(mul_args[0], Number) + and add_args[0].is_positive + and mul_args[0].is_negative): + return add_args + + key, reverse = self._parse_order(order) + terms, gens = self.as_terms() + + if not any(term.is_Order for term, _ in terms): + ordered = sorted(terms, key=key, reverse=reverse) + else: + _terms, _order = [], [] + + for term, repr in terms: + if not term.is_Order: + _terms.append((term, repr)) + else: + _order.append((term, repr)) + + ordered = sorted(_terms, key=key, reverse=True) \ + + sorted(_order, key=key, reverse=True) + + if data: + return ordered, gens + else: + return [term for term, _ in ordered] + + def as_terms(self): + """Transform an expression to a list of terms. """ + from .exprtools import decompose_power + + gens, terms = set(), [] + + for term in Add.make_args(self): + coeff, _term = term.as_coeff_Mul() + + coeff = complex(coeff) + cpart, ncpart = {}, [] + + if _term is not S.One: + for factor in Mul.make_args(_term): + if factor.is_number: + try: + coeff *= complex(factor) + except (TypeError, ValueError): + pass + else: + continue + + if factor.is_commutative: + base, exp = decompose_power(factor) + + cpart[base] = exp + gens.add(base) + else: + ncpart.append(factor) + + coeff = coeff.real, coeff.imag + ncpart = tuple(ncpart) + + terms.append((term, (coeff, cpart, ncpart))) + + gens = sorted(gens, key=default_sort_key) + + k, indices = len(gens), {} + + for i, g in enumerate(gens): + indices[g] = i + + result = [] + + for term, (coeff, cpart, ncpart) in terms: + monom = [0]*k + + for base, exp in cpart.items(): + monom[indices[base]] = exp + + result.append((term, (coeff, tuple(monom), ncpart))) + + return result, gens + + def removeO(self): + """Removes the additive O(..) symbol if there is one""" + return self + + def getO(self): + """Returns the additive O(..) symbol if there is one, else None.""" + return None + + def getn(self): + """ + Returns the order of the expression. + + Explanation + =========== + + The order is determined either from the O(...) term. If there + is no O(...) term, it returns None. + + Examples + ======== + + >>> from sympy import O + >>> from sympy.abc import x + >>> (1 + x + O(x**2)).getn() + 2 + >>> (1 + x).getn() + + """ + o = self.getO() + if o is None: + return None + elif o.is_Order: + o = o.expr + if o is S.One: + return S.Zero + if o.is_Symbol: + return S.One + if o.is_Pow: + return o.args[1] + if o.is_Mul: # x**n*log(x)**n or x**n/log(x)**n + for oi in o.args: + if oi.is_Symbol: + return S.One + if oi.is_Pow: + from .symbol import Dummy, Symbol + syms = oi.atoms(Symbol) + if len(syms) == 1: + x = syms.pop() + oi = oi.subs(x, Dummy('x', positive=True)) + if oi.base.is_Symbol and oi.exp.is_Rational: + return abs(oi.exp) + + raise NotImplementedError('not sure of order of %s' % o) + + def count_ops(self, visual=None): + from .function import count_ops + return count_ops(self, visual) + + def args_cnc(self, cset=False, warn=True, split_1=True): + """Return [commutative factors, non-commutative factors] of self. + + Explanation + =========== + + self is treated as a Mul and the ordering of the factors is maintained. + If ``cset`` is True the commutative factors will be returned in a set. + If there were repeated factors (as may happen with an unevaluated Mul) + then an error will be raised unless it is explicitly suppressed by + setting ``warn`` to False. + + Note: -1 is always separated from a Number unless split_1 is False. + + Examples + ======== + + >>> from sympy import symbols, oo + >>> A, B = symbols('A B', commutative=0) + >>> x, y = symbols('x y') + >>> (-2*x*y).args_cnc() + [[-1, 2, x, y], []] + >>> (-2.5*x).args_cnc() + [[-1, 2.5, x], []] + >>> (-2*x*A*B*y).args_cnc() + [[-1, 2, x, y], [A, B]] + >>> (-2*x*A*B*y).args_cnc(split_1=False) + [[-2, x, y], [A, B]] + >>> (-2*x*y).args_cnc(cset=True) + [{-1, 2, x, y}, []] + + The arg is always treated as a Mul: + + >>> (-2 + x + A).args_cnc() + [[], [x - 2 + A]] + >>> (-oo).args_cnc() # -oo is a singleton + [[-1, oo], []] + """ + + if self.is_Mul: + args = list(self.args) + else: + args = [self] + for i, mi in enumerate(args): + if not mi.is_commutative: + c = args[:i] + nc = args[i:] + break + else: + c = args + nc = [] + + if c and split_1 and ( + c[0].is_Number and + c[0].is_extended_negative and + c[0] is not S.NegativeOne): + c[:1] = [S.NegativeOne, -c[0]] + + if cset: + clen = len(c) + c = set(c) + if clen and warn and len(c) != clen: + raise ValueError('repeated commutative arguments: %s' % + [ci for ci in c if list(self.args).count(ci) > 1]) + return [c, nc] + + def coeff(self, x, n=1, right=False, _first=True): + """ + Returns the coefficient from the term(s) containing ``x**n``. If ``n`` + is zero then all terms independent of ``x`` will be returned. + + Explanation + =========== + + When ``x`` is noncommutative, the coefficient to the left (default) or + right of ``x`` can be returned. The keyword 'right' is ignored when + ``x`` is commutative. + + Examples + ======== + + >>> from sympy import symbols + >>> from sympy.abc import x, y, z + + You can select terms that have an explicit negative in front of them: + + >>> (-x + 2*y).coeff(-1) + x + >>> (x - 2*y).coeff(-1) + 2*y + + You can select terms with no Rational coefficient: + + >>> (x + 2*y).coeff(1) + x + >>> (3 + 2*x + 4*x**2).coeff(1) + 0 + + You can select terms independent of x by making n=0; in this case + expr.as_independent(x)[0] is returned (and 0 will be returned instead + of None): + + >>> (3 + 2*x + 4*x**2).coeff(x, 0) + 3 + >>> eq = ((x + 1)**3).expand() + 1 + >>> eq + x**3 + 3*x**2 + 3*x + 2 + >>> [eq.coeff(x, i) for i in reversed(range(4))] + [1, 3, 3, 2] + >>> eq -= 2 + >>> [eq.coeff(x, i) for i in reversed(range(4))] + [1, 3, 3, 0] + + You can select terms that have a numerical term in front of them: + + >>> (-x - 2*y).coeff(2) + -y + >>> from sympy import sqrt + >>> (x + sqrt(2)*x).coeff(sqrt(2)) + x + + The matching is exact: + + >>> (3 + 2*x + 4*x**2).coeff(x) + 2 + >>> (3 + 2*x + 4*x**2).coeff(x**2) + 4 + >>> (3 + 2*x + 4*x**2).coeff(x**3) + 0 + >>> (z*(x + y)**2).coeff((x + y)**2) + z + >>> (z*(x + y)**2).coeff(x + y) + 0 + + In addition, no factoring is done, so 1 + z*(1 + y) is not obtained + from the following: + + >>> (x + z*(x + x*y)).coeff(x) + 1 + + If such factoring is desired, factor_terms can be used first: + + >>> from sympy import factor_terms + >>> factor_terms(x + z*(x + x*y)).coeff(x) + z*(y + 1) + 1 + + >>> n, m, o = symbols('n m o', commutative=False) + >>> n.coeff(n) + 1 + >>> (3*n).coeff(n) + 3 + >>> (n*m + m*n*m).coeff(n) # = (1 + m)*n*m + 1 + m + >>> (n*m + m*n*m).coeff(n, right=True) # = (1 + m)*n*m + m + + If there is more than one possible coefficient 0 is returned: + + >>> (n*m + m*n).coeff(n) + 0 + + If there is only one possible coefficient, it is returned: + + >>> (n*m + x*m*n).coeff(m*n) + x + >>> (n*m + x*m*n).coeff(m*n, right=1) + 1 + + See Also + ======== + + as_coefficient: separate the expression into a coefficient and factor + as_coeff_Add: separate the additive constant from an expression + as_coeff_Mul: separate the multiplicative constant from an expression + as_independent: separate x-dependent terms/factors from others + sympy.polys.polytools.Poly.coeff_monomial: efficiently find the single coefficient of a monomial in Poly + sympy.polys.polytools.Poly.nth: like coeff_monomial but powers of monomial terms are used + """ + x = sympify(x) + if not isinstance(x, Basic): + return S.Zero + + n = as_int(n) + + if not x: + return S.Zero + + if x == self: + if n == 1: + return S.One + return S.Zero + + if x is S.One: + co = [a for a in Add.make_args(self) + if a.as_coeff_Mul()[0] is S.One] + if not co: + return S.Zero + return Add(*co) + + if n == 0: + if x.is_Add and self.is_Add: + c = self.coeff(x, right=right) + if not c: + return S.Zero + if not right: + return self - Add(*[a*x for a in Add.make_args(c)]) + return self - Add(*[x*a for a in Add.make_args(c)]) + return self.as_independent(x, as_Add=True)[0] + + # continue with the full method, looking for this power of x: + x = x**n + + def incommon(l1, l2): + if not l1 or not l2: + return [] + n = min(len(l1), len(l2)) + for i in range(n): + if l1[i] != l2[i]: + return l1[:i] + return l1[:] + + def find(l, sub, first=True): + """ Find where list sub appears in list l. When ``first`` is True + the first occurrence from the left is returned, else the last + occurrence is returned. Return None if sub is not in l. + + Examples + ======== + + >> l = range(5)*2 + >> find(l, [2, 3]) + 2 + >> find(l, [2, 3], first=0) + 7 + >> find(l, [2, 4]) + None + + """ + if not sub or not l or len(sub) > len(l): + return None + n = len(sub) + if not first: + l.reverse() + sub.reverse() + for i in range(len(l) - n + 1): + if all(l[i + j] == sub[j] for j in range(n)): + break + else: + i = None + if not first: + l.reverse() + sub.reverse() + if i is not None and not first: + i = len(l) - (i + n) + return i + + co = [] + args = Add.make_args(self) + self_c = self.is_commutative + x_c = x.is_commutative + if self_c and not x_c: + return S.Zero + if _first and self.is_Add and not self_c and not x_c: + # get the part that depends on x exactly + xargs = Mul.make_args(x) + d = Add(*[i for i in Add.make_args(self.as_independent(x)[1]) + if all(xi in Mul.make_args(i) for xi in xargs)]) + rv = d.coeff(x, right=right, _first=False) + if not rv.is_Add or not right: + return rv + c_part, nc_part = zip(*[i.args_cnc() for i in rv.args]) + if has_variety(c_part): + return rv + return Add(*[Mul._from_args(i) for i in nc_part]) + + one_c = self_c or x_c + xargs, nx = x.args_cnc(cset=True, warn=bool(not x_c)) + # find the parts that pass the commutative terms + for a in args: + margs, nc = a.args_cnc(cset=True, warn=bool(not self_c)) + if nc is None: + nc = [] + if len(xargs) > len(margs): + continue + resid = margs.difference(xargs) + if len(resid) + len(xargs) == len(margs): + if one_c: + co.append(Mul(*(list(resid) + nc))) + else: + co.append((resid, nc)) + if one_c: + if co == []: + return S.Zero + elif co: + return Add(*co) + else: # both nc + # now check the non-comm parts + if not co: + return S.Zero + if all(n == co[0][1] for r, n in co): + ii = find(co[0][1], nx, right) + if ii is not None: + if not right: + return Mul(Add(*[Mul(*r) for r, c in co]), Mul(*co[0][1][:ii])) + else: + return Mul(*co[0][1][ii + len(nx):]) + beg = reduce(incommon, (n[1] for n in co)) + if beg: + ii = find(beg, nx, right) + if ii is not None: + if not right: + gcdc = co[0][0] + for i in range(1, len(co)): + gcdc = gcdc.intersection(co[i][0]) + if not gcdc: + break + return Mul(*(list(gcdc) + beg[:ii])) + else: + m = ii + len(nx) + return Add(*[Mul(*(list(r) + n[m:])) for r, n in co]) + end = list(reversed( + reduce(incommon, (list(reversed(n[1])) for n in co)))) + if end: + ii = find(end, nx, right) + if ii is not None: + if not right: + return Add(*[Mul(*(list(r) + n[:-len(end) + ii])) for r, n in co]) + else: + return Mul(*end[ii + len(nx):]) + # look for single match + hit = None + for i, (r, n) in enumerate(co): + ii = find(n, nx, right) + if ii is not None: + if not hit: + hit = ii, r, n + else: + break + else: + if hit: + ii, r, n = hit + if not right: + return Mul(*(list(r) + n[:ii])) + else: + return Mul(*n[ii + len(nx):]) + + return S.Zero + + def as_expr(self, *gens): + """ + Convert a polynomial to a SymPy expression. + + Examples + ======== + + >>> from sympy import sin + >>> from sympy.abc import x, y + + >>> f = (x**2 + x*y).as_poly(x, y) + >>> f.as_expr() + x**2 + x*y + + >>> sin(x).as_expr() + sin(x) + + """ + return self + + def as_coefficient(self, expr): + """ + Extracts symbolic coefficient at the given expression. In + other words, this functions separates 'self' into the product + of 'expr' and 'expr'-free coefficient. If such separation + is not possible it will return None. + + Examples + ======== + + >>> from sympy import E, pi, sin, I, Poly + >>> from sympy.abc import x + + >>> E.as_coefficient(E) + 1 + >>> (2*E).as_coefficient(E) + 2 + >>> (2*sin(E)*E).as_coefficient(E) + + Two terms have E in them so a sum is returned. (If one were + desiring the coefficient of the term exactly matching E then + the constant from the returned expression could be selected. + Or, for greater precision, a method of Poly can be used to + indicate the desired term from which the coefficient is + desired.) + + >>> (2*E + x*E).as_coefficient(E) + x + 2 + >>> _.args[0] # just want the exact match + 2 + >>> p = Poly(2*E + x*E); p + Poly(x*E + 2*E, x, E, domain='ZZ') + >>> p.coeff_monomial(E) + 2 + >>> p.nth(0, 1) + 2 + + Since the following cannot be written as a product containing + E as a factor, None is returned. (If the coefficient ``2*x`` is + desired then the ``coeff`` method should be used.) + + >>> (2*E*x + x).as_coefficient(E) + >>> (2*E*x + x).coeff(E) + 2*x + + >>> (E*(x + 1) + x).as_coefficient(E) + + >>> (2*pi*I).as_coefficient(pi*I) + 2 + >>> (2*I).as_coefficient(pi*I) + + See Also + ======== + + coeff: return sum of terms have a given factor + as_coeff_Add: separate the additive constant from an expression + as_coeff_Mul: separate the multiplicative constant from an expression + as_independent: separate x-dependent terms/factors from others + sympy.polys.polytools.Poly.coeff_monomial: efficiently find the single coefficient of a monomial in Poly + sympy.polys.polytools.Poly.nth: like coeff_monomial but powers of monomial terms are used + + + """ + + r = self.extract_multiplicatively(expr) + if r and not r.has(expr): + return r + + def as_independent(self, *deps, **hint) -> tuple[Expr, Expr]: + """ + A mostly naive separation of a Mul or Add into arguments that are not + are dependent on deps. To obtain as complete a separation of variables + as possible, use a separation method first, e.g.: + + * separatevars() to change Mul, Add and Pow (including exp) into Mul + * .expand(mul=True) to change Add or Mul into Add + * .expand(log=True) to change log expr into an Add + + The only non-naive thing that is done here is to respect noncommutative + ordering of variables and to always return (0, 0) for `self` of zero + regardless of hints. + + For nonzero `self`, the returned tuple (i, d) has the + following interpretation: + + * i will has no variable that appears in deps + * d will either have terms that contain variables that are in deps, or + be equal to 0 (when self is an Add) or 1 (when self is a Mul) + * if self is an Add then self = i + d + * if self is a Mul then self = i*d + * otherwise (self, S.One) or (S.One, self) is returned. + + To force the expression to be treated as an Add, use the hint as_Add=True + + Examples + ======== + + -- self is an Add + + >>> from sympy import sin, cos, exp + >>> from sympy.abc import x, y, z + + >>> (x + x*y).as_independent(x) + (0, x*y + x) + >>> (x + x*y).as_independent(y) + (x, x*y) + >>> (2*x*sin(x) + y + x + z).as_independent(x) + (y + z, 2*x*sin(x) + x) + >>> (2*x*sin(x) + y + x + z).as_independent(x, y) + (z, 2*x*sin(x) + x + y) + + -- self is a Mul + + >>> (x*sin(x)*cos(y)).as_independent(x) + (cos(y), x*sin(x)) + + non-commutative terms cannot always be separated out when self is a Mul + + >>> from sympy import symbols + >>> n1, n2, n3 = symbols('n1 n2 n3', commutative=False) + >>> (n1 + n1*n2).as_independent(n2) + (n1, n1*n2) + >>> (n2*n1 + n1*n2).as_independent(n2) + (0, n1*n2 + n2*n1) + >>> (n1*n2*n3).as_independent(n1) + (1, n1*n2*n3) + >>> (n1*n2*n3).as_independent(n2) + (n1, n2*n3) + >>> ((x-n1)*(x-y)).as_independent(x) + (1, (x - y)*(x - n1)) + + -- self is anything else: + + >>> (sin(x)).as_independent(x) + (1, sin(x)) + >>> (sin(x)).as_independent(y) + (sin(x), 1) + >>> exp(x+y).as_independent(x) + (1, exp(x + y)) + + -- force self to be treated as an Add: + + >>> (3*x).as_independent(x, as_Add=True) + (0, 3*x) + + -- force self to be treated as a Mul: + + >>> (3+x).as_independent(x, as_Add=False) + (1, x + 3) + >>> (-3+x).as_independent(x, as_Add=False) + (1, x - 3) + + Note how the below differs from the above in making the + constant on the dep term positive. + + >>> (y*(-3+x)).as_independent(x) + (y, x - 3) + + -- use .as_independent() for true independence testing instead + of .has(). The former considers only symbols in the free + symbols while the latter considers all symbols + + >>> from sympy import Integral + >>> I = Integral(x, (x, 1, 2)) + >>> I.has(x) + True + >>> x in I.free_symbols + False + >>> I.as_independent(x) == (I, 1) + True + >>> (I + x).as_independent(x) == (I, x) + True + + Note: when trying to get independent terms, a separation method + might need to be used first. In this case, it is important to keep + track of what you send to this routine so you know how to interpret + the returned values + + >>> from sympy import separatevars, log + >>> separatevars(exp(x+y)).as_independent(x) + (exp(y), exp(x)) + >>> (x + x*y).as_independent(y) + (x, x*y) + >>> separatevars(x + x*y).as_independent(y) + (x, y + 1) + >>> (x*(1 + y)).as_independent(y) + (x, y + 1) + >>> (x*(1 + y)).expand(mul=True).as_independent(y) + (x, x*y) + >>> a, b=symbols('a b', positive=True) + >>> (log(a*b).expand(log=True)).as_independent(b) + (log(a), log(b)) + + See Also + ======== + + separatevars + expand_log + sympy.core.add.Add.as_two_terms + sympy.core.mul.Mul.as_two_terms + as_coeff_mul + """ + from .symbol import Symbol + from .add import _unevaluated_Add + from .mul import _unevaluated_Mul + + if self is S.Zero: + return (self, self) + + func = self.func + if hint.get('as_Add', isinstance(self, Add) ): + want = Add + else: + want = Mul + + # sift out deps into symbolic and other and ignore + # all symbols but those that are in the free symbols + sym = set() + other = [] + for d in deps: + if isinstance(d, Symbol): # Symbol.is_Symbol is True + sym.add(d) + else: + other.append(d) + + def has(e): + """return the standard has() if there are no literal symbols, else + check to see that symbol-deps are in the free symbols.""" + has_other = e.has(*other) + if not sym: + return has_other + return has_other or e.has(*(e.free_symbols & sym)) + + if (want is not func or + func is not Add and func is not Mul): + if has(self): + return (want.identity, self) + else: + return (self, want.identity) + else: + if func is Add: + args = list(self.args) + else: + args, nc = self.args_cnc() + + d = sift(args, has) + depend = d[True] + indep = d[False] + if func is Add: # all terms were treated as commutative + return (Add(*indep), _unevaluated_Add(*depend)) + else: # handle noncommutative by stopping at first dependent term + for i, n in enumerate(nc): + if has(n): + depend.extend(nc[i:]) + break + indep.append(n) + return Mul(*indep), ( + Mul(*depend, evaluate=False) if nc else + _unevaluated_Mul(*depend)) + + def as_real_imag(self, deep=True, **hints): + """Performs complex expansion on 'self' and returns a tuple + containing collected both real and imaginary parts. This + method cannot be confused with re() and im() functions, + which does not perform complex expansion at evaluation. + + However it is possible to expand both re() and im() + functions and get exactly the same results as with + a single call to this function. + + >>> from sympy import symbols, I + + >>> x, y = symbols('x,y', real=True) + + >>> (x + y*I).as_real_imag() + (x, y) + + >>> from sympy.abc import z, w + + >>> (z + w*I).as_real_imag() + (re(z) - im(w), re(w) + im(z)) + + """ + if hints.get('ignore') == self: + return None + else: + from sympy.functions.elementary.complexes import im, re + return (re(self), im(self)) + + def as_powers_dict(self): + """Return self as a dictionary of factors with each factor being + treated as a power. The keys are the bases of the factors and the + values, the corresponding exponents. The resulting dictionary should + be used with caution if the expression is a Mul and contains non- + commutative factors since the order that they appeared will be lost in + the dictionary. + + See Also + ======== + as_ordered_factors: An alternative for noncommutative applications, + returning an ordered list of factors. + args_cnc: Similar to as_ordered_factors, but guarantees separation + of commutative and noncommutative factors. + """ + d = defaultdict(int) + d.update([self.as_base_exp()]) + return d + + def as_coefficients_dict(self, *syms): + """Return a dictionary mapping terms to their Rational coefficient. + Since the dictionary is a defaultdict, inquiries about terms which + were not present will return a coefficient of 0. + + If symbols ``syms`` are provided, any multiplicative terms + independent of them will be considered a coefficient and a + regular dictionary of syms-dependent generators as keys and + their corresponding coefficients as values will be returned. + + Examples + ======== + + >>> from sympy.abc import a, x, y + >>> (3*x + a*x + 4).as_coefficients_dict() + {1: 4, x: 3, a*x: 1} + >>> _[a] + 0 + >>> (3*a*x).as_coefficients_dict() + {a*x: 3} + >>> (3*a*x).as_coefficients_dict(x) + {x: 3*a} + >>> (3*a*x).as_coefficients_dict(y) + {1: 3*a*x} + + """ + d = defaultdict(list) + if not syms: + for ai in Add.make_args(self): + c, m = ai.as_coeff_Mul() + d[m].append(c) + for k, v in d.items(): + if len(v) == 1: + d[k] = v[0] + else: + d[k] = Add(*v) + else: + ind, dep = self.as_independent(*syms, as_Add=True) + for i in Add.make_args(dep): + if i.is_Mul: + c, x = i.as_coeff_mul(*syms) + if c is S.One: + d[i].append(c) + else: + d[i._new_rawargs(*x)].append(c) + elif i: + d[i].append(S.One) + d = {k: Add(*d[k]) for k in d} + if ind is not S.Zero: + d.update({S.One: ind}) + di = defaultdict(int) + di.update(d) + return di + + def as_base_exp(self) -> tuple[Expr, Expr]: + # a -> b ** e + return self, S.One + + def as_coeff_mul(self, *deps, **kwargs) -> tuple[Expr, tuple[Expr, ...]]: + """Return the tuple (c, args) where self is written as a Mul, ``m``. + + c should be a Rational multiplied by any factors of the Mul that are + independent of deps. + + args should be a tuple of all other factors of m; args is empty + if self is a Number or if self is independent of deps (when given). + + This should be used when you do not know if self is a Mul or not but + you want to treat self as a Mul or if you want to process the + individual arguments of the tail of self as a Mul. + + - if you know self is a Mul and want only the head, use self.args[0]; + - if you do not want to process the arguments of the tail but need the + tail then use self.as_two_terms() which gives the head and tail; + - if you want to split self into an independent and dependent parts + use ``self.as_independent(*deps)`` + + >>> from sympy import S + >>> from sympy.abc import x, y + >>> (S(3)).as_coeff_mul() + (3, ()) + >>> (3*x*y).as_coeff_mul() + (3, (x, y)) + >>> (3*x*y).as_coeff_mul(x) + (3*y, (x,)) + >>> (3*y).as_coeff_mul(x) + (3*y, ()) + """ + if deps: + if not self.has(*deps): + return self, () + return S.One, (self,) + + def as_coeff_add(self, *deps) -> tuple[Expr, tuple[Expr, ...]]: + """Return the tuple (c, args) where self is written as an Add, ``a``. + + c should be a Rational added to any terms of the Add that are + independent of deps. + + args should be a tuple of all other terms of ``a``; args is empty + if self is a Number or if self is independent of deps (when given). + + This should be used when you do not know if self is an Add or not but + you want to treat self as an Add or if you want to process the + individual arguments of the tail of self as an Add. + + - if you know self is an Add and want only the head, use self.args[0]; + - if you do not want to process the arguments of the tail but need the + tail then use self.as_two_terms() which gives the head and tail. + - if you want to split self into an independent and dependent parts + use ``self.as_independent(*deps)`` + + >>> from sympy import S + >>> from sympy.abc import x, y + >>> (S(3)).as_coeff_add() + (3, ()) + >>> (3 + x).as_coeff_add() + (3, (x,)) + >>> (3 + x + y).as_coeff_add(x) + (y + 3, (x,)) + >>> (3 + y).as_coeff_add(x) + (y + 3, ()) + + """ + if deps: + if not self.has_free(*deps): + return self, () + return S.Zero, (self,) + + def primitive(self): + """Return the positive Rational that can be extracted non-recursively + from every term of self (i.e., self is treated like an Add). This is + like the as_coeff_Mul() method but primitive always extracts a positive + Rational (never a negative or a Float). + + Examples + ======== + + >>> from sympy.abc import x + >>> (3*(x + 1)**2).primitive() + (3, (x + 1)**2) + >>> a = (6*x + 2); a.primitive() + (2, 3*x + 1) + >>> b = (x/2 + 3); b.primitive() + (1/2, x + 6) + >>> (a*b).primitive() == (1, a*b) + True + """ + if not self: + return S.One, S.Zero + c, r = self.as_coeff_Mul(rational=True) + if c.is_negative: + c, r = -c, -r + return c, r + + def as_content_primitive(self, radical=False, clear=True): + """This method should recursively remove a Rational from all arguments + and return that (content) and the new self (primitive). The content + should always be positive and ``Mul(*foo.as_content_primitive()) == foo``. + The primitive need not be in canonical form and should try to preserve + the underlying structure if possible (i.e. expand_mul should not be + applied to self). + + Examples + ======== + + >>> from sympy import sqrt + >>> from sympy.abc import x, y, z + + >>> eq = 2 + 2*x + 2*y*(3 + 3*y) + + The as_content_primitive function is recursive and retains structure: + + >>> eq.as_content_primitive() + (2, x + 3*y*(y + 1) + 1) + + Integer powers will have Rationals extracted from the base: + + >>> ((2 + 6*x)**2).as_content_primitive() + (4, (3*x + 1)**2) + >>> ((2 + 6*x)**(2*y)).as_content_primitive() + (1, (2*(3*x + 1))**(2*y)) + + Terms may end up joining once their as_content_primitives are added: + + >>> ((5*(x*(1 + y)) + 2*x*(3 + 3*y))).as_content_primitive() + (11, x*(y + 1)) + >>> ((3*(x*(1 + y)) + 2*x*(3 + 3*y))).as_content_primitive() + (9, x*(y + 1)) + >>> ((3*(z*(1 + y)) + 2.0*x*(3 + 3*y))).as_content_primitive() + (1, 6.0*x*(y + 1) + 3*z*(y + 1)) + >>> ((5*(x*(1 + y)) + 2*x*(3 + 3*y))**2).as_content_primitive() + (121, x**2*(y + 1)**2) + >>> ((x*(1 + y) + 0.4*x*(3 + 3*y))**2).as_content_primitive() + (1, 4.84*x**2*(y + 1)**2) + + Radical content can also be factored out of the primitive: + + >>> (2*sqrt(2) + 4*sqrt(10)).as_content_primitive(radical=True) + (2, sqrt(2)*(1 + 2*sqrt(5))) + + If clear=False (default is True) then content will not be removed + from an Add if it can be distributed to leave one or more + terms with integer coefficients. + + >>> (x/2 + y).as_content_primitive() + (1/2, x + 2*y) + >>> (x/2 + y).as_content_primitive(clear=False) + (1, x/2 + y) + """ + return S.One, self + + def as_numer_denom(self): + """Return the numerator and the denominator of an expression. + + expression -> a/b -> a, b + + This is just a stub that should be defined by + an object's class methods to get anything else. + + See Also + ======== + + normal: return ``a/b`` instead of ``(a, b)`` + + """ + return self, S.One + + def normal(self): + """Return the expression as a fraction. + + expression -> a/b + + See Also + ======== + + as_numer_denom: return ``(a, b)`` instead of ``a/b`` + + """ + from .mul import _unevaluated_Mul + n, d = self.as_numer_denom() + if d is S.One: + return n + if d.is_Number: + return _unevaluated_Mul(n, 1/d) + else: + return n/d + + def extract_multiplicatively(self, c): + """Return None if it's not possible to make self in the form + c * something in a nice way, i.e. preserving the properties + of arguments of self. + + Examples + ======== + + >>> from sympy import symbols, Rational + + >>> x, y = symbols('x,y', real=True) + + >>> ((x*y)**3).extract_multiplicatively(x**2 * y) + x*y**2 + + >>> ((x*y)**3).extract_multiplicatively(x**4 * y) + + >>> (2*x).extract_multiplicatively(2) + x + + >>> (2*x).extract_multiplicatively(3) + + >>> (Rational(1, 2)*x).extract_multiplicatively(3) + x/6 + + """ + from sympy.functions.elementary.exponential import exp + from .add import _unevaluated_Add + c = sympify(c) + if self is S.NaN: + return None + if c is S.One: + return self + elif c == self: + return S.One + + if c.is_Add: + cc, pc = c.primitive() + if cc is not S.One: + c = Mul(cc, pc, evaluate=False) + + if c.is_Mul: + a, b = c.as_two_terms() + x = self.extract_multiplicatively(a) + if x is not None: + return x.extract_multiplicatively(b) + else: + return x + + quotient = self / c + if self.is_Number: + if self is S.Infinity: + if c.is_positive: + return S.Infinity + elif self is S.NegativeInfinity: + if c.is_negative: + return S.Infinity + elif c.is_positive: + return S.NegativeInfinity + elif self is S.ComplexInfinity: + if not c.is_zero: + return S.ComplexInfinity + elif self.is_Integer: + if not quotient.is_Integer: + return None + elif self.is_positive and quotient.is_negative: + return None + else: + return quotient + elif self.is_Rational: + if not quotient.is_Rational: + return None + elif self.is_positive and quotient.is_negative: + return None + else: + return quotient + elif self.is_Float: + if not quotient.is_Float: + return None + elif self.is_positive and quotient.is_negative: + return None + else: + return quotient + elif self.is_NumberSymbol or self.is_Symbol or self is S.ImaginaryUnit: + if quotient.is_Mul and len(quotient.args) == 2: + if quotient.args[0].is_Integer and quotient.args[0].is_positive and quotient.args[1] == self: + return quotient + elif quotient.is_Integer and c.is_Number: + return quotient + elif self.is_Add: + cs, ps = self.primitive() + # assert cs >= 1 + if c.is_Number and c is not S.NegativeOne: + # assert c != 1 (handled at top) + if cs is not S.One: + if c.is_negative: + xc = -(cs.extract_multiplicatively(-c)) + else: + xc = cs.extract_multiplicatively(c) + if xc is not None: + return xc*ps # rely on 2-arg Mul to restore Add + return # |c| != 1 can only be extracted from cs + if c == ps: + return cs + # check args of ps + newargs = [] + for arg in ps.args: + newarg = arg.extract_multiplicatively(c) + if newarg is None: + return # all or nothing + newargs.append(newarg) + if cs is not S.One: + args = [cs*t for t in newargs] + # args may be in different order + return _unevaluated_Add(*args) + else: + return Add._from_args(newargs) + elif self.is_Mul: + args = list(self.args) + for i, arg in enumerate(args): + newarg = arg.extract_multiplicatively(c) + if newarg is not None: + args[i] = newarg + return Mul(*args) + elif self.is_Pow or isinstance(self, exp): + sb, se = self.as_base_exp() + cb, ce = c.as_base_exp() + if cb == sb: + new_exp = se.extract_additively(ce) + if new_exp is not None: + return Pow(sb, new_exp) + elif c == sb: + new_exp = self.exp.extract_additively(1) + if new_exp is not None: + return Pow(sb, new_exp) + + def extract_additively(self, c): + """Return self - c if it's possible to subtract c from self and + make all matching coefficients move towards zero, else return None. + + Examples + ======== + + >>> from sympy.abc import x, y + >>> e = 2*x + 3 + >>> e.extract_additively(x + 1) + x + 2 + >>> e.extract_additively(3*x) + >>> e.extract_additively(4) + >>> (y*(x + 1)).extract_additively(x + 1) + >>> ((x + 1)*(x + 2*y + 1) + 3).extract_additively(x + 1) + (x + 1)*(x + 2*y) + 3 + + See Also + ======== + extract_multiplicatively + coeff + as_coefficient + + """ + + c = sympify(c) + if self is S.NaN: + return None + if c.is_zero: + return self + elif c == self: + return S.Zero + elif self == S.Zero: + return None + + if self.is_Number: + if not c.is_Number: + return None + co = self + diff = co - c + # XXX should we match types? i.e should 3 - .1 succeed? + if (co > 0 and diff >= 0 and diff < co or + co < 0 and diff <= 0 and diff > co): + return diff + return None + + if c.is_Number: + co, t = self.as_coeff_Add() + xa = co.extract_additively(c) + if xa is None: + return None + return xa + t + + # handle the args[0].is_Number case separately + # since we will have trouble looking for the coeff of + # a number. + if c.is_Add and c.args[0].is_Number: + # whole term as a term factor + co = self.coeff(c) + xa0 = (co.extract_additively(1) or 0)*c + if xa0: + diff = self - co*c + return (xa0 + (diff.extract_additively(c) or diff)) or None + # term-wise + h, t = c.as_coeff_Add() + sh, st = self.as_coeff_Add() + xa = sh.extract_additively(h) + if xa is None: + return None + xa2 = st.extract_additively(t) + if xa2 is None: + return None + return xa + xa2 + + # whole term as a term factor + co, diff = _corem(self, c) + xa0 = (co.extract_additively(1) or 0)*c + if xa0: + return (xa0 + (diff.extract_additively(c) or diff)) or None + # term-wise + coeffs = [] + for a in Add.make_args(c): + ac, at = a.as_coeff_Mul() + co = self.coeff(at) + if not co: + return None + coc, cot = co.as_coeff_Add() + xa = coc.extract_additively(ac) + if xa is None: + return None + self -= co*at + coeffs.append((cot + xa)*at) + coeffs.append(self) + return Add(*coeffs) + + @property + def expr_free_symbols(self): + """ + Like ``free_symbols``, but returns the free symbols only if + they are contained in an expression node. + + Examples + ======== + + >>> from sympy.abc import x, y + >>> (x + y).expr_free_symbols # doctest: +SKIP + {x, y} + + If the expression is contained in a non-expression object, do not return + the free symbols. Compare: + + >>> from sympy import Tuple + >>> t = Tuple(x + y) + >>> t.expr_free_symbols # doctest: +SKIP + set() + >>> t.free_symbols + {x, y} + """ + sympy_deprecation_warning(""" + The expr_free_symbols property is deprecated. Use free_symbols to get + the free symbols of an expression. + """, + deprecated_since_version="1.9", + active_deprecations_target="deprecated-expr-free-symbols") + return {j for i in self.args for j in i.expr_free_symbols} + + def could_extract_minus_sign(self): + """Return True if self has -1 as a leading factor or has + more literal negative signs than positive signs in a sum, + otherwise False. + + Examples + ======== + + >>> from sympy.abc import x, y + >>> e = x - y + >>> {i.could_extract_minus_sign() for i in (e, -e)} + {False, True} + + Though the ``y - x`` is considered like ``-(x - y)``, since it + is in a product without a leading factor of -1, the result is + false below: + + >>> (x*(y - x)).could_extract_minus_sign() + False + + To put something in canonical form wrt to sign, use `signsimp`: + + >>> from sympy import signsimp + >>> signsimp(x*(y - x)) + -x*(x - y) + >>> _.could_extract_minus_sign() + True + """ + return False + + def extract_branch_factor(self, allow_half=False): + """ + Try to write self as ``exp_polar(2*pi*I*n)*z`` in a nice way. + Return (z, n). + + >>> from sympy import exp_polar, I, pi + >>> from sympy.abc import x, y + >>> exp_polar(I*pi).extract_branch_factor() + (exp_polar(I*pi), 0) + >>> exp_polar(2*I*pi).extract_branch_factor() + (1, 1) + >>> exp_polar(-pi*I).extract_branch_factor() + (exp_polar(I*pi), -1) + >>> exp_polar(3*pi*I + x).extract_branch_factor() + (exp_polar(x + I*pi), 1) + >>> (y*exp_polar(-5*pi*I)*exp_polar(3*pi*I + 2*pi*x)).extract_branch_factor() + (y*exp_polar(2*pi*x), -1) + >>> exp_polar(-I*pi/2).extract_branch_factor() + (exp_polar(-I*pi/2), 0) + + If allow_half is True, also extract exp_polar(I*pi): + + >>> exp_polar(I*pi).extract_branch_factor(allow_half=True) + (1, 1/2) + >>> exp_polar(2*I*pi).extract_branch_factor(allow_half=True) + (1, 1) + >>> exp_polar(3*I*pi).extract_branch_factor(allow_half=True) + (1, 3/2) + >>> exp_polar(-I*pi).extract_branch_factor(allow_half=True) + (1, -1/2) + """ + from sympy.functions.elementary.exponential import exp_polar + from sympy.functions.elementary.integers import ceiling + + n = S.Zero + res = S.One + args = Mul.make_args(self) + exps = [] + for arg in args: + if isinstance(arg, exp_polar): + exps += [arg.exp] + else: + res *= arg + piimult = S.Zero + extras = [] + + ipi = S.Pi*S.ImaginaryUnit + while exps: + exp = exps.pop() + if exp.is_Add: + exps += exp.args + continue + if exp.is_Mul: + coeff = exp.as_coefficient(ipi) + if coeff is not None: + piimult += coeff + continue + extras += [exp] + if piimult.is_number: + coeff = piimult + tail = () + else: + coeff, tail = piimult.as_coeff_add(*piimult.free_symbols) + # round down to nearest multiple of 2 + branchfact = ceiling(coeff/2 - S.Half)*2 + n += branchfact/2 + c = coeff - branchfact + if allow_half: + nc = c.extract_additively(1) + if nc is not None: + n += S.Half + c = nc + newexp = ipi*Add(*((c, ) + tail)) + Add(*extras) + if newexp != 0: + res *= exp_polar(newexp) + return res, n + + def is_polynomial(self, *syms): + r""" + Return True if self is a polynomial in syms and False otherwise. + + This checks if self is an exact polynomial in syms. This function + returns False for expressions that are "polynomials" with symbolic + exponents. Thus, you should be able to apply polynomial algorithms to + expressions for which this returns True, and Poly(expr, \*syms) should + work if and only if expr.is_polynomial(\*syms) returns True. The + polynomial does not have to be in expanded form. If no symbols are + given, all free symbols in the expression will be used. + + This is not part of the assumptions system. You cannot do + Symbol('z', polynomial=True). + + Examples + ======== + + >>> from sympy import Symbol, Function + >>> x = Symbol('x') + >>> ((x**2 + 1)**4).is_polynomial(x) + True + >>> ((x**2 + 1)**4).is_polynomial() + True + >>> (2**x + 1).is_polynomial(x) + False + >>> (2**x + 1).is_polynomial(2**x) + True + >>> f = Function('f') + >>> (f(x) + 1).is_polynomial(x) + False + >>> (f(x) + 1).is_polynomial(f(x)) + True + >>> (1/f(x) + 1).is_polynomial(f(x)) + False + + >>> n = Symbol('n', nonnegative=True, integer=True) + >>> (x**n + 1).is_polynomial(x) + False + + This function does not attempt any nontrivial simplifications that may + result in an expression that does not appear to be a polynomial to + become one. + + >>> from sympy import sqrt, factor, cancel + >>> y = Symbol('y', positive=True) + >>> a = sqrt(y**2 + 2*y + 1) + >>> a.is_polynomial(y) + False + >>> factor(a) + y + 1 + >>> factor(a).is_polynomial(y) + True + + >>> b = (y**2 + 2*y + 1)/(y + 1) + >>> b.is_polynomial(y) + False + >>> cancel(b) + y + 1 + >>> cancel(b).is_polynomial(y) + True + + See also .is_rational_function() + + """ + if syms: + syms = set(map(sympify, syms)) + else: + syms = self.free_symbols + if not syms: + return True + + return self._eval_is_polynomial(syms) + + def _eval_is_polynomial(self, syms): + if self in syms: + return True + if not self.has_free(*syms): + # constant polynomial + return True + # subclasses should return True or False + + def is_rational_function(self, *syms): + """ + Test whether function is a ratio of two polynomials in the given + symbols, syms. When syms is not given, all free symbols will be used. + The rational function does not have to be in expanded or in any kind of + canonical form. + + This function returns False for expressions that are "rational + functions" with symbolic exponents. Thus, you should be able to call + .as_numer_denom() and apply polynomial algorithms to the result for + expressions for which this returns True. + + This is not part of the assumptions system. You cannot do + Symbol('z', rational_function=True). + + Examples + ======== + + >>> from sympy import Symbol, sin + >>> from sympy.abc import x, y + + >>> (x/y).is_rational_function() + True + + >>> (x**2).is_rational_function() + True + + >>> (x/sin(y)).is_rational_function(y) + False + + >>> n = Symbol('n', integer=True) + >>> (x**n + 1).is_rational_function(x) + False + + This function does not attempt any nontrivial simplifications that may + result in an expression that does not appear to be a rational function + to become one. + + >>> from sympy import sqrt, factor + >>> y = Symbol('y', positive=True) + >>> a = sqrt(y**2 + 2*y + 1)/y + >>> a.is_rational_function(y) + False + >>> factor(a) + (y + 1)/y + >>> factor(a).is_rational_function(y) + True + + See also is_algebraic_expr(). + + """ + if syms: + syms = set(map(sympify, syms)) + else: + syms = self.free_symbols + if not syms: + return self not in _illegal + + return self._eval_is_rational_function(syms) + + def _eval_is_rational_function(self, syms): + if self in syms: + return True + if not self.has_xfree(syms): + return True + # subclasses should return True or False + + def is_meromorphic(self, x, a): + """ + This tests whether an expression is meromorphic as + a function of the given symbol ``x`` at the point ``a``. + + This method is intended as a quick test that will return + None if no decision can be made without simplification or + more detailed analysis. + + Examples + ======== + + >>> from sympy import zoo, log, sin, sqrt + >>> from sympy.abc import x + + >>> f = 1/x**2 + 1 - 2*x**3 + >>> f.is_meromorphic(x, 0) + True + >>> f.is_meromorphic(x, 1) + True + >>> f.is_meromorphic(x, zoo) + True + + >>> g = x**log(3) + >>> g.is_meromorphic(x, 0) + False + >>> g.is_meromorphic(x, 1) + True + >>> g.is_meromorphic(x, zoo) + False + + >>> h = sin(1/x)*x**2 + >>> h.is_meromorphic(x, 0) + False + >>> h.is_meromorphic(x, 1) + True + >>> h.is_meromorphic(x, zoo) + True + + Multivalued functions are considered meromorphic when their + branches are meromorphic. Thus most functions are meromorphic + everywhere except at essential singularities and branch points. + In particular, they will be meromorphic also on branch cuts + except at their endpoints. + + >>> log(x).is_meromorphic(x, -1) + True + >>> log(x).is_meromorphic(x, 0) + False + >>> sqrt(x).is_meromorphic(x, -1) + True + >>> sqrt(x).is_meromorphic(x, 0) + False + + """ + if not x.is_symbol: + raise TypeError("{} should be of symbol type".format(x)) + a = sympify(a) + + return self._eval_is_meromorphic(x, a) + + def _eval_is_meromorphic(self, x, a): + if self == x: + return True + if not self.has_free(x): + return True + # subclasses should return True or False + + def is_algebraic_expr(self, *syms): + """ + This tests whether a given expression is algebraic or not, in the + given symbols, syms. When syms is not given, all free symbols + will be used. The rational function does not have to be in expanded + or in any kind of canonical form. + + This function returns False for expressions that are "algebraic + expressions" with symbolic exponents. This is a simple extension to the + is_rational_function, including rational exponentiation. + + Examples + ======== + + >>> from sympy import Symbol, sqrt + >>> x = Symbol('x', real=True) + >>> sqrt(1 + x).is_rational_function() + False + >>> sqrt(1 + x).is_algebraic_expr() + True + + This function does not attempt any nontrivial simplifications that may + result in an expression that does not appear to be an algebraic + expression to become one. + + >>> from sympy import exp, factor + >>> a = sqrt(exp(x)**2 + 2*exp(x) + 1)/(exp(x) + 1) + >>> a.is_algebraic_expr(x) + False + >>> factor(a).is_algebraic_expr() + True + + See Also + ======== + + is_rational_function + + References + ========== + + .. [1] https://en.wikipedia.org/wiki/Algebraic_expression + + """ + if syms: + syms = set(map(sympify, syms)) + else: + syms = self.free_symbols + if not syms: + return True + + return self._eval_is_algebraic_expr(syms) + + def _eval_is_algebraic_expr(self, syms): + if self in syms: + return True + if not self.has_free(*syms): + return True + # subclasses should return True or False + + ################################################################################### + ##################### SERIES, LEADING TERM, LIMIT, ORDER METHODS ################## + ################################################################################### + + def series(self, x=None, x0=0, n=6, dir="+", logx=None, cdir=0): + """ + Series expansion of "self" around ``x = x0`` yielding either terms of + the series one by one (the lazy series given when n=None), else + all the terms at once when n != None. + + Returns the series expansion of "self" around the point ``x = x0`` + with respect to ``x`` up to ``O((x - x0)**n, x, x0)`` (default n is 6). + + If ``x=None`` and ``self`` is univariate, the univariate symbol will + be supplied, otherwise an error will be raised. + + Parameters + ========== + + expr : Expression + The expression whose series is to be expanded. + + x : Symbol + It is the variable of the expression to be calculated. + + x0 : Value + The value around which ``x`` is calculated. Can be any value + from ``-oo`` to ``oo``. + + n : Value + The value used to represent the order in terms of ``x**n``, + up to which the series is to be expanded. + + dir : String, optional + The series-expansion can be bi-directional. If ``dir="+"``, + then (x->x0+). If ``dir="-", then (x->x0-). For infinite + ``x0`` (``oo`` or ``-oo``), the ``dir`` argument is determined + from the direction of the infinity (i.e., ``dir="-"`` for + ``oo``). + + logx : optional + It is used to replace any log(x) in the returned series with a + symbolic value rather than evaluating the actual value. + + cdir : optional + It stands for complex direction, and indicates the direction + from which the expansion needs to be evaluated. + + Examples + ======== + + >>> from sympy import cos, exp, tan + >>> from sympy.abc import x, y + >>> cos(x).series() + 1 - x**2/2 + x**4/24 + O(x**6) + >>> cos(x).series(n=4) + 1 - x**2/2 + O(x**4) + >>> cos(x).series(x, x0=1, n=2) + cos(1) - (x - 1)*sin(1) + O((x - 1)**2, (x, 1)) + >>> e = cos(x + exp(y)) + >>> e.series(y, n=2) + cos(x + 1) - y*sin(x + 1) + O(y**2) + >>> e.series(x, n=2) + cos(exp(y)) - x*sin(exp(y)) + O(x**2) + + If ``n=None`` then a generator of the series terms will be returned. + + >>> term=cos(x).series(n=None) + >>> [next(term) for i in range(2)] + [1, -x**2/2] + + For ``dir=+`` (default) the series is calculated from the right and + for ``dir=-`` the series from the left. For smooth functions this + flag will not alter the results. + + >>> abs(x).series(dir="+") + x + >>> abs(x).series(dir="-") + -x + >>> f = tan(x) + >>> f.series(x, 2, 6, "+") + tan(2) + (1 + tan(2)**2)*(x - 2) + (x - 2)**2*(tan(2)**3 + tan(2)) + + (x - 2)**3*(1/3 + 4*tan(2)**2/3 + tan(2)**4) + (x - 2)**4*(tan(2)**5 + + 5*tan(2)**3/3 + 2*tan(2)/3) + (x - 2)**5*(2/15 + 17*tan(2)**2/15 + + 2*tan(2)**4 + tan(2)**6) + O((x - 2)**6, (x, 2)) + + >>> f.series(x, 2, 3, "-") + tan(2) + (2 - x)*(-tan(2)**2 - 1) + (2 - x)**2*(tan(2)**3 + tan(2)) + + O((x - 2)**3, (x, 2)) + + For rational expressions this method may return original expression without the Order term. + >>> (1/x).series(x, n=8) + 1/x + + Returns + ======= + + Expr : Expression + Series expansion of the expression about x0 + + Raises + ====== + + TypeError + If "n" and "x0" are infinity objects + + PoleError + If "x0" is an infinity object + + """ + if x is None: + syms = self.free_symbols + if not syms: + return self + elif len(syms) > 1: + raise ValueError('x must be given for multivariate functions.') + x = syms.pop() + + from .symbol import Dummy, Symbol + if isinstance(x, Symbol): + dep = x in self.free_symbols + else: + d = Dummy() + dep = d in self.xreplace({x: d}).free_symbols + if not dep: + if n is None: + return (s for s in [self]) + else: + return self + + if len(dir) != 1 or dir not in '+-': + raise ValueError("Dir must be '+' or '-'") + + x0 = sympify(x0) + cdir = sympify(cdir) + from sympy.functions.elementary.complexes import im, sign + + if not cdir.is_zero: + if cdir.is_real: + dir = '+' if cdir.is_positive else '-' + else: + dir = '+' if im(cdir).is_positive else '-' + else: + if x0 and x0.is_infinite: + cdir = sign(x0).simplify() + elif str(dir) == "+": + cdir = S.One + elif str(dir) == "-": + cdir = S.NegativeOne + elif cdir == S.Zero: + cdir = S.One + + cdir = cdir/abs(cdir) + + if x0 and x0.is_infinite: + from .function import PoleError + try: + s = self.subs(x, cdir/x).series(x, n=n, dir='+', cdir=1) + if n is None: + return (si.subs(x, cdir/x) for si in s) + return s.subs(x, cdir/x) + except PoleError: + s = self.subs(x, cdir*x).aseries(x, n=n) + return s.subs(x, cdir*x) + + # use rep to shift origin to x0 and change sign (if dir is negative) + # and undo the process with rep2 + if x0 or cdir != 1: + s = self.subs({x: x0 + cdir*x}).series(x, x0=0, n=n, dir='+', logx=logx, cdir=1) + if n is None: # lseries... + return (si.subs({x: x/cdir - x0/cdir}) for si in s) + return s.subs({x: x/cdir - x0/cdir}) + + # from here on it's x0=0 and dir='+' handling + + if x.is_positive is x.is_negative is None or x.is_Symbol is not True: + # replace x with an x that has a positive assumption + xpos = Dummy('x', positive=True) + rv = self.subs(x, xpos).series(xpos, x0, n, dir, logx=logx, cdir=cdir) + if n is None: + return (s.subs(xpos, x) for s in rv) + else: + return rv.subs(xpos, x) + + from sympy.series.order import Order + if n is not None: # nseries handling + s1 = self._eval_nseries(x, n=n, logx=logx, cdir=cdir) + o = s1.getO() or S.Zero + if o: + # make sure the requested order is returned + ngot = o.getn() + if ngot > n: + # leave o in its current form (e.g. with x*log(x)) so + # it eats terms properly, then replace it below + if n != 0: + s1 += o.subs(x, x**Rational(n, ngot)) + else: + s1 += Order(1, x) + elif ngot < n: + # increase the requested number of terms to get the desired + # number keep increasing (up to 9) until the received order + # is different than the original order and then predict how + # many additional terms are needed + from sympy.functions.elementary.integers import ceiling + for more in range(1, 9): + s1 = self._eval_nseries(x, n=n + more, logx=logx, cdir=cdir) + newn = s1.getn() + if newn != ngot: + ndo = n + ceiling((n - ngot)*more/(newn - ngot)) + s1 = self._eval_nseries(x, n=ndo, logx=logx, cdir=cdir) + while s1.getn() < n: + s1 = self._eval_nseries(x, n=ndo, logx=logx, cdir=cdir) + ndo += 1 + break + else: + raise ValueError('Could not calculate %s terms for %s' + % (str(n), self)) + s1 += Order(x**n, x) + o = s1.getO() + s1 = s1.removeO() + elif s1.has(Order): + # asymptotic expansion + return s1 + else: + o = Order(x**n, x) + s1done = s1.doit() + try: + if (s1done + o).removeO() == s1done: + o = S.Zero + except NotImplementedError: + return s1 + + try: + from sympy.simplify.radsimp import collect + return collect(s1, x) + o + except NotImplementedError: + return s1 + o + + else: # lseries handling + def yield_lseries(s): + """Return terms of lseries one at a time.""" + for si in s: + if not si.is_Add: + yield si + continue + # yield terms 1 at a time if possible + # by increasing order until all the + # terms have been returned + yielded = 0 + o = Order(si, x)*x + ndid = 0 + ndo = len(si.args) + while 1: + do = (si - yielded + o).removeO() + o *= x + if not do or do.is_Order: + continue + if do.is_Add: + ndid += len(do.args) + else: + ndid += 1 + yield do + if ndid == ndo: + break + yielded += do + + return yield_lseries(self.removeO()._eval_lseries(x, logx=logx, cdir=cdir)) + + def aseries(self, x=None, n=6, bound=0, hir=False): + """Asymptotic Series expansion of self. + This is equivalent to ``self.series(x, oo, n)``. + + Parameters + ========== + + self : Expression + The expression whose series is to be expanded. + + x : Symbol + It is the variable of the expression to be calculated. + + n : Value + The value used to represent the order in terms of ``x**n``, + up to which the series is to be expanded. + + hir : Boolean + Set this parameter to be True to produce hierarchical series. + It stops the recursion at an early level and may provide nicer + and more useful results. + + bound : Value, Integer + Use the ``bound`` parameter to give limit on rewriting + coefficients in its normalised form. + + Examples + ======== + + >>> from sympy import sin, exp + >>> from sympy.abc import x + + >>> e = sin(1/x + exp(-x)) - sin(1/x) + + >>> e.aseries(x) + (1/(24*x**4) - 1/(2*x**2) + 1 + O(x**(-6), (x, oo)))*exp(-x) + + >>> e.aseries(x, n=3, hir=True) + -exp(-2*x)*sin(1/x)/2 + exp(-x)*cos(1/x) + O(exp(-3*x), (x, oo)) + + >>> e = exp(exp(x)/(1 - 1/x)) + + >>> e.aseries(x) + exp(exp(x)/(1 - 1/x)) + + >>> e.aseries(x, bound=3) # doctest: +SKIP + exp(exp(x)/x**2)*exp(exp(x)/x)*exp(-exp(x) + exp(x)/(1 - 1/x) - exp(x)/x - exp(x)/x**2)*exp(exp(x)) + + For rational expressions this method may return original expression without the Order term. + >>> (1/x).aseries(x, n=8) + 1/x + + Returns + ======= + + Expr + Asymptotic series expansion of the expression. + + Notes + ===== + + This algorithm is directly induced from the limit computational algorithm provided by Gruntz. + It majorly uses the mrv and rewrite sub-routines. The overall idea of this algorithm is first + to look for the most rapidly varying subexpression w of a given expression f and then expands f + in a series in w. Then same thing is recursively done on the leading coefficient + till we get constant coefficients. + + If the most rapidly varying subexpression of a given expression f is f itself, + the algorithm tries to find a normalised representation of the mrv set and rewrites f + using this normalised representation. + + If the expansion contains an order term, it will be either ``O(x ** (-n))`` or ``O(w ** (-n))`` + where ``w`` belongs to the most rapidly varying expression of ``self``. + + References + ========== + + .. [1] Gruntz, Dominik. A new algorithm for computing asymptotic series. + In: Proc. 1993 Int. Symp. Symbolic and Algebraic Computation. 1993. + pp. 239-244. + .. [2] Gruntz thesis - p90 + .. [3] https://en.wikipedia.org/wiki/Asymptotic_expansion + + See Also + ======== + + Expr.aseries: See the docstring of this function for complete details of this wrapper. + """ + + from .symbol import Dummy + + if x.is_positive is x.is_negative is None: + xpos = Dummy('x', positive=True) + return self.subs(x, xpos).aseries(xpos, n, bound, hir).subs(xpos, x) + + from .function import PoleError + from sympy.series.gruntz import mrv, rewrite + + try: + om, exps = mrv(self, x) + except PoleError: + return self + + # We move one level up by replacing `x` by `exp(x)`, and then + # computing the asymptotic series for f(exp(x)). Then asymptotic series + # can be obtained by moving one-step back, by replacing x by ln(x). + + from sympy.functions.elementary.exponential import exp, log + from sympy.series.order import Order + + if x in om: + s = self.subs(x, exp(x)).aseries(x, n, bound, hir).subs(x, log(x)) + if s.getO(): + return s + Order(1/x**n, (x, S.Infinity)) + return s + + k = Dummy('k', positive=True) + # f is rewritten in terms of omega + func, logw = rewrite(exps, om, x, k) + + if self in om: + if bound <= 0: + return self + s = (self.exp).aseries(x, n, bound=bound) + s = s.func(*[t.removeO() for t in s.args]) + try: + res = exp(s.subs(x, 1/x).as_leading_term(x).subs(x, 1/x)) + except PoleError: + res = self + + func = exp(self.args[0] - res.args[0]) / k + logw = log(1/res) + + s = func.series(k, 0, n) + + # Hierarchical series + if hir: + return s.subs(k, exp(logw)) + + o = s.getO() + terms = sorted(Add.make_args(s.removeO()), key=lambda i: int(i.as_coeff_exponent(k)[1])) + s = S.Zero + has_ord = False + + # Then we recursively expand these coefficients one by one into + # their asymptotic series in terms of their most rapidly varying subexpressions. + for t in terms: + coeff, expo = t.as_coeff_exponent(k) + if coeff.has(x): + # Recursive step + snew = coeff.aseries(x, n, bound=bound-1) + if has_ord and snew.getO(): + break + elif snew.getO(): + has_ord = True + s += (snew * k**expo) + else: + s += t + + if not o or has_ord: + return s.subs(k, exp(logw)) + return (s + o).subs(k, exp(logw)) + + + def taylor_term(self, n, x, *previous_terms): + """General method for the taylor term. + + This method is slow, because it differentiates n-times. Subclasses can + redefine it to make it faster by using the "previous_terms". + """ + from .symbol import Dummy + from sympy.functions.combinatorial.factorials import factorial + + x = sympify(x) + _x = Dummy('x') + return self.subs(x, _x).diff(_x, n).subs(_x, x).subs(x, 0) * x**n / factorial(n) + + def lseries(self, x=None, x0=0, dir='+', logx=None, cdir=0): + """ + Wrapper for series yielding an iterator of the terms of the series. + + Note: an infinite series will yield an infinite iterator. The following, + for exaxmple, will never terminate. It will just keep printing terms + of the sin(x) series:: + + for term in sin(x).lseries(x): + print term + + The advantage of lseries() over nseries() is that many times you are + just interested in the next term in the series (i.e. the first term for + example), but you do not know how many you should ask for in nseries() + using the "n" parameter. + + See also nseries(). + """ + return self.series(x, x0, n=None, dir=dir, logx=logx, cdir=cdir) + + def _eval_lseries(self, x, logx=None, cdir=0): + # default implementation of lseries is using nseries(), and adaptively + # increasing the "n". As you can see, it is not very efficient, because + # we are calculating the series over and over again. Subclasses should + # override this method and implement much more efficient yielding of + # terms. + n = 0 + series = self._eval_nseries(x, n=n, logx=logx, cdir=cdir) + + while series.is_Order: + n += 1 + series = self._eval_nseries(x, n=n, logx=logx, cdir=cdir) + + e = series.removeO() + yield e + if e is S.Zero: + return + + while 1: + while 1: + n += 1 + series = self._eval_nseries(x, n=n, logx=logx, cdir=cdir).removeO() + if e != series: + break + if (series - self).cancel() is S.Zero: + return + yield series - e + e = series + + def nseries(self, x=None, x0=0, n=6, dir='+', logx=None, cdir=0): + """ + Wrapper to _eval_nseries if assumptions allow, else to series. + + If x is given, x0 is 0, dir='+', and self has x, then _eval_nseries is + called. This calculates "n" terms in the innermost expressions and + then builds up the final series just by "cross-multiplying" everything + out. + + The optional ``logx`` parameter can be used to replace any log(x) in the + returned series with a symbolic value to avoid evaluating log(x) at 0. A + symbol to use in place of log(x) should be provided. + + Advantage -- it's fast, because we do not have to determine how many + terms we need to calculate in advance. + + Disadvantage -- you may end up with less terms than you may have + expected, but the O(x**n) term appended will always be correct and + so the result, though perhaps shorter, will also be correct. + + If any of those assumptions is not met, this is treated like a + wrapper to series which will try harder to return the correct + number of terms. + + See also lseries(). + + Examples + ======== + + >>> from sympy import sin, log, Symbol + >>> from sympy.abc import x, y + >>> sin(x).nseries(x, 0, 6) + x - x**3/6 + x**5/120 + O(x**6) + >>> log(x+1).nseries(x, 0, 5) + x - x**2/2 + x**3/3 - x**4/4 + O(x**5) + + Handling of the ``logx`` parameter --- in the following example the + expansion fails since ``sin`` does not have an asymptotic expansion + at -oo (the limit of log(x) as x approaches 0): + + >>> e = sin(log(x)) + >>> e.nseries(x, 0, 6) + Traceback (most recent call last): + ... + PoleError: ... + ... + >>> logx = Symbol('logx') + >>> e.nseries(x, 0, 6, logx=logx) + sin(logx) + + In the following example, the expansion works but only returns self + unless the ``logx`` parameter is used: + + >>> e = x**y + >>> e.nseries(x, 0, 2) + x**y + >>> e.nseries(x, 0, 2, logx=logx) + exp(logx*y) + + """ + if x and x not in self.free_symbols: + return self + if x is None or x0 or dir != '+': # {see XPOS above} or (x.is_positive == x.is_negative == None): + return self.series(x, x0, n, dir, cdir=cdir) + else: + return self._eval_nseries(x, n=n, logx=logx, cdir=cdir) + + def _eval_nseries(self, x, n, logx, cdir): + """ + Return terms of series for self up to O(x**n) at x=0 + from the positive direction. + + This is a method that should be overridden in subclasses. Users should + never call this method directly (use .nseries() instead), so you do not + have to write docstrings for _eval_nseries(). + """ + raise NotImplementedError(filldedent(""" + The _eval_nseries method should be added to + %s to give terms up to O(x**n) at x=0 + from the positive direction so it is available when + nseries calls it.""" % self.func) + ) + + def limit(self, x, xlim, dir='+'): + """ Compute limit x->xlim. + """ + from sympy.series.limits import limit + return limit(self, x, xlim, dir) + + def compute_leading_term(self, x, logx=None): + """Deprecated function to compute the leading term of a series. + + as_leading_term is only allowed for results of .series() + This is a wrapper to compute a series first. + """ + from sympy.utilities.exceptions import SymPyDeprecationWarning + + SymPyDeprecationWarning( + feature="compute_leading_term", + useinstead="as_leading_term", + issue=21843, + deprecated_since_version="1.12" + ).warn() + + from sympy.functions.elementary.piecewise import Piecewise, piecewise_fold + if self.has(Piecewise): + expr = piecewise_fold(self) + else: + expr = self + if self.removeO() == 0: + return self + + from .symbol import Dummy + from sympy.functions.elementary.exponential import log + from sympy.series.order import Order + + _logx = logx + logx = Dummy('logx') if logx is None else logx + res = Order(1) + incr = S.One + while res.is_Order: + res = expr._eval_nseries(x, n=1+incr, logx=logx).cancel().powsimp().trigsimp() + incr *= 2 + + if _logx is None: + res = res.subs(logx, log(x)) + + return res.as_leading_term(x) + + @cacheit + def as_leading_term(self, *symbols, logx=None, cdir=0): + """ + Returns the leading (nonzero) term of the series expansion of self. + + The _eval_as_leading_term routines are used to do this, and they must + always return a non-zero value. + + Examples + ======== + + >>> from sympy.abc import x + >>> (1 + x + x**2).as_leading_term(x) + 1 + >>> (1/x**2 + x + x**2).as_leading_term(x) + x**(-2) + + """ + if len(symbols) > 1: + c = self + for x in symbols: + c = c.as_leading_term(x, logx=logx, cdir=cdir) + return c + elif not symbols: + return self + x = sympify(symbols[0]) + if not x.is_symbol: + raise ValueError('expecting a Symbol but got %s' % x) + if x not in self.free_symbols: + return self + obj = self._eval_as_leading_term(x, logx=logx, cdir=cdir) + if obj is not None: + from sympy.simplify.powsimp import powsimp + return powsimp(obj, deep=True, combine='exp') + raise NotImplementedError('as_leading_term(%s, %s)' % (self, x)) + + def _eval_as_leading_term(self, x, logx=None, cdir=0): + return self + + def as_coeff_exponent(self, x) -> tuple[Expr, Expr]: + """ ``c*x**e -> c,e`` where x can be any symbolic expression. + """ + from sympy.simplify.radsimp import collect + s = collect(self, x) + c, p = s.as_coeff_mul(x) + if len(p) == 1: + b, e = p[0].as_base_exp() + if b == x: + return c, e + return s, S.Zero + + def leadterm(self, x, logx=None, cdir=0): + """ + Returns the leading term a*x**b as a tuple (a, b). + + Examples + ======== + + >>> from sympy.abc import x + >>> (1+x+x**2).leadterm(x) + (1, 0) + >>> (1/x**2+x+x**2).leadterm(x) + (1, -2) + + """ + from .symbol import Dummy + from sympy.functions.elementary.exponential import log + l = self.as_leading_term(x, logx=logx, cdir=cdir) + d = Dummy('logx') + if l.has(log(x)): + l = l.subs(log(x), d) + c, e = l.as_coeff_exponent(x) + if x in c.free_symbols: + raise ValueError(filldedent(""" + cannot compute leadterm(%s, %s). The coefficient + should have been free of %s but got %s""" % (self, x, x, c))) + c = c.subs(d, log(x)) + return c, e + + def as_coeff_Mul(self, rational: bool = False) -> tuple['Number', Expr]: + """Efficiently extract the coefficient of a product.""" + return S.One, self + + def as_coeff_Add(self, rational=False) -> tuple['Number', Expr]: + """Efficiently extract the coefficient of a summation.""" + return S.Zero, self + + def fps(self, x=None, x0=0, dir=1, hyper=True, order=4, rational=True, + full=False): + """ + Compute formal power power series of self. + + See the docstring of the :func:`fps` function in sympy.series.formal for + more information. + """ + from sympy.series.formal import fps + + return fps(self, x, x0, dir, hyper, order, rational, full) + + def fourier_series(self, limits=None): + """Compute fourier sine/cosine series of self. + + See the docstring of the :func:`fourier_series` in sympy.series.fourier + for more information. + """ + from sympy.series.fourier import fourier_series + + return fourier_series(self, limits) + + ################################################################################### + ##################### DERIVATIVE, INTEGRAL, FUNCTIONAL METHODS #################### + ################################################################################### + + def diff(self, *symbols, **assumptions): + assumptions.setdefault("evaluate", True) + return _derivative_dispatch(self, *symbols, **assumptions) + + ########################################################################### + ###################### EXPRESSION EXPANSION METHODS ####################### + ########################################################################### + + # Relevant subclasses should override _eval_expand_hint() methods. See + # the docstring of expand() for more info. + + def _eval_expand_complex(self, **hints): + real, imag = self.as_real_imag(**hints) + return real + S.ImaginaryUnit*imag + + @staticmethod + def _expand_hint(expr, hint, deep=True, **hints): + """ + Helper for ``expand()``. Recursively calls ``expr._eval_expand_hint()``. + + Returns ``(expr, hit)``, where expr is the (possibly) expanded + ``expr`` and ``hit`` is ``True`` if ``expr`` was truly expanded and + ``False`` otherwise. + """ + hit = False + # XXX: Hack to support non-Basic args + # | + # V + if deep and getattr(expr, 'args', ()) and not expr.is_Atom: + sargs = [] + for arg in expr.args: + arg, arghit = Expr._expand_hint(arg, hint, **hints) + hit |= arghit + sargs.append(arg) + + if hit: + expr = expr.func(*sargs) + + if hasattr(expr, hint): + newexpr = getattr(expr, hint)(**hints) + if newexpr != expr: + return (newexpr, True) + + return (expr, hit) + + @cacheit + def expand(self, deep=True, modulus=None, power_base=True, power_exp=True, + mul=True, log=True, multinomial=True, basic=True, **hints): + """ + Expand an expression using hints. + + See the docstring of the expand() function in sympy.core.function for + more information. + + """ + from sympy.simplify.radsimp import fraction + + hints.update(power_base=power_base, power_exp=power_exp, mul=mul, + log=log, multinomial=multinomial, basic=basic) + + expr = self + # default matches fraction's default + _fraction = lambda x: fraction(x, hints.get('exact', False)) + if hints.pop('frac', False): + n, d = [a.expand(deep=deep, modulus=modulus, **hints) + for a in _fraction(self)] + return n/d + elif hints.pop('denom', False): + n, d = _fraction(self) + return n/d.expand(deep=deep, modulus=modulus, **hints) + elif hints.pop('numer', False): + n, d = _fraction(self) + return n.expand(deep=deep, modulus=modulus, **hints)/d + + # Although the hints are sorted here, an earlier hint may get applied + # at a given node in the expression tree before another because of how + # the hints are applied. e.g. expand(log(x*(y + z))) -> log(x*y + + # x*z) because while applying log at the top level, log and mul are + # applied at the deeper level in the tree so that when the log at the + # upper level gets applied, the mul has already been applied at the + # lower level. + + # Additionally, because hints are only applied once, the expression + # may not be expanded all the way. For example, if mul is applied + # before multinomial, x*(x + 1)**2 won't be expanded all the way. For + # now, we just use a special case to make multinomial run before mul, + # so that at least polynomials will be expanded all the way. In the + # future, smarter heuristics should be applied. + # TODO: Smarter heuristics + + def _expand_hint_key(hint): + """Make multinomial come before mul""" + if hint == 'mul': + return 'mulz' + return hint + + for hint in sorted(hints.keys(), key=_expand_hint_key): + use_hint = hints[hint] + if use_hint: + hint = '_eval_expand_' + hint + expr, hit = Expr._expand_hint(expr, hint, deep=deep, **hints) + + while True: + was = expr + if hints.get('multinomial', False): + expr, _ = Expr._expand_hint( + expr, '_eval_expand_multinomial', deep=deep, **hints) + if hints.get('mul', False): + expr, _ = Expr._expand_hint( + expr, '_eval_expand_mul', deep=deep, **hints) + if hints.get('log', False): + expr, _ = Expr._expand_hint( + expr, '_eval_expand_log', deep=deep, **hints) + if expr == was: + break + + if modulus is not None: + modulus = sympify(modulus) + + if not modulus.is_Integer or modulus <= 0: + raise ValueError( + "modulus must be a positive integer, got %s" % modulus) + + terms = [] + + for term in Add.make_args(expr): + coeff, tail = term.as_coeff_Mul(rational=True) + + coeff %= modulus + + if coeff: + terms.append(coeff*tail) + + expr = Add(*terms) + + return expr + + ########################################################################### + ################### GLOBAL ACTION VERB WRAPPER METHODS #################### + ########################################################################### + + def integrate(self, *args, **kwargs): + """See the integrate function in sympy.integrals""" + from sympy.integrals.integrals import integrate + return integrate(self, *args, **kwargs) + + def nsimplify(self, constants=(), tolerance=None, full=False): + """See the nsimplify function in sympy.simplify""" + from sympy.simplify.simplify import nsimplify + return nsimplify(self, constants, tolerance, full) + + def separate(self, deep=False, force=False): + """See the separate function in sympy.simplify""" + from .function import expand_power_base + return expand_power_base(self, deep=deep, force=force) + + def collect(self, syms, func=None, evaluate=True, exact=False, distribute_order_term=True): + """See the collect function in sympy.simplify""" + from sympy.simplify.radsimp import collect + return collect(self, syms, func, evaluate, exact, distribute_order_term) + + def together(self, *args, **kwargs): + """See the together function in sympy.polys""" + from sympy.polys.rationaltools import together + return together(self, *args, **kwargs) + + def apart(self, x=None, **args): + """See the apart function in sympy.polys""" + from sympy.polys.partfrac import apart + return apart(self, x, **args) + + def ratsimp(self): + """See the ratsimp function in sympy.simplify""" + from sympy.simplify.ratsimp import ratsimp + return ratsimp(self) + + def trigsimp(self, **args): + """See the trigsimp function in sympy.simplify""" + from sympy.simplify.trigsimp import trigsimp + return trigsimp(self, **args) + + def radsimp(self, **kwargs): + """See the radsimp function in sympy.simplify""" + from sympy.simplify.radsimp import radsimp + return radsimp(self, **kwargs) + + def powsimp(self, *args, **kwargs): + """See the powsimp function in sympy.simplify""" + from sympy.simplify.powsimp import powsimp + return powsimp(self, *args, **kwargs) + + def combsimp(self): + """See the combsimp function in sympy.simplify""" + from sympy.simplify.combsimp import combsimp + return combsimp(self) + + def gammasimp(self): + """See the gammasimp function in sympy.simplify""" + from sympy.simplify.gammasimp import gammasimp + return gammasimp(self) + + def factor(self, *gens, **args): + """See the factor() function in sympy.polys.polytools""" + from sympy.polys.polytools import factor + return factor(self, *gens, **args) + + def cancel(self, *gens, **args): + """See the cancel function in sympy.polys""" + from sympy.polys.polytools import cancel + return cancel(self, *gens, **args) + + def invert(self, g, *gens, **args): + """Return the multiplicative inverse of ``self`` mod ``g`` + where ``self`` (and ``g``) may be symbolic expressions). + + See Also + ======== + sympy.core.intfunc.mod_inverse, sympy.polys.polytools.invert + """ + if self.is_number and getattr(g, 'is_number', True): + return mod_inverse(self, g) + from sympy.polys.polytools import invert + return invert(self, g, *gens, **args) + + def round(self, n=None): + """Return x rounded to the given decimal place. + + If a complex number would results, apply round to the real + and imaginary components of the number. + + Examples + ======== + + >>> from sympy import pi, E, I, S, Number + >>> pi.round() + 3 + >>> pi.round(2) + 3.14 + >>> (2*pi + E*I).round() + 6 + 3*I + + The round method has a chopping effect: + + >>> (2*pi + I/10).round() + 6 + >>> (pi/10 + 2*I).round() + 2*I + >>> (pi/10 + E*I).round(2) + 0.31 + 2.72*I + + Notes + ===== + + The Python ``round`` function uses the SymPy ``round`` method so it + will always return a SymPy number (not a Python float or int): + + >>> isinstance(round(S(123), -2), Number) + True + """ + x = self + + if not x.is_number: + raise TypeError("Cannot round symbolic expression") + if not x.is_Atom: + if not pure_complex(x.n(2), or_real=True): + raise TypeError( + 'Expected a number but got %s:' % func_name(x)) + elif x in _illegal: + return x + if not (xr := x.is_extended_real): + r, i = x.as_real_imag() + if xr is False: + return r.round(n) + S.ImaginaryUnit*i.round(n) + if i.equals(0): + return r.round(n) + if not x: + return S.Zero if n is None else x + + p = as_int(n or 0) + + if x.is_Integer: + return Integer(round(int(x), p)) + + digits_to_decimal = _mag(x) # _mag(12) = 2, _mag(.012) = -1 + allow = digits_to_decimal + p + precs = [f._prec for f in x.atoms(Float)] + dps = prec_to_dps(max(precs)) if precs else None + if dps is None: + # assume everything is exact so use the Python + # float default or whatever was requested + dps = max(15, allow) + else: + allow = min(allow, dps) + # this will shift all digits to right of decimal + # and give us dps to work with as an int + shift = -digits_to_decimal + dps + extra = 1 # how far we look past known digits + # NOTE + # mpmath will calculate the binary representation to + # an arbitrary number of digits but we must base our + # answer on a finite number of those digits, e.g. + # .575 2589569785738035/2**52 in binary. + # mpmath shows us that the first 18 digits are + # >>> Float(.575).n(18) + # 0.574999999999999956 + # The default precision is 15 digits and if we ask + # for 15 we get + # >>> Float(.575).n(15) + # 0.575000000000000 + # mpmath handles rounding at the 15th digit. But we + # need to be careful since the user might be asking + # for rounding at the last digit and our semantics + # are to round toward the even final digit when there + # is a tie. So the extra digit will be used to make + # that decision. In this case, the value is the same + # to 15 digits: + # >>> Float(.575).n(16) + # 0.5750000000000000 + # Now converting this to the 15 known digits gives + # 575000000000000.0 + # which rounds to integer + # 5750000000000000 + # And now we can round to the desired digt, e.g. at + # the second from the left and we get + # 5800000000000000 + # and rescaling that gives + # 0.58 + # as the final result. + # If the value is made slightly less than 0.575 we might + # still obtain the same value: + # >>> Float(.575-1e-16).n(16)*10**15 + # 574999999999999.8 + # What 15 digits best represents the known digits (which are + # to the left of the decimal? 5750000000000000, the same as + # before. The only way we will round down (in this case) is + # if we declared that we had more than 15 digits of precision. + # For example, if we use 16 digits of precision, the integer + # we deal with is + # >>> Float(.575-1e-16).n(17)*10**16 + # 5749999999999998.4 + # and this now rounds to 5749999999999998 and (if we round to + # the 2nd digit from the left) we get 5700000000000000. + # + xf = x.n(dps + extra)*Pow(10, shift) + if xf.is_Number and xf._prec == 1: # xf.is_Add will raise below + # is x == 0? + if x.equals(0): + return Float(0) + raise ValueError('not computing with precision') + xi = Integer(xf) + # use the last digit to select the value of xi + # nearest to x before rounding at the desired digit + sign = 1 if x > 0 else -1 + dif2 = sign*(xf - xi).n(extra) + if dif2 < 0: + raise NotImplementedError( + 'not expecting int(x) to round away from 0') + if dif2 > .5: + xi += sign # round away from 0 + elif dif2 == .5: + xi += sign if xi%2 else -sign # round toward even + # shift p to the new position + ip = p - shift + # let Python handle the int rounding then rescale + xr = round(xi.p, ip) + # restore scale + rv = Rational(xr, Pow(10, shift)) + # return Float or Integer + if rv.is_Integer: + if n is None: # the single-arg case + return rv + # use str or else it won't be a float + return Float(str(rv), dps) # keep same precision + else: + if not allow and rv > self: + allow += 1 + return Float(rv, allow) + + __round__ = round + + def _eval_derivative_matrix_lines(self, x): + from sympy.matrices.expressions.matexpr import _LeftRightArgs + return [_LeftRightArgs([S.One, S.One], higher=self._eval_derivative(x))] + + +class AtomicExpr(Atom, Expr): + """ + A parent class for object which are both atoms and Exprs. + + For example: Symbol, Number, Rational, Integer, ... + But not: Add, Mul, Pow, ... + """ + is_number = False + is_Atom = True + + __slots__ = () + + def _eval_derivative(self, s): + if self == s: + return S.One + return S.Zero + + def _eval_derivative_n_times(self, s, n): + from .containers import Tuple + from sympy.matrices.expressions.matexpr import MatrixExpr + from sympy.matrices.matrixbase import MatrixBase + if isinstance(s, (MatrixBase, Tuple, Iterable, MatrixExpr)): + return super()._eval_derivative_n_times(s, n) + from .relational import Eq + from sympy.functions.elementary.piecewise import Piecewise + if self == s: + return Piecewise((self, Eq(n, 0)), (1, Eq(n, 1)), (0, True)) + else: + return Piecewise((self, Eq(n, 0)), (0, True)) + + def _eval_is_polynomial(self, syms): + return True + + def _eval_is_rational_function(self, syms): + return self not in _illegal + + def _eval_is_meromorphic(self, x, a): + from sympy.calculus.accumulationbounds import AccumBounds + return (not self.is_Number or self.is_finite) and not isinstance(self, AccumBounds) + + def _eval_is_algebraic_expr(self, syms): + return True + + def _eval_nseries(self, x, n, logx, cdir=0): + return self + + @property + def expr_free_symbols(self): + sympy_deprecation_warning(""" + The expr_free_symbols property is deprecated. Use free_symbols to get + the free symbols of an expression. + """, + deprecated_since_version="1.9", + active_deprecations_target="deprecated-expr-free-symbols") + return {self} + + +def _mag(x): + r"""Return integer $i$ such that $0.1 \le x/10^i < 1$ + + Examples + ======== + + >>> from sympy.core.expr import _mag + >>> from sympy import Float + >>> _mag(Float(.1)) + 0 + >>> _mag(Float(.01)) + -1 + >>> _mag(Float(1234)) + 4 + """ + from math import log10, ceil, log + xpos = abs(x.n()) + if not xpos: + return S.Zero + try: + mag_first_dig = int(ceil(log10(xpos))) + except (ValueError, OverflowError): + mag_first_dig = int(ceil(Float(mpf_log(xpos._mpf_, 53))/log(10))) + # check that we aren't off by 1 + if (xpos/10**mag_first_dig) >= 1: + assert 1 <= (xpos/10**mag_first_dig) < 10 + mag_first_dig += 1 + return mag_first_dig + + +class UnevaluatedExpr(Expr): + """ + Expression that is not evaluated unless released. + + Examples + ======== + + >>> from sympy import UnevaluatedExpr + >>> from sympy.abc import x + >>> x*(1/x) + 1 + >>> x*UnevaluatedExpr(1/x) + x*1/x + + """ + + def __new__(cls, arg, **kwargs): + arg = _sympify(arg) + obj = Expr.__new__(cls, arg, **kwargs) + return obj + + def doit(self, **hints): + if hints.get("deep", True): + return self.args[0].doit(**hints) + else: + return self.args[0] + + + +def unchanged(func, *args): + """Return True if `func` applied to the `args` is unchanged. + Can be used instead of `assert foo == foo`. + + Examples + ======== + + >>> from sympy import Piecewise, cos, pi + >>> from sympy.core.expr import unchanged + >>> from sympy.abc import x + + >>> unchanged(cos, 1) # instead of assert cos(1) == cos(1) + True + + >>> unchanged(cos, pi) + False + + Comparison of args uses the builtin capabilities of the object's + arguments to test for equality so args can be defined loosely. Here, + the ExprCondPair arguments of Piecewise compare as equal to the + tuples that can be used to create the Piecewise: + + >>> unchanged(Piecewise, (x, x > 1), (0, True)) + True + """ + f = func(*args) + return f.func == func and f.args == args + + +class ExprBuilder: + def __init__(self, op, args=None, validator=None, check=True): + if not hasattr(op, "__call__"): + raise TypeError("op {} needs to be callable".format(op)) + self.op = op + if args is None: + self.args = [] + else: + self.args = args + self.validator = validator + if (validator is not None) and check: + self.validate() + + @staticmethod + def _build_args(args): + return [i.build() if isinstance(i, ExprBuilder) else i for i in args] + + def validate(self): + if self.validator is None: + return + args = self._build_args(self.args) + self.validator(*args) + + def build(self, check=True): + args = self._build_args(self.args) + if self.validator and check: + self.validator(*args) + return self.op(*args) + + def append_argument(self, arg, check=True): + self.args.append(arg) + if self.validator and check: + self.validate(*self.args) + + def __getitem__(self, item): + if item == 0: + return self.op + else: + return self.args[item-1] + + def __repr__(self): + return str(self.build()) + + def search_element(self, elem): + for i, arg in enumerate(self.args): + if isinstance(arg, ExprBuilder): + ret = arg.search_index(elem) + if ret is not None: + return (i,) + ret + elif id(arg) == id(elem): + return (i,) + return None + + +from .mul import Mul +from .add import Add +from .power import Pow +from .function import Function, _derivative_dispatch +from .mod import Mod +from .exprtools import factor_terms +from .numbers import Float, Integer, Rational, _illegal, int_valued diff --git a/lib/python3.12/site-packages/sympy/core/exprtools.py b/lib/python3.12/site-packages/sympy/core/exprtools.py new file mode 100644 index 0000000000000000000000000000000000000000..869b9d569f4c7e962773337f739136ff271a3897 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/core/exprtools.py @@ -0,0 +1,1569 @@ +"""Tools for manipulating of large commutative expressions. """ + +from .add import Add +from .mul import Mul, _keep_coeff +from .power import Pow +from .basic import Basic +from .expr import Expr +from .function import expand_power_exp +from .sympify import sympify +from .numbers import Rational, Integer, Number, I, equal_valued +from .singleton import S +from .sorting import default_sort_key, ordered +from .symbol import Dummy +from .traversal import preorder_traversal +from .coreerrors import NonCommutativeExpression +from .containers import Tuple, Dict +from sympy.external.gmpy import SYMPY_INTS +from sympy.utilities.iterables import (common_prefix, common_suffix, + variations, iterable, is_sequence) + +from collections import defaultdict +from typing import Tuple as tTuple + + +_eps = Dummy(positive=True) + + +def _isnumber(i): + return isinstance(i, (SYMPY_INTS, float)) or i.is_Number + + +def _monotonic_sign(self): + """Return the value closest to 0 that ``self`` may have if all symbols + are signed and the result is uniformly the same sign for all values of symbols. + If a symbol is only signed but not known to be an + integer or the result is 0 then a symbol representative of the sign of self + will be returned. Otherwise, None is returned if a) the sign could be positive + or negative or b) self is not in one of the following forms: + + - L(x, y, ...) + A: a function linear in all symbols x, y, ... with an + additive constant; if A is zero then the function can be a monomial whose + sign is monotonic over the range of the variables, e.g. (x + 1)**3 if x is + nonnegative. + - A/L(x, y, ...) + B: the inverse of a function linear in all symbols x, y, ... + that does not have a sign change from positive to negative for any set + of values for the variables. + - M(x, y, ...) + A: a monomial M whose factors are all signed and a constant, A. + - A/M(x, y, ...) + B: the inverse of a monomial and constants A and B. + - P(x): a univariate polynomial + + Examples + ======== + + >>> from sympy.core.exprtools import _monotonic_sign as F + >>> from sympy import Dummy + >>> nn = Dummy(integer=True, nonnegative=True) + >>> p = Dummy(integer=True, positive=True) + >>> p2 = Dummy(integer=True, positive=True) + >>> F(nn + 1) + 1 + >>> F(p - 1) + _nneg + >>> F(nn*p + 1) + 1 + >>> F(p2*p + 1) + 2 + >>> F(nn - 1) # could be negative, zero or positive + """ + if not self.is_extended_real: + return + + if (-self).is_Symbol: + rv = _monotonic_sign(-self) + return rv if rv is None else -rv + + if not self.is_Add and self.as_numer_denom()[1].is_number: + s = self + if s.is_prime: + if s.is_odd: + return Integer(3) + else: + return Integer(2) + elif s.is_composite: + if s.is_odd: + return Integer(9) + else: + return Integer(4) + elif s.is_positive: + if s.is_even: + if s.is_prime is False: + return Integer(4) + else: + return Integer(2) + elif s.is_integer: + return S.One + else: + return _eps + elif s.is_extended_negative: + if s.is_even: + return Integer(-2) + elif s.is_integer: + return S.NegativeOne + else: + return -_eps + if s.is_zero or s.is_extended_nonpositive or s.is_extended_nonnegative: + return S.Zero + return None + + # univariate polynomial + free = self.free_symbols + if len(free) == 1: + if self.is_polynomial(): + from sympy.polys.polytools import real_roots + from sympy.polys.polyroots import roots + from sympy.polys.polyerrors import PolynomialError + x = free.pop() + x0 = _monotonic_sign(x) + if x0 in (_eps, -_eps): + x0 = S.Zero + if x0 is not None: + d = self.diff(x) + if d.is_number: + currentroots = [] + else: + try: + currentroots = real_roots(d) + except (PolynomialError, NotImplementedError): + currentroots = [r for r in roots(d, x) if r.is_extended_real] + y = self.subs(x, x0) + if x.is_nonnegative and all( + (r - x0).is_nonpositive for r in currentroots): + if y.is_nonnegative and d.is_positive: + if y: + return y if y.is_positive else Dummy('pos', positive=True) + else: + return Dummy('nneg', nonnegative=True) + if y.is_nonpositive and d.is_negative: + if y: + return y if y.is_negative else Dummy('neg', negative=True) + else: + return Dummy('npos', nonpositive=True) + elif x.is_nonpositive and all( + (r - x0).is_nonnegative for r in currentroots): + if y.is_nonnegative and d.is_negative: + if y: + return Dummy('pos', positive=True) + else: + return Dummy('nneg', nonnegative=True) + if y.is_nonpositive and d.is_positive: + if y: + return Dummy('neg', negative=True) + else: + return Dummy('npos', nonpositive=True) + else: + n, d = self.as_numer_denom() + den = None + if n.is_number: + den = _monotonic_sign(d) + elif not d.is_number: + if _monotonic_sign(n) is not None: + den = _monotonic_sign(d) + if den is not None and (den.is_positive or den.is_negative): + v = n*den + if v.is_positive: + return Dummy('pos', positive=True) + elif v.is_nonnegative: + return Dummy('nneg', nonnegative=True) + elif v.is_negative: + return Dummy('neg', negative=True) + elif v.is_nonpositive: + return Dummy('npos', nonpositive=True) + return None + + # multivariate + c, a = self.as_coeff_Add() + v = None + if not a.is_polynomial(): + # F/A or A/F where A is a number and F is a signed, rational monomial + n, d = a.as_numer_denom() + if not (n.is_number or d.is_number): + return + if ( + a.is_Mul or a.is_Pow) and \ + a.is_rational and \ + all(p.exp.is_Integer for p in a.atoms(Pow) if p.is_Pow) and \ + (a.is_positive or a.is_negative): + v = S.One + for ai in Mul.make_args(a): + if ai.is_number: + v *= ai + continue + reps = {} + for x in ai.free_symbols: + reps[x] = _monotonic_sign(x) + if reps[x] is None: + return + v *= ai.subs(reps) + elif c: + # signed linear expression + if not any(p for p in a.atoms(Pow) if not p.is_number) and (a.is_nonpositive or a.is_nonnegative): + free = list(a.free_symbols) + p = {} + for i in free: + v = _monotonic_sign(i) + if v is None: + return + p[i] = v or (_eps if i.is_nonnegative else -_eps) + v = a.xreplace(p) + if v is not None: + rv = v + c + if v.is_nonnegative and rv.is_positive: + return rv.subs(_eps, 0) + if v.is_nonpositive and rv.is_negative: + return rv.subs(_eps, 0) + + +def decompose_power(expr: Expr) -> tTuple[Expr, int]: + """ + Decompose power into symbolic base and integer exponent. + + Examples + ======== + + >>> from sympy.core.exprtools import decompose_power + >>> from sympy.abc import x, y + >>> from sympy import exp + + >>> decompose_power(x) + (x, 1) + >>> decompose_power(x**2) + (x, 2) + >>> decompose_power(exp(2*y/3)) + (exp(y/3), 2) + + """ + base, exp = expr.as_base_exp() + + if exp.is_Number: + if exp.is_Rational: + if not exp.is_Integer: + base = Pow(base, Rational(1, exp.q)) # type: ignore + e = exp.p # type: ignore + else: + base, e = expr, 1 + else: + exp, tail = exp.as_coeff_Mul(rational=True) + + if exp is S.NegativeOne: + base, e = Pow(base, tail), -1 + elif exp is not S.One: + # todo: after dropping python 3.7 support, use overload and Literal + # in as_coeff_Mul to make exp Rational, and remove these 2 ignores + tail = _keep_coeff(Rational(1, exp.q), tail) # type: ignore + base, e = Pow(base, tail), exp.p # type: ignore + else: + base, e = expr, 1 + + return base, e + + +def decompose_power_rat(expr: Expr) -> tTuple[Expr, Rational]: + """ + Decompose power into symbolic base and rational exponent; + if the exponent is not a Rational, then separate only the + integer coefficient. + + Examples + ======== + + >>> from sympy.core.exprtools import decompose_power_rat + >>> from sympy.abc import x + >>> from sympy import sqrt, exp + + >>> decompose_power_rat(sqrt(x)) + (x, 1/2) + >>> decompose_power_rat(exp(-3*x/2)) + (exp(x/2), -3) + + """ + _ = base, exp = expr.as_base_exp() + return _ if exp.is_Rational else decompose_power(expr) + + +class Factors: + """Efficient representation of ``f_1*f_2*...*f_n``.""" + + __slots__ = ('factors', 'gens') + + def __init__(self, factors=None): # Factors + """Initialize Factors from dict or expr. + + Examples + ======== + + >>> from sympy.core.exprtools import Factors + >>> from sympy.abc import x + >>> from sympy import I + >>> e = 2*x**3 + >>> Factors(e) + Factors({2: 1, x: 3}) + >>> Factors(e.as_powers_dict()) + Factors({2: 1, x: 3}) + >>> f = _ + >>> f.factors # underlying dictionary + {2: 1, x: 3} + >>> f.gens # base of each factor + frozenset({2, x}) + >>> Factors(0) + Factors({0: 1}) + >>> Factors(I) + Factors({I: 1}) + + Notes + ===== + + Although a dictionary can be passed, only minimal checking is + performed: powers of -1 and I are made canonical. + + """ + if isinstance(factors, (SYMPY_INTS, float)): + factors = S(factors) + if isinstance(factors, Factors): + factors = factors.factors.copy() + elif factors in (None, S.One): + factors = {} + elif factors is S.Zero or factors == 0: + factors = {S.Zero: S.One} + elif isinstance(factors, Number): + n = factors + factors = {} + if n < 0: + factors[S.NegativeOne] = S.One + n = -n + if n is not S.One: + if n.is_Float or n.is_Integer or n is S.Infinity: + factors[n] = S.One + elif n.is_Rational: + # since we're processing Numbers, the denominator is + # stored with a negative exponent; all other factors + # are left . + if n.p != 1: + factors[Integer(n.p)] = S.One + factors[Integer(n.q)] = S.NegativeOne + else: + raise ValueError('Expected Float|Rational|Integer, not %s' % n) + elif isinstance(factors, Basic) and not factors.args: + factors = {factors: S.One} + elif isinstance(factors, Expr): + c, nc = factors.args_cnc() + i = c.count(I) + for _ in range(i): + c.remove(I) + factors = dict(Mul._from_args(c).as_powers_dict()) + # Handle all rational Coefficients + for f in list(factors.keys()): + if isinstance(f, Rational) and not isinstance(f, Integer): + p, q = Integer(f.p), Integer(f.q) + factors[p] = (factors[p] if p in factors else S.Zero) + factors[f] + factors[q] = (factors[q] if q in factors else S.Zero) - factors[f] + factors.pop(f) + if i: + factors[I] = factors.get(I, S.Zero) + i + if nc: + factors[Mul(*nc, evaluate=False)] = S.One + else: + factors = factors.copy() # /!\ should be dict-like + + # tidy up -/+1 and I exponents if Rational + + handle = [k for k in factors if k is I or k in (-1, 1)] + if handle: + i1 = S.One + for k in handle: + if not _isnumber(factors[k]): + continue + i1 *= k**factors.pop(k) + if i1 is not S.One: + for a in i1.args if i1.is_Mul else [i1]: # at worst, -1.0*I*(-1)**e + if a is S.NegativeOne: + factors[a] = S.One + elif a is I: + factors[I] = S.One + elif a.is_Pow: + factors[a.base] = factors.get(a.base, S.Zero) + a.exp + elif equal_valued(a, 1): + factors[a] = S.One + elif equal_valued(a, -1): + factors[-a] = S.One + factors[S.NegativeOne] = S.One + else: + raise ValueError('unexpected factor in i1: %s' % a) + + self.factors = factors + keys = getattr(factors, 'keys', None) + if keys is None: + raise TypeError('expecting Expr or dictionary') + self.gens = frozenset(keys()) + + def __hash__(self): # Factors + keys = tuple(ordered(self.factors.keys())) + values = [self.factors[k] for k in keys] + return hash((keys, values)) + + def __repr__(self): # Factors + return "Factors({%s})" % ', '.join( + ['%s: %s' % (k, v) for k, v in ordered(self.factors.items())]) + + @property + def is_zero(self): # Factors + """ + >>> from sympy.core.exprtools import Factors + >>> Factors(0).is_zero + True + """ + f = self.factors + return len(f) == 1 and S.Zero in f + + @property + def is_one(self): # Factors + """ + >>> from sympy.core.exprtools import Factors + >>> Factors(1).is_one + True + """ + return not self.factors + + def as_expr(self): # Factors + """Return the underlying expression. + + Examples + ======== + + >>> from sympy.core.exprtools import Factors + >>> from sympy.abc import x, y + >>> Factors((x*y**2).as_powers_dict()).as_expr() + x*y**2 + + """ + + args = [] + for factor, exp in self.factors.items(): + if exp != 1: + if isinstance(exp, Integer): + b, e = factor.as_base_exp() + e = _keep_coeff(exp, e) + args.append(b**e) + else: + args.append(factor**exp) + else: + args.append(factor) + return Mul(*args) + + def mul(self, other): # Factors + """Return Factors of ``self * other``. + + Examples + ======== + + >>> from sympy.core.exprtools import Factors + >>> from sympy.abc import x, y, z + >>> a = Factors((x*y**2).as_powers_dict()) + >>> b = Factors((x*y/z).as_powers_dict()) + >>> a.mul(b) + Factors({x: 2, y: 3, z: -1}) + >>> a*b + Factors({x: 2, y: 3, z: -1}) + """ + if not isinstance(other, Factors): + other = Factors(other) + if any(f.is_zero for f in (self, other)): + return Factors(S.Zero) + factors = dict(self.factors) + + for factor, exp in other.factors.items(): + if factor in factors: + exp = factors[factor] + exp + + if not exp: + del factors[factor] + continue + + factors[factor] = exp + + return Factors(factors) + + def normal(self, other): + """Return ``self`` and ``other`` with ``gcd`` removed from each. + The only differences between this and method ``div`` is that this + is 1) optimized for the case when there are few factors in common and + 2) this does not raise an error if ``other`` is zero. + + See Also + ======== + div + + """ + if not isinstance(other, Factors): + other = Factors(other) + if other.is_zero: + return (Factors(), Factors(S.Zero)) + if self.is_zero: + return (Factors(S.Zero), Factors()) + + self_factors = dict(self.factors) + other_factors = dict(other.factors) + + for factor, self_exp in self.factors.items(): + try: + other_exp = other.factors[factor] + except KeyError: + continue + + exp = self_exp - other_exp + + if not exp: + del self_factors[factor] + del other_factors[factor] + elif _isnumber(exp): + if exp > 0: + self_factors[factor] = exp + del other_factors[factor] + else: + del self_factors[factor] + other_factors[factor] = -exp + else: + r = self_exp.extract_additively(other_exp) + if r is not None: + if r: + self_factors[factor] = r + del other_factors[factor] + else: # should be handled already + del self_factors[factor] + del other_factors[factor] + else: + sc, sa = self_exp.as_coeff_Add() + if sc: + oc, oa = other_exp.as_coeff_Add() + diff = sc - oc + if diff > 0: + self_factors[factor] -= oc + other_exp = oa + elif diff < 0: + self_factors[factor] -= sc + other_factors[factor] -= sc + other_exp = oa - diff + else: + self_factors[factor] = sa + other_exp = oa + if other_exp: + other_factors[factor] = other_exp + else: + del other_factors[factor] + + return Factors(self_factors), Factors(other_factors) + + def div(self, other): # Factors + """Return ``self`` and ``other`` with ``gcd`` removed from each. + This is optimized for the case when there are many factors in common. + + Examples + ======== + + >>> from sympy.core.exprtools import Factors + >>> from sympy.abc import x, y, z + >>> from sympy import S + + >>> a = Factors((x*y**2).as_powers_dict()) + >>> a.div(a) + (Factors({}), Factors({})) + >>> a.div(x*z) + (Factors({y: 2}), Factors({z: 1})) + + The ``/`` operator only gives ``quo``: + + >>> a/x + Factors({y: 2}) + + Factors treats its factors as though they are all in the numerator, so + if you violate this assumption the results will be correct but will + not strictly correspond to the numerator and denominator of the ratio: + + >>> a.div(x/z) + (Factors({y: 2}), Factors({z: -1})) + + Factors is also naive about bases: it does not attempt any denesting + of Rational-base terms, for example the following does not become + 2**(2*x)/2. + + >>> Factors(2**(2*x + 2)).div(S(8)) + (Factors({2: 2*x + 2}), Factors({8: 1})) + + factor_terms can clean up such Rational-bases powers: + + >>> from sympy import factor_terms + >>> n, d = Factors(2**(2*x + 2)).div(S(8)) + >>> n.as_expr()/d.as_expr() + 2**(2*x + 2)/8 + >>> factor_terms(_) + 2**(2*x)/2 + + """ + quo, rem = dict(self.factors), {} + + if not isinstance(other, Factors): + other = Factors(other) + if other.is_zero: + raise ZeroDivisionError + if self.is_zero: + return (Factors(S.Zero), Factors()) + + for factor, exp in other.factors.items(): + if factor in quo: + d = quo[factor] - exp + if _isnumber(d): + if d <= 0: + del quo[factor] + + if d >= 0: + if d: + quo[factor] = d + + continue + + exp = -d + + else: + r = quo[factor].extract_additively(exp) + if r is not None: + if r: + quo[factor] = r + else: # should be handled already + del quo[factor] + else: + other_exp = exp + sc, sa = quo[factor].as_coeff_Add() + if sc: + oc, oa = other_exp.as_coeff_Add() + diff = sc - oc + if diff > 0: + quo[factor] -= oc + other_exp = oa + elif diff < 0: + quo[factor] -= sc + other_exp = oa - diff + else: + quo[factor] = sa + other_exp = oa + if other_exp: + rem[factor] = other_exp + else: + assert factor not in rem + continue + + rem[factor] = exp + + return Factors(quo), Factors(rem) + + def quo(self, other): # Factors + """Return numerator Factor of ``self / other``. + + Examples + ======== + + >>> from sympy.core.exprtools import Factors + >>> from sympy.abc import x, y, z + >>> a = Factors((x*y**2).as_powers_dict()) + >>> b = Factors((x*y/z).as_powers_dict()) + >>> a.quo(b) # same as a/b + Factors({y: 1}) + """ + return self.div(other)[0] + + def rem(self, other): # Factors + """Return denominator Factors of ``self / other``. + + Examples + ======== + + >>> from sympy.core.exprtools import Factors + >>> from sympy.abc import x, y, z + >>> a = Factors((x*y**2).as_powers_dict()) + >>> b = Factors((x*y/z).as_powers_dict()) + >>> a.rem(b) + Factors({z: -1}) + >>> a.rem(a) + Factors({}) + """ + return self.div(other)[1] + + def pow(self, other): # Factors + """Return self raised to a non-negative integer power. + + Examples + ======== + + >>> from sympy.core.exprtools import Factors + >>> from sympy.abc import x, y + >>> a = Factors((x*y**2).as_powers_dict()) + >>> a**2 + Factors({x: 2, y: 4}) + + """ + if isinstance(other, Factors): + other = other.as_expr() + if other.is_Integer: + other = int(other) + if isinstance(other, SYMPY_INTS) and other >= 0: + factors = {} + + if other: + for factor, exp in self.factors.items(): + factors[factor] = exp*other + + return Factors(factors) + else: + raise ValueError("expected non-negative integer, got %s" % other) + + def gcd(self, other): # Factors + """Return Factors of ``gcd(self, other)``. The keys are + the intersection of factors with the minimum exponent for + each factor. + + Examples + ======== + + >>> from sympy.core.exprtools import Factors + >>> from sympy.abc import x, y, z + >>> a = Factors((x*y**2).as_powers_dict()) + >>> b = Factors((x*y/z).as_powers_dict()) + >>> a.gcd(b) + Factors({x: 1, y: 1}) + """ + if not isinstance(other, Factors): + other = Factors(other) + if other.is_zero: + return Factors(self.factors) + + factors = {} + + for factor, exp in self.factors.items(): + factor, exp = sympify(factor), sympify(exp) + if factor in other.factors: + lt = (exp - other.factors[factor]).is_negative + if lt == True: + factors[factor] = exp + elif lt == False: + factors[factor] = other.factors[factor] + + return Factors(factors) + + def lcm(self, other): # Factors + """Return Factors of ``lcm(self, other)`` which are + the union of factors with the maximum exponent for + each factor. + + Examples + ======== + + >>> from sympy.core.exprtools import Factors + >>> from sympy.abc import x, y, z + >>> a = Factors((x*y**2).as_powers_dict()) + >>> b = Factors((x*y/z).as_powers_dict()) + >>> a.lcm(b) + Factors({x: 1, y: 2, z: -1}) + """ + if not isinstance(other, Factors): + other = Factors(other) + if any(f.is_zero for f in (self, other)): + return Factors(S.Zero) + + factors = dict(self.factors) + + for factor, exp in other.factors.items(): + if factor in factors: + exp = max(exp, factors[factor]) + + factors[factor] = exp + + return Factors(factors) + + def __mul__(self, other): # Factors + return self.mul(other) + + def __divmod__(self, other): # Factors + return self.div(other) + + def __truediv__(self, other): # Factors + return self.quo(other) + + def __mod__(self, other): # Factors + return self.rem(other) + + def __pow__(self, other): # Factors + return self.pow(other) + + def __eq__(self, other): # Factors + if not isinstance(other, Factors): + other = Factors(other) + return self.factors == other.factors + + def __ne__(self, other): # Factors + return not self == other + + +class Term: + """Efficient representation of ``coeff*(numer/denom)``. """ + + __slots__ = ('coeff', 'numer', 'denom') + + def __init__(self, term, numer=None, denom=None): # Term + if numer is None and denom is None: + if not term.is_commutative: + raise NonCommutativeExpression( + 'commutative expression expected') + + coeff, factors = term.as_coeff_mul() + numer, denom = defaultdict(int), defaultdict(int) + + for factor in factors: + base, exp = decompose_power(factor) + + if base.is_Add: + cont, base = base.primitive() + coeff *= cont**exp + + if exp > 0: + numer[base] += exp + else: + denom[base] += -exp + + numer = Factors(numer) + denom = Factors(denom) + else: + coeff = term + + if numer is None: + numer = Factors() + + if denom is None: + denom = Factors() + + self.coeff = coeff + self.numer = numer + self.denom = denom + + def __hash__(self): # Term + return hash((self.coeff, self.numer, self.denom)) + + def __repr__(self): # Term + return "Term(%s, %s, %s)" % (self.coeff, self.numer, self.denom) + + def as_expr(self): # Term + return self.coeff*(self.numer.as_expr()/self.denom.as_expr()) + + def mul(self, other): # Term + coeff = self.coeff*other.coeff + numer = self.numer.mul(other.numer) + denom = self.denom.mul(other.denom) + + numer, denom = numer.normal(denom) + + return Term(coeff, numer, denom) + + def inv(self): # Term + return Term(1/self.coeff, self.denom, self.numer) + + def quo(self, other): # Term + return self.mul(other.inv()) + + def pow(self, other): # Term + if other < 0: + return self.inv().pow(-other) + else: + return Term(self.coeff ** other, + self.numer.pow(other), + self.denom.pow(other)) + + def gcd(self, other): # Term + return Term(self.coeff.gcd(other.coeff), + self.numer.gcd(other.numer), + self.denom.gcd(other.denom)) + + def lcm(self, other): # Term + return Term(self.coeff.lcm(other.coeff), + self.numer.lcm(other.numer), + self.denom.lcm(other.denom)) + + def __mul__(self, other): # Term + if isinstance(other, Term): + return self.mul(other) + else: + return NotImplemented + + def __truediv__(self, other): # Term + if isinstance(other, Term): + return self.quo(other) + else: + return NotImplemented + + def __pow__(self, other): # Term + if isinstance(other, SYMPY_INTS): + return self.pow(other) + else: + return NotImplemented + + def __eq__(self, other): # Term + return (self.coeff == other.coeff and + self.numer == other.numer and + self.denom == other.denom) + + def __ne__(self, other): # Term + return not self == other + + +def _gcd_terms(terms, isprimitive=False, fraction=True): + """Helper function for :func:`gcd_terms`. + + Parameters + ========== + + isprimitive : boolean, optional + If ``isprimitive`` is True then the call to primitive + for an Add will be skipped. This is useful when the + content has already been extracted. + + fraction : boolean, optional + If ``fraction`` is True then the expression will appear over a common + denominator, the lcm of all term denominators. + """ + + if isinstance(terms, Basic) and not isinstance(terms, Tuple): + terms = Add.make_args(terms) + + terms = list(map(Term, [t for t in terms if t])) + + # there is some simplification that may happen if we leave this + # here rather than duplicate it before the mapping of Term onto + # the terms + if len(terms) == 0: + return S.Zero, S.Zero, S.One + + if len(terms) == 1: + cont = terms[0].coeff + numer = terms[0].numer.as_expr() + denom = terms[0].denom.as_expr() + + else: + cont = terms[0] + for term in terms[1:]: + cont = cont.gcd(term) + + for i, term in enumerate(terms): + terms[i] = term.quo(cont) + + if fraction: + denom = terms[0].denom + + for term in terms[1:]: + denom = denom.lcm(term.denom) + + numers = [] + for term in terms: + numer = term.numer.mul(denom.quo(term.denom)) + numers.append(term.coeff*numer.as_expr()) + else: + numers = [t.as_expr() for t in terms] + denom = Term(S.One).numer + + cont = cont.as_expr() + numer = Add(*numers) + denom = denom.as_expr() + + if not isprimitive and numer.is_Add: + _cont, numer = numer.primitive() + cont *= _cont + + return cont, numer, denom + + +def gcd_terms(terms, isprimitive=False, clear=True, fraction=True): + """Compute the GCD of ``terms`` and put them together. + + Parameters + ========== + + terms : Expr + Can be an expression or a non-Basic sequence of expressions + which will be handled as though they are terms from a sum. + + isprimitive : bool, optional + If ``isprimitive`` is True the _gcd_terms will not run the primitive + method on the terms. + + clear : bool, optional + It controls the removal of integers from the denominator of an Add + expression. When True (default), all numerical denominator will be cleared; + when False the denominators will be cleared only if all terms had numerical + denominators other than 1. + + fraction : bool, optional + When True (default), will put the expression over a common + denominator. + + Examples + ======== + + >>> from sympy import gcd_terms + >>> from sympy.abc import x, y + + >>> gcd_terms((x + 1)**2*y + (x + 1)*y**2) + y*(x + 1)*(x + y + 1) + >>> gcd_terms(x/2 + 1) + (x + 2)/2 + >>> gcd_terms(x/2 + 1, clear=False) + x/2 + 1 + >>> gcd_terms(x/2 + y/2, clear=False) + (x + y)/2 + >>> gcd_terms(x/2 + 1/x) + (x**2 + 2)/(2*x) + >>> gcd_terms(x/2 + 1/x, fraction=False) + (x + 2/x)/2 + >>> gcd_terms(x/2 + 1/x, fraction=False, clear=False) + x/2 + 1/x + + >>> gcd_terms(x/2/y + 1/x/y) + (x**2 + 2)/(2*x*y) + >>> gcd_terms(x/2/y + 1/x/y, clear=False) + (x**2/2 + 1)/(x*y) + >>> gcd_terms(x/2/y + 1/x/y, clear=False, fraction=False) + (x/2 + 1/x)/y + + The ``clear`` flag was ignored in this case because the returned + expression was a rational expression, not a simple sum. + + See Also + ======== + + factor_terms, sympy.polys.polytools.terms_gcd + + """ + def mask(terms): + """replace nc portions of each term with a unique Dummy symbols + and return the replacements to restore them""" + args = [(a, []) if a.is_commutative else a.args_cnc() for a in terms] + reps = [] + for i, (c, nc) in enumerate(args): + if nc: + nc = Mul(*nc) + d = Dummy() + reps.append((d, nc)) + c.append(d) + args[i] = Mul(*c) + else: + args[i] = c + return args, dict(reps) + + isadd = isinstance(terms, Add) + addlike = isadd or not isinstance(terms, Basic) and \ + is_sequence(terms, include=set) and \ + not isinstance(terms, Dict) + + if addlike: + if isadd: # i.e. an Add + terms = list(terms.args) + else: + terms = sympify(terms) + terms, reps = mask(terms) + cont, numer, denom = _gcd_terms(terms, isprimitive, fraction) + numer = numer.xreplace(reps) + coeff, factors = cont.as_coeff_Mul() + if not clear: + c, _coeff = coeff.as_coeff_Mul() + if not c.is_Integer and not clear and numer.is_Add: + n, d = c.as_numer_denom() + _numer = numer/d + if any(a.as_coeff_Mul()[0].is_Integer + for a in _numer.args): + numer = _numer + coeff = n*_coeff + return _keep_coeff(coeff, factors*numer/denom, clear=clear) + + if not isinstance(terms, Basic): + return terms + + if terms.is_Atom: + return terms + + if terms.is_Mul: + c, args = terms.as_coeff_mul() + return _keep_coeff(c, Mul(*[gcd_terms(i, isprimitive, clear, fraction) + for i in args]), clear=clear) + + def handle(a): + # don't treat internal args like terms of an Add + if not isinstance(a, Expr): + if isinstance(a, Basic): + if not a.args: + return a + return a.func(*[handle(i) for i in a.args]) + return type(a)([handle(i) for i in a]) + return gcd_terms(a, isprimitive, clear, fraction) + + if isinstance(terms, Dict): + return Dict(*[(k, handle(v)) for k, v in terms.args]) + return terms.func(*[handle(i) for i in terms.args]) + + +def _factor_sum_int(expr, **kwargs): + """Return Sum or Integral object with factors that are not + in the wrt variables removed. In cases where there are additive + terms in the function of the object that are independent, the + object will be separated into two objects. + + Examples + ======== + + >>> from sympy import Sum, factor_terms + >>> from sympy.abc import x, y + >>> factor_terms(Sum(x + y, (x, 1, 3))) + y*Sum(1, (x, 1, 3)) + Sum(x, (x, 1, 3)) + >>> factor_terms(Sum(x*y, (x, 1, 3))) + y*Sum(x, (x, 1, 3)) + + Notes + ===== + + If a function in the summand or integrand is replaced + with a symbol, then this simplification should not be + done or else an incorrect result will be obtained when + the symbol is replaced with an expression that depends + on the variables of summation/integration: + + >>> eq = Sum(y, (x, 1, 3)) + >>> factor_terms(eq).subs(y, x).doit() + 3*x + >>> eq.subs(y, x).doit() + 6 + """ + result = expr.function + if result == 0: + return S.Zero + limits = expr.limits + + # get the wrt variables + wrt = {i.args[0] for i in limits} + + # factor out any common terms that are independent of wrt + f = factor_terms(result, **kwargs) + i, d = f.as_independent(*wrt) + if isinstance(f, Add): + return i * expr.func(1, *limits) + expr.func(d, *limits) + else: + return i * expr.func(d, *limits) + + +def factor_terms(expr, radical=False, clear=False, fraction=False, sign=True): + """Remove common factors from terms in all arguments without + changing the underlying structure of the expr. No expansion or + simplification (and no processing of non-commutatives) is performed. + + Parameters + ========== + + radical: bool, optional + If radical=True then a radical common to all terms will be factored + out of any Add sub-expressions of the expr. + + clear : bool, optional + If clear=False (default) then coefficients will not be separated + from a single Add if they can be distributed to leave one or more + terms with integer coefficients. + + fraction : bool, optional + If fraction=True (default is False) then a common denominator will be + constructed for the expression. + + sign : bool, optional + If sign=True (default) then even if the only factor in common is a -1, + it will be factored out of the expression. + + Examples + ======== + + >>> from sympy import factor_terms, Symbol + >>> from sympy.abc import x, y + >>> factor_terms(x + x*(2 + 4*y)**3) + x*(8*(2*y + 1)**3 + 1) + >>> A = Symbol('A', commutative=False) + >>> factor_terms(x*A + x*A + x*y*A) + x*(y*A + 2*A) + + When ``clear`` is False, a rational will only be factored out of an + Add expression if all terms of the Add have coefficients that are + fractions: + + >>> factor_terms(x/2 + 1, clear=False) + x/2 + 1 + >>> factor_terms(x/2 + 1, clear=True) + (x + 2)/2 + + If a -1 is all that can be factored out, to *not* factor it out, the + flag ``sign`` must be False: + + >>> factor_terms(-x - y) + -(x + y) + >>> factor_terms(-x - y, sign=False) + -x - y + >>> factor_terms(-2*x - 2*y, sign=False) + -2*(x + y) + + See Also + ======== + + gcd_terms, sympy.polys.polytools.terms_gcd + + """ + def do(expr): + from sympy.concrete.summations import Sum + from sympy.integrals.integrals import Integral + is_iterable = iterable(expr) + + if not isinstance(expr, Basic) or expr.is_Atom: + if is_iterable: + return type(expr)([do(i) for i in expr]) + return expr + + if expr.is_Pow or expr.is_Function or \ + is_iterable or not hasattr(expr, 'args_cnc'): + args = expr.args + newargs = tuple([do(i) for i in args]) + if newargs == args: + return expr + return expr.func(*newargs) + + if isinstance(expr, (Sum, Integral)): + return _factor_sum_int(expr, + radical=radical, clear=clear, + fraction=fraction, sign=sign) + + cont, p = expr.as_content_primitive(radical=radical, clear=clear) + if p.is_Add: + list_args = [do(a) for a in Add.make_args(p)] + # get a common negative (if there) which gcd_terms does not remove + if not any(a.as_coeff_Mul()[0].extract_multiplicatively(-1) is None + for a in list_args): + cont = -cont + list_args = [-a for a in list_args] + # watch out for exp(-(x+2)) which gcd_terms will change to exp(-x-2) + special = {} + for i, a in enumerate(list_args): + b, e = a.as_base_exp() + if e.is_Mul and e != Mul(*e.args): + list_args[i] = Dummy() + special[list_args[i]] = a + # rebuild p not worrying about the order which gcd_terms will fix + p = Add._from_args(list_args) + p = gcd_terms(p, + isprimitive=True, + clear=clear, + fraction=fraction).xreplace(special) + elif p.args: + p = p.func( + *[do(a) for a in p.args]) + rv = _keep_coeff(cont, p, clear=clear, sign=sign) + return rv + expr = sympify(expr) + return do(expr) + + +def _mask_nc(eq, name=None): + """ + Return ``eq`` with non-commutative objects replaced with Dummy + symbols. A dictionary that can be used to restore the original + values is returned: if it is None, the expression is noncommutative + and cannot be made commutative. The third value returned is a list + of any non-commutative symbols that appear in the returned equation. + + Explanation + =========== + + All non-commutative objects other than Symbols are replaced with + a non-commutative Symbol. Identical objects will be identified + by identical symbols. + + If there is only 1 non-commutative object in an expression it will + be replaced with a commutative symbol. Otherwise, the non-commutative + entities are retained and the calling routine should handle + replacements in this case since some care must be taken to keep + track of the ordering of symbols when they occur within Muls. + + Parameters + ========== + + name : str + ``name``, if given, is the name that will be used with numbered Dummy + variables that will replace the non-commutative objects and is mainly + used for doctesting purposes. + + Examples + ======== + + >>> from sympy.physics.secondquant import Commutator, NO, F, Fd + >>> from sympy import symbols + >>> from sympy.core.exprtools import _mask_nc + >>> from sympy.abc import x, y + >>> A, B, C = symbols('A,B,C', commutative=False) + + One nc-symbol: + + >>> _mask_nc(A**2 - x**2, 'd') + (_d0**2 - x**2, {_d0: A}, []) + + Multiple nc-symbols: + + >>> _mask_nc(A**2 - B**2, 'd') + (A**2 - B**2, {}, [A, B]) + + An nc-object with nc-symbols but no others outside of it: + + >>> _mask_nc(1 + x*Commutator(A, B), 'd') + (_d0*x + 1, {_d0: Commutator(A, B)}, []) + >>> _mask_nc(NO(Fd(x)*F(y)), 'd') + (_d0, {_d0: NO(CreateFermion(x)*AnnihilateFermion(y))}, []) + + Multiple nc-objects: + + >>> eq = x*Commutator(A, B) + x*Commutator(A, C)*Commutator(A, B) + >>> _mask_nc(eq, 'd') + (x*_d0 + x*_d1*_d0, {_d0: Commutator(A, B), _d1: Commutator(A, C)}, [_d0, _d1]) + + Multiple nc-objects and nc-symbols: + + >>> eq = A*Commutator(A, B) + B*Commutator(A, C) + >>> _mask_nc(eq, 'd') + (A*_d0 + B*_d1, {_d0: Commutator(A, B), _d1: Commutator(A, C)}, [_d0, _d1, A, B]) + + """ + name = name or 'mask' + # Make Dummy() append sequential numbers to the name + + def numbered_names(): + i = 0 + while True: + yield name + str(i) + i += 1 + + names = numbered_names() + + def Dummy(*args, **kwargs): + from .symbol import Dummy + return Dummy(next(names), *args, **kwargs) + + expr = eq + if expr.is_commutative: + return eq, {}, [] + + # identify nc-objects; symbols and other + rep = [] + nc_obj = set() + nc_syms = set() + pot = preorder_traversal(expr, keys=default_sort_key) + for i, a in enumerate(pot): + if any(a == r[0] for r in rep): + pot.skip() + elif not a.is_commutative: + if a.is_symbol: + nc_syms.add(a) + pot.skip() + elif not (a.is_Add or a.is_Mul or a.is_Pow): + nc_obj.add(a) + pot.skip() + + # If there is only one nc symbol or object, it can be factored regularly + # but polys is going to complain, so replace it with a Dummy. + if len(nc_obj) == 1 and not nc_syms: + rep.append((nc_obj.pop(), Dummy())) + elif len(nc_syms) == 1 and not nc_obj: + rep.append((nc_syms.pop(), Dummy())) + + # Any remaining nc-objects will be replaced with an nc-Dummy and + # identified as an nc-Symbol to watch out for + nc_obj = sorted(nc_obj, key=default_sort_key) + for n in nc_obj: + nc = Dummy(commutative=False) + rep.append((n, nc)) + nc_syms.add(nc) + expr = expr.subs(rep) + + nc_syms = list(nc_syms) + nc_syms.sort(key=default_sort_key) + return expr, {v: k for k, v in rep}, nc_syms + + +def factor_nc(expr): + """Return the factored form of ``expr`` while handling non-commutative + expressions. + + Examples + ======== + + >>> from sympy import factor_nc, Symbol + >>> from sympy.abc import x + >>> A = Symbol('A', commutative=False) + >>> B = Symbol('B', commutative=False) + >>> factor_nc((x**2 + 2*A*x + A**2).expand()) + (x + A)**2 + >>> factor_nc(((x + A)*(x + B)).expand()) + (x + A)*(x + B) + """ + expr = sympify(expr) + if not isinstance(expr, Expr) or not expr.args: + return expr + if not expr.is_Add: + return expr.func(*[factor_nc(a) for a in expr.args]) + expr = expr.func(*[expand_power_exp(i) for i in expr.args]) + + from sympy.polys.polytools import gcd, factor + expr, rep, nc_symbols = _mask_nc(expr) + + if rep: + return factor(expr).subs(rep) + else: + args = [a.args_cnc() for a in Add.make_args(expr)] + c = g = l = r = S.One + hit = False + # find any commutative gcd term + for i, a in enumerate(args): + if i == 0: + c = Mul._from_args(a[0]) + elif a[0]: + c = gcd(c, Mul._from_args(a[0])) + else: + c = S.One + if c is not S.One: + hit = True + c, g = c.as_coeff_Mul() + if g is not S.One: + for i, (cc, _) in enumerate(args): + cc = list(Mul.make_args(Mul._from_args(list(cc))/g)) + args[i][0] = cc + for i, (cc, _) in enumerate(args): + if cc: + cc[0] = cc[0]/c + else: + cc = [1/c] + args[i][0] = cc + # find any noncommutative common prefix + for i, a in enumerate(args): + if i == 0: + n = a[1][:] + else: + n = common_prefix(n, a[1]) + if not n: + # is there a power that can be extracted? + if not args[0][1]: + break + b, e = args[0][1][0].as_base_exp() + ok = False + if e.is_Integer: + for t in args: + if not t[1]: + break + bt, et = t[1][0].as_base_exp() + if et.is_Integer and bt == b: + e = min(e, et) + else: + break + else: + ok = hit = True + l = b**e + il = b**-e + for _ in args: + _[1][0] = il*_[1][0] + break + if not ok: + break + else: + hit = True + lenn = len(n) + l = Mul(*n) + for _ in args: + _[1] = _[1][lenn:] + # find any noncommutative common suffix + for i, a in enumerate(args): + if i == 0: + n = a[1][:] + else: + n = common_suffix(n, a[1]) + if not n: + # is there a power that can be extracted? + if not args[0][1]: + break + b, e = args[0][1][-1].as_base_exp() + ok = False + if e.is_Integer: + for t in args: + if not t[1]: + break + bt, et = t[1][-1].as_base_exp() + if et.is_Integer and bt == b: + e = min(e, et) + else: + break + else: + ok = hit = True + r = b**e + il = b**-e + for _ in args: + _[1][-1] = _[1][-1]*il + break + if not ok: + break + else: + hit = True + lenn = len(n) + r = Mul(*n) + for _ in args: + _[1] = _[1][:len(_[1]) - lenn] + if hit: + mid = Add(*[Mul(*cc)*Mul(*nc) for cc, nc in args]) + else: + mid = expr + + from sympy.simplify.powsimp import powsimp + + # sort the symbols so the Dummys would appear in the same + # order as the original symbols, otherwise you may introduce + # a factor of -1, e.g. A**2 - B**2) -- {A:y, B:x} --> y**2 - x**2 + # and the former factors into two terms, (A - B)*(A + B) while the + # latter factors into 3 terms, (-1)*(x - y)*(x + y) + rep1 = [(n, Dummy()) for n in sorted(nc_symbols, key=default_sort_key)] + unrep1 = [(v, k) for k, v in rep1] + unrep1.reverse() + new_mid, r2, _ = _mask_nc(mid.subs(rep1)) + new_mid = powsimp(factor(new_mid)) + + new_mid = new_mid.subs(r2).subs(unrep1) + + if new_mid.is_Pow: + return _keep_coeff(c, g*l*new_mid*r) + + if new_mid.is_Mul: + def _pemexpand(expr): + "Expand with the minimal set of hints necessary to check the result." + return expr.expand(deep=True, mul=True, power_exp=True, + power_base=False, basic=False, multinomial=True, log=False) + # XXX TODO there should be a way to inspect what order the terms + # must be in and just select the plausible ordering without + # checking permutations + cfac = [] + ncfac = [] + for f in new_mid.args: + if f.is_commutative: + cfac.append(f) + else: + b, e = f.as_base_exp() + if e.is_Integer: + ncfac.extend([b]*e) + else: + ncfac.append(f) + pre_mid = g*Mul(*cfac)*l + target = _pemexpand(expr/c) + for s in variations(ncfac, len(ncfac)): + ok = pre_mid*Mul(*s)*r + if _pemexpand(ok) == target: + return _keep_coeff(c, ok) + + # mid was an Add that didn't factor successfully + return _keep_coeff(c, g*l*mid*r) diff --git a/lib/python3.12/site-packages/sympy/core/facts.py b/lib/python3.12/site-packages/sympy/core/facts.py new file mode 100644 index 0000000000000000000000000000000000000000..0b98d9b14bbac661d3c0fd1d1fd87977a792fb74 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/core/facts.py @@ -0,0 +1,634 @@ +r"""This is rule-based deduction system for SymPy + +The whole thing is split into two parts + + - rules compilation and preparation of tables + - runtime inference + +For rule-based inference engines, the classical work is RETE algorithm [1], +[2] Although we are not implementing it in full (or even significantly) +it's still worth a read to understand the underlying ideas. + +In short, every rule in a system of rules is one of two forms: + + - atom -> ... (alpha rule) + - And(atom1, atom2, ...) -> ... (beta rule) + + +The major complexity is in efficient beta-rules processing and usually for an +expert system a lot of effort goes into code that operates on beta-rules. + + +Here we take minimalistic approach to get something usable first. + + - (preparation) of alpha- and beta- networks, everything except + - (runtime) FactRules.deduce_all_facts + + _____________________________________ + ( Kirr: I've never thought that doing ) + ( logic stuff is that difficult... ) + ------------------------------------- + o ^__^ + o (oo)\_______ + (__)\ )\/\ + ||----w | + || || + + +Some references on the topic +---------------------------- + +[1] https://en.wikipedia.org/wiki/Rete_algorithm +[2] http://reports-archive.adm.cs.cmu.edu/anon/1995/CMU-CS-95-113.pdf + +https://en.wikipedia.org/wiki/Propositional_formula +https://en.wikipedia.org/wiki/Inference_rule +https://en.wikipedia.org/wiki/List_of_rules_of_inference +""" + +from collections import defaultdict +from typing import Iterator + +from .logic import Logic, And, Or, Not + + +def _base_fact(atom): + """Return the literal fact of an atom. + + Effectively, this merely strips the Not around a fact. + """ + if isinstance(atom, Not): + return atom.arg + else: + return atom + + +def _as_pair(atom): + if isinstance(atom, Not): + return (atom.arg, False) + else: + return (atom, True) + +# XXX this prepares forward-chaining rules for alpha-network + + +def transitive_closure(implications): + """ + Computes the transitive closure of a list of implications + + Uses Warshall's algorithm, as described at + http://www.cs.hope.edu/~cusack/Notes/Notes/DiscreteMath/Warshall.pdf. + """ + full_implications = set(implications) + literals = set().union(*map(set, full_implications)) + + for k in literals: + for i in literals: + if (i, k) in full_implications: + for j in literals: + if (k, j) in full_implications: + full_implications.add((i, j)) + + return full_implications + + +def deduce_alpha_implications(implications): + """deduce all implications + + Description by example + ---------------------- + + given set of logic rules: + + a -> b + b -> c + + we deduce all possible rules: + + a -> b, c + b -> c + + + implications: [] of (a,b) + return: {} of a -> set([b, c, ...]) + """ + implications = implications + [(Not(j), Not(i)) for (i, j) in implications] + res = defaultdict(set) + full_implications = transitive_closure(implications) + for a, b in full_implications: + if a == b: + continue # skip a->a cyclic input + + res[a].add(b) + + # Clean up tautologies and check consistency + for a, impl in res.items(): + impl.discard(a) + na = Not(a) + if na in impl: + raise ValueError( + 'implications are inconsistent: %s -> %s %s' % (a, na, impl)) + + return res + + +def apply_beta_to_alpha_route(alpha_implications, beta_rules): + """apply additional beta-rules (And conditions) to already-built + alpha implication tables + + TODO: write about + + - static extension of alpha-chains + - attaching refs to beta-nodes to alpha chains + + + e.g. + + alpha_implications: + + a -> [b, !c, d] + b -> [d] + ... + + + beta_rules: + + &(b,d) -> e + + + then we'll extend a's rule to the following + + a -> [b, !c, d, e] + """ + x_impl = {} + for x in alpha_implications.keys(): + x_impl[x] = (set(alpha_implications[x]), []) + for bcond, bimpl in beta_rules: + for bk in bcond.args: + if bk in x_impl: + continue + x_impl[bk] = (set(), []) + + # static extensions to alpha rules: + # A: x -> a,b B: &(a,b) -> c ==> A: x -> a,b,c + seen_static_extension = True + while seen_static_extension: + seen_static_extension = False + + for bcond, bimpl in beta_rules: + if not isinstance(bcond, And): + raise TypeError("Cond is not And") + bargs = set(bcond.args) + for x, (ximpls, bb) in x_impl.items(): + x_all = ximpls | {x} + # A: ... -> a B: &(...) -> a is non-informative + if bimpl not in x_all and bargs.issubset(x_all): + ximpls.add(bimpl) + + # we introduced new implication - now we have to restore + # completeness of the whole set. + bimpl_impl = x_impl.get(bimpl) + if bimpl_impl is not None: + ximpls |= bimpl_impl[0] + seen_static_extension = True + + # attach beta-nodes which can be possibly triggered by an alpha-chain + for bidx, (bcond, bimpl) in enumerate(beta_rules): + bargs = set(bcond.args) + for x, (ximpls, bb) in x_impl.items(): + x_all = ximpls | {x} + # A: ... -> a B: &(...) -> a (non-informative) + if bimpl in x_all: + continue + # A: x -> a... B: &(!a,...) -> ... (will never trigger) + # A: x -> a... B: &(...) -> !a (will never trigger) + if any(Not(xi) in bargs or Not(xi) == bimpl for xi in x_all): + continue + + if bargs & x_all: + bb.append(bidx) + + return x_impl + + +def rules_2prereq(rules): + """build prerequisites table from rules + + Description by example + ---------------------- + + given set of logic rules: + + a -> b, c + b -> c + + we build prerequisites (from what points something can be deduced): + + b <- a + c <- a, b + + rules: {} of a -> [b, c, ...] + return: {} of c <- [a, b, ...] + + Note however, that this prerequisites may be *not* enough to prove a + fact. An example is 'a -> b' rule, where prereq(a) is b, and prereq(b) + is a. That's because a=T -> b=T, and b=F -> a=F, but a=F -> b=? + """ + prereq = defaultdict(set) + for (a, _), impl in rules.items(): + if isinstance(a, Not): + a = a.args[0] + for (i, _) in impl: + if isinstance(i, Not): + i = i.args[0] + prereq[i].add(a) + return prereq + +################ +# RULES PROVER # +################ + + +class TautologyDetected(Exception): + """(internal) Prover uses it for reporting detected tautology""" + pass + + +class Prover: + """ai - prover of logic rules + + given a set of initial rules, Prover tries to prove all possible rules + which follow from given premises. + + As a result proved_rules are always either in one of two forms: alpha or + beta: + + Alpha rules + ----------- + + This are rules of the form:: + + a -> b & c & d & ... + + + Beta rules + ---------- + + This are rules of the form:: + + &(a,b,...) -> c & d & ... + + + i.e. beta rules are join conditions that say that something follows when + *several* facts are true at the same time. + """ + + def __init__(self): + self.proved_rules = [] + self._rules_seen = set() + + def split_alpha_beta(self): + """split proved rules into alpha and beta chains""" + rules_alpha = [] # a -> b + rules_beta = [] # &(...) -> b + for a, b in self.proved_rules: + if isinstance(a, And): + rules_beta.append((a, b)) + else: + rules_alpha.append((a, b)) + return rules_alpha, rules_beta + + @property + def rules_alpha(self): + return self.split_alpha_beta()[0] + + @property + def rules_beta(self): + return self.split_alpha_beta()[1] + + def process_rule(self, a, b): + """process a -> b rule""" # TODO write more? + if (not a) or isinstance(b, bool): + return + if isinstance(a, bool): + return + if (a, b) in self._rules_seen: + return + else: + self._rules_seen.add((a, b)) + + # this is the core of processing + try: + self._process_rule(a, b) + except TautologyDetected: + pass + + def _process_rule(self, a, b): + # right part first + + # a -> b & c --> a -> b ; a -> c + # (?) FIXME this is only correct when b & c != null ! + + if isinstance(b, And): + sorted_bargs = sorted(b.args, key=str) + for barg in sorted_bargs: + self.process_rule(a, barg) + + # a -> b | c --> !b & !c -> !a + # --> a & !b -> c + # --> a & !c -> b + elif isinstance(b, Or): + sorted_bargs = sorted(b.args, key=str) + # detect tautology first + if not isinstance(a, Logic): # Atom + # tautology: a -> a|c|... + if a in sorted_bargs: + raise TautologyDetected(a, b, 'a -> a|c|...') + self.process_rule(And(*[Not(barg) for barg in b.args]), Not(a)) + + for bidx in range(len(sorted_bargs)): + barg = sorted_bargs[bidx] + brest = sorted_bargs[:bidx] + sorted_bargs[bidx + 1:] + self.process_rule(And(a, Not(barg)), Or(*brest)) + + # left part + + # a & b -> c --> IRREDUCIBLE CASE -- WE STORE IT AS IS + # (this will be the basis of beta-network) + elif isinstance(a, And): + sorted_aargs = sorted(a.args, key=str) + if b in sorted_aargs: + raise TautologyDetected(a, b, 'a & b -> a') + self.proved_rules.append((a, b)) + # XXX NOTE at present we ignore !c -> !a | !b + + elif isinstance(a, Or): + sorted_aargs = sorted(a.args, key=str) + if b in sorted_aargs: + raise TautologyDetected(a, b, 'a | b -> a') + for aarg in sorted_aargs: + self.process_rule(aarg, b) + + else: + # both `a` and `b` are atoms + self.proved_rules.append((a, b)) # a -> b + self.proved_rules.append((Not(b), Not(a))) # !b -> !a + +######################################## + + +class FactRules: + """Rules that describe how to deduce facts in logic space + + When defined, these rules allow implications to quickly be determined + for a set of facts. For this precomputed deduction tables are used. + see `deduce_all_facts` (forward-chaining) + + Also it is possible to gather prerequisites for a fact, which is tried + to be proven. (backward-chaining) + + + Definition Syntax + ----------------- + + a -> b -- a=T -> b=T (and automatically b=F -> a=F) + a -> !b -- a=T -> b=F + a == b -- a -> b & b -> a + a -> b & c -- a=T -> b=T & c=T + # TODO b | c + + + Internals + --------- + + .full_implications[k, v]: all the implications of fact k=v + .beta_triggers[k, v]: beta rules that might be triggered when k=v + .prereq -- {} k <- [] of k's prerequisites + + .defined_facts -- set of defined fact names + """ + + def __init__(self, rules): + """Compile rules into internal lookup tables""" + + if isinstance(rules, str): + rules = rules.splitlines() + + # --- parse and process rules --- + P = Prover() + + for rule in rules: + # XXX `a` is hardcoded to be always atom + a, op, b = rule.split(None, 2) + + a = Logic.fromstring(a) + b = Logic.fromstring(b) + + if op == '->': + P.process_rule(a, b) + elif op == '==': + P.process_rule(a, b) + P.process_rule(b, a) + else: + raise ValueError('unknown op %r' % op) + + # --- build deduction networks --- + self.beta_rules = [] + for bcond, bimpl in P.rules_beta: + self.beta_rules.append( + ({_as_pair(a) for a in bcond.args}, _as_pair(bimpl))) + + # deduce alpha implications + impl_a = deduce_alpha_implications(P.rules_alpha) + + # now: + # - apply beta rules to alpha chains (static extension), and + # - further associate beta rules to alpha chain (for inference + # at runtime) + impl_ab = apply_beta_to_alpha_route(impl_a, P.rules_beta) + + # extract defined fact names + self.defined_facts = {_base_fact(k) for k in impl_ab.keys()} + + # build rels (forward chains) + full_implications = defaultdict(set) + beta_triggers = defaultdict(set) + for k, (impl, betaidxs) in impl_ab.items(): + full_implications[_as_pair(k)] = {_as_pair(i) for i in impl} + beta_triggers[_as_pair(k)] = betaidxs + + self.full_implications = full_implications + self.beta_triggers = beta_triggers + + # build prereq (backward chains) + prereq = defaultdict(set) + rel_prereq = rules_2prereq(full_implications) + for k, pitems in rel_prereq.items(): + prereq[k] |= pitems + self.prereq = prereq + + def _to_python(self) -> str: + """ Generate a string with plain python representation of the instance """ + return '\n'.join(self.print_rules()) + + @classmethod + def _from_python(cls, data : dict): + """ Generate an instance from the plain python representation """ + self = cls('') + for key in ['full_implications', 'beta_triggers', 'prereq']: + d=defaultdict(set) + d.update(data[key]) + setattr(self, key, d) + self.beta_rules = data['beta_rules'] + self.defined_facts = set(data['defined_facts']) + + return self + + def _defined_facts_lines(self): + yield 'defined_facts = [' + for fact in sorted(self.defined_facts): + yield f' {fact!r},' + yield '] # defined_facts' + + def _full_implications_lines(self): + yield 'full_implications = dict( [' + for fact in sorted(self.defined_facts): + for value in (True, False): + yield f' # Implications of {fact} = {value}:' + yield f' (({fact!r}, {value!r}), set( (' + implications = self.full_implications[(fact, value)] + for implied in sorted(implications): + yield f' {implied!r},' + yield ' ) ),' + yield ' ),' + yield ' ] ) # full_implications' + + def _prereq_lines(self): + yield 'prereq = {' + yield '' + for fact in sorted(self.prereq): + yield f' # facts that could determine the value of {fact}' + yield f' {fact!r}: {{' + for pfact in sorted(self.prereq[fact]): + yield f' {pfact!r},' + yield ' },' + yield '' + yield '} # prereq' + + def _beta_rules_lines(self): + reverse_implications = defaultdict(list) + for n, (pre, implied) in enumerate(self.beta_rules): + reverse_implications[implied].append((pre, n)) + + yield '# Note: the order of the beta rules is used in the beta_triggers' + yield 'beta_rules = [' + yield '' + m = 0 + indices = {} + for implied in sorted(reverse_implications): + fact, value = implied + yield f' # Rules implying {fact} = {value}' + for pre, n in reverse_implications[implied]: + indices[n] = m + m += 1 + setstr = ", ".join(map(str, sorted(pre))) + yield f' ({{{setstr}}},' + yield f' {implied!r}),' + yield '' + yield '] # beta_rules' + + yield 'beta_triggers = {' + for query in sorted(self.beta_triggers): + fact, value = query + triggers = [indices[n] for n in self.beta_triggers[query]] + yield f' {query!r}: {triggers!r},' + yield '} # beta_triggers' + + def print_rules(self) -> Iterator[str]: + """ Returns a generator with lines to represent the facts and rules """ + yield from self._defined_facts_lines() + yield '' + yield '' + yield from self._full_implications_lines() + yield '' + yield '' + yield from self._prereq_lines() + yield '' + yield '' + yield from self._beta_rules_lines() + yield '' + yield '' + yield "generated_assumptions = {'defined_facts': defined_facts, 'full_implications': full_implications," + yield " 'prereq': prereq, 'beta_rules': beta_rules, 'beta_triggers': beta_triggers}" + + +class InconsistentAssumptions(ValueError): + def __str__(self): + kb, fact, value = self.args + return "%s, %s=%s" % (kb, fact, value) + + +class FactKB(dict): + """ + A simple propositional knowledge base relying on compiled inference rules. + """ + def __str__(self): + return '{\n%s}' % ',\n'.join( + ["\t%s: %s" % i for i in sorted(self.items())]) + + def __init__(self, rules): + self.rules = rules + + def _tell(self, k, v): + """Add fact k=v to the knowledge base. + + Returns True if the KB has actually been updated, False otherwise. + """ + if k in self and self[k] is not None: + if self[k] == v: + return False + else: + raise InconsistentAssumptions(self, k, v) + else: + self[k] = v + return True + + # ********************************************* + # * This is the workhorse, so keep it *fast*. * + # ********************************************* + def deduce_all_facts(self, facts): + """ + Update the KB with all the implications of a list of facts. + + Facts can be specified as a dictionary or as a list of (key, value) + pairs. + """ + # keep frequently used attributes locally, so we'll avoid extra + # attribute access overhead + full_implications = self.rules.full_implications + beta_triggers = self.rules.beta_triggers + beta_rules = self.rules.beta_rules + + if isinstance(facts, dict): + facts = facts.items() + + while facts: + beta_maytrigger = set() + + # --- alpha chains --- + for k, v in facts: + if not self._tell(k, v) or v is None: + continue + + # lookup routing tables + for key, value in full_implications[k, v]: + self._tell(key, value) + + beta_maytrigger.update(beta_triggers[k, v]) + + # --- beta chains --- + facts = [] + for bidx in beta_maytrigger: + bcond, bimpl = beta_rules[bidx] + if all(self.get(k) is v for k, v in bcond): + facts.append(bimpl) diff --git a/lib/python3.12/site-packages/sympy/core/function.py b/lib/python3.12/site-packages/sympy/core/function.py new file mode 100644 index 0000000000000000000000000000000000000000..62d45d01ad989e8312a39010e9ded363a12c50e6 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/core/function.py @@ -0,0 +1,3393 @@ +""" +There are three types of functions implemented in SymPy: + + 1) defined functions (in the sense that they can be evaluated) like + exp or sin; they have a name and a body: + f = exp + 2) undefined function which have a name but no body. Undefined + functions can be defined using a Function class as follows: + f = Function('f') + (the result will be a Function instance) + 3) anonymous function (or lambda function) which have a body (defined + with dummy variables) but have no name: + f = Lambda(x, exp(x)*x) + f = Lambda((x, y), exp(x)*y) + The fourth type of functions are composites, like (sin + cos)(x); these work in + SymPy core, but are not yet part of SymPy. + + Examples + ======== + + >>> import sympy + >>> f = sympy.Function("f") + >>> from sympy.abc import x + >>> f(x) + f(x) + >>> print(sympy.srepr(f(x).func)) + Function('f') + >>> f(x).args + (x,) + +""" + +from __future__ import annotations +from typing import Any +from collections.abc import Iterable + +from .add import Add +from .basic import Basic, _atomic +from .cache import cacheit +from .containers import Tuple, Dict +from .decorators import _sympifyit +from .evalf import pure_complex +from .expr import Expr, AtomicExpr +from .logic import fuzzy_and, fuzzy_or, fuzzy_not, FuzzyBool +from .mul import Mul +from .numbers import Rational, Float, Integer +from .operations import LatticeOp +from .parameters import global_parameters +from .rules import Transform +from .singleton import S +from .sympify import sympify, _sympify + +from .sorting import default_sort_key, ordered +from sympy.utilities.exceptions import (sympy_deprecation_warning, + SymPyDeprecationWarning, ignore_warnings) +from sympy.utilities.iterables import (has_dups, sift, iterable, + is_sequence, uniq, topological_sort) +from sympy.utilities.lambdify import MPMATH_TRANSLATIONS +from sympy.utilities.misc import as_int, filldedent, func_name + +import mpmath +from mpmath.libmp.libmpf import prec_to_dps + +import inspect +from collections import Counter + +def _coeff_isneg(a): + """Return True if the leading Number is negative. + + Examples + ======== + + >>> from sympy.core.function import _coeff_isneg + >>> from sympy import S, Symbol, oo, pi + >>> _coeff_isneg(-3*pi) + True + >>> _coeff_isneg(S(3)) + False + >>> _coeff_isneg(-oo) + True + >>> _coeff_isneg(Symbol('n', negative=True)) # coeff is 1 + False + + For matrix expressions: + + >>> from sympy import MatrixSymbol, sqrt + >>> A = MatrixSymbol("A", 3, 3) + >>> _coeff_isneg(-sqrt(2)*A) + True + >>> _coeff_isneg(sqrt(2)*A) + False + """ + + if a.is_MatMul: + a = a.args[0] + if a.is_Mul: + a = a.args[0] + return a.is_Number and a.is_extended_negative + + +class PoleError(Exception): + pass + + +class ArgumentIndexError(ValueError): + def __str__(self): + return ("Invalid operation with argument number %s for Function %s" % + (self.args[1], self.args[0])) + + +class BadSignatureError(TypeError): + '''Raised when a Lambda is created with an invalid signature''' + pass + + +class BadArgumentsError(TypeError): + '''Raised when a Lambda is called with an incorrect number of arguments''' + pass + + +# Python 3 version that does not raise a Deprecation warning +def arity(cls): + """Return the arity of the function if it is known, else None. + + Explanation + =========== + + When default values are specified for some arguments, they are + optional and the arity is reported as a tuple of possible values. + + Examples + ======== + + >>> from sympy import arity, log + >>> arity(lambda x: x) + 1 + >>> arity(log) + (1, 2) + >>> arity(lambda *x: sum(x)) is None + True + """ + eval_ = getattr(cls, 'eval', cls) + + parameters = inspect.signature(eval_).parameters.items() + if [p for _, p in parameters if p.kind == p.VAR_POSITIONAL]: + return + p_or_k = [p for _, p in parameters if p.kind == p.POSITIONAL_OR_KEYWORD] + # how many have no default and how many have a default value + no, yes = map(len, sift(p_or_k, + lambda p:p.default == p.empty, binary=True)) + return no if not yes else tuple(range(no, no + yes + 1)) + +class FunctionClass(type): + """ + Base class for function classes. FunctionClass is a subclass of type. + + Use Function('' [ , signature ]) to create + undefined function classes. + """ + _new = type.__new__ + + def __init__(cls, *args, **kwargs): + # honor kwarg value or class-defined value before using + # the number of arguments in the eval function (if present) + nargs = kwargs.pop('nargs', cls.__dict__.get('nargs', arity(cls))) + if nargs is None and 'nargs' not in cls.__dict__: + for supcls in cls.__mro__: + if hasattr(supcls, '_nargs'): + nargs = supcls._nargs + break + else: + continue + + # Canonicalize nargs here; change to set in nargs. + if is_sequence(nargs): + if not nargs: + raise ValueError(filldedent(''' + Incorrectly specified nargs as %s: + if there are no arguments, it should be + `nargs = 0`; + if there are any number of arguments, + it should be + `nargs = None`''' % str(nargs))) + nargs = tuple(ordered(set(nargs))) + elif nargs is not None: + nargs = (as_int(nargs),) + cls._nargs = nargs + + # When __init__ is called from UndefinedFunction it is called with + # just one arg but when it is called from subclassing Function it is + # called with the usual (name, bases, namespace) type() signature. + if len(args) == 3: + namespace = args[2] + if 'eval' in namespace and not isinstance(namespace['eval'], classmethod): + raise TypeError("eval on Function subclasses should be a class method (defined with @classmethod)") + + @property + def __signature__(self): + """ + Allow Python 3's inspect.signature to give a useful signature for + Function subclasses. + """ + # Python 3 only, but backports (like the one in IPython) still might + # call this. + try: + from inspect import signature + except ImportError: + return None + + # TODO: Look at nargs + return signature(self.eval) + + @property + def free_symbols(self): + return set() + + @property + def xreplace(self): + # Function needs args so we define a property that returns + # a function that takes args...and then use that function + # to return the right value + return lambda rule, **_: rule.get(self, self) + + @property + def nargs(self): + """Return a set of the allowed number of arguments for the function. + + Examples + ======== + + >>> from sympy import Function + >>> f = Function('f') + + If the function can take any number of arguments, the set of whole + numbers is returned: + + >>> Function('f').nargs + Naturals0 + + If the function was initialized to accept one or more arguments, a + corresponding set will be returned: + + >>> Function('f', nargs=1).nargs + {1} + >>> Function('f', nargs=(2, 1)).nargs + {1, 2} + + The undefined function, after application, also has the nargs + attribute; the actual number of arguments is always available by + checking the ``args`` attribute: + + >>> f = Function('f') + >>> f(1).nargs + Naturals0 + >>> len(f(1).args) + 1 + """ + from sympy.sets.sets import FiniteSet + # XXX it would be nice to handle this in __init__ but there are import + # problems with trying to import FiniteSet there + return FiniteSet(*self._nargs) if self._nargs else S.Naturals0 + + def _valid_nargs(self, n : int) -> bool: + """ Return True if the specified integer is a valid number of arguments + + The number of arguments n is guaranteed to be an integer and positive + + """ + if self._nargs: + return n in self._nargs + + nargs = self.nargs + return nargs is S.Naturals0 or n in nargs + + def __repr__(cls): + return cls.__name__ + + +class Application(Basic, metaclass=FunctionClass): + """ + Base class for applied functions. + + Explanation + =========== + + Instances of Application represent the result of applying an application of + any type to any object. + """ + + is_Function = True + + @cacheit + def __new__(cls, *args, **options): + from sympy.sets.fancysets import Naturals0 + from sympy.sets.sets import FiniteSet + + args = list(map(sympify, args)) + evaluate = options.pop('evaluate', global_parameters.evaluate) + # WildFunction (and anything else like it) may have nargs defined + # and we throw that value away here + options.pop('nargs', None) + + if options: + raise ValueError("Unknown options: %s" % options) + + if evaluate: + evaluated = cls.eval(*args) + if evaluated is not None: + return evaluated + + obj = super().__new__(cls, *args, **options) + + # make nargs uniform here + sentinel = object() + objnargs = getattr(obj, "nargs", sentinel) + if objnargs is not sentinel: + # things passing through here: + # - functions subclassed from Function (e.g. myfunc(1).nargs) + # - functions like cos(1).nargs + # - AppliedUndef with given nargs like Function('f', nargs=1)(1).nargs + # Canonicalize nargs here + if is_sequence(objnargs): + nargs = tuple(ordered(set(objnargs))) + elif objnargs is not None: + nargs = (as_int(objnargs),) + else: + nargs = None + else: + # things passing through here: + # - WildFunction('f').nargs + # - AppliedUndef with no nargs like Function('f')(1).nargs + nargs = obj._nargs # note the underscore here + # convert to FiniteSet + obj.nargs = FiniteSet(*nargs) if nargs else Naturals0() + return obj + + @classmethod + def eval(cls, *args): + """ + Returns a canonical form of cls applied to arguments args. + + Explanation + =========== + + The ``eval()`` method is called when the class ``cls`` is about to be + instantiated and it should return either some simplified instance + (possible of some other class), or if the class ``cls`` should be + unmodified, return None. + + Examples of ``eval()`` for the function "sign" + + .. code-block:: python + + @classmethod + def eval(cls, arg): + if arg is S.NaN: + return S.NaN + if arg.is_zero: return S.Zero + if arg.is_positive: return S.One + if arg.is_negative: return S.NegativeOne + if isinstance(arg, Mul): + coeff, terms = arg.as_coeff_Mul(rational=True) + if coeff is not S.One: + return cls(coeff) * cls(terms) + + """ + return + + @property + def func(self): + return self.__class__ + + def _eval_subs(self, old, new): + if (old.is_Function and new.is_Function and + callable(old) and callable(new) and + old == self.func and len(self.args) in new.nargs): + return new(*[i._subs(old, new) for i in self.args]) + + +class Function(Application, Expr): + r""" + Base class for applied mathematical functions. + + It also serves as a constructor for undefined function classes. + + See the :ref:`custom-functions` guide for details on how to subclass + ``Function`` and what methods can be defined. + + Examples + ======== + + **Undefined Functions** + + To create an undefined function, pass a string of the function name to + ``Function``. + + >>> from sympy import Function, Symbol + >>> x = Symbol('x') + >>> f = Function('f') + >>> g = Function('g')(x) + >>> f + f + >>> f(x) + f(x) + >>> g + g(x) + >>> f(x).diff(x) + Derivative(f(x), x) + >>> g.diff(x) + Derivative(g(x), x) + + Assumptions can be passed to ``Function`` the same as with a + :class:`~.Symbol`. Alternatively, you can use a ``Symbol`` with + assumptions for the function name and the function will inherit the name + and assumptions associated with the ``Symbol``: + + >>> f_real = Function('f', real=True) + >>> f_real(x).is_real + True + >>> f_real_inherit = Function(Symbol('f', real=True)) + >>> f_real_inherit(x).is_real + True + + Note that assumptions on a function are unrelated to the assumptions on + the variables it is called on. If you want to add a relationship, subclass + ``Function`` and define custom assumptions handler methods. See the + :ref:`custom-functions-assumptions` section of the :ref:`custom-functions` + guide for more details. + + **Custom Function Subclasses** + + The :ref:`custom-functions` guide has several + :ref:`custom-functions-complete-examples` of how to subclass ``Function`` + to create a custom function. + + """ + + @property + def _diff_wrt(self): + return False + + @cacheit + def __new__(cls, *args, **options): + # Handle calls like Function('f') + if cls is Function: + return UndefinedFunction(*args, **options) + + n = len(args) + + if not cls._valid_nargs(n): + # XXX: exception message must be in exactly this format to + # make it work with NumPy's functions like vectorize(). See, + # for example, https://github.com/numpy/numpy/issues/1697. + # The ideal solution would be just to attach metadata to + # the exception and change NumPy to take advantage of this. + temp = ('%(name)s takes %(qual)s %(args)s ' + 'argument%(plural)s (%(given)s given)') + raise TypeError(temp % { + 'name': cls, + 'qual': 'exactly' if len(cls.nargs) == 1 else 'at least', + 'args': min(cls.nargs), + 'plural': 's'*(min(cls.nargs) != 1), + 'given': n}) + + evaluate = options.get('evaluate', global_parameters.evaluate) + result = super().__new__(cls, *args, **options) + if evaluate and isinstance(result, cls) and result.args: + _should_evalf = [cls._should_evalf(a) for a in result.args] + pr2 = min(_should_evalf) + if pr2 > 0: + pr = max(_should_evalf) + result = result.evalf(prec_to_dps(pr)) + + return _sympify(result) + + @classmethod + def _should_evalf(cls, arg): + """ + Decide if the function should automatically evalf(). + + Explanation + =========== + + By default (in this implementation), this happens if (and only if) the + ARG is a floating point number (including complex numbers). + This function is used by __new__. + + Returns the precision to evalf to, or -1 if it should not evalf. + """ + if arg.is_Float: + return arg._prec + if not arg.is_Add: + return -1 + m = pure_complex(arg) + if m is None: + return -1 + # the elements of m are of type Number, so have a _prec + return max(m[0]._prec, m[1]._prec) + + @classmethod + def class_key(cls): + from sympy.sets.fancysets import Naturals0 + funcs = { + 'exp': 10, + 'log': 11, + 'sin': 20, + 'cos': 21, + 'tan': 22, + 'cot': 23, + 'sinh': 30, + 'cosh': 31, + 'tanh': 32, + 'coth': 33, + 'conjugate': 40, + 're': 41, + 'im': 42, + 'arg': 43, + } + name = cls.__name__ + + try: + i = funcs[name] + except KeyError: + i = 0 if isinstance(cls.nargs, Naturals0) else 10000 + + return 4, i, name + + def _eval_evalf(self, prec): + + def _get_mpmath_func(fname): + """Lookup mpmath function based on name""" + if isinstance(self, AppliedUndef): + # Shouldn't lookup in mpmath but might have ._imp_ + return None + + if not hasattr(mpmath, fname): + fname = MPMATH_TRANSLATIONS.get(fname, None) + if fname is None: + return None + return getattr(mpmath, fname) + + _eval_mpmath = getattr(self, '_eval_mpmath', None) + if _eval_mpmath is None: + func = _get_mpmath_func(self.func.__name__) + args = self.args + else: + func, args = _eval_mpmath() + + # Fall-back evaluation + if func is None: + imp = getattr(self, '_imp_', None) + if imp is None: + return None + try: + return Float(imp(*[i.evalf(prec) for i in self.args]), prec) + except (TypeError, ValueError): + return None + + # Convert all args to mpf or mpc + # Convert the arguments to *higher* precision than requested for the + # final result. + # XXX + 5 is a guess, it is similar to what is used in evalf.py. Should + # we be more intelligent about it? + try: + args = [arg._to_mpmath(prec + 5) for arg in args] + def bad(m): + from mpmath import mpf, mpc + # the precision of an mpf value is the last element + # if that is 1 (and m[1] is not 1 which would indicate a + # power of 2), then the eval failed; so check that none of + # the arguments failed to compute to a finite precision. + # Note: An mpc value has two parts, the re and imag tuple; + # check each of those parts, too. Anything else is allowed to + # pass + if isinstance(m, mpf): + m = m._mpf_ + return m[1] !=1 and m[-1] == 1 + elif isinstance(m, mpc): + m, n = m._mpc_ + return m[1] !=1 and m[-1] == 1 and \ + n[1] !=1 and n[-1] == 1 + else: + return False + if any(bad(a) for a in args): + raise ValueError # one or more args failed to compute with significance + except ValueError: + return + + with mpmath.workprec(prec): + v = func(*args) + + return Expr._from_mpmath(v, prec) + + def _eval_derivative(self, s): + # f(x).diff(s) -> x.diff(s) * f.fdiff(1)(s) + i = 0 + l = [] + for a in self.args: + i += 1 + da = a.diff(s) + if da.is_zero: + continue + try: + df = self.fdiff(i) + except ArgumentIndexError: + df = Function.fdiff(self, i) + l.append(df * da) + return Add(*l) + + def _eval_is_commutative(self): + return fuzzy_and(a.is_commutative for a in self.args) + + def _eval_is_meromorphic(self, x, a): + if not self.args: + return True + if any(arg.has(x) for arg in self.args[1:]): + return False + + arg = self.args[0] + if not arg._eval_is_meromorphic(x, a): + return None + + return fuzzy_not(type(self).is_singular(arg.subs(x, a))) + + _singularities: FuzzyBool | tuple[Expr, ...] = None + + @classmethod + def is_singular(cls, a): + """ + Tests whether the argument is an essential singularity + or a branch point, or the functions is non-holomorphic. + """ + ss = cls._singularities + if ss in (True, None, False): + return ss + + return fuzzy_or(a.is_infinite if s is S.ComplexInfinity + else (a - s).is_zero for s in ss) + + def as_base_exp(self): + """ + Returns the method as the 2-tuple (base, exponent). + """ + return self, S.One + + def _eval_aseries(self, n, args0, x, logx): + """ + Compute an asymptotic expansion around args0, in terms of self.args. + This function is only used internally by _eval_nseries and should not + be called directly; derived classes can overwrite this to implement + asymptotic expansions. + """ + raise PoleError(filldedent(''' + Asymptotic expansion of %s around %s is + not implemented.''' % (type(self), args0))) + + def _eval_nseries(self, x, n, logx, cdir=0): + """ + This function does compute series for multivariate functions, + but the expansion is always in terms of *one* variable. + + Examples + ======== + + >>> from sympy import atan2 + >>> from sympy.abc import x, y + >>> atan2(x, y).series(x, n=2) + atan2(0, y) + x/y + O(x**2) + >>> atan2(x, y).series(y, n=2) + -y/x + atan2(x, 0) + O(y**2) + + This function also computes asymptotic expansions, if necessary + and possible: + + >>> from sympy import loggamma + >>> loggamma(1/x)._eval_nseries(x,0,None) + -1/x - log(x)/x + log(x)/2 + O(1) + + """ + from .symbol import uniquely_named_symbol + from sympy.series.order import Order + from sympy.sets.sets import FiniteSet + args = self.args + args0 = [t.limit(x, 0) for t in args] + if any(t.is_finite is False for t in args0): + from .numbers import oo, zoo, nan + a = [t.as_leading_term(x, logx=logx) for t in args] + a0 = [t.limit(x, 0) for t in a] + if any(t.has(oo, -oo, zoo, nan) for t in a0): + return self._eval_aseries(n, args0, x, logx) + # Careful: the argument goes to oo, but only logarithmically so. We + # are supposed to do a power series expansion "around the + # logarithmic term". e.g. + # f(1+x+log(x)) + # -> f(1+logx) + x*f'(1+logx) + O(x**2) + # where 'logx' is given in the argument + a = [t._eval_nseries(x, n, logx) for t in args] + z = [r - r0 for (r, r0) in zip(a, a0)] + p = [Dummy() for _ in z] + q = [] + v = None + for ai, zi, pi in zip(a0, z, p): + if zi.has(x): + if v is not None: + raise NotImplementedError + q.append(ai + pi) + v = pi + else: + q.append(ai) + e1 = self.func(*q) + if v is None: + return e1 + s = e1._eval_nseries(v, n, logx) + o = s.getO() + s = s.removeO() + s = s.subs(v, zi).expand() + Order(o.expr.subs(v, zi), x) + return s + if (self.func.nargs is S.Naturals0 + or (self.func.nargs == FiniteSet(1) and args0[0]) + or any(c > 1 for c in self.func.nargs)): + e = self + e1 = e.expand() + if e == e1: + #for example when e = sin(x+1) or e = sin(cos(x)) + #let's try the general algorithm + if len(e.args) == 1: + # issue 14411 + e = e.func(e.args[0].cancel()) + term = e.subs(x, S.Zero) + if term.is_finite is False or term is S.NaN: + raise PoleError("Cannot expand %s around 0" % (self)) + series = term + fact = S.One + + _x = uniquely_named_symbol('xi', self) + e = e.subs(x, _x) + for i in range(1, n): + fact *= Rational(i) + e = e.diff(_x) + subs = e.subs(_x, S.Zero) + if subs is S.NaN: + # try to evaluate a limit if we have to + subs = e.limit(_x, S.Zero) + if subs.is_finite is False: + raise PoleError("Cannot expand %s around 0" % (self)) + term = subs*(x**i)/fact + term = term.expand() + series += term + return series + Order(x**n, x) + return e1.nseries(x, n=n, logx=logx) + arg = self.args[0] + l = [] + g = None + # try to predict a number of terms needed + nterms = n + 2 + cf = Order(arg.as_leading_term(x), x).getn() + if cf != 0: + nterms = (n/cf).ceiling() + for i in range(nterms): + g = self.taylor_term(i, arg, g) + g = g.nseries(x, n=n, logx=logx) + l.append(g) + return Add(*l) + Order(x**n, x) + + def fdiff(self, argindex=1): + """ + Returns the first derivative of the function. + """ + if not (1 <= argindex <= len(self.args)): + raise ArgumentIndexError(self, argindex) + ix = argindex - 1 + A = self.args[ix] + if A._diff_wrt: + if len(self.args) == 1 or not A.is_Symbol: + return _derivative_dispatch(self, A) + for i, v in enumerate(self.args): + if i != ix and A in v.free_symbols: + # it can't be in any other argument's free symbols + # issue 8510 + break + else: + return _derivative_dispatch(self, A) + + # See issue 4624 and issue 4719, 5600 and 8510 + D = Dummy('xi_%i' % argindex, dummy_index=hash(A)) + args = self.args[:ix] + (D,) + self.args[ix + 1:] + return Subs(Derivative(self.func(*args), D), D, A) + + def _eval_as_leading_term(self, x, logx=None, cdir=0): + """Stub that should be overridden by new Functions to return + the first non-zero term in a series if ever an x-dependent + argument whose leading term vanishes as x -> 0 might be encountered. + See, for example, cos._eval_as_leading_term. + """ + from sympy.series.order import Order + args = [a.as_leading_term(x, logx=logx) for a in self.args] + o = Order(1, x) + if any(x in a.free_symbols and o.contains(a) for a in args): + # Whereas x and any finite number are contained in O(1, x), + # expressions like 1/x are not. If any arg simplified to a + # vanishing expression as x -> 0 (like x or x**2, but not + # 3, 1/x, etc...) then the _eval_as_leading_term is needed + # to supply the first non-zero term of the series, + # + # e.g. expression leading term + # ---------- ------------ + # cos(1/x) cos(1/x) + # cos(cos(x)) cos(1) + # cos(x) 1 <- _eval_as_leading_term needed + # sin(x) x <- _eval_as_leading_term needed + # + raise NotImplementedError( + '%s has no _eval_as_leading_term routine' % self.func) + else: + return self + + +class AppliedUndef(Function): + """ + Base class for expressions resulting from the application of an undefined + function. + """ + + is_number = False + + def __new__(cls, *args, **options): + args = list(map(sympify, args)) + u = [a.name for a in args if isinstance(a, UndefinedFunction)] + if u: + raise TypeError('Invalid argument: expecting an expression, not UndefinedFunction%s: %s' % ( + 's'*(len(u) > 1), ', '.join(u))) + obj = super().__new__(cls, *args, **options) + return obj + + def _eval_as_leading_term(self, x, logx=None, cdir=0): + return self + + @property + def _diff_wrt(self): + """ + Allow derivatives wrt to undefined functions. + + Examples + ======== + + >>> from sympy import Function, Symbol + >>> f = Function('f') + >>> x = Symbol('x') + >>> f(x)._diff_wrt + True + >>> f(x).diff(x) + Derivative(f(x), x) + """ + return True + + +class UndefSageHelper: + """ + Helper to facilitate Sage conversion. + """ + def __get__(self, ins, typ): + import sage.all as sage + if ins is None: + return lambda: sage.function(typ.__name__) + else: + args = [arg._sage_() for arg in ins.args] + return lambda : sage.function(ins.__class__.__name__)(*args) + +_undef_sage_helper = UndefSageHelper() + +class UndefinedFunction(FunctionClass): + """ + The (meta)class of undefined functions. + """ + def __new__(mcl, name, bases=(AppliedUndef,), __dict__=None, **kwargs): + from .symbol import _filter_assumptions + # Allow Function('f', real=True) + # and/or Function(Symbol('f', real=True)) + assumptions, kwargs = _filter_assumptions(kwargs) + if isinstance(name, Symbol): + assumptions = name._merge(assumptions) + name = name.name + elif not isinstance(name, str): + raise TypeError('expecting string or Symbol for name') + else: + commutative = assumptions.get('commutative', None) + assumptions = Symbol(name, **assumptions).assumptions0 + if commutative is None: + assumptions.pop('commutative') + __dict__ = __dict__ or {} + # put the `is_*` for into __dict__ + __dict__.update({'is_%s' % k: v for k, v in assumptions.items()}) + # You can add other attributes, although they do have to be hashable + # (but seriously, if you want to add anything other than assumptions, + # just subclass Function) + __dict__.update(kwargs) + # add back the sanitized assumptions without the is_ prefix + kwargs.update(assumptions) + # Save these for __eq__ + __dict__.update({'_kwargs': kwargs}) + # do this for pickling + __dict__['__module__'] = None + obj = super().__new__(mcl, name, bases, __dict__) + obj.name = name + obj._sage_ = _undef_sage_helper + return obj + + def __instancecheck__(cls, instance): + return cls in type(instance).__mro__ + + _kwargs: dict[str, bool | None] = {} + + def __hash__(self): + return hash((self.class_key(), frozenset(self._kwargs.items()))) + + def __eq__(self, other): + return (isinstance(other, self.__class__) and + self.class_key() == other.class_key() and + self._kwargs == other._kwargs) + + def __ne__(self, other): + return not self == other + + @property + def _diff_wrt(self): + return False + + +# XXX: The type: ignore on WildFunction is because mypy complains: +# +# sympy/core/function.py:939: error: Cannot determine type of 'sort_key' in +# base class 'Expr' +# +# Somehow this is because of the @cacheit decorator but it is not clear how to +# fix it. + + +class WildFunction(Function, AtomicExpr): # type: ignore + """ + A WildFunction function matches any function (with its arguments). + + Examples + ======== + + >>> from sympy import WildFunction, Function, cos + >>> from sympy.abc import x, y + >>> F = WildFunction('F') + >>> f = Function('f') + >>> F.nargs + Naturals0 + >>> x.match(F) + >>> F.match(F) + {F_: F_} + >>> f(x).match(F) + {F_: f(x)} + >>> cos(x).match(F) + {F_: cos(x)} + >>> f(x, y).match(F) + {F_: f(x, y)} + + To match functions with a given number of arguments, set ``nargs`` to the + desired value at instantiation: + + >>> F = WildFunction('F', nargs=2) + >>> F.nargs + {2} + >>> f(x).match(F) + >>> f(x, y).match(F) + {F_: f(x, y)} + + To match functions with a range of arguments, set ``nargs`` to a tuple + containing the desired number of arguments, e.g. if ``nargs = (1, 2)`` + then functions with 1 or 2 arguments will be matched. + + >>> F = WildFunction('F', nargs=(1, 2)) + >>> F.nargs + {1, 2} + >>> f(x).match(F) + {F_: f(x)} + >>> f(x, y).match(F) + {F_: f(x, y)} + >>> f(x, y, 1).match(F) + + """ + + # XXX: What is this class attribute used for? + include: set[Any] = set() + + def __init__(cls, name, **assumptions): + from sympy.sets.sets import Set, FiniteSet + cls.name = name + nargs = assumptions.pop('nargs', S.Naturals0) + if not isinstance(nargs, Set): + # Canonicalize nargs here. See also FunctionClass. + if is_sequence(nargs): + nargs = tuple(ordered(set(nargs))) + elif nargs is not None: + nargs = (as_int(nargs),) + nargs = FiniteSet(*nargs) + cls.nargs = nargs + + def matches(self, expr, repl_dict=None, old=False): + if not isinstance(expr, (AppliedUndef, Function)): + return None + if len(expr.args) not in self.nargs: + return None + + if repl_dict is None: + repl_dict = {} + else: + repl_dict = repl_dict.copy() + + repl_dict[self] = expr + return repl_dict + + +class Derivative(Expr): + """ + Carries out differentiation of the given expression with respect to symbols. + + Examples + ======== + + >>> from sympy import Derivative, Function, symbols, Subs + >>> from sympy.abc import x, y + >>> f, g = symbols('f g', cls=Function) + + >>> Derivative(x**2, x, evaluate=True) + 2*x + + Denesting of derivatives retains the ordering of variables: + + >>> Derivative(Derivative(f(x, y), y), x) + Derivative(f(x, y), y, x) + + Contiguously identical symbols are merged into a tuple giving + the symbol and the count: + + >>> Derivative(f(x), x, x, y, x) + Derivative(f(x), (x, 2), y, x) + + If the derivative cannot be performed, and evaluate is True, the + order of the variables of differentiation will be made canonical: + + >>> Derivative(f(x, y), y, x, evaluate=True) + Derivative(f(x, y), x, y) + + Derivatives with respect to undefined functions can be calculated: + + >>> Derivative(f(x)**2, f(x), evaluate=True) + 2*f(x) + + Such derivatives will show up when the chain rule is used to + evalulate a derivative: + + >>> f(g(x)).diff(x) + Derivative(f(g(x)), g(x))*Derivative(g(x), x) + + Substitution is used to represent derivatives of functions with + arguments that are not symbols or functions: + + >>> f(2*x + 3).diff(x) == 2*Subs(f(y).diff(y), y, 2*x + 3) + True + + Notes + ===== + + Simplification of high-order derivatives: + + Because there can be a significant amount of simplification that can be + done when multiple differentiations are performed, results will be + automatically simplified in a fairly conservative fashion unless the + keyword ``simplify`` is set to False. + + >>> from sympy import sqrt, diff, Function, symbols + >>> from sympy.abc import x, y, z + >>> f, g = symbols('f,g', cls=Function) + + >>> e = sqrt((x + 1)**2 + x) + >>> diff(e, (x, 5), simplify=False).count_ops() + 136 + >>> diff(e, (x, 5)).count_ops() + 30 + + Ordering of variables: + + If evaluate is set to True and the expression cannot be evaluated, the + list of differentiation symbols will be sorted, that is, the expression is + assumed to have continuous derivatives up to the order asked. + + Derivative wrt non-Symbols: + + For the most part, one may not differentiate wrt non-symbols. + For example, we do not allow differentiation wrt `x*y` because + there are multiple ways of structurally defining where x*y appears + in an expression: a very strict definition would make + (x*y*z).diff(x*y) == 0. Derivatives wrt defined functions (like + cos(x)) are not allowed, either: + + >>> (x*y*z).diff(x*y) + Traceback (most recent call last): + ... + ValueError: Can't calculate derivative wrt x*y. + + To make it easier to work with variational calculus, however, + derivatives wrt AppliedUndef and Derivatives are allowed. + For example, in the Euler-Lagrange method one may write + F(t, u, v) where u = f(t) and v = f'(t). These variables can be + written explicitly as functions of time:: + + >>> from sympy.abc import t + >>> F = Function('F') + >>> U = f(t) + >>> V = U.diff(t) + + The derivative wrt f(t) can be obtained directly: + + >>> direct = F(t, U, V).diff(U) + + When differentiation wrt a non-Symbol is attempted, the non-Symbol + is temporarily converted to a Symbol while the differentiation + is performed and the same answer is obtained: + + >>> indirect = F(t, U, V).subs(U, x).diff(x).subs(x, U) + >>> assert direct == indirect + + The implication of this non-symbol replacement is that all + functions are treated as independent of other functions and the + symbols are independent of the functions that contain them:: + + >>> x.diff(f(x)) + 0 + >>> g(x).diff(f(x)) + 0 + + It also means that derivatives are assumed to depend only + on the variables of differentiation, not on anything contained + within the expression being differentiated:: + + >>> F = f(x) + >>> Fx = F.diff(x) + >>> Fx.diff(F) # derivative depends on x, not F + 0 + >>> Fxx = Fx.diff(x) + >>> Fxx.diff(Fx) # derivative depends on x, not Fx + 0 + + The last example can be made explicit by showing the replacement + of Fx in Fxx with y: + + >>> Fxx.subs(Fx, y) + Derivative(y, x) + + Since that in itself will evaluate to zero, differentiating + wrt Fx will also be zero: + + >>> _.doit() + 0 + + Replacing undefined functions with concrete expressions + + One must be careful to replace undefined functions with expressions + that contain variables consistent with the function definition and + the variables of differentiation or else insconsistent result will + be obtained. Consider the following example: + + >>> eq = f(x)*g(y) + >>> eq.subs(f(x), x*y).diff(x, y).doit() + y*Derivative(g(y), y) + g(y) + >>> eq.diff(x, y).subs(f(x), x*y).doit() + y*Derivative(g(y), y) + + The results differ because `f(x)` was replaced with an expression + that involved both variables of differentiation. In the abstract + case, differentiation of `f(x)` by `y` is 0; in the concrete case, + the presence of `y` made that derivative nonvanishing and produced + the extra `g(y)` term. + + Defining differentiation for an object + + An object must define ._eval_derivative(symbol) method that returns + the differentiation result. This function only needs to consider the + non-trivial case where expr contains symbol and it should call the diff() + method internally (not _eval_derivative); Derivative should be the only + one to call _eval_derivative. + + Any class can allow derivatives to be taken with respect to + itself (while indicating its scalar nature). See the + docstring of Expr._diff_wrt. + + See Also + ======== + _sort_variable_count + """ + + is_Derivative = True + + @property + def _diff_wrt(self): + """An expression may be differentiated wrt a Derivative if + it is in elementary form. + + Examples + ======== + + >>> from sympy import Function, Derivative, cos + >>> from sympy.abc import x + >>> f = Function('f') + + >>> Derivative(f(x), x)._diff_wrt + True + >>> Derivative(cos(x), x)._diff_wrt + False + >>> Derivative(x + 1, x)._diff_wrt + False + + A Derivative might be an unevaluated form of what will not be + a valid variable of differentiation if evaluated. For example, + + >>> Derivative(f(f(x)), x).doit() + Derivative(f(x), x)*Derivative(f(f(x)), f(x)) + + Such an expression will present the same ambiguities as arise + when dealing with any other product, like ``2*x``, so ``_diff_wrt`` + is False: + + >>> Derivative(f(f(x)), x)._diff_wrt + False + """ + return self.expr._diff_wrt and isinstance(self.doit(), Derivative) + + def __new__(cls, expr, *variables, **kwargs): + expr = sympify(expr) + symbols_or_none = getattr(expr, "free_symbols", None) + has_symbol_set = isinstance(symbols_or_none, set) + + if not has_symbol_set: + raise ValueError(filldedent(''' + Since there are no variables in the expression %s, + it cannot be differentiated.''' % expr)) + + # determine value for variables if it wasn't given + if not variables: + variables = expr.free_symbols + if len(variables) != 1: + if expr.is_number: + return S.Zero + if len(variables) == 0: + raise ValueError(filldedent(''' + Since there are no variables in the expression, + the variable(s) of differentiation must be supplied + to differentiate %s''' % expr)) + else: + raise ValueError(filldedent(''' + Since there is more than one variable in the + expression, the variable(s) of differentiation + must be supplied to differentiate %s''' % expr)) + + # Split the list of variables into a list of the variables we are diff + # wrt, where each element of the list has the form (s, count) where + # s is the entity to diff wrt and count is the order of the + # derivative. + variable_count = [] + array_likes = (tuple, list, Tuple) + + from sympy.tensor.array import Array, NDimArray + + for i, v in enumerate(variables): + if isinstance(v, UndefinedFunction): + raise TypeError( + "cannot differentiate wrt " + "UndefinedFunction: %s" % v) + + if isinstance(v, array_likes): + if len(v) == 0: + # Ignore empty tuples: Derivative(expr, ... , (), ... ) + continue + if isinstance(v[0], array_likes): + # Derive by array: Derivative(expr, ... , [[x, y, z]], ... ) + if len(v) == 1: + v = Array(v[0]) + count = 1 + else: + v, count = v + v = Array(v) + else: + v, count = v + if count == 0: + continue + variable_count.append(Tuple(v, count)) + continue + + v = sympify(v) + if isinstance(v, Integer): + if i == 0: + raise ValueError("First variable cannot be a number: %i" % v) + count = v + prev, prevcount = variable_count[-1] + if prevcount != 1: + raise TypeError("tuple {} followed by number {}".format((prev, prevcount), v)) + if count == 0: + variable_count.pop() + else: + variable_count[-1] = Tuple(prev, count) + else: + count = 1 + variable_count.append(Tuple(v, count)) + + # light evaluation of contiguous, identical + # items: (x, 1), (x, 1) -> (x, 2) + merged = [] + for t in variable_count: + v, c = t + if c.is_negative: + raise ValueError( + 'order of differentiation must be nonnegative') + if merged and merged[-1][0] == v: + c += merged[-1][1] + if not c: + merged.pop() + else: + merged[-1] = Tuple(v, c) + else: + merged.append(t) + variable_count = merged + + # sanity check of variables of differentation; we waited + # until the counts were computed since some variables may + # have been removed because the count was 0 + for v, c in variable_count: + # v must have _diff_wrt True + if not v._diff_wrt: + __ = '' # filler to make error message neater + raise ValueError(filldedent(''' + Can't calculate derivative wrt %s.%s''' % (v, + __))) + + # We make a special case for 0th derivative, because there is no + # good way to unambiguously print this. + if len(variable_count) == 0: + return expr + + evaluate = kwargs.get('evaluate', False) + + if evaluate: + if isinstance(expr, Derivative): + expr = expr.canonical + variable_count = [ + (v.canonical if isinstance(v, Derivative) else v, c) + for v, c in variable_count] + + # Look for a quick exit if there are symbols that don't appear in + # expression at all. Note, this cannot check non-symbols like + # Derivatives as those can be created by intermediate + # derivatives. + zero = False + free = expr.free_symbols + from sympy.matrices.expressions.matexpr import MatrixExpr + + for v, c in variable_count: + vfree = v.free_symbols + if c.is_positive and vfree: + if isinstance(v, AppliedUndef): + # these match exactly since + # x.diff(f(x)) == g(x).diff(f(x)) == 0 + # and are not created by differentiation + D = Dummy() + if not expr.xreplace({v: D}).has(D): + zero = True + break + elif isinstance(v, MatrixExpr): + zero = False + break + elif isinstance(v, Symbol) and v not in free: + zero = True + break + else: + if not free & vfree: + # e.g. v is IndexedBase or Matrix + zero = True + break + if zero: + return cls._get_zero_with_shape_like(expr) + + # make the order of symbols canonical + #TODO: check if assumption of discontinuous derivatives exist + variable_count = cls._sort_variable_count(variable_count) + + # denest + if isinstance(expr, Derivative): + variable_count = list(expr.variable_count) + variable_count + expr = expr.expr + return _derivative_dispatch(expr, *variable_count, **kwargs) + + # we return here if evaluate is False or if there is no + # _eval_derivative method + if not evaluate or not hasattr(expr, '_eval_derivative'): + # return an unevaluated Derivative + if evaluate and variable_count == [(expr, 1)] and expr.is_scalar: + # special hack providing evaluation for classes + # that have defined is_scalar=True but have no + # _eval_derivative defined + return S.One + return Expr.__new__(cls, expr, *variable_count) + + # evaluate the derivative by calling _eval_derivative method + # of expr for each variable + # ------------------------------------------------------------- + nderivs = 0 # how many derivatives were performed + unhandled = [] + from sympy.matrices.matrixbase import MatrixBase + for i, (v, count) in enumerate(variable_count): + + old_expr = expr + old_v = None + + is_symbol = v.is_symbol or isinstance(v, + (Iterable, Tuple, MatrixBase, NDimArray)) + + if not is_symbol: + old_v = v + v = Dummy('xi') + expr = expr.xreplace({old_v: v}) + # Derivatives and UndefinedFunctions are independent + # of all others + clashing = not (isinstance(old_v, (Derivative, AppliedUndef))) + if v not in expr.free_symbols and not clashing: + return expr.diff(v) # expr's version of 0 + if not old_v.is_scalar and not hasattr( + old_v, '_eval_derivative'): + # special hack providing evaluation for classes + # that have defined is_scalar=True but have no + # _eval_derivative defined + expr *= old_v.diff(old_v) + + obj = cls._dispatch_eval_derivative_n_times(expr, v, count) + if obj is not None and obj.is_zero: + return obj + + nderivs += count + + if old_v is not None: + if obj is not None: + # remove the dummy that was used + obj = obj.subs(v, old_v) + # restore expr + expr = old_expr + + if obj is None: + # we've already checked for quick-exit conditions + # that give 0 so the remaining variables + # are contained in the expression but the expression + # did not compute a derivative so we stop taking + # derivatives + unhandled = variable_count[i:] + break + + expr = obj + + # what we have so far can be made canonical + expr = expr.replace( + lambda x: isinstance(x, Derivative), + lambda x: x.canonical) + + if unhandled: + if isinstance(expr, Derivative): + unhandled = list(expr.variable_count) + unhandled + expr = expr.expr + expr = Expr.__new__(cls, expr, *unhandled) + + if (nderivs > 1) == True and kwargs.get('simplify', True): + from .exprtools import factor_terms + from sympy.simplify.simplify import signsimp + expr = factor_terms(signsimp(expr)) + return expr + + @property + def canonical(cls): + return cls.func(cls.expr, + *Derivative._sort_variable_count(cls.variable_count)) + + @classmethod + def _sort_variable_count(cls, vc): + """ + Sort (variable, count) pairs into canonical order while + retaining order of variables that do not commute during + differentiation: + + * symbols and functions commute with each other + * derivatives commute with each other + * a derivative does not commute with anything it contains + * any other object is not allowed to commute if it has + free symbols in common with another object + + Examples + ======== + + >>> from sympy import Derivative, Function, symbols + >>> vsort = Derivative._sort_variable_count + >>> x, y, z = symbols('x y z') + >>> f, g, h = symbols('f g h', cls=Function) + + Contiguous items are collapsed into one pair: + + >>> vsort([(x, 1), (x, 1)]) + [(x, 2)] + >>> vsort([(y, 1), (f(x), 1), (y, 1), (f(x), 1)]) + [(y, 2), (f(x), 2)] + + Ordering is canonical. + + >>> def vsort0(*v): + ... # docstring helper to + ... # change vi -> (vi, 0), sort, and return vi vals + ... return [i[0] for i in vsort([(i, 0) for i in v])] + + >>> vsort0(y, x) + [x, y] + >>> vsort0(g(y), g(x), f(y)) + [f(y), g(x), g(y)] + + Symbols are sorted as far to the left as possible but never + move to the left of a derivative having the same symbol in + its variables; the same applies to AppliedUndef which are + always sorted after Symbols: + + >>> dfx = f(x).diff(x) + >>> assert vsort0(dfx, y) == [y, dfx] + >>> assert vsort0(dfx, x) == [dfx, x] + """ + if not vc: + return [] + vc = list(vc) + if len(vc) == 1: + return [Tuple(*vc[0])] + V = list(range(len(vc))) + E = [] + v = lambda i: vc[i][0] + D = Dummy() + def _block(d, v, wrt=False): + # return True if v should not come before d else False + if d == v: + return wrt + if d.is_Symbol: + return False + if isinstance(d, Derivative): + # a derivative blocks if any of it's variables contain + # v; the wrt flag will return True for an exact match + # and will cause an AppliedUndef to block if v is in + # the arguments + if any(_block(k, v, wrt=True) + for k in d._wrt_variables): + return True + return False + if not wrt and isinstance(d, AppliedUndef): + return False + if v.is_Symbol: + return v in d.free_symbols + if isinstance(v, AppliedUndef): + return _block(d.xreplace({v: D}), D) + return d.free_symbols & v.free_symbols + for i in range(len(vc)): + for j in range(i): + if _block(v(j), v(i)): + E.append((j,i)) + # this is the default ordering to use in case of ties + O = dict(zip(ordered(uniq([i for i, c in vc])), range(len(vc)))) + ix = topological_sort((V, E), key=lambda i: O[v(i)]) + # merge counts of contiguously identical items + merged = [] + for v, c in [vc[i] for i in ix]: + if merged and merged[-1][0] == v: + merged[-1][1] += c + else: + merged.append([v, c]) + return [Tuple(*i) for i in merged] + + def _eval_is_commutative(self): + return self.expr.is_commutative + + def _eval_derivative(self, v): + # If v (the variable of differentiation) is not in + # self.variables, we might be able to take the derivative. + if v not in self._wrt_variables: + dedv = self.expr.diff(v) + if isinstance(dedv, Derivative): + return dedv.func(dedv.expr, *(self.variable_count + dedv.variable_count)) + # dedv (d(self.expr)/dv) could have simplified things such that the + # derivative wrt things in self.variables can now be done. Thus, + # we set evaluate=True to see if there are any other derivatives + # that can be done. The most common case is when dedv is a simple + # number so that the derivative wrt anything else will vanish. + return self.func(dedv, *self.variables, evaluate=True) + # In this case v was in self.variables so the derivative wrt v has + # already been attempted and was not computed, either because it + # couldn't be or evaluate=False originally. + variable_count = list(self.variable_count) + variable_count.append((v, 1)) + return self.func(self.expr, *variable_count, evaluate=False) + + def doit(self, **hints): + expr = self.expr + if hints.get('deep', True): + expr = expr.doit(**hints) + hints['evaluate'] = True + rv = self.func(expr, *self.variable_count, **hints) + if rv!= self and rv.has(Derivative): + rv = rv.doit(**hints) + return rv + + @_sympifyit('z0', NotImplementedError) + def doit_numerically(self, z0): + """ + Evaluate the derivative at z numerically. + + When we can represent derivatives at a point, this should be folded + into the normal evalf. For now, we need a special method. + """ + if len(self.free_symbols) != 1 or len(self.variables) != 1: + raise NotImplementedError('partials and higher order derivatives') + z = list(self.free_symbols)[0] + + def eval(x): + f0 = self.expr.subs(z, Expr._from_mpmath(x, prec=mpmath.mp.prec)) + f0 = f0.evalf(prec_to_dps(mpmath.mp.prec)) + return f0._to_mpmath(mpmath.mp.prec) + return Expr._from_mpmath(mpmath.diff(eval, + z0._to_mpmath(mpmath.mp.prec)), + mpmath.mp.prec) + + @property + def expr(self): + return self._args[0] + + @property + def _wrt_variables(self): + # return the variables of differentiation without + # respect to the type of count (int or symbolic) + return [i[0] for i in self.variable_count] + + @property + def variables(self): + # TODO: deprecate? YES, make this 'enumerated_variables' and + # name _wrt_variables as variables + # TODO: support for `d^n`? + rv = [] + for v, count in self.variable_count: + if not count.is_Integer: + raise TypeError(filldedent(''' + Cannot give expansion for symbolic count. If you just + want a list of all variables of differentiation, use + _wrt_variables.''')) + rv.extend([v]*count) + return tuple(rv) + + @property + def variable_count(self): + return self._args[1:] + + @property + def derivative_count(self): + return sum([count for _, count in self.variable_count], 0) + + @property + def free_symbols(self): + ret = self.expr.free_symbols + # Add symbolic counts to free_symbols + for _, count in self.variable_count: + ret.update(count.free_symbols) + return ret + + @property + def kind(self): + return self.args[0].kind + + def _eval_subs(self, old, new): + # The substitution (old, new) cannot be done inside + # Derivative(expr, vars) for a variety of reasons + # as handled below. + if old in self._wrt_variables: + # first handle the counts + expr = self.func(self.expr, *[(v, c.subs(old, new)) + for v, c in self.variable_count]) + if expr != self: + return expr._eval_subs(old, new) + # quick exit case + if not getattr(new, '_diff_wrt', False): + # case (0): new is not a valid variable of + # differentiation + if isinstance(old, Symbol): + # don't introduce a new symbol if the old will do + return Subs(self, old, new) + else: + xi = Dummy('xi') + return Subs(self.xreplace({old: xi}), xi, new) + + # If both are Derivatives with the same expr, check if old is + # equivalent to self or if old is a subderivative of self. + if old.is_Derivative and old.expr == self.expr: + if self.canonical == old.canonical: + return new + + # collections.Counter doesn't have __le__ + def _subset(a, b): + return all((a[i] <= b[i]) == True for i in a) + + old_vars = Counter(dict(reversed(old.variable_count))) + self_vars = Counter(dict(reversed(self.variable_count))) + if _subset(old_vars, self_vars): + return _derivative_dispatch(new, *(self_vars - old_vars).items()).canonical + + args = list(self.args) + newargs = [x._subs(old, new) for x in args] + if args[0] == old: + # complete replacement of self.expr + # we already checked that the new is valid so we know + # it won't be a problem should it appear in variables + return _derivative_dispatch(*newargs) + + if newargs[0] != args[0]: + # case (1) can't change expr by introducing something that is in + # the _wrt_variables if it was already in the expr + # e.g. + # for Derivative(f(x, g(y)), y), x cannot be replaced with + # anything that has y in it; for f(g(x), g(y)).diff(g(y)) + # g(x) cannot be replaced with anything that has g(y) + syms = {vi: Dummy() for vi in self._wrt_variables + if not vi.is_Symbol} + wrt = {syms.get(vi, vi) for vi in self._wrt_variables} + forbidden = args[0].xreplace(syms).free_symbols & wrt + nfree = new.xreplace(syms).free_symbols + ofree = old.xreplace(syms).free_symbols + if (nfree - ofree) & forbidden: + return Subs(self, old, new) + + viter = ((i, j) for ((i, _), (j, _)) in zip(newargs[1:], args[1:])) + if any(i != j for i, j in viter): # a wrt-variable change + # case (2) can't change vars by introducing a variable + # that is contained in expr, e.g. + # for Derivative(f(z, g(h(x), y)), y), y cannot be changed to + # x, h(x), or g(h(x), y) + for a in _atomic(self.expr, recursive=True): + for i in range(1, len(newargs)): + vi, _ = newargs[i] + if a == vi and vi != args[i][0]: + return Subs(self, old, new) + # more arg-wise checks + vc = newargs[1:] + oldv = self._wrt_variables + newe = self.expr + subs = [] + for i, (vi, ci) in enumerate(vc): + if not vi._diff_wrt: + # case (3) invalid differentiation expression so + # create a replacement dummy + xi = Dummy('xi_%i' % i) + # replace the old valid variable with the dummy + # in the expression + newe = newe.xreplace({oldv[i]: xi}) + # and replace the bad variable with the dummy + vc[i] = (xi, ci) + # and record the dummy with the new (invalid) + # differentiation expression + subs.append((xi, vi)) + + if subs: + # handle any residual substitution in the expression + newe = newe._subs(old, new) + # return the Subs-wrapped derivative + return Subs(Derivative(newe, *vc), *zip(*subs)) + + # everything was ok + return _derivative_dispatch(*newargs) + + def _eval_lseries(self, x, logx, cdir=0): + dx = self.variables + for term in self.expr.lseries(x, logx=logx, cdir=cdir): + yield self.func(term, *dx) + + def _eval_nseries(self, x, n, logx, cdir=0): + arg = self.expr.nseries(x, n=n, logx=logx) + o = arg.getO() + dx = self.variables + rv = [self.func(a, *dx) for a in Add.make_args(arg.removeO())] + if o: + rv.append(o/x) + return Add(*rv) + + def _eval_as_leading_term(self, x, logx=None, cdir=0): + series_gen = self.expr.lseries(x) + d = S.Zero + for leading_term in series_gen: + d = diff(leading_term, *self.variables) + if d != 0: + break + return d + + def as_finite_difference(self, points=1, x0=None, wrt=None): + """ Expresses a Derivative instance as a finite difference. + + Parameters + ========== + + points : sequence or coefficient, optional + If sequence: discrete values (length >= order+1) of the + independent variable used for generating the finite + difference weights. + If it is a coefficient, it will be used as the step-size + for generating an equidistant sequence of length order+1 + centered around ``x0``. Default: 1 (step-size 1) + + x0 : number or Symbol, optional + the value of the independent variable (``wrt``) at which the + derivative is to be approximated. Default: same as ``wrt``. + + wrt : Symbol, optional + "with respect to" the variable for which the (partial) + derivative is to be approximated for. If not provided it + is required that the derivative is ordinary. Default: ``None``. + + + Examples + ======== + + >>> from sympy import symbols, Function, exp, sqrt, Symbol + >>> x, h = symbols('x h') + >>> f = Function('f') + >>> f(x).diff(x).as_finite_difference() + -f(x - 1/2) + f(x + 1/2) + + The default step size and number of points are 1 and + ``order + 1`` respectively. We can change the step size by + passing a symbol as a parameter: + + >>> f(x).diff(x).as_finite_difference(h) + -f(-h/2 + x)/h + f(h/2 + x)/h + + We can also specify the discretized values to be used in a + sequence: + + >>> f(x).diff(x).as_finite_difference([x, x+h, x+2*h]) + -3*f(x)/(2*h) + 2*f(h + x)/h - f(2*h + x)/(2*h) + + The algorithm is not restricted to use equidistant spacing, nor + do we need to make the approximation around ``x0``, but we can get + an expression estimating the derivative at an offset: + + >>> e, sq2 = exp(1), sqrt(2) + >>> xl = [x-h, x+h, x+e*h] + >>> f(x).diff(x, 1).as_finite_difference(xl, x+h*sq2) # doctest: +ELLIPSIS + 2*h*((h + sqrt(2)*h)/(2*h) - (-sqrt(2)*h + h)/(2*h))*f(E*h + x)/... + + To approximate ``Derivative`` around ``x0`` using a non-equidistant + spacing step, the algorithm supports assignment of undefined + functions to ``points``: + + >>> dx = Function('dx') + >>> f(x).diff(x).as_finite_difference(points=dx(x), x0=x-h) + -f(-h + x - dx(-h + x)/2)/dx(-h + x) + f(-h + x + dx(-h + x)/2)/dx(-h + x) + + Partial derivatives are also supported: + + >>> y = Symbol('y') + >>> d2fdxdy=f(x,y).diff(x,y) + >>> d2fdxdy.as_finite_difference(wrt=x) + -Derivative(f(x - 1/2, y), y) + Derivative(f(x + 1/2, y), y) + + We can apply ``as_finite_difference`` to ``Derivative`` instances in + compound expressions using ``replace``: + + >>> (1 + 42**f(x).diff(x)).replace(lambda arg: arg.is_Derivative, + ... lambda arg: arg.as_finite_difference()) + 42**(-f(x - 1/2) + f(x + 1/2)) + 1 + + + See also + ======== + + sympy.calculus.finite_diff.apply_finite_diff + sympy.calculus.finite_diff.differentiate_finite + sympy.calculus.finite_diff.finite_diff_weights + + """ + from sympy.calculus.finite_diff import _as_finite_diff + return _as_finite_diff(self, points, x0, wrt) + + @classmethod + def _get_zero_with_shape_like(cls, expr): + return S.Zero + + @classmethod + def _dispatch_eval_derivative_n_times(cls, expr, v, count): + # Evaluate the derivative `n` times. If + # `_eval_derivative_n_times` is not overridden by the current + # object, the default in `Basic` will call a loop over + # `_eval_derivative`: + return expr._eval_derivative_n_times(v, count) + + +def _derivative_dispatch(expr, *variables, **kwargs): + from sympy.matrices.matrixbase import MatrixBase + from sympy.matrices.expressions.matexpr import MatrixExpr + from sympy.tensor.array import NDimArray + array_types = (MatrixBase, MatrixExpr, NDimArray, list, tuple, Tuple) + if isinstance(expr, array_types) or any(isinstance(i[0], array_types) if isinstance(i, (tuple, list, Tuple)) else isinstance(i, array_types) for i in variables): + from sympy.tensor.array.array_derivatives import ArrayDerivative + return ArrayDerivative(expr, *variables, **kwargs) + return Derivative(expr, *variables, **kwargs) + + +class Lambda(Expr): + """ + Lambda(x, expr) represents a lambda function similar to Python's + 'lambda x: expr'. A function of several variables is written as + Lambda((x, y, ...), expr). + + Examples + ======== + + A simple example: + + >>> from sympy import Lambda + >>> from sympy.abc import x + >>> f = Lambda(x, x**2) + >>> f(4) + 16 + + For multivariate functions, use: + + >>> from sympy.abc import y, z, t + >>> f2 = Lambda((x, y, z, t), x + y**z + t**z) + >>> f2(1, 2, 3, 4) + 73 + + It is also possible to unpack tuple arguments: + + >>> f = Lambda(((x, y), z), x + y + z) + >>> f((1, 2), 3) + 6 + + A handy shortcut for lots of arguments: + + >>> p = x, y, z + >>> f = Lambda(p, x + y*z) + >>> f(*p) + x + y*z + + """ + is_Function = True + + def __new__(cls, signature, expr): + if iterable(signature) and not isinstance(signature, (tuple, Tuple)): + sympy_deprecation_warning( + """ + Using a non-tuple iterable as the first argument to Lambda + is deprecated. Use Lambda(tuple(args), expr) instead. + """, + deprecated_since_version="1.5", + active_deprecations_target="deprecated-non-tuple-lambda", + ) + signature = tuple(signature) + sig = signature if iterable(signature) else (signature,) + sig = sympify(sig) + cls._check_signature(sig) + + if len(sig) == 1 and sig[0] == expr: + return S.IdentityFunction + + return Expr.__new__(cls, sig, sympify(expr)) + + @classmethod + def _check_signature(cls, sig): + syms = set() + + def rcheck(args): + for a in args: + if a.is_symbol: + if a in syms: + raise BadSignatureError("Duplicate symbol %s" % a) + syms.add(a) + elif isinstance(a, Tuple): + rcheck(a) + else: + raise BadSignatureError("Lambda signature should be only tuples" + " and symbols, not %s" % a) + + if not isinstance(sig, Tuple): + raise BadSignatureError("Lambda signature should be a tuple not %s" % sig) + # Recurse through the signature: + rcheck(sig) + + @property + def signature(self): + """The expected form of the arguments to be unpacked into variables""" + return self._args[0] + + @property + def expr(self): + """The return value of the function""" + return self._args[1] + + @property + def variables(self): + """The variables used in the internal representation of the function""" + def _variables(args): + if isinstance(args, Tuple): + for arg in args: + yield from _variables(arg) + else: + yield args + return tuple(_variables(self.signature)) + + @property + def nargs(self): + from sympy.sets.sets import FiniteSet + return FiniteSet(len(self.signature)) + + bound_symbols = variables + + @property + def free_symbols(self): + return self.expr.free_symbols - set(self.variables) + + def __call__(self, *args): + n = len(args) + if n not in self.nargs: # Lambda only ever has 1 value in nargs + # XXX: exception message must be in exactly this format to + # make it work with NumPy's functions like vectorize(). See, + # for example, https://github.com/numpy/numpy/issues/1697. + # The ideal solution would be just to attach metadata to + # the exception and change NumPy to take advantage of this. + ## XXX does this apply to Lambda? If not, remove this comment. + temp = ('%(name)s takes exactly %(args)s ' + 'argument%(plural)s (%(given)s given)') + raise BadArgumentsError(temp % { + 'name': self, + 'args': list(self.nargs)[0], + 'plural': 's'*(list(self.nargs)[0] != 1), + 'given': n}) + + d = self._match_signature(self.signature, args) + + return self.expr.xreplace(d) + + def _match_signature(self, sig, args): + + symargmap = {} + + def rmatch(pars, args): + for par, arg in zip(pars, args): + if par.is_symbol: + symargmap[par] = arg + elif isinstance(par, Tuple): + if not isinstance(arg, (tuple, Tuple)) or len(args) != len(pars): + raise BadArgumentsError("Can't match %s and %s" % (args, pars)) + rmatch(par, arg) + + rmatch(sig, args) + + return symargmap + + @property + def is_identity(self): + """Return ``True`` if this ``Lambda`` is an identity function. """ + return self.signature == self.expr + + def _eval_evalf(self, prec): + return self.func(self.args[0], self.args[1].evalf(n=prec_to_dps(prec))) + + +class Subs(Expr): + """ + Represents unevaluated substitutions of an expression. + + ``Subs(expr, x, x0)`` represents the expression resulting + from substituting x with x0 in expr. + + Parameters + ========== + + expr : Expr + An expression. + + x : tuple, variable + A variable or list of distinct variables. + + x0 : tuple or list of tuples + A point or list of evaluation points + corresponding to those variables. + + Examples + ======== + + >>> from sympy import Subs, Function, sin, cos + >>> from sympy.abc import x, y, z + >>> f = Function('f') + + Subs are created when a particular substitution cannot be made. The + x in the derivative cannot be replaced with 0 because 0 is not a + valid variables of differentiation: + + >>> f(x).diff(x).subs(x, 0) + Subs(Derivative(f(x), x), x, 0) + + Once f is known, the derivative and evaluation at 0 can be done: + + >>> _.subs(f, sin).doit() == sin(x).diff(x).subs(x, 0) == cos(0) + True + + Subs can also be created directly with one or more variables: + + >>> Subs(f(x)*sin(y) + z, (x, y), (0, 1)) + Subs(z + f(x)*sin(y), (x, y), (0, 1)) + >>> _.doit() + z + f(0)*sin(1) + + Notes + ===== + + ``Subs`` objects are generally useful to represent unevaluated derivatives + calculated at a point. + + The variables may be expressions, but they are subjected to the limitations + of subs(), so it is usually a good practice to use only symbols for + variables, since in that case there can be no ambiguity. + + There's no automatic expansion - use the method .doit() to effect all + possible substitutions of the object and also of objects inside the + expression. + + When evaluating derivatives at a point that is not a symbol, a Subs object + is returned. One is also able to calculate derivatives of Subs objects - in + this case the expression is always expanded (for the unevaluated form, use + Derivative()). + + In order to allow expressions to combine before doit is done, a + representation of the Subs expression is used internally to make + expressions that are superficially different compare the same: + + >>> a, b = Subs(x, x, 0), Subs(y, y, 0) + >>> a + b + 2*Subs(x, x, 0) + + This can lead to unexpected consequences when using methods + like `has` that are cached: + + >>> s = Subs(x, x, 0) + >>> s.has(x), s.has(y) + (True, False) + >>> ss = s.subs(x, y) + >>> ss.has(x), ss.has(y) + (True, False) + >>> s, ss + (Subs(x, x, 0), Subs(y, y, 0)) + """ + def __new__(cls, expr, variables, point, **assumptions): + if not is_sequence(variables, Tuple): + variables = [variables] + variables = Tuple(*variables) + + if has_dups(variables): + repeated = [str(v) for v, i in Counter(variables).items() if i > 1] + __ = ', '.join(repeated) + raise ValueError(filldedent(''' + The following expressions appear more than once: %s + ''' % __)) + + point = Tuple(*(point if is_sequence(point, Tuple) else [point])) + + if len(point) != len(variables): + raise ValueError('Number of point values must be the same as ' + 'the number of variables.') + + if not point: + return sympify(expr) + + # denest + if isinstance(expr, Subs): + variables = expr.variables + variables + point = expr.point + point + expr = expr.expr + else: + expr = sympify(expr) + + # use symbols with names equal to the point value (with prepended _) + # to give a variable-independent expression + pre = "_" + pts = sorted(set(point), key=default_sort_key) + from sympy.printing.str import StrPrinter + class CustomStrPrinter(StrPrinter): + def _print_Dummy(self, expr): + return str(expr) + str(expr.dummy_index) + def mystr(expr, **settings): + p = CustomStrPrinter(settings) + return p.doprint(expr) + while 1: + s_pts = {p: Symbol(pre + mystr(p)) for p in pts} + reps = [(v, s_pts[p]) + for v, p in zip(variables, point)] + # if any underscore-prepended symbol is already a free symbol + # and is a variable with a different point value, then there + # is a clash, e.g. _0 clashes in Subs(_0 + _1, (_0, _1), (1, 0)) + # because the new symbol that would be created is _1 but _1 + # is already mapped to 0 so __0 and __1 are used for the new + # symbols + if any(r in expr.free_symbols and + r in variables and + Symbol(pre + mystr(point[variables.index(r)])) != r + for _, r in reps): + pre += "_" + continue + break + + obj = Expr.__new__(cls, expr, Tuple(*variables), point) + obj._expr = expr.xreplace(dict(reps)) + return obj + + def _eval_is_commutative(self): + return self.expr.is_commutative + + def doit(self, **hints): + e, v, p = self.args + + # remove self mappings + for i, (vi, pi) in enumerate(zip(v, p)): + if vi == pi: + v = v[:i] + v[i + 1:] + p = p[:i] + p[i + 1:] + if not v: + return self.expr + + if isinstance(e, Derivative): + # apply functions first, e.g. f -> cos + undone = [] + for i, vi in enumerate(v): + if isinstance(vi, FunctionClass): + e = e.subs(vi, p[i]) + else: + undone.append((vi, p[i])) + if not isinstance(e, Derivative): + e = e.doit() + if isinstance(e, Derivative): + # do Subs that aren't related to differentiation + undone2 = [] + D = Dummy() + arg = e.args[0] + for vi, pi in undone: + if D not in e.xreplace({vi: D}).free_symbols: + if arg.has(vi): + e = e.subs(vi, pi) + else: + undone2.append((vi, pi)) + undone = undone2 + # differentiate wrt variables that are present + wrt = [] + D = Dummy() + expr = e.expr + free = expr.free_symbols + for vi, ci in e.variable_count: + if isinstance(vi, Symbol) and vi in free: + expr = expr.diff((vi, ci)) + elif D in expr.subs(vi, D).free_symbols: + expr = expr.diff((vi, ci)) + else: + wrt.append((vi, ci)) + # inject remaining subs + rv = expr.subs(undone) + # do remaining differentiation *in order given* + for vc in wrt: + rv = rv.diff(vc) + else: + # inject remaining subs + rv = e.subs(undone) + else: + rv = e.doit(**hints).subs(list(zip(v, p))) + + if hints.get('deep', True) and rv != self: + rv = rv.doit(**hints) + return rv + + def evalf(self, prec=None, **options): + return self.doit().evalf(prec, **options) + + n = evalf # type:ignore + + @property + def variables(self): + """The variables to be evaluated""" + return self._args[1] + + bound_symbols = variables + + @property + def expr(self): + """The expression on which the substitution operates""" + return self._args[0] + + @property + def point(self): + """The values for which the variables are to be substituted""" + return self._args[2] + + @property + def free_symbols(self): + return (self.expr.free_symbols - set(self.variables) | + set(self.point.free_symbols)) + + @property + def expr_free_symbols(self): + sympy_deprecation_warning(""" + The expr_free_symbols property is deprecated. Use free_symbols to get + the free symbols of an expression. + """, + deprecated_since_version="1.9", + active_deprecations_target="deprecated-expr-free-symbols") + # Don't show the warning twice from the recursive call + with ignore_warnings(SymPyDeprecationWarning): + return (self.expr.expr_free_symbols - set(self.variables) | + set(self.point.expr_free_symbols)) + + def __eq__(self, other): + if not isinstance(other, Subs): + return False + return self._hashable_content() == other._hashable_content() + + def __ne__(self, other): + return not(self == other) + + def __hash__(self): + return super().__hash__() + + def _hashable_content(self): + return (self._expr.xreplace(self.canonical_variables), + ) + tuple(ordered([(v, p) for v, p in + zip(self.variables, self.point) if not self.expr.has(v)])) + + def _eval_subs(self, old, new): + # Subs doit will do the variables in order; the semantics + # of subs for Subs is have the following invariant for + # Subs object foo: + # foo.doit().subs(reps) == foo.subs(reps).doit() + pt = list(self.point) + if old in self.variables: + if _atomic(new) == {new} and not any( + i.has(new) for i in self.args): + # the substitution is neutral + return self.xreplace({old: new}) + # any occurrence of old before this point will get + # handled by replacements from here on + i = self.variables.index(old) + for j in range(i, len(self.variables)): + pt[j] = pt[j]._subs(old, new) + return self.func(self.expr, self.variables, pt) + v = [i._subs(old, new) for i in self.variables] + if v != list(self.variables): + return self.func(self.expr, self.variables + (old,), pt + [new]) + expr = self.expr._subs(old, new) + pt = [i._subs(old, new) for i in self.point] + return self.func(expr, v, pt) + + def _eval_derivative(self, s): + # Apply the chain rule of the derivative on the substitution variables: + f = self.expr + vp = V, P = self.variables, self.point + val = Add.fromiter(p.diff(s)*Subs(f.diff(v), *vp).doit() + for v, p in zip(V, P)) + + # these are all the free symbols in the expr + efree = f.free_symbols + # some symbols like IndexedBase include themselves and args + # as free symbols + compound = {i for i in efree if len(i.free_symbols) > 1} + # hide them and see what independent free symbols remain + dums = {Dummy() for i in compound} + masked = f.xreplace(dict(zip(compound, dums))) + ifree = masked.free_symbols - dums + # include the compound symbols + free = ifree | compound + # remove the variables already handled + free -= set(V) + # add back any free symbols of remaining compound symbols + free |= {i for j in free & compound for i in j.free_symbols} + # if symbols of s are in free then there is more to do + if free & s.free_symbols: + val += Subs(f.diff(s), self.variables, self.point).doit() + return val + + def _eval_nseries(self, x, n, logx, cdir=0): + if x in self.point: + # x is the variable being substituted into + apos = self.point.index(x) + other = self.variables[apos] + else: + other = x + arg = self.expr.nseries(other, n=n, logx=logx) + o = arg.getO() + terms = Add.make_args(arg.removeO()) + rv = Add(*[self.func(a, *self.args[1:]) for a in terms]) + if o: + rv += o.subs(other, x) + return rv + + def _eval_as_leading_term(self, x, logx=None, cdir=0): + if x in self.point: + ipos = self.point.index(x) + xvar = self.variables[ipos] + return self.expr.as_leading_term(xvar) + if x in self.variables: + # if `x` is a dummy variable, it means it won't exist after the + # substitution has been performed: + return self + # The variable is independent of the substitution: + return self.expr.as_leading_term(x) + + +def diff(f, *symbols, **kwargs): + """ + Differentiate f with respect to symbols. + + Explanation + =========== + + This is just a wrapper to unify .diff() and the Derivative class; its + interface is similar to that of integrate(). You can use the same + shortcuts for multiple variables as with Derivative. For example, + diff(f(x), x, x, x) and diff(f(x), x, 3) both return the third derivative + of f(x). + + You can pass evaluate=False to get an unevaluated Derivative class. Note + that if there are 0 symbols (such as diff(f(x), x, 0), then the result will + be the function (the zeroth derivative), even if evaluate=False. + + Examples + ======== + + >>> from sympy import sin, cos, Function, diff + >>> from sympy.abc import x, y + >>> f = Function('f') + + >>> diff(sin(x), x) + cos(x) + >>> diff(f(x), x, x, x) + Derivative(f(x), (x, 3)) + >>> diff(f(x), x, 3) + Derivative(f(x), (x, 3)) + >>> diff(sin(x)*cos(y), x, 2, y, 2) + sin(x)*cos(y) + + >>> type(diff(sin(x), x)) + cos + >>> type(diff(sin(x), x, evaluate=False)) + + >>> type(diff(sin(x), x, 0)) + sin + >>> type(diff(sin(x), x, 0, evaluate=False)) + sin + + >>> diff(sin(x)) + cos(x) + >>> diff(sin(x*y)) + Traceback (most recent call last): + ... + ValueError: specify differentiation variables to differentiate sin(x*y) + + Note that ``diff(sin(x))`` syntax is meant only for convenience + in interactive sessions and should be avoided in library code. + + References + ========== + + .. [1] https://reference.wolfram.com/legacy/v5_2/Built-inFunctions/AlgebraicComputation/Calculus/D.html + + See Also + ======== + + Derivative + idiff: computes the derivative implicitly + + """ + if hasattr(f, 'diff'): + return f.diff(*symbols, **kwargs) + kwargs.setdefault('evaluate', True) + return _derivative_dispatch(f, *symbols, **kwargs) + + +def expand(e, deep=True, modulus=None, power_base=True, power_exp=True, + mul=True, log=True, multinomial=True, basic=True, **hints): + r""" + Expand an expression using methods given as hints. + + Explanation + =========== + + Hints evaluated unless explicitly set to False are: ``basic``, ``log``, + ``multinomial``, ``mul``, ``power_base``, and ``power_exp`` The following + hints are supported but not applied unless set to True: ``complex``, + ``func``, and ``trig``. In addition, the following meta-hints are + supported by some or all of the other hints: ``frac``, ``numer``, + ``denom``, ``modulus``, and ``force``. ``deep`` is supported by all + hints. Additionally, subclasses of Expr may define their own hints or + meta-hints. + + The ``basic`` hint is used for any special rewriting of an object that + should be done automatically (along with the other hints like ``mul``) + when expand is called. This is a catch-all hint to handle any sort of + expansion that may not be described by the existing hint names. To use + this hint an object should override the ``_eval_expand_basic`` method. + Objects may also define their own expand methods, which are not run by + default. See the API section below. + + If ``deep`` is set to ``True`` (the default), things like arguments of + functions are recursively expanded. Use ``deep=False`` to only expand on + the top level. + + If the ``force`` hint is used, assumptions about variables will be ignored + in making the expansion. + + Hints + ===== + + These hints are run by default + + mul + --- + + Distributes multiplication over addition: + + >>> from sympy import cos, exp, sin + >>> from sympy.abc import x, y, z + >>> (y*(x + z)).expand(mul=True) + x*y + y*z + + multinomial + ----------- + + Expand (x + y + ...)**n where n is a positive integer. + + >>> ((x + y + z)**2).expand(multinomial=True) + x**2 + 2*x*y + 2*x*z + y**2 + 2*y*z + z**2 + + power_exp + --------- + + Expand addition in exponents into multiplied bases. + + >>> exp(x + y).expand(power_exp=True) + exp(x)*exp(y) + >>> (2**(x + y)).expand(power_exp=True) + 2**x*2**y + + power_base + ---------- + + Split powers of multiplied bases. + + This only happens by default if assumptions allow, or if the + ``force`` meta-hint is used: + + >>> ((x*y)**z).expand(power_base=True) + (x*y)**z + >>> ((x*y)**z).expand(power_base=True, force=True) + x**z*y**z + >>> ((2*y)**z).expand(power_base=True) + 2**z*y**z + + Note that in some cases where this expansion always holds, SymPy performs + it automatically: + + >>> (x*y)**2 + x**2*y**2 + + log + --- + + Pull out power of an argument as a coefficient and split logs products + into sums of logs. + + Note that these only work if the arguments of the log function have the + proper assumptions--the arguments must be positive and the exponents must + be real--or else the ``force`` hint must be True: + + >>> from sympy import log, symbols + >>> log(x**2*y).expand(log=True) + log(x**2*y) + >>> log(x**2*y).expand(log=True, force=True) + 2*log(x) + log(y) + >>> x, y = symbols('x,y', positive=True) + >>> log(x**2*y).expand(log=True) + 2*log(x) + log(y) + + basic + ----- + + This hint is intended primarily as a way for custom subclasses to enable + expansion by default. + + These hints are not run by default: + + complex + ------- + + Split an expression into real and imaginary parts. + + >>> x, y = symbols('x,y') + >>> (x + y).expand(complex=True) + re(x) + re(y) + I*im(x) + I*im(y) + >>> cos(x).expand(complex=True) + -I*sin(re(x))*sinh(im(x)) + cos(re(x))*cosh(im(x)) + + Note that this is just a wrapper around ``as_real_imag()``. Most objects + that wish to redefine ``_eval_expand_complex()`` should consider + redefining ``as_real_imag()`` instead. + + func + ---- + + Expand other functions. + + >>> from sympy import gamma + >>> gamma(x + 1).expand(func=True) + x*gamma(x) + + trig + ---- + + Do trigonometric expansions. + + >>> cos(x + y).expand(trig=True) + -sin(x)*sin(y) + cos(x)*cos(y) + >>> sin(2*x).expand(trig=True) + 2*sin(x)*cos(x) + + Note that the forms of ``sin(n*x)`` and ``cos(n*x)`` in terms of ``sin(x)`` + and ``cos(x)`` are not unique, due to the identity `\sin^2(x) + \cos^2(x) + = 1`. The current implementation uses the form obtained from Chebyshev + polynomials, but this may change. See `this MathWorld article + `_ for more + information. + + Notes + ===== + + - You can shut off unwanted methods:: + + >>> (exp(x + y)*(x + y)).expand() + x*exp(x)*exp(y) + y*exp(x)*exp(y) + >>> (exp(x + y)*(x + y)).expand(power_exp=False) + x*exp(x + y) + y*exp(x + y) + >>> (exp(x + y)*(x + y)).expand(mul=False) + (x + y)*exp(x)*exp(y) + + - Use deep=False to only expand on the top level:: + + >>> exp(x + exp(x + y)).expand() + exp(x)*exp(exp(x)*exp(y)) + >>> exp(x + exp(x + y)).expand(deep=False) + exp(x)*exp(exp(x + y)) + + - Hints are applied in an arbitrary, but consistent order (in the current + implementation, they are applied in alphabetical order, except + multinomial comes before mul, but this may change). Because of this, + some hints may prevent expansion by other hints if they are applied + first. For example, ``mul`` may distribute multiplications and prevent + ``log`` and ``power_base`` from expanding them. Also, if ``mul`` is + applied before ``multinomial`, the expression might not be fully + distributed. The solution is to use the various ``expand_hint`` helper + functions or to use ``hint=False`` to this function to finely control + which hints are applied. Here are some examples:: + + >>> from sympy import expand, expand_mul, expand_power_base + >>> x, y, z = symbols('x,y,z', positive=True) + + >>> expand(log(x*(y + z))) + log(x) + log(y + z) + + Here, we see that ``log`` was applied before ``mul``. To get the mul + expanded form, either of the following will work:: + + >>> expand_mul(log(x*(y + z))) + log(x*y + x*z) + >>> expand(log(x*(y + z)), log=False) + log(x*y + x*z) + + A similar thing can happen with the ``power_base`` hint:: + + >>> expand((x*(y + z))**x) + (x*y + x*z)**x + + To get the ``power_base`` expanded form, either of the following will + work:: + + >>> expand((x*(y + z))**x, mul=False) + x**x*(y + z)**x + >>> expand_power_base((x*(y + z))**x) + x**x*(y + z)**x + + >>> expand((x + y)*y/x) + y + y**2/x + + The parts of a rational expression can be targeted:: + + >>> expand((x + y)*y/x/(x + 1), frac=True) + (x*y + y**2)/(x**2 + x) + >>> expand((x + y)*y/x/(x + 1), numer=True) + (x*y + y**2)/(x*(x + 1)) + >>> expand((x + y)*y/x/(x + 1), denom=True) + y*(x + y)/(x**2 + x) + + - The ``modulus`` meta-hint can be used to reduce the coefficients of an + expression post-expansion:: + + >>> expand((3*x + 1)**2) + 9*x**2 + 6*x + 1 + >>> expand((3*x + 1)**2, modulus=5) + 4*x**2 + x + 1 + + - Either ``expand()`` the function or ``.expand()`` the method can be + used. Both are equivalent:: + + >>> expand((x + 1)**2) + x**2 + 2*x + 1 + >>> ((x + 1)**2).expand() + x**2 + 2*x + 1 + + API + === + + Objects can define their own expand hints by defining + ``_eval_expand_hint()``. The function should take the form:: + + def _eval_expand_hint(self, **hints): + # Only apply the method to the top-level expression + ... + + See also the example below. Objects should define ``_eval_expand_hint()`` + methods only if ``hint`` applies to that specific object. The generic + ``_eval_expand_hint()`` method defined in Expr will handle the no-op case. + + Each hint should be responsible for expanding that hint only. + Furthermore, the expansion should be applied to the top-level expression + only. ``expand()`` takes care of the recursion that happens when + ``deep=True``. + + You should only call ``_eval_expand_hint()`` methods directly if you are + 100% sure that the object has the method, as otherwise you are liable to + get unexpected ``AttributeError``s. Note, again, that you do not need to + recursively apply the hint to args of your object: this is handled + automatically by ``expand()``. ``_eval_expand_hint()`` should + generally not be used at all outside of an ``_eval_expand_hint()`` method. + If you want to apply a specific expansion from within another method, use + the public ``expand()`` function, method, or ``expand_hint()`` functions. + + In order for expand to work, objects must be rebuildable by their args, + i.e., ``obj.func(*obj.args) == obj`` must hold. + + Expand methods are passed ``**hints`` so that expand hints may use + 'metahints'--hints that control how different expand methods are applied. + For example, the ``force=True`` hint described above that causes + ``expand(log=True)`` to ignore assumptions is such a metahint. The + ``deep`` meta-hint is handled exclusively by ``expand()`` and is not + passed to ``_eval_expand_hint()`` methods. + + Note that expansion hints should generally be methods that perform some + kind of 'expansion'. For hints that simply rewrite an expression, use the + .rewrite() API. + + Examples + ======== + + >>> from sympy import Expr, sympify + >>> class MyClass(Expr): + ... def __new__(cls, *args): + ... args = sympify(args) + ... return Expr.__new__(cls, *args) + ... + ... def _eval_expand_double(self, *, force=False, **hints): + ... ''' + ... Doubles the args of MyClass. + ... + ... If there more than four args, doubling is not performed, + ... unless force=True is also used (False by default). + ... ''' + ... if not force and len(self.args) > 4: + ... return self + ... return self.func(*(self.args + self.args)) + ... + >>> a = MyClass(1, 2, MyClass(3, 4)) + >>> a + MyClass(1, 2, MyClass(3, 4)) + >>> a.expand(double=True) + MyClass(1, 2, MyClass(3, 4, 3, 4), 1, 2, MyClass(3, 4, 3, 4)) + >>> a.expand(double=True, deep=False) + MyClass(1, 2, MyClass(3, 4), 1, 2, MyClass(3, 4)) + + >>> b = MyClass(1, 2, 3, 4, 5) + >>> b.expand(double=True) + MyClass(1, 2, 3, 4, 5) + >>> b.expand(double=True, force=True) + MyClass(1, 2, 3, 4, 5, 1, 2, 3, 4, 5) + + See Also + ======== + + expand_log, expand_mul, expand_multinomial, expand_complex, expand_trig, + expand_power_base, expand_power_exp, expand_func, sympy.simplify.hyperexpand.hyperexpand + + """ + # don't modify this; modify the Expr.expand method + hints['power_base'] = power_base + hints['power_exp'] = power_exp + hints['mul'] = mul + hints['log'] = log + hints['multinomial'] = multinomial + hints['basic'] = basic + return sympify(e).expand(deep=deep, modulus=modulus, **hints) + +# This is a special application of two hints + +def _mexpand(expr, recursive=False): + # expand multinomials and then expand products; this may not always + # be sufficient to give a fully expanded expression (see + # test_issue_8247_8354 in test_arit) + if expr is None: + return + was = None + while was != expr: + was, expr = expr, expand_mul(expand_multinomial(expr)) + if not recursive: + break + return expr + + +# These are simple wrappers around single hints. + + +def expand_mul(expr, deep=True): + """ + Wrapper around expand that only uses the mul hint. See the expand + docstring for more information. + + Examples + ======== + + >>> from sympy import symbols, expand_mul, exp, log + >>> x, y = symbols('x,y', positive=True) + >>> expand_mul(exp(x+y)*(x+y)*log(x*y**2)) + x*exp(x + y)*log(x*y**2) + y*exp(x + y)*log(x*y**2) + + """ + return sympify(expr).expand(deep=deep, mul=True, power_exp=False, + power_base=False, basic=False, multinomial=False, log=False) + + +def expand_multinomial(expr, deep=True): + """ + Wrapper around expand that only uses the multinomial hint. See the expand + docstring for more information. + + Examples + ======== + + >>> from sympy import symbols, expand_multinomial, exp + >>> x, y = symbols('x y', positive=True) + >>> expand_multinomial((x + exp(x + 1))**2) + x**2 + 2*x*exp(x + 1) + exp(2*x + 2) + + """ + return sympify(expr).expand(deep=deep, mul=False, power_exp=False, + power_base=False, basic=False, multinomial=True, log=False) + + +def expand_log(expr, deep=True, force=False, factor=False): + """ + Wrapper around expand that only uses the log hint. See the expand + docstring for more information. + + Examples + ======== + + >>> from sympy import symbols, expand_log, exp, log + >>> x, y = symbols('x,y', positive=True) + >>> expand_log(exp(x+y)*(x+y)*log(x*y**2)) + (x + y)*(log(x) + 2*log(y))*exp(x + y) + + """ + from sympy.functions.elementary.exponential import log + from sympy.simplify.radsimp import fraction + if factor is False: + def _handleMul(x): + # look for the simple case of expanded log(b**a)/log(b) -> a in args + n, d = fraction(x) + n = [i for i in n.atoms(log) if i.args[0].is_Integer] + d = [i for i in d.atoms(log) if i.args[0].is_Integer] + if len(n) == 1 and len(d) == 1: + n = n[0] + d = d[0] + from sympy import multiplicity + m = multiplicity(d.args[0], n.args[0]) + if m: + r = m + log(n.args[0]//d.args[0]**m)/d + x = x.subs(n, d*r) + x1 = expand_mul(expand_log(x, deep=deep, force=force, factor=True)) + if x1.count(log) <= x.count(log): + return x1 + return x + + expr = expr.replace( + lambda x: x.is_Mul and all(any(isinstance(i, log) and i.args[0].is_Rational + for i in Mul.make_args(j)) for j in x.as_numer_denom()), + _handleMul) + + return sympify(expr).expand(deep=deep, log=True, mul=False, + power_exp=False, power_base=False, multinomial=False, + basic=False, force=force, factor=factor) + + +def expand_func(expr, deep=True): + """ + Wrapper around expand that only uses the func hint. See the expand + docstring for more information. + + Examples + ======== + + >>> from sympy import expand_func, gamma + >>> from sympy.abc import x + >>> expand_func(gamma(x + 2)) + x*(x + 1)*gamma(x) + + """ + return sympify(expr).expand(deep=deep, func=True, basic=False, + log=False, mul=False, power_exp=False, power_base=False, multinomial=False) + + +def expand_trig(expr, deep=True): + """ + Wrapper around expand that only uses the trig hint. See the expand + docstring for more information. + + Examples + ======== + + >>> from sympy import expand_trig, sin + >>> from sympy.abc import x, y + >>> expand_trig(sin(x+y)*(x+y)) + (x + y)*(sin(x)*cos(y) + sin(y)*cos(x)) + + """ + return sympify(expr).expand(deep=deep, trig=True, basic=False, + log=False, mul=False, power_exp=False, power_base=False, multinomial=False) + + +def expand_complex(expr, deep=True): + """ + Wrapper around expand that only uses the complex hint. See the expand + docstring for more information. + + Examples + ======== + + >>> from sympy import expand_complex, exp, sqrt, I + >>> from sympy.abc import z + >>> expand_complex(exp(z)) + I*exp(re(z))*sin(im(z)) + exp(re(z))*cos(im(z)) + >>> expand_complex(sqrt(I)) + sqrt(2)/2 + sqrt(2)*I/2 + + See Also + ======== + + sympy.core.expr.Expr.as_real_imag + """ + return sympify(expr).expand(deep=deep, complex=True, basic=False, + log=False, mul=False, power_exp=False, power_base=False, multinomial=False) + + +def expand_power_base(expr, deep=True, force=False): + """ + Wrapper around expand that only uses the power_base hint. + + A wrapper to expand(power_base=True) which separates a power with a base + that is a Mul into a product of powers, without performing any other + expansions, provided that assumptions about the power's base and exponent + allow. + + deep=False (default is True) will only apply to the top-level expression. + + force=True (default is False) will cause the expansion to ignore + assumptions about the base and exponent. When False, the expansion will + only happen if the base is non-negative or the exponent is an integer. + + >>> from sympy.abc import x, y, z + >>> from sympy import expand_power_base, sin, cos, exp, Symbol + + >>> (x*y)**2 + x**2*y**2 + + >>> (2*x)**y + (2*x)**y + >>> expand_power_base(_) + 2**y*x**y + + >>> expand_power_base((x*y)**z) + (x*y)**z + >>> expand_power_base((x*y)**z, force=True) + x**z*y**z + >>> expand_power_base(sin((x*y)**z), deep=False) + sin((x*y)**z) + >>> expand_power_base(sin((x*y)**z), force=True) + sin(x**z*y**z) + + >>> expand_power_base((2*sin(x))**y + (2*cos(x))**y) + 2**y*sin(x)**y + 2**y*cos(x)**y + + >>> expand_power_base((2*exp(y))**x) + 2**x*exp(y)**x + + >>> expand_power_base((2*cos(x))**y) + 2**y*cos(x)**y + + Notice that sums are left untouched. If this is not the desired behavior, + apply full ``expand()`` to the expression: + + >>> expand_power_base(((x+y)*z)**2) + z**2*(x + y)**2 + >>> (((x+y)*z)**2).expand() + x**2*z**2 + 2*x*y*z**2 + y**2*z**2 + + >>> expand_power_base((2*y)**(1+z)) + 2**(z + 1)*y**(z + 1) + >>> ((2*y)**(1+z)).expand() + 2*2**z*y**(z + 1) + + The power that is unexpanded can be expanded safely when + ``y != 0``, otherwise different values might be obtained for the expression: + + >>> prev = _ + + If we indicate that ``y`` is positive but then replace it with + a value of 0 after expansion, the expression becomes 0: + + >>> p = Symbol('p', positive=True) + >>> prev.subs(y, p).expand().subs(p, 0) + 0 + + But if ``z = -1`` the expression would not be zero: + + >>> prev.subs(y, 0).subs(z, -1) + 1 + + See Also + ======== + + expand + + """ + return sympify(expr).expand(deep=deep, log=False, mul=False, + power_exp=False, power_base=True, multinomial=False, + basic=False, force=force) + + +def expand_power_exp(expr, deep=True): + """ + Wrapper around expand that only uses the power_exp hint. + + See the expand docstring for more information. + + Examples + ======== + + >>> from sympy import expand_power_exp, Symbol + >>> from sympy.abc import x, y + >>> expand_power_exp(3**(y + 2)) + 9*3**y + >>> expand_power_exp(x**(y + 2)) + x**(y + 2) + + If ``x = 0`` the value of the expression depends on the + value of ``y``; if the expression were expanded the result + would be 0. So expansion is only done if ``x != 0``: + + >>> expand_power_exp(Symbol('x', zero=False)**(y + 2)) + x**2*x**y + """ + return sympify(expr).expand(deep=deep, complex=False, basic=False, + log=False, mul=False, power_exp=True, power_base=False, multinomial=False) + + +def count_ops(expr, visual=False): + """ + Return a representation (integer or expression) of the operations in expr. + + Parameters + ========== + + expr : Expr + If expr is an iterable, the sum of the op counts of the + items will be returned. + + visual : bool, optional + If ``False`` (default) then the sum of the coefficients of the + visual expression will be returned. + If ``True`` then the number of each type of operation is shown + with the core class types (or their virtual equivalent) multiplied by the + number of times they occur. + + Examples + ======== + + >>> from sympy.abc import a, b, x, y + >>> from sympy import sin, count_ops + + Although there is not a SUB object, minus signs are interpreted as + either negations or subtractions: + + >>> (x - y).count_ops(visual=True) + SUB + >>> (-x).count_ops(visual=True) + NEG + + Here, there are two Adds and a Pow: + + >>> (1 + a + b**2).count_ops(visual=True) + 2*ADD + POW + + In the following, an Add, Mul, Pow and two functions: + + >>> (sin(x)*x + sin(x)**2).count_ops(visual=True) + ADD + MUL + POW + 2*SIN + + for a total of 5: + + >>> (sin(x)*x + sin(x)**2).count_ops(visual=False) + 5 + + Note that "what you type" is not always what you get. The expression + 1/x/y is translated by sympy into 1/(x*y) so it gives a DIV and MUL rather + than two DIVs: + + >>> (1/x/y).count_ops(visual=True) + DIV + MUL + + The visual option can be used to demonstrate the difference in + operations for expressions in different forms. Here, the Horner + representation is compared with the expanded form of a polynomial: + + >>> eq=x*(1 + x*(2 + x*(3 + x))) + >>> count_ops(eq.expand(), visual=True) - count_ops(eq, visual=True) + -MUL + 3*POW + + The count_ops function also handles iterables: + + >>> count_ops([x, sin(x), None, True, x + 2], visual=False) + 2 + >>> count_ops([x, sin(x), None, True, x + 2], visual=True) + ADD + SIN + >>> count_ops({x: sin(x), x + 2: y + 1}, visual=True) + 2*ADD + SIN + + """ + from .relational import Relational + from sympy.concrete.summations import Sum + from sympy.integrals.integrals import Integral + from sympy.logic.boolalg import BooleanFunction + from sympy.simplify.radsimp import fraction + + expr = sympify(expr) + if isinstance(expr, Expr) and not expr.is_Relational: + + ops = [] + args = [expr] + NEG = Symbol('NEG') + DIV = Symbol('DIV') + SUB = Symbol('SUB') + ADD = Symbol('ADD') + EXP = Symbol('EXP') + while args: + a = args.pop() + + # if the following fails because the object is + # not Basic type, then the object should be fixed + # since it is the intention that all args of Basic + # should themselves be Basic + if a.is_Rational: + #-1/3 = NEG + DIV + if a is not S.One: + if a.p < 0: + ops.append(NEG) + if a.q != 1: + ops.append(DIV) + continue + elif a.is_Mul or a.is_MatMul: + if _coeff_isneg(a): + ops.append(NEG) + if a.args[0] is S.NegativeOne: + a = a.as_two_terms()[1] + else: + a = -a + n, d = fraction(a) + if n.is_Integer: + ops.append(DIV) + if n < 0: + ops.append(NEG) + args.append(d) + continue # won't be -Mul but could be Add + elif d is not S.One: + if not d.is_Integer: + args.append(d) + ops.append(DIV) + args.append(n) + continue # could be -Mul + elif a.is_Add or a.is_MatAdd: + aargs = list(a.args) + negs = 0 + for i, ai in enumerate(aargs): + if _coeff_isneg(ai): + negs += 1 + args.append(-ai) + if i > 0: + ops.append(SUB) + else: + args.append(ai) + if i > 0: + ops.append(ADD) + if negs == len(aargs): # -x - y = NEG + SUB + ops.append(NEG) + elif _coeff_isneg(aargs[0]): # -x + y = SUB, but already recorded ADD + ops.append(SUB - ADD) + continue + if a.is_Pow and a.exp is S.NegativeOne: + ops.append(DIV) + args.append(a.base) # won't be -Mul but could be Add + continue + if a == S.Exp1: + ops.append(EXP) + continue + if a.is_Pow and a.base == S.Exp1: + ops.append(EXP) + args.append(a.exp) + continue + if a.is_Mul or isinstance(a, LatticeOp): + o = Symbol(a.func.__name__.upper()) + # count the args + ops.append(o*(len(a.args) - 1)) + elif a.args and ( + a.is_Pow or a.is_Function or isinstance(a, (Derivative, Integral, Sum))): + # if it's not in the list above we don't + # consider a.func something to count, e.g. + # Tuple, MatrixSymbol, etc... + if isinstance(a.func, UndefinedFunction): + o = Symbol("FUNC_" + a.func.__name__.upper()) + else: + o = Symbol(a.func.__name__.upper()) + ops.append(o) + + if not a.is_Symbol: + args.extend(a.args) + + elif isinstance(expr, Dict): + ops = [count_ops(k, visual=visual) + + count_ops(v, visual=visual) for k, v in expr.items()] + elif iterable(expr): + ops = [count_ops(i, visual=visual) for i in expr] + elif isinstance(expr, (Relational, BooleanFunction)): + ops = [] + for arg in expr.args: + ops.append(count_ops(arg, visual=True)) + o = Symbol(func_name(expr, short=True).upper()) + ops.append(o) + elif not isinstance(expr, Basic): + ops = [] + else: # it's Basic not isinstance(expr, Expr): + if not isinstance(expr, Basic): + raise TypeError("Invalid type of expr") + else: + ops = [] + args = [expr] + while args: + a = args.pop() + + if a.args: + o = Symbol(type(a).__name__.upper()) + if a.is_Boolean: + ops.append(o*(len(a.args)-1)) + else: + ops.append(o) + args.extend(a.args) + + if not ops: + if visual: + return S.Zero + return 0 + + ops = Add(*ops) + + if visual: + return ops + + if ops.is_Number: + return int(ops) + + return sum(int((a.args or [1])[0]) for a in Add.make_args(ops)) + + +def nfloat(expr, n=15, exponent=False, dkeys=False): + """Make all Rationals in expr Floats except those in exponents + (unless the exponents flag is set to True) and those in undefined + functions. When processing dictionaries, do not modify the keys + unless ``dkeys=True``. + + Examples + ======== + + >>> from sympy import nfloat, cos, pi, sqrt + >>> from sympy.abc import x, y + >>> nfloat(x**4 + x/2 + cos(pi/3) + 1 + sqrt(y)) + x**4 + 0.5*x + sqrt(y) + 1.5 + >>> nfloat(x**4 + sqrt(y), exponent=True) + x**4.0 + y**0.5 + + Container types are not modified: + + >>> type(nfloat((1, 2))) is tuple + True + """ + from sympy.matrices.matrixbase import MatrixBase + + kw = {"n": n, "exponent": exponent, "dkeys": dkeys} + + if isinstance(expr, MatrixBase): + return expr.applyfunc(lambda e: nfloat(e, **kw)) + + # handling of iterable containers + if iterable(expr, exclude=str): + if isinstance(expr, (dict, Dict)): + if dkeys: + args = [tuple((nfloat(i, **kw) for i in a)) + for a in expr.items()] + else: + args = [(k, nfloat(v, **kw)) for k, v in expr.items()] + if isinstance(expr, dict): + return type(expr)(args) + else: + return expr.func(*args) + elif isinstance(expr, Basic): + return expr.func(*[nfloat(a, **kw) for a in expr.args]) + return type(expr)([nfloat(a, **kw) for a in expr]) + + rv = sympify(expr) + + if rv.is_Number: + return Float(rv, n) + elif rv.is_number: + # evalf doesn't always set the precision + rv = rv.n(n) + if rv.is_Number: + rv = Float(rv.n(n), n) + else: + pass # pure_complex(rv) is likely True + return rv + elif rv.is_Atom: + return rv + elif rv.is_Relational: + args_nfloat = (nfloat(arg, **kw) for arg in rv.args) + return rv.func(*args_nfloat) + + + # watch out for RootOf instances that don't like to have + # their exponents replaced with Dummies and also sometimes have + # problems with evaluating at low precision (issue 6393) + from sympy.polys.rootoftools import RootOf + rv = rv.xreplace({ro: ro.n(n) for ro in rv.atoms(RootOf)}) + + from .power import Pow + if not exponent: + reps = [(p, Pow(p.base, Dummy())) for p in rv.atoms(Pow)] + rv = rv.xreplace(dict(reps)) + rv = rv.n(n) + if not exponent: + rv = rv.xreplace({d.exp: p.exp for p, d in reps}) + else: + # Pow._eval_evalf special cases Integer exponents so if + # exponent is suppose to be handled we have to do so here + rv = rv.xreplace(Transform( + lambda x: Pow(x.base, Float(x.exp, n)), + lambda x: x.is_Pow and x.exp.is_Integer)) + + return rv.xreplace(Transform( + lambda x: x.func(*nfloat(x.args, n, exponent)), + lambda x: isinstance(x, Function) and not isinstance(x, AppliedUndef))) + + +from .symbol import Dummy, Symbol diff --git a/lib/python3.12/site-packages/sympy/core/intfunc.py b/lib/python3.12/site-packages/sympy/core/intfunc.py new file mode 100644 index 0000000000000000000000000000000000000000..112f737ebcb584d41ace39b6a2015b0f05b89aeb --- /dev/null +++ b/lib/python3.12/site-packages/sympy/core/intfunc.py @@ -0,0 +1,532 @@ +""" +The routines here were removed from numbers.py, power.py, +digits.py and factor_.py so they could be imported into core +without raising circular import errors. + +Although the name 'intfunc' was chosen to represent functions that +work with integers, it can also be thought of as containing +internal/core functions that are needed by the classes of the core. +""" + +import math +import sys +from functools import lru_cache + +from .sympify import sympify +from .singleton import S +from sympy.external.gmpy import (gcd as number_gcd, lcm as number_lcm, sqrt, + iroot, bit_scan1, gcdext) +from sympy.utilities.misc import as_int, filldedent + + +def num_digits(n, base=10): + """Return the number of digits needed to express n in give base. + + Examples + ======== + + >>> from sympy.core.intfunc import num_digits + >>> num_digits(10) + 2 + >>> num_digits(10, 2) # 1010 -> 4 digits + 4 + >>> num_digits(-100, 16) # -64 -> 2 digits + 2 + + + Parameters + ========== + + n: integer + The number whose digits are counted. + + b: integer + The base in which digits are computed. + + See Also + ======== + sympy.ntheory.digits.digits, sympy.ntheory.digits.count_digits + """ + if base < 0: + raise ValueError('base must be int greater than 1') + if not n: + return 1 + e, t = integer_log(abs(n), base) + return 1 + e + + +def integer_log(n, b): + r""" + Returns ``(e, bool)`` where e is the largest nonnegative integer + such that :math:`|n| \geq |b^e|` and ``bool`` is True if $n = b^e$. + + Examples + ======== + + >>> from sympy import integer_log + >>> integer_log(125, 5) + (3, True) + >>> integer_log(17, 9) + (1, False) + + If the base is positive and the number negative the + return value will always be the same except for 2: + + >>> integer_log(-4, 2) + (2, False) + >>> integer_log(-16, 4) + (0, False) + + When the base is negative, the returned value + will only be True if the parity of the exponent is + correct for the sign of the base: + + >>> integer_log(4, -2) + (2, True) + >>> integer_log(8, -2) + (3, False) + >>> integer_log(-8, -2) + (3, True) + >>> integer_log(-4, -2) + (2, False) + + See Also + ======== + integer_nthroot + sympy.ntheory.primetest.is_square + sympy.ntheory.factor_.multiplicity + sympy.ntheory.factor_.perfect_power + """ + n = as_int(n) + b = as_int(b) + + if b < 0: + e, t = integer_log(abs(n), -b) + # (-2)**3 == -8 + # (-2)**2 = 4 + t = t and e % 2 == (n < 0) + return e, t + if b <= 1: + raise ValueError('base must be 2 or more') + if n < 0: + if b != 2: + return 0, False + e, t = integer_log(-n, b) + return e, False + if n == 0: + raise ValueError('n cannot be 0') + + if n < b: + return 0, n == 1 + if b == 2: + e = n.bit_length() - 1 + return e, trailing(n) == e + t = trailing(b) + if 2**t == b: + e = int(n.bit_length() - 1)//t + n_ = 1 << (t*e) + return e, n_ == n + + d = math.floor(math.log10(n) / math.log10(b)) + n_ = b ** d + while n_ <= n: # this will iterate 0, 1 or 2 times + d += 1 + n_ *= b + return d - (n_ > n), (n_ == n or n_//b == n) + + +def trailing(n): + """Count the number of trailing zero digits in the binary + representation of n, i.e. determine the largest power of 2 + that divides n. + + Examples + ======== + + >>> from sympy import trailing + >>> trailing(128) + 7 + >>> trailing(63) + 0 + + See Also + ======== + sympy.ntheory.factor_.multiplicity + + """ + if not n: + return 0 + return bit_scan1(int(n)) + + +@lru_cache(1024) +def igcd(*args): + """Computes nonnegative integer greatest common divisor. + + Explanation + =========== + + The algorithm is based on the well known Euclid's algorithm [1]_. To + improve speed, ``igcd()`` has its own caching mechanism. + If you do not need the cache mechanism, using ``sympy.external.gmpy.gcd``. + + Examples + ======== + + >>> from sympy import igcd + >>> igcd(2, 4) + 2 + >>> igcd(5, 10, 15) + 5 + + References + ========== + + .. [1] https://en.wikipedia.org/wiki/Euclidean_algorithm + + """ + if len(args) < 2: + raise TypeError("igcd() takes at least 2 arguments (%s given)" % len(args)) + return int(number_gcd(*map(as_int, args))) + + +igcd2 = math.gcd + + +def igcd_lehmer(a, b): + r"""Computes greatest common divisor of two integers. + + Explanation + =========== + + Euclid's algorithm for the computation of the greatest + common divisor ``gcd(a, b)`` of two (positive) integers + $a$ and $b$ is based on the division identity + $$ a = q \times b + r$$, + where the quotient $q$ and the remainder $r$ are integers + and $0 \le r < b$. Then each common divisor of $a$ and $b$ + divides $r$, and it follows that ``gcd(a, b) == gcd(b, r)``. + The algorithm works by constructing the sequence + r0, r1, r2, ..., where r0 = a, r1 = b, and each rn + is the remainder from the division of the two preceding + elements. + + In Python, ``q = a // b`` and ``r = a % b`` are obtained by the + floor division and the remainder operations, respectively. + These are the most expensive arithmetic operations, especially + for large a and b. + + Lehmer's algorithm [1]_ is based on the observation that the quotients + ``qn = r(n-1) // rn`` are in general small integers even + when a and b are very large. Hence the quotients can be + usually determined from a relatively small number of most + significant bits. + + The efficiency of the algorithm is further enhanced by not + computing each long remainder in Euclid's sequence. The remainders + are linear combinations of a and b with integer coefficients + derived from the quotients. The coefficients can be computed + as far as the quotients can be determined from the chosen + most significant parts of a and b. Only then a new pair of + consecutive remainders is computed and the algorithm starts + anew with this pair. + + References + ========== + + .. [1] https://en.wikipedia.org/wiki/Lehmer%27s_GCD_algorithm + + """ + a, b = abs(as_int(a)), abs(as_int(b)) + if a < b: + a, b = b, a + + # The algorithm works by using one or two digit division + # whenever possible. The outer loop will replace the + # pair (a, b) with a pair of shorter consecutive elements + # of the Euclidean gcd sequence until a and b + # fit into two Python (long) int digits. + nbits = 2 * sys.int_info.bits_per_digit + + while a.bit_length() > nbits and b != 0: + # Quotients are mostly small integers that can + # be determined from most significant bits. + n = a.bit_length() - nbits + x, y = int(a >> n), int(b >> n) # most significant bits + + # Elements of the Euclidean gcd sequence are linear + # combinations of a and b with integer coefficients. + # Compute the coefficients of consecutive pairs + # a' = A*a + B*b, b' = C*a + D*b + # using small integer arithmetic as far as possible. + A, B, C, D = 1, 0, 0, 1 # initial values + + while True: + # The coefficients alternate in sign while looping. + # The inner loop combines two steps to keep track + # of the signs. + + # At this point we have + # A > 0, B <= 0, C <= 0, D > 0, + # x' = x + B <= x < x" = x + A, + # y' = y + C <= y < y" = y + D, + # and + # x'*N <= a' < x"*N, y'*N <= b' < y"*N, + # where N = 2**n. + + # Now, if y' > 0, and x"//y' and x'//y" agree, + # then their common value is equal to q = a'//b'. + # In addition, + # x'%y" = x' - q*y" < x" - q*y' = x"%y', + # and + # (x'%y")*N < a'%b' < (x"%y')*N. + + # On the other hand, we also have x//y == q, + # and therefore + # x'%y" = x + B - q*(y + D) = x%y + B', + # x"%y' = x + A - q*(y + C) = x%y + A', + # where + # B' = B - q*D < 0, A' = A - q*C > 0. + + if y + C <= 0: + break + q = (x + A) // (y + C) + + # Now x'//y" <= q, and equality holds if + # x' - q*y" = (x - q*y) + (B - q*D) >= 0. + # This is a minor optimization to avoid division. + x_qy, B_qD = x - q * y, B - q * D + if x_qy + B_qD < 0: + break + + # Next step in the Euclidean sequence. + x, y = y, x_qy + A, B, C, D = C, D, A - q * C, B_qD + + # At this point the signs of the coefficients + # change and their roles are interchanged. + # A <= 0, B > 0, C > 0, D < 0, + # x' = x + A <= x < x" = x + B, + # y' = y + D < y < y" = y + C. + + if y + D <= 0: + break + q = (x + B) // (y + D) + x_qy, A_qC = x - q * y, A - q * C + if x_qy + A_qC < 0: + break + + x, y = y, x_qy + A, B, C, D = C, D, A_qC, B - q * D + # Now the conditions on top of the loop + # are again satisfied. + # A > 0, B < 0, C < 0, D > 0. + + if B == 0: + # This can only happen when y == 0 in the beginning + # and the inner loop does nothing. + # Long division is forced. + a, b = b, a % b + continue + + # Compute new long arguments using the coefficients. + a, b = A * a + B * b, C * a + D * b + + # Small divisors. Finish with the standard algorithm. + while b: + a, b = b, a % b + + return a + + +def ilcm(*args): + """Computes integer least common multiple. + + Examples + ======== + + >>> from sympy import ilcm + >>> ilcm(5, 10) + 10 + >>> ilcm(7, 3) + 21 + >>> ilcm(5, 10, 15) + 30 + + """ + if len(args) < 2: + raise TypeError("ilcm() takes at least 2 arguments (%s given)" % len(args)) + return int(number_lcm(*map(as_int, args))) + + +def igcdex(a, b): + """Returns x, y, g such that g = x*a + y*b = gcd(a, b). + + Examples + ======== + + >>> from sympy.core.intfunc import igcdex + >>> igcdex(2, 3) + (-1, 1, 1) + >>> igcdex(10, 12) + (-1, 1, 2) + + >>> x, y, g = igcdex(100, 2004) + >>> x, y, g + (-20, 1, 4) + >>> x*100 + y*2004 + 4 + + """ + if (not a) and (not b): + return (0, 1, 0) + g, x, y = gcdext(int(a), int(b)) + return x, y, g + + +def mod_inverse(a, m): + r""" + Return the number $c$ such that, $a \times c = 1 \pmod{m}$ + where $c$ has the same sign as $m$. If no such value exists, + a ValueError is raised. + + Examples + ======== + + >>> from sympy import mod_inverse, S + + Suppose we wish to find multiplicative inverse $x$ of + 3 modulo 11. This is the same as finding $x$ such + that $3x = 1 \pmod{11}$. One value of x that satisfies + this congruence is 4. Because $3 \times 4 = 12$ and $12 = 1 \pmod{11}$. + This is the value returned by ``mod_inverse``: + + >>> mod_inverse(3, 11) + 4 + >>> mod_inverse(-3, 11) + 7 + + When there is a common factor between the numerators of + `a` and `m` the inverse does not exist: + + >>> mod_inverse(2, 4) + Traceback (most recent call last): + ... + ValueError: inverse of 2 mod 4 does not exist + + >>> mod_inverse(S(2)/7, S(5)/2) + 7/2 + + References + ========== + + .. [1] https://en.wikipedia.org/wiki/Modular_multiplicative_inverse + .. [2] https://en.wikipedia.org/wiki/Extended_Euclidean_algorithm + """ + c = None + try: + a, m = as_int(a), as_int(m) + if m != 1 and m != -1: + x, _, g = igcdex(a, m) + if g == 1: + c = x % m + except ValueError: + a, m = sympify(a), sympify(m) + if not (a.is_number and m.is_number): + raise TypeError( + filldedent( + """ + Expected numbers for arguments; symbolic `mod_inverse` + is not implemented + but symbolic expressions can be handled with the + similar function, + sympy.polys.polytools.invert""" + ) + ) + big = m > 1 + if big not in (S.true, S.false): + raise ValueError("m > 1 did not evaluate; try to simplify %s" % m) + elif big: + c = 1 / a + if c is None: + raise ValueError("inverse of %s (mod %s) does not exist" % (a, m)) + return c + + +def isqrt(n): + r""" Return the largest integer less than or equal to `\sqrt{n}`. + + Parameters + ========== + + n : non-negative integer + + Returns + ======= + + int : `\left\lfloor\sqrt{n}\right\rfloor` + + Raises + ====== + + ValueError + If n is negative. + TypeError + If n is of a type that cannot be compared to ``int``. + Therefore, a TypeError is raised for ``str``, but not for ``float``. + + Examples + ======== + + >>> from sympy.core.intfunc import isqrt + >>> isqrt(0) + 0 + >>> isqrt(9) + 3 + >>> isqrt(10) + 3 + >>> isqrt("30") + Traceback (most recent call last): + ... + TypeError: '<' not supported between instances of 'str' and 'int' + >>> from sympy.core.numbers import Rational + >>> isqrt(Rational(-1, 2)) + Traceback (most recent call last): + ... + ValueError: n must be nonnegative + + """ + if n < 0: + raise ValueError("n must be nonnegative") + return int(sqrt(int(n))) + + +def integer_nthroot(y, n): + """ + Return a tuple containing x = floor(y**(1/n)) + and a boolean indicating whether the result is exact (that is, + whether x**n == y). + + Examples + ======== + + >>> from sympy import integer_nthroot + >>> integer_nthroot(16, 2) + (4, True) + >>> integer_nthroot(26, 2) + (5, False) + + To simply determine if a number is a perfect square, the is_square + function should be used: + + >>> from sympy.ntheory.primetest import is_square + >>> is_square(26) + False + + See Also + ======== + sympy.ntheory.primetest.is_square + integer_log + """ + x, b = iroot(as_int(y), as_int(n)) + return int(x), b diff --git a/lib/python3.12/site-packages/sympy/core/kind.py b/lib/python3.12/site-packages/sympy/core/kind.py new file mode 100644 index 0000000000000000000000000000000000000000..83c5929eda14114659f2a5a72eb2d8b91a560f0e --- /dev/null +++ b/lib/python3.12/site-packages/sympy/core/kind.py @@ -0,0 +1,388 @@ +""" +Module to efficiently partition SymPy objects. + +This system is introduced because class of SymPy object does not always +represent the mathematical classification of the entity. For example, +``Integral(1, x)`` and ``Integral(Matrix([1,2]), x)`` are both instance +of ``Integral`` class. However the former is number and the latter is +matrix. + +One way to resolve this is defining subclass for each mathematical type, +such as ``MatAdd`` for the addition between matrices. Basic algebraic +operation such as addition or multiplication take this approach, but +defining every class for every mathematical object is not scalable. + +Therefore, we define the "kind" of the object and let the expression +infer the kind of itself from its arguments. Function and class can +filter the arguments by their kind, and behave differently according to +the type of itself. + +This module defines basic kinds for core objects. Other kinds such as +``ArrayKind`` or ``MatrixKind`` can be found in corresponding modules. + +.. notes:: + This approach is experimental, and can be replaced or deleted in the future. + See https://github.com/sympy/sympy/pull/20549. +""" + +from collections import defaultdict + +from .cache import cacheit +from sympy.multipledispatch.dispatcher import (Dispatcher, + ambiguity_warn, ambiguity_register_error_ignore_dup, + str_signature, RaiseNotImplementedError) + + +class KindMeta(type): + """ + Metaclass for ``Kind``. + + Assigns empty ``dict`` as class attribute ``_inst`` for every class, + in order to endow singleton-like behavior. + """ + def __new__(cls, clsname, bases, dct): + dct['_inst'] = {} + return super().__new__(cls, clsname, bases, dct) + + +class Kind(object, metaclass=KindMeta): + """ + Base class for kinds. + + Kind of the object represents the mathematical classification that + the entity falls into. It is expected that functions and classes + recognize and filter the argument by its kind. + + Kind of every object must be carefully selected so that it shows the + intention of design. Expressions may have different kind according + to the kind of its arguments. For example, arguments of ``Add`` + must have common kind since addition is group operator, and the + resulting ``Add()`` has the same kind. + + For the performance, each kind is as broad as possible and is not + based on set theory. For example, ``NumberKind`` includes not only + complex number but expression containing ``S.Infinity`` or ``S.NaN`` + which are not strictly number. + + Kind may have arguments as parameter. For example, ``MatrixKind()`` + may be constructed with one element which represents the kind of its + elements. + + ``Kind`` behaves in singleton-like fashion. Same signature will + return the same object. + + """ + def __new__(cls, *args): + if args in cls._inst: + inst = cls._inst[args] + else: + inst = super().__new__(cls) + cls._inst[args] = inst + return inst + + +class _UndefinedKind(Kind): + """ + Default kind for all SymPy object. If the kind is not defined for + the object, or if the object cannot infer the kind from its + arguments, this will be returned. + + Examples + ======== + + >>> from sympy import Expr + >>> Expr().kind + UndefinedKind + """ + def __new__(cls): + return super().__new__(cls) + + def __repr__(self): + return "UndefinedKind" + +UndefinedKind = _UndefinedKind() + + +class _NumberKind(Kind): + """ + Kind for all numeric object. + + This kind represents every number, including complex numbers, + infinity and ``S.NaN``. Other objects such as quaternions do not + have this kind. + + Most ``Expr`` are initially designed to represent the number, so + this will be the most common kind in SymPy core. For example + ``Symbol()``, which represents a scalar, has this kind as long as it + is commutative. + + Numbers form a field. Any operation between number-kind objects will + result this kind as well. + + Examples + ======== + + >>> from sympy import S, oo, Symbol + >>> S.One.kind + NumberKind + >>> (-oo).kind + NumberKind + >>> S.NaN.kind + NumberKind + + Commutative symbol are treated as number. + + >>> x = Symbol('x') + >>> x.kind + NumberKind + >>> Symbol('y', commutative=False).kind + UndefinedKind + + Operation between numbers results number. + + >>> (x+1).kind + NumberKind + + See Also + ======== + + sympy.core.expr.Expr.is_Number : check if the object is strictly + subclass of ``Number`` class. + + sympy.core.expr.Expr.is_number : check if the object is number + without any free symbol. + + """ + def __new__(cls): + return super().__new__(cls) + + def __repr__(self): + return "NumberKind" + +NumberKind = _NumberKind() + + +class _BooleanKind(Kind): + """ + Kind for boolean objects. + + SymPy's ``S.true``, ``S.false``, and built-in ``True`` and ``False`` + have this kind. Boolean number ``1`` and ``0`` are not relevant. + + Examples + ======== + + >>> from sympy import S, Q + >>> S.true.kind + BooleanKind + >>> Q.even(3).kind + BooleanKind + """ + def __new__(cls): + return super().__new__(cls) + + def __repr__(self): + return "BooleanKind" + +BooleanKind = _BooleanKind() + + +class KindDispatcher: + """ + Dispatcher to select a kind from multiple kinds by binary dispatching. + + .. notes:: + This approach is experimental, and can be replaced or deleted in + the future. + + Explanation + =========== + + SymPy object's :obj:`sympy.core.kind.Kind()` vaguely represents the + algebraic structure where the object belongs to. Therefore, with + given operation, we can always find a dominating kind among the + different kinds. This class selects the kind by recursive binary + dispatching. If the result cannot be determined, ``UndefinedKind`` + is returned. + + Examples + ======== + + Multiplication between numbers return number. + + >>> from sympy import NumberKind, Mul + >>> Mul._kind_dispatcher(NumberKind, NumberKind) + NumberKind + + Multiplication between number and unknown-kind object returns unknown kind. + + >>> from sympy import UndefinedKind + >>> Mul._kind_dispatcher(NumberKind, UndefinedKind) + UndefinedKind + + Any number and order of kinds is allowed. + + >>> Mul._kind_dispatcher(UndefinedKind, NumberKind) + UndefinedKind + >>> Mul._kind_dispatcher(NumberKind, UndefinedKind, NumberKind) + UndefinedKind + + Since matrix forms a vector space over scalar field, multiplication + between matrix with numeric element and number returns matrix with + numeric element. + + >>> from sympy.matrices import MatrixKind + >>> Mul._kind_dispatcher(MatrixKind(NumberKind), NumberKind) + MatrixKind(NumberKind) + + If a matrix with number element and another matrix with unknown-kind + element are multiplied, we know that the result is matrix but the + kind of its elements is unknown. + + >>> Mul._kind_dispatcher(MatrixKind(NumberKind), MatrixKind(UndefinedKind)) + MatrixKind(UndefinedKind) + + Parameters + ========== + + name : str + + commutative : bool, optional + If True, binary dispatch will be automatically registered in + reversed order as well. + + doc : str, optional + + """ + def __init__(self, name, commutative=False, doc=None): + self.name = name + self.doc = doc + self.commutative = commutative + self._dispatcher = Dispatcher(name) + + def __repr__(self): + return "" % self.name + + def register(self, *types, **kwargs): + """ + Register the binary dispatcher for two kind classes. + + If *self.commutative* is ``True``, signature in reversed order is + automatically registered as well. + """ + on_ambiguity = kwargs.pop("on_ambiguity", None) + if not on_ambiguity: + if self.commutative: + on_ambiguity = ambiguity_register_error_ignore_dup + else: + on_ambiguity = ambiguity_warn + kwargs.update(on_ambiguity=on_ambiguity) + + if not len(types) == 2: + raise RuntimeError( + "Only binary dispatch is supported, but got %s types: <%s>." % ( + len(types), str_signature(types) + )) + + def _(func): + self._dispatcher.add(types, func, **kwargs) + if self.commutative: + self._dispatcher.add(tuple(reversed(types)), func, **kwargs) + return _ + + def __call__(self, *args, **kwargs): + if self.commutative: + kinds = frozenset(args) + else: + kinds = [] + prev = None + for a in args: + if prev is not a: + kinds.append(a) + prev = a + return self.dispatch_kinds(kinds, **kwargs) + + @cacheit + def dispatch_kinds(self, kinds, **kwargs): + # Quick exit for the case where all kinds are same + if len(kinds) == 1: + result, = kinds + if not isinstance(result, Kind): + raise RuntimeError("%s is not a kind." % result) + return result + + for i,kind in enumerate(kinds): + if not isinstance(kind, Kind): + raise RuntimeError("%s is not a kind." % kind) + + if i == 0: + result = kind + else: + prev_kind = result + + t1, t2 = type(prev_kind), type(kind) + k1, k2 = prev_kind, kind + func = self._dispatcher.dispatch(t1, t2) + if func is None and self.commutative: + # try reversed order + func = self._dispatcher.dispatch(t2, t1) + k1, k2 = k2, k1 + if func is None: + # unregistered kind relation + result = UndefinedKind + else: + result = func(k1, k2) + if not isinstance(result, Kind): + raise RuntimeError( + "Dispatcher for {!r} and {!r} must return a Kind, but got {!r}".format( + prev_kind, kind, result + )) + + return result + + @property + def __doc__(self): + docs = [ + "Kind dispatcher : %s" % self.name, + "Note that support for this is experimental. See the docs for :class:`KindDispatcher` for details" + ] + + if self.doc: + docs.append(self.doc) + + s = "Registered kind classes\n" + s += '=' * len(s) + docs.append(s) + + amb_sigs = [] + + typ_sigs = defaultdict(list) + for sigs in self._dispatcher.ordering[::-1]: + key = self._dispatcher.funcs[sigs] + typ_sigs[key].append(sigs) + + for func, sigs in typ_sigs.items(): + + sigs_str = ', '.join('<%s>' % str_signature(sig) for sig in sigs) + + if isinstance(func, RaiseNotImplementedError): + amb_sigs.append(sigs_str) + continue + + s = 'Inputs: %s\n' % sigs_str + s += '-' * len(s) + '\n' + if func.__doc__: + s += func.__doc__.strip() + else: + s += func.__name__ + docs.append(s) + + if amb_sigs: + s = "Ambiguous kind classes\n" + s += '=' * len(s) + docs.append(s) + + s = '\n'.join(amb_sigs) + docs.append(s) + + return '\n\n'.join(docs) diff --git a/lib/python3.12/site-packages/sympy/core/logic.py b/lib/python3.12/site-packages/sympy/core/logic.py new file mode 100644 index 0000000000000000000000000000000000000000..73b525b4b07cb06de0751340614eff15ca3bb510 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/core/logic.py @@ -0,0 +1,427 @@ +"""Logic expressions handling + +NOTE +---- + +at present this is mainly needed for facts.py, feel free however to improve +this stuff for general purpose. +""" + +from __future__ import annotations +from typing import Optional + +# Type of a fuzzy bool +FuzzyBool = Optional[bool] + + +def _torf(args): + """Return True if all args are True, False if they + are all False, else None. + + >>> from sympy.core.logic import _torf + >>> _torf((True, True)) + True + >>> _torf((False, False)) + False + >>> _torf((True, False)) + """ + sawT = sawF = False + for a in args: + if a is True: + if sawF: + return + sawT = True + elif a is False: + if sawT: + return + sawF = True + else: + return + return sawT + + +def _fuzzy_group(args, quick_exit=False): + """Return True if all args are True, None if there is any None else False + unless ``quick_exit`` is True (then return None as soon as a second False + is seen. + + ``_fuzzy_group`` is like ``fuzzy_and`` except that it is more + conservative in returning a False, waiting to make sure that all + arguments are True or False and returning None if any arguments are + None. It also has the capability of permiting only a single False and + returning None if more than one is seen. For example, the presence of a + single transcendental amongst rationals would indicate that the group is + no longer rational; but a second transcendental in the group would make the + determination impossible. + + + Examples + ======== + + >>> from sympy.core.logic import _fuzzy_group + + By default, multiple Falses mean the group is broken: + + >>> _fuzzy_group([False, False, True]) + False + + If multiple Falses mean the group status is unknown then set + `quick_exit` to True so None can be returned when the 2nd False is seen: + + >>> _fuzzy_group([False, False, True], quick_exit=True) + + But if only a single False is seen then the group is known to + be broken: + + >>> _fuzzy_group([False, True, True], quick_exit=True) + False + + """ + saw_other = False + for a in args: + if a is True: + continue + if a is None: + return + if quick_exit and saw_other: + return + saw_other = True + return not saw_other + + +def fuzzy_bool(x): + """Return True, False or None according to x. + + Whereas bool(x) returns True or False, fuzzy_bool allows + for the None value and non-false values (which become None), too. + + Examples + ======== + + >>> from sympy.core.logic import fuzzy_bool + >>> from sympy.abc import x + >>> fuzzy_bool(x), fuzzy_bool(None) + (None, None) + >>> bool(x), bool(None) + (True, False) + + """ + if x is None: + return None + if x in (True, False): + return bool(x) + + +def fuzzy_and(args): + """Return True (all True), False (any False) or None. + + Examples + ======== + + >>> from sympy.core.logic import fuzzy_and + >>> from sympy import Dummy + + If you had a list of objects to test the commutivity of + and you want the fuzzy_and logic applied, passing an + iterator will allow the commutativity to only be computed + as many times as necessary. With this list, False can be + returned after analyzing the first symbol: + + >>> syms = [Dummy(commutative=False), Dummy()] + >>> fuzzy_and(s.is_commutative for s in syms) + False + + That False would require less work than if a list of pre-computed + items was sent: + + >>> fuzzy_and([s.is_commutative for s in syms]) + False + """ + + rv = True + for ai in args: + ai = fuzzy_bool(ai) + if ai is False: + return False + if rv: # this will stop updating if a None is ever trapped + rv = ai + return rv + + +def fuzzy_not(v): + """ + Not in fuzzy logic + + Return None if `v` is None else `not v`. + + Examples + ======== + + >>> from sympy.core.logic import fuzzy_not + >>> fuzzy_not(True) + False + >>> fuzzy_not(None) + >>> fuzzy_not(False) + True + + """ + if v is None: + return v + else: + return not v + + +def fuzzy_or(args): + """ + Or in fuzzy logic. Returns True (any True), False (all False), or None + + See the docstrings of fuzzy_and and fuzzy_not for more info. fuzzy_or is + related to the two by the standard De Morgan's law. + + >>> from sympy.core.logic import fuzzy_or + >>> fuzzy_or([True, False]) + True + >>> fuzzy_or([True, None]) + True + >>> fuzzy_or([False, False]) + False + >>> print(fuzzy_or([False, None])) + None + + """ + rv = False + for ai in args: + ai = fuzzy_bool(ai) + if ai is True: + return True + if rv is False: # this will stop updating if a None is ever trapped + rv = ai + return rv + + +def fuzzy_xor(args): + """Return None if any element of args is not True or False, else + True (if there are an odd number of True elements), else False.""" + t = f = 0 + for a in args: + ai = fuzzy_bool(a) + if ai: + t += 1 + elif ai is False: + f += 1 + else: + return + return t % 2 == 1 + + +def fuzzy_nand(args): + """Return False if all args are True, True if they are all False, + else None.""" + return fuzzy_not(fuzzy_and(args)) + + +class Logic: + """Logical expression""" + # {} 'op' -> LogicClass + op_2class: dict[str, type[Logic]] = {} + + def __new__(cls, *args): + obj = object.__new__(cls) + obj.args = args + return obj + + def __getnewargs__(self): + return self.args + + def __hash__(self): + return hash((type(self).__name__,) + tuple(self.args)) + + def __eq__(a, b): + if not isinstance(b, type(a)): + return False + else: + return a.args == b.args + + def __ne__(a, b): + if not isinstance(b, type(a)): + return True + else: + return a.args != b.args + + def __lt__(self, other): + if self.__cmp__(other) == -1: + return True + return False + + def __cmp__(self, other): + if type(self) is not type(other): + a = str(type(self)) + b = str(type(other)) + else: + a = self.args + b = other.args + return (a > b) - (a < b) + + def __str__(self): + return '%s(%s)' % (self.__class__.__name__, + ', '.join(str(a) for a in self.args)) + + __repr__ = __str__ + + @staticmethod + def fromstring(text): + """Logic from string with space around & and | but none after !. + + e.g. + + !a & b | c + """ + lexpr = None # current logical expression + schedop = None # scheduled operation + for term in text.split(): + # operation symbol + if term in '&|': + if schedop is not None: + raise ValueError( + 'double op forbidden: "%s %s"' % (term, schedop)) + if lexpr is None: + raise ValueError( + '%s cannot be in the beginning of expression' % term) + schedop = term + continue + if '&' in term or '|' in term: + raise ValueError('& and | must have space around them') + if term[0] == '!': + if len(term) == 1: + raise ValueError('do not include space after "!"') + term = Not(term[1:]) + + # already scheduled operation, e.g. '&' + if schedop: + lexpr = Logic.op_2class[schedop](lexpr, term) + schedop = None + continue + + # this should be atom + if lexpr is not None: + raise ValueError( + 'missing op between "%s" and "%s"' % (lexpr, term)) + + lexpr = term + + # let's check that we ended up in correct state + if schedop is not None: + raise ValueError('premature end-of-expression in "%s"' % text) + if lexpr is None: + raise ValueError('"%s" is empty' % text) + + # everything looks good now + return lexpr + + +class AndOr_Base(Logic): + + def __new__(cls, *args): + bargs = [] + for a in args: + if a == cls.op_x_notx: + return a + elif a == (not cls.op_x_notx): + continue # skip this argument + bargs.append(a) + + args = sorted(set(cls.flatten(bargs)), key=hash) + + for a in args: + if Not(a) in args: + return cls.op_x_notx + + if len(args) == 1: + return args.pop() + elif len(args) == 0: + return not cls.op_x_notx + + return Logic.__new__(cls, *args) + + @classmethod + def flatten(cls, args): + # quick-n-dirty flattening for And and Or + args_queue = list(args) + res = [] + + while True: + try: + arg = args_queue.pop(0) + except IndexError: + break + if isinstance(arg, Logic): + if isinstance(arg, cls): + args_queue.extend(arg.args) + continue + res.append(arg) + + args = tuple(res) + return args + + +class And(AndOr_Base): + op_x_notx = False + + def _eval_propagate_not(self): + # !(a&b&c ...) == !a | !b | !c ... + return Or(*[Not(a) for a in self.args]) + + # (a|b|...) & c == (a&c) | (b&c) | ... + def expand(self): + + # first locate Or + for i, arg in enumerate(self.args): + if isinstance(arg, Or): + arest = self.args[:i] + self.args[i + 1:] + + orterms = [And(*(arest + (a,))) for a in arg.args] + for j in range(len(orterms)): + if isinstance(orterms[j], Logic): + orterms[j] = orterms[j].expand() + + res = Or(*orterms) + return res + + return self + + +class Or(AndOr_Base): + op_x_notx = True + + def _eval_propagate_not(self): + # !(a|b|c ...) == !a & !b & !c ... + return And(*[Not(a) for a in self.args]) + + +class Not(Logic): + + def __new__(cls, arg): + if isinstance(arg, str): + return Logic.__new__(cls, arg) + + elif isinstance(arg, bool): + return not arg + elif isinstance(arg, Not): + return arg.args[0] + + elif isinstance(arg, Logic): + # XXX this is a hack to expand right from the beginning + arg = arg._eval_propagate_not() + return arg + + else: + raise ValueError('Not: unknown argument %r' % (arg,)) + + @property + def arg(self): + return self.args[0] + + +Logic.op_2class['&'] = And +Logic.op_2class['|'] = Or +Logic.op_2class['!'] = Not diff --git a/lib/python3.12/site-packages/sympy/core/mod.py b/lib/python3.12/site-packages/sympy/core/mod.py new file mode 100644 index 0000000000000000000000000000000000000000..3f0e07a5612c416632d1a0e8e433c09d1053ded8 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/core/mod.py @@ -0,0 +1,260 @@ +from .add import Add +from .exprtools import gcd_terms +from .function import Function +from .kind import NumberKind +from .logic import fuzzy_and, fuzzy_not +from .mul import Mul +from .numbers import equal_valued +from .singleton import S + + +class Mod(Function): + """Represents a modulo operation on symbolic expressions. + + Parameters + ========== + + p : Expr + Dividend. + + q : Expr + Divisor. + + Notes + ===== + + The convention used is the same as Python's: the remainder always has the + same sign as the divisor. + + Many objects can be evaluated modulo ``n`` much faster than they can be + evaluated directly (or at all). For this, ``evaluate=False`` is + necessary to prevent eager evaluation: + + >>> from sympy import binomial, factorial, Mod, Pow + >>> Mod(Pow(2, 10**16, evaluate=False), 97) + 61 + >>> Mod(factorial(10**9, evaluate=False), 10**9 + 9) + 712524808 + >>> Mod(binomial(10**18, 10**12, evaluate=False), (10**5 + 3)**2) + 3744312326 + + Examples + ======== + + >>> from sympy.abc import x, y + >>> x**2 % y + Mod(x**2, y) + >>> _.subs({x: 5, y: 6}) + 1 + + """ + + kind = NumberKind + + @classmethod + def eval(cls, p, q): + def number_eval(p, q): + """Try to return p % q if both are numbers or +/-p is known + to be less than or equal q. + """ + + if q.is_zero: + raise ZeroDivisionError("Modulo by zero") + if p is S.NaN or q is S.NaN or p.is_finite is False or q.is_finite is False: + return S.NaN + if p is S.Zero or p in (q, -q) or (p.is_integer and q == 1): + return S.Zero + + if q.is_Number: + if p.is_Number: + return p%q + if q == 2: + if p.is_even: + return S.Zero + elif p.is_odd: + return S.One + + if hasattr(p, '_eval_Mod'): + rv = getattr(p, '_eval_Mod')(q) + if rv is not None: + return rv + + # by ratio + r = p/q + if r.is_integer: + return S.Zero + try: + d = int(r) + except TypeError: + pass + else: + if isinstance(d, int): + rv = p - d*q + if (rv*q < 0) == True: + rv += q + return rv + + # by difference + # -2|q| < p < 2|q| + d = abs(p) + for _ in range(2): + d -= abs(q) + if d.is_negative: + if q.is_positive: + if p.is_positive: + return d + q + elif p.is_negative: + return -d + elif q.is_negative: + if p.is_positive: + return d + elif p.is_negative: + return -d + q + break + + rv = number_eval(p, q) + if rv is not None: + return rv + + # denest + if isinstance(p, cls): + qinner = p.args[1] + if qinner % q == 0: + return cls(p.args[0], q) + elif (qinner*(q - qinner)).is_nonnegative: + # |qinner| < |q| and have same sign + return p + elif isinstance(-p, cls): + qinner = (-p).args[1] + if qinner % q == 0: + return cls(-(-p).args[0], q) + elif (qinner*(q + qinner)).is_nonpositive: + # |qinner| < |q| and have different sign + return p + elif isinstance(p, Add): + # separating into modulus and non modulus + both_l = non_mod_l, mod_l = [], [] + for arg in p.args: + both_l[isinstance(arg, cls)].append(arg) + # if q same for all + if mod_l and all(inner.args[1] == q for inner in mod_l): + net = Add(*non_mod_l) + Add(*[i.args[0] for i in mod_l]) + return cls(net, q) + + elif isinstance(p, Mul): + # separating into modulus and non modulus + both_l = non_mod_l, mod_l = [], [] + for arg in p.args: + both_l[isinstance(arg, cls)].append(arg) + + if mod_l and all(inner.args[1] == q for inner in mod_l) and all(t.is_integer for t in p.args) and q.is_integer: + # finding distributive term + non_mod_l = [cls(x, q) for x in non_mod_l] + mod = [] + non_mod = [] + for j in non_mod_l: + if isinstance(j, cls): + mod.append(j.args[0]) + else: + non_mod.append(j) + prod_mod = Mul(*mod) + prod_non_mod = Mul(*non_mod) + prod_mod1 = Mul(*[i.args[0] for i in mod_l]) + net = prod_mod1*prod_mod + return prod_non_mod*cls(net, q) + + if q.is_Integer and q is not S.One: + if all(t.is_integer for t in p.args): + non_mod_l = [i % q if i.is_Integer else i for i in p.args] + if any(iq is S.Zero for iq in non_mod_l): + return S.Zero + + p = Mul(*(non_mod_l + mod_l)) + + # XXX other possibilities? + + from sympy.polys.polyerrors import PolynomialError + from sympy.polys.polytools import gcd + + # extract gcd; any further simplification should be done by the user + try: + G = gcd(p, q) + if not equal_valued(G, 1): + p, q = [gcd_terms(i/G, clear=False, fraction=False) + for i in (p, q)] + except PolynomialError: # issue 21373 + G = S.One + pwas, qwas = p, q + + # simplify terms + # (x + y + 2) % x -> Mod(y + 2, x) + if p.is_Add: + args = [] + for i in p.args: + a = cls(i, q) + if a.count(cls) > i.count(cls): + args.append(i) + else: + args.append(a) + if args != list(p.args): + p = Add(*args) + + else: + # handle coefficients if they are not Rational + # since those are not handled by factor_terms + # e.g. Mod(.6*x, .3*y) -> 0.3*Mod(2*x, y) + cp, p = p.as_coeff_Mul() + cq, q = q.as_coeff_Mul() + ok = False + if not cp.is_Rational or not cq.is_Rational: + r = cp % cq + if equal_valued(r, 0): + G *= cq + p *= int(cp/cq) + ok = True + if not ok: + p = cp*p + q = cq*q + + # simple -1 extraction + if p.could_extract_minus_sign() and q.could_extract_minus_sign(): + G, p, q = [-i for i in (G, p, q)] + + # check again to see if p and q can now be handled as numbers + rv = number_eval(p, q) + if rv is not None: + return rv*G + + # put 1.0 from G on inside + if G.is_Float and equal_valued(G, 1): + p *= G + return cls(p, q, evaluate=False) + elif G.is_Mul and G.args[0].is_Float and equal_valued(G.args[0], 1): + p = G.args[0]*p + G = Mul._from_args(G.args[1:]) + return G*cls(p, q, evaluate=(p, q) != (pwas, qwas)) + + def _eval_is_integer(self): + p, q = self.args + if fuzzy_and([p.is_integer, q.is_integer, fuzzy_not(q.is_zero)]): + return True + + def _eval_is_nonnegative(self): + if self.args[1].is_positive: + return True + + def _eval_is_nonpositive(self): + if self.args[1].is_negative: + return True + + def _eval_rewrite_as_floor(self, a, b, **kwargs): + from sympy.functions.elementary.integers import floor + return a - b*floor(a/b) + + def _eval_as_leading_term(self, x, logx=None, cdir=0): + from sympy.functions.elementary.integers import floor + return self.rewrite(floor)._eval_as_leading_term(x, logx=logx, cdir=cdir) + + def _eval_nseries(self, x, n, logx, cdir=0): + from sympy.functions.elementary.integers import floor + return self.rewrite(floor)._eval_nseries(x, n, logx=logx, cdir=cdir) diff --git a/lib/python3.12/site-packages/sympy/core/mul.py b/lib/python3.12/site-packages/sympy/core/mul.py new file mode 100644 index 0000000000000000000000000000000000000000..d93dbde7586c87e8076347c4d8062b39a4e13c22 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/core/mul.py @@ -0,0 +1,2196 @@ +from typing import Tuple as tTuple +from collections import defaultdict +from functools import reduce +from itertools import product +import operator + +from .sympify import sympify +from .basic import Basic, _args_sortkey +from .singleton import S +from .operations import AssocOp, AssocOpDispatcher +from .cache import cacheit +from .intfunc import integer_nthroot, trailing +from .logic import fuzzy_not, _fuzzy_group +from .expr import Expr +from .parameters import global_parameters +from .kind import KindDispatcher +from .traversal import bottom_up + +from sympy.utilities.iterables import sift + +# internal marker to indicate: +# "there are still non-commutative objects -- don't forget to process them" +class NC_Marker: + is_Order = False + is_Mul = False + is_Number = False + is_Poly = False + + is_commutative = False + + +def _mulsort(args): + # in-place sorting of args + args.sort(key=_args_sortkey) + + +def _unevaluated_Mul(*args): + """Return a well-formed unevaluated Mul: Numbers are collected and + put in slot 0, any arguments that are Muls will be flattened, and args + are sorted. Use this when args have changed but you still want to return + an unevaluated Mul. + + Examples + ======== + + >>> from sympy.core.mul import _unevaluated_Mul as uMul + >>> from sympy import S, sqrt, Mul + >>> from sympy.abc import x + >>> a = uMul(*[S(3.0), x, S(2)]) + >>> a.args[0] + 6.00000000000000 + >>> a.args[1] + x + + Two unevaluated Muls with the same arguments will + always compare as equal during testing: + + >>> m = uMul(sqrt(2), sqrt(3)) + >>> m == uMul(sqrt(3), sqrt(2)) + True + >>> u = Mul(sqrt(3), sqrt(2), evaluate=False) + >>> m == uMul(u) + True + >>> m == Mul(*m.args) + False + + """ + args = list(args) + newargs = [] + ncargs = [] + co = S.One + while args: + a = args.pop() + if a.is_Mul: + c, nc = a.args_cnc() + args.extend(c) + if nc: + ncargs.append(Mul._from_args(nc)) + elif a.is_Number: + co *= a + else: + newargs.append(a) + _mulsort(newargs) + if co is not S.One: + newargs.insert(0, co) + if ncargs: + newargs.append(Mul._from_args(ncargs)) + return Mul._from_args(newargs) + + +class Mul(Expr, AssocOp): + """ + Expression representing multiplication operation for algebraic field. + + .. deprecated:: 1.7 + + Using arguments that aren't subclasses of :class:`~.Expr` in core + operators (:class:`~.Mul`, :class:`~.Add`, and :class:`~.Pow`) is + deprecated. See :ref:`non-expr-args-deprecated` for details. + + Every argument of ``Mul()`` must be ``Expr``. Infix operator ``*`` + on most scalar objects in SymPy calls this class. + + Another use of ``Mul()`` is to represent the structure of abstract + multiplication so that its arguments can be substituted to return + different class. Refer to examples section for this. + + ``Mul()`` evaluates the argument unless ``evaluate=False`` is passed. + The evaluation logic includes: + + 1. Flattening + ``Mul(x, Mul(y, z))`` -> ``Mul(x, y, z)`` + + 2. Identity removing + ``Mul(x, 1, y)`` -> ``Mul(x, y)`` + + 3. Exponent collecting by ``.as_base_exp()`` + ``Mul(x, x**2)`` -> ``Pow(x, 3)`` + + 4. Term sorting + ``Mul(y, x, 2)`` -> ``Mul(2, x, y)`` + + Since multiplication can be vector space operation, arguments may + have the different :obj:`sympy.core.kind.Kind()`. Kind of the + resulting object is automatically inferred. + + Examples + ======== + + >>> from sympy import Mul + >>> from sympy.abc import x, y + >>> Mul(x, 1) + x + >>> Mul(x, x) + x**2 + + If ``evaluate=False`` is passed, result is not evaluated. + + >>> Mul(1, 2, evaluate=False) + 1*2 + >>> Mul(x, x, evaluate=False) + x*x + + ``Mul()`` also represents the general structure of multiplication + operation. + + >>> from sympy import MatrixSymbol + >>> A = MatrixSymbol('A', 2,2) + >>> expr = Mul(x,y).subs({y:A}) + >>> expr + x*A + >>> type(expr) + + + See Also + ======== + + MatMul + + """ + __slots__ = () + + args: tTuple[Expr, ...] + + is_Mul = True + + _args_type = Expr + _kind_dispatcher = KindDispatcher("Mul_kind_dispatcher", commutative=True) + + @property + def kind(self): + arg_kinds = (a.kind for a in self.args) + return self._kind_dispatcher(*arg_kinds) + + def could_extract_minus_sign(self): + if self == (-self): + return False # e.g. zoo*x == -zoo*x + c = self.args[0] + return c.is_Number and c.is_extended_negative + def __neg__(self): + c, args = self.as_coeff_mul() + if args[0] is not S.ComplexInfinity: + c = -c + if c is not S.One: + if args[0].is_Number: + args = list(args) + if c is S.NegativeOne: + args[0] = -args[0] + else: + args[0] *= c + else: + args = (c,) + args + return self._from_args(args, self.is_commutative) + + @classmethod + def flatten(cls, seq): + """Return commutative, noncommutative and order arguments by + combining related terms. + + Notes + ===== + * In an expression like ``a*b*c``, Python process this through SymPy + as ``Mul(Mul(a, b), c)``. This can have undesirable consequences. + + - Sometimes terms are not combined as one would like: + {c.f. https://github.com/sympy/sympy/issues/4596} + + >>> from sympy import Mul, sqrt + >>> from sympy.abc import x, y, z + >>> 2*(x + 1) # this is the 2-arg Mul behavior + 2*x + 2 + >>> y*(x + 1)*2 + 2*y*(x + 1) + >>> 2*(x + 1)*y # 2-arg result will be obtained first + y*(2*x + 2) + >>> Mul(2, x + 1, y) # all 3 args simultaneously processed + 2*y*(x + 1) + >>> 2*((x + 1)*y) # parentheses can control this behavior + 2*y*(x + 1) + + Powers with compound bases may not find a single base to + combine with unless all arguments are processed at once. + Post-processing may be necessary in such cases. + {c.f. https://github.com/sympy/sympy/issues/5728} + + >>> a = sqrt(x*sqrt(y)) + >>> a**3 + (x*sqrt(y))**(3/2) + >>> Mul(a,a,a) + (x*sqrt(y))**(3/2) + >>> a*a*a + x*sqrt(y)*sqrt(x*sqrt(y)) + >>> _.subs(a.base, z).subs(z, a.base) + (x*sqrt(y))**(3/2) + + - If more than two terms are being multiplied then all the + previous terms will be re-processed for each new argument. + So if each of ``a``, ``b`` and ``c`` were :class:`Mul` + expression, then ``a*b*c`` (or building up the product + with ``*=``) will process all the arguments of ``a`` and + ``b`` twice: once when ``a*b`` is computed and again when + ``c`` is multiplied. + + Using ``Mul(a, b, c)`` will process all arguments once. + + * The results of Mul are cached according to arguments, so flatten + will only be called once for ``Mul(a, b, c)``. If you can + structure a calculation so the arguments are most likely to be + repeats then this can save time in computing the answer. For + example, say you had a Mul, M, that you wished to divide by ``d[i]`` + and multiply by ``n[i]`` and you suspect there are many repeats + in ``n``. It would be better to compute ``M*n[i]/d[i]`` rather + than ``M/d[i]*n[i]`` since every time n[i] is a repeat, the + product, ``M*n[i]`` will be returned without flattening -- the + cached value will be returned. If you divide by the ``d[i]`` + first (and those are more unique than the ``n[i]``) then that will + create a new Mul, ``M/d[i]`` the args of which will be traversed + again when it is multiplied by ``n[i]``. + + {c.f. https://github.com/sympy/sympy/issues/5706} + + This consideration is moot if the cache is turned off. + + NB + -- + The validity of the above notes depends on the implementation + details of Mul and flatten which may change at any time. Therefore, + you should only consider them when your code is highly performance + sensitive. + + Removal of 1 from the sequence is already handled by AssocOp.__new__. + """ + + from sympy.calculus.accumulationbounds import AccumBounds + from sympy.matrices.expressions import MatrixExpr + rv = None + if len(seq) == 2: + a, b = seq + if b.is_Rational: + a, b = b, a + seq = [a, b] + assert a is not S.One + if a.is_Rational and not a.is_zero: + r, b = b.as_coeff_Mul() + if b.is_Add: + if r is not S.One: # 2-arg hack + # leave the Mul as a Mul? + ar = a*r + if ar is S.One: + arb = b + else: + arb = cls(a*r, b, evaluate=False) + rv = [arb], [], None + elif global_parameters.distribute and b.is_commutative: + newb = Add(*[_keep_coeff(a, bi) for bi in b.args]) + rv = [newb], [], None + if rv: + return rv + + # apply associativity, separate commutative part of seq + c_part = [] # out: commutative factors + nc_part = [] # out: non-commutative factors + + nc_seq = [] + + coeff = S.One # standalone term + # e.g. 3 * ... + + c_powers = [] # (base,exp) n + # e.g. (x,n) for x + + num_exp = [] # (num-base, exp) y + # e.g. (3, y) for ... * 3 * ... + + neg1e = S.Zero # exponent on -1 extracted from Number-based Pow and I + + pnum_rat = {} # (num-base, Rat-exp) 1/2 + # e.g. (3, 1/2) for ... * 3 * ... + + order_symbols = None + + # --- PART 1 --- + # + # "collect powers and coeff": + # + # o coeff + # o c_powers + # o num_exp + # o neg1e + # o pnum_rat + # + # NOTE: this is optimized for all-objects-are-commutative case + for o in seq: + # O(x) + if o.is_Order: + o, order_symbols = o.as_expr_variables(order_symbols) + + # Mul([...]) + if o.is_Mul: + if o.is_commutative: + seq.extend(o.args) # XXX zerocopy? + + else: + # NCMul can have commutative parts as well + for q in o.args: + if q.is_commutative: + seq.append(q) + else: + nc_seq.append(q) + + # append non-commutative marker, so we don't forget to + # process scheduled non-commutative objects + seq.append(NC_Marker) + + continue + + # 3 + elif o.is_Number: + if o is S.NaN or coeff is S.ComplexInfinity and o.is_zero: + # we know for sure the result will be nan + return [S.NaN], [], None + elif coeff.is_Number or isinstance(coeff, AccumBounds): # it could be zoo + coeff *= o + if coeff is S.NaN: + # we know for sure the result will be nan + return [S.NaN], [], None + continue + + elif isinstance(o, AccumBounds): + coeff = o.__mul__(coeff) + continue + + elif o is S.ComplexInfinity: + if not coeff: + # 0 * zoo = NaN + return [S.NaN], [], None + coeff = S.ComplexInfinity + continue + + elif o is S.ImaginaryUnit: + neg1e += S.Half + continue + + elif o.is_commutative: + # e + # o = b + b, e = o.as_base_exp() + + # y + # 3 + if o.is_Pow: + if b.is_Number: + + # get all the factors with numeric base so they can be + # combined below, but don't combine negatives unless + # the exponent is an integer + if e.is_Rational: + if e.is_Integer: + coeff *= Pow(b, e) # it is an unevaluated power + continue + elif e.is_negative: # also a sign of an unevaluated power + seq.append(Pow(b, e)) + continue + elif b.is_negative: + neg1e += e + b = -b + if b is not S.One: + pnum_rat.setdefault(b, []).append(e) + continue + elif b.is_positive or e.is_integer: + num_exp.append((b, e)) + continue + + c_powers.append((b, e)) + + # NON-COMMUTATIVE + # TODO: Make non-commutative exponents not combine automatically + else: + if o is not NC_Marker: + nc_seq.append(o) + + # process nc_seq (if any) + while nc_seq: + o = nc_seq.pop(0) + if not nc_part: + nc_part.append(o) + continue + + # b c b+c + # try to combine last terms: a * a -> a + o1 = nc_part.pop() + b1, e1 = o1.as_base_exp() + b2, e2 = o.as_base_exp() + new_exp = e1 + e2 + # Only allow powers to combine if the new exponent is + # not an Add. This allow things like a**2*b**3 == a**5 + # if a.is_commutative == False, but prohibits + # a**x*a**y and x**a*x**b from combining (x,y commute). + if b1 == b2 and (not new_exp.is_Add): + o12 = b1 ** new_exp + + # now o12 could be a commutative object + if o12.is_commutative: + seq.append(o12) + continue + else: + nc_seq.insert(0, o12) + + else: + nc_part.extend([o1, o]) + + # We do want a combined exponent if it would not be an Add, such as + # y 2y 3y + # x * x -> x + # We determine if two exponents have the same term by using + # as_coeff_Mul. + # + # Unfortunately, this isn't smart enough to consider combining into + # exponents that might already be adds, so things like: + # z - y y + # x * x will be left alone. This is because checking every possible + # combination can slow things down. + + # gather exponents of common bases... + def _gather(c_powers): + common_b = {} # b:e + for b, e in c_powers: + co = e.as_coeff_Mul() + common_b.setdefault(b, {}).setdefault( + co[1], []).append(co[0]) + for b, d in common_b.items(): + for di, li in d.items(): + d[di] = Add(*li) + new_c_powers = [] + for b, e in common_b.items(): + new_c_powers.extend([(b, c*t) for t, c in e.items()]) + return new_c_powers + + # in c_powers + c_powers = _gather(c_powers) + + # and in num_exp + num_exp = _gather(num_exp) + + # --- PART 2 --- + # + # o process collected powers (x**0 -> 1; x**1 -> x; otherwise Pow) + # o combine collected powers (2**x * 3**x -> 6**x) + # with numeric base + + # ................................ + # now we have: + # - coeff: + # - c_powers: (b, e) + # - num_exp: (2, e) + # - pnum_rat: {(1/3, [1/3, 2/3, 1/4])} + + # 0 1 + # x -> 1 x -> x + + # this should only need to run twice; if it fails because + # it needs to be run more times, perhaps this should be + # changed to a "while True" loop -- the only reason it + # isn't such now is to allow a less-than-perfect result to + # be obtained rather than raising an error or entering an + # infinite loop + for i in range(2): + new_c_powers = [] + changed = False + for b, e in c_powers: + if e.is_zero: + # canceling out infinities yields NaN + if (b.is_Add or b.is_Mul) and any(infty in b.args + for infty in (S.ComplexInfinity, S.Infinity, + S.NegativeInfinity)): + return [S.NaN], [], None + continue + if e is S.One: + if b.is_Number: + coeff *= b + continue + p = b + if e is not S.One: + p = Pow(b, e) + # check to make sure that the base doesn't change + # after exponentiation; to allow for unevaluated + # Pow, we only do so if b is not already a Pow + if p.is_Pow and not b.is_Pow: + bi = b + b, e = p.as_base_exp() + if b != bi: + changed = True + c_part.append(p) + new_c_powers.append((b, e)) + # there might have been a change, but unless the base + # matches some other base, there is nothing to do + if changed and len({ + b for b, e in new_c_powers}) != len(new_c_powers): + # start over again + c_part = [] + c_powers = _gather(new_c_powers) + else: + break + + # x x x + # 2 * 3 -> 6 + inv_exp_dict = {} # exp:Mul(num-bases) x x + # e.g. x:6 for ... * 2 * 3 * ... + for b, e in num_exp: + inv_exp_dict.setdefault(e, []).append(b) + for e, b in inv_exp_dict.items(): + inv_exp_dict[e] = cls(*b) + c_part.extend([Pow(b, e) for e, b in inv_exp_dict.items() if e]) + + # b, e -> e' = sum(e), b + # {(1/5, [1/3]), (1/2, [1/12, 1/4]} -> {(1/3, [1/5, 1/2])} + comb_e = {} + for b, e in pnum_rat.items(): + comb_e.setdefault(Add(*e), []).append(b) + del pnum_rat + # process them, reducing exponents to values less than 1 + # and updating coeff if necessary else adding them to + # num_rat for further processing + num_rat = [] + for e, b in comb_e.items(): + b = cls(*b) + if e.q == 1: + coeff *= Pow(b, e) + continue + if e.p > e.q: + e_i, ep = divmod(e.p, e.q) + coeff *= Pow(b, e_i) + e = Rational(ep, e.q) + num_rat.append((b, e)) + del comb_e + + # extract gcd of bases in num_rat + # 2**(1/3)*6**(1/4) -> 2**(1/3+1/4)*3**(1/4) + pnew = defaultdict(list) + i = 0 # steps through num_rat which may grow + while i < len(num_rat): + bi, ei = num_rat[i] + if bi == 1: + i += 1 + continue + grow = [] + for j in range(i + 1, len(num_rat)): + bj, ej = num_rat[j] + g = bi.gcd(bj) + if g is not S.One: + # 4**r1*6**r2 -> 2**(r1+r2) * 2**r1 * 3**r2 + # this might have a gcd with something else + e = ei + ej + if e.q == 1: + coeff *= Pow(g, e) + else: + if e.p > e.q: + e_i, ep = divmod(e.p, e.q) # change e in place + coeff *= Pow(g, e_i) + e = Rational(ep, e.q) + grow.append((g, e)) + # update the jth item + num_rat[j] = (bj/g, ej) + # update bi that we are checking with + bi = bi/g + if bi is S.One: + break + if bi is not S.One: + obj = Pow(bi, ei) + if obj.is_Number: + coeff *= obj + else: + # changes like sqrt(12) -> 2*sqrt(3) + for obj in Mul.make_args(obj): + if obj.is_Number: + coeff *= obj + else: + assert obj.is_Pow + bi, ei = obj.args + pnew[ei].append(bi) + + num_rat.extend(grow) + i += 1 + + # combine bases of the new powers + for e, b in pnew.items(): + pnew[e] = cls(*b) + + # handle -1 and I + if neg1e: + # treat I as (-1)**(1/2) and compute -1's total exponent + p, q = neg1e.as_numer_denom() + # if the integer part is odd, extract -1 + n, p = divmod(p, q) + if n % 2: + coeff = -coeff + # if it's a multiple of 1/2 extract I + if q == 2: + c_part.append(S.ImaginaryUnit) + elif p: + # see if there is any positive base this power of + # -1 can join + neg1e = Rational(p, q) + for e, b in pnew.items(): + if e == neg1e and b.is_positive: + pnew[e] = -b + break + else: + # keep it separate; we've already evaluated it as + # much as possible so evaluate=False + c_part.append(Pow(S.NegativeOne, neg1e, evaluate=False)) + + # add all the pnew powers + c_part.extend([Pow(b, e) for e, b in pnew.items()]) + + # oo, -oo + if coeff in (S.Infinity, S.NegativeInfinity): + def _handle_for_oo(c_part, coeff_sign): + new_c_part = [] + for t in c_part: + if t.is_extended_positive: + continue + if t.is_extended_negative: + coeff_sign *= -1 + continue + new_c_part.append(t) + return new_c_part, coeff_sign + c_part, coeff_sign = _handle_for_oo(c_part, 1) + nc_part, coeff_sign = _handle_for_oo(nc_part, coeff_sign) + coeff *= coeff_sign + + # zoo + if coeff is S.ComplexInfinity: + # zoo might be + # infinite_real + bounded_im + # bounded_real + infinite_im + # infinite_real + infinite_im + # and non-zero real or imaginary will not change that status. + c_part = [c for c in c_part if not (fuzzy_not(c.is_zero) and + c.is_extended_real is not None)] + nc_part = [c for c in nc_part if not (fuzzy_not(c.is_zero) and + c.is_extended_real is not None)] + + # 0 + elif coeff.is_zero: + # we know for sure the result will be 0 except the multiplicand + # is infinity or a matrix + if any(isinstance(c, MatrixExpr) for c in nc_part): + return [coeff], nc_part, order_symbols + if any(c.is_finite == False for c in c_part): + return [S.NaN], [], order_symbols + return [coeff], [], order_symbols + + # check for straggling Numbers that were produced + _new = [] + for i in c_part: + if i.is_Number: + coeff *= i + else: + _new.append(i) + c_part = _new + + # order commutative part canonically + _mulsort(c_part) + + # current code expects coeff to be always in slot-0 + if coeff is not S.One: + c_part.insert(0, coeff) + + # we are done + if (global_parameters.distribute and not nc_part and len(c_part) == 2 and + c_part[0].is_Number and c_part[0].is_finite and c_part[1].is_Add): + # 2*(1+a) -> 2 + 2 * a + coeff = c_part[0] + c_part = [Add(*[coeff*f for f in c_part[1].args])] + + return c_part, nc_part, order_symbols + + def _eval_power(self, e): + + # don't break up NC terms: (A*B)**3 != A**3*B**3, it is A*B*A*B*A*B + cargs, nc = self.args_cnc(split_1=False) + + if e.is_Integer: + return Mul(*[Pow(b, e, evaluate=False) for b in cargs]) * \ + Pow(Mul._from_args(nc), e, evaluate=False) + if e.is_Rational and e.q == 2: + if self.is_imaginary: + a = self.as_real_imag()[1] + if a.is_Rational: + n, d = abs(a/2).as_numer_denom() + n, t = integer_nthroot(n, 2) + if t: + d, t = integer_nthroot(d, 2) + if t: + from sympy.functions.elementary.complexes import sign + r = sympify(n)/d + return _unevaluated_Mul(r**e.p, (1 + sign(a)*S.ImaginaryUnit)**e.p) + + p = Pow(self, e, evaluate=False) + + if e.is_Rational or e.is_Float: + return p._eval_expand_power_base() + + return p + + @classmethod + def class_key(cls): + return 3, 0, cls.__name__ + + def _eval_evalf(self, prec): + c, m = self.as_coeff_Mul() + if c is S.NegativeOne: + if m.is_Mul: + rv = -AssocOp._eval_evalf(m, prec) + else: + mnew = m._eval_evalf(prec) + if mnew is not None: + m = mnew + rv = -m + else: + rv = AssocOp._eval_evalf(self, prec) + if rv.is_number: + return rv.expand() + return rv + + @property + def _mpc_(self): + """ + Convert self to an mpmath mpc if possible + """ + from .numbers import Float + im_part, imag_unit = self.as_coeff_Mul() + if imag_unit is not S.ImaginaryUnit: + # ValueError may seem more reasonable but since it's a @property, + # we need to use AttributeError to keep from confusing things like + # hasattr. + raise AttributeError("Cannot convert Mul to mpc. Must be of the form Number*I") + + return (Float(0)._mpf_, Float(im_part)._mpf_) + + @cacheit + def as_two_terms(self): + """Return head and tail of self. + + This is the most efficient way to get the head and tail of an + expression. + + - if you want only the head, use self.args[0]; + - if you want to process the arguments of the tail then use + self.as_coef_mul() which gives the head and a tuple containing + the arguments of the tail when treated as a Mul. + - if you want the coefficient when self is treated as an Add + then use self.as_coeff_add()[0] + + Examples + ======== + + >>> from sympy.abc import x, y + >>> (3*x*y).as_two_terms() + (3, x*y) + """ + args = self.args + + if len(args) == 1: + return S.One, self + elif len(args) == 2: + return args + + else: + return args[0], self._new_rawargs(*args[1:]) + + @cacheit + def as_coeff_mul(self, *deps, rational=True, **kwargs): + if deps: + l1, l2 = sift(self.args, lambda x: x.has(*deps), binary=True) + return self._new_rawargs(*l2), tuple(l1) + args = self.args + if args[0].is_Number: + if not rational or args[0].is_Rational: + return args[0], args[1:] + elif args[0].is_extended_negative: + return S.NegativeOne, (-args[0],) + args[1:] + return S.One, args + + def as_coeff_Mul(self, rational=False): + """ + Efficiently extract the coefficient of a product. + """ + coeff, args = self.args[0], self.args[1:] + + if coeff.is_Number: + if not rational or coeff.is_Rational: + if len(args) == 1: + return coeff, args[0] + else: + return coeff, self._new_rawargs(*args) + elif coeff.is_extended_negative: + return S.NegativeOne, self._new_rawargs(*((-coeff,) + args)) + return S.One, self + + def as_real_imag(self, deep=True, **hints): + from sympy.functions.elementary.complexes import Abs, im, re + other = [] + coeffr = [] + coeffi = [] + addterms = S.One + for a in self.args: + r, i = a.as_real_imag() + if i.is_zero: + coeffr.append(r) + elif r.is_zero: + coeffi.append(i*S.ImaginaryUnit) + elif a.is_commutative: + aconj = a.conjugate() if other else None + # search for complex conjugate pairs: + for i, x in enumerate(other): + if x == aconj: + coeffr.append(Abs(x)**2) + del other[i] + break + else: + if a.is_Add: + addterms *= a + else: + other.append(a) + else: + other.append(a) + m = self.func(*other) + if hints.get('ignore') == m: + return + if len(coeffi) % 2: + imco = im(coeffi.pop(0)) + # all other pairs make a real factor; they will be + # put into reco below + else: + imco = S.Zero + reco = self.func(*(coeffr + coeffi)) + r, i = (reco*re(m), reco*im(m)) + if addterms == 1: + if m == 1: + if imco.is_zero: + return (reco, S.Zero) + else: + return (S.Zero, reco*imco) + if imco is S.Zero: + return (r, i) + return (-imco*i, imco*r) + from .function import expand_mul + addre, addim = expand_mul(addterms, deep=False).as_real_imag() + if imco is S.Zero: + return (r*addre - i*addim, i*addre + r*addim) + else: + r, i = -imco*i, imco*r + return (r*addre - i*addim, r*addim + i*addre) + + @staticmethod + def _expandsums(sums): + """ + Helper function for _eval_expand_mul. + + sums must be a list of instances of Basic. + """ + + L = len(sums) + if L == 1: + return sums[0].args + terms = [] + left = Mul._expandsums(sums[:L//2]) + right = Mul._expandsums(sums[L//2:]) + + terms = [Mul(a, b) for a in left for b in right] + added = Add(*terms) + return Add.make_args(added) # it may have collapsed down to one term + + def _eval_expand_mul(self, **hints): + from sympy.simplify.radsimp import fraction + + # Handle things like 1/(x*(x + 1)), which are automatically converted + # to 1/x*1/(x + 1) + expr = self + # default matches fraction's default + n, d = fraction(expr, hints.get('exact', False)) + if d.is_Mul: + n, d = [i._eval_expand_mul(**hints) if i.is_Mul else i + for i in (n, d)] + expr = n/d + if not expr.is_Mul: + return expr + + plain, sums, rewrite = [], [], False + for factor in expr.args: + if factor.is_Add: + sums.append(factor) + rewrite = True + else: + if factor.is_commutative: + plain.append(factor) + else: + sums.append(Basic(factor)) # Wrapper + + if not rewrite: + return expr + else: + plain = self.func(*plain) + if sums: + deep = hints.get("deep", False) + terms = self.func._expandsums(sums) + args = [] + for term in terms: + t = self.func(plain, term) + if t.is_Mul and any(a.is_Add for a in t.args) and deep: + t = t._eval_expand_mul() + args.append(t) + return Add(*args) + else: + return plain + + @cacheit + def _eval_derivative(self, s): + args = list(self.args) + terms = [] + for i in range(len(args)): + d = args[i].diff(s) + if d: + # Note: reduce is used in step of Mul as Mul is unable to + # handle subtypes and operation priority: + terms.append(reduce(lambda x, y: x*y, (args[:i] + [d] + args[i + 1:]), S.One)) + return Add.fromiter(terms) + + @cacheit + def _eval_derivative_n_times(self, s, n): + from .function import AppliedUndef + from .symbol import Symbol, symbols, Dummy + if not isinstance(s, (AppliedUndef, Symbol)): + # other types of s may not be well behaved, e.g. + # (cos(x)*sin(y)).diff([[x, y, z]]) + return super()._eval_derivative_n_times(s, n) + from .numbers import Integer + args = self.args + m = len(args) + if isinstance(n, (int, Integer)): + # https://en.wikipedia.org/wiki/General_Leibniz_rule#More_than_two_factors + terms = [] + from sympy.ntheory.multinomial import multinomial_coefficients_iterator + for kvals, c in multinomial_coefficients_iterator(m, n): + p = Mul(*[arg.diff((s, k)) for k, arg in zip(kvals, args)]) + terms.append(c * p) + return Add(*terms) + from sympy.concrete.summations import Sum + from sympy.functions.combinatorial.factorials import factorial + from sympy.functions.elementary.miscellaneous import Max + kvals = symbols("k1:%i" % m, cls=Dummy) + klast = n - sum(kvals) + nfact = factorial(n) + e, l = (# better to use the multinomial? + nfact/prod(map(factorial, kvals))/factorial(klast)*\ + Mul(*[args[t].diff((s, kvals[t])) for t in range(m-1)])*\ + args[-1].diff((s, Max(0, klast))), + [(k, 0, n) for k in kvals]) + return Sum(e, *l) + + def _eval_difference_delta(self, n, step): + from sympy.series.limitseq import difference_delta as dd + arg0 = self.args[0] + rest = Mul(*self.args[1:]) + return (arg0.subs(n, n + step) * dd(rest, n, step) + dd(arg0, n, step) * + rest) + + def _matches_simple(self, expr, repl_dict): + # handle (w*3).matches('x*5') -> {w: x*5/3} + coeff, terms = self.as_coeff_Mul() + terms = Mul.make_args(terms) + if len(terms) == 1: + newexpr = self.__class__._combine_inverse(expr, coeff) + return terms[0].matches(newexpr, repl_dict) + return + + def matches(self, expr, repl_dict=None, old=False): + expr = sympify(expr) + if self.is_commutative and expr.is_commutative: + return self._matches_commutative(expr, repl_dict, old) + elif self.is_commutative is not expr.is_commutative: + return None + + # Proceed only if both both expressions are non-commutative + c1, nc1 = self.args_cnc() + c2, nc2 = expr.args_cnc() + c1, c2 = [c or [1] for c in [c1, c2]] + + # TODO: Should these be self.func? + comm_mul_self = Mul(*c1) + comm_mul_expr = Mul(*c2) + + repl_dict = comm_mul_self.matches(comm_mul_expr, repl_dict, old) + + # If the commutative arguments didn't match and aren't equal, then + # then the expression as a whole doesn't match + if not repl_dict and c1 != c2: + return None + + # Now match the non-commutative arguments, expanding powers to + # multiplications + nc1 = Mul._matches_expand_pows(nc1) + nc2 = Mul._matches_expand_pows(nc2) + + repl_dict = Mul._matches_noncomm(nc1, nc2, repl_dict) + + return repl_dict or None + + @staticmethod + def _matches_expand_pows(arg_list): + new_args = [] + for arg in arg_list: + if arg.is_Pow and arg.exp > 0: + new_args.extend([arg.base] * arg.exp) + else: + new_args.append(arg) + return new_args + + @staticmethod + def _matches_noncomm(nodes, targets, repl_dict=None): + """Non-commutative multiplication matcher. + + `nodes` is a list of symbols within the matcher multiplication + expression, while `targets` is a list of arguments in the + multiplication expression being matched against. + """ + if repl_dict is None: + repl_dict = {} + else: + repl_dict = repl_dict.copy() + + # List of possible future states to be considered + agenda = [] + # The current matching state, storing index in nodes and targets + state = (0, 0) + node_ind, target_ind = state + # Mapping between wildcard indices and the index ranges they match + wildcard_dict = {} + + while target_ind < len(targets) and node_ind < len(nodes): + node = nodes[node_ind] + + if node.is_Wild: + Mul._matches_add_wildcard(wildcard_dict, state) + + states_matches = Mul._matches_new_states(wildcard_dict, state, + nodes, targets) + if states_matches: + new_states, new_matches = states_matches + agenda.extend(new_states) + if new_matches: + for match in new_matches: + repl_dict[match] = new_matches[match] + if not agenda: + return None + else: + state = agenda.pop() + node_ind, target_ind = state + + return repl_dict + + @staticmethod + def _matches_add_wildcard(dictionary, state): + node_ind, target_ind = state + if node_ind in dictionary: + begin, end = dictionary[node_ind] + dictionary[node_ind] = (begin, target_ind) + else: + dictionary[node_ind] = (target_ind, target_ind) + + @staticmethod + def _matches_new_states(dictionary, state, nodes, targets): + node_ind, target_ind = state + node = nodes[node_ind] + target = targets[target_ind] + + # Don't advance at all if we've exhausted the targets but not the nodes + if target_ind >= len(targets) - 1 and node_ind < len(nodes) - 1: + return None + + if node.is_Wild: + match_attempt = Mul._matches_match_wilds(dictionary, node_ind, + nodes, targets) + if match_attempt: + # If the same node has been matched before, don't return + # anything if the current match is diverging from the previous + # match + other_node_inds = Mul._matches_get_other_nodes(dictionary, + nodes, node_ind) + for ind in other_node_inds: + other_begin, other_end = dictionary[ind] + curr_begin, curr_end = dictionary[node_ind] + + other_targets = targets[other_begin:other_end + 1] + current_targets = targets[curr_begin:curr_end + 1] + + for curr, other in zip(current_targets, other_targets): + if curr != other: + return None + + # A wildcard node can match more than one target, so only the + # target index is advanced + new_state = [(node_ind, target_ind + 1)] + # Only move on to the next node if there is one + if node_ind < len(nodes) - 1: + new_state.append((node_ind + 1, target_ind + 1)) + return new_state, match_attempt + else: + # If we're not at a wildcard, then make sure we haven't exhausted + # nodes but not targets, since in this case one node can only match + # one target + if node_ind >= len(nodes) - 1 and target_ind < len(targets) - 1: + return None + + match_attempt = node.matches(target) + + if match_attempt: + return [(node_ind + 1, target_ind + 1)], match_attempt + elif node == target: + return [(node_ind + 1, target_ind + 1)], None + else: + return None + + @staticmethod + def _matches_match_wilds(dictionary, wildcard_ind, nodes, targets): + """Determine matches of a wildcard with sub-expression in `target`.""" + wildcard = nodes[wildcard_ind] + begin, end = dictionary[wildcard_ind] + terms = targets[begin:end + 1] + # TODO: Should this be self.func? + mult = Mul(*terms) if len(terms) > 1 else terms[0] + return wildcard.matches(mult) + + @staticmethod + def _matches_get_other_nodes(dictionary, nodes, node_ind): + """Find other wildcards that may have already been matched.""" + ind_node = nodes[node_ind] + return [ind for ind in dictionary if nodes[ind] == ind_node] + + @staticmethod + def _combine_inverse(lhs, rhs): + """ + Returns lhs/rhs, but treats arguments like symbols, so things + like oo/oo return 1 (instead of a nan) and ``I`` behaves like + a symbol instead of sqrt(-1). + """ + from sympy.simplify.simplify import signsimp + from .symbol import Dummy + if lhs == rhs: + return S.One + + def check(l, r): + if l.is_Float and r.is_comparable: + # if both objects are added to 0 they will share the same "normalization" + # and are more likely to compare the same. Since Add(foo, 0) will not allow + # the 0 to pass, we use __add__ directly. + return l.__add__(0) == r.evalf().__add__(0) + return False + if check(lhs, rhs) or check(rhs, lhs): + return S.One + if any(i.is_Pow or i.is_Mul for i in (lhs, rhs)): + # gruntz and limit wants a literal I to not combine + # with a power of -1 + d = Dummy('I') + _i = {S.ImaginaryUnit: d} + i_ = {d: S.ImaginaryUnit} + a = lhs.xreplace(_i).as_powers_dict() + b = rhs.xreplace(_i).as_powers_dict() + blen = len(b) + for bi in tuple(b.keys()): + if bi in a: + a[bi] -= b.pop(bi) + if not a[bi]: + a.pop(bi) + if len(b) != blen: + lhs = Mul(*[k**v for k, v in a.items()]).xreplace(i_) + rhs = Mul(*[k**v for k, v in b.items()]).xreplace(i_) + rv = lhs/rhs + srv = signsimp(rv) + return srv if srv.is_Number else rv + + def as_powers_dict(self): + d = defaultdict(int) + for term in self.args: + for b, e in term.as_powers_dict().items(): + d[b] += e + return d + + def as_numer_denom(self): + # don't use _from_args to rebuild the numerators and denominators + # as the order is not guaranteed to be the same once they have + # been separated from each other + numers, denoms = list(zip(*[f.as_numer_denom() for f in self.args])) + return self.func(*numers), self.func(*denoms) + + def as_base_exp(self): + e1 = None + bases = [] + nc = 0 + for m in self.args: + b, e = m.as_base_exp() + if not b.is_commutative: + nc += 1 + if e1 is None: + e1 = e + elif e != e1 or nc > 1: + return self, S.One + bases.append(b) + return self.func(*bases), e1 + + def _eval_is_polynomial(self, syms): + return all(term._eval_is_polynomial(syms) for term in self.args) + + def _eval_is_rational_function(self, syms): + return all(term._eval_is_rational_function(syms) for term in self.args) + + def _eval_is_meromorphic(self, x, a): + return _fuzzy_group((arg.is_meromorphic(x, a) for arg in self.args), + quick_exit=True) + + def _eval_is_algebraic_expr(self, syms): + return all(term._eval_is_algebraic_expr(syms) for term in self.args) + + _eval_is_commutative = lambda self: _fuzzy_group( + a.is_commutative for a in self.args) + + def _eval_is_complex(self): + comp = _fuzzy_group(a.is_complex for a in self.args) + if comp is False: + if any(a.is_infinite for a in self.args): + if any(a.is_zero is not False for a in self.args): + return None + return False + return comp + + def _eval_is_zero_infinite_helper(self): + # + # Helper used by _eval_is_zero and _eval_is_infinite. + # + # Three-valued logic is tricky so let us reason this carefully. It + # would be nice to say that we just check is_zero/is_infinite in all + # args but we need to be careful about the case that one arg is zero + # and another is infinite like Mul(0, oo) or more importantly a case + # where it is not known if the arguments are zero or infinite like + # Mul(y, 1/x). If either y or x could be zero then there is a + # *possibility* that we have Mul(0, oo) which should give None for both + # is_zero and is_infinite. + # + # We keep track of whether we have seen a zero or infinity but we also + # need to keep track of whether we have *possibly* seen one which + # would be indicated by None. + # + # For each argument there is the possibility that is_zero might give + # True, False or None and likewise that is_infinite might give True, + # False or None, giving 9 combinations. The True cases for is_zero and + # is_infinite are mutually exclusive though so there are 3 main cases: + # + # - is_zero = True + # - is_infinite = True + # - is_zero and is_infinite are both either False or None + # + # At the end seen_zero and seen_infinite can be any of 9 combinations + # of True/False/None. Unless one is False though we cannot return + # anything except None: + # + # - is_zero=True needs seen_zero=True and seen_infinite=False + # - is_zero=False needs seen_zero=False + # - is_infinite=True needs seen_infinite=True and seen_zero=False + # - is_infinite=False needs seen_infinite=False + # - anything else gives both is_zero=None and is_infinite=None + # + # The loop only sets the flags to True or None and never back to False. + # Hence as soon as neither flag is False we exit early returning None. + # In particular as soon as we encounter a single arg that has + # is_zero=is_infinite=None we exit. This is a common case since it is + # the default assumptions for a Symbol and also the case for most + # expressions containing such a symbol. The early exit gives a big + # speedup for something like Mul(*symbols('x:1000')).is_zero. + # + seen_zero = seen_infinite = False + + for a in self.args: + if a.is_zero: + if seen_infinite is not False: + return None, None + seen_zero = True + elif a.is_infinite: + if seen_zero is not False: + return None, None + seen_infinite = True + else: + if seen_zero is False and a.is_zero is None: + if seen_infinite is not False: + return None, None + seen_zero = None + if seen_infinite is False and a.is_infinite is None: + if seen_zero is not False: + return None, None + seen_infinite = None + + return seen_zero, seen_infinite + + def _eval_is_zero(self): + # True iff any arg is zero and no arg is infinite but need to handle + # three valued logic carefully. + seen_zero, seen_infinite = self._eval_is_zero_infinite_helper() + + if seen_zero is False: + return False + elif seen_zero is True and seen_infinite is False: + return True + else: + return None + + def _eval_is_infinite(self): + # True iff any arg is infinite and no arg is zero but need to handle + # three valued logic carefully. + seen_zero, seen_infinite = self._eval_is_zero_infinite_helper() + + if seen_infinite is True and seen_zero is False: + return True + elif seen_infinite is False: + return False + else: + return None + + # We do not need to implement _eval_is_finite because the assumptions + # system can infer it from finite = not infinite. + + def _eval_is_rational(self): + r = _fuzzy_group((a.is_rational for a in self.args), quick_exit=True) + if r: + return r + elif r is False: + # All args except one are rational + if all(a.is_zero is False for a in self.args): + return False + + def _eval_is_algebraic(self): + r = _fuzzy_group((a.is_algebraic for a in self.args), quick_exit=True) + if r: + return r + elif r is False: + # All args except one are algebraic + if all(a.is_zero is False for a in self.args): + return False + + # without involving odd/even checks this code would suffice: + #_eval_is_integer = lambda self: _fuzzy_group( + # (a.is_integer for a in self.args), quick_exit=True) + def _eval_is_integer(self): + is_rational = self._eval_is_rational() + if is_rational is False: + return False + + numerators = [] + denominators = [] + unknown = False + for a in self.args: + hit = False + if a.is_integer: + if abs(a) is not S.One: + numerators.append(a) + elif a.is_Rational: + n, d = a.as_numer_denom() + if abs(n) is not S.One: + numerators.append(n) + if d is not S.One: + denominators.append(d) + elif a.is_Pow: + b, e = a.as_base_exp() + if not b.is_integer or not e.is_integer: + hit = unknown = True + if e.is_negative: + denominators.append(2 if a is S.Half else + Pow(a, S.NegativeOne)) + elif not hit: + # int b and pos int e: a = b**e is integer + assert not e.is_positive + # for rational self and e equal to zero: a = b**e is 1 + assert not e.is_zero + return # sign of e unknown -> self.is_integer unknown + else: + # x**2, 2**x, or x**y with x and y int-unknown -> unknown + return + else: + return + + if not denominators and not unknown: + return True + + allodd = lambda x: all(i.is_odd for i in x) + alleven = lambda x: all(i.is_even for i in x) + anyeven = lambda x: any(i.is_even for i in x) + + from .relational import is_gt + if not numerators and denominators and all( + is_gt(_, S.One) for _ in denominators): + return False + elif unknown: + return + elif allodd(numerators) and anyeven(denominators): + return False + elif anyeven(numerators) and denominators == [2]: + return True + elif alleven(numerators) and allodd(denominators + ) and (Mul(*denominators, evaluate=False) - 1 + ).is_positive: + return False + if len(denominators) == 1: + d = denominators[0] + if d.is_Integer and d.is_even: + # if minimal power of 2 in num vs den is not + # negative then we have an integer + if (Add(*[i.as_base_exp()[1] for i in + numerators if i.is_even]) - trailing(d.p) + ).is_nonnegative: + return True + if len(numerators) == 1: + n = numerators[0] + if n.is_Integer and n.is_even: + # if minimal power of 2 in den vs num is positive + # then we have have a non-integer + if (Add(*[i.as_base_exp()[1] for i in + denominators if i.is_even]) - trailing(n.p) + ).is_positive: + return False + + def _eval_is_polar(self): + has_polar = any(arg.is_polar for arg in self.args) + return has_polar and \ + all(arg.is_polar or arg.is_positive for arg in self.args) + + def _eval_is_extended_real(self): + return self._eval_real_imag(True) + + def _eval_real_imag(self, real): + zero = False + t_not_re_im = None + + for t in self.args: + if (t.is_complex or t.is_infinite) is False and t.is_extended_real is False: + return False + elif t.is_imaginary: # I + real = not real + elif t.is_extended_real: # 2 + if not zero: + z = t.is_zero + if not z and zero is False: + zero = z + elif z: + if all(a.is_finite for a in self.args): + return True + return + elif t.is_extended_real is False: + # symbolic or literal like `2 + I` or symbolic imaginary + if t_not_re_im: + return # complex terms might cancel + t_not_re_im = t + elif t.is_imaginary is False: # symbolic like `2` or `2 + I` + if t_not_re_im: + return # complex terms might cancel + t_not_re_im = t + else: + return + + if t_not_re_im: + if t_not_re_im.is_extended_real is False: + if real: # like 3 + return zero # 3*(smthng like 2 + I or i) is not real + if t_not_re_im.is_imaginary is False: # symbolic 2 or 2 + I + if not real: # like I + return zero # I*(smthng like 2 or 2 + I) is not real + elif zero is False: + return real # can't be trumped by 0 + elif real: + return real # doesn't matter what zero is + + def _eval_is_imaginary(self): + if all(a.is_zero is False and a.is_finite for a in self.args): + return self._eval_real_imag(False) + + def _eval_is_hermitian(self): + return self._eval_herm_antiherm(True) + + def _eval_is_antihermitian(self): + return self._eval_herm_antiherm(False) + + def _eval_herm_antiherm(self, herm): + for t in self.args: + if t.is_hermitian is None or t.is_antihermitian is None: + return + if t.is_hermitian: + continue + elif t.is_antihermitian: + herm = not herm + else: + return + + if herm is not False: + return herm + + is_zero = self._eval_is_zero() + if is_zero: + return True + elif is_zero is False: + return herm + + def _eval_is_irrational(self): + for t in self.args: + a = t.is_irrational + if a: + others = list(self.args) + others.remove(t) + if all((x.is_rational and fuzzy_not(x.is_zero)) is True for x in others): + return True + return + if a is None: + return + if all(x.is_real for x in self.args): + return False + + def _eval_is_extended_positive(self): + """Return True if self is positive, False if not, and None if it + cannot be determined. + + Explanation + =========== + + This algorithm is non-recursive and works by keeping track of the + sign which changes when a negative or nonpositive is encountered. + Whether a nonpositive or nonnegative is seen is also tracked since + the presence of these makes it impossible to return True, but + possible to return False if the end result is nonpositive. e.g. + + pos * neg * nonpositive -> pos or zero -> None is returned + pos * neg * nonnegative -> neg or zero -> False is returned + """ + return self._eval_pos_neg(1) + + def _eval_pos_neg(self, sign): + saw_NON = saw_NOT = False + for t in self.args: + if t.is_extended_positive: + continue + elif t.is_extended_negative: + sign = -sign + elif t.is_zero: + if all(a.is_finite for a in self.args): + return False + return + elif t.is_extended_nonpositive: + sign = -sign + saw_NON = True + elif t.is_extended_nonnegative: + saw_NON = True + # FIXME: is_positive/is_negative is False doesn't take account of + # Symbol('x', infinite=True, extended_real=True) which has + # e.g. is_positive is False but has uncertain sign. + elif t.is_positive is False: + sign = -sign + if saw_NOT: + return + saw_NOT = True + elif t.is_negative is False: + if saw_NOT: + return + saw_NOT = True + else: + return + if sign == 1 and saw_NON is False and saw_NOT is False: + return True + if sign < 0: + return False + + def _eval_is_extended_negative(self): + return self._eval_pos_neg(-1) + + def _eval_is_odd(self): + is_integer = self._eval_is_integer() + if is_integer is not True: + return is_integer + + from sympy.simplify.radsimp import fraction + n, d = fraction(self) + if d.is_Integer and d.is_even: + # if minimal power of 2 in num vs den is + # positive then we have an even number + if (Add(*[i.as_base_exp()[1] for i in + Mul.make_args(n) if i.is_even]) - trailing(d.p) + ).is_positive: + return False + return + r, acc = True, 1 + for t in self.args: + if abs(t) is S.One: + continue + if t.is_even: + return False + if r is False: + pass + elif acc != 1 and (acc + t).is_odd: + r = False + elif t.is_even is None: + r = None + acc = t + return r + + def _eval_is_even(self): + from sympy.simplify.radsimp import fraction + n, d = fraction(self) + if n.is_Integer and n.is_even: + # if minimal power of 2 in den vs num is not + # negative then this is not an integer and + # can't be even + if (Add(*[i.as_base_exp()[1] for i in + Mul.make_args(d) if i.is_even]) - trailing(n.p) + ).is_nonnegative: + return False + + def _eval_is_composite(self): + """ + Here we count the number of arguments that have a minimum value + greater than two. + If there are more than one of such a symbol then the result is composite. + Else, the result cannot be determined. + """ + number_of_args = 0 # count of symbols with minimum value greater than one + for arg in self.args: + if not (arg.is_integer and arg.is_positive): + return None + if (arg-1).is_positive: + number_of_args += 1 + + if number_of_args > 1: + return True + + def _eval_subs(self, old, new): + from sympy.functions.elementary.complexes import sign + from sympy.ntheory.factor_ import multiplicity + from sympy.simplify.powsimp import powdenest + from sympy.simplify.radsimp import fraction + + if not old.is_Mul: + return None + + # try keep replacement literal so -2*x doesn't replace 4*x + if old.args[0].is_Number and old.args[0] < 0: + if self.args[0].is_Number: + if self.args[0] < 0: + return self._subs(-old, -new) + return None + + def base_exp(a): + # if I and -1 are in a Mul, they get both end up with + # a -1 base (see issue 6421); all we want here are the + # true Pow or exp separated into base and exponent + from sympy.functions.elementary.exponential import exp + if a.is_Pow or isinstance(a, exp): + return a.as_base_exp() + return a, S.One + + def breakup(eq): + """break up powers of eq when treated as a Mul: + b**(Rational*e) -> b**e, Rational + commutatives come back as a dictionary {b**e: Rational} + noncommutatives come back as a list [(b**e, Rational)] + """ + + (c, nc) = (defaultdict(int), []) + for a in Mul.make_args(eq): + a = powdenest(a) + (b, e) = base_exp(a) + if e is not S.One: + (co, _) = e.as_coeff_mul() + b = Pow(b, e/co) + e = co + if a.is_commutative: + c[b] += e + else: + nc.append([b, e]) + return (c, nc) + + def rejoin(b, co): + """ + Put rational back with exponent; in general this is not ok, but + since we took it from the exponent for analysis, it's ok to put + it back. + """ + + (b, e) = base_exp(b) + return Pow(b, e*co) + + def ndiv(a, b): + """if b divides a in an extractive way (like 1/4 divides 1/2 + but not vice versa, and 2/5 does not divide 1/3) then return + the integer number of times it divides, else return 0. + """ + if not b.q % a.q or not a.q % b.q: + return int(a/b) + return 0 + + # give Muls in the denominator a chance to be changed (see issue 5651) + # rv will be the default return value + rv = None + n, d = fraction(self) + self2 = self + if d is not S.One: + self2 = n._subs(old, new)/d._subs(old, new) + if not self2.is_Mul: + return self2._subs(old, new) + if self2 != self: + rv = self2 + + # Now continue with regular substitution. + + # handle the leading coefficient and use it to decide if anything + # should even be started; we always know where to find the Rational + # so it's a quick test + + co_self = self2.args[0] + co_old = old.args[0] + co_xmul = None + if co_old.is_Rational and co_self.is_Rational: + # if coeffs are the same there will be no updating to do + # below after breakup() step; so skip (and keep co_xmul=None) + if co_old != co_self: + co_xmul = co_self.extract_multiplicatively(co_old) + elif co_old.is_Rational: + return rv + + # break self and old into factors + + (c, nc) = breakup(self2) + (old_c, old_nc) = breakup(old) + + # update the coefficients if we had an extraction + # e.g. if co_self were 2*(3/35*x)**2 and co_old = 3/5 + # then co_self in c is replaced by (3/5)**2 and co_residual + # is 2*(1/7)**2 + + if co_xmul and co_xmul.is_Rational and abs(co_old) != 1: + mult = S(multiplicity(abs(co_old), co_self)) + c.pop(co_self) + if co_old in c: + c[co_old] += mult + else: + c[co_old] = mult + co_residual = co_self/co_old**mult + else: + co_residual = 1 + + # do quick tests to see if we can't succeed + + ok = True + if len(old_nc) > len(nc): + # more non-commutative terms + ok = False + elif len(old_c) > len(c): + # more commutative terms + ok = False + elif {i[0] for i in old_nc}.difference({i[0] for i in nc}): + # unmatched non-commutative bases + ok = False + elif set(old_c).difference(set(c)): + # unmatched commutative terms + ok = False + elif any(sign(c[b]) != sign(old_c[b]) for b in old_c): + # differences in sign + ok = False + if not ok: + return rv + + if not old_c: + cdid = None + else: + rat = [] + for (b, old_e) in old_c.items(): + c_e = c[b] + rat.append(ndiv(c_e, old_e)) + if not rat[-1]: + return rv + cdid = min(rat) + + if not old_nc: + ncdid = None + for i in range(len(nc)): + nc[i] = rejoin(*nc[i]) + else: + ncdid = 0 # number of nc replacements we did + take = len(old_nc) # how much to look at each time + limit = cdid or S.Infinity # max number that we can take + failed = [] # failed terms will need subs if other terms pass + i = 0 + while limit and i + take <= len(nc): + hit = False + + # the bases must be equivalent in succession, and + # the powers must be extractively compatible on the + # first and last factor but equal in between. + + rat = [] + for j in range(take): + if nc[i + j][0] != old_nc[j][0]: + break + elif j == 0: + rat.append(ndiv(nc[i + j][1], old_nc[j][1])) + elif j == take - 1: + rat.append(ndiv(nc[i + j][1], old_nc[j][1])) + elif nc[i + j][1] != old_nc[j][1]: + break + else: + rat.append(1) + j += 1 + else: + ndo = min(rat) + if ndo: + if take == 1: + if cdid: + ndo = min(cdid, ndo) + nc[i] = Pow(new, ndo)*rejoin(nc[i][0], + nc[i][1] - ndo*old_nc[0][1]) + else: + ndo = 1 + + # the left residual + + l = rejoin(nc[i][0], nc[i][1] - ndo* + old_nc[0][1]) + + # eliminate all middle terms + + mid = new + + # the right residual (which may be the same as the middle if take == 2) + + ir = i + take - 1 + r = (nc[ir][0], nc[ir][1] - ndo* + old_nc[-1][1]) + if r[1]: + if i + take < len(nc): + nc[i:i + take] = [l*mid, r] + else: + r = rejoin(*r) + nc[i:i + take] = [l*mid*r] + else: + + # there was nothing left on the right + + nc[i:i + take] = [l*mid] + + limit -= ndo + ncdid += ndo + hit = True + if not hit: + + # do the subs on this failing factor + + failed.append(i) + i += 1 + else: + + if not ncdid: + return rv + + # although we didn't fail, certain nc terms may have + # failed so we rebuild them after attempting a partial + # subs on them + + failed.extend(range(i, len(nc))) + for i in failed: + nc[i] = rejoin(*nc[i]).subs(old, new) + + # rebuild the expression + + if cdid is None: + do = ncdid + elif ncdid is None: + do = cdid + else: + do = min(ncdid, cdid) + + margs = [] + for b in c: + if b in old_c: + + # calculate the new exponent + + e = c[b] - old_c[b]*do + margs.append(rejoin(b, e)) + else: + margs.append(rejoin(b.subs(old, new), c[b])) + if cdid and not ncdid: + + # in case we are replacing commutative with non-commutative, + # we want the new term to come at the front just like the + # rest of this routine + + margs = [Pow(new, cdid)] + margs + return co_residual*self2.func(*margs)*self2.func(*nc) + + def _eval_nseries(self, x, n, logx, cdir=0): + from .function import PoleError + from sympy.functions.elementary.integers import ceiling + from sympy.series.order import Order + + def coeff_exp(term, x): + lt = term.as_coeff_exponent(x) + if lt[0].has(x): + try: + lt = term.leadterm(x) + except ValueError: + return term, S.Zero + return lt + + ords = [] + + try: + for t in self.args: + coeff, exp = t.leadterm(x) + if not coeff.has(x): + ords.append((t, exp)) + else: + raise ValueError + + n0 = sum(t[1] for t in ords if t[1].is_number) + facs = [] + for t, m in ords: + n1 = ceiling(n - n0 + (m if m.is_number else 0)) + s = t.nseries(x, n=n1, logx=logx, cdir=cdir) + ns = s.getn() + if ns is not None: + if ns < n1: # less than expected + n -= n1 - ns # reduce n + facs.append(s) + + except (ValueError, NotImplementedError, TypeError, PoleError): + # XXX: Catching so many generic exceptions around a large block of + # code will mask bugs. Whatever purpose catching these exceptions + # serves should be handled in a different way. + n0 = sympify(sum(t[1] for t in ords if t[1].is_number)) + if n0.is_nonnegative: + n0 = S.Zero + facs = [t.nseries(x, n=ceiling(n-n0), logx=logx, cdir=cdir) for t in self.args] + from sympy.simplify.powsimp import powsimp + res = powsimp(self.func(*facs).expand(), combine='exp', deep=True) + if res.has(Order): + res += Order(x**n, x) + return res + + res = S.Zero + ords2 = [Add.make_args(factor) for factor in facs] + + for fac in product(*ords2): + ords3 = [coeff_exp(term, x) for term in fac] + coeffs, powers = zip(*ords3) + power = sum(powers) + if (power - n).is_negative: + res += Mul(*coeffs)*(x**power) + + def max_degree(e, x): + if e is x: + return S.One + if e.is_Atom: + return S.Zero + if e.is_Add: + return max(max_degree(a, x) for a in e.args) + if e.is_Mul: + return Add(*[max_degree(a, x) for a in e.args]) + if e.is_Pow: + return max_degree(e.base, x)*e.exp + return S.Zero + + if self.is_polynomial(x): + from sympy.polys.polyerrors import PolynomialError + from sympy.polys.polytools import degree + try: + if max_degree(self, x) >= n or degree(self, x) != degree(res, x): + res += Order(x**n, x) + except PolynomialError: + pass + else: + return res + + if res != self: + if (self - res).subs(x, 0) == S.Zero and n > 0: + lt = self._eval_as_leading_term(x, logx=logx, cdir=cdir) + if lt == S.Zero: + return res + res += Order(x**n, x) + return res + + def _eval_as_leading_term(self, x, logx=None, cdir=0): + return self.func(*[t.as_leading_term(x, logx=logx, cdir=cdir) for t in self.args]) + + def _eval_conjugate(self): + return self.func(*[t.conjugate() for t in self.args]) + + def _eval_transpose(self): + return self.func(*[t.transpose() for t in self.args[::-1]]) + + def _eval_adjoint(self): + return self.func(*[t.adjoint() for t in self.args[::-1]]) + + def as_content_primitive(self, radical=False, clear=True): + """Return the tuple (R, self/R) where R is the positive Rational + extracted from self. + + Examples + ======== + + >>> from sympy import sqrt + >>> (-3*sqrt(2)*(2 - 2*sqrt(2))).as_content_primitive() + (6, -sqrt(2)*(1 - sqrt(2))) + + See docstring of Expr.as_content_primitive for more examples. + """ + + coef = S.One + args = [] + for a in self.args: + c, p = a.as_content_primitive(radical=radical, clear=clear) + coef *= c + if p is not S.One: + args.append(p) + # don't use self._from_args here to reconstruct args + # since there may be identical args now that should be combined + # e.g. (2+2*x)*(3+3*x) should be (6, (1 + x)**2) not (6, (1+x)*(1+x)) + return coef, self.func(*args) + + def as_ordered_factors(self, order=None): + """Transform an expression into an ordered list of factors. + + Examples + ======== + + >>> from sympy import sin, cos + >>> from sympy.abc import x, y + + >>> (2*x*y*sin(x)*cos(x)).as_ordered_factors() + [2, x, y, sin(x), cos(x)] + + """ + cpart, ncpart = self.args_cnc() + cpart.sort(key=lambda expr: expr.sort_key(order=order)) + return cpart + ncpart + + @property + def _sorted_args(self): + return tuple(self.as_ordered_factors()) + +mul = AssocOpDispatcher('mul') + + +def prod(a, start=1): + """Return product of elements of a. Start with int 1 so if only + ints are included then an int result is returned. + + Examples + ======== + + >>> from sympy import prod, S + >>> prod(range(3)) + 0 + >>> type(_) is int + True + >>> prod([S(2), 3]) + 6 + >>> _.is_Integer + True + + You can start the product at something other than 1: + + >>> prod([1, 2], 3) + 6 + + """ + return reduce(operator.mul, a, start) + + +def _keep_coeff(coeff, factors, clear=True, sign=False): + """Return ``coeff*factors`` unevaluated if necessary. + + If ``clear`` is False, do not keep the coefficient as a factor + if it can be distributed on a single factor such that one or + more terms will still have integer coefficients. + + If ``sign`` is True, allow a coefficient of -1 to remain factored out. + + Examples + ======== + + >>> from sympy.core.mul import _keep_coeff + >>> from sympy.abc import x, y + >>> from sympy import S + + >>> _keep_coeff(S.Half, x + 2) + (x + 2)/2 + >>> _keep_coeff(S.Half, x + 2, clear=False) + x/2 + 1 + >>> _keep_coeff(S.Half, (x + 2)*y, clear=False) + y*(x + 2)/2 + >>> _keep_coeff(S(-1), x + y) + -x - y + >>> _keep_coeff(S(-1), x + y, sign=True) + -(x + y) + """ + if not coeff.is_Number: + if factors.is_Number: + factors, coeff = coeff, factors + else: + return coeff*factors + if factors is S.One: + return coeff + if coeff is S.One: + return factors + elif coeff is S.NegativeOne and not sign: + return -factors + elif factors.is_Add: + if not clear and coeff.is_Rational and coeff.q != 1: + args = [i.as_coeff_Mul() for i in factors.args] + args = [(_keep_coeff(c, coeff), m) for c, m in args] + if any(c.is_Integer for c, _ in args): + return Add._from_args([Mul._from_args( + i[1:] if i[0] == 1 else i) for i in args]) + return Mul(coeff, factors, evaluate=False) + elif factors.is_Mul: + margs = list(factors.args) + if margs[0].is_Number: + margs[0] *= coeff + if margs[0] == 1: + margs.pop(0) + else: + margs.insert(0, coeff) + return Mul._from_args(margs) + else: + m = coeff*factors + if m.is_Number and not factors.is_Number: + m = Mul._from_args((coeff, factors)) + return m + +def expand_2arg(e): + def do(e): + if e.is_Mul: + c, r = e.as_coeff_Mul() + if c.is_Number and r.is_Add: + return _unevaluated_Add(*[c*ri for ri in r.args]) + return e + return bottom_up(e, do) + + +from .numbers import Rational +from .power import Pow +from .add import Add, _unevaluated_Add diff --git a/lib/python3.12/site-packages/sympy/core/multidimensional.py b/lib/python3.12/site-packages/sympy/core/multidimensional.py new file mode 100644 index 0000000000000000000000000000000000000000..133e0ab6cba6a87c627feb6f6034a6daed1128c5 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/core/multidimensional.py @@ -0,0 +1,131 @@ +""" +Provides functionality for multidimensional usage of scalar-functions. + +Read the vectorize docstring for more details. +""" + +from functools import wraps + + +def apply_on_element(f, args, kwargs, n): + """ + Returns a structure with the same dimension as the specified argument, + where each basic element is replaced by the function f applied on it. All + other arguments stay the same. + """ + # Get the specified argument. + if isinstance(n, int): + structure = args[n] + is_arg = True + elif isinstance(n, str): + structure = kwargs[n] + is_arg = False + + # Define reduced function that is only dependent on the specified argument. + def f_reduced(x): + if hasattr(x, "__iter__"): + return list(map(f_reduced, x)) + else: + if is_arg: + args[n] = x + else: + kwargs[n] = x + return f(*args, **kwargs) + + # f_reduced will call itself recursively so that in the end f is applied to + # all basic elements. + return list(map(f_reduced, structure)) + + +def iter_copy(structure): + """ + Returns a copy of an iterable object (also copying all embedded iterables). + """ + return [iter_copy(i) if hasattr(i, "__iter__") else i for i in structure] + + +def structure_copy(structure): + """ + Returns a copy of the given structure (numpy-array, list, iterable, ..). + """ + if hasattr(structure, "copy"): + return structure.copy() + return iter_copy(structure) + + +class vectorize: + """ + Generalizes a function taking scalars to accept multidimensional arguments. + + Examples + ======== + + >>> from sympy import vectorize, diff, sin, symbols, Function + >>> x, y, z = symbols('x y z') + >>> f, g, h = list(map(Function, 'fgh')) + + >>> @vectorize(0) + ... def vsin(x): + ... return sin(x) + + >>> vsin([1, x, y]) + [sin(1), sin(x), sin(y)] + + >>> @vectorize(0, 1) + ... def vdiff(f, y): + ... return diff(f, y) + + >>> vdiff([f(x, y, z), g(x, y, z), h(x, y, z)], [x, y, z]) + [[Derivative(f(x, y, z), x), Derivative(f(x, y, z), y), Derivative(f(x, y, z), z)], [Derivative(g(x, y, z), x), Derivative(g(x, y, z), y), Derivative(g(x, y, z), z)], [Derivative(h(x, y, z), x), Derivative(h(x, y, z), y), Derivative(h(x, y, z), z)]] + """ + def __init__(self, *mdargs): + """ + The given numbers and strings characterize the arguments that will be + treated as data structures, where the decorated function will be applied + to every single element. + If no argument is given, everything is treated multidimensional. + """ + for a in mdargs: + if not isinstance(a, (int, str)): + raise TypeError("a is of invalid type") + self.mdargs = mdargs + + def __call__(self, f): + """ + Returns a wrapper for the one-dimensional function that can handle + multidimensional arguments. + """ + @wraps(f) + def wrapper(*args, **kwargs): + # Get arguments that should be treated multidimensional + if self.mdargs: + mdargs = self.mdargs + else: + mdargs = range(len(args)) + kwargs.keys() + + arglength = len(args) + + for n in mdargs: + if isinstance(n, int): + if n >= arglength: + continue + entry = args[n] + is_arg = True + elif isinstance(n, str): + try: + entry = kwargs[n] + except KeyError: + continue + is_arg = False + if hasattr(entry, "__iter__"): + # Create now a copy of the given array and manipulate then + # the entries directly. + if is_arg: + args = list(args) + args[n] = structure_copy(entry) + else: + kwargs[n] = structure_copy(entry) + result = apply_on_element(wrapper, args, kwargs, n) + return result + return f(*args, **kwargs) + return wrapper diff --git a/lib/python3.12/site-packages/sympy/core/numbers.py b/lib/python3.12/site-packages/sympy/core/numbers.py new file mode 100644 index 0000000000000000000000000000000000000000..5b23d74506d93c1560a7ba5430cd2c29db4afba7 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/core/numbers.py @@ -0,0 +1,4378 @@ +from __future__ import annotations + +import numbers +import decimal +import fractions +import math + +from .containers import Tuple +from .sympify import (SympifyError, _sympy_converter, sympify, _convert_numpy_types, + _sympify, _is_numpy_instance) +from .singleton import S, Singleton +from .basic import Basic +from .expr import Expr, AtomicExpr +from .evalf import pure_complex +from .cache import cacheit, clear_cache +from .decorators import _sympifyit +from .intfunc import num_digits, igcd, ilcm, mod_inverse, integer_nthroot +from .logic import fuzzy_not +from .kind import NumberKind +from sympy.external.gmpy import SYMPY_INTS, gmpy, flint +from sympy.multipledispatch import dispatch +import mpmath +import mpmath.libmp as mlib +from mpmath.libmp import bitcount, round_nearest as rnd +from mpmath.libmp.backend import MPZ +from mpmath.libmp import mpf_pow, mpf_pi, mpf_e, phi_fixed +from mpmath.ctx_mp_python import mpnumeric +from mpmath.libmp.libmpf import ( + finf as _mpf_inf, fninf as _mpf_ninf, + fnan as _mpf_nan, fzero, _normalize as mpf_normalize, + prec_to_dps, dps_to_prec) +from sympy.utilities.misc import debug +from .parameters import global_parameters + +_LOG2 = math.log(2) + + +def comp(z1, z2, tol=None): + r"""Return a bool indicating whether the error between z1 and z2 + is $\le$ ``tol``. + + Examples + ======== + + If ``tol`` is ``None`` then ``True`` will be returned if + :math:`|z1 - z2|\times 10^p \le 5` where $p$ is minimum value of the + decimal precision of each value. + + >>> from sympy import comp, pi + >>> pi4 = pi.n(4); pi4 + 3.142 + >>> comp(_, 3.142) + True + >>> comp(pi4, 3.141) + False + >>> comp(pi4, 3.143) + False + + A comparison of strings will be made + if ``z1`` is a Number and ``z2`` is a string or ``tol`` is ''. + + >>> comp(pi4, 3.1415) + True + >>> comp(pi4, 3.1415, '') + False + + When ``tol`` is provided and $z2$ is non-zero and + :math:`|z1| > 1` the error is normalized by :math:`|z1|`: + + >>> abs(pi4 - 3.14)/pi4 + 0.000509791731426756 + >>> comp(pi4, 3.14, .001) # difference less than 0.1% + True + >>> comp(pi4, 3.14, .0005) # difference less than 0.1% + False + + When :math:`|z1| \le 1` the absolute error is used: + + >>> 1/pi4 + 0.3183 + >>> abs(1/pi4 - 0.3183)/(1/pi4) + 3.07371499106316e-5 + >>> abs(1/pi4 - 0.3183) + 9.78393554684764e-6 + >>> comp(1/pi4, 0.3183, 1e-5) + True + + To see if the absolute error between ``z1`` and ``z2`` is less + than or equal to ``tol``, call this as ``comp(z1 - z2, 0, tol)`` + or ``comp(z1 - z2, tol=tol)``: + + >>> abs(pi4 - 3.14) + 0.00160156249999988 + >>> comp(pi4 - 3.14, 0, .002) + True + >>> comp(pi4 - 3.14, 0, .001) + False + """ + if isinstance(z2, str): + if not pure_complex(z1, or_real=True): + raise ValueError('when z2 is a str z1 must be a Number') + return str(z1) == z2 + if not z1: + z1, z2 = z2, z1 + if not z1: + return True + if not tol: + a, b = z1, z2 + if tol == '': + return str(a) == str(b) + if tol is None: + a, b = sympify(a), sympify(b) + if not all(i.is_number for i in (a, b)): + raise ValueError('expecting 2 numbers') + fa = a.atoms(Float) + fb = b.atoms(Float) + if not fa and not fb: + # no floats -- compare exactly + return a == b + # get a to be pure_complex + for _ in range(2): + ca = pure_complex(a, or_real=True) + if not ca: + if fa: + a = a.n(prec_to_dps(min(i._prec for i in fa))) + ca = pure_complex(a, or_real=True) + break + else: + fa, fb = fb, fa + a, b = b, a + cb = pure_complex(b) + if not cb and fb: + b = b.n(prec_to_dps(min(i._prec for i in fb))) + cb = pure_complex(b, or_real=True) + if ca and cb and (ca[1] or cb[1]): + return all(comp(i, j) for i, j in zip(ca, cb)) + tol = 10**prec_to_dps(min(a._prec, getattr(b, '_prec', a._prec))) + return int(abs(a - b)*tol) <= 5 + diff = abs(z1 - z2) + az1 = abs(z1) + if z2 and az1 > 1: + return diff/az1 <= tol + else: + return diff <= tol + + +def mpf_norm(mpf, prec): + """Return the mpf tuple normalized appropriately for the indicated + precision after doing a check to see if zero should be returned or + not when the mantissa is 0. ``mpf_normlize`` always assumes that this + is zero, but it may not be since the mantissa for mpf's values "+inf", + "-inf" and "nan" have a mantissa of zero, too. + + Note: this is not intended to validate a given mpf tuple, so sending + mpf tuples that were not created by mpmath may produce bad results. This + is only a wrapper to ``mpf_normalize`` which provides the check for non- + zero mpfs that have a 0 for the mantissa. + """ + sign, man, expt, bc = mpf + if not man: + # hack for mpf_normalize which does not do this; + # it assumes that if man is zero the result is 0 + # (see issue 6639) + if not bc: + return fzero + else: + # don't change anything; this should already + # be a well formed mpf tuple + return mpf + + # Necessary if mpmath is using the gmpy backend + from mpmath.libmp.backend import MPZ + rv = mpf_normalize(sign, MPZ(man), expt, bc, prec, rnd) + return rv + +# TODO: we should use the warnings module +_errdict = {"divide": False} + + +def seterr(divide=False): + """ + Should SymPy raise an exception on 0/0 or return a nan? + + divide == True .... raise an exception + divide == False ... return nan + """ + if _errdict["divide"] != divide: + clear_cache() + _errdict["divide"] = divide + + +def _as_integer_ratio(p): + neg_pow, man, expt, _ = getattr(p, '_mpf_', mpmath.mpf(p)._mpf_) + p = [1, -1][neg_pow % 2]*man + if expt < 0: + q = 2**-expt + else: + q = 1 + p *= 2**expt + return int(p), int(q) + + +def _decimal_to_Rational_prec(dec): + """Convert an ordinary decimal instance to a Rational.""" + if not dec.is_finite(): + raise TypeError("dec must be finite, got %s." % dec) + s, d, e = dec.as_tuple() + prec = len(d) + if e >= 0: # it's an integer + rv = Integer(int(dec)) + else: + s = (-1)**s + d = sum(di*10**i for i, di in enumerate(reversed(d))) + rv = Rational(s*d, 10**-e) + return rv, prec + +_dig = str.maketrans(dict.fromkeys('1234567890')) + +def _literal_float(s): + """return True if s is space-trimmed number literal else False + + Python allows underscore as digit separators: there must be a + digit on each side. So neither a leading underscore nor a + double underscore are valid as part of a number. A number does + not have to precede the decimal point, but there must be a + digit before the optional "e" or "E" that begins the signs + exponent of the number which must be an integer, perhaps with + underscore separators. + + SymPy allows space as a separator; if the calling routine replaces + them with underscores then the same semantics will be enforced + for them as for underscores: there can only be 1 *between* digits. + + We don't check for error from float(s) because we don't know + whether s is malicious or not. A regex for this could maybe + be written but will it be understood by most who read it? + """ + # mantissa and exponent + parts = s.split('e') + if len(parts) > 2: + return False + if len(parts) == 2: + m, e = parts + if e.startswith(tuple('+-')): + e = e[1:] + if not e: + return False + else: + m, e = s, '1' + # integer and fraction of mantissa + parts = m.split('.') + if len(parts) > 2: + return False + elif len(parts) == 2: + i, f = parts + else: + i, f = m, '1' + if not i and not f: + return False + if i and i[0] in '+-': + i = i[1:] + if not i: # -.3e4 -> -0.3e4 + i = '1' + f = f or '1' + # check that all groups contain only digits and are not null + for n in (i, f, e): + for g in n.split('_'): + if not g or g.translate(_dig): + return False + return True + +# (a,b) -> gcd(a,b) + +# TODO caching with decorator, but not to degrade performance + + +class Number(AtomicExpr): + """Represents atomic numbers in SymPy. + + Explanation + =========== + + Floating point numbers are represented by the Float class. + Rational numbers (of any size) are represented by the Rational class. + Integer numbers (of any size) are represented by the Integer class. + Float and Rational are subclasses of Number; Integer is a subclass + of Rational. + + For example, ``2/3`` is represented as ``Rational(2, 3)`` which is + a different object from the floating point number obtained with + Python division ``2/3``. Even for numbers that are exactly + represented in binary, there is a difference between how two forms, + such as ``Rational(1, 2)`` and ``Float(0.5)``, are used in SymPy. + The rational form is to be preferred in symbolic computations. + + Other kinds of numbers, such as algebraic numbers ``sqrt(2)`` or + complex numbers ``3 + 4*I``, are not instances of Number class as + they are not atomic. + + See Also + ======== + + Float, Integer, Rational + """ + is_commutative = True + is_number = True + is_Number = True + + __slots__ = () + + # Used to make max(x._prec, y._prec) return x._prec when only x is a float + _prec = -1 + + kind = NumberKind + + def __new__(cls, *obj): + if len(obj) == 1: + obj = obj[0] + + if isinstance(obj, Number): + return obj + if isinstance(obj, SYMPY_INTS): + return Integer(obj) + if isinstance(obj, tuple) and len(obj) == 2: + return Rational(*obj) + if isinstance(obj, (float, mpmath.mpf, decimal.Decimal)): + return Float(obj) + if isinstance(obj, str): + _obj = obj.lower() # float('INF') == float('inf') + if _obj == 'nan': + return S.NaN + elif _obj == 'inf': + return S.Infinity + elif _obj == '+inf': + return S.Infinity + elif _obj == '-inf': + return S.NegativeInfinity + val = sympify(obj) + if isinstance(val, Number): + return val + else: + raise ValueError('String "%s" does not denote a Number' % obj) + msg = "expected str|int|long|float|Decimal|Number object but got %r" + raise TypeError(msg % type(obj).__name__) + + def could_extract_minus_sign(self): + return bool(self.is_extended_negative) + + def invert(self, other, *gens, **args): + from sympy.polys.polytools import invert + if getattr(other, 'is_number', True): + return mod_inverse(self, other) + return invert(self, other, *gens, **args) + + def __divmod__(self, other): + from sympy.functions.elementary.complexes import sign + + try: + other = Number(other) + if self.is_infinite or S.NaN in (self, other): + return (S.NaN, S.NaN) + except TypeError: + return NotImplemented + if not other: + raise ZeroDivisionError('modulo by zero') + if self.is_Integer and other.is_Integer: + return Tuple(*divmod(self.p, other.p)) + elif isinstance(other, Float): + rat = self/Rational(other) + else: + rat = self/other + if other.is_finite: + w = int(rat) if rat >= 0 else int(rat) - 1 + r = self - other*w + if r == Float(other): + w += 1 + r = 0 + if isinstance(self, Float) or isinstance(other, Float): + r = Float(r) # in case w or r is 0 + else: + w = 0 if not self or (sign(self) == sign(other)) else -1 + r = other if w else self + return Tuple(w, r) + + def __rdivmod__(self, other): + try: + other = Number(other) + except TypeError: + return NotImplemented + return divmod(other, self) + + def _as_mpf_val(self, prec): + """Evaluation of mpf tuple accurate to at least prec bits.""" + raise NotImplementedError('%s needs ._as_mpf_val() method' % + (self.__class__.__name__)) + + def _eval_evalf(self, prec): + return Float._new(self._as_mpf_val(prec), prec) + + def _as_mpf_op(self, prec): + prec = max(prec, self._prec) + return self._as_mpf_val(prec), prec + + def __float__(self): + return mlib.to_float(self._as_mpf_val(53)) + + def floor(self): + raise NotImplementedError('%s needs .floor() method' % + (self.__class__.__name__)) + + def ceiling(self): + raise NotImplementedError('%s needs .ceiling() method' % + (self.__class__.__name__)) + + def __floor__(self): + return self.floor() + + def __ceil__(self): + return self.ceiling() + + def _eval_conjugate(self): + return self + + def _eval_order(self, *symbols): + from sympy.series.order import Order + # Order(5, x, y) -> Order(1,x,y) + return Order(S.One, *symbols) + + def _eval_subs(self, old, new): + if old == -self: + return -new + return self # there is no other possibility + + @classmethod + def class_key(cls): + return 1, 0, 'Number' + + @cacheit + def sort_key(self, order=None): + return self.class_key(), (0, ()), (), self + + @_sympifyit('other', NotImplemented) + def __add__(self, other): + if isinstance(other, Number) and global_parameters.evaluate: + if other is S.NaN: + return S.NaN + elif other is S.Infinity: + return S.Infinity + elif other is S.NegativeInfinity: + return S.NegativeInfinity + return AtomicExpr.__add__(self, other) + + @_sympifyit('other', NotImplemented) + def __sub__(self, other): + if isinstance(other, Number) and global_parameters.evaluate: + if other is S.NaN: + return S.NaN + elif other is S.Infinity: + return S.NegativeInfinity + elif other is S.NegativeInfinity: + return S.Infinity + return AtomicExpr.__sub__(self, other) + + @_sympifyit('other', NotImplemented) + def __mul__(self, other): + if isinstance(other, Number) and global_parameters.evaluate: + if other is S.NaN: + return S.NaN + elif other is S.Infinity: + if self.is_zero: + return S.NaN + elif self.is_positive: + return S.Infinity + else: + return S.NegativeInfinity + elif other is S.NegativeInfinity: + if self.is_zero: + return S.NaN + elif self.is_positive: + return S.NegativeInfinity + else: + return S.Infinity + elif isinstance(other, Tuple): + return NotImplemented + return AtomicExpr.__mul__(self, other) + + @_sympifyit('other', NotImplemented) + def __truediv__(self, other): + if isinstance(other, Number) and global_parameters.evaluate: + if other is S.NaN: + return S.NaN + elif other in (S.Infinity, S.NegativeInfinity): + return S.Zero + return AtomicExpr.__truediv__(self, other) + + def __eq__(self, other): + raise NotImplementedError('%s needs .__eq__() method' % + (self.__class__.__name__)) + + def __ne__(self, other): + raise NotImplementedError('%s needs .__ne__() method' % + (self.__class__.__name__)) + + def __lt__(self, other): + try: + other = _sympify(other) + except SympifyError: + raise TypeError("Invalid comparison %s < %s" % (self, other)) + raise NotImplementedError('%s needs .__lt__() method' % + (self.__class__.__name__)) + + def __le__(self, other): + try: + other = _sympify(other) + except SympifyError: + raise TypeError("Invalid comparison %s <= %s" % (self, other)) + raise NotImplementedError('%s needs .__le__() method' % + (self.__class__.__name__)) + + def __gt__(self, other): + try: + other = _sympify(other) + except SympifyError: + raise TypeError("Invalid comparison %s > %s" % (self, other)) + return _sympify(other).__lt__(self) + + def __ge__(self, other): + try: + other = _sympify(other) + except SympifyError: + raise TypeError("Invalid comparison %s >= %s" % (self, other)) + return _sympify(other).__le__(self) + + def __hash__(self): + return super().__hash__() + + def is_constant(self, *wrt, **flags): + return True + + def as_coeff_mul(self, *deps, rational=True, **kwargs): + # a -> c*t + if self.is_Rational or not rational: + return self, () + elif self.is_negative: + return S.NegativeOne, (-self,) + return S.One, (self,) + + def as_coeff_add(self, *deps): + # a -> c + t + if self.is_Rational: + return self, () + return S.Zero, (self,) + + def as_coeff_Mul(self, rational=False): + """Efficiently extract the coefficient of a product.""" + if not rational: + return self, S.One + return S.One, self + + def as_coeff_Add(self, rational=False): + """Efficiently extract the coefficient of a summation.""" + if not rational: + return self, S.Zero + return S.Zero, self + + def gcd(self, other): + """Compute GCD of `self` and `other`. """ + from sympy.polys.polytools import gcd + return gcd(self, other) + + def lcm(self, other): + """Compute LCM of `self` and `other`. """ + from sympy.polys.polytools import lcm + return lcm(self, other) + + def cofactors(self, other): + """Compute GCD and cofactors of `self` and `other`. """ + from sympy.polys.polytools import cofactors + return cofactors(self, other) + + +class Float(Number): + """Represent a floating-point number of arbitrary precision. + + Examples + ======== + + >>> from sympy import Float + >>> Float(3.5) + 3.50000000000000 + >>> Float(3) + 3.00000000000000 + + Creating Floats from strings (and Python ``int`` and ``long`` + types) will give a minimum precision of 15 digits, but the + precision will automatically increase to capture all digits + entered. + + >>> Float(1) + 1.00000000000000 + >>> Float(10**20) + 100000000000000000000. + >>> Float('1e20') + 100000000000000000000. + + However, *floating-point* numbers (Python ``float`` types) retain + only 15 digits of precision: + + >>> Float(1e20) + 1.00000000000000e+20 + >>> Float(1.23456789123456789) + 1.23456789123457 + + It may be preferable to enter high-precision decimal numbers + as strings: + + >>> Float('1.23456789123456789') + 1.23456789123456789 + + The desired number of digits can also be specified: + + >>> Float('1e-3', 3) + 0.00100 + >>> Float(100, 4) + 100.0 + + Float can automatically count significant figures if a null string + is sent for the precision; spaces or underscores are also allowed. (Auto- + counting is only allowed for strings, ints and longs). + + >>> Float('123 456 789.123_456', '') + 123456789.123456 + >>> Float('12e-3', '') + 0.012 + >>> Float(3, '') + 3. + + If a number is written in scientific notation, only the digits before the + exponent are considered significant if a decimal appears, otherwise the + "e" signifies only how to move the decimal: + + >>> Float('60.e2', '') # 2 digits significant + 6.0e+3 + >>> Float('60e2', '') # 4 digits significant + 6000. + >>> Float('600e-2', '') # 3 digits significant + 6.00 + + Notes + ===== + + Floats are inexact by their nature unless their value is a binary-exact + value. + + >>> approx, exact = Float(.1, 1), Float(.125, 1) + + For calculation purposes, evalf needs to be able to change the precision + but this will not increase the accuracy of the inexact value. The + following is the most accurate 5-digit approximation of a value of 0.1 + that had only 1 digit of precision: + + >>> approx.evalf(5) + 0.099609 + + By contrast, 0.125 is exact in binary (as it is in base 10) and so it + can be passed to Float or evalf to obtain an arbitrary precision with + matching accuracy: + + >>> Float(exact, 5) + 0.12500 + >>> exact.evalf(20) + 0.12500000000000000000 + + Trying to make a high-precision Float from a float is not disallowed, + but one must keep in mind that the *underlying float* (not the apparent + decimal value) is being obtained with high precision. For example, 0.3 + does not have a finite binary representation. The closest rational is + the fraction 5404319552844595/2**54. So if you try to obtain a Float of + 0.3 to 20 digits of precision you will not see the same thing as 0.3 + followed by 19 zeros: + + >>> Float(0.3, 20) + 0.29999999999999998890 + + If you want a 20-digit value of the decimal 0.3 (not the floating point + approximation of 0.3) you should send the 0.3 as a string. The underlying + representation is still binary but a higher precision than Python's float + is used: + + >>> Float('0.3', 20) + 0.30000000000000000000 + + Although you can increase the precision of an existing Float using Float + it will not increase the accuracy -- the underlying value is not changed: + + >>> def show(f): # binary rep of Float + ... from sympy import Mul, Pow + ... s, m, e, b = f._mpf_ + ... v = Mul(int(m), Pow(2, int(e), evaluate=False), evaluate=False) + ... print('%s at prec=%s' % (v, f._prec)) + ... + >>> t = Float('0.3', 3) + >>> show(t) + 4915/2**14 at prec=13 + >>> show(Float(t, 20)) # higher prec, not higher accuracy + 4915/2**14 at prec=70 + >>> show(Float(t, 2)) # lower prec + 307/2**10 at prec=10 + + The same thing happens when evalf is used on a Float: + + >>> show(t.evalf(20)) + 4915/2**14 at prec=70 + >>> show(t.evalf(2)) + 307/2**10 at prec=10 + + Finally, Floats can be instantiated with an mpf tuple (n, c, p) to + produce the number (-1)**n*c*2**p: + + >>> n, c, p = 1, 5, 0 + >>> (-1)**n*c*2**p + -5 + >>> Float((1, 5, 0)) + -5.00000000000000 + + An actual mpf tuple also contains the number of bits in c as the last + element of the tuple: + + >>> _._mpf_ + (1, 5, 0, 3) + + This is not needed for instantiation and is not the same thing as the + precision. The mpf tuple and the precision are two separate quantities + that Float tracks. + + In SymPy, a Float is a number that can be computed with arbitrary + precision. Although floating point 'inf' and 'nan' are not such + numbers, Float can create these numbers: + + >>> Float('-inf') + -oo + >>> _.is_Float + False + + Zero in Float only has a single value. Values are not separate for + positive and negative zeroes. + """ + __slots__ = ('_mpf_', '_prec') + + _mpf_: tuple[int, int, int, int] + + # A Float, though rational in form, does not behave like + # a rational in all Python expressions so we deal with + # exceptions (where we want to deal with the rational + # form of the Float as a rational) at the source rather + # than assigning a mathematically loaded category of 'rational' + is_rational = None + is_irrational = None + is_number = True + + is_real = True + is_extended_real = True + + is_Float = True + + _remove_non_digits = str.maketrans(dict.fromkeys("-+_.")) + + def __new__(cls, num, dps=None, precision=None): + if dps is not None and precision is not None: + raise ValueError('Both decimal and binary precision supplied. ' + 'Supply only one. ') + + if isinstance(num, str): + _num = num = num.strip() # Python ignores leading and trailing space + num = num.replace(' ', '_').lower() # Float treats spaces as digit sep; E -> e + if num.startswith('.') and len(num) > 1: + num = '0' + num + elif num.startswith('-.') and len(num) > 2: + num = '-0.' + num[2:] + elif num in ('inf', '+inf'): + return S.Infinity + elif num == '-inf': + return S.NegativeInfinity + elif num == 'nan': + return S.NaN + elif not _literal_float(num): + raise ValueError('string-float not recognized: %s' % _num) + elif isinstance(num, float) and num == 0: + num = '0' + elif isinstance(num, float) and num == float('inf'): + return S.Infinity + elif isinstance(num, float) and num == float('-inf'): + return S.NegativeInfinity + elif isinstance(num, float) and math.isnan(num): + return S.NaN + elif isinstance(num, (SYMPY_INTS, Integer)): + num = str(num) + elif num is S.Infinity: + return num + elif num is S.NegativeInfinity: + return num + elif num is S.NaN: + return num + elif _is_numpy_instance(num): # support for numpy datatypes + num = _convert_numpy_types(num) + elif isinstance(num, mpmath.mpf): + if precision is None: + if dps is None: + precision = num.context.prec + num = num._mpf_ + + if dps is None and precision is None: + dps = 15 + if isinstance(num, Float): + return num + if isinstance(num, str): + try: + Num = decimal.Decimal(num) + except decimal.InvalidOperation: + pass + else: + isint = '.' not in num + num, dps = _decimal_to_Rational_prec(Num) + if num.is_Integer and isint: + # 12e3 is shorthand for int, not float; + # 12.e3 would be the float version + dps = max(dps, num_digits(num)) + dps = max(15, dps) + precision = dps_to_prec(dps) + elif precision == '' and dps is None or precision is None and dps == '': + if not isinstance(num, str): + raise ValueError('The null string can only be used when ' + 'the number to Float is passed as a string or an integer.') + try: + Num = decimal.Decimal(num) + except decimal.InvalidOperation: + raise ValueError('string-float not recognized by Decimal: %s' % num) + else: + isint = '.' not in num + num, dps = _decimal_to_Rational_prec(Num) + if num.is_Integer and isint: + # without dec, e-notation is short for int + dps = max(dps, num_digits(num)) + precision = dps_to_prec(dps) + + # decimal precision(dps) is set and maybe binary precision(precision) + # as well.From here on binary precision is used to compute the Float. + # Hence, if supplied use binary precision else translate from decimal + # precision. + + if precision is None or precision == '': + precision = dps_to_prec(dps) + + precision = int(precision) + + if isinstance(num, float): + _mpf_ = mlib.from_float(num, precision, rnd) + elif isinstance(num, str): + _mpf_ = mlib.from_str(num, precision, rnd) + elif isinstance(num, decimal.Decimal): + if num.is_finite(): + _mpf_ = mlib.from_str(str(num), precision, rnd) + elif num.is_nan(): + return S.NaN + elif num.is_infinite(): + if num > 0: + return S.Infinity + return S.NegativeInfinity + else: + raise ValueError("unexpected decimal value %s" % str(num)) + elif isinstance(num, tuple) and len(num) in (3, 4): + if isinstance(num[1], str): + # it's a hexadecimal (coming from a pickled object) + num = list(num) + # If we're loading an object pickled in Python 2 into + # Python 3, we may need to strip a tailing 'L' because + # of a shim for int on Python 3, see issue #13470. + if num[1].endswith('L'): + num[1] = num[1][:-1] + # Strip leading '0x' - gmpy2 only documents such inputs + # with base prefix as valid when the 2nd argument (base) is 0. + # When mpmath uses Sage as the backend, however, it + # ends up including '0x' when preparing the picklable tuple. + # See issue #19690. + if num[1].startswith('0x'): + num[1] = num[1][2:] + # Now we can assume that it is in standard form + num[1] = MPZ(num[1], 16) + _mpf_ = tuple(num) + else: + if len(num) == 4: + # handle normalization hack + return Float._new(num, precision) + else: + if not all(( + num[0] in (0, 1), + num[1] >= 0, + all(type(i) in (int, int) for i in num) + )): + raise ValueError('malformed mpf: %s' % (num,)) + # don't compute number or else it may + # over/underflow + return Float._new( + (num[0], num[1], num[2], bitcount(num[1])), + precision) + elif isinstance(num, (Number, NumberSymbol)): + _mpf_ = num._as_mpf_val(precision) + else: + _mpf_ = mpmath.mpf(num, prec=precision)._mpf_ + + return cls._new(_mpf_, precision, zero=False) + + @classmethod + def _new(cls, _mpf_, _prec, zero=True): + # special cases + if zero and _mpf_ == fzero: + return S.Zero # Float(0) -> 0.0; Float._new((0,0,0,0)) -> 0 + elif _mpf_ == _mpf_nan: + return S.NaN + elif _mpf_ == _mpf_inf: + return S.Infinity + elif _mpf_ == _mpf_ninf: + return S.NegativeInfinity + + obj = Expr.__new__(cls) + obj._mpf_ = mpf_norm(_mpf_, _prec) + obj._prec = _prec + return obj + + def __getnewargs_ex__(self): + sign, man, exp, bc = self._mpf_ + arg = (sign, hex(man)[2:], exp, bc) + kwargs = {'precision': self._prec} + return ((arg,), kwargs) + + def _hashable_content(self): + return (self._mpf_, self._prec) + + def floor(self): + return Integer(int(mlib.to_int( + mlib.mpf_floor(self._mpf_, self._prec)))) + + def ceiling(self): + return Integer(int(mlib.to_int( + mlib.mpf_ceil(self._mpf_, self._prec)))) + + def __floor__(self): + return self.floor() + + def __ceil__(self): + return self.ceiling() + + @property + def num(self): + return mpmath.mpf(self._mpf_) + + def _as_mpf_val(self, prec): + rv = mpf_norm(self._mpf_, prec) + if rv != self._mpf_ and self._prec == prec: + debug(self._mpf_, rv) + return rv + + def _as_mpf_op(self, prec): + return self._mpf_, max(prec, self._prec) + + def _eval_is_finite(self): + if self._mpf_ in (_mpf_inf, _mpf_ninf): + return False + return True + + def _eval_is_infinite(self): + if self._mpf_ in (_mpf_inf, _mpf_ninf): + return True + return False + + def _eval_is_integer(self): + if self._mpf_ == fzero: + return True + if not int_valued(self): + return False + + def _eval_is_negative(self): + if self._mpf_ in (_mpf_ninf, _mpf_inf): + return False + return self.num < 0 + + def _eval_is_positive(self): + if self._mpf_ in (_mpf_ninf, _mpf_inf): + return False + return self.num > 0 + + def _eval_is_extended_negative(self): + if self._mpf_ == _mpf_ninf: + return True + if self._mpf_ == _mpf_inf: + return False + return self.num < 0 + + def _eval_is_extended_positive(self): + if self._mpf_ == _mpf_inf: + return True + if self._mpf_ == _mpf_ninf: + return False + return self.num > 0 + + def _eval_is_zero(self): + return self._mpf_ == fzero + + def __bool__(self): + return self._mpf_ != fzero + + def __neg__(self): + if not self: + return self + return Float._new(mlib.mpf_neg(self._mpf_), self._prec) + + @_sympifyit('other', NotImplemented) + def __add__(self, other): + if isinstance(other, Number) and global_parameters.evaluate: + rhs, prec = other._as_mpf_op(self._prec) + return Float._new(mlib.mpf_add(self._mpf_, rhs, prec, rnd), prec) + return Number.__add__(self, other) + + @_sympifyit('other', NotImplemented) + def __sub__(self, other): + if isinstance(other, Number) and global_parameters.evaluate: + rhs, prec = other._as_mpf_op(self._prec) + return Float._new(mlib.mpf_sub(self._mpf_, rhs, prec, rnd), prec) + return Number.__sub__(self, other) + + @_sympifyit('other', NotImplemented) + def __mul__(self, other): + if isinstance(other, Number) and global_parameters.evaluate: + rhs, prec = other._as_mpf_op(self._prec) + return Float._new(mlib.mpf_mul(self._mpf_, rhs, prec, rnd), prec) + return Number.__mul__(self, other) + + @_sympifyit('other', NotImplemented) + def __truediv__(self, other): + if isinstance(other, Number) and other != 0 and global_parameters.evaluate: + rhs, prec = other._as_mpf_op(self._prec) + return Float._new(mlib.mpf_div(self._mpf_, rhs, prec, rnd), prec) + return Number.__truediv__(self, other) + + @_sympifyit('other', NotImplemented) + def __mod__(self, other): + if isinstance(other, Rational) and other.q != 1 and global_parameters.evaluate: + # calculate mod with Rationals, *then* round the result + return Float(Rational.__mod__(Rational(self), other), + precision=self._prec) + if isinstance(other, Float) and global_parameters.evaluate: + r = self/other + if int_valued(r): + return Float(0, precision=max(self._prec, other._prec)) + if isinstance(other, Number) and global_parameters.evaluate: + rhs, prec = other._as_mpf_op(self._prec) + return Float._new(mlib.mpf_mod(self._mpf_, rhs, prec, rnd), prec) + return Number.__mod__(self, other) + + @_sympifyit('other', NotImplemented) + def __rmod__(self, other): + if isinstance(other, Float) and global_parameters.evaluate: + return other.__mod__(self) + if isinstance(other, Number) and global_parameters.evaluate: + rhs, prec = other._as_mpf_op(self._prec) + return Float._new(mlib.mpf_mod(rhs, self._mpf_, prec, rnd), prec) + return Number.__rmod__(self, other) + + def _eval_power(self, expt): + """ + expt is symbolic object but not equal to 0, 1 + + (-p)**r -> exp(r*log(-p)) -> exp(r*(log(p) + I*Pi)) -> + -> p**r*(sin(Pi*r) + cos(Pi*r)*I) + """ + if equal_valued(self, 0): + if expt.is_extended_positive: + return self + if expt.is_extended_negative: + return S.ComplexInfinity + if isinstance(expt, Number): + if isinstance(expt, Integer): + prec = self._prec + return Float._new( + mlib.mpf_pow_int(self._mpf_, expt.p, prec, rnd), prec) + elif isinstance(expt, Rational) and \ + expt.p == 1 and expt.q % 2 and self.is_negative: + return Pow(S.NegativeOne, expt, evaluate=False)*( + -self)._eval_power(expt) + expt, prec = expt._as_mpf_op(self._prec) + mpfself = self._mpf_ + try: + y = mpf_pow(mpfself, expt, prec, rnd) + return Float._new(y, prec) + except mlib.ComplexResult: + re, im = mlib.mpc_pow( + (mpfself, fzero), (expt, fzero), prec, rnd) + return Float._new(re, prec) + \ + Float._new(im, prec)*S.ImaginaryUnit + + def __abs__(self): + return Float._new(mlib.mpf_abs(self._mpf_), self._prec) + + def __int__(self): + if self._mpf_ == fzero: + return 0 + return int(mlib.to_int(self._mpf_)) # uses round_fast = round_down + + def __eq__(self, other): + if isinstance(other, float): + other = Float(other) + return Basic.__eq__(self, other) + + def __ne__(self, other): + eq = self.__eq__(other) + if eq is NotImplemented: + return eq + else: + return not eq + + def __hash__(self): + float_val = float(self) + if not math.isinf(float_val): + return hash(float_val) + return Basic.__hash__(self) + + def _Frel(self, other, op): + try: + other = _sympify(other) + except SympifyError: + return NotImplemented + if other.is_Rational: + # test self*other.q other.p without losing precision + ''' + >>> f = Float(.1,2) + >>> i = 1234567890 + >>> (f*i)._mpf_ + (0, 471, 18, 9) + >>> mlib.mpf_mul(f._mpf_, mlib.from_int(i)) + (0, 505555550955, -12, 39) + ''' + smpf = mlib.mpf_mul(self._mpf_, mlib.from_int(other.q)) + ompf = mlib.from_int(other.p) + return _sympify(bool(op(smpf, ompf))) + elif other.is_Float: + return _sympify(bool( + op(self._mpf_, other._mpf_))) + elif other.is_comparable and other not in ( + S.Infinity, S.NegativeInfinity): + other = other.evalf(prec_to_dps(self._prec)) + if other._prec > 1: + if other.is_Number: + return _sympify(bool( + op(self._mpf_, other._as_mpf_val(self._prec)))) + + def __gt__(self, other): + if isinstance(other, NumberSymbol): + return other.__lt__(self) + rv = self._Frel(other, mlib.mpf_gt) + if rv is None: + return Expr.__gt__(self, other) + return rv + + def __ge__(self, other): + if isinstance(other, NumberSymbol): + return other.__le__(self) + rv = self._Frel(other, mlib.mpf_ge) + if rv is None: + return Expr.__ge__(self, other) + return rv + + def __lt__(self, other): + if isinstance(other, NumberSymbol): + return other.__gt__(self) + rv = self._Frel(other, mlib.mpf_lt) + if rv is None: + return Expr.__lt__(self, other) + return rv + + def __le__(self, other): + if isinstance(other, NumberSymbol): + return other.__ge__(self) + rv = self._Frel(other, mlib.mpf_le) + if rv is None: + return Expr.__le__(self, other) + return rv + + def epsilon_eq(self, other, epsilon="1e-15"): + return abs(self - other) < Float(epsilon) + + def __format__(self, format_spec): + return format(decimal.Decimal(str(self)), format_spec) + + +# Add sympify converters +_sympy_converter[float] = _sympy_converter[decimal.Decimal] = Float + +# this is here to work nicely in Sage +RealNumber = Float + + +class Rational(Number): + """Represents rational numbers (p/q) of any size. + + Examples + ======== + + >>> from sympy import Rational, nsimplify, S, pi + >>> Rational(1, 2) + 1/2 + + Rational is unprejudiced in accepting input. If a float is passed, the + underlying value of the binary representation will be returned: + + >>> Rational(.5) + 1/2 + >>> Rational(.2) + 3602879701896397/18014398509481984 + + If the simpler representation of the float is desired then consider + limiting the denominator to the desired value or convert the float to + a string (which is roughly equivalent to limiting the denominator to + 10**12): + + >>> Rational(str(.2)) + 1/5 + >>> Rational(.2).limit_denominator(10**12) + 1/5 + + An arbitrarily precise Rational is obtained when a string literal is + passed: + + >>> Rational("1.23") + 123/100 + >>> Rational('1e-2') + 1/100 + >>> Rational(".1") + 1/10 + >>> Rational('1e-2/3.2') + 1/320 + + The conversion of other types of strings can be handled by + the sympify() function, and conversion of floats to expressions + or simple fractions can be handled with nsimplify: + + >>> S('.[3]') # repeating digits in brackets + 1/3 + >>> S('3**2/10') # general expressions + 9/10 + >>> nsimplify(.3) # numbers that have a simple form + 3/10 + + But if the input does not reduce to a literal Rational, an error will + be raised: + + >>> Rational(pi) + Traceback (most recent call last): + ... + TypeError: invalid input: pi + + + Low-level + --------- + + Access numerator and denominator as .p and .q: + + >>> r = Rational(3, 4) + >>> r + 3/4 + >>> r.p + 3 + >>> r.q + 4 + + Note that p and q return integers (not SymPy Integers) so some care + is needed when using them in expressions: + + >>> r.p/r.q + 0.75 + + If an unevaluated Rational is desired, ``gcd=1`` can be passed and + this will keep common divisors of the numerator and denominator + from being eliminated. It is not possible, however, to leave a + negative value in the denominator. + + >>> Rational(2, 4, gcd=1) + 2/4 + >>> Rational(2, -4, gcd=1).q + 4 + + See Also + ======== + sympy.core.sympify.sympify, sympy.simplify.simplify.nsimplify + """ + is_real = True + is_integer = False + is_rational = True + is_number = True + + __slots__ = ('p', 'q') + + p: int + q: int + + is_Rational = True + + @cacheit + def __new__(cls, p, q=None, gcd=None): + if q is None: + if isinstance(p, Rational): + return p + + if isinstance(p, SYMPY_INTS): + pass + else: + if isinstance(p, (float, Float)): + return Rational(*_as_integer_ratio(p)) + + if not isinstance(p, str): + try: + p = sympify(p) + except (SympifyError, SyntaxError): + pass # error will raise below + else: + if p.count('/') > 1: + raise TypeError('invalid input: %s' % p) + p = p.replace(' ', '') + pq = p.rsplit('/', 1) + if len(pq) == 2: + p, q = pq + fp = fractions.Fraction(p) + fq = fractions.Fraction(q) + p = fp/fq + try: + p = fractions.Fraction(p) + except ValueError: + pass # error will raise below + else: + return Rational(p.numerator, p.denominator, 1) + + if not isinstance(p, Rational): + raise TypeError('invalid input: %s' % p) + + q = 1 + gcd = 1 + Q = 1 + + if not isinstance(p, SYMPY_INTS): + p = Rational(p) + Q *= p.q + p = p.p + else: + p = int(p) + + if not isinstance(q, SYMPY_INTS): + q = Rational(q) + p *= q.q + Q *= q.p + else: + Q *= int(q) + q = Q + + # p and q are now ints + if q == 0: + if p == 0: + if _errdict["divide"]: + raise ValueError("Indeterminate 0/0") + else: + return S.NaN + return S.ComplexInfinity + if q < 0: + q = -q + p = -p + if not gcd: + gcd = igcd(abs(p), q) + if gcd > 1: + p //= gcd + q //= gcd + if q == 1: + return Integer(p) + if p == 1 and q == 2: + return S.Half + obj = Expr.__new__(cls) + obj.p = p + obj.q = q + return obj + + def limit_denominator(self, max_denominator=1000000): + """Closest Rational to self with denominator at most max_denominator. + + Examples + ======== + + >>> from sympy import Rational + >>> Rational('3.141592653589793').limit_denominator(10) + 22/7 + >>> Rational('3.141592653589793').limit_denominator(100) + 311/99 + + """ + f = fractions.Fraction(self.p, self.q) + return Rational(f.limit_denominator(fractions.Fraction(int(max_denominator)))) + + def __getnewargs__(self): + return (self.p, self.q) + + def _hashable_content(self): + return (self.p, self.q) + + def _eval_is_positive(self): + return self.p > 0 + + def _eval_is_zero(self): + return self.p == 0 + + def __neg__(self): + return Rational(-self.p, self.q) + + @_sympifyit('other', NotImplemented) + def __add__(self, other): + if global_parameters.evaluate: + if isinstance(other, Integer): + return Rational(self.p + self.q*other.p, self.q, 1) + elif isinstance(other, Rational): + #TODO: this can probably be optimized more + return Rational(self.p*other.q + self.q*other.p, self.q*other.q) + elif isinstance(other, Float): + return other + self + else: + return Number.__add__(self, other) + return Number.__add__(self, other) + __radd__ = __add__ + + @_sympifyit('other', NotImplemented) + def __sub__(self, other): + if global_parameters.evaluate: + if isinstance(other, Integer): + return Rational(self.p - self.q*other.p, self.q, 1) + elif isinstance(other, Rational): + return Rational(self.p*other.q - self.q*other.p, self.q*other.q) + elif isinstance(other, Float): + return -other + self + else: + return Number.__sub__(self, other) + return Number.__sub__(self, other) + @_sympifyit('other', NotImplemented) + def __rsub__(self, other): + if global_parameters.evaluate: + if isinstance(other, Integer): + return Rational(self.q*other.p - self.p, self.q, 1) + elif isinstance(other, Rational): + return Rational(self.q*other.p - self.p*other.q, self.q*other.q) + elif isinstance(other, Float): + return -self + other + else: + return Number.__rsub__(self, other) + return Number.__rsub__(self, other) + @_sympifyit('other', NotImplemented) + def __mul__(self, other): + if global_parameters.evaluate: + if isinstance(other, Integer): + return Rational(self.p*other.p, self.q, igcd(other.p, self.q)) + elif isinstance(other, Rational): + return Rational(self.p*other.p, self.q*other.q, igcd(self.p, other.q)*igcd(self.q, other.p)) + elif isinstance(other, Float): + return other*self + else: + return Number.__mul__(self, other) + return Number.__mul__(self, other) + __rmul__ = __mul__ + + @_sympifyit('other', NotImplemented) + def __truediv__(self, other): + if global_parameters.evaluate: + if isinstance(other, Integer): + if self.p and other.p == S.Zero: + return S.ComplexInfinity + else: + return Rational(self.p, self.q*other.p, igcd(self.p, other.p)) + elif isinstance(other, Rational): + return Rational(self.p*other.q, self.q*other.p, igcd(self.p, other.p)*igcd(self.q, other.q)) + elif isinstance(other, Float): + return self*(1/other) + else: + return Number.__truediv__(self, other) + return Number.__truediv__(self, other) + @_sympifyit('other', NotImplemented) + def __rtruediv__(self, other): + if global_parameters.evaluate: + if isinstance(other, Integer): + return Rational(other.p*self.q, self.p, igcd(self.p, other.p)) + elif isinstance(other, Rational): + return Rational(other.p*self.q, other.q*self.p, igcd(self.p, other.p)*igcd(self.q, other.q)) + elif isinstance(other, Float): + return other*(1/self) + else: + return Number.__rtruediv__(self, other) + return Number.__rtruediv__(self, other) + + @_sympifyit('other', NotImplemented) + def __mod__(self, other): + if global_parameters.evaluate: + if isinstance(other, Rational): + n = (self.p*other.q) // (other.p*self.q) + return Rational(self.p*other.q - n*other.p*self.q, self.q*other.q) + if isinstance(other, Float): + # calculate mod with Rationals, *then* round the answer + return Float(self.__mod__(Rational(other)), + precision=other._prec) + return Number.__mod__(self, other) + return Number.__mod__(self, other) + + @_sympifyit('other', NotImplemented) + def __rmod__(self, other): + if isinstance(other, Rational): + return Rational.__mod__(other, self) + return Number.__rmod__(self, other) + + def _eval_power(self, expt): + if isinstance(expt, Number): + if isinstance(expt, Float): + return self._eval_evalf(expt._prec)**expt + if expt.is_extended_negative: + # (3/4)**-2 -> (4/3)**2 + ne = -expt + if (ne is S.One): + return Rational(self.q, self.p) + if self.is_negative: + return S.NegativeOne**expt*Rational(self.q, -self.p)**ne + else: + return Rational(self.q, self.p)**ne + if expt is S.Infinity: # -oo already caught by test for negative + if self.p > self.q: + # (3/2)**oo -> oo + return S.Infinity + if self.p < -self.q: + # (-3/2)**oo -> oo + I*oo + return S.Infinity + S.Infinity*S.ImaginaryUnit + return S.Zero + if isinstance(expt, Integer): + # (4/3)**2 -> 4**2 / 3**2 + return Rational(self.p**expt.p, self.q**expt.p, 1) + if isinstance(expt, Rational): + intpart = expt.p // expt.q + if intpart: + intpart += 1 + remfracpart = intpart*expt.q - expt.p + ratfracpart = Rational(remfracpart, expt.q) + if self.p != 1: + return Integer(self.p)**expt*Integer(self.q)**ratfracpart*Rational(1, self.q**intpart, 1) + return Integer(self.q)**ratfracpart*Rational(1, self.q**intpart, 1) + else: + remfracpart = expt.q - expt.p + ratfracpart = Rational(remfracpart, expt.q) + if self.p != 1: + return Integer(self.p)**expt*Integer(self.q)**ratfracpart*Rational(1, self.q, 1) + return Integer(self.q)**ratfracpart*Rational(1, self.q, 1) + + if self.is_extended_negative and expt.is_even: + return (-self)**expt + + return + + def _as_mpf_val(self, prec): + return mlib.from_rational(self.p, self.q, prec, rnd) + + def _mpmath_(self, prec, rnd): + return mpmath.make_mpf(mlib.from_rational(self.p, self.q, prec, rnd)) + + def __abs__(self): + return Rational(abs(self.p), self.q) + + def __int__(self): + p, q = self.p, self.q + if p < 0: + return -int(-p//q) + return int(p//q) + + def floor(self): + return Integer(self.p // self.q) + + def ceiling(self): + return -Integer(-self.p // self.q) + + def __floor__(self): + return self.floor() + + def __ceil__(self): + return self.ceiling() + + def __eq__(self, other): + try: + other = _sympify(other) + except SympifyError: + return NotImplemented + if not isinstance(other, Number): + # S(0) == S.false is False + # S(0) == False is True + return False + if not self: + return not other + if other.is_NumberSymbol: + if other.is_irrational: + return False + return other.__eq__(self) + if other.is_Rational: + # a Rational is always in reduced form so will never be 2/4 + # so we can just check equivalence of args + return self.p == other.p and self.q == other.q + return False + + def __ne__(self, other): + return not self == other + + def _Rrel(self, other, attr): + # if you want self < other, pass self, other, __gt__ + try: + other = _sympify(other) + except SympifyError: + return NotImplemented + if other.is_Number: + op = None + s, o = self, other + if other.is_NumberSymbol: + op = getattr(o, attr) + elif other.is_Float: + op = getattr(o, attr) + elif other.is_Rational: + s, o = Integer(s.p*o.q), Integer(s.q*o.p) + op = getattr(o, attr) + if op: + return op(s) + if o.is_number and o.is_extended_real: + return Integer(s.p), s.q*o + + def __gt__(self, other): + rv = self._Rrel(other, '__lt__') + if rv is None: + rv = self, other + elif not isinstance(rv, tuple): + return rv + return Expr.__gt__(*rv) + + def __ge__(self, other): + rv = self._Rrel(other, '__le__') + if rv is None: + rv = self, other + elif not isinstance(rv, tuple): + return rv + return Expr.__ge__(*rv) + + def __lt__(self, other): + rv = self._Rrel(other, '__gt__') + if rv is None: + rv = self, other + elif not isinstance(rv, tuple): + return rv + return Expr.__lt__(*rv) + + def __le__(self, other): + rv = self._Rrel(other, '__ge__') + if rv is None: + rv = self, other + elif not isinstance(rv, tuple): + return rv + return Expr.__le__(*rv) + + def __hash__(self): + return super().__hash__() + + def factors(self, limit=None, use_trial=True, use_rho=False, + use_pm1=False, verbose=False, visual=False): + """A wrapper to factorint which return factors of self that are + smaller than limit (or cheap to compute). Special methods of + factoring are disabled by default so that only trial division is used. + """ + from sympy.ntheory.factor_ import factorrat + + return factorrat(self, limit=limit, use_trial=use_trial, + use_rho=use_rho, use_pm1=use_pm1, + verbose=verbose).copy() + + @property + def numerator(self): + return self.p + + @property + def denominator(self): + return self.q + + @_sympifyit('other', NotImplemented) + def gcd(self, other): + if isinstance(other, Rational): + if other == S.Zero: + return other + return Rational( + igcd(self.p, other.p), + ilcm(self.q, other.q)) + return Number.gcd(self, other) + + @_sympifyit('other', NotImplemented) + def lcm(self, other): + if isinstance(other, Rational): + return Rational( + self.p // igcd(self.p, other.p) * other.p, + igcd(self.q, other.q)) + return Number.lcm(self, other) + + def as_numer_denom(self): + return Integer(self.p), Integer(self.q) + + def as_content_primitive(self, radical=False, clear=True): + """Return the tuple (R, self/R) where R is the positive Rational + extracted from self. + + Examples + ======== + + >>> from sympy import S + >>> (S(-3)/2).as_content_primitive() + (3/2, -1) + + See docstring of Expr.as_content_primitive for more examples. + """ + + if self: + if self.is_positive: + return self, S.One + return -self, S.NegativeOne + return S.One, self + + def as_coeff_Mul(self, rational=False): + """Efficiently extract the coefficient of a product.""" + return self, S.One + + def as_coeff_Add(self, rational=False): + """Efficiently extract the coefficient of a summation.""" + return self, S.Zero + + +class Integer(Rational): + """Represents integer numbers of any size. + + Examples + ======== + + >>> from sympy import Integer + >>> Integer(3) + 3 + + If a float or a rational is passed to Integer, the fractional part + will be discarded; the effect is of rounding toward zero. + + >>> Integer(3.8) + 3 + >>> Integer(-3.8) + -3 + + A string is acceptable input if it can be parsed as an integer: + + >>> Integer("9" * 20) + 99999999999999999999 + + It is rarely needed to explicitly instantiate an Integer, because + Python integers are automatically converted to Integer when they + are used in SymPy expressions. + """ + q = 1 + is_integer = True + is_number = True + + is_Integer = True + + __slots__ = () + + def _as_mpf_val(self, prec): + return mlib.from_int(self.p, prec, rnd) + + def _mpmath_(self, prec, rnd): + return mpmath.make_mpf(self._as_mpf_val(prec)) + + @cacheit + def __new__(cls, i): + if isinstance(i, str): + i = i.replace(' ', '') + # whereas we cannot, in general, make a Rational from an + # arbitrary expression, we can make an Integer unambiguously + # (except when a non-integer expression happens to round to + # an integer). So we proceed by taking int() of the input and + # let the int routines determine whether the expression can + # be made into an int or whether an error should be raised. + try: + ival = int(i) + except TypeError: + raise TypeError( + "Argument of Integer should be of numeric type, got %s." % i) + # We only work with well-behaved integer types. This converts, for + # example, numpy.int32 instances. + if ival == 1: + return S.One + if ival == -1: + return S.NegativeOne + if ival == 0: + return S.Zero + obj = Expr.__new__(cls) + obj.p = ival + return obj + + def __getnewargs__(self): + return (self.p,) + + # Arithmetic operations are here for efficiency + def __int__(self): + return self.p + + def floor(self): + return Integer(self.p) + + def ceiling(self): + return Integer(self.p) + + def __floor__(self): + return self.floor() + + def __ceil__(self): + return self.ceiling() + + def __neg__(self): + return Integer(-self.p) + + def __abs__(self): + if self.p >= 0: + return self + else: + return Integer(-self.p) + + def __divmod__(self, other): + if isinstance(other, Integer) and global_parameters.evaluate: + return Tuple(*(divmod(self.p, other.p))) + else: + return Number.__divmod__(self, other) + + def __rdivmod__(self, other): + if isinstance(other, int) and global_parameters.evaluate: + return Tuple(*(divmod(other, self.p))) + else: + try: + other = Number(other) + except TypeError: + msg = "unsupported operand type(s) for divmod(): '%s' and '%s'" + oname = type(other).__name__ + sname = type(self).__name__ + raise TypeError(msg % (oname, sname)) + return Number.__divmod__(other, self) + + # TODO make it decorator + bytecodehacks? + def __add__(self, other): + if global_parameters.evaluate: + if isinstance(other, int): + return Integer(self.p + other) + elif isinstance(other, Integer): + return Integer(self.p + other.p) + elif isinstance(other, Rational): + return Rational(self.p*other.q + other.p, other.q, 1) + return Rational.__add__(self, other) + else: + return Add(self, other) + + def __radd__(self, other): + if global_parameters.evaluate: + if isinstance(other, int): + return Integer(other + self.p) + elif isinstance(other, Rational): + return Rational(other.p + self.p*other.q, other.q, 1) + return Rational.__radd__(self, other) + return Rational.__radd__(self, other) + + def __sub__(self, other): + if global_parameters.evaluate: + if isinstance(other, int): + return Integer(self.p - other) + elif isinstance(other, Integer): + return Integer(self.p - other.p) + elif isinstance(other, Rational): + return Rational(self.p*other.q - other.p, other.q, 1) + return Rational.__sub__(self, other) + return Rational.__sub__(self, other) + + def __rsub__(self, other): + if global_parameters.evaluate: + if isinstance(other, int): + return Integer(other - self.p) + elif isinstance(other, Rational): + return Rational(other.p - self.p*other.q, other.q, 1) + return Rational.__rsub__(self, other) + return Rational.__rsub__(self, other) + + def __mul__(self, other): + if global_parameters.evaluate: + if isinstance(other, int): + return Integer(self.p*other) + elif isinstance(other, Integer): + return Integer(self.p*other.p) + elif isinstance(other, Rational): + return Rational(self.p*other.p, other.q, igcd(self.p, other.q)) + return Rational.__mul__(self, other) + return Rational.__mul__(self, other) + + def __rmul__(self, other): + if global_parameters.evaluate: + if isinstance(other, int): + return Integer(other*self.p) + elif isinstance(other, Rational): + return Rational(other.p*self.p, other.q, igcd(self.p, other.q)) + return Rational.__rmul__(self, other) + return Rational.__rmul__(self, other) + + def __mod__(self, other): + if global_parameters.evaluate: + if isinstance(other, int): + return Integer(self.p % other) + elif isinstance(other, Integer): + return Integer(self.p % other.p) + return Rational.__mod__(self, other) + return Rational.__mod__(self, other) + + def __rmod__(self, other): + if global_parameters.evaluate: + if isinstance(other, int): + return Integer(other % self.p) + elif isinstance(other, Integer): + return Integer(other.p % self.p) + return Rational.__rmod__(self, other) + return Rational.__rmod__(self, other) + + def __eq__(self, other): + if isinstance(other, int): + return (self.p == other) + elif isinstance(other, Integer): + return (self.p == other.p) + return Rational.__eq__(self, other) + + def __ne__(self, other): + return not self == other + + def __gt__(self, other): + try: + other = _sympify(other) + except SympifyError: + return NotImplemented + if other.is_Integer: + return _sympify(self.p > other.p) + return Rational.__gt__(self, other) + + def __lt__(self, other): + try: + other = _sympify(other) + except SympifyError: + return NotImplemented + if other.is_Integer: + return _sympify(self.p < other.p) + return Rational.__lt__(self, other) + + def __ge__(self, other): + try: + other = _sympify(other) + except SympifyError: + return NotImplemented + if other.is_Integer: + return _sympify(self.p >= other.p) + return Rational.__ge__(self, other) + + def __le__(self, other): + try: + other = _sympify(other) + except SympifyError: + return NotImplemented + if other.is_Integer: + return _sympify(self.p <= other.p) + return Rational.__le__(self, other) + + def __hash__(self): + return hash(self.p) + + def __index__(self): + return self.p + + ######################################## + + def _eval_is_odd(self): + return bool(self.p % 2) + + def _eval_power(self, expt): + """ + Tries to do some simplifications on self**expt + + Returns None if no further simplifications can be done. + + Explanation + =========== + + When exponent is a fraction (so we have for example a square root), + we try to find a simpler representation by factoring the argument + up to factors of 2**15, e.g. + + - sqrt(4) becomes 2 + - sqrt(-4) becomes 2*I + - (2**(3+7)*3**(6+7))**Rational(1,7) becomes 6*18**(3/7) + + Further simplification would require a special call to factorint on + the argument which is not done here for sake of speed. + + """ + from sympy.ntheory.factor_ import perfect_power + + if expt is S.Infinity: + if self.p > S.One: + return S.Infinity + # cases -1, 0, 1 are done in their respective classes + return S.Infinity + S.ImaginaryUnit*S.Infinity + if expt is S.NegativeInfinity: + return Rational(1, self, 1)**S.Infinity + if not isinstance(expt, Number): + # simplify when expt is even + # (-2)**k --> 2**k + if self.is_negative and expt.is_even: + return (-self)**expt + if isinstance(expt, Float): + # Rational knows how to exponentiate by a Float + return super()._eval_power(expt) + if not isinstance(expt, Rational): + return + if expt is S.Half and self.is_negative: + # we extract I for this special case since everyone is doing so + return S.ImaginaryUnit*Pow(-self, expt) + if expt.is_negative: + # invert base and change sign on exponent + ne = -expt + if self.is_negative: + return S.NegativeOne**expt*Rational(1, -self, 1)**ne + else: + return Rational(1, self.p, 1)**ne + # see if base is a perfect root, sqrt(4) --> 2 + x, xexact = integer_nthroot(abs(self.p), expt.q) + if xexact: + # if it's a perfect root we've finished + result = Integer(x**abs(expt.p)) + if self.is_negative: + result *= S.NegativeOne**expt + return result + + # The following is an algorithm where we collect perfect roots + # from the factors of base. + + # if it's not an nth root, it still might be a perfect power + b_pos = int(abs(self.p)) + p = perfect_power(b_pos) + if p is not False: + dict = {p[0]: p[1]} + else: + dict = Integer(b_pos).factors(limit=2**15) + + # now process the dict of factors + out_int = 1 # integer part + out_rad = 1 # extracted radicals + sqr_int = 1 + sqr_gcd = 0 + sqr_dict = {} + for prime, exponent in dict.items(): + exponent *= expt.p + # remove multiples of expt.q: (2**12)**(1/10) -> 2*(2**2)**(1/10) + div_e, div_m = divmod(exponent, expt.q) + if div_e > 0: + out_int *= prime**div_e + if div_m > 0: + # see if the reduced exponent shares a gcd with e.q + # (2**2)**(1/10) -> 2**(1/5) + g = igcd(div_m, expt.q) + if g != 1: + out_rad *= Pow(prime, Rational(div_m//g, expt.q//g, 1)) + else: + sqr_dict[prime] = div_m + # identify gcd of remaining powers + for p, ex in sqr_dict.items(): + if sqr_gcd == 0: + sqr_gcd = ex + else: + sqr_gcd = igcd(sqr_gcd, ex) + if sqr_gcd == 1: + break + for k, v in sqr_dict.items(): + sqr_int *= k**(v//sqr_gcd) + if sqr_int == b_pos and out_int == 1 and out_rad == 1: + result = None + else: + result = out_int*out_rad*Pow(sqr_int, Rational(sqr_gcd, expt.q)) + if self.is_negative: + result *= Pow(S.NegativeOne, expt) + return result + + def _eval_is_prime(self): + from sympy.ntheory.primetest import isprime + + return isprime(self) + + def _eval_is_composite(self): + if self > 1: + return fuzzy_not(self.is_prime) + else: + return False + + def as_numer_denom(self): + return self, S.One + + @_sympifyit('other', NotImplemented) + def __floordiv__(self, other): + if not isinstance(other, Expr): + return NotImplemented + if isinstance(other, Integer): + return Integer(self.p // other) + return divmod(self, other)[0] + + def __rfloordiv__(self, other): + return Integer(Integer(other).p // self.p) + + # These bitwise operations (__lshift__, __rlshift__, ..., __invert__) are defined + # for Integer only and not for general SymPy expressions. This is to achieve + # compatibility with the numbers.Integral ABC which only defines these operations + # among instances of numbers.Integral. Therefore, these methods check explicitly for + # integer types rather than using sympify because they should not accept arbitrary + # symbolic expressions and there is no symbolic analogue of numbers.Integral's + # bitwise operations. + def __lshift__(self, other): + if isinstance(other, (int, Integer, numbers.Integral)): + return Integer(self.p << int(other)) + else: + return NotImplemented + + def __rlshift__(self, other): + if isinstance(other, (int, numbers.Integral)): + return Integer(int(other) << self.p) + else: + return NotImplemented + + def __rshift__(self, other): + if isinstance(other, (int, Integer, numbers.Integral)): + return Integer(self.p >> int(other)) + else: + return NotImplemented + + def __rrshift__(self, other): + if isinstance(other, (int, numbers.Integral)): + return Integer(int(other) >> self.p) + else: + return NotImplemented + + def __and__(self, other): + if isinstance(other, (int, Integer, numbers.Integral)): + return Integer(self.p & int(other)) + else: + return NotImplemented + + def __rand__(self, other): + if isinstance(other, (int, numbers.Integral)): + return Integer(int(other) & self.p) + else: + return NotImplemented + + def __xor__(self, other): + if isinstance(other, (int, Integer, numbers.Integral)): + return Integer(self.p ^ int(other)) + else: + return NotImplemented + + def __rxor__(self, other): + if isinstance(other, (int, numbers.Integral)): + return Integer(int(other) ^ self.p) + else: + return NotImplemented + + def __or__(self, other): + if isinstance(other, (int, Integer, numbers.Integral)): + return Integer(self.p | int(other)) + else: + return NotImplemented + + def __ror__(self, other): + if isinstance(other, (int, numbers.Integral)): + return Integer(int(other) | self.p) + else: + return NotImplemented + + def __invert__(self): + return Integer(~self.p) + +# Add sympify converters +_sympy_converter[int] = Integer + + +class AlgebraicNumber(Expr): + r""" + Class for representing algebraic numbers in SymPy. + + Symbolically, an instance of this class represents an element + $\alpha \in \mathbb{Q}(\theta) \hookrightarrow \mathbb{C}$. That is, the + algebraic number $\alpha$ is represented as an element of a particular + number field $\mathbb{Q}(\theta)$, with a particular embedding of this + field into the complex numbers. + + Formally, the primitive element $\theta$ is given by two data points: (1) + its minimal polynomial (which defines $\mathbb{Q}(\theta)$), and (2) a + particular complex number that is a root of this polynomial (which defines + the embedding $\mathbb{Q}(\theta) \hookrightarrow \mathbb{C}$). Finally, + the algebraic number $\alpha$ which we represent is then given by the + coefficients of a polynomial in $\theta$. + """ + + __slots__ = ('rep', 'root', 'alias', 'minpoly', '_own_minpoly') + + is_AlgebraicNumber = True + is_algebraic = True + is_number = True + + + kind = NumberKind + + # Optional alias symbol is not free. + # Actually, alias should be a Str, but some methods + # expect that it be an instance of Expr. + free_symbols: set[Basic] = set() + + def __new__(cls, expr, coeffs=None, alias=None, **args): + r""" + Construct a new algebraic number $\alpha$ belonging to a number field + $k = \mathbb{Q}(\theta)$. + + There are four instance attributes to be determined: + + =========== ============================================================================ + Attribute Type/Meaning + =========== ============================================================================ + ``root`` :py:class:`~.Expr` for $\theta$ as a complex number + ``minpoly`` :py:class:`~.Poly`, the minimal polynomial of $\theta$ + ``rep`` :py:class:`~sympy.polys.polyclasses.DMP` giving $\alpha$ as poly in $\theta$ + ``alias`` :py:class:`~.Symbol` for $\theta$, or ``None`` + =========== ============================================================================ + + See Parameters section for how they are determined. + + Parameters + ========== + + expr : :py:class:`~.Expr`, or pair $(m, r)$ + There are three distinct modes of construction, depending on what + is passed as *expr*. + + **(1)** *expr* is an :py:class:`~.AlgebraicNumber`: + In this case we begin by copying all four instance attributes from + *expr*. If *coeffs* were also given, we compose the two coeff + polynomials (see below). If an *alias* was given, it overrides. + + **(2)** *expr* is any other type of :py:class:`~.Expr`: + Then ``root`` will equal *expr*. Therefore it + must express an algebraic quantity, and we will compute its + ``minpoly``. + + **(3)** *expr* is an ordered pair $(m, r)$ giving the + ``minpoly`` $m$, and a ``root`` $r$ thereof, which together + define $\theta$. In this case $m$ may be either a univariate + :py:class:`~.Poly` or any :py:class:`~.Expr` which represents the + same, while $r$ must be some :py:class:`~.Expr` representing a + complex number that is a root of $m$, including both explicit + expressions in radicals, and instances of + :py:class:`~.ComplexRootOf` or :py:class:`~.AlgebraicNumber`. + + coeffs : list, :py:class:`~.ANP`, None, optional (default=None) + This defines ``rep``, giving the algebraic number $\alpha$ as a + polynomial in $\theta$. + + If a list, the elements should be integers or rational numbers. + If an :py:class:`~.ANP`, we take its coefficients (using its + :py:meth:`~.ANP.to_list()` method). If ``None``, then the list of + coefficients defaults to ``[1, 0]``, meaning that $\alpha = \theta$ + is the primitive element of the field. + + If *expr* was an :py:class:`~.AlgebraicNumber`, let $g(x)$ be its + ``rep`` polynomial, and let $f(x)$ be the polynomial defined by + *coeffs*. Then ``self.rep`` will represent the composition + $(f \circ g)(x)$. + + alias : str, :py:class:`~.Symbol`, None, optional (default=None) + This is a way to provide a name for the primitive element. We + described several ways in which the *expr* argument can define the + value of the primitive element, but none of these methods gave it + a name. Here, for example, *alias* could be set as + ``Symbol('theta')``, in order to make this symbol appear when + $\alpha$ is printed, or rendered as a polynomial, using the + :py:meth:`~.as_poly()` method. + + Examples + ======== + + Recall that we are constructing an algebraic number as a field element + $\alpha \in \mathbb{Q}(\theta)$. + + >>> from sympy import AlgebraicNumber, sqrt, CRootOf, S + >>> from sympy.abc import x + + Example (1): $\alpha = \theta = \sqrt{2}$ + + >>> a1 = AlgebraicNumber(sqrt(2)) + >>> a1.minpoly_of_element().as_expr(x) + x**2 - 2 + >>> a1.evalf(10) + 1.414213562 + + Example (2): $\alpha = 3 \sqrt{2} - 5$, $\theta = \sqrt{2}$. We can + either build on the last example: + + >>> a2 = AlgebraicNumber(a1, [3, -5]) + >>> a2.as_expr() + -5 + 3*sqrt(2) + + or start from scratch: + + >>> a2 = AlgebraicNumber(sqrt(2), [3, -5]) + >>> a2.as_expr() + -5 + 3*sqrt(2) + + Example (3): $\alpha = 6 \sqrt{2} - 11$, $\theta = \sqrt{2}$. Again we + can build on the previous example, and we see that the coeff polys are + composed: + + >>> a3 = AlgebraicNumber(a2, [2, -1]) + >>> a3.as_expr() + -11 + 6*sqrt(2) + + reflecting the fact that $(2x - 1) \circ (3x - 5) = 6x - 11$. + + Example (4): $\alpha = \sqrt{2}$, $\theta = \sqrt{2} + \sqrt{3}$. The + easiest way is to use the :py:func:`~.to_number_field()` function: + + >>> from sympy import to_number_field + >>> a4 = to_number_field(sqrt(2), sqrt(2) + sqrt(3)) + >>> a4.minpoly_of_element().as_expr(x) + x**2 - 2 + >>> a4.to_root() + sqrt(2) + >>> a4.primitive_element() + sqrt(2) + sqrt(3) + >>> a4.coeffs() + [1/2, 0, -9/2, 0] + + but if you already knew the right coefficients, you could construct it + directly: + + >>> a4 = AlgebraicNumber(sqrt(2) + sqrt(3), [S(1)/2, 0, S(-9)/2, 0]) + >>> a4.to_root() + sqrt(2) + >>> a4.primitive_element() + sqrt(2) + sqrt(3) + + Example (5): Construct the Golden Ratio as an element of the 5th + cyclotomic field, supposing we already know its coefficients. This time + we introduce the alias $\zeta$ for the primitive element of the field: + + >>> from sympy import cyclotomic_poly + >>> from sympy.abc import zeta + >>> a5 = AlgebraicNumber(CRootOf(cyclotomic_poly(5), -1), + ... [-1, -1, 0, 0], alias=zeta) + >>> a5.as_poly().as_expr() + -zeta**3 - zeta**2 + >>> a5.evalf() + 1.61803398874989 + + (The index ``-1`` to ``CRootOf`` selects the complex root with the + largest real and imaginary parts, which in this case is + $\mathrm{e}^{2i\pi/5}$. See :py:class:`~.ComplexRootOf`.) + + Example (6): Building on the last example, construct the number + $2 \phi \in \mathbb{Q}(\phi)$, where $\phi$ is the Golden Ratio: + + >>> from sympy.abc import phi + >>> a6 = AlgebraicNumber(a5.to_root(), coeffs=[2, 0], alias=phi) + >>> a6.as_poly().as_expr() + 2*phi + >>> a6.primitive_element().evalf() + 1.61803398874989 + + Note that we needed to use ``a5.to_root()``, since passing ``a5`` as + the first argument would have constructed the number $2 \phi$ as an + element of the field $\mathbb{Q}(\zeta)$: + + >>> a6_wrong = AlgebraicNumber(a5, coeffs=[2, 0]) + >>> a6_wrong.as_poly().as_expr() + -2*zeta**3 - 2*zeta**2 + >>> a6_wrong.primitive_element().evalf() + 0.309016994374947 + 0.951056516295154*I + + """ + from sympy.polys.polyclasses import ANP, DMP + from sympy.polys.numberfields import minimal_polynomial + + expr = sympify(expr) + rep0 = None + alias0 = None + + if isinstance(expr, (tuple, Tuple)): + minpoly, root = expr + + if not minpoly.is_Poly: + from sympy.polys.polytools import Poly + minpoly = Poly(minpoly) + elif expr.is_AlgebraicNumber: + minpoly, root, rep0, alias0 = (expr.minpoly, expr.root, + expr.rep, expr.alias) + else: + minpoly, root = minimal_polynomial( + expr, args.get('gen'), polys=True), expr + + dom = minpoly.get_domain() + + if coeffs is not None: + if not isinstance(coeffs, ANP): + rep = DMP.from_sympy_list(sympify(coeffs), 0, dom) + scoeffs = Tuple(*coeffs) + else: + rep = DMP.from_list(coeffs.to_list(), 0, dom) + scoeffs = Tuple(*coeffs.to_list()) + + else: + rep = DMP.from_list([1, 0], 0, dom) + scoeffs = Tuple(1, 0) + + if rep0 is not None: + from sympy.polys.densetools import dup_compose + c = dup_compose(rep.to_list(), rep0.to_list(), dom) + rep = DMP.from_list(c, 0, dom) + scoeffs = Tuple(*c) + + if rep.degree() >= minpoly.degree(): + rep = rep.rem(minpoly.rep) + + sargs = (root, scoeffs) + + alias = alias or alias0 + if alias is not None: + from .symbol import Symbol + if not isinstance(alias, Symbol): + alias = Symbol(alias) + sargs = sargs + (alias,) + + obj = Expr.__new__(cls, *sargs) + + obj.rep = rep + obj.root = root + obj.alias = alias + obj.minpoly = minpoly + + obj._own_minpoly = None + + return obj + + def __hash__(self): + return super().__hash__() + + def _eval_evalf(self, prec): + return self.as_expr()._evalf(prec) + + @property + def is_aliased(self): + """Returns ``True`` if ``alias`` was set. """ + return self.alias is not None + + def as_poly(self, x=None): + """Create a Poly instance from ``self``. """ + from sympy.polys.polytools import Poly, PurePoly + if x is not None: + return Poly.new(self.rep, x) + else: + if self.alias is not None: + return Poly.new(self.rep, self.alias) + else: + from .symbol import Dummy + return PurePoly.new(self.rep, Dummy('x')) + + def as_expr(self, x=None): + """Create a Basic expression from ``self``. """ + return self.as_poly(x or self.root).as_expr().expand() + + def coeffs(self): + """Returns all SymPy coefficients of an algebraic number. """ + return [ self.rep.dom.to_sympy(c) for c in self.rep.all_coeffs() ] + + def native_coeffs(self): + """Returns all native coefficients of an algebraic number. """ + return self.rep.all_coeffs() + + def to_algebraic_integer(self): + """Convert ``self`` to an algebraic integer. """ + from sympy.polys.polytools import Poly + + f = self.minpoly + + if f.LC() == 1: + return self + + coeff = f.LC()**(f.degree() - 1) + poly = f.compose(Poly(f.gen/f.LC())) + + minpoly = poly*coeff + root = f.LC()*self.root + + return AlgebraicNumber((minpoly, root), self.coeffs()) + + def _eval_simplify(self, **kwargs): + from sympy.polys.rootoftools import CRootOf + from sympy.polys import minpoly + measure, ratio = kwargs['measure'], kwargs['ratio'] + for r in [r for r in self.minpoly.all_roots() if r.func != CRootOf]: + if minpoly(self.root - r).is_Symbol: + # use the matching root if it's simpler + if measure(r) < ratio*measure(self.root): + return AlgebraicNumber(r) + return self + + def field_element(self, coeffs): + r""" + Form another element of the same number field. + + Explanation + =========== + + If we represent $\alpha \in \mathbb{Q}(\theta)$, form another element + $\beta \in \mathbb{Q}(\theta)$ of the same number field. + + Parameters + ========== + + coeffs : list, :py:class:`~.ANP` + Like the *coeffs* arg to the class + :py:meth:`constructor<.AlgebraicNumber.__new__>`, defines the + new element as a polynomial in the primitive element. + + If a list, the elements should be integers or rational numbers. + If an :py:class:`~.ANP`, we take its coefficients (using its + :py:meth:`~.ANP.to_list()` method). + + Examples + ======== + + >>> from sympy import AlgebraicNumber, sqrt + >>> a = AlgebraicNumber(sqrt(5), [-1, 1]) + >>> b = a.field_element([3, 2]) + >>> print(a) + 1 - sqrt(5) + >>> print(b) + 2 + 3*sqrt(5) + >>> print(b.primitive_element() == a.primitive_element()) + True + + See Also + ======== + + AlgebraicNumber + """ + return AlgebraicNumber( + (self.minpoly, self.root), coeffs=coeffs, alias=self.alias) + + @property + def is_primitive_element(self): + r""" + Say whether this algebraic number $\alpha \in \mathbb{Q}(\theta)$ is + equal to the primitive element $\theta$ for its field. + """ + c = self.coeffs() + # Second case occurs if self.minpoly is linear: + return c == [1, 0] or c == [self.root] + + def primitive_element(self): + r""" + Get the primitive element $\theta$ for the number field + $\mathbb{Q}(\theta)$ to which this algebraic number $\alpha$ belongs. + + Returns + ======= + + AlgebraicNumber + + """ + if self.is_primitive_element: + return self + return self.field_element([1, 0]) + + def to_primitive_element(self, radicals=True): + r""" + Convert ``self`` to an :py:class:`~.AlgebraicNumber` instance that is + equal to its own primitive element. + + Explanation + =========== + + If we represent $\alpha \in \mathbb{Q}(\theta)$, $\alpha \neq \theta$, + construct a new :py:class:`~.AlgebraicNumber` that represents + $\alpha \in \mathbb{Q}(\alpha)$. + + Examples + ======== + + >>> from sympy import sqrt, to_number_field + >>> from sympy.abc import x + >>> a = to_number_field(sqrt(2), sqrt(2) + sqrt(3)) + + The :py:class:`~.AlgebraicNumber` ``a`` represents the number + $\sqrt{2}$ in the field $\mathbb{Q}(\sqrt{2} + \sqrt{3})$. Rendering + ``a`` as a polynomial, + + >>> a.as_poly().as_expr(x) + x**3/2 - 9*x/2 + + reflects the fact that $\sqrt{2} = \theta^3/2 - 9 \theta/2$, where + $\theta = \sqrt{2} + \sqrt{3}$. + + ``a`` is not equal to its own primitive element. Its minpoly + + >>> a.minpoly.as_poly().as_expr(x) + x**4 - 10*x**2 + 1 + + is that of $\theta$. + + Converting to a primitive element, + + >>> a_prim = a.to_primitive_element() + >>> a_prim.minpoly.as_poly().as_expr(x) + x**2 - 2 + + we obtain an :py:class:`~.AlgebraicNumber` whose ``minpoly`` is that of + the number itself. + + Parameters + ========== + + radicals : boolean, optional (default=True) + If ``True``, then we will try to return an + :py:class:`~.AlgebraicNumber` whose ``root`` is an expression + in radicals. If that is not possible (or if *radicals* is + ``False``), ``root`` will be a :py:class:`~.ComplexRootOf`. + + Returns + ======= + + AlgebraicNumber + + See Also + ======== + + is_primitive_element + + """ + if self.is_primitive_element: + return self + m = self.minpoly_of_element() + r = self.to_root(radicals=radicals) + return AlgebraicNumber((m, r)) + + def minpoly_of_element(self): + r""" + Compute the minimal polynomial for this algebraic number. + + Explanation + =========== + + Recall that we represent an element $\alpha \in \mathbb{Q}(\theta)$. + Our instance attribute ``self.minpoly`` is the minimal polynomial for + our primitive element $\theta$. This method computes the minimal + polynomial for $\alpha$. + + """ + if self._own_minpoly is None: + if self.is_primitive_element: + self._own_minpoly = self.minpoly + else: + from sympy.polys.numberfields.minpoly import minpoly + theta = self.primitive_element() + self._own_minpoly = minpoly(self.as_expr(theta), polys=True) + return self._own_minpoly + + def to_root(self, radicals=True, minpoly=None): + """ + Convert to an :py:class:`~.Expr` that is not an + :py:class:`~.AlgebraicNumber`, specifically, either a + :py:class:`~.ComplexRootOf`, or, optionally and where possible, an + expression in radicals. + + Parameters + ========== + + radicals : boolean, optional (default=True) + If ``True``, then we will try to return the root as an expression + in radicals. If that is not possible, we will return a + :py:class:`~.ComplexRootOf`. + + minpoly : :py:class:`~.Poly` + If the minimal polynomial for `self` has been pre-computed, it can + be passed in order to save time. + + """ + if self.is_primitive_element and not isinstance(self.root, AlgebraicNumber): + return self.root + m = minpoly or self.minpoly_of_element() + roots = m.all_roots(radicals=radicals) + if len(roots) == 1: + return roots[0] + ex = self.as_expr() + for b in roots: + if m.same_root(b, ex): + return b + + +class RationalConstant(Rational): + """ + Abstract base class for rationals with specific behaviors + + Derived classes must define class attributes p and q and should probably all + be singletons. + """ + __slots__ = () + + def __new__(cls): + return AtomicExpr.__new__(cls) + + +class IntegerConstant(Integer): + __slots__ = () + + def __new__(cls): + return AtomicExpr.__new__(cls) + + +class Zero(IntegerConstant, metaclass=Singleton): + """The number zero. + + Zero is a singleton, and can be accessed by ``S.Zero`` + + Examples + ======== + + >>> from sympy import S, Integer + >>> Integer(0) is S.Zero + True + >>> 1/S.Zero + zoo + + References + ========== + + .. [1] https://en.wikipedia.org/wiki/Zero + """ + + p = 0 + q = 1 + is_positive = False + is_negative = False + is_zero = True + is_number = True + is_comparable = True + + __slots__ = () + + def __getnewargs__(self): + return () + + @staticmethod + def __abs__(): + return S.Zero + + @staticmethod + def __neg__(): + return S.Zero + + def _eval_power(self, expt): + if expt.is_extended_positive: + return self + if expt.is_extended_negative: + return S.ComplexInfinity + if expt.is_extended_real is False: + return S.NaN + if expt.is_zero: + return S.One + + # infinities are already handled with pos and neg + # tests above; now throw away leading numbers on Mul + # exponent since 0**-x = zoo**x even when x == 0 + coeff, terms = expt.as_coeff_Mul() + if coeff.is_negative: + return S.ComplexInfinity**terms + if coeff is not S.One: # there is a Number to discard + return self**terms + + def _eval_order(self, *symbols): + # Order(0,x) -> 0 + return self + + def __bool__(self): + return False + + +class One(IntegerConstant, metaclass=Singleton): + """The number one. + + One is a singleton, and can be accessed by ``S.One``. + + Examples + ======== + + >>> from sympy import S, Integer + >>> Integer(1) is S.One + True + + References + ========== + + .. [1] https://en.wikipedia.org/wiki/1_%28number%29 + """ + is_number = True + is_positive = True + + p = 1 + q = 1 + + __slots__ = () + + def __getnewargs__(self): + return () + + @staticmethod + def __abs__(): + return S.One + + @staticmethod + def __neg__(): + return S.NegativeOne + + def _eval_power(self, expt): + return self + + def _eval_order(self, *symbols): + return + + @staticmethod + def factors(limit=None, use_trial=True, use_rho=False, use_pm1=False, + verbose=False, visual=False): + if visual: + return S.One + else: + return {} + + +class NegativeOne(IntegerConstant, metaclass=Singleton): + """The number negative one. + + NegativeOne is a singleton, and can be accessed by ``S.NegativeOne``. + + Examples + ======== + + >>> from sympy import S, Integer + >>> Integer(-1) is S.NegativeOne + True + + See Also + ======== + + One + + References + ========== + + .. [1] https://en.wikipedia.org/wiki/%E2%88%921_%28number%29 + + """ + is_number = True + + p = -1 + q = 1 + + __slots__ = () + + def __getnewargs__(self): + return () + + @staticmethod + def __abs__(): + return S.One + + @staticmethod + def __neg__(): + return S.One + + def _eval_power(self, expt): + if expt.is_odd: + return S.NegativeOne + if expt.is_even: + return S.One + if isinstance(expt, Number): + if isinstance(expt, Float): + return Float(-1.0)**expt + if expt is S.NaN: + return S.NaN + if expt in (S.Infinity, S.NegativeInfinity): + return S.NaN + if expt is S.Half: + return S.ImaginaryUnit + if isinstance(expt, Rational): + if expt.q == 2: + return S.ImaginaryUnit**Integer(expt.p) + i, r = divmod(expt.p, expt.q) + if i: + return self**i*self**Rational(r, expt.q) + return + + +class Half(RationalConstant, metaclass=Singleton): + """The rational number 1/2. + + Half is a singleton, and can be accessed by ``S.Half``. + + Examples + ======== + + >>> from sympy import S, Rational + >>> Rational(1, 2) is S.Half + True + + References + ========== + + .. [1] https://en.wikipedia.org/wiki/One_half + """ + is_number = True + + p = 1 + q = 2 + + __slots__ = () + + def __getnewargs__(self): + return () + + @staticmethod + def __abs__(): + return S.Half + + +class Infinity(Number, metaclass=Singleton): + r"""Positive infinite quantity. + + Explanation + =========== + + In real analysis the symbol `\infty` denotes an unbounded + limit: `x\to\infty` means that `x` grows without bound. + + Infinity is often used not only to define a limit but as a value + in the affinely extended real number system. Points labeled `+\infty` + and `-\infty` can be added to the topological space of the real numbers, + producing the two-point compactification of the real numbers. Adding + algebraic properties to this gives us the extended real numbers. + + Infinity is a singleton, and can be accessed by ``S.Infinity``, + or can be imported as ``oo``. + + Examples + ======== + + >>> from sympy import oo, exp, limit, Symbol + >>> 1 + oo + oo + >>> 42/oo + 0 + >>> x = Symbol('x') + >>> limit(exp(x), x, oo) + oo + + See Also + ======== + + NegativeInfinity, NaN + + References + ========== + + .. [1] https://en.wikipedia.org/wiki/Infinity + """ + + is_commutative = True + is_number = True + is_complex = False + is_extended_real = True + is_infinite = True + is_comparable = True + is_extended_positive = True + is_prime = False + + __slots__ = () + + def __new__(cls): + return AtomicExpr.__new__(cls) + + def _latex(self, printer): + return r"\infty" + + def _eval_subs(self, old, new): + if self == old: + return new + + def _eval_evalf(self, prec=None): + return Float('inf') + + def evalf(self, prec=None, **options): + return self._eval_evalf(prec) + + @_sympifyit('other', NotImplemented) + def __add__(self, other): + if isinstance(other, Number) and global_parameters.evaluate: + if other in (S.NegativeInfinity, S.NaN): + return S.NaN + return self + return Number.__add__(self, other) + __radd__ = __add__ + + @_sympifyit('other', NotImplemented) + def __sub__(self, other): + if isinstance(other, Number) and global_parameters.evaluate: + if other in (S.Infinity, S.NaN): + return S.NaN + return self + return Number.__sub__(self, other) + + @_sympifyit('other', NotImplemented) + def __rsub__(self, other): + return (-self).__add__(other) + + @_sympifyit('other', NotImplemented) + def __mul__(self, other): + if isinstance(other, Number) and global_parameters.evaluate: + if other.is_zero or other is S.NaN: + return S.NaN + if other.is_extended_positive: + return self + return S.NegativeInfinity + return Number.__mul__(self, other) + __rmul__ = __mul__ + + @_sympifyit('other', NotImplemented) + def __truediv__(self, other): + if isinstance(other, Number) and global_parameters.evaluate: + if other is S.Infinity or \ + other is S.NegativeInfinity or \ + other is S.NaN: + return S.NaN + if other.is_extended_nonnegative: + return self + return S.NegativeInfinity + return Number.__truediv__(self, other) + + def __abs__(self): + return S.Infinity + + def __neg__(self): + return S.NegativeInfinity + + def _eval_power(self, expt): + """ + ``expt`` is symbolic object but not equal to 0 or 1. + + ================ ======= ============================== + Expression Result Notes + ================ ======= ============================== + ``oo ** nan`` ``nan`` + ``oo ** -p`` ``0`` ``p`` is number, ``oo`` + ================ ======= ============================== + + See Also + ======== + Pow + NaN + NegativeInfinity + + """ + if expt.is_extended_positive: + return S.Infinity + if expt.is_extended_negative: + return S.Zero + if expt is S.NaN: + return S.NaN + if expt is S.ComplexInfinity: + return S.NaN + if expt.is_extended_real is False and expt.is_number: + from sympy.functions.elementary.complexes import re + expt_real = re(expt) + if expt_real.is_positive: + return S.ComplexInfinity + if expt_real.is_negative: + return S.Zero + if expt_real.is_zero: + return S.NaN + + return self**expt.evalf() + + def _as_mpf_val(self, prec): + return mlib.finf + + def __hash__(self): + return super().__hash__() + + def __eq__(self, other): + return other is S.Infinity or other == float('inf') + + def __ne__(self, other): + return other is not S.Infinity and other != float('inf') + + __gt__ = Expr.__gt__ + __ge__ = Expr.__ge__ + __lt__ = Expr.__lt__ + __le__ = Expr.__le__ + + @_sympifyit('other', NotImplemented) + def __mod__(self, other): + if not isinstance(other, Expr): + return NotImplemented + return S.NaN + + __rmod__ = __mod__ + + def floor(self): + return self + + def ceiling(self): + return self + +oo = S.Infinity + + +class NegativeInfinity(Number, metaclass=Singleton): + """Negative infinite quantity. + + NegativeInfinity is a singleton, and can be accessed + by ``S.NegativeInfinity``. + + See Also + ======== + + Infinity + """ + + is_extended_real = True + is_complex = False + is_commutative = True + is_infinite = True + is_comparable = True + is_extended_negative = True + is_number = True + is_prime = False + + __slots__ = () + + def __new__(cls): + return AtomicExpr.__new__(cls) + + def _latex(self, printer): + return r"-\infty" + + def _eval_subs(self, old, new): + if self == old: + return new + + def _eval_evalf(self, prec=None): + return Float('-inf') + + def evalf(self, prec=None, **options): + return self._eval_evalf(prec) + + @_sympifyit('other', NotImplemented) + def __add__(self, other): + if isinstance(other, Number) and global_parameters.evaluate: + if other in (S.Infinity, S.NaN): + return S.NaN + return self + return Number.__add__(self, other) + __radd__ = __add__ + + @_sympifyit('other', NotImplemented) + def __sub__(self, other): + if isinstance(other, Number) and global_parameters.evaluate: + if other in (S.NegativeInfinity, S.NaN): + return S.NaN + return self + return Number.__sub__(self, other) + + @_sympifyit('other', NotImplemented) + def __rsub__(self, other): + return (-self).__add__(other) + + @_sympifyit('other', NotImplemented) + def __mul__(self, other): + if isinstance(other, Number) and global_parameters.evaluate: + if other.is_zero or other is S.NaN: + return S.NaN + if other.is_extended_positive: + return self + return S.Infinity + return Number.__mul__(self, other) + __rmul__ = __mul__ + + @_sympifyit('other', NotImplemented) + def __truediv__(self, other): + if isinstance(other, Number) and global_parameters.evaluate: + if other is S.Infinity or \ + other is S.NegativeInfinity or \ + other is S.NaN: + return S.NaN + if other.is_extended_nonnegative: + return self + return S.Infinity + return Number.__truediv__(self, other) + + def __abs__(self): + return S.Infinity + + def __neg__(self): + return S.Infinity + + def _eval_power(self, expt): + """ + ``expt`` is symbolic object but not equal to 0 or 1. + + ================ ======= ============================== + Expression Result Notes + ================ ======= ============================== + ``(-oo) ** nan`` ``nan`` + ``(-oo) ** oo`` ``nan`` + ``(-oo) ** -oo`` ``nan`` + ``(-oo) ** e`` ``oo`` ``e`` is positive even integer + ``(-oo) ** o`` ``-oo`` ``o`` is positive odd integer + ================ ======= ============================== + + See Also + ======== + + Infinity + Pow + NaN + + """ + if expt.is_number: + if expt is S.NaN or \ + expt is S.Infinity or \ + expt is S.NegativeInfinity: + return S.NaN + + if isinstance(expt, Integer) and expt.is_extended_positive: + if expt.is_odd: + return S.NegativeInfinity + else: + return S.Infinity + + inf_part = S.Infinity**expt + s_part = S.NegativeOne**expt + if inf_part == 0 and s_part.is_finite: + return inf_part + if (inf_part is S.ComplexInfinity and + s_part.is_finite and not s_part.is_zero): + return S.ComplexInfinity + return s_part*inf_part + + def _as_mpf_val(self, prec): + return mlib.fninf + + def __hash__(self): + return super().__hash__() + + def __eq__(self, other): + return other is S.NegativeInfinity or other == float('-inf') + + def __ne__(self, other): + return other is not S.NegativeInfinity and other != float('-inf') + + __gt__ = Expr.__gt__ + __ge__ = Expr.__ge__ + __lt__ = Expr.__lt__ + __le__ = Expr.__le__ + + @_sympifyit('other', NotImplemented) + def __mod__(self, other): + if not isinstance(other, Expr): + return NotImplemented + return S.NaN + + __rmod__ = __mod__ + + def floor(self): + return self + + def ceiling(self): + return self + + def as_powers_dict(self): + return {S.NegativeOne: 1, S.Infinity: 1} + + +class NaN(Number, metaclass=Singleton): + """ + Not a Number. + + Explanation + =========== + + This serves as a place holder for numeric values that are indeterminate. + Most operations on NaN, produce another NaN. Most indeterminate forms, + such as ``0/0`` or ``oo - oo` produce NaN. Two exceptions are ``0**0`` + and ``oo**0``, which all produce ``1`` (this is consistent with Python's + float). + + NaN is loosely related to floating point nan, which is defined in the + IEEE 754 floating point standard, and corresponds to the Python + ``float('nan')``. Differences are noted below. + + NaN is mathematically not equal to anything else, even NaN itself. This + explains the initially counter-intuitive results with ``Eq`` and ``==`` in + the examples below. + + NaN is not comparable so inequalities raise a TypeError. This is in + contrast with floating point nan where all inequalities are false. + + NaN is a singleton, and can be accessed by ``S.NaN``, or can be imported + as ``nan``. + + Examples + ======== + + >>> from sympy import nan, S, oo, Eq + >>> nan is S.NaN + True + >>> oo - oo + nan + >>> nan + 1 + nan + >>> Eq(nan, nan) # mathematical equality + False + >>> nan == nan # structural equality + True + + References + ========== + + .. [1] https://en.wikipedia.org/wiki/NaN + + """ + is_commutative = True + is_extended_real = None + is_real = None + is_rational = None + is_algebraic = None + is_transcendental = None + is_integer = None + is_comparable = False + is_finite = None + is_zero = None + is_prime = None + is_positive = None + is_negative = None + is_number = True + + __slots__ = () + + def __new__(cls): + return AtomicExpr.__new__(cls) + + def _latex(self, printer): + return r"\text{NaN}" + + def __neg__(self): + return self + + @_sympifyit('other', NotImplemented) + def __add__(self, other): + return self + + @_sympifyit('other', NotImplemented) + def __sub__(self, other): + return self + + @_sympifyit('other', NotImplemented) + def __mul__(self, other): + return self + + @_sympifyit('other', NotImplemented) + def __truediv__(self, other): + return self + + def floor(self): + return self + + def ceiling(self): + return self + + def _as_mpf_val(self, prec): + return _mpf_nan + + def __hash__(self): + return super().__hash__() + + def __eq__(self, other): + # NaN is structurally equal to another NaN + return other is S.NaN + + def __ne__(self, other): + return other is not S.NaN + + # Expr will _sympify and raise TypeError + __gt__ = Expr.__gt__ + __ge__ = Expr.__ge__ + __lt__ = Expr.__lt__ + __le__ = Expr.__le__ + +nan = S.NaN + +@dispatch(NaN, Expr) # type:ignore +def _eval_is_eq(a, b): # noqa:F811 + return False + + +class ComplexInfinity(AtomicExpr, metaclass=Singleton): + r"""Complex infinity. + + Explanation + =========== + + In complex analysis the symbol `\tilde\infty`, called "complex + infinity", represents a quantity with infinite magnitude, but + undetermined complex phase. + + ComplexInfinity is a singleton, and can be accessed by + ``S.ComplexInfinity``, or can be imported as ``zoo``. + + Examples + ======== + + >>> from sympy import zoo + >>> zoo + 42 + zoo + >>> 42/zoo + 0 + >>> zoo + zoo + nan + >>> zoo*zoo + zoo + + See Also + ======== + + Infinity + """ + + is_commutative = True + is_infinite = True + is_number = True + is_prime = False + is_complex = False + is_extended_real = False + + kind = NumberKind + + __slots__ = () + + def __new__(cls): + return AtomicExpr.__new__(cls) + + def _latex(self, printer): + return r"\tilde{\infty}" + + @staticmethod + def __abs__(): + return S.Infinity + + def floor(self): + return self + + def ceiling(self): + return self + + @staticmethod + def __neg__(): + return S.ComplexInfinity + + def _eval_power(self, expt): + if expt is S.ComplexInfinity: + return S.NaN + + if isinstance(expt, Number): + if expt.is_zero: + return S.NaN + else: + if expt.is_positive: + return S.ComplexInfinity + else: + return S.Zero + + +zoo = S.ComplexInfinity + + +class NumberSymbol(AtomicExpr): + + is_commutative = True + is_finite = True + is_number = True + + __slots__ = () + + is_NumberSymbol = True + + kind = NumberKind + + def __new__(cls): + return AtomicExpr.__new__(cls) + + def approximation(self, number_cls): + """ Return an interval with number_cls endpoints + that contains the value of NumberSymbol. + If not implemented, then return None. + """ + + def _eval_evalf(self, prec): + return Float._new(self._as_mpf_val(prec), prec) + + def __eq__(self, other): + try: + other = _sympify(other) + except SympifyError: + return NotImplemented + if self is other: + return True + if other.is_Number and self.is_irrational: + return False + + return False # NumberSymbol != non-(Number|self) + + def __ne__(self, other): + return not self == other + + def __le__(self, other): + if self is other: + return S.true + return Expr.__le__(self, other) + + def __ge__(self, other): + if self is other: + return S.true + return Expr.__ge__(self, other) + + def __int__(self): + # subclass with appropriate return value + raise NotImplementedError + + def __hash__(self): + return super().__hash__() + + +class Exp1(NumberSymbol, metaclass=Singleton): + r"""The `e` constant. + + Explanation + =========== + + The transcendental number `e = 2.718281828\ldots` is the base of the + natural logarithm and of the exponential function, `e = \exp(1)`. + Sometimes called Euler's number or Napier's constant. + + Exp1 is a singleton, and can be accessed by ``S.Exp1``, + or can be imported as ``E``. + + Examples + ======== + + >>> from sympy import exp, log, E + >>> E is exp(1) + True + >>> log(E) + 1 + + References + ========== + + .. [1] https://en.wikipedia.org/wiki/E_%28mathematical_constant%29 + """ + + is_real = True + is_positive = True + is_negative = False # XXX Forces is_negative/is_nonnegative + is_irrational = True + is_number = True + is_algebraic = False + is_transcendental = True + + __slots__ = () + + def _latex(self, printer): + return r"e" + + @staticmethod + def __abs__(): + return S.Exp1 + + def __int__(self): + return 2 + + def _as_mpf_val(self, prec): + return mpf_e(prec) + + def approximation_interval(self, number_cls): + if issubclass(number_cls, Integer): + return (Integer(2), Integer(3)) + elif issubclass(number_cls, Rational): + pass + + def _eval_power(self, expt): + if global_parameters.exp_is_pow: + return self._eval_power_exp_is_pow(expt) + else: + from sympy.functions.elementary.exponential import exp + return exp(expt) + + def _eval_power_exp_is_pow(self, arg): + if arg.is_Number: + if arg is oo: + return oo + elif arg == -oo: + return S.Zero + from sympy.functions.elementary.exponential import log + if isinstance(arg, log): + return arg.args[0] + + # don't autoexpand Pow or Mul (see the issue 3351): + elif not arg.is_Add: + Ioo = I*oo + if arg in [Ioo, -Ioo]: + return nan + + coeff = arg.coeff(pi*I) + if coeff: + if (2*coeff).is_integer: + if coeff.is_even: + return S.One + elif coeff.is_odd: + return S.NegativeOne + elif (coeff + S.Half).is_even: + return -I + elif (coeff + S.Half).is_odd: + return I + elif coeff.is_Rational: + ncoeff = coeff % 2 # restrict to [0, 2pi) + if ncoeff > 1: # restrict to (-pi, pi] + ncoeff -= 2 + if ncoeff != coeff: + return S.Exp1**(ncoeff*S.Pi*S.ImaginaryUnit) + + # Warning: code in risch.py will be very sensitive to changes + # in this (see DifferentialExtension). + + # look for a single log factor + + coeff, terms = arg.as_coeff_Mul() + + # but it can't be multiplied by oo + if coeff in (oo, -oo): + return + + coeffs, log_term = [coeff], None + for term in Mul.make_args(terms): + if isinstance(term, log): + if log_term is None: + log_term = term.args[0] + else: + return + elif term.is_comparable: + coeffs.append(term) + else: + return + + return log_term**Mul(*coeffs) if log_term else None + elif arg.is_Add: + out = [] + add = [] + argchanged = False + for a in arg.args: + if a is S.One: + add.append(a) + continue + newa = self**a + if isinstance(newa, Pow) and newa.base is self: + if newa.exp != a: + add.append(newa.exp) + argchanged = True + else: + add.append(a) + else: + out.append(newa) + if out or argchanged: + return Mul(*out)*Pow(self, Add(*add), evaluate=False) + elif arg.is_Matrix: + return arg.exp() + + def _eval_rewrite_as_sin(self, **kwargs): + from sympy.functions.elementary.trigonometric import sin + return sin(I + S.Pi/2) - I*sin(I) + + def _eval_rewrite_as_cos(self, **kwargs): + from sympy.functions.elementary.trigonometric import cos + return cos(I) + I*cos(I + S.Pi/2) + +E = S.Exp1 + + +class Pi(NumberSymbol, metaclass=Singleton): + r"""The `\pi` constant. + + Explanation + =========== + + The transcendental number `\pi = 3.141592654\ldots` represents the ratio + of a circle's circumference to its diameter, the area of the unit circle, + the half-period of trigonometric functions, and many other things + in mathematics. + + Pi is a singleton, and can be accessed by ``S.Pi``, or can + be imported as ``pi``. + + Examples + ======== + + >>> from sympy import S, pi, oo, sin, exp, integrate, Symbol + >>> S.Pi + pi + >>> pi > 3 + True + >>> pi.is_irrational + True + >>> x = Symbol('x') + >>> sin(x + 2*pi) + sin(x) + >>> integrate(exp(-x**2), (x, -oo, oo)) + sqrt(pi) + + References + ========== + + .. [1] https://en.wikipedia.org/wiki/Pi + """ + + is_real = True + is_positive = True + is_negative = False + is_irrational = True + is_number = True + is_algebraic = False + is_transcendental = True + + __slots__ = () + + def _latex(self, printer): + return r"\pi" + + @staticmethod + def __abs__(): + return S.Pi + + def __int__(self): + return 3 + + def _as_mpf_val(self, prec): + return mpf_pi(prec) + + def approximation_interval(self, number_cls): + if issubclass(number_cls, Integer): + return (Integer(3), Integer(4)) + elif issubclass(number_cls, Rational): + return (Rational(223, 71, 1), Rational(22, 7, 1)) + +pi = S.Pi + + +class GoldenRatio(NumberSymbol, metaclass=Singleton): + r"""The golden ratio, `\phi`. + + Explanation + =========== + + `\phi = \frac{1 + \sqrt{5}}{2}` is an algebraic number. Two quantities + are in the golden ratio if their ratio is the same as the ratio of + their sum to the larger of the two quantities, i.e. their maximum. + + GoldenRatio is a singleton, and can be accessed by ``S.GoldenRatio``. + + Examples + ======== + + >>> from sympy import S + >>> S.GoldenRatio > 1 + True + >>> S.GoldenRatio.expand(func=True) + 1/2 + sqrt(5)/2 + >>> S.GoldenRatio.is_irrational + True + + References + ========== + + .. [1] https://en.wikipedia.org/wiki/Golden_ratio + """ + + is_real = True + is_positive = True + is_negative = False + is_irrational = True + is_number = True + is_algebraic = True + is_transcendental = False + + __slots__ = () + + def _latex(self, printer): + return r"\phi" + + def __int__(self): + return 1 + + def _as_mpf_val(self, prec): + # XXX track down why this has to be increased + rv = mlib.from_man_exp(phi_fixed(prec + 10), -prec - 10) + return mpf_norm(rv, prec) + + def _eval_expand_func(self, **hints): + from sympy.functions.elementary.miscellaneous import sqrt + return S.Half + S.Half*sqrt(5) + + def approximation_interval(self, number_cls): + if issubclass(number_cls, Integer): + return (S.One, Rational(2)) + elif issubclass(number_cls, Rational): + pass + + _eval_rewrite_as_sqrt = _eval_expand_func + + +class TribonacciConstant(NumberSymbol, metaclass=Singleton): + r"""The tribonacci constant. + + Explanation + =========== + + The tribonacci numbers are like the Fibonacci numbers, but instead + of starting with two predetermined terms, the sequence starts with + three predetermined terms and each term afterwards is the sum of the + preceding three terms. + + The tribonacci constant is the ratio toward which adjacent tribonacci + numbers tend. It is a root of the polynomial `x^3 - x^2 - x - 1 = 0`, + and also satisfies the equation `x + x^{-3} = 2`. + + TribonacciConstant is a singleton, and can be accessed + by ``S.TribonacciConstant``. + + Examples + ======== + + >>> from sympy import S + >>> S.TribonacciConstant > 1 + True + >>> S.TribonacciConstant.expand(func=True) + 1/3 + (19 - 3*sqrt(33))**(1/3)/3 + (3*sqrt(33) + 19)**(1/3)/3 + >>> S.TribonacciConstant.is_irrational + True + >>> S.TribonacciConstant.n(20) + 1.8392867552141611326 + + References + ========== + + .. [1] https://en.wikipedia.org/wiki/Generalizations_of_Fibonacci_numbers#Tribonacci_numbers + """ + + is_real = True + is_positive = True + is_negative = False + is_irrational = True + is_number = True + is_algebraic = True + is_transcendental = False + + __slots__ = () + + def _latex(self, printer): + return r"\text{TribonacciConstant}" + + def __int__(self): + return 1 + + def _as_mpf_val(self, prec): + return self._eval_evalf(prec)._mpf_ + + def _eval_evalf(self, prec): + rv = self._eval_expand_func(function=True)._eval_evalf(prec + 4) + return Float(rv, precision=prec) + + def _eval_expand_func(self, **hints): + from sympy.functions.elementary.miscellaneous import cbrt, sqrt + return (1 + cbrt(19 - 3*sqrt(33)) + cbrt(19 + 3*sqrt(33))) / 3 + + def approximation_interval(self, number_cls): + if issubclass(number_cls, Integer): + return (S.One, Rational(2)) + elif issubclass(number_cls, Rational): + pass + + _eval_rewrite_as_sqrt = _eval_expand_func + + +class EulerGamma(NumberSymbol, metaclass=Singleton): + r"""The Euler-Mascheroni constant. + + Explanation + =========== + + `\gamma = 0.5772157\ldots` (also called Euler's constant) is a mathematical + constant recurring in analysis and number theory. It is defined as the + limiting difference between the harmonic series and the + natural logarithm: + + .. math:: \gamma = \lim\limits_{n\to\infty} + \left(\sum\limits_{k=1}^n\frac{1}{k} - \ln n\right) + + EulerGamma is a singleton, and can be accessed by ``S.EulerGamma``. + + Examples + ======== + + >>> from sympy import S + >>> S.EulerGamma.is_irrational + >>> S.EulerGamma > 0 + True + >>> S.EulerGamma > 1 + False + + References + ========== + + .. [1] https://en.wikipedia.org/wiki/Euler%E2%80%93Mascheroni_constant + """ + + is_real = True + is_positive = True + is_negative = False + is_irrational = None + is_number = True + + __slots__ = () + + def _latex(self, printer): + return r"\gamma" + + def __int__(self): + return 0 + + def _as_mpf_val(self, prec): + # XXX track down why this has to be increased + v = mlib.libhyper.euler_fixed(prec + 10) + rv = mlib.from_man_exp(v, -prec - 10) + return mpf_norm(rv, prec) + + def approximation_interval(self, number_cls): + if issubclass(number_cls, Integer): + return (S.Zero, S.One) + elif issubclass(number_cls, Rational): + return (S.Half, Rational(3, 5, 1)) + + +class Catalan(NumberSymbol, metaclass=Singleton): + r"""Catalan's constant. + + Explanation + =========== + + $G = 0.91596559\ldots$ is given by the infinite series + + .. math:: G = \sum_{k=0}^{\infty} \frac{(-1)^k}{(2k+1)^2} + + Catalan is a singleton, and can be accessed by ``S.Catalan``. + + Examples + ======== + + >>> from sympy import S + >>> S.Catalan.is_irrational + >>> S.Catalan > 0 + True + >>> S.Catalan > 1 + False + + References + ========== + + .. [1] https://en.wikipedia.org/wiki/Catalan%27s_constant + """ + + is_real = True + is_positive = True + is_negative = False + is_irrational = None + is_number = True + + __slots__ = () + + def __int__(self): + return 0 + + def _as_mpf_val(self, prec): + # XXX track down why this has to be increased + v = mlib.catalan_fixed(prec + 10) + rv = mlib.from_man_exp(v, -prec - 10) + return mpf_norm(rv, prec) + + def approximation_interval(self, number_cls): + if issubclass(number_cls, Integer): + return (S.Zero, S.One) + elif issubclass(number_cls, Rational): + return (Rational(9, 10, 1), S.One) + + def _eval_rewrite_as_Sum(self, k_sym=None, symbols=None, **hints): + if (k_sym is not None) or (symbols is not None): + return self + from .symbol import Dummy + from sympy.concrete.summations import Sum + k = Dummy('k', integer=True, nonnegative=True) + return Sum(S.NegativeOne**k / (2*k+1)**2, (k, 0, S.Infinity)) + + def _latex(self, printer): + return "G" + + +class ImaginaryUnit(AtomicExpr, metaclass=Singleton): + r"""The imaginary unit, `i = \sqrt{-1}`. + + I is a singleton, and can be accessed by ``S.I``, or can be + imported as ``I``. + + Examples + ======== + + >>> from sympy import I, sqrt + >>> sqrt(-1) + I + >>> I*I + -1 + >>> 1/I + -I + + References + ========== + + .. [1] https://en.wikipedia.org/wiki/Imaginary_unit + """ + + is_commutative = True + is_imaginary = True + is_finite = True + is_number = True + is_algebraic = True + is_transcendental = False + + kind = NumberKind + + __slots__ = () + + def _latex(self, printer): + return printer._settings['imaginary_unit_latex'] + + @staticmethod + def __abs__(): + return S.One + + def _eval_evalf(self, prec): + return self + + def _eval_conjugate(self): + return -S.ImaginaryUnit + + def _eval_power(self, expt): + """ + b is I = sqrt(-1) + e is symbolic object but not equal to 0, 1 + + I**r -> (-1)**(r/2) -> exp(r/2*Pi*I) -> sin(Pi*r/2) + cos(Pi*r/2)*I, r is decimal + I**0 mod 4 -> 1 + I**1 mod 4 -> I + I**2 mod 4 -> -1 + I**3 mod 4 -> -I + """ + + if isinstance(expt, Integer): + expt = expt % 4 + if expt == 0: + return S.One + elif expt == 1: + return S.ImaginaryUnit + elif expt == 2: + return S.NegativeOne + elif expt == 3: + return -S.ImaginaryUnit + if isinstance(expt, Rational): + i, r = divmod(expt, 2) + rv = Pow(S.ImaginaryUnit, r, evaluate=False) + if i % 2: + return Mul(S.NegativeOne, rv, evaluate=False) + return rv + + def as_base_exp(self): + return S.NegativeOne, S.Half + + @property + def _mpc_(self): + return (Float(0)._mpf_, Float(1)._mpf_) + + +I = S.ImaginaryUnit + + +def int_valued(x): + """return True only for a literal Number whose internal + representation as a fraction has a denominator of 1, + else False, i.e. integer, with no fractional part. + """ + if isinstance(x, (SYMPY_INTS, int)): + return True + if type(x) is float: + return x.is_integer() + if isinstance(x, Integer): + return True + if isinstance(x, Float): + # x = s*m*2**p; _mpf_ = s,m,e,p + return x._mpf_[2] >= 0 + return False # or add new types to recognize + + +def equal_valued(x, y): + """Compare expressions treating plain floats as rationals. + + Examples + ======== + + >>> from sympy import S, symbols, Rational, Float + >>> from sympy.core.numbers import equal_valued + >>> equal_valued(1, 2) + False + >>> equal_valued(1, 1) + True + + In SymPy expressions with Floats compare unequal to corresponding + expressions with rationals: + + >>> x = symbols('x') + >>> x**2 == x**2.0 + False + + However an individual Float compares equal to a Rational: + + >>> Rational(1, 2) == Float(0.5) + False + + In a future version of SymPy this might change so that Rational and Float + compare unequal. This function provides the behavior currently expected of + ``==`` so that it could still be used if the behavior of ``==`` were to + change in future. + + >>> equal_valued(1, 1.0) # Float vs Rational + True + >>> equal_valued(S(1).n(3), S(1).n(5)) # Floats of different precision + True + + Explanation + =========== + + In future SymPy verions Float and Rational might compare unequal and floats + with different precisions might compare unequal. In that context a function + is needed that can check if a number is equal to 1 or 0 etc. The idea is + that instead of testing ``if x == 1:`` if we want to accept floats like + ``1.0`` as well then the test can be written as ``if equal_valued(x, 1):`` + or ``if equal_valued(x, 2):``. Since this function is intended to be used + in situations where one or both operands are expected to be concrete + numbers like 1 or 0 the function does not recurse through the args of any + compound expression to compare any nested floats. + + References + ========== + + .. [1] https://github.com/sympy/sympy/pull/20033 + """ + x = _sympify(x) + y = _sympify(y) + + # Handle everything except Float/Rational first + if not x.is_Float and not y.is_Float: + return x == y + elif x.is_Float and y.is_Float: + # Compare values without regard for precision + return x._mpf_ == y._mpf_ + elif x.is_Float: + x, y = y, x + if not x.is_Rational: + return False + + # Now y is Float and x is Rational. A simple approach at this point would + # just be x == Rational(y) but if y has a large exponent creating a + # Rational could be prohibitively expensive. + + sign, man, exp, _ = y._mpf_ + p, q = x.p, x.q + + if sign: + man = -man + + if exp == 0: + # y odd integer + return q == 1 and man == p + elif exp > 0: + # y even integer + if q != 1: + return False + if p.bit_length() != man.bit_length() + exp: + return False + return man << exp == p + else: + # y non-integer. Need p == man and q == 2**-exp + if p != man: + return False + neg_exp = -exp + if q.bit_length() - 1 != neg_exp: + return False + return (1 << neg_exp) == q + + +def all_close(expr1, expr2, rtol=1e-5, atol=1e-8): + """Return True if expr1 and expr2 are numerically close. + + The expressions must have the same structure, but any Rational, Integer, or + Float numbers they contain are compared approximately using rtol and atol. + Any other parts of expressions are compared exactly. + + Relative tolerance is measured with respect to expr2 so when used in + testing expr2 should be the expected correct answer. + + Examples + ======== + + >>> from sympy import exp + >>> from sympy.abc import x, y + >>> from sympy.core.numbers import all_close + >>> expr1 = 0.1*exp(x - y) + >>> expr2 = exp(x - y)/10 + >>> expr1 + 0.1*exp(x - y) + >>> expr2 + exp(x - y)/10 + >>> expr1 == expr2 + False + >>> all_close(expr1, expr2) + True + """ + NUM_TYPES = (Rational, Float) + + def _all_close(expr1, expr2, rtol, atol): + num1 = isinstance(expr1, NUM_TYPES) + num2 = isinstance(expr2, NUM_TYPES) + if num1 != num2: + return False + elif num1: + return bool(abs(expr1 - expr2) <= atol + rtol*abs(expr2)) + elif expr1.is_Atom: + return expr1 == expr2 + elif expr1.func != expr2.func or len(expr1.args) != len(expr2.args): + return False + elif expr1.is_Add or expr1.is_Mul: + return _all_close_ac(expr1, expr2, rtol, atol) + else: + args = zip(expr1.args, expr2.args) + return all(_all_close(a1, a2, rtol, atol) for a1, a2 in args) + + def _all_close_ac(expr1, expr2, rtol, atol): + # Compare expressions with associative commutative operators for + # approximate equality. This could be horribly inefficient for large + # expressions e.g. an Add with many terms. + args2 = list(expr2.args) + for arg1 in expr1.args: + for i, arg2 in enumerate(args2): + if _all_close(arg1, arg2, rtol, atol): + args2.pop(i) + break + else: + return False + return True + + return _all_close(_sympify(expr1), _sympify(expr2), rtol, atol) + + +@dispatch(Tuple, Number) # type:ignore +def _eval_is_eq(self, other): # noqa: F811 + return False + + +def sympify_fractions(f): + return Rational(f.numerator, f.denominator, 1) + +_sympy_converter[fractions.Fraction] = sympify_fractions + + +if gmpy is not None: + + def sympify_mpz(x): + return Integer(int(x)) + + def sympify_mpq(x): + return Rational(int(x.numerator), int(x.denominator)) + + _sympy_converter[type(gmpy.mpz(1))] = sympify_mpz + _sympy_converter[type(gmpy.mpq(1, 2))] = sympify_mpq + + +if flint is not None: + + def sympify_fmpz(x): + return Integer(int(x)) + + def sympify_fmpq(x): + return Rational(int(x.numerator), int(x.denominator)) + + _sympy_converter[type(flint.fmpz(1))] = sympify_fmpz + _sympy_converter[type(flint.fmpq(1, 2))] = sympify_fmpq + + +def sympify_mpmath(x): + return Expr._from_mpmath(x, x.context.prec) + +_sympy_converter[mpnumeric] = sympify_mpmath + + +def sympify_complex(a): + real, imag = list(map(sympify, (a.real, a.imag))) + return real + S.ImaginaryUnit*imag + +_sympy_converter[complex] = sympify_complex + +from .power import Pow +from .mul import Mul +Mul.identity = One() +from .add import Add +Add.identity = Zero() + + +def _register_classes(): + numbers.Number.register(Number) + numbers.Real.register(Float) + numbers.Rational.register(Rational) + numbers.Integral.register(Integer) + +_register_classes() + +_illegal = (S.NaN, S.Infinity, S.NegativeInfinity, S.ComplexInfinity) diff --git a/lib/python3.12/site-packages/sympy/core/operations.py b/lib/python3.12/site-packages/sympy/core/operations.py new file mode 100644 index 0000000000000000000000000000000000000000..efd4f6b85de499dad8c30d75cf63ec98bd439998 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/core/operations.py @@ -0,0 +1,718 @@ +from __future__ import annotations +from operator import attrgetter +from collections import defaultdict + +from sympy.utilities.exceptions import sympy_deprecation_warning + +from .sympify import _sympify as _sympify_, sympify +from .basic import Basic +from .cache import cacheit +from .sorting import ordered +from .logic import fuzzy_and +from .parameters import global_parameters +from sympy.utilities.iterables import sift +from sympy.multipledispatch.dispatcher import (Dispatcher, + ambiguity_register_error_ignore_dup, + str_signature, RaiseNotImplementedError) + + +class AssocOp(Basic): + """ Associative operations, can separate noncommutative and + commutative parts. + + (a op b) op c == a op (b op c) == a op b op c. + + Base class for Add and Mul. + + This is an abstract base class, concrete derived classes must define + the attribute `identity`. + + .. deprecated:: 1.7 + + Using arguments that aren't subclasses of :class:`~.Expr` in core + operators (:class:`~.Mul`, :class:`~.Add`, and :class:`~.Pow`) is + deprecated. See :ref:`non-expr-args-deprecated` for details. + + Parameters + ========== + + *args : + Arguments which are operated + + evaluate : bool, optional + Evaluate the operation. If not passed, refer to ``global_parameters.evaluate``. + """ + + # for performance reason, we don't let is_commutative go to assumptions, + # and keep it right here + __slots__: tuple[str, ...] = ('is_commutative',) + + _args_type: type[Basic] | None = None + + @cacheit + def __new__(cls, *args, evaluate=None, _sympify=True): + # Allow faster processing by passing ``_sympify=False``, if all arguments + # are already sympified. + if _sympify: + args = list(map(_sympify_, args)) + + # Disallow non-Expr args in Add/Mul + typ = cls._args_type + if typ is not None: + from .relational import Relational + if any(isinstance(arg, Relational) for arg in args): + raise TypeError("Relational cannot be used in %s" % cls.__name__) + + # This should raise TypeError once deprecation period is over: + for arg in args: + if not isinstance(arg, typ): + sympy_deprecation_warning( + f""" + +Using non-Expr arguments in {cls.__name__} is deprecated (in this case, one of +the arguments has type {type(arg).__name__!r}). + +If you really did intend to use a multiplication or addition operation with +this object, use the * or + operator instead. + + """, + deprecated_since_version="1.7", + active_deprecations_target="non-expr-args-deprecated", + stacklevel=4, + ) + + if evaluate is None: + evaluate = global_parameters.evaluate + if not evaluate: + obj = cls._from_args(args) + obj = cls._exec_constructor_postprocessors(obj) + return obj + + args = [a for a in args if a is not cls.identity] + + if len(args) == 0: + return cls.identity + if len(args) == 1: + return args[0] + + c_part, nc_part, order_symbols = cls.flatten(args) + is_commutative = not nc_part + obj = cls._from_args(c_part + nc_part, is_commutative) + obj = cls._exec_constructor_postprocessors(obj) + + if order_symbols is not None: + from sympy.series.order import Order + return Order(obj, *order_symbols) + return obj + + @classmethod + def _from_args(cls, args, is_commutative=None): + """Create new instance with already-processed args. + If the args are not in canonical order, then a non-canonical + result will be returned, so use with caution. The order of + args may change if the sign of the args is changed.""" + if len(args) == 0: + return cls.identity + elif len(args) == 1: + return args[0] + + obj = super().__new__(cls, *args) + if is_commutative is None: + is_commutative = fuzzy_and(a.is_commutative for a in args) + obj.is_commutative = is_commutative + return obj + + def _new_rawargs(self, *args, reeval=True, **kwargs): + """Create new instance of own class with args exactly as provided by + caller but returning the self class identity if args is empty. + + Examples + ======== + + This is handy when we want to optimize things, e.g. + + >>> from sympy import Mul, S + >>> from sympy.abc import x, y + >>> e = Mul(3, x, y) + >>> e.args + (3, x, y) + >>> Mul(*e.args[1:]) + x*y + >>> e._new_rawargs(*e.args[1:]) # the same as above, but faster + x*y + + Note: use this with caution. There is no checking of arguments at + all. This is best used when you are rebuilding an Add or Mul after + simply removing one or more args. If, for example, modifications, + result in extra 1s being inserted they will show up in the result: + + >>> m = (x*y)._new_rawargs(S.One, x); m + 1*x + >>> m == x + False + >>> m.is_Mul + True + + Another issue to be aware of is that the commutativity of the result + is based on the commutativity of self. If you are rebuilding the + terms that came from a commutative object then there will be no + problem, but if self was non-commutative then what you are + rebuilding may now be commutative. + + Although this routine tries to do as little as possible with the + input, getting the commutativity right is important, so this level + of safety is enforced: commutativity will always be recomputed if + self is non-commutative and kwarg `reeval=False` has not been + passed. + """ + if reeval and self.is_commutative is False: + is_commutative = None + else: + is_commutative = self.is_commutative + return self._from_args(args, is_commutative) + + @classmethod + def flatten(cls, seq): + """Return seq so that none of the elements are of type `cls`. This is + the vanilla routine that will be used if a class derived from AssocOp + does not define its own flatten routine.""" + # apply associativity, no commutativity property is used + new_seq = [] + while seq: + o = seq.pop() + if o.__class__ is cls: # classes must match exactly + seq.extend(o.args) + else: + new_seq.append(o) + new_seq.reverse() + + # c_part, nc_part, order_symbols + return [], new_seq, None + + def _matches_commutative(self, expr, repl_dict=None, old=False): + """ + Matches Add/Mul "pattern" to an expression "expr". + + repl_dict ... a dictionary of (wild: expression) pairs, that get + returned with the results + + This function is the main workhorse for Add/Mul. + + Examples + ======== + + >>> from sympy import symbols, Wild, sin + >>> a = Wild("a") + >>> b = Wild("b") + >>> c = Wild("c") + >>> x, y, z = symbols("x y z") + >>> (a+sin(b)*c)._matches_commutative(x+sin(y)*z) + {a_: x, b_: y, c_: z} + + In the example above, "a+sin(b)*c" is the pattern, and "x+sin(y)*z" is + the expression. + + The repl_dict contains parts that were already matched. For example + here: + + >>> (x+sin(b)*c)._matches_commutative(x+sin(y)*z, repl_dict={a: x}) + {a_: x, b_: y, c_: z} + + the only function of the repl_dict is to return it in the + result, e.g. if you omit it: + + >>> (x+sin(b)*c)._matches_commutative(x+sin(y)*z) + {b_: y, c_: z} + + the "a: x" is not returned in the result, but otherwise it is + equivalent. + + """ + from .function import _coeff_isneg + # make sure expr is Expr if pattern is Expr + from .expr import Expr + if isinstance(self, Expr) and not isinstance(expr, Expr): + return None + + if repl_dict is None: + repl_dict = {} + + # handle simple patterns + if self == expr: + return repl_dict + + d = self._matches_simple(expr, repl_dict) + if d is not None: + return d + + # eliminate exact part from pattern: (2+a+w1+w2).matches(expr) -> (w1+w2).matches(expr-a-2) + from .function import WildFunction + from .symbol import Wild + wild_part, exact_part = sift(self.args, lambda p: + p.has(Wild, WildFunction) and not expr.has(p), + binary=True) + if not exact_part: + wild_part = list(ordered(wild_part)) + if self.is_Add: + # in addition to normal ordered keys, impose + # sorting on Muls with leading Number to put + # them in order + wild_part = sorted(wild_part, key=lambda x: + x.args[0] if x.is_Mul and x.args[0].is_Number else + 0) + else: + exact = self._new_rawargs(*exact_part) + free = expr.free_symbols + if free and (exact.free_symbols - free): + # there are symbols in the exact part that are not + # in the expr; but if there are no free symbols, let + # the matching continue + return None + newexpr = self._combine_inverse(expr, exact) + if not old and (expr.is_Add or expr.is_Mul): + check = newexpr + if _coeff_isneg(check): + check = -check + if check.count_ops() > expr.count_ops(): + return None + newpattern = self._new_rawargs(*wild_part) + return newpattern.matches(newexpr, repl_dict) + + # now to real work ;) + i = 0 + saw = set() + while expr not in saw: + saw.add(expr) + args = tuple(ordered(self.make_args(expr))) + if self.is_Add and expr.is_Add: + # in addition to normal ordered keys, impose + # sorting on Muls with leading Number to put + # them in order + args = tuple(sorted(args, key=lambda x: + x.args[0] if x.is_Mul and x.args[0].is_Number else + 0)) + expr_list = (self.identity,) + args + for last_op in reversed(expr_list): + for w in reversed(wild_part): + d1 = w.matches(last_op, repl_dict) + if d1 is not None: + d2 = self.xreplace(d1).matches(expr, d1) + if d2 is not None: + return d2 + + if i == 0: + if self.is_Mul: + # make e**i look like Mul + if expr.is_Pow and expr.exp.is_Integer: + from .mul import Mul + if expr.exp > 0: + expr = Mul(*[expr.base, expr.base**(expr.exp - 1)], evaluate=False) + else: + expr = Mul(*[1/expr.base, expr.base**(expr.exp + 1)], evaluate=False) + i += 1 + continue + + elif self.is_Add: + # make i*e look like Add + c, e = expr.as_coeff_Mul() + if abs(c) > 1: + from .add import Add + if c > 0: + expr = Add(*[e, (c - 1)*e], evaluate=False) + else: + expr = Add(*[-e, (c + 1)*e], evaluate=False) + i += 1 + continue + + # try collection on non-Wild symbols + from sympy.simplify.radsimp import collect + was = expr + did = set() + for w in reversed(wild_part): + c, w = w.as_coeff_mul(Wild) + free = c.free_symbols - did + if free: + did.update(free) + expr = collect(expr, free) + if expr != was: + i += 0 + continue + + break # if we didn't continue, there is nothing more to do + + return + + def _has_matcher(self): + """Helper for .has() that checks for containment of + subexpressions within an expr by using sets of args + of similar nodes, e.g. x + 1 in x + y + 1 checks + to see that {x, 1} & {x, y, 1} == {x, 1} + """ + def _ncsplit(expr): + # this is not the same as args_cnc because here + # we don't assume expr is a Mul -- hence deal with args -- + # and always return a set. + cpart, ncpart = sift(expr.args, + lambda arg: arg.is_commutative is True, binary=True) + return set(cpart), ncpart + + c, nc = _ncsplit(self) + cls = self.__class__ + + def is_in(expr): + if isinstance(expr, cls): + if expr == self: + return True + _c, _nc = _ncsplit(expr) + if (c & _c) == c: + if not nc: + return True + elif len(nc) <= len(_nc): + for i in range(len(_nc) - len(nc) + 1): + if _nc[i:i + len(nc)] == nc: + return True + return False + return is_in + + def _eval_evalf(self, prec): + """ + Evaluate the parts of self that are numbers; if the whole thing + was a number with no functions it would have been evaluated, but + it wasn't so we must judiciously extract the numbers and reconstruct + the object. This is *not* simply replacing numbers with evaluated + numbers. Numbers should be handled in the largest pure-number + expression as possible. So the code below separates ``self`` into + number and non-number parts and evaluates the number parts and + walks the args of the non-number part recursively (doing the same + thing). + """ + from .add import Add + from .mul import Mul + from .symbol import Symbol + from .function import AppliedUndef + if isinstance(self, (Mul, Add)): + x, tail = self.as_independent(Symbol, AppliedUndef) + # if x is an AssocOp Function then the _evalf below will + # call _eval_evalf (here) so we must break the recursion + if not (tail is self.identity or + isinstance(x, AssocOp) and x.is_Function or + x is self.identity and isinstance(tail, AssocOp)): + # here, we have a number so we just call to _evalf with prec; + # prec is not the same as n, it is the binary precision so + # that's why we don't call to evalf. + x = x._evalf(prec) if x is not self.identity else self.identity + args = [] + tail_args = tuple(self.func.make_args(tail)) + for a in tail_args: + # here we call to _eval_evalf since we don't know what we + # are dealing with and all other _eval_evalf routines should + # be doing the same thing (i.e. taking binary prec and + # finding the evalf-able args) + newa = a._eval_evalf(prec) + if newa is None: + args.append(a) + else: + args.append(newa) + return self.func(x, *args) + + # this is the same as above, but there were no pure-number args to + # deal with + args = [] + for a in self.args: + newa = a._eval_evalf(prec) + if newa is None: + args.append(a) + else: + args.append(newa) + return self.func(*args) + + @classmethod + def make_args(cls, expr): + """ + Return a sequence of elements `args` such that cls(*args) == expr + + Examples + ======== + + >>> from sympy import Symbol, Mul, Add + >>> x, y = map(Symbol, 'xy') + + >>> Mul.make_args(x*y) + (x, y) + >>> Add.make_args(x*y) + (x*y,) + >>> set(Add.make_args(x*y + y)) == set([y, x*y]) + True + + """ + if isinstance(expr, cls): + return expr.args + else: + return (sympify(expr),) + + def doit(self, **hints): + if hints.get('deep', True): + terms = [term.doit(**hints) for term in self.args] + else: + terms = self.args + return self.func(*terms, evaluate=True) + +class ShortCircuit(Exception): + pass + + +class LatticeOp(AssocOp): + """ + Join/meet operations of an algebraic lattice[1]. + + Explanation + =========== + + These binary operations are associative (op(op(a, b), c) = op(a, op(b, c))), + commutative (op(a, b) = op(b, a)) and idempotent (op(a, a) = op(a) = a). + Common examples are AND, OR, Union, Intersection, max or min. They have an + identity element (op(identity, a) = a) and an absorbing element + conventionally called zero (op(zero, a) = zero). + + This is an abstract base class, concrete derived classes must declare + attributes zero and identity. All defining properties are then respected. + + Examples + ======== + + >>> from sympy import Integer + >>> from sympy.core.operations import LatticeOp + >>> class my_join(LatticeOp): + ... zero = Integer(0) + ... identity = Integer(1) + >>> my_join(2, 3) == my_join(3, 2) + True + >>> my_join(2, my_join(3, 4)) == my_join(2, 3, 4) + True + >>> my_join(0, 1, 4, 2, 3, 4) + 0 + >>> my_join(1, 2) + 2 + + References + ========== + + .. [1] https://en.wikipedia.org/wiki/Lattice_%28order%29 + """ + + is_commutative = True + + def __new__(cls, *args, **options): + args = (_sympify_(arg) for arg in args) + + try: + # /!\ args is a generator and _new_args_filter + # must be careful to handle as such; this + # is done so short-circuiting can be done + # without having to sympify all values + _args = frozenset(cls._new_args_filter(args)) + except ShortCircuit: + return sympify(cls.zero) + if not _args: + return sympify(cls.identity) + elif len(_args) == 1: + return set(_args).pop() + else: + # XXX in almost every other case for __new__, *_args is + # passed along, but the expectation here is for _args + obj = super(AssocOp, cls).__new__(cls, *ordered(_args)) + obj._argset = _args + return obj + + @classmethod + def _new_args_filter(cls, arg_sequence, call_cls=None): + """Generator filtering args""" + ncls = call_cls or cls + for arg in arg_sequence: + if arg == ncls.zero: + raise ShortCircuit(arg) + elif arg == ncls.identity: + continue + elif arg.func == ncls: + yield from arg.args + else: + yield arg + + @classmethod + def make_args(cls, expr): + """ + Return a set of args such that cls(*arg_set) == expr. + """ + if isinstance(expr, cls): + return expr._argset + else: + return frozenset([sympify(expr)]) + + +class AssocOpDispatcher: + """ + Handler dispatcher for associative operators + + .. notes:: + This approach is experimental, and can be replaced or deleted in the future. + See https://github.com/sympy/sympy/pull/19463. + + Explanation + =========== + + If arguments of different types are passed, the classes which handle the operation for each type + are collected. Then, a class which performs the operation is selected by recursive binary dispatching. + Dispatching relation can be registered by ``register_handlerclass`` method. + + Priority registration is unordered. You cannot make ``A*B`` and ``B*A`` refer to + different handler classes. All logic dealing with the order of arguments must be implemented + in the handler class. + + Examples + ======== + + >>> from sympy import Add, Expr, Symbol + >>> from sympy.core.add import add + + >>> class NewExpr(Expr): + ... @property + ... def _add_handler(self): + ... return NewAdd + >>> class NewAdd(NewExpr, Add): + ... pass + >>> add.register_handlerclass((Add, NewAdd), NewAdd) + + >>> a, b = Symbol('a'), NewExpr() + >>> add(a, b) == NewAdd(a, b) + True + + """ + def __init__(self, name, doc=None): + self.name = name + self.doc = doc + self.handlerattr = "_%s_handler" % name + self._handlergetter = attrgetter(self.handlerattr) + self._dispatcher = Dispatcher(name) + + def __repr__(self): + return "" % self.name + + def register_handlerclass(self, classes, typ, on_ambiguity=ambiguity_register_error_ignore_dup): + """ + Register the handler class for two classes, in both straight and reversed order. + + Paramteters + =========== + + classes : tuple of two types + Classes who are compared with each other. + + typ: + Class which is registered to represent *cls1* and *cls2*. + Handler method of *self* must be implemented in this class. + """ + if not len(classes) == 2: + raise RuntimeError( + "Only binary dispatch is supported, but got %s types: <%s>." % ( + len(classes), str_signature(classes) + )) + if len(set(classes)) == 1: + raise RuntimeError( + "Duplicate types <%s> cannot be dispatched." % str_signature(classes) + ) + self._dispatcher.add(tuple(classes), typ, on_ambiguity=on_ambiguity) + self._dispatcher.add(tuple(reversed(classes)), typ, on_ambiguity=on_ambiguity) + + @cacheit + def __call__(self, *args, _sympify=True, **kwargs): + """ + Parameters + ========== + + *args : + Arguments which are operated + """ + if _sympify: + args = tuple(map(_sympify_, args)) + handlers = frozenset(map(self._handlergetter, args)) + + # no need to sympify again + return self.dispatch(handlers)(*args, _sympify=False, **kwargs) + + @cacheit + def dispatch(self, handlers): + """ + Select the handler class, and return its handler method. + """ + + # Quick exit for the case where all handlers are same + if len(handlers) == 1: + h, = handlers + if not isinstance(h, type): + raise RuntimeError("Handler {!r} is not a type.".format(h)) + return h + + # Recursively select with registered binary priority + for i, typ in enumerate(handlers): + + if not isinstance(typ, type): + raise RuntimeError("Handler {!r} is not a type.".format(typ)) + + if i == 0: + handler = typ + else: + prev_handler = handler + handler = self._dispatcher.dispatch(prev_handler, typ) + + if not isinstance(handler, type): + raise RuntimeError( + "Dispatcher for {!r} and {!r} must return a type, but got {!r}".format( + prev_handler, typ, handler + )) + + # return handler class + return handler + + @property + def __doc__(self): + docs = [ + "Multiply dispatched associative operator: %s" % self.name, + "Note that support for this is experimental, see the docs for :class:`AssocOpDispatcher` for details" + ] + + if self.doc: + docs.append(self.doc) + + s = "Registered handler classes\n" + s += '=' * len(s) + docs.append(s) + + amb_sigs = [] + + typ_sigs = defaultdict(list) + for sigs in self._dispatcher.ordering[::-1]: + key = self._dispatcher.funcs[sigs] + typ_sigs[key].append(sigs) + + for typ, sigs in typ_sigs.items(): + + sigs_str = ', '.join('<%s>' % str_signature(sig) for sig in sigs) + + if isinstance(typ, RaiseNotImplementedError): + amb_sigs.append(sigs_str) + continue + + s = 'Inputs: %s\n' % sigs_str + s += '-' * len(s) + '\n' + s += typ.__name__ + docs.append(s) + + if amb_sigs: + s = "Ambiguous handler classes\n" + s += '=' * len(s) + docs.append(s) + + s = '\n'.join(amb_sigs) + docs.append(s) + + return '\n\n'.join(docs) diff --git a/lib/python3.12/site-packages/sympy/core/parameters.py b/lib/python3.12/site-packages/sympy/core/parameters.py new file mode 100644 index 0000000000000000000000000000000000000000..d911a3652bf02fa5b24c43b138035a57be687228 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/core/parameters.py @@ -0,0 +1,161 @@ +"""Thread-safe global parameters""" + +from .cache import clear_cache +from contextlib import contextmanager +from threading import local + +class _global_parameters(local): + """ + Thread-local global parameters. + + Explanation + =========== + + This class generates thread-local container for SymPy's global parameters. + Every global parameters must be passed as keyword argument when generating + its instance. + A variable, `global_parameters` is provided as default instance for this class. + + WARNING! Although the global parameters are thread-local, SymPy's cache is not + by now. + This may lead to undesired result in multi-threading operations. + + Examples + ======== + + >>> from sympy.abc import x + >>> from sympy.core.cache import clear_cache + >>> from sympy.core.parameters import global_parameters as gp + + >>> gp.evaluate + True + >>> x+x + 2*x + + >>> log = [] + >>> def f(): + ... clear_cache() + ... gp.evaluate = False + ... log.append(x+x) + ... clear_cache() + >>> import threading + >>> thread = threading.Thread(target=f) + >>> thread.start() + >>> thread.join() + + >>> print(log) + [x + x] + + >>> gp.evaluate + True + >>> x+x + 2*x + + References + ========== + + .. [1] https://docs.python.org/3/library/threading.html + + """ + def __init__(self, **kwargs): + self.__dict__.update(kwargs) + + def __setattr__(self, name, value): + if getattr(self, name) != value: + clear_cache() + return super().__setattr__(name, value) + +global_parameters = _global_parameters(evaluate=True, distribute=True, exp_is_pow=False) + +class evaluate: + """ Control automatic evaluation + + Explanation + =========== + + This context manager controls whether or not all SymPy functions evaluate + by default. + + Note that much of SymPy expects evaluated expressions. This functionality + is experimental and is unlikely to function as intended on large + expressions. + + Examples + ======== + + >>> from sympy import evaluate + >>> from sympy.abc import x + >>> print(x + x) + 2*x + >>> with evaluate(False): + ... print(x + x) + x + x + """ + def __init__(self, x): + self.x = x + self.old = [] + + def __enter__(self): + self.old.append(global_parameters.evaluate) + global_parameters.evaluate = self.x + + def __exit__(self, exc_type, exc_val, exc_tb): + global_parameters.evaluate = self.old.pop() + +@contextmanager +def distribute(x): + """ Control automatic distribution of Number over Add + + Explanation + =========== + + This context manager controls whether or not Mul distribute Number over + Add. Plan is to avoid distributing Number over Add in all of sympy. Once + that is done, this contextmanager will be removed. + + Examples + ======== + + >>> from sympy.abc import x + >>> from sympy.core.parameters import distribute + >>> print(2*(x + 1)) + 2*x + 2 + >>> with distribute(False): + ... print(2*(x + 1)) + 2*(x + 1) + """ + + old = global_parameters.distribute + + try: + global_parameters.distribute = x + yield + finally: + global_parameters.distribute = old + + +@contextmanager +def _exp_is_pow(x): + """ + Control whether `e^x` should be represented as ``exp(x)`` or a ``Pow(E, x)``. + + Examples + ======== + + >>> from sympy import exp + >>> from sympy.abc import x + >>> from sympy.core.parameters import _exp_is_pow + >>> with _exp_is_pow(True): print(type(exp(x))) + + >>> with _exp_is_pow(False): print(type(exp(x))) + exp + """ + old = global_parameters.exp_is_pow + + clear_cache() + try: + global_parameters.exp_is_pow = x + yield + finally: + clear_cache() + global_parameters.exp_is_pow = old diff --git a/lib/python3.12/site-packages/sympy/core/power.py b/lib/python3.12/site-packages/sympy/core/power.py new file mode 100644 index 0000000000000000000000000000000000000000..02eb307fd65598a6587ffabb52fc404f73622d5f --- /dev/null +++ b/lib/python3.12/site-packages/sympy/core/power.py @@ -0,0 +1,1844 @@ +from __future__ import annotations +from typing import Callable +from itertools import product + +from .sympify import _sympify +from .cache import cacheit +from .singleton import S +from .expr import Expr +from .evalf import PrecisionExhausted +from .function import (expand_complex, expand_multinomial, + expand_mul, _mexpand, PoleError) +from .logic import fuzzy_bool, fuzzy_not, fuzzy_and, fuzzy_or +from .parameters import global_parameters +from .relational import is_gt, is_lt +from .kind import NumberKind, UndefinedKind +from sympy.utilities.iterables import sift +from sympy.utilities.exceptions import sympy_deprecation_warning +from sympy.utilities.misc import as_int +from sympy.multipledispatch import Dispatcher + + +class Pow(Expr): + """ + Defines the expression x**y as "x raised to a power y" + + .. deprecated:: 1.7 + + Using arguments that aren't subclasses of :class:`~.Expr` in core + operators (:class:`~.Mul`, :class:`~.Add`, and :class:`~.Pow`) is + deprecated. See :ref:`non-expr-args-deprecated` for details. + + Singleton definitions involving (0, 1, -1, oo, -oo, I, -I): + + +--------------+---------+-----------------------------------------------+ + | expr | value | reason | + +==============+=========+===============================================+ + | z**0 | 1 | Although arguments over 0**0 exist, see [2]. | + +--------------+---------+-----------------------------------------------+ + | z**1 | z | | + +--------------+---------+-----------------------------------------------+ + | (-oo)**(-1) | 0 | | + +--------------+---------+-----------------------------------------------+ + | (-1)**-1 | -1 | | + +--------------+---------+-----------------------------------------------+ + | S.Zero**-1 | zoo | This is not strictly true, as 0**-1 may be | + | | | undefined, but is convenient in some contexts | + | | | where the base is assumed to be positive. | + +--------------+---------+-----------------------------------------------+ + | 1**-1 | 1 | | + +--------------+---------+-----------------------------------------------+ + | oo**-1 | 0 | | + +--------------+---------+-----------------------------------------------+ + | 0**oo | 0 | Because for all complex numbers z near | + | | | 0, z**oo -> 0. | + +--------------+---------+-----------------------------------------------+ + | 0**-oo | zoo | This is not strictly true, as 0**oo may be | + | | | oscillating between positive and negative | + | | | values or rotating in the complex plane. | + | | | It is convenient, however, when the base | + | | | is positive. | + +--------------+---------+-----------------------------------------------+ + | 1**oo | nan | Because there are various cases where | + | 1**-oo | | lim(x(t),t)=1, lim(y(t),t)=oo (or -oo), | + | | | but lim( x(t)**y(t), t) != 1. See [3]. | + +--------------+---------+-----------------------------------------------+ + | b**zoo | nan | Because b**z has no limit as z -> zoo | + +--------------+---------+-----------------------------------------------+ + | (-1)**oo | nan | Because of oscillations in the limit. | + | (-1)**(-oo) | | | + +--------------+---------+-----------------------------------------------+ + | oo**oo | oo | | + +--------------+---------+-----------------------------------------------+ + | oo**-oo | 0 | | + +--------------+---------+-----------------------------------------------+ + | (-oo)**oo | nan | | + | (-oo)**-oo | | | + +--------------+---------+-----------------------------------------------+ + | oo**I | nan | oo**e could probably be best thought of as | + | (-oo)**I | | the limit of x**e for real x as x tends to | + | | | oo. If e is I, then the limit does not exist | + | | | and nan is used to indicate that. | + +--------------+---------+-----------------------------------------------+ + | oo**(1+I) | zoo | If the real part of e is positive, then the | + | (-oo)**(1+I) | | limit of abs(x**e) is oo. So the limit value | + | | | is zoo. | + +--------------+---------+-----------------------------------------------+ + | oo**(-1+I) | 0 | If the real part of e is negative, then the | + | -oo**(-1+I) | | limit is 0. | + +--------------+---------+-----------------------------------------------+ + + Because symbolic computations are more flexible than floating point + calculations and we prefer to never return an incorrect answer, + we choose not to conform to all IEEE 754 conventions. This helps + us avoid extra test-case code in the calculation of limits. + + See Also + ======== + + sympy.core.numbers.Infinity + sympy.core.numbers.NegativeInfinity + sympy.core.numbers.NaN + + References + ========== + + .. [1] https://en.wikipedia.org/wiki/Exponentiation + .. [2] https://en.wikipedia.org/wiki/Zero_to_the_power_of_zero + .. [3] https://en.wikipedia.org/wiki/Indeterminate_forms + + """ + is_Pow = True + + __slots__ = ('is_commutative',) + + args: tuple[Expr, Expr] + _args: tuple[Expr, Expr] + + @cacheit + def __new__(cls, b, e, evaluate=None): + if evaluate is None: + evaluate = global_parameters.evaluate + + b = _sympify(b) + e = _sympify(e) + + # XXX: This can be removed when non-Expr args are disallowed rather + # than deprecated. + from .relational import Relational + if isinstance(b, Relational) or isinstance(e, Relational): + raise TypeError('Relational cannot be used in Pow') + + # XXX: This should raise TypeError once deprecation period is over: + for arg in [b, e]: + if not isinstance(arg, Expr): + sympy_deprecation_warning( + f""" + Using non-Expr arguments in Pow is deprecated (in this case, one of the + arguments is of type {type(arg).__name__!r}). + + If you really did intend to construct a power with this base, use the ** + operator instead.""", + deprecated_since_version="1.7", + active_deprecations_target="non-expr-args-deprecated", + stacklevel=4, + ) + + if evaluate: + if e is S.ComplexInfinity: + return S.NaN + if e is S.Infinity: + if is_gt(b, S.One): + return S.Infinity + if is_gt(b, S.NegativeOne) and is_lt(b, S.One): + return S.Zero + if is_lt(b, S.NegativeOne): + if b.is_finite: + return S.ComplexInfinity + if b.is_finite is False: + return S.NaN + if e is S.Zero: + return S.One + elif e is S.One: + return b + elif e == -1 and not b: + return S.ComplexInfinity + elif e.__class__.__name__ == "AccumulationBounds": + if b == S.Exp1: + from sympy.calculus.accumulationbounds import AccumBounds + return AccumBounds(Pow(b, e.min), Pow(b, e.max)) + # autosimplification if base is a number and exp odd/even + # if base is Number then the base will end up positive; we + # do not do this with arbitrary expressions since symbolic + # cancellation might occur as in (x - 1)/(1 - x) -> -1. If + # we returned Piecewise((-1, Ne(x, 1))) for such cases then + # we could do this...but we don't + elif (e.is_Symbol and e.is_integer or e.is_Integer + ) and (b.is_number and b.is_Mul or b.is_Number + ) and b.could_extract_minus_sign(): + if e.is_even: + b = -b + elif e.is_odd: + return -Pow(-b, e) + if S.NaN in (b, e): # XXX S.NaN**x -> S.NaN under assumption that x != 0 + return S.NaN + elif b is S.One: + if abs(e).is_infinite: + return S.NaN + return S.One + else: + # recognize base as E + from sympy.functions.elementary.exponential import exp_polar + if not e.is_Atom and b is not S.Exp1 and not isinstance(b, exp_polar): + from .exprtools import factor_terms + from sympy.functions.elementary.exponential import log + from sympy.simplify.radsimp import fraction + c, ex = factor_terms(e, sign=False).as_coeff_Mul() + num, den = fraction(ex) + if isinstance(den, log) and den.args[0] == b: + return S.Exp1**(c*num) + elif den.is_Add: + from sympy.functions.elementary.complexes import sign, im + s = sign(im(b)) + if s.is_Number and s and den == \ + log(-factor_terms(b, sign=False)) + s*S.ImaginaryUnit*S.Pi: + return S.Exp1**(c*num) + + obj = b._eval_power(e) + if obj is not None: + return obj + obj = Expr.__new__(cls, b, e) + obj = cls._exec_constructor_postprocessors(obj) + if not isinstance(obj, Pow): + return obj + obj.is_commutative = (b.is_commutative and e.is_commutative) + return obj + + def inverse(self, argindex=1): + if self.base == S.Exp1: + from sympy.functions.elementary.exponential import log + return log + return None + + @property + def base(self) -> Expr: + return self._args[0] + + @property + def exp(self) -> Expr: + return self._args[1] + + @property + def kind(self): + if self.exp.kind is NumberKind: + return self.base.kind + else: + return UndefinedKind + + @classmethod + def class_key(cls): + return 3, 2, cls.__name__ + + def _eval_refine(self, assumptions): + from sympy.assumptions.ask import ask, Q + b, e = self.as_base_exp() + if ask(Q.integer(e), assumptions) and b.could_extract_minus_sign(): + if ask(Q.even(e), assumptions): + return Pow(-b, e) + elif ask(Q.odd(e), assumptions): + return -Pow(-b, e) + + def _eval_power(self, other): + b, e = self.as_base_exp() + if b is S.NaN: + return (b**e)**other # let __new__ handle it + + s = None + if other.is_integer: + s = 1 + elif b.is_polar: # e.g. exp_polar, besselj, var('p', polar=True)... + s = 1 + elif e.is_extended_real is not None: + from sympy.functions.elementary.complexes import arg, im, re, sign + from sympy.functions.elementary.exponential import exp, log + from sympy.functions.elementary.integers import floor + # helper functions =========================== + def _half(e): + """Return True if the exponent has a literal 2 as the + denominator, else None.""" + if getattr(e, 'q', None) == 2: + return True + n, d = e.as_numer_denom() + if n.is_integer and d == 2: + return True + def _n2(e): + """Return ``e`` evaluated to a Number with 2 significant + digits, else None.""" + try: + rv = e.evalf(2, strict=True) + if rv.is_Number: + return rv + except PrecisionExhausted: + pass + # =================================================== + if e.is_extended_real: + # we need _half(other) with constant floor or + # floor(S.Half - e*arg(b)/2/pi) == 0 + + + # handle -1 as special case + if e == -1: + # floor arg. is 1/2 + arg(b)/2/pi + if _half(other): + if b.is_negative is True: + return S.NegativeOne**other*Pow(-b, e*other) + elif b.is_negative is False: # XXX ok if im(b) != 0? + return Pow(b, -other) + elif e.is_even: + if b.is_extended_real: + b = abs(b) + if b.is_imaginary: + b = abs(im(b))*S.ImaginaryUnit + + if (abs(e) < 1) == True or e == 1: + s = 1 # floor = 0 + elif b.is_extended_nonnegative: + s = 1 # floor = 0 + elif re(b).is_extended_nonnegative and (abs(e) < 2) == True: + s = 1 # floor = 0 + elif _half(other): + s = exp(2*S.Pi*S.ImaginaryUnit*other*floor( + S.Half - e*arg(b)/(2*S.Pi))) + if s.is_extended_real and _n2(sign(s) - s) == 0: + s = sign(s) + else: + s = None + else: + # e.is_extended_real is False requires: + # _half(other) with constant floor or + # floor(S.Half - im(e*log(b))/2/pi) == 0 + try: + s = exp(2*S.ImaginaryUnit*S.Pi*other* + floor(S.Half - im(e*log(b))/2/S.Pi)) + # be careful to test that s is -1 or 1 b/c sign(I) == I: + # so check that s is real + if s.is_extended_real and _n2(sign(s) - s) == 0: + s = sign(s) + else: + s = None + except PrecisionExhausted: + s = None + + if s is not None: + return s*Pow(b, e*other) + + def _eval_Mod(self, q): + r"""A dispatched function to compute `b^e \bmod q`, dispatched + by ``Mod``. + + Notes + ===== + + Algorithms: + + 1. For unevaluated integer power, use built-in ``pow`` function + with 3 arguments, if powers are not too large wrt base. + + 2. For very large powers, use totient reduction if $e \ge \log(m)$. + Bound on m, is for safe factorization memory wise i.e. $m^{1/4}$. + For pollard-rho to be faster than built-in pow $\log(e) > m^{1/4}$ + check is added. + + 3. For any unevaluated power found in `b` or `e`, the step 2 + will be recursed down to the base and the exponent + such that the $b \bmod q$ becomes the new base and + $\phi(q) + e \bmod \phi(q)$ becomes the new exponent, and then + the computation for the reduced expression can be done. + """ + + base, exp = self.base, self.exp + + if exp.is_integer and exp.is_positive: + if q.is_integer and base % q == 0: + return S.Zero + + from sympy.functions.combinatorial.numbers import totient + + if base.is_Integer and exp.is_Integer and q.is_Integer: + b, e, m = int(base), int(exp), int(q) + mb = m.bit_length() + if mb <= 80 and e >= mb and e.bit_length()**4 >= m: + phi = int(totient(m)) + return Integer(pow(b, phi + e%phi, m)) + return Integer(pow(b, e, m)) + + from .mod import Mod + + if isinstance(base, Pow) and base.is_integer and base.is_number: + base = Mod(base, q) + return Mod(Pow(base, exp, evaluate=False), q) + + if isinstance(exp, Pow) and exp.is_integer and exp.is_number: + bit_length = int(q).bit_length() + # XXX Mod-Pow actually attempts to do a hanging evaluation + # if this dispatched function returns None. + # May need some fixes in the dispatcher itself. + if bit_length <= 80: + phi = totient(q) + exp = phi + Mod(exp, phi) + return Mod(Pow(base, exp, evaluate=False), q) + + def _eval_is_even(self): + if self.exp.is_integer and self.exp.is_positive: + return self.base.is_even + + def _eval_is_negative(self): + ext_neg = Pow._eval_is_extended_negative(self) + if ext_neg is True: + return self.is_finite + return ext_neg + + def _eval_is_extended_positive(self): + if self.base == self.exp: + if self.base.is_extended_nonnegative: + return True + elif self.base.is_positive: + if self.exp.is_real: + return True + elif self.base.is_extended_negative: + if self.exp.is_even: + return True + if self.exp.is_odd: + return False + elif self.base.is_zero: + if self.exp.is_extended_real: + return self.exp.is_zero + elif self.base.is_extended_nonpositive: + if self.exp.is_odd: + return False + elif self.base.is_imaginary: + if self.exp.is_integer: + m = self.exp % 4 + if m.is_zero: + return True + if m.is_integer and m.is_zero is False: + return False + if self.exp.is_imaginary: + from sympy.functions.elementary.exponential import log + return log(self.base).is_imaginary + + def _eval_is_extended_negative(self): + if self.exp is S.Half: + if self.base.is_complex or self.base.is_extended_real: + return False + if self.base.is_extended_negative: + if self.exp.is_odd and self.base.is_finite: + return True + if self.exp.is_even: + return False + elif self.base.is_extended_positive: + if self.exp.is_extended_real: + return False + elif self.base.is_zero: + if self.exp.is_extended_real: + return False + elif self.base.is_extended_nonnegative: + if self.exp.is_extended_nonnegative: + return False + elif self.base.is_extended_nonpositive: + if self.exp.is_even: + return False + elif self.base.is_extended_real: + if self.exp.is_even: + return False + + def _eval_is_zero(self): + if self.base.is_zero: + if self.exp.is_extended_positive: + return True + elif self.exp.is_extended_nonpositive: + return False + elif self.base == S.Exp1: + return self.exp is S.NegativeInfinity + elif self.base.is_zero is False: + if self.base.is_finite and self.exp.is_finite: + return False + elif self.exp.is_negative: + return self.base.is_infinite + elif self.exp.is_nonnegative: + return False + elif self.exp.is_infinite and self.exp.is_extended_real: + if (1 - abs(self.base)).is_extended_positive: + return self.exp.is_extended_positive + elif (1 - abs(self.base)).is_extended_negative: + return self.exp.is_extended_negative + elif self.base.is_finite and self.exp.is_negative: + # when self.base.is_zero is None + return False + + def _eval_is_integer(self): + b, e = self.args + if b.is_rational: + if b.is_integer is False and e.is_positive: + return False # rat**nonneg + if b.is_integer and e.is_integer: + if b is S.NegativeOne: + return True + if e.is_nonnegative or e.is_positive: + return True + if b.is_integer and e.is_negative and (e.is_finite or e.is_integer): + if fuzzy_not((b - 1).is_zero) and fuzzy_not((b + 1).is_zero): + return False + if b.is_Number and e.is_Number: + check = self.func(*self.args) + return check.is_Integer + if e.is_negative and b.is_positive and (b - 1).is_positive: + return False + if e.is_negative and b.is_negative and (b + 1).is_negative: + return False + + def _eval_is_extended_real(self): + if self.base is S.Exp1: + if self.exp.is_extended_real: + return True + elif self.exp.is_imaginary: + return (2*S.ImaginaryUnit*self.exp/S.Pi).is_even + + from sympy.functions.elementary.exponential import log, exp + real_b = self.base.is_extended_real + if real_b is None: + if self.base.func == exp and self.base.exp.is_imaginary: + return self.exp.is_imaginary + if self.base.func == Pow and self.base.base is S.Exp1 and self.base.exp.is_imaginary: + return self.exp.is_imaginary + return + real_e = self.exp.is_extended_real + if real_e is None: + return + if real_b and real_e: + if self.base.is_extended_positive: + return True + elif self.base.is_extended_nonnegative and self.exp.is_extended_nonnegative: + return True + elif self.exp.is_integer and self.base.is_extended_nonzero: + return True + elif self.exp.is_integer and self.exp.is_nonnegative: + return True + elif self.base.is_extended_negative: + if self.exp.is_Rational: + return False + if real_e and self.exp.is_extended_negative and self.base.is_zero is False: + return Pow(self.base, -self.exp).is_extended_real + im_b = self.base.is_imaginary + im_e = self.exp.is_imaginary + if im_b: + if self.exp.is_integer: + if self.exp.is_even: + return True + elif self.exp.is_odd: + return False + elif im_e and log(self.base).is_imaginary: + return True + elif self.exp.is_Add: + c, a = self.exp.as_coeff_Add() + if c and c.is_Integer: + return Mul( + self.base**c, self.base**a, evaluate=False).is_extended_real + elif self.base in (-S.ImaginaryUnit, S.ImaginaryUnit): + if (self.exp/2).is_integer is False: + return False + if real_b and im_e: + if self.base is S.NegativeOne: + return True + c = self.exp.coeff(S.ImaginaryUnit) + if c: + if self.base.is_rational and c.is_rational: + if self.base.is_nonzero and (self.base - 1).is_nonzero and c.is_nonzero: + return False + ok = (c*log(self.base)/S.Pi).is_integer + if ok is not None: + return ok + + if real_b is False and real_e: # we already know it's not imag + if isinstance(self.exp, Rational) and self.exp.p == 1: + return False + from sympy.functions.elementary.complexes import arg + i = arg(self.base)*self.exp/S.Pi + if i.is_complex: # finite + return i.is_integer + + def _eval_is_complex(self): + + if self.base == S.Exp1: + return fuzzy_or([self.exp.is_complex, self.exp.is_extended_negative]) + + if all(a.is_complex for a in self.args) and self._eval_is_finite(): + return True + + def _eval_is_imaginary(self): + if self.base.is_commutative is False: + return False + + if self.base.is_imaginary: + if self.exp.is_integer: + odd = self.exp.is_odd + if odd is not None: + return odd + return + + if self.base == S.Exp1: + f = 2 * self.exp / (S.Pi*S.ImaginaryUnit) + # exp(pi*integer) = 1 or -1, so not imaginary + if f.is_even: + return False + # exp(pi*integer + pi/2) = I or -I, so it is imaginary + if f.is_odd: + return True + return None + + if self.exp.is_imaginary: + from sympy.functions.elementary.exponential import log + imlog = log(self.base).is_imaginary + if imlog is not None: + return False # I**i -> real; (2*I)**i -> complex ==> not imaginary + + if self.base.is_extended_real and self.exp.is_extended_real: + if self.base.is_positive: + return False + else: + rat = self.exp.is_rational + if not rat: + return rat + if self.exp.is_integer: + return False + else: + half = (2*self.exp).is_integer + if half: + return self.base.is_negative + return half + + if self.base.is_extended_real is False: # we already know it's not imag + from sympy.functions.elementary.complexes import arg + i = arg(self.base)*self.exp/S.Pi + isodd = (2*i).is_odd + if isodd is not None: + return isodd + + def _eval_is_odd(self): + if self.exp.is_integer: + if self.exp.is_positive: + return self.base.is_odd + elif self.exp.is_nonnegative and self.base.is_odd: + return True + elif self.base is S.NegativeOne: + return True + + def _eval_is_finite(self): + if self.exp.is_negative: + if self.base.is_zero: + return False + if self.base.is_infinite or self.base.is_nonzero: + return True + c1 = self.base.is_finite + if c1 is None: + return + c2 = self.exp.is_finite + if c2 is None: + return + if c1 and c2: + if self.exp.is_nonnegative or fuzzy_not(self.base.is_zero): + return True + + def _eval_is_prime(self): + ''' + An integer raised to the n(>=2)-th power cannot be a prime. + ''' + if self.base.is_integer and self.exp.is_integer and (self.exp - 1).is_positive: + return False + + def _eval_is_composite(self): + """ + A power is composite if both base and exponent are greater than 1 + """ + if (self.base.is_integer and self.exp.is_integer and + ((self.base - 1).is_positive and (self.exp - 1).is_positive or + (self.base + 1).is_negative and self.exp.is_positive and self.exp.is_even)): + return True + + def _eval_is_polar(self): + return self.base.is_polar + + def _eval_subs(self, old, new): + from sympy.calculus.accumulationbounds import AccumBounds + + if isinstance(self.exp, AccumBounds): + b = self.base.subs(old, new) + e = self.exp.subs(old, new) + if isinstance(e, AccumBounds): + return e.__rpow__(b) + return self.func(b, e) + + from sympy.functions.elementary.exponential import exp, log + + def _check(ct1, ct2, old): + """Return (bool, pow, remainder_pow) where, if bool is True, then the + exponent of Pow `old` will combine with `pow` so the substitution + is valid, otherwise bool will be False. + + For noncommutative objects, `pow` will be an integer, and a factor + `Pow(old.base, remainder_pow)` needs to be included. If there is + no such factor, None is returned. For commutative objects, + remainder_pow is always None. + + cti are the coefficient and terms of an exponent of self or old + In this _eval_subs routine a change like (b**(2*x)).subs(b**x, y) + will give y**2 since (b**x)**2 == b**(2*x); if that equality does + not hold then the substitution should not occur so `bool` will be + False. + + """ + coeff1, terms1 = ct1 + coeff2, terms2 = ct2 + if terms1 == terms2: + if old.is_commutative: + # Allow fractional powers for commutative objects + pow = coeff1/coeff2 + try: + as_int(pow, strict=False) + combines = True + except ValueError: + b, e = old.as_base_exp() + # These conditions ensure that (b**e)**f == b**(e*f) for any f + combines = b.is_positive and e.is_real or b.is_nonnegative and e.is_nonnegative + + return combines, pow, None + else: + # With noncommutative symbols, substitute only integer powers + if not isinstance(terms1, tuple): + terms1 = (terms1,) + if not all(term.is_integer for term in terms1): + return False, None, None + + try: + # Round pow toward zero + pow, remainder = divmod(as_int(coeff1), as_int(coeff2)) + if pow < 0 and remainder != 0: + pow += 1 + remainder -= as_int(coeff2) + + if remainder == 0: + remainder_pow = None + else: + remainder_pow = Mul(remainder, *terms1) + + return True, pow, remainder_pow + except ValueError: + # Can't substitute + pass + + return False, None, None + + if old == self.base or (old == exp and self.base == S.Exp1): + if new.is_Function and isinstance(new, Callable): + return new(self.exp._subs(old, new)) + else: + return new**self.exp._subs(old, new) + + # issue 10829: (4**x - 3*y + 2).subs(2**x, y) -> y**2 - 3*y + 2 + if isinstance(old, self.func) and self.exp == old.exp: + l = log(self.base, old.base) + if l.is_Number: + return Pow(new, l) + + if isinstance(old, self.func) and self.base == old.base: + if self.exp.is_Add is False: + ct1 = self.exp.as_independent(Symbol, as_Add=False) + ct2 = old.exp.as_independent(Symbol, as_Add=False) + ok, pow, remainder_pow = _check(ct1, ct2, old) + if ok: + # issue 5180: (x**(6*y)).subs(x**(3*y),z)->z**2 + result = self.func(new, pow) + if remainder_pow is not None: + result = Mul(result, Pow(old.base, remainder_pow)) + return result + else: # b**(6*x + a).subs(b**(3*x), y) -> y**2 * b**a + # exp(exp(x) + exp(x**2)).subs(exp(exp(x)), w) -> w * exp(exp(x**2)) + oarg = old.exp + new_l = [] + o_al = [] + ct2 = oarg.as_coeff_mul() + for a in self.exp.args: + newa = a._subs(old, new) + ct1 = newa.as_coeff_mul() + ok, pow, remainder_pow = _check(ct1, ct2, old) + if ok: + new_l.append(new**pow) + if remainder_pow is not None: + o_al.append(remainder_pow) + continue + elif not old.is_commutative and not newa.is_integer: + # If any term in the exponent is non-integer, + # we do not do any substitutions in the noncommutative case + return + o_al.append(newa) + if new_l: + expo = Add(*o_al) + new_l.append(Pow(self.base, expo, evaluate=False) if expo != 1 else self.base) + return Mul(*new_l) + + if (isinstance(old, exp) or (old.is_Pow and old.base is S.Exp1)) and self.exp.is_extended_real and self.base.is_positive: + ct1 = old.exp.as_independent(Symbol, as_Add=False) + ct2 = (self.exp*log(self.base)).as_independent( + Symbol, as_Add=False) + ok, pow, remainder_pow = _check(ct1, ct2, old) + if ok: + result = self.func(new, pow) # (2**x).subs(exp(x*log(2)), z) -> z + if remainder_pow is not None: + result = Mul(result, Pow(old.base, remainder_pow)) + return result + + def as_base_exp(self): + """Return base and exp of self. + + Explanation + =========== + + If base a Rational less than 1, then return 1/Rational, -exp. + If this extra processing is not needed, the base and exp + properties will give the raw arguments. + + Examples + ======== + + >>> from sympy import Pow, S + >>> p = Pow(S.Half, 2, evaluate=False) + >>> p.as_base_exp() + (2, -2) + >>> p.args + (1/2, 2) + >>> p.base, p.exp + (1/2, 2) + + """ + + b, e = self.args + if b.is_Rational and b.p < b.q and b.p > 0: + return 1/b, -e + return b, e + + def _eval_adjoint(self): + from sympy.functions.elementary.complexes import adjoint + i, p = self.exp.is_integer, self.base.is_positive + if i: + return adjoint(self.base)**self.exp + if p: + return self.base**adjoint(self.exp) + if i is False and p is False: + expanded = expand_complex(self) + if expanded != self: + return adjoint(expanded) + + def _eval_conjugate(self): + from sympy.functions.elementary.complexes import conjugate as c + i, p = self.exp.is_integer, self.base.is_positive + if i: + return c(self.base)**self.exp + if p: + return self.base**c(self.exp) + if i is False and p is False: + expanded = expand_complex(self) + if expanded != self: + return c(expanded) + if self.is_extended_real: + return self + + def _eval_transpose(self): + from sympy.functions.elementary.complexes import transpose + if self.base == S.Exp1: + return self.func(S.Exp1, self.exp.transpose()) + i, p = self.exp.is_integer, (self.base.is_complex or self.base.is_infinite) + if p: + return self.base**self.exp + if i: + return transpose(self.base)**self.exp + if i is False and p is False: + expanded = expand_complex(self) + if expanded != self: + return transpose(expanded) + + def _eval_expand_power_exp(self, **hints): + """a**(n + m) -> a**n*a**m""" + b = self.base + e = self.exp + if b == S.Exp1: + from sympy.concrete.summations import Sum + if isinstance(e, Sum) and e.is_commutative: + from sympy.concrete.products import Product + return Product(self.func(b, e.function), *e.limits) + if e.is_Add and (hints.get('force', False) or + b.is_zero is False or e._all_nonneg_or_nonppos()): + if e.is_commutative: + return Mul(*[self.func(b, x) for x in e.args]) + if b.is_commutative: + c, nc = sift(e.args, lambda x: x.is_commutative, binary=True) + if c: + return Mul(*[self.func(b, x) for x in c] + )*b**Add._from_args(nc) + return self + + def _eval_expand_power_base(self, **hints): + """(a*b)**n -> a**n * b**n""" + force = hints.get('force', False) + + b = self.base + e = self.exp + if not b.is_Mul: + return self + + cargs, nc = b.args_cnc(split_1=False) + + # expand each term - this is top-level-only + # expansion but we have to watch out for things + # that don't have an _eval_expand method + if nc: + nc = [i._eval_expand_power_base(**hints) + if hasattr(i, '_eval_expand_power_base') else i + for i in nc] + + if e.is_Integer: + if e.is_positive: + rv = Mul(*nc*e) + else: + rv = Mul(*[i**-1 for i in nc[::-1]]*-e) + if cargs: + rv *= Mul(*cargs)**e + return rv + + if not cargs: + return self.func(Mul(*nc), e, evaluate=False) + + nc = [Mul(*nc)] + + # sift the commutative bases + other, maybe_real = sift(cargs, lambda x: x.is_extended_real is False, + binary=True) + def pred(x): + if x is S.ImaginaryUnit: + return S.ImaginaryUnit + polar = x.is_polar + if polar: + return True + if polar is None: + return fuzzy_bool(x.is_extended_nonnegative) + sifted = sift(maybe_real, pred) + nonneg = sifted[True] + other += sifted[None] + neg = sifted[False] + imag = sifted[S.ImaginaryUnit] + if imag: + I = S.ImaginaryUnit + i = len(imag) % 4 + if i == 0: + pass + elif i == 1: + other.append(I) + elif i == 2: + if neg: + nonn = -neg.pop() + if nonn is not S.One: + nonneg.append(nonn) + else: + neg.append(S.NegativeOne) + else: + if neg: + nonn = -neg.pop() + if nonn is not S.One: + nonneg.append(nonn) + else: + neg.append(S.NegativeOne) + other.append(I) + del imag + + # bring out the bases that can be separated from the base + + if force or e.is_integer: + # treat all commutatives the same and put nc in other + cargs = nonneg + neg + other + other = nc + else: + # this is just like what is happening automatically, except + # that now we are doing it for an arbitrary exponent for which + # no automatic expansion is done + + assert not e.is_Integer + + # handle negatives by making them all positive and putting + # the residual -1 in other + if len(neg) > 1: + o = S.One + if not other and neg[0].is_Number: + o *= neg.pop(0) + if len(neg) % 2: + o = -o + for n in neg: + nonneg.append(-n) + if o is not S.One: + other.append(o) + elif neg and other: + if neg[0].is_Number and neg[0] is not S.NegativeOne: + other.append(S.NegativeOne) + nonneg.append(-neg[0]) + else: + other.extend(neg) + else: + other.extend(neg) + del neg + + cargs = nonneg + other += nc + + rv = S.One + if cargs: + if e.is_Rational: + npow, cargs = sift(cargs, lambda x: x.is_Pow and + x.exp.is_Rational and x.base.is_number, + binary=True) + rv = Mul(*[self.func(b.func(*b.args), e) for b in npow]) + rv *= Mul(*[self.func(b, e, evaluate=False) for b in cargs]) + if other: + rv *= self.func(Mul(*other), e, evaluate=False) + return rv + + def _eval_expand_multinomial(self, **hints): + """(a + b + ..)**n -> a**n + n*a**(n-1)*b + .., n is nonzero integer""" + + base, exp = self.args + result = self + + if exp.is_Rational and exp.p > 0 and base.is_Add: + if not exp.is_Integer: + n = Integer(exp.p // exp.q) + + if not n: + return result + else: + radical, result = self.func(base, exp - n), [] + + expanded_base_n = self.func(base, n) + if expanded_base_n.is_Pow: + expanded_base_n = \ + expanded_base_n._eval_expand_multinomial() + for term in Add.make_args(expanded_base_n): + result.append(term*radical) + + return Add(*result) + + n = int(exp) + + if base.is_commutative: + order_terms, other_terms = [], [] + + for b in base.args: + if b.is_Order: + order_terms.append(b) + else: + other_terms.append(b) + + if order_terms: + # (f(x) + O(x^n))^m -> f(x)^m + m*f(x)^{m-1} *O(x^n) + f = Add(*other_terms) + o = Add(*order_terms) + + if n == 2: + return expand_multinomial(f**n, deep=False) + n*f*o + else: + g = expand_multinomial(f**(n - 1), deep=False) + return expand_mul(f*g, deep=False) + n*g*o + + if base.is_number: + # Efficiently expand expressions of the form (a + b*I)**n + # where 'a' and 'b' are real numbers and 'n' is integer. + a, b = base.as_real_imag() + + if a.is_Rational and b.is_Rational: + if not a.is_Integer: + if not b.is_Integer: + k = self.func(a.q * b.q, n) + a, b = a.p*b.q, a.q*b.p + else: + k = self.func(a.q, n) + a, b = a.p, a.q*b + elif not b.is_Integer: + k = self.func(b.q, n) + a, b = a*b.q, b.p + else: + k = 1 + + a, b, c, d = int(a), int(b), 1, 0 + + while n: + if n & 1: + c, d = a*c - b*d, b*c + a*d + n -= 1 + a, b = a*a - b*b, 2*a*b + n //= 2 + + I = S.ImaginaryUnit + + if k == 1: + return c + I*d + else: + return Integer(c)/k + I*d/k + + p = other_terms + # (x + y)**3 -> x**3 + 3*x**2*y + 3*x*y**2 + y**3 + # in this particular example: + # p = [x,y]; n = 3 + # so now it's easy to get the correct result -- we get the + # coefficients first: + from sympy.ntheory.multinomial import multinomial_coefficients + from sympy.polys.polyutils import basic_from_dict + expansion_dict = multinomial_coefficients(len(p), n) + # in our example: {(3, 0): 1, (1, 2): 3, (0, 3): 1, (2, 1): 3} + # and now construct the expression. + return basic_from_dict(expansion_dict, *p) + else: + if n == 2: + return Add(*[f*g for f in base.args for g in base.args]) + else: + multi = (base**(n - 1))._eval_expand_multinomial() + if multi.is_Add: + return Add(*[f*g for f in base.args + for g in multi.args]) + else: + # XXX can this ever happen if base was an Add? + return Add(*[f*multi for f in base.args]) + elif (exp.is_Rational and exp.p < 0 and base.is_Add and + abs(exp.p) > exp.q): + return 1 / self.func(base, -exp)._eval_expand_multinomial() + elif exp.is_Add and base.is_Number and (hints.get('force', False) or + base.is_zero is False or exp._all_nonneg_or_nonppos()): + # a + b a b + # n --> n n, where n, a, b are Numbers + # XXX should be in expand_power_exp? + coeff, tail = [], [] + for term in exp.args: + if term.is_Number: + coeff.append(self.func(base, term)) + else: + tail.append(term) + return Mul(*(coeff + [self.func(base, Add._from_args(tail))])) + else: + return result + + def as_real_imag(self, deep=True, **hints): + if self.exp.is_Integer: + from sympy.polys.polytools import poly + + exp = self.exp + re_e, im_e = self.base.as_real_imag(deep=deep) + if not im_e: + return self, S.Zero + a, b = symbols('a b', cls=Dummy) + if exp >= 0: + if re_e.is_Number and im_e.is_Number: + # We can be more efficient in this case + expr = expand_multinomial(self.base**exp) + if expr != self: + return expr.as_real_imag() + + expr = poly( + (a + b)**exp) # a = re, b = im; expr = (a + b*I)**exp + else: + mag = re_e**2 + im_e**2 + re_e, im_e = re_e/mag, -im_e/mag + if re_e.is_Number and im_e.is_Number: + # We can be more efficient in this case + expr = expand_multinomial((re_e + im_e*S.ImaginaryUnit)**-exp) + if expr != self: + return expr.as_real_imag() + + expr = poly((a + b)**-exp) + + # Terms with even b powers will be real + r = [i for i in expr.terms() if not i[0][1] % 2] + re_part = Add(*[cc*a**aa*b**bb for (aa, bb), cc in r]) + # Terms with odd b powers will be imaginary + r = [i for i in expr.terms() if i[0][1] % 4 == 1] + im_part1 = Add(*[cc*a**aa*b**bb for (aa, bb), cc in r]) + r = [i for i in expr.terms() if i[0][1] % 4 == 3] + im_part3 = Add(*[cc*a**aa*b**bb for (aa, bb), cc in r]) + + return (re_part.subs({a: re_e, b: S.ImaginaryUnit*im_e}), + im_part1.subs({a: re_e, b: im_e}) + im_part3.subs({a: re_e, b: -im_e})) + + from sympy.functions.elementary.trigonometric import atan2, cos, sin + + if self.exp.is_Rational: + re_e, im_e = self.base.as_real_imag(deep=deep) + + if im_e.is_zero and self.exp is S.Half: + if re_e.is_extended_nonnegative: + return self, S.Zero + if re_e.is_extended_nonpositive: + return S.Zero, (-self.base)**self.exp + + # XXX: This is not totally correct since for x**(p/q) with + # x being imaginary there are actually q roots, but + # only a single one is returned from here. + r = self.func(self.func(re_e, 2) + self.func(im_e, 2), S.Half) + + t = atan2(im_e, re_e) + + rp, tp = self.func(r, self.exp), t*self.exp + + return rp*cos(tp), rp*sin(tp) + elif self.base is S.Exp1: + from sympy.functions.elementary.exponential import exp + re_e, im_e = self.exp.as_real_imag() + if deep: + re_e = re_e.expand(deep, **hints) + im_e = im_e.expand(deep, **hints) + c, s = cos(im_e), sin(im_e) + return exp(re_e)*c, exp(re_e)*s + else: + from sympy.functions.elementary.complexes import im, re + if deep: + hints['complex'] = False + + expanded = self.expand(deep, **hints) + if hints.get('ignore') == expanded: + return None + else: + return (re(expanded), im(expanded)) + else: + return re(self), im(self) + + def _eval_derivative(self, s): + from sympy.functions.elementary.exponential import log + dbase = self.base.diff(s) + dexp = self.exp.diff(s) + return self * (dexp * log(self.base) + dbase * self.exp/self.base) + + def _eval_evalf(self, prec): + base, exp = self.as_base_exp() + if base == S.Exp1: + # Use mpmath function associated to class "exp": + from sympy.functions.elementary.exponential import exp as exp_function + return exp_function(self.exp, evaluate=False)._eval_evalf(prec) + base = base._evalf(prec) + if not exp.is_Integer: + exp = exp._evalf(prec) + if exp.is_negative and base.is_number and base.is_extended_real is False: + base = base.conjugate() / (base * base.conjugate())._evalf(prec) + exp = -exp + return self.func(base, exp).expand() + return self.func(base, exp) + + def _eval_is_polynomial(self, syms): + if self.exp.has(*syms): + return False + + if self.base.has(*syms): + return bool(self.base._eval_is_polynomial(syms) and + self.exp.is_Integer and (self.exp >= 0)) + else: + return True + + def _eval_is_rational(self): + # The evaluation of self.func below can be very expensive in the case + # of integer**integer if the exponent is large. We should try to exit + # before that if possible: + if (self.exp.is_integer and self.base.is_rational + and fuzzy_not(fuzzy_and([self.exp.is_negative, self.base.is_zero]))): + return True + p = self.func(*self.as_base_exp()) # in case it's unevaluated + if not p.is_Pow: + return p.is_rational + b, e = p.as_base_exp() + if e.is_Rational and b.is_Rational: + # we didn't check that e is not an Integer + # because Rational**Integer autosimplifies + return False + if e.is_integer: + if b.is_rational: + if fuzzy_not(b.is_zero) or e.is_nonnegative: + return True + if b == e: # always rational, even for 0**0 + return True + elif b.is_irrational: + return e.is_zero + if b is S.Exp1: + if e.is_rational and e.is_nonzero: + return False + + def _eval_is_algebraic(self): + def _is_one(expr): + try: + return (expr - 1).is_zero + except ValueError: + # when the operation is not allowed + return False + + if self.base.is_zero or _is_one(self.base): + return True + elif self.base is S.Exp1: + s = self.func(*self.args) + if s.func == self.func: + if self.exp.is_nonzero: + if self.exp.is_algebraic: + return False + elif (self.exp/S.Pi).is_rational: + return False + elif (self.exp/(S.ImaginaryUnit*S.Pi)).is_rational: + return True + else: + return s.is_algebraic + elif self.exp.is_rational: + if self.base.is_algebraic is False: + return self.exp.is_zero + if self.base.is_zero is False: + if self.exp.is_nonzero: + return self.base.is_algebraic + elif self.base.is_algebraic: + return True + if self.exp.is_positive: + return self.base.is_algebraic + elif self.base.is_algebraic and self.exp.is_algebraic: + if ((fuzzy_not(self.base.is_zero) + and fuzzy_not(_is_one(self.base))) + or self.base.is_integer is False + or self.base.is_irrational): + return self.exp.is_rational + + def _eval_is_rational_function(self, syms): + if self.exp.has(*syms): + return False + + if self.base.has(*syms): + return self.base._eval_is_rational_function(syms) and \ + self.exp.is_Integer + else: + return True + + def _eval_is_meromorphic(self, x, a): + # f**g is meromorphic if g is an integer and f is meromorphic. + # E**(log(f)*g) is meromorphic if log(f)*g is meromorphic + # and finite. + base_merom = self.base._eval_is_meromorphic(x, a) + exp_integer = self.exp.is_Integer + if exp_integer: + return base_merom + + exp_merom = self.exp._eval_is_meromorphic(x, a) + if base_merom is False: + # f**g = E**(log(f)*g) may be meromorphic if the + # singularities of log(f) and g cancel each other, + # for example, if g = 1/log(f). Hence, + return False if exp_merom else None + elif base_merom is None: + return None + + b = self.base.subs(x, a) + # b is extended complex as base is meromorphic. + # log(base) is finite and meromorphic when b != 0, zoo. + b_zero = b.is_zero + if b_zero: + log_defined = False + else: + log_defined = fuzzy_and((b.is_finite, fuzzy_not(b_zero))) + + if log_defined is False: # zero or pole of base + return exp_integer # False or None + elif log_defined is None: + return None + + if not exp_merom: + return exp_merom # False or None + + return self.exp.subs(x, a).is_finite + + def _eval_is_algebraic_expr(self, syms): + if self.exp.has(*syms): + return False + + if self.base.has(*syms): + return self.base._eval_is_algebraic_expr(syms) and \ + self.exp.is_Rational + else: + return True + + def _eval_rewrite_as_exp(self, base, expo, **kwargs): + from sympy.functions.elementary.exponential import exp, log + + if base.is_zero or base.has(exp) or expo.has(exp): + return base**expo + + evaluate = expo.has(Symbol) + + if base.has(Symbol): + # delay evaluation if expo is non symbolic + # (as exp(x*log(5)) automatically reduces to x**5) + if global_parameters.exp_is_pow: + return Pow(S.Exp1, log(base)*expo, evaluate=evaluate) + else: + return exp(log(base)*expo, evaluate=evaluate) + + else: + from sympy.functions.elementary.complexes import arg, Abs + return exp((log(Abs(base)) + S.ImaginaryUnit*arg(base))*expo) + + def as_numer_denom(self): + if not self.is_commutative: + return self, S.One + base, exp = self.as_base_exp() + n, d = base.as_numer_denom() + # this should be the same as ExpBase.as_numer_denom wrt + # exponent handling + neg_exp = exp.is_negative + if exp.is_Mul and not neg_exp and not exp.is_positive: + neg_exp = exp.could_extract_minus_sign() + int_exp = exp.is_integer + # the denominator cannot be separated from the numerator if + # its sign is unknown unless the exponent is an integer, e.g. + # sqrt(a/b) != sqrt(a)/sqrt(b) when a=1 and b=-1. But if the + # denominator is negative the numerator and denominator can + # be negated and the denominator (now positive) separated. + if not (d.is_extended_real or int_exp): + n = base + d = S.One + dnonpos = d.is_nonpositive + if dnonpos: + n, d = -n, -d + elif dnonpos is None and not int_exp: + n = base + d = S.One + if neg_exp: + n, d = d, n + exp = -exp + if exp.is_infinite: + if n is S.One and d is not S.One: + return n, self.func(d, exp) + if n is not S.One and d is S.One: + return self.func(n, exp), d + return self.func(n, exp), self.func(d, exp) + + def matches(self, expr, repl_dict=None, old=False): + expr = _sympify(expr) + if repl_dict is None: + repl_dict = {} + + # special case, pattern = 1 and expr.exp can match to 0 + if expr is S.One: + d = self.exp.matches(S.Zero, repl_dict) + if d is not None: + return d + + # make sure the expression to be matched is an Expr + if not isinstance(expr, Expr): + return None + + b, e = expr.as_base_exp() + + # special case number + sb, se = self.as_base_exp() + if sb.is_Symbol and se.is_Integer and expr: + if e.is_rational: + return sb.matches(b**(e/se), repl_dict) + return sb.matches(expr**(1/se), repl_dict) + + d = repl_dict.copy() + d = self.base.matches(b, d) + if d is None: + return None + + d = self.exp.xreplace(d).matches(e, d) + if d is None: + return Expr.matches(self, expr, repl_dict) + return d + + def _eval_nseries(self, x, n, logx, cdir=0): + # NOTE! This function is an important part of the gruntz algorithm + # for computing limits. It has to return a generalized power + # series with coefficients in C(log, log(x)). In more detail: + # It has to return an expression + # c_0*x**e_0 + c_1*x**e_1 + ... (finitely many terms) + # where e_i are numbers (not necessarily integers) and c_i are + # expressions involving only numbers, the log function, and log(x). + # The series expansion of b**e is computed as follows: + # 1) We express b as f*(1 + g) where f is the leading term of b. + # g has order O(x**d) where d is strictly positive. + # 2) Then b**e = (f**e)*((1 + g)**e). + # (1 + g)**e is computed using binomial series. + from sympy.functions.elementary.exponential import exp, log + from sympy.series.limits import limit + from sympy.series.order import Order + from sympy.core.sympify import sympify + if self.base is S.Exp1: + e_series = self.exp.nseries(x, n=n, logx=logx) + if e_series.is_Order: + return 1 + e_series + e0 = limit(e_series.removeO(), x, 0) + if e0 is S.NegativeInfinity: + return Order(x**n, x) + if e0 is S.Infinity: + return self + t = e_series - e0 + exp_series = term = exp(e0) + # series of exp(e0 + t) in t + for i in range(1, n): + term *= t/i + term = term.nseries(x, n=n, logx=logx) + exp_series += term + exp_series += Order(t**n, x) + from sympy.simplify.powsimp import powsimp + return powsimp(exp_series, deep=True, combine='exp') + from sympy.simplify.powsimp import powdenest + from .numbers import _illegal + self = powdenest(self, force=True).trigsimp() + b, e = self.as_base_exp() + + if e.has(*_illegal): + raise PoleError() + + if e.has(x): + return exp(e*log(b))._eval_nseries(x, n=n, logx=logx, cdir=cdir) + + if logx is not None and b.has(log): + from .symbol import Wild + c, ex = symbols('c, ex', cls=Wild, exclude=[x]) + b = b.replace(log(c*x**ex), log(c) + ex*logx) + self = b**e + + b = b.removeO() + try: + from sympy.functions.special.gamma_functions import polygamma + if b.has(polygamma, S.EulerGamma) and logx is not None: + raise ValueError() + _, m = b.leadterm(x) + except (ValueError, NotImplementedError, PoleError): + b = b._eval_nseries(x, n=max(2, n), logx=logx, cdir=cdir).removeO() + if b.has(S.NaN, S.ComplexInfinity): + raise NotImplementedError() + _, m = b.leadterm(x) + + if e.has(log): + from sympy.simplify.simplify import logcombine + e = logcombine(e).cancel() + + if not (m.is_zero or e.is_number and e.is_real): + if self == self._eval_as_leading_term(x, logx=logx, cdir=cdir): + res = exp(e*log(b))._eval_nseries(x, n=n, logx=logx, cdir=cdir) + if res == exp(e*log(b)): + return self + return res + + f = b.as_leading_term(x, logx=logx) + g = (b/f - S.One).cancel(expand=False) + if not m.is_number: + raise NotImplementedError() + maxpow = n - m*e + if maxpow.has(Symbol): + maxpow = sympify(n) + + if maxpow.is_negative: + return Order(x**(m*e), x) + + if g.is_zero: + r = f**e + if r != self: + r += Order(x**n, x) + return r + + def coeff_exp(term, x): + coeff, exp = S.One, S.Zero + for factor in Mul.make_args(term): + if factor.has(x): + base, exp = factor.as_base_exp() + if base != x: + try: + return term.leadterm(x) + except ValueError: + return term, S.Zero + else: + coeff *= factor + return coeff, exp + + def mul(d1, d2): + res = {} + for e1, e2 in product(d1, d2): + ex = e1 + e2 + if ex < maxpow: + res[ex] = res.get(ex, S.Zero) + d1[e1]*d2[e2] + return res + + try: + c, d = g.leadterm(x, logx=logx) + except (ValueError, NotImplementedError): + if limit(g/x**maxpow, x, 0) == 0: + # g has higher order zero + return f**e + e*f**e*g # first term of binomial series + else: + raise NotImplementedError() + if c.is_Float and d == S.Zero: + # Convert floats like 0.5 to exact SymPy numbers like S.Half, to + # prevent rounding errors which can induce wrong values of d leading + # to a NotImplementedError being returned from the block below. + from sympy.simplify.simplify import nsimplify + _, d = nsimplify(g).leadterm(x, logx=logx) + if not d.is_positive: + g = g.simplify() + if g.is_zero: + return f**e + _, d = g.leadterm(x, logx=logx) + if not d.is_positive: + g = ((b - f)/f).expand() + _, d = g.leadterm(x, logx=logx) + if not d.is_positive: + raise NotImplementedError() + + from sympy.functions.elementary.integers import ceiling + gpoly = g._eval_nseries(x, n=ceiling(maxpow), logx=logx, cdir=cdir).removeO() + gterms = {} + + for term in Add.make_args(gpoly): + co1, e1 = coeff_exp(term, x) + gterms[e1] = gterms.get(e1, S.Zero) + co1 + + k = S.One + terms = {S.Zero: S.One} + tk = gterms + + from sympy.functions.combinatorial.factorials import factorial, ff + + while (k*d - maxpow).is_negative: + coeff = ff(e, k)/factorial(k) + for ex in tk: + terms[ex] = terms.get(ex, S.Zero) + coeff*tk[ex] + tk = mul(tk, gterms) + k += S.One + + from sympy.functions.elementary.complexes import im + + if not e.is_integer and m.is_zero and f.is_negative: + ndir = (b - f).dir(x, cdir) + if im(ndir).is_negative: + inco, inex = coeff_exp(f**e*(-1)**(-2*e), x) + elif im(ndir).is_zero: + inco, inex = coeff_exp(exp(e*log(b)).as_leading_term(x, logx=logx, cdir=cdir), x) + else: + inco, inex = coeff_exp(f**e, x) + else: + inco, inex = coeff_exp(f**e, x) + res = S.Zero + + for e1 in terms: + ex = e1 + inex + res += terms[e1]*inco*x**(ex) + + if not (e.is_integer and e.is_positive and (e*d - n).is_nonpositive and + res == _mexpand(self)): + try: + res += Order(x**n, x) + except NotImplementedError: + return exp(e*log(b))._eval_nseries(x, n=n, logx=logx, cdir=cdir) + return res + + def _eval_as_leading_term(self, x, logx=None, cdir=0): + from sympy.functions.elementary.exponential import exp, log + e = self.exp + b = self.base + if self.base is S.Exp1: + arg = e.as_leading_term(x, logx=logx) + arg0 = arg.subs(x, 0) + if arg0 is S.NaN: + arg0 = arg.limit(x, 0) + if arg0.is_infinite is False: + return S.Exp1**arg0 + raise PoleError("Cannot expand %s around 0" % (self)) + elif e.has(x): + lt = exp(e * log(b)) + return lt.as_leading_term(x, logx=logx, cdir=cdir) + else: + from sympy.functions.elementary.complexes import im + try: + f = b.as_leading_term(x, logx=logx, cdir=cdir) + except PoleError: + return self + if not e.is_integer and f.is_negative and not f.has(x): + ndir = (b - f).dir(x, cdir) + if im(ndir).is_negative: + # Normally, f**e would evaluate to exp(e*log(f)) but on branch cuts + # an other value is expected through the following computation + # exp(e*(log(f) - 2*pi*I)) == f**e*exp(-2*e*pi*I) == f**e*(-1)**(-2*e). + return self.func(f, e) * (-1)**(-2*e) + elif im(ndir).is_zero: + log_leadterm = log(b)._eval_as_leading_term(x, logx=logx, cdir=cdir) + if log_leadterm.is_infinite is False: + return exp(e*log_leadterm) + return self.func(f, e) + + @cacheit + def _taylor_term(self, n, x, *previous_terms): # of (1 + x)**e + from sympy.functions.combinatorial.factorials import binomial + return binomial(self.exp, n) * self.func(x, n) + + def taylor_term(self, n, x, *previous_terms): + if self.base is not S.Exp1: + return super().taylor_term(n, x, *previous_terms) + if n < 0: + return S.Zero + if n == 0: + return S.One + from .sympify import sympify + x = sympify(x) + if previous_terms: + p = previous_terms[-1] + if p is not None: + return p * x / n + from sympy.functions.combinatorial.factorials import factorial + return x**n/factorial(n) + + def _eval_rewrite_as_sin(self, base, exp, **hints): + if self.base is S.Exp1: + from sympy.functions.elementary.trigonometric import sin + return sin(S.ImaginaryUnit*self.exp + S.Pi/2) - S.ImaginaryUnit*sin(S.ImaginaryUnit*self.exp) + + def _eval_rewrite_as_cos(self, base, exp, **hints): + if self.base is S.Exp1: + from sympy.functions.elementary.trigonometric import cos + return cos(S.ImaginaryUnit*self.exp) + S.ImaginaryUnit*cos(S.ImaginaryUnit*self.exp + S.Pi/2) + + def _eval_rewrite_as_tanh(self, base, exp, **hints): + if self.base is S.Exp1: + from sympy.functions.elementary.hyperbolic import tanh + return (1 + tanh(self.exp/2))/(1 - tanh(self.exp/2)) + + def _eval_rewrite_as_sqrt(self, base, exp, **kwargs): + from sympy.functions.elementary.trigonometric import sin, cos + if base is not S.Exp1: + return None + if exp.is_Mul: + coeff = exp.coeff(S.Pi * S.ImaginaryUnit) + if coeff and coeff.is_number: + cosine, sine = cos(S.Pi*coeff), sin(S.Pi*coeff) + if not isinstance(cosine, cos) and not isinstance (sine, sin): + return cosine + S.ImaginaryUnit*sine + + def as_content_primitive(self, radical=False, clear=True): + """Return the tuple (R, self/R) where R is the positive Rational + extracted from self. + + Examples + ======== + + >>> from sympy import sqrt + >>> sqrt(4 + 4*sqrt(2)).as_content_primitive() + (2, sqrt(1 + sqrt(2))) + >>> sqrt(3 + 3*sqrt(2)).as_content_primitive() + (1, sqrt(3)*sqrt(1 + sqrt(2))) + + >>> from sympy import expand_power_base, powsimp, Mul + >>> from sympy.abc import x, y + + >>> ((2*x + 2)**2).as_content_primitive() + (4, (x + 1)**2) + >>> (4**((1 + y)/2)).as_content_primitive() + (2, 4**(y/2)) + >>> (3**((1 + y)/2)).as_content_primitive() + (1, 3**((y + 1)/2)) + >>> (3**((5 + y)/2)).as_content_primitive() + (9, 3**((y + 1)/2)) + >>> eq = 3**(2 + 2*x) + >>> powsimp(eq) == eq + True + >>> eq.as_content_primitive() + (9, 3**(2*x)) + >>> powsimp(Mul(*_)) + 3**(2*x + 2) + + >>> eq = (2 + 2*x)**y + >>> s = expand_power_base(eq); s.is_Mul, s + (False, (2*x + 2)**y) + >>> eq.as_content_primitive() + (1, (2*(x + 1))**y) + >>> s = expand_power_base(_[1]); s.is_Mul, s + (True, 2**y*(x + 1)**y) + + See docstring of Expr.as_content_primitive for more examples. + """ + + b, e = self.as_base_exp() + b = _keep_coeff(*b.as_content_primitive(radical=radical, clear=clear)) + ce, pe = e.as_content_primitive(radical=radical, clear=clear) + if b.is_Rational: + #e + #= ce*pe + #= ce*(h + t) + #= ce*h + ce*t + #=> self + #= b**(ce*h)*b**(ce*t) + #= b**(cehp/cehq)*b**(ce*t) + #= b**(iceh + r/cehq)*b**(ce*t) + #= b**(iceh)*b**(r/cehq)*b**(ce*t) + #= b**(iceh)*b**(ce*t + r/cehq) + h, t = pe.as_coeff_Add() + if h.is_Rational and b != S.Zero: + ceh = ce*h + c = self.func(b, ceh) + r = S.Zero + if not c.is_Rational: + iceh, r = divmod(ceh.p, ceh.q) + c = self.func(b, iceh) + return c, self.func(b, _keep_coeff(ce, t + r/ce/ceh.q)) + e = _keep_coeff(ce, pe) + # b**e = (h*t)**e = h**e*t**e = c*m*t**e + if e.is_Rational and b.is_Mul: + h, t = b.as_content_primitive(radical=radical, clear=clear) # h is positive + c, m = self.func(h, e).as_coeff_Mul() # so c is positive + m, me = m.as_base_exp() + if m is S.One or me == e: # probably always true + # return the following, not return c, m*Pow(t, e) + # which would change Pow into Mul; we let SymPy + # decide what to do by using the unevaluated Mul, e.g + # should it stay as sqrt(2 + 2*sqrt(5)) or become + # sqrt(2)*sqrt(1 + sqrt(5)) + return c, self.func(_keep_coeff(m, t), e) + return S.One, self.func(b, e) + + def is_constant(self, *wrt, **flags): + expr = self + if flags.get('simplify', True): + expr = expr.simplify() + b, e = expr.as_base_exp() + bz = b.equals(0) + if bz: # recalculate with assumptions in case it's unevaluated + new = b**e + if new != expr: + return new.is_constant() + econ = e.is_constant(*wrt) + bcon = b.is_constant(*wrt) + if bcon: + if econ: + return True + bz = b.equals(0) + if bz is False: + return False + elif bcon is None: + return None + + return e.equals(0) + + def _eval_difference_delta(self, n, step): + b, e = self.args + if e.has(n) and not b.has(n): + new_e = e.subs(n, n + step) + return (b**(new_e - e) - 1) * self + +power = Dispatcher('power') +power.add((object, object), Pow) + +from .add import Add +from .numbers import Integer, Rational +from .mul import Mul, _keep_coeff +from .symbol import Symbol, Dummy, symbols diff --git a/lib/python3.12/site-packages/sympy/core/random.py b/lib/python3.12/site-packages/sympy/core/random.py new file mode 100644 index 0000000000000000000000000000000000000000..c02986283523b39462a1e2c0b97e3fb230cff100 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/core/random.py @@ -0,0 +1,227 @@ +""" +When you need to use random numbers in SymPy library code, import from here +so there is only one generator working for SymPy. Imports from here should +behave the same as if they were being imported from Python's random module. +But only the routines currently used in SymPy are included here. To use others +import ``rng`` and access the method directly. For example, to capture the +current state of the generator use ``rng.getstate()``. + +There is intentionally no Random to import from here. If you want +to control the state of the generator, import ``seed`` and call it +with or without an argument to set the state. + +Examples +======== + +>>> from sympy.core.random import random, seed +>>> assert random() < 1 +>>> seed(1); a = random() +>>> b = random() +>>> seed(1); c = random() +>>> assert a == c +>>> assert a != b # remote possibility this will fail + +""" +from sympy.utilities.iterables import is_sequence +from sympy.utilities.misc import as_int + +import random as _random +rng = _random.Random() + +choice = rng.choice +random = rng.random +randint = rng.randint +randrange = rng.randrange +sample = rng.sample +# seed = rng.seed +shuffle = rng.shuffle +uniform = rng.uniform + +_assumptions_rng = _random.Random() +_assumptions_shuffle = _assumptions_rng.shuffle + + +def seed(a=None, version=2): + rng.seed(a=a, version=version) + _assumptions_rng.seed(a=a, version=version) + + +def random_complex_number(a=2, b=-1, c=3, d=1, rational=False, tolerance=None): + """ + Return a random complex number. + + To reduce chance of hitting branch cuts or anything, we guarantee + b <= Im z <= d, a <= Re z <= c + + When rational is True, a rational approximation to a random number + is obtained within specified tolerance, if any. + """ + from sympy.core.numbers import I + from sympy.simplify.simplify import nsimplify + A, B = uniform(a, c), uniform(b, d) + if not rational: + return A + I*B + return (nsimplify(A, rational=True, tolerance=tolerance) + + I*nsimplify(B, rational=True, tolerance=tolerance)) + + +def verify_numerically(f, g, z=None, tol=1.0e-6, a=2, b=-1, c=3, d=1): + """ + Test numerically that f and g agree when evaluated in the argument z. + + If z is None, all symbols will be tested. This routine does not test + whether there are Floats present with precision higher than 15 digits + so if there are, your results may not be what you expect due to round- + off errors. + + Examples + ======== + + >>> from sympy import sin, cos + >>> from sympy.abc import x + >>> from sympy.core.random import verify_numerically as tn + >>> tn(sin(x)**2 + cos(x)**2, 1, x) + True + """ + from sympy.core.symbol import Symbol + from sympy.core.sympify import sympify + from sympy.core.numbers import comp + f, g = (sympify(i) for i in (f, g)) + if z is None: + z = f.free_symbols | g.free_symbols + elif isinstance(z, Symbol): + z = [z] + reps = list(zip(z, [random_complex_number(a, b, c, d) for _ in z])) + z1 = f.subs(reps).n() + z2 = g.subs(reps).n() + return comp(z1, z2, tol) + + +def test_derivative_numerically(f, z, tol=1.0e-6, a=2, b=-1, c=3, d=1): + """ + Test numerically that the symbolically computed derivative of f + with respect to z is correct. + + This routine does not test whether there are Floats present with + precision higher than 15 digits so if there are, your results may + not be what you expect due to round-off errors. + + Examples + ======== + + >>> from sympy import sin + >>> from sympy.abc import x + >>> from sympy.core.random import test_derivative_numerically as td + >>> td(sin(x), x) + True + """ + from sympy.core.numbers import comp + from sympy.core.function import Derivative + z0 = random_complex_number(a, b, c, d) + f1 = f.diff(z).subs(z, z0) + f2 = Derivative(f, z).doit_numerically(z0) + return comp(f1.n(), f2.n(), tol) + + +def _randrange(seed=None): + """Return a randrange generator. + + ``seed`` can be + + * None - return randomly seeded generator + * int - return a generator seeded with the int + * list - the values to be returned will be taken from the list + in the order given; the provided list is not modified. + + Examples + ======== + + >>> from sympy.core.random import _randrange + >>> rr = _randrange() + >>> rr(1000) # doctest: +SKIP + 999 + >>> rr = _randrange(3) + >>> rr(1000) # doctest: +SKIP + 238 + >>> rr = _randrange([0, 5, 1, 3, 4]) + >>> rr(3), rr(3) + (0, 1) + """ + if seed is None: + return randrange + elif isinstance(seed, int): + rng.seed(seed) + return randrange + elif is_sequence(seed): + seed = list(seed) # make a copy + seed.reverse() + + def give(a, b=None, seq=seed): + if b is None: + a, b = 0, a + a, b = as_int(a), as_int(b) + w = b - a + if w < 1: + raise ValueError('_randrange got empty range') + try: + x = seq.pop() + except IndexError: + raise ValueError('_randrange sequence was too short') + if a <= x < b: + return x + else: + return give(a, b, seq) + return give + else: + raise ValueError('_randrange got an unexpected seed') + + +def _randint(seed=None): + """Return a randint generator. + + ``seed`` can be + + * None - return randomly seeded generator + * int - return a generator seeded with the int + * list - the values to be returned will be taken from the list + in the order given; the provided list is not modified. + + Examples + ======== + + >>> from sympy.core.random import _randint + >>> ri = _randint() + >>> ri(1, 1000) # doctest: +SKIP + 999 + >>> ri = _randint(3) + >>> ri(1, 1000) # doctest: +SKIP + 238 + >>> ri = _randint([0, 5, 1, 2, 4]) + >>> ri(1, 3), ri(1, 3) + (1, 2) + """ + if seed is None: + return randint + elif isinstance(seed, int): + rng.seed(seed) + return randint + elif is_sequence(seed): + seed = list(seed) # make a copy + seed.reverse() + + def give(a, b, seq=seed): + a, b = as_int(a), as_int(b) + w = b - a + if w < 0: + raise ValueError('_randint got empty range') + try: + x = seq.pop() + except IndexError: + raise ValueError('_randint sequence was too short') + if a <= x <= b: + return x + else: + return give(a, b, seq) + return give + else: + raise ValueError('_randint got an unexpected seed') diff --git a/lib/python3.12/site-packages/sympy/core/relational.py b/lib/python3.12/site-packages/sympy/core/relational.py new file mode 100644 index 0000000000000000000000000000000000000000..5ccfc7e9632e650def00dab4559b209a1e6099e9 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/core/relational.py @@ -0,0 +1,1615 @@ +from __future__ import annotations + +from .basic import Atom, Basic +from .sorting import ordered +from .evalf import EvalfMixin +from .function import AppliedUndef +from .numbers import int_valued +from .singleton import S +from .sympify import _sympify, SympifyError +from .parameters import global_parameters +from .logic import fuzzy_bool, fuzzy_xor, fuzzy_and, fuzzy_not +from sympy.logic.boolalg import Boolean, BooleanAtom +from sympy.utilities.iterables import sift +from sympy.utilities.misc import filldedent +from sympy.utilities.exceptions import sympy_deprecation_warning + + +__all__ = ( + 'Rel', 'Eq', 'Ne', 'Lt', 'Le', 'Gt', 'Ge', + 'Relational', 'Equality', 'Unequality', 'StrictLessThan', 'LessThan', + 'StrictGreaterThan', 'GreaterThan', +) + +from .expr import Expr +from sympy.multipledispatch import dispatch +from .containers import Tuple +from .symbol import Symbol + + +def _nontrivBool(side): + return isinstance(side, Boolean) and \ + not isinstance(side, Atom) + + +# Note, see issue 4986. Ideally, we wouldn't want to subclass both Boolean +# and Expr. +# from .. import Expr + + +def _canonical(cond): + # return a condition in which all relationals are canonical + reps = {r: r.canonical for r in cond.atoms(Relational)} + return cond.xreplace(reps) + # XXX: AttributeError was being caught here but it wasn't triggered by any of + # the tests so I've removed it... + + +def _canonical_coeff(rel): + # return -2*x + 1 < 0 as x > 1/2 + # XXX make this part of Relational.canonical? + rel = rel.canonical + if not rel.is_Relational or rel.rhs.is_Boolean: + return rel # Eq(x, True) + b, l = rel.lhs.as_coeff_Add(rational=True) + m, lhs = l.as_coeff_Mul(rational=True) + rhs = (rel.rhs - b)/m + if m < 0: + return rel.reversed.func(lhs, rhs) + return rel.func(lhs, rhs) + + +class Relational(Boolean, EvalfMixin): + """Base class for all relation types. + + Explanation + =========== + + Subclasses of Relational should generally be instantiated directly, but + Relational can be instantiated with a valid ``rop`` value to dispatch to + the appropriate subclass. + + Parameters + ========== + + rop : str or None + Indicates what subclass to instantiate. Valid values can be found + in the keys of Relational.ValidRelationOperator. + + Examples + ======== + + >>> from sympy import Rel + >>> from sympy.abc import x, y + >>> Rel(y, x + x**2, '==') + Eq(y, x**2 + x) + + A relation's type can be defined upon creation using ``rop``. + The relation type of an existing expression can be obtained + using its ``rel_op`` property. + Here is a table of all the relation types, along with their + ``rop`` and ``rel_op`` values: + + +---------------------+----------------------------+------------+ + |Relation |``rop`` |``rel_op`` | + +=====================+============================+============+ + |``Equality`` |``==`` or ``eq`` or ``None``|``==`` | + +---------------------+----------------------------+------------+ + |``Unequality`` |``!=`` or ``ne`` |``!=`` | + +---------------------+----------------------------+------------+ + |``GreaterThan`` |``>=`` or ``ge`` |``>=`` | + +---------------------+----------------------------+------------+ + |``LessThan`` |``<=`` or ``le`` |``<=`` | + +---------------------+----------------------------+------------+ + |``StrictGreaterThan``|``>`` or ``gt`` |``>`` | + +---------------------+----------------------------+------------+ + |``StrictLessThan`` |``<`` or ``lt`` |``<`` | + +---------------------+----------------------------+------------+ + + For example, setting ``rop`` to ``==`` produces an + ``Equality`` relation, ``Eq()``. + So does setting ``rop`` to ``eq``, or leaving ``rop`` unspecified. + That is, the first three ``Rel()`` below all produce the same result. + Using a ``rop`` from a different row in the table produces a + different relation type. + For example, the fourth ``Rel()`` below using ``lt`` for ``rop`` + produces a ``StrictLessThan`` inequality: + + >>> from sympy import Rel + >>> from sympy.abc import x, y + >>> Rel(y, x + x**2, '==') + Eq(y, x**2 + x) + >>> Rel(y, x + x**2, 'eq') + Eq(y, x**2 + x) + >>> Rel(y, x + x**2) + Eq(y, x**2 + x) + >>> Rel(y, x + x**2, 'lt') + y < x**2 + x + + To obtain the relation type of an existing expression, + get its ``rel_op`` property. + For example, ``rel_op`` is ``==`` for the ``Equality`` relation above, + and ``<`` for the strict less than inequality above: + + >>> from sympy import Rel + >>> from sympy.abc import x, y + >>> my_equality = Rel(y, x + x**2, '==') + >>> my_equality.rel_op + '==' + >>> my_inequality = Rel(y, x + x**2, 'lt') + >>> my_inequality.rel_op + '<' + + """ + __slots__ = () + + ValidRelationOperator: dict[str | None, type[Relational]] = {} + + is_Relational = True + + # ValidRelationOperator - Defined below, because the necessary classes + # have not yet been defined + + def __new__(cls, lhs, rhs, rop=None, **assumptions): + # If called by a subclass, do nothing special and pass on to Basic. + if cls is not Relational: + return Basic.__new__(cls, lhs, rhs, **assumptions) + + # XXX: Why do this? There should be a separate function to make a + # particular subclass of Relational from a string. + # + # If called directly with an operator, look up the subclass + # corresponding to that operator and delegate to it + cls = cls.ValidRelationOperator.get(rop, None) + if cls is None: + raise ValueError("Invalid relational operator symbol: %r" % rop) + + if not issubclass(cls, (Eq, Ne)): + # validate that Booleans are not being used in a relational + # other than Eq/Ne; + # Note: Symbol is a subclass of Boolean but is considered + # acceptable here. + if any(map(_nontrivBool, (lhs, rhs))): + raise TypeError(filldedent(''' + A Boolean argument can only be used in + Eq and Ne; all other relationals expect + real expressions. + ''')) + + return cls(lhs, rhs, **assumptions) + + @property + def lhs(self): + """The left-hand side of the relation.""" + return self._args[0] + + @property + def rhs(self): + """The right-hand side of the relation.""" + return self._args[1] + + @property + def reversed(self): + """Return the relationship with sides reversed. + + Examples + ======== + + >>> from sympy import Eq + >>> from sympy.abc import x + >>> Eq(x, 1) + Eq(x, 1) + >>> _.reversed + Eq(1, x) + >>> x < 1 + x < 1 + >>> _.reversed + 1 > x + """ + ops = {Eq: Eq, Gt: Lt, Ge: Le, Lt: Gt, Le: Ge, Ne: Ne} + a, b = self.args + return Relational.__new__(ops.get(self.func, self.func), b, a) + + @property + def reversedsign(self): + """Return the relationship with signs reversed. + + Examples + ======== + + >>> from sympy import Eq + >>> from sympy.abc import x + >>> Eq(x, 1) + Eq(x, 1) + >>> _.reversedsign + Eq(-x, -1) + >>> x < 1 + x < 1 + >>> _.reversedsign + -x > -1 + """ + a, b = self.args + if not (isinstance(a, BooleanAtom) or isinstance(b, BooleanAtom)): + ops = {Eq: Eq, Gt: Lt, Ge: Le, Lt: Gt, Le: Ge, Ne: Ne} + return Relational.__new__(ops.get(self.func, self.func), -a, -b) + else: + return self + + @property + def negated(self): + """Return the negated relationship. + + Examples + ======== + + >>> from sympy import Eq + >>> from sympy.abc import x + >>> Eq(x, 1) + Eq(x, 1) + >>> _.negated + Ne(x, 1) + >>> x < 1 + x < 1 + >>> _.negated + x >= 1 + + Notes + ===== + + This works more or less identical to ``~``/``Not``. The difference is + that ``negated`` returns the relationship even if ``evaluate=False``. + Hence, this is useful in code when checking for e.g. negated relations + to existing ones as it will not be affected by the `evaluate` flag. + + """ + ops = {Eq: Ne, Ge: Lt, Gt: Le, Le: Gt, Lt: Ge, Ne: Eq} + # If there ever will be new Relational subclasses, the following line + # will work until it is properly sorted out + # return ops.get(self.func, lambda a, b, evaluate=False: ~(self.func(a, + # b, evaluate=evaluate)))(*self.args, evaluate=False) + return Relational.__new__(ops.get(self.func), *self.args) + + @property + def weak(self): + """return the non-strict version of the inequality or self + + EXAMPLES + ======== + + >>> from sympy.abc import x + >>> (x < 1).weak + x <= 1 + >>> _.weak + x <= 1 + """ + return self + + @property + def strict(self): + """return the strict version of the inequality or self + + EXAMPLES + ======== + + >>> from sympy.abc import x + >>> (x <= 1).strict + x < 1 + >>> _.strict + x < 1 + """ + return self + + def _eval_evalf(self, prec): + return self.func(*[s._evalf(prec) for s in self.args]) + + @property + def canonical(self): + """Return a canonical form of the relational by putting a + number on the rhs, canonically removing a sign or else + ordering the args canonically. No other simplification is + attempted. + + Examples + ======== + + >>> from sympy.abc import x, y + >>> x < 2 + x < 2 + >>> _.reversed.canonical + x < 2 + >>> (-y < x).canonical + x > -y + >>> (-y > x).canonical + x < -y + >>> (-y < -x).canonical + x < y + + The canonicalization is recursively applied: + + >>> from sympy import Eq + >>> Eq(x < y, y > x).canonical + True + """ + args = tuple([i.canonical if isinstance(i, Relational) else i for i in self.args]) + if args != self.args: + r = self.func(*args) + if not isinstance(r, Relational): + return r + else: + r = self + if r.rhs.is_number: + if r.rhs.is_Number and r.lhs.is_Number and r.lhs > r.rhs: + r = r.reversed + elif r.lhs.is_number: + r = r.reversed + elif tuple(ordered(args)) != args: + r = r.reversed + + LHS_CEMS = getattr(r.lhs, 'could_extract_minus_sign', None) + RHS_CEMS = getattr(r.rhs, 'could_extract_minus_sign', None) + + if isinstance(r.lhs, BooleanAtom) or isinstance(r.rhs, BooleanAtom): + return r + + # Check if first value has negative sign + if LHS_CEMS and LHS_CEMS(): + return r.reversedsign + elif not r.rhs.is_number and RHS_CEMS and RHS_CEMS(): + # Right hand side has a minus, but not lhs. + # How does the expression with reversed signs behave? + # This is so that expressions of the type + # Eq(x, -y) and Eq(-x, y) + # have the same canonical representation + expr1, _ = ordered([r.lhs, -r.rhs]) + if expr1 != r.lhs: + return r.reversed.reversedsign + + return r + + def equals(self, other, failing_expression=False): + """Return True if the sides of the relationship are mathematically + identical and the type of relationship is the same. + If failing_expression is True, return the expression whose truth value + was unknown.""" + if isinstance(other, Relational): + if other in (self, self.reversed): + return True + a, b = self, other + if a.func in (Eq, Ne) or b.func in (Eq, Ne): + if a.func != b.func: + return False + left, right = [i.equals(j, + failing_expression=failing_expression) + for i, j in zip(a.args, b.args)] + if left is True: + return right + if right is True: + return left + lr, rl = [i.equals(j, failing_expression=failing_expression) + for i, j in zip(a.args, b.reversed.args)] + if lr is True: + return rl + if rl is True: + return lr + e = (left, right, lr, rl) + if all(i is False for i in e): + return False + for i in e: + if i not in (True, False): + return i + else: + if b.func != a.func: + b = b.reversed + if a.func != b.func: + return False + left = a.lhs.equals(b.lhs, + failing_expression=failing_expression) + if left is False: + return False + right = a.rhs.equals(b.rhs, + failing_expression=failing_expression) + if right is False: + return False + if left is True: + return right + return left + + def _eval_simplify(self, **kwargs): + from .add import Add + from .expr import Expr + r = self + r = r.func(*[i.simplify(**kwargs) for i in r.args]) + if r.is_Relational: + if not isinstance(r.lhs, Expr) or not isinstance(r.rhs, Expr): + return r + dif = r.lhs - r.rhs + # replace dif with a valid Number that will + # allow a definitive comparison with 0 + v = None + if dif.is_comparable: + v = dif.n(2) + if any(i._prec == 1 for i in v.as_real_imag()): + rv, iv = [i.n(2) for i in dif.as_real_imag()] + v = rv + S.ImaginaryUnit*iv + elif dif.equals(0): # XXX this is expensive + v = S.Zero + if v is not None: + r = r.func._eval_relation(v, S.Zero) + r = r.canonical + # If there is only one symbol in the expression, + # try to write it on a simplified form + free = list(filter(lambda x: x.is_real is not False, r.free_symbols)) + if len(free) == 1: + try: + from sympy.solvers.solveset import linear_coeffs + x = free.pop() + dif = r.lhs - r.rhs + m, b = linear_coeffs(dif, x) + if m.is_zero is False: + if m.is_negative: + # Dividing with a negative number, so change order of arguments + # canonical will put the symbol back on the lhs later + r = r.func(-b / m, x) + else: + r = r.func(x, -b / m) + else: + r = r.func(b, S.Zero) + except ValueError: + # maybe not a linear function, try polynomial + from sympy.polys.polyerrors import PolynomialError + from sympy.polys.polytools import gcd, Poly, poly + try: + p = poly(dif, x) + c = p.all_coeffs() + constant = c[-1] + c[-1] = 0 + scale = gcd(c) + c = [ctmp / scale for ctmp in c] + r = r.func(Poly.from_list(c, x).as_expr(), -constant / scale) + except PolynomialError: + pass + elif len(free) >= 2: + try: + from sympy.solvers.solveset import linear_coeffs + from sympy.polys.polytools import gcd + free = list(ordered(free)) + dif = r.lhs - r.rhs + m = linear_coeffs(dif, *free) + constant = m[-1] + del m[-1] + scale = gcd(m) + m = [mtmp / scale for mtmp in m] + nzm = list(filter(lambda f: f[0] != 0, list(zip(m, free)))) + if scale.is_zero is False: + if constant != 0: + # lhs: expression, rhs: constant + newexpr = Add(*[i * j for i, j in nzm]) + r = r.func(newexpr, -constant / scale) + else: + # keep first term on lhs + lhsterm = nzm[0][0] * nzm[0][1] + del nzm[0] + newexpr = Add(*[i * j for i, j in nzm]) + r = r.func(lhsterm, -newexpr) + + else: + r = r.func(constant, S.Zero) + except ValueError: + pass + # Did we get a simplified result? + r = r.canonical + measure = kwargs['measure'] + if measure(r) < kwargs['ratio'] * measure(self): + return r + else: + return self + + def _eval_trigsimp(self, **opts): + from sympy.simplify.trigsimp import trigsimp + return self.func(trigsimp(self.lhs, **opts), trigsimp(self.rhs, **opts)) + + def expand(self, **kwargs): + args = (arg.expand(**kwargs) for arg in self.args) + return self.func(*args) + + def __bool__(self): + raise TypeError("cannot determine truth value of Relational") + + def _eval_as_set(self): + # self is univariate and periodicity(self, x) in (0, None) + from sympy.solvers.inequalities import solve_univariate_inequality + from sympy.sets.conditionset import ConditionSet + syms = self.free_symbols + assert len(syms) == 1 + x = syms.pop() + try: + xset = solve_univariate_inequality(self, x, relational=False) + except NotImplementedError: + # solve_univariate_inequality raises NotImplementedError for + # unsolvable equations/inequalities. + xset = ConditionSet(x, self, S.Reals) + return xset + + @property + def binary_symbols(self): + # override where necessary + return set() + + +Rel = Relational + + +class Equality(Relational): + """ + An equal relation between two objects. + + Explanation + =========== + + Represents that two objects are equal. If they can be easily shown + to be definitively equal (or unequal), this will reduce to True (or + False). Otherwise, the relation is maintained as an unevaluated + Equality object. Use the ``simplify`` function on this object for + more nontrivial evaluation of the equality relation. + + As usual, the keyword argument ``evaluate=False`` can be used to + prevent any evaluation. + + Examples + ======== + + >>> from sympy import Eq, simplify, exp, cos + >>> from sympy.abc import x, y + >>> Eq(y, x + x**2) + Eq(y, x**2 + x) + >>> Eq(2, 5) + False + >>> Eq(2, 5, evaluate=False) + Eq(2, 5) + >>> _.doit() + False + >>> Eq(exp(x), exp(x).rewrite(cos)) + Eq(exp(x), sinh(x) + cosh(x)) + >>> simplify(_) + True + + See Also + ======== + + sympy.logic.boolalg.Equivalent : for representing equality between two + boolean expressions + + Notes + ===== + + Python treats 1 and True (and 0 and False) as being equal; SymPy + does not. And integer will always compare as unequal to a Boolean: + + >>> Eq(True, 1), True == 1 + (False, True) + + This class is not the same as the == operator. The == operator tests + for exact structural equality between two expressions; this class + compares expressions mathematically. + + If either object defines an ``_eval_Eq`` method, it can be used in place of + the default algorithm. If ``lhs._eval_Eq(rhs)`` or ``rhs._eval_Eq(lhs)`` + returns anything other than None, that return value will be substituted for + the Equality. If None is returned by ``_eval_Eq``, an Equality object will + be created as usual. + + Since this object is already an expression, it does not respond to + the method ``as_expr`` if one tries to create `x - y` from ``Eq(x, y)``. + If ``eq = Eq(x, y)`` then write `eq.lhs - eq.rhs` to get ``x - y``. + + .. deprecated:: 1.5 + + ``Eq(expr)`` with a single argument is a shorthand for ``Eq(expr, 0)``, + but this behavior is deprecated and will be removed in a future version + of SymPy. + + """ + rel_op = '==' + + __slots__ = () + + is_Equality = True + + def __new__(cls, lhs, rhs, **options): + evaluate = options.pop('evaluate', global_parameters.evaluate) + lhs = _sympify(lhs) + rhs = _sympify(rhs) + if evaluate: + val = is_eq(lhs, rhs) + if val is None: + return cls(lhs, rhs, evaluate=False) + else: + return _sympify(val) + + return Relational.__new__(cls, lhs, rhs) + + @classmethod + def _eval_relation(cls, lhs, rhs): + return _sympify(lhs == rhs) + + def _eval_rewrite_as_Add(self, L, R, evaluate=True, **kwargs): + """ + return Eq(L, R) as L - R. To control the evaluation of + the result set pass `evaluate=True` to give L - R; + if `evaluate=None` then terms in L and R will not cancel + but they will be listed in canonical order; otherwise + non-canonical args will be returned. If one side is 0, the + non-zero side will be returned. + + .. deprecated:: 1.13 + + The method ``Eq.rewrite(Add)`` is deprecated. + See :ref:`eq-rewrite-Add` for details. + + Examples + ======== + + >>> from sympy import Eq, Add + >>> from sympy.abc import b, x + >>> eq = Eq(x + b, x - b) + >>> eq.rewrite(Add) #doctest: +SKIP + 2*b + >>> eq.rewrite(Add, evaluate=None).args #doctest: +SKIP + (b, b, x, -x) + >>> eq.rewrite(Add, evaluate=False).args #doctest: +SKIP + (b, x, b, -x) + """ + sympy_deprecation_warning(""" + Eq.rewrite(Add) is deprecated. + + For ``eq = Eq(a, b)`` use ``eq.lhs - eq.rhs`` to obtain + ``a - b``. + """, + deprecated_since_version="1.13", + active_deprecations_target="eq-rewrite-Add", + stacklevel=5, + ) + from .add import _unevaluated_Add, Add + if L == 0: + return R + if R == 0: + return L + if evaluate: + # allow cancellation of args + return L - R + args = Add.make_args(L) + Add.make_args(-R) + if evaluate is None: + # no cancellation, but canonical + return _unevaluated_Add(*args) + # no cancellation, not canonical + return Add._from_args(args) + + @property + def binary_symbols(self): + if S.true in self.args or S.false in self.args: + if self.lhs.is_Symbol: + return {self.lhs} + elif self.rhs.is_Symbol: + return {self.rhs} + return set() + + def _eval_simplify(self, **kwargs): + # standard simplify + e = super()._eval_simplify(**kwargs) + if not isinstance(e, Equality): + return e + from .expr import Expr + if not isinstance(e.lhs, Expr) or not isinstance(e.rhs, Expr): + return e + free = self.free_symbols + if len(free) == 1: + try: + from .add import Add + from sympy.solvers.solveset import linear_coeffs + x = free.pop() + m, b = linear_coeffs( + Add(e.lhs, -e.rhs, evaluate=False), x) + if m.is_zero is False: + enew = e.func(x, -b / m) + else: + enew = e.func(m * x, -b) + measure = kwargs['measure'] + if measure(enew) <= kwargs['ratio'] * measure(e): + e = enew + except ValueError: + pass + return e.canonical + + def integrate(self, *args, **kwargs): + """See the integrate function in sympy.integrals""" + from sympy.integrals.integrals import integrate + return integrate(self, *args, **kwargs) + + def as_poly(self, *gens, **kwargs): + '''Returns lhs-rhs as a Poly + + Examples + ======== + + >>> from sympy import Eq + >>> from sympy.abc import x + >>> Eq(x**2, 1).as_poly(x) + Poly(x**2 - 1, x, domain='ZZ') + ''' + return (self.lhs - self.rhs).as_poly(*gens, **kwargs) + + +Eq = Equality + + +class Unequality(Relational): + """An unequal relation between two objects. + + Explanation + =========== + + Represents that two objects are not equal. If they can be shown to be + definitively equal, this will reduce to False; if definitively unequal, + this will reduce to True. Otherwise, the relation is maintained as an + Unequality object. + + Examples + ======== + + >>> from sympy import Ne + >>> from sympy.abc import x, y + >>> Ne(y, x+x**2) + Ne(y, x**2 + x) + + See Also + ======== + Equality + + Notes + ===== + This class is not the same as the != operator. The != operator tests + for exact structural equality between two expressions; this class + compares expressions mathematically. + + This class is effectively the inverse of Equality. As such, it uses the + same algorithms, including any available `_eval_Eq` methods. + + """ + rel_op = '!=' + + __slots__ = () + + def __new__(cls, lhs, rhs, **options): + lhs = _sympify(lhs) + rhs = _sympify(rhs) + evaluate = options.pop('evaluate', global_parameters.evaluate) + if evaluate: + val = is_neq(lhs, rhs) + if val is None: + return cls(lhs, rhs, evaluate=False) + else: + return _sympify(val) + + return Relational.__new__(cls, lhs, rhs, **options) + + @classmethod + def _eval_relation(cls, lhs, rhs): + return _sympify(lhs != rhs) + + @property + def binary_symbols(self): + if S.true in self.args or S.false in self.args: + if self.lhs.is_Symbol: + return {self.lhs} + elif self.rhs.is_Symbol: + return {self.rhs} + return set() + + def _eval_simplify(self, **kwargs): + # simplify as an equality + eq = Equality(*self.args)._eval_simplify(**kwargs) + if isinstance(eq, Equality): + # send back Ne with the new args + return self.func(*eq.args) + return eq.negated # result of Ne is the negated Eq + + +Ne = Unequality + + +class _Inequality(Relational): + """Internal base class for all *Than types. + + Each subclass must implement _eval_relation to provide the method for + comparing two real numbers. + + """ + __slots__ = () + + def __new__(cls, lhs, rhs, **options): + + try: + lhs = _sympify(lhs) + rhs = _sympify(rhs) + except SympifyError: + return NotImplemented + + evaluate = options.pop('evaluate', global_parameters.evaluate) + if evaluate: + for me in (lhs, rhs): + if me.is_extended_real is False: + raise TypeError("Invalid comparison of non-real %s" % me) + if me is S.NaN: + raise TypeError("Invalid NaN comparison") + # First we invoke the appropriate inequality method of `lhs` + # (e.g., `lhs.__lt__`). That method will try to reduce to + # boolean or raise an exception. It may keep calling + # superclasses until it reaches `Expr` (e.g., `Expr.__lt__`). + # In some cases, `Expr` will just invoke us again (if neither it + # nor a subclass was able to reduce to boolean or raise an + # exception). In that case, it must call us with + # `evaluate=False` to prevent infinite recursion. + return cls._eval_relation(lhs, rhs, **options) + + # make a "non-evaluated" Expr for the inequality + return Relational.__new__(cls, lhs, rhs, **options) + + @classmethod + def _eval_relation(cls, lhs, rhs, **options): + val = cls._eval_fuzzy_relation(lhs, rhs) + if val is None: + return cls(lhs, rhs, evaluate=False) + else: + return _sympify(val) + + +class _Greater(_Inequality): + """Not intended for general use + + _Greater is only used so that GreaterThan and StrictGreaterThan may + subclass it for the .gts and .lts properties. + + """ + __slots__ = () + + @property + def gts(self): + return self._args[0] + + @property + def lts(self): + return self._args[1] + + +class _Less(_Inequality): + """Not intended for general use. + + _Less is only used so that LessThan and StrictLessThan may subclass it for + the .gts and .lts properties. + + """ + __slots__ = () + + @property + def gts(self): + return self._args[1] + + @property + def lts(self): + return self._args[0] + + +class GreaterThan(_Greater): + r"""Class representations of inequalities. + + Explanation + =========== + + The ``*Than`` classes represent inequal relationships, where the left-hand + side is generally bigger or smaller than the right-hand side. For example, + the GreaterThan class represents an inequal relationship where the + left-hand side is at least as big as the right side, if not bigger. In + mathematical notation: + + lhs $\ge$ rhs + + In total, there are four ``*Than`` classes, to represent the four + inequalities: + + +-----------------+--------+ + |Class Name | Symbol | + +=================+========+ + |GreaterThan | ``>=`` | + +-----------------+--------+ + |LessThan | ``<=`` | + +-----------------+--------+ + |StrictGreaterThan| ``>`` | + +-----------------+--------+ + |StrictLessThan | ``<`` | + +-----------------+--------+ + + All classes take two arguments, lhs and rhs. + + +----------------------------+-----------------+ + |Signature Example | Math Equivalent | + +============================+=================+ + |GreaterThan(lhs, rhs) | lhs $\ge$ rhs | + +----------------------------+-----------------+ + |LessThan(lhs, rhs) | lhs $\le$ rhs | + +----------------------------+-----------------+ + |StrictGreaterThan(lhs, rhs) | lhs $>$ rhs | + +----------------------------+-----------------+ + |StrictLessThan(lhs, rhs) | lhs $<$ rhs | + +----------------------------+-----------------+ + + In addition to the normal .lhs and .rhs of Relations, ``*Than`` inequality + objects also have the .lts and .gts properties, which represent the "less + than side" and "greater than side" of the operator. Use of .lts and .gts + in an algorithm rather than .lhs and .rhs as an assumption of inequality + direction will make more explicit the intent of a certain section of code, + and will make it similarly more robust to client code changes: + + >>> from sympy import GreaterThan, StrictGreaterThan + >>> from sympy import LessThan, StrictLessThan + >>> from sympy import And, Ge, Gt, Le, Lt, Rel, S + >>> from sympy.abc import x, y, z + >>> from sympy.core.relational import Relational + + >>> e = GreaterThan(x, 1) + >>> e + x >= 1 + >>> '%s >= %s is the same as %s <= %s' % (e.gts, e.lts, e.lts, e.gts) + 'x >= 1 is the same as 1 <= x' + + Examples + ======== + + One generally does not instantiate these classes directly, but uses various + convenience methods: + + >>> for f in [Ge, Gt, Le, Lt]: # convenience wrappers + ... print(f(x, 2)) + x >= 2 + x > 2 + x <= 2 + x < 2 + + Another option is to use the Python inequality operators (``>=``, ``>``, + ``<=``, ``<``) directly. Their main advantage over the ``Ge``, ``Gt``, + ``Le``, and ``Lt`` counterparts, is that one can write a more + "mathematical looking" statement rather than littering the math with + oddball function calls. However there are certain (minor) caveats of + which to be aware (search for 'gotcha', below). + + >>> x >= 2 + x >= 2 + >>> _ == Ge(x, 2) + True + + However, it is also perfectly valid to instantiate a ``*Than`` class less + succinctly and less conveniently: + + >>> Rel(x, 1, ">") + x > 1 + >>> Relational(x, 1, ">") + x > 1 + + >>> StrictGreaterThan(x, 1) + x > 1 + >>> GreaterThan(x, 1) + x >= 1 + >>> LessThan(x, 1) + x <= 1 + >>> StrictLessThan(x, 1) + x < 1 + + Notes + ===== + + There are a couple of "gotchas" to be aware of when using Python's + operators. + + The first is that what your write is not always what you get: + + >>> 1 < x + x > 1 + + Due to the order that Python parses a statement, it may + not immediately find two objects comparable. When ``1 < x`` + is evaluated, Python recognizes that the number 1 is a native + number and that x is *not*. Because a native Python number does + not know how to compare itself with a SymPy object + Python will try the reflective operation, ``x > 1`` and that is the + form that gets evaluated, hence returned. + + If the order of the statement is important (for visual output to + the console, perhaps), one can work around this annoyance in a + couple ways: + + (1) "sympify" the literal before comparison + + >>> S(1) < x + 1 < x + + (2) use one of the wrappers or less succinct methods described + above + + >>> Lt(1, x) + 1 < x + >>> Relational(1, x, "<") + 1 < x + + The second gotcha involves writing equality tests between relationals + when one or both sides of the test involve a literal relational: + + >>> e = x < 1; e + x < 1 + >>> e == e # neither side is a literal + True + >>> e == x < 1 # expecting True, too + False + >>> e != x < 1 # expecting False + x < 1 + >>> x < 1 != x < 1 # expecting False or the same thing as before + Traceback (most recent call last): + ... + TypeError: cannot determine truth value of Relational + + The solution for this case is to wrap literal relationals in + parentheses: + + >>> e == (x < 1) + True + >>> e != (x < 1) + False + >>> (x < 1) != (x < 1) + False + + The third gotcha involves chained inequalities not involving + ``==`` or ``!=``. Occasionally, one may be tempted to write: + + >>> e = x < y < z + Traceback (most recent call last): + ... + TypeError: symbolic boolean expression has no truth value. + + Due to an implementation detail or decision of Python [1]_, + there is no way for SymPy to create a chained inequality with + that syntax so one must use And: + + >>> e = And(x < y, y < z) + >>> type( e ) + And + >>> e + (x < y) & (y < z) + + Although this can also be done with the '&' operator, it cannot + be done with the 'and' operarator: + + >>> (x < y) & (y < z) + (x < y) & (y < z) + >>> (x < y) and (y < z) + Traceback (most recent call last): + ... + TypeError: cannot determine truth value of Relational + + .. [1] This implementation detail is that Python provides no reliable + method to determine that a chained inequality is being built. + Chained comparison operators are evaluated pairwise, using "and" + logic (see + https://docs.python.org/3/reference/expressions.html#not-in). This + is done in an efficient way, so that each object being compared + is only evaluated once and the comparison can short-circuit. For + example, ``1 > 2 > 3`` is evaluated by Python as ``(1 > 2) and (2 + > 3)``. The ``and`` operator coerces each side into a bool, + returning the object itself when it short-circuits. The bool of + the --Than operators will raise TypeError on purpose, because + SymPy cannot determine the mathematical ordering of symbolic + expressions. Thus, if we were to compute ``x > y > z``, with + ``x``, ``y``, and ``z`` being Symbols, Python converts the + statement (roughly) into these steps: + + (1) x > y > z + (2) (x > y) and (y > z) + (3) (GreaterThanObject) and (y > z) + (4) (GreaterThanObject.__bool__()) and (y > z) + (5) TypeError + + Because of the ``and`` added at step 2, the statement gets turned into a + weak ternary statement, and the first object's ``__bool__`` method will + raise TypeError. Thus, creating a chained inequality is not possible. + + In Python, there is no way to override the ``and`` operator, or to + control how it short circuits, so it is impossible to make something + like ``x > y > z`` work. There was a PEP to change this, + :pep:`335`, but it was officially closed in March, 2012. + + """ + __slots__ = () + + rel_op = '>=' + + @classmethod + def _eval_fuzzy_relation(cls, lhs, rhs): + return is_ge(lhs, rhs) + + @property + def strict(self): + return Gt(*self.args) + +Ge = GreaterThan + + +class LessThan(_Less): + __doc__ = GreaterThan.__doc__ + __slots__ = () + + rel_op = '<=' + + @classmethod + def _eval_fuzzy_relation(cls, lhs, rhs): + return is_le(lhs, rhs) + + @property + def strict(self): + return Lt(*self.args) + +Le = LessThan + + +class StrictGreaterThan(_Greater): + __doc__ = GreaterThan.__doc__ + __slots__ = () + + rel_op = '>' + + @classmethod + def _eval_fuzzy_relation(cls, lhs, rhs): + return is_gt(lhs, rhs) + + @property + def weak(self): + return Ge(*self.args) + + +Gt = StrictGreaterThan + + +class StrictLessThan(_Less): + __doc__ = GreaterThan.__doc__ + __slots__ = () + + rel_op = '<' + + @classmethod + def _eval_fuzzy_relation(cls, lhs, rhs): + return is_lt(lhs, rhs) + + @property + def weak(self): + return Le(*self.args) + +Lt = StrictLessThan + +# A class-specific (not object-specific) data item used for a minor speedup. +# It is defined here, rather than directly in the class, because the classes +# that it references have not been defined until now (e.g. StrictLessThan). +Relational.ValidRelationOperator = { + None: Equality, + '==': Equality, + 'eq': Equality, + '!=': Unequality, + '<>': Unequality, + 'ne': Unequality, + '>=': GreaterThan, + 'ge': GreaterThan, + '<=': LessThan, + 'le': LessThan, + '>': StrictGreaterThan, + 'gt': StrictGreaterThan, + '<': StrictLessThan, + 'lt': StrictLessThan, +} + + +def _n2(a, b): + """Return (a - b).evalf(2) if a and b are comparable, else None. + This should only be used when a and b are already sympified. + """ + # /!\ it is very important (see issue 8245) not to + # use a re-evaluated number in the calculation of dif + if a.is_comparable and b.is_comparable: + dif = (a - b).evalf(2) + if dif.is_comparable: + return dif + + +@dispatch(Expr, Expr) +def _eval_is_ge(lhs, rhs): + return None + + +@dispatch(Basic, Basic) +def _eval_is_eq(lhs, rhs): + return None + + +@dispatch(Tuple, Expr) # type: ignore +def _eval_is_eq(lhs, rhs): # noqa:F811 + return False + + +@dispatch(Tuple, AppliedUndef) # type: ignore +def _eval_is_eq(lhs, rhs): # noqa:F811 + return None + + +@dispatch(Tuple, Symbol) # type: ignore +def _eval_is_eq(lhs, rhs): # noqa:F811 + return None + + +@dispatch(Tuple, Tuple) # type: ignore +def _eval_is_eq(lhs, rhs): # noqa:F811 + if len(lhs) != len(rhs): + return False + + return fuzzy_and(fuzzy_bool(is_eq(s, o)) for s, o in zip(lhs, rhs)) + + +def is_lt(lhs, rhs, assumptions=None): + """Fuzzy bool for lhs is strictly less than rhs. + + See the docstring for :func:`~.is_ge` for more. + """ + return fuzzy_not(is_ge(lhs, rhs, assumptions)) + + +def is_gt(lhs, rhs, assumptions=None): + """Fuzzy bool for lhs is strictly greater than rhs. + + See the docstring for :func:`~.is_ge` for more. + """ + return fuzzy_not(is_le(lhs, rhs, assumptions)) + + +def is_le(lhs, rhs, assumptions=None): + """Fuzzy bool for lhs is less than or equal to rhs. + + See the docstring for :func:`~.is_ge` for more. + """ + return is_ge(rhs, lhs, assumptions) + + +def is_ge(lhs, rhs, assumptions=None): + """ + Fuzzy bool for *lhs* is greater than or equal to *rhs*. + + Parameters + ========== + + lhs : Expr + The left-hand side of the expression, must be sympified, + and an instance of expression. Throws an exception if + lhs is not an instance of expression. + + rhs : Expr + The right-hand side of the expression, must be sympified + and an instance of expression. Throws an exception if + lhs is not an instance of expression. + + assumptions: Boolean, optional + Assumptions taken to evaluate the inequality. + + Returns + ======= + + ``True`` if *lhs* is greater than or equal to *rhs*, ``False`` if *lhs* + is less than *rhs*, and ``None`` if the comparison between *lhs* and + *rhs* is indeterminate. + + Explanation + =========== + + This function is intended to give a relatively fast determination and + deliberately does not attempt slow calculations that might help in + obtaining a determination of True or False in more difficult cases. + + The four comparison functions ``is_le``, ``is_lt``, ``is_ge``, and ``is_gt`` are + each implemented in terms of ``is_ge`` in the following way: + + is_ge(x, y) := is_ge(x, y) + is_le(x, y) := is_ge(y, x) + is_lt(x, y) := fuzzy_not(is_ge(x, y)) + is_gt(x, y) := fuzzy_not(is_ge(y, x)) + + Therefore, supporting new type with this function will ensure behavior for + other three functions as well. + + To maintain these equivalences in fuzzy logic it is important that in cases where + either x or y is non-real all comparisons will give None. + + Examples + ======== + + >>> from sympy import S, Q + >>> from sympy.core.relational import is_ge, is_le, is_gt, is_lt + >>> from sympy.abc import x + >>> is_ge(S(2), S(0)) + True + >>> is_ge(S(0), S(2)) + False + >>> is_le(S(0), S(2)) + True + >>> is_gt(S(0), S(2)) + False + >>> is_lt(S(2), S(0)) + False + + Assumptions can be passed to evaluate the quality which is otherwise + indeterminate. + + >>> print(is_ge(x, S(0))) + None + >>> is_ge(x, S(0), assumptions=Q.positive(x)) + True + + New types can be supported by dispatching to ``_eval_is_ge``. + + >>> from sympy import Expr, sympify + >>> from sympy.multipledispatch import dispatch + >>> class MyExpr(Expr): + ... def __new__(cls, arg): + ... return super().__new__(cls, sympify(arg)) + ... @property + ... def value(self): + ... return self.args[0] + >>> @dispatch(MyExpr, MyExpr) + ... def _eval_is_ge(a, b): + ... return is_ge(a.value, b.value) + >>> a = MyExpr(1) + >>> b = MyExpr(2) + >>> is_ge(b, a) + True + >>> is_le(a, b) + True + """ + from sympy.assumptions.wrapper import AssumptionsWrapper, is_extended_nonnegative + + if not (isinstance(lhs, Expr) and isinstance(rhs, Expr)): + raise TypeError("Can only compare inequalities with Expr") + + retval = _eval_is_ge(lhs, rhs) + + if retval is not None: + return retval + else: + n2 = _n2(lhs, rhs) + if n2 is not None: + # use float comparison for infinity. + # otherwise get stuck in infinite recursion + if n2 in (S.Infinity, S.NegativeInfinity): + n2 = float(n2) + return n2 >= 0 + + _lhs = AssumptionsWrapper(lhs, assumptions) + _rhs = AssumptionsWrapper(rhs, assumptions) + if _lhs.is_extended_real and _rhs.is_extended_real: + if (_lhs.is_infinite and _lhs.is_extended_positive) or (_rhs.is_infinite and _rhs.is_extended_negative): + return True + diff = lhs - rhs + if diff is not S.NaN: + rv = is_extended_nonnegative(diff, assumptions) + if rv is not None: + return rv + + +def is_neq(lhs, rhs, assumptions=None): + """Fuzzy bool for lhs does not equal rhs. + + See the docstring for :func:`~.is_eq` for more. + """ + return fuzzy_not(is_eq(lhs, rhs, assumptions)) + + +def is_eq(lhs, rhs, assumptions=None): + """ + Fuzzy bool representing mathematical equality between *lhs* and *rhs*. + + Parameters + ========== + + lhs : Expr + The left-hand side of the expression, must be sympified. + + rhs : Expr + The right-hand side of the expression, must be sympified. + + assumptions: Boolean, optional + Assumptions taken to evaluate the equality. + + Returns + ======= + + ``True`` if *lhs* is equal to *rhs*, ``False`` is *lhs* is not equal to *rhs*, + and ``None`` if the comparison between *lhs* and *rhs* is indeterminate. + + Explanation + =========== + + This function is intended to give a relatively fast determination and + deliberately does not attempt slow calculations that might help in + obtaining a determination of True or False in more difficult cases. + + :func:`~.is_neq` calls this function to return its value, so supporting + new type with this function will ensure correct behavior for ``is_neq`` + as well. + + Examples + ======== + + >>> from sympy import Q, S + >>> from sympy.core.relational import is_eq, is_neq + >>> from sympy.abc import x + >>> is_eq(S(0), S(0)) + True + >>> is_neq(S(0), S(0)) + False + >>> is_eq(S(0), S(2)) + False + >>> is_neq(S(0), S(2)) + True + + Assumptions can be passed to evaluate the equality which is otherwise + indeterminate. + + >>> print(is_eq(x, S(0))) + None + >>> is_eq(x, S(0), assumptions=Q.zero(x)) + True + + New types can be supported by dispatching to ``_eval_is_eq``. + + >>> from sympy import Basic, sympify + >>> from sympy.multipledispatch import dispatch + >>> class MyBasic(Basic): + ... def __new__(cls, arg): + ... return Basic.__new__(cls, sympify(arg)) + ... @property + ... def value(self): + ... return self.args[0] + ... + >>> @dispatch(MyBasic, MyBasic) + ... def _eval_is_eq(a, b): + ... return is_eq(a.value, b.value) + ... + >>> a = MyBasic(1) + >>> b = MyBasic(1) + >>> is_eq(a, b) + True + >>> is_neq(a, b) + False + + """ + # here, _eval_Eq is only called for backwards compatibility + # new code should use is_eq with multiple dispatch as + # outlined in the docstring + for side1, side2 in (lhs, rhs), (rhs, lhs): + eval_func = getattr(side1, '_eval_Eq', None) + if eval_func is not None: + retval = eval_func(side2) + if retval is not None: + return retval + + retval = _eval_is_eq(lhs, rhs) + if retval is not None: + return retval + + if dispatch(type(lhs), type(rhs)) != dispatch(type(rhs), type(lhs)): + retval = _eval_is_eq(rhs, lhs) + if retval is not None: + return retval + + # retval is still None, so go through the equality logic + # If expressions have the same structure, they must be equal. + if lhs == rhs: + return True # e.g. True == True + elif all(isinstance(i, BooleanAtom) for i in (rhs, lhs)): + return False # True != False + elif not (lhs.is_Symbol or rhs.is_Symbol) and ( + isinstance(lhs, Boolean) != + isinstance(rhs, Boolean)): + return False # only Booleans can equal Booleans + + from sympy.assumptions.wrapper import (AssumptionsWrapper, + is_infinite, is_extended_real) + from .add import Add + + _lhs = AssumptionsWrapper(lhs, assumptions) + _rhs = AssumptionsWrapper(rhs, assumptions) + + if _lhs.is_infinite or _rhs.is_infinite: + if fuzzy_xor([_lhs.is_infinite, _rhs.is_infinite]): + return False + if fuzzy_xor([_lhs.is_extended_real, _rhs.is_extended_real]): + return False + if fuzzy_and([_lhs.is_extended_real, _rhs.is_extended_real]): + return fuzzy_xor([_lhs.is_extended_positive, fuzzy_not(_rhs.is_extended_positive)]) + + # Try to split real/imaginary parts and equate them + I = S.ImaginaryUnit + + def split_real_imag(expr): + real_imag = lambda t: ( + 'real' if is_extended_real(t, assumptions) else + 'imag' if is_extended_real(I*t, assumptions) else None) + return sift(Add.make_args(expr), real_imag) + + lhs_ri = split_real_imag(lhs) + if not lhs_ri[None]: + rhs_ri = split_real_imag(rhs) + if not rhs_ri[None]: + eq_real = is_eq(Add(*lhs_ri['real']), Add(*rhs_ri['real']), assumptions) + eq_imag = is_eq(I * Add(*lhs_ri['imag']), I * Add(*rhs_ri['imag']), assumptions) + return fuzzy_and(map(fuzzy_bool, [eq_real, eq_imag])) + + from sympy.functions.elementary.complexes import arg + # Compare e.g. zoo with 1+I*oo by comparing args + arglhs = arg(lhs) + argrhs = arg(rhs) + # Guard against Eq(nan, nan) -> False + if not (arglhs == S.NaN and argrhs == S.NaN): + return fuzzy_bool(is_eq(arglhs, argrhs, assumptions)) + + if all(isinstance(i, Expr) for i in (lhs, rhs)): + # see if the difference evaluates + dif = lhs - rhs + _dif = AssumptionsWrapper(dif, assumptions) + z = _dif.is_zero + if z is not None: + if z is False and _dif.is_commutative: # issue 10728 + return False + if z: + return True + + # is_zero cannot help decide integer/rational with Float + c, t = dif.as_coeff_Add() + if c.is_Float: + if int_valued(c): + if t.is_integer is False: + return False + elif t.is_rational is False: + return False + + n2 = _n2(lhs, rhs) + if n2 is not None: + return _sympify(n2 == 0) + + # see if the ratio evaluates + n, d = dif.as_numer_denom() + rv = None + _n = AssumptionsWrapper(n, assumptions) + _d = AssumptionsWrapper(d, assumptions) + if _n.is_zero: + rv = _d.is_nonzero + elif _n.is_finite: + if _d.is_infinite: + rv = True + elif _n.is_zero is False: + rv = _d.is_infinite + if rv is None: + # if the condition that makes the denominator + # infinite does not make the original expression + # True then False can be returned + from sympy.simplify.simplify import clear_coefficients + l, r = clear_coefficients(d, S.Infinity) + args = [_.subs(l, r) for _ in (lhs, rhs)] + if args != [lhs, rhs]: + rv = fuzzy_bool(is_eq(*args, assumptions)) + if rv is True: + rv = None + elif any(is_infinite(a, assumptions) for a in Add.make_args(n)): + # (inf or nan)/x != 0 + rv = False + if rv is not None: + return rv diff --git a/lib/python3.12/site-packages/sympy/core/rules.py b/lib/python3.12/site-packages/sympy/core/rules.py new file mode 100644 index 0000000000000000000000000000000000000000..5ae331f71b21c8a6ef35f499c5c5c89239349e9c --- /dev/null +++ b/lib/python3.12/site-packages/sympy/core/rules.py @@ -0,0 +1,66 @@ +""" +Replacement rules. +""" + +class Transform: + """ + Immutable mapping that can be used as a generic transformation rule. + + Parameters + ========== + + transform : callable + Computes the value corresponding to any key. + + filter : callable, optional + If supplied, specifies which objects are in the mapping. + + Examples + ======== + + >>> from sympy.core.rules import Transform + >>> from sympy.abc import x + + This Transform will return, as a value, one more than the key: + + >>> add1 = Transform(lambda x: x + 1) + >>> add1[1] + 2 + >>> add1[x] + x + 1 + + By default, all values are considered to be in the dictionary. If a filter + is supplied, only the objects for which it returns True are considered as + being in the dictionary: + + >>> add1_odd = Transform(lambda x: x + 1, lambda x: x%2 == 1) + >>> 2 in add1_odd + False + >>> add1_odd.get(2, 0) + 0 + >>> 3 in add1_odd + True + >>> add1_odd[3] + 4 + >>> add1_odd.get(3, 0) + 4 + """ + + def __init__(self, transform, filter=lambda x: True): + self._transform = transform + self._filter = filter + + def __contains__(self, item): + return self._filter(item) + + def __getitem__(self, key): + if self._filter(key): + return self._transform(key) + else: + raise KeyError(key) + + def get(self, item, default=None): + if item in self: + return self[item] + else: + return default diff --git a/lib/python3.12/site-packages/sympy/core/singleton.py b/lib/python3.12/site-packages/sympy/core/singleton.py new file mode 100644 index 0000000000000000000000000000000000000000..aedf1885058900eece7061d65c53d5704172a596 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/core/singleton.py @@ -0,0 +1,174 @@ +"""Singleton mechanism""" + + +from .core import Registry +from .sympify import sympify + + +class SingletonRegistry(Registry): + """ + The registry for the singleton classes (accessible as ``S``). + + Explanation + =========== + + This class serves as two separate things. + + The first thing it is is the ``SingletonRegistry``. Several classes in + SymPy appear so often that they are singletonized, that is, using some + metaprogramming they are made so that they can only be instantiated once + (see the :class:`sympy.core.singleton.Singleton` class for details). For + instance, every time you create ``Integer(0)``, this will return the same + instance, :class:`sympy.core.numbers.Zero`. All singleton instances are + attributes of the ``S`` object, so ``Integer(0)`` can also be accessed as + ``S.Zero``. + + Singletonization offers two advantages: it saves memory, and it allows + fast comparison. It saves memory because no matter how many times the + singletonized objects appear in expressions in memory, they all point to + the same single instance in memory. The fast comparison comes from the + fact that you can use ``is`` to compare exact instances in Python + (usually, you need to use ``==`` to compare things). ``is`` compares + objects by memory address, and is very fast. + + Examples + ======== + + >>> from sympy import S, Integer + >>> a = Integer(0) + >>> a is S.Zero + True + + For the most part, the fact that certain objects are singletonized is an + implementation detail that users should not need to worry about. In SymPy + library code, ``is`` comparison is often used for performance purposes + The primary advantage of ``S`` for end users is the convenient access to + certain instances that are otherwise difficult to type, like ``S.Half`` + (instead of ``Rational(1, 2)``). + + When using ``is`` comparison, make sure the argument is sympified. For + instance, + + >>> x = 0 + >>> x is S.Zero + False + + This problem is not an issue when using ``==``, which is recommended for + most use-cases: + + >>> 0 == S.Zero + True + + The second thing ``S`` is is a shortcut for + :func:`sympy.core.sympify.sympify`. :func:`sympy.core.sympify.sympify` is + the function that converts Python objects such as ``int(1)`` into SymPy + objects such as ``Integer(1)``. It also converts the string form of an + expression into a SymPy expression, like ``sympify("x**2")`` -> + ``Symbol("x")**2``. ``S(1)`` is the same thing as ``sympify(1)`` + (basically, ``S.__call__`` has been defined to call ``sympify``). + + This is for convenience, since ``S`` is a single letter. It's mostly + useful for defining rational numbers. Consider an expression like ``x + + 1/2``. If you enter this directly in Python, it will evaluate the ``1/2`` + and give ``0.5``, because both arguments are ints (see also + :ref:`tutorial-gotchas-final-notes`). However, in SymPy, you usually want + the quotient of two integers to give an exact rational number. The way + Python's evaluation works, at least one side of an operator needs to be a + SymPy object for the SymPy evaluation to take over. You could write this + as ``x + Rational(1, 2)``, but this is a lot more typing. A shorter + version is ``x + S(1)/2``. Since ``S(1)`` returns ``Integer(1)``, the + division will return a ``Rational`` type, since it will call + ``Integer.__truediv__``, which knows how to return a ``Rational``. + + """ + __slots__ = () + + # Also allow things like S(5) + __call__ = staticmethod(sympify) + + def __init__(self): + self._classes_to_install = {} + # Dict of classes that have been registered, but that have not have been + # installed as an attribute of this SingletonRegistry. + # Installation automatically happens at the first attempt to access the + # attribute. + # The purpose of this is to allow registration during class + # initialization during import, but not trigger object creation until + # actual use (which should not happen until after all imports are + # finished). + + def register(self, cls): + # Make sure a duplicate class overwrites the old one + if hasattr(self, cls.__name__): + delattr(self, cls.__name__) + self._classes_to_install[cls.__name__] = cls + + def __getattr__(self, name): + """Python calls __getattr__ if no attribute of that name was installed + yet. + + Explanation + =========== + + This __getattr__ checks whether a class with the requested name was + already registered but not installed; if no, raises an AttributeError. + Otherwise, retrieves the class, calculates its singleton value, installs + it as an attribute of the given name, and unregisters the class.""" + if name not in self._classes_to_install: + raise AttributeError( + "Attribute '%s' was not installed on SymPy registry %s" % ( + name, self)) + class_to_install = self._classes_to_install[name] + value_to_install = class_to_install() + self.__setattr__(name, value_to_install) + del self._classes_to_install[name] + return value_to_install + + def __repr__(self): + return "S" + +S = SingletonRegistry() + + +class Singleton(type): + """ + Metaclass for singleton classes. + + Explanation + =========== + + A singleton class has only one instance which is returned every time the + class is instantiated. Additionally, this instance can be accessed through + the global registry object ``S`` as ``S.``. + + Examples + ======== + + >>> from sympy import S, Basic + >>> from sympy.core.singleton import Singleton + >>> class MySingleton(Basic, metaclass=Singleton): + ... pass + >>> Basic() is Basic() + False + >>> MySingleton() is MySingleton() + True + >>> S.MySingleton is MySingleton() + True + + Notes + ===== + + Instance creation is delayed until the first time the value is accessed. + (SymPy versions before 1.0 would create the instance during class + creation time, which would be prone to import cycles.) + """ + def __init__(cls, *args, **kwargs): + cls._instance = obj = Basic.__new__(cls) + cls.__new__ = lambda cls: obj + cls.__getnewargs__ = lambda obj: () + cls.__getstate__ = lambda obj: None + S.register(cls) + + +# Delayed to avoid cyclic import +from .basic import Basic diff --git a/lib/python3.12/site-packages/sympy/core/sorting.py b/lib/python3.12/site-packages/sympy/core/sorting.py new file mode 100644 index 0000000000000000000000000000000000000000..399a7efa1f6cbe1ebdf6307c14b411df36fc7de0 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/core/sorting.py @@ -0,0 +1,312 @@ +from collections import defaultdict + +from .sympify import sympify, SympifyError +from sympy.utilities.iterables import iterable, uniq + + +__all__ = ['default_sort_key', 'ordered'] + + +def default_sort_key(item, order=None): + """Return a key that can be used for sorting. + + The key has the structure: + + (class_key, (len(args), args), exponent.sort_key(), coefficient) + + This key is supplied by the sort_key routine of Basic objects when + ``item`` is a Basic object or an object (other than a string) that + sympifies to a Basic object. Otherwise, this function produces the + key. + + The ``order`` argument is passed along to the sort_key routine and is + used to determine how the terms *within* an expression are ordered. + (See examples below) ``order`` options are: 'lex', 'grlex', 'grevlex', + and reversed values of the same (e.g. 'rev-lex'). The default order + value is None (which translates to 'lex'). + + Examples + ======== + + >>> from sympy import S, I, default_sort_key, sin, cos, sqrt + >>> from sympy.core.function import UndefinedFunction + >>> from sympy.abc import x + + The following are equivalent ways of getting the key for an object: + + >>> x.sort_key() == default_sort_key(x) + True + + Here are some examples of the key that is produced: + + >>> default_sort_key(UndefinedFunction('f')) + ((0, 0, 'UndefinedFunction'), (1, ('f',)), ((1, 0, 'Number'), + (0, ()), (), 1), 1) + >>> default_sort_key('1') + ((0, 0, 'str'), (1, ('1',)), ((1, 0, 'Number'), (0, ()), (), 1), 1) + >>> default_sort_key(S.One) + ((1, 0, 'Number'), (0, ()), (), 1) + >>> default_sort_key(2) + ((1, 0, 'Number'), (0, ()), (), 2) + + While sort_key is a method only defined for SymPy objects, + default_sort_key will accept anything as an argument so it is + more robust as a sorting key. For the following, using key= + lambda i: i.sort_key() would fail because 2 does not have a sort_key + method; that's why default_sort_key is used. Note, that it also + handles sympification of non-string items likes ints: + + >>> a = [2, I, -I] + >>> sorted(a, key=default_sort_key) + [2, -I, I] + + The returned key can be used anywhere that a key can be specified for + a function, e.g. sort, min, max, etc...: + + >>> a.sort(key=default_sort_key); a[0] + 2 + >>> min(a, key=default_sort_key) + 2 + + Notes + ===== + + The key returned is useful for getting items into a canonical order + that will be the same across platforms. It is not directly useful for + sorting lists of expressions: + + >>> a, b = x, 1/x + + Since ``a`` has only 1 term, its value of sort_key is unaffected by + ``order``: + + >>> a.sort_key() == a.sort_key('rev-lex') + True + + If ``a`` and ``b`` are combined then the key will differ because there + are terms that can be ordered: + + >>> eq = a + b + >>> eq.sort_key() == eq.sort_key('rev-lex') + False + >>> eq.as_ordered_terms() + [x, 1/x] + >>> eq.as_ordered_terms('rev-lex') + [1/x, x] + + But since the keys for each of these terms are independent of ``order``'s + value, they do not sort differently when they appear separately in a list: + + >>> sorted(eq.args, key=default_sort_key) + [1/x, x] + >>> sorted(eq.args, key=lambda i: default_sort_key(i, order='rev-lex')) + [1/x, x] + + The order of terms obtained when using these keys is the order that would + be obtained if those terms were *factors* in a product. + + Although it is useful for quickly putting expressions in canonical order, + it does not sort expressions based on their complexity defined by the + number of operations, power of variables and others: + + >>> sorted([sin(x)*cos(x), sin(x)], key=default_sort_key) + [sin(x)*cos(x), sin(x)] + >>> sorted([x, x**2, sqrt(x), x**3], key=default_sort_key) + [sqrt(x), x, x**2, x**3] + + See Also + ======== + + ordered, sympy.core.expr.Expr.as_ordered_factors, sympy.core.expr.Expr.as_ordered_terms + + """ + from .basic import Basic + from .singleton import S + + if isinstance(item, Basic): + return item.sort_key(order=order) + + if iterable(item, exclude=str): + if isinstance(item, dict): + args = item.items() + unordered = True + elif isinstance(item, set): + args = item + unordered = True + else: + # e.g. tuple, list + args = list(item) + unordered = False + + args = [default_sort_key(arg, order=order) for arg in args] + + if unordered: + # e.g. dict, set + args = sorted(args) + + cls_index, args = 10, (len(args), tuple(args)) + else: + if not isinstance(item, str): + try: + item = sympify(item, strict=True) + except SympifyError: + # e.g. lambda x: x + pass + else: + if isinstance(item, Basic): + # e.g int -> Integer + return default_sort_key(item) + # e.g. UndefinedFunction + + # e.g. str + cls_index, args = 0, (1, (str(item),)) + + return (cls_index, 0, item.__class__.__name__ + ), args, S.One.sort_key(), S.One + + +def _node_count(e): + # this not only counts nodes, it affirms that the + # args are Basic (i.e. have an args property). If + # some object has a non-Basic arg, it needs to be + # fixed since it is intended that all Basic args + # are of Basic type (though this is not easy to enforce). + if e.is_Float: + return 0.5 + return 1 + sum(map(_node_count, e.args)) + + +def _nodes(e): + """ + A helper for ordered() which returns the node count of ``e`` which + for Basic objects is the number of Basic nodes in the expression tree + but for other objects is 1 (unless the object is an iterable or dict + for which the sum of nodes is returned). + """ + from .basic import Basic + from .function import Derivative + + if isinstance(e, Basic): + if isinstance(e, Derivative): + return _nodes(e.expr) + sum(i[1] if i[1].is_Number else + _nodes(i[1]) for i in e.variable_count) + return _node_count(e) + elif iterable(e): + return 1 + sum(_nodes(ei) for ei in e) + elif isinstance(e, dict): + return 1 + sum(_nodes(k) + _nodes(v) for k, v in e.items()) + else: + return 1 + + +def ordered(seq, keys=None, default=True, warn=False): + """Return an iterator of the seq where keys are used to break ties + in a conservative fashion: if, after applying a key, there are no + ties then no other keys will be computed. + + Two default keys will be applied if 1) keys are not provided or + 2) the given keys do not resolve all ties (but only if ``default`` + is True). The two keys are ``_nodes`` (which places smaller + expressions before large) and ``default_sort_key`` which (if the + ``sort_key`` for an object is defined properly) should resolve + any ties. This strategy is similar to sorting done by + ``Basic.compare``, but differs in that ``ordered`` never makes a + decision based on an objects name. + + If ``warn`` is True then an error will be raised if there were no + keys remaining to break ties. This can be used if it was expected that + there should be no ties between items that are not identical. + + Examples + ======== + + >>> from sympy import ordered, count_ops + >>> from sympy.abc import x, y + + The count_ops is not sufficient to break ties in this list and the first + two items appear in their original order (i.e. the sorting is stable): + + >>> list(ordered([y + 2, x + 2, x**2 + y + 3], + ... count_ops, default=False, warn=False)) + ... + [y + 2, x + 2, x**2 + y + 3] + + The default_sort_key allows the tie to be broken: + + >>> list(ordered([y + 2, x + 2, x**2 + y + 3])) + ... + [x + 2, y + 2, x**2 + y + 3] + + Here, sequences are sorted by length, then sum: + + >>> seq, keys = [[[1, 2, 1], [0, 3, 1], [1, 1, 3], [2], [1]], [ + ... lambda x: len(x), + ... lambda x: sum(x)]] + ... + >>> list(ordered(seq, keys, default=False, warn=False)) + [[1], [2], [1, 2, 1], [0, 3, 1], [1, 1, 3]] + + If ``warn`` is True, an error will be raised if there were not + enough keys to break ties: + + >>> list(ordered(seq, keys, default=False, warn=True)) + Traceback (most recent call last): + ... + ValueError: not enough keys to break ties + + + Notes + ===== + + The decorated sort is one of the fastest ways to sort a sequence for + which special item comparison is desired: the sequence is decorated, + sorted on the basis of the decoration (e.g. making all letters lower + case) and then undecorated. If one wants to break ties for items that + have the same decorated value, a second key can be used. But if the + second key is expensive to compute then it is inefficient to decorate + all items with both keys: only those items having identical first key + values need to be decorated. This function applies keys successively + only when needed to break ties. By yielding an iterator, use of the + tie-breaker is delayed as long as possible. + + This function is best used in cases when use of the first key is + expected to be a good hashing function; if there are no unique hashes + from application of a key, then that key should not have been used. The + exception, however, is that even if there are many collisions, if the + first group is small and one does not need to process all items in the + list then time will not be wasted sorting what one was not interested + in. For example, if one were looking for the minimum in a list and + there were several criteria used to define the sort order, then this + function would be good at returning that quickly if the first group + of candidates is small relative to the number of items being processed. + + """ + + d = defaultdict(list) + if keys: + if isinstance(keys, (list, tuple)): + keys = list(keys) + f = keys.pop(0) + else: + f = keys + keys = [] + for a in seq: + d[f(a)].append(a) + else: + if not default: + raise ValueError('if default=False then keys must be provided') + d[None].extend(seq) + + for k, value in sorted(d.items()): + if len(value) > 1: + if keys: + value = ordered(value, keys, default, warn) + elif default: + value = ordered(value, (_nodes, default_sort_key,), + default=False, warn=warn) + elif warn: + u = list(uniq(value)) + if len(u) > 1: + raise ValueError( + 'not enough keys to break ties: %s' % u) + yield from value diff --git a/lib/python3.12/site-packages/sympy/core/symbol.py b/lib/python3.12/site-packages/sympy/core/symbol.py new file mode 100644 index 0000000000000000000000000000000000000000..2b51740dfadb430567c1766f42d5e9d1ebf41dc9 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/core/symbol.py @@ -0,0 +1,979 @@ +from __future__ import annotations + +from .assumptions import StdFactKB, _assume_defined +from .basic import Basic, Atom +from .cache import cacheit +from .containers import Tuple +from .expr import Expr, AtomicExpr +from .function import AppliedUndef, FunctionClass +from .kind import NumberKind, UndefinedKind +from .logic import fuzzy_bool +from .singleton import S +from .sorting import ordered +from .sympify import sympify +from sympy.logic.boolalg import Boolean +from sympy.utilities.iterables import sift, is_sequence +from sympy.utilities.misc import filldedent + +import string +import re as _re +import random +from itertools import product +from typing import Any + + +class Str(Atom): + """ + Represents string in SymPy. + + Explanation + =========== + + Previously, ``Symbol`` was used where string is needed in ``args`` of SymPy + objects, e.g. denoting the name of the instance. However, since ``Symbol`` + represents mathematical scalar, this class should be used instead. + + """ + __slots__ = ('name',) + + def __new__(cls, name, **kwargs): + if not isinstance(name, str): + raise TypeError("name should be a string, not %s" % repr(type(name))) + obj = Expr.__new__(cls, **kwargs) + obj.name = name + return obj + + def __getnewargs__(self): + return (self.name,) + + def _hashable_content(self): + return (self.name,) + + +def _filter_assumptions(kwargs): + """Split the given dict into assumptions and non-assumptions. + Keys are taken as assumptions if they correspond to an + entry in ``_assume_defined``. + """ + assumptions, nonassumptions = map(dict, sift(kwargs.items(), + lambda i: i[0] in _assume_defined, + binary=True)) + Symbol._sanitize(assumptions) + return assumptions, nonassumptions + +def _symbol(s, matching_symbol=None, **assumptions): + """Return s if s is a Symbol, else if s is a string, return either + the matching_symbol if the names are the same or else a new symbol + with the same assumptions as the matching symbol (or the + assumptions as provided). + + Examples + ======== + + >>> from sympy import Symbol + >>> from sympy.core.symbol import _symbol + >>> _symbol('y') + y + >>> _.is_real is None + True + >>> _symbol('y', real=True).is_real + True + + >>> x = Symbol('x') + >>> _symbol(x, real=True) + x + >>> _.is_real is None # ignore attribute if s is a Symbol + True + + Below, the variable sym has the name 'foo': + + >>> sym = Symbol('foo', real=True) + + Since 'x' is not the same as sym's name, a new symbol is created: + + >>> _symbol('x', sym).name + 'x' + + It will acquire any assumptions give: + + >>> _symbol('x', sym, real=False).is_real + False + + Since 'foo' is the same as sym's name, sym is returned + + >>> _symbol('foo', sym) + foo + + Any assumptions given are ignored: + + >>> _symbol('foo', sym, real=False).is_real + True + + NB: the symbol here may not be the same as a symbol with the same + name defined elsewhere as a result of different assumptions. + + See Also + ======== + + sympy.core.symbol.Symbol + + """ + if isinstance(s, str): + if matching_symbol and matching_symbol.name == s: + return matching_symbol + return Symbol(s, **assumptions) + elif isinstance(s, Symbol): + return s + else: + raise ValueError('symbol must be string for symbol name or Symbol') + +def uniquely_named_symbol(xname, exprs=(), compare=str, modify=None, **assumptions): + """ + Return a symbol whose name is derivated from *xname* but is unique + from any other symbols in *exprs*. + + *xname* and symbol names in *exprs* are passed to *compare* to be + converted to comparable forms. If ``compare(xname)`` is not unique, + it is recursively passed to *modify* until unique name is acquired. + + Parameters + ========== + + xname : str or Symbol + Base name for the new symbol. + + exprs : Expr or iterable of Expr + Expressions whose symbols are compared to *xname*. + + compare : function + Unary function which transforms *xname* and symbol names from + *exprs* to comparable form. + + modify : function + Unary function which modifies the string. Default is appending + the number, or increasing the number if exists. + + Examples + ======== + + By default, a number is appended to *xname* to generate unique name. + If the number already exists, it is recursively increased. + + >>> from sympy.core.symbol import uniquely_named_symbol, Symbol + >>> uniquely_named_symbol('x', Symbol('x')) + x0 + >>> uniquely_named_symbol('x', (Symbol('x'), Symbol('x0'))) + x1 + >>> uniquely_named_symbol('x0', (Symbol('x1'), Symbol('x0'))) + x2 + + Name generation can be controlled by passing *modify* parameter. + + >>> from sympy.abc import x + >>> uniquely_named_symbol('x', x, modify=lambda s: 2*s) + xx + + """ + def numbered_string_incr(s, start=0): + if not s: + return str(start) + i = len(s) - 1 + while i != -1: + if not s[i].isdigit(): + break + i -= 1 + n = str(int(s[i + 1:] or start - 1) + 1) + return s[:i + 1] + n + + default = None + if is_sequence(xname): + xname, default = xname + x = compare(xname) + if not exprs: + return _symbol(x, default, **assumptions) + if not is_sequence(exprs): + exprs = [exprs] + names = set().union( + [i.name for e in exprs for i in e.atoms(Symbol)] + + [i.func.name for e in exprs for i in e.atoms(AppliedUndef)]) + if modify is None: + modify = numbered_string_incr + while any(x == compare(s) for s in names): + x = modify(x) + return _symbol(x, default, **assumptions) +_uniquely_named_symbol = uniquely_named_symbol + +class Symbol(AtomicExpr, Boolean): + """ + Symbol class is used to create symbolic variables. + + Explanation + =========== + + Symbolic variables are placeholders for mathematical symbols that can represent numbers, constants, or any other mathematical entities and can be used in mathematical expressions and to perform symbolic computations. + + Assumptions: + + commutative = True + positive = True + real = True + imaginary = True + complex = True + complete list of more assumptions- :ref:`predicates` + + You can override the default assumptions in the constructor. + + Examples + ======== + + >>> from sympy import Symbol + >>> x = Symbol("x", positive=True) + >>> x.is_positive + True + >>> x.is_negative + False + + passing in greek letters: + + >>> from sympy import Symbol + >>> alpha = Symbol('alpha') + >>> alpha #doctest: +SKIP + α + + Trailing digits are automatically treated like subscripts of what precedes them in the name. + General format to add subscript to a symbol : + `` = Symbol('_')`` + + >>> from sympy import Symbol + >>> alpha_i = Symbol('alpha_i') + >>> alpha_i #doctest: +SKIP + αᵢ + + Parameters + ========== + + AtomicExpr: variable name + Boolean: Assumption with a boolean value(True or False) + """ + + is_comparable = False + + __slots__ = ('name', '_assumptions_orig', '_assumptions0') + + name: str + + is_Symbol = True + is_symbol = True + + @property + def kind(self): + if self.is_commutative: + return NumberKind + return UndefinedKind + + @property + def _diff_wrt(self): + """Allow derivatives wrt Symbols. + + Examples + ======== + + >>> from sympy import Symbol + >>> x = Symbol('x') + >>> x._diff_wrt + True + """ + return True + + @staticmethod + def _sanitize(assumptions, obj=None): + """Remove None, convert values to bool, check commutativity *in place*. + """ + + # be strict about commutativity: cannot be None + is_commutative = fuzzy_bool(assumptions.get('commutative', True)) + if is_commutative is None: + whose = '%s ' % obj.__name__ if obj else '' + raise ValueError( + '%scommutativity must be True or False.' % whose) + + # sanitize other assumptions so 1 -> True and 0 -> False + for key in list(assumptions.keys()): + v = assumptions[key] + if v is None: + assumptions.pop(key) + continue + assumptions[key] = bool(v) + + def _merge(self, assumptions): + base = self.assumptions0 + for k in set(assumptions) & set(base): + if assumptions[k] != base[k]: + raise ValueError(filldedent(''' + non-matching assumptions for %s: existing value + is %s and new value is %s''' % ( + k, base[k], assumptions[k]))) + base.update(assumptions) + return base + + def __new__(cls, name, **assumptions): + """Symbols are identified by name and assumptions:: + + >>> from sympy import Symbol + >>> Symbol("x") == Symbol("x") + True + >>> Symbol("x", real=True) == Symbol("x", real=False) + False + + """ + cls._sanitize(assumptions, cls) + return Symbol.__xnew_cached_(cls, name, **assumptions) + + @staticmethod + def __xnew__(cls, name, **assumptions): # never cached (e.g. dummy) + if not isinstance(name, str): + raise TypeError("name should be a string, not %s" % repr(type(name))) + + # This is retained purely so that srepr can include commutative=True if + # that was explicitly specified but not if it was not. Ideally srepr + # should not distinguish these cases because the symbols otherwise + # compare equal and are considered equivalent. + # + # See https://github.com/sympy/sympy/issues/8873 + # + assumptions_orig = assumptions.copy() + + # The only assumption that is assumed by default is comutative=True: + assumptions.setdefault('commutative', True) + + assumptions_kb = StdFactKB(assumptions) + assumptions0 = dict(assumptions_kb) + + obj = Expr.__new__(cls) + obj.name = name + + obj._assumptions = assumptions_kb + obj._assumptions_orig = assumptions_orig + obj._assumptions0 = assumptions0 + + # The three assumptions dicts are all a little different: + # + # >>> from sympy import Symbol + # >>> x = Symbol('x', finite=True) + # >>> x.is_positive # query an assumption + # >>> x._assumptions + # {'finite': True, 'infinite': False, 'commutative': True, 'positive': None} + # >>> x._assumptions0 + # {'finite': True, 'infinite': False, 'commutative': True} + # >>> x._assumptions_orig + # {'finite': True} + # + # Two symbols with the same name are equal if their _assumptions0 are + # the same. Arguably it should be _assumptions_orig that is being + # compared because that is more transparent to the user (it is + # what was passed to the constructor modulo changes made by _sanitize). + + return obj + + @staticmethod + @cacheit + def __xnew_cached_(cls, name, **assumptions): # symbols are always cached + return Symbol.__xnew__(cls, name, **assumptions) + + def __getnewargs_ex__(self): + return ((self.name,), self._assumptions_orig) + + # NOTE: __setstate__ is not needed for pickles created by __getnewargs_ex__ + # but was used before Symbol was changed to use __getnewargs_ex__ in v1.9. + # Pickles created in previous SymPy versions will still need __setstate__ + # so that they can be unpickled in SymPy > v1.9. + + def __setstate__(self, state): + for name, value in state.items(): + setattr(self, name, value) + + def _hashable_content(self): + # Note: user-specified assumptions not hashed, just derived ones + return (self.name,) + tuple(sorted(self.assumptions0.items())) + + def _eval_subs(self, old, new): + if old.is_Pow: + from sympy.core.power import Pow + return Pow(self, S.One, evaluate=False)._eval_subs(old, new) + + def _eval_refine(self, assumptions): + return self + + @property + def assumptions0(self): + return self._assumptions0.copy() + + @cacheit + def sort_key(self, order=None): + return self.class_key(), (1, (self.name,)), S.One.sort_key(), S.One + + def as_dummy(self): + # only put commutativity in explicitly if it is False + return Dummy(self.name) if self.is_commutative is not False \ + else Dummy(self.name, commutative=self.is_commutative) + + def as_real_imag(self, deep=True, **hints): + if hints.get('ignore') == self: + return None + else: + from sympy.functions.elementary.complexes import im, re + return (re(self), im(self)) + + def is_constant(self, *wrt, **flags): + if not wrt: + return False + return self not in wrt + + @property + def free_symbols(self): + return {self} + + binary_symbols = free_symbols # in this case, not always + + def as_set(self): + return S.UniversalSet + + +class Dummy(Symbol): + """Dummy symbols are each unique, even if they have the same name: + + Examples + ======== + + >>> from sympy import Dummy + >>> Dummy("x") == Dummy("x") + False + + If a name is not supplied then a string value of an internal count will be + used. This is useful when a temporary variable is needed and the name + of the variable used in the expression is not important. + + >>> Dummy() #doctest: +SKIP + _Dummy_10 + + """ + + # In the rare event that a Dummy object needs to be recreated, both the + # `name` and `dummy_index` should be passed. This is used by `srepr` for + # example: + # >>> d1 = Dummy() + # >>> d2 = eval(srepr(d1)) + # >>> d2 == d1 + # True + # + # If a new session is started between `srepr` and `eval`, there is a very + # small chance that `d2` will be equal to a previously-created Dummy. + + _count = 0 + _prng = random.Random() + _base_dummy_index = _prng.randint(10**6, 9*10**6) + + __slots__ = ('dummy_index',) + + is_Dummy = True + + def __new__(cls, name=None, dummy_index=None, **assumptions): + if dummy_index is not None: + assert name is not None, "If you specify a dummy_index, you must also provide a name" + + if name is None: + name = "Dummy_" + str(Dummy._count) + + if dummy_index is None: + dummy_index = Dummy._base_dummy_index + Dummy._count + Dummy._count += 1 + + cls._sanitize(assumptions, cls) + obj = Symbol.__xnew__(cls, name, **assumptions) + + obj.dummy_index = dummy_index + + return obj + + def __getnewargs_ex__(self): + return ((self.name, self.dummy_index), self._assumptions_orig) + + @cacheit + def sort_key(self, order=None): + return self.class_key(), ( + 2, (self.name, self.dummy_index)), S.One.sort_key(), S.One + + def _hashable_content(self): + return Symbol._hashable_content(self) + (self.dummy_index,) + + +class Wild(Symbol): + """ + A Wild symbol matches anything, or anything + without whatever is explicitly excluded. + + Parameters + ========== + + name : str + Name of the Wild instance. + + exclude : iterable, optional + Instances in ``exclude`` will not be matched. + + properties : iterable of functions, optional + Functions, each taking an expressions as input + and returns a ``bool``. All functions in ``properties`` + need to return ``True`` in order for the Wild instance + to match the expression. + + Examples + ======== + + >>> from sympy import Wild, WildFunction, cos, pi + >>> from sympy.abc import x, y, z + >>> a = Wild('a') + >>> x.match(a) + {a_: x} + >>> pi.match(a) + {a_: pi} + >>> (3*x**2).match(a*x) + {a_: 3*x} + >>> cos(x).match(a) + {a_: cos(x)} + >>> b = Wild('b', exclude=[x]) + >>> (3*x**2).match(b*x) + >>> b.match(a) + {a_: b_} + >>> A = WildFunction('A') + >>> A.match(a) + {a_: A_} + + Tips + ==== + + When using Wild, be sure to use the exclude + keyword to make the pattern more precise. + Without the exclude pattern, you may get matches + that are technically correct, but not what you + wanted. For example, using the above without + exclude: + + >>> from sympy import symbols + >>> a, b = symbols('a b', cls=Wild) + >>> (2 + 3*y).match(a*x + b*y) + {a_: 2/x, b_: 3} + + This is technically correct, because + (2/x)*x + 3*y == 2 + 3*y, but you probably + wanted it to not match at all. The issue is that + you really did not want a and b to include x and y, + and the exclude parameter lets you specify exactly + this. With the exclude parameter, the pattern will + not match. + + >>> a = Wild('a', exclude=[x, y]) + >>> b = Wild('b', exclude=[x, y]) + >>> (2 + 3*y).match(a*x + b*y) + + Exclude also helps remove ambiguity from matches. + + >>> E = 2*x**3*y*z + >>> a, b = symbols('a b', cls=Wild) + >>> E.match(a*b) + {a_: 2*y*z, b_: x**3} + >>> a = Wild('a', exclude=[x, y]) + >>> E.match(a*b) + {a_: z, b_: 2*x**3*y} + >>> a = Wild('a', exclude=[x, y, z]) + >>> E.match(a*b) + {a_: 2, b_: x**3*y*z} + + Wild also accepts a ``properties`` parameter: + + >>> a = Wild('a', properties=[lambda k: k.is_Integer]) + >>> E.match(a*b) + {a_: 2, b_: x**3*y*z} + + """ + is_Wild = True + + __slots__ = ('exclude', 'properties') + + def __new__(cls, name, exclude=(), properties=(), **assumptions): + exclude = tuple([sympify(x) for x in exclude]) + properties = tuple(properties) + cls._sanitize(assumptions, cls) + return Wild.__xnew__(cls, name, exclude, properties, **assumptions) + + def __getnewargs__(self): + return (self.name, self.exclude, self.properties) + + @staticmethod + @cacheit + def __xnew__(cls, name, exclude, properties, **assumptions): + obj = Symbol.__xnew__(cls, name, **assumptions) + obj.exclude = exclude + obj.properties = properties + return obj + + def _hashable_content(self): + return super()._hashable_content() + (self.exclude, self.properties) + + # TODO add check against another Wild + def matches(self, expr, repl_dict=None, old=False): + if any(expr.has(x) for x in self.exclude): + return None + if not all(f(expr) for f in self.properties): + return None + if repl_dict is None: + repl_dict = {} + else: + repl_dict = repl_dict.copy() + repl_dict[self] = expr + return repl_dict + + +_range = _re.compile('([0-9]*:[0-9]+|[a-zA-Z]?:[a-zA-Z])') + + +def symbols(names, *, cls=Symbol, **args) -> Any: + r""" + Transform strings into instances of :class:`Symbol` class. + + :func:`symbols` function returns a sequence of symbols with names taken + from ``names`` argument, which can be a comma or whitespace delimited + string, or a sequence of strings:: + + >>> from sympy import symbols, Function + + >>> x, y, z = symbols('x,y,z') + >>> a, b, c = symbols('a b c') + + The type of output is dependent on the properties of input arguments:: + + >>> symbols('x') + x + >>> symbols('x,') + (x,) + >>> symbols('x,y') + (x, y) + >>> symbols(('a', 'b', 'c')) + (a, b, c) + >>> symbols(['a', 'b', 'c']) + [a, b, c] + >>> symbols({'a', 'b', 'c'}) + {a, b, c} + + If an iterable container is needed for a single symbol, set the ``seq`` + argument to ``True`` or terminate the symbol name with a comma:: + + >>> symbols('x', seq=True) + (x,) + + To reduce typing, range syntax is supported to create indexed symbols. + Ranges are indicated by a colon and the type of range is determined by + the character to the right of the colon. If the character is a digit + then all contiguous digits to the left are taken as the nonnegative + starting value (or 0 if there is no digit left of the colon) and all + contiguous digits to the right are taken as 1 greater than the ending + value:: + + >>> symbols('x:10') + (x0, x1, x2, x3, x4, x5, x6, x7, x8, x9) + + >>> symbols('x5:10') + (x5, x6, x7, x8, x9) + >>> symbols('x5(:2)') + (x50, x51) + + >>> symbols('x5:10,y:5') + (x5, x6, x7, x8, x9, y0, y1, y2, y3, y4) + + >>> symbols(('x5:10', 'y:5')) + ((x5, x6, x7, x8, x9), (y0, y1, y2, y3, y4)) + + If the character to the right of the colon is a letter, then the single + letter to the left (or 'a' if there is none) is taken as the start + and all characters in the lexicographic range *through* the letter to + the right are used as the range:: + + >>> symbols('x:z') + (x, y, z) + >>> symbols('x:c') # null range + () + >>> symbols('x(:c)') + (xa, xb, xc) + + >>> symbols(':c') + (a, b, c) + + >>> symbols('a:d, x:z') + (a, b, c, d, x, y, z) + + >>> symbols(('a:d', 'x:z')) + ((a, b, c, d), (x, y, z)) + + Multiple ranges are supported; contiguous numerical ranges should be + separated by parentheses to disambiguate the ending number of one + range from the starting number of the next:: + + >>> symbols('x:2(1:3)') + (x01, x02, x11, x12) + >>> symbols(':3:2') # parsing is from left to right + (00, 01, 10, 11, 20, 21) + + Only one pair of parentheses surrounding ranges are removed, so to + include parentheses around ranges, double them. And to include spaces, + commas, or colons, escape them with a backslash:: + + >>> symbols('x((a:b))') + (x(a), x(b)) + >>> symbols(r'x(:1\,:2)') # or r'x((:1)\,(:2))' + (x(0,0), x(0,1)) + + All newly created symbols have assumptions set according to ``args``:: + + >>> a = symbols('a', integer=True) + >>> a.is_integer + True + + >>> x, y, z = symbols('x,y,z', real=True) + >>> x.is_real and y.is_real and z.is_real + True + + Despite its name, :func:`symbols` can create symbol-like objects like + instances of Function or Wild classes. To achieve this, set ``cls`` + keyword argument to the desired type:: + + >>> symbols('f,g,h', cls=Function) + (f, g, h) + + >>> type(_[0]) + + + """ + result = [] + + if isinstance(names, str): + marker = 0 + splitters = r'\,', r'\:', r'\ ' + literals: list[tuple[str, str]] = [] + for splitter in splitters: + if splitter in names: + while chr(marker) in names: + marker += 1 + lit_char = chr(marker) + marker += 1 + names = names.replace(splitter, lit_char) + literals.append((lit_char, splitter[1:])) + def literal(s): + if literals: + for c, l in literals: + s = s.replace(c, l) + return s + + names = names.strip() + as_seq = names.endswith(',') + if as_seq: + names = names[:-1].rstrip() + if not names: + raise ValueError('no symbols given') + + # split on commas + names = [n.strip() for n in names.split(',')] + if not all(n for n in names): + raise ValueError('missing symbol between commas') + # split on spaces + for i in range(len(names) - 1, -1, -1): + names[i: i + 1] = names[i].split() + + seq = args.pop('seq', as_seq) + + for name in names: + if not name: + raise ValueError('missing symbol') + + if ':' not in name: + symbol = cls(literal(name), **args) + result.append(symbol) + continue + + split: list[str] = _range.split(name) + split_list: list[list[str]] = [] + # remove 1 layer of bounding parentheses around ranges + for i in range(len(split) - 1): + if i and ':' in split[i] and split[i] != ':' and \ + split[i - 1].endswith('(') and \ + split[i + 1].startswith(')'): + split[i - 1] = split[i - 1][:-1] + split[i + 1] = split[i + 1][1:] + for s in split: + if ':' in s: + if s.endswith(':'): + raise ValueError('missing end range') + a, b = s.split(':') + if b[-1] in string.digits: + a_i = 0 if not a else int(a) + b_i = int(b) + split_list.append([str(c) for c in range(a_i, b_i)]) + else: + a = a or 'a' + split_list.append([string.ascii_letters[c] for c in range( + string.ascii_letters.index(a), + string.ascii_letters.index(b) + 1)]) # inclusive + if not split_list[-1]: + break + else: + split_list.append([s]) + else: + seq = True + if len(split_list) == 1: + names = split_list[0] + else: + names = [''.join(s) for s in product(*split_list)] + if literals: + result.extend([cls(literal(s), **args) for s in names]) + else: + result.extend([cls(s, **args) for s in names]) + + if not seq and len(result) <= 1: + if not result: + return () + return result[0] + + return tuple(result) + else: + for name in names: + result.append(symbols(name, cls=cls, **args)) + + return type(names)(result) + + +def var(names, **args): + """ + Create symbols and inject them into the global namespace. + + Explanation + =========== + + This calls :func:`symbols` with the same arguments and puts the results + into the *global* namespace. It's recommended not to use :func:`var` in + library code, where :func:`symbols` has to be used:: + + Examples + ======== + + >>> from sympy import var + + >>> var('x') + x + >>> x # noqa: F821 + x + + >>> var('a,ab,abc') + (a, ab, abc) + >>> abc # noqa: F821 + abc + + >>> var('x,y', real=True) + (x, y) + >>> x.is_real and y.is_real # noqa: F821 + True + + See :func:`symbols` documentation for more details on what kinds of + arguments can be passed to :func:`var`. + + """ + def traverse(symbols, frame): + """Recursively inject symbols to the global namespace. """ + for symbol in symbols: + if isinstance(symbol, Basic): + frame.f_globals[symbol.name] = symbol + elif isinstance(symbol, FunctionClass): + frame.f_globals[symbol.__name__] = symbol + else: + traverse(symbol, frame) + + from inspect import currentframe + frame = currentframe().f_back + + try: + syms = symbols(names, **args) + + if syms is not None: + if isinstance(syms, Basic): + frame.f_globals[syms.name] = syms + elif isinstance(syms, FunctionClass): + frame.f_globals[syms.__name__] = syms + else: + traverse(syms, frame) + finally: + del frame # break cyclic dependencies as stated in inspect docs + + return syms + +def disambiguate(*iter): + """ + Return a Tuple containing the passed expressions with symbols + that appear the same when printed replaced with numerically + subscripted symbols, and all Dummy symbols replaced with Symbols. + + Parameters + ========== + + iter: list of symbols or expressions. + + Examples + ======== + + >>> from sympy.core.symbol import disambiguate + >>> from sympy import Dummy, Symbol, Tuple + >>> from sympy.abc import y + + >>> tup = Symbol('_x'), Dummy('x'), Dummy('x') + >>> disambiguate(*tup) + (x_2, x, x_1) + + >>> eqs = Tuple(Symbol('x')/y, Dummy('x')/y) + >>> disambiguate(*eqs) + (x_1/y, x/y) + + >>> ix = Symbol('x', integer=True) + >>> vx = Symbol('x') + >>> disambiguate(vx + ix) + (x + x_1,) + + To make your own mapping of symbols to use, pass only the free symbols + of the expressions and create a dictionary: + + >>> free = eqs.free_symbols + >>> mapping = dict(zip(free, disambiguate(*free))) + >>> eqs.xreplace(mapping) + (x_1/y, x/y) + + """ + new_iter = Tuple(*iter) + key = lambda x:tuple(sorted(x.assumptions0.items())) + syms = ordered(new_iter.free_symbols, keys=key) + mapping = {} + for s in syms: + mapping.setdefault(str(s).lstrip('_'), []).append(s) + reps = {} + for k in mapping: + # the first or only symbol doesn't get subscripted but make + # sure that it's a Symbol, not a Dummy + mapk0 = Symbol("%s" % (k), **mapping[k][0].assumptions0) + if mapping[k][0] != mapk0: + reps[mapping[k][0]] = mapk0 + # the others get subscripts (and are made into Symbols) + skip = 0 + for i in range(1, len(mapping[k])): + while True: + name = "%s_%i" % (k, i + skip) + if name not in mapping: + break + skip += 1 + ki = mapping[k][i] + reps[ki] = Symbol(name, **ki.assumptions0) + return new_iter.xreplace(reps) diff --git a/lib/python3.12/site-packages/sympy/core/sympify.py b/lib/python3.12/site-packages/sympy/core/sympify.py new file mode 100644 index 0000000000000000000000000000000000000000..c849462799c7f0438b954981bf7c10954dc9d964 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/core/sympify.py @@ -0,0 +1,620 @@ +"""sympify -- convert objects SymPy internal format""" + +from __future__ import annotations +from typing import Any, Callable + +import mpmath.libmp as mlib + +from inspect import getmro +import string +from sympy.core.random import choice + +from .parameters import global_parameters + +from sympy.utilities.iterables import iterable + + +class SympifyError(ValueError): + def __init__(self, expr, base_exc=None): + self.expr = expr + self.base_exc = base_exc + + def __str__(self): + if self.base_exc is None: + return "SympifyError: %r" % (self.expr,) + + return ("Sympify of expression '%s' failed, because of exception being " + "raised:\n%s: %s" % (self.expr, self.base_exc.__class__.__name__, + str(self.base_exc))) + + +converter: dict[type[Any], Callable[[Any], Basic]] = {} + +#holds the conversions defined in SymPy itself, i.e. non-user defined conversions +_sympy_converter: dict[type[Any], Callable[[Any], Basic]] = {} + +#alias for clearer use in the library +_external_converter = converter + +class CantSympify: + """ + Mix in this trait to a class to disallow sympification of its instances. + + Examples + ======== + + >>> from sympy import sympify + >>> from sympy.core.sympify import CantSympify + + >>> class Something(dict): + ... pass + ... + >>> sympify(Something()) + {} + + >>> class Something(dict, CantSympify): + ... pass + ... + >>> sympify(Something()) + Traceback (most recent call last): + ... + SympifyError: SympifyError: {} + + """ + + __slots__ = () + + +def _is_numpy_instance(a): + """ + Checks if an object is an instance of a type from the numpy module. + """ + # This check avoids unnecessarily importing NumPy. We check the whole + # __mro__ in case any base type is a numpy type. + return any(type_.__module__ == 'numpy' + for type_ in type(a).__mro__) + + +def _convert_numpy_types(a, **sympify_args): + """ + Converts a numpy datatype input to an appropriate SymPy type. + """ + import numpy as np + if not isinstance(a, np.floating): + if np.iscomplex(a): + return _sympy_converter[complex](a.item()) + else: + return sympify(a.item(), **sympify_args) + else: + from .numbers import Float + prec = np.finfo(a).nmant + 1 + # E.g. double precision means prec=53 but nmant=52 + # Leading bit of mantissa is always 1, so is not stored + p, q = a.as_integer_ratio() + a = mlib.from_rational(p, q, prec) + return Float(a, precision=prec) + + +def sympify(a, locals=None, convert_xor=True, strict=False, rational=False, + evaluate=None): + """ + Converts an arbitrary expression to a type that can be used inside SymPy. + + Explanation + =========== + + It will convert Python ints into instances of :class:`~.Integer`, floats + into instances of :class:`~.Float`, etc. It is also able to coerce + symbolic expressions which inherit from :class:`~.Basic`. This can be + useful in cooperation with SAGE. + + .. warning:: + Note that this function uses ``eval``, and thus shouldn't be used on + unsanitized input. + + If the argument is already a type that SymPy understands, it will do + nothing but return that value. This can be used at the beginning of a + function to ensure you are working with the correct type. + + Examples + ======== + + >>> from sympy import sympify + + >>> sympify(2).is_integer + True + >>> sympify(2).is_real + True + + >>> sympify(2.0).is_real + True + >>> sympify("2.0").is_real + True + >>> sympify("2e-45").is_real + True + + If the expression could not be converted, a SympifyError is raised. + + >>> sympify("x***2") + Traceback (most recent call last): + ... + SympifyError: SympifyError: "could not parse 'x***2'" + + When attempting to parse non-Python syntax using ``sympify``, it raises a + ``SympifyError``: + + >>> sympify("2x+1") + Traceback (most recent call last): + ... + SympifyError: Sympify of expression 'could not parse '2x+1'' failed + + To parse non-Python syntax, use ``parse_expr`` from ``sympy.parsing.sympy_parser``. + + >>> from sympy.parsing.sympy_parser import parse_expr + >>> parse_expr("2x+1", transformations="all") + 2*x + 1 + + For more details about ``transformations``: see :func:`~sympy.parsing.sympy_parser.parse_expr` + + Locals + ------ + + The sympification happens with access to everything that is loaded + by ``from sympy import *``; anything used in a string that is not + defined by that import will be converted to a symbol. In the following, + the ``bitcount`` function is treated as a symbol and the ``O`` is + interpreted as the :class:`~.Order` object (used with series) and it raises + an error when used improperly: + + >>> s = 'bitcount(42)' + >>> sympify(s) + bitcount(42) + >>> sympify("O(x)") + O(x) + >>> sympify("O + 1") + Traceback (most recent call last): + ... + TypeError: unbound method... + + In order to have ``bitcount`` be recognized it can be imported into a + namespace dictionary and passed as locals: + + >>> ns = {} + >>> exec('from sympy.core.evalf import bitcount', ns) + >>> sympify(s, locals=ns) + 6 + + In order to have the ``O`` interpreted as a Symbol, identify it as such + in the namespace dictionary. This can be done in a variety of ways; all + three of the following are possibilities: + + >>> from sympy import Symbol + >>> ns["O"] = Symbol("O") # method 1 + >>> exec('from sympy.abc import O', ns) # method 2 + >>> ns.update(dict(O=Symbol("O"))) # method 3 + >>> sympify("O + 1", locals=ns) + O + 1 + + If you want *all* single-letter and Greek-letter variables to be symbols + then you can use the clashing-symbols dictionaries that have been defined + there as private variables: ``_clash1`` (single-letter variables), + ``_clash2`` (the multi-letter Greek names) or ``_clash`` (both single and + multi-letter names that are defined in ``abc``). + + >>> from sympy.abc import _clash1 + >>> set(_clash1) # if this fails, see issue #23903 + {'E', 'I', 'N', 'O', 'Q', 'S'} + >>> sympify('I & Q', _clash1) + I & Q + + Strict + ------ + + If the option ``strict`` is set to ``True``, only the types for which an + explicit conversion has been defined are converted. In the other + cases, a SympifyError is raised. + + >>> print(sympify(None)) + None + >>> sympify(None, strict=True) + Traceback (most recent call last): + ... + SympifyError: SympifyError: None + + .. deprecated:: 1.6 + + ``sympify(obj)`` automatically falls back to ``str(obj)`` when all + other conversion methods fail, but this is deprecated. ``strict=True`` + will disable this deprecated behavior. See + :ref:`deprecated-sympify-string-fallback`. + + Evaluation + ---------- + + If the option ``evaluate`` is set to ``False``, then arithmetic and + operators will be converted into their SymPy equivalents and the + ``evaluate=False`` option will be added. Nested ``Add`` or ``Mul`` will + be denested first. This is done via an AST transformation that replaces + operators with their SymPy equivalents, so if an operand redefines any + of those operations, the redefined operators will not be used. If + argument a is not a string, the mathematical expression is evaluated + before being passed to sympify, so adding ``evaluate=False`` will still + return the evaluated result of expression. + + >>> sympify('2**2 / 3 + 5') + 19/3 + >>> sympify('2**2 / 3 + 5', evaluate=False) + 2**2/3 + 5 + >>> sympify('4/2+7', evaluate=True) + 9 + >>> sympify('4/2+7', evaluate=False) + 4/2 + 7 + >>> sympify(4/2+7, evaluate=False) + 9.00000000000000 + + Extending + --------- + + To extend ``sympify`` to convert custom objects (not derived from ``Basic``), + just define a ``_sympy_`` method to your class. You can do that even to + classes that you do not own by subclassing or adding the method at runtime. + + >>> from sympy import Matrix + >>> class MyList1(object): + ... def __iter__(self): + ... yield 1 + ... yield 2 + ... return + ... def __getitem__(self, i): return list(self)[i] + ... def _sympy_(self): return Matrix(self) + >>> sympify(MyList1()) + Matrix([ + [1], + [2]]) + + If you do not have control over the class definition you could also use the + ``converter`` global dictionary. The key is the class and the value is a + function that takes a single argument and returns the desired SymPy + object, e.g. ``converter[MyList] = lambda x: Matrix(x)``. + + >>> class MyList2(object): # XXX Do not do this if you control the class! + ... def __iter__(self): # Use _sympy_! + ... yield 1 + ... yield 2 + ... return + ... def __getitem__(self, i): return list(self)[i] + >>> from sympy.core.sympify import converter + >>> converter[MyList2] = lambda x: Matrix(x) + >>> sympify(MyList2()) + Matrix([ + [1], + [2]]) + + Notes + ===== + + The keywords ``rational`` and ``convert_xor`` are only used + when the input is a string. + + convert_xor + ----------- + + >>> sympify('x^y',convert_xor=True) + x**y + >>> sympify('x^y',convert_xor=False) + x ^ y + + rational + -------- + + >>> sympify('0.1',rational=False) + 0.1 + >>> sympify('0.1',rational=True) + 1/10 + + Sometimes autosimplification during sympification results in expressions + that are very different in structure than what was entered. Until such + autosimplification is no longer done, the ``kernS`` function might be of + some use. In the example below you can see how an expression reduces to + $-1$ by autosimplification, but does not do so when ``kernS`` is used. + + >>> from sympy.core.sympify import kernS + >>> from sympy.abc import x + >>> -2*(-(-x + 1/x)/(x*(x - 1/x)**2) - 1/(x*(x - 1/x))) - 1 + -1 + >>> s = '-2*(-(-x + 1/x)/(x*(x - 1/x)**2) - 1/(x*(x - 1/x))) - 1' + >>> sympify(s) + -1 + >>> kernS(s) + -2*(-(-x + 1/x)/(x*(x - 1/x)**2) - 1/(x*(x - 1/x))) - 1 + + Parameters + ========== + + a : + - any object defined in SymPy + - standard numeric Python types: ``int``, ``long``, ``float``, ``Decimal`` + - strings (like ``"0.09"``, ``"2e-19"`` or ``'sin(x)'``) + - booleans, including ``None`` (will leave ``None`` unchanged) + - dicts, lists, sets or tuples containing any of the above + + convert_xor : bool, optional + If true, treats ``^`` as exponentiation. + If False, treats ``^`` as XOR itself. + Used only when input is a string. + + locals : any object defined in SymPy, optional + In order to have strings be recognized it can be imported + into a namespace dictionary and passed as locals. + + strict : bool, optional + If the option strict is set to ``True``, only the types for which + an explicit conversion has been defined are converted. In the + other cases, a SympifyError is raised. + + rational : bool, optional + If ``True``, converts floats into :class:`~.Rational`. + If ``False``, it lets floats remain as it is. + Used only when input is a string. + + evaluate : bool, optional + If False, then arithmetic and operators will be converted into + their SymPy equivalents. If True the expression will be evaluated + and the result will be returned. + + """ + # XXX: If a is a Basic subclass rather than instance (e.g. sin rather than + # sin(x)) then a.__sympy__ will be the property. Only on the instance will + # a.__sympy__ give the *value* of the property (True). Since sympify(sin) + # was used for a long time we allow it to pass. However if strict=True as + # is the case in internal calls to _sympify then we only allow + # is_sympy=True. + # + # https://github.com/sympy/sympy/issues/20124 + is_sympy = getattr(a, '__sympy__', None) + if is_sympy is True: + return a + elif is_sympy is not None: + if not strict: + return a + else: + raise SympifyError(a) + + if isinstance(a, CantSympify): + raise SympifyError(a) + + cls = getattr(a, "__class__", None) + + #Check if there exists a converter for any of the types in the mro + for superclass in getmro(cls): + #First check for user defined converters + conv = _external_converter.get(superclass) + if conv is None: + #if none exists, check for SymPy defined converters + conv = _sympy_converter.get(superclass) + if conv is not None: + return conv(a) + + if cls is type(None): + if strict: + raise SympifyError(a) + else: + return a + + if evaluate is None: + evaluate = global_parameters.evaluate + + # Support for basic numpy datatypes + if _is_numpy_instance(a): + import numpy as np + if np.isscalar(a): + return _convert_numpy_types(a, locals=locals, + convert_xor=convert_xor, strict=strict, rational=rational, + evaluate=evaluate) + + _sympy_ = getattr(a, "_sympy_", None) + if _sympy_ is not None: + return a._sympy_() + + if not strict: + # Put numpy array conversion _before_ float/int, see + # . + flat = getattr(a, "flat", None) + if flat is not None: + shape = getattr(a, "shape", None) + if shape is not None: + from sympy.tensor.array import Array + return Array(a.flat, a.shape) # works with e.g. NumPy arrays + + if not isinstance(a, str): + if _is_numpy_instance(a): + import numpy as np + assert not isinstance(a, np.number) + if isinstance(a, np.ndarray): + # Scalar arrays (those with zero dimensions) have sympify + # called on the scalar element. + if a.ndim == 0: + try: + return sympify(a.item(), + locals=locals, + convert_xor=convert_xor, + strict=strict, + rational=rational, + evaluate=evaluate) + except SympifyError: + pass + elif hasattr(a, '__float__'): + # float and int can coerce size-one numpy arrays to their lone + # element. See issue https://github.com/numpy/numpy/issues/10404. + return sympify(float(a)) + elif hasattr(a, '__int__'): + return sympify(int(a)) + + if strict: + raise SympifyError(a) + + if iterable(a): + try: + return type(a)([sympify(x, locals=locals, convert_xor=convert_xor, + rational=rational, evaluate=evaluate) for x in a]) + except TypeError: + # Not all iterables are rebuildable with their type. + pass + + if not isinstance(a, str): + raise SympifyError('cannot sympify object of type %r' % type(a)) + + from sympy.parsing.sympy_parser import (parse_expr, TokenError, + standard_transformations) + from sympy.parsing.sympy_parser import convert_xor as t_convert_xor + from sympy.parsing.sympy_parser import rationalize as t_rationalize + + transformations = standard_transformations + + if rational: + transformations += (t_rationalize,) + if convert_xor: + transformations += (t_convert_xor,) + + try: + a = a.replace('\n', '') + expr = parse_expr(a, local_dict=locals, transformations=transformations, evaluate=evaluate) + except (TokenError, SyntaxError) as exc: + raise SympifyError('could not parse %r' % a, exc) + + return expr + + +def _sympify(a): + """ + Short version of :func:`~.sympify` for internal usage for ``__add__`` and + ``__eq__`` methods where it is ok to allow some things (like Python + integers and floats) in the expression. This excludes things (like strings) + that are unwise to allow into such an expression. + + >>> from sympy import Integer + >>> Integer(1) == 1 + True + + >>> Integer(1) == '1' + False + + >>> from sympy.abc import x + >>> x + 1 + x + 1 + + >>> x + '1' + Traceback (most recent call last): + ... + TypeError: unsupported operand type(s) for +: 'Symbol' and 'str' + + see: sympify + + """ + return sympify(a, strict=True) + + +def kernS(s): + """Use a hack to try keep autosimplification from distributing a + a number into an Add; this modification does not + prevent the 2-arg Mul from becoming an Add, however. + + Examples + ======== + + >>> from sympy.core.sympify import kernS + >>> from sympy.abc import x, y + + The 2-arg Mul distributes a number (or minus sign) across the terms + of an expression, but kernS will prevent that: + + >>> 2*(x + y), -(x + 1) + (2*x + 2*y, -x - 1) + >>> kernS('2*(x + y)') + 2*(x + y) + >>> kernS('-(x + 1)') + -(x + 1) + + If use of the hack fails, the un-hacked string will be passed to sympify... + and you get what you get. + + XXX This hack should not be necessary once issue 4596 has been resolved. + """ + hit = False + quoted = '"' in s or "'" in s + if '(' in s and not quoted: + if s.count('(') != s.count(")"): + raise SympifyError('unmatched left parenthesis') + + # strip all space from s + s = ''.join(s.split()) + olds = s + # now use space to represent a symbol that + # will + # step 1. turn potential 2-arg Muls into 3-arg versions + # 1a. *( -> * *( + s = s.replace('*(', '* *(') + # 1b. close up exponentials + s = s.replace('** *', '**') + # 2. handle the implied multiplication of a negated + # parenthesized expression in two steps + # 2a: -(...) --> -( *(...) + target = '-( *(' + s = s.replace('-(', target) + # 2b: double the matching closing parenthesis + # -( *(...) --> -( *(...)) + i = nest = 0 + assert target.endswith('(') # assumption below + while True: + j = s.find(target, i) + if j == -1: + break + j += len(target) - 1 + for j in range(j, len(s)): + if s[j] == "(": + nest += 1 + elif s[j] == ")": + nest -= 1 + if nest == 0: + break + s = s[:j] + ")" + s[j:] + i = j + 2 # the first char after 2nd ) + if ' ' in s: + # get a unique kern + kern = '_' + while kern in s: + kern += choice(string.ascii_letters + string.digits) + s = s.replace(' ', kern) + hit = kern in s + else: + hit = False + + for i in range(2): + try: + expr = sympify(s) + break + except TypeError: # the kern might cause unknown errors... + if hit: + s = olds # maybe it didn't like the kern; use un-kerned s + hit = False + continue + expr = sympify(s) # let original error raise + + if not hit: + return expr + + from .symbol import Symbol + rep = {Symbol(kern): 1} + def _clear(expr): + if isinstance(expr, (list, tuple, set)): + return type(expr)([_clear(e) for e in expr]) + if hasattr(expr, 'subs'): + return expr.subs(rep, hack2=True) + return expr + expr = _clear(expr) + # hope that kern is not there anymore + return expr + + +# Avoid circular import +from .basic import Basic diff --git a/lib/python3.12/site-packages/sympy/core/tests/__init__.py b/lib/python3.12/site-packages/sympy/core/tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/lib/python3.12/site-packages/sympy/core/tests/test_args.py b/lib/python3.12/site-packages/sympy/core/tests/test_args.py new file mode 100644 index 0000000000000000000000000000000000000000..6c69e8f5555d5f93db5cda671e039ed6d19eaf7e --- /dev/null +++ b/lib/python3.12/site-packages/sympy/core/tests/test_args.py @@ -0,0 +1,5455 @@ +"""Test whether all elements of cls.args are instances of Basic. """ + +# NOTE: keep tests sorted by (module, class name) key. If a class can't +# be instantiated, add it here anyway with @SKIP("abstract class) (see +# e.g. Function). + +import os +import re + +from sympy.assumptions.ask import Q +from sympy.core.basic import Basic +from sympy.core.function import (Function, Lambda) +from sympy.core.numbers import (Rational, oo, pi) +from sympy.core.relational import Eq +from sympy.core.singleton import S +from sympy.core.symbol import symbols +from sympy.functions.elementary.exponential import (exp, log) +from sympy.functions.elementary.miscellaneous import sqrt +from sympy.functions.elementary.trigonometric import sin + +from sympy.testing.pytest import SKIP + +a, b, c, x, y, z = symbols('a,b,c,x,y,z') + + +whitelist = [ + "sympy.assumptions.predicates", # tested by test_predicates() + "sympy.assumptions.relation.equality", # tested by test_predicates() +] + +def test_all_classes_are_tested(): + this = os.path.split(__file__)[0] + path = os.path.join(this, os.pardir, os.pardir) + sympy_path = os.path.abspath(path) + prefix = os.path.split(sympy_path)[0] + os.sep + + re_cls = re.compile(r"^class ([A-Za-z][A-Za-z0-9_]*)\s*\(", re.MULTILINE) + + modules = {} + + for root, dirs, files in os.walk(sympy_path): + module = root.replace(prefix, "").replace(os.sep, ".") + + for file in files: + if file.startswith(("_", "test_", "bench_")): + continue + if not file.endswith(".py"): + continue + + with open(os.path.join(root, file), encoding='utf-8') as f: + text = f.read() + + submodule = module + '.' + file[:-3] + + if any(submodule.startswith(wpath) for wpath in whitelist): + continue + + names = re_cls.findall(text) + + if not names: + continue + + try: + mod = __import__(submodule, fromlist=names) + except ImportError: + continue + + def is_Basic(name): + cls = getattr(mod, name) + if hasattr(cls, '_sympy_deprecated_func'): + cls = cls._sympy_deprecated_func + if not isinstance(cls, type): + # check instance of singleton class with same name + cls = type(cls) + return issubclass(cls, Basic) + + names = list(filter(is_Basic, names)) + + if names: + modules[submodule] = names + + ns = globals() + failed = [] + + for module, names in modules.items(): + mod = module.replace('.', '__') + + for name in names: + test = 'test_' + mod + '__' + name + + if test not in ns: + failed.append(module + '.' + name) + + assert not failed, "Missing classes: %s. Please add tests for these to sympy/core/tests/test_args.py." % ", ".join(failed) + + +def _test_args(obj): + all_basic = all(isinstance(arg, Basic) for arg in obj.args) + # Ideally obj.func(*obj.args) would always recreate the object, but for + # now, we only require it for objects with non-empty .args + recreatable = not obj.args or obj.func(*obj.args) == obj + return all_basic and recreatable + + +def test_sympy__algebras__quaternion__Quaternion(): + from sympy.algebras.quaternion import Quaternion + assert _test_args(Quaternion(x, 1, 2, 3)) + + +def test_sympy__assumptions__assume__AppliedPredicate(): + from sympy.assumptions.assume import AppliedPredicate, Predicate + assert _test_args(AppliedPredicate(Predicate("test"), 2)) + assert _test_args(Q.is_true(True)) + +@SKIP("abstract class") +def test_sympy__assumptions__assume__Predicate(): + pass + +def test_predicates(): + predicates = [ + getattr(Q, attr) + for attr in Q.__class__.__dict__ + if not attr.startswith('__')] + for p in predicates: + assert _test_args(p) + +def test_sympy__assumptions__assume__UndefinedPredicate(): + from sympy.assumptions.assume import Predicate + assert _test_args(Predicate("test")) + +@SKIP('abstract class') +def test_sympy__assumptions__relation__binrel__BinaryRelation(): + pass + +def test_sympy__assumptions__relation__binrel__AppliedBinaryRelation(): + assert _test_args(Q.eq(1, 2)) + +def test_sympy__assumptions__wrapper__AssumptionsWrapper(): + from sympy.assumptions.wrapper import AssumptionsWrapper + assert _test_args(AssumptionsWrapper(x, Q.positive(x))) + +@SKIP("abstract Class") +def test_sympy__codegen__ast__CodegenAST(): + from sympy.codegen.ast import CodegenAST + assert _test_args(CodegenAST()) + +@SKIP("abstract Class") +def test_sympy__codegen__ast__AssignmentBase(): + from sympy.codegen.ast import AssignmentBase + assert _test_args(AssignmentBase(x, 1)) + +@SKIP("abstract Class") +def test_sympy__codegen__ast__AugmentedAssignment(): + from sympy.codegen.ast import AugmentedAssignment + assert _test_args(AugmentedAssignment(x, 1)) + +def test_sympy__codegen__ast__AddAugmentedAssignment(): + from sympy.codegen.ast import AddAugmentedAssignment + assert _test_args(AddAugmentedAssignment(x, 1)) + +def test_sympy__codegen__ast__SubAugmentedAssignment(): + from sympy.codegen.ast import SubAugmentedAssignment + assert _test_args(SubAugmentedAssignment(x, 1)) + +def test_sympy__codegen__ast__MulAugmentedAssignment(): + from sympy.codegen.ast import MulAugmentedAssignment + assert _test_args(MulAugmentedAssignment(x, 1)) + +def test_sympy__codegen__ast__DivAugmentedAssignment(): + from sympy.codegen.ast import DivAugmentedAssignment + assert _test_args(DivAugmentedAssignment(x, 1)) + +def test_sympy__codegen__ast__ModAugmentedAssignment(): + from sympy.codegen.ast import ModAugmentedAssignment + assert _test_args(ModAugmentedAssignment(x, 1)) + +def test_sympy__codegen__ast__CodeBlock(): + from sympy.codegen.ast import CodeBlock, Assignment + assert _test_args(CodeBlock(Assignment(x, 1), Assignment(y, 2))) + +def test_sympy__codegen__ast__For(): + from sympy.codegen.ast import For, CodeBlock, AddAugmentedAssignment + from sympy.sets import Range + assert _test_args(For(x, Range(10), CodeBlock(AddAugmentedAssignment(y, 1)))) + + +def test_sympy__codegen__ast__Token(): + from sympy.codegen.ast import Token + assert _test_args(Token()) + + +def test_sympy__codegen__ast__ContinueToken(): + from sympy.codegen.ast import ContinueToken + assert _test_args(ContinueToken()) + +def test_sympy__codegen__ast__BreakToken(): + from sympy.codegen.ast import BreakToken + assert _test_args(BreakToken()) + +def test_sympy__codegen__ast__NoneToken(): + from sympy.codegen.ast import NoneToken + assert _test_args(NoneToken()) + +def test_sympy__codegen__ast__String(): + from sympy.codegen.ast import String + assert _test_args(String('foobar')) + +def test_sympy__codegen__ast__QuotedString(): + from sympy.codegen.ast import QuotedString + assert _test_args(QuotedString('foobar')) + +def test_sympy__codegen__ast__Comment(): + from sympy.codegen.ast import Comment + assert _test_args(Comment('this is a comment')) + +def test_sympy__codegen__ast__Node(): + from sympy.codegen.ast import Node + assert _test_args(Node()) + assert _test_args(Node(attrs={1, 2, 3})) + + +def test_sympy__codegen__ast__Type(): + from sympy.codegen.ast import Type + assert _test_args(Type('float128')) + + +def test_sympy__codegen__ast__IntBaseType(): + from sympy.codegen.ast import IntBaseType + assert _test_args(IntBaseType('bigint')) + + +def test_sympy__codegen__ast___SizedIntType(): + from sympy.codegen.ast import _SizedIntType + assert _test_args(_SizedIntType('int128', 128)) + + +def test_sympy__codegen__ast__SignedIntType(): + from sympy.codegen.ast import SignedIntType + assert _test_args(SignedIntType('int128_with_sign', 128)) + + +def test_sympy__codegen__ast__UnsignedIntType(): + from sympy.codegen.ast import UnsignedIntType + assert _test_args(UnsignedIntType('unt128', 128)) + + +def test_sympy__codegen__ast__FloatBaseType(): + from sympy.codegen.ast import FloatBaseType + assert _test_args(FloatBaseType('positive_real')) + + +def test_sympy__codegen__ast__FloatType(): + from sympy.codegen.ast import FloatType + assert _test_args(FloatType('float242', 242, nmant=142, nexp=99)) + + +def test_sympy__codegen__ast__ComplexBaseType(): + from sympy.codegen.ast import ComplexBaseType + assert _test_args(ComplexBaseType('positive_cmplx')) + +def test_sympy__codegen__ast__ComplexType(): + from sympy.codegen.ast import ComplexType + assert _test_args(ComplexType('complex42', 42, nmant=15, nexp=5)) + + +def test_sympy__codegen__ast__Attribute(): + from sympy.codegen.ast import Attribute + assert _test_args(Attribute('noexcept')) + + +def test_sympy__codegen__ast__Variable(): + from sympy.codegen.ast import Variable, Type, value_const + assert _test_args(Variable(x)) + assert _test_args(Variable(y, Type('float32'), {value_const})) + assert _test_args(Variable(z, type=Type('float64'))) + + +def test_sympy__codegen__ast__Pointer(): + from sympy.codegen.ast import Pointer, Type, pointer_const + assert _test_args(Pointer(x)) + assert _test_args(Pointer(y, type=Type('float32'))) + assert _test_args(Pointer(z, Type('float64'), {pointer_const})) + + +def test_sympy__codegen__ast__Declaration(): + from sympy.codegen.ast import Declaration, Variable, Type + vx = Variable(x, type=Type('float')) + assert _test_args(Declaration(vx)) + + +def test_sympy__codegen__ast__While(): + from sympy.codegen.ast import While, AddAugmentedAssignment + assert _test_args(While(abs(x) < 1, [AddAugmentedAssignment(x, -1)])) + + +def test_sympy__codegen__ast__Scope(): + from sympy.codegen.ast import Scope, AddAugmentedAssignment + assert _test_args(Scope([AddAugmentedAssignment(x, -1)])) + + +def test_sympy__codegen__ast__Stream(): + from sympy.codegen.ast import Stream + assert _test_args(Stream('stdin')) + +def test_sympy__codegen__ast__Print(): + from sympy.codegen.ast import Print + assert _test_args(Print([x, y])) + assert _test_args(Print([x, y], "%d %d")) + + +def test_sympy__codegen__ast__FunctionPrototype(): + from sympy.codegen.ast import FunctionPrototype, real, Declaration, Variable + inp_x = Declaration(Variable(x, type=real)) + assert _test_args(FunctionPrototype(real, 'pwer', [inp_x])) + + +def test_sympy__codegen__ast__FunctionDefinition(): + from sympy.codegen.ast import FunctionDefinition, real, Declaration, Variable, Assignment + inp_x = Declaration(Variable(x, type=real)) + assert _test_args(FunctionDefinition(real, 'pwer', [inp_x], [Assignment(x, x**2)])) + + +def test_sympy__codegen__ast__Raise(): + from sympy.codegen.ast import Raise + assert _test_args(Raise(x)) + + +def test_sympy__codegen__ast__Return(): + from sympy.codegen.ast import Return + assert _test_args(Return(x)) + + +def test_sympy__codegen__ast__RuntimeError_(): + from sympy.codegen.ast import RuntimeError_ + assert _test_args(RuntimeError_('"message"')) + + +def test_sympy__codegen__ast__FunctionCall(): + from sympy.codegen.ast import FunctionCall + assert _test_args(FunctionCall('pwer', [x])) + + +def test_sympy__codegen__ast__Element(): + from sympy.codegen.ast import Element + assert _test_args(Element('x', range(3))) + + +def test_sympy__codegen__cnodes__CommaOperator(): + from sympy.codegen.cnodes import CommaOperator + assert _test_args(CommaOperator(1, 2)) + + +def test_sympy__codegen__cnodes__goto(): + from sympy.codegen.cnodes import goto + assert _test_args(goto('early_exit')) + + +def test_sympy__codegen__cnodes__Label(): + from sympy.codegen.cnodes import Label + assert _test_args(Label('early_exit')) + + +def test_sympy__codegen__cnodes__PreDecrement(): + from sympy.codegen.cnodes import PreDecrement + assert _test_args(PreDecrement(x)) + + +def test_sympy__codegen__cnodes__PostDecrement(): + from sympy.codegen.cnodes import PostDecrement + assert _test_args(PostDecrement(x)) + + +def test_sympy__codegen__cnodes__PreIncrement(): + from sympy.codegen.cnodes import PreIncrement + assert _test_args(PreIncrement(x)) + + +def test_sympy__codegen__cnodes__PostIncrement(): + from sympy.codegen.cnodes import PostIncrement + assert _test_args(PostIncrement(x)) + + +def test_sympy__codegen__cnodes__struct(): + from sympy.codegen.ast import real, Variable + from sympy.codegen.cnodes import struct + assert _test_args(struct(declarations=[ + Variable(x, type=real), + Variable(y, type=real) + ])) + + +def test_sympy__codegen__cnodes__union(): + from sympy.codegen.ast import float32, int32, Variable + from sympy.codegen.cnodes import union + assert _test_args(union(declarations=[ + Variable(x, type=float32), + Variable(y, type=int32) + ])) + + +def test_sympy__codegen__cxxnodes__using(): + from sympy.codegen.cxxnodes import using + assert _test_args(using('std::vector')) + assert _test_args(using('std::vector', 'vec')) + + +def test_sympy__codegen__fnodes__Program(): + from sympy.codegen.fnodes import Program + assert _test_args(Program('foobar', [])) + +def test_sympy__codegen__fnodes__Module(): + from sympy.codegen.fnodes import Module + assert _test_args(Module('foobar', [], [])) + + +def test_sympy__codegen__fnodes__Subroutine(): + from sympy.codegen.fnodes import Subroutine + x = symbols('x', real=True) + assert _test_args(Subroutine('foo', [x], [])) + + +def test_sympy__codegen__fnodes__GoTo(): + from sympy.codegen.fnodes import GoTo + assert _test_args(GoTo([10])) + assert _test_args(GoTo([10, 20], x > 1)) + + +def test_sympy__codegen__fnodes__FortranReturn(): + from sympy.codegen.fnodes import FortranReturn + assert _test_args(FortranReturn(10)) + + +def test_sympy__codegen__fnodes__Extent(): + from sympy.codegen.fnodes import Extent + assert _test_args(Extent()) + assert _test_args(Extent(None)) + assert _test_args(Extent(':')) + assert _test_args(Extent(-3, 4)) + assert _test_args(Extent(x, y)) + + +def test_sympy__codegen__fnodes__use_rename(): + from sympy.codegen.fnodes import use_rename + assert _test_args(use_rename('loc', 'glob')) + + +def test_sympy__codegen__fnodes__use(): + from sympy.codegen.fnodes import use + assert _test_args(use('modfoo', only='bar')) + + +def test_sympy__codegen__fnodes__SubroutineCall(): + from sympy.codegen.fnodes import SubroutineCall + assert _test_args(SubroutineCall('foo', ['bar', 'baz'])) + + +def test_sympy__codegen__fnodes__Do(): + from sympy.codegen.fnodes import Do + assert _test_args(Do([], 'i', 1, 42)) + + +def test_sympy__codegen__fnodes__ImpliedDoLoop(): + from sympy.codegen.fnodes import ImpliedDoLoop + assert _test_args(ImpliedDoLoop('i', 'i', 1, 42)) + + +def test_sympy__codegen__fnodes__ArrayConstructor(): + from sympy.codegen.fnodes import ArrayConstructor + assert _test_args(ArrayConstructor([1, 2, 3])) + from sympy.codegen.fnodes import ImpliedDoLoop + idl = ImpliedDoLoop('i', 'i', 1, 42) + assert _test_args(ArrayConstructor([1, idl, 3])) + + +def test_sympy__codegen__fnodes__sum_(): + from sympy.codegen.fnodes import sum_ + assert _test_args(sum_('arr')) + + +def test_sympy__codegen__fnodes__product_(): + from sympy.codegen.fnodes import product_ + assert _test_args(product_('arr')) + + +def test_sympy__codegen__numpy_nodes__logaddexp(): + from sympy.codegen.numpy_nodes import logaddexp + assert _test_args(logaddexp(x, y)) + + +def test_sympy__codegen__numpy_nodes__logaddexp2(): + from sympy.codegen.numpy_nodes import logaddexp2 + assert _test_args(logaddexp2(x, y)) + + +def test_sympy__codegen__pynodes__List(): + from sympy.codegen.pynodes import List + assert _test_args(List(1, 2, 3)) + + +def test_sympy__codegen__pynodes__NumExprEvaluate(): + from sympy.codegen.pynodes import NumExprEvaluate + assert _test_args(NumExprEvaluate(x)) + + +def test_sympy__codegen__scipy_nodes__cosm1(): + from sympy.codegen.scipy_nodes import cosm1 + assert _test_args(cosm1(x)) + + +def test_sympy__codegen__scipy_nodes__powm1(): + from sympy.codegen.scipy_nodes import powm1 + assert _test_args(powm1(x, y)) + + +def test_sympy__codegen__abstract_nodes__List(): + from sympy.codegen.abstract_nodes import List + assert _test_args(List(1, 2, 3)) + +def test_sympy__combinatorics__graycode__GrayCode(): + from sympy.combinatorics.graycode import GrayCode + # an integer is given and returned from GrayCode as the arg + assert _test_args(GrayCode(3, start='100')) + assert _test_args(GrayCode(3, rank=1)) + + +def test_sympy__combinatorics__permutations__Permutation(): + from sympy.combinatorics.permutations import Permutation + assert _test_args(Permutation([0, 1, 2, 3])) + +def test_sympy__combinatorics__permutations__AppliedPermutation(): + from sympy.combinatorics.permutations import Permutation + from sympy.combinatorics.permutations import AppliedPermutation + p = Permutation([0, 1, 2, 3]) + assert _test_args(AppliedPermutation(p, x)) + +def test_sympy__combinatorics__perm_groups__PermutationGroup(): + from sympy.combinatorics.permutations import Permutation + from sympy.combinatorics.perm_groups import PermutationGroup + assert _test_args(PermutationGroup([Permutation([0, 1])])) + + +def test_sympy__combinatorics__polyhedron__Polyhedron(): + from sympy.combinatorics.permutations import Permutation + from sympy.combinatorics.polyhedron import Polyhedron + from sympy.abc import w, x, y, z + pgroup = [Permutation([[0, 1, 2], [3]]), + Permutation([[0, 1, 3], [2]]), + Permutation([[0, 2, 3], [1]]), + Permutation([[1, 2, 3], [0]]), + Permutation([[0, 1], [2, 3]]), + Permutation([[0, 2], [1, 3]]), + Permutation([[0, 3], [1, 2]]), + Permutation([[0, 1, 2, 3]])] + corners = [w, x, y, z] + faces = [(w, x, y), (w, y, z), (w, z, x), (x, y, z)] + assert _test_args(Polyhedron(corners, faces, pgroup)) + + +def test_sympy__combinatorics__prufer__Prufer(): + from sympy.combinatorics.prufer import Prufer + assert _test_args(Prufer([[0, 1], [0, 2], [0, 3]], 4)) + + +def test_sympy__combinatorics__partitions__Partition(): + from sympy.combinatorics.partitions import Partition + assert _test_args(Partition([1])) + + +def test_sympy__combinatorics__partitions__IntegerPartition(): + from sympy.combinatorics.partitions import IntegerPartition + assert _test_args(IntegerPartition([1])) + + +def test_sympy__concrete__products__Product(): + from sympy.concrete.products import Product + assert _test_args(Product(x, (x, 0, 10))) + assert _test_args(Product(x, (x, 0, y), (y, 0, 10))) + + +@SKIP("abstract Class") +def test_sympy__concrete__expr_with_limits__ExprWithLimits(): + from sympy.concrete.expr_with_limits import ExprWithLimits + assert _test_args(ExprWithLimits(x, (x, 0, 10))) + assert _test_args(ExprWithLimits(x*y, (x, 0, 10.),(y,1.,3))) + + +@SKIP("abstract Class") +def test_sympy__concrete__expr_with_limits__AddWithLimits(): + from sympy.concrete.expr_with_limits import AddWithLimits + assert _test_args(AddWithLimits(x, (x, 0, 10))) + assert _test_args(AddWithLimits(x*y, (x, 0, 10),(y,1,3))) + + +@SKIP("abstract Class") +def test_sympy__concrete__expr_with_intlimits__ExprWithIntLimits(): + from sympy.concrete.expr_with_intlimits import ExprWithIntLimits + assert _test_args(ExprWithIntLimits(x, (x, 0, 10))) + assert _test_args(ExprWithIntLimits(x*y, (x, 0, 10),(y,1,3))) + + +def test_sympy__concrete__summations__Sum(): + from sympy.concrete.summations import Sum + assert _test_args(Sum(x, (x, 0, 10))) + assert _test_args(Sum(x, (x, 0, y), (y, 0, 10))) + + +def test_sympy__core__add__Add(): + from sympy.core.add import Add + assert _test_args(Add(x, y, z, 2)) + + +def test_sympy__core__basic__Atom(): + from sympy.core.basic import Atom + assert _test_args(Atom()) + + +def test_sympy__core__basic__Basic(): + from sympy.core.basic import Basic + assert _test_args(Basic()) + + +def test_sympy__core__containers__Dict(): + from sympy.core.containers import Dict + assert _test_args(Dict({x: y, y: z})) + + +def test_sympy__core__containers__Tuple(): + from sympy.core.containers import Tuple + assert _test_args(Tuple(x, y, z, 2)) + + +def test_sympy__core__expr__AtomicExpr(): + from sympy.core.expr import AtomicExpr + assert _test_args(AtomicExpr()) + + +def test_sympy__core__expr__Expr(): + from sympy.core.expr import Expr + assert _test_args(Expr()) + + +def test_sympy__core__expr__UnevaluatedExpr(): + from sympy.core.expr import UnevaluatedExpr + from sympy.abc import x + assert _test_args(UnevaluatedExpr(x)) + + +def test_sympy__core__function__Application(): + from sympy.core.function import Application + assert _test_args(Application(1, 2, 3)) + + +def test_sympy__core__function__AppliedUndef(): + from sympy.core.function import AppliedUndef + assert _test_args(AppliedUndef(1, 2, 3)) + + +def test_sympy__core__function__Derivative(): + from sympy.core.function import Derivative + assert _test_args(Derivative(2, x, y, 3)) + + +@SKIP("abstract class") +def test_sympy__core__function__Function(): + pass + + +def test_sympy__core__function__Lambda(): + assert _test_args(Lambda((x, y), x + y + z)) + + +def test_sympy__core__function__Subs(): + from sympy.core.function import Subs + assert _test_args(Subs(x + y, x, 2)) + + +def test_sympy__core__function__WildFunction(): + from sympy.core.function import WildFunction + assert _test_args(WildFunction('f')) + + +def test_sympy__core__mod__Mod(): + from sympy.core.mod import Mod + assert _test_args(Mod(x, 2)) + + +def test_sympy__core__mul__Mul(): + from sympy.core.mul import Mul + assert _test_args(Mul(2, x, y, z)) + + +def test_sympy__core__numbers__Catalan(): + from sympy.core.numbers import Catalan + assert _test_args(Catalan()) + + +def test_sympy__core__numbers__ComplexInfinity(): + from sympy.core.numbers import ComplexInfinity + assert _test_args(ComplexInfinity()) + + +def test_sympy__core__numbers__EulerGamma(): + from sympy.core.numbers import EulerGamma + assert _test_args(EulerGamma()) + + +def test_sympy__core__numbers__Exp1(): + from sympy.core.numbers import Exp1 + assert _test_args(Exp1()) + + +def test_sympy__core__numbers__Float(): + from sympy.core.numbers import Float + assert _test_args(Float(1.23)) + + +def test_sympy__core__numbers__GoldenRatio(): + from sympy.core.numbers import GoldenRatio + assert _test_args(GoldenRatio()) + + +def test_sympy__core__numbers__TribonacciConstant(): + from sympy.core.numbers import TribonacciConstant + assert _test_args(TribonacciConstant()) + + +def test_sympy__core__numbers__Half(): + from sympy.core.numbers import Half + assert _test_args(Half()) + + +def test_sympy__core__numbers__ImaginaryUnit(): + from sympy.core.numbers import ImaginaryUnit + assert _test_args(ImaginaryUnit()) + + +def test_sympy__core__numbers__Infinity(): + from sympy.core.numbers import Infinity + assert _test_args(Infinity()) + + +def test_sympy__core__numbers__Integer(): + from sympy.core.numbers import Integer + assert _test_args(Integer(7)) + + +@SKIP("abstract class") +def test_sympy__core__numbers__IntegerConstant(): + pass + + +def test_sympy__core__numbers__NaN(): + from sympy.core.numbers import NaN + assert _test_args(NaN()) + + +def test_sympy__core__numbers__NegativeInfinity(): + from sympy.core.numbers import NegativeInfinity + assert _test_args(NegativeInfinity()) + + +def test_sympy__core__numbers__NegativeOne(): + from sympy.core.numbers import NegativeOne + assert _test_args(NegativeOne()) + + +def test_sympy__core__numbers__Number(): + from sympy.core.numbers import Number + assert _test_args(Number(1, 7)) + + +def test_sympy__core__numbers__NumberSymbol(): + from sympy.core.numbers import NumberSymbol + assert _test_args(NumberSymbol()) + + +def test_sympy__core__numbers__One(): + from sympy.core.numbers import One + assert _test_args(One()) + + +def test_sympy__core__numbers__Pi(): + from sympy.core.numbers import Pi + assert _test_args(Pi()) + + +def test_sympy__core__numbers__Rational(): + from sympy.core.numbers import Rational + assert _test_args(Rational(1, 7)) + + +@SKIP("abstract class") +def test_sympy__core__numbers__RationalConstant(): + pass + + +def test_sympy__core__numbers__Zero(): + from sympy.core.numbers import Zero + assert _test_args(Zero()) + + +@SKIP("abstract class") +def test_sympy__core__operations__AssocOp(): + pass + + +@SKIP("abstract class") +def test_sympy__core__operations__LatticeOp(): + pass + + +def test_sympy__core__power__Pow(): + from sympy.core.power import Pow + assert _test_args(Pow(x, 2)) + + +def test_sympy__core__relational__Equality(): + from sympy.core.relational import Equality + assert _test_args(Equality(x, 2)) + + +def test_sympy__core__relational__GreaterThan(): + from sympy.core.relational import GreaterThan + assert _test_args(GreaterThan(x, 2)) + + +def test_sympy__core__relational__LessThan(): + from sympy.core.relational import LessThan + assert _test_args(LessThan(x, 2)) + + +@SKIP("abstract class") +def test_sympy__core__relational__Relational(): + pass + + +def test_sympy__core__relational__StrictGreaterThan(): + from sympy.core.relational import StrictGreaterThan + assert _test_args(StrictGreaterThan(x, 2)) + + +def test_sympy__core__relational__StrictLessThan(): + from sympy.core.relational import StrictLessThan + assert _test_args(StrictLessThan(x, 2)) + + +def test_sympy__core__relational__Unequality(): + from sympy.core.relational import Unequality + assert _test_args(Unequality(x, 2)) + + +def test_sympy__sandbox__indexed_integrals__IndexedIntegral(): + from sympy.tensor import IndexedBase, Idx + from sympy.sandbox.indexed_integrals import IndexedIntegral + A = IndexedBase('A') + i, j = symbols('i j', integer=True) + a1, a2 = symbols('a1:3', cls=Idx) + assert _test_args(IndexedIntegral(A[a1], A[a2])) + assert _test_args(IndexedIntegral(A[i], A[j])) + + +def test_sympy__calculus__accumulationbounds__AccumulationBounds(): + from sympy.calculus.accumulationbounds import AccumulationBounds + assert _test_args(AccumulationBounds(0, 1)) + + +def test_sympy__sets__ordinals__OmegaPower(): + from sympy.sets.ordinals import OmegaPower + assert _test_args(OmegaPower(1, 1)) + +def test_sympy__sets__ordinals__Ordinal(): + from sympy.sets.ordinals import Ordinal, OmegaPower + assert _test_args(Ordinal(OmegaPower(2, 1))) + +def test_sympy__sets__ordinals__OrdinalOmega(): + from sympy.sets.ordinals import OrdinalOmega + assert _test_args(OrdinalOmega()) + +def test_sympy__sets__ordinals__OrdinalZero(): + from sympy.sets.ordinals import OrdinalZero + assert _test_args(OrdinalZero()) + + +def test_sympy__sets__powerset__PowerSet(): + from sympy.sets.powerset import PowerSet + from sympy.core.singleton import S + assert _test_args(PowerSet(S.EmptySet)) + + +def test_sympy__sets__sets__EmptySet(): + from sympy.sets.sets import EmptySet + assert _test_args(EmptySet()) + + +def test_sympy__sets__sets__UniversalSet(): + from sympy.sets.sets import UniversalSet + assert _test_args(UniversalSet()) + + +def test_sympy__sets__sets__FiniteSet(): + from sympy.sets.sets import FiniteSet + assert _test_args(FiniteSet(x, y, z)) + + +def test_sympy__sets__sets__Interval(): + from sympy.sets.sets import Interval + assert _test_args(Interval(0, 1)) + + +def test_sympy__sets__sets__ProductSet(): + from sympy.sets.sets import ProductSet, Interval + assert _test_args(ProductSet(Interval(0, 1), Interval(0, 1))) + + +@SKIP("does it make sense to test this?") +def test_sympy__sets__sets__Set(): + from sympy.sets.sets import Set + assert _test_args(Set()) + + +def test_sympy__sets__sets__Intersection(): + from sympy.sets.sets import Intersection, Interval + from sympy.core.symbol import Symbol + x = Symbol('x') + y = Symbol('y') + S = Intersection(Interval(0, x), Interval(y, 1)) + assert isinstance(S, Intersection) + assert _test_args(S) + + +def test_sympy__sets__sets__Union(): + from sympy.sets.sets import Union, Interval + assert _test_args(Union(Interval(0, 1), Interval(2, 3))) + + +def test_sympy__sets__sets__Complement(): + from sympy.sets.sets import Complement, Interval + assert _test_args(Complement(Interval(0, 2), Interval(0, 1))) + + +def test_sympy__sets__sets__SymmetricDifference(): + from sympy.sets.sets import FiniteSet, SymmetricDifference + assert _test_args(SymmetricDifference(FiniteSet(1, 2, 3), \ + FiniteSet(2, 3, 4))) + +def test_sympy__sets__sets__DisjointUnion(): + from sympy.sets.sets import FiniteSet, DisjointUnion + assert _test_args(DisjointUnion(FiniteSet(1, 2, 3), \ + FiniteSet(2, 3, 4))) + + +def test_sympy__physics__quantum__trace__Tr(): + from sympy.physics.quantum.trace import Tr + a, b = symbols('a b', commutative=False) + assert _test_args(Tr(a + b)) + + +def test_sympy__sets__setexpr__SetExpr(): + from sympy.sets.setexpr import SetExpr + from sympy.sets.sets import Interval + assert _test_args(SetExpr(Interval(0, 1))) + + +def test_sympy__sets__fancysets__Rationals(): + from sympy.sets.fancysets import Rationals + assert _test_args(Rationals()) + + +def test_sympy__sets__fancysets__Naturals(): + from sympy.sets.fancysets import Naturals + assert _test_args(Naturals()) + + +def test_sympy__sets__fancysets__Naturals0(): + from sympy.sets.fancysets import Naturals0 + assert _test_args(Naturals0()) + + +def test_sympy__sets__fancysets__Integers(): + from sympy.sets.fancysets import Integers + assert _test_args(Integers()) + + +def test_sympy__sets__fancysets__Reals(): + from sympy.sets.fancysets import Reals + assert _test_args(Reals()) + + +def test_sympy__sets__fancysets__Complexes(): + from sympy.sets.fancysets import Complexes + assert _test_args(Complexes()) + + +def test_sympy__sets__fancysets__ComplexRegion(): + from sympy.sets.fancysets import ComplexRegion + from sympy.core.singleton import S + from sympy.sets import Interval + a = Interval(0, 1) + b = Interval(2, 3) + theta = Interval(0, 2*S.Pi) + assert _test_args(ComplexRegion(a*b)) + assert _test_args(ComplexRegion(a*theta, polar=True)) + + +def test_sympy__sets__fancysets__CartesianComplexRegion(): + from sympy.sets.fancysets import CartesianComplexRegion + from sympy.sets import Interval + a = Interval(0, 1) + b = Interval(2, 3) + assert _test_args(CartesianComplexRegion(a*b)) + + +def test_sympy__sets__fancysets__PolarComplexRegion(): + from sympy.sets.fancysets import PolarComplexRegion + from sympy.core.singleton import S + from sympy.sets import Interval + a = Interval(0, 1) + theta = Interval(0, 2*S.Pi) + assert _test_args(PolarComplexRegion(a*theta)) + + +def test_sympy__sets__fancysets__ImageSet(): + from sympy.sets.fancysets import ImageSet + from sympy.core.singleton import S + from sympy.core.symbol import Symbol + x = Symbol('x') + assert _test_args(ImageSet(Lambda(x, x**2), S.Naturals)) + + +def test_sympy__sets__fancysets__Range(): + from sympy.sets.fancysets import Range + assert _test_args(Range(1, 5, 1)) + + +def test_sympy__sets__conditionset__ConditionSet(): + from sympy.sets.conditionset import ConditionSet + from sympy.core.singleton import S + from sympy.core.symbol import Symbol + x = Symbol('x') + assert _test_args(ConditionSet(x, Eq(x**2, 1), S.Reals)) + + +def test_sympy__sets__contains__Contains(): + from sympy.sets.fancysets import Range + from sympy.sets.contains import Contains + assert _test_args(Contains(x, Range(0, 10, 2))) + + +# STATS + + +from sympy.stats.crv_types import NormalDistribution +nd = NormalDistribution(0, 1) +from sympy.stats.frv_types import DieDistribution +die = DieDistribution(6) + + +def test_sympy__stats__crv__ContinuousDomain(): + from sympy.sets.sets import Interval + from sympy.stats.crv import ContinuousDomain + assert _test_args(ContinuousDomain({x}, Interval(-oo, oo))) + + +def test_sympy__stats__crv__SingleContinuousDomain(): + from sympy.sets.sets import Interval + from sympy.stats.crv import SingleContinuousDomain + assert _test_args(SingleContinuousDomain(x, Interval(-oo, oo))) + + +def test_sympy__stats__crv__ProductContinuousDomain(): + from sympy.sets.sets import Interval + from sympy.stats.crv import SingleContinuousDomain, ProductContinuousDomain + D = SingleContinuousDomain(x, Interval(-oo, oo)) + E = SingleContinuousDomain(y, Interval(0, oo)) + assert _test_args(ProductContinuousDomain(D, E)) + + +def test_sympy__stats__crv__ConditionalContinuousDomain(): + from sympy.sets.sets import Interval + from sympy.stats.crv import (SingleContinuousDomain, + ConditionalContinuousDomain) + D = SingleContinuousDomain(x, Interval(-oo, oo)) + assert _test_args(ConditionalContinuousDomain(D, x > 0)) + + +def test_sympy__stats__crv__ContinuousPSpace(): + from sympy.sets.sets import Interval + from sympy.stats.crv import ContinuousPSpace, SingleContinuousDomain + D = SingleContinuousDomain(x, Interval(-oo, oo)) + assert _test_args(ContinuousPSpace(D, nd)) + + +def test_sympy__stats__crv__SingleContinuousPSpace(): + from sympy.stats.crv import SingleContinuousPSpace + assert _test_args(SingleContinuousPSpace(x, nd)) + +@SKIP("abstract class") +def test_sympy__stats__rv__Distribution(): + pass + +@SKIP("abstract class") +def test_sympy__stats__crv__SingleContinuousDistribution(): + pass + + +def test_sympy__stats__drv__SingleDiscreteDomain(): + from sympy.stats.drv import SingleDiscreteDomain + assert _test_args(SingleDiscreteDomain(x, S.Naturals)) + + +def test_sympy__stats__drv__ProductDiscreteDomain(): + from sympy.stats.drv import SingleDiscreteDomain, ProductDiscreteDomain + X = SingleDiscreteDomain(x, S.Naturals) + Y = SingleDiscreteDomain(y, S.Integers) + assert _test_args(ProductDiscreteDomain(X, Y)) + + +def test_sympy__stats__drv__SingleDiscretePSpace(): + from sympy.stats.drv import SingleDiscretePSpace + from sympy.stats.drv_types import PoissonDistribution + assert _test_args(SingleDiscretePSpace(x, PoissonDistribution(1))) + +def test_sympy__stats__drv__DiscretePSpace(): + from sympy.stats.drv import DiscretePSpace, SingleDiscreteDomain + density = Lambda(x, 2**(-x)) + domain = SingleDiscreteDomain(x, S.Naturals) + assert _test_args(DiscretePSpace(domain, density)) + +def test_sympy__stats__drv__ConditionalDiscreteDomain(): + from sympy.stats.drv import ConditionalDiscreteDomain, SingleDiscreteDomain + X = SingleDiscreteDomain(x, S.Naturals0) + assert _test_args(ConditionalDiscreteDomain(X, x > 2)) + +def test_sympy__stats__joint_rv__JointPSpace(): + from sympy.stats.joint_rv import JointPSpace, JointDistribution + assert _test_args(JointPSpace('X', JointDistribution(1))) + +def test_sympy__stats__joint_rv__JointRandomSymbol(): + from sympy.stats.joint_rv import JointRandomSymbol + assert _test_args(JointRandomSymbol(x)) + +def test_sympy__stats__joint_rv_types__JointDistributionHandmade(): + from sympy.tensor.indexed import Indexed + from sympy.stats.joint_rv_types import JointDistributionHandmade + x1, x2 = (Indexed('x', i) for i in (1, 2)) + assert _test_args(JointDistributionHandmade(x1 + x2, S.Reals**2)) + + +def test_sympy__stats__joint_rv__MarginalDistribution(): + from sympy.stats.rv import RandomSymbol + from sympy.stats.joint_rv import MarginalDistribution + r = RandomSymbol(S('r')) + assert _test_args(MarginalDistribution(r, (r,))) + + +def test_sympy__stats__compound_rv__CompoundDistribution(): + from sympy.stats.compound_rv import CompoundDistribution + from sympy.stats.drv_types import PoissonDistribution, Poisson + r = Poisson('r', 10) + assert _test_args(CompoundDistribution(PoissonDistribution(r))) + + +def test_sympy__stats__compound_rv__CompoundPSpace(): + from sympy.stats.compound_rv import CompoundPSpace, CompoundDistribution + from sympy.stats.drv_types import PoissonDistribution, Poisson + r = Poisson('r', 5) + C = CompoundDistribution(PoissonDistribution(r)) + assert _test_args(CompoundPSpace('C', C)) + + +@SKIP("abstract class") +def test_sympy__stats__drv__SingleDiscreteDistribution(): + pass + +@SKIP("abstract class") +def test_sympy__stats__drv__DiscreteDistribution(): + pass + +@SKIP("abstract class") +def test_sympy__stats__drv__DiscreteDomain(): + pass + + +def test_sympy__stats__rv__RandomDomain(): + from sympy.stats.rv import RandomDomain + from sympy.sets.sets import FiniteSet + assert _test_args(RandomDomain(FiniteSet(x), FiniteSet(1, 2, 3))) + + +def test_sympy__stats__rv__SingleDomain(): + from sympy.stats.rv import SingleDomain + from sympy.sets.sets import FiniteSet + assert _test_args(SingleDomain(x, FiniteSet(1, 2, 3))) + + +def test_sympy__stats__rv__ConditionalDomain(): + from sympy.stats.rv import ConditionalDomain, RandomDomain + from sympy.sets.sets import FiniteSet + D = RandomDomain(FiniteSet(x), FiniteSet(1, 2)) + assert _test_args(ConditionalDomain(D, x > 1)) + +def test_sympy__stats__rv__MatrixDomain(): + from sympy.stats.rv import MatrixDomain + from sympy.matrices import MatrixSet + from sympy.core.singleton import S + assert _test_args(MatrixDomain(x, MatrixSet(2, 2, S.Reals))) + +def test_sympy__stats__rv__PSpace(): + from sympy.stats.rv import PSpace, RandomDomain + from sympy.sets.sets import FiniteSet + D = RandomDomain(FiniteSet(x), FiniteSet(1, 2, 3, 4, 5, 6)) + assert _test_args(PSpace(D, die)) + + +@SKIP("abstract Class") +def test_sympy__stats__rv__SinglePSpace(): + pass + + +def test_sympy__stats__rv__RandomSymbol(): + from sympy.stats.rv import RandomSymbol + from sympy.stats.crv import SingleContinuousPSpace + A = SingleContinuousPSpace(x, nd) + assert _test_args(RandomSymbol(x, A)) + + +@SKIP("abstract Class") +def test_sympy__stats__rv__ProductPSpace(): + pass + + +def test_sympy__stats__rv__IndependentProductPSpace(): + from sympy.stats.rv import IndependentProductPSpace + from sympy.stats.crv import SingleContinuousPSpace + A = SingleContinuousPSpace(x, nd) + B = SingleContinuousPSpace(y, nd) + assert _test_args(IndependentProductPSpace(A, B)) + + +def test_sympy__stats__rv__ProductDomain(): + from sympy.sets.sets import Interval + from sympy.stats.rv import ProductDomain, SingleDomain + D = SingleDomain(x, Interval(-oo, oo)) + E = SingleDomain(y, Interval(0, oo)) + assert _test_args(ProductDomain(D, E)) + + +def test_sympy__stats__symbolic_probability__Probability(): + from sympy.stats.symbolic_probability import Probability + from sympy.stats import Normal + X = Normal('X', 0, 1) + assert _test_args(Probability(X > 0)) + + +def test_sympy__stats__symbolic_probability__Expectation(): + from sympy.stats.symbolic_probability import Expectation + from sympy.stats import Normal + X = Normal('X', 0, 1) + assert _test_args(Expectation(X > 0)) + + +def test_sympy__stats__symbolic_probability__Covariance(): + from sympy.stats.symbolic_probability import Covariance + from sympy.stats import Normal + X = Normal('X', 0, 1) + Y = Normal('Y', 0, 3) + assert _test_args(Covariance(X, Y)) + + +def test_sympy__stats__symbolic_probability__Variance(): + from sympy.stats.symbolic_probability import Variance + from sympy.stats import Normal + X = Normal('X', 0, 1) + assert _test_args(Variance(X)) + + +def test_sympy__stats__symbolic_probability__Moment(): + from sympy.stats.symbolic_probability import Moment + from sympy.stats import Normal + X = Normal('X', 0, 1) + assert _test_args(Moment(X, 3, 2, X > 3)) + + +def test_sympy__stats__symbolic_probability__CentralMoment(): + from sympy.stats.symbolic_probability import CentralMoment + from sympy.stats import Normal + X = Normal('X', 0, 1) + assert _test_args(CentralMoment(X, 2, X > 1)) + + +def test_sympy__stats__frv_types__DiscreteUniformDistribution(): + from sympy.stats.frv_types import DiscreteUniformDistribution + from sympy.core.containers import Tuple + assert _test_args(DiscreteUniformDistribution(Tuple(*list(range(6))))) + + +def test_sympy__stats__frv_types__DieDistribution(): + assert _test_args(die) + + +def test_sympy__stats__frv_types__BernoulliDistribution(): + from sympy.stats.frv_types import BernoulliDistribution + assert _test_args(BernoulliDistribution(S.Half, 0, 1)) + + +def test_sympy__stats__frv_types__BinomialDistribution(): + from sympy.stats.frv_types import BinomialDistribution + assert _test_args(BinomialDistribution(5, S.Half, 1, 0)) + +def test_sympy__stats__frv_types__BetaBinomialDistribution(): + from sympy.stats.frv_types import BetaBinomialDistribution + assert _test_args(BetaBinomialDistribution(5, 1, 1)) + + +def test_sympy__stats__frv_types__HypergeometricDistribution(): + from sympy.stats.frv_types import HypergeometricDistribution + assert _test_args(HypergeometricDistribution(10, 5, 3)) + + +def test_sympy__stats__frv_types__RademacherDistribution(): + from sympy.stats.frv_types import RademacherDistribution + assert _test_args(RademacherDistribution()) + +def test_sympy__stats__frv_types__IdealSolitonDistribution(): + from sympy.stats.frv_types import IdealSolitonDistribution + assert _test_args(IdealSolitonDistribution(10)) + +def test_sympy__stats__frv_types__RobustSolitonDistribution(): + from sympy.stats.frv_types import RobustSolitonDistribution + assert _test_args(RobustSolitonDistribution(1000, 0.5, 0.1)) + +def test_sympy__stats__frv__FiniteDomain(): + from sympy.stats.frv import FiniteDomain + assert _test_args(FiniteDomain({(x, 1), (x, 2)})) # x can be 1 or 2 + + +def test_sympy__stats__frv__SingleFiniteDomain(): + from sympy.stats.frv import SingleFiniteDomain + assert _test_args(SingleFiniteDomain(x, {1, 2})) # x can be 1 or 2 + + +def test_sympy__stats__frv__ProductFiniteDomain(): + from sympy.stats.frv import SingleFiniteDomain, ProductFiniteDomain + xd = SingleFiniteDomain(x, {1, 2}) + yd = SingleFiniteDomain(y, {1, 2}) + assert _test_args(ProductFiniteDomain(xd, yd)) + + +def test_sympy__stats__frv__ConditionalFiniteDomain(): + from sympy.stats.frv import SingleFiniteDomain, ConditionalFiniteDomain + xd = SingleFiniteDomain(x, {1, 2}) + assert _test_args(ConditionalFiniteDomain(xd, x > 1)) + + +def test_sympy__stats__frv__FinitePSpace(): + from sympy.stats.frv import FinitePSpace, SingleFiniteDomain + xd = SingleFiniteDomain(x, {1, 2, 3, 4, 5, 6}) + assert _test_args(FinitePSpace(xd, {(x, 1): S.Half, (x, 2): S.Half})) + + xd = SingleFiniteDomain(x, {1, 2}) + assert _test_args(FinitePSpace(xd, {(x, 1): S.Half, (x, 2): S.Half})) + + +def test_sympy__stats__frv__SingleFinitePSpace(): + from sympy.stats.frv import SingleFinitePSpace + from sympy.core.symbol import Symbol + + assert _test_args(SingleFinitePSpace(Symbol('x'), die)) + + +def test_sympy__stats__frv__ProductFinitePSpace(): + from sympy.stats.frv import SingleFinitePSpace, ProductFinitePSpace + from sympy.core.symbol import Symbol + xp = SingleFinitePSpace(Symbol('x'), die) + yp = SingleFinitePSpace(Symbol('y'), die) + assert _test_args(ProductFinitePSpace(xp, yp)) + +@SKIP("abstract class") +def test_sympy__stats__frv__SingleFiniteDistribution(): + pass + +@SKIP("abstract class") +def test_sympy__stats__crv__ContinuousDistribution(): + pass + + +def test_sympy__stats__frv_types__FiniteDistributionHandmade(): + from sympy.stats.frv_types import FiniteDistributionHandmade + from sympy.core.containers import Dict + assert _test_args(FiniteDistributionHandmade(Dict({1: 1}))) + + +def test_sympy__stats__crv_types__ContinuousDistributionHandmade(): + from sympy.stats.crv_types import ContinuousDistributionHandmade + from sympy.core.function import Lambda + from sympy.sets.sets import Interval + from sympy.abc import x + assert _test_args(ContinuousDistributionHandmade(Lambda(x, 2*x), + Interval(0, 1))) + + +def test_sympy__stats__drv_types__DiscreteDistributionHandmade(): + from sympy.stats.drv_types import DiscreteDistributionHandmade + from sympy.core.function import Lambda + from sympy.sets.sets import FiniteSet + from sympy.abc import x + assert _test_args(DiscreteDistributionHandmade(Lambda(x, Rational(1, 10)), + FiniteSet(*range(10)))) + + +def test_sympy__stats__rv__Density(): + from sympy.stats.rv import Density + from sympy.stats.crv_types import Normal + assert _test_args(Density(Normal('x', 0, 1))) + + +def test_sympy__stats__crv_types__ArcsinDistribution(): + from sympy.stats.crv_types import ArcsinDistribution + assert _test_args(ArcsinDistribution(0, 1)) + + +def test_sympy__stats__crv_types__BeniniDistribution(): + from sympy.stats.crv_types import BeniniDistribution + assert _test_args(BeniniDistribution(1, 1, 1)) + + +def test_sympy__stats__crv_types__BetaDistribution(): + from sympy.stats.crv_types import BetaDistribution + assert _test_args(BetaDistribution(1, 1)) + +def test_sympy__stats__crv_types__BetaNoncentralDistribution(): + from sympy.stats.crv_types import BetaNoncentralDistribution + assert _test_args(BetaNoncentralDistribution(1, 1, 1)) + + +def test_sympy__stats__crv_types__BetaPrimeDistribution(): + from sympy.stats.crv_types import BetaPrimeDistribution + assert _test_args(BetaPrimeDistribution(1, 1)) + +def test_sympy__stats__crv_types__BoundedParetoDistribution(): + from sympy.stats.crv_types import BoundedParetoDistribution + assert _test_args(BoundedParetoDistribution(1, 1, 2)) + +def test_sympy__stats__crv_types__CauchyDistribution(): + from sympy.stats.crv_types import CauchyDistribution + assert _test_args(CauchyDistribution(0, 1)) + + +def test_sympy__stats__crv_types__ChiDistribution(): + from sympy.stats.crv_types import ChiDistribution + assert _test_args(ChiDistribution(1)) + + +def test_sympy__stats__crv_types__ChiNoncentralDistribution(): + from sympy.stats.crv_types import ChiNoncentralDistribution + assert _test_args(ChiNoncentralDistribution(1,1)) + + +def test_sympy__stats__crv_types__ChiSquaredDistribution(): + from sympy.stats.crv_types import ChiSquaredDistribution + assert _test_args(ChiSquaredDistribution(1)) + + +def test_sympy__stats__crv_types__DagumDistribution(): + from sympy.stats.crv_types import DagumDistribution + assert _test_args(DagumDistribution(1, 1, 1)) + + +def test_sympy__stats__crv_types__DavisDistribution(): + from sympy.stats.crv_types import DavisDistribution + assert _test_args(DavisDistribution(1, 1, 1)) + + +def test_sympy__stats__crv_types__ExGaussianDistribution(): + from sympy.stats.crv_types import ExGaussianDistribution + assert _test_args(ExGaussianDistribution(1, 1, 1)) + + +def test_sympy__stats__crv_types__ExponentialDistribution(): + from sympy.stats.crv_types import ExponentialDistribution + assert _test_args(ExponentialDistribution(1)) + + +def test_sympy__stats__crv_types__ExponentialPowerDistribution(): + from sympy.stats.crv_types import ExponentialPowerDistribution + assert _test_args(ExponentialPowerDistribution(0, 1, 1)) + + +def test_sympy__stats__crv_types__FDistributionDistribution(): + from sympy.stats.crv_types import FDistributionDistribution + assert _test_args(FDistributionDistribution(1, 1)) + + +def test_sympy__stats__crv_types__FisherZDistribution(): + from sympy.stats.crv_types import FisherZDistribution + assert _test_args(FisherZDistribution(1, 1)) + + +def test_sympy__stats__crv_types__FrechetDistribution(): + from sympy.stats.crv_types import FrechetDistribution + assert _test_args(FrechetDistribution(1, 1, 1)) + + +def test_sympy__stats__crv_types__GammaInverseDistribution(): + from sympy.stats.crv_types import GammaInverseDistribution + assert _test_args(GammaInverseDistribution(1, 1)) + + +def test_sympy__stats__crv_types__GammaDistribution(): + from sympy.stats.crv_types import GammaDistribution + assert _test_args(GammaDistribution(1, 1)) + +def test_sympy__stats__crv_types__GumbelDistribution(): + from sympy.stats.crv_types import GumbelDistribution + assert _test_args(GumbelDistribution(1, 1, False)) + +def test_sympy__stats__crv_types__GompertzDistribution(): + from sympy.stats.crv_types import GompertzDistribution + assert _test_args(GompertzDistribution(1, 1)) + +def test_sympy__stats__crv_types__KumaraswamyDistribution(): + from sympy.stats.crv_types import KumaraswamyDistribution + assert _test_args(KumaraswamyDistribution(1, 1)) + +def test_sympy__stats__crv_types__LaplaceDistribution(): + from sympy.stats.crv_types import LaplaceDistribution + assert _test_args(LaplaceDistribution(0, 1)) + +def test_sympy__stats__crv_types__LevyDistribution(): + from sympy.stats.crv_types import LevyDistribution + assert _test_args(LevyDistribution(0, 1)) + +def test_sympy__stats__crv_types__LogCauchyDistribution(): + from sympy.stats.crv_types import LogCauchyDistribution + assert _test_args(LogCauchyDistribution(0, 1)) + +def test_sympy__stats__crv_types__LogisticDistribution(): + from sympy.stats.crv_types import LogisticDistribution + assert _test_args(LogisticDistribution(0, 1)) + +def test_sympy__stats__crv_types__LogLogisticDistribution(): + from sympy.stats.crv_types import LogLogisticDistribution + assert _test_args(LogLogisticDistribution(1, 1)) + +def test_sympy__stats__crv_types__LogitNormalDistribution(): + from sympy.stats.crv_types import LogitNormalDistribution + assert _test_args(LogitNormalDistribution(0, 1)) + +def test_sympy__stats__crv_types__LogNormalDistribution(): + from sympy.stats.crv_types import LogNormalDistribution + assert _test_args(LogNormalDistribution(0, 1)) + +def test_sympy__stats__crv_types__LomaxDistribution(): + from sympy.stats.crv_types import LomaxDistribution + assert _test_args(LomaxDistribution(1, 2)) + +def test_sympy__stats__crv_types__MaxwellDistribution(): + from sympy.stats.crv_types import MaxwellDistribution + assert _test_args(MaxwellDistribution(1)) + +def test_sympy__stats__crv_types__MoyalDistribution(): + from sympy.stats.crv_types import MoyalDistribution + assert _test_args(MoyalDistribution(1,2)) + +def test_sympy__stats__crv_types__NakagamiDistribution(): + from sympy.stats.crv_types import NakagamiDistribution + assert _test_args(NakagamiDistribution(1, 1)) + + +def test_sympy__stats__crv_types__NormalDistribution(): + from sympy.stats.crv_types import NormalDistribution + assert _test_args(NormalDistribution(0, 1)) + +def test_sympy__stats__crv_types__GaussianInverseDistribution(): + from sympy.stats.crv_types import GaussianInverseDistribution + assert _test_args(GaussianInverseDistribution(1, 1)) + + +def test_sympy__stats__crv_types__ParetoDistribution(): + from sympy.stats.crv_types import ParetoDistribution + assert _test_args(ParetoDistribution(1, 1)) + +def test_sympy__stats__crv_types__PowerFunctionDistribution(): + from sympy.stats.crv_types import PowerFunctionDistribution + assert _test_args(PowerFunctionDistribution(2,0,1)) + +def test_sympy__stats__crv_types__QuadraticUDistribution(): + from sympy.stats.crv_types import QuadraticUDistribution + assert _test_args(QuadraticUDistribution(1, 2)) + +def test_sympy__stats__crv_types__RaisedCosineDistribution(): + from sympy.stats.crv_types import RaisedCosineDistribution + assert _test_args(RaisedCosineDistribution(1, 1)) + +def test_sympy__stats__crv_types__RayleighDistribution(): + from sympy.stats.crv_types import RayleighDistribution + assert _test_args(RayleighDistribution(1)) + +def test_sympy__stats__crv_types__ReciprocalDistribution(): + from sympy.stats.crv_types import ReciprocalDistribution + assert _test_args(ReciprocalDistribution(5, 30)) + +def test_sympy__stats__crv_types__ShiftedGompertzDistribution(): + from sympy.stats.crv_types import ShiftedGompertzDistribution + assert _test_args(ShiftedGompertzDistribution(1, 1)) + +def test_sympy__stats__crv_types__StudentTDistribution(): + from sympy.stats.crv_types import StudentTDistribution + assert _test_args(StudentTDistribution(1)) + +def test_sympy__stats__crv_types__TrapezoidalDistribution(): + from sympy.stats.crv_types import TrapezoidalDistribution + assert _test_args(TrapezoidalDistribution(1, 2, 3, 4)) + +def test_sympy__stats__crv_types__TriangularDistribution(): + from sympy.stats.crv_types import TriangularDistribution + assert _test_args(TriangularDistribution(-1, 0, 1)) + + +def test_sympy__stats__crv_types__UniformDistribution(): + from sympy.stats.crv_types import UniformDistribution + assert _test_args(UniformDistribution(0, 1)) + + +def test_sympy__stats__crv_types__UniformSumDistribution(): + from sympy.stats.crv_types import UniformSumDistribution + assert _test_args(UniformSumDistribution(1)) + + +def test_sympy__stats__crv_types__VonMisesDistribution(): + from sympy.stats.crv_types import VonMisesDistribution + assert _test_args(VonMisesDistribution(1, 1)) + + +def test_sympy__stats__crv_types__WeibullDistribution(): + from sympy.stats.crv_types import WeibullDistribution + assert _test_args(WeibullDistribution(1, 1)) + + +def test_sympy__stats__crv_types__WignerSemicircleDistribution(): + from sympy.stats.crv_types import WignerSemicircleDistribution + assert _test_args(WignerSemicircleDistribution(1)) + + +def test_sympy__stats__drv_types__GeometricDistribution(): + from sympy.stats.drv_types import GeometricDistribution + assert _test_args(GeometricDistribution(.5)) + +def test_sympy__stats__drv_types__HermiteDistribution(): + from sympy.stats.drv_types import HermiteDistribution + assert _test_args(HermiteDistribution(1, 2)) + +def test_sympy__stats__drv_types__LogarithmicDistribution(): + from sympy.stats.drv_types import LogarithmicDistribution + assert _test_args(LogarithmicDistribution(.5)) + + +def test_sympy__stats__drv_types__NegativeBinomialDistribution(): + from sympy.stats.drv_types import NegativeBinomialDistribution + assert _test_args(NegativeBinomialDistribution(.5, .5)) + +def test_sympy__stats__drv_types__FlorySchulzDistribution(): + from sympy.stats.drv_types import FlorySchulzDistribution + assert _test_args(FlorySchulzDistribution(.5)) + +def test_sympy__stats__drv_types__PoissonDistribution(): + from sympy.stats.drv_types import PoissonDistribution + assert _test_args(PoissonDistribution(1)) + + +def test_sympy__stats__drv_types__SkellamDistribution(): + from sympy.stats.drv_types import SkellamDistribution + assert _test_args(SkellamDistribution(1, 1)) + + +def test_sympy__stats__drv_types__YuleSimonDistribution(): + from sympy.stats.drv_types import YuleSimonDistribution + assert _test_args(YuleSimonDistribution(.5)) + + +def test_sympy__stats__drv_types__ZetaDistribution(): + from sympy.stats.drv_types import ZetaDistribution + assert _test_args(ZetaDistribution(1.5)) + + +def test_sympy__stats__joint_rv__JointDistribution(): + from sympy.stats.joint_rv import JointDistribution + assert _test_args(JointDistribution(1, 2, 3, 4)) + + +def test_sympy__stats__joint_rv_types__MultivariateNormalDistribution(): + from sympy.stats.joint_rv_types import MultivariateNormalDistribution + assert _test_args( + MultivariateNormalDistribution([0, 1], [[1, 0],[0, 1]])) + +def test_sympy__stats__joint_rv_types__MultivariateLaplaceDistribution(): + from sympy.stats.joint_rv_types import MultivariateLaplaceDistribution + assert _test_args(MultivariateLaplaceDistribution([0, 1], [[1, 0],[0, 1]])) + + +def test_sympy__stats__joint_rv_types__MultivariateTDistribution(): + from sympy.stats.joint_rv_types import MultivariateTDistribution + assert _test_args(MultivariateTDistribution([0, 1], [[1, 0],[0, 1]], 1)) + + +def test_sympy__stats__joint_rv_types__NormalGammaDistribution(): + from sympy.stats.joint_rv_types import NormalGammaDistribution + assert _test_args(NormalGammaDistribution(1, 2, 3, 4)) + +def test_sympy__stats__joint_rv_types__GeneralizedMultivariateLogGammaDistribution(): + from sympy.stats.joint_rv_types import GeneralizedMultivariateLogGammaDistribution + v, l, mu = (4, [1, 2, 3, 4], [1, 2, 3, 4]) + assert _test_args(GeneralizedMultivariateLogGammaDistribution(S.Half, v, l, mu)) + +def test_sympy__stats__joint_rv_types__MultivariateBetaDistribution(): + from sympy.stats.joint_rv_types import MultivariateBetaDistribution + assert _test_args(MultivariateBetaDistribution([1, 2, 3])) + +def test_sympy__stats__joint_rv_types__MultivariateEwensDistribution(): + from sympy.stats.joint_rv_types import MultivariateEwensDistribution + assert _test_args(MultivariateEwensDistribution(5, 1)) + +def test_sympy__stats__joint_rv_types__MultinomialDistribution(): + from sympy.stats.joint_rv_types import MultinomialDistribution + assert _test_args(MultinomialDistribution(5, [0.5, 0.1, 0.3])) + +def test_sympy__stats__joint_rv_types__NegativeMultinomialDistribution(): + from sympy.stats.joint_rv_types import NegativeMultinomialDistribution + assert _test_args(NegativeMultinomialDistribution(5, [0.5, 0.1, 0.3])) + +def test_sympy__stats__rv__RandomIndexedSymbol(): + from sympy.stats.rv import RandomIndexedSymbol, pspace + from sympy.stats.stochastic_process_types import DiscreteMarkovChain + X = DiscreteMarkovChain("X") + assert _test_args(RandomIndexedSymbol(X[0].symbol, pspace(X[0]))) + +def test_sympy__stats__rv__RandomMatrixSymbol(): + from sympy.stats.rv import RandomMatrixSymbol + from sympy.stats.random_matrix import RandomMatrixPSpace + pspace = RandomMatrixPSpace('P') + assert _test_args(RandomMatrixSymbol('M', 3, 3, pspace)) + +def test_sympy__stats__stochastic_process__StochasticPSpace(): + from sympy.stats.stochastic_process import StochasticPSpace + from sympy.stats.stochastic_process_types import StochasticProcess + from sympy.stats.frv_types import BernoulliDistribution + assert _test_args(StochasticPSpace("Y", StochasticProcess("Y", [1, 2, 3]), BernoulliDistribution(S.Half, 1, 0))) + +def test_sympy__stats__stochastic_process_types__StochasticProcess(): + from sympy.stats.stochastic_process_types import StochasticProcess + assert _test_args(StochasticProcess("Y", [1, 2, 3])) + +def test_sympy__stats__stochastic_process_types__MarkovProcess(): + from sympy.stats.stochastic_process_types import MarkovProcess + assert _test_args(MarkovProcess("Y", [1, 2, 3])) + +def test_sympy__stats__stochastic_process_types__DiscreteTimeStochasticProcess(): + from sympy.stats.stochastic_process_types import DiscreteTimeStochasticProcess + assert _test_args(DiscreteTimeStochasticProcess("Y", [1, 2, 3])) + +def test_sympy__stats__stochastic_process_types__ContinuousTimeStochasticProcess(): + from sympy.stats.stochastic_process_types import ContinuousTimeStochasticProcess + assert _test_args(ContinuousTimeStochasticProcess("Y", [1, 2, 3])) + +def test_sympy__stats__stochastic_process_types__TransitionMatrixOf(): + from sympy.stats.stochastic_process_types import TransitionMatrixOf, DiscreteMarkovChain + from sympy.matrices.expressions.matexpr import MatrixSymbol + DMC = DiscreteMarkovChain("Y") + assert _test_args(TransitionMatrixOf(DMC, MatrixSymbol('T', 3, 3))) + +def test_sympy__stats__stochastic_process_types__GeneratorMatrixOf(): + from sympy.stats.stochastic_process_types import GeneratorMatrixOf, ContinuousMarkovChain + from sympy.matrices.expressions.matexpr import MatrixSymbol + DMC = ContinuousMarkovChain("Y") + assert _test_args(GeneratorMatrixOf(DMC, MatrixSymbol('T', 3, 3))) + +def test_sympy__stats__stochastic_process_types__StochasticStateSpaceOf(): + from sympy.stats.stochastic_process_types import StochasticStateSpaceOf, DiscreteMarkovChain + DMC = DiscreteMarkovChain("Y") + assert _test_args(StochasticStateSpaceOf(DMC, [0, 1, 2])) + +def test_sympy__stats__stochastic_process_types__DiscreteMarkovChain(): + from sympy.stats.stochastic_process_types import DiscreteMarkovChain + from sympy.matrices.expressions.matexpr import MatrixSymbol + assert _test_args(DiscreteMarkovChain("Y", [0, 1, 2], MatrixSymbol('T', 3, 3))) + +def test_sympy__stats__stochastic_process_types__ContinuousMarkovChain(): + from sympy.stats.stochastic_process_types import ContinuousMarkovChain + from sympy.matrices.expressions.matexpr import MatrixSymbol + assert _test_args(ContinuousMarkovChain("Y", [0, 1, 2], MatrixSymbol('T', 3, 3))) + +def test_sympy__stats__stochastic_process_types__BernoulliProcess(): + from sympy.stats.stochastic_process_types import BernoulliProcess + assert _test_args(BernoulliProcess("B", 0.5, 1, 0)) + +def test_sympy__stats__stochastic_process_types__CountingProcess(): + from sympy.stats.stochastic_process_types import CountingProcess + assert _test_args(CountingProcess("C")) + +def test_sympy__stats__stochastic_process_types__PoissonProcess(): + from sympy.stats.stochastic_process_types import PoissonProcess + assert _test_args(PoissonProcess("X", 2)) + +def test_sympy__stats__stochastic_process_types__WienerProcess(): + from sympy.stats.stochastic_process_types import WienerProcess + assert _test_args(WienerProcess("X")) + +def test_sympy__stats__stochastic_process_types__GammaProcess(): + from sympy.stats.stochastic_process_types import GammaProcess + assert _test_args(GammaProcess("X", 1, 2)) + +def test_sympy__stats__random_matrix__RandomMatrixPSpace(): + from sympy.stats.random_matrix import RandomMatrixPSpace + from sympy.stats.random_matrix_models import RandomMatrixEnsembleModel + model = RandomMatrixEnsembleModel('R', 3) + assert _test_args(RandomMatrixPSpace('P', model=model)) + +def test_sympy__stats__random_matrix_models__RandomMatrixEnsembleModel(): + from sympy.stats.random_matrix_models import RandomMatrixEnsembleModel + assert _test_args(RandomMatrixEnsembleModel('R', 3)) + +def test_sympy__stats__random_matrix_models__GaussianEnsembleModel(): + from sympy.stats.random_matrix_models import GaussianEnsembleModel + assert _test_args(GaussianEnsembleModel('G', 3)) + +def test_sympy__stats__random_matrix_models__GaussianUnitaryEnsembleModel(): + from sympy.stats.random_matrix_models import GaussianUnitaryEnsembleModel + assert _test_args(GaussianUnitaryEnsembleModel('U', 3)) + +def test_sympy__stats__random_matrix_models__GaussianOrthogonalEnsembleModel(): + from sympy.stats.random_matrix_models import GaussianOrthogonalEnsembleModel + assert _test_args(GaussianOrthogonalEnsembleModel('U', 3)) + +def test_sympy__stats__random_matrix_models__GaussianSymplecticEnsembleModel(): + from sympy.stats.random_matrix_models import GaussianSymplecticEnsembleModel + assert _test_args(GaussianSymplecticEnsembleModel('U', 3)) + +def test_sympy__stats__random_matrix_models__CircularEnsembleModel(): + from sympy.stats.random_matrix_models import CircularEnsembleModel + assert _test_args(CircularEnsembleModel('C', 3)) + +def test_sympy__stats__random_matrix_models__CircularUnitaryEnsembleModel(): + from sympy.stats.random_matrix_models import CircularUnitaryEnsembleModel + assert _test_args(CircularUnitaryEnsembleModel('U', 3)) + +def test_sympy__stats__random_matrix_models__CircularOrthogonalEnsembleModel(): + from sympy.stats.random_matrix_models import CircularOrthogonalEnsembleModel + assert _test_args(CircularOrthogonalEnsembleModel('O', 3)) + +def test_sympy__stats__random_matrix_models__CircularSymplecticEnsembleModel(): + from sympy.stats.random_matrix_models import CircularSymplecticEnsembleModel + assert _test_args(CircularSymplecticEnsembleModel('S', 3)) + +def test_sympy__stats__symbolic_multivariate_probability__ExpectationMatrix(): + from sympy.stats import ExpectationMatrix + from sympy.stats.rv import RandomMatrixSymbol + assert _test_args(ExpectationMatrix(RandomMatrixSymbol('R', 2, 1))) + +def test_sympy__stats__symbolic_multivariate_probability__VarianceMatrix(): + from sympy.stats import VarianceMatrix + from sympy.stats.rv import RandomMatrixSymbol + assert _test_args(VarianceMatrix(RandomMatrixSymbol('R', 3, 1))) + +def test_sympy__stats__symbolic_multivariate_probability__CrossCovarianceMatrix(): + from sympy.stats import CrossCovarianceMatrix + from sympy.stats.rv import RandomMatrixSymbol + assert _test_args(CrossCovarianceMatrix(RandomMatrixSymbol('R', 3, 1), + RandomMatrixSymbol('X', 3, 1))) + +def test_sympy__stats__matrix_distributions__MatrixPSpace(): + from sympy.stats.matrix_distributions import MatrixDistribution, MatrixPSpace + from sympy.matrices.dense import Matrix + M = MatrixDistribution(1, Matrix([[1, 0], [0, 1]])) + assert _test_args(MatrixPSpace('M', M, 2, 2)) + +def test_sympy__stats__matrix_distributions__MatrixDistribution(): + from sympy.stats.matrix_distributions import MatrixDistribution + from sympy.matrices.dense import Matrix + assert _test_args(MatrixDistribution(1, Matrix([[1, 0], [0, 1]]))) + +def test_sympy__stats__matrix_distributions__MatrixGammaDistribution(): + from sympy.stats.matrix_distributions import MatrixGammaDistribution + from sympy.matrices.dense import Matrix + assert _test_args(MatrixGammaDistribution(3, 4, Matrix([[1, 0], [0, 1]]))) + +def test_sympy__stats__matrix_distributions__WishartDistribution(): + from sympy.stats.matrix_distributions import WishartDistribution + from sympy.matrices.dense import Matrix + assert _test_args(WishartDistribution(3, Matrix([[1, 0], [0, 1]]))) + +def test_sympy__stats__matrix_distributions__MatrixNormalDistribution(): + from sympy.stats.matrix_distributions import MatrixNormalDistribution + from sympy.matrices.expressions.matexpr import MatrixSymbol + L = MatrixSymbol('L', 1, 2) + S1 = MatrixSymbol('S1', 1, 1) + S2 = MatrixSymbol('S2', 2, 2) + assert _test_args(MatrixNormalDistribution(L, S1, S2)) + +def test_sympy__stats__matrix_distributions__MatrixStudentTDistribution(): + from sympy.stats.matrix_distributions import MatrixStudentTDistribution + from sympy.matrices.expressions.matexpr import MatrixSymbol + v = symbols('v', positive=True) + Omega = MatrixSymbol('Omega', 3, 3) + Sigma = MatrixSymbol('Sigma', 1, 1) + Location = MatrixSymbol('Location', 1, 3) + assert _test_args(MatrixStudentTDistribution(v, Location, Omega, Sigma)) + +def test_sympy__utilities__matchpy_connector__WildDot(): + from sympy.utilities.matchpy_connector import WildDot + assert _test_args(WildDot("w_")) + + +def test_sympy__utilities__matchpy_connector__WildPlus(): + from sympy.utilities.matchpy_connector import WildPlus + assert _test_args(WildPlus("w__")) + + +def test_sympy__utilities__matchpy_connector__WildStar(): + from sympy.utilities.matchpy_connector import WildStar + assert _test_args(WildStar("w___")) + + +def test_sympy__core__symbol__Str(): + from sympy.core.symbol import Str + assert _test_args(Str('t')) + +def test_sympy__core__symbol__Dummy(): + from sympy.core.symbol import Dummy + assert _test_args(Dummy('t')) + + +def test_sympy__core__symbol__Symbol(): + from sympy.core.symbol import Symbol + assert _test_args(Symbol('t')) + + +def test_sympy__core__symbol__Wild(): + from sympy.core.symbol import Wild + assert _test_args(Wild('x', exclude=[x])) + + +@SKIP("abstract class") +def test_sympy__functions__combinatorial__factorials__CombinatorialFunction(): + pass + + +def test_sympy__functions__combinatorial__factorials__FallingFactorial(): + from sympy.functions.combinatorial.factorials import FallingFactorial + assert _test_args(FallingFactorial(2, x)) + + +def test_sympy__functions__combinatorial__factorials__MultiFactorial(): + from sympy.functions.combinatorial.factorials import MultiFactorial + assert _test_args(MultiFactorial(x)) + + +def test_sympy__functions__combinatorial__factorials__RisingFactorial(): + from sympy.functions.combinatorial.factorials import RisingFactorial + assert _test_args(RisingFactorial(2, x)) + + +def test_sympy__functions__combinatorial__factorials__binomial(): + from sympy.functions.combinatorial.factorials import binomial + assert _test_args(binomial(2, x)) + + +def test_sympy__functions__combinatorial__factorials__subfactorial(): + from sympy.functions.combinatorial.factorials import subfactorial + assert _test_args(subfactorial(x)) + + +def test_sympy__functions__combinatorial__factorials__factorial(): + from sympy.functions.combinatorial.factorials import factorial + assert _test_args(factorial(x)) + + +def test_sympy__functions__combinatorial__factorials__factorial2(): + from sympy.functions.combinatorial.factorials import factorial2 + assert _test_args(factorial2(x)) + + +def test_sympy__functions__combinatorial__numbers__bell(): + from sympy.functions.combinatorial.numbers import bell + assert _test_args(bell(x, y)) + + +def test_sympy__functions__combinatorial__numbers__bernoulli(): + from sympy.functions.combinatorial.numbers import bernoulli + assert _test_args(bernoulli(x)) + + +def test_sympy__functions__combinatorial__numbers__catalan(): + from sympy.functions.combinatorial.numbers import catalan + assert _test_args(catalan(x)) + + +def test_sympy__functions__combinatorial__numbers__genocchi(): + from sympy.functions.combinatorial.numbers import genocchi + assert _test_args(genocchi(x)) + + +def test_sympy__functions__combinatorial__numbers__euler(): + from sympy.functions.combinatorial.numbers import euler + assert _test_args(euler(x)) + + +def test_sympy__functions__combinatorial__numbers__andre(): + from sympy.functions.combinatorial.numbers import andre + assert _test_args(andre(x)) + + +def test_sympy__functions__combinatorial__numbers__carmichael(): + from sympy.functions.combinatorial.numbers import carmichael + assert _test_args(carmichael(x)) + + +def test_sympy__functions__combinatorial__numbers__divisor_sigma(): + from sympy.functions.combinatorial.numbers import divisor_sigma + k = symbols('k', integer=True) + n = symbols('n', integer=True) + t = divisor_sigma(n, k) + assert _test_args(t) + + +def test_sympy__functions__combinatorial__numbers__fibonacci(): + from sympy.functions.combinatorial.numbers import fibonacci + assert _test_args(fibonacci(x)) + + +def test_sympy__functions__combinatorial__numbers__jacobi_symbol(): + from sympy.functions.combinatorial.numbers import jacobi_symbol + assert _test_args(jacobi_symbol(2, 3)) + + +def test_sympy__functions__combinatorial__numbers__kronecker_symbol(): + from sympy.functions.combinatorial.numbers import kronecker_symbol + assert _test_args(kronecker_symbol(2, 3)) + + +def test_sympy__functions__combinatorial__numbers__legendre_symbol(): + from sympy.functions.combinatorial.numbers import legendre_symbol + assert _test_args(legendre_symbol(2, 3)) + + +def test_sympy__functions__combinatorial__numbers__mobius(): + from sympy.functions.combinatorial.numbers import mobius + assert _test_args(mobius(2)) + + +def test_sympy__functions__combinatorial__numbers__motzkin(): + from sympy.functions.combinatorial.numbers import motzkin + assert _test_args(motzkin(5)) + + +def test_sympy__functions__combinatorial__numbers__partition(): + from sympy.core.symbol import Symbol + from sympy.functions.combinatorial.numbers import partition + assert _test_args(partition(Symbol('a', integer=True))) + + +def test_sympy__functions__combinatorial__numbers__primenu(): + from sympy.functions.combinatorial.numbers import primenu + n = symbols('n', integer=True) + t = primenu(n) + assert _test_args(t) + + +def test_sympy__functions__combinatorial__numbers__primeomega(): + from sympy.functions.combinatorial.numbers import primeomega + n = symbols('n', integer=True) + t = primeomega(n) + assert _test_args(t) + + +def test_sympy__functions__combinatorial__numbers__primepi(): + from sympy.functions.combinatorial.numbers import primepi + n = symbols('n') + t = primepi(n) + assert _test_args(t) + + +def test_sympy__functions__combinatorial__numbers__reduced_totient(): + from sympy.functions.combinatorial.numbers import reduced_totient + k = symbols('k', integer=True) + t = reduced_totient(k) + assert _test_args(t) + + +def test_sympy__functions__combinatorial__numbers__totient(): + from sympy.functions.combinatorial.numbers import totient + k = symbols('k', integer=True) + t = totient(k) + assert _test_args(t) + + +def test_sympy__functions__combinatorial__numbers__tribonacci(): + from sympy.functions.combinatorial.numbers import tribonacci + assert _test_args(tribonacci(x)) + + +def test_sympy__functions__combinatorial__numbers__udivisor_sigma(): + from sympy.functions.combinatorial.numbers import udivisor_sigma + k = symbols('k', integer=True) + n = symbols('n', integer=True) + t = udivisor_sigma(n, k) + assert _test_args(t) + + +def test_sympy__functions__combinatorial__numbers__harmonic(): + from sympy.functions.combinatorial.numbers import harmonic + assert _test_args(harmonic(x, 2)) + + +def test_sympy__functions__combinatorial__numbers__lucas(): + from sympy.functions.combinatorial.numbers import lucas + assert _test_args(lucas(x)) + + +def test_sympy__functions__elementary__complexes__Abs(): + from sympy.functions.elementary.complexes import Abs + assert _test_args(Abs(x)) + + +def test_sympy__functions__elementary__complexes__adjoint(): + from sympy.functions.elementary.complexes import adjoint + assert _test_args(adjoint(x)) + + +def test_sympy__functions__elementary__complexes__arg(): + from sympy.functions.elementary.complexes import arg + assert _test_args(arg(x)) + + +def test_sympy__functions__elementary__complexes__conjugate(): + from sympy.functions.elementary.complexes import conjugate + assert _test_args(conjugate(x)) + + +def test_sympy__functions__elementary__complexes__im(): + from sympy.functions.elementary.complexes import im + assert _test_args(im(x)) + + +def test_sympy__functions__elementary__complexes__re(): + from sympy.functions.elementary.complexes import re + assert _test_args(re(x)) + + +def test_sympy__functions__elementary__complexes__sign(): + from sympy.functions.elementary.complexes import sign + assert _test_args(sign(x)) + + +def test_sympy__functions__elementary__complexes__polar_lift(): + from sympy.functions.elementary.complexes import polar_lift + assert _test_args(polar_lift(x)) + + +def test_sympy__functions__elementary__complexes__periodic_argument(): + from sympy.functions.elementary.complexes import periodic_argument + assert _test_args(periodic_argument(x, y)) + + +def test_sympy__functions__elementary__complexes__principal_branch(): + from sympy.functions.elementary.complexes import principal_branch + assert _test_args(principal_branch(x, y)) + + +def test_sympy__functions__elementary__complexes__transpose(): + from sympy.functions.elementary.complexes import transpose + assert _test_args(transpose(x)) + + +def test_sympy__functions__elementary__exponential__LambertW(): + from sympy.functions.elementary.exponential import LambertW + assert _test_args(LambertW(2)) + + +@SKIP("abstract class") +def test_sympy__functions__elementary__exponential__ExpBase(): + pass + + +def test_sympy__functions__elementary__exponential__exp(): + from sympy.functions.elementary.exponential import exp + assert _test_args(exp(2)) + + +def test_sympy__functions__elementary__exponential__exp_polar(): + from sympy.functions.elementary.exponential import exp_polar + assert _test_args(exp_polar(2)) + + +def test_sympy__functions__elementary__exponential__log(): + from sympy.functions.elementary.exponential import log + assert _test_args(log(2)) + + +@SKIP("abstract class") +def test_sympy__functions__elementary__hyperbolic__HyperbolicFunction(): + pass + + +@SKIP("abstract class") +def test_sympy__functions__elementary__hyperbolic__ReciprocalHyperbolicFunction(): + pass + + +@SKIP("abstract class") +def test_sympy__functions__elementary__hyperbolic__InverseHyperbolicFunction(): + pass + + +def test_sympy__functions__elementary__hyperbolic__acosh(): + from sympy.functions.elementary.hyperbolic import acosh + assert _test_args(acosh(2)) + + +def test_sympy__functions__elementary__hyperbolic__acoth(): + from sympy.functions.elementary.hyperbolic import acoth + assert _test_args(acoth(2)) + + +def test_sympy__functions__elementary__hyperbolic__asinh(): + from sympy.functions.elementary.hyperbolic import asinh + assert _test_args(asinh(2)) + + +def test_sympy__functions__elementary__hyperbolic__atanh(): + from sympy.functions.elementary.hyperbolic import atanh + assert _test_args(atanh(2)) + + +def test_sympy__functions__elementary__hyperbolic__asech(): + from sympy.functions.elementary.hyperbolic import asech + assert _test_args(asech(x)) + +def test_sympy__functions__elementary__hyperbolic__acsch(): + from sympy.functions.elementary.hyperbolic import acsch + assert _test_args(acsch(x)) + +def test_sympy__functions__elementary__hyperbolic__cosh(): + from sympy.functions.elementary.hyperbolic import cosh + assert _test_args(cosh(2)) + + +def test_sympy__functions__elementary__hyperbolic__coth(): + from sympy.functions.elementary.hyperbolic import coth + assert _test_args(coth(2)) + + +def test_sympy__functions__elementary__hyperbolic__csch(): + from sympy.functions.elementary.hyperbolic import csch + assert _test_args(csch(2)) + + +def test_sympy__functions__elementary__hyperbolic__sech(): + from sympy.functions.elementary.hyperbolic import sech + assert _test_args(sech(2)) + + +def test_sympy__functions__elementary__hyperbolic__sinh(): + from sympy.functions.elementary.hyperbolic import sinh + assert _test_args(sinh(2)) + + +def test_sympy__functions__elementary__hyperbolic__tanh(): + from sympy.functions.elementary.hyperbolic import tanh + assert _test_args(tanh(2)) + + +@SKIP("abstract class") +def test_sympy__functions__elementary__integers__RoundFunction(): + pass + + +def test_sympy__functions__elementary__integers__ceiling(): + from sympy.functions.elementary.integers import ceiling + assert _test_args(ceiling(x)) + + +def test_sympy__functions__elementary__integers__floor(): + from sympy.functions.elementary.integers import floor + assert _test_args(floor(x)) + + +def test_sympy__functions__elementary__integers__frac(): + from sympy.functions.elementary.integers import frac + assert _test_args(frac(x)) + + +def test_sympy__functions__elementary__miscellaneous__IdentityFunction(): + from sympy.functions.elementary.miscellaneous import IdentityFunction + assert _test_args(IdentityFunction()) + + +def test_sympy__functions__elementary__miscellaneous__Max(): + from sympy.functions.elementary.miscellaneous import Max + assert _test_args(Max(x, 2)) + + +def test_sympy__functions__elementary__miscellaneous__Min(): + from sympy.functions.elementary.miscellaneous import Min + assert _test_args(Min(x, 2)) + + +@SKIP("abstract class") +def test_sympy__functions__elementary__miscellaneous__MinMaxBase(): + pass + + +def test_sympy__functions__elementary__miscellaneous__Rem(): + from sympy.functions.elementary.miscellaneous import Rem + assert _test_args(Rem(x, 2)) + + +def test_sympy__functions__elementary__piecewise__ExprCondPair(): + from sympy.functions.elementary.piecewise import ExprCondPair + assert _test_args(ExprCondPair(1, True)) + + +def test_sympy__functions__elementary__piecewise__Piecewise(): + from sympy.functions.elementary.piecewise import Piecewise + assert _test_args(Piecewise((1, x >= 0), (0, True))) + + +@SKIP("abstract class") +def test_sympy__functions__elementary__trigonometric__TrigonometricFunction(): + pass + +@SKIP("abstract class") +def test_sympy__functions__elementary__trigonometric__ReciprocalTrigonometricFunction(): + pass + +@SKIP("abstract class") +def test_sympy__functions__elementary__trigonometric__InverseTrigonometricFunction(): + pass + +def test_sympy__functions__elementary__trigonometric__acos(): + from sympy.functions.elementary.trigonometric import acos + assert _test_args(acos(2)) + + +def test_sympy__functions__elementary__trigonometric__acot(): + from sympy.functions.elementary.trigonometric import acot + assert _test_args(acot(2)) + + +def test_sympy__functions__elementary__trigonometric__asin(): + from sympy.functions.elementary.trigonometric import asin + assert _test_args(asin(2)) + + +def test_sympy__functions__elementary__trigonometric__asec(): + from sympy.functions.elementary.trigonometric import asec + assert _test_args(asec(x)) + + +def test_sympy__functions__elementary__trigonometric__acsc(): + from sympy.functions.elementary.trigonometric import acsc + assert _test_args(acsc(x)) + + +def test_sympy__functions__elementary__trigonometric__atan(): + from sympy.functions.elementary.trigonometric import atan + assert _test_args(atan(2)) + + +def test_sympy__functions__elementary__trigonometric__atan2(): + from sympy.functions.elementary.trigonometric import atan2 + assert _test_args(atan2(2, 3)) + + +def test_sympy__functions__elementary__trigonometric__cos(): + from sympy.functions.elementary.trigonometric import cos + assert _test_args(cos(2)) + + +def test_sympy__functions__elementary__trigonometric__csc(): + from sympy.functions.elementary.trigonometric import csc + assert _test_args(csc(2)) + + +def test_sympy__functions__elementary__trigonometric__cot(): + from sympy.functions.elementary.trigonometric import cot + assert _test_args(cot(2)) + + +def test_sympy__functions__elementary__trigonometric__sin(): + assert _test_args(sin(2)) + + +def test_sympy__functions__elementary__trigonometric__sinc(): + from sympy.functions.elementary.trigonometric import sinc + assert _test_args(sinc(2)) + + +def test_sympy__functions__elementary__trigonometric__sec(): + from sympy.functions.elementary.trigonometric import sec + assert _test_args(sec(2)) + + +def test_sympy__functions__elementary__trigonometric__tan(): + from sympy.functions.elementary.trigonometric import tan + assert _test_args(tan(2)) + + +@SKIP("abstract class") +def test_sympy__functions__special__bessel__BesselBase(): + pass + + +@SKIP("abstract class") +def test_sympy__functions__special__bessel__SphericalBesselBase(): + pass + + +@SKIP("abstract class") +def test_sympy__functions__special__bessel__SphericalHankelBase(): + pass + + +def test_sympy__functions__special__bessel__besseli(): + from sympy.functions.special.bessel import besseli + assert _test_args(besseli(x, 1)) + + +def test_sympy__functions__special__bessel__besselj(): + from sympy.functions.special.bessel import besselj + assert _test_args(besselj(x, 1)) + + +def test_sympy__functions__special__bessel__besselk(): + from sympy.functions.special.bessel import besselk + assert _test_args(besselk(x, 1)) + + +def test_sympy__functions__special__bessel__bessely(): + from sympy.functions.special.bessel import bessely + assert _test_args(bessely(x, 1)) + + +def test_sympy__functions__special__bessel__hankel1(): + from sympy.functions.special.bessel import hankel1 + assert _test_args(hankel1(x, 1)) + + +def test_sympy__functions__special__bessel__hankel2(): + from sympy.functions.special.bessel import hankel2 + assert _test_args(hankel2(x, 1)) + + +def test_sympy__functions__special__bessel__jn(): + from sympy.functions.special.bessel import jn + assert _test_args(jn(0, x)) + + +def test_sympy__functions__special__bessel__yn(): + from sympy.functions.special.bessel import yn + assert _test_args(yn(0, x)) + + +def test_sympy__functions__special__bessel__hn1(): + from sympy.functions.special.bessel import hn1 + assert _test_args(hn1(0, x)) + + +def test_sympy__functions__special__bessel__hn2(): + from sympy.functions.special.bessel import hn2 + assert _test_args(hn2(0, x)) + + +def test_sympy__functions__special__bessel__AiryBase(): + pass + + +def test_sympy__functions__special__bessel__airyai(): + from sympy.functions.special.bessel import airyai + assert _test_args(airyai(2)) + + +def test_sympy__functions__special__bessel__airybi(): + from sympy.functions.special.bessel import airybi + assert _test_args(airybi(2)) + + +def test_sympy__functions__special__bessel__airyaiprime(): + from sympy.functions.special.bessel import airyaiprime + assert _test_args(airyaiprime(2)) + + +def test_sympy__functions__special__bessel__airybiprime(): + from sympy.functions.special.bessel import airybiprime + assert _test_args(airybiprime(2)) + + +def test_sympy__functions__special__bessel__marcumq(): + from sympy.functions.special.bessel import marcumq + assert _test_args(marcumq(x, y, z)) + + +def test_sympy__functions__special__elliptic_integrals__elliptic_k(): + from sympy.functions.special.elliptic_integrals import elliptic_k as K + assert _test_args(K(x)) + + +def test_sympy__functions__special__elliptic_integrals__elliptic_f(): + from sympy.functions.special.elliptic_integrals import elliptic_f as F + assert _test_args(F(x, y)) + + +def test_sympy__functions__special__elliptic_integrals__elliptic_e(): + from sympy.functions.special.elliptic_integrals import elliptic_e as E + assert _test_args(E(x)) + assert _test_args(E(x, y)) + + +def test_sympy__functions__special__elliptic_integrals__elliptic_pi(): + from sympy.functions.special.elliptic_integrals import elliptic_pi as P + assert _test_args(P(x, y)) + assert _test_args(P(x, y, z)) + + +def test_sympy__functions__special__delta_functions__DiracDelta(): + from sympy.functions.special.delta_functions import DiracDelta + assert _test_args(DiracDelta(x, 1)) + + +def test_sympy__functions__special__singularity_functions__SingularityFunction(): + from sympy.functions.special.singularity_functions import SingularityFunction + assert _test_args(SingularityFunction(x, y, z)) + + +def test_sympy__functions__special__delta_functions__Heaviside(): + from sympy.functions.special.delta_functions import Heaviside + assert _test_args(Heaviside(x)) + + +def test_sympy__functions__special__error_functions__erf(): + from sympy.functions.special.error_functions import erf + assert _test_args(erf(2)) + +def test_sympy__functions__special__error_functions__erfc(): + from sympy.functions.special.error_functions import erfc + assert _test_args(erfc(2)) + +def test_sympy__functions__special__error_functions__erfi(): + from sympy.functions.special.error_functions import erfi + assert _test_args(erfi(2)) + +def test_sympy__functions__special__error_functions__erf2(): + from sympy.functions.special.error_functions import erf2 + assert _test_args(erf2(2, 3)) + +def test_sympy__functions__special__error_functions__erfinv(): + from sympy.functions.special.error_functions import erfinv + assert _test_args(erfinv(2)) + +def test_sympy__functions__special__error_functions__erfcinv(): + from sympy.functions.special.error_functions import erfcinv + assert _test_args(erfcinv(2)) + +def test_sympy__functions__special__error_functions__erf2inv(): + from sympy.functions.special.error_functions import erf2inv + assert _test_args(erf2inv(2, 3)) + +@SKIP("abstract class") +def test_sympy__functions__special__error_functions__FresnelIntegral(): + pass + + +def test_sympy__functions__special__error_functions__fresnels(): + from sympy.functions.special.error_functions import fresnels + assert _test_args(fresnels(2)) + + +def test_sympy__functions__special__error_functions__fresnelc(): + from sympy.functions.special.error_functions import fresnelc + assert _test_args(fresnelc(2)) + + +def test_sympy__functions__special__error_functions__erfs(): + from sympy.functions.special.error_functions import _erfs + assert _test_args(_erfs(2)) + + +def test_sympy__functions__special__error_functions__Ei(): + from sympy.functions.special.error_functions import Ei + assert _test_args(Ei(2)) + + +def test_sympy__functions__special__error_functions__li(): + from sympy.functions.special.error_functions import li + assert _test_args(li(2)) + + +def test_sympy__functions__special__error_functions__Li(): + from sympy.functions.special.error_functions import Li + assert _test_args(Li(5)) + + +@SKIP("abstract class") +def test_sympy__functions__special__error_functions__TrigonometricIntegral(): + pass + + +def test_sympy__functions__special__error_functions__Si(): + from sympy.functions.special.error_functions import Si + assert _test_args(Si(2)) + + +def test_sympy__functions__special__error_functions__Ci(): + from sympy.functions.special.error_functions import Ci + assert _test_args(Ci(2)) + + +def test_sympy__functions__special__error_functions__Shi(): + from sympy.functions.special.error_functions import Shi + assert _test_args(Shi(2)) + + +def test_sympy__functions__special__error_functions__Chi(): + from sympy.functions.special.error_functions import Chi + assert _test_args(Chi(2)) + + +def test_sympy__functions__special__error_functions__expint(): + from sympy.functions.special.error_functions import expint + assert _test_args(expint(y, x)) + + +def test_sympy__functions__special__gamma_functions__gamma(): + from sympy.functions.special.gamma_functions import gamma + assert _test_args(gamma(x)) + + +def test_sympy__functions__special__gamma_functions__loggamma(): + from sympy.functions.special.gamma_functions import loggamma + assert _test_args(loggamma(x)) + + +def test_sympy__functions__special__gamma_functions__lowergamma(): + from sympy.functions.special.gamma_functions import lowergamma + assert _test_args(lowergamma(x, 2)) + + +def test_sympy__functions__special__gamma_functions__polygamma(): + from sympy.functions.special.gamma_functions import polygamma + assert _test_args(polygamma(x, 2)) + +def test_sympy__functions__special__gamma_functions__digamma(): + from sympy.functions.special.gamma_functions import digamma + assert _test_args(digamma(x)) + +def test_sympy__functions__special__gamma_functions__trigamma(): + from sympy.functions.special.gamma_functions import trigamma + assert _test_args(trigamma(x)) + +def test_sympy__functions__special__gamma_functions__uppergamma(): + from sympy.functions.special.gamma_functions import uppergamma + assert _test_args(uppergamma(x, 2)) + +def test_sympy__functions__special__gamma_functions__multigamma(): + from sympy.functions.special.gamma_functions import multigamma + assert _test_args(multigamma(x, 1)) + + +def test_sympy__functions__special__beta_functions__beta(): + from sympy.functions.special.beta_functions import beta + assert _test_args(beta(x)) + assert _test_args(beta(x, x)) + +def test_sympy__functions__special__beta_functions__betainc(): + from sympy.functions.special.beta_functions import betainc + assert _test_args(betainc(a, b, x, y)) + +def test_sympy__functions__special__beta_functions__betainc_regularized(): + from sympy.functions.special.beta_functions import betainc_regularized + assert _test_args(betainc_regularized(a, b, x, y)) + + +def test_sympy__functions__special__mathieu_functions__MathieuBase(): + pass + + +def test_sympy__functions__special__mathieu_functions__mathieus(): + from sympy.functions.special.mathieu_functions import mathieus + assert _test_args(mathieus(1, 1, 1)) + + +def test_sympy__functions__special__mathieu_functions__mathieuc(): + from sympy.functions.special.mathieu_functions import mathieuc + assert _test_args(mathieuc(1, 1, 1)) + + +def test_sympy__functions__special__mathieu_functions__mathieusprime(): + from sympy.functions.special.mathieu_functions import mathieusprime + assert _test_args(mathieusprime(1, 1, 1)) + + +def test_sympy__functions__special__mathieu_functions__mathieucprime(): + from sympy.functions.special.mathieu_functions import mathieucprime + assert _test_args(mathieucprime(1, 1, 1)) + + +@SKIP("abstract class") +def test_sympy__functions__special__hyper__TupleParametersBase(): + pass + + +@SKIP("abstract class") +def test_sympy__functions__special__hyper__TupleArg(): + pass + + +def test_sympy__functions__special__hyper__hyper(): + from sympy.functions.special.hyper import hyper + assert _test_args(hyper([1, 2, 3], [4, 5], x)) + + +def test_sympy__functions__special__hyper__meijerg(): + from sympy.functions.special.hyper import meijerg + assert _test_args(meijerg([1, 2, 3], [4, 5], [6], [], x)) + + +@SKIP("abstract class") +def test_sympy__functions__special__hyper__HyperRep(): + pass + + +def test_sympy__functions__special__hyper__HyperRep_power1(): + from sympy.functions.special.hyper import HyperRep_power1 + assert _test_args(HyperRep_power1(x, y)) + + +def test_sympy__functions__special__hyper__HyperRep_power2(): + from sympy.functions.special.hyper import HyperRep_power2 + assert _test_args(HyperRep_power2(x, y)) + + +def test_sympy__functions__special__hyper__HyperRep_log1(): + from sympy.functions.special.hyper import HyperRep_log1 + assert _test_args(HyperRep_log1(x)) + + +def test_sympy__functions__special__hyper__HyperRep_atanh(): + from sympy.functions.special.hyper import HyperRep_atanh + assert _test_args(HyperRep_atanh(x)) + + +def test_sympy__functions__special__hyper__HyperRep_asin1(): + from sympy.functions.special.hyper import HyperRep_asin1 + assert _test_args(HyperRep_asin1(x)) + + +def test_sympy__functions__special__hyper__HyperRep_asin2(): + from sympy.functions.special.hyper import HyperRep_asin2 + assert _test_args(HyperRep_asin2(x)) + + +def test_sympy__functions__special__hyper__HyperRep_sqrts1(): + from sympy.functions.special.hyper import HyperRep_sqrts1 + assert _test_args(HyperRep_sqrts1(x, y)) + + +def test_sympy__functions__special__hyper__HyperRep_sqrts2(): + from sympy.functions.special.hyper import HyperRep_sqrts2 + assert _test_args(HyperRep_sqrts2(x, y)) + + +def test_sympy__functions__special__hyper__HyperRep_log2(): + from sympy.functions.special.hyper import HyperRep_log2 + assert _test_args(HyperRep_log2(x)) + + +def test_sympy__functions__special__hyper__HyperRep_cosasin(): + from sympy.functions.special.hyper import HyperRep_cosasin + assert _test_args(HyperRep_cosasin(x, y)) + + +def test_sympy__functions__special__hyper__HyperRep_sinasin(): + from sympy.functions.special.hyper import HyperRep_sinasin + assert _test_args(HyperRep_sinasin(x, y)) + +def test_sympy__functions__special__hyper__appellf1(): + from sympy.functions.special.hyper import appellf1 + a, b1, b2, c, x, y = symbols('a b1 b2 c x y') + assert _test_args(appellf1(a, b1, b2, c, x, y)) + +@SKIP("abstract class") +def test_sympy__functions__special__polynomials__OrthogonalPolynomial(): + pass + + +def test_sympy__functions__special__polynomials__jacobi(): + from sympy.functions.special.polynomials import jacobi + assert _test_args(jacobi(x, y, 2, 2)) + + +def test_sympy__functions__special__polynomials__gegenbauer(): + from sympy.functions.special.polynomials import gegenbauer + assert _test_args(gegenbauer(x, 2, 2)) + + +def test_sympy__functions__special__polynomials__chebyshevt(): + from sympy.functions.special.polynomials import chebyshevt + assert _test_args(chebyshevt(x, 2)) + + +def test_sympy__functions__special__polynomials__chebyshevt_root(): + from sympy.functions.special.polynomials import chebyshevt_root + assert _test_args(chebyshevt_root(3, 2)) + + +def test_sympy__functions__special__polynomials__chebyshevu(): + from sympy.functions.special.polynomials import chebyshevu + assert _test_args(chebyshevu(x, 2)) + + +def test_sympy__functions__special__polynomials__chebyshevu_root(): + from sympy.functions.special.polynomials import chebyshevu_root + assert _test_args(chebyshevu_root(3, 2)) + + +def test_sympy__functions__special__polynomials__hermite(): + from sympy.functions.special.polynomials import hermite + assert _test_args(hermite(x, 2)) + + +def test_sympy__functions__special__polynomials__hermite_prob(): + from sympy.functions.special.polynomials import hermite_prob + assert _test_args(hermite_prob(x, 2)) + + +def test_sympy__functions__special__polynomials__legendre(): + from sympy.functions.special.polynomials import legendre + assert _test_args(legendre(x, 2)) + + +def test_sympy__functions__special__polynomials__assoc_legendre(): + from sympy.functions.special.polynomials import assoc_legendre + assert _test_args(assoc_legendre(x, 0, y)) + + +def test_sympy__functions__special__polynomials__laguerre(): + from sympy.functions.special.polynomials import laguerre + assert _test_args(laguerre(x, 2)) + + +def test_sympy__functions__special__polynomials__assoc_laguerre(): + from sympy.functions.special.polynomials import assoc_laguerre + assert _test_args(assoc_laguerre(x, 0, y)) + + +def test_sympy__functions__special__spherical_harmonics__Ynm(): + from sympy.functions.special.spherical_harmonics import Ynm + assert _test_args(Ynm(1, 1, x, y)) + + +def test_sympy__functions__special__spherical_harmonics__Znm(): + from sympy.functions.special.spherical_harmonics import Znm + assert _test_args(Znm(x, y, 1, 1)) + + +def test_sympy__functions__special__tensor_functions__LeviCivita(): + from sympy.functions.special.tensor_functions import LeviCivita + assert _test_args(LeviCivita(x, y, 2)) + + +def test_sympy__functions__special__tensor_functions__KroneckerDelta(): + from sympy.functions.special.tensor_functions import KroneckerDelta + assert _test_args(KroneckerDelta(x, y)) + + +def test_sympy__functions__special__zeta_functions__dirichlet_eta(): + from sympy.functions.special.zeta_functions import dirichlet_eta + assert _test_args(dirichlet_eta(x)) + + +def test_sympy__functions__special__zeta_functions__riemann_xi(): + from sympy.functions.special.zeta_functions import riemann_xi + assert _test_args(riemann_xi(x)) + + +def test_sympy__functions__special__zeta_functions__zeta(): + from sympy.functions.special.zeta_functions import zeta + assert _test_args(zeta(101)) + + +def test_sympy__functions__special__zeta_functions__lerchphi(): + from sympy.functions.special.zeta_functions import lerchphi + assert _test_args(lerchphi(x, y, z)) + + +def test_sympy__functions__special__zeta_functions__polylog(): + from sympy.functions.special.zeta_functions import polylog + assert _test_args(polylog(x, y)) + + +def test_sympy__functions__special__zeta_functions__stieltjes(): + from sympy.functions.special.zeta_functions import stieltjes + assert _test_args(stieltjes(x, y)) + + +def test_sympy__integrals__integrals__Integral(): + from sympy.integrals.integrals import Integral + assert _test_args(Integral(2, (x, 0, 1))) + + +def test_sympy__integrals__risch__NonElementaryIntegral(): + from sympy.integrals.risch import NonElementaryIntegral + assert _test_args(NonElementaryIntegral(exp(-x**2), x)) + + +@SKIP("abstract class") +def test_sympy__integrals__transforms__IntegralTransform(): + pass + + +def test_sympy__integrals__transforms__MellinTransform(): + from sympy.integrals.transforms import MellinTransform + assert _test_args(MellinTransform(2, x, y)) + + +def test_sympy__integrals__transforms__InverseMellinTransform(): + from sympy.integrals.transforms import InverseMellinTransform + assert _test_args(InverseMellinTransform(2, x, y, 0, 1)) + + +def test_sympy__integrals__laplace__LaplaceTransform(): + from sympy.integrals.laplace import LaplaceTransform + assert _test_args(LaplaceTransform(2, x, y)) + + +def test_sympy__integrals__laplace__InverseLaplaceTransform(): + from sympy.integrals.laplace import InverseLaplaceTransform + assert _test_args(InverseLaplaceTransform(2, x, y, 0)) + + +@SKIP("abstract class") +def test_sympy__integrals__transforms__FourierTypeTransform(): + pass + + +def test_sympy__integrals__transforms__InverseFourierTransform(): + from sympy.integrals.transforms import InverseFourierTransform + assert _test_args(InverseFourierTransform(2, x, y)) + + +def test_sympy__integrals__transforms__FourierTransform(): + from sympy.integrals.transforms import FourierTransform + assert _test_args(FourierTransform(2, x, y)) + + +@SKIP("abstract class") +def test_sympy__integrals__transforms__SineCosineTypeTransform(): + pass + + +def test_sympy__integrals__transforms__InverseSineTransform(): + from sympy.integrals.transforms import InverseSineTransform + assert _test_args(InverseSineTransform(2, x, y)) + + +def test_sympy__integrals__transforms__SineTransform(): + from sympy.integrals.transforms import SineTransform + assert _test_args(SineTransform(2, x, y)) + + +def test_sympy__integrals__transforms__InverseCosineTransform(): + from sympy.integrals.transforms import InverseCosineTransform + assert _test_args(InverseCosineTransform(2, x, y)) + + +def test_sympy__integrals__transforms__CosineTransform(): + from sympy.integrals.transforms import CosineTransform + assert _test_args(CosineTransform(2, x, y)) + + +@SKIP("abstract class") +def test_sympy__integrals__transforms__HankelTypeTransform(): + pass + + +def test_sympy__integrals__transforms__InverseHankelTransform(): + from sympy.integrals.transforms import InverseHankelTransform + assert _test_args(InverseHankelTransform(2, x, y, 0)) + + +def test_sympy__integrals__transforms__HankelTransform(): + from sympy.integrals.transforms import HankelTransform + assert _test_args(HankelTransform(2, x, y, 0)) + + +def test_sympy__liealgebras__cartan_type__Standard_Cartan(): + from sympy.liealgebras.cartan_type import Standard_Cartan + assert _test_args(Standard_Cartan("A", 2)) + +def test_sympy__liealgebras__weyl_group__WeylGroup(): + from sympy.liealgebras.weyl_group import WeylGroup + assert _test_args(WeylGroup("B4")) + +def test_sympy__liealgebras__root_system__RootSystem(): + from sympy.liealgebras.root_system import RootSystem + assert _test_args(RootSystem("A2")) + +def test_sympy__liealgebras__type_a__TypeA(): + from sympy.liealgebras.type_a import TypeA + assert _test_args(TypeA(2)) + +def test_sympy__liealgebras__type_b__TypeB(): + from sympy.liealgebras.type_b import TypeB + assert _test_args(TypeB(4)) + +def test_sympy__liealgebras__type_c__TypeC(): + from sympy.liealgebras.type_c import TypeC + assert _test_args(TypeC(4)) + +def test_sympy__liealgebras__type_d__TypeD(): + from sympy.liealgebras.type_d import TypeD + assert _test_args(TypeD(4)) + +def test_sympy__liealgebras__type_e__TypeE(): + from sympy.liealgebras.type_e import TypeE + assert _test_args(TypeE(6)) + +def test_sympy__liealgebras__type_f__TypeF(): + from sympy.liealgebras.type_f import TypeF + assert _test_args(TypeF(4)) + +def test_sympy__liealgebras__type_g__TypeG(): + from sympy.liealgebras.type_g import TypeG + assert _test_args(TypeG(2)) + + +def test_sympy__logic__boolalg__And(): + from sympy.logic.boolalg import And + assert _test_args(And(x, y, 1)) + + +@SKIP("abstract class") +def test_sympy__logic__boolalg__Boolean(): + pass + + +def test_sympy__logic__boolalg__BooleanFunction(): + from sympy.logic.boolalg import BooleanFunction + assert _test_args(BooleanFunction(1, 2, 3)) + +@SKIP("abstract class") +def test_sympy__logic__boolalg__BooleanAtom(): + pass + +def test_sympy__logic__boolalg__BooleanTrue(): + from sympy.logic.boolalg import true + assert _test_args(true) + +def test_sympy__logic__boolalg__BooleanFalse(): + from sympy.logic.boolalg import false + assert _test_args(false) + +def test_sympy__logic__boolalg__Equivalent(): + from sympy.logic.boolalg import Equivalent + assert _test_args(Equivalent(x, 2)) + + +def test_sympy__logic__boolalg__ITE(): + from sympy.logic.boolalg import ITE + assert _test_args(ITE(x, y, 1)) + + +def test_sympy__logic__boolalg__Implies(): + from sympy.logic.boolalg import Implies + assert _test_args(Implies(x, y)) + + +def test_sympy__logic__boolalg__Nand(): + from sympy.logic.boolalg import Nand + assert _test_args(Nand(x, y, 1)) + + +def test_sympy__logic__boolalg__Nor(): + from sympy.logic.boolalg import Nor + assert _test_args(Nor(x, y)) + + +def test_sympy__logic__boolalg__Not(): + from sympy.logic.boolalg import Not + assert _test_args(Not(x)) + + +def test_sympy__logic__boolalg__Or(): + from sympy.logic.boolalg import Or + assert _test_args(Or(x, y)) + + +def test_sympy__logic__boolalg__Xor(): + from sympy.logic.boolalg import Xor + assert _test_args(Xor(x, y, 2)) + +def test_sympy__logic__boolalg__Xnor(): + from sympy.logic.boolalg import Xnor + assert _test_args(Xnor(x, y, 2)) + +def test_sympy__logic__boolalg__Exclusive(): + from sympy.logic.boolalg import Exclusive + assert _test_args(Exclusive(x, y, z)) + + +def test_sympy__matrices__matrixbase__DeferredVector(): + from sympy.matrices.matrixbase import DeferredVector + assert _test_args(DeferredVector("X")) + + +@SKIP("abstract class") +def test_sympy__matrices__expressions__matexpr__MatrixBase(): + pass + + +@SKIP("abstract class") +def test_sympy__matrices__immutable__ImmutableRepMatrix(): + pass + + +def test_sympy__matrices__immutable__ImmutableDenseMatrix(): + from sympy.matrices.immutable import ImmutableDenseMatrix + m = ImmutableDenseMatrix([[1, 2], [3, 4]]) + assert _test_args(m) + assert _test_args(Basic(*list(m))) + m = ImmutableDenseMatrix(1, 1, [1]) + assert _test_args(m) + assert _test_args(Basic(*list(m))) + m = ImmutableDenseMatrix(2, 2, lambda i, j: 1) + assert m[0, 0] is S.One + m = ImmutableDenseMatrix(2, 2, lambda i, j: 1/(1 + i) + 1/(1 + j)) + assert m[1, 1] is S.One # true div. will give 1.0 if i,j not sympified + assert _test_args(m) + assert _test_args(Basic(*list(m))) + + +def test_sympy__matrices__immutable__ImmutableSparseMatrix(): + from sympy.matrices.immutable import ImmutableSparseMatrix + m = ImmutableSparseMatrix([[1, 2], [3, 4]]) + assert _test_args(m) + assert _test_args(Basic(*list(m))) + m = ImmutableSparseMatrix(1, 1, {(0, 0): 1}) + assert _test_args(m) + assert _test_args(Basic(*list(m))) + m = ImmutableSparseMatrix(1, 1, [1]) + assert _test_args(m) + assert _test_args(Basic(*list(m))) + m = ImmutableSparseMatrix(2, 2, lambda i, j: 1) + assert m[0, 0] is S.One + m = ImmutableSparseMatrix(2, 2, lambda i, j: 1/(1 + i) + 1/(1 + j)) + assert m[1, 1] is S.One # true div. will give 1.0 if i,j not sympified + assert _test_args(m) + assert _test_args(Basic(*list(m))) + + +def test_sympy__matrices__expressions__slice__MatrixSlice(): + from sympy.matrices.expressions.slice import MatrixSlice + from sympy.matrices.expressions import MatrixSymbol + X = MatrixSymbol('X', 4, 4) + assert _test_args(MatrixSlice(X, (0, 2), (0, 2))) + + +def test_sympy__matrices__expressions__applyfunc__ElementwiseApplyFunction(): + from sympy.matrices.expressions.applyfunc import ElementwiseApplyFunction + from sympy.matrices.expressions import MatrixSymbol + X = MatrixSymbol("X", x, x) + func = Lambda(x, x**2) + assert _test_args(ElementwiseApplyFunction(func, X)) + + +def test_sympy__matrices__expressions__blockmatrix__BlockDiagMatrix(): + from sympy.matrices.expressions.blockmatrix import BlockDiagMatrix + from sympy.matrices.expressions import MatrixSymbol + X = MatrixSymbol('X', x, x) + Y = MatrixSymbol('Y', y, y) + assert _test_args(BlockDiagMatrix(X, Y)) + + +def test_sympy__matrices__expressions__blockmatrix__BlockMatrix(): + from sympy.matrices.expressions.blockmatrix import BlockMatrix + from sympy.matrices.expressions import MatrixSymbol, ZeroMatrix + X = MatrixSymbol('X', x, x) + Y = MatrixSymbol('Y', y, y) + Z = MatrixSymbol('Z', x, y) + O = ZeroMatrix(y, x) + assert _test_args(BlockMatrix([[X, Z], [O, Y]])) + + +def test_sympy__matrices__expressions__inverse__Inverse(): + from sympy.matrices.expressions.inverse import Inverse + from sympy.matrices.expressions import MatrixSymbol + assert _test_args(Inverse(MatrixSymbol('A', 3, 3))) + + +def test_sympy__matrices__expressions__matadd__MatAdd(): + from sympy.matrices.expressions.matadd import MatAdd + from sympy.matrices.expressions import MatrixSymbol + X = MatrixSymbol('X', x, y) + Y = MatrixSymbol('Y', x, y) + assert _test_args(MatAdd(X, Y)) + + +@SKIP("abstract class") +def test_sympy__matrices__expressions__matexpr__MatrixExpr(): + pass + +def test_sympy__matrices__expressions__matexpr__MatrixElement(): + from sympy.matrices.expressions.matexpr import MatrixSymbol, MatrixElement + from sympy.core.singleton import S + assert _test_args(MatrixElement(MatrixSymbol('A', 3, 5), S(2), S(3))) + +def test_sympy__matrices__expressions__matexpr__MatrixSymbol(): + from sympy.matrices.expressions.matexpr import MatrixSymbol + assert _test_args(MatrixSymbol('A', 3, 5)) + + +def test_sympy__matrices__expressions__special__OneMatrix(): + from sympy.matrices.expressions.special import OneMatrix + assert _test_args(OneMatrix(3, 5)) + + +def test_sympy__matrices__expressions__special__ZeroMatrix(): + from sympy.matrices.expressions.special import ZeroMatrix + assert _test_args(ZeroMatrix(3, 5)) + + +def test_sympy__matrices__expressions__special__GenericZeroMatrix(): + from sympy.matrices.expressions.special import GenericZeroMatrix + assert _test_args(GenericZeroMatrix()) + + +def test_sympy__matrices__expressions__special__Identity(): + from sympy.matrices.expressions.special import Identity + assert _test_args(Identity(3)) + + +def test_sympy__matrices__expressions__special__GenericIdentity(): + from sympy.matrices.expressions.special import GenericIdentity + assert _test_args(GenericIdentity()) + + +def test_sympy__matrices__expressions__sets__MatrixSet(): + from sympy.matrices.expressions.sets import MatrixSet + from sympy.core.singleton import S + assert _test_args(MatrixSet(2, 2, S.Reals)) + +def test_sympy__matrices__expressions__matmul__MatMul(): + from sympy.matrices.expressions.matmul import MatMul + from sympy.matrices.expressions import MatrixSymbol + X = MatrixSymbol('X', x, y) + Y = MatrixSymbol('Y', y, x) + assert _test_args(MatMul(X, Y)) + + +def test_sympy__matrices__expressions__dotproduct__DotProduct(): + from sympy.matrices.expressions.dotproduct import DotProduct + from sympy.matrices.expressions import MatrixSymbol + X = MatrixSymbol('X', x, 1) + Y = MatrixSymbol('Y', x, 1) + assert _test_args(DotProduct(X, Y)) + +def test_sympy__matrices__expressions__diagonal__DiagonalMatrix(): + from sympy.matrices.expressions.diagonal import DiagonalMatrix + from sympy.matrices.expressions import MatrixSymbol + x = MatrixSymbol('x', 10, 1) + assert _test_args(DiagonalMatrix(x)) + +def test_sympy__matrices__expressions__diagonal__DiagonalOf(): + from sympy.matrices.expressions.diagonal import DiagonalOf + from sympy.matrices.expressions import MatrixSymbol + X = MatrixSymbol('x', 10, 10) + assert _test_args(DiagonalOf(X)) + +def test_sympy__matrices__expressions__diagonal__DiagMatrix(): + from sympy.matrices.expressions.diagonal import DiagMatrix + from sympy.matrices.expressions import MatrixSymbol + x = MatrixSymbol('x', 10, 1) + assert _test_args(DiagMatrix(x)) + +def test_sympy__matrices__expressions__hadamard__HadamardProduct(): + from sympy.matrices.expressions.hadamard import HadamardProduct + from sympy.matrices.expressions import MatrixSymbol + X = MatrixSymbol('X', x, y) + Y = MatrixSymbol('Y', x, y) + assert _test_args(HadamardProduct(X, Y)) + +def test_sympy__matrices__expressions__hadamard__HadamardPower(): + from sympy.matrices.expressions.hadamard import HadamardPower + from sympy.matrices.expressions import MatrixSymbol + from sympy.core.symbol import Symbol + X = MatrixSymbol('X', x, y) + n = Symbol("n") + assert _test_args(HadamardPower(X, n)) + +def test_sympy__matrices__expressions__kronecker__KroneckerProduct(): + from sympy.matrices.expressions.kronecker import KroneckerProduct + from sympy.matrices.expressions import MatrixSymbol + X = MatrixSymbol('X', x, y) + Y = MatrixSymbol('Y', x, y) + assert _test_args(KroneckerProduct(X, Y)) + + +def test_sympy__matrices__expressions__matpow__MatPow(): + from sympy.matrices.expressions.matpow import MatPow + from sympy.matrices.expressions import MatrixSymbol + X = MatrixSymbol('X', x, x) + assert _test_args(MatPow(X, 2)) + + +def test_sympy__matrices__expressions__transpose__Transpose(): + from sympy.matrices.expressions.transpose import Transpose + from sympy.matrices.expressions import MatrixSymbol + assert _test_args(Transpose(MatrixSymbol('A', 3, 5))) + + +def test_sympy__matrices__expressions__adjoint__Adjoint(): + from sympy.matrices.expressions.adjoint import Adjoint + from sympy.matrices.expressions import MatrixSymbol + assert _test_args(Adjoint(MatrixSymbol('A', 3, 5))) + + +def test_sympy__matrices__expressions__trace__Trace(): + from sympy.matrices.expressions.trace import Trace + from sympy.matrices.expressions import MatrixSymbol + assert _test_args(Trace(MatrixSymbol('A', 3, 3))) + +def test_sympy__matrices__expressions__determinant__Determinant(): + from sympy.matrices.expressions.determinant import Determinant + from sympy.matrices.expressions import MatrixSymbol + assert _test_args(Determinant(MatrixSymbol('A', 3, 3))) + +def test_sympy__matrices__expressions__determinant__Permanent(): + from sympy.matrices.expressions.determinant import Permanent + from sympy.matrices.expressions import MatrixSymbol + assert _test_args(Permanent(MatrixSymbol('A', 3, 4))) + +def test_sympy__matrices__expressions__funcmatrix__FunctionMatrix(): + from sympy.matrices.expressions.funcmatrix import FunctionMatrix + from sympy.core.symbol import symbols + i, j = symbols('i,j') + assert _test_args(FunctionMatrix(3, 3, Lambda((i, j), i - j) )) + +def test_sympy__matrices__expressions__fourier__DFT(): + from sympy.matrices.expressions.fourier import DFT + from sympy.core.singleton import S + assert _test_args(DFT(S(2))) + +def test_sympy__matrices__expressions__fourier__IDFT(): + from sympy.matrices.expressions.fourier import IDFT + from sympy.core.singleton import S + assert _test_args(IDFT(S(2))) + +from sympy.matrices.expressions import MatrixSymbol +X = MatrixSymbol('X', 10, 10) + +def test_sympy__matrices__expressions__factorizations__LofLU(): + from sympy.matrices.expressions.factorizations import LofLU + assert _test_args(LofLU(X)) + +def test_sympy__matrices__expressions__factorizations__UofLU(): + from sympy.matrices.expressions.factorizations import UofLU + assert _test_args(UofLU(X)) + +def test_sympy__matrices__expressions__factorizations__QofQR(): + from sympy.matrices.expressions.factorizations import QofQR + assert _test_args(QofQR(X)) + +def test_sympy__matrices__expressions__factorizations__RofQR(): + from sympy.matrices.expressions.factorizations import RofQR + assert _test_args(RofQR(X)) + +def test_sympy__matrices__expressions__factorizations__LofCholesky(): + from sympy.matrices.expressions.factorizations import LofCholesky + assert _test_args(LofCholesky(X)) + +def test_sympy__matrices__expressions__factorizations__UofCholesky(): + from sympy.matrices.expressions.factorizations import UofCholesky + assert _test_args(UofCholesky(X)) + +def test_sympy__matrices__expressions__factorizations__EigenVectors(): + from sympy.matrices.expressions.factorizations import EigenVectors + assert _test_args(EigenVectors(X)) + +def test_sympy__matrices__expressions__factorizations__EigenValues(): + from sympy.matrices.expressions.factorizations import EigenValues + assert _test_args(EigenValues(X)) + +def test_sympy__matrices__expressions__factorizations__UofSVD(): + from sympy.matrices.expressions.factorizations import UofSVD + assert _test_args(UofSVD(X)) + +def test_sympy__matrices__expressions__factorizations__VofSVD(): + from sympy.matrices.expressions.factorizations import VofSVD + assert _test_args(VofSVD(X)) + +def test_sympy__matrices__expressions__factorizations__SofSVD(): + from sympy.matrices.expressions.factorizations import SofSVD + assert _test_args(SofSVD(X)) + +@SKIP("abstract class") +def test_sympy__matrices__expressions__factorizations__Factorization(): + pass + +def test_sympy__matrices__expressions__permutation__PermutationMatrix(): + from sympy.combinatorics import Permutation + from sympy.matrices.expressions.permutation import PermutationMatrix + assert _test_args(PermutationMatrix(Permutation([2, 0, 1]))) + +def test_sympy__matrices__expressions__permutation__MatrixPermute(): + from sympy.combinatorics import Permutation + from sympy.matrices.expressions.matexpr import MatrixSymbol + from sympy.matrices.expressions.permutation import MatrixPermute + A = MatrixSymbol('A', 3, 3) + assert _test_args(MatrixPermute(A, Permutation([2, 0, 1]))) + +def test_sympy__matrices__expressions__companion__CompanionMatrix(): + from sympy.core.symbol import Symbol + from sympy.matrices.expressions.companion import CompanionMatrix + from sympy.polys.polytools import Poly + + x = Symbol('x') + p = Poly([1, 2, 3], x) + assert _test_args(CompanionMatrix(p)) + +def test_sympy__physics__vector__frame__CoordinateSym(): + from sympy.physics.vector import CoordinateSym + from sympy.physics.vector import ReferenceFrame + assert _test_args(CoordinateSym('R_x', ReferenceFrame('R'), 0)) + + +@SKIP("abstract class") +def test_sympy__physics__biomechanics__curve__CharacteristicCurveFunction(): + pass + + +def test_sympy__physics__biomechanics__curve__TendonForceLengthDeGroote2016(): + from sympy.physics.biomechanics import TendonForceLengthDeGroote2016 + l_T_tilde, c0, c1, c2, c3 = symbols('l_T_tilde, c0, c1, c2, c3') + assert _test_args(TendonForceLengthDeGroote2016(l_T_tilde, c0, c1, c2, c3)) + + +def test_sympy__physics__biomechanics__curve__TendonForceLengthInverseDeGroote2016(): + from sympy.physics.biomechanics import TendonForceLengthInverseDeGroote2016 + fl_T, c0, c1, c2, c3 = symbols('fl_T, c0, c1, c2, c3') + assert _test_args(TendonForceLengthInverseDeGroote2016(fl_T, c0, c1, c2, c3)) + + +def test_sympy__physics__biomechanics__curve__FiberForceLengthPassiveDeGroote2016(): + from sympy.physics.biomechanics import FiberForceLengthPassiveDeGroote2016 + l_M_tilde, c0, c1 = symbols('l_M_tilde, c0, c1') + assert _test_args(FiberForceLengthPassiveDeGroote2016(l_M_tilde, c0, c1)) + + +def test_sympy__physics__biomechanics__curve__FiberForceLengthPassiveInverseDeGroote2016(): + from sympy.physics.biomechanics import FiberForceLengthPassiveInverseDeGroote2016 + fl_M_pas, c0, c1 = symbols('fl_M_pas, c0, c1') + assert _test_args(FiberForceLengthPassiveInverseDeGroote2016(fl_M_pas, c0, c1)) + + +def test_sympy__physics__biomechanics__curve__FiberForceLengthActiveDeGroote2016(): + from sympy.physics.biomechanics import FiberForceLengthActiveDeGroote2016 + l_M_tilde, c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11 = symbols('l_M_tilde, c0:12') + assert _test_args(FiberForceLengthActiveDeGroote2016(l_M_tilde, c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11)) + + +def test_sympy__physics__biomechanics__curve__FiberForceVelocityDeGroote2016(): + from sympy.physics.biomechanics import FiberForceVelocityDeGroote2016 + v_M_tilde, c0, c1, c2, c3 = symbols('v_M_tilde, c0, c1, c2, c3') + assert _test_args(FiberForceVelocityDeGroote2016(v_M_tilde, c0, c1, c2, c3)) + + +def test_sympy__physics__biomechanics__curve__FiberForceVelocityInverseDeGroote2016(): + from sympy.physics.biomechanics import FiberForceVelocityInverseDeGroote2016 + fv_M, c0, c1, c2, c3 = symbols('fv_M, c0, c1, c2, c3') + assert _test_args(FiberForceVelocityInverseDeGroote2016(fv_M, c0, c1, c2, c3)) + + +def test_sympy__physics__paulialgebra__Pauli(): + from sympy.physics.paulialgebra import Pauli + assert _test_args(Pauli(1)) + + +def test_sympy__physics__quantum__anticommutator__AntiCommutator(): + from sympy.physics.quantum.anticommutator import AntiCommutator + assert _test_args(AntiCommutator(x, y)) + + +def test_sympy__physics__quantum__cartesian__PositionBra3D(): + from sympy.physics.quantum.cartesian import PositionBra3D + assert _test_args(PositionBra3D(x, y, z)) + + +def test_sympy__physics__quantum__cartesian__PositionKet3D(): + from sympy.physics.quantum.cartesian import PositionKet3D + assert _test_args(PositionKet3D(x, y, z)) + + +def test_sympy__physics__quantum__cartesian__PositionState3D(): + from sympy.physics.quantum.cartesian import PositionState3D + assert _test_args(PositionState3D(x, y, z)) + + +def test_sympy__physics__quantum__cartesian__PxBra(): + from sympy.physics.quantum.cartesian import PxBra + assert _test_args(PxBra(x, y, z)) + + +def test_sympy__physics__quantum__cartesian__PxKet(): + from sympy.physics.quantum.cartesian import PxKet + assert _test_args(PxKet(x, y, z)) + + +def test_sympy__physics__quantum__cartesian__PxOp(): + from sympy.physics.quantum.cartesian import PxOp + assert _test_args(PxOp(x, y, z)) + + +def test_sympy__physics__quantum__cartesian__XBra(): + from sympy.physics.quantum.cartesian import XBra + assert _test_args(XBra(x)) + + +def test_sympy__physics__quantum__cartesian__XKet(): + from sympy.physics.quantum.cartesian import XKet + assert _test_args(XKet(x)) + + +def test_sympy__physics__quantum__cartesian__XOp(): + from sympy.physics.quantum.cartesian import XOp + assert _test_args(XOp(x)) + + +def test_sympy__physics__quantum__cartesian__YOp(): + from sympy.physics.quantum.cartesian import YOp + assert _test_args(YOp(x)) + + +def test_sympy__physics__quantum__cartesian__ZOp(): + from sympy.physics.quantum.cartesian import ZOp + assert _test_args(ZOp(x)) + + +def test_sympy__physics__quantum__cg__CG(): + from sympy.physics.quantum.cg import CG + from sympy.core.singleton import S + assert _test_args(CG(Rational(3, 2), Rational(3, 2), S.Half, Rational(-1, 2), 1, 1)) + + +def test_sympy__physics__quantum__cg__Wigner3j(): + from sympy.physics.quantum.cg import Wigner3j + assert _test_args(Wigner3j(6, 0, 4, 0, 2, 0)) + + +def test_sympy__physics__quantum__cg__Wigner6j(): + from sympy.physics.quantum.cg import Wigner6j + assert _test_args(Wigner6j(1, 2, 3, 2, 1, 2)) + + +def test_sympy__physics__quantum__cg__Wigner9j(): + from sympy.physics.quantum.cg import Wigner9j + assert _test_args(Wigner9j(2, 1, 1, Rational(3, 2), S.Half, 1, S.Half, S.Half, 0)) + +def test_sympy__physics__quantum__circuitplot__Mz(): + from sympy.physics.quantum.circuitplot import Mz + assert _test_args(Mz(0)) + +def test_sympy__physics__quantum__circuitplot__Mx(): + from sympy.physics.quantum.circuitplot import Mx + assert _test_args(Mx(0)) + +def test_sympy__physics__quantum__commutator__Commutator(): + from sympy.physics.quantum.commutator import Commutator + A, B = symbols('A,B', commutative=False) + assert _test_args(Commutator(A, B)) + + +def test_sympy__physics__quantum__constants__HBar(): + from sympy.physics.quantum.constants import HBar + assert _test_args(HBar()) + + +def test_sympy__physics__quantum__dagger__Dagger(): + from sympy.physics.quantum.dagger import Dagger + from sympy.physics.quantum.state import Ket + assert _test_args(Dagger(Dagger(Ket('psi')))) + + +def test_sympy__physics__quantum__gate__CGate(): + from sympy.physics.quantum.gate import CGate, Gate + assert _test_args(CGate((0, 1), Gate(2))) + + +def test_sympy__physics__quantum__gate__CGateS(): + from sympy.physics.quantum.gate import CGateS, Gate + assert _test_args(CGateS((0, 1), Gate(2))) + + +def test_sympy__physics__quantum__gate__CNotGate(): + from sympy.physics.quantum.gate import CNotGate + assert _test_args(CNotGate(0, 1)) + + +def test_sympy__physics__quantum__gate__Gate(): + from sympy.physics.quantum.gate import Gate + assert _test_args(Gate(0)) + + +def test_sympy__physics__quantum__gate__HadamardGate(): + from sympy.physics.quantum.gate import HadamardGate + assert _test_args(HadamardGate(0)) + + +def test_sympy__physics__quantum__gate__IdentityGate(): + from sympy.physics.quantum.gate import IdentityGate + assert _test_args(IdentityGate(0)) + + +def test_sympy__physics__quantum__gate__OneQubitGate(): + from sympy.physics.quantum.gate import OneQubitGate + assert _test_args(OneQubitGate(0)) + + +def test_sympy__physics__quantum__gate__PhaseGate(): + from sympy.physics.quantum.gate import PhaseGate + assert _test_args(PhaseGate(0)) + + +def test_sympy__physics__quantum__gate__SwapGate(): + from sympy.physics.quantum.gate import SwapGate + assert _test_args(SwapGate(0, 1)) + + +def test_sympy__physics__quantum__gate__TGate(): + from sympy.physics.quantum.gate import TGate + assert _test_args(TGate(0)) + + +def test_sympy__physics__quantum__gate__TwoQubitGate(): + from sympy.physics.quantum.gate import TwoQubitGate + assert _test_args(TwoQubitGate(0)) + + +def test_sympy__physics__quantum__gate__UGate(): + from sympy.physics.quantum.gate import UGate + from sympy.matrices.immutable import ImmutableDenseMatrix + from sympy.core.containers import Tuple + from sympy.core.numbers import Integer + assert _test_args( + UGate(Tuple(Integer(1)), ImmutableDenseMatrix([[1, 0], [0, 2]]))) + + +def test_sympy__physics__quantum__gate__XGate(): + from sympy.physics.quantum.gate import XGate + assert _test_args(XGate(0)) + + +def test_sympy__physics__quantum__gate__YGate(): + from sympy.physics.quantum.gate import YGate + assert _test_args(YGate(0)) + + +def test_sympy__physics__quantum__gate__ZGate(): + from sympy.physics.quantum.gate import ZGate + assert _test_args(ZGate(0)) + + +def test_sympy__physics__quantum__grover__OracleGateFunction(): + from sympy.physics.quantum.grover import OracleGateFunction + @OracleGateFunction + def f(qubit): + return + assert _test_args(f) + +def test_sympy__physics__quantum__grover__OracleGate(): + from sympy.physics.quantum.grover import OracleGate + def f(qubit): + return + assert _test_args(OracleGate(1,f)) + + +def test_sympy__physics__quantum__grover__WGate(): + from sympy.physics.quantum.grover import WGate + assert _test_args(WGate(1)) + + +def test_sympy__physics__quantum__hilbert__ComplexSpace(): + from sympy.physics.quantum.hilbert import ComplexSpace + assert _test_args(ComplexSpace(x)) + + +def test_sympy__physics__quantum__hilbert__DirectSumHilbertSpace(): + from sympy.physics.quantum.hilbert import DirectSumHilbertSpace, ComplexSpace, FockSpace + c = ComplexSpace(2) + f = FockSpace() + assert _test_args(DirectSumHilbertSpace(c, f)) + + +def test_sympy__physics__quantum__hilbert__FockSpace(): + from sympy.physics.quantum.hilbert import FockSpace + assert _test_args(FockSpace()) + + +def test_sympy__physics__quantum__hilbert__HilbertSpace(): + from sympy.physics.quantum.hilbert import HilbertSpace + assert _test_args(HilbertSpace()) + + +def test_sympy__physics__quantum__hilbert__L2(): + from sympy.physics.quantum.hilbert import L2 + from sympy.core.numbers import oo + from sympy.sets.sets import Interval + assert _test_args(L2(Interval(0, oo))) + + +def test_sympy__physics__quantum__hilbert__TensorPowerHilbertSpace(): + from sympy.physics.quantum.hilbert import TensorPowerHilbertSpace, FockSpace + f = FockSpace() + assert _test_args(TensorPowerHilbertSpace(f, 2)) + + +def test_sympy__physics__quantum__hilbert__TensorProductHilbertSpace(): + from sympy.physics.quantum.hilbert import TensorProductHilbertSpace, FockSpace, ComplexSpace + c = ComplexSpace(2) + f = FockSpace() + assert _test_args(TensorProductHilbertSpace(f, c)) + + +def test_sympy__physics__quantum__innerproduct__InnerProduct(): + from sympy.physics.quantum import Bra, Ket, InnerProduct + b = Bra('b') + k = Ket('k') + assert _test_args(InnerProduct(b, k)) + + +def test_sympy__physics__quantum__operator__DifferentialOperator(): + from sympy.physics.quantum.operator import DifferentialOperator + from sympy.core.function import (Derivative, Function) + f = Function('f') + assert _test_args(DifferentialOperator(1/x*Derivative(f(x), x), f(x))) + + +def test_sympy__physics__quantum__operator__HermitianOperator(): + from sympy.physics.quantum.operator import HermitianOperator + assert _test_args(HermitianOperator('H')) + + +def test_sympy__physics__quantum__operator__IdentityOperator(): + from sympy.physics.quantum.operator import IdentityOperator + assert _test_args(IdentityOperator(5)) + + +def test_sympy__physics__quantum__operator__Operator(): + from sympy.physics.quantum.operator import Operator + assert _test_args(Operator('A')) + + +def test_sympy__physics__quantum__operator__OuterProduct(): + from sympy.physics.quantum.operator import OuterProduct + from sympy.physics.quantum import Ket, Bra + b = Bra('b') + k = Ket('k') + assert _test_args(OuterProduct(k, b)) + + +def test_sympy__physics__quantum__operator__UnitaryOperator(): + from sympy.physics.quantum.operator import UnitaryOperator + assert _test_args(UnitaryOperator('U')) + + +def test_sympy__physics__quantum__piab__PIABBra(): + from sympy.physics.quantum.piab import PIABBra + assert _test_args(PIABBra('B')) + + +def test_sympy__physics__quantum__boson__BosonOp(): + from sympy.physics.quantum.boson import BosonOp + assert _test_args(BosonOp('a')) + assert _test_args(BosonOp('a', False)) + + +def test_sympy__physics__quantum__boson__BosonFockKet(): + from sympy.physics.quantum.boson import BosonFockKet + assert _test_args(BosonFockKet(1)) + + +def test_sympy__physics__quantum__boson__BosonFockBra(): + from sympy.physics.quantum.boson import BosonFockBra + assert _test_args(BosonFockBra(1)) + + +def test_sympy__physics__quantum__boson__BosonCoherentKet(): + from sympy.physics.quantum.boson import BosonCoherentKet + assert _test_args(BosonCoherentKet(1)) + + +def test_sympy__physics__quantum__boson__BosonCoherentBra(): + from sympy.physics.quantum.boson import BosonCoherentBra + assert _test_args(BosonCoherentBra(1)) + + +def test_sympy__physics__quantum__fermion__FermionOp(): + from sympy.physics.quantum.fermion import FermionOp + assert _test_args(FermionOp('c')) + assert _test_args(FermionOp('c', False)) + + +def test_sympy__physics__quantum__fermion__FermionFockKet(): + from sympy.physics.quantum.fermion import FermionFockKet + assert _test_args(FermionFockKet(1)) + + +def test_sympy__physics__quantum__fermion__FermionFockBra(): + from sympy.physics.quantum.fermion import FermionFockBra + assert _test_args(FermionFockBra(1)) + + +def test_sympy__physics__quantum__pauli__SigmaOpBase(): + from sympy.physics.quantum.pauli import SigmaOpBase + assert _test_args(SigmaOpBase()) + + +def test_sympy__physics__quantum__pauli__SigmaX(): + from sympy.physics.quantum.pauli import SigmaX + assert _test_args(SigmaX()) + + +def test_sympy__physics__quantum__pauli__SigmaY(): + from sympy.physics.quantum.pauli import SigmaY + assert _test_args(SigmaY()) + + +def test_sympy__physics__quantum__pauli__SigmaZ(): + from sympy.physics.quantum.pauli import SigmaZ + assert _test_args(SigmaZ()) + + +def test_sympy__physics__quantum__pauli__SigmaMinus(): + from sympy.physics.quantum.pauli import SigmaMinus + assert _test_args(SigmaMinus()) + + +def test_sympy__physics__quantum__pauli__SigmaPlus(): + from sympy.physics.quantum.pauli import SigmaPlus + assert _test_args(SigmaPlus()) + + +def test_sympy__physics__quantum__pauli__SigmaZKet(): + from sympy.physics.quantum.pauli import SigmaZKet + assert _test_args(SigmaZKet(0)) + + +def test_sympy__physics__quantum__pauli__SigmaZBra(): + from sympy.physics.quantum.pauli import SigmaZBra + assert _test_args(SigmaZBra(0)) + + +def test_sympy__physics__quantum__piab__PIABHamiltonian(): + from sympy.physics.quantum.piab import PIABHamiltonian + assert _test_args(PIABHamiltonian('P')) + + +def test_sympy__physics__quantum__piab__PIABKet(): + from sympy.physics.quantum.piab import PIABKet + assert _test_args(PIABKet('K')) + + +def test_sympy__physics__quantum__qexpr__QExpr(): + from sympy.physics.quantum.qexpr import QExpr + assert _test_args(QExpr(0)) + + +def test_sympy__physics__quantum__qft__Fourier(): + from sympy.physics.quantum.qft import Fourier + assert _test_args(Fourier(0, 1)) + + +def test_sympy__physics__quantum__qft__IQFT(): + from sympy.physics.quantum.qft import IQFT + assert _test_args(IQFT(0, 1)) + + +def test_sympy__physics__quantum__qft__QFT(): + from sympy.physics.quantum.qft import QFT + assert _test_args(QFT(0, 1)) + + +def test_sympy__physics__quantum__qft__RkGate(): + from sympy.physics.quantum.qft import RkGate + assert _test_args(RkGate(0, 1)) + + +def test_sympy__physics__quantum__qubit__IntQubit(): + from sympy.physics.quantum.qubit import IntQubit + assert _test_args(IntQubit(0)) + + +def test_sympy__physics__quantum__qubit__IntQubitBra(): + from sympy.physics.quantum.qubit import IntQubitBra + assert _test_args(IntQubitBra(0)) + + +def test_sympy__physics__quantum__qubit__IntQubitState(): + from sympy.physics.quantum.qubit import IntQubitState, QubitState + assert _test_args(IntQubitState(QubitState(0, 1))) + + +def test_sympy__physics__quantum__qubit__Qubit(): + from sympy.physics.quantum.qubit import Qubit + assert _test_args(Qubit(0, 0, 0)) + + +def test_sympy__physics__quantum__qubit__QubitBra(): + from sympy.physics.quantum.qubit import QubitBra + assert _test_args(QubitBra('1', 0)) + + +def test_sympy__physics__quantum__qubit__QubitState(): + from sympy.physics.quantum.qubit import QubitState + assert _test_args(QubitState(0, 1)) + + +def test_sympy__physics__quantum__density__Density(): + from sympy.physics.quantum.density import Density + from sympy.physics.quantum.state import Ket + assert _test_args(Density([Ket(0), 0.5], [Ket(1), 0.5])) + + +@SKIP("TODO: sympy.physics.quantum.shor: Cmod Not Implemented") +def test_sympy__physics__quantum__shor__CMod(): + from sympy.physics.quantum.shor import CMod + assert _test_args(CMod()) + + +def test_sympy__physics__quantum__spin__CoupledSpinState(): + from sympy.physics.quantum.spin import CoupledSpinState + assert _test_args(CoupledSpinState(1, 0, (1, 1))) + assert _test_args(CoupledSpinState(1, 0, (1, S.Half, S.Half))) + assert _test_args(CoupledSpinState( + 1, 0, (1, S.Half, S.Half), ((2, 3, S.Half), (1, 2, 1)) )) + j, m, j1, j2, j3, j12, x = symbols('j m j1:4 j12 x') + assert CoupledSpinState( + j, m, (j1, j2, j3)).subs(j2, x) == CoupledSpinState(j, m, (j1, x, j3)) + assert CoupledSpinState(j, m, (j1, j2, j3), ((1, 3, j12), (1, 2, j)) ).subs(j12, x) == \ + CoupledSpinState(j, m, (j1, j2, j3), ((1, 3, x), (1, 2, j)) ) + + +def test_sympy__physics__quantum__spin__J2Op(): + from sympy.physics.quantum.spin import J2Op + assert _test_args(J2Op('J')) + + +def test_sympy__physics__quantum__spin__JminusOp(): + from sympy.physics.quantum.spin import JminusOp + assert _test_args(JminusOp('J')) + + +def test_sympy__physics__quantum__spin__JplusOp(): + from sympy.physics.quantum.spin import JplusOp + assert _test_args(JplusOp('J')) + + +def test_sympy__physics__quantum__spin__JxBra(): + from sympy.physics.quantum.spin import JxBra + assert _test_args(JxBra(1, 0)) + + +def test_sympy__physics__quantum__spin__JxBraCoupled(): + from sympy.physics.quantum.spin import JxBraCoupled + assert _test_args(JxBraCoupled(1, 0, (1, 1))) + + +def test_sympy__physics__quantum__spin__JxKet(): + from sympy.physics.quantum.spin import JxKet + assert _test_args(JxKet(1, 0)) + + +def test_sympy__physics__quantum__spin__JxKetCoupled(): + from sympy.physics.quantum.spin import JxKetCoupled + assert _test_args(JxKetCoupled(1, 0, (1, 1))) + + +def test_sympy__physics__quantum__spin__JxOp(): + from sympy.physics.quantum.spin import JxOp + assert _test_args(JxOp('J')) + + +def test_sympy__physics__quantum__spin__JyBra(): + from sympy.physics.quantum.spin import JyBra + assert _test_args(JyBra(1, 0)) + + +def test_sympy__physics__quantum__spin__JyBraCoupled(): + from sympy.physics.quantum.spin import JyBraCoupled + assert _test_args(JyBraCoupled(1, 0, (1, 1))) + + +def test_sympy__physics__quantum__spin__JyKet(): + from sympy.physics.quantum.spin import JyKet + assert _test_args(JyKet(1, 0)) + + +def test_sympy__physics__quantum__spin__JyKetCoupled(): + from sympy.physics.quantum.spin import JyKetCoupled + assert _test_args(JyKetCoupled(1, 0, (1, 1))) + + +def test_sympy__physics__quantum__spin__JyOp(): + from sympy.physics.quantum.spin import JyOp + assert _test_args(JyOp('J')) + + +def test_sympy__physics__quantum__spin__JzBra(): + from sympy.physics.quantum.spin import JzBra + assert _test_args(JzBra(1, 0)) + + +def test_sympy__physics__quantum__spin__JzBraCoupled(): + from sympy.physics.quantum.spin import JzBraCoupled + assert _test_args(JzBraCoupled(1, 0, (1, 1))) + + +def test_sympy__physics__quantum__spin__JzKet(): + from sympy.physics.quantum.spin import JzKet + assert _test_args(JzKet(1, 0)) + + +def test_sympy__physics__quantum__spin__JzKetCoupled(): + from sympy.physics.quantum.spin import JzKetCoupled + assert _test_args(JzKetCoupled(1, 0, (1, 1))) + + +def test_sympy__physics__quantum__spin__JzOp(): + from sympy.physics.quantum.spin import JzOp + assert _test_args(JzOp('J')) + + +def test_sympy__physics__quantum__spin__Rotation(): + from sympy.physics.quantum.spin import Rotation + assert _test_args(Rotation(pi, 0, pi/2)) + + +def test_sympy__physics__quantum__spin__SpinState(): + from sympy.physics.quantum.spin import SpinState + assert _test_args(SpinState(1, 0)) + + +def test_sympy__physics__quantum__spin__WignerD(): + from sympy.physics.quantum.spin import WignerD + assert _test_args(WignerD(0, 1, 2, 3, 4, 5)) + + +def test_sympy__physics__quantum__state__Bra(): + from sympy.physics.quantum.state import Bra + assert _test_args(Bra(0)) + + +def test_sympy__physics__quantum__state__BraBase(): + from sympy.physics.quantum.state import BraBase + assert _test_args(BraBase(0)) + + +def test_sympy__physics__quantum__state__Ket(): + from sympy.physics.quantum.state import Ket + assert _test_args(Ket(0)) + + +def test_sympy__physics__quantum__state__KetBase(): + from sympy.physics.quantum.state import KetBase + assert _test_args(KetBase(0)) + + +def test_sympy__physics__quantum__state__State(): + from sympy.physics.quantum.state import State + assert _test_args(State(0)) + + +def test_sympy__physics__quantum__state__StateBase(): + from sympy.physics.quantum.state import StateBase + assert _test_args(StateBase(0)) + + +def test_sympy__physics__quantum__state__OrthogonalBra(): + from sympy.physics.quantum.state import OrthogonalBra + assert _test_args(OrthogonalBra(0)) + + +def test_sympy__physics__quantum__state__OrthogonalKet(): + from sympy.physics.quantum.state import OrthogonalKet + assert _test_args(OrthogonalKet(0)) + + +def test_sympy__physics__quantum__state__OrthogonalState(): + from sympy.physics.quantum.state import OrthogonalState + assert _test_args(OrthogonalState(0)) + + +def test_sympy__physics__quantum__state__TimeDepBra(): + from sympy.physics.quantum.state import TimeDepBra + assert _test_args(TimeDepBra('psi', 't')) + + +def test_sympy__physics__quantum__state__TimeDepKet(): + from sympy.physics.quantum.state import TimeDepKet + assert _test_args(TimeDepKet('psi', 't')) + + +def test_sympy__physics__quantum__state__TimeDepState(): + from sympy.physics.quantum.state import TimeDepState + assert _test_args(TimeDepState('psi', 't')) + + +def test_sympy__physics__quantum__state__Wavefunction(): + from sympy.physics.quantum.state import Wavefunction + from sympy.functions import sin + from sympy.functions.elementary.piecewise import Piecewise + n = 1 + L = 1 + g = Piecewise((0, x < 0), (0, x > L), (sqrt(2//L)*sin(n*pi*x/L), True)) + assert _test_args(Wavefunction(g, x)) + + +def test_sympy__physics__quantum__tensorproduct__TensorProduct(): + from sympy.physics.quantum.tensorproduct import TensorProduct + x, y = symbols("x y", commutative=False) + assert _test_args(TensorProduct(x, y)) + + +def test_sympy__physics__quantum__identitysearch__GateIdentity(): + from sympy.physics.quantum.gate import X + from sympy.physics.quantum.identitysearch import GateIdentity + assert _test_args(GateIdentity(X(0), X(0))) + + +def test_sympy__physics__quantum__sho1d__SHOOp(): + from sympy.physics.quantum.sho1d import SHOOp + assert _test_args(SHOOp('a')) + + +def test_sympy__physics__quantum__sho1d__RaisingOp(): + from sympy.physics.quantum.sho1d import RaisingOp + assert _test_args(RaisingOp('a')) + + +def test_sympy__physics__quantum__sho1d__LoweringOp(): + from sympy.physics.quantum.sho1d import LoweringOp + assert _test_args(LoweringOp('a')) + + +def test_sympy__physics__quantum__sho1d__NumberOp(): + from sympy.physics.quantum.sho1d import NumberOp + assert _test_args(NumberOp('N')) + + +def test_sympy__physics__quantum__sho1d__Hamiltonian(): + from sympy.physics.quantum.sho1d import Hamiltonian + assert _test_args(Hamiltonian('H')) + + +def test_sympy__physics__quantum__sho1d__SHOState(): + from sympy.physics.quantum.sho1d import SHOState + assert _test_args(SHOState(0)) + + +def test_sympy__physics__quantum__sho1d__SHOKet(): + from sympy.physics.quantum.sho1d import SHOKet + assert _test_args(SHOKet(0)) + + +def test_sympy__physics__quantum__sho1d__SHOBra(): + from sympy.physics.quantum.sho1d import SHOBra + assert _test_args(SHOBra(0)) + + +def test_sympy__physics__secondquant__AnnihilateBoson(): + from sympy.physics.secondquant import AnnihilateBoson + assert _test_args(AnnihilateBoson(0)) + + +def test_sympy__physics__secondquant__AnnihilateFermion(): + from sympy.physics.secondquant import AnnihilateFermion + assert _test_args(AnnihilateFermion(0)) + + +@SKIP("abstract class") +def test_sympy__physics__secondquant__Annihilator(): + pass + + +def test_sympy__physics__secondquant__AntiSymmetricTensor(): + from sympy.physics.secondquant import AntiSymmetricTensor + i, j = symbols('i j', below_fermi=True) + a, b = symbols('a b', above_fermi=True) + assert _test_args(AntiSymmetricTensor('v', (a, i), (b, j))) + + +def test_sympy__physics__secondquant__BosonState(): + from sympy.physics.secondquant import BosonState + assert _test_args(BosonState((0, 1))) + + +@SKIP("abstract class") +def test_sympy__physics__secondquant__BosonicOperator(): + pass + + +def test_sympy__physics__secondquant__Commutator(): + from sympy.physics.secondquant import Commutator + x, y = symbols('x y', commutative=False) + assert _test_args(Commutator(x, y)) + + +def test_sympy__physics__secondquant__CreateBoson(): + from sympy.physics.secondquant import CreateBoson + assert _test_args(CreateBoson(0)) + + +def test_sympy__physics__secondquant__CreateFermion(): + from sympy.physics.secondquant import CreateFermion + assert _test_args(CreateFermion(0)) + + +@SKIP("abstract class") +def test_sympy__physics__secondquant__Creator(): + pass + + +def test_sympy__physics__secondquant__Dagger(): + from sympy.physics.secondquant import Dagger + assert _test_args(Dagger(x)) + + +def test_sympy__physics__secondquant__FermionState(): + from sympy.physics.secondquant import FermionState + assert _test_args(FermionState((0, 1))) + + +def test_sympy__physics__secondquant__FermionicOperator(): + from sympy.physics.secondquant import FermionicOperator + assert _test_args(FermionicOperator(0)) + + +def test_sympy__physics__secondquant__FockState(): + from sympy.physics.secondquant import FockState + assert _test_args(FockState((0, 1))) + + +def test_sympy__physics__secondquant__FockStateBosonBra(): + from sympy.physics.secondquant import FockStateBosonBra + assert _test_args(FockStateBosonBra((0, 1))) + + +def test_sympy__physics__secondquant__FockStateBosonKet(): + from sympy.physics.secondquant import FockStateBosonKet + assert _test_args(FockStateBosonKet((0, 1))) + + +def test_sympy__physics__secondquant__FockStateBra(): + from sympy.physics.secondquant import FockStateBra + assert _test_args(FockStateBra((0, 1))) + + +def test_sympy__physics__secondquant__FockStateFermionBra(): + from sympy.physics.secondquant import FockStateFermionBra + assert _test_args(FockStateFermionBra((0, 1))) + + +def test_sympy__physics__secondquant__FockStateFermionKet(): + from sympy.physics.secondquant import FockStateFermionKet + assert _test_args(FockStateFermionKet((0, 1))) + + +def test_sympy__physics__secondquant__FockStateKet(): + from sympy.physics.secondquant import FockStateKet + assert _test_args(FockStateKet((0, 1))) + + +def test_sympy__physics__secondquant__InnerProduct(): + from sympy.physics.secondquant import InnerProduct + from sympy.physics.secondquant import FockStateKet, FockStateBra + assert _test_args(InnerProduct(FockStateBra((0, 1)), FockStateKet((0, 1)))) + + +def test_sympy__physics__secondquant__NO(): + from sympy.physics.secondquant import NO, F, Fd + assert _test_args(NO(Fd(x)*F(y))) + + +def test_sympy__physics__secondquant__PermutationOperator(): + from sympy.physics.secondquant import PermutationOperator + assert _test_args(PermutationOperator(0, 1)) + + +def test_sympy__physics__secondquant__SqOperator(): + from sympy.physics.secondquant import SqOperator + assert _test_args(SqOperator(0)) + + +def test_sympy__physics__secondquant__TensorSymbol(): + from sympy.physics.secondquant import TensorSymbol + assert _test_args(TensorSymbol(x)) + + +def test_sympy__physics__control__lti__LinearTimeInvariant(): + # Direct instances of LinearTimeInvariant class are not allowed. + # func(*args) tests for its derived classes (TransferFunction, + # Series, Parallel and TransferFunctionMatrix) should pass. + pass + + +def test_sympy__physics__control__lti__SISOLinearTimeInvariant(): + # Direct instances of SISOLinearTimeInvariant class are not allowed. + pass + + +def test_sympy__physics__control__lti__MIMOLinearTimeInvariant(): + # Direct instances of MIMOLinearTimeInvariant class are not allowed. + pass + + +def test_sympy__physics__control__lti__TransferFunction(): + from sympy.physics.control.lti import TransferFunction + assert _test_args(TransferFunction(2, 3, x)) + + +def test_sympy__physics__control__lti__Series(): + from sympy.physics.control import Series, TransferFunction + tf1 = TransferFunction(x**2 - y**3, y - z, x) + tf2 = TransferFunction(y - x, z + y, x) + assert _test_args(Series(tf1, tf2)) + + +def test_sympy__physics__control__lti__MIMOSeries(): + from sympy.physics.control import MIMOSeries, TransferFunction, TransferFunctionMatrix + tf1 = TransferFunction(x**2 - y**3, y - z, x) + tf2 = TransferFunction(y - x, z + y, x) + tfm_1 = TransferFunctionMatrix([[tf2, tf1]]) + tfm_2 = TransferFunctionMatrix([[tf1, tf2], [tf2, tf1]]) + tfm_3 = TransferFunctionMatrix([[tf1], [tf2]]) + assert _test_args(MIMOSeries(tfm_3, tfm_2, tfm_1)) + + +def test_sympy__physics__control__lti__Parallel(): + from sympy.physics.control import Parallel, TransferFunction + tf1 = TransferFunction(x**2 - y**3, y - z, x) + tf2 = TransferFunction(y - x, z + y, x) + assert _test_args(Parallel(tf1, tf2)) + + +def test_sympy__physics__control__lti__MIMOParallel(): + from sympy.physics.control import MIMOParallel, TransferFunction, TransferFunctionMatrix + tf1 = TransferFunction(x**2 - y**3, y - z, x) + tf2 = TransferFunction(y - x, z + y, x) + tfm_1 = TransferFunctionMatrix([[tf1, tf2], [tf2, tf1]]) + tfm_2 = TransferFunctionMatrix([[tf2, tf1], [tf1, tf2]]) + assert _test_args(MIMOParallel(tfm_1, tfm_2)) + + +def test_sympy__physics__control__lti__Feedback(): + from sympy.physics.control import TransferFunction, Feedback + tf1 = TransferFunction(x**2 - y**3, y - z, x) + tf2 = TransferFunction(y - x, z + y, x) + assert _test_args(Feedback(tf1, tf2)) + assert _test_args(Feedback(tf1, tf2, 1)) + + +def test_sympy__physics__control__lti__MIMOFeedback(): + from sympy.physics.control import TransferFunction, MIMOFeedback, TransferFunctionMatrix + tf1 = TransferFunction(x**2 - y**3, y - z, x) + tf2 = TransferFunction(y - x, z + y, x) + tfm_1 = TransferFunctionMatrix([[tf2, tf1], [tf1, tf2]]) + tfm_2 = TransferFunctionMatrix([[tf1, tf2], [tf2, tf1]]) + assert _test_args(MIMOFeedback(tfm_1, tfm_2)) + assert _test_args(MIMOFeedback(tfm_1, tfm_2, 1)) + + +def test_sympy__physics__control__lti__TransferFunctionMatrix(): + from sympy.physics.control import TransferFunction, TransferFunctionMatrix + tf1 = TransferFunction(x**2 - y**3, y - z, x) + tf2 = TransferFunction(y - x, z + y, x) + assert _test_args(TransferFunctionMatrix([[tf1, tf2]])) + + +def test_sympy__physics__control__lti__StateSpace(): + from sympy.matrices.dense import Matrix + from sympy.physics.control import StateSpace + A = Matrix([[-5, -1], [3, -1]]) + B = Matrix([2, 5]) + C = Matrix([[1, 2]]) + D = Matrix([0]) + assert _test_args(StateSpace(A, B, C, D)) + + +def test_sympy__physics__units__dimensions__Dimension(): + from sympy.physics.units.dimensions import Dimension + assert _test_args(Dimension("length", "L")) + + +def test_sympy__physics__units__dimensions__DimensionSystem(): + from sympy.physics.units.dimensions import DimensionSystem + from sympy.physics.units.definitions.dimension_definitions import length, time, velocity + assert _test_args(DimensionSystem((length, time), (velocity,))) + + +def test_sympy__physics__units__quantities__Quantity(): + from sympy.physics.units.quantities import Quantity + assert _test_args(Quantity("dam")) + + +def test_sympy__physics__units__quantities__PhysicalConstant(): + from sympy.physics.units.quantities import PhysicalConstant + assert _test_args(PhysicalConstant("foo")) + + +def test_sympy__physics__units__prefixes__Prefix(): + from sympy.physics.units.prefixes import Prefix + assert _test_args(Prefix('kilo', 'k', 3)) + + +def test_sympy__core__numbers__AlgebraicNumber(): + from sympy.core.numbers import AlgebraicNumber + assert _test_args(AlgebraicNumber(sqrt(2), [1, 2, 3])) + + +def test_sympy__polys__polytools__GroebnerBasis(): + from sympy.polys.polytools import GroebnerBasis + assert _test_args(GroebnerBasis([x, y, z], x, y, z)) + + +def test_sympy__polys__polytools__Poly(): + from sympy.polys.polytools import Poly + assert _test_args(Poly(2, x, y)) + + +def test_sympy__polys__polytools__PurePoly(): + from sympy.polys.polytools import PurePoly + assert _test_args(PurePoly(2, x, y)) + + +@SKIP('abstract class') +def test_sympy__polys__rootoftools__RootOf(): + pass + + +def test_sympy__polys__rootoftools__ComplexRootOf(): + from sympy.polys.rootoftools import ComplexRootOf + assert _test_args(ComplexRootOf(x**3 + x + 1, 0)) + + +def test_sympy__polys__rootoftools__RootSum(): + from sympy.polys.rootoftools import RootSum + assert _test_args(RootSum(x**3 + x + 1, sin)) + + +def test_sympy__series__limits__Limit(): + from sympy.series.limits import Limit + assert _test_args(Limit(x, x, 0, dir='-')) + + +def test_sympy__series__order__Order(): + from sympy.series.order import Order + assert _test_args(Order(1, x, y)) + + +@SKIP('Abstract Class') +def test_sympy__series__sequences__SeqBase(): + pass + + +def test_sympy__series__sequences__EmptySequence(): + # Need to import the instance from series not the class from + # series.sequence + from sympy.series import EmptySequence + assert _test_args(EmptySequence) + + +@SKIP('Abstract Class') +def test_sympy__series__sequences__SeqExpr(): + pass + + +def test_sympy__series__sequences__SeqPer(): + from sympy.series.sequences import SeqPer + assert _test_args(SeqPer((1, 2, 3), (0, 10))) + + +def test_sympy__series__sequences__SeqFormula(): + from sympy.series.sequences import SeqFormula + assert _test_args(SeqFormula(x**2, (0, 10))) + + +def test_sympy__series__sequences__RecursiveSeq(): + from sympy.series.sequences import RecursiveSeq + y = Function("y") + n = symbols("n") + assert _test_args(RecursiveSeq(y(n - 1) + y(n - 2), y(n), n, (0, 1))) + assert _test_args(RecursiveSeq(y(n - 1) + y(n - 2), y(n), n)) + + +def test_sympy__series__sequences__SeqExprOp(): + from sympy.series.sequences import SeqExprOp, sequence + s1 = sequence((1, 2, 3)) + s2 = sequence(x**2) + assert _test_args(SeqExprOp(s1, s2)) + + +def test_sympy__series__sequences__SeqAdd(): + from sympy.series.sequences import SeqAdd, sequence + s1 = sequence((1, 2, 3)) + s2 = sequence(x**2) + assert _test_args(SeqAdd(s1, s2)) + + +def test_sympy__series__sequences__SeqMul(): + from sympy.series.sequences import SeqMul, sequence + s1 = sequence((1, 2, 3)) + s2 = sequence(x**2) + assert _test_args(SeqMul(s1, s2)) + + +@SKIP('Abstract Class') +def test_sympy__series__series_class__SeriesBase(): + pass + + +def test_sympy__series__fourier__FourierSeries(): + from sympy.series.fourier import fourier_series + assert _test_args(fourier_series(x, (x, -pi, pi))) + +def test_sympy__series__fourier__FiniteFourierSeries(): + from sympy.series.fourier import fourier_series + assert _test_args(fourier_series(sin(pi*x), (x, -1, 1))) + + +def test_sympy__series__formal__FormalPowerSeries(): + from sympy.series.formal import fps + assert _test_args(fps(log(1 + x), x)) + + +def test_sympy__series__formal__Coeff(): + from sympy.series.formal import fps + assert _test_args(fps(x**2 + x + 1, x)) + + +@SKIP('Abstract Class') +def test_sympy__series__formal__FiniteFormalPowerSeries(): + pass + + +def test_sympy__series__formal__FormalPowerSeriesProduct(): + from sympy.series.formal import fps + f1, f2 = fps(sin(x)), fps(exp(x)) + assert _test_args(f1.product(f2, x)) + + +def test_sympy__series__formal__FormalPowerSeriesCompose(): + from sympy.series.formal import fps + f1, f2 = fps(exp(x)), fps(sin(x)) + assert _test_args(f1.compose(f2, x)) + + +def test_sympy__series__formal__FormalPowerSeriesInverse(): + from sympy.series.formal import fps + f1 = fps(exp(x)) + assert _test_args(f1.inverse(x)) + + +def test_sympy__simplify__hyperexpand__Hyper_Function(): + from sympy.simplify.hyperexpand import Hyper_Function + assert _test_args(Hyper_Function([2], [1])) + + +def test_sympy__simplify__hyperexpand__G_Function(): + from sympy.simplify.hyperexpand import G_Function + assert _test_args(G_Function([2], [1], [], [])) + + +@SKIP("abstract class") +def test_sympy__tensor__array__ndim_array__ImmutableNDimArray(): + pass + + +def test_sympy__tensor__array__dense_ndim_array__ImmutableDenseNDimArray(): + from sympy.tensor.array.dense_ndim_array import ImmutableDenseNDimArray + densarr = ImmutableDenseNDimArray(range(10, 34), (2, 3, 4)) + assert _test_args(densarr) + + +def test_sympy__tensor__array__sparse_ndim_array__ImmutableSparseNDimArray(): + from sympy.tensor.array.sparse_ndim_array import ImmutableSparseNDimArray + sparr = ImmutableSparseNDimArray(range(10, 34), (2, 3, 4)) + assert _test_args(sparr) + + +def test_sympy__tensor__array__array_comprehension__ArrayComprehension(): + from sympy.tensor.array.array_comprehension import ArrayComprehension + arrcom = ArrayComprehension(x, (x, 1, 5)) + assert _test_args(arrcom) + +def test_sympy__tensor__array__array_comprehension__ArrayComprehensionMap(): + from sympy.tensor.array.array_comprehension import ArrayComprehensionMap + arrcomma = ArrayComprehensionMap(lambda: 0, (x, 1, 5)) + assert _test_args(arrcomma) + + +def test_sympy__tensor__array__array_derivatives__ArrayDerivative(): + from sympy.tensor.array.array_derivatives import ArrayDerivative + A = MatrixSymbol("A", 2, 2) + arrder = ArrayDerivative(A, A, evaluate=False) + assert _test_args(arrder) + +def test_sympy__tensor__array__expressions__array_expressions__ArraySymbol(): + from sympy.tensor.array.expressions.array_expressions import ArraySymbol + m, n, k = symbols("m n k") + array = ArraySymbol("A", (m, n, k, 2)) + assert _test_args(array) + +def test_sympy__tensor__array__expressions__array_expressions__ArrayElement(): + from sympy.tensor.array.expressions.array_expressions import ArrayElement + m, n, k = symbols("m n k") + ae = ArrayElement("A", (m, n, k, 2)) + assert _test_args(ae) + +def test_sympy__tensor__array__expressions__array_expressions__ZeroArray(): + from sympy.tensor.array.expressions.array_expressions import ZeroArray + m, n, k = symbols("m n k") + za = ZeroArray(m, n, k, 2) + assert _test_args(za) + +def test_sympy__tensor__array__expressions__array_expressions__OneArray(): + from sympy.tensor.array.expressions.array_expressions import OneArray + m, n, k = symbols("m n k") + za = OneArray(m, n, k, 2) + assert _test_args(za) + +def test_sympy__tensor__functions__TensorProduct(): + from sympy.tensor.functions import TensorProduct + A = MatrixSymbol('A', 3, 3) + B = MatrixSymbol('B', 3, 3) + tp = TensorProduct(A, B) + assert _test_args(tp) + + +def test_sympy__tensor__indexed__Idx(): + from sympy.tensor.indexed import Idx + assert _test_args(Idx('test')) + assert _test_args(Idx('test', (0, 10))) + assert _test_args(Idx('test', 2)) + assert _test_args(Idx('test', x)) + + +def test_sympy__tensor__indexed__Indexed(): + from sympy.tensor.indexed import Indexed, Idx + assert _test_args(Indexed('A', Idx('i'), Idx('j'))) + + +def test_sympy__tensor__indexed__IndexedBase(): + from sympy.tensor.indexed import IndexedBase + assert _test_args(IndexedBase('A', shape=(x, y))) + assert _test_args(IndexedBase('A', 1)) + assert _test_args(IndexedBase('A')[0, 1]) + + +def test_sympy__tensor__tensor__TensorIndexType(): + from sympy.tensor.tensor import TensorIndexType + assert _test_args(TensorIndexType('Lorentz')) + + +@SKIP("deprecated class") +def test_sympy__tensor__tensor__TensorType(): + pass + + +def test_sympy__tensor__tensor__TensorSymmetry(): + from sympy.tensor.tensor import TensorSymmetry, get_symmetric_group_sgs + assert _test_args(TensorSymmetry(get_symmetric_group_sgs(2))) + + +def test_sympy__tensor__tensor__TensorHead(): + from sympy.tensor.tensor import TensorIndexType, TensorSymmetry, get_symmetric_group_sgs, TensorHead + Lorentz = TensorIndexType('Lorentz', dummy_name='L') + sym = TensorSymmetry(get_symmetric_group_sgs(1)) + assert _test_args(TensorHead('p', [Lorentz], sym, 0)) + + +def test_sympy__tensor__tensor__TensorIndex(): + from sympy.tensor.tensor import TensorIndexType, TensorIndex + Lorentz = TensorIndexType('Lorentz', dummy_name='L') + assert _test_args(TensorIndex('i', Lorentz)) + +@SKIP("abstract class") +def test_sympy__tensor__tensor__TensExpr(): + pass + +def test_sympy__tensor__tensor__TensAdd(): + from sympy.tensor.tensor import TensorIndexType, TensorSymmetry, get_symmetric_group_sgs, tensor_indices, TensAdd, tensor_heads + Lorentz = TensorIndexType('Lorentz', dummy_name='L') + a, b = tensor_indices('a,b', Lorentz) + sym = TensorSymmetry(get_symmetric_group_sgs(1)) + p, q = tensor_heads('p,q', [Lorentz], sym) + t1 = p(a) + t2 = q(a) + assert _test_args(TensAdd(t1, t2)) + + +def test_sympy__tensor__tensor__Tensor(): + from sympy.tensor.tensor import TensorIndexType, TensorSymmetry, get_symmetric_group_sgs, tensor_indices, TensorHead + Lorentz = TensorIndexType('Lorentz', dummy_name='L') + a, b = tensor_indices('a,b', Lorentz) + sym = TensorSymmetry(get_symmetric_group_sgs(1)) + p = TensorHead('p', [Lorentz], sym) + assert _test_args(p(a)) + + +def test_sympy__tensor__tensor__TensMul(): + from sympy.tensor.tensor import TensorIndexType, TensorSymmetry, get_symmetric_group_sgs, tensor_indices, tensor_heads + Lorentz = TensorIndexType('Lorentz', dummy_name='L') + a, b = tensor_indices('a,b', Lorentz) + sym = TensorSymmetry(get_symmetric_group_sgs(1)) + p, q = tensor_heads('p, q', [Lorentz], sym) + assert _test_args(3*p(a)*q(b)) + + +def test_sympy__tensor__tensor__TensorElement(): + from sympy.tensor.tensor import TensorIndexType, TensorHead, TensorElement + L = TensorIndexType("L") + A = TensorHead("A", [L, L]) + telem = TensorElement(A(x, y), {x: 1}) + assert _test_args(telem) + +def test_sympy__tensor__tensor__WildTensor(): + from sympy.tensor.tensor import TensorIndexType, WildTensorHead, TensorIndex + Lorentz = TensorIndexType('Lorentz', dummy_name='L') + a = TensorIndex('a', Lorentz) + p = WildTensorHead('p') + assert _test_args(p(a)) + +def test_sympy__tensor__tensor__WildTensorHead(): + from sympy.tensor.tensor import WildTensorHead + assert _test_args(WildTensorHead('p')) + +def test_sympy__tensor__tensor__WildTensorIndex(): + from sympy.tensor.tensor import TensorIndexType, WildTensorIndex + Lorentz = TensorIndexType('Lorentz', dummy_name='L') + assert _test_args(WildTensorIndex('i', Lorentz)) + +def test_sympy__tensor__toperators__PartialDerivative(): + from sympy.tensor.tensor import TensorIndexType, tensor_indices, TensorHead + from sympy.tensor.toperators import PartialDerivative + Lorentz = TensorIndexType('Lorentz', dummy_name='L') + a, b = tensor_indices('a,b', Lorentz) + A = TensorHead("A", [Lorentz]) + assert _test_args(PartialDerivative(A(a), A(b))) + + +def test_as_coeff_add(): + assert (7, (3*x, 4*x**2)) == (7 + 3*x + 4*x**2).as_coeff_add() + + +def test_sympy__geometry__curve__Curve(): + from sympy.geometry.curve import Curve + assert _test_args(Curve((x, 1), (x, 0, 1))) + + +def test_sympy__geometry__point__Point(): + from sympy.geometry.point import Point + assert _test_args(Point(0, 1)) + + +def test_sympy__geometry__point__Point2D(): + from sympy.geometry.point import Point2D + assert _test_args(Point2D(0, 1)) + + +def test_sympy__geometry__point__Point3D(): + from sympy.geometry.point import Point3D + assert _test_args(Point3D(0, 1, 2)) + + +def test_sympy__geometry__ellipse__Ellipse(): + from sympy.geometry.ellipse import Ellipse + assert _test_args(Ellipse((0, 1), 2, 3)) + + +def test_sympy__geometry__ellipse__Circle(): + from sympy.geometry.ellipse import Circle + assert _test_args(Circle((0, 1), 2)) + + +def test_sympy__geometry__parabola__Parabola(): + from sympy.geometry.parabola import Parabola + from sympy.geometry.line import Line + assert _test_args(Parabola((0, 0), Line((2, 3), (4, 3)))) + + +@SKIP("abstract class") +def test_sympy__geometry__line__LinearEntity(): + pass + + +def test_sympy__geometry__line__Line(): + from sympy.geometry.line import Line + assert _test_args(Line((0, 1), (2, 3))) + + +def test_sympy__geometry__line__Ray(): + from sympy.geometry.line import Ray + assert _test_args(Ray((0, 1), (2, 3))) + + +def test_sympy__geometry__line__Segment(): + from sympy.geometry.line import Segment + assert _test_args(Segment((0, 1), (2, 3))) + +@SKIP("abstract class") +def test_sympy__geometry__line__LinearEntity2D(): + pass + + +def test_sympy__geometry__line__Line2D(): + from sympy.geometry.line import Line2D + assert _test_args(Line2D((0, 1), (2, 3))) + + +def test_sympy__geometry__line__Ray2D(): + from sympy.geometry.line import Ray2D + assert _test_args(Ray2D((0, 1), (2, 3))) + + +def test_sympy__geometry__line__Segment2D(): + from sympy.geometry.line import Segment2D + assert _test_args(Segment2D((0, 1), (2, 3))) + + +@SKIP("abstract class") +def test_sympy__geometry__line__LinearEntity3D(): + pass + + +def test_sympy__geometry__line__Line3D(): + from sympy.geometry.line import Line3D + assert _test_args(Line3D((0, 1, 1), (2, 3, 4))) + + +def test_sympy__geometry__line__Segment3D(): + from sympy.geometry.line import Segment3D + assert _test_args(Segment3D((0, 1, 1), (2, 3, 4))) + + +def test_sympy__geometry__line__Ray3D(): + from sympy.geometry.line import Ray3D + assert _test_args(Ray3D((0, 1, 1), (2, 3, 4))) + + +def test_sympy__geometry__plane__Plane(): + from sympy.geometry.plane import Plane + assert _test_args(Plane((1, 1, 1), (-3, 4, -2), (1, 2, 3))) + + +def test_sympy__geometry__polygon__Polygon(): + from sympy.geometry.polygon import Polygon + assert _test_args(Polygon((0, 1), (2, 3), (4, 5), (6, 7))) + + +def test_sympy__geometry__polygon__RegularPolygon(): + from sympy.geometry.polygon import RegularPolygon + assert _test_args(RegularPolygon((0, 1), 2, 3, 4)) + + +def test_sympy__geometry__polygon__Triangle(): + from sympy.geometry.polygon import Triangle + assert _test_args(Triangle((0, 1), (2, 3), (4, 5))) + + +def test_sympy__geometry__entity__GeometryEntity(): + from sympy.geometry.entity import GeometryEntity + from sympy.geometry.point import Point + assert _test_args(GeometryEntity(Point(1, 0), 1, [1, 2])) + +@SKIP("abstract class") +def test_sympy__geometry__entity__GeometrySet(): + pass + +def test_sympy__diffgeom__diffgeom__Manifold(): + from sympy.diffgeom import Manifold + assert _test_args(Manifold('name', 3)) + + +def test_sympy__diffgeom__diffgeom__Patch(): + from sympy.diffgeom import Manifold, Patch + assert _test_args(Patch('name', Manifold('name', 3))) + + +def test_sympy__diffgeom__diffgeom__CoordSystem(): + from sympy.diffgeom import Manifold, Patch, CoordSystem + assert _test_args(CoordSystem('name', Patch('name', Manifold('name', 3)))) + assert _test_args(CoordSystem('name', Patch('name', Manifold('name', 3)), [a, b, c])) + + +def test_sympy__diffgeom__diffgeom__CoordinateSymbol(): + from sympy.diffgeom import Manifold, Patch, CoordSystem, CoordinateSymbol + assert _test_args(CoordinateSymbol(CoordSystem('name', Patch('name', Manifold('name', 3)), [a, b, c]), 0)) + + +def test_sympy__diffgeom__diffgeom__Point(): + from sympy.diffgeom import Manifold, Patch, CoordSystem, Point + assert _test_args(Point( + CoordSystem('name', Patch('name', Manifold('name', 3)), [a, b, c]), [x, y])) + + +def test_sympy__diffgeom__diffgeom__BaseScalarField(): + from sympy.diffgeom import Manifold, Patch, CoordSystem, BaseScalarField + cs = CoordSystem('name', Patch('name', Manifold('name', 3)), [a, b, c]) + assert _test_args(BaseScalarField(cs, 0)) + + +def test_sympy__diffgeom__diffgeom__BaseVectorField(): + from sympy.diffgeom import Manifold, Patch, CoordSystem, BaseVectorField + cs = CoordSystem('name', Patch('name', Manifold('name', 3)), [a, b, c]) + assert _test_args(BaseVectorField(cs, 0)) + + +def test_sympy__diffgeom__diffgeom__Differential(): + from sympy.diffgeom import Manifold, Patch, CoordSystem, BaseScalarField, Differential + cs = CoordSystem('name', Patch('name', Manifold('name', 3)), [a, b, c]) + assert _test_args(Differential(BaseScalarField(cs, 0))) + + +def test_sympy__diffgeom__diffgeom__Commutator(): + from sympy.diffgeom import Manifold, Patch, CoordSystem, BaseVectorField, Commutator + cs = CoordSystem('name', Patch('name', Manifold('name', 3)), [a, b, c]) + cs1 = CoordSystem('name1', Patch('name', Manifold('name', 3)), [a, b, c]) + v = BaseVectorField(cs, 0) + v1 = BaseVectorField(cs1, 0) + assert _test_args(Commutator(v, v1)) + + +def test_sympy__diffgeom__diffgeom__TensorProduct(): + from sympy.diffgeom import Manifold, Patch, CoordSystem, BaseScalarField, Differential, TensorProduct + cs = CoordSystem('name', Patch('name', Manifold('name', 3)), [a, b, c]) + d = Differential(BaseScalarField(cs, 0)) + assert _test_args(TensorProduct(d, d)) + + +def test_sympy__diffgeom__diffgeom__WedgeProduct(): + from sympy.diffgeom import Manifold, Patch, CoordSystem, BaseScalarField, Differential, WedgeProduct + cs = CoordSystem('name', Patch('name', Manifold('name', 3)), [a, b, c]) + d = Differential(BaseScalarField(cs, 0)) + d1 = Differential(BaseScalarField(cs, 1)) + assert _test_args(WedgeProduct(d, d1)) + + +def test_sympy__diffgeom__diffgeom__LieDerivative(): + from sympy.diffgeom import Manifold, Patch, CoordSystem, BaseScalarField, Differential, BaseVectorField, LieDerivative + cs = CoordSystem('name', Patch('name', Manifold('name', 3)), [a, b, c]) + d = Differential(BaseScalarField(cs, 0)) + v = BaseVectorField(cs, 0) + assert _test_args(LieDerivative(v, d)) + + +def test_sympy__diffgeom__diffgeom__BaseCovarDerivativeOp(): + from sympy.diffgeom import Manifold, Patch, CoordSystem, BaseCovarDerivativeOp + cs = CoordSystem('name', Patch('name', Manifold('name', 3)), [a, b, c]) + assert _test_args(BaseCovarDerivativeOp(cs, 0, [[[0, ]*3, ]*3, ]*3)) + + +def test_sympy__diffgeom__diffgeom__CovarDerivativeOp(): + from sympy.diffgeom import Manifold, Patch, CoordSystem, BaseVectorField, CovarDerivativeOp + cs = CoordSystem('name', Patch('name', Manifold('name', 3)), [a, b, c]) + v = BaseVectorField(cs, 0) + _test_args(CovarDerivativeOp(v, [[[0, ]*3, ]*3, ]*3)) + + +def test_sympy__categories__baseclasses__Class(): + from sympy.categories.baseclasses import Class + assert _test_args(Class()) + + +def test_sympy__categories__baseclasses__Object(): + from sympy.categories import Object + assert _test_args(Object("A")) + + +@SKIP("abstract class") +def test_sympy__categories__baseclasses__Morphism(): + pass + + +def test_sympy__categories__baseclasses__IdentityMorphism(): + from sympy.categories import Object, IdentityMorphism + assert _test_args(IdentityMorphism(Object("A"))) + + +def test_sympy__categories__baseclasses__NamedMorphism(): + from sympy.categories import Object, NamedMorphism + assert _test_args(NamedMorphism(Object("A"), Object("B"), "f")) + + +def test_sympy__categories__baseclasses__CompositeMorphism(): + from sympy.categories import Object, NamedMorphism, CompositeMorphism + A = Object("A") + B = Object("B") + C = Object("C") + f = NamedMorphism(A, B, "f") + g = NamedMorphism(B, C, "g") + assert _test_args(CompositeMorphism(f, g)) + + +def test_sympy__categories__baseclasses__Diagram(): + from sympy.categories import Object, NamedMorphism, Diagram + A = Object("A") + B = Object("B") + f = NamedMorphism(A, B, "f") + d = Diagram([f]) + assert _test_args(d) + + +def test_sympy__categories__baseclasses__Category(): + from sympy.categories import Object, NamedMorphism, Diagram, Category + A = Object("A") + B = Object("B") + C = Object("C") + f = NamedMorphism(A, B, "f") + g = NamedMorphism(B, C, "g") + d1 = Diagram([f, g]) + d2 = Diagram([f]) + K = Category("K", commutative_diagrams=[d1, d2]) + assert _test_args(K) + + +def test_sympy__physics__optics__waves__TWave(): + from sympy.physics.optics import TWave + A, f, phi = symbols('A, f, phi') + assert _test_args(TWave(A, f, phi)) + + +def test_sympy__physics__optics__gaussopt__BeamParameter(): + from sympy.physics.optics import BeamParameter + assert _test_args(BeamParameter(530e-9, 1, w=1e-3, n=1)) + + +def test_sympy__physics__optics__medium__Medium(): + from sympy.physics.optics import Medium + assert _test_args(Medium('m')) + + +def test_sympy__physics__optics__medium__MediumN(): + from sympy.physics.optics.medium import Medium + assert _test_args(Medium('m', n=2)) + + +def test_sympy__physics__optics__medium__MediumPP(): + from sympy.physics.optics.medium import Medium + assert _test_args(Medium('m', permittivity=2, permeability=2)) + + +def test_sympy__tensor__array__expressions__array_expressions__ArrayContraction(): + from sympy.tensor.array.expressions.array_expressions import ArrayContraction + from sympy.tensor.indexed import IndexedBase + A = symbols("A", cls=IndexedBase) + assert _test_args(ArrayContraction(A, (0, 1))) + + +def test_sympy__tensor__array__expressions__array_expressions__ArrayDiagonal(): + from sympy.tensor.array.expressions.array_expressions import ArrayDiagonal + from sympy.tensor.indexed import IndexedBase + A = symbols("A", cls=IndexedBase) + assert _test_args(ArrayDiagonal(A, (0, 1))) + + +def test_sympy__tensor__array__expressions__array_expressions__ArrayTensorProduct(): + from sympy.tensor.array.expressions.array_expressions import ArrayTensorProduct + from sympy.tensor.indexed import IndexedBase + A, B = symbols("A B", cls=IndexedBase) + assert _test_args(ArrayTensorProduct(A, B)) + + +def test_sympy__tensor__array__expressions__array_expressions__ArrayAdd(): + from sympy.tensor.array.expressions.array_expressions import ArrayAdd + from sympy.tensor.indexed import IndexedBase + A, B = symbols("A B", cls=IndexedBase) + assert _test_args(ArrayAdd(A, B)) + + +def test_sympy__tensor__array__expressions__array_expressions__PermuteDims(): + from sympy.tensor.array.expressions.array_expressions import PermuteDims + A = MatrixSymbol("A", 4, 4) + assert _test_args(PermuteDims(A, (1, 0))) + + +def test_sympy__tensor__array__expressions__array_expressions__ArrayElementwiseApplyFunc(): + from sympy.tensor.array.expressions.array_expressions import ArraySymbol, ArrayElementwiseApplyFunc + A = ArraySymbol("A", (4,)) + assert _test_args(ArrayElementwiseApplyFunc(exp, A)) + + +def test_sympy__tensor__array__expressions__array_expressions__Reshape(): + from sympy.tensor.array.expressions.array_expressions import ArraySymbol, Reshape + A = ArraySymbol("A", (4,)) + assert _test_args(Reshape(A, (2, 2))) + + +def test_sympy__codegen__ast__Assignment(): + from sympy.codegen.ast import Assignment + assert _test_args(Assignment(x, y)) + + +def test_sympy__codegen__cfunctions__expm1(): + from sympy.codegen.cfunctions import expm1 + assert _test_args(expm1(x)) + + +def test_sympy__codegen__cfunctions__log1p(): + from sympy.codegen.cfunctions import log1p + assert _test_args(log1p(x)) + + +def test_sympy__codegen__cfunctions__exp2(): + from sympy.codegen.cfunctions import exp2 + assert _test_args(exp2(x)) + + +def test_sympy__codegen__cfunctions__log2(): + from sympy.codegen.cfunctions import log2 + assert _test_args(log2(x)) + + +def test_sympy__codegen__cfunctions__fma(): + from sympy.codegen.cfunctions import fma + assert _test_args(fma(x, y, z)) + + +def test_sympy__codegen__cfunctions__log10(): + from sympy.codegen.cfunctions import log10 + assert _test_args(log10(x)) + + +def test_sympy__codegen__cfunctions__Sqrt(): + from sympy.codegen.cfunctions import Sqrt + assert _test_args(Sqrt(x)) + +def test_sympy__codegen__cfunctions__Cbrt(): + from sympy.codegen.cfunctions import Cbrt + assert _test_args(Cbrt(x)) + +def test_sympy__codegen__cfunctions__hypot(): + from sympy.codegen.cfunctions import hypot + assert _test_args(hypot(x, y)) + + +def test_sympy__codegen__cfunctions__isnan(): + from sympy.codegen.cfunctions import isnan + assert _test_args(isnan(x)) + + +def test_sympy__codegen__fnodes__FFunction(): + from sympy.codegen.fnodes import FFunction + assert _test_args(FFunction('f')) + + +def test_sympy__codegen__fnodes__F95Function(): + from sympy.codegen.fnodes import F95Function + assert _test_args(F95Function('f')) + + +def test_sympy__codegen__fnodes__isign(): + from sympy.codegen.fnodes import isign + assert _test_args(isign(1, x)) + + +def test_sympy__codegen__fnodes__dsign(): + from sympy.codegen.fnodes import dsign + assert _test_args(dsign(1, x)) + + +def test_sympy__codegen__fnodes__cmplx(): + from sympy.codegen.fnodes import cmplx + assert _test_args(cmplx(x, y)) + + +def test_sympy__codegen__fnodes__kind(): + from sympy.codegen.fnodes import kind + assert _test_args(kind(x)) + + +def test_sympy__codegen__fnodes__merge(): + from sympy.codegen.fnodes import merge + assert _test_args(merge(1, 2, Eq(x, 0))) + + +def test_sympy__codegen__fnodes___literal(): + from sympy.codegen.fnodes import _literal + assert _test_args(_literal(1)) + + +def test_sympy__codegen__fnodes__literal_sp(): + from sympy.codegen.fnodes import literal_sp + assert _test_args(literal_sp(1)) + + +def test_sympy__codegen__fnodes__literal_dp(): + from sympy.codegen.fnodes import literal_dp + assert _test_args(literal_dp(1)) + + +def test_sympy__codegen__matrix_nodes__MatrixSolve(): + from sympy.matrices import MatrixSymbol + from sympy.codegen.matrix_nodes import MatrixSolve + A = MatrixSymbol('A', 3, 3) + v = MatrixSymbol('x', 3, 1) + assert _test_args(MatrixSolve(A, v)) + + +def test_sympy__vector__coordsysrect__CoordSys3D(): + from sympy.vector.coordsysrect import CoordSys3D + assert _test_args(CoordSys3D('C')) + + +def test_sympy__vector__point__Point(): + from sympy.vector.point import Point + assert _test_args(Point('P')) + + +def test_sympy__vector__basisdependent__BasisDependent(): + #from sympy.vector.basisdependent import BasisDependent + #These classes have been created to maintain an OOP hierarchy + #for Vectors and Dyadics. Are NOT meant to be initialized + pass + + +def test_sympy__vector__basisdependent__BasisDependentMul(): + #from sympy.vector.basisdependent import BasisDependentMul + #These classes have been created to maintain an OOP hierarchy + #for Vectors and Dyadics. Are NOT meant to be initialized + pass + + +def test_sympy__vector__basisdependent__BasisDependentAdd(): + #from sympy.vector.basisdependent import BasisDependentAdd + #These classes have been created to maintain an OOP hierarchy + #for Vectors and Dyadics. Are NOT meant to be initialized + pass + + +def test_sympy__vector__basisdependent__BasisDependentZero(): + #from sympy.vector.basisdependent import BasisDependentZero + #These classes have been created to maintain an OOP hierarchy + #for Vectors and Dyadics. Are NOT meant to be initialized + pass + + +def test_sympy__vector__vector__BaseVector(): + from sympy.vector.vector import BaseVector + from sympy.vector.coordsysrect import CoordSys3D + C = CoordSys3D('C') + assert _test_args(BaseVector(0, C, ' ', ' ')) + + +def test_sympy__vector__vector__VectorAdd(): + from sympy.vector.vector import VectorAdd, VectorMul + from sympy.vector.coordsysrect import CoordSys3D + C = CoordSys3D('C') + from sympy.abc import a, b, c, x, y, z + v1 = a*C.i + b*C.j + c*C.k + v2 = x*C.i + y*C.j + z*C.k + assert _test_args(VectorAdd(v1, v2)) + assert _test_args(VectorMul(x, v1)) + + +def test_sympy__vector__vector__VectorMul(): + from sympy.vector.vector import VectorMul + from sympy.vector.coordsysrect import CoordSys3D + C = CoordSys3D('C') + from sympy.abc import a + assert _test_args(VectorMul(a, C.i)) + + +def test_sympy__vector__vector__VectorZero(): + from sympy.vector.vector import VectorZero + assert _test_args(VectorZero()) + + +def test_sympy__vector__vector__Vector(): + #from sympy.vector.vector import Vector + #Vector is never to be initialized using args + pass + + +def test_sympy__vector__vector__Cross(): + from sympy.vector.vector import Cross + from sympy.vector.coordsysrect import CoordSys3D + C = CoordSys3D('C') + _test_args(Cross(C.i, C.j)) + + +def test_sympy__vector__vector__Dot(): + from sympy.vector.vector import Dot + from sympy.vector.coordsysrect import CoordSys3D + C = CoordSys3D('C') + _test_args(Dot(C.i, C.j)) + + +def test_sympy__vector__dyadic__Dyadic(): + #from sympy.vector.dyadic import Dyadic + #Dyadic is never to be initialized using args + pass + + +def test_sympy__vector__dyadic__BaseDyadic(): + from sympy.vector.dyadic import BaseDyadic + from sympy.vector.coordsysrect import CoordSys3D + C = CoordSys3D('C') + assert _test_args(BaseDyadic(C.i, C.j)) + + +def test_sympy__vector__dyadic__DyadicMul(): + from sympy.vector.dyadic import BaseDyadic, DyadicMul + from sympy.vector.coordsysrect import CoordSys3D + C = CoordSys3D('C') + assert _test_args(DyadicMul(3, BaseDyadic(C.i, C.j))) + + +def test_sympy__vector__dyadic__DyadicAdd(): + from sympy.vector.dyadic import BaseDyadic, DyadicAdd + from sympy.vector.coordsysrect import CoordSys3D + C = CoordSys3D('C') + assert _test_args(2 * DyadicAdd(BaseDyadic(C.i, C.i), + BaseDyadic(C.i, C.j))) + + +def test_sympy__vector__dyadic__DyadicZero(): + from sympy.vector.dyadic import DyadicZero + assert _test_args(DyadicZero()) + + +def test_sympy__vector__deloperator__Del(): + from sympy.vector.deloperator import Del + assert _test_args(Del()) + + +def test_sympy__vector__implicitregion__ImplicitRegion(): + from sympy.vector.implicitregion import ImplicitRegion + from sympy.abc import x, y + assert _test_args(ImplicitRegion((x, y), y**3 - 4*x)) + + +def test_sympy__vector__integrals__ParametricIntegral(): + from sympy.vector.integrals import ParametricIntegral + from sympy.vector.parametricregion import ParametricRegion + from sympy.vector.coordsysrect import CoordSys3D + C = CoordSys3D('C') + assert _test_args(ParametricIntegral(C.y*C.i - 10*C.j,\ + ParametricRegion((x, y), (x, 1, 3), (y, -2, 2)))) + +def test_sympy__vector__operators__Curl(): + from sympy.vector.operators import Curl + from sympy.vector.coordsysrect import CoordSys3D + C = CoordSys3D('C') + assert _test_args(Curl(C.i)) + + +def test_sympy__vector__operators__Laplacian(): + from sympy.vector.operators import Laplacian + from sympy.vector.coordsysrect import CoordSys3D + C = CoordSys3D('C') + assert _test_args(Laplacian(C.i)) + + +def test_sympy__vector__operators__Divergence(): + from sympy.vector.operators import Divergence + from sympy.vector.coordsysrect import CoordSys3D + C = CoordSys3D('C') + assert _test_args(Divergence(C.i)) + + +def test_sympy__vector__operators__Gradient(): + from sympy.vector.operators import Gradient + from sympy.vector.coordsysrect import CoordSys3D + C = CoordSys3D('C') + assert _test_args(Gradient(C.x)) + + +def test_sympy__vector__orienters__Orienter(): + #from sympy.vector.orienters import Orienter + #Not to be initialized + pass + + +def test_sympy__vector__orienters__ThreeAngleOrienter(): + #from sympy.vector.orienters import ThreeAngleOrienter + #Not to be initialized + pass + + +def test_sympy__vector__orienters__AxisOrienter(): + from sympy.vector.orienters import AxisOrienter + from sympy.vector.coordsysrect import CoordSys3D + C = CoordSys3D('C') + assert _test_args(AxisOrienter(x, C.i)) + + +def test_sympy__vector__orienters__BodyOrienter(): + from sympy.vector.orienters import BodyOrienter + assert _test_args(BodyOrienter(x, y, z, '123')) + + +def test_sympy__vector__orienters__SpaceOrienter(): + from sympy.vector.orienters import SpaceOrienter + assert _test_args(SpaceOrienter(x, y, z, '123')) + + +def test_sympy__vector__orienters__QuaternionOrienter(): + from sympy.vector.orienters import QuaternionOrienter + a, b, c, d = symbols('a b c d') + assert _test_args(QuaternionOrienter(a, b, c, d)) + + +def test_sympy__vector__parametricregion__ParametricRegion(): + from sympy.abc import t + from sympy.vector.parametricregion import ParametricRegion + assert _test_args(ParametricRegion((t, t**3), (t, 0, 2))) + + +def test_sympy__vector__scalar__BaseScalar(): + from sympy.vector.scalar import BaseScalar + from sympy.vector.coordsysrect import CoordSys3D + C = CoordSys3D('C') + assert _test_args(BaseScalar(0, C, ' ', ' ')) + + +def test_sympy__physics__wigner__Wigner3j(): + from sympy.physics.wigner import Wigner3j + assert _test_args(Wigner3j(0, 0, 0, 0, 0, 0)) + + +def test_sympy__combinatorics__schur_number__SchurNumber(): + from sympy.combinatorics.schur_number import SchurNumber + assert _test_args(SchurNumber(x)) + + +def test_sympy__combinatorics__perm_groups__SymmetricPermutationGroup(): + from sympy.combinatorics.perm_groups import SymmetricPermutationGroup + assert _test_args(SymmetricPermutationGroup(5)) + + +def test_sympy__combinatorics__perm_groups__Coset(): + from sympy.combinatorics.permutations import Permutation + from sympy.combinatorics.perm_groups import PermutationGroup, Coset + a = Permutation(1, 2) + b = Permutation(0, 1) + G = PermutationGroup([a, b]) + assert _test_args(Coset(a, G)) diff --git a/lib/python3.12/site-packages/sympy/core/tests/test_arit.py b/lib/python3.12/site-packages/sympy/core/tests/test_arit.py new file mode 100644 index 0000000000000000000000000000000000000000..d9848c39eb8c57580ee925f9d40e5e0a149c3174 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/core/tests/test_arit.py @@ -0,0 +1,2472 @@ +from sympy.core.add import Add +from sympy.core.basic import Basic +from sympy.core.mod import Mod +from sympy.core.mul import Mul +from sympy.core.numbers import (Float, I, Integer, Rational, comp, nan, + oo, pi, zoo) +from sympy.core.power import Pow +from sympy.core.singleton import S +from sympy.core.symbol import (Dummy, Symbol, symbols) +from sympy.core.sympify import sympify +from sympy.functions.combinatorial.factorials import factorial +from sympy.functions.elementary.complexes import (im, re, sign) +from sympy.functions.elementary.exponential import (exp, log) +from sympy.functions.elementary.integers import floor +from sympy.functions.elementary.miscellaneous import (Max, sqrt) +from sympy.functions.elementary.trigonometric import (atan, cos, sin) +from sympy.polys.polytools import Poly +from sympy.sets.sets import FiniteSet + +from sympy.core.parameters import distribute, evaluate +from sympy.core.expr import unchanged +from sympy.utilities.iterables import permutations +from sympy.testing.pytest import XFAIL, raises, warns +from sympy.utilities.exceptions import SymPyDeprecationWarning +from sympy.core.random import verify_numerically +from sympy.functions.elementary.trigonometric import asin + +from itertools import product + +a, c, x, y, z = symbols('a,c,x,y,z') +b = Symbol("b", positive=True) + + +def same_and_same_prec(a, b): + # stricter matching for Floats + return a == b and a._prec == b._prec + + +def test_bug1(): + assert re(x) != x + x.series(x, 0, 1) + assert re(x) != x + + +def test_Symbol(): + e = a*b + assert e == a*b + assert a*b*b == a*b**2 + assert a*b*b + c == c + a*b**2 + assert a*b*b - c == -c + a*b**2 + + x = Symbol('x', complex=True, real=False) + assert x.is_imaginary is None # could be I or 1 + I + x = Symbol('x', complex=True, imaginary=False) + assert x.is_real is None # could be 1 or 1 + I + x = Symbol('x', real=True) + assert x.is_complex + x = Symbol('x', imaginary=True) + assert x.is_complex + x = Symbol('x', real=False, imaginary=False) + assert x.is_complex is None # might be a non-number + + +def test_arit0(): + p = Rational(5) + e = a*b + assert e == a*b + e = a*b + b*a + assert e == 2*a*b + e = a*b + b*a + a*b + p*b*a + assert e == 8*a*b + e = a*b + b*a + a*b + p*b*a + a + assert e == a + 8*a*b + e = a + a + assert e == 2*a + e = a + b + a + assert e == b + 2*a + e = a + b*b + a + b*b + assert e == 2*a + 2*b**2 + e = a + Rational(2) + b*b + a + b*b + p + assert e == 7 + 2*a + 2*b**2 + e = (a + b*b + a + b*b)*p + assert e == 5*(2*a + 2*b**2) + e = (a*b*c + c*b*a + b*a*c)*p + assert e == 15*a*b*c + e = (a*b*c + c*b*a + b*a*c)*p - Rational(15)*a*b*c + assert e == Rational(0) + e = Rational(50)*(a - a) + assert e == Rational(0) + e = b*a - b - a*b + b + assert e == Rational(0) + e = a*b + c**p + assert e == a*b + c**5 + e = a/b + assert e == a*b**(-1) + e = a*2*2 + assert e == 4*a + e = 2 + a*2/2 + assert e == 2 + a + e = 2 - a - 2 + assert e == -a + e = 2*a*2 + assert e == 4*a + e = 2/a/2 + assert e == a**(-1) + e = 2**a**2 + assert e == 2**(a**2) + e = -(1 + a) + assert e == -1 - a + e = S.Half*(1 + a) + assert e == S.Half + a/2 + + +def test_div(): + e = a/b + assert e == a*b**(-1) + e = a/b + c/2 + assert e == a*b**(-1) + Rational(1)/2*c + e = (1 - b)/(b - 1) + assert e == (1 + -b)*((-1) + b)**(-1) + + +def test_pow_arit(): + n1 = Rational(1) + n2 = Rational(2) + n5 = Rational(5) + e = a*a + assert e == a**2 + e = a*a*a + assert e == a**3 + e = a*a*a*a**Rational(6) + assert e == a**9 + e = a*a*a*a**Rational(6) - a**Rational(9) + assert e == Rational(0) + e = a**(b - b) + assert e == Rational(1) + e = (a + Rational(1) - a)**b + assert e == Rational(1) + + e = (a + b + c)**n2 + assert e == (a + b + c)**2 + assert e.expand() == 2*b*c + 2*a*c + 2*a*b + a**2 + c**2 + b**2 + + e = (a + b)**n2 + assert e == (a + b)**2 + assert e.expand() == 2*a*b + a**2 + b**2 + + e = (a + b)**(n1/n2) + assert e == sqrt(a + b) + assert e.expand() == sqrt(a + b) + + n = n5**(n1/n2) + assert n == sqrt(5) + e = n*a*b - n*b*a + assert e == Rational(0) + e = n*a*b + n*b*a + assert e == 2*a*b*sqrt(5) + assert e.diff(a) == 2*b*sqrt(5) + assert e.diff(a) == 2*b*sqrt(5) + e = a/b**2 + assert e == a*b**(-2) + + assert sqrt(2*(1 + sqrt(2))) == (2*(1 + 2**S.Half))**S.Half + + x = Symbol('x') + y = Symbol('y') + + assert ((x*y)**3).expand() == y**3 * x**3 + assert ((x*y)**-3).expand() == y**-3 * x**-3 + + assert (x**5*(3*x)**(3)).expand() == 27 * x**8 + assert (x**5*(-3*x)**(3)).expand() == -27 * x**8 + assert (x**5*(3*x)**(-3)).expand() == x**2 * Rational(1, 27) + assert (x**5*(-3*x)**(-3)).expand() == x**2 * Rational(-1, 27) + + # expand_power_exp + _x = Symbol('x', zero=False) + _y = Symbol('y', zero=False) + assert (_x**(y**(x + exp(x + y)) + z)).expand(deep=False) == \ + _x**z*_x**(y**(x + exp(x + y))) + assert (_x**(_y**(x + exp(x + y)) + z)).expand() == \ + _x**z*_x**(_y**x*_y**(exp(x)*exp(y))) + + n = Symbol('n', even=False) + k = Symbol('k', even=True) + o = Symbol('o', odd=True) + + assert unchanged(Pow, -1, x) + assert unchanged(Pow, -1, n) + assert (-2)**k == 2**k + assert (-1)**k == 1 + assert (-1)**o == -1 + + +def test_pow2(): + # x**(2*y) is always (x**y)**2 but is only (x**2)**y if + # x.is_positive or y.is_integer + # let x = 1 to see why the following are not true. + assert (-x)**Rational(2, 3) != x**Rational(2, 3) + assert (-x)**Rational(5, 7) != -x**Rational(5, 7) + assert ((-x)**2)**Rational(1, 3) != ((-x)**Rational(1, 3))**2 + assert sqrt(x**2) != x + + +def test_pow3(): + assert sqrt(2)**3 == 2 * sqrt(2) + assert sqrt(2)**3 == sqrt(8) + + +def test_mod_pow(): + for s, t, u, v in [(4, 13, 497, 445), (4, -3, 497, 365), + (3.2, 2.1, 1.9, 0.1031015682350942), (S(3)/2, 5, S(5)/6, S(3)/32)]: + assert pow(S(s), t, u) == v + assert pow(S(s), S(t), u) == v + assert pow(S(s), t, S(u)) == v + assert pow(S(s), S(t), S(u)) == v + assert pow(S(2), S(10000000000), S(3)) == 1 + assert pow(x, y, z) == x**y%z + raises(TypeError, lambda: pow(S(4), "13", 497)) + raises(TypeError, lambda: pow(S(4), 13, "497")) + + +def test_pow_E(): + assert 2**(y/log(2)) == S.Exp1**y + assert 2**(y/log(2)/3) == S.Exp1**(y/3) + assert 3**(1/log(-3)) != S.Exp1 + assert (3 + 2*I)**(1/(log(-3 - 2*I) + I*pi)) == S.Exp1 + assert (4 + 2*I)**(1/(log(-4 - 2*I) + I*pi)) == S.Exp1 + assert (3 + 2*I)**(1/(log(-3 - 2*I, 3)/2 + I*pi/log(3)/2)) == 9 + assert (3 + 2*I)**(1/(log(3 + 2*I, 3)/2)) == 9 + # every time tests are run they will affirm with a different random + # value that this identity holds + while 1: + b = x._random() + r, i = b.as_real_imag() + if i: + break + assert verify_numerically(b**(1/(log(-b) + sign(i)*I*pi).n()), S.Exp1) + + +def test_pow_issue_3516(): + assert 4**Rational(1, 4) == sqrt(2) + + +def test_pow_im(): + for m in (-2, -1, 2): + for d in (3, 4, 5): + b = m*I + for i in range(1, 4*d + 1): + e = Rational(i, d) + assert (b**e - b.n()**e.n()).n(2, chop=1e-10) == 0 + + e = Rational(7, 3) + assert (2*x*I)**e == 4*2**Rational(1, 3)*(I*x)**e # same as Wolfram Alpha + im = symbols('im', imaginary=True) + assert (2*im*I)**e == 4*2**Rational(1, 3)*(I*im)**e + + args = [I, I, I, I, 2] + e = Rational(1, 3) + ans = 2**e + assert Mul(*args, evaluate=False)**e == ans + assert Mul(*args)**e == ans + args = [I, I, I, 2] + e = Rational(1, 3) + ans = 2**e*(-I)**e + assert Mul(*args, evaluate=False)**e == ans + assert Mul(*args)**e == ans + args.append(-3) + ans = (6*I)**e + assert Mul(*args, evaluate=False)**e == ans + assert Mul(*args)**e == ans + args.append(-1) + ans = (-6*I)**e + assert Mul(*args, evaluate=False)**e == ans + assert Mul(*args)**e == ans + + args = [I, I, 2] + e = Rational(1, 3) + ans = (-2)**e + assert Mul(*args, evaluate=False)**e == ans + assert Mul(*args)**e == ans + args.append(-3) + ans = (6)**e + assert Mul(*args, evaluate=False)**e == ans + assert Mul(*args)**e == ans + args.append(-1) + ans = (-6)**e + assert Mul(*args, evaluate=False)**e == ans + assert Mul(*args)**e == ans + assert Mul(Pow(-1, Rational(3, 2), evaluate=False), I, I) == I + assert Mul(I*Pow(I, S.Half, evaluate=False)) == sqrt(I)*I + + +def test_real_mul(): + assert Float(0) * pi * x == 0 + assert set((Float(1) * pi * x).args) == {Float(1), pi, x} + + +def test_ncmul(): + A = Symbol("A", commutative=False) + B = Symbol("B", commutative=False) + C = Symbol("C", commutative=False) + assert A*B != B*A + assert A*B*C != C*B*A + assert A*b*B*3*C == 3*b*A*B*C + assert A*b*B*3*C != 3*b*B*A*C + assert A*b*B*3*C == 3*A*B*C*b + + assert A + B == B + A + assert (A + B)*C != C*(A + B) + + assert C*(A + B)*C != C*C*(A + B) + + assert A*A == A**2 + assert (A + B)*(A + B) == (A + B)**2 + + assert A**-1 * A == 1 + assert A/A == 1 + assert A/(A**2) == 1/A + + assert A/(1 + A) == A/(1 + A) + + assert set((A + B + 2*(A + B)).args) == \ + {A, B, 2*(A + B)} + + +def test_mul_add_identity(): + m = Mul(1, 2) + assert isinstance(m, Rational) and m.p == 2 and m.q == 1 + m = Mul(1, 2, evaluate=False) + assert isinstance(m, Mul) and m.args == (1, 2) + m = Mul(0, 1) + assert m is S.Zero + m = Mul(0, 1, evaluate=False) + assert isinstance(m, Mul) and m.args == (0, 1) + m = Add(0, 1) + assert m is S.One + m = Add(0, 1, evaluate=False) + assert isinstance(m, Add) and m.args == (0, 1) + + +def test_ncpow(): + x = Symbol('x', commutative=False) + y = Symbol('y', commutative=False) + z = Symbol('z', commutative=False) + a = Symbol('a') + b = Symbol('b') + c = Symbol('c') + + assert (x**2)*(y**2) != (y**2)*(x**2) + assert (x**-2)*y != y*(x**2) + assert 2**x*2**y != 2**(x + y) + assert 2**x*2**y*2**z != 2**(x + y + z) + assert 2**x*2**(2*x) == 2**(3*x) + assert 2**x*2**(2*x)*2**x == 2**(4*x) + assert exp(x)*exp(y) != exp(y)*exp(x) + assert exp(x)*exp(y)*exp(z) != exp(y)*exp(x)*exp(z) + assert exp(x)*exp(y)*exp(z) != exp(x + y + z) + assert x**a*x**b != x**(a + b) + assert x**a*x**b*x**c != x**(a + b + c) + assert x**3*x**4 == x**7 + assert x**3*x**4*x**2 == x**9 + assert x**a*x**(4*a) == x**(5*a) + assert x**a*x**(4*a)*x**a == x**(6*a) + + +def test_powerbug(): + x = Symbol("x") + assert x**1 != (-x)**1 + assert x**2 == (-x)**2 + assert x**3 != (-x)**3 + assert x**4 == (-x)**4 + assert x**5 != (-x)**5 + assert x**6 == (-x)**6 + + assert x**128 == (-x)**128 + assert x**129 != (-x)**129 + + assert (2*x)**2 == (-2*x)**2 + + +def test_Mul_doesnt_expand_exp(): + x = Symbol('x') + y = Symbol('y') + assert unchanged(Mul, exp(x), exp(y)) + assert unchanged(Mul, 2**x, 2**y) + assert x**2*x**3 == x**5 + assert 2**x*3**x == 6**x + assert x**(y)*x**(2*y) == x**(3*y) + assert sqrt(2)*sqrt(2) == 2 + assert 2**x*2**(2*x) == 2**(3*x) + assert sqrt(2)*2**Rational(1, 4)*5**Rational(3, 4) == 10**Rational(3, 4) + assert (x**(-log(5)/log(3))*x)/(x*x**( - log(5)/log(3))) == sympify(1) + + +def test_Mul_is_integer(): + k = Symbol('k', integer=True) + n = Symbol('n', integer=True) + nr = Symbol('nr', rational=False) + ir = Symbol('ir', irrational=True) + nz = Symbol('nz', integer=True, zero=False) + e = Symbol('e', even=True) + o = Symbol('o', odd=True) + i2 = Symbol('2', prime=True, even=True) + + assert (k/3).is_integer is None + assert (nz/3).is_integer is None + assert (nr/3).is_integer is False + assert (ir/3).is_integer is False + assert (x*k*n).is_integer is None + assert (e/2).is_integer is True + assert (e**2/2).is_integer is True + assert (2/k).is_integer is None + assert (2/k**2).is_integer is None + assert ((-1)**k*n).is_integer is True + assert (3*k*e/2).is_integer is True + assert (2*k*e/3).is_integer is None + assert (e/o).is_integer is None + assert (o/e).is_integer is False + assert (o/i2).is_integer is False + assert Mul(k, 1/k, evaluate=False).is_integer is None + assert Mul(2., S.Half, evaluate=False).is_integer is None + assert (2*sqrt(k)).is_integer is None + assert (2*k**n).is_integer is None + + s = 2**2**2**Pow(2, 1000, evaluate=False) + m = Mul(s, s, evaluate=False) + assert m.is_integer + + # broken in 1.6 and before, see #20161 + xq = Symbol('xq', rational=True) + yq = Symbol('yq', rational=True) + assert (xq*yq).is_integer is None + e_20161 = Mul(-1,Mul(1,Pow(2,-1,evaluate=False),evaluate=False),evaluate=False) + assert e_20161.is_integer is not True # expand(e_20161) -> -1/2, but no need to see that in the assumption without evaluation + + +def test_Add_Mul_is_integer(): + x = Symbol('x') + + k = Symbol('k', integer=True) + n = Symbol('n', integer=True) + nk = Symbol('nk', integer=False) + nr = Symbol('nr', rational=False) + nz = Symbol('nz', integer=True, zero=False) + + assert (-nk).is_integer is None + assert (-nr).is_integer is False + assert (2*k).is_integer is True + assert (-k).is_integer is True + + assert (k + nk).is_integer is False + assert (k + n).is_integer is True + assert (k + x).is_integer is None + assert (k + n*x).is_integer is None + assert (k + n/3).is_integer is None + assert (k + nz/3).is_integer is None + assert (k + nr/3).is_integer is False + + assert ((1 + sqrt(3))*(-sqrt(3) + 1)).is_integer is not False + assert (1 + (1 + sqrt(3))*(-sqrt(3) + 1)).is_integer is not False + + +def test_Add_Mul_is_finite(): + x = Symbol('x', extended_real=True, finite=False) + + assert sin(x).is_finite is True + assert (x*sin(x)).is_finite is None + assert (x*atan(x)).is_finite is False + assert (1024*sin(x)).is_finite is True + assert (sin(x)*exp(x)).is_finite is None + assert (sin(x)*cos(x)).is_finite is True + assert (x*sin(x)*exp(x)).is_finite is None + + assert (sin(x) - 67).is_finite is True + assert (sin(x) + exp(x)).is_finite is not True + assert (1 + x).is_finite is False + assert (1 + x**2 + (1 + x)*(1 - x)).is_finite is None + assert (sqrt(2)*(1 + x)).is_finite is False + assert (sqrt(2)*(1 + x)*(1 - x)).is_finite is False + + +def test_Mul_is_even_odd(): + x = Symbol('x', integer=True) + y = Symbol('y', integer=True) + + k = Symbol('k', odd=True) + n = Symbol('n', odd=True) + m = Symbol('m', even=True) + + assert (2*x).is_even is True + assert (2*x).is_odd is False + + assert (3*x).is_even is None + assert (3*x).is_odd is None + + assert (k/3).is_integer is None + assert (k/3).is_even is None + assert (k/3).is_odd is None + + assert (2*n).is_even is True + assert (2*n).is_odd is False + + assert (2*m).is_even is True + assert (2*m).is_odd is False + + assert (-n).is_even is False + assert (-n).is_odd is True + + assert (k*n).is_even is False + assert (k*n).is_odd is True + + assert (k*m).is_even is True + assert (k*m).is_odd is False + + assert (k*n*m).is_even is True + assert (k*n*m).is_odd is False + + assert (k*m*x).is_even is True + assert (k*m*x).is_odd is False + + # issue 6791: + assert (x/2).is_integer is None + assert (k/2).is_integer is False + assert (m/2).is_integer is True + + assert (x*y).is_even is None + assert (x*x).is_even is None + assert (x*(x + k)).is_even is True + assert (x*(x + m)).is_even is None + + assert (x*y).is_odd is None + assert (x*x).is_odd is None + assert (x*(x + k)).is_odd is False + assert (x*(x + m)).is_odd is None + + # issue 8648 + assert (m**2/2).is_even + assert (m**2/3).is_even is False + assert (2/m**2).is_odd is False + assert (2/m).is_odd is None + + +@XFAIL +def test_evenness_in_ternary_integer_product_with_odd(): + # Tests that oddness inference is independent of term ordering. + # Term ordering at the point of testing depends on SymPy's symbol order, so + # we try to force a different order by modifying symbol names. + x = Symbol('x', integer=True) + y = Symbol('y', integer=True) + k = Symbol('k', odd=True) + assert (x*y*(y + k)).is_even is True + assert (y*x*(x + k)).is_even is True + + +def test_evenness_in_ternary_integer_product_with_even(): + x = Symbol('x', integer=True) + y = Symbol('y', integer=True) + m = Symbol('m', even=True) + assert (x*y*(y + m)).is_even is None + + +@XFAIL +def test_oddness_in_ternary_integer_product_with_odd(): + # Tests that oddness inference is independent of term ordering. + # Term ordering at the point of testing depends on SymPy's symbol order, so + # we try to force a different order by modifying symbol names. + x = Symbol('x', integer=True) + y = Symbol('y', integer=True) + k = Symbol('k', odd=True) + assert (x*y*(y + k)).is_odd is False + assert (y*x*(x + k)).is_odd is False + + +def test_oddness_in_ternary_integer_product_with_even(): + x = Symbol('x', integer=True) + y = Symbol('y', integer=True) + m = Symbol('m', even=True) + assert (x*y*(y + m)).is_odd is None + + +def test_Mul_is_rational(): + x = Symbol('x') + n = Symbol('n', integer=True) + m = Symbol('m', integer=True, nonzero=True) + + assert (n/m).is_rational is True + assert (x/pi).is_rational is None + assert (x/n).is_rational is None + assert (m/pi).is_rational is False + + r = Symbol('r', rational=True) + assert (pi*r).is_rational is None + + # issue 8008 + z = Symbol('z', zero=True) + i = Symbol('i', imaginary=True) + assert (z*i).is_rational is True + bi = Symbol('i', imaginary=True, finite=True) + assert (z*bi).is_zero is True + + +def test_Add_is_rational(): + x = Symbol('x') + n = Symbol('n', rational=True) + m = Symbol('m', rational=True) + + assert (n + m).is_rational is True + assert (x + pi).is_rational is None + assert (x + n).is_rational is None + assert (n + pi).is_rational is False + + +def test_Add_is_even_odd(): + x = Symbol('x', integer=True) + + k = Symbol('k', odd=True) + n = Symbol('n', odd=True) + m = Symbol('m', even=True) + + assert (k + 7).is_even is True + assert (k + 7).is_odd is False + + assert (-k + 7).is_even is True + assert (-k + 7).is_odd is False + + assert (k - 12).is_even is False + assert (k - 12).is_odd is True + + assert (-k - 12).is_even is False + assert (-k - 12).is_odd is True + + assert (k + n).is_even is True + assert (k + n).is_odd is False + + assert (k + m).is_even is False + assert (k + m).is_odd is True + + assert (k + n + m).is_even is True + assert (k + n + m).is_odd is False + + assert (k + n + x + m).is_even is None + assert (k + n + x + m).is_odd is None + + +def test_Mul_is_negative_positive(): + x = Symbol('x', real=True) + y = Symbol('y', extended_real=False, complex=True) + z = Symbol('z', zero=True) + + e = 2*z + assert e.is_Mul and e.is_positive is False and e.is_negative is False + + neg = Symbol('neg', negative=True) + pos = Symbol('pos', positive=True) + nneg = Symbol('nneg', nonnegative=True) + npos = Symbol('npos', nonpositive=True) + + assert neg.is_negative is True + assert (-neg).is_negative is False + assert (2*neg).is_negative is True + + assert (2*pos)._eval_is_extended_negative() is False + assert (2*pos).is_negative is False + + assert pos.is_negative is False + assert (-pos).is_negative is True + assert (2*pos).is_negative is False + + assert (pos*neg).is_negative is True + assert (2*pos*neg).is_negative is True + assert (-pos*neg).is_negative is False + assert (pos*neg*y).is_negative is False # y.is_real=F; !real -> !neg + + assert nneg.is_negative is False + assert (-nneg).is_negative is None + assert (2*nneg).is_negative is False + + assert npos.is_negative is None + assert (-npos).is_negative is False + assert (2*npos).is_negative is None + + assert (nneg*npos).is_negative is None + + assert (neg*nneg).is_negative is None + assert (neg*npos).is_negative is False + + assert (pos*nneg).is_negative is False + assert (pos*npos).is_negative is None + + assert (npos*neg*nneg).is_negative is False + assert (npos*pos*nneg).is_negative is None + + assert (-npos*neg*nneg).is_negative is None + assert (-npos*pos*nneg).is_negative is False + + assert (17*npos*neg*nneg).is_negative is False + assert (17*npos*pos*nneg).is_negative is None + + assert (neg*npos*pos*nneg).is_negative is False + + assert (x*neg).is_negative is None + assert (nneg*npos*pos*x*neg).is_negative is None + + assert neg.is_positive is False + assert (-neg).is_positive is True + assert (2*neg).is_positive is False + + assert pos.is_positive is True + assert (-pos).is_positive is False + assert (2*pos).is_positive is True + + assert (pos*neg).is_positive is False + assert (2*pos*neg).is_positive is False + assert (-pos*neg).is_positive is True + assert (-pos*neg*y).is_positive is False # y.is_real=F; !real -> !neg + + assert nneg.is_positive is None + assert (-nneg).is_positive is False + assert (2*nneg).is_positive is None + + assert npos.is_positive is False + assert (-npos).is_positive is None + assert (2*npos).is_positive is False + + assert (nneg*npos).is_positive is False + + assert (neg*nneg).is_positive is False + assert (neg*npos).is_positive is None + + assert (pos*nneg).is_positive is None + assert (pos*npos).is_positive is False + + assert (npos*neg*nneg).is_positive is None + assert (npos*pos*nneg).is_positive is False + + assert (-npos*neg*nneg).is_positive is False + assert (-npos*pos*nneg).is_positive is None + + assert (17*npos*neg*nneg).is_positive is None + assert (17*npos*pos*nneg).is_positive is False + + assert (neg*npos*pos*nneg).is_positive is None + + assert (x*neg).is_positive is None + assert (nneg*npos*pos*x*neg).is_positive is None + + +def test_Mul_is_negative_positive_2(): + a = Symbol('a', nonnegative=True) + b = Symbol('b', nonnegative=True) + c = Symbol('c', nonpositive=True) + d = Symbol('d', nonpositive=True) + + assert (a*b).is_nonnegative is True + assert (a*b).is_negative is False + assert (a*b).is_zero is None + assert (a*b).is_positive is None + + assert (c*d).is_nonnegative is True + assert (c*d).is_negative is False + assert (c*d).is_zero is None + assert (c*d).is_positive is None + + assert (a*c).is_nonpositive is True + assert (a*c).is_positive is False + assert (a*c).is_zero is None + assert (a*c).is_negative is None + + +def test_Mul_is_nonpositive_nonnegative(): + x = Symbol('x', real=True) + + k = Symbol('k', negative=True) + n = Symbol('n', positive=True) + u = Symbol('u', nonnegative=True) + v = Symbol('v', nonpositive=True) + + assert k.is_nonpositive is True + assert (-k).is_nonpositive is False + assert (2*k).is_nonpositive is True + + assert n.is_nonpositive is False + assert (-n).is_nonpositive is True + assert (2*n).is_nonpositive is False + + assert (n*k).is_nonpositive is True + assert (2*n*k).is_nonpositive is True + assert (-n*k).is_nonpositive is False + + assert u.is_nonpositive is None + assert (-u).is_nonpositive is True + assert (2*u).is_nonpositive is None + + assert v.is_nonpositive is True + assert (-v).is_nonpositive is None + assert (2*v).is_nonpositive is True + + assert (u*v).is_nonpositive is True + + assert (k*u).is_nonpositive is True + assert (k*v).is_nonpositive is None + + assert (n*u).is_nonpositive is None + assert (n*v).is_nonpositive is True + + assert (v*k*u).is_nonpositive is None + assert (v*n*u).is_nonpositive is True + + assert (-v*k*u).is_nonpositive is True + assert (-v*n*u).is_nonpositive is None + + assert (17*v*k*u).is_nonpositive is None + assert (17*v*n*u).is_nonpositive is True + + assert (k*v*n*u).is_nonpositive is None + + assert (x*k).is_nonpositive is None + assert (u*v*n*x*k).is_nonpositive is None + + assert k.is_nonnegative is False + assert (-k).is_nonnegative is True + assert (2*k).is_nonnegative is False + + assert n.is_nonnegative is True + assert (-n).is_nonnegative is False + assert (2*n).is_nonnegative is True + + assert (n*k).is_nonnegative is False + assert (2*n*k).is_nonnegative is False + assert (-n*k).is_nonnegative is True + + assert u.is_nonnegative is True + assert (-u).is_nonnegative is None + assert (2*u).is_nonnegative is True + + assert v.is_nonnegative is None + assert (-v).is_nonnegative is True + assert (2*v).is_nonnegative is None + + assert (u*v).is_nonnegative is None + + assert (k*u).is_nonnegative is None + assert (k*v).is_nonnegative is True + + assert (n*u).is_nonnegative is True + assert (n*v).is_nonnegative is None + + assert (v*k*u).is_nonnegative is True + assert (v*n*u).is_nonnegative is None + + assert (-v*k*u).is_nonnegative is None + assert (-v*n*u).is_nonnegative is True + + assert (17*v*k*u).is_nonnegative is True + assert (17*v*n*u).is_nonnegative is None + + assert (k*v*n*u).is_nonnegative is True + + assert (x*k).is_nonnegative is None + assert (u*v*n*x*k).is_nonnegative is None + + +def test_Add_is_negative_positive(): + x = Symbol('x', real=True) + + k = Symbol('k', negative=True) + n = Symbol('n', positive=True) + u = Symbol('u', nonnegative=True) + v = Symbol('v', nonpositive=True) + + assert (k - 2).is_negative is True + assert (k + 17).is_negative is None + assert (-k - 5).is_negative is None + assert (-k + 123).is_negative is False + + assert (k - n).is_negative is True + assert (k + n).is_negative is None + assert (-k - n).is_negative is None + assert (-k + n).is_negative is False + + assert (k - n - 2).is_negative is True + assert (k + n + 17).is_negative is None + assert (-k - n - 5).is_negative is None + assert (-k + n + 123).is_negative is False + + assert (-2*k + 123*n + 17).is_negative is False + + assert (k + u).is_negative is None + assert (k + v).is_negative is True + assert (n + u).is_negative is False + assert (n + v).is_negative is None + + assert (u - v).is_negative is False + assert (u + v).is_negative is None + assert (-u - v).is_negative is None + assert (-u + v).is_negative is None + + assert (u - v + n + 2).is_negative is False + assert (u + v + n + 2).is_negative is None + assert (-u - v + n + 2).is_negative is None + assert (-u + v + n + 2).is_negative is None + + assert (k + x).is_negative is None + assert (k + x - n).is_negative is None + + assert (k - 2).is_positive is False + assert (k + 17).is_positive is None + assert (-k - 5).is_positive is None + assert (-k + 123).is_positive is True + + assert (k - n).is_positive is False + assert (k + n).is_positive is None + assert (-k - n).is_positive is None + assert (-k + n).is_positive is True + + assert (k - n - 2).is_positive is False + assert (k + n + 17).is_positive is None + assert (-k - n - 5).is_positive is None + assert (-k + n + 123).is_positive is True + + assert (-2*k + 123*n + 17).is_positive is True + + assert (k + u).is_positive is None + assert (k + v).is_positive is False + assert (n + u).is_positive is True + assert (n + v).is_positive is None + + assert (u - v).is_positive is None + assert (u + v).is_positive is None + assert (-u - v).is_positive is None + assert (-u + v).is_positive is False + + assert (u - v - n - 2).is_positive is None + assert (u + v - n - 2).is_positive is None + assert (-u - v - n - 2).is_positive is None + assert (-u + v - n - 2).is_positive is False + + assert (n + x).is_positive is None + assert (n + x - k).is_positive is None + + z = (-3 - sqrt(5) + (-sqrt(10)/2 - sqrt(2)/2)**2) + assert z.is_zero + z = sqrt(1 + sqrt(3)) + sqrt(3 + 3*sqrt(3)) - sqrt(10 + 6*sqrt(3)) + assert z.is_zero + + +def test_Add_is_nonpositive_nonnegative(): + x = Symbol('x', real=True) + + k = Symbol('k', negative=True) + n = Symbol('n', positive=True) + u = Symbol('u', nonnegative=True) + v = Symbol('v', nonpositive=True) + + assert (u - 2).is_nonpositive is None + assert (u + 17).is_nonpositive is False + assert (-u - 5).is_nonpositive is True + assert (-u + 123).is_nonpositive is None + + assert (u - v).is_nonpositive is None + assert (u + v).is_nonpositive is None + assert (-u - v).is_nonpositive is None + assert (-u + v).is_nonpositive is True + + assert (u - v - 2).is_nonpositive is None + assert (u + v + 17).is_nonpositive is None + assert (-u - v - 5).is_nonpositive is None + assert (-u + v - 123).is_nonpositive is True + + assert (-2*u + 123*v - 17).is_nonpositive is True + + assert (k + u).is_nonpositive is None + assert (k + v).is_nonpositive is True + assert (n + u).is_nonpositive is False + assert (n + v).is_nonpositive is None + + assert (k - n).is_nonpositive is True + assert (k + n).is_nonpositive is None + assert (-k - n).is_nonpositive is None + assert (-k + n).is_nonpositive is False + + assert (k - n + u + 2).is_nonpositive is None + assert (k + n + u + 2).is_nonpositive is None + assert (-k - n + u + 2).is_nonpositive is None + assert (-k + n + u + 2).is_nonpositive is False + + assert (u + x).is_nonpositive is None + assert (v - x - n).is_nonpositive is None + + assert (u - 2).is_nonnegative is None + assert (u + 17).is_nonnegative is True + assert (-u - 5).is_nonnegative is False + assert (-u + 123).is_nonnegative is None + + assert (u - v).is_nonnegative is True + assert (u + v).is_nonnegative is None + assert (-u - v).is_nonnegative is None + assert (-u + v).is_nonnegative is None + + assert (u - v + 2).is_nonnegative is True + assert (u + v + 17).is_nonnegative is None + assert (-u - v - 5).is_nonnegative is None + assert (-u + v - 123).is_nonnegative is False + + assert (2*u - 123*v + 17).is_nonnegative is True + + assert (k + u).is_nonnegative is None + assert (k + v).is_nonnegative is False + assert (n + u).is_nonnegative is True + assert (n + v).is_nonnegative is None + + assert (k - n).is_nonnegative is False + assert (k + n).is_nonnegative is None + assert (-k - n).is_nonnegative is None + assert (-k + n).is_nonnegative is True + + assert (k - n - u - 2).is_nonnegative is False + assert (k + n - u - 2).is_nonnegative is None + assert (-k - n - u - 2).is_nonnegative is None + assert (-k + n - u - 2).is_nonnegative is None + + assert (u - x).is_nonnegative is None + assert (v + x + n).is_nonnegative is None + + +def test_Pow_is_integer(): + x = Symbol('x') + + k = Symbol('k', integer=True) + n = Symbol('n', integer=True, nonnegative=True) + m = Symbol('m', integer=True, positive=True) + + assert (k**2).is_integer is True + assert (k**(-2)).is_integer is None + assert ((m + 1)**(-2)).is_integer is False + assert (m**(-1)).is_integer is None # issue 8580 + + assert (2**k).is_integer is None + assert (2**(-k)).is_integer is None + + assert (2**n).is_integer is True + assert (2**(-n)).is_integer is None + + assert (2**m).is_integer is True + assert (2**(-m)).is_integer is False + + assert (x**2).is_integer is None + assert (2**x).is_integer is None + + assert (k**n).is_integer is True + assert (k**(-n)).is_integer is None + + assert (k**x).is_integer is None + assert (x**k).is_integer is None + + assert (k**(n*m)).is_integer is True + assert (k**(-n*m)).is_integer is None + + assert sqrt(3).is_integer is False + assert sqrt(.3).is_integer is False + assert Pow(3, 2, evaluate=False).is_integer is True + assert Pow(3, 0, evaluate=False).is_integer is True + assert Pow(3, -2, evaluate=False).is_integer is False + assert Pow(S.Half, 3, evaluate=False).is_integer is False + # decided by re-evaluating + assert Pow(3, S.Half, evaluate=False).is_integer is False + assert Pow(3, S.Half, evaluate=False).is_integer is False + assert Pow(4, S.Half, evaluate=False).is_integer is True + assert Pow(S.Half, -2, evaluate=False).is_integer is True + + assert ((-1)**k).is_integer + + # issue 8641 + x = Symbol('x', real=True, integer=False) + assert (x**2).is_integer is None + + # issue 10458 + x = Symbol('x', positive=True) + assert (1/(x + 1)).is_integer is False + assert (1/(-x - 1)).is_integer is False + assert (-1/(x + 1)).is_integer is False + # issue 23287 + assert (x**2/2).is_integer is None + + # issue 8648-like + k = Symbol('k', even=True) + assert (k**3/2).is_integer + assert (k**3/8).is_integer + assert (k**3/16).is_integer is None + assert (2/k).is_integer is None + assert (2/k**2).is_integer is False + o = Symbol('o', odd=True) + assert (k/o).is_integer is None + o = Symbol('o', odd=True, prime=True) + assert (k/o).is_integer is False + + +def test_Pow_is_real(): + x = Symbol('x', real=True) + y = Symbol('y', positive=True) + + assert (x**2).is_real is True + assert (x**3).is_real is True + assert (x**x).is_real is None + assert (y**x).is_real is True + + assert (x**Rational(1, 3)).is_real is None + assert (y**Rational(1, 3)).is_real is True + + assert sqrt(-1 - sqrt(2)).is_real is False + + i = Symbol('i', imaginary=True) + assert (i**i).is_real is None + assert (I**i).is_extended_real is True + assert ((-I)**i).is_extended_real is True + assert (2**i).is_real is None # (2**(pi/log(2) * I)) is real, 2**I is not + assert (2**I).is_real is False + assert (2**-I).is_real is False + assert (i**2).is_extended_real is True + assert (i**3).is_extended_real is False + assert (i**x).is_real is None # could be (-I)**(2/3) + e = Symbol('e', even=True) + o = Symbol('o', odd=True) + k = Symbol('k', integer=True) + assert (i**e).is_extended_real is True + assert (i**o).is_extended_real is False + assert (i**k).is_real is None + assert (i**(4*k)).is_extended_real is True + + x = Symbol("x", nonnegative=True) + y = Symbol("y", nonnegative=True) + assert im(x**y).expand(complex=True) is S.Zero + assert (x**y).is_real is True + i = Symbol('i', imaginary=True) + assert (exp(i)**I).is_extended_real is True + assert log(exp(i)).is_imaginary is None # i could be 2*pi*I + c = Symbol('c', complex=True) + assert log(c).is_real is None # c could be 0 or 2, too + assert log(exp(c)).is_real is None # log(0), log(E), ... + n = Symbol('n', negative=False) + assert log(n).is_real is None + n = Symbol('n', nonnegative=True) + assert log(n).is_real is None + + assert sqrt(-I).is_real is False # issue 7843 + + i = Symbol('i', integer=True) + assert (1/(i-1)).is_real is None + assert (1/(i-1)).is_extended_real is None + + # test issue 20715 + from sympy.core.parameters import evaluate + x = S(-1) + with evaluate(False): + assert x.is_negative is True + + f = Pow(x, -1) + with evaluate(False): + assert f.is_imaginary is False + + +def test_real_Pow(): + k = Symbol('k', integer=True, nonzero=True) + assert (k**(I*pi/log(k))).is_real + + +def test_Pow_is_finite(): + xe = Symbol('xe', extended_real=True) + xr = Symbol('xr', real=True) + p = Symbol('p', positive=True) + n = Symbol('n', negative=True) + i = Symbol('i', integer=True) + + assert (xe**2).is_finite is None # xe could be oo + assert (xr**2).is_finite is True + + assert (xe**xe).is_finite is None + assert (xr**xe).is_finite is None + assert (xe**xr).is_finite is None + # FIXME: The line below should be True rather than None + # assert (xr**xr).is_finite is True + assert (xr**xr).is_finite is None + + assert (p**xe).is_finite is None + assert (p**xr).is_finite is True + + assert (n**xe).is_finite is None + assert (n**xr).is_finite is True + + assert (sin(xe)**2).is_finite is True + assert (sin(xr)**2).is_finite is True + + assert (sin(xe)**xe).is_finite is None # xe, xr could be -pi + assert (sin(xr)**xr).is_finite is None + + # FIXME: Should the line below be True rather than None? + assert (sin(xe)**exp(xe)).is_finite is None + assert (sin(xr)**exp(xr)).is_finite is True + + assert (1/sin(xe)).is_finite is None # if zero, no, otherwise yes + assert (1/sin(xr)).is_finite is None + + assert (1/exp(xe)).is_finite is None # xe could be -oo + assert (1/exp(xr)).is_finite is True + + assert (1/S.Pi).is_finite is True + + assert (1/(i-1)).is_finite is None + + +def test_Pow_is_even_odd(): + x = Symbol('x') + + k = Symbol('k', even=True) + n = Symbol('n', odd=True) + m = Symbol('m', integer=True, nonnegative=True) + p = Symbol('p', integer=True, positive=True) + + assert ((-1)**n).is_odd + assert ((-1)**k).is_odd + assert ((-1)**(m - p)).is_odd + + assert (k**2).is_even is True + assert (n**2).is_even is False + assert (2**k).is_even is None + assert (x**2).is_even is None + + assert (k**m).is_even is None + assert (n**m).is_even is False + + assert (k**p).is_even is True + assert (n**p).is_even is False + + assert (m**k).is_even is None + assert (p**k).is_even is None + + assert (m**n).is_even is None + assert (p**n).is_even is None + + assert (k**x).is_even is None + assert (n**x).is_even is None + + assert (k**2).is_odd is False + assert (n**2).is_odd is True + assert (3**k).is_odd is None + + assert (k**m).is_odd is None + assert (n**m).is_odd is True + + assert (k**p).is_odd is False + assert (n**p).is_odd is True + + assert (m**k).is_odd is None + assert (p**k).is_odd is None + + assert (m**n).is_odd is None + assert (p**n).is_odd is None + + assert (k**x).is_odd is None + assert (n**x).is_odd is None + + +def test_Pow_is_negative_positive(): + r = Symbol('r', real=True) + + k = Symbol('k', integer=True, positive=True) + n = Symbol('n', even=True) + m = Symbol('m', odd=True) + + x = Symbol('x') + + assert (2**r).is_positive is True + assert ((-2)**r).is_positive is None + assert ((-2)**n).is_positive is True + assert ((-2)**m).is_positive is False + + assert (k**2).is_positive is True + assert (k**(-2)).is_positive is True + + assert (k**r).is_positive is True + assert ((-k)**r).is_positive is None + assert ((-k)**n).is_positive is True + assert ((-k)**m).is_positive is False + + assert (2**r).is_negative is False + assert ((-2)**r).is_negative is None + assert ((-2)**n).is_negative is False + assert ((-2)**m).is_negative is True + + assert (k**2).is_negative is False + assert (k**(-2)).is_negative is False + + assert (k**r).is_negative is False + assert ((-k)**r).is_negative is None + assert ((-k)**n).is_negative is False + assert ((-k)**m).is_negative is True + + assert (2**x).is_positive is None + assert (2**x).is_negative is None + + +def test_Pow_is_zero(): + z = Symbol('z', zero=True) + e = z**2 + assert e.is_zero + assert e.is_positive is False + assert e.is_negative is False + + assert Pow(0, 0, evaluate=False).is_zero is False + assert Pow(0, 3, evaluate=False).is_zero + assert Pow(0, oo, evaluate=False).is_zero + assert Pow(0, -3, evaluate=False).is_zero is False + assert Pow(0, -oo, evaluate=False).is_zero is False + assert Pow(2, 2, evaluate=False).is_zero is False + + a = Symbol('a', zero=False) + assert Pow(a, 3).is_zero is False # issue 7965 + + assert Pow(2, oo, evaluate=False).is_zero is False + assert Pow(2, -oo, evaluate=False).is_zero + assert Pow(S.Half, oo, evaluate=False).is_zero + assert Pow(S.Half, -oo, evaluate=False).is_zero is False + + # All combinations of real/complex base/exponent + h = S.Half + T = True + F = False + N = None + + pow_iszero = [ + ['**', 0, h, 1, 2, -h, -1,-2,-2*I,-I/2,I/2,1+I,oo,-oo,zoo], + [ 0, F, T, T, T, F, F, F, F, F, F, N, T, F, N], + [ h, F, F, F, F, F, F, F, F, F, F, F, T, F, N], + [ 1, F, F, F, F, F, F, F, F, F, F, F, F, F, N], + [ 2, F, F, F, F, F, F, F, F, F, F, F, F, T, N], + [ -h, F, F, F, F, F, F, F, F, F, F, F, T, F, N], + [ -1, F, F, F, F, F, F, F, F, F, F, F, F, F, N], + [ -2, F, F, F, F, F, F, F, F, F, F, F, F, T, N], + [-2*I, F, F, F, F, F, F, F, F, F, F, F, F, T, N], + [-I/2, F, F, F, F, F, F, F, F, F, F, F, T, F, N], + [ I/2, F, F, F, F, F, F, F, F, F, F, F, T, F, N], + [ 1+I, F, F, F, F, F, F, F, F, F, F, F, F, T, N], + [ oo, F, F, F, F, T, T, T, F, F, F, F, F, T, N], + [ -oo, F, F, F, F, T, T, T, F, F, F, F, F, T, N], + [ zoo, F, F, F, F, T, T, T, N, N, N, N, F, T, N] + ] + + def test_table(table): + n = len(table[0]) + for row in range(1, n): + base = table[row][0] + for col in range(1, n): + exp = table[0][col] + is_zero = table[row][col] + # The actual test here: + assert Pow(base, exp, evaluate=False).is_zero is is_zero + + test_table(pow_iszero) + + # A zero symbol... + zo, zo2 = symbols('zo, zo2', zero=True) + + # All combinations of finite symbols + zf, zf2 = symbols('zf, zf2', finite=True) + wf, wf2 = symbols('wf, wf2', nonzero=True) + xf, xf2 = symbols('xf, xf2', real=True) + yf, yf2 = symbols('yf, yf2', nonzero=True) + af, af2 = symbols('af, af2', positive=True) + bf, bf2 = symbols('bf, bf2', nonnegative=True) + cf, cf2 = symbols('cf, cf2', negative=True) + df, df2 = symbols('df, df2', nonpositive=True) + + # Without finiteness: + zi, zi2 = symbols('zi, zi2') + wi, wi2 = symbols('wi, wi2', zero=False) + xi, xi2 = symbols('xi, xi2', extended_real=True) + yi, yi2 = symbols('yi, yi2', zero=False, extended_real=True) + ai, ai2 = symbols('ai, ai2', extended_positive=True) + bi, bi2 = symbols('bi, bi2', extended_nonnegative=True) + ci, ci2 = symbols('ci, ci2', extended_negative=True) + di, di2 = symbols('di, di2', extended_nonpositive=True) + + pow_iszero_sym = [ + ['**',zo,wf,yf,af,cf,zf,xf,bf,df,zi,wi,xi,yi,ai,bi,ci,di], + [ zo2, F, N, N, T, F, N, N, N, F, N, N, N, N, T, N, F, F], + [ wf2, F, F, F, F, F, F, F, F, F, N, N, N, N, N, N, N, N], + [ yf2, F, F, F, F, F, F, F, F, F, N, N, N, N, N, N, N, N], + [ af2, F, F, F, F, F, F, F, F, F, N, N, N, N, N, N, N, N], + [ cf2, F, F, F, F, F, F, F, F, F, N, N, N, N, N, N, N, N], + [ zf2, N, N, N, N, F, N, N, N, N, N, N, N, N, N, N, N, N], + [ xf2, N, N, N, N, F, N, N, N, N, N, N, N, N, N, N, N, N], + [ bf2, N, N, N, N, F, N, N, N, N, N, N, N, N, N, N, N, N], + [ df2, N, N, N, N, F, N, N, N, N, N, N, N, N, N, N, N, N], + [ zi2, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N], + [ wi2, F, N, N, F, N, N, N, F, N, N, N, N, N, N, N, N, N], + [ xi2, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N], + [ yi2, F, N, N, F, N, N, N, F, N, N, N, N, N, N, N, N, N], + [ ai2, F, N, N, F, N, N, N, F, N, N, N, N, N, N, N, N, N], + [ bi2, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N], + [ ci2, F, N, N, F, N, N, N, F, N, N, N, N, N, N, N, N, N], + [ di2, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N] + ] + + test_table(pow_iszero_sym) + + # In some cases (x**x).is_zero is different from (x**y).is_zero even if y + # has the same assumptions as x. + assert (zo ** zo).is_zero is False + assert (wf ** wf).is_zero is False + assert (yf ** yf).is_zero is False + assert (af ** af).is_zero is False + assert (cf ** cf).is_zero is False + assert (zf ** zf).is_zero is None + assert (xf ** xf).is_zero is None + assert (bf ** bf).is_zero is False # None in table + assert (df ** df).is_zero is None + assert (zi ** zi).is_zero is None + assert (wi ** wi).is_zero is None + assert (xi ** xi).is_zero is None + assert (yi ** yi).is_zero is None + assert (ai ** ai).is_zero is False # None in table + assert (bi ** bi).is_zero is False # None in table + assert (ci ** ci).is_zero is None + assert (di ** di).is_zero is None + + +def test_Pow_is_nonpositive_nonnegative(): + x = Symbol('x', real=True) + + k = Symbol('k', integer=True, nonnegative=True) + l = Symbol('l', integer=True, positive=True) + n = Symbol('n', even=True) + m = Symbol('m', odd=True) + + assert (x**(4*k)).is_nonnegative is True + assert (2**x).is_nonnegative is True + assert ((-2)**x).is_nonnegative is None + assert ((-2)**n).is_nonnegative is True + assert ((-2)**m).is_nonnegative is False + + assert (k**2).is_nonnegative is True + assert (k**(-2)).is_nonnegative is None + assert (k**k).is_nonnegative is True + + assert (k**x).is_nonnegative is None # NOTE (0**x).is_real = U + assert (l**x).is_nonnegative is True + assert (l**x).is_positive is True + assert ((-k)**x).is_nonnegative is None + + assert ((-k)**m).is_nonnegative is None + + assert (2**x).is_nonpositive is False + assert ((-2)**x).is_nonpositive is None + assert ((-2)**n).is_nonpositive is False + assert ((-2)**m).is_nonpositive is True + + assert (k**2).is_nonpositive is None + assert (k**(-2)).is_nonpositive is None + + assert (k**x).is_nonpositive is None + assert ((-k)**x).is_nonpositive is None + assert ((-k)**n).is_nonpositive is None + + + assert (x**2).is_nonnegative is True + i = symbols('i', imaginary=True) + assert (i**2).is_nonpositive is True + assert (i**4).is_nonpositive is False + assert (i**3).is_nonpositive is False + assert (I**i).is_nonnegative is True + assert (exp(I)**i).is_nonnegative is True + + assert ((-l)**n).is_nonnegative is True + assert ((-l)**m).is_nonpositive is True + assert ((-k)**n).is_nonnegative is None + assert ((-k)**m).is_nonpositive is None + + +def test_Mul_is_imaginary_real(): + r = Symbol('r', real=True) + p = Symbol('p', positive=True) + i1 = Symbol('i1', imaginary=True) + i2 = Symbol('i2', imaginary=True) + x = Symbol('x') + + assert I.is_imaginary is True + assert I.is_real is False + assert (-I).is_imaginary is True + assert (-I).is_real is False + assert (3*I).is_imaginary is True + assert (3*I).is_real is False + assert (I*I).is_imaginary is False + assert (I*I).is_real is True + + e = (p + p*I) + j = Symbol('j', integer=True, zero=False) + assert (e**j).is_real is None + assert (e**(2*j)).is_real is None + assert (e**j).is_imaginary is None + assert (e**(2*j)).is_imaginary is None + + assert (e**-1).is_imaginary is False + assert (e**2).is_imaginary + assert (e**3).is_imaginary is False + assert (e**4).is_imaginary is False + assert (e**5).is_imaginary is False + assert (e**-1).is_real is False + assert (e**2).is_real is False + assert (e**3).is_real is False + assert (e**4).is_real is True + assert (e**5).is_real is False + assert (e**3).is_complex + + assert (r*i1).is_imaginary is None + assert (r*i1).is_real is None + + assert (x*i1).is_imaginary is None + assert (x*i1).is_real is None + + assert (i1*i2).is_imaginary is False + assert (i1*i2).is_real is True + + assert (r*i1*i2).is_imaginary is False + assert (r*i1*i2).is_real is True + + # Github's issue 5874: + nr = Symbol('nr', real=False, complex=True) # e.g. I or 1 + I + a = Symbol('a', real=True, nonzero=True) + b = Symbol('b', real=True) + assert (i1*nr).is_real is None + assert (a*nr).is_real is False + assert (b*nr).is_real is None + + ni = Symbol('ni', imaginary=False, complex=True) # e.g. 2 or 1 + I + a = Symbol('a', real=True, nonzero=True) + b = Symbol('b', real=True) + assert (i1*ni).is_real is False + assert (a*ni).is_real is None + assert (b*ni).is_real is None + + +def test_Mul_hermitian_antihermitian(): + xz, yz = symbols('xz, yz', zero=True, antihermitian=True) + xf, yf = symbols('xf, yf', hermitian=False, antihermitian=False, finite=True) + xh, yh = symbols('xh, yh', hermitian=True, antihermitian=False, nonzero=True) + xa, ya = symbols('xa, ya', hermitian=False, antihermitian=True, zero=False, finite=True) + assert (xz*xh).is_hermitian is True + assert (xz*xh).is_antihermitian is True + assert (xz*xa).is_hermitian is True + assert (xz*xa).is_antihermitian is True + assert (xf*yf).is_hermitian is None + assert (xf*yf).is_antihermitian is None + assert (xh*yh).is_hermitian is True + assert (xh*yh).is_antihermitian is False + assert (xh*ya).is_hermitian is False + assert (xh*ya).is_antihermitian is True + assert (xa*ya).is_hermitian is True + assert (xa*ya).is_antihermitian is False + + a = Symbol('a', hermitian=True, zero=False) + b = Symbol('b', hermitian=True) + c = Symbol('c', hermitian=False) + d = Symbol('d', antihermitian=True) + e1 = Mul(a, b, c, evaluate=False) + e2 = Mul(b, a, c, evaluate=False) + e3 = Mul(a, b, c, d, evaluate=False) + e4 = Mul(b, a, c, d, evaluate=False) + e5 = Mul(a, c, evaluate=False) + e6 = Mul(a, c, d, evaluate=False) + assert e1.is_hermitian is None + assert e2.is_hermitian is None + assert e1.is_antihermitian is None + assert e2.is_antihermitian is None + assert e3.is_antihermitian is None + assert e4.is_antihermitian is None + assert e5.is_antihermitian is None + assert e6.is_antihermitian is None + + +def test_Add_is_comparable(): + assert (x + y).is_comparable is False + assert (x + 1).is_comparable is False + assert (Rational(1, 3) - sqrt(8)).is_comparable is True + + +def test_Mul_is_comparable(): + assert (x*y).is_comparable is False + assert (x*2).is_comparable is False + assert (sqrt(2)*Rational(1, 3)).is_comparable is True + + +def test_Pow_is_comparable(): + assert (x**y).is_comparable is False + assert (x**2).is_comparable is False + assert (sqrt(Rational(1, 3))).is_comparable is True + + +def test_Add_is_positive_2(): + e = Rational(1, 3) - sqrt(8) + assert e.is_positive is False + assert e.is_negative is True + + e = pi - 1 + assert e.is_positive is True + assert e.is_negative is False + + +def test_Add_is_irrational(): + i = Symbol('i', irrational=True) + + assert i.is_irrational is True + assert i.is_rational is False + + assert (i + 1).is_irrational is True + assert (i + 1).is_rational is False + + +def test_Mul_is_irrational(): + expr = Mul(1, 2, 3, evaluate=False) + assert expr.is_irrational is False + expr = Mul(1, I, I, evaluate=False) + assert expr.is_rational is None # I * I = -1 but *no evaluation allowed* + # sqrt(2) * I * I = -sqrt(2) is irrational but + # this can't be determined without evaluating the + # expression and the eval_is routines shouldn't do that + expr = Mul(sqrt(2), I, I, evaluate=False) + assert expr.is_irrational is None + + +def test_issue_3531(): + # https://github.com/sympy/sympy/issues/3531 + # https://github.com/sympy/sympy/pull/18116 + class MightyNumeric(tuple): + def __rtruediv__(self, other): + return "something" + + assert sympify(1)/MightyNumeric((1, 2)) == "something" + + +def test_issue_3531b(): + class Foo: + def __init__(self): + self.field = 1.0 + + def __mul__(self, other): + self.field = self.field * other + + def __rmul__(self, other): + self.field = other * self.field + f = Foo() + x = Symbol("x") + assert f*x == x*f + + +def test_bug3(): + a = Symbol("a") + b = Symbol("b", positive=True) + e = 2*a + b + f = b + 2*a + assert e == f + + +def test_suppressed_evaluation(): + a = Add(0, 3, 2, evaluate=False) + b = Mul(1, 3, 2, evaluate=False) + c = Pow(3, 2, evaluate=False) + assert a != 6 + assert a.func is Add + assert a.args == (0, 3, 2) + assert b != 6 + assert b.func is Mul + assert b.args == (1, 3, 2) + assert c != 9 + assert c.func is Pow + assert c.args == (3, 2) + + +def test_AssocOp_doit(): + a = Add(x,x, evaluate=False) + b = Mul(y,y, evaluate=False) + c = Add(b,b, evaluate=False) + d = Mul(a,a, evaluate=False) + assert c.doit(deep=False).func == Mul + assert c.doit(deep=False).args == (2,y,y) + assert c.doit().func == Mul + assert c.doit().args == (2, Pow(y,2)) + assert d.doit(deep=False).func == Pow + assert d.doit(deep=False).args == (a, 2*S.One) + assert d.doit().func == Mul + assert d.doit().args == (4*S.One, Pow(x,2)) + + +def test_Add_Mul_Expr_args(): + nonexpr = [Basic(), Poly(x, x), FiniteSet(x)] + for typ in [Add, Mul]: + for obj in nonexpr: + # The cache can mess with the stacklevel check + with warns(SymPyDeprecationWarning, test_stacklevel=False): + typ(obj, 1) + + +def test_Add_as_coeff_mul(): + # issue 5524. These should all be (1, self) + assert (x + 1).as_coeff_mul() == (1, (x + 1,)) + assert (x + 2).as_coeff_mul() == (1, (x + 2,)) + assert (x + 3).as_coeff_mul() == (1, (x + 3,)) + + assert (x - 1).as_coeff_mul() == (1, (x - 1,)) + assert (x - 2).as_coeff_mul() == (1, (x - 2,)) + assert (x - 3).as_coeff_mul() == (1, (x - 3,)) + + n = Symbol('n', integer=True) + assert (n + 1).as_coeff_mul() == (1, (n + 1,)) + assert (n + 2).as_coeff_mul() == (1, (n + 2,)) + assert (n + 3).as_coeff_mul() == (1, (n + 3,)) + + assert (n - 1).as_coeff_mul() == (1, (n - 1,)) + assert (n - 2).as_coeff_mul() == (1, (n - 2,)) + assert (n - 3).as_coeff_mul() == (1, (n - 3,)) + + +def test_Pow_as_coeff_mul_doesnt_expand(): + assert exp(x + y).as_coeff_mul() == (1, (exp(x + y),)) + assert exp(x + exp(x + y)) != exp(x + exp(x)*exp(y)) + +def test_issue_24751(): + expr = Add(-2, -3, evaluate=False) + expr1 = Add(-1, expr, evaluate=False) + assert int(expr1) == int((-3 - 2) - 1) + + +def test_issue_3514_18626(): + assert sqrt(S.Half) * sqrt(6) == 2 * sqrt(3)/2 + assert S.Half*sqrt(6)*sqrt(2) == sqrt(3) + assert sqrt(6)/2*sqrt(2) == sqrt(3) + assert sqrt(6)*sqrt(2)/2 == sqrt(3) + assert sqrt(8)**Rational(2, 3) == 2 + + +def test_make_args(): + assert Add.make_args(x) == (x,) + assert Mul.make_args(x) == (x,) + + assert Add.make_args(x*y*z) == (x*y*z,) + assert Mul.make_args(x*y*z) == (x*y*z).args + + assert Add.make_args(x + y + z) == (x + y + z).args + assert Mul.make_args(x + y + z) == (x + y + z,) + + assert Add.make_args((x + y)**z) == ((x + y)**z,) + assert Mul.make_args((x + y)**z) == ((x + y)**z,) + + +def test_issue_5126(): + assert (-2)**x*(-3)**x != 6**x + i = Symbol('i', integer=1) + assert (-2)**i*(-3)**i == 6**i + + +def test_Rational_as_content_primitive(): + c, p = S.One, S.Zero + assert (c*p).as_content_primitive() == (c, p) + c, p = S.Half, S.One + assert (c*p).as_content_primitive() == (c, p) + + +def test_Add_as_content_primitive(): + assert (x + 2).as_content_primitive() == (1, x + 2) + + assert (3*x + 2).as_content_primitive() == (1, 3*x + 2) + assert (3*x + 3).as_content_primitive() == (3, x + 1) + assert (3*x + 6).as_content_primitive() == (3, x + 2) + + assert (3*x + 2*y).as_content_primitive() == (1, 3*x + 2*y) + assert (3*x + 3*y).as_content_primitive() == (3, x + y) + assert (3*x + 6*y).as_content_primitive() == (3, x + 2*y) + + assert (3/x + 2*x*y*z**2).as_content_primitive() == (1, 3/x + 2*x*y*z**2) + assert (3/x + 3*x*y*z**2).as_content_primitive() == (3, 1/x + x*y*z**2) + assert (3/x + 6*x*y*z**2).as_content_primitive() == (3, 1/x + 2*x*y*z**2) + + assert (2*x/3 + 4*y/9).as_content_primitive() == \ + (Rational(2, 9), 3*x + 2*y) + assert (2*x/3 + 2.5*y).as_content_primitive() == \ + (Rational(1, 3), 2*x + 7.5*y) + + # the coefficient may sort to a position other than 0 + p = 3 + x + y + assert (2*p).expand().as_content_primitive() == (2, p) + assert (2.0*p).expand().as_content_primitive() == (1, 2.*p) + p *= -1 + assert (2*p).expand().as_content_primitive() == (2, p) + + +def test_Mul_as_content_primitive(): + assert (2*x).as_content_primitive() == (2, x) + assert (x*(2 + 2*x)).as_content_primitive() == (2, x*(1 + x)) + assert (x*(2 + 2*y)*(3*x + 3)**2).as_content_primitive() == \ + (18, x*(1 + y)*(x + 1)**2) + assert ((2 + 2*x)**2*(3 + 6*x) + S.Half).as_content_primitive() == \ + (S.Half, 24*(x + 1)**2*(2*x + 1) + 1) + + +def test_Pow_as_content_primitive(): + assert (x**y).as_content_primitive() == (1, x**y) + assert ((2*x + 2)**y).as_content_primitive() == \ + (1, (Mul(2, (x + 1), evaluate=False))**y) + assert ((2*x + 2)**3).as_content_primitive() == (8, (x + 1)**3) + + +def test_issue_5460(): + u = Mul(2, (1 + x), evaluate=False) + assert (2 + u).args == (2, u) + + +def test_product_irrational(): + assert (I*pi).is_irrational is False + # The following used to be deduced from the above bug: + assert (I*pi).is_positive is False + + +def test_issue_5919(): + assert (x/(y*(1 + y))).expand() == x/(y**2 + y) + + +def test_Mod(): + assert Mod(x, 1).func is Mod + assert pi % pi is S.Zero + assert Mod(5, 3) == 2 + assert Mod(-5, 3) == 1 + assert Mod(5, -3) == -1 + assert Mod(-5, -3) == -2 + assert type(Mod(3.2, 2, evaluate=False)) == Mod + assert 5 % x == Mod(5, x) + assert x % 5 == Mod(x, 5) + assert x % y == Mod(x, y) + assert (x % y).subs({x: 5, y: 3}) == 2 + assert Mod(nan, 1) is nan + assert Mod(1, nan) is nan + assert Mod(nan, nan) is nan + + assert Mod(0, x) == 0 + with raises(ZeroDivisionError): + Mod(x, 0) + + k = Symbol('k', integer=True) + m = Symbol('m', integer=True, positive=True) + assert (x**m % x).func is Mod + assert (k**(-m) % k).func is Mod + assert k**m % k == 0 + assert (-2*k)**m % k == 0 + + # Float handling + point3 = Float(3.3) % 1 + assert (x - 3.3) % 1 == Mod(1.*x + 1 - point3, 1) + assert Mod(-3.3, 1) == 1 - point3 + assert Mod(0.7, 1) == Float(0.7) + e = Mod(1.3, 1) + assert comp(e, .3) and e.is_Float + e = Mod(1.3, .7) + assert comp(e, .6) and e.is_Float + e = Mod(1.3, Rational(7, 10)) + assert comp(e, .6) and e.is_Float + e = Mod(Rational(13, 10), 0.7) + assert comp(e, .6) and e.is_Float + e = Mod(Rational(13, 10), Rational(7, 10)) + assert comp(e, .6) and e.is_Rational + + # check that sign is right + r2 = sqrt(2) + r3 = sqrt(3) + for i in [-r3, -r2, r2, r3]: + for j in [-r3, -r2, r2, r3]: + assert verify_numerically(i % j, i.n() % j.n()) + for _x in range(4): + for _y in range(9): + reps = [(x, _x), (y, _y)] + assert Mod(3*x + y, 9).subs(reps) == (3*_x + _y) % 9 + + # denesting + t = Symbol('t', real=True) + assert Mod(Mod(x, t), t) == Mod(x, t) + assert Mod(-Mod(x, t), t) == Mod(-x, t) + assert Mod(Mod(x, 2*t), t) == Mod(x, t) + assert Mod(-Mod(x, 2*t), t) == Mod(-x, t) + assert Mod(Mod(x, t), 2*t) == Mod(x, t) + assert Mod(-Mod(x, t), -2*t) == -Mod(x, t) + for i in [-4, -2, 2, 4]: + for j in [-4, -2, 2, 4]: + for k in range(4): + assert Mod(Mod(x, i), j).subs({x: k}) == (k % i) % j + assert Mod(-Mod(x, i), j).subs({x: k}) == -(k % i) % j + + # known difference + assert Mod(5*sqrt(2), sqrt(5)) == 5*sqrt(2) - 3*sqrt(5) + p = symbols('p', positive=True) + assert Mod(2, p + 3) == 2 + assert Mod(-2, p + 3) == p + 1 + assert Mod(2, -p - 3) == -p - 1 + assert Mod(-2, -p - 3) == -2 + assert Mod(p + 5, p + 3) == 2 + assert Mod(-p - 5, p + 3) == p + 1 + assert Mod(p + 5, -p - 3) == -p - 1 + assert Mod(-p - 5, -p - 3) == -2 + assert Mod(p + 1, p - 1).func is Mod + + # handling sums + assert (x + 3) % 1 == Mod(x, 1) + assert (x + 3.0) % 1 == Mod(1.*x, 1) + assert (x - S(33)/10) % 1 == Mod(x + S(7)/10, 1) + + a = Mod(.6*x + y, .3*y) + b = Mod(0.1*y + 0.6*x, 0.3*y) + # Test that a, b are equal, with 1e-14 accuracy in coefficients + eps = 1e-14 + assert abs((a.args[0] - b.args[0]).subs({x: 1, y: 1})) < eps + assert abs((a.args[1] - b.args[1]).subs({x: 1, y: 1})) < eps + + assert (x + 1) % x == 1 % x + assert (x + y) % x == y % x + assert (x + y + 2) % x == (y + 2) % x + assert (a + 3*x + 1) % (2*x) == Mod(a + x + 1, 2*x) + assert (12*x + 18*y) % (3*x) == 3*Mod(6*y, x) + + # gcd extraction + assert (-3*x) % (-2*y) == -Mod(3*x, 2*y) + assert (.6*pi) % (.3*x*pi) == 0.3*pi*Mod(2, x) + assert (.6*pi) % (.31*x*pi) == pi*Mod(0.6, 0.31*x) + assert (6*pi) % (.3*x*pi) == 0.3*pi*Mod(20, x) + assert (6*pi) % (.31*x*pi) == pi*Mod(6, 0.31*x) + assert (6*pi) % (.42*x*pi) == pi*Mod(6, 0.42*x) + assert (12*x) % (2*y) == 2*Mod(6*x, y) + assert (12*x) % (3*5*y) == 3*Mod(4*x, 5*y) + assert (12*x) % (15*x*y) == 3*x*Mod(4, 5*y) + assert (-2*pi) % (3*pi) == pi + assert (2*x + 2) % (x + 1) == 0 + assert (x*(x + 1)) % (x + 1) == (x + 1)*Mod(x, 1) + assert Mod(5.0*x, 0.1*y) == 0.1*Mod(50*x, y) + i = Symbol('i', integer=True) + assert (3*i*x) % (2*i*y) == i*Mod(3*x, 2*y) + assert Mod(4*i, 4) == 0 + + # issue 8677 + n = Symbol('n', integer=True, positive=True) + assert factorial(n) % n == 0 + assert factorial(n + 2) % n == 0 + assert (factorial(n + 4) % (n + 5)).func is Mod + + # Wilson's theorem + assert factorial(18042, evaluate=False) % 18043 == 18042 + p = Symbol('n', prime=True) + assert factorial(p - 1) % p == p - 1 + assert factorial(p - 1) % -p == -1 + assert (factorial(3, evaluate=False) % 4).doit() == 2 + n = Symbol('n', composite=True, odd=True) + assert factorial(n - 1) % n == 0 + + # symbolic with known parity + n = Symbol('n', even=True) + assert Mod(n, 2) == 0 + n = Symbol('n', odd=True) + assert Mod(n, 2) == 1 + + # issue 10963 + assert (x**6000%400).args[1] == 400 + + #issue 13543 + assert Mod(Mod(x + 1, 2) + 1, 2) == Mod(x, 2) + + x1 = Symbol('x1', integer=True) + assert Mod(Mod(x1 + 2, 4)*(x1 + 4), 4) == Mod(x1*(x1 + 2), 4) + assert Mod(Mod(x1 + 2, 4)*4, 4) == 0 + + # issue 15493 + i, j = symbols('i j', integer=True, positive=True) + assert Mod(3*i, 2) == Mod(i, 2) + assert Mod(8*i/j, 4) == 4*Mod(2*i/j, 1) + assert Mod(8*i, 4) == 0 + + # rewrite + assert Mod(x, y).rewrite(floor) == x - y*floor(x/y) + assert ((x - Mod(x, y))/y).rewrite(floor) == floor(x/y) + + # issue 21373 + from sympy.functions.elementary.hyperbolic import sinh + from sympy.functions.elementary.piecewise import Piecewise + + x_r, y_r = symbols('x_r y_r', real=True) + assert (Piecewise((x_r, y_r > x_r), (y_r, True)) / z) % 1 + expr = exp(sinh(Piecewise((x_r, y_r > x_r), (y_r, True)) / z)) + expr.subs({1: 1.0}) + sinh(Piecewise((x_r, y_r > x_r), (y_r, True)) * z ** -1.0).is_zero + + # issue 24215 + from sympy.abc import phi + assert Mod(4.0*Mod(phi, 1) , 2) == 2.0*(Mod(2*(Mod(phi, 1)), 1)) + + xi = symbols('x', integer=True) + assert unchanged(Mod, xi, 2) + assert Mod(3*xi, 2) == Mod(xi, 2) + assert unchanged(Mod, 3*x, 2) + + +def test_Mod_Pow(): + # modular exponentiation + assert isinstance(Mod(Pow(2, 2, evaluate=False), 3), Integer) + + assert Mod(Pow(4, 13, evaluate=False), 497) == Mod(Pow(4, 13), 497) + assert Mod(Pow(2, 10000000000, evaluate=False), 3) == 1 + assert Mod(Pow(32131231232, 9**10**6, evaluate=False),10**12) == \ + pow(32131231232,9**10**6,10**12) + assert Mod(Pow(33284959323, 123**999, evaluate=False),11**13) == \ + pow(33284959323,123**999,11**13) + assert Mod(Pow(78789849597, 333**555, evaluate=False),12**9) == \ + pow(78789849597,333**555,12**9) + + # modular nested exponentiation + expr = Pow(2, 2, evaluate=False) + expr = Pow(2, expr, evaluate=False) + assert Mod(expr, 3**10) == 16 + expr = Pow(2, expr, evaluate=False) + assert Mod(expr, 3**10) == 6487 + expr = Pow(2, expr, evaluate=False) + assert Mod(expr, 3**10) == 32191 + expr = Pow(2, expr, evaluate=False) + assert Mod(expr, 3**10) == 18016 + expr = Pow(2, expr, evaluate=False) + assert Mod(expr, 3**10) == 5137 + + expr = Pow(2, 2, evaluate=False) + expr = Pow(expr, 2, evaluate=False) + assert Mod(expr, 3**10) == 16 + expr = Pow(expr, 2, evaluate=False) + assert Mod(expr, 3**10) == 256 + expr = Pow(expr, 2, evaluate=False) + assert Mod(expr, 3**10) == 6487 + expr = Pow(expr, 2, evaluate=False) + assert Mod(expr, 3**10) == 38281 + expr = Pow(expr, 2, evaluate=False) + assert Mod(expr, 3**10) == 15928 + + expr = Pow(2, 2, evaluate=False) + expr = Pow(expr, expr, evaluate=False) + assert Mod(expr, 3**10) == 256 + expr = Pow(expr, expr, evaluate=False) + assert Mod(expr, 3**10) == 9229 + expr = Pow(expr, expr, evaluate=False) + assert Mod(expr, 3**10) == 25708 + expr = Pow(expr, expr, evaluate=False) + assert Mod(expr, 3**10) == 26608 + expr = Pow(expr, expr, evaluate=False) + # XXX This used to fail in a nondeterministic way because of overflow + # error. + assert Mod(expr, 3**10) == 1966 + + +def test_Mod_is_integer(): + p = Symbol('p', integer=True) + q1 = Symbol('q1', integer=True) + q2 = Symbol('q2', integer=True, nonzero=True) + assert Mod(x, y).is_integer is None + assert Mod(p, q1).is_integer is None + assert Mod(x, q2).is_integer is None + assert Mod(p, q2).is_integer + + +def test_Mod_is_nonposneg(): + n = Symbol('n', integer=True) + k = Symbol('k', integer=True, positive=True) + assert (n%3).is_nonnegative + assert Mod(n, -3).is_nonpositive + assert Mod(n, k).is_nonnegative + assert Mod(n, -k).is_nonpositive + assert Mod(k, n).is_nonnegative is None + + +def test_issue_6001(): + A = Symbol("A", commutative=False) + eq = A + A**2 + # it doesn't matter whether it's True or False; they should + # just all be the same + assert ( + eq.is_commutative == + (eq + 1).is_commutative == + (A + 1).is_commutative) + + B = Symbol("B", commutative=False) + # Although commutative terms could cancel we return True + # meaning "there are non-commutative symbols; aftersubstitution + # that definition can change, e.g. (A*B).subs(B,A**-1) -> 1 + assert (sqrt(2)*A).is_commutative is False + assert (sqrt(2)*A*B).is_commutative is False + + +def test_polar(): + from sympy.functions.elementary.complexes import polar_lift + p = Symbol('p', polar=True) + x = Symbol('x') + assert p.is_polar + assert x.is_polar is None + assert S.One.is_polar is None + assert (p**x).is_polar is True + assert (x**p).is_polar is None + assert ((2*p)**x).is_polar is True + assert (2*p).is_polar is True + assert (-2*p).is_polar is not True + assert (polar_lift(-2)*p).is_polar is True + + q = Symbol('q', polar=True) + assert (p*q)**2 == p**2 * q**2 + assert (2*q)**2 == 4 * q**2 + assert ((p*q)**x).expand() == p**x * q**x + + +def test_issue_6040(): + a, b = Pow(1, 2, evaluate=False), S.One + assert a != b + assert b != a + assert not (a == b) + assert not (b == a) + + +def test_issue_6082(): + # Comparison is symmetric + assert Basic.compare(Max(x, 1), Max(x, 2)) == \ + - Basic.compare(Max(x, 2), Max(x, 1)) + # Equal expressions compare equal + assert Basic.compare(Max(x, 1), Max(x, 1)) == 0 + # Basic subtypes (such as Max) compare different than standard types + assert Basic.compare(Max(1, x), frozenset((1, x))) != 0 + + +def test_issue_6077(): + assert x**2.0/x == x**1.0 + assert x/x**2.0 == x**-1.0 + assert x*x**2.0 == x**3.0 + assert x**1.5*x**2.5 == x**4.0 + + assert 2**(2.0*x)/2**x == 2**(1.0*x) + assert 2**x/2**(2.0*x) == 2**(-1.0*x) + assert 2**x*2**(2.0*x) == 2**(3.0*x) + assert 2**(1.5*x)*2**(2.5*x) == 2**(4.0*x) + + +def test_mul_flatten_oo(): + p = symbols('p', positive=True) + n, m = symbols('n,m', negative=True) + x_im = symbols('x_im', imaginary=True) + assert n*oo is -oo + assert n*m*oo is oo + assert p*oo is oo + assert x_im*oo != I*oo # i could be +/- 3*I -> +/-oo + + +def test_add_flatten(): + # see https://github.com/sympy/sympy/issues/2633#issuecomment-29545524 + a = oo + I*oo + b = oo - I*oo + assert a + b is nan + assert a - b is nan + # FIXME: This evaluates as: + # >>> 1/a + # 0*(oo + oo*I) + # which should not simplify to 0. Should be fixed in Pow.eval + #assert (1/a).simplify() == (1/b).simplify() == 0 + + a = Pow(2, 3, evaluate=False) + assert a + a == 16 + + +def test_issue_5160_6087_6089_6090(): + # issue 6087 + assert ((-2*x*y**y)**3.2).n(2) == (2**3.2*(-x*y**y)**3.2).n(2) + # issue 6089 + A, B, C = symbols('A,B,C', commutative=False) + assert (2.*B*C)**3 == 8.0*(B*C)**3 + assert (-2.*B*C)**3 == -8.0*(B*C)**3 + assert (-2*B*C)**2 == 4*(B*C)**2 + # issue 5160 + assert sqrt(-1.0*x) == 1.0*sqrt(-x) + assert sqrt(1.0*x) == 1.0*sqrt(x) + # issue 6090 + assert (-2*x*y*A*B)**2 == 4*x**2*y**2*(A*B)**2 + + +def test_float_int_round(): + assert int(float(sqrt(10))) == int(sqrt(10)) + assert int(pi**1000) % 10 == 2 + assert int(Float('1.123456789012345678901234567890e20', '')) == \ + int(112345678901234567890) + assert int(Float('1.123456789012345678901234567890e25', '')) == \ + int(11234567890123456789012345) + # decimal forces float so it's not an exact integer ending in 000000 + assert int(Float('1.123456789012345678901234567890e35', '')) == \ + 112345678901234567890123456789000192 + assert int(Float('123456789012345678901234567890e5', '')) == \ + 12345678901234567890123456789000000 + assert Integer(Float('1.123456789012345678901234567890e20', '')) == \ + 112345678901234567890 + assert Integer(Float('1.123456789012345678901234567890e25', '')) == \ + 11234567890123456789012345 + # decimal forces float so it's not an exact integer ending in 000000 + assert Integer(Float('1.123456789012345678901234567890e35', '')) == \ + 112345678901234567890123456789000192 + assert Integer(Float('123456789012345678901234567890e5', '')) == \ + 12345678901234567890123456789000000 + assert same_and_same_prec(Float('123000e-2',''), Float('1230.00', '')) + assert same_and_same_prec(Float('123000e2',''), Float('12300000', '')) + + assert int(1 + Rational('.9999999999999999999999999')) == 1 + assert int(pi/1e20) == 0 + assert int(1 + pi/1e20) == 1 + assert int(Add(1.2, -2, evaluate=False)) == int(1.2 - 2) + assert int(Add(1.2, +2, evaluate=False)) == int(1.2 + 2) + assert int(Add(1 + Float('.99999999999999999', ''), evaluate=False)) == 1 + raises(TypeError, lambda: float(x)) + raises(TypeError, lambda: float(sqrt(-1))) + + assert int(12345678901234567890 + cos(1)**2 + sin(1)**2) == \ + 12345678901234567891 + + +def test_issue_6611a(): + assert Mul.flatten([3**Rational(1, 3), + Pow(-Rational(1, 9), Rational(2, 3), evaluate=False)]) == \ + ([Rational(1, 3), (-1)**Rational(2, 3)], [], None) + + +def test_denest_add_mul(): + # when working with evaluated expressions make sure they denest + eq = x + 1 + eq = Add(eq, 2, evaluate=False) + eq = Add(eq, 2, evaluate=False) + assert Add(*eq.args) == x + 5 + eq = x*2 + eq = Mul(eq, 2, evaluate=False) + eq = Mul(eq, 2, evaluate=False) + assert Mul(*eq.args) == 8*x + # but don't let them denest unnecessarily + eq = Mul(-2, x - 2, evaluate=False) + assert 2*eq == Mul(-4, x - 2, evaluate=False) + assert -eq == Mul(2, x - 2, evaluate=False) + + +def test_mul_coeff(): + # It is important that all Numbers be removed from the seq; + # This can be tricky when powers combine to produce those numbers + p = exp(I*pi/3) + assert p**2*x*p*y*p*x*p**2 == x**2*y + + +def test_mul_zero_detection(): + nz = Dummy(real=True, zero=False) + r = Dummy(extended_real=True) + c = Dummy(real=False, complex=True) + c2 = Dummy(real=False, complex=True) + i = Dummy(imaginary=True) + e = nz*r*c + assert e.is_imaginary is None + assert e.is_extended_real is None + e = nz*c + assert e.is_imaginary is None + assert e.is_extended_real is False + e = nz*i*c + assert e.is_imaginary is False + assert e.is_extended_real is None + # check for more than one complex; it is important to use + # uniquely named Symbols to ensure that two factors appear + # e.g. if the symbols have the same name they just become + # a single factor, a power. + e = nz*i*c*c2 + assert e.is_imaginary is None + assert e.is_extended_real is None + + # _eval_is_extended_real and _eval_is_zero both employ trapping of the + # zero value so args should be tested in both directions and + # TO AVOID GETTING THE CACHED RESULT, Dummy MUST BE USED + + # real is unknown + def test(z, b, e): + if z.is_zero and b.is_finite: + assert e.is_extended_real and e.is_zero + else: + assert e.is_extended_real is None + if b.is_finite: + if z.is_zero: + assert e.is_zero + else: + assert e.is_zero is None + elif b.is_finite is False: + if z.is_zero is None: + assert e.is_zero is None + else: + assert e.is_zero is False + + + for iz, ib in product(*[[True, False, None]]*2): + z = Dummy('z', nonzero=iz) + b = Dummy('f', finite=ib) + e = Mul(z, b, evaluate=False) + test(z, b, e) + z = Dummy('nz', nonzero=iz) + b = Dummy('f', finite=ib) + e = Mul(b, z, evaluate=False) + test(z, b, e) + + # real is True + def test(z, b, e): + if z.is_zero and not b.is_finite: + assert e.is_extended_real is None + else: + assert e.is_extended_real is True + + for iz, ib in product(*[[True, False, None]]*2): + z = Dummy('z', nonzero=iz, extended_real=True) + b = Dummy('b', finite=ib, extended_real=True) + e = Mul(z, b, evaluate=False) + test(z, b, e) + z = Dummy('z', nonzero=iz, extended_real=True) + b = Dummy('b', finite=ib, extended_real=True) + e = Mul(b, z, evaluate=False) + test(z, b, e) + + +def test_Mul_with_zero_infinite(): + zer = Dummy(zero=True) + inf = Dummy(finite=False) + + e = Mul(zer, inf, evaluate=False) + assert e.is_extended_positive is None + assert e.is_hermitian is None + + e = Mul(inf, zer, evaluate=False) + assert e.is_extended_positive is None + assert e.is_hermitian is None + + +def test_Mul_does_not_cancel_infinities(): + a, b = symbols('a b') + assert ((zoo + 3*a)/(3*a + zoo)) is nan + assert ((b - oo)/(b - oo)) is nan + # issue 13904 + expr = (1/(a+b) + 1/(a-b))/(1/(a+b) - 1/(a-b)) + assert expr.subs(b, a) is nan + + +def test_Mul_does_not_distribute_infinity(): + a, b = symbols('a b') + assert ((1 + I)*oo).is_Mul + assert ((a + b)*(-oo)).is_Mul + assert ((a + 1)*zoo).is_Mul + assert ((1 + I)*oo).is_finite is False + z = (1 + I)*oo + assert ((1 - I)*z).expand() is oo + + +def test_issue_8247_8354(): + from sympy.functions.elementary.trigonometric import tan + z = sqrt(1 + sqrt(3)) + sqrt(3 + 3*sqrt(3)) - sqrt(10 + 6*sqrt(3)) + assert z.is_positive is False # it's 0 + z = S('''-2**(1/3)*(3*sqrt(93) + 29)**2 - 4*(3*sqrt(93) + 29)**(4/3) + + 12*sqrt(93)*(3*sqrt(93) + 29)**(1/3) + 116*(3*sqrt(93) + 29)**(1/3) + + 174*2**(1/3)*sqrt(93) + 1678*2**(1/3)''') + assert z.is_positive is False # it's 0 + z = 2*(-3*tan(19*pi/90) + sqrt(3))*cos(11*pi/90)*cos(19*pi/90) - \ + sqrt(3)*(-3 + 4*cos(19*pi/90)**2) + assert z.is_positive is not True # it's zero and it shouldn't hang + z = S('''9*(3*sqrt(93) + 29)**(2/3)*((3*sqrt(93) + + 29)**(1/3)*(-2**(2/3)*(3*sqrt(93) + 29)**(1/3) - 2) - 2*2**(1/3))**3 + + 72*(3*sqrt(93) + 29)**(2/3)*(81*sqrt(93) + 783) + (162*sqrt(93) + + 1566)*((3*sqrt(93) + 29)**(1/3)*(-2**(2/3)*(3*sqrt(93) + 29)**(1/3) - + 2) - 2*2**(1/3))**2''') + assert z.is_positive is False # it's 0 (and a single _mexpand isn't enough) + + +def test_Add_is_zero(): + x, y = symbols('x y', zero=True) + assert (x + y).is_zero + + # Issue 15873 + e = -2*I + (1 + I)**2 + assert e.is_zero is None + + +def test_issue_14392(): + assert (sin(zoo)**2).as_real_imag() == (nan, nan) + + +def test_divmod(): + assert divmod(x, y) == (x//y, x % y) + assert divmod(x, 3) == (x//3, x % 3) + assert divmod(3, x) == (3//x, 3 % x) + + +def test__neg__(): + assert -(x*y) == -x*y + assert -(-x*y) == x*y + assert -(1.*x) == -1.*x + assert -(-1.*x) == 1.*x + assert -(2.*x) == -2.*x + assert -(-2.*x) == 2.*x + with distribute(False): + eq = -(x + y) + assert eq.is_Mul and eq.args == (-1, x + y) + with evaluate(False): + eq = -(x + y) + assert eq.is_Mul and eq.args == (-1, x + y) + + +def test_issue_18507(): + assert Mul(zoo, zoo, 0) is nan + + +def test_issue_17130(): + e = Add(b, -b, I, -I, evaluate=False) + assert e.is_zero is None # ideally this would be True + + +def test_issue_21034(): + e = -I*log((re(asin(5)) + I*im(asin(5)))/sqrt(re(asin(5))**2 + im(asin(5))**2))/pi + assert e.round(2) + + +def test_issue_22021(): + from sympy.calculus.accumulationbounds import AccumBounds + # these objects are special cases in Mul + from sympy.tensor.tensor import TensorIndexType, tensor_indices, tensor_heads + L = TensorIndexType("L") + i = tensor_indices("i", L) + A, B = tensor_heads("A B", [L]) + e = A(i) + B(i) + assert -e == -1*e + e = zoo + x + assert -e == -1*e + a = AccumBounds(1, 2) + e = a + x + assert -e == -1*e + for args in permutations((zoo, a, x)): + e = Add(*args, evaluate=False) + assert -e == -1*e + assert 2*Add(1, x, x, evaluate=False) == 4*x + 2 + + +def test_issue_22244(): + assert -(zoo*x) == zoo*x + + +def test_issue_22453(): + from sympy.utilities.iterables import cartes + e = Symbol('e', extended_positive=True) + for a, b in cartes(*[[oo, -oo, 3]]*2): + if a == b == 3: + continue + i = a + I*b + assert i**(1 + e) is S.ComplexInfinity + assert i**-e is S.Zero + assert unchanged(Pow, i, e) + assert 1/(oo + I*oo) is S.Zero + r, i = [Dummy(infinite=True, extended_real=True) for _ in range(2)] + assert 1/(r + I*i) is S.Zero + assert 1/(3 + I*i) is S.Zero + assert 1/(r + I*3) is S.Zero + + +def test_issue_22613(): + assert (0**(x - 2)).as_content_primitive() == (1, 0**(x - 2)) + assert (0**(x + 2)).as_content_primitive() == (1, 0**(x + 2)) + + +def test_issue_25176(): + assert sqrt(-4*3**(S(3)/4)*I/3) == 2*3**(S(7)/8)*sqrt(-I)/3 diff --git a/lib/python3.12/site-packages/sympy/core/tests/test_compatibility.py b/lib/python3.12/site-packages/sympy/core/tests/test_compatibility.py new file mode 100644 index 0000000000000000000000000000000000000000..31d2bed07b21aa2fa489273dca9edfc9993cfd86 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/core/tests/test_compatibility.py @@ -0,0 +1,6 @@ +from sympy.testing.pytest import warns_deprecated_sympy + +def test_compatibility_submodule(): + # Test the sympy.core.compatibility deprecation warning + with warns_deprecated_sympy(): + import sympy.core.compatibility # noqa:F401 diff --git a/lib/python3.12/site-packages/sympy/core/tests/test_complex.py b/lib/python3.12/site-packages/sympy/core/tests/test_complex.py new file mode 100644 index 0000000000000000000000000000000000000000..a607e0bdb4db859336aa30aa61f43bfb57d5df88 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/core/tests/test_complex.py @@ -0,0 +1,226 @@ +from sympy.core.function import expand_complex +from sympy.core.numbers import (I, Integer, Rational, pi) +from sympy.core.power import Pow +from sympy.core.singleton import S +from sympy.core.symbol import (Symbol, symbols) +from sympy.functions.elementary.complexes import (Abs, conjugate, im, re, sign) +from sympy.functions.elementary.exponential import exp +from sympy.functions.elementary.hyperbolic import (cosh, coth, sinh, tanh) +from sympy.functions.elementary.miscellaneous import sqrt +from sympy.functions.elementary.trigonometric import (cos, cot, sin, tan) + +def test_complex(): + a = Symbol("a", real=True) + b = Symbol("b", real=True) + e = (a + I*b)*(a - I*b) + assert e.expand() == a**2 + b**2 + assert sqrt(I) == Pow(I, S.Half) + + +def test_conjugate(): + a = Symbol("a", real=True) + b = Symbol("b", real=True) + c = Symbol("c", imaginary=True) + d = Symbol("d", imaginary=True) + x = Symbol('x') + z = a + I*b + c + I*d + zc = a - I*b - c + I*d + assert conjugate(z) == zc + assert conjugate(exp(z)) == exp(zc) + assert conjugate(exp(I*x)) == exp(-I*conjugate(x)) + assert conjugate(z**5) == zc**5 + assert conjugate(abs(x)) == abs(x) + assert conjugate(sign(z)) == sign(zc) + assert conjugate(sin(z)) == sin(zc) + assert conjugate(cos(z)) == cos(zc) + assert conjugate(tan(z)) == tan(zc) + assert conjugate(cot(z)) == cot(zc) + assert conjugate(sinh(z)) == sinh(zc) + assert conjugate(cosh(z)) == cosh(zc) + assert conjugate(tanh(z)) == tanh(zc) + assert conjugate(coth(z)) == coth(zc) + + +def test_abs1(): + a = Symbol("a", real=True) + b = Symbol("b", real=True) + assert abs(a) == Abs(a) + assert abs(-a) == abs(a) + assert abs(a + I*b) == sqrt(a**2 + b**2) + + +def test_abs2(): + a = Symbol("a", real=False) + b = Symbol("b", real=False) + assert abs(a) != a + assert abs(-a) != a + assert abs(a + I*b) != sqrt(a**2 + b**2) + + +def test_evalc(): + x = Symbol("x", real=True) + y = Symbol("y", real=True) + z = Symbol("z") + assert ((x + I*y)**2).expand(complex=True) == x**2 + 2*I*x*y - y**2 + assert expand_complex(z**(2*I)) == (re((re(z) + I*im(z))**(2*I)) + + I*im((re(z) + I*im(z))**(2*I))) + assert expand_complex( + z**(2*I), deep=False) == I*im(z**(2*I)) + re(z**(2*I)) + + assert exp(I*x) != cos(x) + I*sin(x) + assert exp(I*x).expand(complex=True) == cos(x) + I*sin(x) + assert exp(I*x + y).expand(complex=True) == exp(y)*cos(x) + I*sin(x)*exp(y) + + assert sin(I*x).expand(complex=True) == I * sinh(x) + assert sin(x + I*y).expand(complex=True) == sin(x)*cosh(y) + \ + I * sinh(y) * cos(x) + + assert cos(I*x).expand(complex=True) == cosh(x) + assert cos(x + I*y).expand(complex=True) == cos(x)*cosh(y) - \ + I * sinh(y) * sin(x) + + assert tan(I*x).expand(complex=True) == tanh(x) * I + assert tan(x + I*y).expand(complex=True) == ( + sin(2*x)/(cos(2*x) + cosh(2*y)) + + I*sinh(2*y)/(cos(2*x) + cosh(2*y))) + + assert sinh(I*x).expand(complex=True) == I * sin(x) + assert sinh(x + I*y).expand(complex=True) == sinh(x)*cos(y) + \ + I * sin(y) * cosh(x) + + assert cosh(I*x).expand(complex=True) == cos(x) + assert cosh(x + I*y).expand(complex=True) == cosh(x)*cos(y) + \ + I * sin(y) * sinh(x) + + assert tanh(I*x).expand(complex=True) == tan(x) * I + assert tanh(x + I*y).expand(complex=True) == ( + (sinh(x)*cosh(x) + I*cos(y)*sin(y)) / + (sinh(x)**2 + cos(y)**2)).expand() + + +def test_pythoncomplex(): + x = Symbol("x") + assert 4j*x != 4*x*I + assert 4j*x == 4.0*x*I + assert 4.1j*x != 4*x*I + + +def test_rootcomplex(): + R = Rational + assert ((+1 + I)**R(1, 2)).expand( + complex=True) == 2**R(1, 4)*cos( pi/8) + 2**R(1, 4)*sin( pi/8)*I + assert ((-1 - I)**R(1, 2)).expand( + complex=True) == 2**R(1, 4)*cos(3*pi/8) - 2**R(1, 4)*sin(3*pi/8)*I + assert (sqrt(-10)*I).as_real_imag() == (-sqrt(10), 0) + + +def test_expand_inverse(): + assert (1/(1 + I)).expand(complex=True) == (1 - I)/2 + assert ((1 + 2*I)**(-2)).expand(complex=True) == (-3 - 4*I)/25 + assert ((1 + I)**(-8)).expand(complex=True) == Rational(1, 16) + + +def test_expand_complex(): + assert ((2 + 3*I)**10).expand(complex=True) == -341525 - 145668*I + # the following two tests are to ensure the SymPy uses an efficient + # algorithm for calculating powers of complex numbers. They should execute + # in something like 0.01s. + assert ((2 + 3*I)**1000).expand(complex=True) == \ + -81079464736246615951519029367296227340216902563389546989376269312984127074385455204551402940331021387412262494620336565547972162814110386834027871072723273110439771695255662375718498785908345629702081336606863762777939617745464755635193139022811989314881997210583159045854968310911252660312523907616129080027594310008539817935736331124833163907518549408018652090650537035647520296539436440394920287688149200763245475036722326561143851304795139005599209239350981457301460233967137708519975586996623552182807311159141501424576682074392689622074945519232029999 + \ + 46938745946789557590804551905243206242164799136976022474337918748798900569942573265747576032611189047943842446167719177749107138603040963603119861476016947257034472364028585381714774667326478071264878108114128915685688115488744955550920239128462489496563930809677159214598114273887061533057125164518549173898349061972857446844052995037423459472376202251620778517659247970283904820245958198842631651569984310559418135975795868314764489884749573052997832686979294085577689571149679540256349988338406458116270429842222666345146926395233040564229555893248370000*I + assert ((2 + 3*I/4)**1000).expand(complex=True) == \ + Integer(1)*37079892761199059751745775382463070250205990218394308874593455293485167797989691280095867197640410033222367257278387021789651672598831503296531725827158233077451476545928116965316544607115843772405184272449644892857783761260737279675075819921259597776770965829089907990486964515784097181964312256560561065607846661496055417619388874421218472707497847700629822858068783288579581649321248495739224020822198695759609598745114438265083593711851665996586461937988748911532242908776883696631067311443171682974330675406616373422505939887984366289623091300746049101284856530270685577940283077888955692921951247230006346681086274961362500646889925803654263491848309446197554307105991537357310209426736453173441104334496173618419659521888945605315751089087820455852582920963561495787655250624781448951403353654348109893478206364632640344111022531861683064175862889459084900614967785405977231549003280842218501570429860550379522498497412180001/114813069527425452423283320117768198402231770208869520047764273682576626139237031385665948631650626991844596463898746277344711896086305533142593135616665318539129989145312280000688779148240044871428926990063486244781615463646388363947317026040466353970904996558162398808944629605623311649536164221970332681344168908984458505602379484807914058900934776500429002716706625830522008132236281291761267883317206598995396418127021779858404042159853183251540889433902091920554957783589672039160081957216630582755380425583726015528348786419432054508915275783882625175435528800822842770817965453762184851149029376 + \ + I*421638390580169706973991429333213477486930178424989246669892530737775352519112934278994501272111385966211392610029433824534634841747911783746811994443436271013377059560245191441549885048056920190833693041257216263519792201852046825443439142932464031501882145407459174948712992271510309541474392303461939389368955986650538525895866713074543004916049550090364398070215427272240155060576252568700906004691224321432509053286859100920489253598392100207663785243368195857086816912514025693453058403158416856847185079684216151337200057494966741268925263085619240941610301610538225414050394612058339070756009433535451561664522479191267503989904464718368605684297071150902631208673621618217106272361061676184840810762902463998065947687814692402219182668782278472952758690939877465065070481351343206840649517150634973307937551168752642148704904383991876969408056379195860410677814566225456558230131911142229028179902418223009651437985670625/1793954211366022694113801876840128100034871409513586250746316776290259783425578615401030447369541046747571819748417910583511123376348523955353017744010395602173906080395504375010762174191250701116076984219741972574712741619474818186676828531882286780795390571221287481389759837587864244524002565968286448146002639202882164150037179450123657170327105882819203167448541028601906377066191895183769810676831353109303069033234715310287563158747705988305326397404720186258671215368588625611876280581509852855552819149745718992630449787803625851701801184123166018366180137512856918294030710215034138299203584 + assert ((2 + 3*I)**-1000).expand(complex=True) == \ + Integer(1)*-81079464736246615951519029367296227340216902563389546989376269312984127074385455204551402940331021387412262494620336565547972162814110386834027871072723273110439771695255662375718498785908345629702081336606863762777939617745464755635193139022811989314881997210583159045854968310911252660312523907616129080027594310008539817935736331124833163907518549408018652090650537035647520296539436440394920287688149200763245475036722326561143851304795139005599209239350981457301460233967137708519975586996623552182807311159141501424576682074392689622074945519232029999/8777125472973511649630750050295188683351430110097915876250894978429797369155961290321829625004920141758416719066805645579710744290541337680113772670040386863849283653078324415471816788604945889094925784900885812724984087843737442111926413818245854362613018058774368703971604921858023116665586358870612944209398056562604561248859926344335598822815885851096698226775053153403320782439987679978321289537645645163767251396759519805603090332694449553371530571613352311006350058217982509738362083094920649452123351717366337410243853659113315547584871655479914439219520157174729130746351059075207407866012574386726064196992865627149566238044625779078186624347183905913357718850537058578084932880569701242598663149911276357125355850792073635533676541250531086757377369962506979378337216411188347761901006460813413505861461267545723590468627854202034450569581626648934062198718362303420281555886394558137408159453103395918783625713213314350531051312551733021627153081075080140680608080529736975658786227362251632725009435866547613598753584705455955419696609282059191031962604169242974038517575645939316377801594539335940001 - Integer(1)*46938745946789557590804551905243206242164799136976022474337918748798900569942573265747576032611189047943842446167719177749107138603040963603119861476016947257034472364028585381714774667326478071264878108114128915685688115488744955550920239128462489496563930809677159214598114273887061533057125164518549173898349061972857446844052995037423459472376202251620778517659247970283904820245958198842631651569984310559418135975795868314764489884749573052997832686979294085577689571149679540256349988338406458116270429842222666345146926395233040564229555893248370000*I/8777125472973511649630750050295188683351430110097915876250894978429797369155961290321829625004920141758416719066805645579710744290541337680113772670040386863849283653078324415471816788604945889094925784900885812724984087843737442111926413818245854362613018058774368703971604921858023116665586358870612944209398056562604561248859926344335598822815885851096698226775053153403320782439987679978321289537645645163767251396759519805603090332694449553371530571613352311006350058217982509738362083094920649452123351717366337410243853659113315547584871655479914439219520157174729130746351059075207407866012574386726064196992865627149566238044625779078186624347183905913357718850537058578084932880569701242598663149911276357125355850792073635533676541250531086757377369962506979378337216411188347761901006460813413505861461267545723590468627854202034450569581626648934062198718362303420281555886394558137408159453103395918783625713213314350531051312551733021627153081075080140680608080529736975658786227362251632725009435866547613598753584705455955419696609282059191031962604169242974038517575645939316377801594539335940001 + assert ((2 + 3*I/4)**-1000).expand(complex=True) == \ + Integer(1)*4257256305661027385394552848555894604806501409793288342610746813288539790051927148781268212212078237301273165351052934681382567968787279534591114913777456610214738290619922068269909423637926549603264174216950025398244509039145410016404821694746262142525173737175066432954496592560621330313807235750500564940782099283410261748370262433487444897446779072067625787246390824312580440138770014838135245148574339248259670887549732495841810961088930810608893772914812838358159009303794863047635845688453859317690488124382253918725010358589723156019888846606295866740117645571396817375322724096486161308083462637370825829567578309445855481578518239186117686659177284332344643124760453112513611749309168470605289172320376911472635805822082051716625171429727162039621902266619821870482519063133136820085579315127038372190224739238686708451840610064871885616258831386810233957438253532027049148030157164346719204500373766157143311767338973363806106967439378604898250533766359989107510507493549529158818602327525235240510049484816090584478644771183158342479140194633579061295740839490629457435283873180259847394582069479062820225159699506175855369539201399183443253793905149785994830358114153241481884290274629611529758663543080724574566578220908907477622643689220814376054314972190402285121776593824615083669045183404206291739005554569305329760211752815718335731118664756831942466773261465213581616104242113894521054475516019456867271362053692785300826523328020796670205463390909136593859765912483565093461468865534470710132881677639651348709376/2103100954337624833663208713697737151593634525061637972297915388685604042449504336765884978184588688426595940401280828953096857809292320006227881797146858511436638446932833617514351442216409828605662238790280753075176269765767010004889778647709740770757817960711900340755635772183674511158570690702969774966791073165467918123298694584729211212414462628433370481195120564586361368504153395406845170075275051749019600057116719726628746724489572189061061036426955163696859127711110719502594479795200686212257570291758725259007379710596548777812659422174199194837355646482046783616494013289495563083118517507178847555801163089723056310287760875135196081975602765511153122381201303871673391366630940702817360340900568748719988954847590748960761446218262344767250783946365392689256634180417145926390656439421745644011831124277463643383712803287985472471755648426749842410972650924240795946699346613614779460399530274263580007672855851663196114585312432954432654691485867618908420370875753749297487803461900447407917655296784879220450937110470920633595689721819488638484547259978337741496090602390463594556401615298457456112485536498177883358587125449801777718900375736758266215245325999241624148841915093787519330809347240990363802360596034171167818310322276373120180985148650099673289383722502488957717848531612020897298448601714154586319660314294591620415272119454982220034319689607295960162971300417552364254983071768070124456169427638371140064235083443242844616326538396503937972586505546495649094344512270582463639152160238137952390380581401171977159154009407415523525096743009110916334144716516647041176989758534635251844947906038080852185583742296318878233394998111078843229681030277039104786225656992262073797524057992347971177720807155842376332851559276430280477639539393920006008737472164850104411971830120295750221200029811143140323763349636629725073624360001 - Integer(1)*3098214262599218784594285246258841485430681674561917573155883806818465520660668045042109232930382494608383663464454841313154390741655282039877410154577448327874989496074260116195788919037407420625081798124301494353693248757853222257918294662198297114746822817460991242508743651430439120439020484502408313310689912381846149597061657483084652685283853595100434135149479564507015504022249330340259111426799121454516345905101620532787348293877485702600390665276070250119465888154331218827342488849948540687659846652377277250614246402784754153678374932540789808703029043827352976139228402417432199779415751301480406673762521987999573209628597459357964214510139892316208670927074795773830798600837815329291912002136924506221066071242281626618211060464126372574400100990746934953437169840312584285942093951405864225230033279614235191326102697164613004299868695519642598882914862568516635347204441042798206770888274175592401790040170576311989738272102077819127459014286741435419468254146418098278519775722104890854275995510700298782146199325790002255362719776098816136732897323406228294203133323296591166026338391813696715894870956511298793595675308998014158717167429941371979636895553724830981754579086664608880698350866487717403917070872269853194118364230971216854931998642990452908852258008095741042117326241406479532880476938937997238098399302185675832474590293188864060116934035867037219176916416481757918864533515526389079998129329045569609325290897577497835388451456680707076072624629697883854217331728051953671643278797380171857920000*I/2103100954337624833663208713697737151593634525061637972297915388685604042449504336765884978184588688426595940401280828953096857809292320006227881797146858511436638446932833617514351442216409828605662238790280753075176269765767010004889778647709740770757817960711900340755635772183674511158570690702969774966791073165467918123298694584729211212414462628433370481195120564586361368504153395406845170075275051749019600057116719726628746724489572189061061036426955163696859127711110719502594479795200686212257570291758725259007379710596548777812659422174199194837355646482046783616494013289495563083118517507178847555801163089723056310287760875135196081975602765511153122381201303871673391366630940702817360340900568748719988954847590748960761446218262344767250783946365392689256634180417145926390656439421745644011831124277463643383712803287985472471755648426749842410972650924240795946699346613614779460399530274263580007672855851663196114585312432954432654691485867618908420370875753749297487803461900447407917655296784879220450937110470920633595689721819488638484547259978337741496090602390463594556401615298457456112485536498177883358587125449801777718900375736758266215245325999241624148841915093787519330809347240990363802360596034171167818310322276373120180985148650099673289383722502488957717848531612020897298448601714154586319660314294591620415272119454982220034319689607295960162971300417552364254983071768070124456169427638371140064235083443242844616326538396503937972586505546495649094344512270582463639152160238137952390380581401171977159154009407415523525096743009110916334144716516647041176989758534635251844947906038080852185583742296318878233394998111078843229681030277039104786225656992262073797524057992347971177720807155842376332851559276430280477639539393920006008737472164850104411971830120295750221200029811143140323763349636629725073624360001 + + a = Symbol('a', real=True) + b = Symbol('b', real=True) + assert exp(a*(2 + I*b)).expand(complex=True) == \ + I*exp(2*a)*sin(a*b) + exp(2*a)*cos(a*b) + + +def test_expand(): + f = (16 - 2*sqrt(29))**2 + assert f.expand() == 372 - 64*sqrt(29) + f = (Integer(1)/2 + I/2)**10 + assert f.expand() == I/32 + f = (Integer(1)/2 + I)**10 + assert f.expand() == Integer(237)/1024 - 779*I/256 + + +def test_re_im1652(): + x = Symbol('x') + assert re(x) == re(conjugate(x)) + assert im(x) == - im(conjugate(x)) + assert im(x)*re(conjugate(x)) + im(conjugate(x)) * re(x) == 0 + + +def test_issue_5084(): + x = Symbol('x') + assert ((x + x*I)/(1 + I)).as_real_imag() == (re((x + I*x)/(1 + I) + ), im((x + I*x)/(1 + I))) + + +def test_issue_5236(): + assert (cos(1 + I)**3).as_real_imag() == (-3*sin(1)**2*sinh(1)**2*cos(1)*cosh(1) + + cos(1)**3*cosh(1)**3, -3*cos(1)**2*cosh(1)**2*sin(1)*sinh(1) + sin(1)**3*sinh(1)**3) + + +def test_real_imag(): + x, y, z = symbols('x, y, z') + X, Y, Z = symbols('X, Y, Z', commutative=False) + a = Symbol('a', real=True) + assert (2*a*x).as_real_imag() == (2*a*re(x), 2*a*im(x)) + + # issue 5395: + assert (x*x.conjugate()).as_real_imag() == (Abs(x)**2, 0) + assert im(x*x.conjugate()) == 0 + assert im(x*y.conjugate()*z*y) == im(x*z)*Abs(y)**2 + assert im(x*y.conjugate()*x*y) == im(x**2)*Abs(y)**2 + assert im(Z*y.conjugate()*X*y) == im(Z*X)*Abs(y)**2 + assert im(X*X.conjugate()) == im(X*X.conjugate(), evaluate=False) + assert (sin(x)*sin(x).conjugate()).as_real_imag() == \ + (Abs(sin(x))**2, 0) + + # issue 6573: + assert (x**2).as_real_imag() == (re(x)**2 - im(x)**2, 2*re(x)*im(x)) + + # issue 6428: + r = Symbol('r', real=True) + i = Symbol('i', imaginary=True) + assert (i*r*x).as_real_imag() == (I*i*r*im(x), -I*i*r*re(x)) + assert (i*r*x*(y + 2)).as_real_imag() == ( + I*i*r*(re(y) + 2)*im(x) + I*i*r*re(x)*im(y), + -I*i*r*(re(y) + 2)*re(x) + I*i*r*im(x)*im(y)) + + # issue 7106: + assert ((1 + I)/(1 - I)).as_real_imag() == (0, 1) + assert ((1 + 2*I)*(1 + 3*I)).as_real_imag() == (-5, 5) + + +def test_pow_issue_1724(): + e = ((S.NegativeOne)**(S.One/3)) + assert e.conjugate().n() == e.n().conjugate() + e = S('-2/3 - (-29/54 + sqrt(93)/18)**(1/3) - 1/(9*(-29/54 + sqrt(93)/18)**(1/3))') + assert e.conjugate().n() == e.n().conjugate() + e = 2**I + assert e.conjugate().n() == e.n().conjugate() + + +def test_issue_5429(): + assert sqrt(I).conjugate() != sqrt(I) + +def test_issue_4124(): + from sympy.core.numbers import oo + assert expand_complex(I*oo) == oo*I + +def test_issue_11518(): + x = Symbol("x", real=True) + y = Symbol("y", real=True) + r = sqrt(x**2 + y**2) + assert conjugate(r) == r + s = abs(x + I * y) + assert conjugate(s) == r diff --git a/lib/python3.12/site-packages/sympy/core/tests/test_containers.py b/lib/python3.12/site-packages/sympy/core/tests/test_containers.py new file mode 100644 index 0000000000000000000000000000000000000000..23357b9f667fffc82d93b2b1adb42b495114c67e --- /dev/null +++ b/lib/python3.12/site-packages/sympy/core/tests/test_containers.py @@ -0,0 +1,217 @@ +from collections import defaultdict + +from sympy.core.basic import Basic +from sympy.core.containers import (Dict, Tuple) +from sympy.core.numbers import Integer +from sympy.core.kind import NumberKind +from sympy.matrices.kind import MatrixKind +from sympy.core.singleton import S +from sympy.core.symbol import symbols +from sympy.core.sympify import sympify +from sympy.matrices.dense import Matrix +from sympy.sets.sets import FiniteSet +from sympy.core.containers import tuple_wrapper, TupleKind +from sympy.core.expr import unchanged +from sympy.core.function import Function, Lambda +from sympy.core.relational import Eq +from sympy.testing.pytest import raises +from sympy.utilities.iterables import is_sequence, iterable + +from sympy.abc import x, y + + +def test_Tuple(): + t = (1, 2, 3, 4) + st = Tuple(*t) + assert set(sympify(t)) == set(st) + assert len(t) == len(st) + assert set(sympify(t[:2])) == set(st[:2]) + assert isinstance(st[:], Tuple) + assert st == Tuple(1, 2, 3, 4) + assert st.func(*st.args) == st + p, q, r, s = symbols('p q r s') + t2 = (p, q, r, s) + st2 = Tuple(*t2) + assert st2.atoms() == set(t2) + assert st == st2.subs({p: 1, q: 2, r: 3, s: 4}) + # issue 5505 + assert all(isinstance(arg, Basic) for arg in st.args) + assert Tuple(p, 1).subs(p, 0) == Tuple(0, 1) + assert Tuple(p, Tuple(p, 1)).subs(p, 0) == Tuple(0, Tuple(0, 1)) + + assert Tuple(t2) == Tuple(Tuple(*t2)) + assert Tuple.fromiter(t2) == Tuple(*t2) + assert Tuple.fromiter(x for x in range(4)) == Tuple(0, 1, 2, 3) + assert st2.fromiter(st2.args) == st2 + + +def test_Tuple_contains(): + t1, t2 = Tuple(1), Tuple(2) + assert t1 in Tuple(1, 2, 3, t1, Tuple(t2)) + assert t2 not in Tuple(1, 2, 3, t1, Tuple(t2)) + + +def test_Tuple_concatenation(): + assert Tuple(1, 2) + Tuple(3, 4) == Tuple(1, 2, 3, 4) + assert (1, 2) + Tuple(3, 4) == Tuple(1, 2, 3, 4) + assert Tuple(1, 2) + (3, 4) == Tuple(1, 2, 3, 4) + raises(TypeError, lambda: Tuple(1, 2) + 3) + raises(TypeError, lambda: 1 + Tuple(2, 3)) + + #the Tuple case in __radd__ is only reached when a subclass is involved + class Tuple2(Tuple): + def __radd__(self, other): + return Tuple.__radd__(self, other + other) + assert Tuple(1, 2) + Tuple2(3, 4) == Tuple(1, 2, 1, 2, 3, 4) + assert Tuple2(1, 2) + Tuple(3, 4) == Tuple(1, 2, 3, 4) + + +def test_Tuple_equality(): + assert not isinstance(Tuple(1, 2), tuple) + assert (Tuple(1, 2) == (1, 2)) is True + assert (Tuple(1, 2) != (1, 2)) is False + assert (Tuple(1, 2) == (1, 3)) is False + assert (Tuple(1, 2) != (1, 3)) is True + assert (Tuple(1, 2) == Tuple(1, 2)) is True + assert (Tuple(1, 2) != Tuple(1, 2)) is False + assert (Tuple(1, 2) == Tuple(1, 3)) is False + assert (Tuple(1, 2) != Tuple(1, 3)) is True + + +def test_Tuple_Eq(): + assert Eq(Tuple(), Tuple()) is S.true + assert Eq(Tuple(1), 1) is S.false + assert Eq(Tuple(1, 2), Tuple(1)) is S.false + assert Eq(Tuple(1), Tuple(1)) is S.true + assert Eq(Tuple(1, 2), Tuple(1, 3)) is S.false + assert Eq(Tuple(1, 2), Tuple(1, 2)) is S.true + assert unchanged(Eq, Tuple(1, x), Tuple(1, 2)) + assert Eq(Tuple(1, x), Tuple(1, 2)).subs(x, 2) is S.true + assert unchanged(Eq, Tuple(1, 2), x) + f = Function('f') + assert unchanged(Eq, Tuple(1), f(x)) + assert Eq(Tuple(1), f(x)).subs(x, 1).subs(f, Lambda(y, (y,))) is S.true + + +def test_Tuple_comparision(): + assert (Tuple(1, 3) >= Tuple(-10, 30)) is S.true + assert (Tuple(1, 3) <= Tuple(-10, 30)) is S.false + assert (Tuple(1, 3) >= Tuple(1, 3)) is S.true + assert (Tuple(1, 3) <= Tuple(1, 3)) is S.true + + +def test_Tuple_tuple_count(): + assert Tuple(0, 1, 2, 3).tuple_count(4) == 0 + assert Tuple(0, 4, 1, 2, 3).tuple_count(4) == 1 + assert Tuple(0, 4, 1, 4, 2, 3).tuple_count(4) == 2 + assert Tuple(0, 4, 1, 4, 2, 4, 3).tuple_count(4) == 3 + + +def test_Tuple_index(): + assert Tuple(4, 0, 1, 2, 3).index(4) == 0 + assert Tuple(0, 4, 1, 2, 3).index(4) == 1 + assert Tuple(0, 1, 4, 2, 3).index(4) == 2 + assert Tuple(0, 1, 2, 4, 3).index(4) == 3 + assert Tuple(0, 1, 2, 3, 4).index(4) == 4 + + raises(ValueError, lambda: Tuple(0, 1, 2, 3).index(4)) + raises(ValueError, lambda: Tuple(4, 0, 1, 2, 3).index(4, 1)) + raises(ValueError, lambda: Tuple(0, 1, 2, 3, 4).index(4, 1, 4)) + + +def test_Tuple_mul(): + assert Tuple(1, 2, 3)*2 == Tuple(1, 2, 3, 1, 2, 3) + assert 2*Tuple(1, 2, 3) == Tuple(1, 2, 3, 1, 2, 3) + assert Tuple(1, 2, 3)*Integer(2) == Tuple(1, 2, 3, 1, 2, 3) + assert Integer(2)*Tuple(1, 2, 3) == Tuple(1, 2, 3, 1, 2, 3) + + raises(TypeError, lambda: Tuple(1, 2, 3)*S.Half) + raises(TypeError, lambda: S.Half*Tuple(1, 2, 3)) + + +def test_tuple_wrapper(): + + @tuple_wrapper + def wrap_tuples_and_return(*t): + return t + + p = symbols('p') + assert wrap_tuples_and_return(p, 1) == (p, 1) + assert wrap_tuples_and_return((p, 1)) == (Tuple(p, 1),) + assert wrap_tuples_and_return(1, (p, 2), 3) == (1, Tuple(p, 2), 3) + + +def test_iterable_is_sequence(): + ordered = [[], (), Tuple(), Matrix([[]])] + unordered = [set()] + not_sympy_iterable = [{}, '', ''] + assert all(is_sequence(i) for i in ordered) + assert all(not is_sequence(i) for i in unordered) + assert all(iterable(i) for i in ordered + unordered) + assert all(not iterable(i) for i in not_sympy_iterable) + assert all(iterable(i, exclude=None) for i in not_sympy_iterable) + + +def test_TupleKind(): + kind = TupleKind(NumberKind, MatrixKind(NumberKind)) + assert Tuple(1, Matrix([1, 2])).kind is kind + assert Tuple(1, 2).kind is TupleKind(NumberKind, NumberKind) + assert Tuple(1, 2).kind.element_kind == (NumberKind, NumberKind) + + +def test_Dict(): + x, y, z = symbols('x y z') + d = Dict({x: 1, y: 2, z: 3}) + assert d[x] == 1 + assert d[y] == 2 + raises(KeyError, lambda: d[2]) + raises(KeyError, lambda: d['2']) + assert len(d) == 3 + assert set(d.keys()) == {x, y, z} + assert set(d.values()) == {S.One, S(2), S(3)} + assert d.get(5, 'default') == 'default' + assert d.get('5', 'default') == 'default' + assert x in d and z in d and 5 not in d and '5' not in d + assert d.has(x) and d.has(1) # SymPy Basic .has method + + # Test input types + # input - a Python dict + # input - items as args - SymPy style + assert (Dict({x: 1, y: 2, z: 3}) == + Dict((x, 1), (y, 2), (z, 3))) + + raises(TypeError, lambda: Dict(((x, 1), (y, 2), (z, 3)))) + with raises(NotImplementedError): + d[5] = 6 # assert immutability + + assert set( + d.items()) == {Tuple(x, S.One), Tuple(y, S(2)), Tuple(z, S(3))} + assert set(d) == {x, y, z} + assert str(d) == '{x: 1, y: 2, z: 3}' + assert d.__repr__() == '{x: 1, y: 2, z: 3}' + + # Test creating a Dict from a Dict. + d = Dict({x: 1, y: 2, z: 3}) + assert d == Dict(d) + + # Test for supporting defaultdict + d = defaultdict(int) + assert d[x] == 0 + assert d[y] == 0 + assert d[z] == 0 + assert Dict(d) + d = Dict(d) + assert len(d) == 3 + assert set(d.keys()) == {x, y, z} + assert set(d.values()) == {S.Zero, S.Zero, S.Zero} + + +def test_issue_5788(): + args = [(1, 2), (2, 1)] + for o in [Dict, Tuple, FiniteSet]: + # __eq__ and arg handling + if o != Tuple: + assert o(*args) == o(*reversed(args)) + pair = [o(*args), o(*reversed(args))] + assert sorted(pair) == sorted(pair) + assert set(o(*args)) # doesn't fail diff --git a/lib/python3.12/site-packages/sympy/core/tests/test_equal.py b/lib/python3.12/site-packages/sympy/core/tests/test_equal.py new file mode 100644 index 0000000000000000000000000000000000000000..82213b757cda5fbd80310e387bdf00cc1c9c25fe --- /dev/null +++ b/lib/python3.12/site-packages/sympy/core/tests/test_equal.py @@ -0,0 +1,89 @@ +from sympy.core.numbers import Rational +from sympy.core.symbol import (Dummy, Symbol) +from sympy.functions.elementary.exponential import exp + + +def test_equal(): + b = Symbol("b") + a = Symbol("a") + e1 = a + b + e2 = 2*a*b + e3 = a**3*b**2 + e4 = a*b + b*a + assert not e1 == e2 + assert not e1 == e2 + assert e1 != e2 + assert e2 == e4 + assert e2 != e3 + assert not e2 == e3 + + x = Symbol("x") + e1 = exp(x + 1/x) + y = Symbol("x") + e2 = exp(y + 1/y) + assert e1 == e2 + assert not e1 != e2 + y = Symbol("y") + e2 = exp(y + 1/y) + assert not e1 == e2 + assert e1 != e2 + + e5 = Rational(3) + 2*x - x - x + assert e5 == 3 + assert 3 == e5 + assert e5 != 4 + assert 4 != e5 + assert e5 != 3 + x + assert 3 + x != e5 + + +def test_expevalbug(): + x = Symbol("x") + e1 = exp(1*x) + e3 = exp(x) + assert e1 == e3 + + +def test_cmp_bug1(): + class T: + pass + + t = T() + x = Symbol("x") + + assert not (x == t) + assert (x != t) + + +def test_cmp_bug2(): + class T: + pass + + t = T() + + assert not (Symbol == t) + assert (Symbol != t) + + +def test_cmp_issue_4357(): + """ Check that Basic subclasses can be compared with sympifiable objects. + + https://github.com/sympy/sympy/issues/4357 + """ + assert not (Symbol == 1) + assert (Symbol != 1) + assert not (Symbol == 'x') + assert (Symbol != 'x') + + +def test_dummy_eq(): + x = Symbol('x') + y = Symbol('y') + + u = Dummy('u') + + assert (u**2 + 1).dummy_eq(x**2 + 1) is True + assert ((u**2 + 1) == (x**2 + 1)) is False + + assert (u**2 + y).dummy_eq(x**2 + y, x) is True + assert (u**2 + y).dummy_eq(x**2 + y, y) is False diff --git a/lib/python3.12/site-packages/sympy/core/tests/test_eval.py b/lib/python3.12/site-packages/sympy/core/tests/test_eval.py new file mode 100644 index 0000000000000000000000000000000000000000..9c1633f77b50483afee21c6d9fca232b1279d2b9 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/core/tests/test_eval.py @@ -0,0 +1,95 @@ +from sympy.core.function import Function +from sympy.core.numbers import (I, Rational) +from sympy.core.singleton import S +from sympy.core.symbol import Symbol +from sympy.functions.elementary.exponential import exp +from sympy.functions.elementary.miscellaneous import sqrt +from sympy.functions.elementary.trigonometric import (cos, tan) +from sympy.testing.pytest import XFAIL + + +def test_add_eval(): + a = Symbol("a") + b = Symbol("b") + c = Rational(1) + p = Rational(5) + assert a*b + c + p == a*b + 6 + assert c + a + p == a + 6 + assert c + a - p == a + (-4) + assert a + a == 2*a + assert a + p + a == 2*a + 5 + assert c + p == Rational(6) + assert b + a - b == a + + +def test_addmul_eval(): + a = Symbol("a") + b = Symbol("b") + c = Rational(1) + p = Rational(5) + assert c + a + b*c + a - p == 2*a + b + (-4) + assert a*2 + p + a == a*2 + 5 + a + assert a*2 + p + a == 3*a + 5 + assert a*2 + a == 3*a + + +def test_pow_eval(): + # XXX Pow does not fully support conversion of negative numbers + # to their complex equivalent + + assert sqrt(-1) == I + + assert sqrt(-4) == 2*I + assert sqrt( 4) == 2 + assert (8)**Rational(1, 3) == 2 + assert (-8)**Rational(1, 3) == 2*((-1)**Rational(1, 3)) + + assert sqrt(-2) == I*sqrt(2) + assert (-1)**Rational(1, 3) != I + assert (-10)**Rational(1, 3) != I*((10)**Rational(1, 3)) + assert (-2)**Rational(1, 4) != (2)**Rational(1, 4) + + assert 64**Rational(1, 3) == 4 + assert 64**Rational(2, 3) == 16 + assert 24/sqrt(64) == 3 + assert (-27)**Rational(1, 3) == 3*(-1)**Rational(1, 3) + + assert (cos(2) / tan(2))**2 == (cos(2) / tan(2))**2 + + +@XFAIL +def test_pow_eval_X1(): + assert (-1)**Rational(1, 3) == S.Half + S.Half*I*sqrt(3) + + +def test_mulpow_eval(): + x = Symbol('x') + assert sqrt(50)/(sqrt(2)*x) == 5/x + assert sqrt(27)/sqrt(3) == 3 + + +def test_evalpow_bug(): + x = Symbol("x") + assert 1/(1/x) == x + assert 1/(-1/x) == -x + + +def test_symbol_expand(): + x = Symbol('x') + y = Symbol('y') + + f = x**4*y**4 + assert f == x**4*y**4 + assert f == f.expand() + + g = (x*y)**4 + assert g == f + assert g.expand() == f + assert g.expand() == g.expand().expand() + + +def test_function(): + f, l = map(Function, 'fl') + x = Symbol('x') + assert exp(l(x))*l(x)/exp(l(x)) == l(x) + assert exp(f(x))*f(x)/exp(f(x)) == f(x) diff --git a/lib/python3.12/site-packages/sympy/core/tests/test_evalf.py b/lib/python3.12/site-packages/sympy/core/tests/test_evalf.py new file mode 100644 index 0000000000000000000000000000000000000000..05d0fd0b58b756570c954d7d7e35e0c2cb589746 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/core/tests/test_evalf.py @@ -0,0 +1,734 @@ +import math + +from sympy.concrete.products import (Product, product) +from sympy.concrete.summations import Sum +from sympy.core.add import Add +from sympy.core.evalf import N +from sympy.core.function import (Function, nfloat) +from sympy.core.mul import Mul +from sympy.core import (GoldenRatio) +from sympy.core.numbers import (AlgebraicNumber, E, Float, I, Rational, + oo, zoo, nan, pi) +from sympy.core.power import Pow +from sympy.core.relational import Eq +from sympy.core.singleton import S +from sympy.core.symbol import Symbol +from sympy.core.sympify import sympify +from sympy.functions.combinatorial.factorials import factorial +from sympy.functions.combinatorial.numbers import fibonacci +from sympy.functions.elementary.complexes import (Abs, re, im) +from sympy.functions.elementary.exponential import (exp, log) +from sympy.functions.elementary.hyperbolic import (acosh, cosh) +from sympy.functions.elementary.integers import (ceiling, floor) +from sympy.functions.elementary.miscellaneous import (Max, sqrt) +from sympy.functions.elementary.trigonometric import (acos, atan, cos, sin, tan) +from sympy.integrals.integrals import (Integral, integrate) +from sympy.polys.polytools import factor +from sympy.polys.rootoftools import CRootOf +from sympy.polys.specialpolys import cyclotomic_poly +from sympy.printing import srepr +from sympy.printing.str import sstr +from sympy.simplify.simplify import simplify +from sympy.core.numbers import comp +from sympy.core.evalf import (complex_accuracy, PrecisionExhausted, + scaled_zero, get_integer_part, as_mpmath, evalf, _evalf_with_bounded_error) +from mpmath import inf, ninf, make_mpc +from mpmath.libmp.libmpf import from_float, fzero +from sympy.core.expr import unchanged +from sympy.testing.pytest import raises, XFAIL +from sympy.abc import n, x, y + + +def NS(e, n=15, **options): + return sstr(sympify(e).evalf(n, **options), full_prec=True) + + +def test_evalf_helpers(): + from mpmath.libmp import finf + assert complex_accuracy((from_float(2.0), None, 35, None)) == 35 + assert complex_accuracy((from_float(2.0), from_float(10.0), 35, 100)) == 37 + assert complex_accuracy( + (from_float(2.0), from_float(1000.0), 35, 100)) == 43 + assert complex_accuracy((from_float(2.0), from_float(10.0), 100, 35)) == 35 + assert complex_accuracy( + (from_float(2.0), from_float(1000.0), 100, 35)) == 35 + assert complex_accuracy(finf) == math.inf + assert complex_accuracy(zoo) == math.inf + raises(ValueError, lambda: get_integer_part(zoo, 1, {})) + + +def test_evalf_basic(): + assert NS('pi', 15) == '3.14159265358979' + assert NS('2/3', 10) == '0.6666666667' + assert NS('355/113-pi', 6) == '2.66764e-7' + assert NS('16*atan(1/5)-4*atan(1/239)', 15) == '3.14159265358979' + + +def test_cancellation(): + assert NS(Add(pi, Rational(1, 10**1000), -pi, evaluate=False), 15, + maxn=1200) == '1.00000000000000e-1000' + + +def test_evalf_powers(): + assert NS('pi**(10**20)', 10) == '1.339148777e+49714987269413385435' + assert NS(pi**(10**100), 10) == ('4.946362032e+4971498726941338543512682882' + '9089887365167832438044244613405349992494711208' + '95526746555473864642912223') + assert NS('2**(1/10**50)', 15) == '1.00000000000000' + assert NS('2**(1/10**50)-1', 15) == '6.93147180559945e-51' + +# Evaluation of Rump's ill-conditioned polynomial + + +def test_evalf_rump(): + a = 1335*y**6/4 + x**2*(11*x**2*y**2 - y**6 - 121*y**4 - 2) + 11*y**8/2 + x/(2*y) + assert NS(a, 15, subs={x: 77617, y: 33096}) == '-0.827396059946821' + + +def test_evalf_complex(): + assert NS('2*sqrt(pi)*I', 10) == '3.544907702*I' + assert NS('3+3*I', 15) == '3.00000000000000 + 3.00000000000000*I' + assert NS('E+pi*I', 15) == '2.71828182845905 + 3.14159265358979*I' + assert NS('pi * (3+4*I)', 15) == '9.42477796076938 + 12.5663706143592*I' + assert NS('I*(2+I)', 15) == '-1.00000000000000 + 2.00000000000000*I' + + +@XFAIL +def test_evalf_complex_bug(): + assert NS('(pi+E*I)*(E+pi*I)', 15) in ('0.e-15 + 17.25866050002*I', + '0.e-17 + 17.25866050002*I', '-0.e-17 + 17.25866050002*I') + + +def test_evalf_complex_powers(): + assert NS('(E+pi*I)**100000000000000000') == \ + '-3.58896782867793e+61850354284995199 + 4.58581754997159e+61850354284995199*I' + # XXX: rewrite if a+a*I simplification introduced in SymPy + #assert NS('(pi + pi*I)**2') in ('0.e-15 + 19.7392088021787*I', '0.e-16 + 19.7392088021787*I') + assert NS('(pi + pi*I)**2', chop=True) == '19.7392088021787*I' + assert NS( + '(pi + 1/10**8 + pi*I)**2') == '6.2831853e-8 + 19.7392088650106*I' + assert NS('(pi + 1/10**12 + pi*I)**2') == '6.283e-12 + 19.7392088021850*I' + assert NS('(pi + pi*I)**4', chop=True) == '-389.636364136010' + assert NS( + '(pi + 1/10**8 + pi*I)**4') == '-389.636366616512 + 2.4805021e-6*I' + assert NS('(pi + 1/10**12 + pi*I)**4') == '-389.636364136258 + 2.481e-10*I' + assert NS( + '(10000*pi + 10000*pi*I)**4', chop=True) == '-3.89636364136010e+18' + + +@XFAIL +def test_evalf_complex_powers_bug(): + assert NS('(pi + pi*I)**4') == '-389.63636413601 + 0.e-14*I' + + +def test_evalf_exponentiation(): + assert NS(sqrt(-pi)) == '1.77245385090552*I' + assert NS(Pow(pi*I, Rational( + 1, 2), evaluate=False)) == '1.25331413731550 + 1.25331413731550*I' + assert NS(pi**I) == '0.413292116101594 + 0.910598499212615*I' + assert NS(pi**(E + I/3)) == '20.8438653991931 + 8.36343473930031*I' + assert NS((pi + I/3)**(E + I/3)) == '17.2442906093590 + 13.6839376767037*I' + assert NS(exp(pi)) == '23.1406926327793' + assert NS(exp(pi + E*I)) == '-21.0981542849657 + 9.50576358282422*I' + assert NS(pi**pi) == '36.4621596072079' + assert NS((-pi)**pi) == '-32.9138577418939 - 15.6897116534332*I' + assert NS((-pi)**(-pi)) == '-0.0247567717232697 + 0.0118013091280262*I' + +# An example from Smith, "Multiple Precision Complex Arithmetic and Functions" + + +def test_evalf_complex_cancellation(): + A = Rational('63287/100000') + B = Rational('52498/100000') + C = Rational('69301/100000') + D = Rational('83542/100000') + F = Rational('2231321613/2500000000') + # XXX: the number of returned mantissa digits in the real part could + # change with the implementation. What matters is that the returned digits are + # correct; those that are showing now are correct. + # >>> ((A+B*I)*(C+D*I)).expand() + # 64471/10000000000 + 2231321613*I/2500000000 + # >>> 2231321613*4 + # 8925286452L + assert NS((A + B*I)*(C + D*I), 6) == '6.44710e-6 + 0.892529*I' + assert NS((A + B*I)*(C + D*I), 10) == '6.447100000e-6 + 0.8925286452*I' + assert NS((A + B*I)*( + C + D*I) - F*I, 5) in ('6.4471e-6 + 0.e-14*I', '6.4471e-6 - 0.e-14*I') + + +def test_evalf_logs(): + assert NS("log(3+pi*I)", 15) == '1.46877619736226 + 0.808448792630022*I' + assert NS("log(pi*I)", 15) == '1.14472988584940 + 1.57079632679490*I' + assert NS('log(-1 + 0.00001)', 2) == '-1.0e-5 + 3.1*I' + assert NS('log(100, 10, evaluate=False)', 15) == '2.00000000000000' + assert NS('-2*I*log(-(-1)**(S(1)/9))', 15) == '-5.58505360638185' + + +def test_evalf_trig(): + assert NS('sin(1)', 15) == '0.841470984807897' + assert NS('cos(1)', 15) == '0.540302305868140' + assert NS('sin(10**-6)', 15) == '9.99999999999833e-7' + assert NS('cos(10**-6)', 15) == '0.999999999999500' + assert NS('sin(E*10**100)', 15) == '0.409160531722613' + # Some input near roots + assert NS(sin(exp(pi*sqrt(163))*pi), 15) == '-2.35596641936785e-12' + assert NS(sin(pi*10**100 + Rational(7, 10**5), evaluate=False), 15, maxn=120) == \ + '6.99999999428333e-5' + assert NS(sin(Rational(7, 10**5), evaluate=False), 15) == \ + '6.99999999428333e-5' + +# Check detection of various false identities + + +def test_evalf_near_integers(): + # Binet's formula + f = lambda n: ((1 + sqrt(5))**n)/(2**n * sqrt(5)) + assert NS(f(5000) - fibonacci(5000), 10, maxn=1500) == '5.156009964e-1046' + # Some near-integer identities from + # http://mathworld.wolfram.com/AlmostInteger.html + assert NS('sin(2017*2**(1/5))', 15) == '-1.00000000000000' + assert NS('sin(2017*2**(1/5))', 20) == '-0.99999999999999997857' + assert NS('1+sin(2017*2**(1/5))', 15) == '2.14322287389390e-17' + assert NS('45 - 613*E/37 + 35/991', 15) == '6.03764498766326e-11' + + +def test_evalf_ramanujan(): + assert NS(exp(pi*sqrt(163)) - 640320**3 - 744, 10) == '-7.499274028e-13' + # A related identity + A = 262537412640768744*exp(-pi*sqrt(163)) + B = 196884*exp(-2*pi*sqrt(163)) + C = 103378831900730205293632*exp(-3*pi*sqrt(163)) + assert NS(1 - A - B + C, 10) == '1.613679005e-59' + +# Input that for various reasons have failed at some point + + +def test_evalf_bugs(): + assert NS(sin(1) + exp(-10**10), 10) == NS(sin(1), 10) + assert NS(exp(10**10) + sin(1), 10) == NS(exp(10**10), 10) + assert NS('expand_log(log(1+1/10**50))', 20) == '1.0000000000000000000e-50' + assert NS('log(10**100,10)', 10) == '100.0000000' + assert NS('log(2)', 10) == '0.6931471806' + assert NS( + '(sin(x)-x)/x**3', 15, subs={x: '1/10**50'}) == '-0.166666666666667' + assert NS(sin(1) + Rational( + 1, 10**100)*I, 15) == '0.841470984807897 + 1.00000000000000e-100*I' + assert x.evalf() == x + assert NS((1 + I)**2*I, 6) == '-2.00000' + d = {n: ( + -1)**Rational(6, 7), y: (-1)**Rational(4, 7), x: (-1)**Rational(2, 7)} + assert NS((x*(1 + y*(1 + n))).subs(d).evalf(), 6) == '0.346011 + 0.433884*I' + assert NS(((-I - sqrt(2)*I)**2).evalf()) == '-5.82842712474619' + assert NS((1 + I)**2*I, 15) == '-2.00000000000000' + # issue 4758 (1/2): + assert NS(pi.evalf(69) - pi) == '-4.43863937855894e-71' + # issue 4758 (2/2): With the bug present, this still only fails if the + # terms are in the order given here. This is not generally the case, + # because the order depends on the hashes of the terms. + assert NS(20 - 5008329267844*n**25 - 477638700*n**37 - 19*n, + subs={n: .01}) == '19.8100000000000' + assert NS(((x - 1)*(1 - x)**1000).n() + ) == '(1.00000000000000 - x)**1000*(x - 1.00000000000000)' + assert NS((-x).n()) == '-x' + assert NS((-2*x).n()) == '-2.00000000000000*x' + assert NS((-2*x*y).n()) == '-2.00000000000000*x*y' + assert cos(x).n(subs={x: 1+I}) == cos(x).subs(x, 1+I).n() + # issue 6660. Also NaN != mpmath.nan + # In this order: + # 0*nan, 0/nan, 0*inf, 0/inf + # 0+nan, 0-nan, 0+inf, 0-inf + # >>> n = Some Number + # n*nan, n/nan, n*inf, n/inf + # n+nan, n-nan, n+inf, n-inf + assert (0*E**(oo)).n() is S.NaN + assert (0/E**(oo)).n() is S.Zero + + assert (0+E**(oo)).n() is S.Infinity + assert (0-E**(oo)).n() is S.NegativeInfinity + + assert (5*E**(oo)).n() is S.Infinity + assert (5/E**(oo)).n() is S.Zero + + assert (5+E**(oo)).n() is S.Infinity + assert (5-E**(oo)).n() is S.NegativeInfinity + + #issue 7416 + assert as_mpmath(0.0, 10, {'chop': True}) == 0 + + #issue 5412 + assert ((oo*I).n() == S.Infinity*I) + assert ((oo+oo*I).n() == S.Infinity + S.Infinity*I) + + #issue 11518 + assert NS(2*x**2.5, 5) == '2.0000*x**2.5000' + + #issue 13076 + assert NS(Mul(Max(0, y), x, evaluate=False).evalf()) == 'x*Max(0, y)' + + #issue 18516 + assert NS(log(S(3273390607896141870013189696827599152216642046043064789483291368096133796404674554883270092325904157150886684127560071009217256545885393053328527589376)/36360291795869936842385267079543319118023385026001623040346035832580600191583895484198508262979388783308179702534403855752855931517013066142992430916562025780021771247847643450125342836565813209972590371590152578728008385990139795377610001).evalf(15, chop=True)) == '-oo' + + +def test_evalf_integer_parts(): + a = floor(log(8)/log(2) - exp(-1000), evaluate=False) + b = floor(log(8)/log(2), evaluate=False) + assert a.evalf() == 3.0 + assert b.evalf() == 3.0 + # equals, as a fallback, can still fail but it might succeed as here + assert ceiling(10*(sin(1)**2 + cos(1)**2)) == 10 + + assert int(floor(factorial(50)/E, evaluate=False).evalf(70)) == \ + int(11188719610782480504630258070757734324011354208865721592720336800) + assert int(ceiling(factorial(50)/E, evaluate=False).evalf(70)) == \ + int(11188719610782480504630258070757734324011354208865721592720336801) + assert int(floor(GoldenRatio**999 / sqrt(5) + S.Half) + .evalf(1000)) == fibonacci(999) + assert int(floor(GoldenRatio**1000 / sqrt(5) + S.Half) + .evalf(1000)) == fibonacci(1000) + + assert ceiling(x).evalf(subs={x: 3}) == 3.0 + assert ceiling(x).evalf(subs={x: 3*I}) == 3.0*I + assert ceiling(x).evalf(subs={x: 2 + 3*I}) == 2.0 + 3.0*I + assert ceiling(x).evalf(subs={x: 3.}) == 3.0 + assert ceiling(x).evalf(subs={x: 3.*I}) == 3.0*I + assert ceiling(x).evalf(subs={x: 2. + 3*I}) == 2.0 + 3.0*I + + assert float((floor(1.5, evaluate=False)+1/9).evalf()) == 1 + 1/9 + assert float((floor(0.5, evaluate=False)+20).evalf()) == 20 + + # issue 19991 + n = 1169809367327212570704813632106852886389036911 + r = 744723773141314414542111064094745678855643068 + + assert floor(n / (pi / 2)) == r + assert floor(80782 * sqrt(2)) == 114242 + + # issue 20076 + assert 260515 - floor(260515/pi + 1/2) * pi == atan(tan(260515)) + + assert floor(x).evalf(subs={x: sqrt(2)}) == 1.0 + + +def test_evalf_trig_zero_detection(): + a = sin(160*pi, evaluate=False) + t = a.evalf(maxn=100) + assert abs(t) < 1e-100 + assert t._prec < 2 + assert a.evalf(chop=True) == 0 + raises(PrecisionExhausted, lambda: a.evalf(strict=True)) + + +def test_evalf_sum(): + assert Sum(n,(n,1,2)).evalf() == 3. + assert Sum(n,(n,1,2)).doit().evalf() == 3. + # the next test should return instantly + assert Sum(1/n,(n,1,2)).evalf() == 1.5 + + # issue 8219 + assert Sum(E/factorial(n), (n, 0, oo)).evalf() == (E*E).evalf() + # issue 8254 + assert Sum(2**n*n/factorial(n), (n, 0, oo)).evalf() == (2*E*E).evalf() + # issue 8411 + s = Sum(1/x**2, (x, 100, oo)) + assert s.n() == s.doit().n() + + +def test_evalf_divergent_series(): + raises(ValueError, lambda: Sum(1/n, (n, 1, oo)).evalf()) + raises(ValueError, lambda: Sum(n/(n**2 + 1), (n, 1, oo)).evalf()) + raises(ValueError, lambda: Sum((-1)**n, (n, 1, oo)).evalf()) + raises(ValueError, lambda: Sum((-1)**n, (n, 1, oo)).evalf()) + raises(ValueError, lambda: Sum(n**2, (n, 1, oo)).evalf()) + raises(ValueError, lambda: Sum(2**n, (n, 1, oo)).evalf()) + raises(ValueError, lambda: Sum((-2)**n, (n, 1, oo)).evalf()) + raises(ValueError, lambda: Sum((2*n + 3)/(3*n**2 + 4), (n, 0, oo)).evalf()) + raises(ValueError, lambda: Sum((0.5*n**3)/(n**4 + 1), (n, 0, oo)).evalf()) + + +def test_evalf_product(): + assert Product(n, (n, 1, 10)).evalf() == 3628800. + assert comp(Product(1 - S.Half**2/n**2, (n, 1, oo)).n(5), 0.63662) + assert Product(n, (n, -1, 3)).evalf() == 0 + + +def test_evalf_py_methods(): + assert abs(float(pi + 1) - 4.1415926535897932) < 1e-10 + assert abs(complex(pi + 1) - 4.1415926535897932) < 1e-10 + assert abs( + complex(pi + E*I) - (3.1415926535897931 + 2.7182818284590451j)) < 1e-10 + raises(TypeError, lambda: float(pi + x)) + + +def test_evalf_power_subs_bugs(): + assert (x**2).evalf(subs={x: 0}) == 0 + assert sqrt(x).evalf(subs={x: 0}) == 0 + assert (x**Rational(2, 3)).evalf(subs={x: 0}) == 0 + assert (x**x).evalf(subs={x: 0}) == 1.0 + assert (3**x).evalf(subs={x: 0}) == 1.0 + assert exp(x).evalf(subs={x: 0}) == 1.0 + assert ((2 + I)**x).evalf(subs={x: 0}) == 1.0 + assert (0**x).evalf(subs={x: 0}) == 1.0 + + +def test_evalf_arguments(): + raises(TypeError, lambda: pi.evalf(method="garbage")) + + +def test_implemented_function_evalf(): + from sympy.utilities.lambdify import implemented_function + f = Function('f') + f = implemented_function(f, lambda x: x + 1) + assert str(f(x)) == "f(x)" + assert str(f(2)) == "f(2)" + assert f(2).evalf() == 3.0 + assert f(x).evalf() == f(x) + f = implemented_function(Function('sin'), lambda x: x + 1) + assert f(2).evalf() != sin(2) + del f._imp_ # XXX: due to caching _imp_ would influence all other tests + + +def test_evaluate_false(): + for no in [0, False]: + assert Add(3, 2, evaluate=no).is_Add + assert Mul(3, 2, evaluate=no).is_Mul + assert Pow(3, 2, evaluate=no).is_Pow + assert Pow(y, 2, evaluate=True) - Pow(y, 2, evaluate=True) == 0 + + +def test_evalf_relational(): + assert Eq(x/5, y/10).evalf() == Eq(0.2*x, 0.1*y) + # if this first assertion fails it should be replaced with + # one that doesn't + assert unchanged(Eq, (3 - I)**2/2 + I, 0) + assert Eq((3 - I)**2/2 + I, 0).n() is S.false + assert nfloat(Eq((3 - I)**2 + I, 0)) == S.false + + +def test_issue_5486(): + assert not cos(sqrt(0.5 + I)).n().is_Function + + +def test_issue_5486_bug(): + from sympy.core.expr import Expr + from sympy.core.numbers import I + assert abs(Expr._from_mpmath(I._to_mpmath(15), 15) - I) < 1.0e-15 + + +def test_bugs(): + from sympy.functions.elementary.complexes import (polar_lift, re) + + assert abs(re((1 + I)**2)) < 1e-15 + + # anything that evalf's to 0 will do in place of polar_lift + assert abs(polar_lift(0)).n() == 0 + + +def test_subs(): + assert NS('besseli(-x, y) - besseli(x, y)', subs={x: 3.5, y: 20.0}) == \ + '-4.92535585957223e-10' + assert NS('Piecewise((x, x>0)) + Piecewise((1-x, x>0))', subs={x: 0.1}) == \ + '1.00000000000000' + raises(TypeError, lambda: x.evalf(subs=(x, 1))) + + +def test_issue_4956_5204(): + # issue 4956 + v = S('''(-27*12**(1/3)*sqrt(31)*I + + 27*2**(2/3)*3**(1/3)*sqrt(31)*I)/(-2511*2**(2/3)*3**(1/3) + + (29*18**(1/3) + 9*2**(1/3)*3**(2/3)*sqrt(31)*I + + 87*2**(1/3)*3**(1/6)*I)**2)''') + assert NS(v, 1) == '0.e-118 - 0.e-118*I' + + # issue 5204 + v = S('''-(357587765856 + 18873261792*249**(1/2) + 56619785376*I*83**(1/2) + + 108755765856*I*3**(1/2) + 41281887168*6**(1/3)*(1422 + + 54*249**(1/2))**(1/3) - 1239810624*6**(1/3)*249**(1/2)*(1422 + + 54*249**(1/2))**(1/3) - 3110400000*I*6**(1/3)*83**(1/2)*(1422 + + 54*249**(1/2))**(1/3) + 13478400000*I*3**(1/2)*6**(1/3)*(1422 + + 54*249**(1/2))**(1/3) + 1274950152*6**(2/3)*(1422 + + 54*249**(1/2))**(2/3) + 32347944*6**(2/3)*249**(1/2)*(1422 + + 54*249**(1/2))**(2/3) - 1758790152*I*3**(1/2)*6**(2/3)*(1422 + + 54*249**(1/2))**(2/3) - 304403832*I*6**(2/3)*83**(1/2)*(1422 + + 4*249**(1/2))**(2/3))/(175732658352 + (1106028 + 25596*249**(1/2) + + 76788*I*83**(1/2))**2)''') + assert NS(v, 5) == '0.077284 + 1.1104*I' + assert NS(v, 1) == '0.08 + 1.*I' + + +def test_old_docstring(): + a = (E + pi*I)*(E - pi*I) + assert NS(a) == '17.2586605000200' + assert a.n() == 17.25866050002001 + + +def test_issue_4806(): + assert integrate(atan(x)**2, (x, -1, 1)).evalf().round(1) == Float(0.5, 1) + assert atan(0, evaluate=False).n() == 0 + + +def test_evalf_mul(): + # SymPy should not try to expand this; it should be handled term-wise + # in evalf through mpmath + assert NS(product(1 + sqrt(n)*I, (n, 1, 500)), 1) == '5.e+567 + 2.e+568*I' + + +def test_scaled_zero(): + a, b = (([0], 1, 100, 1), -1) + assert scaled_zero(100) == (a, b) + assert scaled_zero(a) == (0, 1, 100, 1) + a, b = (([1], 1, 100, 1), -1) + assert scaled_zero(100, -1) == (a, b) + assert scaled_zero(a) == (1, 1, 100, 1) + raises(ValueError, lambda: scaled_zero(scaled_zero(100))) + raises(ValueError, lambda: scaled_zero(100, 2)) + raises(ValueError, lambda: scaled_zero(100, 0)) + raises(ValueError, lambda: scaled_zero((1, 5, 1, 3))) + + +def test_chop_value(): + for i in range(-27, 28): + assert (Pow(10, i)*2).n(chop=10**i) and not (Pow(10, i)).n(chop=10**i) + + +def test_infinities(): + assert oo.evalf(chop=True) == inf + assert (-oo).evalf(chop=True) == ninf + + +def test_to_mpmath(): + assert sqrt(3)._to_mpmath(20)._mpf_ == (0, int(908093), -19, 20) + assert S(3.2)._to_mpmath(20)._mpf_ == (0, int(838861), -18, 20) + + +def test_issue_6632_evalf(): + add = (-100000*sqrt(2500000001) + 5000000001) + assert add.n() == 9.999999998e-11 + assert (add*add).n() == 9.999999996e-21 + + +def test_issue_4945(): + from sympy.abc import H + assert (H/0).evalf(subs={H:1}) == zoo + + +def test_evalf_integral(): + # test that workprec has to increase in order to get a result other than 0 + eps = Rational(1, 1000000) + assert Integral(sin(x), (x, -pi, pi + eps)).n(2)._prec == 10 + + +def test_issue_8821_highprec_from_str(): + s = str(pi.evalf(128)) + p = N(s) + assert Abs(sin(p)) < 1e-15 + p = N(s, 64) + assert Abs(sin(p)) < 1e-64 + + +def test_issue_8853(): + p = Symbol('x', even=True, positive=True) + assert floor(-p - S.Half).is_even == False + assert floor(-p + S.Half).is_even == True + assert ceiling(p - S.Half).is_even == True + assert ceiling(p + S.Half).is_even == False + + assert get_integer_part(S.Half, -1, {}, True) == (0, 0) + assert get_integer_part(S.Half, 1, {}, True) == (1, 0) + assert get_integer_part(Rational(-1, 2), -1, {}, True) == (-1, 0) + assert get_integer_part(Rational(-1, 2), 1, {}, True) == (0, 0) + + +def test_issue_17681(): + class identity_func(Function): + + def _eval_evalf(self, *args, **kwargs): + return self.args[0].evalf(*args, **kwargs) + + assert floor(identity_func(S(0))) == 0 + assert get_integer_part(S(0), 1, {}, True) == (0, 0) + + +def test_issue_9326(): + from sympy.core.symbol import Dummy + d1 = Dummy('d') + d2 = Dummy('d') + e = d1 + d2 + assert e.evalf(subs = {d1: 1, d2: 2}) == 3.0 + + +def test_issue_10323(): + assert ceiling(sqrt(2**30 + 1)) == 2**15 + 1 + + +def test_AssocOp_Function(): + # the first arg of Min is not comparable in the imaginary part + raises(ValueError, lambda: S(''' + Min(-sqrt(3)*cos(pi/18)/6 + re(1/((-1/2 - sqrt(3)*I/2)*(1/6 + + sqrt(3)*I/18)**(1/3)))/3 + sin(pi/18)/2 + 2 + I*(-cos(pi/18)/2 - + sqrt(3)*sin(pi/18)/6 + im(1/((-1/2 - sqrt(3)*I/2)*(1/6 + + sqrt(3)*I/18)**(1/3)))/3), re(1/((-1/2 + sqrt(3)*I/2)*(1/6 + + sqrt(3)*I/18)**(1/3)))/3 - sqrt(3)*cos(pi/18)/6 - sin(pi/18)/2 + 2 + + I*(im(1/((-1/2 + sqrt(3)*I/2)*(1/6 + sqrt(3)*I/18)**(1/3)))/3 - + sqrt(3)*sin(pi/18)/6 + cos(pi/18)/2))''')) + # if that is changed so a non-comparable number remains as + # an arg, then the Min/Max instantiation needs to be changed + # to watch out for non-comparable args when making simplifications + # and the following test should be added instead (with e being + # the sympified expression above): + # raises(ValueError, lambda: e._eval_evalf(2)) + + +def test_issue_10395(): + eq = x*Max(0, y) + assert nfloat(eq) == eq + eq = x*Max(y, -1.1) + assert nfloat(eq) == eq + assert Max(y, 4).n() == Max(4.0, y) + + +def test_issue_13098(): + assert floor(log(S('9.'+'9'*20), 10)) == 0 + assert ceiling(log(S('9.'+'9'*20), 10)) == 1 + assert floor(log(20 - S('9.'+'9'*20), 10)) == 1 + assert ceiling(log(20 - S('9.'+'9'*20), 10)) == 2 + + +def test_issue_14601(): + e = 5*x*y/2 - y*(35*(x**3)/2 - 15*x/2) + subst = {x:0.0, y:0.0} + e2 = e.evalf(subs=subst) + assert float(e2) == 0.0 + assert float((x + x*(x**2 + x)).evalf(subs={x: 0.0})) == 0.0 + + +def test_issue_11151(): + z = S.Zero + e = Sum(z, (x, 1, 2)) + assert e != z # it shouldn't evaluate + # when it does evaluate, this is what it should give + assert evalf(e, 15, {}) == \ + evalf(z, 15, {}) == (None, None, 15, None) + # so this shouldn't fail + assert (e/2).n() == 0 + # this was where the issue appeared + expr0 = Sum(x**2 + x, (x, 1, 2)) + expr1 = Sum(0, (x, 1, 2)) + expr2 = expr1/expr0 + assert simplify(factor(expr2) - expr2) == 0 + + +def test_issue_13425(): + assert N('2**.5', 30) == N('sqrt(2)', 30) + assert N('x - x', 30) == 0 + assert abs((N('pi*.1', 22)*10 - pi).n()) < 1e-22 + + +def test_issue_17421(): + assert N(acos(-I + acosh(cosh(cosh(1) + I)))) == 1.0*I + + +def test_issue_20291(): + from sympy.sets import EmptySet, Reals + from sympy.sets.sets import (Complement, FiniteSet, Intersection) + a = Symbol('a') + b = Symbol('b') + A = FiniteSet(a, b) + assert A.evalf(subs={a: 1, b: 2}) == FiniteSet(1.0, 2.0) + B = FiniteSet(a-b, 1) + assert B.evalf(subs={a: 1, b: 2}) == FiniteSet(-1.0, 1.0) + + sol = Complement(Intersection(FiniteSet(-b/2 - sqrt(b**2-4*pi)/2), Reals), FiniteSet(0)) + assert sol.evalf(subs={b: 1}) == EmptySet + + +def test_evalf_with_zoo(): + assert (1/x).evalf(subs={x: 0}) == zoo # issue 8242 + assert (-1/x).evalf(subs={x: 0}) == zoo # PR 16150 + assert (0 ** x).evalf(subs={x: -1}) == zoo # PR 16150 + assert (0 ** x).evalf(subs={x: -1 + I}) == nan + assert Mul(2, Pow(0, -1, evaluate=False), evaluate=False).evalf() == zoo # issue 21147 + assert Mul(x, 1/x, evaluate=False).evalf(subs={x: 0}) == Mul(x, 1/x, evaluate=False).subs(x, 0) == nan + assert Mul(1/x, 1/x, evaluate=False).evalf(subs={x: 0}) == zoo + assert Mul(1/x, Abs(1/x), evaluate=False).evalf(subs={x: 0}) == zoo + assert Abs(zoo, evaluate=False).evalf() == oo + assert re(zoo, evaluate=False).evalf() == nan + assert im(zoo, evaluate=False).evalf() == nan + assert Add(zoo, zoo, evaluate=False).evalf() == nan + assert Add(oo, zoo, evaluate=False).evalf() == nan + assert Pow(zoo, -1, evaluate=False).evalf() == 0 + assert Pow(zoo, Rational(-1, 3), evaluate=False).evalf() == 0 + assert Pow(zoo, Rational(1, 3), evaluate=False).evalf() == zoo + assert Pow(zoo, S.Half, evaluate=False).evalf() == zoo + assert Pow(zoo, 2, evaluate=False).evalf() == zoo + assert Pow(0, zoo, evaluate=False).evalf() == nan + assert log(zoo, evaluate=False).evalf() == zoo + assert zoo.evalf(chop=True) == zoo + assert x.evalf(subs={x: zoo}) == zoo + + +def test_evalf_with_bounded_error(): + cases = [ + # zero + (Rational(0), None, 1), + # zero im part + (pi, None, 10), + # zero real part + (pi*I, None, 10), + # re and im nonzero + (2-3*I, None, 5), + # similar tests again, but using eps instead of m + (Rational(0), Rational(1, 2), None), + (pi, Rational(1, 1000), None), + (pi * I, Rational(1, 1000), None), + (2 - 3 * I, Rational(1, 1000), None), + # very large eps + (2 - 3 * I, Rational(1000), None), + # case where x already small, hence some cancellation in p = m + n - 1 + (Rational(1234, 10**8), Rational(1, 10**12), None), + ] + for x0, eps, m in cases: + a, b, _, _ = evalf(x0, 53, {}) + c, d, _, _ = _evalf_with_bounded_error(x0, eps, m) + if eps is None: + eps = 2**(-m) + z = make_mpc((a or fzero, b or fzero)) + w = make_mpc((c or fzero, d or fzero)) + assert abs(w - z) < eps + + # eps must be positive + raises(ValueError, lambda: _evalf_with_bounded_error(pi, Rational(0))) + raises(ValueError, lambda: _evalf_with_bounded_error(pi, -pi)) + raises(ValueError, lambda: _evalf_with_bounded_error(pi, I)) + + +def test_issue_22849(): + a = -8 + 3 * sqrt(3) + x = AlgebraicNumber(a) + assert evalf(a, 1, {}) == evalf(x, 1, {}) + + +def test_evalf_real_alg_num(): + # This test demonstrates why the entry for `AlgebraicNumber` in + # `sympy.core.evalf._create_evalf_table()` has to use `x.to_root()`, + # instead of `x.as_expr()`. If the latter is used, then `z` will be + # a complex number with `0.e-20` for imaginary part, even though `a5` + # is a real number. + zeta = Symbol('zeta') + a5 = AlgebraicNumber(CRootOf(cyclotomic_poly(5), -1), [-1, -1, 0, 0], alias=zeta) + z = a5.evalf() + assert isinstance(z, Float) + assert not hasattr(z, '_mpc_') + assert hasattr(z, '_mpf_') + + +def test_issue_20733(): + expr = 1/((x - 9)*(x - 8)*(x - 7)*(x - 4)**2*(x - 3)**3*(x - 2)) + assert str(expr.evalf(1, subs={x:1})) == '-4.e-5' + assert str(expr.evalf(2, subs={x:1})) == '-4.1e-5' + assert str(expr.evalf(11, subs={x:1})) == '-4.1335978836e-5' + assert str(expr.evalf(20, subs={x:1})) == '-0.000041335978835978835979' + + expr = Mul(*((x - i) for i in range(2, 1000))) + assert srepr(expr.evalf(2, subs={x: 1})) == "Float('4.0271e+2561', precision=10)" + assert srepr(expr.evalf(10, subs={x: 1})) == "Float('4.02790050126e+2561', precision=37)" + assert srepr(expr.evalf(53, subs={x: 1})) == "Float('4.0279005012722099453824067459760158730668154575647110393e+2561', precision=179)" diff --git a/lib/python3.12/site-packages/sympy/core/tests/test_expand.py b/lib/python3.12/site-packages/sympy/core/tests/test_expand.py new file mode 100644 index 0000000000000000000000000000000000000000..e7abb5daacebbe81664b3de3a7ac35a490ab31bc --- /dev/null +++ b/lib/python3.12/site-packages/sympy/core/tests/test_expand.py @@ -0,0 +1,364 @@ +from sympy.core.expr import unchanged +from sympy.core.mul import Mul +from sympy.core.numbers import (I, Rational as R, pi) +from sympy.core.power import Pow +from sympy.core.singleton import S +from sympy.core.symbol import Symbol +from sympy.functions.combinatorial.factorials import factorial +from sympy.functions.elementary.exponential import (exp, log) +from sympy.functions.elementary.miscellaneous import sqrt +from sympy.functions.elementary.trigonometric import (cos, sin) +from sympy.series.order import O +from sympy.simplify.radsimp import expand_numer +from sympy.core.function import (expand, expand_multinomial, + expand_power_base, expand_log) + +from sympy.testing.pytest import raises +from sympy.core.random import verify_numerically + +from sympy.abc import x, y, z + + +def test_expand_no_log(): + assert ( + (1 + log(x**4))**2).expand(log=False) == 1 + 2*log(x**4) + log(x**4)**2 + assert ((1 + log(x**4))*(1 + log(x**3))).expand( + log=False) == 1 + log(x**4) + log(x**3) + log(x**4)*log(x**3) + + +def test_expand_no_multinomial(): + assert ((1 + x)*(1 + (1 + x)**4)).expand(multinomial=False) == \ + 1 + x + (1 + x)**4 + x*(1 + x)**4 + + +def test_expand_negative_integer_powers(): + expr = (x + y)**(-2) + assert expr.expand() == 1 / (2*x*y + x**2 + y**2) + assert expr.expand(multinomial=False) == (x + y)**(-2) + expr = (x + y)**(-3) + assert expr.expand() == 1 / (3*x*x*y + 3*x*y*y + x**3 + y**3) + assert expr.expand(multinomial=False) == (x + y)**(-3) + expr = (x + y)**(2) * (x + y)**(-4) + assert expr.expand() == 1 / (2*x*y + x**2 + y**2) + assert expr.expand(multinomial=False) == (x + y)**(-2) + + +def test_expand_non_commutative(): + A = Symbol('A', commutative=False) + B = Symbol('B', commutative=False) + C = Symbol('C', commutative=False) + a = Symbol('a') + b = Symbol('b') + i = Symbol('i', integer=True) + n = Symbol('n', negative=True) + m = Symbol('m', negative=True) + p = Symbol('p', polar=True) + np = Symbol('p', polar=False) + + assert (C*(A + B)).expand() == C*A + C*B + assert (C*(A + B)).expand() != A*C + B*C + assert ((A + B)**2).expand() == A**2 + A*B + B*A + B**2 + assert ((A + B)**3).expand() == (A**2*B + B**2*A + A*B**2 + B*A**2 + + A**3 + B**3 + A*B*A + B*A*B) + # issue 6219 + assert ((a*A*B*A**-1)**2).expand() == a**2*A*B**2/A + # Note that (a*A*B*A**-1)**2 is automatically converted to a**2*(A*B*A**-1)**2 + assert ((a*A*B*A**-1)**2).expand(deep=False) == a**2*(A*B*A**-1)**2 + assert ((a*A*B*A**-1)**2).expand() == a**2*(A*B**2*A**-1) + assert ((a*A*B*A**-1)**2).expand(force=True) == a**2*A*B**2*A**(-1) + assert ((a*A*B)**2).expand() == a**2*A*B*A*B + assert ((a*A)**2).expand() == a**2*A**2 + assert ((a*A*B)**i).expand() == a**i*(A*B)**i + assert ((a*A*(B*(A*B/A)**2))**i).expand() == a**i*(A*B*A*B**2/A)**i + # issue 6558 + assert (A*B*(A*B)**-1).expand() == 1 + assert ((a*A)**i).expand() == a**i*A**i + assert ((a*A*B*A**-1)**3).expand() == a**3*A*B**3/A + assert ((a*A*B*A*B/A)**3).expand() == \ + a**3*A*B*(A*B**2)*(A*B**2)*A*B*A**(-1) + assert ((a*A*B*A*B/A)**-2).expand() == \ + A*B**-1*A**-1*B**-2*A**-1*B**-1*A**-1/a**2 + assert ((a*b*A*B*A**-1)**i).expand() == a**i*b**i*(A*B/A)**i + assert ((a*(a*b)**i)**i).expand() == a**i*a**(i**2)*b**(i**2) + e = Pow(Mul(a, 1/a, A, B, evaluate=False), S(2), evaluate=False) + assert e.expand() == A*B*A*B + assert sqrt(a*(A*b)**i).expand() == sqrt(a*b**i*A**i) + assert (sqrt(-a)**a).expand() == sqrt(-a)**a + assert expand((-2*n)**(i/3)) == 2**(i/3)*(-n)**(i/3) + assert expand((-2*n*m)**(i/a)) == (-2)**(i/a)*(-n)**(i/a)*(-m)**(i/a) + assert expand((-2*a*p)**b) == 2**b*p**b*(-a)**b + assert expand((-2*a*np)**b) == 2**b*(-a*np)**b + assert expand(sqrt(A*B)) == sqrt(A*B) + assert expand(sqrt(-2*a*b)) == sqrt(2)*sqrt(-a*b) + + +def test_expand_radicals(): + a = (x + y)**R(1, 2) + + assert (a**1).expand() == a + assert (a**3).expand() == x*a + y*a + assert (a**5).expand() == x**2*a + 2*x*y*a + y**2*a + + assert (1/a**1).expand() == 1/a + assert (1/a**3).expand() == 1/(x*a + y*a) + assert (1/a**5).expand() == 1/(x**2*a + 2*x*y*a + y**2*a) + + a = (x + y)**R(1, 3) + + assert (a**1).expand() == a + assert (a**2).expand() == a**2 + assert (a**4).expand() == x*a + y*a + assert (a**5).expand() == x*a**2 + y*a**2 + assert (a**7).expand() == x**2*a + 2*x*y*a + y**2*a + + +def test_expand_modulus(): + assert ((x + y)**11).expand(modulus=11) == x**11 + y**11 + assert ((x + sqrt(2)*y)**11).expand(modulus=11) == x**11 + 10*sqrt(2)*y**11 + assert (x + y/2).expand(modulus=1) == y/2 + + raises(ValueError, lambda: ((x + y)**11).expand(modulus=0)) + raises(ValueError, lambda: ((x + y)**11).expand(modulus=x)) + + +def test_issue_5743(): + assert (x*sqrt( + x + y)*(1 + sqrt(x + y))).expand() == x**2 + x*y + x*sqrt(x + y) + assert (x*sqrt( + x + y)*(1 + x*sqrt(x + y))).expand() == x**3 + x**2*y + x*sqrt(x + y) + + +def test_expand_frac(): + assert expand((x + y)*y/x/(x + 1), frac=True) == \ + (x*y + y**2)/(x**2 + x) + assert expand((x + y)*y/x/(x + 1), numer=True) == \ + (x*y + y**2)/(x*(x + 1)) + assert expand((x + y)*y/x/(x + 1), denom=True) == \ + y*(x + y)/(x**2 + x) + eq = (x + 1)**2/y + assert expand_numer(eq, multinomial=False) == eq + # issue 26329 + eq = (exp(x*z) - exp(y*z))/exp(z*(x + y)) + ans = exp(-y*z) - exp(-x*z) + assert eq.expand(numer=True) != ans + assert eq.expand(numer=True, exact=True) == ans + assert expand_numer(eq) != ans + assert expand_numer(eq, exact=True) == ans + + +def test_issue_6121(): + eq = -I*exp(-3*I*pi/4)/(4*pi**(S(3)/2)*sqrt(x)) + assert eq.expand(complex=True) # does not give oo recursion + eq = -I*exp(-3*I*pi/4)/(4*pi**(R(3, 2))*sqrt(x)) + assert eq.expand(complex=True) # does not give oo recursion + + +def test_expand_power_base(): + assert expand_power_base((x*y*z)**4) == x**4*y**4*z**4 + assert expand_power_base((x*y*z)**x).is_Pow + assert expand_power_base((x*y*z)**x, force=True) == x**x*y**x*z**x + assert expand_power_base((x*(y*z)**2)**3) == x**3*y**6*z**6 + + assert expand_power_base((sin((x*y)**2)*y)**z).is_Pow + assert expand_power_base( + (sin((x*y)**2)*y)**z, force=True) == sin((x*y)**2)**z*y**z + assert expand_power_base( + (sin((x*y)**2)*y)**z, deep=True) == (sin(x**2*y**2)*y)**z + + assert expand_power_base(exp(x)**2) == exp(2*x) + assert expand_power_base((exp(x)*exp(y))**2) == exp(2*x)*exp(2*y) + + assert expand_power_base( + (exp((x*y)**z)*exp(y))**2) == exp(2*(x*y)**z)*exp(2*y) + assert expand_power_base((exp((x*y)**z)*exp( + y))**2, deep=True, force=True) == exp(2*x**z*y**z)*exp(2*y) + + assert expand_power_base((exp(x)*exp(y))**z).is_Pow + assert expand_power_base( + (exp(x)*exp(y))**z, force=True) == exp(x)**z*exp(y)**z + + +def test_expand_arit(): + a = Symbol("a") + b = Symbol("b", positive=True) + c = Symbol("c") + + p = R(5) + e = (a + b)*c + assert e == c*(a + b) + assert (e.expand() - a*c - b*c) == R(0) + e = (a + b)*(a + b) + assert e == (a + b)**2 + assert e.expand() == 2*a*b + a**2 + b**2 + e = (a + b)*(a + b)**R(2) + assert e == (a + b)**3 + assert e.expand() == 3*b*a**2 + 3*a*b**2 + a**3 + b**3 + assert e.expand() == 3*b*a**2 + 3*a*b**2 + a**3 + b**3 + e = (a + b)*(a + c)*(b + c) + assert e == (a + c)*(a + b)*(b + c) + assert e.expand() == 2*a*b*c + b*a**2 + c*a**2 + b*c**2 + a*c**2 + c*b**2 + a*b**2 + e = (a + R(1))**p + assert e == (1 + a)**5 + assert e.expand() == 1 + 5*a + 10*a**2 + 10*a**3 + 5*a**4 + a**5 + e = (a + b + c)*(a + c + p) + assert e == (5 + a + c)*(a + b + c) + assert e.expand() == 5*a + 5*b + 5*c + 2*a*c + b*c + a*b + a**2 + c**2 + x = Symbol("x") + s = exp(x*x) - 1 + e = s.nseries(x, 0, 6)/x**2 + assert e.expand() == 1 + x**2/2 + O(x**4) + + e = (x*(y + z))**(x*(y + z))*(x + y) + assert e.expand(power_exp=False, power_base=False) == x*(x*y + x* + z)**(x*y + x*z) + y*(x*y + x*z)**(x*y + x*z) + assert e.expand(power_exp=False, power_base=False, deep=False) == x* \ + (x*(y + z))**(x*(y + z)) + y*(x*(y + z))**(x*(y + z)) + e = x * (x + (y + 1)**2) + assert e.expand(deep=False) == x**2 + x*(y + 1)**2 + e = (x*(y + z))**z + assert e.expand(power_base=True, mul=True, deep=True) in [x**z*(y + + z)**z, (x*y + x*z)**z] + assert ((2*y)**z).expand() == 2**z*y**z + p = Symbol('p', positive=True) + assert sqrt(-x).expand().is_Pow + assert sqrt(-x).expand(force=True) == I*sqrt(x) + assert ((2*y*p)**z).expand() == 2**z*p**z*y**z + assert ((2*y*p*x)**z).expand() == 2**z*p**z*(x*y)**z + assert ((2*y*p*x)**z).expand(force=True) == 2**z*p**z*x**z*y**z + assert ((2*y*p*-pi)**z).expand() == 2**z*pi**z*p**z*(-y)**z + assert ((2*y*p*-pi*x)**z).expand() == 2**z*pi**z*p**z*(-x*y)**z + n = Symbol('n', negative=True) + m = Symbol('m', negative=True) + assert ((-2*x*y*n)**z).expand() == 2**z*(-n)**z*(x*y)**z + assert ((-2*x*y*n*m)**z).expand() == 2**z*(-m)**z*(-n)**z*(-x*y)**z + # issue 5482 + assert sqrt(-2*x*n) == sqrt(2)*sqrt(-n)*sqrt(x) + # issue 5605 (2) + assert (cos(x + y)**2).expand(trig=True) in [ + (-sin(x)*sin(y) + cos(x)*cos(y))**2, + sin(x)**2*sin(y)**2 - 2*sin(x)*sin(y)*cos(x)*cos(y) + cos(x)**2*cos(y)**2 + ] + + # Check that this isn't too slow + x = Symbol('x') + W = 1 + for i in range(1, 21): + W = W * (x - i) + W = W.expand() + assert W.has(-1672280820*x**15) + +def test_expand_mul(): + # part of issue 20597 + e = Mul(2, 3, evaluate=False) + assert e.expand() == 6 + + e = Mul(2, 3, 1/x, evaluate=False) + assert e.expand() == 6/x + e = Mul(2, R(1, 3), evaluate=False) + assert e.expand() == R(2, 3) + +def test_power_expand(): + """Test for Pow.expand()""" + a = Symbol('a') + b = Symbol('b') + p = (a + b)**2 + assert p.expand() == a**2 + b**2 + 2*a*b + + p = (1 + 2*(1 + a))**2 + assert p.expand() == 9 + 4*(a**2) + 12*a + + p = 2**(a + b) + assert p.expand() == 2**a*2**b + + A = Symbol('A', commutative=False) + B = Symbol('B', commutative=False) + assert (2**(A + B)).expand() == 2**(A + B) + assert (A**(a + b)).expand() != A**(a + b) + + +def test_issues_5919_6830(): + # issue 5919 + n = -1 + 1/x + z = n/x/(-n)**2 - 1/n/x + assert expand(z) == 1/(x**2 - 2*x + 1) - 1/(x - 2 + 1/x) - 1/(-x + 1) + + # issue 6830 + p = (1 + x)**2 + assert expand_multinomial((1 + x*p)**2) == ( + x**2*(x**4 + 4*x**3 + 6*x**2 + 4*x + 1) + 2*x*(x**2 + 2*x + 1) + 1) + assert expand_multinomial((1 + (y + x)*p)**2) == ( + 2*((x + y)*(x**2 + 2*x + 1)) + (x**2 + 2*x*y + y**2)* + (x**4 + 4*x**3 + 6*x**2 + 4*x + 1) + 1) + A = Symbol('A', commutative=False) + p = (1 + A)**2 + assert expand_multinomial((1 + x*p)**2) == ( + x**2*(1 + 4*A + 6*A**2 + 4*A**3 + A**4) + 2*x*(1 + 2*A + A**2) + 1) + assert expand_multinomial((1 + (y + x)*p)**2) == ( + (x + y)*(1 + 2*A + A**2)*2 + (x**2 + 2*x*y + y**2)* + (1 + 4*A + 6*A**2 + 4*A**3 + A**4) + 1) + assert expand_multinomial((1 + (y + x)*p)**3) == ( + (x + y)*(1 + 2*A + A**2)*3 + (x**2 + 2*x*y + y**2)*(1 + 4*A + + 6*A**2 + 4*A**3 + A**4)*3 + (x**3 + 3*x**2*y + 3*x*y**2 + y**3)*(1 + 6*A + + 15*A**2 + 20*A**3 + 15*A**4 + 6*A**5 + A**6) + 1) + # unevaluate powers + eq = (Pow((x + 1)*((A + 1)**2), 2, evaluate=False)) + # - in this case the base is not an Add so no further + # expansion is done + assert expand_multinomial(eq) == \ + (x**2 + 2*x + 1)*(1 + 4*A + 6*A**2 + 4*A**3 + A**4) + # - but here, the expanded base *is* an Add so it gets expanded + eq = (Pow(((A + 1)**2), 2, evaluate=False)) + assert expand_multinomial(eq) == 1 + 4*A + 6*A**2 + 4*A**3 + A**4 + + # coverage + def ok(a, b, n): + e = (a + I*b)**n + return verify_numerically(e, expand_multinomial(e)) + + for a in [2, S.Half]: + for b in [3, R(1, 3)]: + for n in range(2, 6): + assert ok(a, b, n) + + assert expand_multinomial((x + 1 + O(z))**2) == \ + 1 + 2*x + x**2 + O(z) + assert expand_multinomial((x + 1 + O(z))**3) == \ + 1 + 3*x + 3*x**2 + x**3 + O(z) + + assert expand_multinomial(3**(x + y + 3)) == 27*3**(x + y) + +def test_expand_log(): + t = Symbol('t', positive=True) + # after first expansion, -2*log(2) + log(4); then 0 after second + assert expand(log(t**2) - log(t**2/4) - 2*log(2)) == 0 + assert expand_log(log(7*6)/log(6)) == 1 + log(7)/log(6) + b = factorial(10) + assert expand_log(log(7*b**4)/log(b) + ) == 4 + log(7)/log(b) + + +def test_issue_23952(): + assert (x**(y + z)).expand(force=True) == x**y*x**z + one = Symbol('1', integer=True, prime=True, odd=True, positive=True) + two = Symbol('2', integer=True, prime=True, even=True) + e = two - one + for b in (0, x): + # 0**e = 0, 0**-e = zoo; but if expanded then nan + assert unchanged(Pow, b, e) # power_exp + assert unchanged(Pow, b, -e) # power_exp + assert unchanged(Pow, b, y - x) # power_exp + assert unchanged(Pow, b, 3 - x) # multinomial + assert (b**e).expand().is_Pow # power_exp + assert (b**-e).expand().is_Pow # power_exp + assert (b**(y - x)).expand().is_Pow # power_exp + assert (b**(3 - x)).expand().is_Pow # multinomial + nn1 = Symbol('nn1', nonnegative=True) + nn2 = Symbol('nn2', nonnegative=True) + nn3 = Symbol('nn3', nonnegative=True) + assert (x**(nn1 + nn2)).expand() == x**nn1*x**nn2 + assert (x**(-nn1 - nn2)).expand() == x**-nn1*x**-nn2 + assert unchanged(Pow, x, nn1 + nn2 - nn3) + assert unchanged(Pow, x, 1 + nn2 - nn3) + assert unchanged(Pow, x, nn1 - nn2) + assert unchanged(Pow, x, 1 - nn2) + assert unchanged(Pow, x, -1 + nn2) diff --git a/lib/python3.12/site-packages/sympy/core/tests/test_expr.py b/lib/python3.12/site-packages/sympy/core/tests/test_expr.py new file mode 100644 index 0000000000000000000000000000000000000000..8299ca312cad03d68064c592e4f4e5adc7a96fcc --- /dev/null +++ b/lib/python3.12/site-packages/sympy/core/tests/test_expr.py @@ -0,0 +1,2288 @@ +from sympy.assumptions.refine import refine +from sympy.concrete.summations import Sum +from sympy.core.add import Add +from sympy.core.basic import Basic +from sympy.core.containers import Tuple +from sympy.core.expr import (ExprBuilder, unchanged, Expr, + UnevaluatedExpr) +from sympy.core.function import (Function, expand, WildFunction, + AppliedUndef, Derivative, diff, Subs) +from sympy.core.mul import Mul +from sympy.core.numbers import (NumberSymbol, E, zoo, oo, Float, I, + Rational, nan, Integer, Number, pi, _illegal) +from sympy.core.power import Pow +from sympy.core.relational import Ge, Lt, Gt, Le +from sympy.core.singleton import S +from sympy.core.sorting import default_sort_key +from sympy.core.symbol import Symbol, symbols, Dummy, Wild +from sympy.core.sympify import sympify +from sympy.functions.combinatorial.factorials import factorial +from sympy.functions.elementary.exponential import exp_polar, exp, log +from sympy.functions.elementary.miscellaneous import sqrt, Max +from sympy.functions.elementary.piecewise import Piecewise +from sympy.functions.elementary.trigonometric import tan, sin, cos +from sympy.functions.special.delta_functions import (Heaviside, + DiracDelta) +from sympy.functions.special.error_functions import Si +from sympy.functions.special.gamma_functions import gamma +from sympy.integrals.integrals import integrate, Integral +from sympy.physics.secondquant import FockState +from sympy.polys.partfrac import apart +from sympy.polys.polytools import factor, cancel, Poly +from sympy.polys.rationaltools import together +from sympy.series.order import O +from sympy.sets.sets import FiniteSet +from sympy.simplify.combsimp import combsimp +from sympy.simplify.gammasimp import gammasimp +from sympy.simplify.powsimp import powsimp +from sympy.simplify.radsimp import collect, radsimp +from sympy.simplify.ratsimp import ratsimp +from sympy.simplify.simplify import simplify, nsimplify +from sympy.simplify.trigsimp import trigsimp +from sympy.tensor.indexed import Indexed +from sympy.physics.units import meter + +from sympy.testing.pytest import raises, XFAIL + +from sympy.abc import a, b, c, n, t, u, x, y, z + + +f, g, h = symbols('f,g,h', cls=Function) + + +class DummyNumber: + """ + Minimal implementation of a number that works with SymPy. + + If one has a Number class (e.g. Sage Integer, or some other custom class) + that one wants to work well with SymPy, one has to implement at least the + methods of this class DummyNumber, resp. its subclasses I5 and F1_1. + + Basically, one just needs to implement either __int__() or __float__() and + then one needs to make sure that the class works with Python integers and + with itself. + """ + + def __radd__(self, a): + if isinstance(a, (int, float)): + return a + self.number + return NotImplemented + + def __add__(self, a): + if isinstance(a, (int, float, DummyNumber)): + return self.number + a + return NotImplemented + + def __rsub__(self, a): + if isinstance(a, (int, float)): + return a - self.number + return NotImplemented + + def __sub__(self, a): + if isinstance(a, (int, float, DummyNumber)): + return self.number - a + return NotImplemented + + def __rmul__(self, a): + if isinstance(a, (int, float)): + return a * self.number + return NotImplemented + + def __mul__(self, a): + if isinstance(a, (int, float, DummyNumber)): + return self.number * a + return NotImplemented + + def __rtruediv__(self, a): + if isinstance(a, (int, float)): + return a / self.number + return NotImplemented + + def __truediv__(self, a): + if isinstance(a, (int, float, DummyNumber)): + return self.number / a + return NotImplemented + + def __rpow__(self, a): + if isinstance(a, (int, float)): + return a ** self.number + return NotImplemented + + def __pow__(self, a): + if isinstance(a, (int, float, DummyNumber)): + return self.number ** a + return NotImplemented + + def __pos__(self): + return self.number + + def __neg__(self): + return - self.number + + +class I5(DummyNumber): + number = 5 + + def __int__(self): + return self.number + + +class F1_1(DummyNumber): + number = 1.1 + + def __float__(self): + return self.number + +i5 = I5() +f1_1 = F1_1() + +# basic SymPy objects +basic_objs = [ + Rational(2), + Float("1.3"), + x, + y, + pow(x, y)*y, +] + +# all supported objects +all_objs = basic_objs + [ + 5, + 5.5, + i5, + f1_1 +] + + +def dotest(s): + for xo in all_objs: + for yo in all_objs: + s(xo, yo) + return True + + +def test_basic(): + def j(a, b): + x = a + x = +a + x = -a + x = a + b + x = a - b + x = a*b + x = a/b + x = a**b + del x + assert dotest(j) + + +def test_ibasic(): + def s(a, b): + x = a + x += b + x = a + x -= b + x = a + x *= b + x = a + x /= b + assert dotest(s) + + +class NonBasic: + '''This class represents an object that knows how to implement binary + operations like +, -, etc with Expr but is not a subclass of Basic itself. + The NonExpr subclass below does subclass Basic but not Expr. + + For both NonBasic and NonExpr it should be possible for them to override + Expr.__add__ etc because Expr.__add__ should be returning NotImplemented + for non Expr classes. Otherwise Expr.__add__ would create meaningless + objects like Add(Integer(1), FiniteSet(2)) and it wouldn't be possible for + other classes to override these operations when interacting with Expr. + ''' + def __add__(self, other): + return SpecialOp('+', self, other) + + def __radd__(self, other): + return SpecialOp('+', other, self) + + def __sub__(self, other): + return SpecialOp('-', self, other) + + def __rsub__(self, other): + return SpecialOp('-', other, self) + + def __mul__(self, other): + return SpecialOp('*', self, other) + + def __rmul__(self, other): + return SpecialOp('*', other, self) + + def __truediv__(self, other): + return SpecialOp('/', self, other) + + def __rtruediv__(self, other): + return SpecialOp('/', other, self) + + def __floordiv__(self, other): + return SpecialOp('//', self, other) + + def __rfloordiv__(self, other): + return SpecialOp('//', other, self) + + def __mod__(self, other): + return SpecialOp('%', self, other) + + def __rmod__(self, other): + return SpecialOp('%', other, self) + + def __divmod__(self, other): + return SpecialOp('divmod', self, other) + + def __rdivmod__(self, other): + return SpecialOp('divmod', other, self) + + def __pow__(self, other): + return SpecialOp('**', self, other) + + def __rpow__(self, other): + return SpecialOp('**', other, self) + + def __lt__(self, other): + return SpecialOp('<', self, other) + + def __gt__(self, other): + return SpecialOp('>', self, other) + + def __le__(self, other): + return SpecialOp('<=', self, other) + + def __ge__(self, other): + return SpecialOp('>=', self, other) + + +class NonExpr(Basic, NonBasic): + '''Like NonBasic above except this is a subclass of Basic but not Expr''' + pass + + +class SpecialOp(): + '''Represents the results of operations with NonBasic and NonExpr''' + def __new__(cls, op, arg1, arg2): + obj = object.__new__(cls) + obj.args = (op, arg1, arg2) + return obj + + +class NonArithmetic(Basic): + '''Represents a Basic subclass that does not support arithmetic operations''' + pass + + +def test_cooperative_operations(): + '''Tests that Expr uses binary operations cooperatively. + + In particular it should be possible for non-Expr classes to override + binary operators like +, - etc when used with Expr instances. This should + work for non-Expr classes whether they are Basic subclasses or not. Also + non-Expr classes that do not define binary operators with Expr should give + TypeError. + ''' + # A bunch of instances of Expr subclasses + exprs = [ + Expr(), + S.Zero, + S.One, + S.Infinity, + S.NegativeInfinity, + S.ComplexInfinity, + S.Half, + Float(0.5), + Integer(2), + Symbol('x'), + Mul(2, Symbol('x')), + Add(2, Symbol('x')), + Pow(2, Symbol('x')), + ] + + for e in exprs: + # Test that these classes can override arithmetic operations in + # combination with various Expr types. + for ne in [NonBasic(), NonExpr()]: + + results = [ + (ne + e, ('+', ne, e)), + (e + ne, ('+', e, ne)), + (ne - e, ('-', ne, e)), + (e - ne, ('-', e, ne)), + (ne * e, ('*', ne, e)), + (e * ne, ('*', e, ne)), + (ne / e, ('/', ne, e)), + (e / ne, ('/', e, ne)), + (ne // e, ('//', ne, e)), + (e // ne, ('//', e, ne)), + (ne % e, ('%', ne, e)), + (e % ne, ('%', e, ne)), + (divmod(ne, e), ('divmod', ne, e)), + (divmod(e, ne), ('divmod', e, ne)), + (ne ** e, ('**', ne, e)), + (e ** ne, ('**', e, ne)), + (e < ne, ('>', ne, e)), + (ne < e, ('<', ne, e)), + (e > ne, ('<', ne, e)), + (ne > e, ('>', ne, e)), + (e <= ne, ('>=', ne, e)), + (ne <= e, ('<=', ne, e)), + (e >= ne, ('<=', ne, e)), + (ne >= e, ('>=', ne, e)), + ] + + for res, args in results: + assert type(res) is SpecialOp and res.args == args + + # These classes do not support binary operators with Expr. Every + # operation should raise in combination with any of the Expr types. + for na in [NonArithmetic(), object()]: + + raises(TypeError, lambda : e + na) + raises(TypeError, lambda : na + e) + raises(TypeError, lambda : e - na) + raises(TypeError, lambda : na - e) + raises(TypeError, lambda : e * na) + raises(TypeError, lambda : na * e) + raises(TypeError, lambda : e / na) + raises(TypeError, lambda : na / e) + raises(TypeError, lambda : e // na) + raises(TypeError, lambda : na // e) + raises(TypeError, lambda : e % na) + raises(TypeError, lambda : na % e) + raises(TypeError, lambda : divmod(e, na)) + raises(TypeError, lambda : divmod(na, e)) + raises(TypeError, lambda : e ** na) + raises(TypeError, lambda : na ** e) + raises(TypeError, lambda : e > na) + raises(TypeError, lambda : na > e) + raises(TypeError, lambda : e < na) + raises(TypeError, lambda : na < e) + raises(TypeError, lambda : e >= na) + raises(TypeError, lambda : na >= e) + raises(TypeError, lambda : e <= na) + raises(TypeError, lambda : na <= e) + + +def test_relational(): + from sympy.core.relational import Lt + assert (pi < 3) is S.false + assert (pi <= 3) is S.false + assert (pi > 3) is S.true + assert (pi >= 3) is S.true + assert (-pi < 3) is S.true + assert (-pi <= 3) is S.true + assert (-pi > 3) is S.false + assert (-pi >= 3) is S.false + r = Symbol('r', real=True) + assert (r - 2 < r - 3) is S.false + assert Lt(x + I, x + I + 2).func == Lt # issue 8288 + + +def test_relational_assumptions(): + m1 = Symbol("m1", nonnegative=False) + m2 = Symbol("m2", positive=False) + m3 = Symbol("m3", nonpositive=False) + m4 = Symbol("m4", negative=False) + assert (m1 < 0) == Lt(m1, 0) + assert (m2 <= 0) == Le(m2, 0) + assert (m3 > 0) == Gt(m3, 0) + assert (m4 >= 0) == Ge(m4, 0) + m1 = Symbol("m1", nonnegative=False, real=True) + m2 = Symbol("m2", positive=False, real=True) + m3 = Symbol("m3", nonpositive=False, real=True) + m4 = Symbol("m4", negative=False, real=True) + assert (m1 < 0) is S.true + assert (m2 <= 0) is S.true + assert (m3 > 0) is S.true + assert (m4 >= 0) is S.true + m1 = Symbol("m1", negative=True) + m2 = Symbol("m2", nonpositive=True) + m3 = Symbol("m3", positive=True) + m4 = Symbol("m4", nonnegative=True) + assert (m1 < 0) is S.true + assert (m2 <= 0) is S.true + assert (m3 > 0) is S.true + assert (m4 >= 0) is S.true + m1 = Symbol("m1", negative=False, real=True) + m2 = Symbol("m2", nonpositive=False, real=True) + m3 = Symbol("m3", positive=False, real=True) + m4 = Symbol("m4", nonnegative=False, real=True) + assert (m1 < 0) is S.false + assert (m2 <= 0) is S.false + assert (m3 > 0) is S.false + assert (m4 >= 0) is S.false + + +# See https://github.com/sympy/sympy/issues/17708 +#def test_relational_noncommutative(): +# from sympy import Lt, Gt, Le, Ge +# A, B = symbols('A,B', commutative=False) +# assert (A < B) == Lt(A, B) +# assert (A <= B) == Le(A, B) +# assert (A > B) == Gt(A, B) +# assert (A >= B) == Ge(A, B) + + +def test_basic_nostr(): + for obj in basic_objs: + raises(TypeError, lambda: obj + '1') + raises(TypeError, lambda: obj - '1') + if obj == 2: + assert obj * '1' == '11' + else: + raises(TypeError, lambda: obj * '1') + raises(TypeError, lambda: obj / '1') + raises(TypeError, lambda: obj ** '1') + + +def test_series_expansion_for_uniform_order(): + assert (1/x + y + x).series(x, 0, 0) == 1/x + O(1, x) + assert (1/x + y + x).series(x, 0, 1) == 1/x + y + O(x) + assert (1/x + 1 + x).series(x, 0, 0) == 1/x + O(1, x) + assert (1/x + 1 + x).series(x, 0, 1) == 1/x + 1 + O(x) + assert (1/x + x).series(x, 0, 0) == 1/x + O(1, x) + assert (1/x + y + y*x + x).series(x, 0, 0) == 1/x + O(1, x) + assert (1/x + y + y*x + x).series(x, 0, 1) == 1/x + y + O(x) + + +def test_leadterm(): + assert (3 + 2*x**(log(3)/log(2) - 1)).leadterm(x) == (3, 0) + + assert (1/x**2 + 1 + x + x**2).leadterm(x)[1] == -2 + assert (1/x + 1 + x + x**2).leadterm(x)[1] == -1 + assert (x**2 + 1/x).leadterm(x)[1] == -1 + assert (1 + x**2).leadterm(x)[1] == 0 + assert (x + 1).leadterm(x)[1] == 0 + assert (x + x**2).leadterm(x)[1] == 1 + assert (x**2).leadterm(x)[1] == 2 + + +def test_as_leading_term(): + assert (3 + 2*x**(log(3)/log(2) - 1)).as_leading_term(x) == 3 + assert (1/x**2 + 1 + x + x**2).as_leading_term(x) == 1/x**2 + assert (1/x + 1 + x + x**2).as_leading_term(x) == 1/x + assert (x**2 + 1/x).as_leading_term(x) == 1/x + assert (1 + x**2).as_leading_term(x) == 1 + assert (x + 1).as_leading_term(x) == 1 + assert (x + x**2).as_leading_term(x) == x + assert (x**2).as_leading_term(x) == x**2 + assert (x + oo).as_leading_term(x) is oo + + raises(ValueError, lambda: (x + 1).as_leading_term(1)) + + # https://github.com/sympy/sympy/issues/21177 + e = -3*x + (x + Rational(3, 2) - sqrt(3)*S.ImaginaryUnit/2)**2\ + - Rational(3, 2) + 3*sqrt(3)*S.ImaginaryUnit/2 + assert e.as_leading_term(x) == \ + (12*sqrt(3)*x - 12*S.ImaginaryUnit*x)/(4*sqrt(3) + 12*S.ImaginaryUnit) + + # https://github.com/sympy/sympy/issues/21245 + e = 1 - x - x**2 + d = (1 + sqrt(5))/2 + assert e.subs(x, y + 1/d).as_leading_term(y) == \ + (-576*sqrt(5)*y - 1280*y)/(256*sqrt(5) + 576) + + +def test_leadterm2(): + assert (x*cos(1)*cos(1 + sin(1)) + sin(1 + sin(1))).leadterm(x) == \ + (sin(1 + sin(1)), 0) + + +def test_leadterm3(): + assert (y + z + x).leadterm(x) == (y + z, 0) + + +def test_as_leading_term2(): + assert (x*cos(1)*cos(1 + sin(1)) + sin(1 + sin(1))).as_leading_term(x) == \ + sin(1 + sin(1)) + + +def test_as_leading_term3(): + assert (2 + pi + x).as_leading_term(x) == 2 + pi + assert (2*x + pi*x + x**2).as_leading_term(x) == 2*x + pi*x + + +def test_as_leading_term4(): + # see issue 6843 + n = Symbol('n', integer=True, positive=True) + r = -n**3/(2*n**2 + 4*n + 2) - n**2/(n**2 + 2*n + 1) + \ + n**2/(n + 1) - n/(2*n**2 + 4*n + 2) + n/(n*x + x) + 2*n/(n + 1) - \ + 1 + 1/(n*x + x) + 1/(n + 1) - 1/x + assert r.as_leading_term(x).cancel() == n/2 + + +def test_as_leading_term_stub(): + class foo(Function): + pass + assert foo(1/x).as_leading_term(x) == foo(1/x) + assert foo(1).as_leading_term(x) == foo(1) + raises(NotImplementedError, lambda: foo(x).as_leading_term(x)) + + +def test_as_leading_term_deriv_integral(): + # related to issue 11313 + assert Derivative(x ** 3, x).as_leading_term(x) == 3*x**2 + assert Derivative(x ** 3, y).as_leading_term(x) == 0 + + assert Integral(x ** 3, x).as_leading_term(x) == x**4/4 + assert Integral(x ** 3, y).as_leading_term(x) == y*x**3 + + assert Derivative(exp(x), x).as_leading_term(x) == 1 + assert Derivative(log(x), x).as_leading_term(x) == (1/x).as_leading_term(x) + + +def test_atoms(): + assert x.atoms() == {x} + assert (1 + x).atoms() == {x, S.One} + + assert (1 + 2*cos(x)).atoms(Symbol) == {x} + assert (1 + 2*cos(x)).atoms(Symbol, Number) == {S.One, S(2), x} + + assert (2*(x**(y**x))).atoms() == {S(2), x, y} + + assert S.Half.atoms() == {S.Half} + assert S.Half.atoms(Symbol) == set() + + assert sin(oo).atoms(oo) == set() + + assert Poly(0, x).atoms() == {S.Zero, x} + assert Poly(1, x).atoms() == {S.One, x} + + assert Poly(x, x).atoms() == {x} + assert Poly(x, x, y).atoms() == {x, y} + assert Poly(x + y, x, y).atoms() == {x, y} + assert Poly(x + y, x, y, z).atoms() == {x, y, z} + assert Poly(x + y*t, x, y, z).atoms() == {t, x, y, z} + + assert (I*pi).atoms(NumberSymbol) == {pi} + assert (I*pi).atoms(NumberSymbol, I) == \ + (I*pi).atoms(I, NumberSymbol) == {pi, I} + + assert exp(exp(x)).atoms(exp) == {exp(exp(x)), exp(x)} + assert (1 + x*(2 + y) + exp(3 + z)).atoms(Add) == \ + {1 + x*(2 + y) + exp(3 + z), 2 + y, 3 + z} + + # issue 6132 + e = (f(x) + sin(x) + 2) + assert e.atoms(AppliedUndef) == \ + {f(x)} + assert e.atoms(AppliedUndef, Function) == \ + {f(x), sin(x)} + assert e.atoms(Function) == \ + {f(x), sin(x)} + assert e.atoms(AppliedUndef, Number) == \ + {f(x), S(2)} + assert e.atoms(Function, Number) == \ + {S(2), sin(x), f(x)} + + +def test_is_polynomial(): + k = Symbol('k', nonnegative=True, integer=True) + + assert Rational(2).is_polynomial(x, y, z) is True + assert (S.Pi).is_polynomial(x, y, z) is True + + assert x.is_polynomial(x) is True + assert x.is_polynomial(y) is True + + assert (x**2).is_polynomial(x) is True + assert (x**2).is_polynomial(y) is True + + assert (x**(-2)).is_polynomial(x) is False + assert (x**(-2)).is_polynomial(y) is True + + assert (2**x).is_polynomial(x) is False + assert (2**x).is_polynomial(y) is True + + assert (x**k).is_polynomial(x) is False + assert (x**k).is_polynomial(k) is False + assert (x**x).is_polynomial(x) is False + assert (k**k).is_polynomial(k) is False + assert (k**x).is_polynomial(k) is False + + assert (x**(-k)).is_polynomial(x) is False + assert ((2*x)**k).is_polynomial(x) is False + + assert (x**2 + 3*x - 8).is_polynomial(x) is True + assert (x**2 + 3*x - 8).is_polynomial(y) is True + + assert (x**2 + 3*x - 8).is_polynomial() is True + + assert sqrt(x).is_polynomial(x) is False + assert (sqrt(x)**3).is_polynomial(x) is False + + assert (x**2 + 3*x*sqrt(y) - 8).is_polynomial(x) is True + assert (x**2 + 3*x*sqrt(y) - 8).is_polynomial(y) is False + + assert ((x**2)*(y**2) + x*(y**2) + y*x + exp(2)).is_polynomial() is True + assert ((x**2)*(y**2) + x*(y**2) + y*x + exp(x)).is_polynomial() is False + + assert ( + (x**2)*(y**2) + x*(y**2) + y*x + exp(2)).is_polynomial(x, y) is True + assert ( + (x**2)*(y**2) + x*(y**2) + y*x + exp(x)).is_polynomial(x, y) is False + + assert (1/f(x) + 1).is_polynomial(f(x)) is False + + +def test_is_rational_function(): + assert Integer(1).is_rational_function() is True + assert Integer(1).is_rational_function(x) is True + + assert Rational(17, 54).is_rational_function() is True + assert Rational(17, 54).is_rational_function(x) is True + + assert (12/x).is_rational_function() is True + assert (12/x).is_rational_function(x) is True + + assert (x/y).is_rational_function() is True + assert (x/y).is_rational_function(x) is True + assert (x/y).is_rational_function(x, y) is True + + assert (x**2 + 1/x/y).is_rational_function() is True + assert (x**2 + 1/x/y).is_rational_function(x) is True + assert (x**2 + 1/x/y).is_rational_function(x, y) is True + + assert (sin(y)/x).is_rational_function() is False + assert (sin(y)/x).is_rational_function(y) is False + assert (sin(y)/x).is_rational_function(x) is True + assert (sin(y)/x).is_rational_function(x, y) is False + + for i in _illegal: + assert not i.is_rational_function() + for d in (1, x): + assert not (i/d).is_rational_function() + + +def test_is_meromorphic(): + f = a/x**2 + b + x + c*x**2 + assert f.is_meromorphic(x, 0) is True + assert f.is_meromorphic(x, 1) is True + assert f.is_meromorphic(x, zoo) is True + + g = 3 + 2*x**(log(3)/log(2) - 1) + assert g.is_meromorphic(x, 0) is False + assert g.is_meromorphic(x, 1) is True + assert g.is_meromorphic(x, zoo) is False + + n = Symbol('n', integer=True) + e = sin(1/x)**n*x + assert e.is_meromorphic(x, 0) is False + assert e.is_meromorphic(x, 1) is True + assert e.is_meromorphic(x, zoo) is False + + e = log(x)**pi + assert e.is_meromorphic(x, 0) is False + assert e.is_meromorphic(x, 1) is False + assert e.is_meromorphic(x, 2) is True + assert e.is_meromorphic(x, zoo) is False + + assert (log(x)**a).is_meromorphic(x, 0) is False + assert (log(x)**a).is_meromorphic(x, 1) is False + assert (a**log(x)).is_meromorphic(x, 0) is None + assert (3**log(x)).is_meromorphic(x, 0) is False + assert (3**log(x)).is_meromorphic(x, 1) is True + +def test_is_algebraic_expr(): + assert sqrt(3).is_algebraic_expr(x) is True + assert sqrt(3).is_algebraic_expr() is True + + eq = ((1 + x**2)/(1 - y**2))**(S.One/3) + assert eq.is_algebraic_expr(x) is True + assert eq.is_algebraic_expr(y) is True + + assert (sqrt(x) + y**(S(2)/3)).is_algebraic_expr(x) is True + assert (sqrt(x) + y**(S(2)/3)).is_algebraic_expr(y) is True + assert (sqrt(x) + y**(S(2)/3)).is_algebraic_expr() is True + + assert (cos(y)/sqrt(x)).is_algebraic_expr() is False + assert (cos(y)/sqrt(x)).is_algebraic_expr(x) is True + assert (cos(y)/sqrt(x)).is_algebraic_expr(y) is False + assert (cos(y)/sqrt(x)).is_algebraic_expr(x, y) is False + + +def test_SAGE1(): + #see https://github.com/sympy/sympy/issues/3346 + class MyInt: + def _sympy_(self): + return Integer(5) + m = MyInt() + e = Rational(2)*m + assert e == 10 + + raises(TypeError, lambda: Rational(2)*MyInt) + + +def test_SAGE2(): + class MyInt: + def __int__(self): + return 5 + assert sympify(MyInt()) == 5 + e = Rational(2)*MyInt() + assert e == 10 + + raises(TypeError, lambda: Rational(2)*MyInt) + + +def test_SAGE3(): + class MySymbol: + def __rmul__(self, other): + return ('mys', other, self) + + o = MySymbol() + e = x*o + + assert e == ('mys', x, o) + + +def test_len(): + e = x*y + assert len(e.args) == 2 + e = x + y + z + assert len(e.args) == 3 + + +def test_doit(): + a = Integral(x**2, x) + + assert isinstance(a.doit(), Integral) is False + + assert isinstance(a.doit(integrals=True), Integral) is False + assert isinstance(a.doit(integrals=False), Integral) is True + + assert (2*Integral(x, x)).doit() == x**2 + + +def test_attribute_error(): + raises(AttributeError, lambda: x.cos()) + raises(AttributeError, lambda: x.sin()) + raises(AttributeError, lambda: x.exp()) + + +def test_args(): + assert (x*y).args in ((x, y), (y, x)) + assert (x + y).args in ((x, y), (y, x)) + assert (x*y + 1).args in ((x*y, 1), (1, x*y)) + assert sin(x*y).args == (x*y,) + assert sin(x*y).args[0] == x*y + assert (x**y).args == (x, y) + assert (x**y).args[0] == x + assert (x**y).args[1] == y + + +def test_noncommutative_expand_issue_3757(): + A, B, C = symbols('A,B,C', commutative=False) + assert A*B - B*A != 0 + assert (A*(A + B)*B).expand() == A**2*B + A*B**2 + assert (A*(A + B + C)*B).expand() == A**2*B + A*B**2 + A*C*B + + +def test_as_numer_denom(): + a, b, c = symbols('a, b, c') + + assert nan.as_numer_denom() == (nan, 1) + assert oo.as_numer_denom() == (oo, 1) + assert (-oo).as_numer_denom() == (-oo, 1) + assert zoo.as_numer_denom() == (zoo, 1) + assert (-zoo).as_numer_denom() == (zoo, 1) + + assert x.as_numer_denom() == (x, 1) + assert (1/x).as_numer_denom() == (1, x) + assert (x/y).as_numer_denom() == (x, y) + assert (x/2).as_numer_denom() == (x, 2) + assert (x*y/z).as_numer_denom() == (x*y, z) + assert (x/(y*z)).as_numer_denom() == (x, y*z) + assert S.Half.as_numer_denom() == (1, 2) + assert (1/y**2).as_numer_denom() == (1, y**2) + assert (x/y**2).as_numer_denom() == (x, y**2) + assert ((x**2 + 1)/y).as_numer_denom() == (x**2 + 1, y) + assert (x*(y + 1)/y**7).as_numer_denom() == (x*(y + 1), y**7) + assert (x**-2).as_numer_denom() == (1, x**2) + assert (a/x + b/2/x + c/3/x).as_numer_denom() == \ + (6*a + 3*b + 2*c, 6*x) + assert (a/x + b/2/x + c/3/y).as_numer_denom() == \ + (2*c*x + y*(6*a + 3*b), 6*x*y) + assert (a/x + b/2/x + c/.5/x).as_numer_denom() == \ + (2*a + b + 4.0*c, 2*x) + # this should take no more than a few seconds + assert int(log(Add(*[Dummy()/i/x for i in range(1, 705)] + ).as_numer_denom()[1]/x).n(4)) == 705 + for i in [S.Infinity, S.NegativeInfinity, S.ComplexInfinity]: + assert (i + x/3).as_numer_denom() == \ + (x + i, 3) + assert (S.Infinity + x/3 + y/4).as_numer_denom() == \ + (4*x + 3*y + S.Infinity, 12) + assert (oo*x + zoo*y).as_numer_denom() == \ + (zoo*y + oo*x, 1) + + A, B, C = symbols('A,B,C', commutative=False) + + assert (A*B*C**-1).as_numer_denom() == (A*B*C**-1, 1) + assert (A*B*C**-1/x).as_numer_denom() == (A*B*C**-1, x) + assert (C**-1*A*B).as_numer_denom() == (C**-1*A*B, 1) + assert (C**-1*A*B/x).as_numer_denom() == (C**-1*A*B, x) + assert ((A*B*C)**-1).as_numer_denom() == ((A*B*C)**-1, 1) + assert ((A*B*C)**-1/x).as_numer_denom() == ((A*B*C)**-1, x) + + # the following morphs from Add to Mul during processing + assert Add(0, (x + y)/z/-2, evaluate=False).as_numer_denom( + ) == (-x - y, 2*z) + + +def test_trunc(): + import math + x, y = symbols('x y') + assert math.trunc(2) == 2 + assert math.trunc(4.57) == 4 + assert math.trunc(-5.79) == -5 + assert math.trunc(pi) == 3 + assert math.trunc(log(7)) == 1 + assert math.trunc(exp(5)) == 148 + assert math.trunc(cos(pi)) == -1 + assert math.trunc(sin(5)) == 0 + + raises(TypeError, lambda: math.trunc(x)) + raises(TypeError, lambda: math.trunc(x + y**2)) + raises(TypeError, lambda: math.trunc(oo)) + + +def test_as_independent(): + assert S.Zero.as_independent(x, as_Add=True) == (0, 0) + assert S.Zero.as_independent(x, as_Add=False) == (0, 0) + assert (2*x*sin(x) + y + x).as_independent(x) == (y, x + 2*x*sin(x)) + assert (2*x*sin(x) + y + x).as_independent(y) == (x + 2*x*sin(x), y) + + assert (2*x*sin(x) + y + x).as_independent(x, y) == (0, y + x + 2*x*sin(x)) + + assert (x*sin(x)*cos(y)).as_independent(x) == (cos(y), x*sin(x)) + assert (x*sin(x)*cos(y)).as_independent(y) == (x*sin(x), cos(y)) + + assert (x*sin(x)*cos(y)).as_independent(x, y) == (1, x*sin(x)*cos(y)) + + assert (sin(x)).as_independent(x) == (1, sin(x)) + assert (sin(x)).as_independent(y) == (sin(x), 1) + + assert (2*sin(x)).as_independent(x) == (2, sin(x)) + assert (2*sin(x)).as_independent(y) == (2*sin(x), 1) + + # issue 4903 = 1766b + n1, n2, n3 = symbols('n1 n2 n3', commutative=False) + assert (n1 + n1*n2).as_independent(n2) == (n1, n1*n2) + assert (n2*n1 + n1*n2).as_independent(n2) == (0, n1*n2 + n2*n1) + assert (n1*n2*n1).as_independent(n2) == (n1, n2*n1) + assert (n1*n2*n1).as_independent(n1) == (1, n1*n2*n1) + + assert (3*x).as_independent(x, as_Add=True) == (0, 3*x) + assert (3*x).as_independent(x, as_Add=False) == (3, x) + assert (3 + x).as_independent(x, as_Add=True) == (3, x) + assert (3 + x).as_independent(x, as_Add=False) == (1, 3 + x) + + # issue 5479 + assert (3*x).as_independent(Symbol) == (3, x) + + # issue 5648 + assert (n1*x*y).as_independent(x) == (n1*y, x) + assert ((x + n1)*(x - y)).as_independent(x) == (1, (x + n1)*(x - y)) + assert ((x + n1)*(x - y)).as_independent(y) == (x + n1, x - y) + assert (DiracDelta(x - n1)*DiracDelta(x - y)).as_independent(x) \ + == (1, DiracDelta(x - n1)*DiracDelta(x - y)) + assert (x*y*n1*n2*n3).as_independent(n2) == (x*y*n1, n2*n3) + assert (x*y*n1*n2*n3).as_independent(n1) == (x*y, n1*n2*n3) + assert (x*y*n1*n2*n3).as_independent(n3) == (x*y*n1*n2, n3) + assert (DiracDelta(x - n1)*DiracDelta(y - n1)*DiracDelta(x - n2)).as_independent(y) == \ + (DiracDelta(x - n1)*DiracDelta(x - n2), DiracDelta(y - n1)) + + # issue 5784 + assert (x + Integral(x, (x, 1, 2))).as_independent(x, strict=True) == \ + (Integral(x, (x, 1, 2)), x) + + eq = Add(x, -x, 2, -3, evaluate=False) + assert eq.as_independent(x) == (-1, Add(x, -x, evaluate=False)) + eq = Mul(x, 1/x, 2, -3, evaluate=False) + assert eq.as_independent(x) == (-6, Mul(x, 1/x, evaluate=False)) + + assert (x*y).as_independent(z, as_Add=True) == (x*y, 0) + +@XFAIL +def test_call_2(): + # TODO UndefinedFunction does not subclass Expr + assert (2*f)(x) == 2*f(x) + + +def test_replace(): + e = log(sin(x)) + tan(sin(x**2)) + + assert e.replace(sin, cos) == log(cos(x)) + tan(cos(x**2)) + assert e.replace( + sin, lambda a: sin(2*a)) == log(sin(2*x)) + tan(sin(2*x**2)) + + a = Wild('a') + b = Wild('b') + + assert e.replace(sin(a), cos(a)) == log(cos(x)) + tan(cos(x**2)) + assert e.replace( + sin(a), lambda a: sin(2*a)) == log(sin(2*x)) + tan(sin(2*x**2)) + # test exact + assert (2*x).replace(a*x + b, b - a, exact=True) == 2*x + assert (2*x).replace(a*x + b, b - a) == 2*x + assert (2*x).replace(a*x + b, b - a, exact=False) == 2/x + assert (2*x).replace(a*x + b, lambda a, b: b - a, exact=True) == 2*x + assert (2*x).replace(a*x + b, lambda a, b: b - a) == 2*x + assert (2*x).replace(a*x + b, lambda a, b: b - a, exact=False) == 2/x + + g = 2*sin(x**3) + + assert g.replace( + lambda expr: expr.is_Number, lambda expr: expr**2) == 4*sin(x**9) + + assert cos(x).replace(cos, sin, map=True) == (sin(x), {cos(x): sin(x)}) + assert sin(x).replace(cos, sin) == sin(x) + + cond, func = lambda x: x.is_Mul, lambda x: 2*x + assert (x*y).replace(cond, func, map=True) == (2*x*y, {x*y: 2*x*y}) + assert (x*(1 + x*y)).replace(cond, func, map=True) == \ + (2*x*(2*x*y + 1), {x*(2*x*y + 1): 2*x*(2*x*y + 1), x*y: 2*x*y}) + assert (y*sin(x)).replace(sin, lambda expr: sin(expr)/y, map=True) == \ + (sin(x), {sin(x): sin(x)/y}) + # if not simultaneous then y*sin(x) -> y*sin(x)/y = sin(x) -> sin(x)/y + assert (y*sin(x)).replace(sin, lambda expr: sin(expr)/y, + simultaneous=False) == sin(x)/y + assert (x**2 + O(x**3)).replace(Pow, lambda b, e: b**e/e + ) == x**2/2 + O(x**3) + assert (x**2 + O(x**3)).replace(Pow, lambda b, e: b**e/e, + simultaneous=False) == x**2/2 + O(x**3) + assert (x*(x*y + 3)).replace(lambda x: x.is_Mul, lambda x: 2 + x) == \ + x*(x*y + 5) + 2 + e = (x*y + 1)*(2*x*y + 1) + 1 + assert e.replace(cond, func, map=True) == ( + 2*((2*x*y + 1)*(4*x*y + 1)) + 1, + {2*x*y: 4*x*y, x*y: 2*x*y, (2*x*y + 1)*(4*x*y + 1): + 2*((2*x*y + 1)*(4*x*y + 1))}) + assert x.replace(x, y) == y + assert (x + 1).replace(1, 2) == x + 2 + + # https://groups.google.com/forum/#!topic/sympy/8wCgeC95tz0 + n1, n2, n3 = symbols('n1:4', commutative=False) + assert (n1*f(n2)).replace(f, lambda x: x) == n1*n2 + assert (n3*f(n2)).replace(f, lambda x: x) == n3*n2 + + # issue 16725 + assert S.Zero.replace(Wild('x'), 1) == 1 + # let the user override the default decision of False + assert S.Zero.replace(Wild('x'), 1, exact=True) == 0 + + +def test_find(): + expr = (x + y + 2 + sin(3*x)) + + assert expr.find(lambda u: u.is_Integer) == {S(2), S(3)} + assert expr.find(lambda u: u.is_Symbol) == {x, y} + + assert expr.find(lambda u: u.is_Integer, group=True) == {S(2): 1, S(3): 1} + assert expr.find(lambda u: u.is_Symbol, group=True) == {x: 2, y: 1} + + assert expr.find(Integer) == {S(2), S(3)} + assert expr.find(Symbol) == {x, y} + + assert expr.find(Integer, group=True) == {S(2): 1, S(3): 1} + assert expr.find(Symbol, group=True) == {x: 2, y: 1} + + a = Wild('a') + + expr = sin(sin(x)) + sin(x) + cos(x) + x + + assert expr.find(lambda u: type(u) is sin) == {sin(x), sin(sin(x))} + assert expr.find( + lambda u: type(u) is sin, group=True) == {sin(x): 2, sin(sin(x)): 1} + + assert expr.find(sin(a)) == {sin(x), sin(sin(x))} + assert expr.find(sin(a), group=True) == {sin(x): 2, sin(sin(x)): 1} + + assert expr.find(sin) == {sin(x), sin(sin(x))} + assert expr.find(sin, group=True) == {sin(x): 2, sin(sin(x)): 1} + + +def test_count(): + expr = (x + y + 2 + sin(3*x)) + + assert expr.count(lambda u: u.is_Integer) == 2 + assert expr.count(lambda u: u.is_Symbol) == 3 + + assert expr.count(Integer) == 2 + assert expr.count(Symbol) == 3 + assert expr.count(2) == 1 + + a = Wild('a') + + assert expr.count(sin) == 1 + assert expr.count(sin(a)) == 1 + assert expr.count(lambda u: type(u) is sin) == 1 + + assert f(x).count(f(x)) == 1 + assert f(x).diff(x).count(f(x)) == 1 + assert f(x).diff(x).count(x) == 2 + + +def test_has_basics(): + p = Wild('p') + + assert sin(x).has(x) + assert sin(x).has(sin) + assert not sin(x).has(y) + assert not sin(x).has(cos) + assert f(x).has(x) + assert f(x).has(f) + assert not f(x).has(y) + assert not f(x).has(g) + + assert f(x).diff(x).has(x) + assert f(x).diff(x).has(f) + assert f(x).diff(x).has(Derivative) + assert not f(x).diff(x).has(y) + assert not f(x).diff(x).has(g) + assert not f(x).diff(x).has(sin) + + assert (x**2).has(Symbol) + assert not (x**2).has(Wild) + assert (2*p).has(Wild) + + assert not x.has() + + # see issue at https://github.com/sympy/sympy/issues/5190 + assert not S(1).has(Wild) + assert not x.has(Wild) + + +def test_has_multiple(): + f = x**2*y + sin(2**t + log(z)) + + assert f.has(x) + assert f.has(y) + assert f.has(z) + assert f.has(t) + + assert not f.has(u) + + assert f.has(x, y, z, t) + assert f.has(x, y, z, t, u) + + i = Integer(4400) + + assert not i.has(x) + + assert (i*x**i).has(x) + assert not (i*y**i).has(x) + assert (i*y**i).has(x, y) + assert not (i*y**i).has(x, z) + + +def test_has_piecewise(): + f = (x*y + 3/y)**(3 + 2) + p = Piecewise((g(x), x < -1), (1, x <= 1), (f, True)) + + assert p.has(x) + assert p.has(y) + assert not p.has(z) + assert p.has(1) + assert p.has(3) + assert not p.has(4) + assert p.has(f) + assert p.has(g) + assert not p.has(h) + + +def test_has_iterative(): + A, B, C = symbols('A,B,C', commutative=False) + f = x*gamma(x)*sin(x)*exp(x*y)*A*B*C*cos(x*A*B) + + assert f.has(x) + assert f.has(x*y) + assert f.has(x*sin(x)) + assert not f.has(x*sin(y)) + assert f.has(x*A) + assert f.has(x*A*B) + assert not f.has(x*A*C) + assert f.has(x*A*B*C) + assert not f.has(x*A*C*B) + assert f.has(x*sin(x)*A*B*C) + assert not f.has(x*sin(x)*A*C*B) + assert not f.has(x*sin(y)*A*B*C) + assert f.has(x*gamma(x)) + assert not f.has(x + sin(x)) + + assert (x & y & z).has(x & z) + + +def test_has_integrals(): + f = Integral(x**2 + sin(x*y*z), (x, 0, x + y + z)) + + assert f.has(x + y) + assert f.has(x + z) + assert f.has(y + z) + + assert f.has(x*y) + assert f.has(x*z) + assert f.has(y*z) + + assert not f.has(2*x + y) + assert not f.has(2*x*y) + + +def test_has_tuple(): + assert Tuple(x, y).has(x) + assert not Tuple(x, y).has(z) + assert Tuple(f(x), g(x)).has(x) + assert not Tuple(f(x), g(x)).has(y) + assert Tuple(f(x), g(x)).has(f) + assert Tuple(f(x), g(x)).has(f(x)) + # XXX to be deprecated + #assert not Tuple(f, g).has(x) + #assert Tuple(f, g).has(f) + #assert not Tuple(f, g).has(h) + assert Tuple(True).has(True) + assert Tuple(True).has(S.true) + assert not Tuple(True).has(1) + + +def test_has_units(): + from sympy.physics.units import m, s + + assert (x*m/s).has(x) + assert (x*m/s).has(y, z) is False + + +def test_has_polys(): + poly = Poly(x**2 + x*y*sin(z), x, y, t) + + assert poly.has(x) + assert poly.has(x, y, z) + assert poly.has(x, y, z, t) + + +def test_has_physics(): + assert FockState((x, y)).has(x) + + +def test_as_poly_as_expr(): + f = x**2 + 2*x*y + + assert f.as_poly().as_expr() == f + assert f.as_poly(x, y).as_expr() == f + + assert (f + sin(x)).as_poly(x, y) is None + + p = Poly(f, x, y) + + assert p.as_poly() == p + + # https://github.com/sympy/sympy/issues/20610 + assert S(2).as_poly() is None + assert sqrt(2).as_poly(extension=True) is None + + raises(AttributeError, lambda: Tuple(x, x).as_poly(x)) + raises(AttributeError, lambda: Tuple(x ** 2, x, y).as_poly(x)) + + +def test_nonzero(): + assert bool(S.Zero) is False + assert bool(S.One) is True + assert bool(x) is True + assert bool(x + y) is True + assert bool(x - x) is False + assert bool(x*y) is True + assert bool(x*1) is True + assert bool(x*0) is False + + +def test_is_number(): + assert Float(3.14).is_number is True + assert Integer(737).is_number is True + assert Rational(3, 2).is_number is True + assert Rational(8).is_number is True + assert x.is_number is False + assert (2*x).is_number is False + assert (x + y).is_number is False + assert log(2).is_number is True + assert log(x).is_number is False + assert (2 + log(2)).is_number is True + assert (8 + log(2)).is_number is True + assert (2 + log(x)).is_number is False + assert (8 + log(2) + x).is_number is False + assert (1 + x**2/x - x).is_number is True + assert Tuple(Integer(1)).is_number is False + assert Add(2, x).is_number is False + assert Mul(3, 4).is_number is True + assert Pow(log(2), 2).is_number is True + assert oo.is_number is True + g = WildFunction('g') + assert g.is_number is False + assert (2*g).is_number is False + assert (x**2).subs(x, 3).is_number is True + + # test extensibility of .is_number + # on subinstances of Basic + class A(Basic): + pass + a = A() + assert a.is_number is False + + +def test_as_coeff_add(): + assert S(2).as_coeff_add() == (2, ()) + assert S(3.0).as_coeff_add() == (0, (S(3.0),)) + assert S(-3.0).as_coeff_add() == (0, (S(-3.0),)) + assert x.as_coeff_add() == (0, (x,)) + assert (x - 1).as_coeff_add() == (-1, (x,)) + assert (x + 1).as_coeff_add() == (1, (x,)) + assert (x + 2).as_coeff_add() == (2, (x,)) + assert (x + y).as_coeff_add(y) == (x, (y,)) + assert (3*x).as_coeff_add(y) == (3*x, ()) + # don't do expansion + e = (x + y)**2 + assert e.as_coeff_add(y) == (0, (e,)) + + +def test_as_coeff_mul(): + assert S(2).as_coeff_mul() == (2, ()) + assert S(3.0).as_coeff_mul() == (1, (S(3.0),)) + assert S(-3.0).as_coeff_mul() == (-1, (S(3.0),)) + assert S(-3.0).as_coeff_mul(rational=False) == (-S(3.0), ()) + assert x.as_coeff_mul() == (1, (x,)) + assert (-x).as_coeff_mul() == (-1, (x,)) + assert (2*x).as_coeff_mul() == (2, (x,)) + assert (x*y).as_coeff_mul(y) == (x, (y,)) + assert (3 + x).as_coeff_mul() == (1, (3 + x,)) + assert (3 + x).as_coeff_mul(y) == (3 + x, ()) + # don't do expansion + e = exp(x + y) + assert e.as_coeff_mul(y) == (1, (e,)) + e = 2**(x + y) + assert e.as_coeff_mul(y) == (1, (e,)) + assert (1.1*x).as_coeff_mul(rational=False) == (1.1, (x,)) + assert (1.1*x).as_coeff_mul() == (1, (1.1, x)) + assert (-oo*x).as_coeff_mul(rational=True) == (-1, (oo, x)) + + +def test_as_coeff_exponent(): + assert (3*x**4).as_coeff_exponent(x) == (3, 4) + assert (2*x**3).as_coeff_exponent(x) == (2, 3) + assert (4*x**2).as_coeff_exponent(x) == (4, 2) + assert (6*x**1).as_coeff_exponent(x) == (6, 1) + assert (3*x**0).as_coeff_exponent(x) == (3, 0) + assert (2*x**0).as_coeff_exponent(x) == (2, 0) + assert (1*x**0).as_coeff_exponent(x) == (1, 0) + assert (0*x**0).as_coeff_exponent(x) == (0, 0) + assert (-1*x**0).as_coeff_exponent(x) == (-1, 0) + assert (-2*x**0).as_coeff_exponent(x) == (-2, 0) + assert (2*x**3 + pi*x**3).as_coeff_exponent(x) == (2 + pi, 3) + assert (x*log(2)/(2*x + pi*x)).as_coeff_exponent(x) == \ + (log(2)/(2 + pi), 0) + # issue 4784 + D = Derivative + fx = D(f(x), x) + assert fx.as_coeff_exponent(f(x)) == (fx, 0) + + +def test_extractions(): + for base in (2, S.Exp1): + assert Pow(base**x, 3, evaluate=False + ).extract_multiplicatively(base**x) == base**(2*x) + assert (base**(5*x)).extract_multiplicatively( + base**(3*x)) == base**(2*x) + assert ((x*y)**3).extract_multiplicatively(x**2 * y) == x*y**2 + assert ((x*y)**3).extract_multiplicatively(x**4 * y) is None + assert (2*x).extract_multiplicatively(2) == x + assert (2*x).extract_multiplicatively(3) is None + assert (2*x).extract_multiplicatively(-1) is None + assert (S.Half*x).extract_multiplicatively(3) == x/6 + assert (sqrt(x)).extract_multiplicatively(x) is None + assert (sqrt(x)).extract_multiplicatively(1/x) is None + assert x.extract_multiplicatively(-x) is None + assert (-2 - 4*I).extract_multiplicatively(-2) == 1 + 2*I + assert (-2 - 4*I).extract_multiplicatively(3) is None + assert (-2*x - 4*y - 8).extract_multiplicatively(-2) == x + 2*y + 4 + assert (-2*x*y - 4*x**2*y).extract_multiplicatively(-2*y) == 2*x**2 + x + assert (2*x*y + 4*x**2*y).extract_multiplicatively(2*y) == 2*x**2 + x + assert (-4*y**2*x).extract_multiplicatively(-3*y) is None + assert (2*x).extract_multiplicatively(1) == 2*x + assert (-oo).extract_multiplicatively(5) is -oo + assert (oo).extract_multiplicatively(5) is oo + + assert ((x*y)**3).extract_additively(1) is None + assert (x + 1).extract_additively(x) == 1 + assert (x + 1).extract_additively(2*x) is None + assert (x + 1).extract_additively(-x) is None + assert (-x + 1).extract_additively(2*x) is None + assert (2*x + 3).extract_additively(x) == x + 3 + assert (2*x + 3).extract_additively(2) == 2*x + 1 + assert (2*x + 3).extract_additively(3) == 2*x + assert (2*x + 3).extract_additively(-2) is None + assert (2*x + 3).extract_additively(3*x) is None + assert (2*x + 3).extract_additively(2*x) == 3 + assert x.extract_additively(0) == x + assert S(2).extract_additively(x) is None + assert S(2.).extract_additively(2.) is S.Zero + assert S(2.).extract_additively(2) is S.Zero + assert S(2*x + 3).extract_additively(x + 1) == x + 2 + assert S(2*x + 3).extract_additively(y + 1) is None + assert S(2*x - 3).extract_additively(x + 1) is None + assert S(2*x - 3).extract_additively(y + z) is None + assert ((a + 1)*x*4 + y).extract_additively(x).expand() == \ + 4*a*x + 3*x + y + assert ((a + 1)*x*4 + 3*y).extract_additively(x + 2*y).expand() == \ + 4*a*x + 3*x + y + assert (y*(x + 1)).extract_additively(x + 1) is None + assert ((y + 1)*(x + 1) + 3).extract_additively(x + 1) == \ + y*(x + 1) + 3 + assert ((x + y)*(x + 1) + x + y + 3).extract_additively(x + y) == \ + x*(x + y) + 3 + assert (x + y + 2*((x + y)*(x + 1)) + 3).extract_additively((x + y)*(x + 1)) == \ + x + y + (x + 1)*(x + y) + 3 + assert ((y + 1)*(x + 2*y + 1) + 3).extract_additively(y + 1) == \ + (x + 2*y)*(y + 1) + 3 + assert (-x - x*I).extract_additively(-x) == -I*x + # extraction does not leave artificats, now + assert (4*x*(y + 1) + y).extract_additively(x) == x*(4*y + 3) + y + + n = Symbol("n", integer=True) + assert (Integer(-3)).could_extract_minus_sign() is True + assert (-n*x + x).could_extract_minus_sign() != \ + (n*x - x).could_extract_minus_sign() + assert (x - y).could_extract_minus_sign() != \ + (-x + y).could_extract_minus_sign() + assert (1 - x - y).could_extract_minus_sign() is True + assert (1 - x + y).could_extract_minus_sign() is False + assert ((-x - x*y)/y).could_extract_minus_sign() is False + assert ((x + x*y)/(-y)).could_extract_minus_sign() is True + assert ((x + x*y)/y).could_extract_minus_sign() is False + assert ((-x - y)/(x + y)).could_extract_minus_sign() is False + + class sign_invariant(Function, Expr): + nargs = 1 + def __neg__(self): + return self + foo = sign_invariant(x) + assert foo == -foo + assert foo.could_extract_minus_sign() is False + assert (x - y).could_extract_minus_sign() is False + assert (-x + y).could_extract_minus_sign() is True + assert (x - 1).could_extract_minus_sign() is False + assert (1 - x).could_extract_minus_sign() is True + assert (sqrt(2) - 1).could_extract_minus_sign() is True + assert (1 - sqrt(2)).could_extract_minus_sign() is False + # check that result is canonical + eq = (3*x + 15*y).extract_multiplicatively(3) + assert eq.args == eq.func(*eq.args).args + + +def test_nan_extractions(): + for r in (1, 0, I, nan): + assert nan.extract_additively(r) is None + assert nan.extract_multiplicatively(r) is None + + +def test_coeff(): + assert (x + 1).coeff(x + 1) == 1 + assert (3*x).coeff(0) == 0 + assert (z*(1 + x)*x**2).coeff(1 + x) == z*x**2 + assert (1 + 2*x*x**(1 + x)).coeff(x*x**(1 + x)) == 2 + assert (1 + 2*x**(y + z)).coeff(x**(y + z)) == 2 + assert (3 + 2*x + 4*x**2).coeff(1) == 0 + assert (3 + 2*x + 4*x**2).coeff(-1) == 0 + assert (3 + 2*x + 4*x**2).coeff(x) == 2 + assert (3 + 2*x + 4*x**2).coeff(x**2) == 4 + assert (3 + 2*x + 4*x**2).coeff(x**3) == 0 + + assert (-x/8 + x*y).coeff(x) == Rational(-1, 8) + y + assert (-x/8 + x*y).coeff(-x) == S.One/8 + assert (4*x).coeff(2*x) == 0 + assert (2*x).coeff(2*x) == 1 + assert (-oo*x).coeff(x*oo) == -1 + assert (10*x).coeff(x, 0) == 0 + assert (10*x).coeff(10*x, 0) == 0 + + n1, n2 = symbols('n1 n2', commutative=False) + assert (n1*n2).coeff(n1) == 1 + assert (n1*n2).coeff(n2) == n1 + assert (n1*n2 + x*n1).coeff(n1) == 1 # 1*n1*(n2+x) + assert (n2*n1 + x*n1).coeff(n1) == n2 + x + assert (n2*n1 + x*n1**2).coeff(n1) == n2 + assert (n1**x).coeff(n1) == 0 + assert (n1*n2 + n2*n1).coeff(n1) == 0 + assert (2*(n1 + n2)*n2).coeff(n1 + n2, right=1) == n2 + assert (2*(n1 + n2)*n2).coeff(n1 + n2, right=0) == 2 + + assert (2*f(x) + 3*f(x).diff(x)).coeff(f(x)) == 2 + + expr = z*(x + y)**2 + expr2 = z*(x + y)**2 + z*(2*x + 2*y)**2 + assert expr.coeff(z) == (x + y)**2 + assert expr.coeff(x + y) == 0 + assert expr2.coeff(z) == (x + y)**2 + (2*x + 2*y)**2 + + assert (x + y + 3*z).coeff(1) == x + y + assert (-x + 2*y).coeff(-1) == x + assert (x - 2*y).coeff(-1) == 2*y + assert (3 + 2*x + 4*x**2).coeff(1) == 0 + assert (-x - 2*y).coeff(2) == -y + assert (x + sqrt(2)*x).coeff(sqrt(2)) == x + assert (3 + 2*x + 4*x**2).coeff(x) == 2 + assert (3 + 2*x + 4*x**2).coeff(x**2) == 4 + assert (3 + 2*x + 4*x**2).coeff(x**3) == 0 + assert (z*(x + y)**2).coeff((x + y)**2) == z + assert (z*(x + y)**2).coeff(x + y) == 0 + assert (2 + 2*x + (x + 1)*y).coeff(x + 1) == y + + assert (x + 2*y + 3).coeff(1) == x + assert (x + 2*y + 3).coeff(x, 0) == 2*y + 3 + assert (x**2 + 2*y + 3*x).coeff(x**2, 0) == 2*y + 3*x + assert x.coeff(0, 0) == 0 + assert x.coeff(x, 0) == 0 + + n, m, o, l = symbols('n m o l', commutative=False) + assert n.coeff(n) == 1 + assert y.coeff(n) == 0 + assert (3*n).coeff(n) == 3 + assert (2 + n).coeff(x*m) == 0 + assert (2*x*n*m).coeff(x) == 2*n*m + assert (2 + n).coeff(x*m*n + y) == 0 + assert (2*x*n*m).coeff(3*n) == 0 + assert (n*m + m*n*m).coeff(n) == 1 + m + assert (n*m + m*n*m).coeff(n, right=True) == m # = (1 + m)*n*m + assert (n*m + m*n).coeff(n) == 0 + assert (n*m + o*m*n).coeff(m*n) == o + assert (n*m + o*m*n).coeff(m*n, right=True) == 1 + assert (n*m + n*m*n).coeff(n*m, right=True) == 1 + n # = n*m*(n + 1) + + assert (x*y).coeff(z, 0) == x*y + + assert (x*n + y*n + z*m).coeff(n) == x + y + assert (n*m + n*o + o*l).coeff(n, right=True) == m + o + assert (x*n*m*n + y*n*m*o + z*l).coeff(m, right=True) == x*n + y*o + assert (x*n*m*n + x*n*m*o + z*l).coeff(m, right=True) == n + o + assert (x*n*m*n + x*n*m*o + z*l).coeff(m) == x*n + + +def test_coeff2(): + r, kappa = symbols('r, kappa') + psi = Function("psi") + g = 1/r**2 * (2*r*psi(r).diff(r, 1) + r**2 * psi(r).diff(r, 2)) + g = g.expand() + assert g.coeff(psi(r).diff(r)) == 2/r + + +def test_coeff2_0(): + r, kappa = symbols('r, kappa') + psi = Function("psi") + g = 1/r**2 * (2*r*psi(r).diff(r, 1) + r**2 * psi(r).diff(r, 2)) + g = g.expand() + + assert g.coeff(psi(r).diff(r, 2)) == 1 + + +def test_coeff_expand(): + expr = z*(x + y)**2 + expr2 = z*(x + y)**2 + z*(2*x + 2*y)**2 + assert expr.coeff(z) == (x + y)**2 + assert expr2.coeff(z) == (x + y)**2 + (2*x + 2*y)**2 + + +def test_integrate(): + assert x.integrate(x) == x**2/2 + assert x.integrate((x, 0, 1)) == S.Half + + +def test_as_base_exp(): + assert x.as_base_exp() == (x, S.One) + assert (x*y*z).as_base_exp() == (x*y*z, S.One) + assert (x + y + z).as_base_exp() == (x + y + z, S.One) + assert ((x + y)**z).as_base_exp() == (x + y, z) + + +def test_issue_4963(): + assert hasattr(Mul(x, y), "is_commutative") + assert hasattr(Mul(x, y, evaluate=False), "is_commutative") + assert hasattr(Pow(x, y), "is_commutative") + assert hasattr(Pow(x, y, evaluate=False), "is_commutative") + expr = Mul(Pow(2, 2, evaluate=False), 3, evaluate=False) + 1 + assert hasattr(expr, "is_commutative") + + +def test_action_verbs(): + assert nsimplify(1/(exp(3*pi*x/5) + 1)) == \ + (1/(exp(3*pi*x/5) + 1)).nsimplify() + assert ratsimp(1/x + 1/y) == (1/x + 1/y).ratsimp() + assert trigsimp(log(x), deep=True) == (log(x)).trigsimp(deep=True) + assert radsimp(1/(2 + sqrt(2))) == (1/(2 + sqrt(2))).radsimp() + assert radsimp(1/(a + b*sqrt(c)), symbolic=False) == \ + (1/(a + b*sqrt(c))).radsimp(symbolic=False) + assert powsimp(x**y*x**z*y**z, combine='all') == \ + (x**y*x**z*y**z).powsimp(combine='all') + assert (x**t*y**t).powsimp(force=True) == (x*y)**t + assert simplify(x**y*x**z*y**z) == (x**y*x**z*y**z).simplify() + assert together(1/x + 1/y) == (1/x + 1/y).together() + assert collect(a*x**2 + b*x**2 + a*x - b*x + c, x) == \ + (a*x**2 + b*x**2 + a*x - b*x + c).collect(x) + assert apart(y/(y + 2)/(y + 1), y) == (y/(y + 2)/(y + 1)).apart(y) + assert combsimp(y/(x + 2)/(x + 1)) == (y/(x + 2)/(x + 1)).combsimp() + assert gammasimp(gamma(x)/gamma(x-5)) == (gamma(x)/gamma(x-5)).gammasimp() + assert factor(x**2 + 5*x + 6) == (x**2 + 5*x + 6).factor() + assert refine(sqrt(x**2)) == sqrt(x**2).refine() + assert cancel((x**2 + 5*x + 6)/(x + 2)) == ((x**2 + 5*x + 6)/(x + 2)).cancel() + + +def test_as_powers_dict(): + assert x.as_powers_dict() == {x: 1} + assert (x**y*z).as_powers_dict() == {x: y, z: 1} + assert Mul(2, 2, evaluate=False).as_powers_dict() == {S(2): S(2)} + assert (x*y).as_powers_dict()[z] == 0 + assert (x + y).as_powers_dict()[z] == 0 + + +def test_as_coefficients_dict(): + check = [S.One, x, y, x*y, 1] + assert [Add(3*x, 2*x, y, 3).as_coefficients_dict()[i] for i in check] == \ + [3, 5, 1, 0, 3] + assert [Add(3*x, 2*x, y, 3, evaluate=False).as_coefficients_dict()[i] + for i in check] == [3, 5, 1, 0, 3] + assert [(3*x*y).as_coefficients_dict()[i] for i in check] == \ + [0, 0, 0, 3, 0] + assert [(3.0*x*y).as_coefficients_dict()[i] for i in check] == \ + [0, 0, 0, 3.0, 0] + assert (3.0*x*y).as_coefficients_dict()[3.0*x*y] == 0 + eq = x*(x + 1)*a + x*b + c/x + assert eq.as_coefficients_dict(x) == {x: b, 1/x: c, + x*(x + 1): a} + assert eq.expand().as_coefficients_dict(x) == {x**2: a, x: a + b, 1/x: c} + assert x.as_coefficients_dict() == {x: S.One} + + +def test_args_cnc(): + A = symbols('A', commutative=False) + assert (x + A).args_cnc() == \ + [[], [x + A]] + assert (x + a).args_cnc() == \ + [[a + x], []] + assert (x*a).args_cnc() == \ + [[a, x], []] + assert (x*y*A*(A + 1)).args_cnc(cset=True) == \ + [{x, y}, [A, 1 + A]] + assert Mul(x, x, evaluate=False).args_cnc(cset=True, warn=False) == \ + [{x}, []] + assert Mul(x, x**2, evaluate=False).args_cnc(cset=True, warn=False) == \ + [{x, x**2}, []] + raises(ValueError, lambda: Mul(x, x, evaluate=False).args_cnc(cset=True)) + assert Mul(x, y, x, evaluate=False).args_cnc() == \ + [[x, y, x], []] + # always split -1 from leading number + assert (-1.*x).args_cnc() == [[-1, 1.0, x], []] + + +def test_new_rawargs(): + n = Symbol('n', commutative=False) + a = x + n + assert a.is_commutative is False + assert a._new_rawargs(x).is_commutative + assert a._new_rawargs(x, y).is_commutative + assert a._new_rawargs(x, n).is_commutative is False + assert a._new_rawargs(x, y, n).is_commutative is False + m = x*n + assert m.is_commutative is False + assert m._new_rawargs(x).is_commutative + assert m._new_rawargs(n).is_commutative is False + assert m._new_rawargs(x, y).is_commutative + assert m._new_rawargs(x, n).is_commutative is False + assert m._new_rawargs(x, y, n).is_commutative is False + + assert m._new_rawargs(x, n, reeval=False).is_commutative is False + assert m._new_rawargs(S.One) is S.One + + +def test_issue_5226(): + assert Add(evaluate=False) == 0 + assert Mul(evaluate=False) == 1 + assert Mul(x + y, evaluate=False).is_Add + + +def test_free_symbols(): + # free_symbols should return the free symbols of an object + assert S.One.free_symbols == set() + assert x.free_symbols == {x} + assert Integral(x, (x, 1, y)).free_symbols == {y} + assert (-Integral(x, (x, 1, y))).free_symbols == {y} + assert meter.free_symbols == set() + assert (meter**x).free_symbols == {x} + + +def test_has_free(): + assert x.has_free(x) + assert not x.has_free(y) + assert (x + y).has_free(x) + assert (x + y).has_free(*(x, z)) + assert f(x).has_free(x) + assert f(x).has_free(f(x)) + assert Integral(f(x), (f(x), 1, y)).has_free(y) + assert not Integral(f(x), (f(x), 1, y)).has_free(x) + assert not Integral(f(x), (f(x), 1, y)).has_free(f(x)) + # simple extraction + assert (x + 1 + y).has_free(x + 1) + assert not (x + 2 + y).has_free(x + 1) + assert (2 + 3*x*y).has_free(3*x) + raises(TypeError, lambda: x.has_free({x, y})) + s = FiniteSet(1, 2) + assert Piecewise((s, x > 3), (4, True)).has_free(s) + assert not Piecewise((1, x > 3), (4, True)).has_free(s) + # can't make set of these, but fallback will handle + raises(TypeError, lambda: x.has_free(y, [])) + + +def test_has_xfree(): + assert (x + 1).has_xfree({x}) + assert ((x + 1)**2).has_xfree({x + 1}) + assert not (x + y + 1).has_xfree({x + 1}) + raises(TypeError, lambda: x.has_xfree(x)) + raises(TypeError, lambda: x.has_xfree([x])) + + +def test_issue_5300(): + x = Symbol('x', commutative=False) + assert x*sqrt(2)/sqrt(6) == x*sqrt(3)/3 + + +def test_floordiv(): + from sympy.functions.elementary.integers import floor + assert x // y == floor(x / y) + + +def test_as_coeff_Mul(): + assert Integer(3).as_coeff_Mul() == (Integer(3), Integer(1)) + assert Rational(3, 4).as_coeff_Mul() == (Rational(3, 4), Integer(1)) + assert Float(5.0).as_coeff_Mul() == (Float(5.0), Integer(1)) + assert Float(0.0).as_coeff_Mul() == (Float(0.0), Integer(1)) + + assert (Integer(3)*x).as_coeff_Mul() == (Integer(3), x) + assert (Rational(3, 4)*x).as_coeff_Mul() == (Rational(3, 4), x) + assert (Float(5.0)*x).as_coeff_Mul() == (Float(5.0), x) + + assert (Integer(3)*x*y).as_coeff_Mul() == (Integer(3), x*y) + assert (Rational(3, 4)*x*y).as_coeff_Mul() == (Rational(3, 4), x*y) + assert (Float(5.0)*x*y).as_coeff_Mul() == (Float(5.0), x*y) + + assert (x).as_coeff_Mul() == (S.One, x) + assert (x*y).as_coeff_Mul() == (S.One, x*y) + assert (-oo*x).as_coeff_Mul(rational=True) == (-1, oo*x) + + +def test_as_coeff_Add(): + assert Integer(3).as_coeff_Add() == (Integer(3), Integer(0)) + assert Rational(3, 4).as_coeff_Add() == (Rational(3, 4), Integer(0)) + assert Float(5.0).as_coeff_Add() == (Float(5.0), Integer(0)) + + assert (Integer(3) + x).as_coeff_Add() == (Integer(3), x) + assert (Rational(3, 4) + x).as_coeff_Add() == (Rational(3, 4), x) + assert (Float(5.0) + x).as_coeff_Add() == (Float(5.0), x) + assert (Float(5.0) + x).as_coeff_Add(rational=True) == (0, Float(5.0) + x) + + assert (Integer(3) + x + y).as_coeff_Add() == (Integer(3), x + y) + assert (Rational(3, 4) + x + y).as_coeff_Add() == (Rational(3, 4), x + y) + assert (Float(5.0) + x + y).as_coeff_Add() == (Float(5.0), x + y) + + assert (x).as_coeff_Add() == (S.Zero, x) + assert (x*y).as_coeff_Add() == (S.Zero, x*y) + + +def test_expr_sorting(): + + exprs = [1/x**2, 1/x, sqrt(sqrt(x)), sqrt(x), x, sqrt(x)**3, x**2] + assert sorted(exprs, key=default_sort_key) == exprs + + exprs = [x, 2*x, 2*x**2, 2*x**3, x**n, 2*x**n, sin(x), sin(x)**n, + sin(x**2), cos(x), cos(x**2), tan(x)] + assert sorted(exprs, key=default_sort_key) == exprs + + exprs = [x + 1, x**2 + x + 1, x**3 + x**2 + x + 1] + assert sorted(exprs, key=default_sort_key) == exprs + + exprs = [S(4), x - 3*I/2, x + 3*I/2, x - 4*I + 1, x + 4*I + 1] + assert sorted(exprs, key=default_sort_key) == exprs + + exprs = [f(1), f(2), f(3), f(1, 2, 3), g(1), g(2), g(3), g(1, 2, 3)] + assert sorted(exprs, key=default_sort_key) == exprs + + exprs = [f(x), g(x), exp(x), sin(x), cos(x), factorial(x)] + assert sorted(exprs, key=default_sort_key) == exprs + + exprs = [Tuple(x, y), Tuple(x, z), Tuple(x, y, z)] + assert sorted(exprs, key=default_sort_key) == exprs + + exprs = [[3], [1, 2]] + assert sorted(exprs, key=default_sort_key) == exprs + + exprs = [[1, 2], [2, 3]] + assert sorted(exprs, key=default_sort_key) == exprs + + exprs = [[1, 2], [1, 2, 3]] + assert sorted(exprs, key=default_sort_key) == exprs + + exprs = [{x: -y}, {x: y}] + assert sorted(exprs, key=default_sort_key) == exprs + + exprs = [{1}, {1, 2}] + assert sorted(exprs, key=default_sort_key) == exprs + + a, b = exprs = [Dummy('x'), Dummy('x')] + assert sorted([b, a], key=default_sort_key) == exprs + + +def test_as_ordered_factors(): + + assert x.as_ordered_factors() == [x] + assert (2*x*x**n*sin(x)*cos(x)).as_ordered_factors() \ + == [Integer(2), x, x**n, sin(x), cos(x)] + + args = [f(1), f(2), f(3), f(1, 2, 3), g(1), g(2), g(3), g(1, 2, 3)] + expr = Mul(*args) + + assert expr.as_ordered_factors() == args + + A, B = symbols('A,B', commutative=False) + + assert (A*B).as_ordered_factors() == [A, B] + assert (B*A).as_ordered_factors() == [B, A] + + +def test_as_ordered_terms(): + + assert x.as_ordered_terms() == [x] + assert (sin(x)**2*cos(x) + sin(x)*cos(x)**2 + 1).as_ordered_terms() \ + == [sin(x)**2*cos(x), sin(x)*cos(x)**2, 1] + + args = [f(1), f(2), f(3), f(1, 2, 3), g(1), g(2), g(3), g(1, 2, 3)] + expr = Add(*args) + + assert expr.as_ordered_terms() == args + + assert (1 + 4*sqrt(3)*pi*x).as_ordered_terms() == [4*pi*x*sqrt(3), 1] + + assert ( 2 + 3*I).as_ordered_terms() == [2, 3*I] + assert (-2 + 3*I).as_ordered_terms() == [-2, 3*I] + assert ( 2 - 3*I).as_ordered_terms() == [2, -3*I] + assert (-2 - 3*I).as_ordered_terms() == [-2, -3*I] + + assert ( 4 + 3*I).as_ordered_terms() == [4, 3*I] + assert (-4 + 3*I).as_ordered_terms() == [-4, 3*I] + assert ( 4 - 3*I).as_ordered_terms() == [4, -3*I] + assert (-4 - 3*I).as_ordered_terms() == [-4, -3*I] + + e = x**2*y**2 + x*y**4 + y + 2 + + assert e.as_ordered_terms(order="lex") == [x**2*y**2, x*y**4, y, 2] + assert e.as_ordered_terms(order="grlex") == [x*y**4, x**2*y**2, y, 2] + assert e.as_ordered_terms(order="rev-lex") == [2, y, x*y**4, x**2*y**2] + assert e.as_ordered_terms(order="rev-grlex") == [2, y, x**2*y**2, x*y**4] + + k = symbols('k') + assert k.as_ordered_terms(data=True) == ([(k, ((1.0, 0.0), (1,), ()))], [k]) + + +def test_sort_key_atomic_expr(): + from sympy.physics.units import m, s + assert sorted([-m, s], key=lambda arg: arg.sort_key()) == [-m, s] + + +def test_eval_interval(): + assert exp(x)._eval_interval(*Tuple(x, 0, 1)) == exp(1) - exp(0) + + # issue 4199 + a = x/y + raises(NotImplementedError, lambda: a._eval_interval(x, S.Zero, oo)._eval_interval(y, oo, S.Zero)) + raises(NotImplementedError, lambda: a._eval_interval(x, S.Zero, oo)._eval_interval(y, S.Zero, oo)) + a = x - y + raises(NotImplementedError, lambda: a._eval_interval(x, S.One, oo)._eval_interval(y, oo, S.One)) + raises(ValueError, lambda: x._eval_interval(x, None, None)) + a = -y*Heaviside(x - y) + assert a._eval_interval(x, -oo, oo) == -y + assert a._eval_interval(x, oo, -oo) == y + + +def test_eval_interval_zoo(): + # Test that limit is used when zoo is returned + assert Si(1/x)._eval_interval(x, S.Zero, S.One) == -pi/2 + Si(1) + + +def test_primitive(): + assert (3*(x + 1)**2).primitive() == (3, (x + 1)**2) + assert (6*x + 2).primitive() == (2, 3*x + 1) + assert (x/2 + 3).primitive() == (S.Half, x + 6) + eq = (6*x + 2)*(x/2 + 3) + assert eq.primitive()[0] == 1 + eq = (2 + 2*x)**2 + assert eq.primitive()[0] == 1 + assert (4.0*x).primitive() == (1, 4.0*x) + assert (4.0*x + y/2).primitive() == (S.Half, 8.0*x + y) + assert (-2*x).primitive() == (2, -x) + assert Add(5*z/7, 0.5*x, 3*y/2, evaluate=False).primitive() == \ + (S.One/14, 7.0*x + 21*y + 10*z) + for i in [S.Infinity, S.NegativeInfinity, S.ComplexInfinity]: + assert (i + x/3).primitive() == \ + (S.One/3, i + x) + assert (S.Infinity + 2*x/3 + 4*y/7).primitive() == \ + (S.One/21, 14*x + 12*y + oo) + assert S.Zero.primitive() == (S.One, S.Zero) + + +def test_issue_5843(): + a = 1 + x + assert (2*a).extract_multiplicatively(a) == 2 + assert (4*a).extract_multiplicatively(2*a) == 2 + assert ((3*a)*(2*a)).extract_multiplicatively(a) == 6*a + + +def test_is_constant(): + from sympy.solvers.solvers import checksol + assert Sum(x, (x, 1, 10)).is_constant() is True + assert Sum(x, (x, 1, n)).is_constant() is False + assert Sum(x, (x, 1, n)).is_constant(y) is True + assert Sum(x, (x, 1, n)).is_constant(n) is False + assert Sum(x, (x, 1, n)).is_constant(x) is True + eq = a*cos(x)**2 + a*sin(x)**2 - a + assert eq.is_constant() is True + assert eq.subs({x: pi, a: 2}) == eq.subs({x: pi, a: 3}) == 0 + assert x.is_constant() is False + assert x.is_constant(y) is True + assert log(x/y).is_constant() is False + + assert checksol(x, x, Sum(x, (x, 1, n))) is False + assert checksol(x, x, Sum(x, (x, 1, n))) is False + assert f(1).is_constant + assert checksol(x, x, f(x)) is False + + assert Pow(x, S.Zero, evaluate=False).is_constant() is True # == 1 + assert Pow(S.Zero, x, evaluate=False).is_constant() is False # == 0 or 1 + assert (2**x).is_constant() is False + assert Pow(S(2), S(3), evaluate=False).is_constant() is True + + z1, z2 = symbols('z1 z2', zero=True) + assert (z1 + 2*z2).is_constant() is True + + assert meter.is_constant() is True + assert (3*meter).is_constant() is True + assert (x*meter).is_constant() is False + + +def test_equals(): + assert (-3 - sqrt(5) + (-sqrt(10)/2 - sqrt(2)/2)**2).equals(0) + assert (x**2 - 1).equals((x + 1)*(x - 1)) + assert (cos(x)**2 + sin(x)**2).equals(1) + assert (a*cos(x)**2 + a*sin(x)**2).equals(a) + r = sqrt(2) + assert (-1/(r + r*x) + 1/r/(1 + x)).equals(0) + assert factorial(x + 1).equals((x + 1)*factorial(x)) + assert sqrt(3).equals(2*sqrt(3)) is False + assert (sqrt(5)*sqrt(3)).equals(sqrt(3)) is False + assert (sqrt(5) + sqrt(3)).equals(0) is False + assert (sqrt(5) + pi).equals(0) is False + assert meter.equals(0) is False + assert (3*meter**2).equals(0) is False + eq = -(-1)**(S(3)/4)*6**(S.One/4) + (-6)**(S.One/4)*I + if eq != 0: # if canonicalization makes this zero, skip the test + assert eq.equals(0) + assert sqrt(x).equals(0) is False + + # from integrate(x*sqrt(1 + 2*x), x); + # diff is zero only when assumptions allow + i = 2*sqrt(2)*x**(S(5)/2)*(1 + 1/(2*x))**(S(5)/2)/5 + \ + 2*sqrt(2)*x**(S(3)/2)*(1 + 1/(2*x))**(S(5)/2)/(-6 - 3/x) + ans = sqrt(2*x + 1)*(6*x**2 + x - 1)/15 + diff = i - ans + assert diff.equals(0) is None # should be False, but previously this was False due to wrong intermediate result + assert diff.subs(x, Rational(-1, 2)/2) == 7*sqrt(2)/120 + # there are regions for x for which the expression is True, for + # example, when x < -1/2 or x > 0 the expression is zero + p = Symbol('p', positive=True) + assert diff.subs(x, p).equals(0) is True + assert diff.subs(x, -1).equals(0) is True + + # prove via minimal_polynomial or self-consistency + eq = sqrt(1 + sqrt(3)) + sqrt(3 + 3*sqrt(3)) - sqrt(10 + 6*sqrt(3)) + assert eq.equals(0) + q = 3**Rational(1, 3) + 3 + p = expand(q**3)**Rational(1, 3) + assert (p - q).equals(0) + + # issue 6829 + # eq = q*x + q/4 + x**4 + x**3 + 2*x**2 - S.One/3 + # z = eq.subs(x, solve(eq, x)[0]) + q = symbols('q') + z = (q*(-sqrt(-2*(-(q - S(7)/8)**S(2)/8 - S(2197)/13824)**(S.One/3) - + S(13)/12)/2 - sqrt((2*q - S(7)/4)/sqrt(-2*(-(q - S(7)/8)**S(2)/8 - + S(2197)/13824)**(S.One/3) - S(13)/12) + 2*(-(q - S(7)/8)**S(2)/8 - + S(2197)/13824)**(S.One/3) - S(13)/6)/2 - S.One/4) + q/4 + (-sqrt(-2*(-(q + - S(7)/8)**S(2)/8 - S(2197)/13824)**(S.One/3) - S(13)/12)/2 - sqrt((2*q + - S(7)/4)/sqrt(-2*(-(q - S(7)/8)**S(2)/8 - S(2197)/13824)**(S.One/3) - + S(13)/12) + 2*(-(q - S(7)/8)**S(2)/8 - S(2197)/13824)**(S.One/3) - + S(13)/6)/2 - S.One/4)**4 + (-sqrt(-2*(-(q - S(7)/8)**S(2)/8 - + S(2197)/13824)**(S.One/3) - S(13)/12)/2 - sqrt((2*q - + S(7)/4)/sqrt(-2*(-(q - S(7)/8)**S(2)/8 - S(2197)/13824)**(S.One/3) - + S(13)/12) + 2*(-(q - S(7)/8)**S(2)/8 - S(2197)/13824)**(S.One/3) - + S(13)/6)/2 - S.One/4)**3 + 2*(-sqrt(-2*(-(q - S(7)/8)**S(2)/8 - + S(2197)/13824)**(S.One/3) - S(13)/12)/2 - sqrt((2*q - + S(7)/4)/sqrt(-2*(-(q - S(7)/8)**S(2)/8 - S(2197)/13824)**(S.One/3) - + S(13)/12) + 2*(-(q - S(7)/8)**S(2)/8 - S(2197)/13824)**(S.One/3) - + S(13)/6)/2 - S.One/4)**2 - Rational(1, 3)) + assert z.equals(0) + + +def test_random(): + from sympy.functions.combinatorial.numbers import lucas + from sympy.simplify.simplify import posify + assert posify(x)[0]._random() is not None + assert lucas(n)._random(2, -2, 0, -1, 1) is None + + # issue 8662 + assert Piecewise((Max(x, y), z))._random() is None + + +def test_round(): + assert str(Float('0.1249999').round(2)) == '0.12' + d20 = 12345678901234567890 + ans = S(d20).round(2) + assert ans.is_Integer and ans == d20 + ans = S(d20).round(-2) + assert ans.is_Integer and ans == 12345678901234567900 + assert str(S('1/7').round(4)) == '0.1429' + assert str(S('.[12345]').round(4)) == '0.1235' + assert str(S('.1349').round(2)) == '0.13' + n = S(12345) + ans = n.round() + assert ans.is_Integer + assert ans == n + ans = n.round(1) + assert ans.is_Integer + assert ans == n + ans = n.round(4) + assert ans.is_Integer + assert ans == n + assert n.round(-1) == 12340 + + r = Float(str(n)).round(-4) + assert r == 10000.0 + + assert n.round(-5) == 0 + + assert str((pi + sqrt(2)).round(2)) == '4.56' + assert (10*(pi + sqrt(2))).round(-1) == 50.0 + raises(TypeError, lambda: round(x + 2, 2)) + assert str(S(2.3).round(1)) == '2.3' + # rounding in SymPy (as in Decimal) should be + # exact for the given precision; we check here + # that when a 5 follows the last digit that + # the rounded digit will be even. + for i in range(-99, 100): + # construct a decimal that ends in 5, e.g. 123 -> 0.1235 + s = str(abs(i)) + p = len(s) # we are going to round to the last digit of i + n = '0.%s5' % s # put a 5 after i's digits + j = p + 2 # 2 for '0.' + if i < 0: # 1 for '-' + j += 1 + n = '-' + n + v = str(Float(n).round(p))[:j] # pertinent digits + if v.endswith('.'): + continue # it ends with 0 which is even + L = int(v[-1]) # last digit + assert L % 2 == 0, (n, '->', v) + + assert (Float(.3, 3) + 2*pi).round() == 7 + assert (Float(.3, 3) + 2*pi*100).round() == 629 + assert (pi + 2*E*I).round() == 3 + 5*I + # don't let request for extra precision give more than + # what is known (in this case, only 3 digits) + assert str((Float(.03, 3) + 2*pi/100).round(5)) == '0.0928' + assert str((Float(.03, 3) + 2*pi/100).round(4)) == '0.0928' + + assert S.Zero.round() == 0 + + a = (Add(1, Float('1.' + '9'*27, ''), evaluate=0)) + assert a.round(10) == Float('3.000000000000000000000000000', '') + assert a.round(25) == Float('3.000000000000000000000000000', '') + assert a.round(26) == Float('3.000000000000000000000000000', '') + assert a.round(27) == Float('2.999999999999999999999999999', '') + assert a.round(30) == Float('2.999999999999999999999999999', '') + #assert a.round(10) == Float('3.0000000000', '') + #assert a.round(25) == Float('3.0000000000000000000000000', '') + #assert a.round(26) == Float('3.00000000000000000000000000', '') + #assert a.round(27) == Float('2.999999999999999999999999999', '') + #assert a.round(30) == Float('2.999999999999999999999999999', '') + + # XXX: Should round set the precision of the result? + # The previous version of the tests above is this but they only pass + # because Floats with unequal precision compare equal: + # + # assert a.round(10) == Float('3.0000000000', '') + # assert a.round(25) == Float('3.0000000000000000000000000', '') + # assert a.round(26) == Float('3.00000000000000000000000000', '') + # assert a.round(27) == Float('2.999999999999999999999999999', '') + # assert a.round(30) == Float('2.999999999999999999999999999', '') + + raises(TypeError, lambda: x.round()) + raises(TypeError, lambda: f(1).round()) + + # exact magnitude of 10 + assert str(S.One.round()) == '1' + assert str(S(100).round()) == '100' + + # applied to real and imaginary portions + assert (2*pi + E*I).round() == 6 + 3*I + assert (2*pi + I/10).round() == 6 + assert (pi/10 + 2*I).round() == 2*I + # the lhs re and im parts are Float with dps of 2 + # and those on the right have dps of 15 so they won't compare + # equal unless we use string or compare components (which will + # then coerce the floats to the same precision) or re-create + # the floats + assert str((pi/10 + E*I).round(2)) == '0.31 + 2.72*I' + assert str((pi/10 + E*I).round(2).as_real_imag()) == '(0.31, 2.72)' + assert str((pi/10 + E*I).round(2)) == '0.31 + 2.72*I' + + # issue 6914 + assert (I**(I + 3)).round(3) == Float('-0.208', '')*I + + # issue 8720 + assert S(-123.6).round() == -124 + assert S(-1.5).round() == -2 + assert S(-100.5).round() == -100 + assert S(-1.5 - 10.5*I).round() == -2 - 10*I + + # issue 7961 + assert str(S(0.006).round(2)) == '0.01' + assert str(S(0.00106).round(4)) == '0.0011' + + # issue 8147 + assert S.NaN.round() is S.NaN + assert S.Infinity.round() is S.Infinity + assert S.NegativeInfinity.round() is S.NegativeInfinity + assert S.ComplexInfinity.round() is S.ComplexInfinity + + # check that types match + for i in range(2): + fi = float(i) + # 2 args + assert all(type(round(i, p)) is int for p in (-1, 0, 1)) + assert all(S(i).round(p).is_Integer for p in (-1, 0, 1)) + assert all(type(round(fi, p)) is float for p in (-1, 0, 1)) + assert all(S(fi).round(p).is_Float for p in (-1, 0, 1)) + # 1 arg (p is None) + assert type(round(i)) is int + assert S(i).round().is_Integer + assert type(round(fi)) is int + assert S(fi).round().is_Integer + + # issue 25698 + n = 6000002 + assert int(n*(log(n) + log(log(n)))) == 110130079 + one = cos(2)**2 + sin(2)**2 + eq = exp(one*I*pi) + qr, qi = eq.as_real_imag() + assert qi.round(2) == 0.0 + assert eq.round(2) == -1.0 + eq = one - 1/S(10**120) + assert S.true not in (eq > 1, eq < 1) + assert int(eq) == int(.9) == 0 + assert int(-eq) == int(-.9) == 0 + + +def test_held_expression_UnevaluatedExpr(): + x = symbols("x") + he = UnevaluatedExpr(1/x) + e1 = x*he + + assert isinstance(e1, Mul) + assert e1.args == (x, he) + assert e1.doit() == 1 + assert UnevaluatedExpr(Derivative(x, x)).doit(deep=False + ) == Derivative(x, x) + assert UnevaluatedExpr(Derivative(x, x)).doit() == 1 + + xx = Mul(x, x, evaluate=False) + assert xx != x**2 + + ue2 = UnevaluatedExpr(xx) + assert isinstance(ue2, UnevaluatedExpr) + assert ue2.args == (xx,) + assert ue2.doit() == x**2 + assert ue2.doit(deep=False) == xx + + x2 = UnevaluatedExpr(2)*2 + assert type(x2) is Mul + assert x2.args == (2, UnevaluatedExpr(2)) + +def test_round_exception_nostr(): + # Don't use the string form of the expression in the round exception, as + # it's too slow + s = Symbol('bad') + try: + s.round() + except TypeError as e: + assert 'bad' not in str(e) + else: + # Did not raise + raise AssertionError("Did not raise") + + +def test_extract_branch_factor(): + assert exp_polar(2.0*I*pi).extract_branch_factor() == (1, 1) + + +def test_identity_removal(): + assert Add.make_args(x + 0) == (x,) + assert Mul.make_args(x*1) == (x,) + + +def test_float_0(): + assert Float(0.0) + 1 == Float(1.0) + + +@XFAIL +def test_float_0_fail(): + assert Float(0.0)*x == Float(0.0) + assert (x + Float(0.0)).is_Add + + +def test_issue_6325(): + ans = (b**2 + z**2 - (b*(a + b*t) + z*(c + t*z))**2/( + (a + b*t)**2 + (c + t*z)**2))/sqrt((a + b*t)**2 + (c + t*z)**2) + e = sqrt((a + b*t)**2 + (c + z*t)**2) + assert diff(e, t, 2) == ans + assert e.diff(t, 2) == ans + assert diff(e, t, 2, simplify=False) != ans + + +def test_issue_7426(): + f1 = a % c + f2 = x % z + assert f1.equals(f2) is None + + +def test_issue_11122(): + x = Symbol('x', extended_positive=False) + assert unchanged(Gt, x, 0) # (x > 0) + # (x > 0) should remain unevaluated after PR #16956 + + x = Symbol('x', positive=False, real=True) + assert (x > 0) is S.false + + +def test_issue_10651(): + x = Symbol('x', real=True) + e1 = (-1 + x)/(1 - x) + e3 = (4*x**2 - 4)/((1 - x)*(1 + x)) + e4 = 1/(cos(x)**2) - (tan(x))**2 + x = Symbol('x', positive=True) + e5 = (1 + x)/x + assert e1.is_constant() is None + assert e3.is_constant() is None + assert e4.is_constant() is None + assert e5.is_constant() is False + + +def test_issue_10161(): + x = symbols('x', real=True) + assert x*abs(x)*abs(x) == x**3 + + +def test_issue_10755(): + x = symbols('x') + raises(TypeError, lambda: int(log(x))) + raises(TypeError, lambda: log(x).round(2)) + + +def test_issue_11877(): + x = symbols('x') + assert integrate(log(S.Half - x), (x, 0, S.Half)) == Rational(-1, 2) -log(2)/2 + + +def test_normal(): + x = symbols('x') + e = Mul(S.Half, 1 + x, evaluate=False) + assert e.normal() == e + + +def test_expr(): + x = symbols('x') + raises(TypeError, lambda: tan(x).series(x, 2, oo, "+")) + + +def test_ExprBuilder(): + eb = ExprBuilder(Mul) + eb.args.extend([x, x]) + assert eb.build() == x**2 + + +def test_issue_22020(): + from sympy.parsing.sympy_parser import parse_expr + x = parse_expr("log((2*V/3-V)/C)/-(R+r)*C") + y = parse_expr("log((2*V/3-V)/C)/-(R+r)*2") + assert x.equals(y) is False + + +def test_non_string_equality(): + # Expressions should not compare equal to strings + x = symbols('x') + one = sympify(1) + assert (x == 'x') is False + assert (x != 'x') is True + assert (one == '1') is False + assert (one != '1') is True + assert (x + 1 == 'x + 1') is False + assert (x + 1 != 'x + 1') is True + + # Make sure == doesn't try to convert the resulting expression to a string + # (e.g., by calling sympify() instead of _sympify()) + + class BadRepr: + def __repr__(self): + raise RuntimeError + + assert (x == BadRepr()) is False + assert (x != BadRepr()) is True + + +def test_21494(): + from sympy.testing.pytest import warns_deprecated_sympy + + with warns_deprecated_sympy(): + assert x.expr_free_symbols == {x} + + with warns_deprecated_sympy(): + assert Basic().expr_free_symbols == set() + + with warns_deprecated_sympy(): + assert S(2).expr_free_symbols == {S(2)} + + with warns_deprecated_sympy(): + assert Indexed("A", x).expr_free_symbols == {Indexed("A", x)} + + with warns_deprecated_sympy(): + assert Subs(x, x, 0).expr_free_symbols == set() + + +def test_Expr__eq__iterable_handling(): + assert x != range(3) + + +def test_format(): + assert '{:1.2f}'.format(S.Zero) == '0.00' + assert '{:+3.0f}'.format(S(3)) == ' +3' + assert '{:23.20f}'.format(pi) == ' 3.14159265358979323846' + assert '{:50.48f}'.format(exp(sin(1))) == '2.319776824715853173956590377503266813254904772376' + + +def test_issue_24045(): + assert powsimp(exp(a)/((c*a - c*b)*(Float(1.0)*c*a - Float(1.0)*c*b))) # doesn't raise diff --git a/lib/python3.12/site-packages/sympy/core/tests/test_exprtools.py b/lib/python3.12/site-packages/sympy/core/tests/test_exprtools.py new file mode 100644 index 0000000000000000000000000000000000000000..b550db1606866fb76442980ea2139aaf61219525 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/core/tests/test_exprtools.py @@ -0,0 +1,493 @@ +"""Tests for tools for manipulating of large commutative expressions. """ + +from sympy.concrete.summations import Sum +from sympy.core.add import Add +from sympy.core.basic import Basic +from sympy.core.containers import (Dict, Tuple) +from sympy.core.function import Function +from sympy.core.mul import Mul +from sympy.core.numbers import (I, Rational, oo) +from sympy.core.singleton import S +from sympy.core.symbol import (Dummy, Symbol, symbols) +from sympy.functions.elementary.exponential import (exp, log) +from sympy.functions.elementary.miscellaneous import (root, sqrt) +from sympy.functions.elementary.trigonometric import (cos, sin) +from sympy.integrals.integrals import Integral +from sympy.series.order import O +from sympy.sets.sets import Interval +from sympy.simplify.radsimp import collect +from sympy.simplify.simplify import simplify +from sympy.core.exprtools import (decompose_power, Factors, Term, _gcd_terms, + gcd_terms, factor_terms, factor_nc, _mask_nc, + _monotonic_sign) +from sympy.core.mul import _keep_coeff as _keep_coeff +from sympy.simplify.cse_opts import sub_pre +from sympy.testing.pytest import raises + +from sympy.abc import a, b, t, x, y, z + + +def test_decompose_power(): + assert decompose_power(x) == (x, 1) + assert decompose_power(x**2) == (x, 2) + assert decompose_power(x**(2*y)) == (x**y, 2) + assert decompose_power(x**(2*y/3)) == (x**(y/3), 2) + assert decompose_power(x**(y*Rational(2, 3))) == (x**(y/3), 2) + + +def test_Factors(): + assert Factors() == Factors({}) == Factors(S.One) + assert Factors().as_expr() is S.One + assert Factors({x: 2, y: 3, sin(x): 4}).as_expr() == x**2*y**3*sin(x)**4 + assert Factors(S.Infinity) == Factors({oo: 1}) + assert Factors(S.NegativeInfinity) == Factors({oo: 1, -1: 1}) + # issue #18059: + assert Factors((x**2)**S.Half).as_expr() == (x**2)**S.Half + + a = Factors({x: 5, y: 3, z: 7}) + b = Factors({ y: 4, z: 3, t: 10}) + + assert a.mul(b) == a*b == Factors({x: 5, y: 7, z: 10, t: 10}) + + assert a.div(b) == divmod(a, b) == \ + (Factors({x: 5, z: 4}), Factors({y: 1, t: 10})) + assert a.quo(b) == a/b == Factors({x: 5, z: 4}) + assert a.rem(b) == a % b == Factors({y: 1, t: 10}) + + assert a.pow(3) == a**3 == Factors({x: 15, y: 9, z: 21}) + assert b.pow(3) == b**3 == Factors({y: 12, z: 9, t: 30}) + + assert a.gcd(b) == Factors({y: 3, z: 3}) + assert a.lcm(b) == Factors({x: 5, y: 4, z: 7, t: 10}) + + a = Factors({x: 4, y: 7, t: 7}) + b = Factors({z: 1, t: 3}) + + assert a.normal(b) == (Factors({x: 4, y: 7, t: 4}), Factors({z: 1})) + + assert Factors(sqrt(2)*x).as_expr() == sqrt(2)*x + + assert Factors(-I)*I == Factors() + assert Factors({S.NegativeOne: S(3)})*Factors({S.NegativeOne: S.One, I: S(5)}) == \ + Factors(I) + assert Factors(sqrt(I)*I) == Factors(I**(S(3)/2)) == Factors({I: S(3)/2}) + assert Factors({I: S(3)/2}).as_expr() == I**(S(3)/2) + + assert Factors(S(2)**x).div(S(3)**x) == \ + (Factors({S(2): x}), Factors({S(3): x})) + assert Factors(2**(2*x + 2)).div(S(8)) == \ + (Factors({S(2): 2*x + 2}), Factors({S(8): S.One})) + + # coverage + # /!\ things break if this is not True + assert Factors({S.NegativeOne: Rational(3, 2)}) == Factors({I: S.One, S.NegativeOne: S.One}) + assert Factors({I: S.One, S.NegativeOne: Rational(1, 3)}).as_expr() == I*(-1)**Rational(1, 3) + + assert Factors(-1.) == Factors({S.NegativeOne: S.One, S(1.): 1}) + assert Factors(-2.) == Factors({S.NegativeOne: S.One, S(2.): 1}) + assert Factors((-2.)**x) == Factors({S(-2.): x}) + assert Factors(S(-2)) == Factors({S.NegativeOne: S.One, S(2): 1}) + assert Factors(S.Half) == Factors({S(2): -S.One}) + assert Factors(Rational(3, 2)) == Factors({S(3): S.One, S(2): S.NegativeOne}) + assert Factors({I: S.One}) == Factors(I) + assert Factors({-1.0: 2, I: 1}) == Factors({S(1.0): 1, I: 1}) + assert Factors({S.NegativeOne: Rational(-3, 2)}).as_expr() == I + A = symbols('A', commutative=False) + assert Factors(2*A**2) == Factors({S(2): 1, A**2: 1}) + assert Factors(I) == Factors({I: S.One}) + assert Factors(x).normal(S(2)) == (Factors(x), Factors(S(2))) + assert Factors(x).normal(S.Zero) == (Factors(), Factors(S.Zero)) + raises(ZeroDivisionError, lambda: Factors(x).div(S.Zero)) + assert Factors(x).mul(S(2)) == Factors(2*x) + assert Factors(x).mul(S.Zero).is_zero + assert Factors(x).mul(1/x).is_one + assert Factors(x**sqrt(2)**3).as_expr() == x**(2*sqrt(2)) + assert Factors(x)**Factors(S(2)) == Factors(x**2) + assert Factors(x).gcd(S.Zero) == Factors(x) + assert Factors(x).lcm(S.Zero).is_zero + assert Factors(S.Zero).div(x) == (Factors(S.Zero), Factors()) + assert Factors(x).div(x) == (Factors(), Factors()) + assert Factors({x: .2})/Factors({x: .2}) == Factors() + assert Factors(x) != Factors() + assert Factors(S.Zero).normal(x) == (Factors(S.Zero), Factors()) + n, d = x**(2 + y), x**2 + f = Factors(n) + assert f.div(d) == f.normal(d) == (Factors(x**y), Factors()) + assert f.gcd(d) == Factors() + d = x**y + assert f.div(d) == f.normal(d) == (Factors(x**2), Factors()) + assert f.gcd(d) == Factors(d) + n = d = 2**x + f = Factors(n) + assert f.div(d) == f.normal(d) == (Factors(), Factors()) + assert f.gcd(d) == Factors(d) + n, d = 2**x, 2**y + f = Factors(n) + assert f.div(d) == f.normal(d) == (Factors({S(2): x}), Factors({S(2): y})) + assert f.gcd(d) == Factors() + + # extraction of constant only + n = x**(x + 3) + assert Factors(n).normal(x**-3) == (Factors({x: x + 6}), Factors({})) + assert Factors(n).normal(x**3) == (Factors({x: x}), Factors({})) + assert Factors(n).normal(x**4) == (Factors({x: x}), Factors({x: 1})) + assert Factors(n).normal(x**(y - 3)) == \ + (Factors({x: x + 6}), Factors({x: y})) + assert Factors(n).normal(x**(y + 3)) == (Factors({x: x}), Factors({x: y})) + assert Factors(n).normal(x**(y + 4)) == \ + (Factors({x: x}), Factors({x: y + 1})) + + assert Factors(n).div(x**-3) == (Factors({x: x + 6}), Factors({})) + assert Factors(n).div(x**3) == (Factors({x: x}), Factors({})) + assert Factors(n).div(x**4) == (Factors({x: x}), Factors({x: 1})) + assert Factors(n).div(x**(y - 3)) == \ + (Factors({x: x + 6}), Factors({x: y})) + assert Factors(n).div(x**(y + 3)) == (Factors({x: x}), Factors({x: y})) + assert Factors(n).div(x**(y + 4)) == \ + (Factors({x: x}), Factors({x: y + 1})) + + assert Factors(3 * x / 2) == Factors({3: 1, 2: -1, x: 1}) + assert Factors(x * x / y) == Factors({x: 2, y: -1}) + assert Factors(27 * x / y**9) == Factors({27: 1, x: 1, y: -9}) + + +def test_Term(): + a = Term(4*x*y**2/z/t**3) + b = Term(2*x**3*y**5/t**3) + + assert a == Term(4, Factors({x: 1, y: 2}), Factors({z: 1, t: 3})) + assert b == Term(2, Factors({x: 3, y: 5}), Factors({t: 3})) + + assert a.as_expr() == 4*x*y**2/z/t**3 + assert b.as_expr() == 2*x**3*y**5/t**3 + + assert a.inv() == \ + Term(S.One/4, Factors({z: 1, t: 3}), Factors({x: 1, y: 2})) + assert b.inv() == Term(S.Half, Factors({t: 3}), Factors({x: 3, y: 5})) + + assert a.mul(b) == a*b == \ + Term(8, Factors({x: 4, y: 7}), Factors({z: 1, t: 6})) + assert a.quo(b) == a/b == Term(2, Factors({}), Factors({x: 2, y: 3, z: 1})) + + assert a.pow(3) == a**3 == \ + Term(64, Factors({x: 3, y: 6}), Factors({z: 3, t: 9})) + assert b.pow(3) == b**3 == Term(8, Factors({x: 9, y: 15}), Factors({t: 9})) + + assert a.pow(-3) == a**(-3) == \ + Term(S.One/64, Factors({z: 3, t: 9}), Factors({x: 3, y: 6})) + assert b.pow(-3) == b**(-3) == \ + Term(S.One/8, Factors({t: 9}), Factors({x: 9, y: 15})) + + assert a.gcd(b) == Term(2, Factors({x: 1, y: 2}), Factors({t: 3})) + assert a.lcm(b) == Term(4, Factors({x: 3, y: 5}), Factors({z: 1, t: 3})) + + a = Term(4*x*y**2/z/t**3) + b = Term(2*x**3*y**5*t**7) + + assert a.mul(b) == Term(8, Factors({x: 4, y: 7, t: 4}), Factors({z: 1})) + + assert Term((2*x + 2)**3) == Term(8, Factors({x + 1: 3}), Factors({})) + assert Term((2*x + 2)*(3*x + 6)**2) == \ + Term(18, Factors({x + 1: 1, x + 2: 2}), Factors({})) + + +def test_gcd_terms(): + f = 2*(x + 1)*(x + 4)/(5*x**2 + 5) + (2*x + 2)*(x + 5)/(x**2 + 1)/5 + \ + (2*x + 2)*(x + 6)/(5*x**2 + 5) + + assert _gcd_terms(f) == ((Rational(6, 5))*((1 + x)/(1 + x**2)), 5 + x, 1) + assert _gcd_terms(Add.make_args(f)) == \ + ((Rational(6, 5))*((1 + x)/(1 + x**2)), 5 + x, 1) + + newf = (Rational(6, 5))*((1 + x)*(5 + x)/(1 + x**2)) + assert gcd_terms(f) == newf + args = Add.make_args(f) + # non-Basic sequences of terms treated as terms of Add + assert gcd_terms(list(args)) == newf + assert gcd_terms(tuple(args)) == newf + assert gcd_terms(set(args)) == newf + # but a Basic sequence is treated as a container + assert gcd_terms(Tuple(*args)) != newf + assert gcd_terms(Basic(Tuple(S(1), 3*y + 3*x*y), Tuple(S(1), S(3)))) == \ + Basic(Tuple(S(1), 3*y*(x + 1)), Tuple(S(1), S(3))) + # but we shouldn't change keys of a dictionary or some may be lost + assert gcd_terms(Dict((x*(1 + y), S(2)), (x + x*y, y + x*y))) == \ + Dict({x*(y + 1): S(2), x + x*y: y*(1 + x)}) + + assert gcd_terms((2*x + 2)**3 + (2*x + 2)**2) == 4*(x + 1)**2*(2*x + 3) + + assert gcd_terms(0) == 0 + assert gcd_terms(1) == 1 + assert gcd_terms(x) == x + assert gcd_terms(2 + 2*x) == Mul(2, 1 + x, evaluate=False) + arg = x*(2*x + 4*y) + garg = 2*x*(x + 2*y) + assert gcd_terms(arg) == garg + assert gcd_terms(sin(arg)) == sin(garg) + + # issue 6139-like + alpha, alpha1, alpha2, alpha3 = symbols('alpha:4') + a = alpha**2 - alpha*x**2 + alpha + x**3 - x*(alpha + 1) + rep = (alpha, (1 + sqrt(5))/2 + alpha1*x + alpha2*x**2 + alpha3*x**3) + s = (a/(x - alpha)).subs(*rep).series(x, 0, 1) + assert simplify(collect(s, x)) == -sqrt(5)/2 - Rational(3, 2) + O(x) + + # issue 5917 + assert _gcd_terms([S.Zero, S.Zero]) == (0, 0, 1) + assert _gcd_terms([2*x + 4]) == (2, x + 2, 1) + + eq = x/(x + 1/x) + assert gcd_terms(eq, fraction=False) == eq + eq = x/2/y + 1/x/y + assert gcd_terms(eq, fraction=True, clear=True) == \ + (x**2 + 2)/(2*x*y) + assert gcd_terms(eq, fraction=True, clear=False) == \ + (x**2/2 + 1)/(x*y) + assert gcd_terms(eq, fraction=False, clear=True) == \ + (x + 2/x)/(2*y) + assert gcd_terms(eq, fraction=False, clear=False) == \ + (x/2 + 1/x)/y + + +def test_factor_terms(): + A = Symbol('A', commutative=False) + assert factor_terms(9*(x + x*y + 1) + (3*x + 3)**(2 + 2*x)) == \ + 9*x*y + 9*x + _keep_coeff(S(3), x + 1)**_keep_coeff(S(2), x + 1) + 9 + assert factor_terms(9*(x + x*y + 1) + (3)**(2 + 2*x)) == \ + _keep_coeff(S(9), 3**(2*x) + x*y + x + 1) + assert factor_terms(3**(2 + 2*x) + a*3**(2 + 2*x)) == \ + 9*3**(2*x)*(a + 1) + assert factor_terms(x + x*A) == \ + x*(1 + A) + assert factor_terms(sin(x + x*A)) == \ + sin(x*(1 + A)) + assert factor_terms((3*x + 3)**((2 + 2*x)/3)) == \ + _keep_coeff(S(3), x + 1)**_keep_coeff(Rational(2, 3), x + 1) + assert factor_terms(x + (x*y + x)**(3*x + 3)) == \ + x + (x*(y + 1))**_keep_coeff(S(3), x + 1) + assert factor_terms(a*(x + x*y) + b*(x*2 + y*x*2)) == \ + x*(a + 2*b)*(y + 1) + i = Integral(x, (x, 0, oo)) + assert factor_terms(i) == i + + assert factor_terms(x/2 + y) == x/2 + y + # fraction doesn't apply to integer denominators + assert factor_terms(x/2 + y, fraction=True) == x/2 + y + # clear *does* apply to the integer denominators + assert factor_terms(x/2 + y, clear=True) == Mul(S.Half, x + 2*y, evaluate=False) + + # check radical extraction + eq = sqrt(2) + sqrt(10) + assert factor_terms(eq) == eq + assert factor_terms(eq, radical=True) == sqrt(2)*(1 + sqrt(5)) + eq = root(-6, 3) + root(6, 3) + assert factor_terms(eq, radical=True) == 6**(S.One/3)*(1 + (-1)**(S.One/3)) + + eq = [x + x*y] + ans = [x*(y + 1)] + for c in [list, tuple, set]: + assert factor_terms(c(eq)) == c(ans) + assert factor_terms(Tuple(x + x*y)) == Tuple(x*(y + 1)) + assert factor_terms(Interval(0, 1)) == Interval(0, 1) + e = 1/sqrt(a/2 + 1) + assert factor_terms(e, clear=False) == 1/sqrt(a/2 + 1) + assert factor_terms(e, clear=True) == sqrt(2)/sqrt(a + 2) + + eq = x/(x + 1/x) + 1/(x**2 + 1) + assert factor_terms(eq, fraction=False) == eq + assert factor_terms(eq, fraction=True) == 1 + + assert factor_terms((1/(x**3 + x**2) + 2/x**2)*y) == \ + y*(2 + 1/(x + 1))/x**2 + + # if not True, then processesing for this in factor_terms is not necessary + assert gcd_terms(-x - y) == -x - y + assert factor_terms(-x - y) == Mul(-1, x + y, evaluate=False) + + # if not True, then "special" processesing in factor_terms is not necessary + assert gcd_terms(exp(Mul(-1, x + 1))) == exp(-x - 1) + e = exp(-x - 2) + x + assert factor_terms(e) == exp(Mul(-1, x + 2, evaluate=False)) + x + assert factor_terms(e, sign=False) == e + assert factor_terms(exp(-4*x - 2) - x) == -x + exp(Mul(-2, 2*x + 1, evaluate=False)) + + # sum/integral tests + for F in (Sum, Integral): + assert factor_terms(F(x, (y, 1, 10))) == x * F(1, (y, 1, 10)) + assert factor_terms(F(x, (y, 1, 10)) + x) == x * (1 + F(1, (y, 1, 10))) + assert factor_terms(F(x*y + x*y**2, (y, 1, 10))) == x*F(y*(y + 1), (y, 1, 10)) + + # expressions involving Pow terms with base 0 + assert factor_terms(0**(x - 2) - 1) == 0**(x - 2) - 1 + assert factor_terms(0**(x + 2) - 1) == 0**(x + 2) - 1 + assert factor_terms((0**(x + 2) - 1).subs(x,-2)) == 0 + + +def test_xreplace(): + e = Mul(2, 1 + x, evaluate=False) + assert e.xreplace({}) == e + assert e.xreplace({y: x}) == e + + +def test_factor_nc(): + x, y = symbols('x,y') + k = symbols('k', integer=True) + n, m, o = symbols('n,m,o', commutative=False) + + # mul and multinomial expansion is needed + from sympy.core.function import _mexpand + e = x*(1 + y)**2 + assert _mexpand(e) == x + x*2*y + x*y**2 + + def factor_nc_test(e): + ex = _mexpand(e) + assert ex.is_Add + f = factor_nc(ex) + assert not f.is_Add and _mexpand(f) == ex + + factor_nc_test(x*(1 + y)) + factor_nc_test(n*(x + 1)) + factor_nc_test(n*(x + m)) + factor_nc_test((x + m)*n) + factor_nc_test(n*m*(x*o + n*o*m)*n) + s = Sum(x, (x, 1, 2)) + factor_nc_test(x*(1 + s)) + factor_nc_test(x*(1 + s)*s) + factor_nc_test(x*(1 + sin(s))) + factor_nc_test((1 + n)**2) + + factor_nc_test((x + n)*(x + m)*(x + y)) + factor_nc_test(x*(n*m + 1)) + factor_nc_test(x*(n*m + x)) + factor_nc_test(x*(x*n*m + 1)) + factor_nc_test(n*(m/x + o)) + factor_nc_test(m*(n + o/2)) + factor_nc_test(x*n*(x*m + 1)) + factor_nc_test(x*(m*n + x*n*m)) + factor_nc_test(n*(1 - m)*n**2) + + factor_nc_test((n + m)**2) + factor_nc_test((n - m)*(n + m)**2) + factor_nc_test((n + m)**2*(n - m)) + factor_nc_test((m - n)*(n + m)**2*(n - m)) + + assert factor_nc(n*(n + n*m)) == n**2*(1 + m) + assert factor_nc(m*(m*n + n*m*n**2)) == m*(m + n*m*n)*n + eq = m*sin(n) - sin(n)*m + assert factor_nc(eq) == eq + + # for coverage: + from sympy.physics.secondquant import Commutator + from sympy.polys.polytools import factor + eq = 1 + x*Commutator(m, n) + assert factor_nc(eq) == eq + eq = x*Commutator(m, n) + x*Commutator(m, o)*Commutator(m, n) + assert factor(eq) == x*(1 + Commutator(m, o))*Commutator(m, n) + + # issue 6534 + assert (2*n + 2*m).factor() == 2*(n + m) + + # issue 6701 + _n = symbols('nz', zero=False, commutative=False) + assert factor_nc(_n**k + _n**(k + 1)) == _n**k*(1 + _n) + assert factor_nc((m*n)**k + (m*n)**(k + 1)) == (1 + m*n)*(m*n)**k + + # issue 6918 + assert factor_nc(-n*(2*x**2 + 2*x)) == -2*n*x*(x + 1) + + +def test_issue_6360(): + a, b = symbols("a b") + apb = a + b + eq = apb + apb**2*(-2*a - 2*b) + assert factor_terms(sub_pre(eq)) == a + b - 2*(a + b)**3 + + +def test_issue_7903(): + a = symbols(r'a', real=True) + t = exp(I*cos(a)) + exp(-I*sin(a)) + assert t.simplify() + +def test_issue_8263(): + F, G = symbols('F, G', commutative=False, cls=Function) + x, y = symbols('x, y') + expr, dummies, _ = _mask_nc(F(x)*G(y) - G(y)*F(x)) + for v in dummies.values(): + assert not v.is_commutative + assert not expr.is_zero + +def test_monotonic_sign(): + F = _monotonic_sign + x = symbols('x') + assert F(x) is None + assert F(-x) is None + assert F(Dummy(prime=True)) == 2 + assert F(Dummy(prime=True, odd=True)) == 3 + assert F(Dummy(composite=True)) == 4 + assert F(Dummy(composite=True, odd=True)) == 9 + assert F(Dummy(positive=True, integer=True)) == 1 + assert F(Dummy(positive=True, even=True)) == 2 + assert F(Dummy(positive=True, even=True, prime=False)) == 4 + assert F(Dummy(negative=True, integer=True)) == -1 + assert F(Dummy(negative=True, even=True)) == -2 + assert F(Dummy(zero=True)) == 0 + assert F(Dummy(nonnegative=True)) == 0 + assert F(Dummy(nonpositive=True)) == 0 + + assert F(Dummy(positive=True) + 1).is_positive + assert F(Dummy(positive=True, integer=True) - 1).is_nonnegative + assert F(Dummy(positive=True) - 1) is None + assert F(Dummy(negative=True) + 1) is None + assert F(Dummy(negative=True, integer=True) - 1).is_nonpositive + assert F(Dummy(negative=True) - 1).is_negative + assert F(-Dummy(positive=True) + 1) is None + assert F(-Dummy(positive=True, integer=True) - 1).is_negative + assert F(-Dummy(positive=True) - 1).is_negative + assert F(-Dummy(negative=True) + 1).is_positive + assert F(-Dummy(negative=True, integer=True) - 1).is_nonnegative + assert F(-Dummy(negative=True) - 1) is None + x = Dummy(negative=True) + assert F(x**3).is_nonpositive + assert F(x**3 + log(2)*x - 1).is_negative + x = Dummy(positive=True) + assert F(-x**3).is_nonpositive + + p = Dummy(positive=True) + assert F(1/p).is_positive + assert F(p/(p + 1)).is_positive + p = Dummy(nonnegative=True) + assert F(p/(p + 1)).is_nonnegative + p = Dummy(positive=True) + assert F(-1/p).is_negative + p = Dummy(nonpositive=True) + assert F(p/(-p + 1)).is_nonpositive + + p = Dummy(positive=True, integer=True) + q = Dummy(positive=True, integer=True) + assert F(-2/p/q).is_negative + assert F(-2/(p - 1)/q) is None + + assert F((p - 1)*q + 1).is_positive + assert F(-(p - 1)*q - 1).is_negative + +def test_issue_17256(): + from sympy.sets.fancysets import Range + x = Symbol('x') + s1 = Sum(x + 1, (x, 1, 9)) + s2 = Sum(x + 1, (x, Range(1, 10))) + a = Symbol('a') + r1 = s1.xreplace({x:a}) + r2 = s2.xreplace({x:a}) + + assert r1.doit() == r2.doit() + s1 = Sum(x + 1, (x, 0, 9)) + s2 = Sum(x + 1, (x, Range(10))) + a = Symbol('a') + r1 = s1.xreplace({x:a}) + r2 = s2.xreplace({x:a}) + assert r1 == r2 + +def test_issue_21623(): + from sympy.matrices.expressions.matexpr import MatrixSymbol + M = MatrixSymbol('X', 2, 2) + assert gcd_terms(M[0,0], 1) == M[0,0] diff --git a/lib/python3.12/site-packages/sympy/core/tests/test_facts.py b/lib/python3.12/site-packages/sympy/core/tests/test_facts.py new file mode 100644 index 0000000000000000000000000000000000000000..7ca04877d0bdaf8124258ea1d25a10bcfa0f5f3a --- /dev/null +++ b/lib/python3.12/site-packages/sympy/core/tests/test_facts.py @@ -0,0 +1,312 @@ +from sympy.core.facts import (deduce_alpha_implications, + apply_beta_to_alpha_route, rules_2prereq, FactRules, FactKB) +from sympy.core.logic import And, Not +from sympy.testing.pytest import raises + +T = True +F = False +U = None + + +def test_deduce_alpha_implications(): + def D(i): + I = deduce_alpha_implications(i) + P = rules_2prereq({ + (k, True): {(v, True) for v in S} for k, S in I.items()}) + return I, P + + # transitivity + I, P = D([('a', 'b'), ('b', 'c')]) + assert I == {'a': {'b', 'c'}, 'b': {'c'}, Not('b'): + {Not('a')}, Not('c'): {Not('a'), Not('b')}} + assert P == {'a': {'b', 'c'}, 'b': {'a', 'c'}, 'c': {'a', 'b'}} + + # Duplicate entry + I, P = D([('a', 'b'), ('b', 'c'), ('b', 'c')]) + assert I == {'a': {'b', 'c'}, 'b': {'c'}, Not('b'): {Not('a')}, Not('c'): {Not('a'), Not('b')}} + assert P == {'a': {'b', 'c'}, 'b': {'a', 'c'}, 'c': {'a', 'b'}} + + # see if it is tolerant to cycles + assert D([('a', 'a'), ('a', 'a')]) == ({}, {}) + assert D([('a', 'b'), ('b', 'a')]) == ( + {'a': {'b'}, 'b': {'a'}, Not('a'): {Not('b')}, Not('b'): {Not('a')}}, + {'a': {'b'}, 'b': {'a'}}) + + # see if it catches inconsistency + raises(ValueError, lambda: D([('a', Not('a'))])) + raises(ValueError, lambda: D([('a', 'b'), ('b', Not('a'))])) + raises(ValueError, lambda: D([('a', 'b'), ('b', 'c'), ('b', 'na'), + ('na', Not('a'))])) + + # see if it handles implications with negations + I, P = D([('a', Not('b')), ('c', 'b')]) + assert I == {'a': {Not('b'), Not('c')}, 'b': {Not('a')}, 'c': {'b', Not('a')}, Not('b'): {Not('c')}} + assert P == {'a': {'b', 'c'}, 'b': {'a', 'c'}, 'c': {'a', 'b'}} + I, P = D([(Not('a'), 'b'), ('a', 'c')]) + assert I == {'a': {'c'}, Not('a'): {'b'}, Not('b'): {'a', + 'c'}, Not('c'): {Not('a'), 'b'},} + assert P == {'a': {'b', 'c'}, 'b': {'a', 'c'}, 'c': {'a', 'b'}} + + + # Long deductions + I, P = D([('a', 'b'), ('b', 'c'), ('c', 'd'), ('d', 'e')]) + assert I == {'a': {'b', 'c', 'd', 'e'}, 'b': {'c', 'd', 'e'}, + 'c': {'d', 'e'}, 'd': {'e'}, Not('b'): {Not('a')}, + Not('c'): {Not('a'), Not('b')}, Not('d'): {Not('a'), Not('b'), + Not('c')}, Not('e'): {Not('a'), Not('b'), Not('c'), Not('d')}} + assert P == {'a': {'b', 'c', 'd', 'e'}, 'b': {'a', 'c', 'd', + 'e'}, 'c': {'a', 'b', 'd', 'e'}, 'd': {'a', 'b', 'c', 'e'}, + 'e': {'a', 'b', 'c', 'd'}} + + # something related to real-world + I, P = D([('rat', 'real'), ('int', 'rat')]) + + assert I == {'int': {'rat', 'real'}, 'rat': {'real'}, + Not('real'): {Not('rat'), Not('int')}, Not('rat'): {Not('int')}} + assert P == {'rat': {'int', 'real'}, 'real': {'int', 'rat'}, + 'int': {'rat', 'real'}} + + +# TODO move me to appropriate place +def test_apply_beta_to_alpha_route(): + APPLY = apply_beta_to_alpha_route + + # indicates empty alpha-chain with attached beta-rule #bidx + def Q(bidx): + return (set(), [bidx]) + + # x -> a &(a,b) -> x -- x -> a + A = {'x': {'a'}} + B = [(And('a', 'b'), 'x')] + assert APPLY(A, B) == {'x': ({'a'}, []), 'a': Q(0), 'b': Q(0)} + + # x -> a &(a,!x) -> b -- x -> a + A = {'x': {'a'}} + B = [(And('a', Not('x')), 'b')] + assert APPLY(A, B) == {'x': ({'a'}, []), Not('x'): Q(0), 'a': Q(0)} + + # x -> a b &(a,b) -> c -- x -> a b c + A = {'x': {'a', 'b'}} + B = [(And('a', 'b'), 'c')] + assert APPLY(A, B) == \ + {'x': ({'a', 'b', 'c'}, []), 'a': Q(0), 'b': Q(0)} + + # x -> a &(a,b) -> y -- x -> a [#0] + A = {'x': {'a'}} + B = [(And('a', 'b'), 'y')] + assert APPLY(A, B) == {'x': ({'a'}, [0]), 'a': Q(0), 'b': Q(0)} + + # x -> a b c &(a,b) -> c -- x -> a b c + A = {'x': {'a', 'b', 'c'}} + B = [(And('a', 'b'), 'c')] + assert APPLY(A, B) == \ + {'x': ({'a', 'b', 'c'}, []), 'a': Q(0), 'b': Q(0)} + + # x -> a b &(a,b,c) -> y -- x -> a b [#0] + A = {'x': {'a', 'b'}} + B = [(And('a', 'b', 'c'), 'y')] + assert APPLY(A, B) == \ + {'x': ({'a', 'b'}, [0]), 'a': Q(0), 'b': Q(0), 'c': Q(0)} + + # x -> a b &(a,b) -> c -- x -> a b c d + # c -> d c -> d + A = {'x': {'a', 'b'}, 'c': {'d'}} + B = [(And('a', 'b'), 'c')] + assert APPLY(A, B) == {'x': ({'a', 'b', 'c', 'd'}, []), + 'c': ({'d'}, []), 'a': Q(0), 'b': Q(0)} + + # x -> a b &(a,b) -> c -- x -> a b c d e + # c -> d &(c,d) -> e c -> d e + A = {'x': {'a', 'b'}, 'c': {'d'}} + B = [(And('a', 'b'), 'c'), (And('c', 'd'), 'e')] + assert APPLY(A, B) == {'x': ({'a', 'b', 'c', 'd', 'e'}, []), + 'c': ({'d', 'e'}, []), 'a': Q(0), 'b': Q(0), 'd': Q(1)} + + # x -> a b &(a,y) -> z -- x -> a b y z + # &(a,b) -> y + A = {'x': {'a', 'b'}} + B = [(And('a', 'y'), 'z'), (And('a', 'b'), 'y')] + assert APPLY(A, B) == {'x': ({'a', 'b', 'y', 'z'}, []), + 'a': (set(), [0, 1]), 'y': Q(0), 'b': Q(1)} + + # x -> a b &(a,!b) -> c -- x -> a b + A = {'x': {'a', 'b'}} + B = [(And('a', Not('b')), 'c')] + assert APPLY(A, B) == \ + {'x': ({'a', 'b'}, []), 'a': Q(0), Not('b'): Q(0)} + + # !x -> !a !b &(!a,b) -> c -- !x -> !a !b + A = {Not('x'): {Not('a'), Not('b')}} + B = [(And(Not('a'), 'b'), 'c')] + assert APPLY(A, B) == \ + {Not('x'): ({Not('a'), Not('b')}, []), Not('a'): Q(0), 'b': Q(0)} + + # x -> a b &(b,c) -> !a -- x -> a b + A = {'x': {'a', 'b'}} + B = [(And('b', 'c'), Not('a'))] + assert APPLY(A, B) == {'x': ({'a', 'b'}, []), 'b': Q(0), 'c': Q(0)} + + # x -> a b &(a, b) -> c -- x -> a b c p + # c -> p a + A = {'x': {'a', 'b'}, 'c': {'p', 'a'}} + B = [(And('a', 'b'), 'c')] + assert APPLY(A, B) == {'x': ({'a', 'b', 'c', 'p'}, []), + 'c': ({'p', 'a'}, []), 'a': Q(0), 'b': Q(0)} + + +def test_FactRules_parse(): + f = FactRules('a -> b') + assert f.prereq == {'b': {'a'}, 'a': {'b'}} + + f = FactRules('a -> !b') + assert f.prereq == {'b': {'a'}, 'a': {'b'}} + + f = FactRules('!a -> b') + assert f.prereq == {'b': {'a'}, 'a': {'b'}} + + f = FactRules('!a -> !b') + assert f.prereq == {'b': {'a'}, 'a': {'b'}} + + f = FactRules('!z == nz') + assert f.prereq == {'z': {'nz'}, 'nz': {'z'}} + + +def test_FactRules_parse2(): + raises(ValueError, lambda: FactRules('a -> !a')) + + +def test_FactRules_deduce(): + f = FactRules(['a -> b', 'b -> c', 'b -> d', 'c -> e']) + + def D(facts): + kb = FactKB(f) + kb.deduce_all_facts(facts) + return kb + + assert D({'a': T}) == {'a': T, 'b': T, 'c': T, 'd': T, 'e': T} + assert D({'b': T}) == { 'b': T, 'c': T, 'd': T, 'e': T} + assert D({'c': T}) == { 'c': T, 'e': T} + assert D({'d': T}) == { 'd': T } + assert D({'e': T}) == { 'e': T} + + assert D({'a': F}) == {'a': F } + assert D({'b': F}) == {'a': F, 'b': F } + assert D({'c': F}) == {'a': F, 'b': F, 'c': F } + assert D({'d': F}) == {'a': F, 'b': F, 'd': F } + + assert D({'a': U}) == {'a': U} # XXX ok? + + +def test_FactRules_deduce2(): + # pos/neg/zero, but the rules are not sufficient to derive all relations + f = FactRules(['pos -> !neg', 'pos -> !z']) + + def D(facts): + kb = FactKB(f) + kb.deduce_all_facts(facts) + return kb + + assert D({'pos': T}) == {'pos': T, 'neg': F, 'z': F} + assert D({'pos': F}) == {'pos': F } + assert D({'neg': T}) == {'pos': F, 'neg': T } + assert D({'neg': F}) == { 'neg': F } + assert D({'z': T}) == {'pos': F, 'z': T} + assert D({'z': F}) == { 'z': F} + + # pos/neg/zero. rules are sufficient to derive all relations + f = FactRules(['pos -> !neg', 'neg -> !pos', 'pos -> !z', 'neg -> !z']) + + assert D({'pos': T}) == {'pos': T, 'neg': F, 'z': F} + assert D({'pos': F}) == {'pos': F } + assert D({'neg': T}) == {'pos': F, 'neg': T, 'z': F} + assert D({'neg': F}) == { 'neg': F } + assert D({'z': T}) == {'pos': F, 'neg': F, 'z': T} + assert D({'z': F}) == { 'z': F} + + +def test_FactRules_deduce_multiple(): + # deduction that involves _several_ starting points + f = FactRules(['real == pos | npos']) + + def D(facts): + kb = FactKB(f) + kb.deduce_all_facts(facts) + return kb + + assert D({'real': T}) == {'real': T} + assert D({'real': F}) == {'real': F, 'pos': F, 'npos': F} + assert D({'pos': T}) == {'real': T, 'pos': T} + assert D({'npos': T}) == {'real': T, 'npos': T} + + # --- key tests below --- + assert D({'pos': F, 'npos': F}) == {'real': F, 'pos': F, 'npos': F} + assert D({'real': T, 'pos': F}) == {'real': T, 'pos': F, 'npos': T} + assert D({'real': T, 'npos': F}) == {'real': T, 'pos': T, 'npos': F} + + assert D({'pos': T, 'npos': F}) == {'real': T, 'pos': T, 'npos': F} + assert D({'pos': F, 'npos': T}) == {'real': T, 'pos': F, 'npos': T} + + +def test_FactRules_deduce_multiple2(): + f = FactRules(['real == neg | zero | pos']) + + def D(facts): + kb = FactKB(f) + kb.deduce_all_facts(facts) + return kb + + assert D({'real': T}) == {'real': T} + assert D({'real': F}) == {'real': F, 'neg': F, 'zero': F, 'pos': F} + assert D({'neg': T}) == {'real': T, 'neg': T} + assert D({'zero': T}) == {'real': T, 'zero': T} + assert D({'pos': T}) == {'real': T, 'pos': T} + + # --- key tests below --- + assert D({'neg': F, 'zero': F, 'pos': F}) == {'real': F, 'neg': F, + 'zero': F, 'pos': F} + assert D({'real': T, 'neg': F}) == {'real': T, 'neg': F} + assert D({'real': T, 'zero': F}) == {'real': T, 'zero': F} + assert D({'real': T, 'pos': F}) == {'real': T, 'pos': F} + + assert D({'real': T, 'zero': F, 'pos': F}) == {'real': T, + 'neg': T, 'zero': F, 'pos': F} + assert D({'real': T, 'neg': F, 'pos': F}) == {'real': T, + 'neg': F, 'zero': T, 'pos': F} + assert D({'real': T, 'neg': F, 'zero': F }) == {'real': T, + 'neg': F, 'zero': F, 'pos': T} + + assert D({'neg': T, 'zero': F, 'pos': F}) == {'real': T, 'neg': T, + 'zero': F, 'pos': F} + assert D({'neg': F, 'zero': T, 'pos': F}) == {'real': T, 'neg': F, + 'zero': T, 'pos': F} + assert D({'neg': F, 'zero': F, 'pos': T}) == {'real': T, 'neg': F, + 'zero': F, 'pos': T} + + +def test_FactRules_deduce_base(): + # deduction that starts from base + + f = FactRules(['real == neg | zero | pos', + 'neg -> real & !zero & !pos', + 'pos -> real & !zero & !neg']) + base = FactKB(f) + + base.deduce_all_facts({'real': T, 'neg': F}) + assert base == {'real': T, 'neg': F} + + base.deduce_all_facts({'zero': F}) + assert base == {'real': T, 'neg': F, 'zero': F, 'pos': T} + + +def test_FactRules_deduce_staticext(): + # verify that static beta-extensions deduction takes place + f = FactRules(['real == neg | zero | pos', + 'neg -> real & !zero & !pos', + 'pos -> real & !zero & !neg', + 'nneg == real & !neg', + 'npos == real & !pos']) + + assert ('npos', True) in f.full_implications[('neg', True)] + assert ('nneg', True) in f.full_implications[('pos', True)] + assert ('nneg', True) in f.full_implications[('zero', True)] + assert ('npos', True) in f.full_implications[('zero', True)] diff --git a/lib/python3.12/site-packages/sympy/core/tests/test_kind.py b/lib/python3.12/site-packages/sympy/core/tests/test_kind.py new file mode 100644 index 0000000000000000000000000000000000000000..cbfdffb9304b49488756752ca198fd4067087437 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/core/tests/test_kind.py @@ -0,0 +1,57 @@ +from sympy.core.add import Add +from sympy.core.kind import NumberKind, UndefinedKind +from sympy.core.mul import Mul +from sympy.core.numbers import pi, zoo, I, AlgebraicNumber +from sympy.core.singleton import S +from sympy.core.symbol import Symbol +from sympy.integrals.integrals import Integral +from sympy.core.function import Derivative +from sympy.matrices import (Matrix, SparseMatrix, ImmutableMatrix, + ImmutableSparseMatrix, MatrixSymbol, MatrixKind, MatMul) + +comm_x = Symbol('x') +noncomm_x = Symbol('x', commutative=False) + +def test_NumberKind(): + assert S.One.kind is NumberKind + assert pi.kind is NumberKind + assert S.NaN.kind is NumberKind + assert zoo.kind is NumberKind + assert I.kind is NumberKind + assert AlgebraicNumber(1).kind is NumberKind + +def test_Add_kind(): + assert Add(2, 3, evaluate=False).kind is NumberKind + assert Add(2,comm_x).kind is NumberKind + assert Add(2,noncomm_x).kind is UndefinedKind + +def test_mul_kind(): + assert Mul(2,comm_x, evaluate=False).kind is NumberKind + assert Mul(2,3, evaluate=False).kind is NumberKind + assert Mul(noncomm_x,2, evaluate=False).kind is UndefinedKind + assert Mul(2,noncomm_x, evaluate=False).kind is UndefinedKind + +def test_Symbol_kind(): + assert comm_x.kind is NumberKind + assert noncomm_x.kind is UndefinedKind + +def test_Integral_kind(): + A = MatrixSymbol('A', 2,2) + assert Integral(comm_x, comm_x).kind is NumberKind + assert Integral(A, comm_x).kind is MatrixKind(NumberKind) + +def test_Derivative_kind(): + A = MatrixSymbol('A', 2,2) + assert Derivative(comm_x, comm_x).kind is NumberKind + assert Derivative(A, comm_x).kind is MatrixKind(NumberKind) + +def test_Matrix_kind(): + classes = (Matrix, SparseMatrix, ImmutableMatrix, ImmutableSparseMatrix) + for cls in classes: + m = cls.zeros(3, 2) + assert m.kind is MatrixKind(NumberKind) + +def test_MatMul_kind(): + M = Matrix([[1,2],[3,4]]) + assert MatMul(2, M).kind is MatrixKind(NumberKind) + assert MatMul(comm_x, M).kind is MatrixKind(NumberKind) diff --git a/lib/python3.12/site-packages/sympy/core/tests/test_match.py b/lib/python3.12/site-packages/sympy/core/tests/test_match.py new file mode 100644 index 0000000000000000000000000000000000000000..caa25e3c9dc1233e4566484f38a6b002ed4e72bd --- /dev/null +++ b/lib/python3.12/site-packages/sympy/core/tests/test_match.py @@ -0,0 +1,766 @@ +from sympy import abc +from sympy.concrete.summations import Sum +from sympy.core.add import Add +from sympy.core.function import (Derivative, Function, diff) +from sympy.core.mul import Mul +from sympy.core.numbers import (Float, I, Integer, Rational, oo, pi) +from sympy.core.singleton import S +from sympy.core.symbol import (Symbol, Wild, symbols) +from sympy.functions.elementary.exponential import (exp, log) +from sympy.functions.elementary.miscellaneous import sqrt +from sympy.functions.elementary.trigonometric import (cos, sin) +from sympy.functions.special.hyper import meijerg +from sympy.polys.polytools import Poly +from sympy.simplify.radsimp import collect +from sympy.simplify.simplify import signsimp + +from sympy.testing.pytest import XFAIL + + +def test_symbol(): + x = Symbol('x') + a, b, c, p, q = map(Wild, 'abcpq') + + e = x + assert e.match(x) == {} + assert e.matches(x) == {} + assert e.match(a) == {a: x} + + e = Rational(5) + assert e.match(c) == {c: 5} + assert e.match(e) == {} + assert e.match(e + 1) is None + + +def test_add(): + x, y, a, b, c = map(Symbol, 'xyabc') + p, q, r = map(Wild, 'pqr') + + e = a + b + assert e.match(p + b) == {p: a} + assert e.match(p + a) == {p: b} + + e = 1 + b + assert e.match(p + b) == {p: 1} + + e = a + b + c + assert e.match(a + p + c) == {p: b} + assert e.match(b + p + c) == {p: a} + + e = a + b + c + x + assert e.match(a + p + x + c) == {p: b} + assert e.match(b + p + c + x) == {p: a} + assert e.match(b) is None + assert e.match(b + p) == {p: a + c + x} + assert e.match(a + p + c) == {p: b + x} + assert e.match(b + p + c) == {p: a + x} + + e = 4*x + 5 + assert e.match(4*x + p) == {p: 5} + assert e.match(3*x + p) == {p: x + 5} + assert e.match(p*x + 5) == {p: 4} + + +def test_power(): + x, y, a, b, c = map(Symbol, 'xyabc') + p, q, r = map(Wild, 'pqr') + + e = (x + y)**a + assert e.match(p**q) == {p: x + y, q: a} + assert e.match(p**p) is None + + e = (x + y)**(x + y) + assert e.match(p**p) == {p: x + y} + assert e.match(p**q) == {p: x + y, q: x + y} + + e = (2*x)**2 + assert e.match(p*q**r) == {p: 4, q: x, r: 2} + + e = Integer(1) + assert e.match(x**p) == {p: 0} + + +def test_match_exclude(): + x = Symbol('x') + y = Symbol('y') + p = Wild("p") + q = Wild("q") + r = Wild("r") + + e = Rational(6) + assert e.match(2*p) == {p: 3} + + e = 3/(4*x + 5) + assert e.match(3/(p*x + q)) == {p: 4, q: 5} + + e = 3/(4*x + 5) + assert e.match(p/(q*x + r)) == {p: 3, q: 4, r: 5} + + e = 2/(x + 1) + assert e.match(p/(q*x + r)) == {p: 2, q: 1, r: 1} + + e = 1/(x + 1) + assert e.match(p/(q*x + r)) == {p: 1, q: 1, r: 1} + + e = 4*x + 5 + assert e.match(p*x + q) == {p: 4, q: 5} + + e = 4*x + 5*y + 6 + assert e.match(p*x + q*y + r) == {p: 4, q: 5, r: 6} + + a = Wild('a', exclude=[x]) + + e = 3*x + assert e.match(p*x) == {p: 3} + assert e.match(a*x) == {a: 3} + + e = 3*x**2 + assert e.match(p*x) == {p: 3*x} + assert e.match(a*x) is None + + e = 3*x + 3 + 6/x + assert e.match(p*x**2 + p*x + 2*p) == {p: 3/x} + assert e.match(a*x**2 + a*x + 2*a) is None + + +def test_mul(): + x, y, a, b, c = map(Symbol, 'xyabc') + p, q = map(Wild, 'pq') + + e = 4*x + assert e.match(p*x) == {p: 4} + assert e.match(p*y) is None + assert e.match(e + p*y) == {p: 0} + + e = a*x*b*c + assert e.match(p*x) == {p: a*b*c} + assert e.match(c*p*x) == {p: a*b} + + e = (a + b)*(a + c) + assert e.match((p + b)*(p + c)) == {p: a} + + e = x + assert e.match(p*x) == {p: 1} + + e = exp(x) + assert e.match(x**p*exp(x*q)) == {p: 0, q: 1} + + e = I*Poly(x, x) + assert e.match(I*p) == {p: x} + + +def test_mul_noncommutative(): + x, y = symbols('x y') + A, B, C = symbols('A B C', commutative=False) + u, v = symbols('u v', cls=Wild) + w, z = symbols('w z', cls=Wild, commutative=False) + + assert (u*v).matches(x) in ({v: x, u: 1}, {u: x, v: 1}) + assert (u*v).matches(x*y) in ({v: y, u: x}, {u: y, v: x}) + assert (u*v).matches(A) is None + assert (u*v).matches(A*B) is None + assert (u*v).matches(x*A) is None + assert (u*v).matches(x*y*A) is None + assert (u*v).matches(x*A*B) is None + assert (u*v).matches(x*y*A*B) is None + + assert (v*w).matches(x) is None + assert (v*w).matches(x*y) is None + assert (v*w).matches(A) == {w: A, v: 1} + assert (v*w).matches(A*B) == {w: A*B, v: 1} + assert (v*w).matches(x*A) == {w: A, v: x} + assert (v*w).matches(x*y*A) == {w: A, v: x*y} + assert (v*w).matches(x*A*B) == {w: A*B, v: x} + assert (v*w).matches(x*y*A*B) == {w: A*B, v: x*y} + + assert (v*w).matches(-x) is None + assert (v*w).matches(-x*y) is None + assert (v*w).matches(-A) == {w: A, v: -1} + assert (v*w).matches(-A*B) == {w: A*B, v: -1} + assert (v*w).matches(-x*A) == {w: A, v: -x} + assert (v*w).matches(-x*y*A) == {w: A, v: -x*y} + assert (v*w).matches(-x*A*B) == {w: A*B, v: -x} + assert (v*w).matches(-x*y*A*B) == {w: A*B, v: -x*y} + + assert (w*z).matches(x) is None + assert (w*z).matches(x*y) is None + assert (w*z).matches(A) is None + assert (w*z).matches(A*B) == {w: A, z: B} + assert (w*z).matches(B*A) == {w: B, z: A} + assert (w*z).matches(A*B*C) in [{w: A, z: B*C}, {w: A*B, z: C}] + assert (w*z).matches(x*A) is None + assert (w*z).matches(x*y*A) is None + assert (w*z).matches(x*A*B) is None + assert (w*z).matches(x*y*A*B) is None + + assert (w*A).matches(A) is None + assert (A*w*B).matches(A*B) is None + + assert (u*w*z).matches(x) is None + assert (u*w*z).matches(x*y) is None + assert (u*w*z).matches(A) is None + assert (u*w*z).matches(A*B) == {u: 1, w: A, z: B} + assert (u*w*z).matches(B*A) == {u: 1, w: B, z: A} + assert (u*w*z).matches(x*A) is None + assert (u*w*z).matches(x*y*A) is None + assert (u*w*z).matches(x*A*B) == {u: x, w: A, z: B} + assert (u*w*z).matches(x*B*A) == {u: x, w: B, z: A} + assert (u*w*z).matches(x*y*A*B) == {u: x*y, w: A, z: B} + assert (u*w*z).matches(x*y*B*A) == {u: x*y, w: B, z: A} + + assert (u*A).matches(x*A) == {u: x} + assert (u*A).matches(x*A*B) is None + assert (u*B).matches(x*A) is None + assert (u*A*B).matches(x*A*B) == {u: x} + assert (u*A*B).matches(x*B*A) is None + assert (u*A*B).matches(x*A) is None + + assert (u*w*A).matches(x*A*B) is None + assert (u*w*B).matches(x*A*B) == {u: x, w: A} + + assert (u*v*A*B).matches(x*A*B) in [{u: x, v: 1}, {v: x, u: 1}] + assert (u*v*A*B).matches(x*B*A) is None + assert (u*v*A*B).matches(u*v*A*C) is None + + +def test_mul_noncommutative_mismatch(): + A, B, C = symbols('A B C', commutative=False) + w = symbols('w', cls=Wild, commutative=False) + + assert (w*B*w).matches(A*B*A) == {w: A} + assert (w*B*w).matches(A*C*B*A*C) == {w: A*C} + assert (w*B*w).matches(A*C*B*A*B) is None + assert (w*B*w).matches(A*B*C) is None + assert (w*w*C).matches(A*B*C) is None + + +def test_mul_noncommutative_pow(): + A, B, C = symbols('A B C', commutative=False) + w = symbols('w', cls=Wild, commutative=False) + + assert (A*B*w).matches(A*B**2) == {w: B} + assert (A*(B**2)*w*(B**3)).matches(A*B**8) == {w: B**3} + assert (A*B*w*C).matches(A*(B**4)*C) == {w: B**3} + + assert (A*B*(w**(-1))).matches(A*B*(C**(-1))) == {w: C} + assert (A*(B*w)**(-1)*C).matches(A*(B*C)**(-1)*C) == {w: C} + + assert ((w**2)*B*C).matches((A**2)*B*C) == {w: A} + assert ((w**2)*B*(w**3)).matches((A**2)*B*(A**3)) == {w: A} + assert ((w**2)*B*(w**4)).matches((A**2)*B*(A**2)) is None + +def test_complex(): + a, b, c = map(Symbol, 'abc') + x, y = map(Wild, 'xy') + + assert (1 + I).match(x + I) == {x: 1} + assert (a + I).match(x + I) == {x: a} + assert (2*I).match(x*I) == {x: 2} + assert (a*I).match(x*I) == {x: a} + assert (a*I).match(x*y) == {x: I, y: a} + assert (2*I).match(x*y) == {x: 2, y: I} + assert (a + b*I).match(x + y*I) == {x: a, y: b} + + +def test_functions(): + from sympy.core.function import WildFunction + x = Symbol('x') + g = WildFunction('g') + p = Wild('p') + q = Wild('q') + + f = cos(5*x) + notf = x + assert f.match(p*cos(q*x)) == {p: 1, q: 5} + assert f.match(p*g) == {p: 1, g: cos(5*x)} + assert notf.match(g) is None + + +@XFAIL +def test_functions_X1(): + from sympy.core.function import WildFunction + x = Symbol('x') + g = WildFunction('g') + p = Wild('p') + q = Wild('q') + + f = cos(5*x) + assert f.match(p*g(q*x)) == {p: 1, g: cos, q: 5} + + +def test_interface(): + x, y = map(Symbol, 'xy') + p, q = map(Wild, 'pq') + + assert (x + 1).match(p + 1) == {p: x} + assert (x*3).match(p*3) == {p: x} + assert (x**3).match(p**3) == {p: x} + assert (x*cos(y)).match(p*cos(q)) == {p: x, q: y} + + assert (x*y).match(p*q) in [{p:x, q:y}, {p:y, q:x}] + assert (x + y).match(p + q) in [{p:x, q:y}, {p:y, q:x}] + assert (x*y + 1).match(p*q) in [{p:1, q:1 + x*y}, {p:1 + x*y, q:1}] + + +def test_derivative1(): + x, y = map(Symbol, 'xy') + p, q = map(Wild, 'pq') + + f = Function('f', nargs=1) + fd = Derivative(f(x), x) + + assert fd.match(p) == {p: fd} + assert (fd + 1).match(p + 1) == {p: fd} + assert (fd).match(fd) == {} + assert (3*fd).match(p*fd) is not None + assert (3*fd - 1).match(p*fd + q) == {p: 3, q: -1} + + +def test_derivative_bug1(): + f = Function("f") + x = Symbol("x") + a = Wild("a", exclude=[f, x]) + b = Wild("b", exclude=[f]) + pattern = a * Derivative(f(x), x, x) + b + expr = Derivative(f(x), x) + x**2 + d1 = {b: x**2} + d2 = pattern.xreplace(d1).matches(expr, d1) + assert d2 is None + + +def test_derivative2(): + f = Function("f") + x = Symbol("x") + a = Wild("a", exclude=[f, x]) + b = Wild("b", exclude=[f]) + e = Derivative(f(x), x) + assert e.match(Derivative(f(x), x)) == {} + assert e.match(Derivative(f(x), x, x)) is None + e = Derivative(f(x), x, x) + assert e.match(Derivative(f(x), x)) is None + assert e.match(Derivative(f(x), x, x)) == {} + e = Derivative(f(x), x) + x**2 + assert e.match(a*Derivative(f(x), x) + b) == {a: 1, b: x**2} + assert e.match(a*Derivative(f(x), x, x) + b) is None + e = Derivative(f(x), x, x) + x**2 + assert e.match(a*Derivative(f(x), x) + b) is None + assert e.match(a*Derivative(f(x), x, x) + b) == {a: 1, b: x**2} + + +def test_match_deriv_bug1(): + n = Function('n') + l = Function('l') + + x = Symbol('x') + p = Wild('p') + + e = diff(l(x), x)/x - diff(diff(n(x), x), x)/2 - \ + diff(n(x), x)**2/4 + diff(n(x), x)*diff(l(x), x)/4 + e = e.subs(n(x), -l(x)).doit() + t = x*exp(-l(x)) + t2 = t.diff(x, x)/t + assert e.match( (p*t2).expand() ) == {p: Rational(-1, 2)} + + +def test_match_bug2(): + x, y = map(Symbol, 'xy') + p, q, r = map(Wild, 'pqr') + res = (x + y).match(p + q + r) + assert (p + q + r).subs(res) == x + y + + +def test_match_bug3(): + x, a, b = map(Symbol, 'xab') + p = Wild('p') + assert (b*x*exp(a*x)).match(x*exp(p*x)) is None + + +def test_match_bug4(): + x = Symbol('x') + p = Wild('p') + e = x + assert e.match(-p*x) == {p: -1} + + +def test_match_bug5(): + x = Symbol('x') + p = Wild('p') + e = -x + assert e.match(-p*x) == {p: 1} + + +def test_match_bug6(): + x = Symbol('x') + p = Wild('p') + e = x + assert e.match(3*p*x) == {p: Rational(1)/3} + + +def test_match_polynomial(): + x = Symbol('x') + a = Wild('a', exclude=[x]) + b = Wild('b', exclude=[x]) + c = Wild('c', exclude=[x]) + d = Wild('d', exclude=[x]) + + eq = 4*x**3 + 3*x**2 + 2*x + 1 + pattern = a*x**3 + b*x**2 + c*x + d + assert eq.match(pattern) == {a: 4, b: 3, c: 2, d: 1} + assert (eq - 3*x**2).match(pattern) == {a: 4, b: 0, c: 2, d: 1} + assert (x + sqrt(2) + 3).match(a + b*x + c*x**2) == \ + {b: 1, a: sqrt(2) + 3, c: 0} + + +def test_exclude(): + x, y, a = map(Symbol, 'xya') + p = Wild('p', exclude=[1, x]) + q = Wild('q') + r = Wild('r', exclude=[sin, y]) + + assert sin(x).match(r) is None + assert cos(y).match(r) is None + + e = 3*x**2 + y*x + a + assert e.match(p*x**2 + q*x + r) == {p: 3, q: y, r: a} + + e = x + 1 + assert e.match(x + p) is None + assert e.match(p + 1) is None + assert e.match(x + 1 + p) == {p: 0} + + e = cos(x) + 5*sin(y) + assert e.match(r) is None + assert e.match(cos(y) + r) is None + assert e.match(r + p*sin(q)) == {r: cos(x), p: 5, q: y} + + +def test_floats(): + a, b = map(Wild, 'ab') + + e = cos(0.12345, evaluate=False)**2 + r = e.match(a*cos(b)**2) + assert r == {a: 1, b: Float(0.12345)} + + +def test_Derivative_bug1(): + f = Function("f") + x = abc.x + a = Wild("a", exclude=[f(x)]) + b = Wild("b", exclude=[f(x)]) + eq = f(x).diff(x) + assert eq.match(a*Derivative(f(x), x) + b) == {a: 1, b: 0} + + +def test_match_wild_wild(): + p = Wild('p') + q = Wild('q') + r = Wild('r') + + assert p.match(q + r) in [ {q: p, r: 0}, {q: 0, r: p} ] + assert p.match(q*r) in [ {q: p, r: 1}, {q: 1, r: p} ] + + p = Wild('p') + q = Wild('q', exclude=[p]) + r = Wild('r') + + assert p.match(q + r) == {q: 0, r: p} + assert p.match(q*r) == {q: 1, r: p} + + p = Wild('p') + q = Wild('q', exclude=[p]) + r = Wild('r', exclude=[p]) + + assert p.match(q + r) is None + assert p.match(q*r) is None + + +def test__combine_inverse(): + x, y = symbols("x y") + assert Mul._combine_inverse(x*I*y, x*I) == y + assert Mul._combine_inverse(x*x**(1 + y), x**(1 + y)) == x + assert Mul._combine_inverse(x*I*y, y*I) == x + assert Mul._combine_inverse(oo*I*y, y*I) is oo + assert Mul._combine_inverse(oo*I*y, oo*I) == y + assert Mul._combine_inverse(oo*I*y, oo*I) == y + assert Mul._combine_inverse(oo*y, -oo) == -y + assert Mul._combine_inverse(-oo*y, oo) == -y + assert Mul._combine_inverse((1-exp(x/y)),(exp(x/y)-1)) == -1 + assert Add._combine_inverse(oo, oo) is S.Zero + assert Add._combine_inverse(oo*I, oo*I) is S.Zero + assert Add._combine_inverse(x*oo, x*oo) is S.Zero + assert Add._combine_inverse(-x*oo, -x*oo) is S.Zero + assert Add._combine_inverse((x - oo)*(x + oo), -oo) + + +def test_issue_3773(): + x = symbols('x') + z, phi, r = symbols('z phi r') + c, A, B, N = symbols('c A B N', cls=Wild) + l = Wild('l', exclude=(0,)) + + eq = z * sin(2*phi) * r**7 + matcher = c * sin(phi*N)**l * r**A * log(r)**B + + assert eq.match(matcher) == {c: z, l: 1, N: 2, A: 7, B: 0} + assert (-eq).match(matcher) == {c: -z, l: 1, N: 2, A: 7, B: 0} + assert (x*eq).match(matcher) == {c: x*z, l: 1, N: 2, A: 7, B: 0} + assert (-7*x*eq).match(matcher) == {c: -7*x*z, l: 1, N: 2, A: 7, B: 0} + + matcher = c*sin(phi*N)**l * r**A + + assert eq.match(matcher) == {c: z, l: 1, N: 2, A: 7} + assert (-eq).match(matcher) == {c: -z, l: 1, N: 2, A: 7} + assert (x*eq).match(matcher) == {c: x*z, l: 1, N: 2, A: 7} + assert (-7*x*eq).match(matcher) == {c: -7*x*z, l: 1, N: 2, A: 7} + + +def test_issue_3883(): + from sympy.abc import gamma, mu, x + f = (-gamma * (x - mu)**2 - log(gamma) + log(2*pi))/2 + a, b, c = symbols('a b c', cls=Wild, exclude=(gamma,)) + + assert f.match(a * log(gamma) + b * gamma + c) == \ + {a: Rational(-1, 2), b: -(-mu + x)**2/2, c: log(2*pi)/2} + assert f.expand().collect(gamma).match(a * log(gamma) + b * gamma + c) == \ + {a: Rational(-1, 2), b: (-(x - mu)**2/2).expand(), c: (log(2*pi)/2).expand()} + g1 = Wild('g1', exclude=[gamma]) + g2 = Wild('g2', exclude=[gamma]) + g3 = Wild('g3', exclude=[gamma]) + assert f.expand().match(g1 * log(gamma) + g2 * gamma + g3) == \ + {g3: log(2)/2 + log(pi)/2, g1: Rational(-1, 2), g2: -mu**2/2 + mu*x - x**2/2} + + +def test_issue_4418(): + x = Symbol('x') + a, b, c = symbols('a b c', cls=Wild, exclude=(x,)) + f, g = symbols('f g', cls=Function) + + eq = diff(g(x)*f(x).diff(x), x) + + assert eq.match( + g(x).diff(x)*f(x).diff(x) + g(x)*f(x).diff(x, x) + c) == {c: 0} + assert eq.match(a*g(x).diff( + x)*f(x).diff(x) + b*g(x)*f(x).diff(x, x) + c) == {a: 1, b: 1, c: 0} + + +def test_issue_4700(): + f = Function('f') + x = Symbol('x') + a, b = symbols('a b', cls=Wild, exclude=(f(x),)) + + p = a*f(x) + b + eq1 = sin(x) + eq2 = f(x) + sin(x) + eq3 = f(x) + x + sin(x) + eq4 = x + sin(x) + + assert eq1.match(p) == {a: 0, b: sin(x)} + assert eq2.match(p) == {a: 1, b: sin(x)} + assert eq3.match(p) == {a: 1, b: x + sin(x)} + assert eq4.match(p) == {a: 0, b: x + sin(x)} + + +def test_issue_5168(): + a, b, c = symbols('a b c', cls=Wild) + x = Symbol('x') + f = Function('f') + + assert x.match(a) == {a: x} + assert x.match(a*f(x)**c) == {a: x, c: 0} + assert x.match(a*b) == {a: 1, b: x} + assert x.match(a*b*f(x)**c) == {a: 1, b: x, c: 0} + + assert (-x).match(a) == {a: -x} + assert (-x).match(a*f(x)**c) == {a: -x, c: 0} + assert (-x).match(a*b) == {a: -1, b: x} + assert (-x).match(a*b*f(x)**c) == {a: -1, b: x, c: 0} + + assert (2*x).match(a) == {a: 2*x} + assert (2*x).match(a*f(x)**c) == {a: 2*x, c: 0} + assert (2*x).match(a*b) == {a: 2, b: x} + assert (2*x).match(a*b*f(x)**c) == {a: 2, b: x, c: 0} + + assert (-2*x).match(a) == {a: -2*x} + assert (-2*x).match(a*f(x)**c) == {a: -2*x, c: 0} + assert (-2*x).match(a*b) == {a: -2, b: x} + assert (-2*x).match(a*b*f(x)**c) == {a: -2, b: x, c: 0} + + +def test_issue_4559(): + x = Symbol('x') + e = Symbol('e') + w = Wild('w', exclude=[x]) + y = Wild('y') + + # this is as it should be + + assert (3/x).match(w/y) == {w: 3, y: x} + assert (3*x).match(w*y) == {w: 3, y: x} + assert (x/3).match(y/w) == {w: 3, y: x} + assert (3*x).match(y/w) == {w: S.One/3, y: x} + assert (3*x).match(y/w) == {w: Rational(1, 3), y: x} + + # these could be allowed to fail + + assert (x/3).match(w/y) == {w: S.One/3, y: 1/x} + assert (3*x).match(w/y) == {w: 3, y: 1/x} + assert (3/x).match(w*y) == {w: 3, y: 1/x} + + # Note that solve will give + # multiple roots but match only gives one: + # + # >>> solve(x**r-y**2,y) + # [-x**(r/2), x**(r/2)] + + r = Symbol('r', rational=True) + assert (x**r).match(y**2) == {y: x**(r/2)} + assert (x**e).match(y**2) == {y: sqrt(x**e)} + + # since (x**i = y) -> x = y**(1/i) where i is an integer + # the following should also be valid as long as y is not + # zero when i is negative. + + a = Wild('a') + + e = S.Zero + assert e.match(a) == {a: e} + assert e.match(1/a) is None + assert e.match(a**.3) is None + + e = S(3) + assert e.match(1/a) == {a: 1/e} + assert e.match(1/a**2) == {a: 1/sqrt(e)} + e = pi + assert e.match(1/a) == {a: 1/e} + assert e.match(1/a**2) == {a: 1/sqrt(e)} + assert (-e).match(sqrt(a)) is None + assert (-e).match(a**2) == {a: I*sqrt(pi)} + +# The pattern matcher doesn't know how to handle (x - a)**2 == (a - x)**2. To +# avoid ambiguity in actual applications, don't put a coefficient (including a +# minus sign) in front of a wild. +@XFAIL +def test_issue_4883(): + a = Wild('a') + x = Symbol('x') + + e = [i**2 for i in (x - 2, 2 - x)] + p = [i**2 for i in (x - a, a- x)] + for eq in e: + for pat in p: + assert eq.match(pat) == {a: 2} + + +def test_issue_4319(): + x, y = symbols('x y') + + p = -x*(S.One/8 - y) + ans = {S.Zero, y - S.One/8} + + def ok(pat): + assert set(p.match(pat).values()) == ans + + ok(Wild("coeff", exclude=[x])*x + Wild("rest")) + ok(Wild("w", exclude=[x])*x + Wild("rest")) + ok(Wild("coeff", exclude=[x])*x + Wild("rest")) + ok(Wild("w", exclude=[x])*x + Wild("rest")) + ok(Wild("e", exclude=[x])*x + Wild("rest")) + ok(Wild("ress", exclude=[x])*x + Wild("rest")) + ok(Wild("resu", exclude=[x])*x + Wild("rest")) + + +def test_issue_3778(): + p, c, q = symbols('p c q', cls=Wild) + x = Symbol('x') + + assert (sin(x)**2).match(sin(p)*sin(q)*c) == {q: x, c: 1, p: x} + assert (2*sin(x)).match(sin(p) + sin(q) + c) == {q: x, c: 0, p: x} + + +def test_issue_6103(): + x = Symbol('x') + a = Wild('a') + assert (-I*x*oo).match(I*a*oo) == {a: -x} + + +def test_issue_3539(): + a = Wild('a') + x = Symbol('x') + assert (x - 2).match(a - x) is None + assert (6/x).match(a*x) is None + assert (6/x**2).match(a/x) == {a: 6/x} + +def test_gh_issue_2711(): + x = Symbol('x') + f = meijerg(((), ()), ((0,), ()), x) + a = Wild('a') + b = Wild('b') + + assert f.find(a) == {(S.Zero,), ((), ()), ((S.Zero,), ()), x, S.Zero, + (), meijerg(((), ()), ((S.Zero,), ()), x)} + assert f.find(a + b) == \ + {meijerg(((), ()), ((S.Zero,), ()), x), x, S.Zero} + assert f.find(a**2) == {meijerg(((), ()), ((S.Zero,), ()), x), x} + + +def test_issue_17354(): + from sympy.core.symbol import (Wild, symbols) + x, y = symbols("x y", real=True) + a, b = symbols("a b", cls=Wild) + assert ((0 <= x).reversed | (y <= x)).match((1/a <= b) | (a <= b)) is None + + +def test_match_issue_17397(): + f = Function("f") + x = Symbol("x") + a3 = Wild('a3', exclude=[f(x), f(x).diff(x), f(x).diff(x, 2)]) + b3 = Wild('b3', exclude=[f(x), f(x).diff(x), f(x).diff(x, 2)]) + c3 = Wild('c3', exclude=[f(x), f(x).diff(x), f(x).diff(x, 2)]) + deq = a3*(f(x).diff(x, 2)) + b3*f(x).diff(x) + c3*f(x) + + eq = (x-2)**2*(f(x).diff(x, 2)) + (x-2)*(f(x).diff(x)) + ((x-2)**2 - 4)*f(x) + r = collect(eq, [f(x).diff(x, 2), f(x).diff(x), f(x)]).match(deq) + assert r == {a3: (x - 2)**2, c3: (x - 2)**2 - 4, b3: x - 2} + + eq =x*f(x) + x*Derivative(f(x), (x, 2)) - 4*f(x) + Derivative(f(x), x) \ + - 4*Derivative(f(x), (x, 2)) - 2*Derivative(f(x), x)/x + 4*Derivative(f(x), (x, 2))/x + r = collect(eq, [f(x).diff(x, 2), f(x).diff(x), f(x)]).match(deq) + assert r == {a3: x - 4 + 4/x, b3: 1 - 2/x, c3: x - 4} + + +def test_match_issue_21942(): + a, r, w = symbols('a, r, w', nonnegative=True) + p = symbols('p', positive=True) + g_ = Wild('g') + pattern = g_ ** (1 / (1 - p)) + eq = (a * r ** (1 - p) + w ** (1 - p) * (1 - a)) ** (1 / (1 - p)) + m = {g_: a * r ** (1 - p) + w ** (1 - p) * (1 - a)} + assert pattern.matches(eq) == m + assert (-pattern).matches(-eq) == m + assert pattern.matches(signsimp(eq)) is None + + +def test_match_terms(): + X, Y = map(Wild, "XY") + x, y, z = symbols('x y z') + assert (5*y - x).match(5*X - Y) == {X: y, Y: x} + # 15907 + assert (x + (y - 1)*z).match(x + X*z) == {X: y - 1} + # 20747 + assert (x - log(x/y)*(1-exp(x/y))).match(x - log(X/y)*(1-exp(x/y))) == {X: x} + + +def test_match_bound(): + V, W = map(Wild, "VW") + x, y = symbols('x y') + assert Sum(x, (x, 1, 2)).match(Sum(y, (y, 1, W))) == {W: 2} + assert Sum(x, (x, 1, 2)).match(Sum(V, (V, 1, W))) == {W: 2, V:x} + assert Sum(x, (x, 1, 2)).match(Sum(V, (V, 1, 2))) == {V:x} + + +def test_issue_22462(): + x, f = symbols('x'), Function('f') + n, Q = symbols('n Q', cls=Wild) + pattern = -Q*f(x)**n + eq = 5*f(x)**2 + assert pattern.matches(eq) == {n: 2, Q: -5} diff --git a/lib/python3.12/site-packages/sympy/core/tests/test_numbers.py b/lib/python3.12/site-packages/sympy/core/tests/test_numbers.py new file mode 100644 index 0000000000000000000000000000000000000000..054fd7591b28d352b648c98d78cdda10a4fe2cb1 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/core/tests/test_numbers.py @@ -0,0 +1,2300 @@ +import numbers as nums +import decimal +from sympy.concrete.summations import Sum +from sympy.core import (EulerGamma, Catalan, TribonacciConstant, + GoldenRatio) +from sympy.core.containers import Tuple +from sympy.core.expr import unchanged +from sympy.core.logic import fuzzy_not +from sympy.core.mul import Mul +from sympy.core.numbers import (mpf_norm, seterr, + Integer, I, pi, comp, Rational, E, nan, + oo, AlgebraicNumber, Number, Float, zoo, equal_valued, + int_valued, all_close) +from sympy.core.intfunc import (igcd, igcdex, igcd2, igcd_lehmer, + ilcm, integer_nthroot, isqrt, integer_log, mod_inverse) +from sympy.core.power import Pow +from sympy.core.relational import Ge, Gt, Le, Lt +from sympy.core.singleton import S +from sympy.core.symbol import Dummy, Symbol +from sympy.core.sympify import sympify +from sympy.functions.combinatorial.factorials import factorial +from sympy.functions.elementary.integers import floor +from sympy.functions.combinatorial.numbers import fibonacci +from sympy.functions.elementary.exponential import exp, log +from sympy.functions.elementary.miscellaneous import sqrt, cbrt +from sympy.functions.elementary.trigonometric import cos, sin +from sympy.polys.domains.realfield import RealField +from sympy.printing.latex import latex +from sympy.printing.repr import srepr +from sympy.simplify import simplify +from sympy.polys.domains.groundtypes import PythonRational +from sympy.utilities.decorator import conserve_mpmath_dps +from sympy.utilities.iterables import permutations +from sympy.testing.pytest import XFAIL, raises, _both_exp_pow +from sympy import Add + +from mpmath import mpf +import mpmath +from sympy.core import numbers +t = Symbol('t', real=False) + +_ninf = float(-oo) +_inf = float(oo) + + +def same_and_same_prec(a, b): + # stricter matching for Floats + return a == b and a._prec == b._prec + + +def test_seterr(): + seterr(divide=True) + raises(ValueError, lambda: S.Zero/S.Zero) + seterr(divide=False) + assert S.Zero / S.Zero is S.NaN + + +def test_mod(): + x = S.Half + y = Rational(3, 4) + z = Rational(5, 18043) + + assert x % x == 0 + assert x % y == S.Half + assert x % z == Rational(3, 36086) + assert y % x == Rational(1, 4) + assert y % y == 0 + assert y % z == Rational(9, 72172) + assert z % x == Rational(5, 18043) + assert z % y == Rational(5, 18043) + assert z % z == 0 + + a = Float(2.6) + + assert (a % .2) == 0.0 + assert (a % 2).round(15) == 0.6 + assert (a % 0.5).round(15) == 0.1 + + p = Symbol('p', infinite=True) + + assert oo % oo is nan + assert zoo % oo is nan + assert 5 % oo is nan + assert p % 5 is nan + + # In these two tests, if the precision of m does + # not match the precision of the ans, then it is + # likely that the change made now gives an answer + # with degraded accuracy. + r = Rational(500, 41) + f = Float('.36', 3) + m = r % f + ans = Float(r % Rational(f), 3) + assert m == ans and m._prec == ans._prec + f = Float('8.36', 3) + m = f % r + ans = Float(Rational(f) % r, 3) + assert m == ans and m._prec == ans._prec + + s = S.Zero + + assert s % float(1) == 0.0 + + # No rounding required since these numbers can be represented + # exactly. + assert Rational(3, 4) % Float(1.1) == 0.75 + assert Float(1.5) % Rational(5, 4) == 0.25 + assert Rational(5, 4).__rmod__(Float('1.5')) == 0.25 + assert Float('1.5').__rmod__(Float('2.75')) == Float('1.25') + assert 2.75 % Float('1.5') == Float('1.25') + + a = Integer(7) + b = Integer(4) + + assert type(a % b) == Integer + assert a % b == Integer(3) + assert Integer(1) % Rational(2, 3) == Rational(1, 3) + assert Rational(7, 5) % Integer(1) == Rational(2, 5) + assert Integer(2) % 1.5 == 0.5 + + assert Integer(3).__rmod__(Integer(10)) == Integer(1) + assert Integer(10) % 4 == Integer(2) + assert 15 % Integer(4) == Integer(3) + + +def test_divmod(): + x = Symbol("x") + assert divmod(S(12), S(8)) == Tuple(1, 4) + assert divmod(-S(12), S(8)) == Tuple(-2, 4) + assert divmod(S.Zero, S.One) == Tuple(0, 0) + raises(ZeroDivisionError, lambda: divmod(S.Zero, S.Zero)) + raises(ZeroDivisionError, lambda: divmod(S.One, S.Zero)) + assert divmod(S(12), 8) == Tuple(1, 4) + assert divmod(12, S(8)) == Tuple(1, 4) + assert S(1024)//x == 1024//x == floor(1024/x) + + assert divmod(S("2"), S("3/2")) == Tuple(S("1"), S("1/2")) + assert divmod(S("3/2"), S("2")) == Tuple(S("0"), S("3/2")) + assert divmod(S("2"), S("3.5")) == Tuple(S("0"), S("2.")) + assert divmod(S("3.5"), S("2")) == Tuple(S("1"), S("1.5")) + assert divmod(S("2"), S("1/3")) == Tuple(S("6"), S("0")) + assert divmod(S("1/3"), S("2")) == Tuple(S("0"), S("1/3")) + assert divmod(S("2"), S("1/10")) == Tuple(S("20"), S("0")) + assert divmod(S("2"), S(".1"))[0] == 19 + assert divmod(S("0.1"), S("2")) == Tuple(S("0"), S("0.1")) + assert divmod(S("2"), 2) == Tuple(S("1"), S("0")) + assert divmod(2, S("2")) == Tuple(S("1"), S("0")) + assert divmod(S("2"), 1.5) == Tuple(S("1"), S("0.5")) + assert divmod(1.5, S("2")) == Tuple(S("0"), S("1.5")) + assert divmod(0.3, S("2")) == Tuple(S("0"), S("0.3")) + assert divmod(S("3/2"), S("3.5")) == Tuple(S("0"), S(3/2)) + assert divmod(S("3.5"), S("3/2")) == Tuple(S("2"), S("0.5")) + assert divmod(S("3/2"), S("1/3")) == Tuple(S("4"), S("1/6")) + assert divmod(S("1/3"), S("3/2")) == Tuple(S("0"), S("1/3")) + assert divmod(S("3/2"), S("0.1"))[0] == 14 + assert divmod(S("0.1"), S("3/2")) == Tuple(S("0"), S("0.1")) + assert divmod(S("3/2"), 2) == Tuple(S("0"), S("3/2")) + assert divmod(2, S("3/2")) == Tuple(S("1"), S("1/2")) + assert divmod(S("3/2"), 1.5) == Tuple(S("1"), S("0.")) + assert divmod(1.5, S("3/2")) == Tuple(S("1"), S("0.")) + assert divmod(S("3/2"), 0.3) == Tuple(S("5"), S("0.")) + assert divmod(0.3, S("3/2")) == Tuple(S("0"), S("0.3")) + assert divmod(S("1/3"), S("3.5")) == (0, 1/3) + assert divmod(S("3.5"), S("0.1")) == Tuple(S("35"), S("0.")) + assert divmod(S("0.1"), S("3.5")) == Tuple(S("0"), S("0.1")) + assert divmod(S("3.5"), 2) == Tuple(S("1"), S("1.5")) + assert divmod(2, S("3.5")) == Tuple(S("0"), S("2.")) + assert divmod(S("3.5"), 1.5) == Tuple(S("2"), S("0.5")) + assert divmod(1.5, S("3.5")) == Tuple(S("0"), S("1.5")) + assert divmod(0.3, S("3.5")) == Tuple(S("0"), S("0.3")) + assert divmod(S("0.1"), S("1/3")) == Tuple(S("0"), S("0.1")) + assert divmod(S("1/3"), 2) == Tuple(S("0"), S("1/3")) + assert divmod(2, S("1/3")) == Tuple(S("6"), S("0")) + assert divmod(S("1/3"), 1.5) == (0, 1/3) + assert divmod(0.3, S("1/3")) == (0, 0.3) + assert divmod(S("0.1"), 2) == (0, 0.1) + assert divmod(2, S("0.1"))[0] == 19 + assert divmod(S("0.1"), 1.5) == (0, 0.1) + assert divmod(1.5, S("0.1")) == Tuple(S("15"), S("0.")) + assert divmod(S("0.1"), 0.3) == Tuple(S("0"), S("0.1")) + + assert str(divmod(S("2"), 0.3)) == '(6, 0.2)' + assert str(divmod(S("3.5"), S("1/3"))) == '(10, 0.166666666666667)' + assert str(divmod(S("3.5"), 0.3)) == '(11, 0.2)' + assert str(divmod(S("1/3"), S("0.1"))) == '(3, 0.0333333333333333)' + assert str(divmod(1.5, S("1/3"))) == '(4, 0.166666666666667)' + assert str(divmod(S("1/3"), 0.3)) == '(1, 0.0333333333333333)' + assert str(divmod(0.3, S("0.1"))) == '(2, 0.1)' + + assert divmod(-3, S(2)) == (-2, 1) + assert divmod(S(-3), S(2)) == (-2, 1) + assert divmod(S(-3), 2) == (-2, 1) + + assert divmod(oo, 1) == (S.NaN, S.NaN) + assert divmod(S.NaN, 1) == (S.NaN, S.NaN) + assert divmod(1, S.NaN) == (S.NaN, S.NaN) + ans = [(-1, oo), (-1, oo), (0, 0), (0, 1), (0, 2)] + OO = float('inf') + ANS = [tuple(map(float, i)) for i in ans] + assert [divmod(i, oo) for i in range(-2, 3)] == ans + ans = [(0, -2), (0, -1), (0, 0), (-1, -oo), (-1, -oo)] + ANS = [tuple(map(float, i)) for i in ans] + assert [divmod(i, -oo) for i in range(-2, 3)] == ans + assert [divmod(i, -OO) for i in range(-2, 3)] == ANS + + # sympy's divmod gives an Integer for the quotient rather than a float + dmod = lambda a, b: tuple([j if i else int(j) for i, j in enumerate(divmod(a, b))]) + for a in (4, 4., 4.25, 0, 0., -4, -4. -4.25): + for b in (2, 2., 2.5, -2, -2., -2.5): + assert divmod(S(a), S(b)) == dmod(a, b) + + +def test_igcd(): + assert igcd(0, 0) == 0 + assert igcd(0, 1) == 1 + assert igcd(1, 0) == 1 + assert igcd(0, 7) == 7 + assert igcd(7, 0) == 7 + assert igcd(7, 1) == 1 + assert igcd(1, 7) == 1 + assert igcd(-1, 0) == 1 + assert igcd(0, -1) == 1 + assert igcd(-1, -1) == 1 + assert igcd(-1, 7) == 1 + assert igcd(7, -1) == 1 + assert igcd(8, 2) == 2 + assert igcd(4, 8) == 4 + assert igcd(8, 16) == 8 + assert igcd(7, -3) == 1 + assert igcd(-7, 3) == 1 + assert igcd(-7, -3) == 1 + assert igcd(*[10, 20, 30]) == 10 + raises(TypeError, lambda: igcd()) + raises(TypeError, lambda: igcd(2)) + raises(ValueError, lambda: igcd(0, None)) + raises(ValueError, lambda: igcd(1, 2.2)) + for args in permutations((45.1, 1, 30)): + raises(ValueError, lambda: igcd(*args)) + for args in permutations((1, 2, None)): + raises(ValueError, lambda: igcd(*args)) + + +def test_igcd_lehmer(): + a, b = fibonacci(10001), fibonacci(10000) + # len(str(a)) == 2090 + # small divisors, long Euclidean sequence + assert igcd_lehmer(a, b) == 1 + c = fibonacci(100) + assert igcd_lehmer(a*c, b*c) == c + # big divisor + assert igcd_lehmer(a, 10**1000) == 1 + # swapping argument + assert igcd_lehmer(1, 2) == igcd_lehmer(2, 1) + + +def test_igcd2(): + # short loop + assert igcd2(2**100 - 1, 2**99 - 1) == 1 + # Lehmer's algorithm + a, b = int(fibonacci(10001)), int(fibonacci(10000)) + assert igcd2(a, b) == 1 + + +def test_ilcm(): + assert ilcm(0, 0) == 0 + assert ilcm(1, 0) == 0 + assert ilcm(0, 1) == 0 + assert ilcm(1, 1) == 1 + assert ilcm(2, 1) == 2 + assert ilcm(8, 2) == 8 + assert ilcm(8, 6) == 24 + assert ilcm(8, 7) == 56 + assert ilcm(*[10, 20, 30]) == 60 + raises(ValueError, lambda: ilcm(8.1, 7)) + raises(ValueError, lambda: ilcm(8, 7.1)) + raises(TypeError, lambda: ilcm(8)) + + +def test_igcdex(): + assert igcdex(2, 3) == (-1, 1, 1) + assert igcdex(10, 12) == (-1, 1, 2) + assert igcdex(100, 2004) == (-20, 1, 4) + assert igcdex(0, 0) == (0, 1, 0) + assert igcdex(1, 0) == (1, 0, 1) + + +def _strictly_equal(a, b): + return (a.p, a.q, type(a.p), type(a.q)) == \ + (b.p, b.q, type(b.p), type(b.q)) + + +def _test_rational_new(cls): + """ + Tests that are common between Integer and Rational. + """ + assert cls(0) is S.Zero + assert cls(1) is S.One + assert cls(-1) is S.NegativeOne + # These look odd, but are similar to int(): + assert cls('1') is S.One + assert cls('-1') is S.NegativeOne + + i = Integer(10) + assert _strictly_equal(i, cls('10')) + assert _strictly_equal(i, cls('10')) + assert _strictly_equal(i, cls(int(10))) + assert _strictly_equal(i, cls(i)) + + raises(TypeError, lambda: cls(Symbol('x'))) + + +def test_Integer_new(): + """ + Test for Integer constructor + """ + _test_rational_new(Integer) + + assert _strictly_equal(Integer(0.9), S.Zero) + assert _strictly_equal(Integer(10.5), Integer(10)) + raises(ValueError, lambda: Integer("10.5")) + assert Integer(Rational('1.' + '9'*20)) == 1 + + +def test_Rational_new(): + """" + Test for Rational constructor + """ + _test_rational_new(Rational) + + n1 = S.Half + assert n1 == Rational(Integer(1), 2) + assert n1 == Rational(Integer(1), Integer(2)) + assert n1 == Rational(1, Integer(2)) + assert n1 == Rational(S.Half) + assert 1 == Rational(n1, n1) + assert Rational(3, 2) == Rational(S.Half, Rational(1, 3)) + assert Rational(3, 1) == Rational(1, Rational(1, 3)) + n3_4 = Rational(3, 4) + assert Rational('3/4') == n3_4 + assert -Rational('-3/4') == n3_4 + assert Rational('.76').limit_denominator(4) == n3_4 + assert Rational(19, 25).limit_denominator(4) == n3_4 + assert Rational('19/25').limit_denominator(4) == n3_4 + assert Rational(1.0, 3) == Rational(1, 3) + assert Rational(1, 3.0) == Rational(1, 3) + assert Rational(Float(0.5)) == S.Half + assert Rational('1e2/1e-2') == Rational(10000) + assert Rational('1 234') == Rational(1234) + assert Rational('1/1 234') == Rational(1, 1234) + assert Rational(-1, 0) is S.ComplexInfinity + assert Rational(1, 0) is S.ComplexInfinity + # Make sure Rational doesn't lose precision on Floats + assert Rational(pi.evalf(100)).evalf(100) == pi.evalf(100) + raises(TypeError, lambda: Rational('3**3')) + raises(TypeError, lambda: Rational('1/2 + 2/3')) + + # handle fractions.Fraction instances + try: + import fractions + assert Rational(fractions.Fraction(1, 2)) == S.Half + except ImportError: + pass + + assert Rational(PythonRational(2, 6)) == Rational(1, 3) + + assert Rational(2, 4, gcd=1).q == 4 + n = Rational(2, -4, gcd=1) + assert n.q == 4 + assert n.p == -2 + +def test_issue_24543(): + for p in ('1.5', 1.5, 2): + for q in ('1.5', 1.5, 2): + assert Rational(p, q).as_numer_denom() == Rational('%s/%s'%(p,q)).as_numer_denom() + + assert Rational('0.5', '100') == Rational(1, 200) + + +def test_Number_new(): + """" + Test for Number constructor + """ + # Expected behavior on numbers and strings + assert Number(1) is S.One + assert Number(2).__class__ is Integer + assert Number(-622).__class__ is Integer + assert Number(5, 3).__class__ is Rational + assert Number(5.3).__class__ is Float + assert Number('1') is S.One + assert Number('2').__class__ is Integer + assert Number('-622').__class__ is Integer + assert Number('5/3').__class__ is Rational + assert Number('5.3').__class__ is Float + raises(ValueError, lambda: Number('cos')) + raises(TypeError, lambda: Number(cos)) + a = Rational(3, 5) + assert Number(a) is a # Check idempotence on Numbers + u = ['inf', '-inf', 'nan', 'iNF', '+inf'] + v = [oo, -oo, nan, oo, oo] + for i, a in zip(u, v): + assert Number(i) is a, (i, Number(i), a) + + +def test_Number_cmp(): + n1 = Number(1) + n2 = Number(2) + n3 = Number(-3) + + assert n1 < n2 + assert n1 <= n2 + assert n3 < n1 + assert n2 > n3 + assert n2 >= n3 + + raises(TypeError, lambda: n1 < S.NaN) + raises(TypeError, lambda: n1 <= S.NaN) + raises(TypeError, lambda: n1 > S.NaN) + raises(TypeError, lambda: n1 >= S.NaN) + + +def test_Rational_cmp(): + n1 = Rational(1, 4) + n2 = Rational(1, 3) + n3 = Rational(2, 4) + n4 = Rational(2, -4) + n5 = Rational(0) + n6 = Rational(1) + n7 = Rational(3) + n8 = Rational(-3) + + assert n8 < n5 + assert n5 < n6 + assert n6 < n7 + assert n8 < n7 + assert n7 > n8 + assert (n1 + 1)**n2 < 2 + assert ((n1 + n6)/n7) < 1 + + assert n4 < n3 + assert n2 < n3 + assert n1 < n2 + assert n3 > n1 + assert not n3 < n1 + assert not (Rational(-1) > 0) + assert Rational(-1) < 0 + + raises(TypeError, lambda: n1 < S.NaN) + raises(TypeError, lambda: n1 <= S.NaN) + raises(TypeError, lambda: n1 > S.NaN) + raises(TypeError, lambda: n1 >= S.NaN) + + +def test_Float(): + def eq(a, b): + t = Float("1.0E-15") + return (-t < a - b < t) + + zeros = (0, S.Zero, 0., Float(0)) + for i, j in permutations(zeros[:-1], 2): + assert i == j + for i, j in permutations(zeros[-2:], 2): + assert i == j + for z in zeros: + assert z in zeros + assert S.Zero.is_zero + + a = Float(2) ** Float(3) + assert eq(a.evalf(), Float(8)) + assert eq((pi ** -1).evalf(), Float("0.31830988618379067")) + a = Float(2) ** Float(4) + assert eq(a.evalf(), Float(16)) + assert (S(.3) == S(.5)) is False + + mpf = (0, 5404319552844595, -52, 53) + x_str = Float((0, '13333333333333', -52, 53)) + x_0xstr = Float((0, '0x13333333333333', -52, 53)) + x2_str = Float((0, '26666666666666', -53, 54)) + x_hex = Float((0, int(0x13333333333333), -52, 53)) + x_dec = Float(mpf) + assert x_str == x_0xstr == x_hex == x_dec == Float(1.2) + # x2_str was entered slightly malformed in that the mantissa + # was even -- it should be odd and the even part should be + # included with the exponent, but this is resolved by normalization + # ONLY IF REQUIREMENTS of mpf_norm are met: the bitcount must + # be exact: double the mantissa ==> increase bc by 1 + assert Float(1.2)._mpf_ == mpf + assert x2_str._mpf_ == mpf + + assert Float((0, int(0), -123, -1)) is S.NaN + assert Float((0, int(0), -456, -2)) is S.Infinity + assert Float((1, int(0), -789, -3)) is S.NegativeInfinity + # if you don't give the full signature, it's not special + assert Float((0, int(0), -123)) == Float(0) + assert Float((0, int(0), -456)) == Float(0) + assert Float((1, int(0), -789)) == Float(0) + + raises(ValueError, lambda: Float((0, 7, 1, 3), '')) + + assert Float('0.0').is_finite is True + assert Float('0.0').is_negative is False + assert Float('0.0').is_positive is False + assert Float('0.0').is_infinite is False + assert Float('0.0').is_zero is True + + # rationality properties + # if the integer test fails then the use of intlike + # should be removed from gamma_functions.py + assert Float(1).is_integer is None + assert Float(1).is_rational is None + assert Float(1).is_irrational is None + assert sqrt(2).n(15).is_rational is None + assert sqrt(2).n(15).is_irrational is None + + # do not automatically evalf + def teq(a): + assert (a.evalf() == a) is False + assert (a.evalf() != a) is True + assert (a == a.evalf()) is False + assert (a != a.evalf()) is True + + teq(pi) + teq(2*pi) + teq(cos(0.1, evaluate=False)) + + # long integer + i = 12345678901234567890 + assert same_and_same_prec(Float(12, ''), Float('12', '')) + assert same_and_same_prec(Float(Integer(i), ''), Float(i, '')) + assert same_and_same_prec(Float(i, ''), Float(str(i), 20)) + assert same_and_same_prec(Float(str(i)), Float(i, '')) + assert same_and_same_prec(Float(i), Float(i, '')) + + # inexact floats (repeating binary = denom not multiple of 2) + # cannot have precision greater than 15 + assert Float(.125, 22)._prec == 76 + assert Float(2.0, 22)._prec == 76 + # only default prec is equal, even for exactly representable float + assert Float(.125, 22) != .125 + #assert Float(2.0, 22) == 2 + assert float(Float('.12500000000000001', '')) == .125 + raises(ValueError, lambda: Float(.12500000000000001, '')) + + # allow spaces + assert Float('123 456.123 456') == Float('123456.123456') + assert Integer('123 456') == Integer('123456') + assert Rational('123 456.123 456') == Rational('123456.123456') + assert Float(' .3e2') == Float('0.3e2') + # but treat them as strictly ass underscore between digits: only 1 + raises(ValueError, lambda: Float('1 2')) + + # allow underscore between digits + assert Float('1_23.4_56') == Float('123.456') + # assert Float('1_23.4_5_6', 12) == Float('123.456', 12) + # ...but not in all cases (per Py 3.6) + raises(ValueError, lambda: Float('1_')) + raises(ValueError, lambda: Float('1__2')) + raises(ValueError, lambda: Float('_1')) + raises(ValueError, lambda: Float('_inf')) + + # allow auto precision detection + assert Float('.1', '') == Float(.1, 1) + assert Float('.125', '') == Float(.125, 3) + assert Float('.100', '') == Float(.1, 3) + assert Float('2.0', '') == Float('2', 2) + + raises(ValueError, lambda: Float("12.3d-4", "")) + raises(ValueError, lambda: Float(12.3, "")) + raises(ValueError, lambda: Float('.')) + raises(ValueError, lambda: Float('-.')) + + zero = Float('0.0') + assert Float('-0') == zero + assert Float('.0') == zero + assert Float('-.0') == zero + assert Float('-0.0') == zero + assert Float(0.0) == zero + assert Float(0) == zero + assert Float(0, '') == Float('0', '') + assert Float(1) == Float(1.0) + assert Float(S.Zero) == zero + assert Float(S.One) == Float(1.0) + + assert Float(decimal.Decimal('0.1'), 3) == Float('.1', 3) + assert Float(decimal.Decimal('nan')) is S.NaN + assert Float(decimal.Decimal('Infinity')) is S.Infinity + assert Float(decimal.Decimal('-Infinity')) is S.NegativeInfinity + + assert '{:.3f}'.format(Float(4.236622)) == '4.237' + assert '{:.35f}'.format(Float(pi.n(40), 40)) == \ + '3.14159265358979323846264338327950288' + + # unicode + assert Float('0.73908513321516064100000000') == \ + Float('0.73908513321516064100000000') + assert Float('0.73908513321516064100000000', 28) == \ + Float('0.73908513321516064100000000', 28) + + # binary precision + # Decimal value 0.1 cannot be expressed precisely as a base 2 fraction + a = Float(S.One/10, dps=15) + b = Float(S.One/10, dps=16) + p = Float(S.One/10, precision=53) + q = Float(S.One/10, precision=54) + assert a._mpf_ == p._mpf_ + assert not a._mpf_ == q._mpf_ + assert not b._mpf_ == q._mpf_ + + # Precision specifying errors + raises(ValueError, lambda: Float("1.23", dps=3, precision=10)) + raises(ValueError, lambda: Float("1.23", dps="", precision=10)) + raises(ValueError, lambda: Float("1.23", dps=3, precision="")) + raises(ValueError, lambda: Float("1.23", dps="", precision="")) + + # from NumberSymbol + assert same_and_same_prec(Float(pi, 32), pi.evalf(32)) + assert same_and_same_prec(Float(Catalan), Catalan.evalf()) + + # oo and nan + u = ['inf', '-inf', 'nan', 'iNF', '+inf'] + v = [oo, -oo, nan, oo, oo] + for i, a in zip(u, v): + assert Float(i) is a + + +def test_zero_not_false(): + # https://github.com/sympy/sympy/issues/20796 + assert (S(0.0) == S.false) is False + assert (S.false == S(0.0)) is False + assert (S(0) == S.false) is False + assert (S.false == S(0)) is False + + +@conserve_mpmath_dps +def test_float_mpf(): + import mpmath + mpmath.mp.dps = 100 + mp_pi = mpmath.pi() + + assert Float(mp_pi, 100) == Float(mp_pi._mpf_, 100) == pi.evalf(100) + + mpmath.mp.dps = 15 + + assert Float(mp_pi, 100) == Float(mp_pi._mpf_, 100) == pi.evalf(100) + + +def test_Float_RealElement(): + repi = RealField(dps=100)(pi.evalf(100)) + # We still have to pass the precision because Float doesn't know what + # RealElement is, but make sure it keeps full precision from the result. + assert Float(repi, 100) == pi.evalf(100) + + +def test_Float_default_to_highprec_from_str(): + s = str(pi.evalf(128)) + assert same_and_same_prec(Float(s), Float(s, '')) + + +def test_Float_eval(): + a = Float(3.2) + assert (a**2).is_Float + + +def test_Float_issue_2107(): + a = Float(0.1, 10) + b = Float("0.1", 10) + + assert a - a == 0 + assert a + (-a) == 0 + assert S.Zero + a - a == 0 + assert S.Zero + a + (-a) == 0 + + assert b - b == 0 + assert b + (-b) == 0 + assert S.Zero + b - b == 0 + assert S.Zero + b + (-b) == 0 + + +def test_issue_14289(): + from sympy.polys.numberfields import to_number_field + + a = 1 - sqrt(2) + b = to_number_field(a) + assert b.as_expr() == a + assert b.minpoly(a).expand() == 0 + + +def test_Float_from_tuple(): + a = Float((0, '1L', 0, 1)) + b = Float((0, '1', 0, 1)) + assert a == b + + +def test_Infinity(): + assert oo != 1 + assert 1*oo is oo + assert 1 != oo + assert oo != -oo + assert oo != Symbol("x")**3 + assert oo + 1 is oo + assert 2 + oo is oo + assert 3*oo + 2 is oo + assert S.Half**oo == 0 + assert S.Half**(-oo) is oo + assert -oo*3 is -oo + assert oo + oo is oo + assert -oo + oo*(-5) is -oo + assert 1/oo == 0 + assert 1/(-oo) == 0 + assert 8/oo == 0 + assert oo % 2 is nan + assert 2 % oo is nan + assert oo/oo is nan + assert oo/-oo is nan + assert -oo/oo is nan + assert -oo/-oo is nan + assert oo - oo is nan + assert oo - -oo is oo + assert -oo - oo is -oo + assert -oo - -oo is nan + assert oo + -oo is nan + assert -oo + oo is nan + assert oo + oo is oo + assert -oo + oo is nan + assert oo + -oo is nan + assert -oo + -oo is -oo + assert oo*oo is oo + assert -oo*oo is -oo + assert oo*-oo is -oo + assert -oo*-oo is oo + assert oo/0 is oo + assert -oo/0 is -oo + assert 0/oo == 0 + assert 0/-oo == 0 + assert oo*0 is nan + assert -oo*0 is nan + assert 0*oo is nan + assert 0*-oo is nan + assert oo + 0 is oo + assert -oo + 0 is -oo + assert 0 + oo is oo + assert 0 + -oo is -oo + assert oo - 0 is oo + assert -oo - 0 is -oo + assert 0 - oo is -oo + assert 0 - -oo is oo + assert oo/2 is oo + assert -oo/2 is -oo + assert oo/-2 is -oo + assert -oo/-2 is oo + assert oo*2 is oo + assert -oo*2 is -oo + assert oo*-2 is -oo + assert 2/oo == 0 + assert 2/-oo == 0 + assert -2/oo == 0 + assert -2/-oo == 0 + assert 2*oo is oo + assert 2*-oo is -oo + assert -2*oo is -oo + assert -2*-oo is oo + assert 2 + oo is oo + assert 2 - oo is -oo + assert -2 + oo is oo + assert -2 - oo is -oo + assert 2 + -oo is -oo + assert 2 - -oo is oo + assert -2 + -oo is -oo + assert -2 - -oo is oo + assert S(2) + oo is oo + assert S(2) - oo is -oo + assert oo/I == -oo*I + assert -oo/I == oo*I + assert oo*float(1) == _inf and (oo*float(1)) is oo + assert -oo*float(1) == _ninf and (-oo*float(1)) is -oo + assert oo/float(1) == _inf and (oo/float(1)) is oo + assert -oo/float(1) == _ninf and (-oo/float(1)) is -oo + assert oo*float(-1) == _ninf and (oo*float(-1)) is -oo + assert -oo*float(-1) == _inf and (-oo*float(-1)) is oo + assert oo/float(-1) == _ninf and (oo/float(-1)) is -oo + assert -oo/float(-1) == _inf and (-oo/float(-1)) is oo + assert oo + float(1) == _inf and (oo + float(1)) is oo + assert -oo + float(1) == _ninf and (-oo + float(1)) is -oo + assert oo - float(1) == _inf and (oo - float(1)) is oo + assert -oo - float(1) == _ninf and (-oo - float(1)) is -oo + assert float(1)*oo == _inf and (float(1)*oo) is oo + assert float(1)*-oo == _ninf and (float(1)*-oo) is -oo + assert float(1)/oo == 0 + assert float(1)/-oo == 0 + assert float(-1)*oo == _ninf and (float(-1)*oo) is -oo + assert float(-1)*-oo == _inf and (float(-1)*-oo) is oo + assert float(-1)/oo == 0 + assert float(-1)/-oo == 0 + assert float(1) + oo is oo + assert float(1) + -oo is -oo + assert float(1) - oo is -oo + assert float(1) - -oo is oo + assert oo == float(oo) + assert (oo != float(oo)) is False + assert type(float(oo)) is float + assert -oo == float(-oo) + assert (-oo != float(-oo)) is False + assert type(float(-oo)) is float + + assert Float('nan') is nan + assert nan*1.0 is nan + assert -1.0*nan is nan + assert nan*oo is nan + assert nan*-oo is nan + assert nan/oo is nan + assert nan/-oo is nan + assert nan + oo is nan + assert nan + -oo is nan + assert nan - oo is nan + assert nan - -oo is nan + assert -oo * S.Zero is nan + + assert oo*nan is nan + assert -oo*nan is nan + assert oo/nan is nan + assert -oo/nan is nan + assert oo + nan is nan + assert -oo + nan is nan + assert oo - nan is nan + assert -oo - nan is nan + assert S.Zero * oo is nan + assert oo.is_Rational is False + assert isinstance(oo, Rational) is False + + assert S.One/oo == 0 + assert -S.One/oo == 0 + assert S.One/-oo == 0 + assert -S.One/-oo == 0 + assert S.One*oo is oo + assert -S.One*oo is -oo + assert S.One*-oo is -oo + assert -S.One*-oo is oo + assert S.One/nan is nan + assert S.One - -oo is oo + assert S.One + nan is nan + assert S.One - nan is nan + assert nan - S.One is nan + assert nan/S.One is nan + assert -oo - S.One is -oo + + +def test_Infinity_2(): + x = Symbol('x') + assert oo*x != oo + assert oo*(pi - 1) is oo + assert oo*(1 - pi) is -oo + + assert (-oo)*x != -oo + assert (-oo)*(pi - 1) is -oo + assert (-oo)*(1 - pi) is oo + + assert (-1)**S.NaN is S.NaN + assert oo - _inf is S.NaN + assert oo + _ninf is S.NaN + assert oo*0 is S.NaN + assert oo/_inf is S.NaN + assert oo/_ninf is S.NaN + assert oo**S.NaN is S.NaN + assert -oo + _inf is S.NaN + assert -oo - _ninf is S.NaN + assert -oo*S.NaN is S.NaN + assert -oo*0 is S.NaN + assert -oo/_inf is S.NaN + assert -oo/_ninf is S.NaN + assert -oo/S.NaN is S.NaN + assert abs(-oo) is oo + assert all((-oo)**i is S.NaN for i in (oo, -oo, S.NaN)) + assert (-oo)**3 is -oo + assert (-oo)**2 is oo + assert abs(S.ComplexInfinity) is oo + + +def test_Mul_Infinity_Zero(): + assert Float(0)*_inf is nan + assert Float(0)*_ninf is nan + assert Float(0)*_inf is nan + assert Float(0)*_ninf is nan + assert _inf*Float(0) is nan + assert _ninf*Float(0) is nan + assert _inf*Float(0) is nan + assert _ninf*Float(0) is nan + + +def test_Div_By_Zero(): + assert 1/S.Zero is zoo + assert 1/Float(0) is zoo + assert 0/S.Zero is nan + assert 0/Float(0) is nan + assert S.Zero/0 is nan + assert Float(0)/0 is nan + assert -1/S.Zero is zoo + assert -1/Float(0) is zoo + + +@_both_exp_pow +def test_Infinity_inequations(): + assert oo > pi + assert not (oo < pi) + assert exp(-3) < oo + + assert _inf > pi + assert not (_inf < pi) + assert exp(-3) < _inf + + raises(TypeError, lambda: oo < I) + raises(TypeError, lambda: oo <= I) + raises(TypeError, lambda: oo > I) + raises(TypeError, lambda: oo >= I) + raises(TypeError, lambda: -oo < I) + raises(TypeError, lambda: -oo <= I) + raises(TypeError, lambda: -oo > I) + raises(TypeError, lambda: -oo >= I) + + raises(TypeError, lambda: I < oo) + raises(TypeError, lambda: I <= oo) + raises(TypeError, lambda: I > oo) + raises(TypeError, lambda: I >= oo) + raises(TypeError, lambda: I < -oo) + raises(TypeError, lambda: I <= -oo) + raises(TypeError, lambda: I > -oo) + raises(TypeError, lambda: I >= -oo) + + assert oo > -oo and oo >= -oo + assert (oo < -oo) == False and (oo <= -oo) == False + assert -oo < oo and -oo <= oo + assert (-oo > oo) == False and (-oo >= oo) == False + + assert (oo < oo) == False # issue 7775 + assert (oo > oo) == False + assert (-oo > -oo) == False and (-oo < -oo) == False + assert oo >= oo and oo <= oo and -oo >= -oo and -oo <= -oo + assert (-oo < -_inf) == False + assert (oo > _inf) == False + assert -oo >= -_inf + assert oo <= _inf + + x = Symbol('x') + b = Symbol('b', finite=True, real=True) + assert (x < oo) == Lt(x, oo) # issue 7775 + assert b < oo and b > -oo and b <= oo and b >= -oo + assert oo > b and oo >= b and (oo < b) == False and (oo <= b) == False + assert (-oo > b) == False and (-oo >= b) == False and -oo < b and -oo <= b + assert (oo < x) == Lt(oo, x) and (oo > x) == Gt(oo, x) + assert (oo <= x) == Le(oo, x) and (oo >= x) == Ge(oo, x) + assert (-oo < x) == Lt(-oo, x) and (-oo > x) == Gt(-oo, x) + assert (-oo <= x) == Le(-oo, x) and (-oo >= x) == Ge(-oo, x) + + +def test_NaN(): + assert nan is nan + assert nan != 1 + assert 1*nan is nan + assert 1 != nan + assert -nan is nan + assert oo != Symbol("x")**3 + assert 2 + nan is nan + assert 3*nan + 2 is nan + assert -nan*3 is nan + assert nan + nan is nan + assert -nan + nan*(-5) is nan + assert 8/nan is nan + raises(TypeError, lambda: nan > 0) + raises(TypeError, lambda: nan < 0) + raises(TypeError, lambda: nan >= 0) + raises(TypeError, lambda: nan <= 0) + raises(TypeError, lambda: 0 < nan) + raises(TypeError, lambda: 0 > nan) + raises(TypeError, lambda: 0 <= nan) + raises(TypeError, lambda: 0 >= nan) + assert nan**0 == 1 # as per IEEE 754 + assert 1**nan is nan # IEEE 754 is not the best choice for symbolic work + # test Pow._eval_power's handling of NaN + assert Pow(nan, 0, evaluate=False)**2 == 1 + for n in (1, 1., S.One, S.NegativeOne, Float(1)): + assert n + nan is nan + assert n - nan is nan + assert nan + n is nan + assert nan - n is nan + assert n/nan is nan + assert nan/n is nan + + +def test_special_numbers(): + assert isinstance(S.NaN, Number) is True + assert isinstance(S.Infinity, Number) is True + assert isinstance(S.NegativeInfinity, Number) is True + + assert S.NaN.is_number is True + assert S.Infinity.is_number is True + assert S.NegativeInfinity.is_number is True + assert S.ComplexInfinity.is_number is True + + assert isinstance(S.NaN, Rational) is False + assert isinstance(S.Infinity, Rational) is False + assert isinstance(S.NegativeInfinity, Rational) is False + + assert S.NaN.is_rational is not True + assert S.Infinity.is_rational is not True + assert S.NegativeInfinity.is_rational is not True + + +def test_powers(): + assert integer_nthroot(1, 2) == (1, True) + assert integer_nthroot(1, 5) == (1, True) + assert integer_nthroot(2, 1) == (2, True) + assert integer_nthroot(2, 2) == (1, False) + assert integer_nthroot(2, 5) == (1, False) + assert integer_nthroot(4, 2) == (2, True) + assert integer_nthroot(123**25, 25) == (123, True) + assert integer_nthroot(123**25 + 1, 25) == (123, False) + assert integer_nthroot(123**25 - 1, 25) == (122, False) + assert integer_nthroot(1, 1) == (1, True) + assert integer_nthroot(0, 1) == (0, True) + assert integer_nthroot(0, 3) == (0, True) + assert integer_nthroot(10000, 1) == (10000, True) + assert integer_nthroot(4, 2) == (2, True) + assert integer_nthroot(16, 2) == (4, True) + assert integer_nthroot(26, 2) == (5, False) + assert integer_nthroot(1234567**7, 7) == (1234567, True) + assert integer_nthroot(1234567**7 + 1, 7) == (1234567, False) + assert integer_nthroot(1234567**7 - 1, 7) == (1234566, False) + b = 25**1000 + assert integer_nthroot(b, 1000) == (25, True) + assert integer_nthroot(b + 1, 1000) == (25, False) + assert integer_nthroot(b - 1, 1000) == (24, False) + c = 10**400 + c2 = c**2 + assert integer_nthroot(c2, 2) == (c, True) + assert integer_nthroot(c2 + 1, 2) == (c, False) + assert integer_nthroot(c2 - 1, 2) == (c - 1, False) + assert integer_nthroot(2, 10**10) == (1, False) + + p, r = integer_nthroot(int(factorial(10000)), 100) + assert p % (10**10) == 5322420655 + assert not r + + # Test that this is fast + assert integer_nthroot(2, 10**10) == (1, False) + + # output should be int if possible + assert type(integer_nthroot(2**61, 2)[0]) is int + + +def test_integer_nthroot_overflow(): + assert integer_nthroot(10**(50*50), 50) == (10**50, True) + assert integer_nthroot(10**100000, 10000) == (10**10, True) + + +def test_integer_log(): + raises(ValueError, lambda: integer_log(2, 1)) + raises(ValueError, lambda: integer_log(0, 2)) + raises(ValueError, lambda: integer_log(1.1, 2)) + raises(ValueError, lambda: integer_log(1, 2.2)) + + assert integer_log(1, 2) == (0, True) + assert integer_log(1, 3) == (0, True) + assert integer_log(2, 3) == (0, False) + assert integer_log(3, 3) == (1, True) + assert integer_log(3*2, 3) == (1, False) + assert integer_log(3**2, 3) == (2, True) + assert integer_log(3*4, 3) == (2, False) + assert integer_log(3**3, 3) == (3, True) + assert integer_log(27, 5) == (2, False) + assert integer_log(2, 3) == (0, False) + assert integer_log(-4, 2) == (2, False) + assert integer_log(-16, 4) == (0, False) + assert integer_log(-4, -2) == (2, False) + assert integer_log(4, -2) == (2, True) + assert integer_log(-8, -2) == (3, True) + assert integer_log(8, -2) == (3, False) + assert integer_log(-9, 3) == (0, False) + assert integer_log(-9, -3) == (2, False) + assert integer_log(9, -3) == (2, True) + assert integer_log(-27, -3) == (3, True) + assert integer_log(27, -3) == (3, False) + + +def test_isqrt(): + from math import sqrt as _sqrt + limit = 4503599761588223 + assert int(_sqrt(limit)) == integer_nthroot(limit, 2)[0] + assert int(_sqrt(limit + 1)) != integer_nthroot(limit + 1, 2)[0] + assert isqrt(limit + 1) == integer_nthroot(limit + 1, 2)[0] + assert isqrt(limit + S.Half) == integer_nthroot(limit, 2)[0] + assert isqrt(limit + 1 + S.Half) == integer_nthroot(limit + 1, 2)[0] + assert isqrt(limit + 2 + S.Half) == integer_nthroot(limit + 2, 2)[0] + + # Regression tests for https://github.com/sympy/sympy/issues/17034 + assert isqrt(4503599761588224) == 67108864 + assert isqrt(9999999999999999) == 99999999 + + # Other corner cases, especially involving non-integers. + raises(ValueError, lambda: isqrt(-1)) + raises(ValueError, lambda: isqrt(-10**1000)) + raises(ValueError, lambda: isqrt(Rational(-1, 2))) + + tiny = Rational(1, 10**1000) + raises(ValueError, lambda: isqrt(-tiny)) + assert isqrt(1-tiny) == 0 + assert isqrt(4503599761588224-tiny) == 67108864 + assert isqrt(10**100 - tiny) == 10**50 - 1 + + +def test_powers_Integer(): + """Test Integer._eval_power""" + # check infinity + assert S.One ** S.Infinity is S.NaN + assert S.NegativeOne** S.Infinity is S.NaN + assert S(2) ** S.Infinity is S.Infinity + assert S(-2)** S.Infinity == zoo + assert S(0) ** S.Infinity is S.Zero + + # check Nan + assert S.One ** S.NaN is S.NaN + assert S.NegativeOne ** S.NaN is S.NaN + + # check for exact roots + assert S.NegativeOne ** Rational(6, 5) == - (-1)**(S.One/5) + assert sqrt(S(4)) == 2 + assert sqrt(S(-4)) == I * 2 + assert S(16) ** Rational(1, 4) == 2 + assert S(-16) ** Rational(1, 4) == 2 * (-1)**Rational(1, 4) + assert S(9) ** Rational(3, 2) == 27 + assert S(-9) ** Rational(3, 2) == -27*I + assert S(27) ** Rational(2, 3) == 9 + assert S(-27) ** Rational(2, 3) == 9 * (S.NegativeOne ** Rational(2, 3)) + assert (-2) ** Rational(-2, 1) == Rational(1, 4) + + # not exact roots + assert sqrt(-3) == I*sqrt(3) + assert (3) ** (Rational(3, 2)) == 3 * sqrt(3) + assert (-3) ** (Rational(3, 2)) == - 3 * sqrt(-3) + assert (-3) ** (Rational(5, 2)) == 9 * I * sqrt(3) + assert (-3) ** (Rational(7, 2)) == - I * 27 * sqrt(3) + assert (2) ** (Rational(3, 2)) == 2 * sqrt(2) + assert (2) ** (Rational(-3, 2)) == sqrt(2) / 4 + assert (81) ** (Rational(2, 3)) == 9 * (S(3) ** (Rational(2, 3))) + assert (-81) ** (Rational(2, 3)) == 9 * (S(-3) ** (Rational(2, 3))) + assert (-3) ** Rational(-7, 3) == \ + -(-1)**Rational(2, 3)*3**Rational(2, 3)/27 + assert (-3) ** Rational(-2, 3) == \ + -(-1)**Rational(1, 3)*3**Rational(1, 3)/3 + + # join roots + assert sqrt(6) + sqrt(24) == 3*sqrt(6) + assert sqrt(2) * sqrt(3) == sqrt(6) + + # separate symbols & constansts + x = Symbol("x") + assert sqrt(49 * x) == 7 * sqrt(x) + assert sqrt((3 - sqrt(pi)) ** 2) == 3 - sqrt(pi) + + # check that it is fast for big numbers + assert (2**64 + 1) ** Rational(4, 3) + assert (2**64 + 1) ** Rational(17, 25) + + # negative rational power and negative base + assert (-3) ** Rational(-7, 3) == \ + -(-1)**Rational(2, 3)*3**Rational(2, 3)/27 + assert (-3) ** Rational(-2, 3) == \ + -(-1)**Rational(1, 3)*3**Rational(1, 3)/3 + assert (-2) ** Rational(-10, 3) == \ + (-1)**Rational(2, 3)*2**Rational(2, 3)/16 + assert abs(Pow(-2, Rational(-10, 3)).n() - + Pow(-2, Rational(-10, 3), evaluate=False).n()) < 1e-16 + + # negative base and rational power with some simplification + assert (-8) ** Rational(2, 5) == \ + 2*(-1)**Rational(2, 5)*2**Rational(1, 5) + assert (-4) ** Rational(9, 5) == \ + -8*(-1)**Rational(4, 5)*2**Rational(3, 5) + + assert S(1234).factors() == {617: 1, 2: 1} + assert Rational(2*3, 3*5*7).factors() == {2: 1, 5: -1, 7: -1} + + # test that eval_power factors numbers bigger than + # the current limit in factor_trial_division (2**15) + from sympy.ntheory.generate import nextprime + n = nextprime(2**15) + assert sqrt(n**2) == n + assert sqrt(n**3) == n*sqrt(n) + assert sqrt(4*n) == 2*sqrt(n) + + # check that factors of base with powers sharing gcd with power are removed + assert (2**4*3)**Rational(1, 6) == 2**Rational(2, 3)*3**Rational(1, 6) + assert (2**4*3)**Rational(5, 6) == 8*2**Rational(1, 3)*3**Rational(5, 6) + + # check that bases sharing a gcd are exptracted + assert 2**Rational(1, 3)*3**Rational(1, 4)*6**Rational(1, 5) == \ + 2**Rational(8, 15)*3**Rational(9, 20) + assert sqrt(8)*24**Rational(1, 3)*6**Rational(1, 5) == \ + 4*2**Rational(7, 10)*3**Rational(8, 15) + assert sqrt(8)*(-24)**Rational(1, 3)*(-6)**Rational(1, 5) == \ + 4*(-3)**Rational(8, 15)*2**Rational(7, 10) + assert 2**Rational(1, 3)*2**Rational(8, 9) == 2*2**Rational(2, 9) + assert 2**Rational(2, 3)*6**Rational(1, 3) == 2*3**Rational(1, 3) + assert 2**Rational(2, 3)*6**Rational(8, 9) == \ + 2*2**Rational(5, 9)*3**Rational(8, 9) + assert (-2)**Rational(2, S(3))*(-4)**Rational(1, S(3)) == -2*2**Rational(1, 3) + assert 3*Pow(3, 2, evaluate=False) == 3**3 + assert 3*Pow(3, Rational(-1, 3), evaluate=False) == 3**Rational(2, 3) + assert (-2)**Rational(1, 3)*(-3)**Rational(1, 4)*(-5)**Rational(5, 6) == \ + -(-1)**Rational(5, 12)*2**Rational(1, 3)*3**Rational(1, 4) * \ + 5**Rational(5, 6) + + assert Integer(-2)**Symbol('', even=True) == \ + Integer(2)**Symbol('', even=True) + assert (-1)**Float(.5) == 1.0*I + + +def test_powers_Rational(): + """Test Rational._eval_power""" + # check infinity + assert S.Half ** S.Infinity == 0 + assert Rational(3, 2) ** S.Infinity is S.Infinity + assert Rational(-1, 2) ** S.Infinity == 0 + assert Rational(-3, 2) ** S.Infinity == zoo + + # check Nan + assert Rational(3, 4) ** S.NaN is S.NaN + assert Rational(-2, 3) ** S.NaN is S.NaN + + # exact roots on numerator + assert sqrt(Rational(4, 3)) == 2 * sqrt(3) / 3 + assert Rational(4, 3) ** Rational(3, 2) == 8 * sqrt(3) / 9 + assert sqrt(Rational(-4, 3)) == I * 2 * sqrt(3) / 3 + assert Rational(-4, 3) ** Rational(3, 2) == - I * 8 * sqrt(3) / 9 + assert Rational(27, 2) ** Rational(1, 3) == 3 * (2 ** Rational(2, 3)) / 2 + assert Rational(5**3, 8**3) ** Rational(4, 3) == Rational(5**4, 8**4) + + # exact root on denominator + assert sqrt(Rational(1, 4)) == S.Half + assert sqrt(Rational(1, -4)) == I * S.Half + assert sqrt(Rational(3, 4)) == sqrt(3) / 2 + assert sqrt(Rational(3, -4)) == I * sqrt(3) / 2 + assert Rational(5, 27) ** Rational(1, 3) == (5 ** Rational(1, 3)) / 3 + + # not exact roots + assert sqrt(S.Half) == sqrt(2) / 2 + assert sqrt(Rational(-4, 7)) == I * sqrt(Rational(4, 7)) + assert Rational(-3, 2)**Rational(-7, 3) == \ + -4*(-1)**Rational(2, 3)*2**Rational(1, 3)*3**Rational(2, 3)/27 + assert Rational(-3, 2)**Rational(-2, 3) == \ + -(-1)**Rational(1, 3)*2**Rational(2, 3)*3**Rational(1, 3)/3 + assert Rational(-3, 2)**Rational(-10, 3) == \ + 8*(-1)**Rational(2, 3)*2**Rational(1, 3)*3**Rational(2, 3)/81 + assert abs(Pow(Rational(-2, 3), Rational(-7, 4)).n() - + Pow(Rational(-2, 3), Rational(-7, 4), evaluate=False).n()) < 1e-16 + + # negative integer power and negative rational base + assert Rational(-2, 3) ** Rational(-2, 1) == Rational(9, 4) + + a = Rational(1, 10) + assert a**Float(a, 2) == Float(a, 2)**Float(a, 2) + assert Rational(-2, 3)**Symbol('', even=True) == \ + Rational(2, 3)**Symbol('', even=True) + + +def test_powers_Float(): + assert str((S('-1/10')**S('3/10')).n()) == str(Float(-.1)**(.3)) + + +def test_lshift_Integer(): + assert Integer(0) << Integer(2) == Integer(0) + assert Integer(0) << 2 == Integer(0) + assert 0 << Integer(2) == Integer(0) + + assert Integer(0b11) << Integer(0) == Integer(0b11) + assert Integer(0b11) << 0 == Integer(0b11) + assert 0b11 << Integer(0) == Integer(0b11) + + assert Integer(0b11) << Integer(2) == Integer(0b11 << 2) + assert Integer(0b11) << 2 == Integer(0b11 << 2) + assert 0b11 << Integer(2) == Integer(0b11 << 2) + + assert Integer(-0b11) << Integer(2) == Integer(-0b11 << 2) + assert Integer(-0b11) << 2 == Integer(-0b11 << 2) + assert -0b11 << Integer(2) == Integer(-0b11 << 2) + + raises(TypeError, lambda: Integer(2) << 0.0) + raises(TypeError, lambda: 0.0 << Integer(2)) + raises(ValueError, lambda: Integer(1) << Integer(-1)) + + +def test_rshift_Integer(): + assert Integer(0) >> Integer(2) == Integer(0) + assert Integer(0) >> 2 == Integer(0) + assert 0 >> Integer(2) == Integer(0) + + assert Integer(0b11) >> Integer(0) == Integer(0b11) + assert Integer(0b11) >> 0 == Integer(0b11) + assert 0b11 >> Integer(0) == Integer(0b11) + + assert Integer(0b11) >> Integer(2) == Integer(0) + assert Integer(0b11) >> 2 == Integer(0) + assert 0b11 >> Integer(2) == Integer(0) + + assert Integer(-0b11) >> Integer(2) == Integer(-1) + assert Integer(-0b11) >> 2 == Integer(-1) + assert -0b11 >> Integer(2) == Integer(-1) + + assert Integer(0b1100) >> Integer(2) == Integer(0b1100 >> 2) + assert Integer(0b1100) >> 2 == Integer(0b1100 >> 2) + assert 0b1100 >> Integer(2) == Integer(0b1100 >> 2) + + assert Integer(-0b1100) >> Integer(2) == Integer(-0b1100 >> 2) + assert Integer(-0b1100) >> 2 == Integer(-0b1100 >> 2) + assert -0b1100 >> Integer(2) == Integer(-0b1100 >> 2) + + raises(TypeError, lambda: Integer(0b10) >> 0.0) + raises(TypeError, lambda: 0.0 >> Integer(2)) + raises(ValueError, lambda: Integer(1) >> Integer(-1)) + + +def test_and_Integer(): + assert Integer(0b01010101) & Integer(0b10101010) == Integer(0) + assert Integer(0b01010101) & 0b10101010 == Integer(0) + assert 0b01010101 & Integer(0b10101010) == Integer(0) + + assert Integer(0b01010101) & Integer(0b11011011) == Integer(0b01010001) + assert Integer(0b01010101) & 0b11011011 == Integer(0b01010001) + assert 0b01010101 & Integer(0b11011011) == Integer(0b01010001) + + assert -Integer(0b01010101) & Integer(0b11011011) == Integer(-0b01010101 & 0b11011011) + assert Integer(-0b01010101) & 0b11011011 == Integer(-0b01010101 & 0b11011011) + assert -0b01010101 & Integer(0b11011011) == Integer(-0b01010101 & 0b11011011) + + assert Integer(0b01010101) & -Integer(0b11011011) == Integer(0b01010101 & -0b11011011) + assert Integer(0b01010101) & -0b11011011 == Integer(0b01010101 & -0b11011011) + assert 0b01010101 & Integer(-0b11011011) == Integer(0b01010101 & -0b11011011) + + raises(TypeError, lambda: Integer(2) & 0.0) + raises(TypeError, lambda: 0.0 & Integer(2)) + + +def test_xor_Integer(): + assert Integer(0b01010101) ^ Integer(0b11111111) == Integer(0b10101010) + assert Integer(0b01010101) ^ 0b11111111 == Integer(0b10101010) + assert 0b01010101 ^ Integer(0b11111111) == Integer(0b10101010) + + assert Integer(0b01010101) ^ Integer(0b11011011) == Integer(0b10001110) + assert Integer(0b01010101) ^ 0b11011011 == Integer(0b10001110) + assert 0b01010101 ^ Integer(0b11011011) == Integer(0b10001110) + + assert -Integer(0b01010101) ^ Integer(0b11011011) == Integer(-0b01010101 ^ 0b11011011) + assert Integer(-0b01010101) ^ 0b11011011 == Integer(-0b01010101 ^ 0b11011011) + assert -0b01010101 ^ Integer(0b11011011) == Integer(-0b01010101 ^ 0b11011011) + + assert Integer(0b01010101) ^ -Integer(0b11011011) == Integer(0b01010101 ^ -0b11011011) + assert Integer(0b01010101) ^ -0b11011011 == Integer(0b01010101 ^ -0b11011011) + assert 0b01010101 ^ Integer(-0b11011011) == Integer(0b01010101 ^ -0b11011011) + + raises(TypeError, lambda: Integer(2) ^ 0.0) + raises(TypeError, lambda: 0.0 ^ Integer(2)) + + +def test_or_Integer(): + assert Integer(0b01010101) | Integer(0b10101010) == Integer(0b11111111) + assert Integer(0b01010101) | 0b10101010 == Integer(0b11111111) + assert 0b01010101 | Integer(0b10101010) == Integer(0b11111111) + + assert Integer(0b01010101) | Integer(0b11011011) == Integer(0b11011111) + assert Integer(0b01010101) | 0b11011011 == Integer(0b11011111) + assert 0b01010101 | Integer(0b11011011) == Integer(0b11011111) + + assert -Integer(0b01010101) | Integer(0b11011011) == Integer(-0b01010101 | 0b11011011) + assert Integer(-0b01010101) | 0b11011011 == Integer(-0b01010101 | 0b11011011) + assert -0b01010101 | Integer(0b11011011) == Integer(-0b01010101 | 0b11011011) + + assert Integer(0b01010101) | -Integer(0b11011011) == Integer(0b01010101 | -0b11011011) + assert Integer(0b01010101) | -0b11011011 == Integer(0b01010101 | -0b11011011) + assert 0b01010101 | Integer(-0b11011011) == Integer(0b01010101 | -0b11011011) + + raises(TypeError, lambda: Integer(2) | 0.0) + raises(TypeError, lambda: 0.0 | Integer(2)) + + +def test_invert_Integer(): + assert ~Integer(0b01010101) == Integer(-0b01010110) + assert ~Integer(0b01010101) == Integer(~0b01010101) + assert ~(~Integer(0b01010101)) == Integer(0b01010101) + + +def test_abs1(): + assert Rational(1, 6) != Rational(-1, 6) + assert abs(Rational(1, 6)) == abs(Rational(-1, 6)) + + +def test_accept_int(): + assert not Float(4) == 4 + assert Float(4) != 4 + assert Float(4) == 4.0 + + +def test_dont_accept_str(): + assert Float("0.2") != "0.2" + assert not (Float("0.2") == "0.2") + + +def test_int(): + a = Rational(5) + assert int(a) == 5 + a = Rational(9, 10) + assert int(a) == int(-a) == 0 + assert 1/(-1)**Rational(2, 3) == -(-1)**Rational(1, 3) + # issue 10368 + a = Rational(32442016954, 78058255275) + assert type(int(a)) is type(int(-a)) is int + + +def test_int_NumberSymbols(): + assert int(Catalan) == 0 + assert int(EulerGamma) == 0 + assert int(pi) == 3 + assert int(E) == 2 + assert int(GoldenRatio) == 1 + assert int(TribonacciConstant) == 1 + for i in [Catalan, E, EulerGamma, GoldenRatio, TribonacciConstant, pi]: + a, b = i.approximation_interval(Integer) + ia = int(i) + assert ia == a + assert isinstance(ia, int) + assert b == a + 1 + assert a.is_Integer and b.is_Integer + + +def test_real_bug(): + x = Symbol("x") + assert str(2.0*x*x) in ["(2.0*x)*x", "2.0*x**2", "2.00000000000000*x**2"] + assert str(2.1*x*x) != "(2.0*x)*x" + + +def test_bug_sqrt(): + assert ((sqrt(Rational(2)) + 1)*(sqrt(Rational(2)) - 1)).expand() == 1 + + +def test_pi_Pi(): + "Test that pi (instance) is imported, but Pi (class) is not" + from sympy import pi # noqa + with raises(ImportError): + from sympy import Pi # noqa + + +def test_no_len(): + # there should be no len for numbers + raises(TypeError, lambda: len(Rational(2))) + raises(TypeError, lambda: len(Rational(2, 3))) + raises(TypeError, lambda: len(Integer(2))) + + +def test_issue_3321(): + assert sqrt(Rational(1, 5)) == Rational(1, 5)**S.Half + assert 5 * sqrt(Rational(1, 5)) == sqrt(5) + + +def test_issue_3692(): + assert ((-1)**Rational(1, 6)).expand(complex=True) == I/2 + sqrt(3)/2 + assert ((-5)**Rational(1, 6)).expand(complex=True) == \ + 5**Rational(1, 6)*I/2 + 5**Rational(1, 6)*sqrt(3)/2 + assert ((-64)**Rational(1, 6)).expand(complex=True) == I + sqrt(3) + + +def test_issue_3423(): + x = Symbol("x") + assert sqrt(x - 1).as_base_exp() == (x - 1, S.Half) + assert sqrt(x - 1) != I*sqrt(1 - x) + + +def test_issue_3449(): + x = Symbol("x") + assert sqrt(x - 1).subs(x, 5) == 2 + + +def test_issue_13890(): + x = Symbol("x") + e = (-x/4 - S.One/12)**x - 1 + f = simplify(e) + a = Rational(9, 5) + assert abs(e.subs(x,a).evalf() - f.subs(x,a).evalf()) < 1e-15 + + +def test_Integer_factors(): + def F(i): + return Integer(i).factors() + + assert F(1) == {} + assert F(2) == {2: 1} + assert F(3) == {3: 1} + assert F(4) == {2: 2} + assert F(5) == {5: 1} + assert F(6) == {2: 1, 3: 1} + assert F(7) == {7: 1} + assert F(8) == {2: 3} + assert F(9) == {3: 2} + assert F(10) == {2: 1, 5: 1} + assert F(11) == {11: 1} + assert F(12) == {2: 2, 3: 1} + assert F(13) == {13: 1} + assert F(14) == {2: 1, 7: 1} + assert F(15) == {3: 1, 5: 1} + assert F(16) == {2: 4} + assert F(17) == {17: 1} + assert F(18) == {2: 1, 3: 2} + assert F(19) == {19: 1} + assert F(20) == {2: 2, 5: 1} + assert F(21) == {3: 1, 7: 1} + assert F(22) == {2: 1, 11: 1} + assert F(23) == {23: 1} + assert F(24) == {2: 3, 3: 1} + assert F(25) == {5: 2} + assert F(26) == {2: 1, 13: 1} + assert F(27) == {3: 3} + assert F(28) == {2: 2, 7: 1} + assert F(29) == {29: 1} + assert F(30) == {2: 1, 3: 1, 5: 1} + assert F(31) == {31: 1} + assert F(32) == {2: 5} + assert F(33) == {3: 1, 11: 1} + assert F(34) == {2: 1, 17: 1} + assert F(35) == {5: 1, 7: 1} + assert F(36) == {2: 2, 3: 2} + assert F(37) == {37: 1} + assert F(38) == {2: 1, 19: 1} + assert F(39) == {3: 1, 13: 1} + assert F(40) == {2: 3, 5: 1} + assert F(41) == {41: 1} + assert F(42) == {2: 1, 3: 1, 7: 1} + assert F(43) == {43: 1} + assert F(44) == {2: 2, 11: 1} + assert F(45) == {3: 2, 5: 1} + assert F(46) == {2: 1, 23: 1} + assert F(47) == {47: 1} + assert F(48) == {2: 4, 3: 1} + assert F(49) == {7: 2} + assert F(50) == {2: 1, 5: 2} + assert F(51) == {3: 1, 17: 1} + + +def test_Rational_factors(): + def F(p, q, visual=None): + return Rational(p, q).factors(visual=visual) + + assert F(2, 3) == {2: 1, 3: -1} + assert F(2, 9) == {2: 1, 3: -2} + assert F(2, 15) == {2: 1, 3: -1, 5: -1} + assert F(6, 10) == {3: 1, 5: -1} + + +def test_issue_4107(): + assert pi*(E + 10) + pi*(-E - 10) != 0 + assert pi*(E + 10**10) + pi*(-E - 10**10) != 0 + assert pi*(E + 10**20) + pi*(-E - 10**20) != 0 + assert pi*(E + 10**80) + pi*(-E - 10**80) != 0 + + assert (pi*(E + 10) + pi*(-E - 10)).expand() == 0 + assert (pi*(E + 10**10) + pi*(-E - 10**10)).expand() == 0 + assert (pi*(E + 10**20) + pi*(-E - 10**20)).expand() == 0 + assert (pi*(E + 10**80) + pi*(-E - 10**80)).expand() == 0 + + +def test_IntegerInteger(): + a = Integer(4) + b = Integer(a) + + assert a == b + + +def test_Rational_gcd_lcm_cofactors(): + assert Integer(4).gcd(2) == Integer(2) + assert Integer(4).lcm(2) == Integer(4) + assert Integer(4).gcd(Integer(2)) == Integer(2) + assert Integer(4).lcm(Integer(2)) == Integer(4) + a, b = 720**99911, 480**12342 + assert Integer(a).lcm(b) == a*b/Integer(a).gcd(b) + + assert Integer(4).gcd(3) == Integer(1) + assert Integer(4).lcm(3) == Integer(12) + assert Integer(4).gcd(Integer(3)) == Integer(1) + assert Integer(4).lcm(Integer(3)) == Integer(12) + + assert Rational(4, 3).gcd(2) == Rational(2, 3) + assert Rational(4, 3).lcm(2) == Integer(4) + assert Rational(4, 3).gcd(Integer(2)) == Rational(2, 3) + assert Rational(4, 3).lcm(Integer(2)) == Integer(4) + + assert Integer(4).gcd(Rational(2, 9)) == Rational(2, 9) + assert Integer(4).lcm(Rational(2, 9)) == Integer(4) + + assert Rational(4, 3).gcd(Rational(2, 9)) == Rational(2, 9) + assert Rational(4, 3).lcm(Rational(2, 9)) == Rational(4, 3) + assert Rational(4, 5).gcd(Rational(2, 9)) == Rational(2, 45) + assert Rational(4, 5).lcm(Rational(2, 9)) == Integer(4) + assert Rational(5, 9).lcm(Rational(3, 7)) == Rational(Integer(5).lcm(3),Integer(9).gcd(7)) + + assert Integer(4).cofactors(2) == (Integer(2), Integer(2), Integer(1)) + assert Integer(4).cofactors(Integer(2)) == \ + (Integer(2), Integer(2), Integer(1)) + + assert Integer(4).gcd(Float(2.0)) == Float(1.0) + assert Integer(4).lcm(Float(2.0)) == Float(8.0) + assert Integer(4).cofactors(Float(2.0)) == (Float(1.0), Float(4.0), Float(2.0)) + + assert S.Half.gcd(Float(2.0)) == Float(1.0) + assert S.Half.lcm(Float(2.0)) == Float(1.0) + assert S.Half.cofactors(Float(2.0)) == \ + (Float(1.0), Float(0.5), Float(2.0)) + + +def test_Float_gcd_lcm_cofactors(): + assert Float(2.0).gcd(Integer(4)) == Float(1.0) + assert Float(2.0).lcm(Integer(4)) == Float(8.0) + assert Float(2.0).cofactors(Integer(4)) == (Float(1.0), Float(2.0), Float(4.0)) + + assert Float(2.0).gcd(S.Half) == Float(1.0) + assert Float(2.0).lcm(S.Half) == Float(1.0) + assert Float(2.0).cofactors(S.Half) == \ + (Float(1.0), Float(2.0), Float(0.5)) + + +def test_issue_4611(): + assert abs(pi._evalf(50) - 3.14159265358979) < 1e-10 + assert abs(E._evalf(50) - 2.71828182845905) < 1e-10 + assert abs(Catalan._evalf(50) - 0.915965594177219) < 1e-10 + assert abs(EulerGamma._evalf(50) - 0.577215664901533) < 1e-10 + assert abs(GoldenRatio._evalf(50) - 1.61803398874989) < 1e-10 + assert abs(TribonacciConstant._evalf(50) - 1.83928675521416) < 1e-10 + + x = Symbol("x") + assert (pi + x).evalf() == pi.evalf() + x + assert (E + x).evalf() == E.evalf() + x + assert (Catalan + x).evalf() == Catalan.evalf() + x + assert (EulerGamma + x).evalf() == EulerGamma.evalf() + x + assert (GoldenRatio + x).evalf() == GoldenRatio.evalf() + x + assert (TribonacciConstant + x).evalf() == TribonacciConstant.evalf() + x + + +@conserve_mpmath_dps +def test_conversion_to_mpmath(): + assert mpmath.mpmathify(Integer(1)) == mpmath.mpf(1) + assert mpmath.mpmathify(S.Half) == mpmath.mpf(0.5) + assert mpmath.mpmathify(Float('1.23', 15)) == mpmath.mpf('1.23') + + assert mpmath.mpmathify(I) == mpmath.mpc(1j) + + assert mpmath.mpmathify(1 + 2*I) == mpmath.mpc(1 + 2j) + assert mpmath.mpmathify(1.0 + 2*I) == mpmath.mpc(1 + 2j) + assert mpmath.mpmathify(1 + 2.0*I) == mpmath.mpc(1 + 2j) + assert mpmath.mpmathify(1.0 + 2.0*I) == mpmath.mpc(1 + 2j) + assert mpmath.mpmathify(S.Half + S.Half*I) == mpmath.mpc(0.5 + 0.5j) + + assert mpmath.mpmathify(2*I) == mpmath.mpc(2j) + assert mpmath.mpmathify(2.0*I) == mpmath.mpc(2j) + assert mpmath.mpmathify(S.Half*I) == mpmath.mpc(0.5j) + + mpmath.mp.dps = 100 + assert mpmath.mpmathify(pi.evalf(100) + pi.evalf(100)*I) == mpmath.pi + mpmath.pi*mpmath.j + assert mpmath.mpmathify(pi.evalf(100)*I) == mpmath.pi*mpmath.j + + +def test_relational(): + # real + x = S(.1) + assert (x != cos) is True + assert (x == cos) is False + + # rational + x = Rational(1, 3) + assert (x != cos) is True + assert (x == cos) is False + + # integer defers to rational so these tests are omitted + + # number symbol + x = pi + assert (x != cos) is True + assert (x == cos) is False + + +def test_Integer_as_index(): + assert 'hello'[Integer(2):] == 'llo' + + +def test_Rational_int(): + assert int( Rational(7, 5)) == 1 + assert int( S.Half) == 0 + assert int(Rational(-1, 2)) == 0 + assert int(-Rational(7, 5)) == -1 + + +def test_zoo(): + b = Symbol('b', finite=True) + nz = Symbol('nz', nonzero=True) + p = Symbol('p', positive=True) + n = Symbol('n', negative=True) + im = Symbol('i', imaginary=True) + c = Symbol('c', complex=True) + pb = Symbol('pb', positive=True) + nb = Symbol('nb', negative=True) + imb = Symbol('ib', imaginary=True, finite=True) + for i in [I, S.Infinity, S.NegativeInfinity, S.Zero, S.One, S.Pi, S.Half, S(3), log(3), + b, nz, p, n, im, pb, nb, imb, c]: + if i.is_finite and (i.is_real or i.is_imaginary): + assert i + zoo is zoo + assert i - zoo is zoo + assert zoo + i is zoo + assert zoo - i is zoo + elif i.is_finite is not False: + assert (i + zoo).is_Add + assert (i - zoo).is_Add + assert (zoo + i).is_Add + assert (zoo - i).is_Add + else: + assert (i + zoo) is S.NaN + assert (i - zoo) is S.NaN + assert (zoo + i) is S.NaN + assert (zoo - i) is S.NaN + + if fuzzy_not(i.is_zero) and (i.is_extended_real or i.is_imaginary): + assert i*zoo is zoo + assert zoo*i is zoo + elif i.is_zero: + assert i*zoo is S.NaN + assert zoo*i is S.NaN + else: + assert (i*zoo).is_Mul + assert (zoo*i).is_Mul + + if fuzzy_not((1/i).is_zero) and (i.is_real or i.is_imaginary): + assert zoo/i is zoo + elif (1/i).is_zero: + assert zoo/i is S.NaN + elif i.is_zero: + assert zoo/i is zoo + else: + assert (zoo/i).is_Mul + + assert (I*oo).is_Mul # allow directed infinity + assert zoo + zoo is S.NaN + assert zoo * zoo is zoo + assert zoo - zoo is S.NaN + assert zoo/zoo is S.NaN + assert zoo**zoo is S.NaN + assert zoo**0 is S.One + assert zoo**2 is zoo + assert 1/zoo is S.Zero + + assert Mul.flatten([S.NegativeOne, oo, S(0)]) == ([S.NaN], [], None) + + +def test_issue_4122(): + x = Symbol('x', nonpositive=True) + assert oo + x is oo + x = Symbol('x', extended_nonpositive=True) + assert (oo + x).is_Add + x = Symbol('x', finite=True) + assert (oo + x).is_Add # x could be imaginary + x = Symbol('x', nonnegative=True) + assert oo + x is oo + x = Symbol('x', extended_nonnegative=True) + assert oo + x is oo + x = Symbol('x', finite=True, real=True) + assert oo + x is oo + + # similarly for negative infinity + x = Symbol('x', nonnegative=True) + assert -oo + x is -oo + x = Symbol('x', extended_nonnegative=True) + assert (-oo + x).is_Add + x = Symbol('x', finite=True) + assert (-oo + x).is_Add + x = Symbol('x', nonpositive=True) + assert -oo + x is -oo + x = Symbol('x', extended_nonpositive=True) + assert -oo + x is -oo + x = Symbol('x', finite=True, real=True) + assert -oo + x is -oo + + +def test_GoldenRatio_expand(): + assert GoldenRatio.expand(func=True) == S.Half + sqrt(5)/2 + + +def test_TribonacciConstant_expand(): + assert TribonacciConstant.expand(func=True) == \ + (1 + cbrt(19 - 3*sqrt(33)) + cbrt(19 + 3*sqrt(33))) / 3 + + +def test_as_content_primitive(): + assert S.Zero.as_content_primitive() == (1, 0) + assert S.Half.as_content_primitive() == (S.Half, 1) + assert (Rational(-1, 2)).as_content_primitive() == (S.Half, -1) + assert S(3).as_content_primitive() == (3, 1) + assert S(3.1).as_content_primitive() == (1, 3.1) + + +def test_hashing_sympy_integers(): + # Test for issue 5072 + assert {Integer(3)} == {int(3)} + assert hash(Integer(4)) == hash(int(4)) + + +def test_rounding_issue_4172(): + assert int((E**100).round()) == \ + 26881171418161354484126255515800135873611119 + assert int((pi**100).round()) == \ + 51878483143196131920862615246303013562686760680406 + assert int((Rational(1)/EulerGamma**100).round()) == \ + 734833795660954410469466 + + +@XFAIL +def test_mpmath_issues(): + from mpmath.libmp.libmpf import _normalize + import mpmath.libmp as mlib + rnd = mlib.round_nearest + mpf = (0, int(0), -123, -1, 53, rnd) # nan + assert _normalize(mpf, 53) != (0, int(0), 0, 0) + mpf = (0, int(0), -456, -2, 53, rnd) # +inf + assert _normalize(mpf, 53) != (0, int(0), 0, 0) + mpf = (1, int(0), -789, -3, 53, rnd) # -inf + assert _normalize(mpf, 53) != (0, int(0), 0, 0) + + from mpmath.libmp.libmpf import fnan + assert mlib.mpf_eq(fnan, fnan) + + +def test_Catalan_EulerGamma_prec(): + n = GoldenRatio + f = Float(n.n(), 5) + assert f._mpf_ == (0, int(212079), -17, 18) + assert f._prec == 20 + assert n._as_mpf_val(20) == f._mpf_ + + n = EulerGamma + f = Float(n.n(), 5) + assert f._mpf_ == (0, int(302627), -19, 19) + assert f._prec == 20 + assert n._as_mpf_val(20) == f._mpf_ + + +def test_Catalan_rewrite(): + k = Dummy('k', integer=True, nonnegative=True) + assert Catalan.rewrite(Sum).dummy_eq( + Sum((-1)**k/(2*k + 1)**2, (k, 0, oo))) + assert Catalan.rewrite() == Catalan + + +def test_bool_eq(): + assert 0 == False + assert S(0) == False + assert S(0) != S.false + assert 1 == True + assert S.One == True + assert S.One != S.true + + +def test_Float_eq(): + # Floats with different precision should not compare equal + assert Float(.5, 10) != Float(.5, 11) != Float(.5, 1) + # but floats that aren't exact in base-2 still + # don't compare the same because they have different + # underlying mpf values + assert Float(.12, 3) != Float(.12, 4) + assert Float(.12, 3) != .12 + assert 0.12 != Float(.12, 3) + assert Float('.12', 22) != .12 + # issue 11707 + # but Float/Rational -- except for 0 -- + # are exact so Rational(x) = Float(y) only if + # Rational(x) == Rational(Float(y)) + assert Float('1.1') != Rational(11, 10) + assert Rational(11, 10) != Float('1.1') + # coverage + assert not Float(3) == 2 + assert not Float(3) == Float(2) + assert not Float(3) == 3 + assert not Float(2**2) == S.Half + assert Float(2**2) == 4.0 + assert not Float(2**-2) == 1 + assert Float(2**-1) == 0.5 + assert not Float(2*3) == 3 + assert not Float(2*3) == 0.5 + assert Float(2*3) == 6.0 + assert not Float(2*3) == 6 + assert not Float(2*3) == 8 + assert not Float(.75) == Rational(3, 4) + assert Float(.75) == 0.75 + assert Float(5/18) == 5/18 + # 4473 + assert Float(2.) != 3 + assert not Float((0,1,-3)) == S.One/8 + assert Float((0,1,-3)) == 1/8 + assert Float((0,1,-3)) != S.One/9 + # 16196 + assert not 2 == Float(2) # unlike Python + assert t**2 != t**2.0 + + +def test_issue_6640(): + from mpmath.libmp.libmpf import finf, fninf + # fnan is not included because Float no longer returns fnan, + # but otherwise, the same sort of test could apply + assert Float(finf).is_zero is False + assert Float(fninf).is_zero is False + assert bool(Float(0)) is False + + +def test_issue_6349(): + assert Float('23.e3', '')._prec == 10 + assert Float('23e3', '')._prec == 20 + assert Float('23000', '')._prec == 20 + assert Float('-23000', '')._prec == 20 + + +def test_mpf_norm(): + assert mpf_norm((1, 0, 1, 0), 10) == mpf('0')._mpf_ + assert Float._new((1, 0, 1, 0), 10)._mpf_ == mpf('0')._mpf_ + + +def test_latex(): + assert latex(pi) == r"\pi" + assert latex(E) == r"e" + assert latex(GoldenRatio) == r"\phi" + assert latex(TribonacciConstant) == r"\text{TribonacciConstant}" + assert latex(EulerGamma) == r"\gamma" + assert latex(oo) == r"\infty" + assert latex(-oo) == r"-\infty" + assert latex(zoo) == r"\tilde{\infty}" + assert latex(nan) == r"\text{NaN}" + assert latex(I) == r"i" + + +def test_issue_7742(): + assert -oo % 1 is nan + + +def test_simplify_AlgebraicNumber(): + A = AlgebraicNumber + e = 3**(S.One/6)*(3 + (135 + 78*sqrt(3))**Rational(2, 3))/(45 + 26*sqrt(3))**(S.One/3) + assert simplify(A(e)) == A(12) # wester test_C20 + + e = (41 + 29*sqrt(2))**(S.One/5) + assert simplify(A(e)) == A(1 + sqrt(2)) # wester test_C21 + + e = (3 + 4*I)**Rational(3, 2) + assert simplify(A(e)) == A(2 + 11*I) # issue 4401 + + +def test_Float_idempotence(): + x = Float('1.23', '') + y = Float(x) + z = Float(x, 15) + assert same_and_same_prec(y, x) + assert not same_and_same_prec(z, x) + x = Float(10**20) + y = Float(x) + z = Float(x, 15) + assert same_and_same_prec(y, x) + assert not same_and_same_prec(z, x) + + +def test_comp1(): + # sqrt(2) = 1.414213 5623730950... + a = sqrt(2).n(7) + assert comp(a, 1.4142129) is False + assert comp(a, 1.4142130) + # ... + assert comp(a, 1.4142141) + assert comp(a, 1.4142142) is False + assert comp(sqrt(2).n(2), '1.4') + assert comp(sqrt(2).n(2), Float(1.4, 2), '') + assert comp(sqrt(2).n(2), 1.4, '') + assert comp(sqrt(2).n(2), Float(1.4, 3), '') is False + assert comp(sqrt(2) + sqrt(3)*I, 1.4 + 1.7*I, .1) + assert not comp(sqrt(2) + sqrt(3)*I, (1.5 + 1.7*I)*0.89, .1) + assert comp(sqrt(2) + sqrt(3)*I, (1.5 + 1.7*I)*0.90, .1) + assert comp(sqrt(2) + sqrt(3)*I, (1.5 + 1.7*I)*1.07, .1) + assert not comp(sqrt(2) + sqrt(3)*I, (1.5 + 1.7*I)*1.08, .1) + assert [(i, j) + for i in range(130, 150) + for j in range(170, 180) + if comp((sqrt(2)+ I*sqrt(3)).n(3), i/100. + I*j/100.)] == [ + (141, 173), (142, 173)] + raises(ValueError, lambda: comp(t, '1')) + raises(ValueError, lambda: comp(t, 1)) + assert comp(0, 0.0) + assert comp(.5, S.Half) + assert comp(2 + sqrt(2), 2.0 + sqrt(2)) + assert not comp(0, 1) + assert not comp(2, sqrt(2)) + assert not comp(2 + I, 2.0 + sqrt(2)) + assert not comp(2.0 + sqrt(2), 2 + I) + assert not comp(2.0 + sqrt(2), sqrt(3)) + assert comp(1/pi.n(4), 0.3183, 1e-5) + assert not comp(1/pi.n(4), 0.3183, 8e-6) + + +def test_issue_9491(): + assert oo**zoo is nan + + +def test_issue_10063(): + assert 2**Float(3) == Float(8) + + +def test_issue_10020(): + assert oo**I is S.NaN + assert oo**(1 + I) is S.ComplexInfinity + assert oo**(-1 + I) is S.Zero + assert (-oo)**I is S.NaN + assert (-oo)**(-1 + I) is S.Zero + assert oo**t == Pow(oo, t, evaluate=False) + assert (-oo)**t == Pow(-oo, t, evaluate=False) + + +def test_invert_numbers(): + assert S(2).invert(5) == 3 + assert S(2).invert(Rational(5, 2)) == S.Half + assert S(2).invert(5.) == S.Half + assert S(2).invert(S(5)) == 3 + assert S(2.).invert(5) == 0.5 + assert S(sqrt(2)).invert(5) == 1/sqrt(2) + assert S(sqrt(2)).invert(sqrt(3)) == 1/sqrt(2) + + +def test_mod_inverse(): + assert mod_inverse(3, 11) == 4 + assert mod_inverse(5, 11) == 9 + assert mod_inverse(21124921, 521512) == 7713 + assert mod_inverse(124215421, 5125) == 2981 + assert mod_inverse(214, 12515) == 1579 + assert mod_inverse(5823991, 3299) == 1442 + assert mod_inverse(123, 44) == 39 + assert mod_inverse(2, 5) == 3 + assert mod_inverse(-2, 5) == 2 + assert mod_inverse(2, -5) == -2 + assert mod_inverse(-2, -5) == -3 + assert mod_inverse(-3, -7) == -5 + x = Symbol('x') + assert S(2).invert(x) == S.Half + raises(TypeError, lambda: mod_inverse(2, x)) + raises(ValueError, lambda: mod_inverse(2, S.Half)) + raises(ValueError, lambda: mod_inverse(2, cos(1)**2 + sin(1)**2)) + + +def test_golden_ratio_rewrite_as_sqrt(): + assert GoldenRatio.rewrite(sqrt) == S.Half + sqrt(5)*S.Half + + +def test_tribonacci_constant_rewrite_as_sqrt(): + assert TribonacciConstant.rewrite(sqrt) == \ + (1 + cbrt(19 - 3*sqrt(33)) + cbrt(19 + 3*sqrt(33))) / 3 + + +def test_comparisons_with_unknown_type(): + class Foo: + """ + Class that is unaware of Basic, and relies on both classes returning + the NotImplemented singleton for equivalence to evaluate to False. + + """ + + ni, nf, nr = Integer(3), Float(1.0), Rational(1, 3) + foo = Foo() + + for n in ni, nf, nr, oo, -oo, zoo, nan: + assert n != foo + assert foo != n + assert not n == foo + assert not foo == n + raises(TypeError, lambda: n < foo) + raises(TypeError, lambda: foo > n) + raises(TypeError, lambda: n > foo) + raises(TypeError, lambda: foo < n) + raises(TypeError, lambda: n <= foo) + raises(TypeError, lambda: foo >= n) + raises(TypeError, lambda: n >= foo) + raises(TypeError, lambda: foo <= n) + + class Bar: + """ + Class that considers itself equal to any instance of Number except + infinities and nans, and relies on SymPy types returning the + NotImplemented singleton for symmetric equality relations. + + """ + def __eq__(self, other): + if other in (oo, -oo, zoo, nan): + return False + if isinstance(other, Number): + return True + return NotImplemented + + def __ne__(self, other): + return not self == other + + bar = Bar() + + for n in ni, nf, nr: + assert n == bar + assert bar == n + assert not n != bar + assert not bar != n + + for n in oo, -oo, zoo, nan: + assert n != bar + assert bar != n + assert not n == bar + assert not bar == n + + for n in ni, nf, nr, oo, -oo, zoo, nan: + raises(TypeError, lambda: n < bar) + raises(TypeError, lambda: bar > n) + raises(TypeError, lambda: n > bar) + raises(TypeError, lambda: bar < n) + raises(TypeError, lambda: n <= bar) + raises(TypeError, lambda: bar >= n) + raises(TypeError, lambda: n >= bar) + raises(TypeError, lambda: bar <= n) + + +def test_NumberSymbol_comparison(): + from sympy.core.tests.test_relational import rel_check + rpi = Rational('905502432259640373/288230376151711744') + fpi = Float(float(pi)) + assert rel_check(rpi, fpi) + + +def test_Integer_precision(): + # Make sure Integer inputs for keyword args work + assert Float('1.0', dps=Integer(15))._prec == 53 + assert Float('1.0', precision=Integer(15))._prec == 15 + assert type(Float('1.0', precision=Integer(15))._prec) == int + assert sympify(srepr(Float('1.0', precision=15))) == Float('1.0', precision=15) + + +def test_numpy_to_float(): + from sympy.testing.pytest import skip + from sympy.external import import_module + np = import_module('numpy') + if not np: + skip('numpy not installed. Abort numpy tests.') + + def check_prec_and_relerr(npval, ratval): + prec = np.finfo(npval).nmant + 1 + x = Float(npval) + assert x._prec == prec + y = Float(ratval, precision=prec) + assert abs((x - y)/y) < 2**(-(prec + 1)) + + check_prec_and_relerr(np.float16(2.0/3), Rational(2, 3)) + check_prec_and_relerr(np.float32(2.0/3), Rational(2, 3)) + check_prec_and_relerr(np.float64(2.0/3), Rational(2, 3)) + # extended precision, on some arch/compilers: + x = np.longdouble(2)/3 + check_prec_and_relerr(x, Rational(2, 3)) + y = Float(x, precision=10) + assert same_and_same_prec(y, Float(Rational(2, 3), precision=10)) + + raises(TypeError, lambda: Float(np.complex64(1+2j))) + raises(TypeError, lambda: Float(np.complex128(1+2j))) + + +def test_Integer_ceiling_floor(): + a = Integer(4) + + assert a.floor() == a + assert a.ceiling() == a + + +def test_ComplexInfinity(): + assert zoo.floor() is zoo + assert zoo.ceiling() is zoo + assert zoo**zoo is S.NaN + + +def test_Infinity_floor_ceiling_power(): + assert oo.floor() is oo + assert oo.ceiling() is oo + assert oo**S.NaN is S.NaN + assert oo**zoo is S.NaN + + +def test_One_power(): + assert S.One**12 is S.One + assert S.NegativeOne**S.NaN is S.NaN + + +def test_NegativeInfinity(): + assert (-oo).floor() is -oo + assert (-oo).ceiling() is -oo + assert (-oo)**11 is -oo + assert (-oo)**12 is oo + + +def test_issue_6133(): + raises(TypeError, lambda: (-oo < None)) + raises(TypeError, lambda: (S(-2) < None)) + raises(TypeError, lambda: (oo < None)) + raises(TypeError, lambda: (oo > None)) + raises(TypeError, lambda: (S(2) < None)) + + +def test_abc(): + x = numbers.Float(5) + assert(isinstance(x, nums.Number)) + assert(isinstance(x, numbers.Number)) + assert(isinstance(x, nums.Real)) + y = numbers.Rational(1, 3) + assert(isinstance(y, nums.Number)) + assert(y.numerator == 1) + assert(y.denominator == 3) + assert(isinstance(y, nums.Rational)) + z = numbers.Integer(3) + assert(isinstance(z, nums.Number)) + assert(isinstance(z, numbers.Number)) + assert(isinstance(z, nums.Rational)) + assert(isinstance(z, numbers.Rational)) + assert(isinstance(z, nums.Integral)) + + +def test_floordiv(): + assert S(2)//S.Half == 4 + + +def test_negation(): + assert -S.Zero is S.Zero + assert -Float(0) is not S.Zero and -Float(0) == 0.0 + + +def test_exponentiation_of_0(): + x = Symbol('x') + assert 0**-x == zoo**x + assert unchanged(Pow, 0, x) + x = Symbol('x', zero=True) + assert 0**-x == S.One + assert 0**x == S.One + + +def test_int_valued(): + x = Symbol('x') + assert int_valued(x) == False + assert int_valued(S.Half) == False + assert int_valued(S.One) == True + assert int_valued(Float(1)) == True + assert int_valued(Float(1.1)) == False + assert int_valued(pi) == False + + +def test_equal_valued(): + x = Symbol('x') + + equal_values = [ + [1, 1.0, S(1), S(1.0), S(1).n(5)], + [2, 2.0, S(2), S(2.0), S(2).n(5)], + [-1, -1.0, -S(1), -S(1.0), -S(1).n(5)], + [0.5, S(0.5), S(1)/2], + [-0.5, -S(0.5), -S(1)/2], + [0, 0.0, S(0), S(0.0), S(0).n()], + [pi], [pi.n()], # <-- not equal + [S(1)/10], [0.1, S(0.1)], # <-- not equal + [S(0.1).n(5)], + [oo], + [cos(x/2)], [cos(0.5*x)], # <-- no recursion + ] + + for m, values_m in enumerate(equal_values): + for value_i in values_m: + + # All values in same list equal + for value_j in values_m: + assert equal_valued(value_i, value_j) is True + + # Not equal to anything in any other list: + for n, values_n in enumerate(equal_values): + if n == m: + continue + for value_j in values_n: + assert equal_valued(value_i, value_j) is False + + +def test_all_close(): + x = Symbol('x') + assert all_close(2, 2) is True + assert all_close(2, 2.0000) is True + assert all_close(2, 2.0001) is False + assert all_close(1/3, 1/3.0001) is False + assert all_close(1/3, 1/3.0001, 1e-3, 1e-3) is True + assert all_close(1/3, Rational(1, 3)) is True + assert all_close(0.1*exp(0.2*x), exp(x/5)/10) is True + # The expressions should be structurally the same: + assert all_close(1.4142135623730951, sqrt(2)) is False + assert all_close(1.4142135623730951, sqrt(2).evalf()) is True + assert all_close(x + 1e-20, x) is False + # We should be able to match terms of an Add/Mul in any order + assert all_close(Add(1, 2, evaluate=False), Add(2, 1, evaluate=False)) diff --git a/lib/python3.12/site-packages/sympy/core/tests/test_operations.py b/lib/python3.12/site-packages/sympy/core/tests/test_operations.py new file mode 100644 index 0000000000000000000000000000000000000000..c60d691ef00ee9601ada04ef68e2db37794a81ad --- /dev/null +++ b/lib/python3.12/site-packages/sympy/core/tests/test_operations.py @@ -0,0 +1,110 @@ +from sympy.core.expr import Expr +from sympy.core.numbers import Integer +from sympy.core.singleton import S +from sympy.core.symbol import (Symbol, symbols) +from sympy.core.operations import AssocOp, LatticeOp +from sympy.testing.pytest import raises +from sympy.core.sympify import SympifyError +from sympy.core.add import Add, add +from sympy.core.mul import Mul, mul + +# create the simplest possible Lattice class + + +class join(LatticeOp): + zero = Integer(0) + identity = Integer(1) + + +def test_lattice_simple(): + assert join(join(2, 3), 4) == join(2, join(3, 4)) + assert join(2, 3) == join(3, 2) + assert join(0, 2) == 0 + assert join(1, 2) == 2 + assert join(2, 2) == 2 + + assert join(join(2, 3), 4) == join(2, 3, 4) + assert join() == 1 + assert join(4) == 4 + assert join(1, 4, 2, 3, 1, 3, 2) == join(2, 3, 4) + + +def test_lattice_shortcircuit(): + raises(SympifyError, lambda: join(object)) + assert join(0, object) == 0 + + +def test_lattice_print(): + assert str(join(5, 4, 3, 2)) == 'join(2, 3, 4, 5)' + + +def test_lattice_make_args(): + assert join.make_args(join(2, 3, 4)) == {S(2), S(3), S(4)} + assert join.make_args(0) == {0} + assert list(join.make_args(0))[0] is S.Zero + assert Add.make_args(0)[0] is S.Zero + + +def test_issue_14025(): + a, b, c, d = symbols('a,b,c,d', commutative=False) + assert Mul(a, b, c).has(c*b) == False + assert Mul(a, b, c).has(b*c) == True + assert Mul(a, b, c, d).has(b*c*d) == True + + +def test_AssocOp_flatten(): + a, b, c, d = symbols('a,b,c,d') + + class MyAssoc(AssocOp): + identity = S.One + + assert MyAssoc(a, MyAssoc(b, c)).args == \ + MyAssoc(MyAssoc(a, b), c).args == \ + MyAssoc(MyAssoc(a, b, c)).args == \ + MyAssoc(a, b, c).args == \ + (a, b, c) + u = MyAssoc(b, c) + v = MyAssoc(u, d, evaluate=False) + assert v.args == (u, d) + # like Add, any unevaluated outer call will flatten inner args + assert MyAssoc(a, v).args == (a, b, c, d) + + +def test_add_dispatcher(): + + class NewBase(Expr): + @property + def _add_handler(self): + return NewAdd + class NewAdd(NewBase, Add): + pass + add.register_handlerclass((Add, NewAdd), NewAdd) + + a, b = Symbol('a'), NewBase() + + # Add called as fallback + assert add(1, 2) == Add(1, 2) + assert add(a, a) == Add(a, a) + + # selection by registered priority + assert add(a,b,a) == NewAdd(2*a, b) + + +def test_mul_dispatcher(): + + class NewBase(Expr): + @property + def _mul_handler(self): + return NewMul + class NewMul(NewBase, Mul): + pass + mul.register_handlerclass((Mul, NewMul), NewMul) + + a, b = Symbol('a'), NewBase() + + # Mul called as fallback + assert mul(1, 2) == Mul(1, 2) + assert mul(a, a) == Mul(a, a) + + # selection by registered priority + assert mul(a,b,a) == NewMul(a**2, b) diff --git a/lib/python3.12/site-packages/sympy/core/tests/test_power.py b/lib/python3.12/site-packages/sympy/core/tests/test_power.py new file mode 100644 index 0000000000000000000000000000000000000000..9dcc828d854c75cebc8177663035e976ae10f9e5 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/core/tests/test_power.py @@ -0,0 +1,663 @@ +from sympy.core import ( + Basic, Rational, Symbol, S, Float, Integer, Mul, Number, Pow, + Expr, I, nan, pi, symbols, oo, zoo, N) +from sympy.core.parameters import global_parameters +from sympy.core.tests.test_evalf import NS +from sympy.core.function import expand_multinomial +from sympy.functions.elementary.miscellaneous import sqrt, cbrt +from sympy.functions.elementary.exponential import exp, log +from sympy.functions.special.error_functions import erf +from sympy.functions.elementary.trigonometric import ( + sin, cos, tan, sec, csc, atan) +from sympy.functions.elementary.hyperbolic import cosh, sinh, tanh +from sympy.polys import Poly +from sympy.series.order import O +from sympy.sets import FiniteSet +from sympy.core.power import power +from sympy.core.intfunc import integer_nthroot +from sympy.testing.pytest import warns, _both_exp_pow +from sympy.utilities.exceptions import SymPyDeprecationWarning +from sympy.abc import a, b, c, x, y + +def test_rational(): + a = Rational(1, 5) + + r = sqrt(5)/5 + assert sqrt(a) == r + assert 2*sqrt(a) == 2*r + + r = a*a**S.Half + assert a**Rational(3, 2) == r + assert 2*a**Rational(3, 2) == 2*r + + r = a**5*a**Rational(2, 3) + assert a**Rational(17, 3) == r + assert 2 * a**Rational(17, 3) == 2*r + + +def test_large_rational(): + e = (Rational(123712**12 - 1, 7) + Rational(1, 7))**Rational(1, 3) + assert e == 234232585392159195136 * (Rational(1, 7)**Rational(1, 3)) + + +def test_negative_real(): + def feq(a, b): + return abs(a - b) < 1E-10 + + assert feq(S.One / Float(-0.5), -Integer(2)) + + +def test_expand(): + assert (2**(-1 - x)).expand() == S.Half*2**(-x) + + +def test_issue_3449(): + #test if powers are simplified correctly + #see also issue 3995 + assert ((x**Rational(1, 3))**Rational(2)) == x**Rational(2, 3) + assert ( + (x**Rational(3))**Rational(2, 5)) == (x**Rational(3))**Rational(2, 5) + + a = Symbol('a', real=True) + b = Symbol('b', real=True) + assert (a**2)**b == (abs(a)**b)**2 + assert sqrt(1/a) != 1/sqrt(a) # e.g. for a = -1 + assert (a**3)**Rational(1, 3) != a + assert (x**a)**b != x**(a*b) # e.g. x = -1, a=2, b=1/2 + assert (x**.5)**b == x**(.5*b) + assert (x**.5)**.5 == x**.25 + assert (x**2.5)**.5 != x**1.25 # e.g. for x = 5*I + + k = Symbol('k', integer=True) + m = Symbol('m', integer=True) + assert (x**k)**m == x**(k*m) + assert Number(5)**Rational(2, 3) == Number(25)**Rational(1, 3) + + assert (x**.5)**2 == x**1.0 + assert (x**2)**k == (x**k)**2 == x**(2*k) + + a = Symbol('a', positive=True) + assert (a**3)**Rational(2, 5) == a**Rational(6, 5) + assert (a**2)**b == (a**b)**2 + assert (a**Rational(2, 3))**x == a**(x*Rational(2, 3)) != (a**x)**Rational(2, 3) + + +def test_issue_3866(): + assert --sqrt(sqrt(5) - 1) == sqrt(sqrt(5) - 1) + + +def test_negative_one(): + x = Symbol('x', complex=True) + y = Symbol('y', complex=True) + assert 1/x**y == x**(-y) + + +def test_issue_4362(): + neg = Symbol('neg', negative=True) + nonneg = Symbol('nonneg', nonnegative=True) + any = Symbol('any') + num, den = sqrt(1/neg).as_numer_denom() + assert num == sqrt(-1) + assert den == sqrt(-neg) + num, den = sqrt(1/nonneg).as_numer_denom() + assert num == 1 + assert den == sqrt(nonneg) + num, den = sqrt(1/any).as_numer_denom() + assert num == sqrt(1/any) + assert den == 1 + + def eqn(num, den, pow): + return (num/den)**pow + npos = 1 + nneg = -1 + dpos = 2 - sqrt(3) + dneg = 1 - sqrt(3) + assert dpos > 0 and dneg < 0 and npos > 0 and nneg < 0 + # pos or neg integer + eq = eqn(npos, dpos, 2) + assert eq.is_Pow and eq.as_numer_denom() == (1, dpos**2) + eq = eqn(npos, dneg, 2) + assert eq.is_Pow and eq.as_numer_denom() == (1, dneg**2) + eq = eqn(nneg, dpos, 2) + assert eq.is_Pow and eq.as_numer_denom() == (1, dpos**2) + eq = eqn(nneg, dneg, 2) + assert eq.is_Pow and eq.as_numer_denom() == (1, dneg**2) + eq = eqn(npos, dpos, -2) + assert eq.is_Pow and eq.as_numer_denom() == (dpos**2, 1) + eq = eqn(npos, dneg, -2) + assert eq.is_Pow and eq.as_numer_denom() == (dneg**2, 1) + eq = eqn(nneg, dpos, -2) + assert eq.is_Pow and eq.as_numer_denom() == (dpos**2, 1) + eq = eqn(nneg, dneg, -2) + assert eq.is_Pow and eq.as_numer_denom() == (dneg**2, 1) + # pos or neg rational + pow = S.Half + eq = eqn(npos, dpos, pow) + assert eq.is_Pow and eq.as_numer_denom() == (npos**pow, dpos**pow) + eq = eqn(npos, dneg, pow) + assert eq.is_Pow is False and eq.as_numer_denom() == ((-npos)**pow, (-dneg)**pow) + eq = eqn(nneg, dpos, pow) + assert not eq.is_Pow or eq.as_numer_denom() == (nneg**pow, dpos**pow) + eq = eqn(nneg, dneg, pow) + assert eq.is_Pow and eq.as_numer_denom() == ((-nneg)**pow, (-dneg)**pow) + eq = eqn(npos, dpos, -pow) + assert eq.is_Pow and eq.as_numer_denom() == (dpos**pow, npos**pow) + eq = eqn(npos, dneg, -pow) + assert eq.is_Pow is False and eq.as_numer_denom() == (-(-npos)**pow*(-dneg)**pow, npos) + eq = eqn(nneg, dpos, -pow) + assert not eq.is_Pow or eq.as_numer_denom() == (dpos**pow, nneg**pow) + eq = eqn(nneg, dneg, -pow) + assert eq.is_Pow and eq.as_numer_denom() == ((-dneg)**pow, (-nneg)**pow) + # unknown exponent + pow = 2*any + eq = eqn(npos, dpos, pow) + assert eq.is_Pow and eq.as_numer_denom() == (npos**pow, dpos**pow) + eq = eqn(npos, dneg, pow) + assert eq.is_Pow and eq.as_numer_denom() == ((-npos)**pow, (-dneg)**pow) + eq = eqn(nneg, dpos, pow) + assert eq.is_Pow and eq.as_numer_denom() == (nneg**pow, dpos**pow) + eq = eqn(nneg, dneg, pow) + assert eq.is_Pow and eq.as_numer_denom() == ((-nneg)**pow, (-dneg)**pow) + eq = eqn(npos, dpos, -pow) + assert eq.as_numer_denom() == (dpos**pow, npos**pow) + eq = eqn(npos, dneg, -pow) + assert eq.is_Pow and eq.as_numer_denom() == ((-dneg)**pow, (-npos)**pow) + eq = eqn(nneg, dpos, -pow) + assert eq.is_Pow and eq.as_numer_denom() == (dpos**pow, nneg**pow) + eq = eqn(nneg, dneg, -pow) + assert eq.is_Pow and eq.as_numer_denom() == ((-dneg)**pow, (-nneg)**pow) + + assert ((1/(1 + x/3))**(-S.One)).as_numer_denom() == (3 + x, 3) + notp = Symbol('notp', positive=False) # not positive does not imply real + b = ((1 + x/notp)**-2) + assert (b**(-y)).as_numer_denom() == (1, b**y) + assert (b**(-S.One)).as_numer_denom() == ((notp + x)**2, notp**2) + nonp = Symbol('nonp', nonpositive=True) + assert (((1 + x/nonp)**-2)**(-S.One)).as_numer_denom() == ((-nonp - + x)**2, nonp**2) + + n = Symbol('n', negative=True) + assert (x**n).as_numer_denom() == (1, x**-n) + assert sqrt(1/n).as_numer_denom() == (S.ImaginaryUnit, sqrt(-n)) + n = Symbol('0 or neg', nonpositive=True) + # if x and n are split up without negating each term and n is negative + # then the answer might be wrong; if n is 0 it won't matter since + # 1/oo and 1/zoo are both zero as is sqrt(0)/sqrt(-x) unless x is also + # zero (in which case the negative sign doesn't matter): + # 1/sqrt(1/-1) = -I but sqrt(-1)/sqrt(1) = I + assert (1/sqrt(x/n)).as_numer_denom() == (sqrt(-n), sqrt(-x)) + c = Symbol('c', complex=True) + e = sqrt(1/c) + assert e.as_numer_denom() == (e, 1) + i = Symbol('i', integer=True) + assert ((1 + x/y)**i).as_numer_denom() == ((x + y)**i, y**i) + + +def test_Pow_Expr_args(): + bases = [Basic(), Poly(x, x), FiniteSet(x)] + for base in bases: + # The cache can mess with the stacklevel test + with warns(SymPyDeprecationWarning, test_stacklevel=False): + Pow(base, S.One) + + +def test_Pow_signs(): + """Cf. issues 4595 and 5250""" + n = Symbol('n', even=True) + assert (3 - y)**2 != (y - 3)**2 + assert (3 - y)**n != (y - 3)**n + assert (-3 + y - x)**2 != (3 - y + x)**2 + assert (y - 3)**3 != -(3 - y)**3 + + +def test_power_with_noncommutative_mul_as_base(): + x = Symbol('x', commutative=False) + y = Symbol('y', commutative=False) + assert not (x*y)**3 == x**3*y**3 + assert (2*x*y)**3 == 8*(x*y)**3 + + +@_both_exp_pow +def test_power_rewrite_exp(): + assert (I**I).rewrite(exp) == exp(-pi/2) + + expr = (2 + 3*I)**(4 + 5*I) + assert expr.rewrite(exp) == exp((4 + 5*I)*(log(sqrt(13)) + I*atan(Rational(3, 2)))) + assert expr.rewrite(exp).expand() == \ + 169*exp(5*I*log(13)/2)*exp(4*I*atan(Rational(3, 2)))*exp(-5*atan(Rational(3, 2))) + + assert ((6 + 7*I)**5).rewrite(exp) == 7225*sqrt(85)*exp(5*I*atan(Rational(7, 6))) + + expr = 5**(6 + 7*I) + assert expr.rewrite(exp) == exp((6 + 7*I)*log(5)) + assert expr.rewrite(exp).expand() == 15625*exp(7*I*log(5)) + + assert Pow(123, 789, evaluate=False).rewrite(exp) == 123**789 + assert (1**I).rewrite(exp) == 1**I + assert (0**I).rewrite(exp) == 0**I + + expr = (-2)**(2 + 5*I) + assert expr.rewrite(exp) == exp((2 + 5*I)*(log(2) + I*pi)) + assert expr.rewrite(exp).expand() == 4*exp(-5*pi)*exp(5*I*log(2)) + + assert ((-2)**S(-5)).rewrite(exp) == (-2)**S(-5) + + x, y = symbols('x y') + assert (x**y).rewrite(exp) == exp(y*log(x)) + if global_parameters.exp_is_pow: + assert (7**x).rewrite(exp) == Pow(S.Exp1, x*log(7), evaluate=False) + else: + assert (7**x).rewrite(exp) == exp(x*log(7), evaluate=False) + assert ((2 + 3*I)**x).rewrite(exp) == exp(x*(log(sqrt(13)) + I*atan(Rational(3, 2)))) + assert (y**(5 + 6*I)).rewrite(exp) == exp(log(y)*(5 + 6*I)) + + assert all((1/func(x)).rewrite(exp) == 1/(func(x).rewrite(exp)) for func in + (sin, cos, tan, sec, csc, sinh, cosh, tanh)) + + +def test_zero(): + assert 0**x != 0 + assert 0**(2*x) == 0**x + assert 0**(1.0*x) == 0**x + assert 0**(2.0*x) == 0**x + assert (0**(2 - x)).as_base_exp() == (0, 2 - x) + assert 0**(x - 2) != S.Infinity**(2 - x) + assert 0**(2*x*y) == 0**(x*y) + assert 0**(-2*x*y) == S.ComplexInfinity**(x*y) + assert Float(0)**2 is not S.Zero + assert Float(0)**2 == 0.0 + assert Float(0)**-2 is zoo + assert Float(0)**oo is S.Zero + + #Test issue 19572 + assert 0 ** -oo is zoo + assert power(0, -oo) is zoo + assert Float(0)**-oo is zoo + +def test_pow_as_base_exp(): + assert (S.Infinity**(2 - x)).as_base_exp() == (S.Infinity, 2 - x) + assert (S.Infinity**(x - 2)).as_base_exp() == (S.Infinity, x - 2) + p = S.Half**x + assert p.base, p.exp == p.as_base_exp() == (S(2), -x) + p = (S(3)/2)**x + assert p.base, p.exp == p.as_base_exp() == (3*S.Half, x) + p = (S(2)/3)**x + assert p.as_base_exp() == (S(3)/2, -x) + assert p.base, p.exp == (S(2)/3, x) + # issue 8344: + assert Pow(1, 2, evaluate=False).as_base_exp() == (S.One, S(2)) + + +def test_nseries(): + assert sqrt(I*x - 1)._eval_nseries(x, 4, None, 1) == I + x/2 + I*x**2/8 - x**3/16 + O(x**4) + assert sqrt(I*x - 1)._eval_nseries(x, 4, None, -1) == -I - x/2 - I*x**2/8 + x**3/16 + O(x**4) + assert cbrt(I*x - 1)._eval_nseries(x, 4, None, 1) == (-1)**(S(1)/3) - (-1)**(S(5)/6)*x/3 + \ + (-1)**(S(1)/3)*x**2/9 + 5*(-1)**(S(5)/6)*x**3/81 + O(x**4) + assert cbrt(I*x - 1)._eval_nseries(x, 4, None, -1) == -(-1)**(S(2)/3) - (-1)**(S(1)/6)*x/3 - \ + (-1)**(S(2)/3)*x**2/9 + 5*(-1)**(S(1)/6)*x**3/81 + O(x**4) + assert (1 / (exp(-1/x) + 1/x))._eval_nseries(x, 2, None) == x + O(x**2) + # test issue 23752 + assert sqrt(-I*x**2 + x - 3)._eval_nseries(x, 4, None, 1) == -sqrt(3)*I + sqrt(3)*I*x/6 - \ + sqrt(3)*I*x**2*(-S(1)/72 + I/6) - sqrt(3)*I*x**3*(-S(1)/432 + I/36) + O(x**4) + assert sqrt(-I*x**2 + x - 3)._eval_nseries(x, 4, None, -1) == -sqrt(3)*I + sqrt(3)*I*x/6 - \ + sqrt(3)*I*x**2*(-S(1)/72 + I/6) - sqrt(3)*I*x**3*(-S(1)/432 + I/36) + O(x**4) + assert cbrt(-I*x**2 + x - 3)._eval_nseries(x, 4, None, 1) == -(-1)**(S(2)/3)*3**(S(1)/3) + \ + (-1)**(S(2)/3)*3**(S(1)/3)*x/9 - (-1)**(S(2)/3)*3**(S(1)/3)*x**2*(-S(1)/81 + I/9) - \ + (-1)**(S(2)/3)*3**(S(1)/3)*x**3*(-S(5)/2187 + 2*I/81) + O(x**4) + assert cbrt(-I*x**2 + x - 3)._eval_nseries(x, 4, None, -1) == -(-1)**(S(2)/3)*3**(S(1)/3) + \ + (-1)**(S(2)/3)*3**(S(1)/3)*x/9 - (-1)**(S(2)/3)*3**(S(1)/3)*x**2*(-S(1)/81 + I/9) - \ + (-1)**(S(2)/3)*3**(S(1)/3)*x**3*(-S(5)/2187 + 2*I/81) + O(x**4) + + +def test_issue_6100_12942_4473(): + assert x**1.0 != x + assert x != x**1.0 + assert True != x**1.0 + assert x**1.0 is not True + assert x is not True + assert x*y != (x*y)**1.0 + # Pow != Symbol + assert (x**1.0)**1.0 != x + assert (x**1.0)**2.0 != x**2 + b = Expr() + assert Pow(b, 1.0, evaluate=False) != b + # if the following gets distributed as a Mul (x**1.0*y**1.0 then + # __eq__ methods could be added to Symbol and Pow to detect the + # power-of-1.0 case. + assert ((x*y)**1.0).func is Pow + + +def test_issue_6208(): + from sympy.functions.elementary.miscellaneous import root + assert sqrt(33**(I*9/10)) == -33**(I*9/20) + assert root((6*I)**(2*I), 3).as_base_exp()[1] == Rational(1, 3) # != 2*I/3 + assert root((6*I)**(I/3), 3).as_base_exp()[1] == I/9 + assert sqrt(exp(3*I)) == exp(3*I/2) + assert sqrt(-sqrt(3)*(1 + 2*I)) == sqrt(sqrt(3))*sqrt(-1 - 2*I) + assert sqrt(exp(5*I)) == -exp(5*I/2) + assert root(exp(5*I), 3).exp == Rational(1, 3) + + +def test_issue_6990(): + assert (sqrt(a + b*x + x**2)).series(x, 0, 3).removeO() == \ + sqrt(a)*x**2*(1/(2*a) - b**2/(8*a**2)) + sqrt(a) + b*x/(2*sqrt(a)) + + +def test_issue_6068(): + assert sqrt(sin(x)).series(x, 0, 7) == \ + sqrt(x) - x**Rational(5, 2)/12 + x**Rational(9, 2)/1440 - \ + x**Rational(13, 2)/24192 + O(x**7) + assert sqrt(sin(x)).series(x, 0, 9) == \ + sqrt(x) - x**Rational(5, 2)/12 + x**Rational(9, 2)/1440 - \ + x**Rational(13, 2)/24192 - 67*x**Rational(17, 2)/29030400 + O(x**9) + assert sqrt(sin(x**3)).series(x, 0, 19) == \ + x**Rational(3, 2) - x**Rational(15, 2)/12 + x**Rational(27, 2)/1440 + O(x**19) + assert sqrt(sin(x**3)).series(x, 0, 20) == \ + x**Rational(3, 2) - x**Rational(15, 2)/12 + x**Rational(27, 2)/1440 - \ + x**Rational(39, 2)/24192 + O(x**20) + + +def test_issue_6782(): + assert sqrt(sin(x**3)).series(x, 0, 7) == x**Rational(3, 2) + O(x**7) + assert sqrt(sin(x**4)).series(x, 0, 3) == x**2 + O(x**3) + + +def test_issue_6653(): + assert (1 / sqrt(1 + sin(x**2))).series(x, 0, 3) == 1 - x**2/2 + O(x**3) + + +def test_issue_6429(): + f = (c**2 + x)**(0.5) + assert f.series(x, x0=0, n=1) == (c**2)**0.5 + O(x) + assert f.taylor_term(0, x) == (c**2)**0.5 + assert f.taylor_term(1, x) == 0.5*x*(c**2)**(-0.5) + assert f.taylor_term(2, x) == -0.125*x**2*(c**2)**(-1.5) + + +def test_issue_7638(): + f = pi/log(sqrt(2)) + assert ((1 + I)**(I*f/2))**0.3 == (1 + I)**(0.15*I*f) + # if 1/3 -> 1.0/3 this should fail since it cannot be shown that the + # sign will be +/-1; for the previous "small arg" case, it didn't matter + # that this could not be proved + assert (1 + I)**(4*I*f) == ((1 + I)**(12*I*f))**Rational(1, 3) + + assert (((1 + I)**(I*(1 + 7*f)))**Rational(1, 3)).exp == Rational(1, 3) + r = symbols('r', real=True) + assert sqrt(r**2) == abs(r) + assert cbrt(r**3) != r + assert sqrt(Pow(2*I, 5*S.Half)) != (2*I)**Rational(5, 4) + p = symbols('p', positive=True) + assert cbrt(p**2) == p**Rational(2, 3) + assert NS(((0.2 + 0.7*I)**(0.7 + 1.0*I))**(0.5 - 0.1*I), 1) == '0.4 + 0.2*I' + assert sqrt(1/(1 + I)) == sqrt(1 - I)/sqrt(2) # or 1/sqrt(1 + I) + e = 1/(1 - sqrt(2)) + assert sqrt(e) == I/sqrt(-1 + sqrt(2)) + assert e**Rational(-1, 2) == -I*sqrt(-1 + sqrt(2)) + assert sqrt((cos(1)**2 + sin(1)**2 - 1)**(3 + I)).exp in [S.Half, + Rational(3, 2) + I/2] + assert sqrt(r**Rational(4, 3)) != r**Rational(2, 3) + assert sqrt((p + I)**Rational(4, 3)) == (p + I)**Rational(2, 3) + + for q in 1+I, 1-I: + assert sqrt(q**2) == q + for q in -1+I, -1-I: + assert sqrt(q**2) == -q + + assert sqrt((p + r*I)**2) != p + r*I + e = (1 + I/5) + assert sqrt(e**5) == e**(5*S.Half) + assert sqrt(e**6) == e**3 + assert sqrt((1 + I*r)**6) != (1 + I*r)**3 + + +def test_issue_8582(): + assert 1**oo is nan + assert 1**(-oo) is nan + assert 1**zoo is nan + assert 1**(oo + I) is nan + assert 1**(1 + I*oo) is nan + assert 1**(oo + I*oo) is nan + + +def test_issue_8650(): + n = Symbol('n', integer=True, nonnegative=True) + assert (n**n).is_positive is True + x = 5*n + 5 + assert (x**(5*(n + 1))).is_positive is True + + +def test_issue_13914(): + b = Symbol('b') + assert (-1)**zoo is nan + assert 2**zoo is nan + assert (S.Half)**(1 + zoo) is nan + assert I**(zoo + I) is nan + assert b**(I + zoo) is nan + + +def test_better_sqrt(): + n = Symbol('n', integer=True, nonnegative=True) + assert sqrt(3 + 4*I) == 2 + I + assert sqrt(3 - 4*I) == 2 - I + assert sqrt(-3 - 4*I) == 1 - 2*I + assert sqrt(-3 + 4*I) == 1 + 2*I + assert sqrt(32 + 24*I) == 6 + 2*I + assert sqrt(32 - 24*I) == 6 - 2*I + assert sqrt(-32 - 24*I) == 2 - 6*I + assert sqrt(-32 + 24*I) == 2 + 6*I + + # triple (3, 4, 5): + # parity of 3 matches parity of 5 and + # den, 4, is a square + assert sqrt((3 + 4*I)/4) == 1 + I/2 + # triple (8, 15, 17) + # parity of 8 doesn't match parity of 17 but + # den/2, 8/2, is a square + assert sqrt((8 + 15*I)/8) == (5 + 3*I)/4 + # handle the denominator + assert sqrt((3 - 4*I)/25) == (2 - I)/5 + assert sqrt((3 - 4*I)/26) == (2 - I)/sqrt(26) + # mul + # issue #12739 + assert sqrt((3 + 4*I)/(3 - 4*I)) == (3 + 4*I)/5 + assert sqrt(2/(3 + 4*I)) == sqrt(2)/5*(2 - I) + assert sqrt(n/(3 + 4*I)).subs(n, 2) == sqrt(2)/5*(2 - I) + assert sqrt(-2/(3 + 4*I)) == sqrt(2)/5*(1 + 2*I) + assert sqrt(-n/(3 + 4*I)).subs(n, 2) == sqrt(2)/5*(1 + 2*I) + # power + assert sqrt(1/(3 + I*4)) == (2 - I)/5 + assert sqrt(1/(3 - I)) == sqrt(10)*sqrt(3 + I)/10 + # symbolic + i = symbols('i', imaginary=True) + assert sqrt(3/i) == Mul(sqrt(3), 1/sqrt(i), evaluate=False) + # multiples of 1/2; don't make this too automatic + assert sqrt(3 + 4*I)**3 == (2 + I)**3 + assert Pow(3 + 4*I, Rational(3, 2)) == 2 + 11*I + assert Pow(6 + 8*I, Rational(3, 2)) == 2*sqrt(2)*(2 + 11*I) + n, d = (3 + 4*I), (3 - 4*I)**3 + a = n/d + assert a.args == (1/d, n) + eq = sqrt(a) + assert eq.args == (a, S.Half) + assert expand_multinomial(eq) == sqrt((-117 + 44*I)*(3 + 4*I))/125 + assert eq.expand() == (7 - 24*I)/125 + + # issue 12775 + # pos im part + assert sqrt(2*I) == (1 + I) + assert sqrt(2*9*I) == Mul(3, 1 + I, evaluate=False) + assert Pow(2*I, 3*S.Half) == (1 + I)**3 + # neg im part + assert sqrt(-I/2) == Mul(S.Half, 1 - I, evaluate=False) + # fractional im part + assert Pow(Rational(-9, 2)*I, Rational(3, 2)) == 27*(1 - I)**3/8 + + +def test_issue_2993(): + assert str((2.3*x - 4)**0.3) == '1.5157165665104*(0.575*x - 1)**0.3' + assert str((2.3*x + 4)**0.3) == '1.5157165665104*(0.575*x + 1)**0.3' + assert str((-2.3*x + 4)**0.3) == '1.5157165665104*(1 - 0.575*x)**0.3' + assert str((-2.3*x - 4)**0.3) == '1.5157165665104*(-0.575*x - 1)**0.3' + assert str((2.3*x - 2)**0.3) == '1.28386201800527*(x - 0.869565217391304)**0.3' + assert str((-2.3*x - 2)**0.3) == '1.28386201800527*(-x - 0.869565217391304)**0.3' + assert str((-2.3*x + 2)**0.3) == '1.28386201800527*(0.869565217391304 - x)**0.3' + assert str((2.3*x + 2)**0.3) == '1.28386201800527*(x + 0.869565217391304)**0.3' + assert str((2.3*x - 4)**Rational(1, 3)) == '2**(2/3)*(0.575*x - 1)**(1/3)' + eq = (2.3*x + 4) + assert eq**2 == 16*(0.575*x + 1)**2 + assert (1/eq).args == (eq, -1) # don't change trivial power + # issue 17735 + q=.5*exp(x) - .5*exp(-x) + 0.1 + assert int((q**2).subs(x, 1)) == 1 + # issue 17756 + y = Symbol('y') + assert len(sqrt(x/(x + y)**2 + Float('0.008', 30)).subs(y, pi.n(25)).atoms(Float)) == 2 + # issue 17756 + a, b, c, d, e, f, g = symbols('a:g') + expr = sqrt(1 + a*(c**4 + g*d - 2*g*e - f*(-g + d))**2/ + (c**3*b**2*(d - 3*e + 2*f)**2))/2 + r = [ + (a, N('0.0170992456333788667034850458615', 30)), + (b, N('0.0966594956075474769169134801223', 30)), + (c, N('0.390911862903463913632151616184', 30)), + (d, N('0.152812084558656566271750185933', 30)), + (e, N('0.137562344465103337106561623432', 30)), + (f, N('0.174259178881496659302933610355', 30)), + (g, N('0.220745448491223779615401870086', 30))] + tru = expr.n(30, subs=dict(r)) + seq = expr.subs(r) + # although `tru` is the right way to evaluate + # expr with numerical values, `seq` will have + # significant loss of precision if extraction of + # the largest coefficient of a power's base's terms + # is done improperly + assert seq == tru + +def test_issue_17450(): + assert (erf(cosh(1)**7)**I).is_real is None + assert (erf(cosh(1)**7)**I).is_imaginary is False + assert (Pow(exp(1+sqrt(2)), ((1-sqrt(2))*I*pi), evaluate=False)).is_real is None + assert ((-10)**(10*I*pi/3)).is_real is False + assert ((-5)**(4*I*pi)).is_real is False + + +def test_issue_18190(): + assert sqrt(1 / tan(1 + I)) == 1 / sqrt(tan(1 + I)) + + +def test_issue_14815(): + x = Symbol('x', real=True) + assert sqrt(x).is_extended_negative is False + x = Symbol('x', real=False) + assert sqrt(x).is_extended_negative is None + x = Symbol('x', complex=True) + assert sqrt(x).is_extended_negative is False + x = Symbol('x', extended_real=True) + assert sqrt(x).is_extended_negative is False + assert sqrt(zoo, evaluate=False).is_extended_negative is False + assert sqrt(nan, evaluate=False).is_extended_negative is None + + +def test_issue_18509(): + x = Symbol('x', prime=True) + assert x**oo is oo + assert (1/x)**oo is S.Zero + assert (-1/x)**oo is S.Zero + assert (-x)**oo is zoo + assert (-oo)**(-1 + I) is S.Zero + assert (-oo)**(1 + I) is zoo + assert (oo)**(-1 + I) is S.Zero + assert (oo)**(1 + I) is zoo + + +def test_issue_18762(): + e, p = symbols('e p') + g0 = sqrt(1 + e**2 - 2*e*cos(p)) + assert len(g0.series(e, 1, 3).args) == 4 + + +def test_issue_21860(): + e = 3*2**Rational(66666666667,200000000000)*3**Rational(16666666667,50000000000)*x**Rational(66666666667, 200000000000) + ans = Mul(Rational(3, 2), + Pow(Integer(2), Rational(33333333333, 100000000000)), + Pow(Integer(3), Rational(26666666667, 40000000000))) + assert e.xreplace({x: Rational(3,8)}) == ans + + +def test_issue_21647(): + e = log((Integer(567)/500)**(811*(Integer(567)/500)**x/100)) + ans = log(Mul(Rational(64701150190720499096094005280169087619821081527, + 76293945312500000000000000000000000000000000000), + Pow(Integer(2), Rational(396204892125479941, 781250000000000000)), + Pow(Integer(3), Rational(385045107874520059, 390625000000000000)), + Pow(Integer(5), Rational(407364676376439823, 1562500000000000000)), + Pow(Integer(7), Rational(385045107874520059, 1562500000000000000)))) + assert e.xreplace({x: 6}) == ans + + +def test_issue_21762(): + e = (x**2 + 6)**(Integer(33333333333333333)/50000000000000000) + ans = Mul(Rational(5, 4), + Pow(Integer(2), Rational(16666666666666667, 25000000000000000)), + Pow(Integer(5), Rational(8333333333333333, 25000000000000000))) + assert e.xreplace({x: S.Half}) == ans + + +def test_issue_14704(): + a = 144**144 + x, xexact = integer_nthroot(a,a) + assert x == 1 and xexact is False + + +def test_rational_powers_larger_than_one(): + assert Rational(2, 3)**Rational(3, 2) == 2*sqrt(6)/9 + assert Rational(1, 6)**Rational(9, 4) == 6**Rational(3, 4)/216 + assert Rational(3, 7)**Rational(7, 3) == 9*3**Rational(1, 3)*7**Rational(2, 3)/343 + + +def test_power_dispatcher(): + + class NewBase(Expr): + pass + class NewPow(NewBase, Pow): + pass + a, b = Symbol('a'), NewBase() + + @power.register(Expr, NewBase) + @power.register(NewBase, Expr) + @power.register(NewBase, NewBase) + def _(a, b): + return NewPow(a, b) + + # Pow called as fallback + assert power(2, 3) == 8*S.One + assert power(a, 2) == Pow(a, 2) + assert power(a, a) == Pow(a, a) + + # NewPow called by dispatch + assert power(a, b) == NewPow(a, b) + assert power(b, a) == NewPow(b, a) + assert power(b, b) == NewPow(b, b) + + +def test_powers_of_I(): + assert [sqrt(I)**i for i in range(13)] == [ + 1, sqrt(I), I, sqrt(I)**3, -1, -sqrt(I), -I, -sqrt(I)**3, + 1, sqrt(I), I, sqrt(I)**3, -1] + assert sqrt(I)**(S(9)/2) == -I**(S(1)/4) + + +def test_issue_23918(): + b = S(2)/3 + assert (b**x).as_base_exp() == (1/b, -x) + + +def test_issue_26546(): + x = Symbol('x', real=True) + assert x.is_extended_real is True + assert sqrt(x+I).is_extended_real is False + assert Pow(x+I, S.Half).is_extended_real is False + assert Pow(x+I, Rational(1,2)).is_extended_real is False + assert Pow(x+I, Rational(1,13)).is_extended_real is False + assert Pow(x+I, Rational(2,3)).is_extended_real is None diff --git a/lib/python3.12/site-packages/sympy/core/tests/test_priority.py b/lib/python3.12/site-packages/sympy/core/tests/test_priority.py new file mode 100644 index 0000000000000000000000000000000000000000..7be30f24033698e43ed68bdbaa0f3566465de173 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/core/tests/test_priority.py @@ -0,0 +1,144 @@ +from sympy.core.decorators import call_highest_priority +from sympy.core.expr import Expr +from sympy.core.mod import Mod +from sympy.core.numbers import Integer +from sympy.core.symbol import Symbol +from sympy.functions.elementary.integers import floor + + +class Higher(Integer): + ''' + Integer of value 1 and _op_priority 20 + + Operations handled by this class return 1 and reverse operations return 2 + ''' + + _op_priority = 20.0 + result = 1 + + def __new__(cls): + obj = Expr.__new__(cls) + obj.p = 1 + return obj + + @call_highest_priority('__rmul__') + def __mul__(self, other): + return self.result + + @call_highest_priority('__mul__') + def __rmul__(self, other): + return 2*self.result + + @call_highest_priority('__radd__') + def __add__(self, other): + return self.result + + @call_highest_priority('__add__') + def __radd__(self, other): + return 2*self.result + + @call_highest_priority('__rsub__') + def __sub__(self, other): + return self.result + + @call_highest_priority('__sub__') + def __rsub__(self, other): + return 2*self.result + + @call_highest_priority('__rpow__') + def __pow__(self, other): + return self.result + + @call_highest_priority('__pow__') + def __rpow__(self, other): + return 2*self.result + + @call_highest_priority('__rtruediv__') + def __truediv__(self, other): + return self.result + + @call_highest_priority('__truediv__') + def __rtruediv__(self, other): + return 2*self.result + + @call_highest_priority('__rmod__') + def __mod__(self, other): + return self.result + + @call_highest_priority('__mod__') + def __rmod__(self, other): + return 2*self.result + + @call_highest_priority('__rfloordiv__') + def __floordiv__(self, other): + return self.result + + @call_highest_priority('__floordiv__') + def __rfloordiv__(self, other): + return 2*self.result + + +class Lower(Higher): + ''' + Integer of value -1 and _op_priority 5 + + Operations handled by this class return -1 and reverse operations return -2 + ''' + + _op_priority = 5.0 + result = -1 + + def __new__(cls): + obj = Expr.__new__(cls) + obj.p = -1 + return obj + + +x = Symbol('x') +h = Higher() +l = Lower() + + +def test_mul(): + assert h*l == h*x == 1 + assert l*h == x*h == 2 + assert x*l == l*x == -x + + +def test_add(): + assert h + l == h + x == 1 + assert l + h == x + h == 2 + assert x + l == l + x == x - 1 + + +def test_sub(): + assert h - l == h - x == 1 + assert l - h == x - h == 2 + assert x - l == -(l - x) == x + 1 + + +def test_pow(): + assert h**l == h**x == 1 + assert l**h == x**h == 2 + assert (x**l).args == (1/x).args and (x**l).is_Pow + assert (l**x).args == ((-1)**x).args and (l**x).is_Pow + + +def test_div(): + assert h/l == h/x == 1 + assert l/h == x/h == 2 + assert x/l == 1/(l/x) == -x + + +def test_mod(): + assert h%l == h%x == 1 + assert l%h == x%h == 2 + assert x%l == Mod(x, -1) + assert l%x == Mod(-1, x) + + +def test_floordiv(): + assert h//l == h//x == 1 + assert l//h == x//h == 2 + assert x//l == floor(-x) + assert l//x == floor(-1/x) diff --git a/lib/python3.12/site-packages/sympy/core/tests/test_relational.py b/lib/python3.12/site-packages/sympy/core/tests/test_relational.py new file mode 100644 index 0000000000000000000000000000000000000000..6ab6405c49db677b2c0cbdec7892d46cda841428 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/core/tests/test_relational.py @@ -0,0 +1,1271 @@ +from sympy.core.logic import fuzzy_and +from sympy.core.sympify import _sympify +from sympy.multipledispatch import dispatch +from sympy.testing.pytest import XFAIL, raises +from sympy.assumptions.ask import Q +from sympy.core.add import Add +from sympy.core.basic import Basic +from sympy.core.expr import Expr, unchanged +from sympy.core.function import Function +from sympy.core.mul import Mul +from sympy.core.numbers import (Float, I, Rational, nan, oo, pi, zoo) +from sympy.core.power import Pow +from sympy.core.singleton import S +from sympy.core.symbol import (Symbol, symbols) +from sympy.functions.elementary.complexes import sign, Abs +from sympy.functions.elementary.piecewise import Piecewise +from sympy.functions.elementary.exponential import (exp, exp_polar, log) +from sympy.functions.elementary.integers import (ceiling, floor) +from sympy.functions.elementary.miscellaneous import sqrt +from sympy.functions.elementary.trigonometric import (cos, sin) +from sympy.logic.boolalg import (And, Implies, Not, Or, Xor) +from sympy.sets import Reals +from sympy.simplify.simplify import simplify +from sympy.simplify.trigsimp import trigsimp +from sympy.core.relational import (Relational, Equality, Unequality, + GreaterThan, LessThan, StrictGreaterThan, + StrictLessThan, Rel, Eq, Lt, Le, + Gt, Ge, Ne, is_le, is_gt, is_ge, is_lt, is_eq, is_neq) +from sympy.sets.sets import Interval, FiniteSet + +from itertools import combinations + +x, y, z, t = symbols('x,y,z,t') + + +def rel_check(a, b): + from sympy.testing.pytest import raises + assert a.is_number and b.is_number + for do in range(len({type(a), type(b)})): + if S.NaN in (a, b): + v = [(a == b), (a != b)] + assert len(set(v)) == 1 and v[0] == False + assert not (a != b) and not (a == b) + assert raises(TypeError, lambda: a < b) + assert raises(TypeError, lambda: a <= b) + assert raises(TypeError, lambda: a > b) + assert raises(TypeError, lambda: a >= b) + else: + E = [(a == b), (a != b)] + assert len(set(E)) == 2 + v = [ + (a < b), (a <= b), (a > b), (a >= b)] + i = [ + [True, True, False, False], + [False, True, False, True], # <-- i == 1 + [False, False, True, True]].index(v) + if i == 1: + assert E[0] or (a.is_Float != b.is_Float) # ugh + else: + assert E[1] + a, b = b, a + return True + + +def test_rel_ne(): + assert Relational(x, y, '!=') == Ne(x, y) + + # issue 6116 + p = Symbol('p', positive=True) + assert Ne(p, 0) is S.true + + +def test_rel_subs(): + e = Relational(x, y, '==') + e = e.subs(x, z) + + assert isinstance(e, Equality) + assert e.lhs == z + assert e.rhs == y + + e = Relational(x, y, '>=') + e = e.subs(x, z) + + assert isinstance(e, GreaterThan) + assert e.lhs == z + assert e.rhs == y + + e = Relational(x, y, '<=') + e = e.subs(x, z) + + assert isinstance(e, LessThan) + assert e.lhs == z + assert e.rhs == y + + e = Relational(x, y, '>') + e = e.subs(x, z) + + assert isinstance(e, StrictGreaterThan) + assert e.lhs == z + assert e.rhs == y + + e = Relational(x, y, '<') + e = e.subs(x, z) + + assert isinstance(e, StrictLessThan) + assert e.lhs == z + assert e.rhs == y + + e = Eq(x, 0) + assert e.subs(x, 0) is S.true + assert e.subs(x, 1) is S.false + + +def test_wrappers(): + e = x + x**2 + + res = Relational(y, e, '==') + assert Rel(y, x + x**2, '==') == res + assert Eq(y, x + x**2) == res + + res = Relational(y, e, '<') + assert Lt(y, x + x**2) == res + + res = Relational(y, e, '<=') + assert Le(y, x + x**2) == res + + res = Relational(y, e, '>') + assert Gt(y, x + x**2) == res + + res = Relational(y, e, '>=') + assert Ge(y, x + x**2) == res + + res = Relational(y, e, '!=') + assert Ne(y, x + x**2) == res + + +def test_Eq_Ne(): + + assert Eq(x, x) # issue 5719 + + # issue 6116 + p = Symbol('p', positive=True) + assert Eq(p, 0) is S.false + + # issue 13348; 19048 + # SymPy is strict about 0 and 1 not being + # interpreted as Booleans + assert Eq(True, 1) is S.false + assert Eq(False, 0) is S.false + assert Eq(~x, 0) is S.false + assert Eq(~x, 1) is S.false + assert Ne(True, 1) is S.true + assert Ne(False, 0) is S.true + assert Ne(~x, 0) is S.true + assert Ne(~x, 1) is S.true + + assert Eq((), 1) is S.false + assert Ne((), 1) is S.true + + +def test_as_poly(): + from sympy.polys.polytools import Poly + # Only Eq should have an as_poly method: + assert Eq(x, 1).as_poly() == Poly(x - 1, x, domain='ZZ') + raises(AttributeError, lambda: Ne(x, 1).as_poly()) + raises(AttributeError, lambda: Ge(x, 1).as_poly()) + raises(AttributeError, lambda: Gt(x, 1).as_poly()) + raises(AttributeError, lambda: Le(x, 1).as_poly()) + raises(AttributeError, lambda: Lt(x, 1).as_poly()) + + +def test_rel_Infinity(): + # NOTE: All of these are actually handled by sympy.core.Number, and do + # not create Relational objects. + assert (oo > oo) is S.false + assert (oo > -oo) is S.true + assert (oo > 1) is S.true + assert (oo < oo) is S.false + assert (oo < -oo) is S.false + assert (oo < 1) is S.false + assert (oo >= oo) is S.true + assert (oo >= -oo) is S.true + assert (oo >= 1) is S.true + assert (oo <= oo) is S.true + assert (oo <= -oo) is S.false + assert (oo <= 1) is S.false + assert (-oo > oo) is S.false + assert (-oo > -oo) is S.false + assert (-oo > 1) is S.false + assert (-oo < oo) is S.true + assert (-oo < -oo) is S.false + assert (-oo < 1) is S.true + assert (-oo >= oo) is S.false + assert (-oo >= -oo) is S.true + assert (-oo >= 1) is S.false + assert (-oo <= oo) is S.true + assert (-oo <= -oo) is S.true + assert (-oo <= 1) is S.true + + +def test_infinite_symbol_inequalities(): + x = Symbol('x', extended_positive=True, infinite=True) + y = Symbol('y', extended_positive=True, infinite=True) + z = Symbol('z', extended_negative=True, infinite=True) + w = Symbol('w', extended_negative=True, infinite=True) + + inf_set = (x, y, oo) + ninf_set = (z, w, -oo) + + for inf1 in inf_set: + assert (inf1 < 1) is S.false + assert (inf1 > 1) is S.true + assert (inf1 <= 1) is S.false + assert (inf1 >= 1) is S.true + + for inf2 in inf_set: + assert (inf1 < inf2) is S.false + assert (inf1 > inf2) is S.false + assert (inf1 <= inf2) is S.true + assert (inf1 >= inf2) is S.true + + for ninf1 in ninf_set: + assert (inf1 < ninf1) is S.false + assert (inf1 > ninf1) is S.true + assert (inf1 <= ninf1) is S.false + assert (inf1 >= ninf1) is S.true + assert (ninf1 < inf1) is S.true + assert (ninf1 > inf1) is S.false + assert (ninf1 <= inf1) is S.true + assert (ninf1 >= inf1) is S.false + + for ninf1 in ninf_set: + assert (ninf1 < 1) is S.true + assert (ninf1 > 1) is S.false + assert (ninf1 <= 1) is S.true + assert (ninf1 >= 1) is S.false + + for ninf2 in ninf_set: + assert (ninf1 < ninf2) is S.false + assert (ninf1 > ninf2) is S.false + assert (ninf1 <= ninf2) is S.true + assert (ninf1 >= ninf2) is S.true + + +def test_bool(): + assert Eq(0, 0) is S.true + assert Eq(1, 0) is S.false + assert Ne(0, 0) is S.false + assert Ne(1, 0) is S.true + assert Lt(0, 1) is S.true + assert Lt(1, 0) is S.false + assert Le(0, 1) is S.true + assert Le(1, 0) is S.false + assert Le(0, 0) is S.true + assert Gt(1, 0) is S.true + assert Gt(0, 1) is S.false + assert Ge(1, 0) is S.true + assert Ge(0, 1) is S.false + assert Ge(1, 1) is S.true + assert Eq(I, 2) is S.false + assert Ne(I, 2) is S.true + raises(TypeError, lambda: Gt(I, 2)) + raises(TypeError, lambda: Ge(I, 2)) + raises(TypeError, lambda: Lt(I, 2)) + raises(TypeError, lambda: Le(I, 2)) + a = Float('.000000000000000000001', '') + b = Float('.0000000000000000000001', '') + assert Eq(pi + a, pi + b) is S.false + + +def test_rich_cmp(): + assert (x < y) == Lt(x, y) + assert (x <= y) == Le(x, y) + assert (x > y) == Gt(x, y) + assert (x >= y) == Ge(x, y) + + +def test_doit(): + from sympy.core.symbol import Symbol + p = Symbol('p', positive=True) + n = Symbol('n', negative=True) + np = Symbol('np', nonpositive=True) + nn = Symbol('nn', nonnegative=True) + + assert Gt(p, 0).doit() is S.true + assert Gt(p, 1).doit() == Gt(p, 1) + assert Ge(p, 0).doit() is S.true + assert Le(p, 0).doit() is S.false + assert Lt(n, 0).doit() is S.true + assert Le(np, 0).doit() is S.true + assert Gt(nn, 0).doit() == Gt(nn, 0) + assert Lt(nn, 0).doit() is S.false + + assert Eq(x, 0).doit() == Eq(x, 0) + + +def test_new_relational(): + x = Symbol('x') + + assert Eq(x, 0) == Relational(x, 0) # None ==> Equality + assert Eq(x, 0) == Relational(x, 0, '==') + assert Eq(x, 0) == Relational(x, 0, 'eq') + assert Eq(x, 0) == Equality(x, 0) + + assert Eq(x, 0) != Relational(x, 1) # None ==> Equality + assert Eq(x, 0) != Relational(x, 1, '==') + assert Eq(x, 0) != Relational(x, 1, 'eq') + assert Eq(x, 0) != Equality(x, 1) + + assert Eq(x, -1) == Relational(x, -1) # None ==> Equality + assert Eq(x, -1) == Relational(x, -1, '==') + assert Eq(x, -1) == Relational(x, -1, 'eq') + assert Eq(x, -1) == Equality(x, -1) + assert Eq(x, -1) != Relational(x, 1) # None ==> Equality + assert Eq(x, -1) != Relational(x, 1, '==') + assert Eq(x, -1) != Relational(x, 1, 'eq') + assert Eq(x, -1) != Equality(x, 1) + + assert Ne(x, 0) == Relational(x, 0, '!=') + assert Ne(x, 0) == Relational(x, 0, '<>') + assert Ne(x, 0) == Relational(x, 0, 'ne') + assert Ne(x, 0) == Unequality(x, 0) + assert Ne(x, 0) != Relational(x, 1, '!=') + assert Ne(x, 0) != Relational(x, 1, '<>') + assert Ne(x, 0) != Relational(x, 1, 'ne') + assert Ne(x, 0) != Unequality(x, 1) + + assert Ge(x, 0) == Relational(x, 0, '>=') + assert Ge(x, 0) == Relational(x, 0, 'ge') + assert Ge(x, 0) == GreaterThan(x, 0) + assert Ge(x, 1) != Relational(x, 0, '>=') + assert Ge(x, 1) != Relational(x, 0, 'ge') + assert Ge(x, 1) != GreaterThan(x, 0) + assert (x >= 1) == Relational(x, 1, '>=') + assert (x >= 1) == Relational(x, 1, 'ge') + assert (x >= 1) == GreaterThan(x, 1) + assert (x >= 0) != Relational(x, 1, '>=') + assert (x >= 0) != Relational(x, 1, 'ge') + assert (x >= 0) != GreaterThan(x, 1) + + assert Le(x, 0) == Relational(x, 0, '<=') + assert Le(x, 0) == Relational(x, 0, 'le') + assert Le(x, 0) == LessThan(x, 0) + assert Le(x, 1) != Relational(x, 0, '<=') + assert Le(x, 1) != Relational(x, 0, 'le') + assert Le(x, 1) != LessThan(x, 0) + assert (x <= 1) == Relational(x, 1, '<=') + assert (x <= 1) == Relational(x, 1, 'le') + assert (x <= 1) == LessThan(x, 1) + assert (x <= 0) != Relational(x, 1, '<=') + assert (x <= 0) != Relational(x, 1, 'le') + assert (x <= 0) != LessThan(x, 1) + + assert Gt(x, 0) == Relational(x, 0, '>') + assert Gt(x, 0) == Relational(x, 0, 'gt') + assert Gt(x, 0) == StrictGreaterThan(x, 0) + assert Gt(x, 1) != Relational(x, 0, '>') + assert Gt(x, 1) != Relational(x, 0, 'gt') + assert Gt(x, 1) != StrictGreaterThan(x, 0) + assert (x > 1) == Relational(x, 1, '>') + assert (x > 1) == Relational(x, 1, 'gt') + assert (x > 1) == StrictGreaterThan(x, 1) + assert (x > 0) != Relational(x, 1, '>') + assert (x > 0) != Relational(x, 1, 'gt') + assert (x > 0) != StrictGreaterThan(x, 1) + + assert Lt(x, 0) == Relational(x, 0, '<') + assert Lt(x, 0) == Relational(x, 0, 'lt') + assert Lt(x, 0) == StrictLessThan(x, 0) + assert Lt(x, 1) != Relational(x, 0, '<') + assert Lt(x, 1) != Relational(x, 0, 'lt') + assert Lt(x, 1) != StrictLessThan(x, 0) + assert (x < 1) == Relational(x, 1, '<') + assert (x < 1) == Relational(x, 1, 'lt') + assert (x < 1) == StrictLessThan(x, 1) + assert (x < 0) != Relational(x, 1, '<') + assert (x < 0) != Relational(x, 1, 'lt') + assert (x < 0) != StrictLessThan(x, 1) + + # finally, some fuzz testing + from sympy.core.random import randint + for i in range(100): + while 1: + strtype, length = (chr, 65535) if randint(0, 1) else (chr, 255) + relation_type = strtype(randint(0, length)) + if randint(0, 1): + relation_type += strtype(randint(0, length)) + if relation_type not in ('==', 'eq', '!=', '<>', 'ne', '>=', 'ge', + '<=', 'le', '>', 'gt', '<', 'lt', ':=', + '+=', '-=', '*=', '/=', '%='): + break + + raises(ValueError, lambda: Relational(x, 1, relation_type)) + assert all(Relational(x, 0, op).rel_op == '==' for op in ('eq', '==')) + assert all(Relational(x, 0, op).rel_op == '!=' + for op in ('ne', '<>', '!=')) + assert all(Relational(x, 0, op).rel_op == '>' for op in ('gt', '>')) + assert all(Relational(x, 0, op).rel_op == '<' for op in ('lt', '<')) + assert all(Relational(x, 0, op).rel_op == '>=' for op in ('ge', '>=')) + assert all(Relational(x, 0, op).rel_op == '<=' for op in ('le', '<=')) + + +def test_relational_arithmetic(): + for cls in [Eq, Ne, Le, Lt, Ge, Gt]: + rel = cls(x, y) + raises(TypeError, lambda: 0+rel) + raises(TypeError, lambda: 1*rel) + raises(TypeError, lambda: 1**rel) + raises(TypeError, lambda: rel**1) + raises(TypeError, lambda: Add(0, rel)) + raises(TypeError, lambda: Mul(1, rel)) + raises(TypeError, lambda: Pow(1, rel)) + raises(TypeError, lambda: Pow(rel, 1)) + + +def test_relational_bool_output(): + # https://github.com/sympy/sympy/issues/5931 + raises(TypeError, lambda: bool(x > 3)) + raises(TypeError, lambda: bool(x >= 3)) + raises(TypeError, lambda: bool(x < 3)) + raises(TypeError, lambda: bool(x <= 3)) + raises(TypeError, lambda: bool(Eq(x, 3))) + raises(TypeError, lambda: bool(Ne(x, 3))) + + +def test_relational_logic_symbols(): + # See issue 6204 + assert (x < y) & (z < t) == And(x < y, z < t) + assert (x < y) | (z < t) == Or(x < y, z < t) + assert ~(x < y) == Not(x < y) + assert (x < y) >> (z < t) == Implies(x < y, z < t) + assert (x < y) << (z < t) == Implies(z < t, x < y) + assert (x < y) ^ (z < t) == Xor(x < y, z < t) + + assert isinstance((x < y) & (z < t), And) + assert isinstance((x < y) | (z < t), Or) + assert isinstance(~(x < y), GreaterThan) + assert isinstance((x < y) >> (z < t), Implies) + assert isinstance((x < y) << (z < t), Implies) + assert isinstance((x < y) ^ (z < t), (Or, Xor)) + + +def test_univariate_relational_as_set(): + assert (x > 0).as_set() == Interval(0, oo, True, True) + assert (x >= 0).as_set() == Interval(0, oo) + assert (x < 0).as_set() == Interval(-oo, 0, True, True) + assert (x <= 0).as_set() == Interval(-oo, 0) + assert Eq(x, 0).as_set() == FiniteSet(0) + assert Ne(x, 0).as_set() == Interval(-oo, 0, True, True) + \ + Interval(0, oo, True, True) + + assert (x**2 >= 4).as_set() == Interval(-oo, -2) + Interval(2, oo) + + +@XFAIL +def test_multivariate_relational_as_set(): + assert (x*y >= 0).as_set() == Interval(0, oo)*Interval(0, oo) + \ + Interval(-oo, 0)*Interval(-oo, 0) + + +def test_Not(): + assert Not(Equality(x, y)) == Unequality(x, y) + assert Not(Unequality(x, y)) == Equality(x, y) + assert Not(StrictGreaterThan(x, y)) == LessThan(x, y) + assert Not(StrictLessThan(x, y)) == GreaterThan(x, y) + assert Not(GreaterThan(x, y)) == StrictLessThan(x, y) + assert Not(LessThan(x, y)) == StrictGreaterThan(x, y) + + +def test_evaluate(): + assert str(Eq(x, x, evaluate=False)) == 'Eq(x, x)' + assert Eq(x, x, evaluate=False).doit() == S.true + assert str(Ne(x, x, evaluate=False)) == 'Ne(x, x)' + assert Ne(x, x, evaluate=False).doit() == S.false + + assert str(Ge(x, x, evaluate=False)) == 'x >= x' + assert str(Le(x, x, evaluate=False)) == 'x <= x' + assert str(Gt(x, x, evaluate=False)) == 'x > x' + assert str(Lt(x, x, evaluate=False)) == 'x < x' + + +def assert_all_ineq_raise_TypeError(a, b): + raises(TypeError, lambda: a > b) + raises(TypeError, lambda: a >= b) + raises(TypeError, lambda: a < b) + raises(TypeError, lambda: a <= b) + raises(TypeError, lambda: b > a) + raises(TypeError, lambda: b >= a) + raises(TypeError, lambda: b < a) + raises(TypeError, lambda: b <= a) + + +def assert_all_ineq_give_class_Inequality(a, b): + """All inequality operations on `a` and `b` result in class Inequality.""" + from sympy.core.relational import _Inequality as Inequality + assert isinstance(a > b, Inequality) + assert isinstance(a >= b, Inequality) + assert isinstance(a < b, Inequality) + assert isinstance(a <= b, Inequality) + assert isinstance(b > a, Inequality) + assert isinstance(b >= a, Inequality) + assert isinstance(b < a, Inequality) + assert isinstance(b <= a, Inequality) + + +def test_imaginary_compare_raises_TypeError(): + # See issue #5724 + assert_all_ineq_raise_TypeError(I, x) + + +def test_complex_compare_not_real(): + # two cases which are not real + y = Symbol('y', imaginary=True) + z = Symbol('z', complex=True, extended_real=False) + for w in (y, z): + assert_all_ineq_raise_TypeError(2, w) + # some cases which should remain un-evaluated + t = Symbol('t') + x = Symbol('x', real=True) + z = Symbol('z', complex=True) + for w in (x, z, t): + assert_all_ineq_give_class_Inequality(2, w) + + +def test_imaginary_and_inf_compare_raises_TypeError(): + # See pull request #7835 + y = Symbol('y', imaginary=True) + assert_all_ineq_raise_TypeError(oo, y) + assert_all_ineq_raise_TypeError(-oo, y) + + +def test_complex_pure_imag_not_ordered(): + raises(TypeError, lambda: 2*I < 3*I) + + # more generally + x = Symbol('x', real=True, nonzero=True) + y = Symbol('y', imaginary=True) + z = Symbol('z', complex=True) + assert_all_ineq_raise_TypeError(I, y) + + t = I*x # an imaginary number, should raise errors + assert_all_ineq_raise_TypeError(2, t) + + t = -I*y # a real number, so no errors + assert_all_ineq_give_class_Inequality(2, t) + + t = I*z # unknown, should be unevaluated + assert_all_ineq_give_class_Inequality(2, t) + + +def test_x_minus_y_not_same_as_x_lt_y(): + """ + A consequence of pull request #7792 is that `x - y < 0` and `x < y` + are not synonymous. + """ + x = I + 2 + y = I + 3 + raises(TypeError, lambda: x < y) + assert x - y < 0 + + ineq = Lt(x, y, evaluate=False) + raises(TypeError, lambda: ineq.doit()) + assert ineq.lhs - ineq.rhs < 0 + + t = Symbol('t', imaginary=True) + x = 2 + t + y = 3 + t + ineq = Lt(x, y, evaluate=False) + raises(TypeError, lambda: ineq.doit()) + assert ineq.lhs - ineq.rhs < 0 + + # this one should give error either way + x = I + 2 + y = 2*I + 3 + raises(TypeError, lambda: x < y) + raises(TypeError, lambda: x - y < 0) + + +def test_nan_equality_exceptions(): + # See issue #7774 + import random + assert Equality(nan, nan) is S.false + assert Unequality(nan, nan) is S.true + + # See issue #7773 + A = (x, S.Zero, S.One/3, pi, oo, -oo) + assert Equality(nan, random.choice(A)) is S.false + assert Equality(random.choice(A), nan) is S.false + assert Unequality(nan, random.choice(A)) is S.true + assert Unequality(random.choice(A), nan) is S.true + + +def test_nan_inequality_raise_errors(): + # See discussion in pull request #7776. We test inequalities with + # a set including examples of various classes. + for q in (x, S.Zero, S(10), S.One/3, pi, S(1.3), oo, -oo, nan): + assert_all_ineq_raise_TypeError(q, nan) + + +def test_nan_complex_inequalities(): + # Comparisons of NaN with non-real raise errors, we're not too + # fussy whether its the NaN error or complex error. + for r in (I, zoo, Symbol('z', imaginary=True)): + assert_all_ineq_raise_TypeError(r, nan) + + +def test_complex_infinity_inequalities(): + raises(TypeError, lambda: zoo > 0) + raises(TypeError, lambda: zoo >= 0) + raises(TypeError, lambda: zoo < 0) + raises(TypeError, lambda: zoo <= 0) + + +def test_inequalities_symbol_name_same(): + """Using the operator and functional forms should give same results.""" + # We test all combinations from a set + # FIXME: could replace with random selection after test passes + A = (x, y, S.Zero, S.One/3, pi, oo, -oo) + for a in A: + for b in A: + assert Gt(a, b) == (a > b) + assert Lt(a, b) == (a < b) + assert Ge(a, b) == (a >= b) + assert Le(a, b) == (a <= b) + + for b in (y, S.Zero, S.One/3, pi, oo, -oo): + assert Gt(x, b, evaluate=False) == (x > b) + assert Lt(x, b, evaluate=False) == (x < b) + assert Ge(x, b, evaluate=False) == (x >= b) + assert Le(x, b, evaluate=False) == (x <= b) + + for b in (y, S.Zero, S.One/3, pi, oo, -oo): + assert Gt(b, x, evaluate=False) == (b > x) + assert Lt(b, x, evaluate=False) == (b < x) + assert Ge(b, x, evaluate=False) == (b >= x) + assert Le(b, x, evaluate=False) == (b <= x) + + +def test_inequalities_symbol_name_same_complex(): + """Using the operator and functional forms should give same results. + With complex non-real numbers, both should raise errors. + """ + # FIXME: could replace with random selection after test passes + for a in (x, S.Zero, S.One/3, pi, oo, Rational(1, 3)): + raises(TypeError, lambda: Gt(a, I)) + raises(TypeError, lambda: a > I) + raises(TypeError, lambda: Lt(a, I)) + raises(TypeError, lambda: a < I) + raises(TypeError, lambda: Ge(a, I)) + raises(TypeError, lambda: a >= I) + raises(TypeError, lambda: Le(a, I)) + raises(TypeError, lambda: a <= I) + + +def test_inequalities_cant_sympify_other(): + # see issue 7833 + from operator import gt, lt, ge, le + + bar = "foo" + + for a in (x, S.Zero, S.One/3, pi, I, zoo, oo, -oo, nan, Rational(1, 3)): + for op in (lt, gt, le, ge): + raises(TypeError, lambda: op(a, bar)) + + +def test_ineq_avoid_wild_symbol_flip(): + # see issue #7951, we try to avoid this internally, e.g., by using + # __lt__ instead of "<". + from sympy.core.symbol import Wild + p = symbols('p', cls=Wild) + # x > p might flip, but Gt should not: + assert Gt(x, p) == Gt(x, p, evaluate=False) + # Previously failed as 'p > x': + e = Lt(x, y).subs({y: p}) + assert e == Lt(x, p, evaluate=False) + # Previously failed as 'p <= x': + e = Ge(x, p).doit() + assert e == Ge(x, p, evaluate=False) + + +def test_issue_8245(): + a = S("6506833320952669167898688709329/5070602400912917605986812821504") + assert rel_check(a, a.n(10)) + assert rel_check(a, a.n(20)) + assert rel_check(a, a.n()) + # prec of 31 is enough to fully capture a as mpf + assert Float(a, 31) == Float(str(a.p), '')/Float(str(a.q), '') + for i in range(31): + r = Rational(Float(a, i)) + f = Float(r) + assert (f < a) == (Rational(f) < a) + # test sign handling + assert (-f < -a) == (Rational(-f) < -a) + # test equivalence handling + isa = Float(a.p,'')/Float(a.q,'') + assert isa <= a + assert not isa < a + assert isa >= a + assert not isa > a + assert isa > 0 + + a = sqrt(2) + r = Rational(str(a.n(30))) + assert rel_check(a, r) + + a = sqrt(2) + r = Rational(str(a.n(29))) + assert rel_check(a, r) + + assert Eq(log(cos(2)**2 + sin(2)**2), 0) is S.true + + +def test_issue_8449(): + p = Symbol('p', nonnegative=True) + assert Lt(-oo, p) + assert Ge(-oo, p) is S.false + assert Gt(oo, -p) + assert Le(oo, -p) is S.false + + +def test_simplify_relational(): + assert simplify(x*(y + 1) - x*y - x + 1 < x) == (x > 1) + assert simplify(x*(y + 1) - x*y - x - 1 < x) == (x > -1) + assert simplify(x < x*(y + 1) - x*y - x + 1) == (x < 1) + q, r = symbols("q r") + assert (((-q + r) - (q - r)) <= 0).simplify() == (q >= r) + root2 = sqrt(2) + equation = ((root2 * (-q + r) - root2 * (q - r)) <= 0).simplify() + assert equation == (q >= r) + r = S.One < x + # canonical operations are not the same as simplification, + # so if there is no simplification, canonicalization will + # be done unless the measure forbids it + assert simplify(r) == r.canonical + assert simplify(r, ratio=0) != r.canonical + # this is not a random test; in _eval_simplify + # this will simplify to S.false and that is the + # reason for the 'if r.is_Relational' in Relational's + # _eval_simplify routine + assert simplify(-(2**(pi*Rational(3, 2)) + 6**pi)**(1/pi) + + 2*(2**(pi/2) + 3**pi)**(1/pi) < 0) is S.false + # canonical at least + assert Eq(y, x).simplify() == Eq(x, y) + assert Eq(x - 1, 0).simplify() == Eq(x, 1) + assert Eq(x - 1, x).simplify() == S.false + assert Eq(2*x - 1, x).simplify() == Eq(x, 1) + assert Eq(2*x, 4).simplify() == Eq(x, 2) + z = cos(1)**2 + sin(1)**2 - 1 # z.is_zero is None + assert Eq(z*x, 0).simplify() == S.true + + assert Ne(y, x).simplify() == Ne(x, y) + assert Ne(x - 1, 0).simplify() == Ne(x, 1) + assert Ne(x - 1, x).simplify() == S.true + assert Ne(2*x - 1, x).simplify() == Ne(x, 1) + assert Ne(2*x, 4).simplify() == Ne(x, 2) + assert Ne(z*x, 0).simplify() == S.false + + # No real-valued assumptions + assert Ge(y, x).simplify() == Le(x, y) + assert Ge(x - 1, 0).simplify() == Ge(x, 1) + assert Ge(x - 1, x).simplify() == S.false + assert Ge(2*x - 1, x).simplify() == Ge(x, 1) + assert Ge(2*x, 4).simplify() == Ge(x, 2) + assert Ge(z*x, 0).simplify() == S.true + assert Ge(x, -2).simplify() == Ge(x, -2) + assert Ge(-x, -2).simplify() == Le(x, 2) + assert Ge(x, 2).simplify() == Ge(x, 2) + assert Ge(-x, 2).simplify() == Le(x, -2) + + assert Le(y, x).simplify() == Ge(x, y) + assert Le(x - 1, 0).simplify() == Le(x, 1) + assert Le(x - 1, x).simplify() == S.true + assert Le(2*x - 1, x).simplify() == Le(x, 1) + assert Le(2*x, 4).simplify() == Le(x, 2) + assert Le(z*x, 0).simplify() == S.true + assert Le(x, -2).simplify() == Le(x, -2) + assert Le(-x, -2).simplify() == Ge(x, 2) + assert Le(x, 2).simplify() == Le(x, 2) + assert Le(-x, 2).simplify() == Ge(x, -2) + + assert Gt(y, x).simplify() == Lt(x, y) + assert Gt(x - 1, 0).simplify() == Gt(x, 1) + assert Gt(x - 1, x).simplify() == S.false + assert Gt(2*x - 1, x).simplify() == Gt(x, 1) + assert Gt(2*x, 4).simplify() == Gt(x, 2) + assert Gt(z*x, 0).simplify() == S.false + assert Gt(x, -2).simplify() == Gt(x, -2) + assert Gt(-x, -2).simplify() == Lt(x, 2) + assert Gt(x, 2).simplify() == Gt(x, 2) + assert Gt(-x, 2).simplify() == Lt(x, -2) + + assert Lt(y, x).simplify() == Gt(x, y) + assert Lt(x - 1, 0).simplify() == Lt(x, 1) + assert Lt(x - 1, x).simplify() == S.true + assert Lt(2*x - 1, x).simplify() == Lt(x, 1) + assert Lt(2*x, 4).simplify() == Lt(x, 2) + assert Lt(z*x, 0).simplify() == S.false + assert Lt(x, -2).simplify() == Lt(x, -2) + assert Lt(-x, -2).simplify() == Gt(x, 2) + assert Lt(x, 2).simplify() == Lt(x, 2) + assert Lt(-x, 2).simplify() == Gt(x, -2) + + # Test particular branches of _eval_simplify + m = exp(1) - exp_polar(1) + assert simplify(m*x > 1) is S.false + # These two test the same branch + assert simplify(m*x + 2*m*y > 1) is S.false + assert simplify(m*x + y > 1 + y) is S.false + + +def test_equals(): + w, x, y, z = symbols('w:z') + f = Function('f') + assert Eq(x, 1).equals(Eq(x*(y + 1) - x*y - x + 1, x)) + assert Eq(x, y).equals(x < y, True) == False + assert Eq(x, f(1)).equals(Eq(x, f(2)), True) == f(1) - f(2) + assert Eq(f(1), y).equals(Eq(f(2), y), True) == f(1) - f(2) + assert Eq(x, f(1)).equals(Eq(f(2), x), True) == f(1) - f(2) + assert Eq(f(1), x).equals(Eq(x, f(2)), True) == f(1) - f(2) + assert Eq(w, x).equals(Eq(y, z), True) == False + assert Eq(f(1), f(2)).equals(Eq(f(3), f(4)), True) == f(1) - f(3) + assert (x < y).equals(y > x, True) == True + assert (x < y).equals(y >= x, True) == False + assert (x < y).equals(z < y, True) == False + assert (x < y).equals(x < z, True) == False + assert (x < f(1)).equals(x < f(2), True) == f(1) - f(2) + assert (f(1) < x).equals(f(2) < x, True) == f(1) - f(2) + + +def test_reversed(): + assert (x < y).reversed == (y > x) + assert (x <= y).reversed == (y >= x) + assert Eq(x, y, evaluate=False).reversed == Eq(y, x, evaluate=False) + assert Ne(x, y, evaluate=False).reversed == Ne(y, x, evaluate=False) + assert (x >= y).reversed == (y <= x) + assert (x > y).reversed == (y < x) + + +def test_canonical(): + c = [i.canonical for i in ( + x + y < z, + x + 2 > 3, + x < 2, + S(2) > x, + x**2 > -x/y, + Gt(3, 2, evaluate=False) + )] + assert [i.canonical for i in c] == c + assert [i.reversed.canonical for i in c] == c + assert not any(i.lhs.is_Number and not i.rhs.is_Number for i in c) + + c = [i.reversed.func(i.rhs, i.lhs, evaluate=False).canonical for i in c] + assert [i.canonical for i in c] == c + assert [i.reversed.canonical for i in c] == c + assert not any(i.lhs.is_Number and not i.rhs.is_Number for i in c) + assert Eq(y < x, x > y).canonical is S.true + + +@XFAIL +def test_issue_8444_nonworkingtests(): + x = symbols('x', real=True) + assert (x <= oo) == (x >= -oo) == True + + x = symbols('x') + assert x >= floor(x) + assert (x < floor(x)) == False + assert x <= ceiling(x) + assert (x > ceiling(x)) == False + + +def test_issue_8444_workingtests(): + x = symbols('x') + assert Gt(x, floor(x)) == Gt(x, floor(x), evaluate=False) + assert Ge(x, floor(x)) == Ge(x, floor(x), evaluate=False) + assert Lt(x, ceiling(x)) == Lt(x, ceiling(x), evaluate=False) + assert Le(x, ceiling(x)) == Le(x, ceiling(x), evaluate=False) + i = symbols('i', integer=True) + assert (i > floor(i)) == False + assert (i < ceiling(i)) == False + + +def test_issue_10304(): + d = cos(1)**2 + sin(1)**2 - 1 + assert d.is_comparable is False # if this fails, find a new d + e = 1 + d*I + assert simplify(Eq(e, 0)) is S.false + + +def test_issue_18412(): + d = (Rational(1, 6) + z / 4 / y) + assert Eq(x, pi * y**3 * d).replace(y**3, z) == Eq(x, pi * z * d) + + +def test_issue_10401(): + x = symbols('x') + fin = symbols('inf', finite=True) + inf = symbols('inf', infinite=True) + inf2 = symbols('inf2', infinite=True) + infx = symbols('infx', infinite=True, extended_real=True) + # Used in the commented tests below: + #infx2 = symbols('infx2', infinite=True, extended_real=True) + infnx = symbols('inf~x', infinite=True, extended_real=False) + infnx2 = symbols('inf~x2', infinite=True, extended_real=False) + infp = symbols('infp', infinite=True, extended_positive=True) + infp1 = symbols('infp1', infinite=True, extended_positive=True) + infn = symbols('infn', infinite=True, extended_negative=True) + zero = symbols('z', zero=True) + nonzero = symbols('nz', zero=False, finite=True) + + assert Eq(1/(1/x + 1), 1).func is Eq + assert Eq(1/(1/x + 1), 1).subs(x, S.ComplexInfinity) is S.true + assert Eq(1/(1/fin + 1), 1) is S.false + + T, F = S.true, S.false + assert Eq(fin, inf) is F + assert Eq(inf, inf2) not in (T, F) and inf != inf2 + assert Eq(1 + inf, 2 + inf2) not in (T, F) and inf != inf2 + assert Eq(infp, infp1) is T + assert Eq(infp, infn) is F + assert Eq(1 + I*oo, I*oo) is F + assert Eq(I*oo, 1 + I*oo) is F + assert Eq(1 + I*oo, 2 + I*oo) is F + assert Eq(1 + I*oo, 2 + I*infx) is F + assert Eq(1 + I*oo, 2 + infx) is F + # FIXME: The test below fails because (-infx).is_extended_positive is True + # (should be None) + #assert Eq(1 + I*infx, 1 + I*infx2) not in (T, F) and infx != infx2 + # + assert Eq(zoo, sqrt(2) + I*oo) is F + assert Eq(zoo, oo) is F + r = Symbol('r', real=True) + i = Symbol('i', imaginary=True) + assert Eq(i*I, r) not in (T, F) + assert Eq(infx, infnx) is F + assert Eq(infnx, infnx2) not in (T, F) and infnx != infnx2 + assert Eq(zoo, oo) is F + assert Eq(inf/inf2, 0) is F + assert Eq(inf/fin, 0) is F + assert Eq(fin/inf, 0) is T + assert Eq(zero/nonzero, 0) is T and ((zero/nonzero) != 0) + # The commented out test below is incorrect because: + assert zoo == -zoo + assert Eq(zoo, -zoo) is T + assert Eq(oo, -oo) is F + assert Eq(inf, -inf) not in (T, F) + + assert Eq(fin/(fin + 1), 1) is S.false + + o = symbols('o', odd=True) + assert Eq(o, 2*o) is S.false + + p = symbols('p', positive=True) + assert Eq(p/(p - 1), 1) is F + + +def test_issue_10633(): + assert Eq(True, False) == False + assert Eq(False, True) == False + assert Eq(True, True) == True + assert Eq(False, False) == True + + +def test_issue_10927(): + x = symbols('x') + assert str(Eq(x, oo)) == 'Eq(x, oo)' + assert str(Eq(x, -oo)) == 'Eq(x, -oo)' + + +def test_issues_13081_12583_12534(): + # 13081 + r = Rational('905502432259640373/288230376151711744') + assert (r < pi) is S.false + assert (r > pi) is S.true + # 12583 + v = sqrt(2) + u = sqrt(v) + 2/sqrt(10 - 8/sqrt(2 - v) + 4*v*(1/sqrt(2 - v) - 1)) + assert (u >= 0) is S.true + # 12534; Rational vs NumberSymbol + # here are some precisions for which Rational forms + # at a lower and higher precision bracket the value of pi + # e.g. for p = 20: + # Rational(pi.n(p + 1)).n(25) = 3.14159265358979323846 2834 + # pi.n(25) = 3.14159265358979323846 2643 + # Rational(pi.n(p )).n(25) = 3.14159265358979323846 1987 + assert [p for p in range(20, 50) if + (Rational(pi.n(p)) < pi) and + (pi < Rational(pi.n(p + 1)))] == [20, 24, 27, 33, 37, 43, 48] + # pick one such precision and affirm that the reversed operation + # gives the opposite result, i.e. if x < y is true then x > y + # must be false + for i in (20, 21): + v = pi.n(i) + assert rel_check(Rational(v), pi) + assert rel_check(v, pi) + assert rel_check(pi.n(20), pi.n(21)) + # Float vs Rational + # the rational form is less than the floating representation + # at the same precision + assert [i for i in range(15, 50) if Rational(pi.n(i)) > pi.n(i)] == [] + # this should be the same if we reverse the relational + assert [i for i in range(15, 50) if pi.n(i) < Rational(pi.n(i))] == [] + +def test_issue_18188(): + from sympy.sets.conditionset import ConditionSet + result1 = Eq(x*cos(x) - 3*sin(x), 0) + assert result1.as_set() == ConditionSet(x, Eq(x*cos(x) - 3*sin(x), 0), Reals) + + result2 = Eq(x**2 + sqrt(x*2) + sin(x), 0) + assert result2.as_set() == ConditionSet(x, Eq(sqrt(2)*sqrt(x) + x**2 + sin(x), 0), Reals) + +def test_binary_symbols(): + ans = {x} + for f in Eq, Ne: + for t in S.true, S.false: + eq = f(x, S.true) + assert eq.binary_symbols == ans + assert eq.reversed.binary_symbols == ans + assert f(x, 1).binary_symbols == set() + + +def test_rel_args(): + # can't have Boolean args; this is automatic for True/False + # with Python 3 and we confirm that SymPy does the same + # for true/false + for op in ['<', '<=', '>', '>=']: + for b in (S.true, x < 1, And(x, y)): + for v in (0.1, 1, 2**32, t, S.One): + raises(TypeError, lambda: Relational(b, v, op)) + + +def test_nothing_happens_to_Eq_condition_during_simplify(): + # issue 25701 + r = symbols('r', real=True) + assert Eq(2*sign(r + 3)/(5*Abs(r + 3)**Rational(3, 5)), 0 + ).simplify() == Eq(Piecewise( + (0, Eq(r, -3)), ((r + 3)/(5*Abs((r + 3)**Rational(8, 5)))*2, True)), 0) + + +def test_issue_15847(): + a = Ne(x*(x + y), x**2 + x*y) + assert simplify(a) == False + + +def test_negated_property(): + eq = Eq(x, y) + assert eq.negated == Ne(x, y) + + eq = Ne(x, y) + assert eq.negated == Eq(x, y) + + eq = Ge(x + y, y - x) + assert eq.negated == Lt(x + y, y - x) + + for f in (Eq, Ne, Ge, Gt, Le, Lt): + assert f(x, y).negated.negated == f(x, y) + + +def test_reversedsign_property(): + eq = Eq(x, y) + assert eq.reversedsign == Eq(-x, -y) + + eq = Ne(x, y) + assert eq.reversedsign == Ne(-x, -y) + + eq = Ge(x + y, y - x) + assert eq.reversedsign == Le(-x - y, x - y) + + for f in (Eq, Ne, Ge, Gt, Le, Lt): + assert f(x, y).reversedsign.reversedsign == f(x, y) + + for f in (Eq, Ne, Ge, Gt, Le, Lt): + assert f(-x, y).reversedsign.reversedsign == f(-x, y) + + for f in (Eq, Ne, Ge, Gt, Le, Lt): + assert f(x, -y).reversedsign.reversedsign == f(x, -y) + + for f in (Eq, Ne, Ge, Gt, Le, Lt): + assert f(-x, -y).reversedsign.reversedsign == f(-x, -y) + + +def test_reversed_reversedsign_property(): + for f in (Eq, Ne, Ge, Gt, Le, Lt): + assert f(x, y).reversed.reversedsign == f(x, y).reversedsign.reversed + + for f in (Eq, Ne, Ge, Gt, Le, Lt): + assert f(-x, y).reversed.reversedsign == f(-x, y).reversedsign.reversed + + for f in (Eq, Ne, Ge, Gt, Le, Lt): + assert f(x, -y).reversed.reversedsign == f(x, -y).reversedsign.reversed + + for f in (Eq, Ne, Ge, Gt, Le, Lt): + assert f(-x, -y).reversed.reversedsign == \ + f(-x, -y).reversedsign.reversed + + +def test_improved_canonical(): + def test_different_forms(listofforms): + for form1, form2 in combinations(listofforms, 2): + assert form1.canonical == form2.canonical + + def generate_forms(expr): + return [expr, expr.reversed, expr.reversedsign, + expr.reversed.reversedsign] + + test_different_forms(generate_forms(x > -y)) + test_different_forms(generate_forms(x >= -y)) + test_different_forms(generate_forms(Eq(x, -y))) + test_different_forms(generate_forms(Ne(x, -y))) + test_different_forms(generate_forms(pi < x)) + test_different_forms(generate_forms(pi - 5*y < -x + 2*y**2 - 7)) + + assert (pi >= x).canonical == (x <= pi) + + +def test_set_equality_canonical(): + a, b, c = symbols('a b c') + + A = Eq(FiniteSet(a, b, c), FiniteSet(1, 2, 3)) + B = Ne(FiniteSet(a, b, c), FiniteSet(4, 5, 6)) + + assert A.canonical == A.reversed + assert B.canonical == B.reversed + + +def test_trigsimp(): + # issue 16736 + s, c = sin(2*x), cos(2*x) + eq = Eq(s, c) + assert trigsimp(eq) == eq # no rearrangement of sides + # simplification of sides might result in + # an unevaluated Eq + changed = trigsimp(Eq(s + c, sqrt(2))) + assert isinstance(changed, Eq) + assert changed.subs(x, pi/8) is S.true + # or an evaluated one + assert trigsimp(Eq(cos(x)**2 + sin(x)**2, 1)) is S.true + + +def test_polynomial_relation_simplification(): + assert Ge(3*x*(x + 1) + 4, 3*x).simplify() in [Ge(x**2, -Rational(4,3)), Le(-x**2, Rational(4, 3))] + assert Le(-(3*x*(x + 1) + 4), -3*x).simplify() in [Ge(x**2, -Rational(4,3)), Le(-x**2, Rational(4, 3))] + assert ((x**2+3)*(x**2-1)+3*x >= 2*x**2).simplify() in [(x**4 + 3*x >= 3), (-x**4 - 3*x <= -3)] + + +def test_multivariate_linear_function_simplification(): + assert Ge(x + y, x - y).simplify() == Ge(y, 0) + assert Le(-x + y, -x - y).simplify() == Le(y, 0) + assert Eq(2*x + y, 2*x + y - 3).simplify() == False + assert (2*x + y > 2*x + y - 3).simplify() == True + assert (2*x + y < 2*x + y - 3).simplify() == False + assert (2*x + y < 2*x + y + 3).simplify() == True + a, b, c, d, e, f, g = symbols('a b c d e f g') + assert Lt(a + b + c + 2*d, 3*d - f + g). simplify() == Lt(a, -b - c + d - f + g) + + +def test_nonpolymonial_relations(): + assert Eq(cos(x), 0).simplify() == Eq(cos(x), 0) + +def test_18778(): + raises(TypeError, lambda: is_le(Basic(), Basic())) + raises(TypeError, lambda: is_gt(Basic(), Basic())) + raises(TypeError, lambda: is_ge(Basic(), Basic())) + raises(TypeError, lambda: is_lt(Basic(), Basic())) + +def test_EvalEq(): + """ + + This test exists to ensure backwards compatibility. + The method to use is _eval_is_eq + """ + from sympy.core.expr import Expr + + class PowTest(Expr): + def __new__(cls, base, exp): + return Basic.__new__(PowTest, _sympify(base), _sympify(exp)) + + def _eval_Eq(lhs, rhs): + if type(lhs) == PowTest and type(rhs) == PowTest: + return lhs.args[0] == rhs.args[0] and lhs.args[1] == rhs.args[1] + + assert is_eq(PowTest(3, 4), PowTest(3,4)) + assert is_eq(PowTest(3, 4), _sympify(4)) is None + assert is_neq(PowTest(3, 4), PowTest(3,7)) + + +def test_is_eq(): + # test assumptions + assert is_eq(x, y, Q.infinite(x) & Q.finite(y)) is False + assert is_eq(x, y, Q.infinite(x) & Q.infinite(y) & Q.extended_real(x) & ~Q.extended_real(y)) is False + assert is_eq(x, y, Q.infinite(x) & Q.infinite(y) & Q.extended_positive(x) & Q.extended_negative(y)) is False + + assert is_eq(x+I, y+I, Q.infinite(x) & Q.finite(y)) is False + assert is_eq(1+x*I, 1+y*I, Q.infinite(x) & Q.finite(y)) is False + + assert is_eq(x, S(0), assumptions=Q.zero(x)) + assert is_eq(x, S(0), assumptions=~Q.zero(x)) is False + assert is_eq(x, S(0), assumptions=Q.nonzero(x)) is False + assert is_neq(x, S(0), assumptions=Q.zero(x)) is False + assert is_neq(x, S(0), assumptions=~Q.zero(x)) + assert is_neq(x, S(0), assumptions=Q.nonzero(x)) + + # test registration + class PowTest(Expr): + def __new__(cls, base, exp): + return Basic.__new__(cls, _sympify(base), _sympify(exp)) + + @dispatch(PowTest, PowTest) + def _eval_is_eq(lhs, rhs): + if type(lhs) == PowTest and type(rhs) == PowTest: + return fuzzy_and([is_eq(lhs.args[0], rhs.args[0]), is_eq(lhs.args[1], rhs.args[1])]) + + assert is_eq(PowTest(3, 4), PowTest(3,4)) + assert is_eq(PowTest(3, 4), _sympify(4)) is None + assert is_neq(PowTest(3, 4), PowTest(3,7)) + + +def test_is_ge_le(): + # test assumptions + assert is_ge(x, S(0), Q.nonnegative(x)) is True + assert is_ge(x, S(0), Q.negative(x)) is False + + # test registration + class PowTest(Expr): + def __new__(cls, base, exp): + return Basic.__new__(cls, _sympify(base), _sympify(exp)) + + @dispatch(PowTest, PowTest) + def _eval_is_ge(lhs, rhs): + if type(lhs) == PowTest and type(rhs) == PowTest: + return fuzzy_and([is_ge(lhs.args[0], rhs.args[0]), is_ge(lhs.args[1], rhs.args[1])]) + + assert is_ge(PowTest(3, 9), PowTest(3,2)) + assert is_gt(PowTest(3, 9), PowTest(3,2)) + assert is_le(PowTest(3, 2), PowTest(3,9)) + assert is_lt(PowTest(3, 2), PowTest(3,9)) + + +def test_weak_strict(): + for func in (Eq, Ne): + eq = func(x, 1) + assert eq.strict == eq.weak == eq + eq = Gt(x, 1) + assert eq.weak == Ge(x, 1) + assert eq.strict == eq + eq = Lt(x, 1) + assert eq.weak == Le(x, 1) + assert eq.strict == eq + eq = Ge(x, 1) + assert eq.strict == Gt(x, 1) + assert eq.weak == eq + eq = Le(x, 1) + assert eq.strict == Lt(x, 1) + assert eq.weak == eq + + +def test_issue_23731(): + i = symbols('i', integer=True) + assert unchanged(Eq, i, 1.0) + assert unchanged(Eq, i/2, 0.5) + ni = symbols('ni', integer=False) + assert Eq(ni, 1) == False + assert unchanged(Eq, ni, .1) + assert Eq(ni, 1.0) == False + nr = symbols('nr', rational=False) + assert Eq(nr, .1) == False + + +def test_rewrite_Add(): + from sympy.testing.pytest import warns_deprecated_sympy + with warns_deprecated_sympy(): + assert Eq(x, y).rewrite(Add) == x - y diff --git a/lib/python3.12/site-packages/sympy/core/tests/test_singleton.py b/lib/python3.12/site-packages/sympy/core/tests/test_singleton.py new file mode 100644 index 0000000000000000000000000000000000000000..893713f27d74b884391ad800d186eafe5337ab1c --- /dev/null +++ b/lib/python3.12/site-packages/sympy/core/tests/test_singleton.py @@ -0,0 +1,76 @@ +from sympy.core.basic import Basic +from sympy.core.numbers import Rational +from sympy.core.singleton import S, Singleton + +def test_Singleton(): + + class MySingleton(Basic, metaclass=Singleton): + pass + + MySingleton() # force instantiation + assert MySingleton() is not Basic() + assert MySingleton() is MySingleton() + assert S.MySingleton is MySingleton() + + class MySingleton_sub(MySingleton): + pass + + MySingleton_sub() + assert MySingleton_sub() is not MySingleton() + assert MySingleton_sub() is MySingleton_sub() + +def test_singleton_redefinition(): + class TestSingleton(Basic, metaclass=Singleton): + pass + + assert TestSingleton() is S.TestSingleton + + class TestSingleton(Basic, metaclass=Singleton): + pass + + assert TestSingleton() is S.TestSingleton + +def test_names_in_namespace(): + # Every singleton name should be accessible from the 'from sympy import *' + # namespace in addition to the S object. However, it does not need to be + # by the same name (e.g., oo instead of S.Infinity). + + # As a general rule, things should only be added to the singleton registry + # if they are used often enough that code can benefit either from the + # performance benefit of being able to use 'is' (this only matters in very + # tight loops), or from the memory savings of having exactly one instance + # (this matters for the numbers singletons, but very little else). The + # singleton registry is already a bit overpopulated, and things cannot be + # removed from it without breaking backwards compatibility. So if you got + # here by adding something new to the singletons, ask yourself if it + # really needs to be singletonized. Note that SymPy classes compare to one + # another just fine, so Class() == Class() will give True even if each + # Class() returns a new instance. Having unique instances is only + # necessary for the above noted performance gains. It should not be needed + # for any behavioral purposes. + + # If you determine that something really should be a singleton, it must be + # accessible to sympify() without using 'S' (hence this test). Also, its + # str printer should print a form that does not use S. This is because + # sympify() disables attribute lookups by default for safety purposes. + d = {} + exec('from sympy import *', d) + + for name in dir(S) + list(S._classes_to_install): + if name.startswith('_'): + continue + if name == 'register': + continue + if isinstance(getattr(S, name), Rational): + continue + if getattr(S, name).__module__.startswith('sympy.physics'): + continue + if name in ['MySingleton', 'MySingleton_sub', 'TestSingleton']: + # From the tests above + continue + if name == 'NegativeInfinity': + # Accessible by -oo + continue + + # Use is here to ensure it is the exact same object + assert any(getattr(S, name) is i for i in d.values()), name diff --git a/lib/python3.12/site-packages/sympy/core/tests/test_sorting.py b/lib/python3.12/site-packages/sympy/core/tests/test_sorting.py new file mode 100644 index 0000000000000000000000000000000000000000..a18dbfb624552cf2fa11bb7f3c3a9e865caeb0c4 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/core/tests/test_sorting.py @@ -0,0 +1,28 @@ +from sympy.core.sorting import default_sort_key, ordered +from sympy.testing.pytest import raises + +from sympy.abc import x + + +def test_default_sort_key(): + func = lambda x: x + assert sorted([func, x, func], key=default_sort_key) == [func, func, x] + + class C: + def __repr__(self): + return 'x.y' + func = C() + assert sorted([x, func], key=default_sort_key) == [func, x] + + +def test_ordered(): + # Issue 7210 - this had been failing with python2/3 problems + assert (list(ordered([{1:3, 2:4, 9:10}, {1:3}])) == \ + [{1: 3}, {1: 3, 2: 4, 9: 10}]) + # warnings should not be raised for identical items + l = [1, 1] + assert list(ordered(l, warn=True)) == l + l = [[1], [2], [1]] + assert list(ordered(l, warn=True)) == [[1], [1], [2]] + raises(ValueError, lambda: list(ordered(['a', 'ab'], keys=[lambda x: x[0]], + default=False, warn=True))) diff --git a/lib/python3.12/site-packages/sympy/core/tests/test_traversal.py b/lib/python3.12/site-packages/sympy/core/tests/test_traversal.py new file mode 100644 index 0000000000000000000000000000000000000000..8bf067283eaba5d4a073a73feb07aac199055a7f --- /dev/null +++ b/lib/python3.12/site-packages/sympy/core/tests/test_traversal.py @@ -0,0 +1,119 @@ +from sympy.core.basic import Basic +from sympy.core.containers import Tuple +from sympy.core.sorting import default_sort_key +from sympy.core.symbol import symbols +from sympy.core.singleton import S +from sympy.core.function import expand, Function +from sympy.core.numbers import I +from sympy.integrals.integrals import Integral +from sympy.polys.polytools import factor +from sympy.core.traversal import preorder_traversal, use, postorder_traversal, iterargs, iterfreeargs +from sympy.functions.elementary.piecewise import ExprCondPair, Piecewise +from sympy.testing.pytest import warns_deprecated_sympy +from sympy.utilities.iterables import capture + +b1 = Basic() +b2 = Basic(b1) +b3 = Basic(b2) +b21 = Basic(b2, b1) + + +def test_preorder_traversal(): + expr = Basic(b21, b3) + assert list( + preorder_traversal(expr)) == [expr, b21, b2, b1, b1, b3, b2, b1] + assert list(preorder_traversal(('abc', ('d', 'ef')))) == [ + ('abc', ('d', 'ef')), 'abc', ('d', 'ef'), 'd', 'ef'] + + result = [] + pt = preorder_traversal(expr) + for i in pt: + result.append(i) + if i == b2: + pt.skip() + assert result == [expr, b21, b2, b1, b3, b2] + + w, x, y, z = symbols('w:z') + expr = z + w*(x + y) + assert list(preorder_traversal([expr], keys=default_sort_key)) == \ + [[w*(x + y) + z], w*(x + y) + z, z, w*(x + y), w, x + y, x, y] + assert list(preorder_traversal((x + y)*z, keys=True)) == \ + [z*(x + y), z, x + y, x, y] + + +def test_use(): + x, y = symbols('x y') + + assert use(0, expand) == 0 + + f = (x + y)**2*x + 1 + + assert use(f, expand, level=0) == x**3 + 2*x**2*y + x*y**2 + + 1 + assert use(f, expand, level=1) == x**3 + 2*x**2*y + x*y**2 + + 1 + assert use(f, expand, level=2) == 1 + x*(2*x*y + x**2 + y**2) + assert use(f, expand, level=3) == (x + y)**2*x + 1 + + f = (x**2 + 1)**2 - 1 + kwargs = {'gaussian': True} + + assert use(f, factor, level=0, kwargs=kwargs) == x**2*(x**2 + 2) + assert use(f, factor, level=1, kwargs=kwargs) == (x + I)**2*(x - I)**2 - 1 + assert use(f, factor, level=2, kwargs=kwargs) == (x + I)**2*(x - I)**2 - 1 + assert use(f, factor, level=3, kwargs=kwargs) == (x**2 + 1)**2 - 1 + + +def test_postorder_traversal(): + x, y, z, w = symbols('x y z w') + expr = z + w*(x + y) + expected = [z, w, x, y, x + y, w*(x + y), w*(x + y) + z] + assert list(postorder_traversal(expr, keys=default_sort_key)) == expected + assert list(postorder_traversal(expr, keys=True)) == expected + + expr = Piecewise((x, x < 1), (x**2, True)) + expected = [ + x, 1, x, x < 1, ExprCondPair(x, x < 1), + 2, x, x**2, S.true, + ExprCondPair(x**2, True), Piecewise((x, x < 1), (x**2, True)) + ] + assert list(postorder_traversal(expr, keys=default_sort_key)) == expected + assert list(postorder_traversal( + [expr], keys=default_sort_key)) == expected + [[expr]] + + assert list(postorder_traversal(Integral(x**2, (x, 0, 1)), + keys=default_sort_key)) == [ + 2, x, x**2, 0, 1, x, Tuple(x, 0, 1), + Integral(x**2, Tuple(x, 0, 1)) + ] + assert list(postorder_traversal(('abc', ('d', 'ef')))) == [ + 'abc', 'd', 'ef', ('d', 'ef'), ('abc', ('d', 'ef'))] + + +def test_iterargs(): + f = Function('f') + x = symbols('x') + assert list(iterfreeargs(Integral(f(x), (f(x), 1)))) == [ + Integral(f(x), (f(x), 1)), 1] + assert list(iterargs(Integral(f(x), (f(x), 1)))) == [ + Integral(f(x), (f(x), 1)), f(x), (f(x), 1), x, f(x), 1, x] + +def test_deprecated_imports(): + x = symbols('x') + + with warns_deprecated_sympy(): + from sympy.core.basic import preorder_traversal + preorder_traversal(x) + with warns_deprecated_sympy(): + from sympy.simplify.simplify import bottom_up + bottom_up(x, lambda x: x) + with warns_deprecated_sympy(): + from sympy.simplify.simplify import walk + walk(x, lambda x: x) + with warns_deprecated_sympy(): + from sympy.simplify.traversaltools import use + use(x, lambda x: x) + with warns_deprecated_sympy(): + from sympy.utilities.iterables import postorder_traversal + postorder_traversal(x) + with warns_deprecated_sympy(): + from sympy.utilities.iterables import interactive_traversal + capture(lambda: interactive_traversal(x)) diff --git a/lib/python3.12/site-packages/sympy/core/tests/test_truediv.py b/lib/python3.12/site-packages/sympy/core/tests/test_truediv.py new file mode 100644 index 0000000000000000000000000000000000000000..1fcf9e1ab754d05a3b47e7ec0c2be5ea9929da02 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/core/tests/test_truediv.py @@ -0,0 +1,54 @@ +#this module tests that SymPy works with true division turned on + +from sympy.core.numbers import (Float, Rational) +from sympy.core.symbol import Symbol + + +def test_truediv(): + assert 1/2 != 0 + assert Rational(1)/2 != 0 + + +def dotest(s): + x = Symbol("x") + y = Symbol("y") + l = [ + Rational(2), + Float("1.3"), + x, + y, + pow(x, y)*y, + 5, + 5.5 + ] + for x in l: + for y in l: + s(x, y) + return True + + +def test_basic(): + def s(a, b): + x = a + x = +a + x = -a + x = a + b + x = a - b + x = a*b + x = a/b + x = a**b + del x + assert dotest(s) + + +def test_ibasic(): + def s(a, b): + x = a + x += b + x = a + x -= b + x = a + x *= b + x = a + x /= b + assert dotest(s) diff --git a/lib/python3.12/site-packages/sympy/core/trace.py b/lib/python3.12/site-packages/sympy/core/trace.py new file mode 100644 index 0000000000000000000000000000000000000000..58326ce1fdd5038f0b5805afe7c453314a22cb6a --- /dev/null +++ b/lib/python3.12/site-packages/sympy/core/trace.py @@ -0,0 +1,12 @@ +from sympy.utilities.exceptions import sympy_deprecation_warning + +sympy_deprecation_warning( + """ + sympy.core.trace is deprecated. Use sympy.physics.quantum.trace + instead. + """, + deprecated_since_version="1.10", + active_deprecations_target="sympy-core-trace-deprecated", +) + +from sympy.physics.quantum.trace import Tr # noqa:F401 diff --git a/lib/python3.12/site-packages/sympy/core/traversal.py b/lib/python3.12/site-packages/sympy/core/traversal.py new file mode 100644 index 0000000000000000000000000000000000000000..6574b12d98a54b8fedb705e3ca674948534f20c2 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/core/traversal.py @@ -0,0 +1,300 @@ +from .basic import Basic +from .sorting import ordered +from .sympify import sympify +from sympy.utilities.iterables import iterable + + + +def iterargs(expr): + """Yield the args of a Basic object in a breadth-first traversal. + Depth-traversal stops if `arg.args` is either empty or is not + an iterable. + + Examples + ======== + + >>> from sympy import Integral, Function + >>> from sympy.abc import x + >>> f = Function('f') + >>> from sympy.core.traversal import iterargs + >>> list(iterargs(Integral(f(x), (f(x), 1)))) + [Integral(f(x), (f(x), 1)), f(x), (f(x), 1), x, f(x), 1, x] + + See Also + ======== + iterfreeargs, preorder_traversal + """ + args = [expr] + for i in args: + yield i + args.extend(i.args) + + +def iterfreeargs(expr, _first=True): + """Yield the args of a Basic object in a breadth-first traversal. + Depth-traversal stops if `arg.args` is either empty or is not + an iterable. The bound objects of an expression will be returned + as canonical variables. + + Examples + ======== + + >>> from sympy import Integral, Function + >>> from sympy.abc import x + >>> f = Function('f') + >>> from sympy.core.traversal import iterfreeargs + >>> list(iterfreeargs(Integral(f(x), (f(x), 1)))) + [Integral(f(x), (f(x), 1)), 1] + + See Also + ======== + iterargs, preorder_traversal + """ + args = [expr] + for i in args: + yield i + if _first and hasattr(i, 'bound_symbols'): + void = i.canonical_variables.values() + for i in iterfreeargs(i.as_dummy(), _first=False): + if not i.has(*void): + yield i + args.extend(i.args) + + +class preorder_traversal: + """ + Do a pre-order traversal of a tree. + + This iterator recursively yields nodes that it has visited in a pre-order + fashion. That is, it yields the current node then descends through the + tree breadth-first to yield all of a node's children's pre-order + traversal. + + + For an expression, the order of the traversal depends on the order of + .args, which in many cases can be arbitrary. + + Parameters + ========== + node : SymPy expression + The expression to traverse. + keys : (default None) sort key(s) + The key(s) used to sort args of Basic objects. When None, args of Basic + objects are processed in arbitrary order. If key is defined, it will + be passed along to ordered() as the only key(s) to use to sort the + arguments; if ``key`` is simply True then the default keys of ordered + will be used. + + Yields + ====== + subtree : SymPy expression + All of the subtrees in the tree. + + Examples + ======== + + >>> from sympy import preorder_traversal, symbols + >>> x, y, z = symbols('x y z') + + The nodes are returned in the order that they are encountered unless key + is given; simply passing key=True will guarantee that the traversal is + unique. + + >>> list(preorder_traversal((x + y)*z, keys=None)) # doctest: +SKIP + [z*(x + y), z, x + y, y, x] + >>> list(preorder_traversal((x + y)*z, keys=True)) + [z*(x + y), z, x + y, x, y] + + """ + def __init__(self, node, keys=None): + self._skip_flag = False + self._pt = self._preorder_traversal(node, keys) + + def _preorder_traversal(self, node, keys): + yield node + if self._skip_flag: + self._skip_flag = False + return + if isinstance(node, Basic): + if not keys and hasattr(node, '_argset'): + # LatticeOp keeps args as a set. We should use this if we + # don't care about the order, to prevent unnecessary sorting. + args = node._argset + else: + args = node.args + if keys: + if keys != True: + args = ordered(args, keys, default=False) + else: + args = ordered(args) + for arg in args: + yield from self._preorder_traversal(arg, keys) + elif iterable(node): + for item in node: + yield from self._preorder_traversal(item, keys) + + def skip(self): + """ + Skip yielding current node's (last yielded node's) subtrees. + + Examples + ======== + + >>> from sympy import preorder_traversal, symbols + >>> x, y, z = symbols('x y z') + >>> pt = preorder_traversal((x + y*z)*z) + >>> for i in pt: + ... print(i) + ... if i == x + y*z: + ... pt.skip() + z*(x + y*z) + z + x + y*z + """ + self._skip_flag = True + + def __next__(self): + return next(self._pt) + + def __iter__(self): + return self + + +def use(expr, func, level=0, args=(), kwargs={}): + """ + Use ``func`` to transform ``expr`` at the given level. + + Examples + ======== + + >>> from sympy import use, expand + >>> from sympy.abc import x, y + + >>> f = (x + y)**2*x + 1 + + >>> use(f, expand, level=2) + x*(x**2 + 2*x*y + y**2) + 1 + >>> expand(f) + x**3 + 2*x**2*y + x*y**2 + 1 + + """ + def _use(expr, level): + if not level: + return func(expr, *args, **kwargs) + else: + if expr.is_Atom: + return expr + else: + level -= 1 + _args = [_use(arg, level) for arg in expr.args] + return expr.__class__(*_args) + + return _use(sympify(expr), level) + + +def walk(e, *target): + """Iterate through the args that are the given types (target) and + return a list of the args that were traversed; arguments + that are not of the specified types are not traversed. + + Examples + ======== + + >>> from sympy.core.traversal import walk + >>> from sympy import Min, Max + >>> from sympy.abc import x, y, z + >>> list(walk(Min(x, Max(y, Min(1, z))), Min)) + [Min(x, Max(y, Min(1, z)))] + >>> list(walk(Min(x, Max(y, Min(1, z))), Min, Max)) + [Min(x, Max(y, Min(1, z))), Max(y, Min(1, z)), Min(1, z)] + + See Also + ======== + + bottom_up + """ + if isinstance(e, target): + yield e + for i in e.args: + yield from walk(i, *target) + + +def bottom_up(rv, F, atoms=False, nonbasic=False): + """Apply ``F`` to all expressions in an expression tree from the + bottom up. If ``atoms`` is True, apply ``F`` even if there are no args; + if ``nonbasic`` is True, try to apply ``F`` to non-Basic objects. + """ + args = getattr(rv, 'args', None) + if args is not None: + if args: + args = tuple([bottom_up(a, F, atoms, nonbasic) for a in args]) + if args != rv.args: + rv = rv.func(*args) + rv = F(rv) + elif atoms: + rv = F(rv) + else: + if nonbasic: + try: + rv = F(rv) + except TypeError: + pass + + return rv + + +def postorder_traversal(node, keys=None): + """ + Do a postorder traversal of a tree. + + This generator recursively yields nodes that it has visited in a postorder + fashion. That is, it descends through the tree depth-first to yield all of + a node's children's postorder traversal before yielding the node itself. + + Parameters + ========== + + node : SymPy expression + The expression to traverse. + keys : (default None) sort key(s) + The key(s) used to sort args of Basic objects. When None, args of Basic + objects are processed in arbitrary order. If key is defined, it will + be passed along to ordered() as the only key(s) to use to sort the + arguments; if ``key`` is simply True then the default keys of + ``ordered`` will be used (node count and default_sort_key). + + Yields + ====== + subtree : SymPy expression + All of the subtrees in the tree. + + Examples + ======== + + >>> from sympy import postorder_traversal + >>> from sympy.abc import w, x, y, z + + The nodes are returned in the order that they are encountered unless key + is given; simply passing key=True will guarantee that the traversal is + unique. + + >>> list(postorder_traversal(w + (x + y)*z)) # doctest: +SKIP + [z, y, x, x + y, z*(x + y), w, w + z*(x + y)] + >>> list(postorder_traversal(w + (x + y)*z, keys=True)) + [w, z, x, y, x + y, z*(x + y), w + z*(x + y)] + + + """ + if isinstance(node, Basic): + args = node.args + if keys: + if keys != True: + args = ordered(args, keys, default=False) + else: + args = ordered(args) + for arg in args: + yield from postorder_traversal(arg, keys) + elif iterable(node): + for item in node: + yield from postorder_traversal(item, keys) + yield node diff --git a/lib/python3.12/site-packages/sympy/solvers/__init__.py b/lib/python3.12/site-packages/sympy/solvers/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..b255f85ee2da3ea2f23aa37caa76f400c6e27193 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/solvers/__init__.py @@ -0,0 +1,75 @@ +"""A module for solving all kinds of equations. + + Examples + ======== + + >>> from sympy.solvers import solve + >>> from sympy.abc import x + >>> solve(((x + 1)**5).expand(), x) + [-1] +""" +from sympy.core.assumptions import check_assumptions, failing_assumptions + +from .solvers import solve, solve_linear_system, solve_linear_system_LU, \ + solve_undetermined_coeffs, nsolve, solve_linear, checksol, \ + det_quick, inv_quick + +from .diophantine import diophantine + +from .recurr import rsolve, rsolve_poly, rsolve_ratio, rsolve_hyper + +from .ode import checkodesol, classify_ode, dsolve, \ + homogeneous_order + +from .polysys import solve_poly_system, solve_triangulated + +from .pde import pde_separate, pde_separate_add, pde_separate_mul, \ + pdsolve, classify_pde, checkpdesol + +from .deutils import ode_order + +from .inequalities import reduce_inequalities, reduce_abs_inequality, \ + reduce_abs_inequalities, solve_poly_inequality, solve_rational_inequalities, solve_univariate_inequality + +from .decompogen import decompogen + +from .solveset import solveset, linsolve, linear_eq_to_matrix, nonlinsolve, substitution + +from .simplex import lpmin, lpmax, linprog + +# This is here instead of sympy/sets/__init__.py to avoid circular import issues +from ..core.singleton import S +Complexes = S.Complexes + +__all__ = [ + 'solve', 'solve_linear_system', 'solve_linear_system_LU', + 'solve_undetermined_coeffs', 'nsolve', 'solve_linear', 'checksol', + 'det_quick', 'inv_quick', 'check_assumptions', 'failing_assumptions', + + 'diophantine', + + 'rsolve', 'rsolve_poly', 'rsolve_ratio', 'rsolve_hyper', + + 'checkodesol', 'classify_ode', 'dsolve', 'homogeneous_order', + + 'solve_poly_system', 'solve_triangulated', + + 'pde_separate', 'pde_separate_add', 'pde_separate_mul', 'pdsolve', + 'classify_pde', 'checkpdesol', + + 'ode_order', + + 'reduce_inequalities', 'reduce_abs_inequality', 'reduce_abs_inequalities', + 'solve_poly_inequality', 'solve_rational_inequalities', + 'solve_univariate_inequality', + + 'decompogen', + + 'solveset', 'linsolve', 'linear_eq_to_matrix', 'nonlinsolve', + 'substitution', + + # This is here instead of sympy/sets/__init__.py to avoid circular import issues + 'Complexes', + + 'lpmin', 'lpmax', 'linprog' +] diff --git a/lib/python3.12/site-packages/sympy/solvers/bivariate.py b/lib/python3.12/site-packages/sympy/solvers/bivariate.py new file mode 100644 index 0000000000000000000000000000000000000000..eec3df246e62b7f44de90296fb4987ada3887aae --- /dev/null +++ b/lib/python3.12/site-packages/sympy/solvers/bivariate.py @@ -0,0 +1,509 @@ +from sympy.core.add import Add +from sympy.core.exprtools import factor_terms +from sympy.core.function import expand_log, _mexpand +from sympy.core.power import Pow +from sympy.core.singleton import S +from sympy.core.sorting import ordered +from sympy.core.symbol import Dummy +from sympy.functions.elementary.exponential import (LambertW, exp, log) +from sympy.functions.elementary.miscellaneous import root +from sympy.polys.polyroots import roots +from sympy.polys.polytools import Poly, factor +from sympy.simplify.simplify import separatevars +from sympy.simplify.radsimp import collect +from sympy.simplify.simplify import powsimp +from sympy.solvers.solvers import solve, _invert +from sympy.utilities.iterables import uniq + + +def _filtered_gens(poly, symbol): + """process the generators of ``poly``, returning the set of generators that + have ``symbol``. If there are two generators that are inverses of each other, + prefer the one that has no denominator. + + Examples + ======== + + >>> from sympy.solvers.bivariate import _filtered_gens + >>> from sympy import Poly, exp + >>> from sympy.abc import x + >>> _filtered_gens(Poly(x + 1/x + exp(x)), x) + {x, exp(x)} + + """ + # TODO it would be good to pick the smallest divisible power + # instead of the base for something like x**4 + x**2 --> + # return x**2 not x + gens = {g for g in poly.gens if symbol in g.free_symbols} + for g in list(gens): + ag = 1/g + if g in gens and ag in gens: + if ag.as_numer_denom()[1] is not S.One: + g = ag + gens.remove(g) + return gens + + +def _mostfunc(lhs, func, X=None): + """Returns the term in lhs which contains the most of the + func-type things e.g. log(log(x)) wins over log(x) if both terms appear. + + ``func`` can be a function (exp, log, etc...) or any other SymPy object, + like Pow. + + If ``X`` is not ``None``, then the function returns the term composed with the + most ``func`` having the specified variable. + + Examples + ======== + + >>> from sympy.solvers.bivariate import _mostfunc + >>> from sympy import exp + >>> from sympy.abc import x, y + >>> _mostfunc(exp(x) + exp(exp(x) + 2), exp) + exp(exp(x) + 2) + >>> _mostfunc(exp(x) + exp(exp(y) + 2), exp) + exp(exp(y) + 2) + >>> _mostfunc(exp(x) + exp(exp(y) + 2), exp, x) + exp(x) + >>> _mostfunc(x, exp, x) is None + True + >>> _mostfunc(exp(x) + exp(x*y), exp, x) + exp(x) + """ + fterms = [tmp for tmp in lhs.atoms(func) if (not X or + X.is_Symbol and X in tmp.free_symbols or + not X.is_Symbol and tmp.has(X))] + if len(fterms) == 1: + return fterms[0] + elif fterms: + return max(list(ordered(fterms)), key=lambda x: x.count(func)) + return None + + +def _linab(arg, symbol): + """Return ``a, b, X`` assuming ``arg`` can be written as ``a*X + b`` + where ``X`` is a symbol-dependent factor and ``a`` and ``b`` are + independent of ``symbol``. + + Examples + ======== + + >>> from sympy.solvers.bivariate import _linab + >>> from sympy.abc import x, y + >>> from sympy import exp, S + >>> _linab(S(2), x) + (2, 0, 1) + >>> _linab(2*x, x) + (2, 0, x) + >>> _linab(y + y*x + 2*x, x) + (y + 2, y, x) + >>> _linab(3 + 2*exp(x), x) + (2, 3, exp(x)) + """ + arg = factor_terms(arg.expand()) + ind, dep = arg.as_independent(symbol) + if arg.is_Mul and dep.is_Add: + a, b, x = _linab(dep, symbol) + return ind*a, ind*b, x + if not arg.is_Add: + b = 0 + a, x = ind, dep + else: + b = ind + a, x = separatevars(dep).as_independent(symbol, as_Add=False) + if x.could_extract_minus_sign(): + a = -a + x = -x + return a, b, x + + +def _lambert(eq, x): + """ + Given an expression assumed to be in the form + ``F(X, a..f) = a*log(b*X + c) + d*X + f = 0`` + where X = g(x) and x = g^-1(X), return the Lambert solution, + ``x = g^-1(-c/b + (a/d)*W(d/(a*b)*exp(c*d/a/b)*exp(-f/a)))``. + """ + eq = _mexpand(expand_log(eq)) + mainlog = _mostfunc(eq, log, x) + if not mainlog: + return [] # violated assumptions + other = eq.subs(mainlog, 0) + if isinstance(-other, log): + eq = (eq - other).subs(mainlog, mainlog.args[0]) + mainlog = mainlog.args[0] + if not isinstance(mainlog, log): + return [] # violated assumptions + other = -(-other).args[0] + eq += other + if x not in other.free_symbols: + return [] # violated assumptions + d, f, X2 = _linab(other, x) + logterm = collect(eq - other, mainlog) + a = logterm.as_coefficient(mainlog) + if a is None or x in a.free_symbols: + return [] # violated assumptions + logarg = mainlog.args[0] + b, c, X1 = _linab(logarg, x) + if X1 != X2: + return [] # violated assumptions + + # invert the generator X1 so we have x(u) + u = Dummy('rhs') + xusolns = solve(X1 - u, x) + + # There are infinitely many branches for LambertW + # but only branches for k = -1 and 0 might be real. The k = 0 + # branch is real and the k = -1 branch is real if the LambertW argumen + # in in range [-1/e, 0]. Since `solve` does not return infinite + # solutions we will only include the -1 branch if it tests as real. + # Otherwise, inclusion of any LambertW in the solution indicates to + # the user that there are imaginary solutions corresponding to + # different k values. + lambert_real_branches = [-1, 0] + sol = [] + + # solution of the given Lambert equation is like + # sol = -c/b + (a/d)*LambertW(arg, k), + # where arg = d/(a*b)*exp((c*d-b*f)/a/b) and k in lambert_real_branches. + # Instead of considering the single arg, `d/(a*b)*exp((c*d-b*f)/a/b)`, + # the individual `p` roots obtained when writing `exp((c*d-b*f)/a/b)` + # as `exp(A/p) = exp(A)**(1/p)`, where `p` is an Integer, are used. + + # calculating args for LambertW + num, den = ((c*d-b*f)/a/b).as_numer_denom() + p, den = den.as_coeff_Mul() + e = exp(num/den) + t = Dummy('t') + args = [d/(a*b)*t for t in roots(t**p - e, t).keys()] + + # calculating solutions from args + for arg in args: + for k in lambert_real_branches: + w = LambertW(arg, k) + if k and not w.is_real: + continue + rhs = -c/b + (a/d)*w + + sol.extend(xu.subs(u, rhs) for xu in xusolns) + return sol + + +def _solve_lambert(f, symbol, gens): + """Return solution to ``f`` if it is a Lambert-type expression + else raise NotImplementedError. + + For ``f(X, a..f) = a*log(b*X + c) + d*X - f = 0`` the solution + for ``X`` is ``X = -c/b + (a/d)*W(d/(a*b)*exp(c*d/a/b)*exp(f/a))``. + There are a variety of forms for `f(X, a..f)` as enumerated below: + + 1a1) + if B**B = R for R not in [0, 1] (since those cases would already + be solved before getting here) then log of both sides gives + log(B) + log(log(B)) = log(log(R)) and + X = log(B), a = 1, b = 1, c = 0, d = 1, f = log(log(R)) + 1a2) + if B*(b*log(B) + c)**a = R then log of both sides gives + log(B) + a*log(b*log(B) + c) = log(R) and + X = log(B), d=1, f=log(R) + 1b) + if a*log(b*B + c) + d*B = R and + X = B, f = R + 2a) + if (b*B + c)*exp(d*B + g) = R then log of both sides gives + log(b*B + c) + d*B + g = log(R) and + X = B, a = 1, f = log(R) - g + 2b) + if g*exp(d*B + h) - b*B = c then the log form is + log(g) + d*B + h - log(b*B + c) = 0 and + X = B, a = -1, f = -h - log(g) + 3) + if d*p**(a*B + g) - b*B = c then the log form is + log(d) + (a*B + g)*log(p) - log(b*B + c) = 0 and + X = B, a = -1, d = a*log(p), f = -log(d) - g*log(p) + """ + + def _solve_even_degree_expr(expr, t, symbol): + """Return the unique solutions of equations derived from + ``expr`` by replacing ``t`` with ``+/- symbol``. + + Parameters + ========== + + expr : Expr + The expression which includes a dummy variable t to be + replaced with +symbol and -symbol. + + symbol : Symbol + The symbol for which a solution is being sought. + + Returns + ======= + + List of unique solution of the two equations generated by + replacing ``t`` with positive and negative ``symbol``. + + Notes + ===== + + If ``expr = 2*log(t) + x/2` then solutions for + ``2*log(x) + x/2 = 0`` and ``2*log(-x) + x/2 = 0`` are + returned by this function. Though this may seem + counter-intuitive, one must note that the ``expr`` being + solved here has been derived from a different expression. For + an expression like ``eq = x**2*g(x) = 1``, if we take the + log of both sides we obtain ``log(x**2) + log(g(x)) = 0``. If + x is positive then this simplifies to + ``2*log(x) + log(g(x)) = 0``; the Lambert-solving routines will + return solutions for this, but we must also consider the + solutions for ``2*log(-x) + log(g(x))`` since those must also + be a solution of ``eq`` which has the same value when the ``x`` + in ``x**2`` is negated. If `g(x)` does not have even powers of + symbol then we do not want to replace the ``x`` there with + ``-x``. So the role of the ``t`` in the expression received by + this function is to mark where ``+/-x`` should be inserted + before obtaining the Lambert solutions. + + """ + nlhs, plhs = [ + expr.xreplace({t: sgn*symbol}) for sgn in (-1, 1)] + sols = _solve_lambert(nlhs, symbol, gens) + if plhs != nlhs: + sols.extend(_solve_lambert(plhs, symbol, gens)) + # uniq is needed for a case like + # 2*log(t) - log(-z**2) + log(z + log(x) + log(z)) + # where substituting t with +/-x gives all the same solution; + # uniq, rather than list(set()), is used to maintain canonical + # order + return list(uniq(sols)) + + nrhs, lhs = f.as_independent(symbol, as_Add=True) + rhs = -nrhs + + lamcheck = [tmp for tmp in gens + if (tmp.func in [exp, log] or + (tmp.is_Pow and symbol in tmp.exp.free_symbols))] + if not lamcheck: + raise NotImplementedError() + + if lhs.is_Add or lhs.is_Mul: + # replacing all even_degrees of symbol with dummy variable t + # since these will need special handling; non-Add/Mul do not + # need this handling + t = Dummy('t', **symbol.assumptions0) + lhs = lhs.replace( + lambda i: # find symbol**even + i.is_Pow and i.base == symbol and i.exp.is_even, + lambda i: # replace t**even + t**i.exp) + + if lhs.is_Add and lhs.has(t): + t_indep = lhs.subs(t, 0) + t_term = lhs - t_indep + _rhs = rhs - t_indep + if not t_term.is_Add and _rhs and not ( + t_term.has(S.ComplexInfinity, S.NaN)): + eq = expand_log(log(t_term) - log(_rhs)) + return _solve_even_degree_expr(eq, t, symbol) + elif lhs.is_Mul and rhs: + # this needs to happen whether t is present or not + lhs = expand_log(log(lhs), force=True) + rhs = log(rhs) + if lhs.has(t) and lhs.is_Add: + # it expanded from Mul to Add + eq = lhs - rhs + return _solve_even_degree_expr(eq, t, symbol) + + # restore symbol in lhs + lhs = lhs.xreplace({t: symbol}) + + lhs = powsimp(factor(lhs, deep=True)) + + # make sure we have inverted as completely as possible + r = Dummy() + i, lhs = _invert(lhs - r, symbol) + rhs = i.xreplace({r: rhs}) + + # For the first forms: + # + # 1a1) B**B = R will arrive here as B*log(B) = log(R) + # lhs is Mul so take log of both sides: + # log(B) + log(log(B)) = log(log(R)) + # 1a2) B*(b*log(B) + c)**a = R will arrive unchanged so + # lhs is Mul, so take log of both sides: + # log(B) + a*log(b*log(B) + c) = log(R) + # 1b) d*log(a*B + b) + c*B = R will arrive unchanged so + # lhs is Add, so isolate c*B and expand log of both sides: + # log(c) + log(B) = log(R - d*log(a*B + b)) + + soln = [] + if not soln: + mainlog = _mostfunc(lhs, log, symbol) + if mainlog: + if lhs.is_Mul and rhs != 0: + soln = _lambert(log(lhs) - log(rhs), symbol) + elif lhs.is_Add: + other = lhs.subs(mainlog, 0) + if other and not other.is_Add and [ + tmp for tmp in other.atoms(Pow) + if symbol in tmp.free_symbols]: + if not rhs: + diff = log(other) - log(other - lhs) + else: + diff = log(lhs - other) - log(rhs - other) + soln = _lambert(expand_log(diff), symbol) + else: + #it's ready to go + soln = _lambert(lhs - rhs, symbol) + + # For the next forms, + # + # collect on main exp + # 2a) (b*B + c)*exp(d*B + g) = R + # lhs is mul, so take log of both sides: + # log(b*B + c) + d*B = log(R) - g + # 2b) g*exp(d*B + h) - b*B = R + # lhs is add, so add b*B to both sides, + # take the log of both sides and rearrange to give + # log(R + b*B) - d*B = log(g) + h + + if not soln: + mainexp = _mostfunc(lhs, exp, symbol) + if mainexp: + lhs = collect(lhs, mainexp) + if lhs.is_Mul and rhs != 0: + soln = _lambert(expand_log(log(lhs) - log(rhs)), symbol) + elif lhs.is_Add: + # move all but mainexp-containing term to rhs + other = lhs.subs(mainexp, 0) + mainterm = lhs - other + rhs = rhs - other + if (mainterm.could_extract_minus_sign() and + rhs.could_extract_minus_sign()): + mainterm *= -1 + rhs *= -1 + diff = log(mainterm) - log(rhs) + soln = _lambert(expand_log(diff), symbol) + + # For the last form: + # + # 3) d*p**(a*B + g) - b*B = c + # collect on main pow, add b*B to both sides, + # take log of both sides and rearrange to give + # a*B*log(p) - log(b*B + c) = -log(d) - g*log(p) + if not soln: + mainpow = _mostfunc(lhs, Pow, symbol) + if mainpow and symbol in mainpow.exp.free_symbols: + lhs = collect(lhs, mainpow) + if lhs.is_Mul and rhs != 0: + # b*B = 0 + soln = _lambert(expand_log(log(lhs) - log(rhs)), symbol) + elif lhs.is_Add: + # move all but mainpow-containing term to rhs + other = lhs.subs(mainpow, 0) + mainterm = lhs - other + rhs = rhs - other + diff = log(mainterm) - log(rhs) + soln = _lambert(expand_log(diff), symbol) + + if not soln: + raise NotImplementedError('%s does not appear to have a solution in ' + 'terms of LambertW' % f) + + return list(ordered(soln)) + + +def bivariate_type(f, x, y, *, first=True): + """Given an expression, f, 3 tests will be done to see what type + of composite bivariate it might be, options for u(x, y) are:: + + x*y + x+y + x*y+x + x*y+y + + If it matches one of these types, ``u(x, y)``, ``P(u)`` and dummy + variable ``u`` will be returned. Solving ``P(u)`` for ``u`` and + equating the solutions to ``u(x, y)`` and then solving for ``x`` or + ``y`` is equivalent to solving the original expression for ``x`` or + ``y``. If ``x`` and ``y`` represent two functions in the same + variable, e.g. ``x = g(t)`` and ``y = h(t)``, then if ``u(x, y) - p`` + can be solved for ``t`` then these represent the solutions to + ``P(u) = 0`` when ``p`` are the solutions of ``P(u) = 0``. + + Only positive values of ``u`` are considered. + + Examples + ======== + + >>> from sympy import solve + >>> from sympy.solvers.bivariate import bivariate_type + >>> from sympy.abc import x, y + >>> eq = (x**2 - 3).subs(x, x + y) + >>> bivariate_type(eq, x, y) + (x + y, _u**2 - 3, _u) + >>> uxy, pu, u = _ + >>> usol = solve(pu, u); usol + [sqrt(3)] + >>> [solve(uxy - s) for s in solve(pu, u)] + [[{x: -y + sqrt(3)}]] + >>> all(eq.subs(s).equals(0) for sol in _ for s in sol) + True + + """ + + u = Dummy('u', positive=True) + + if first: + p = Poly(f, x, y) + f = p.as_expr() + _x = Dummy() + _y = Dummy() + rv = bivariate_type(Poly(f.subs({x: _x, y: _y}), _x, _y), _x, _y, first=False) + if rv: + reps = {_x: x, _y: y} + return rv[0].xreplace(reps), rv[1].xreplace(reps), rv[2] + return + + p = f + f = p.as_expr() + + # f(x*y) + args = Add.make_args(p.as_expr()) + new = [] + for a in args: + a = _mexpand(a.subs(x, u/y)) + free = a.free_symbols + if x in free or y in free: + break + new.append(a) + else: + return x*y, Add(*new), u + + def ok(f, v, c): + new = _mexpand(f.subs(v, c)) + free = new.free_symbols + return None if (x in free or y in free) else new + + # f(a*x + b*y) + new = [] + d = p.degree(x) + if p.degree(y) == d: + a = root(p.coeff_monomial(x**d), d) + b = root(p.coeff_monomial(y**d), d) + new = ok(f, x, (u - b*y)/a) + if new is not None: + return a*x + b*y, new, u + + # f(a*x*y + b*y) + new = [] + d = p.degree(x) + if p.degree(y) == d: + for itry in range(2): + a = root(p.coeff_monomial(x**d*y**d), d) + b = root(p.coeff_monomial(y**d), d) + new = ok(f, x, (u - b*y)/a/y) + if new is not None: + return a*x*y + b*y, new, u + x, y = y, x diff --git a/lib/python3.12/site-packages/sympy/solvers/decompogen.py b/lib/python3.12/site-packages/sympy/solvers/decompogen.py new file mode 100644 index 0000000000000000000000000000000000000000..ec1b3b683511a34e6f98b9839d112b87517390d8 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/solvers/decompogen.py @@ -0,0 +1,126 @@ +from sympy.core import (Function, Pow, sympify, Expr) +from sympy.core.relational import Relational +from sympy.core.singleton import S +from sympy.polys import Poly, decompose +from sympy.utilities.misc import func_name +from sympy.functions.elementary.miscellaneous import Min, Max + + +def decompogen(f, symbol): + """ + Computes General functional decomposition of ``f``. + Given an expression ``f``, returns a list ``[f_1, f_2, ..., f_n]``, + where:: + f = f_1 o f_2 o ... f_n = f_1(f_2(... f_n)) + + Note: This is a General decomposition function. It also decomposes + Polynomials. For only Polynomial decomposition see ``decompose`` in polys. + + Examples + ======== + + >>> from sympy.abc import x + >>> from sympy import decompogen, sqrt, sin, cos + >>> decompogen(sin(cos(x)), x) + [sin(x), cos(x)] + >>> decompogen(sin(x)**2 + sin(x) + 1, x) + [x**2 + x + 1, sin(x)] + >>> decompogen(sqrt(6*x**2 - 5), x) + [sqrt(x), 6*x**2 - 5] + >>> decompogen(sin(sqrt(cos(x**2 + 1))), x) + [sin(x), sqrt(x), cos(x), x**2 + 1] + >>> decompogen(x**4 + 2*x**3 - x - 1, x) + [x**2 - x - 1, x**2 + x] + + """ + f = sympify(f) + if not isinstance(f, Expr) or isinstance(f, Relational): + raise TypeError('expecting Expr but got: `%s`' % func_name(f)) + if symbol not in f.free_symbols: + return [f] + + + # ===== Simple Functions ===== # + if isinstance(f, (Function, Pow)): + if f.is_Pow and f.base == S.Exp1: + arg = f.exp + else: + arg = f.args[0] + if arg == symbol: + return [f] + return [f.subs(arg, symbol)] + decompogen(arg, symbol) + + # ===== Min/Max Functions ===== # + if isinstance(f, (Min, Max)): + args = list(f.args) + d0 = None + for i, a in enumerate(args): + if not a.has_free(symbol): + continue + d = decompogen(a, symbol) + if len(d) == 1: + d = [symbol] + d + if d0 is None: + d0 = d[1:] + elif d[1:] != d0: + # decomposition is not the same for each arg: + # mark as having no decomposition + d = [symbol] + break + args[i] = d[0] + if d[0] == symbol: + return [f] + return [f.func(*args)] + d0 + + # ===== Convert to Polynomial ===== # + fp = Poly(f) + gens = list(filter(lambda x: symbol in x.free_symbols, fp.gens)) + + if len(gens) == 1 and gens[0] != symbol: + f1 = f.subs(gens[0], symbol) + f2 = gens[0] + return [f1] + decompogen(f2, symbol) + + # ===== Polynomial decompose() ====== # + try: + return decompose(f) + except ValueError: + return [f] + + +def compogen(g_s, symbol): + """ + Returns the composition of functions. + Given a list of functions ``g_s``, returns their composition ``f``, + where: + f = g_1 o g_2 o .. o g_n + + Note: This is a General composition function. It also composes Polynomials. + For only Polynomial composition see ``compose`` in polys. + + Examples + ======== + + >>> from sympy.solvers.decompogen import compogen + >>> from sympy.abc import x + >>> from sympy import sqrt, sin, cos + >>> compogen([sin(x), cos(x)], x) + sin(cos(x)) + >>> compogen([x**2 + x + 1, sin(x)], x) + sin(x)**2 + sin(x) + 1 + >>> compogen([sqrt(x), 6*x**2 - 5], x) + sqrt(6*x**2 - 5) + >>> compogen([sin(x), sqrt(x), cos(x), x**2 + 1], x) + sin(sqrt(cos(x**2 + 1))) + >>> compogen([x**2 - x - 1, x**2 + x], x) + -x**2 - x + (x**2 + x)**2 - 1 + """ + if len(g_s) == 1: + return g_s[0] + + foo = g_s[0].subs(symbol, g_s[1]) + + if len(g_s) == 2: + return foo + + return compogen([foo] + g_s[2:], symbol) diff --git a/lib/python3.12/site-packages/sympy/solvers/deutils.py b/lib/python3.12/site-packages/sympy/solvers/deutils.py new file mode 100644 index 0000000000000000000000000000000000000000..c968b65c8d518b82ed308ba932ea9297a3fe3808 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/solvers/deutils.py @@ -0,0 +1,273 @@ +"""Utility functions for classifying and solving +ordinary and partial differential equations. + +Contains +======== +_preprocess +ode_order +_desolve + +""" +from sympy.core import Pow +from sympy.core.function import Derivative, AppliedUndef +from sympy.core.relational import Equality +from sympy.core.symbol import Wild + +def _preprocess(expr, func=None, hint='_Integral'): + """Prepare expr for solving by making sure that differentiation + is done so that only func remains in unevaluated derivatives and + (if hint does not end with _Integral) that doit is applied to all + other derivatives. If hint is None, do not do any differentiation. + (Currently this may cause some simple differential equations to + fail.) + + In case func is None, an attempt will be made to autodetect the + function to be solved for. + + >>> from sympy.solvers.deutils import _preprocess + >>> from sympy import Derivative, Function + >>> from sympy.abc import x, y, z + >>> f, g = map(Function, 'fg') + + If f(x)**p == 0 and p>0 then we can solve for f(x)=0 + >>> _preprocess((f(x).diff(x)-4)**5, f(x)) + (Derivative(f(x), x) - 4, f(x)) + + Apply doit to derivatives that contain more than the function + of interest: + + >>> _preprocess(Derivative(f(x) + x, x)) + (Derivative(f(x), x) + 1, f(x)) + + Do others if the differentiation variable(s) intersect with those + of the function of interest or contain the function of interest: + + >>> _preprocess(Derivative(g(x), y, z), f(y)) + (0, f(y)) + >>> _preprocess(Derivative(f(y), z), f(y)) + (0, f(y)) + + Do others if the hint does not end in '_Integral' (the default + assumes that it does): + + >>> _preprocess(Derivative(g(x), y), f(x)) + (Derivative(g(x), y), f(x)) + >>> _preprocess(Derivative(f(x), y), f(x), hint='') + (0, f(x)) + + Do not do any derivatives if hint is None: + + >>> eq = Derivative(f(x) + 1, x) + Derivative(f(x), y) + >>> _preprocess(eq, f(x), hint=None) + (Derivative(f(x) + 1, x) + Derivative(f(x), y), f(x)) + + If it's not clear what the function of interest is, it must be given: + + >>> eq = Derivative(f(x) + g(x), x) + >>> _preprocess(eq, g(x)) + (Derivative(f(x), x) + Derivative(g(x), x), g(x)) + >>> try: _preprocess(eq) + ... except ValueError: print("A ValueError was raised.") + A ValueError was raised. + + """ + if isinstance(expr, Pow): + # if f(x)**p=0 then f(x)=0 (p>0) + if (expr.exp).is_positive: + expr = expr.base + derivs = expr.atoms(Derivative) + if not func: + funcs = set().union(*[d.atoms(AppliedUndef) for d in derivs]) + if len(funcs) != 1: + raise ValueError('The function cannot be ' + 'automatically detected for %s.' % expr) + func = funcs.pop() + fvars = set(func.args) + if hint is None: + return expr, func + reps = [(d, d.doit()) for d in derivs if not hint.endswith('_Integral') or + d.has(func) or set(d.variables) & fvars] + eq = expr.subs(reps) + return eq, func + + +def ode_order(expr, func): + """ + Returns the order of a given differential + equation with respect to func. + + This function is implemented recursively. + + Examples + ======== + + >>> from sympy import Function + >>> from sympy.solvers.deutils import ode_order + >>> from sympy.abc import x + >>> f, g = map(Function, ['f', 'g']) + >>> ode_order(f(x).diff(x, 2) + f(x).diff(x)**2 + + ... f(x).diff(x), f(x)) + 2 + >>> ode_order(f(x).diff(x, 2) + g(x).diff(x, 3), f(x)) + 2 + >>> ode_order(f(x).diff(x, 2) + g(x).diff(x, 3), g(x)) + 3 + + """ + a = Wild('a', exclude=[func]) + if expr.match(a): + return 0 + + if isinstance(expr, Derivative): + if expr.args[0] == func: + return len(expr.variables) + else: + args = expr.args[0].args + rv = len(expr.variables) + if args: + rv += max(ode_order(_, func) for _ in args) + return rv + else: + return max(ode_order(_, func) for _ in expr.args) if expr.args else 0 + + +def _desolve(eq, func=None, hint="default", ics=None, simplify=True, *, prep=True, **kwargs): + """This is a helper function to dsolve and pdsolve in the ode + and pde modules. + + If the hint provided to the function is "default", then a dict with + the following keys are returned + + 'func' - It provides the function for which the differential equation + has to be solved. This is useful when the expression has + more than one function in it. + + 'default' - The default key as returned by classifier functions in ode + and pde.py + + 'hint' - The hint given by the user for which the differential equation + is to be solved. If the hint given by the user is 'default', + then the value of 'hint' and 'default' is the same. + + 'order' - The order of the function as returned by ode_order + + 'match' - It returns the match as given by the classifier functions, for + the default hint. + + If the hint provided to the function is not "default" and is not in + ('all', 'all_Integral', 'best'), then a dict with the above mentioned keys + is returned along with the keys which are returned when dict in + classify_ode or classify_pde is set True + + If the hint given is in ('all', 'all_Integral', 'best'), then this function + returns a nested dict, with the keys, being the set of classified hints + returned by classifier functions, and the values being the dict of form + as mentioned above. + + Key 'eq' is a common key to all the above mentioned hints which returns an + expression if eq given by user is an Equality. + + See Also + ======== + classify_ode(ode.py) + classify_pde(pde.py) + """ + if isinstance(eq, Equality): + eq = eq.lhs - eq.rhs + + # preprocess the equation and find func if not given + if prep or func is None: + eq, func = _preprocess(eq, func) + prep = False + + # type is an argument passed by the solve functions in ode and pde.py + # that identifies whether the function caller is an ordinary + # or partial differential equation. Accordingly corresponding + # changes are made in the function. + type = kwargs.get('type', None) + xi = kwargs.get('xi') + eta = kwargs.get('eta') + x0 = kwargs.get('x0', 0) + terms = kwargs.get('n') + + if type == 'ode': + from sympy.solvers.ode import classify_ode, allhints + classifier = classify_ode + string = 'ODE ' + dummy = '' + + elif type == 'pde': + from sympy.solvers.pde import classify_pde, allhints + classifier = classify_pde + string = 'PDE ' + dummy = 'p' + + # Magic that should only be used internally. Prevents classify_ode from + # being called more than it needs to be by passing its results through + # recursive calls. + if kwargs.get('classify', True): + hints = classifier(eq, func, dict=True, ics=ics, xi=xi, eta=eta, + n=terms, x0=x0, hint=hint, prep=prep) + + else: + # Here is what all this means: + # + # hint: The hint method given to _desolve() by the user. + # hints: The dictionary of hints that match the DE, along with other + # information (including the internal pass-through magic). + # default: The default hint to return, the first hint from allhints + # that matches the hint; obtained from classify_ode(). + # match: Dictionary containing the match dictionary for each hint + # (the parts of the DE for solving). When going through the + # hints in "all", this holds the match string for the current + # hint. + # order: The order of the DE, as determined by ode_order(). + hints = kwargs.get('hint', + {'default': hint, + hint: kwargs['match'], + 'order': kwargs['order']}) + if not hints['default']: + # classify_ode will set hints['default'] to None if no hints match. + if hint not in allhints and hint != 'default': + raise ValueError("Hint not recognized: " + hint) + elif hint not in hints['ordered_hints'] and hint != 'default': + raise ValueError(string + str(eq) + " does not match hint " + hint) + # If dsolve can't solve the purely algebraic equation then dsolve will raise + # ValueError + elif hints['order'] == 0: + raise ValueError( + str(eq) + " is not a solvable differential equation in " + str(func)) + else: + raise NotImplementedError(dummy + "solve" + ": Cannot solve " + str(eq)) + if hint == 'default': + return _desolve(eq, func, ics=ics, hint=hints['default'], simplify=simplify, + prep=prep, x0=x0, classify=False, order=hints['order'], + match=hints[hints['default']], xi=xi, eta=eta, n=terms, type=type) + elif hint in ('all', 'all_Integral', 'best'): + retdict = {} + gethints = set(hints) - {'order', 'default', 'ordered_hints'} + if hint == 'all_Integral': + for i in hints: + if i.endswith('_Integral'): + gethints.remove(i[:-len('_Integral')]) + # special cases + for k in ["1st_homogeneous_coeff_best", "1st_power_series", + "lie_group", "2nd_power_series_ordinary", "2nd_power_series_regular"]: + if k in gethints: + gethints.remove(k) + for i in gethints: + sol = _desolve(eq, func, ics=ics, hint=i, x0=x0, simplify=simplify, prep=prep, + classify=False, n=terms, order=hints['order'], match=hints[i], type=type) + retdict[i] = sol + retdict['all'] = True + retdict['eq'] = eq + return retdict + elif hint not in allhints: # and hint not in ('default', 'ordered_hints'): + raise ValueError("Hint not recognized: " + hint) + elif hint not in hints: + raise ValueError(string + str(eq) + " does not match hint " + hint) + else: + # Key added to identify the hint needed to solve the equation + hints['hint'] = hint + hints.update({'func': func, 'eq': eq}) + return hints diff --git a/lib/python3.12/site-packages/sympy/solvers/inequalities.py b/lib/python3.12/site-packages/sympy/solvers/inequalities.py new file mode 100644 index 0000000000000000000000000000000000000000..9a5cf9737f7e66b98949aa93ffd3adb44c144b1a --- /dev/null +++ b/lib/python3.12/site-packages/sympy/solvers/inequalities.py @@ -0,0 +1,986 @@ +"""Tools for solving inequalities and systems of inequalities. """ +import itertools + +from sympy.calculus.util import (continuous_domain, periodicity, + function_range) +from sympy.core import sympify +from sympy.core.exprtools import factor_terms +from sympy.core.relational import Relational, Lt, Ge, Eq +from sympy.core.symbol import Symbol, Dummy +from sympy.sets.sets import Interval, FiniteSet, Union, Intersection +from sympy.core.singleton import S +from sympy.core.function import expand_mul +from sympy.functions.elementary.complexes import Abs +from sympy.logic import And +from sympy.polys import Poly, PolynomialError, parallel_poly_from_expr +from sympy.polys.polyutils import _nsort +from sympy.solvers.solveset import solvify, solveset +from sympy.utilities.iterables import sift, iterable +from sympy.utilities.misc import filldedent + + +def solve_poly_inequality(poly, rel): + """Solve a polynomial inequality with rational coefficients. + + Examples + ======== + + >>> from sympy import solve_poly_inequality, Poly + >>> from sympy.abc import x + + >>> solve_poly_inequality(Poly(x, x, domain='ZZ'), '==') + [{0}] + + >>> solve_poly_inequality(Poly(x**2 - 1, x, domain='ZZ'), '!=') + [Interval.open(-oo, -1), Interval.open(-1, 1), Interval.open(1, oo)] + + >>> solve_poly_inequality(Poly(x**2 - 1, x, domain='ZZ'), '==') + [{-1}, {1}] + + See Also + ======== + solve_poly_inequalities + """ + if not isinstance(poly, Poly): + raise ValueError( + 'For efficiency reasons, `poly` should be a Poly instance') + if poly.as_expr().is_number: + t = Relational(poly.as_expr(), 0, rel) + if t is S.true: + return [S.Reals] + elif t is S.false: + return [S.EmptySet] + else: + raise NotImplementedError( + "could not determine truth value of %s" % t) + + reals, intervals = poly.real_roots(multiple=False), [] + + if rel == '==': + for root, _ in reals: + interval = Interval(root, root) + intervals.append(interval) + elif rel == '!=': + left = S.NegativeInfinity + + for right, _ in reals + [(S.Infinity, 1)]: + interval = Interval(left, right, True, True) + intervals.append(interval) + left = right + else: + if poly.LC() > 0: + sign = +1 + else: + sign = -1 + + eq_sign, equal = None, False + + if rel == '>': + eq_sign = +1 + elif rel == '<': + eq_sign = -1 + elif rel == '>=': + eq_sign, equal = +1, True + elif rel == '<=': + eq_sign, equal = -1, True + else: + raise ValueError("'%s' is not a valid relation" % rel) + + right, right_open = S.Infinity, True + + for left, multiplicity in reversed(reals): + if multiplicity % 2: + if sign == eq_sign: + intervals.insert( + 0, Interval(left, right, not equal, right_open)) + + sign, right, right_open = -sign, left, not equal + else: + if sign == eq_sign and not equal: + intervals.insert( + 0, Interval(left, right, True, right_open)) + right, right_open = left, True + elif sign != eq_sign and equal: + intervals.insert(0, Interval(left, left)) + + if sign == eq_sign: + intervals.insert( + 0, Interval(S.NegativeInfinity, right, True, right_open)) + + return intervals + + +def solve_poly_inequalities(polys): + """Solve polynomial inequalities with rational coefficients. + + Examples + ======== + + >>> from sympy import Poly + >>> from sympy.solvers.inequalities import solve_poly_inequalities + >>> from sympy.abc import x + >>> solve_poly_inequalities((( + ... Poly(x**2 - 3), ">"), ( + ... Poly(-x**2 + 1), ">"))) + Union(Interval.open(-oo, -sqrt(3)), Interval.open(-1, 1), Interval.open(sqrt(3), oo)) + """ + return Union(*[s for p in polys for s in solve_poly_inequality(*p)]) + + +def solve_rational_inequalities(eqs): + """Solve a system of rational inequalities with rational coefficients. + + Examples + ======== + + >>> from sympy.abc import x + >>> from sympy import solve_rational_inequalities, Poly + + >>> solve_rational_inequalities([[ + ... ((Poly(-x + 1), Poly(1, x)), '>='), + ... ((Poly(-x + 1), Poly(1, x)), '<=')]]) + {1} + + >>> solve_rational_inequalities([[ + ... ((Poly(x), Poly(1, x)), '!='), + ... ((Poly(-x + 1), Poly(1, x)), '>=')]]) + Union(Interval.open(-oo, 0), Interval.Lopen(0, 1)) + + See Also + ======== + solve_poly_inequality + """ + result = S.EmptySet + + for _eqs in eqs: + if not _eqs: + continue + + global_intervals = [Interval(S.NegativeInfinity, S.Infinity)] + + for (numer, denom), rel in _eqs: + numer_intervals = solve_poly_inequality(numer*denom, rel) + denom_intervals = solve_poly_inequality(denom, '==') + + intervals = [] + + for numer_interval, global_interval in itertools.product( + numer_intervals, global_intervals): + interval = numer_interval.intersect(global_interval) + + if interval is not S.EmptySet: + intervals.append(interval) + + global_intervals = intervals + + intervals = [] + + for global_interval in global_intervals: + for denom_interval in denom_intervals: + global_interval -= denom_interval + + if global_interval is not S.EmptySet: + intervals.append(global_interval) + + global_intervals = intervals + + if not global_intervals: + break + + for interval in global_intervals: + result = result.union(interval) + + return result + + +def reduce_rational_inequalities(exprs, gen, relational=True): + """Reduce a system of rational inequalities with rational coefficients. + + Examples + ======== + + >>> from sympy import Symbol + >>> from sympy.solvers.inequalities import reduce_rational_inequalities + + >>> x = Symbol('x', real=True) + + >>> reduce_rational_inequalities([[x**2 <= 0]], x) + Eq(x, 0) + + >>> reduce_rational_inequalities([[x + 2 > 0]], x) + -2 < x + >>> reduce_rational_inequalities([[(x + 2, ">")]], x) + -2 < x + >>> reduce_rational_inequalities([[x + 2]], x) + Eq(x, -2) + + This function find the non-infinite solution set so if the unknown symbol + is declared as extended real rather than real then the result may include + finiteness conditions: + + >>> y = Symbol('y', extended_real=True) + >>> reduce_rational_inequalities([[y + 2 > 0]], y) + (-2 < y) & (y < oo) + """ + exact = True + eqs = [] + solution = S.EmptySet # add pieces for each group + for _exprs in exprs: + if not _exprs: + continue + _eqs = [] + _sol = S.Reals + for expr in _exprs: + if isinstance(expr, tuple): + expr, rel = expr + else: + if expr.is_Relational: + expr, rel = expr.lhs - expr.rhs, expr.rel_op + else: + expr, rel = expr, '==' + + if expr is S.true: + numer, denom, rel = S.Zero, S.One, '==' + elif expr is S.false: + numer, denom, rel = S.One, S.One, '==' + else: + numer, denom = expr.together().as_numer_denom() + + try: + (numer, denom), opt = parallel_poly_from_expr( + (numer, denom), gen) + except PolynomialError: + raise PolynomialError(filldedent(''' + only polynomials and rational functions are + supported in this context. + ''')) + + if not opt.domain.is_Exact: + numer, denom, exact = numer.to_exact(), denom.to_exact(), False + + domain = opt.domain.get_exact() + + if not (domain.is_ZZ or domain.is_QQ): + expr = numer/denom + expr = Relational(expr, 0, rel) + _sol &= solve_univariate_inequality(expr, gen, relational=False) + else: + _eqs.append(((numer, denom), rel)) + + if _eqs: + _sol &= solve_rational_inequalities([_eqs]) + exclude = solve_rational_inequalities([[((d, d.one), '==') + for i in eqs for ((n, d), _) in i if d.has(gen)]]) + _sol -= exclude + + solution |= _sol + + if not exact and solution: + solution = solution.evalf() + + if relational: + solution = solution.as_relational(gen) + + return solution + + +def reduce_abs_inequality(expr, rel, gen): + """Reduce an inequality with nested absolute values. + + Examples + ======== + + >>> from sympy import reduce_abs_inequality, Abs, Symbol + >>> x = Symbol('x', real=True) + + >>> reduce_abs_inequality(Abs(x - 5) - 3, '<', x) + (2 < x) & (x < 8) + + >>> reduce_abs_inequality(Abs(x + 2)*3 - 13, '<', x) + (-19/3 < x) & (x < 7/3) + + See Also + ======== + + reduce_abs_inequalities + """ + if gen.is_extended_real is False: + raise TypeError(filldedent(''' + Cannot solve inequalities with absolute values containing + non-real variables. + ''')) + + def _bottom_up_scan(expr): + exprs = [] + + if expr.is_Add or expr.is_Mul: + op = expr.func + + for arg in expr.args: + _exprs = _bottom_up_scan(arg) + + if not exprs: + exprs = _exprs + else: + exprs = [(op(expr, _expr), conds + _conds) for (expr, conds), (_expr, _conds) in + itertools.product(exprs, _exprs)] + elif expr.is_Pow: + n = expr.exp + if not n.is_Integer: + raise ValueError("Only Integer Powers are allowed on Abs.") + + exprs.extend((expr**n, conds) for expr, conds in _bottom_up_scan(expr.base)) + elif isinstance(expr, Abs): + _exprs = _bottom_up_scan(expr.args[0]) + + for expr, conds in _exprs: + exprs.append(( expr, conds + [Ge(expr, 0)])) + exprs.append((-expr, conds + [Lt(expr, 0)])) + else: + exprs = [(expr, [])] + + return exprs + + mapping = {'<': '>', '<=': '>='} + inequalities = [] + + for expr, conds in _bottom_up_scan(expr): + if rel not in mapping.keys(): + expr = Relational( expr, 0, rel) + else: + expr = Relational(-expr, 0, mapping[rel]) + + inequalities.append([expr] + conds) + + return reduce_rational_inequalities(inequalities, gen) + + +def reduce_abs_inequalities(exprs, gen): + """Reduce a system of inequalities with nested absolute values. + + Examples + ======== + + >>> from sympy import reduce_abs_inequalities, Abs, Symbol + >>> x = Symbol('x', extended_real=True) + + >>> reduce_abs_inequalities([(Abs(3*x - 5) - 7, '<'), + ... (Abs(x + 25) - 13, '>')], x) + (-2/3 < x) & (x < 4) & (((-oo < x) & (x < -38)) | ((-12 < x) & (x < oo))) + + >>> reduce_abs_inequalities([(Abs(x - 4) + Abs(3*x - 5) - 7, '<')], x) + (1/2 < x) & (x < 4) + + See Also + ======== + + reduce_abs_inequality + """ + return And(*[ reduce_abs_inequality(expr, rel, gen) + for expr, rel in exprs ]) + + +def solve_univariate_inequality(expr, gen, relational=True, domain=S.Reals, continuous=False): + """Solves a real univariate inequality. + + Parameters + ========== + + expr : Relational + The target inequality + gen : Symbol + The variable for which the inequality is solved + relational : bool + A Relational type output is expected or not + domain : Set + The domain over which the equation is solved + continuous: bool + True if expr is known to be continuous over the given domain + (and so continuous_domain() does not need to be called on it) + + Raises + ====== + + NotImplementedError + The solution of the inequality cannot be determined due to limitation + in :func:`sympy.solvers.solveset.solvify`. + + Notes + ===== + + Currently, we cannot solve all the inequalities due to limitations in + :func:`sympy.solvers.solveset.solvify`. Also, the solution returned for trigonometric inequalities + are restricted in its periodic interval. + + See Also + ======== + + sympy.solvers.solveset.solvify: solver returning solveset solutions with solve's output API + + Examples + ======== + + >>> from sympy import solve_univariate_inequality, Symbol, sin, Interval, S + >>> x = Symbol('x') + + >>> solve_univariate_inequality(x**2 >= 4, x) + ((2 <= x) & (x < oo)) | ((-oo < x) & (x <= -2)) + + >>> solve_univariate_inequality(x**2 >= 4, x, relational=False) + Union(Interval(-oo, -2), Interval(2, oo)) + + >>> domain = Interval(0, S.Infinity) + >>> solve_univariate_inequality(x**2 >= 4, x, False, domain) + Interval(2, oo) + + >>> solve_univariate_inequality(sin(x) > 0, x, relational=False) + Interval.open(0, pi) + + """ + from sympy.solvers.solvers import denoms + + if domain.is_subset(S.Reals) is False: + raise NotImplementedError(filldedent(''' + Inequalities in the complex domain are + not supported. Try the real domain by + setting domain=S.Reals''')) + elif domain is not S.Reals: + rv = solve_univariate_inequality( + expr, gen, relational=False, continuous=continuous).intersection(domain) + if relational: + rv = rv.as_relational(gen) + return rv + else: + pass # continue with attempt to solve in Real domain + + # This keeps the function independent of the assumptions about `gen`. + # `solveset` makes sure this function is called only when the domain is + # real. + _gen = gen + _domain = domain + if gen.is_extended_real is False: + rv = S.EmptySet + return rv if not relational else rv.as_relational(_gen) + elif gen.is_extended_real is None: + gen = Dummy('gen', extended_real=True) + try: + expr = expr.xreplace({_gen: gen}) + except TypeError: + raise TypeError(filldedent(''' + When gen is real, the relational has a complex part + which leads to an invalid comparison like I < 0. + ''')) + + rv = None + + if expr is S.true: + rv = domain + + elif expr is S.false: + rv = S.EmptySet + + else: + e = expr.lhs - expr.rhs + period = periodicity(e, gen) + if period == S.Zero: + e = expand_mul(e) + const = expr.func(e, 0) + if const is S.true: + rv = domain + elif const is S.false: + rv = S.EmptySet + elif period is not None: + frange = function_range(e, gen, domain) + + rel = expr.rel_op + if rel in ('<', '<='): + if expr.func(frange.sup, 0): + rv = domain + elif not expr.func(frange.inf, 0): + rv = S.EmptySet + + elif rel in ('>', '>='): + if expr.func(frange.inf, 0): + rv = domain + elif not expr.func(frange.sup, 0): + rv = S.EmptySet + + inf, sup = domain.inf, domain.sup + if sup - inf is S.Infinity: + domain = Interval(0, period, False, True).intersect(_domain) + _domain = domain + + if rv is None: + n, d = e.as_numer_denom() + try: + if gen not in n.free_symbols and len(e.free_symbols) > 1: + raise ValueError + # this might raise ValueError on its own + # or it might give None... + solns = solvify(e, gen, domain) + if solns is None: + # in which case we raise ValueError + raise ValueError + except (ValueError, NotImplementedError): + # replace gen with generic x since it's + # univariate anyway + raise NotImplementedError(filldedent(''' + The inequality, %s, cannot be solved using + solve_univariate_inequality. + ''' % expr.subs(gen, Symbol('x')))) + + expanded_e = expand_mul(e) + def valid(x): + # this is used to see if gen=x satisfies the + # relational by substituting it into the + # expanded form and testing against 0, e.g. + # if expr = x*(x + 1) < 2 then e = x*(x + 1) - 2 + # and expanded_e = x**2 + x - 2; the test is + # whether a given value of x satisfies + # x**2 + x - 2 < 0 + # + # expanded_e, expr and gen used from enclosing scope + v = expanded_e.subs(gen, expand_mul(x)) + try: + r = expr.func(v, 0) + except TypeError: + r = S.false + if r in (S.true, S.false): + return r + if v.is_extended_real is False: + return S.false + else: + v = v.n(2) + if v.is_comparable: + return expr.func(v, 0) + # not comparable or couldn't be evaluated + raise NotImplementedError( + 'relationship did not evaluate: %s' % r) + + singularities = [] + for d in denoms(expr, gen): + singularities.extend(solvify(d, gen, domain)) + if not continuous: + domain = continuous_domain(expanded_e, gen, domain) + + include_x = '=' in expr.rel_op and expr.rel_op != '!=' + + try: + discontinuities = set(domain.boundary - + FiniteSet(domain.inf, domain.sup)) + # remove points that are not between inf and sup of domain + critical_points = FiniteSet(*(solns + singularities + list( + discontinuities))).intersection( + Interval(domain.inf, domain.sup, + domain.inf not in domain, domain.sup not in domain)) + if all(r.is_number for r in critical_points): + reals = _nsort(critical_points, separated=True)[0] + else: + sifted = sift(critical_points, lambda x: x.is_extended_real) + if sifted[None]: + # there were some roots that weren't known + # to be real + raise NotImplementedError + try: + reals = sifted[True] + if len(reals) > 1: + reals = sorted(reals) + except TypeError: + raise NotImplementedError + except NotImplementedError: + raise NotImplementedError('sorting of these roots is not supported') + + # If expr contains imaginary coefficients, only take real + # values of x for which the imaginary part is 0 + make_real = S.Reals + if (coeffI := expanded_e.coeff(S.ImaginaryUnit)) != S.Zero: + check = True + im_sol = FiniteSet() + try: + a = solveset(coeffI, gen, domain) + if not isinstance(a, Interval): + for z in a: + if z not in singularities and valid(z) and z.is_extended_real: + im_sol += FiniteSet(z) + else: + start, end = a.inf, a.sup + for z in _nsort(critical_points + FiniteSet(end)): + valid_start = valid(start) + if start != end: + valid_z = valid(z) + pt = _pt(start, z) + if pt not in singularities and pt.is_extended_real and valid(pt): + if valid_start and valid_z: + im_sol += Interval(start, z) + elif valid_start: + im_sol += Interval.Ropen(start, z) + elif valid_z: + im_sol += Interval.Lopen(start, z) + else: + im_sol += Interval.open(start, z) + start = z + for s in singularities: + im_sol -= FiniteSet(s) + except (TypeError): + im_sol = S.Reals + check = False + + if im_sol is S.EmptySet: + raise ValueError(filldedent(''' + %s contains imaginary parts which cannot be + made 0 for any value of %s satisfying the + inequality, leading to relations like I < 0. + ''' % (expr.subs(gen, _gen), _gen))) + + make_real = make_real.intersect(im_sol) + + sol_sets = [S.EmptySet] + + start = domain.inf + if start in domain and valid(start) and start.is_finite: + sol_sets.append(FiniteSet(start)) + + for x in reals: + end = x + + if valid(_pt(start, end)): + sol_sets.append(Interval(start, end, True, True)) + + if x in singularities: + singularities.remove(x) + else: + if x in discontinuities: + discontinuities.remove(x) + _valid = valid(x) + else: # it's a solution + _valid = include_x + if _valid: + sol_sets.append(FiniteSet(x)) + + start = end + + end = domain.sup + if end in domain and valid(end) and end.is_finite: + sol_sets.append(FiniteSet(end)) + + if valid(_pt(start, end)): + sol_sets.append(Interval.open(start, end)) + + if coeffI != S.Zero and check: + rv = (make_real).intersect(_domain) + else: + rv = Intersection( + (Union(*sol_sets)), make_real, _domain).subs(gen, _gen) + + return rv if not relational else rv.as_relational(_gen) + + +def _pt(start, end): + """Return a point between start and end""" + if not start.is_infinite and not end.is_infinite: + pt = (start + end)/2 + elif start.is_infinite and end.is_infinite: + pt = S.Zero + else: + if (start.is_infinite and start.is_extended_positive is None or + end.is_infinite and end.is_extended_positive is None): + raise ValueError('cannot proceed with unsigned infinite values') + if (end.is_infinite and end.is_extended_negative or + start.is_infinite and start.is_extended_positive): + start, end = end, start + # if possible, use a multiple of self which has + # better behavior when checking assumptions than + # an expression obtained by adding or subtracting 1 + if end.is_infinite: + if start.is_extended_positive: + pt = start*2 + elif start.is_extended_negative: + pt = start*S.Half + else: + pt = start + 1 + elif start.is_infinite: + if end.is_extended_positive: + pt = end*S.Half + elif end.is_extended_negative: + pt = end*2 + else: + pt = end - 1 + return pt + + +def _solve_inequality(ie, s, linear=False): + """Return the inequality with s isolated on the left, if possible. + If the relationship is non-linear, a solution involving And or Or + may be returned. False or True are returned if the relationship + is never True or always True, respectively. + + If `linear` is True (default is False) an `s`-dependent expression + will be isolated on the left, if possible + but it will not be solved for `s` unless the expression is linear + in `s`. Furthermore, only "safe" operations which do not change the + sense of the relationship are applied: no division by an unsigned + value is attempted unless the relationship involves Eq or Ne and + no division by a value not known to be nonzero is ever attempted. + + Examples + ======== + + >>> from sympy import Eq, Symbol + >>> from sympy.solvers.inequalities import _solve_inequality as f + >>> from sympy.abc import x, y + + For linear expressions, the symbol can be isolated: + + >>> f(x - 2 < 0, x) + x < 2 + >>> f(-x - 6 < x, x) + x > -3 + + Sometimes nonlinear relationships will be False + + >>> f(x**2 + 4 < 0, x) + False + + Or they may involve more than one region of values: + + >>> f(x**2 - 4 < 0, x) + (-2 < x) & (x < 2) + + To restrict the solution to a relational, set linear=True + and only the x-dependent portion will be isolated on the left: + + >>> f(x**2 - 4 < 0, x, linear=True) + x**2 < 4 + + Division of only nonzero quantities is allowed, so x cannot + be isolated by dividing by y: + + >>> y.is_nonzero is None # it is unknown whether it is 0 or not + True + >>> f(x*y < 1, x) + x*y < 1 + + And while an equality (or inequality) still holds after dividing by a + non-zero quantity + + >>> nz = Symbol('nz', nonzero=True) + >>> f(Eq(x*nz, 1), x) + Eq(x, 1/nz) + + the sign must be known for other inequalities involving > or <: + + >>> f(x*nz <= 1, x) + nz*x <= 1 + >>> p = Symbol('p', positive=True) + >>> f(x*p <= 1, x) + x <= 1/p + + When there are denominators in the original expression that + are removed by expansion, conditions for them will be returned + as part of the result: + + >>> f(x < x*(2/x - 1), x) + (x < 1) & Ne(x, 0) + """ + from sympy.solvers.solvers import denoms + if s not in ie.free_symbols: + return ie + if ie.rhs == s: + ie = ie.reversed + if ie.lhs == s and s not in ie.rhs.free_symbols: + return ie + + def classify(ie, s, i): + # return True or False if ie evaluates when substituting s with + # i else None (if unevaluated) or NaN (when there is an error + # in evaluating) + try: + v = ie.subs(s, i) + if v is S.NaN: + return v + elif v not in (True, False): + return + return v + except TypeError: + return S.NaN + + rv = None + oo = S.Infinity + expr = ie.lhs - ie.rhs + try: + p = Poly(expr, s) + if p.degree() == 0: + rv = ie.func(p.as_expr(), 0) + elif not linear and p.degree() > 1: + # handle in except clause + raise NotImplementedError + except (PolynomialError, NotImplementedError): + if not linear: + try: + rv = reduce_rational_inequalities([[ie]], s) + except PolynomialError: + rv = solve_univariate_inequality(ie, s) + # remove restrictions wrt +/-oo that may have been + # applied when using sets to simplify the relationship + okoo = classify(ie, s, oo) + if okoo is S.true and classify(rv, s, oo) is S.false: + rv = rv.subs(s < oo, True) + oknoo = classify(ie, s, -oo) + if (oknoo is S.true and + classify(rv, s, -oo) is S.false): + rv = rv.subs(-oo < s, True) + rv = rv.subs(s > -oo, True) + if rv is S.true: + rv = (s <= oo) if okoo is S.true else (s < oo) + if oknoo is not S.true: + rv = And(-oo < s, rv) + else: + p = Poly(expr) + + conds = [] + if rv is None: + e = p.as_expr() # this is in expanded form + # Do a safe inversion of e, moving non-s terms + # to the rhs and dividing by a nonzero factor if + # the relational is Eq/Ne; for other relationals + # the sign must also be positive or negative + rhs = 0 + b, ax = e.as_independent(s, as_Add=True) + e -= b + rhs -= b + ef = factor_terms(e) + a, e = ef.as_independent(s, as_Add=False) + if (a.is_zero != False or # don't divide by potential 0 + a.is_negative == + a.is_positive is None and # if sign is not known then + ie.rel_op not in ('!=', '==')): # reject if not Eq/Ne + e = ef + a = S.One + rhs /= a + if a.is_positive: + rv = ie.func(e, rhs) + else: + rv = ie.reversed.func(e, rhs) + + # return conditions under which the value is + # valid, too. + beginning_denoms = denoms(ie.lhs) | denoms(ie.rhs) + current_denoms = denoms(rv) + for d in beginning_denoms - current_denoms: + c = _solve_inequality(Eq(d, 0), s, linear=linear) + if isinstance(c, Eq) and c.lhs == s: + if classify(rv, s, c.rhs) is S.true: + # rv is permitting this value but it shouldn't + conds.append(~c) + for i in (-oo, oo): + if (classify(rv, s, i) is S.true and + classify(ie, s, i) is not S.true): + conds.append(s < i if i is oo else i < s) + + conds.append(rv) + return And(*conds) + + +def _reduce_inequalities(inequalities, symbols): + # helper for reduce_inequalities + + poly_part, abs_part = {}, {} + other = [] + + for inequality in inequalities: + + expr, rel = inequality.lhs, inequality.rel_op # rhs is 0 + + # check for gens using atoms which is more strict than free_symbols to + # guard against EX domain which won't be handled by + # reduce_rational_inequalities + gens = expr.atoms(Symbol) + + if len(gens) == 1: + gen = gens.pop() + else: + common = expr.free_symbols & symbols + if len(common) == 1: + gen = common.pop() + other.append(_solve_inequality(Relational(expr, 0, rel), gen)) + continue + else: + raise NotImplementedError(filldedent(''' + inequality has more than one symbol of interest. + ''')) + + if expr.is_polynomial(gen): + poly_part.setdefault(gen, []).append((expr, rel)) + else: + components = expr.find(lambda u: + u.has(gen) and ( + u.is_Function or u.is_Pow and not u.exp.is_Integer)) + if components and all(isinstance(i, Abs) for i in components): + abs_part.setdefault(gen, []).append((expr, rel)) + else: + other.append(_solve_inequality(Relational(expr, 0, rel), gen)) + + poly_reduced = [reduce_rational_inequalities([exprs], gen) for gen, exprs in poly_part.items()] + abs_reduced = [reduce_abs_inequalities(exprs, gen) for gen, exprs in abs_part.items()] + + return And(*(poly_reduced + abs_reduced + other)) + + +def reduce_inequalities(inequalities, symbols=[]): + """Reduce a system of inequalities with rational coefficients. + + Examples + ======== + + >>> from sympy.abc import x, y + >>> from sympy import reduce_inequalities + + >>> reduce_inequalities(0 <= x + 3, []) + (-3 <= x) & (x < oo) + + >>> reduce_inequalities(0 <= x + y*2 - 1, [x]) + (x < oo) & (x >= 1 - 2*y) + """ + if not iterable(inequalities): + inequalities = [inequalities] + inequalities = [sympify(i) for i in inequalities] + + gens = set().union(*[i.free_symbols for i in inequalities]) + + if not iterable(symbols): + symbols = [symbols] + symbols = (set(symbols) or gens) & gens + if any(i.is_extended_real is False for i in symbols): + raise TypeError(filldedent(''' + inequalities cannot contain symbols that are not real. + ''')) + + # make vanilla symbol real + recast = {i: Dummy(i.name, extended_real=True) + for i in gens if i.is_extended_real is None} + inequalities = [i.xreplace(recast) for i in inequalities] + symbols = {i.xreplace(recast) for i in symbols} + + # prefilter + keep = [] + for i in inequalities: + if isinstance(i, Relational): + i = i.func(i.lhs.as_expr() - i.rhs.as_expr(), 0) + elif i not in (True, False): + i = Eq(i, 0) + if i == True: + continue + elif i == False: + return S.false + if i.lhs.is_number: + raise NotImplementedError( + "could not determine truth value of %s" % i) + keep.append(i) + inequalities = keep + del keep + + # solve system + rv = _reduce_inequalities(inequalities, symbols) + + # restore original symbols and return + return rv.xreplace({v: k for k, v in recast.items()}) diff --git a/lib/python3.12/site-packages/sympy/solvers/pde.py b/lib/python3.12/site-packages/sympy/solvers/pde.py new file mode 100644 index 0000000000000000000000000000000000000000..75e5503145af859e0f9bf9e95f3fd9d88bbfc5dc --- /dev/null +++ b/lib/python3.12/site-packages/sympy/solvers/pde.py @@ -0,0 +1,971 @@ +""" +This module contains pdsolve() and different helper functions that it +uses. It is heavily inspired by the ode module and hence the basic +infrastructure remains the same. + +**Functions in this module** + + These are the user functions in this module: + + - pdsolve() - Solves PDE's + - classify_pde() - Classifies PDEs into possible hints for dsolve(). + - pde_separate() - Separate variables in partial differential equation either by + additive or multiplicative separation approach. + + These are the helper functions in this module: + + - pde_separate_add() - Helper function for searching additive separable solutions. + - pde_separate_mul() - Helper function for searching multiplicative + separable solutions. + +**Currently implemented solver methods** + +The following methods are implemented for solving partial differential +equations. See the docstrings of the various pde_hint() functions for +more information on each (run help(pde)): + + - 1st order linear homogeneous partial differential equations + with constant coefficients. + - 1st order linear general partial differential equations + with constant coefficients. + - 1st order linear partial differential equations with + variable coefficients. + +""" +from functools import reduce + +from itertools import combinations_with_replacement +from sympy.simplify import simplify # type: ignore +from sympy.core import Add, S +from sympy.core.function import Function, expand, AppliedUndef, Subs +from sympy.core.relational import Equality, Eq +from sympy.core.symbol import Symbol, Wild, symbols +from sympy.functions import exp +from sympy.integrals.integrals import Integral, integrate +from sympy.utilities.iterables import has_dups, is_sequence +from sympy.utilities.misc import filldedent + +from sympy.solvers.deutils import _preprocess, ode_order, _desolve +from sympy.solvers.solvers import solve +from sympy.simplify.radsimp import collect + +import operator + + +allhints = ( + "1st_linear_constant_coeff_homogeneous", + "1st_linear_constant_coeff", + "1st_linear_constant_coeff_Integral", + "1st_linear_variable_coeff" + ) + + +def pdsolve(eq, func=None, hint='default', dict=False, solvefun=None, **kwargs): + """ + Solves any (supported) kind of partial differential equation. + + **Usage** + + pdsolve(eq, f(x,y), hint) -> Solve partial differential equation + eq for function f(x,y), using method hint. + + **Details** + + ``eq`` can be any supported partial differential equation (see + the pde docstring for supported methods). This can either + be an Equality, or an expression, which is assumed to be + equal to 0. + + ``f(x,y)`` is a function of two variables whose derivatives in that + variable make up the partial differential equation. In many + cases it is not necessary to provide this; it will be autodetected + (and an error raised if it could not be detected). + + ``hint`` is the solving method that you want pdsolve to use. Use + classify_pde(eq, f(x,y)) to get all of the possible hints for + a PDE. The default hint, 'default', will use whatever hint + is returned first by classify_pde(). See Hints below for + more options that you can use for hint. + + ``solvefun`` is the convention used for arbitrary functions returned + by the PDE solver. If not set by the user, it is set by default + to be F. + + **Hints** + + Aside from the various solving methods, there are also some + meta-hints that you can pass to pdsolve(): + + "default": + This uses whatever hint is returned first by + classify_pde(). This is the default argument to + pdsolve(). + + "all": + To make pdsolve apply all relevant classification hints, + use pdsolve(PDE, func, hint="all"). This will return a + dictionary of hint:solution terms. If a hint causes + pdsolve to raise the NotImplementedError, value of that + hint's key will be the exception object raised. The + dictionary will also include some special keys: + + - order: The order of the PDE. See also ode_order() in + deutils.py + - default: The solution that would be returned by + default. This is the one produced by the hint that + appears first in the tuple returned by classify_pde(). + + "all_Integral": + This is the same as "all", except if a hint also has a + corresponding "_Integral" hint, it only returns the + "_Integral" hint. This is useful if "all" causes + pdsolve() to hang because of a difficult or impossible + integral. This meta-hint will also be much faster than + "all", because integrate() is an expensive routine. + + See also the classify_pde() docstring for more info on hints, + and the pde docstring for a list of all supported hints. + + **Tips** + - You can declare the derivative of an unknown function this way: + + >>> from sympy import Function, Derivative + >>> from sympy.abc import x, y # x and y are the independent variables + >>> f = Function("f")(x, y) # f is a function of x and y + >>> # fx will be the partial derivative of f with respect to x + >>> fx = Derivative(f, x) + >>> # fy will be the partial derivative of f with respect to y + >>> fy = Derivative(f, y) + + - See test_pde.py for many tests, which serves also as a set of + examples for how to use pdsolve(). + - pdsolve always returns an Equality class (except for the case + when the hint is "all" or "all_Integral"). Note that it is not possible + to get an explicit solution for f(x, y) as in the case of ODE's + - Do help(pde.pde_hintname) to get help more information on a + specific hint + + + Examples + ======== + + >>> from sympy.solvers.pde import pdsolve + >>> from sympy import Function, Eq + >>> from sympy.abc import x, y + >>> f = Function('f') + >>> u = f(x, y) + >>> ux = u.diff(x) + >>> uy = u.diff(y) + >>> eq = Eq(1 + (2*(ux/u)) + (3*(uy/u)), 0) + >>> pdsolve(eq) + Eq(f(x, y), F(3*x - 2*y)*exp(-2*x/13 - 3*y/13)) + + """ + + if not solvefun: + solvefun = Function('F') + + # See the docstring of _desolve for more details. + hints = _desolve(eq, func=func, hint=hint, simplify=True, + type='pde', **kwargs) + eq = hints.pop('eq', False) + all_ = hints.pop('all', False) + + if all_: + # TODO : 'best' hint should be implemented when adequate + # number of hints are added. + pdedict = {} + failed_hints = {} + gethints = classify_pde(eq, dict=True) + pdedict.update({'order': gethints['order'], + 'default': gethints['default']}) + for hint in hints: + try: + rv = _helper_simplify(eq, hint, hints[hint]['func'], + hints[hint]['order'], hints[hint][hint], solvefun) + except NotImplementedError as detail: + failed_hints[hint] = detail + else: + pdedict[hint] = rv + pdedict.update(failed_hints) + return pdedict + + else: + return _helper_simplify(eq, hints['hint'], hints['func'], + hints['order'], hints[hints['hint']], solvefun) + + +def _helper_simplify(eq, hint, func, order, match, solvefun): + """Helper function of pdsolve that calls the respective + pde functions to solve for the partial differential + equations. This minimizes the computation in + calling _desolve multiple times. + """ + + if hint.endswith("_Integral"): + solvefunc = globals()[ + "pde_" + hint[:-len("_Integral")]] + else: + solvefunc = globals()["pde_" + hint] + return _handle_Integral(solvefunc(eq, func, order, + match, solvefun), func, order, hint) + + +def _handle_Integral(expr, func, order, hint): + r""" + Converts a solution with integrals in it into an actual solution. + + Simplifies the integral mainly using doit() + """ + if hint.endswith("_Integral"): + return expr + + elif hint == "1st_linear_constant_coeff": + return simplify(expr.doit()) + + else: + return expr + + +def classify_pde(eq, func=None, dict=False, *, prep=True, **kwargs): + """ + Returns a tuple of possible pdsolve() classifications for a PDE. + + The tuple is ordered so that first item is the classification that + pdsolve() uses to solve the PDE by default. In general, + classifications near the beginning of the list will produce + better solutions faster than those near the end, though there are + always exceptions. To make pdsolve use a different classification, + use pdsolve(PDE, func, hint=). See also the pdsolve() + docstring for different meta-hints you can use. + + If ``dict`` is true, classify_pde() will return a dictionary of + hint:match expression terms. This is intended for internal use by + pdsolve(). Note that because dictionaries are ordered arbitrarily, + this will most likely not be in the same order as the tuple. + + You can get help on different hints by doing help(pde.pde_hintname), + where hintname is the name of the hint without "_Integral". + + See sympy.pde.allhints or the sympy.pde docstring for a list of all + supported hints that can be returned from classify_pde. + + + Examples + ======== + + >>> from sympy.solvers.pde import classify_pde + >>> from sympy import Function, Eq + >>> from sympy.abc import x, y + >>> f = Function('f') + >>> u = f(x, y) + >>> ux = u.diff(x) + >>> uy = u.diff(y) + >>> eq = Eq(1 + (2*(ux/u)) + (3*(uy/u)), 0) + >>> classify_pde(eq) + ('1st_linear_constant_coeff_homogeneous',) + """ + + if func and len(func.args) != 2: + raise NotImplementedError("Right now only partial " + "differential equations of two variables are supported") + + if prep or func is None: + prep, func_ = _preprocess(eq, func) + if func is None: + func = func_ + + if isinstance(eq, Equality): + if eq.rhs != 0: + return classify_pde(eq.lhs - eq.rhs, func) + eq = eq.lhs + + f = func.func + x = func.args[0] + y = func.args[1] + fx = f(x,y).diff(x) + fy = f(x,y).diff(y) + + # TODO : For now pde.py uses support offered by the ode_order function + # to find the order with respect to a multi-variable function. An + # improvement could be to classify the order of the PDE on the basis of + # individual variables. + order = ode_order(eq, f(x,y)) + + # hint:matchdict or hint:(tuple of matchdicts) + # Also will contain "default": and "order":order items. + matching_hints = {'order': order} + + if not order: + if dict: + matching_hints["default"] = None + return matching_hints + return () + + eq = expand(eq) + + a = Wild('a', exclude = [f(x,y)]) + b = Wild('b', exclude = [f(x,y), fx, fy, x, y]) + c = Wild('c', exclude = [f(x,y), fx, fy, x, y]) + d = Wild('d', exclude = [f(x,y), fx, fy, x, y]) + e = Wild('e', exclude = [f(x,y), fx, fy]) + n = Wild('n', exclude = [x, y]) + # Try removing the smallest power of f(x,y) + # from the highest partial derivatives of f(x,y) + reduced_eq = eq + if eq.is_Add: + power = None + for i in set(combinations_with_replacement((x,y), order)): + coeff = eq.coeff(f(x,y).diff(*i)) + if coeff == 1: + continue + match = coeff.match(a*f(x,y)**n) + if match and match[a]: + if power is None or match[n] < power: + power = match[n] + if power: + den = f(x,y)**power + reduced_eq = Add(*[arg/den for arg in eq.args]) + + if order == 1: + reduced_eq = collect(reduced_eq, f(x, y)) + r = reduced_eq.match(b*fx + c*fy + d*f(x,y) + e) + if r: + if not r[e]: + ## Linear first-order homogeneous partial-differential + ## equation with constant coefficients + r.update({'b': b, 'c': c, 'd': d}) + matching_hints["1st_linear_constant_coeff_homogeneous"] = r + elif r[b]**2 + r[c]**2 != 0: + ## Linear first-order general partial-differential + ## equation with constant coefficients + r.update({'b': b, 'c': c, 'd': d, 'e': e}) + matching_hints["1st_linear_constant_coeff"] = r + matching_hints["1st_linear_constant_coeff_Integral"] = r + + else: + b = Wild('b', exclude=[f(x, y), fx, fy]) + c = Wild('c', exclude=[f(x, y), fx, fy]) + d = Wild('d', exclude=[f(x, y), fx, fy]) + r = reduced_eq.match(b*fx + c*fy + d*f(x,y) + e) + if r: + r.update({'b': b, 'c': c, 'd': d, 'e': e}) + matching_hints["1st_linear_variable_coeff"] = r + + # Order keys based on allhints. + rettuple = tuple(i for i in allhints if i in matching_hints) + + if dict: + # Dictionaries are ordered arbitrarily, so make note of which + # hint would come first for pdsolve(). Use an ordered dict in Py 3. + matching_hints["default"] = None + matching_hints["ordered_hints"] = rettuple + for i in allhints: + if i in matching_hints: + matching_hints["default"] = i + break + return matching_hints + return rettuple + + +def checkpdesol(pde, sol, func=None, solve_for_func=True): + """ + Checks if the given solution satisfies the partial differential + equation. + + pde is the partial differential equation which can be given in the + form of an equation or an expression. sol is the solution for which + the pde is to be checked. This can also be given in an equation or + an expression form. If the function is not provided, the helper + function _preprocess from deutils is used to identify the function. + + If a sequence of solutions is passed, the same sort of container will be + used to return the result for each solution. + + The following methods are currently being implemented to check if the + solution satisfies the PDE: + + 1. Directly substitute the solution in the PDE and check. If the + solution has not been solved for f, then it will solve for f + provided solve_for_func has not been set to False. + + If the solution satisfies the PDE, then a tuple (True, 0) is returned. + Otherwise a tuple (False, expr) where expr is the value obtained + after substituting the solution in the PDE. However if a known solution + returns False, it may be due to the inability of doit() to simplify it to zero. + + Examples + ======== + + >>> from sympy import Function, symbols + >>> from sympy.solvers.pde import checkpdesol, pdsolve + >>> x, y = symbols('x y') + >>> f = Function('f') + >>> eq = 2*f(x,y) + 3*f(x,y).diff(x) + 4*f(x,y).diff(y) + >>> sol = pdsolve(eq) + >>> assert checkpdesol(eq, sol)[0] + >>> eq = x*f(x,y) + f(x,y).diff(x) + >>> checkpdesol(eq, sol) + (False, (x*F(4*x - 3*y) - 6*F(4*x - 3*y)/25 + 4*Subs(Derivative(F(_xi_1), _xi_1), _xi_1, 4*x - 3*y))*exp(-6*x/25 - 8*y/25)) + """ + + # Converting the pde into an equation + if not isinstance(pde, Equality): + pde = Eq(pde, 0) + + # If no function is given, try finding the function present. + if func is None: + try: + _, func = _preprocess(pde.lhs) + except ValueError: + funcs = [s.atoms(AppliedUndef) for s in ( + sol if is_sequence(sol, set) else [sol])] + funcs = set().union(funcs) + if len(funcs) != 1: + raise ValueError( + 'must pass func arg to checkpdesol for this case.') + func = funcs.pop() + + # If the given solution is in the form of a list or a set + # then return a list or set of tuples. + if is_sequence(sol, set): + return type(sol)([checkpdesol( + pde, i, func=func, + solve_for_func=solve_for_func) for i in sol]) + + # Convert solution into an equation + if not isinstance(sol, Equality): + sol = Eq(func, sol) + elif sol.rhs == func: + sol = sol.reversed + + # Try solving for the function + solved = sol.lhs == func and not sol.rhs.has(func) + if solve_for_func and not solved: + solved = solve(sol, func) + if solved: + if len(solved) == 1: + return checkpdesol(pde, Eq(func, solved[0]), + func=func, solve_for_func=False) + else: + return checkpdesol(pde, [Eq(func, t) for t in solved], + func=func, solve_for_func=False) + + # try direct substitution of the solution into the PDE and simplify + if sol.lhs == func: + pde = pde.lhs - pde.rhs + s = simplify(pde.subs(func, sol.rhs).doit()) + return s is S.Zero, s + + raise NotImplementedError(filldedent(''' + Unable to test if %s is a solution to %s.''' % (sol, pde))) + + + +def pde_1st_linear_constant_coeff_homogeneous(eq, func, order, match, solvefun): + r""" + Solves a first order linear homogeneous + partial differential equation with constant coefficients. + + The general form of this partial differential equation is + + .. math:: a \frac{\partial f(x,y)}{\partial x} + + b \frac{\partial f(x,y)}{\partial y} + c f(x,y) = 0 + + where `a`, `b` and `c` are constants. + + The general solution is of the form: + + .. math:: + f(x, y) = F(- a y + b x ) e^{- \frac{c (a x + b y)}{a^2 + b^2}} + + and can be found in SymPy with ``pdsolve``:: + + >>> from sympy.solvers import pdsolve + >>> from sympy.abc import x, y, a, b, c + >>> from sympy import Function, pprint + >>> f = Function('f') + >>> u = f(x,y) + >>> ux = u.diff(x) + >>> uy = u.diff(y) + >>> genform = a*ux + b*uy + c*u + >>> pprint(genform) + d d + a*--(f(x, y)) + b*--(f(x, y)) + c*f(x, y) + dx dy + + >>> pprint(pdsolve(genform)) + -c*(a*x + b*y) + --------------- + 2 2 + a + b + f(x, y) = F(-a*y + b*x)*e + + Examples + ======== + + >>> from sympy import pdsolve + >>> from sympy import Function, pprint + >>> from sympy.abc import x,y + >>> f = Function('f') + >>> pdsolve(f(x,y) + f(x,y).diff(x) + f(x,y).diff(y)) + Eq(f(x, y), F(x - y)*exp(-x/2 - y/2)) + >>> pprint(pdsolve(f(x,y) + f(x,y).diff(x) + f(x,y).diff(y))) + x y + - - - - + 2 2 + f(x, y) = F(x - y)*e + + References + ========== + + - Viktor Grigoryan, "Partial Differential Equations" + Math 124A - Fall 2010, pp.7 + + """ + # TODO : For now homogeneous first order linear PDE's having + # two variables are implemented. Once there is support for + # solving systems of ODE's, this can be extended to n variables. + + f = func.func + x = func.args[0] + y = func.args[1] + b = match[match['b']] + c = match[match['c']] + d = match[match['d']] + return Eq(f(x,y), exp(-S(d)/(b**2 + c**2)*(b*x + c*y))*solvefun(c*x - b*y)) + + +def pde_1st_linear_constant_coeff(eq, func, order, match, solvefun): + r""" + Solves a first order linear partial differential equation + with constant coefficients. + + The general form of this partial differential equation is + + .. math:: a \frac{\partial f(x,y)}{\partial x} + + b \frac{\partial f(x,y)}{\partial y} + + c f(x,y) = G(x,y) + + where `a`, `b` and `c` are constants and `G(x, y)` can be an arbitrary + function in `x` and `y`. + + The general solution of the PDE is: + + .. math:: + f(x, y) = \left. \left[F(\eta) + \frac{1}{a^2 + b^2} + \int\limits^{a x + b y} G\left(\frac{a \xi + b \eta}{a^2 + b^2}, + \frac{- a \eta + b \xi}{a^2 + b^2} \right) + e^{\frac{c \xi}{a^2 + b^2}}\, d\xi\right] + e^{- \frac{c \xi}{a^2 + b^2}} + \right|_{\substack{\eta=- a y + b x\\ \xi=a x + b y }}\, , + + where `F(\eta)` is an arbitrary single-valued function. The solution + can be found in SymPy with ``pdsolve``:: + + >>> from sympy.solvers import pdsolve + >>> from sympy.abc import x, y, a, b, c + >>> from sympy import Function, pprint + >>> f = Function('f') + >>> G = Function('G') + >>> u = f(x, y) + >>> ux = u.diff(x) + >>> uy = u.diff(y) + >>> genform = a*ux + b*uy + c*u - G(x,y) + >>> pprint(genform) + d d + a*--(f(x, y)) + b*--(f(x, y)) + c*f(x, y) - G(x, y) + dx dy + >>> pprint(pdsolve(genform, hint='1st_linear_constant_coeff_Integral')) + // a*x + b*y \ \| + || / | || + || | | || + || | c*xi | || + || | ------- | || + || | 2 2 | || + || | /a*xi + b*eta -a*eta + b*xi\ a + b | || + || | G|------------, -------------|*e d(xi)| || + || | | 2 2 2 2 | | || + || | \ a + b a + b / | -c*xi || + || | | -------|| + || / | 2 2|| + || | a + b || + f(x, y) = ||F(eta) + -------------------------------------------------------|*e || + || 2 2 | || + \\ a + b / /|eta=-a*y + b*x, xi=a*x + b*y + + Examples + ======== + + >>> from sympy.solvers.pde import pdsolve + >>> from sympy import Function, pprint, exp + >>> from sympy.abc import x,y + >>> f = Function('f') + >>> eq = -2*f(x,y).diff(x) + 4*f(x,y).diff(y) + 5*f(x,y) - exp(x + 3*y) + >>> pdsolve(eq) + Eq(f(x, y), (F(4*x + 2*y)*exp(x/2) + exp(x + 4*y)/15)*exp(-y)) + + References + ========== + + - Viktor Grigoryan, "Partial Differential Equations" + Math 124A - Fall 2010, pp.7 + + """ + + # TODO : For now homogeneous first order linear PDE's having + # two variables are implemented. Once there is support for + # solving systems of ODE's, this can be extended to n variables. + xi, eta = symbols("xi eta") + f = func.func + x = func.args[0] + y = func.args[1] + b = match[match['b']] + c = match[match['c']] + d = match[match['d']] + e = -match[match['e']] + expterm = exp(-S(d)/(b**2 + c**2)*xi) + functerm = solvefun(eta) + solvedict = solve((b*x + c*y - xi, c*x - b*y - eta), x, y) + # Integral should remain as it is in terms of xi, + # doit() should be done in _handle_Integral. + genterm = (1/S(b**2 + c**2))*Integral( + (1/expterm*e).subs(solvedict), (xi, b*x + c*y)) + return Eq(f(x,y), Subs(expterm*(functerm + genterm), + (eta, xi), (c*x - b*y, b*x + c*y))) + + +def pde_1st_linear_variable_coeff(eq, func, order, match, solvefun): + r""" + Solves a first order linear partial differential equation + with variable coefficients. The general form of this partial + differential equation is + + .. math:: a(x, y) \frac{\partial f(x, y)}{\partial x} + + b(x, y) \frac{\partial f(x, y)}{\partial y} + + c(x, y) f(x, y) = G(x, y) + + where `a(x, y)`, `b(x, y)`, `c(x, y)` and `G(x, y)` are arbitrary + functions in `x` and `y`. This PDE is converted into an ODE by + making the following transformation: + + 1. `\xi` as `x` + + 2. `\eta` as the constant in the solution to the differential + equation `\frac{dy}{dx} = -\frac{b}{a}` + + Making the previous substitutions reduces it to the linear ODE + + .. math:: a(\xi, \eta)\frac{du}{d\xi} + c(\xi, \eta)u - G(\xi, \eta) = 0 + + which can be solved using ``dsolve``. + + >>> from sympy.abc import x, y + >>> from sympy import Function, pprint + >>> a, b, c, G, f= [Function(i) for i in ['a', 'b', 'c', 'G', 'f']] + >>> u = f(x,y) + >>> ux = u.diff(x) + >>> uy = u.diff(y) + >>> genform = a(x, y)*u + b(x, y)*ux + c(x, y)*uy - G(x,y) + >>> pprint(genform) + d d + -G(x, y) + a(x, y)*f(x, y) + b(x, y)*--(f(x, y)) + c(x, y)*--(f(x, y)) + dx dy + + + Examples + ======== + + >>> from sympy.solvers.pde import pdsolve + >>> from sympy import Function, pprint + >>> from sympy.abc import x,y + >>> f = Function('f') + >>> eq = x*(u.diff(x)) - y*(u.diff(y)) + y**2*u - y**2 + >>> pdsolve(eq) + Eq(f(x, y), F(x*y)*exp(y**2/2) + 1) + + References + ========== + + - Viktor Grigoryan, "Partial Differential Equations" + Math 124A - Fall 2010, pp.7 + + """ + from sympy.solvers.ode import dsolve + + xi, eta = symbols("xi eta") + f = func.func + x = func.args[0] + y = func.args[1] + b = match[match['b']] + c = match[match['c']] + d = match[match['d']] + e = -match[match['e']] + + + if not d: + # To deal with cases like b*ux = e or c*uy = e + if not (b and c): + if c: + try: + tsol = integrate(e/c, y) + except NotImplementedError: + raise NotImplementedError("Unable to find a solution" + " due to inability of integrate") + else: + return Eq(f(x,y), solvefun(x) + tsol) + if b: + try: + tsol = integrate(e/b, x) + except NotImplementedError: + raise NotImplementedError("Unable to find a solution" + " due to inability of integrate") + else: + return Eq(f(x,y), solvefun(y) + tsol) + + if not c: + # To deal with cases when c is 0, a simpler method is used. + # The PDE reduces to b*(u.diff(x)) + d*u = e, which is a linear ODE in x + plode = f(x).diff(x)*b + d*f(x) - e + sol = dsolve(plode, f(x)) + syms = sol.free_symbols - plode.free_symbols - {x, y} + rhs = _simplify_variable_coeff(sol.rhs, syms, solvefun, y) + return Eq(f(x, y), rhs) + + if not b: + # To deal with cases when b is 0, a simpler method is used. + # The PDE reduces to c*(u.diff(y)) + d*u = e, which is a linear ODE in y + plode = f(y).diff(y)*c + d*f(y) - e + sol = dsolve(plode, f(y)) + syms = sol.free_symbols - plode.free_symbols - {x, y} + rhs = _simplify_variable_coeff(sol.rhs, syms, solvefun, x) + return Eq(f(x, y), rhs) + + dummy = Function('d') + h = (c/b).subs(y, dummy(x)) + sol = dsolve(dummy(x).diff(x) - h, dummy(x)) + if isinstance(sol, list): + sol = sol[0] + solsym = sol.free_symbols - h.free_symbols - {x, y} + if len(solsym) == 1: + solsym = solsym.pop() + etat = (solve(sol, solsym)[0]).subs(dummy(x), y) + ysub = solve(eta - etat, y)[0] + deq = (b*(f(x).diff(x)) + d*f(x) - e).subs(y, ysub) + final = (dsolve(deq, f(x), hint='1st_linear')).rhs + if isinstance(final, list): + final = final[0] + finsyms = final.free_symbols - deq.free_symbols - {x, y} + rhs = _simplify_variable_coeff(final, finsyms, solvefun, etat) + return Eq(f(x, y), rhs) + + else: + raise NotImplementedError("Cannot solve the partial differential equation due" + " to inability of constantsimp") + + +def _simplify_variable_coeff(sol, syms, func, funcarg): + r""" + Helper function to replace constants by functions in 1st_linear_variable_coeff + """ + eta = Symbol("eta") + if len(syms) == 1: + sym = syms.pop() + final = sol.subs(sym, func(funcarg)) + + else: + for sym in syms: + final = sol.subs(sym, func(funcarg)) + + return simplify(final.subs(eta, funcarg)) + + +def pde_separate(eq, fun, sep, strategy='mul'): + """Separate variables in partial differential equation either by additive + or multiplicative separation approach. It tries to rewrite an equation so + that one of the specified variables occurs on a different side of the + equation than the others. + + :param eq: Partial differential equation + + :param fun: Original function F(x, y, z) + + :param sep: List of separated functions [X(x), u(y, z)] + + :param strategy: Separation strategy. You can choose between additive + separation ('add') and multiplicative separation ('mul') which is + default. + + Examples + ======== + + >>> from sympy import E, Eq, Function, pde_separate, Derivative as D + >>> from sympy.abc import x, t + >>> u, X, T = map(Function, 'uXT') + + >>> eq = Eq(D(u(x, t), x), E**(u(x, t))*D(u(x, t), t)) + >>> pde_separate(eq, u(x, t), [X(x), T(t)], strategy='add') + [exp(-X(x))*Derivative(X(x), x), exp(T(t))*Derivative(T(t), t)] + + >>> eq = Eq(D(u(x, t), x, 2), D(u(x, t), t, 2)) + >>> pde_separate(eq, u(x, t), [X(x), T(t)], strategy='mul') + [Derivative(X(x), (x, 2))/X(x), Derivative(T(t), (t, 2))/T(t)] + + See Also + ======== + pde_separate_add, pde_separate_mul + """ + + do_add = False + if strategy == 'add': + do_add = True + elif strategy == 'mul': + do_add = False + else: + raise ValueError('Unknown strategy: %s' % strategy) + + if isinstance(eq, Equality): + if eq.rhs != 0: + return pde_separate(Eq(eq.lhs - eq.rhs, 0), fun, sep, strategy) + else: + return pde_separate(Eq(eq, 0), fun, sep, strategy) + + if eq.rhs != 0: + raise ValueError("Value should be 0") + + # Handle arguments + orig_args = list(fun.args) + subs_args = [arg for s in sep for arg in s.args] + + if do_add: + functions = reduce(operator.add, sep) + else: + functions = reduce(operator.mul, sep) + + # Check whether variables match + if len(subs_args) != len(orig_args): + raise ValueError("Variable counts do not match") + # Check for duplicate arguments like [X(x), u(x, y)] + if has_dups(subs_args): + raise ValueError("Duplicate substitution arguments detected") + # Check whether the variables match + if set(orig_args) != set(subs_args): + raise ValueError("Arguments do not match") + + # Substitute original function with separated... + result = eq.lhs.subs(fun, functions).doit() + + # Divide by terms when doing multiplicative separation + if not do_add: + eq = 0 + for i in result.args: + eq += i/functions + result = eq + + svar = subs_args[0] + dvar = subs_args[1:] + return _separate(result, svar, dvar) + + +def pde_separate_add(eq, fun, sep): + """ + Helper function for searching additive separable solutions. + + Consider an equation of two independent variables x, y and a dependent + variable w, we look for the product of two functions depending on different + arguments: + + `w(x, y, z) = X(x) + y(y, z)` + + Examples + ======== + + >>> from sympy import E, Eq, Function, pde_separate_add, Derivative as D + >>> from sympy.abc import x, t + >>> u, X, T = map(Function, 'uXT') + + >>> eq = Eq(D(u(x, t), x), E**(u(x, t))*D(u(x, t), t)) + >>> pde_separate_add(eq, u(x, t), [X(x), T(t)]) + [exp(-X(x))*Derivative(X(x), x), exp(T(t))*Derivative(T(t), t)] + + """ + return pde_separate(eq, fun, sep, strategy='add') + + +def pde_separate_mul(eq, fun, sep): + """ + Helper function for searching multiplicative separable solutions. + + Consider an equation of two independent variables x, y and a dependent + variable w, we look for the product of two functions depending on different + arguments: + + `w(x, y, z) = X(x)*u(y, z)` + + Examples + ======== + + >>> from sympy import Function, Eq, pde_separate_mul, Derivative as D + >>> from sympy.abc import x, y + >>> u, X, Y = map(Function, 'uXY') + + >>> eq = Eq(D(u(x, y), x, 2), D(u(x, y), y, 2)) + >>> pde_separate_mul(eq, u(x, y), [X(x), Y(y)]) + [Derivative(X(x), (x, 2))/X(x), Derivative(Y(y), (y, 2))/Y(y)] + + """ + return pde_separate(eq, fun, sep, strategy='mul') + + +def _separate(eq, dep, others): + """Separate expression into two parts based on dependencies of variables.""" + + # FIRST PASS + # Extract derivatives depending our separable variable... + terms = set() + for term in eq.args: + if term.is_Mul: + for i in term.args: + if i.is_Derivative and not i.has(*others): + terms.add(term) + continue + elif term.is_Derivative and not term.has(*others): + terms.add(term) + # Find the factor that we need to divide by + div = set() + for term in terms: + ext, sep = term.expand().as_independent(dep) + # Failed? + if sep.has(*others): + return None + div.add(ext) + # FIXME: Find lcm() of all the divisors and divide with it, instead of + # current hack :( + # https://github.com/sympy/sympy/issues/4597 + if len(div) > 0: + # double sum required or some tests will fail + eq = Add(*[simplify(Add(*[term/i for i in div])) for term in eq.args]) + # SECOND PASS - separate the derivatives + div = set() + lhs = rhs = 0 + for term in eq.args: + # Check, whether we have already term with independent variable... + if not term.has(*others): + lhs += term + continue + # ...otherwise, try to separate + temp, sep = term.expand().as_independent(dep) + # Failed? + if sep.has(*others): + return None + # Extract the divisors + div.add(sep) + rhs -= term.expand() + # Do the division + fulldiv = reduce(operator.add, div) + lhs = simplify(lhs/fulldiv).expand() + rhs = simplify(rhs/fulldiv).expand() + # ...and check whether we were successful :) + if lhs.has(*others) or rhs.has(dep): + return None + return [lhs, rhs] diff --git a/lib/python3.12/site-packages/sympy/solvers/polysys.py b/lib/python3.12/site-packages/sympy/solvers/polysys.py new file mode 100644 index 0000000000000000000000000000000000000000..eb4787e2fc32c3128bbf7de4770fadc0d211f9e8 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/solvers/polysys.py @@ -0,0 +1,437 @@ +"""Solvers of systems of polynomial equations. """ +import itertools + +from sympy.core import S +from sympy.core.sorting import default_sort_key +from sympy.polys import Poly, groebner, roots +from sympy.polys.polytools import parallel_poly_from_expr +from sympy.polys.polyerrors import (ComputationFailed, + PolificationFailed, CoercionFailed) +from sympy.simplify import rcollect +from sympy.utilities import postfixes +from sympy.utilities.misc import filldedent + + +class SolveFailed(Exception): + """Raised when solver's conditions were not met. """ + + +def solve_poly_system(seq, *gens, strict=False, **args): + """ + Return a list of solutions for the system of polynomial equations + or else None. + + Parameters + ========== + + seq: a list/tuple/set + Listing all the equations that are needed to be solved + gens: generators + generators of the equations in seq for which we want the + solutions + strict: a boolean (default is False) + if strict is True, NotImplementedError will be raised if + the solution is known to be incomplete (which can occur if + not all solutions are expressible in radicals) + args: Keyword arguments + Special options for solving the equations. + + + Returns + ======= + + List[Tuple] + a list of tuples with elements being solutions for the + symbols in the order they were passed as gens + None + None is returned when the computed basis contains only the ground. + + Examples + ======== + + >>> from sympy import solve_poly_system + >>> from sympy.abc import x, y + + >>> solve_poly_system([x*y - 2*y, 2*y**2 - x**2], x, y) + [(0, 0), (2, -sqrt(2)), (2, sqrt(2))] + + >>> solve_poly_system([x**5 - x + y**3, y**2 - 1], x, y, strict=True) + Traceback (most recent call last): + ... + UnsolvableFactorError + + """ + try: + polys, opt = parallel_poly_from_expr(seq, *gens, **args) + except PolificationFailed as exc: + raise ComputationFailed('solve_poly_system', len(seq), exc) + + if len(polys) == len(opt.gens) == 2: + f, g = polys + + if all(i <= 2 for i in f.degree_list() + g.degree_list()): + try: + return solve_biquadratic(f, g, opt) + except SolveFailed: + pass + + return solve_generic(polys, opt, strict=strict) + + +def solve_biquadratic(f, g, opt): + """Solve a system of two bivariate quadratic polynomial equations. + + Parameters + ========== + + f: a single Expr or Poly + First equation + g: a single Expr or Poly + Second Equation + opt: an Options object + For specifying keyword arguments and generators + + Returns + ======= + + List[Tuple] + a list of tuples with elements being solutions for the + symbols in the order they were passed as gens + None + None is returned when the computed basis contains only the ground. + + Examples + ======== + + >>> from sympy import Options, Poly + >>> from sympy.abc import x, y + >>> from sympy.solvers.polysys import solve_biquadratic + >>> NewOption = Options((x, y), {'domain': 'ZZ'}) + + >>> a = Poly(y**2 - 4 + x, y, x, domain='ZZ') + >>> b = Poly(y*2 + 3*x - 7, y, x, domain='ZZ') + >>> solve_biquadratic(a, b, NewOption) + [(1/3, 3), (41/27, 11/9)] + + >>> a = Poly(y + x**2 - 3, y, x, domain='ZZ') + >>> b = Poly(-y + x - 4, y, x, domain='ZZ') + >>> solve_biquadratic(a, b, NewOption) + [(7/2 - sqrt(29)/2, -sqrt(29)/2 - 1/2), (sqrt(29)/2 + 7/2, -1/2 + \ + sqrt(29)/2)] + """ + G = groebner([f, g]) + + if len(G) == 1 and G[0].is_ground: + return None + + if len(G) != 2: + raise SolveFailed + + x, y = opt.gens + p, q = G + if not p.gcd(q).is_ground: + # not 0-dimensional + raise SolveFailed + + p = Poly(p, x, expand=False) + p_roots = [rcollect(expr, y) for expr in roots(p).keys()] + + q = q.ltrim(-1) + q_roots = list(roots(q).keys()) + + solutions = [(p_root.subs(y, q_root), q_root) for q_root, p_root in + itertools.product(q_roots, p_roots)] + + return sorted(solutions, key=default_sort_key) + + +def solve_generic(polys, opt, strict=False): + """ + Solve a generic system of polynomial equations. + + Returns all possible solutions over C[x_1, x_2, ..., x_m] of a + set F = { f_1, f_2, ..., f_n } of polynomial equations, using + Groebner basis approach. For now only zero-dimensional systems + are supported, which means F can have at most a finite number + of solutions. If the basis contains only the ground, None is + returned. + + The algorithm works by the fact that, supposing G is the basis + of F with respect to an elimination order (here lexicographic + order is used), G and F generate the same ideal, they have the + same set of solutions. By the elimination property, if G is a + reduced, zero-dimensional Groebner basis, then there exists an + univariate polynomial in G (in its last variable). This can be + solved by computing its roots. Substituting all computed roots + for the last (eliminated) variable in other elements of G, new + polynomial system is generated. Applying the above procedure + recursively, a finite number of solutions can be found. + + The ability of finding all solutions by this procedure depends + on the root finding algorithms. If no solutions were found, it + means only that roots() failed, but the system is solvable. To + overcome this difficulty use numerical algorithms instead. + + Parameters + ========== + + polys: a list/tuple/set + Listing all the polynomial equations that are needed to be solved + opt: an Options object + For specifying keyword arguments and generators + strict: a boolean + If strict is True, NotImplementedError will be raised if the solution + is known to be incomplete + + Returns + ======= + + List[Tuple] + a list of tuples with elements being solutions for the + symbols in the order they were passed as gens + None + None is returned when the computed basis contains only the ground. + + References + ========== + + .. [Buchberger01] B. Buchberger, Groebner Bases: A Short + Introduction for Systems Theorists, In: R. Moreno-Diaz, + B. Buchberger, J.L. Freire, Proceedings of EUROCAST'01, + February, 2001 + + .. [Cox97] D. Cox, J. Little, D. O'Shea, Ideals, Varieties + and Algorithms, Springer, Second Edition, 1997, pp. 112 + + Raises + ======== + + NotImplementedError + If the system is not zero-dimensional (does not have a finite + number of solutions) + + UnsolvableFactorError + If ``strict`` is True and not all solution components are + expressible in radicals + + Examples + ======== + + >>> from sympy import Poly, Options + >>> from sympy.solvers.polysys import solve_generic + >>> from sympy.abc import x, y + >>> NewOption = Options((x, y), {'domain': 'ZZ'}) + + >>> a = Poly(x - y + 5, x, y, domain='ZZ') + >>> b = Poly(x + y - 3, x, y, domain='ZZ') + >>> solve_generic([a, b], NewOption) + [(-1, 4)] + + >>> a = Poly(x - 2*y + 5, x, y, domain='ZZ') + >>> b = Poly(2*x - y - 3, x, y, domain='ZZ') + >>> solve_generic([a, b], NewOption) + [(11/3, 13/3)] + + >>> a = Poly(x**2 + y, x, y, domain='ZZ') + >>> b = Poly(x + y*4, x, y, domain='ZZ') + >>> solve_generic([a, b], NewOption) + [(0, 0), (1/4, -1/16)] + + >>> a = Poly(x**5 - x + y**3, x, y, domain='ZZ') + >>> b = Poly(y**2 - 1, x, y, domain='ZZ') + >>> solve_generic([a, b], NewOption, strict=True) + Traceback (most recent call last): + ... + UnsolvableFactorError + + """ + def _is_univariate(f): + """Returns True if 'f' is univariate in its last variable. """ + for monom in f.monoms(): + if any(monom[:-1]): + return False + + return True + + def _subs_root(f, gen, zero): + """Replace generator with a root so that the result is nice. """ + p = f.as_expr({gen: zero}) + + if f.degree(gen) >= 2: + p = p.expand(deep=False) + + return p + + def _solve_reduced_system(system, gens, entry=False): + """Recursively solves reduced polynomial systems. """ + if len(system) == len(gens) == 1: + # the below line will produce UnsolvableFactorError if + # strict=True and the solution from `roots` is incomplete + zeros = list(roots(system[0], gens[-1], strict=strict).keys()) + return [(zero,) for zero in zeros] + + basis = groebner(system, gens, polys=True) + + if len(basis) == 1 and basis[0].is_ground: + if not entry: + return [] + else: + return None + + univariate = list(filter(_is_univariate, basis)) + + if len(basis) < len(gens): + raise NotImplementedError(filldedent(''' + only zero-dimensional systems supported + (finite number of solutions) + ''')) + + if len(univariate) == 1: + f = univariate.pop() + else: + raise NotImplementedError(filldedent(''' + only zero-dimensional systems supported + (finite number of solutions) + ''')) + + gens = f.gens + gen = gens[-1] + + # the below line will produce UnsolvableFactorError if + # strict=True and the solution from `roots` is incomplete + zeros = list(roots(f.ltrim(gen), strict=strict).keys()) + + if not zeros: + return [] + + if len(basis) == 1: + return [(zero,) for zero in zeros] + + solutions = [] + + for zero in zeros: + new_system = [] + new_gens = gens[:-1] + + for b in basis[:-1]: + eq = _subs_root(b, gen, zero) + + if eq is not S.Zero: + new_system.append(eq) + + for solution in _solve_reduced_system(new_system, new_gens): + solutions.append(solution + (zero,)) + + if solutions and len(solutions[0]) != len(gens): + raise NotImplementedError(filldedent(''' + only zero-dimensional systems supported + (finite number of solutions) + ''')) + return solutions + + try: + result = _solve_reduced_system(polys, opt.gens, entry=True) + except CoercionFailed: + raise NotImplementedError + + if result is not None: + return sorted(result, key=default_sort_key) + + +def solve_triangulated(polys, *gens, **args): + """ + Solve a polynomial system using Gianni-Kalkbrenner algorithm. + + The algorithm proceeds by computing one Groebner basis in the ground + domain and then by iteratively computing polynomial factorizations in + appropriately constructed algebraic extensions of the ground domain. + + Parameters + ========== + + polys: a list/tuple/set + Listing all the equations that are needed to be solved + gens: generators + generators of the equations in polys for which we want the + solutions + args: Keyword arguments + Special options for solving the equations + + Returns + ======= + + List[Tuple] + A List of tuples. Solutions for symbols that satisfy the + equations listed in polys + + Examples + ======== + + >>> from sympy import solve_triangulated + >>> from sympy.abc import x, y, z + + >>> F = [x**2 + y + z - 1, x + y**2 + z - 1, x + y + z**2 - 1] + + >>> solve_triangulated(F, x, y, z) + [(0, 0, 1), (0, 1, 0), (1, 0, 0)] + + References + ========== + + 1. Patrizia Gianni, Teo Mora, Algebraic Solution of System of + Polynomial Equations using Groebner Bases, AAECC-5 on Applied Algebra, + Algebraic Algorithms and Error-Correcting Codes, LNCS 356 247--257, 1989 + + """ + G = groebner(polys, gens, polys=True) + G = list(reversed(G)) + + domain = args.get('domain') + + if domain is not None: + for i, g in enumerate(G): + G[i] = g.set_domain(domain) + + f, G = G[0].ltrim(-1), G[1:] + dom = f.get_domain() + + zeros = f.ground_roots() + solutions = {((zero,), dom) for zero in zeros} + + var_seq = reversed(gens[:-1]) + vars_seq = postfixes(gens[1:]) + + for var, vars in zip(var_seq, vars_seq): + _solutions = set() + + for values, dom in solutions: + H, mapping = [], list(zip(vars, values)) + + for g in G: + _vars = (var,) + vars + + if g.has_only_gens(*_vars) and g.degree(var) != 0: + h = g.ltrim(var).eval(dict(mapping)) + + if g.degree(var) == h.degree(): + H.append(h) + + p = min(H, key=lambda h: h.degree()) + zeros = p.ground_roots() + + for zero in zeros: + if not zero.is_Rational: + dom_zero = dom.algebraic_field(zero) + else: + dom_zero = dom + + _solutions.add(((zero,) + values, dom_zero)) + + solutions = _solutions + + solutions = list(solutions) + + for i, (solution, _) in enumerate(solutions): + solutions[i] = solution + + return sorted(solutions, key=default_sort_key) diff --git a/lib/python3.12/site-packages/sympy/solvers/recurr.py b/lib/python3.12/site-packages/sympy/solvers/recurr.py new file mode 100644 index 0000000000000000000000000000000000000000..ba627bbd4cb0844f11a8743634f5f10328aadca8 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/solvers/recurr.py @@ -0,0 +1,843 @@ +r""" +This module is intended for solving recurrences or, in other words, +difference equations. Currently supported are linear, inhomogeneous +equations with polynomial or rational coefficients. + +The solutions are obtained among polynomials, rational functions, +hypergeometric terms, or combinations of hypergeometric term which +are pairwise dissimilar. + +``rsolve_X`` functions were meant as a low level interface +for ``rsolve`` which would use Mathematica's syntax. + +Given a recurrence relation: + + .. math:: a_{k}(n) y(n+k) + a_{k-1}(n) y(n+k-1) + + ... + a_{0}(n) y(n) = f(n) + +where `k > 0` and `a_{i}(n)` are polynomials in `n`. To use +``rsolve_X`` we need to put all coefficients in to a list ``L`` of +`k+1` elements the following way: + + ``L = [a_{0}(n), ..., a_{k-1}(n), a_{k}(n)]`` + +where ``L[i]``, for `i=0, \ldots, k`, maps to +`a_{i}(n) y(n+i)` (`y(n+i)` is implicit). + +For example if we would like to compute `m`-th Bernoulli polynomial +up to a constant (example was taken from rsolve_poly docstring), +then we would use `b(n+1) - b(n) = m n^{m-1}` recurrence, which +has solution `b(n) = B_m + C`. + +Then ``L = [-1, 1]`` and `f(n) = m n^(m-1)` and finally for `m=4`: + +>>> from sympy import Symbol, bernoulli, rsolve_poly +>>> n = Symbol('n', integer=True) + +>>> rsolve_poly([-1, 1], 4*n**3, n) +C0 + n**4 - 2*n**3 + n**2 + +>>> bernoulli(4, n) +n**4 - 2*n**3 + n**2 - 1/30 + +For the sake of completeness, `f(n)` can be: + + [1] a polynomial -> rsolve_poly + [2] a rational function -> rsolve_ratio + [3] a hypergeometric function -> rsolve_hyper +""" +from collections import defaultdict + +from sympy.concrete import product +from sympy.core.singleton import S +from sympy.core.numbers import Rational, I +from sympy.core.symbol import Symbol, Wild, Dummy +from sympy.core.relational import Equality +from sympy.core.add import Add +from sympy.core.mul import Mul +from sympy.core.sorting import default_sort_key +from sympy.core.sympify import sympify + +from sympy.simplify import simplify, hypersimp, hypersimilar # type: ignore +from sympy.solvers import solve, solve_undetermined_coeffs +from sympy.polys import Poly, quo, gcd, lcm, roots, resultant +from sympy.functions import binomial, factorial, FallingFactorial, RisingFactorial +from sympy.matrices import Matrix, casoratian +from sympy.utilities.iterables import numbered_symbols + + +def rsolve_poly(coeffs, f, n, shift=0, **hints): + r""" + Given linear recurrence operator `\operatorname{L}` of order + `k` with polynomial coefficients and inhomogeneous equation + `\operatorname{L} y = f`, where `f` is a polynomial, we seek for + all polynomial solutions over field `K` of characteristic zero. + + The algorithm performs two basic steps: + + (1) Compute degree `N` of the general polynomial solution. + (2) Find all polynomials of degree `N` or less + of `\operatorname{L} y = f`. + + There are two methods for computing the polynomial solutions. + If the degree bound is relatively small, i.e. it's smaller than + or equal to the order of the recurrence, then naive method of + undetermined coefficients is being used. This gives a system + of algebraic equations with `N+1` unknowns. + + In the other case, the algorithm performs transformation of the + initial equation to an equivalent one for which the system of + algebraic equations has only `r` indeterminates. This method is + quite sophisticated (in comparison with the naive one) and was + invented together by Abramov, Bronstein and Petkovsek. + + It is possible to generalize the algorithm implemented here to + the case of linear q-difference and differential equations. + + Lets say that we would like to compute `m`-th Bernoulli polynomial + up to a constant. For this we can use `b(n+1) - b(n) = m n^{m-1}` + recurrence, which has solution `b(n) = B_m + C`. For example: + + >>> from sympy import Symbol, rsolve_poly + >>> n = Symbol('n', integer=True) + + >>> rsolve_poly([-1, 1], 4*n**3, n) + C0 + n**4 - 2*n**3 + n**2 + + References + ========== + + .. [1] S. A. Abramov, M. Bronstein and M. Petkovsek, On polynomial + solutions of linear operator equations, in: T. Levelt, ed., + Proc. ISSAC '95, ACM Press, New York, 1995, 290-296. + + .. [2] M. Petkovsek, Hypergeometric solutions of linear recurrences + with polynomial coefficients, J. Symbolic Computation, + 14 (1992), 243-264. + + .. [3] M. Petkovsek, H. S. Wilf, D. Zeilberger, A = B, 1996. + + """ + f = sympify(f) + + if not f.is_polynomial(n): + return None + + homogeneous = f.is_zero + + r = len(coeffs) - 1 + + coeffs = [Poly(coeff, n) for coeff in coeffs] + + polys = [Poly(0, n)]*(r + 1) + terms = [(S.Zero, S.NegativeInfinity)]*(r + 1) + + for i in range(r + 1): + for j in range(i, r + 1): + polys[i] += coeffs[j]*(binomial(j, i).as_poly(n)) + + if not polys[i].is_zero: + (exp,), coeff = polys[i].LT() + terms[i] = (coeff, exp) + + d = b = terms[0][1] + + for i in range(1, r + 1): + if terms[i][1] > d: + d = terms[i][1] + + if terms[i][1] - i > b: + b = terms[i][1] - i + + d, b = int(d), int(b) + + x = Dummy('x') + + degree_poly = S.Zero + + for i in range(r + 1): + if terms[i][1] - i == b: + degree_poly += terms[i][0]*FallingFactorial(x, i) + + nni_roots = list(roots(degree_poly, x, filter='Z', + predicate=lambda r: r >= 0).keys()) + + if nni_roots: + N = [max(nni_roots)] + else: + N = [] + + if homogeneous: + N += [-b - 1] + else: + N += [f.as_poly(n).degree() - b, -b - 1] + + N = int(max(N)) + + if N < 0: + if homogeneous: + if hints.get('symbols', False): + return (S.Zero, []) + else: + return S.Zero + else: + return None + + if N <= r: + C = [] + y = E = S.Zero + + for i in range(N + 1): + C.append(Symbol('C' + str(i + shift))) + y += C[i] * n**i + + for i in range(r + 1): + E += coeffs[i].as_expr()*y.subs(n, n + i) + + solutions = solve_undetermined_coeffs(E - f, C, n) + + if solutions is not None: + _C = C + C = [c for c in C if (c not in solutions)] + result = y.subs(solutions) + else: + return None # TBD + else: + A = r + U = N + A + b + 1 + + nni_roots = list(roots(polys[r], filter='Z', + predicate=lambda r: r >= 0).keys()) + + if nni_roots != []: + a = max(nni_roots) + 1 + else: + a = S.Zero + + def _zero_vector(k): + return [S.Zero] * k + + def _one_vector(k): + return [S.One] * k + + def _delta(p, k): + B = S.One + D = p.subs(n, a + k) + + for i in range(1, k + 1): + B *= Rational(i - k - 1, i) + D += B * p.subs(n, a + k - i) + + return D + + alpha = {} + + for i in range(-A, d + 1): + I = _one_vector(d + 1) + + for k in range(1, d + 1): + I[k] = I[k - 1] * (x + i - k + 1)/k + + alpha[i] = S.Zero + + for j in range(A + 1): + for k in range(d + 1): + B = binomial(k, i + j) + D = _delta(polys[j].as_expr(), k) + + alpha[i] += I[k]*B*D + + V = Matrix(U, A, lambda i, j: int(i == j)) + + if homogeneous: + for i in range(A, U): + v = _zero_vector(A) + + for k in range(1, A + b + 1): + if i - k < 0: + break + + B = alpha[k - A].subs(x, i - k) + + for j in range(A): + v[j] += B * V[i - k, j] + + denom = alpha[-A].subs(x, i) + + for j in range(A): + V[i, j] = -v[j] / denom + else: + G = _zero_vector(U) + + for i in range(A, U): + v = _zero_vector(A) + g = S.Zero + + for k in range(1, A + b + 1): + if i - k < 0: + break + + B = alpha[k - A].subs(x, i - k) + + for j in range(A): + v[j] += B * V[i - k, j] + + g += B * G[i - k] + + denom = alpha[-A].subs(x, i) + + for j in range(A): + V[i, j] = -v[j] / denom + + G[i] = (_delta(f, i - A) - g) / denom + + P, Q = _one_vector(U), _zero_vector(A) + + for i in range(1, U): + P[i] = (P[i - 1] * (n - a - i + 1)/i).expand() + + for i in range(A): + Q[i] = Add(*[(v*p).expand() for v, p in zip(V[:, i], P)]) + + if not homogeneous: + h = Add(*[(g*p).expand() for g, p in zip(G, P)]) + + C = [Symbol('C' + str(i + shift)) for i in range(A)] + + g = lambda i: Add(*[c*_delta(q, i) for c, q in zip(C, Q)]) + + if homogeneous: + E = [g(i) for i in range(N + 1, U)] + else: + E = [g(i) + _delta(h, i) for i in range(N + 1, U)] + + if E != []: + solutions = solve(E, *C) + + if not solutions: + if homogeneous: + if hints.get('symbols', False): + return (S.Zero, []) + else: + return S.Zero + else: + return None + else: + solutions = {} + + if homogeneous: + result = S.Zero + else: + result = h + + _C = C[:] + for c, q in list(zip(C, Q)): + if c in solutions: + s = solutions[c]*q + C.remove(c) + else: + s = c*q + + result += s.expand() + + if C != _C: + # renumber so they are contiguous + result = result.xreplace(dict(zip(C, _C))) + C = _C[:len(C)] + + if hints.get('symbols', False): + return (result, C) + else: + return result + + +def rsolve_ratio(coeffs, f, n, **hints): + r""" + Given linear recurrence operator `\operatorname{L}` of order `k` + with polynomial coefficients and inhomogeneous equation + `\operatorname{L} y = f`, where `f` is a polynomial, we seek + for all rational solutions over field `K` of characteristic zero. + + This procedure accepts only polynomials, however if you are + interested in solving recurrence with rational coefficients + then use ``rsolve`` which will pre-process the given equation + and run this procedure with polynomial arguments. + + The algorithm performs two basic steps: + + (1) Compute polynomial `v(n)` which can be used as universal + denominator of any rational solution of equation + `\operatorname{L} y = f`. + + (2) Construct new linear difference equation by substitution + `y(n) = u(n)/v(n)` and solve it for `u(n)` finding all its + polynomial solutions. Return ``None`` if none were found. + + The algorithm implemented here is a revised version of the original + Abramov's algorithm, developed in 1989. The new approach is much + simpler to implement and has better overall efficiency. This + method can be easily adapted to the q-difference equations case. + + Besides finding rational solutions alone, this functions is + an important part of Hyper algorithm where it is used to find + a particular solution for the inhomogeneous part of a recurrence. + + Examples + ======== + + >>> from sympy.abc import x + >>> from sympy.solvers.recurr import rsolve_ratio + >>> rsolve_ratio([-2*x**3 + x**2 + 2*x - 1, 2*x**3 + x**2 - 6*x, + ... - 2*x**3 - 11*x**2 - 18*x - 9, 2*x**3 + 13*x**2 + 22*x + 8], 0, x) + C0*(2*x - 3)/(2*(x**2 - 1)) + + References + ========== + + .. [1] S. A. Abramov, Rational solutions of linear difference + and q-difference equations with polynomial coefficients, + in: T. Levelt, ed., Proc. ISSAC '95, ACM Press, New York, + 1995, 285-289 + + See Also + ======== + + rsolve_hyper + """ + f = sympify(f) + + if not f.is_polynomial(n): + return None + + coeffs = list(map(sympify, coeffs)) + + r = len(coeffs) - 1 + + A, B = coeffs[r], coeffs[0] + A = A.subs(n, n - r).expand() + + h = Dummy('h') + + res = resultant(A, B.subs(n, n + h), n) + + if not res.is_polynomial(h): + p, q = res.as_numer_denom() + res = quo(p, q, h) + + nni_roots = list(roots(res, h, filter='Z', + predicate=lambda r: r >= 0).keys()) + + if not nni_roots: + return rsolve_poly(coeffs, f, n, **hints) + else: + C, numers = S.One, [S.Zero]*(r + 1) + + for i in range(int(max(nni_roots)), -1, -1): + d = gcd(A, B.subs(n, n + i), n) + + A = quo(A, d, n) + B = quo(B, d.subs(n, n - i), n) + + C *= Mul(*[d.subs(n, n - j) for j in range(i + 1)]) + + denoms = [C.subs(n, n + i) for i in range(r + 1)] + + for i in range(r + 1): + g = gcd(coeffs[i], denoms[i], n) + + numers[i] = quo(coeffs[i], g, n) + denoms[i] = quo(denoms[i], g, n) + + for i in range(r + 1): + numers[i] *= Mul(*(denoms[:i] + denoms[i + 1:])) + + result = rsolve_poly(numers, f * Mul(*denoms), n, **hints) + + if result is not None: + if hints.get('symbols', False): + return (simplify(result[0] / C), result[1]) + else: + return simplify(result / C) + else: + return None + + +def rsolve_hyper(coeffs, f, n, **hints): + r""" + Given linear recurrence operator `\operatorname{L}` of order `k` + with polynomial coefficients and inhomogeneous equation + `\operatorname{L} y = f` we seek for all hypergeometric solutions + over field `K` of characteristic zero. + + The inhomogeneous part can be either hypergeometric or a sum + of a fixed number of pairwise dissimilar hypergeometric terms. + + The algorithm performs three basic steps: + + (1) Group together similar hypergeometric terms in the + inhomogeneous part of `\operatorname{L} y = f`, and find + particular solution using Abramov's algorithm. + + (2) Compute generating set of `\operatorname{L}` and find basis + in it, so that all solutions are linearly independent. + + (3) Form final solution with the number of arbitrary + constants equal to dimension of basis of `\operatorname{L}`. + + Term `a(n)` is hypergeometric if it is annihilated by first order + linear difference equations with polynomial coefficients or, in + simpler words, if consecutive term ratio is a rational function. + + The output of this procedure is a linear combination of fixed + number of hypergeometric terms. However the underlying method + can generate larger class of solutions - D'Alembertian terms. + + Note also that this method not only computes the kernel of the + inhomogeneous equation, but also reduces in to a basis so that + solutions generated by this procedure are linearly independent + + Examples + ======== + + >>> from sympy.solvers import rsolve_hyper + >>> from sympy.abc import x + + >>> rsolve_hyper([-1, -1, 1], 0, x) + C0*(1/2 - sqrt(5)/2)**x + C1*(1/2 + sqrt(5)/2)**x + + >>> rsolve_hyper([-1, 1], 1 + x, x) + C0 + x*(x + 1)/2 + + References + ========== + + .. [1] M. Petkovsek, Hypergeometric solutions of linear recurrences + with polynomial coefficients, J. Symbolic Computation, + 14 (1992), 243-264. + + .. [2] M. Petkovsek, H. S. Wilf, D. Zeilberger, A = B, 1996. + """ + coeffs = list(map(sympify, coeffs)) + + f = sympify(f) + + r, kernel, symbols = len(coeffs) - 1, [], set() + + if not f.is_zero: + if f.is_Add: + similar = {} + + for g in f.expand().args: + if not g.is_hypergeometric(n): + return None + + for h in similar.keys(): + if hypersimilar(g, h, n): + similar[h] += g + break + else: + similar[g] = S.Zero + + inhomogeneous = [g + h for g, h in similar.items()] + elif f.is_hypergeometric(n): + inhomogeneous = [f] + else: + return None + + for i, g in enumerate(inhomogeneous): + coeff, polys = S.One, coeffs[:] + denoms = [S.One]*(r + 1) + + s = hypersimp(g, n) + + for j in range(1, r + 1): + coeff *= s.subs(n, n + j - 1) + + p, q = coeff.as_numer_denom() + + polys[j] *= p + denoms[j] = q + + for j in range(r + 1): + polys[j] *= Mul(*(denoms[:j] + denoms[j + 1:])) + + # FIXME: The call to rsolve_ratio below should suffice (rsolve_poly + # call can be removed) but the XFAIL test_rsolve_ratio_missed must + # be fixed first. + R = rsolve_ratio(polys, Mul(*denoms), n, symbols=True) + if R is not None: + R, syms = R + if syms: + R = R.subs(zip(syms, [0]*len(syms))) + else: + R = rsolve_poly(polys, Mul(*denoms), n) + + if R: + inhomogeneous[i] *= R + else: + return None + + result = Add(*inhomogeneous) + result = simplify(result) + else: + result = S.Zero + + Z = Dummy('Z') + + p, q = coeffs[0], coeffs[r].subs(n, n - r + 1) + + p_factors = list(roots(p, n).keys()) + q_factors = list(roots(q, n).keys()) + + factors = [(S.One, S.One)] + + for p in p_factors: + for q in q_factors: + if p.is_integer and q.is_integer and p <= q: + continue + else: + factors += [(n - p, n - q)] + + p = [(n - p, S.One) for p in p_factors] + q = [(S.One, n - q) for q in q_factors] + + factors = p + factors + q + + for A, B in factors: + polys, degrees = [], [] + D = A*B.subs(n, n + r - 1) + + for i in range(r + 1): + a = Mul(*[A.subs(n, n + j) for j in range(i)]) + b = Mul(*[B.subs(n, n + j) for j in range(i, r)]) + + poly = quo(coeffs[i]*a*b, D, n) + polys.append(poly.as_poly(n)) + + if not poly.is_zero: + degrees.append(polys[i].degree()) + + if degrees: + d, poly = max(degrees), S.Zero + else: + return None + + for i in range(r + 1): + coeff = polys[i].nth(d) + + if coeff is not S.Zero: + poly += coeff * Z**i + + for z in roots(poly, Z).keys(): + if z.is_zero: + continue + + recurr_coeffs = [polys[i].as_expr()*z**i for i in range(r + 1)] + if d == 0 and 0 != Add(*[recurr_coeffs[j]*j for j in range(1, r + 1)]): + # faster inline check (than calling rsolve_poly) for a + # constant solution to a constant coefficient recurrence. + sol = [Symbol("C" + str(len(symbols)))] + else: + sol, syms = rsolve_poly(recurr_coeffs, 0, n, len(symbols), symbols=True) + sol = sol.collect(syms) + sol = [sol.coeff(s) for s in syms] + + for C in sol: + ratio = z * A * C.subs(n, n + 1) / B / C + ratio = simplify(ratio) + # If there is a nonnegative root in the denominator of the ratio, + # this indicates that the term y(n_root) is zero, and one should + # start the product with the term y(n_root + 1). + n0 = 0 + for n_root in roots(ratio.as_numer_denom()[1], n).keys(): + if n_root.has(I): + return None + elif (n0 < (n_root + 1)) == True: + n0 = n_root + 1 + K = product(ratio, (n, n0, n - 1)) + if K.has(factorial, FallingFactorial, RisingFactorial): + K = simplify(K) + + if casoratian(kernel + [K], n, zero=False) != 0: + kernel.append(K) + + kernel.sort(key=default_sort_key) + sk = list(zip(numbered_symbols('C'), kernel)) + + for C, ker in sk: + result += C * ker + + if hints.get('symbols', False): + # XXX: This returns the symbols in a non-deterministic order + symbols |= {s for s, k in sk} + return (result, list(symbols)) + else: + return result + + +def rsolve(f, y, init=None): + r""" + Solve univariate recurrence with rational coefficients. + + Given `k`-th order linear recurrence `\operatorname{L} y = f`, + or equivalently: + + .. math:: a_{k}(n) y(n+k) + a_{k-1}(n) y(n+k-1) + + \cdots + a_{0}(n) y(n) = f(n) + + where `a_{i}(n)`, for `i=0, \ldots, k`, are polynomials or rational + functions in `n`, and `f` is a hypergeometric function or a sum + of a fixed number of pairwise dissimilar hypergeometric terms in + `n`, finds all solutions or returns ``None``, if none were found. + + Initial conditions can be given as a dictionary in two forms: + + (1) ``{ n_0 : v_0, n_1 : v_1, ..., n_m : v_m}`` + (2) ``{y(n_0) : v_0, y(n_1) : v_1, ..., y(n_m) : v_m}`` + + or as a list ``L`` of values: + + ``L = [v_0, v_1, ..., v_m]`` + + where ``L[i] = v_i``, for `i=0, \ldots, m`, maps to `y(n_i)`. + + Examples + ======== + + Lets consider the following recurrence: + + .. math:: (n - 1) y(n + 2) - (n^2 + 3 n - 2) y(n + 1) + + 2 n (n + 1) y(n) = 0 + + >>> from sympy import Function, rsolve + >>> from sympy.abc import n + >>> y = Function('y') + + >>> f = (n - 1)*y(n + 2) - (n**2 + 3*n - 2)*y(n + 1) + 2*n*(n + 1)*y(n) + + >>> rsolve(f, y(n)) + 2**n*C0 + C1*factorial(n) + + >>> rsolve(f, y(n), {y(0):0, y(1):3}) + 3*2**n - 3*factorial(n) + + See Also + ======== + + rsolve_poly, rsolve_ratio, rsolve_hyper + + """ + if isinstance(f, Equality): + f = f.lhs - f.rhs + + n = y.args[0] + k = Wild('k', exclude=(n,)) + + # Preprocess user input to allow things like + # y(n) + a*(y(n + 1) + y(n - 1))/2 + f = f.expand().collect(y.func(Wild('m', integer=True))) + + h_part = defaultdict(list) + i_part = [] + for g in Add.make_args(f): + coeff, dep = g.as_coeff_mul(y.func) + if not dep: + i_part.append(coeff) + continue + for h in dep: + if h.is_Function and h.func == y.func: + result = h.args[0].match(n + k) + if result is not None: + h_part[int(result[k])].append(coeff) + continue + raise ValueError( + "'%s(%s + k)' expected, got '%s'" % (y.func, n, h)) + for k in h_part: + h_part[k] = Add(*h_part[k]) + h_part.default_factory = lambda: 0 + i_part = Add(*i_part) + + for k, coeff in h_part.items(): + h_part[k] = simplify(coeff) + + common = S.One + + if not i_part.is_zero and not i_part.is_hypergeometric(n) and \ + not (i_part.is_Add and all((x.is_hypergeometric(n) for x in i_part.expand().args))): + raise ValueError("The independent term should be a sum of hypergeometric functions, got '%s'" % i_part) + + for coeff in h_part.values(): + if coeff.is_rational_function(n): + if not coeff.is_polynomial(n): + common = lcm(common, coeff.as_numer_denom()[1], n) + else: + raise ValueError( + "Polynomial or rational function expected, got '%s'" % coeff) + + i_numer, i_denom = i_part.as_numer_denom() + + if i_denom.is_polynomial(n): + common = lcm(common, i_denom, n) + + if common is not S.One: + for k, coeff in h_part.items(): + numer, denom = coeff.as_numer_denom() + h_part[k] = numer*quo(common, denom, n) + + i_part = i_numer*quo(common, i_denom, n) + + K_min = min(h_part.keys()) + + if K_min < 0: + K = abs(K_min) + + H_part = defaultdict(lambda: S.Zero) + i_part = i_part.subs(n, n + K).expand() + common = common.subs(n, n + K).expand() + + for k, coeff in h_part.items(): + H_part[k + K] = coeff.subs(n, n + K).expand() + else: + H_part = h_part + + K_max = max(H_part.keys()) + coeffs = [H_part[i] for i in range(K_max + 1)] + + result = rsolve_hyper(coeffs, -i_part, n, symbols=True) + + if result is None: + return None + + solution, symbols = result + + if init in ({}, []): + init = None + + if symbols and init is not None: + if isinstance(init, list): + init = {i: init[i] for i in range(len(init))} + + equations = [] + + for k, v in init.items(): + try: + i = int(k) + except TypeError: + if k.is_Function and k.func == y.func: + i = int(k.args[0]) + else: + raise ValueError("Integer or term expected, got '%s'" % k) + + eq = solution.subs(n, i) - v + if eq.has(S.NaN): + eq = solution.limit(n, i) - v + equations.append(eq) + + result = solve(equations, *symbols) + + if not result: + return None + else: + solution = solution.subs(result) + + return solution diff --git a/lib/python3.12/site-packages/sympy/solvers/simplex.py b/lib/python3.12/site-packages/sympy/solvers/simplex.py new file mode 100644 index 0000000000000000000000000000000000000000..f3940844c8e5eaa5aa2cfb38be88c0cbb170c5e5 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/solvers/simplex.py @@ -0,0 +1,1141 @@ +"""Tools for optimizing a linear function for a given simplex. + +For the linear objective function ``f`` with linear constraints +expressed using `Le`, `Ge` or `Eq` can be found with ``lpmin`` or +``lpmax``. The symbols are **unbounded** unless specifically +constrained. + +As an alternative, the matrices describing the objective and the +constraints, and an optional list of bounds can be passed to +``linprog`` which will solve for the minimization of ``C*x`` +under constraints ``A*x <= b`` and/or ``Aeq*x = beq``, and +individual bounds for variables given as ``(lo, hi)``. The values +returned are **nonnegative** unless bounds are provided that +indicate otherwise. + +Errors that might be raised are UnboundedLPError when there is no +finite solution for the system or InfeasibleLPError when the +constraints represent impossible conditions (i.e. a non-existant + simplex). + +Here is a simple 1-D system: minimize `x` given that ``x >= 1``. + + >>> from sympy.solvers.simplex import lpmin, linprog + >>> from sympy.abc import x + + The function and a list with the constraint is passed directly + to `lpmin`: + + >>> lpmin(x, [x >= 1]) + (1, {x: 1}) + + For `linprog` the matrix for the objective is `[1]` and the + uivariate constraint can be passed as a bound with None acting + as infinity: + + >>> linprog([1], bounds=(1, None)) + (1, [1]) + + Or the matrices, corresponding to ``x >= 1`` expressed as + ``-x <= -1`` as required by the routine, can be passed: + + >>> linprog([1], [-1], [-1]) + (1, [1]) + + If there is no limit for the objective, an error is raised. + In this case there is a valid region of interest (simplex) + but no limit to how small ``x`` can be: + + >>> lpmin(x, []) + Traceback (most recent call last): + ... + sympy.solvers.simplex.UnboundedLPError: + Objective function can assume arbitrarily large values! + + An error is raised if there is no possible solution: + + >>> lpmin(x,[x<=1,x>=2]) + Traceback (most recent call last): + ... + sympy.solvers.simplex.InfeasibleLPError: + Inconsistent/False constraint +""" + +from sympy.core import sympify +from sympy.core.exprtools import factor_terms +from sympy.core.relational import Le, Ge, Eq +from sympy.core.singleton import S +from sympy.core.symbol import Dummy +from sympy.core.sorting import ordered +from sympy.functions.elementary.complexes import sign +from sympy.matrices.dense import Matrix, zeros +from sympy.solvers.solveset import linear_eq_to_matrix +from sympy.utilities.iterables import numbered_symbols +from sympy.utilities.misc import filldedent + + +class UnboundedLPError(Exception): + """ + A linear programing problem is said to be unbounded if its objective + function can assume arbitrarily large values. + + Example + ======= + + Suppose you want to maximize + 2x + subject to + x >= 0 + + There's no upper limit that 2x can take. + """ + + pass + + +class InfeasibleLPError(Exception): + """ + A linear programing problem is considered infeasible if its + constraint set is empty. That is, if the set of all vectors + satisfying the contraints is empty, then the problem is infeasible. + + Example + ======= + + Suppose you want to maximize + x + subject to + x >= 10 + x <= 9 + + No x can satisfy those constraints. + """ + + pass + + +def _pivot(M, i, j): + """ + The pivot element `M[i, j]` is inverted and the rest of the matrix + modified and returned as a new matrix; original is left unmodified. + + Example + ======= + + >>> from sympy.matrices.dense import Matrix + >>> from sympy.solvers.simplex import _pivot + >>> from sympy import var + >>> Matrix(3, 3, var('a:i')) + Matrix([ + [a, b, c], + [d, e, f], + [g, h, i]]) + >>> _pivot(_, 1, 0) + Matrix([ + [-a/d, -a*e/d + b, -a*f/d + c], + [ 1/d, e/d, f/d], + [-g/d, h - e*g/d, i - f*g/d]]) + """ + Mi, Mj, Mij = M[i, :], M[:, j], M[i, j] + if Mij == 0: + raise ZeroDivisionError( + "Tried to pivot about zero-valued entry.") + A = M - Mj * (Mi / Mij) + A[i, :] = Mi / Mij + A[:, j] = -Mj / Mij + A[i, j] = 1 / Mij + return A + + +def _choose_pivot_row(A, B, candidate_rows, pivot_col, Y): + # Choose row with smallest ratio + # If there are ties, pick using Bland's rule + return min(candidate_rows, key=lambda i: (B[i] / A[i, pivot_col], Y[i])) + + +def _simplex(A, B, C, D=None, dual=False): + """Return ``(o, x, y)`` obtained from the two-phase simplex method + using Bland's rule: ``o`` is the minimum value of primal, + ``Cx - D``, under constraints ``Ax <= B`` (with ``x >= 0``) and + the maximum of the dual, ``y^{T}B - D``, under constraints + ``A^{T}*y >= C^{T}`` (with ``y >= 0``). To compute the dual of + the system, pass `dual=True` and ``(o, y, x)`` will be returned. + + Note: the nonnegative constraints for ``x`` and ``y`` supercede + any values of ``A`` and ``B`` that are inconsistent with that + assumption, so if a constraint of ``x >= -1`` is represented + in ``A`` and ``B``, no value will be obtained that is negative; if + a constraint of ``x <= -1`` is represented, an error will be + raised since no solution is possible. + + This routine relies on the ability of determining whether an + expression is 0 or not. This is guaranteed if the input contains + only Float or Rational entries. It will raise a TypeError if + a relationship does not evaluate to True or False. + + Examples + ======== + + >>> from sympy.solvers.simplex import _simplex + >>> from sympy import Matrix + + Consider the simple minimization of ``f = x + y + 1`` under the + constraint that ``y + 2*x >= 4``. This is the "standard form" of + a minimization. + + In the nonnegative quadrant, this inequality describes a area above + a triangle with vertices at (0, 4), (0, 0) and (2, 0). The minimum + of ``f`` occurs at (2, 0). Define A, B, C, D for the standard + minimization: + + >>> A = Matrix([[2, 1]]) + >>> B = Matrix([4]) + >>> C = Matrix([[1, 1]]) + >>> D = Matrix([-1]) + + Confirm that this is the system of interest: + + >>> from sympy.abc import x, y + >>> X = Matrix([x, y]) + >>> (C*X - D)[0] + x + y + 1 + >>> [i >= j for i, j in zip(A*X, B)] + [2*x + y >= 4] + + Since `_simplex` will do a minimization for constraints given as + ``A*x <= B``, the signs of ``A`` and ``B`` must be negated since + the currently correspond to a greater-than inequality: + + >>> _simplex(-A, -B, C, D) + (3, [2, 0], [1/2]) + + The dual of minimizing ``f`` is maximizing ``F = c*y - d`` for + ``a*y <= b`` where ``a``, ``b``, ``c``, ``d`` are derived from the + transpose of the matrix representation of the standard minimization: + + >>> tr = lambda a, b, c, d: [i.T for i in (a, c, b, d)] + >>> a, b, c, d = tr(A, B, C, D) + + This time ``a*x <= b`` is the expected inequality for the `_simplex` + method, but to maximize ``F``, the sign of ``c`` and ``d`` must be + changed (so that minimizing the negative will give the negative of + the maximum of ``F``): + + >>> _simplex(a, b, -c, -d) + (-3, [1/2], [2, 0]) + + The negative of ``F`` and the min of ``f`` are the same. The dual + point `[1/2]` is the value of ``y`` that minimized ``F = c*y - d`` + under constraints a*x <= b``: + + >>> y = Matrix(['y']) + >>> (c*y - d)[0] + 4*y + 1 + >>> [i <= j for i, j in zip(a*y,b)] + [2*y <= 1, y <= 1] + + In this 1-dimensional dual system, the more restrictive contraint is + the first which limits ``y`` between 0 and 1/2 and the maximum of + ``F`` is attained at the nonzero value, hence is ``4*(1/2) + 1 = 3``. + + In this case the values for ``x`` and ``y`` were the same when the + dual representation was solved. This is not always the case (though + the value of the function will be the same). + + >>> l = [[1, 1], [-1, 1], [0, 1], [-1, 0]], [5, 1, 2, -1], [[1, 1]], [-1] + >>> A, B, C, D = [Matrix(i) for i in l] + >>> _simplex(A, B, -C, -D) + (-6, [3, 2], [1, 0, 0, 0]) + >>> _simplex(A, B, -C, -D, dual=True) # [5, 0] != [3, 2] + (-6, [1, 0, 0, 0], [5, 0]) + + In both cases the function has the same value: + + >>> Matrix(C)*Matrix([3, 2]) == Matrix(C)*Matrix([5, 0]) + True + + See Also + ======== + _lp - poses min/max problem in form compatible with _simplex + lpmin - minimization which calls _lp + lpmax - maximimzation which calls _lp + + References + ========== + + .. [1] Thomas S. Ferguson, LINEAR PROGRAMMING: A Concise Introduction + web.tecnico.ulisboa.pt/mcasquilho/acad/or/ftp/FergusonUCLA_lp.pdf + + """ + A, B, C, D = [Matrix(i) for i in (A, B, C, D or [0])] + if dual: + _o, d, p = _simplex(-A.T, C.T, B.T, -D) + return -_o, d, p + + if A and B: + M = Matrix([[A, B], [C, D]]) + else: + if A or B: + raise ValueError("must give A and B") + # no constraints given + M = Matrix([[C, D]]) + n = M.cols - 1 + m = M.rows - 1 + + if not all(i.is_Float or i.is_Rational for i in M): + # with literal Float and Rational we are guaranteed the + # ability of determining whether an expression is 0 or not + raise TypeError(filldedent(""" + Only rationals and floats are allowed. + """ + ) + ) + + # x variables have priority over y variables during Bland's rule + # since False < True + X = [(False, j) for j in range(n)] + Y = [(True, i) for i in range(m)] + + # Phase 1: find a feasible solution or determine none exist + + ## keep track of last pivot row and column + last = None + + while True: + B = M[:-1, -1] + A = M[:-1, :-1] + if all(B[i] >= 0 for i in range(B.rows)): + # We have found a feasible solution + break + + # Find k: first row with a negative rightmost entry + for k in range(B.rows): + if B[k] < 0: + break # use current value of k below + else: + pass # error will raise below + + # Choose pivot column, c + piv_cols = [_ for _ in range(A.cols) if A[k, _] < 0] + if not piv_cols: + raise InfeasibleLPError(filldedent(""" + The constraint set is empty!""")) + _, c = min((X[i], i) for i in piv_cols) # Bland's rule + + # Choose pivot row, r + piv_rows = [_ for _ in range(A.rows) if A[_, c] > 0 and B[_] > 0] + piv_rows.append(k) + r = _choose_pivot_row(A, B, piv_rows, c, Y) + + # check for oscillation + if (r, c) == last: + # Not sure what to do here; it looks like there will be + # oscillations; see o1 test added at this commit to + # see a system with no solution and the o2 for one + # with a solution. In the case of o2, the solution + # from linprog is the same as the one from lpmin, but + # the matrices created in the lpmin case are different + # than those created without replacements in linprog and + # the matrices in the linprog case lead to oscillations. + # If the matrices could be re-written in linprog like + # lpmin does, this behavior could be avoided and then + # perhaps the oscillating case would only occur when + # there is no solution. For now, the output is checked + # before exit if oscillations were detected and an + # error is raised there if the solution was invalid. + # + # cf section 6 of Ferguson for a non-cycling modification + last = True + break + last = r, c + + M = _pivot(M, r, c) + X[c], Y[r] = Y[r], X[c] + + # Phase 2: from a feasible solution, pivot to optimal + while True: + B = M[:-1, -1] + A = M[:-1, :-1] + C = M[-1, :-1] + + # Choose a pivot column, c + piv_cols = [] + piv_cols = [_ for _ in range(n) if C[_] < 0] + if not piv_cols: + break + _, c = min((X[i], i) for i in piv_cols) # Bland's rule + + # Choose a pivot row, r + piv_rows = [_ for _ in range(m) if A[_, c] > 0] + if not piv_rows: + raise UnboundedLPError(filldedent(""" + Objective function can assume + arbitrarily large values!""")) + r = _choose_pivot_row(A, B, piv_rows, c, Y) + + M = _pivot(M, r, c) + X[c], Y[r] = Y[r], X[c] + + argmax = [None] * n + argmin_dual = [None] * m + + for i, (v, n) in enumerate(X): + if v == False: + argmax[n] = 0 + else: + argmin_dual[n] = M[-1, i] + + for i, (v, n) in enumerate(Y): + if v == True: + argmin_dual[n] = 0 + else: + argmax[n] = M[i, -1] + + if last and not all(i >= 0 for i in argmax + argmin_dual): + raise InfeasibleLPError(filldedent(""" + Oscillating system led to invalid solution. + If you believe there was a valid solution, please + report this as a bug.""")) + return -M[-1, -1], argmax, argmin_dual + + +## routines that use _simplex or support those that do + + +def _abcd(M, list=False): + """return parts of M as matrices or lists + + Examples + ======== + + >>> from sympy import Matrix + >>> from sympy.solvers.simplex import _abcd + + >>> m = Matrix(3, 3, range(9)); m + Matrix([ + [0, 1, 2], + [3, 4, 5], + [6, 7, 8]]) + >>> a, b, c, d = _abcd(m) + >>> a + Matrix([ + [0, 1], + [3, 4]]) + >>> b + Matrix([ + [2], + [5]]) + >>> c + Matrix([[6, 7]]) + >>> d + Matrix([[8]]) + + The matrices can be returned as compact lists, too: + + >>> L = a, b, c, d = _abcd(m, list=True); L + ([[0, 1], [3, 4]], [2, 5], [[6, 7]], [8]) + """ + + def aslist(i): + l = i.tolist() + if len(l[0]) == 1: # col vector + return [i[0] for i in l] + return l + + m = M[:-1, :-1], M[:-1, -1], M[-1, :-1], M[-1:, -1:] + if not list: + return m + return tuple([aslist(i) for i in m]) + + +def _m(a, b, c, d=None): + """return Matrix([[a, b], [c, d]]) from matrices + in Matrix or list form. + + Examples + ======== + + >>> from sympy import Matrix + >>> from sympy.solvers.simplex import _abcd, _m + >>> m = Matrix(3, 3, range(9)) + >>> L = _abcd(m, list=True); L + ([[0, 1], [3, 4]], [2, 5], [[6, 7]], [8]) + >>> _abcd(m) + (Matrix([ + [0, 1], + [3, 4]]), Matrix([ + [2], + [5]]), Matrix([[6, 7]]), Matrix([[8]])) + >>> assert m == _m(*L) == _m(*_) + """ + a, b, c, d = [Matrix(i) for i in (a, b, c, d or [0])] + return Matrix([[a, b], [c, d]]) + + +def _primal_dual(M, factor=True): + """return primal and dual function and constraints + assuming that ``M = Matrix([[A, b], [c, d]])`` and the + function ``c*x - d`` is being minimized with ``Ax >= b`` + for nonnegative values of ``x``. The dual and its + constraints will be for maximizing `b.T*y - d` subject + to ``A.T*y <= c.T``. + + Examples + ======== + + >>> from sympy.solvers.simplex import _primal_dual, lpmin, lpmax + >>> from sympy import Matrix + + The following matrix represents the primal task of + minimizing x + y + 7 for y >= x + 1 and y >= -2*x + 3. + The dual task seeks to maximize x + 3*y + 7 with + 2*y - x <= 1 and and x + y <= 1: + + >>> M = Matrix([ + ... [-1, 1, 1], + ... [ 2, 1, 3], + ... [ 1, 1, -7]]) + >>> p, d = _primal_dual(M) + + The minimum of the primal and maximum of the dual are the same + (though they occur at different points): + + >>> lpmin(*p) + (28/3, {x1: 2/3, x2: 5/3}) + >>> lpmax(*d) + (28/3, {y1: 1/3, y2: 2/3}) + + If the equivalent (but canonical) inequalities are + desired, leave `factor=True`, otherwise the unmodified + inequalities for M will be returned. + + >>> m = Matrix([ + ... [-3, -2, 4, -2], + ... [ 2, 0, 0, -2], + ... [ 0, 1, -3, 0]]) + + >>> _primal_dual(m, False) # last condition is 2*x1 >= -2 + ((x2 - 3*x3, + [-3*x1 - 2*x2 + 4*x3 >= -2, 2*x1 >= -2]), + (-2*y1 - 2*y2, + [-3*y1 + 2*y2 <= 0, -2*y1 <= 1, 4*y1 <= -3])) + + >>> _primal_dual(m) # condition now x1 >= -1 + ((x2 - 3*x3, + [-3*x1 - 2*x2 + 4*x3 >= -2, x1 >= -1]), + (-2*y1 - 2*y2, + [-3*y1 + 2*y2 <= 0, -2*y1 <= 1, 4*y1 <= -3])) + + If you pass the transpose of the matrix, the primal will be + identified as the standard minimization problem and the + dual as the standard maximization: + + >>> _primal_dual(m.T) + ((-2*x1 - 2*x2, + [-3*x1 + 2*x2 >= 0, -2*x1 >= 1, 4*x1 >= -3]), + (y2 - 3*y3, + [-3*y1 - 2*y2 + 4*y3 <= -2, y1 <= -1])) + + A matrix must have some size or else None will be returned for + the functions: + + >>> _primal_dual(Matrix([[1, 2]])) + ((x1 - 2, []), (-2, [])) + + >>> _primal_dual(Matrix([])) + ((None, []), (None, [])) + + References + ========== + + .. [1] David Galvin, Relations between Primal and Dual + www3.nd.edu/~dgalvin1/30210/30210_F07/presentations/dual_opt.pdf + """ + if not M: + return (None, []), (None, []) + if not hasattr(M, "shape"): + if len(M) not in (3, 4): + raise ValueError("expecting Matrix or 3 or 4 lists") + M = _m(*M) + m, n = [i - 1 for i in M.shape] + A, b, c, d = _abcd(M) + d = d[0] + _ = lambda x: numbered_symbols(x, start=1) + x = Matrix([i for i, j in zip(_("x"), range(n))]) + yT = Matrix([i for i, j in zip(_("y"), range(m))]).T + + def ineq(L, r, op): + rv = [] + for r in (op(i, j) for i, j in zip(L, r)): + if r == True: + continue + elif r == False: + return [False] + if factor: + f = factor_terms(r) + if f.lhs.is_Mul and f.rhs % f.lhs.args[0] == 0: + assert len(f.lhs.args) == 2, f.lhs + k = f.lhs.args[0] + r = r.func(sign(k) * f.lhs.args[1], f.rhs // abs(k)) + rv.append(r) + return rv + + eq = lambda x, d: x[0] - d if x else -d + F = eq(c * x, d) + f = eq(yT * b, d) + return (F, ineq(A * x, b, Ge)), (f, ineq(yT * A, c, Le)) + + +def _rel_as_nonpos(constr, syms): + """return `(np, d, aux)` where `np` is a list of nonpositive + expressions that represent the given constraints (possibly + rewritten in terms of auxilliary variables) expressible with + nonnegative symbols, and `d` is a dictionary mapping a given + symbols to an expression with an auxilliary variable. In some + cases a symbol will be used as part of the change of variables, + e.g. x: x - z1 instead of x: z1 - z2. + + If any constraint is False/empty, return None. All variables in + ``constr`` are assumed to be unbounded unless explicitly indicated + otherwise with a univariate constraint, e.g. ``x >= 0`` will + restrict ``x`` to nonnegative values. + + The ``syms`` must be included so all symbols can be given an + unbounded assumption if they are not otherwise bound with + univariate conditions like ``x <= 3``. + + Examples + ======== + + >>> from sympy.solvers.simplex import _rel_as_nonpos + >>> from sympy.abc import x, y + >>> _rel_as_nonpos([x >= y, x >= 0, y >= 0], (x, y)) + ([-x + y], {}, []) + >>> _rel_as_nonpos([x >= 3, x <= 5], [x]) + ([_z1 - 2], {x: _z1 + 3}, [_z1]) + >>> _rel_as_nonpos([x <= 5], [x]) + ([], {x: 5 - _z1}, [_z1]) + >>> _rel_as_nonpos([x >= 1], [x]) + ([], {x: _z1 + 1}, [_z1]) + """ + r = {} # replacements to handle change of variables + np = [] # nonpositive expressions + aux = [] # auxilliary symbols added + ui = numbered_symbols("z", start=1, cls=Dummy) # auxilliary symbols + univariate = {} # {x: interval} for univariate constraints + unbound = [] # symbols designated as unbound + syms = set(syms) # the expected syms of the system + + # separate out univariates + for i in constr: + if i == True: + continue # ignore + if i == False: + return # no solution + if i.has(S.Infinity, S.NegativeInfinity): + raise ValueError("only finite bounds are permitted") + if isinstance(i, (Le, Ge)): + i = i.lts - i.gts + freei = i.free_symbols + if freei - syms: + raise ValueError( + "unexpected symbol(s) in constraint: %s" % (freei - syms) + ) + if len(freei) > 1: + np.append(i) + elif freei: + x = freei.pop() + if x in unbound: + continue # will handle later + ivl = Le(i, 0, evaluate=False).as_set() + if x not in univariate: + univariate[x] = ivl + else: + univariate[x] &= ivl + elif i: + return False + else: + raise TypeError(filldedent(""" + only equalities like Eq(x, y) or non-strict + inequalities like x >= y are allowed in lp, not %s""" % i)) + + # introduce auxilliary variables as needed for univariate + # inequalities + for x in syms: + i = univariate.get(x, True) + if not i: + return None # no solution possible + if i == True: + unbound.append(x) + continue + a, b = i.inf, i.sup + if a.is_infinite: + u = next(ui) + r[x] = b - u + aux.append(u) + elif b.is_infinite: + if a: + u = next(ui) + r[x] = a + u + aux.append(u) + else: + # standard nonnegative relationship + pass + else: + u = next(ui) + aux.append(u) + # shift so u = x - a => x = u + a + r[x] = u + a + # add constraint for u <= b - a + # since when u = b-a then x = u + a = b - a + a = b: + # the upper limit for x + np.append(u - (b - a)) + + # make change of variables for unbound variables + for x in unbound: + u = next(ui) + r[x] = u - x # reusing x + aux.append(u) + + return np, r, aux + + +def _lp_matrices(objective, constraints): + """return A, B, C, D, r, x+X, X for maximizing + objective = Cx - D with constraints Ax <= B, introducing + introducing auxilliary variables, X, as necessary to make + replacements of symbols as given in r, {xi: expression with Xj}, + so all variables in x+X will take on nonnegative values. + + Every univariate condition creates a semi-infinite + condition, e.g. a single ``x <= 3`` creates the + interval ``[-oo, 3]`` while ``x <= 3`` and ``x >= 2`` + create an interval ``[2, 3]``. Variables not in a univariate + expression will take on nonnegative values. + """ + + # sympify input and collect free symbols + F = sympify(objective) + np = [sympify(i) for i in constraints] + syms = set.union(*[i.free_symbols for i in [F] + np], set()) + + # change Eq(x, y) to x - y <= 0 and y - x <= 0 + for i in range(len(np)): + if isinstance(np[i], Eq): + np[i] = np[i].lhs - np[i].rhs <= 0 + np.append(-np[i].lhs <= 0) + + # convert constraints to nonpositive expressions + _ = _rel_as_nonpos(np, syms) + if _ is None: + raise InfeasibleLPError(filldedent(""" + Inconsistent/False constraint""")) + np, r, aux = _ + + # do change of variables + F = F.xreplace(r) + np = [i.xreplace(r) for i in np] + + # convert to matrices + xx = list(ordered(syms)) + aux + A, B = linear_eq_to_matrix(np, xx) + C, D = linear_eq_to_matrix([F], xx) + return A, B, C, D, r, xx, aux + + +def _lp(min_max, f, constr): + """Return the optimization (min or max) of ``f`` with the given + constraints. All variables are unbounded unless constrained. + + If `min_max` is 'max' then the results corresponding to the + maximization of ``f`` will be returned, else the minimization. + The constraints can be given as Le, Ge or Eq expressions. + + Examples + ======== + + >>> from sympy.solvers.simplex import _lp as lp + >>> from sympy import Eq + >>> from sympy.abc import x, y, z + >>> f = x + y - 2*z + >>> c = [7*x + 4*y - 7*z <= 3, 3*x - y + 10*z <= 6] + >>> c += [i >= 0 for i in (x, y, z)] + >>> lp(min, f, c) + (-6/5, {x: 0, y: 0, z: 3/5}) + + By passing max, the maximum value for f under the constraints + is returned (if possible): + + >>> lp(max, f, c) + (3/4, {x: 0, y: 3/4, z: 0}) + + Constraints that are equalities will require that the solution + also satisfy them: + + >>> lp(max, f, c + [Eq(y - 9*x, 1)]) + (5/7, {x: 0, y: 1, z: 1/7}) + + All symbols are reported, even if they are not in the objective + function: + + >>> lp(min, x, [y + x >= 3, x >= 0]) + (0, {x: 0, y: 3}) + """ + # get the matrix components for the system expressed + # in terms of only nonnegative variables + A, B, C, D, r, xx, aux = _lp_matrices(f, constr) + + how = str(min_max).lower() + if "max" in how: + # _simplex minimizes for Ax <= B so we + # have to change the sign of the function + # and negate the optimal value returned + _o, p, d = _simplex(A, B, -C, -D) + o = -_o + elif "min" in how: + o, p, d = _simplex(A, B, C, D) + else: + raise ValueError("expecting min or max") + + # restore original variables and remove aux from p + p = dict(zip(xx, p)) + if r: # p has original symbols and auxilliary symbols + # if r has x: x - z1 use values from p to update + r = {k: v.xreplace(p) for k, v in r.items()} + # then use the actual value of x (= x - z1) in p + p.update(r) + # don't show aux + p = {k: p[k] for k in ordered(p) if k not in aux} + + # not returning dual since there may be extra constraints + # when a variable has finite bounds + return o, p + + +def lpmin(f, constr): + """return minimum of linear equation ``f`` under + linear constraints expressed using Ge, Le or Eq. + + All variables are unbounded unless constrained. + + Examples + ======== + + >>> from sympy.solvers.simplex import lpmin + >>> from sympy import Eq + >>> from sympy.abc import x, y + >>> lpmin(x, [2*x - 3*y >= -1, Eq(x + 3*y, 2), x <= 2*y]) + (1/3, {x: 1/3, y: 5/9}) + + Negative values for variables are permitted unless explicitly + exluding, so minimizing ``x`` for ``x <= 3`` is an + unbounded problem while the following has a bounded solution: + + >>> lpmin(x, [x >= 0, x <= 3]) + (0, {x: 0}) + + Without indicating that ``x`` is nonnegative, there + is no minimum for this objective: + + >>> lpmin(x, [x <= 3]) + Traceback (most recent call last): + ... + sympy.solvers.simplex.UnboundedLPError: + Objective function can assume arbitrarily large values! + + See Also + ======== + linprog, lpmax + """ + return _lp(min, f, constr) + + +def lpmax(f, constr): + """return maximum of linear equation ``f`` under + linear constraints expressed using Ge, Le or Eq. + + All variables are unbounded unless constrained. + + Examples + ======== + + >>> from sympy.solvers.simplex import lpmax + >>> from sympy import Eq + >>> from sympy.abc import x, y + >>> lpmax(x, [2*x - 3*y >= -1, Eq(x+ 3*y,2), x <= 2*y]) + (4/5, {x: 4/5, y: 2/5}) + + Negative values for variables are permitted unless explicitly + exluding: + + >>> lpmax(x, [x <= -1]) + (-1, {x: -1}) + + If a non-negative constraint is added for x, there is no + possible solution: + + >>> lpmax(x, [x <= -1, x >= 0]) + Traceback (most recent call last): + ... + sympy.solvers.simplex.InfeasibleLPError: inconsistent/False constraint + + See Also + ======== + linprog, lpmin + """ + return _lp(max, f, constr) + + +def _handle_bounds(bounds): + # introduce auxilliary variables as needed for univariate + # inequalities + + unbound = [] + R = [0] * len(bounds) # a (growing) row of zeros + + def n(): + return len(R) - 1 + + def Arow(inc=1): + R.extend([0] * inc) + return R[:] + + row = [] + for x, (a, b) in enumerate(bounds): + if a is None and b is None: + unbound.append(x) + elif a is None: + # r[x] = b - u + A = Arow() + A[x] = 1 + A[n()] = 1 + B = [b] + row.append((A, B)) + A = [0] * len(A) + A[x] = -1 + A[n()] = -1 + B = [-b] + row.append((A, B)) + elif b is None: + if a: + # r[x] = a + u + A = Arow() + A[x] = 1 + A[n()] = -1 + B = [a] + row.append((A, B)) + A = [0] * len(A) + A[x] = -1 + A[n()] = 1 + B = [-a] + row.append((A, B)) + else: + # standard nonnegative relationship + pass + else: + # r[x] = u + a + A = Arow() + A[x] = 1 + A[n()] = -1 + B = [a] + row.append((A, B)) + A = [0] * len(A) + A[x] = -1 + A[n()] = 1 + B = [-a] + row.append((A, B)) + # u <= b - a + A = [0] * len(A) + A[x] = 0 + A[n()] = 1 + B = [b - a] + row.append((A, B)) + + # make change of variables for unbound variables + for x in unbound: + # r[x] = u - v + A = Arow(2) + B = [0] + A[x] = 1 + A[n()] = 1 + A[n() - 1] = -1 + row.append((A, B)) + A = [0] * len(A) + A[x] = -1 + A[n()] = -1 + A[n() - 1] = 1 + row.append((A, B)) + + return Matrix([r+[0]*(len(R) - len(r)) for r,_ in row] + ), Matrix([i[1] for i in row]) + + +def linprog(c, A=None, b=None, A_eq=None, b_eq=None, bounds=None): + """Return the minimization of ``c*x`` with the given + constraints ``A*x <= b`` and ``A_eq*x = b_eq``. Unless bounds + are given, variables will have nonnegative values in the solution. + + If ``A`` is not given, then the dimension of the system will + be determined by the length of ``C``. + + By default, all variables will be nonnegative. If ``bounds`` + is given as a single tuple, ``(lo, hi)``, then all variables + will be constrained to be between ``lo`` and ``hi``. Use + None for a ``lo`` or ``hi`` if it is unconstrained in the + negative or positive direction, respectively, e.g. + ``(None, 0)`` indicates nonpositive values. To set + individual ranges, pass a list with length equal to the + number of columns in ``A``, each element being a tuple; if + only a few variables take on non-default values they can be + passed as a dictionary with keys giving the corresponding + column to which the variable is assigned, e.g. ``bounds={2: + (1, 4)}`` would limit the 3rd variable to have a value in + range ``[1, 4]``. + + Examples + ======== + + >>> from sympy.solvers.simplex import linprog + >>> from sympy import symbols, Eq, linear_eq_to_matrix as M, Matrix + >>> x = x1, x2, x3, x4 = symbols('x1:5') + >>> X = Matrix(x) + >>> c, d = M(5*x2 + x3 + 4*x4 - x1, x) + >>> a, b = M([5*x2 + 2*x3 + 5*x4 - (x1 + 5)], x) + >>> aeq, beq = M([Eq(3*x2 + x4, 2), Eq(-x1 + x3 + 2*x4, 1)], x) + >>> constr = [i <= j for i,j in zip(a*X, b)] + >>> constr += [Eq(i, j) for i,j in zip(aeq*X, beq)] + >>> linprog(c, a, b, aeq, beq) + (9/2, [0, 1/2, 0, 1/2]) + >>> assert all(i.subs(dict(zip(x, _[1]))) for i in constr) + + See Also + ======== + lpmin, lpmax + """ + + ## the objective + C = Matrix(c) + if C.rows != 1 and C.cols == 1: + C = C.T + if C.rows != 1: + raise ValueError("C must be a single row.") + + ## the inequalities + if not A: + if b: + raise ValueError("A and b must both be given") + # the governing equations will be simple constraints + # on variables + A, b = zeros(0, C.cols), zeros(C.cols, 1) + else: + A, b = [Matrix(i) for i in (A, b)] + + if A.cols != C.cols: + raise ValueError("number of columns in A and C must match") + + ## the equalities + if A_eq is None: + if not b_eq is None: + raise ValueError("A_eq and b_eq must both be given") + else: + A_eq, b_eq = [Matrix(i) for i in (A_eq, b_eq)] + # if x == y then x <= y and x >= y (-x <= -y) + A = A.col_join(A_eq) + A = A.col_join(-A_eq) + b = b.col_join(b_eq) + b = b.col_join(-b_eq) + + if not (bounds is None or bounds == {} or bounds == (0, None)): + ## the bounds are interpreted + if type(bounds) is tuple and len(bounds) == 2: + bounds = [bounds] * A.cols + elif len(bounds) == A.cols and all( + type(i) is tuple and len(i) == 2 for i in bounds): + pass # individual bounds + elif type(bounds) is dict and all( + type(i) is tuple and len(i) == 2 + for i in bounds.values()): + # sparse bounds + db = bounds + bounds = [(0, None)] * A.cols + while db: + i, j = db.popitem() + bounds[i] = j # IndexError if out-of-bounds indices + else: + raise ValueError("unexpected bounds %s" % bounds) + A_, b_ = _handle_bounds(bounds) + aux = A_.cols - A.cols + if A: + A = Matrix([[A, zeros(A.rows, aux)], [A_]]) + b = b.col_join(b_) + else: + A = A_ + b = b_ + C = C.row_join(zeros(1, aux)) + else: + aux = -A.cols # set so -aux will give all cols below + + o, p, d = _simplex(A, b, C) + return o, p[:-aux] # don't include aux values + +def show_linprog(c, A=None, b=None, A_eq=None, b_eq=None, bounds=None): + from sympy import symbols + ## the objective + C = Matrix(c) + if C.rows != 1 and C.cols == 1: + C = C.T + if C.rows != 1: + raise ValueError("C must be a single row.") + + ## the inequalities + if not A: + if b: + raise ValueError("A and b must both be given") + # the governing equations will be simple constraints + # on variables + A, b = zeros(0, C.cols), zeros(C.cols, 1) + else: + A, b = [Matrix(i) for i in (A, b)] + + if A.cols != C.cols: + raise ValueError("number of columns in A and C must match") + + ## the equalities + if A_eq is None: + if not b_eq is None: + raise ValueError("A_eq and b_eq must both be given") + else: + A_eq, b_eq = [Matrix(i) for i in (A_eq, b_eq)] + + if not (bounds is None or bounds == {} or bounds == (0, None)): + ## the bounds are interpreted + if type(bounds) is tuple and len(bounds) == 2: + bounds = [bounds] * A.cols + elif len(bounds) == A.cols and all( + type(i) is tuple and len(i) == 2 for i in bounds): + pass # individual bounds + elif type(bounds) is dict and all( + type(i) is tuple and len(i) == 2 + for i in bounds.values()): + # sparse bounds + db = bounds + bounds = [(0, None)] * A.cols + while db: + i, j = db.popitem() + bounds[i] = j # IndexError if out-of-bounds indices + else: + raise ValueError("unexpected bounds %s" % bounds) + + x = Matrix(symbols('x1:%s' % (A.cols+1))) + f,c = (C*x)[0], [i<=j for i,j in zip(A*x, b)] + [Eq(i,j) for i,j in zip(A_eq*x,b_eq)] + for i, (lo, hi) in enumerate(bounds): + if lo is None and hi is None: + continue + if lo is None: + c.append(x[i]<=hi) + elif hi is None: + c.append(x[i]>=lo) + else: + c.append(x[i]>=lo) + c.append(x[i]<=hi) + return f,c diff --git a/lib/python3.12/site-packages/sympy/solvers/solvers.py b/lib/python3.12/site-packages/sympy/solvers/solvers.py new file mode 100644 index 0000000000000000000000000000000000000000..68b196cf305d48d790a79cfb907f4e05bd8817b1 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/solvers/solvers.py @@ -0,0 +1,3678 @@ +""" +This module contain solvers for all kinds of equations: + + - algebraic or transcendental, use solve() + + - recurrence, use rsolve() + + - differential, use dsolve() + + - nonlinear (numerically), use nsolve() + (you will need a good starting point) + +""" +from __future__ import annotations + +from sympy.core import (S, Add, Symbol, Dummy, Expr, Mul) +from sympy.core.assumptions import check_assumptions +from sympy.core.exprtools import factor_terms +from sympy.core.function import (expand_mul, expand_log, Derivative, + AppliedUndef, UndefinedFunction, nfloat, + Function, expand_power_exp, _mexpand, expand, + expand_func) +from sympy.core.logic import fuzzy_not +from sympy.core.numbers import Float, Rational, _illegal +from sympy.core.intfunc import integer_log, ilcm +from sympy.core.power import Pow +from sympy.core.relational import Eq, Ne +from sympy.core.sorting import ordered, default_sort_key +from sympy.core.sympify import sympify, _sympify +from sympy.core.traversal import preorder_traversal +from sympy.logic.boolalg import And, BooleanAtom + +from sympy.functions import (log, exp, LambertW, cos, sin, tan, acos, asin, atan, + Abs, re, im, arg, sqrt, atan2) +from sympy.functions.combinatorial.factorials import binomial +from sympy.functions.elementary.hyperbolic import HyperbolicFunction +from sympy.functions.elementary.piecewise import piecewise_fold, Piecewise +from sympy.functions.elementary.trigonometric import TrigonometricFunction +from sympy.integrals.integrals import Integral +from sympy.ntheory.factor_ import divisors +from sympy.simplify import (simplify, collect, powsimp, posify, # type: ignore + powdenest, nsimplify, denom, logcombine, sqrtdenest, fraction, + separatevars) +from sympy.simplify.sqrtdenest import sqrt_depth +from sympy.simplify.fu import TR1, TR2i, TR10, TR11 +from sympy.strategies.rl import rebuild +from sympy.matrices.exceptions import NonInvertibleMatrixError +from sympy.matrices import Matrix, zeros +from sympy.polys import roots, cancel, factor, Poly +from sympy.polys.solvers import sympy_eqs_to_ring, solve_lin_sys +from sympy.polys.polyerrors import GeneratorsNeeded, PolynomialError +from sympy.polys.polytools import gcd +from sympy.utilities.lambdify import lambdify +from sympy.utilities.misc import filldedent, debugf +from sympy.utilities.iterables import (connected_components, + generate_bell, uniq, iterable, is_sequence, subsets, flatten, sift) +from sympy.utilities.decorator import conserve_mpmath_dps + +from mpmath import findroot + +from sympy.solvers.polysys import solve_poly_system + +from types import GeneratorType +from collections import defaultdict +from itertools import combinations, product + +import warnings + + +def recast_to_symbols(eqs, symbols): + """ + Return (e, s, d) where e and s are versions of *eqs* and + *symbols* in which any non-Symbol objects in *symbols* have + been replaced with generic Dummy symbols and d is a dictionary + that can be used to restore the original expressions. + + Examples + ======== + + >>> from sympy.solvers.solvers import recast_to_symbols + >>> from sympy import symbols, Function + >>> x, y = symbols('x y') + >>> fx = Function('f')(x) + >>> eqs, syms = [fx + 1, x, y], [fx, y] + >>> e, s, d = recast_to_symbols(eqs, syms); (e, s, d) + ([_X0 + 1, x, y], [_X0, y], {_X0: f(x)}) + + The original equations and symbols can be restored using d: + + >>> assert [i.xreplace(d) for i in eqs] == eqs + >>> assert [d.get(i, i) for i in s] == syms + + """ + if not iterable(eqs) and iterable(symbols): + raise ValueError('Both eqs and symbols must be iterable') + orig = list(symbols) + symbols = list(ordered(symbols)) + swap_sym = {} + i = 0 + for s in symbols: + if not isinstance(s, Symbol) and s not in swap_sym: + swap_sym[s] = Dummy('X%d' % i) + i += 1 + new_f = [] + for i in eqs: + isubs = getattr(i, 'subs', None) + if isubs is not None: + new_f.append(isubs(swap_sym)) + else: + new_f.append(i) + restore = {v: k for k, v in swap_sym.items()} + return new_f, [swap_sym.get(i, i) for i in orig], restore + + +def _ispow(e): + """Return True if e is a Pow or is exp.""" + return isinstance(e, Expr) and (e.is_Pow or isinstance(e, exp)) + + +def _simple_dens(f, symbols): + # when checking if a denominator is zero, we can just check the + # base of powers with nonzero exponents since if the base is zero + # the power will be zero, too. To keep it simple and fast, we + # limit simplification to exponents that are Numbers + dens = set() + for d in denoms(f, symbols): + if d.is_Pow and d.exp.is_Number: + if d.exp.is_zero: + continue # foo**0 is never 0 + d = d.base + dens.add(d) + return dens + + +def denoms(eq, *symbols): + """ + Return (recursively) set of all denominators that appear in *eq* + that contain any symbol in *symbols*; if *symbols* are not + provided then all denominators will be returned. + + Examples + ======== + + >>> from sympy.solvers.solvers import denoms + >>> from sympy.abc import x, y, z + + >>> denoms(x/y) + {y} + + >>> denoms(x/(y*z)) + {y, z} + + >>> denoms(3/x + y/z) + {x, z} + + >>> denoms(x/2 + y/z) + {2, z} + + If *symbols* are provided then only denominators containing + those symbols will be returned: + + >>> denoms(1/x + 1/y + 1/z, y, z) + {y, z} + + """ + + pot = preorder_traversal(eq) + dens = set() + for p in pot: + # Here p might be Tuple or Relational + # Expr subtrees (e.g. lhs and rhs) will be traversed after by pot + if not isinstance(p, Expr): + continue + den = denom(p) + if den is S.One: + continue + dens.update(Mul.make_args(den)) + if not symbols: + return dens + elif len(symbols) == 1: + if iterable(symbols[0]): + symbols = symbols[0] + return {d for d in dens if any(s in d.free_symbols for s in symbols)} + + +def checksol(f, symbol, sol=None, **flags): + """ + Checks whether sol is a solution of equation f == 0. + + Explanation + =========== + + Input can be either a single symbol and corresponding value + or a dictionary of symbols and values. When given as a dictionary + and flag ``simplify=True``, the values in the dictionary will be + simplified. *f* can be a single equation or an iterable of equations. + A solution must satisfy all equations in *f* to be considered valid; + if a solution does not satisfy any equation, False is returned; if one or + more checks are inconclusive (and none are False) then None is returned. + + Examples + ======== + + >>> from sympy import checksol, symbols + >>> x, y = symbols('x,y') + >>> checksol(x**4 - 1, x, 1) + True + >>> checksol(x**4 - 1, x, 0) + False + >>> checksol(x**2 + y**2 - 5**2, {x: 3, y: 4}) + True + + To check if an expression is zero using ``checksol()``, pass it + as *f* and send an empty dictionary for *symbol*: + + >>> checksol(x**2 + x - x*(x + 1), {}) + True + + None is returned if ``checksol()`` could not conclude. + + flags: + 'numerical=True (default)' + do a fast numerical check if ``f`` has only one symbol. + 'minimal=True (default is False)' + a very fast, minimal testing. + 'warn=True (default is False)' + show a warning if checksol() could not conclude. + 'simplify=True (default)' + simplify solution before substituting into function and + simplify the function before trying specific simplifications + 'force=True (default is False)' + make positive all symbols without assumptions regarding sign. + + """ + from sympy.physics.units import Unit + + minimal = flags.get('minimal', False) + + if sol is not None: + sol = {symbol: sol} + elif isinstance(symbol, dict): + sol = symbol + else: + msg = 'Expecting (sym, val) or ({sym: val}, None) but got (%s, %s)' + raise ValueError(msg % (symbol, sol)) + + if iterable(f): + if not f: + raise ValueError('no functions to check') + rv = True + for fi in f: + check = checksol(fi, sol, **flags) + if check: + continue + if check is False: + return False + rv = None # don't return, wait to see if there's a False + return rv + + f = _sympify(f) + + if f.is_number: + return f.is_zero + + if isinstance(f, Poly): + f = f.as_expr() + elif isinstance(f, (Eq, Ne)): + if f.rhs in (S.true, S.false): + f = f.reversed + B, E = f.args + if isinstance(B, BooleanAtom): + f = f.subs(sol) + if not f.is_Boolean: + return + elif isinstance(f, Eq): + f = Add(f.lhs, -f.rhs, evaluate=False) + + if isinstance(f, BooleanAtom): + return bool(f) + elif not f.is_Relational and not f: + return True + + illegal = set(_illegal) + if any(sympify(v).atoms() & illegal for k, v in sol.items()): + return False + + attempt = -1 + numerical = flags.get('numerical', True) + while 1: + attempt += 1 + if attempt == 0: + val = f.subs(sol) + if isinstance(val, Mul): + val = val.as_independent(Unit)[0] + if val.atoms() & illegal: + return False + elif attempt == 1: + if not val.is_number: + if not val.is_constant(*list(sol.keys()), simplify=not minimal): + return False + # there are free symbols -- simple expansion might work + _, val = val.as_content_primitive() + val = _mexpand(val.as_numer_denom()[0], recursive=True) + elif attempt == 2: + if minimal: + return + if flags.get('simplify', True): + for k in sol: + sol[k] = simplify(sol[k]) + # start over without the failed expanded form, possibly + # with a simplified solution + val = simplify(f.subs(sol)) + if flags.get('force', True): + val, reps = posify(val) + # expansion may work now, so try again and check + exval = _mexpand(val, recursive=True) + if exval.is_number: + # we can decide now + val = exval + else: + # if there are no radicals and no functions then this can't be + # zero anymore -- can it? + pot = preorder_traversal(expand_mul(val)) + seen = set() + saw_pow_func = False + for p in pot: + if p in seen: + continue + seen.add(p) + if p.is_Pow and not p.exp.is_Integer: + saw_pow_func = True + elif p.is_Function: + saw_pow_func = True + elif isinstance(p, UndefinedFunction): + saw_pow_func = True + if saw_pow_func: + break + if saw_pow_func is False: + return False + if flags.get('force', True): + # don't do a zero check with the positive assumptions in place + val = val.subs(reps) + nz = fuzzy_not(val.is_zero) + if nz is not None: + # issue 5673: nz may be True even when False + # so these are just hacks to keep a false positive + # from being returned + + # HACK 1: LambertW (issue 5673) + if val.is_number and val.has(LambertW): + # don't eval this to verify solution since if we got here, + # numerical must be False + return None + + # add other HACKs here if necessary, otherwise we assume + # the nz value is correct + return not nz + break + if val.is_Rational: + return val == 0 + if numerical and val.is_number: + return (abs(val.n(18).n(12, chop=True)) < 1e-9) is S.true + + if flags.get('warn', False): + warnings.warn("\n\tWarning: could not verify solution %s." % sol) + # returns None if it can't conclude + # TODO: improve solution testing + + +def solve(f, *symbols, **flags): + r""" + Algebraically solves equations and systems of equations. + + Explanation + =========== + + Currently supported: + - polynomial + - transcendental + - piecewise combinations of the above + - systems of linear and polynomial equations + - systems containing relational expressions + - systems implied by undetermined coefficients + + Examples + ======== + + The default output varies according to the input and might + be a list (possibly empty), a dictionary, a list of + dictionaries or tuples, or an expression involving relationals. + For specifics regarding different forms of output that may appear, see :ref:`solve_output`. + Let it suffice here to say that to obtain a uniform output from + `solve` use ``dict=True`` or ``set=True`` (see below). + + >>> from sympy import solve, Poly, Eq, Matrix, Symbol + >>> from sympy.abc import x, y, z, a, b + + The expressions that are passed can be Expr, Equality, or Poly + classes (or lists of the same); a Matrix is considered to be a + list of all the elements of the matrix: + + >>> solve(x - 3, x) + [3] + >>> solve(Eq(x, 3), x) + [3] + >>> solve(Poly(x - 3), x) + [3] + >>> solve(Matrix([[x, x + y]]), x, y) == solve([x, x + y], x, y) + True + + If no symbols are indicated to be of interest and the equation is + univariate, a list of values is returned; otherwise, the keys in + a dictionary will indicate which (of all the variables used in + the expression(s)) variables and solutions were found: + + >>> solve(x**2 - 4) + [-2, 2] + >>> solve((x - a)*(y - b)) + [{a: x}, {b: y}] + >>> solve([x - 3, y - 1]) + {x: 3, y: 1} + >>> solve([x - 3, y**2 - 1]) + [{x: 3, y: -1}, {x: 3, y: 1}] + + If you pass symbols for which solutions are sought, the output will vary + depending on the number of symbols you passed, whether you are passing + a list of expressions or not, and whether a linear system was solved. + Uniform output is attained by using ``dict=True`` or ``set=True``. + + >>> #### *** feel free to skip to the stars below *** #### + >>> from sympy import TableForm + >>> h = [None, ';|;'.join(['e', 's', 'solve(e, s)', 'solve(e, s, dict=True)', + ... 'solve(e, s, set=True)']).split(';')] + >>> t = [] + >>> for e, s in [ + ... (x - y, y), + ... (x - y, [x, y]), + ... (x**2 - y, [x, y]), + ... ([x - 3, y -1], [x, y]), + ... ]: + ... how = [{}, dict(dict=True), dict(set=True)] + ... res = [solve(e, s, **f) for f in how] + ... t.append([e, '|', s, '|'] + [res[0], '|', res[1], '|', res[2]]) + ... + >>> # ******************************************************* # + >>> TableForm(t, headings=h, alignments="<") + e | s | solve(e, s) | solve(e, s, dict=True) | solve(e, s, set=True) + --------------------------------------------------------------------------------------- + x - y | y | [x] | [{y: x}] | ([y], {(x,)}) + x - y | [x, y] | [(y, y)] | [{x: y}] | ([x, y], {(y, y)}) + x**2 - y | [x, y] | [(x, x**2)] | [{y: x**2}] | ([x, y], {(x, x**2)}) + [x - 3, y - 1] | [x, y] | {x: 3, y: 1} | [{x: 3, y: 1}] | ([x, y], {(3, 1)}) + + * If any equation does not depend on the symbol(s) given, it will be + eliminated from the equation set and an answer may be given + implicitly in terms of variables that were not of interest: + + >>> solve([x - y, y - 3], x) + {x: y} + + When you pass all but one of the free symbols, an attempt + is made to find a single solution based on the method of + undetermined coefficients. If it succeeds, a dictionary of values + is returned. If you want an algebraic solutions for one + or more of the symbols, pass the expression to be solved in a list: + + >>> e = a*x + b - 2*x - 3 + >>> solve(e, [a, b]) + {a: 2, b: 3} + >>> solve([e], [a, b]) + {a: -b/x + (2*x + 3)/x} + + When there is no solution for any given symbol which will make all + expressions zero, the empty list is returned (or an empty set in + the tuple when ``set=True``): + + >>> from sympy import sqrt + >>> solve(3, x) + [] + >>> solve(x - 3, y) + [] + >>> solve(sqrt(x) + 1, x, set=True) + ([x], set()) + + When an object other than a Symbol is given as a symbol, it is + isolated algebraically and an implicit solution may be obtained. + This is mostly provided as a convenience to save you from replacing + the object with a Symbol and solving for that Symbol. It will only + work if the specified object can be replaced with a Symbol using the + subs method: + + >>> from sympy import exp, Function + >>> f = Function('f') + + >>> solve(f(x) - x, f(x)) + [x] + >>> solve(f(x).diff(x) - f(x) - x, f(x).diff(x)) + [x + f(x)] + >>> solve(f(x).diff(x) - f(x) - x, f(x)) + [-x + Derivative(f(x), x)] + >>> solve(x + exp(x)**2, exp(x), set=True) + ([exp(x)], {(-sqrt(-x),), (sqrt(-x),)}) + + >>> from sympy import Indexed, IndexedBase, Tuple + >>> A = IndexedBase('A') + >>> eqs = Tuple(A[1] + A[2] - 3, A[1] - A[2] + 1) + >>> solve(eqs, eqs.atoms(Indexed)) + {A[1]: 1, A[2]: 2} + + * To solve for a function within a derivative, use :func:`~.dsolve`. + + To solve for a symbol implicitly, use implicit=True: + + >>> solve(x + exp(x), x) + [-LambertW(1)] + >>> solve(x + exp(x), x, implicit=True) + [-exp(x)] + + It is possible to solve for anything in an expression that can be + replaced with a symbol using :obj:`~sympy.core.basic.Basic.subs`: + + >>> solve(x + 2 + sqrt(3), x + 2) + [-sqrt(3)] + >>> solve((x + 2 + sqrt(3), x + 4 + y), y, x + 2) + {y: -2 + sqrt(3), x + 2: -sqrt(3)} + + * Nothing heroic is done in this implicit solving so you may end up + with a symbol still in the solution: + + >>> eqs = (x*y + 3*y + sqrt(3), x + 4 + y) + >>> solve(eqs, y, x + 2) + {y: -sqrt(3)/(x + 3), x + 2: -2*x/(x + 3) - 6/(x + 3) + sqrt(3)/(x + 3)} + >>> solve(eqs, y*x, x) + {x: -y - 4, x*y: -3*y - sqrt(3)} + + * If you attempt to solve for a number, remember that the number + you have obtained does not necessarily mean that the value is + equivalent to the expression obtained: + + >>> solve(sqrt(2) - 1, 1) + [sqrt(2)] + >>> solve(x - y + 1, 1) # /!\ -1 is targeted, too + [x/(y - 1)] + >>> [_.subs(z, -1) for _ in solve((x - y + 1).subs(-1, z), 1)] + [-x + y] + + **Additional Examples** + + ``solve()`` with check=True (default) will run through the symbol tags to + eliminate unwanted solutions. If no assumptions are included, all possible + solutions will be returned: + + >>> x = Symbol("x") + >>> solve(x**2 - 1) + [-1, 1] + + By setting the ``positive`` flag, only one solution will be returned: + + >>> pos = Symbol("pos", positive=True) + >>> solve(pos**2 - 1) + [1] + + When the solutions are checked, those that make any denominator zero + are automatically excluded. If you do not want to exclude such solutions, + then use the check=False option: + + >>> from sympy import sin, limit + >>> solve(sin(x)/x) # 0 is excluded + [pi] + + If ``check=False``, then a solution to the numerator being zero is found + but the value of $x = 0$ is a spurious solution since $\sin(x)/x$ has the well + known limit (without discontinuity) of 1 at $x = 0$: + + >>> solve(sin(x)/x, check=False) + [0, pi] + + In the following case, however, the limit exists and is equal to the + value of $x = 0$ that is excluded when check=True: + + >>> eq = x**2*(1/x - z**2/x) + >>> solve(eq, x) + [] + >>> solve(eq, x, check=False) + [0] + >>> limit(eq, x, 0, '-') + 0 + >>> limit(eq, x, 0, '+') + 0 + + **Solving Relationships** + + When one or more expressions passed to ``solve`` is a relational, + a relational result is returned (and the ``dict`` and ``set`` flags + are ignored): + + >>> solve(x < 3) + (-oo < x) & (x < 3) + >>> solve([x < 3, x**2 > 4], x) + ((-oo < x) & (x < -2)) | ((2 < x) & (x < 3)) + >>> solve([x + y - 3, x > 3], x) + (3 < x) & (x < oo) & Eq(x, 3 - y) + + Although checking of assumptions on symbols in relationals + is not done, setting assumptions will affect how certain + relationals might automatically simplify: + + >>> solve(x**2 > 4) + ((-oo < x) & (x < -2)) | ((2 < x) & (x < oo)) + + >>> r = Symbol('r', real=True) + >>> solve(r**2 > 4) + (2 < r) | (r < -2) + + There is currently no algorithm in SymPy that allows you to use + relationships to resolve more than one variable. So the following + does not determine that ``q < 0`` (and trying to solve for ``r`` + and ``q`` will raise an error): + + >>> from sympy import symbols + >>> r, q = symbols('r, q', real=True) + >>> solve([r + q - 3, r > 3], r) + (3 < r) & Eq(r, 3 - q) + + You can directly call the routine that ``solve`` calls + when it encounters a relational: :func:`~.reduce_inequalities`. + It treats Expr like Equality. + + >>> from sympy import reduce_inequalities + >>> reduce_inequalities([x**2 - 4]) + Eq(x, -2) | Eq(x, 2) + + If each relationship contains only one symbol of interest, + the expressions can be processed for multiple symbols: + + >>> reduce_inequalities([0 <= x - 1, y < 3], [x, y]) + (-oo < y) & (1 <= x) & (x < oo) & (y < 3) + + But an error is raised if any relationship has more than one + symbol of interest: + + >>> reduce_inequalities([0 <= x*y - 1, y < 3], [x, y]) + Traceback (most recent call last): + ... + NotImplementedError: + inequality has more than one symbol of interest. + + **Disabling High-Order Explicit Solutions** + + When solving polynomial expressions, you might not want explicit solutions + (which can be quite long). If the expression is univariate, ``CRootOf`` + instances will be returned instead: + + >>> solve(x**3 - x + 1) + [-1/((-1/2 - sqrt(3)*I/2)*(3*sqrt(69)/2 + 27/2)**(1/3)) - + (-1/2 - sqrt(3)*I/2)*(3*sqrt(69)/2 + 27/2)**(1/3)/3, + -(-1/2 + sqrt(3)*I/2)*(3*sqrt(69)/2 + 27/2)**(1/3)/3 - + 1/((-1/2 + sqrt(3)*I/2)*(3*sqrt(69)/2 + 27/2)**(1/3)), + -(3*sqrt(69)/2 + 27/2)**(1/3)/3 - + 1/(3*sqrt(69)/2 + 27/2)**(1/3)] + >>> solve(x**3 - x + 1, cubics=False) + [CRootOf(x**3 - x + 1, 0), + CRootOf(x**3 - x + 1, 1), + CRootOf(x**3 - x + 1, 2)] + + If the expression is multivariate, no solution might be returned: + + >>> solve(x**3 - x + a, x, cubics=False) + [] + + Sometimes solutions will be obtained even when a flag is False because the + expression could be factored. In the following example, the equation can + be factored as the product of a linear and a quadratic factor so explicit + solutions (which did not require solving a cubic expression) are obtained: + + >>> eq = x**3 + 3*x**2 + x - 1 + >>> solve(eq, cubics=False) + [-1, -1 + sqrt(2), -sqrt(2) - 1] + + **Solving Equations Involving Radicals** + + Because of SymPy's use of the principle root, some solutions + to radical equations will be missed unless check=False: + + >>> from sympy import root + >>> eq = root(x**3 - 3*x**2, 3) + 1 - x + >>> solve(eq) + [] + >>> solve(eq, check=False) + [1/3] + + In the above example, there is only a single solution to the + equation. Other expressions will yield spurious roots which + must be checked manually; roots which give a negative argument + to odd-powered radicals will also need special checking: + + >>> from sympy import real_root, S + >>> eq = root(x, 3) - root(x, 5) + S(1)/7 + >>> solve(eq) # this gives 2 solutions but misses a 3rd + [CRootOf(7*x**5 - 7*x**3 + 1, 1)**15, + CRootOf(7*x**5 - 7*x**3 + 1, 2)**15] + >>> sol = solve(eq, check=False) + >>> [abs(eq.subs(x,i).n(2)) for i in sol] + [0.48, 0.e-110, 0.e-110, 0.052, 0.052] + + The first solution is negative so ``real_root`` must be used to see that it + satisfies the expression: + + >>> abs(real_root(eq.subs(x, sol[0])).n(2)) + 0.e-110 + + If the roots of the equation are not real then more care will be + necessary to find the roots, especially for higher order equations. + Consider the following expression: + + >>> expr = root(x, 3) - root(x, 5) + + We will construct a known value for this expression at x = 3 by selecting + the 1-th root for each radical: + + >>> expr1 = root(x, 3, 1) - root(x, 5, 1) + >>> v = expr1.subs(x, -3) + + The ``solve`` function is unable to find any exact roots to this equation: + + >>> eq = Eq(expr, v); eq1 = Eq(expr1, v) + >>> solve(eq, check=False), solve(eq1, check=False) + ([], []) + + The function ``unrad``, however, can be used to get a form of the equation + for which numerical roots can be found: + + >>> from sympy.solvers.solvers import unrad + >>> from sympy import nroots + >>> e, (p, cov) = unrad(eq) + >>> pvals = nroots(e) + >>> inversion = solve(cov, x)[0] + >>> xvals = [inversion.subs(p, i) for i in pvals] + + Although ``eq`` or ``eq1`` could have been used to find ``xvals``, the + solution can only be verified with ``expr1``: + + >>> z = expr - v + >>> [xi.n(chop=1e-9) for xi in xvals if abs(z.subs(x, xi).n()) < 1e-9] + [] + >>> z1 = expr1 - v + >>> [xi.n(chop=1e-9) for xi in xvals if abs(z1.subs(x, xi).n()) < 1e-9] + [-3.0] + + Parameters + ========== + + f : + - a single Expr or Poly that must be zero + - an Equality + - a Relational expression + - a Boolean + - iterable of one or more of the above + + symbols : (object(s) to solve for) specified as + - none given (other non-numeric objects will be used) + - single symbol + - denested list of symbols + (e.g., ``solve(f, x, y)``) + - ordered iterable of symbols + (e.g., ``solve(f, [x, y])``) + + flags : + dict=True (default is False) + Return list (perhaps empty) of solution mappings. + set=True (default is False) + Return list of symbols and set of tuple(s) of solution(s). + exclude=[] (default) + Do not try to solve for any of the free symbols in exclude; + if expressions are given, the free symbols in them will + be extracted automatically. + check=True (default) + If False, do not do any testing of solutions. This can be + useful if you want to include solutions that make any + denominator zero. + numerical=True (default) + Do a fast numerical check if *f* has only one symbol. + minimal=True (default is False) + A very fast, minimal testing. + warn=True (default is False) + Show a warning if ``checksol()`` could not conclude. + simplify=True (default) + Simplify all but polynomials of order 3 or greater before + returning them and (if check is not False) use the + general simplify function on the solutions and the + expression obtained when they are substituted into the + function which should be zero. + force=True (default is False) + Make positive all symbols without assumptions regarding sign. + rational=True (default) + Recast Floats as Rational; if this option is not used, the + system containing Floats may fail to solve because of issues + with polys. If rational=None, Floats will be recast as + rationals but the answer will be recast as Floats. If the + flag is False then nothing will be done to the Floats. + manual=True (default is False) + Do not use the polys/matrix method to solve a system of + equations, solve them one at a time as you might "manually." + implicit=True (default is False) + Allows ``solve`` to return a solution for a pattern in terms of + other functions that contain that pattern; this is only + needed if the pattern is inside of some invertible function + like cos, exp, ect. + particular=True (default is False) + Instructs ``solve`` to try to find a particular solution to + a linear system with as many zeros as possible; this is very + expensive. + quick=True (default is False; ``particular`` must be True) + Selects a fast heuristic to find a solution with many zeros + whereas a value of False uses the very slow method guaranteed + to find the largest number of zeros possible. + cubics=True (default) + Return explicit solutions when cubic expressions are encountered. + When False, quartics and quintics are disabled, too. + quartics=True (default) + Return explicit solutions when quartic expressions are encountered. + When False, quintics are disabled, too. + quintics=True (default) + Return explicit solutions (if possible) when quintic expressions + are encountered. + + See Also + ======== + + rsolve: For solving recurrence relationships + dsolve: For solving differential equations + + """ + from .inequalities import reduce_inequalities + + # checking/recording flags + ########################################################################### + + # set solver types explicitly; as soon as one is False + # all the rest will be False + hints = ('cubics', 'quartics', 'quintics') + default = True + for k in hints: + default = flags.setdefault(k, bool(flags.get(k, default))) + + # allow solution to contain symbol if True: + implicit = flags.get('implicit', False) + + # record desire to see warnings + warn = flags.get('warn', False) + + # this flag will be needed for quick exits below, so record + # now -- but don't record `dict` yet since it might change + as_set = flags.get('set', False) + + # keeping track of how f was passed + bare_f = not iterable(f) + + # check flag usage for particular/quick which should only be used + # with systems of equations + if flags.get('quick', None) is not None: + if not flags.get('particular', None): + raise ValueError('when using `quick`, `particular` should be True') + if flags.get('particular', False) and bare_f: + raise ValueError(filldedent(""" + The 'particular/quick' flag is usually used with systems of + equations. Either pass your equation in a list or + consider using a solver like `diophantine` if you are + looking for a solution in integers.""")) + + # sympify everything, creating list of expressions and list of symbols + ########################################################################### + + def _sympified_list(w): + return list(map(sympify, w if iterable(w) else [w])) + f, symbols = (_sympified_list(w) for w in [f, symbols]) + + # preprocess symbol(s) + ########################################################################### + + ordered_symbols = None # were the symbols in a well defined order? + if not symbols: + # get symbols from equations + symbols = set().union(*[fi.free_symbols for fi in f]) + if len(symbols) < len(f): + for fi in f: + pot = preorder_traversal(fi) + for p in pot: + if isinstance(p, AppliedUndef): + if not as_set: + flags['dict'] = True # better show symbols + symbols.add(p) + pot.skip() # don't go any deeper + ordered_symbols = False + symbols = list(ordered(symbols)) # to make it canonical + else: + if len(symbols) == 1 and iterable(symbols[0]): + symbols = symbols[0] + ordered_symbols = symbols and is_sequence(symbols, + include=GeneratorType) + _symbols = list(uniq(symbols)) + if len(_symbols) != len(symbols): + ordered_symbols = False + symbols = list(ordered(symbols)) + else: + symbols = _symbols + + # check for duplicates + if len(symbols) != len(set(symbols)): + raise ValueError('duplicate symbols given') + # remove those not of interest + exclude = flags.pop('exclude', set()) + if exclude: + if isinstance(exclude, Expr): + exclude = [exclude] + exclude = set().union(*[e.free_symbols for e in sympify(exclude)]) + symbols = [s for s in symbols if s not in exclude] + + # preprocess equation(s) + ########################################################################### + + # automatically ignore True values + if isinstance(f, list): + f = [s for s in f if s is not S.true] + + # handle canonicalization of equation types + for i, fi in enumerate(f): + if isinstance(fi, (Eq, Ne)): + if 'ImmutableDenseMatrix' in [type(a).__name__ for a in fi.args]: + fi = fi.lhs - fi.rhs + else: + L, R = fi.args + if isinstance(R, BooleanAtom): + L, R = R, L + if isinstance(L, BooleanAtom): + if isinstance(fi, Ne): + L = ~L + if R.is_Relational: + fi = ~R if L is S.false else R + elif R.is_Symbol: + return L + elif R.is_Boolean and (~R).is_Symbol: + return ~L + else: + raise NotImplementedError(filldedent(''' + Unanticipated argument of Eq when other arg + is True or False. + ''')) + elif isinstance(fi, Eq): + fi = Add(fi.lhs, -fi.rhs, evaluate=False) + f[i] = fi + + # *** dispatch and handle as a system of relationals + # ************************************************** + if fi.is_Relational: + if len(symbols) != 1: + raise ValueError("can only solve for one symbol at a time") + if warn and symbols[0].assumptions0: + warnings.warn(filldedent(""" + \tWarning: assumptions about variable '%s' are + not handled currently.""" % symbols[0])) + return reduce_inequalities(f, symbols=symbols) + + # convert Poly to expression + if isinstance(fi, Poly): + f[i] = fi.as_expr() + + # rewrite hyperbolics in terms of exp if they have symbols of + # interest + f[i] = f[i].replace(lambda w: isinstance(w, HyperbolicFunction) and \ + w.has_free(*symbols), lambda w: w.rewrite(exp)) + + # if we have a Matrix, we need to iterate over its elements again + if f[i].is_Matrix: + bare_f = False + f.extend(list(f[i])) + f[i] = S.Zero + + # if we can split it into real and imaginary parts then do so + freei = f[i].free_symbols + if freei and all(s.is_extended_real or s.is_imaginary for s in freei): + fr, fi = f[i].as_real_imag() + # accept as long as new re, im, arg or atan2 are not introduced + had = f[i].atoms(re, im, arg, atan2) + if fr and fi and fr != fi and not any( + i.atoms(re, im, arg, atan2) - had for i in (fr, fi)): + if bare_f: + bare_f = False + f[i: i + 1] = [fr, fi] + + # real/imag handling ----------------------------- + if any(isinstance(fi, (bool, BooleanAtom)) for fi in f): + if as_set: + return [], set() + return [] + + for i, fi in enumerate(f): + # Abs + while True: + was = fi + fi = fi.replace(Abs, lambda arg: + separatevars(Abs(arg)).rewrite(Piecewise) if arg.has(*symbols) + else Abs(arg)) + if was == fi: + break + + for e in fi.find(Abs): + if e.has(*symbols): + raise NotImplementedError('solving %s when the argument ' + 'is not real or imaginary.' % e) + + # arg + fi = fi.replace(arg, lambda a: arg(a).rewrite(atan2).rewrite(atan)) + + # save changes + f[i] = fi + + # see if re(s) or im(s) appear + freim = [fi for fi in f if fi.has(re, im)] + if freim: + irf = [] + for s in symbols: + if s.is_real or s.is_imaginary: + continue # neither re(x) nor im(x) will appear + # if re(s) or im(s) appear, the auxiliary equation must be present + if any(fi.has(re(s), im(s)) for fi in freim): + irf.append((s, re(s) + S.ImaginaryUnit*im(s))) + if irf: + for s, rhs in irf: + f = [fi.xreplace({s: rhs}) for fi in f] + [s - rhs] + symbols.extend([re(s), im(s)]) + if bare_f: + bare_f = False + flags['dict'] = True + # end of real/imag handling ----------------------------- + + # we can solve for non-symbol entities by replacing them with Dummy symbols + f, symbols, swap_sym = recast_to_symbols(f, symbols) + # this set of symbols (perhaps recast) is needed below + symset = set(symbols) + + # get rid of equations that have no symbols of interest; we don't + # try to solve them because the user didn't ask and they might be + # hard to solve; this means that solutions may be given in terms + # of the eliminated equations e.g. solve((x-y, y-3), x) -> {x: y} + newf = [] + for fi in f: + # let the solver handle equations that.. + # - have no symbols but are expressions + # - have symbols of interest + # - have no symbols of interest but are constant + # but when an expression is not constant and has no symbols of + # interest, it can't change what we obtain for a solution from + # the remaining equations so we don't include it; and if it's + # zero it can be removed and if it's not zero, there is no + # solution for the equation set as a whole + # + # The reason for doing this filtering is to allow an answer + # to be obtained to queries like solve((x - y, y), x); without + # this mod the return value is [] + ok = False + if fi.free_symbols & symset: + ok = True + else: + if fi.is_number: + if fi.is_Number: + if fi.is_zero: + continue + return [] + ok = True + else: + if fi.is_constant(): + ok = True + if ok: + newf.append(fi) + if not newf: + if as_set: + return symbols, set() + return [] + f = newf + del newf + + # mask off any Object that we aren't going to invert: Derivative, + # Integral, etc... so that solving for anything that they contain will + # give an implicit solution + seen = set() + non_inverts = set() + for fi in f: + pot = preorder_traversal(fi) + for p in pot: + if not isinstance(p, Expr) or isinstance(p, Piecewise): + pass + elif (isinstance(p, bool) or + not p.args or + p in symset or + p.is_Add or p.is_Mul or + p.is_Pow and not implicit or + p.is_Function and not implicit) and p.func not in (re, im): + continue + elif p not in seen: + seen.add(p) + if p.free_symbols & symset: + non_inverts.add(p) + else: + continue + pot.skip() + del seen + non_inverts = dict(list(zip(non_inverts, [Dummy() for _ in non_inverts]))) + f = [fi.subs(non_inverts) for fi in f] + + # Both xreplace and subs are needed below: xreplace to force substitution + # inside Derivative, subs to handle non-straightforward substitutions + non_inverts = [(v, k.xreplace(swap_sym).subs(swap_sym)) for k, v in non_inverts.items()] + + # rationalize Floats + floats = False + if flags.get('rational', True) is not False: + for i, fi in enumerate(f): + if fi.has(Float): + floats = True + f[i] = nsimplify(fi, rational=True) + + # capture any denominators before rewriting since + # they may disappear after the rewrite, e.g. issue 14779 + flags['_denominators'] = _simple_dens(f[0], symbols) + + # Any embedded piecewise functions need to be brought out to the + # top level so that the appropriate strategy gets selected. + # However, this is necessary only if one of the piecewise + # functions depends on one of the symbols we are solving for. + def _has_piecewise(e): + if e.is_Piecewise: + return e.has(*symbols) + return any(_has_piecewise(a) for a in e.args) + for i, fi in enumerate(f): + if _has_piecewise(fi): + f[i] = piecewise_fold(fi) + + # expand angles of sums; in general, expand_trig will allow + # more roots to be found but this is not a great solultion + # to not returning a parametric solution, otherwise + # many values can be returned that have a simple + # relationship between values + targs = {t for fi in f for t in fi.atoms(TrigonometricFunction)} + if len(targs) > 1: + add, other = sift(targs, lambda x: x.args[0].is_Add, binary=True) + add, other = [[i for i in l if i.has_free(*symbols)] for l in (add, other)] + trep = {} + for t in add: + a = t.args[0] + ind, dep = a.as_independent(*symbols) + if dep in symbols or -dep in symbols: + # don't let expansion expand wrt anything in ind + n = Dummy() if not ind.is_Number else ind + trep[t] = TR10(t.func(dep + n)).xreplace({n: ind}) + if other and len(other) <= 2: + base = gcd(*[i.args[0] for i in other]) if len(other) > 1 else other[0].args[0] + for i in other: + trep[i] = TR11(i, base) + f = [fi.xreplace(trep) for fi in f] + + # + # try to get a solution + ########################################################################### + if bare_f: + solution = None + if len(symbols) != 1: + solution = _solve_undetermined(f[0], symbols, flags) + if not solution: + solution = _solve(f[0], *symbols, **flags) + else: + linear, solution = _solve_system(f, symbols, **flags) + assert type(solution) is list + assert not solution or type(solution[0]) is dict, solution + # + # postprocessing + ########################################################################### + # capture as_dict flag now (as_set already captured) + as_dict = flags.get('dict', False) + + # define how solution will get unpacked + tuple_format = lambda s: [tuple([i.get(x, x) for x in symbols]) for i in s] + if as_dict or as_set: + unpack = None + elif bare_f: + if len(symbols) == 1: + unpack = lambda s: [i[symbols[0]] for i in s] + elif len(solution) == 1 and len(solution[0]) == len(symbols): + # undetermined linear coeffs solution + unpack = lambda s: s[0] + elif ordered_symbols: + unpack = tuple_format + else: + unpack = lambda s: s + else: + if solution: + if linear and len(solution) == 1: + # if you want the tuple solution for the linear + # case, use `set=True` + unpack = lambda s: s[0] + elif ordered_symbols: + unpack = tuple_format + else: + unpack = lambda s: s + else: + unpack = None + + # Restore masked-off objects + if non_inverts and type(solution) is list: + solution = [{k: v.subs(non_inverts) for k, v in s.items()} + for s in solution] + + # Restore original "symbols" if a dictionary is returned. + # This is not necessary for + # - the single univariate equation case + # since the symbol will have been removed from the solution; + # - the nonlinear poly_system since that only supports zero-dimensional + # systems and those results come back as a list + # + # ** unless there were Derivatives with the symbols, but those were handled + # above. + if swap_sym: + symbols = [swap_sym.get(k, k) for k in symbols] + for i, sol in enumerate(solution): + solution[i] = {swap_sym.get(k, k): v.subs(swap_sym) + for k, v in sol.items()} + + # Get assumptions about symbols, to filter solutions. + # Note that if assumptions about a solution can't be verified, it is still + # returned. + check = flags.get('check', True) + + # restore floats + if floats and solution and flags.get('rational', None) is None: + solution = nfloat(solution, exponent=False) + # nfloat might reveal more duplicates + solution = _remove_duplicate_solutions(solution) + + if check and solution: # assumption checking + warn = flags.get('warn', False) + got_None = [] # solutions for which one or more symbols gave None + no_False = [] # solutions for which no symbols gave False + for sol in solution: + a_None = False + for symb, val in sol.items(): + test = check_assumptions(val, **symb.assumptions0) + if test: + continue + if test is False: + break + a_None = True + else: + no_False.append(sol) + if a_None: + got_None.append(sol) + + solution = no_False + if warn and got_None: + warnings.warn(filldedent(""" + \tWarning: assumptions concerning following solution(s) + cannot be checked:""" + '\n\t' + + ', '.join(str(s) for s in got_None))) + + # + # done + ########################################################################### + + if not solution: + if as_set: + return symbols, set() + return [] + + # make orderings canonical for list of dictionaries + if not as_set: # for set, no point in ordering + solution = [{k: s[k] for k in ordered(s)} for s in solution] + solution.sort(key=default_sort_key) + + if not (as_set or as_dict): + return unpack(solution) + + if as_dict: + return solution + + # set output: (symbols, {t1, t2, ...}) from list of dictionaries; + # include all symbols for those that like a verbose solution + # and to resolve any differences in dictionary keys. + # + # The set results can easily be used to make a verbose dict as + # k, v = solve(eqs, syms, set=True) + # sol = [dict(zip(k,i)) for i in v] + # + if ordered_symbols: + k = symbols # keep preferred order + else: + # just unify the symbols for which solutions were found + k = list(ordered(set(flatten(tuple(i.keys()) for i in solution)))) + return k, {tuple([s.get(ki, ki) for ki in k]) for s in solution} + + +def _solve_undetermined(g, symbols, flags): + """solve helper to return a list with one dict (solution) else None + + A direct call to solve_undetermined_coeffs is more flexible and + can return both multiple solutions and handle more than one independent + variable. Here, we have to be more cautious to keep from solving + something that does not look like an undetermined coeffs system -- + to minimize the surprise factor since singularities that cancel are not + prohibited in solve_undetermined_coeffs. + """ + if g.free_symbols - set(symbols): + sol = solve_undetermined_coeffs(g, symbols, **dict(flags, dict=True, set=None)) + if len(sol) == 1: + return sol + + +def _solve(f, *symbols, **flags): + """Return a checked solution for *f* in terms of one or more of the + symbols in the form of a list of dictionaries. + + If no method is implemented to solve the equation, a NotImplementedError + will be raised. In the case that conversion of an expression to a Poly + gives None a ValueError will be raised. + """ + + not_impl_msg = "No algorithms are implemented to solve equation %s" + + if len(symbols) != 1: + # look for solutions for desired symbols that are independent + # of symbols already solved for, e.g. if we solve for x = y + # then no symbol having x in its solution will be returned. + + # First solve for linear symbols (since that is easier and limits + # solution size) and then proceed with symbols appearing + # in a non-linear fashion. Ideally, if one is solving a single + # expression for several symbols, they would have to be + # appear in factors of an expression, but we do not here + # attempt factorization. XXX perhaps handling a Mul + # should come first in this routine whether there is + # one or several symbols. + nonlin_s = [] + got_s = set() + rhs_s = set() + result = [] + for s in symbols: + xi, v = solve_linear(f, symbols=[s]) + if xi == s: + # no need to check but we should simplify if desired + if flags.get('simplify', True): + v = simplify(v) + vfree = v.free_symbols + if vfree & got_s: + # was linear, but has redundant relationship + # e.g. x - y = 0 has y == x is redundant for x == y + # so ignore + continue + rhs_s |= vfree + got_s.add(xi) + result.append({xi: v}) + elif xi: # there might be a non-linear solution if xi is not 0 + nonlin_s.append(s) + if not nonlin_s: + return result + for s in nonlin_s: + try: + soln = _solve(f, s, **flags) + for sol in soln: + if sol[s].free_symbols & got_s: + # depends on previously solved symbols: ignore + continue + got_s.add(s) + result.append(sol) + except NotImplementedError: + continue + if got_s: + return result + else: + raise NotImplementedError(not_impl_msg % f) + + # solve f for a single variable + + symbol = symbols[0] + + # expand binomials only if it has the unknown symbol + f = f.replace(lambda e: isinstance(e, binomial) and e.has(symbol), + lambda e: expand_func(e)) + + # checking will be done unless it is turned off before making a + # recursive call; the variables `checkdens` and `check` are + # captured here (for reference below) in case flag value changes + flags['check'] = checkdens = check = flags.pop('check', True) + + # build up solutions if f is a Mul + if f.is_Mul: + result = set() + for m in f.args: + if m in {S.NegativeInfinity, S.ComplexInfinity, S.Infinity}: + result = set() + break + soln = _vsolve(m, symbol, **flags) + result.update(set(soln)) + result = [{symbol: v} for v in result] + if check: + # all solutions have been checked but now we must + # check that the solutions do not set denominators + # in any factor to zero + dens = flags.get('_denominators', _simple_dens(f, symbols)) + result = [s for s in result if + not any(checksol(den, s, **flags) for den in + dens)] + # set flags for quick exit at end; solutions for each + # factor were already checked and simplified + check = False + flags['simplify'] = False + + elif f.is_Piecewise: + result = set() + if any(e.is_zero for e, c in f.args): + f = f.simplify() # failure imminent w/o help + + cond = neg = True + for expr, cnd in f.args: + # the explicit condition for this expr is the current cond + # and none of the previous conditions + cond = And(neg, cnd) + neg = And(neg, ~cond) + + if expr.is_zero and cond.simplify() != False: + raise NotImplementedError(filldedent(''' + An expression is already zero when %s. + This means that in this *region* the solution + is zero but solve can only represent discrete, + not interval, solutions. If this is a spurious + interval it might be resolved with simplification + of the Piecewise conditions.''' % cond)) + candidates = _vsolve(expr, symbol, **flags) + + for candidate in candidates: + if candidate in result: + # an unconditional value was already there + continue + try: + v = cond.subs(symbol, candidate) + _eval_simplify = getattr(v, '_eval_simplify', None) + if _eval_simplify is not None: + # unconditionally take the simplification of v + v = _eval_simplify(ratio=2, measure=lambda x: 1) + except TypeError: + # incompatible type with condition(s) + continue + if v == False: + continue + if v == True: + result.add(candidate) + else: + result.add(Piecewise( + (candidate, v), + (S.NaN, True))) + # solutions already checked and simplified + # **************************************** + return [{symbol: r} for r in result] + else: + # first see if it really depends on symbol and whether there + # is only a linear solution + f_num, sol = solve_linear(f, symbols=symbols) + if f_num.is_zero or sol is S.NaN: + return [] + elif f_num.is_Symbol: + # no need to check but simplify if desired + if flags.get('simplify', True): + sol = simplify(sol) + return [{f_num: sol}] + + poly = None + # check for a single Add generator + if not f_num.is_Add: + add_args = [i for i in f_num.atoms(Add) + if symbol in i.free_symbols] + if len(add_args) == 1: + gen = add_args[0] + spart = gen.as_independent(symbol)[1].as_base_exp()[0] + if spart == symbol: + try: + poly = Poly(f_num, spart) + except PolynomialError: + pass + + result = False # no solution was obtained + msg = '' # there is no failure message + + # Poly is generally robust enough to convert anything to + # a polynomial and tell us the different generators that it + # contains, so we will inspect the generators identified by + # polys to figure out what to do. + + # try to identify a single generator that will allow us to solve this + # as a polynomial, followed (perhaps) by a change of variables if the + # generator is not a symbol + + try: + if poly is None: + poly = Poly(f_num) + if poly is None: + raise ValueError('could not convert %s to Poly' % f_num) + except GeneratorsNeeded: + simplified_f = simplify(f_num) + if simplified_f != f_num: + return _solve(simplified_f, symbol, **flags) + raise ValueError('expression appears to be a constant') + + gens = [g for g in poly.gens if g.has(symbol)] + + def _as_base_q(x): + """Return (b**e, q) for x = b**(p*e/q) where p/q is the leading + Rational of the exponent of x, e.g. exp(-2*x/3) -> (exp(x), 3) + """ + b, e = x.as_base_exp() + if e.is_Rational: + return b, e.q + if not e.is_Mul: + return x, 1 + c, ee = e.as_coeff_Mul() + if c.is_Rational and c is not S.One: # c could be a Float + return b**ee, c.q + return x, 1 + + if len(gens) > 1: + # If there is more than one generator, it could be that the + # generators have the same base but different powers, e.g. + # >>> Poly(exp(x) + 1/exp(x)) + # Poly(exp(-x) + exp(x), exp(-x), exp(x), domain='ZZ') + # + # If unrad was not disabled then there should be no rational + # exponents appearing as in + # >>> Poly(sqrt(x) + sqrt(sqrt(x))) + # Poly(sqrt(x) + x**(1/4), sqrt(x), x**(1/4), domain='ZZ') + + bases, qs = list(zip(*[_as_base_q(g) for g in gens])) + bases = set(bases) + + if len(bases) > 1 or not all(q == 1 for q in qs): + funcs = {b for b in bases if b.is_Function} + + trig = {_ for _ in funcs if + isinstance(_, TrigonometricFunction)} + other = funcs - trig + if not other and len(funcs.intersection(trig)) > 1: + newf = None + if f_num.is_Add and len(f_num.args) == 2: + # check for sin(x)**p = cos(x)**p + _args = f_num.args + t = a, b = [i.atoms(Function).intersection( + trig) for i in _args] + if all(len(i) == 1 for i in t): + a, b = [i.pop() for i in t] + if isinstance(a, cos): + a, b = b, a + _args = _args[::-1] + if isinstance(a, sin) and isinstance(b, cos + ) and a.args[0] == b.args[0]: + # sin(x) + cos(x) = 0 -> tan(x) + 1 = 0 + newf, _d = (TR2i(_args[0]/_args[1]) + 1 + ).as_numer_denom() + if not _d.is_Number: + newf = None + if newf is None: + newf = TR1(f_num).rewrite(tan) + if newf != f_num: + # don't check the rewritten form --check + # solutions in the un-rewritten form below + flags['check'] = False + result = _solve(newf, symbol, **flags) + flags['check'] = check + + # just a simple case - see if replacement of single function + # clears all symbol-dependent functions, e.g. + # log(x) - log(log(x) - 1) - 3 can be solved even though it has + # two generators. + + if result is False and funcs: + funcs = list(ordered(funcs)) # put shallowest function first + f1 = funcs[0] + t = Dummy('t') + # perform the substitution + ftry = f_num.subs(f1, t) + + # if no Functions left, we can proceed with usual solve + if not ftry.has(symbol): + cv_sols = _solve(ftry, t, **flags) + cv_inv = list(ordered(_vsolve(t - f1, symbol, **flags)))[0] + result = [{symbol: cv_inv.subs(sol)} for sol in cv_sols] + + if result is False: + msg = 'multiple generators %s' % gens + + else: + # e.g. case where gens are exp(x), exp(-x) + u = bases.pop() + t = Dummy('t') + inv = _vsolve(u - t, symbol, **flags) + if isinstance(u, (Pow, exp)): + # this will be resolved by factor in _tsolve but we might + # as well try a simple expansion here to get things in + # order so something like the following will work now without + # having to factor: + # + # >>> eq = (exp(I*(-x-2))+exp(I*(x+2))) + # >>> eq.subs(exp(x),y) # fails + # exp(I*(-x - 2)) + exp(I*(x + 2)) + # >>> eq.expand().subs(exp(x),y) # works + # y**I*exp(2*I) + y**(-I)*exp(-2*I) + def _expand(p): + b, e = p.as_base_exp() + e = expand_mul(e) + return expand_power_exp(b**e) + ftry = f_num.replace( + lambda w: w.is_Pow or isinstance(w, exp), + _expand).subs(u, t) + if not ftry.has(symbol): + soln = _solve(ftry, t, **flags) + result = [{symbol: i.subs(s)} for i in inv for s in soln] + + elif len(gens) == 1: + + # There is only one generator that we are interested in, but + # there may have been more than one generator identified by + # polys (e.g. for symbols other than the one we are interested + # in) so recast the poly in terms of our generator of interest. + # Also use composite=True with f_num since Poly won't update + # poly as documented in issue 8810. + + poly = Poly(f_num, gens[0], composite=True) + + # if we aren't on the tsolve-pass, use roots + if not flags.pop('tsolve', False): + soln = None + deg = poly.degree() + flags['tsolve'] = True + hints = ('cubics', 'quartics', 'quintics') + solvers = {h: flags.get(h) for h in hints} + soln = roots(poly, **solvers) + if sum(soln.values()) < deg: + # e.g. roots(32*x**5 + 400*x**4 + 2032*x**3 + + # 5000*x**2 + 6250*x + 3189) -> {} + # so all_roots is used and RootOf instances are + # returned *unless* the system is multivariate + # or high-order EX domain. + try: + soln = poly.all_roots() + except NotImplementedError: + if not flags.get('incomplete', True): + raise NotImplementedError( + filldedent(''' + Neither high-order multivariate polynomials + nor sorting of EX-domain polynomials is supported. + If you want to see any results, pass keyword incomplete=True to + solve; to see numerical values of roots + for univariate expressions, use nroots. + ''')) + else: + pass + else: + soln = list(soln.keys()) + + if soln is not None: + u = poly.gen + if u != symbol: + try: + t = Dummy('t') + inv = _vsolve(u - t, symbol, **flags) + soln = {i.subs(t, s) for i in inv for s in soln} + except NotImplementedError: + # perhaps _tsolve can handle f_num + soln = None + else: + check = False # only dens need to be checked + if soln is not None: + if len(soln) > 2: + # if the flag wasn't set then unset it since high-order + # results are quite long. Perhaps one could base this + # decision on a certain critical length of the + # roots. In addition, wester test M2 has an expression + # whose roots can be shown to be real with the + # unsimplified form of the solution whereas only one of + # the simplified forms appears to be real. + flags['simplify'] = flags.get('simplify', False) + if soln is not None: + result = [{symbol: v} for v in soln] + + # fallback if above fails + # ----------------------- + if result is False: + # try unrad + if flags.pop('_unrad', True): + try: + u = unrad(f_num, symbol) + except (ValueError, NotImplementedError): + u = False + if u: + eq, cov = u + if cov: + isym, ieq = cov + inv = _vsolve(ieq, symbol, **flags)[0] + rv = {inv.subs(xi) for xi in _solve(eq, isym, **flags)} + else: + try: + rv = set(_vsolve(eq, symbol, **flags)) + except NotImplementedError: + rv = None + if rv is not None: + result = [{symbol: v} for v in rv] + # if the flag wasn't set then unset it since unrad results + # can be quite long or of very high order + flags['simplify'] = flags.get('simplify', False) + else: + pass # for coverage + + # try _tsolve + if result is False: + flags.pop('tsolve', None) # allow tsolve to be used on next pass + try: + soln = _tsolve(f_num, symbol, **flags) + if soln is not None: + result = [{symbol: v} for v in soln] + except PolynomialError: + pass + # ----------- end of fallback ---------------------------- + + if result is False: + raise NotImplementedError('\n'.join([msg, not_impl_msg % f])) + + result = _remove_duplicate_solutions(result) + + if flags.get('simplify', True): + result = [{k: d[k].simplify() for k in d} for d in result] + # Simplification might reveal more duplicates + result = _remove_duplicate_solutions(result) + # we just simplified the solution so we now set the flag to + # False so the simplification doesn't happen again in checksol() + flags['simplify'] = False + + if checkdens: + # reject any result that makes any denom. affirmatively 0; + # if in doubt, keep it + dens = _simple_dens(f, symbols) + result = [r for r in result if + not any(checksol(d, r, **flags) + for d in dens)] + if check: + # keep only results if the check is not False + result = [r for r in result if + checksol(f_num, r, **flags) is not False] + return result + + +def _remove_duplicate_solutions(solutions: list[dict[Expr, Expr]] + ) -> list[dict[Expr, Expr]]: + """Remove duplicates from a list of dicts""" + solutions_set = set() + solutions_new = [] + + for sol in solutions: + solset = frozenset(sol.items()) + if solset not in solutions_set: + solutions_new.append(sol) + solutions_set.add(solset) + + return solutions_new + + +def _solve_system(exprs, symbols, **flags): + """return ``(linear, solution)`` where ``linear`` is True + if the system was linear, else False; ``solution`` + is a list of dictionaries giving solutions for the symbols + """ + if not exprs: + return False, [] + + if flags.pop('_split', True): + # Split the system into connected components + V = exprs + symsset = set(symbols) + exprsyms = {e: e.free_symbols & symsset for e in exprs} + E = [] + sym_indices = {sym: i for i, sym in enumerate(symbols)} + for n, e1 in enumerate(exprs): + for e2 in exprs[:n]: + # Equations are connected if they share a symbol + if exprsyms[e1] & exprsyms[e2]: + E.append((e1, e2)) + G = V, E + subexprs = connected_components(G) + if len(subexprs) > 1: + subsols = [] + linear = True + for subexpr in subexprs: + subsyms = set() + for e in subexpr: + subsyms |= exprsyms[e] + subsyms = sorted(subsyms, key = lambda x: sym_indices[x]) + flags['_split'] = False # skip split step + _linear, subsol = _solve_system(subexpr, subsyms, **flags) + if linear: + linear = linear and _linear + if not isinstance(subsol, list): + subsol = [subsol] + subsols.append(subsol) + # Full solution is cartesion product of subsystems + sols = [] + for soldicts in product(*subsols): + sols.append(dict(item for sd in soldicts + for item in sd.items())) + return linear, sols + + polys = [] + dens = set() + failed = [] + result = [] + solved_syms = [] + linear = True + manual = flags.get('manual', False) + checkdens = check = flags.get('check', True) + + for j, g in enumerate(exprs): + dens.update(_simple_dens(g, symbols)) + i, d = _invert(g, *symbols) + if d in symbols: + if linear: + linear = solve_linear(g, 0, [d])[0] == d + g = d - i + g = g.as_numer_denom()[0] + if manual: + failed.append(g) + continue + + poly = g.as_poly(*symbols, extension=True) + + if poly is not None: + polys.append(poly) + else: + failed.append(g) + + if polys: + if all(p.is_linear for p in polys): + n, m = len(polys), len(symbols) + matrix = zeros(n, m + 1) + + for i, poly in enumerate(polys): + for monom, coeff in poly.terms(): + try: + j = monom.index(1) + matrix[i, j] = coeff + except ValueError: + matrix[i, m] = -coeff + + # returns a dictionary ({symbols: values}) or None + if flags.pop('particular', False): + result = minsolve_linear_system(matrix, *symbols, **flags) + else: + result = solve_linear_system(matrix, *symbols, **flags) + result = [result] if result else [] + if failed: + if result: + solved_syms = list(result[0].keys()) # there is only one result dict + else: + solved_syms = [] + # linear doesn't change + else: + linear = False + if len(symbols) > len(polys): + + free = set().union(*[p.free_symbols for p in polys]) + free = list(ordered(free.intersection(symbols))) + got_s = set() + result = [] + for syms in subsets(free, len(polys)): + try: + # returns [], None or list of tuples + res = solve_poly_system(polys, *syms) + if res: + for r in set(res): + skip = False + for r1 in r: + if got_s and any(ss in r1.free_symbols + for ss in got_s): + # sol depends on previously + # solved symbols: discard it + skip = True + if not skip: + got_s.update(syms) + result.append(dict(list(zip(syms, r)))) + except NotImplementedError: + pass + if got_s: + solved_syms = list(got_s) + else: + raise NotImplementedError('no valid subset found') + else: + try: + result = solve_poly_system(polys, *symbols) + if result: + solved_syms = symbols + result = [dict(list(zip(solved_syms, r))) for r in set(result)] + except NotImplementedError: + failed.extend([g.as_expr() for g in polys]) + solved_syms = [] + + # convert None or [] to [{}] + result = result or [{}] + + if failed: + linear = False + # For each failed equation, see if we can solve for one of the + # remaining symbols from that equation. If so, we update the + # solution set and continue with the next failed equation, + # repeating until we are done or we get an equation that can't + # be solved. + def _ok_syms(e, sort=False): + rv = e.free_symbols & legal + + # Solve first for symbols that have lower degree in the equation. + # Ideally we want to solve firstly for symbols that appear linearly + # with rational coefficients e.g. if e = x*y + z then we should + # solve for z first. + def key(sym): + ep = e.as_poly(sym) + if ep is None: + complexity = (S.Infinity, S.Infinity, S.Infinity) + else: + coeff_syms = ep.LC().free_symbols + complexity = (ep.degree(), len(coeff_syms & rv), len(coeff_syms)) + return complexity + (default_sort_key(sym),) + + if sort: + rv = sorted(rv, key=key) + return rv + + legal = set(symbols) # what we are interested in + # sort so equation with the fewest potential symbols is first + u = Dummy() # used in solution checking + for eq in ordered(failed, lambda _: len(_ok_syms(_))): + newresult = [] + bad_results = [] + hit = False + for r in result: + got_s = set() + # update eq with everything that is known so far + eq2 = eq.subs(r) + # if check is True then we see if it satisfies this + # equation, otherwise we just accept it + if check and r: + b = checksol(u, u, eq2, minimal=True) + if b is not None: + # this solution is sufficient to know whether + # it is valid or not so we either accept or + # reject it, then continue + if b: + newresult.append(r) + else: + bad_results.append(r) + continue + # search for a symbol amongst those available that + # can be solved for + ok_syms = _ok_syms(eq2, sort=True) + if not ok_syms: + if r: + newresult.append(r) + break # skip as it's independent of desired symbols + for s in ok_syms: + try: + soln = _vsolve(eq2, s, **flags) + except NotImplementedError: + continue + # put each solution in r and append the now-expanded + # result in the new result list; use copy since the + # solution for s is being added in-place + for sol in soln: + if got_s and any(ss in sol.free_symbols for ss in got_s): + # sol depends on previously solved symbols: discard it + continue + rnew = r.copy() + for k, v in r.items(): + rnew[k] = v.subs(s, sol) + # and add this new solution + rnew[s] = sol + # check that it is independent of previous solutions + iset = set(rnew.items()) + for i in newresult: + if len(i) < len(iset) and not set(i.items()) - iset: + # this is a superset of a known solution that + # is smaller + break + else: + # keep it + newresult.append(rnew) + hit = True + got_s.add(s) + if not hit: + raise NotImplementedError('could not solve %s' % eq2) + else: + result = newresult + for b in bad_results: + if b in result: + result.remove(b) + + if not result: + return False, [] + + # rely on linear/polynomial system solvers to simplify + # XXX the following tests show that the expressions + # returned are not the same as they would be if simplify + # were applied to this: + # sympy/solvers/ode/tests/test_systems/test__classify_linear_system + # sympy/solvers/tests/test_solvers/test_issue_4886 + # so the docs should be updated to reflect that or else + # the following should be `bool(failed) or not linear` + default_simplify = bool(failed) + if flags.get('simplify', default_simplify): + for r in result: + for k in r: + r[k] = simplify(r[k]) + flags['simplify'] = False # don't need to do so in checksol now + + if checkdens: + result = [r for r in result + if not any(checksol(d, r, **flags) for d in dens)] + + if check and not linear: + result = [r for r in result + if not any(checksol(e, r, **flags) is False for e in exprs)] + + result = [r for r in result if r] + return linear, result + + +def solve_linear(lhs, rhs=0, symbols=[], exclude=[]): + r""" + Return a tuple derived from ``f = lhs - rhs`` that is one of + the following: ``(0, 1)``, ``(0, 0)``, ``(symbol, solution)``, ``(n, d)``. + + Explanation + =========== + + ``(0, 1)`` meaning that ``f`` is independent of the symbols in *symbols* + that are not in *exclude*. + + ``(0, 0)`` meaning that there is no solution to the equation amongst the + symbols given. If the first element of the tuple is not zero, then the + function is guaranteed to be dependent on a symbol in *symbols*. + + ``(symbol, solution)`` where symbol appears linearly in the numerator of + ``f``, is in *symbols* (if given), and is not in *exclude* (if given). No + simplification is done to ``f`` other than a ``mul=True`` expansion, so the + solution will correspond strictly to a unique solution. + + ``(n, d)`` where ``n`` and ``d`` are the numerator and denominator of ``f`` + when the numerator was not linear in any symbol of interest; ``n`` will + never be a symbol unless a solution for that symbol was found (in which case + the second element is the solution, not the denominator). + + Examples + ======== + + >>> from sympy import cancel, Pow + + ``f`` is independent of the symbols in *symbols* that are not in + *exclude*: + + >>> from sympy import cos, sin, solve_linear + >>> from sympy.abc import x, y, z + >>> eq = y*cos(x)**2 + y*sin(x)**2 - y # = y*(1 - 1) = 0 + >>> solve_linear(eq) + (0, 1) + >>> eq = cos(x)**2 + sin(x)**2 # = 1 + >>> solve_linear(eq) + (0, 1) + >>> solve_linear(x, exclude=[x]) + (0, 1) + + The variable ``x`` appears as a linear variable in each of the + following: + + >>> solve_linear(x + y**2) + (x, -y**2) + >>> solve_linear(1/x - y**2) + (x, y**(-2)) + + When not linear in ``x`` or ``y`` then the numerator and denominator are + returned: + + >>> solve_linear(x**2/y**2 - 3) + (x**2 - 3*y**2, y**2) + + If the numerator of the expression is a symbol, then ``(0, 0)`` is + returned if the solution for that symbol would have set any + denominator to 0: + + >>> eq = 1/(1/x - 2) + >>> eq.as_numer_denom() + (x, 1 - 2*x) + >>> solve_linear(eq) + (0, 0) + + But automatic rewriting may cause a symbol in the denominator to + appear in the numerator so a solution will be returned: + + >>> (1/x)**-1 + x + >>> solve_linear((1/x)**-1) + (x, 0) + + Use an unevaluated expression to avoid this: + + >>> solve_linear(Pow(1/x, -1, evaluate=False)) + (0, 0) + + If ``x`` is allowed to cancel in the following expression, then it + appears to be linear in ``x``, but this sort of cancellation is not + done by ``solve_linear`` so the solution will always satisfy the + original expression without causing a division by zero error. + + >>> eq = x**2*(1/x - z**2/x) + >>> solve_linear(cancel(eq)) + (x, 0) + >>> solve_linear(eq) + (x**2*(1 - z**2), x) + + A list of symbols for which a solution is desired may be given: + + >>> solve_linear(x + y + z, symbols=[y]) + (y, -x - z) + + A list of symbols to ignore may also be given: + + >>> solve_linear(x + y + z, exclude=[x]) + (y, -x - z) + + (A solution for ``y`` is obtained because it is the first variable + from the canonically sorted list of symbols that had a linear + solution.) + + """ + if isinstance(lhs, Eq): + if rhs: + raise ValueError(filldedent(''' + If lhs is an Equality, rhs must be 0 but was %s''' % rhs)) + rhs = lhs.rhs + lhs = lhs.lhs + dens = None + eq = lhs - rhs + n, d = eq.as_numer_denom() + if not n: + return S.Zero, S.One + + free = n.free_symbols + if not symbols: + symbols = free + else: + bad = [s for s in symbols if not s.is_Symbol] + if bad: + if len(bad) == 1: + bad = bad[0] + if len(symbols) == 1: + eg = 'solve(%s, %s)' % (eq, symbols[0]) + else: + eg = 'solve(%s, *%s)' % (eq, list(symbols)) + raise ValueError(filldedent(''' + solve_linear only handles symbols, not %s. To isolate + non-symbols use solve, e.g. >>> %s <<<. + ''' % (bad, eg))) + symbols = free.intersection(symbols) + symbols = symbols.difference(exclude) + if not symbols: + return S.Zero, S.One + + # derivatives are easy to do but tricky to analyze to see if they + # are going to disallow a linear solution, so for simplicity we + # just evaluate the ones that have the symbols of interest + derivs = defaultdict(list) + for der in n.atoms(Derivative): + csym = der.free_symbols & symbols + for c in csym: + derivs[c].append(der) + + all_zero = True + for xi in sorted(symbols, key=default_sort_key): # canonical order + # if there are derivatives in this var, calculate them now + if isinstance(derivs[xi], list): + derivs[xi] = {der: der.doit() for der in derivs[xi]} + newn = n.subs(derivs[xi]) + dnewn_dxi = newn.diff(xi) + # dnewn_dxi can be nonzero if it survives differentation by any + # of its free symbols + free = dnewn_dxi.free_symbols + if dnewn_dxi and (not free or any(dnewn_dxi.diff(s) for s in free) or free == symbols): + all_zero = False + if dnewn_dxi is S.NaN: + break + if xi not in dnewn_dxi.free_symbols: + vi = -1/dnewn_dxi*(newn.subs(xi, 0)) + if dens is None: + dens = _simple_dens(eq, symbols) + if not any(checksol(di, {xi: vi}, minimal=True) is True + for di in dens): + # simplify any trivial integral + irep = [(i, i.doit()) for i in vi.atoms(Integral) if + i.function.is_number] + # do a slight bit of simplification + vi = expand_mul(vi.subs(irep)) + return xi, vi + if all_zero: + return S.Zero, S.One + if n.is_Symbol: # no solution for this symbol was found + return S.Zero, S.Zero + return n, d + + +def minsolve_linear_system(system, *symbols, **flags): + r""" + Find a particular solution to a linear system. + + Explanation + =========== + + In particular, try to find a solution with the minimal possible number + of non-zero variables using a naive algorithm with exponential complexity. + If ``quick=True``, a heuristic is used. + + """ + quick = flags.get('quick', False) + # Check if there are any non-zero solutions at all + s0 = solve_linear_system(system, *symbols, **flags) + if not s0 or all(v == 0 for v in s0.values()): + return s0 + if quick: + # We just solve the system and try to heuristically find a nice + # solution. + s = solve_linear_system(system, *symbols) + def update(determined, solution): + delete = [] + for k, v in solution.items(): + solution[k] = v.subs(determined) + if not solution[k].free_symbols: + delete.append(k) + determined[k] = solution[k] + for k in delete: + del solution[k] + determined = {} + update(determined, s) + while s: + # NOTE sort by default_sort_key to get deterministic result + k = max((k for k in s.values()), + key=lambda x: (len(x.free_symbols), default_sort_key(x))) + kfree = k.free_symbols + x = next(reversed(list(ordered(kfree)))) + if len(kfree) != 1: + determined[x] = S.Zero + else: + val = _vsolve(k, x, check=False)[0] + if not val and not any(v.subs(x, val) for v in s.values()): + determined[x] = S.One + else: + determined[x] = val + update(determined, s) + return determined + else: + # We try to select n variables which we want to be non-zero. + # All others will be assumed zero. We try to solve the modified system. + # If there is a non-trivial solution, just set the free variables to + # one. If we do this for increasing n, trying all combinations of + # variables, we will find an optimal solution. + # We speed up slightly by starting at one less than the number of + # variables the quick method manages. + N = len(symbols) + bestsol = minsolve_linear_system(system, *symbols, quick=True) + n0 = len([x for x in bestsol.values() if x != 0]) + for n in range(n0 - 1, 1, -1): + debugf('minsolve: %s', n) + thissol = None + for nonzeros in combinations(range(N), n): + subm = Matrix([system.col(i).T for i in nonzeros] + [system.col(-1).T]).T + s = solve_linear_system(subm, *[symbols[i] for i in nonzeros]) + if s and not all(v == 0 for v in s.values()): + subs = [(symbols[v], S.One) for v in nonzeros] + for k, v in s.items(): + s[k] = v.subs(subs) + for sym in symbols: + if sym not in s: + if symbols.index(sym) in nonzeros: + s[sym] = S.One + else: + s[sym] = S.Zero + thissol = s + break + if thissol is None: + break + bestsol = thissol + return bestsol + + +def solve_linear_system(system, *symbols, **flags): + r""" + Solve system of $N$ linear equations with $M$ variables, which means + both under- and overdetermined systems are supported. + + Explanation + =========== + + The possible number of solutions is zero, one, or infinite. Respectively, + this procedure will return None or a dictionary with solutions. In the + case of underdetermined systems, all arbitrary parameters are skipped. + This may cause a situation in which an empty dictionary is returned. + In that case, all symbols can be assigned arbitrary values. + + Input to this function is a $N\times M + 1$ matrix, which means it has + to be in augmented form. If you prefer to enter $N$ equations and $M$ + unknowns then use ``solve(Neqs, *Msymbols)`` instead. Note: a local + copy of the matrix is made by this routine so the matrix that is + passed will not be modified. + + The algorithm used here is fraction-free Gaussian elimination, + which results, after elimination, in an upper-triangular matrix. + Then solutions are found using back-substitution. This approach + is more efficient and compact than the Gauss-Jordan method. + + Examples + ======== + + >>> from sympy import Matrix, solve_linear_system + >>> from sympy.abc import x, y + + Solve the following system:: + + x + 4 y == 2 + -2 x + y == 14 + + >>> system = Matrix(( (1, 4, 2), (-2, 1, 14))) + >>> solve_linear_system(system, x, y) + {x: -6, y: 2} + + A degenerate system returns an empty dictionary: + + >>> system = Matrix(( (0,0,0), (0,0,0) )) + >>> solve_linear_system(system, x, y) + {} + + """ + assert system.shape[1] == len(symbols) + 1 + + # This is just a wrapper for solve_lin_sys + eqs = list(system * Matrix(symbols + (-1,))) + eqs, ring = sympy_eqs_to_ring(eqs, symbols) + sol = solve_lin_sys(eqs, ring, _raw=False) + if sol is not None: + sol = {sym:val for sym, val in sol.items() if sym != val} + return sol + + +def solve_undetermined_coeffs(equ, coeffs, *syms, **flags): + r""" + Solve a system of equations in $k$ parameters that is formed by + matching coefficients in variables ``coeffs`` that are on + factors dependent on the remaining variables (or those given + explicitly by ``syms``. + + Explanation + =========== + + The result of this function is a dictionary with symbolic values of those + parameters with respect to coefficients in $q$ -- empty if there + is no solution or coefficients do not appear in the equation -- else + None (if the system was not recognized). If there is more than one + solution, the solutions are passed as a list. The output can be modified using + the same semantics as for `solve` since the flags that are passed are sent + directly to `solve` so, for example the flag ``dict=True`` will always return a list + of solutions as dictionaries. + + This function accepts both Equality and Expr class instances. + The solving process is most efficient when symbols are specified + in addition to parameters to be determined, but an attempt to + determine them (if absent) will be made. If an expected solution is not + obtained (and symbols were not specified) try specifying them. + + Examples + ======== + + >>> from sympy import Eq, solve_undetermined_coeffs + >>> from sympy.abc import a, b, c, h, p, k, x, y + + >>> solve_undetermined_coeffs(Eq(a*x + a + b, x/2), [a, b], x) + {a: 1/2, b: -1/2} + >>> solve_undetermined_coeffs(a - 2, [a]) + {a: 2} + + The equation can be nonlinear in the symbols: + + >>> X, Y, Z = y, x**y, y*x**y + >>> eq = a*X + b*Y + c*Z - X - 2*Y - 3*Z + >>> coeffs = a, b, c + >>> syms = x, y + >>> solve_undetermined_coeffs(eq, coeffs, syms) + {a: 1, b: 2, c: 3} + + And the system can be nonlinear in coefficients, too, but if + there is only a single solution, it will be returned as a + dictionary: + + >>> eq = a*x**2 + b*x + c - ((x - h)**2 + 4*p*k)/4/p + >>> solve_undetermined_coeffs(eq, (h, p, k), x) + {h: -b/(2*a), k: (4*a*c - b**2)/(4*a), p: 1/(4*a)} + + Multiple solutions are always returned in a list: + + >>> solve_undetermined_coeffs(a**2*x + b - x, [a, b], x) + [{a: -1, b: 0}, {a: 1, b: 0}] + + Using flag ``dict=True`` (in keeping with semantics in :func:`~.solve`) + will force the result to always be a list with any solutions + as elements in that list. + + >>> solve_undetermined_coeffs(a*x - 2*x, [a], dict=True) + [{a: 2}] + """ + if not (coeffs and all(i.is_Symbol for i in coeffs)): + raise ValueError('must provide symbols for coeffs') + + if isinstance(equ, Eq): + eq = equ.lhs - equ.rhs + else: + eq = equ + + ceq = cancel(eq) + xeq = _mexpand(ceq.as_numer_denom()[0], recursive=True) + + free = xeq.free_symbols + coeffs = free & set(coeffs) + if not coeffs: + return ([], {}) if flags.get('set', None) else [] # solve(0, x) -> [] + + if not syms: + # e.g. A*exp(x) + B - (exp(x) + y) separated into parts that + # don't/do depend on coeffs gives + # -(exp(x) + y), A*exp(x) + B + # then see what symbols are common to both + # {x} = {x, A, B} - {x, y} + ind, dep = xeq.as_independent(*coeffs, as_Add=True) + dfree = dep.free_symbols + syms = dfree & ind.free_symbols + if not syms: + # but if the system looks like (a + b)*x + b - c + # then {} = {a, b, x} - c + # so calculate {x} = {a, b, x} - {a, b} + syms = dfree - set(coeffs) + if not syms: + syms = [Dummy()] + else: + if len(syms) == 1 and iterable(syms[0]): + syms = syms[0] + e, s, _ = recast_to_symbols([xeq], syms) + xeq = e[0] + syms = s + + # find the functional forms in which symbols appear + + gens = set(xeq.as_coefficients_dict(*syms).keys()) - {1} + cset = set(coeffs) + if any(g.has_xfree(cset) for g in gens): + return # a generator contained a coefficient symbol + + # make sure we are working with symbols for generators + + e, gens, _ = recast_to_symbols([xeq], list(gens)) + xeq = e[0] + + # collect coefficients in front of generators + + system = list(collect(xeq, gens, evaluate=False).values()) + + # get a solution + + soln = solve(system, coeffs, **flags) + + # unpack unless told otherwise if length is 1 + + settings = flags.get('dict', None) or flags.get('set', None) + if type(soln) is dict or settings or len(soln) != 1: + return soln + return soln[0] + + +def solve_linear_system_LU(matrix, syms): + """ + Solves the augmented matrix system using ``LUsolve`` and returns a + dictionary in which solutions are keyed to the symbols of *syms* as ordered. + + Explanation + =========== + + The matrix must be invertible. + + Examples + ======== + + >>> from sympy import Matrix, solve_linear_system_LU + >>> from sympy.abc import x, y, z + + >>> solve_linear_system_LU(Matrix([ + ... [1, 2, 0, 1], + ... [3, 2, 2, 1], + ... [2, 0, 0, 1]]), [x, y, z]) + {x: 1/2, y: 1/4, z: -1/2} + + See Also + ======== + + LUsolve + + """ + if matrix.rows != matrix.cols - 1: + raise ValueError("Rows should be equal to columns - 1") + A = matrix[:matrix.rows, :matrix.rows] + b = matrix[:, matrix.cols - 1:] + soln = A.LUsolve(b) + solutions = {} + for i in range(soln.rows): + solutions[syms[i]] = soln[i, 0] + return solutions + + +def det_perm(M): + """ + Return the determinant of *M* by using permutations to select factors. + + Explanation + =========== + + For sizes larger than 8 the number of permutations becomes prohibitively + large, or if there are no symbols in the matrix, it is better to use the + standard determinant routines (e.g., ``M.det()``.) + + See Also + ======== + + det_minor + det_quick + + """ + args = [] + s = True + n = M.rows + list_ = M.flat() + for perm in generate_bell(n): + fac = [] + idx = 0 + for j in perm: + fac.append(list_[idx + j]) + idx += n + term = Mul(*fac) # disaster with unevaluated Mul -- takes forever for n=7 + args.append(term if s else -term) + s = not s + return Add(*args) + + +def det_minor(M): + """ + Return the ``det(M)`` computed from minors without + introducing new nesting in products. + + See Also + ======== + + det_perm + det_quick + + """ + n = M.rows + if n == 2: + return M[0, 0]*M[1, 1] - M[1, 0]*M[0, 1] + else: + return sum((1, -1)[i % 2]*Add(*[M[0, i]*d for d in + Add.make_args(det_minor(M.minor_submatrix(0, i)))]) + if M[0, i] else S.Zero for i in range(n)) + + +def det_quick(M, method=None): + """ + Return ``det(M)`` assuming that either + there are lots of zeros or the size of the matrix + is small. If this assumption is not met, then the normal + Matrix.det function will be used with method = ``method``. + + See Also + ======== + + det_minor + det_perm + + """ + if any(i.has(Symbol) for i in M): + if M.rows < 8 and all(i.has(Symbol) for i in M): + return det_perm(M) + return det_minor(M) + else: + return M.det(method=method) if method else M.det() + + +def inv_quick(M): + """Return the inverse of ``M``, assuming that either + there are lots of zeros or the size of the matrix + is small. + """ + if not all(i.is_Number for i in M): + if not any(i.is_Number for i in M): + det = lambda _: det_perm(_) + else: + det = lambda _: det_minor(_) + else: + return M.inv() + n = M.rows + d = det(M) + if d == S.Zero: + raise NonInvertibleMatrixError("Matrix det == 0; not invertible") + ret = zeros(n) + s1 = -1 + for i in range(n): + s = s1 = -s1 + for j in range(n): + di = det(M.minor_submatrix(i, j)) + ret[j, i] = s*di/d + s = -s + return ret + + +# these are functions that have multiple inverse values per period +multi_inverses = { + sin: lambda x: (asin(x), S.Pi - asin(x)), + cos: lambda x: (acos(x), 2*S.Pi - acos(x)), +} + + +def _vsolve(e, s, **flags): + """return list of scalar values for the solution of e for symbol s""" + return [i[s] for i in _solve(e, s, **flags)] + + +def _tsolve(eq, sym, **flags): + """ + Helper for ``_solve`` that solves a transcendental equation with respect + to the given symbol. Various equations containing powers and logarithms, + can be solved. + + There is currently no guarantee that all solutions will be returned or + that a real solution will be favored over a complex one. + + Either a list of potential solutions will be returned or None will be + returned (in the case that no method was known to get a solution + for the equation). All other errors (like the inability to cast an + expression as a Poly) are unhandled. + + Examples + ======== + + >>> from sympy import log, ordered + >>> from sympy.solvers.solvers import _tsolve as tsolve + >>> from sympy.abc import x + + >>> list(ordered(tsolve(3**(2*x + 5) - 4, x))) + [-5/2 + log(2)/log(3), (-5*log(3)/2 + log(2) + I*pi)/log(3)] + + >>> tsolve(log(x) + 2*x, x) + [LambertW(2)/2] + + """ + if 'tsolve_saw' not in flags: + flags['tsolve_saw'] = [] + if eq in flags['tsolve_saw']: + return None + else: + flags['tsolve_saw'].append(eq) + + rhs, lhs = _invert(eq, sym) + + if lhs == sym: + return [rhs] + try: + if lhs.is_Add: + # it's time to try factoring; powdenest is used + # to try get powers in standard form for better factoring + f = factor(powdenest(lhs - rhs)) + if f.is_Mul: + return _vsolve(f, sym, **flags) + if rhs: + f = logcombine(lhs, force=flags.get('force', True)) + if f.count(log) != lhs.count(log): + if isinstance(f, log): + return _vsolve(f.args[0] - exp(rhs), sym, **flags) + return _tsolve(f - rhs, sym, **flags) + + elif lhs.is_Pow: + if lhs.exp.is_Integer: + if lhs - rhs != eq: + return _vsolve(lhs - rhs, sym, **flags) + + if sym not in lhs.exp.free_symbols: + return _vsolve(lhs.base - rhs**(1/lhs.exp), sym, **flags) + + # _tsolve calls this with Dummy before passing the actual number in. + if any(t.is_Dummy for t in rhs.free_symbols): + raise NotImplementedError # _tsolve will call here again... + + # a ** g(x) == 0 + if not rhs: + # f(x)**g(x) only has solutions where f(x) == 0 and g(x) != 0 at + # the same place + sol_base = _vsolve(lhs.base, sym, **flags) + return [s for s in sol_base if lhs.exp.subs(sym, s) != 0] # XXX use checksol here? + + # a ** g(x) == b + if not lhs.base.has(sym): + if lhs.base == 0: + return _vsolve(lhs.exp, sym, **flags) if rhs != 0 else [] + + # Gets most solutions... + if lhs.base == rhs.as_base_exp()[0]: + # handles case when bases are equal + sol = _vsolve(lhs.exp - rhs.as_base_exp()[1], sym, **flags) + else: + # handles cases when bases are not equal and exp + # may or may not be equal + f = exp(log(lhs.base)*lhs.exp) - exp(log(rhs)) + sol = _vsolve(f, sym, **flags) + + # Check for duplicate solutions + def equal(expr1, expr2): + _ = Dummy() + eq = checksol(expr1 - _, _, expr2) + if eq is None: + if nsimplify(expr1) != nsimplify(expr2): + return False + # they might be coincidentally the same + # so check more rigorously + eq = expr1.equals(expr2) # XXX expensive but necessary? + return eq + + # Guess a rational exponent + e_rat = nsimplify(log(abs(rhs))/log(abs(lhs.base))) + e_rat = simplify(posify(e_rat)[0]) + n, d = fraction(e_rat) + if expand(lhs.base**n - rhs**d) == 0: + sol = [s for s in sol if not equal(lhs.exp.subs(sym, s), e_rat)] + sol.extend(_vsolve(lhs.exp - e_rat, sym, **flags)) + + return list(set(sol)) + + # f(x) ** g(x) == c + else: + sol = [] + logform = lhs.exp*log(lhs.base) - log(rhs) + if logform != lhs - rhs: + try: + sol.extend(_vsolve(logform, sym, **flags)) + except NotImplementedError: + pass + + # Collect possible solutions and check with substitution later. + check = [] + if rhs == 1: + # f(x) ** g(x) = 1 -- g(x)=0 or f(x)=+-1 + check.extend(_vsolve(lhs.exp, sym, **flags)) + check.extend(_vsolve(lhs.base - 1, sym, **flags)) + check.extend(_vsolve(lhs.base + 1, sym, **flags)) + elif rhs.is_Rational: + for d in (i for i in divisors(abs(rhs.p)) if i != 1): + e, t = integer_log(rhs.p, d) + if not t: + continue # rhs.p != d**b + for s in divisors(abs(rhs.q)): + if s**e== rhs.q: + r = Rational(d, s) + check.extend(_vsolve(lhs.base - r, sym, **flags)) + check.extend(_vsolve(lhs.base + r, sym, **flags)) + check.extend(_vsolve(lhs.exp - e, sym, **flags)) + elif rhs.is_irrational: + b_l, e_l = lhs.base.as_base_exp() + n, d = (e_l*lhs.exp).as_numer_denom() + b, e = sqrtdenest(rhs).as_base_exp() + check = [sqrtdenest(i) for i in (_vsolve(lhs.base - b, sym, **flags))] + check.extend([sqrtdenest(i) for i in (_vsolve(lhs.exp - e, sym, **flags))]) + if e_l*d != 1: + check.extend(_vsolve(b_l**n - rhs**(e_l*d), sym, **flags)) + for s in check: + ok = checksol(eq, sym, s) + if ok is None: + ok = eq.subs(sym, s).equals(0) + if ok: + sol.append(s) + return list(set(sol)) + + elif lhs.is_Function and len(lhs.args) == 1: + if lhs.func in multi_inverses: + # sin(x) = 1/3 -> x - asin(1/3) & x - (pi - asin(1/3)) + soln = [] + for i in multi_inverses[type(lhs)](rhs): + soln.extend(_vsolve(lhs.args[0] - i, sym, **flags)) + return list(set(soln)) + elif lhs.func == LambertW: + return _vsolve(lhs.args[0] - rhs*exp(rhs), sym, **flags) + + rewrite = lhs.rewrite(exp) + rewrite = rebuild(rewrite) # avoid rewrites involving evaluate=False + if rewrite != lhs: + return _vsolve(rewrite - rhs, sym, **flags) + except NotImplementedError: + pass + + # maybe it is a lambert pattern + if flags.pop('bivariate', True): + # lambert forms may need some help being recognized, e.g. changing + # 2**(3*x) + x**3*log(2)**3 + 3*x**2*log(2)**2 + 3*x*log(2) + 1 + # to 2**(3*x) + (x*log(2) + 1)**3 + + # make generator in log have exponent of 1 + logs = eq.atoms(log) + spow = min( + {i.exp for j in logs for i in j.atoms(Pow) + if i.base == sym} or {1}) + if spow != 1: + p = sym**spow + u = Dummy('bivariate-cov') + ueq = eq.subs(p, u) + if not ueq.has_free(sym): + sol = _vsolve(ueq, u, **flags) + inv = _vsolve(p - u, sym) + return [i.subs(u, s) for i in inv for s in sol] + + g = _filtered_gens(eq.as_poly(), sym) + up_or_log = set() + for gi in g: + if isinstance(gi, (exp, log)) or (gi.is_Pow and gi.base == S.Exp1): + up_or_log.add(gi) + elif gi.is_Pow: + gisimp = powdenest(expand_power_exp(gi)) + if gisimp.is_Pow and sym in gisimp.exp.free_symbols: + up_or_log.add(gi) + eq_down = expand_log(expand_power_exp(eq)).subs( + dict(list(zip(up_or_log, [0]*len(up_or_log))))) + eq = expand_power_exp(factor(eq_down, deep=True) + (eq - eq_down)) + rhs, lhs = _invert(eq, sym) + if lhs.has(sym): + try: + poly = lhs.as_poly() + g = _filtered_gens(poly, sym) + _eq = lhs - rhs + sols = _solve_lambert(_eq, sym, g) + # use a simplified form if it satisfies eq + # and has fewer operations + for n, s in enumerate(sols): + ns = nsimplify(s) + if ns != s and ns.count_ops() <= s.count_ops(): + ok = checksol(_eq, sym, ns) + if ok is None: + ok = _eq.subs(sym, ns).equals(0) + if ok: + sols[n] = ns + return sols + except NotImplementedError: + # maybe it's a convoluted function + if len(g) == 2: + try: + gpu = bivariate_type(lhs - rhs, *g) + if gpu is None: + raise NotImplementedError + g, p, u = gpu + flags['bivariate'] = False + inversion = _tsolve(g - u, sym, **flags) + if inversion: + sol = _vsolve(p, u, **flags) + return list({i.subs(u, s) + for i in inversion for s in sol}) + except NotImplementedError: + pass + else: + pass + + if flags.pop('force', True): + flags['force'] = False + pos, reps = posify(lhs - rhs) + if rhs == S.ComplexInfinity: + return [] + for u, s in reps.items(): + if s == sym: + break + else: + u = sym + if pos.has(u): + try: + soln = _vsolve(pos, u, **flags) + return [s.subs(reps) for s in soln] + except NotImplementedError: + pass + else: + pass # here for coverage + + return # here for coverage + + +# TODO: option for calculating J numerically + +@conserve_mpmath_dps +def nsolve(*args, dict=False, **kwargs): + r""" + Solve a nonlinear equation system numerically: ``nsolve(f, [args,] x0, + modules=['mpmath'], **kwargs)``. + + Explanation + =========== + + ``f`` is a vector function of symbolic expressions representing the system. + *args* are the variables. If there is only one variable, this argument can + be omitted. ``x0`` is a starting vector close to a solution. + + Use the modules keyword to specify which modules should be used to + evaluate the function and the Jacobian matrix. Make sure to use a module + that supports matrices. For more information on the syntax, please see the + docstring of ``lambdify``. + + If the keyword arguments contain ``dict=True`` (default is False) ``nsolve`` + will return a list (perhaps empty) of solution mappings. This might be + especially useful if you want to use ``nsolve`` as a fallback to solve since + using the dict argument for both methods produces return values of + consistent type structure. Please note: to keep this consistent with + ``solve``, the solution will be returned in a list even though ``nsolve`` + (currently at least) only finds one solution at a time. + + Overdetermined systems are supported. + + Examples + ======== + + >>> from sympy import Symbol, nsolve + >>> import mpmath + >>> mpmath.mp.dps = 15 + >>> x1 = Symbol('x1') + >>> x2 = Symbol('x2') + >>> f1 = 3 * x1**2 - 2 * x2**2 - 1 + >>> f2 = x1**2 - 2 * x1 + x2**2 + 2 * x2 - 8 + >>> print(nsolve((f1, f2), (x1, x2), (-1, 1))) + Matrix([[-1.19287309935246], [1.27844411169911]]) + + For one-dimensional functions the syntax is simplified: + + >>> from sympy import sin, nsolve + >>> from sympy.abc import x + >>> nsolve(sin(x), x, 2) + 3.14159265358979 + >>> nsolve(sin(x), 2) + 3.14159265358979 + + To solve with higher precision than the default, use the prec argument: + + >>> from sympy import cos + >>> nsolve(cos(x) - x, 1) + 0.739085133215161 + >>> nsolve(cos(x) - x, 1, prec=50) + 0.73908513321516064165531208767387340401341175890076 + >>> cos(_) + 0.73908513321516064165531208767387340401341175890076 + + To solve for complex roots of real functions, a nonreal initial point + must be specified: + + >>> from sympy import I + >>> nsolve(x**2 + 2, I) + 1.4142135623731*I + + ``mpmath.findroot`` is used and you can find their more extensive + documentation, especially concerning keyword parameters and + available solvers. Note, however, that functions which are very + steep near the root, the verification of the solution may fail. In + this case you should use the flag ``verify=False`` and + independently verify the solution. + + >>> from sympy import cos, cosh + >>> f = cos(x)*cosh(x) - 1 + >>> nsolve(f, 3.14*100) + Traceback (most recent call last): + ... + ValueError: Could not find root within given tolerance. (1.39267e+230 > 2.1684e-19) + >>> ans = nsolve(f, 3.14*100, verify=False); ans + 312.588469032184 + >>> f.subs(x, ans).n(2) + 2.1e+121 + >>> (f/f.diff(x)).subs(x, ans).n(2) + 7.4e-15 + + One might safely skip the verification if bounds of the root are known + and a bisection method is used: + + >>> bounds = lambda i: (3.14*i, 3.14*(i + 1)) + >>> nsolve(f, bounds(100), solver='bisect', verify=False) + 315.730061685774 + + Alternatively, a function may be better behaved when the + denominator is ignored. Since this is not always the case, however, + the decision of what function to use is left to the discretion of + the user. + + >>> eq = x**2/(1 - x)/(1 - 2*x)**2 - 100 + >>> nsolve(eq, 0.46) + Traceback (most recent call last): + ... + ValueError: Could not find root within given tolerance. (10000 > 2.1684e-19) + Try another starting point or tweak arguments. + >>> nsolve(eq.as_numer_denom()[0], 0.46) + 0.46792545969349058 + + """ + # there are several other SymPy functions that use method= so + # guard against that here + if 'method' in kwargs: + raise ValueError(filldedent(''' + Keyword "method" should not be used in this context. When using + some mpmath solvers directly, the keyword "method" is + used, but when using nsolve (and findroot) the keyword to use is + "solver".''')) + + if 'prec' in kwargs: + import mpmath + mpmath.mp.dps = kwargs.pop('prec') + + # keyword argument to return result as a dictionary + as_dict = dict + from builtins import dict # to unhide the builtin + + # interpret arguments + if len(args) == 3: + f = args[0] + fargs = args[1] + x0 = args[2] + if iterable(fargs) and iterable(x0): + if len(x0) != len(fargs): + raise TypeError('nsolve expected exactly %i guess vectors, got %i' + % (len(fargs), len(x0))) + elif len(args) == 2: + f = args[0] + fargs = None + x0 = args[1] + if iterable(f): + raise TypeError('nsolve expected 3 arguments, got 2') + elif len(args) < 2: + raise TypeError('nsolve expected at least 2 arguments, got %i' + % len(args)) + else: + raise TypeError('nsolve expected at most 3 arguments, got %i' + % len(args)) + modules = kwargs.get('modules', ['mpmath']) + if iterable(f): + f = list(f) + for i, fi in enumerate(f): + if isinstance(fi, Eq): + f[i] = fi.lhs - fi.rhs + f = Matrix(f).T + if iterable(x0): + x0 = list(x0) + if not isinstance(f, Matrix): + # assume it's a SymPy expression + if isinstance(f, Eq): + f = f.lhs - f.rhs + elif f.is_Relational: + raise TypeError('nsolve cannot accept inequalities') + syms = f.free_symbols + if fargs is None: + fargs = syms.copy().pop() + if not (len(syms) == 1 and (fargs in syms or fargs[0] in syms)): + raise ValueError(filldedent(''' + expected a one-dimensional and numerical function''')) + + # the function is much better behaved if there is no denominator + # but sending the numerator is left to the user since sometimes + # the function is better behaved when the denominator is present + # e.g., issue 11768 + + f = lambdify(fargs, f, modules) + x = sympify(findroot(f, x0, **kwargs)) + if as_dict: + return [{fargs: x}] + return x + + if len(fargs) > f.cols: + raise NotImplementedError(filldedent(''' + need at least as many equations as variables''')) + verbose = kwargs.get('verbose', False) + if verbose: + print('f(x):') + print(f) + # derive Jacobian + J = f.jacobian(fargs) + if verbose: + print('J(x):') + print(J) + # create functions + f = lambdify(fargs, f.T, modules) + J = lambdify(fargs, J, modules) + # solve the system numerically + x = findroot(f, x0, J=J, **kwargs) + if as_dict: + return [dict(zip(fargs, [sympify(xi) for xi in x]))] + return Matrix(x) + + +def _invert(eq, *symbols, **kwargs): + """ + Return tuple (i, d) where ``i`` is independent of *symbols* and ``d`` + contains symbols. + + Explanation + =========== + + ``i`` and ``d`` are obtained after recursively using algebraic inversion + until an uninvertible ``d`` remains. If there are no free symbols then + ``d`` will be zero. Some (but not necessarily all) solutions to the + expression ``i - d`` will be related to the solutions of the original + expression. + + Examples + ======== + + >>> from sympy.solvers.solvers import _invert as invert + >>> from sympy import sqrt, cos + >>> from sympy.abc import x, y + >>> invert(x - 3) + (3, x) + >>> invert(3) + (3, 0) + >>> invert(2*cos(x) - 1) + (1/2, cos(x)) + >>> invert(sqrt(x) - 3) + (3, sqrt(x)) + >>> invert(sqrt(x) + y, x) + (-y, sqrt(x)) + >>> invert(sqrt(x) + y, y) + (-sqrt(x), y) + >>> invert(sqrt(x) + y, x, y) + (0, sqrt(x) + y) + + If there is more than one symbol in a power's base and the exponent + is not an Integer, then the principal root will be used for the + inversion: + + >>> invert(sqrt(x + y) - 2) + (4, x + y) + >>> invert(sqrt(x + y) + 2) # note +2 instead of -2 + (4, x + y) + + If the exponent is an Integer, setting ``integer_power`` to True + will force the principal root to be selected: + + >>> invert(x**2 - 4, integer_power=True) + (2, x) + + """ + eq = sympify(eq) + if eq.args: + # make sure we are working with flat eq + eq = eq.func(*eq.args) + free = eq.free_symbols + if not symbols: + symbols = free + if not free & set(symbols): + return eq, S.Zero + + dointpow = bool(kwargs.get('integer_power', False)) + + lhs = eq + rhs = S.Zero + while True: + was = lhs + while True: + indep, dep = lhs.as_independent(*symbols) + + # dep + indep == rhs + if lhs.is_Add: + # this indicates we have done it all + if indep.is_zero: + break + + lhs = dep + rhs -= indep + + # dep * indep == rhs + else: + # this indicates we have done it all + if indep is S.One: + break + + lhs = dep + rhs /= indep + + # collect like-terms in symbols + if lhs.is_Add: + terms = {} + for a in lhs.args: + i, d = a.as_independent(*symbols) + terms.setdefault(d, []).append(i) + if any(len(v) > 1 for v in terms.values()): + args = [] + for d, i in terms.items(): + if len(i) > 1: + args.append(Add(*i)*d) + else: + args.append(i[0]*d) + lhs = Add(*args) + + # if it's a two-term Add with rhs = 0 and two powers we can get the + # dependent terms together, e.g. 3*f(x) + 2*g(x) -> f(x)/g(x) = -2/3 + if lhs.is_Add and not rhs and len(lhs.args) == 2 and \ + not lhs.is_polynomial(*symbols): + a, b = ordered(lhs.args) + ai, ad = a.as_independent(*symbols) + bi, bd = b.as_independent(*symbols) + if any(_ispow(i) for i in (ad, bd)): + a_base, a_exp = ad.as_base_exp() + b_base, b_exp = bd.as_base_exp() + if a_base == b_base and a_exp.extract_additively(b_exp) is None: + # a = -b and exponents do not have canceling terms/factors + # e.g. if exponents were 3*x and x then the ratio would have + # an exponent of 2*x: one of the roots would be lost + rat = powsimp(powdenest(ad/bd)) + lhs = rat + rhs = -bi/ai + else: + rat = ad/bd + _lhs = powsimp(ad/bd) + if _lhs != rat: + lhs = _lhs + rhs = -bi/ai + elif ai == -bi: + if isinstance(ad, Function) and ad.func == bd.func: + if len(ad.args) == len(bd.args) == 1: + lhs = ad.args[0] - bd.args[0] + elif len(ad.args) == len(bd.args): + # should be able to solve + # f(x, y) - f(2 - x, 0) == 0 -> x == 1 + raise NotImplementedError( + 'equal function with more than 1 argument') + else: + raise ValueError( + 'function with different numbers of args') + + elif lhs.is_Mul and any(_ispow(a) for a in lhs.args): + lhs = powsimp(powdenest(lhs)) + + if lhs.is_Function: + if hasattr(lhs, 'inverse') and lhs.inverse() is not None and len(lhs.args) == 1: + # -1 + # f(x) = g -> x = f (g) + # + # /!\ inverse should not be defined if there are multiple values + # for the function -- these are handled in _tsolve + # + rhs = lhs.inverse()(rhs) + lhs = lhs.args[0] + elif isinstance(lhs, atan2): + y, x = lhs.args + lhs = 2*atan(y/(sqrt(x**2 + y**2) + x)) + elif lhs.func == rhs.func: + if len(lhs.args) == len(rhs.args) == 1: + lhs = lhs.args[0] + rhs = rhs.args[0] + elif len(lhs.args) == len(rhs.args): + # should be able to solve + # f(x, y) == f(2, 3) -> x == 2 + # f(x, x + y) == f(2, 3) -> x == 2 + raise NotImplementedError( + 'equal function with more than 1 argument') + else: + raise ValueError( + 'function with different numbers of args') + + + if rhs and lhs.is_Pow and lhs.exp.is_Integer and lhs.exp < 0: + lhs = 1/lhs + rhs = 1/rhs + + # base**a = b -> base = b**(1/a) if + # a is an Integer and dointpow=True (this gives real branch of root) + # a is not an Integer and the equation is multivariate and the + # base has more than 1 symbol in it + # The rationale for this is that right now the multi-system solvers + # doesn't try to resolve generators to see, for example, if the whole + # system is written in terms of sqrt(x + y) so it will just fail, so we + # do that step here. + if lhs.is_Pow and ( + lhs.exp.is_Integer and dointpow or not lhs.exp.is_Integer and + len(symbols) > 1 and len(lhs.base.free_symbols & set(symbols)) > 1): + rhs = rhs**(1/lhs.exp) + lhs = lhs.base + + if lhs == was: + break + return rhs, lhs + + +def unrad(eq, *syms, **flags): + """ + Remove radicals with symbolic arguments and return (eq, cov), + None, or raise an error. + + Explanation + =========== + + None is returned if there are no radicals to remove. + + NotImplementedError is raised if there are radicals and they cannot be + removed or if the relationship between the original symbols and the + change of variable needed to rewrite the system as a polynomial cannot + be solved. + + Otherwise the tuple, ``(eq, cov)``, is returned where: + + *eq*, ``cov`` + *eq* is an equation without radicals (in the symbol(s) of + interest) whose solutions are a superset of the solutions to the + original expression. *eq* might be rewritten in terms of a new + variable; the relationship to the original variables is given by + ``cov`` which is a list containing ``v`` and ``v**p - b`` where + ``p`` is the power needed to clear the radical and ``b`` is the + radical now expressed as a polynomial in the symbols of interest. + For example, for sqrt(2 - x) the tuple would be + ``(c, c**2 - 2 + x)``. The solutions of *eq* will contain + solutions to the original equation (if there are any). + + *syms* + An iterable of symbols which, if provided, will limit the focus of + radical removal: only radicals with one or more of the symbols of + interest will be cleared. All free symbols are used if *syms* is not + set. + + *flags* are used internally for communication during recursive calls. + Two options are also recognized: + + ``take``, when defined, is interpreted as a single-argument function + that returns True if a given Pow should be handled. + + Radicals can be removed from an expression if: + + * All bases of the radicals are the same; a change of variables is + done in this case. + * If all radicals appear in one term of the expression. + * There are only four terms with sqrt() factors or there are less than + four terms having sqrt() factors. + * There are only two terms with radicals. + + Examples + ======== + + >>> from sympy.solvers.solvers import unrad + >>> from sympy.abc import x + >>> from sympy import sqrt, Rational, root + + >>> unrad(sqrt(x)*x**Rational(1, 3) + 2) + (x**5 - 64, []) + >>> unrad(sqrt(x) + root(x + 1, 3)) + (-x**3 + x**2 + 2*x + 1, []) + >>> eq = sqrt(x) + root(x, 3) - 2 + >>> unrad(eq) + (_p**3 + _p**2 - 2, [_p, _p**6 - x]) + + """ + + uflags = {"check": False, "simplify": False} + + def _cov(p, e): + if cov: + # XXX - uncovered + oldp, olde = cov + if Poly(e, p).degree(p) in (1, 2): + cov[:] = [p, olde.subs(oldp, _vsolve(e, p, **uflags)[0])] + else: + raise NotImplementedError + else: + cov[:] = [p, e] + + def _canonical(eq, cov): + if cov: + # change symbol to vanilla so no solutions are eliminated + p, e = cov + rep = {p: Dummy(p.name)} + eq = eq.xreplace(rep) + cov = [p.xreplace(rep), e.xreplace(rep)] + + # remove constants and powers of factors since these don't change + # the location of the root; XXX should factor or factor_terms be used? + eq = factor_terms(_mexpand(eq.as_numer_denom()[0], recursive=True), clear=True) + if eq.is_Mul: + args = [] + for f in eq.args: + if f.is_number: + continue + if f.is_Pow: + args.append(f.base) + else: + args.append(f) + eq = Mul(*args) # leave as Mul for more efficient solving + + # make the sign canonical + margs = list(Mul.make_args(eq)) + changed = False + for i, m in enumerate(margs): + if m.could_extract_minus_sign(): + margs[i] = -m + changed = True + if changed: + eq = Mul(*margs, evaluate=False) + + return eq, cov + + def _Q(pow): + # return leading Rational of denominator of Pow's exponent + c = pow.as_base_exp()[1].as_coeff_Mul()[0] + if not c.is_Rational: + return S.One + return c.q + + # define the _take method that will determine whether a term is of interest + def _take(d): + # return True if coefficient of any factor's exponent's den is not 1 + for pow in Mul.make_args(d): + if not pow.is_Pow: + continue + if _Q(pow) == 1: + continue + if pow.free_symbols & syms: + return True + return False + _take = flags.setdefault('_take', _take) + + if isinstance(eq, Eq): + eq = eq.lhs - eq.rhs # XXX legacy Eq as Eqn support + elif not isinstance(eq, Expr): + return + + cov, nwas, rpt = [flags.setdefault(k, v) for k, v in + sorted({"cov": [], "n": None, "rpt": 0}.items())] + + # preconditioning + eq = powdenest(factor_terms(eq, radical=True, clear=True)) + eq = eq.as_numer_denom()[0] + eq = _mexpand(eq, recursive=True) + if eq.is_number: + return + + # see if there are radicals in symbols of interest + syms = set(syms) or eq.free_symbols # _take uses this + poly = eq.as_poly() + gens = [g for g in poly.gens if _take(g)] + if not gens: + return + + # recast poly in terms of eigen-gens + poly = eq.as_poly(*gens) + + # not a polynomial e.g. 1 + sqrt(x)*exp(sqrt(x)) with gen sqrt(x) + if poly is None: + return + + # - an exponent has a symbol of interest (don't handle) + if any(g.exp.has(*syms) for g in gens): + return + + def _rads_bases_lcm(poly): + # if all the bases are the same or all the radicals are in one + # term, `lcm` will be the lcm of the denominators of the + # exponents of the radicals + lcm = 1 + rads = set() + bases = set() + for g in poly.gens: + q = _Q(g) + if q != 1: + rads.add(g) + lcm = ilcm(lcm, q) + bases.add(g.base) + return rads, bases, lcm + rads, bases, lcm = _rads_bases_lcm(poly) + + covsym = Dummy('p', nonnegative=True) + + # only keep in syms symbols that actually appear in radicals; + # and update gens + newsyms = set() + for r in rads: + newsyms.update(syms & r.free_symbols) + if newsyms != syms: + syms = newsyms + # get terms together that have common generators + drad = dict(zip(rads, range(len(rads)))) + rterms = {(): []} + args = Add.make_args(poly.as_expr()) + for t in args: + if _take(t): + common = set(t.as_poly().gens).intersection(rads) + key = tuple(sorted([drad[i] for i in common])) + else: + key = () + rterms.setdefault(key, []).append(t) + others = Add(*rterms.pop(())) + rterms = [Add(*rterms[k]) for k in rterms.keys()] + + # the output will depend on the order terms are processed, so + # make it canonical quickly + rterms = list(reversed(list(ordered(rterms)))) + + ok = False # we don't have a solution yet + depth = sqrt_depth(eq) + + if len(rterms) == 1 and not (rterms[0].is_Add and lcm > 2): + eq = rterms[0]**lcm - ((-others)**lcm) + ok = True + else: + if len(rterms) == 1 and rterms[0].is_Add: + rterms = list(rterms[0].args) + if len(bases) == 1: + b = bases.pop() + if len(syms) > 1: + x = b.free_symbols + else: + x = syms + x = list(ordered(x))[0] + try: + inv = _vsolve(covsym**lcm - b, x, **uflags) + if not inv: + raise NotImplementedError + eq = poly.as_expr().subs(b, covsym**lcm).subs(x, inv[0]) + _cov(covsym, covsym**lcm - b) + return _canonical(eq, cov) + except NotImplementedError: + pass + + if len(rterms) == 2: + if not others: + eq = rterms[0]**lcm - (-rterms[1])**lcm + ok = True + elif not log(lcm, 2).is_Integer: + # the lcm-is-power-of-two case is handled below + r0, r1 = rterms + if flags.get('_reverse', False): + r1, r0 = r0, r1 + i0 = _rads0, _bases0, lcm0 = _rads_bases_lcm(r0.as_poly()) + i1 = _rads1, _bases1, lcm1 = _rads_bases_lcm(r1.as_poly()) + for reverse in range(2): + if reverse: + i0, i1 = i1, i0 + r0, r1 = r1, r0 + _rads1, _, lcm1 = i1 + _rads1 = Mul(*_rads1) + t1 = _rads1**lcm1 + c = covsym**lcm1 - t1 + for x in syms: + try: + sol = _vsolve(c, x, **uflags) + if not sol: + raise NotImplementedError + neweq = r0.subs(x, sol[0]) + covsym*r1/_rads1 + \ + others + tmp = unrad(neweq, covsym) + if tmp: + eq, newcov = tmp + if newcov: + newp, newc = newcov + _cov(newp, c.subs(covsym, + _vsolve(newc, covsym, **uflags)[0])) + else: + _cov(covsym, c) + else: + eq = neweq + _cov(covsym, c) + ok = True + break + except NotImplementedError: + if reverse: + raise NotImplementedError( + 'no successful change of variable found') + else: + pass + if ok: + break + elif len(rterms) == 3: + # two cube roots and another with order less than 5 + # (so an analytical solution can be found) or a base + # that matches one of the cube root bases + info = [_rads_bases_lcm(i.as_poly()) for i in rterms] + RAD = 0 + BASES = 1 + LCM = 2 + if info[0][LCM] != 3: + info.append(info.pop(0)) + rterms.append(rterms.pop(0)) + elif info[1][LCM] != 3: + info.append(info.pop(1)) + rterms.append(rterms.pop(1)) + if info[0][LCM] == info[1][LCM] == 3: + if info[1][BASES] != info[2][BASES]: + info[0], info[1] = info[1], info[0] + rterms[0], rterms[1] = rterms[1], rterms[0] + if info[1][BASES] == info[2][BASES]: + eq = rterms[0]**3 + (rterms[1] + rterms[2] + others)**3 + ok = True + elif info[2][LCM] < 5: + # a*root(A, 3) + b*root(B, 3) + others = c + a, b, c, d, A, B = [Dummy(i) for i in 'abcdAB'] + # zz represents the unraded expression into which the + # specifics for this case are substituted + zz = (c - d)*(A**3*a**9 + 3*A**2*B*a**6*b**3 - + 3*A**2*a**6*c**3 + 9*A**2*a**6*c**2*d - 9*A**2*a**6*c*d**2 + + 3*A**2*a**6*d**3 + 3*A*B**2*a**3*b**6 + 21*A*B*a**3*b**3*c**3 - + 63*A*B*a**3*b**3*c**2*d + 63*A*B*a**3*b**3*c*d**2 - + 21*A*B*a**3*b**3*d**3 + 3*A*a**3*c**6 - 18*A*a**3*c**5*d + + 45*A*a**3*c**4*d**2 - 60*A*a**3*c**3*d**3 + 45*A*a**3*c**2*d**4 - + 18*A*a**3*c*d**5 + 3*A*a**3*d**6 + B**3*b**9 - 3*B**2*b**6*c**3 + + 9*B**2*b**6*c**2*d - 9*B**2*b**6*c*d**2 + 3*B**2*b**6*d**3 + + 3*B*b**3*c**6 - 18*B*b**3*c**5*d + 45*B*b**3*c**4*d**2 - + 60*B*b**3*c**3*d**3 + 45*B*b**3*c**2*d**4 - 18*B*b**3*c*d**5 + + 3*B*b**3*d**6 - c**9 + 9*c**8*d - 36*c**7*d**2 + 84*c**6*d**3 - + 126*c**5*d**4 + 126*c**4*d**5 - 84*c**3*d**6 + 36*c**2*d**7 - + 9*c*d**8 + d**9) + def _t(i): + b = Mul(*info[i][RAD]) + return cancel(rterms[i]/b), Mul(*info[i][BASES]) + aa, AA = _t(0) + bb, BB = _t(1) + cc = -rterms[2] + dd = others + eq = zz.xreplace(dict(zip( + (a, A, b, B, c, d), + (aa, AA, bb, BB, cc, dd)))) + ok = True + # handle power-of-2 cases + if not ok: + if log(lcm, 2).is_Integer and (not others and + len(rterms) == 4 or len(rterms) < 4): + def _norm2(a, b): + return a**2 + b**2 + 2*a*b + + if len(rterms) == 4: + # (r0+r1)**2 - (r2+r3)**2 + r0, r1, r2, r3 = rterms + eq = _norm2(r0, r1) - _norm2(r2, r3) + ok = True + elif len(rterms) == 3: + # (r1+r2)**2 - (r0+others)**2 + r0, r1, r2 = rterms + eq = _norm2(r1, r2) - _norm2(r0, others) + ok = True + elif len(rterms) == 2: + # r0**2 - (r1+others)**2 + r0, r1 = rterms + eq = r0**2 - _norm2(r1, others) + ok = True + + new_depth = sqrt_depth(eq) if ok else depth + rpt += 1 # XXX how many repeats with others unchanging is enough? + if not ok or ( + nwas is not None and len(rterms) == nwas and + new_depth is not None and new_depth == depth and + rpt > 3): + raise NotImplementedError('Cannot remove all radicals') + + flags.update({"cov": cov, "n": len(rterms), "rpt": rpt}) + neq = unrad(eq, *syms, **flags) + if neq: + eq, cov = neq + eq, cov = _canonical(eq, cov) + return eq, cov + + +# delayed imports +from sympy.solvers.bivariate import ( + bivariate_type, _solve_lambert, _filtered_gens) diff --git a/lib/python3.12/site-packages/sympy/stats/__pycache__/frv_types.cpython-312.pyc b/lib/python3.12/site-packages/sympy/stats/__pycache__/frv_types.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3eabd0744786bf92ab7f8111d80a88081046d90d Binary files /dev/null and b/lib/python3.12/site-packages/sympy/stats/__pycache__/frv_types.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/stats/__pycache__/symbolic_multivariate_probability.cpython-312.pyc b/lib/python3.12/site-packages/sympy/stats/__pycache__/symbolic_multivariate_probability.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d88668107403abbeaf24d4cf7fbd837a78ad8dca Binary files /dev/null and b/lib/python3.12/site-packages/sympy/stats/__pycache__/symbolic_multivariate_probability.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/stats/sampling/__init__.py b/lib/python3.12/site-packages/sympy/stats/sampling/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/lib/python3.12/site-packages/sympy/stats/sampling/__pycache__/__init__.cpython-312.pyc b/lib/python3.12/site-packages/sympy/stats/sampling/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1b38faa79d35b71568f4bacccbdc5ff43af84340 Binary files /dev/null and b/lib/python3.12/site-packages/sympy/stats/sampling/__pycache__/__init__.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/stats/sampling/__pycache__/sample_numpy.cpython-312.pyc b/lib/python3.12/site-packages/sympy/stats/sampling/__pycache__/sample_numpy.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6a5a16b47857e567b4cae3b02204ef4fdca1d9a7 Binary files /dev/null and b/lib/python3.12/site-packages/sympy/stats/sampling/__pycache__/sample_numpy.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/stats/sampling/__pycache__/sample_pymc.cpython-312.pyc b/lib/python3.12/site-packages/sympy/stats/sampling/__pycache__/sample_pymc.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b8eea985ebe01a4ed1cc4ba55846bbeaf49bf536 Binary files /dev/null and b/lib/python3.12/site-packages/sympy/stats/sampling/__pycache__/sample_pymc.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/stats/sampling/__pycache__/sample_scipy.cpython-312.pyc b/lib/python3.12/site-packages/sympy/stats/sampling/__pycache__/sample_scipy.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..689a4aa51018e7bde76ea06c0b07907a24b7b741 Binary files /dev/null and b/lib/python3.12/site-packages/sympy/stats/sampling/__pycache__/sample_scipy.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/stats/sampling/sample_numpy.py b/lib/python3.12/site-packages/sympy/stats/sampling/sample_numpy.py new file mode 100644 index 0000000000000000000000000000000000000000..d65417945449ed8b62d2547215d8908f84820a9b --- /dev/null +++ b/lib/python3.12/site-packages/sympy/stats/sampling/sample_numpy.py @@ -0,0 +1,105 @@ +from functools import singledispatch + +from sympy.external import import_module +from sympy.stats.crv_types import BetaDistribution, ChiSquaredDistribution, ExponentialDistribution, GammaDistribution, \ + LogNormalDistribution, NormalDistribution, ParetoDistribution, UniformDistribution, FDistributionDistribution, GumbelDistribution, LaplaceDistribution, \ + LogisticDistribution, RayleighDistribution, TriangularDistribution +from sympy.stats.drv_types import GeometricDistribution, PoissonDistribution, ZetaDistribution +from sympy.stats.frv_types import BinomialDistribution, HypergeometricDistribution + + +numpy = import_module('numpy') + + +@singledispatch +def do_sample_numpy(dist, size, rand_state): + return None + + +# CRV: + +@do_sample_numpy.register(BetaDistribution) +def _(dist: BetaDistribution, size, rand_state): + return rand_state.beta(a=float(dist.alpha), b=float(dist.beta), size=size) + + +@do_sample_numpy.register(ChiSquaredDistribution) +def _(dist: ChiSquaredDistribution, size, rand_state): + return rand_state.chisquare(df=float(dist.k), size=size) + + +@do_sample_numpy.register(ExponentialDistribution) +def _(dist: ExponentialDistribution, size, rand_state): + return rand_state.exponential(1 / float(dist.rate), size=size) + +@do_sample_numpy.register(FDistributionDistribution) +def _(dist: FDistributionDistribution, size, rand_state): + return rand_state.f(dfnum = float(dist.d1), dfden = float(dist.d2), size=size) + +@do_sample_numpy.register(GammaDistribution) +def _(dist: GammaDistribution, size, rand_state): + return rand_state.gamma(shape = float(dist.k), scale = float(dist.theta), size=size) + +@do_sample_numpy.register(GumbelDistribution) +def _(dist: GumbelDistribution, size, rand_state): + return rand_state.gumbel(loc = float(dist.mu), scale = float(dist.beta), size=size) + +@do_sample_numpy.register(LaplaceDistribution) +def _(dist: LaplaceDistribution, size, rand_state): + return rand_state.laplace(loc = float(dist.mu), scale = float(dist.b), size=size) + +@do_sample_numpy.register(LogisticDistribution) +def _(dist: LogisticDistribution, size, rand_state): + return rand_state.logistic(loc = float(dist.mu), scale = float(dist.s), size=size) + +@do_sample_numpy.register(LogNormalDistribution) +def _(dist: LogNormalDistribution, size, rand_state): + return rand_state.lognormal(mean = float(dist.mean), sigma = float(dist.std), size=size) + +@do_sample_numpy.register(NormalDistribution) +def _(dist: NormalDistribution, size, rand_state): + return rand_state.normal(loc = float(dist.mean), scale = float(dist.std), size=size) + +@do_sample_numpy.register(RayleighDistribution) +def _(dist: RayleighDistribution, size, rand_state): + return rand_state.rayleigh(scale = float(dist.sigma), size=size) + +@do_sample_numpy.register(ParetoDistribution) +def _(dist: ParetoDistribution, size, rand_state): + return (numpy.random.pareto(a=float(dist.alpha), size=size) + 1) * float(dist.xm) + +@do_sample_numpy.register(TriangularDistribution) +def _(dist: TriangularDistribution, size, rand_state): + return rand_state.triangular(left = float(dist.a), mode = float(dist.b), right = float(dist.c), size=size) + +@do_sample_numpy.register(UniformDistribution) +def _(dist: UniformDistribution, size, rand_state): + return rand_state.uniform(low=float(dist.left), high=float(dist.right), size=size) + + +# DRV: + +@do_sample_numpy.register(GeometricDistribution) +def _(dist: GeometricDistribution, size, rand_state): + return rand_state.geometric(p=float(dist.p), size=size) + + +@do_sample_numpy.register(PoissonDistribution) +def _(dist: PoissonDistribution, size, rand_state): + return rand_state.poisson(lam=float(dist.lamda), size=size) + + +@do_sample_numpy.register(ZetaDistribution) +def _(dist: ZetaDistribution, size, rand_state): + return rand_state.zipf(a=float(dist.s), size=size) + + +# FRV: + +@do_sample_numpy.register(BinomialDistribution) +def _(dist: BinomialDistribution, size, rand_state): + return rand_state.binomial(n=int(dist.n), p=float(dist.p), size=size) + +@do_sample_numpy.register(HypergeometricDistribution) +def _(dist: HypergeometricDistribution, size, rand_state): + return rand_state.hypergeometric(ngood = int(dist.N), nbad = int(dist.m), nsample = int(dist.n), size=size) diff --git a/lib/python3.12/site-packages/sympy/stats/sampling/sample_pymc.py b/lib/python3.12/site-packages/sympy/stats/sampling/sample_pymc.py new file mode 100644 index 0000000000000000000000000000000000000000..546f02a3092815af2e54b4a164463f62ece7a024 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/stats/sampling/sample_pymc.py @@ -0,0 +1,99 @@ +from functools import singledispatch +from sympy.external import import_module +from sympy.stats.crv_types import BetaDistribution, CauchyDistribution, ChiSquaredDistribution, ExponentialDistribution, \ + GammaDistribution, LogNormalDistribution, NormalDistribution, ParetoDistribution, UniformDistribution, \ + GaussianInverseDistribution +from sympy.stats.drv_types import PoissonDistribution, GeometricDistribution, NegativeBinomialDistribution +from sympy.stats.frv_types import BinomialDistribution, BernoulliDistribution + + +try: + import pymc +except ImportError: + pymc = import_module('pymc3') + +@singledispatch +def do_sample_pymc(dist): + return None + + +# CRV: + +@do_sample_pymc.register(BetaDistribution) +def _(dist: BetaDistribution): + return pymc.Beta('X', alpha=float(dist.alpha), beta=float(dist.beta)) + + +@do_sample_pymc.register(CauchyDistribution) +def _(dist: CauchyDistribution): + return pymc.Cauchy('X', alpha=float(dist.x0), beta=float(dist.gamma)) + + +@do_sample_pymc.register(ChiSquaredDistribution) +def _(dist: ChiSquaredDistribution): + return pymc.ChiSquared('X', nu=float(dist.k)) + + +@do_sample_pymc.register(ExponentialDistribution) +def _(dist: ExponentialDistribution): + return pymc.Exponential('X', lam=float(dist.rate)) + + +@do_sample_pymc.register(GammaDistribution) +def _(dist: GammaDistribution): + return pymc.Gamma('X', alpha=float(dist.k), beta=1 / float(dist.theta)) + + +@do_sample_pymc.register(LogNormalDistribution) +def _(dist: LogNormalDistribution): + return pymc.Lognormal('X', mu=float(dist.mean), sigma=float(dist.std)) + + +@do_sample_pymc.register(NormalDistribution) +def _(dist: NormalDistribution): + return pymc.Normal('X', float(dist.mean), float(dist.std)) + + +@do_sample_pymc.register(GaussianInverseDistribution) +def _(dist: GaussianInverseDistribution): + return pymc.Wald('X', mu=float(dist.mean), lam=float(dist.shape)) + + +@do_sample_pymc.register(ParetoDistribution) +def _(dist: ParetoDistribution): + return pymc.Pareto('X', alpha=float(dist.alpha), m=float(dist.xm)) + + +@do_sample_pymc.register(UniformDistribution) +def _(dist: UniformDistribution): + return pymc.Uniform('X', lower=float(dist.left), upper=float(dist.right)) + + +# DRV: + +@do_sample_pymc.register(GeometricDistribution) +def _(dist: GeometricDistribution): + return pymc.Geometric('X', p=float(dist.p)) + + +@do_sample_pymc.register(NegativeBinomialDistribution) +def _(dist: NegativeBinomialDistribution): + return pymc.NegativeBinomial('X', mu=float((dist.p * dist.r) / (1 - dist.p)), + alpha=float(dist.r)) + + +@do_sample_pymc.register(PoissonDistribution) +def _(dist: PoissonDistribution): + return pymc.Poisson('X', mu=float(dist.lamda)) + + +# FRV: + +@do_sample_pymc.register(BernoulliDistribution) +def _(dist: BernoulliDistribution): + return pymc.Bernoulli('X', p=float(dist.p)) + + +@do_sample_pymc.register(BinomialDistribution) +def _(dist: BinomialDistribution): + return pymc.Binomial('X', n=int(dist.n), p=float(dist.p)) diff --git a/lib/python3.12/site-packages/sympy/stats/sampling/sample_scipy.py b/lib/python3.12/site-packages/sympy/stats/sampling/sample_scipy.py new file mode 100644 index 0000000000000000000000000000000000000000..f12508f68844488e9a14b1476005164eb422796e --- /dev/null +++ b/lib/python3.12/site-packages/sympy/stats/sampling/sample_scipy.py @@ -0,0 +1,167 @@ +from functools import singledispatch + +from sympy.core.symbol import Dummy +from sympy.functions.elementary.exponential import exp +from sympy.utilities.lambdify import lambdify +from sympy.external import import_module +from sympy.stats import DiscreteDistributionHandmade +from sympy.stats.crv import SingleContinuousDistribution +from sympy.stats.crv_types import ChiSquaredDistribution, ExponentialDistribution, GammaDistribution, \ + LogNormalDistribution, NormalDistribution, ParetoDistribution, UniformDistribution, BetaDistribution, \ + StudentTDistribution, CauchyDistribution +from sympy.stats.drv_types import GeometricDistribution, LogarithmicDistribution, NegativeBinomialDistribution, \ + PoissonDistribution, SkellamDistribution, YuleSimonDistribution, ZetaDistribution +from sympy.stats.frv import SingleFiniteDistribution + + +scipy = import_module("scipy", import_kwargs={'fromlist':['stats']}) + + +@singledispatch +def do_sample_scipy(dist, size, seed): + return None + + +# CRV + +@do_sample_scipy.register(SingleContinuousDistribution) +def _(dist: SingleContinuousDistribution, size, seed): + # if we don't need to make a handmade pdf, we won't + import scipy.stats + + z = Dummy('z') + handmade_pdf = lambdify(z, dist.pdf(z), ['numpy', 'scipy']) + + class scipy_pdf(scipy.stats.rv_continuous): + def _pdf(dist, x): + return handmade_pdf(x) + + scipy_rv = scipy_pdf(a=float(dist.set._inf), + b=float(dist.set._sup), name='scipy_pdf') + return scipy_rv.rvs(size=size, random_state=seed) + + +@do_sample_scipy.register(ChiSquaredDistribution) +def _(dist: ChiSquaredDistribution, size, seed): + # same parametrisation + return scipy.stats.chi2.rvs(df=float(dist.k), size=size, random_state=seed) + + +@do_sample_scipy.register(ExponentialDistribution) +def _(dist: ExponentialDistribution, size, seed): + # https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.expon.html#scipy.stats.expon + return scipy.stats.expon.rvs(scale=1 / float(dist.rate), size=size, random_state=seed) + + +@do_sample_scipy.register(GammaDistribution) +def _(dist: GammaDistribution, size, seed): + # https://stackoverflow.com/questions/42150965/how-to-plot-gamma-distribution-with-alpha-and-beta-parameters-in-python + return scipy.stats.gamma.rvs(a=float(dist.k), scale=float(dist.theta), size=size, random_state=seed) + + +@do_sample_scipy.register(LogNormalDistribution) +def _(dist: LogNormalDistribution, size, seed): + # https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.lognorm.html + return scipy.stats.lognorm.rvs(scale=float(exp(dist.mean)), s=float(dist.std), size=size, random_state=seed) + + +@do_sample_scipy.register(NormalDistribution) +def _(dist: NormalDistribution, size, seed): + return scipy.stats.norm.rvs(loc=float(dist.mean), scale=float(dist.std), size=size, random_state=seed) + + +@do_sample_scipy.register(ParetoDistribution) +def _(dist: ParetoDistribution, size, seed): + # https://stackoverflow.com/questions/42260519/defining-pareto-distribution-in-python-scipy + return scipy.stats.pareto.rvs(b=float(dist.alpha), scale=float(dist.xm), size=size, random_state=seed) + + +@do_sample_scipy.register(StudentTDistribution) +def _(dist: StudentTDistribution, size, seed): + return scipy.stats.t.rvs(df=float(dist.nu), size=size, random_state=seed) + + +@do_sample_scipy.register(UniformDistribution) +def _(dist: UniformDistribution, size, seed): + # https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.uniform.html + return scipy.stats.uniform.rvs(loc=float(dist.left), scale=float(dist.right - dist.left), size=size, random_state=seed) + + +@do_sample_scipy.register(BetaDistribution) +def _(dist: BetaDistribution, size, seed): + # same parametrisation + return scipy.stats.beta.rvs(a=float(dist.alpha), b=float(dist.beta), size=size, random_state=seed) + + +@do_sample_scipy.register(CauchyDistribution) +def _(dist: CauchyDistribution, size, seed): + return scipy.stats.cauchy.rvs(loc=float(dist.x0), scale=float(dist.gamma), size=size, random_state=seed) + + +# DRV: + +@do_sample_scipy.register(DiscreteDistributionHandmade) +def _(dist: DiscreteDistributionHandmade, size, seed): + from scipy.stats import rv_discrete + + z = Dummy('z') + handmade_pmf = lambdify(z, dist.pdf(z), ['numpy', 'scipy']) + + class scipy_pmf(rv_discrete): + def _pmf(dist, x): + return handmade_pmf(x) + + scipy_rv = scipy_pmf(a=float(dist.set._inf), b=float(dist.set._sup), + name='scipy_pmf') + return scipy_rv.rvs(size=size, random_state=seed) + + +@do_sample_scipy.register(GeometricDistribution) +def _(dist: GeometricDistribution, size, seed): + return scipy.stats.geom.rvs(p=float(dist.p), size=size, random_state=seed) + + +@do_sample_scipy.register(LogarithmicDistribution) +def _(dist: LogarithmicDistribution, size, seed): + return scipy.stats.logser.rvs(p=float(dist.p), size=size, random_state=seed) + + +@do_sample_scipy.register(NegativeBinomialDistribution) +def _(dist: NegativeBinomialDistribution, size, seed): + return scipy.stats.nbinom.rvs(n=float(dist.r), p=float(dist.p), size=size, random_state=seed) + + +@do_sample_scipy.register(PoissonDistribution) +def _(dist: PoissonDistribution, size, seed): + return scipy.stats.poisson.rvs(mu=float(dist.lamda), size=size, random_state=seed) + + +@do_sample_scipy.register(SkellamDistribution) +def _(dist: SkellamDistribution, size, seed): + return scipy.stats.skellam.rvs(mu1=float(dist.mu1), mu2=float(dist.mu2), size=size, random_state=seed) + + +@do_sample_scipy.register(YuleSimonDistribution) +def _(dist: YuleSimonDistribution, size, seed): + return scipy.stats.yulesimon.rvs(alpha=float(dist.rho), size=size, random_state=seed) + + +@do_sample_scipy.register(ZetaDistribution) +def _(dist: ZetaDistribution, size, seed): + return scipy.stats.zipf.rvs(a=float(dist.s), size=size, random_state=seed) + + +# FRV: + +@do_sample_scipy.register(SingleFiniteDistribution) +def _(dist: SingleFiniteDistribution, size, seed): + # scipy can handle with custom distributions + + from scipy.stats import rv_discrete + density_ = dist.dict + x, y = [], [] + for k, v in density_.items(): + x.append(int(k)) + y.append(float(v)) + scipy_rv = rv_discrete(name='scipy_rv', values=(x, y)) + return scipy_rv.rvs(size=size, random_state=seed) diff --git a/lib/python3.12/site-packages/sympy/stats/sampling/tests/__init__.py b/lib/python3.12/site-packages/sympy/stats/sampling/tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/lib/python3.12/site-packages/sympy/stats/sampling/tests/__pycache__/__init__.cpython-312.pyc b/lib/python3.12/site-packages/sympy/stats/sampling/tests/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0ed085d78d70da23a99d8391f2196ce81c609681 Binary files /dev/null and b/lib/python3.12/site-packages/sympy/stats/sampling/tests/__pycache__/__init__.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/stats/sampling/tests/__pycache__/test_sample_continuous_rv.cpython-312.pyc b/lib/python3.12/site-packages/sympy/stats/sampling/tests/__pycache__/test_sample_continuous_rv.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6efb994c4e3ebfdb32a0bda931bec6d4e51cb5db Binary files /dev/null and b/lib/python3.12/site-packages/sympy/stats/sampling/tests/__pycache__/test_sample_continuous_rv.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/stats/sampling/tests/__pycache__/test_sample_discrete_rv.cpython-312.pyc b/lib/python3.12/site-packages/sympy/stats/sampling/tests/__pycache__/test_sample_discrete_rv.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1ef399d6f676a5c1ce54ef9c1243f9bbdf49823f Binary files /dev/null and b/lib/python3.12/site-packages/sympy/stats/sampling/tests/__pycache__/test_sample_discrete_rv.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/stats/sampling/tests/__pycache__/test_sample_finite_rv.cpython-312.pyc b/lib/python3.12/site-packages/sympy/stats/sampling/tests/__pycache__/test_sample_finite_rv.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0bdf939e04cb9880d82d269e1cb3e16254c4e8ae Binary files /dev/null and b/lib/python3.12/site-packages/sympy/stats/sampling/tests/__pycache__/test_sample_finite_rv.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/stats/sampling/tests/test_sample_continuous_rv.py b/lib/python3.12/site-packages/sympy/stats/sampling/tests/test_sample_continuous_rv.py new file mode 100644 index 0000000000000000000000000000000000000000..953bb602df5e63da2882ee118de9dbf24b6f7804 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/stats/sampling/tests/test_sample_continuous_rv.py @@ -0,0 +1,181 @@ +from sympy.core.numbers import oo +from sympy.core.symbol import Symbol +from sympy.functions.elementary.exponential import exp +from sympy.sets.sets import Interval +from sympy.external import import_module +from sympy.stats import Beta, Chi, Normal, Gamma, Exponential, LogNormal, Pareto, ChiSquared, Uniform, sample, \ + BetaPrime, Cauchy, GammaInverse, GaussianInverse, StudentT, Weibull, density, ContinuousRV, FDistribution, \ + Gumbel, Laplace, Logistic, Rayleigh, Triangular +from sympy.testing.pytest import skip, raises + + +def test_sample_numpy(): + distribs_numpy = [ + Beta("B", 1, 1), + Normal("N", 0, 1), + Gamma("G", 2, 7), + Exponential("E", 2), + LogNormal("LN", 0, 1), + Pareto("P", 1, 1), + ChiSquared("CS", 2), + Uniform("U", 0, 1), + FDistribution("FD", 1, 2), + Gumbel("GB", 1, 2), + Laplace("L", 1, 2), + Logistic("LO", 1, 2), + Rayleigh("R", 1), + Triangular("T", 1, 2, 2), + ] + size = 3 + numpy = import_module('numpy') + if not numpy: + skip('Numpy is not installed. Abort tests for _sample_numpy.') + else: + for X in distribs_numpy: + samps = sample(X, size=size, library='numpy') + for sam in samps: + assert sam in X.pspace.domain.set + raises(NotImplementedError, + lambda: sample(Chi("C", 1), library='numpy')) + raises(NotImplementedError, + lambda: Chi("C", 1).pspace.distribution.sample(library='tensorflow')) + + +def test_sample_scipy(): + distribs_scipy = [ + Beta("B", 1, 1), + BetaPrime("BP", 1, 1), + Cauchy("C", 1, 1), + Chi("C", 1), + Normal("N", 0, 1), + Gamma("G", 2, 7), + GammaInverse("GI", 1, 1), + GaussianInverse("GUI", 1, 1), + Exponential("E", 2), + LogNormal("LN", 0, 1), + Pareto("P", 1, 1), + StudentT("S", 2), + ChiSquared("CS", 2), + Uniform("U", 0, 1) + ] + size = 3 + scipy = import_module('scipy') + if not scipy: + skip('Scipy is not installed. Abort tests for _sample_scipy.') + else: + for X in distribs_scipy: + samps = sample(X, size=size, library='scipy') + samps2 = sample(X, size=(2, 2), library='scipy') + for sam in samps: + assert sam in X.pspace.domain.set + for i in range(2): + for j in range(2): + assert samps2[i][j] in X.pspace.domain.set + + +def test_sample_pymc(): + distribs_pymc = [ + Beta("B", 1, 1), + Cauchy("C", 1, 1), + Normal("N", 0, 1), + Gamma("G", 2, 7), + GaussianInverse("GI", 1, 1), + Exponential("E", 2), + LogNormal("LN", 0, 1), + Pareto("P", 1, 1), + ChiSquared("CS", 2), + Uniform("U", 0, 1) + ] + size = 3 + pymc = import_module('pymc') + if not pymc: + skip('PyMC is not installed. Abort tests for _sample_pymc.') + else: + for X in distribs_pymc: + samps = sample(X, size=size, library='pymc') + for sam in samps: + assert sam in X.pspace.domain.set + raises(NotImplementedError, + lambda: sample(Chi("C", 1), library='pymc')) + + +def test_sampling_gamma_inverse(): + scipy = import_module('scipy') + if not scipy: + skip('Scipy not installed. Abort tests for sampling of gamma inverse.') + X = GammaInverse("x", 1, 1) + assert sample(X) in X.pspace.domain.set + + +def test_lognormal_sampling(): + # Right now, only density function and sampling works + scipy = import_module('scipy') + if not scipy: + skip('Scipy is not installed. Abort tests') + for i in range(3): + X = LogNormal('x', i, 1) + assert sample(X) in X.pspace.domain.set + + size = 5 + samps = sample(X, size=size) + for samp in samps: + assert samp in X.pspace.domain.set + + +def test_sampling_gaussian_inverse(): + scipy = import_module('scipy') + if not scipy: + skip('Scipy not installed. Abort tests for sampling of Gaussian inverse.') + X = GaussianInverse("x", 1, 1) + assert sample(X, library='scipy') in X.pspace.domain.set + + +def test_prefab_sampling(): + scipy = import_module('scipy') + if not scipy: + skip('Scipy is not installed. Abort tests') + N = Normal('X', 0, 1) + L = LogNormal('L', 0, 1) + E = Exponential('Ex', 1) + P = Pareto('P', 1, 3) + W = Weibull('W', 1, 1) + U = Uniform('U', 0, 1) + B = Beta('B', 2, 5) + G = Gamma('G', 1, 3) + + variables = [N, L, E, P, W, U, B, G] + niter = 10 + size = 5 + for var in variables: + for _ in range(niter): + assert sample(var) in var.pspace.domain.set + samps = sample(var, size=size) + for samp in samps: + assert samp in var.pspace.domain.set + + +def test_sample_continuous(): + z = Symbol('z') + Z = ContinuousRV(z, exp(-z), set=Interval(0, oo)) + assert density(Z)(-1) == 0 + + scipy = import_module('scipy') + if not scipy: + skip('Scipy is not installed. Abort tests') + assert sample(Z) in Z.pspace.domain.set + sym, val = list(Z.pspace.sample().items())[0] + assert sym == Z and val in Interval(0, oo) + + libraries = ['scipy', 'numpy', 'pymc'] + for lib in libraries: + try: + imported_lib = import_module(lib) + if imported_lib: + s0, s1, s2 = [], [], [] + s0 = sample(Z, size=10, library=lib, seed=0) + s1 = sample(Z, size=10, library=lib, seed=0) + s2 = sample(Z, size=10, library=lib, seed=1) + assert all(s0 == s1) + assert all(s1 != s2) + except NotImplementedError: + continue diff --git a/lib/python3.12/site-packages/sympy/stats/sampling/tests/test_sample_discrete_rv.py b/lib/python3.12/site-packages/sympy/stats/sampling/tests/test_sample_discrete_rv.py new file mode 100644 index 0000000000000000000000000000000000000000..10029af647b9811fe1e29f05360069c20ea1e659 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/stats/sampling/tests/test_sample_discrete_rv.py @@ -0,0 +1,99 @@ +from sympy.core.singleton import S +from sympy.core.symbol import Symbol +from sympy.external import import_module +from sympy.stats import Geometric, Poisson, Zeta, sample, Skellam, DiscreteRV, Logarithmic, NegativeBinomial, YuleSimon +from sympy.testing.pytest import skip, raises, slow + + +def test_sample_numpy(): + distribs_numpy = [ + Geometric('G', 0.5), + Poisson('P', 1), + Zeta('Z', 2) + ] + size = 3 + numpy = import_module('numpy') + if not numpy: + skip('Numpy is not installed. Abort tests for _sample_numpy.') + else: + for X in distribs_numpy: + samps = sample(X, size=size, library='numpy') + for sam in samps: + assert sam in X.pspace.domain.set + raises(NotImplementedError, + lambda: sample(Skellam('S', 1, 1), library='numpy')) + raises(NotImplementedError, + lambda: Skellam('S', 1, 1).pspace.distribution.sample(library='tensorflow')) + + +def test_sample_scipy(): + p = S(2)/3 + x = Symbol('x', integer=True, positive=True) + pdf = p*(1 - p)**(x - 1) # pdf of Geometric Distribution + distribs_scipy = [ + DiscreteRV(x, pdf, set=S.Naturals), + Geometric('G', 0.5), + Logarithmic('L', 0.5), + NegativeBinomial('N', 5, 0.4), + Poisson('P', 1), + Skellam('S', 1, 1), + YuleSimon('Y', 1), + Zeta('Z', 2) + ] + size = 3 + scipy = import_module('scipy') + if not scipy: + skip('Scipy is not installed. Abort tests for _sample_scipy.') + else: + for X in distribs_scipy: + samps = sample(X, size=size, library='scipy') + samps2 = sample(X, size=(2, 2), library='scipy') + for sam in samps: + assert sam in X.pspace.domain.set + for i in range(2): + for j in range(2): + assert samps2[i][j] in X.pspace.domain.set + + +def test_sample_pymc(): + distribs_pymc = [ + Geometric('G', 0.5), + Poisson('P', 1), + NegativeBinomial('N', 5, 0.4) + ] + size = 3 + pymc = import_module('pymc') + if not pymc: + skip('PyMC is not installed. Abort tests for _sample_pymc.') + else: + for X in distribs_pymc: + samps = sample(X, size=size, library='pymc') + for sam in samps: + assert sam in X.pspace.domain.set + raises(NotImplementedError, + lambda: sample(Skellam('S', 1, 1), library='pymc')) + +@slow +def test_sample_discrete(): + X = Geometric('X', S.Half) + scipy = import_module('scipy') + if not scipy: + skip('Scipy not installed. Abort tests') + assert sample(X) in X.pspace.domain.set + samps = sample(X, size=2) # This takes long time if ran without scipy + for samp in samps: + assert samp in X.pspace.domain.set + + libraries = ['scipy', 'numpy', 'pymc'] + for lib in libraries: + try: + imported_lib = import_module(lib) + if imported_lib: + s0, s1, s2 = [], [], [] + s0 = sample(X, size=10, library=lib, seed=0) + s1 = sample(X, size=10, library=lib, seed=0) + s2 = sample(X, size=10, library=lib, seed=1) + assert all(s0 == s1) + assert not all(s1 == s2) + except NotImplementedError: + continue diff --git a/lib/python3.12/site-packages/sympy/stats/sampling/tests/test_sample_finite_rv.py b/lib/python3.12/site-packages/sympy/stats/sampling/tests/test_sample_finite_rv.py new file mode 100644 index 0000000000000000000000000000000000000000..96cabe0ff4aaa5977e16600217fbbdeb08b962ae --- /dev/null +++ b/lib/python3.12/site-packages/sympy/stats/sampling/tests/test_sample_finite_rv.py @@ -0,0 +1,94 @@ +from sympy.core.numbers import Rational +from sympy.core.singleton import S +from sympy.external import import_module +from sympy.stats import Binomial, sample, Die, FiniteRV, DiscreteUniform, Bernoulli, BetaBinomial, Hypergeometric, \ + Rademacher +from sympy.testing.pytest import skip, raises + +def test_given_sample(): + X = Die('X', 6) + scipy = import_module('scipy') + if not scipy: + skip('Scipy is not installed. Abort tests') + assert sample(X, X > 5) == 6 + +def test_sample_numpy(): + distribs_numpy = [ + Binomial("B", 5, 0.4), + Hypergeometric("H", 2, 1, 1) + ] + size = 3 + numpy = import_module('numpy') + if not numpy: + skip('Numpy is not installed. Abort tests for _sample_numpy.') + else: + for X in distribs_numpy: + samps = sample(X, size=size, library='numpy') + for sam in samps: + assert sam in X.pspace.domain.set + raises(NotImplementedError, + lambda: sample(Die("D"), library='numpy')) + raises(NotImplementedError, + lambda: Die("D").pspace.sample(library='tensorflow')) + + +def test_sample_scipy(): + distribs_scipy = [ + FiniteRV('F', {1: S.Half, 2: Rational(1, 4), 3: Rational(1, 4)}), + DiscreteUniform("Y", list(range(5))), + Die("D"), + Bernoulli("Be", 0.3), + Binomial("Bi", 5, 0.4), + BetaBinomial("Bb", 2, 1, 1), + Hypergeometric("H", 1, 1, 1), + Rademacher("R") + ] + + size = 3 + scipy = import_module('scipy') + if not scipy: + skip('Scipy not installed. Abort tests for _sample_scipy.') + else: + for X in distribs_scipy: + samps = sample(X, size=size) + samps2 = sample(X, size=(2, 2)) + for sam in samps: + assert sam in X.pspace.domain.set + for i in range(2): + for j in range(2): + assert samps2[i][j] in X.pspace.domain.set + + +def test_sample_pymc(): + distribs_pymc = [ + Bernoulli('B', 0.2), + Binomial('N', 5, 0.4) + ] + size = 3 + pymc = import_module('pymc') + if not pymc: + skip('PyMC is not installed. Abort tests for _sample_pymc.') + else: + for X in distribs_pymc: + samps = sample(X, size=size, library='pymc') + for sam in samps: + assert sam in X.pspace.domain.set + raises(NotImplementedError, + lambda: (sample(Die("D"), library='pymc'))) + + +def test_sample_seed(): + F = FiniteRV('F', {1: S.Half, 2: Rational(1, 4), 3: Rational(1, 4)}) + size = 10 + libraries = ['scipy', 'numpy', 'pymc'] + for lib in libraries: + try: + imported_lib = import_module(lib) + if imported_lib: + s0 = sample(F, size=size, library=lib, seed=0) + s1 = sample(F, size=size, library=lib, seed=0) + s2 = sample(F, size=size, library=lib, seed=1) + assert all(s0 == s1) + assert not all(s1 == s2) + except NotImplementedError: + continue