Ejemplo n.º 1
0
 def _eval_rewrite_as_ITE(self, *args, **kwargs):
     byfree = {}
     args = list(args)
     default = any(c == True for b, c in args)
     for i, (b, c) in enumerate(args):
         if not isinstance(b, Boolean) and b != True:
             raise TypeError(
                 filldedent('''
                 Expecting Boolean or bool but got `%s`
                 ''' % func_name(b)))
         if c == True:
             break
         # loop over independent conditions for this b
         for c in c.args if isinstance(c, Or) else [c]:
             free = c.free_symbols
             x = free.pop()
             try:
                 byfree[x] = byfree.setdefault(x,
                                               S.EmptySet).union(c.as_set())
             except NotImplementedError:
                 if not default:
                     raise NotImplementedError(
                         filldedent('''
                         A method to determine whether a multivariate
                         conditional is consistent with a complete coverage
                         of all variables has not been implemented so the
                         rewrite is being stopped after encountering `%s`.
                         This error would not occur if a default expression
                         like `(foo, True)` were given.
                         ''' % c))
             if byfree[x] in (S.UniversalSet, S.Reals):
                 # collapse the ith condition to True and break
                 args[i] = list(args[i])
                 c = args[i][1] = True
                 break
         if c == True:
             break
     if c != True:
         raise ValueError(
             filldedent('''
             Conditions must cover all reals or a final default
             condition `(foo, True)` must be given.
             '''))
     last, _ = args[i]  # ignore all past ith arg
     for a, c in reversed(args[:i]):
         last = ITE(c, a, last)
     return _canonical(last)
Ejemplo n.º 2
0
 def _eval_rewrite_as_ITE(self, *args):
     byfree = {}
     args = list(args)
     default = any(c == True for b, c in args)
     for i, (b, c) in enumerate(args):
         if not isinstance(b, Boolean) and b != True:
             raise TypeError(filldedent('''
                 Expecting Boolean or bool but got `%s`
                 ''' % func_name(b)))
         if c == True:
             break
         # loop over independent conditions for this b
         for c in c.args if isinstance(c, Or) else [c]:
             free = c.free_symbols
             x = free.pop()
             try:
                 byfree[x] = byfree.setdefault(
                     x, S.EmptySet).union(c.as_set())
             except NotImplementedError:
                 if not default:
                     raise NotImplementedError(filldedent('''
                         A method to determine whether a multivariate
                         conditional is consistent with a complete coverage
                         of all variables has not been implemented so the
                         rewrite is being stopped after encountering `%s`.
                         This error would not occur if a default expression
                         like `(foo, True)` were given.
                         ''' % c))
             if byfree[x] in (S.UniversalSet, S.Reals):
                 # collapse the ith condition to True and break
                 args[i] = list(args[i])
                 c = args[i][1] = True
                 break
         if c == True:
             break
     if c != True:
         raise ValueError(filldedent('''
             Conditions must cover all reals or a final default
             condition `(foo, True)` must be given.
             '''))
     last, _ = args[i]  # ignore all past ith arg
     for a, c in reversed(args[:i]):
         last = ITE(c, a, last)
     return _canonical(last)
