Ejemplo n.º 1
0
    def _matches(self, expr, repl_dict={}):
        # weed out negative one prefixes
        sign = 1
        a, b = self.as_two_terms()
        if a is S.NegativeOne:
            if b.is_Mul:
                sign = -sign
            else:
                # the remainder, b, is not a Mul anymore
                return b.matches(-expr, repl_dict)
        expr = sympify(expr)
        if expr.is_Mul and expr.args[0] is S.NegativeOne:
            expr = -expr
            sign = -sign

        if not expr.is_Mul:
            # expr can only match if it matches b and a matches +/- 1
            if len(self.args) == 2:
                # quickly test for equality
                if b == expr:
                    return a.matches(Rational(sign), repl_dict)
                # do more expensive match
                dd = b.matches(expr, repl_dict)
                if dd is None:
                    return None
                dd = a.matches(Rational(sign), dd)
                return dd
            return None

        d = repl_dict.copy()

        # weed out identical terms
        pp = list(self.args)
        ee = list(expr.args)
        for p in self.args:
            if p in expr.args:
                ee.remove(p)
                pp.remove(p)

        # only one symbol left in pattern -> match the remaining expression
        if len(pp) == 1 and isinstance(pp[0], C.Wild):
            if len(ee) == 1:
                d[pp[0]] = sign * ee[0]
            else:
                d[pp[0]] = sign * expr.func(*ee)
            return d

        if len(ee) != len(pp):
            return None

        for p, e in zip(pp, ee):
            d = p.xreplace(d).matches(e, d)
            if d is None:
                return None
        return d
Ejemplo n.º 2
0
    def test_rational_is_integer(self):
        self.assertTrue(Rational.is_integer(F(-1, 1)))
        self.assertTrue(Rational.is_integer(F(0, 1)))
        self.assertTrue(Rational.is_integer(F(1, 1)))
        self.assertTrue(Rational.is_integer(F(42, 1)))
        self.assertTrue(Rational.is_integer(F(2, 2)))
        self.assertTrue(Rational.is_integer(F(8, 4)))

        self.assertFalse(Rational.is_integer(F(1, 2)))
        self.assertFalse(Rational.is_integer(F(1, 3)))
        self.assertFalse(Rational.is_integer(F(2, 3)))
Ejemplo n.º 3
0
 def _eval_nseries(self, x, x0, n):
     assert len(self.args) == 1
     arg = self.args[0]
     arg0 = arg.limit(x, 0)
     from sympy import oo
     if arg0 in [-oo, oo]:
         raise PoleError("Cannot expand around %s" % (arg))
     if arg0 is not S.Zero:
         e = self.func(arg)
         e1 = e.expand()
         if e == e1:
             #for example when e = sin(x+1) or e = sin(cos(x))
             #let's try the general algorithm
             term = e.subs(x, S.Zero)
             series = term
             fact = S.One
             for i in range(n - 1):
                 i += 1
                 fact *= Rational(i)
                 e = e.diff(x)
                 term = e.subs(x, S.Zero) * (x**i) / fact
                 term = term.expand()
                 series += term
             return series + C.Order(x**n, x)
         return self.nseries(x, x0, n)
     l = []
     g = None
     for i in xrange(n + 2):
         g = self.taylor_term(i, arg, g)
         g = g.nseries(x, x0, n)
         l.append(g)
     return Add(*l) + C.Order(x**n, x)
Ejemplo n.º 4
0
    def _eval_nseries(self, x, n):
        """
        This function does compute series for multivariate functions,
        but the expansion is always in terms of *one* variable.
        Examples:

        >>> from sympy import atan2, O
        >>> from sympy.abc import x, y
        >>> atan2(x, y).series(x, n=2)
        atan2(0, y) + x/y + O(x**2)
        >>> atan2(x, y).series(y, n=2)
        atan2(x, 0) - y/x + O(y**2)
        """
        if self.func.nargs is None:
            raise NotImplementedError('series for user-defined \
functions are not supported.')
        args = self.args
        args0 = [t.limit(x, 0) for t in args]
        if any([t is S.NaN or t.is_bounded is False for t in args0]):
            raise PoleError("Cannot expand %s around 0" % (args))
        if (self.func.nargs == 1 and args0[0]) or self.func.nargs > 1:
            e = self
            e1 = e.expand()
            if e == e1:
                #for example when e = sin(x+1) or e = sin(cos(x))
                #let's try the general algorithm
                term = e.subs(x, S.Zero)
                if term.is_bounded is False or term is S.NaN:
                    raise PoleError("Cannot expand %s around 0" % (self))
                series = term
                fact = S.One
                for i in range(n - 1):
                    i += 1
                    fact *= Rational(i)
                    e = e.diff(x)
                    subs = e.subs(x, S.Zero)
                    if subs is S.NaN:
                        # try to evaluate a limit if we have to
                        subs = e.limit(x, S.Zero)
                    if subs.is_bounded is False:
                        raise PoleError("Cannot expand %s around 0" % (self))
                    term = subs * (x**i) / fact
                    term = term.expand()
                    series += term
                return series + C.Order(x**n, x)
            return e1.nseries(x, n=n)
        arg = self.args[0]
        l = []
        g = None
        for i in xrange(n + 2):
            g = self.taylor_term(i, arg, g)
            g = g.nseries(x, n=n)
            l.append(g)
        return Add(*l) + C.Order(x**n, x)
