Ejemplo n.º 1
0
def test_exclude():
    x, y, a = map(Symbol, 'xya')
    p = Wild('p', exclude=[1, x])
    q = Wild('q')
    r = Wild('r', exclude=[sin, y])

    assert sin(x).match(r) is None
    assert cos(y).match(r) is None

    e = 3*x**2 + y*x + a
    assert e.match(p*x**2 + q*x + r) == {p: 3, q: y, r: a}

    e = x + 1
    assert e.match(x + p) is None
    assert e.match(p + 1) is None
    assert e.match(x + 1 + p) == {p: 0}

    e = cos(x) + 5*sin(y)
    assert e.match(r) is None
    assert e.match(cos(y) + r) is None
    assert e.match(r + p*sin(q)) == {r: cos(x), p: 5, q: y}
Ejemplo n.º 2
0
    def __new__(cls, recurrence, yn, n, initial=None, start=0):
        if not isinstance(yn, AppliedUndef):
            raise TypeError(
                "recurrence sequence must be an applied undefined function"
                ", found `{}`".format(yn))

        if not isinstance(n, Basic) or not n.is_symbol:
            raise TypeError("recurrence variable must be a symbol"
                            ", found `{}`".format(n))

        if yn.args != (n, ):
            raise TypeError("recurrence sequence does not match symbol")

        y = yn.func

        k = Wild("k", exclude=(n, ))
        degree = 0

        # Find all applications of y in the recurrence and check that:
        #   1. The function y is only being used with a single argument; and
        #   2. All arguments are n + k for constant negative integers k.

        prev_ys = recurrence.find(y)
        for prev_y in prev_ys:
            if len(prev_y.args) != 1:
                raise TypeError("Recurrence should be in a single variable")

            shift = prev_y.args[0].match(n + k)[k]
            if not (shift.is_constant() and shift.is_integer and shift < 0):
                raise TypeError("Recurrence should have constant,"
                                " negative, integer shifts"
                                " (found {})".format(prev_y))

            if -shift > degree:
                degree = -shift

        if not initial:
            initial = [Dummy("c_{}".format(k)) for k in range(degree)]

        if len(initial) != degree:
            raise ValueError("Number of initial terms must equal degree")

        degree = Integer(degree)
        start = sympify(start)

        initial = Tuple(*(sympify(x) for x in initial))

        seq = Basic.__new__(cls, recurrence, yn, n, initial, start)

        seq.cache = {y(start + k): init for k, init in enumerate(initial)}
        seq.degree = degree

        return seq
Ejemplo n.º 3
0
def match_2nd_hypergeometric(eq, func):
    x = func.args[0]
    df = func.diff(x)
    a3 = Wild('a3', exclude=[func, func.diff(x), func.diff(x, 2)])
    b3 = Wild('b3', exclude=[func, func.diff(x), func.diff(x, 2)])
    c3 = Wild('c3', exclude=[func, func.diff(x), func.diff(x, 2)])
    deq = a3*(func.diff(x, 2)) + b3*df + c3*func
    r = collect(eq,
        [func.diff(x, 2), func.diff(x), func]).match(deq)
    if r:
        if not all([r[key].is_polynomial() for key in r]):
            n, d = eq.as_numer_denom()
            eq = expand(n)
            r = collect(eq, [func.diff(x, 2), func.diff(x), func]).match(deq)

    if r and r[a3]!=0:
        A = cancel(r[b3]/r[a3])
        B = cancel(r[c3]/r[a3])
        return [A, B]
    else:
        return []
Ejemplo n.º 4
0
 def _equation(self, fx, x, order):
     P, Q = self.wilds()
     df = fx.diff(x)
     eq = self.ode_problem.eq_expanded
     c = Wild('c', exclude=[fx])
     d = Wild('d', exclude=[df, fx.diff(x, 2)])
     e = Wild('e', exclude=[df])
     eq = expand(eq)
     eq = eq.collect(fx)
     r = eq.match(e * df + d)
     self.fxx = None
     self.gx = None
     self.kx = None
     if r:
         r2 = r.copy()
         r2[c] = S.Zero
         if r2[d].is_Add:
             # Separate the terms having f(x) to r[d] and
             # remaining to r[c]
             no_f, r2[d] = r2[d].as_independent(fx)
             r2[c] += no_f
         factor = simplify(r2[d].diff(fx) / r[e])
         if factor and not factor.has(fx):
             r2[d] = factor_terms(r2[d])
             u = r2[d].as_independent(fx, as_Add=False)[1]
             r2.update({'a': e, 'b': d, 'c': c, 'u': u})
             r2[d] /= u
             r2[e] /= u.diff(fx)
             self.coeffs = r2
             du = u.diff(x)
             temp = du + (r2[r2['b']] /
                          r2[r2['a']]) * u - r2[r2['c']] / r2[r2['a']]
             temp = expand(temp / temp.coeff(fx.diff(x)))
             self.fxx = r2[e]
             self.kx = r2[d]
             self.gx = -r2[c]
             self.substituting = u
             return fx.diff(x) + P * fx - Q
     return S.Zero
Ejemplo n.º 5
0
def test_match_deriv_bug1():
    n = Function('n')
    l = Function('l')

    x = Symbol('x')
    p = Wild('p')

    e = diff(l(x), x)/x - diff(diff(n(x), x), x)/2 - \
        diff(n(x), x)**2/4 + diff(n(x), x)*diff(l(x), x)/4
    e = e.subs(n(x), -l(x)).doit()
    t = x*exp(-l(x))
    t2 = t.diff(x, x)/t
    assert e.match( (p*t2).expand() ) == {p: Rational(-1, 2)}
Ejemplo n.º 6
0
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("_issue_3130", commutative=False),
            Wild,
            Wild("x"),
    ):
        check(c)
Ejemplo n.º 7
0
    def _compare_pretty(a, b):
        from sympy.series.order import Order
        if isinstance(a, Order) and not isinstance(b, Order):
            return 1
        if not isinstance(a, Order) and isinstance(b, Order):
            return -1

        if a.is_Rational and b.is_Rational:
            return cmp(a.p * b.q, b.p * a.q)
        else:
            from sympy.core.symbol import Wild
            p1, p2, p3 = Wild("p1"), Wild("p2"), Wild("p3")
            r_a = a.match(p1 * p2**p3)
            if r_a and p3 in r_a:
                a3 = r_a[p3]
                r_b = b.match(p1 * p2**p3)
                if r_b and p3 in r_b:
                    b3 = r_b[p3]
                    c = Basic.compare(a3, b3)
                    if c != 0:
                        return c

        return Basic.compare(a, b)
Ejemplo n.º 8
0
def test_gate():
    """Test a basic gate."""
    h = HadamardGate(1)
    assert h.min_qubits == 2
    assert h.nqubits == 1

    i0 = Wild('i0')
    i1 = Wild('i1')
    h0_w1 = HadamardGate(i0)
    h0_w2 = HadamardGate(i0)
    h1_w1 = HadamardGate(i1)

    assert h0_w1 == h0_w2
    assert h0_w1 != h1_w1
    assert h1_w1 != h0_w2

    cnot_10_w1 = CNOT(i1, i0)
    cnot_10_w2 = CNOT(i1, i0)
    cnot_01_w1 = CNOT(i0, i1)

    assert cnot_10_w1 == cnot_10_w2
    assert cnot_10_w1 != cnot_01_w1
    assert cnot_10_w2 != cnot_01_w1
Ejemplo n.º 9
0
def hyper_re(DE, r, k):
    """Converts a DE into a RE.

    Performs the substitution:

    .. math::
        x^l f^j(x) \\to (k + 1 - l)_j . a_{k + j - l}

    Normalises the terms so that lowest order of a term is always r(k).

    Examples
    ========

    >>> from sympy import Function, Derivative
    >>> from sympy.series.formal import hyper_re
    >>> from sympy.abc import x, k
    >>> f, r = Function('f'), Function('r')

    >>> hyper_re(-f(x) + Derivative(f(x)), r, k)
    (k + 1)*r(k + 1) - r(k)
    >>> hyper_re(-x*f(x) + Derivative(f(x), (x, 2)), r, k)
    (k + 2)*(k + 3)*r(k + 3) - r(k)

    See Also
    ========

    sympy.series.formal.exp_re
    """
    RE = S.Zero

    g = DE.atoms(Function).pop()
    x = g.atoms(Symbol).pop()

    mini = None
    for t in Add.make_args(DE.expand()):
        coeff, d = t.as_independent(g)
        c, v = coeff.as_independent(x)
        l = v.as_coeff_exponent(x)[1]
        if isinstance(d, Derivative):
            j = d.derivative_count
        else:
            j = 0
        RE += c * rf(k + 1 - l, j) * r(k + j - l)
        if mini is None or j - l < mini:
            mini = j - l

    RE = RE.subs(k, k - mini)

    m = Wild('m')
    return RE.collect(r(k + m))
