예제 #1
0
    def _eval_nseries(self, x, n):
        from sympy import powsimp, collect, exp, log, O, ceiling

        b, e = self.args
        if e.is_Integer:
            if e > 0:
                # positive integer powers are easy to expand, e.g.:
                # sin(x)**4 = (x-x**3/3+...)**4 = ...
                return Pow(b._eval_nseries(x, n=n),
                           e)._eval_expand_multinomial(deep=False)
            elif e is S.NegativeOne:
                # this is also easy to expand using the formula:
                # 1/(1 + x) = 1 + x + x**2 + x**3 ...
                # so we need to rewrite base to the form "1+x"
                if b.has(log(x)):
                    # we need to handle the log(x) singularity:
                    y = Dummy("y")
                    p = self.subs(log(x), -1 / y)
                    if not p.has(x):
                        p = p._eval_nseries(y, n=n)
                        p = p.subs(y, -1 / log(x))
                        return p

                b = b._eval_nseries(x, n=n)
                if b.has(log(x)):
                    # we need to handle the log(x) singularity:
                    y = Dummy("y")
                    self0 = 1 / b
                    p = self0.subs(log(x), -1 / y)
                    if not p.has(x):
                        p = p._eval_nseries(y, n=n)
                        p = p.subs(y, -1 / log(x))
                        return p
                prefactor = b.as_leading_term(x)
                # express "rest" as: rest = 1 + k*x**l + ... + O(x**n)
                rest = ((b - prefactor) / prefactor)._eval_expand_mul()
                if rest == 0:
                    # if prefactor == w**4 + x**2*w**4 + 2*x*w**4, we need to
                    # factor the w**4 out using collect:
                    return 1 / collect(prefactor, x)
                if rest.is_Order:
                    return (1 + rest) / prefactor
                n2 = rest.getn()
                if n2 is not None:
                    n = n2

                term2 = collect(rest.as_leading_term(x), x)
                k, l = C.Wild("k"), C.Wild("l")
                r = term2.match(k * x**l)
                # if term2 is NaN then r will not contain l
                k = r.get(k, S.One)
                l = r.get(l, S.Zero)
                if l.is_Rational and l > 0:
                    pass
                elif l.is_number and l > 0:
                    l = l.evalf()
                else:
                    raise NotImplementedError()

                terms = [1 / prefactor]
                for m in xrange(1, ceiling(n / l)):
                    new_term = terms[-1] * (-rest)
                    if new_term.is_Pow:
                        new_term = new_term._eval_expand_multinomial(
                            deep=False)
                    else:
                        new_term = new_term._eval_expand_mul(deep=False)
                    terms.append(new_term)
                if n2 is None:
                    # Append O(...) because it is not included in "r"
                    terms.append(O(x**n))
                return powsimp(Add(*terms), deep=True, combine='exp')
            else:
                # negative powers are rewritten to the cases above, for example:
                # sin(x)**(-4) = 1/( sin(x)**4) = ...
                # and expand the denominator:
                denominator = (b**(-e))._eval_nseries(x, n=n)
                if 1 / denominator == self:
                    return self
                # now we have a type 1/f(x), that we know how to expand
                return (1 / denominator)._eval_nseries(x, n=n)

        if e.has(x):
            return exp(e * log(b))._eval_nseries(x, n=n)

        if b == x:
            return powsimp(self, deep=True, combine='exp')

        # work for b(x)**e where e is not an Integer and does not contain x
        # and hopefully has no other symbols

        def e2int(e):
            """return the integer value (if possible) of e and a
            flag indicating whether it is bounded or not."""
            n = e.limit(x, 0)
            unbounded = n.is_unbounded
            if not unbounded:
                # XXX was int or floor intended? int used to behave like floor
                # so int(-Rational(1, 2)) returned -1 rather than int's 0
                try:
                    n = int(n)
                except TypeError:
                    #well, the n is something more complicated (like 1+log(2))
                    try:
                        n = int(n.evalf()) + 1  # XXX why is 1 being added?
                    except TypeError:
                        pass  # hope that base allows this to be resolved
                n = _sympify(n)
                if n.is_Integer:
                    assert n.is_nonnegative
            return n, unbounded

        order = O(x**n, x)
        ei, unbounded = e2int(e)
        b0 = b.limit(x, 0)
        if unbounded and (b0 is S.One or b0.has(Symbol)):
            # XXX what order
            if b0 is S.One:
                resid = (b - 1)
                if resid.is_positive:
                    return S.Infinity
                elif resid.is_negative:
                    return S.Zero
                raise ValueError('cannot determine sign of %s' % resid)

            return b0**ei

        if (b0 is S.Zero or b0.is_unbounded):
            if unbounded is not False:
                return b0**e  # XXX what order

            if not ei.is_number:  # if not, how will we proceed?
                raise ValueError('expecting numerical exponent but got %s' %
                                 ei)

            nuse = n - ei
            lt = b.as_leading_term(x)
            #  XXX o is not used -- was this to be used as o and o2 below to compute a new e?
            o = order * lt**(1 - e)
            bs = b._eval_nseries(x, n=nuse)
            if bs.is_Add:
                bs = bs.removeO()
            if bs.is_Add:
                # bs -> lt + rest -> lt*(1 + (bs/lt - 1))
                return ((Pow(lt, e) * Pow(
                    (bs / lt).expand(), e).nseries(x, n=nuse)).expand() +
                        order)

            return bs**e + order

        # either b0 is bounded but neither 1 nor 0 or e is unbounded
        # b -> b0 + (b-b0) -> b0 * (1 + (b/b0-1))
        o2 = order * (b0**-e)
        z = (b / b0 - 1)
        o = O(z, x)
        #r = self._compute_oseries3(z, o2, self.taylor_term)
        if o is S.Zero or o2 is S.Zero:
            unbounded = True
        else:
            if o.expr.is_number:
                e2 = log(o2.expr * x) / log(x)
            else:
                e2 = log(o2.expr) / log(o.expr)
            n, unbounded = e2int(e2)
        if unbounded:
            # requested accuracy gives infinite series,
            # order is probably nonpolynomial e.g. O(exp(-1/x), x).
            r = 1 + z
        else:
            l = []
            g = None
            for i in xrange(n + 2):
                g = self.taylor_term(i, z, g)
                g = g.nseries(x, n=n)
                l.append(g)
            r = Add(*l)
        return r * b0**e + order
