Ejemplo n.º 1
0
def test_distribute():

    assert distribute_and_over_or(Or(And(A, B), C)) == And(Or(A, C), Or(B, C))
    assert distribute_or_over_and(And(A, Or(B, C))) == Or(And(A, B), And(A, C))
Ejemplo n.º 2
0
    def _intervals(self, sym):
        """Return a list of unique tuples, (a, b, e, i), where a and b
        are the lower and upper bounds in which the expression e of
        argument i in self is defined and a < b (when involving
        numbers) or a <= b when involving symbols.

        If there are any relationals not involving sym, or any
        relational cannot be solved for sym, NotImplementedError is
        raised. The calling routine should have removed such
        relationals before calling this routine.

        The evaluated conditions will be returned as ranges.
        Discontinuous ranges will be returned separately with
        identical expressions. The first condition that evaluates to
        True will be returned as the last tuple with a, b = -oo, oo.
        """
        from sympy.solvers.inequalities import _solve_inequality
        from sympy.logic.boolalg import to_cnf, distribute_or_over_and

        assert isinstance(self, Piecewise)

        def _solve_relational(r):
            if sym not in r.free_symbols:
                nonsymfail(r)
            rv = _solve_inequality(r, sym)
            if isinstance(rv, Relational):
                free = rv.args[1].free_symbols
                if rv.args[0] != sym or sym in free:
                    raise NotImplementedError(
                        filldedent('''
                        Unable to solve relational
                        %s for %s.''' % (r, sym)))
                if rv.rel_op == '==':
                    # this equality has been affirmed to have the form
                    # Eq(sym, rhs) where rhs is sym-free; it represents
                    # a zero-width interval which will be ignored
                    # whether it is an isolated condition or contained
                    # within an And or an Or
                    rv = S.false
                elif rv.rel_op == '!=':
                    try:
                        rv = Or(sym < rv.rhs, sym > rv.rhs)
                    except TypeError:
                        # e.g. x != I ==> all real x satisfy
                        rv = S.true
            elif rv == (S.NegativeInfinity < sym) & (sym < S.Infinity):
                rv = S.true
            return rv

        def nonsymfail(cond):
            raise NotImplementedError(
                filldedent('''
                A condition not involving
                %s appeared: %s''' % (sym, cond)))

        # make self canonical wrt Relationals
        reps = dict([(r, _solve_relational(r))
                     for r in self.atoms(Relational)])
        # process args individually so if any evaluate, their position
        # in the original Piecewise will be known
        args = [i.xreplace(reps) for i in self.args]

        # precondition args
        expr_cond = []
        default = idefault = None
        for i, (expr, cond) in enumerate(args):
            if cond is S.false:
                continue
            elif cond is S.true:
                default = expr
                idefault = i
                break

            cond = to_cnf(cond)
            if isinstance(cond, And):
                cond = distribute_or_over_and(cond)

            if isinstance(cond, Or):
                expr_cond.extend([(i, expr, o) for o in cond.args
                                  if not isinstance(o, Equality)])
            elif cond is not S.false:
                expr_cond.append((i, expr, cond))

        # determine intervals represented by conditions
        int_expr = []
        for iarg, expr, cond in expr_cond:
            if isinstance(cond, And):
                lower = S.NegativeInfinity
                upper = S.Infinity
                for cond2 in cond.args:
                    if isinstance(cond2, Equality):
                        lower = upper  # ignore
                        break
                    elif cond2.lts == sym:
                        upper = Min(cond2.gts, upper)
                    elif cond2.gts == sym:
                        lower = Max(cond2.lts, lower)
                    else:
                        nonsymfail(cond2)  # should never get here
            elif isinstance(cond, Relational):
                lower, upper = cond.lts, cond.gts  # part 1: initialize with givens
                if cond.lts == sym:  # part 1a: expand the side ...
                    lower = S.NegativeInfinity  # e.g. x <= 0 ---> -oo <= 0
                elif cond.gts == sym:  # part 1a: ... that can be expanded
                    upper = S.Infinity  # e.g. x >= 0 --->  oo >= 0
                else:
                    nonsymfail(cond)
            else:
                raise NotImplementedError('unrecognized condition: %s' % cond)

            lower, upper = lower, Max(lower, upper)
            if (lower >= upper) is not S.true:
                int_expr.append((lower, upper, expr, iarg))

        if default is not None:
            int_expr.append(
                (S.NegativeInfinity, S.Infinity, default, idefault))

        return list(uniq(int_expr))
Ejemplo n.º 3
0
def test_distribute():
    assert distribute_and_over_or(Or(And(A, B), C)) == And(Or(A, C), Or(B, C))
    assert distribute_or_over_and(And(A, Or(B, C))) == Or(And(A, B), And(A, C))