Ejemplo n.º 10
0
def solve_ODE_first_order(eq, f):
    """
    solves many kinds of first order odes, different methods are used
    depending on the form of the given equation. Now the linear
    and Bernoulli cases are implemented.
    """
    from sympy.integrals.integrals import integrate
    C1 = Symbol("C1")
    x = f.args[0]
    f = f.func

    #linear case: a(x)*f'(x)+b(x)*f(x)+c(x) = 0
    a = Wild('a', exclude=[f(x)])
    b = Wild('b', exclude=[f(x)])
    c = Wild('c', exclude=[f(x)])

    r = eq.match(a * diff(f(x), x) + b * f(x) + c)
    if r:
        t = exp(integrate(r[b] / r[a], x))
        tt = integrate(t * (-r[c] / r[a]), x)
        return (tt + C1) / t

    #Bernoulli case: a(x)*f'(x)+b(x)*f(x)+c(x)*f(x)^n = 0
    n = Wild('n', exclude=[f(x)])

    r = eq.match(a * diff(f(x), x) + b * f(x) + c * f(x)**n)
    if r:
        if r[n] == 1:
            return C1 * exp(integrate(-(r[b] + r[c]), x))
        # r[n] != 1 ie, the real bernoulli case
        t = exp((1 - r[n]) * integrate(r[b] / r[a], x))
        tt = (r[n] - 1) * integrate(t * r[c] / r[a], x)
        return ((tt + C1) / t)**(1 / (1 - r[n]))

    #other cases of first order odes will be implemented here

    raise NotImplementedError("solve_ODE_first_order: Cannot solve " + str(eq))
Ejemplo n.º 11
0
def solve_ODE_first_order(eq, f):
    """
    solves many kinds of first order odes, different methods are used
    depending on the form of the given equation. Now the linear
    case is implemented.
    """
    from sympy.integrals.integrals import integrate
    x = f.args[0]
    f = f.func

    #linear case: a(x)*f'(x)+b(x)*f(x)+c(x) = 0
    a = Wild('a', exclude=[f(x)])
    b = Wild('b', exclude=[f(x)])
    c = Wild('c', exclude=[f(x)])

    r = eq.match(a * diff(f(x), x) + b * f(x) + c)
    if r:
        t = C.exp(integrate(r[b] / r[a], x))
        tt = integrate(t * (-r[c] / r[a]), x)
        return (tt + Symbol("C1")) / t

    #other cases of first order odes will be implemented here

    raise NotImplementedError("solve_ODE_first_order: Cannot solve " + str(eq))
Ejemplo n.º 12
0
def test_match_wild_wild():
    p = Wild('p')
    q = Wild('q')
    r = Wild('r')

    assert p.match(q + r) in [ {q: p, r: 0}, {q: 0, r: p} ]
    assert p.match(q*r) in [ {q: p, r: 1}, {q: 1, r: p} ]

    p = Wild('p')
    q = Wild('q', exclude=[p])
    r = Wild('r')

    assert p.match(q + r) == {q: 0, r: p}
    assert p.match(q*r) == {q: 1, r: p}

    p = Wild('p')
    q = Wild('q', exclude=[p])
    r = Wild('r', exclude=[p])

    assert p.match(q + r) is None
    assert p.match(q*r) is None
Ejemplo n.º 13
0
def limitinf(e, x, leadsimp=False):
    """Limit e(x) for x-> oo.

    Explanation
    ===========

    If ``leadsimp`` is True, an attempt is made to simplify the leading
    term of the series expansion of ``e``. That may succeed even if
    ``e`` cannot be simplified.
    """
    # rewrite e in terms of tractable functions only

    if not e.has(x):
        return e  # e is a constant
    from sympy.simplify.powsimp import powdenest
    if e.has(Order):
        e = e.expand().removeO()
    if not x.is_positive or x.is_integer:
        # We make sure that x.is_positive is True and x.is_integer is None
        # so we get all the correct mathematical behavior from the expression.
        # We need a fresh variable.
        p = Dummy('p', positive=True)
        e = e.subs(x, p)
        x = p
    e = e.rewrite('tractable', deep=True, limitvar=x)
    e = powdenest(e)
    c0, e0 = mrv_leadterm(e, x)
    sig = sign(e0, x)
    if sig == 1:
        return S.Zero  # e0>0: lim f = 0
    elif sig == -1:  # e0<0: lim f = +-oo (the sign depends on the sign of c0)
        if c0.match(I * Wild("a", exclude=[I])):
            return c0 * oo
        s = sign(c0, x)
        # the leading term shouldn't be 0:
        if s == 0:
            raise ValueError("Leading term should not be 0")
        return s * oo
    elif sig == 0:
        if leadsimp:
            c0 = c0.simplify()
        return limitinf(c0, x, leadsimp)  # e0=0: lim f = lim c0
    else:
        raise ValueError("{} could not be evaluated".format(sig))
Ejemplo n.º 14
0
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 None

    # 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 None
        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 None
        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))
Ejemplo n.º 15
0
def deriv_degree(expr, func):
    """ get the order of a given ode, the function is implemented
    recursively """
    a = Wild('a', exclude=[func])

    order = 0
    if isinstance(expr, Derivative):
        order = len(expr.symbols)
    else:
        for arg in expr.args:
            if isinstance(arg, Derivative):
                order = max(order, len(arg.symbols))
            elif expr.match(a):
                order = 0
            else:
                for arg1 in arg.args:
                    order = max(order, deriv_degree(arg1, func))

    return order
Ejemplo n.º 16
0
def test_issue_3773():
    x = symbols('x')
    z, phi, r = symbols('z phi r')
    c, A, B, N = symbols('c A B N', cls=Wild)
    l = Wild('l', exclude=(0,))

    eq = z * sin(2*phi) * r**7
    matcher = c * sin(phi*N)**l * r**A * log(r)**B

    assert eq.match(matcher) == {c: z, l: 1, N: 2, A: 7, B: 0}
    assert (-eq).match(matcher) == {c: -z, l: 1, N: 2, A: 7, B: 0}
    assert (x*eq).match(matcher) == {c: x*z, l: 1, N: 2, A: 7, B: 0}
    assert (-7*x*eq).match(matcher) == {c: -7*x*z, l: 1, N: 2, A: 7, B: 0}

    matcher = c*sin(phi*N)**l * r**A

    assert eq.match(matcher) == {c: z, l: 1, N: 2, A: 7}
    assert (-eq).match(matcher) == {c: -z, l: 1, N: 2, A: 7}
    assert (x*eq).match(matcher) == {c: x*z, l: 1, N: 2, A: 7}
    assert (-7*x*eq).match(matcher) == {c: -7*x*z, l: 1, N: 2, A: 7}
Ejemplo n.º 17
0
 def _eval_nseries(self, x, n, logx, cdir=0):
     # NOTE Please see the comment at the beginning of this file, labelled
     #      IMPORTANT.
     from sympy.functions.elementary.complexes import sign
     from sympy.functions.elementary.integers import ceiling
     from sympy.series.limits import limit
     from sympy.series.order import Order
     from sympy.simplify.powsimp import powsimp
     arg = self.exp
     arg_series = arg._eval_nseries(x, n=n, logx=logx)
     if arg_series.is_Order:
         return 1 + arg_series
     arg0 = limit(arg_series.removeO(), x, 0)
     if arg0 is S.NegativeInfinity:
         return Order(x**n, x)
     if arg0 is S.Infinity:
         return self
     # checking for indecisiveness/ sign terms in arg0
     if any(isinstance(arg, (sign, ImaginaryUnit)) for arg in arg0.args):
         return self
     t = Dummy("t")
     nterms = n
     try:
         cf = Order(arg.as_leading_term(x, logx=logx), x).getn()
     except (NotImplementedError, PoleError):
         cf = 0
     if cf and cf > 0:
         nterms = ceiling(n/cf)
     exp_series = exp(t)._taylor(t, nterms)
     r = exp(arg0)*exp_series.subs(t, arg_series - arg0)
     if cf and cf > 1:
         r += Order((arg_series - arg0)**n, x)/x**((cf-1)*n)
     else:
         r += Order((arg_series - arg0)**n, x)
     r = r.expand()
     r = powsimp(r, deep=True, combine='exp')
     # powsimp may introduce unexpanded (-1)**Rational; see PR #17201
     simplerat = lambda x: x.is_Rational and x.q in [3, 4, 6]
     w = Wild('w', properties=[simplerat])
     r = r.replace(S.NegativeOne**w, expand_complex(S.NegativeOne**w))
     return r
