def _replaced(self, repl_map, allow_relabeling, assumptions, requirements, equality_repl_requirements): ''' Returns this expression with sub-expressions substituted according to the replacement map (repl_map) dictionary. When an operater of an Operation is substituted by a Lambda map, the operation itself will be substituted with the Lambda map applied to the operands. For example, substituting f : (x,y) -> x+y on f(a, b) will result in a+b. When performing operation substitution with a range of parameters, the Lambda map application will require the number of these parameters to equal with the number of corresponding operand elements. For example, f : (a, b_1, ..., b_n) -> a*b_1 + ... + a*b_n n : 3 applied to f(w, x, y, z) will result in w*x + w*y + w*z provided that |(b_1, ..., b_3)| = |(x, y, z)| is proven. Assumptions may be needed to prove such requirements. Requirements will be appended to the 'requirements' list if one is provided. There are limitations with respect the Lambda map application involving iterated parameters when perfoming operation substitution in order to keep derivation rules (i.e., instantiation) simple. For details, see the ExprRange.substituted documentation. ''' from proveit import Lambda, ExprRange if len(repl_map) > 0 and (self in repl_map): # The full expression is to be substituted. return repl_map[self] # Perform substitutions for the operator(s) and operand(s). subbed_operator = \ self.operator.replaced(repl_map, allow_relabeling, assumptions, requirements, equality_repl_requirements) subbed_operands = \ self.operands.replaced(repl_map, allow_relabeling, assumptions, requirements, equality_repl_requirements) # Check if the operator is being substituted by a Lambda map in # which case we should perform full operation substitution. if isinstance(subbed_operator, Lambda): # Substitute the entire operation via a Lambda map # application. For example, f(x, y) -> x + y, # or g(a, b_1, ..., b_n) -> a * b_1 + ... + a * b_n. if isinstance(subbed_operator.body, ExprRange): raise ImproperReplacement( self, repl_map, "The function %s cannot be defined using this " "lambda, %s, that has an ExprRange for its body; " "that could lead to tuple length contradictions." % (self.operator, subbed_operator)) return Lambda._apply( subbed_operator.parameters, subbed_operator.body, *subbed_operands.entries, assumptions=assumptions, requirements=requirements, equality_repl_requirements=equality_repl_requirements) # If the operator is a literal operator of # an Operation class defined via an "_operator_" class # attribute, then create the Operation of that class. if subbed_operator in Operation.operation_class_of_operator: op_class = Operation.operation_class_of_operator[subbed_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. subbed_sub_exprs = (subbed_operator, subbed_operands) substituted = op_class._checked_make( ['Operation'], sub_expressions=subbed_sub_exprs) return substituted._auto_reduced(assumptions, requirements, equality_repl_requirements) subbed_sub_exprs = (subbed_operator, subbed_operands) substituted = self.__class__._checked_make(self._core_info, subbed_sub_exprs) return substituted._auto_reduced(assumptions, requirements, equality_repl_requirements)
def _replaced(self, repl_map, allow_relabeling, assumptions, requirements, equality_repl_requirements): ''' Returns this expression with sub-expressions substituted according to the replacement map (repl_map) dictionary. When an operater of an Operation is substituted by a Lambda map, the operation itself will be substituted with the Lambda map applied to the operands. For example, substituting f : (x,y) -> x+y on f(a, b) will result in a+b. When performing operation substitution with a range of parameters, the Lambda map application will require the number of these parameters to equal with the number of corresponding operand elements. For example, f : (a, b_1, ..., b_n) -> a*b_1 + ... + a*b_n n : 3 applied to f(w, x, y, z) will result in w*x + w*y + w*z provided that |(b_1, ..., b_3)| = |(x, y, z)| is proven. Assumptions may be needed to prove such requirements. Requirements will be appended to the 'requirements' list if one is provided. There are limitations with respect the Lambda map application involving iterated parameters when perfoming operation substitution in order to keep derivation rules (i.e., instantiation) simple. For details, see the ExprRange.substituted documentation. ''' from proveit import (Lambda, singleOrCompositeExpression, compositeExpression, ExprTuple, ExprRange) if len(repl_map) > 0 and (self in repl_map): # The full expression is to be substituted. return repl_map[self] # Perform substitutions for the operator(s) and operand(s). subbed_operator_or_operators = \ self.operator_or_operators.replaced(repl_map, allow_relabeling, assumptions, requirements, equality_repl_requirements) subbed_operand_or_operands = \ self.operand_or_operands.replaced(repl_map, allow_relabeling, assumptions, requirements, equality_repl_requirements) subbed_operators = compositeExpression(subbed_operator_or_operators) # Check if the operator is being substituted by a Lambda map in # which case we should perform full operation substitution. if len(subbed_operators) == 1: subbed_operator = subbed_operators[0] if isinstance(subbed_operator, Lambda): # Substitute the entire operation via a Lambda map # application. For example, f(x, y) -> x + y, # or g(a, b_1, ..., b_n) -> a * b_1 + ... + a * b_n. if isinstance(subbed_operator.body, ExprRange): raise ImproperReplacement( self, repl_map, "The function %s cannot be defined using this " "lambda, %s, that has an ExprRange for its body; " "that could lead to tuple length contradictions." % (self.operator, subbed_operator)) if len(self.operands)==1 and \ not isinstance(self.operands[0], ExprRange): # A single operand case (even if that operand # happens to be a tuple). subbed_operands = [subbed_operand_or_operands] else: subbed_operands = subbed_operand_or_operands return Lambda._apply( subbed_operator.parameters, subbed_operator.body, *subbed_operands, assumptions=assumptions, requirements=requirements, equality_repl_requirements=equality_repl_requirements) had_singular_operand = hasattr(self, 'operand') if (had_singular_operand and isinstance(subbed_operand_or_operands, ExprTuple) and not isinstance(self.operand_or_operands, ExprTuple)): # If a singular operand is replaced with an ExprTuple, # we must wrap an extra ExprTuple around it to indicate # that it is still a singular operand with the operand # as the ExprTuple (rather than expanding to multiple # operands). subbed_operand_or_operands = ExprTuple(subbed_operand_or_operands) else: # Possibly collapse multiple operands to a single operand # via "do_singular_reduction=True". subbed_operand_or_operands = singleOrCompositeExpression( subbed_operand_or_operands, do_singular_reduction=True) # 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. subbed_sub_exprs = (operator, subbed_operand_or_operands) substituted = op_class._checked_make( ['Operation'], styles=None, subExpressions=subbed_sub_exprs) return substituted._auto_reduced( assumptions, requirements, equality_repl_requirements) subbed_sub_exprs = (subbed_operator_or_operators, subbed_operand_or_operands) substituted = self.__class__._checked_make(self._coreInfo, self.getStyles(), subbed_sub_exprs) return substituted._auto_reduced(assumptions, requirements, equality_repl_requirements)