Ejemplo n.º 1
0
def reduceOperands(innerExpr,
                   inPlace=True,
                   mustEvaluate=False,
                   assumptions=USE_DEFAULTS):
    '''
    Attempt to return an InnerExpr object that is provably equivalent to
    the given innerExpr but with simplified operands at the inner-expression
    level. 
    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 is True, the simplified
    operands must be irreducible values (see isIrreducibleValue).
    '''
    # Any of the operands that can be simplified must be replaced with their evaluation
    from proveit import InnerExpr
    assert isinstance(
        innerExpr,
        InnerExpr), "Expecting 'innerExpr' to be of type 'InnerExpr'"
    inner = innerExpr.exprHierarchy[-1]
    while True:
        allReduced = True
        for operand in inner.operands:
            if not mustEvaluate or not isIrreducibleValue(operand):
                # the operand is not an irreducible value so it must be evaluated
                operandEval = operand.evaluation(
                    assumptions=assumptions
                ) if mustEvaluate else operand.simplification(
                    assumptions=assumptions)
                if mustEvaluate and not isIrreducibleValue(operandEval.rhs):
                    raise EvaluationError(
                        'Evaluations expected to be irreducible values')
                if operandEval.lhs != operandEval.rhs:
                    # compose map to replace all instances of the operand within the inner expression
                    lambdaMap = innerExpr.replMap().compose(
                        Lambda.globalRepl(inner, operand))
                    # substitute in the evaluated value
                    if inPlace:
                        innerExpr = InnerExpr(
                            operandEval.subRightSideInto(lambdaMap),
                            innerExpr.innerExprPath)
                    else:
                        innerExpr = InnerExpr(
                            operandEval.substitution(lambdaMap).rhs,
                            innerExpr.innerExprPath)
                    allReduced = False
                    break  # start over (there may have been multiple substitutions)
        if allReduced: return innerExpr
        inner = innerExpr.exprHierarchy[-1]
Ejemplo n.º 2
0
 def evaluation(self, assumptions=USE_DEFAULTS):
     '''
     Given operands that may be evaluated to irreducible values that
     may be compared, or if there is a known evaluation of this
     equality, derive and return this expression equated to
     TRUE or FALSE.
     '''
     if self.lhs == self.rhs:
         # prove equality is true by reflexivity
         return evaluateTruth(self.prove().expr, assumptions=[])
     if isIrreducibleValue(self.lhs) and isIrreducibleValue(self.rhs):
         # Irreducible values must know how to evaluate the equality
         # between each other, where appropriate.
         return self.lhs.evalEquality(self.rhs)
     return TransitiveRelation.evaluation(self, assumptions)
Ejemplo n.º 3
0
 def sideEffects(self, knownTruth):
     '''
     Record the knownTruth in Equals.knownEqualities, associated from
     the left hand side and the right hand side.  This information may
     be useful for concluding new equations via transitivity. 
     If the right hand side is an "irreducible value" (see 
     isIrreducibleValue), also record it in Equals.evaluations for use
     when the evaluation method is called.   Some side-effects
     derivations are also attempted depending upon the form of
     this equality.
     '''
     from proveit.logic.boolean._common_ import TRUE, FALSE
     Equals.knownEqualities.setdefault(self.lhs, set()).add(knownTruth)
     Equals.knownEqualities.setdefault(self.rhs, set()).add(knownTruth)
     if isIrreducibleValue(self.rhs):
         Equals.simplifications.setdefault(self.lhs, set()).add(knownTruth)
         Equals.evaluations.setdefault(self.lhs, set()).add(knownTruth)
     if (self.lhs != self.rhs):
         # automatically derive the reversed form which is equivalent
         yield self.deriveReversed
     if self.rhs == FALSE:
         # derive lhs => FALSE from lhs = FALSE
         yield self.deriveContradiction
         # derive lhs from Not(lhs) = FALSE, if self is in this form
         #yield self.deriveViaFalsifiedNegation
     if self.rhs in (TRUE, FALSE):
         # automatically derive A from A=TRUE or Not(A) from A=FALSE
         yield self.deriveViaBooleanEquality
     if hasattr(self.lhs, 'equalitySideEffects'):
         for sideEffect in self.lhs.equalitySideEffects(knownTruth):
             yield sideEffect