Ejemplo n.º 18
0
def _transform_DE_RE(DE, g, k, order, syms):
    """Converts DE with free parameters into RE of hypergeometric type."""
    from sympy.solvers.solveset import linsolve

    RE = hyper_re(DE, g, k)

    eq = []
    for i in range(1, order):
        coeff = RE.coeff(g(k + i))
        eq.append(coeff)
    sol = dict(zip(syms, (i for s in linsolve(eq, list(syms)) for i in s)))
    if sol:
        m = Wild('m')
        RE = RE.subs(sol)
        RE = RE.factor().as_numer_denom()[0].collect(g(k + m))
        RE = RE.as_coeff_mul(g)[1][0]
        for i in range(order):  # smallest order should be g(k)
            if RE.coeff(g(k + i)) and i:
                RE = RE.subs(k, k - i)
                break
    return RE
Ejemplo n.º 19
0
def ode_order(expr, func):
    """
    Returns the order of a given differential
    equation with respect to func.

    This function is implemented recursively.

    Examples
    ========

    >>> from sympy import Function
    >>> from sympy.solvers.deutils import ode_order
    >>> from sympy.abc import x
    >>> f, g = map(Function, ['f', 'g'])
    >>> ode_order(f(x).diff(x, 2) + f(x).diff(x)**2 +
    ... f(x).diff(x), f(x))
    2
    >>> ode_order(f(x).diff(x, 2) + g(x).diff(x, 3), f(x))
    2
    >>> ode_order(f(x).diff(x, 2) + g(x).diff(x, 3), g(x))
    3

    """
    a = Wild('a', exclude=[func])
    if expr.match(a):
        return 0

    if isinstance(expr, Derivative):
        if expr.args[0] == func:
            return len(expr.variables)
        else:
            order = 0
            for arg in expr.args[0].args:
                order = max(order, ode_order(arg, func) + len(expr.variables))
            return order
    else:
        order = 0
        for arg in expr.args:
            order = max(order, ode_order(arg, func))
        return order
Ejemplo n.º 20
0
def test_Wild_properties():
    S = sympify
    # these tests only include Atoms
    x = Symbol("x")
    y = Symbol("y")
    p = Symbol("p", positive=True)
    k = Symbol("k", integer=True)
    n = Symbol("n", integer=True, positive=True)

    given_patterns = [
        x, y, p, k, -k, n, -n,
        S(-3), S(3), pi,
        Rational(3, 2), I
    ]

    integerp = lambda k: k.is_integer
    positivep = lambda k: k.is_positive
    symbolp = lambda k: k.is_Symbol
    realp = lambda k: k.is_extended_real

    S = Wild("S", properties=[symbolp])
    R = Wild("R", properties=[realp])
    Y = Wild("Y", exclude=[x, p, k, n])
    P = Wild("P", properties=[positivep])
    K = Wild("K", properties=[integerp])
    N = Wild("N", properties=[positivep, integerp])

    given_wildcards = [S, R, Y, P, K, N]

    goodmatch = {
        S: (x, y, p, k, n),
        R: (p, k, -k, n, -n, -3, 3, pi, Rational(3, 2)),
        Y: (y, -3, 3, pi, Rational(3, 2), I),
        P: (p, n, 3, pi, Rational(3, 2)),
        K: (k, -k, n, -n, -3, 3),
        N: (n, 3)
    }

    for A in given_wildcards:
        for pat in given_patterns:
            d = pat.match(A)
            if pat in goodmatch[A]:
                assert d[A] in goodmatch[A]
            else:
                assert d is None
Ejemplo n.º 21
0
 def _eval_nseries(self, x, n, logx):
     # NOTE Please see the comment at the beginning of this file, labelled
     #      IMPORTANT.
     from sympy import limit, oo, Order, powsimp, Wild, expand_complex
     arg = self.args[0]
     arg_series = arg._eval_nseries(x, n=n, logx=logx)
     if arg_series.is_Order:
         return 1 + arg_series
     arg0 = limit(arg_series.removeO(), x, 0)
     if arg0 in [-oo, oo]:
         return self
     t = Dummy("t")
     exp_series = exp(t)._taylor(t, n)
     o = exp_series.getO()
     exp_series = exp_series.removeO()
     r = exp(arg0) * exp_series.subs(t, arg_series - arg0)
     r += Order(o.expr.subs(t, (arg_series - arg0)), x)
     r = r.expand()
     r = powsimp(r, deep=True, combine='exp')
     # powsimp may introduce unexpanded (-1)**Rational; see PR #17201
     simplerat = lambda x: x.is_Rational and x.q in [3, 4, 6]
     w = Wild('w', properties=[simplerat])
     r = r.replace((-1)**w, expand_complex((-1)**w))
     return r
Ejemplo n.º 22
0
def _check_varsh_sum_872_4(e):
    alpha = symbols('alpha')
    beta = symbols('beta')
    a = Wild('a')
    b = Wild('b')
    c = Wild('c')
    cp = Wild('cp')
    gamma = Wild('gamma')
    gammap = Wild('gammap')
    cg1 = CG(a, alpha, b, beta, c, gamma)
    cg2 = CG(a, alpha, b, beta, cp, gammap)
    match1 = e.match(Sum(cg1 * cg2, (alpha, -a, a), (beta, -b, b)))
    if match1 is not None and len(match1) == 6:
        return (KroneckerDelta(c, cp) *
                KroneckerDelta(gamma, gammap)).subs(match1)
    match2 = e.match(Sum(cg1**2, (alpha, -a, a), (beta, -b, b)))
    if match2 is not None and len(match2) == 4:
        return S.One
    return e