Ejemplo n.º 5
0
    def _eval_nseries(self, x, n):
        if self.func.nargs != 1:
            raise NotImplementedError('series for user-defined and \
multi-arg functions are not supported.')
        arg = self.args[0]
        arg0 = arg.limit(x, 0)
        from sympy import oo
        if arg0 == S.NaN or arg0.is_bounded == False:
            raise PoleError("Cannot expand %s around 0" % (arg))
        if arg0:
            e = self
            e1 = e.expand()
            if e == e1:
                #for example when e = sin(x+1) or e = sin(cos(x))
                #let's try the general algorithm
                term = e.subs(x, S.Zero)
                if arg0 == S.NaN or term.is_bounded == False:
                    raise PoleError("Cannot expand %s around 0" % (self))
                series = term
                fact = S.One
                for i in range(n - 1):
                    i += 1
                    fact *= Rational(i)
                    e = e.diff(x)
                    subs = e.subs(x, S.Zero)
                    if subs == S.NaN:
                        # try to evaluate a limit if we have to
                        subs = e.limit(x, S.Zero)
                        if subs.is_bounded == False:
                            raise PoleError("Cannot expand %s around 0" %
                                            (self))
                    term = subs * (x**i) / fact
                    term = term.expand()
                    series += term
                return series + C.Order(x**n, x)
            return e1.nseries(x, n=n)
        l = []
        g = None
        for i in xrange(n + 2):
            g = self.taylor_term(i, arg, g)
            g = g.nseries(x, n=n)
            l.append(g)
        return Add(*l) + C.Order(x**n, x)
Ejemplo n.º 6
0
    def _matches(self, expr, repl_dict={}, evaluate=False):
        if evaluate:
            return self.subs(repl_dict).matches(expr, repl_dict)

        # weed out negative one prefixes
        sign = 1
        a, b = self.as_two_terms()
        if a is S.NegativeOne:
            if b.is_Mul:
                sign = -sign
            else:
                # the remainder, b, is not a Mul anymore
                return b.matches(-expr, repl_dict, evaluate)
        expr = sympify(expr)
        if expr.is_Mul and expr.args[0] is S.NegativeOne:
            expr = -expr; sign = -sign

        if not expr.is_Mul:
            # expr can only match if it matches b and a matches +/- 1
            if len(self.args) == 2:
                # quickly test for equality
                if b == expr:
                    return a.matches(Rational(sign), repl_dict, evaluate)
                # do more expensive match
                dd = b.matches(expr, repl_dict, evaluate)
                if dd == None:
                    return None
                dd = a.matches(Rational(sign), dd, evaluate)
                return dd
            return None

        d = repl_dict.copy()

        # weed out identical terms
        pp = list(self.args)
        ee = list(expr.args)
        for p in self.args:
            if p in expr.args:
                ee.remove(p)
                pp.remove(p)

        # only one symbol left in pattern -> match the remaining expression
        if len(pp) == 1 and isinstance(pp[0], C.Wild):
            if len(ee) == 1:
                d[pp[0]] = sign * ee[0]
            else:
                d[pp[0]] = sign * (type(expr)(*ee))
            return d

        if len(ee) != len(pp):
            return None

        i = 0
        for p, e in zip(pp, ee):
            if i == 0 and sign != 1:
                try:
                    e = sign * e
                except TypeError:
                    return None
            d = p.matches(e, d, evaluate=not i)
            i += 1
            if d is None:
                return None
        return d
