예제 #1
0
 def conclude(self, assumptions):
     '''
     If the domain has a 'foldForall' method, attempt to conclude this Forall statement
     via 'concludeAsFolded' or by proving the instance expression under proper assumptions
     and then generalizing.
     '''
     # first try to prove via generalization without automation
     extra_assumptions = tuple(self.inclusiveConditions())
     try:
         proven_inst_expr = self.explicitInstanceExpr().prove(
             assumptions=extra_assumptions +
             defaults.checkedAssumptions(assumptions),
             automation=False)
         return proven_inst_expr.generalize(self.explicitInstanceVars(),
                                            conditions=extra_assumptions)
     except:
         pass
     # next try 'foldAsForall' on the domain (if applicable)
     hasFoldAsForall = False
     if self.hasDomain() and hasattr(self.domain, 'foldAsForall'):
         # try foldAsForall first
         hasFoldAsForall = True
         try:
             return self.concludeAsFolded(assumptions)
         except:
             pass
     # lastly, try to prove via generalization with automation
     try:
         proven_inst_expr = self.explicitInstanceExpr().prove(
             assumptions=extra_assumptions +
             defaults.checkedAssumptions(assumptions))
         instanceVarLists = [list(self.explicitInstanceVars())]
         conditions = list(self.inclusiveConditions())
         # see if we can generalize multiple levels simultaneously for a shorter proof
         while isinstance(proven_inst_expr.proof(), Generalization):
             instanceVarLists.append(
                 list(proven_inst_expr.explicitInstanceVars()))
             conditions += proven_inst_expr.conditions
             proven_inst_expr = proven_inst_expr.proof().requiredTruths[0]
         return proven_inst_expr.generalize(forallVarLists=instanceVarLists,
                                            conditions=conditions)
     except ProofFailure:
         if hasFoldAsForall:
             raise ProofFailure(
                 self, assumptions,
                 "Unable to conclude automatically; both the 'foldAsForall' method on the domain and automated generalization failed."
             )
         else:
             raise ProofFailure(
                 self, assumptions,
                 "Unable to conclude automatically; the domain has no 'foldAsForall' method and automated generalization failed."
             )
예제 #2
0
 def call_method_with_known_truth_assumptions(*args, **kwargs):
     if len(args) > assumptionsIndex:
         args = list(args)
         assumptions = args[assumptionsIndex]
         assumptions = defaults.checkedAssumptions(assumptions)                    
         assumptions += self.assumptions
         args[assumptionsIndex] = defaults.checkedAssumptions(assumptions)
     else:
         assumptions = kwargs.get('assumptions', USE_DEFAULTS)
         assumptions = defaults.checkedAssumptions(assumptions)
         assumptions = tuple(assumptions) + self.assumptions
         kwargs['assumptions'] = defaults.checkedAssumptions(assumptions)
     return attr.__call__(*args, **kwargs)
예제 #3
0
def rounding_deduceInNumberSet(expr, number_set, roundingRealClosureThm,
                               roundingRealPosClosureThm,
                               assumptions=USE_DEFAULTS):
        '''
        Given a number set number_set, attempt to prove that the given
        Ceil, Floor, or Round expression is in that number set using
        the appropriate closure theorem.
        '''
        from proveit import ProofFailure
        from proveit._common_ import x
        from proveit.logic import InSet
        from proveit.number import Integers, Naturals

        # among other things, convert any assumptions=None
        # to assumptions=()
        assumptions = defaults.checkedAssumptions(assumptions)

        if number_set == Integers:
            return roundingRealClosureThm.specialize(
                        {x:expr.operand}, assumptions=assumptions)

        if number_set == Naturals:
            return roundingRealPosClosureThm.specialize(
                        {x:expr.operand}, assumptions=assumptions)

        msg = ("The rounding_methods.py function "
               "'rounding_deduceInNumberSet()' is not implemented for the "
               "%s set"%str(number_set))
        raise ProofFailure(InSet(expr, number_set), assumptions, msg)
예제 #4
0
 def applyTransitivities(chain, assumptions=USE_DEFAULTS):
     '''
     Apply transitvity rules on a list of relations in the given chain
     to proof the relation over the chain end points.
     Each element of the chain must be a KnownTruth object that represents
     a proven relation for which transitivity rules may be applied via
     an 'applyTransitivity' method (such as a KnownTruth for a proven
     Equals statement).  The chain must "connect" in the sense that any
     two neighbors in the chain can be joined vie applyTransitivity.
     The transitivity rule will be applied left to right.
     '''
     assumptions = defaults.checkedAssumptions(assumptions)
     if len(chain) == 0:
         raise TransitivityException(None, assumptions,
                                     'Empty transitivity relation train')
     if not all(isinstance(element, KnownTruth) for element in chain):
         raise TypeError(
             'Expecting chain elements to be KnownTruth objects')
     while len(chain) >= 2:
         first = chain.pop(0)
         second = chain.pop(0)
         new_relation = first.applyTransitivity(second,
                                                assumptions=assumptions)
         if not isinstance(new_relation, KnownTruth):
             raise TypeError(
                 "applyTransitivity should return a KnownTruth, not %s" %
                 new_relation)
         chain.insert(0, new_relation)
     return chain[0]  # we are done
예제 #5
0
def apply_roundingElimination(expr, roundingEliminationThm,
                              assumptions=USE_DEFAULTS):
    '''
    Let F(x) represent the relevant Ceil(x), Floor(x), or Round(x)
    fxn calling the apply_roundingElimination() method from the
    respective F(x).roundingElimination() method.
    For the trivial case of F(x) where the operand x is already
    an integer, derive and return this rounding F expression equated
    with the operand itself: |- F(x) = x. For example, |- Ceil(x) = x.
    Assumptions may be necessary to deduce necessary conditions
    (for example, that x actually is an integer) for the simplification.
    This method is utilized by the F(x).apply_reducedSimplification()
    method, indirectly via the F(x).roundingElimination() method, but
    only after the operand x is verified to already be proven (or
    assumed) to be an integer.
    For the case where the operand is of the form x = real + int,
    see the apply_roundingExtraction() function.
    '''
    from proveit._common_ import x

    # among other things, convert any assumptions=None
    # to assumptions=() to avoid later len() errors
    assumptions = defaults.checkedAssumptions(assumptions)

    return roundingEliminationThm.specialize(
                {x:expr.operand}, assumptions=assumptions)
예제 #6
0
 def applyTransitivity(self, other, assumptions=USE_DEFAULTS):
     '''
     Apply transitivity to derive a new relation from 'self' and 'other'.
     For example, from self:a<b, other:b=c, derive a<c.
     This must be implemented for the different types of transitive
     relations.  This default version handles the case where
     'other' is an equality (as in the above example.
     '''
     from proveit.logic import Equals
     #print 'apply transitivity', self, other
     assumptions = defaults.checkedAssumptions(assumptions)
     if isinstance(other, Equals):
         if other.lhs in (self.lhs, self.rhs):
             subrule = other.subRightSideInto
             commonExpr = other.lhs
         elif other.rhs in (self.lhs, self.rhs):
             subrule = other.subLeftSideInto
             commonExpr = other.rhs
         else:
             raise ValueError(
                 "Equality does not involve either side of inequality!")
         if commonExpr == self.lhs:
             # replace the lhs of self with its counterpart from the "other" equality.
             return subrule(self.innerExpr().lhs, assumptions=assumptions)
         elif commonExpr == self.rhs:
             # replace the rhs of self with its counterpart from the "other" equality.
             return subrule(self.innerExpr().rhs, assumptions=assumptions)
     raise NotImplementedError(
         'Must implement applyTransitivity appropriately for each kind of TransitiveRelation'
     )
