Example #1
0
 def _cholesky(self, hermitian=True):
     """Helper function of cholesky.
     Without the error checks.
     To be used privately.
     Implements the Cholesky-Banachiewicz algorithm.
     Returns L such that L*L.H == self if hermitian flag is True,
     or L*L.T == self if hermitian is False.
     """
     L = zeros(self.rows, self.rows)
     if hermitian:
         for i in range(self.rows):
             for j in range(i):
                 L[i, j] = (1 / L[j, j])*expand_mul(self[i, j] -
                     sum(L[i, k]*L[j, k].conjugate() for k in range(j)))
             Lii2 = expand_mul(self[i, i] -
                 sum(L[i, k]*L[i, k].conjugate() for k in range(i)))
             if Lii2.is_positive is False:
                 raise ValueError("Matrix must be positive-definite")
             L[i, i] = sqrt(Lii2)
     else:
         for i in range(self.rows):
             for j in range(i):
                 L[i, j] = (1 / L[j, j])*(self[i, j] -
                     sum(L[i, k]*L[j, k] for k in range(j)))
             L[i, i] = sqrt(self[i, i] -
                 sum(L[i, k]**2 for k in range(i)))
     return self._new(L)
Example #2
0
 def _LDLdecomposition(self, hermitian=True):
     """Helper function of LDLdecomposition.
     Without the error checks.
     To be used privately.
     Returns L and D such that L*D*L.H == self if hermitian flag is True,
     or L*D*L.T == self if hermitian is False.
     """
     # https://en.wikipedia.org/wiki/Cholesky_decomposition#LDL_decomposition_2
     D = zeros(self.rows, self.rows)
     L = eye(self.rows)
     if hermitian:
         for i in range(self.rows):
             for j in range(i):
                 L[i, j] = (1 / D[j, j])*expand_mul(self[i, j] - sum(
                     L[i, k]*L[j, k].conjugate()*D[k, k] for k in range(j)))
             D[i, i] = expand_mul(self[i, i] -
                 sum(L[i, k]*L[i, k].conjugate()*D[k, k] for k in range(i)))
             if D[i, i].is_positive is False:
                 raise ValueError("Matrix must be positive-definite")
     else:
         for i in range(self.rows):
             for j in range(i):
                 L[i, j] = (1 / D[j, j])*(self[i, j] - sum(
                     L[i, k]*L[j, k]*D[k, k] for k in range(j)))
             D[i, i] = self[i, i] - sum(L[i, k]**2*D[k, k] for k in range(i))
     return self._new(L), self._new(D)
Example #3
0
    def _bell_incomplete_poly(n, k, symbols):
        r"""
        The second kind of Bell polynomials (incomplete Bell polynomials).

        Calculated by recurrence formula:

        .. math:: B_{n,k}(x_1, x_2, \dotsc, x_{n-k+1}) =
                \sum_{m=1}^{n-k+1}
                \x_m \binom{n-1}{m-1} B_{n-m,k-1}(x_1, x_2, \dotsc, x_{n-m-k})

        where
            B_{0,0} = 1;
            B_{n,0} = 0; for n>=1
            B_{0,k} = 0; for k>=1

        """
        if (n == 0) and (k == 0):
            return S.One
        elif (n == 0) or (k == 0):
            return S.Zero
        s = S.Zero
        a = S.One
        for m in xrange(1, n - k + 2):
            s += a * bell._bell_incomplete_poly(
                n - m, k - 1, symbols) * symbols[m - 1]
            a = a * (n - m) / m
        return expand_mul(s)
Example #4
0
def test_issue_11230():
    # a specific test that always failed
    a, b, f, k, l, i = symbols('a b f k l i')
    p = [a*b*f*k*l, a*i*k**2*l, f*i*k**2*l]
    R, C = cse(p)
    assert not any(i.is_Mul for a in C for i in a.args)

    # random tests for the issue
    from random import choice
    from sympy.core.function import expand_mul
    s = symbols('a:m')
    # 35 Mul tests, none of which should ever fail
    ex = [Mul(*[choice(s) for i in range(5)]) for i in range(7)]
    for p in subsets(ex, 3):
        p = list(p)
        R, C = cse(p)
        assert not any(i.is_Mul for a in C for i in a.args)
        for ri in reversed(R):
            for i in range(len(C)):
                C[i] = C[i].subs(*ri)
        assert p == C
    # 35 Add tests, none of which should ever fail
    ex = [Add(*[choice(s[:7]) for i in range(5)]) for i in range(7)]
    for p in subsets(ex, 3):
        p = list(p)
        was = R, C = cse(p)
        assert not any(i.is_Add for a in C for i in a.args)
        for ri in reversed(R):
            for i in range(len(C)):
                C[i] = C[i].subs(*ri)
        # use expand_mul to handle cases like this:
        # p = [a + 2*b + 2*e, 2*b + c + 2*e, b + 2*c + 2*g]
        # x0 = 2*(b + e) is identified giving a rebuilt p that
        # is now `[a + 2*(b + e), c + 2*(b + e), b + 2*c + 2*g]`
        assert p == [expand_mul(i) for i in C]
Example #5
0
 def valid(x):
     # this is used to see if gen=x satisfies the
     # relational by substituting it into the
     # expanded form and testing against 0, e.g.
     # if expr = x*(x + 1) < 2 then e = x*(x + 1) - 2
     # and expanded_e = x**2 + x - 2; the test is
     # whether a given value of x satisfies
     # x**2 + x - 2 < 0
     #
     # expanded_e, expr and gen used from enclosing scope
     v = expanded_e.subs(gen, expand_mul(x))
     try:
         r = expr.func(v, 0)
     except TypeError:
         r = S.false
     if r in (S.true, S.false):
         return r
     if v.is_real is False:
         return S.false
     else:
         v = v.n(2)
         if v.is_comparable:
             return expr.func(v, 0)
         # not comparable or couldn't be evaluated
         raise NotImplementedError(
             'relationship did not evaluate: %s' % r)
Example #6
0
    def _eval_as_leading_term(self, x):
        from sympy import expand_mul, factor_terms

        old = self

        expr = expand_mul(self)
        if not expr.is_Add:
            return expr.as_leading_term(x)

        infinite = [t for t in expr.args if t.is_infinite]

        expr = expr.func(*[t.as_leading_term(x) for t in expr.args]).removeO()
        if not expr:
            # simple leading term analysis gave us 0 but we have to send
            # back a term, so compute the leading term (via series)
            return old.compute_leading_term(x)
        elif expr is S.NaN:
            return old.func._from_args(infinite)
        elif not expr.is_Add:
            return expr
        else:
            plain = expr.func(*[s for s, _ in expr.extract_leading_order(x)])
            rv = factor_terms(plain, fraction=False)
            rv_simplify = rv.simplify()
            # if it simplifies to an x-free expression, return that;
            # tests don't fail if we don't but it seems nicer to do this
            if x not in rv_simplify.free_symbols:
                if rv_simplify.is_zero and plain.is_zero is not True:
                    return (expr - plain)._eval_as_leading_term(x)
                return rv_simplify
            return rv
Example #7
0
def sqrtdenest(expr, max_iter=3):
    """Denests sqrts in an expression that contain other square roots
    if possible, otherwise returns the expr unchanged. This is based on the
    algorithms of [1].

    Examples
    ========

    >>> from sympy.simplify.sqrtdenest import sqrtdenest
    >>> from sympy import sqrt
    >>> sqrtdenest(sqrt(5 + 2 * sqrt(6)))
    sqrt(2) + sqrt(3)

    See Also
    ========
    sympy.solvers.solvers.unrad

    References
    ==========
    [1] http://researcher.watson.ibm.com/researcher/files/us-fagin/symb85.pdf

    [2] D. J. Jeffrey and A. D. Rich, 'Symplifying Square Roots of Square Roots
    by Denesting' (available at http://www.cybertester.com/data/denest.pdf)

    """
    expr = expand_mul(sympify(expr))
    for i in range(max_iter):
        z = _sqrtdenest0(expr)
        if expr == z:
            return expr
        expr = z
    return expr
Example #8
0
def sqrtdenest(expr, max_iter=3):
    """Denests sqrts in an expression that contain other square roots
    if possible, otherwise returns the expr unchanged. This is based on the
    algorithms of [1].

    Examples
    ========

    >>> from sympy.simplify.sqrtdenest import sqrtdenest
    >>> from sympy import sqrt
    >>> sqrtdenest(sqrt(5 + 2 * sqrt(6)))
    sqrt(2) + sqrt(3)

    See Also
    ========
    sympy.solvers.solvers.unrad

    References
    ==========
    [1] http://www.almaden.ibm.com/cs/people/fagin/symb85.pdf
    """
    expr = expand_mul(sympify(expr))
    for i in range(max_iter):
        z = _sqrtdenest0(expr)
        if expr == z:
            return expr
        expr = z
    return expr
Example #9
0
 def _bell_poly(n, prev):
     s = 1
     a = 1
     for k in xrange(2, n + 1):
         a = a * (n - k + 1) // (k - 1)
         s += a * prev[k - 1]
     return expand_mul(_sym * s)
Example #10
0
 def _LDLdecomposition(self):
     """Helper function of LDLdecomposition.
     Without the error checks.
     To be used privately.
     """
     # https://en.wikipedia.org/wiki/Cholesky_decomposition#LDL_decomposition_2
     D = zeros(self.rows, self.rows)
     L = eye(self.rows)
     for i in range(self.rows):
         for j in range(i):
             L[i, j] = (1 / D[j, j])*expand_mul(self[i, j] - sum(
                 L[i, k]*L[j, k].conjugate()*D[k, k] for k in range(j)))
         D[i, i] = expand_mul(self[i, i] -
             sum(L[i, k]*L[i, k].conjugate()*D[k, k] for k in range(i)))
         if D[i, i].is_positive is False:
             raise ValueError("Matrix must be positive-definite")
     return self._new(L), self._new(D)
Example #11
0
 def _cholesky(self):
     """Helper function of cholesky.
     Without the error checks.
     To be used privately.
     Implements the Cholesky-Banachiewicz algorithm.
     """
     L = zeros(self.rows, self.rows)
     for i in range(self.rows):
         for j in range(i):
             L[i, j] = (1 / L[j, j])*expand_mul(self[i, j] -
                 sum(L[i, k]*L[j, k].conjugate() for k in range(j)))
         Lii2 = expand_mul(self[i, i] -
             sum(L[i, k]*L[i, k].conjugate() for k in range(i)))
         if Lii2.is_positive is False:
             raise ValueError("Matrix must be positive-definite")
         L[i, i] = sqrt(Lii2)
     return self._new(L)
Example #12
0
def convolution_fwht(a, b):
    """
    Performs dyadic (*bitwise-XOR*) convolution using Fast Walsh Hadamard
    Transform.

    The convolution is automatically padded to the right with zeros, as the
    *radix-2 FWHT* requires the number of sample points to be a power of 2.

    Parameters
    ==========

    a, b : iterables
        The sequences for which convolution is performed.

    Examples
    ========

    >>> from sympy import symbols, S, I
    >>> from sympy.discrete.convolutions import convolution_fwht

    >>> u, v, x, y = symbols('u v x y')
    >>> convolution_fwht([u, v], [x, y])
    [u*x + v*y, u*y + v*x]

    >>> convolution_fwht([2, 3], [4, 5])
    [23, 22]
    >>> convolution_fwht([2, 5 + 4*I, 7], [6*I, 7, 3 + 4*I])
    [56 + 68*I, -10 + 30*I, 6 + 50*I, 48 + 32*I]

    >>> convolution_fwht([S(33)/7, S(55)/6, S(7)/4], [S(2)/3, 5])
    [2057/42, 1870/63, 7/6, 35/4]

    References
    ==========

    .. [1] https://www.radioeng.cz/fulltexts/2002/02_03_40_42.pdf
    .. [2] https://en.wikipedia.org/wiki/Hadamard_transform

    """

    if not a or not b:
        return []

    a, b = a[:], b[:]
    n = max(len(a), len(b))

    if n&(n - 1): # not a power of 2
        n = 2**n.bit_length()

    # padding with zeros
    a += [S.Zero]*(n - len(a))
    b += [S.Zero]*(n - len(b))

    a, b = fwht(a), fwht(b)
    a = [expand_mul(x*y) for x, y in zip(a, b)]
    a = ifwht(a)

    return a
Example #13
0
 def _combine_inverse(lhs, rhs):
     """
     Returns lhs - rhs, but treats oo like a symbol so oo - oo
     returns 0, instead of a nan.
     """
     from sympy.core.function import expand_mul
     from sympy.core.symbol import Dummy
     inf = (S.Infinity, S.NegativeInfinity)
     if lhs.has(*inf) or rhs.has(*inf):
         oo = Dummy('oo')
         reps = {
             S.Infinity: oo,
             S.NegativeInfinity: -oo}
         ireps = dict([(v, k) for k, v in reps.items()])
         eq = expand_mul(lhs.xreplace(reps) - rhs.xreplace(reps))
         if eq.has(oo):
             eq = eq.replace(
                 lambda x: x.is_Pow and x.base == oo,
                 lambda x: x.base)
         return eq.xreplace(ireps)
     else:
         return expand_mul(lhs - rhs)
Example #14
0
 def eval(cls, p, q):
     from sympy.simplify.simplify import nsimplify
     if q.is_Number:
         float = not q.is_Rational
         pnew = expand_mul(p)
         if pnew.is_Number:
             float = float or not pnew.is_Rational
             if not float:
                 return pnew % q
             return Float(nsimplify(pnew) % nsimplify(q))
         elif pnew.is_Add and pnew.args[0].is_Number:
             r, p = pnew.as_two_terms()
             p += Mod(r, q)
     return Mod(p, q, evaluate=False)
Example #15
0
def convolution_fft(a, b, dps=None):
    """
    Performs linear convolution using Fast Fourier Transform.

    Parameters
    ==========

    a, b : iterables
        The sequences for which convolution is performed.
    dps : Integer
        Specifies the number of decimal digits for precision.

    Examples
    ========

    >>> from sympy import S, I
    >>> from sympy.discrete.convolutions import convolution_fft

    >>> convolution_fft([2, 3], [4, 5])
    [8, 22, 15]
    >>> convolution_fft([2, 5], [6, 7, 3])
    [12, 44, 41, 15]
    >>> convolution_fft([1 + 2*I, 4 + 3*I], [S(5)/4, 6])
    [5/4 + 5*I/2, 11 + 63*I/4, 24 + 18*I]

    References
    ==========

    .. [1] https://en.wikipedia.org/wiki/Convolution_theorem
    .. [2] https://en.wikipedia.org/wiki/Discrete_Fourier_transform_(general%29

    """

    a, b = a[:], b[:]
    n = m = len(a) + len(b) - 1 # convolution size

    if n > 0 and n&(n - 1): # not a power of 2
        n = 2**n.bit_length()

    # padding with zeros
    a += [S.Zero]*(n - len(a))
    b += [S.Zero]*(n - len(b))

    a, b = fft(a, dps), fft(b, dps)
    a = [expand_mul(x*y) for x, y in zip(a, b)]
    a = ifft(a, dps)[:m]

    return a
