def _eval_rewrite_as_Add(self, *args, **kwargs): """return Eq(L, R) as L - R. To control the evaluation of the result set pass `evaluate=True` to give L - R; if `evaluate=None` then terms in L and R will not cancel but they will be listed in canonical order; otherwise non-canonical args will be returned. Examples ======== >>> from sympy import Eq, Add >>> from sympy.abc import b, x >>> eq = Eq(x + b, x - b) >>> eq.rewrite(Add) 2*b >>> eq.rewrite(Add, evaluate=None).args (b, b, x, -x) >>> eq.rewrite(Add, evaluate=False).args (b, x, b, -x) """ L, R = args evaluate = kwargs.get('evaluate', True) if evaluate: # allow cancellation of args return L - R args = Add.make_args(L) + Add.make_args(-R) if evaluate is None: # no cancellation, but canonical return _unevaluated_Add(*args) # no cancellation, not canonical return Add._from_args(args)
def _gcd_terms(terms, isprimitive=False, fraction=True): """Helper function for :func:`gcd_terms`. If ``isprimitive`` is True then the call to primitive for an Add will be skipped. This is useful when the content has already been extrated. If ``fraction`` is True then the expression will appear over a common denominator, the lcm of all term denominators. """ if isinstance(terms, Basic) and not isinstance(terms, Tuple): terms = Add.make_args(terms) terms = map(Term, [t for t in terms if t]) # there is some simplification that may happen if we leave this # here rather than duplicate it before the mapping of Term onto # the terms if len(terms) == 0: return S.Zero, S.Zero, S.One if len(terms) == 1: cont = terms[0].coeff numer = terms[0].numer.as_expr() denom = terms[0].denom.as_expr() else: cont = terms[0] for term in terms[1:]: cont = cont.gcd(term) for i, term in enumerate(terms): terms[i] = term.quo(cont) if fraction: denom = terms[0].denom for term in terms[1:]: denom = denom.lcm(term.denom) numers = [] for term in terms: numer = term.numer.mul(denom.quo(term.denom)) numers.append(term.coeff*numer.as_expr()) else: numers = [t.as_expr() for t in terms] denom = Term(S(1)).numer cont = cont.as_expr() numer = Add(*numers) denom = denom.as_expr() if not isprimitive and numer.is_Add: _cont, numer = numer.primitive() cont *= _cont return cont, numer, denom
def factor_terms(expr, radical=False): """Remove common factors from terms in all arguments without changing the underlying structure of the expr. No expansion or simplification (and no processing of non-commutatives) is performed. If radical=True then a radical common to all terms will be factored out of any Add sub-expressions of the expr. Examples ======== >>> from sympy import factor_terms, Symbol >>> from sympy.abc import x, y >>> factor_terms(x + x*(2 + 4*y)**3) x*(8*(2*y + 1)**3 + 1) >>> A = Symbol('A', commutative=False) >>> factor_terms(x*A + x*A + x*y*A) x*(y*A + 2*A) """ expr = sympify(expr) is_iterable = iterable(expr) if not isinstance(expr, Basic) or expr.is_Atom: if is_iterable: return type(expr)([factor_terms(i, radical=radical) for i in expr]) return expr if expr.is_Function or is_iterable or not hasattr(expr, 'args_cnc'): args = expr.args newargs = tuple([factor_terms(i, radical=radical) for i in args]) if newargs == args: return expr return expr.func(*newargs) cont, p = expr.as_content_primitive(radical=radical) list_args, nc = zip(*[ai.args_cnc() for ai in Add.make_args(p)]) list_args = list(list_args) nc = [((Dummy(), Mul._from_args(i)) if i else None) for i in nc] ncreps = dict([i for i in nc if i is not None]) for i, a in enumerate(list_args): if nc[i] is not None: a.append(nc[i][0]) a = Mul._from_args(a) # gcd_terms will fix up ordering list_args[i] = gcd_terms(a, isprimitive=True) # cancel terms that may not have cancelled p = Add._from_args(list_args) # gcd_terms will fix up ordering p = gcd_terms(p, isprimitive=True).xreplace(ncreps) return _keep_coeff(cont, p)
def _peeloff_pi(arg): """ Split ARG into two parts, a "rest" and a multiple of pi/2. This assumes ARG to be an Add. The multiple of pi returned in the second position is always a Rational. Examples: >>> from sympy.functions.elementary.trigonometric import _peeloff_pi as peel >>> from sympy import pi >>> from sympy.abc import x, y >>> peel(x + pi/2) (x, pi/2) >>> peel(x + 2*pi/3 + pi*y) (x + pi*y + pi/6, pi/2) """ for a in Add.make_args(arg): if a is S.Pi: K = S.One break elif a.is_Mul: K, p = a.as_two_terms() if p is S.Pi and K.is_Rational: break else: return arg, S.Zero m1 = (K % S.Half) * S.Pi m2 = K*S.Pi - m1 return arg - m2, m2
def _eval_imageset(self, f): expr = f.expr if not isinstance(expr, Expr): return if len(f.variables) > 1: return n = f.variables[0] # f(x) + c and f(-x) + c cover the same integers # so choose the form that has the fewest negatives c = f(0) fx = f(n) - c f_x = f(-n) - c neg_count = lambda e: sum(_coeff_isneg(_) for _ in Add.make_args(e)) if neg_count(f_x) < neg_count(fx): expr = f_x + c a = Wild('a', exclude=[n]) b = Wild('b', exclude=[n]) match = expr.match(a*n + b) if match and match[a]: # canonical shift expr = match[a]*n + match[b] % match[a] if expr != f.expr: return ImageSet(Lambda(n, expr), S.Integers)
def _transform_explike_DE(DE, g, x, order, syms): """Converts DE with free parameters into DE with constant coefficients.""" from sympy.solvers.solveset import linsolve eq = [] highest_coeff = DE.coeff(Derivative(g(x), x, order)) for i in range(order): coeff = DE.coeff(Derivative(g(x), x, i)) coeff = (coeff / highest_coeff).expand().collect(x) for t in Add.make_args(coeff): eq.append(t) temp = [] for e in eq: if e.has(x): break elif e.has(Symbol): temp.append(e) else: eq = temp if eq: sol = dict(zip(syms, (i for s in linsolve(eq, list(syms)) for i in s))) if sol: DE = DE.subs(sol) DE = DE.factor().as_coeff_mul(Derivative)[1][0] DE = DE.collect(Derivative(g(x))) return DE
def _peeloff_ipi(arg): """ Split ARG into two parts, a "rest" and a multiple of I*pi/2. This assumes ARG to be an Add. The multiple of I*pi returned in the second position is always a Rational. Examples ======== >>> from sympy.functions.elementary.hyperbolic import _peeloff_ipi as peel >>> from sympy import pi, I >>> from sympy.abc import x, y >>> peel(x + I*pi/2) (x, I*pi/2) >>> peel(x + I*2*pi/3 + I*pi*y) (x + I*pi*y + I*pi/6, I*pi/2) """ for a in Add.make_args(arg): if a == S.Pi*S.ImaginaryUnit: K = S.One break elif a.is_Mul: K, p = a.as_two_terms() if p == S.Pi*S.ImaginaryUnit and K.is_Rational: break else: return arg, S.Zero m1 = (K % S.Half)*S.Pi*S.ImaginaryUnit m2 = K*S.Pi*S.ImaginaryUnit - m1 return arg - m2, m2
def _eval_simplify(self, ratio=1.7, measure=None): from sympy.simplify.simplify import factor_sum, sum_combine from sympy.core.function import expand from sympy.core.mul import Mul # split the function into adds terms = Add.make_args(expand(self.function)) s_t = [] # Sum Terms o_t = [] # Other Terms for term in terms: if term.has(Sum): # if there is an embedded sum here # it is of the form x * (Sum(whatever)) # hence we make a Mul out of it, and simplify all interior sum terms subterms = Mul.make_args(expand(term)) out_terms = [] for subterm in subterms: # go through each term if isinstance(subterm, Sum): # if it's a sum, simplify it out_terms.append(subterm._eval_simplify()) else: # otherwise, add it as is out_terms.append(subterm) # turn it back into a Mul s_t.append(Mul(*out_terms)) else: o_t.append(term) # next try to combine any interior sums for further simplification result = Add(sum_combine(s_t), *o_t) return factor_sum(result, limits=self.limits)
def do(expr): from sympy.concrete.summations import Sum from sympy.simplify.simplify import factor_sum is_iterable = iterable(expr) if not isinstance(expr, Basic) or expr.is_Atom: if is_iterable: return type(expr)([do(i) for i in expr]) return expr if expr.is_Pow or expr.is_Function or \ is_iterable or not hasattr(expr, 'args_cnc'): args = expr.args newargs = tuple([do(i) for i in args]) if newargs == args: return expr return expr.func(*newargs) if isinstance(expr, Sum): return factor_sum(expr, radical=radical, clear=clear, fraction=fraction, sign=sign) cont, p = expr.as_content_primitive(radical=radical, clear=clear) if p.is_Add: list_args = [do(a) for a in Add.make_args(p)] # get a common negative (if there) which gcd_terms does not remove if all(a.as_coeff_Mul()[0].extract_multiplicatively(-1) is not None for a in list_args): cont = -cont list_args = [-a for a in list_args] # watch out for exp(-(x+2)) which gcd_terms will change to exp(-x-2) special = {} for i, a in enumerate(list_args): b, e = a.as_base_exp() if e.is_Mul and e != Mul(*e.args): list_args[i] = Dummy() special[list_args[i]] = a # rebuild p not worrying about the order which gcd_terms will fix p = Add._from_args(list_args) p = gcd_terms(p, isprimitive=True, clear=clear, fraction=fraction).xreplace(special) elif p.args: p = p.func( *[do(a) for a in p.args]) rv = _keep_coeff(cont, p, clear=clear, sign=sign) return rv
def factor_terms(expr): """Remove common factors from terms in all arguments without changing the underlying structure of the expr. No expansion or simplification (and no processing of non-commutative) is performed. **Examples** >>> from sympy import factor_terms, Symbol >>> from sympy.abc import x, y >>> factor_terms(x + x*(2 + 4*y)**3) x*(8*(2*y + 1)**3 + 1) >>> A = Symbol('A', commutative=False) >>> factor_terms(x*A + x*A + x*y*A) x*(y*A + 2*A) """ expr = sympify(expr) if iterable(expr): return type(expr)([factor_terms(i) for i in expr]) if not isinstance(expr, Basic) or expr.is_Atom: return expr if expr.is_Function: return expr.func(*[factor_terms(i) for i in expr.args]) cont, p = expr.as_content_primitive() list_args, nc = zip(*[ai.args_cnc(clist=True) for ai in Add.make_args(p)]) list_args = list(list_args) nc = [((Dummy(), Mul._from_args(i)) if i else None) for i in nc] ncreps = dict([i for i in nc if i is not None]) for i, a in enumerate(list_args): if nc[i] is not None: a.append(nc[i][0]) a = Mul._from_args(a) # gcd_terms will fix up ordering list_args[i] = gcd_terms(a, isprimitive=True) # cancel terms that may not have cancelled p = Add._from_args(list_args) # gcd_terms will fix up ordering p = gcd_terms(p, isprimitive=True).subs(ncreps) # exact subs could be used here return _keep_coeff(cont, p)
def _gcd_terms(terms, isprimitive=False): """Helper function for :func:`gcd_terms`. If `isprimitive` is True then the call to primitive for an Add will be skipped. This is useful when the content has already been extrated.""" if isinstance(terms, Basic) and not isinstance(terms, Tuple): terms = Add.make_args(terms) if len(terms) <= 1: if not terms: return S.Zero, S.Zero, S.One else: return terms[0], S.One, S.One terms = map(Term, terms) cont = terms[0] for term in terms[1:]: cont = cont.gcd(term) for i, term in enumerate(terms): terms[i] = term.quo(cont) denom = terms[0].denom for term in terms[1:]: denom = denom.lcm(term.denom) numers = [] for term in terms: numer = term.numer.mul(denom.quo(term.denom)) numers.append(term.coeff*numer.as_expr()) cont = cont.as_expr() numer = Add(*numers) denom = denom.as_expr() if not isprimitive and numer.is_Add: _cont, numer = numer.primitive() cont *= _cont return cont, numer, denom
def _solve_explike_DE(f, x, DE, g, k): """Solves DE with constant coefficients.""" from sympy.solvers import rsolve for t in Add.make_args(DE): coeff, d = t.as_independent(g) if coeff.free_symbols: return RE = exp_re(DE, g, k) init = {} for i in range(len(Add.make_args(RE))): if i: f = f.diff(x) init[g(k).subs(k, i)] = f.limit(x, 0) sol = rsolve(RE, g(k), init) if sol: return (sol / factorial(k), S.Zero, S.Zero)
def _solve_hyper_RE(f, x, RE, g, k): """See docstring of :func:`rsolve_hypergeometric` for details.""" terms = Add.make_args(RE) if len(terms) == 2: gs = list(RE.atoms(Function)) P, Q = map(RE.coeff, gs) m = gs[1].args[0] - gs[0].args[0] if m < 0: P, Q = Q, P m = abs(m) return rsolve_hypergeometric(f, x, P, Q, k, m)
def _gcd_terms(terms): """Helper function for :func:`gcd_terms`. """ if isinstance(terms, Basic): terms = Add.make_args(terms) if len(terms) <= 1: if not terms: return S.Zero, S.Zero, S.One else: return terms[0], S.One, S.One terms = map(Term, terms) cont = terms[0] for term in terms[1:]: cont = cont.gcd(term) for i, term in enumerate(terms): terms[i] = term.quo(cont) denom = terms[0].denom for term in terms[1:]: denom = denom.lcm(term.denom) numers = [] for term in terms: numer = term.numer.mul(denom.quo(term.denom)) numers.append(term.coeff*numer.as_expr()) cont = cont.as_expr() numer = Add(*numers) denom = denom.as_expr() if numer.is_Add: _cont, numer = numer.primitive() cont *= _cont return cont, numer, denom
def hyper_re(DE, r, k): """Converts a DE into a RE. Performs the substitution: .. math:: x^l f^j(x) \\to (k + 1 - l)_j . a_{k + j - l} Normalises the terms so that lowest order of a term is always r(k). Examples ======== >>> from sympy import Function, Derivative >>> from sympy.series.formal import hyper_re >>> from sympy.abc import x, k >>> f, r = Function('f'), Function('r') >>> hyper_re(-f(x) + Derivative(f(x)), r, k) (k + 1)*r(k + 1) - r(k) >>> hyper_re(-x*f(x) + Derivative(f(x), x, x), r, k) (k + 2)*(k + 3)*r(k + 3) - r(k) See Also ======== sympy.series.formal.exp_re """ RE = S.Zero g = DE.atoms(Function).pop() x = g.atoms(Symbol).pop() mini = None for t in Add.make_args(DE.expand()): coeff, d = t.as_independent(g) c, v = coeff.as_independent(x) l = v.as_coeff_exponent(x)[1] if isinstance(d, Derivative): j = len(d.args[1:]) else: j = 0 RE += c * rf(k + 1 - l, j) * r(k + j - l) if mini is None or j - l < mini: mini = j - l RE = RE.subs(k, k - mini) m = Wild('m') return RE.collect(r(k + m))
def calc_part(expr, nexpr): from sympy.core.add import Add nint = int(to_int(nexpr, rnd)) n, c, p, b = nexpr is_int = (p == 0) if not is_int: # if there are subs and they all contain integer re/im parts # then we can (hopefully) safely substitute them into the # expression s = options.get('subs', False) if s: doit = True from sympy.core.compatibility import as_int for v in s.values(): try: as_int(v) except ValueError: try: [as_int(i) for i in v.as_real_imag()] continue except (ValueError, AttributeError): doit = False break if doit: expr = expr.subs(s) expr = Add(expr, -nint, evaluate=False) x, _, x_acc, _ = evalf(expr, 10, options) try: check_target(expr, (x, None, x_acc, None), 3) except PrecisionExhausted: if not expr.equals(0): raise PrecisionExhausted x = fzero nint += int(no*(mpf_cmp(x or fzero, fzero) == no)) nint = from_int(nint) return nint, fastlog(nint) + 10
def _solve_simple(f, x, DE, g, k): """Converts DE into RE and solves using :func:`rsolve`.""" from sympy.solvers import rsolve RE = hyper_re(DE, g, k) init = {} for i in range(len(Add.make_args(RE))): if i: f = f.diff(x) init[g(k).subs(k, i)] = f.limit(x, 0) / factorial(i) sol = rsolve(RE, g(k), init) if sol: return (sol, S.Zero, S.Zero)
def exp_re(DE, r, k): """Converts a DE with constant coefficients (explike) into a RE. Performs the substitution: .. math:: f^j(x) \\to r(k + j) Normalises the terms so that lowest order of a term is always r(k). Examples ======== >>> from sympy import Function, Derivative >>> from sympy.series.formal import exp_re >>> from sympy.abc import x, k >>> f, r = Function('f'), Function('r') >>> exp_re(-f(x) + Derivative(f(x)), r, k) -r(k) + r(k + 1) >>> exp_re(Derivative(f(x), x) + Derivative(f(x), x, x), r, k) r(k) + r(k + 1) See Also ======== sympy.series.formal.hyper_re """ RE = S.Zero g = DE.atoms(Function).pop() mini = None for t in Add.make_args(DE): coeff, d = t.as_independent(g) if isinstance(d, Derivative): j = len(d.args) - 1 else: j = 0 if mini is None or j < mini: mini = j RE += coeff * r(k + j) if mini: RE = RE.subs(k, k - mini) return RE
def _transform_explike_DE(DE, g, x, order, syms): """Converts DE with free parameters into DE with constant coefficients.""" from sympy.solvers import solve eq = [] highest_coeff = DE.coeff(Derivative(g(x), x, order)) for i in range(order): coeff = DE.coeff(Derivative(g(x), x, i)) coeff = (coeff / highest_coeff).expand().collect(x) for t in Add.make_args(coeff): if t.has(x): eq.append(t) sol = solve(eq, syms, dict=True) if sol: DE = DE.subs(sol[0]) DE = DE.factor().as_coeff_mul(Derivative)[1][0] DE = DE.collect(Derivative(g(x))) return DE
def integrate(field=None): irreducibles = set() for poly in reducibles: for z in poly.atoms(Symbol): if z in V: break else: continue irreducibles |= set(root_factors(poly, z, filter=field)) log_coeffs, log_part = [], [] B = _symbols('B', len(irreducibles)) for i, poly in enumerate(irreducibles): if poly.has(*V): log_coeffs.append(B[i]) log_part.append(log_coeffs[-1] * log(poly)) coeffs = poly_coeffs + log_coeffs candidate = poly_part/poly_denom + Add(*log_part) h = F - derivation(candidate) / denom numer = h.as_numer_denom()[0].expand() equations = {} for term in Add.make_args(numer): coeff, dependent = term.as_independent(*V) if dependent in equations: equations[dependent] += coeff else: equations[dependent] = coeff solution = solve(equations.values(), *coeffs) if solution is not None: return (solution, candidate, coeffs) else: return None
def integrate(self, x=None, **kwargs): """Integrate Formal Power Series. Examples ======== >>> from sympy import fps, sin, integrate >>> from sympy.abc import x >>> f = fps(sin(x)) >>> f.integrate(x).truncate() -1 + x**2/2 - x**4/24 + O(x**6) >>> integrate(f, (x, 0, 1)) -cos(1) + 1 """ from sympy.integrals import integrate if x is None: x = self.x elif iterable(x): return integrate(self.function, x) f = integrate(self.function, x) ind = integrate(self.ind, x) ind += (f - ind).limit(x, 0) # constant of integration pow_xk = self._get_pow_x(self.xk.formula) ak = self.ak k = ak.variables[0] if ak.formula.has(x): form = [] for e, c in ak.formula.args: temp = S.Zero for t in Add.make_args(e): pow_x = self._get_pow_x(t) temp += t / (pow_xk + pow_x + 1) form.append((temp, c)) form = Piecewise(*form) ak = sequence(form.subs(k, k - 1), (k, ak.start + 1, ak.stop)) else: ak = sequence((ak.formula / (pow_xk + 1)).subs(k, k - 1), (k, ak.start + 1, ak.stop)) return self.func(f, self.x, self.x0, self.dir, (ak, self.xk, ind))
def dominant(expr, n): """Finds the most dominating term in an expression. if limit(a/b, n, oo) is oo then a dominates b. if limit(a/b, n, oo) is 0 then b dominates a. else a and b are comparable. returns the most dominant term. If no unique domiant term, then returns ``None``. Examples ======== >>> from sympy import Sum >>> from sympy.series.limitseq import dominant >>> from sympy.abc import n, k >>> dominant(5*n**3 + 4*n**2 + n + 1, n) 5*n**3 >>> dominant(2**n + Sum(k, (k, 0, n)), n) 2**n See Also ======== sympy.series.limitseq.dominant """ terms = Add.make_args(expr.expand(func=True)) term0 = terms[-1] comp = [term0] # comparable terms for t in terms[:-1]: e = (term0 / t).gammasimp() l = limit_seq(e, n) if l is S.Zero: term0 = t comp = [term0] elif l is None: return None elif l not in [S.Infinity, -S.Infinity]: comp.append(t) if len(comp) > 1: return None return term0
def _eval_term(self, pt): try: pt_xk = self.xk.coeff(pt) pt_ak = self.ak.coeff(pt).simplify() # Simplify the coefficients except IndexError: term = S.Zero else: term = (pt_ak * pt_xk) if self.ind: ind = S.Zero for t in Add.make_args(self.ind): pow_x = self._get_pow_x(t) if pt == 0 and pow_x < 1: ind += t elif pow_x >= pt and pow_x < pt + 1: ind += t term += ind return term.collect(self.x)
def _eval_derivative(self, x): f = self.function.diff(x) ind = self.ind.diff(x) pow_xk = self._get_pow_x(self.xk.formula) ak = self.ak k = ak.variables[0] if ak.formula.has(x): form = [] for e, c in ak.formula.args: temp = S.Zero for t in Add.make_args(e): pow_x = self._get_pow_x(t) temp += t * (pow_xk + pow_x) form.append((temp, c)) form = Piecewise(*form) ak = sequence(form.subs(k, k + 1), (k, ak.start - 1, ak.stop)) else: ak = sequence((ak.formula * pow_xk).subs(k, k + 1), (k, ak.start - 1, ak.stop)) return self.func(f, self.x, self.x0, self.dir, (ak, self.xk, ind))
def eval(cls, arg): from sympy.calculus import AccumBounds from sympy.sets.setexpr import SetExpr from sympy.matrices.matrices import MatrixBase from sympy import im, logcombine, re if arg.is_Number: if arg is S.NaN: return S.NaN elif arg.is_zero: return S.One elif arg is S.One: return S.Exp1 elif arg is S.Infinity: return S.Infinity elif arg is S.NegativeInfinity: return S.Zero elif arg is S.ComplexInfinity: return S.NaN elif isinstance(arg, log): return arg.args[0] elif isinstance(arg, AccumBounds): return AccumBounds(exp(arg.min), exp(arg.max)) elif isinstance(arg, SetExpr): return arg._eval_func(cls) elif arg.is_Mul: coeff = arg.as_coefficient(S.Pi * S.ImaginaryUnit) if coeff: if (2 * coeff).is_integer: if coeff.is_even: return S.One elif coeff.is_odd: return S.NegativeOne elif (coeff + S.Half).is_even: return -S.ImaginaryUnit elif (coeff + S.Half).is_odd: return S.ImaginaryUnit elif coeff.is_Rational: ncoeff = coeff % 2 # restrict to [0, 2pi) if ncoeff > 1: # restrict to (-pi, pi] ncoeff -= 2 if ncoeff != coeff: return cls(ncoeff * S.Pi * S.ImaginaryUnit) # Warning: code in risch.py will be very sensitive to changes # in this (see DifferentialExtension). # look for a single log factor coeff, terms = arg.as_coeff_Mul() # but it can't be multiplied by oo if coeff in [S.NegativeInfinity, S.Infinity]: if terms.is_number: if coeff is S.NegativeInfinity: terms = -terms if re(terms).is_zero and terms is not S.Zero: return S.NaN if re(terms).is_positive and im(terms) is not S.Zero: return S.ComplexInfinity if re(terms).is_negative: return S.Zero return None coeffs, log_term = [coeff], None for term in Mul.make_args(terms): term_ = logcombine(term) if isinstance(term_, log): if log_term is None: log_term = term_.args[0] else: return None elif term.is_comparable: coeffs.append(term) else: return None return log_term**Mul(*coeffs) if log_term else None elif arg.is_Add: out = [] add = [] argchanged = False for a in arg.args: if a is S.One: add.append(a) continue newa = cls(a) if isinstance(newa, cls): if newa.args[0] != a: add.append(newa.args[0]) argchanged = True else: add.append(a) else: out.append(newa) if out or argchanged: return Mul(*out) * cls(Add(*add), evaluate=False) elif isinstance(arg, MatrixBase): return arg.exp() if arg.is_zero: return S.One
def rational_laurent_series(num, den, x, r, m, n): r""" The function computes the Laurent series coefficients of a rational function. Parameters ========== num: A Poly object that is the numerator of `f(x)`. den: A Poly object that is the denominator of `f(x)`. x: The variable of expansion of the series. r: The point of expansion of the series. m: Multiplicity of r if r is a pole of `f(x)`. Should be zero otherwise. n: Order of the term upto which the series is expanded. Returns ======= series: A dictionary that has power of the term as key and coefficient of that term as value. Below is a basic outline of how the Laurent series of a rational function `f(x)` about `x_0` is being calculated - 1. Substitute `x + x_0` in place of `x`. If `x_0` is a pole of `f(x)`, multiply the expression by `x^m` where `m` is the multiplicity of `x_0`. Denote the the resulting expression as g(x). We do this substitution so that we can now find the Laurent series of g(x) about `x = 0`. 2. We can then assume that the Laurent series of `g(x)` takes the following form - .. math:: g(x) = \frac{num(x)}{den(x)} = \sum_{m = 0}^{\infty} a_m x^m where `a_m` denotes the Laurent series coefficients. 3. Multiply the denominator to the RHS of the equation and form a recurrence relation for the coefficients `a_m`. """ one = Poly(1, x, extension=True) if r == oo: # Series at x = oo is equal to first transforming # the function from x -> 1/x and finding the # series at x = 0 num, den = inverse_transform_poly(num, den, x) r = S(0) if r: # For an expansion about a non-zero point, a # transformation from x -> x + r must be made num = num.transform(Poly(x + r, x, extension=True), one) den = den.transform(Poly(x + r, x, extension=True), one) # Remove the pole from the denominator if the series # expansion is about one of the poles num, den = (num * x**m).cancel(den, include=True) # Equate coefficients for the first terms (base case) maxdegree = 1 + max(num.degree(), den.degree()) syms = symbols(f'a:{maxdegree}', cls=Dummy) diff = num - den * Poly(syms[::-1], x) coeff_diffs = diff.all_coeffs()[::-1][:maxdegree] (coeffs, ) = linsolve(coeff_diffs, syms) # Use the recursion relation for the rest recursion = den.all_coeffs()[::-1] div, rec_rhs = recursion[0], recursion[1:] series = list(coeffs) while len(series) < n: next_coeff = Add(*(c * series[-1 - n] for n, c in enumerate(rec_rhs))) / div series.append(-next_coeff) series = {m - i: val for i, val in enumerate(series)} return series
def factor_nc(expr): """Return the factored form of ``expr`` while handling non-commutative expressions. Examples ======== >>> from sympy.core.exprtools import factor_nc >>> from sympy import Symbol >>> from sympy.abc import x >>> A = Symbol('A', commutative=False) >>> B = Symbol('B', commutative=False) >>> factor_nc((x**2 + 2*A*x + A**2).expand()) (x + A)**2 >>> factor_nc(((x + A)*(x + B)).expand()) (x + A)*(x + B) """ from sympy.simplify.simplify import powsimp from sympy.polys import gcd, factor def _pemexpand(expr): "Expand with the minimal set of hints necessary to check the result." return expr.expand(deep=True, mul=True, power_exp=True, power_base=False, basic=False, multinomial=True, log=False) expr = sympify(expr) if not isinstance(expr, Expr) or not expr.args: return expr if not expr.is_Add: return expr.func(*[factor_nc(a) for a in expr.args]) expr, rep, nc_symbols = _mask_nc(expr) if rep: return factor(expr).subs(rep) else: args = [a.args_cnc() for a in Add.make_args(expr)] c = g = l = r = S.One hit = False # find any commutative gcd term for i, a in enumerate(args): if i == 0: c = Mul._from_args(a[0]) elif a[0]: c = gcd(c, Mul._from_args(a[0])) else: c = S.One if c is not S.One: hit = True c, g = c.as_coeff_Mul() if g is not S.One: for i, (cc, _) in enumerate(args): cc = list(Mul.make_args(Mul._from_args(list(cc)) / g)) args[i][0] = cc for i, (cc, _) in enumerate(args): cc[0] = cc[0] / c args[i][0] = cc # find any noncommutative common prefix for i, a in enumerate(args): if i == 0: n = a[1][:] else: n = common_prefix(n, a[1]) if not n: # is there a power that can be extracted? if not args[0][1]: break b, e = args[0][1][0].as_base_exp() ok = False if e.is_Integer: for t in args: if not t[1]: break bt, et = t[1][0].as_base_exp() if et.is_Integer and bt == b: e = min(e, et) else: break else: ok = hit = True l = b**e il = b**-e for i, a in enumerate(args): args[i][1][0] = il * args[i][1][0] break if not ok: break else: hit = True lenn = len(n) l = Mul(*n) for i, a in enumerate(args): args[i][1] = args[i][1][lenn:] # find any noncommutative common suffix for i, a in enumerate(args): if i == 0: n = a[1][:] else: n = common_suffix(n, a[1]) if not n: # is there a power that can be extracted? if not args[0][1]: break b, e = args[0][1][-1].as_base_exp() ok = False if e.is_Integer: for t in args: if not t[1]: break bt, et = t[1][-1].as_base_exp() if et.is_Integer and bt == b: e = min(e, et) else: break else: ok = hit = True r = b**e il = b**-e for i, a in enumerate(args): args[i][1][-1] = args[i][1][-1] * il break if not ok: break else: hit = True lenn = len(n) r = Mul(*n) for i, a in enumerate(args): args[i][1] = a[1][:len(a[1]) - lenn] if hit: mid = Add(*[Mul(*cc) * Mul(*nc) for cc, nc in args]) else: mid = expr # sort the symbols so the Dummys would appear in the same # order as the original symbols, otherwise you may introduce # a factor of -1, e.g. A**2 - B**2) -- {A:y, B:x} --> y**2 - x**2 # and the former factors into two terms, (A - B)*(A + B) while the # latter factors into 3 terms, (-1)*(x - y)*(x + y) rep1 = [(n, Dummy()) for n in sorted(nc_symbols, key=default_sort_key)] unrep1 = [(v, k) for k, v in rep1] unrep1.reverse() new_mid, r2, _ = _mask_nc(mid.subs(rep1)) new_mid = powsimp(factor(new_mid)) new_mid = new_mid.subs(r2).subs(unrep1) if new_mid.is_Pow: return _keep_coeff(c, g * l * new_mid * r) if new_mid.is_Mul: # XXX TODO there should be a way to inspect what order the terms # must be in and just select the plausible ordering without # checking permutations cfac = [] ncfac = [] for f in new_mid.args: if f.is_commutative: cfac.append(f) else: b, e = f.as_base_exp() if e.is_Integer: ncfac.extend([b] * e) else: ncfac.append(f) pre_mid = g * Mul(*cfac) * l target = _pemexpand(expr / c) for s in variations(ncfac, len(ncfac)): ok = pre_mid * Mul(*s) * r if _pemexpand(ok) == target: return _keep_coeff(c, ok) # mid was an Add that didn't factor successfully return _keep_coeff(c, g * l * mid * r)
def eval(cls, arg): if arg.is_Number: if arg is S.NaN: return S.NaN elif arg is S.Zero: return S.One elif arg is S.One: return S.Exp1 elif arg is S.Infinity: return S.Infinity elif arg is S.NegativeInfinity: return S.Zero elif arg.func is log: return arg.args[0] elif arg.is_Mul: coeff = arg.as_coefficient(S.Pi * S.ImaginaryUnit) if coeff is not None: if (2 * coeff).is_integer: if coeff.is_even: return S.One elif coeff.is_odd: return S.NegativeOne elif (coeff + S.Half).is_even: return -S.ImaginaryUnit elif (coeff + S.Half).is_odd: return S.ImaginaryUnit I = S.ImaginaryUnit oo = S.Infinity a = Wild("a", exclude=[I, oo]) r = arg.match(I * a * oo) if r and r[a] != 0: return S.NaN args = Add.make_args(arg) included, excluded = [], [] for arg in args: coeff, terms = arg.as_coeff_mul() if coeff is S.Infinity: excluded.append(coeff ** Mul(*terms)) else: coeffs, log_term = [coeff], None for term in terms: if term.func is log: if log_term is None: log_term = term.args[0] else: log_term = None break elif term.is_comparable: coeffs.append(term) else: log_term = None break if log_term is not None: excluded.append(log_term ** Mul(*coeffs)) else: included.append(arg) if excluded: return Mul(*(excluded + [cls(Add(*included))]))
def __new__(cls, lhs, rhs=0, **options): from sympy.core.add import Add from sympy.core.logic import fuzzy_bool from sympy.core.expr import _n2 from sympy.simplify.simplify import clear_coefficients lhs = _sympify(lhs) rhs = _sympify(rhs) evaluate = options.pop('evaluate', global_evaluate[0]) if evaluate: # If one expression has an _eval_Eq, return its results. if hasattr(lhs, '_eval_Eq'): r = lhs._eval_Eq(rhs) if r is not None: return r if hasattr(rhs, '_eval_Eq'): r = rhs._eval_Eq(lhs) if r is not None: return r # If expressions have the same structure, they must be equal. if lhs == rhs: return S.true elif all(isinstance(i, BooleanAtom) for i in (rhs, lhs)): return S.false # check finiteness fin = L, R = [i.is_finite for i in (lhs, rhs)] if None not in fin: if L != R: return S.false if L is False: if lhs == -rhs: # Eq(oo, -oo) return S.false return S.true elif None in fin and False in fin: return Relational.__new__(cls, lhs, rhs, **options) if all(isinstance(i, Expr) for i in (lhs, rhs)): # see if the difference evaluates dif = lhs - rhs z = dif.is_zero if z is not None: if z is False and dif.is_commutative: # issue 10728 return S.false if z: return S.true # evaluate numerically if possible n2 = _n2(lhs, rhs) if n2 is not None: return _sympify(n2 == 0) # see if the ratio evaluates n, d = dif.as_numer_denom() rv = None if n.is_zero: rv = d.is_nonzero elif n.is_finite: if d.is_infinite: rv = S.true elif n.is_zero is False: rv = d.is_infinite if rv is None: # if the condition that makes the denominator infinite does not # make the original expression True then False can be returned l, r = clear_coefficients(d, S.Infinity) args = [_.subs(l, r) for _ in (lhs, rhs)] if args != [lhs, rhs]: rv = fuzzy_bool(Eq(*args)) if rv is True: rv = None elif any(a.is_infinite for a in Add.make_args(n)): # (inf or nan)/x != 0 rv = S.false if rv is not None: return _sympify(rv) return Relational.__new__(cls, lhs, rhs, **options)
def dot(self, p2): """Return dot product of self with another Point.""" p2 = Point(p2) return Add(*[a * b for a, b in zip(self, p2)])
def _eval_rewrite_as_Heaviside(self, *args): from sympy import Heaviside return Add(*[j*Mul(*[Heaviside(i-j) for i in args if i!=j]) \ for j in args])
def add_terms(terms, prec, target_prec): """ Helper for evalf_add. Adds a list of (mpfval, accuracy) terms. Returns ------- - None, None if there are no non-zero terms; - terms[0] if there is only 1 term; - scaled_zero if the sum of the terms produces a zero by cancellation e.g. mpfs representing 1 and -1 would produce a scaled zero which need special handling since they are not actually zero and they are purposely malformed to ensure that they can't be used in anything but accuracy calculations; - a tuple that is scaled to target_prec that corresponds to the sum of the terms. The returned mpf tuple will be normalized to target_prec; the input prec is used to define the working precision. XXX explain why this is needed and why one can't just loop using mpf_add """ from sympy.core.core import C terms = [t for t in terms if not iszero(t)] if not terms: return None, None elif len(terms) == 1: return terms[0] # see if any argument is NaN or oo and thus warrants a special return special = [] for t in terms: arg = C.Float._new(t[0], 1) if arg is S.NaN or arg.is_unbounded: special.append(arg) if special: from sympy.core.add import Add rv = evalf(Add(*special), prec + 4, {}) return rv[0], rv[2] working_prec = 2*prec sum_man, sum_exp, absolute_error = 0, 0, MINUS_INF for x, accuracy in terms: sign, man, exp, bc = x if sign: man = -man absolute_error = max(absolute_error, bc + exp - accuracy) delta = exp - sum_exp if exp >= sum_exp: # x much larger than existing sum? # first: quick test if ((delta > working_prec) and ((not sum_man) or delta - bitcount(abs(sum_man)) > working_prec)): sum_man = man sum_exp = exp else: sum_man += (man << delta) else: delta = -delta # x much smaller than existing sum? if delta - bc > working_prec: if not sum_man: sum_man, sum_exp = man, exp else: sum_man = (sum_man << delta) + man sum_exp = exp if not sum_man: return scaled_zero(absolute_error) if sum_man < 0: sum_sign = 1 sum_man = -sum_man else: sum_sign = 0 sum_bc = bitcount(sum_man) sum_accuracy = sum_exp + sum_bc - absolute_error r = normalize(sum_sign, sum_man, sum_exp, sum_bc, target_prec, rnd), sum_accuracy #print "returning", to_str(r[0],50), r[1] return r
def eval(cls, arg): if arg.is_Number: if arg is S.NaN: return S.NaN elif arg is S.Zero: return S.One elif arg is S.One: return S.Exp1 elif arg is S.Infinity: return S.Infinity elif arg is S.NegativeInfinity: return S.Zero elif arg.func is log: return arg.args[0] elif arg.is_Mul: coeff = arg.as_coefficient(S.Pi * S.ImaginaryUnit) if coeff is not None: if (2 * coeff).is_integer: if coeff.is_even: return S.One elif coeff.is_odd: return S.NegativeOne elif (coeff + S.Half).is_even: return -S.ImaginaryUnit elif (coeff + S.Half).is_odd: return S.ImaginaryUnit I = S.ImaginaryUnit oo = S.Infinity a = Wild("a", exclude=[I, oo]) r = arg.match(I * a * oo) if r and r[a] != 0: return S.NaN args = Add.make_args(arg) included, excluded = [], [] for arg in args: coeff, terms = arg.as_coeff_mul() if coeff is S.Infinity: excluded.append(coeff**Mul(*terms)) else: coeffs, log_term = [coeff], None for term in terms: if term.func is log: if log_term is None: log_term = term.args[0] else: log_term = None break elif term.is_comparable: coeffs.append(term) else: log_term = None break if log_term is not None: excluded.append(log_term**Mul(*coeffs)) else: included.append(arg) if excluded: return Mul(*(excluded + [cls(Add(*included))]))
def rsolve_hyper(coeffs, f, n, **hints): r""" Given linear recurrence operator `\operatorname{L}` of order `k` with polynomial coefficients and inhomogeneous equation `\operatorname{L} y = f` we seek for all hypergeometric solutions over field `K` of characteristic zero. The inhomogeneous part can be either hypergeometric or a sum of a fixed number of pairwise dissimilar hypergeometric terms. The algorithm performs three basic steps: (1) Group together similar hypergeometric terms in the inhomogeneous part of `\operatorname{L} y = f`, and find particular solution using Abramov's algorithm. (2) Compute generating set of `\operatorname{L}` and find basis in it, so that all solutions are linearly independent. (3) Form final solution with the number of arbitrary constants equal to dimension of basis of `\operatorname{L}`. Term `a(n)` is hypergeometric if it is annihilated by first order linear difference equations with polynomial coefficients or, in simpler words, if consecutive term ratio is a rational function. The output of this procedure is a linear combination of fixed number of hypergeometric terms. However the underlying method can generate larger class of solutions - D'Alembertian terms. Note also that this method not only computes the kernel of the inhomogeneous equation, but also reduces in to a basis so that solutions generated by this procedure are linearly independent Examples ======== >>> from sympy.solvers import rsolve_hyper >>> from sympy.abc import x >>> rsolve_hyper([-1, -1, 1], 0, x) C0*(1/2 + sqrt(5)/2)**x + C1*(-sqrt(5)/2 + 1/2)**x >>> rsolve_hyper([-1, 1], 1 + x, x) C0 + x*(x + 1)/2 References ========== .. [1] M. Petkovsek, Hypergeometric solutions of linear recurrences with polynomial coefficients, J. Symbolic Computation, 14 (1992), 243-264. .. [2] M. Petkovsek, H. S. Wilf, D. Zeilberger, A = B, 1996. """ coeffs = list(map(sympify, coeffs)) f = sympify(f) r, kernel, symbols = len(coeffs) - 1, [], set() if not f.is_zero: if f.is_Add: similar = {} for g in f.expand().args: if not g.is_hypergeometric(n): return None for h in similar.keys(): if hypersimilar(g, h, n): similar[h] += g break else: similar[g] = S.Zero inhomogeneous = [] for g, h in similar.items(): inhomogeneous.append(g + h) elif f.is_hypergeometric(n): inhomogeneous = [f] else: return None for i, g in enumerate(inhomogeneous): coeff, polys = S.One, coeffs[:] denoms = [S.One]*(r + 1) s = hypersimp(g, n) for j in range(1, r + 1): coeff *= s.subs(n, n + j - 1) p, q = coeff.as_numer_denom() polys[j] *= p denoms[j] = q for j in range(r + 1): polys[j] *= Mul(*(denoms[:j] + denoms[j + 1:])) R = rsolve_poly(polys, Mul(*denoms), n) if not (R is None or R is S.Zero): inhomogeneous[i] *= R else: return None result = Add(*inhomogeneous) else: result = S.Zero Z = Dummy('Z') p, q = coeffs[0], coeffs[r].subs(n, n - r + 1) p_factors = [z for z in roots(p, n).keys()] q_factors = [z for z in roots(q, n).keys()] factors = [(S.One, S.One)] for p in p_factors: for q in q_factors: if p.is_integer and q.is_integer and p <= q: continue else: factors += [(n - p, n - q)] p = [(n - p, S.One) for p in p_factors] q = [(S.One, n - q) for q in q_factors] factors = p + factors + q for A, B in factors: polys, degrees = [], [] D = A*B.subs(n, n + r - 1) for i in range(r + 1): a = Mul(*[A.subs(n, n + j) for j in range(i)]) b = Mul(*[B.subs(n, n + j) for j in range(i, r)]) poly = quo(coeffs[i]*a*b, D, n) polys.append(poly.as_poly(n)) if not poly.is_zero: degrees.append(polys[i].degree()) if degrees: d, poly = max(degrees), S.Zero else: return None for i in range(r + 1): coeff = polys[i].nth(d) if coeff is not S.Zero: poly += coeff * Z**i for z in roots(poly, Z).keys(): if z.is_zero: continue (C, s) = rsolve_poly([polys[i]*z**i for i in range(r + 1)], 0, n, symbols=True) if C is not None and C is not S.Zero: symbols |= set(s) ratio = z * A * C.subs(n, n + 1) / B / C ratio = simplify(ratio) # If there is a nonnegative root in the denominator of the ratio, # this indicates that the term y(n_root) is zero, and one should # start the product with the term y(n_root + 1). n0 = 0 for n_root in roots(ratio.as_numer_denom()[1], n).keys(): if n_root.has(I): return None elif (n0 < (n_root + 1)) == True: n0 = n_root + 1 K = product(ratio, (n, n0, n - 1)) if K.has(factorial, FallingFactorial, RisingFactorial): K = simplify(K) if casoratian(kernel + [K], n, zero=False) != 0: kernel.append(K) kernel.sort(key=default_sort_key) sk = list(zip(numbered_symbols('C'), kernel)) if sk: for C, ker in sk: result += C * ker else: return None if hints.get('symbols', False): symbols |= {s for s, k in sk} return (result, list(symbols)) else: return result
def test_doit(): a = OperationsOnlyMatrix([[Add(x, x, evaluate=False)]]) assert a[0] != 2*x assert a.doit() == Matrix([[2*x]])
def eval(cls, arg): from sympy.assumptions import ask, Q from sympy.calculus import AccumBounds if arg.is_Number: if arg is S.NaN: return S.NaN elif arg is S.Zero: return S.One elif arg is S.One: return S.Exp1 elif arg is S.Infinity: return S.Infinity elif arg is S.NegativeInfinity: return S.Zero elif arg.func is log: return arg.args[0] elif isinstance(arg, AccumBounds): return AccumBounds(exp(arg.min), exp(arg.max)) elif arg.is_Mul: if arg.is_number or arg.is_Symbol: coeff = arg.coeff(S.Pi * S.ImaginaryUnit) if coeff: if ask(Q.integer(2 * coeff)): if ask(Q.even(coeff)): return S.One elif ask(Q.odd(coeff)): return S.NegativeOne elif ask(Q.even(coeff + S.Half)): return -S.ImaginaryUnit elif ask(Q.odd(coeff + S.Half)): return S.ImaginaryUnit # Warning: code in risch.py will be very sensitive to changes # in this (see DifferentialExtension). # look for a single log factor coeff, terms = arg.as_coeff_Mul() # but it can't be multiplied by oo if coeff in [S.NegativeInfinity, S.Infinity]: return None coeffs, log_term = [coeff], None for term in Mul.make_args(terms): if term.func is log: if log_term is None: log_term = term.args[0] else: return None elif term.is_comparable: coeffs.append(term) else: return None return log_term**Mul(*coeffs) if log_term else None elif arg.is_Add: out = [] add = [] for a in arg.args: if a is S.One: add.append(a) continue newa = cls(a) if newa.func is cls: add.append(a) else: out.append(newa) if out: return Mul(*out) * cls(Add(*add), evaluate=False) elif arg.is_Matrix: return arg.exp()
def eval(cls, arg): if arg.is_Number: if arg is S.NaN: return S.NaN elif arg is S.Zero: return S.One elif arg is S.One: return S.Exp1 elif arg is S.Infinity: return S.Infinity elif arg is S.NegativeInfinity: return S.Zero elif arg.func is log: return arg.args[0] elif arg.is_Mul: Ioo = S.ImaginaryUnit * S.Infinity if arg in [Ioo, -Ioo]: return S.NaN coeff = arg.coeff(S.Pi * S.ImaginaryUnit) if coeff: if (2 * coeff).is_integer: if coeff.is_even: return S.One elif coeff.is_odd: return S.NegativeOne elif (coeff + S.Half).is_even: return -S.ImaginaryUnit elif (coeff + S.Half).is_odd: return S.ImaginaryUnit # Warning: code in risch.py will be very sensitive to changes # in this (see DifferentialExtension). # look for a single log factor coeff, terms = arg.as_coeff_Mul() # but it can't be multiplied by oo if coeff in [S.NegativeInfinity, S.Infinity]: return None coeffs, log_term = [coeff], None for term in Mul.make_args(terms): if term.func is log: if log_term is None: log_term = term.args[0] else: return None elif term.is_comparable: coeffs.append(term) else: return None return log_term**Mul(*coeffs) if log_term else None elif arg.is_Add: out = [] add = [] for a in arg.args: if a is S.One: add.append(a) continue newa = cls(a) if newa.func is cls: add.append(a) else: out.append(newa) if out: return Mul(*out) * cls(Add(*add), evaluate=False) elif arg.is_Matrix: from sympy import Matrix return arg.exp()
def test_core_add(): x = Symbol("x") for c in (Add, Add(x, 4)): check(c)
def _eval_nseries(self, x, n, logx, cdir=0): # NOTE Please see the comment at the beginning of this file, labelled # IMPORTANT. from sympy.series.order import Order from sympy.simplify.simplify import logcombine if not logx: logx = log(x) if self.args[0] == x: return logx arg = self.args[0] k, l = Wild("k"), Wild("l") r = arg.match(k * x**l) if r is not None: k, l = r[k], r[l] if l != 0 and not l.has(x) and not k.has(x): r = log(k) + l * logx # XXX true regardless of assumptions? return r def coeff_exp(term, x): coeff, exp = S.One, S.Zero for factor in Mul.make_args(term): if factor.has(x): base, exp = factor.as_base_exp() if base != x: try: return term.leadterm(x) except ValueError: return term, S.Zero else: coeff *= factor return coeff, exp # TODO new and probably slow try: a, b = arg.leadterm(x) s = arg.nseries(x, n=n + b, logx=logx) except (ValueError, NotImplementedError, PoleError): s = arg.nseries(x, n=n, logx=logx) while s.is_Order: n += 1 s = arg.nseries(x, n=n, logx=logx) a, b = s.removeO().leadterm(x) p = cancel(s / (a * x**b) - 1).expand().powsimp() if p.has(exp): p = logcombine(p) if isinstance(p, Order): n = p.getn() _, d = coeff_exp(p, x) if not d.is_positive: return log(a) + b * logx + Order(x**n, x) def mul(d1, d2): res = {} for e1, e2 in product(d1, d2): ex = e1 + e2 if ex < n: res[ex] = res.get(ex, S.Zero) + d1[e1] * d2[e2] return res pterms = {} for term in Add.make_args(p): co1, e1 = coeff_exp(term, x) pterms[e1] = pterms.get(e1, S.Zero) + co1.removeO() k = S.One terms = {} pk = pterms while k * d < n: coeff = -S.NegativeOne**k / k for ex in pk: terms[ex] = terms.get(ex, S.Zero) + coeff * pk[ex] pk = mul(pk, pterms) k += S.One res = log(a) + b * logx for ex in terms: res += terms[ex] * x**(ex) if cdir != 0: cdir = self.args[0].dir(x, cdir) if a.is_real and a.is_negative and im(cdir) < 0: res -= 2 * I * S.Pi return res + Order(x**n, x)
def split_real_imag(expr): real_imag = lambda t: ( 'real' if is_extended_real(t, assumptions) else 'imag' if is_extended_real(I*t, assumptions) else None) return sift(Add.make_args(expr), real_imag)
def _eval_integral(self, f, x, meijerg=None, risch=None, manual=None, conds='piecewise'): """ Calculate the anti-derivative to the function f(x). The following algorithms are applied (roughly in this order): 1. Simple heuristics (based on pattern matching and integral table): - most frequently used functions (e.g. polynomials, products of trig functions) 2. Integration of rational functions: - A complete algorithm for integrating rational functions is implemented (the Lazard-Rioboo-Trager algorithm). The algorithm also uses the partial fraction decomposition algorithm implemented in apart() as a preprocessor to make this process faster. Note that the integral of a rational function is always elementary, but in general, it may include a RootSum. 3. Full Risch algorithm: - The Risch algorithm is a complete decision procedure for integrating elementary functions, which means that given any elementary function, it will either compute an elementary antiderivative, or else prove that none exists. Currently, part of transcendental case is implemented, meaning elementary integrals containing exponentials, logarithms, and (soon!) trigonometric functions can be computed. The algebraic case, e.g., functions containing roots, is much more difficult and is not implemented yet. - If the routine fails (because the integrand is not elementary, or because a case is not implemented yet), it continues on to the next algorithms below. If the routine proves that the integrals is nonelementary, it still moves on to the algorithms below, because we might be able to find a closed-form solution in terms of special functions. If risch=True, however, it will stop here. 4. The Meijer G-Function algorithm: - This algorithm works by first rewriting the integrand in terms of very general Meijer G-Function (meijerg in SymPy), integrating it, and then rewriting the result back, if possible. This algorithm is particularly powerful for definite integrals (which is actually part of a different method of Integral), since it can compute closed-form solutions of definite integrals even when no closed-form indefinite integral exists. But it also is capable of computing many indefinite integrals as well. - Another advantage of this method is that it can use some results about the Meijer G-Function to give a result in terms of a Piecewise expression, which allows to express conditionally convergent integrals. - Setting meijerg=True will cause integrate() to use only this method. 5. The "manual integration" algorithm: - This algorithm tries to mimic how a person would find an antiderivative by hand, for example by looking for a substitution or applying integration by parts. This algorithm does not handle as many integrands but can return results in a more familiar form. - Sometimes this algorithm can evaluate parts of an integral; in this case integrate() will try to evaluate the rest of the integrand using the other methods here. - Setting manual=True will cause integrate() to use only this method. 6. The Heuristic Risch algorithm: - This is a heuristic version of the Risch algorithm, meaning that it is not deterministic. This is tried as a last resort because it can be very slow. It is still used because not enough of the full Risch algorithm is implemented, so that there are still some integrals that can only be computed using this method. The goal is to implement enough of the Risch and Meijer G methods so that this can be deleted. """ from sympy.integrals.risch import risch_integrate if risch: try: return risch_integrate(f, x, conds=conds) except NotImplementedError: return None if manual: try: result = manualintegrate(f, x) if result is not None and result.func != Integral: return result except (ValueError, PolynomialError): pass # if it is a poly(x) then let the polynomial integrate itself (fast) # # It is important to make this check first, otherwise the other code # will return a sympy expression instead of a Polynomial. # # see Polynomial for details. if isinstance(f, Poly) and not meijerg: return f.integrate(x) # Piecewise antiderivatives need to call special integrate. if f.func is Piecewise: return f._eval_integral(x) # let's cut it short if `f` does not depend on `x` if not f.has(x): return f*x # try to convert to poly(x) and then integrate if successful (fast) poly = f.as_poly(x) if poly is not None and not meijerg: return poly.integrate().as_expr() if risch is not False: try: result, i = risch_integrate(f, x, separate_integral=True, conds=conds) except NotImplementedError: pass else: if i: # There was a nonelementary integral. Try integrating it. return result + i.doit(risch=False) else: return result # since Integral(f=g1+g2+...) == Integral(g1) + Integral(g2) + ... # we are going to handle Add terms separately, # if `f` is not Add -- we only have one term # Note that in general, this is a bad idea, because Integral(g1) + # Integral(g2) might not be computable, even if Integral(g1 + g2) is. # For example, Integral(x**x + x**x*log(x)). But many heuristics only # work term-wise. So we compute this step last, after trying # risch_integrate. We also try risch_integrate again in this loop, # because maybe the integral is a sum of an elementary part and a # nonelementary part (like erf(x) + exp(x)). risch_integrate() is # quite fast, so this is acceptable. parts = [] args = Add.make_args(f) for g in args: coeff, g = g.as_independent(x) # g(x) = const if g is S.One and not meijerg: parts.append(coeff*x) continue # g(x) = expr + O(x**n) order_term = g.getO() if order_term is not None: h = self._eval_integral(g.removeO(), x) if h is not None: h_order_expr = self._eval_integral(order_term.expr, x) if h_order_expr is not None: h_order_term = order_term.func( h_order_expr, *order_term.variables) parts.append(coeff*(h + h_order_term)) continue # NOTE: if there is O(x**n) and we fail to integrate then there is # no point in trying other methods because they will fail anyway. return None # c # g(x) = (a*x+b) if g.is_Pow and not g.exp.has(x) and not meijerg: a = Wild('a', exclude=[x]) b = Wild('b', exclude=[x]) M = g.base.match(a*x + b) if M is not None: if g.exp == -1: h = C.log(g.base) elif conds != 'piecewise': h = g.base**(g.exp + 1) / (g.exp + 1) else: h1 = C.log(g.base) h2 = g.base**(g.exp + 1) / (g.exp + 1) h = Piecewise((h1, Eq(g.exp, -1)), (h2, True)) parts.append(coeff * h / M[a]) continue # poly(x) # g(x) = ------- # poly(x) if g.is_rational_function(x) and not meijerg: parts.append(coeff * ratint(g, x)) continue if not meijerg: # g(x) = Mul(trig) h = trigintegrate(g, x, conds=conds) if h is not None: parts.append(coeff * h) continue # g(x) has at least a DiracDelta term h = deltaintegrate(g, x) if h is not None: parts.append(coeff * h) continue # Try risch again. if risch is not False: try: h, i = risch_integrate(g, x, separate_integral=True, conds=conds) except NotImplementedError: h = None else: if i: h = h + i.doit(risch=False) parts.append(coeff*h) continue # fall back to heurisch try: if conds == 'piecewise': h = heurisch_wrapper(g, x, hints=[]) else: h = heurisch(g, x, hints=[]) except PolynomialError: # XXX: this exception means there is a bug in the # implementation of heuristic Risch integration # algorithm. h = None else: h = None if meijerg is not False and h is None: # rewrite using G functions try: h = meijerint_indefinite(g, x) except NotImplementedError: from sympy.integrals.meijerint import _debug _debug('NotImplementedError from meijerint_definite') res = None if h is not None: parts.append(coeff * h) continue if h is None and manual is not False: try: result = manualintegrate(g, x) if result is not None and not isinstance(result, Integral): if result.has(Integral): # try to have other algorithms do the integrals # manualintegrate can't handle result = result.func(*[ arg.doit(manual=False) if arg.has(Integral) else arg for arg in result.args ]).expand(multinomial=False, log=False, power_exp=False, power_base=False) if not result.has(Integral): parts.append(coeff * result) continue except (ValueError, PolynomialError): # can't handle some SymPy expressions pass # if we failed maybe it was because we had # a product that could have been expanded, # so let's try an expansion of the whole # thing before giving up; we don't try this # out the outset because there are things # that cannot be solved unless they are # NOT expanded e.g., x**x*(1+log(x)). There # should probably be a checker somewhere in this # routine to look for such cases and try to do # collection on the expressions if they are already # in an expanded form if not h and len(args) == 1: f = f.expand(mul=True, deep=False) if f.is_Add: # Note: risch will be identical on the expanded # expression, but maybe it will be able to pick out parts, # like x*(exp(x) + erf(x)). return self._eval_integral(f, x, meijerg=meijerg, risch=risch, conds=conds) if h is not None: parts.append(coeff * h) else: return None return Add(*parts)
def _compute_fps(f, x, x0, dir, hyper, order, rational, full): """Recursive wrapper to compute fps. See :func:`compute_fps` for details. """ if x0 in [S.Infinity, -S.Infinity]: dir = S.One if x0 is S.Infinity else -S.One temp = f.subs(x, 1 / x) result = _compute_fps(temp, x, 0, dir, hyper, order, rational, full) if result is None: return None return (result[0], result[1].subs(x, 1 / x), result[2].subs(x, 1 / x)) elif x0 or dir == -S.One: if dir == -S.One: rep = -x + x0 rep2 = -x rep2b = x0 else: rep = x + x0 rep2 = x rep2b = -x0 temp = f.subs(x, rep) result = _compute_fps(temp, x, 0, S.One, hyper, order, rational, full) if result is None: return None return (result[0], result[1].subs(x, rep2 + rep2b), result[2].subs(x, rep2 + rep2b)) if f.is_polynomial(x): return None # Break instances of Add # this allows application of different # algorithms on different terms increasing the # range of admissible functions. if isinstance(f, Add): result = False ak = sequence(S.Zero, (0, oo)) ind, xk = S.Zero, None for t in Add.make_args(f): res = _compute_fps(t, x, 0, S.One, hyper, order, rational, full) if res: if not result: result = True xk = res[1] if res[0].start > ak.start: seq = ak s, f = ak.start, res[0].start else: seq = res[0] s, f = res[0].start, ak.start save = Add( *[z[0] * z[1] for z in zip(seq[0:(f - s)], xk[s:f])]) ak += res[0] ind += res[2] + save else: ind += t if result: return ak, xk, ind return None result = None # from here on it's x0=0 and dir=1 handling k = Dummy('k') if rational: result = rational_algorithm(f, x, k, order, full) if result is None and hyper: result = hyper_algorithm(f, x, k, order) if result is None: return None ak = sequence(result[0], (k, result[2], oo)) xk = sequence(x**k, (k, 0, oo)) ind = result[1] return ak, xk, ind
def _eval_integral(self, f, x, meijerg=None, risch=None, manual=None, conds='piecewise'): """ Calculate the anti-derivative to the function f(x). The following algorithms are applied (roughly in this order): 1. Simple heuristics (based on pattern matching and integral table): - most frequently used functions (e.g. polynomials, products of trig functions) 2. Integration of rational functions: - A complete algorithm for integrating rational functions is implemented (the Lazard-Rioboo-Trager algorithm). The algorithm also uses the partial fraction decomposition algorithm implemented in apart() as a preprocessor to make this process faster. Note that the integral of a rational function is always elementary, but in general, it may include a RootSum. 3. Full Risch algorithm: - The Risch algorithm is a complete decision procedure for integrating elementary functions, which means that given any elementary function, it will either compute an elementary antiderivative, or else prove that none exists. Currently, part of transcendental case is implemented, meaning elementary integrals containing exponentials, logarithms, and (soon!) trigonometric functions can be computed. The algebraic case, e.g., functions containing roots, is much more difficult and is not implemented yet. - If the routine fails (because the integrand is not elementary, or because a case is not implemented yet), it continues on to the next algorithms below. If the routine proves that the integrals is nonelementary, it still moves on to the algorithms below, because we might be able to find a closed-form solution in terms of special functions. If risch=True, however, it will stop here. 4. The Meijer G-Function algorithm: - This algorithm works by first rewriting the integrand in terms of very general Meijer G-Function (meijerg in SymPy), integrating it, and then rewriting the result back, if possible. This algorithm is particularly powerful for definite integrals (which is actually part of a different method of Integral), since it can compute closed-form solutions of definite integrals even when no closed-form indefinite integral exists. But it also is capable of computing many indefinite integrals as well. - Another advantage of this method is that it can use some results about the Meijer G-Function to give a result in terms of a Piecewise expression, which allows to express conditionally convergent integrals. - Setting meijerg=True will cause integrate() to use only this method. 5. The "manual integration" algorithm: - This algorithm tries to mimic how a person would find an antiderivative by hand, for example by looking for a substitution or applying integration by parts. This algorithm does not handle as many integrands but can return results in a more familiar form. - Sometimes this algorithm can evaluate parts of an integral; in this case integrate() will try to evaluate the rest of the integrand using the other methods here. - Setting manual=True will cause integrate() to use only this method. 6. The Heuristic Risch algorithm: - This is a heuristic version of the Risch algorithm, meaning that it is not deterministic. This is tried as a last resort because it can be very slow. It is still used because not enough of the full Risch algorithm is implemented, so that there are still some integrals that can only be computed using this method. The goal is to implement enough of the Risch and Meijer G methods so that this can be deleted. """ from sympy.integrals.risch import risch_integrate manual = True # force manual integration if risch: try: return risch_integrate(f, x, conds=conds) except NotImplementedError: return None if manual: try: result = manualintegrate(f, x) if result is not None and result.func != Integral: return result except (ValueError, PolynomialError): pass # if it is a poly(x) then let the polynomial integrate itself (fast) # # It is important to make this check first, otherwise the other code # will return a sympy expression instead of a Polynomial. # # see Polynomial for details. if isinstance(f, Poly) and not meijerg: return f.integrate(x) # Piecewise antiderivatives need to call special integrate. if f.func is Piecewise: return f._eval_integral(x) # let's cut it short if `f` does not depend on `x` if not f.has(x): return f*x # try to convert to poly(x) and then integrate if successful (fast) poly = f.as_poly(x) if poly is not None and not meijerg: add_comment("The function is a polinomial therefore the antiderivative is") ad = poly.integrate().as_expr() add_exp(ad) return ad if risch is not False: try: result, i = risch_integrate(f, x, separate_integral=True, conds=conds) except NotImplementedError: pass else: if i: # There was a nonelementary integral. Try integrating it. return result + i.doit(risch=False) else: return result # since Integral(f=g1+g2+...) == Integral(g1) + Integral(g2) + ... # we are going to handle Add terms separately, # if `f` is not Add -- we only have one term # Note that in general, this is a bad idea, because Integral(g1) + # Integral(g2) might not be computable, even if Integral(g1 + g2) is. # For example, Integral(x**x + x**x*log(x)). But many heuristics only # work term-wise. So we compute this step last, after trying # risch_integrate. We also try risch_integrate again in this loop, # because maybe the integral is a sum of an elementary part and a # nonelementary part (like erf(x) + exp(x)). risch_integrate() is # quite fast, so this is acceptable. parts = [] args = Add.make_args(f) for g in args: coeff, g = g.as_independent(x) # g(x) = const if g is S.One and not meijerg: parts.append(coeff*x) continue # g(x) = expr + O(x**n) order_term = g.getO() if order_term is not None: h = self._eval_integral(g.removeO(), x) if h is not None: h_order_expr = self._eval_integral(order_term.expr, x) if h_order_expr is not None: h_order_term = order_term.func( h_order_expr, *order_term.variables) parts.append(coeff*(h + h_order_term)) continue # NOTE: if there is O(x**n) and we fail to integrate then there is # no point in trying other methods because they will fail anyway. return None # c # g(x) = (a*x+b) if g.is_Pow and not g.exp.has(x) and not meijerg: a = Wild('a', exclude=[x]) b = Wild('b', exclude=[x]) M = g.base.match(a*x + b) if M is not None: if g.exp == -1: h = C.log(g.base) elif conds != 'piecewise': h = g.base**(g.exp + 1) / (g.exp + 1) else: h1 = C.log(g.base) h2 = g.base**(g.exp + 1) / (g.exp + 1) h = Piecewise((h1, Eq(g.exp, -1)), (h2, True)) parts.append(coeff * h / M[a]) continue # poly(x) # g(x) = ------- # poly(x) if g.is_rational_function(x) and not meijerg: parts.append(coeff * ratint(g, x)) continue if not meijerg: # g(x) = Mul(trig) h = trigintegrate(g, x, conds=conds) if h is not None: parts.append(coeff * h) continue # g(x) has at least a DiracDelta term h = deltaintegrate(g, x) if h is not None: parts.append(coeff * h) continue # Try risch again. if risch is not False: try: h, i = risch_integrate(g, x, separate_integral=True, conds=conds) except NotImplementedError: h = None else: if i: h = h + i.doit(risch=False) parts.append(coeff*h) continue # fall back to heurisch try: if conds == 'piecewise': h = heurisch_wrapper(g, x, hints=[]) else: h = heurisch(g, x, hints=[]) except PolynomialError: # XXX: this exception means there is a bug in the # implementation of heuristic Risch integration # algorithm. h = None else: h = None if meijerg is not False and h is None: # rewrite using G functions try: h = meijerint_indefinite(g, x) except NotImplementedError: from sympy.integrals.meijerint import _debug _debug('NotImplementedError from meijerint_definite') res = None if h is not None: parts.append(coeff * h) continue if h is None and manual is not False: try: result = manualintegrate(g, x) if result is not None and not isinstance(result, Integral): if result.has(Integral): # try to have other algorithms do the integrals # manualintegrate can't handle result = result.func(*[ arg.doit(manual=False) if arg.has(Integral) else arg for arg in result.args ]).expand(multinomial=False, log=False, power_exp=False, power_base=False) if not result.has(Integral): parts.append(coeff * result) continue except (ValueError, PolynomialError): # can't handle some SymPy expressions pass # if we failed maybe it was because we had # a product that could have been expanded, # so let's try an expansion of the whole # thing before giving up; we don't try this # out the outset because there are things # that cannot be solved unless they are # NOT expanded e.g., x**x*(1+log(x)). There # should probably be a checker somewhere in this # routine to look for such cases and try to do # collection on the expressions if they are already # in an expanded form if not h and len(args) == 1: f = f.expand(mul=True, deep=False) if f.is_Add: # Note: risch will be identical on the expanded # expression, but maybe it will be able to pick out parts, # like x*(exp(x) + erf(x)). return self._eval_integral(f, x, meijerg=meijerg, risch=risch, conds=conds) if h is not None: parts.append(coeff * h) else: return None return Add(*parts)
def rational_algorithm(f, x, k, order=4, full=False): """Rational algorithm for computing formula of coefficients of Formal Power Series of a function. Applicable when f(x) or some derivative of f(x) is a rational function in x. :func:`rational_algorithm` uses :func:`apart` function for partial fraction decomposition. :func:`apart` by default uses 'undetermined coefficients method'. By setting ``full=True``, 'Bronstein's algorithm' can be used instead. Looks for derivative of a function up to 4'th order (by default). This can be overridden using order option. Returns ======= formula : Expr ind : Expr Independent terms. order : int Examples ======== >>> from sympy import log, atan, I >>> from sympy.series.formal import rational_algorithm as ra >>> from sympy.abc import x, k >>> ra(1 / (1 - x), x, k) (1, 0, 0) >>> ra(log(1 + x), x, k) (-(-1)**(-k)/k, 0, 1) >>> ra(atan(x), x, k, full=True) ((-I*(-I)**(-k)/2 + I*I**(-k)/2)/k, 0, 1) Notes ===== By setting ``full=True``, range of admissible functions to be solved using ``rational_algorithm`` can be increased. This option should be used carefully as it can signifcantly slow down the computation as ``doit`` is performed on the :class:`RootSum` object returned by the ``apart`` function. Use ``full=False`` whenever possible. See Also ======== sympy.polys.partfrac.apart References ========== .. [1] Formal Power Series - Dominik Gruntz, Wolfram Koepf .. [2] Power Series in Computer Algebra - Wolfram Koepf """ from sympy.polys import RootSum, apart from sympy.integrals import integrate diff = f ds = [] # list of diff for i in range(order + 1): if i: diff = diff.diff(x) if diff.is_rational_function(x): coeff, sep = S.Zero, S.Zero terms = apart(diff, x, full=full) if terms.has(RootSum): terms = terms.doit() for t in Add.make_args(terms): num, den = t.as_numer_denom() if not den.has(x): sep += t else: if isinstance(den, Mul): # m*(n*x - a)**j -> (n*x - a)**j ind = den.as_independent(x) den = ind[1] num /= ind[0] # (n*x - a)**j -> (x - b) den, j = den.as_base_exp() a, xterm = den.as_coeff_add(x) # term -> m/x**n if not a: sep += t continue xc = xterm[0].coeff(x) a /= -xc num /= xc**j ak = ((-1)**j * num * binomial(j + k - 1, k).rewrite(factorial) / a**(j + k)) coeff += ak # Hacky, better way? if coeff is S.Zero: return None if (coeff.has(x) or coeff.has(zoo) or coeff.has(oo) or coeff.has(nan)): return None for j in range(i): coeff = (coeff / (k + j + 1)) sep = integrate(sep, x) sep += (ds.pop() - sep).limit(x, 0) # constant of integration return (coeff.subs(k, k - i), sep, i) else: ds.append(diff) return None
def limit_seq(expr, n=None, trials=5): """Finds the limit of a sequence as index n tends to infinity. Parameters ========== expr : Expr SymPy expression for the n-th term of the sequence n : Symbol, optional The index of the sequence, an integer that tends to positive infinity. If None, inferred from the expression unless it has multiple symbols. trials: int, optional The algorithm is highly recursive. ``trials`` is a safeguard from infinite recursion in case the limit is not easily computed by the algorithm. Try increasing ``trials`` if the algorithm returns ``None``. Admissible Terms ================ The algorithm is designed for sequences built from rational functions, indefinite sums, and indefinite products over an indeterminate n. Terms of alternating sign are also allowed, but more complex oscillatory behavior is not supported. Examples ======== >>> from sympy import limit_seq, Sum, binomial >>> from sympy.abc import n, k, m >>> limit_seq((5*n**3 + 3*n**2 + 4) / (3*n**3 + 4*n - 5), n) 5/3 >>> limit_seq(binomial(2*n, n) / Sum(binomial(2*k, k), (k, 1, n)), n) 3/4 >>> limit_seq(Sum(k**2 * Sum(2**m/m, (m, 1, k)), (k, 1, n)) / (2**n*n), n) 4 See Also ======== sympy.series.limitseq.dominant References ========== .. [1] Computing Limits of Sequences - Manuel Kauers """ from sympy.concrete.summations import Sum from sympy.calculus.util import AccumulationBounds if n is None: free = expr.free_symbols if len(free) == 1: n = free.pop() elif not free: return expr else: raise ValueError("Expression has more than one variable. " "Please specify a variable.") elif n not in expr.free_symbols: return expr expr = expr.rewrite(fibonacci, S.GoldenRatio) n_ = Dummy("n", integer=True, positive=True) n1 = Dummy("n", odd=True, positive=True) n2 = Dummy("n", even=True, positive=True) # If there is a negative term raised to a power involving n, or a # trigonometric function, then consider even and odd n separately. powers = (p.as_base_exp() for p in expr.atoms(Pow)) if (any(b.is_negative and e.has(n) for b, e in powers) or expr.has(cos, sin)): L1 = _limit_seq(expr.xreplace({n: n1}), n1, trials) if L1 is not None: L2 = _limit_seq(expr.xreplace({n: n2}), n2, trials) if L1 != L2: if L1.is_comparable and L2.is_comparable: return AccumulationBounds(Min(L1, L2), Max(L1, L2)) else: return None else: L1 = _limit_seq(expr.xreplace({n: n_}), n_, trials) if L1 is not None: return L1 else: if expr.is_Add: limits = [limit_seq(term, n, trials) for term in expr.args] if any(result is None for result in limits): return None else: return Add(*limits) # Maybe the absolute value is easier to deal with (though not if # it has a Sum). If it tends to 0, the limit is 0. elif not expr.has(Sum): lim = _limit_seq(Abs(expr.xreplace({n: n_})), n_, trials) if lim is not None and lim.is_zero: return S.Zero
def _integrate(field=None): irreducibles = set() for poly in reducibles: for z in poly.atoms(Symbol): if z in V: break else: continue irreducibles |= set(root_factors(poly, z, filter=field)) log_coeffs, log_part = [], [] B = _symbols('B', len(irreducibles)) for i, poly in enumerate(irreducibles): if poly.has(*V): log_coeffs.append(B[i]) log_part.append(log_coeffs[-1] * log(poly)) coeffs = poly_coeffs + log_coeffs # TODO: Currently it's better to use symbolic expressions here instead # of rational functions, because it's simpler and FracElement doesn't # give big speed improvement yet. This is because cancelation is slow # due to slow polynomial GCD algorithms. If this gets improved then # revise this code. candidate = poly_part / poly_denom + Add(*log_part) h = F - _derivation(candidate) / denom raw_numer = h.as_numer_denom()[0] # Rewrite raw_numer as a polynomial in K[coeffs][V] where K is a field # that we have to determine. We can't use simply atoms() because log(3), # sqrt(y) and similar expressions can appear, leading to non-trivial # domains. syms = set(coeffs) | set(V) non_syms = set([]) def find_non_syms(expr): if expr.is_Integer or expr.is_Rational: pass # ignore trivial numbers elif expr in syms: pass # ignore variables elif not expr.has(*syms): non_syms.add(expr) elif expr.is_Add or expr.is_Mul or expr.is_Pow: list(map(find_non_syms, expr.args)) else: # TODO: Non-polynomial expression. This should have been # filtered out at an earlier stage. raise PolynomialError try: find_non_syms(raw_numer) except PolynomialError: return None else: ground, _ = construct_domain(non_syms, field=True) coeff_ring = PolyRing(coeffs, ground) ring = PolyRing(V, coeff_ring) numer = ring.from_expr(raw_numer) solution = solve_lin_sys(numer.coeffs(), coeff_ring) if solution is None: return None else: solution = [(k.as_expr(), v.as_expr()) for k, v in solution.items()] return candidate.subs(solution).subs( list(zip(coeffs, [S.Zero] * len(coeffs))))
def rational_algorithm(f, x, k, order=4, full=False): """Rational algorithm for computing formula of coefficients of Formal Power Series of a function. Applicable when f(x) or some derivative of f(x) is a rational function in x. :func:`rational_algorithm` uses :func:`apart` function for partial fraction decomposition. :func:`apart` by default uses 'undetermined coefficients method'. By setting ``full=True``, 'Bronstein's algorithm' can be used instead. Looks for derivative of a function up to 4'th order (by default). This can be overriden using order option. Returns ======= formula : Expr ind : Expr Independent terms. order : int Examples ======== >>> from sympy import log, atan, I >>> from sympy.series.formal import rational_algorithm as ra >>> from sympy.abc import x, k >>> ra(1 / (1 - x), x, k) (1, 0, 0) >>> ra(log(1 + x), x, k) (-(-1)**(-k)/k, 0, 1) >>> ra(atan(x), x, k, full=True) ((-I*(-I)**(-k)/2 + I*I**(-k)/2)/k, 0, 1) Notes ===== By setting ``full=True``, range of admissible functions to be solved using ``rational_algorithm`` can be increased. This option should be used carefully as it can signifcantly slow down the computation as ``doit`` is performed on the :class:`RootSum` object returned by the ``apart`` function. Use ``full=False`` whenever possible. See Also ======== sympy.polys.partfrac.apart References ========== .. [1] Formal Power Series - Dominik Gruntz, Wolfram Koepf .. [2] Power Series in Computer Algebra - Wolfram Koepf """ from sympy.polys import RootSum, apart from sympy.integrals import integrate diff = f ds = [] # list of diff for i in range(order + 1): if i: diff = diff.diff(x) if diff.is_rational_function(x): coeff, sep = S.Zero, S.Zero terms = apart(diff, x, full=full) if terms.has(RootSum): terms = terms.doit() for t in Add.make_args(terms): num, den = t.as_numer_denom() if not den.has(x): sep += t else: if isinstance(den, Mul): # m*(n*x - a)**j -> (n*x - a)**j ind = den.as_independent(x) den = ind[1] num /= ind[0] # (n*x - a)**j -> (x - b) den, j = den.as_base_exp() a, xterm = den.as_coeff_add(x) # term -> m/x**n if not a: sep += t continue xc = xterm[0].coeff(x) a /= -xc num /= xc**j ak = ((-1)**j * num * binomial(j + k - 1, k).rewrite(factorial) / a**(j + k)) coeff += ak # Hacky, better way? if coeff is S.Zero: return None if (coeff.has(x) or coeff.has(zoo) or coeff.has(oo) or coeff.has(nan)): return None for j in range(i): coeff = (coeff / (k + j + 1)) sep = integrate(sep, x) sep += (ds.pop() - sep).limit(x, 0) # constant of integration return (coeff.subs(k, k - i), sep, i) else: ds.append(diff) return None
from sympy.core.add import Add from sympy.core.relational import Eq from sympy.core.symbol import Symbol from sympy.printing.latex import latex t = {} # type: tDict[tTuple[Type, ...], List[Any]] _create_lookup_table(t) doc = "" for about, category in sorted(t.items()): if about == (): doc += 'Elementary functions:\n\n' else: doc += 'Functions involving ' + ', '.join( '`%s`' % latex(list(category[0][0].atoms(func))[0]) for func in about) + ':\n\n' for formula, gs, cond, hint in category: if not isinstance(gs, list): g = Symbol('\\text{generated}') else: g = Add(*[fac * f for (fac, f) in gs]) obj = Eq(formula, g) if cond is True: cond = "" else: cond = ',\\text{ if } %s' % latex(cond) doc += ".. math::\n %s%s\n\n" % (latex(obj), cond) __doc__ = doc
def _makeDE(k): eq = f.diff(x, k) + Add(*[a[i] * f.diff(x, i) for i in range(0, k)]) DE = g(x).diff(x, k) + Add(*[a[i] * g(x).diff(x, i) for i in range(0, k)]) return eq, DE
def eval_sum_direct(expr, limits): from sympy.core import Add (i, a, b) = limits dif = b - a return Add(*[expr.subs(i, a + j) for j in range(dif + 1)])
def _derivation(h): return Add(*[d * h.diff(v) for d, v in zip(numers, V)])
def bivariate_type(f, x, y, *, first=True): """Given an expression, f, 3 tests will be done to see what type of composite bivariate it might be, options for u(x, y) are:: x*y x+y x*y+x x*y+y If it matches one of these types, ``u(x, y)``, ``P(u)`` and dummy variable ``u`` will be returned. Solving ``P(u)`` for ``u`` and equating the solutions to ``u(x, y)`` and then solving for ``x`` or ``y`` is equivalent to solving the original expression for ``x`` or ``y``. If ``x`` and ``y`` represent two functions in the same variable, e.g. ``x = g(t)`` and ``y = h(t)``, then if ``u(x, y) - p`` can be solved for ``t`` then these represent the solutions to ``P(u) = 0`` when ``p`` are the solutions of ``P(u) = 0``. Only positive values of ``u`` are considered. Examples ======== >>> from sympy.solvers.solvers import solve >>> from sympy.solvers.bivariate import bivariate_type >>> from sympy.abc import x, y >>> eq = (x**2 - 3).subs(x, x + y) >>> bivariate_type(eq, x, y) (x + y, _u**2 - 3, _u) >>> uxy, pu, u = _ >>> usol = solve(pu, u); usol [sqrt(3)] >>> [solve(uxy - s) for s in solve(pu, u)] [[{x: -y + sqrt(3)}]] >>> all(eq.subs(s).equals(0) for sol in _ for s in sol) True """ u = Dummy('u', positive=True) if first: p = Poly(f, x, y) f = p.as_expr() _x = Dummy() _y = Dummy() rv = bivariate_type(Poly(f.subs({ x: _x, y: _y }), _x, _y), _x, _y, first=False) if rv: reps = {_x: x, _y: y} return rv[0].xreplace(reps), rv[1].xreplace(reps), rv[2] return p = f f = p.as_expr() # f(x*y) args = Add.make_args(p.as_expr()) new = [] for a in args: a = _mexpand(a.subs(x, u / y)) free = a.free_symbols if x in free or y in free: break new.append(a) else: return x * y, Add(*new), u def ok(f, v, c): new = _mexpand(f.subs(v, c)) free = new.free_symbols return None if (x in free or y in free) else new # f(a*x + b*y) new = [] d = p.degree(x) if p.degree(y) == d: a = root(p.coeff_monomial(x**d), d) b = root(p.coeff_monomial(y**d), d) new = ok(f, x, (u - b * y) / a) if new is not None: return a * x + b * y, new, u # f(a*x*y + b*y) new = [] d = p.degree(x) if p.degree(y) == d: for itry in range(2): a = root(p.coeff_monomial(x**d * y**d), d) b = root(p.coeff_monomial(y**d), d) new = ok(f, x, (u - b * y) / a / y) if new is not None: return a * x * y + b * y, new, u x, y = y, x
def heurisch(f, x, rewrite=False, hints=None, mappings=None, retries=3, degree_offset=0, unnecessary_permutations=None): """ Compute indefinite integral using heuristic Risch algorithm. This is a heuristic approach to indefinite integration in finite terms using the extended heuristic (parallel) Risch algorithm, based on Manuel Bronstein's "Poor Man's Integrator". The algorithm supports various classes of functions including transcendental elementary or special functions like Airy, Bessel, Whittaker and Lambert. Note that this algorithm is not a decision procedure. If it isn't able to compute the antiderivative for a given function, then this is not a proof that such a functions does not exist. One should use recursive Risch algorithm in such case. It's an open question if this algorithm can be made a full decision procedure. This is an internal integrator procedure. You should use toplevel 'integrate' function in most cases, as this procedure needs some preprocessing steps and otherwise may fail. Specification ============= heurisch(f, x, rewrite=False, hints=None) where f : expression x : symbol rewrite -> force rewrite 'f' in terms of 'tan' and 'tanh' hints -> a list of functions that may appear in anti-derivate - hints = None --> no suggestions at all - hints = [ ] --> try to figure out - hints = [f1, ..., fn] --> we know better Examples ======== >>> from sympy import tan >>> from sympy.integrals.heurisch import heurisch >>> from sympy.abc import x, y >>> heurisch(y*tan(x), x) y*log(tan(x)**2 + 1)/2 See Manuel Bronstein's "Poor Man's Integrator": [1] http://www-sop.inria.fr/cafe/Manuel.Bronstein/pmint/index.html For more information on the implemented algorithm refer to: [2] K. Geddes, L. Stefanus, On the Risch-Norman Integration Method and its Implementation in Maple, Proceedings of ISSAC'89, ACM Press, 212-217. [3] J. H. Davenport, On the Parallel Risch Algorithm (I), Proceedings of EUROCAM'82, LNCS 144, Springer, 144-157. [4] J. H. Davenport, On the Parallel Risch Algorithm (III): Use of Tangents, SIGSAM Bulletin 16 (1982), 3-6. [5] J. H. Davenport, B. M. Trager, On the Parallel Risch Algorithm (II), ACM Transactions on Mathematical Software 11 (1985), 356-362. See Also ======== sympy.integrals.integrals.Integral.doit sympy.integrals.integrals.Integral components """ f = sympify(f) if x not in f.free_symbols: return f * x if not f.is_Add: indep, f = f.as_independent(x) else: indep = S.One rewritables = { (sin, cos, cot): tan, (sinh, cosh, coth): tanh, } if rewrite: for candidates, rule in rewritables.items(): f = f.rewrite(candidates, rule) else: for candidates in rewritables.keys(): if f.has(*candidates): break else: rewrite = True terms = components(f, x) if hints is not None: if not hints: a = Wild('a', exclude=[x]) b = Wild('b', exclude=[x]) c = Wild('c', exclude=[x]) for g in set(terms): if g.is_Function: if g.func is li: M = g.args[0].match(a * x**b) if M is not None: terms.add( x * (li(M[a] * x**M[b]) - (M[a] * x**M[b])**(-1 / M[b]) * Ei( (M[b] + 1) * log(M[a] * x**M[b]) / M[b]))) #terms.add( x*(li(M[a]*x**M[b]) - (x**M[b])**(-1/M[b])*Ei((M[b]+1)*log(M[a]*x**M[b])/M[b])) ) #terms.add( x*(li(M[a]*x**M[b]) - x*Ei((M[b]+1)*log(M[a]*x**M[b])/M[b])) ) #terms.add( li(M[a]*x**M[b]) - Ei((M[b]+1)*log(M[a]*x**M[b])/M[b]) ) elif g.func is exp: M = g.args[0].match(a * x**2) if M is not None: if M[a].is_positive: terms.add(erfi(sqrt(M[a]) * x)) else: # M[a].is_negative or unknown terms.add(erf(sqrt(-M[a]) * x)) M = g.args[0].match(a * x**2 + b * x + c) if M is not None: if M[a].is_positive: terms.add( sqrt(pi / 4 * (-M[a])) * exp(M[c] - M[b]**2 / (4 * M[a])) * erfi( sqrt(M[a]) * x + M[b] / (2 * sqrt(M[a])))) elif M[a].is_negative: terms.add( sqrt(pi / 4 * (-M[a])) * exp(M[c] - M[b]**2 / (4 * M[a])) * erf( sqrt(-M[a]) * x - M[b] / (2 * sqrt(-M[a])))) M = g.args[0].match(a * log(x)**2) if M is not None: if M[a].is_positive: terms.add( erfi( sqrt(M[a]) * log(x) + 1 / (2 * sqrt(M[a])))) if M[a].is_negative: terms.add( erf( sqrt(-M[a]) * log(x) - 1 / (2 * sqrt(-M[a])))) elif g.is_Pow: if g.exp.is_Rational and g.exp.q == 2: M = g.base.match(a * x**2 + b) if M is not None and M[b].is_positive: if M[a].is_positive: terms.add(asinh(sqrt(M[a] / M[b]) * x)) elif M[a].is_negative: terms.add(asin(sqrt(-M[a] / M[b]) * x)) M = g.base.match(a * x**2 - b) if M is not None and M[b].is_positive: if M[a].is_positive: terms.add(acosh(sqrt(M[a] / M[b]) * x)) elif M[a].is_negative: terms.add((-M[b] / 2 * sqrt(-M[a]) * atan( sqrt(-M[a]) * x / sqrt(M[a] * x**2 - M[b])) )) else: terms |= set(hints) for g in set(terms): terms |= components(cancel(g.diff(x)), x) # TODO: caching is significant factor for why permutations work at all. Change this. V = _symbols('x', len(terms)) mapping = dict(list(zip(terms, V))) rev_mapping = {} if unnecessary_permutations is None: unnecessary_permutations = [] for k, v in mapping.items(): rev_mapping[v] = k if mappings is None: # Pre-sort mapping in order of largest to smallest expressions (last is always x). def _sort_key(arg): return default_sort_key(arg[0].as_independent(x)[1]) #optimizing the number of permutations of mappping unnecessary_permutations = [(x, mapping[x])] del mapping[x] mapping = sorted(list(mapping.items()), key=_sort_key, reverse=True) mappings = permutations(mapping) def _substitute(expr): return expr.subs(mapping) for mapping in mappings: mapping = list(mapping) mapping = mapping + unnecessary_permutations diffs = [_substitute(cancel(g.diff(x))) for g in terms] denoms = [g.as_numer_denom()[1] for g in diffs] if all(h.is_polynomial(*V) for h in denoms) and _substitute(f).is_rational_function(*V): denom = reduce(lambda p, q: lcm(p, q, *V), denoms) break else: if not rewrite: result = heurisch( f, x, rewrite=True, hints=hints, unnecessary_permutations=unnecessary_permutations) if result is not None: return indep * result return None numers = [cancel(denom * g) for g in diffs] def _derivation(h): return Add(*[d * h.diff(v) for d, v in zip(numers, V)]) def _deflation(p): for y in V: if not p.has(y): continue if _derivation(p) is not S.Zero: c, q = p.as_poly(y).primitive() return _deflation(c) * gcd(q, q.diff(y)).as_expr() else: return p def _splitter(p): for y in V: if not p.has(y): continue if _derivation(y) is not S.Zero: c, q = p.as_poly(y).primitive() q = q.as_expr() h = gcd(q, _derivation(q), y) s = quo(h, gcd(q, q.diff(y), y), y) c_split = _splitter(c) if s.as_poly(y).degree() == 0: return (c_split[0], q * c_split[1]) q_split = _splitter(cancel(q / s)) return (c_split[0] * q_split[0] * s, c_split[1] * q_split[1]) else: return (S.One, p) special = {} for term in terms: if term.is_Function: if term.func is tan: special[1 + _substitute(term)**2] = False elif term.func is tanh: special[1 + _substitute(term)] = False special[1 - _substitute(term)] = False elif term.func is C.LambertW: special[_substitute(term)] = True F = _substitute(f) P, Q = F.as_numer_denom() u_split = _splitter(denom) v_split = _splitter(Q) polys = list(v_split) + [u_split[0]] + list(special.keys()) s = u_split[0] * Mul(*[k for k, v in special.items() if v]) polified = [p.as_poly(*V) for p in [s, P, Q]] if None in polified: return None a, b, c = [p.total_degree() for p in polified] poly_denom = (s * v_split[0] * _deflation(v_split[1])).as_expr() def _exponent(g): if g.is_Pow: if g.exp.is_Rational and g.exp.q != 1: if g.exp.p > 0: return g.exp.p + g.exp.q - 1 else: return abs(g.exp.p + g.exp.q) else: return 1 elif not g.is_Atom and g.args: return max([_exponent(h) for h in g.args]) else: return 1 A, B = _exponent(f), a + max(b, c) if A > 1 and B > 1: monoms = itermonomials(V, A + B - 1 + degree_offset) else: monoms = itermonomials(V, A + B + degree_offset) poly_coeffs = _symbols('A', len(monoms)) poly_part = Add( *[poly_coeffs[i] * monomial for i, monomial in enumerate(monoms)]) reducibles = set() for poly in polys: if poly.has(*V): try: factorization = factor(poly, greedy=True) except PolynomialError: factorization = poly factorization = poly if factorization.is_Mul: reducibles |= set(factorization.args) else: reducibles.add(factorization) def _integrate(field=None): irreducibles = set() for poly in reducibles: for z in poly.atoms(Symbol): if z in V: break else: continue irreducibles |= set(root_factors(poly, z, filter=field)) log_coeffs, log_part = [], [] B = _symbols('B', len(irreducibles)) for i, poly in enumerate(irreducibles): if poly.has(*V): log_coeffs.append(B[i]) log_part.append(log_coeffs[-1] * log(poly)) coeffs = poly_coeffs + log_coeffs # TODO: Currently it's better to use symbolic expressions here instead # of rational functions, because it's simpler and FracElement doesn't # give big speed improvement yet. This is because cancelation is slow # due to slow polynomial GCD algorithms. If this gets improved then # revise this code. candidate = poly_part / poly_denom + Add(*log_part) h = F - _derivation(candidate) / denom raw_numer = h.as_numer_denom()[0] # Rewrite raw_numer as a polynomial in K[coeffs][V] where K is a field # that we have to determine. We can't use simply atoms() because log(3), # sqrt(y) and similar expressions can appear, leading to non-trivial # domains. syms = set(coeffs) | set(V) non_syms = set([]) def find_non_syms(expr): if expr.is_Integer or expr.is_Rational: pass # ignore trivial numbers elif expr in syms: pass # ignore variables elif not expr.has(*syms): non_syms.add(expr) elif expr.is_Add or expr.is_Mul or expr.is_Pow: list(map(find_non_syms, expr.args)) else: # TODO: Non-polynomial expression. This should have been # filtered out at an earlier stage. raise PolynomialError try: find_non_syms(raw_numer) except PolynomialError: return None else: ground, _ = construct_domain(non_syms, field=True) coeff_ring = PolyRing(coeffs, ground) ring = PolyRing(V, coeff_ring) numer = ring.from_expr(raw_numer) solution = solve_lin_sys(numer.coeffs(), coeff_ring) if solution is None: return None else: solution = [(k.as_expr(), v.as_expr()) for k, v in solution.items()] return candidate.subs(solution).subs( list(zip(coeffs, [S.Zero] * len(coeffs)))) if not (F.atoms(Symbol) - set(V)): solution = _integrate('Q') if solution is None: solution = _integrate() else: solution = _integrate() if solution is not None: antideriv = solution.subs(rev_mapping) antideriv = cancel(antideriv).expand(force=True) if antideriv.is_Add: antideriv = antideriv.as_independent(x)[1] return indep * antideriv else: if retries >= 0: result = heurisch( f, x, mappings=mappings, rewrite=rewrite, hints=hints, retries=retries - 1, unnecessary_permutations=unnecessary_permutations) if result is not None: return indep * result return None
def dot(self, p): """Return dot product of self with another Point.""" if not is_sequence(p): p = Point(p) # raise the error via Point return Add(*(a * b for a, b in zip(self, p)))
def rsolve(f, y, init=None): r""" Solve univariate recurrence with rational coefficients. Given `k`-th order linear recurrence `\operatorname{L} y = f`, or equivalently: .. math:: a_{k}(n) y(n+k) + a_{k-1}(n) y(n+k-1) + \cdots + a_{0}(n) y(n) = f(n) where `a_{i}(n)`, for `i=0, \ldots, k`, are polynomials or rational functions in `n`, and `f` is a hypergeometric function or a sum of a fixed number of pairwise dissimilar hypergeometric terms in `n`, finds all solutions or returns ``None``, if none were found. Initial conditions can be given as a dictionary in two forms: (1) ``{ n_0 : v_0, n_1 : v_1, ..., n_m : v_m}`` (2) ``{y(n_0) : v_0, y(n_1) : v_1, ..., y(n_m) : v_m}`` or as a list ``L`` of values: ``L = [v_0, v_1, ..., v_m]`` where ``L[i] = v_i``, for `i=0, \ldots, m`, maps to `y(n_i)`. Examples ======== Lets consider the following recurrence: .. math:: (n - 1) y(n + 2) - (n^2 + 3 n - 2) y(n + 1) + 2 n (n + 1) y(n) = 0 >>> from sympy import Function, rsolve >>> from sympy.abc import n >>> y = Function('y') >>> f = (n - 1)*y(n + 2) - (n**2 + 3*n - 2)*y(n + 1) + 2*n*(n + 1)*y(n) >>> rsolve(f, y(n)) 2**n*C0 + C1*factorial(n) >>> rsolve(f, y(n), {y(0):0, y(1):3}) 3*2**n - 3*factorial(n) See Also ======== rsolve_poly, rsolve_ratio, rsolve_hyper """ if isinstance(f, Equality): f = f.lhs - f.rhs n = y.args[0] k = Wild('k', exclude=(n,)) # Preprocess user input to allow things like # y(n) + a*(y(n + 1) + y(n - 1))/2 f = f.expand().collect(y.func(Wild('m', integer=True))) h_part = defaultdict(lambda: S.Zero) i_part = S.Zero for g in Add.make_args(f): coeff = S.One kspec = None for h in Mul.make_args(g): if h.is_Function: if h.func == y.func: result = h.args[0].match(n + k) if result is not None: kspec = int(result[k]) else: raise ValueError( "'%s(%s + k)' expected, got '%s'" % (y.func, n, h)) else: raise ValueError( "'%s' expected, got '%s'" % (y.func, h.func)) else: coeff *= h if kspec is not None: h_part[kspec] += coeff else: i_part += coeff for k, coeff in h_part.items(): h_part[k] = simplify(coeff) common = S.One for coeff in h_part.values(): if coeff.is_rational_function(n): if not coeff.is_polynomial(n): common = lcm(common, coeff.as_numer_denom()[1], n) else: raise ValueError( "Polynomial or rational function expected, got '%s'" % coeff) i_numer, i_denom = i_part.as_numer_denom() if i_denom.is_polynomial(n): common = lcm(common, i_denom, n) if common is not S.One: for k, coeff in h_part.items(): numer, denom = coeff.as_numer_denom() h_part[k] = numer*quo(common, denom, n) i_part = i_numer*quo(common, i_denom, n) K_min = min(h_part.keys()) if K_min < 0: K = abs(K_min) H_part = defaultdict(lambda: S.Zero) i_part = i_part.subs(n, n + K).expand() common = common.subs(n, n + K).expand() for k, coeff in h_part.items(): H_part[k + K] = coeff.subs(n, n + K).expand() else: H_part = h_part K_max = max(H_part.keys()) coeffs = [H_part[i] for i in range(K_max + 1)] result = rsolve_hyper(coeffs, -i_part, n, symbols=True) if result is None: return None solution, symbols = result if init == {} or init == []: init = None if symbols and init is not None: if isinstance(init, list): init = {i: init[i] for i in range(len(init))} equations = [] for k, v in init.items(): try: i = int(k) except TypeError: if k.is_Function and k.func == y.func: i = int(k.args[0]) else: raise ValueError("Integer or term expected, got '%s'" % k) try: eq = solution.limit(n, i) - v except NotImplementedError: eq = solution.subs(n, i) - v equations.append(eq) result = solve(equations, *symbols) if not result: return None else: solution = solution.subs(result) return solution
def __new__(cls, lhs, rhs=None, **options): from sympy.core.add import Add from sympy.core.logic import fuzzy_bool from sympy.core.expr import _n2 from sympy.simplify.simplify import clear_coefficients if rhs is None: SymPyDeprecationWarning( feature="Eq(expr) with rhs default to 0", useinstead="Eq(expr, 0)", issue=16587, deprecated_since_version="1.5" ).warn() rhs = 0 lhs = _sympify(lhs) rhs = _sympify(rhs) evaluate = options.pop('evaluate', global_evaluate[0]) if evaluate: # If one expression has an _eval_Eq, return its results. if hasattr(lhs, '_eval_Eq'): r = lhs._eval_Eq(rhs) if r is not None: return r if hasattr(rhs, '_eval_Eq'): r = rhs._eval_Eq(lhs) if r is not None: return r # If expressions have the same structure, they must be equal. if lhs == rhs: return S.true # e.g. True == True elif all(isinstance(i, BooleanAtom) for i in (rhs, lhs)): return S.false # True != False elif not (lhs.is_Symbol or rhs.is_Symbol) and ( isinstance(lhs, Boolean) != isinstance(rhs, Boolean)): return S.false # only Booleans can equal Booleans # check finiteness fin = L, R = [i.is_finite for i in (lhs, rhs)] if None not in fin: if L != R: return S.false if L is False: if lhs == -rhs: # Eq(oo, -oo) return S.false return S.true elif None in fin and False in fin: return Relational.__new__(cls, lhs, rhs, **options) if all(isinstance(i, Expr) for i in (lhs, rhs)): # see if the difference evaluates dif = lhs - rhs z = dif.is_zero if z is not None: if z is False and dif.is_commutative: # issue 10728 return S.false if z: return S.true # evaluate numerically if possible n2 = _n2(lhs, rhs) if n2 is not None: return _sympify(n2 == 0) # see if the ratio evaluates n, d = dif.as_numer_denom() rv = None if n.is_zero: rv = d.is_nonzero elif n.is_finite: if d.is_infinite: rv = S.true elif n.is_zero is False: rv = d.is_infinite if rv is None: # if the condition that makes the denominator # infinite does not make the original expression # True then False can be returned l, r = clear_coefficients(d, S.Infinity) args = [_.subs(l, r) for _ in (lhs, rhs)] if args != [lhs, rhs]: rv = fuzzy_bool(Eq(*args)) if rv is True: rv = None elif any(a.is_infinite for a in Add.make_args(n)): # (inf or nan)/x != 0 rv = S.false if rv is not None: return _sympify(rv) return Relational.__new__(cls, lhs, rhs, **options)
def _compute_fps(f, x, x0, dir, hyper, order, rational, full): """Recursive wrapper to compute fps. See :func:`compute_fps` for details. """ if x0 in [S.Infinity, -S.Infinity]: dir = S.One if x0 is S.Infinity else -S.One temp = f.subs(x, 1/x) result = _compute_fps(temp, x, 0, dir, hyper, order, rational, full) if result is None: return None return (result[0], result[1].subs(x, 1/x), result[2].subs(x, 1/x)) elif x0 or dir == -S.One: if dir == -S.One: rep = -x + x0 rep2 = -x rep2b = x0 else: rep = x + x0 rep2 = x rep2b = -x0 temp = f.subs(x, rep) result = _compute_fps(temp, x, 0, S.One, hyper, order, rational, full) if result is None: return None return (result[0], result[1].subs(x, rep2 + rep2b), result[2].subs(x, rep2 + rep2b)) if f.is_polynomial(x): return None # Break instances of Add # this allows application of different # algorithms on different terms increasing the # range of admissible functions. if isinstance(f, Add): result = False ak = sequence(S.Zero, (0, oo)) ind, xk = S.Zero, None for t in Add.make_args(f): res = _compute_fps(t, x, 0, S.One, hyper, order, rational, full) if res: if not result: result = True xk = res[1] if res[0].start > ak.start: seq = ak s, f = ak.start, res[0].start else: seq = res[0] s, f = res[0].start, ak.start save = Add(*[z[0]*z[1] for z in zip(seq[0:(f - s)], xk[s:f])]) ak += res[0] ind += res[2] + save else: ind += t if result: return ak, xk, ind return None result = None # from here on it's x0=0 and dir=1 handling k = Dummy('k') if rational: result = rational_algorithm(f, x, k, order, full) if result is None and hyper: result = hyper_algorithm(f, x, k, order) if result is None: return None ak = sequence(result[0], (k, result[2], oo)) xk = sequence(x**k, (k, 0, oo)) ind = result[1] return ak, xk, ind
def rsolve_poly(coeffs, f, n, **hints): r""" Given linear recurrence operator `\operatorname{L}` of order `k` with polynomial coefficients and inhomogeneous equation `\operatorname{L} y = f`, where `f` is a polynomial, we seek for all polynomial solutions over field `K` of characteristic zero. The algorithm performs two basic steps: (1) Compute degree `N` of the general polynomial solution. (2) Find all polynomials of degree `N` or less of `\operatorname{L} y = f`. There are two methods for computing the polynomial solutions. If the degree bound is relatively small, i.e. it's smaller than or equal to the order of the recurrence, then naive method of undetermined coefficients is being used. This gives system of algebraic equations with `N+1` unknowns. In the other case, the algorithm performs transformation of the initial equation to an equivalent one, for which the system of algebraic equations has only `r` indeterminates. This method is quite sophisticated (in comparison with the naive one) and was invented together by Abramov, Bronstein and Petkovsek. It is possible to generalize the algorithm implemented here to the case of linear q-difference and differential equations. Lets say that we would like to compute `m`-th Bernoulli polynomial up to a constant. For this we can use `b(n+1) - b(n) = m n^{m-1}` recurrence, which has solution `b(n) = B_m + C`. For example: >>> from sympy import Symbol, rsolve_poly >>> n = Symbol('n', integer=True) >>> rsolve_poly([-1, 1], 4*n**3, n) C0 + n**4 - 2*n**3 + n**2 References ========== .. [1] S. A. Abramov, M. Bronstein and M. Petkovsek, On polynomial solutions of linear operator equations, in: T. Levelt, ed., Proc. ISSAC '95, ACM Press, New York, 1995, 290-296. .. [2] M. Petkovsek, Hypergeometric solutions of linear recurrences with polynomial coefficients, J. Symbolic Computation, 14 (1992), 243-264. .. [3] M. Petkovsek, H. S. Wilf, D. Zeilberger, A = B, 1996. """ f = sympify(f) if not f.is_polynomial(n): return None homogeneous = f.is_zero r = len(coeffs) - 1 coeffs = [Poly(coeff, n) for coeff in coeffs] polys = [Poly(0, n)]*(r + 1) terms = [(S.Zero, S.NegativeInfinity)]*(r + 1) for i in range(r + 1): for j in range(i, r + 1): polys[i] += coeffs[j]*binomial(j, i) if not polys[i].is_zero: (exp,), coeff = polys[i].LT() terms[i] = (coeff, exp) d = b = terms[0][1] for i in range(1, r + 1): if terms[i][1] > d: d = terms[i][1] if terms[i][1] - i > b: b = terms[i][1] - i d, b = int(d), int(b) x = Dummy('x') degree_poly = S.Zero for i in range(r + 1): if terms[i][1] - i == b: degree_poly += terms[i][0]*FallingFactorial(x, i) nni_roots = list(roots(degree_poly, x, filter='Z', predicate=lambda r: r >= 0).keys()) if nni_roots: N = [max(nni_roots)] else: N = [] if homogeneous: N += [-b - 1] else: N += [f.as_poly(n).degree() - b, -b - 1] N = int(max(N)) if N < 0: if homogeneous: if hints.get('symbols', False): return (S.Zero, []) else: return S.Zero else: return None if N <= r: C = [] y = E = S.Zero for i in range(N + 1): C.append(Symbol('C' + str(i))) y += C[i] * n**i for i in range(r + 1): E += coeffs[i].as_expr()*y.subs(n, n + i) solutions = solve_undetermined_coeffs(E - f, C, n) if solutions is not None: C = [c for c in C if (c not in solutions)] result = y.subs(solutions) else: return None # TBD else: A = r U = N + A + b + 1 nni_roots = list(roots(polys[r], filter='Z', predicate=lambda r: r >= 0).keys()) if nni_roots != []: a = max(nni_roots) + 1 else: a = S.Zero def _zero_vector(k): return [S.Zero] * k def _one_vector(k): return [S.One] * k def _delta(p, k): B = S.One D = p.subs(n, a + k) for i in range(1, k + 1): B *= -Rational(k - i + 1, i) D += B * p.subs(n, a + k - i) return D alpha = {} for i in range(-A, d + 1): I = _one_vector(d + 1) for k in range(1, d + 1): I[k] = I[k - 1] * (x + i - k + 1)/k alpha[i] = S.Zero for j in range(A + 1): for k in range(d + 1): B = binomial(k, i + j) D = _delta(polys[j].as_expr(), k) alpha[i] += I[k]*B*D V = Matrix(U, A, lambda i, j: int(i == j)) if homogeneous: for i in range(A, U): v = _zero_vector(A) for k in range(1, A + b + 1): if i - k < 0: break B = alpha[k - A].subs(x, i - k) for j in range(A): v[j] += B * V[i - k, j] denom = alpha[-A].subs(x, i) for j in range(A): V[i, j] = -v[j] / denom else: G = _zero_vector(U) for i in range(A, U): v = _zero_vector(A) g = S.Zero for k in range(1, A + b + 1): if i - k < 0: break B = alpha[k - A].subs(x, i - k) for j in range(A): v[j] += B * V[i - k, j] g += B * G[i - k] denom = alpha[-A].subs(x, i) for j in range(A): V[i, j] = -v[j] / denom G[i] = (_delta(f, i - A) - g) / denom P, Q = _one_vector(U), _zero_vector(A) for i in range(1, U): P[i] = (P[i - 1] * (n - a - i + 1)/i).expand() for i in range(A): Q[i] = Add(*[(v*p).expand() for v, p in zip(V[:, i], P)]) if not homogeneous: h = Add(*[(g*p).expand() for g, p in zip(G, P)]) C = [Symbol('C' + str(i)) for i in range(A)] g = lambda i: Add(*[c*_delta(q, i) for c, q in zip(C, Q)]) if homogeneous: E = [g(i) for i in range(N + 1, U)] else: E = [g(i) + _delta(h, i) for i in range(N + 1, U)] if E != []: solutions = solve(E, *C) if not solutions: if homogeneous: if hints.get('symbols', False): return (S.Zero, []) else: return S.Zero else: return None else: solutions = {} if homogeneous: result = S.Zero else: result = h for c, q in list(zip(C, Q)): if c in solutions: s = solutions[c]*q C.remove(c) else: s = c*q result += s.expand() if hints.get('symbols', False): return (result, C) else: return result
def bivariate_type(f, x, y, **kwargs): """Given an expression, f, 3 tests will be done to see what type of composite bivariate it might be, options for u(x, y) are:: x*y x+y x*y+x x*y+y If it matches one of these types, ``u(x, y)``, ``P(u)`` and dummy variable ``u`` will be returned. Solving ``P(u)`` for ``u`` and equating the solutions to ``u(x, y)`` and then solving for ``x`` or ``y`` is equivalent to solving the original expression for ``x`` or ``y``. If ``x`` and ``y`` represent two functions in the same variable, e.g. ``x = g(t)`` and ``y = h(t)``, then if ``u(x, y) - p`` can be solved for ``t`` then these represent the solutions to ``P(u) = 0`` when ``p`` are the solutions of ``P(u) = 0``. Only positive values of ``u`` are considered. Examples ======== >>> from sympy.solvers.solvers import solve >>> from sympy.solvers.bivariate import bivariate_type >>> from sympy.abc import x, y >>> eq = (x**2 - 3).subs(x, x + y) >>> bivariate_type(eq, x, y) (x + y, _u**2 - 3, _u) >>> uxy, pu, u = _ >>> usol = solve(pu, u); usol [sqrt(3)] >>> [solve(uxy - s) for s in solve(pu, u)] [[{x: -y + sqrt(3)}]] >>> all(eq.subs(s).equals(0) for sol in _ for s in sol) True """ u = Dummy('u', positive=True) if kwargs.pop('first', True): p = Poly(f, x, y) f = p.as_expr() _x = Dummy() _y = Dummy() rv = bivariate_type(Poly(f.subs({x: _x, y: _y}), _x, _y), _x, _y, first=False) if rv: reps = {_x: x, _y: y} return rv[0].xreplace(reps), rv[1].xreplace(reps), rv[2] return p = f f = p.as_expr() # f(x*y) args = Add.make_args(p.as_expr()) new = [] for a in args: a = _mexpand(a.subs(x, u/y)) free = a.free_symbols if x in free or y in free: break new.append(a) else: return x*y, Add(*new), u def ok(f, v, c): new = _mexpand(f.subs(v, c)) free = new.free_symbols return None if (x in free or y in free) else new # f(a*x + b*y) new = [] d = p.degree(x) if p.degree(y) == d: a = root(p.coeff_monomial(x**d), d) b = root(p.coeff_monomial(y**d), d) new = ok(f, x, (u - b*y)/a) if new is not None: return a*x + b*y, new, u # f(a*x*y + b*y) new = [] d = p.degree(x) if p.degree(y) == d: for itry in range(2): a = root(p.coeff_monomial(x**d*y**d), d) b = root(p.coeff_monomial(y**d), d) new = ok(f, x, (u - b*y)/a/y) if new is not None: return a*x*y + b*y, new, u x, y = y, x
def is_eq(lhs, rhs, assumptions=None): """ Fuzzy bool representing mathematical equality between *lhs* and *rhs*. Parameters ========== lhs : Expr The left-hand side of the expression, must be sympified. rhs : Expr The right-hand side of the expression, must be sympified. assumptions: Boolean, optional Assumptions taken to evaluate the equality. Returns ======= ``True`` if *lhs* is equal to *rhs*, ``False`` is *lhs* is not equal to *rhs*, and ``None`` if the comparison between *lhs* and *rhs* is indeterminate. Explanation =========== This function is intended to give a relatively fast determination and deliberately does not attempt slow calculations that might help in obtaining a determination of True or False in more difficult cases. :func:`~.is_neq` calls this function to return its value, so supporting new type with this function will ensure correct behavior for ``is_neq`` as well. Examples ======== >>> from sympy import Q, S >>> from sympy.core.relational import is_eq, is_neq >>> from sympy.abc import x >>> is_eq(S(0), S(0)) True >>> is_neq(S(0), S(0)) False >>> is_eq(S(0), S(2)) False >>> is_neq(S(0), S(2)) True Assumptions can be passed to evaluate the equality which is otherwise indeterminate. >>> print(is_eq(x, S(0))) None >>> is_eq(x, S(0), assumptions=Q.zero(x)) True New types can be supported by dispatching to ``_eval_is_eq``. >>> from sympy import Basic, sympify >>> from sympy.multipledispatch import dispatch >>> class MyBasic(Basic): ... def __new__(cls, arg): ... return Basic.__new__(cls, sympify(arg)) ... @property ... def value(self): ... return self.args[0] ... >>> @dispatch(MyBasic, MyBasic) ... def _eval_is_eq(a, b): ... return is_eq(a.value, b.value) ... >>> a = MyBasic(1) >>> b = MyBasic(1) >>> is_eq(a, b) True >>> is_neq(a, b) False """ from sympy.assumptions.wrapper import (AssumptionsWrapper, is_infinite, is_extended_real) from sympy.core.add import Add from sympy.functions.elementary.complexes import arg from sympy.simplify.simplify import clear_coefficients from sympy.utilities.iterables import sift # here, _eval_Eq is only called for backwards compatibility # new code should use is_eq with multiple dispatch as # outlined in the docstring for side1, side2 in (lhs, rhs), (rhs, lhs): eval_func = getattr(side1, '_eval_Eq', None) if eval_func is not None: retval = eval_func(side2) if retval is not None: return retval retval = _eval_is_eq(lhs, rhs) if retval is not None: return retval if dispatch(type(lhs), type(rhs)) != dispatch(type(rhs), type(lhs)): retval = _eval_is_eq(rhs, lhs) if retval is not None: return retval # retval is still None, so go through the equality logic # If expressions have the same structure, they must be equal. if lhs == rhs: return True # e.g. True == True elif all(isinstance(i, BooleanAtom) for i in (rhs, lhs)): return False # True != False elif not (lhs.is_Symbol or rhs.is_Symbol) and ( isinstance(lhs, Boolean) != isinstance(rhs, Boolean)): return False # only Booleans can equal Booleans _lhs = AssumptionsWrapper(lhs, assumptions) _rhs = AssumptionsWrapper(rhs, assumptions) if _lhs.is_infinite or _rhs.is_infinite: if fuzzy_xor([_lhs.is_infinite, _rhs.is_infinite]): return False if fuzzy_xor([_lhs.is_extended_real, _rhs.is_extended_real]): return False if fuzzy_and([_lhs.is_extended_real, _rhs.is_extended_real]): return fuzzy_xor([_lhs.is_extended_positive, fuzzy_not(_rhs.is_extended_positive)]) # Try to split real/imaginary parts and equate them I = S.ImaginaryUnit def split_real_imag(expr): real_imag = lambda t: ( 'real' if is_extended_real(t, assumptions) else 'imag' if is_extended_real(I*t, assumptions) else None) return sift(Add.make_args(expr), real_imag) lhs_ri = split_real_imag(lhs) if not lhs_ri[None]: rhs_ri = split_real_imag(rhs) if not rhs_ri[None]: eq_real = is_eq(Add(*lhs_ri['real']), Add(*rhs_ri['real']), assumptions) eq_imag = is_eq(I * Add(*lhs_ri['imag']), I * Add(*rhs_ri['imag']), assumptions) return fuzzy_and(map(fuzzy_bool, [eq_real, eq_imag])) # Compare e.g. zoo with 1+I*oo by comparing args arglhs = arg(lhs) argrhs = arg(rhs) # Guard against Eq(nan, nan) -> Falsesymp if not (arglhs == S.NaN and argrhs == S.NaN): return fuzzy_bool(is_eq(arglhs, argrhs, assumptions)) if all(isinstance(i, Expr) for i in (lhs, rhs)): # see if the difference evaluates dif = lhs - rhs _dif = AssumptionsWrapper(dif, assumptions) z = _dif.is_zero if z is not None: if z is False and _dif.is_commutative: # issue 10728 return False if z: return True n2 = _n2(lhs, rhs) if n2 is not None: return _sympify(n2 == 0) # see if the ratio evaluates n, d = dif.as_numer_denom() rv = None _n = AssumptionsWrapper(n, assumptions) _d = AssumptionsWrapper(d, assumptions) if _n.is_zero: rv = _d.is_nonzero elif _n.is_finite: if _d.is_infinite: rv = True elif _n.is_zero is False: rv = _d.is_infinite if rv is None: # if the condition that makes the denominator # infinite does not make the original expression # True then False can be returned l, r = clear_coefficients(d, S.Infinity) args = [_.subs(l, r) for _ in (lhs, rhs)] if args != [lhs, rhs]: rv = fuzzy_bool(is_eq(*args, assumptions)) if rv is True: rv = None elif any(is_infinite(a, assumptions) for a in Add.make_args(n)): # (inf or nan)/x != 0 rv = False if rv is not None: return rv