예제 #7
0
파일: abs.py 프로젝트: shoelbling/Prove-It
def isEqualToOrSubsetEqOf(number_set,
                          equal_sets=None,
                          subset_sets=None,
                          subset_eq_sets=None,
                          assumptions=None):
    '''
    A utility function used in the doReducedSimplification() method
    to test whether the number set specified by number_set:
    • is equal to any of the number sets provided in the list of
      equal_sets
    • OR is already known/proven to be a proper subset of any of the
      number sets provided in the list of subset_sets,
    • OR is already known/proven to be an improper subset of any of the
      number sets provided in the list of subset_eq_sets,
    returning True at the first such equality, subset, or subset_eq
    relation found to be True.
    '''
    # among other things, convert any assumptions=None
    # to assumptions=() (thus averting len(None) errors)
    assumptions = defaults.checkedAssumptions(assumptions)

    if not equal_sets == None:
        for temp_set in equal_sets:
            if number_set == temp_set:
                return True
    if not subset_eq_sets == None:
        for temp_set in subset_eq_sets:
            if SubsetEq(number_set, temp_set).proven(assumptions):
                return True
    if not subset_sets == None:
        for temp_set in subset_sets:
            if Subset(number_set, temp_set).proven(assumptions):
                return True
    return False
예제 #8
0
    def deduceInNumberSet(self, number_set, assumptions=USE_DEFAULTS):
        '''
        Given a number set number_set (such as Integers, Reals, etc),
        attempt to prove that the given ModAbs expression is in that
        number set using the appropriate closure theorem.
        '''
        from proveit._common_ import a, b
        from proveit.logic import InSet
        from proveit.number.modular._theorems_ import (
                modAbsIntClosure, modAbsRealClosure)
        from proveit.number import Integers, Reals

        # among other things, make sure non-existent assumptions
        # manifest as empty tuple () rather than None
        assumptions = defaults.checkedAssumptions(assumptions)

        if number_set == Integers:
            return modAbsIntClosure.specialize(
                    {a:self.value, b:self.divisor}, assumptions=assumptions)

        if number_set == Reals:
            return modAbsRealClosure.specialize(
                    {a:self.value, b:self.divisor}, assumptions=assumptions)

        msg = ("'ModAbs.deduceInNumberSet()' not implemented for "
               "the %s set"%str(number_set))
        raise ProofFailure(InSet(self, number_set), assumptions, msg)
예제 #9
0
 def sorted_items(cls, items, reorder=True, assumptions=USE_DEFAULTS):
     '''
     Return the given items in sorted order with respect to this 
     TransitivityRelation class (cls) under the given assumptions
     using known transitive relations.
     If reorder is False, raise a TransitivityException if the
     items are not in sorted order as provided.
     Weak relations (e.g., <=) are only considered when calling
     this method on a weak relation class (otherwise, only
     equality and strong relations are used in the sorting).
     '''
     from proveit.number import isLiteralInt
     assumptions = defaults.checkedAssumptions(assumptions)
     if all(isLiteralInt(item) for item in items):
         # All the items are integers.  Use efficient n log(n) sorting to
         # get them in the proper order and then use fixedTransitivitySort
         # to efficiently prove this order.
         items = sorted(items, key=lambda item: item.asInt())
         reorder = False
     if reorder:
         sorter = TransitivitySorter(cls, items, assumptions=assumptions)
         return list(sorter)
     else:
         return cls._fixedTransitivitySort(items,
                                           assumptions=assumptions).operands
예제 #10
0
 def sort(RelationClass, items, reorder=True, assumptions=USE_DEFAULTS):
     assumptions = defaults.checkedAssumptions(assumptions)
     if reorder:
         return RelationClass._transitivitySort(items,
                                                assumptions=assumptions)
     else:
         return RelationClass._fixedTransitivitySort(
             items, assumptions=assumptions)
예제 #11
0
def denyViaContradiction(contradictoryExpr, conclusion, assumptions):
    '''
    Deny the conclusion (affirm its negation) via reductio ad absurdum.
    First calls deriveContradiction on the contradictoryExpr to derive FALSE,
    then derive the negated conclusion after proving that the conclusion itself
    implies FALSE.  The conclusion must be a Boolean.
    '''
    from proveit.logic import Not
    assumptions = defaults.checkedAssumptions(assumptions)
    extendedAssumptions = assumptions + (conclusion,)
    return contradictoryExpr.deriveContradiction(extendedAssumptions).asImplication(conclusion).deriveViaContradiction(assumptions)
예제 #12
0
 def concludeViaTransitivity(self, assumptions=USE_DEFAULTS):
     from proveit.logic import Equals
     assumptions = defaults.checkedAssumptions(assumptions)
     if self.__class__ == Equals:
         forceStrong = False  # "strong" vs "weak" doesn't make sense when the relation is simply equality
     else:
         forceStrong = (
             self.__class__ == self.__class__._checkedStrongRelationClass())
     return self.__class__._transitivitySearch(self.lhs,
                                               self.rhs,
                                               forceStrong=forceStrong,
                                               assumptions=assumptions)
예제 #13
0
파일: abs.py 프로젝트: shoelbling/Prove-It
    def deduceInNumberSet(self, number_set, assumptions=USE_DEFAULTS):
        '''
        Given a number set number_set (such as Integers, Reals, etc),
        attempt to prove that the given expression is in that number
        set using the appropriate closure theorem.
        '''
        from proveit.number.absolute_value._theorems_ import (
            absComplexClosure, absNonzeroClosure, absComplexClosureNonNegReals)
        from proveit.number import Complexes, Reals, RealsNonNeg, RealsPos

        # among other things, make sure non-existent assumptions
        # manifest as empty tuple () rather than None
        assumptions = defaults.checkedAssumptions(assumptions)

        if number_set == Reals:
            return absComplexClosure.specialize({a: self.operand},
                                                assumptions=assumptions)

        if number_set == RealsPos:
            return absNonzeroClosure.specialize({a: self.operand},
                                                assumptions=assumptions)

        if number_set == RealsNonNeg:
            return absComplexClosureNonNegReals.specialize(
                {a: self.operand}, assumptions=assumptions)

        # To be thorough and a little more general, we check if the
        # specified number_set is already proven to *contain* one of
        # the number sets we have theorems for -- for example,
        #     Y=Complexes contain X=Reals, and
        #     Y=(-1, inf) contains X=RealsPos,
        # but we don't have specific thms for those supersets Y.
        # If so, use the appropiate thm to determine that self is in X,
        # then prove that self must also be in Y since Y contains X.
        if Subset(Reals, number_set).proven(assumptions=assumptions):
            absComplexClosure.specialize({a: self.operand},
                                         assumptions=assumptions)
            return InSet(self, number_set).prove(assumptions=assumptions)
        if Subset(RealsPos, number_set).proven(assumptions=assumptions):
            absNonzeroClosure.specialize({a: self.operand},
                                         assumptions=assumptions)
            return InSet(self, number_set).prove(assumptions=assumptions)
        if Subset(RealsNonNeg, number_set).proven(assumptions=assumptions):
            absComplexClosureNonNegReals.specialize({a: self.operand},
                                                    assumptions=assumptions)
            return InSet(self, number_set).prove(assumptions=assumptions)

        # otherwise, we just don't have the right thm to make it work
        msg = ("'Abs.deduceInNumberSet()' not implemented for "
               "the %s set" % str(number_set))
        raise ProofFailure(InSet(self, number_set), assumptions, msg)
예제 #14
0
 def sort(cls, items, reorder=True, assumptions=USE_DEFAULTS):
     '''
     Return the proven Sequence, a known truth for an expression 
     of type cls.SequenceClass(), representing the sorted sequence
     according to the cls.sorted_items method.
     Weak relations (e.g., <=) are only considered when calling
     this method on a weak relation class (otherwise, only
     equality and strong relations are used in the sorting).
     '''
     automation = True
     assumptions = defaults.checkedAssumptions(assumptions)
     if reorder:
         items = cls.sorted_items(items, reorder, assumptions)
         #print("sorted", items)
         automation = False # Direct relations already proven.
     return cls._fixedTransitivitySort(items, assumptions=assumptions, 
                                       automation=automation)
