Esempio n. 1
0
    def getElem(self, indices, base=0, assumptions=USE_DEFAULTS, requirements=None):
        '''
        Return the tensor element at the indices location, given
        as an Expression, using the given assumptions as needed
        to interpret the location expression.  Required
        truths, proven under the given assumptions, that 
        were used to make this interpretation will be
        appended to the given 'requirements' (if provided).
        '''
        from proveit.number import num, Less, Add, subtract
        from .iteration import Iter
        from .composite import _simplifiedCoord
        if len(indices) != self.ndims:
            raise ExprArrayError("The 'indices' has the wrong number of dimensions: %d instead of %d"%(len(indices), self.ndims))
        
        if requirements is None: requirements = [] # requirements won't be passed back in this case

        if base != 0: 
            # subtract off the base if it is not zero
            indices = [subtract(index, num(self.base)) for index in indices]
        tensor_loc = [_simplifiedCoord(index, assumptions, requirements) for index in indices]

        lower_indices = []
        upper_indices = []
        for coord, sorted_coords in zip(tensor_loc, self.sortedCoordLists):
            lower, upper = None, None
            try:
                lower, upper = Less.insert(sorted_coords, coord, assumptions=assumptions)
            except:
                raise ExprArrayError("Could not determine the 'indices' range within the tensor coordinates under the given assumptions")
            # The relationship to the lower and upper coordinate bounds are requirements for determining
            # the element being assessed.
            requirements.append(Less.sort((sorted_coords[lower], coord), reorder=False, assumptions=assumptions))
            requirements.append(Less.sort((coord, sorted_coords[upper]), reorder=False, assumptions=assumptions))
            lower_indices.append(lower)
            upper_indices.append(upper)
        
        if tuple(lower_indices) not in self.entryOrigins or tuple(upper_indices) not in self.entryOrigins:
            raise ExprArrayError("Tensor element could not be found at %s"%str(tensor_loc))
        rel_entry_origin = self.relEntryOrigins[lower_indices]
        if self.relEntryOrigins[upper_indices] != rel_entry_origin:
            raise ExprArrayError("Tensor element is ambiguous for %s under the given assumptions"%str(tensor_loc))
        
        entry = self[rel_entry_origin]
        if isinstance(entry, Iter):
            # indexing into an iteration
            entry_origin = self.tensorLoc(rel_entry_origin)
            iter_start_indices = entry.start_indices
            iter_loc = [Add(iter_start, subtract(coord, origin)) for iter_start, coord, origin in zip(iter_start_indices, tensor_loc, entry_origin)] 
            simplified_iter_loc = [_simplifiedCoord(coord, assumptions, requirements) for coord in iter_loc]
            return entry.getInstance(simplified_iter_loc, assumptions=assumptions, requirements=requirements)
        else:
            # just a single-element entry
            assert lower_indices==upper_indices, "A single-element entry should not have been determined if there was an ambiguous range for 'tensor_loc'"
            return entry
Esempio n. 2
0
    def getInstance(self,
                    indices,
                    assumptions=USE_DEFAULTS,
                    requirements=None):
        '''
        Return the iteration instance with the given indices
        as an Expression, using the given assumptions as needed
        to interpret the indices expression.  Required
        truths, proven under the given assumptions, that 
        were used to make this interpretation will be
        appended to the given 'requirements' (if provided).
        '''
        from proveit.number import Less

        if requirements is None:
            requirements = []  # requirements won't be passed back in this case

        # first make sure that the indices are in the iteration range
        for index, start, end in zip(indices, self.start_indices,
                                     self.end_indices):
            for first, second in ((start, index), (index, end)):
                relation = None
                try:
                    relation = Less.sort([first, second],
                                         reorder=False,
                                         assumptions=assumptions)
                except:
                    raise IterationError(
                        "Indices not provably within the iteration range: %s <= %s"
                        % (first, second))
                requirements.append(relation)

        # map to the desired instance
        return self.lambda_map.mapped(indices)
Esempio n. 3
0
 def notEqual(self, other, assumptions=USE_DEFAULTS):
     from proveit.number import Less
     from proveit.number.ordering._theorems_ import lessIsNotEq, gtrIsNotEq
     _a, _b = Less.sorted_items([self, other], assumptions=assumptions)
     if self == _a:
         return lessIsNotEq.specialize({
             a: _a,
             b: _b
         },
                                       assumptions=assumptions)
     else:
         return gtrIsNotEq.specialize({
             a: _b,
             b: _a
         },
                                      assumptions=assumptions)
