Exemple #1
0
 def empty_range(_i, _j, _f, assumptions):
     # If the start and end are literal ints and form an
     # empty range, then it should be straightforward to
     # prove that the range is empty.
     from proveit.numbers import is_literal_int, Add
     from proveit.logic import Equals
     from proveit import m
     _m = entries[0].start_index
     _n = entries[0].end_index
     empty_req = Equals(Add(_n, one), _m)
     if is_literal_int(_m) and is_literal_int(_n):
         if _n.as_int() + 1 == _m.as_int():
             empty_req.prove()
     if empty_req.proven(assumptions):
         _f = Lambda(
             (entries[0].parameter, entries[0].body.parameter),
             entries[0].body.body)
         _i = entry_map(_i)
         _j = entry_map(_j)
         return len_of_empty_range_of_ranges.instantiate(
             {
                 m: _m,
                 n: _n,
                 f: _f,
                 i: _i,
                 j: _j
             },
             assumptions=assumptions)
Exemple #2
0
    def deduce_equality(self,
                        equality,
                        assumptions=USE_DEFAULTS,
                        minimal_automation=False):
        from proveit.logic import Equals
        if not isinstance(equality, Equals):
            raise ValueError("The 'equality' should be an Equals expression")
        if equality.lhs != self:
            raise ValueError("The left side of 'equality' should be 'self'")
        # Try a special-case "typical equality".
        if isinstance(equality.rhs, Len):
            if (isinstance(equality.rhs.operand, ExprTuple)
                    and isinstance(self.operand, ExprTuple)):
                if (equality.rhs.operand.num_entries() == 1
                        and isinstance(equality.rhs.operand[0], ExprRange)):
                    try:
                        eq = \
                            self.typical_eq(assumptions=assumptions)
                        if eq.expr == equality:
                            return eq
                    except (NotImplementedError, ValueError):
                        pass

        # Next try to compute each side, simplify each side, and
        # prove they are equal.
        lhs_computation = equality.lhs.computation(assumptions=assumptions)
        if isinstance(equality.rhs, Len):
            # Compute both lengths and see if we can prove that they
            # are equal.
            rhs_computation = equality.rhs.computation(assumptions=assumptions)
            eq = Equals(lhs_computation.rhs, rhs_computation.rhs)
            if eq.lhs == eq.rhs:
                # Trivial reflection -- automation is okay for that.
                eq = eq.prove()
            else:
                eq = eq.prove(assumptions, automation=not minimal_automation)
            return Equals.apply_transitivities(
                [lhs_computation, eq, rhs_computation],
                assumptions=assumptions)
        else:
            # Compute the lhs length and see if we can prove that it is
            # equal to the rhs.
            eq = Equals(lhs_computation.rhs, equality.rhs)
            if eq.lhs == eq.rhs:
                # Trivial reflection -- automation is okay for that.
                eq = eq.prove()
            else:
                eq = eq.prove(assumptions, automation=not minimal_automation)
            return lhs_computation.apply_transitivity(eq,
                                                      assumptions=assumptions)
Exemple #3
0
 def deduce_equality(self, equality, **defaults_config):
     from proveit.logic import Equals
     if not isinstance(equality, Equals):
         raise ValueError("The 'equality' should be an Equals expression")
     if equality.lhs != self:
         raise ValueError("The left side of 'equality' should be 'self'")
     if (isinstance(equality.rhs, Conditional)
             and equality.lhs.condition == equality.rhs.condition):
         value_eq = Equals(equality.lhs.value, equality.rhs.value)
         value_eq.prove(assumptions=defaults.assumptions +
                        (self.condition, ))
         return equality.lhs.value_substitution(value_eq)
     raise NotImplementedError(
         "Conditional.deduce_equality only implemented "
         "for equating Conditionals with the same "
         "condition.")
