Example #1
0
def quantity_simplify(expr):
    if expr.is_Atom:
        return expr
    if not expr.is_Mul:
        return expr.func(*map(quantity_simplify, expr.args))

    if expr.has(Prefix):
        coeff, args = expr.as_coeff_mul(Prefix)
        args = list(args)
        for arg in args:
            if isinstance(arg, Pow):
                coeff = coeff * (arg.base.scale_factor ** arg.exp)
            else:
                coeff = coeff * arg.scale_factor
        expr = coeff

    coeff, args = expr.as_coeff_mul(Quantity)
    args_pow = [arg.as_base_exp() for arg in args]
    quantity_pow, other_pow = sift(args_pow, lambda x: isinstance(x[0], Quantity), binary=True)
    quantity_pow_by_dim = sift(quantity_pow, lambda x: x[0].dimension)
    # Just pick the first quantity:
    ref_quantities = [i[0][0] for i in quantity_pow_by_dim.values()]
    new_quantities = [
        Mul.fromiter(
            (quantity*i.scale_factor/quantity.scale_factor)**p for i, p in v)
            if len(v) > 1 else v[0][0]**v[0][1]
        for quantity, (k, v) in zip(ref_quantities, quantity_pow_by_dim.items())]
    return coeff*Mul.fromiter(other_pow)*Mul.fromiter(new_quantities)
Example #2
0
def quantity_simplify(expr):
    if expr.is_Atom:
        return expr
    if not expr.is_Mul:
        return expr.func(*map(quantity_simplify, expr.args))

    if expr.has(Prefix):
        coeff, args = expr.as_coeff_mul(Prefix)
        args = list(args)
        for arg in args:
            if isinstance(arg, Pow):
                coeff = coeff * (arg.base.scale_factor**arg.exp)
            else:
                coeff = coeff * arg.scale_factor
        expr = coeff

    coeff, args = expr.as_coeff_mul(Quantity)
    args_pow = [arg.as_base_exp() for arg in args]
    quantity_pow, other_pow = sift(args_pow,
                                   lambda x: isinstance(x[0], Quantity),
                                   binary=True)
    quantity_pow_by_dim = sift(quantity_pow, lambda x: x[0].dimension)
    # Just pick the first quantity:
    ref_quantities = [i[0][0] for i in quantity_pow_by_dim.values()]
    new_quantities = [
        Mul.fromiter((quantity * i.scale_factor / quantity.scale_factor)**p
                     for i, p in v) if len(v) > 1 else v[0][0]**v[0][1]
        for quantity, (k,
                       v) in zip(ref_quantities, quantity_pow_by_dim.items())
    ]
    return coeff * Mul.fromiter(other_pow) * Mul.fromiter(new_quantities)
Example #3
0
 def _group_Add_terms(self, add):
     numbers, non_num = sift(add.args,
                             lambda arg: arg.is_number,
                             binary=True)
     numsum = sum(numbers)
     terms_with_func, other = sift(non_num,
                                   lambda arg: arg.has(self.func),
                                   binary=True)
     return numsum, terms_with_func, other
Example #4
0
    def _separate_imaginary_from_complex(cls, complexes):
        from sympy.utilities.iterables import sift

        def is_imag(c):
            '''
            return True if all roots are imaginary (ax**2 + b)
            return False if no roots are imaginary
            return None if 2 roots are imaginary (ax**N'''
            u, f, k = c
            deg = f.degree()
            if f.length() == 2:
                if deg == 2:
                    return True  # both imag
                elif _ispow2(deg):
                    if f.LC() * f.TC() < 0:
                        return None  # 2 are imag
            return False  # none are imag

        # separate according to the function
        sifted = sift(complexes, lambda c: c[1])
        del complexes
        imag = []
        complexes = []
        for f in sifted:
            isift = sift(sifted[f], lambda c: is_imag(c))
            imag.extend(isift.pop(True, []))
            complexes.extend(isift.pop(False, []))
            mixed = isift.pop(None, [])
            assert not isift
            if not mixed:
                continue
            while True:
                # the non-imaginary ones will be on one side or the other
                # of the y-axis
                i = 0
                while i < len(mixed):
                    u, f, k = mixed[i]
                    if u.ax * u.bx > 0:
                        complexes.append(mixed.pop(i))
                    else:
                        i += 1
                if len(mixed) == 2:
                    imag.extend(mixed)
                    break
                # refine
                for i, (u, f, k) in enumerate(mixed):
                    u = u._inner_refine()
                    mixed[i] = u, f, k
        return imag, complexes
Example #5
0
    def _separate_imaginary_from_complex(cls, complexes):
        from sympy.utilities.iterables import sift

        def is_imag(c):
            '''
            return True if all roots are imaginary (ax**2 + b)
            return False if no roots are imaginary
            return None if 2 roots are imaginary (ax**N'''
            u, f, k = c
            deg = f.degree()
            if f.length() == 2:
                if deg == 2:
                    return True  # both imag
                elif _ispow2(deg):
                    if f.LC()*f.TC() < 0:
                        return None  # 2 are imag
            return False  # none are imag
        # separate according to the function
        sifted = sift(complexes, lambda c: c[1])
        del complexes
        imag = []
        complexes = []
        for f in sifted:
            isift = sift(sifted[f], lambda c: is_imag(c))
            imag.extend(isift.pop(True, []))
            complexes.extend(isift.pop(False, []))
            mixed = isift.pop(None, [])
            assert not isift
            if not mixed:
                continue
            while True:
                # the non-imaginary ones will be on one side or the other
                # of the y-axis
                i = 0
                while i < len(mixed):
                    u, f, k = mixed[i]
                    if u.ax*u.bx > 0:
                        complexes.append(mixed.pop(i))
                    else:
                        i += 1
                if len(mixed) == 2:
                    imag.extend(mixed)
                    break
                # refine
                for i, (u, f, k) in enumerate(mixed):
                    u = u._inner_refine()
                    mixed[i] = u, f, k
        return imag, complexes
Example #6
0
    def _eval_expand_power_base(self, deep=True, **hints):
        """(a*b)**n -> a**n * b**n"""
        force = hints.get('force', False)
        b, ewas = self.args
        if deep:
            e = self.exp.expand(deep=deep, **hints)
        else:
            e = self.exp
        if b.is_Mul:
            bargs = b.args
            if force or e.is_integer:
                nonneg = bargs
                other = []
            elif ewas.is_Rational or len(
                    bargs) == 2 and bargs[0] is S.NegativeOne:
                # the Rational exponent was already expanded automatically
                # if there is a negative Number * foo, foo must be unknown
                #    or else it, too, would have automatically expanded;
                #    sqrt(-Number*foo) -> sqrt(Number)*sqrt(-foo); then
                #    sqrt(-foo) -> unchanged if foo is not positive else
                #               -> I*sqrt(foo)
                #    So...if we have a 2 arg Mul and the first is a Number
                #    that number is -1 and there is nothing more than can
                #    be done without the force=True hint
                nonneg = []
            else:
                # this is just like what is happening automatically, except
                # that now we are doing it for an arbitrary exponent for which
                # no automatic expansion is done
                def pred(x):
                    if x.is_polar is None:
                        return x.is_nonnegative
                    return x.is_polar

                sifted = sift(b.args, pred)
                nonneg = sifted.get(True, [])
                other = sifted.get(None, [])
                neg = sifted.get(False, [])

                # make sure the Number gets pulled out
                if neg and neg[0].is_Number and neg[0] is not S.NegativeOne:
                    nonneg.append(-neg[0])
                    neg[0] = S.NegativeOne

                # leave behind a negative sign
                oddneg = len(neg) % 2
                if oddneg:
                    other.append(S.NegativeOne)

                # negate all negatives and append to nonneg
                nonneg += [-n for n in neg]

            if nonneg:  # then there's a new expression to return
                other = [Pow(Mul(*other), e)]
                if deep:
                    return Mul(*([Pow(b.expand(deep=deep, **hints), e)\
                    for b in nonneg] + other))
                else:
                    return Mul(*([Pow(b, e) for b in nonneg] + other))
        return Pow(b, e)
Example #7
0
 def _ncsplit(expr):
     # this is not the same as args_cnc because here
     # we don't assume expr is a Mul -- hence deal with args --
     # and always return a set.
     cpart, ncpart = sift(expr.args,
         lambda arg: arg.is_commutative is True, binary=True)
     return set(cpart), ncpart
Example #8
0
def merge_explicit(vecadd):
    """ Merge Vector arguments
    Example
    ========
    >>> from sympy.vector import CoordSys3D
    >>> v1 = VectorSymbol("v1")
    >>> v2 = VectorSymbol("v2")
    >>> C = CoordSys3D("C")
    >>> vn1 = 2 * C.i + 3 * C.j + 4 * C.k
    >>> vn2 = x * C.i + y * C.j + x * y * C.k
    >>> expr = v1 + v2 + vn1 + vn2
    >>> pprint(expr)
    (2*C.i + 3*C.j + 4*C.k) + (x*C.i + y*C.j + x*y*C.k) + v1 + v2
    >>> pprint(merge_explicit(expr))
    ((2 + x)*C.i + (3 + y)*C.j + (4 + x*y)*C.k) + v1 + v2
    """

    # there has to be some bug deep inside the core, I absolutely need this
    # function to correctly perform addition
    def recreate_args(args):
        return [a.func(*a.args) for a in args]

    # Adapted from sympy.matrices.expressions.matadd.py
    groups = sift(vecadd.args, lambda arg: isinstance(arg, (Vector)))
    if len(groups[True]) > 1:
        return VecAdd(*(recreate_args(groups[False]) +
                        [reduce(add, recreate_args(groups[True]))]))
        # return VecAdd(*(groups[False] + [reduce(add, groups[True])]))
    else:
        return vecadd