예제 #15
0
 def concludeViaTransitivity(self, assumptions=USE_DEFAULTS):
     from proveit.logic import Equals
     assumptions = defaults.checkedAssumptions(assumptions)
     proven_relation = self.__class__._transitivitySearch(self.lhs, self.rhs, assumptions=assumptions)
     relation = proven_relation.expr
     if relation.__class__ != self.__class__:
         if self.__class__ == self.__class__._checkedWeakRelationClass():
             if relation.__class__ == self.__class__._checkedStrongRelationClass():
                 return relation.deriveRelaxed()
             elif relation.__class__ == Equals:
                 # Derive a weaker relation via the strong relation 
                 # of equality:
                 return self.concludeViaEquality(assumptions)
         msg = ("Not able to conclude the desired relation of %s"
                 " from the proven relation of %s."
                 %(str(self), str(relation)))
         raise TransitivityException(self, assumptions, msg)
     return proven_relation
예제 #16
0
 def mergesort(cls, item_iterators, assumptions=USE_DEFAULTS,
               skip_exact_reps=False, skip_equiv_reps=False,
               requirements=None):
     '''
     Return the proven Sequence, a known truth for an expression 
     of type cls.SequenceClass(), representing the sorted sequence
     according to the cls.mergesorted_items method.
     Weak relations (e.g., <=) are only considered when calling
     this method on a weak relation class (otherwise, only
     equality and strong relations are used in the sorting).
     '''
     assumptions = defaults.checkedAssumptions(assumptions)
     items = cls.mergesorted_items(item_iterators, assumptions,
                                   skip_exact_reps, skip_equiv_reps,
                                   requirements)
     items = list(items)
     #print("merge sorted", items)
     return cls._fixedTransitivitySort(items, assumptions=assumptions,
                                        automation=False)        
예제 #17
0
 def conclude(self, assumptions=USE_DEFAULTS):
     '''
     From [element != a] AND ... AND [element != n],
     derive and return [element not in {a, b, ..., n}],
     where self is the EnumNonmembership object.
     '''
     # among other things, convert any assumptions=None
     # to assumptions=()
     assumptions = defaults.checkedAssumptions(assumptions)
     from ._theorems_ import nonmembershipFold
     enum_elements = self.domain.elements
     element = self.element
     operands = self.domain.operands
     return nonmembershipFold.instantiate(
         {
             n: num(len(enum_elements)),
             x: self.element,
             y: enum_elements
         },
         assumptions=assumptions)
예제 #18
0
    def conclude(self, assumptions=USE_DEFAULTS):
        '''
        Try to deduce that the given element is in the number set under 
        the given assumptions.
        '''
        element = self.element

        # See if the element is known to be equal with something
        # that is known to be in the number set.
        assumptions_set = set(defaults.checkedAssumptions(assumptions))
        for eq, equiv_elem in Equals.knownRelationsFromLeft(
                element, assumptions_set):
            try:
                equiv_elem_in_set = InSet(equiv_elem, self.number_set)
                equiv_elem_in_set.prove(assumptions, automation=False)
                return eq.subLeftSideInto(equiv_elem_in_set, assumptions)
            except ProofFailure:
                pass
        '''
        # Maybe let's not simplify first.  If
        # See if we can simplify the element first.
        if hasattr(element, 'simplification'):
            simplification = element.simplification(assumptions=assumptions)
            element = simplification.rhs
            if element != self.element:
                # Prove membersip for the simplified element
                elem_in_set = InSet(element, self.number_set).prove(assumptions)
                # Substitute into the original.
                return simplification.subLeftSideInto(elem_in_set, assumptions)
        '''

        # Try the 'deduceInNumberSet' method.
        if hasattr(element, 'deduceInNumberSet'):
            return element.deduceInNumberSet(self.number_set,
                                             assumptions=assumptions)
        else:
            msg = str(element) + " has no 'deduceInNumberSet' method."
            raise ProofFailure(InSet(self.element, self.number_set),
                               assumptions, msg)
예제 #19
0
파일: equals.py 프로젝트: rmmilewi/Prove-It
 def invert(lambda_map, rhs, assumptions=USE_DEFAULTS):
     '''
     Given some x -> f(x) map and a right-hand-side, find the
     x for which f(x) = rhs amongst known equalities under the
     given assumptions.  Return this x if one is found; return
     None otherwise.
     '''
     assumptionsSet = set(defaults.checkedAssumptions(assumptions))
     if (lambda_map, rhs) in Equals.inversions:
         # Previous solution(s) exist.  Use one if the assumptions are sufficient.
         for known_equality, inversion in Equals.inversions[(lambda_map,
                                                             rhs)]:
             if known_equality.isSufficient(assumptionsSet):
                 return inversion
     # The mapping may be a trivial identity: f(x) = f(x)
     try:
         x = lambda_map.extractParameter(rhs)
         # Found a trivial inversion.  Store it for future reference.
         known_equality = Equals(rhs, rhs).prove()
         Equals.inversions.setdefault((lambda_map, rhs), []).append(
             (known_equality, x))
         return x  # Return the found inversion.
     except ParameterExtractionError:
         pass  # well, it was worth a try
     # Search among known relations for a solution.
     for known_equality, lhs in Equals.knownRelationsFromRight(
             rhs, assumptionsSet):
         try:
             x = lambda_map.extractParameter(lhs)
             # Found an inversion.  Store it for future reference.
             Equals.inversions.setdefault((lambda_map, rhs), []).append(
                 (known_equality, x))
             return x  # Return the found inversion.
         except ParameterExtractionError:
             pass  # not a match.  keep looking.
     raise InversionError("No inversion found to map %s onto %s" %
                          (str(lambda_map), str(rhs)))
예제 #20
0
    def deduceInNumberSet(self, number_set, assumptions=USE_DEFAULTS):
        '''
        Given a number set number_set (such as Integers, Reals, etc),
        attempt to prove that the given expression is in that number
        set using the appropriate closure theorem.
        Created: 3/21/2020 by wdc, based on the same method in the Add
                 and Exp classes.
        Last modified: 3/26/2020 by wdc.
                       Added defaults.checkedAssumptions to avoid
                       ProofFailure error.
        Previously modified: 3/21/2020 by wdc. Creation
        Once established, these authorship notations can be deleted.
        '''
        from proveit.logic import InSet
        from proveit.number.absolute_value._theorems_ import (
            absComplexClosure, absNonzeroClosure, absComplexClosureNonNegReals)
        from proveit.number import Complexes, Reals, RealsNonNeg, RealsPos

        # among other things, make sure non-existent assumptions
        # manifest as empty tuple () rather than None
        assumptions = defaults.checkedAssumptions(assumptions)

        if number_set == Reals:
            return absComplexClosure.specialize({a: self.operand},
                                                assumptions=assumptions)

        if number_set == RealsPos:
            return absNonzeroClosure.specialize({a: self.operand},
                                                assumptions=assumptions)

        if number_set == RealsNonNeg:
            return absComplexClosureNonNegReals.specialize(
                {a: self.operand}, assumptions=assumptions)

        msg = ("'Abs.deduceInNumberSet()' not implemented for "
               "the %s set" % str(number_set))
        raise ProofFailure(InSet(self, number_set), assumptions, msg)