Ejemplo n.º 7
0
    def flatten(cls, seq):
        """Return commutative, noncommutative and order arguments by
        combining related terms.

        ** Developer Notes **
            * In an expression like ``a*b*c``, python process this through sympy
              as ``Mul(Mul(a, b), c)``. This can have undesirable consequences.

              -  Sometimes terms are not combined as one would like:
                 {c.f. http://code.google.com/p/sympy/issues/detail?id=1497}

                >>> from sympy import Mul, sqrt
                >>> from sympy.abc import x, y, z
                >>> 2*(x + 1) # this is the 2-arg Mul behavior
                2*x + 2
                >>> y*(x + 1)*2
                2*y*(x + 1)
                >>> 2*(x + 1)*y # 2-arg result will be obtained first
                y*(2*x + 2)
                >>> Mul(2, x + 1, y) # all 3 args simultaneously processed
                2*y*(x + 1)
                >>> 2*((x + 1)*y) # parentheses can control this behavior
                2*y*(x + 1)

                Powers with compound bases may not find a single base to
                combine with unless all arguments are processed at once.
                Post-processing may be necessary in such cases.
                {c.f. http://code.google.com/p/sympy/issues/detail?id=2629}

                >>> a = sqrt(x*sqrt(y))
                >>> a**3
                (x*sqrt(y))**(3/2)
                >>> Mul(a,a,a)
                (x*sqrt(y))**(3/2)
                >>> a*a*a
                x*sqrt(y)*sqrt(x*sqrt(y))
                >>> _.subs(a.base, z).subs(z, a.base)
                (x*sqrt(y))**(3/2)

              -  If more than two terms are being multiplied then all the
                 previous terms will be re-processed for each new argument.
                 So if each of ``a``, ``b`` and ``c`` were :class:`Mul`
                 expression, then ``a*b*c`` (or building up the product
                 with ``*=``) will  process all the arguments of ``a`` and
                 ``b`` twice: once when ``a*b`` is computed and again when
                 ``c`` is multiplied.

                 Using ``Mul(a, b, c)`` will process all arguments once.

            * The results of Mul are cached according to arguments, so flatten
              will only be called once for ``Mul(a, b, c)``. If you can
              structure a calculation so the arguments are most likely to be
              repeats then this can save time in computing the answer. For
              example, say you had a Mul, M, that you wished to divide by ``d[i]``
              and multiply by ``n[i]`` and you suspect there are many repeats
              in ``n``. It would be better to compute ``M*n[i]/d[i]`` rather
              than ``M/d[i]*n[i]`` since every time n[i] is a repeat, the
              product, ``M*n[i]`` will be returned without flattening -- the
              cached value will be returned. If you divide by the ``d[i]``
              first (and those are more unique than the ``n[i]``) then that will
              create a new Mul, ``M/d[i]`` the args of which will be traversed
              again when it is multiplied by ``n[i]``.

              {c.f. http://code.google.com/p/sympy/issues/detail?id=2607}

              This consideration is moot if the cache is turned off.

            NB
              The validity of the above notes depends on the implementation
              details of Mul and flatten which may change at any time. Therefore,
              you should only consider them when your code is highly performance
              sensitive.

        """
        # apply associativity, separate commutative part of seq
        c_part = []         # out: commutative factors
        nc_part = []        # out: non-commutative factors

        nc_seq = []

        coeff = S.One       # standalone term
                            # e.g. 3 * ...

        c_powers = []       # (base,exp)      n
                            # e.g. (x,n) for x

        num_exp = []        # (num-base, exp)           y
                            # e.g.  (3, y)  for  ... * 3  * ...

        neg1e = 0           # exponent on -1 extracted from Number-based Pow

        pnum_rat = {}       # (num-base, Rat-exp)          1/2
                            # e.g.  (3, 1/2)  for  ... * 3     * ...

        order_symbols = None



        # --- PART 1 ---
        #
        # "collect powers and coeff":
        #
        # o coeff
        # o c_powers
        # o num_exp
        # o neg1e
        # o pnum_rat
        #
        # NOTE: this is optimized for all-objects-are-commutative case

        for o in seq:
            # O(x)
            if o.is_Order:
                o, order_symbols = o.as_expr_variables(order_symbols)

            # Mul([...])
            if o.is_Mul:
                if o.is_commutative:
                    seq.extend(o.args)    # XXX zerocopy?

                else:
                    # NCMul can have commutative parts as well
                    for q in o.args:
                        if q.is_commutative:
                            seq.append(q)
                        else:
                            nc_seq.append(q)

                    # append non-commutative marker, so we don't forget to
                    # process scheduled non-commutative objects
                    seq.append(NC_Marker)

                continue

            # 3
            elif o.is_Number:
                if o is S.NaN or coeff is S.ComplexInfinity and o is S.Zero:
                    # we know for sure the result will be nan
                    return [S.NaN], [], None
                elif coeff.is_Number: # it could be zoo
                    coeff *= o
                    if coeff is S.NaN:
                        # we know for sure the result will be nan
                        return [S.NaN], [], None
                continue

            elif o is S.ComplexInfinity:
                if not coeff or coeff is S.ComplexInfinity:
                    # we know for sure the result will be nan
                    return [S.NaN], [], None
                coeff = S.ComplexInfinity
                continue

            elif o.is_commutative:
                #      e
                # o = b
                b, e = o.as_base_exp()

                #  y
                # 3
                if o.is_Pow and b.is_Number:

                    # get all the factors with numeric base so they can be
                    # combined below, but don't combine negatives unless
                    # the exponent is an integer
                    if e.is_Rational:
                        if e.is_Integer:
                            coeff *= Pow(b, e) # it is an unevaluated power
                            continue
                        elif e.is_negative:    # also a sign of an unevaluated power
                            seq.append(Pow(b, e))
                            continue
                        elif b.is_negative:
                            neg1e += e
                            b = -b
                        if b is not S.One:
                            pnum_rat.setdefault(b, []).append(e)
                        continue
                    elif b.is_positive or e.is_integer:
                        num_exp.append((b, e))
                        continue
                c_powers.append((b,e))

            # NON-COMMUTATIVE
            # TODO: Make non-commutative exponents not combine automatically
            else:
                if o is not NC_Marker:
                    nc_seq.append(o)

                # process nc_seq (if any)
                while nc_seq:
                    o = nc_seq.pop(0)
                    if not nc_part:
                        nc_part.append(o)
                        continue

                    #                             b    c       b+c
                    # try to combine last terms: a  * a   ->  a
                    o1 = nc_part.pop()
                    b1,e1 = o1.as_base_exp()
                    b2,e2 = o.as_base_exp()
                    new_exp = e1 + e2
                    # Only allow powers to combine if the new exponent is
                    # not an Add. This allow things like a**2*b**3 == a**5
                    # if a.is_commutative == False, but prohibits
                    # a**x*a**y and x**a*x**b from combining (x,y commute).
                    if b1==b2 and (not new_exp.is_Add):
                        o12 = b1 ** new_exp

                        # now o12 could be a commutative object
                        if o12.is_commutative:
                            seq.append(o12)
                            continue
                        else:
                            nc_seq.insert(0, o12)

                    else:
                        nc_part.append(o1)
                        nc_part.append(o)

        # We do want a combined exponent if it would not be an Add, such as
        #  y    2y     3y
        # x  * x   -> x
        # We determine this if two exponents have the same term in as_coeff_mul
        #
        # Unfortunately, this isn't smart enough to consider combining into
        # exponents that might already be adds, so things like:
        #  z - y    y
        # x      * x  will be left alone.  This is because checking every possible
        # combination can slow things down.

        # gather exponents of common bases...
        # in c_powers
        new_c_powers = []
        common_b = {} # b:e
        for b, e in c_powers:
            co = e.as_coeff_mul()
            common_b.setdefault(b, {}).setdefault(co[1], []).append(co[0])
        for b, d in common_b.items():
            for di, li in d.items():
                d[di] = Add(*li)
        for b, e in common_b.items():
            for t, c in e.items():
                new_c_powers.append((b, c*Mul(*t)))
        c_powers = new_c_powers

        # and in num_exp
        new_num_exp = []
        common_b = {} # b:e
        for b, e in num_exp:
            co = e.as_coeff_mul()
            common_b.setdefault(b, {}).setdefault(co[1], []).append(co[0])
        for b, d in common_b.items():
            for di, li in d.items():
                d[di] = Add(*li)
        for b, e in common_b.items():
            for t, c in e.items():
                new_num_exp.append((b,c*Mul(*t)))
        num_exp = new_num_exp

        # --- PART 2 ---
        #
        # o process collected powers  (x**0 -> 1; x**1 -> x; otherwise Pow)
        # o combine collected powers  (2**x * 3**x -> 6**x)
        #   with numeric base

        # ................................
        # now we have:
        # - coeff:
        # - c_powers:    (b, e)
        # - num_exp:     (2, e)
        # - pnum_rat:    {(1/3, [1/3, 2/3, 1/4])}

        #  0             1
        # x  -> 1       x  -> x
        for b, e in c_powers:
            if e is S.One:
                if b.is_Number:
                    coeff *= b
                else:
                    c_part.append(b)
            elif not e is S.Zero:
                c_part.append(Pow(b, e))

        #  x    x     x
        # 2  * 3  -> 6
        inv_exp_dict = {}   # exp:Mul(num-bases)     x    x
                            # e.g.  x:6  for  ... * 2  * 3  * ...
        for b, e in num_exp:
            inv_exp_dict.setdefault(e, []).append(b)
        for e, b in inv_exp_dict.items():
            inv_exp_dict[e] = Mul(*b)
        c_part.extend([Pow(b, e) for e, b in inv_exp_dict.iteritems() if e])

        # b, e -> e, b
        # {(1/5, [1/3]), (1/2, [1/12, 1/4]} -> {(1/3, [1/5, 1/2])}
        comb_e = {}
        for b, e in pnum_rat.iteritems():
            comb_e.setdefault(Add(*e), []).append(b)
        del pnum_rat
        # process them, reducing exponents to values less than 1
        # and updating coeff if necessary else adding them to
        # num_rat for further processing
        num_rat = []
        for e, b in comb_e.iteritems():
            b = Mul(*b)
            if e.q == 1:
                coeff *= Pow(b, e)
                continue
            if e.p > e.q:
                e_i, ep = divmod(e.p, e.q)
                coeff *= Pow(b, e_i)
                e = Rational(ep, e.q)
            num_rat.append((b, e))
        del comb_e

        # extract gcd of bases in num_rat
        # 2**(1/3)*6**(1/4) -> 2**(1/3+1/4)*3**(1/4)
        pnew = {}
        i = 0 # steps through num_rat which may grow
        while i < len(num_rat):
            bi, ei = num_rat[i]
            grow = []
            for j in range(i + 1, len(num_rat)):
                bj, ej = num_rat[j]
                g = igcd(bi, bj)
                if g != 1:
                    # 4**r1*6**r2 -> 2**(r1+r2)  *  2**r1 *  3**r2
                    # this might have a gcd with something else
                    e = ei + ej
                    if e.q == 1:
                        coeff *= Pow(g, e)
                    else:
                        if e.p > e.q:
                            e_i, ep = divmod(e.p, e.q) # change e in place
                            coeff *= Pow(g, e_i)
                            e = Rational(ep, e.q)
                        grow.append((g, e))
                    # update the jth item
                    num_rat[j] = (bj//g, ej)
                    # update bi that we are checking with
                    bi = bi//g
                    if bi is S.One:
                        break
            if bi is not S.One:
                obj = Pow(bi, ei)
                if obj.is_Number:
                    coeff *= obj
                else:
                    if obj.is_Mul: # sqrt(12) -> 2*sqrt(3)
                        c, obj = obj.args # expecting only 2 args
                        coeff *= c
                        assert obj.is_Pow
                        bi, ei = obj.args
                    pnew.setdefault(ei, []).append(bi)

            num_rat.extend(grow)
            i += 1

        # combine bases of the new powers
        for e, b in pnew.iteritems():
            pnew[e] = Mul(*b)

        # see if there is a base with matching coefficient
        # that the -1 can be joined with
        if neg1e:
            p = Pow(S.NegativeOne, neg1e)
            if p.is_Number:
                coeff *= p
            else:
                if p.is_Mul:
                    c, p = p.args
                    coeff *= c
                    assert p.is_Pow and p.base is S.NegativeOne
                    neg1e = p.args[1]
                for e, b in pnew.iteritems():
                    if e == neg1e and b.is_positive:
                        pnew[e] = -b
                        break
                else:
                    c_part.append(p)

        # add all the pnew powers
        c_part.extend([Pow(b, e) for e, b in pnew.iteritems()])

        # oo, -oo
        if (coeff is S.Infinity) or (coeff is S.NegativeInfinity):
            new_c_part = []
            coeff_sign = 1
            for t in c_part:
                if t.is_positive:
                    continue
                if t.is_negative:
                    coeff_sign *= -1
                    continue
                new_c_part.append(t)
            c_part = new_c_part
            new_nc_part = []
            for t in nc_part:
                if t.is_positive:
                    continue
                if t.is_negative:
                    coeff_sign *= -1
                    continue
                new_nc_part.append(t)
            nc_part = new_nc_part
            coeff *= coeff_sign

        # zoo
        if coeff is S.ComplexInfinity:
            # zoo might be
            #   unbounded_real + bounded_im
            #   bounded_real + unbounded_im
            #   unbounded_real + unbounded_im
            # and non-zero real or imaginary will not change that status.
            c_part = [c for c in c_part if not (c.is_nonzero and
                                                c.is_real is not None)]
            nc_part = [c for c in nc_part if not (c.is_nonzero and
                                                  c.is_real is not None)]

        # 0
        elif coeff is S.Zero:
            # we know for sure the result will be 0
            return [coeff], [], order_symbols

        # order commutative part canonically
        c_part.sort(key=cmp_to_key(Basic.compare))

        # current code expects coeff to be always in slot-0
        if coeff is not S.One:
            c_part.insert(0, coeff)


        # we are done
        if len(c_part)==2 and c_part[0].is_Number and c_part[1].is_Add:
            # 2*(1+a) -> 2 + 2 * a
            coeff = c_part[0]
            c_part = [Add(*[coeff*f for f in c_part[1].args])]

        return c_part, nc_part, order_symbols
Ejemplo n.º 8
0
def _rgcd(a, b):
    return Rational(Integer(igcd(a.p, b.p)), Integer(ilcm(a.q, b.q)))
Ejemplo n.º 9
0
    def _eval_nseries(self, x, n, logx):
        """
        This function does compute series for multivariate functions,
        but the expansion is always in terms of *one* variable.
        Examples:

        >>> from sympy import atan2, O
        >>> from sympy.abc import x, y
        >>> atan2(x, y).series(x, n=2)
        atan2(0, y) + x/y + O(x**2)
        >>> atan2(x, y).series(y, n=2)
        atan2(x, 0) - y/x + O(y**2)

        This function also computes asymptotic expansions, if necessary
        and possible:

        >>> from sympy import loggamma
        >>> loggamma(1/x)._eval_nseries(x,0,None)
        log(x)/2 - log(x)/x - 1/x + O(1)
        """
        if self.func.nargs is None:
            raise NotImplementedError('series for user-defined \
functions are not supported.')
        args = self.args
        args0 = [t.limit(x, 0) for t in args]
        if any([t.is_bounded == False for t in args0]):
            from sympy import Dummy, oo, zoo, nan
            a = [t.compute_leading_term(x, logx=logx) for t in args]
            a0 = [t.limit(x, 0) for t in a]
            if any([t.has(oo, -oo, zoo, nan) for t in a0]):
                return self._eval_aseries(n, args0, x,
                                          logx)._eval_nseries(x, n, logx)
            # Careful: the argument goes to oo, but only logarithmically so. We
            # are supposed to do a power series expansion "around the
            # logarithmic term". e.g.
            #      f(1+x+log(x))
            #     -> f(1+logx) + x*f'(1+logx) + O(x**2)
            # where 'logx' is given in the argument
            a = [t._eval_nseries(x, n, logx) for t in args]
            z = [r - r0 for (r, r0) in zip(a, a0)]
            p = [Dummy() for t in z]
            q = []
            v = None
            w = None
            for ai, zi, pi in zip(a0, z, p):
                if zi.has(x):
                    if v is not None: raise NotImplementedError
                    q.append(ai + pi)
                    v = pi
                    w = zi
                else:
                    q.append(ai)
            e1 = self.func(*q)
            if v is None:
                return e1
            s = e1._eval_nseries(v, n, logx)
            o = s.getO()
            s = s.removeO()
            s = s.subs(v, zi).expand() + C.Order(o.expr.subs(v, zi), x)
            return s
        if (self.func.nargs == 1 and args0[0]) or self.func.nargs > 1:
            e = self
            e1 = e.expand()
            if e == e1:
                #for example when e = sin(x+1) or e = sin(cos(x))
                #let's try the general algorithm
                term = e.subs(x, S.Zero)
                if term.is_bounded is False or term is S.NaN:
                    raise PoleError("Cannot expand %s around 0" % (self))
                series = term
                fact = S.One
                for i in range(n - 1):
                    i += 1
                    fact *= Rational(i)
                    e = e.diff(x)
                    subs = e.subs(x, S.Zero)
                    if subs is S.NaN:
                        # try to evaluate a limit if we have to
                        subs = e.limit(x, S.Zero)
                    if subs.is_bounded is False:
                        raise PoleError("Cannot expand %s around 0" % (self))
                    term = subs * (x**i) / fact
                    term = term.expand()
                    series += term
                return series + C.Order(x**n, x)
            return e1.nseries(x, n=n, logx=logx)
        arg = self.args[0]
        l = []
        g = None
        for i in xrange(n + 2):
            g = self.taylor_term(i, arg, g)
            g = g.nseries(x, n=n, logx=logx)
            l.append(g)
        return Add(*l) + C.Order(x**n, x)
Ejemplo n.º 10
0
    def flatten(cls, seq):

        # apply associativity, separate commutative part of seq
        c_part = []  # out: commutative factors
        nc_part = []  # out: non-commutative factors

        nc_seq = []

        coeff = S.One  # standalone term
        # e.g. 3 * ...

        c_powers = []  # (base,exp)      n
        # e.g. (x,n) for x

        num_exp = []  # (num-base, exp)           y
        # e.g.  (3, y)  for  ... * 3  * ...

        neg1e = 0  # exponent on -1 extracted from Number-based Pow

        pnum_rat = {}  # (num-base, Rat-exp)          1/2
        # e.g.  (3, 1/2)  for  ... * 3     * ...

        order_symbols = None

        # --- PART 1 ---
        #
        # "collect powers and coeff":
        #
        # o coeff
        # o c_powers
        # o num_exp
        # o neg1e
        # o pnum_rat
        #
        # NOTE: this is optimized for all-objects-are-commutative case

        for o in seq:
            # O(x)
            if o.is_Order:
                o, order_symbols = o.as_expr_variables(order_symbols)

            # Mul([...])
            if o.is_Mul:
                if o.is_commutative:
                    seq.extend(o.args)  # XXX zerocopy?

                else:
                    # NCMul can have commutative parts as well
                    for q in o.args:
                        if q.is_commutative:
                            seq.append(q)
                        else:
                            nc_seq.append(q)

                    # append non-commutative marker, so we don't forget to
                    # process scheduled non-commutative objects
                    seq.append(NC_Marker)

                continue

            # 3
            elif o.is_Number:
                if o is S.NaN or coeff is S.ComplexInfinity and o is S.Zero:
                    # we know for sure the result will be nan
                    return [S.NaN], [], None
                elif coeff.is_Number:  # it could be zoo
                    coeff *= o
                    if coeff is S.NaN:
                        # we know for sure the result will be nan
                        return [S.NaN], [], None
                continue

            elif o is S.ComplexInfinity:
                if not coeff or coeff is S.ComplexInfinity:
                    # we know for sure the result will be nan
                    return [S.NaN], [], None
                coeff = S.ComplexInfinity
                continue

            elif o.is_commutative:
                #      e
                # o = b
                b, e = o.as_base_exp()

                #  y
                # 3
                if o.is_Pow and b.is_Number:

                    # get all the factors with numeric base so they can be
                    # combined below, but don't combine negatives unless
                    # the exponent is an integer
                    if e.is_Rational:
                        if e.is_Integer:
                            coeff *= Pow(b, e)  # it is an unevaluated power
                            continue
                        elif e.is_negative:  # also a sign of an unevaluated power
                            seq.append(Pow(b, e))
                            continue
                        elif b.is_negative:
                            neg1e += e
                            b = -b
                        if b is not S.One:
                            pnum_rat.setdefault(b, []).append(e)
                        continue
                    elif b.is_positive or e.is_integer:
                        num_exp.append((b, e))
                        continue
                c_powers.append((b, e))

            # NON-COMMUTATIVE
            # TODO: Make non-commutative exponents not combine automatically
            else:
                if o is not NC_Marker:
                    nc_seq.append(o)

                # process nc_seq (if any)
                while nc_seq:
                    o = nc_seq.pop(0)
                    if not nc_part:
                        nc_part.append(o)
                        continue

                    #                             b    c       b+c
                    # try to combine last terms: a  * a   ->  a
                    o1 = nc_part.pop()
                    b1, e1 = o1.as_base_exp()
                    b2, e2 = o.as_base_exp()
                    new_exp = e1 + e2
                    # Only allow powers to combine if the new exponent is
                    # not an Add. This allow things like a**2*b**3 == a**5
                    # if a.is_commutative == False, but prohibits
                    # a**x*a**y and x**a*x**b from combining (x,y commute).
                    if b1 == b2 and (not new_exp.is_Add):
                        o12 = b1**new_exp

                        # now o12 could be a commutative object
                        if o12.is_commutative:
                            seq.append(o12)
                            continue
                        else:
                            nc_seq.insert(0, o12)

                    else:
                        nc_part.append(o1)
                        nc_part.append(o)

        # We do want a combined exponent if it would not be an Add, such as
        #  y    2y     3y
        # x  * x   -> x
        # We determine this if two exponents have the same term in as_coeff_mul
        #
        # Unfortunately, this isn't smart enough to consider combining into
        # exponents that might already be adds, so things like:
        #  z - y    y
        # x      * x  will be left alone.  This is because checking every possible
        # combination can slow things down.

        # gather exponents of common bases...
        # in c_powers
        new_c_powers = []
        common_b = {}  # b:e
        for b, e in c_powers:
            co = e.as_coeff_mul()
            common_b.setdefault(b, {}).setdefault(co[1], []).append(co[0])
        for b, d in common_b.items():
            for di, li in d.items():
                d[di] = Add(*li)
        for b, e in common_b.items():
            for t, c in e.items():
                new_c_powers.append((b, c * Mul(*t)))
        c_powers = new_c_powers

        # and in num_exp
        new_num_exp = []
        common_b = {}  # b:e
        for b, e in num_exp:
            co = e.as_coeff_mul()
            common_b.setdefault(b, {}).setdefault(co[1], []).append(co[0])
        for b, d in common_b.items():
            for di, li in d.items():
                d[di] = Add(*li)
        for b, e in common_b.items():
            for t, c in e.items():
                new_num_exp.append((b, c * Mul(*t)))
        num_exp = new_num_exp

        # --- PART 2 ---
        #
        # o process collected powers  (x**0 -> 1; x**1 -> x; otherwise Pow)
        # o combine collected powers  (2**x * 3**x -> 6**x)
        #   with numeric base

        # ................................
        # now we have:
        # - coeff:
        # - c_powers:    (b, e)
        # - num_exp:     (2, e)
        # - pnum_rat:    {(1/3, [1/3, 2/3, 1/4])}

        #  0             1
        # x  -> 1       x  -> x
        for b, e in c_powers:
            if e is S.One:
                if b.is_Number:
                    coeff *= b
                else:
                    c_part.append(b)
            elif not e is S.Zero:
                c_part.append(Pow(b, e))

        #  x    x     x
        # 2  * 3  -> 6
        inv_exp_dict = {}  # exp:Mul(num-bases)     x    x
        # e.g.  x:6  for  ... * 2  * 3  * ...
        for b, e in num_exp:
            inv_exp_dict.setdefault(e, []).append(b)
        for e, b in inv_exp_dict.items():
            inv_exp_dict[e] = Mul(*b)
        c_part.extend([Pow(b, e) for e, b in inv_exp_dict.iteritems() if e])

        # b, e -> e, b
        # {(1/5, [1/3]), (1/2, [1/12, 1/4]} -> {(1/3, [1/5, 1/2])}
        comb_e = {}
        for b, e in pnum_rat.iteritems():
            comb_e.setdefault(Add(*e), []).append(b)
        del pnum_rat
        # process them, reducing exponents to values less than 1
        # and updating coeff if necessary else adding them to
        # num_rat for further processing
        num_rat = []
        for e, b in comb_e.iteritems():
            b = Mul(*b)
            if e.q == 1:
                coeff *= Pow(b, e)
                continue
            if e.p > e.q:
                e_i, ep = divmod(e.p, e.q)
                coeff *= Pow(b, e_i)
                e = Rational(ep, e.q)
            num_rat.append((b, e))
        del comb_e

        # extract gcd of bases in num_rat
        # 2**(1/3)*6**(1/4) -> 2**(1/3+1/4)*3**(1/4)
        pnew = {}
        i = 0  # steps through num_rat which may grow
        while i < len(num_rat):
            bi, ei = num_rat[i]
            grow = []
            for j in range(i + 1, len(num_rat)):
                bj, ej = num_rat[j]
                g = igcd(bi, bj)
                if g != 1:
                    # 4**r1*6**r2 -> 2**(r1+r2)  *  2**r1 *  3**r2
                    # this might have a gcd with something else
                    e = ei + ej
                    if e.q == 1:
                        coeff *= Pow(g, e)
                    else:
                        if e.p > e.q:
                            e_i, ep = divmod(e.p, e.q)  # change e in place
                            coeff *= Pow(g, e_i)
                            e = Rational(ep, e.q)
                        grow.append((g, e))
                    # update the jth item
                    num_rat[j] = (bj // g, ej)
                    # update bi that we are checking with
                    bi = bi // g
                    if bi is S.One:
                        break
            if bi is not S.One:
                obj = Pow(bi, ei)
                if obj.is_Number:
                    coeff *= obj
                else:
                    if obj.is_Mul:  # 12**(1/2) -> 2*sqrt(3)
                        c, obj = obj.args  # expecting only 2 args
                        coeff *= c
                        assert obj.is_Pow
                        bi, ei = obj.args
                    pnew.setdefault(ei, []).append(bi)

            num_rat.extend(grow)
            i += 1

        # combine bases of the new powers
        for e, b in pnew.iteritems():
            pnew[e] = Mul(*b)

        # see if there is a base with matching coefficient
        # that the -1 can be joined with
        if neg1e:
            p = Pow(S.NegativeOne, neg1e)
            if p.is_Number:
                coeff *= p
            else:
                if p.is_Mul:
                    c, p = p.args
                    coeff *= c
                    assert p.is_Pow and p.base is S.NegativeOne
                    neg1e = p.args[1]
                for e, b in pnew.iteritems():
                    if e == neg1e and b.is_positive:
                        pnew[e] = -b
                        break
                else:
                    c_part.append(p)

        # add all the pnew powers
        c_part.extend([Pow(b, e) for e, b in pnew.iteritems()])

        # oo, -oo
        if (coeff is S.Infinity) or (coeff is S.NegativeInfinity):
            new_c_part = []
            coeff_sign = 1
            for t in c_part:
                if t.is_positive:
                    continue
                if t.is_negative:
                    coeff_sign *= -1
                    continue
                new_c_part.append(t)
            c_part = new_c_part
            new_nc_part = []
            for t in nc_part:
                if t.is_positive:
                    continue
                if t.is_negative:
                    coeff_sign *= -1
                    continue
                new_nc_part.append(t)
            nc_part = new_nc_part
            coeff *= coeff_sign

        # zoo
        if coeff is S.ComplexInfinity:
            # zoo might be
            #   unbounded_real + bounded_im
            #   bounded_real + unbounded_im
            #   unbounded_real + unbounded_im
            # and non-zero real or imaginary will not change that status.
            c_part = [
                c for c in c_part
                if not (c.is_nonzero and c.is_real is not None)
            ]
            nc_part = [
                c for c in nc_part
                if not (c.is_nonzero and c.is_real is not None)
            ]

        # 0
        elif coeff is S.Zero:
            # we know for sure the result will be 0
            return [coeff], [], order_symbols

        # order commutative part canonically
        c_part.sort(key=cmp_to_key(Basic.compare))

        # current code expects coeff to be always in slot-0
        if coeff is not S.One:
            c_part.insert(0, coeff)

        # we are done
        if len(c_part) == 2 and c_part[0].is_Number and c_part[1].is_Add:
            # 2*(1+a) -> 2 + 2 * a
            coeff = c_part[0]
            c_part = [Add(*[coeff * f for f in c_part[1].args])]

        return c_part, nc_part, order_symbols
Ejemplo n.º 11
0
    def _matches(self, expr, repl_dict={}, evaluate=False):
        # weed out negative one prefixes
        sign = 1
        if self.args[0] == -1:
            self = -self
            sign = -sign
        if expr.is_Mul and expr.args[0] == -1:
            expr = -expr
            sign = -sign

        if evaluate:
            return self.subs(repl_dict).matches(expr, repl_dict)
        expr = sympify(expr)
        if not isinstance(expr, self.__class__):
            # if we can omit the first factor, we can match it to sign * one
            if Mul(*self.args[1:]) == expr:
                return self.args[0].matches(Rational(sign), repl_dict,
                                            evaluate)
            # two-factor product: if the 2nd factor matches, the first part must be sign * one
            if len(self.args[:]) == 2:
                dd = self.args[1].matches(expr, repl_dict, evaluate)
                if dd == None:
                    return None
                dd = self.args[0].matches(Rational(sign), dd, evaluate)
                return dd
            return None

        if len(self.args[:]) == 0:
            if self == expr:
                return repl_dict
            return None
        d = repl_dict.copy()

        # weed out identical terms
        pp = list(self.args)
        ee = list(expr.args)
        for p in self.args:
            if p in expr.args:
                ee.remove(p)
                pp.remove(p)

        # only one symbol left in pattern -> match the remaining expression
        from symbol import Wild
        if len(pp) == 1 and isinstance(pp[0], Wild):
            if len(ee) == 1:
                d[pp[0]] = sign * ee[0]
            else:
                d[pp[0]] = sign * (type(expr)(*ee))
            return d

        if len(ee) != len(pp):
            return None

        i = 0
        for p, e in zip(pp, ee):
            if i == 0 and sign != 1:
                try:
                    e = sign * e
                except TypeError:
                    return None
            d = p.matches(e, d, evaluate=not i)
            i += 1
            if d is None:
                return None
        return d