Esempio n. 4
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)))
Esempio n. 5
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)
Esempio n. 6
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])
Esempio n. 7
0
    def substituted(self,
                    exprMap,
                    relabelMap=None,
                    reservedVars=None,
                    assumptions=USE_DEFAULTS,
                    requirements=None):
        '''
        Returns this expression with the substitutions made 
        according to exprMap and/or relabeled according to relabelMap.
        Attempt to automatically expand the iteration if any Indexed 
        sub-expressions substitute their variable for a composite
        (list or tensor).  Indexed should index variables that represent
        composites, but substituting the composite is a signal that
        an outer iteration should be expanded.  An exception is
        raised if this fails.
        '''
        from proveit.logic import Equals
        from proveit.number import Less, LessEq, Subtract, Add, one
        from composite import _simplifiedCoord
        from proveit._core_.expression.expr import _NoExpandedIteration

        assumptions = defaults.checkedAssumptions(assumptions)
        arg_sorting_assumptions = list(assumptions)

        new_requirements = []

        # Collect the iteration ranges from Indexed sub-Expressions
        # whose variable is being replaced with a Composite (list or tensor).
        # If there are not any, we won't expand the iteration at this point.
        # While we are at it, get all of the end points of the
        # ranges along each axis (as well as end points +/-1 that may be
        # needed if there are overlaps): 'special_points'.
        iter_ranges = set()
        iter_params = self.lambda_map.parameters
        special_points = [set() for _ in xrange(len(iter_params))]
        subbed_start = self.start_indices.substituted(exprMap, relabelMap,
                                                      reservedVars,
                                                      assumptions,
                                                      new_requirements)
        subbed_end = self.end_indices.substituted(exprMap, relabelMap,
                                                  reservedVars, assumptions,
                                                  new_requirements)
        try:
            for iter_range in self.lambda_map.body._expandingIterRanges(
                    iter_params, subbed_start, subbed_end, exprMap, relabelMap,
                    reservedVars, assumptions, new_requirements):
                iter_ranges.add(iter_range)
                for axis, (start, end) in enumerate(zip(*iter_range)):
                    special_points[axis].add(start)
                    special_points[axis].add(end)
                    # Preemptively include start-1 and end+1 in case it is required for splitting up overlapping ranges
                    # (we won't add simplification requirements until we find we actually need them.)
                    # Not necesary in the 1D case.
                    # Add the coordinate simplification to argument sorting assumtions -
                    # after all, this sorting does not go directly into the requirements.
                    start_minus_one = _simplifiedCoord(
                        Subtract(start, one),
                        assumptions=assumptions,
                        requirements=arg_sorting_assumptions)
                    end_plus_one = _simplifiedCoord(
                        Add(end, one),
                        assumptions=assumptions,
                        requirements=arg_sorting_assumptions)
                    special_points[axis].update(
                        {start_minus_one, end_plus_one})
                    # Add start-1<start and end<end+1 assumptions to ease argument sorting -
                    # after all, this sorting does not go directly into the requirements.
                    arg_sorting_assumptions.append(Less(
                        start_minus_one, start))
                    arg_sorting_assumptions.append(Less(end, end_plus_one))
                    arg_sorting_assumptions.append(
                        Equals(end, Subtract(end_plus_one, one)))
                    # Also add start<=end to ease the argument sorting requirement even though it
                    # may not strictly be true if an empty range is possible.  In such a case, we
                    # still want things sorted this way while we don't know if the range is empty or not
                    # and it does not go directly into the requirements.
                    arg_sorting_assumptions.append(LessEq(start, end))

            # There are Indexed sub-Expressions whose variable is
            # being replaced with a Composite, so let us
            # expand the iteration for all of the relevant
            # iteration ranges.
            # Sort the argument value ranges.

            arg_sorting_relations = []
            for axis in xrange(self.ndims):
                if len(special_points[axis]) == 0:
                    arg_sorting_relation = None
                else:
                    arg_sorting_relation = Less.sort(
                        special_points[axis],
                        assumptions=arg_sorting_assumptions)
                arg_sorting_relations.append(arg_sorting_relation)

            # Put the iteration ranges in terms of indices of the sorting relation operands
            # (relative indices w.r.t. the sorting relation order).
            rel_iter_ranges = set()
            for iter_range in iter_ranges:
                range_start, range_end = iter_range
                rel_range_start = tuple([
                    arg_sorting_relation.operands.index(arg)
                    for arg, arg_sorting_relation in zip(
                        range_start, arg_sorting_relations)
                ])
                rel_range_end = tuple([
                    arg_sorting_relation.operands.index(arg)
                    for arg, arg_sorting_relation in zip(
                        range_end, arg_sorting_relations)
                ])
                rel_iter_ranges.add((rel_range_start, rel_range_end))

            rel_iter_ranges = sorted(
                self._makeNonoverlappingRangeSet(rel_iter_ranges,
                                                 arg_sorting_relations,
                                                 assumptions,
                                                 new_requirements))

            # Generate the expanded list/tensor to replace the iterations.
            if self.ndims == 1: lst = []
            else: tensor = dict()
            for rel_iter_range in rel_iter_ranges:
                # get the starting location of this iteration range
                start_loc = tuple(
                    arg_sorting_relation.operands[idx]
                    for arg_sorting_relation, idx in zip(
                        arg_sorting_relations, rel_iter_range[0]))
                if rel_iter_range[0] == rel_iter_range[1]:
                    # single element entry (starting and ending location the same)
                    inner_expr_map = dict(exprMap)
                    inner_expr_map.update({
                        param: arg
                        for param, arg in zip(self.lambda_map.parameters,
                                              start_loc)
                    })
                    for param in self.lambda_map.parameters:
                        relabelMap.pop(param, None)
                    entry = self.lambda_map.body.substituted(
                        inner_expr_map, relabelMap, reservedVars, assumptions,
                        new_requirements)
                else:
                    # iterate over a sub-range
                    end_loc = tuple(
                        arg_sorting_relation.operands[idx]
                        for arg_sorting_relation, idx in zip(
                            arg_sorting_relations, rel_iter_range[1]))
                    # Shift the iteration parameter so that the iteration will have the same start-indices
                    # for this sub-range (like shifting a viewing window, moving the origin to the start of the sub-range).
                    # Include assumptions that the lambda_map parameters are in the shifted start_loc to end_loc range.
                    range_expr_map = dict(exprMap)
                    range_assumptions = list(assumptions)
                    for start_idx, param, range_start, range_end in zip(
                            self.start_indices, self.lambda_map.parameters,
                            start_loc, end_loc):
                        range_expr_map[param] = Add(
                            param, Subtract(range_start, start_idx))
                        range_assumptions += Less.sort((start_idx, param),
                                                       reorder=False,
                                                       assumptions=assumptions)
                        range_assumptions += Less.sort(
                            (param, Subtract(range_end, start_idx)),
                            reorder=False,
                            assumptions=assumptions)
                    range_lambda_body = self.lambda_map.body.substituted(
                        range_expr_map, relabelMap, reservedVars,
                        range_assumptions, new_requirements)
                    range_lambda_map = Lambda(self.lambda_map.parameters,
                                              range_lambda_body)
                    # Add the shifted sub-range iteration to the appropriate starting location.
                    end_indices = [
                        _simplifiedCoord(Subtract(range_end, start_idx),
                                         assumptions, new_requirements)
                        for start_idx, range_end in zip(
                            self.start_indices, end_loc)
                    ]
                    entry = Iter(range_lambda_map, self.start_indices,
                                 end_indices)
                if self.ndims == 1: lst.append(entry)
                else: tensor[start_loc] = entry

            if self.ndims == 1:
                subbed_self = compositeExpression(lst)
            else:
                subbed_self = compositeExpression(tensor)

        except _NoExpandedIteration:
            # No Indexed sub-Expressions whose variable is
            # replaced with a Composite, so let us not expand the
            # iteration.  Just do an ordinary substitution.
            subbed_map = self.lambda_map.substituted(exprMap, relabelMap,
                                                     reservedVars, assumptions,
                                                     new_requirements)
            subbed_self = Iter(subbed_map, subbed_start, subbed_end)

        for requirement in new_requirements:
            requirement._restrictionChecked(
                reservedVars
            )  # make sure requirements don't use reserved variable in a nested scope
        if requirements is not None:
            requirements += new_requirements  # append new requirements

        return subbed_self