Ejemplo n.º 4
0
    def _intervals(self, sym):
        """Return a list of unique tuples, (a, b, e, i), where a and b
        are the lower and upper bounds in which the expression e of
        argument i in self is defined and a < b (when involving
        numbers) or a <= b when involving symbols.

        If there are any relationals not involving sym, or any
        relational cannot be solved for sym, NotImplementedError is
        raised. The calling routine should have removed such
        relationals before calling this routine.

        The evaluated conditions will be returned as ranges.
        Discontinuous ranges will be returned separately with
        identical expressions. The first condition that evaluates to
        True will be returned as the last tuple with a, b = -oo, oo.
        """
        from sympy.solvers.inequalities import _solve_inequality
        from sympy.logic.boolalg import to_cnf, distribute_or_over_and

        assert isinstance(self, Piecewise)

        def _solve_relational(r):
            if sym not in r.free_symbols:
                nonsymfail(r)
            rv = _solve_inequality(r, sym)
            if isinstance(rv, Relational):
                free = rv.args[1].free_symbols
                if rv.args[0] != sym or sym in free:
                    raise NotImplementedError(filldedent('''
                        Unable to solve relational
                        %s for %s.''' % (r, sym)))
                if rv.rel_op == '==':
                    # this equality has been affirmed to have the form
                    # Eq(sym, rhs) where rhs is sym-free; it represents
                    # a zero-width interval which will be ignored
                    # whether it is an isolated condition or contained
                    # within an And or an Or
                    rv = S.false
                elif rv.rel_op == '!=':
                    try:
                        rv = Or(sym < rv.rhs, sym > rv.rhs)
                    except TypeError:
                        # e.g. x != I ==> all real x satisfy
                        rv = S.true
            elif rv == (S.NegativeInfinity < sym) & (sym < S.Infinity):
                rv = S.true
            return rv

        def nonsymfail(cond):
            raise NotImplementedError(filldedent('''
                A condition not involving
                %s appeared: %s''' % (sym, cond)))

        # make self canonical wrt Relationals
        reps = dict([
            (r, _solve_relational(r)) for r in self.atoms(Relational)])
        # process args individually so if any evaluate, their position
        # in the original Piecewise will be known
        args = [i.xreplace(reps) for i in self.args]

        # precondition args
        expr_cond = []
        default = idefault = None
        for i, (expr, cond) in enumerate(args):
            if cond is S.false:
                continue
            elif cond is S.true:
                default = expr
                idefault = i
                break

            cond = to_cnf(cond)
            if isinstance(cond, And):
                cond = distribute_or_over_and(cond)

            if isinstance(cond, Or):
                expr_cond.extend(
                    [(i, expr, o) for o in cond.args
                    if not isinstance(o, Equality)])
            elif cond is not S.false:
                expr_cond.append((i, expr, cond))

        # determine intervals represented by conditions
        int_expr = []
        for iarg, expr, cond in expr_cond:
            if isinstance(cond, And):
                lower = S.NegativeInfinity
                upper = S.Infinity
                for cond2 in cond.args:
                    if isinstance(cond2, Equality):
                        lower = upper  # ignore
                        break
                    elif cond2.lts == sym:
                        upper = Min(cond2.gts, upper)
                    elif cond2.gts == sym:
                        lower = Max(cond2.lts, lower)
                    else:
                        nonsymfail(cond2)  # should never get here
            elif isinstance(cond, Relational):
                lower, upper = cond.lts, cond.gts  # part 1: initialize with givens
                if cond.lts == sym:                # part 1a: expand the side ...
                    lower = S.NegativeInfinity   # e.g. x <= 0 ---> -oo <= 0
                elif cond.gts == sym:            # part 1a: ... that can be expanded
                    upper = S.Infinity           # e.g. x >= 0 --->  oo >= 0
                else:
                    nonsymfail(cond)
            else:
                raise NotImplementedError(
                    'unrecognized condition: %s' % cond)

            lower, upper = lower, Max(lower, upper)
            if (lower >= upper) is not S.true:
                int_expr.append((lower, upper, expr, iarg))

        if default is not None:
            int_expr.append(
                (S.NegativeInfinity, S.Infinity, default, idefault))

        return list(uniq(int_expr))
