def _expr_big(cls, z, n): if n.is_even: return (n - Rational(1, 2)) * pi * I + log( sqrt(z) / 2) + I * asin(1 / sqrt(z)) else: return (n - Rational(1, 2)) * pi * I + log( sqrt(z) / 2) - I * asin(1 / sqrt(z))
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]" 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]"
def _expr_small(cls, z): return asin(sqrt(z)) / sqrt(z)
def _expr_small(cls, a, z): return sqrt(z) / sqrt(1 - z) * sin(2 * a * asin(sqrt(z)))
def _expr_small(cls, a, z): return cos(2 * a * asin(sqrt(z)))
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]"
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