Ejemplo n.º 1
0
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)
Ejemplo n.º 2
0
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)
Ejemplo n.º 3
0
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)
Ejemplo n.º 4
0
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)
Ejemplo n.º 5
0
    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
Ejemplo n.º 6
0
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]
Ejemplo n.º 7
0
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]
Ejemplo n.º 8
0
    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
Ejemplo n.º 9
0
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
Ejemplo n.º 10
0
    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
Ejemplo n.º 11
0
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)
Ejemplo n.º 12
0
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)
Ejemplo n.º 13
0
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
Ejemplo n.º 14
0
    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
Ejemplo n.º 15
0
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
Ejemplo n.º 16
0
    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