Ejemplo n.º 23
0
def rsolve(f, y, init=None):
    """Solve univariate recurrence with rational coefficients.

       Given k-th order linear recurrence Ly = f, or equivalently:

         a_{k}(n) y(n+k) + a_{k-1}(n) y(n+k-1) + ... + a_{0}(n) y(n) = f

       where a_{i}(n), for i=0..k, are polynomials or rational functions
       in n, and f is a hypergeometric function or a sum of a fixed number
       of pairwise dissimilar hypergeometric terms in n, finds all solutions
       or returns None, if none were found.

       Initial conditions can be given as a dictionary in two forms:

          [1] {   n_0  : v_0,   n_1  : v_1, ...,   n_m  : v_m }
          [2] { y(n_0) : v_0, y(n_1) : v_1, ..., y(n_m) : v_m }

       or as a list L of values:

          L = [ v_0, v_1, ..., v_m ]

       where L[i] = v_i, for i=0..m, maps to y(n_i).

       As an example lets consider the following recurrence:

         (n - 1) y(n + 2) - (n**2 + 3 n - 2) y(n + 1) + 2 n (n + 1) y(n) == 0

       >>> from sympy import Function, rsolve
       >>> from sympy.abc import n
       >>> y = Function('y')

       >>> f = (n-1)*y(n+2) - (n**2+3*n-2)*y(n+1) + 2*n*(n+1)*y(n)

       >>> rsolve(f, y(n))
       C0*gamma(1 + n) + C1*2**n

       >>> rsolve(f, y(n), { y(0):0, y(1):3 })
       -3*gamma(1 + n) + 3*2**n

    """
    if isinstance(f, Equality):
        f = f.lhs - f.rhs

    if f.is_Add:
        F = f.args
    else:
        F = [f]

    k = Wild('k')
    n = y.args[0]

    h_part = {}
    i_part = S.Zero

    for g in F:
        if g.is_Mul:
            G = g.args
        else:
            G = [g]

        coeff = S.One
        kspec = None

        for h in G:
            if h.is_Function:
                if h.func == y.func:
                    result = h.args[0].match(n + k)

                    if result is not None:
                        kspec = int(result[k])
                    else:
                        raise ValueError("'%s(%s+k)' expected, got '%s'" % (y.func, n, h))
                else:
                    raise ValueError("'%s' expected, got '%s'" % (y.func, h.func))
            else:
                coeff *= h

        if kspec is not None:
            if kspec in h_part:
                h_part[kspec] += coeff
            else:
                h_part[kspec] = coeff
        else:
            i_part += coeff

    for k, coeff in h_part.iteritems():
        h_part[k] = simplify(coeff)

    common = S.One

    for coeff in h_part.itervalues():
        if coeff.is_rational_function(n):
            if not coeff.is_polynomial(n):
                common = lcm(common, coeff.as_numer_denom()[1], n)
        else:
            raise ValueError("Polynomial or rational function expected, got '%s'" % coeff)

    i_numer, i_denom = i_part.as_numer_denom()

    if i_denom.is_polynomial(n):
        common = lcm(common, i_denom, n)

    if common is not S.One:
        for k, coeff in h_part.iteritems():
            numer, denom = coeff.as_numer_denom()
            h_part[k] = numer*exquo(common, denom, n)

        i_part = i_numer*exquo(common, i_denom, n)

    K_min = min(h_part.keys())

    if K_min < 0:
        K = abs(K_min)

        H_part = {}
        i_part = i_part.subs(n, n+K).expand()
        common = common.subs(n, n+K).expand()

        for k, coeff in h_part.iteritems():
            H_part[k+K] = coeff.subs(n, n+K).expand()
    else:
        H_part = h_part

    K_max = max(H_part.keys())
    coeffs = []

    for i in xrange(0, K_max+1):
        if i in H_part:
            coeffs.append(H_part[i])
        else:
            coeffs.append(S.Zero)

    result = rsolve_hyper(coeffs, i_part, n, symbols=True)

    if result is None:
        return None
    else:
        solution, symbols = result

        if symbols and init is not None:
            equations = []

            if type(init) is list:
                for i in xrange(0, len(init)):
                    eq = solution.subs(n, i) - init[i]
                    equations.append(eq)
            else:
                for k, v in init.iteritems():
                    try:
                        i = int(k)
                    except TypeError:
                        if k.is_Function and k.func == y.func:
                            i = int(k.args[0])
                        else:
                            raise ValueError("Integer or term expected, got '%s'" % k)

                    eq = solution.subs(n, i) - v
                    equations.append(eq)

            result = solve(equations, *symbols)

            if result is None:
                return None
            else:
                for k, v in result.iteritems():
                    solution = solution.subs(k, v)

    return (solution.expand()) / common
Ejemplo n.º 24
0
    def _eval_nseries(self, x, n, logx, cdir=0):
        # NOTE Please see the comment at the beginning of this file, labelled
        #      IMPORTANT.
        from sympy import im, cancel, I, Order, logcombine
        from itertools import product
        if not logx:
            logx = log(x)
        if self.args[0] == x:
            return logx
        arg = self.args[0]
        k, l = Wild("k"), Wild("l")
        r = arg.match(k * x**l)
        if r is not None:
            k, l = r[k], r[l]
            if l != 0 and not l.has(x) and not k.has(x):
                r = log(k) + l * logx  # XXX true regardless of assumptions?
                return r

        def coeff_exp(term, x):
            coeff, exp = S.One, S.Zero
            for factor in Mul.make_args(term):
                if factor.has(x):
                    base, exp = factor.as_base_exp()
                    if base != x:
                        try:
                            return term.leadterm(x)
                        except ValueError:
                            return term, S.Zero
                else:
                    coeff *= factor
            return coeff, exp

        # TODO new and probably slow
        try:
            a, b = arg.leadterm(x)
            s = arg.nseries(x, n=n + b, logx=logx)
        except (ValueError, NotImplementedError):
            s = arg.nseries(x, n=n, logx=logx)
            while s.is_Order:
                n += 1
                s = arg.nseries(x, n=n, logx=logx)
        a, b = s.removeO().leadterm(x)
        p = cancel(s / (a * x**b) - 1).expand().powsimp()
        if p.has(exp):
            p = logcombine(p)
        if isinstance(p, Order):
            n = p.getn()
        _, d = coeff_exp(p, x)
        if not d.is_positive:
            return log(a) + b * logx + Order(x**n, x)

        def mul(d1, d2):
            res = {}
            for e1, e2 in product(d1, d2):
                ex = e1 + e2
                if ex < n:
                    res[ex] = res.get(ex, S.Zero) + d1[e1] * d2[e2]
            return res

        pterms = {}

        for term in Add.make_args(p):
            co1, e1 = coeff_exp(term, x)
            pterms[e1] = pterms.get(e1, S.Zero) + co1.removeO()

        k = S.One
        terms = {}
        pk = pterms

        while k * d < n:
            coeff = -(-1)**k / k
            for ex in pk:
                terms[ex] = terms.get(ex, S.Zero) + coeff * pk[ex]
            pk = mul(pk, pterms)
            k += S.One

        res = log(a) + b * logx
        for ex in terms:
            res += terms[ex] * x**(ex)

        if cdir != 0:
            cdir = self.args[0].dir(x, cdir)
        if a.is_real and a.is_negative and im(cdir) < 0:
            res -= 2 * I * S.Pi
        return res + Order(x**n, x)