예제 #21
0
def defaultSimplification(innerExpr,
                          inPlace=False,
                          mustEvaluate=False,
                          operandsOnly=False,
                          assumptions=USE_DEFAULTS,
                          automation=True):
    '''
    Default attempt to simplify the given inner expression under the 
    given assumptions.  If successful, returns a KnownTruth (using a 
    subset of the given assumptions) that expresses an equality between 
    the expression (on the left) and one with a simplified form for the 
    "inner" part (in some canonical sense determined by the Operation).
    If inPlace is True, the top-level expression must be a KnownTruth
    and the simplified KnownTruth is derived instead of an equivalence
    relation.
    If mustEvaluate=True, the simplified form must be an irreducible 
    value (see isIrreducibleValue).  Specifically, this method checks to
    see if an appropriate simplification/evaluation has already been 
    proven.  If not, but if it is an Operation, call the 
    simplification/evaluation method on all operands, make these 
    substitions, then call simplification/evaluation on the expression 
    with operands substituted for simplified forms.  It also treats, 
    as a special case, evaluating the expression to be true if it is in 
    the set of assumptions [also see KnownTruth.evaluation and 
    evaluateTruth].  If operandsOnly = True, only simplify the operands
    of the inner expression.
    '''
    # among other things, convert any assumptions=None
    # to assumptions=() to avoid len(None) errors
    assumptions = defaults.checkedAssumptions(assumptions)

    from proveit.logic import TRUE, FALSE
    from proveit.logic.boolean._axioms_ import trueAxiom
    topLevel = innerExpr.exprHierarchy[0]
    inner = innerExpr.exprHierarchy[-1]
    if operandsOnly:
        # Just do the reduction of the operands at the level below the
        # "inner expression"
        reducedInnerExpr = reduceOperands(innerExpr, inPlace, mustEvaluate,
                                          assumptions)
        if inPlace:
            try:
                return reducedInnerExpr.exprHierarchy[0].prove(
                    assumptions, automation=False)
            except:
                assert False
        try:
            eq = Equals(topLevel, reducedInnerExpr.exprHierarchy[0])
            return eq.prove(assumptions, automation=False)
        except:
            assert False

    def innerSimplification(innerEquivalence):
        if inPlace:
            return innerEquivalence.subRightSideInto(innerExpr,
                                                     assumptions=assumptions)
        return innerEquivalence.substitution(innerExpr,
                                             assumptions=assumptions)

    if isIrreducibleValue(inner):
        return Equals(inner, inner).prove()
    assumptionsSet = set(defaults.checkedAssumptions(assumptions))

    # See if the expression is already known to be true as a special
    # case.
    try:
        inner.prove(assumptionsSet, automation=False)
        trueEval = evaluateTruth(inner, assumptionsSet)  # A=TRUE given A
        if inner == topLevel:
            if inPlace: return trueAxiom
            else: return trueEval
        return innerSimplification(trueEval)
    except:
        pass
    # See if the negation of the expression is already known to be true
    # as a special case.
    try:
        inner.disprove(assumptionsSet, automation=False)
        falseEval = evaluateFalsehood(inner,
                                      assumptionsSet)  # A=FALSE given Not(A)
        return innerSimplification(falseEval)
    except:
        pass

    # ================================================================ #
    # See if the expression already has a proven simplification        #
    # ================================================================ #

    # construct the key for the known_simplifications dictionary
    assumptions_sorted = sorted(assumptions, key=lambda expr: hash(expr))
    known_simplifications_key = (inner, tuple(assumptions_sorted))

    if (mustEvaluate and inner in Equals.known_evaluation_sets):
        evaluations = Equals.known_evaluation_sets[inner]
        candidates = []
        for knownTruth in evaluations:
            if knownTruth.isSufficient(assumptionsSet):
                # Found existing evaluation suitable for the assumptions
                candidates.append(knownTruth)
        if len(candidates) >= 1:
            # Return the "best" candidate with respect to fewest number
            # of steps.
            min_key = lambda knownTruth: knownTruth.proof().numSteps()
            simplification = min(candidates, key=min_key)
            return innerSimplification(simplification)

    elif (not mustEvaluate
          and known_simplifications_key in Equals.known_simplifications):
        simplification = Equals.known_simplifications[
            known_simplifications_key]
        return innerSimplification(simplification)

    # ================================================================ #

    if not automation:
        msg = 'Unknown evaluation (without automation): ' + str(inner)
        raise SimplificationError(msg)

    # See if the expression is equal to something that has an evaluation
    # or is already known to be true.
    if inner in Equals.knownEqualities:
        for knownEq in Equals.knownEqualities[inner]:
            try:
                if knownEq.isSufficient(assumptionsSet):
                    if inPlace:
                        # Should first substitute in the known
                        # equivalence then simplify that.
                        if inner == knownEq.lhs:
                            knownEq.subRightSideInto(innerExpr, assumptions)
                        elif inner == knownEq.rhs:
                            knownEq.subLeftSideInto(innerExpr, assumptions)
                    # Use mustEvaluate=True.  Simply being equal to
                    # something simplified isn't necessarily the
                    # appropriate simplification for "inner" itself.
                    alt_inner = knownEq.otherSide(inner).innerExpr()
                    equivSimp = \
                        defaultSimplification(alt_inner,  inPlace=inPlace,
                                            mustEvaluate=True,
                                            assumptions=assumptions,
                                            automation=False)
                    if inPlace:
                        # Returns KnownTruth with simplification:
                        return equivSimp
                    innerEquiv = knownEq.applyTransitivity(
                        equivSimp, assumptions)
                    if inner == topLevel: return innerEquiv
                    return innerEquiv.substitution(innerExpr,
                                                   assumptions=assumptions)
            except SimplificationError:
                pass
    # try to simplify via reduction
    if not isinstance(inner, Operation):
        if mustEvaluate:
            raise EvaluationError('Unknown evaluation: ' + str(inner),
                                  assumptions)
        else:
            # don't know how to simplify, so keep it the same
            return innerSimplification(Equals(inner, inner).prove())
    reducedInnerExpr = reduceOperands(innerExpr, inPlace, mustEvaluate,
                                      assumptions)
    if reducedInnerExpr == innerExpr:
        if mustEvaluate:
            # Since it wasn't irreducible to begin with, it must change
            # in order to evaluate.
            raise SimplificationError('Unable to evaluate: ' + str(inner))
        else:
            raise SimplificationError('Unable to simplify: ' + str(inner))
    # evaluate/simplify the reduced inner expression
    inner = reducedInnerExpr.exprHierarchy[-1]
    if mustEvaluate:
        innerEquiv = inner.evaluation(assumptions)
    else:
        innerEquiv = inner.simplification(assumptions)
    value = innerEquiv.rhs
    if value == TRUE:
        # Attempt to evaluate via proving the expression;
        # This should result in a shorter proof if allowed
        # (e.g., if theorems are usable).
        try:
            evaluateTruth(inner, assumptions)
        except:
            pass
    if value == FALSE:
        # Attempt to evaluate via disproving the expression;
        # This should result in a shorter proof if allowed
        # (e.g., if theorems are usable).
        try:
            evaluateFalsehood(inner, assumptions)
        except:
            pass
    reducedSimplification = innerSimplification(innerEquiv)
    if inPlace:
        simplification = reducedSimplification
    else:
        # Via transitivity, go from the original expression to the
        # reduced expression (simplified inner operands) and then the
        # final simplification (simplified inner expression).
        reduced_top_level = reducedInnerExpr.exprHierarchy[0]
        eq1 = Equals(topLevel, reduced_top_level)
        eq1.prove(assumptions, automation=False)
        eq2 = Equals(reduced_top_level, reducedSimplification.rhs)
        eq2.prove(assumptions, automation=False)
        simplification = eq1.applyTransitivity(eq2, assumptions)
    if not inPlace and topLevel == inner:
        # Store direct simplifications in the known_simplifications
        # dictionary for next time.
        Equals.known_simplifications[
            known_simplifications_key] = simplification
        if isIrreducibleValue(value):
            # also store it in the known_evaluation_sets dictionary for
            # next time, since it evaluated to an irreducible value.
            Equals.known_evaluation_sets.setdefault(topLevel,
                                                    set()).add(simplification)
    return simplification
