Example #1
0
from proveit.logic import Forall, Or, Equals, Implies
from proveit.number import Reals
from proveit.number import Less, LessEq, Greater, GreaterEq
from proveit.common import x, y, z
from proveit import beginAxioms, endAxioms

beginAxioms(locals())

lessThanEqualsDef = Forall([x, y],
                           Or(Less(x, y), Equals(x, y)),
                           domain=Reals,
                           conditions=LessEq(x, y))
lessThanEqualsDef

greaterThanEqualsDef = Forall([x, y],
                              Or(Greater(x, y), Equals(x, y)),
                              domain=Reals,
                              conditions=GreaterEq(x, y))
greaterThanEqualsDef

reverseGreaterThanEquals = Forall((x, y), Implies(GreaterEq(x, y),
                                                  LessEq(y, x)))
reverseGreaterThanEquals

reverseLessThanEquals = Forall((x, y), Implies(LessEq(x, y), GreaterEq(y, x)))
reverseLessThanEquals

reverseGreaterThan = Forall((x, y), Implies(Greater(x, y), Less(y, x)))
reverseGreaterThan

reverseLessThan = Forall((x, y), Implies(Less(x, y), Greater(y, x)))
Example #2
0
def deducePositive(exprOrList, assumptions=frozenset(), dontTryRealsPos=False):
    '''
    For each given expression, attempt to derive that it is positive
    under the given assumptions.  If successful, returns the deduced statement,
    otherwise raises an Exception.  
    '''
    from proveit.number import Greater, num

    if not isinstance(assumptions, set) and not isinstance(
            assumptions, frozenset):
        raise Exception('assumptions should be a set')

    if not isinstance(exprOrList, Expression) or isinstance(
            exprOrList, ExprTuple):
        # If it isn't an Expression, assume it's iterable and deduce each
        return [
            deducePositive(expr, assumptions=assumptions)
            for expr in exprOrList
        ]
    # A single Expression:
    expr = exprOrList
    try:
        # may be done before we started
        return Greater(expr, num(0)).checked(assumptions)
    except:
        pass  # not so simple

    if not dontTryRealsPos:
        try:
            # see if we can deduce in RealsPos first
            deduceInNumberSet(expr, RealsPos, assumptions, dontTryPos=True)
            inRealsPos = True
        except:
            inRealsPos = False  # not so simple
        if inRealsPos:
            deduceInReals(expr, assumptions)
            return real.theorems.inRealsPos_iff_positive.specialize({
                a: expr
            }).deriveRight().checked(assumptions)

    # Try using positiveTheorem
    if not isinstance(expr, NumberOp):
        # See of the Expression class has deduceNotZero method (as a last resort):
        if hasattr(expr, 'deducePositive'):
            return expr.deducePositive()
        raise DeducePositiveException(expr, assumptions)
    positiveThm = expr._positiveTheorem()
    if positiveThm is None:
        raise DeducePositiveException(expr, assumptions)
    assert isinstance(
        positiveThm,
        Forall), 'Expecting deduce positive theorem to be a Forall expression'
    iVars = positiveThm.instanceVar
    # Specialize the closure theorem differently for AccociativeOperation compared with other cases
    if isinstance(expr, AssociativeOperation):
        assert len(
            iVars
        ) == 1, 'Expecting one instance variables for the positive theorem of an AssociativeOperation'
        assert isinstance(
            iVars[0], Etcetera
        ), 'Expecting the instance variable for the positive theorem of an AssociativeOperation to be an Etcetera Variable'
        positiveSpec = positiveThm.specialize({iVars[0]: expr.operands})
    else:
        if len(iVars) != len(expr.operands):
            raise Exception(
                'Expecting the number of instance variables for the closure theorem to be the same as the number of operands of the Expression'
            )
        positiveSpec = positiveThm.specialize(
            {iVar: operand
             for iVar, operand in zip(iVars, expr.operands)})
    # deduce any of the requirements for the notEqZeroThm application
    _deduceRequirements(positiveThm, positiveSpec, assumptions)
    try:
        return GreaterThan(expr, num(0)).checked(assumptions)
    except:
        raise DeducePositiveException(expr, assumptions)