Ejemplo n.º 25
0
def heurisch(f, x, rewrite=False, hints=None, mappings=None, retries=3,
             degree_offset=0, unnecessary_permutations=None,
             _try_heurisch=None):
    """
    Compute indefinite integral using heuristic Risch algorithm.

    Explanation
    ===========

    This is a heuristic approach to indefinite integration in finite
    terms using the extended heuristic (parallel) Risch algorithm, based
    on Manuel Bronstein's "Poor Man's Integrator".

    The algorithm supports various classes of functions including
    transcendental elementary or special functions like Airy,
    Bessel, Whittaker and Lambert.

    Note that this algorithm is not a decision procedure. If it isn't
    able to compute the antiderivative for a given function, then this is
    not a proof that such a functions does not exist.  One should use
    recursive Risch algorithm in such case.  It's an open question if
    this algorithm can be made a full decision procedure.

    This is an internal integrator procedure. You should use top level
    'integrate' function in most cases, as this procedure needs some
    preprocessing steps and otherwise may fail.

    Specification
    =============

     heurisch(f, x, rewrite=False, hints=None)

       where
         f : expression
         x : symbol

         rewrite -> force rewrite 'f' in terms of 'tan' and 'tanh'
         hints   -> a list of functions that may appear in anti-derivate

          - hints = None          --> no suggestions at all
          - hints = [ ]           --> try to figure out
          - hints = [f1, ..., fn] --> we know better

    Examples
    ========

    >>> from sympy import tan
    >>> from sympy.integrals.heurisch import heurisch
    >>> from sympy.abc import x, y

    >>> heurisch(y*tan(x), x)
    y*log(tan(x)**2 + 1)/2

    See Manuel Bronstein's "Poor Man's Integrator":

    References
    ==========

    .. [1] http://www-sop.inria.fr/cafe/Manuel.Bronstein/pmint/index.html

    For more information on the implemented algorithm refer to:

    .. [2] K. Geddes, L. Stefanus, On the Risch-Norman Integration
       Method and its Implementation in Maple, Proceedings of
       ISSAC'89, ACM Press, 212-217.

    .. [3] J. H. Davenport, On the Parallel Risch Algorithm (I),
       Proceedings of EUROCAM'82, LNCS 144, Springer, 144-157.

    .. [4] J. H. Davenport, On the Parallel Risch Algorithm (III):
       Use of Tangents, SIGSAM Bulletin 16 (1982), 3-6.

    .. [5] J. H. Davenport, B. M. Trager, On the Parallel Risch
       Algorithm (II), ACM Transactions on Mathematical
       Software 11 (1985), 356-362.

    See Also
    ========

    sympy.integrals.integrals.Integral.doit
    sympy.integrals.integrals.Integral
    sympy.integrals.heurisch.components
    """
    f = sympify(f)

    # There are some functions that Heurisch cannot currently handle,
    # so do not even try.
    # Set _try_heurisch=True to skip this check
    if _try_heurisch is not True:
        if f.has(Abs, re, im, sign, Heaviside, DiracDelta, floor, ceiling, arg):
            return

    if not f.has_free(x):
        return f*x

    if not f.is_Add:
        indep, f = f.as_independent(x)
    else:
        indep = S.One

    rewritables = {
        (sin, cos, cot): tan,
        (sinh, cosh, coth): tanh,
    }

    if rewrite:
        for candidates, rule in rewritables.items():
            f = f.rewrite(candidates, rule)
    else:
        for candidates in rewritables.keys():
            if f.has(*candidates):
                break
        else:
            rewrite = True

    terms = components(f, x)

    if hints is not None:
        if not hints:
            a = Wild('a', exclude=[x])
            b = Wild('b', exclude=[x])
            c = Wild('c', exclude=[x])

            for g in set(terms):  # using copy of terms
                if g.is_Function:
                    if isinstance(g, li):
                        M = g.args[0].match(a*x**b)

                        if M is not None:
                            terms.add( x*(li(M[a]*x**M[b]) - (M[a]*x**M[b])**(-1/M[b])*Ei((M[b]+1)*log(M[a]*x**M[b])/M[b])) )
                            #terms.add( x*(li(M[a]*x**M[b]) - (x**M[b])**(-1/M[b])*Ei((M[b]+1)*log(M[a]*x**M[b])/M[b])) )
                            #terms.add( x*(li(M[a]*x**M[b]) - x*Ei((M[b]+1)*log(M[a]*x**M[b])/M[b])) )
                            #terms.add( li(M[a]*x**M[b]) - Ei((M[b]+1)*log(M[a]*x**M[b])/M[b]) )

                    elif isinstance(g, exp):
                        M = g.args[0].match(a*x**2)

                        if M is not None:
                            if M[a].is_positive:
                                terms.add(erfi(sqrt(M[a])*x))
                            else: # M[a].is_negative or unknown
                                terms.add(erf(sqrt(-M[a])*x))

                        M = g.args[0].match(a*x**2 + b*x + c)

                        if M is not None:
                            if M[a].is_positive:
                                terms.add(sqrt(pi/4*(-M[a]))*exp(M[c] - M[b]**2/(4*M[a]))*
                                          erfi(sqrt(M[a])*x + M[b]/(2*sqrt(M[a]))))
                            elif M[a].is_negative:
                                terms.add(sqrt(pi/4*(-M[a]))*exp(M[c] - M[b]**2/(4*M[a]))*
                                          erf(sqrt(-M[a])*x - M[b]/(2*sqrt(-M[a]))))

                        M = g.args[0].match(a*log(x)**2)

                        if M is not None:
                            if M[a].is_positive:
                                terms.add(erfi(sqrt(M[a])*log(x) + 1/(2*sqrt(M[a]))))
                            if M[a].is_negative:
                                terms.add(erf(sqrt(-M[a])*log(x) - 1/(2*sqrt(-M[a]))))

                elif g.is_Pow:
                    if g.exp.is_Rational and g.exp.q == 2:
                        M = g.base.match(a*x**2 + b)

                        if M is not None and M[b].is_positive:
                            if M[a].is_positive:
                                terms.add(asinh(sqrt(M[a]/M[b])*x))
                            elif M[a].is_negative:
                                terms.add(asin(sqrt(-M[a]/M[b])*x))

                        M = g.base.match(a*x**2 - b)

                        if M is not None and M[b].is_positive:
                            if M[a].is_positive:
                                terms.add(acosh(sqrt(M[a]/M[b])*x))
                            elif M[a].is_negative:
                                terms.add(-M[b]/2*sqrt(-M[a])*
                                           atan(sqrt(-M[a])*x/sqrt(M[a]*x**2 - M[b])))

        else:
            terms |= set(hints)

    dcache = DiffCache(x)

    for g in set(terms):  # using copy of terms
        terms |= components(dcache.get_diff(g), x)

    # TODO: caching is significant factor for why permutations work at all. Change this.
    V = _symbols('x', len(terms))


    # sort mapping expressions from largest to smallest (last is always x).
    mapping = list(reversed(list(zip(*ordered(                          #
        [(a[0].as_independent(x)[1], a) for a in zip(terms, V)])))[1])) #
    rev_mapping = {v: k for k, v in mapping}                            #
    if mappings is None:                                                #
        # optimizing the number of permutations of mapping              #
        assert mapping[-1][0] == x # if not, find it and correct this comment
        unnecessary_permutations = [mapping.pop(-1)]
        mappings = permutations(mapping)
    else:
        unnecessary_permutations = unnecessary_permutations or []

    def _substitute(expr):
        return expr.subs(mapping)

    for mapping in mappings:
        mapping = list(mapping)
        mapping = mapping + unnecessary_permutations
        diffs = [ _substitute(dcache.get_diff(g)) for g in terms ]
        denoms = [ g.as_numer_denom()[1] for g in diffs ]
        if all(h.is_polynomial(*V) for h in denoms) and _substitute(f).is_rational_function(*V):
            denom = reduce(lambda p, q: lcm(p, q, *V), denoms)
            break
    else:
        if not rewrite:
            result = heurisch(f, x, rewrite=True, hints=hints,
                unnecessary_permutations=unnecessary_permutations)

            if result is not None:
                return indep*result
        return None

    numers = [ cancel(denom*g) for g in diffs ]
    def _derivation(h):
        return Add(*[ d * h.diff(v) for d, v in zip(numers, V) ])

    def _deflation(p):
        for y in V:
            if not p.has(y):
                continue

            if _derivation(p) is not S.Zero:
                c, q = p.as_poly(y).primitive()
                return _deflation(c)*gcd(q, q.diff(y)).as_expr()

        return p

    def _splitter(p):
        for y in V:
            if not p.has(y):
                continue

            if _derivation(y) is not S.Zero:
                c, q = p.as_poly(y).primitive()

                q = q.as_expr()

                h = gcd(q, _derivation(q), y)
                s = quo(h, gcd(q, q.diff(y), y), y)

                c_split = _splitter(c)

                if s.as_poly(y).degree() == 0:
                    return (c_split[0], q * c_split[1])

                q_split = _splitter(cancel(q / s))

                return (c_split[0]*q_split[0]*s, c_split[1]*q_split[1])

        return (S.One, p)

    special = {}

    for term in terms:
        if term.is_Function:
            if isinstance(term, tan):
                special[1 + _substitute(term)**2] = False
            elif isinstance(term, tanh):
                special[1 + _substitute(term)] = False
                special[1 - _substitute(term)] = False
            elif isinstance(term, LambertW):
                special[_substitute(term)] = True

    F = _substitute(f)

    P, Q = F.as_numer_denom()

    u_split = _splitter(denom)
    v_split = _splitter(Q)

    polys = set(list(v_split) + [ u_split[0] ] + list(special.keys()))

    s = u_split[0] * Mul(*[ k for k, v in special.items() if v ])
    polified = [ p.as_poly(*V) for p in [s, P, Q] ]

    if None in polified:
        return None

    #--- definitions for _integrate
    a, b, c = [ p.total_degree() for p in polified ]

    poly_denom = (s * v_split[0] * _deflation(v_split[1])).as_expr()

    def _exponent(g):
        if g.is_Pow:
            if g.exp.is_Rational and g.exp.q != 1:
                if g.exp.p > 0:
                    return g.exp.p + g.exp.q - 1
                else:
                    return abs(g.exp.p + g.exp.q)
            else:
                return 1
        elif not g.is_Atom and g.args:
            return max([ _exponent(h) for h in g.args ])
        else:
            return 1

    A, B = _exponent(f), a + max(b, c)

    if A > 1 and B > 1:
        monoms = tuple(ordered(itermonomials(V, A + B - 1 + degree_offset)))
    else:
        monoms = tuple(ordered(itermonomials(V, A + B + degree_offset)))

    poly_coeffs = _symbols('A', len(monoms))

    poly_part = Add(*[ poly_coeffs[i]*monomial
        for i, monomial in enumerate(monoms) ])

    reducibles = set()

    for poly in ordered(polys):
        coeff, factors = factor_list(poly, *V)
        reducibles.add(coeff)
        for fact, mul in factors:
            reducibles.add(fact)

    def _integrate(field=None):
        atans = set()
        pairs = set()

        if field == 'Q':
            irreducibles = set(reducibles)
        else:
            setV = set(V)
            irreducibles = set()
            for poly in ordered(reducibles):
                zV = setV & set(iterfreeargs(poly))
                for z in ordered(zV):
                    s = set(root_factors(poly, z, filter=field))
                    irreducibles |= s
                    break

        log_part, atan_part = [], []

        for poly in ordered(irreducibles):
            m = collect(poly, I, evaluate=False)
            y = m.get(I, S.Zero)
            if y:
                x = m.get(S.One, S.Zero)
                if x.has(I) or y.has(I):
                    continue  # nontrivial x + I*y
                pairs.add((x, y))
                irreducibles.remove(poly)

        while pairs:
            x, y = pairs.pop()
            if (x, -y) in pairs:
                pairs.remove((x, -y))
                # Choosing b with no minus sign
                if y.could_extract_minus_sign():
                    y = -y
                irreducibles.add(x*x + y*y)
                atans.add(atan(x/y))
            else:
                irreducibles.add(x + I*y)


        B = _symbols('B', len(irreducibles))
        C = _symbols('C', len(atans))

        # Note: the ordering matters here
        for poly, b in reversed(list(zip(ordered(irreducibles), B))):
            if poly.has(*V):
                poly_coeffs.append(b)
                log_part.append(b * log(poly))

        for poly, c in reversed(list(zip(ordered(atans), C))):
            if poly.has(*V):
                poly_coeffs.append(c)
                atan_part.append(c * poly)

        # TODO: Currently it's better to use symbolic expressions here instead
        # of rational functions, because it's simpler and FracElement doesn't
        # give big speed improvement yet. This is because cancellation is slow
        # due to slow polynomial GCD algorithms. If this gets improved then
        # revise this code.
        candidate = poly_part/poly_denom + Add(*log_part) + Add(*atan_part)
        h = F - _derivation(candidate) / denom
        raw_numer = h.as_numer_denom()[0]

        # Rewrite raw_numer as a polynomial in K[coeffs][V] where K is a field
        # that we have to determine. We can't use simply atoms() because log(3),
        # sqrt(y) and similar expressions can appear, leading to non-trivial
        # domains.
        syms = set(poly_coeffs) | set(V)
        non_syms = set()

        def find_non_syms(expr):
            if expr.is_Integer or expr.is_Rational:
                pass # ignore trivial numbers
            elif expr in syms:
                pass # ignore variables
            elif not expr.has_free(*syms):
                non_syms.add(expr)
            elif expr.is_Add or expr.is_Mul or expr.is_Pow:
                list(map(find_non_syms, expr.args))
            else:
                # TODO: Non-polynomial expression. This should have been
                # filtered out at an earlier stage.
                raise PolynomialError

        try:
            find_non_syms(raw_numer)
        except PolynomialError:
            return None
        else:
            ground, _ = construct_domain(non_syms, field=True)

        coeff_ring = PolyRing(poly_coeffs, ground)
        ring = PolyRing(V, coeff_ring)
        try:
            numer = ring.from_expr(raw_numer)
        except ValueError:
            raise PolynomialError
        solution = solve_lin_sys(numer.coeffs(), coeff_ring, _raw=False)

        if solution is None:
            return None
        else:
            return candidate.xreplace(solution).xreplace(
                dict(zip(poly_coeffs, [S.Zero]*len(poly_coeffs))))

    if all(isinstance(_, Symbol) for _ in V):
        more_free = F.free_symbols - set(V)
    else:
        Fd = F.as_dummy()
        more_free = Fd.xreplace(dict(zip(V, (Dummy() for _ in V)))
            ).free_symbols & Fd.free_symbols
    if not more_free:
        # all free generators are identified in V
        solution = _integrate('Q')

        if solution is None:
            solution = _integrate()
    else:
        solution = _integrate()

    if solution is not None:
        antideriv = solution.subs(rev_mapping)
        antideriv = cancel(antideriv).expand()

        if antideriv.is_Add:
            antideriv = antideriv.as_independent(x)[1]

        return indep*antideriv
    else:
        if retries >= 0:
            result = heurisch(f, x, mappings=mappings, rewrite=rewrite, hints=hints, retries=retries - 1, unnecessary_permutations=unnecessary_permutations)

            if result is not None:
                return indep*result

        return None
