def _dict_from_expr(expr, opt): """Transform an expression into a multinomial form. """ if expr.is_commutative is False: raise PolynomialError('non-commutative expressions are not supported') def _is_expandable_pow(expr): return (expr.is_Pow and expr.exp.is_positive and expr.exp.is_Integer and expr.base.is_Add) if opt.expand is not False: expr = expr.expand() # TODO: Integrate this into expand() itself while any(_is_expandable_pow(i) or i.is_Mul and any(_is_expandable_pow(j) for j in i.args) for i in Add.make_args(expr)): expr = expand_multinomial(expr) while any(i.is_Mul and any(j.is_Add for j in i.args) for i in Add.make_args(expr)): expr = expand_mul(expr) if opt.gens: rep, gens = _dict_from_expr_if_gens(expr, opt) else: rep, gens = _dict_from_expr_no_gens(expr, opt) return rep, opt.clone({'gens': gens})
def _sqrtdenest34(expr): """Helper that denests the square root of three or four surds. Examples ======== >>> from sympy import sqrt >>> from sympy.simplify.sqrtdenest import _sqrtdenest34 >>> _sqrtdenest34(sqrt(-72*sqrt(2) + 158*sqrt(5) + 498)) -sqrt(10) + sqrt(2) + 9 + 9*sqrt(5) >>> _sqrtdenest34(sqrt(12 + 2*sqrt(6) + 2*sqrt(14) + 2*sqrt(21))) sqrt(2) + sqrt(3) + sqrt(7) References ========== - D. J. Jeffrey and A. D. Rich, 'Symplifying Square Roots of Square Roots by Denesting' """ from sympy.simplify.simplify import radsimp if expr.base < 0: return sqrt(-1)*_sqrtdenest34(sqrt(-expr.base)) # a should be > b so we sort; this also makes the process canonical args = sorted(expr.base.args, reverse=True) a = Add._from_args(args[:2]) b = Add._from_args(args[2:]) c = _sqrtdenest1(sqrt(_mexpand(a**2 - b**2))) if sqrt_depth(c) > 1: return expr d = _sqrtdenest1(sqrt(a + c)) if sqrt_depth(d) > 1: return expr return _mexpand(radsimp((d/sqrt(2) + b/(sqrt(2)*d))))
def interpolate(data, x): """ Construct an interpolating polynomial for the data points. Examples ======== >>> from sympy.polys.polyfuncs import interpolate >>> from sympy.abc import x A list is interpreted as though it were paired with a range starting from 1: >>> interpolate([1, 4, 9, 16], x) x**2 This can be made explicit by giving a list of coordinates: >>> interpolate([(1, 1), (2, 4), (3, 9)], x) x**2 The (x, y) coordinates can also be given as keys and values of a dictionary (and the points need not be equispaced): >>> interpolate([(-1, 2), (1, 2), (2, 5)], x) x**2 + 1 >>> interpolate({-1: 2, 1: 2, 2: 5}, x) x**2 + 1 """ n = len(data) poly = None if isinstance(data, dict): X, Y = list(zip(*data.items())) poly = interpolating_poly(n, x, X, Y) else: if isinstance(data[0], tuple): X, Y = list(zip(*data)) poly = interpolating_poly(n, x, X, Y) else: Y = list(data) numert = Mul(*[(x - i) for i in range(1, n + 1)]) denom = -factorial(n - 1) if n%2 == 0 else factorial(n - 1) coeffs = [] for i in range(1, n + 1): coeffs.append(numert/(x - i)/denom) denom = denom/(i - n)*i poly = Add(*[coeff*y for coeff, y in zip(coeffs, Y)]) return poly.expand()
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 = Add.make_args(arg) 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: Add(*xs), [included, reverted, excluded]) return cls(a) - im(b) + c
def _set_function(f, self): expr = f.expr if not isinstance(expr, Expr): return if len(f.variables) > 1: return n = f.variables[0] # f(x) + c and f(-x) + c cover the same integers # so choose the form that has the fewest negatives c = f(0) fx = f(n) - c f_x = f(-n) - c neg_count = lambda e: sum(_coeff_isneg(_) for _ in Add.make_args(e)) if neg_count(f_x) < neg_count(fx): expr = f_x + c a = Wild('a', exclude=[n]) b = Wild('b', exclude=[n]) match = expr.match(a*n + b) if match and match[a]: # canonical shift expr = match[a]*n + match[b] % match[a] if expr != f.expr: return ImageSet(Lambda(n, expr), S.Integers)
def eval(cls, arg): from sympy import AccumBounds, im def _eval(arg): if arg is S.Infinity or arg is S.NegativeInfinity: return AccumBounds(0, 1) if arg.is_integer: return S.Zero if arg.is_number: if arg is S.NaN: return S.NaN elif arg is S.ComplexInfinity: return None else: return arg - floor(arg) return cls(arg, evaluate=False) terms = Add.make_args(arg) real, imag = S.Zero, S.Zero for t in terms: # Two checks are needed for complex arguments # see issue-7649 for details if t.is_imaginary or (S.ImaginaryUnit*t).is_real: i = im(t) if not i.has(S.ImaginaryUnit): imag += i else: real += t else: real += t real = _eval(real) imag = _eval(imag) return real + S.ImaginaryUnit*imag
def eval(cls, arg): if arg is S.NaN: return S.NaN elif arg.is_real: return S.Zero elif arg.is_imaginary: return -S.ImaginaryUnit * arg elif arg.is_Function and arg.func == conjugate: return -im(arg.args[0]) else: included, reverted, excluded = [], [], [] args = Add.make_args(arg) for term in args: coeff = term.as_coefficient(S.ImaginaryUnit) if coeff is not None: if not coeff.is_real: reverted.append(coeff) else: excluded.append(coeff) elif term.has(S.ImaginaryUnit) or not term.is_real: # Try to do some advanced expansion. If # impossible, don't try to do im(arg) again # (because this is what we are trying to do now). real_imag = term.as_real_imag(ignore=arg) if real_imag: excluded.append(real_imag[1]) else: included.append(term) if len(args) != len(included): a, b, c = map(lambda xs: Add(*xs), [included, reverted, excluded]) return cls(a) + re(b) + c
def apart(f, x=None, full=False, **options): """ Compute partial fraction decomposition of a rational function. Given a rational function ``f`` compute partial fraction decomposition of ``f``. Two algorithms are available: one is based on undetermined coefficients method and the other is Bronstein's full partial fraction decomposition algorithm. Examples ======== >>> from sympy.polys.partfrac import apart >>> from sympy.abc import x, y >>> apart(y/(x + 2)/(x + 1), x) -y/(x + 2) + y/(x + 1) """ allowed_flags(options, []) f = sympify(f) if f.is_Atom: return f else: P, Q = f.as_numer_denom() options = set_defaults(options, extension=True) (P, Q), opt = parallel_poly_from_expr((P, Q), x, **options) if P.is_multivariate: raise NotImplementedError( "multivariate partial fraction decomposition") common, P, Q = P.cancel(Q) poly, P = P.div(Q, auto=True) P, Q = P.rat_clear_denoms(Q) if Q.degree() <= 1: partial = P/Q else: if not full: partial = apart_undetermined_coeffs(P, Q) else: partial = apart_full_decomposition(P, Q) terms = S.Zero for term in Add.make_args(partial): if term.has(RootSum): terms += term else: terms += factor(term) return common*(poly.as_expr() + terms)
def doit(self, **hints): """Evaluates limit""" e, z, z0, dir = self.args if hints.get('deep', True): e = e.doit(**hints) z = z.doit(**hints) z0 = z0.doit(**hints) if e == z: return z0 if not e.has(z): return e # gruntz fails on factorials but works with the gamma function # If no factorial term is present, e should remain unchanged. # factorial is defined to be zero for negative inputs (which # differs from gamma) so only rewrite for positive z0. if z0.is_positive: e = e.rewrite(factorial, gamma) if e.is_Mul: if abs(z0) is S.Infinity: # XXX todo: this should probably be stated in the # negative -- i.e. to exclude expressions that should # not be handled this way but I'm not sure what that # condition is; when ok is True it means that the leading # term approach is going to succeed (hopefully) ok = lambda w: (z in w.free_symbols and any(a.is_polynomial(z) or any(z in m.free_symbols and m.is_polynomial(z) for m in Mul.make_args(a)) for a in Add.make_args(w))) if all(ok(w) for w in e.as_numer_denom()): u = Dummy(positive=(z0 is S.Infinity)) inve = e.subs(z, 1/u) r = limit(inve.as_leading_term(u), u, S.Zero, "+" if z0 is S.Infinity else "-") if isinstance(r, Limit): return self else: return r if e.is_Order: return Order(limit(e.expr, z, z0), *e.args[1:]) try: r = gruntz(e, z, z0, dir) if r is S.NaN: raise PoleError() except (PoleError, ValueError): r = heuristics(e, z, z0, dir) if r is None: return self return r
def __new__(cls, *args, **kwargs): args = list(map(sympify, args)) check = kwargs.get('check', False) obj = Basic.__new__(cls, *args) if check: if all(not isinstance(i, MatrixExpr) for i in args): return Add.fromiter(args) validate(*args) return obj
def __new__(cls, expr, *symbols, **assumptions): expr = sympify(expr).expand() if expr is S.NaN: return S.NaN if symbols: symbols = map(sympify, symbols) if not all(isinstance(s, Symbol) for s in symbols): raise NotImplementedError('Order at points other than 0 not supported.') else: symbols = list(expr.free_symbols) if expr.is_Order: new_symbols = list(expr.variables) for s in symbols: if s not in new_symbols: new_symbols.append(s) if len(new_symbols) == len(expr.variables): return expr symbols = new_symbols elif symbols: if expr.is_Add: lst = expr.extract_leading_order(*symbols) expr = Add(*[f.expr for (e,f) in lst]) elif expr: expr = expr.as_leading_term(*symbols) coeff, terms = expr.as_coeff_mul() expr = Mul(*[t for t in terms if t.has(*symbols)]) if expr is S.Zero: return expr elif not expr.has(*symbols): expr = S.One # create Order instance: symbols.sort(Basic.compare) obj = Expr.__new__(cls, expr, *symbols, **assumptions) return obj
def sum_simplify(s): """Main function for Sum simplification""" from sympy.concrete.summations import Sum from sympy.core.function import expand terms = Add.make_args(expand(s)) s_t = [] # Sum Terms o_t = [] # Other Terms for term in terms: if isinstance(term, Mul): other = 1 sum_terms = [] if not term.has(Sum): o_t.append(term) continue mul_terms = Mul.make_args(term) for mul_term in mul_terms: if isinstance(mul_term, Sum): r = mul_term._eval_simplify() sum_terms.extend(Add.make_args(r)) else: other = other * mul_term if len(sum_terms): #some simplification may have happened #use if so s_t.append(Mul(*sum_terms) * other) else: o_t.append(other) elif isinstance(term, Sum): #as above, we need to turn this into an add list r = term._eval_simplify() s_t.extend(Add.make_args(r)) else: o_t.append(term) result = Add(sum_combine(s_t), *o_t) return result
def as_terms(self): """Transform an expression to a list of terms. """ from sympy.core import Add, Mul, S from sympy.core.exprtools import decompose_power gens, terms = set([]), [] for term in Add.make_args(self): coeff, _term = term.as_coeff_Mul() coeff = complex(coeff) cpart, ncpart = {}, [] if _term is not S.One: for factor in Mul.make_args(_term): if factor.is_number: try: coeff *= complex(factor) except ValueError: pass else: continue if factor.is_commutative: base, exp = decompose_power(factor) cpart[base] = exp gens.add(base) else: ncpart.append(factor) coeff = coeff.real, coeff.imag ncpart = tuple(ncpart) terms.append((term, (coeff, cpart, ncpart))) gens = sorted(gens, key=Basic.sorted_key) k, indices = len(gens), {} for i, g in enumerate(gens): indices[g] = i result = [] for term, (coeff, cpart, ncpart) in terms: monom = [0]*k for base, exp in cpart.iteritems(): monom[indices[base]] = exp result.append((term, (coeff, tuple(monom), ncpart))) return result, gens
def sum_simplify(s): """Main function for Sum simplification""" from sympy.concrete.summations import Sum terms = Add.make_args(s) s_t = [] # Sum Terms o_t = [] # Other Terms for term in terms: if isinstance(term, Mul): constant = 1 other = 1 s = 0 n_sum_terms = 0 for j in range(len(term.args)): if isinstance(term.args[j], Sum): s = term.args[j] n_sum_terms = n_sum_terms + 1 elif term.args[j].is_number == True: constant = constant * term.args[j] else: other = other * term.args[j] if other == 1 and n_sum_terms == 1: # Insert the constant inside the Sum s_t.append(Sum(constant * s.function, *s.limits)) elif other != 1 and n_sum_terms == 1: o_t.append(other * Sum(constant * s.function, *s.limits)) else: o_t.append(term) elif isinstance(term, Sum): s_t.append(term) else: o_t.append(term) used = [False] * len(s_t) for method in range(2): for i, s_term1 in enumerate(s_t): if not used[i]: for j, s_term2 in enumerate(s_t): if not used[j] and i != j: temp = sum_add(s_term1, s_term2, method) if isinstance(temp, Sum): s_t[i] = temp s_term1 = s_t[i] used[j] = True result = Add(*o_t) for i, s_term in enumerate(s_t): if not used[i]: result = Add(result, s_term) return result
def eval(cls, arg): from sympy import im if arg.is_integer or arg.is_finite is False: return arg if arg.is_imaginary or (S.ImaginaryUnit*arg).is_real: i = im(arg) if not i.has(S.ImaginaryUnit): return cls(i)*S.ImaginaryUnit return cls(arg, evaluate=False) v = cls._eval_number(arg) if v is not None: return v # Integral, numerical, symbolic part ipart = npart = spart = S.Zero # Extract integral (or complex integral) terms terms = Add.make_args(arg) for t in terms: if t.is_integer or (t.is_imaginary and im(t).is_integer): ipart += t elif t.has(Symbol): spart += t else: npart += t if not (npart or spart): return ipart # Evaluate npart numerically if independent of spart if npart and ( not spart or npart.is_real and (spart.is_imaginary or (S.ImaginaryUnit*spart).is_real) or npart.is_imaginary and spart.is_real): try: r, i = get_integer_part( npart, cls._dir, {}, return_ints=True) ipart += Integer(r) + Integer(i)*S.ImaginaryUnit npart = S.Zero except (PrecisionExhausted, NotImplementedError): pass spart += npart if not spart: return ipart elif spart.is_imaginary or (S.ImaginaryUnit*spart).is_real: return ipart + cls(im(spart), evaluate=False)*S.ImaginaryUnit else: return ipart + cls(spart, evaluate=False)
def _sqrtdenest34(expr): """Helper that denests the square root of three or four surds. Examples ======== >>> from sympy import sqrt >>> from sympy.simplify.sqrtdenest import _sqrtdenest34 >>> _sqrtdenest34(sqrt(-72*sqrt(2) + 158*sqrt(5) + 498)) -sqrt(10) + sqrt(2) + 9 + 9*sqrt(5) >>> _sqrtdenest34(sqrt(12 + 2*sqrt(6) + 2*sqrt(14) + 2*sqrt(21))) sqrt(2) + sqrt(3) + sqrt(7) References ========== - D. J. Jeffrey and A. D. Rich, 'Symplifying Square Roots of Square Roots by Denesting' """ from sympy.simplify.simplify import radsimp if not is_sqrt(expr): return expr a_plus_b = expr.base if not (a_plus_b.is_Add and len(a_plus_b.args) > 2): return expr if a_plus_b < 0: return sqrt(-1)*_sqrtdenest34(sqrt(-a_plus_b)) args = a_plus_b.args a = Add._from_args(args[:2]) b = Add._from_args(args[2:]) if a < b: # we want a > b; since a + b > 0 then a**2 > b**2, too a, b = b, a c = _sqrtdenest1(sqrt(_mexpand(a**2 - b**2))) if sqrt_depth(c) > 1: return expr d = _sqrtdenest1(sqrt(a + c)) if sqrt_depth(d) > 1: return expr return _mexpand(radsimp((d/sqrt(2) + b/(sqrt(2)*d))))
def _parallel_dict_from_expr_if_gens(exprs, opt): """Transform expressions into a multinomial form given generators. """ k, indices = len(opt.gens), {} for i, g in enumerate(opt.gens): indices[g] = i polys = [] for expr in exprs: poly = {} if expr.is_Equality: expr = expr.lhs - expr.rhs for term in Add.make_args(expr): coeff, monom = [], [0]*k for factor in Mul.make_args(term): if not _not_a_coeff(factor) and factor.is_Number: coeff.append(factor) else: try: if opt.series is False: base, exp = decompose_power(factor) if exp < 0: exp, base = -exp, Pow(base, -S.One) else: base, exp = decompose_power_rat(factor) monom[indices[base]] = exp except KeyError: if not factor.free_symbols.intersection(opt.gens): coeff.append(factor) else: raise PolynomialError("%s contains an element of " "the set of generators." % factor) monom = tuple(monom) if monom in poly: poly[monom] += Mul(*coeff) else: poly[monom] = Mul(*coeff) polys.append(poly) return polys, opt.gens
def _sqrt_match(p): """Return [a, b, r] for p.match(a + b*sqrt(r)) where, in addition to matching, sqrt(r) also has then maximal sqrt_depth among addends of p. Examples ======== >>> from sympy.functions.elementary.miscellaneous import sqrt >>> from sympy.simplify.sqrtdenest import _sqrt_match >>> _sqrt_match(1 + sqrt(2) + sqrt(2)*sqrt(3) + 2*sqrt(1+sqrt(5))) [1 + sqrt(2) + sqrt(6), 2, 1 + sqrt(5)] """ p = _mexpand(p) if p.is_Number: res = (p, S.Zero, S.Zero) elif p.is_Add: pargs = list(p.args) # to make the process canonical, the argument is included in the tuple # so when the max is selected, it will be the largest arg having a # given depth v = [(sqrt_depth(x), x, i) for i, x in enumerate(pargs)] nmax = max(v) if nmax[0] == 0: res = [] else: depth, _, i = nmax r = pargs.pop(i) a = Add._from_args(pargs) b = S.One if r.is_Mul: bv = [] rv = [] for x in r.args: if sqrt_depth(x) < depth: bv.append(x) else: rv.append(x) b = Mul._from_args(bv) r = Mul._from_args(rv) res = (a, b, r**2) else: b, r = p.as_coeff_Mul() if is_sqrt(r): res = (S.Zero, b, r**2) else: res = [] return list(res)
def __new__(cls, *args, **kwargs): if not args: return GenericZeroMatrix() # This must be removed aggressively in the constructor to avoid # TypeErrors from GenericZeroMatrix().shape args = filter(lambda i: GenericZeroMatrix() != i, args) args = list(map(sympify, args)) check = kwargs.get('check', False) obj = Basic.__new__(cls, *args) if check: if all(not isinstance(i, MatrixExpr) for i in args): return Add.fromiter(args) validate(*args) return obj
def eval(cls, arg): if arg.is_integer: return arg if arg.is_imaginary: return cls(C.im(arg))*S.ImaginaryUnit v = cls._eval_number(arg) if v is not None: return v # Integral, numerical, symbolic part ipart = npart = spart = S.Zero # Extract integral (or complex integral) terms terms = Add.make_args(arg) for t in terms: if t.is_integer or (t.is_imaginary and C.im(t).is_integer): ipart += t elif t.has(C.Symbol): spart += t else: npart += t if not (npart or spart): return ipart # Evaluate npart numerically if independent of spart if npart and ( not spart or npart.is_real and spart.is_imaginary or npart.is_imaginary and spart.is_real): try: re, im = get_integer_part( npart, cls._dir, {}, return_ints=True) ipart += C.Integer(re) + C.Integer(im)*S.ImaginaryUnit npart = S.Zero except (PrecisionExhausted, NotImplementedError): pass spart = npart + spart if not spart: return ipart elif spart.is_imaginary: return ipart + cls(C.im(spart), evaluate=False)*S.ImaginaryUnit else: return ipart + cls(spart, evaluate=False)
def __new__(cls, expr, *symbols, **assumptions): expr = sympify(expr).expand() if expr is S.NaN: return S.NaN if symbols: symbols = map(sympify, symbols) if not all(isinstance(s, Symbol) for s in symbols): raise NotImplementedError( 'Order at points other than 0 not supported.') else: symbols = list(expr.free_symbols) if expr.is_Order: new_symbols = list(expr.variables) for s in symbols: if s not in new_symbols: new_symbols.append(s) if len(new_symbols) == len(expr.variables): return expr symbols = new_symbols elif symbols: if expr.is_Add: lst = expr.extract_leading_order(*symbols) expr = Add(*[f.expr for (e, f) in lst]) elif expr: if len(symbols) > 1 or expr.is_commutative is False: # TODO # We cannot use compute_leading_term because that only # works in one symbol. expr = expr.as_leading_term(*symbols) else: expr = expr.compute_leading_term(symbols[0]) terms = expr.as_coeff_mul(*symbols)[1] s = set(symbols) expr = Mul(*[t for t in terms if s & t.free_symbols]) if expr is S.Zero: return expr elif not expr.has(*symbols): expr = S.One # create Order instance: symbols.sort(key=cmp_to_key(Basic.compare)) obj = Expr.__new__(cls, expr, *symbols, **assumptions) return obj
def _parallel_dict_from_expr_if_gens(exprs, opt): """Transform expressions into a multinomial form given generators. """ k, indices = len(opt.gens), {} for i, g in enumerate(opt.gens): indices[g] = i polys = [] for expr in exprs: poly = {} for term in Add.make_args(expr): coeff, monom = [], [0]*k for factor in Mul.make_args(term): if factor.is_Number: coeff.append(factor) else: try: base, exp = decompose_power(factor) if exp < 0: exp, base = -exp, Pow(base, -S.One) monom[indices[base]] = exp except KeyError: if not factor.has(*opt.gens): coeff.append(factor) else: raise PolynomialError("%s contains an element of the generators set" % factor) monom = tuple(monom) if monom in poly: poly[monom] += Mul(*coeff) else: poly[monom] = Mul(*coeff) polys.append(poly) return polys, opt.gens
def _together(expr): if isinstance(expr, Basic): if expr.is_Atom or (expr.is_Function and not deep): return expr elif expr.is_Add: return gcd_terms(map(_together, Add.make_args(expr))) elif expr.is_Pow: base = _together(expr.base) if deep: exp = _together(expr.exp) else: exp = expr.exp return expr.__class__(base, exp) else: return expr.__class__(*[ _together(arg) for arg in expr.args ]) elif hasattr(expr, '__iter__'): return expr.__class__([ _together(ex) for ex in expr ]) return expr
def _print_MatAdd(self, expr): c, terms = expr.as_coeff_Add() tex = [] if c < 0: tex.append("-") tex.append(self._print(-c)) for term in Add.make_args(terms): coeff, M = term.as_coeff_Mul() if coeff < 0: tex.append("-") coeff = -coeff else: tex.append("+") if coeff != 1: tex.append(self._print(coeff)) tex.append(self._print(M)) return " ".join(tex)
def _matrix_derivative(expr, x): from sympy import Derivative lines = expr._eval_derivative_matrix_lines(x) parts = [i.build() for i in lines] from sympy.codegen.array_utils import recognize_matrix_expression parts = [[recognize_matrix_expression(j).doit() for j in i] for i in parts] def _get_shape(elem): if isinstance(elem, MatrixExpr): return elem.shape return (1, 1) def get_rank(parts): return sum([j not in (1, None) for i in parts for j in _get_shape(i)]) ranks = [get_rank(i) for i in parts] rank = ranks[0] def contract_one_dims(parts): if len(parts) == 1: return parts[0] else: p1, p2 = parts[:2] if p2.is_Matrix: p2 = p2.T pbase = p1*p2 if len(parts) == 2: return pbase else: # len(parts) > 2 if pbase.is_Matrix: raise ValueError("") return pbase*Mul.fromiter(parts[2:]) if rank <= 2: return Add.fromiter([contract_one_dims(i) for i in parts]) return Derivative(expr, x)
def eval(cls, arg): if arg is S.NaN: return S.NaN elif arg is S.ComplexInfinity: return S.NaN elif arg.is_real: return arg elif arg.is_imaginary or (S.ImaginaryUnit*arg).is_real: return S.Zero elif arg.is_Matrix: return arg.as_real_imag()[0] elif arg.is_Function and isinstance(arg, conjugate): return re(arg.args[0]) else: included, reverted, excluded = [], [], [] args = Add.make_args(arg) for term in args: 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: # Try to do some advanced expansion. If # impossible, don't try to do re(arg) again # (because this is what we are trying to do now). real_imag = term.as_real_imag(ignore=arg) if real_imag: excluded.append(real_imag[0]) else: included.append(term) if len(args) != len(included): a, b, c = (Add(*xs) for xs in [included, reverted, excluded]) return cls(a) - im(b) + c
def _eval_refine(self): arg = self.args[0] if arg.is_zero: return S.Zero if arg.is_nonnegative: return arg if arg.is_nonpositive: return -arg if arg.is_Add: expr_list = [] for _arg in Add.make_args(arg): if _arg.is_negative or _arg.is_negative is None: return None if _arg.is_zero: expr_list.append(S.Zero) elif _arg.is_nonnegative: expr_list.append(_arg) elif _arg.is_nonpositive: expr_list.append(-_arg) if expr_list: return Add(*expr_list) return arg
def collect_sqrt(expr, evaluate=None): """Return expr with terms having common square roots collected together. If ``evaluate`` is False a count indicating the number of sqrt-containing terms will be returned and, if non-zero, the terms of the Add will be returned, else the expression itself will be returned as a single term. If ``evaluate`` is True, the expression with any collected terms will be returned. Note: since I = sqrt(-1), it is collected, too. Examples ======== >>> from sympy import sqrt >>> from sympy.simplify.radsimp import collect_sqrt >>> from sympy.abc import a, b >>> r2, r3, r5 = [sqrt(i) for i in [2, 3, 5]] >>> collect_sqrt(a*r2 + b*r2) sqrt(2)*(a + b) >>> collect_sqrt(a*r2 + b*r2 + a*r3 + b*r3) sqrt(2)*(a + b) + sqrt(3)*(a + b) >>> collect_sqrt(a*r2 + b*r2 + a*r3 + b*r5) sqrt(3)*a + sqrt(5)*b + sqrt(2)*(a + b) If evaluate is False then the arguments will be sorted and returned as a list and a count of the number of sqrt-containing terms will be returned: >>> collect_sqrt(a*r2 + b*r2 + a*r3 + b*r5, evaluate=False) ((sqrt(3)*a, sqrt(5)*b, sqrt(2)*(a + b)), 3) >>> collect_sqrt(a*sqrt(2) + b, evaluate=False) ((b, sqrt(2)*a), 1) >>> collect_sqrt(a + b, evaluate=False) ((a + b,), 0) See Also ======== collect, collect_const, rcollect """ if evaluate is None: evaluate = global_evaluate[0] # this step will help to standardize any complex arguments # of sqrts coeff, expr = expr.as_content_primitive() vars = set() for a in Add.make_args(expr): for m in a.args_cnc()[0]: if m.is_number and (m.is_Pow and m.exp.is_Rational and m.exp.q == 2 or m is S.ImaginaryUnit): vars.add(m) # we only want radicals, so exclude Number handling; in this case # d will be evaluated d = collect_const(expr, *vars, Numbers=False) hit = expr != d if not evaluate: nrad = 0 # make the evaluated args canonical args = list(ordered(Add.make_args(d))) for i, m in enumerate(args): c, nc = m.args_cnc() for ci in c: # XXX should this be restricted to ci.is_number as above? if ci.is_Pow and ci.exp.is_Rational and ci.exp.q == 2 or \ ci is S.ImaginaryUnit: nrad += 1 break args[i] *= coeff if not (hit or nrad): args = [Add(*args)] return tuple(args), nrad return coeff * d
def is_deriv_k(fa, fd, DE): """ Checks if Df/f is the derivative of an element of k(t). a in k(t) is the derivative of an element of k(t) if there exists b in k(t) such that a = Db. Either returns (ans, u), such that Df/f == Du, or None, which means that Df/f is not the derivative of an element of k(t). ans is a list of tuples such that Add(*[i*j for i, j in ans]) == u. This is useful for seeing exactly which elements of k(t) produce u. This function uses the structure theorem approach, which says that for any f in K, Df/f is the derivative of a element of K if and only if there are ri in QQ such that:: --- --- Dt \ r * Dt + \ r * i Df / i i / i --- = --. --- --- t f i in L i in E i K/C(x) K/C(x) Where C = Const(K), L_K/C(x) = { i in {1, ..., n} such that t_i is transcendental over C(x)(t_1, ..., t_i-1) and Dt_i = Da_i/a_i, for some a_i in C(x)(t_1, ..., t_i-1)* } (i.e., the set of all indices of logarithmic monomials of K over C(x)), and E_K/C(x) = { i in {1, ..., n} such that t_i is transcendental over C(x)(t_1, ..., t_i-1) and Dt_i/t_i = Da_i, for some a_i in C(x)(t_1, ..., t_i-1) } (i.e., the set of all indices of hyperexponential monomials of K over C(x)). If K is an elementary extension over C(x), then the cardinality of L_K/C(x) U E_K/C(x) is exactly the transcendence degree of K over C(x). Furthermore, because Const_D(K) == Const_D(C(x)) == C, deg(Dt_i) == 1 when t_i is in E_K/C(x) and deg(Dt_i) == 0 when t_i is in L_K/C(x), implying in particular that E_K/C(x) and L_K/C(x) are disjoint. The sets L_K/C(x) and E_K/C(x) must, by their nature, be computed recursively using this same function. Therefore, it is required to pass them as indices to D (or T). E_args are the arguments of the hyperexponentials indexed by E_K (i.e., if i is in E_K, then T[i] == exp(E_args[i])). This is needed to compute the final answer u such that Df/f == Du. log(f) will be the same as u up to a additive constant. This is because they will both behave the same as monomials. For example, both log(x) and log(2*x) == log(x) + log(2) satisfy Dt == 1/x, because log(2) is constant. Therefore, the term const is returned. const is such that log(const) + f == u. This is calculated by dividing the arguments of one logarithm from the other. Therefore, it is necessary to pass the arguments of the logarithmic terms in L_args. To handle the case where we are given Df/f, not f, use is_deriv_k_in_field(). """ # Compute Df/f dfa, dfd = fd*(fd*derivation(fa, DE) - fa*derivation(fd, DE)), fd**2*fa dfa, dfd = dfa.cancel(dfd, include=True) # Our assumption here is that each monomial is recursively transcendental if len(DE.L_K) + len(DE.E_K) != len(DE.D) - 1: if [i for i in DE.cases if i == 'tan'] or \ set([i for i in DE.cases if i == 'primitive']) - set(DE.L_K): raise NotImplementedError("Real version of the structure " "theorems with hypertangent support is not yet implemented.") # TODO: What should really be done in this case? raise NotImplementedError("Nonelementary extensions not supported " "in the structure theorems.") E_part = [DE.D[i].quo(Poly(DE.T[i], DE.T[i])).as_expr() for i in DE.E_K] L_part = [DE.D[i].as_expr() for i in DE.L_K] lhs = Matrix([E_part + L_part]) rhs = Matrix([dfa.as_expr()/dfd.as_expr()]) A, u = constant_system(lhs, rhs, DE) if not all(derivation(i, DE, basic=True).is_zero for i in u) or not A: # If the elements of u are not all constant # Note: See comment in constant_system # Also note: derivation(basic=True) calls cancel() return None else: if not all(i.is_Rational for i in u): raise NotImplementedError("Cannot work with non-rational " "coefficients in this case.") else: terms = DE.E_args + [DE.T[i] for i in DE.L_K] ans = list(zip(terms, u)) result = Add(*[Mul(i, j) for i, j in ans]) argterms = [DE.T[i] for i in DE.E_K] + DE.L_args l = [] ld = [] for i, j in zip(argterms, u): # We need to get around things like sqrt(x**2) != x # and also sqrt(x**2 + 2*x + 1) != x + 1 # Issue 10798: i need not be a polynomial i, d = i.as_numer_denom() icoeff, iterms = sqf_list(i) l.append(Mul(*([Pow(icoeff, j)] + [Pow(b, e*j) for b, e in iterms]))) dcoeff, dterms = sqf_list(d) ld.append(Mul(*([Pow(dcoeff, j)] + [Pow(b, e*j) for b, e in dterms]))) const = cancel(fa.as_expr()/fd.as_expr()/Mul(*l)*Mul(*ld)) return (ans, result, const)
def __new__(cls, expr, func=None, x=None, auto=True, quadratic=False): """Construct a new ``RootSum`` instance of roots of a polynomial.""" coeff, poly = cls._transform(expr, x) if not poly.is_univariate: raise MultivariatePolynomialError( "only univariate polynomials are allowed") if func is None: func = Lambda(poly.gen, poly.gen) else: try: is_func = func.is_Function except AttributeError: is_func = False if is_func and 1 in func.nargs: if not isinstance(func, Lambda): func = Lambda(poly.gen, func(poly.gen)) else: raise ValueError("expected a univariate function, got %s" % func) var, expr = func.variables[0], func.expr if coeff is not S.One: expr = expr.subs(var, coeff * var) deg = poly.degree() if not expr.has(var): return deg * expr if expr.is_Add: add_const, expr = expr.as_independent(var) else: add_const = S.Zero if expr.is_Mul: mul_const, expr = expr.as_independent(var) else: mul_const = S.One func = Lambda(var, expr) rational = cls._is_func_rational(poly, func) factors, terms = _pure_factors(poly), [] for poly, k in factors: if poly.is_linear: term = func(roots_linear(poly)[0]) elif quadratic and poly.is_quadratic: term = sum(map(func, roots_quadratic(poly))) else: if not rational or not auto: term = cls._new(poly, func, auto) else: term = cls._rational_case(poly, func) terms.append(k * term) return mul_const * Add(*terms) + deg * add_const
def recurse_expr(expr, index_ranges={}): if expr.is_Mul: nonmatargs = [] pos_arg = [] pos_ind = [] dlinks = {} link_ind = [] counter = 0 args_ind = [] for arg in expr.args: retvals = recurse_expr(arg, index_ranges) assert isinstance(retvals, list) if isinstance(retvals, list): for i in retvals: args_ind.append(i) else: args_ind.append(retvals) for arg_symbol, arg_indices in args_ind: if arg_indices is None: nonmatargs.append(arg_symbol) continue if isinstance(arg_symbol, MatrixElement): arg_symbol = arg_symbol.args[0] pos_arg.append(arg_symbol) pos_ind.append(arg_indices) link_ind.append([None] * len(arg_indices)) for i, ind in enumerate(arg_indices): if ind in dlinks: other_i = dlinks[ind] link_ind[counter][i] = other_i link_ind[other_i[0]][other_i[1]] = (counter, i) dlinks[ind] = (counter, i) counter += 1 counter2 = 0 lines = {} while counter2 < len(link_ind): for i, e in enumerate(link_ind): if None in e: line_start_index = (i, e.index(None)) break cur_ind_pos = line_start_index cur_line = [] index1 = pos_ind[cur_ind_pos[0]][cur_ind_pos[1]] while True: d, r = cur_ind_pos if pos_arg[d] != 1: if r % 2 == 1: cur_line.append(transpose(pos_arg[d])) else: cur_line.append(pos_arg[d]) next_ind_pos = link_ind[d][1 - r] counter2 += 1 # Mark as visited, there will be no `None` anymore: link_ind[d] = (-1, -1) if next_ind_pos is None: index2 = pos_ind[d][1 - r] lines[(index1, index2)] = cur_line break cur_ind_pos = next_ind_pos lines = { k: MatMul.fromiter(v) if len(v) != 1 else v[0] for k, v in lines.items() } return [(Mul.fromiter(nonmatargs), None)] + [(MatrixElement( a, i, j), (i, j)) for (i, j), a in lines.items()] elif expr.is_Add: res = [recurse_expr(i) for i in expr.args] d = collections.defaultdict(list) for res_addend in res: scalar = 1 for elem, indices in res_addend: if indices is None: scalar = elem continue indices = tuple(sorted(indices, key=default_sort_key)) d[indices].append(scalar * remove_matelement(elem, *indices)) scalar = 1 return [(MatrixElement(Add.fromiter(v), *k), k) for k, v in d.items()] elif isinstance(expr, KroneckerDelta): i1, i2 = expr.args if dimensions is not None: identity = Identity(dimensions[0]) else: identity = S.One return [(MatrixElement(identity, i1, i2), (i1, i2))] elif isinstance(expr, MatrixElement): matrix_symbol, i1, i2 = expr.args if i1 in index_ranges: r1, r2 = index_ranges[i1] if r1 != 0 or matrix_symbol.shape[0] != r2 + 1: raise ValueError( "index range mismatch: {0} vs. (0, {1})".format( (r1, r2), matrix_symbol.shape[0])) if i2 in index_ranges: r1, r2 = index_ranges[i2] if r1 != 0 or matrix_symbol.shape[1] != r2 + 1: raise ValueError( "index range mismatch: {0} vs. (0, {1})".format( (r1, r2), matrix_symbol.shape[1])) if (i1 == i2) and (i1 in index_ranges): return [(trace(matrix_symbol), None)] return [(MatrixElement(matrix_symbol, i1, i2), (i1, i2))] elif isinstance(expr, Sum): return recurse_expr( expr.args[0], index_ranges={i[0]: i[1:] for i in expr.args[1:]}) else: return [(expr, None)]
def eval(cls, n, z): n, z = list(map(sympify, (n, z))) from sympy import unpolarify if n.is_integer: if n.is_nonnegative: nz = unpolarify(z) if z != nz: return polygamma(n, nz) if n == -1: return loggamma(z) else: if z.is_Number: if z is S.NaN: return S.NaN elif z is S.Infinity: if n.is_Number: if n is S.Zero: return S.Infinity else: return S.Zero elif z.is_Integer: if z.is_nonpositive: return S.ComplexInfinity else: if n is S.Zero: return -S.EulerGamma + harmonic(z - 1, 1) elif n.is_odd: return (-1)**(n + 1) * factorial(n) * zeta( n + 1, z) if n == 0: if z is S.NaN: return S.NaN elif z.is_Rational: # TODO actually *any* n/m can be done, but that is messy lookup = { S(1) / 2: -2 * log(2) - S.EulerGamma, S(1) / 3: -S.Pi / 2 / sqrt(3) - 3 * log(3) / 2 - S.EulerGamma, S(1) / 4: -S.Pi / 2 - 3 * log(2) - S.EulerGamma, S(3) / 4: -3 * log(2) - S.EulerGamma + S.Pi / 2, S(2) / 3: -3 * log(3) / 2 + S.Pi / 2 / sqrt(3) - S.EulerGamma } if z > 0: n = floor(z) z0 = z - n if z0 in lookup: return lookup[z0] + Add( *[1 / (z0 + k) for k in range(n)]) elif z < 0: n = floor(1 - z) z0 = z + n if z0 in lookup: return lookup[z0] - Add( *[1 / (z0 - 1 - k) for k in range(n)]) elif z in (S.Infinity, S.NegativeInfinity): return S.Infinity else: t = z.extract_multiplicatively(S.ImaginaryUnit) if t in (S.Infinity, S.NegativeInfinity): return S.Infinity
def sum(self, items): return Add(*items)
def test_ZeroMatrix_doit(): Znn = ZeroMatrix(Add(n, n, evaluate=False), n) assert isinstance(Znn.rows, Add) assert Znn.doit() == ZeroMatrix(2 * n, n) assert isinstance(Znn.doit().rows, Mul)
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_expr() # 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 = Add.make_args(f) 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 try: h = heurisch(g, x, hints=[]) except PolynomialError: # XXX: this exception means there is a bug in the # implementation of heuristic Risch integration # algorithm. h = None # 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 Add(*parts)
def _eval_nseries(self, x, n, logx): terms, order = self.function.nseries(x, n=n, logx=logx).as_coeff_add(C.Order) return integrate(terms, *self.limits) + Add(*order) * x
def _eval_trace(self): from .trace import trace return Add(*[trace(arg) for arg in self.args]).doit()
def _entry(self, i, j, expand=None): return Add(*[arg._entry(i, j) for arg in self.args])
def limit(e, z, z0, dir="+"): """ Compute the limit of e(z) at the point z0. z0 can be any expression, including oo and -oo. For dir="+" (default) it calculates the limit from the right (z->z0+) and for dir="-" the limit from the left (z->z0-). For infinite z0 (oo or -oo), the dir argument doesn't matter. Examples ======== >>> from sympy import limit, sin, Symbol, oo >>> from sympy.abc import x >>> limit(sin(x)/x, x, 0) 1 >>> limit(1/x, x, 0, dir="+") oo >>> limit(1/x, x, 0, dir="-") -oo >>> limit(1/x, x, oo) 0 Notes ===== First we try some heuristics for easy and frequent cases like "x", "1/x", "x**2" and similar, so that it's fast. For all other cases, we use the Gruntz algorithm (see the gruntz() function). """ e = sympify(e) z = sympify(z) z0 = sympify(z0) if e == z: return z0 if not e.has(z): return e # gruntz fails on factorials but works with the gamma function # If no factorial term is present, e should remain unchanged. # factorial is defined to be zero for negative inputs (which # differs from gamma) so only rewrite for positive z0. if z0.is_positive: e = e.rewrite(factorial, gamma) if e.is_Mul: if abs(z0) is S.Infinity: # XXX todo: this should probably be stated in the # negative -- i.e. to exclude expressions that should # not be handled this way but I'm not sure what that # condition is; when ok is True it means that the leading # term approach is going to succeed (hopefully) ok = lambda w: (z in w.free_symbols and any( a.is_polynomial(z) or any( z in m.free_symbols and m.is_polynomial(z) for m in Mul.make_args(a)) for a in Add.make_args(w))) if all(ok(w) for w in e.as_numer_denom()): u = C.Dummy(positive=(z0 is S.Infinity)) inve = e.subs(z, 1 / u) return limit(inve.as_leading_term(u), u, S.Zero, "+" if z0 is S.Infinity else "-") if e.is_Order: return C.Order(limit(e.expr, z, z0), *e.args[1:]) try: r = gruntz(e, z, z0, dir) if r is S.NaN: raise PoleError() except (PoleError, ValueError): r = heuristics(e, z, z0, dir) return r
def handle(expr): # Handle first reduces to the case # expr = 1/d, where d is an add, or d is base**p/2. # We do this by recursively calling handle on each piece. from sympy.simplify.simplify import nsimplify n, d = fraction(expr) if expr.is_Atom or (d.is_Atom and n.is_Atom): return expr elif not n.is_Atom: n = n.func(*[handle(a) for a in n.args]) return _unevaluated_Mul(n, handle(1 / d)) elif n is not S.One: return _unevaluated_Mul(n, handle(1 / d)) elif d.is_Mul: return _unevaluated_Mul(*[handle(1 / d) for d in d.args]) # By this step, expr is 1/d, and d is not a mul. if not symbolic and d.free_symbols: return expr if ispow2(d): d2 = sqrtdenest(sqrt(d.base))**numer(d.exp) if d2 != d: return handle(1 / d2) elif d.is_Pow and (d.exp.is_integer or d.base.is_positive): # (1/d**i) = (1/d)**i return handle(1 / d.base)**d.exp if not (d.is_Add or ispow2(d)): return 1 / d.func(*[handle(a) for a in d.args]) # handle 1/d treating d as an Add (though it may not be) keep = True # keep changes that are made # flatten it and collect radicals after checking for special # conditions d = _mexpand(d) # did it change? if d.is_Atom: return 1 / d # is it a number that might be handled easily? if d.is_number: _d = nsimplify(d) if _d.is_Number and _d.equals(d): return 1 / _d while True: # collect similar terms collected = defaultdict(list) for m in Add.make_args(d): # d might have become non-Add p2 = [] other = [] for i in Mul.make_args(m): if ispow2(i, log2=True): p2.append(i.base if i.exp is S.Half else i.base**( 2 * i.exp)) elif i is S.ImaginaryUnit: p2.append(S.NegativeOne) else: other.append(i) collected[tuple(ordered(p2))].append(Mul(*other)) rterms = list(ordered(list(collected.items()))) rterms = [(Mul(*i), Add(*j)) for i, j in rterms] nrad = len(rterms) - (1 if rterms[0][0] is S.One else 0) if nrad < 1: break elif nrad > max_terms: # there may have been invalid operations leading to this point # so don't keep changes, e.g. this expression is troublesome # in collecting terms so as not to raise the issue of 2834: # r = sqrt(sqrt(5) + 5) # eq = 1/(sqrt(5)*r + 2*sqrt(5)*sqrt(-sqrt(5) + 5) + 5*r) keep = False break if len(rterms) > 4: # in general, only 4 terms can be removed with repeated squaring # but other considerations can guide selection of radical terms # so that radicals are removed if all( [x.is_Integer and (y**2).is_Rational for x, y in rterms]): nd, d = rad_rationalize( S.One, Add._from_args([sqrt(x) * y for x, y in rterms])) n *= nd else: # is there anything else that might be attempted? keep = False break from sympy.simplify.powsimp import powsimp, powdenest num = powsimp(_num(rterms)) n *= num d *= num d = powdenest(_mexpand(d), force=symbolic) if d.is_Atom: break if not keep: return expr return _unevaluated_Mul(n, 1 / d)
def collect_const(expr, *vars, **kwargs): """A non-greedy collection of terms with similar number coefficients in an Add expr. If ``vars`` is given then only those constants will be targeted. Although any Number can also be targeted, if this is not desired set ``Numbers=False`` and no Float or Rational will be collected. Examples ======== >>> from sympy import sqrt >>> from sympy.abc import a, s, x, y, z >>> from sympy.simplify.radsimp import collect_const >>> collect_const(sqrt(3) + sqrt(3)*(1 + sqrt(2))) sqrt(3)*(sqrt(2) + 2) >>> collect_const(sqrt(3)*s + sqrt(7)*s + sqrt(3) + sqrt(7)) (sqrt(3) + sqrt(7))*(s + 1) >>> s = sqrt(2) + 2 >>> collect_const(sqrt(3)*s + sqrt(3) + sqrt(7)*s + sqrt(7)) (sqrt(2) + 3)*(sqrt(3) + sqrt(7)) >>> collect_const(sqrt(3)*s + sqrt(3) + sqrt(7)*s + sqrt(7), sqrt(3)) sqrt(7) + sqrt(3)*(sqrt(2) + 3) + sqrt(7)*(sqrt(2) + 2) The collection is sign-sensitive, giving higher precedence to the unsigned values: >>> collect_const(x - y - z) x - (y + z) >>> collect_const(-y - z) -(y + z) >>> collect_const(2*x - 2*y - 2*z, 2) 2*(x - y - z) >>> collect_const(2*x - 2*y - 2*z, -2) 2*x - 2*(y + z) See Also ======== collect, collect_sqrt, rcollect """ if not expr.is_Add: return expr recurse = False Numbers = kwargs.get('Numbers', True) if not vars: recurse = True vars = set() for a in expr.args: for m in Mul.make_args(a): if m.is_number: vars.add(m) else: vars = sympify(vars) if not Numbers: vars = [v for v in vars if not v.is_Number] vars = list(ordered(vars)) for v in vars: terms = defaultdict(list) Fv = Factors(v) for m in Add.make_args(expr): f = Factors(m) q, r = f.div(Fv) if r.is_one: # only accept this as a true factor if # it didn't change an exponent from an Integer # to a non-Integer, e.g. 2/sqrt(2) -> sqrt(2) # -- we aren't looking for this sort of change fwas = f.factors.copy() fnow = q.factors if not any(k in fwas and fwas[k].is_Integer and not fnow[k].is_Integer for k in fnow): terms[v].append(q.as_expr()) continue terms[S.One].append(m) args = [] hit = False uneval = False for k in ordered(terms): v = terms[k] if k is S.One: args.extend(v) continue if len(v) > 1: v = Add(*v) hit = True if recurse and v != expr: vars.append(v) else: v = v[0] # be careful not to let uneval become True unless # it must be because it's going to be more expensive # to rebuild the expression as an unevaluated one if Numbers and k.is_Number and v.is_Add: args.append(_keep_coeff(k, v, sign=True)) uneval = True else: args.append(k * v) if hit: if uneval: expr = _unevaluated_Add(*args) else: expr = Add(*args) if not expr.is_Add: break return expr
def refine_Pow(expr, assumptions): """ Handler for instances of Pow. >>> from sympy import Symbol, Q >>> from sympy.assumptions.refine import refine_Pow >>> from sympy.abc import x,y,z >>> refine_Pow((-1)**x, Q.real(x)) >>> refine_Pow((-1)**x, Q.even(x)) 1 >>> refine_Pow((-1)**x, Q.odd(x)) -1 For powers of -1, even parts of the exponent can be simplified: >>> refine_Pow((-1)**(x+y), Q.even(x)) (-1)**y >>> refine_Pow((-1)**(x+y+z), Q.odd(x) & Q.odd(z)) (-1)**y >>> refine_Pow((-1)**(x+y+2), Q.odd(x)) (-1)**(y + 1) >>> refine_Pow((-1)**(x+3), True) (-1)**(x + 1) """ from sympy.core import Pow, Rational from sympy.functions.elementary.complexes import Abs from sympy.functions import sign if isinstance(expr.base, Abs): if ask(Q.real(expr.base.args[0]), assumptions) and \ ask(Q.even(expr.exp), assumptions): return expr.base.args[0]**expr.exp if ask(Q.real(expr.base), assumptions): if expr.base.is_number: if ask(Q.even(expr.exp), assumptions): return abs(expr.base)**expr.exp if ask(Q.odd(expr.exp), assumptions): return sign(expr.base) * abs(expr.base)**expr.exp if isinstance(expr.exp, Rational): if type(expr.base) is Pow: return abs(expr.base.base)**(expr.base.exp * expr.exp) if expr.base is S.NegativeOne: if expr.exp.is_Add: old = expr # For powers of (-1) we can remove # - even terms # - pairs of odd terms # - a single odd term + 1 # - A numerical constant N can be replaced with mod(N,2) coeff, terms = expr.exp.as_coeff_add() terms = set(terms) even_terms = set([]) odd_terms = set([]) initial_number_of_terms = len(terms) for t in terms: if ask(Q.even(t), assumptions): even_terms.add(t) elif ask(Q.odd(t), assumptions): odd_terms.add(t) terms -= even_terms if len(odd_terms) % 2: terms -= odd_terms new_coeff = (coeff + S.One) % 2 else: terms -= odd_terms new_coeff = coeff % 2 if new_coeff != coeff or len(terms) < initial_number_of_terms: terms.add(new_coeff) expr = expr.base**(Add(*terms)) # Handle (-1)**((-1)**n/2 + m/2) e2 = 2 * expr.exp if ask(Q.even(e2), assumptions): if e2.could_extract_minus_sign(): e2 *= expr.base if e2.is_Add: i, p = e2.as_two_terms() if p.is_Pow and p.base is S.NegativeOne: if ask(Q.integer(p.exp), assumptions): i = (i + 1) / 2 if ask(Q.even(i), assumptions): return expr.base**p.exp elif ask(Q.odd(i), assumptions): return expr.base**(p.exp + 1) else: return expr.base**(p.exp + i) if old != expr: return expr
def test_OneMatrix_doit(): Unn = OneMatrix(Add(n, n, evaluate=False), n) assert isinstance(Unn.rows, Add) assert Unn.doit() == OneMatrix(2 * n, n) assert isinstance(Unn.doit().rows, Mul)
def classify_pde(eq, func=None, dict=False, *, prep=True, **kwargs): """ Returns a tuple of possible pdsolve() classifications for a PDE. The tuple is ordered so that first item is the classification that pdsolve() uses to solve the PDE by default. In general, classifications near the beginning of the list will produce better solutions faster than those near the end, though there are always exceptions. To make pdsolve use a different classification, use pdsolve(PDE, func, hint=<classification>). See also the pdsolve() docstring for different meta-hints you can use. If ``dict`` is true, classify_pde() will return a dictionary of hint:match expression terms. This is intended for internal use by pdsolve(). Note that because dictionaries are ordered arbitrarily, this will most likely not be in the same order as the tuple. You can get help on different hints by doing help(pde.pde_hintname), where hintname is the name of the hint without "_Integral". See sympy.pde.allhints or the sympy.pde docstring for a list of all supported hints that can be returned from classify_pde. Examples ======== >>> from sympy.solvers.pde import classify_pde >>> from sympy import Function, Eq >>> from sympy.abc import x, y >>> f = Function('f') >>> u = f(x, y) >>> ux = u.diff(x) >>> uy = u.diff(y) >>> eq = Eq(1 + (2*(ux/u)) + (3*(uy/u)), 0) >>> classify_pde(eq) ('1st_linear_constant_coeff_homogeneous',) """ if func and len(func.args) != 2: raise NotImplementedError( "Right now only partial " "differential equations of two variables are supported") if prep or func is None: prep, func_ = _preprocess(eq, func) if func is None: func = func_ if isinstance(eq, Equality): if eq.rhs != 0: return classify_pde(eq.lhs - eq.rhs, func) eq = eq.lhs f = func.func x = func.args[0] y = func.args[1] fx = f(x, y).diff(x) fy = f(x, y).diff(y) # TODO : For now pde.py uses support offered by the ode_order function # to find the order with respect to a multi-variable function. An # improvement could be to classify the order of the PDE on the basis of # individual variables. order = ode_order(eq, f(x, y)) # hint:matchdict or hint:(tuple of matchdicts) # Also will contain "default":<default hint> and "order":order items. matching_hints = {'order': order} if not order: if dict: matching_hints["default"] = None return matching_hints else: return () eq = expand(eq) a = Wild('a', exclude=[f(x, y)]) b = Wild('b', exclude=[f(x, y), fx, fy, x, y]) c = Wild('c', exclude=[f(x, y), fx, fy, x, y]) d = Wild('d', exclude=[f(x, y), fx, fy, x, y]) e = Wild('e', exclude=[f(x, y), fx, fy]) n = Wild('n', exclude=[x, y]) # Try removing the smallest power of f(x,y) # from the highest partial derivatives of f(x,y) reduced_eq = None if eq.is_Add: var = set(combinations_with_replacement((x, y), order)) dummyvar = var.copy() power = None for i in var: coeff = eq.coeff(f(x, y).diff(*i)) if coeff != 1: match = coeff.match(a * f(x, y)**n) if match and match[a]: power = match[n] dummyvar.remove(i) break dummyvar.remove(i) for i in dummyvar: coeff = eq.coeff(f(x, y).diff(*i)) if coeff != 1: match = coeff.match(a * f(x, y)**n) if match and match[a] and match[n] < power: power = match[n] if power: den = f(x, y)**power reduced_eq = Add(*[arg / den for arg in eq.args]) if not reduced_eq: reduced_eq = eq if order == 1: reduced_eq = collect(reduced_eq, f(x, y)) r = reduced_eq.match(b * fx + c * fy + d * f(x, y) + e) if r: if not r[e]: ## Linear first-order homogeneous partial-differential ## equation with constant coefficients r.update({'b': b, 'c': c, 'd': d}) matching_hints["1st_linear_constant_coeff_homogeneous"] = r else: if r[b]**2 + r[c]**2 != 0: ## Linear first-order general partial-differential ## equation with constant coefficients r.update({'b': b, 'c': c, 'd': d, 'e': e}) matching_hints["1st_linear_constant_coeff"] = r matching_hints["1st_linear_constant_coeff_Integral"] = r else: b = Wild('b', exclude=[f(x, y), fx, fy]) c = Wild('c', exclude=[f(x, y), fx, fy]) d = Wild('d', exclude=[f(x, y), fx, fy]) r = reduced_eq.match(b * fx + c * fy + d * f(x, y) + e) if r: r.update({'b': b, 'c': c, 'd': d, 'e': e}) matching_hints["1st_linear_variable_coeff"] = r # Order keys based on allhints. retlist = [] for i in allhints: if i in matching_hints: retlist.append(i) if dict: # Dictionaries are ordered arbitrarily, so make note of which # hint would come first for pdsolve(). Use an ordered dict in Py 3. matching_hints["default"] = None matching_hints["ordered_hints"] = tuple(retlist) for i in allhints: if i in matching_hints: matching_hints["default"] = i break return matching_hints else: return tuple(retlist)
def symmetrize(F, *gens, **args): """ Rewrite a polynomial in terms of elementary symmetric polynomials. A symmetric polynomial is a multivariate polynomial that remains invariant under any variable permutation, i.e., if ``f = f(x_1, x_2, ..., x_n)``, then ``f = f(x_{i_1}, x_{i_2}, ..., x_{i_n})``, where ``(i_1, i_2, ..., i_n)`` is a permutation of ``(1, 2, ..., n)`` (an element of the group ``S_n``). Returns a tuple of symmetric polynomials ``(f1, f2, ..., fn)`` such that ``f = f1 + f2 + ... + fn``. Examples ======== >>> from sympy.polys.polyfuncs import symmetrize >>> from sympy.abc import x, y >>> symmetrize(x**2 + y**2) (-2*x*y + (x + y)**2, 0) >>> symmetrize(x**2 + y**2, formal=True) (s1**2 - 2*s2, 0, [(s1, x + y), (s2, x*y)]) >>> symmetrize(x**2 - y**2) (-2*x*y + (x + y)**2, -2*y**2) >>> symmetrize(x**2 - y**2, formal=True) (s1**2 - 2*s2, -2*y**2, [(s1, x + y), (s2, x*y)]) """ allowed_flags(args, ['formal', 'symbols']) iterable = True if not hasattr(F, '__iter__'): iterable = False F = [F] try: F, opt = parallel_poly_from_expr(F, *gens, **args) except PolificationFailed as exc: result = [] for expr in exc.exprs: if expr.is_Number: result.append((expr, S.Zero)) else: raise ComputationFailed('symmetrize', len(F), exc) if not iterable: result, = result if not exc.opt.formal: return result else: if iterable: return result, [] else: return result + ([], ) polys, symbols = [], opt.symbols gens, dom = opt.gens, opt.domain for i in range(len(gens)): poly = symmetric_poly(i + 1, gens, polys=True) polys.append((next(symbols), poly.set_domain(dom))) indices = list(range(len(gens) - 1)) weights = list(range(len(gens), 0, -1)) result = [] for f in F: symmetric = [] if not f.is_homogeneous: symmetric.append(f.TC()) f -= f.TC() while f: _height, _monom, _coeff = -1, None, None for i, (monom, coeff) in enumerate(f.terms()): if all(monom[i] >= monom[i + 1] for i in indices): height = max([n * m for n, m in zip(weights, monom)]) if height > _height: _height, _monom, _coeff = height, monom, coeff if _height != -1: monom, coeff = _monom, _coeff else: break exponents = [] for m1, m2 in zip(monom, monom[1:] + (0, )): exponents.append(m1 - m2) term = [s**n for (s, _), n in zip(polys, exponents)] poly = [p**n for (_, p), n in zip(polys, exponents)] symmetric.append(Mul(coeff, *term)) product = poly[0].mul(coeff) for p in poly[1:]: product = product.mul(p) f -= product result.append((Add(*symmetric), f.as_expr())) polys = [(s, p.as_expr()) for s, p in polys] if not opt.formal: for i, (sym, non_sym) in enumerate(result): result[i] = (sym.subs(polys), non_sym) if not iterable: result, = result if not opt.formal: return result else: if iterable: return result, polys else: return result + (polys, )
def _eval_trace(self): if self.rowblocksizes == self.colblocksizes: return Add( *[Trace(self.blocks[i, i]) for i in range(self.blockshape[0])]) raise NotImplementedError( "Can't perform trace of irregular blockshape")
def doit(self, **hints): """Evaluates limit""" from sympy.series.limitseq import limit_seq e, z, z0, dir = self.args if hints.get('deep', True): e = e.doit(**hints) z = z.doit(**hints) z0 = z0.doit(**hints) if e == z: return z0 if not e.has(z): return e # gruntz fails on factorials but works with the gamma function # If no factorial term is present, e should remain unchanged. # factorial is defined to be zero for negative inputs (which # differs from gamma) so only rewrite for positive z0. if z0.is_positive: e = e.rewrite(factorial, gamma) if e.is_Mul: if abs(z0) is S.Infinity: # XXX todo: this should probably be stated in the # negative -- i.e. to exclude expressions that should # not be handled this way but I'm not sure what that # condition is; when ok is True it means that the leading # term approach is going to succeed (hopefully) ok = lambda w: (z in w.free_symbols and any( a.is_polynomial(z) or any( z in m.free_symbols and m.is_polynomial(z) for m in Mul.make_args(a)) for a in Add.make_args(w))) if all(ok(w) for w in e.as_numer_denom()): u = Dummy(positive=(z0 is S.Infinity)) inve = e.subs(z, 1 / u) r = limit(inve.as_leading_term(u), u, S.Zero, "+" if z0 is S.Infinity else "-") if isinstance(r, Limit): return self else: return r if e.is_Order: return Order(limit(e.expr, z, z0), *e.args[1:]) try: r = gruntz(e, z, z0, dir) if r is S.NaN: raise PoleError() except (PoleError, ValueError): r = heuristics(e, z, z0, dir) if r is None: return self except NotImplementedError: # Trying finding limits of sequences if hints.get('sequence', True) and z0 is S.Infinity: trials = hints.get('trials', 5) r = limit_seq(e, z, trials) if r is None: raise NotImplementedError() else: raise NotImplementedError() return r
def apart(f, x=None, full=False, **options): """ Compute partial fraction decomposition of a rational function. Given a rational function ``f`` compute partial fraction decomposition of ``f``. Two algorithms are available: one is based on undetermined coefficients method and the other is Bronstein's full partial fraction decomposition algorithm. Examples ======== >>> from sympy.polys.partfrac import apart >>> from sympy.abc import x, y By default, using the undetermined coefficients method: >>> apart(y/(x + 2)/(x + 1), x) -y/(x + 2) + y/(x + 1) You can choose Bronstein's algorithm by setting ``full=True``: >>> apart(y/(x**2 + x + 1), x) y/(x**2 + x + 1) >>> apart(y/(x**2 + x + 1), x, full=True) RootSum(_w**2 + _w + 1, Lambda(_a, (-2*_a*y/3 - y/3)/(-_a + x))) See Also ======== apart_list, assemble_partfrac_list """ allowed_flags(options, []) f = sympify(f) if f.is_Atom: return f else: P, Q = f.as_numer_denom() _options = options.copy() options = set_defaults(options, extension=True) try: (P, Q), opt = parallel_poly_from_expr((P, Q), x, **options) except PolynomialError as msg: if f.is_commutative: raise PolynomialError(msg) # non-commutative if f.is_Mul: c, nc = f.args_cnc(split_1=False) nc = f.func(*[apart(i, x=x, full=full, **_options) for i in nc]) if c: c = apart(f.func._from_args(c), x=x, full=full, **_options) return c * nc else: return nc elif f.is_Add: c = [] nc = [] for i in f.args: if i.is_commutative: c.append(i) else: try: nc.append(apart(i, x=x, full=full, **_options)) except NotImplementedError: nc.append(i) return apart(f.func(*c), x=x, full=full, **_options) + f.func(*nc) else: reps = [] pot = preorder_traversal(f) pot.next() for e in pot: try: reps.append((e, apart(e, x=x, full=full, **_options))) pot.skip() # this was handled successfully except NotImplementedError: pass return f.xreplace(dict(reps)) if P.is_multivariate: fc = f.cancel() if fc != f: return apart(fc, x=x, full=full, **_options) raise NotImplementedError( "multivariate partial fraction decomposition") common, P, Q = P.cancel(Q) poly, P = P.div(Q, auto=True) P, Q = P.rat_clear_denoms(Q) if Q.degree() <= 1: partial = P / Q else: if not full: partial = apart_undetermined_coeffs(P, Q) else: partial = apart_full_decomposition(P, Q) terms = S.Zero for term in Add.make_args(partial): if term.has(RootSum): terms += term else: terms += factor(term) return common * (poly.as_expr() + terms)
def _doprint_loops(self, expr, assign_to=None): # Here we print an expression that contains Indexed objects, they # correspond to arrays in the generated code. The low-level implementation # involves looping over array elements and possibly storing results in temporary # variables or accumulate it in the assign_to object. if self._settings.get('contract', True): from sympy.tensor import get_contraction_structure # Setup loops over non-dummy indices -- all terms need these indices = self._get_expression_indices(expr, assign_to) # Setup loops over dummy indices -- each term needs separate treatment dummies = get_contraction_structure(expr) else: indices = [] dummies = {None: (expr, )} openloop, closeloop = self._get_loop_opening_ending(indices) # terms with no summations first if None in dummies: text = StrPrinter.doprint(self, Add(*dummies[None])) else: # If all terms have summations we must initialize array to Zero text = StrPrinter.doprint(self, 0) # skip redundant assignments (where lhs == rhs) lhs_printed = self._print(assign_to) lines = [] if text != lhs_printed: lines.extend(openloop) if assign_to is not None: text = self._get_statement("%s = %s" % (lhs_printed, text)) lines.append(text) lines.extend(closeloop) # then terms with summations for d in dummies: if isinstance(d, tuple): indices = self._sort_optimized(d, expr) openloop_d, closeloop_d = self._get_loop_opening_ending( indices) for term in dummies[d]: if term in dummies and not ( [list(f.keys()) for f in dummies[term]] == [[None] for f in dummies[term]]): # If one factor in the term has it's own internal # contractions, those must be computed first. # (temporary variables?) raise NotImplementedError( "FIXME: no support for contractions in factor yet") else: # We need the lhs expression as an accumulator for # the loops, i.e # # for (int d=0; d < dim; d++){ # lhs[] = lhs[] + term[][d] # } ^.................. the accumulator # # We check if the expression already contains the # lhs, and raise an exception if it does, as that # syntax is currently undefined. FIXME: What would be # a good interpretation? if assign_to is None: raise AssignmentError( "need assignment variable for loops") if term.has(assign_to): raise ValueError("FIXME: lhs present in rhs,\ this is undefined in CodePrinter") lines.extend(openloop) lines.extend(openloop_d) text = "%s = %s" % (lhs_printed, StrPrinter.doprint( self, assign_to + term)) lines.append(self._get_statement(text)) lines.extend(closeloop_d) lines.extend(closeloop) return "\n".join(lines)
def apart(f, x=None, full=False, **options): """ Compute partial fraction decomposition of a rational function. Given a rational function ``f`` compute partial fraction decomposition of ``f``. Two algorithms are available: one is based on undetermined coefficients method and the other is Bronstein's full partial fraction decomposition algorithm. Examples ======== >>> from sympy.polys.partfrac import apart >>> from sympy.abc import x, y By default, using the undetermined coefficients method: >>> apart(y/(x + 2)/(x + 1), x) -y/(x + 2) + y/(x + 1) You can choose Bronstein's algorithm by setting ``full=True``: >>> apart(y/(x**2 + x + 1), x) y/(x**2 + x + 1) >>> apart(y/(x**2 + x + 1), x, full=True) RootSum(_w**2 + _w + 1, Lambda(_a, (-2*_a*y/3 - y/3)/(-_a + x))) See Also ======== apart_list, assemble_partfrac_list """ allowed_flags(options, []) f = sympify(f) if f.is_Atom: return f else: P, Q = f.as_numer_denom() _options = options.copy() options = set_defaults(options, extension=True) try: (P, Q), opt = parallel_poly_from_expr((P, Q), x, **options) except PolynomialError as msg: if f.is_commutative: raise PolynomialError(msg) # non-commutative if f.is_Mul: c, nc = f.args_cnc(split_1=False) nc = Mul(*[apart(i, x=x, full=full, **_options) for i in nc]) if c: c = apart(Mul._from_args(c), x=x, full=full, **_options) return c*nc else: return nc elif f.is_Add: c = [] nc = [] for i in f.args: if i.is_commutative: c.append(i) else: try: nc.append(apart(i, x=x, full=full, **_options)) except NotImplementedError: nc.append(i) return apart(Add(*c), x=x, full=full, **_options) + Add(*nc) else: reps = [] pot = preorder_traversal(f) pot.next() for e in pot: try: reps.append((e, apart(e, x=x, full=full, **_options))) pot.skip() # this was handled successfully except NotImplementedError: pass return f.xreplace(dict(reps)) if P.is_multivariate: fc = f.cancel() if fc != f: return apart(fc, x=x, full=full, **_options) raise NotImplementedError( "multivariate partial fraction decomposition") common, P, Q = P.cancel(Q) poly, P = P.div(Q, auto=True) P, Q = P.rat_clear_denoms(Q) if Q.degree() <= 1: partial = P/Q else: if not full: partial = apart_undetermined_coeffs(P, Q) else: partial = apart_full_decomposition(P, Q) terms = S.Zero for term in Add.make_args(partial): if term.has(RootSum): terms += term else: terms += factor(term) return common*(poly.as_expr() + terms)
def __new__(cls, expr, *symbols, **assumptions): expr = sympify(expr).expand() if expr is S.NaN: return S.NaN if symbols: symbols = map(sympify, symbols) else: symbols = list(expr.free_symbols) symbols.sort(Basic.compare) if expr.is_Order: new_symbols = list(expr.variables) for s in symbols: if s not in new_symbols: new_symbols.append(s) if len(new_symbols) == len(expr.variables): return expr symbols = new_symbols elif symbols: symbol_map = {} new_symbols = [] for s in symbols: if isinstance(s, Symbol): new_symbols.append(s) continue z = Dummy('z') x1, s1 = solve4linearsymbol(s, z) expr = expr.subs(x1, s1) symbol_map[z] = s new_symbols.append(z) if symbol_map: r = Order(expr, *new_symbols, **assumptions) expr = r.expr.subs(symbol_map) symbols = [] for s in r.variables: if symbol_map.has_key(s): symbols.append(symbol_map[s]) else: symbols.append(s) else: if expr.is_Add: lst = expr.extract_leading_order(*symbols) expr = Add(*[f.expr for (e, f) in lst]) else: expr = expr.as_leading_term(*symbols) coeff, terms = expr.as_coeff_mul() if coeff is S.Zero: return coeff expr = Mul(*[t for t in terms if t.has(*symbols)]) elif expr is not S.Zero: expr = S.One if expr is S.Zero: return expr # create Order instance: obj = Expr.__new__(cls, expr, *symbols, **assumptions) return obj
def _entry(self, i, j): return Add(*[arg._entry(i, j) for arg in self.args])
def is_log_deriv_k_t_radical(fa, fd, DE, Df=True): """ Checks if Df is the logarithmic derivative of a k(t)-radical. b in k(t) can be written as the logarithmic derivative of a k(t) radical if there exist n in ZZ and u in k(t) with n, u != 0 such that n*b == Du/u. Either returns (ans, u, n, const) or None, which means that Df cannot be written as the logarithmic derivative of a k(t)-radical. ans is a list of tuples such that Mul(*[i**j for i, j in ans]) == u. This is useful for seeing exactly what elements of k(t) produce u. This function uses the structure theorem approach, which says that for any f in K, Df is the logarithmic derivative of a K-radical if and only if there are ri in QQ such that:: --- --- Dt \ r * Dt + \ r * i / i i / i --- = Df. --- --- t i in L i in E i K/C(x) K/C(x) Where C = Const(K), L_K/C(x) = { i in {1, ..., n} such that t_i is transcendental over C(x)(t_1, ..., t_i-1) and Dt_i = Da_i/a_i, for some a_i in C(x)(t_1, ..., t_i-1)* } (i.e., the set of all indices of logarithmic monomials of K over C(x)), and E_K/C(x) = { i in {1, ..., n} such that t_i is transcendental over C(x)(t_1, ..., t_i-1) and Dt_i/t_i = Da_i, for some a_i in C(x)(t_1, ..., t_i-1) } (i.e., the set of all indices of hyperexponential monomials of K over C(x)). If K is an elementary extension over C(x), then the cardinality of L_K/C(x) U E_K/C(x) is exactly the transcendence degree of K over C(x). Furthermore, because Const_D(K) == Const_D(C(x)) == C, deg(Dt_i) == 1 when t_i is in E_K/C(x) and deg(Dt_i) == 0 when t_i is in L_K/C(x), implying in particular that E_K/C(x) and L_K/C(x) are disjoint. The sets L_K/C(x) and E_K/C(x) must, by their nature, be computed recursively using this same function. Therefore, it is required to pass them as indices to D (or T). L_args are the arguments of the logarithms indexed by L_K (i.e., if i is in L_K, then T[i] == log(L_args[i])). This is needed to compute the final answer u such that n*f == Du/u. exp(f) will be the same as u up to a multiplicative constant. This is because they will both behave the same as monomials. For example, both exp(x) and exp(x + 1) == E*exp(x) satisfy Dt == t. Therefore, the term const is returned. const is such that exp(const)*f == u. This is calculated by subtracting the arguments of one exponential from the other. Therefore, it is necessary to pass the arguments of the exponential terms in E_args. To handle the case where we are given Df, not f, use is_log_deriv_k_t_radical_in_field(). """ H = [] if Df: dfa, dfd = (fd*derivation(fa, DE) - fa*derivation(fd, DE)).cancel(fd**2, include=True) else: dfa, dfd = fa, fd # Our assumption here is that each monomial is recursively transcendental if len(DE.L_K) + len(DE.E_K) != len(DE.D) - 1: if [i for i in DE.cases if i == 'tan'] or \ set([i for i in DE.cases if i == 'primitive']) - set(DE.L_K): raise NotImplementedError("Real version of the structure " "theorems with hypertangent support is not yet implemented.") # TODO: What should really be done in this case? raise NotImplementedError("Nonelementary extensions not supported " "in the structure theorems.") E_part = [DE.D[i].quo(Poly(DE.T[i], DE.T[i])).as_expr() for i in DE.E_K] L_part = [DE.D[i].as_expr() for i in DE.L_K] lhs = Matrix([E_part + L_part]) rhs = Matrix([dfa.as_expr()/dfd.as_expr()]) A, u = constant_system(lhs, rhs, DE) if not all(derivation(i, DE, basic=True).is_zero for i in u) or not A: # If the elements of u are not all constant # Note: See comment in constant_system # Also note: derivation(basic=True) calls cancel() return None else: if not all(i.is_Rational for i in u): # TODO: But maybe we can tell if they're not rational, like # log(2)/log(3). Also, there should be an option to continue # anyway, even if the result might potentially be wrong. raise NotImplementedError("Cannot work with non-rational " "coefficients in this case.") else: n = reduce(ilcm, [i.as_numer_denom()[1] for i in u]) u *= n terms = [DE.T[i] for i in DE.E_K] + DE.L_args ans = list(zip(terms, u)) result = Mul(*[Pow(i, j) for i, j in ans]) # exp(f) will be the same as result up to a multiplicative # constant. We now find the log of that constant. argterms = DE.E_args + [DE.T[i] for i in DE.L_K] const = cancel(fa.as_expr()/fd.as_expr() - Add(*[Mul(i, j/n) for i, j in zip(argterms, u)])) return (ans, result, n, const)
def test_MatAdd_postprocessor_xfail(): # This is difficult to get working because of the way that Add processes # its args. z = zeros(2) assert Add(z, S.NaN) == Add(S.NaN, z)
def test_Identity_doit(): Inn = Identity(Add(n, n, evaluate=False)) assert isinstance(Inn.rows, Add) assert Inn.doit() == Identity(2 * n) assert isinstance(Inn.doit().rows, Mul)
def collect(expr, syms, func=None, evaluate=None, exact=False, distribute_order_term=True): """ Collect additive terms of an expression. This function collects additive terms of an expression with respect to a list of expression up to powers with rational exponents. By the term symbol here are meant arbitrary expressions, which can contain powers, products, sums etc. In other words symbol is a pattern which will be searched for in the expression's terms. The input expression is not expanded by :func:`collect`, so user is expected to provide an expression is an appropriate form. This makes :func:`collect` more predictable as there is no magic happening behind the scenes. However, it is important to note, that powers of products are converted to products of powers using the :func:`expand_power_base` function. There are two possible types of output. First, if ``evaluate`` flag is set, this function will return an expression with collected terms or else it will return a dictionary with expressions up to rational powers as keys and collected coefficients as values. Examples ======== >>> from sympy import S, collect, expand, factor, Wild >>> from sympy.abc import a, b, c, x, y, z This function can collect symbolic coefficients in polynomials or rational expressions. It will manage to find all integer or rational powers of collection variable:: >>> collect(a*x**2 + b*x**2 + a*x - b*x + c, x) c + x**2*(a + b) + x*(a - b) The same result can be achieved in dictionary form:: >>> d = collect(a*x**2 + b*x**2 + a*x - b*x + c, x, evaluate=False) >>> d[x**2] a + b >>> d[x] a - b >>> d[S.One] c You can also work with multivariate polynomials. However, remember that this function is greedy so it will care only about a single symbol at time, in specification order:: >>> collect(x**2 + y*x**2 + x*y + y + a*y, [x, y]) x**2*(y + 1) + x*y + y*(a + 1) Also more complicated expressions can be used as patterns:: >>> from sympy import sin, log >>> collect(a*sin(2*x) + b*sin(2*x), sin(2*x)) (a + b)*sin(2*x) >>> collect(a*x*log(x) + b*(x*log(x)), x*log(x)) x*(a + b)*log(x) You can use wildcards in the pattern:: >>> w = Wild('w1') >>> collect(a*x**y - b*x**y, w**y) x**y*(a - b) It is also possible to work with symbolic powers, although it has more complicated behavior, because in this case power's base and symbolic part of the exponent are treated as a single symbol:: >>> collect(a*x**c + b*x**c, x) a*x**c + b*x**c >>> collect(a*x**c + b*x**c, x**c) x**c*(a + b) However if you incorporate rationals to the exponents, then you will get well known behavior:: >>> collect(a*x**(2*c) + b*x**(2*c), x**c) x**(2*c)*(a + b) Note also that all previously stated facts about :func:`collect` function apply to the exponential function, so you can get:: >>> from sympy import exp >>> collect(a*exp(2*x) + b*exp(2*x), exp(x)) (a + b)*exp(2*x) If you are interested only in collecting specific powers of some symbols then set ``exact`` flag in arguments:: >>> collect(a*x**7 + b*x**7, x, exact=True) a*x**7 + b*x**7 >>> collect(a*x**7 + b*x**7, x**7, exact=True) x**7*(a + b) You can also apply this function to differential equations, where derivatives of arbitrary order can be collected. Note that if you collect with respect to a function or a derivative of a function, all derivatives of that function will also be collected. Use ``exact=True`` to prevent this from happening:: >>> from sympy import Derivative as D, collect, Function >>> f = Function('f') (x) >>> collect(a*D(f,x) + b*D(f,x), D(f,x)) (a + b)*Derivative(f(x), x) >>> collect(a*D(D(f,x),x) + b*D(D(f,x),x), f) (a + b)*Derivative(f(x), x, x) >>> collect(a*D(D(f,x),x) + b*D(D(f,x),x), D(f,x), exact=True) a*Derivative(f(x), x, x) + b*Derivative(f(x), x, x) >>> collect(a*D(f,x) + b*D(f,x) + a*f + b*f, f) (a + b)*f(x) + (a + b)*Derivative(f(x), x) Or you can even match both derivative order and exponent at the same time:: >>> collect(a*D(D(f,x),x)**2 + b*D(D(f,x),x)**2, D(f,x)) (a + b)*Derivative(f(x), x, x)**2 Finally, you can apply a function to each of the collected coefficients. For example you can factorize symbolic coefficients of polynomial:: >>> f = expand((x + a + 1)**3) >>> collect(f, x, factor) x**3 + 3*x**2*(a + 1) + 3*x*(a + 1)**2 + (a + 1)**3 .. note:: Arguments are expected to be in expanded form, so you might have to call :func:`expand` prior to calling this function. See Also ======== collect_const, collect_sqrt, rcollect """ expr = sympify(expr) syms = list(syms) if iterable(syms) else [syms] if evaluate is None: evaluate = global_evaluate[0] def make_expression(terms): product = [] for term, rat, sym, deriv in terms: if deriv is not None: var, order = deriv while order > 0: term, order = Derivative(term, var), order - 1 if sym is None: if rat is S.One: product.append(term) else: product.append(Pow(term, rat)) else: product.append(Pow(term, rat * sym)) return Mul(*product) def parse_derivative(deriv): # scan derivatives tower in the input expression and return # underlying function and maximal differentiation order expr, sym, order = deriv.expr, deriv.variables[0], 1 for s in deriv.variables[1:]: if s == sym: order += 1 else: raise NotImplementedError( 'Improve MV Derivative support in collect') while isinstance(expr, Derivative): s0 = expr.variables[0] for s in expr.variables: if s != s0: raise NotImplementedError( 'Improve MV Derivative support in collect') if s0 == sym: expr, order = expr.expr, order + len(expr.variables) else: break return expr, (sym, Rational(order)) def parse_term(expr): """Parses expression expr and outputs tuple (sexpr, rat_expo, sym_expo, deriv) where: - sexpr is the base expression - rat_expo is the rational exponent that sexpr is raised to - sym_expo is the symbolic exponent that sexpr is raised to - deriv contains the derivatives the the expression for example, the output of x would be (x, 1, None, None) the output of 2**x would be (2, 1, x, None) """ rat_expo, sym_expo = S.One, None sexpr, deriv = expr, None if expr.is_Pow: if isinstance(expr.base, Derivative): sexpr, deriv = parse_derivative(expr.base) else: sexpr = expr.base if expr.exp.is_Number: rat_expo = expr.exp else: coeff, tail = expr.exp.as_coeff_Mul() if coeff.is_Number: rat_expo, sym_expo = coeff, tail else: sym_expo = expr.exp elif expr.func is exp: arg = expr.args[0] if arg.is_Rational: sexpr, rat_expo = S.Exp1, arg elif arg.is_Mul: coeff, tail = arg.as_coeff_Mul(rational=True) sexpr, rat_expo = exp(tail), coeff elif isinstance(expr, Derivative): sexpr, deriv = parse_derivative(expr) return sexpr, rat_expo, sym_expo, deriv def parse_expression(terms, pattern): """Parse terms searching for a pattern. terms is a list of tuples as returned by parse_terms; pattern is an expression treated as a product of factors """ pattern = Mul.make_args(pattern) if len(terms) < len(pattern): # pattern is longer than matched product # so no chance for positive parsing result return None else: pattern = [parse_term(elem) for elem in pattern] terms = terms[:] # need a copy elems, common_expo, has_deriv = [], None, False for elem, e_rat, e_sym, e_ord in pattern: if elem.is_Number and e_rat == 1 and e_sym is None: # a constant is a match for everything continue for j in range(len(terms)): if terms[j] is None: continue term, t_rat, t_sym, t_ord = terms[j] # keeping track of whether one of the terms had # a derivative or not as this will require rebuilding # the expression later if t_ord is not None: has_deriv = True if (term.match(elem) is not None and (t_sym == e_sym or t_sym is not None and e_sym is not None and t_sym.match(e_sym) is not None)): if exact is False: # we don't have to be exact so find common exponent # for both expression's term and pattern's element expo = t_rat / e_rat if common_expo is None: # first time common_expo = expo else: # common exponent was negotiated before so # there is no chance for a pattern match unless # common and current exponents are equal if common_expo != expo: common_expo = 1 else: # we ought to be exact so all fields of # interest must match in every details if e_rat != t_rat or e_ord != t_ord: continue # found common term so remove it from the expression # and try to match next element in the pattern elems.append(terms[j]) terms[j] = None break else: # pattern element not found return None return [_f for _f in terms if _f], elems, common_expo, has_deriv if evaluate: if expr.is_Mul: return expr.func(*[ collect(term, syms, func, True, exact, distribute_order_term) for term in expr.args ]) elif expr.is_Pow: b = collect(expr.base, syms, func, True, exact, distribute_order_term) return Pow(b, expr.exp) syms = [expand_power_base(i, deep=False) for i in syms] order_term = None if distribute_order_term: order_term = expr.getO() if order_term is not None: if order_term.has(*syms): order_term = None else: expr = expr.removeO() summa = [expand_power_base(i, deep=False) for i in Add.make_args(expr)] collected, disliked = defaultdict(list), S.Zero for product in summa: c, nc = product.args_cnc(split_1=False) args = list(ordered(c)) + nc terms = [parse_term(i) for i in args] small_first = True for symbol in syms: if SYMPY_DEBUG: print("DEBUG: parsing of expression %s with symbol %s " % (str(terms), str(symbol))) if isinstance(symbol, Derivative) and small_first: terms = list(reversed(terms)) small_first = not small_first result = parse_expression(terms, symbol) if SYMPY_DEBUG: print("DEBUG: returned %s" % str(result)) if result is not None: terms, elems, common_expo, has_deriv = result # when there was derivative in current pattern we # will need to rebuild its expression from scratch if not has_deriv: margs = [] for elem in elems: if elem[2] is None: e = elem[1] else: e = elem[1] * elem[2] margs.append(Pow(elem[0], e)) index = Mul(*margs) else: index = make_expression(elems) terms = expand_power_base(make_expression(terms), deep=False) index = expand_power_base(index, deep=False) collected[index].append(terms) break else: # none of the patterns matched disliked += product # add terms now for each key collected = {k: Add(*v) for k, v in collected.items()} if disliked is not S.Zero: collected[S.One] = disliked if order_term is not None: for key, val in collected.items(): collected[key] = val + order_term if func is not None: collected = dict([(key, func(val)) for key, val in collected.items()]) if evaluate: return Add(*[key * val for key, val in collected.items()]) else: return collected
def _eval_integral(self, f, x, meijerg=None): """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) and not meijerg: 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 and not meijerg: return poly.integrate().as_expr() # 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 = Add.make_args(f) for g in args: coeff, g = g.as_independent(x) # g(x) = const if g is S.One and not meijerg: parts.append(coeff*x) continue # g(x) = expr + O(x**n) order_term = g.getO() if order_term is not None: h = self._eval_integral(g.removeO(), x) if h is not None: h_order_expr = self._eval_integral(order_term.expr, x) if h_order_expr is not None: h_order_term = order_term.func(h_order_expr, *order_term.variables) parts.append(coeff*(h + h_order_term)) continue # NOTE: if there is O(x**n) and we fail to integrate then there is # no point in trying other methods because they will fail anyway. return None # c # g(x) = (a*x+b) if g.is_Pow and not g.exp.has(x) and not meijerg: 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) and not meijerg: parts.append(coeff * ratint(g, x)) continue if not meijerg: # 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 if not meijerg: # fall back to the more general algorithm try: h = heurisch(g, x, hints=[]) except PolynomialError: # XXX: this exception means there is a bug in the # implementation of heuristic Risch integration # algorithm. h = None else: h = None if meijerg is not False and h is None: # rewrite using G functions h = meijerint_indefinite(g, x) if h is not None: parts.append(coeff * h) continue # 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, meijerg) if h is not None: parts.append(coeff * h) else: return None return Add(*parts)
def test_MatAdd_postprocessor(): # Some of these are nonsensical, but we do not raise errors for Add # because that breaks algorithms that want to replace matrices with dummy # symbols. z = zeros(2) assert Add(0, z) == Add(z, 0) == z a = Add(S.Infinity, z) assert a == Add(z, S.Infinity) assert isinstance(a, Add) assert a.args == (S.Infinity, z) a = Add(S.ComplexInfinity, z) assert a == Add(z, S.ComplexInfinity) assert isinstance(a, Add) assert a.args == (S.ComplexInfinity, z) a = Add(z, S.NaN) # assert a == Add(S.NaN, z) # See the XFAIL above assert isinstance(a, Add) assert a.args == (S.NaN, z) M = Matrix([[1, 2], [3, 4]]) a = Add(x, M) assert a == Add(M, x) assert isinstance(a, Add) assert a.args == (x, M) A = MatrixSymbol("A", 2, 2) assert Add(A, M) == Add(M, A) == A + M # Scalars should be absorbed into constant matrices (producing an error) a = Add(x, M, A) assert (a == Add(M, x, A) == Add(M, A, x) == Add(x, A, M) == Add(A, x, M) == Add(A, M, x)) assert isinstance(a, Add) assert a.args == (x, A + M) assert Add(M, M) == 2 * M assert Add(M, A, M) == Add(M, M, A) == Add(A, M, M) == A + 2 * M a = Add(A, x, M, M, x) assert isinstance(a, Add) assert a.args == (2 * x, A + 2 * M)
def __trigsimp(expr, deep=False): """recursive helper for trigsimp""" from sympy.simplify.fu import TR10i if _trigpat is None: _trigpats() a, b, c, d, matchers_division, matchers_add, \ matchers_identity, artifacts = _trigpat if expr.is_Mul: # do some simplifications like sin/cos -> tan: if not expr.is_commutative: com, nc = expr.args_cnc() expr = _trigsimp(Mul._from_args(com), deep)*Mul._from_args(nc) else: for i, (pattern, simp, ok1, ok2) in enumerate(matchers_division): if not _dotrig(expr, pattern): continue newexpr = _match_div_rewrite(expr, i) if newexpr is not None: if newexpr != expr: expr = newexpr break else: continue # use SymPy matching instead res = expr.match(pattern) if res and res.get(c, 0): if not res[c].is_integer: ok = ok1.subs(res) if not ok.is_positive: continue ok = ok2.subs(res) if not ok.is_positive: continue # if "a" contains any of trig or hyperbolic funcs with # argument "b" then skip the simplification if any(w.args[0] == res[b] for w in res[a].atoms( TrigonometricFunction, HyperbolicFunction)): continue # simplify and finish: expr = simp.subs(res) break # process below if expr.is_Add: args = [] for term in expr.args: if not term.is_commutative: com, nc = term.args_cnc() nc = Mul._from_args(nc) term = Mul._from_args(com) else: nc = S.One term = _trigsimp(term, deep) for pattern, result in matchers_identity: res = term.match(pattern) if res is not None: term = result.subs(res) break args.append(term*nc) if args != expr.args: expr = Add(*args) expr = min(expr, expand(expr), key=count_ops) if expr.is_Add: for pattern, result in matchers_add: if not _dotrig(expr, pattern): continue expr = TR10i(expr) if expr.has(HyperbolicFunction): res = expr.match(pattern) # if "d" contains any trig or hyperbolic funcs with # argument "a" or "b" then skip the simplification; # this isn't perfect -- see tests if res is None or not (a in res and b in res) or any( w.args[0] in (res[a], res[b]) for w in res[d].atoms( TrigonometricFunction, HyperbolicFunction)): continue expr = result.subs(res) break # Reduce any lingering artifacts, such as sin(x)**2 changing # to 1 - cos(x)**2 when sin(x)**2 was "simpler" for pattern, result, ex in artifacts: if not _dotrig(expr, pattern): continue # Substitute a new wild that excludes some function(s) # to help influence a better match. This is because # sometimes, for example, 'a' would match sec(x)**2 a_t = Wild('a', exclude=[ex]) pattern = pattern.subs(a, a_t) result = result.subs(a, a_t) m = expr.match(pattern) was = None while m and was != expr: was = expr if m[a_t] == 0 or \ -m[a_t] in m[c].args or m[a_t] + m[c] == 0: break if d in m and m[a_t]*m[d] + m[c] == 0: break expr = result.subs(m) m = expr.match(pattern) m.setdefault(c, S.Zero) elif expr.is_Mul or expr.is_Pow or deep and expr.args: expr = expr.func(*[_trigsimp(a, deep) for a in expr.args]) try: if not expr.has(*_trigs): raise TypeError e = expr.atoms(exp) new = expr.rewrite(exp, deep=deep) if new == e: raise TypeError fnew = factor(new) if fnew != new: new = sorted([new, factor(new)], key=count_ops)[0] # if all exp that were introduced disappeared then accept it if not (new.atoms(exp) - e): expr = new except TypeError: pass return expr
def _parallel_dict_from_expr_no_gens(exprs, opt): """Transform expressions into a multinomial form and figure out generators. """ if opt.domain is not None: def _is_coeff(factor): return factor in opt.domain elif opt.extension is True: def _is_coeff(factor): return factor.is_algebraic elif opt.greedy is not False: def _is_coeff(factor): return False else: def _is_coeff(factor): return factor.is_number gens, reprs = set([]), [] for expr in exprs: terms = [] if expr.is_Equality: expr = expr.lhs - expr.rhs for term in Add.make_args(expr): coeff, elements = [], {} for factor in Mul.make_args(term): if not _not_a_coeff(factor) and (factor.is_Number or _is_coeff(factor)): coeff.append(factor) else: if opt.series is False: base, exp = decompose_power(factor) if exp < 0: exp, base = -exp, Pow(base, -S.One) else: base, exp = decompose_power_rat(factor) elements[base] = elements.setdefault(base, 0) + exp gens.add(base) terms.append((coeff, elements)) reprs.append(terms) if not gens: if len(exprs) == 1: arg = exprs[0] else: arg = (exprs, ) raise GeneratorsNeeded("specify generators to give %s a meaning" % arg) gens = _sort_gens(gens, opt=opt) k, indices = len(gens), {} for i, g in enumerate(gens): indices[g] = i polys = [] for terms in reprs: poly = {} for coeff, term in terms: monom = [0] * k for base, exp in term.items(): monom[indices[base]] = exp monom = tuple(monom) if monom in poly: poly[monom] += Mul(*coeff) else: poly[monom] = Mul(*coeff) polys.append(poly) return polys, tuple(gens)