Example #9
0
        def factor_minmax(args):
            is_other = lambda arg: isinstance(arg, other)
            other_args, remaining_args = sift(args, is_other, binary=True)
            if not other_args:
                return args

            # Min(Max(x, y, z), Max(x, y, u, v)) -> {x,y}, ({z}, {u,v})
            arg_sets = [set(arg.args) for arg in other_args]
            common = set.intersection(*arg_sets)
            if not common:
                return args

            new_other_args = list(common)
            arg_sets_diff = [arg_set - common for arg_set in arg_sets]

            # If any set is empty after removing common then all can be
            # discarded e.g. Min(Max(a, b, c), Max(a, b)) -> Max(a, b)
            if all(arg_sets_diff):
                other_args_diff = [
                    other(*s, evaluate=False) for s in arg_sets_diff
                ]
                new_other_args.append(cls(*other_args_diff, evaluate=False))

            other_args_factored = other(*new_other_args, evaluate=False)
            return remaining_args + [other_args_factored]
Example #10
0
 def _eval_simplify(self, ratio, measure, rational, inverse):
     args = [
         a._eval_simplify(ratio, measure, rational, inverse)
         for a in self.args
     ]
     for i, (expr, cond) in enumerate(args):
         # try to simplify conditions and the expression for
         # equalities that are part of the condition, e.g.
         # Piecewise((n, And(Eq(n,0), Eq(n + m, 0))), (1, True))
         # -> Piecewise((0, And(Eq(n, 0), Eq(m, 0))), (1, True))
         if isinstance(cond, And):
             eqs, other = sift(cond.args,
                               lambda i: isinstance(i, Equality),
                               binary=True)
         elif isinstance(cond, Equality):
             eqs, other = [cond], []
         else:
             eqs = other = []
         if eqs:
             eqs = list(ordered(eqs))
             for j, e in enumerate(eqs):
                 # these blessed lhs objects behave like Symbols
                 # and the rhs are simple replacements for the "symbols"
                 if isinstance(e.lhs, (Symbol, UndefinedFunction)) and \
                     isinstance(e.rhs,
                         (Rational, NumberSymbol,
                         Symbol, UndefinedFunction)):
                     expr = expr.subs(*e.args)
                     eqs[j + 1:] = [ei.subs(*e.args) for ei in eqs[j + 1:]]
                     other = [ei.subs(*e.args) for ei in other]
             cond = And(*(eqs + other))
             args[i] = args[i].func(expr, cond)
     return self.func(*args)
Example #11
0
File: util.py Project: bjodah/sympy
def quantity_simplify(expr):
    """Return an equivalent expression in which prefixes are replaced
    with numerical values and all units of a given dimension are the
    unified in a canonical manner.

    Examples
    ========

    >>> from sympy.physics.units.util import quantity_simplify
    >>> from sympy.physics.units.prefixes import kilo
    >>> from sympy.physics.units import foot, inch
    >>> quantity_simplify(kilo*foot*inch)
    250*foot**2/3
    >>> quantity_simplify(foot - 6*inch)
    foot/2
    """

    if expr.is_Atom or not expr.has(Prefix, Quantity):
        return expr

    # replace all prefixes with numerical values
    p = expr.atoms(Prefix)
    expr = expr.xreplace({p: p.scale_factor for p in p})

    # replace all quantities of given dimension with a canonical
    # quantity, chosen from those in the expression
    d = sift(expr.atoms(Quantity), lambda i: i.dimension)
    for k in d:
        if len(d[k]) == 1:
            continue
        v = list(ordered(d[k]))
        ref = v[0]/v[0].scale_factor
        expr = expr.xreplace({vi: ref*vi.scale_factor for vi in v[1:]})

    return expr
Example #12
0
 def _eval_simplify(self, ratio, measure, rational, inverse):
     args = [a._eval_simplify(ratio, measure, rational, inverse)
         for a in self.args]
     for i, (expr, cond) in enumerate(args):
         # try to simplify conditions and the expression for
         # equalities that are part of the condition, e.g.
         # Piecewise((n, And(Eq(n,0), Eq(n + m, 0))), (1, True))
         # -> Piecewise((0, And(Eq(n, 0), Eq(m, 0))), (1, True))
         if isinstance(cond, And):
             eqs, other = sift(cond.args,
                 lambda i: isinstance(i, Equality), binary=True)
         elif isinstance(cond, Equality):
             eqs, other = [cond], []
         else:
             eqs = other = []
         if eqs:
             eqs = list(ordered(eqs))
             for j, e in enumerate(eqs):
                 # these blessed lhs objects behave like Symbols
                 # and the rhs are simple replacements for the "symbols"
                 if isinstance(e.lhs, (Symbol, UndefinedFunction)) and \
                     isinstance(e.rhs,
                         (Rational, NumberSymbol,
                         Symbol, UndefinedFunction)):
                     expr = expr.subs(*e.args)
                     eqs[j + 1:] = [ei.subs(*e.args) for ei in eqs[j + 1:]]
                     other = [ei.subs(*e.args) for ei in other]
             cond = And(*(eqs + other))
             args[i] = args[i].func(expr, cond)
     return self.func(*args)
Example #13
0
def MatMul_elements(matrix_predicate, scalar_predicate, expr, assumptions):
    d = sift(expr.args, lambda x: isinstance(x, MatrixExpr))
    factors, matrices = d[False], d[True]
    return fuzzy_and([
        test_closed_group(Basic(*factors), assumptions, scalar_predicate),
        test_closed_group(Basic(*matrices), assumptions, matrix_predicate)
    ])
Example #14
0
def quantity_simplify(expr):
    """Return an equivalent expression in which prefixes are replaced
    with numerical values and all units of a given dimension are the
    unified in a canonical manner.

    Examples
    ========

    >>> from sympy.physics.units.util import quantity_simplify
    >>> from sympy.physics.units.prefixes import kilo
    >>> from sympy.physics.units import foot, inch
    >>> quantity_simplify(kilo*foot*inch)
    250*foot**2/3
    >>> quantity_simplify(foot - 6*inch)
    foot/2
    """

    if expr.is_Atom or not expr.has(Prefix, Quantity):
        return expr

    # replace all prefixes with numerical values
    p = expr.atoms(Prefix)
    expr = expr.xreplace({p: p.scale_factor for p in p})

    # replace all quantities of given dimension with a canonical
    # quantity, chosen from those in the expression
    d = sift(expr.atoms(Quantity), lambda i: i.dimension)
    for k in d:
        if len(d[k]) == 1:
            continue
        v = list(ordered(d[k]))
        ref = v[0] / v[0].scale_factor
        expr = expr.xreplace({vi: ref * vi.scale_factor for vi in v[1:]})

    return expr
Example #15
0
 def _ncsplit(expr):
     # this is not the same as args_cnc because here
     # we don't assume expr is a Mul -- hence deal with args --
     # and always return a set.
     cpart, ncpart = sift(expr.args,
         lambda arg: arg.is_commutative is True, binary=True)
     return set(cpart), ncpart
Example #16
0
    def replace_in_Add(self, e):
        """ passed as second argument to Basic.replace(...) """
        numsum, terms_with_func, other_non_num_terms = self._group_Add_terms(e)
        if numsum == 0:
            return e
        substituted, untouched = [], []
        for with_func in terms_with_func:
            if with_func.is_Mul:
                func, coeff = sift(with_func.args,
                                   lambda arg: arg.func == self.func,
                                   binary=True)
                if len(func) == 1 and len(coeff) == 1:
                    func, coeff = func[0], coeff[0]
                else:
                    coeff = None
            elif with_func.func == self.func:
                func, coeff = with_func, S.One
            else:
                coeff = None

            if coeff is not None and coeff.is_number and sign(
                    coeff) == -sign(numsum):
                if self.opportunistic:
                    do_substitute = abs(coeff + numsum) < abs(numsum)
                else:
                    do_substitute = coeff + numsum == 0

                if do_substitute:  # advantageous substitution
                    numsum += coeff
                    substituted.append(coeff * self.func_m_1(*func.args))
                    continue
            untouched.append(with_func)

        return e.func(numsum, *substituted, *untouched, *other_non_num_terms)
