def extractMyInitArgValue(self, argName): ''' Return the most proper initialization value for the initialization argument of the given name in order to reconstruct this Expression in its current style. ''' init_argname_mapping = self.__class__._init_argname_mapping_ argName = init_argname_mapping.get(argName, argName) if argName=='operator': return self.operator # simply the operator elif argName=='instanceVarOrVars': # return the joined instance variables according to style. return singleOrCompositeExpression(OperationOverInstances.explicitInstanceVars(self)) elif argName=='instanceExpr': # return the inner instance expression after joining the # instance variables according to the style return OperationOverInstances.explicitInstanceExpr(self) elif argName=='domain' or argName=='domains': # return the proper single domain or list of domains if self.domain is None: return None domains = OperationOverInstances.explicitDomains(self) if domains == [self.domain]*len(domains): return self.domain if argName=='domain' else None elif not None in domains: return ExprList(*domains) if argName=='domains' else None return None elif argName=='conditions': # return the joined conditions excluding domain conditions return singleOrCompositeExpression(OperationOverInstances.explicitConditions(self))
def __init__(self, lambda_map, start_index_or_indices, end_index_or_indices, styles=dict(), requirements=tuple()): if not isinstance(lambda_map, Lambda): raise TypeError( 'When creating an Iter Expression, the lambda_map argument must be a Lambda expression' ) if isinstance(start_index_or_indices, ExprList) and len(start_index_or_indices) == 1: start_index_or_indices = start_index_or_indices[0] self.start_index_or_indices = singleOrCompositeExpression( start_index_or_indices) if isinstance(self.start_index_or_indices, Composite): # a composite of multiple indices self.start_indices = self.start_index_or_indices else: # a single index self.start_index = self.start_index_or_indices # wrap a single index in a composite for convenience self.start_indices = compositeExpression( self.start_index_or_indices) if isinstance(end_index_or_indices, ExprList) and len(end_index_or_indices) == 1: end_index_or_indices = end_index_or_indices[0] self.end_index_or_indices = singleOrCompositeExpression( end_index_or_indices) if isinstance(self.end_index_or_indices, Composite): # a composite of multiple indices self.end_indices = self.end_index_or_indices else: # a single index self.end_index = self.end_index_or_indices # wrap a single index in a composite for convenience self.end_indices = compositeExpression(self.end_index_or_indices) self.ndims = len(self.start_indices) if self.ndims != len(self.end_indices): raise ValueError( "Inconsistent number of 'start' and 'end' indices") if len(lambda_map.parameters) != len(self.start_indices): raise ValueError( "Inconsistent number of indices and lambda map parameters") Expression.__init__(self, ['Iter'], [ lambda_map, self.start_index_or_indices, self.end_index_or_indices ], styles=styles, requirements=requirements) self.lambda_map = lambda_map
def __init__(self, operator_or_operators, operand_or_operands, styles=dict(), requirements=tuple()): ''' Create an operation with the given operator(s) and operand(s). The operator(s) must be Label(s) (a Variable or a Literal). When there is a single operator, there will be an 'operator' attribute. When there is a single operand, there will be an 'operand' attribute. In any case, there will be 'operators' and 'operands' attributes that bundle the one or more Expressions into a composite Expression. ''' from proveit._core_.expression.composite import Composite, compositeExpression, singleOrCompositeExpression, Iter, Indexed from proveit._core_.expression.label.label import Label from proveit import Context if hasattr(self.__class__, '_operator_') and operator_or_operators==self.__class__._operator_: operator = operator_or_operators context = Context(inspect.getfile(self.__class__)) if Expression.contexts[operator] != context: raise OperationError("Expecting '_operator_' Context to match the Context of the Operation sub-class. Use 'context=__file__'.") self.operator_or_operators = singleOrCompositeExpression(operator_or_operators) self.operand_or_operands = singleOrCompositeExpression(operand_or_operands) if isinstance(self.operator_or_operators, Composite): # a composite of multiple operators: self.operators = self.operator_or_operators for operator in self.operators: if isinstance(operator, Iter): if not isinstance(operator.lambda_map.body, Indexed): raise TypeError('operators must be Labels, Indexed variables, or iteration (Iter) over Indexed variables.') elif not isinstance(operator, Label) and not isinstance(operator, Indexed): raise TypeError('operator must be a Label, Indexed variable, or iteration (Iter) over Indexed variables.') else: # a single operator self.operator = self.operator_or_operators if not isinstance(self.operator, Label) and not isinstance(self.operator, Indexed): raise TypeError('operator must be a Label, Indexed variable, or iteration (Iter) over Indexed variables.') # wrap a single operator in a composite for convenience self.operators = compositeExpression(self.operator) if isinstance(self.operand_or_operands, Composite): # a composite of multiple operands self.operands = self.operand_or_operands else: # a single operand self.operand = self.operand_or_operands # wrap a single operand in a composite for convenience self.operands = compositeExpression(self.operand) if 'operation' not in styles: styles['operation'] = 'normal' # vs 'function if 'wrapPositions' not in styles: styles['wrapPositions'] = '()' # no wrapping by default if 'justification' not in styles: styles['justification'] = 'center' Expression.__init__(self, ['Operation'], [self.operator_or_operators, self.operand_or_operands], styles=styles, requirements=requirements)
def extractInitArgValue(argName, operator, operand): ''' Given a name of one of the arguments of the __init__ method, return the corresponding value as determined by the given operator and operand for an OperationOverInstances Expression. Override this if the __init__ argument names are different than the default. ''' assert isinstance( operand, Lambda ), "Expecting OperationOverInstances operand to be a Lambda expression" if argName == 'operator': return operator if argName == 'domain' or argName == 'domains': return None # specify domains implicitly through conditions if argName == 'instanceVarOrVars': return singleOrCompositeExpression(operand.parameters) elif argName == 'instanceExpr': return operand.body elif argName == 'conditions': conditions = operand.conditions #if len(conditions)==0: return tuple() return conditions
def __init__(self, parameter_or_parameters, body, conditions=tuple(), styles=None, requirements=tuple()): ''' Initialize a Lambda function expression given parameter(s) and a body. Each parameter must be a Variable. When there is a single parameter, there will be a 'parameter' attribute. Either way, there will be a 'parameters' attribute that bundles the one or more Variables into an ExprList. The 'body' attribute will be the lambda function body Expression (that may or may not be a Composite). Zero or more expressions may be provided. ''' from proveit._core_.expression.composite import compositeExpression, singleOrCompositeExpression, Iter if styles is None: styles = dict() self.parameters = compositeExpression(parameter_or_parameters) parameterVars = [getParamVar(parameter) for parameter in self.parameters] if len(self.parameters) == 1: # has a single parameter self.parameter = self.parameters[0] self.parameter_or_parameters = self.parameter else: self.parameter_or_parameters = self.parameters self.parameterVars = tuple(parameterVars) self.parameterVarSet = frozenset(parameterVars) if len(self.parameterVarSet) != len(self.parameters): raise ValueError('Lambda parameters Variables must be unique with respect to each other.') body = singleOrCompositeExpression(body) if not isinstance(body, Expression): raise TypeError('A Lambda body must be of type Expression') if isinstance(body, Iter): raise TypeError('An Iter must be within an ExprList or ExprTensor, not directly as a Lambda body') self.body = body self.conditions = compositeExpression(conditions) for requirement in self.body.getRequirements(): if not self.parameterVarSet.isdisjoint(requirement.freeVars()): raise LambdaError("Cannot generate a Lambda expression with parameter variables involved in Lambda body requirements: " + str(requirement)) sub_exprs = [self.parameter_or_parameters, self.body] if len(self.conditions)>0: sub_exprs.append(self.conditions) # Create a "generic" version (if not already) of the Lambda expression since the # choice of parameter labeling is irrelevant. generic_body_vars = self.body._genericExpr.usedVars() generic_condition_vars = self.conditions._genericExpr.usedVars() used_generic_vars = generic_body_vars.union(generic_condition_vars) generic_params = tuple(safeDummyVars(len(self.parameterVars), *(used_generic_vars-self.parameterVarSet))) if generic_params != self.parameterVars: relabel_map = {param:generic_param for param, generic_param in zip(self.parameterVars, generic_params)} # temporarily disable automation during the relabeling process prev_automation = defaults.automation defaults.automation = False generic_parameters = self.parameters._genericExpr.relabeled(relabel_map) generic_body = self.body._genericExpr.relabeled(relabel_map) generic_conditions = self.conditions._genericExpr.relabeled(relabel_map) self._genericExpr = Lambda(generic_parameters, generic_body, generic_conditions, styles=dict(styles), requirements=requirements) defaults.automation = prev_automation # restore to previous value Expression.__init__(self, ['Lambda'], sub_exprs, styles=styles, requirements=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)
def __init__(self, operator_or_operators, operand_or_operands, styles=None): ''' Create an operation with the given operator(s) and operand(s). The operator(s) must be Label(s) (a Variable or a Literal). When there is a single operator, there will be an 'operator' attribute. When there is a single operand, there will be an 'operand' attribute. In any case, there will be 'operators' and 'operands' attributes that bundle the one or more Expressions into a composite Expression. ''' from proveit._core_.expression.composite import ( Composite, compositeExpression, singleOrCompositeExpression, ExprTuple, ExprRange) from proveit._core_.expression.label.label import Label from .indexed_var import IndexedVar if styles is None: styles = dict() if hasattr(self.__class__, '_operator_' ) and operator_or_operators == self.__class__._operator_: operator = operator_or_operators #if Expression.contexts[operator._style_id] != operator.context: # raise OperationError("Expecting '_operator_' Context to match the Context of the Operation sub-class. Use 'context=__file__'.") if isinstance(operator_or_operators, ExprRange): operator_or_operators = [operator_or_operators] if isinstance(operand_or_operands, ExprRange): operand_or_operands = [operand_or_operands] self.operator_or_operators = singleOrCompositeExpression( operator_or_operators) # Reduce to a single operand if it is just a tuple with # one non-ExprRange and non-ExprTuple element. self.operand_or_operands = singleOrCompositeExpression( operand_or_operands, do_singular_reduction=True) def raiseBadOperatorType(operator): raise TypeError('operator(s) must be a Label, an indexed variable ' '(IndexedVar), or iteration (Iter) over indexed' 'variables (IndexedVar). %s is none of those.' % str(operator)) if isinstance(self.operator_or_operators, Composite): # a composite of multiple operators: self.operators = self.operator_or_operators for operator in self.operators: if isinstance(operator, ExprRange): if not isinstance(operator.body, IndexedVar): raiseBadOperatorType(operator) elif not isinstance(operator, Label) and not isinstance( operator, IndexedVar): raiseBadOperatorType(operator) else: # a single operator self.operator = self.operator_or_operators if not isinstance(self.operator, Label) and not isinstance( self.operator, IndexedVar): raiseBadOperatorType(self.operator) # wrap a single operator in a composite for convenience self.operators = compositeExpression(self.operator) if isinstance(self.operand_or_operands, Composite): # a composite of multiple operands self.operands = self.operand_or_operands if (isinstance(self.operands, ExprTuple) and len(self.operands) == 1 and isinstance(self.operands[0], ExprTuple)): # This is a single operand that is an ExprTuple. self.operand = self.operands[0] else: # a single operand self.operand = self.operand_or_operands # wrap a single operand in a composite for convenience self.operands = compositeExpression(self.operand) if 'operation' not in styles: styles['operation'] = 'infix' # vs 'function' if 'wrapPositions' not in styles: styles['wrapPositions'] = '()' # no wrapping by default if 'justification' not in styles: styles['justification'] = 'center' sub_exprs = (self.operator_or_operators, self.operand_or_operands) if isinstance(self, IndexedVar): core_type = 'IndexedVar' else: core_type = 'Operation' Expression.__init__(self, [core_type], sub_exprs, styles=styles)
def __init__(self, parameter_or_parameters, body, start_index_or_indices, end_index_or_indices, styles=None, requirements=tuple(), _lambda_map=None): ''' Create an Iter that represents an iteration of the body for the parameter(s) ranging from the start index/indices to the end index/indices. A Lambda expression will be created as its sub-expression that maps the parameter(s) to the body with conditions that restrict the parameter(s) to the appropriate interval. _lambda_map is used internally for efficiently rebuilding an Iter. ''' from proveit.logic import InSet from proveit.number import Interval if _lambda_map is not None: # Use the provided 'lambda_map' instead of creating one. lambda_map = _lambda_map pos_args = (parameter_or_parameters, body, start_index_or_indices, end_index_or_indices) if pos_args != (None, None, None, None): raise ValueError( "Positional arguments of the Init constructor " "should be None if lambda_map is provided.") parameters = lambda_map.parameters body = lambda_map.body conditions = lambda_map.conditions if len(conditions) != len(parameters): raise ValueError( "Inconsistent number of conditions and lambda " "map parameters") start_indices, end_indices = [], [] for param, condition in zip(parameters, conditions): invalid_condition_msg = ( "Not the right kind of lambda_map condition " "for an iteration") if not isinstance(condition, InSet) or condition.element != param: raise ValueError(invalid_condition_msg) domain = condition.domain if not isinstance(domain, Interval): raise ValueError(invalid_condition_msg) start_index, end_index = domain.lowerBound, domain.upperBound start_indices.append(start_index) end_indices.append(end_index) self.start_indices = ExprTuple(*start_indices) self.end_indices = ExprTuple(*end_indices) if len(parameters) == 1: self.start_index = self.start_indices[0] self.end_index = self.end_indices[0] self.start_index_or_indices = self.start_index self.end_index_or_indices = self.end_index else: self.start_index_or_indices = self.start_indices self.end_index_or_indices = self.end_indices else: parameters = compositeExpression(parameter_or_parameters) start_index_or_indices = singleOrCompositeExpression( start_index_or_indices) if isinstance(start_index_or_indices, ExprTuple) and len(start_index_or_indices) == 1: start_index_or_indices = start_index_or_indices[0] self.start_index_or_indices = start_index_or_indices if isinstance(start_index_or_indices, Composite): # a composite of multiple indices self.start_indices = self.start_index_or_indices else: # a single index self.start_index = self.start_index_or_indices # wrap a single index in a composite for convenience self.start_indices = compositeExpression( self.start_index_or_indices) end_index_or_indices = singleOrCompositeExpression( end_index_or_indices) if isinstance(end_index_or_indices, ExprTuple) and len(end_index_or_indices) == 1: end_index_or_indices = end_index_or_indices[0] self.end_index_or_indices = end_index_or_indices if isinstance(self.end_index_or_indices, Composite): # a composite of multiple indices self.end_indices = self.end_index_or_indices else: # a single index self.end_index = self.end_index_or_indices # wrap a single index in a composite for convenience self.end_indices = compositeExpression( self.end_index_or_indices) conditions = [] for param, start_index, end_index in zip(parameters, self.start_indices, self.end_indices): conditions.append( InSet(param, Interval(start_index, end_index))) lambda_map = Lambda(parameters, body, conditions=conditions) self.ndims = len(self.start_indices) if self.ndims != len(self.end_indices): raise ValueError( "Inconsistent number of 'start' and 'end' indices") if len(parameters) != len(self.start_indices): raise ValueError( "Inconsistent number of indices and lambda map parameters") Expression.__init__(self, ['Iter'], [lambda_map], styles=styles, requirements=requirements) self.lambda_map = lambda_map self._checkIndexedRestriction(body)
def __init__(self, parameter_or_parameters, body, conditions=tuple(), styles=dict(), requirements=tuple()): ''' Initialize a Lambda function expression given parameter(s) and a body. Each parameter must be a Variable. When there is a single parameter, there will be a 'parameter' attribute. Either way, there will be a 'parameters' attribute that bundles the one or more Variables into an ExprList. The 'body' attribute will be the lambda function body Expression (that may or may not be a Composite). Zero or more expressions may be provided. ''' from proveit._core_.expression.composite import compositeExpression, singleOrCompositeExpression, Iter, Indexed from proveit._core_.expression.label import Variable self.parameters = compositeExpression(parameter_or_parameters) parameterVars = list() for parameter in self.parameters: if isinstance(parameter, Iter) and isinstance( parameter.lambda_map.body, Indexed): parameterVars.append(parameter.lambda_map.body.var) elif isinstance(parameter, Indexed): parameterVars.append(parameter.var) elif isinstance(parameter, Variable): parameterVars.append(parameter) else: raise TypeError( 'parameters must be a Variables, Indexed variable, or iteration (Iter) over Indexed variables.' ) if len(self.parameters) == 1: # has a single parameter self.parameter = self.parameters[0] self.parameter_or_parameters = self.parameter else: self.parameter_or_parameters = self.parameters self.parameterVars = tuple(parameterVars) self.parameterVarSet = frozenset(parameterVars) if len(self.parameterVarSet) != len(self.parameters): raise ValueError( 'Lambda parameters Variables must be unique with respect to each other.' ) body = singleOrCompositeExpression(body) if not isinstance(body, Expression): raise TypeError('A Lambda body must be of type Expression') if isinstance(body, Iter): raise TypeError( 'An Iter must be within an ExprList or ExprTensor, not directly as a Lambda body' ) self.body = body self.conditions = compositeExpression(conditions) for requirement in self.body.requirements: if not self.parameterVarSet.isdisjoint(requirement.freeVars()): raise LambdaError( "Cannot generate a Lambda expression with parameter variables involved in Lambda body requirements: " + str(requirement)) Expression.__init__( self, ['Lambda'], [self.parameter_or_parameters, self.body, self.conditions], styles=styles, requirements=requirements)