Example #3
0
    def entryRanges(self, base, start_indices, end_indices, assumptions,
                    requirements):
        '''
        For each entry of the tensor that is fully or partially contained in the window defined
        via start_indices and end_indices (as Expressions that can be provably sorted
        against tensor coordinates), yield the start and end of the intersection of the
        entry range and the window.
        '''

        from proveit.number import Less, Greater
        if requirements is None:
            requirements = []  # requirements won't be passed back in this case

        # For each axis, obtain the sorted coordinates of the substituted tensor,
        # insert the start and end indices for the desired range, and determine
        # the starting and ending locations relative to operator positions of the
        # expanded sorting relations.
        coord_sorting_relations = [
        ]  # expanded sorting relations (including start and end indices) along each axis
        rel_start_loc = [
        ]  # start location relative to the new sorting locations along each axis
        rel_end_loc = [
        ]  # end location relative to the new sorting locations along each axis
        for axis in range(self.ndims):  # for each axis
            start_index = start_indices[axis]
            end_index = end_indices[axis]

            sorted_coords = self.sortedCoordLists[axis]
            # insert the start_index and the end_index into the sorted list of coordinates in their proper places
            coord_sorting_relation = Less.sort(sorted_coords +
                                               [start_index, end_index],
                                               assumptions=assumptions)
            # get the relative start and end integer coordinates
            rel_start_loc.append(
                coord_sorting_relation.operands.index(start_index))
            rel_end_loc.append(
                coord_sorting_relation.operands.index(end_index))
            # remember these sorting relations
            coord_sorting_relations.append(coord_sorting_relation)

        # For each entry of the substituted tensor, determine if it is within the start/end
        # "window".  If so, yield the intersected range in terms of parameter values
        # (inverted from the tensor coordinates).  Keep track of the requirements.
        for rel_loc_in_tensor, entry in self.items():
            # convert from the relative location within the tensor to the
            # tensor location in absolute coordinates.
            entry_start = self.tensorLoc(rel_loc_in_tensor)
            entry_end = self.endCorner(rel_loc_in_tensor)

            # convert from the absolute tensor location to the relative
            # location w.r.t. the  coord_sorting_relations that include
            # the startArgs and endArgs of the window.
            rel_entry_start = [
                coord_sorting_relation.index(coord)
                for coord, coord_sorting_relation in zip(
                    entry_start, coord_sorting_relations)
            ]
            rel_entry_end = [
                coord_sorting_relation.index(coord)
                for coord, coord_sorting_relation in zip(
                    entry_end, coord_sorting_relations)
            ]

            # get the intersection of the entry range and the considered window,
            rel_intersection_start = [
                max(a, b) for a, b in zip(rel_start_loc, rel_entry_start)
            ]
            rel_intersection_end = [
                min(a, b) for a, b in zip(rel_end_loc, rel_entry_end)
            ]

            # translate the intersection region to absolute coordinates
            intersection_start = [
                coord_sorting_relation.operands[i]
                for i, coord_sorting_relation in zip(rel_intersection_start,
                                                     coord_sorting_relations)
            ]
            intersection_end = [
                coord_sorting_relation.operands[i]
                for i, coord_sorting_relation in zip(rel_intersection_end,
                                                     coord_sorting_relations)
            ]

            if any(a > b for a, b in zip(rel_intersection_start,
                                         rel_intersection_end)):
                # empty intersection, but we need to include requirements that prove this.
                for axis, (a, b) in enumerate(
                        zip(rel_intersection_start, rel_intersection_end)):
                    if a > b:
                        # add the requirements showing the intersection is empty along the first such axis.
                        coord_sorting_relation = coord_sorting_relations[axis]
                        aCoord, bCoord = coord_sorting_relation.operands[
                            a], coord_sorting_relation.operands[b]
                        empty_intersection_relation = Greater.sort(
                            [aCoord, bCoord], assumptions=assumptions)
                        requirements.append(empty_intersection_relation)
            else:
                # There is a non-empty intersection rectangle to yield for a particular entry.

                # Let's get the requirements that prove the intersection:
                for axis, (a, b, c, d, e, f) in enumerate(
                        zip(rel_intersection_start, rel_intersection_end,
                            rel_start_loc, rel_entry_start, rel_end_loc,
                            rel_entry_end)):
                    # add the requirements that determine the intersection along this axis.
                    for j, k in ((a, b), (c, d), (e, f)):
                        coord_sorting_relation = coord_sorting_relations[axis]
                        jCoord, kCoord = coord_sorting_relation.operands[
                            j], coord_sorting_relation.operands[k]
                        empty_intersection_relation = Less.sort(
                            [jCoord, kCoord], assumptions=assumptions)
                        requirements.append(empty_intersection_relation)
                yield (intersection_start, intersection_end)