Esempio n. 8
0
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

greaterThanInBools = Forall([a, b],
                            InSet(Greater(a, b), Booleans),
                            domain=Reals)
greaterThanInBools
Esempio n. 9
0
    def getElem(self,
                coord,
                base=1,
                hint_idx=None,
                assumptions=USE_DEFAULTS,
                requirements=None):
        '''
        Return the tuple element at the coordinate, given as an 
        Expression, using the given assumptions as needed to interpret 
        the location indicated by this expression.  Required truths, 
        proven under the given assumptions, that  were used to make this
        interpretation will be appended to the given 'requirements' 
        (if provided).
        If a hint_idx is provided, use it as a starting entry
        index from which to search for the coordinate.  Otherwise,
        use the previously queried entry as the 'hint'.
        '''
        from .composite import _generateCoordOrderAssumptions, \
            _simplifiedCoord
        from proveit.number import num, Naturals, Less, LessEq, \
            dist_add, Neg, dist_subtract
        from proveit.logic import Equals, InSet
        from proveit.relation import TransitivityException
        from .iteration import Iter

        if len(self) == 0:
            raise ValueError("An empty ExprTuple has no elements to get")

        if requirements is None:
            requirements = [
            ]  # create the requirements list, but it won't be used

        nentries = len(self.entries)

        # First handle the likely case that the coordinate of the
        # element is just the starting coordinate of an entry.
        coord_to_idx = self.entryCoordToIndex(base, assumptions, requirements)

        coord = _simplifiedCoord(coord, assumptions, requirements)
        if coord in coord_to_idx:
            # Found the coordinate as the start of an entry.
            start_idx = coord_to_idx[coord]
            entry = self.entries[start_idx]
            if not isinstance(entry, Iter):
                self._lastQueriedEntryIndex = start_idx
                return entry  # just a normal entry
            # If this is an iteration entry, we need to be careful.
            # Ostensibly, we would want to return entry.first() but we
            # need to be make sure it is not an empty iteration.
            # Instead, we'll treat it like the "hard" case starting
            # from start_idx.
        elif hint_idx is not None:
            # Use the provided hint as the starting point entry
            # index.
            start_idx = hint_idx
        else:
            # We use the last queried index as the starting point
            # to make typical use-cases more efficient.
            start_idx = self._lastQueriedEntryIndex

        try:
            # First we need to find an entry whose starting coordinate
            # is at or beyond our desired 'coord'.  Search starting
            # from the "hint".
            coord_simp_requirements = []
            coords = self.entryCoords(base, assumptions, requirements,
                                      coord_simp_requirements)
            coord_order_assumptions = \
                list(_generateCoordOrderAssumptions(coords))
            extended_assumptions = assumptions + coord_order_assumptions

            # Record relations between the given 'coord' and each
            # entry coordinate in case we want to reuse it.'
            relations = [None] * (nentries + 1)

            # Search for the right 'idx' of the entry starting
            # from start_idx and going forward until we have gone
            # too far.
            for idx in range(start_idx, nentries + 1):
                # Check if 'coord' is less than coords[idx]
                #print("sort", coord, coords[idx], assumptions)
                relation = LessEq.sort([coord, coords[idx]],
                                       assumptions=extended_assumptions)
                relations[idx] = relation
                rel_first, rel_op = relation.operands[0], relation.operator
                if rel_first == coord and rel_op == Less._operator_:
                    break
                elif idx == nentries:
                    raise IndexError("Coordinate %s past the range of "
                                     "the ExprTuple, %s" %
                                     (str(coord), str(self)))

            # Now go back to an entry whose starting coordinate is less
            # than or equal to the desired 'coord'.
            while idx > 0:
                idx -= 1
                try:
                    # Try to prove coords[idx] <= coord.
                    relation = LessEq.sort([coords[idx], coord],
                                           assumptions=extended_assumptions,
                                           reorder=False)
                    relations[idx] = relation
                    break
                except TransitivityException:
                    # Since we could not prove that
                    # coords[idx] <= coord, we must prove
                    # coord < coords[idx] and keep going back.
                    relation = Less(coord,
                                    coords[idx]).prove(extended_assumptions)
                    relations[idx] = relation
                    continue

            # We have the right index.  Include coordinate
            # simplifications up to that point as requirements.
            coord_simp_req_map = {eq.rhs: eq for eq in coord_simp_requirements}
            for prev_coord in coords[:idx + 1]:
                if prev_coord in coord_simp_req_map:
                    requirements.append(coord_simp_req_map[prev_coord])

            # The 'coord' is within this particular entry.
            # Record the required relations that prove that.
            self._lastQueriedEntryIndex = idx
            requirements.append(relations[idx])
            requirements.append(relations[idx + 1])

            # And return the appropriate element within the
            # entry.
            entry = self.entries[idx]
            if relations[idx].operator == Equals._operator_:
                # Special case -- coord at the entry origin.
                if isinstance(entry, Iter):
                    return entry.first()
                else:
                    return entry

            # The entry must be an iteration.
            if not isinstance(entry, Iter):
                raise ExprTupleError("Invalid coordinate, %s, in "
                                     "ExprTuple, %s." %
                                     (str(coord), str(self)))

            # Make sure the coordinate is valid and not "in between"
            # coordinates at unit intervals.
            valid_coord = InSet(dist_subtract(coord, coords[idx]), Naturals)
            requirements.append(valid_coord.prove(assumptions))

            # Get the appropriate element within the iteration.
            iter_start_index = entry.start_index
            iter_loc = dist_add(iter_start_index,
                                dist_subtract(coord, coords[idx]))
            simplified_iter_loc = _simplifiedCoord(iter_loc, assumptions,
                                                   requirements)
            # Does the same as 'entry.getInstance' but without checking
            # requirements; we don't need to worry about these requirements
            # because we already satisfied the requirements that we need.
            return entry.lambda_map.mapped(simplified_iter_loc)

        except ProofFailure as e:
            msg = ("Could not determine the element at "
                   "%s of the ExprTuple %s under assumptions %s." %
                   (str(coord), str(self), str(e.assumptions)))
            raise ExprTupleError(msg)

        raise IndexError("Unable to prove that "
                         "%s > %d to be within ExprTuple %s." %
                         (str(coord), base, str(self)))
