def heuristics(e, z, z0, dir): """Computes the limit of an expression term-wise. Parameters are the same as for the ``limit`` function. Works with the arguments of expression ``e`` one by one, computing the limit of each and then combining the results. This approach works only for simple limits, but it is fast. """ from sympy.calculus.util import AccumBounds rv = None if abs(z0) is S.Infinity: rv = limit(e.subs(z, 1/z), z, S.Zero, "+" if z0 is S.Infinity else "-") if isinstance(rv, Limit): return elif e.is_Mul or e.is_Add or e.is_Pow or e.is_Function: r = [] for a in e.args: l = limit(a, z, z0, dir) if l.has(S.Infinity) and l.is_finite is None: if isinstance(e, Add): m = factor_terms(e) if not isinstance(m, Mul): # try together m = together(m) if not isinstance(m, Mul): # try factor if the previous methods failed m = factor(e) if isinstance(m, Mul): return heuristics(m, z, z0, dir) return return elif isinstance(l, Limit): return elif l is S.NaN: return else: r.append(l) if r: rv = e.func(*r) if rv is S.NaN and e.is_Mul and any(isinstance(rr, AccumBounds) for rr in r): r2 = [] e2 = [] for ii in range(len(r)): if isinstance(r[ii], AccumBounds): r2.append(r[ii]) else: e2.append(e.args[ii]) if len(e2) > 0: e3 = Mul(*e2).simplify() l = limit(e3, z, z0, dir) rv = l * Mul(*r2) if rv is S.NaN: try: rat_e = ratsimp(e) except PolynomialError: return if rat_e is S.NaN or rat_e == e: return return limit(rat_e, z, z0, dir) return rv
def distance(self, o): """ Finds the shortest distance between a line and a point. Raises ====== NotImplementedError is raised if o is not a Point Examples ======== >>> from sympy import Point, Line >>> p1, p2 = Point(0, 0), Point(1, 1) >>> s = Line(p1, p2) >>> s.distance(Point(-1, 1)) sqrt(2) >>> s.distance((-1, 2)) 3*sqrt(2)/2 """ if not isinstance(o, Point): if is_sequence(o): o = Point(o) a, b, c = self.coefficients if 0 in (a, b): return self.perpendicular_segment(o).length m = self.slope x = o.x y = m*x - c/b return abs(factor_terms(o.y - y))/sqrt(1 + m**2)
def _eval_as_leading_term(self, x): from sympy import expand_mul, factor_terms old = self expr = expand_mul(self) if not expr.is_Add: return expr.as_leading_term(x) infinite = [t for t in expr.args if t.is_infinite] expr = expr.func(*[t.as_leading_term(x) for t in expr.args]).removeO() if not expr: # simple leading term analysis gave us 0 but we have to send # back a term, so compute the leading term (via series) return old.compute_leading_term(x) elif expr is S.NaN: return old.func._from_args(infinite) elif not expr.is_Add: return expr else: plain = expr.func(*[s for s, _ in expr.extract_leading_order(x)]) rv = factor_terms(plain, fraction=False) rv_simplify = rv.simplify() # if it simplifies to an x-free expression, return that; # tests don't fail if we don't but it seems nicer to do this if x not in rv_simplify.free_symbols: if rv_simplify.is_zero and plain.is_zero is not True: return (expr - plain)._eval_as_leading_term(x) return rv_simplify return rv
def _is_connected(cls, x, y): """ Check if x and y are connected somehow. """ from sympy.core.exprtools import factor_terms def hit(v, t, f): if not v.is_Relational: return t if v else f for i in range(2): if x == y: return True r = hit(x >= y, Max, Min) if r is not None: return r r = hit(y <= x, Max, Min) if r is not None: return r r = hit(x <= y, Min, Max) if r is not None: return r r = hit(y >= x, Min, Max) if r is not None: return r # simplification can be expensive, so be conservative # in what is attempted x = factor_terms(x - y) y = S.Zero return False
def heuristics(e, z, z0, dir): from sympy.calculus.util import AccumBounds rv = None if abs(z0) is S.Infinity: rv = limit(e.subs(z, 1/z), z, S.Zero, "+" if z0 is S.Infinity else "-") if isinstance(rv, Limit): return elif e.is_Mul or e.is_Add or e.is_Pow or e.is_Function: r = [] for a in e.args: l = limit(a, z, z0, dir) if l.has(S.Infinity) and l.is_finite is None: if isinstance(e, Add): m = factor_terms(e) if not isinstance(m, Mul): # try together m = together(m) if not isinstance(m, Mul): # try factor if the previous methods failed m = factor(e) if isinstance(m, Mul): return heuristics(m, z, z0, dir) return return elif isinstance(l, Limit): return elif l is S.NaN: return else: r.append(l) if r: rv = e.func(*r) if rv is S.NaN and e.is_Mul and any(isinstance(rr, AccumBounds) for rr in r): r2 = [] e2 = [] for ii in range(len(r)): if isinstance(r[ii], AccumBounds): r2.append(r[ii]) else: e2.append(e.args[ii]) if len(e2) > 0: e3 = Mul(*e2).simplify() l = limit(e3, z, z0, dir) rv = l * Mul(*r2) if rv is S.NaN: try: rat_e = ratsimp(e) except PolynomialError: return if rat_e is S.NaN or rat_e == e: return return limit(rat_e, z, z0, dir) return rv
def eval(cls, arg): if not arg.is_Atom: c, arg_ = factor_terms(arg).as_coeff_Mul() if arg_.is_Mul: arg_ = Mul(*[a if (sign(a) not in (-1, 1)) else sign(a) for a in arg_.args]) arg_ = sign(c) * arg_ else: arg_ = arg x, y = re(arg_), im(arg_) rv = C.atan2(y, x) if rv.is_number and not rv.atoms(AppliedUndef): return rv if arg_ != arg: return cls(arg_, evaluate=False)
def test_factor_terms(): A = Symbol('A', commutative=False) assert factor_terms(9*(x + x*y + 1) + (3*x + 3)**(2 + 2*x)) == \ 9*x*y + 9*x + _keep_coeff(S(3), x + 1)**_keep_coeff(S(2), x + 1) + 9 assert factor_terms(9*(x + x*y + 1) + (3)**(2 + 2*x)) == \ _keep_coeff(S(9), 3**(2*x) + x*y + x + 1) assert factor_terms(3**(2 + 2*x) + a*3**(2 + 2*x)) == \ 9*3**(2*x)*(a + 1) assert factor_terms(x + x*A) == \ x*(1 + A) assert factor_terms(sin(x + x*A)) == \ sin(x*(1 + A)) assert factor_terms((3*x + 3)**((2 + 2*x)/3)) == \ _keep_coeff(S(3), x + 1)**_keep_coeff(S(2)/3, x + 1) assert factor_terms(x + (x*y + x)**(3*x + 3)) == \ x + (x*(y + 1))**_keep_coeff(S(3), x + 1) assert factor_terms(a*(x + x*y) + b*(x*2 + y*x*2)) == \ x*(a + 2*b)*(y + 1) i = Integral(x, (x, 0, oo)) assert factor_terms(i) == i
def calculate_series(e, x, logx=None): """ Calculates at least one term of the series of "e" in "x". This is a place that fails most often, so it is in its own function. """ from sympy.core.exprtools import factor_terms n = 1 while 1: series = e.nseries(x, n=n, logx=logx) if not series.has(Order): # The series expansion is locally exact. return series series = series.removeO() series = factor_terms(series, fraction=True) if series: return series n *= 2
def eval(cls, arg): if isinstance(arg, exp_polar): return periodic_argument(arg, oo) if not arg.is_Atom: c, arg_ = factor_terms(arg).as_coeff_Mul() if arg_.is_Mul: arg_ = Mul(*[a if (sign(a) not in (-1, 1)) else sign(a) for a in arg_.args]) arg_ = sign(c)*arg_ else: arg_ = arg if arg_.atoms(AppliedUndef): return x, y = arg_.as_real_imag() rv = atan2(y, x) if rv.is_number: return rv if arg_ != arg: return cls(arg_, evaluate=False)
def heuristics(e, z, z0, dir): rv = None if abs(z0) is S.Infinity: rv = limit(e.subs(z, 1/z), z, S.Zero, "+" if z0 is S.Infinity else "-") if isinstance(rv, Limit): return elif e.is_Mul or e.is_Add or e.is_Pow or e.is_Function: r = [] for a in e.args: l = limit(a, z, z0, dir) if l.has(S.Infinity) and l.is_finite is None: if isinstance(e, Add): m = factor_terms(e) if not isinstance(m, Mul): # try together m = together(m) if not isinstance(m, Mul): # try factor if the previous methods failed m = factor(e) if isinstance(m, Mul): return heuristics(m, z, z0, dir) return return elif isinstance(l, Limit): return elif l is S.NaN: return else: r.append(l) if r: rv = e.func(*r) if rv is S.NaN: try: rat_e = ratsimp(e) except PolynomialError: return if rat_e is S.NaN or rat_e == e: return return limit(rat_e, z, z0, dir) return rv
def _eval_power(self, e): if e.is_Rational and self.is_number: from sympy.core.evalf import pure_complex from sympy.core.mul import _unevaluated_Mul from sympy.core.exprtools import factor_terms from sympy.core.function import expand_multinomial from sympy.functions.elementary.complexes import sign from sympy.functions.elementary.miscellaneous import sqrt ri = pure_complex(self) if ri: r, i = ri if e.q == 2: D = sqrt(r**2 + i**2) if D.is_Rational: # (r, i, D) is a Pythagorean triple root = sqrt(factor_terms((D - r)/2))**e.p return root*expand_multinomial(( # principle value (D + r)/abs(i) + sign(i)*S.ImaginaryUnit)**e.p) elif e == -1: return _unevaluated_Mul( r - i*S.ImaginaryUnit, 1/(r**2 + i**2))
def roots(f, *gens, auto=True, cubics=True, trig=False, quartics=True, quintics=False, multiple=False, filter=None, predicate=None, strict=False, **flags): """ Computes symbolic roots of a univariate polynomial. Given a univariate polynomial f with symbolic coefficients (or a list of the polynomial's coefficients), returns a dictionary with its roots and their multiplicities. Only roots expressible via radicals will be returned. To get a complete set of roots use RootOf class or numerical methods instead. By default cubic and quartic formulas are used in the algorithm. To disable them because of unreadable output set ``cubics=False`` or ``quartics=False`` respectively. If cubic roots are real but are expressed in terms of complex numbers (casus irreducibilis [1]) the ``trig`` flag can be set to True to have the solutions returned in terms of cosine and inverse cosine functions. To get roots from a specific domain set the ``filter`` flag with one of the following specifiers: Z, Q, R, I, C. By default all roots are returned (this is equivalent to setting ``filter='C'``). By default a dictionary is returned giving a compact result in case of multiple roots. However to get a list containing all those roots set the ``multiple`` flag to True; the list will have identical roots appearing next to each other in the result. (For a given Poly, the all_roots method will give the roots in sorted numerical order.) If the ``strict`` flag is True, ``UnsolvableFactorError`` will be raised if the roots found are known to be incomplete (because some roots are not expressible in radicals). Examples ======== >>> from sympy import Poly, roots, degree >>> from sympy.abc import x, y >>> roots(x**2 - 1, x) {-1: 1, 1: 1} >>> p = Poly(x**2-1, x) >>> roots(p) {-1: 1, 1: 1} >>> p = Poly(x**2-y, x, y) >>> roots(Poly(p, x)) {-sqrt(y): 1, sqrt(y): 1} >>> roots(x**2 - y, x) {-sqrt(y): 1, sqrt(y): 1} >>> roots([1, 0, -1]) {-1: 1, 1: 1} ``roots`` will only return roots expressible in radicals. If the given polynomial has some or all of its roots inexpressible in radicals, the result of ``roots`` will be incomplete or empty respectively. Example where result is incomplete: >>> roots((x-1)*(x**5-x+1), x) {1: 1} In this case, the polynomial has an unsolvable quintic factor whose roots cannot be expressed by radicals. The polynomial has a rational root (due to the factor `(x-1)`), which is returned since ``roots`` always finds all rational roots. Example where result is empty: >>> roots(x**7-3*x**2+1, x) {} Here, the polynomial has no roots expressible in radicals, so ``roots`` returns an empty dictionary. The result produced by ``roots`` is complete if and only if the sum of the multiplicity of each root is equal to the degree of the polynomial. If strict=True, UnsolvableFactorError will be raised if the result is incomplete. The result can be be checked for completeness as follows: >>> f = x**3-2*x**2+1 >>> sum(roots(f, x).values()) == degree(f, x) True >>> f = (x-1)*(x**5-x+1) >>> sum(roots(f, x).values()) == degree(f, x) False References ========== .. [1] https://en.wikipedia.org/wiki/Cubic_function#Trigonometric_.28and_hyperbolic.29_method """ from sympy.polys.polytools import to_rational_coeffs flags = dict(flags) if isinstance(f, list): if gens: raise ValueError('redundant generators given') x = Dummy('x') poly, i = {}, len(f) - 1 for coeff in f: poly[i], i = sympify(coeff), i - 1 f = Poly(poly, x, field=True) else: try: F = Poly(f, *gens, **flags) if not isinstance(f, Poly) and not F.gen.is_Symbol: raise PolynomialError("generator must be a Symbol") f = F except GeneratorsNeeded: if multiple: return [] else: return {} else: n = f.degree() if f.length() == 2 and n > 2: # check for foo**n in constant if dep is c*gen**m con, dep = f.as_expr().as_independent(*f.gens) fcon = -(-con).factor() if fcon != con: con = fcon bases = [] for i in Mul.make_args(con): if i.is_Pow: b, e = i.as_base_exp() if e.is_Integer and b.is_Add: bases.append((b, Dummy(positive=True))) if bases: rv = roots(Poly((dep + con).xreplace(dict(bases)), *f.gens), *F.gens, auto=auto, cubics=cubics, trig=trig, quartics=quartics, quintics=quintics, multiple=multiple, filter=filter, predicate=predicate, **flags) return { factor_terms(k.xreplace({v: k for k, v in bases})): v for k, v in rv.items() } if f.is_multivariate: raise PolynomialError('multivariate polynomials are not supported') def _update_dict(result, zeros, currentroot, k): if currentroot == S.Zero: if S.Zero in zeros: zeros[S.Zero] += k else: zeros[S.Zero] = k if currentroot in result: result[currentroot] += k else: result[currentroot] = k def _try_decompose(f): """Find roots using functional decomposition. """ factors, roots = f.decompose(), [] for currentroot in _try_heuristics(factors[0]): roots.append(currentroot) for currentfactor in factors[1:]: previous, roots = list(roots), [] for currentroot in previous: g = currentfactor - Poly(currentroot, f.gen) for currentroot in _try_heuristics(g): roots.append(currentroot) return roots def _try_heuristics(f): """Find roots using formulas and some tricks. """ if f.is_ground: return [] if f.is_monomial: return [S.Zero] * f.degree() if f.length() == 2: if f.degree() == 1: return list(map(cancel, roots_linear(f))) else: return roots_binomial(f) result = [] for i in [-1, 1]: if not f.eval(i): f = f.quo(Poly(f.gen - i, f.gen)) result.append(i) break n = f.degree() if n == 1: result += list(map(cancel, roots_linear(f))) elif n == 2: result += list(map(cancel, roots_quadratic(f))) elif f.is_cyclotomic: result += roots_cyclotomic(f) elif n == 3 and cubics: result += roots_cubic(f, trig=trig) elif n == 4 and quartics: result += roots_quartic(f) elif n == 5 and quintics: result += roots_quintic(f) return result # Convert the generators to symbols dumgens = symbols('x:%d' % len(f.gens), cls=Dummy) f = f.per(f.rep, dumgens) (k, ), f = f.terms_gcd() if not k: zeros = {} else: zeros = {S.Zero: k} coeff, f = preprocess_roots(f) if auto and f.get_domain().is_Ring: f = f.to_field() # Use EX instead of ZZ_I or QQ_I if f.get_domain().is_QQ_I: f = f.per(f.rep.convert(EX)) rescale_x = None translate_x = None result = {} if not f.is_ground: dom = f.get_domain() if not dom.is_Exact and dom.is_Numerical: for r in f.nroots(): _update_dict(result, zeros, r, 1) elif f.degree() == 1: _update_dict(result, zeros, roots_linear(f)[0], 1) elif f.length() == 2: roots_fun = roots_quadratic if f.degree() == 2 else roots_binomial for r in roots_fun(f): _update_dict(result, zeros, r, 1) else: _, factors = Poly(f.as_expr()).factor_list() if len(factors) == 1 and f.degree() == 2: for r in roots_quadratic(f): _update_dict(result, zeros, r, 1) else: if len(factors) == 1 and factors[0][1] == 1: if f.get_domain().is_EX: res = to_rational_coeffs(f) if res: if res[0] is None: translate_x, f = res[2:] else: rescale_x, f = res[1], res[-1] result = roots(f) if not result: for currentroot in _try_decompose(f): _update_dict(result, zeros, currentroot, 1) else: for r in _try_heuristics(f): _update_dict(result, zeros, r, 1) else: for currentroot in _try_decompose(f): _update_dict(result, zeros, currentroot, 1) else: for currentfactor, k in factors: for r in _try_heuristics( Poly(currentfactor, f.gen, field=True)): _update_dict(result, zeros, r, k) if coeff is not S.One: _result, result, = result, {} for currentroot, k in _result.items(): result[coeff * currentroot] = k if filter not in [None, 'C']: handlers = { 'Z': lambda r: r.is_Integer, 'Q': lambda r: r.is_Rational, 'R': lambda r: all(a.is_real for a in r.as_numer_denom()), 'I': lambda r: r.is_imaginary, } try: query = handlers[filter] except KeyError: raise ValueError("Invalid filter: %s" % filter) for zero in dict(result).keys(): if not query(zero): del result[zero] if predicate is not None: for zero in dict(result).keys(): if not predicate(zero): del result[zero] if rescale_x: result1 = {} for k, v in result.items(): result1[k * rescale_x] = v result = result1 if translate_x: result1 = {} for k, v in result.items(): result1[k + translate_x] = v result = result1 # adding zero roots after non-trivial roots have been translated result.update(zeros) if strict and sum(result.values()) < f.degree(): raise UnsolvableFactorError( filldedent(''' Strict mode: some factors cannot be solved in radicals, so a complete list of solutions cannot be returned. Call roots with strict=False to get solutions expressible in radicals (if there are any). ''')) if not multiple: return result else: zeros = [] for zero in ordered(result): zeros.extend([zero] * result[zero]) return zeros
def _solve_inequality(ie, s, linear=False): """Return the inequality with s isolated on the left, if possible. If the relationship is non-linear, a solution involving And or Or may be returned. False or True are returned if the relationship is never True or always True, respectively. If `linear` is True (default is False) an `s`-dependent expression will be isoloated on the left, if possible but it will not be solved for `s` unless the expression is linear in `s`. Furthermore, only "safe" operations which don't change the sense of the relationship are applied: no division by an unsigned value is attempted unless the relationship involves Eq or Ne and no division by a value not known to be nonzero is ever attempted. Examples ======== >>> from sympy import Eq, Symbol >>> from sympy.solvers.inequalities import _solve_inequality as f >>> from sympy.abc import x, y For linear expressions, the symbol can be isolated: >>> f(x - 2 < 0, x) x < 2 >>> f(-x - 6 < x, x) x > -3 Sometimes nonlinear relationships will be False >>> f(x**2 + 4 < 0, x) False Or they may involve more than one region of values: >>> f(x**2 - 4 < 0, x) (-2 < x) & (x < 2) To restrict the solution to a relational, set linear=True and only the x-dependent portion will be isolated on the left: >>> f(x**2 - 4 < 0, x, linear=True) x**2 < 4 Division of only nonzero quantities is allowed, so x cannot be isolated by dividing by y: >>> y.is_nonzero is None # it is unknown whether it is 0 or not True >>> f(x*y < 1, x) x*y < 1 And while an equality (or unequality) still holds after dividing by a non-zero quantity >>> nz = Symbol('nz', nonzero=True) >>> f(Eq(x*nz, 1), x) Eq(x, 1/nz) the sign must be known for other inequalities involving > or <: >>> f(x*nz <= 1, x) nz*x <= 1 >>> p = Symbol('p', positive=True) >>> f(x*p <= 1, x) x <= 1/p When there are denominators in the original expression that are removed by expansion, conditions for them will be returned as part of the result: >>> f(x < x*(2/x - 1), x) (x < 1) & Ne(x, 0) """ from sympy.solvers.solvers import denoms if s not in ie.free_symbols: return ie if ie.rhs == s: ie = ie.reversed if ie.lhs == s and s not in ie.rhs.free_symbols: return ie expr = ie.lhs - ie.rhs rv = None try: p = Poly(expr, s) if p.degree() == 0: rv = ie.func(p.as_expr(), 0) elif not linear and p.degree() > 1: # handle in except clause raise NotImplementedError except (PolynomialError, NotImplementedError): if not linear: try: return reduce_rational_inequalities([[ie]], s) except PolynomialError: return solve_univariate_inequality(ie, s) else: p = Poly(expr) e = expanded = p.as_expr() # this is in exanded form if rv is None: # Do a safe inversion of e, moving non-s terms # to the rhs and dividing by a nonzero factor if # the relational is Eq/Ne; for other relationals # the sign must also be positive or negative rhs = 0 b, ax = e.as_independent(s, as_Add=True) e -= b rhs -= b ef = factor_terms(e) a, e = ef.as_independent(s, as_Add=False) if (a.is_zero != False or # don't divide by potential 0 a.is_negative == a.is_positive == None and # if sign is not known then ie.rel_op not in ('!=', '==')): # reject if not Eq/Ne e = ef a = S.One rhs /= a if a.is_positive: rv = ie.func(e, rhs) else: rv = ie.reversed.func(e, rhs) # return conditions under which the value is # valid, too. conds = [rv] beginning_denoms = denoms(ie.lhs) | denoms(ie.rhs) current_denoms = denoms(expanded) for d in beginning_denoms - current_denoms: conds.append(_solve_inequality(Ne(d, 0), s, linear=linear)) return And(*conds)
def test_issue_3261(): a, b = symbols("a b") apb = a + b eq = apb + apb**2*(-2*a - 2*b) assert factor_terms(sub_pre(eq)) == a + b - 2*(a + b)**3
def _eval_product(self, term, limits): from sympy.concrete.delta import deltaproduct, _has_simple_delta from sympy.concrete.summations import summation from sympy.functions import KroneckerDelta, RisingFactorial (k, a, n) = limits if k not in term.free_symbols: if (term - 1).is_zero: return S.One return term**(n - a + 1) if a == n: return term.subs(k, a) if term.has(KroneckerDelta) and _has_simple_delta(term, limits[0]): return deltaproduct(term, limits) dif = n - a if dif.is_Integer: return Mul(*[term.subs(k, a + i) for i in range(dif + 1)]) elif term.is_polynomial(k): poly = term.as_poly(k) A = B = Q = S.One all_roots = roots(poly) M = 0 for r, m in all_roots.items(): M += m A *= RisingFactorial(a - r, n - a + 1)**m Q *= (n - r)**m if M < poly.degree(): arg = quo(poly, Q.as_poly(k)) B = self.func(arg, (k, a, n)).doit() return poly.LC()**(n - a + 1) * A * B elif term.is_Add: factored = factor_terms(term, fraction=True) if factored.is_Mul: return self._eval_product(factored, (k, a, n)) elif term.is_Mul: exclude, include = [], [] for t in term.args: p = self._eval_product(t, (k, a, n)) if p is not None: exclude.append(p) else: include.append(t) if not exclude: return None else: arg = term._new_rawargs(*include) A = Mul(*exclude) B = self.func(arg, (k, a, n)).doit() return A * B elif term.is_Pow: if not term.base.has(k): s = summation(term.exp, (k, a, n)) return term.base**s elif not term.exp.has(k): p = self._eval_product(term.base, (k, a, n)) if p is not None: return p**term.exp elif isinstance(term, Product): evaluated = term.doit() f = self._eval_product(evaluated, limits) if f is None: return self.func(evaluated, limits) else: return f
def doit(self, **hints): """Evaluates limit""" from sympy.series.limitseq import limit_seq from sympy.functions import RisingFactorial e, z, z0, dir = self.args if hints.get('deep', True): e = e.doit(**hints) z = z.doit(**hints) z0 = z0.doit(**hints) if e == z: return z0 if not e.has(z): return e # gruntz fails on factorials but works with the gamma function # If no factorial term is present, e should remain unchanged. # factorial is defined to be zero for negative inputs (which # differs from gamma) so only rewrite for positive z0. if z0.is_positive: e = e.rewrite([factorial, RisingFactorial], gamma) if e.is_Mul: if abs(z0) is S.Infinity: e = factor_terms(e) e = e.rewrite(fibonacci, GoldenRatio) ok = lambda w: (z in w.free_symbols and any(a.is_polynomial(z) or any(z in m.free_symbols and m.is_polynomial(z) for m in Mul.make_args(a)) for a in Add.make_args(w))) if all(ok(w) for w in e.as_numer_denom()): u = Dummy(positive=(z0 is S.Infinity)) inve = e.subs(z, 1/u) r = limit(inve.as_leading_term(u), u, S.Zero, "+" if z0 is S.Infinity else "-") if isinstance(r, Limit): return self else: return r if e.is_Order: return Order(limit(e.expr, z, z0), *e.args[1:]) try: r = gruntz(e, z, z0, dir) if r is S.NaN: raise PoleError() except (PoleError, ValueError): r = heuristics(e, z, z0, dir) if r is None: return self except NotImplementedError: # Trying finding limits of sequences if hints.get('sequence', True) and z0 is S.Infinity: trials = hints.get('trials', 5) r = limit_seq(e, z, trials) if r is None: raise NotImplementedError() else: raise NotImplementedError() return r
def test_factor_terms(): A = Symbol('A', commutative=False) assert factor_terms(9*(x + x*y + 1) + (3*x + 3)**(2 + 2*x)) == \ 9*x*y + 9*x + _keep_coeff(S(3), x + 1)**_keep_coeff(S(2), x + 1) + 9 assert factor_terms(9*(x + x*y + 1) + (3)**(2 + 2*x)) == \ _keep_coeff(S(9), 3**(2*x) + x*y + x + 1) assert factor_terms(3**(2 + 2*x) + a*3**(2 + 2*x)) == \ 9*3**(2*x)*(a + 1) assert factor_terms(x + x*A) == \ x*(1 + A) assert factor_terms(sin(x + x*A)) == \ sin(x*(1 + A)) assert factor_terms((3*x + 3)**((2 + 2*x)/3)) == \ _keep_coeff(S(3), x + 1)**_keep_coeff(S(2)/3, x + 1) assert factor_terms(x + (x*y + x)**(3*x + 3)) == \ x + (x*(y + 1))**_keep_coeff(S(3), x + 1) assert factor_terms(a*(x + x*y) + b*(x*2 + y*x*2)) == \ x*(a + 2*b)*(y + 1) i = Integral(x, (x, 0, oo)) assert factor_terms(i) == i eq = sqrt(2) + sqrt(10) assert factor_terms(eq) == eq assert factor_terms(eq, radical=True) == sqrt(2)*(1 + sqrt(5)) eq = [x + x*y] ans = [x*(y + 1)] for c in [list, tuple, set]: assert factor_terms(c(eq)) == c(ans) assert factor_terms(Tuple(x + x*y)) == Tuple(x*(y + 1)) assert factor_terms(Interval(0, 1)) == Interval(0, 1) e = 1/sqrt(a/2 + 1) assert factor_terms(e, clear=False) == 1/sqrt(a/2 + 1) assert factor_terms(e, clear=True) == sqrt(2)/sqrt(a + 2)
def _solve_inequality(ie, s, linear=False): """Return the inequality with s isolated on the left, if possible. If the relationship is non-linear, a solution involving And or Or may be returned. False or True are returned if the relationship is never True or always True, respectively. If `linear` is True (default is False) an `s`-dependent expression will be isolated on the left, if possible but it will not be solved for `s` unless the expression is linear in `s`. Furthermore, only "safe" operations which don't change the sense of the relationship are applied: no division by an unsigned value is attempted unless the relationship involves Eq or Ne and no division by a value not known to be nonzero is ever attempted. Examples ======== >>> from sympy import Eq, Symbol >>> from sympy.solvers.inequalities import _solve_inequality as f >>> from sympy.abc import x, y For linear expressions, the symbol can be isolated: >>> f(x - 2 < 0, x) x < 2 >>> f(-x - 6 < x, x) x > -3 Sometimes nonlinear relationships will be False >>> f(x**2 + 4 < 0, x) False Or they may involve more than one region of values: >>> f(x**2 - 4 < 0, x) (-2 < x) & (x < 2) To restrict the solution to a relational, set linear=True and only the x-dependent portion will be isolated on the left: >>> f(x**2 - 4 < 0, x, linear=True) x**2 < 4 Division of only nonzero quantities is allowed, so x cannot be isolated by dividing by y: >>> y.is_nonzero is None # it is unknown whether it is 0 or not True >>> f(x*y < 1, x) x*y < 1 And while an equality (or inequality) still holds after dividing by a non-zero quantity >>> nz = Symbol('nz', nonzero=True) >>> f(Eq(x*nz, 1), x) Eq(x, 1/nz) the sign must be known for other inequalities involving > or <: >>> f(x*nz <= 1, x) nz*x <= 1 >>> p = Symbol('p', positive=True) >>> f(x*p <= 1, x) x <= 1/p When there are denominators in the original expression that are removed by expansion, conditions for them will be returned as part of the result: >>> f(x < x*(2/x - 1), x) (x < 1) & Ne(x, 0) """ from sympy.solvers.solvers import denoms if s not in ie.free_symbols: return ie if ie.rhs == s: ie = ie.reversed if ie.lhs == s and s not in ie.rhs.free_symbols: return ie def classify(ie, s, i): # return True or False if ie evaluates when substituting s with # i else None (if unevaluated) or NaN (when there is an error # in evaluating) try: v = ie.subs(s, i) if v is S.NaN: return v elif v not in (True, False): return return v except TypeError: return S.NaN rv = None oo = S.Infinity expr = ie.lhs - ie.rhs try: p = Poly(expr, s) if p.degree() == 0: rv = ie.func(p.as_expr(), 0) elif not linear and p.degree() > 1: # handle in except clause raise NotImplementedError except (PolynomialError, NotImplementedError): if not linear: try: rv = reduce_rational_inequalities([[ie]], s) except PolynomialError: rv = solve_univariate_inequality(ie, s) # remove restrictions wrt +/-oo that may have been # applied when using sets to simplify the relationship okoo = classify(ie, s, oo) if okoo is S.true and classify(rv, s, oo) is S.false: rv = rv.subs(s < oo, True) oknoo = classify(ie, s, -oo) if (oknoo is S.true and classify(rv, s, -oo) is S.false): rv = rv.subs(-oo < s, True) rv = rv.subs(s > -oo, True) if rv is S.true: rv = (s <= oo) if okoo is S.true else (s < oo) if oknoo is not S.true: rv = And(-oo < s, rv) else: p = Poly(expr) conds = [] if rv is None: e = p.as_expr() # this is in expanded form # Do a safe inversion of e, moving non-s terms # to the rhs and dividing by a nonzero factor if # the relational is Eq/Ne; for other relationals # the sign must also be positive or negative rhs = 0 b, ax = e.as_independent(s, as_Add=True) e -= b rhs -= b ef = factor_terms(e) a, e = ef.as_independent(s, as_Add=False) if (a.is_zero != False or # don't divide by potential 0 a.is_negative == a.is_positive == None and # if sign is not known then ie.rel_op not in ('!=', '==')): # reject if not Eq/Ne e = ef a = S.One rhs /= a if a.is_positive: rv = ie.func(e, rhs) else: rv = ie.reversed.func(e, rhs) # return conditions under which the value is # valid, too. beginning_denoms = denoms(ie.lhs) | denoms(ie.rhs) current_denoms = denoms(rv) for d in beginning_denoms - current_denoms: c = _solve_inequality(Eq(d, 0), s, linear=linear) if isinstance(c, Eq) and c.lhs == s: if classify(rv, s, c.rhs) is S.true: # rv is permitting this value but it shouldn't conds.append(~c) for i in (-oo, oo): if (classify(rv, s, i) is S.true and classify(ie, s, i) is not S.true): conds.append(s < i if i is oo else i < s) conds.append(rv) return And(*conds)
def doit(self, **hints): """Evaluates limit""" from sympy.series.limitseq import limit_seq from sympy.functions import RisingFactorial e, z, z0, dir = self.args if hints.get('deep', True): e = e.doit(**hints) z = z.doit(**hints) z0 = z0.doit(**hints) if e == z: return z0 if not e.has(z): return e # gruntz fails on factorials but works with the gamma function # If no factorial term is present, e should remain unchanged. # factorial is defined to be zero for negative inputs (which # differs from gamma) so only rewrite for positive z0. if z0.is_positive: e = e.rewrite([factorial, RisingFactorial], gamma) if e.is_Mul: if abs(z0) is S.Infinity: e = factor_terms(e) e = e.rewrite(fibonacci, GoldenRatio) ok = lambda w: (z in w.free_symbols and any( a.is_polynomial(z) or any( z in m.free_symbols and m.is_polynomial(z) for m in Mul.make_args(a)) for a in Add.make_args(w))) if all(ok(w) for w in e.as_numer_denom()): u = Dummy(positive=True) if z0 is S.NegativeInfinity: inve = e.subs(z, -1 / u) else: inve = e.subs(z, 1 / u) r = limit(inve.as_leading_term(u), u, S.Zero, "+") if isinstance(r, Limit): return self else: return r if e.is_Order: return Order(limit(e.expr, z, z0), *e.args[1:]) try: r = gruntz(e, z, z0, dir) if r is S.NaN: raise PoleError() except (PoleError, ValueError): r = heuristics(e, z, z0, dir) if r is None: return self except NotImplementedError: # Trying finding limits of sequences if hints.get('sequence', True) and z0 is S.Infinity: trials = hints.get('trials', 5) r = limit_seq(e, z, trials) if r is None: raise NotImplementedError() else: raise NotImplementedError() return r
def _eval_product(self, term, limits): from sympy.concrete.delta import deltaproduct, _has_simple_delta from sympy.concrete.summations import summation from sympy.functions import KroneckerDelta, RisingFactorial (k, a, n) = limits if k not in term.free_symbols: if (term - 1).is_zero: return S.One return term**(n - a + 1) if a == n: return term.subs(k, a) if term.has(KroneckerDelta) and _has_simple_delta(term, limits[0]): return deltaproduct(term, limits) dif = n - a definite = dif.is_Integer if definite and (dif < 100): return self._eval_product_direct(term, limits) elif term.is_polynomial(k): poly = term.as_poly(k) A = B = Q = S.One all_roots = roots(poly) M = 0 for r, m in all_roots.items(): M += m A *= RisingFactorial(a - r, n - a + 1)**m Q *= (n - r)**m if M < poly.degree(): arg = quo(poly, Q.as_poly(k)) B = self.func(arg, (k, a, n)).doit() return poly.LC()**(n - a + 1) * A * B elif term.is_Add: factored = factor_terms(term, fraction=True) if factored.is_Mul: return self._eval_product(factored, (k, a, n)) elif term.is_Mul: # Factor in part without the summation variable and part with without_k, with_k = term.as_coeff_mul(k) if len(with_k) >= 2: # More than one term including k, so still a multiplication exclude, include = [], [] for t in with_k: p = self._eval_product(t, (k, a, n)) if p is not None: exclude.append(p) else: include.append(t) if not exclude: return None else: arg = term._new_rawargs(*include) A = Mul(*exclude) B = self.func(arg, (k, a, n)).doit() return without_k**(n - a + 1)*A * B else: # Just a single term p = self._eval_product(with_k[0], (k, a, n)) if p is None: p = self.func(with_k[0], (k, a, n)).doit() return without_k**(n - a + 1)*p elif term.is_Pow: if not term.base.has(k): s = summation(term.exp, (k, a, n)) return term.base**s elif not term.exp.has(k): p = self._eval_product(term.base, (k, a, n)) if p is not None: return p**term.exp elif isinstance(term, Product): evaluated = term.doit() f = self._eval_product(evaluated, limits) if f is None: return self.func(evaluated, limits) else: return f if definite: return self._eval_product_direct(term, limits)
def doit(self, **hints): """Evaluates the limit. Parameters ========== deep : bool, optional (default: True) Invoke the ``doit`` method of the expressions involved before taking the limit. hints : optional keyword arguments To be passed to ``doit`` methods; only used if deep is True. """ from sympy import Abs, sign e, z, z0, dir = self.args if z0 is S.ComplexInfinity: raise NotImplementedError("Limits at complex " "infinity are not implemented") if hints.get('deep', True): e = e.doit(**hints) z = z.doit(**hints) z0 = z0.doit(**hints) if e == z: return z0 if not e.has(z): return e if z0 is S.NaN: return S.NaN if e.has(S.Infinity, S.NegativeInfinity, S.ComplexInfinity, S.NaN): return self if e.is_Order: return Order(limit(e.expr, z, z0), *e.args[1:]) cdir = 0 if str(dir) == "+": cdir = 1 elif str(dir) == "-": cdir = -1 def set_signs(expr): if not expr.args: return expr newargs = tuple(set_signs(arg) for arg in expr.args) if newargs != expr.args: expr = expr.func(*newargs) abs_flag = isinstance(expr, Abs) sign_flag = isinstance(expr, sign) if abs_flag or sign_flag: sig = limit(expr.args[0], z, z0, dir) if sig.is_zero: sig = limit(1 / expr.args[0], z, z0, dir) if sig.is_extended_real: if (sig < 0) == True: return -expr.args[0] if abs_flag else S.NegativeOne elif (sig > 0) == True: return expr.args[0] if abs_flag else S.One return expr e = set_signs(e) if e.is_meromorphic(z, z0): if abs(z0) is S.Infinity: newe = e.subs(z, -1 / z) else: newe = e.subs(z, z + z0) try: coeff, ex = newe.leadterm(z, cdir=cdir) except ValueError: pass else: if ex > 0: return S.Zero elif ex == 0: return coeff if str(dir) == "+" or not (int(ex) & 1): return S.Infinity * sign(coeff) elif str(dir) == "-": return S.NegativeInfinity * sign(coeff) else: return S.ComplexInfinity if abs(z0) is S.Infinity: if e.is_Mul: e = factor_terms(e) newe = e.subs(z, 1 / z) # cdir changes sign as oo- should become 0+ cdir = -cdir else: newe = e.subs(z, z + z0) try: coeff, ex = newe.leadterm(z, cdir=cdir) except (ValueError, NotImplementedError, PoleError): # The NotImplementedError catching is for custom functions if e.is_Pow: r = self.pow_heuristics() if r is not None: return r else: if coeff.has(S.Infinity, S.NegativeInfinity, S.ComplexInfinity): return self if not coeff.has(z): if ex.is_positive: return S.Zero elif ex == 0: return coeff elif ex.is_negative: if ex.is_integer: if str(dir) == "-" or ex.is_even: return S.Infinity * sign(coeff) elif str(dir) == "+": return S.NegativeInfinity * sign(coeff) else: return S.ComplexInfinity else: if str(dir) == "+": return S.Infinity * sign(coeff) elif str(dir) == "-": return S.NegativeInfinity * sign( coeff) * S.NegativeOne**(S.One + ex) else: return S.ComplexInfinity # gruntz fails on factorials but works with the gamma function # If no factorial term is present, e should remain unchanged. # factorial is defined to be zero for negative inputs (which # differs from gamma) so only rewrite for positive z0. if z0.is_extended_positive: e = e.rewrite(factorial, gamma) l = None try: if str(dir) == '+-': r = gruntz(e, z, z0, '+') l = gruntz(e, z, z0, '-') if l != r: raise ValueError( "The limit does not exist since " "left hand limit = %s and right hand limit = %s" % (l, r)) else: r = gruntz(e, z, z0, dir) if r is S.NaN or l is S.NaN: raise PoleError() except (PoleError, ValueError): if l is not None: raise r = heuristics(e, z, z0, dir) if r is None: return self return r
def test_factor_terms(): A = Symbol('A', commutative=False) assert factor_terms(9*(x + x*y + 1) + (3*x + 3)**(2 + 2*x)) == \ 9*x*y + 9*x + _keep_coeff(S(3), x + 1)**_keep_coeff(S(2), x + 1) + 9 assert factor_terms(9*(x + x*y + 1) + (3)**(2 + 2*x)) == \ _keep_coeff(S(9), 3**(2*x) + x*y + x + 1) assert factor_terms(3**(2 + 2*x) + a*3**(2 + 2*x)) == \ 9*3**(2*x)*(a + 1) assert factor_terms(x + x*A) == \ x*(1 + A) assert factor_terms(sin(x + x*A)) == \ sin(x*(1 + A)) assert factor_terms((3*x + 3)**((2 + 2*x)/3)) == \ _keep_coeff(S(3), x + 1)**_keep_coeff(S(2)/3, x + 1) assert factor_terms(x + (x*y + x)**(3*x + 3)) == \ x + (x*(y + 1))**_keep_coeff(S(3), x + 1) assert factor_terms(a*(x + x*y) + b*(x*2 + y*x*2)) == \ x*(a + 2*b)*(y + 1) i = Integral(x, (x, 0, oo)) assert factor_terms(i) == i assert factor_terms(x/2 + y) == x/2 + y # fraction doesn't apply to integer denominators assert factor_terms(x/2 + y, fraction=True) == x/2 + y # clear *does* apply to the integer denominators assert factor_terms(x/2 + y, clear=True) == Mul(S.Half, x + 2*y, evaluate=False) # check radical extraction eq = sqrt(2) + sqrt(10) assert factor_terms(eq) == eq assert factor_terms(eq, radical=True) == sqrt(2)*(1 + sqrt(5)) eq = root(-6, 3) + root(6, 3) assert factor_terms(eq, radical=True) == 6**(S(1)/3)*(1 + (-1)**(S(1)/3)) eq = [x + x*y] ans = [x*(y + 1)] for c in [list, tuple, set]: assert factor_terms(c(eq)) == c(ans) assert factor_terms(Tuple(x + x*y)) == Tuple(x*(y + 1)) assert factor_terms(Interval(0, 1)) == Interval(0, 1) e = 1/sqrt(a/2 + 1) assert factor_terms(e, clear=False) == 1/sqrt(a/2 + 1) assert factor_terms(e, clear=True) == sqrt(2)/sqrt(a + 2) eq = x/(x + 1/x) + 1/(x**2 + 1) assert factor_terms(eq, fraction=False) == eq assert factor_terms(eq, fraction=True) == 1 assert factor_terms((1/(x**3 + x**2) + 2/x**2)*y) == \ y*(2 + 1/(x + 1))/x**2 # if not True, then processesing for this in factor_terms is not necessary assert gcd_terms(-x - y) == -x - y assert factor_terms(-x - y) == Mul(-1, x + y, evaluate=False) # if not True, then "special" processesing in factor_terms is not necessary assert gcd_terms(exp(Mul(-1, x + 1))) == exp(-x - 1) e = exp(-x - 2) + x assert factor_terms(e) == exp(Mul(-1, x + 2, evaluate=False)) + x assert factor_terms(e, sign=False) == e assert factor_terms(exp(-4*x - 2) - x) == -x + exp(Mul(-2, 2*x + 1, evaluate=False)) # sum tests assert factor_terms(Sum(x, (y, 1, 10))) == x * Sum(1, (y, 1, 10)) assert factor_terms(Sum(x, (y, 1, 10)) + x) == x * (1 + Sum(1, (y, 1, 10))) assert factor_terms(Sum(x*y + x*y**2, (y, 1, 10))) == x*Sum(y*(y + 1), (y, 1, 10))
def doit(self, **hints): """Evaluates the limit. Parameters ========== deep : bool, optional (default: True) Invoke the ``doit`` method of the expressions involved before taking the limit. hints : optional keyword arguments To be passed to ``doit`` methods; only used if deep is True. """ from sympy import sign from sympy.functions import RisingFactorial e, z, z0, dir = self.args if z0 is S.ComplexInfinity: raise NotImplementedError("Limits at complex " "infinity are not implemented") if hints.get('deep', True): e = e.doit(**hints) z = z.doit(**hints) z0 = z0.doit(**hints) if e == z: return z0 if not e.has(z): return e cdir = 0 if str(dir) == "+": cdir = 1 elif str(dir) == "-": cdir = -1 if e.is_meromorphic(z, z0): if abs(z0) is S.Infinity: newe = e.subs(z, -1 / z) else: newe = e.subs(z, z + z0) try: coeff, exp = newe.leadterm(z, cdir) except ValueError: pass else: if exp > 0: return S.Zero elif exp == 0: return coeff if str(dir) == "+" or not (int(exp) & 1): return S.Infinity * sign(coeff) elif str(dir) == "-": return S.NegativeInfinity * sign(coeff) else: return S.ComplexInfinity # gruntz fails on factorials but works with the gamma function # If no factorial term is present, e should remain unchanged. # factorial is defined to be zero for negative inputs (which # differs from gamma) so only rewrite for positive z0. if z0.is_extended_positive: e = e.rewrite([factorial, RisingFactorial], gamma) if e.is_Mul and abs(z0) is S.Infinity: e = factor_terms(e) u = Dummy('u', positive=True) if z0 is S.NegativeInfinity: inve = e.subs(z, -1 / u) else: inve = e.subs(z, 1 / u) try: f = inve.as_leading_term(u).gammasimp() if f.is_meromorphic(u, S.Zero): r = limit(f, u, S.Zero, "+") if isinstance(r, Limit): return self else: return r except (ValueError, NotImplementedError, PoleError): pass if e.is_Order: return Order(limit(e.expr, z, z0), *e.args[1:]) l = None try: if str(dir) == '+-': r = gruntz(e, z, z0, '+') l = gruntz(e, z, z0, '-') if l != r: raise ValueError( "The limit does not exist since " "left hand limit = %s and right hand limit = %s" % (l, r)) else: r = gruntz(e, z, z0, dir) if r is S.NaN or l is S.NaN: raise PoleError() except (PoleError, ValueError): if l is not None: raise r = heuristics(e, z, z0, dir) if r is None: return self return r
def test_issue_6360(): a, b = symbols("a b") apb = a + b eq = apb + apb**2 * (-2 * a - 2 * b) assert factor_terms(sub_pre(eq)) == a + b - 2 * (a + b)**3
def test_factor_terms(): A = Symbol('A', commutative=False) assert factor_terms(9*(x + x*y + 1) + (3*x + 3)**(2 + 2*x)) == \ 9*x*y + 9*x + _keep_coeff(S(3), x + 1)**_keep_coeff(S(2), x + 1) + 9 assert factor_terms(9*(x + x*y + 1) + (3)**(2 + 2*x)) == \ _keep_coeff(S(9), 3**(2*x) + x*y + x + 1) assert factor_terms(3**(2 + 2*x) + a*3**(2 + 2*x)) == \ 9*3**(2*x)*(a + 1) assert factor_terms(x + x*A) == \ x*(1 + A) assert factor_terms(sin(x + x*A)) == \ sin(x*(1 + A)) assert factor_terms((3*x + 3)**((2 + 2*x)/3)) == \ _keep_coeff(S(3), x + 1)**_keep_coeff(S(2)/3, x + 1) assert factor_terms(x + (x*y + x)**(3*x + 3)) == \ x + (x*(y + 1))**_keep_coeff(S(3), x + 1) assert factor_terms(a*(x + x*y) + b*(x*2 + y*x*2)) == \ x*(a + 2*b)*(y + 1) i = Integral(x, (x, 0, oo)) assert factor_terms(i) == i eq = sqrt(2) + sqrt(10) assert factor_terms(eq) == eq assert factor_terms(eq, radical=True) == sqrt(2) * (1 + sqrt(5))
def roots_quadratic(f): """Returns a list of roots of a quadratic polynomial. If the domain is ZZ then the roots will be sorted with negatives coming before positives. The ordering will be the same for any numerical coefficients as long as the assumptions tested are correct, otherwise the ordering will not be sorted (but will be canonical). """ a, b, c = f.all_coeffs() dom = f.get_domain() def _sqrt(d): # remove squares from square root since both will be represented # in the results; a similar thing is happening in roots() but # must be duplicated here because not all quadratics are binomials co = [] other = [] for di in Mul.make_args(d): if di.is_Pow and di.exp.is_Integer and di.exp % 2 == 0: co.append(Pow(di.base, di.exp//2)) else: other.append(di) if co: d = Mul(*other) co = Mul(*co) return co*sqrt(d) return sqrt(d) def _simplify(expr): if dom.is_Composite: return factor(expr) else: return simplify(expr) if c is S.Zero: r0, r1 = S.Zero, -b/a if not dom.is_Numerical: r1 = _simplify(r1) elif r1.is_negative: r0, r1 = r1, r0 elif b is S.Zero: r = -c/a if not dom.is_Numerical: r = _simplify(r) R = _sqrt(r) r0 = -R r1 = R else: d = b**2 - 4*a*c A = 2*a B = -b/A if not dom.is_Numerical: d = _simplify(d) B = _simplify(B) D = factor_terms(_sqrt(d)/A) r0 = B - D r1 = B + D if a.is_negative: r0, r1 = r1, r0 elif not dom.is_Numerical: r0, r1 = [expand_2arg(i) for i in (r0, r1)] return [r0, r1]
def test_factor_terms(): A = Symbol('A', commutative=False) assert factor_terms(9*(x + x*y + 1) + (3*x + 3)**(2 + 2*x)) == \ 9*x*y + 9*x + _keep_coeff(S(3), x + 1)**_keep_coeff(S(2), x + 1) + 9 assert factor_terms(9*(x + x*y + 1) + (3)**(2 + 2*x)) == \ _keep_coeff(S(9), 3**(2*x) + x*y + x + 1) assert factor_terms(3**(2 + 2*x) + a*3**(2 + 2*x)) == \ 9*3**(2*x)*(a + 1) assert factor_terms(x + x*A) == \ x*(1 + A) assert factor_terms(sin(x + x*A)) == \ sin(x*(1 + A)) assert factor_terms((3*x + 3)**((2 + 2*x)/3)) == \ _keep_coeff(S(3), x + 1)**_keep_coeff(S(2)/3, x + 1) assert factor_terms(x + (x*y + x)**(3*x + 3)) == \ x + (x*(y + 1))**_keep_coeff(S(3), x + 1) assert factor_terms(a*(x + x*y) + b*(x*2 + y*x*2)) == \ x*(a + 2*b)*(y + 1) i = Integral(x, (x, 0, oo)) assert factor_terms(i) == i eq = sqrt(2) + sqrt(10) assert factor_terms(eq) == eq assert factor_terms(eq, radical=True) == sqrt(2) * (1 + sqrt(5)) eq = [x + x * y] ans = [x * (y + 1)] for c in [list, tuple, set]: assert factor_terms(c(eq)) == c(ans) assert factor_terms(Tuple(x + x * y)) == Tuple(x * (y + 1)) assert factor_terms(Interval(0, 1)) == Interval(0, 1) e = 1 / sqrt(a / 2 + 1) assert factor_terms(e, clear=False) == 1 / sqrt(a / 2 + 1) assert factor_terms(e, clear=True) == sqrt(2) / sqrt(a + 2) eq = x / (x + 1 / x) + 1 / (x**2 + 1) assert factor_terms(eq, fraction=False) == eq assert factor_terms(eq, fraction=True) == 1 assert factor_terms((1/(x**3 + x**2) + 2/x**2)*y) == \ y*(2 + 1/(x + 1))/x**2
def _eval_simplify(self, **kwargs): return self.func(factor_terms(self.args[0])) # XXX include doit?
def doit(self, **hints): """Evaluates the limit. Parameters ========== deep : bool, optional (default: True) Invoke the ``doit`` method of the expressions involved before taking the limit. hints : optional keyword arguments To be passed to ``doit`` methods; only used if deep is True. """ from sympy import Abs, exp, log, sign from sympy.calculus.util import AccumBounds e, z, z0, dir = self.args if z0 is S.ComplexInfinity: raise NotImplementedError("Limits at complex " "infinity are not implemented") if hints.get('deep', True): e = e.doit(**hints) z = z.doit(**hints) z0 = z0.doit(**hints) if e == z: return z0 if not e.has(z): return e cdir = 0 if str(dir) == "+": cdir = 1 elif str(dir) == "-": cdir = -1 def remove_abs(expr): if not expr.args: return expr newargs = tuple(remove_abs(arg) for arg in expr.args) if newargs != expr.args: expr = expr.func(*newargs) if isinstance(expr, Abs): sig = limit(expr.args[0], z, z0, dir) if sig.is_zero: sig = limit(1/expr.args[0], z, z0, dir) if sig.is_extended_real: if (sig < 0) == True: return -expr.args[0] elif (sig > 0) == True: return expr.args[0] return expr e = remove_abs(e) if e.is_meromorphic(z, z0): if abs(z0) is S.Infinity: newe = e.subs(z, -1/z) else: newe = e.subs(z, z + z0) try: coeff, ex = newe.leadterm(z, cdir) except (ValueError, NotImplementedError): pass else: if ex > 0: return S.Zero elif ex == 0: return coeff if str(dir) == "+" or not(int(ex) & 1): return S.Infinity*sign(coeff) elif str(dir) == "-": return S.NegativeInfinity*sign(coeff) else: return S.ComplexInfinity # gruntz fails on factorials but works with the gamma function # If no factorial term is present, e should remain unchanged. # factorial is defined to be zero for negative inputs (which # differs from gamma) so only rewrite for positive z0. if z0.is_extended_positive: e = e.rewrite(factorial, gamma) if e.is_Mul and abs(z0) is S.Infinity: e = factor_terms(e) u = Dummy('u', positive=True) if z0 is S.NegativeInfinity: inve = e.subs(z, -1/u) else: inve = e.subs(z, 1/u) try: f = inve.as_leading_term(u).gammasimp() if f.is_meromorphic(u, S.Zero): r = limit(f, u, S.Zero, "+") if isinstance(r, Limit): return self else: return r except (ValueError, NotImplementedError, PoleError): pass if e.is_Order: return Order(limit(e.expr, z, z0), *e.args[1:]) if e.is_Pow: if e.has(S.Infinity, S.NegativeInfinity, S.ComplexInfinity, S.NaN): return self b1, e1 = e.base, e.exp f1 = e1*log(b1) if f1.is_meromorphic(z, z0): res = limit(f1, z, z0) return exp(res) ex_lim = limit(e1, z, z0) base_lim = limit(b1, z, z0) if base_lim is S.One: if ex_lim in (S.Infinity, S.NegativeInfinity): res = limit(e1*(b1 - 1), z, z0) return exp(res) elif ex_lim.is_real: return S.One if base_lim in (S.Zero, S.Infinity, S.NegativeInfinity) and ex_lim is S.Zero: res = limit(f1, z, z0) return exp(res) if base_lim is S.NegativeInfinity: if ex_lim is S.NegativeInfinity: return S.Zero if ex_lim is S.Infinity: return S.ComplexInfinity if not isinstance(base_lim, AccumBounds) and not isinstance(ex_lim, AccumBounds): res = base_lim**ex_lim if res is not S.ComplexInfinity and not res.is_Pow: return res l = None try: if str(dir) == '+-': r = gruntz(e, z, z0, '+') l = gruntz(e, z, z0, '-') if l != r: raise ValueError("The limit does not exist since " "left hand limit = %s and right hand limit = %s" % (l, r)) else: r = gruntz(e, z, z0, dir) if r is S.NaN or l is S.NaN: raise PoleError() except (PoleError, ValueError): if l is not None: raise r = heuristics(e, z, z0, dir) if r is None: return self return r
def test_factor_terms(): A = Symbol('A', commutative=False) assert factor_terms(9*(x + x*y + 1) + (3*x + 3)**(2 + 2*x)) == \ 9*x*y + 9*x + _keep_coeff(S(3), x + 1)**_keep_coeff(S(2), x + 1) + 9 assert factor_terms(9*(x + x*y + 1) + (3)**(2 + 2*x)) == \ _keep_coeff(S(9), 3**(2*x) + x*y + x + 1) assert factor_terms(3**(2 + 2*x) + a*3**(2 + 2*x)) == \ 9*3**(2*x)*(a + 1) assert factor_terms(x + x*A) == \ x*(1 + A) assert factor_terms(sin(x + x*A)) == \ sin(x*(1 + A)) assert factor_terms((3*x + 3)**((2 + 2*x)/3)) == \ _keep_coeff(S(3), x + 1)**_keep_coeff(S(2)/3, x + 1) assert factor_terms(x + (x*y + x)**(3*x + 3)) == \ x + (x*(y + 1))**_keep_coeff(S(3), x + 1) assert factor_terms(a*(x + x*y) + b*(x*2 + y*x*2)) == \ x*(a + 2*b)*(y + 1) i = Integral(x, (x, 0, oo)) assert factor_terms(i) == i # check radical extraction eq = sqrt(2) + sqrt(10) assert factor_terms(eq) == eq assert factor_terms(eq, radical=True) == sqrt(2) * (1 + sqrt(5)) eq = root(-6, 3) + root(6, 3) assert factor_terms(eq, radical=True) == 6**(S(1) / 3) * (1 + (-1)**(S(1) / 3)) eq = [x + x * y] ans = [x * (y + 1)] for c in [list, tuple, set]: assert factor_terms(c(eq)) == c(ans) assert factor_terms(Tuple(x + x * y)) == Tuple(x * (y + 1)) assert factor_terms(Interval(0, 1)) == Interval(0, 1) e = 1 / sqrt(a / 2 + 1) assert factor_terms(e, clear=False) == 1 / sqrt(a / 2 + 1) assert factor_terms(e, clear=True) == sqrt(2) / sqrt(a + 2) eq = x / (x + 1 / x) + 1 / (x**2 + 1) assert factor_terms(eq, fraction=False) == eq assert factor_terms(eq, fraction=True) == 1 assert factor_terms((1/(x**3 + x**2) + 2/x**2)*y) == \ y*(2 + 1/(x + 1))/x**2 # if not True, then processesing for this in factor_terms is not necessary assert gcd_terms(-x - y) == -x - y assert factor_terms(-x - y) == Mul(-1, x + y, evaluate=False) # if not True, then "special" processesing in factor_terms is not necessary assert gcd_terms(exp(Mul(-1, x + 1))) == exp(-x - 1) e = exp(-x - 2) + x assert factor_terms(e) == exp(Mul(-1, x + 2, evaluate=False)) + x assert factor_terms(e, sign=False) == e assert factor_terms(exp(-4 * x - 2) - x) == -x + exp(Mul(-2, 2 * x + 1, evaluate=False))
def doit(self, **hints): """Evaluates the limit. Parameters ========== deep : bool, optional (default: True) Invoke the ``doit`` method of the expressions involved before taking the limit. hints : optional keyword arguments To be passed to ``doit`` methods; only used if deep is True. """ from sympy.series.limitseq import limit_seq from sympy.functions import RisingFactorial e, z, z0, dir = self.args if z0 is S.ComplexInfinity: raise NotImplementedError("Limits at complex " "infinity are not implemented") if hints.get('deep', True): e = e.doit(**hints) z = z.doit(**hints) z0 = z0.doit(**hints) if e == z: return z0 if not e.has(z): return e # gruntz fails on factorials but works with the gamma function # If no factorial term is present, e should remain unchanged. # factorial is defined to be zero for negative inputs (which # differs from gamma) so only rewrite for positive z0. if z0.is_extended_positive: e = e.rewrite([factorial, RisingFactorial], gamma) if e.is_Mul: if abs(z0) is S.Infinity: e = factor_terms(e) e = e.rewrite(fibonacci, GoldenRatio) ok = lambda w: (z in w.free_symbols and any( a.is_polynomial(z) or any( z in m.free_symbols and m.is_polynomial(z) for m in Mul.make_args(a)) for a in Add.make_args(w))) if all(ok(w) for w in e.as_numer_denom()): u = Dummy(positive=True) if z0 is S.NegativeInfinity: inve = e.subs(z, -1 / u) else: inve = e.subs(z, 1 / u) try: r = limit(inve.as_leading_term(u), u, S.Zero, "+") if isinstance(r, Limit): return self else: return r except ValueError: pass if e.is_Order: return Order(limit(e.expr, z, z0), *e.args[1:]) l = None try: if str(dir) == '+-': r = gruntz(e, z, z0, '+') l = gruntz(e, z, z0, '-') if l != r: raise ValueError( "The limit does not exist since " "left hand limit = %s and right hand limit = %s" % (l, r)) else: r = gruntz(e, z, z0, dir) if r is S.NaN or l is S.NaN: raise PoleError() except (PoleError, ValueError): if l is not None: raise r = heuristics(e, z, z0, dir) if r is None: return self return r
def test_factor_terms(): A = Symbol('A', commutative=False) assert factor_terms(9*(x + x*y + 1) + (3*x + 3)**(2 + 2*x)) == \ 9*x*y + 9*x + _keep_coeff(S(3), x + 1)**_keep_coeff(S(2), x + 1) + 9 assert factor_terms(9*(x + x*y + 1) + (3)**(2 + 2*x)) == \ _keep_coeff(S(9), 3**(2*x) + x*y + x + 1) assert factor_terms(3**(2 + 2*x) + a*3**(2 + 2*x)) == \ 9*3**(2*x)*(a + 1) assert factor_terms(x + x*A) == \ x*(1 + A) assert factor_terms(sin(x + x*A)) == \ sin(x*(1 + A)) assert factor_terms((3*x + 3)**((2 + 2*x)/3)) == \ _keep_coeff(S(3), x + 1)**_keep_coeff(S(2)/3, x + 1) assert factor_terms(x + (x*y + x)**(3*x + 3)) == \ x + (x*(y + 1))**_keep_coeff(S(3), x + 1) assert factor_terms(a*(x + x*y) + b*(x*2 + y*x*2)) == \ x*(a + 2*b)*(y + 1) i = Integral(x, (x, 0, oo)) assert factor_terms(i) == i eq = sqrt(2) + sqrt(10) assert factor_terms(eq) == eq assert factor_terms(eq, radical=True) == sqrt(2)*(1 + sqrt(5)) eq = [x + x*y] ans = [x*(y + 1)] for c in [list, tuple, set]: assert factor_terms(c(eq)) == c(ans) assert factor_terms(Tuple(x + x*y)) == Tuple(x*(y + 1)) assert factor_terms(Interval(0, 1)) == Interval(0, 1) e = 1/sqrt(a/2 + 1) assert factor_terms(e, clear=False) == 1/sqrt(a/2 + 1) assert factor_terms(e, clear=True) == sqrt(2)/sqrt(a + 2) eq = x/(x + 1/x) + 1/(x**2 + 1) assert factor_terms(eq, fraction=False) == eq assert factor_terms(eq, fraction=True) == 1 assert factor_terms((1/(x**3 + x**2) + 2/x**2)*y) == \ y*(2 + 1/(x + 1))/x**2 assert factor_terms(-x - y) == Mul(-1, x + y, evaluate=False) # if not True, then processes for this in factor_terms is not necessary assert gcd_terms(-x - y) == -x - y
def heuristics(e, z, z0, dir): """Computes the limit of an expression term-wise. Parameters are the same as for the ``limit`` function. Works with the arguments of expression ``e`` one by one, computing the limit of each and then combining the results. This approach works only for simple limits, but it is fast. """ from sympy.calculus.util import AccumBounds rv = None if abs(z0) is S.Infinity: rv = limit(e.subs(z, 1 / z), z, S.Zero, "+" if z0 is S.Infinity else "-") if isinstance(rv, Limit): return elif e.is_Mul or e.is_Add or e.is_Pow or e.is_Function: r = [] for a in e.args: l = limit(a, z, z0, dir) if l.has(S.Infinity) and l.is_finite is None: if isinstance(e, Add): m = factor_terms(e) if not isinstance(m, Mul): # try together m = together(m) if not isinstance( m, Mul): # try factor if the previous methods failed m = factor(e) if isinstance(m, Mul): return heuristics(m, z, z0, dir) return return elif isinstance(l, Limit): return elif l is S.NaN: return else: r.append(l) if r: rv = e.func(*r) if rv is S.NaN and e.is_Mul and any( isinstance(rr, AccumBounds) for rr in r): r2 = [] e2 = [] for ii in range(len(r)): if isinstance(r[ii], AccumBounds): r2.append(r[ii]) else: e2.append(e.args[ii]) if len(e2) > 0: e3 = Mul(*e2).simplify() l = limit(e3, z, z0, dir) rv = l * Mul(*r2) if rv is S.NaN: try: rat_e = ratsimp(e) except PolynomialError: return if rat_e is S.NaN or rat_e == e: return return limit(rat_e, z, z0, dir) return rv
def simp_hyp(expr): return factor_terms(expand_mul(expr)).rewrite(sin)
def doit(self, **hints): """Evaluates the limit. Parameters ========== deep : bool, optional (default: True) Invoke the ``doit`` method of the expressions involved before taking the limit. hints : optional keyword arguments To be passed to ``doit`` methods; only used if deep is True. """ from sympy.series.limitseq import limit_seq from sympy.functions import RisingFactorial e, z, z0, dir = self.args if z0 is S.ComplexInfinity: raise NotImplementedError("Limits at complex " "infinity are not implemented") if hints.get('deep', True): e = e.doit(**hints) z = z.doit(**hints) z0 = z0.doit(**hints) if e == z: return z0 if not e.has(z): return e # gruntz fails on factorials but works with the gamma function # If no factorial term is present, e should remain unchanged. # factorial is defined to be zero for negative inputs (which # differs from gamma) so only rewrite for positive z0. if z0.is_positive: e = e.rewrite([factorial, RisingFactorial], gamma) if e.is_Mul: if abs(z0) is S.Infinity: e = factor_terms(e) e = e.rewrite(fibonacci, GoldenRatio) ok = lambda w: (z in w.free_symbols and any(a.is_polynomial(z) or any(z in m.free_symbols and m.is_polynomial(z) for m in Mul.make_args(a)) for a in Add.make_args(w))) if all(ok(w) for w in e.as_numer_denom()): u = Dummy(positive=True) if z0 is S.NegativeInfinity: inve = e.subs(z, -1/u) else: inve = e.subs(z, 1/u) r = limit(inve.as_leading_term(u), u, S.Zero, "+") if isinstance(r, Limit): return self else: return r if e.is_Order: return Order(limit(e.expr, z, z0), *e.args[1:]) try: r = gruntz(e, z, z0, dir) if r is S.NaN: raise PoleError() except (PoleError, ValueError): r = heuristics(e, z, z0, dir) if r is None: return self return r
def test_factor_terms(): A = Symbol("A", commutative=False) assert (factor_terms(9 * (x + x * y + 1) + (3 * x + 3)**(2 + 2 * x)) == 9 * x * y + 9 * x + _keep_coeff(S(3), x + 1)**_keep_coeff(S(2), x + 1) + 9) assert factor_terms(9 * (x + x * y + 1) + (3)**(2 + 2 * x)) == _keep_coeff( S(9), 3**(2 * x) + x * y + x + 1) assert factor_terms(3**(2 + 2 * x) + a * 3**(2 + 2 * x)) == 9 * 3**(2 * x) * (a + 1) assert factor_terms(x + x * A) == x * (1 + A) assert factor_terms(sin(x + x * A)) == sin(x * (1 + A)) assert factor_terms((3 * x + 3)**((2 + 2 * x) / 3)) == _keep_coeff( S(3), x + 1)**_keep_coeff(Rational(2, 3), x + 1) assert factor_terms(x + (x * y + x)**(3 * x + 3)) == x + ( x * (y + 1))**_keep_coeff(S(3), x + 1) assert factor_terms(a * (x + x * y) + b * (x * 2 + y * x * 2)) == x * (a + 2 * b) * (y + 1) i = Integral(x, (x, 0, oo)) assert factor_terms(i) == i assert factor_terms(x / 2 + y) == x / 2 + y # fraction doesn't apply to integer denominators assert factor_terms(x / 2 + y, fraction=True) == x / 2 + y # clear *does* apply to the integer denominators assert factor_terms(x / 2 + y, clear=True) == Mul(S.Half, x + 2 * y, evaluate=False) # check radical extraction eq = sqrt(2) + sqrt(10) assert factor_terms(eq) == eq assert factor_terms(eq, radical=True) == sqrt(2) * (1 + sqrt(5)) eq = root(-6, 3) + root(6, 3) assert factor_terms( eq, radical=True) == 6**(S.One / 3) * (1 + (-1)**(S.One / 3)) eq = [x + x * y] ans = [x * (y + 1)] for c in [list, tuple, set]: assert factor_terms(c(eq)) == c(ans) assert factor_terms(Tuple(x + x * y)) == Tuple(x * (y + 1)) assert factor_terms(Interval(0, 1)) == Interval(0, 1) e = 1 / sqrt(a / 2 + 1) assert factor_terms(e, clear=False) == 1 / sqrt(a / 2 + 1) assert factor_terms(e, clear=True) == sqrt(2) / sqrt(a + 2) eq = x / (x + 1 / x) + 1 / (x**2 + 1) assert factor_terms(eq, fraction=False) == eq assert factor_terms(eq, fraction=True) == 1 assert (factor_terms( (1 / (x**3 + x**2) + 2 / x**2) * y) == y * (2 + 1 / (x + 1)) / x**2) # if not True, then processesing for this in factor_terms is not necessary assert gcd_terms(-x - y) == -x - y assert factor_terms(-x - y) == Mul(-1, x + y, evaluate=False) # if not True, then "special" processesing in factor_terms is not necessary assert gcd_terms(exp(Mul(-1, x + 1))) == exp(-x - 1) e = exp(-x - 2) + x assert factor_terms(e) == exp(Mul(-1, x + 2, evaluate=False)) + x assert factor_terms(e, sign=False) == e assert factor_terms(exp(-4 * x - 2) - x) == -x + exp(Mul(-2, 2 * x + 1, evaluate=False)) # sum/integral tests for F in (Sum, Integral): assert factor_terms(F(x, (y, 1, 10))) == x * F(1, (y, 1, 10)) assert factor_terms(F(x, (y, 1, 10)) + x) == x * (1 + F(1, (y, 1, 10))) assert factor_terms(F(x * y + x * y**2, (y, 1, 10))) == x * F(y * (y + 1), (y, 1, 10))
def continued_fraction_reduce(cf): """ Reduce a continued fraction to a rational or quadratic irrational. Compute the rational or quadratic irrational number from its terminating or periodic continued fraction expansion. The continued fraction expansion (cf) should be supplied as a terminating iterator supplying the terms of the expansion. For terminating continued fractions, this is equivalent to ``list(continued_fraction_convergents(cf))[-1]``, only a little more efficient. If the expansion has a repeating part, a list of the repeating terms should be returned as the last element from the iterator. This is the format returned by continued_fraction_periodic. For quadratic irrationals, returns the largest solution found, which is generally the one sought, if the fraction is in canonical form (all terms positive except possibly the first). Examples ======== >>> from sympy.ntheory.continued_fraction import continued_fraction_reduce >>> continued_fraction_reduce([1, 2, 3, 4, 5]) 225/157 >>> continued_fraction_reduce([-2, 1, 9, 7, 1, 2]) -256/233 >>> continued_fraction_reduce([2, 1, 2, 1, 1, 4, 1, 1, 6, 1, 1, 8]).n(10) 2.718281835 >>> continued_fraction_reduce([1, 4, 2, [3, 1]]) (sqrt(21) + 287)/238 >>> continued_fraction_reduce([[1]]) (1 + sqrt(5))/2 >>> from sympy.ntheory.continued_fraction import continued_fraction_periodic >>> continued_fraction_reduce(continued_fraction_periodic(8, 5, 13)) (sqrt(13) + 8)/5 See Also ======== continued_fraction_periodic """ from sympy.core.exprtools import factor_terms from sympy.core.symbol import Dummy from sympy.solvers import solve period = [] x = Dummy('x') def untillist(cf): for nxt in cf: if isinstance(nxt, list): period.extend(nxt) yield x break yield nxt a = Integer(0) for a in continued_fraction_convergents(untillist(cf)): pass if period: y = Dummy('y') solns = solve(continued_fraction_reduce(period + [y]) - y, y) solns.sort() pure = solns[-1] rv = a.subs(x, pure).radsimp() else: rv = a if rv.is_Add: rv = factor_terms(rv) if rv.is_Mul and rv.args[0] == -1: rv = rv.func(*rv.args) return rv
def __new__(cls, expr, *variables, **assumptions): global numderiv global derarr derarr.append(expr) expr = sympify(expr) # There are no variables, we differentiate wrt all of the free symbols # in expr. if not variables: variables = expr.free_symbols if len(variables) != 1: from sympy.utilities.misc import filldedent raise ValueError(filldedent(''' Since there is more than one variable in the expression, the variable(s) of differentiation must be supplied to differentiate %s''' % expr)) # Standardize the variables by sympifying them and making appending a # count of 1 if there is only one variable: diff(e,x)->diff(e,x,1). variables = list(sympify(variables)) if not variables[-1].is_Integer or len(variables) == 1: variables.append(S.One) # Split the list of variables into a list of the variables we are diff # wrt, where each element of the list has the form (s, count) where # s is the entity to diff wrt and count is the order of the # derivative. variable_count = [] all_zero = True i = 0 while i < len(variables) - 1: # process up to final Integer v, count = variables[i: i + 2] iwas = i if v._diff_wrt: # We need to test the more specific case of count being an # Integer first. if count.is_Integer: count = int(count) i += 2 elif count._diff_wrt: count = 1 i += 1 """ if i == iwas: # didn't get an update because of bad input from sympy.utilities.misc import filldedent raise ValueError(filldedent(''' Can\'t differentiate wrt the variable: %s, %s''' % (v, count))) """ if all_zero and not count == 0: all_zero = False if count: variable_count.append((v, count)) # We make a special case for 0th derivative, because there is no # good way to unambiguously print this. if all_zero: return expr # Pop evaluate because it is not really an assumption and we will need # to track it carefully below. evaluate = assumptions.pop('evaluate', False) # Look for a quick exit if there are symbols that don't appear in # expression at all. Note, this cannnot check non-symbols like # functions and Derivatives as those can be created by intermediate # derivatives. if evaluate: symbol_set = set(sc[0] for sc in variable_count if sc[0].is_Symbol) if symbol_set.difference(expr.free_symbols): return S.Zero # We make a generator so as to only generate a variable when necessary. # If a high order of derivative is requested and the expr becomes 0 # after a few differentiations, then we won't need the other variables. variablegen = (v for v, count in variable_count for i in xrange(count)) # If we can't compute the derivative of expr (but we wanted to) and # expr is itself not a Derivative, finish building an unevaluated # derivative class by calling Expr.__new__. if (not (hasattr(expr, '_eval_derivative') and evaluate) and (not isinstance(expr, Derivative))): variables = list(variablegen) # If we wanted to evaluate, we sort the variables into standard # order for later comparisons. This is too aggressive if evaluate # is False, so we don't do it in that case. if evaluate: #TODO: check if assumption of discontinuous derivatives exist variables = cls._sort_variables(variables) # Here we *don't* need to reinject evaluate into assumptions # because we are done with it and it is not an assumption that # Expr knows about. obj = Expr.__new__(cls, expr, *variables, **assumptions) #print("O",obj) return obj # Compute the derivative now by repeatedly calling the # _eval_derivative method of expr for each variable. When this method # returns None, the derivative couldn't be computed wrt that variable # and we save the variable for later. unhandled_variables = [] # Once we encouter a non_symbol that is unhandled, we stop taking # derivatives entirely. This is because derivatives wrt functions # don't commute with derivatives wrt symbols and we can't safely # continue. unhandled_non_symbol = False nderivs = 0 # how many derivatives were performed #print("varibale",variablegen) for v in variablegen: is_symbol = v.is_Symbol if unhandled_non_symbol: obj = None else: if not is_symbol: new_v = C.Dummy('xi_%i' % i) new_v.dummy_index = hash(v) expr = expr.subs(v, new_v) old_v = v v = new_v numderiv+=1 obj = expr._eval_derivative(v) numderiv-=1 nderivs += 1 if not is_symbol: if obj is not None: #print("first",obj) obj = obj.subs(v, old_v) #print("second",obj) #print("v ",v,"old ",old_v) v = old_v #print("----expr is",obj) if obj is None: #print ("yeyyyyy") unhandled_variables.append(v) if not is_symbol: unhandled_non_symbol = True elif obj is S.Zero: return S.Zero else: expr = obj #print("---expr is",expr.args[0:]) if unhandled_variables: unhandled_variables = cls._sort_variables(unhandled_variables) #print("1",expr) expr = Expr.__new__(cls, expr, *unhandled_variables, **assumptions) else: # We got a Derivative at the end of it all, and we rebuild it by # sorting its variables. #print(isinstance(expr,Derivative)) if isinstance(expr, Derivative): #print("#",expr.args[0]) expr = cls( expr.args[0], *cls._sort_variables(expr.args[1:]) ) #print("##",expr) #print("--expr is",expr) #print("-expr is",expr) if nderivs > 1 and assumptions.get('simplify', True): from sympy.core.exprtools import factor_terms from sympy.simplify.simplify import signsimp expr = factor_terms(signsimp(expr)) #print("expr is",expr) if numderiv==0: # no of recursive derivatives from sympy.printing.pretty import pprint temparr = derarr # so that following derivative does not causes infinite loop because it will keep adding to derarr. derarr=[] # prevents infinite loop for i in temparr: pprint(Derivative(i,'x',evaluate=False)) derarr = [] # empties, so that next derivative expr find it empty.. return expr