|
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): |
|
|
|
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 |
|
|
|
@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: |
|
|
|
|
|
|
|
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 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_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): |
|
|
|
|
|
|
|
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): |
|
|
|
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 |
|
|
|
return i - (1 if i > 0 else -1) |
|
return i |
|
|
|
def __float__(self): |
|
|
|
|
|
|
|
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): |
|
|
|
|
|
|
|
|
|
|
|
return None |
|
else: |
|
reps = {} |
|
nmag = abs(self.evalf(2)) |
|
|
|
if not hasattr(nmag, '_prec'): |
|
|
|
return None |
|
|
|
if nmag._prec == 1: |
|
|
|
|
|
|
|
|
|
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) |
|
|
|
|
|
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 |
|
|
|
|
|
|
|
wrt = set(wrt) |
|
if wrt and not wrt & free: |
|
return True |
|
wrt = wrt or free |
|
|
|
|
|
expr = self |
|
if simplify: |
|
expr = expr.simplify() |
|
|
|
|
|
|
|
|
|
if expr.is_zero: |
|
return True |
|
|
|
|
|
wrt_number = {sym for sym in wrt if sym.kind is NumberKind} |
|
|
|
|
|
failing_number = None |
|
if wrt_number == free: |
|
|
|
try: |
|
a = expr.subs(list(zip(free, [0]*len(free))), |
|
simultaneous=True) |
|
if a is S.NaN: |
|
|
|
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: |
|
|
|
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 |
|
|
|
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 |
|
|
|
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 |
|
|
|
|
|
|
|
|
|
|
|
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 |
|
|
|
|
|
|
|
|
|
|
|
diff = factor_terms(simplify(self - other), radical=True) |
|
|
|
if not diff: |
|
return True |
|
|
|
if not diff.has(Add, Mod): |
|
|
|
|
|
return False |
|
|
|
factors = diff.as_coeff_mul()[1] |
|
if len(factors) > 1: |
|
fac_zero = [fac.equals(0) for fac in factors] |
|
if None not in fac_zero: |
|
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: |
|
|
|
|
|
|
|
return |
|
|
|
if constant is True: |
|
|
|
ndiff = diff._random() |
|
|
|
|
|
if ndiff and ndiff.is_comparable: |
|
return False |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if diff.is_number: |
|
|
|
surds = [s for s in diff.atoms(Pow) if s.args[0].is_Integer] |
|
|
|
surds.sort(key=lambda x: -x.args[0]) |
|
for s in surds: |
|
try: |
|
|
|
|
|
|
|
|
|
|
|
sol = solve(diff, s, simplify=False) |
|
if sol: |
|
if s in sol: |
|
|
|
return True |
|
if all(si.is_Integer for si in sol): |
|
|
|
|
|
return False |
|
if all(i.is_algebraic is False for i in sol): |
|
|
|
return False |
|
if any(si in surds for si in sol): |
|
|
|
|
|
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 |
|
|
|
|
|
|
|
if True: |
|
try: |
|
mp = minimal_polynomial(diff) |
|
if mp.is_Symbol: |
|
return True |
|
return False |
|
except (NotAlgebraic, NotImplementedError): |
|
pass |
|
|
|
|
|
|
|
|
|
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: |
|
|
|
try: |
|
n2 = self._eval_evalf(2) |
|
|
|
|
|
|
|
except ValueError: |
|
return None |
|
if n2 is None: |
|
return None |
|
if getattr(n2, '_prec', 1) == 1: |
|
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) |
|
|
|
|
|
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: |
|
|
|
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): |
|
|
|
|
|
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): |
|
|
|
|
|
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: |
|
|
|
|
|
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: |
|
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] |
|
|
|
|
|
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: |
|
|
|
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)) |
|
|
|
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: |
|
|
|
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):]) |
|
|
|
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 |
|
|
|
|
|
|
|
sym = set() |
|
other = [] |
|
for d in deps: |
|
if isinstance(d, Symbol): |
|
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: |
|
return (Add(*indep), _unevaluated_Add(*depend)) |
|
else: |
|
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]: |
|
|
|
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() |
|
|
|
if c.is_Number and c is not S.NegativeOne: |
|
|
|
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 |
|
return |
|
if c == ps: |
|
return cs |
|
|
|
newargs = [] |
|
for arg in ps.args: |
|
newarg = arg.extract_multiplicatively(c) |
|
if newarg is None: |
|
return |
|
newargs.append(newarg) |
|
if cs is not S.One: |
|
args = [cs*t for t in newargs] |
|
|
|
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 |
|
|
|
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 |
|
|
|
|
|
|
|
|
|
if c.is_Add and c.args[0].is_Number: |
|
|
|
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 |
|
|
|
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 |
|
|
|
|
|
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 |
|
|
|
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) |
|
|
|
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): |
|
|
|
return True |
|
|
|
|
|
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 |
|
|
|
|
|
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 |
|
|
|
|
|
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 |
|
|
|
|
|
|
|
|
|
|
|
|
|
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) |
|
|
|
|
|
|
|
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: |
|
return (si.subs({x: x/cdir - x0/cdir}) for si in s) |
|
return s.subs({x: x/cdir - x0/cdir}) |
|
|
|
|
|
|
|
if x.is_positive is x.is_negative is None or x.is_Symbol is not True: |
|
|
|
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: |
|
s1 = self._eval_nseries(x, n=n, logx=logx, cdir=cdir) |
|
o = s1.getO() or S.Zero |
|
if o: |
|
|
|
ngot = o.getn() |
|
if ngot > n: |
|
|
|
|
|
if n != 0: |
|
s1 += o.subs(x, x**Rational(n, ngot)) |
|
else: |
|
s1 += Order(1, x) |
|
elif ngot < n: |
|
|
|
|
|
|
|
|
|
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): |
|
|
|
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: |
|
def yield_lseries(s): |
|
"""Return terms of lseries one at a time.""" |
|
for si in s: |
|
if not si.is_Add: |
|
yield si |
|
continue |
|
|
|
|
|
|
|
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 |
|
|
|
|
|
|
|
|
|
|
|
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) |
|
|
|
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) |
|
|
|
|
|
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 |
|
|
|
|
|
|
|
for t in terms: |
|
coeff, expo = t.as_coeff_exponent(k) |
|
if coeff.has(x): |
|
|
|
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): |
|
|
|
|
|
|
|
|
|
|
|
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 != '+': |
|
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) |
|
|
|
|
|
|
|
|
|
|
|
def diff(self, *symbols, **assumptions): |
|
assumptions.setdefault("evaluate", True) |
|
return _derivative_dispatch(self, *symbols, **assumptions) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
|
|
|
|
|
|
|
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 |
|
|
|
_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 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
|
|
|
|
|
|
|
|
|
|
|
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) |
|
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: |
|
|
|
|
|
dps = max(15, allow) |
|
else: |
|
allow = min(allow, dps) |
|
|
|
|
|
shift = -digits_to_decimal + dps |
|
extra = 1 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
xf = x.n(dps + extra)*Pow(10, shift) |
|
if xf.is_Number and xf._prec == 1: |
|
|
|
if x.equals(0): |
|
return Float(0) |
|
raise ValueError('not computing with precision') |
|
xi = Integer(xf) |
|
|
|
|
|
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 |
|
elif dif2 == .5: |
|
xi += sign if xi%2 else -sign |
|
|
|
ip = p - shift |
|
|
|
xr = round(xi.p, ip) |
|
|
|
rv = Rational(xr, Pow(10, shift)) |
|
|
|
if rv.is_Integer: |
|
if n is None: |
|
return rv |
|
|
|
return Float(str(rv), dps) |
|
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))) |
|
|
|
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 |
|
|