Example #17
0
    def _eval_expand_power_base(self, deep=True, **hints):
        """(a*b)**n -> a**n * b**n"""
        force = hints.get("force", False)
        b, ewas = self.args
        if deep:
            e = self.exp.expand(deep=deep, **hints)
        else:
            e = self.exp
        if b.is_Mul:
            bargs = b.args
            if force or e.is_integer:
                nonneg = bargs
                other = []
            elif ewas.is_Rational or len(bargs) == 2 and bargs[0] is S.NegativeOne:
                # the Rational exponent was already expanded automatically
                # if there is a negative Number * foo, foo must be unknown
                #    or else it, too, would have automatically expanded;
                #    sqrt(-Number*foo) -> sqrt(Number)*sqrt(-foo); then
                #    sqrt(-foo) -> unchanged if foo is not positive else
                #               -> I*sqrt(foo)
                #    So...if we have a 2 arg Mul and the first is a Number
                #    that number is -1 and there is nothing more than can
                #    be done without the force=True hint
                nonneg = []
            else:
                # this is just like what is happening automatically, except
                # that now we are doing it for an arbitrary exponent for which
                # no automatic expansion is done
                def pred(x):
                    if x.is_polar is None:
                        return x.is_nonnegative
                    return x.is_polar

                sifted = sift(b.args, pred)
                nonneg = sifted.get(True, [])
                other = sifted.get(None, [])
                neg = sifted.get(False, [])

                # make sure the Number gets pulled out
                if neg and neg[0].is_Number and neg[0] is not S.NegativeOne:
                    nonneg.append(-neg[0])
                    neg[0] = S.NegativeOne

                # leave behind a negative sign
                oddneg = len(neg) % 2
                if oddneg:
                    other.append(S.NegativeOne)

                # negate all negatives and append to nonneg
                nonneg += [-n for n in neg]

            if nonneg:  # then there's a new expression to return
                other = [Pow(Mul(*other), e)]
                if deep:
                    return Mul(*([Pow(b.expand(deep=deep, **hints), e) for b in nonneg] + other))
                else:
                    return Mul(*([Pow(b, e) for b in nonneg] + other))
        return Pow(b, e)
Example #18
0
 def __new__(cls, sym, condition, base_set):
     # nonlinsolve uses ConditionSet to return an unsolved system
     # of equations (see _return_conditionset in solveset) so until
     # that is changed we do minimal checking of the args
     unsolved = isinstance(sym, (Tuple, tuple))
     if unsolved:
         sym = Tuple(*sym)
         condition = FiniteSet(*condition)
     else:
         condition = as_Boolean(condition)
     if isinstance(base_set, set):
         base_set = FiniteSet(*base_set)
     elif not isinstance(base_set, Set):
         raise TypeError('expecting set for base_set')
     if condition == S.false:
         return S.EmptySet
     if condition == S.true:
         return base_set
     if isinstance(base_set, EmptySet):
         return base_set
     if not unsolved:
         if isinstance(base_set, FiniteSet):
             sifted = sift(base_set,
                           lambda _: fuzzy_bool(condition.subs(sym, _)))
             if sifted[None]:
                 return Union(
                     FiniteSet(*sifted[True]),
                     Basic.__new__(cls, sym, condition,
                                   FiniteSet(*sifted[None])))
             else:
                 return FiniteSet(*sifted[True])
         if isinstance(base_set, cls):
             s, c, base_set = base_set.args
             if sym == s:
                 condition = And(condition, c)
             elif sym not in c.free_symbols:
                 condition = And(condition, c.xreplace({s: sym}))
             elif s not in condition.free_symbols:
                 condition = And(condition.xreplace({sym: s}), c)
                 sym = s
             else:
                 # user will have to use cls.sym to get symbol
                 dum = Symbol('lambda')
                 if dum in condition.free_symbols or \
                         dum in c.free_symbols:
                     dum = Dummy(str(dum))
                 condition = And(condition.xreplace({sym: dum}),
                                 c.xreplace({s: dum}))
                 sym = dum
         if sym in base_set.free_symbols or \
                 not isinstance(sym, Symbol):
             s = Symbol('lambda')
             if s in base_set.free_symbols:
                 s = Dummy('lambda')
             condition = condition.xreplace({sym: s})
             sym = s
     return Basic.__new__(cls, sym, condition, base_set)
Example #19
0
 def conglomerate(expr):
     """ Conglomerate together identical args x + x -> 2x """
     groups = sift(expr.args, key)
     counts = dict((k, sum(map(count, args))) for k, args in groups.items())
     newargs = [combine(cnt, mat) for mat, cnt in counts.items()]
     if set(newargs) != set(expr.args):
         return new(type(expr), *newargs)
     else:
         return expr
Example #20
0
 def conglomerate(expr):
     """ Conglomerate together identical args x + x -> 2x """
     groups = sift(expr.args, key)
     counts = dict((k, sum(map(count, args))) for k, args in groups.items())
     newargs = [combine(cnt, mat) for mat, cnt in counts.items()]
     if set(newargs) != set(expr.args):
         return new(type(expr), *newargs)
     else:
         return expr
Example #21
0
    def __new__(cls, variable, condition, base_set=S.UniversalSet):
        # nonlinsolve uses ConditionSet to return an unsolved system
        # of equations (see _return_conditionset in solveset) so until
        # that is changed we do minimal checking of the args
        if isinstance(variable, (Tuple, tuple)):  # unsolved eqns syntax
            variable = Tuple(*variable)
            condition = FiniteSet(*condition)
            return Basic.__new__(cls, variable, condition, base_set)
        condition = as_Boolean(condition)
        if isinstance(base_set, set):
            base_set = FiniteSet(*base_set)
        elif not base_set.is_set:
            raise TypeError('expecting set for base_set')
        if condition is S.false:
            return S.EmptySet
        if condition is S.true:
            return base_set
        if isinstance(base_set, EmptySet):
            return base_set
        know = None
        if isinstance(base_set, FiniteSet):
            sifted = sift(
                base_set, lambda _: fuzzy_bool(
                    condition.subs(variable, _)))
            if sifted[None]:
                know = FiniteSet(*sifted[True])
                base_set = FiniteSet(*sifted[None])
            else:
                return FiniteSet(*sifted[True])
        if isinstance(base_set, cls):
            s, c, base_set = base_set.args
            if variable == s:
                condition = And(condition, c)
            elif variable not in c.free_symbols:
                condition = And(condition, c.xreplace({s: variable}))
            elif s not in condition.free_symbols:
                condition = And(condition.xreplace({variable: s}), c)
                variable = s
            else:
                # user will have to use cls.variable to get symbol
                dum = Symbol('lambda')
                if dum in condition.free_symbols or \
                        dum in c.free_symbols:
                    dum = Dummy(str(dum))
                condition = And(
                    condition.xreplace({variable: dum}),
                    c.xreplace({s: dum}))
                variable = dum
        from sympy.tensor.indexed import Slice, IndexedBase
        assert isinstance(variable, (Symbol, Slice, IndexedBase))
#             s = Dummy('lambda')
#             if s not in condition.xreplace({variable: s}).free_symbols:
#                 raise ValueError('non-symbol dummy not recognized in condition')
        if condition.is_BooleanFalse:
            return S.EmptySet
        rv = Basic.__new__(cls, variable, condition, base_set)
        return rv if know is None else Union(know, rv)
Example #22
0
def quantity_simplify(expr):
    """Return an equivalent expression in which prefixes are replaced
    with numerical values and products of related quantities are
    unified in a canonical manner.

    Examples
    ========

    >>> from sympy.physics.units.util import quantity_simplify
    >>> from sympy.physics.units.prefixes import kilo
    >>> from sympy.physics.units import foot, inch
    >>> quantity_simplify(kilo*foot*inch)
    250*foot**2/3
    """

    if expr.is_Atom:
        return expr

    if isinstance(expr, Prefix):
        return expr.scale_factor

    expr = expr.func(*map(quantity_simplify, expr.args))

    if not expr.is_Mul or not expr.has(Quantity):
        return expr

    args_pow = [arg.as_base_exp() for arg in expr.args]
    quantity_pow, other_pow = sift(args_pow,
                                   lambda x: isinstance(x[0], Quantity),
                                   binary=True)
    coeff = Mul.fromiter([Pow(b, e, evaluate=False) for b, e in other_pow])
    quantity_pow_by_dim = sift(quantity_pow, lambda x: x[0].dimension)
    new_quantities = []
    for _, bp in quantity_pow_by_dim.items():
        if len(bp) == 1:
            new_quantities.append(bp[0][0]**bp[0][1])
        else:
            # just let reference quantity be the first quantity,
            # picked from an ordered list
            bp = list(ordered(bp))
            ref = bp[0][0] / bp[0][0].scale_factor
            new_quantities.append(
                Mul.fromiter(((ref * b.scale_factor)**p for b, p in bp)))
    return coeff * Mul.fromiter(new_quantities)
Example #23
0
def _filter_assumptions(kwargs):
    """Split the given dict into assumptions and non-assumptions.
    Keys are taken as assumptions if they correspond to an
    entry in ``_assume_defined``.
    """
    assumptions, nonassumptions = map(
        dict,
        sift(kwargs.items(), lambda i: i[0] in _assume_defined, binary=True))
    Symbol._sanitize(assumptions)
    return assumptions, nonassumptions
Example #24
0
def _handle_finite_sets(op, x, y, commutative):
    # Handle finite sets:
    fs_args, other = sift([x, y], lambda x: isinstance(x, FiniteSet), binary=True)
    if len(fs_args) == 2:
        return FiniteSet(*[op(i, j) for i in fs_args[0] for j in fs_args[1]])
    elif len(fs_args) == 1:
        sets = [_apply_operation(op, other[0], i, commutative) for i in fs_args[0]]
        return Union(*sets)
    else:
        return None