Ejemplo n.º 4
0
 def conclude(self, assumptions):
     '''
     Attempt to conclude the equality various ways:
     simple reflexivity (x=x), via an evaluation (if one side is an irreducible),
     or via transitivity.
     '''
     from proveit.logic import TRUE, FALSE, Implies, Iff
     if self.lhs == self.rhs:
         # Trivial x=x
         return self.concludeViaReflexivity()
     if self.lhs or self.rhs in (TRUE, FALSE):
         try:
             # Try to conclude as TRUE or FALSE.
             return self.concludeBooleanEquality(assumptions)
         except ProofFailure:
             pass
     if isIrreducibleValue(self.rhs):
         return self.lhs.evaluation()
     elif isIrreducibleValue(self.lhs):
         return self.rhs.evaluation().deriveReversed()
     try:
         Implies(self.lhs, self.rhs).prove(assumptions, automation=False)
         Implies(self.rhs, self.lhs).prove(assumptions, automation=False)
         # lhs => rhs and rhs => lhs, so attempt to prove lhs = rhs via lhs <=> rhs
         # which works when they can both be proven to be Booleans.
         try:
             return Iff(self.lhs, self.rhs).deriveEquality(assumptions)
         except:
             from proveit.logic.boolean.implication._theorems_ import eqFromMutualImpl
             return eqFromMutualImpl.specialize({
                 A: self.lhs,
                 B: self.rhs
             },
                                                assumptions=assumptions)
     except ProofFailure:
         pass
     """
     # Use concludeEquality if available
     if hasattr(self.lhs, 'concludeEquality'):
         return self.lhs.concludeEquality(assumptions)
     if hasattr(self.rhs, 'concludeEquality'):
         return self.rhs.concludeEquality(assumptions)
     """
     # 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)
Ejemplo n.º 5
0
    def sideEffects(self, knownTruth):
        '''
        Record the knownTruth in Equals.knownEqualities, associated from
        the left hand side and the right hand side.  This information
        may be useful for concluding new equations via transitivity. 
        If the right hand side is an "irreducible value" (see 
        isIrreducibleValue), also record it in
        Equals.known_evaluation_sets for use when the evaluation
        method is called.   Some side-effects derivations are also
        attempted depending upon the form of this equality.
        If the rhs is an "irreducible value" (see isIrreducibleValue),
        also record the knownTruth in the Equals.known_simplifications
        and Equals.known_evaluation_sets dictionaries, for use when the
        simplification or evaluation method is called. The key for the
        known_simplifications dictionary is the specific *combination*
        of the lhs expression along with the assumptions in the form
        (expr, tuple(sorted(assumptions))); the key for the
        known_evaluation_sets dictionary is just the lhs expression
        without the specific assumptions. Some side-effects
        derivations are also attempted depending upon the form of this
        equality.
        '''
        from proveit.logic.boolean._common_ import TRUE, FALSE
        Equals.knownEqualities.setdefault(self.lhs, set()).add(knownTruth)
        Equals.knownEqualities.setdefault(self.rhs, set()).add(knownTruth)

        if isIrreducibleValue(self.rhs):
            assumptions_sorted = sorted(knownTruth.assumptions,
                                        key=lambda expr: hash(expr))
            lhsKey = (self.lhs, tuple(assumptions_sorted))
            # n.b.: the values in the known_simplifications
            # dictionary consist of single KnownTruths not sets
            Equals.known_simplifications[lhsKey] = knownTruth
            Equals.known_evaluation_sets.setdefault(self.lhs,
                                                    set()).add(knownTruth)

        if (self.lhs != self.rhs):
            # automatically derive the reversed form which is equivalent
            yield self.deriveReversed
        if self.rhs == FALSE:
            try:
                self.lhs.prove(automation=False)
                # derive FALSE given lhs=FALSE and lhs.
                yield self.deriveContradiction
            except ProofFailure:
                pass
            # Use this form after merging in 'Expression.proven' commite:
            #if self.lhs.proven(): # If lhs is proven using default assumptions.
            #    # derive FALSE given lhs=FALSE and lhs.
            #    yield self.deriveContradiction
        if self.rhs in (TRUE, FALSE):
            # automatically derive A from A=TRUE or Not(A) from A=FALSE
            yield self.deriveViaBooleanEquality
        if hasattr(self.lhs, 'equalitySideEffects'):
            for sideEffect in self.lhs.equalitySideEffects(knownTruth):
                yield sideEffect