Esempio n. 10
0
    def substituted(self,
                    exprMap,
                    relabelMap=None,
                    reservedVars=None,
                    assumptions=USE_DEFAULTS,
                    requirements=None):
        '''
        Returns this expression with the substitutions made 
        according to exprMap and/or relabeled according to relabelMap.
        Attempt to automatically expand the iteration if any Indexed 
        sub-expressions substitute their variable for a composite
        (list or tensor).  Indexed should index variables that represent
        composites, but substituting the composite is a signal that
        an outer iteration should be expanded.  An exception is
        raised if this fails.
        '''
        from .composite import _generateCoordOrderAssumptions
        from proveit import ProofFailure, ExprArray
        from proveit.logic import Equals, InSet
        from proveit.number import Less, LessEq, dist_add, \
            zero, one, dist_subtract, Naturals, Integers
        from .composite import _simplifiedCoord
        from proveit._core_.expression.expr import _NoExpandedIteration
        from proveit._core_.expression.label.var import safeDummyVars

        self._checkRelabelMap(relabelMap)
        if relabelMap is None: relabelMap = dict()

        assumptions = defaults.checkedAssumptions(assumptions)
        new_requirements = []
        iter_params = self.lambda_map.parameters
        iter_body = self.lambda_map.body
        ndims = self.ndims
        subbed_start = self.start_indices.substituted(exprMap, relabelMap,
                                                      reservedVars,
                                                      assumptions,
                                                      new_requirements)
        subbed_end = self.end_indices.substituted(exprMap, relabelMap,
                                                  reservedVars, assumptions,
                                                  new_requirements)

        #print("iteration substituted", self, subbed_start, subbed_end)

        # Need to handle the change in scope within the lambda
        # expression.  We won't use 'new_params'.  They aren't relavent
        # after an expansion, this won't be used.
        new_params, inner_expr_map, inner_assumptions, inner_reservations \
            = self.lambda_map._innerScopeSub(exprMap, relabelMap,
                  reservedVars, assumptions, new_requirements)

        # Get sorted substitution parameter start and end
        # values demarcating how the entry array must be split up for
        # each axis.
        all_entry_starts = [None] * ndims
        all_entry_ends = [None] * ndims
        do_expansion = False
        for axis in range(ndims):
            try:
                empty_eq = Equals(dist_add(subbed_end[axis], one),
                                  subbed_start[axis])
                try:
                    # Check if this is an empty iteration which
                    # happens when end+1=start.
                    empty_eq.prove(assumptions, automation=False)
                    all_entry_starts[axis] = all_entry_ends[axis] = []
                    do_expansion = True
                    continue
                except ProofFailure:
                    pass
                param_vals = \
                    iter_body._iterSubParamVals(axis, iter_params[axis],
                                                subbed_start[axis],
                                                subbed_end[axis],
                                                inner_expr_map, relabelMap,
                                                inner_reservations,
                                                inner_assumptions,
                                                new_requirements)
                assert param_vals[0] == subbed_start[axis]
                if param_vals[-1] != subbed_end[axis]:
                    # The last of the param_vals should either be
                    # subbed_end[axis] or known to be
                    # subbed_end[axis]+1.  Let's double-check.
                    eq = Equals(dist_add(subbed_end[axis], one),
                                param_vals[-1])
                    eq.prove(assumptions, automation=False)
                # Populate the entry starts and ends using the
                # param_vals which indicate that start of each contained
                # entry plus the end of this iteration.
                all_entry_starts[axis] = []
                all_entry_ends[axis] = []
                for left, right in zip(param_vals[:-1], param_vals[1:]):
                    all_entry_starts[axis].append(left)
                    try:
                        eq = Equals(dist_add(left, one), right)
                        eq.prove(assumptions, automation=False)
                        new_requirements.append(
                            eq.prove(assumptions, automation=False))
                        # Simple single-entry case: the start and end
                        # are the same.
                        entry_end = left
                    except:
                        # Not the simple case; perform the positive
                        # integrality check.
                        requirement = InSet(dist_subtract(right, left),
                                            Naturals)
                        # Knowing the simplification may help prove the
                        # requirement.
                        _simplifiedCoord(requirement, assumptions, [])
                        try:
                            new_requirements.append(
                                requirement.prove(assumptions))
                        except ProofFailure as e:
                            raise IterationError("Failed to prove requirement "
                                                 "%s:\n%s" % (requirement, e))
                        if right == subbed_end[axis]:
                            # This last entry is the inclusive end
                            # rather than past the end, so it is an
                            # exception.
                            entry_end = right
                        else:
                            # Subtract one from the start of the next
                            # entyr to get the end of this entry.
                            entry_end = dist_subtract(right, one)
                            entry_end = _simplifiedCoord(
                                entry_end, assumptions, requirements)
                    all_entry_ends[axis].append(entry_end)
                # See if we should add the end value as an extra
                # singular entry.  If param_vals[-1] is at the inclusive
                # end, then we have a singular final entry.
                if param_vals[-1] == subbed_end[axis]:
                    end_val = subbed_end[axis]
                    all_entry_starts[axis].append(end_val)
                    all_entry_ends[axis].append(end_val)
                else:
                    # Otherwise, the last param_val will be one after
                    # the inclusive end which we will want to use below
                    # when building the last iteration entry.
                    all_entry_starts[axis].append(param_vals[-1])
                do_expansion = True
            except EmptyIterException:
                # Indexing over a negative or empty range.  The only way this
                # should be allowed is if subbed_end+1=subbed_start.
                Equals(dist_add(subbed_end[axis], one),
                       subbed_start[axis]).prove(assumptions)
                all_entry_starts[axis] = all_entry_ends[axis] = []
                do_expansion = True
            except _NoExpandedIteration:
                pass

        if do_expansion:
            # There are Indexed sub-Expressions whose variable is
            # being replaced with a Composite, so let us
            # expand the iteration for all of the relevant
            # iteration ranges.
            # Sort the argument value ranges.

            # We must have "substition parameter values" along each
            # axis:
            if None in all_entry_starts or None in all_entry_ends:
                raise IterationError("Must expand all axes or none of the "
                                     "axes, when substituting %s" % str(self))

            # Generate the expanded tuple/array as the substition
            # of 'self'.
            shape = [len(all_entry_ends[axis]) for axis in range(ndims)]
            entries = ExprArray.make_empty_entries(shape)
            indices_by_axis = [range(extent) for extent in shape]
            #print('shape', shape, 'indices_by_axis', indices_by_axis, 'sub_param_vals', sub_param_vals)

            extended_inner_assumptions = list(inner_assumptions)
            for axis_starts in all_entry_starts:
                # Generate assumptions that order the
                # successive entry start parameter values
                # must be natural numbers. (This is a requirement for
                # iteration instances and is a simple fact of
                # succession for single entries.)
                extended_inner_assumptions.extend(
                    _generateCoordOrderAssumptions(axis_starts))

            # Maintain lists of parameter values that come before each given entry.
            #prev_param_vals = [[] for axis in range(ndims)]

            # Iterate over each of the new entries, obtaining indices
            # into sub_param_vals for the start parameters of the entry.
            for entry_indices in itertools.product(*indices_by_axis):
                entry_starts = [axis_starts[i] for axis_starts, i in \
                                zip(all_entry_starts, entry_indices)]
                entry_ends = [axis_ends[i] for axis_ends, i in \
                                zip(all_entry_ends, entry_indices)]

                is_singular_entry = True
                for entry_start, entry_end in zip(entry_starts, entry_ends):
                    # Note that empty ranges will be skipped because
                    # equivalent parameter values should be skipped in
                    # the param_vals above.
                    if entry_start != entry_end:
                        # Not a singular entry along this axis, so
                        # it is not a singular entry.  We must do an
                        # iteration for this entry.
                        is_singular_entry = False

                if is_singular_entry:
                    # Single element entry.

                    # Generate the entry by making appropriate
                    # parameter substitutions for the iteration body.
                    entry_inner_expr_map = dict(inner_expr_map)
                    entry_inner_expr_map.update({
                        param: arg
                        for param, arg in zip(iter_params, entry_starts)
                    })
                    for param in iter_params:
                        relabelMap.pop(param, None)
                    entry = iter_body.substituted(entry_inner_expr_map,
                                                  relabelMap,
                                                  inner_reservations,
                                                  extended_inner_assumptions,
                                                  new_requirements)
                else:
                    # Iteration entry.
                    # Shift the iteration parameter so that the
                    # iteration will have the same start-indices
                    # for this sub-range (like shifting a viewing
                    # window, moving the origin to the start of the
                    # sub-range).

                    # Generate "safe" new parameters (the Variables are
                    # not used for anything that might conflict).
                    # Avoid using free variables from these expressions:
                    unsafe_var_exprs = [self]
                    unsafe_var_exprs.extend(exprMap.values())
                    unsafe_var_exprs.extend(relabelMap.values())
                    unsafe_var_exprs.extend(entry_starts)
                    unsafe_var_exprs.extend(entry_ends)
                    new_params = safeDummyVars(ndims, *unsafe_var_exprs)

                    # Make assumptions that places the parameter(s) in the
                    # appropriate range and at an integral coordinate position.
                    # Note, it is possible that this actually represents an
                    # empty range and that these assumptions are contradictory;
                    # but this still suits our purposes regardless.
                    # Also, we will choose to shift the parameter so it
                    # starts at the start index of the iteration.
                    range_expr_map = dict(inner_expr_map)
                    range_assumptions = []
                    shifted_entry_ends = []
                    for axis, (param, new_param, entry_start, entry_end) \
                            in enumerate(zip(iter_params, new_params,
                                             entry_starts, entry_ends)):
                        start_idx = self.start_indices[axis]
                        shift = dist_subtract(entry_start, start_idx)
                        shift = _simplifiedCoord(shift, assumptions,
                                                 new_requirements)
                        if shift != zero:
                            shifted_param = dist_add(new_param, shift)
                        else:
                            shifted_param = new_param
                        range_expr_map[param] = shifted_param
                        shifted_end = dist_subtract(entry_end, shift)
                        shifted_end = _simplifiedCoord(shifted_end,
                                                       assumptions,
                                                       new_requirements)
                        shifted_entry_ends.append(shifted_end)
                        assumption = InSet(new_param, Integers)
                        range_assumptions.append(assumption)
                        assumption = LessEq(entry_start, shifted_param)
                        range_assumptions.append(assumption)
                        # Assume differences with each of the previous
                        # range starts are natural numbers as should be
                        # the case given requirements that have been
                        # met.
                        next_index = entry_indices[axis] + 1
                        prev_starts = all_entry_starts[axis][:next_index]
                        for prev_start in prev_starts:
                            assumption = InSet(
                                dist_subtract(shifted_param, prev_start),
                                Naturals)
                            range_assumptions.append(assumption)
                        next_start = all_entry_starts[axis][next_index]
                        assumption = Less(shifted_param, next_start)
                        range_assumptions.append(assumption)

                    # Perform the substitution.
                    # The fact that our "new parameters" are "safe"
                    # alleviates the need to reserve anything extra.
                    range_lambda_body = iter_body.substituted(
                        range_expr_map, relabelMap, reservedVars,
                        extended_inner_assumptions + range_assumptions,
                        new_requirements)
                    # Any requirements that involve the new parameters
                    # are a direct consequence of the iteration range
                    # and are not external requirements:
                    new_requirements = \
                        [requirement for requirement in new_requirements
                         if requirement.freeVars().isdisjoint(new_params)]
                    entry = Iter(new_params, range_lambda_body,
                                 self.start_indices, shifted_entry_ends)
                # Set this entry in the entries array.
                ExprArray.set_entry(entries, entry_indices, entry)
                '''      
                    # Iteration entry.
                    # Shift the iteration parameter so that the 
                    # iteration will have the same start-indices
                    # for this sub-range (like shifting a viewing 
                    # window, moving the origin to the start of the 
                    # sub-range).

                    # Generate "safe" new parameters (the Variables are
                    # not used for anything that might conflict).
                    # Avoid using free variables from these expressions:
                    unsafe_var_exprs = [self]
                    unsafe_var_exprs.extend(exprMap.values())
                    unsafe_var_exprs.extend(relabelMap.values())
                    unsafe_var_exprs.extend(entry_start_vals)
                    unsafe_var_exprs.extend(entry_end_vals)
                    new_params = safeDummyVars(len(iter_params), 
                                               *unsafe_var_exprs)
                    
                    # Make the appropriate substitution mapping
                    # and add appropriate assumptions for the iteration
                    # parameter(s).
                    range_expr_map = dict(inner_expr_map)
                    range_assumptions = []
                    for start_idx, param, new_param, range_start, range_end \
                            in zip(subbed_start, iter_params, new_params, 
                                   entry_start_vals, entry_end_vals):
                        shifted_param = Add(new_param, subtract(range_start, start_idx))
                        shifted_param = _simplifiedCoord(shifted_param, assumptions,
                                                         requirements)
                        range_expr_map[param] = shifted_param
                        # Include assumptions that the parameters are 
                        # in the proper range.
                        assumption = LessEq(start_idx, new_param)
                        range_assumptions.append(assumption)
                        assumption = InSet(subtract(new_param, start_idx), Naturals)
                        #assumption = LessEq(new_param,
                        #                    subtract(range_end, start_idx))
                        assumption = LessEq(new_param, range_end)
                        range_assumptions.append(assumption)
                    
                    # Perform the substitution.
                    # The fact that our "new parameters" are "safe" 
                    # alleviates the need to reserve anything extra.
                    range_lambda_body = iter_body.substituted(range_expr_map, 
                        relabelMap, reservedVars, 
                        inner_assumptions+range_assumptions, new_requirements)
                    # Any requirements that involve the new parameters 
                    # are a direct consequence of the iteration range 
                    # and are not external requirements:
                    new_requirements = \
                        [requirement for requirement in new_requirements 
                         if requirement.freeVars().isdisjoint(new_params)]
                    range_lambda_map = Lambda(new_params, range_lambda_body)
                    # Obtain the appropriate end indices.
                    end_indices = \
                        [_simplifiedCoord(subtract(range_end, start_idx), 
                                          assumptions, new_requirements) 
                         for start_idx, range_end in zip(subbed_start, 
                                                          entry_end_vals)]
                    entry = Iter(range_lambda_map, subbed_start, end_indices)
                # Set this entry in the entries array.
                ExprArray.set_entry(entries, entry_start_indices, entry)
                '''
            subbed_self = compositeExpression(entries)
        else:
            # No Indexed sub-Expressions whose variable is
            # replaced with a Composite, so let us not expand the
            # iteration.  Just do an ordinary substitution.
            new_requirements = []  # Fresh new requirements.
            subbed_map = self.lambda_map.substituted(exprMap, relabelMap,
                                                     reservedVars, assumptions,
                                                     new_requirements)
            subbed_self = Iter(subbed_map.parameters, subbed_map.body,
                               subbed_start, subbed_end)

        for requirement in new_requirements:
            # Make sure requirements don't use reserved variable in a
            # nested scope.
            requirement._restrictionChecked(reservedVars)
        if requirements is not None:
            requirements += new_requirements  # append new requirements

        return subbed_self