def eval_sum_symbolic(f, limits):
    from sympy.functions import harmonic, bernoulli

    f_orig = f
    (i, a, b) = limits
    if not f.has(i):
        return f*(b - a + 1)

    # Linearity
    if f.is_Mul:
        L, R = f.as_two_terms()

        if not L.has(i):
            sR = eval_sum_symbolic(R, (i, a, b))
            if sR:
                return L*sR

        if not R.has(i):
            sL = eval_sum_symbolic(L, (i, a, b))
            if sL:
                return R*sL

        try:
            f = apart(f, i)  # see if it becomes an Add
        except PolynomialError:
            pass

    if f.is_Add:
        L, R = f.as_two_terms()
        lrsum = telescopic(L, R, (i, a, b))

        if lrsum:
            return lrsum

        lsum = eval_sum_symbolic(L, (i, a, b))
        rsum = eval_sum_symbolic(R, (i, a, b))

        if None not in (lsum, rsum):
            r = lsum + rsum
            if not r is S.NaN:
                return r

    # Polynomial terms with Faulhaber's formula
    n = Wild('n')
    result = f.match(i**n)

    if result is not None:
        n = result[n]

        if n.is_Integer:
            if n >= 0:
                if (b is S.Infinity and not a is S.NegativeInfinity) or \
                   (a is S.NegativeInfinity and not b is S.Infinity):
                    return S.Infinity
                return ((bernoulli(n + 1, b + 1) - bernoulli(n + 1, a))/(n + 1)).expand()
            elif a.is_Integer and a >= 1:
                if n == -1:
                    return harmonic(b) - harmonic(a - 1)
                else:
                    return harmonic(b, abs(n)) - harmonic(a - 1, abs(n))

    if not (a.has(S.Infinity, S.NegativeInfinity) or
            b.has(S.Infinity, S.NegativeInfinity)):
        # Geometric terms
        c1 = Wild('c1', exclude=[i])
        c2 = Wild('c2', exclude=[i])
        c3 = Wild('c3', exclude=[i])
        wexp = Wild('wexp')

        # Here we first attempt powsimp on f for easier matching with the
        # exponential pattern, and attempt expansion on the exponent for easier
        # matching with the linear pattern.
        e = f.powsimp().match(c1 ** wexp)
        if e is not None:
            e_exp = e.pop(wexp).expand().match(c2*i + c3)
            if e_exp is not None:
                e.update(e_exp)

        if e is not None:
            p = (c1**c3).subs(e)
            q = (c1**c2).subs(e)

            r = p*(q**a - q**(b + 1))/(1 - q)
            l = p*(b - a + 1)

            return Piecewise((l, Eq(q, S.One)), (r, True))

        r = gosper_sum(f, (i, a, b))

        if isinstance(r, (Mul,Add)):
            from sympy import ordered, Tuple
            non_limit = r.free_symbols - Tuple(*limits[1:]).free_symbols
            den = denom(together(r))
            den_sym = non_limit & den.free_symbols
            args = []
            for v in ordered(den_sym):
                try:
                    s = solve(den, v)
                    m = Eq(v, s[0]) if s else S.false
                    if m != False:
                        args.append((Sum(f_orig.subs(*m.args), limits).doit(), m))
                    break
                except NotImplementedError:
                    continue

            args.append((r, True))
            return Piecewise(*args)

        if not r in (None, S.NaN):
            return r

    h = eval_sum_hyper(f_orig, (i, a, b))
    if h is not None:
        return h

    factored = f_orig.factor()
    if factored != f_orig:
        return eval_sum_symbolic(factored, (i, a, b))