Example #25
0
 def __new__(cls, sym, condition, base_set=S.UniversalSet):
     # nonlinsolve uses ConditionSet to return an unsolved system
     # of equations (see _return_conditionset in solveset) so until
     # that is changed we do minimal checking of the args
     if isinstance(sym, (Tuple, tuple)):  # unsolved eqns syntax
         sym = Tuple(*sym)
         condition = FiniteSet(*condition)
         return Basic.__new__(cls, sym, condition, base_set)
     condition = as_Boolean(condition)
     if isinstance(base_set, set):
         base_set = FiniteSet(*base_set)
     elif not isinstance(base_set, Set):
         raise TypeError('expecting set for base_set')
     if condition is S.false:
         return S.EmptySet
     if condition is S.true:
         return base_set
     if isinstance(base_set, EmptySet):
         return base_set
     know = None
     if isinstance(base_set, FiniteSet):
         sifted = sift(
             base_set, lambda _: fuzzy_bool(
                 condition.subs(sym, _)))
         if sifted[None]:
             know = FiniteSet(*sifted[True])
             base_set = FiniteSet(*sifted[None])
         else:
             return FiniteSet(*sifted[True])
     if isinstance(base_set, cls):
         s, c, base_set = base_set.args
         if sym == s:
             condition = And(condition, c)
         elif sym not in c.free_symbols:
             condition = And(condition, c.xreplace({s: sym}))
         elif s not in condition.free_symbols:
             condition = And(condition.xreplace({sym: s}), c)
             sym = s
         else:
             # user will have to use cls.sym to get symbol
             dum = Symbol('lambda')
             if dum in condition.free_symbols or \
                     dum in c.free_symbols:
                 dum = Dummy(str(dum))
             condition = And(
                 condition.xreplace({sym: dum}),
                 c.xreplace({s: dum}))
             sym = dum
     if not isinstance(sym, Symbol):
         s = Dummy('lambda')
         if s not in condition.xreplace({sym: s}).free_symbols:
             raise ValueError(
                 'non-symbol dummy not recognized in condition')
     rv = Basic.__new__(cls, sym, condition, base_set)
     return rv if know is None else Union(know, rv)
Example #26
0
 def __new__(cls, sym, condition, base_set=S.UniversalSet):
     # nonlinsolve uses ConditionSet to return an unsolved system
     # of equations (see _return_conditionset in solveset) so until
     # that is changed we do minimal checking of the args
     if isinstance(sym, (Tuple, tuple)):  # unsolved eqns syntax
         sym = Tuple(*sym)
         condition = FiniteSet(*condition)
         return Basic.__new__(cls, sym, condition, base_set)
     condition = as_Boolean(condition)
     if isinstance(base_set, set):
         base_set = FiniteSet(*base_set)
     elif not isinstance(base_set, Set):
         raise TypeError('expecting set for base_set')
     if condition is S.false:
         return S.EmptySet
     if condition is S.true:
         return base_set
     if isinstance(base_set, EmptySet):
         return base_set
     know = None
     if isinstance(base_set, FiniteSet):
         sifted = sift(
             base_set, lambda _: fuzzy_bool(
                 condition.subs(sym, _)))
         if sifted[None]:
             know = FiniteSet(*sifted[True])
             base_set = FiniteSet(*sifted[None])
         else:
             return FiniteSet(*sifted[True])
     if isinstance(base_set, cls):
         s, c, base_set = base_set.args
         if sym == s:
             condition = And(condition, c)
         elif sym not in c.free_symbols:
             condition = And(condition, c.xreplace({s: sym}))
         elif s not in condition.free_symbols:
             condition = And(condition.xreplace({sym: s}), c)
             sym = s
         else:
             # user will have to use cls.sym to get symbol
             dum = Symbol('lambda')
             if dum in condition.free_symbols or \
                     dum in c.free_symbols:
                 dum = Dummy(str(dum))
             condition = And(
                 condition.xreplace({sym: dum}),
                 c.xreplace({s: dum}))
             sym = dum
     if not isinstance(sym, Symbol):
         s = Dummy('lambda')
         if s not in condition.xreplace({sym: s}).free_symbols:
             raise ValueError(
                 'non-symbol dummy not recognized in condition')
     rv = Basic.__new__(cls, sym, condition, base_set)
     return rv if know is None else Union(know, rv)
Example #27
0
def _expm1_value(e):
    numbers, non_num = sift(e.args, lambda arg: arg.is_number, binary=True)
    non_num_exp, non_num_other = sift(non_num, lambda arg: arg.has(exp),
        binary=True)
    numsum = sum(numbers)
    new_exp_terms, done = [], False
    for exp_term in non_num_exp:
        if done:
            new_exp_terms.append(exp_term)
        else:
            looking_at = exp_term + numsum
            attempt = _try_expm1(looking_at)
            if looking_at == attempt:
                new_exp_terms.append(exp_term)
            else:
                done = True
                new_exp_terms.append(attempt)
    if not done:
        new_exp_terms.append(numsum)
    return e.func(*chain(new_exp_terms, non_num_other))
Example #28
0
def _expm1_value(e):
    numbers, non_num = sift(e.args, lambda arg: arg.is_number, binary=True)
    non_num_exp, non_num_other = sift(non_num, lambda arg: arg.has(exp),
        binary=True)
    numsum = sum(numbers)
    new_exp_terms, done = [], False
    for exp_term in non_num_exp:
        if done:
            new_exp_terms.append(exp_term)
        else:
            looking_at = exp_term + numsum
            attempt = _try_expm1(looking_at)
            if looking_at == attempt:
                new_exp_terms.append(exp_term)
            else:
                done = True
                new_exp_terms.append(attempt)
    if not done:
        new_exp_terms.append(numsum)
    return e.func(*chain(new_exp_terms, non_num_other))
Example #29
0
 def __call__(self, e):
     numbers, non_num = sift(e.args, lambda arg: arg.is_number, binary=True)
     non_num_func, non_num_other = sift(non_num, lambda arg: arg.has(self.func),
         binary=True)
     numsum = sum(numbers)
     new_func_terms, done = [], False
     for func_term in non_num_func:
         if done:
             new_func_terms.append(func_term)
         else:
             looking_at = func_term + numsum
             attempt = self._try_func_m_1(looking_at)
             if looking_at == attempt:
                 new_func_terms.append(func_term)
             else:
                 done = True
                 new_func_terms.append(attempt)
     if not done:
         new_func_terms.append(numsum)
     return e.func(*chain(new_func_terms, non_num_other))
Example #30
0
def test_sift():
    assert sift(list(range(5)), lambda _: _ % 2) == {1: [1, 3], 0: [0, 2, 4]}
    assert sift([x, y], lambda _: _.has(x)) == {False: [y], True: [x]}
    assert sift([S.One], lambda _: _.has(x)) == {False: [1]}
    assert sift([0, 1, 2, 3], lambda x: x % 2, binary=True) == ([1, 3], [0, 2])
    assert sift([0, 1, 2, 3], lambda x: x % 3 == 1, binary=True) == ([1], [0, 2, 3])
    raises(ValueError, lambda: sift([0, 1, 2, 3], lambda x: x % 3, binary=True))
Example #31
0
def bc_matadd(expr):
    args = sift(expr.args, lambda M: isinstance(M, BlockMatrix))
    blocks = args[True]
    if not blocks:
        return expr

    nonblocks = args[False]
    block = blocks[0]
    for b in blocks[1:]:
        block = block._blockadd(b)
    if nonblocks:
        return MatAdd(*nonblocks) + block
    else:
        return block
Example #32
0
def _handle_finite_sets(op, x, y, commutative):
    # Handle finite sets:
    fs_args, other = sift([x, y],
                          lambda x: isinstance(x, FiniteSet),
                          binary=True)
    if len(fs_args) == 2:
        return FiniteSet(*[op(i, j) for i in fs_args[0] for j in fs_args[1]])
    elif len(fs_args) == 1:
        sets = [
            _apply_operation(op, other[0], i, commutative) for i in fs_args[0]
        ]
        return Union(*sets)
    else:
        return None
Example #33
0
 def _eval_factor(self, **hints):
     if 1 == len(self.limits):
         summand = self.function.factor(**hints)
         if summand.is_Mul:
             out = sift(summand.args, lambda w: w.is_commutative \
                 and not w.has(*self.variables))
             return Mul(*out[True])*self.func(Mul(*out[False]), \
                 *self.limits)
     else:
         summand = self.func(self.function, self.limits[0:-1]).factor()
         if not summand.has(self.variables[-1]):
             return self.func(1, [self.limits[-1]]).doit() * summand
         elif isinstance(summand, Mul):
             return self.func(summand, self.limits[-1]).factor()
     return self
Example #34
0
 def __new__(cls, sym, condition, base_set):
     if condition == S.false:
         return S.EmptySet
     if condition == S.true:
         return base_set
     if isinstance(base_set, EmptySet):
         return base_set
     if isinstance(base_set, FiniteSet):
         sifted = sift(base_set, lambda _: fuzzy_bool(condition.subs(sym, _)))
         if sifted[None]:
             return Union(FiniteSet(*sifted[True]),
                          Basic.__new__(cls, sym, condition, FiniteSet(*sifted[None])))
         else:
             return FiniteSet(*sifted[True])
     return Basic.__new__(cls, sym, condition, base_set)
