def test_apart(): assert apart(1 / (x + 2) / (x + 1), x) == 1 / (1 + x) - 1 / (2 + x) assert apart(1 / (x + 1) / (x + 5), x) == -1 / (5 + x) / 4 + 1 / (1 + x) / 4 f = apart(1 / (x - y) / (x - z), x) assert f.subs({y: 1, z: 2}) == apart(1 / (x - 1) / (x - 2), x) assert apart((E * x + 2) / (x - pi) * (x - 1), x) in [ 2 - E + E * pi + E * x - 1 / (x - pi) * (2 - 2 * pi + E * pi - E * pi ** 2), 2 - E + E * pi + E * x + 1 / (x - pi) * (-2 + 2 * pi - E * pi + E * pi ** 2), ] M = Matrix(2, 2, lambda i, j: 1 / (x - (i + 1)) / (x - (1 - j))) assert apart(M, x) in [ Matrix([[(x - 1) ** (-2), -1 / x - 1 / (1 - x)], [1 / (1 - x) - 1 / (2 - x), -S.Half / x - S.Half / (2 - x)]]), Matrix( [[(-1 + x) ** (-2), -1 / x + 1 / (-1 + x)], [-1 / (-1 + x) + 1 / (-2 + x), -S.Half / x + S.Half / (-2 + x)]] ), ] assert apart(Eq((x ** 2 + 1) / (x + 1), sin(x)), x) == Eq(x - 1 + 2 / (x + 1), sin(x)) assert str(apart(1 / (1 + x ** 5), x, evaluate=False)) in [ "RootSum(Lambda(_a, -1/5/(x - _a)*_a), x**5 + 1, x)", "RootSum(Lambda(_a, -_a/(5*(x - _a))), x**5 + 1, x)", ]
def test_apart(): raises(ValueError, "apart(1/(x+1)/(y+2))") assert apart(1) == 1 assert apart(1, x) == 1 assert apart(1 / (x + 2) / (x + 1)) == 1 / (1 + x) - 1 / (2 + x) assert apart(1 / (x + 1) / (x + 5)) == -1 / (5 + x) / 4 + 1 / (1 + x) / 4 f = apart(1 / (x - y) / (x - z), x) assert f.subs({y: 1, z: 2}) == apart(1 / (x - 1) / (x - 2), x) assert apart((E * x + 2) / (x - pi) * (x - 1), x) in [ 2 - E + E * pi + E * x - 1 / (x - pi) * (2 - 2 * pi + E * pi - E * pi**2), 2 - E + E * pi + E * x + 1 / (x - pi) * (-2 + 2 * pi - E * pi + E * pi**2), ] M = Matrix(2, 2, lambda i, j: 1 / (x - (i + 1)) / (x - (1 - j))) assert apart(M, x) in [ Matrix([ [(x - 1)**(-2), -1 / x - 1 / (1 - x)], [1 / (1 - x) - 1 / (2 - x), -S.Half / x - S.Half / (2 - x)], ]), Matrix([ [(-1 + x)**(-2), -1 / x + 1 / (-1 + x)], [-1 / (-1 + x) + 1 / (-2 + x), -S.Half / x + S.Half / (-2 + x)], ]), ] assert apart(Eq((x**2+1)/(x+1), sin(x)), x) == \ Eq(x - 1 + 2/(x+1), sin(x)) assert str(apart(1 / (1 + x**5), x, evaluate=False)) in [ "RootSum(Lambda(_a, -1/5/(x - _a)*_a), x**5 + 1, x, domain='ZZ')", "RootSum(Lambda(_a, -_a/(5*(x - _a))), x**5 + 1, x, domain='ZZ')" ]
def apart(self, z=None, **args): """See the apart function in sympy.simplify""" from sympy.simplify import apart return apart(self, z=None, **args)
def _eval_integral(self, f, x): """Calculate the antiderivative to the function f(x). This is a powerful function that should in theory be able to integrate everything that can be integrated. If you find something, that it doesn't, it is easy to implement it. (1) Simple heuristics (based on pattern matching and integral table): - most frequently used functions (eg. polynomials) - functions non-integrable by any of the following algorithms (eg. exp(-x**2)) (2) Integration of rational functions: (a) using apart() - apart() is full partial fraction decomposition procedure based on Bronstein-Salvy algorithm. It gives formal decomposition with no polynomial factorization at all (so it's fast and gives the most general results). However it needs much better implementation of RootsOf class (if fact any implementation). (b) using Trager's algorithm - possibly faster than (a) but needs implementation :) (3) Whichever implementation of pmInt (Mateusz, Kirill's or a combination of both). - this way we can handle efficiently huge class of elementary and special functions (4) Recursive Risch algorithm as described in Bronstein's integration tutorial. - this way we can handle those integrable functions for which (3) fails (5) Powerful heuristics based mostly on user defined rules. - handle complicated, rarely used cases """ # if it is a poly(x) then let the polynomial integrate itself (fast) # # It is important to make this check first, otherwise the other code # will return a sympy expression instead of a Polynomial. # # see Polynomial for details. if isinstance(f, Poly): return f.integrate(x) # Piecewise antiderivatives need to call special integrate. if isinstance(f,Piecewise): return f._eval_integral(x) # let's cut it short if `f` does not depend on `x` if not f.has(x): return f*x # try to convert to poly(x) and then integrate if successful (fast) poly = f.as_poly(x) if poly is not None: return poly.integrate(x).as_basic() # since Integral(f=g1+g2+...) == Integral(g1) + Integral(g2) + ... # we are going to handle Add terms separately, # if `f` is not Add -- we only have one term if not f.is_Add: f = [f] parts = [] if isinstance(f, Basic): f = f.args for g in f: coeff, g = g.as_independent(x) # g(x) = const if g is S.One: parts.append(coeff * x) continue # c # g(x) = (a*x+b) if g.is_Pow and not g.exp.has(x): a = Wild('a', exclude=[x]) b = Wild('b', exclude=[x]) M = g.base.match(a*x + b) if M is not None: if g.exp == -1: h = C.log(g.base) else: h = g.base**(g.exp+1) / (g.exp+1) parts.append(coeff * h / M[a]) continue # poly(x) # g(x) = ------- # poly(x) if g.is_fraction(x): h = self._eval_integral(apart(g, x), x) parts.append(coeff * h) continue # g(x) = Mul(trig) h = trigintegrate(g, x) if h is not None: parts.append(coeff * h) continue # g(x) has at least a DiracDelta term h = deltaintegrate(g,x) if h is not None: parts.append(coeff * h) continue # fall back to the more general algorithm h = heurisch(g, x, hints=[]) if h is not None: parts.append(coeff * h) else: return None return C.Add(*parts)
def _eval_integral(self, f, x): """Calculate the antiderivative to the function f(x). This is a powerful function that should in theory be able to integrate everything that can be integrated. If you find something, that it doesn't, it is easy to implement it. (1) Simple heuristics (based on pattern matching and integral table): - most frequently used functions (eg. polynomials) - functions non-integrable by any of the following algorithms (eg. exp(-x**2)) (2) Integration of rational functions: (a) using apart() - apart() is full partial fraction decomposition procedure based on Bronstein-Salvy algorithm. It gives formal decomposition with no polynomial factorization at all (so it's fast and gives the most general results). However it needs much better implementation of RootsOf class (if fact any implementation). (b) using Trager's algorithm - possibly faster than (a) but needs implementation :) (3) Whichever implementation of pmInt (Mateusz, Kirill's or a combination of both). - this way we can handle efficiently huge class of elementary and special functions (4) Recursive Risch algorithm as described in Bronstein's integration tutorial. - this way we can handle those integrable functions for which (3) fails (5) Powerful heuristics based mostly on user defined rules. - handle complicated, rarely used cases """ # if it is a poly(x) then let the polynomial integrate itself (fast) # # It is important to make this check first, otherwise the other code # will return a sympy expression instead of a Polynomial. # # see Polynomial for details. if isinstance(f, Poly): return f.integrate(x) # Piecewise antiderivatives need to call special integrate. if isinstance(f, Piecewise): return f._eval_integral(x) # let's cut it short if `f` does not depend on `x` if not f.has(x): return f * x # try to convert to poly(x) and then integrate if successful (fast) poly = f.as_poly(x) if poly is not None: return poly.integrate(x).as_basic() # since Integral(f=g1+g2+...) == Integral(g1) + Integral(g2) + ... # we are going to handle Add terms separately, # if `f` is not Add -- we only have one term if not f.is_Add: f = [f] parts = [] if isinstance(f, Basic): f = f.args for g in f: coeff, g = g.as_independent(x) # g(x) = const if g is S.One: parts.append(coeff * x) continue # c # g(x) = (a*x+b) if g.is_Pow and not g.exp.has(x): a = Wild('a', exclude=[x]) b = Wild('b', exclude=[x]) M = g.base.match(a * x + b) if M is not None: if g.exp == -1: h = C.log(g.base) else: h = g.base**(g.exp + 1) / (g.exp + 1) parts.append(coeff * h / M[a]) continue # poly(x) # g(x) = ------- # poly(x) if g.is_fraction(x): h = self._eval_integral(apart(g, x), x) parts.append(coeff * h) continue # g(x) = Mul(trig) h = trigintegrate(g, x) if h is not None: parts.append(coeff * h) continue # g(x) has at least a DiracDelta term h = deltaintegrate(g, x) if h is not None: parts.append(coeff * h) continue # fall back to the more general algorithm h = heurisch(g, x, hints=[]) if h is not None: parts.append(coeff * h) else: return None return C.Add(*parts)