def eval(cls, arg): if arg is S.NaN: return S.NaN elif arg.is_real: return arg elif arg.is_Function and arg.func == conjugate: return re(arg.args[0]) else: included, reverted, excluded = [], [], [] arg = make_list(arg, C.Add) for term in arg: coeff = term.as_coefficient(S.ImaginaryUnit) if coeff is not None: if not coeff.is_real: reverted.append(coeff) elif not term.has(S.ImaginaryUnit) and term.is_real: excluded.append(term) else: included.append(term) if len(arg) != len(included): a, b, c = map(lambda xs: C.Add(*xs), [included, reverted, excluded]) return cls(a) - im(b) + c
def could_extract_minus_sign(self): """Canonical way to choose an element in the set {e, -e} where e is any expression. If the canonical element is e, we have e.could_extract_minus_sign() == True, else e.could_extract_minus_sign() == False. For any expression, the set {e.could_extract_minus_sign(), (-e).could_extract_minus_sign()} must be {True, False}. >>> from sympy.abc import x, y >>> (x-y).could_extract_minus_sign() != (y-x).could_extract_minus_sign() True """ from sympy.utilities.iterables import make_list negative_self = -self self_has_minus = (self.extract_multiplicatively(-1) != None) negative_self_has_minus = ((negative_self).extract_multiplicatively(-1) != None) if self_has_minus != negative_self_has_minus: return self_has_minus else: if self.is_Add: # We choose the one with less arguments with minus signs all_args = len(self.args) negative_args = len([ False for arg in self.args if arg.could_extract_minus_sign() ]) positive_args = all_args - negative_args if positive_args > negative_args: return False elif positive_args < negative_args: return True elif self.is_Mul: # We choose the one with an odd number of minus signs num, den = self.as_numer_denom() args = (make_list(num, Mul)) + (make_list(den, Mul)) arg_signs = [arg.could_extract_minus_sign() for arg in args] negative_args = filter(None, arg_signs) return len(negative_args) % 2 == 1 # As a last resort, we choose the one with greater hash return hash(self) < hash(negative_self)
def could_extract_minus_sign(self): """Canonical way to choose an element in the set {e, -e} where e is any expression. If the canonical element is e, we have e.could_extract_minus_sign() == True, else e.could_extract_minus_sign() == False. For any expression, the set {e.could_extract_minus_sign(), (-e).could_extract_minus_sign()} must be {True, False}. >>> from sympy.abc import x, y >>> (x-y).could_extract_minus_sign() != (y-x).could_extract_minus_sign() True """ from sympy.utilities.iterables import make_list negative_self = -self self_has_minus = (self.extract_multiplicatively(-1) != None) negative_self_has_minus = ((negative_self).extract_multiplicatively(-1) != None) if self_has_minus != negative_self_has_minus: return self_has_minus else: if self.is_Add: # We choose the one with less arguments with minus signs all_args = len(self.args) negative_args = len([False for arg in self.args if arg.could_extract_minus_sign()]) positive_args = all_args - negative_args if positive_args > negative_args: return False elif positive_args < negative_args: return True elif self.is_Mul: # We choose the one with an odd number of minus signs num, den = self.as_numer_denom() args = (make_list(num, Mul)) + (make_list(den, Mul)) arg_signs = [arg.could_extract_minus_sign() for arg in args] negative_args = filter(None, arg_signs) return len(negative_args) % 2 == 1 # As a last resort, we choose the one with greater hash return hash(self) < hash(negative_self)
def _expandsums(sums): """ Helper function for _eval_expand_mul. sums must be a list of instances of Basic. """ L = len(sums) if L == 1: return sums[0].args terms = [] left = Mul._expandsums(sums[:L//2]) right = Mul._expandsums(sums[L//2:]) terms = [Mul(a, b) for a in left for b in right] added = Add(*terms) return make_list(added, Add) #it may have collapsed down to one term
def _expandsums(sums): """ Helper function for _eval_expand_mul. sums must be a list of instances of Basic. """ from sympy.utilities.iterables import make_list L = len(sums) if L == 1: return sums[0].args terms = [] left = Mul._expandsums(sums[:L // 2]) right = Mul._expandsums(sums[L // 2:]) terms = [Mul(a, b) for a in left for b in right] added = Add(*terms) return make_list(added, Add) #it may have collapsed down to one term
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() equations = {} for term in make_list(numer, Add): coeff, dependent = term.as_independent(*V) if dependent in equations: equations[dependent] += coeff else: equations[dependent] = coeff solution = solve(equations.values(), *coeffs) if solution is not None: return (solution, candidate, coeffs) else: return None
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, domain=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() equations = {} for term in make_list(numer, Add): coeff, dependent = term.as_independent(*V) if dependent in equations: equations[dependent] += coeff else: equations[dependent] = coeff solution = solve(equations.values(), *coeffs) if solution is not None: return (solution, candidate, coeffs) else: return None
def _eval_integral(self, f, x): """Calculate the anti-derivative 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 (e.g. polynomials) - functions non-integrable by any of the following algorithms (e.g. 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 f.func is 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 parts = [] for g in make_list(f, Add): 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_rational_function(x): parts.append(coeff * ratint(g, x)) 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 anti-derivative 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 (e.g. polynomials) - functions non-integrable by any of the following algorithms (e.g. 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 f.func is 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().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 parts = [] args = make_list(f, Add) for g in args: 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_rational_function(x): parts.append(coeff * ratint(g, x)) 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 we failed maybe it was because we had # a product that could have been expanded, # so let's try an expansion of the whole # thing before giving up; we don't try this # out the outset because there are things # that cannot be solved unless they are # NOT expanded e.g., x**x*(1+log(x)). There # should probably be a checker somewhere in this # routine to look for such cases and try to do # collection on the expressions if they are already # in an expanded form if not h and len(args) == 1: f = f.expand(mul=True, deep=False) if f.is_Add: return self._eval_integral(f, x) if h is not None: parts.append(coeff * h) else: return None return C.Add(*parts)