def __init__(self, indices, summand, domain, conditions=tuple()): r''' Sum summand over indices over domains. Arguments serve analogous roles to Forall arguments (found in basiclogic/booleans): indices: instance vars summand: instanceExpressions domains: conditions (except no longer optional) ''' OperationOverInstances.__init__(self, Sum._operator_, indices, summand, domain=domain, conditions=conditions) if len(self.instanceVars) != 1: raise ValueError('Only one index allowed per integral!') self.indices = self.instanceVars self.summand = self.instanceExpr self.index = self.instanceVars[0] if isinstance(self.domain, RealInterval): raise ValueError( 'Sum cannot sum over non-discrete set (e.g. Interval)') elif self.domain == Reals: raise ValueError('Sum cannot sum over Reals.') elif self.domain == Integers: self.domain = Interval(Neg(infinity), infinity) elif self.domain == Naturals: self.domain = Interval(zero, infinity)
def __init__(self, instanceVarOrVars, instanceElement, domain=None, domains=None, conditions=tuple()): ''' Create an expression representing the set of all instanceElement for instanceVar(s) such that the conditions are satisfied: {instanceElement | conditions}_{instanceVar(s) \in S} ''' # nestMultiIvars=False will ensure it does NOT treat multiple instance variables as # nested SetOfAll operations -- that would not make sense. # (unlike forall, exists, summation, and product where it does make sense). OperationOverInstances.__init__(self, SetOfAll._operator_, instanceVarOrVars, instanceElement, domain=domain, conditions=conditions, nestMultiIvars=False) self.instanceElement = self.instanceExpr if hasattr(self, 'instanceVar'): if not hasattr(self, 'domain'): raise ValueError("SetOfAll requires a domain") elif hasattr(self, 'instanceVars'): if not hasattr(self, 'domains') or None in self.domains: raise ValueError("SetOfAll requires a domain(s)") else: assert False, "Expecting either 'instanceVar' or 'instanceVars' to be set"
def __init__(self, instanceVar, instanceElement, domain, conditions=tuple()): ''' Create an expression representing the set of all instanceElement for instanceVars such that the conditions are satisfied: {instanceElement | conditions}_{instanceVar \in S} ''' OperationOverInstances.__init__(self, SetOfAll._operator_, instanceVar, instanceElement, domain=domain, conditions=conditions) self.instanceElement = self.instanceExpr
def __init__(self, indexOrIndices, summand, domain=None, domains=None, conditions=tuple(), _lambda_map=None): r''' Sum summand over indices over domains. Arguments serve analogous roles to Forall arguments (found in basiclogic/booleans): indices: instance vars summand: instanceExpressions domains: conditions (except no longer optional) ''' # nestMultiIvars=True will cause it to treat multiple instance variables as nested Sum operations internally # and only join them together as a style consequence. OperationOverInstances.__init__(self, Sum._operator_, indexOrIndices, summand, domain=domain, domains=domains, conditions=conditions, nestMultiIvars=True, _lambda_map=_lambda_map) if hasattr(self, 'instanceVar'): self.index = self.instanceVar if hasattr(self, 'instanceVars'): self.indices = self.instanceVars self.summand = self.instanceExpr """
def __init__(self, instance_param_or_params, instance_element, domain=None, *, domains=None, condition=None, conditions=None, _lambda_map=None): ''' Create an expression representing the set of all instance_element for instance parameter(s) such that the conditions are satisfied: {instance_element | conditions}_{instance_param_or_params \in S} ''' OperationOverInstances.__init__(self, SetOfAll._operator_, instance_param_or_params, instance_element, domain=domain, domains=domains, condition=condition, conditions=conditions, _lambda_map=_lambda_map) self.instance_element = self.instance_expr if hasattr(self, 'instance_param'): if not hasattr(self, 'domain'): raise ValueError("SetOfAll requires a domain") elif hasattr(self, 'instance_params'): if not hasattr(self, 'domains') or None in self.domains: raise ValueError("SetOfAll requires domains") else: assert False, ( "Expecting either 'instance_param' or 'instance_params' " "to be set")
def __init__(self, instanceParamOrParams, instanceExpr, *, domain=None, domains=None, condition=None, conditions=None, _lambda_map=None): ''' Create a exists (there exists) expression: exists_{instanceParamOrParams | conditions} instanceExpr This expresses that there exists a value of the instance parameters(s) for which the optional condition(s) is/are satisfied and the instanceExpr is true. The instance parameters(s) and condition(s) may be singular or plural (iterable). ''' OperationOverInstances.__init__(self, NotExists._operator_, instanceParamOrParams, instanceExpr, domain=domain, domains=domains, condition=condition, conditions=conditions, _lambda_map=_lambda_map)
def __init__(self, instanceParamOrParams, instanceExpr, *, domain=None, domains=None, condition=None, conditions=None, _lambda_map=None): ''' Create a Forall expression: forall_{instanceParamOrParams | conditions} instanceExpr. This expresses that the instanceExpr is true for all values of the instance parameter(s) given that the optional condition(s) is/are satisfied. The instance parameter(s) and condition(s) may be singular or plural (iterable). ''' OperationOverInstances.__init__(self, Forall._operator_, instanceParamOrParams, instanceExpr, domain=domain, domains=domains, condition=condition, conditions=conditions, _lambda_map=_lambda_map)
def __init__(self, indexOrIndices, summand, *, domain=None, domains=None, condition=None, conditions=None, _lambda_map=None): r''' Sum summand over indices over domains. Arguments serve analogous roles to Forall arguments (found in basiclogic/booleans): indices: instance vars summand: instanceExpressions domains: conditions (except no longer optional) ''' OperationOverInstances.__init__(self, Sum._operator_, indexOrIndices, summand, domain=domain, domains=domains, condition=condition, conditions=conditions, _lambda_map=_lambda_map) if hasattr(self, 'instanceVar'): self.index = self.instanceVar if hasattr(self, 'instanceVars'): self.indices = self.instanceVars self.summand = self.instanceExpr """
def __init__(self, instanceVarOrVars, instanceExpr, domain=None, domains=None, conditions=tuple(), _lambda_map=None): ''' Create a exists (there exists) expression: exists_{instanceVars | conditions} instanceExpr This expresses that there exists a value of the instanceVar(s) for which the optional condition(s) is/are satisfied and the instanceExpr is true. The instanceVar(s) and condition(s) may be singular or plural (iterable). ''' # nestMultiIvars=True will cause it to treat multiple instance # variables as nested NotExists operations internally # and only join them together as a style consequence. OperationOverInstances.__init__(self, NotExists._operator_, instanceVarOrVars, instanceExpr, domain, domains, conditions, nestMultiIvars=True, _lambda_map=_lambda_map)
def __init__(self, index, integrand, domain, *, condition=None, conditions=None): r''' Integrates integrand over indices over domain. Arguments serve analogous roles to Forall arguments (found in basiclogic/booleans): index: single instance var integrand: instanceExpressions domains: conditions (except no longer optional) ''' OperationOverInstances.__init__(self, Integrate._operator_, index, integrand, domain=domain, condition=condition, conditions=conditions) if len(self.instanceVars) != 1: raise ValueError('Only one index allowed per integral!') elif isinstance(self.domain, Interval): raise ValueError('Can\'t integrate over DiscreteContiguousSet!') elif self.domain == Reals: self.domain = IntervalCC(Neg(infinity), infinity) self.index = self.instanceVars[0] self.integrand = self.instanceExpr
def __init__(self, instanceVarOrVars, instanceExpr, domain=None, domains=None, conditions = tuple()): ''' Create a Forall expression: forall_{instanceVars | conditions} instanceExpr. This expresses that the instanceExpr is true for all values of the instanceVar(s) given that the optional condition(s) is/are satisfied. The instanceVar(s) and condition(s) may be singular or plural (iterable). ''' OperationOverInstances.__init__(self, Forall._operator_, instanceVarOrVars, instanceExpr, domain, domains, conditions)
def __init__(self, indices, summand, domain, conditions=tuple()): r''' Sum summand over indices over domains. Arguments serve analogous roles to Forall arguments (found in basiclogic/booleans): indices: instance vars summand: instanceExpressions domains: conditions (except no longer optional) ''' OperationOverInstances.__init__(self, Prod._operator_, indices, summand, domain=domain, conditions=conditions)
def __init__(self, instanceVarOrVars, instanceExpr, domain=None, domains=None, conditions = tuple(), _lambda_map=None): ''' Create a Forall expression: forall_{instanceVars | conditions} instanceExpr. This expresses that the instanceExpr is true for all values of the instanceVar(s) given that the optional condition(s) is/are satisfied. The instanceVar(s) and condition(s) may be singular or plural (iterable). ''' # nestMultiIvars=True will cause it to treat multiple instance # variables as nested Forall operations internally # and only join them together as a style consequence. OperationOverInstances.__init__(self, Forall._operator_, instanceVarOrVars, instanceExpr, domain, domains, conditions, nestMultiIvars=True, _lambda_map=_lambda_map)
def _formatted(self, format_type, **kwargs): from proveit.numbers import Interval # MUST BE UPDATED TO DEAL WITH 'joining' NESTED LEVELS fence = kwargs['fence'] if 'fence' in kwargs else False if isinstance(self.domain, Interval): lower = self.domain.lower_bound.formatted(format_type) upper = self.domain.upper_bound.formatted(format_type) formatted_inner = self.operator.formatted( format_type) + r'_{' + self.index.formatted( format_type) + '=' + lower + r'}' + r'^{' + upper + r'} ' explicit_ivars = list(self.explicit_instance_vars()) has_explicit_ivars = (len(explicit_ivars) > 0) explicit_conds = list(self.explicit_conditions()) has_explicit_conds = (len(explicit_conds) > 0) if has_explicit_conds: if has_explicit_ivars: formatted_inner += " | " formatted_inner += ', '.join( condition.formatted(format_type) for condition in explicit_conds) formatted_inner += self.summand.formatted(format_type, fence=True) return maybe_fenced(format_type, formatted_inner, fence=fence) else: return OperationOverInstances._formatted(self, format_type, fence=fence)
def __init__(self, instanceVarOrVars, instanceExpr, domain=None, domains=None, conditions=tuple()): ''' Create a exists (there exists) expression: exists_{instanceVars | condition} instanceExpr This expresses that there exists a value of the instanceVar(s) for which the optional condition(s) is/are satisfied and the instanceExpr is true. The instanceVar(s) and condition(s) may be singular or plural (iterable). ''' OperationOverInstances.__init__(self, Exists._operator_, instanceVarOrVars, instanceExpr, domain, domains, conditions)
def shallow_simplification(self, *, must_evaluate=False, **defaults_config): ''' From this forall statement, evaluate it to TRUE or FALSE if possible by calling the domain's forall_evaluation method ''' from proveit.logic import EvaluationError if must_evaluate and not self.has_domain(): # Cannot automatically evaluate a forall statement with # no domain. raise EvaluationError(self) if hasattr(self, 'instance_param'): if hasattr(self.domain, 'forall_evaluation'): # Use the domain's forall_evaluation method return self.domain.forall_evaluation(self) ''' # Let's not do this fancy stuff just yet. # Evaluate an unbundled version unbundle_eq = self.unbundle_equality() return unbundle_eq.rhs.evaluation() ''' if must_evaluate: raise EvaluationError(self) return OperationOverInstances.shallow_simplification(self)
def __init__(self, indices, summand, domain, conditions=tuple()): r''' Sum summand over indices over domains. Arguments serve analogous roles to Forall arguments (found in basiclogic/booleans): indices: instance vars summand: instanceExpressions domains: conditions (except no longer optional) ''' # nestMultiIvars=True will cause it to treat multiple instance variables as nested Prod operations internally # and only join them together as a style consequence. OperationOverInstances.__init__(self, Prod._operator_, indices, summand, domain=domain, conditions=conditions, nestMultiIvars=True)
def extractInitArgValue(argName, operators, operands): ''' Given a name of one of the arguments of the __init__ method, return the corresponding value contained in the 'operands' composite expression (i.e., the operands of a constructed operation). ''' if argName == 'indexOrIndices': argName = 'instanceVarOrVars' elif argName == 'summand': argName = 'instanceExpr' return OperationOverInstances.extractInitArgValue( argName, operators, operands)
def __init__(self, index_or_indices, summand, *, domain=None, domains=None, condition=None, conditions=None, styles=None, _lambda_map=None): r''' Sum summand over indices over domains. Arguments serve analogous roles to Forall arguments (found in basiclogic.booleanss): indices: instance vars summand: instance_expressions domains: conditions (except no longer optional) ''' if (domains is not None): raise NotImplementedError("Sum class not yet implemented for " "multiple domains nor for multiple " "indices.") OperationOverInstances.__init__(self, Sum._operator_, index_or_indices, summand, domain=domain, domains=domains, condition=condition, conditions=conditions, styles=styles, _lambda_map=_lambda_map) if hasattr(self, 'instance_param'): self.index = self.instance_param if hasattr(self, 'instance_params'): self.indices = self.instance_params self.summand = self.instance_expr """
def _formatted(self, format_type, **kwargs): # MUST BE UPDATED TO DEAL WITH 'joining' NESTED LEVELS fence = kwargs['fence'] if 'fence' in kwargs else False explicit_conds = self.explicit_conditions() if isinstance(self.domain, Interval) and len(explicit_conds) == 0: formatted_operator = self.operator.formatted(format_type) formatted_index = self.index.formatted(format_type) formatted_lower = self.domain.lower_bound.formatted(format_type) formatted_upper = self.domain.upper_bound.formatted(format_type) formatted_summand = self.summand.formatted(format_type, fence=True) formatted_inner = "%s_{%s = %s}^{%s} %s" % ( formatted_operator, formatted_index, formatted_lower, formatted_upper, formatted_summand) return maybe_fenced(format_type, formatted_inner, fence=fence) else: return OperationOverInstances._formatted(self, format_type, fence=fence)
def _formatted(self, formatType, **kwargs): # MUST BE UPDATED TO DEAL WITH 'joining' NESTED LEVELS fence = kwargs['fence'] if 'fence' in kwargs else False if isinstance(self.domain,Interval): lower = self.domain.lowerBound.formatted(formatType) upper = self.domain.upperBound.formatted(formatType) formattedInner = self.operator.formatted(formatType)+r'_{'+self.index.formatted(formatType)+'='+lower+r'}'+r'^{'+upper+r'} ' explicitIvars = list(self.explicitInstanceVars()) hasExplicitIvars = (len(explicitIvars) > 0) explicitConds = list(self.explicitConditions()) hasExplicitConds = (len(explicitConds) > 0) if hasExplicitConds: if hasExplicitIvars: formattedInner += " | " formattedInner += ', '.join(condition.formatted(formatType) for condition in explicitConds) formattedInner += self.summand.formatted(formatType, fence=fence) return maybeFenced(formatType, formattedInner, fence=fence) else: return OperationOverInstances._formatted(self, formatType, fence)
def _formatted(self, format_type, **kwargs): # ACTUALLY, notice that the open-interval versions # probably lead to incorrect upper/lower bounds on the # formatted integrals. For example, an integral over the # open half-open interval (0, 1] would need to be formatted # or expressed as a limit as 'a' approaches 0 from the right # of the integral from 0 to 1. fence = kwargs['fence'] if 'fence' in kwargs else False if (isinstance(self.domain,IntervalCC) or isinstance(self.domain,IntervalCO) or isinstance(self.domain,IntervalOC) or isinstance(self.domain,IntervalOO)): lower = self.domain.lower_bound.formatted(format_type) upper = self.domain.upper_bound.formatted(format_type) formatted_inner = ( self.operator.formatted(format_type) + r'_{' + lower + r'}' + r'^{' + upper + r'} ') explicit_ivars = list(self.explicit_instance_vars()) has_explicit_ivars = (len(explicit_ivars) > 0) explicit_conds = list(self.explicit_conditions()) has_explicit_conds = (len(explicit_conds) > 0) if has_explicit_conds: if has_explicit_ivars: formatted_inner += " | " formatted_inner += ', '.join(condition.formatted(format_type) for condition in explicit_conds) formatted_inner += ( self.integrand.formatted(format_type, fence=fence) + r'\,d' + self.index.formatted(format_type)) # old/previous # return (self.operator.formatted(format_type) + # r'_{' + lower + r'}' + r'^{' + upper + r'}' + # self.integrand.formatted(format_type, fence=fence) + # 'd' + self.index.formatted(format_type)) return maybe_fenced(format_type, formatted_inner, fence=fence) else: return OperationOverInstances._formatted(self, format_type, fence=fence)
def extractInitArgValue(argName, operators, operands): ''' Given a name of one of the arguments of the __init__ method, return the corresponding value contained in the 'operands' composite expression (i.e., the operands of a constructed operation). ''' from proveit.logic import InSet if argName=='instanceElement': return operands.body # instance mapping elif argName=='instanceVar': return operands.parameter elif argName=='domain': # the first condition must be the domain condition domainCondition = operands.conditions[0] assert isinstance(domainCondition, InSet), "Expecting the first condition of a SetOfAll object to be the domain condition of type InSet" assert domainCondition.element==operands.parameter, "Expecting the first condition of a SetOfAll object to be the domain condition with the proper element" return domainCondition.domain elif argName=='conditions': return ExprList(*operands.conditions[1:]) # all except the domain condition else: return OperationOverInstances.extractInitArgValue(argName, operators, operands)
def _formatted(self, formatType, **kwargs): fence = kwargs['fence'] if 'fence' in kwargs else False if isinstance(self.domain, Interval): lower = self.domain.lowerBound.formatted(formatType) upper = self.domain.upperBound.formatted(formatType) formattedInner = self.operator.formatted( formatType) + r'_{' + self.index.formatted( formatType) + '=' + lower + r'}' + r'^{' + upper + r'} ' implicitIvars = self.implicitInstanceVars(formatType) hasExplicitIvars = (len(implicitIvars) < len(self.instanceVars)) implicitConditions = self.implicitConditions(formatType) hasExplicitConditions = self.hasCondition() and ( len(implicitConditions) < len(self.conditions)) if hasExplicitConditions: if hasExplicitIvars: formattedInner += " | " formattedInner += ', '.join( condition.formatted(formatType) for condition in self.conditions if condition not in implicitConditions) formattedInner += self.summand.formatted(formatType, fence=fence) return maybeFenced(formatType, formattedInner, fence=fence) else: return OperationOverInstances._formatted(self, formatType, fence)
def substitute_instances(self, universality, **defaults_config): ''' Derive from this Exists operation, Exists_{..x.. in S | ..Q(..x..)..} P(..x..), one that substitutes instance expressions given some universality = forall_{..x.. in S | P(..x..), ..Q(..x..)..} R(..x..). or forall_{..x.. in S | ..Q(..x..)..} P(..x..) = R(..x..). Either is allowed in the theory of the existential quantifier. Derive and return the following type of existential operation assuming universality: Exists_{..x.. in S | ..Q(..x..)..} R(..x..) Works also when there is no domain S and/or no conditions ..Q... ''' raise NotImplementedError("Need to test/update") from . import existential_implication, no_domain_existential_implication from proveit import Etcetera from proveit.logic import Forall from proveit._generic_ import InstanceSubstitutionException if isinstance(universality, Judgment): universality = universality.expr if not isinstance(universality, Forall): raise InstanceSubstitutionException( "'universality' must be a forall expression", self, universality) if self.instance_expr in universality.conditions: # map from the forall instance variables to self's instance # variables i_var_substitutions = { forall_ivar: self_ivar for forall_ivar, self_ivar in zip(universality.instance_vars, self.instance_vars) } first_condition = universality.conditions[0].substituted( i_var_substitutions) if first_condition != self.instance_expr: raise InstanceSubstitutionException( "The first condition of the 'universality' must match the instance expression of the Exists operation having instances substituted", self, universality) if (universality.instance_vars.num_entries() != self.instance_vars.num_entries()): raise InstanceSubstitutionException( "'universality' must have the same number of variables as the Exists operation having instances substituted", self, universality) if universality.domain != self.domain: raise InstanceSubstitutionException( "'universality' must have the same domain as the Exists having instances substituted", self, universality) if ExpressionList(universality.conditions[1:]).substituted( i_var_substitutions) != self.conditions: raise InstanceSubstitutionException( "'universality' must have the same conditions as the Exists operation having instances substituted, in addition to the Exists instance expression", self, universality) _x = universality.instance_vars, _y = self.instance_params _P = Lambda(_y, self.instance_expr) _Q = Lambda(_y, self.condition) _R = Lambda( _y, universality.instance_expr.substituted(i_var_substitutions)) _impl = existential_implication.instantiate({ P: _P, Q: _Q, R: _R, S: self.domain, x: _x, y: _y, z: _y }) return _impl.derive_consequent().derive_consequent() # Default to the OperationOverInstances version which works with # universally quantified equalities. return OperationOverInstances.substitute(self, universality)
def __init__(self, index_or_indices, integrand, *, domain=None, domains=None, condition=None, conditions=None, styles=None, _lambda_map=None): r''' Represents a definite integral of the integrand over the index_or_indices over the domain, with arguments serving roles analogous to those for Forall arguments (found in logic.booleans.quantification.universality) and Sum arguments (found in numbers.summation): index_or_indices: a single instance var such as x integrand : instance expression domain : conditions Despite the generality preserved for the arguments, such Integrate objects are currently defined only for a single index and over a single continuous interval domain defined by a numerical set (such as Real, RealPos, etc.) or real interval (using IntervalCC, IntervalCO, IntervalOC, or IntervalOO objects). ''' # original # OperationOverInstances.__init__( # self, Integrate._operator_, index, integrand, # domain=domain, condition=condition, conditions=conditions, # styles=styles) # new 20220111 # If domain expressed as a special number set, # re-express domain as a continuous interval before # feeding as argument to OperationOverInstances.__init__(). # if domain == Real: # domain = IntervalOO(Neg(infinity), infinity) # elif domain == RealPos: # domain = IntervalOO(zero, infinity) # elif domain == RealNonNeg: # domain = IntervalCO(zero, infinity) # elif domain == RealNeg: # domain = IntervalOO(Neg(infinity), zero) # elif domain == RealNonPos: # domain = IntervalOC(Neg(infinity), zero) OperationOverInstances.__init__( self, Integrate._operator_, index_or_indices, integrand, domain=domain, domains=domains, condition=condition, conditions=conditions, styles=styles, _lambda_map=_lambda_map) # original # if len(self.instance_vars) != 1: # raise ValueError('Only one index allowed per integral!') # elif isinstance(self.domain, Interval): # raise ValueError('Can\'t integrate over DiscreteContiguousSet!') # elif self.domain == Real: # self.domain = IntervalCC(Neg(infinity), infinity) # self.index = self.instance_vars[0] # self.integrand = self.instance_expr # new if hasattr(self, 'instance_param'): self.index = self.instance_param if hasattr(self, 'instance_params'): self.indices = self.instance_params if len(self.instance_params.entries) != 1: raise ValueError('Only one index allowed per integral!') if isinstance(self.domain, Interval): # elaborate this error message later raise ValueError('Can\'t integrate over DiscreteContiguousSet!') self.integrand = self.instance_expr
def substituteInstances(self, universality, assumptions=USE_DEFAULTS): ''' Derive from this Exists operation, Exists_{..x.. in S | ..Q(..x..)..} P(..x..), one that substitutes instance expressions given some universality = forall_{..x.. in S | P(..x..), ..Q(..x..)..} R(..x..). or forall_{..x.. in S | ..Q(..x..)..} P(..x..) = R(..x..). Either is allowed in the context of the existential quantifier. Derive and return the following type of existential operation assuming universality: Exists_{..x.. in S | ..Q(..x..)..} R(..x..) Works also when there is no domain S and/or no conditions ..Q... ''' raise NotImplementedError("Need to update") from ._theorems_ import existentialImplication, noDomainExistentialImplication from proveit import Etcetera from proveit.logic import Forall from proveit._generic_ import InstanceSubstitutionException from proveit._common_ import n, Qmulti, xMulti, yMulti, zMulti, S if isinstance(universality, KnownTruth): universality = universality.expr if not isinstance(universality, Forall): raise InstanceSubstitutionException( "'universality' must be a forall expression", self, universality) if self.instanceExpr in universality.conditions: # map from the forall instance variables to self's instance variables iVarSubstitutions = { forallIvar: selfIvar for forallIvar, selfIvar in zip(universality.instanceVars, self.instanceVars) } firstCondition = universality.conditions[0].substituted( iVarSubstitutions) if firstCondition != self.instanceExpr: raise InstanceSubstitutionException( "The first condition of the 'universality' must match the instance expression of the Exists operation having instances substituted", self, universality) if len(universality.instanceVars) != len(self.instanceVars): raise InstanceSubstitutionException( "'universality' must have the same number of variables as the Exists operation having instances substituted", self, universality) if universality.domain != self.domain: raise InstanceSubstitutionException( "'universality' must have the same domain as the Exists having instances substituted", self, universality) if ExpressionList(universality.conditions[1:]).substituted( iVarSubstitutions) != self.conditions: raise InstanceSubstitutionException( "'universality' must have the same conditions as the Exists operation having instances substituted, in addition to the Exists instance expression", self, universality) P_op, P_op_sub = Operation(P, self.instanceVars), self.instanceExpr Q_op, Q_op_sub = Operation(Qmulti, self.instanceVars), self.conditions R_op, R_op_sub = Operation( R, self.instanceVars), universality.instanceExpr.substituted( iVarSubstitutions) if self.hasDomain(): return existentialImplication.specialize({S:self.domain, P_op:P_op_sub, Q_op:Q_op_sub, R_op:R_op_sub}, \ relabelMap={xMulti:universality.instanceVars, yMulti:self.instanceVars, zMulti:self.instanceVars}, assumptions=assumptions).deriveConsequent(assumptions).deriveConsequent(assumptions) else: return noDomainExistentialImplication.specialize( { P_op: P_op_sub, Q_op: Q_op_sub, R_op: R_op_sub }, relabelMap={ xMulti: universality.instanceVars, yMulti: self.instanceVars, zMulti: self.instanceVars }, assumptions=assumptions).deriveConsequent( assumptions).deriveConsequent(assumptions) # Default to the OperationOverInstances version which works with universally quantified equivalences. return OperationOverInstances.substitute(self, universality, assumptions=assumptions)