Ejemplo n.º 27
0
def heurisch(f, x, rewrite=False, hints=None, mappings=None, retries=3):
    """
    Compute indefinite integral using heuristic Risch algorithm.

    This is a heuristic approach to indefinite integration in finite
    terms using the extended heuristic (parallel) Risch algorithm, based
    on Manuel Bronstein's "Poor Man's Integrator".

    The algorithm supports various classes of functions including
    transcendental elementary or special functions like Airy,
    Bessel, Whittaker and Lambert.

    Note that this algorithm is not a decision procedure. If it isn't
    able to compute the antiderivative for a given function, then this is
    not a proof that such a functions does not exist.  One should use
    recursive Risch algorithm in such case.  It's an open question if
    this algorithm can be made a full decision procedure.

    This is an internal integrator procedure. You should use toplevel
    'integrate' function in most cases,  as this procedure needs some
    preprocessing steps and otherwise may fail.

    Specification
    =============

     heurisch(f, x, rewrite=False, hints=None)

       where
         f : expression
         x : symbol

         rewrite -> force rewrite 'f' in terms of 'tan' and 'tanh'
         hints   -> a list of functions that may appear in anti-derivate

          - hints = None          --> no suggestions at all
          - hints = [ ]           --> try to figure out
          - hints = [f1, ..., fn] --> we know better

    Examples
    ========

    >>> from sympy import tan
    >>> from sympy.integrals.heurisch import heurisch
    >>> from sympy.abc import x, y

    >>> heurisch(y*tan(x), x)
    y*log(tan(x)**2 + 1)/2

    See Manuel Bronstein's "Poor Man's Integrator":

    [1] http://www-sop.inria.fr/cafe/Manuel.Bronstein/pmint/index.html

    For more information on the implemented algorithm refer to:

    [2] K. Geddes, L. Stefanus, On the Risch-Norman Integration
       Method and its Implementation in Maple, Proceedings of
       ISSAC'89, ACM Press, 212-217.

    [3] J. H. Davenport, On the Parallel Risch Algorithm (I),
       Proceedings of EUROCAM'82, LNCS 144, Springer, 144-157.

    [4] J. H. Davenport, On the Parallel Risch Algorithm (III):
       Use of Tangents, SIGSAM Bulletin 16 (1982), 3-6.

    [5] J. H. Davenport, B. M. Trager, On the Parallel Risch
       Algorithm (II), ACM Transactions on Mathematical
       Software 11 (1985), 356-362.

    See Also
    ========

    sympy.integrals.integrals.Integral.doit
    sympy.integrals.integrals.Integral
    components
    """
    f = sympify(f)

    if not f.is_Add:
        indep, f = f.as_independent(x)
    else:
        indep = S.One

    if not f.has(x):
        return indep * f * x

    rewritables = {
        (sin, cos, cot): tan,
        (sinh, cosh, coth): tanh,
    }

    if rewrite:
        for candidates, rule in rewritables.iteritems():
            f = f.rewrite(candidates, rule)
    else:
        for candidates in rewritables.iterkeys():
            if f.has(*candidates):
                break
        else:
            rewrite = True

    terms = components(f, x)

    if hints is not None:
        if not hints:
            a = Wild('a', exclude=[x])
            b = Wild('b', exclude=[x])
            c = Wild('c', exclude=[x])

            for g in set(terms):
                if g.is_Function:
                    if g.func is exp:
                        M = g.args[0].match(a * x**2)

                        if M is not None:
                            terms.add(erf(sqrt(-M[a]) * x))

                        M = g.args[0].match(a * x**2 + b * x + c)

                        if M is not None:
                            if M[a].is_positive:
                                terms.add(
                                    sqrt(pi / 4 * (-M[a])) *
                                    exp(M[c] - M[b]**2 / (4 * M[a])) *
                                    erf(-sqrt(-M[a]) * x + M[b] /
                                        (2 * sqrt(-M[a]))))
                            elif M[a].is_negative:
                                terms.add(
                                    sqrt(pi / 4 * (-M[a])) *
                                    exp(M[c] - M[b]**2 / (4 * M[a])) * erf(
                                        sqrt(-M[a]) * x - M[b] /
                                        (2 * sqrt(-M[a]))))

                        M = g.args[0].match(a * log(x)**2)

                        if M is not None:
                            if M[a].is_positive:
                                terms.add(-I * erf(I *
                                                   (sqrt(M[a]) * log(x) + 1 /
                                                    (2 * sqrt(M[a])))))
                            if M[a].is_negative:
                                terms.add(
                                    erf(
                                        sqrt(-M[a]) * log(x) - 1 /
                                        (2 * sqrt(-M[a]))))

                elif g.is_Pow:
                    if g.exp.is_Rational and g.exp.q == 2:
                        M = g.base.match(a * x**2 + b)

                        if M is not None and M[b].is_positive:
                            if M[a].is_positive:
                                terms.add(asinh(sqrt(M[a] / M[b]) * x))
                            elif M[a].is_negative:
                                terms.add(asin(sqrt(-M[a] / M[b]) * x))

                        M = g.base.match(a * x**2 - b)

                        if M is not None and M[b].is_positive:
                            if M[a].is_positive:
                                terms.add(acosh(sqrt(M[a] / M[b]) * x))
                            elif M[a].is_negative:
                                terms.add((-M[b] / 2 * sqrt(-M[a]) * atan(
                                    sqrt(-M[a]) * x / sqrt(M[a] * x**2 - M[b]))
                                           ))

        else:
            terms |= set(hints)

    for g in set(terms):
        terms |= components(cancel(g.diff(x)), x)

    # TODO: caching is significant factor for why permutations work at all. Change this.
    V = _symbols('x', len(terms))

    mapping = dict(zip(terms, V))

    rev_mapping = {}

    for k, v in mapping.iteritems():
        rev_mapping[v] = k

    if mappings is None:
        # Pre-sort mapping in order of largest to smallest expressions (last is always x).
        def _sort_key(arg):
            return default_sort_key(arg[0].as_independent(x)[1])

        mapping = sorted(mapping.items(), key=_sort_key, reverse=True)
        mappings = permutations(mapping)

    def _substitute(expr):
        return expr.subs(mapping)

    for mapping in mappings:
        # TODO: optimize this by not generating permutations where mapping[-1] != x.
        if mapping[-1][0] != x:
            continue

        mapping = list(mapping)

        diffs = [_substitute(cancel(g.diff(x))) for g in terms]
        denoms = [g.as_numer_denom()[1] for g in diffs]

        if all(h.is_polynomial(*V)
               for h in denoms) and _substitute(f).is_rational_function(*V):
            denom = reduce(lambda p, q: lcm(p, q, *V), denoms)
            break
    else:
        if not rewrite:
            result = heurisch(f, x, rewrite=True, hints=hints)

            if result is not None:
                return indep * result

        return None

    numers = [cancel(denom * g) for g in diffs]

    def _derivation(h):
        return Add(*[d * h.diff(v) for d, v in zip(numers, V)])

    def _deflation(p):
        for y in V:
            if not p.has(y):
                continue

            if _derivation(p) is not S.Zero:
                c, q = p.as_poly(y).primitive()
                return _deflation(c) * gcd(q, q.diff(y)).as_expr()
        else:
            return p

    def _splitter(p):
        for y in V:
            if not p.has(y):
                continue

            if _derivation(y) is not S.Zero:
                c, q = p.as_poly(y).primitive()

                q = q.as_expr()

                h = gcd(q, _derivation(q), y)
                s = quo(h, gcd(q, q.diff(y), y), y)

                c_split = _splitter(c)

                if s.as_poly(y).degree() == 0:
                    return (c_split[0], q * c_split[1])

                q_split = _splitter(cancel(q / s))

                return (c_split[0] * q_split[0] * s, c_split[1] * q_split[1])
        else:
            return (S.One, p)

    special = {}

    for term in terms:
        if term.is_Function:
            if term.func is tan:
                special[1 + _substitute(term)**2] = False
            elif term.func is tanh:
                special[1 + _substitute(term)] = False
                special[1 - _substitute(term)] = False
            elif term.func is C.LambertW:
                special[_substitute(term)] = True

    F = _substitute(f)

    P, Q = F.as_numer_denom()

    u_split = _splitter(denom)
    v_split = _splitter(Q)

    polys = list(v_split) + [u_split[0]] + special.keys()

    s = u_split[0] * Mul(*[k for k, v in special.iteritems() if v])
    polified = [p.as_poly(*V) for p in [s, P, Q]]

    if None in polified:
        return None

    a, b, c = [p.total_degree() for p in polified]

    poly_denom = (s * v_split[0] * _deflation(v_split[1])).as_expr()

    def _exponent(g):
        if g.is_Pow:
            if g.exp.is_Rational and g.exp.q != 1:
                if g.exp.p > 0:
                    return g.exp.p + g.exp.q - 1
                else:
                    return abs(g.exp.p + g.exp.q)
            else:
                return 1
        elif not g.is_Atom and g.args:
            return max([_exponent(h) for h in g.args])
        else:
            return 1

    A, B = _exponent(f), a + max(b, c)

    if A > 1 and B > 1:
        monoms = monomials(V, A + B - 1)
    else:
        monoms = monomials(V, A + B)

    poly_coeffs = _symbols('A', len(monoms))

    poly_part = Add(
        *[poly_coeffs[i] * monomial for i, monomial in enumerate(monoms)])

    reducibles = set()

    for poly in polys:
        if poly.has(*V):
            try:
                factorization = factor(poly, greedy=True)
            except PolynomialError:
                factorization = poly
            factorization = poly

            if factorization.is_Mul:
                reducibles |= set(factorization.args)
            else:
                reducibles.add(factorization)

    def _integrate(field=None):
        irreducibles = set()

        for poly in reducibles:
            for z in poly.atoms(Symbol):
                if z in V:
                    break
            else:
                continue

            irreducibles |= set(root_factors(poly, z, filter=field))

        log_coeffs, log_part = [], []
        B = _symbols('B', len(irreducibles))

        for i, poly in enumerate(irreducibles):
            if poly.has(*V):
                log_coeffs.append(B[i])
                log_part.append(log_coeffs[-1] * log(poly))

        coeffs = poly_coeffs + log_coeffs

        candidate = poly_part / poly_denom + Add(*log_part)

        h = F - _derivation(candidate) / denom

        numer = h.as_numer_denom()[0].expand(force=True)

        equations = defaultdict(lambda: S.Zero)

        for term in Add.make_args(numer):
            coeff, dependent = term.as_independent(*V)
            equations[dependent] += coeff

        solution = solve(equations.values(), *coeffs)

        return (solution, candidate, coeffs) if solution else None

    if not (F.atoms(Symbol) - set(V)):
        result = _integrate('Q')

        if result is None:
            result = _integrate()
    else:
        result = _integrate()

    if result is not None:
        (solution, candidate, coeffs) = result

        antideriv = candidate.subs(solution)

        for coeff in coeffs:
            if coeff not in solution:
                antideriv = antideriv.subs(coeff, S.Zero)

        antideriv = antideriv.subs(rev_mapping)
        antideriv = cancel(antideriv).expand(force=True)

        if antideriv.is_Add:
            antideriv = antideriv.as_independent(x)[1]

        return indep * antideriv
    else:
        if retries >= 0:
            result = heurisch(f,
                              x,
                              mappings=mappings,
                              rewrite=rewrite,
                              hints=hints,
                              retries=retries - 1)

            if result is not None:
                return indep * result

        return None
