def shallow_simplification(self, *, must_evaluate=False, **defaults_config): ''' From this forall statement, evaluate it to TRUE or FALSE if possible by calling the domain's forall_evaluation method ''' from proveit.logic import EvaluationError if must_evaluate and not self.has_domain(): # Cannot automatically evaluate a forall statement with # no domain. raise EvaluationError(self) if hasattr(self, 'instance_param'): if hasattr(self.domain, 'forall_evaluation'): # Use the domain's forall_evaluation method return self.domain.forall_evaluation(self) ''' # Let's not do this fancy stuff just yet. # Evaluate an unbundled version unbundle_eq = self.unbundle_equality() return unbundle_eq.rhs.evaluation() ''' if must_evaluate: raise EvaluationError(self) return OperationOverInstances.shallow_simplification(self)
def forallEvaluation(self, forallStmt, assumptions): ''' Given a forall statement over the BOOLEANS domain, evaluate to TRUE or FALSE if possible. ''' from proveit.logic import Forall, Equals, EvaluationError from ._theorems_ import falseEqFalse, trueEqTrue from ._theorems_ import forallBoolEvalTrue, forallBoolEvalFalseViaTF, forallBoolEvalFalseViaFF, forallBoolEvalFalseViaFT from ._common_ import TRUE, FALSE, Booleans from conjunction import compose assert(isinstance(forallStmt, Forall)), "May only apply forallEvaluation method of BOOLEANS to a forall statement" assert(forallStmt.domain == Booleans), "May only apply forallEvaluation method of BOOLEANS to a forall statement with the BOOLEANS domain" assert(len(forallStmt.instanceVars) == 1), "May only apply forallEvaluation method of BOOLEANS to a forall statement with 1 instance variable" instanceVar = forallStmt.instanceVars[0] instanceExpr = forallStmt.instanceExpr P_op = Operation(P, instanceVar) trueInstance = instanceExpr.substituted({instanceVar:TRUE}) falseInstance = instanceExpr.substituted({instanceVar:FALSE}) if trueInstance == TRUE and falseInstance == FALSE: # special case of Forall_{A in BOOLEANS} A falseEqFalse # FALSE = FALSE trueEqTrue # TRUE = TRUE return forallBoolEvalFalseViaTF.specialize({P_op:instanceExpr}).deriveConclusion() else: # must evaluate for the TRUE and FALSE case separately evalTrueInstance = trueInstance.evaluation(assumptions) evalFalseInstance = falseInstance.evaluation(assumptions) if not isinstance(evalTrueInstance.expr, Equals) or not isinstance(evalFalseInstance.expr, Equals): raise EvaluationError('Quantified instances must produce equalities as evaluations') # proper evaluations for both cases (TRUE and FALSE) trueCaseVal = evalTrueInstance.rhs falseCaseVal = evalFalseInstance.rhs if trueCaseVal == TRUE and falseCaseVal == TRUE: # both cases are TRUE, so the forall over booleans is TRUE compose([evalTrueInstance.deriveViaBooleanEquality(), evalFalseInstance.deriveViaBooleanEquality()], assumptions) forallBoolEvalTrue.specialize({P_op:instanceExpr, A:instanceVar}) return forallBoolEvalTrue.specialize({P_op:instanceExpr, A:instanceVar}, assumptions=assumptions).deriveConclusion(assumptions) else: # one case is FALSE, so the forall over booleans is FALSE compose([evalTrueInstance, evalFalseInstance], assumptions) if trueCaseVal == FALSE and falseCaseVal == FALSE: return forallBoolEvalFalseViaFF.specialize({P_op:instanceExpr, A:instanceVar}, assumptions=assumptions).deriveConclusion(assumptions) elif trueCaseVal == FALSE and falseCaseVal == TRUE: return forallBoolEvalFalseViaFT.specialize({P_op:instanceExpr, A:instanceVar}, assumptions=assumptions).deriveConclusion(assumptions) elif trueCaseVal == TRUE and falseCaseVal == FALSE: return forallBoolEvalFalseViaTF.specialize({P_op:instanceExpr, A:instanceVar}, assumptions=assumptions).deriveConclusion(assumptions) else: raise EvaluationError('Quantified instance evaluations must be TRUE or FALSE')
def evaluation(self, **defaults_config): ''' If possible, return a Judgment of this expression equal to an irreducible value. This Operation.evaluation version simplifies the operands and then calls shallow_simplification with must_evaluat=True. ''' from proveit import UnsatisfiedPrerequisites, ProofFailure from proveit.logic import EvaluationError, SimplificationError # Try to simplify the operands first. reduction = self.simplification_of_operands( simplify_with_known_evaluations=True) # After making sure the operands have been simplified, # try 'shallow_simplification' with must_evaluate=True. try: if reduction.lhs == reduction.rhs: # _no_eval_check is a directive to the @equality_prover wrapper # to tell it not to check for an existing evaluation if we have # already checked. return self.shallow_simplification(must_evaluate=True, _no_eval_check=True) evaluation = reduction.rhs.shallow_simplification( must_evaluate=True) except (SimplificationError, UnsatisfiedPrerequisites, NotImplementedError, ProofFailure): raise EvaluationError(self) return reduction.apply_transitivity(evaluation)
def shallow_simplification(self, *, must_evaluate=False, **defaults_config): ''' For a generic Function expression (e.g., "f(x)"), there is no evaluation strategy. ''' from proveit.logic import EvaluationError if must_evaluate: raise EvaluationError(self) return Operation.shallow_simplification(self)
def doReducedEvaluation(self, assumptions=USE_DEFAULTS, **kwargs): ''' Only handles -0 = 0 or double negation. ''' from proveit.logic import EvaluationError from ._theorems_ import negatedZero from proveit.number import zero if self.operand == zero: return negatedZero if isinstance(self.operand, Neg) and isIrreducibleValue( self.operand.operand): return self.doubleNegSimplification(assumptions) raise EvaluationError(self, assumptions)
def shallow_simplification(self, *, must_evaluate=False, **defaults_config): ''' Proves that this ExprTuple is equal to an ExprTuple with ExprRanges reduced unless these are "preserved" expressions. ''' from proveit.relation import TransRelUpdater from proveit import ExprRange from proveit.logic import is_irreducible_value, EvaluationError expr = self eq = TransRelUpdater(expr) if defaults.preserve_all: # Preserve all sub-expressions -- don't simplify. return eq.relation _k = 0 for entry in self.entries: if isinstance(entry, ExprRange): if entry in defaults.preserved_exprs: if must_evaluate: # An ExprRange is not irreducible. raise EvaluationError(self) # Preserve this entry -- don't simplify it. _k += ExprTuple(entry).num_entries() continue entry_simp = entry.reduction() if must_evaluate and not is_irreducible_value(entry_simp.rhs): raise EvaluationError(self) if entry_simp.lhs != entry_simp.rhs: substitution = expr.substitution( entry_simp, start_idx=_k, preserve_all=True) expr = eq.update(substitution) _k += entry_simp.rhs.num_entries() else: if must_evaluate and not is_irreducible_value(entry): raise EvaluationError(self) _k += 1 return eq.relation
def do_reduced_evaluation(self, assumptions=USE_DEFAULTS, **kwargs): ''' Only handles -0 = 0 or double negation. ''' from proveit.logic import EvaluationError from . import negated_zero from proveit.numbers import zero if self.operand == zero: return negated_zero if isinstance( self.operand, Neg) and is_irreducible_value( self.operand.operand): return self.double_neg_simplification(assumptions) raise EvaluationError(self, assumptions)
def shallow_simplification(self, *, must_evaluate=False, **defaults_config): ''' If the operands that to TRUE or FALSE, we can evaluate this expression as TRUE or FALSE. ''' # IMPORTANT: load in truth-table evaluations from . import implies_t_t, implies_t_f, implies_f_t, implies_f_f try: return Operation.shallow_simplification( self, must_evaluate=must_evaluate) except NotImplementedError: # Should have been able to do the evaluation from the # loaded truth table. # If it can't we are unable to evaluate it. from proveit.logic import EvaluationError raise EvaluationError(self)
def do_reduced_evaluation(self, assumptions=USE_DEFAULTS): ''' For trivial cases, a zero or one exponent or zero or one base, derive and return this exponential expression equated with a evaluated form. Assumptions may be necessary to deduce necessary conditions for the simplification. ''' from proveit.logic import EvaluationError from proveit.numbers import zero, one from . import exp_zero_eq_one, exponentiated_zero, exponentiated_one if self.exponent == zero: return exp_zero_eq_one.instantiate({a: self.base}) # =1 elif self.base == zero: return exponentiated_zero.instantiate({x: self.exponent}) # =0 elif self.base == one: return exponentiated_one.instantiate({x: self.exponent}) # =1 else: raise EvaluationError( 'Only trivial evaluation is implemented ' '(zero or one for the base or exponent).', assumptions)
def shallow_simplification(self, *, must_evaluate=False, **defaults_config): from proveit.logic import TRUE, FALSE, EvaluationError, evaluate_truth from proveit.logic.booleans.negation import (negation_intro, falsified_negation_intro) from . import not_t, not_f # load in truth-table evaluations if self.operand == TRUE: return not_t elif self.operand == FALSE: return not_f elif self.operand.proven(): # evaluate to FALSE via falsified_negation_intro return falsified_negation_intro.instantiate({A: self.operand}) elif self.operand.disproven(): # evaluate to TRUE via falsified_negation_intro return evaluate_truth(negation_intro.instantiate({A: self.operand})) elif must_evaluate: # The simplification of the operands may not have # worked hard enough. Let's work harder if we must # evaluate. evaluated = self.operand.evaluation().rhs if evaluated in (TRUE, FALSE): # All the pieces should be there now. return self.evaluation(automation=True) raise EvaluationError(self) if hasattr(self.operand, 'shallow_simplification_of_negation'): # If the operand has a 'shallow_simplification_of_negation', # use that. return self.operand.shallow_simplification_of_negation() # May now be able to evaluate via loaded truth tables. return Operation.shallow_simplification(self)
def wrapper(*args, **kwargs): ''' The wrapper for the equality_prover decorator. ''' from proveit._core_.expression.expr import Expression from proveit.logic import Equals, TRUE, EvaluationError # Obtain the original Expression to be on the left side # of the resulting equality Judgment. _self = args[0] if isinstance(_self, Expression): expr = _self elif hasattr(_self, 'expr'): expr = _self.expr else: raise TypeError("@equality_prover, %s, expected to be a " "method for an Expression type or it must " "have an 'expr' attribute." % func) proven_truth = None if is_simplification_method or is_evaluation_method: from proveit.logic import is_irreducible_value if is_irreducible_value(expr): # Already irreducible. Done. proven_truth = (Equals(expr, expr).conclude_via_reflexivity()) # If _no_eval_check is set to True, don't bother # checking for an existing evaluation. Used internally # in Operation.simplification, Operation.evaluation, # Conditional.simplification, and Judgment.simplify. _no_eval_check = kwargs.pop('_no_eval_check', False) if (not _no_eval_check and (is_evaluation_method or (defaults.simplify_with_known_evaluations and is_simplification_method))): from proveit.logic import evaluate_truth if expr.proven(): # The expression is proven so it equals true. proven_truth = Equals(expr, TRUE).conclude_boolean_equality() else: # See if there is a known evaluation (or if one may # be derived via known equalities if # defaults.automation is enabled). if 'assumptions' in kwargs: # Use new assumptions temporarily. with defaults.temporary() as tmp_defaults: tmp_defaults.assumptions = kwargs.get( 'assumptions') if expr.proven(): # expr is proven, so it evaluates # to TRUE. proven_truth = evaluate_truth(expr) else: proven_truth = Equals.get_known_evaluation( expr) else: proven_truth = Equals.get_known_evaluation(expr) # For an 'evaluation' or 'simplification', we should # force auto_simplify on and preserve_all off to # simplify as much as possible. kwargs['auto_simplify'] = True kwargs['preserve_all'] = False if proven_truth is None: proven_truth = decorated_relation_prover(*args, **kwargs) proven_expr = proven_truth.expr if not isinstance(proven_expr, Equals): raise TypeError("@equality_prover, %s, expected to prove an " "Equals expression, not %s of type %s." % (func, proven_expr, proven_expr.__class__)) if is_evaluation_method or (is_shallow_simplification_method and kwargs.get('must_evaluate', False) == True): # The right side of an evaluation must be irreducible. from proveit.logic import is_irreducible_value if not is_irreducible_value(proven_expr.rhs): raise EvaluationError(_self) return proven_truth
def doReducedEvaluation(self, assumptions=USE_DEFAULTS, **kwargs): ''' Derive and return this multiplication expression equated with an irreducible value. Handle the trivial case of a zero factor or do pairwise evaluation after simplifying negations and eliminating one factors. ''' from ._theorems_ import multZeroLeft, multZeroRight, multZeroAny from proveit.logic import isIrreducibleValue, EvaluationError from proveit.number import zero # First check for any zero factors -- quickest way to do an evaluation. try: zeroIdx = self.operands.index(zero) if len(self.operands) == 2: if zeroIdx == 0: return multZeroLeft.specialize({x: self.operands[1]}, assumptions=assumptions) else: return multZeroRight.specialize({x: self.operands[0]}, assumptions=assumptions) _a = self.operands[:zeroIdx] _b = self.operands[zeroIdx + 1:] _i = ExprTuple(*_a.length(assumptions)) _j = ExprTuple(*_b.length(assumptions)) return multZeroAny.specialize({ i: _i, j: _j, a: _a, b: _b }, assumptions=assumptions) except (ValueError, ProofFailure): pass # No such "luck" regarding a simple multiplication by zero. expr = self # A convenience to allow successive update to the equation via transitivities. # (starting with self=self). eq = TransRelUpdater(self, assumptions) # Simplify negations -- factor them out. expr = eq.update(expr.negSimplifications(assumptions)) if not isinstance(expr, Mult): # The expression may have changed to a negation after doing # negSimplification. Start the simplification of this new # expression fresh at this point. eq.update(expr.evaluation(assumptions)) return eq.relation # Eliminate any factors of one. expr = eq.update(expr.oneEliminations(assumptions)) if isIrreducibleValue(expr): return eq.relation # done if len(self.operands) > 2: eq.update(pairwiseEvaluation(expr, assumptions)) return eq.relation raise EvaluationError(self, assumptions)
def shallow_simplification(self, *, must_evaluate=False, **defaults_config): ''' Attempt to determine whether this disjunction, with simplified operands, evaluates to TRUE or FALSE under the given assumptions. If all operands have simplified to FALSE, the disjunction is FALSE. If any of the operands have simplified to TRUE, the disjunction may be TRUE (if the other operands are provably Boolean). If it can't be evaluated, and must_evaluate is False, ungroup nested disjunctions if that is an active simplification direction. Also, if applicable, perform a unary reduction: Or(A) = A. ''' from proveit.logic import (Equals, FALSE, TRUE, EvaluationError, is_irreducible_value) # load in truth-table evaluations from . import or_t_t, or_t_f, or_f_t, or_f_f if self.operands.num_entries() == 0: from proveit.logic.booleans.disjunction import \ empty_disjunction_eval # Or() = FALSE return empty_disjunction_eval # Check whether or not all of the operands are FALSE # or any are TRUE. all_are_false = True for operand in self.operands: if operand != FALSE: all_are_false = False if operand == TRUE: # If any simplified operand is TRUE, the disjunction # may only evaluate to TRUE if it can be evaluated. # Only use automation here if 'must_evaluate' is True. self.conclude(automation=must_evaluate) return Equals(self, TRUE).prove() # If all of the operands are FALSE, we can prove that the # conjunction is equal to FALSE. if all_are_false: self.conclude_negation() return Equals(self, FALSE).prove() if must_evaluate: if not all( is_irreducible_value(operand) for operand in self.operands): # The simplification of the operands may not have # worked hard enough. Let's work harder if we # must evaluate. for operand in self.operands: if not is_irreducible_value(operand): operand.evaluation() return self.evaluation() # Can't evaluate the conjunction if no operand was # FALSE but they aren't all TRUE. raise EvaluationError(self) if self.operands.is_single(): # Or(A) = A return self.unary_reduction() expr = self # for convenience updating our equation eq = TransRelUpdater(expr) if Or._simplification_directives_.ungroup: # ungroup the expression (disassociate nested disjunctions). _n = 0 length = expr.operands.num_entries() - 1 # loop through all operands while _n < length: operand = expr.operands[_n] if isinstance(operand, Or): # if it is grouped, ungroup it expr = eq.update( expr.disassociation(_n, auto_simplify=False)) length = expr.operands.num_entries() _n += 1 return Expression.shallow_simplification(self)
def shallow_simplification(self, *, must_evaluate=False, **defaults_config): ''' Attempt to determine whether this conjunction, with simplified operands, evaluates to TRUE or FALSE under the given assumptions. If all operands have simplified to TRUE, the conjunction is TRUE. If any of the operands have simplified to FALSE, the conjunction may be FALSE (if the other operands are provably Boolean). If it can't be evaluated, and must_evaluate is False, ungroup nested conjunctions if that is an active simplification direction. Also, if applicable, perform a unary reduction: And(A) = A. ''' from proveit import ExprRange from proveit.logic import (Equals, FALSE, TRUE, EvaluationError, is_irreducible_value) # load in truth-table evaluations from . import (and_t_t, and_t_f, and_f_t, and_f_f, conjunction_eq_quantification) if self.operands.num_entries() == 0: from proveit.logic.booleans.conjunction import \ empty_conjunction_eval # And() = TRUE return empty_conjunction_eval # Check whether or not all of the operands are TRUE # or any are FALSE. all_are_true = True for operand in self.operands: if operand != TRUE: all_are_true = False if operand == FALSE: # If any simplified operand is FALSE, the conjunction # may only evaluate to FALSE if it can be evaluated. # Only use automation here if 'must_evaluate' is True. self.conclude_negation(automation=must_evaluate) return Equals(self, FALSE).prove() # If all of the operands are TRUE, we can prove that the # conjunction is equal to TRUE. if all_are_true: self.conclude() return Equals(self, TRUE).prove() if must_evaluate: if self.operands.contains_range(): if self.operands.num_entries() == 1: # Conjunction of a single ExprRange. Convert # to a universal quantification and evaluate that. expr_range = self.operands[0] _i = expr_range.true_start_index _j = expr_range.true_end_index _P = expr_range.lambda_map conj_eq_quant = (conjunction_eq_quantification.instantiate( { i: _i, j: _j, P: _P }, preserve_all=True)) return conj_eq_quant.apply_transitivity( conj_eq_quant.rhs.evaluation()) return prove_via_grouping_ranges( self, lambda expr, **kwargs: expr.evaluation(**kwargs)) if not all( is_irreducible_value(operand) for operand in self.operands): # The simplification of the operands may not have # worked hard enough. Let's work harder if we # must evaluate. for operand in self.operands: if not is_irreducible_value(operand): operand.evaluation() return self.evaluation() # Can't evaluate the conjunction if no operand was # FALSE but they aren't all TRUE. raise EvaluationError(self) if self.operands.is_single(): # And(A) = A return self.unary_reduction() expr = self # for convenience updating our equation eq = TransRelUpdater(expr) if And._simplification_directives_.ungroup: # ungroup the expression (disassociate nested conjunctions). _n = 0 length = expr.operands.num_entries() - 1 # loop through all operands while _n < length: operand = expr.operands[_n] if isinstance(operand, And): # if it is grouped, ungroup it expr = eq.update( expr.disassociation(_n, auto_simplify=False)) length = expr.operands.num_entries() _n += 1 return Expression.shallow_simplification(self)
def range_expansion(self, **defaults_config): ''' For self an ExprTuple with a single entry that is an ExprRange of the form f(i),...,f(j), where 0 <= (j-i) <= 9 (i.e. the ExprRange represents 1 to 10 elements), derive and return an equality between self and an ExprTuple with explicit entries replacing the ExprRange. For example, if self = ExprTuple(f(3),...,f(6)), then self.range_expansion() would return: |- ExprTuple(f(3),...,f(6)) = ExprTuple(f(3), f(4), f(5), f(6)) ''' # Check that we have a an ExprTuple with # (1) a single entry # (2) and the single entry is an ExprRange # (these restrictions can be relaxed later to make the # method more general) # ExprTuple with single entry if not self.num_entries() == 1: raise ValueError( "ExprTuple.range_expansion() implemented only for " "ExprTuples with a single entry (and the single " "entry must be an ExprRange). Instead, the ExprTuple " "{0} has {1} entries.".format(self, self.num_entries)) # and the single entry is an ExprRange: from proveit import ExprRange if not isinstance(self.entries[0], ExprRange): raise ValueError( "ExprTuple.range_expansion() implemented only for " "ExprTuples with a single entry (and the single " "entry must be an ExprRange). Instead, the ExprTuple " "is {0}.".format(self)) from proveit import Function from proveit.logic import EvaluationError from proveit.numbers import subtract _the_expr_range = self[0] # _n = self.num_elements() try: _n = subtract(self[0].true_end_index, self[0].true_start_index).evaluated() except EvaluationError as the_error: _diff = subtract(self[0].true_end_index, self[0].true_start_index) print("EvaluationError: {0}. The ExprRange {1} must represent " "a known, finite number of elements, but all we know is " "that it represents {2} elements.".format( the_error, self[0], _diff)) raise EvaluationError( subtract(self[0].true_end_index, self[0].true_start_index)) _n = _n.as_int() + 1 # actual number of elems being represented if not (1 <= _n and _n <= 9): raise ValueError( "ExprTuple.range_expansion() implemented only for " "ExprTuples with a single entry, with the single " "entry being an ExprRange representing a finite " "number of elements n with 1 <= n <= 9. Instead, " "the ExprTuple is {0} with number of elements equal " "to {1}.".format(self[0], _n)) # id the correct theorem for the number of entries import proveit.numbers.numerals.decimals expansion_thm = proveit.numbers.numerals.decimals\ .__getattr__('range_%d_expansion' % _n) # instantiate and return the identified expansion theorem _f, _i, _j = expansion_thm.instance_vars _safe_var = self.safe_dummy_var() _idx_param = _the_expr_range.parameter _fxn_sub = _the_expr_range.body.basic_replaced( {_idx_param: _safe_var}) _i_sub = _the_expr_range.true_start_index _j_sub = _the_expr_range.true_end_index return expansion_thm.instantiate( {Function(_f, _safe_var): _fxn_sub, _i: _i_sub, _j: _j_sub})