Example #1
0
    def conclude(self, **defaults_config):
        from proveit.logic import FALSE, Not, evaluation_or_simplification
        if is_irreducible_value(self.lhs) and is_irreducible_value(self.rhs):
            # prove that two irreducible values are not equal
            return self.lhs.not_equal(self.rhs)
        if self.lhs == FALSE or self.rhs == FALSE:
            try:
                # prove something is not false by proving it to be true
                return self.conclude_via_double_negation()
            except BaseException:
                pass
        if Not(Equals(self.lhs, self.rhs)).proven():
            # Conclude (x ≠ y) by knowing that Not(x = y) is true.
            return self.conclude_as_folded()

        # Try the standard Relation strategies -- evaluate or
        # simplify both sides.
        try:
            return Relation.conclude(self)
        except ProofFailure:
            # Both sides are already irreducible or simplified.
            pass

        if hasattr(self.lhs, 'not_equal'):
            # If there is a 'not_equal' method, use that.
            # The responsibility then shifts to that method for
            # determining what strategies should be attempted
            # (with the recommendation that it should not attempt
            # multiple non-trivial automation strategies).
            # A good practice is to try the 'conclude_as_folded'
            # strategy if it doesn't fall into any specially-handled
            # case.
            return self.lhs.not_equal(self.rhs)

        return self.conclude_as_folded()
Example #2
0
 def conclude(self, assumptions):
     from proveit.logic import FALSE
     if is_irreducible_value(self.lhs) and is_irreducible_value(self.rhs):
         # prove that two irreducible values are not equal
         return self.lhs.not_equal(self.rhs, assumptions)
     if self.lhs == FALSE or self.rhs == FALSE:
         try:
             # prove something is not false by proving it to be true
             return self.conclude_via_double_negation(assumptions)
         except BaseException:
             pass
     if hasattr(self.lhs, 'not_equal') and is_irreducible_value(self.rhs):
         try:
             return self.lhs.not_equal(self.rhs, assumptions)
         except BaseException:
             pass
     if hasattr(self.lhs, 'deduce_not_equal'):
         # If there is a 'deduce_not_equal' method, use that.
         # The responsibility then shifts to that method for
         # determining what strategies should be attempted
         # (with the recommendation that it should not attempt
         # multiple non-trivial automation strategies).
         eq = self.lhs.deduce_not_equal(self, assumptions)
         if eq.expr != self:
             raise ValueError("'deduce_not_equal' not implemented "
                              "correctly; must deduce the 'inequality' "
                              "that it is given if it can: "
                              "'%s' != '%s'" % (eq.expr, self))
         return eq
     try:
         return self.conclude_as_folded(assumptions)
     except BaseException:
         # try the default (reduction)
         return Operation.conclude(assumptions)
Example #3
0
 def evaluation(self, assumptions=USE_DEFAULTS, automation=True):
     '''
     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 automation:
         if self.lhs == self.rhs:
             # prove equality is true by reflexivity
             return evaluate_truth(self.prove().expr, assumptions=[])
         if is_irreducible_value(self.lhs) and is_irreducible_value(
                 self.rhs):
             # Irreducible values must know how to evaluate the equality
             # between each other, where appropriate.
             return self.lhs.eval_equality(self.rhs)
         return TransitiveRelation.evaluation(self, assumptions)
     return Operation.evaluation(self, assumptions, automation)
Example #4
0
    def side_effects(self, judgment):
        '''
        Record the judgment in Equals.known_equalities, 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
        is_irreducible_value), 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 is_irreducible_value),
        also record the judgment 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.booleans import TRUE, FALSE
        Equals.known_equalities.setdefault(self.lhs, set()).add(judgment)
        Equals.known_equalities.setdefault(self.rhs, set()).add(judgment)

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

        if (self.lhs != self.rhs):
            # automatically derive the reversed form which is equivalent
            yield self.derive_reversed
        if self.rhs == FALSE:
            try:
                self.lhs.prove(automation=False)
                # derive FALSE given lhs=FALSE and lhs.
                yield self.derive_contradiction
            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.derive_contradiction
        if self.rhs in (TRUE, FALSE):
            # automatically derive A from A=TRUE or Not(A) from A=FALSE
            yield self.derive_via_boolean_equality
        if hasattr(self.lhs, 'equality_side_effects'):
            for side_effect in self.lhs.equality_side_effects(judgment):
                yield side_effect
