def deduceInNumberSet(self, numberSet, assumptions=USE_DEFAULTS): from ._theorems_ import summationNatsClosure, summationIntsClosure, summationRealsClosure, summationComplexesClosure P_op, P_op_sub = Operation(P, self.instanceVars), self.instanceExpr Q_op, Q_op_sub = Operation(Qmulti, self.instanceVars), self.conditions Operation(P, self.instanceVars) self.summand if numberSet == Naturals: thm = summationNatsClosure elif numberSet == Integers: thm = summationIntsClosure elif numberSet == Reals: thm = summationRealsClosure elif numberSet == Complexes: thm = summationComplexesClosure else: raise ProofFailure( InSet(self, numberSet), assumptions, "'deduceInNumberSet' not implemented for the %s set" % str(numberSet)) return thm.specialize( { P_op: P_op_sub, S: self.domain, Q_op: Q_op_sub }, relabelMap={ xMulti: self.instanceVars }, assumptions=assumptions).deriveConsequent(assumptions=assumptions)
def deriveBundled(self, assumptions=USE_DEFAULTS): ''' From a nested forall statement, derive the bundled forall statement. For example, forall_{x | Q(x)} forall_{y | R(y)} P(x, y) becomes forall_{x, y | Q(x), R(y)} P(x, y). ''' raise NotImplementedError("Need to update") from ._theorems_ import bundling assert isinstance(self.instanceExpr, Forall), "Can only bundle nested forall statements" innerForall = self.instanceExpr composedInstanceVars = ExprList( [self.instanceVars, innerForall.instanceVars]) P_op, P_op_sub = Operation( P, composedInstanceVars), innerForall.instanceExpr Q_op, Q_op_sub = Operation(Qmulti, self.instanceVars), self.conditions R_op, R_op_sub = Operation( Rmulti, innerForall.instanceVars), innerForall.conditions return bundling.specialize({ xMulti: self.instanceVars, yMulti: innerForall.instanceVars, P_op: P_op_sub, Q_op: Q_op_sub, R_op: R_op_sub, S: self.domain }).deriveConclusion(assumptions)
def deriveNegatedForall(self, assumptions=USE_DEFAULTS): ''' From [exists_{x | Q(x)} Not(P(x))], derive and return Not(forall_{x | Q(x)} P(x)). From [exists_{x | Q(x)} P(x)], derive and return Not(forall_{x | Q(x)} (P(x) != TRUE)). ''' raise NotImplementedError("Need to update") from ._axioms_ import existsDef from ._theorems_ import existsNotImpliesNotForall from proveit.logic import Not Q_op, Q_op_sub = Operation(Qmulti, self.instanceVars), self.conditions if isinstance(self.instanceExpr, Not): P_op, P_op_sub = Operation( P, self.instanceVars), self.instanceExpr.operand return existsNotImpliesNotForall.specialize( { P_op: P_op_sub, Q_op: Q_op_sub, S: self.domain }, assumptions=assumptions, relabelMap={ xMulti: self.instanceVars }).deriveConsequent(assumptions) else: P_op, P_op_sub = Operation(P, self.instanceVars), self.instanceExpr return existsDef.specialize( { P_op: P_op_sub, Q_op: Q_op_sub, S: self.domain }, assumptions=assumptions, relabelMap={ xMulti: self.instanceVars }).deriveRightViaEquivalence(assumptions)
def foldAsForall(self, forallStmt, assumptions=USE_DEFAULTS): ''' Given forall_{A in Booleans} P(A), conclude and return it from [P(TRUE) and P(FALSE)]. ''' from proveit.logic import Forall from ._theorems_ import foldForallOverBool, foldConditionedForallOverBool from ._common_ import Booleans assert ( isinstance(forallStmt, Forall) ), "May only apply foldAsForall method of Booleans to a forall statement" assert ( forallStmt.domain == Booleans ), "May only apply foldAsForall method of Booleans to a forall statement with the Booleans domain" assert ( len(forallStmt.conditions) <= 2 ), "May only apply foldAsForall method of Booleans to a forall statement with the Booleans domain but no other conditions" assert ( not hasattr(forallStmt, 'instanceVars') ), "May only apply foldAsForall method of Booleans to a forall statement with 1 instance variable" if (len(forallStmt.conditions) == 2): Q_op, Q_op_sub = Operation( Q, forallStmt.instanceVar), forallStmt.conditions[1] P_op, P_op_sub = Operation( P, forallStmt.instanceVar), forallStmt.instanceExpr return foldConditionedForallOverBool.specialize( { Q_op: Q_op_sub, P_op: P_op_sub }, {A: forallStmt.instanceVar}) else: # forall_{A in Booleans} P(A), assuming P(TRUE) and P(FALSE) P_op, P_op_sub = Operation( P, forallStmt.instanceVar), forallStmt.instanceExpr return foldForallOverBool.specialize({P_op: P_op_sub}, {A: forallStmt.instanceVar})
def deduceInBool(self, assumptions=USE_DEFAULTS): ''' Attempt to deduce, then return, that this forall expression is in the set of BOOLEANS, as all forall expressions are (they are taken to be false when not true). ''' raise NotImplementedError("Need to update") from _axioms_ import forallInBool P_op, P_op_sub = Operation(P, self.instanceVars), self.instanceExpr Q_op, Q_op_sub = Operation(Qmulti, self.instanceVars), self.conditions return forallInBool.specialize({P_op:P_op_sub, Q_op:Q_op_sub, xMulti:self.instanceVars, S:self.domain})
def conclude_as_folded(self, assumptions=USE_DEFAULTS): ''' Prove and return some NotExists_{x | Q(x)} P(x) assuming Not(Exists_{x | Q(x)} P(x)). This is automatically derived; see Not.derive_side_effects(..) and Not.derive_not_exists(..). ''' from . import not_exists_folding P_op, P_op_sub = Operation(P, self.instance_vars), self.instance_expr Q_op, Q_op_sub = Etcetera( Operation(Q, self.instance_vars)), self.conditions folding = not_exists_folding.instantiate( {P_op: P_op_sub, Q_op: Q_op_sub, x_multi: self.instance_vars, S: self.domain}) return folding.derive_conclusion(assumptions)
def deduceMembership(self, element, assumptions=USE_DEFAULTS): ''' From P(x), derive and return (x in {y | P(y)}), where x is meant as the given element. ''' from _theorems_ import foldComprehension, foldBasicComprehension Q_op, Q_op_sub = Operation(Qmulti, self.instanceVars), self.conditions if len(self.instanceVars) == 1 and self.instanceElement == self.instanceVars[0] and len(self.conditions) == 1: Pop, Psub = Operation(P, self.instanceVars), self.conditions[0] return foldBasicComprehension.specialize({S:self.domain, Q_op:Q_op_sub, x:element}, {y:self.instanceVars[0]}, assumptions=assumptions) else: f_op, f_sub = Operation(f, self.instanceVars), self.instanceElement return foldComprehension.specialize({S:self.domain, Q_op:Q_op_sub, f_op:f_sub, x:element}, {yMulti:self.instanceVars}).deriveConclusion(assumptions)
def unfold(self, assumptions=USE_DEFAULTS): ''' Derive and return Not(Exists_{x | Q(x)} P(x)) from NotExists_{x | Q(x)} P(x) ''' from ._theorems_ import notExistsUnfolding P_op, P_op_sub = Operation(P, self.instanceVars), self.instanceExpr Q_op, Q_op_sub = Etcetera(Operation( Q, self.instanceVars)), self.conditions return notExistsUnfolding.specialize({ P_op: P_op_sub, Q_op: Q_op_sub, xMulti: self.instanceVars, S: self.domain }).deriveConclusion(assumptions)
def unfold(self, assumptions=USE_DEFAULTS): ''' Derive and return Not(Exists_{x | Q(x)} P(x)) from NotExists_{x | Q(x)} P(x) ''' from . import not_exists_unfolding P_op, P_op_sub = Operation(P, self.instance_vars), self.instance_expr Q_op, Q_op_sub = Etcetera( Operation(Q, self.instance_vars)), self.conditions return not_exists_unfolding.instantiate( { P_op: P_op_sub, Q_op: Q_op_sub, x_multi: self.instance_vars, S: self.domain}).derive_conclusion(assumptions)
def join(self, secondSummation, assumptions=frozenset()): ''' Join the "second summation" with "this" summation, deducing and returning the equivalence of these summations added with the joined summation. Both summation must be over Intervals. The relation between the first summation upper bound, UB1, and the second summation lower bound, LB2 must be explicitly either UB1 = LB2-1 or LB2=UB1+1. ''' from theorems import sumSplitAfter, sumSplitBefore from proveit.number.common import one from proveit.number import Sub, Add if not isinstance(self.domain, Interval) or not isinstance(secondSummation.domain, Interval): raise Exception('Sum joining only implemented for Interval domains') if self.summand != secondSummation.summand: raise Exception('Sum joining only allowed when the summands are the same') if self.domain.upperBound == Sub(secondSummation.domain.lowerBound, one): sumSplit = sumSplitBefore splitIndex = secondSummation.domain.lowerBound elif secondSummation.domain.lowerBound == Add(self.domain.upperBound, one): sumSplit = sumSplitAfter splitIndex = self.domain.upperBound else: raise Exception('Sum joining only implemented when there is an explicit increment of one from the upper bound and the second summations lower bound') lowerBound, upperBound = self.domain.lowerBound, secondSummation.domain.upperBound deduceInIntegers(lowerBound, assumptions) deduceInIntegers(upperBound, assumptions) deduceInIntegers(splitIndex, assumptions) return sumSplit.specialize({Operation(f, self.instanceVars):self.summand}).specialize({a:lowerBound, b:splitIndex, c:upperBound, x:self.indices[0]}).deriveReversed()
def conclude(self, assumptions): from ._theorems_ import subsetEqViaEquality from proveit import ProofFailure from proveit.logic import SetOfAll try: # first attempt a transitivity search return ContainmentRelation.conclude(self, assumptions) except ProofFailure: pass # transitivity search failed # Any set contains itself if self.operands[0] == self.operands[1]: return subsetEqViaEquality.specialize({A:self.operands[0], B:self.operands[1]}) # Check for special case of [{x | Q*(x)}_{x \in S}] \subseteq S if isinstance(self.subset, SetOfAll): from proveit.logic.set_theory.comprehension._theorems_ import comprehensionIsSubset setOfAll = self.subset if len(setOfAll.instanceVars)==1 and setOfAll.instanceElement == setOfAll.instanceVars[0] and setOfAll.domain==self.superset: Q_op, Q_op_sub = Operation(Qmulti, setOfAll.instanceVars), setOfAll.conditions return comprehensionIsSubset.specialize({S:setOfAll.domain, Q_op:Q_op_sub}, relabelMap={x:setOfAll.instanceVars[0]}, assumptions=assumptions) # Finally, attempt to conclude A subseteq B via forall_{x in A} x in B. return self.concludeAsFolded(elemInstanceVar=safeDummyVar(self), assumptions=assumptions)
def split(self, splitIndex, side='after', assumptions=frozenset()): r''' Splits summation over one Interval {a ... c} into two summations. If side == 'after', it splits into a summation over {a ... splitIndex} plus a summation over {splitIndex+1 ... c}. If side == 'before', it splits into a summation over {a ... splitIndex-1} plus a summation over {splitIndex ... c}. As special cases, splitIndex==a with side == 'after' splits off the first single term. Also, splitIndex==c with side == 'before' splits off the last single term. r''' if not isinstance(self.domain, Interval): raise Exception( 'Sum splitting only implemented for Interval domains') if side == 'before' and self.domain.upperBound == splitIndex: return self.splitOffLast() if side == 'after' and self.domain.lowerBound == splitIndex: return self.splitOffFirst() if isinstance(self.domain, Interval) and len(self.instanceVars) == 1: from theorems import sumSplitAfter, sumSplitBefore sumSplit = sumSplitAfter if side == 'after' else sumSplitBefore deduceInIntegers(self.domain.lowerBound, assumptions) deduceInIntegers(self.domain.upperBound, assumptions) deduceInIntegers(splitIndex, assumptions) # Also needs lowerBound <= splitIndex and splitIndex < upperBound return sumSplit.specialize({ Operation(f, self.instanceVars): self.summand }).specialize({ a: self.domain.lowerBound, b: splitIndex, c: self.domain.upperBound, x: self.indices[0] }) raise Exception( "splitOffLast only implemented for a summation over a Interval of one instance variable" )
def concludeAsFolded(self, assumptions=USE_DEFAULTS): ''' Prove and return some NotExists_{x | Q(x)} P(x) assuming Not(Exists_{x | Q(x)} P(x)). This is automatically derived; see Not.deriveSideEffects(..) and Not.deriveNotExists(..). ''' from ._theorems_ import notExistsFolding P_op, P_op_sub = Operation(P, self.instanceVars), self.instanceExpr Q_op, Q_op_sub = Etcetera(Operation( Q, self.instanceVars)), self.conditions folding = notExistsFolding.specialize({ P_op: P_op_sub, Q_op: Q_op_sub, xMulti: self.instanceVars, S: self.domain }) return folding.deriveConclusion(assumptions)
def distribute(self, assumptions=frozenset()): r''' Distribute the denominator through the numerate. Returns the equality that equates self to this new version. Examples: :math:`(a + b + c) / d = a / d + b / d + c / d` :math:`(a - b) / d = a / d - b / d` :math:`\left(\sum_x f(x)\right / y = \sum_x [f(x) / y]` Give any assumptions necessary to prove that the operands are in Complexes so that the associative and commutation theorems are applicable. ''' from proveit.number import Add, Subtract, Sum from ._theorems_ import distributeFractionThroughSum, distributeFractionThroughSubtract, distributeFractionThroughSummation if isinstance(self.numerator, Add): return distributeFractionThroughSum.specialize({xEtc:self.numerator.operands, y:self.denominator}) elif isinstance(self.numerator, Subtract): return distributeFractionThroughSubtract.specialize({x:self.numerator.operands[0], y:self.numerator.operands[1], z:self.denominator}) elif isinstance(self.numerator, Sum): # Should deduce in Complexes, but distributeThroughSummation doesn't have a domain restriction right now # because this is a little tricky. To do. #deduceInComplexes(self.operands, assumptions) yEtcSub = self.numerator.indices Pop, Pop_sub = Operation(P, self.numerator.indices), self.numerator.summand S_sub = self.numerator.domain dummyVar = safeDummyVar(self) spec1 = distributeFractionThroughSummation.specialize({Pop:Pop_sub, S:S_sub, yEtc:yEtcSub, z:dummyVar}) return spec1.deriveConclusion().specialize({dummyVar:self.denominator}) else: raise Exception("Unsupported operand type to distribute over: " + self.numerator.__class__)
def conclude(self, assumptions): from ._theorems_ import subsetEqViaEquality from proveit import ProofFailure from proveit.logic import SetOfAll, Equals try: # first attempt a transitivity search return ContainmentRelation.conclude(self, assumptions) except ProofFailure: pass # transitivity search failed # Any set contains itself try: Equals(self.operands[0], self.operands[1]).prove(assumptions, automation=False) return subsetEqViaEquality.specialize({A: self.operands[0], B: self.operands[1]}) except ProofFailure: pass # Check for special case of [{x | Q*(x)}_{x \in S}] \subseteq S if isinstance(self.subset, SetOfAll): from proveit.logic.set_theory.comprehension._theorems_ import comprehensionIsSubset setOfAll = self.subset if len(setOfAll.instanceVars)==1 and setOfAll.instanceElement == setOfAll.instanceVars[0] and setOfAll.domain==self.superset: Q_op, Q_op_sub = Operation(Qmulti, setOfAll.instanceVars), setOfAll.conditions return comprehensionIsSubset.specialize({S:setOfAll.domain, Q_op:Q_op_sub}, relabelMap={x:setOfAll.instanceVars[0]}, assumptions=assumptions) # Finally, attempt to conclude A subseteq B via forall_{x in A} x in B. # Issue: Variables do not match when using safeDummyVar: _x_ to x. # We need to automate this better, right now it is only practical to do concludeAsFolded manually. return self.concludeAsFolded(elemInstanceVar=safeDummyVar(self), assumptions=assumptions)
def splitOffFirst(self, assumptions=frozenset()): from theorems import sumSplitFirst # only for associative summation if isinstance(self.domain, Interval) and len(self.instanceVars) == 1: deduceInIntegers(self.domain.lowerBound, assumptions) deduceInIntegers(self.domain.upperBound, assumptions) # Also needs lowerBound < upperBound return sumSplitFirst.specialize({Operation(f, self.instanceVars):self.summand}).specialize({a:self.domain.lowerBound, b:self.domain.upperBound, x:self.indices[0]}) raise Exception("splitOffLast only implemented for a summation over a Interval of one instance variable")
def deduceInBool(self, assumptions=USE_DEFAULTS): ''' Deduce, then return, that this exists expression is in the set of BOOLEANS as all exists expressions are (they are taken to be false when not true). ''' raise NotImplementedError("Need to update") from ._theorems_ import existsInBool P_op, P_op_sub = Operation(P, self.instanceVars), self.instanceExpr Q_op, Q_op_sub = Operation(Qmulti, self.instanceVars), self.conditions return existsInBool.specialize( { P_op: P_op_sub, Q_op: Q_op_sub, S: self.domain }, relabelMap={xMulti: self.instanceVars}, assumptions=assumptions)
def foldAsForall(self, forallStmt, assumptions=USE_DEFAULTS): ''' Given forall_{A in Booleans} P(A), conclude and return it from [P(TRUE) and P(FALSE)]. ''' from proveit.logic import Forall, And from ._theorems_ import foldForallOverBool, foldConditionedForallOverBool from ._common_ import Booleans assert ( isinstance(forallStmt, Forall) ), "May only apply foldAsForall method of Booleans to a forall statement" assert ( forallStmt.domain == Booleans ), "May only apply foldAsForall method of Booleans to a forall statement with the Booleans domain" if (len(forallStmt.conditions) > 1): if len(forallStmt.conditions) == 2: condition = forallStmt.conditions[1] else: condition = And(*forallStmt.conditions[1:]) Qx = Operation(Q, forallStmt.instanceVar) _Qx = condition Px = Operation(P, forallStmt.instanceVar) _Px = forallStmt.instanceExpr _A = forallStmt.instanceVar return foldConditionedForallOverBool.instantiate( { Qx: _Qx, Px: _Px, A: _A }, num_forall_eliminations=1, assumptions=assumptions) else: # forall_{A in Booleans} P(A), assuming P(TRUE) and P(FALSE) Px = Operation(P, forallStmt.instanceVar) _Px = forallStmt.instanceExpr _A = forallStmt.instanceVar return foldForallOverBool.instantiate({ Px: _Px, A: _A }, num_forall_eliminations=1, assumptions=assumptions)
def fold_as_forall(self, forall_stmt, assumptions=USE_DEFAULTS): ''' Given forall_{A in Boolean} P(A), conclude and return it from [P(TRUE) and P(FALSE)]. ''' from proveit.logic import Forall, And from . import fold_forall_over_bool, fold_conditioned_forall_over_bool from . import Boolean assert ( isinstance(forall_stmt, Forall) ), "May only apply fold_as_forall method of Boolean to a forall statement" assert ( forall_stmt.domain == Boolean ), "May only apply fold_as_forall method of Boolean to a forall statement with the Boolean domain" if forall_stmt.conditions.num_entries() > 1: if forall_stmt.conditions.is_double(): condition = forall_stmt.conditions[1] else: condition = And(*forall_stmt.conditions[1:].entries) Qx = Operation(Q, forall_stmt.instance_var) _Qx = condition Px = Operation(P, forall_stmt.instance_var) _Px = forall_stmt.instance_expr _A = forall_stmt.instance_var return fold_conditioned_forall_over_bool.instantiate( { Qx: _Qx, Px: _Px, A: _A }, num_forall_eliminations=1, assumptions=assumptions) else: # forall_{A in Boolean} P(A), assuming P(TRUE) and P(FALSE) Px = Operation(P, forall_stmt.instance_param) _Px = forall_stmt.instance_expr _A = forall_stmt.instance_param return fold_forall_over_bool.instantiate({ Px: _Px, A: _A }, num_forall_eliminations=1, assumptions=assumptions)
def unfoldMembership(self, element, assumptions=USE_DEFAULTS): ''' From (x in {y | Q(y)})_{y in S}, derive and return [(x in S) and Q(x)], where x is meant as the given element. From (x in {y | ..Q(y)..})_{y in S}, derive and return [(x in S) and ..Q(x)..], where x is meant as the given element. From (x in {f(y) | ..Q(y)..})_{y in S}, derive and return exists_{y in S | ..Q(y)..} x = f(y). Also derive x in S, but this is not returned. ''' from _theorems_ import unfoldComprehension, unfoldBasicComprehension, unfoldBasic1CondComprehension, inSupersetIfInComprehension Q_op, Q_op_sub = Operation(Qmulti, self.instanceVars), self.conditions if len(self.instanceVars) == 1 and self.instanceElement == self.instanceVars[0]: inSupersetIfInComprehension.specialize({S:self.domain, Q_op:Q_op_sub, x:element}, {y:self.instanceVars[0]}, assumptions=assumptions) # x in S side-effect if len(self.conditions) == 1: Q_op, Q_op_sub = Operation(Q, self.instanceVars), self.conditions[0] return unfoldBasic1CondComprehension.specialize({S:self.domain, Q_op:Q_op_sub, x:element}, {y:self.instanceVars[0]}, assumptions=assumptions) else: return unfoldBasicComprehension.specialize({S:self.domain, Q_op:Q_op_sub, x:element}, {y:self.instanceVars[0]}, assumptions=assumptions) else: f_op, f_sub = Operation(f, self.instanceVars), self.instanceElement return unfoldComprehension.specialize({S:self.domain, Q_op:Q_op_sub, f_op:f_sub, x:element}, {yMulti:self.instanceVars}).deriveConclusion(assumptions)
def elimDomain(self, assumptions=USE_DEFAULTS): ''' From [exists_{x in S | Q(x)} P(x)], derive and return [exists_{x | Q(x)} P(x)], eliminating the domain which is a weaker form. ''' raise NotImplementedError("Need to update") from ._theorems_ import existsInGeneral P_op, P_op_sub = Operation(P, self.instanceVars), self.instanceExpr Q_op, Q_op_sub = Operation(Qmulti, self.instanceVars), self.conditions return existsInGeneral.specialize( { P_op: P_op_sub, Q_op: Q_op_sub, S: self.domain }, assumptions=assumptions, relabelMap={ xMulti: self.instanceVars, yMulti: self.instanceVars }).deriveConsequent(assumptions)
def unfoldForall(self, forallStmt, assumptions=USE_DEFAULTS): ''' Given forall_{A in Booleans} P(A), derive and return [P(TRUE) and P(FALSE)]. ''' from proveit.logic import Forall from ._theorems_ import unfoldForallOverBool from ._common_ import Booleans assert(isinstance(forallStmt, Forall)), "May only apply unfoldForall method of Booleans to a forall statement" assert(forallStmt.domain == Booleans), "May only apply unfoldForall method of Booleans to a forall statement with the Booleans domain" assert(len(forallStmt.instanceVars) == 1), "May only apply unfoldForall method of Booleans to a forall statement with 1 instance variable" return unfoldForallOverBool.specialize({Operation(P, forallStmt.instanceVars[0]): forallStmt.instanceExpr, A:forallStmt.instanceVars[0]}).deriveConsequent(assumptions)
def _specializeUnravelingTheorem(self, theorem, *instanceVarLists): raise NotImplementedError("Need to update") assert len(self.instanceVars) > 1, "Can only unravel a forall statement with multiple instance variables" if len(instanceVarLists) == 1: raise ValueError("instanceVarLists should be a list of 2 or more Variable lists") if len(instanceVarLists) > 2: return self.deriveUnraveled(ExprList(instanceVarLists[:-1]), instanceVarLists[-1]).deriveUnraveled(*instanceVarLists[:-1]).checked({self}) outerVars, innerVars = instanceVarLists outerVarSet, innerVarSet = set(outerVars), set(innerVars) assert innerVarSet | outerVarSet == set(self.instanceVars), "outerVars and innterVars must combine to the full set of instance variables" assert innerVarSet.isdisjoint(outerVarSet), "outerVars and innterVars must be disjoint sets" innerConditions = [] outerConditions = [] for condition in self.conditions: if condition.freeVars().isdisjoint(innerVars): outerConditions.append(condition) else: innerConditions.append(condition) P_op, P_op_sub = Operation(P, self.instanceVars), self.instanceExpr Q_op, Q_op_sub = Operation(Qmulti, outerVars), outerConditions R_op, R_op_sub = Operation(Rmulti, innerVars), innerConditions return theorem.specialize({xMulti:outerVars, yMulti:innerVars, P_op:P_op_sub, Q_op:Q_op_sub, R_op:R_op_sub, S:self.domain})
def concludeViaExample(self, exampleInstance, assumptions=USE_DEFAULTS): ''' Conclude and return this [exists_{..y.. in S | ..Q(..x..)..} P(..y..)] from P(..x..) and Q(..x..) and ..x.. in S, where ..x.. is the given exampleInstance. ''' raise NotImplementedError("Need to update") from ._theorems_ import existenceByExample from proveit.logic import InSet if len(self.instanceVars) > 1 and ( not isinstance(exampleInstance, ExprList) or (len(exampleInstance) != len(self.instanceVars))): raise Exception( 'Number in exampleInstance list must match number of instance variables in the Exists expression' ) P_op, P_op_sub = Operation(P, self.instanceVars), self.instanceExpr Q_op, Q_op_sub = Operation(Qmulti, self.instanceVars), self.conditions # P(..x..) where ..x.. is the given exampleInstance exampleMapping = { instanceVar: exampleInstanceElem for instanceVar, exampleInstanceElem in zip( self.instanceVars, exampleInstance if isinstance( exampleInstance, ExpressionList) else [exampleInstance]) } exampleExpr = self.instanceExpr.substituted(exampleMapping) # ..Q(..x..).. where ..x.. is the given exampleInstance exampleConditions = self.conditions.substituted(exampleMapping) if self.hasDomain(): for iVar in self.instanceVars: exampleConditions.append(InSet(iVar, self.domain)) # exists_{..y.. | ..Q(..x..)..} P(..y..)] return existenceByExample.specialize( { P_op: P_op_sub, Q_op: Q_op_sub, S: self.domain }, assumptions=assumptions, relabelMap={ xMulti: exampleInstance, yMulti: self.instanceVars }).deriveConsequent(assumptions=assumptions)
def foldAsForall(self, forallStmt, assumptions=USE_DEFAULTS): ''' Given forall_{A in Booleans} P(A), conclude and return it from [P(TRUE) and P(FALSE)]. ''' from proveit.logic import Forall from ._theorems_ import foldForallOverBool from ._common_ import Booleans assert(isinstance(forallStmt, Forall)), "May only apply foldAsForall method of Booleans to a forall statement" assert(forallStmt.domain == Booleans), "May only apply foldAsForall method of Booleans to a forall statement with the Booleans domain" assert(len(forallStmt.explicitConditions())==0), "May only apply foldAsForall method of Booleans to a forall statement with the Booleans domain but no other conditions" assert(len(forallStmt.instanceVars) == 1), "May only apply foldAsForall method of Booleans to a forall statement with 1 instance variable" # forall_{A in Booleans} P(A), assuming P(TRUE) and P(FALSE) return foldForallOverBool.specialize({Operation(P, forallStmt.instanceVars[0]):forallStmt.instanceExpr}, {A:forallStmt.instanceVars[0]})
def substituteDomain(self, superset, assumptions=USE_DEFAULTS): ''' Substitute the domain with a superset. From [exists_{x in A| Q(x)} P(x)], derive and return [exists_{x in B| Q(x)} P(x)] given A subseteq B. ''' raise NotImplementedError("Need to update") from ._theorems_ import existsInSuperset P_op, P_op_sub = Operation(P, self.instanceVars), self.instanceExpr Q_op, Q_op_sub = Operation(Qmulti, self.instanceVars), self.conditions return existsInSuperset.specialize( { P_op: P_op_sub, Q_op: Q_op_sub, A: self.domain, B: superset }, assumptions=assumptions, relabelMap={ xMulti: self.instanceVars, yMulti: self.instanceVars }).deriveConsequent(assumptions)
def shift(self, shiftAmount, assumptions=frozenset()): ''' Shift the summation indices by the shift amount, deducing and returning the equivalence of this summation with a index-shifted version. ''' from theorems import indexShift if len(self.indices) != 1 or not isinstance(self.domain, Interval): raise Exception('Sum shift only implemented for summations with one index over a Interval') fOp, fOpSub = Operation(f, self.index), self.summand deduceInIntegers(self.domain.lowerBound, assumptions) deduceInIntegers(self.domain.upperBound, assumptions) deduceInIntegers(shiftAmount, assumptions) return indexShift.specialize({fOp:fOpSub, x:self.index}).specialize({a:self.domain.lowerBound, b:self.domain.upperBound, c:shiftAmount})
def factor(self, theFactor, pull="left", groupFactor=False, groupRemainder=None, assumptions=frozenset()): ''' Pull out a common factor from a summation, pulling it either to the "left" or "right". If groupFactor is True and theFactor is a product, it will be grouped together as a sub-product. groupRemainder is not relevant kept for compatibility with other factor methods. Returns the equality that equates self to this new version. Give any assumptions necessary to prove that the operands are in Complexes so that the associative and commutation theorems are applicable. ''' from proveit.number.multiplication.theorems import distributeThroughSummationRev from proveit.number import Mult if not theFactor.freeVars().isdisjoint(self.indices): raise Exception('Cannot factor anything involving summation indices out of a summation') # We may need to factor the summand within the summation summand_assumptions = assumptions | {InSet(index, self.domain) for index in self.indices} summandFactorEq = self.summand.factor(theFactor, pull, groupFactor=False, groupRemainder=True, assumptions=summand_assumptions) summandInstanceEquivalence = summandFactorEq.generalize(self.indices, domain=self.domain).checked(assumptions) eq = Equation(self.instanceSubstitution(summandInstanceEquivalence).checked(assumptions)) factorOperands = theFactor.operands if isinstance(theFactor, Mult) else theFactor xDummy, zDummy = self.safeDummyVars(2) # Now do the actual factoring by reversing distribution if pull == 'left': Pop, Pop_sub = Operation(P, self.indices), summandFactorEq.rhs.operands[-1] xSub = factorOperands zSub = [] elif pull == 'right': Pop, Pop_sub = Operation(P, self.indices), summandFactorEq.rhs.operands[0] xSub = [] zSub = factorOperands # We need to deduce that theFactor is in Complexes and that all instances of Pop_sup are in Complexes. deduceInComplexes(factorOperands, assumptions=assumptions) deduceInComplexes(Pop_sub, assumptions=assumptions | {InSet(idx, self.domain) for idx in self.indices}).generalize(self.indices, domain=self.domain).checked(assumptions) # Now we specialize distributThroughSummationRev spec1 = distributeThroughSummationRev.specialize({Pop:Pop_sub, S:self.domain, yEtc:self.indices, xEtc:Etcetera(MultiVariable(xDummy)), zEtc:Etcetera(MultiVariable(zDummy))}).checked() eq.update(spec1.deriveConclusion().specialize({Etcetera(MultiVariable(xDummy)):xSub, Etcetera(MultiVariable(zDummy)):zSub})) if groupFactor and len(factorOperands) > 1: eq.update(eq.eqExpr.rhs.group(endIdx=len(factorOperands), assumptions=assumptions)) return eq.eqExpr #.checked(assumptions)
def simplification(self, assumptions=frozenset()): ''' For the trivial case of summing over only one item (currently implemented just for a Interval where the endpoints are equal), derive and return this summation expression equated the simplified form of the single term. Assumptions may be necessary to deduce necessary conditions for the simplification. ''' from axioms import sumSingle if isinstance(self.domain, Interval) and self.domain.lowerBound == self.domain.upperBound: if len(self.instanceVars) == 1: deduceInIntegers(self.domain.lowerBound, assumptions) return sumSingle.specialize({Operation(f, self.instanceVars):self.summand}).specialize({a:self.domain.lowerBound}) raise Exception("Sum simplification only implemented for a summation over a Interval of one instance variable where the upper and lower bound is the same")
def forallEvaluation(self, forallStmt, assumptions): ''' Given a forall statement over the BOOLEANS domain, evaluate to TRUE or FALSE if possible. ''' from proveit.logic import Forall, Equals, EvaluationError from ._theorems_ import falseEqFalse, trueEqTrue from ._theorems_ import forallBoolEvalTrue, forallBoolEvalFalseViaTF, forallBoolEvalFalseViaFF, forallBoolEvalFalseViaFT from ._common_ import TRUE, FALSE, Booleans from conjunction import compose assert(isinstance(forallStmt, Forall)), "May only apply forallEvaluation method of BOOLEANS to a forall statement" assert(forallStmt.domain == Booleans), "May only apply forallEvaluation method of BOOLEANS to a forall statement with the BOOLEANS domain" assert(len(forallStmt.instanceVars) == 1), "May only apply forallEvaluation method of BOOLEANS to a forall statement with 1 instance variable" instanceVar = forallStmt.instanceVars[0] instanceExpr = forallStmt.instanceExpr P_op = Operation(P, instanceVar) trueInstance = instanceExpr.substituted({instanceVar:TRUE}) falseInstance = instanceExpr.substituted({instanceVar:FALSE}) if trueInstance == TRUE and falseInstance == FALSE: # special case of Forall_{A in BOOLEANS} A falseEqFalse # FALSE = FALSE trueEqTrue # TRUE = TRUE return forallBoolEvalFalseViaTF.specialize({P_op:instanceExpr}).deriveConclusion() else: # must evaluate for the TRUE and FALSE case separately evalTrueInstance = trueInstance.evaluation(assumptions) evalFalseInstance = falseInstance.evaluation(assumptions) if not isinstance(evalTrueInstance.expr, Equals) or not isinstance(evalFalseInstance.expr, Equals): raise EvaluationError('Quantified instances must produce equalities as evaluations') # proper evaluations for both cases (TRUE and FALSE) trueCaseVal = evalTrueInstance.rhs falseCaseVal = evalFalseInstance.rhs if trueCaseVal == TRUE and falseCaseVal == TRUE: # both cases are TRUE, so the forall over booleans is TRUE compose([evalTrueInstance.deriveViaBooleanEquality(), evalFalseInstance.deriveViaBooleanEquality()], assumptions) forallBoolEvalTrue.specialize({P_op:instanceExpr, A:instanceVar}) return forallBoolEvalTrue.specialize({P_op:instanceExpr, A:instanceVar}, assumptions=assumptions).deriveConclusion(assumptions) else: # one case is FALSE, so the forall over booleans is FALSE compose([evalTrueInstance, evalFalseInstance], assumptions) if trueCaseVal == FALSE and falseCaseVal == FALSE: return forallBoolEvalFalseViaFF.specialize({P_op:instanceExpr, A:instanceVar}, assumptions=assumptions).deriveConclusion(assumptions) elif trueCaseVal == FALSE and falseCaseVal == TRUE: return forallBoolEvalFalseViaFT.specialize({P_op:instanceExpr, A:instanceVar}, assumptions=assumptions).deriveConclusion(assumptions) elif trueCaseVal == TRUE and falseCaseVal == FALSE: return forallBoolEvalFalseViaTF.specialize({P_op:instanceExpr, A:instanceVar}, assumptions=assumptions).deriveConclusion(assumptions) else: raise EvaluationError('Quantified instance evaluations must be TRUE or FALSE')