Example #35
0
 def _eval_factor(self, **hints):
     if 1 == len(self.limits):
         summand = self.function.factor(**hints)
         if summand.is_Mul:
             out = sift(summand.args, lambda w: w.is_commutative \
                 and not w.has(*self.variables))
             return C.Mul(*out[True])*self.func(C.Mul(*out[False]), \
                 *self.limits)
     else:
         summand = self.func(self.function, self.limits[0:-1]).factor()
         if not summand.has(self.variables[-1]):
             return self.func(1, [self.limits[-1]]).doit()*summand
         elif isinstance(summand, C.Mul):
             return self.func(summand, self.limits[-1]).factor()
     return self
Example #36
0
def test_sift():
    assert sift(list(range(5)), lambda _: _ % 2) == {1: [1, 3], 0: [0, 2, 4]}
    assert sift([x, y], lambda _: _.has(x)) == {False: [y], True: [x]}
    assert sift([S.One], lambda _: _.has(x)) == {False: [1]}
    assert sift([0, 1, 2, 3], lambda x: x % 2, binary=True) == (
        [1, 3], [0, 2])
    assert sift([0, 1, 2, 3], lambda x: x % 3 == 1, binary=True) == (
        [1], [0, 2, 3])
    raises(ValueError, lambda:
        sift([0, 1, 2, 3], lambda x: x % 3, binary=True))
Example #37
0
def cse_separate(r, e):
    """Move expressions that are in the form (symbol, expr) out of the
    expressions and sort them into the replacements using the reps_toposort.

    Examples
    ========
    >>> from sympy.simplify.cse_main import cse_separate
    >>> from sympy.abc import x, y, z
    >>> from sympy import cos, exp, cse, Eq
    >>> eq = (x + 1 + exp((x + 1)/(y + 1)) + cos(y + 1))
    >>> cse([eq, Eq(x, z + 1), z - 2], postprocess=cse_separate)
    [[(x0, y + 1), (x, z + 1), (x1, x + 1)], [x1 + exp(x1/x0) + cos(x0), z - 2]]
    """
    syms = set([k for k, v in r])
    d = sift(e, lambda w: w.is_Equality and not bool(w.free_symbols & set(syms)))
    r, e = [r + [w.args for w in d[True]], d[False]]
    return [reps_toposort(r), e]
Example #38
0
def cse_separate(r, e):
    """Move expressions that are in the form (symbol, expr) out of the
    expressions and sort them into the replacements using the reps_toposort.

    Examples
    ========
    >>> from sympy.simplify.cse_main import cse_separate
    >>> from sympy.abc import x, y, z
    >>> from sympy import cos, exp, cse, Eq
    >>> eq = (x + 1 + exp((x + 1)/(y + 1)) + cos(y + 1))
    >>> cse([eq, Eq(x, z + 1), z - 2], postprocess=cse_separate)
    [[(x0, y + 1), (x, z + 1), (x1, x + 1)], [x1 + exp(x1/x0) + cos(x0), z - 2]]
    """
    syms = set([k for k, v in r])
    d = sift(e,
             lambda w: w.is_Equality and not bool(w.free_symbols & set(syms)))
    r, e = [r + [w.args for w in d[True]], d[False]]
    return [reps_toposort(r), e]
Example #39
0
 def __new__(cls, sym, condition, base_set):
     if condition == S.false:
         return S.EmptySet
     if condition == S.true:
         return base_set
     if isinstance(base_set, EmptySet):
         return base_set
     if isinstance(base_set, FiniteSet):
         sifted = sift(base_set,
                       lambda _: fuzzy_bool(condition.subs(sym, _)))
         if sifted[None]:
             return Union(
                 FiniteSet(*sifted[True]),
                 Basic.__new__(cls, sym, condition,
                               FiniteSet(*sifted[None])))
         else:
             return FiniteSet(*sifted[True])
     return Basic.__new__(cls, sym, condition, base_set)
Example #40
0
    def as_coeff_add(self, *deps):
        """
        Returns a tuple (coeff, args) where self is treated as an Add and coeff
        is the Number term and args is a tuple of all other terms.

        Examples
        ========

        >>> from sympy.abc import x
        >>> (7 + 3*x).as_coeff_add()
        (7, (3*x,))
        >>> (7*x).as_coeff_add()
        (0, (7*x,))
        """
        if deps:
            l1, l2 = sift(self.args, lambda x: x.has_free(*deps), binary=True)
            return self._new_rawargs(*l2), tuple(l1)
        coeff, notrat = self.args[0].as_coeff_add()
        if coeff is not S.Zero:
            return coeff, notrat + self.args[1:]
        return S.Zero, self.args
Example #41
0
def merge_explicit_mul(vecmul):
    """ Merge explicit Vector and Expr (Numbers, Symbols, ...) arguments
    Example
    ========
    >>> from sympy.vector import CoordSys3D
    >>> v1 = VectorSymbol("v1")
    >>> v2 = VectorSymbol("v2")
    >>> C = CoordSys3D("C")
    >>> vn1 = R.x * R.i + R.y * R.j + R.k * R.z
    >>> expr = VecMul(2, v1.mag, vn1, x, evaluate=False)
    >>> pprint(expr)
    2*x*(R.x*R.i + R.y*R.j + R.z*R.k)*Magnitude(VectorSymbol(v1))
    >>> pprint(merge_explicit(expr))
    (2*R.x*x*R.i + 2*R.y*x*R.j + 2*R.z*x*R.k)*Magnitude(VectorSymbol(v1))
    """

    groups = sift(vecmul.args, lambda arg: not isinstance(arg, VectorExpr))
    print("\t", groups)
    if len(groups[True]) > 1:
        return VecMul(*(groups[False] + [reduce(mul, groups[True])]))
    else:
        return vecmul
Example #42
0
def cse_separate(r, e):
    """Move expressions that are in the form (symbol, expr) out of the
    expressions and sort them into the replacements using the reps_toposort.

    Examples
    ========
    >>> from sympy.simplify.cse_main import cse_separate
    >>> from sympy.abc import x, y, z
    >>> from sympy import cos, exp, cse, Eq, symbols
    >>> x0, x1 = symbols('x:2')
    >>> eq = (x + 1 + exp((x + 1)/(y + 1)) + cos(y + 1))
    >>> cse([eq, Eq(x, z + 1), z - 2], postprocess=cse_separate) in [
    ... [[(x0, y + 1), (x, z + 1), (x1, x + 1)],
    ...  [x1 + exp(x1/x0) + cos(x0), z - 2]],
    ... [[(x1, y + 1), (x, z + 1), (x0, x + 1)],
    ...  [x0 + exp(x0/x1) + cos(x1), z - 2]]]
    ...
    True
    """
    d = sift(e, lambda w: w.is_Equality and w.lhs.is_Symbol)
    r = r + [w.args for w in d[True]]
    e = d[False]
    return [reps_toposort(r), e]
Example #43
0
def cse_separate(r, e):
    """Move expressions that are in the form (symbol, expr) out of the
    expressions and sort them into the replacements using the reps_toposort.

    Examples
    ========
    >>> from sympy.simplify.cse_main import cse_separate
    >>> from sympy.abc import x, y, z
    >>> from sympy import cos, exp, cse, Eq, symbols
    >>> x0, x1 = symbols('x:2')
    >>> eq = (x + 1 + exp((x + 1)/(y + 1)) + cos(y + 1))
    >>> cse([eq, Eq(x, z + 1), z - 2], postprocess=cse_separate) in [
    ... [[(x0, y + 1), (x, z + 1), (x1, x + 1)],
    ...  [x1 + exp(x1/x0) + cos(x0), z - 2]],
    ... [[(x1, y + 1), (x, z + 1), (x0, x + 1)],
    ...  [x0 + exp(x0/x1) + cos(x1), z - 2]]]
    ...
    True
    """
    d = sift(e, lambda w: w.is_Equality and w.lhs.is_Symbol)
    r = r + [w.args for w in d[True]]
    e = d[False]
    return [reps_toposort(r), e]
Example #44
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 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_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:
                    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 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_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 #45
0
 def split_real_imag(expr):
     real_imag = lambda t: (
         'real' if is_extended_real(t, assumptions) else 'imag'
         if is_extended_real(I * t, assumptions) else None)
     return sift(Add.make_args(expr), real_imag)