Example #5
0
def default_simplification(inner_expr,
                           in_place=False,
                           must_evaluate=False,
                           operands_only=False,
                           assumptions=USE_DEFAULTS,
                           automation=True):
    '''
    Default attempt to simplify the given inner expression under the
    given assumptions.  If successful, returns a Judgment (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 in_place is True, the top-level expression must be a Judgment
    and the simplified Judgment is derived instead of an equivalence
    relation.
    If must_evaluate=True, the simplified form must be an irreducible
    value (see is_irreducible_value).  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 Judgment.evaluation and
    evaluate_truth].  If operands_only = 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.checked_assumptions(assumptions)

    from proveit.logic import TRUE, FALSE
    from proveit.logic.booleans import true_axiom
    top_level = inner_expr.expr_hierarchy[0]
    inner = inner_expr.expr_hierarchy[-1]
    if operands_only:
        # Just do the reduction of the operands at the level below the
        # "inner expression"
        reduced_inner_expr = reduce_operands(inner_expr, in_place,
                                             must_evaluate, assumptions)
        if in_place:
            try:
                return reduced_inner_expr.expr_hierarchy[0].prove(
                    assumptions, automation=False)
            except BaseException:
                assert False
        try:
            eq = Equals(top_level, reduced_inner_expr.expr_hierarchy[0])
            return eq.prove(assumptions, automation=False)
        except BaseException:
            assert False

    def inner_simplification(inner_equivalence):
        if in_place:
            return inner_equivalence.sub_right_side_into(
                inner_expr, assumptions=assumptions)
        return inner_equivalence.substitution(inner_expr,
                                              assumptions=assumptions)

    if is_irreducible_value(inner):
        return Equals(inner, inner).prove()
    assumptions_set = set(defaults.checked_assumptions(assumptions))

    # See if the expression is already known to be true as a special
    # case.
    try:
        inner.prove(assumptions_set, automation=False)
        true_eval = evaluate_truth(inner, assumptions_set)  # A=TRUE given A
        if inner == top_level:
            if in_place:
                return true_axiom
            else:
                return true_eval
        return inner_simplification(true_eval)
    except BaseException:
        pass
    # See if the negation of the expression is already known to be true
    # as a special case.
    try:
        inner.disprove(assumptions_set, automation=False)
        false_eval = evaluate_falsehood(
            inner, assumptions_set)  # A=FALSE given Not(A)
        return inner_simplification(false_eval)
    except BaseException:
        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 (must_evaluate and inner in Equals.known_evaluation_sets):
        evaluations = Equals.known_evaluation_sets[inner]
        candidates = []
        for judgment in evaluations:
            if judgment.is_sufficient(assumptions_set):
                # Found existing evaluation suitable for the assumptions
                candidates.append(judgment)
        if len(candidates) >= 1:
            # Return the "best" candidate with respect to fewest number
            # of steps.
            def min_key(judgment):
                return judgment.proof().num_steps()

            simplification = min(candidates, key=min_key)
            return inner_simplification(simplification)

    elif (not must_evaluate
          and known_simplifications_key in Equals.known_simplifications):
        simplification = Equals.known_simplifications[
            known_simplifications_key]
        if simplification.is_usable():
            return inner_simplification(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.known_equalities:
        for known_eq in Equals.known_equalities[inner]:
            try:
                if known_eq.is_sufficient(assumptions_set):
                    if in_place:
                        # Should first substitute in the known
                        # equivalence then simplify that.
                        if inner == known_eq.lhs:
                            known_eq.sub_right_side_into(
                                inner_expr, assumptions)
                        elif inner == known_eq.rhs:
                            known_eq.sub_left_side_into(
                                inner_expr, assumptions)
                    # Use must_evaluate=True.  Simply being equal to
                    # something simplified isn't necessarily the
                    # appropriate simplification for "inner" itself.
                    alt_inner = known_eq.other_side(inner).inner_expr()
                    equiv_simp = \
                        default_simplification(alt_inner, in_place=in_place,
                                               must_evaluate=True,
                                               assumptions=assumptions,
                                               automation=False)
                    if in_place:
                        # Returns Judgment with simplification:
                        return equiv_simp
                    inner_equiv = known_eq.apply_transitivity(
                        equiv_simp, assumptions)
                    if inner == top_level:
                        return inner_equiv
                    return inner_equiv.substitution(inner_expr,
                                                    assumptions=assumptions)
            except SimplificationError:
                pass
    # try to simplify via reduction
    if not isinstance(inner, Operation):
        if must_evaluate:
            raise EvaluationError('Unknown evaluation: ' + str(inner),
                                  assumptions)
        else:
            # don't know how to simplify, so keep it the same
            return inner_simplification(Equals(inner, inner).prove())
    reduced_inner_expr = reduce_operands(inner_expr, in_place, must_evaluate,
                                         assumptions)
    if reduced_inner_expr == inner_expr:
        if must_evaluate:
            # 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 = reduced_inner_expr.expr_hierarchy[-1]
    if must_evaluate:
        inner_equiv = inner.evaluation(assumptions)
    else:
        inner_equiv = inner.simplification(assumptions)
    value = inner_equiv.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:
            evaluate_truth(inner, assumptions)
        except BaseException:
            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:
            evaluate_falsehood(inner, assumptions)
        except BaseException:
            pass
    reduced_simplification = inner_simplification(inner_equiv)
    if in_place:
        simplification = reduced_simplification
    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 = reduced_inner_expr.expr_hierarchy[0]
        eq1 = Equals(top_level, reduced_top_level)
        eq1.prove(assumptions, automation=False)
        eq2 = Equals(reduced_top_level, reduced_simplification.rhs)
        eq2.prove(assumptions, automation=False)
        simplification = eq1.apply_transitivity(eq2, assumptions)
    if not in_place and top_level == inner:
        # Store direct simplifications in the known_simplifications
        # dictionary for next time.
        Equals.known_simplifications[
            known_simplifications_key] = simplification
        if is_irreducible_value(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(top_level,
                                                    set()).add(simplification)
    return simplification
Example #6
0
def reduce_operands(inner_expr,
                    in_place=True,
                    must_evaluate=False,
                    assumptions=USE_DEFAULTS):
    '''
    Attempt to return an InnerExpr object that is provably equivalent to
    the given inner_expr but with simplified operands at the
    inner-expression level.
    If in_place is True, the top-level expression must be a Judgment
    and the simplified Judgment is derived instead of an equivalence
    relation.
    If must_evaluate is True, the simplified
    operands must be irreducible values (see is_irreducible_value).
    '''
    # Any of the operands that can be simplified must be replaced with
    # their simplification.
    from proveit import InnerExpr, ExprRange
    assert isinstance(inner_expr, InnerExpr), \
        "Expecting 'inner_expr' to be of type 'InnerExpr'"
    inner = inner_expr.expr_hierarchy[-1]
    substitutions = []
    while True:
        all_reduced = True
        for operand in inner.operands:
            if (not is_irreducible_value(operand)
                    and not isinstance(operand, ExprRange)):
                # The operand isn't already irreducible, so try to
                # simplify it.
                if must_evaluate:
                    operand_eval = operand.evaluation(assumptions=assumptions)
                else:
                    operand_eval = operand.simplification(
                        assumptions=assumptions)
                if must_evaluate and not is_irreducible_value(
                        operand_eval.rhs):
                    msg = 'Evaluations expected to be irreducible values'
                    raise EvaluationError(msg, assumptions)
                if operand_eval.lhs != operand_eval.rhs:
                    # Compose map to replace all instances of the
                    # operand within the inner expression.
                    global_repl = Lambda.global_repl(inner, operand)
                    lambda_map = inner_expr.repl_lambda().compose(global_repl)
                    # substitute in the evaluated value
                    if in_place:
                        subbed = operand_eval.sub_right_side_into(lambda_map)
                        inner_expr = InnerExpr(subbed,
                                               inner_expr.inner_expr_path)
                    else:
                        sub = operand_eval.substitution(lambda_map)
                        inner_expr = InnerExpr(sub.rhs,
                                               inner_expr.inner_expr_path)
                        substitutions.append(sub)
                    all_reduced = False
                    # Start over since there may have been multiple
                    # substitutions:
                    break
        if all_reduced:
            break  # done!
        inner = inner_expr.expr_hierarchy[-1]

    if not in_place and len(substitutions) > 1:
        # When there have been multiple substitutions, apply
        # transtivity over the chain of substitutions to equate the
        # end-points.
        Equals.apply_transitivities(substitutions, assumptions)
    return inner_expr
Example #7
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). Use conclude_via_transitivity for transitivity cases.
        '''
        from proveit.logic import TRUE, FALSE, Implies, Iff, in_bool
        if self.lhs == self.rhs:
            # Trivial x=x
            return self.conclude_via_reflexivity()
        if (self.lhs in (TRUE, FALSE)) or (self.rhs in (TRUE, FALSE)):
            try:
                # Try to conclude as TRUE or FALSE.
                return self.conclude_boolean_equality(assumptions)
            except ProofFailure:
                pass
        if is_irreducible_value(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 is_irreducible_value(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.derive_reversed()
            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 in_bool(self.lhs).proven(assumptions)
                and in_bool(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).derive_equality(assumptions)

        if hasattr(self.lhs, 'deduce_equality'):
            # If there is a 'deduce_equality' method, use that.
            # The responsibility then shifts to that method for
            # determining what strategies should be attempted
            # (with the recommendation that it should not attempt
            # multiple non-trivial automation strategies).
            eq = self.lhs.deduce_equality(self, assumptions)
            if eq.expr != self:
                raise ValueError("'deduce_equality' 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 'deduce_equality' 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.apply_transitivities([
                        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 relations, try "
            "'conclude_via_transitivity'.")