예제 #22
0
 def insert(self, item, assumptions=USE_DEFAULTS):
     assumptions = defaults.checkedAssumptions(assumptions)
     Relation = self.__class__.RelationClass()
     return Relation._transitivityInsert(self, self.operators,
                                         self.operands, item, assumptions)
예제 #23
0
파일: abs.py 프로젝트: shoelbling/Prove-It
    def doReducedSimplification(self, assumptions=USE_DEFAULTS):
        '''
        For the case Abs(x) where the operand x is already known to
        be or assumed to be a non-negative real, derive and return 
        this Abs expression equated with the operand itself:
        |- Abs(x) = x. For the case where x is already known or assumed
        to be a negative real, return the Abs expression equated with
        the negative of the operand: |- Abs(x) = -x.
        Assumptions may be necessary to deduce necessary conditions for
        the simplification.
        '''
        from proveit.number import Greater, GreaterEq, Mult, Neg
        from proveit.number import (zero, Naturals, NaturalsPos, RealsNeg,
                                    RealsNonNeg, RealsPos)
        # among other things, convert any assumptions=None
        # to assumptions=() (thus averting len(None) errors)
        assumptions = defaults.checkedAssumptions(assumptions)

        #-- -------------------------------------------------------- --#
        #-- Case (1): Abs(x) where entire operand x is known or      --#
        #--           assumed to be non-negative Real.               --#
        #-- -------------------------------------------------------- --#
        if InSet(self.operand, RealsNonNeg).proven(assumptions=assumptions):
            # Entire operand is known to be or assumed to be a
            # non-negative real, so we can return Abs(x) = x
            return self.absElimination(operand_type='non-negative',
                                       assumptions=assumptions)

        #-- -------------------------------------------------------- --#
        #-- Case (2): Abs(x) where entire operand x is known or      --#
        #--           assumed to be a negative Real.                 --#
        #-- -------------------------------------------------------- --#
        if InSet(self.operand, RealsNeg).proven(assumptions=assumptions):
            # Entire operand is known to be or assumed to be a
            # negative real, so we can return Abs(x) = -x
            return self.absElimination(operand_type='negative',
                                       assumptions=assumptions)

        #-- -------------------------------------------------------- --#
        #-- Case (3): Abs(x) where entire operand x is not yet known --*
        #--           to be a non-negative Real, but can easily be   --#
        #--           proven to be a non-negative Real because it is --#
        #--           (a) known or assumed to be ≥ 0 or
        #--           (b) known or assumed to be in a subset of the  --#
        #--               non-negative Reals, or                     --#
        #--           (c) the addition or product of operands, all   --#
        #--               of which are known or assumed to be non-   --#
        #--               negative reals. TBA!
        #-- -------------------------------------------------------- --#
        if (Greater(self.operand, zero).proven(assumptions=assumptions)
                and not GreaterEq(self.operand,
                                  zero).proven(assumptions=assumptions)):
            GreaterEq(self.operand, zero).prove(assumptions=assumptions)
            # and then it will get picked up in the next if() below

        if GreaterEq(self.operand, zero).proven(assumptions=assumptions):
            from proveit.number.sets.real._theorems_ import (
                inRealsNonNegIfGreaterEqZero)
            inRealsNonNegIfGreaterEqZero.specialize({a: self.operand},
                                                    assumptions=assumptions)
            return self.absElimination(operand_type='non-negative',
                                       assumptions=assumptions)

        if self.operand in InSet.knownMemberships.keys():
            for kt in InSet.knownMemberships[self.operand]:
                if kt.isSufficient(assumptions):
                    if isEqualToOrSubsetEqOf(
                            kt.expr.operands[1],
                            equal_sets=[RealsNonNeg, RealsPos],
                            subset_eq_sets=[Naturals, NaturalsPos, RealsPos],
                            assumptions=assumptions):

                        InSet(self.operand,
                              RealsNonNeg).prove(assumptions=assumptions)
                        return self.absElimination(operand_type='non-negative',
                                                   assumptions=assumptions)

        if isinstance(self.operand, Add) or isinstance(self.operand, Mult):
            count_of_known_memberships = 0
            count_of_known_relevant_memberships = 0
            for op in self.operand.operands:
                if op in InSet.knownMemberships.keys():
                    count_of_known_memberships += 1
            if count_of_known_memberships == len(self.operand.operands):
                for op in self.operand.operands:
                    op_temp_known_memberships = InSet.knownMemberships[op]
                    for kt in op_temp_known_memberships:
                        if (kt.isSufficient(assumptions)
                                and isEqualToOrSubsetEqOf(
                                    kt.expr.operands[1],
                                    equal_sets=[RealsNonNeg, RealsPos],
                                    subset_eq_sets=[
                                        Naturals, NaturalsPos, RealsPos,
                                        RealsNonNeg
                                    ],
                                    assumptions=assumptions)):

                            count_of_known_relevant_memberships += 1
                            break

                if (count_of_known_relevant_memberships == len(
                        self.operand.operands)):
                    # Prove that the sum or product is in
                    # RealsNonNeg and then instantiate absElimination.
                    for op in self.operand.operands:
                        InSet(op, RealsNonNeg).prove(assumptions=assumptions)
                    return self.absElimination(assumptions=assumptions)

        #-- -------------------------------------------------------- --#
        #-- Case (4): Abs(x) where operand x can easily be proven    --#
        #--           to be a negative Real because -x is known to   --#
        #--           be in a subset of the positive Reals           --#
        #-- -------------------------------------------------------- --#
        negated_op = None
        if isinstance(self.operand, Neg):
            negated_op = self.operand.operand
        else:
            negated_op = Neg(self.operand)
        negated_op_simp = negated_op.simplification(
            assumptions=assumptions).rhs

        if negated_op_simp in InSet.knownMemberships.keys():
            from proveit.number.sets.real._theorems_ import (
                negInRealsNegIfPosInRealsPos)
            for kt in InSet.knownMemberships[negated_op_simp]:
                if kt.isSufficient(assumptions):
                    if isEqualToOrSubsetEqOf(
                            kt.expr.operands[1],
                            equal_sets=[RealsNonNeg, RealsPos],
                            subset_sets=[NaturalsPos, RealsPos],
                            subset_eq_sets=[NaturalsPos, RealsPos],
                            assumptions=assumptions):

                        InSet(negated_op_simp,
                              RealsPos).prove(assumptions=assumptions)
                        negInRealsNegIfPosInRealsPos.specialize(
                            {a: negated_op_simp}, assumptions=assumptions)
                        return self.absElimination(operand_type='negative',
                                                   assumptions=assumptions)

        # for updating our equivalence claim(s) for the
        # remaining possibilities
        from proveit import TransRelUpdater
        eq = TransRelUpdater(self, assumptions)
        return eq.relation
예제 #24
0
 def insert(self, item, assumptions=USE_DEFAULTS):
     assumptions = defaults.checkedAssumptions(assumptions)
     return self.__class__._transitivityInsert(self, [self.operator],
                                               self.operands, item,
                                               assumptions)
