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 conclude(self, assumptions): from ._theorems_ import supersetEqViaEquality from proveit import ProofFailure from proveit.logic import 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 supersetEqViaEquality.specialize({ A: self.operands[0], B: self.operands[1] }) except ProofFailure: pass # Finally, attempt to conclude A supseteq B via forall_{x in B} x in A. return self.concludeAsFolded(elemInstanceVar=safeDummyVar(self), assumptions=assumptions)
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 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, Sub, 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, Sub): 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 proveit import ProofFailure from proveit.logic import Equals # Any set contains itself try: Equals(*self.operands).prove(assumptions, automation=False) return self.concludeViaEquality(assumptions) except ProofFailure: pass try: # Attempt a transitivity search return ContainmentRelation.conclude(self, assumptions) except ProofFailure: pass # transitivity search failed # Finally, attempt to conclude A supseteq B via forall_{x in B} x in A. return self.concludeAsFolded(elemInstanceVar=safeDummyVar(self), assumptions=assumptions)
def conclude(self, assumptions): from _theorems_ import supersetEqViaEquality from proveit import ProofFailure try: # first attempt a transitivity search return ContainmentRelation.conclude(self, assumptions) except ProofFailure: pass # transitivity search failed # any set is a superset of itself if self.operands[0] == self.operands[1]: return supersetEqViaEquality.specialize({ A: self.operands[0], B: self.operands[1] }) # Finally, attempt to conclude A supseteq B via forall_{x in B} x in A. return self.concludeAsFolded(elemInstanceVar=safeDummyVar(self), assumptions=assumptions)
def conclude(self, assumptions=USE_DEFAULTS): from proveit import ProofFailure from proveit.logic import SetOfAll, Equals # Any set contains itself try: Equals(*self.operands).prove(assumptions, automation=False) return self.concludeViaEquality(assumptions) except ProofFailure: pass try: # Attempt a transitivity search return ContainmentRelation.conclude(self, assumptions) except ProofFailure: pass # transitivity search failed # 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.allInstanceVars())==1 and setOfAll.instanceElement == setOfAll.allInstanceVars()[0] and setOfAll.domain==self.superset): Q_op, Q_op_sub = ( Operation(QQ, setOfAll.allInstanceVars()), setOfAll.conditions) return comprehensionIsSubset.specialize( {S:setOfAll.domain, Q_op:Q_op_sub}, relabelMap={x:setOfAll.allInstanceVars()[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 varIter(var, start, end): from proveit import safeDummyVar from .indexed import Indexed param = safeDummyVar(var) return Iter(Lambda(param, Indexed(var, param)), start, end)
def cancel(self, operand, pull="left", assumptions=frozenset()): from proveit.number import Mult if self.numerator == self.denominator == operand: # x/x = 1 from ._theorems_ import fracCancelComplete return fracCancelComplete.specialize({ x: operand }).checked(assumptions) if not isinstance(self.numerator, Mult): from ._theorems_ import fracCancelNumerLeft newEq0 = self.denominator.factor( operand, pull=pull, groupFactor=True, groupRemainder=True, assumptions=assumptions).substitution( Fraction(self.numerator, safeDummyVar(self)), safeDummyVar(self)).checked(assumptions) newEq1 = fracCancelNumerLeft.specialize({ x: operand, y: newEq0.rhs.denominator.operands[1] }) return newEq0.applyTransitivity(newEq1) assert isinstance(self.numerator, Mult) if isinstance(self.denominator, Mult): from ._theorems_ import fracCancelLeft newEq0 = self.numerator.factor( operand, pull=pull, groupFactor=True, groupRemainder=True, assumptions=assumptions).substitution( Fraction(safeDummyVar(self), self.denominator), safeDummyVar(self)).checked(assumptions) newEq1 = self.denominator.factor( operand, pull=pull, groupFactor=True, groupRemainder=True, assumptions=assumptions).substitution( Fraction(newEq0.rhs.numerator, safeDummyVar(self)), safeDummyVar(self)).checked(assumptions) newEq2 = fracCancelLeft.specialize({ x: operand, y: newEq1.rhs.numerator.operands[1], z: newEq1.rhs.denominator.operands[1] }) return newEq0.applyTransitivity(newEq1).applyTransitivity(newEq2) # newFracIntermediate = self.numerator.factor(operand).proven().subRightSideInto(self) # newFrac = self.denominator.factor(operand).proven().subRightSideInto(newFracIntermediate) # numRemainingOps = newFrac.numerator.operands[1:] # denomRemainingOps = newFrac.denominator.operands[1:] # return fracCancel1.specialize({x:operand,Etcetera(y):numRemainingOps,Etcetera(z):denomRemainingOps}) else: from ._theorems_ import fracCancelDenomLeft newEq0 = self.numerator.factor( operand, pull=pull, groupFactor=True, groupRemainder=True, assumptions=assumptions).substitution( Fraction(safeDummyVar(self), self.denominator), safeDummyVar(self)).checked(assumptions) newEq1 = fracCancelDenomLeft.specialize({ x: operand, y: newEq0.rhs.numerator.operands[1] }) return newEq0.applyTransitivity(newEq1)
def factor(self, theFactor, pull="left", groupFactor=False, groupRemainder=None, assumptions=frozenset()): ''' Pull out a factor from a fraction, pulling it either to the "left" or "right". The factor may be a product or fraction itself. 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 ._theorems_ import fracInProdRev, prodOfFracsRev, prodOfFracsLeftNumerOneRev, prodOfFracsRightNumerOneRev from proveit.number import Mult, num dummyVar = safeDummyVar(self) eqns = [] if isinstance(theFactor, Frac): # factor the operand denominator out of self's denominator denomFactorEqn = self.denominator.factor(theFactor.denominator, pull, groupFactor=True, groupRemainder=True, assumptions=assumptions) factoredDenom = denomFactorEqn.rhs eqns.append( denomFactorEqn.substitution(Frac(self.numerator, dummyVar), dummyVar)) if theFactor.numerator != num(1) and self.numerator != num(1): # factor the operand numerator out of self's numerator numerFactorEqn = self.numerator.factor(theFactor.numerator, pull, groupFactor=True, groupRemainder=True, assumptions=assumptions) factoredNumer = numerFactorEqn.rhs eqns.append( numerFactorEqn.substitution(Frac(dummyVar, factoredDenom), dummyVar)) # factor the two fractions eqns.append( prodOfFracsRev.specialize({ x: factoredNumer.operands[0], y: factoredNumer.operands[1], z: factoredDenom.operands[0], w: factoredDenom.operands[1] })) else: # special case: one of the numerators is equal to one, no numerator factoring to be done if (pull == 'left') == (theFactor.numerator == num(1)): thm = prodOfFracsLeftNumerOneRev else: thm = prodOfFracsRightNumerOneRev # factor the two fractions eqns.append( thm.specialize({ x: self.numerator, y: factoredDenom.operands[0], z: factoredDenom.operands[1] })) else: numerFactorEqn = self.numerator.factor(theFactor, pull, groupFactor=False, groupRemainder=True, assumptions=assumptions) factoredNumer = numerFactorEqn.rhs eqns.append( numerFactorEqn.substitution(Frac(dummyVar, self.denominator), dummyVar)) # factor the numerator factor from the fraction if pull == 'left': wEtcSub = factoredNumer.operands[:-1] xSub = factoredNumer.operands[-1] zEtcSub = [] elif pull == 'right': wEtcSub = [] xSub = factoredNumer.operands[0] zEtcSub = factoredNumer.operands[1:] eqns.append( fracInProdRev.specialize({ wEtc: wEtcSub, x: xSub, y: self.denominator, zEtc: zEtcSub })) num = len(theFactor.operands) if isinstance(theFactor, Mult) else 1 if groupFactor and num > 1: if pull == 'left': eqns.append(eqns[-1].rhs.group(endIdx=num, assumptions=assumptions)) elif pull == 'right': eqns.append(eqns[-1].rhs.group(startIdx=-num, assumptions=assumptions)) return Equals(eqns[0].lhs, eqns[-1].rhs).prove(assumptions)
def _computation(self, assumptions=USE_DEFAULTS, must_evaluate=False): # Currently not doing anything with must_evaluate # What it should do is make sure it evaluates to a number # and can circumvent any attempt that will not evaluate to # number. from proveit.number import one if not isinstance(self.operand, ExprTuple): # Don't know how to compute the length if the operand is # not a tuple. For example, it could be a variable that # represent a tuple. So just return the self equality. from proveit.logic import Equals return Equals(self, self).prove() entries = self.operand has_range = any(isinstance(entry, ExprRange) for entry in entries) if (len(entries) == 1 and has_range and not isinstance(entries[0].body, ExprRange)): # Compute the length of a single range. Examples: # |(f(1), ..., f(n))| = n # |(f(i), ..., f(j))| = j-i+1 range_entry = entries[0] start_index = range_entry.start_index end_index = range_entry.end_index lambda_map = range_entry.lambda_map if start_index == one: from proveit.core_expr_types.tuples._theorems_ import \ range_from1_len return range_from1_len.instantiate( { f: lambda_map, i: end_index }, assumptions=assumptions) else: from proveit.core_expr_types.tuples._theorems_ import range_len return range_len.instantiate( { f: lambda_map, i: start_index, j: end_index }, assumptions=assumptions) elif not has_range: # Case of all non-range entries. if len(entries) == 0: # zero length. from proveit.core_expr_types.tuples._axioms_ import tuple_len_0 return tuple_len_0 elif len(entries) < 10: # Automatically get the count and equivalence with # the length of the proper iteration starting from # 1. For example, # |(a, b, c)| = 3 # |(a, b, c)| = |(1, .., 3)| import proveit.number.numeral.deci _n = len(entries) len_thm = proveit.number.numeral.deci._theorems_\ .__getattr__('tuple_len_%d'%_n) repl_map = dict() for param, entry in zip(len_thm.explicitInstanceParams(), entries): repl_map[param] = entry return len_thm.specialize(repl_map) else: raise NotImplementedError("Can't handle length computation " ">= 10 for %s" % self) elif (len(entries) == 2 and not isinstance(entries[1], ExprRange) and not isinstance(entries[0].body, ExprRange)): # Case of an extended range: # |(a_1, ..., a_n, b| = n+1 from proveit.core_expr_types.tuples._theorems_ import \ extended_range_len, extended_range_from1_len assert isinstance(entries[0], ExprRange) range_lambda = entries[0].lambda_map range_start = entries[0].start_index range_end = entries[0].end_index if range_start == one: return extended_range_from1_len.instantiate( { f: range_lambda, b: entries[1], i: range_end }, assumptions=assumptions) else: return extended_range_len.instantiate( { f: range_lambda, b: entries[1], i: range_start, j: range_end }, assumptions=assumptions) else: # Handle the general cases via general_len_val, # len_of_ranges_with_repeated_indices, or # len_of_ranges_with_repeated_indices_from_1 from proveit.core_expr_types.tuples._theorems_ import ( general_len, len_of_ranges_with_repeated_indices, len_of_ranges_with_repeated_indices_from_1) _x = safeDummyVar(self) def entry_map(entry): if isinstance(entry, ExprRange): if isinstance(entry.body, ExprRange): # Return an ExprRange of lambda maps. return ExprRange(entry.parameter, entry.body.lambda_map, entry.start_index, entry.end_index) else: # Use the ExprRange entry's map. return entry.lambda_map # For individual elements, just map to the # elemental entry. return Lambda(_x, entry) def entry_start(entry): if isinstance(entry, ExprRange): if isinstance(entry.body, ExprRange): # Return an ExprRange of lambda maps. return ExprRange(entry.parameter, entry.body.start_index, entry.start_index, entry.end_index) else: return entry.start_index return one # for individual elements, use start=end=1 def entry_end(entry): if isinstance(entry, ExprRange): if isinstance(entry.body, ExprRange): # Return an ExprRange of lambda maps. return ExprRange(entry.parameter, entry.body.end_index, entry.start_index, entry.end_index) else: return entry.end_index return one # for individual elements, use start=end=1 _f = [entry_map(entry) for entry in entries] _i = [entry_start(entry) for entry in entries] _j = [entry_end(entry) for entry in entries] _n = Len(_i).computed(assumptions=assumptions, simplify=False) if all(_ == _i[0] for _ in _i) and all(_ == _j[0] for _ in _j): if isinstance(_i[0], ExprRange): if _i[0].is_parameter_independent: # A parameter independent range means they # are all the same. _i = [_i[0].body] if isinstance(_j[0], ExprRange): if _j[0].is_parameter_independent: # A parameter independent range means they # are all the same. _j = [_j[0].body] if (not isinstance(_i[0], ExprRange) and not isinstance(_j[0], ExprRange)): # special cases where the indices are repeated if _i[0] == one: thm = len_of_ranges_with_repeated_indices_from_1 return thm.instantiate({ n: _n, f: _f, i: _j[0] }, assumptions=assumptions) else: thm = len_of_ranges_with_repeated_indices return thm.instantiate( { n: _n, f: _f, i: _i[0], j: _j[0] }, assumptions=assumptions) return general_len.instantiate({ n: _n, f: _f, i: _i, j: _j }, assumptions=assumptions)