Esempio n. 11
0
    def entryRanges(self, base, start_index, end_index, assumptions,
                    requirements):
        '''
        For each entry of the list that is fully or partially contained in the window defined
        via start_indices and end_indices (as Expressions that can be provably sorted
        against list indices), yield the start and end of the intersection of the
        entry range and the window.
        '''
        from proveit.number import one, num, Add, Subtract, Less
        from proveit.logic import Equals
        from iteration import Iter
        from proveit import ProofFailure

        if requirements is None:
            requirements = []  # requirements won't be passed back in this case

        index = num(base)
        started = False
        prev_end = None

        try:
            start_end_relation = Less.sort([start_index, end_index
                                            ]).prove(assumptions=assumptions)
            if start_end_relation.operands[0] != start_index:
                # end comes before start: the range is empty.  This is the vacuous case.
                requirements.append(start_end_relation)
                return
                yield
        except:
            # Unable to prove that the end comes before the start, so assume
            # this will be a finite iteration (if not, the user can decide
            # how long to wait before they realize they are missing something).
            pass

        # Iterate over the entries and track the true element index,
        # including ranges of iterations (Iter objects).
        for i, entry in enumerate(self):
            if not started:
                # We have not yet encounted an entry within the desired window,
                # see if this entry is in the desired window.
                if index == start_index:
                    started = True  # Now we've started
                else:
                    try:
                        start_relation = Less.sort([start_index, index],
                                                   reorder=False,
                                                   assumptions=assumptions)
                        requirements.append(start_relation)
                        if start_relation.operator == Less._operator_ and prev_end is not None:
                            # The start of the window must have occurred before this entry,
                            # and there was a previous entry:
                            yield (start_index, prev_end
                                   )  # Do the range for the previous entry.
                        started = True  # Now we've started
                    except ProofFailure:
                        pass  # We have not started yet.

            # Obtain the ending index of the entry (entry_end) and the next_index
            # (entry_end+1).
            entry_end = index  # unless it is an Iter:
            if isinstance(entry, Iter):
                entry_span = Subtract(entry.end_index, entry.start_index)
                entry_end = _simplifiedCoord(Add(index, entry_span),
                                             assumptions, requirements)

            arrived_at_end = False
            if index == end_index:
                arrived_at_end = True
            else:
                try:
                    index_eq_end = Equals(end_index,
                                          index).prove(assumptions=assumptions,
                                                       automation=False)
                    requirements.append(index_eq_end)
                    arrived_at_end == True
                except ProofFailure:
                    next_index = _simplifiedCoord(Add(entry_end, one),
                                                  assumptions, requirements)
                    """
                    # TO KEEP THINGS SIMPLE, LET'S INSIST THAT THE INDEX MUST MATCH THE END EXACTLY TO STOP
                    # (NOT GOING BEYOND WITHOUT MATCHING).
                    # The exception is when the range is empty which we test at the beginning.
                                                       
                    # See if this entry takes us to the end of the window or beyond.
                    try:
                        print next_index, end_index
                        Less.sort([next_index, end_index], reorder=False, assumptions=assumptions)
                    except ProofFailure:
                        arrived_at_end = True # we have presumably encountered the end
                        if entry_end != end_index:
                            # we require a proven relation that we are at the end
                            end_relation = Less.sort([end_index, next_index], reorder=False, assumptions=assumptions)
                            requirements.append(end_relation)
                    """

            if arrived_at_end:
                if started:
                    # Yield from the start of the entry to the end of the window:
                    yield (index, end_index)
                    break
                else:
                    # The full window is within this entry.
                    start_relation = Less.sort([index, start_index],
                                               reorder=False,
                                               assumptions=assumptions)
                    requirements.append(start_relation)
                    yield (
                        start_index, end_index
                    )  # Yield the full window that is within a single entry.
                    break
            elif started:
                # We have encountered the start but not the end.
                yield (index, entry_end)  # Yield the full range of the entry.

            index = next_index  # Move on to the next entry.
            prev_end = entry_end

        if not arrived_at_end:
            raise IndexError("ExprList index out of range")