Ejemplo n.º 6
0
 def conclude(self, assumptions):
     from proveit.logic import FALSE
     if isIrreducibleValue(self.lhs) and isIrreducibleValue(self.rhs):
         # prove that two irreducible values are not equal
         return self.lhs.notEqual(self.rhs)
     if self.lhs == FALSE or self.rhs == FALSE:
         try:
             # prove something is not false by proving it to be true
             return self.concludeViaDoubleNegation(assumptions)
         except:
             pass
     if hasattr(self.lhs, 'notEquals') and isIrreducibleValue(self.rhs):
         try:
             return self.lhs.notEqual(self.rhs, assumptions)
         except:
             pass
     try:
         return self.concludeAsFolded(assumptions)
     except:
         return Operation.conclude(assumptions) # try the default (reduction)
Ejemplo n.º 7
0
    def evaluation(self, assumptions=USE_DEFAULTS):
        '''
        If possible, return a KnownTruth of this expression equal to its
        irreducible value.  Checks for an existing evaluation.  If it
        doesn't exist, try some default strategies including a reduction.
        Attempt the Expression-class-specific "doReducedEvaluation"
        when necessary.
        '''
        from proveit.logic import Equals, defaultSimplification, SimplificationError
        from proveit import KnownTruth, ProofFailure
        from proveit.logic.irreducible_value import isIrreducibleValue

        method_called = None
        try:
            # First try the default tricks. If a reduction succesfully occurs,
            # evaluation will be called on that reduction.
            evaluation = defaultSimplification(self.innerExpr(),
                                               mustEvaluate=True,
                                               assumptions=assumptions)
            method_called = defaultSimplification
        except SimplificationError as e:
            # The default failed, let's try the Expression-class specific version.
            try:
                evaluation = self.doReducedEvaluation(assumptions)
                method_called = self.doReducedEvaluation
            except NotImplementedError:
                # We have nothing but the default evaluation strategy to try, and that failed.
                raise e

        if not isinstance(evaluation, KnownTruth) or not isinstance(
                evaluation.expr, Equals):
            msg = ("%s must return an KnownTruth, "
                   "not %s for %s assuming %s" %
                   (method_called, evaluation, self, assumptions))
            raise ValueError(msg)
        if evaluation.lhs != self:
            msg = ("%s must return an KnownTruth "
                   "equality with self on the left side, "
                   "not %s for %s assuming %s" %
                   (method_called, evaluation, self, assumptions))
            raise ValueError(msg)
        if not isIrreducibleValue(evaluation.rhs):
            msg = ("%s must return an KnownTruth "
                   "equality with an irreducible value on the right side, "
                   "not %s for %s assuming %s" %
                   (method_name, evaluation, self, assumptions))
            raise ValueError(msg)
        # Note: No need to store in Equals.evaluations or Equals.simplifications; this
        # is done automatically as a side-effect for proven equalities with irreducible
        # right sides.

        return evaluation
Ejemplo n.º 8
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
Ejemplo n.º 9
0
def reduceOperands(innerExpr,
                   inPlace=True,
                   mustEvaluate=False,
                   assumptions=USE_DEFAULTS):
    '''
    Attempt to return an InnerExpr object that is provably equivalent to
    the given innerExpr but with simplified operands at the 
    inner-expression level. 
    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 is True, the simplified
    operands must be irreducible values (see isIrreducibleValue).
    '''
    # Any of the operands that can be simplified must be replaced with
    # their simplification.
    from proveit import InnerExpr, ExprRange
    assert isinstance(innerExpr, InnerExpr), \
        "Expecting 'innerExpr' to be of type 'InnerExpr'"
    inner = innerExpr.exprHierarchy[-1]
    substitutions = []
    while True:
        allReduced = True
        for operand in inner.operands:
            if (not isIrreducibleValue(operand)
                    and not isinstance(operand, ExprRange)):
                # The operand isn't already irreducible, so try to
                # simplify it.
                if mustEvaluate:
                    operandEval = operand.evaluation(assumptions=assumptions)
                else:
                    operandEval = operand.simplification(
                        assumptions=assumptions)
                if mustEvaluate and not isIrreducibleValue(operandEval.rhs):
                    msg = 'Evaluations expected to be irreducible values'
                    raise EvaluationError(msg, assumptions)
                if operandEval.lhs != operandEval.rhs:
                    # Compose map to replace all instances of the
                    # operand within the inner expression.
                    global_repl = Lambda.globalRepl(inner, operand)
                    lambdaMap = innerExpr.repl_lambda().compose(global_repl)
                    # substitute in the evaluated value
                    if inPlace:
                        subbed = operandEval.subRightSideInto(lambdaMap)
                        innerExpr = InnerExpr(subbed, innerExpr.innerExprPath)
                    else:
                        sub = operandEval.substitution(lambdaMap)
                        innerExpr = InnerExpr(sub.rhs, innerExpr.innerExprPath)
                        substitutions.append(sub)
                    allReduced = False
                    # Start over since there may have been multiple
                    # substitutions:
                    break
        if allReduced: break  # done!
        inner = innerExpr.exprHierarchy[-1]

    if not inPlace and len(substitutions) > 1:
        # When there have been multiple substitutions, apply
        # transtivity over the chain of substitutions to equate the
        # end-points.
        Equals.applyTransitivities(substitutions, assumptions)
    return innerExpr