Ejemplo n.º 5
0
    def _intervals(self, sym):
        """Return a list of tuples, (a, b, e, i), where a and b
        are the lower and upper bounds in which the expression e
        of argument i in self is defined.

        If there are any relationals not involving sym, or
        any relational cannot be solved for sym,
        NotImplementedError is raised. The evaluated conditions will
        be returned as ranges. Discontinuous ranges will be
        returned separately with identical expressions and
        the first condition that evaluates to True will be
        returned as the last tuple with a, b = -oo, oo.
        """
        from sympy.solvers.inequalities import _solve_inequality
        from sympy.logic.boolalg import to_cnf, distribute_or_over_and

        assert isinstance(self, Piecewise)

        def _solve_relational(r):
            rv = _solve_inequality(r, sym)
            if isinstance(rv, Relational) and \
                    sym in rv.free_symbols:
                if rv.args[0] != sym:
                    raise NotImplementedError(
                        filldedent('''
                        Unable to solve relational
                        %s for %s.''' % (r, sym)))
                if rv.rel_op == '!=':
                    try:
                        rv = Or(sym < rv.rhs, sym > rv.rhs)
                    except TypeError:
                        # e.g. x != I ==> all real x satisfy
                        rv = S.true
            if rv == (S.NegativeInfinity < sym) & (sym < S.Infinity):
                rv = S.true
            return rv

        def nonsymfail(cond):
            raise NotImplementedError(
                filldedent('''
                A condition not involving
                %s appeared: %s''' % (sym, cond)))

        # make self canonical wrt Relationals
        reps = dict([(r, _solve_relational(r))
                     for r in self.atoms(Relational)])
        # process args individually so if any evaluate, their position
        # in the original Piecewise will be known
        args = [i.xreplace(reps) for i in self.args]

        # precondition args
        expr_cond = []
        default = idefault = None
        for i, (expr, cond) in enumerate(args):
            if cond == False:
                continue
            elif cond == True:
                default = expr
                idefault = i
                break
            elif sym not in cond.free_symbols:
                nonsymfail(cond)

            cond = to_cnf(cond)
            if isinstance(cond, And):
                cond = distribute_or_over_and(cond)

            if isinstance(cond, Or):
                expr_cond.extend([(i, expr, o) for o in cond.args])
            elif cond != False:
                expr_cond.append((i, expr, cond))

        # determine intervals represented by conditions
        int_expr = []
        for iarg, expr, cond in expr_cond:
            if isinstance(cond, Equality):
                if cond.lhs == sym:
                    v = cond.rhs
                    int_expr.append((v, v, expr.subs(sym, v), iarg))
                    continue
                else:
                    nonsymfail(cond)

            if isinstance(cond, And):
                lower = S.NegativeInfinity
                upper = S.Infinity
                nonsym = False
                rhs = None
                for cond2 in cond.args:
                    if isinstance(cond2, Equality):
                        # all relationals were solved and
                        # only sym-dependent ones made it to here
                        assert cond2.lhs == sym
                        assert sym not in cond2.rhs.free_symbols
                        if rhs is None:
                            rhs = cond2.rhs
                        else:
                            same = Eq(cond2.rhs, rhs)
                            if same == False:
                                cond = S.false
                                break
                            elif same != True:
                                raise Undecidable('%s did not evaluate' % same)
                    elif cond2.lts == sym:
                        upper = Min(cond2.gts, upper)
                    elif cond2.gts == sym:
                        lower = Max(cond2.lts, lower)
                    else:
                        # mark but don't fail until later
                        nonsym = cond2
                if rhs is not None and cond not in (S.true, S.false):
                    lower = upper = rhs
                    cond = cond.subs(sym, lower)
                # cond might have evaluated b/c of an Eq
                if cond is S.true:
                    default = expr
                    continue
                elif cond is S.false:
                    continue
                elif nonsym is not False:
                    nonsymfail(nonsym)
                else:
                    pass  # for coverage
            elif isinstance(cond, Relational):
                lower, upper = cond.lts, cond.gts  # part 1: initialize with givens
                if cond.lts == sym:  # part 1a: expand the side ...
                    lower = S.NegativeInfinity  # e.g. x <= 0 ---> -oo <= 0
                elif cond.gts == sym:  # part 1a: ... that can be expanded
                    upper = S.Infinity  # e.g. x >= 0 --->  oo >= 0
                else:
                    nonsymfail(cond)
            else:
                raise NotImplementedError('unrecognized condition: %s' % cond)

            lower, upper = lower, Max(lower, upper)
            if (lower > upper) is not S.true:
                int_expr.append((lower, upper, expr, iarg))

        if default is not None:
            int_expr.append(
                (S.NegativeInfinity, S.Infinity, default, idefault))

        return int_expr