예제 #25
0
    def specialize(self,
                   specializeMap=None,
                   relabelMap=None,
                   assumptions=USE_DEFAULTS):
        '''
        Performs a specialize derivation step to be proven under the given
        assumptions, in addition to the assumptions of the KnownTruth.
        This will eliminate one or more nested Forall operations, specializing
        the instance variables according to specializeMap.  Eliminates
        the number of Forall operations required to utilize all of the
        specializeMap keys.  The default mapping of all instance variables
        is a mapping to itself (e.g., {x:x, y:y}).  Simultaneously, variables 
        may be relabeled via relabelMap (see the relabel method).  Note, there 
        is a difference between  making substitutons simultaneously versus 
        in-series.  For example, the {x:y, y:x} mapping will swap x and y 
        variables, but mapping {x:y} then {y:x} in series would set both 
        variables to x.
        Returns the proven specialized KnownTruth, or throws an exception if the
        proof fails.        
        '''
        from proveit import Operation, Variable, Lambda, singleOrCompositeExpression
        from proveit.logic import Forall
        from .proof import Specialization, SpecializationFailure, ProofFailure

        if not self.isUsable():
            # If this KnownTruth is not usable, see if there is an alternate under the
            # set of assumptions that is usable.
            try:
                alternate = self.expr.prove(assumptions, automation=False)
            except ProofFailure:
                self.raiseUnusableProof()
            return alternate.specialize(specializeMap, relabelMap, assumptions)

        # if no specializeMap is provided, specialize the "explicitInstanceVars" of the Forall with default mappings
        # (mapping instance variables to themselves)
        if specializeMap is None:
            specializeMap = {
                ivar: ivar
                for ivar in self.explicitInstanceVars()
            }
        if relabelMap is None: relabelMap = dict()

        # Include the KnownTruth assumptions along with any provided assumptions
        assumptions = defaults.checkedAssumptions(assumptions)
        assumptions += self.assumptions

        # For any entrys in the subMap with Operation keys, convert
        # them to corresponding operator keys with Lambda substitutions.
        # For example f(x,y):g(x,y) would become f:[(x,y) -> g(x,y)].
        # Convert to composite expressions as needed (via singleOrCompositeExpression).
        processedSubMap = dict()
        for key, sub in specializeMap.items():
            sub = singleOrCompositeExpression(sub)
            if isinstance(key, Operation):
                operation = key
                subVar = operation.operator
                sub = Lambda(operation.operands, sub)
                processedSubMap[subVar] = sub
            elif isinstance(key, Variable):
                processedSubMap[key] = sub
            else:
                raise SpecializationFailure(
                    self, specializeMap, relabelMap, assumptions,
                    'Expecting specializeMap keys to be Variables, MultiVariables, or Operations with Variable/MultiVariable operators; not %s'
                    % str(key.__class__))
        remainingSubVars = set(processedSubMap.keys())

        # Determine the number of Forall eliminations.  There must be at least
        # one (if zero is desired, relabel should be called instead).
        # The number is determined by the instance variables that occur as keys
        # in the subMap.
        expr = self.expr
        numForallEliminations = 0
        while numForallEliminations == 0 or len(remainingSubVars) > 0:
            numForallEliminations += 1
            if not isinstance(expr, Forall):
                raise SpecializationFailure(
                    self, specializeMap, relabelMap, assumptions,
                    'May only specialize instance variables of directly nested Forall operations'
                )
            lambdaExpr = expr.operand
            assert isinstance(
                lambdaExpr,
                Lambda), "Forall Operation operand must be a Lambda function"
            instanceVars, expr, conditions = lambdaExpr.parameterVars, lambdaExpr.body, lambdaExpr.conditions
            for iVar in instanceVars:
                if iVar in remainingSubVars:
                    # remove this instance variable from the remaining substitution variables
                    remainingSubVars.remove(iVar)
                elif iVar not in processedSubMap:
                    # default is to map instance variables to themselves
                    processedSubMap[iVar] = iVar

        return self._checkedTruth(
            Specialization(self,
                           numForallEliminations=numForallEliminations,
                           specializeMap=processedSubMap,
                           relabelMap=relabelMap,
                           assumptions=assumptions))
예제 #26
0
 def insert(RelationClass, sequence, item, assumptions=USE_DEFAULTS):
     assumptions = defaults.checkedAssumptions(assumptions)
     return RelationClass._transitivityInsert(sequence, item, assumptions)
예제 #27
0
    def conclude(self, assumptions):
        '''
        If the instance expression, or some instance expression of
        nested universal quantifiers, is known to be true, conclude
        via generalization.  Otherwise, if the domain has a 'foldForall'
        method, attempt to conclude this Forall statement
        via 'concludeAsFolded'.
        '''
        # first try to prove via generalization without automation
        assumptions = defaults.checkedAssumptions(assumptions)
        expr = self
        instanceParamLists = []
        conditions = []
        while isinstance(expr, Forall):
            new_params = expr.explicitInstanceParams()
            instanceParamLists.append(list(new_params))
            conditions += list(expr.conditions)
            expr = expr.instanceExpr
            new_assumptions = assumptions + tuple(conditions)
            if expr.proven(assumptions=assumptions + tuple(conditions)):
                proven_inst_expr = expr.prove(new_assumptions)
                return proven_inst_expr.generalize(instanceParamLists,
                                                   conditions=conditions)
        # next try 'foldAsForall' on the domain (if applicable)
        if self.hasDomain() and hasattr(self.first_domain(), 'foldAsForall'):
            # try foldAsForall first
            try:
                return self.concludeAsFolded(assumptions)
            except:
                raise ProofFailure(
                    self, assumptions, "Unable to conclude automatically; "
                    "the 'foldAsForall' method on the "
                    "domain failed.")
        else:
            # If there is no 'foldAsForall' strategy to try, we can
            # attempt a different non-trivial strategy of proving
            # via generalization with automation.
            try:
                conditions = list(self.conditions)
                proven_inst_expr = self.instanceExpr.prove(
                    assumptions=assumptions + tuple(conditions))
                instanceParamLists = [list(self.explicitInstanceParams())]
                # see if we can generalize multiple levels
                # simultaneously for a shorter proof
                while isinstance(proven_inst_expr.proof(), Generalization):
                    new_params = proven_inst_expr.explicitInstanceParams()
                    instanceParamLists.append(list(new_params))
                    conditions += proven_inst_expr.conditions
                    proven_inst_expr = proven_inst_expr.proof(
                    ).requiredTruths[0]
                return proven_inst_expr.generalize(instanceParamLists,
                                                   conditions=conditions)
            except ProofFailure:
                raise ProofFailure(
                    self, assumptions, "Unable to conclude automatically; "
                    "the domain has no 'foldAsForall' method "
                    "and automated generalization failed.")

        raise ProofFailure(
            self, assumptions, "Unable to conclude automatically; a "
            "universally quantified instance expression "
            "is not known to be true and the domain has "
            "no 'foldAsForall' method.")