Example #46
0
 def _eval_simplify(self, ratio, measure, rational, inverse):
     args = [a._eval_simplify(ratio, measure, rational, inverse)
         for a in self.args]
     for i, (expr, cond) in enumerate(args):
         # try to simplify conditions and the expression for
         # equalities that are part of the condition, e.g.
         # Piecewise((n, And(Eq(n,0), Eq(n + m, 0))), (1, True))
         # -> Piecewise((0, And(Eq(n, 0), Eq(m, 0))), (1, True))
         if isinstance(cond, And):
             eqs, other = sift(cond.args,
                 lambda i: isinstance(i, Equality), binary=True)
         elif isinstance(cond, Equality):
             eqs, other = [cond], []
         else:
             eqs = other = []
         if eqs:
             eqs = list(ordered(eqs))
             for j, e in enumerate(eqs):
                 # these blessed lhs objects behave like Symbols
                 # and the rhs are simple replacements for the "symbols"
                 if isinstance(e.lhs, (Symbol, UndefinedFunction)) and \
                     isinstance(e.rhs,
                         (Rational, NumberSymbol,
                         Symbol, UndefinedFunction)):
                     expr = expr.subs(*e.args)
                     eqs[j + 1:] = [ei.subs(*e.args) for ei in eqs[j + 1:]]
                     other = [ei.subs(*e.args) for ei in other]
             cond = And(*(eqs + other))
             args[i] = args[i].func(expr, cond)
     # See if expressions valid for a single point happens to evaluate
     # to the same function as in the next piecewise segment, see:
     # https://github.com/sympy/sympy/issues/8458
     prevexpr = None
     for i, (expr, cond) in reversed(list(enumerate(args))):
         if prevexpr is not None:
             _prevexpr = prevexpr
             _expr = expr
             if isinstance(cond, And):
                 eqs, other = sift(cond.args,
                     lambda i: isinstance(i, Equality), binary=True)
             elif isinstance(cond, Equality):
                 eqs, other = [cond], []
             else:
                 eqs = other = []
             if eqs:
                 eqs = list(ordered(eqs))
                 for j, e in enumerate(eqs):
                     # these blessed lhs objects behave like Symbols
                     # and the rhs are simple replacements for the "symbols"
                     if isinstance(e.lhs, (Symbol, UndefinedFunction)) and \
                         isinstance(e.rhs,
                             (Rational, NumberSymbol,
                             Symbol, UndefinedFunction)):
                         _prevexpr = _prevexpr.subs(*e.args)
                         _expr = _expr.subs(*e.args)
             if _prevexpr == _expr:
                 args[i] = args[i].func(args[i+1][0], cond)
             else:
                 prevexpr = expr
         else:
             prevexpr = expr
     return self.func(*args)
Example #47
0
    def _eval_expand_power_base(self, **hints):
        """(a*b)**n -> a**n * b**n"""
        force = hints.get('force', False)

        b = self.base
        e = self.exp
        if not b.is_Mul:
            return self

        cargs, nc = b.args_cnc(split_1=False)

        # expand each term - this is top-level-only
        # expansion but we have to watch out for things
        # that don't have an _eval_expand method
        if nc:
            nc = [i._eval_expand_power_base(**hints)
                if hasattr(i, '_eval_expand_power_base') else i
                for i in nc]

            if e.is_Integer:
                if e.is_positive:
                    rv = Mul(*nc*e)
                else:
                    rv = 1/Mul(*nc*-e)
                if cargs:
                    rv *= Mul(*cargs)**e
                return rv

            if not cargs:
                return self.func(Mul(*nc), e, evaluate=False)

            nc = [Mul(*nc)]

        # sift the commutative bases
        def pred(x):
            if x is S.ImaginaryUnit:
                return S.ImaginaryUnit
            polar = x.is_polar
            if polar:
                return True
            if polar is None:
                return fuzzy_bool(x.is_nonnegative)
        sifted = sift(cargs, pred)
        nonneg = sifted[True]
        other = sifted[None]
        neg = sifted[False]
        imag = sifted[S.ImaginaryUnit]
        if imag:
            I = S.ImaginaryUnit
            i = len(imag) % 4
            if i == 0:
                pass
            elif i == 1:
                other.append(I)
            elif i == 2:
                if neg:
                    nonn = -neg.pop()
                    if nonn is not S.One:
                        nonneg.append(nonn)
                else:
                    neg.append(S.NegativeOne)
            else:
                if neg:
                    nonn = -neg.pop()
                    if nonn is not S.One:
                        nonneg.append(nonn)
                else:
                    neg.append(S.NegativeOne)
                other.append(I)
            del imag

        # bring out the bases that can be separated from the base

        if force or e.is_integer:
            # treat all commutatives the same and put nc in other
            cargs = nonneg + neg + other
            other = nc
        else:
            # this is just like what is happening automatically, except
            # that now we are doing it for an arbitrary exponent for which
            # no automatic expansion is done

            assert not e.is_Integer

            # handle negatives by making them all positive and putting
            # the residual -1 in other
            if len(neg) > 1:
                o = S.One
                if not other and neg[0].is_Number:
                    o *= neg.pop(0)
                if len(neg) % 2:
                    o = -o
                for n in neg:
                    nonneg.append(-n)
                if o is not S.One:
                    other.append(o)
            elif neg and other:
                if neg[0].is_Number and neg[0] is not S.NegativeOne:
                    other.append(S.NegativeOne)
                    nonneg.append(-neg[0])
                else:
                    other.extend(neg)
            else:
                other.extend(neg)
            del neg

            cargs = nonneg
            other += nc

        rv = S.One
        if cargs:
            rv *= Mul(*[self.func(b, e, evaluate=False) for b in cargs])
        if other:
            rv *= self.func(Mul(*other), e, evaluate=False)
        return rv
Example #48
0
def piecewise_fold(expr):
    """
    Takes an expression containing a piecewise function and returns the
    expression in piecewise form. In addition, any ITE conditions are
    rewritten in negation normal form and simplified.

    Examples
    ========

    >>> from sympy import Piecewise, piecewise_fold, sympify as S
    >>> from sympy.abc import x
    >>> p = Piecewise((x, x < 1), (1, S(1) <= x))
    >>> piecewise_fold(x*p)
    Piecewise((x**2, x < 1), (x, True))

    See Also
    ========

    Piecewise
    """
    if not isinstance(expr, Basic) or not expr.has(Piecewise):
        return expr

    new_args = []
    if isinstance(expr, (ExprCondPair, Piecewise)):
        for e, c in expr.args:
            if not isinstance(e, Piecewise):
                e = piecewise_fold(e)
            # we don't keep Piecewise in condition because
            # it has to be checked to see that it's complete
            # and we convert it to ITE at that time
            assert not c.has(Piecewise)  # pragma: no cover
            if isinstance(c, ITE):
                c = c.to_nnf()
                c = simplify_logic(c, form='cnf')
            if isinstance(e, Piecewise):
                new_args.extend([(piecewise_fold(ei), And(ci, c))
                    for ei, ci in e.args])
            else:
                new_args.append((e, c))
    else:
        from sympy.utilities.iterables import cartes, sift, common_prefix
        # Given
        #     P1 = Piecewise((e11, c1), (e12, c2), A)
        #     P2 = Piecewise((e21, c1), (e22, c2), B)
        #     ...
        # the folding of f(P1, P2) is trivially
        # Piecewise(
        #   (f(e11, e21), c1),
        #   (f(e12, e22), c2),
        #   (f(Piecewise(A), Piecewise(B)), True))
        # Certain objects end up rewriting themselves as thus, so
        # we do that grouping before the more generic folding.
        # The following applies this idea when f = Add or f = Mul
        # (and the expression is commutative).
        if expr.is_Add or expr.is_Mul and expr.is_commutative:
            p, args = sift(expr.args, lambda x: x.is_Piecewise, binary=True)
            pc = sift(p, lambda x: x.args[0].cond)
            for c in pc:
                if len(pc[c]) > 1:
                    pargs = [list(i.args) for i in pc[c]]
                    # the first one is the same; there may be more
                    com = common_prefix(*[
                        [i.cond for i in j] for j in pargs])
                    n = len(com)
                    collected = []
                    for i in range(n):
                        collected.append((
                            expr.func(*[ai[i].expr for ai in pargs]),
                            com[i]))
                    remains = []
                    for a in pargs:
                        if n == len(a):  # no more args
                            continue
                        if a[n].cond == True:  # no longer Piecewise
                            remains.append(a[n].expr)
                        else:  # restore the remaining Piecewise
                            remains.append(
                                Piecewise(*a[n:], evaluate=False))
                    if remains:
                        collected.append((expr.func(*remains), True))
                    args.append(Piecewise(*collected, evaluate=False))
                    continue
                args.extend(pc[c])
        else:
            args = expr.args
        # fold
        folded = list(map(piecewise_fold, args))
        for ec in cartes(*[
                (i.args if isinstance(i, Piecewise) else
                 [(i, true)]) for i in folded]):
            e, c = zip(*ec)
            new_args.append((expr.func(*e), And(*c)))

    return Piecewise(*new_args)
