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

beginAxioms(locals())

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

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

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

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

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

reverseLessThan = Forall((x, y), Implies(Less(x, y), Greater(y, x)))
Пример #2
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
Пример #3
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)))
Пример #4
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
Пример #5
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