예제 #1
0
 def _expr_big_minus(cls, z, n):
     return Integer(-1)**n * (asinh(sqrt(z)) / sqrt(z) +
                              n * pi * I / sqrt(z))
예제 #2
0
def test_Function():
    assert mcode(f(x, y, z)) == "f[x, y, z]"
    assert mcode(sin(x) ** cos(x)) == "Sin[x]^Cos[x]"
    assert mcode(sign(x)) == "Sign[x]"

    assert mcode(atanh(x), user_functions={"atanh": "ArcTanh"}) == "ArcTanh[x]"

    assert (mcode(meijerg(((1, 1), (3, 4)), ((1,), ()), x)) ==
            "MeijerG[{{1, 1}, {3, 4}}, {{1}, {}}, x]")
    assert (mcode(hyper((1, 2, 3), (3, 4), x)) ==
            "HypergeometricPFQ[{1, 2, 3}, {3, 4}, x]")

    assert mcode(Min(x, y)) == "Min[x, y]"
    assert mcode(Max(x, y)) == "Max[x, y]"
    assert mcode(Max(x, 2)) == "Max[2, x]"  # issue sympy/sympy#15344

    assert mcode(binomial(x, y)) == "Binomial[x, y]"

    assert mcode(log(x)) == "Log[x]"
    assert mcode(tan(x)) == "Tan[x]"
    assert mcode(cot(x)) == "Cot[x]"
    assert mcode(asin(x)) == "ArcSin[x]"
    assert mcode(acos(x)) == "ArcCos[x]"
    assert mcode(atan(x)) == "ArcTan[x]"
    assert mcode(acot(x)) == "ArcCot[x]"
    assert mcode(sinh(x)) == "Sinh[x]"
    assert mcode(cosh(x)) == "Cosh[x]"
    assert mcode(tanh(x)) == "Tanh[x]"
    assert mcode(coth(x)) == "Coth[x]"
    assert mcode(asinh(x)) == "ArcSinh[x]"
    assert mcode(acosh(x)) == "ArcCosh[x]"
    assert mcode(atanh(x)) == "ArcTanh[x]"
    assert mcode(acoth(x)) == "ArcCoth[x]"
    assert mcode(sech(x)) == "Sech[x]"
    assert mcode(csch(x)) == "Csch[x]"
    assert mcode(erfc(x)) == "Erfc[x]"
    assert mcode(conjugate(x)) == "Conjugate[x]"
    assert mcode(re(x)) == "Re[x]"
    assert mcode(im(x)) == "Im[x]"
    assert mcode(polygamma(x, y)) == "PolyGamma[x, y]"
    assert mcode(factorial(x)) == "Factorial[x]"
    assert mcode(factorial2(x)) == "Factorial2[x]"
    assert mcode(rf(x, y)) == "Pochhammer[x, y]"
    assert mcode(gamma(x)) == "Gamma[x]"
    assert mcode(zeta(x)) == "Zeta[x]"
    assert mcode(Heaviside(x)) == "UnitStep[x]"
    assert mcode(fibonacci(x)) == "Fibonacci[x]"
    assert mcode(polylog(x, y)) == "PolyLog[x, y]"

    class myfunc1(Function):
        @classmethod
        def eval(cls, x):
            pass

    class myfunc2(Function):
        @classmethod
        def eval(cls, x, y):
            pass

    pytest.raises(ValueError,
                  lambda: mcode(myfunc1(x),
                                user_functions={"myfunc1": ["Myfunc1"]}))
    assert mcode(myfunc1(x),
                 user_functions={"myfunc1": "Myfunc1"}) == "Myfunc1[x]"
    assert mcode(myfunc2(x, y),
                 user_functions={"myfunc2": [(lambda *x: False,
                                              "Myfunc2")]}) == "myfunc2[x, y]"
예제 #3
0
 def _expr_big_minus(cls, a, z, n):
     return -1 / sqrt(1 + 1 / z) * sinh(2 * a * asinh(sqrt(z)) +
                                        2 * a * pi * I * n)