Esempio n. 12
0
    def getElem(self,
                index,
                base=1,
                assumptions=USE_DEFAULTS,
                requirements=None):
        '''
        Return the list element at the index, given
        as an Expression, using the given assumptions as needed
        to interpret the location expression.  Required
        truths, proven under the given assumptions, that 
        were used to make this interpretation will be
        appended to the given 'requirements' (if provided).
        '''
        from proveit.number import num, one, lesserSequence, Less, LessEq, Add, Subtract
        from proveit.logic import Equals
        from proveit.relation import TransitivityException
        from .iteration import Iter
        from .composite import _simplifiedCoord

        if len(self) == 0:
            raise ValueError("An empty ExprList has no elements to get")

        if requirements is None:
            requirements = [
            ]  # create the requirements list, but it won't be used

        coord = num(base)
        try:
            for entry in self.entries:
                if isinstance(entry, Iter):
                    # An Iter entry.  First, check whether it is an empty iteration.
                    entry_start_end_relation = Less.sort(
                        [entry.start_index, entry.end_index],
                        assumptions=assumptions)
                    if not entry_start_end_relation.operator == Equals._operator_:
                        # start and end are not determined to be equal (if they were, the
                        # iteration would represent a single element).
                        if entry_start_end_relation.operands[
                                0] == entry.end_index:
                            if entry_start_end_relation.operator == LessEq._operator_:
                                # We don't know if the iteration is empty.
                                raise ExprListError(
                                    "Could not determine if an Iter entry of the ExprList is empty, so we could not determine the 'index' element."
                                )
                            # empty iteration.  skip it, but knowing it is empty is an important requirement
                            requirements.append(
                                entry_start_end_relation
                            )  # need to know: end-of-entry < start-of-entry
                            continue
                    # shift 'coord' to the end of the entry
                    next_coord = _simplifiedCoord(
                        Add(coord, Subtract(entry.end_index,
                                            entry.start_index)), assumptions,
                        requirements)
                    # check whether or not the 'index' is within this entry.
                    index_entryend_relation = Less.sort(
                        [index, next_coord], assumptions=assumptions)
                    if index_entryend_relation.operands[0] == index:
                        # 'index' within this particular entry
                        iter_start_index = entry.start_index
                        entry_origin = coord
                        if index == entry_origin:
                            # special case - index at the entry origin
                            return entry.getInstance(iter_start_index,
                                                     assumptions=assumptions,
                                                     requirements=requirements)
                        iter_loc = Add(iter_start_index,
                                       Subtract(index, entry_origin))
                        simplified_iter_loc = _simplifiedCoord(
                            iter_loc, assumptions, requirements)
                        return entry.getInstance(simplified_iter_loc,
                                                 assumptions=assumptions,
                                                 requirements=requirements)
                    coord = next_coord
                index_coord_relation = Less.sort([index, coord],
                                                 assumptions=assumptions)
                if index_coord_relation.operator == Equals._operator_:
                    # 'index' at this particular single-element entry
                    if index_coord_relation.lhs != index_coord_relation.rhs:
                        requirements.append(
                            index_coord_relation
                        )  # need to know: index == coord, if it's non-trivial
                    return entry
                elif index_coord_relation.operands[0] == index:
                    # 'index' is less than the 'coord' but not known to be equal to the 'coord' but also
                    # not determined to be within a previous entry.  So we simply don't know enough.
                    raise ExprListError(
                        "Could not determine the 'index'-ed element of the ExprList"
                    )
                coord = _simplifiedCoord(Add(coord, one), assumptions,
                                         requirements)
        except TransitivityException:
            raise ExprListError(
                "Could not determine the 'index'-ed element of the ExprList.")
        raise IndexError("Index, %s, past the range of the ExprList, %s" %
                         (str(index), str(self)))