def fdiff(self, argindex=4): from diofant import Sum if argindex == 1: # Diff wrt n raise ArgumentIndexError(self, argindex) elif argindex == 2: # Diff wrt a n, a, b, x = self.args k = Dummy("k") f1 = 1 / (a + b + n + k + 1) f2 = ((a + b + 2*k + 1) * RisingFactorial(b + k + 1, n - k) / ((n - k) * RisingFactorial(a + b + k + 1, n - k))) return Sum(f1 * (jacobi(n, a, b, x) + f2*jacobi(k, a, b, x)), (k, 0, n - 1)) elif argindex == 3: # Diff wrt b n, a, b, x = self.args k = Dummy("k") f1 = 1 / (a + b + n + k + 1) f2 = (-1)**(n - k) * ((a + b + 2*k + 1) * RisingFactorial(a + k + 1, n - k) / ((n - k) * RisingFactorial(a + b + k + 1, n - k))) return Sum(f1 * (jacobi(n, a, b, x) + f2*jacobi(k, a, b, x)), (k, 0, n - 1)) elif argindex == 4: # Diff wrt x n, a, b, x = self.args return S.Half * (a + b + n + 1) * jacobi(n - 1, a + 1, b + 1, x) else: raise ArgumentIndexError(self, argindex)
def __contains__(self, o): if isinstance(o, Point): x = Dummy('x', extended_real=True) y = Dummy('y', extended_real=True) res = self.equation(x, y).subs({x: o.x, y: o.y}) return trigsimp(simplify(res)) is S.Zero elif isinstance(o, Ellipse): return self == o return False
def _do_ellipse_intersection(self, o): """The intersection of an ellipse with another ellipse or a circle. Private helper method for `intersection`. """ x = Dummy('x', extended_real=True) y = Dummy('y', extended_real=True) seq = self.equation(x, y) oeq = o.equation(x, y) result = solve([seq, oeq], [x, y]) return [Point(*r) for r in list(uniq(result))]
def test_core_symbol(): # make the Symbol a unique name that doesn't class with any other # testing variable in this file since after this test the symbol # having the same name will be cached as noncommutative for c in (Dummy, Dummy("x", commutative=False), Symbol, Symbol("_sympyissue_6229", commutative=False), Wild, Wild("x")): check(c)
def _sqrt_symbolic_denest(a, b, r): """Given an expression, sqrt(a + b*sqrt(b)), return the denested expression or None. Algorithm: If r = ra + rb*sqrt(rr), try replacing sqrt(rr) in ``a`` with (y**2 - ra)/rb, and if the result is a quadratic, ca*y**2 + cb*y + cc, and (cb + b)**2 - 4*ca*cc is 0, then sqrt(a + b*sqrt(r)) can be rewritten as sqrt(ca*(sqrt(r) + (cb + b)/(2*ca))**2). Examples ======== >>> from diofant.simplify.sqrtdenest import _sqrt_symbolic_denest, sqrtdenest >>> from diofant import sqrt, Symbol >>> from diofant.abc import x >>> a, b, r = 16 - 2*sqrt(29), 2, -10*sqrt(29) + 55 >>> _sqrt_symbolic_denest(a, b, r) sqrt(-2*sqrt(29) + 11) + sqrt(5) If the expression is numeric, it will be simplified: >>> w = sqrt(sqrt(sqrt(3) + 1) + 1) + 1 + sqrt(2) >>> sqrtdenest(sqrt((w**2).expand())) 1 + sqrt(2) + sqrt(1 + sqrt(1 + sqrt(3))) Otherwise, it will only be simplified if assumptions allow: >>> w = w.subs(sqrt(3), sqrt(x + 3)) >>> sqrtdenest(sqrt((w**2).expand())) sqrt((sqrt(sqrt(sqrt(x + 3) + 1) + 1) + 1 + sqrt(2))**2) Notice that the argument of the sqrt is a square. If x is made positive then the sqrt of the square is resolved: >>> _.subs(x, Symbol('x', positive=True)) sqrt(sqrt(sqrt(x + 3) + 1) + 1) + 1 + sqrt(2) """ a, b, r = map(sympify, (a, b, r)) rval = _sqrt_match(r) if not rval: return ra, rb, rr = rval if rb: y = Dummy('y', positive=True) try: newa = Poly(a.subs(sqrt(rr), (y**2 - ra) / rb), y) except PolynomialError: return if newa.degree() == 2: ca, cb, cc = newa.all_coeffs() cb += b if _mexpand(cb**2 - 4 * ca * cc).equals(0): z = sqrt(ca * (sqrt(r) + cb / (2 * ca))**2) if z.is_number: z = _mexpand(Mul._from_args(z.as_content_primitive())) return z
def _eval_rewrite_as_Sum(self, ap, bq, z): from diofant.functions import factorial, RisingFactorial, Piecewise from diofant import Sum n = Dummy("n", integer=True) rfap = Tuple(*[RisingFactorial(a, n) for a in ap]) rfbq = Tuple(*[RisingFactorial(b, n) for b in bq]) coeff = Mul(*rfap) / Mul(*rfbq) return Piecewise((Sum(coeff * z**n / factorial(n), (n, 0, oo)), self.convergence_statement), (self, True))
def _eval_sum_hyper(f, i, a): """ Returns (res, cond). Sums from a to oo. """ from diofant.functions import hyper from diofant.simplify import hyperexpand, hypersimp, fraction, simplify from diofant.polys.polytools import Poly, factor if a != 0: return _eval_sum_hyper(f.subs(i, i + a), i, 0) if f.subs(i, 0) == 0: if simplify(f.subs(i, Dummy('i', integer=True, positive=True))) == 0: return Integer(0), True return _eval_sum_hyper(f.subs(i, i + 1), i, 0) hs = hypersimp(f, i) if hs is None: return numer, denom = fraction(factor(hs)) top, topl = numer.as_coeff_mul(i) bot, botl = denom.as_coeff_mul(i) ab = [top, bot] factors = [topl, botl] params = [[], []] for k in range(2): for fac in factors[k]: mul = 1 if fac.is_Pow: mul = fac.exp fac = fac.base if not mul.is_Integer: return p = Poly(fac, i) if p.degree() != 1: return m, n = p.all_coeffs() ab[k] *= m**mul params[k] += [n / m] * mul # Add "1" to numerator parameters, to account for implicit n! in # hypergeometric series. ap = params[0] + [1] bq = params[1] x = ab[0] / ab[1] h = hyper(ap, bq, x) e = h try: e = hyperexpand(h) except PolynomialError: pass if e is S.NaN and h.convergence_statement: e = h return f.subs(i, 0) * e, h.convergence_statement
def _eval_is_real(self): for l in self.limits: if len(l) != 3: return if not l[1].is_real or not l[2].is_real: return f = self.function.subs( {v: Dummy("d", real=True) for v in self.variables}) if f.is_real: return True
def _symbols(name, n): """get vector of symbols local to this module""" try: lsyms = _symbols_cache[name] except KeyError: lsyms = [] _symbols_cache[name] = lsyms while len(lsyms) < n: lsyms.append(Dummy('%s%i' % (name, len(lsyms)))) return lsyms[:n]
def eval_sum_hyper(f, i_a_b): from diofant.logic.boolalg import And i, a, b = i_a_b if (b - a).is_Integer: # We are never going to do better than doing the sum in the obvious way return old_sum = Sum(f, (i, a, b)) if b != S.Infinity: if a == S.NegativeInfinity: res = _eval_sum_hyper(f.subs(i, -i), i, -b) if res is not None: return Piecewise(res, (old_sum, True)) else: res1 = _eval_sum_hyper(f, i, a) res2 = _eval_sum_hyper(f, i, b + 1) if res1 is None or res2 is None: return (res1, cond1), (res2, cond2) = res1, res2 cond = And(cond1, cond2) if cond == S.false: return return Piecewise((res1 - res2, cond), (old_sum, True)) if a == S.NegativeInfinity: res1 = _eval_sum_hyper(f.subs(i, -i), i, 1) res2 = _eval_sum_hyper(f, i, 0) if res1 is None or res2 is None: return res1, cond1 = res1 res2, cond2 = res2 cond = And(cond1, cond2) if cond == S.false: return return Piecewise((res1 + res2, cond), (old_sum, True)) # Now b == oo, a != -oo res = _eval_sum_hyper(f, i, a) if res is not None: r, c = res if c == S.false: if r.is_number: f = f.subs(i, Dummy('i', integer=True, positive=True) + a) if f.is_positive or f.is_zero: return S.Infinity elif f.is_negative: return S.NegativeInfinity return return Piecewise(res, (old_sum, True))
def fdiff(self, argindex=3): from diofant import Sum if argindex == 2: # Diff wrt alpha n, alpha, x = self.args k = Dummy("k") return Sum(assoc_laguerre(k, alpha, x) / (n - alpha), (k, 0, n - 1)) elif argindex == 3: # Diff wrt x n, alpha, x = self.args return -assoc_laguerre(n - 1, alpha + 1, x) else: # wrt n raise ArgumentIndexError(self, argindex)
def weak_normalizer(a, d, DE, z=None): """ Weak normalization. Given a derivation D on k[t] and f == a/d in k(t), return q in k[t] such that f - Dq/q is weakly normalized with respect to t. f in k(t) is said to be "weakly normalized" with respect to t if residue_p(f) is not a positive integer for any normal irreducible p in k[t] such that f is in R_p (Definition 6.1.1). If f has an elementary integral, this is equivalent to no logarithm of integral(f) whose argument depends on t has a positive integer coefficient, where the arguments of the logarithms not in k(t) are in k[t]. Returns (q, f - Dq/q) """ z = z or Dummy('z') dn, ds = splitfactor(d, DE) # Compute d1, where dn == d1*d2**2*...*dn**n is a square-free # factorization of d. g = gcd(dn, dn.diff(DE.t)) d_sqf_part = dn.quo(g) d1 = d_sqf_part.quo(gcd(d_sqf_part, g)) a1, b = gcdex_diophantine( d.quo(d1).as_poly(DE.t), d1.as_poly(DE.t), a.as_poly(DE.t)) r = (a - Poly(z, DE.t) * derivation(d1, DE)).as_poly(DE.t).resultant( d1.as_poly(DE.t)) r = Poly(r, z) if not r.has(z): return Poly(1, DE.t), (a, d) N = [i for i in r.real_roots() if i in ZZ and i > 0] q = reduce(mul, [gcd(a - Poly(n, DE.t) * derivation(d1, DE), d1) for n in N], Poly(1, DE.t)) dq = derivation(q, DE) sn = q * a - d * dq sd = q * d sn, sd = sn.cancel(sd, include=True) return q, (sn, sd)
def telescopic(L, R, limits): '''Tries to perform the summation using the telescopic property return None if not possible ''' (i, a, b) = limits if L.is_Add or R.is_Add: return # We want to solve(L.subs(i, i + m) + R, m) # First we try a simple match since this does things that # solve doesn't do, e.g. solve(f(k+m)-f(k), m) fails k = Wild("k") sol = (-R).match(L.subs(i, i + k)) s = None if sol and k in sol: s = sol[k] if not (s.is_Integer and L.subs(i, i + s) == -R): # sometimes match fail(f(x+2).match(-f(x+k))->{k: -2 - 2x})) s = None # But there are things that match doesn't do that solve # can do, e.g. determine that 1/(x + m) = 1/(1 - x) when m = 1 if s is None: m = Dummy('m') try: sol = solve(L.subs(i, i + m) + R, m) or [] except NotImplementedError: return sol = [ si for si in sol if si.is_Integer and (L.subs(i, i + si) + R).expand().is_zero ] if len(sol) != 1: return s = sol[0] if s < 0: return telescopic_direct(R, L, abs(s), (i, a, b)) elif s > 0: return telescopic_direct(L, R, s, (i, a, b))
def _entry(self, i, j, expand=True): coeff, matrices = self.as_coeff_matrices() if len(matrices) == 1: # situation like 2*X, matmul is just X return coeff * matrices[0][i, j] head, tail = matrices[0], matrices[1:] if len(tail) == 0: raise ValueError("lenth of tail cannot be 0") X = head Y = MatMul(*tail) from diofant.core.symbol import Dummy from diofant.concrete.summations import Sum from diofant.matrices import ImmutableMatrix k = Dummy('k', integer=True) if X.has(ImmutableMatrix) or Y.has(ImmutableMatrix): return coeff * Add(*[X[i, k] * Y[k, j] for k in range(X.cols)]) result = Sum(coeff * X[i, k] * Y[k, j], (k, 0, X.cols - 1)) return result.doit() if expand else result
def circumference(self): """The circumference of the ellipse. Examples ======== >>> from diofant import Point, Ellipse >>> p1 = Point(0, 0) >>> e1 = Ellipse(p1, 3, 1) >>> e1.circumference 12*Integral(sqrt((-8*_x**2/9 + 1)/(-_x**2 + 1)), (_x, 0, 1)) """ from diofant import Integral if self.eccentricity == 1: return 2 * pi * self.hradius else: x = Dummy('x', extended_real=True) return 4 * self.major * Integral( sqrt((1 - (self.eccentricity * x)**2) / (1 - x**2)), (x, 0, 1))
def fdiff(self, argindex=3): from diofant import Sum if argindex == 1: # Diff wrt n raise ArgumentIndexError(self, argindex) elif argindex == 2: # Diff wrt a n, a, x = self.args k = Dummy("k") factor1 = 2 * (1 + (-1)**(n - k)) * (k + a) / ((k + n + 2*a) * (n - k)) factor2 = 2*(k + 1) / ((k + 2*a) * (2*k + 2*a + 1)) + \ 2 / (k + n + 2*a) kern = factor1*gegenbauer(k, a, x) + factor2*gegenbauer(n, a, x) return Sum(kern, (k, 0, n - 1)) elif argindex == 3: # Diff wrt x n, a, x = self.args return 2*a*gegenbauer(n - 1, a + 1, x) else: raise ArgumentIndexError(self, argindex)
def rsolve_ratio(coeffs, f, n, **hints): """ Given linear recurrence operator `\operatorname{L}` of order `k` with polynomial coefficients and inhomogeneous equation `\operatorname{L} y = f`, where `f` is a polynomial, we seek for all rational solutions over field `K` of characteristic zero. This procedure accepts only polynomials, however if you are interested in solving recurrence with rational coefficients then use ``rsolve`` which will pre-process the given equation and run this procedure with polynomial arguments. The algorithm performs two basic steps: (1) Compute polynomial `v(n)` which can be used as universal denominator of any rational solution of equation `\operatorname{L} y = f`. (2) Construct new linear difference equation by substitution `y(n) = u(n)/v(n)` and solve it for `u(n)` finding all its polynomial solutions. Return ``None`` if none were found. Algorithm implemented here is a revised version of the original Abramov's algorithm, developed in 1989. The new approach is much simpler to implement and has better overall efficiency. This method can be easily adapted to q-difference equations case. Besides finding rational solutions alone, this functions is an important part of Hyper algorithm were it is used to find particular solution of inhomogeneous part of a recurrence. Examples ======== >>> from diofant.abc import x >>> from diofant.solvers.recurr import rsolve_ratio >>> rsolve_ratio([-2*x**3 + x**2 + 2*x - 1, 2*x**3 + x**2 - 6*x, ... - 2*x**3 - 11*x**2 - 18*x - 9, 2*x**3 + 13*x**2 + 22*x + 8], 0, x) C2*(2*x - 3)/(2*(x**2 - 1)) References ========== .. [1] S. A. Abramov, Rational solutions of linear difference and q-difference equations with polynomial coefficients, in: T. Levelt, ed., Proc. ISSAC '95, ACM Press, New York, 1995, 285-289 See Also ======== rsolve_hyper """ f = sympify(f) if not f.is_polynomial(n): return coeffs = list(map(sympify, coeffs)) r = len(coeffs) - 1 A, B = coeffs[r], coeffs[0] A = A.subs(n, n - r).expand() h = Dummy('h') res = resultant(A, B.subs(n, n + h), n) if not res.is_polynomial(h): p, q = res.as_numer_denom() res = quo(p, q, h) nni_roots = list( roots(res, h, filter='Z', predicate=lambda r: r >= 0).keys()) if not nni_roots: return rsolve_poly(coeffs, f, n, **hints) else: C, numers = S.One, [S.Zero] * (r + 1) for i in range(int(max(nni_roots)), -1, -1): d = gcd(A, B.subs(n, n + i), n) A = quo(A, d, n) B = quo(B, d.subs(n, n - i), n) C *= Mul(*[d.subs(n, n - j) for j in range(0, i + 1)]) denoms = [C.subs(n, n + i) for i in range(0, r + 1)] for i in range(0, r + 1): g = gcd(coeffs[i], denoms[i], n) numers[i] = quo(coeffs[i], g, n) denoms[i] = quo(denoms[i], g, n) for i in range(0, r + 1): numers[i] *= Mul(*(denoms[:i] + denoms[i + 1:])) result = rsolve_poly(numers, f * Mul(*denoms), n, **hints) if result is not None: if hints.get('symbols', False): return simplify(result[0] / C), result[1] else: return simplify(result / C) else: return
def rsolve_hyper(coeffs, f, n, **hints): """ 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 diofant.solvers import rsolve_hyper >>> from diofant.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. Petkovšek, Hypergeometric solutions of linear recurrences with polynomial coefficients, J. Symbolic Computation, 14 (1992), 243-264. .. [2] M. Petkovšek, 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 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 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(0, r + 1): polys[j] *= Mul(*(denoms[:j] + denoms[j + 1:])) R = rsolve_ratio(polys, Mul(*denoms), n, symbols=True) if R is not None: R, syms = R if syms: R = R.subs(zip(syms, [0] * len(syms))) if R: inhomogeneous[i] *= R else: return result = Add(*inhomogeneous) result = simplify(result) else: result = S.Zero Z = Dummy('Z') p, q = coeffs[0], coeffs[r].subs(n, n - r + 1) p_factors = [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(0, r + 1): a = Mul(*[A.subs(n, n + j) for j in range(0, 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()) d, poly = max(degrees), S.Zero for i in range(0, 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) skip = max([-1] + [ v for v in roots(Mul(*ratio.as_numer_denom()), n).keys() if v.is_Integer ]) + 1 K = product(ratio, (n, skip, n - 1)) if K.has(factorial, FallingFactorial, RisingFactorial): K = simplify(K) if casoratian(kernel + [K], n, zero=False) != 0: kernel.append(K) kernel.sort(key=default_sort_key) sk = list(zip(numbered_symbols('C'), kernel)) for C, ker in sk: result += C * ker if hints.get('symbols', False): symbols |= {s for s, k in sk} return result, list(symbols) else: return result
def test_print_Dummy(): d = Dummy('d') p = setup_test_printer() assert p._print_Dummy(d) == "d_%i" % d.dummy_index
def doit(self, **hints): """Evaluates limit. Notes ===== First we handle some trivial cases (i.e. constant), then try Gruntz algorithm (see the :py:mod:`~diofant.series.gruntz` module). """ e, z, z0, dir = self.args if hints.get('deep', True): e = e.doit(**hints) z = z.doit(**hints) z0 = z0.doit(**hints) if str(dir) == 'real': right = limit(e, z, z0, "+") left = limit(e, z, z0, "-") if not (left - right).equals(0): raise PoleError("left and right limits for expression %s at " "point %s=%s seems to be not equal" % (e, z, z0)) else: return right use_heuristics = hints.get('heuristics', True) has_Floats = e.has(Float) if has_Floats: e = e.subs({k: Rational(k) for k in e.atoms(Float)}, simultaneous=True) if z0.has(z): newz = z.as_dummy() r = limit(e.subs(z, newz), newz, z0, dir) if r.func is Limit: r = r.subs(newz, z) return r if e == z: return z0 if not e.has(z): return e if z0 is S.NaN: return S.NaN if e.has(Order): e = e.expand() order = e.getO() if order: if (z, z0) in zip(order.variables, order.point): order = limit(order.expr, z, z0, dir) e = e.removeO() + order try: # Convert to the limit z->oo and use Gruntz algorithm. newe, newz = e, z if z0 == S.NegativeInfinity: newe = e.subs(z, -z) elif z0 != S.Infinity: if str(dir) == "+": newe = e.subs(z, z0 + 1 / z) else: newe = e.subs(z, z0 - 1 / z) if not z.is_positive or not z.is_finite: # We need a fresh variable here to simplify expression further. newz = Dummy(z.name, positive=True, finite=True) newe = newe.subs(z, newz) r = limitinf(newe, newz) except (PoleError, ValueError, NotImplementedError): r = None if use_heuristics: r = heuristics(e, z, z0, dir) if r is None: return self if has_Floats: r = r.evalf() return r
def ufuncify(args, expr, language=None, backend='numpy', tempdir=None, flags=None, verbose=False, helpers=None): """ Generates a binary function that supports broadcasting on numpy arrays. Parameters ---------- args : iterable Either a Symbol or an iterable of symbols. Specifies the argument sequence for the function. expr A Diofant expression that defines the element wise operation. language : string, optional If supplied, (options: 'C' or 'F95'), specifies the language of the generated code. If ``None`` [default], the language is inferred based upon the specified backend. backend : string, optional Backend used to wrap the generated code. Either 'numpy' [default], 'cython', or 'f2py'. tempdir : string, optional Path to directory for temporary files. If this argument is supplied, the generated code and the wrapper input files are left intact in the specified path. flags : iterable, optional Additional option flags that will be passed to the backend verbose : bool, optional If True, autowrap will not mute the command line backends. This can be helpful for debugging. helpers : iterable, optional Used to define auxillary expressions needed for the main expr. If the main expression needs to call a specialized function it should be put in the ``helpers`` iterable. Autowrap will then make sure that the compiled main expression can link to the helper routine. Items should be tuples with (<funtion_name>, <diofant_expression>, <arguments>). It is mandatory to supply an argument sequence to helper routines. Notes ----- The default backend ('numpy') will create actual instances of ``numpy.ufunc``. These support ndimensional broadcasting, and implicit type conversion. Use of the other backends will result in a "ufunc-like" function, which requires equal length 1-dimensional arrays for all arguments, and will not perform any type conversions. References ---------- .. [1] http://docs.scipy.org/doc/numpy/reference/ufuncs.html Examples -------- >>> from diofant.utilities.autowrap import ufuncify >>> from diofant.abc import x, y >>> import numpy as np >>> f = ufuncify((x, y), y + x**2) >>> type(f) is np.ufunc True >>> f([1, 2, 3], 2) array([ 3., 6., 11.]) >>> f(np.arange(5), 3) array([ 3., 4., 7., 12., 19.]) For the F2Py and Cython backends, inputs are required to be equal length 1-dimensional arrays. The F2Py backend will perform type conversion, but the Cython backend will error if the inputs are not of the expected type. >>> f_fortran = ufuncify((x, y), y + x**2, backend='F2Py') >>> f_fortran(1, 2) 3 >>> f_fortran(numpy.array([1, 2, 3]), numpy.array([1.0, 2.0, 3.0])) array([2., 6., 12.]) >>> f_cython = ufuncify((x, y), y + x**2, backend='Cython') >>> f_cython(1, 2) Traceback (most recent call last): ... TypeError: Argument '_x' has incorrect type (expected numpy.ndarray, got int) >>> f_cython(numpy.array([1.0]), numpy.array([2.0])) array([ 3.]) """ if isinstance(args, (Dummy, Symbol)): args = (args, ) else: args = tuple(args) if language: _validate_backend_language(backend, language) else: language = _infer_language(backend) helpers = helpers if helpers else () flags = flags if flags else () if backend.upper() == 'NUMPY': routine = make_routine('autofunc', expr, args) helps = [] for name, expr, args in helpers: helps.append(make_routine(name, expr, args)) code_wrapper = UfuncifyCodeWrapper(CCodeGen("ufuncify"), tempdir, flags, verbose) return code_wrapper.wrap_code(routine, helpers=helps) else: # Dummies are used for all added expressions to prevent name clashes # within the original expression. y = IndexedBase(Dummy()) m = Dummy(integer=True) i = Idx(Dummy(integer=True), m) f = implemented_function(Dummy().name, Lambda(args, expr)) # For each of the args create an indexed version. indexed_args = [IndexedBase(Dummy(str(a))) for a in args] # Order the arguments (out, args, dim) args = [y] + indexed_args + [m] args_with_indices = [a[i] for a in indexed_args] return autowrap(Eq(y[i], f(*args_with_indices)), language, backend, tempdir, tuple(args), flags, verbose, helpers)
def normal_lines(self, p, prec=None): """Normal lines between `p` and the ellipse. Parameters ========== p : Point Returns ======= normal_lines : list with 1, 2 or 4 Lines Examples ======== >>> from diofant import Line, Point, Ellipse >>> e = Ellipse((0, 0), 2, 3) >>> c = e.center >>> e.normal_lines(c + Point(1, 0)) [Line(Point2D(0, 0), Point2D(1, 0))] >>> e.normal_lines(c) [Line(Point2D(0, 0), Point2D(0, 1)), Line(Point2D(0, 0), Point2D(1, 0))] Off-axis points require the solution of a quartic equation. This often leads to very large expressions that may be of little practical use. An approximate solution of `prec` digits can be obtained by passing in the desired value: >>> e.normal_lines((3, 3), prec=2) [Line(Point2D(-38/47, -85/31), Point2D(9/47, -21/17)), Line(Point2D(19/13, -43/21), Point2D(32/13, -8/3))] Whereas the above solution has an operation count of 12, the exact solution has an operation count of 2020. """ p = Point(p) # XXX change True to something like self.angle == 0 if the arbitrarily # rotated ellipse is introduced. # https://github.com/sympy/sympy/issues/2815) if True: rv = [] if p.x == self.center.x: rv.append(Line(self.center, slope=oo)) if p.y == self.center.y: rv.append(Line(self.center, slope=0)) if rv: # at these special orientations of p either 1 or 2 normals # exist and we are done return rv # find the 4 normal points and construct lines through them with # the corresponding slope x, y = Dummy('x', extended_real=True), Dummy('y', extended_real=True) eq = self.equation(x, y) dydx = idiff(eq, y, x) norm = -1 / dydx slope = Line(p, (x, y)).slope seq = slope - norm yis = solve(seq, y)[0] xeq = eq.subs(y, yis).as_numer_denom()[0].expand() if len(xeq.free_symbols) == 1: try: # this is so much faster, it's worth a try xsol = Poly(xeq, x).real_roots() except (DomainError, PolynomialError, NotImplementedError): xsol = _nsort(solve(xeq, x), separated=True)[0] points = [Point(i, solve(eq.subs(x, i), y)[0]) for i in xsol] else: raise NotImplementedError( 'intersections for the general ellipse are not supported') slopes = [norm.subs(zip((x, y), pt.args)) for pt in points] if prec is not None: points = [pt.n(prec) for pt in points] slopes = [i if _not_a_coeff(i) else i.n(prec) for i in slopes] return [Line(pt, slope=s) for pt, s in zip(points, slopes)]
def euler_maclaurin(self, m=0, n=0, eps=0, eval_integral=True): """ Return an Euler-Maclaurin approximation of self, where m is the number of leading terms to sum directly and n is the number of terms in the tail. With m = n = 0, this is simply the corresponding integral plus a first-order endpoint correction. Returns (s, e) where s is the Euler-Maclaurin approximation and e is the estimated error (taken to be the magnitude of the first omitted term in the tail): >>> from diofant.abc import k, a, b >>> Sum(1/k, (k, 2, 5)).doit().evalf() 1.28333333333333 >>> s, e = Sum(1/k, (k, 2, 5)).euler_maclaurin() >>> s -log(2) + 7/20 + log(5) >>> from diofant import sstr >>> print(sstr((s.evalf(), e.evalf()), full_prec=True)) (1.26629073187415, 0.0175000000000000) The endpoints may be symbolic: >>> s, e = Sum(1/k, (k, a, b)).euler_maclaurin() >>> s -log(a) + log(b) + 1/(2*b) + 1/(2*a) >>> e Abs(1/(12*b**2) - 1/(12*a**2)) If the function is a polynomial of degree at most 2n+1, the Euler-Maclaurin formula becomes exact (and e = 0 is returned): >>> Sum(k, (k, 2, b)).euler_maclaurin() (b**2/2 + b/2 - 1, 0) >>> Sum(k, (k, 2, b)).doit() b**2/2 + b/2 - 1 With a nonzero eps specified, the summation is ended as soon as the remainder term is less than the epsilon. """ from diofant.functions import bernoulli, factorial from diofant.integrals import Integral m = int(m) n = int(n) f = self.function if len(self.limits) != 1: raise ValueError("More than 1 limit") i, a, b = self.limits[0] if (a > b) is S.true: if a - b == 1: return S.Zero, S.Zero a, b = b + 1, a - 1 f = -f s = S.Zero if m: if b.is_Integer and a.is_Integer: m = min(m, b - a + 1) if not eps or f.is_polynomial(i): for k in range(m): s += f.subs(i, a + k) else: term = f.subs(i, a) if term: test = abs(term.evalf(3)) < eps if test == S.true: return s, abs(term) elif not (test == S.false): # a symbolic Relational class, can't go further return term, S.Zero s += term for k in range(1, m): term = f.subs(i, a + k) if abs(term.evalf(3)) < eps and term != 0: return s, abs(term) s += term if b - a + 1 == m: return s, S.Zero a += m x = Dummy('x') I = Integral(f.subs(i, x), (x, a, b)) if eval_integral: I = I.doit() s += I def fpoint(expr): if b is S.Infinity: return expr.subs(i, a), 0 return expr.subs(i, a), expr.subs(i, b) fa, fb = fpoint(f) iterm = (fa + fb) / 2 g = f.diff(i) for k in range(1, n + 2): ga, gb = fpoint(g) term = bernoulli(2 * k) / factorial(2 * k) * (gb - ga) if (eps and term and abs(term.evalf(3)) < eps) or (k > n): break s += term g = g.diff(i, 2, simplify=False) return s + iterm, abs(term)
def rsolve_poly(coeffs, f, n, **hints): """ 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 Petkovšek. 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 diofant 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. Petkovšek, On polynomial solutions of linear operator equations, in: T. Levelt, ed., Proc. ISSAC '95, ACM Press, New York, 1995, 290-296. .. [2] M. Petkovšek, Hypergeometric solutions of linear recurrences with polynomial coefficients, J. Symbolic Computation, 14 (1992), 243-264. .. [3] M. Petkovšek, H. S. Wilf, D. Zeilberger, A = B, 1996. """ f = sympify(f) if not f.is_polynomial(n): return homogeneous = f.is_zero r = len(coeffs) - 1 coeffs = [Poly(coeff, n) for coeff in coeffs] g = gcd_list(coeffs + [f], n, polys=True) if not g.is_ground: coeffs = [quo(c, g, n, polys=False) for c in coeffs] f = quo(f, g, n, polys=False) polys = [Poly(0, n)] * (r + 1) terms = [(S.Zero, S.NegativeInfinity)] * (r + 1) for i in range(0, 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(0, 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 if N <= r: C = [] y = E = S.Zero for i in range(0, N + 1): C.append(Symbol('C' + str(i))) y += C[i] * n**i for i in range(0, 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 # 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(0, A + 1): for k in range(0, 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(0, A): v[j] += B * V[i - k, j] denom = alpha[-A].subs(x, i) for j in range(0, 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(0, A): v[j] += B * V[i - k, j] g += B * G[i - k] denom = alpha[-A].subs(x, i) for j in range(0, 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(0, 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(0, A)] def g(i): return 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 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 _eval_derivative(self, sym): """Evaluate the derivative of the current Integral object by differentiating under the integral sign [1], using the Fundamental Theorem of Calculus [2] when possible. Whenever an Integral is encountered that is equivalent to zero or has an integrand that is independent of the variable of integration those integrals are performed. All others are returned as Integral instances which can be resolved with doit() (provided they are integrable). References ========== .. [1] http://en.wikipedia.org/wiki/Differentiation_under_the_integral_sign .. [2] http://en.wikipedia.org/wiki/Fundamental_theorem_of_calculus Examples ======== >>> from diofant import Integral >>> from diofant.abc import x, y >>> i = Integral(x + y, y, (y, 1, x)) >>> i.diff(x) Integral(x + y, (y, x)) + Integral(1, y, (y, 1, x)) >>> i.doit().diff(x) == i.diff(x).doit() True >>> i.diff(y) 0 The previous must be true since there is no y in the evaluated integral: >>> i.free_symbols == {x} True >>> i.doit() 2*x**3/3 - x/2 - 1/6 """ # differentiate under the integral sign; we do not # check for regularity conditions (TODO), see issue 4215 # get limits and the function f, limits = self.function, list(self.limits) # the order matters if variables of integration appear in the limits # so work our way in from the outside to the inside. limit = limits.pop(-1) if len(limit) == 3: x, a, b = limit elif len(limit) == 2: x, b = limit a = None else: a = b = None x = limit[0] if limits: # f is the argument to an integral f = self.func(f, *tuple(limits)) # assemble the pieces def _do(f, ab): dab_dsym = diff(ab, sym) if not dab_dsym: return S.Zero if isinstance(f, Integral): limits = [(x, x) if (len(l) == 1 and l[0] == x) else l for l in f.limits] f = self.func(f.function, *limits) return f.subs(x, ab) * dab_dsym rv = 0 if b is not None: rv += _do(f, b) if a is not None: rv -= _do(f, a) if len(limit) == 1 and sym == x: # the dummy variable *is* also the real-world variable arg = f rv += arg else: # the dummy variable might match sym but it's # only a dummy and the actual variable is determined # by the limits, so mask off the variable of integration # while differentiating u = Dummy('u') arg = f.subs(x, u).diff(sym).subs(u, x) rv += self.func(arg, Tuple(x, a, b)) return rv
def transform(self, x, u): r""" Performs a change of variables from `x` to `u` using the relationship given by `x` and `u` which will define the transformations `f` and `F` (which are inverses of each other) as follows: 1) If `x` is a Symbol (which is a variable of integration) then `u` will be interpreted as some function, f(u), with inverse F(u). This, in effect, just makes the substitution of x with f(x). 2) If `u` is a Symbol then `x` will be interpreted as some function, F(x), with inverse f(u). This is commonly referred to as u-substitution. Once f and F have been identified, the transformation is made as follows: .. math:: \int_a^b x \mathrm{d}x \rightarrow \int_{F(a)}^{F(b)} f(x) \frac{\mathrm{d}}{\mathrm{d}x} where `F(x)` is the inverse of `f(x)` and the limits and integrand have been corrected so as to retain the same value after integration. Notes ===== The mappings, F(x) or f(u), must lead to a unique integral. Linear or rational linear expression, `2*x`, `1/x` and `sqrt(x)`, will always work; quadratic expressions like `x**2 - 1` are acceptable as long as the resulting integrand does not depend on the sign of the solutions (see examples). The integral will be returned unchanged if `x` is not a variable of integration. `x` must be (or contain) only one of of the integration variables. If `u` has more than one free symbol then it should be sent as a tuple (`u`, `uvar`) where `uvar` identifies which variable is replacing the integration variable. XXX can it contain another integration variable? Examples ======== >>> from diofant.abc import a, b, c, d, x, u, y >>> from diofant import Integral, S, cos, sqrt >>> i = Integral(x*cos(x**2 - 1), (x, 0, 1)) transform can change the variable of integration >>> i.transform(x, u) Integral(u*cos(u**2 - 1), (u, 0, 1)) transform can perform u-substitution as long as a unique integrand is obtained: >>> i.transform(x**2 - 1, u) Integral(cos(u)/2, (u, -1, 0)) This attempt fails because x = +/-sqrt(u + 1) and the sign does not cancel out of the integrand: >>> Integral(cos(x**2 - 1), (x, 0, 1)).transform(x**2 - 1, u) Traceback (most recent call last): ... ValueError: The mapping between F(x) and f(u) did not give a unique integrand. transform can do a substitution. Here, the previous result is transformed back into the original expression using "u-substitution": >>> ui = _ >>> _.transform(sqrt(u + 1), x) == i True We can accomplish the same with a regular substitution: >>> ui.transform(u, x**2 - 1) == i True If the `x` does not contain a symbol of integration then the integral will be returned unchanged. Integral `i` does not have an integration variable `a` so no change is made: >>> i.transform(a, x) == i True When `u` has more than one free symbol the symbol that is replacing `x` must be identified by passing `u` as a tuple: >>> Integral(x, (x, 0, 1)).transform(x, (u + a, u)) Integral(a + u, (u, -a, -a + 1)) >>> Integral(x, (x, 0, 1)).transform(x, (u + a, a)) Integral(a + u, (a, -u, -u + 1)) See Also ======== diofant.concrete.expr_with_limits.ExprWithLimits.variables : Lists the integration variables diofant.concrete.expr_with_limits.ExprWithLimits.as_dummy : Replace integration variables with dummy ones """ from diofant.solvers.solvers import solve, posify d = Dummy('d') xfree = x.free_symbols.intersection(self.variables) if len(xfree) > 1: raise ValueError('F(x) can only contain one of: %s' % self.variables) xvar = xfree.pop() if xfree else d if xvar not in self.variables: return self u = sympify(u) if isinstance(u, Expr): ufree = u.free_symbols if len(ufree) != 1: raise ValueError( filldedent(''' When f(u) has more than one free symbol, the one replacing x must be identified: pass f(u) as (f(u), u)''')) uvar = ufree.pop() else: u, uvar = u if uvar not in u.free_symbols: raise ValueError( filldedent(''' Expecting a tuple (expr, symbol) where symbol identified a free symbol in expr, but symbol is not in expr's free symbols.''')) if not isinstance(uvar, (Dummy, Symbol)): raise ValueError( filldedent(''' Expecting a tuple (expr, symbol) but didn't get a symbol; got %s''' % uvar)) if x.is_Symbol and u.is_Symbol: return self.xreplace({x: u}) if not x.is_Symbol and not u.is_Symbol: raise ValueError('either x or u must be a symbol') if uvar == xvar: return self.transform(x, (u.subs(uvar, d), d)).xreplace({d: uvar}) if uvar in self.limits: raise ValueError( filldedent(''' u must contain the same variable as in x or a variable that is not already an integration variable''')) if not x.is_Symbol: F = [x.subs(xvar, d)] soln = solve(u - x, xvar, check=False) if not soln: raise ValueError('no solution for solve(F(x) - f(u), x)') f = [fi.subs(uvar, d) for fi in soln] else: f = [u.subs(uvar, d)] pdiff, reps = posify(u - x) puvar = uvar.subs([(v, k) for k, v in reps.items()]) soln = [s.subs(reps) for s in solve(pdiff, puvar)] if not soln: raise ValueError('no solution for solve(F(x) - f(u), u)') F = [fi.subs(xvar, d) for fi in soln] newfuncs = {(self.function.subs(xvar, fi) * fi.diff(d)).subs(d, uvar) for fi in f} if len(newfuncs) > 1: raise ValueError( filldedent(''' The mapping between F(x) and f(u) did not give a unique integrand.''')) newfunc = newfuncs.pop() def _calc_limit_1(F, a, b): """ replace d with a, using subs if possible, otherwise limit where sign of b is considered """ wok = F.subs(d, a) if wok is S.NaN or wok.is_finite is False and a.is_finite: return limit(sign(b) * F, d, a) return wok def _calc_limit(a, b): """ replace d with a, using subs if possible, otherwise limit where sign of b is considered """ avals = list({_calc_limit_1(Fi, a, b) for Fi in F}) if len(avals) > 1: raise ValueError( filldedent(''' The mapping between F(x) and f(u) did not give a unique limit.''')) return avals[0] newlimits = [] for xab in self.limits: sym = xab[0] if sym == xvar: if len(xab) == 3: a, b = xab[1:] a, b = _calc_limit(a, b), _calc_limit(b, a) if a - b > 0: a, b = b, a newfunc = -newfunc newlimits.append((uvar, a, b)) elif len(xab) == 2: a = _calc_limit(xab[1], 1) newlimits.append((uvar, a)) else: newlimits.append(uvar) else: newlimits.append(xab) return self.func(newfunc, *newlimits)
def tangent_lines(self, p): """Tangent lines between `p` and the ellipse. If `p` is on the ellipse, returns the tangent line through point `p`. Otherwise, returns the tangent line(s) from `p` to the ellipse, or None if no tangent line is possible (e.g., `p` inside ellipse). Parameters ========== p : Point Returns ======= tangent_lines : list with 1 or 2 Lines Raises ====== NotImplementedError Can only find tangent lines for a point, `p`, on the ellipse. See Also ======== diofant.geometry.point.Point, diofant.geometry.line.Line Examples ======== >>> from diofant import Point, Ellipse >>> e1 = Ellipse(Point(0, 0), 3, 2) >>> e1.tangent_lines(Point(3, 0)) [Line(Point2D(3, 0), Point2D(3, -12))] """ p = Point(p) if self.encloses_point(p): return [] if p in self: delta = self.center - p rise = (self.vradius**2) * delta.x run = -(self.hradius**2) * delta.y p2 = Point(simplify(p.x + run), simplify(p.y + rise)) return [Line(p, p2)] else: if len(self.foci) == 2: f1, f2 = self.foci maj = self.hradius test = (2 * maj - Point.distance(f1, p) - Point.distance(f2, p)) else: test = self.radius - Point.distance(self.center, p) if test.is_number and test.is_positive: return [] # else p is outside the ellipse or we can't tell. In case of the # latter, the solutions returned will only be valid if # the point is not inside the ellipse; if it is, nan will result. x, y = Dummy('x'), Dummy('y') eq = self.equation(x, y) dydx = idiff(eq, y, x) slope = Line(p, Point(x, y)).slope tangent_points = solve([slope - dydx, eq], [x, y]) # handle horizontal and vertical tangent lines if len(tangent_points) == 1: assert tangent_points[0][0] == p.x or tangent_points[0][ 1] == p.y return [Line(p, p + Point(1, 0)), Line(p, p + Point(0, 1))] # others return [Line(p, tangent_points[0]), Line(p, tangent_points[1])]
def roots(f, *gens, **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.) Examples ======== >>> from diofant import Poly, roots, sqrt >>> from diofant.abc import x, y >>> roots(x**2 - 1, x) == {-1: 1, 1: 1} True >>> p = Poly(x**2-1, x) >>> roots(p) == {-1: 1, 1: 1} True >>> p = Poly(x**2-y, x, y) >>> roots(Poly(p, x)) == {-sqrt(y): 1, sqrt(y): 1} True >>> roots(x**2 - y, x) == {-sqrt(y): 1, sqrt(y): 1} True >>> roots([1, 0, -1]) == {-1: 1, 1: 1} True References ========== .. [1] http://en.wikipedia.org/wiki/Cubic_function#Trigonometric_.28and_hyperbolic.29_method """ from diofant.polys.polytools import to_rational_coeffs flags = dict(flags) auto = flags.pop('auto', True) cubics = flags.pop('cubics', True) trig = flags.pop('trig', False) quartics = flags.pop('quartics', True) quintics = flags.pop('quintics', False) multiple = flags.pop('multiple', False) filter = flags.pop('filter', None) predicate = flags.pop('predicate', None) 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 f.length == 2 and f.degree() != 1: # check for foo**n factors in the constant n = f.degree() npow_bases = [] expr = f.as_expr() con = expr.as_independent(*gens)[0] for p in Mul.make_args(con): if p.is_Pow and not p.exp % n: npow_bases.append(p.base**(p.exp/n)) else: other.append(p) if npow_bases: b = Mul(*npow_bases) B = Dummy() d = roots(Poly(expr - con + B**n*Mul(*others), *gens, **flags), *gens, **flags) rv = {} for k, v in d.items(): rv[k.subs(B, b)] = v return rv except GeneratorsNeeded: if multiple: return [] else: return {} if f.is_multivariate: raise PolynomialError('multivariate polynomials are not supported') def _update_dict(result, root, k): if root in result: result[root] += k else: result[root] = k def _try_decompose(f): """Find roots using functional decomposition. """ factors, roots = f.decompose(), [] for root in _try_heuristics(factors[0]): roots.append(root) for factor in factors[1:]: previous, roots = list(roots), [] for root in previous: g = factor - Poly(root, f.gen) for root in _try_heuristics(g): roots.append(root) return roots def _try_heuristics(f): """Find roots using formulas and some tricks. """ if f.is_ground: return [] if f.is_monomial: return [Integer(0)]*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 (k,), f = f.terms_gcd() if not k: zeros = {} else: zeros = {Integer(0): k} coeff, f = preprocess_roots(f) if auto and f.get_domain().has_Ring: f = f.to_field() rescale_x = None translate_x = None result = {} if not f.is_ground: if not f.get_domain().is_Exact: for r in f.nroots(): _update_dict(result, r, 1) elif f.degree() == 1: result[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, 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, 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 root in _try_decompose(f): _update_dict(result, root, 1) else: for root in _try_decompose(f): _update_dict(result, root, 1) else: for factor, k in factors: for r in _try_heuristics(Poly(factor, f.gen, field=True)): _update_dict(result, r, k) if coeff is not S.One: _result, result, = result, {} for root, k in _result.items(): result[coeff*root] = k result.update(zeros) if filter not in [None, 'C']: handlers = { 'Z': lambda r: r.is_Integer, 'Q': lambda r: r.is_Rational, 'R': lambda r: r.is_extended_real, '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 if not multiple: return result else: zeros = [] for zero in ordered(result): zeros.extend([zero]*result[zero]) return zeros
from diofant.functions.elementary.miscellaneous import sqrt from diofant.functions.elementary.trigonometric import cos from diofant.functions.special.gamma_functions import gamma from diofant.functions.special.hyper import hyper from diofant.polys.orthopolys import ( jacobi_poly, gegenbauer_poly, chebyshevt_poly, chebyshevu_poly, laguerre_poly, hermite_poly, legendre_poly ) _x = Dummy('dummy_for_special_polynomials') class OrthogonalPolynomial(Function): """Base class for orthogonal polynomials. """ @classmethod def _eval_at_order(cls, n, x): if n.is_integer and n >= 0: return cls._ortho_poly(int(n), _x).subs(_x, x) def _eval_conjugate(self): return self.func(self.args[0], self.args[1].conjugate()) ############################################################################
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 diofant.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/2 + sqrt(5)/2 >>> from diofant.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 diofant.core.symbol import Dummy from diofant.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] return a.subs(x, pure).radsimp() else: return a