예제 #4
0
 def _expr_small_minus(cls, z):
     return asinh(sqrt(z)) / sqrt(z)
예제 #5
0
 def _expr_small_minus(cls, a, z):
     return -sqrt(z) / sqrt(1 + z) * sinh(2 * a * asinh(sqrt(z)))
예제 #6
0
 def _expr_big_minus(cls, a, z, n):
     return cosh(2 * a * asinh(sqrt(z)) + 2 * a * pi * I * n)
예제 #7
0
 def _expr_small_minus(cls, a, z):
     return cosh(2 * a * asinh(sqrt(z)))
예제 #8
0
def test_Function():
    assert mcode(f(x, y, z)) == "f[x, y, z]"
    assert mcode(sin(x) ** cos(x)) == "Sin[x]^Cos[x]"
    assert mcode(sign(x)) == "Sign[x]"

    assert mcode(atanh(x), user_functions={"atanh": "ArcTanh"}) == "ArcTanh[x]"

    assert (mcode(meijerg(((1, 1), (3, 4)), ((1,), ()), x)) ==
            "MeijerG[{{1, 1}, {3, 4}}, {{1}, {}}, x]")
    assert (mcode(hyper((1, 2, 3), (3, 4), x)) ==
            "HypergeometricPFQ[{1, 2, 3}, {3, 4}, x]")

    assert mcode(Min(x, y)) == "Min[x, y]"
    assert mcode(Max(x, y)) == "Max[x, y]"
    assert mcode(Max(x, 2)) == "Max[2, x]"  # issue sympy/sympy#15344

    assert mcode(binomial(x, y)) == "Binomial[x, y]"

    assert mcode(log(x)) == "Log[x]"
    assert mcode(tan(x)) == "Tan[x]"
    assert mcode(cot(x)) == "Cot[x]"
    assert mcode(asin(x)) == "ArcSin[x]"
    assert mcode(acos(x)) == "ArcCos[x]"
    assert mcode(atan(x)) == "ArcTan[x]"
    assert mcode(sinh(x)) == "Sinh[x]"
    assert mcode(cosh(x)) == "Cosh[x]"
    assert mcode(tanh(x)) == "Tanh[x]"
    assert mcode(coth(x)) == "Coth[x]"
    assert mcode(sech(x)) == "Sech[x]"
    assert mcode(csch(x)) == "Csch[x]"
    assert mcode(erfc(x)) == "Erfc[x]"
    assert mcode(conjugate(x)) == "Conjugate[x]"
    assert mcode(re(x)) == "Re[x]"
    assert mcode(im(x)) == "Im[x]"
    assert mcode(polygamma(x, y)) == "PolyGamma[x, y]"
    assert mcode(factorial(x)) == "Factorial[x]"
    assert mcode(factorial2(x)) == "Factorial2[x]"
    assert mcode(rf(x, y)) == "Pochhammer[x, y]"
    assert mcode(gamma(x)) == "Gamma[x]"
    assert mcode(zeta(x)) == "Zeta[x]"
    assert mcode(asinh(x)) == "ArcSinh[x]"
    assert mcode(Heaviside(x)) == "UnitStep[x]"
    assert mcode(fibonacci(x)) == "Fibonacci[x]"
    assert mcode(polylog(x, y)) == "PolyLog[x, y]"
    assert mcode(atanh(x)) == "ArcTanh[x]"

    class myfunc1(Function):
        @classmethod
        def eval(cls, x):
            pass

    class myfunc2(Function):
        @classmethod
        def eval(cls, x, y):
            pass

    pytest.raises(ValueError,
                  lambda: mcode(myfunc1(x),
                                user_functions={"myfunc1": ["Myfunc1"]}))
    assert mcode(myfunc1(x),
                 user_functions={"myfunc1": "Myfunc1"}) == "Myfunc1[x]"
    assert mcode(myfunc2(x, y),
                 user_functions={"myfunc2": [(lambda *x: False,
                                              "Myfunc2")]}) == "myfunc2[x, y]"