Example #49
0
    def rule_gamma(expr, level=0):
        """ Simplify products of gamma functions further. """

        if expr.is_Atom:
            return expr

        def gamma_rat(x):
            # helper to simplify ratios of gammas
            was = x.count(gamma)
            xx = x.replace(gamma, lambda n: _rf(1, (n - 1).expand()
                ).replace(_rf, lambda a, b: gamma(a + b)/gamma(a)))
            if xx.count(gamma) < was:
                x = xx
            return x

        def gamma_factor(x):
            # return True if there is a gamma factor in shallow args
            if isinstance(x, gamma):
                return True
            if x.is_Add or x.is_Mul:
                return any(gamma_factor(xi) for xi in x.args)
            if x.is_Pow and (x.exp.is_integer or x.base.is_positive):
                return gamma_factor(x.base)
            return False

        # recursion step
        if level == 0:
            expr = expr.func(*[rule_gamma(x, level + 1) for x in expr.args])
            level += 1

        if not expr.is_Mul:
            return expr

        # non-commutative step
        if level == 1:
            args, nc = expr.args_cnc()
            if not args:
                return expr
            if nc:
                return rule_gamma(Mul._from_args(args), level + 1)*Mul._from_args(nc)
            level += 1

        # pure gamma handling, not factor absorption
        if level == 2:
            T, F = sift(expr.args, gamma_factor, binary=True)
            gamma_ind = Mul(*F)
            d = Mul(*T)

            nd, dd = d.as_numer_denom()
            for ipass in range(2):
                args = list(ordered(Mul.make_args(nd)))
                for i, ni in enumerate(args):
                    if ni.is_Add:
                        ni, dd = Add(*[
                            rule_gamma(gamma_rat(a/dd), level + 1) for a in ni.args]
                            ).as_numer_denom()
                        args[i] = ni
                        if not dd.has(gamma):
                            break
                nd = Mul(*args)
                if ipass ==  0 and not gamma_factor(nd):
                    break
                nd, dd = dd, nd  # now process in reversed order
            expr = gamma_ind*nd/dd
            if not (expr.is_Mul and (gamma_factor(dd) or gamma_factor(nd))):
                return expr
            level += 1

        # iteration until constant
        if level == 3:
            while True:
                was = expr
                expr = rule_gamma(expr, 4)
                if expr == was:
                    return expr

        numer_gammas = []
        denom_gammas = []
        numer_others = []
        denom_others = []
        def explicate(p):
            if p is S.One:
                return None, []
            b, e = p.as_base_exp()
            if e.is_Integer:
                if isinstance(b, gamma):
                    return True, [b.args[0]]*e
                else:
                    return False, [b]*e
            else:
                return False, [p]

        newargs = list(ordered(expr.args))
        while newargs:
            n, d = newargs.pop().as_numer_denom()
            isg, l = explicate(n)
            if isg:
                numer_gammas.extend(l)
            elif isg is False:
                numer_others.extend(l)
            isg, l = explicate(d)
            if isg:
                denom_gammas.extend(l)
            elif isg is False:
                denom_others.extend(l)

        # =========== level 2 work: pure gamma manipulation =========

        if not as_comb:
            # Try to reduce the number of gamma factors by applying the
            # reflection formula gamma(x)*gamma(1-x) = pi/sin(pi*x)
            for gammas, numer, denom in [(
                numer_gammas, numer_others, denom_others),
                    (denom_gammas, denom_others, numer_others)]:
                new = []
                while gammas:
                    g1 = gammas.pop()
                    if g1.is_integer:
                        new.append(g1)
                        continue
                    for i, g2 in enumerate(gammas):
                        n = g1 + g2 - 1
                        if not n.is_Integer:
                            continue
                        numer.append(S.Pi)
                        denom.append(sin(S.Pi*g1))
                        gammas.pop(i)
                        if n > 0:
                            for k in range(n):
                                numer.append(1 - g1 + k)
                        elif n < 0:
                            for k in range(-n):
                                denom.append(-g1 - k)
                        break
                    else:
                        new.append(g1)
                # /!\ updating IN PLACE
                gammas[:] = new

            # Try to reduce the number of gammas by using the duplication
            # theorem to cancel an upper and lower: gamma(2*s)/gamma(s) =
            # 2**(2*s + 1)/(4*sqrt(pi))*gamma(s + 1/2). Although this could
            # be done with higher argument ratios like gamma(3*x)/gamma(x),
            # this would not reduce the number of gammas as in this case.
            for ng, dg, no, do in [(numer_gammas, denom_gammas, numer_others,
                                    denom_others),
                                   (denom_gammas, numer_gammas, denom_others,
                                    numer_others)]:

                while True:
                    for x in ng:
                        for y in dg:
                            n = x - 2*y
                            if n.is_Integer:
                                break
                        else:
                            continue
                        break
                    else:
                        break
                    ng.remove(x)
                    dg.remove(y)
                    if n > 0:
                        for k in range(n):
                            no.append(2*y + k)
                    elif n < 0:
                        for k in range(-n):
                            do.append(2*y - 1 - k)
                    ng.append(y + S(1)/2)
                    no.append(2**(2*y - 1))
                    do.append(sqrt(S.Pi))

            # Try to reduce the number of gamma factors by applying the
            # multiplication theorem (used when n gammas with args differing
            # by 1/n mod 1 are encountered).
            #
            # run of 2 with args differing by 1/2
            #
            # >>> gammasimp(gamma(x)*gamma(x+S.Half))
            # 2*sqrt(2)*2**(-2*x - 1/2)*sqrt(pi)*gamma(2*x)
            #
            # run of 3 args differing by 1/3 (mod 1)
            #
            # >>> gammasimp(gamma(x)*gamma(x+S(1)/3)*gamma(x+S(2)/3))
            # 6*3**(-3*x - 1/2)*pi*gamma(3*x)
            # >>> gammasimp(gamma(x)*gamma(x+S(1)/3)*gamma(x+S(5)/3))
            # 2*3**(-3*x - 1/2)*pi*(3*x + 2)*gamma(3*x)
            #
            def _run(coeffs):
                # find runs in coeffs such that the difference in terms (mod 1)
                # of t1, t2, ..., tn is 1/n
                u = list(uniq(coeffs))
                for i in range(len(u)):
                    dj = ([((u[j] - u[i]) % 1, j) for j in range(i + 1, len(u))])
                    for one, j in dj:
                        if one.p == 1 and one.q != 1:
                            n = one.q
                            got = [i]
                            get = list(range(1, n))
                            for d, j in dj:
                                m = n*d
                                if m.is_Integer and m in get:
                                    get.remove(m)
                                    got.append(j)
                                    if not get:
                                        break
                            else:
                                continue
                            for i, j in enumerate(got):
                                c = u[j]
                                coeffs.remove(c)
                                got[i] = c
                            return one.q, got[0], got[1:]

            def _mult_thm(gammas, numer, denom):
                # pull off and analyze the leading coefficient from each gamma arg
                # looking for runs in those Rationals

                # expr -> coeff + resid -> rats[resid] = coeff
                rats = {}
                for g in gammas:
                    c, resid = g.as_coeff_Add()
                    rats.setdefault(resid, []).append(c)

                # look for runs in Rationals for each resid
                keys = sorted(rats, key=default_sort_key)
                for resid in keys:
                    coeffs = list(sorted(rats[resid]))
                    new = []
                    while True:
                        run = _run(coeffs)
                        if run is None:
                            break

                        # process the sequence that was found:
                        # 1) convert all the gamma functions to have the right
                        #    argument (could be off by an integer)
                        # 2) append the factors corresponding to the theorem
                        # 3) append the new gamma function

                        n, ui, other = run

                        # (1)
                        for u in other:
                            con = resid + u - 1
                            for k in range(int(u - ui)):
                                numer.append(con - k)

                        con = n*(resid + ui)  # for (2) and (3)

                        # (2)
                        numer.append((2*S.Pi)**(S(n - 1)/2)*
                                     n**(S(1)/2 - con))
                        # (3)
                        new.append(con)

                    # restore resid to coeffs
                    rats[resid] = [resid + c for c in coeffs] + new

                # rebuild the gamma arguments
                g = []
                for resid in keys:
                    g += rats[resid]
                # /!\ updating IN PLACE
                gammas[:] = g

            for l, numer, denom in [(numer_gammas, numer_others, denom_others),
                                    (denom_gammas, denom_others, numer_others)]:
                _mult_thm(l, numer, denom)

        # =========== level >= 2 work: factor absorption =========

        if level >= 2:
            # Try to absorb factors into the gammas: x*gamma(x) -> gamma(x + 1)
            # and gamma(x)/(x - 1) -> gamma(x - 1)
            # This code (in particular repeated calls to find_fuzzy) can be very
            # slow.
            def find_fuzzy(l, x):
                if not l:
                    return
                S1, T1 = compute_ST(x)
                for y in l:
                    S2, T2 = inv[y]
                    if T1 != T2 or (not S1.intersection(S2) and
                                    (S1 != set() or S2 != set())):
                        continue
                    # XXX we want some simplification (e.g. cancel or
                    # simplify) but no matter what it's slow.
                    a = len(cancel(x/y).free_symbols)
                    b = len(x.free_symbols)
                    c = len(y.free_symbols)
                    # TODO is there a better heuristic?
                    if a == 0 and (b > 0 or c > 0):
                        return y

            # We thus try to avoid expensive calls by building the following
            # "invariants": For every factor or gamma function argument
            #   - the set of free symbols S
            #   - the set of functional components T
            # We will only try to absorb if T1==T2 and (S1 intersect S2 != emptyset
            # or S1 == S2 == emptyset)
            inv = {}

            def compute_ST(expr):
                if expr in inv:
                    return inv[expr]
                return (expr.free_symbols, expr.atoms(Function).union(
                        set(e.exp for e in expr.atoms(Pow))))

            def update_ST(expr):
                inv[expr] = compute_ST(expr)
            for expr in numer_gammas + denom_gammas + numer_others + denom_others:
                update_ST(expr)

            for gammas, numer, denom in [(
                numer_gammas, numer_others, denom_others),
                    (denom_gammas, denom_others, numer_others)]:
                new = []
                while gammas:
                    g = gammas.pop()
                    cont = True
                    while cont:
                        cont = False
                        y = find_fuzzy(numer, g)
                        if y is not None:
                            numer.remove(y)
                            if y != g:
                                numer.append(y/g)
                                update_ST(y/g)
                            g += 1
                            cont = True
                        y = find_fuzzy(denom, g - 1)
                        if y is not None:
                            denom.remove(y)
                            if y != g - 1:
                                numer.append((g - 1)/y)
                                update_ST((g - 1)/y)
                            g -= 1
                            cont = True
                    new.append(g)
                # /!\ updating IN PLACE
                gammas[:] = new

        # =========== rebuild expr ==================================

        return Mul(*[gamma(g) for g in numer_gammas]) \
            / Mul(*[gamma(g) for g in denom_gammas]) \
            * Mul(*numer_others) / Mul(*denom_others)