Example #4
0
    def __init__(self,
                 tensor,
                 shape=None,
                 styles=None,
                 assumptions=USE_DEFAULTS,
                 requirements=tuple()):
        '''
        Create an ExprTensor either with a simple, dense tensor (list of lists ... of lists) or
        with a dictionary mapping coordinates (as tuples of expressions that represent integers) 
        to expr elements or Blocks.
        Providing starting and/or ending location(s) can extend the bounds of the tensor beyond
        the elements that are supplied.
        '''
        from .composite import _simplifiedCoord
        from proveit._core_ import KnownTruth
        from proveit.number import Less, Greater, zero, one, num, Add, Subtract

        assumptions = defaults.checkedAssumptions(assumptions)
        requirements = []
        if not isinstance(tensor, dict):
            tensor = {
                loc: element
                for loc, element in ExprTensor._tensorDictFromIterables(
                    tensor, assumptions, requirements)
            }

        # Map direct compositions for the end-coordinate of Iter elements
        # to their simplified forms.
        self.endCoordSimplifications = dict()

        # generate the set of distinct coordinates for each dimension
        coord_sets = None  # simplified versions
        full_tensor = dict()
        ndims = None
        if shape is not None:
            shape = ExprTensor.locAsExprs(shape)
            ndims = len(shape)
        for loc, element in tensor.items():
            if isinstance(element, KnownTruth):
                element = element.expr  # extract the Expression from the KnownTruth
            ndims = len(loc)
            if coord_sets is None:
                coord_sets = [set() for _ in range(ndims)]
            elif len(coord_sets) != ndims:
                if shape is not None:
                    raise ValueError(
                        "length of 'shape' is inconsistent with number of dimensions for ExprTensor locations"
                    )
                else:
                    raise ValueError(
                        "inconsistent number of dimensions for locations of the ExprTensor"
                    )
            for axis, coord in enumerate(list(loc)):
                if isinstance(coord, int):
                    coord = num(
                        coord)  # convert from Python int to an Expression
                    loc[axis] = coord
                coord_sets[axis].add(coord)
                if isinstance(element, Iter):
                    # Add (end-start)+1 of the Iter to get to the end
                    # location of the entry along this axis.
                    orig_end_coord = Add(
                        coord,
                        Subtract(element.end_indices[axis],
                                 element.start_indices[axis]), one)
                    end_coord = _simplifiedCoord(orig_end_coord, assumptions,
                                                 requirements)
                    self.endCoordSimplifications[orig_end_coord] = end_coord
                    coord_sets[axis].add(end_coord)
            full_tensor[tuple(loc)] = element

        if ndims is None:
            raise ExprTensorError("Empty ExprTensor is not allowed")
        if ndims <= 1:
            raise ExprTensorError(
                "ExprTensor must be 2 or more dimensions (use an ExprList for something 1-dimensional"
            )

        # in each dimension, coord_indices will be a dictionary
        # that maps each tensor location coordinate to its relative entry index.
        coord_rel_indices = []
        self.sortedCoordLists = []
        self.coordDiffRelationLists = []
        for axis in range(ndims):  # for each axis
            # KnownTruth sorting relation for the simplified coordinates used along this axis
            # (something with a form like a < b <= c = d <= e, that sorts the tensor location coordinates):
            coord_sorting_relation = Less.sort(coord_sets[axis],
                                               assumptions=assumptions)
            sorted_coords = list(coord_sorting_relation.operands)

            if shape is None:
                # Since nothing was explicitly specified, the shape is dictacted by extending
                # one beyond the last coordinate entry.
                sorted_coords.append(Add(sorted_coords[-1], one))
            else:
                sorted_coords.append(
                    shape[axis]
                )  # append the coordinate for the explicitly specified shape
            if sorted_coords[0] != zero:
                sorted_coords.insert(
                    0, zero
                )  # make sure the first of the sorted coordinates is zero.

            self.sortedCoordLists.append(ExprList(sorted_coords))

            # Add in coordinate expressions that explicitly indicate the difference between coordinates.
            # These may be used in generating the latex form of the ExprTensor.
            diff_relations = []
            for c1, c2 in zip(sorted_coords[:-1], sorted_coords[1:]):
                diff = _simplifiedCoord(Subtract(c2, c1), assumptions,
                                        requirements)
                # get the relationship between the difference of successive coordinate and zero.
                diff_relation = Greater.sort([zero, diff],
                                             assumptions=assumptions)
                if isinstance(diff_relation, Greater):
                    if c2 == sorted_coords[-1] and shape is not None:
                        raise ExprTensorError(
                            "Coordinates extend beyond the specified shape in axis %d: %s after %s"
                            % (axis, str(coord_sorting_relation.operands[-1]),
                               str(shape[axis])))
                    assert tuple(diff_relation.operands) == (
                        diff, zero), 'Inconsistent Less.sort results'
                    # diff > 0, let's compare it with one now
                    diff_relation = Greater.sort([one, diff],
                                                 assumptions=assumptions)
                requirements.append(diff_relation)
                diff_relations.append(diff_relation)
            self.coordDiffRelationLists.append(ExprList(diff_relations))

            # map each coordinate expression to its index into the sorting_relation operands
            coord_rel_indices.append(
                {coord: k
                 for k, coord in enumerate(sorted_coords)})

        # convert from the full tensor with arbitrary expression coordinates to coordinates that are
        # mapped according to sorted relation enumerations.
        rel_index_tensor = dict()
        for loc, element in full_tensor.items():
            rel_index_loc = (
                rel_index_map[coord]
                for coord, rel_index_map in zip(loc, coord_rel_indices))
            rel_index_tensor[rel_index_loc] = element

        sorted_keys = sorted(rel_index_tensor.keys())
        Expression.__init__(self, [
            'ExprTensor',
            str(ndims), ';'.join(str(key) for key in sorted_keys)
        ],
                            self.sortedCoordLists +
                            self.coordDiffRelationLists +
                            [rel_index_tensor[key] for key in sorted_keys],
                            styles=styles,
                            requirements=requirements)
        self.ndims = ndims
        self.relIndexTensor = rel_index_tensor

        # entryOrigins maps relative indices that contain tensor elements to
        # the relative indices of the origin for the corresponding entry.
        # Specifically, single-element entries map indices to themselves, but
        # multi-element Iter entries map each of the encompassed
        # relative index location to the origin relative index location where
        # that Iter entry is stored.
        self.relEntryOrigins = self._makeEntryOrigins()

        # the last coordinates of the sorted coordinates along each eaxis define the shape:
        self.shape = ExprList(
            [sorted_coords[-1] for sorted_coords in self.sortedCoordLists])