예제 #9
0
def heurisch(f,
             x,
             rewrite=False,
             hints=None,
             mappings=None,
             retries=3,
             degree_offset=0,
             unnecessary_permutations=None):
    """
    Compute indefinite integral using heuristic Risch algorithm.

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

    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.

    Parameters
    ==========

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

    f : Expr
        expression
    x : Symbol
        variable

    rewrite : Boolean, optional
        force rewrite 'f' in terms of 'tan' and 'tanh', default False.
    hints : None or list
        a list of functions that may appear in anti-derivate.  If
        None (default) - no suggestions at all, if empty list - try
        to figure out.

    Examples
    ========

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

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

    References
    ==========

    .. [1] Manuel Bronstein's "Poor Man's Integrator",
           http://www-sop.inria.fr/cafe/Manuel.Bronstein/pmint/index.html

    .. [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
    ========

    diofant.integrals.integrals.Integral.doit
    diofant.integrals.integrals.Integral
    diofant.integrals.heurisch.components
    """
    f = sympify(f)
    if x not in f.free_symbols:
        return f * x

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

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

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

    terms = components(f, x)

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

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

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

                elif g.is_Pow:
                    if g.base is S.Exp1:
                        M = g.exp.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.exp.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.exp.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.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):  # using copy of 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))

    # 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(cancel(g.diff(x))) for g in terms]
        denoms = [g.as_numer_denom()[1] for g in diffs]
        if all(h.is_polynomial(*V)
               for h in denoms) and _substitute(f).is_rational_function(*V):
            denom = reduce(lambda p, q: lcm(p, q, *V), denoms)
            break
    else:
        if not rewrite:
            result = heurisch(
                f,
                x,
                rewrite=True,
                hints=hints,
                unnecessary_permutations=unnecessary_permutations)

            if result is not None:
                return indep * result
        return

    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 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

    # --- 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 = itermonomials(V, A + B - 1 + degree_offset)
    else:
        monoms = itermonomials(V, A + B + degree_offset)

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

    poly_part = Add(*[
        poly_coeffs[i] * monomial for i, monomial in enumerate(ordered(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.free_symbols:
                if z in V:
                    break  # should this be: `irreducibles |= \
            else:  # set(root_factors(poly, z, filter=field))`
                continue  # and the line below deleted?
                #                          |
                #                          V
            irreducibles |= set(root_factors(poly, z, filter=field))

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

        # Note: the ordering matters here
        for poly, b in reversed(list(ordered(zip(irreducibles, B)))):
            if poly.has(*V):
                poly_coeffs.append(b)
                log_part.append(b * log(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 cancelation is slow
        # due to slow polynomial GCD algorithms. If this gets improved then
        # revise this code.
        candidate = poly_part / poly_denom + Add(*log_part)
        h = F - _derivation(candidate) / denom
        raw_numer = h.as_numer_denom()[0]

        # Rewrite raw_numer as a polynomial in K[coeffs][V] where K is a field
        # that we have to determine. We can't use simply atoms() because log(3),
        # sqrt(y) and similar expressions can appear, leading to non-trivial
        # domains.
        syms = set(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(*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
        else:
            ground, _ = construct_domain(non_syms, field=True)

        coeff_ring = PolyRing(poly_coeffs, ground)
        ring = PolyRing(V, coeff_ring)

        numer = ring.from_expr(raw_numer)

        solution = solve_lin_sys(numer.coeffs(), coeff_ring)

        if solution is None:
            return
        else:
            solution = [(coeff_ring.symbols[coeff_ring.index(k)], v.as_expr())
                        for k, v in solution.items()]
            return candidate.subs(solution).subs(
                list(zip(poly_coeffs, [S.Zero] * len(poly_coeffs))))

    if not (F.free_symbols - set(V)):
        solution = _integrate('Q')

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

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

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

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

            if result is not None:
                return indep * result

        return