def _createOperand(instanceVars, instanceExpr, conditions): return Lambda(instanceVars, instanceExpr, conditions)
def merger(self, assumptions=USE_DEFAULTS): ''' If this is an tuple of expressions that can be directly merged together into a single ExprRange, return this proven equivalence. For example, {j \in Naturals, k-(j+1) \in Naturals} |- (x_1, .., x_j, x_{j+1}, x_{j+2}, ..., x_k) = (x_1, ..., x_k) ''' from proveit._core_.expression.lambda_expr import Lambda from .expr_range import ExprRange from proveit.relation import TransRelUpdater from proveit.core_expr_types.tuples._theorems_ import ( merge, merge_front, merge_back, merge_extension, merge_pair, merge_series) from proveit._common_ import f, i, j, k, l, x from proveit.number import Add, one # A convenience to allow successive update to the equation via # transitivities (starting with self=self). eq = TransRelUpdater(self, assumptions) # Determine the position of the first ExprRange item and get the # lambda map. first_range_pos = len(self) lambda_map = None for _k, item in enumerate(self): if isinstance(item, ExprRange): lambda_map = Lambda(item.lambda_map.parameter, item.lambda_map.body) first_range_pos = _k break if 1 < first_range_pos: if lambda_map is None: raise NotImplementedError("Means of extracting a lambda " "map has not been implemented") pass # need the lambda map # Collapse singular items at the beginning. front_singles = ExprTuple(eq.expr[:first_range_pos]) i_sub = lambda_map.extractArgument(front_singles[0]) j_sub = lambda_map.extractArgument(front_singles[-1]) if len(front_singles) == 2: # Merge a pair of singular items. front_merger = merge_pair.specialize( { f: lambda_map, i: i_sub, j: j_sub }, assumptions=assumptions) else: # Merge a series of singular items in one shot. front_merger = merge_series.specialize( { f: lambda_map, x: front_singles, i: i_sub, j: j_sub }, assumptions=assumptions) eq.update( front_merger.substitution(self.innerExpr()[:first_range_pos], assumptions=assumptions)) if len(eq.expr) == 1: # We have accomplished a merger down to one item. return eq.relation if len(eq.expr) == 2: # Merge a pair. if isinstance(eq.expr[0], ExprRange): if isinstance(eq.expr[1], ExprRange): # Merge a pair of ExprRanges. item = eq.expr[1] other_lambda_map = Lambda(item.lambda_map.parameter, item.lambda_map.body) if other_lambda_map != lambda_map: raise ExprTupleError( "Cannot merge together ExprRanges " "with different lambda maps: %s vs %s" % (lambda_map, other_lambda_map)) _i, _j = eq.expr[0].start_index, eq.expr[0].end_index _k, _l = eq.expr[1].start_index, eq.expr[1].end_index merger = \ merge.specialize({f:lambda_map, i:_i, j:_j, k:_k, l:_l}, assumptions=assumptions) else: # Merge an ExprRange and a singular item. _i, _j = eq.expr[0].start_index, eq.expr[0].end_index _k = lambda_map.extractArgument(eq.expr[1]) if _k == Add(_j, one): merger = merge_extension.specialize( { f: lambda_map, i: _i, j: _j }, assumptions=assumptions) else: merger = merge_back.specialize( { f: lambda_map, i: _i, j: _j, k: _k }, assumptions=assumptions) else: # Merge a singular item and ExprRange. iSub = lambda_map.extractArgument(eq.expr[0]) jSub, kSub = eq.expr[1].start_index, eq.expr[1].end_index merger = \ merge_front.specialize({f:lambda_map, i:iSub, j:jSub, k:kSub}, assumptions=assumptions) eq.update(merger) return eq.relation while len(eq.expr) > 1: front_merger = ExprTuple(*eq.expr[:2]).merger(assumptions) eq.update( front_merger.substitution(eq.expr.innerExpr(assumptions)[:2], assumptions=assumptions)) return eq.relation
def basic_replaced(self, repl_map, *, allow_relabeling=False, requirements=None): ''' 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.basic_replaced( repl_map, allow_relabeling=allow_relabeling, requirements=requirements) subbed_operands = \ self.operands.basic_replaced( repl_map, allow_relabeling=allow_relabeling, requirements=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, requirements=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) return op_class._checked_make( ['Operation'], sub_expressions=subbed_sub_exprs, style_preferences=self._style_data.styles) subbed_sub_exprs = (subbed_operator, subbed_operands) return self.__class__._checked_make( self._core_info, subbed_sub_exprs, style_preferences=self._style_data.styles)
def merger(self, **defaults_config): ''' If this is an tuple of expressions that can be directly merged together into a single ExprRange, return this proven equivalence. For example, {j \in Natural, k-(j+1) \in Natural} |- (x_1, .., x_j, x_{j+1}, x_{j+2}, ..., x_k) = (x_1, ..., x_k) ''' from proveit._core_.expression.lambda_expr import ( Lambda, ArgumentExtractionError) from .expr_range import ExprRange, simplified_index from proveit.relation import TransRelUpdater from proveit.core_expr_types.tuples import ( merge, merge_front, merge_back, merge_extension, merge_pair, merge_series) from proveit import f, i, j, k, l, x from proveit.numbers import one, Add, subtract # A convenience to allow successive update to the equation via # transitivities (starting with self=self). eq = TransRelUpdater(self) # Determine the position of the first ExprRange item and get the # lambda map. first_range_pos = len(self.entries) lambda_map = None for _k, item in enumerate(self): if isinstance(item, ExprRange): lambda_map = Lambda(item.lambda_map.parameter, item.lambda_map.body) first_range_pos = _k break if 1 < first_range_pos: if lambda_map is None: raise NotImplementedError("Means of extracting a lambda " "map has not been implemented") pass # need the lambda map # Collapse singular items at the beginning. front_singles = ExprTuple(eq.expr[:first_range_pos]) i_sub = lambda_map.extract_argument(front_singles[0]) j_sub = lambda_map.extract_argument(front_singles[-1]) if len(front_singles.entries) == 2: # Merge a pair of singular items. front_merger = merge_pair.instantiate( {f: lambda_map, i: i_sub, j: j_sub}) else: # Merge a series of singular items in one shot. front_merger = merge_series.instantiate( {f: lambda_map, x: front_singles, i: i_sub, j: j_sub}) eq.update( front_merger.substitution( self.inner_expr()[:first_range_pos])) if eq.expr.num_entries() == 1: # We have accomplished a merger down to one item. return eq.relation if eq.expr.num_entries() == 2: # Merge a pair. if isinstance(eq.expr[0], ExprRange): if isinstance(eq.expr[1], ExprRange): # Merge a pair of ExprRanges. item = eq.expr[1] other_lambda_map = Lambda(item.lambda_map.parameter, item.lambda_map.body) if other_lambda_map != lambda_map: raise ExprTupleError( "Cannot merge together ExprRanges " "with different lambda maps: %s vs %s" % (lambda_map, other_lambda_map)) _i, _j = eq.expr[0].true_start_index, eq.expr[0].true_end_index _k, _l = eq.expr[1].true_start_index, eq.expr[1].true_end_index merger = \ merge.instantiate( {f: lambda_map, i: _i, j: _j, k: _k, l: _l}) else: # Merge an ExprRange and a singular item. _i, _j = eq.expr[0].true_start_index, eq.expr[0].true_end_index try: _k = lambda_map.extract_argument(eq.expr[1]) except ArgumentExtractionError: _k = simplified_index(Add(_j, one)) if _k == Add(_j, one): merger = merge_extension.instantiate( {f: lambda_map, i: _i, j: _j}) else: merger = merge_back.instantiate( {f: lambda_map, i: _i, j: _j, k: _k}) else: # Merge a singular item and ExprRange. _i = simplified_index( subtract(eq.expr[1].true_start_index, one)) _j, _k = eq.expr[1].true_start_index, eq.expr[1].true_end_index merger = \ merge_front.instantiate({f: lambda_map, i: _i, j: _j, k: _k}) all_decreasing = all(expr.is_decreasing() for expr in eq.expr if isinstance(expr, ExprRange)) if all_decreasing: # Apply the 'decreasing' order style to match what we # had originally. for _i in (0, 1): if isinstance(eq.expr[_i], ExprRange): merger = (merger.inner_expr().lhs[_i] .with_decreasing_order()) merger = merger.inner_expr().rhs[0].with_decreasing_order() eq.update(merger) return eq.relation while eq.expr.num_entries() > 1: front_merger = ExprTuple(*eq.expr[:2].entries).merger() eq.update(front_merger.substitution( eq.expr.inner_expr()[:2])) return eq.relation