예제 #28
0
def apply_roundingExtraction(expr, roundingExtractionThm, idx_to_extract=None,
                             assumptions=USE_DEFAULTS):
    '''
    Let F(x) represent the relevant Ceil(x), Floor(x), or Round(x)
    fxn calling the apply_roundingExtraction() method from the
    respective F(x).roundingExtraction() method. For the case of F(x)
    where the operand x = x_real + x_int, derive and return
    F(x) = F(x_real) + x_int (thus 'extracting' the integer component
    of x out from inside the function F()). The idx_to_extract is the
    zero-based index of the item in the operands of an Add(a, b, …, n)
    expression to attempt to extract. Assumptions may be necessary to
    deduce necessary conditions (for example, that x_int actually is
    an integer) for the simplification. For example,
    let F(x) = Ceil(x+2+y). Calling
        F(x).roundingExtraction(
                1, assumptions=[InSet(x, Reals), InSet(y, Reals)]),
    will eventually end up here and return
        |- F(x) = Ceil(x+y) + 2
    This method is utilized by the F(x).apply_reducedSimplification()
    method, indirectly via the F(x).roundingExtraction() method, but
    only after the operand x is verified to already be proven (or
    assumed) to be the sum of reals and integers.
    For the case where the entire operand x is itself an integer,
    see the roundingElimination() method.
    This works only if the operand x is an instance of the Add
    class at its outermost level, e.g. x = Add(a, b, …, n). The
    operands of that Add class can be other things, but the extraction
    will work only if the inner operands a, b, ..., n are simple.
    '''
    from proveit._common_ import n, x, y
    from proveit.number import Add
    # from ._theorems_ import roundOfRealPlusInt

    # among other things, convert any assumptions=None
    # to assumptions=() to avoid later len() errors
    assumptions = defaults.checkedAssumptions(assumptions)

    # for convenience while updating our equation
    eq = TransRelUpdater(expr, assumptions)

    # first use Add.commutation to (re-)arrange operands to comform
    # to theorem format, using user-supplied idx_to_extract
    if isinstance(expr.operand, Add):
        expr = eq.update(expr.innerExpr().operand.commutation(idx_to_extract,
                len(expr.operand.operands)-1, assumptions=assumptions))

        # An association step -- because the later application of
        # the roundOfRealPlusInt thm produces a grouping of the 
        # Round operands in the chain of equivalences.
        # BUT, only perform the association if multiple operands are
        # needing to be associated:
        if len(expr.operand.operands)-1 > 1:
            expr = eq.update(expr.innerExpr().operand.association(
                    0, len(expr.operand.operands)-1, assumptions=assumptions))


        # then update by applying the roundOfRealPlusInt thm
        x_sub = expr.operand.operands[0]
        n_sub = expr.operand.operands[1]
        expr = eq.update(roundingExtractionThm.specialize(
                {x:x_sub, n:n_sub}, assumptions=assumptions))

        return eq.relation
    else:
        raise ValueError("In attempting f(x).apply_roundingExtraction(), "
                         "the operand x is not of class 'Add'.")
예제 #29
0
파일: equals.py 프로젝트: rmmilewi/Prove-It
def defaultSimplification(innerExpr,
                          inPlace=False,
                          mustEvaluate=False,
                          operandsOnly=False,
                          assumptions=USE_DEFAULTS,
                          automation=True):
    '''
    Default attempt to simplify the given inner expression under the given assumptions.
    If successful, returns a KnownTruth (using a subset of the given assumptions)
    that expresses an equality between the expression (on the left) and one with 
    a simplified form for the "inner" part (in some canonical sense determined by 
    the Operation).
    If inPlace is True, the top-level expression must be a KnownTruth
    and the simplified KnownTruth is derived instead of an equivalence
    relation.
    If mustEvaluate=True, the simplified form must be an irreducible value
    (see isIrreducibleValue).  Specifically, this method checks to see if an
    appropriate simplification/evaluation has already been proven.  If not, but
    if it is an Operation, call the simplification/evaluation method on
    all operands, make these substitions, then call simplification/evaluation on the
    expression with operands substituted for simplified forms.  It also treats, 
    as a special case, evaluating the expression to be true if it is in the set
    of assumptions [also see KnownTruth.evaluation and evaluateTruth].
    If operandsOnlTrue, only simplify the operands of the inner expression.
    '''
    from proveit.logic import TRUE, FALSE
    from proveit.logic.boolean._axioms_ import trueAxiom
    topLevel = innerExpr.exprHierarchy[0]
    inner = innerExpr.exprHierarchy[-1]
    if operandsOnly:
        # just do the reduction of the operands at the level below the "inner expression"
        reducedInnerExpr = reduceOperands(
            innerExpr, inPlace, mustEvaluate,
            assumptions)  # OR DO WE WANT TO REDUCE AT THE INNER LEVEL??
        if inPlace:
            return reducedInnerExpr.exprHierarchy[0].prove(
                assumptions
            )  # should have already been proven within 'reduceOperands' called above.
        return Equals(topLevel, reducedInnerExpr.exprHierarchy[0]).prove(
            assumptions
        )  # should have already been proven within 'reduceOperands' called above.

    def innerSimplification(innerEquivalence):
        if inPlace:
            return innerEquivalence.subRightSideInto(innerExpr,
                                                     assumptions=assumptions)
        return innerEquivalence.substitution(innerExpr,
                                             assumptions=assumptions)

    if isinstance(inner, IrreducibleValue):
        IrreducibleValue.evaluation(inner)
    assumptionsSet = set(defaults.checkedAssumptions(assumptions))
    # see if the expression is already known to be true as a special case
    try:
        inner.prove(assumptionsSet, automation=False)
        trueEval = evaluateTruth(inner, assumptionsSet)  # A=TRUE given A
        if inner == topLevel:
            if inPlace: return trueAxiom
            else: return trueEval
        return innerSimplification(trueEval)
    except:
        pass
    # see if the negation of the expression is already known to be true as a special case
    try:
        inner.disprove(assumptionsSet, automation=False)
        falseEval = evaluateFalsehood(inner,
                                      assumptionsSet)  # A=FALSE given Not(A)
        return innerSimplification(falseEval)
    except:
        pass
    # See if the expression already has a proven evaluation
    if inner in Equals.evaluations or (not mustEvaluate
                                       and inner in Equals.simplifications):
        simplifications = Equals.evaluations[
            inner] if mustEvaluate else Equals.simplifications[inner]
        candidates = []
        for knownTruth in simplifications:
            if knownTruth.isSufficient(assumptionsSet):
                candidates.append(
                    knownTruth
                )  # found existing evaluation suitable for the assumptions
        if len(candidates) >= 1:
            # return the "best" candidate with respect to fewest number of steps
            evaluation = min(
                candidates,
                key=lambda knownTruth: knownTruth.proof().numSteps())
            return innerSimplification(evaluation)

    if not automation:
        raise EvaluationError('Unknown evaluation (without automation): ' +
                              str(inner))
    # See if the expression is equal to something that has an evaluation or is
    # already known to be true.
    if inner in Equals.knownEqualities:
        for knownTruth in Equals.knownEqualities[inner]:
            try:
                if knownTruth.isSufficient(assumptionsSet):
                    if inPlace:  # should first substitute in the known equivalence then simplify that
                        if inner == knownTruth.lhs:
                            knownTruth.subRightSideInto(innerExpr, assumptions)
                        elif inner == knownTruth.rhs:
                            knownTruth.subLeftSideInto(innerExpr, assumptions)
                    equivSimp = defaultSimplification(
                        knownTruth.otherSide(inner).innerExpr(),
                        inPlace=inPlace,
                        mustEvaluate=mustEvaluate,
                        assumptions=assumptions,
                        automation=False)
                    if inPlace:
                        return equivSimp  # returns KnownTruth with simplification
                    innerEquiv = Equals(inner,
                                        equivSimp.rhs).concludeViaTransitivity(
                                            assumptions=assumptions)
                    if inner == topLevel: return innerEquiv
                    return innerEquiv.substitution(innerExpr,
                                                   assumptions=assumptions)
            except EvaluationError:
                pass
    # try to simplify via reduction
    if not isinstance(inner, Operation):
        if mustEvaluate:
            raise EvaluationError('Unknown evaluation: ' + str(inner))
        else:
            # don't know how to simplify, so keep it the same
            return innerSimplification(Equals(inner, inner).prove())
    reducedInnerExpr = reduceOperands(
        innerExpr, inPlace, mustEvaluate,
        assumptions)  # OR DO WE WANT TO REDUCE AT THE INNER LEVEL??
    if reducedInnerExpr == innerExpr:
        if mustEvaluate:
            # since it wasn't irreducible to begin with, it must change in order to evaluate
            raise EvaluationError('Unable to evaluate: ' + str(inner))
        elif inPlace:
            return topLevel  # don't know how to simplify further
        else:
            return Equals(
                topLevel,
                topLevel).prove()  # don't know how to simplify further
    # evaluate/simplify the reduced inner expression
    inner = reducedInnerExpr.exprHierarchy[-1]
    innerEquiv = inner.evaluation() if mustEvaluate else inner.simplification()
    value = innerEquiv.rhs
    if value == TRUE:
        # Attempt to evaluate via proving the expression;
        # This should result in a shorter proof if allowed
        # (e.g., if theorems are usable).
        try:
            evaluateTruth(inner, assumptions)
        except:
            pass
    if value == FALSE:
        # Attempt to evaluate via disproving the expression;
        # This should result in a shorter proof if allowed
        # (e.g., if theorems are usable).
        try:
            evaluateFalsehood(inner, assumptions)
        except:
            pass
    reducedSimplification = innerSimplification(innerEquiv)
    if inPlace:
        simplification = reducedSimplification
    else:
        # via transitivity, go from the original expression to the reduced expression
        # (simplified inner operands) and then the final simplification (simplified inner
        # expression).
        simplification = Equals(
            topLevel,
            reducedSimplification.rhs).concludeViaTransitivity(assumptions)
    if not inPlace and topLevel == inner:
        # store direct simplifications in the simplifications dictionary for next time
        Equals.simplifications.setdefault(topLevel, set()).add(simplification)
        if isinstance(value, IrreducibleValue):
            # also store it in the evaluations dictionary for next time
            # since it evaluated to an irreducible value.
            Equals.evaluations.setdefault(topLevel, set()).add(simplification)
    return simplification