예제 #2
0
def do_integral(expr, prec, options):
    func = expr.args[0]
    x, xlow, xhigh = expr.args[1]
    orig = mp.prec

    oldmaxprec = options.get('maxprec', DEFAULT_MAXPREC)
    options['maxprec'] = min(oldmaxprec, 2 * prec)

    try:
        mp.prec = prec + 5
        xlow = as_mpmath(xlow, prec + 15, options)
        xhigh = as_mpmath(xhigh, prec + 15, options)

        # Integration is like summation, and we can phone home from
        # the integrand function to update accuracy summation style
        # Note that this accuracy is inaccurate, since it fails
        # to account for the variable quadrature weights,
        # but it is better than nothing

        have_part = [False, False]
        max_real_term = [MINUS_INF]
        max_imag_term = [MINUS_INF]

        def f(t):
            re, im, re_acc, im_acc = evalf(func, mp.prec, {'subs': {x: t}})

            have_part[0] = re or have_part[0]
            have_part[1] = im or have_part[1]

            max_real_term[0] = max(max_real_term[0], fastlog(re))
            max_imag_term[0] = max(max_imag_term[0], fastlog(im))

            if im:
                return mpc(re or fzero, im)
            return mpf(re or fzero)

        if options.get('quad') == 'osc':
            A = C.Wild('A', exclude=[x])
            B = C.Wild('B', exclude=[x])
            D = C.Wild('D')
            m = func.match(C.cos(A * x + B) * D)
            if not m:
                m = func.match(C.sin(A * x + B) * D)
            if not m:
                raise ValueError(
                    "An integrand of the form sin(A*x+B)*f(x) "
                    "or cos(A*x+B)*f(x) is required for oscillatory quadrature"
                )
            period = as_mpmath(2 * S.Pi / m[A], prec + 15, options)
            result = quadosc(f, [xlow, xhigh], period=period)
            # XXX: quadosc does not do error detection yet
            quadrature_error = MINUS_INF
        else:
            result, quadrature_error = quadts(f, [xlow, xhigh], error=1)
            quadrature_error = fastlog(quadrature_error._mpf_)

    finally:
        options['maxprec'] = oldmaxprec
        mp.prec = orig

    if have_part[0]:
        re = result.real._mpf_
        if re == fzero:
            re = mpf_shift(fone,
                           min(-prec, -max_real_term[0], -quadrature_error))
            re_acc = -1
        else:
            re_acc = -max(max_real_term[0] - fastlog(re) - prec,
                          quadrature_error)
    else:
        re, re_acc = None, None

    if have_part[1]:
        im = result.imag._mpf_
        if im == fzero:
            im = mpf_shift(fone,
                           min(-prec, -max_imag_term[0], -quadrature_error))
            im_acc = -1
        else:
            im_acc = -max(max_imag_term[0] - fastlog(im) - prec,
                          quadrature_error)
    else:
        im, im_acc = None, None

    result = re, im, re_acc, im_acc
    return result