Example #50
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 #51
0
 def _sort_args(self, args):
     lists, atoms = sift(args,
         lambda a: isinstance(a, (tuple, list)), binary=True)
     return atoms, lists
Example #52
0
def test_sift():
    assert sift(range(5), lambda _: _ % 2) == {1: [1, 3], 0: [0, 2, 4]}
    assert sift([x, y], lambda _: _.has(x)) == {False: [y], True: [x]}
    assert sift([S.One], lambda _: _.has(x)) == {False: [1]}
Example #53
0
def MatMul_elements(matrix_predicate, scalar_predicate, expr, assumptions):
    d = sift(expr.args, lambda x: isinstance(x, MatrixExpr))
    factors, matrices = d[False], d[True]
    return fuzzy_and([
        test_closed_group(Basic(*factors), assumptions, scalar_predicate),
        test_closed_group(Basic(*matrices), assumptions, matrix_predicate)])
Example #54
0
def _separate_sq(p):
    """
    helper function for ``_minimal_polynomial_sq``

    It selects a rational ``g`` such that the polynomial ``p``
    consists of a sum of terms whose surds squared have gcd equal to ``g``
    and a sum of terms with surds squared prime with ``g``;
    then it takes the field norm to eliminate ``sqrt(g)``

    See simplify.simplify.split_surds and polytools.sqf_norm.

    Examples
    ========

    >>> from sympy import sqrt
    >>> from sympy.abc import x
    >>> from sympy.polys.numberfields import _separate_sq
    >>> p= -x + sqrt(2) + sqrt(3) + sqrt(7)
    >>> p = _separate_sq(p); p
    -x**2 + 2*sqrt(3)*x + 2*sqrt(7)*x - 2*sqrt(21) - 8
    >>> p = _separate_sq(p); p
    -x**4 + 4*sqrt(7)*x**3 - 32*x**2 + 8*sqrt(7)*x + 20
    >>> p = _separate_sq(p); p
    -x**8 + 48*x**6 - 536*x**4 + 1728*x**2 - 400

    """
    from sympy.simplify.simplify import _split_gcd, _mexpand
    from sympy.utilities.iterables import sift
    def is_sqrt(expr):
        return expr.is_Pow and expr.exp is S.Half
    # p = c1*sqrt(q1) + ... + cn*sqrt(qn) -> a = [(c1, q1), .., (cn, qn)]
    a = []
    for y in p.args:
        if not y.is_Mul:
            if is_sqrt(y):
                a.append((S.One, y**2))
            elif y.is_Atom:
                a.append((y, S.One))
            elif y.is_Pow and y.exp.is_integer:
                a.append((y, S.One))
            else:
                raise NotImplementedError
            continue
        sifted = sift(y.args, is_sqrt)
        a.append((Mul(*sifted[False]), Mul(*sifted[True])**2))
    a.sort(key=lambda z: z[1])
    if a[-1][1] is S.One:
        # there are no surds
        return p
    surds = [z for y, z in a]
    for i in range(len(surds)):
        if surds[i] != 1:
            break
    g, b1, b2 = _split_gcd(*surds[i:])
    a1 = []
    a2 = []
    for y, z in a:
        if z in b1:
            a1.append(y*z**S.Half)
        else:
            a2.append(y*z**S.Half)
    p1 = Add(*a1)
    p2 = Add(*a2)
    p = _mexpand(p1**2) - _mexpand(p2**2)
    return p
Example #55
0
    # short entries will be ignored. The ORDER MATTERS
    # so don't re-order the lines for a given address.
    # Other tidying up could be done but we won't do that here.
    def short_entry(line):
        if line.count('<') == 2:
            if line.split('>', 1)[1].split('<')[0].strip():
                return False
        return True
    if len(who[k]) == 1:
        line = who[k][0]
        if not line.strip():
            continue  # ignore blank lines
        out.append(line)
    else:
        uniq = list(OrderedDict.fromkeys(who[k]))
        short, long = sift(uniq, short_entry, binary=True)
        out.extend(long)
        out.extend(short)

if out != lines or not blankline:
    # write lines
    with codecs.open(os.path.realpath(os.path.join(
            __file__, os.path.pardir, os.path.pardir, ".mailmap")),
            "w", "utf-8") as fd:
        fd.write('\n'.join(out))
        fd.write('\n')
    print()
    if out != lines:
        print(yellow('.mailmap lines were re-ordered.'))
    else:
        print(yellow('blank line added to end of .mailmap'))
Example #56
0
    def _matches_commutative(self, expr, repl_dict={}, old=False):
        """
        Matches Add/Mul "pattern" to an expression "expr".

        repl_dict ... a dictionary of (wild: expression) pairs, that get
                      returned with the results

        This function is the main workhorse for Add/Mul.

        For instance:

        >>> from sympy import symbols, Wild, sin
        >>> a = Wild("a")
        >>> b = Wild("b")
        >>> c = Wild("c")
        >>> x, y, z = symbols("x y z")
        >>> (a+sin(b)*c)._matches_commutative(x+sin(y)*z)
        {a_: x, b_: y, c_: z}

        In the example above, "a+sin(b)*c" is the pattern, and "x+sin(y)*z" is
        the expression.

        The repl_dict contains parts that were already matched. For example
        here:

        >>> (x+sin(b)*c)._matches_commutative(x+sin(y)*z, repl_dict={a: x})
        {a_: x, b_: y, c_: z}

        the only function of the repl_dict is to return it in the
        result, e.g. if you omit it:

        >>> (x+sin(b)*c)._matches_commutative(x+sin(y)*z)
        {b_: y, c_: z}

        the "a: x" is not returned in the result, but otherwise it is
        equivalent.

        """
        # make sure expr is Expr if pattern is Expr
        from .expr import Add, Expr
        from sympy import Mul
        if isinstance(self, Expr) and not isinstance(expr, Expr):
            return None

        # handle simple patterns
        if self == expr:
            return repl_dict

        d = self._matches_simple(expr, repl_dict)
        if d is not None:
            return d

        # eliminate exact part from pattern: (2+a+w1+w2).matches(expr) -> (w1+w2).matches(expr-a-2)
        from .function import WildFunction
        from .symbol import Wild
        wild_part, exact_part = sift(self.args, lambda p:
            p.has(Wild, WildFunction) and not expr.has(p),
            binary=True)
        if not exact_part:
            wild_part = list(ordered(wild_part))
        else:
            exact = self._new_rawargs(*exact_part)
            free = expr.free_symbols
            if free and (exact.free_symbols - free):
                # there are symbols in the exact part that are not
                # in the expr; but if there are no free symbols, let
                # the matching continue
                return None
            newexpr = self._combine_inverse(expr, exact)
            if not old and (expr.is_Add or expr.is_Mul):
                if newexpr.count_ops() > expr.count_ops():
                    return None
            newpattern = self._new_rawargs(*wild_part)
            return newpattern.matches(newexpr, repl_dict)

        # now to real work ;)
        i = 0
        saw = set()
        while expr not in saw:
            saw.add(expr)
            expr_list = (self.identity,) + tuple(ordered(self.make_args(expr)))
            for last_op in reversed(expr_list):
                for w in reversed(wild_part):
                    d1 = w.matches(last_op, repl_dict)
                    if d1 is not None:
                        d2 = self.xreplace(d1).matches(expr, d1)
                        if d2 is not None:
                            return d2

            if i == 0:
                if self.is_Mul:
                    # make e**i look like Mul
                    if expr.is_Pow and expr.exp.is_Integer:
                        if expr.exp > 0:
                            expr = Mul(*[expr.base, expr.base**(expr.exp - 1)], evaluate=False)
                        else:
                            expr = Mul(*[1/expr.base, expr.base**(expr.exp + 1)], evaluate=False)
                        i += 1
                        continue

                elif self.is_Add:
                    # make i*e look like Add
                    c, e = expr.as_coeff_Mul()
                    if abs(c) > 1:
                        if c > 0:
                            expr = Add(*[e, (c - 1)*e], evaluate=False)
                        else:
                            expr = Add(*[-e, (c + 1)*e], evaluate=False)
                        i += 1
                        continue

                    # try collection on non-Wild symbols
                    from sympy.simplify.radsimp import collect
                    was = expr
                    did = set()
                    for w in reversed(wild_part):
                        c, w = w.as_coeff_mul(Wild)
                        free = c.free_symbols - did
                        if free:
                            did.update(free)
                            expr = collect(expr, free)
                    if expr != was:
                        i += 0
                        continue

                break  # if we didn't continue, there is nothing more to do

        return