def conjuncts(expr): """Return a list of the conjuncts in the expr s. >>> from sympy.logic.boolalg import conjuncts >>> from sympy.abc import A, B >>> conjuncts(A & B) [A, B] >>> conjuncts(A | B) [Or(A, B)] """ return make_list(expr, And)
def disjuncts(expr): """Return a list of the disjuncts in the sentence s. >>> from sympy.logic.boolalg import disjuncts >>> from sympy.abc import A, B >>> disjuncts(A | B) [A, B] >>> disjuncts(A & B) [And(A, B)] """ return make_list(expr, Or)
def disjuncts(expr): """Return a list of the disjuncts in the sentence s. >>> from sympy.logic.boolalg import disjuncts >>> from sympy.abc import A, B >>> disjuncts(A | B) [A, B] >>> disjuncts(A & B) [And(A, B)] """ from sympy.utilities import make_list return make_list(expr, Or)
def conjuncts(expr): """Return a list of the conjuncts in the expr s. >>> from sympy.logic.boolalg import conjuncts >>> from sympy.abc import A, B >>> conjuncts(A & B) [A, B] >>> conjuncts(A | B) [Or(A, B)] """ from sympy.utilities import make_list return make_list(expr, And)
def parse_expression(terms, pattern): pattern = make_list(pattern, Mul) 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 ] elems, common_expo, has_deriv = [], Rational(1), False for elem, e_rat, e_sym, e_ord in pattern: if e_ord is not None: # there is derivative in the pattern so # there will by small performance penalty has_deriv = True for j in range(len(terms)): term, t_rat, t_sym, t_ord = terms[j] if elem == term and e_sym == t_sym: if exact == False: # we don't have to exact so find common exponent # for both expression's term and pattern's element expo = t_rat / e_rat if isinstance(common_expo, Basic.One): common_expo = expo else: # common exponent was negotiated before so # teher is no chance for pattern match unless # common and current exponents are equal if common_expo != expo: return None else: # we ought to be exact so all fields of # interest must match in very 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]) del terms[j] break else: # pattern element not found return None return terms, elems, common_expo, has_deriv
def to_int_repr(clauses, symbols): """ takes clauses in CNF puts them into integer representation Examples: >>> from sympy.logic.boolalg import to_int_repr >>> from sympy.abc import x, y >>> to_int_repr([x | y, y], [x, y]) == [set([1, 2]), set([2])] True """ def append_symbol(arg, symbols): if arg.func is Not: return -(symbols.index(arg.args[0])+1) else: return symbols.index(arg)+1 from sympy.utilities import make_list return [set(append_symbol(arg, symbols) for arg in make_list(c, Or)) \ for c in clauses]
def to_int_repr(clauses, symbols): """ takes clauses in CNF puts them into integer representation Examples: >>> from sympy.logic.boolalg import to_int_repr >>> from sympy.abc import x, y >>> to_int_repr([x | y, y], [x, y]) == [set([1, 2]), set([2])] True """ def append_symbol(arg, symbols): if arg.func is Not: return -(symbols.index(arg.args[0]) + 1) else: return symbols.index(arg) + 1 from sympy.utilities import make_list return [set(append_symbol(arg, symbols) for arg in make_list(c, Or)) \ for c in clauses]
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 """ pattern = make_list(pattern, Mul) 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] elems, common_expo, has_deriv = [], None, False for elem, e_rat, e_sym, e_ord in pattern: if e_ord is not None: # there is derivative in the pattern so # there will by small performance penalty has_deriv = True for j in range(len(terms)): term, t_rat, t_sym, t_ord = terms[j] if elem.is_Number: # a constant is a match for everything break if elem == term and e_sym == t_sym: if exact == False: # we don't have to 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 # teher is no chance for 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 very 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]) del terms[j] break else: # pattern element not found return None return terms, elems, common_expo, has_deriv
def collect(expr, syms, evaluate=True, exact=False): """Collect additive terms with respect to a list of symbols 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. This function will not apply any redundant expanding to the input expression, so user is assumed to enter expression in final form. This makes 'collect' more predictable as there is no magic behind the scenes. However it is important to note, that powers of products are converted to products of powers using 'separate' function. There are two possible types of output. First, if 'evaluate' flag is set, this function will return a single expression or else it will return a dictionary with separated symbols up to rational powers as keys and collected sub-expressions as values respectively. >>> from sympy import * >>> x, y, z = symbols('x', 'y', 'z') >>> a, b, c = symbols('a', 'b', 'c') This function can collect symbolic coefficients in polynomial 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*(a - b) + x**2*(a + b) The same result can 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[sympify(1)] c You can also work with multi-variate 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*y + y*(1 + a) + x**2*(1 + y) Also more complicated expressions can be used as patterns: >>> 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) It is also possible to work with symbolic powers, although it has more complicated behaviour, 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 behaviour: >>> collect(a*x**(2*c) + b*x**(2*c), x**c) (x**2)**c*(a + b) Note also that all previously stated facts about 'collect' function apply to the exponential function, so you can get: >>> 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 arbitary order can be collected: >>> from sympy import Derivative as D >>> f = Function('f') (x) >>> collect(a*D(f,x) + b*D(f,x), D(f,x)) (a + b)*D(f(x), x) >>> collect(a*D(D(f,x),x) + b*D(D(f,x),x), D(f,x)) (a + b)*D(f(x), x, x) >>> collect(a*D(D(f,x),x) + b*D(D(f,x),x), D(f,x), exact=True) a*D(f(x), x, x) + b*D(f(x), x, x) >>> collect(a*D(D(f,x),x) + b*D(D(f,x),x) + a*D(f,x) + b*D(f,x), D(f,x)) (a + b)*D(f(x), x) + (a + b)*D(f(x), x, x) Or you can even match both derivative order and exponent at time:: >>> collect(a*D(D(f,x),x)**2 + b*D(D(f,x),x)**2, D(f,x)) == \\ ... (a + b)*D(D(f,x),x)**2 True == Notes == - arguments are expected to be in expanded form, so you might have tos call expand() prior to calling this function. """ 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.symbols[0], 1 for s in deriv.symbols[1:]: if s == sym: order += 1 else: raise NotImplementedError( 'Improve MV Derivative support in collect') while isinstance(expr, Derivative): s0 = expr.symbols[0] for s in expr.symbols: if s != s0: raise NotImplementedError( 'Improve MV Derivative support in collect') if s0 == sym: expr, order = expr.expr, order + len(expr.symbols) 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_Rational: rat_expo = expr.exp elif expr.exp.is_Mul: coeff, tail = expr.exp.as_coeff_terms() if coeff.is_Rational: rat_expo, sym_expo = coeff, C.Mul(*tail) else: sym_expo = expr.exp else: sym_expo = expr.exp elif expr.func is C.exp: if expr.args[0].is_Rational: sexpr, rat_expo = S.Exp1, expr.args[0] elif expr.args[0].is_Mul: coeff, tail = expr.args[0].as_coeff_terms() if coeff.is_Rational: sexpr, rat_expo = C.exp(C.Mul(*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 """ pattern = make_list(pattern, Mul) 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] elems, common_expo, has_deriv = [], None, False for elem, e_rat, e_sym, e_ord in pattern: if e_ord is not None: # there is derivative in the pattern so # there will by small performance penalty has_deriv = True for j in range(len(terms)): term, t_rat, t_sym, t_ord = terms[j] if elem.is_Number: # a constant is a match for everything break if elem == term and e_sym == t_sym: if exact == False: # we don't have to 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 # teher is no chance for 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 very 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]) del terms[j] break else: # pattern element not found return None return terms, elems, common_expo, has_deriv if evaluate: if expr.is_Mul: ret = 1 for term in expr.args: ret *= collect(term, syms, True, exact) return ret elif expr.is_Pow: b = collect(expr.base, syms, True, exact) return C.Pow(b, expr.exp) summa = [separate(i) for i in make_list(sympify(expr), Add)] if isinstance(syms, list): syms = [separate(s) for s in syms] else: syms = [separate(syms)] collected, disliked = {}, S.Zero for product in summa: terms = [parse_term(i) for i in make_list(product, Mul)] for symbol in syms: if SYMPY_DEBUG: print "DEBUG: parsing of expression %s with symbol %s " % ( str(terms), str(symbol)) 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: index = 1 for elem in elems: index *= Pow(elem[0], elem[1]) if elem[2] is not None: index **= elem[2] else: index = make_expression(elems) terms = separate(make_expression(terms)) index = separate(index) if index in collected.keys(): collected[index] += terms else: collected[index] = terms break else: # none of the patterns matched disliked += product if disliked is not S.Zero: collected[S.One] = disliked if evaluate: return Add(*[a * b for a, b in collected.iteritems()]) else: return collected
def _together(expr): from sympy.core.function import Function if expr.is_Add: items, coeffs, basis = [], [], {} for elem in expr.args: numer, q = fraction(_together(elem)) denom = {} for term in make_list(q.expand(), Mul): expo = S.One coeff = S.One if term.is_Pow: if term.exp.is_Rational: term, expo = term.base, term.exp elif term.exp.is_Mul: coeff, tail = term.exp.as_coeff_terms() if coeff.is_Rational: tail = C.Mul(*tail) term, expo = Pow(term.base, tail), coeff coeff = S.One elif term.func is C.exp: if term.args[0].is_Rational: term, expo = S.Exp1, term.args[0] elif term.args[0].is_Mul: coeff, tail = term.args[0].as_coeff_terms() if coeff.is_Rational: tail = C.Mul(*tail) term, expo = C.exp(tail), coeff coeff = S.One elif term.is_Rational: coeff = Integer(term.q) term = Integer(term.p) if term in denom: denom[term] += expo else: denom[term] = expo if term in basis: total, maxi = basis[term] n_total = total + expo n_maxi = max(maxi, expo) basis[term] = (n_total, n_maxi) else: basis[term] = (expo, expo) coeffs.append(coeff) items.append((numer, denom)) numerator, denominator = [], [] for (term, (total, maxi)) in basis.iteritems(): basis[term] = (total, total - maxi) if term.func is C.exp: denominator.append(C.exp(maxi * term.args[0])) else: if maxi is S.One: denominator.append(term) else: denominator.append(Pow(term, maxi)) if all([c.is_integer for c in coeffs]): gcds = lambda x, y: igcd(int(x), int(y)) common = Rational(reduce(gcds, coeffs)) else: common = S.One product = Mul(*coeffs) / common for ((numer, denom), coeff) in zip(items, coeffs): expr, coeff = [], product / (coeff * common) for term in basis.iterkeys(): total, sub = basis[term] if term in denom: expo = total - denom[term] - sub else: expo = total - sub if term.func is C.exp: expr.append(C.exp(expo * term.args[0])) else: if expo is S.One: expr.append(term) else: expr.append(Pow(term, expo)) numerator.append(coeff * Mul(*([numer] + expr))) return Add(*numerator) / (product * Mul(*denominator)) elif expr.is_Mul or expr.is_Pow: return type(expr)(*[_together(t) for t in expr.args]) elif expr.is_Function and deep: return expr.func(*[_together(t) for t in expr.args]) else: return expr
def fraction(expr, exact=False): """Returns a pair with expression's numerator and denominator. If the given expression is not a fraction then this function will assume that the denominator is equal to one. This function will not make any attempt to simplify nested fractions or to do any term rewriting at all. If only one of the numerator/denominator pair is needed then use numer(expr) or denom(expr) functions respectively. >>> from sympy import * >>> x, y = symbols('x', 'y') >>> fraction(x/y) (x, y) >>> fraction(x) (x, 1) >>> fraction(1/y**2) (1, y**2) >>> fraction(x*y/2) (x*y, 2) >>> fraction(Rational(1, 2)) (1, 2) This function will also work fine with assumptions: >>> k = Symbol('k', negative=True) >>> fraction(x * y**k) (x, y**(-k)) If we know nothing about sign of some exponent and 'exact' flag is unset, then structure this exponent's structure will be analyzed and pretty fraction will be returned: >>> fraction(2*x**(-y)) (2, x**y) #>>> fraction(exp(-x)) #(1, exp(x)) >>> fraction(exp(-x), exact=True) (exp(-x), 1) """ expr = sympify(expr) numer, denom = [], [] for term in make_list(expr, Mul): if term.is_Pow: if term.exp.is_negative: if term.exp is S.NegativeOne: denom.append(term.base) else: denom.append(Pow(term.base, -term.exp)) elif not exact and term.exp.is_Mul: coeff, tail = term.exp.args[0], Mul( *term.exp.args[1:]) #term.exp.getab() if coeff.is_Rational and coeff.is_negative: denom.append(Pow(term.base, -term.exp)) else: numer.append(term) else: numer.append(term) elif term.func is C.exp: if term.args[0].is_negative: denom.append(C.exp(-term.args[0])) elif not exact and term.args[0].is_Mul: coeff, tail = term.args[0], Mul( *term.args[1:]) #term.args.getab() if coeff.is_Rational and coeff.is_negative: denom.append(C.exp(-term.args[0])) else: numer.append(term) else: numer.append(term) elif term.is_Rational: if term.is_integer: numer.append(term) else: numer.append(Rational(term.p)) denom.append(Rational(term.q)) else: numer.append(term) return Mul(*numer), Mul(*denom)
def fraction(expr, exact=False): """Returns a pair with expression's numerator and denominator. If the given expression is not a fraction then this function will assume that the denominator is equal to one. This function will not make any attempt to simplify nested fractions or to do any term rewriting at all. If only one of the numerator/denominator pair is needed then use numer(expr) or denom(expr) functions respectively. >>> from sympy import * >>> x, y = symbols('x', 'y') >>> fraction(x/y) (x, y) >>> fraction(x) (x, 1) >>> fraction(1/y**2) (1, y**2) >>> fraction(x*y/2) (x*y, 2) >>> fraction(Rational(1, 2)) (1, 2) This function will also work fine with assumptions: >>> k = Symbol('k', negative=True) >>> fraction(x * y**k) (x, y**(-k)) If we know nothing about sign of some exponent and 'exact' flag is unset, then structure this exponent's structure will be analyzed and pretty fraction will be returned: >>> fraction(2*x**(-y)) (2, x**y) #>>> fraction(exp(-x)) #(1, exp(x)) >>> fraction(exp(-x), exact=True) (exp(-x), 1) """ expr = Basic.sympify(expr) #XXX this only works sometimes (caching bug?) if expr == exp(-Symbol("x")) and exact: return (expr, 1) numer, denom = [], [] for term in make_list(expr, Mul): if isinstance(term, Pow): if term.exp.is_negative: if term.exp == Integer(-1): denom.append(term.base) else: denom.append(Pow(term.base, -term.exp)) elif not exact and isinstance(term.exp, Mul): coeff, tail = term.exp[0], Mul(*term.exp[1:])#term.exp.getab() if isinstance(coeff, Rational) and coeff.is_negative: denom.append(Pow(term.base, -term.exp)) else: numer.append(term) else: numer.append(term) elif isinstance(term, Basic.exp): if term[0].is_negative: denom.append(Basic.exp(-term[0])) elif not exact and isinstance(term[0], Mul): coeff, tail = term[0], Mul(*term[1:])#term.args.getab() if isinstance(coeff, Rational) and coeff.is_negative: denom.append(Basic.exp(-term[0])) else: numer.append(term) else: numer.append(term) elif isinstance(term, Rational): if term.is_integer: numer.append(term) else: numer.append(Rational(term.p)) denom.append(Rational(term.q)) else: numer.append(term) return Mul(*numer), Mul(*denom)
def collect(expr, syms, evaluate=True, exact=False): """Collect additive terms with respect to a list of symbols 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. This function will not apply any redundant expanding to the input expression, so user is assumed to enter expression in final form. This makes 'collect' more predictable as there is no magic behind the scenes. However it is important to note, that powers of products are converted to products of powers using 'separate' function. There are two possible types of output. First, if 'evaluate' flag is set, this function will return a single expression or else it will return a dictionary with separated symbols up to rational powers as keys and collected sub-expressions as values respectively. >>> from sympy import * >>> x, y, z = symbols('x', 'y', 'z') >>> a, b, c = symbols('a', 'b', 'c') This function can collect symbolic coefficients in polynomial 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*(a - b) + x**2*(a + b) The same result can achieved in dictionary form: >>> collect(a*x**2 + b*x**2 + a*x - b*x + c, x, evaluate=False) {1: c, x**2: a + b, x: a - b} You can also work with multi-variate 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*y + y*(1 + a) + x**2*(1 + y) >>> collect(x**2*y**4 + z*(x*y**2)**2 + z + a*z, [x*y**2, z]) z*(1 + a) + x**2*y**4*(1 + z) Also more complicated expressions can be used as patterns: >>> collect(a*sin(2*x) + b*sin(2*x), sin(2*x)) (a + b)*sin(2*x) >>> collect(a*x**2*log(x)**2 + b*(x*log(x))**2, x*log(x)) x**2*log(x)**2*(a + b) It is also possible to work with symbolic powers, although it has more complicated behaviour, 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 behaviour: #>>> collect(a*x**(2*c) + b*x**(2*c), x**c) #x**(2*c)*(a + b) Note also that all previously stated facts about 'collect' function apply to the exponential function, so you can get: #>>> 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 arbitary order can be collected: #>>> from sympy import Derivative as D #>>> f = Function(x) #>>> collect(a*D(f,x) + b*D(f,x), D(f,x)) #(a+b)*Function'(x) #>>> collect(a*D(D(f,x),x) + b*D(D(f,x),x), D(f,x)) #(a+b)*(Function'(x))' #>>> collect(a*D(D(f,x),x) + b*D(D(f,x),x), D(f,x), exact=True) #a*(Function'(x))'+b*(Function'(x))' #>>> collect(a*D(D(f,x),x) + b*D(D(f,x),x) + a*D(f,x) + b*D(f,x), D(f,x)) #(a+b)*Function'(x)+(a+b)*(Function'(x))' Or you can even match both derivative order and exponent at time: #>>> collect(a*D(D(f,x),x)**2 + b*D(D(f,x),x)**2, D(f,x)) #(a+b)*(Function'(x))'**2 """ 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 isinstance(rat, Basic.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.f, deriv.x, 1 while isinstance(expr, Derivative) and expr.x == sym: expr, order = expr.f, order+1 return expr, (sym, Rational(order)) def parse_term(expr): rat_expo, sym_expo = Rational(1), None sexpr, deriv = expr, None if isinstance(expr, Pow): if isinstance(expr.base, Derivative): sexpr, deriv = parse_derivative(expr.base) else: sexpr = expr.base if isinstance(expr.exp, Rational): rat_expo = expr.exp elif isinstance(expr.exp, Mul): coeff, tail = term.exp.as_coeff_terms() if isinstance(coeff, Rational): rat_expo, sym_expo = coeff, Basic.Mul(*tail) else: sym_expo = expr.exp else: sym_expo = expr.exp elif isinstance(expr, Basic.exp): if isinstance(expr[0], Rational): sexpr, rat_expo = Basic.exp(Rational(1)), expr[0] elif isinstance(expr[0], Mul): coeff, tail = expr[0].as_coeff_terms() if isinstance(coeff, Rational): sexpr, rat_expo = Basic.exp(Basic.Mul(*tail)), coeff elif isinstance(expr, Derivative): sexpr, deriv = parse_derivative(expr) return sexpr, rat_expo, sym_expo, deriv def parse_expression(terms, pattern): pattern = make_list(pattern, Mul) 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 ] elems, common_expo, has_deriv = [], Rational(1), False for elem, e_rat, e_sym, e_ord in pattern: if e_ord is not None: # there is derivative in the pattern so # there will by small performance penalty has_deriv = True for j in range(len(terms)): term, t_rat, t_sym, t_ord = terms[j] if elem == term and e_sym == t_sym: if exact == False: # we don't have to exact so find common exponent # for both expression's term and pattern's element expo = t_rat / e_rat if isinstance(common_expo, Basic.One): common_expo = expo else: # common exponent was negotiated before so # teher is no chance for pattern match unless # common and current exponents are equal if common_expo != expo: return None else: # we ought to be exact so all fields of # interest must match in very 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]) del terms[j] break else: # pattern element not found return None return terms, elems, common_expo, has_deriv if evaluate: if isinstance(expr, Basic.Mul): ret = 1 for term in expr: ret *= collect(term, syms, True, exact) return ret elif isinstance(expr, Basic.Pow): b = collect(expr.base, syms, True, exact) return Basic.Pow(b, expr.exp) summa = [ separate(i) for i in make_list(Basic.sympify(expr), Add) ] if isinstance(syms, list): syms = [ separate(s) for s in syms ] else: syms = [ separate(syms) ] collected, disliked = {}, Rational(0) for product in summa: terms = [ parse_term(i) for i in make_list(product, Mul) ] for symbol in syms: result = parse_expression(terms, symbol) 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: index = Pow(symbol, common_expo) else: index = make_expression(elems) terms = separate(make_expression(terms)) index = separate(index) if index in collected: collected[index] += terms else: collected[index] = terms break else: # none of the patterns matched disliked += product if disliked != Rational(0): collected[Rational(1)] = disliked if evaluate: return Add(*[ a*b for a, b in collected.iteritems() ]) else: return collected
def _together(expr): from sympy.core.function import Function if isinstance(expr, Add): items, coeffs, basis = [], [], {} for elem in expr: numer, q = fraction(_together(elem)) denom = {} for term in make_list(q.expand(), Mul): expo = Integer(1) coeff = Integer(1) if isinstance(term, Pow): if isinstance(term.exp, Rational): term, expo = term.base, term.exp elif isinstance(term.exp, Mul): coeff, tail = term.exp.as_coeff_terms() if isinstance(coeff, Rational): tail = Basic.Mul(*tail) term, expo = Pow(term.base, tail), coeff coeff = Integer(1) elif isinstance(term, Basic.exp): if isinstance(term[0], Rational): term, expo = Basic.E, term[0] elif isinstance(term[0], Mul): coeff, tail = term[0].as_coeff_terms() if isinstance(coeff, Rational): tail = Basic.Mul(*tail) term, expo = Basic.exp(tail), coeff coeff = Integer(1) elif isinstance(term, Rational): coeff = Integer(term.q) term = Integer(term.p) if term in denom: denom[term] += expo else: denom[term] = expo if term in basis: total, maxi = basis[term] n_total = total + expo n_maxi = max(maxi, expo) basis[term] = (n_total, n_maxi) else: basis[term] = (expo, expo) coeffs.append(coeff) items.append((numer, denom)) numerator, denominator = [], [] for (term, (total, maxi)) in basis.iteritems(): basis[term] = (total, total-maxi) if isinstance(term, Basic.exp): denominator.append(Basic.exp(maxi*term[:])) else: if isinstance(maxi, Basic.One): denominator.append(term) else: denominator.append(Pow(term, maxi)) from sympy.core.numbers import gcd as int_gcd if all([ c.is_integer for c in coeffs ]): gcds = lambda x, y: int_gcd(int(x), int(y)) common = Rational(reduce(gcds, coeffs)) else: common = Rational(1) product = reduce(lambda x, y: x*y, coeffs) / common for ((numer, denom), coeff) in zip(items, coeffs): expr, coeff = [], product / (coeff*common) for term in basis.iterkeys(): total, sub = basis[term] if term in denom: expo = total-denom[term]-sub else: expo = total-sub if isinstance(term, Basic.exp): expr.append(Basic.exp(expo*term[:])) else: if isinstance(expo, Basic.One): expr.append(term) else: expr.append(Pow(term, expo)) numerator.append(coeff*Mul(*([numer] + expr))) return Add(*numerator)/(product*Mul(*denominator)) elif isinstance(expr, (Mul, Pow)): return type(expr)(*[ _together(t) for t in expr ]) elif isinstance(expr, Function) and deep: return expr.func(*[ _together(t) for t in expr ]) else: return expr
def collect(expr, syms, evaluate=True, exact=False): """Collect additive terms with respect to a list of symbols 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. This function will not apply any redundant expanding to the input expression, so user is assumed to enter expression in final form. This makes 'collect' more predictable as there is no magic behind the scenes. However it is important to note, that powers of products are converted to products of powers using 'separate' function. There are two possible types of output. First, if 'evaluate' flag is set, this function will return a single expression or else it will return a dictionary with separated symbols up to rational powers as keys and collected sub-expressions as values respectively. >>> from sympy import * >>> x, y, z = symbols('x', 'y', 'z') >>> a, b, c = symbols('a', 'b', 'c') This function can collect symbolic coefficients in polynomial 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*(a - b) + x**2*(a + b) The same result can 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[sympify(1)] c You can also work with multi-variate 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*y + y*(1 + a) + x**2*(1 + y) Also more complicated expressions can be used as patterns: >>> 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) It is also possible to work with symbolic powers, although it has more complicated behaviour, 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 behaviour: >>> collect(a*x**(2*c) + b*x**(2*c), x**c) (x**2)**c*(a + b) Note also that all previously stated facts about 'collect' function apply to the exponential function, so you can get: >>> 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 arbitary order can be collected: >>> from sympy import Derivative as D >>> f = Function('f') (x) >>> collect(a*D(f,x) + b*D(f,x), D(f,x)) (a + b)*D(f(x), x) >>> collect(a*D(D(f,x),x) + b*D(D(f,x),x), D(f,x)) (a + b)*D(f(x), x, x) >>> collect(a*D(D(f,x),x) + b*D(D(f,x),x), D(f,x), exact=True) a*D(f(x), x, x) + b*D(f(x), x, x) >>> collect(a*D(D(f,x),x) + b*D(D(f,x),x) + a*D(f,x) + b*D(f,x), D(f,x)) (a + b)*D(f(x), x) + (a + b)*D(f(x), x, x) Or you can even match both derivative order and exponent at time: >>> collect(a*D(D(f,x),x)**2 + b*D(D(f,x),x)**2, D(f,x)) == \ (a + b)*D(D(f,x),x)**2 True Notes ===== - arguments are expected to be in expanded form, so you might have to call .expand prior to calling this function. """ 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.symbols[0], 1 for s in deriv.symbols[1:]: if s == sym: order += 1 else: raise NotImplementedError('Improve MV Derivative support in collect') while isinstance(expr, Derivative): s0 = expr.symbols[0] for s in expr.symbols: if s != s0: raise NotImplementedError('Improve MV Derivative support in collect') if s0 == sym: expr, order = expr.expr, order+len(expr.symbols) 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_Rational: rat_expo = expr.exp elif expr.exp.is_Mul: coeff, tail = expr.exp.as_coeff_terms() if coeff.is_Rational: rat_expo, sym_expo = coeff, C.Mul(*tail) else: sym_expo = expr.exp else: sym_expo = expr.exp elif expr.func is C.exp: if expr.args[0].is_Rational: sexpr, rat_expo = S.Exp1, expr.args[0] elif expr.args[0].is_Mul: coeff, tail = expr.args[0].as_coeff_terms() if coeff.is_Rational: sexpr, rat_expo = C.exp(C.Mul(*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 """ pattern = make_list(pattern, Mul) 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 ] elems, common_expo, has_deriv = [], None, False for elem, e_rat, e_sym, e_ord in pattern: if e_ord is not None: # there is derivative in the pattern so # there will by small performance penalty has_deriv = True for j in range(len(terms)): term, t_rat, t_sym, t_ord = terms[j] if elem.is_Number: # a constant is a match for everything break if elem == term and e_sym == t_sym: if exact == False: # we don't have to 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 # teher is no chance for 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 very 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]) del terms[j] break else: # pattern element not found return None return terms, elems, common_expo, has_deriv if evaluate: if expr.is_Mul: ret = 1 for term in expr.args: ret *= collect(term, syms, True, exact) return ret elif expr.is_Pow: b = collect(expr.base, syms, True, exact) return C.Pow(b, expr.exp) summa = [ separate(i) for i in make_list(sympify(expr), Add) ] if isinstance(syms, list): syms = [ separate(s) for s in syms ] else: syms = [ separate(syms) ] collected, disliked = {}, S.Zero for product in summa: terms = [ parse_term(i) for i in make_list(product, Mul) ] for symbol in syms: if SYMPY_DEBUG: print "DEBUG: parsing of expression %s with symbol %s " % (str(terms), str(symbol)) 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: index = 1 for elem in elems: index *= Pow(elem[0], elem[1]) if elem[2] is not None: index **= elem[2] else: index = make_expression(elems) terms = separate(make_expression(terms)) index = separate(index) if index in collected.keys(): collected[index] += terms else: collected[index] = terms break else: # none of the patterns matched disliked += product if disliked is not S.Zero: collected[S.One] = disliked if evaluate: return Add(*[ a*b for a, b in collected.iteritems() ]) else: return collected
def _together(expr): from sympy.core.function import Function if expr.is_Add: items, coeffs, basis = [], [], {} for elem in expr.args: numer, q = fraction(_together(elem)) denom = {} for term in make_list(q.expand(), Mul): expo = S.One coeff = S.One if term.is_Pow: if term.exp.is_Rational: term, expo = term.base, term.exp elif term.exp.is_Mul: coeff, tail = term.exp.as_coeff_terms() if coeff.is_Rational: tail = C.Mul(*tail) term, expo = Pow(term.base, tail), coeff coeff = S.One elif term.func is C.exp: if term.args[0].is_Rational: term, expo = S.Exp1, term.args[0] elif term.args[0].is_Mul: coeff, tail = term.args[0].as_coeff_terms() if coeff.is_Rational: tail = C.Mul(*tail) term, expo = C.exp(tail), coeff coeff = S.One elif term.is_Rational: coeff = Integer(term.q) term = Integer(term.p) if term in denom: denom[term] += expo else: denom[term] = expo if term in basis: total, maxi = basis[term] n_total = total + expo n_maxi = max(maxi, expo) basis[term] = (n_total, n_maxi) else: basis[term] = (expo, expo) coeffs.append(coeff) items.append((numer, denom)) numerator, denominator = [], [] for (term, (total, maxi)) in basis.iteritems(): basis[term] = (total, total-maxi) if term.func is C.exp: denominator.append(C.exp(maxi*term.args[0])) else: if maxi is S.One: denominator.append(term) else: denominator.append(Pow(term, maxi)) if all([ c.is_integer for c in coeffs ]): gcds = lambda x, y: igcd(int(x), int(y)) common = Rational(reduce(gcds, coeffs)) else: common = S.One product = reduce(lambda x, y: x*y, coeffs) / common for ((numer, denom), coeff) in zip(items, coeffs): expr, coeff = [], product / (coeff*common) for term in basis.iterkeys(): total, sub = basis[term] if term in denom: expo = total-denom[term]-sub else: expo = total-sub if term.func is C.exp: expr.append(C.exp(expo*term.args[0])) else: if expo is S.One: expr.append(term) else: expr.append(Pow(term, expo)) numerator.append(coeff*Mul(*([numer] + expr))) return Add(*numerator)/(product*Mul(*denominator)) elif expr.is_Mul or expr.is_Pow: return type(expr)(*[ _together(t) for t in expr.args ]) elif expr.is_Function and deep: return expr.func(*[ _together(t) for t in expr.args ]) else: return expr