예제 #30
0
def apply_reducedSimplification(expr, assumptions=USE_DEFAULTS):
    '''
    Let F(x) represent the relevant Ceil(x), Floor(x), or Round(x)
    fxn calling the apply_reducedSimplification() method from the
    respective F(x).doReducedSimplification() method (which itself is
    likely called from the F(x).simplification() method).
    For the trivial case F(x) where the operand x is already
    known to be or assumed to be an integer, derive and return this
    F(x) expression equated with the operand itself: F(x) = x. For
    example, |- Round(2) = 2 or |- Floor(1) = 1. Assumptions may be
    necessary to deduce necessary conditions for the simplification
    (for example, for deducing that the operand really is an integer).
    For the case where the operand is of the form x = real + int,
    derive and return this F(x) expression equated with F(real) + int.
    For example, |- Floor(x + 2) = Floor(x) + 2. Again, assumptions
    may be necessary to deduce the appropriate set containments for
    the operands within the Add operand x.
    '''
    from proveit._common_ import n, x
    from proveit.logic import InSet
    from proveit.number import Add, Integers, Reals

    # among other things, convert any assumptions=None
    # to assumptions=() (thus averting len(None) errors)
    assumptions = defaults.checkedAssumptions(assumptions)

    #-- -------------------------------------------------------- --#
    #-- Case (1): F(x) where entire operand x is known or        --#
    #--           assumed to be an Integer.                      --#
    #-- -------------------------------------------------------- --#
    if InSet(expr.operand, Integers).proven(assumptions=assumptions):
        # Entire operand is known to be or assumed to be an integer
        # so we can simply remove the Ceil, Floor, or Round wrapper
        return expr.roundingElimination(assumptions)

    #-- -------------------------------------------------------- --#
    #-- Case (2): F(x) where entire operand x is not yet known   --*
    #--           to be an Integer but can EASILY be proven      --#
    #--           to be an Integer.                              --#
    #-- -------------------------------------------------------- --#
    if expr.operand in InSet.knownMemberships.keys():
        from proveit.logic.set_theory import Subset, SubsetEq
        for kt in InSet.knownMemberships[expr.operand]:
            if kt.isSufficient(assumptions):
                if (SubsetEq(kt.expr.operands[1], Integers).proven(assumptions)
                    or 
                    Subset(kt.expr.operands[1], Integers).proven(assumptions)):
                    InSet(expr.operand, Integers).prove()
                    return expr.roundingElimination(assumptions)


    # for updating our equivalence claim(s) for the
    # remaining possibilities
    eq = TransRelUpdater(expr, assumptions)

    #-- -------------------------------------------------------- --#
    #-- Case (3): F(x) = F(Add(a,b,...,n)), where operand x is   --#
    #--           an Add object, not known or assumed to be an   --#
    #--           an int, but addends might be reals and ints    --#
    #-- -------------------------------------------------------- --#
    if isinstance(expr.operand, Add):
        # Try to partition all suboperands into Integers vs.
        # Non-Integers, and if there is at least one integer, try to
        # apply the extraction theorem (allowing an error message
        # if the specialization fails).

        subops = expr.operand.operands

        # Collect indices of operands known or assumed to be
        # ints versus reals versus neither
        indices_of_known_ints = []
        indices_of_non_ints = []
        for i in range(len(subops)):
            the_subop = subops[i]

            # (a) first perform easiest check: is the subop already known
            #     to be an Integer?
            if InSet(the_subop, Integers).proven(assumptions):
                indices_of_known_ints.append(i)

            # (b) then try something just a little harder
            elif the_subop in InSet.knownMemberships.keys():
                from proveit.logic.set_theory import Subset, SubsetEq
                for kt in InSet.knownMemberships[the_subop]:
                    if kt.isSufficient(assumptions):
                        if (SubsetEq(kt.expr.operands[1], Integers).
                                proven(assumptions) or
                            Subset(kt.expr.operands[1], Integers).
                                proven(assumptions)):
                            InSet(the_subop, Integers).prove()
                            indices_of_known_ints.append(i)
                            break

            # (c) then if the_subop is not an integer, note that instead
            if (i not in indices_of_known_ints):
                # we categorize it as a non-integer
                indices_of_non_ints.append(i)

        if len(indices_of_known_ints)>0:
            # Then we have at least one known integer addend, so we
            # rearrange and group the addends, associating the non-ints
            # and associating the ints
            original_addends = list(subops)
            desired_order_by_index = list(
                    indices_of_non_ints + indices_of_known_ints)
            # commute to put reals first, followed by ints
            for i in range(len(original_addends)):
                init_idx = expr.operand.operands.index(
                        original_addends[desired_order_by_index[i]])
                expr = eq.update(
                        expr.innerExpr().operand.commutation(
                                init_idx, i, assumptions=assumptions))

            # associate the non-integers (if more than 1)
            if len(indices_of_non_ints) > 1:
                # associate those elements (already re-arranged to
                # be at the front of the operand.operands):
                expr = eq.update(
                        expr.innerExpr().operand.association(
                                0, len(indices_of_non_ints),
                                assumptions=assumptions))

            # associate the known integers (if more than 1)
            if len(indices_of_known_ints) > 1:
                # associate those elements (already re-arranged to
                # be at the end of the operand.operands):
                if len(indices_of_non_ints) > 0:
                    start_idx = 1
                else:
                    start_idx = 0
                expr = eq.update(
                        expr.innerExpr().operand.association(
                                start_idx,
                                len(indices_of_known_ints),
                                assumptions=assumptions))

            if len(indices_of_known_ints)==len(subops):
                # all the addends were actually integers
                # could probably short-circuit this earlier!
                expr = eq.update(expr.roundingElimination(assumptions))
            else:
                expr = eq.update(expr.roundingExtraction(1, assumptions))
            return eq.relation

        else:
            # We did not find any integers.
            # Instead of returning an error, simply return the original
            # rounding expression equal to itself
            return eq.relation

    #-- -------------------------------------------------------- --#
    #-- Case (4): F(x) where operand x is not known or assumed   --#
    #--           to be an Integer and x is not an Add object    --#
    #-- -------------------------------------------------------- --#
    # apply_reducedSimplification() function is expecting simpler
    # operands; instead of returning an error, though, simply return
    # the trivial equivalence of the original expression with itself
    return eq.relation