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)
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)
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.")
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
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
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]
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