Ejemplo n.º 28
0
 def _wilds(self, f, x, order):
     a = Wild('a', exclude=[x, f(x), f(x).diff(x), 0])
     b = Wild('b', exclude=[x, f(x), f(x).diff(x), 0])
     c = Wild('c', exclude=[x, f(x), f(x).diff(x)])
     d = Wild('d', exclude=[x, f(x), f(x).diff(x)])
     return a, b, c, d
Ejemplo n.º 29
0
 def _wilds(self, f, x, order):
     P = Wild('P', exclude=[f(x)])
     Q = Wild('Q', exclude=[f(x)])
     n = Wild('n', exclude=[x, f(x), f(x).diff(x)])
     return P, Q, n
Ejemplo n.º 30
0
def rsolve(f, y, init=None):
    """
    Solve univariate recurrence with rational coefficients.

    Given `k`-th order linear recurrence `\operatorname{L} y = f`,
    or equivalently:

    .. math:: a_{k}(n) y(n+k) + a_{k-1}(n) y(n+k-1) +
              \dots + a_{0}(n) y(n) = f(n)

    where `a_{i}(n)`, for `i=0, \dots, k`, are polynomials or rational
    functions in `n`, and `f` is a hypergeometric function or a sum
    of a fixed number of pairwise dissimilar hypergeometric terms in
    `n`, finds all solutions or returns ``None``, if none were found.

    Initial conditions can be given as a dictionary in two forms:

        (1) ``{   n_0  : v_0,   n_1  : v_1, ...,   n_m  : v_m }``
        (2) ``{ y(n_0) : v_0, y(n_1) : v_1, ..., y(n_m) : v_m }``

    or as a list ``L`` of values:

        ``L = [ v_0, v_1, ..., v_m ]``

    where ``L[i] = v_i``, for `i=0, \dots, m`, maps to `y(n_i)`.

    Examples
    ========

    Lets consider the following recurrence:

    .. math:: (n - 1) y(n + 2) - (n^2 + 3 n - 2) y(n + 1) +
              2 n (n + 1) y(n) = 0

    >>> from sympy import Function, rsolve
    >>> from sympy.abc import n
    >>> y = Function('y')

    >>> f = (n - 1)*y(n + 2) - (n**2 + 3*n - 2)*y(n + 1) + 2*n*(n + 1)*y(n)

    >>> rsolve(f, y(n))
    2**n*C0 + C1*factorial(n)

    >>> rsolve(f, y(n), { y(0):0, y(1):3 })
    3*2**n - 3*factorial(n)

    See Also
    ========

    rsolve_poly, rsolve_ratio, rsolve_hyper

    """
    if isinstance(f, Equality):
        f = f.lhs - f.rhs

    n = y.args[0]
    k = Wild('k', exclude=(n,))

    # Preprocess user input to allow things like
    # y(n) + a*(y(n + 1) + y(n - 1))/2
    f = f.expand().collect(y.func(Wild('m', integer=True)))

    h_part = defaultdict(lambda: S.Zero)
    i_part = S.Zero
    for g in Add.make_args(f):
        coeff = S.One
        kspec = None
        for h in Mul.make_args(g):
            if h.is_Function:
                if h.func == y.func:
                    result = h.args[0].match(n + k)

                    if result is not None:
                        kspec = int(result[k])
                    else:
                        raise ValueError(
                            "'%s(%s+k)' expected, got '%s'" % (y.func, n, h))
                else:
                    raise ValueError(
                        "'%s' expected, got '%s'" % (y.func, h.func))
            else:
                coeff *= h

        if kspec is not None:
            h_part[kspec] += coeff
        else:
            i_part += coeff

    for k, coeff in h_part.iteritems():
        h_part[k] = simplify(coeff)

    common = S.One

    for coeff in h_part.itervalues():
        if coeff.is_rational_function(n):
            if not coeff.is_polynomial(n):
                common = lcm(common, coeff.as_numer_denom()[1], n)
        else:
            raise ValueError(
                "Polynomial or rational function expected, got '%s'" % coeff)

    i_numer, i_denom = i_part.as_numer_denom()

    if i_denom.is_polynomial(n):
        common = lcm(common, i_denom, n)

    if common is not S.One:
        for k, coeff in h_part.iteritems():
            numer, denom = coeff.as_numer_denom()
            h_part[k] = numer*quo(common, denom, n)

        i_part = i_numer*quo(common, i_denom, n)

    K_min = min(h_part.keys())

    if K_min < 0:
        K = abs(K_min)

        H_part = defaultdict(lambda: S.Zero)
        i_part = i_part.subs(n, n + K).expand()
        common = common.subs(n, n + K).expand()

        for k, coeff in h_part.iteritems():
            H_part[k + K] = coeff.subs(n, n + K).expand()
    else:
        H_part = h_part

    K_max = max(H_part.iterkeys())
    coeffs = [H_part[i] for i in xrange(K_max + 1)]

    result = rsolve_hyper(coeffs, -i_part, n, symbols=True)

    if result is None:
        return None

    solution, symbols = result

    if init == {} or init == []:
        init = None

    if symbols and init is not None:
        if type(init) is list:
            init = dict([(i, init[i]) for i in xrange(len(init))])

        equations = []

        for k, v in init.iteritems():
            try:
                i = int(k)
            except TypeError:
                if k.is_Function and k.func == y.func:
                    i = int(k.args[0])
                else:
                    raise ValueError("Integer or term expected, got '%s'" % k)
            try:
                eq = solution.limit(n, i) - v
            except NotImplementedError:
                eq = solution.subs(n, i) - v
            equations.append(eq)

        result = solve(equations, *symbols)

        if not result:
            return None
        else:
            solution = solution.subs(result)

    return solution