def substituted(self, exprMap, relabelMap=None, reservedVars=None, assumptions=USE_DEFAULTS, requirements=None): ''' Return this expression with its variables substituted according to subMap and/or relabeled according to relabelMap. The Lambda parameters have their own scope within the Lambda body and do not get substituted. They may be relabeled, however. Substitutions within the Lambda body are restricted to exclude the Lambda parameters themselves (these Variables are reserved), consistent with any relabeling. ''' from proveit.logic import Forall self._checkRelabelMap(relabelMap) if len(exprMap)>0 and (self in exprMap): # the full expression is to be substituted return exprMap[self]._restrictionChecked(reservedVars) if relabelMap is None: relabelMap = dict() assumptions = defaults.checkedAssumptions(assumptions) new_params, inner_expr_map, inner_assumptions, inner_reservations = self._innerScopeSub(exprMap, relabelMap, reservedVars, assumptions, requirements) # conditions with substitutions: condition_requirements = [] condition_assumptions = inner_assumptions subbedConditions = self.conditions.substituted(inner_expr_map, relabelMap, inner_reservations, condition_assumptions, condition_requirements) # The lambda body with the substitutions. Add the conditions, with substitutions, as assumptions # since they must be satisfied for the mapping to be well-defined. body_requirements = [] body_assumptions = list(inner_assumptions)+list(subbedConditions) subbedBody = self.body.substituted(inner_expr_map, relabelMap, inner_reservations, body_assumptions, body_requirements) for requirements, requirements_assumptions in zip((condition_requirements, body_requirements), ([], subbedConditions)): for requirement in requirements: if requirement.freeVars().isdisjoint(new_params): requirements.append(requirement) else: # When the requirement involves any of the lambda parameters, we must universally quantify # over those parameters, with appropriate conditions. Appropriate conditions are the # applicable assumptions that involve the lambda parameters. This excludes the 'inner_assumptions' # because they cannot involve lambda parameters (those were excluded). requirement_params = requirement.freeVars().intersection(new_params) requirement_conditions = [condition for condition in requirements_assumptions if not new_params.isdisjoint(condition.freeVars())] requirement = Forall(requirement_params, requirement, conditions=requirement_conditions) requirements.append(requirement) raise ScopingViolation("Substitution requirements must not involve Lambda parameters") try: newLambda = Lambda(new_params, subbedBody, subbedConditions) except TypeError as e: raise ImproperSubstitution(e.args[0]) except ValueError as e: raise ImproperSubstitution(e.args[0]) return newLambda
def _expandingIterRanges(self, iterParams, startArgs, endArgs, exprMap, relabelMap = None, reservedVars = None, assumptions=USE_DEFAULTS, requirements=None): from proveit import Variable, compositeExpression # Can't substitute the lambda parameter variables; they are in a new scope. innerExprMap = {key:value for (key, value) in exprMap.items() if key not in self.parameterVarSet} # Can't use assumptions involving lambda parameter variables innerAssumptions = [assumption for assumption in assumptions if self.parameterVarSet.isdisjoint(assumption.freeVars())] # Handle relabeling and variable reservations consistent with relabeling. innerReservations = dict() if reservedVars is None else dict(reservedVars) for parameterVar in self.parameterVars: # Note that lambda parameters introduce a new scope and don't need to, # themselves, be restriction checked. But they generate new inner restrictions # that disallow any substitution from a variable that isn't in the new scope # to a variable that is in the new scope. # For example, we can relabel y to z in (x, y) -> f(x, y), but not f to x. if parameterVar in relabelMap: relabeledParams = compositeExpression(relabelMap[parameterVar]) for relabeledParam in relabeledParams: if not isinstance(relabeledParam, Variable): raise ImproperSubstitution('May only relabel a Variable to another Variable or list of Variables') innerReservations[relabeledParam] = parameterVar else: # Not relabeled innerReservations[parameterVar] = parameterVar # collect the iter ranges from the body and all conditions iter_ranges = set() for iter_range in self.body.expandingIterRanges(iterParams, startArgs, endArgs, innerExprMap, relabelMap, innerReservations, innerAssumptions, requirements): iter_ranges.add(iter_range) for iter_range in self.conditions.expandingIterRanges(iterParams, startArgs, endArgs, innerExprMap, relabelMap, innerReservations, innerAssumptions, requirements): iter_ranges.add(iter_range) for iter_range in iter_ranges: yield iter_range
def substituted(self, exprMap, relabelMap=None, reservedVars=None, assumptions=USE_DEFAULTS, requirements=None): ''' Return this expression with the variables substituted according to subMap and/or relabeled according to relabelMap. ''' from proveit._core_.expression.composite.composite import compositeExpression from proveit._core_.expression.lambda_expr.lambda_expr import Lambda self._checkRelabelMap(relabelMap) if len(exprMap)>0 and (self in exprMap): return exprMap[self]._restrictionChecked(reservedVars) subbed_operand_or_operands = self.operand_or_operands.substituted(exprMap, relabelMap, reservedVars, assumptions, requirements) subbed_operands = compositeExpression(subbed_operand_or_operands) subbed_operator_or_operators = self.operator_or_operators.substituted(exprMap, relabelMap, reservedVars, assumptions, requirements) subbed_operators = compositeExpression(subbed_operator_or_operators) if len(subbed_operators)==1: subbedOperator = subbed_operators[0] if isinstance(subbedOperator, Lambda): # Substitute the entire operation via a Lambda body # For example, f(x, y) -> x + y. if len(subbed_operands) != len(subbedOperator.parameters): raise ImproperSubstitution('Cannot substitute an Operation with the wrong number of parameters') if len(subbedOperator.conditions) != 0: raise ImproperSubstitution('Operation substitution must be defined via an Unconditioned Lambda expression') operandSubMap = {param:operand for param, operand in zip(subbedOperator.parameters, subbed_operands)} if not reservedVars is None: # the reserved variables of the lambda body excludes the lambda parameters # (i.e., the parameters mask externally reserved variables). lambdaExprReservedVars = {k:v for k, v in reservedVars.items() if k not in subbedOperator.parameterVarSet} else: lambdaExprReservedVars = None return subbedOperator.body._restrictionChecked(lambdaExprReservedVars).substituted(operandSubMap, assumptions=assumptions, requirements=requirements) # remake the Expression with substituted operator and/or operands if len(subbed_operators)==1: # If it is a single operator that is a literal operator of an Operation class # defined via an "_operator_" class attribute, then create the Operation of that class. operator = subbed_operators[0] if operator in Operation.operationClassOfOperator: op_class = Operation.operationClassOfOperator[operator] if op_class != self.__class__: # Don't transfer the styles; they may not apply in the same # manner in the setting of the new operation. return op_class._make(['Operation'], styles=None, subExpressions=[operator, subbed_operand_or_operands]) return self.__class__._make(['Operation'], self.getStyles(), [subbed_operator_or_operators, subbed_operand_or_operands])
def _innerScopeSub(self, exprMap, relabelMap, reservedVars, assumptions, requirements): ''' Helper method for substituted (and used by Iter.substituted) which handles the change in scope properly as well as parameter relabeling (or iterated parameter expansion). ''' from proveit import compositeExpression, Iter, ExprList # Can't substitute the lambda parameter variables; they are in a new scope. inner_expr_map = {key:value for (key, value) in exprMap.items() if key not in self.parameterVarSet} # Handle relabeling and variable reservations consistent with relabeling. inner_reservations = dict() if reservedVars is None else dict(reservedVars) new_params = [] for parameter, parameterVar in zip(self.parameters, self.parameterVars): # Note that lambda parameters introduce a new scope and don't need to, # themselves, be restriction checked. But they generate new inner restrictions # that disallow any substitution from a variable that isn't in the new scope # to a variable that is in the new scope. # For example, we can relabel y to z in (x, y) -> f(x, y), but not f to x. if parameterVar in relabelMap: if isinstance(parameter, Iter): relabeledParams = parameter.substituted(exprMap, relabelMap, reservedVars, assumptions, requirements) if isinstance(relabeledParams, ExprList): # expanding an iteration. For example: x_1, ..., x_n -> a, b, c, d if len(relabeledParams) != len(relabelMap[parameterVar]): raise ImproperSubstitution("Relabeling of iterated parameters incomplete: %d length expansion versus %d length substitution"%(len(relabeledParams), len(relabelMap[parameterVar]))) else: # e.g., x_1, ..., x_n -> y_1, ..., y_n relabeledParams = compositeExpression(relabeledParams) else: relabeledParams = compositeExpression(relabelMap[parameterVar]) for relabeledParam in relabeledParams: new_params.append(relabeledParam) inner_reservations[relabeledParam] = parameterVar else: # can perform a substition in indices of a parameter iteration: x_1, ..., x_n new_params.append(parameter.substituted(inner_expr_map, relabelMap, reservedVars, assumptions, requirements)) inner_reservations[parameterVar] = parameterVar # Can't use assumptions involving lambda parameter variables inner_assumptions = [assumption for assumption in assumptions if assumption.freeVars().isdisjoint(new_params)] return new_params, inner_expr_map, inner_assumptions, inner_reservations
def substituted(self, exprMap, relabelMap=None, reservedVars=None, assumptions=USE_DEFAULTS, requirements=None): ''' Return this expression with its variables substituted according to subMap and/or relabeled according to relabelMap. The Lambda parameters have their own scope within the Lambda body and do not get substituted. They may be relabeled, however. Substitutions within the Lambda body are restricted to exclude the Lambda parameters themselves (these Variables are reserved), consistent with any relabeling. ''' from proveit import compositeExpression, Iter if (exprMap is not None) and (self in exprMap): # the full expression is to be substituted return exprMap[self]._restrictionChecked(reservedVars) if relabelMap is None: relabelMap = dict() assumptions = defaults.checkedAssumptions(assumptions) # Can't substitute the lambda parameter variables; they are in a new scope. innerExprMap = { key: value for (key, value) in exprMap.iteritems() if key not in self.parameterVarSet } # Can't use assumptions involving lambda parameter variables innerAssumptions = [ assumption for assumption in assumptions if self.parameterVarSet.isdisjoint(assumption.freeVars()) ] # Handle relabeling and variable reservations consistent with relabeling. innerReservations = dict() if reservedVars is None else dict( reservedVars) newParams = [] for parameter, parameterVar in zip(self.parameters, self.parameterVars): # Note that lambda parameters introduce a new scope and don't need to, # themselves, be restriction checked. But they generate new inner restrictions # that disallow any substitution from a variable that isn't in the new scope # to a variable that is in the new scope. # For example, we can relabel y to z in (x, y) -> f(x, y), but not f to x. if parameterVar in relabelMap: if isinstance(parameter, Iter): # expanding an iteration. For example: x_1, ..., x_n -> a, b, c, d relabeledParams = parameter.substituted( exprMap, relabelMap, reservedVars, assumptions, requirements) if len(relabeledParams) != len(relabelMap[parameterVar]): raise ImproperSubstitution( "Relabeling of iterated parameters incomplete: %d length expansion versus %d length substitution" % (len(relabeledParams), len(relabelMap[parameterVar]))) else: relabeledParams = compositeExpression( relabelMap[parameterVar]) for relabeledParam in relabeledParams: newParams.append(relabeledParam) innerReservations[relabeledParam] = parameterVar else: # can perform a substition in indices of a parameter iteration: x_1, ..., x_n newParams.append( parameter.substituted(innerExprMap, relabelMap, reservedVars, assumptions, requirements)) innerReservations[parameterVar] = parameterVar # the lambda body with the substitution: subbedBody = self.body.substituted(innerExprMap, relabelMap, innerReservations, innerAssumptions, requirements) # conditions with substitutions: subbedConditions = self.conditions.substituted(innerExprMap, relabelMap, innerReservations, innerAssumptions, requirements) try: newLambda = Lambda(newParams, subbedBody, subbedConditions) except TypeError as e: raise ImproperSubstitution(e.message) except ValueError as e: raise ImproperSubstitution(e.message) return newLambda