Ejemplo n.º 3
0
    def eval(cls, *_args):
        """Either return a modified version of the args or, if no
        modifications were made, return None.

        Modifications that are made here:
        1) relationals are made canonical
        2) any False conditions are dropped
        3) any repeat of a previous condition is ignored
        3) any args past one with a true condition are dropped

        If there are no args left, nan will be returned.
        If there is a single arg with a True condition, its
        corresponding expression will be returned.
        """
        from sympy.functions.elementary.complexes import im, re

        if not _args:
            return Undefined

        if len(_args) == 1 and _args[0][-1] == True:
            return _args[0][0]

        newargs = []  # the unevaluated conditions
        current_cond = set()  # the conditions up to a given e, c pair
        # make conditions canonical
        args = []
        for e, c in _args:
            if (not c.is_Atom and not isinstance(c, Relational)
                    and not c.has(im, re)):
                free = c.free_symbols
                if len(free) == 1:
                    funcs = [
                        i for i in c.atoms(Function)
                        if not isinstance(i, Boolean)
                    ]
                    if len(funcs) == 1 and len(
                            c.xreplace({
                                list(funcs)[0]: Dummy()
                            }).free_symbols) == 1:
                        # we can treat function like a symbol
                        free = funcs
                    _c = c
                    x = free.pop()
                    try:
                        c = c.as_set().as_relational(x)
                    except NotImplementedError:
                        pass
                    else:
                        reps = {}
                        for i in c.atoms(Relational):
                            ic = i.canonical
                            if ic.rhs in (S.Infinity, S.NegativeInfinity):
                                if not _c.has(ic.rhs):
                                    # don't accept introduction of
                                    # new Relationals with +/-oo
                                    reps[i] = S.true
                                elif ('=' not in ic.rel_op and c.xreplace(
                                    {x: i.rhs}) != _c.xreplace({x: i.rhs})):
                                    reps[i] = Relational(
                                        i.lhs, i.rhs, i.rel_op + '=')
                        c = c.xreplace(reps)
            args.append((e, _canonical(c)))

        for expr, cond in args:
            # Check here if expr is a Piecewise and collapse if one of
            # the conds in expr matches cond. This allows the collapsing
            # of Piecewise((Piecewise((x,x<0)),x<0)) to Piecewise((x,x<0)).
            # This is important when using piecewise_fold to simplify
            # multiple Piecewise instances having the same conds.
            # Eventually, this code should be able to collapse Piecewise's
            # having different intervals, but this will probably require
            # using the new assumptions.
            if isinstance(expr, Piecewise):
                unmatching = []
                for i, (e, c) in enumerate(expr.args):
                    if c in current_cond:
                        # this would already have triggered
                        continue
                    if c == cond:
                        if c != True:
                            # nothing past this condition will ever
                            # trigger and only those args before this
                            # that didn't match a previous condition
                            # could possibly trigger
                            if unmatching:
                                expr = Piecewise(*(unmatching + [(e, c)]))
                            else:
                                expr = e
                        break
                    else:
                        unmatching.append((e, c))

            # check for condition repeats
            got = False
            # -- if an And contains a condition that was
            #    already encountered, then the And will be
            #    False: if the previous condition was False
            #    then the And will be False and if the previous
            #    condition is True then then we wouldn't get to
            #    this point. In either case, we can skip this condition.
            for i in ([cond] +
                      (list(cond.args) if isinstance(cond, And) else [])):
                if i in current_cond:
                    got = True
                    break
            if got:
                continue

            # -- if not(c) is already in current_cond then c is
            #    a redundant condition in an And. This does not
            #    apply to Or, however: (e1, c), (e2, Or(~c, d))
            #    is not (e1, c), (e2, d) because if c and d are
            #    both False this would give no results when the
            #    true answer should be (e2, True)
            if isinstance(cond, And):
                nonredundant = []
                for c in cond.args:
                    if (isinstance(c, Relational)
                            and c.negated.canonical in current_cond):
                        continue
                    nonredundant.append(c)
                cond = cond.func(*nonredundant)
            elif isinstance(cond, Relational):
                if cond.negated.canonical in current_cond:
                    cond = S.true

            current_cond.add(cond)

            # collect successive e,c pairs when exprs or cond match
            if newargs:
                if newargs[-1].expr == expr:
                    orcond = Or(cond, newargs[-1].cond)
                    if isinstance(orcond, (And, Or)):
                        orcond = distribute_and_over_or(orcond)
                    newargs[-1] = ExprCondPair(expr, orcond)
                    continue
                elif newargs[-1].cond == cond:
                    newargs[-1] = ExprCondPair(expr, cond)
                    continue

            newargs.append(ExprCondPair(expr, cond))

        # some conditions may have been redundant
        missing = len(newargs) != len(_args)
        # some conditions may have changed
        same = all(a == b for a, b in zip(newargs, _args))
        # if either change happened we return the expr with the
        # updated args
        if not newargs:
            raise ValueError(
                filldedent('''
                There are no conditions (or none that
                are not trivially false) to define an
                expression.'''))
        if missing or not same:
            return cls(*newargs)