Ejemplo n.º 6
0
    def _intervals(self, sym):
        """Return a list of tuples, (a, b, e, i), where a and b
        are the lower and upper bounds in which the expression e
        of argument i in self is defined.

        If there are any relationals not involving sym, or
        any relational cannot be solved for sym,
        NotImplementedError is raised. The evaluated conditions will
        be returned as ranges. Discontinuous ranges will be
        returned separately with identical expressions and
        the first condition that evaluates to True will be
        returned as the last tuple with a, b = -oo, oo.
        """
        from sympy.solvers.inequalities import _solve_inequality
        from sympy.logic.boolalg import to_cnf, distribute_or_over_and

        assert isinstance(self, Piecewise)

        def _solve_relational(r):
            rv = _solve_inequality(r, sym)
            if isinstance(rv, Relational) and \
                    sym in rv.free_symbols:
                if rv.args[0] != sym:
                    raise NotImplementedError(filldedent('''
                        Unable to solve relational
                        %s for %s.''' % (r, sym)))
                if rv.rel_op == '!=':
                    try:
                        rv = Or(sym < rv.rhs, sym > rv.rhs)
                    except TypeError:
                        # e.g. x != I ==> all real x satisfy
                        rv = S.true
            if rv == (S.NegativeInfinity < sym) & (sym < S.Infinity):
                rv = S.true
            return rv

        def nonsymfail(cond):
            raise NotImplementedError(filldedent('''
                A condition not involving
                %s appeared: %s''' % (sym, cond)))

        # make self canonical wrt Relationals
        reps = dict(
            [(r,_solve_relational(r)) for r in self.atoms(Relational)])
        # process args individually so if any evaluate, their position
        # in the original Piecewise will be known
        args = [i.xreplace(reps) for i in self.args]

        # precondition args
        expr_cond = []
        default = idefault = None
        for i, (expr, cond) in enumerate(args):
            if cond == False:
                continue
            elif cond == True:
                default = expr
                idefault = i
                break
            elif sym not in cond.free_symbols:
                nonsymfail(cond)

            cond = to_cnf(cond)
            if isinstance(cond, And):
                cond = distribute_or_over_and(cond)

            if isinstance(cond, Or):
                expr_cond.extend(
                    [(i, expr, o) for o in cond.args])
            elif cond != False:
                expr_cond.append((i, expr, cond))

        # determine intervals represented by conditions
        int_expr = []
        for iarg, expr, cond in expr_cond:
            if isinstance(cond, Equality):
                if cond.lhs == sym:
                    v = cond.rhs
                    int_expr.append((v, v, expr.subs(sym, v), iarg))
                    continue
                else:
                    nonsymfail(cond)

            if isinstance(cond, And):
                lower = S.NegativeInfinity
                upper = S.Infinity
                nonsym = False
                rhs = None
                for cond2 in cond.args:
                    if isinstance(cond2, Equality):
                        # all relationals were solved and
                        # only sym-dependent ones made it to here
                        assert cond2.lhs == sym
                        assert sym not in cond2.rhs.free_symbols
                        if rhs is None:
                            rhs = cond2.rhs
                        else:
                            same = Eq(cond2.rhs, rhs)
                            if same == False:
                                cond = S.false
                                break
                            elif same != True:
                                raise Undecidable('%s did not evaluate' % same)
                    elif cond2.lts == sym:
                        upper = Min(cond2.gts, upper)
                    elif cond2.gts == sym:
                        lower = Max(cond2.lts, lower)
                    else:
                        # mark but don't fail until later
                        nonsym = cond2
                if rhs is not None and cond not in (S.true, S.false):
                    lower = upper = rhs
                    cond = cond.subs(sym, lower)
                # cond might have evaluated b/c of an Eq
                if cond is S.true:
                    default = expr
                    continue
                elif cond is S.false:
                    continue
                elif nonsym is not False:
                    nonsymfail(nonsym)
                else:
                    pass  # for coverage
            elif isinstance(cond, Relational):
                lower, upper = cond.lts, cond.gts  # part 1: initialize with givens
                if cond.lts == sym:                # part 1a: expand the side ...
                    lower = S.NegativeInfinity   # e.g. x <= 0 ---> -oo <= 0
                elif cond.gts == sym:            # part 1a: ... that can be expanded
                    upper = S.Infinity           # e.g. x >= 0 --->  oo >= 0
                else:
                    nonsymfail(cond)
            else:
                raise NotImplementedError(
                    'unrecognized condition: %s' % cond)

            lower, upper = lower, Max(lower, upper)
            if (lower > upper) is not S.true:
                int_expr.append((lower, upper, expr, iarg))

        if default is not None:
            int_expr.append(
                (S.NegativeInfinity, S.Infinity, default, idefault))

        return int_expr