Example #16
0
def _fourier_transform(seq, dps, inverse=False):
    """Utility function for the Discrete Fourier Transform"""

    if not iterable(seq):
        raise TypeError("Expected a sequence of numeric coefficients "
                        "for Fourier Transform")

    a = [sympify(arg) for arg in seq]
    if any(x.has(Symbol) for x in a):
        raise ValueError("Expected non-symbolic coefficients")

    n = len(a)
    if n < 2:
        return a

    b = n.bit_length() - 1
    if n&(n - 1): # not a power of 2
        b += 1
        n = 2**b

    a += [S.Zero]*(n - len(a))
    for i in range(1, n):
        j = int(ibin(i, b, str=True)[::-1], 2)
        if i < j:
            a[i], a[j] = a[j], a[i]

    ang = -2*pi/n if inverse else 2*pi/n

    if dps is not None:
        ang = ang.evalf(dps + 2)

    w = [cos(ang*i) + I*sin(ang*i) for i in range(n // 2)]

    h = 2
    while h <= n:
        hf, ut = h // 2, n // h
        for i in range(0, n, h):
            for j in range(hf):
                u, v = a[i + j], expand_mul(a[i + j + hf]*w[ut * j])
                a[i + j], a[i + j + hf] = u + v, u - v
        h *= 2

    if inverse:
        a = [(x/n).evalf(dps) for x in a] if dps is not None \
                            else [x/n for x in a]

    return a
Example #17
0
    def projection(self, pt):
        """Projection of any point on the plane
           Will result in a 3D point.

        Parameters
        ==========

        Point or Point3D

        Returns
        =======

        Point3D

        Notes
        =====

        For the interaction between 2D and 3D points, you should convert the 2D
        point to 3D by using this method. For example for finding the distance
        between Point(1, 2) and Point3D(1, 2, 3) you can convert the 2D point
        to Point3D(1, 2, 0) by projecting it to plane which is parallel to xy
        plane.

        Examples
        ========

        >>> from sympy import Plane, Point, Point3D
        >>> a = Plane(Point3D(1, 1, 1), normal_vector=(1, 1, 1))
        >>> b = Point(1, 2)
        >>> a.projection(b)
        Point3D(1, 2, 0)
        >>> c = Point3D(1, 1, 1)
        >>> a.projection(c)
        Point3D(4/3, 4/3, 4/3)

        """
        x, y, z = map(Dummy, 'xyz')
        k = expand_mul(self.equation(x, y, z))
        a, b, c = [k.coeff(i) for i in (x, y, z)]
        d = k.xreplace({x: pt.args[0], y: pt.args[1], z:0})
        t = -d/(a**2 + b**2 + c**2)
        if isinstance(pt, Point):
            return Point3D(pt.x + t*a, pt.y + t*b, t*c)
        if isinstance(pt, Point3D):
            return Point3D(pt.x + t*a, pt.y + t*b, pt.z + t*c)
Example #18
0
def _set_function(f, self):
    from sympy.core.function import expand_mul
    if not self:
        return S.EmptySet
    if not isinstance(f.expr, Expr):
        return
    if self.size == 1:
        return FiniteSet(f(self[0]))
    if f is S.IdentityFunction:
        return self

    x = f.variables[0]
    expr = f.expr
    # handle f that is linear in f's variable
    if x not in expr.free_symbols or x in expr.diff(x).free_symbols:
        return
    if self.start.is_finite:
        F = f(self.step*x + self.start)  # for i in range(len(self))
    else:
        F = f(-self.step*x + self[-1])
    F = expand_mul(F)
    if F != expr:
        return imageset(x, F, Range(self.size))
Example #19
0
def trigsimp_old(expr, *, first=True, **opts):
    """
    Reduces expression by using known trig identities.

    Notes
    =====

    deep:
    - Apply trigsimp inside all objects with arguments

    recursive:
    - Use common subexpression elimination (cse()) and apply
    trigsimp recursively (this is quite expensive if the
    expression is large)

    method:
    - Determine the method to use. Valid choices are 'matching' (default),
    'groebner', 'combined', 'fu' and 'futrig'. If 'matching', simplify the
    expression recursively by pattern matching. If 'groebner', apply an
    experimental groebner basis algorithm. In this case further options
    are forwarded to ``trigsimp_groebner``, please refer to its docstring.
    If 'combined', first run the groebner basis algorithm with small
    default parameters, then run the 'matching' algorithm. 'fu' runs the
    collection of trigonometric transformations described by Fu, et al.
    (see the `fu` docstring) while `futrig` runs a subset of Fu-transforms
    that mimic the behavior of `trigsimp`.

    compare:
    - show input and output from `trigsimp` and `futrig` when different,
    but returns the `trigsimp` value.

    Examples
    ========

    >>> from sympy import trigsimp, sin, cos, log, cot
    >>> from sympy.abc import x
    >>> e = 2*sin(x)**2 + 2*cos(x)**2
    >>> trigsimp(e, old=True)
    2
    >>> trigsimp(log(e), old=True)
    log(2*sin(x)**2 + 2*cos(x)**2)
    >>> trigsimp(log(e), deep=True, old=True)
    log(2)

    Using `method="groebner"` (or `"combined"`) can sometimes lead to a lot
    more simplification:

    >>> e = (-sin(x) + 1)/cos(x) + cos(x)/(-sin(x) + 1)
    >>> trigsimp(e, old=True)
    (1 - sin(x))/cos(x) + cos(x)/(1 - sin(x))
    >>> trigsimp(e, method="groebner", old=True)
    2/cos(x)

    >>> trigsimp(1/cot(x)**2, compare=True, old=True)
          futrig: tan(x)**2
    cot(x)**(-2)

    """
    old = expr
    if first:
        if not expr.has(*_trigs):
            return expr

        trigsyms = set().union(*[t.free_symbols for t in expr.atoms(*_trigs)])
        if len(trigsyms) > 1:
            from sympy.simplify.simplify import separatevars

            d = separatevars(expr)
            if d.is_Mul:
                d = separatevars(d, dict=True) or d
            if isinstance(d, dict):
                expr = 1
                for k, v in d.items():
                    # remove hollow factoring
                    was = v
                    v = expand_mul(v)
                    opts['first'] = False
                    vnew = trigsimp(v, **opts)
                    if vnew == v:
                        vnew = was
                    expr *= vnew
                old = expr
            else:
                if d.is_Add:
                    for s in trigsyms:
                        r, e = expr.as_independent(s)
                        if r:
                            opts['first'] = False
                            expr = r + trigsimp(e, **opts)
                            if not expr.is_Add:
                                break
                    old = expr

    recursive = opts.pop('recursive', False)
    deep = opts.pop('deep', False)
    method = opts.pop('method', 'matching')

    def groebnersimp(ex, deep, **opts):
        def traverse(e):
            if e.is_Atom:
                return e
            args = [traverse(x) for x in e.args]
            if e.is_Function or e.is_Pow:
                args = [trigsimp_groebner(x, **opts) for x in args]
            return e.func(*args)

        if deep:
            ex = traverse(ex)
        return trigsimp_groebner(ex, **opts)

    trigsimpfunc = {
        'matching': (lambda x, d: _trigsimp(x, d)),
        'groebner': (lambda x, d: groebnersimp(x, d, **opts)),
        'combined': (lambda x, d: _trigsimp(
            groebnersimp(x, d, polynomial=True, hints=[2, tan]), d))
    }[method]

    if recursive:
        w, g = cse(expr)
        g = trigsimpfunc(g[0], deep)

        for sub in reversed(w):
            g = g.subs(sub[0], sub[1])
            g = trigsimpfunc(g, deep)
        result = g
    else:
        result = trigsimpfunc(expr, deep)

    if opts.get('compare', False):
        f = futrig(old)
        if f != result:
            print('\tfutrig:', f)

    return result
Example #20
0
def _mexpand(expr):
    return expand_mul(expand_multinomial(expr))
Example #21
0
 def simp_hyp(expr):
     return factor_terms(expand_mul(expr)).rewrite(sin)
Example #22
0
    def eval(cls, arg, base=None):
        from sympy import unpolarify
        from sympy.calculus import AccumBounds
        from sympy.sets.setexpr import SetExpr
        from sympy.functions.elementary.complexes import Abs

        arg = sympify(arg)

        if base is not None:
            base = sympify(base)
            if base == 1:
                if arg == 1:
                    return S.NaN
                else:
                    return S.ComplexInfinity
            try:
                # handle extraction of powers of the base now
                # or else expand_log in Mul would have to handle this
                n = multiplicity(base, arg)
                if n:
                    return n + log(arg / base**n) / log(base)
                else:
                    return log(arg) / log(base)
            except ValueError:
                pass
            if base is not S.Exp1:
                return cls(arg) / cls(base)
            else:
                return cls(arg)

        if arg.is_Number:
            if arg.is_zero:
                return S.ComplexInfinity
            elif arg is S.One:
                return S.Zero
            elif arg is S.Infinity:
                return S.Infinity
            elif arg is S.NegativeInfinity:
                return S.Infinity
            elif arg is S.NaN:
                return S.NaN
            elif arg.is_Rational and arg.p == 1:
                return -cls(arg.q)

        I = S.ImaginaryUnit
        if isinstance(arg, exp) and arg.args[0].is_extended_real:
            return arg.args[0]
        elif isinstance(arg, exp) and arg.args[0].is_number:
            r_, i_ = match_real_imag(arg.args[0])
            if i_ and i_.is_comparable:
                i_ %= 2 * S.Pi
                if i_ > S.Pi:
                    i_ -= 2 * S.Pi
                return r_ + expand_mul(i_ * I, deep=False)
        elif isinstance(arg, exp_polar):
            return unpolarify(arg.exp)
        elif isinstance(arg, AccumBounds):
            if arg.min.is_positive:
                return AccumBounds(log(arg.min), log(arg.max))
            else:
                return
        elif isinstance(arg, SetExpr):
            return arg._eval_func(cls)

        if arg.is_number:
            if arg.is_negative:
                return S.Pi * I + cls(-arg)
            elif arg is S.ComplexInfinity:
                return S.ComplexInfinity
            elif arg is S.Exp1:
                return S.One

        if arg.is_zero:
            return S.ComplexInfinity

        # don't autoexpand Pow or Mul (see the issue 3351):
        if not arg.is_Add:
            coeff = arg.as_coefficient(I)

            if coeff is not None:
                if coeff is S.Infinity:
                    return S.Infinity
                elif coeff is S.NegativeInfinity:
                    return S.Infinity
                elif coeff.is_Rational:
                    if coeff.is_nonnegative:
                        return S.Pi * I * S.Half + cls(coeff)
                    else:
                        return -S.Pi * I * S.Half + cls(-coeff)

        if arg.is_number and arg.is_algebraic:
            # Match arg = coeff*(r_ + i_*I) with coeff>0, r_ and i_ real.
            coeff, arg_ = arg.as_independent(I, as_Add=False)
            if coeff.is_negative:
                coeff *= -1
                arg_ *= -1
            arg_ = expand_mul(arg_, deep=False)
            r_, i_ = arg_.as_independent(I, as_Add=True)
            i_ = i_.as_coefficient(I)
            if coeff.is_real and i_ and i_.is_real and r_.is_real:
                if r_.is_zero:
                    if i_.is_positive:
                        return S.Pi * I * S.Half + cls(coeff * i_)
                    elif i_.is_negative:
                        return -S.Pi * I * S.Half + cls(coeff * -i_)
                else:
                    from sympy.simplify import ratsimp
                    # Check for arguments involving rational multiples of pi
                    t = (i_ / r_).cancel()
                    atan_table = {
                        # first quadrant only
                        sqrt(3):
                        S.Pi / 3,
                        1:
                        S.Pi / 4,
                        sqrt(5 - 2 * sqrt(5)):
                        S.Pi / 5,
                        sqrt(2) * sqrt(5 - sqrt(5)) / (1 + sqrt(5)):
                        S.Pi / 5,
                        sqrt(5 + 2 * sqrt(5)):
                        S.Pi * Rational(2, 5),
                        sqrt(2) * sqrt(sqrt(5) + 5) / (-1 + sqrt(5)):
                        S.Pi * Rational(2, 5),
                        sqrt(3) / 3:
                        S.Pi / 6,
                        sqrt(2) - 1:
                        S.Pi / 8,
                        sqrt(2 - sqrt(2)) / sqrt(sqrt(2) + 2):
                        S.Pi / 8,
                        sqrt(2) + 1:
                        S.Pi * Rational(3, 8),
                        sqrt(sqrt(2) + 2) / sqrt(2 - sqrt(2)):
                        S.Pi * Rational(3, 8),
                        sqrt(1 - 2 * sqrt(5) / 5):
                        S.Pi / 10,
                        (-sqrt(2) + sqrt(10)) / (2 * sqrt(sqrt(5) + 5)):
                        S.Pi / 10,
                        sqrt(1 + 2 * sqrt(5) / 5):
                        S.Pi * Rational(3, 10),
                        (sqrt(2) + sqrt(10)) / (2 * sqrt(5 - sqrt(5))):
                        S.Pi * Rational(3, 10),
                        2 - sqrt(3):
                        S.Pi / 12,
                        (-1 + sqrt(3)) / (1 + sqrt(3)):
                        S.Pi / 12,
                        2 + sqrt(3):
                        S.Pi * Rational(5, 12),
                        (1 + sqrt(3)) / (-1 + sqrt(3)):
                        S.Pi * Rational(5, 12)
                    }
                    if t in atan_table:
                        modulus = ratsimp(coeff * Abs(arg_))
                        if r_.is_positive:
                            return cls(modulus) + I * atan_table[t]
                        else:
                            return cls(modulus) + I * (atan_table[t] - S.Pi)
                    elif -t in atan_table:
                        modulus = ratsimp(coeff * Abs(arg_))
                        if r_.is_positive:
                            return cls(modulus) + I * (-atan_table[-t])
                        else:
                            return cls(modulus) + I * (S.Pi - atan_table[-t])
Example #23
0
    def _eval_expand_multinomial(self, **hints):
        """(a+b+..) ** n -> a**n + n*a**(n-1)*b + .., n is nonzero integer"""
        b = self.base
        e = self.exp

        if b is None:
            base = self.base
        else:
            base = b

        if e is None:
            exp = self.exp
        else:
            exp = e

        if e is not None or b is not None:
            result = Pow(base, exp)

            if result.is_Pow:
                base, exp = result.base, result.exp
            else:
                return result
        else:
            result = None

        if exp.is_Rational and exp.p > 0 and base.is_Add:
            if not exp.is_Integer:
                n = Integer(exp.p // exp.q)

                if not n:
                    return Pow(base, exp)
                else:
                    radical, result = Pow(base, exp - n), []

                    expanded_base_n = Pow(base, n)
                    if expanded_base_n.is_Pow:
                        expanded_base_n = expanded_base_n._eval_expand_multinomial()
                    for term in Add.make_args(expanded_base_n):
                        result.append(term*radical)

                    return Add(*result)

            n = int(exp)

            if base.is_commutative:
                order_terms, other_terms = [], []

                for order in base.args:
                    if order.is_Order:
                        order_terms.append(order)
                    else:
                        other_terms.append(order)

                if order_terms:
                    # (f(x) + O(x^n))^m -> f(x)^m + m*f(x)^{m-1} *O(x^n)
                    f = Add(*other_terms)

                    if n == 2:
                        return expand_multinomial(f**n, deep=False) + n*f*Add(*order_terms)
                    else:
                        g = expand_multinomial(f**(n - 1), deep=False)
                        return expand_mul(f*g, deep=False) + n*g*Add(*order_terms)

                if base.is_number:
                    # Efficiently expand expressions of the form (a + b*I)**n
                    # where 'a' and 'b' are real numbers and 'n' is integer.
                    a, b = base.as_real_imag()

                    if a.is_Rational and b.is_Rational:
                        if not a.is_Integer:
                            if not b.is_Integer:
                                k = Pow(a.q * b.q, n)
                                a, b = a.p*b.q, a.q*b.p
                            else:
                                k = Pow(a.q, n)
                                a, b = a.p, a.q*b
                        elif not b.is_Integer:
                            k = Pow(b.q, n)
                            a, b = a*b.q, b.p
                        else:
                            k = 1

                        a, b, c, d = int(a), int(b), 1, 0

                        while n:
                            if n & 1:
                                c, d = a*c-b*d, b*c+a*d
                                n -= 1
                            a, b = a*a-b*b, 2*a*b
                            n //= 2

                        I = S.ImaginaryUnit

                        if k == 1:
                            return c + I*d
                        else:
                            return Integer(c)/k + I*d/k

                p = other_terms
                # (x+y)**3 -> x**3 + 3*x**2*y + 3*x*y**2 + y**3
                # in this particular example:
                # p = [x,y]; n = 3
                # so now it's easy to get the correct result -- we get the
                # coefficients first:
                from sympy import multinomial_coefficients
                expansion_dict = multinomial_coefficients(len(p), n)
                # in our example: {(3, 0): 1, (1, 2): 3, (0, 3): 1, (2, 1): 3}
                # and now construct the expression.

                # An elegant way would be to use Poly, but unfortunately it is
                # slower than the direct method below, so it is commented out:
                #b = {}
                #for k in expansion_dict:
                #    b[k] = Integer(expansion_dict[k])
                #return Poly(b, *p).as_expr()

                from sympy.polys.polyutils import basic_from_dict
                result = basic_from_dict(expansion_dict, *p)
                return result
            else:
                if n == 2:
                    return Add(*[f*g for f in base.args for g in base.args])
                else:
                    multi = (base**(n-1))._eval_expand_multinomial()
                    if multi.is_Add:
                        return Add(*[f*g for f in base.args for g in multi.args])
                    else:
                        return Add(*[f*multi for f in base.args])
        elif exp.is_Rational and exp.p < 0 and base.is_Add and abs(exp.p) > exp.q:
            return 1 / Pow(base, -exp)._eval_expand_multinomial()
        elif exp.is_Add and base.is_Number:
            #  a + b      a  b
            # n      --> n  n  , where n, a, b are Numbers

            coeff, tail = S.One, S.Zero

            for term in exp.args:
                if term.is_Number:
                    coeff *= Pow(base, term)
                else:
                    tail += term

            return coeff * Pow(base, tail)
        else:
            return result
Example #24
0
    def _eval_expand_multinomial(self, **hints):
        """(a+b+..) ** n -> a**n + n*a**(n-1)*b + .., n is nonzero integer"""

        base, exp = self.args
        result = self

        if exp.is_Rational and exp.p > 0 and base.is_Add:
            if not exp.is_Integer:
                n = Integer(exp.p // exp.q)

                if not n:
                    return result
                else:
                    radical, result = self.func(base, exp - n), []

                    expanded_base_n = self.func(base, n)
                    if expanded_base_n.is_Pow:
                        expanded_base_n = \
                            expanded_base_n._eval_expand_multinomial()
                    for term in Add.make_args(expanded_base_n):
                        result.append(term * radical)

                    return Add(*result)

            n = int(exp)

            if base.is_commutative:
                order_terms, other_terms = [], []

                for b in base.args:
                    if b.is_Order:
                        order_terms.append(b)
                    else:
                        other_terms.append(b)

                if order_terms:
                    # (f(x) + O(x^n))^m -> f(x)^m + m*f(x)^{m-1} *O(x^n)
                    f = Add(*other_terms)
                    o = Add(*order_terms)

                    if n == 2:
                        return expand_multinomial(f**n, deep=False) + n * f * o
                    else:
                        g = expand_multinomial(f**(n - 1), deep=False)
                        return expand_mul(f * g, deep=False) + n * g * o

                if base.is_number:
                    # Efficiently expand expressions of the form (a + b*I)**n
                    # where 'a' and 'b' are real numbers and 'n' is integer.
                    a, b = base.as_real_imag()

                    if a.is_Rational and b.is_Rational:
                        if not a.is_Integer:
                            if not b.is_Integer:
                                k = self.func(a.q * b.q, n)
                                a, b = a.p * b.q, a.q * b.p
                            else:
                                k = self.func(a.q, n)
                                a, b = a.p, a.q * b
                        elif not b.is_Integer:
                            k = self.func(b.q, n)
                            a, b = a * b.q, b.p
                        else:
                            k = 1

                        a, b, c, d = int(a), int(b), 1, 0

                        while n:
                            if n & 1:
                                c, d = a * c - b * d, b * c + a * d
                                n -= 1
                            a, b = a * a - b * b, 2 * a * b
                            n //= 2

                        I = S.ImaginaryUnit

                        if k == 1:
                            return c + I * d
                        else:
                            return Integer(c) / k + I * d / k

                p = other_terms
                # (x+y)**3 -> x**3 + 3*x**2*y + 3*x*y**2 + y**3
                # in this particular example:
                # p = [x,y]; n = 3
                # so now it's easy to get the correct result -- we get the
                # coefficients first:
                from sympy import multinomial_coefficients
                from sympy.polys.polyutils import basic_from_dict
                expansion_dict = multinomial_coefficients(len(p), n)
                # in our example: {(3, 0): 1, (1, 2): 3, (0, 3): 1, (2, 1): 3}
                # and now construct the expression.
                return basic_from_dict(expansion_dict, *p)
            else:
                if n == 2:
                    return Add(*[f * g for f in base.args for g in base.args])
                else:
                    multi = (base**(n - 1))._eval_expand_multinomial()
                    if multi.is_Add:
                        return Add(
                            *[f * g for f in base.args for g in multi.args])
                    else:
                        # XXX can this ever happen if base was an Add?
                        return Add(*[f * multi for f in base.args])
        elif (exp.is_Rational and exp.p < 0 and base.is_Add
              and abs(exp.p) > exp.q):
            return 1 / self.func(base, -exp)._eval_expand_multinomial()
        elif exp.is_Add and base.is_Number:
            #  a + b      a  b
            # n      --> n  n  , where n, a, b are Numbers

            coeff, tail = S.One, S.Zero
            for term in exp.args:
                if term.is_Number:
                    coeff *= self.func(base, term)
                else:
                    tail += term

            return coeff * self.func(base, tail)
        else:
            return result
Example #25
0
def intersecting_product(a, b):
    """
    Returns the intersecting product of given sequences.

    The indices of each argument, considered as bit strings, correspond to
    subsets of a finite set.

    The intersecting product of given sequences is the sequence which
    contains the sum of products of the elements of the given sequences
    grouped by the *bitwise-AND* of the corresponding indices.

    The sequence is automatically padded to the right with zeros, as the
    definition of subset based on bitmasks (indices) requires the size of
    sequence to be a power of 2.

    Parameters
    ==========

    a, b : iterables
        The sequences for which intersecting product is to be obtained.

    Examples
    ========

    >>> from sympy import symbols, S, I, intersecting_product
    >>> u, v, x, y, z = symbols('u v x y z')

    >>> intersecting_product([u, v], [x, y])
    [u*x + u*y + v*x, v*y]
    >>> intersecting_product([u, v, x], [y, z])
    [u*y + u*z + v*y + x*y + x*z, v*z, 0, 0]

    >>> intersecting_product([1, S(2)/3], [3, 4 + 5*I])
    [9 + 5*I, 8/3 + 10*I/3]
    >>> intersecting_product([1, 3, S(5)/7], [7, 8])
    [327/7, 24, 0, 0]

    References
    ==========

    .. [1] https://people.csail.mit.edu/rrw/presentations/subset-conv.pdf

    """

    if not a or not b:
        return []

    a, b = a[:], b[:]
    n = max(len(a), len(b))

    if n&(n - 1): # not a power of 2
        n = 2**n.bit_length()

    # padding with zeros
    a += [S.Zero]*(n - len(a))
    b += [S.Zero]*(n - len(b))

    a, b = mobius_transform(a, subset=False), mobius_transform(b, subset=False)
    a = [expand_mul(x*y) for x, y in zip(a, b)]
    a = inverse_mobius_transform(a, subset=False)

    return a
Example #26
0
    def _eval_expand_multinomial(self, **hints):
        """(a+b+..) ** n -> a**n + n*a**(n-1)*b + .., n is nonzero integer"""

        base, exp = self.args
        result = self

        if exp.is_Rational and exp.p > 0 and base.is_Add:
            if not exp.is_Integer:
                n = Integer(exp.p // exp.q)

                if not n:
                    return result
                else:
                    radical, result = self.func(base, exp - n), []

                    expanded_base_n = self.func(base, n)
                    if expanded_base_n.is_Pow:
                        expanded_base_n = \
                            expanded_base_n._eval_expand_multinomial()
                    for term in Add.make_args(expanded_base_n):
                        result.append(term*radical)

                    return Add(*result)

            n = int(exp)

            if base.is_commutative:
                order_terms, other_terms = [], []

                for b in base.args:
                    if b.is_Order:
                        order_terms.append(b)
                    else:
                        other_terms.append(b)

                if order_terms:
                    # (f(x) + O(x^n))^m -> f(x)^m + m*f(x)^{m-1} *O(x^n)
                    f = Add(*other_terms)
                    o = Add(*order_terms)

                    if n == 2:
                        return expand_multinomial(f**n, deep=False) + n*f*o
                    else:
                        g = expand_multinomial(f**(n - 1), deep=False)
                        return expand_mul(f*g, deep=False) + n*g*o

                if base.is_number:
                    # Efficiently expand expressions of the form (a + b*I)**n
                    # where 'a' and 'b' are real numbers and 'n' is integer.
                    a, b = base.as_real_imag()

                    if a.is_Rational and b.is_Rational:
                        if not a.is_Integer:
                            if not b.is_Integer:
                                k = self.func(a.q * b.q, n)
                                a, b = a.p*b.q, a.q*b.p
                            else:
                                k = self.func(a.q, n)
                                a, b = a.p, a.q*b
                        elif not b.is_Integer:
                            k = self.func(b.q, n)
                            a, b = a*b.q, b.p
                        else:
                            k = 1

                        a, b, c, d = int(a), int(b), 1, 0

                        while n:
                            if n & 1:
                                c, d = a*c - b*d, b*c + a*d
                                n -= 1
                            a, b = a*a - b*b, 2*a*b
                            n //= 2

                        I = S.ImaginaryUnit

                        if k == 1:
                            return c + I*d
                        else:
                            return Integer(c)/k + I*d/k

                p = other_terms
                # (x+y)**3 -> x**3 + 3*x**2*y + 3*x*y**2 + y**3
                # in this particular example:
                # p = [x,y]; n = 3
                # so now it's easy to get the correct result -- we get the
                # coefficients first:
                from sympy import multinomial_coefficients
                from sympy.polys.polyutils import basic_from_dict
                expansion_dict = multinomial_coefficients(len(p), n)
                # in our example: {(3, 0): 1, (1, 2): 3, (0, 3): 1, (2, 1): 3}
                # and now construct the expression.
                return basic_from_dict(expansion_dict, *p)
            else:
                if n == 2:
                    return Add(*[f*g for f in base.args for g in base.args])
                else:
                    multi = (base**(n - 1))._eval_expand_multinomial()
                    if multi.is_Add:
                        return Add(*[f*g for f in base.args
                            for g in multi.args])
                    else:
                        # XXX can this ever happen if base was an Add?
                        return Add(*[f*multi for f in base.args])
        elif (exp.is_Rational and exp.p < 0 and base.is_Add and
                abs(exp.p) > exp.q):
            return 1 / self.func(base, -exp)._eval_expand_multinomial()
        elif exp.is_Add and base.is_Number:
            #  a + b      a  b
            # n      --> n  n  , where n, a, b are Numbers

            coeff, tail = S.One, S.Zero
            for term in exp.args:
                if term.is_Number:
                    coeff *= self.func(base, term)
                else:
                    tail += term

            return coeff * self.func(base, tail)
        else:
            return result
Example #27
0
 def simp_pows(expr):
     return simplify(powsimp(expand_mul(expr, deep=False), force=True)).replace(exp_polar, exp)
Example #28
0
def test_asech():
    x = Symbol('x')

    assert unchanged(asech, -x)

    # values at fixed points
    assert asech(1) == 0
    assert asech(-1) == pi * I
    assert asech(0) is oo
    assert asech(2) == I * pi / 3
    assert asech(-2) == 2 * I * pi / 3
    assert asech(nan) is nan

    # at infinites
    assert asech(oo) == I * pi / 2
    assert asech(-oo) == I * pi / 2
    assert asech(zoo) == I * AccumBounds(-pi / 2, pi / 2)

    assert asech(I) == log(1 + sqrt(2)) - I * pi / 2
    assert asech(-I) == log(1 + sqrt(2)) + I * pi / 2
    assert asech(sqrt(2) - sqrt(6)) == 11 * I * pi / 12
    assert asech(sqrt(2 - 2 / sqrt(5))) == I * pi / 10
    assert asech(-sqrt(2 - 2 / sqrt(5))) == 9 * I * pi / 10
    assert asech(2 / sqrt(2 + sqrt(2))) == I * pi / 8
    assert asech(-2 / sqrt(2 + sqrt(2))) == 7 * I * pi / 8
    assert asech(sqrt(5) - 1) == I * pi / 5
    assert asech(1 - sqrt(5)) == 4 * I * pi / 5
    assert asech(-sqrt(2 * (2 + sqrt(2)))) == 5 * I * pi / 8

    # properties
    # asech(x) == acosh(1/x)
    assert asech(sqrt(2)) == acosh(1 / sqrt(2))
    assert asech(2 / sqrt(3)) == acosh(sqrt(3) / 2)
    assert asech(2 / sqrt(2 + sqrt(2))) == acosh(sqrt(2 + sqrt(2)) / 2)
    assert asech(2) == acosh(S.Half)

    # asech(x) == I*acos(1/x)
    # (Note: the exact formula is asech(x) == +/- I*acos(1/x))
    assert asech(-sqrt(2)) == I * acos(-1 / sqrt(2))
    assert asech(-2 / sqrt(3)) == I * acos(-sqrt(3) / 2)
    assert asech(-S(2)) == I * acos(Rational(-1, 2))
    assert asech(-2 / sqrt(2)) == I * acos(-sqrt(2) / 2)

    # sech(asech(x)) / x == 1
    assert expand_mul(sech(asech(sqrt(6) - sqrt(2))) /
                      (sqrt(6) - sqrt(2))) == 1
    assert expand_mul(sech(asech(sqrt(6) + sqrt(2))) /
                      (sqrt(6) + sqrt(2))) == 1
    assert (sech(asech(sqrt(2 + 2 / sqrt(5)))) /
            (sqrt(2 + 2 / sqrt(5)))).simplify() == 1
    assert (sech(asech(-sqrt(2 + 2 / sqrt(5)))) /
            (-sqrt(2 + 2 / sqrt(5)))).simplify() == 1
    assert (sech(asech(sqrt(2 * (2 + sqrt(2))))) /
            (sqrt(2 * (2 + sqrt(2))))).simplify() == 1
    assert expand_mul(sech(asech(1 + sqrt(5))) / (1 + sqrt(5))) == 1
    assert expand_mul(sech(asech(-1 - sqrt(5))) / (-1 - sqrt(5))) == 1
    assert expand_mul(sech(asech(-sqrt(6) - sqrt(2))) /
                      (-sqrt(6) - sqrt(2))) == 1

    # numerical evaluation
    assert str(asech(5 * I).n(6)) == '0.19869 - 1.5708*I'
    assert str(asech(-5 * I).n(6)) == '0.19869 + 1.5708*I'
Example #29
0
    def eval(cls, arg):
        from sympy.simplify.simplify import signsimp
        from sympy.core.function import expand_mul

        if hasattr(arg, '_eval_Abs'):
            obj = arg._eval_Abs()
            if obj is not None:
                return obj
        if not isinstance(arg, Expr):
            raise TypeError("Bad argument type for Abs(): %s" % type(arg))
        # handle what we can
        arg = signsimp(arg, evaluate=False)
        if arg.is_Mul:
            known = []
            unk = []
            for t in arg.args:
                tnew = cls(t)
                if isinstance(tnew, cls):
                    unk.append(tnew.args[0])
                else:
                    known.append(tnew)
            known = Mul(*known)
            unk = cls(Mul(*unk), evaluate=False) if unk else S.One
            return known*unk
        if arg is S.NaN:
            return S.NaN
        if arg is S.ComplexInfinity:
            return S.Infinity
        if arg.is_Pow:
            base, exponent = arg.as_base_exp()
            if base.is_real:
                if exponent.is_integer:
                    if exponent.is_even:
                        return arg
                    if base is S.NegativeOne:
                        return S.One
                    if isinstance(base, cls) and exponent is S.NegativeOne:
                        return arg
                    return Abs(base)**exponent
                if base.is_nonnegative:
                    return base**re(exponent)
                if base.is_negative:
                    return (-base)**re(exponent)*exp(-S.Pi*im(exponent))
                return
            elif not base.has(Symbol): # complex base
                # express base**exponent as exp(exponent*log(base))
                a, b = log(base).as_real_imag()
                z = a + I*b
                return exp(re(exponent*z))

        if isinstance(arg, exp):
            return exp(re(arg.args[0]))
        if isinstance(arg, AppliedUndef):
            return
        if arg.is_Add and arg.has(S.Infinity, S.NegativeInfinity):
            if any(a.is_infinite for a in arg.as_real_imag()):
                return S.Infinity
        if arg.is_zero:
            return S.Zero
        if arg.is_nonnegative:
            return arg
        if arg.is_nonpositive:
            return -arg
        if arg.is_imaginary:
            arg2 = -S.ImaginaryUnit * arg
            if arg2.is_nonnegative:
                return arg2
        # reject result if all new conjugates are just wrappers around
        # an expression that was already in the arg
        conj = signsimp(arg.conjugate(), evaluate=False)
        new_conj = conj.atoms(conjugate) - arg.atoms(conjugate)
        if new_conj and all(arg.has(i.args[0]) for i in new_conj):
            return
        if arg != conj and arg != -conj:
            ignore = arg.atoms(Abs)
            abs_free_arg = arg.xreplace({i: Dummy(real=True) for i in ignore})
            unk = [a for a in abs_free_arg.free_symbols if a.is_real is None]
            if not unk or not all(conj.has(conjugate(u)) for u in unk):
                return sqrt(expand_mul(arg*conj))
Example #30
0
def _minpoly_groebner(ex, x, cls):
    """
    Computes the minimal polynomial of an algebraic number
    using Groebner bases

    Examples
    ========

    >>> from sympy import minimal_polynomial, sqrt, Rational
    >>> from sympy.abc import x
    >>> minimal_polynomial(sqrt(2) + 3*Rational(1, 3), x, compose=False)
    x**2 - 2*x - 1

    """

    generator = numbered_symbols('a', cls=Dummy)
    mapping, symbols = {}, {}

    def update_mapping(ex, exp, base=None):
        a = next(generator)
        symbols[ex] = a

        if base is not None:
            mapping[ex] = a**exp + base
        else:
            mapping[ex] = exp.as_expr(a)

        return a

    def bottom_up_scan(ex):
        """
        Transform a given algebraic expression *ex* into a multivariate
        polynomial, by introducing fresh variables with defining equations.

        Explanation
        ===========

        The critical elements of the algebraic expression *ex* are root
        extractions, instances of :py:class:`~.AlgebraicNumber`, and negative
        powers.

        When we encounter a root extraction or an :py:class:`~.AlgebraicNumber`
        we replace this expression with a fresh variable ``a_i``, and record
        the defining polynomial for ``a_i``. For example, if ``a_0**(1/3)``
        occurs, we will replace it with ``a_1``, and record the new defining
        polynomial ``a_1**3 - a_0``.

        When we encounter a negative power we transform it into a positive
        power by algebraically inverting the base. This means computing the
        minimal polynomial in ``x`` for the base, inverting ``x`` modulo this
        poly (which generates a new polynomial) and then substituting the
        original base expression for ``x`` in this last polynomial.

        We return the transformed expression, and we record the defining
        equations for new symbols using the ``update_mapping()`` function.

        """
        if ex.is_Atom:
            if ex is S.ImaginaryUnit:
                if ex not in mapping:
                    return update_mapping(ex, 2, 1)
                else:
                    return symbols[ex]
            elif ex.is_Rational:
                return ex
        elif ex.is_Add:
            return Add(*[bottom_up_scan(g) for g in ex.args])
        elif ex.is_Mul:
            return Mul(*[bottom_up_scan(g) for g in ex.args])
        elif ex.is_Pow:
            if ex.exp.is_Rational:
                if ex.exp < 0:
                    minpoly_base = _minpoly_groebner(ex.base, x, cls)
                    inverse = invert(x, minpoly_base).as_expr()
                    base_inv = inverse.subs(x, ex.base).expand()

                    if ex.exp == -1:
                        return bottom_up_scan(base_inv)
                    else:
                        ex = base_inv**(-ex.exp)
                if not ex.exp.is_Integer:
                    base, exp = (ex.base**ex.exp.p).expand(), Rational(
                        1, ex.exp.q)
                else:
                    base, exp = ex.base, ex.exp
                base = bottom_up_scan(base)
                expr = base**exp

                if expr not in mapping:
                    if exp.is_Integer:
                        return expr.expand()
                    else:
                        return update_mapping(expr, 1 / exp, -base)
                else:
                    return symbols[expr]
        elif ex.is_AlgebraicNumber:
            if ex not in mapping:
                return update_mapping(ex, ex.minpoly_of_element())
            else:
                return symbols[ex]

        raise NotAlgebraic("%s doesn't seem to be an algebraic number" % ex)

    def simpler_inverse(ex):
        """
        Returns True if it is more likely that the minimal polynomial
        algorithm works better with the inverse
        """
        if ex.is_Pow:
            if (1 / ex.exp).is_integer and ex.exp < 0:
                if ex.base.is_Add:
                    return True
        if ex.is_Mul:
            hit = True
            for p in ex.args:
                if p.is_Add:
                    return False
                if p.is_Pow:
                    if p.base.is_Add and p.exp > 0:
                        return False

            if hit:
                return True
        return False

    inverted = False
    ex = expand_multinomial(ex)
    if ex.is_AlgebraicNumber:
        return ex.minpoly_of_element().as_expr(x)
    elif ex.is_Rational:
        result = ex.q * x - ex.p
    else:
        inverted = simpler_inverse(ex)
        if inverted:
            ex = ex**-1
        res = None
        if ex.is_Pow and (1 / ex.exp).is_Integer:
            n = 1 / ex.exp
            res = _minimal_polynomial_sq(ex.base, n, x)

        elif _is_sum_surds(ex):
            res = _minimal_polynomial_sq(ex, S.One, x)

        if res is not None:
            result = res

        if res is None:
            bus = bottom_up_scan(ex)
            F = [x - bus] + list(mapping.values())
            G = groebner(F, list(symbols.values()) + [x], order='lex')

            _, factors = factor_list(G[-1])
            # by construction G[-1] has root `ex`
            result = _choose_factor(factors, x, ex)
    if inverted:
        result = _invertx(result, x)
        if result.coeff(x**degree(result, x)) < 0:
            result = expand_mul(-result)

    return result
Example #31
0
def test_pinjoint_arbitrary_axis():
    theta, omega = dynamicsymbols('theta_J, omega_J')

    # When the bodies are attached though masscenters but axess are opposite.
    N, A, P, C = _generate_body()
    PinJoint('J', P, C, child_axis=-A.x)

    assert (-A.x).angle_between(N.x) == 0
    assert -A.x.express(N) == N.x
    assert A.dcm(N) == Matrix([[-1, 0, 0], [0, -cos(theta), -sin(theta)],
                               [0, -sin(theta), cos(theta)]])
    assert A.ang_vel_in(N) == omega * N.x
    assert A.ang_vel_in(N).magnitude() == sqrt(omega**2)
    assert C.masscenter.pos_from(P.masscenter) == 0
    assert C.masscenter.pos_from(P.masscenter).express(N).simplify() == 0
    assert C.masscenter.vel(N) == 0

    # When axes are different and parent joint is at masscenter but child joint
    # is at a unit vector from child masscenter.
    N, A, P, C = _generate_body()
    PinJoint('J', P, C, child_axis=A.y, child_joint_pos=A.x)

    assert A.y.angle_between(N.x) == 0  # Axis are aligned
    assert A.y.express(N) == N.x
    assert A.dcm(N) == Matrix([[0, -cos(theta), -sin(theta)], [1, 0, 0],
                               [0, -sin(theta), cos(theta)]])
    assert A.ang_vel_in(N) == omega * N.x
    assert A.ang_vel_in(N).express(A) == omega * A.y
    assert A.ang_vel_in(N).magnitude() == sqrt(omega**2)
    angle = A.ang_vel_in(N).angle_between(A.y)
    assert angle.xreplace({omega: 1}) == 0
    assert C.masscenter.vel(N) == omega * A.z
    assert C.masscenter.pos_from(P.masscenter) == -A.x
    assert (C.masscenter.pos_from(
        P.masscenter).express(N).simplify() == cos(theta) * N.y +
            sin(theta) * N.z)
    assert C.masscenter.vel(N).angle_between(A.x) == pi / 2

    # Similar to previous case but wrt parent body
    N, A, P, C = _generate_body()
    PinJoint('J', P, C, parent_axis=N.y, parent_joint_pos=N.x)

    assert N.y.angle_between(A.x) == 0  # Axis are aligned
    assert N.y.express(A) == A.x
    assert A.dcm(N) == Matrix([[0, 1, 0], [-cos(theta), 0,
                                           sin(theta)],
                               [sin(theta), 0, cos(theta)]])
    assert A.ang_vel_in(N) == omega * N.y
    assert A.ang_vel_in(N).express(A) == omega * A.x
    assert A.ang_vel_in(N).magnitude() == sqrt(omega**2)
    angle = A.ang_vel_in(N).angle_between(A.x)
    assert angle.xreplace({omega: 1}) == 0
    assert C.masscenter.vel(N).simplify() == -omega * N.z
    assert C.masscenter.pos_from(P.masscenter) == N.x

    # Both joint pos id defined but different axes
    N, A, P, C = _generate_body()
    PinJoint('J',
             P,
             C,
             parent_joint_pos=N.x,
             child_joint_pos=A.x,
             child_axis=A.x + A.y)
    assert expand_mul(N.x.angle_between(A.x + A.y)) == 0  # Axis are aligned
    assert (A.x + A.y).express(N).simplify() == sqrt(2) * N.x
    assert _simplify_matrix(A.dcm(N)) == Matrix(
        [[sqrt(2) / 2, -sqrt(2) * cos(theta) / 2, -sqrt(2) * sin(theta) / 2],
         [sqrt(2) / 2,
          sqrt(2) * cos(theta) / 2,
          sqrt(2) * sin(theta) / 2], [0, -sin(theta),
                                      cos(theta)]])
    assert A.ang_vel_in(N) == omega * N.x
    assert (
        A.ang_vel_in(N).express(A).simplify() == (omega * A.x + omega * A.y) /
        sqrt(2))
    assert A.ang_vel_in(N).magnitude() == sqrt(omega**2)
    angle = A.ang_vel_in(N).angle_between(A.x + A.y)
    assert angle.xreplace({omega: 1}) == 0
    assert C.masscenter.vel(N).simplify() == (omega * A.z) / sqrt(2)
    assert C.masscenter.pos_from(P.masscenter) == N.x - A.x
    assert (C.masscenter.pos_from(
        P.masscenter).express(N).simplify() == (1 - sqrt(2) / 2) * N.x +
            sqrt(2) * cos(theta) / 2 * N.y + sqrt(2) * sin(theta) / 2 * N.z)
    assert (C.masscenter.vel(N).express(N).simplify() == -sqrt(2) * omega *
            sin(theta) / 2 * N.y + sqrt(2) * omega * cos(theta) / 2 * N.z)
    assert C.masscenter.vel(N).angle_between(A.x) == pi / 2

    N, A, P, C = _generate_body()
    PinJoint('J',
             P,
             C,
             parent_joint_pos=N.x,
             child_joint_pos=A.x,
             child_axis=A.x + A.y - A.z)
    assert expand_mul(N.x.angle_between(A.x + A.y - A.z)) == 0  # Axis aligned
    assert (A.x + A.y - A.z).express(N).simplify() == sqrt(3) * N.x
    assert _simplify_matrix(A.dcm(N)) == Matrix(
        [[
            sqrt(3) / 3, -sqrt(6) * sin(theta + pi / 4) / 3,
            sqrt(6) * cos(theta + pi / 4) / 3
        ],
         [
             sqrt(3) / 3,
             sqrt(6) * cos(theta + pi / 12) / 3,
             sqrt(6) * sin(theta + pi / 12) / 3
         ],
         [
             -sqrt(3) / 3,
             sqrt(6) * cos(theta + 5 * pi / 12) / 3,
             sqrt(6) * sin(theta + 5 * pi / 12) / 3
         ]])
    assert A.ang_vel_in(N) == omega * N.x
    assert A.ang_vel_in(N).express(
        A).simplify() == (omega * A.x + omega * A.y - omega * A.z) / sqrt(3)
    assert A.ang_vel_in(N).magnitude() == sqrt(omega**2)
    angle = A.ang_vel_in(N).angle_between(A.x + A.y - A.z)
    assert angle.xreplace({omega: 1}) == 0
    assert C.masscenter.vel(N).simplify() == (omega * A.y +
                                              omega * A.z) / sqrt(3)
    assert C.masscenter.pos_from(P.masscenter) == N.x - A.x
    assert (C.masscenter.pos_from(
        P.masscenter).express(N).simplify() == (1 - sqrt(3) / 3) * N.x +
            sqrt(6) * sin(theta + pi / 4) / 3 * N.y -
            sqrt(6) * cos(theta + pi / 4) / 3 * N.z)
    assert (C.masscenter.vel(N).express(N).simplify() == sqrt(6) * omega *
            cos(theta + pi / 4) / 3 * N.y +
            sqrt(6) * omega * sin(theta + pi / 4) / 3 * N.z)
    assert C.masscenter.vel(N).angle_between(A.x) == pi / 2

    N, A, P, C = _generate_body()
    m, n = symbols('m n')
    PinJoint('J',
             P,
             C,
             parent_joint_pos=m * N.x,
             child_joint_pos=n * A.x,
             child_axis=A.x + A.y - A.z,
             parent_axis=N.x - N.y + N.z)
    angle = (N.x - N.y + N.z).angle_between(A.x + A.y - A.z)
    assert expand_mul(angle) == 0  # Axis are aligned
    assert ((
        A.x - A.y +
        A.z).express(N).simplify() == (-4 * cos(theta) / 3 - S(1) / 3) * N.x +
            (S(1) / 3 - 4 * sin(theta + pi / 6) / 3) * N.y +
            (4 * cos(theta + pi / 3) / 3 - S(1) / 3) * N.z)
    assert _simplify_matrix(A.dcm(N)) == Matrix(
        [[
            S(1) / 3 - 2 * cos(theta) / 3,
            -2 * sin(theta + pi / 6) / 3 - S(1) / 3,
            2 * cos(theta + pi / 3) / 3 + S(1) / 3
        ],
         [
             2 * cos(theta + pi / 3) / 3 + S(1) / 3,
             2 * cos(theta) / 3 - S(1) / 3,
             2 * sin(theta + pi / 6) / 3 + S(1) / 3
         ],
         [
             -2 * sin(theta + pi / 6) / 3 - S(1) / 3,
             2 * cos(theta + pi / 3) / 3 + S(1) / 3,
             2 * cos(theta) / 3 - S(1) / 3
         ]])
    assert A.ang_vel_in(N) == (omega * N.x - omega * N.y +
                               omega * N.z) / sqrt(3)
    assert A.ang_vel_in(N).express(
        A).simplify() == (omega * A.x + omega * A.y - omega * A.z) / sqrt(3)
    assert A.ang_vel_in(N).magnitude() == sqrt(omega**2)
    angle = A.ang_vel_in(N).angle_between(A.x + A.y - A.z)
    assert angle.xreplace({omega: 1}) == 0
    assert (
        C.masscenter.vel(N).simplify() == (m * omega * N.y + m * omega * N.z +
                                           n * omega * A.y + n * omega * A.z) /
        sqrt(3))
    assert C.masscenter.pos_from(P.masscenter) == m * N.x - n * A.x
    assert (C.masscenter.pos_from(
        P.masscenter).express(N).simplify() == (m + n *
                                                (2 * cos(theta) - 1) / 3) * N.x
            + n * (2 * sin(theta + pi / 6) + 1) / 3 * N.y - n *
            (2 * cos(theta + pi / 3) + 1) / 3 * N.z)
    assert (C.masscenter.vel(N).express(N).simplify() == -2 * n * omega *
            sin(theta) / 3 * N.x +
            (sqrt(3) * m + 2 * n * cos(theta + pi / 6)) * omega / 3 * N.y +
            (sqrt(3) * m + 2 * n * sin(theta + pi / 3)) * omega / 3 * N.z)
    assert expand_mul(C.masscenter.vel(N).angle_between(m * N.x -
                                                        n * A.x)) == pi / 2
Example #32
0
def minimal_polynomial(ex, x=None, **args):
    """
    Computes the minimal polynomial of an algebraic number.

    Examples
    ========

    >>> from sympy import minimal_polynomial, sqrt
    >>> from sympy.abc import x

    >>> minimal_polynomial(sqrt(2), x)
    x**2 - 2
    >>> minimal_polynomial(sqrt(2) + sqrt(3), x)
    x**4 - 10*x**2 + 1

    """
    from sympy.polys.polytools import degree
    from sympy.core.function import expand_mul
    from sympy.simplify.simplify import _is_sum_surds

    generator = numbered_symbols('a', cls=Dummy)
    mapping, symbols, replace = {}, {}, []

    ex = sympify(ex)

    if x is not None:
        x, cls = sympify(x), Poly
    else:
        x, cls = Dummy('x'), PurePoly

    def update_mapping(ex, exp, base=None):
        a = generator.next()
        symbols[ex] = a

        if base is not None:
            mapping[ex] = a**exp + base
        else:
            mapping[ex] = exp.as_expr(a)

        return a

    def bottom_up_scan(ex):
        if ex.is_Atom:
            if ex is S.ImaginaryUnit:
                if ex not in mapping:
                    return update_mapping(ex, 2, 1)
                else:
                    return symbols[ex]
            elif ex.is_Rational:
                return ex
        elif ex.is_Add:
            return Add(*[bottom_up_scan(g) for g in ex.args])
        elif ex.is_Mul:
            return Mul(*[bottom_up_scan(g) for g in ex.args])
        elif ex.is_Pow:
            if ex.exp.is_Rational:
                if ex.exp < 0 and ex.base.is_Add:
                    coeff, terms = ex.base.as_coeff_add()
                    elt, _ = primitive_element(terms, polys=True)

                    alg = ex.base - coeff

                    # XXX: turn this into eval()
                    inverse = invert(elt.gen + coeff, elt).as_expr()
                    base = inverse.subs(elt.gen, alg).expand()

                    if ex.exp == -1:
                        return bottom_up_scan(base)
                    else:
                        ex = base**(-ex.exp)
                if not ex.exp.is_Integer:
                    base, exp = (ex.base**ex.exp.p).expand(), Rational(
                        1, ex.exp.q)
                else:
                    base, exp = ex.base, ex.exp
                base = bottom_up_scan(base)
                expr = base**exp

                if expr not in mapping:
                    return update_mapping(expr, 1 / exp, -base)
                else:
                    return symbols[expr]
        elif ex.is_AlgebraicNumber:
            if ex.root not in mapping:
                return update_mapping(ex.root, ex.minpoly)
            else:
                return symbols[ex.root]

        raise NotAlgebraic("%s doesn't seem to be an algebraic number" % ex)

    def simpler_inverse(ex):
        """
        Returns True if it is more likely that the minimal polynomial
        algorithm works better with the inverse
        """
        if ex.is_Pow:
            if (1 / ex.exp).is_integer and ex.exp < 0:
                if ex.base.is_Add:
                    return True
        if ex.is_Mul:
            hit = True
            a = []
            for p in ex.args:
                if p.is_Add:
                    return False
                if p.is_Pow:
                    if p.base.is_Add and p.exp > 0:
                        return False

            if hit:
                return True
        return False

    polys = args.get('polys', False)
    prec = args.pop('prec', 10)

    inverted = False
    if ex.is_AlgebraicNumber:
        if not polys:
            return ex.minpoly.as_expr(x)
        else:
            return ex.minpoly.replace(x)
    elif ex.is_Rational:
        result = ex.q * x - ex.p
    else:
        inverted = simpler_inverse(ex)
        if inverted:
            ex = ex**-1
        res = None
        if ex.is_Pow and (1 / ex.exp).is_Integer:
            n = 1 / ex.exp
            res = _minimal_polynomial_sq(ex.base, n, x, prec)

        elif _is_sum_surds(ex):
            res = _minimal_polynomial_sq(ex, S.One, x, prec)

        if res is not None:
            result = res

        if res is None:
            bus = bottom_up_scan(ex)
            F = [x - bus] + mapping.values()
            G = groebner(F, symbols.values() + [x], order='lex')

            _, factors = factor_list(G[-1])
            # by construction G[-1] has root `ex`
            result = _choose_factor(factors, x, ex, prec)
            if result is None:
                raise NotImplementedError(
                    "multiple candidates for the minimal polynomial of %s" %
                    ex)
    if inverted:
        result = expand_mul(x**degree(result) * result.subs(x, 1 / x))
        if result.coeff(x**degree(result)) < 0:
            result = expand_mul(-result)
    if polys:
        return cls(result, x, field=True)
    else:
        return result
Example #33
0
def minimal_polynomial(ex, x=None, **args):
    """
    Computes the minimal polynomial of an algebraic number.

    Examples
    ========

    >>> from sympy import minimal_polynomial, sqrt
    >>> from sympy.abc import x

    >>> minimal_polynomial(sqrt(2), x)
    x**2 - 2
    >>> minimal_polynomial(sqrt(2) + sqrt(3), x)
    x**4 - 10*x**2 + 1

    """
    from sympy.polys.polytools import degree
    from sympy.core.function import expand_mul
    from sympy.simplify.simplify import _is_sum_surds

    generator = numbered_symbols('a', cls=Dummy)
    mapping, symbols, replace = {}, {}, []

    ex = sympify(ex)

    if x is not None:
        x, cls = sympify(x), Poly
    else:
        x, cls = Dummy('x'), PurePoly

    def update_mapping(ex, exp, base=None):
        a = generator.next()
        symbols[ex] = a

        if base is not None:
            mapping[ex] = a**exp + base
        else:
            mapping[ex] = exp.as_expr(a)

        return a

    def bottom_up_scan(ex):
        if ex.is_Atom:
            if ex is S.ImaginaryUnit:
                if ex not in mapping:
                    return update_mapping(ex, 2, 1)
                else:
                    return symbols[ex]
            elif ex.is_Rational:
                return ex
        elif ex.is_Add:
            return Add(*[ bottom_up_scan(g) for g in ex.args ])
        elif ex.is_Mul:
            return Mul(*[ bottom_up_scan(g) for g in ex.args ])
        elif ex.is_Pow:
            if ex.exp.is_Rational:
                if ex.exp < 0 and ex.base.is_Add:
                    coeff, terms = ex.base.as_coeff_add()
                    elt, _ = primitive_element(terms, polys=True)

                    alg = ex.base - coeff

                    # XXX: turn this into eval()
                    inverse = invert(elt.gen + coeff, elt).as_expr()
                    base = inverse.subs(elt.gen, alg).expand()

                    if ex.exp == -1:
                        return bottom_up_scan(base)
                    else:
                        ex = base**(-ex.exp)
                if not ex.exp.is_Integer:
                    base, exp = (
                        ex.base**ex.exp.p).expand(), Rational(1, ex.exp.q)
                else:
                    base, exp = ex.base, ex.exp
                base = bottom_up_scan(base)
                expr = base**exp

                if expr not in mapping:
                    return update_mapping(expr, 1/exp, -base)
                else:
                    return symbols[expr]
        elif ex.is_AlgebraicNumber:
            if ex.root not in mapping:
                return update_mapping(ex.root, ex.minpoly)
            else:
                return symbols[ex.root]

        raise NotAlgebraic("%s doesn't seem to be an algebraic number" % ex)

    def simpler_inverse(ex):
        """
        Returns True if it is more likely that the minimal polynomial
        algorithm works better with the inverse
        """
        if ex.is_Pow:
            if (1/ex.exp).is_integer and ex.exp < 0:
                if ex.base.is_Add:
                    return True
        if ex.is_Mul:
            hit = True
            a = []
            for p in ex.args:
                if p.is_Add:
                    return False
                if p.is_Pow:
                    if p.base.is_Add and p.exp > 0:
                        return False

            if hit:
                return True
        return False

    polys = args.get('polys', False)
    prec = args.pop('prec', 10)

    inverted = False
    if ex.is_AlgebraicNumber:
        if not polys:
            return ex.minpoly.as_expr(x)
        else:
            return ex.minpoly.replace(x)
    elif ex.is_Rational:
        result = ex.q*x - ex.p
    else:
        inverted = simpler_inverse(ex)
        if inverted:
            ex = ex**-1
        res = None
        if ex.is_Pow and (1/ex.exp).is_Integer:
            n = 1/ex.exp
            res = _minimal_polynomial_sq(ex.base, n, x, prec)

        elif _is_sum_surds(ex):
            res = _minimal_polynomial_sq(ex, S.One, x, prec)

        if res is not None:
            result = res

        if res is None:
            bus = bottom_up_scan(ex)
            F = [x - bus] + mapping.values()
            G = groebner(F, symbols.values() + [x], order='lex')

            _, factors = factor_list(G[-1])
            # by construction G[-1] has root `ex`
            result = _choose_factor(factors, x, ex, prec)
            if result is None:
                raise NotImplementedError("multiple candidates for the minimal polynomial of %s" % ex)
    if inverted:
        result = expand_mul(x**degree(result)*result.subs(x, 1/x))
        if result.coeff(x**degree(result)) < 0:
            result = expand_mul(-result)
    if polys:
        return cls(result, x, field=True)
    else:
        return result
Example #34
0
    def _eval_expand_multinomial(self, **hints):
        """(a+b+..) ** n -> a**n + n*a**(n-1)*b + .., n is nonzero integer"""
        b = self.base
        e = self.exp

        if b is None:
            base = self.base
        else:
            base = b

        if e is None:
            exp = self.exp
        else:
            exp = e

        if e is not None or b is not None:
            result = Pow(base, exp)

            if result.is_Pow:
                base, exp = result.base, result.exp
            else:
                return result
        else:
            result = None

        if exp.is_Rational and exp.p > 0 and base.is_Add:
            if not exp.is_Integer:
                n = Integer(exp.p // exp.q)

                if not n:
                    return Pow(base, exp)
                else:
                    radical, result = Pow(base, exp - n), []

                    expanded_base_n = Pow(base, n)
                    if expanded_base_n.is_Pow:
                        expanded_base_n = \
                            expanded_base_n._eval_expand_multinomial()
                    for term in Add.make_args(expanded_base_n):
                        result.append(term * radical)

                    return Add(*result)

            n = int(exp)

            if base.is_commutative:
                order_terms, other_terms = [], []

                for order in base.args:
                    if order.is_Order:
                        order_terms.append(order)
                    else:
                        other_terms.append(order)

                if order_terms:
                    # (f(x) + O(x^n))^m -> f(x)^m + m*f(x)^{m-1} *O(x^n)
                    f = Add(*other_terms)

                    if n == 2:
                        return expand_multinomial(f**n, deep=False) + \
                            n*f*Add(*order_terms)
                    else:
                        g = expand_multinomial(f**(n - 1), deep=False)
                        return expand_mul(f*g, deep=False) + \
                            n*g*Add(*order_terms)

                if base.is_number:
                    # Efficiently expand expressions of the form (a + b*I)**n
                    # where 'a' and 'b' are real numbers and 'n' is integer.
                    a, b = base.as_real_imag()

                    if a.is_Rational and b.is_Rational:
                        if not a.is_Integer:
                            if not b.is_Integer:
                                k = Pow(a.q * b.q, n)
                                a, b = a.p * b.q, a.q * b.p
                            else:
                                k = Pow(a.q, n)
                                a, b = a.p, a.q * b
                        elif not b.is_Integer:
                            k = Pow(b.q, n)
                            a, b = a * b.q, b.p
                        else:
                            k = 1

                        a, b, c, d = int(a), int(b), 1, 0

                        while n:
                            if n & 1:
                                c, d = a * c - b * d, b * c + a * d
                                n -= 1
                            a, b = a * a - b * b, 2 * a * b
                            n //= 2

                        I = S.ImaginaryUnit

                        if k == 1:
                            return c + I * d
                        else:
                            return Integer(c) / k + I * d / k

                p = other_terms
                # (x+y)**3 -> x**3 + 3*x**2*y + 3*x*y**2 + y**3
                # in this particular example:
                # p = [x,y]; n = 3
                # so now it's easy to get the correct result -- we get the
                # coefficients first:
                from sympy import multinomial_coefficients
                expansion_dict = multinomial_coefficients(len(p), n)
                # in our example: {(3, 0): 1, (1, 2): 3, (0, 3): 1, (2, 1): 3}
                # and now construct the expression.

                # An elegant way would be to use Poly, but unfortunately it is
                # slower than the direct method below, so it is commented out:
                #b = {}
                #for k in expansion_dict:
                #    b[k] = Integer(expansion_dict[k])
                #return Poly(b, *p).as_expr()

                from sympy.polys.polyutils import basic_from_dict
                result = basic_from_dict(expansion_dict, *p)
                return result
            else:
                if n == 2:
                    return Add(*[f * g for f in base.args for g in base.args])
                else:
                    multi = (base**(n - 1))._eval_expand_multinomial()
                    if multi.is_Add:
                        return Add(
                            *[f * g for f in base.args for g in multi.args])
                    else:
                        return Add(*[f * multi for f in base.args])
        elif (exp.is_Rational and exp.p < 0 and base.is_Add
              and abs(exp.p) > exp.q):
            return 1 / Pow(base, -exp)._eval_expand_multinomial()
        elif exp.is_Add and base.is_Number:
            #  a + b      a  b
            # n      --> n  n  , where n, a, b are Numbers

            coeff, tail = S.One, S.Zero

            for term in exp.args:
                if term.is_Number:
                    coeff *= Pow(base, term)
                else:
                    tail += term

            return coeff * Pow(base, tail)
        else:
            return result
Example #35
0
    def _eval_nseries(self, x, n, logx):
        # NOTE! This function is an important part of the gruntz algorithm
        #       for computing limits. It has to return a generalized power
        #       series with coefficients in C(log, log(x)). In more detail:
        # It has to return an expression
        #     c_0*x**e_0 + c_1*x**e_1 + ... (finitely many terms)
        # where e_i are numbers (not necessarily integers) and c_i are
        # expressions involving only numbers, the log function, and log(x).
        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 expand_multinomial(self.func(
                    b._eval_nseries(x, n=n, logx=logx), e),
                                          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"

                nuse = n
                cf = 1

                try:
                    ord = b.as_leading_term(x)
                    cf = C.Order(ord, x).getn()
                    if cf and cf.is_Number:
                        nuse = n + 2 * ceiling(cf)
                    else:
                        cf = 1
                except NotImplementedError:
                    pass

                b_orig, prefactor = b, O(1, x)
                while prefactor.is_Order:
                    nuse += 1
                    b = b_orig._eval_nseries(x, n=nuse, logx=logx)
                    prefactor = b.as_leading_term(x)

                # express "rest" as: rest = 1 + k*x**l + ... + O(x**n)
                rest = expand_mul((b - prefactor) / prefactor)

                if rest.is_Order:
                    return 1 / prefactor + rest / prefactor + O(x**n, x)

                k, l = rest.leadterm(x)
                if l.is_Rational and l > 0:
                    pass
                elif l.is_number and l > 0:
                    l = l.evalf()
                elif l == 0:
                    k = k.simplify()
                    if k == 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)
                    else:
                        raise NotImplementedError()
                else:
                    raise NotImplementedError()

                if cf < 0:
                    cf = S.One / abs(cf)

                try:
                    dn = C.Order(1 / prefactor, x).getn()
                    if dn and dn < 0:
                        pass
                    else:
                        dn = 0
                except NotImplementedError:
                    dn = 0

                terms = [1 / prefactor]
                for m in xrange(1, ceiling((n - dn) / l * cf)):
                    new_term = terms[-1] * (-rest)
                    if new_term.is_Pow:
                        new_term = new_term._eval_expand_multinomial(
                            deep=False)
                    else:
                        new_term = expand_mul(new_term, deep=False)
                    terms.append(new_term)
                terms.append(O(x**n, x))
                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:
                nuse, denominator = n, O(1, x)
                while denominator.is_Order:
                    denominator = (b**(-e))._eval_nseries(x, n=nuse, logx=logx)
                    nuse += 1
                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, logx=logx)

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

        # see if the base is as simple as possible
        bx = b
        while bx.is_Pow and bx.exp.is_Rational:
            bx = bx.base
        if bx == x:
            return self

        # 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)
            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

            if e.is_real and e.is_positive:
                lt = b.as_leading_term(x)

                # Try to correct nuse (= m) guess from:
                # (lt + rest + O(x**m))**e =
                # lt**e*(1 + rest/lt + O(x**m)/lt)**e =
                # lt**e + ... + O(x**m)*lt**(e - 1) = ... + O(x**n)
                try:
                    cf = C.Order(lt, x).getn()
                    nuse = ceiling(n - cf * (e - 1))
                except NotImplementedError:
                    pass

            bs = b._eval_nseries(x, n=nuse, logx=logx)
            terms = bs.removeO()
            if terms.is_Add:
                bs = terms
                lt = terms.as_leading_term(x)

                # bs -> lt + rest -> lt*(1 + (bs/lt - 1))
                return ((self.func(lt, e) * self.func(
                    (bs / lt).expand(), e).nseries(
                        x, n=nuse, logx=logx)).expand() + order)

            if bs.is_Add:
                from sympy import O
                # So, bs + O() == terms
                c = Dummy('c')
                res = []
                for arg in bs.args:
                    if arg.is_Order:
                        arg = c * arg.expr
                    res.append(arg)
                bs = Add(*res)
                rv = (bs**e).series(x).subs(c, O(1, x))
                rv += order
                return rv

            rv = bs**e
            if terms != bs:
                rv += order
            return rv

        # 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 non-polynomial 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, logx=logx)
                l.append(g)
            r = Add(*l)
        return expand_mul(r * b0**e) + order
def sincos_to_sum(expr):

    if not expr.has(cos, sin):
        return expr
    else:
        return TR8(expand_mul(TRpower(expr)))
Example #37
0
def convolution_subset(a, b):
    """
    Performs Subset Convolution of given sequences.

    The indices of each argument, considered as bit strings, correspond to
    subsets of a finite set.

    The sequence is automatically padded to the right with zeros, as the
    definition of subset based on bitmasks (indices) requires the size of
    sequence to be a power of 2.

    Parameters
    ==========

    a, b : iterables
        The sequences for which convolution is performed.

    Examples
    ========

    >>> from sympy import symbols, S
    >>> from sympy.discrete.convolutions import convolution_subset
    >>> u, v, x, y, z = symbols('u v x y z')

    >>> convolution_subset([u, v], [x, y])
    [u*x, u*y + v*x]
    >>> convolution_subset([u, v, x], [y, z])
    [u*y, u*z + v*y, x*y, x*z]

    >>> convolution_subset([1, S(2)/3], [3, 4])
    [3, 6]
    >>> convolution_subset([1, 3, S(5)/7], [7])
    [7, 21, 5, 0]

    References
    ==========

    .. [1] https://people.csail.mit.edu/rrw/presentations/subset-conv.pdf

    """

    if not a or not b:
        return []

    if not iterable(a) or not iterable(b):
        raise TypeError("Expected a sequence of coefficients for convolution")

    a = [sympify(arg) for arg in a]
    b = [sympify(arg) for arg in b]
    n = max(len(a), len(b))

    if n&(n - 1): # not a power of 2
        n = 2**n.bit_length()

    # padding with zeros
    a += [S.Zero]*(n - len(a))
    b += [S.Zero]*(n - len(b))

    c = [S.Zero]*n

    for mask in range(n):
        smask = mask
        while smask > 0:
            c[mask] += expand_mul(a[smask] * b[mask^smask])
            smask = (smask - 1)&mask

        c[mask] += expand_mul(a[smask] * b[mask^smask])

    return c
Example #38
0
def periodicity(f, symbol, check=False):
    """
    Tests the given function for periodicity in the given symbol.

    Parameters
    ==========

    f : :py:class:`~.Expr`.
        The concerned function.
    symbol : :py:class:`~.Symbol`
        The variable for which the period is to be determined.
    check : bool, optional
        The flag to verify whether the value being returned is a period or not.

    Returns
    =======

    period
        The period of the function is returned.
        ``None`` is returned when the function is aperiodic or has a complex period.
        The value of $0$ is returned as the period of a constant function.

    Raises
    ======

    NotImplementedError
        The value of the period computed cannot be verified.


    Notes
    =====

    Currently, we do not support functions with a complex period.
    The period of functions having complex periodic values such
    as ``exp``, ``sinh`` is evaluated to ``None``.

    The value returned might not be the "fundamental" period of the given
    function i.e. it may not be the smallest periodic value of the function.

    The verification of the period through the ``check`` flag is not reliable
    due to internal simplification of the given expression. Hence, it is set
    to ``False`` by default.

    Examples
    ========
    >>> from sympy import periodicity, Symbol, sin, cos, tan, exp
    >>> x = Symbol('x')
    >>> f = sin(x) + sin(2*x) + sin(3*x)
    >>> periodicity(f, x)
    2*pi
    >>> periodicity(sin(x)*cos(x), x)
    pi
    >>> periodicity(exp(tan(2*x) - 1), x)
    pi/2
    >>> periodicity(sin(4*x)**cos(2*x), x)
    pi
    >>> periodicity(exp(x), x)
    """
    if symbol.kind is not NumberKind:
        raise NotImplementedError("Cannot use symbol of kind %s" % symbol.kind)
    temp = Dummy('x', real=True)
    f = f.subs(symbol, temp)
    symbol = temp

    def _check(orig_f, period):
        '''Return the checked period or raise an error.'''
        new_f = orig_f.subs(symbol, symbol + period)
        if new_f.equals(orig_f):
            return period
        else:
            raise NotImplementedError(
                filldedent('''
                The period of the given function cannot be verified.
                When `%s` was replaced with `%s + %s` in `%s`, the result
                was `%s` which was not recognized as being the same as
                the original function.
                So either the period was wrong or the two forms were
                not recognized as being equal.
                Set check=False to obtain the value.''' %
                           (symbol, symbol, period, orig_f, new_f)))

    orig_f = f
    period = None

    if isinstance(f, Relational):
        f = f.lhs - f.rhs

    f = f.simplify()

    if symbol not in f.free_symbols:
        return S.Zero

    if isinstance(f, TrigonometricFunction):
        try:
            period = f.period(symbol)
        except NotImplementedError:
            pass

    if isinstance(f, Abs):
        arg = f.args[0]
        if isinstance(arg, (sec, csc, cos)):
            # all but tan and cot might have a
            # a period that is half as large
            # so recast as sin
            arg = sin(arg.args[0])
        period = periodicity(arg, symbol)
        if period is not None and isinstance(arg, sin):
            # the argument of Abs was a trigonometric other than
            # cot or tan; test to see if the half-period
            # is valid. Abs(arg) has behaviour equivalent to
            # orig_f, so use that for test:
            orig_f = Abs(arg)
            try:
                return _check(orig_f, period / 2)
            except NotImplementedError as err:
                if check:
                    raise NotImplementedError(err)
            # else let new orig_f and period be
            # checked below

    if isinstance(f, exp) or (f.is_Pow and f.base == S.Exp1):
        f = Pow(S.Exp1, expand_mul(f.exp))
        if im(f) != 0:
            period_real = periodicity(re(f), symbol)
            period_imag = periodicity(im(f), symbol)
            if period_real is not None and period_imag is not None:
                period = lcim([period_real, period_imag])

    if f.is_Pow and f.base != S.Exp1:
        base, expo = f.args
        base_has_sym = base.has(symbol)
        expo_has_sym = expo.has(symbol)

        if base_has_sym and not expo_has_sym:
            period = periodicity(base, symbol)

        elif expo_has_sym and not base_has_sym:
            period = periodicity(expo, symbol)

        else:
            period = _periodicity(f.args, symbol)

    elif f.is_Mul:
        coeff, g = f.as_independent(symbol, as_Add=False)
        if isinstance(g, TrigonometricFunction) or coeff is not S.One:
            period = periodicity(g, symbol)

        else:
            period = _periodicity(g.args, symbol)

    elif f.is_Add:
        k, g = f.as_independent(symbol)
        if k is not S.Zero:
            return periodicity(g, symbol)

        period = _periodicity(g.args, symbol)

    elif isinstance(f, Mod):
        a, n = f.args

        if a == symbol:
            period = n
        elif isinstance(a, TrigonometricFunction):
            period = periodicity(a, symbol)
        #check if 'f' is linear in 'symbol'
        elif (a.is_polynomial(symbol) and degree(a, symbol) == 1
              and symbol not in n.free_symbols):
            period = Abs(n / a.diff(symbol))

    elif isinstance(f, Piecewise):
        pass  # not handling Piecewise yet as the return type is not favorable

    elif period is None:
        from sympy.solvers.decompogen import compogen, decompogen
        g_s = decompogen(f, symbol)
        num_of_gs = len(g_s)
        if num_of_gs > 1:
            for index, g in enumerate(reversed(g_s)):
                start_index = num_of_gs - 1 - index
                g = compogen(g_s[start_index:], symbol)
                if g not in (orig_f, f):  # Fix for issue 12620
                    period = periodicity(g, symbol)
                    if period is not None:
                        break

    if period is not None:
        if check:
            return _check(orig_f, period)
        return period

    return None
Example #39
0
    def eval(cls, arg):
        from sympy.simplify.simplify import signsimp
        from sympy.core.function import expand_mul
        from sympy.core.power import Pow

        if hasattr(arg, '_eval_Abs'):
            obj = arg._eval_Abs()
            if obj is not None:
                return obj
        if not isinstance(arg, Expr):
            raise TypeError("Bad argument type for Abs(): %s" % type(arg))
        # handle what we can
        arg = signsimp(arg, evaluate=False)
        n, d = arg.as_numer_denom()
        if d.free_symbols and not n.free_symbols:
            return cls(n) / cls(d)

        if arg.is_Mul:
            known = []
            unk = []
            for t in arg.args:
                if t.is_Pow and t.exp.is_integer and t.exp.is_negative:
                    bnew = cls(t.base)
                    if isinstance(bnew, cls):
                        unk.append(t)
                    else:
                        known.append(Pow(bnew, t.exp))
                else:
                    tnew = cls(t)
                    if isinstance(tnew, cls):
                        unk.append(t)
                    else:
                        known.append(tnew)
            known = Mul(*known)
            unk = cls(Mul(*unk), evaluate=False) if unk else S.One
            return known * unk
        if arg is S.NaN:
            return S.NaN
        if arg is S.ComplexInfinity:
            return S.Infinity
        if arg.is_Pow:
            base, exponent = arg.as_base_exp()
            if base.is_extended_real:
                if exponent.is_integer:
                    if exponent.is_even:
                        return arg
                    if base is S.NegativeOne:
                        return S.One
                    return Abs(base)**exponent
                if base.is_extended_nonnegative:
                    return base**re(exponent)
                if base.is_extended_negative:
                    return (-base)**re(exponent) * exp(-S.Pi * im(exponent))
                return
            elif not base.has(Symbol):  # complex base
                # express base**exponent as exp(exponent*log(base))
                a, b = log(base).as_real_imag()
                z = a + I * b
                return exp(re(exponent * z))
        if isinstance(arg, exp):
            return exp(re(arg.args[0]))
        if isinstance(arg, AppliedUndef):
            return
        if arg.is_Add and arg.has(S.Infinity, S.NegativeInfinity):
            if any(a.is_infinite for a in arg.as_real_imag()):
                return S.Infinity
        if arg.is_zero:
            return S.Zero
        if arg.is_extended_nonnegative:
            return arg
        if arg.is_extended_nonpositive:
            return -arg
        if arg.is_imaginary:
            arg2 = -S.ImaginaryUnit * arg
            if arg2.is_extended_nonnegative:
                return arg2
        # reject result if all new conjugates are just wrappers around
        # an expression that was already in the arg
        conj = signsimp(arg.conjugate(), evaluate=False)
        new_conj = conj.atoms(conjugate) - arg.atoms(conjugate)
        if new_conj and all(arg.has(i.args[0]) for i in new_conj):
            return
        if arg != conj and arg != -conj:
            ignore = arg.atoms(Abs)
            abs_free_arg = arg.xreplace({i: Dummy(real=True) for i in ignore})
            unk = [
                a for a in abs_free_arg.free_symbols
                if a.is_extended_real is None
            ]
            if not unk or not all(conj.has(conjugate(u)) for u in unk):
                return sqrt(expand_mul(arg * conj))
Example #40
0
def _mexpand(expr):
    return expand_mul(expand_multinomial(expr))
Example #41
0
def solve_univariate_inequality(expr,
                                gen,
                                relational=True,
                                domain=S.Reals,
                                continuous=False):
    """Solves a real univariate inequality.

    Parameters
    ==========

    expr : Relational
        The target inequality
    gen : Symbol
        The variable for which the inequality is solved
    relational : bool
        A Relational type output is expected or not
    domain : Set
        The domain over which the equation is solved
    continuous: bool
        True if expr is known to be continuous over the given domain
        (and so continuous_domain() doesn't need to be called on it)

    Raises
    ======

    NotImplementedError
        The solution of the inequality cannot be determined due to limitation
        in `solvify`.

    Notes
    =====

    Currently, we cannot solve all the inequalities due to limitations in
    `solvify`. Also, the solution returned for trigonometric inequalities
    are restricted in its periodic interval.

    See Also
    ========

    solvify: solver returning solveset solutions with solve's output API

    Examples
    ========

    >>> from sympy.solvers.inequalities import solve_univariate_inequality
    >>> from sympy import Symbol, sin, Interval, S
    >>> x = Symbol('x')

    >>> solve_univariate_inequality(x**2 >= 4, x)
    ((2 <= x) & (x < oo)) | ((x <= -2) & (-oo < x))

    >>> solve_univariate_inequality(x**2 >= 4, x, relational=False)
    Union(Interval(-oo, -2), Interval(2, oo))

    >>> domain = Interval(0, S.Infinity)
    >>> solve_univariate_inequality(x**2 >= 4, x, False, domain)
    Interval(2, oo)

    >>> solve_univariate_inequality(sin(x) > 0, x, relational=False)
    Interval.open(0, pi)

    """
    from sympy import im
    from sympy.calculus.util import (continuous_domain, periodicity,
                                     function_range)
    from sympy.solvers.solvers import denoms
    from sympy.solvers.solveset import solvify, solveset

    # This keeps the function independent of the assumptions about `gen`.
    # `solveset` makes sure this function is called only when the domain is
    # real.
    _gen = gen
    _domain = domain
    if gen.is_extended_real is False:
        rv = S.EmptySet
        return rv if not relational else rv.as_relational(_gen)
    elif gen.is_extended_real is None:
        gen = Dummy('gen', extended_real=True)
        try:
            expr = expr.xreplace({_gen: gen})
        except TypeError:
            raise TypeError(
                filldedent('''
                When gen is real, the relational has a complex part
                which leads to an invalid comparison like I < 0.
                '''))

    rv = None

    if expr is S.true:
        rv = domain

    elif expr is S.false:
        rv = S.EmptySet

    else:
        e = expr.lhs - expr.rhs
        period = periodicity(e, gen)
        if period == S.Zero:
            e = expand_mul(e)
            const = expr.func(e, 0)
            if const is S.true:
                rv = domain
            elif const is S.false:
                rv = S.EmptySet
        elif period is not None:
            frange = function_range(e, gen, domain)

            rel = expr.rel_op
            if rel == '<' or rel == '<=':
                if expr.func(frange.sup, 0):
                    rv = domain
                elif not expr.func(frange.inf, 0):
                    rv = S.EmptySet

            elif rel == '>' or rel == '>=':
                if expr.func(frange.inf, 0):
                    rv = domain
                elif not expr.func(frange.sup, 0):
                    rv = S.EmptySet

            inf, sup = domain.inf, domain.sup
            if sup - inf is S.Infinity:
                domain = Interval(0, period, False, True)

        if rv is None:
            n, d = e.as_numer_denom()
            try:
                if gen not in n.free_symbols and len(e.free_symbols) > 1:
                    raise ValueError
                # this might raise ValueError on its own
                # or it might give None...
                solns = solvify(e, gen, domain)
                if solns is None:
                    # in which case we raise ValueError
                    raise ValueError
            except (ValueError, NotImplementedError):
                # replace gen with generic x since it's
                # univariate anyway
                raise NotImplementedError(
                    filldedent('''
                    The inequality, %s, cannot be solved using
                    solve_univariate_inequality.
                    ''' % expr.subs(gen, Symbol('x'))))

            expanded_e = expand_mul(e)

            def valid(x):
                # this is used to see if gen=x satisfies the
                # relational by substituting it into the
                # expanded form and testing against 0, e.g.
                # if expr = x*(x + 1) < 2 then e = x*(x + 1) - 2
                # and expanded_e = x**2 + x - 2; the test is
                # whether a given value of x satisfies
                # x**2 + x - 2 < 0
                #
                # expanded_e, expr and gen used from enclosing scope
                v = expanded_e.subs(gen, expand_mul(x))
                try:
                    r = expr.func(v, 0)
                except TypeError:
                    r = S.false
                if r in (S.true, S.false):
                    return r
                if v.is_extended_real is False:
                    return S.false
                else:
                    v = v.n(2)
                    if v.is_comparable:
                        return expr.func(v, 0)
                    # not comparable or couldn't be evaluated
                    raise NotImplementedError(
                        'relationship did not evaluate: %s' % r)

            singularities = []
            for d in denoms(expr, gen):
                singularities.extend(solvify(d, gen, domain))
            if not continuous:
                domain = continuous_domain(expanded_e, gen, domain)

            include_x = '=' in expr.rel_op and expr.rel_op != '!='

            try:
                discontinuities = set(domain.boundary -
                                      FiniteSet(domain.inf, domain.sup))
                # remove points that are not between inf and sup of domain
                critical_points = FiniteSet(
                    *(solns + singularities +
                      list(discontinuities))).intersection(
                          Interval(domain.inf, domain.sup, domain.inf
                                   not in domain, domain.sup not in domain))
                if all(r.is_number for r in critical_points):
                    reals = _nsort(critical_points, separated=True)[0]
                else:
                    sifted = sift(critical_points,
                                  lambda x: x.is_extended_real)
                    if sifted[None]:
                        # there were some roots that weren't known
                        # to be real
                        raise NotImplementedError
                    try:
                        reals = sifted[True]
                        if len(reals) > 1:
                            reals = list(sorted(reals))
                    except TypeError:
                        raise NotImplementedError
            except NotImplementedError:
                raise NotImplementedError(
                    'sorting of these roots is not supported')

            # If expr contains imaginary coefficients, only take real
            # values of x for which the imaginary part is 0
            make_real = S.Reals
            if im(expanded_e) != S.Zero:
                check = True
                im_sol = FiniteSet()
                try:
                    a = solveset(im(expanded_e), gen, domain)
                    if not isinstance(a, Interval):
                        for z in a:
                            if z not in singularities and valid(
                                    z) and z.is_extended_real:
                                im_sol += FiniteSet(z)
                    else:
                        start, end = a.inf, a.sup
                        for z in _nsort(critical_points + FiniteSet(end)):
                            valid_start = valid(start)
                            if start != end:
                                valid_z = valid(z)
                                pt = _pt(start, z)
                                if pt not in singularities and pt.is_extended_real and valid(
                                        pt):
                                    if valid_start and valid_z:
                                        im_sol += Interval(start, z)
                                    elif valid_start:
                                        im_sol += Interval.Ropen(start, z)
                                    elif valid_z:
                                        im_sol += Interval.Lopen(start, z)
                                    else:
                                        im_sol += Interval.open(start, z)
                            start = z
                        for s in singularities:
                            im_sol -= FiniteSet(s)
                except (TypeError):
                    im_sol = S.Reals
                    check = False

                if isinstance(im_sol, EmptySet):
                    raise ValueError(
                        filldedent('''
                        %s contains imaginary parts which cannot be
                        made 0 for any value of %s satisfying the
                        inequality, leading to relations like I < 0.
                        ''' % (expr.subs(gen, _gen), _gen)))

                make_real = make_real.intersect(im_sol)

            sol_sets = [S.EmptySet]

            start = domain.inf
            if valid(start) and start.is_finite:
                sol_sets.append(FiniteSet(start))

            for x in reals:
                end = x

                if valid(_pt(start, end)):
                    sol_sets.append(Interval(start, end, True, True))

                if x in singularities:
                    singularities.remove(x)
                else:
                    if x in discontinuities:
                        discontinuities.remove(x)
                        _valid = valid(x)
                    else:  # it's a solution
                        _valid = include_x
                    if _valid:
                        sol_sets.append(FiniteSet(x))

                start = end

            end = domain.sup
            if valid(end) and end.is_finite:
                sol_sets.append(FiniteSet(end))

            if valid(_pt(start, end)):
                sol_sets.append(Interval.open(start, end))

            if im(expanded_e) != S.Zero and check:
                rv = (make_real).intersect(_domain)
            else:
                rv = Intersection((Union(*sol_sets)), make_real,
                                  _domain).subs(gen, _gen)

    return rv if not relational else rv.as_relational(_gen)
Example #42
0
def _is_zero_after_expand_mul(x):
    """Tests by expand_mul only, suitable for polynomials and rational
    functions."""
    return expand_mul(x) == 0
Example #43
0
def intersecting_product(a, b):
    """
    Returns the intersecting product of given sequences.

    The indices of each argument, considered as bit strings, correspond to
    subsets of a finite set.

    The intersecting product of given sequences is the sequence which
    contains the sum of products of the elements of the given sequences
    grouped by the *bitwise-AND* of the corresponding indices.

    The sequence is automatically padded to the right with zeros, as the
    definition of subset based on bitmasks (indices) requires the size of
    sequence to be a power of 2.

    Parameters
    ==========

    a, b : iterables
        The sequences for which intersecting product is to be obtained.

    Examples
    ========

    >>> from sympy import symbols, S, I, intersecting_product
    >>> u, v, x, y, z = symbols('u v x y z')

    >>> intersecting_product([u, v], [x, y])
    [u*x + u*y + v*x, v*y]
    >>> intersecting_product([u, v, x], [y, z])
    [u*y + u*z + v*y + x*y + x*z, v*z, 0, 0]

    >>> intersecting_product([1, S(2)/3], [3, 4 + 5*I])
    [9 + 5*I, 8/3 + 10*I/3]
    >>> intersecting_product([1, 3, S(5)/7], [7, 8])
    [327/7, 24, 0, 0]

    References
    ==========

    .. [1] https://people.csail.mit.edu/rrw/presentations/subset-conv.pdf

    """

    if not a or not b:
        return []

    a, b = a[:], b[:]
    n = max(len(a), len(b))

    if n&(n - 1): # not a power of 2
        n = 2**n.bit_length()

    # padding with zeros
    a += [S.Zero]*(n - len(a))
    b += [S.Zero]*(n - len(b))

    a, b = mobius_transform(a, subset=False), mobius_transform(b, subset=False)
    a = [expand_mul(x*y) for x, y in zip(a, b)]
    a = inverse_mobius_transform(a, subset=False)

    return a
Example #44
0
def minimal_polynomial(ex, x=None, compose=True, polys=False, domain=None):
    """
    Computes the minimal polynomial of an algebraic element.

    Parameters
    ==========

    ex : Expr
        Element or expression whose minimal polynomial is to be calculated.

    x : Symbol, optional
        Independent variable of the minimal polynomial

    compose : boolean, optional (default=True)
        Method to use for computing minimal polynomial. If ``compose=True``
        (default) then ``_minpoly_compose`` is used, if ``compose=False`` then
        groebner bases are used.

    polys : boolean, optional (default=False)
        If ``True`` returns a ``Poly`` object else an ``Expr`` object.

    domain : Domain, optional
        Ground domain

    Notes
    =====

    By default ``compose=True``, the minimal polynomial of the subexpressions of ``ex``
    are computed, then the arithmetic operations on them are performed using the resultant
    and factorization.
    If ``compose=False``, a bottom-up algorithm is used with ``groebner``.
    The default algorithm stalls less frequently.

    If no ground domain is given, it will be generated automatically from the expression.

    Examples
    ========

    >>> from sympy import minimal_polynomial, sqrt, solve, QQ
    >>> from sympy.abc import x, y

    >>> minimal_polynomial(sqrt(2), x)
    x**2 - 2
    >>> minimal_polynomial(sqrt(2), x, domain=QQ.algebraic_field(sqrt(2)))
    x - sqrt(2)
    >>> minimal_polynomial(sqrt(2) + sqrt(3), x)
    x**4 - 10*x**2 + 1
    >>> minimal_polynomial(solve(x**3 + x + 3)[0], x)
    x**3 + x + 3
    >>> minimal_polynomial(sqrt(y), x)
    x**2 - y

    """

    ex = sympify(ex)
    if ex.is_number:
        # not sure if it's always needed but try it for numbers (issue 8354)
        ex = _mexpand(ex, recursive=True)
    for expr in preorder_traversal(ex):
        if expr.is_AlgebraicNumber:
            compose = False
            break

    if x is not None:
        x, cls = sympify(x), Poly
    else:
        x, cls = Dummy('x'), PurePoly

    if not domain:
        if ex.free_symbols:
            domain = FractionField(QQ, list(ex.free_symbols))
        else:
            domain = QQ
    if hasattr(domain, 'symbols') and x in domain.symbols:
        raise GeneratorsError("the variable %s is an element of the ground "
                              "domain %s" % (x, domain))

    if compose:
        result = _minpoly_compose(ex, x, domain)
        result = result.primitive()[1]
        c = result.coeff(x**degree(result, x))
        if c.is_negative:
            result = expand_mul(-result)
        return cls(result, x, field=True) if polys else result.collect(x)

    if not domain.is_QQ:
        raise NotImplementedError("groebner method only works for QQ")

    result = _minpoly_groebner(ex, x, cls)
    return cls(result, x, field=True) if polys else result.collect(x)
Example #45
0
def convolution_subset(a, b):
    """
    Performs Subset Convolution of given sequences.

    The indices of each argument, considered as bit strings, correspond to
    subsets of a finite set.

    The sequence is automatically padded to the right with zeros, as the
    definition of subset based on bitmasks (indices) requires the size of
    sequence to be a power of 2.

    Parameters
    ==========

    a, b : iterables
        The sequences for which convolution is performed.

    Examples
    ========

    >>> from sympy import symbols, S, I
    >>> from sympy.discrete.convolutions import convolution_subset
    >>> u, v, x, y, z = symbols('u v x y z')

    >>> convolution_subset([u, v], [x, y])
    [u*x, u*y + v*x]
    >>> convolution_subset([u, v, x], [y, z])
    [u*y, u*z + v*y, x*y, x*z]

    >>> convolution_subset([1, S(2)/3], [3, 4])
    [3, 6]
    >>> convolution_subset([1, 3, S(5)/7], [7])
    [7, 21, 5, 0]

    References
    ==========

    .. [1] https://people.csail.mit.edu/rrw/presentations/subset-conv.pdf

    """

    if not a or not b:
        return []

    if not iterable(a) or not iterable(b):
        raise TypeError("Expected a sequence of coefficients for convolution")

    a = [sympify(arg) for arg in a]
    b = [sympify(arg) for arg in b]
    n = max(len(a), len(b))

    if n&(n - 1): # not a power of 2
        n = 2**n.bit_length()

    # padding with zeros
    a += [S.Zero]*(n - len(a))
    b += [S.Zero]*(n - len(b))

    c = [S.Zero]*n

    for mask in range(n):
        smask = mask
        while smask > 0:
            c[mask] += expand_mul(a[smask] * b[mask^smask])
            smask = (smask - 1)&mask

        c[mask] += expand_mul(a[smask] * b[mask^smask])

    return c
Example #46
0
def _linear_neq_order1_type4(match_):
    r"""
    System of n first-order nonconstant-coefficient linear non-homogeneous differential equations

    .. math::
        X' = A(t) X + f(t)

    where $X$ is the vector of $n$ dependent variables, $t$ is the dependent variable, $X'$
    is the first order differential of $X$ with respect to $t$ and $A(t)$ is a $n \times n$
    coefficient matrix.

    Let us define $B$ as antiderivative of coefficient matrix $A$:

    .. math::
        B(t) = \int A(t) dt

    If the system of ODEs defined above is such that its antiderivative $B(t)$ commutes with
    $A(t)$ itself, then, the solution of the above system is given as:

    .. math::
        X = e^{B(t)} ( \int e^{-B(t)} f(t) \,dt + C)

    where $C$ is the vector of constants.

    """
    # Some parts of code is repeated, this needs to be taken care of
    # The constant vector obtained here can be done so in the match
    # function itself.
    eq = match_['eq']
    func = match_['func']
    fc = match_['func_coeff']
    b = match_['rhs']
    n = len(eq)
    t = list(list(eq[0].atoms(Derivative))[0].atoms(Symbol))[0]
    constants = numbered_symbols(prefix='C', cls=Symbol, start=1)

    # This needs to be modified in future so that fc is only of type Matrix
    M = -fc if type(fc) is Matrix else Matrix(n, n,
                                              lambda i, j: -fc[i, func[j], 0])

    Cvect = Matrix(list(next(constants) for _ in range(n)))

    # The code in if block will be removed when it is made sure
    # that the code works without the statements in if block.
    if "commutative_antiderivative" not in match_:
        B, is_commuting = _is_commutative_anti_derivative(M, t)

        # This course is subject to change
        if not is_commuting:
            return None

    else:
        B = match_['commutative_antiderivative']

    sol_vector = B.exp() * ((
        (-B).exp() * b).applyfunc(lambda x: Integral(x, t)) + Cvect)
    sol_vector = [
        collect(expand_mul(s), ordered(sol_vector.atoms(exp)), exact=True)
        for s in sol_vector
    ]

    sol_dict = [Eq(func[i], sol_vector[i]) for i in range(n)]
    return sol_dict
Example #47
0
def test_acsch():
    x = Symbol('x')

    assert unchanged(acsch, x)
    assert acsch(-x) == -acsch(x)

    # values at fixed points
    assert acsch(1) == log(1 + sqrt(2))
    assert acsch(-1) == -log(1 + sqrt(2))
    assert acsch(0) is zoo
    assert acsch(2) == log((1 + sqrt(5)) / 2)
    assert acsch(-2) == -log((1 + sqrt(5)) / 2)

    assert acsch(I) == -I * pi / 2
    assert acsch(-I) == I * pi / 2
    assert acsch(-I * (sqrt(6) + sqrt(2))) == I * pi / 12
    assert acsch(I * (sqrt(2) + sqrt(6))) == -I * pi / 12
    assert acsch(-I * (1 + sqrt(5))) == I * pi / 10
    assert acsch(I * (1 + sqrt(5))) == -I * pi / 10
    assert acsch(-I * 2 / sqrt(2 - sqrt(2))) == I * pi / 8
    assert acsch(I * 2 / sqrt(2 - sqrt(2))) == -I * pi / 8
    assert acsch(-I * 2) == I * pi / 6
    assert acsch(I * 2) == -I * pi / 6
    assert acsch(-I * sqrt(2 + 2 / sqrt(5))) == I * pi / 5
    assert acsch(I * sqrt(2 + 2 / sqrt(5))) == -I * pi / 5
    assert acsch(-I * sqrt(2)) == I * pi / 4
    assert acsch(I * sqrt(2)) == -I * pi / 4
    assert acsch(-I * (sqrt(5) - 1)) == 3 * I * pi / 10
    assert acsch(I * (sqrt(5) - 1)) == -3 * I * pi / 10
    assert acsch(-I * 2 / sqrt(3)) == I * pi / 3
    assert acsch(I * 2 / sqrt(3)) == -I * pi / 3
    assert acsch(-I * 2 / sqrt(2 + sqrt(2))) == 3 * I * pi / 8
    assert acsch(I * 2 / sqrt(2 + sqrt(2))) == -3 * I * pi / 8
    assert acsch(-I * sqrt(2 - 2 / sqrt(5))) == 2 * I * pi / 5
    assert acsch(I * sqrt(2 - 2 / sqrt(5))) == -2 * I * pi / 5
    assert acsch(-I * (sqrt(6) - sqrt(2))) == 5 * I * pi / 12
    assert acsch(I * (sqrt(6) - sqrt(2))) == -5 * I * pi / 12
    assert acsch(nan) is nan

    # properties
    # acsch(x) == asinh(1/x)
    assert acsch(-I * sqrt(2)) == asinh(I / sqrt(2))
    assert acsch(-I * 2 / sqrt(3)) == asinh(I * sqrt(3) / 2)

    # acsch(x) == -I*asin(I/x)
    assert acsch(-I * sqrt(2)) == -I * asin(-1 / sqrt(2))
    assert acsch(-I * 2 / sqrt(3)) == -I * asin(-sqrt(3) / 2)

    # csch(acsch(x)) / x == 1
    assert expand_mul(
        csch(acsch(-I * (sqrt(6) + sqrt(2)))) / (-I *
                                                 (sqrt(6) + sqrt(2)))) == 1
    assert expand_mul(csch(acsch(I * (1 + sqrt(5)))) / (I *
                                                        (1 + sqrt(5)))) == 1
    assert (csch(acsch(I * sqrt(2 - 2 / sqrt(5)))) /
            (I * sqrt(2 - 2 / sqrt(5)))).simplify() == 1
    assert (csch(acsch(-I * sqrt(2 - 2 / sqrt(5)))) /
            (-I * sqrt(2 - 2 / sqrt(5)))).simplify() == 1

    # numerical evaluation
    assert str(acsch(5 * I + 1).n(6)) == '0.0391819 - 0.193363*I'
    assert str(acsch(-5 * I + 1).n(6)) == '0.0391819 + 0.193363*I'
Example #48
0
def linodesolve(A, t, b=None, B=None, type="auto", doit=False):
    r"""
    System of n equations linear first-order differential equations

    Explanation
    ===========

    This solver solves the system of ODEs of the follwing form:

    .. math::
        X'(t) = A(t) X(t) +  b(t)

    Here, $A(t)$ is the coefficient matrix, $X(t)$ is the vector of n independent variables,
    $b(t)$ is the non-homogeneous term and $X'(t)$ is the derivative of $X(t)$

    Depending on the properties of $A(t)$ and $b(t)$, this solver evaluates the solution
    differently.

    When $A(t)$ is constant coefficient matrix and $b(t)$ is zero vector i.e. system is homogeneous,
    the solution is:

    .. math::
        X(t) = \exp(A t) C

    Here, $C$ is a vector of constants and $A$ is the constant coefficient matrix.

    When $A(t)$ is constant coefficient matrix and $b(t)$ is non-zero i.e. system is non-homogeneous,
    the solution is:

    .. math::
        X(t) = e^{A t} ( \int e^{- A t} b \,dt + C)

    When $A(t)$ is coefficient matrix such that its commutative with its antiderivative $B(t)$ and
    $b(t)$ is a zero vector i.e. system is homogeneous, the solution is:

    .. math::
        X(t) = \exp(B(t)) C

    When $A(t)$ is commutative with its antiderivative $B(t)$ and $b(t)$ is non-zero i.e. system is
    non-homogeneous, the solution is:

    .. math::
        X(t) =  e^{B(t)} ( \int e^{-B(t)} b(t) \,dt + C)

    The final solution is the general solution for all the four equations since a constant coefficient
    matrix is always commutative with its antidervative.

    Parameters
    ==========

    A : Matrix
        Coefficient matrix of the system of linear first order ODEs.
    t : Symbol
        Independent variable in the system of ODEs.
    b : Matrix or None
        Non-homogeneous term in the system of ODEs. If None is passed,
        a homogeneous system of ODEs is assumed.
    B : Matrix or None
        Antiderivative of the coefficient matrix. If the antiderivative
        is not passed and the solution requires the term, then the solver
        would compute it internally.
    type : String
        Type of the system of ODEs passed. Depending on the type, the
        solution is evaluated. The type values allowed and the corresponding
        system it solves are: "type1" for constant coefficient homogeneous
        "type2" for constant coefficient non-homogeneous, "type3" for non-constant
        coefficient homogeneous and "type4" for non-constant coefficient non-homogeneous.
        The default value is "auto" which will let the solver decide the correct type of
        the system passed.
    doit : Boolean
        Evaluate the solution if True, default value is False

    Examples
    ========

    To solve the system of ODEs using this function directly, several things must be
    done in the right order. Wrong inputs to the function will lead to incorrect results.

    >>> from sympy import symbols, Function, Eq
    >>> from sympy.solvers.ode.systems import canonical_odes, linear_ode_to_matrix, linodesolve, linodesolve_type
    >>> from sympy.solvers.ode.subscheck import checkodesol
    >>> f, g = symbols("f, g", cls=Function)
    >>> x, a = symbols("x, a")
    >>> funcs = [f(x), g(x)]
    >>> eqs = [Eq(f(x).diff(x) - f(x), a*g(x) + 1), Eq(g(x).diff(x) + g(x), a*f(x))]

    Here, it is important to note that before we derive the coefficient matrix, it is
    important to get the system of ODEs into the desired form. For that we will use
    :obj:`sympy.solvers.ode.systems.canonical_odes()`.

    >>> eqs = canonical_odes(eqs, funcs, x)
    >>> eqs
    [[Eq(Derivative(f(x), x), a*g(x) + f(x) + 1), Eq(Derivative(g(x), x), a*f(x) - g(x))]]

    Now, we will use :obj:`sympy.solvers.ode.systems.linear_ode_to_matrix()` to get the coefficient matrix and the
    non-homogeneous term if it is there.

    >>> eqs = eqs[0]
    >>> (A1, A0), b = linear_ode_to_matrix(eqs, funcs, x, 1)
    >>> A = A0

    We have the coefficient matrices and the non-homogeneous term ready. Now, we can use
    :obj:`sympy.solvers.ode.systems.linodesolve_type()` to get the information for the system of ODEs
    to finally pass it to the solver.

    >>> system_info = linodesolve_type(A, x, b=b)
    >>> sol_vector = linodesolve(A, x, b=b, B=system_info['antiderivative'], type=system_info['type'])

    Now, we can prove if the solution is correct or not by using :obj:`sympy.solvers.ode.checkodesol()`

    >>> sol = [Eq(f, s) for f, s in zip(funcs, sol_vector)]
    >>> checkodesol(eqs, sol)
    (True, [0, 0])

    We can also use the doit method to evaluate the solutions passed by the function.

    >>> sol_vector_evaluated = linodesolve(A, x, b=b, type="type2", doit=True)

    Now, we will look at a system of ODEs which is non-constant.

    >>> eqs = [Eq(f(x).diff(x), f(x) + x*g(x)), Eq(g(x).diff(x), -x*f(x) + g(x))]

    The system defined above is already in the desired form, so we don't have to convert it.

    >>> (A1, A0), b = linear_ode_to_matrix(eqs, funcs, x, 1)
    >>> A = A0

    A user can also pass the commutative antiderivative required for type3 and type4 system of ODEs.
    Passing an incorrect one will lead to incorrect results. If the coefficient matrix is not commutative
    with its antiderivative, then :obj:`sympy.solvers.ode.systems.linodesolve_type()` raises a NotImplementedError.
    If it does have a commutative antiderivative, then the function just returns the information about the system.

    >>> system_info = linodesolve_type(A, x, b=b)

    Now, we can pass the antiderivative as an argument to get the solution. If the system information is not
    passed, then the solver will compute the required arguments internally.

    >>> sol_vector = linodesolve(A, x, b=b)

    Once again, we can verify the solution obtained.

    >>> sol = [Eq(f, s) for f, s in zip(funcs, sol_vector)]
    >>> checkodesol(eqs, sol)
    (True, [0, 0])

    Returns
    =======

    List

    Raises
    ======

    ValueError
        This error is raised when the coefficient matrix, non-homogeneous term
        or the antiderivative, if passed, aren't a matrix or
        don't have correct dimensions
    NonSquareMatrixError
        When the coefficient matrix or its antiderivative, if passed isn't a square
        matrix
    NotImplementedError
        If the coefficient matrix doesn't have a commutative antiderivative

    See Also
    ========

    linear_ode_to_matrix: Coefficient matrix computation function
    canonical_odes: System of ODEs representation change
    linodesolve_type: Getting information about systems of ODEs to pass in this solver

    """

    if not isinstance(A, MatrixBase):
        raise ValueError(
            filldedent('''\
            The coefficients of the system of ODEs should be of type Matrix
        '''))

    if not A.is_square:
        raise NonSquareMatrixError(
            filldedent('''\
            The coefficient matrix must be a square
        '''))

    if b is not None:
        if not isinstance(b, MatrixBase):
            raise ValueError(
                filldedent('''\
                The non-homogeneous terms of the system of ODEs should be of type Matrix
            '''))

        if A.rows != b.rows:
            raise ValueError(
                filldedent('''\
                The system of ODEs should have the same number of non-homogeneous terms and the number of
                equations
            '''))

    if B is not None:
        if not isinstance(B, MatrixBase):
            raise ValueError(
                filldedent('''\
                The antiderivative of coefficients of the system of ODEs should be of type Matrix
            '''))

        if not B.is_square:
            raise NonSquareMatrixError(
                filldedent('''\
                The antiderivative of the coefficient matrix must be a square
            '''))

        if A.rows != B.rows:
            raise ValueError(
                filldedent('''\
                        The coefficient matrix and its antiderivative should have same dimensions
                    '''))

    if not any(type == "type{}".format(i)
               for i in range(1, 5)) and not type == "auto":
        raise ValueError(
            filldedent('''\
                    The input type should be a valid one
                '''))

    n = A.rows

    # constants = numbered_symbols(prefix='C', cls=Dummy, start=const_idx+1)
    Cvect = Matrix(list(Dummy() for _ in range(n)))

    if (type == "type2" or type == "type4") and b is None:
        b = zeros(n, 1)

    if type == "auto":
        system_info = linodesolve_type(A, t, b=b)
        type = system_info["type"]
        B = system_info["antiderivative"]

    if type == "type1" or type == "type2":
        P, J = matrix_exp_jordan_form(A, t)
        P = simplify(P)

        if type == "type1":
            sol_vector = P * (J * Cvect)
        else:
            sol_vector = P * J * (
                (J.inv() * P.inv() * b).applyfunc(lambda x: Integral(x, t)) +
                Cvect)

    else:
        if B is None:
            B, _ = _is_commutative_anti_derivative(A, t)

        if type == "type3":
            sol_vector = B.exp() * Cvect
        else:
            sol_vector = B.exp() * ((
                (-B).exp() * b).applyfunc(lambda x: Integral(x, t)) + Cvect)

    gens = sol_vector.atoms(exp)

    if type != "type1":
        sol_vector = [expand_mul(s) for s in sol_vector]

    sol_vector = [collect(s, ordered(gens), exact=True) for s in sol_vector]

    if doit:
        sol_vector = [s.doit() for s in sol_vector]

    return sol_vector
Example #49
0
def solve_univariate_inequality(expr, gen, relational=True, domain=S.Reals, continuous=False):
    """Solves a real univariate inequality.

    Parameters
    ==========

    expr : Relational
        The target inequality
    gen : Symbol
        The variable for which the inequality is solved
    relational : bool
        A Relational type output is expected or not
    domain : Set
        The domain over which the equation is solved
    continuous: bool
        True if expr is known to be continuous over the given domain
        (and so continuous_domain() doesn't need to be called on it)

    Raises
    ======

    NotImplementedError
        The solution of the inequality cannot be determined due to limitation
        in `solvify`.

    Notes
    =====

    Currently, we cannot solve all the inequalities due to limitations in
    `solvify`. Also, the solution returned for trigonometric inequalities
    are restricted in its periodic interval.

    See Also
    ========

    solvify: solver returning solveset solutions with solve's output API

    Examples
    ========

    >>> from sympy.solvers.inequalities import solve_univariate_inequality
    >>> from sympy import Symbol, sin, Interval, S
    >>> x = Symbol('x')

    >>> solve_univariate_inequality(x**2 >= 4, x)
    ((2 <= x) & (x < oo)) | ((x <= -2) & (-oo < x))

    >>> solve_univariate_inequality(x**2 >= 4, x, relational=False)
    Union(Interval(-oo, -2), Interval(2, oo))

    >>> domain = Interval(0, S.Infinity)
    >>> solve_univariate_inequality(x**2 >= 4, x, False, domain)
    Interval(2, oo)

    >>> solve_univariate_inequality(sin(x) > 0, x, relational=False)
    Interval.open(0, pi)

    """
    from sympy import im
    from sympy.calculus.util import (continuous_domain, periodicity,
        function_range)
    from sympy.solvers.solvers import denoms
    from sympy.solvers.solveset import solveset_real, solvify, solveset
    from sympy.solvers.solvers import solve

    # This keeps the function independent of the assumptions about `gen`.
    # `solveset` makes sure this function is called only when the domain is
    # real.
    _gen = gen
    _domain = domain
    if gen.is_real is False:
        rv = S.EmptySet
        return rv if not relational else rv.as_relational(_gen)
    elif gen.is_real is None:
        gen = Dummy('gen', real=True)
        try:
            expr = expr.xreplace({_gen: gen})
        except TypeError:
            raise TypeError(filldedent('''
When gen is real, the relational has a complex part
which leads to an invalid comparison like I < 0.
            '''))

    rv = None

    if expr is S.true:
        rv = domain

    elif expr is S.false:
        rv = S.EmptySet

    else:
        e = expr.lhs - expr.rhs
        period = periodicity(e, gen)
        if period is not None:
            frange = function_range(e, gen, domain)

            rel = expr.rel_op
            if rel == '<' or rel == '<=':
                if expr.func(frange.sup, 0):
                    rv = domain
                elif not expr.func(frange.inf, 0):
                    rv = S.EmptySet

            elif rel == '>' or rel == '>=':
                if expr.func(frange.inf, 0):
                    rv = domain
                elif not expr.func(frange.sup, 0):
                    rv = S.EmptySet

            inf, sup = domain.inf, domain.sup
            if sup - inf is S.Infinity:
                domain = Interval(0, period, False, True)

        if rv is None:
            n, d = e.as_numer_denom()
            try:
                if gen not in n.free_symbols and len(e.free_symbols) > 1:
                    raise ValueError
                # this might raise ValueError on its own
                # or it might give None...
                solns = solvify(e, gen, domain)
                if solns is None:
                    # in which case we raise ValueError
                    raise ValueError
            except (ValueError, NotImplementedError):
                raise NotImplementedError(filldedent('''
The inequality cannot be solved using solve_univariate_inequality.
                        '''))

            expanded_e = expand_mul(e)
            def valid(x):
                # this is used to see if gen=x satisfies the
                # relational by substituting it into the
                # expanded form and testing against 0, e.g.
                # if expr = x*(x + 1) < 2 then e = x*(x + 1) - 2
                # and expanded_e = x**2 + x - 2; the test is
                # whether a given value of x satisfies
                # x**2 + x - 2 < 0
                #
                # expanded_e, expr and gen used from enclosing scope
                v = expanded_e.subs(gen, x)
                try:
                    r = expr.func(v, 0)
                except TypeError:
                    r = S.false
                if r in (S.true, S.false):
                    return r
                if v.is_real is False:
                    return S.false
                else:
                    v = v.n(2)
                    if v.is_comparable:
                        return expr.func(v, 0)
                    # not comparable or couldn't be evaluated
                    raise NotImplementedError(
                        'relationship did not evaluate: %s' % r)

            singularities = []
            for d in denoms(expr, gen):
                singularities.extend(solvify(d, gen, domain))
            if not continuous:
                domain = continuous_domain(e, gen, domain)

            include_x = '=' in expr.rel_op and expr.rel_op != '!='

            try:
                discontinuities = set(domain.boundary -
                    FiniteSet(domain.inf, domain.sup))
                # remove points that are not between inf and sup of domain
                critical_points = FiniteSet(*(solns + singularities + list(
                    discontinuities))).intersection(
                    Interval(domain.inf, domain.sup,
                    domain.inf not in domain, domain.sup not in domain))
                if all(r.is_number for r in critical_points):
                    reals = _nsort(critical_points, separated=True)[0]
                else:
                    from sympy.utilities.iterables import sift
                    sifted = sift(critical_points, lambda x: x.is_real)
                    if sifted[None]:
                        # there were some roots that weren't known
                        # to be real
                        raise NotImplementedError
                    try:
                        reals = sifted[True]
                        if len(reals) > 1:
                            reals = list(sorted(reals))
                    except TypeError:
                        raise NotImplementedError
            except NotImplementedError:
                raise NotImplementedError('sorting of these roots is not supported')

            #If expr contains imaginary coefficients
            #Only real values of x for which the imaginary part is 0 are taken
            make_real = S.Reals
            if im(expanded_e) != S.Zero:
                check = True
                im_sol = FiniteSet()
                try:
                    a = solveset(im(expanded_e), gen, domain)
                    if not isinstance(a, Interval):
                        for z in a:
                            if z not in singularities and valid(z) and z.is_real:
                                im_sol += FiniteSet(z)
                    else:
                        start, end = a.inf, a.sup
                        for z in _nsort(critical_points + FiniteSet(end)):
                            valid_start = valid(start)
                            if start != end:
                                valid_z = valid(z)
                                pt = _pt(start, z)
                                if pt not in singularities and pt.is_real and valid(pt):
                                    if valid_start and valid_z:
                                        im_sol += Interval(start, z)
                                    elif valid_start:
                                        im_sol += Interval.Ropen(start, z)
                                    elif valid_z:
                                        im_sol += Interval.Lopen(start, z)
                                    else:
                                        im_sol += Interval.open(start, z)
                            start = z
                        for s in singularities:
                            im_sol -= FiniteSet(s)
                except (TypeError):
                    im_sol = S.Reals
                    check = False

                if isinstance(im_sol, EmptySet):
                    raise ValueError(filldedent('''
%s contains imaginary parts which cannot be made 0 for any value of %s
satisfying the inequality, leading to relations like I < 0.
                '''  % (expr.subs(gen, _gen), _gen)))

                make_real = make_real.intersect(im_sol)

            empty = sol_sets = [S.EmptySet]

            start = domain.inf
            if valid(start) and start.is_finite:
                sol_sets.append(FiniteSet(start))

            for x in reals:
                end = x

                if valid(_pt(start, end)):
                    sol_sets.append(Interval(start, end, True, True))

                if x in singularities:
                    singularities.remove(x)
                else:
                    if x in discontinuities:
                        discontinuities.remove(x)
                        _valid = valid(x)
                    else:  # it's a solution
                        _valid = include_x
                    if _valid:
                        sol_sets.append(FiniteSet(x))

                start = end

            end = domain.sup
            if valid(end) and end.is_finite:
                sol_sets.append(FiniteSet(end))

            if valid(_pt(start, end)):
                sol_sets.append(Interval.open(start, end))

            if im(expanded_e) != S.Zero and check:
                rv = (make_real).intersect(_domain)
            else:
                rv = Intersection(
                    (Union(*sol_sets)), make_real, _domain).subs(gen, _gen)

    return rv if not relational else rv.as_relational(_gen)
Example #50
0
    def eval(cls, arg):
        from sympy.simplify.simplify import signsimp
        from sympy.core.function import expand_mul

        if hasattr(arg, '_eval_Abs'):
            obj = arg._eval_Abs()
            if obj is not None:
                return obj
        if not isinstance(arg, Expr):
            raise TypeError("Bad argument type for Abs(): %s" % type(arg))
        # handle what we can
        arg = signsimp(arg, evaluate=False)
        if arg.is_Mul:
            known = []
            unk = []
            for t in arg.args:
                tnew = cls(t)
                if tnew.func is cls:
                    unk.append(tnew.args[0])
                else:
                    known.append(tnew)
            known = Mul(*known)
            unk = cls(Mul(*unk), evaluate=False) if unk else S.One
            return known * unk
        if arg is S.NaN:
            return S.NaN
        if arg.is_Pow:
            base, exponent = arg.as_base_exp()
            if base.is_real:
                if exponent.is_integer:
                    if exponent.is_even:
                        return arg
                    if base is S.NegativeOne:
                        return S.One
                    if base.func is cls and exponent is S.NegativeOne:
                        return arg
                    return Abs(base)**exponent
                if base.is_positive == True:
                    return base**re(exponent)
                return (-base)**re(exponent) * exp(-S.Pi * im(exponent))
        if isinstance(arg, exp):
            return exp(re(arg.args[0]))
        if isinstance(arg, AppliedUndef):
            return
        if arg.is_Add and arg.has(S.Infinity, S.NegativeInfinity):
            if any(a.is_infinite for a in arg.as_real_imag()):
                return S.Infinity
        if arg.is_zero:
            return S.Zero
        if arg.is_nonnegative:
            return arg
        if arg.is_nonpositive:
            return -arg
        if arg.is_imaginary:
            arg2 = -S.ImaginaryUnit * arg
            if arg2.is_nonnegative:
                return arg2
        # reject result if all new conjugates are just wrappers around
        # an expression that was already in the arg
        conj = arg.conjugate()
        new_conj = conj.atoms(conjugate) - arg.atoms(conjugate)
        if new_conj and all(arg.has(i.args[0]) for i in new_conj):
            return
        if arg != conj and arg != -conj:
            ignore = arg.atoms(Abs)
            abs_free_arg = arg.xreplace(
                dict([(i, Dummy(real=True)) for i in ignore]))
            unk = [a for a in abs_free_arg.free_symbols if a.is_real is None]
            if not unk or not all(conj.has(conjugate(u)) for u in unk):
                return sqrt(expand_mul(arg * conj))
Example #51
0
    def _eval_nseries(self, x, n, logx):
        # NOTE! This function is an important part of the gruntz algorithm
        #       for computing limits. It has to return a generalized power
        #       series with coefficients in C(log, log(x)). In more detail:
        # It has to return an expression
        #     c_0*x**e_0 + c_1*x**e_1 + ... (finitely many terms)
        # where e_i are numbers (not necessarily integers) and c_i are
        # expressions involving only numbers, the log function, and log(x).
        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 expand_multinomial(self.func(b._eval_nseries(x, n=n,
                    logx=logx), e), 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"

                nuse = n
                cf = 1

                try:
                    ord = b.as_leading_term(x)
                    cf = C.Order(ord, x).getn()
                    if cf and cf.is_Number:
                        nuse = n + 2*ceiling(cf)
                    else:
                        cf = 1
                except NotImplementedError:
                    pass

                b_orig, prefactor = b, O(1, x)
                while prefactor.is_Order:
                    nuse += 1
                    b = b_orig._eval_nseries(x, n=nuse, logx=logx)
                    prefactor = b.as_leading_term(x)

                # express "rest" as: rest = 1 + k*x**l + ... + O(x**n)
                rest = expand_mul((b - prefactor)/prefactor)

                if rest.is_Order:
                    return 1/prefactor + rest/prefactor + O(x**n, x)

                k, l = rest.leadterm(x)
                if l.is_Rational and l > 0:
                    pass
                elif l.is_number and l > 0:
                    l = l.evalf()
                elif l == 0:
                    k = k.simplify()
                    if k == 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)
                    else:
                        raise NotImplementedError()
                else:
                    raise NotImplementedError()

                if cf < 0:
                    cf = S.One/abs(cf)

                try:
                    dn = C.Order(1/prefactor, x).getn()
                    if dn and dn < 0:
                        pass
                    else:
                        dn = 0
                except NotImplementedError:
                    dn = 0

                terms = [1/prefactor]
                for m in xrange(1, ceiling((n - dn)/l*cf)):
                    new_term = terms[-1]*(-rest)
                    if new_term.is_Pow:
                        new_term = new_term._eval_expand_multinomial(
                            deep=False)
                    else:
                        new_term = expand_mul(new_term, deep=False)
                    terms.append(new_term)
                terms.append(O(x**n, x))
                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:
                nuse, denominator = n, O(1, x)
                while denominator.is_Order:
                    denominator = (b**(-e))._eval_nseries(x, n=nuse, logx=logx)
                    nuse += 1
                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, logx=logx)

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

        # see if the base is as simple as possible
        bx = b
        while bx.is_Pow and bx.exp.is_Rational:
            bx = bx.base
        if bx == x:
            return self

        # 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)
            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

            if e.is_real and e.is_positive:
                lt = b.as_leading_term(x)

                # Try to correct nuse (= m) guess from:
                # (lt + rest + O(x**m))**e =
                # lt**e*(1 + rest/lt + O(x**m)/lt)**e =
                # lt**e + ... + O(x**m)*lt**(e - 1) = ... + O(x**n)
                try:
                    cf = C.Order(lt, x).getn()
                    nuse = ceiling(n - cf*(e - 1))
                except NotImplementedError:
                    pass

            bs = b._eval_nseries(x, n=nuse, logx=logx)
            terms = bs.removeO()
            if terms.is_Add:
                bs = terms
                lt = terms.as_leading_term(x)

                # bs -> lt + rest -> lt*(1 + (bs/lt - 1))
                return ((self.func(lt, e) * self.func((bs/lt).expand(), e).nseries(
                    x, n=nuse, logx=logx)).expand() + order)

            if bs.is_Add:
                from sympy import O
                # So, bs + O() == terms
                c = Dummy('c')
                res = []
                for arg in bs.args:
                    if arg.is_Order:
                        arg = c*arg.expr
                    res.append(arg)
                bs = Add(*res)
                rv = (bs**e).series(x).subs(c, O(1, x))
                rv += order
                return rv

            rv = bs**e
            if terms != bs:
                rv += order
            return rv

        # 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 non-polynomial 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, logx=logx)
                l.append(g)
            r = Add(*l)
        return expand_mul(r*b0**e) + order
Example #52
0
def radsimp(expr, symbolic=True, max_terms=4):
    r"""
    Rationalize the denominator by removing square roots.

    Explanation
    ===========

    The expression returned from radsimp must be used with caution
    since if the denominator contains symbols, it will be possible to make
    substitutions that violate the assumptions of the simplification process:
    that for a denominator matching a + b*sqrt(c), a != +/-b*sqrt(c). (If
    there are no symbols, this assumptions is made valid by collecting terms
    of sqrt(c) so the match variable ``a`` does not contain ``sqrt(c)``.) If
    you do not want the simplification to occur for symbolic denominators, set
    ``symbolic`` to False.

    If there are more than ``max_terms`` radical terms then the expression is
    returned unchanged.

    Examples
    ========

    >>> from sympy import radsimp, sqrt, Symbol, pprint
    >>> from sympy import factor_terms, fraction, signsimp
    >>> from sympy.simplify.radsimp import collect_sqrt
    >>> from sympy.abc import a, b, c

    >>> radsimp(1/(2 + sqrt(2)))
    (2 - sqrt(2))/2
    >>> x,y = map(Symbol, 'xy')
    >>> e = ((2 + 2*sqrt(2))*x + (2 + sqrt(8))*y)/(2 + sqrt(2))
    >>> radsimp(e)
    sqrt(2)*(x + y)

    No simplification beyond removal of the gcd is done. One might
    want to polish the result a little, however, by collecting
    square root terms:

    >>> r2 = sqrt(2)
    >>> r5 = sqrt(5)
    >>> ans = radsimp(1/(y*r2 + x*r2 + a*r5 + b*r5)); pprint(ans)
        ___       ___       ___       ___
      \/ 5 *a + \/ 5 *b - \/ 2 *x - \/ 2 *y
    ------------------------------------------
       2               2      2              2
    5*a  + 10*a*b + 5*b  - 2*x  - 4*x*y - 2*y

    >>> n, d = fraction(ans)
    >>> pprint(factor_terms(signsimp(collect_sqrt(n))/d, radical=True))
            ___             ___
          \/ 5 *(a + b) - \/ 2 *(x + y)
    ------------------------------------------
       2               2      2              2
    5*a  + 10*a*b + 5*b  - 2*x  - 4*x*y - 2*y

    If radicals in the denominator cannot be removed or there is no denominator,
    the original expression will be returned.

    >>> radsimp(sqrt(2)*x + sqrt(2))
    sqrt(2)*x + sqrt(2)

    Results with symbols will not always be valid for all substitutions:

    >>> eq = 1/(a + b*sqrt(c))
    >>> eq.subs(a, b*sqrt(c))
    1/(2*b*sqrt(c))
    >>> radsimp(eq).subs(a, b*sqrt(c))
    nan

    If ``symbolic=False``, symbolic denominators will not be transformed (but
    numeric denominators will still be processed):

    >>> radsimp(eq, symbolic=False)
    1/(a + b*sqrt(c))

    """
    from sympy.simplify.simplify import signsimp

    syms = symbols("a:d A:D")

    def _num(rterms):
        # return the multiplier that will simplify the expression described
        # by rterms [(sqrt arg, coeff), ... ]
        a, b, c, d, A, B, C, D = syms
        if len(rterms) == 2:
            reps = dict(list(zip([A, a, B, b],
                                 [j for i in rterms for j in i])))
            return (sqrt(A) * a - sqrt(B) * b).xreplace(reps)
        if len(rterms) == 3:
            reps = dict(
                list(zip([A, a, B, b, C, c], [j for i in rterms for j in i])))
            return ((sqrt(A) * a + sqrt(B) * b - sqrt(C) * c) *
                    (2 * sqrt(A) * sqrt(B) * a * b - A * a**2 - B * b**2 +
                     C * c**2)).xreplace(reps)
        elif len(rterms) == 4:
            reps = dict(
                list(
                    zip([A, a, B, b, C, c, D, d],
                        [j for i in rterms for j in i])))
            return (
                (sqrt(A) * a + sqrt(B) * b - sqrt(C) * c - sqrt(D) * d) *
                (2 * sqrt(A) * sqrt(B) * a * b - A * a**2 - B * b**2 -
                 2 * sqrt(C) * sqrt(D) * c * d + C * c**2 + D * d**2) *
                (-8 * sqrt(A) * sqrt(B) * sqrt(C) * sqrt(D) * a * b * c * d +
                 A**2 * a**4 - 2 * A * B * a**2 * b**2 -
                 2 * A * C * a**2 * c**2 - 2 * A * D * a**2 * d**2 +
                 B**2 * b**4 - 2 * B * C * b**2 * c**2 -
                 2 * B * D * b**2 * d**2 + C**2 * c**4 -
                 2 * C * D * c**2 * d**2 + D**2 * d**4)).xreplace(reps)
        elif len(rterms) == 1:
            return sqrt(rterms[0][0])
        else:
            raise NotImplementedError

    def ispow2(d, log2=False):
        if not d.is_Pow:
            return False
        e = d.exp
        if e.is_Rational and e.q == 2 or symbolic and denom(e) == 2:
            return True
        if log2:
            q = 1
            if e.is_Rational:
                q = e.q
            elif symbolic:
                d = denom(e)
                if d.is_Integer:
                    q = d
            if q != 1 and log(q, 2).is_Integer:
                return True
        return False

    def handle(expr):
        # Handle first reduces to the case
        # expr = 1/d, where d is an add, or d is base**p/2.
        # We do this by recursively calling handle on each piece.
        from sympy.simplify.simplify import nsimplify

        n, d = fraction(expr)

        if expr.is_Atom or (d.is_Atom and n.is_Atom):
            return expr
        elif not n.is_Atom:
            n = n.func(*[handle(a) for a in n.args])
            return _unevaluated_Mul(n, handle(1 / d))
        elif n is not S.One:
            return _unevaluated_Mul(n, handle(1 / d))
        elif d.is_Mul:
            return _unevaluated_Mul(*[handle(1 / d) for d in d.args])

        # By this step, expr is 1/d, and d is not a mul.
        if not symbolic and d.free_symbols:
            return expr

        if ispow2(d):
            d2 = sqrtdenest(sqrt(d.base))**numer(d.exp)
            if d2 != d:
                return handle(1 / d2)
        elif d.is_Pow and (d.exp.is_integer or d.base.is_positive):
            # (1/d**i) = (1/d)**i
            return handle(1 / d.base)**d.exp

        if not (d.is_Add or ispow2(d)):
            return 1 / d.func(*[handle(a) for a in d.args])

        # handle 1/d treating d as an Add (though it may not be)

        keep = True  # keep changes that are made

        # flatten it and collect radicals after checking for special
        # conditions
        d = _mexpand(d)

        # did it change?
        if d.is_Atom:
            return 1 / d

        # is it a number that might be handled easily?
        if d.is_number:
            _d = nsimplify(d)
            if _d.is_Number and _d.equals(d):
                return 1 / _d

        while True:
            # collect similar terms
            collected = defaultdict(list)
            for m in Add.make_args(d):  # d might have become non-Add
                p2 = []
                other = []
                for i in Mul.make_args(m):
                    if ispow2(i, log2=True):
                        p2.append(i.base if i.exp is S.Half else i.base**(
                            2 * i.exp))
                    elif i is S.ImaginaryUnit:
                        p2.append(S.NegativeOne)
                    else:
                        other.append(i)
                collected[tuple(ordered(p2))].append(Mul(*other))
            rterms = list(ordered(list(collected.items())))
            rterms = [(Mul(*i), Add(*j)) for i, j in rterms]
            nrad = len(rterms) - (1 if rterms[0][0] is S.One else 0)
            if nrad < 1:
                break
            elif nrad > max_terms:
                # there may have been invalid operations leading to this point
                # so don't keep changes, e.g. this expression is troublesome
                # in collecting terms so as not to raise the issue of 2834:
                # r = sqrt(sqrt(5) + 5)
                # eq = 1/(sqrt(5)*r + 2*sqrt(5)*sqrt(-sqrt(5) + 5) + 5*r)
                keep = False
                break
            if len(rterms) > 4:
                # in general, only 4 terms can be removed with repeated squaring
                # but other considerations can guide selection of radical terms
                # so that radicals are removed
                if all(x.is_Integer and (y**2).is_Rational for x, y in rterms):
                    nd, d = rad_rationalize(
                        S.One,
                        Add._from_args([sqrt(x) * y for x, y in rterms]))
                    n *= nd
                else:
                    # is there anything else that might be attempted?
                    keep = False
                break
            from sympy.simplify.powsimp import powsimp, powdenest

            num = powsimp(_num(rterms))
            n *= num
            d *= num
            d = powdenest(_mexpand(d), force=symbolic)
            if d.has(S.Zero, nan, zoo):
                return expr
            if d.is_Atom:
                break

        if not keep:
            return expr
        return _unevaluated_Mul(n, 1 / d)

    coeff, expr = expr.as_coeff_Add()
    expr = expr.normal()
    old = fraction(expr)
    n, d = fraction(handle(expr))
    if old != (n, d):
        if not d.is_Atom:
            was = (n, d)
            n = signsimp(n, evaluate=False)
            d = signsimp(d, evaluate=False)
            u = Factors(_unevaluated_Mul(n, 1 / d))
            u = _unevaluated_Mul(*[k**v for k, v in u.factors.items()])
            n, d = fraction(u)
            if old == (n, d):
                n, d = was
        n = expand_mul(n)
        if d.is_Number or d.is_Add:
            n2, d2 = fraction(gcd_terms(_unevaluated_Mul(n, 1 / d)))
            if d2.is_Number or (d2.count_ops() <= d.count_ops()):
                n, d = [signsimp(i) for i in (n2, d2)]
                if n.is_Mul and n.args[0].is_Number:
                    n = n.func(*n.args)

    return coeff + _unevaluated_Mul(n, 1 / d)