Example #5
0
minRealClosure = Forall((a, b), InSet(Min(a, b), Reals), domain=Reals)
minRealClosure

minRealPosClosure = Forall((a, b), InSet(Min(a, b), RealsPos), domain=RealsPos)
minRealPosClosure

maxRealClosure = Forall((a, b), InSet(Max(a, b), Reals), domain=Reals)
maxRealClosure

maxRealPosClosure = Forall((a, b), InSet(Max(a, b), RealsPos), domain=RealsPos)
maxRealPosClosure

relaxGreaterThan = Forall([a, b],
                          GreaterEq(a, b),
                          domain=Reals,
                          conditions=Greater(a, b))
relaxGreaterThan

relaxLessThan = Forall([a, b],
                       LessEq(a, b),
                       domain=Reals,
                       conditions=Less(a, b))
relaxLessThan

lessThanInBools = Forall([a, b], InSet(Less(a, b), Booleans), domain=Reals)
lessThanInBools

lessThanEqualsInBools = Forall([a, b],
                               InSet(LessEq(a, b), Booleans),
                               domain=Reals)
lessThanEqualsInBools
Example #6
0
    def doReducedSimplification(self, assumptions=USE_DEFAULTS):
        '''
        For the case Abs(x) where the operand x is already known to
        be or assumed to be a non-negative real, derive and return 
        this Abs expression equated with the operand itself:
        |- Abs(x) = x. For the case where x is already known or assumed
        to be a negative real, return the Abs expression equated with
        the negative of the operand: |- Abs(x) = -x.
        Assumptions may be necessary to deduce necessary conditions for
        the simplification.
        '''
        from proveit.number import Greater, GreaterEq, Mult, Neg
        from proveit.number import (zero, Naturals, NaturalsPos, RealsNeg,
                                    RealsNonNeg, RealsPos)
        # among other things, convert any assumptions=None
        # to assumptions=() (thus averting len(None) errors)
        assumptions = defaults.checkedAssumptions(assumptions)

        #-- -------------------------------------------------------- --#
        #-- Case (1): Abs(x) where entire operand x is known or      --#
        #--           assumed to be non-negative Real.               --#
        #-- -------------------------------------------------------- --#
        if InSet(self.operand, RealsNonNeg).proven(assumptions=assumptions):
            # Entire operand is known to be or assumed to be a
            # non-negative real, so we can return Abs(x) = x
            return self.absElimination(operand_type='non-negative',
                                       assumptions=assumptions)

        #-- -------------------------------------------------------- --#
        #-- Case (2): Abs(x) where entire operand x is known or      --#
        #--           assumed to be a negative Real.                 --#
        #-- -------------------------------------------------------- --#
        if InSet(self.operand, RealsNeg).proven(assumptions=assumptions):
            # Entire operand is known to be or assumed to be a
            # negative real, so we can return Abs(x) = -x
            return self.absElimination(operand_type='negative',
                                       assumptions=assumptions)

        #-- -------------------------------------------------------- --#
        #-- Case (3): Abs(x) where entire operand x is not yet known --*
        #--           to be a non-negative Real, but can easily be   --#
        #--           proven to be a non-negative Real because it is --#
        #--           (a) known or assumed to be ≥ 0 or
        #--           (b) known or assumed to be in a subset of the  --#
        #--               non-negative Reals, or                     --#
        #--           (c) the addition or product of operands, all   --#
        #--               of which are known or assumed to be non-   --#
        #--               negative reals. TBA!
        #-- -------------------------------------------------------- --#
        if (Greater(self.operand, zero).proven(assumptions=assumptions)
                and not GreaterEq(self.operand,
                                  zero).proven(assumptions=assumptions)):
            GreaterEq(self.operand, zero).prove(assumptions=assumptions)
            # and then it will get picked up in the next if() below

        if GreaterEq(self.operand, zero).proven(assumptions=assumptions):
            from proveit.number.sets.real._theorems_ import (
                inRealsNonNegIfGreaterEqZero)
            inRealsNonNegIfGreaterEqZero.specialize({a: self.operand},
                                                    assumptions=assumptions)
            return self.absElimination(operand_type='non-negative',
                                       assumptions=assumptions)

        if self.operand in InSet.knownMemberships.keys():
            for kt in InSet.knownMemberships[self.operand]:
                if kt.isSufficient(assumptions):
                    if isEqualToOrSubsetEqOf(
                            kt.expr.operands[1],
                            equal_sets=[RealsNonNeg, RealsPos],
                            subset_eq_sets=[Naturals, NaturalsPos, RealsPos],
                            assumptions=assumptions):

                        InSet(self.operand,
                              RealsNonNeg).prove(assumptions=assumptions)
                        return self.absElimination(operand_type='non-negative',
                                                   assumptions=assumptions)

        if isinstance(self.operand, Add) or isinstance(self.operand, Mult):
            count_of_known_memberships = 0
            count_of_known_relevant_memberships = 0
            for op in self.operand.operands:
                if op in InSet.knownMemberships.keys():
                    count_of_known_memberships += 1
            if count_of_known_memberships == len(self.operand.operands):
                for op in self.operand.operands:
                    op_temp_known_memberships = InSet.knownMemberships[op]
                    for kt in op_temp_known_memberships:
                        if (kt.isSufficient(assumptions)
                                and isEqualToOrSubsetEqOf(
                                    kt.expr.operands[1],
                                    equal_sets=[RealsNonNeg, RealsPos],
                                    subset_eq_sets=[
                                        Naturals, NaturalsPos, RealsPos,
                                        RealsNonNeg
                                    ],
                                    assumptions=assumptions)):

                            count_of_known_relevant_memberships += 1
                            break

                if (count_of_known_relevant_memberships == len(
                        self.operand.operands)):
                    # Prove that the sum or product is in
                    # RealsNonNeg and then instantiate absElimination.
                    for op in self.operand.operands:
                        InSet(op, RealsNonNeg).prove(assumptions=assumptions)
                    return self.absElimination(assumptions=assumptions)

        #-- -------------------------------------------------------- --#
        #-- Case (4): Abs(x) where operand x can easily be proven    --#
        #--           to be a negative Real because -x is known to   --#
        #--           be in a subset of the positive Reals           --#
        #-- -------------------------------------------------------- --#
        negated_op = None
        if isinstance(self.operand, Neg):
            negated_op = self.operand.operand
        else:
            negated_op = Neg(self.operand)
        negated_op_simp = negated_op.simplification(
            assumptions=assumptions).rhs

        if negated_op_simp in InSet.knownMemberships.keys():
            from proveit.number.sets.real._theorems_ import (
                negInRealsNegIfPosInRealsPos)
            for kt in InSet.knownMemberships[negated_op_simp]:
                if kt.isSufficient(assumptions):
                    if isEqualToOrSubsetEqOf(
                            kt.expr.operands[1],
                            equal_sets=[RealsNonNeg, RealsPos],
                            subset_sets=[NaturalsPos, RealsPos],
                            subset_eq_sets=[NaturalsPos, RealsPos],
                            assumptions=assumptions):

                        InSet(negated_op_simp,
                              RealsPos).prove(assumptions=assumptions)
                        negInRealsNegIfPosInRealsPos.specialize(
                            {a: negated_op_simp}, assumptions=assumptions)
                        return self.absElimination(operand_type='negative',
                                                   assumptions=assumptions)

        # for updating our equivalence claim(s) for the
        # remaining possibilities
        from proveit import TransRelUpdater
        eq = TransRelUpdater(self, assumptions)
        return eq.relation