Exemple #4
0
    def simplification(self, assumptions=USE_DEFAULTS):
        '''
        If possible, return a KnownTruth of this expression equal to a
        canonically simplified form. Checks for an existing simplifcation.
        If it doesn't exist, try some default strategies including a reduction.
        Attempt the Expression-class-specific "doReducedSimplication"
        when necessary.
        '''
        from proveit.logic import Equals, defaultSimplification, SimplificationError
        from proveit import KnownTruth, ProofFailure

        method_called = None
        try:
            # First try the default tricks. If a reduction succesfully occurs,
            # simplification will be called on that reduction.
            simplification = defaultSimplification(self.innerExpr(),
                                                   assumptions=assumptions)
            method_called = defaultSimplification
        except SimplificationError as e:
            # The default did nothing, let's try the Expression-class specific versions of
            # evaluation and simplification.
            try:
                # first try evaluation.  that is as simple as it gets.
                simplification = self.doReducedEvaluation(assumptions)
                method_called = self.doReducedEvaluation
            except (NotImplementedError, SimplificationError):
                try:
                    simplification = self.doReducedSimplification(assumptions)
                    method_called = self.doReducedSimplification
                except (NotImplementedError, SimplificationError):
                    # Simplification did not work.  Just use self-equality.
                    self_eq = Equals(self, self)
                    simplification = self_eq.prove()
                    method_called = self_eq.prove

        if not isinstance(simplification, KnownTruth) or not isinstance(
                simplification.expr, Equals):
            msg = ("%s must return a KnownTruth "
                   "equality, not %s for %s assuming %s" %
                   (method_called, simplification, self, assumptions))
            raise ValueError(msg)
        if simplification.lhs != self:
            msg = ("%s must return a KnownTruth "
                   "equality with 'self' on the left side, not %s for %s "
                   "assuming %s" %
                   (method_called, simplification, self, assumptions))
            raise ValueError(msg)
        # Remember this simplification for next time:
        Equals.simplifications.setdefault(self, set()).add(simplification)

        return simplification
Exemple #5
0
 def coord2param(axis, coord):
     if subbed_indices[axis] == iterParams[axis]:
         return coord  # direct indexing that does not need to be inverted
     # The indexing is not direct; for example, x_{f(p)}.
     # We need to invert f to obtain p from f(p)=coord and register the inversion as a requirement:
     # f(p) = coord.
     param = Equals.invert(Lambda(iterParams[axis],
                                  subbed_indices[axis]),
                           coord,
                           assumptions=assumptions)
     inversion = Equals(
         subbed_indices[axis].substituted(
             {iterParams[axis]: param}), coord)
     requirements.append(
         inversion.prove(assumptions=assumptions))
     return param
