def __init__(self, *operands): r''' Add together any number of operands. ''' Operation.__init__(self, Add._operator_, operands) self.terms = self.operands if len(self.terms)==2 and all(term in DIGITS for term in self.terms): if self not in Add.addedNumerals: try: # for single digit addition, import the theorem that provides the evaluation Add.addedNumerals.add(self) proveit.number.numeral.decimal._theorems_.__getattr__('add_%d_%d'%(self.terms[0].asInt(), self.terms[1].asInt())) except: # may fail before the relevent _commons_ and _theorems_ have been generated pass # and that's okay
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 __init__(self, operators, operands, doMinSizeCheck=True): if doMinSizeCheck and len(operators) < 2: raise ValueError("Do not use a TransitiveSequence for fewer than two relationship (it is unnecessary)") if len(operators)+1 != len(operands): raise ValueError("There must be one more operand than operator in a TransitiveSequence") relation_class = self.__class__.RelationClass() assert issubclass(relation_class, TransitiveRelation), "The Relation class of a TransitiveSequence should be a TransitiveRelation" equiv_class = relation_class.EquivalenceClass() relation_operators = (relation_class.WeakRelationClass()._operator_, relation_class.StrongRelationClass()._operator_, equiv_class._operator_) for operator in operators: if operator not in relation_operators: raise TypeError("Operators of TransitiveSequence should be %s, %s, or %s"%tuple(str(relation_operator) for relation_operator in relation_operators)) Operation.__init__(self, operators, operands)
def _formatted(self, format_type, fence=False): formatted_gate_operation = (self.gate_operation.formatted(format_type, fence=False)) if format_type == 'latex': return r'\gate{' + formatted_gate_operation + r'}' else: return Operation.formatted(self, format_type, fence)
def conclude(self, assumptions): ''' Try to automatically conclude this bi-directional implication by reducing its operands to true/false. ''' from . import iff_t_t, iff_t_f, iff_f_t, iff_f_f, true_iff_true, false_iff_false if self in {true_iff_true, false_iff_false}: # should be proven via one of the imported theorems as a simple # special case try: self.evaluation(assumptions) except BaseException: return self.prove() try: # try to prove the bi-directional implication via evaluation reduction. # if that is possible, it is a relatively straightforward thing to # do. return Operation.conclude(assumptions) except BaseException: pass try: # Use a breadth-first search approach to find the shortest # path to get from one end-point to the other. return TransitiveRelation.conclude(self, assumptions) except BaseException: pass # the last attempt is to introduce the Iff via implications each way, an # essentially direct consequence of the definition. return self.conclude_by_definition(assumptions)
def evaluation(self, assumptions=USE_DEFAULTS): ''' Given operands that evaluate to TRUE or FALSE, derive and return the equality of this expression with TRUE or FALSE. ''' from _theorems_ import iffTT, iffTF, iffFT, iffFF # IMPORTANT: load in truth-table evaluations return Operation.evaluation(self, assumptions)
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 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 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 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 shallow_simplification(self, *, must_evaluate=False, **defaults_config): ''' If the left side has a 'not_equal' method, use that for evalaution. Otherwise, if appropriate (must_evaluate is TRUE or the answer is already known) apply the definition to perform the evaluation: A ≠ B via ¬(A = B). ''' from proveit import ProofFailure from proveit.logic import evaluate_truth if self.lhs != self.rhs and hasattr(self.lhs, 'not_equal'): try: # If there is a 'not_equal' method, try to use that. neq = self.lhs.not_equal(self.rhs, automation=must_evaluate) return evaluate_truth(neq) except ProofFailure: pass eq = Equals(self.lhs, self.rhs) if must_evaluate or eq.proven() or eq.disproven(): # Evaluate A ≠ B via evaluating ¬(A = B), definition_equality = self.definition() unfolded_evaluation = definition_equality.rhs.evaluation( automation=must_evaluate) return definition_equality.apply_transitivity(unfolded_evaluation) return Operation.shallow_simplification(self)
def evaluation(self, assumptions=USE_DEFAULTS): ''' Attempt to form evaluation of whether (element in domain) is TRUE or FALSE. If the domain has a 'membershipObject' method, attempt to use the 'equivalence' method from the object it generates. ''' from proveit.logic import Equals, TRUE, NotIn evaluation = None try: # try an 'equivalence' method (via the membership object) equiv = self.membershipObject.equivalence(assumptions) val = equiv.evaluation(assumptions).rhs evaluation = Equals(equiv, val).prove(assumptions=assumptions) except: # try the default evaluation method if necessary evaluation = Operation.evaluation(self, assumptions) # try also to evaluate this by deducing membership or non-membership in case it # generates a shorter proof. try: if evaluation.rhs == TRUE: if hasattr(self, 'membershipObject'): self.membershipObject.conclude(assumptions=assumptions) else: notInDomain = NotIn(self.element, self.domain) if hasattr(notInDomain, 'nonmembershipObject'): notInDomain.nonmembershipObject.conclude( assumptions=assumptions) except: pass return evaluation
def remake_constructor(self): if self.is_reversed(): # Use the 'not_proper_superset' function if it # is reversed. return 'not_proper_superset' # Use the default. return Operation.remake_constructor(self)
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 evaluation(self, assumptions=USE_DEFAULTS, automation=True): ''' Given operands that evaluate to TRUE or FALSE, derive and return the equality of this expression with TRUE or FALSE. ''' from ._theorems_ import impliesTT, impliesFT, impliesFF, impliesTF # load in truth-table evaluations return Operation.evaluation(self, assumptions, automation=automation)
def formatted(self, format_type, fence=False): formatted_gate_operation = self.target_gate.formatted(format_type, fence=False) if format_type == LATEX: return r'\gate{' + formatted_gate_operation + r'}' else: return Operation.formatted(self, format_type, fence)
def conclude(self, assumptions): from proveit.logic import FALSE if is_irreducible_value(self.lhs) and is_irreducible_value(self.rhs): # prove that two irreducible values are not equal return self.lhs.not_equal(self.rhs, assumptions) if self.lhs == FALSE or self.rhs == FALSE: try: # prove something is not false by proving it to be true return self.conclude_via_double_negation(assumptions) except BaseException: pass if hasattr(self.lhs, 'not_equal') and is_irreducible_value(self.rhs): try: return self.lhs.not_equal(self.rhs, assumptions) except BaseException: pass if hasattr(self.lhs, 'deduce_not_equal'): # If there is a 'deduce_not_equal' method, use that. # The responsibility then shifts to that method for # determining what strategies should be attempted # (with the recommendation that it should not attempt # multiple non-trivial automation strategies). eq = self.lhs.deduce_not_equal(self, assumptions) if eq.expr != self: raise ValueError("'deduce_not_equal' not implemented " "correctly; must deduce the 'inequality' " "that it is given if it can: " "'%s' != '%s'" % (eq.expr, self)) return eq try: return self.conclude_as_folded(assumptions) except BaseException: # try the default (reduction) return Operation.conclude(assumptions)
def evaluation(self, assumptions=USE_DEFAULTS, automation=True): ''' Given operands that evaluate to TRUE or FALSE, derive and return the equality of this expression with TRUE or FALSE. ''' from . import iff_t_t, iff_t_f, iff_f_t, iff_f_f # IMPORTANT: load in truth-table evaluations return Operation.evaluation(self, assumptions, automation)
def shallow_simplification(self, *, must_evaluate=False, **defaults_config): if must_evaluate and hasattr(self.operand, 'compute_norm'): return self.evaluation() return Operation.shallow_simplification(self)
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 latex(self, **kwargs): if self.getStyle('division')=='fraction': # only fence if forceFence=True (a fraction within an exponentiation is an example of when fencing should be forced) kwargs['fence'] = kwargs['forceFence'] if 'forceFence' in kwargs else False return maybeFencedLatex(r'\frac{'+self.numerator.latex()+'}{'+self.denominator.latex()+'}', **kwargs) else: return Operation.latex(**kwargs) # normal division
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 __init__(self, operands, *, styles=None): ''' Len can take an explicit ExprTuple as operands, or it may take an expression (such as a varaible) that represents a tuple. Either way, this expression is taken as the 'operands'. ''' if isinstance(operands, ExprRange): # An ExprRange cannot represent an ExprTuple, # so we must want this wrapped in an ExprTuple. operands = ExprTuple(operands) # In order to always recognize that Len only takes a single # operand, we must wrap it as an ExprTuple with one entry. Operation.__init__(self, Len._operator_, operands=operands, styles=styles)
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 formatted(self, formatType, fence=False): print("Entering Target.formatted().") # for testing; delete later formattedGateOperation = self.target_gate.formatted(formatType, fence=False) if formatType == 'latex': return r'\gate{' + formattedGateOperation + r'}' else: return Operation._formatted(self, formatType, fence)
def evaluation(self, assumptions=USE_DEFAULTS, *, automation=True, **kwargs): ''' Given an operand that evaluates to TRUE or FALSE, derive and return the equality of this expression with TRUE or FALSE. ''' from ._theorems_ import notT, notF # load in truth-table evaluations return Operation.evaluation(self, assumptions, automation=automation)
def formatted(self, formatType, fence=False, subFence=True): # Temporary hack to get quantum bra and ket products to display properly. # This should eventually be done differently because we shouldn't need to # know anything about the quantum application here. from proveit.physics.quantum import Bra, Ket, RegisterBra, RegisterKet if len(self.operands) == 2 and (isinstance(self.operands[0], Bra) or isinstance(self.operands[0], RegisterBra)) and (isinstance(self.operands[1], Ket) or isinstance(self.operands[1], RegisterKet)): return self.operands[0].formatted(formatType) + self.operands[1].formatted(formatType, no_lvert=True) # return Operation.formatted(self, formatType, fence, subFence) return Operation._formatted(self, formatType=formatType, fence=fence, subFence=subFence)
def remakeConstructor(self): if self.getStyle('exponent') == 'radical': # Use a different constructor if using the 'radical' style. if self.exponent == frac(one, two): return 'sqrt' else: raise ValueError("Unkown radical type, exponentiating to the power " "of %s"%str(self.exponent)) return Operation.remakeConstructor(self)
def __init__(self, *operands): r''' Multiply together any number of operands from first operand. ''' Operation.__init__(self, Mult._operator_, operands) self.factors = operands if len(self.factors) == 2 and all(factor in DIGITS for factor in self.factors): if self not in Mult.multipliedNumerals: try: # for single digit addition, import the theorem that provides the evaluation Mult.multipliedNumerals.add(self) proveit.number.numeral.deci._theorems_.__getattr__( 'mult_%d_%d' % (self.factors[0].asInt(), self.factors[1].asInt())) except: # may fail before the relevent _commons_ and _theorems_ have been generated pass # and that's okay
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)