Ejemplo n.º 4
0
    def eval(cls, *_args):
        """Either return a modified version of the args or, if no
        modifications were made, return None.

        Modifications that are made here:
        1) relationals are made canonical
        2) any False conditions are dropped
        3) any repeat of a previous condition is ignored
        3) any args past one with a true condition are dropped

        If there are no args left, nan will be returned.
        If there is a single arg with a True condition, its
        corresponding expression will be returned.
        """

        if not _args:
            return Undefined

        if len(_args) == 1 and _args[0][-1] == True:
            return _args[0][0]

        newargs = []  # the unevaluated conditions
        current_cond = set()  # the conditions up to a given e, c pair
        # make conditions canonical
        args = []
        for e, c in _args:
            if not c.is_Atom and not isinstance(c, Relational):
                free = c.free_symbols
                if len(free) == 1:
                    funcs = [i for i in c.atoms(Function)
                        if not isinstance(i, Boolean)]
                    if len(funcs) == 1 and len(
                            c.xreplace({list(funcs)[0]: Dummy()}
                            ).free_symbols) == 1:
                        # we can treat function like a symbol
                        free = funcs
                    _c = c
                    x = free.pop()
                    try:
                        c = c.as_set().as_relational(x)
                    except NotImplementedError:
                        pass
                    else:
                        reps = {}
                        for i in c.atoms(Relational):
                            ic = i.canonical
                            if ic.rhs in (S.Infinity, S.NegativeInfinity):
                                if not _c.has(ic.rhs):
                                    # don't accept introduction of
                                    # new Relationals with +/-oo
                                    reps[i] = S.true
                                elif ('=' not in ic.rel_op and
                                        c.xreplace({x: i.rhs}) !=
                                        _c.xreplace({x: i.rhs})):
                                    reps[i] = Relational(
                                        i.lhs, i.rhs, i.rel_op + '=')
                        c = c.xreplace(reps)
            args.append((e, _canonical(c)))

        for expr, cond in args:
            # Check here if expr is a Piecewise and collapse if one of
            # the conds in expr matches cond. This allows the collapsing
            # of Piecewise((Piecewise((x,x<0)),x<0)) to Piecewise((x,x<0)).
            # This is important when using piecewise_fold to simplify
            # multiple Piecewise instances having the same conds.
            # Eventually, this code should be able to collapse Piecewise's
            # having different intervals, but this will probably require
            # using the new assumptions.
            if isinstance(expr, Piecewise):
                unmatching = []
                for i, (e, c) in enumerate(expr.args):
                    if c in current_cond:
                        # this would already have triggered
                        continue
                    if c == cond:
                        if c != True:
                            # nothing past this condition will ever
                            # trigger and only those args before this
                            # that didn't match a previous condition
                            # could possibly trigger
                            if unmatching:
                                expr = Piecewise(*(
                                    unmatching + [(e, c)]))
                            else:
                                expr = e
                        break
                    else:
                        unmatching.append((e, c))

            # check for condition repeats
            got = False
            # -- if an And contains a condition that was
            #    already encountered, then the And will be
            #    False: if the previous condition was False
            #    then the And will be False and if the previous
            #    condition is True then then we wouldn't get to
            #    this point. In either case, we can skip this condition.
            for i in ([cond] +
                    (list(cond.args) if isinstance(cond, And) else
                    [])):
                if i in current_cond:
                    got = True
                    break
            if got:
                continue

            # -- if not(c) is already in current_cond then c is
            #    a redundant condition in an And. This does not
            #    apply to Or, however: (e1, c), (e2, Or(~c, d))
            #    is not (e1, c), (e2, d) because if c and d are
            #    both False this would give no results when the
            #    true answer should be (e2, True)
            if isinstance(cond, And):
                nonredundant = []
                for c in cond.args:
                    if (isinstance(c, Relational) and
                            (~c).canonical in current_cond):
                        continue
                    nonredundant.append(c)
                cond = cond.func(*nonredundant)
            elif isinstance(cond, Relational):
                if (~cond).canonical in current_cond:
                    cond = S.true

            current_cond.add(cond)

            # collect successive e,c pairs when exprs or cond match
            if newargs:
                if newargs[-1].expr == expr:
                    orcond = Or(cond, newargs[-1].cond)
                    if isinstance(orcond, (And, Or)):
                        orcond = distribute_and_over_or(orcond)
                    newargs[-1] = ExprCondPair(expr, orcond)
                    continue
                elif newargs[-1].cond == cond:
                    orexpr = Or(expr, newargs[-1].expr)
                    if isinstance(orexpr, (And, Or)):
                        orexpr = distribute_and_over_or(orexpr)
                    newargs[-1] == ExprCondPair(orexpr, cond)
                    continue

            newargs.append(ExprCondPair(expr, cond))

        # some conditions may have been redundant
        missing = len(newargs) != len(_args)
        # some conditions may have changed
        same = all(a == b for a, b in zip(newargs, _args))
        # if either change happened we return the expr with the
        # updated args
        if not newargs:
            raise ValueError(filldedent('''
                There are no conditions (or none that
                are not trivially false) to define an
                expression.'''))
        if missing or not same:
            return cls(*newargs)