Exemple #6
0
    def _iterSubParamVals(self,
                          axis,
                          iterParam,
                          startArg,
                          endArg,
                          exprMap,
                          relabelMap=None,
                          reservedVars=None,
                          assumptions=USE_DEFAULTS,
                          requirements=None):
        '''
        Consider a substitution over a containing iteration (Iter) 
        defined via exprMap, relabelMap, etc, and expand the iteration 
        by substituting the "iteration parameter" over the range from 
        the "starting argument" to the "ending argument" 
        (both inclusive as provided).
        
        When the Indexed variable is substituted with a Composite, any 
        containing Iteration is to be expanded over the iteration range.
        This method returns a list of parameter values that covers 
        occupied portions of the full range in a manner that keeps 
        different inner iterations separate.  In particular, the 
        iteration range is broken up for the different Iter entries that
        are contained in this Composite.  If it is not substituted with 
        a composite, _NoExpandedIteration is raised.
        
        Requirements that are passed back ensure that substituted composites are
        valid (with iterations that have natural number extents), that the 
        start and end indices are within range and at integer positions,
        and also includes equalities for employed simplifications or inversions
        (translating from index coordinates to parameters).
        '''
        from .composite import Composite, IndexingError, _simplifiedCoord, \
                                  _generateCoordOrderAssumptions
        from .expr_tuple import ExprTuple
        from .expr_array import ExprArray
        from proveit.logic import Equals, InSet
        from proveit.number import GreaterEq, LessEq, Add, one, num, \
                                      dist_add, dist_subtract, Naturals
        from proveit._core_.expression.expr import _NoExpandedIteration
        from .iteration import Iter, InvalidIterationError

        if requirements is None: requirements = []

        subbed_var = self.var.substituted(exprMap, relabelMap, reservedVars,
                                          assumptions, requirements)
        index = self.indices[axis]
        subbed_index = index.substituted(exprMap, relabelMap, reservedVars,
                                         assumptions, requirements)

        if not isinstance(subbed_var, Composite) or \
                iterParam not in subbed_index.freeVars():
            # No expansion for this parameter here:
            raise _NoExpandedIteration()  # no expansionn

        # We cannot substitute in a Composite without doing an
        # iteration over it.  Only certain iterations are allowed
        # in this manner however.

        if subbed_index != iterParam:
            # The index isn't simply the parameter.  It has a shift.
            # find the shift.
            if not isinstance(subbed_index, Add):
                raise InvalidIterationError(subbed_index, iterParam)
            shift_terms = [
                term for term in subbed_index.operands if term != iterParam
            ]
            if len(shift_terms) == 1:
                shift = shift_terms[0]  # shift by a single term
            else:
                shift = dist_add(*shift_terms)  # shift by multple terms

        start_index = subbed_index.substituted({iterParam: startArg})
        end_index = subbed_index.substituted({iterParam: endArg})
        entry_span_requirements = []
        coord_simp_requirements = []

        if isinstance(subbed_var, ExprTuple):
            coords = subbed_var.entryCoords(self.base, assumptions,
                                            entry_span_requirements,
                                            coord_simp_requirements)
        else:
            if not isinstance(subbed_var, ExprArray):
                subbed_var_class_str = str(subbed_var.__class__)
                raise TypeError("Indexed variable should only be "
                                "substituted with ExprTuple or "
                                "ExprArray, not %s" % subbed_var_class_str)
            coords = subbed_var.entryCoords(self.base, axis, assumptions,
                                            entry_span_requirements,
                                            coord_simp_requirements)
        assert coords[0] == num(self.base)
        coord_order_assumptions = list(_generateCoordOrderAssumptions(coords))
        #print("indexed sub coords", self, assumptions, start_index, coords, end_index)
        extended_assumptions = assumptions + coord_order_assumptions

        # We will include all of the "entry span" requirements
        # ensuring that the ExprTuple or ExprArray is valid
        # (the length of iterations is a natural number).
        # We will only include coordinate simplification
        # requirements up to the last needed coordinate.
        requirements.extend(entry_span_requirements)

        # Find where the start index and end index belongs
        # relative to the entry coordinates.

        # The start is inclusive and is typically expected to
        # toward the beginning of the coordinates.
        # We'll get the first and the last insertion points w.r.t.
        # all coordinates equivalent to start_index.
        # The "first" insertion point may help determine if we have
        # an empty range case.  Otherwise, we use the "last"
        # insertion point so we are not including multiple equivalent
        # parameter values at the start.
        start_pos_firstlast = \
            LessEq.insertion_point(coords, start_index,
                                    equiv_group_pos = 'first&last',
                                    assumptions=extended_assumptions)
        # Check the start for an out of bounds error.
        start_pos = start_pos_firstlast[1]
        if start_pos == 0:
            msg = ("ExprTuple index out of range: %s not proven "
                   "to be >= %s (the base) when assuming %s" %
                   (str(start_index), str(coords[0]), str(assumptions)))
            raise IndexError(msg)

        # The end position splits into two cases.  In the simple case,
        # it lands at a singular entry as the last entry.  Otherwise,
        # we need to add one to the endArg to ensure we get past any
        # iterations that may or may not be empty and find the
        # insertion point.
        end_pos = None
        if end_index in coords:
            # Might be the simple case of a singular entry as the last
            # entry.
            end_pos = coords.index(end_index)
            end_coord = coords[end_pos]
            if isinstance(end_coord, Iter):
                # Not the simple case -- an iteration rather than
                end_pos = None  # a singular entry.
            else:
                # Check the end for an out of bounds error.
                if end_pos == len(coords) - 1:
                    msg = ("ExprTuple index out of range: %s not proven "
                           "to be < %s when assuming %s" %
                           (str(end_index), str(coords[-1]), str(assumptions)))
                    raise IndexError(msg)
                if start_pos_firstlast[0] > end_pos:
                    # Empty range (if valid at all).  Handle this
                    # at the Iter.substituted level.
                    raise EmptyIterException()

        if end_pos is None:
            # Not the simple case.  We need to add one to the endArg
            # to ensure we get past any iterations that may or may not
            # be empty.
            endArg = _simplifiedCoord(dist_add(endArg, one), assumptions,
                                      requirements)
            end_index = subbed_index.substituted({iterParam: endArg})
            # We would typically expect the end-index to come near the
            # end of the coordinates in which case it is more efficient
            # to search for the insertion point in reverse order, so use
            # Greater instead of Less.
            # Use the "last" insertion point for the start so we are not
            # including multiple equivalent parameter values at the
            # end.
            end_pos_from_end = \
                GreaterEq.insertion_point(list(reversed(coords)), end_index,
                                          equiv_group_pos = 'last',
                                          assumptions=extended_assumptions)
            # Check the end for an out of bounds error.
            if end_pos_from_end == 0:
                msg = ("ExprTuple index out of range: %s not proven "
                       "to be <= %s when assuming %s." %
                       (str(end_index), str(coords[-1]), str(assumptions)))
                raise IndexError(msg)
            end_pos = len(coords) - end_pos_from_end
            # Check to see if the range is empty.
            # Note: when start_pos==end_pos is the case when both
            # are within the same entry.
            if start_pos > end_pos:
                # Empty range (if valid at all).  Handle this
                # at the Iter.substituted level.
                raise EmptyIterException()

        # Include coordinate simplification requirements up to
        # the last used coordinate.
        coord_simp_req_map = {eq.rhs: eq for eq in coord_simp_requirements}
        for coord in coords[:end_pos + 1]:
            if coord in coord_simp_req_map:
                requirements.append(coord_simp_req_map[coord])

        # End-point requirements may be needed.
        for coord_and_endpoint in [(coords[start_pos - 1], start_index),
                                   (end_index, coords[end_pos])]:
            if coord_and_endpoint[0] == coord_and_endpoint[1]:
                # When the endpoint index is the same as the
                # coordinate, we don't need to add a requirement.
                continue
            try:
                # See if we simply need to prove an equality between
                # the endpoint index and the coordinate.
                eq = Equals(*coord_and_endpoint)
                eq.prove(assumptions, automation=False)
                requirements.append(eq)
            except ProofFailure:
                # Otherwise, we must prove that the difference
                # between the coordinate and endpoint
                # is in the set of natural numbers (integral and
                # in the correct order).
                requirement = \
                    InSet(dist_subtract(*reversed(coord_and_endpoint)),
                          Naturals)
                # Knowing the simplification may help prove the
                # requirement.
                _simplifiedCoord(requirement, assumptions, [])
                requirements.append(requirement.prove(assumptions))

        # We must put each coordinate in terms of iter parameter
        # values (arguments) via inverting the subbed_index.
        def coord2param(coord):
            if subbed_index == iterParam:
                # Direct indexing that does not need to be inverted:
                return coord
            # We must subtract by the 'shift' that the index
            # adds to the parameter in order to invert from
            # the coordinate back to the corresponding parameter:
            param = dist_subtract(coord, shift)
            param = _simplifiedCoord(param, assumptions, requirements)
            return param

        coord_params = [
            coord2param(coord) for coord in coords[start_pos:end_pos]
        ]

        # If the start and end are the same expression or known to
        # be equal, just return [startArg].
        if startArg == endArg:
            return [startArg]
        try:
            eq = Equals(startArg, endArg)
            requirement = eq.prove(assumptions, automation=False)
            requirements.append(requirement)
            return [startArg]
        except ProofFailure:
            # Return the start, end, and coordinates at the
            # start of entries in between.
            return [startArg] + coord_params + [endArg]
Exemple #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 .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