Ejemplo n.º 10
0
    def conclude(self, assumptions):
        '''
        Attempt to conclude the equality various ways:
        simple reflexivity (x=x), via an evaluation (if one side is an
        irreducible), or via transitivity.
        '''
        from proveit.logic import TRUE, FALSE, Implies, Iff, inBool
        if self.lhs == self.rhs:
            # Trivial x=x
            return self.concludeViaReflexivity()
        if self.lhs or self.rhs in (TRUE, FALSE):
            try:
                # Try to conclude as TRUE or FALSE.
                return self.concludeBooleanEquality(assumptions)
            except ProofFailure:
                pass
        if isIrreducibleValue(self.rhs):
            try:
                evaluation = self.lhs.evaluation(assumptions)
                if evaluation.rhs != self.rhs:
                    raise ProofFailure(
                        self, assumptions,
                        "Does not match with evaluation: %s" % str(evaluation))
                return evaluation
            except EvaluationError as e:
                raise ProofFailure(self, assumptions,
                                   "Evaluation error: %s" % e.message)
        elif isIrreducibleValue(self.lhs):
            try:
                evaluation = self.rhs.evaluation(assumptions)
                if evaluation.rhs != self.lhs:
                    raise ProofFailure(
                        self, assumptions,
                        "Does not match with evaluation: %s" % str(evaluation))
                return evaluation.deriveReversed()
            except EvaluationError as e:
                raise ProofFailure(self, assumptions,
                                   "Evaluation error: %s" % e.message)

        if (Implies(self.lhs, self.rhs).proven(assumptions)
                and Implies(self.rhs, self.lhs).proven(assumptions)
                and inBool(self.lhs).proven(assumptions)
                and inBool(self.rhs).proven(assumptions)):
            # There is mutual implication both sides are known to be
            # boolean.  Conclude equality via mutual implication.
            return Iff(self.lhs, self.rhs).deriveEquality(assumptions)

        if hasattr(self.lhs, 'deduceEquality'):
            # If there is a 'deduceEquality' 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.deduceEquality(self, assumptions)
            if eq.expr != self:
                raise ValueError("'deduceEquality' not implemented "
                                 "correctly; must deduce the 'equality' "
                                 "that it is given if it can: "
                                 "'%s' != '%s'" % (eq.expr, self))
            return eq
        else:
            '''
            If there is no 'deduceEquality' method, we'll try
            simplifying each side to see if they are equal.
            '''
            # Try to prove equality via simplifying both sides.
            lhs_simplification = self.lhs.simplification(assumptions)
            rhs_simplification = self.rhs.simplification(assumptions)
            simplified_lhs = lhs_simplification.rhs
            simplified_rhs = rhs_simplification.rhs
            try:
                if simplified_lhs != self.lhs or simplified_rhs != self.rhs:
                    simplified_eq = Equals(simplified_lhs,
                                           simplified_rhs).prove(assumptions)
                    return Equals.applyTransitivities([
                        lhs_simplification, simplified_eq, rhs_simplification
                    ], assumptions)
            except ProofFailure:
                pass
        raise ProofFailure(
            self, assumptions, "Unable to automatically conclude by "
            "standard means.  To try to prove this via "
            "transitive implication relations, try "
            "'concludeViaTransitivity'.")