def total_ordering(*relations): ''' Return a conjunction of relations in the total ordering style. For example, "a > b >= c = d > e" is a total ordering style for "(a > b) and (b >= c) and (c = d) and (d > e)". ''' from proveit.logic import And conjunction = And(*relations) conjunction = conjunction.with_total_ordering_style() if conjunction.operands.is_single(): # A single relation is a trivial total ordering. return conjunction.operands[0] return conjunction
def __init__(self, value, condition_or_conditions): ''' Create a Conditional with the given particular value and the given condition. If multiple conditions are provided, these will be wrapped in a conjunction internally. However, if the 'condition_delimiter' style is set to 'comma', the conditions will be displayed in a comma delimited fashion if it is within a conjunction. ''' from proveit._core_.expression.composite import \ single_or_composite_expression, Composite, ExprTuple, ExprRange value = single_or_composite_expression(value) assert (isinstance(value, Expression) and not isinstance(value, ExprRange)) condition_or_conditions = \ single_or_composite_expression(condition_or_conditions) if isinstance(condition_or_conditions, ExprTuple): if is_single(condition_or_conditions): condition = condition_or_conditions[0] else: # Must wrap a tuple of conditions in a conjunction. from proveit.logic import And condition = And(*condition_or_conditions.entries) else: condition = condition_or_conditions assert (isinstance(condition, Expression) and not isinstance(condition, Composite)) Expression.__init__(self, ['Conditional'], (value, condition)) self.value = value self.condition = condition
def _equality_replaced_sub_exprs(self, recursion_fn, *, requirements, stored_replacements): ''' Helper for Conditional._auto_simplified_sub_exprs and Expression._manual_equality_replaced. The 'recursion_fn' allows this to satisfy both roles. ''' from proveit.logic import And # Add the 'condition' as an assumption for the 'value' scope. # If there are multiple conditions in a conjunction, add them # as assumptions one by one and allow them to be used for # subsequent conditions. if isinstance(self.condition, And): conditions = self.condition.operands.entries else: conditions = [self.condition] # For each condition, we'll assume the previous substituted # conditions. subbed_conds = [] prev_assumptions = defaults.assumptions inner_stored_repls = stored_replacements for _k, cond in enumerate(conditions): inner_assumptions = (defaults.assumptions + tuple(subbed_conds)) with defaults.temporary() as temp_defaults: temp_defaults.assumptions = inner_assumptions if defaults.assumptions != prev_assumptions: # Since the assumptions have changed, we can no # longer use the stored_replacements from before. inner_stored_repls = dict() prev_assumptions = defaults.assumptions subbed_conds.append( recursion_fn(cond, requirements=requirements, stored_replacements=inner_stored_repls)) # For the value, we'll assume all of the substituted conditions. inner_assumptions = (defaults.assumptions + tuple(subbed_conds)) with defaults.temporary() as temp_defaults: temp_defaults.assumptions = inner_assumptions if defaults.assumptions != prev_assumptions: # Since the assumptions have changed, we can no longer # use the stored_replacements from before. inner_stored_repls = dict() subbed_val = recursion_fn(self.value, requirements=requirements, stored_replacements=inner_stored_repls) if len(subbed_conds) == 1: return (subbed_val, subbed_conds[0]) else: subbed_condition = And(*subbed_conds) if subbed_condition != self.condition: # We can replace the altered subbed condition # conjunction; use the original assumptions and # stored replacements. subbed_condition = recursion_fn( subbed_condition, requirements=requirements, stored_replacements=stored_replacements) return (subbed_val, subbed_condition)
def effectiveCondition(self): ''' Return the effective 'condition' of the OperationOverInstances. If there is no 'condition', return And operating on zero operands. ''' if hasattr(self, 'condition'): return self.condition else: from proveit.logic import And return And()
def negationSideEffects(self, knownTruth): ''' Side-effect derivations to attempt automatically for Not(A or B or .. or .. Z). ''' from proveit.logic import Not, And yield self.deriveInBool # A or B or .. or .. Z in Booleans if len(self.operands) == 2: # Not(A or B) yield self.deduceNotLeftIfNeither # Not(A) yield self.deduceNotRightIfNeither # Not(B) # implemented by JML on 7/2/19 # If all of the operands are negated call the conjunction form of DeMorgan's if all(isinstance(operand, Not) for operand in self.operands): demorganAnd = And(*[operand.operand for operand in self.operands]) yield demorganAnd.concludeViaDemorgans
def total_ordering(*relations, prove=False): ''' Return a conjunction of relations in the total ordering style. For example, "a > b >= c = d > e" is a total ordering style for "(a > b) and (b >= c) and (c = d) and (d > e)". If there is a single relation, just return the relation. If 'prove' is True, return a proven Judgment. ''' from proveit import ExprRange from proveit.logic import And if len(relations) == 1 and not isinstance(relations[0], ExprRange): # Return a trivial, singular relation. relation = relations[0] if prove: relation = relation.prove() return relation # Return a conjunction with the proper style. conjunction = And(*relations) conjunction = conjunction.with_total_ordering_style() if prove: # Prove via composition. # Allow automation to prove the length requirement. return conjunction.conclude_via_composition(automation=True) return conjunction
def negation_side_effects(self, judgment): ''' Side-effect derivations to attempt automatically for Not(A or B or .. or .. Z). ''' from proveit.logic import Not, And if self.operands.num_entries() == 0: return # No side-effects needed for [Or]() yield self.derive_in_bool # A or B or .. or .. Z in Boolean if self.operands.is_double(): # Not(A or B) yield self.deduce_not_left_if_neither # Not(A) yield self.deduce_not_right_if_neither # Not(B) # implemented by JML on 7/2/19 # If all of the operands are negated call the conjunction form of # DeMorgan's if all(isinstance(operand, Not) for operand in self.operands): demorgan_and = And(*[operand.operand for operand in self.operands]) yield demorgan_and.conclude_via_demorgans
def foldAsForall(self, forallStmt, assumptions=USE_DEFAULTS): ''' Given forall_{A in Booleans} P(A), conclude and return it from [P(TRUE) and P(FALSE)]. ''' from proveit.logic import Forall, And from ._theorems_ import foldForallOverBool, foldConditionedForallOverBool from ._common_ import Booleans assert ( isinstance(forallStmt, Forall) ), "May only apply foldAsForall method of Booleans to a forall statement" assert ( forallStmt.domain == Booleans ), "May only apply foldAsForall method of Booleans to a forall statement with the Booleans domain" if (len(forallStmt.conditions) > 1): if len(forallStmt.conditions) == 2: condition = forallStmt.conditions[1] else: condition = And(*forallStmt.conditions[1:]) Qx = Operation(Q, forallStmt.instanceVar) _Qx = condition Px = Operation(P, forallStmt.instanceVar) _Px = forallStmt.instanceExpr _A = forallStmt.instanceVar return foldConditionedForallOverBool.instantiate( { Qx: _Qx, Px: _Px, A: _A }, num_forall_eliminations=1, assumptions=assumptions) else: # forall_{A in Booleans} P(A), assuming P(TRUE) and P(FALSE) Px = Operation(P, forallStmt.instanceVar) _Px = forallStmt.instanceExpr _A = forallStmt.instanceVar return foldForallOverBool.instantiate({ Px: _Px, A: _A }, num_forall_eliminations=1, assumptions=assumptions)
def fold_as_forall(self, forall_stmt, assumptions=USE_DEFAULTS): ''' Given forall_{A in Boolean} P(A), conclude and return it from [P(TRUE) and P(FALSE)]. ''' from proveit.logic import Forall, And from . import fold_forall_over_bool, fold_conditioned_forall_over_bool from . import Boolean assert ( isinstance(forall_stmt, Forall) ), "May only apply fold_as_forall method of Boolean to a forall statement" assert ( forall_stmt.domain == Boolean ), "May only apply fold_as_forall method of Boolean to a forall statement with the Boolean domain" if forall_stmt.conditions.num_entries() > 1: if forall_stmt.conditions.is_double(): condition = forall_stmt.conditions[1] else: condition = And(*forall_stmt.conditions[1:].entries) Qx = Operation(Q, forall_stmt.instance_var) _Qx = condition Px = Operation(P, forall_stmt.instance_var) _Px = forall_stmt.instance_expr _A = forall_stmt.instance_var return fold_conditioned_forall_over_bool.instantiate( { Qx: _Qx, Px: _Px, A: _A }, num_forall_eliminations=1, assumptions=assumptions) else: # forall_{A in Boolean} P(A), assuming P(TRUE) and P(FALSE) Px = Operation(P, forall_stmt.instance_param) _Px = forall_stmt.instance_expr _A = forall_stmt.instance_param return fold_forall_over_bool.instantiate({ Px: _Px, A: _A }, num_forall_eliminations=1, assumptions=assumptions)
def prove_by_cases(self, forall_stmt, **defaults_config): ''' Given forall_{A in Boolean} P(A), conclude and return it from [P(TRUE) and P(FALSE)]. ''' from proveit.logic import Forall, And from . import forall_over_bool_by_cases, conditioned_forall_over_bool_by_cases from . import Boolean assert(isinstance(forall_stmt, Forall)), ( "May only apply prove_by_cases method of Boolean to a " "forall statement") assert(forall_stmt.domain == Boolean), ( "May only apply prove_by_cases method of Boolean " "to a forall statement with the Boolean domain") if forall_stmt.conditions.num_entries() > 1: if forall_stmt.conditions.is_double(): condition = forall_stmt.conditions[1] else: condition = And(*forall_stmt.conditions[1:].entries) Qx = Function(Q, forall_stmt.instance_param) _Qx = condition Px = Function(P, forall_stmt.instance_param) _Px = forall_stmt.instance_expr _A = forall_stmt.instance_param # We may need to auto-simplify in order to flatten the # conditions (if there are multiple conditions beyond the # domain condition), but we must preserve the different # parts. preserved_exprs = {forall_stmt, forall_stmt.instance_expr} preserved_exprs.update(forall_stmt.conditions) return conditioned_forall_over_bool_by_cases.instantiate( {Qx: _Qx, Px: _Px, A: _A}, num_forall_eliminations=1, preserved_exprs=preserved_exprs, auto_simplify=True) else: # forall_{A in Boolean} P(A), assuming P(TRUE) and P(FALSE) Px = Function(P, forall_stmt.instance_param) _Px = forall_stmt.instance_expr _A = forall_stmt.instance_param return forall_over_bool_by_cases.instantiate( {Px: _Px, A: _A}, num_forall_eliminations=1, preserve_expr=forall_stmt)
def substitute_domain(self, superset, **defaults_config): ''' Substitute the domain with a superset. From [exists_{x in A| Q(x)} P(x)], derive and return [exists_{x in B| Q(x)} P(x)] given A subseteq B. ''' from proveit.logic import And from . import exists_in_superset _x = self.instance_params _P = Lambda(_x, self.instance_expr) if self.conditions.num_entries() == 1: _Q = Lambda(_x, self.condition) else: _Q = Lambda(_x, And(self.conditions[1:])) _impl = exists_in_superset.instantiate({ P: _P, Q: _Q, A: self.domain, B: superset, x: _x, y: _x }) return _impl.derive_consequent()
def deduce_number_set(expr, **defaults_config): ''' Prove that 'expr' is an Expression that represents a number in a standard number set that is as restrictive as we can readily know. ''' from proveit.logic import And, InSet, Equals, NotEquals from proveit.numbers import Less, LessEq, zero # Find the first (most restrictive) number set that # contains 'expr' or something equal to it. for number_set in sorted_number_sets: membership = None for eq_expr in Equals.yield_known_equal_expressions(expr): if isinstance(eq_expr, ExprRange): membership = And( ExprRange(eq_expr.parameter, InSet(eq_expr.body, number_set), eq_expr.true_start_index, eq_expr.true_end_index, styles=eq_expr.get_styles())) else: membership = InSet(eq_expr, number_set) if membership.proven(): break # found a known number set membership else: membership = None if membership is not None: membership = InSet(expr, number_set).prove() break if hasattr(expr, 'deduce_number_set'): # Use 'deduce_number_set' method. try: deduced_membership = expr.deduce_number_set() except (UnsatisfiedPrerequisites, ProofFailure): deduced_membership = None if deduced_membership is not None: assert isinstance(deduced_membership, Judgment) if not isinstance(deduced_membership.expr, InSet): raise TypeError("'deduce_number_set' expected to prove an " "InSet type expression") if deduced_membership.expr.element != expr: raise TypeError("'deduce_number_set' was expected to prove " "that %s is in some number set" % expr) # See if this deduced number set is more restrictive than # what we had surmised already. deduced_number_set = deduced_membership.domain if membership is None: membership = deduced_membership number_set = deduced_number_set elif (deduced_number_set != number_set and number_set.includes(deduced_number_set)): number_set = deduced_number_set membership = deduced_membership if membership is None: from proveit import defaults raise UnsatisfiedPrerequisites( "Unable to prove any number membership for %s" % expr) # Already proven to be in some number set, # Let's see if we can restrict it further. if Less(zero, expr).proven(): # positive number_set = pos_number_set.get(number_set, None) elif Less(expr, zero).proven(): # negative number_set = neg_number_set.get(number_set, None) elif LessEq(zero, expr).proven(): # non-negative number_set = nonneg_number_set.get(number_set, None) elif LessEq(expr, zero).proven(): # non-positive number_set = nonpos_number_set.get(number_set, None) elif NotEquals(expr, zero).proven(): number_set = nonzero_number_set.get(number_set, None) if number_set is None: # Just use what we have already proven. return membership.prove() return InSet(expr, number_set).prove()
def eliminate(skolem_constants, judgment, **defaults_config): ''' For the provided judgment of the form S |– alpha and the tuple of Skolem constants skolem_constants that had been specified earlier using the Exists.choose(), derive and return a new judgment S' |– alpha where all assumptions in S involving only the given skolem_constants are now eliminated. This process will only work if the provided skolem_constants exactly match a set of Skolem constants used earlier in an Exists.choose() method to produce the Skolem constant-based subset of assumptions you wish to eliminate from S. ''' from proveit import Lambda from proveit import n, P, Q, alpha from proveit.logic import And from proveit.core_expr_types import (x_1_to_n, y_1_to_n) from proveit.logic.booleans.quantification.existence import ( skolem_elim) if skolem_constants not in Exists.skolem_consts_to_existential: raise KeyError("In calling Exists.eliminate(), the Skolem " "constants provided were: {}, but you can only " "eliminate Skolem constants that were chosen " "earlier when using Exists.choose() and the " "Skolem constants to be eliminated must appear " "exactly as specified in the original " "Exists.choose() method.".format(skolem_constants)) existential = Exists.skolem_consts_to_existential[skolem_constants] skolem_assumptions = set( existential.choose(*skolem_constants, print_message=False)) with defaults.temporary() as temp_defaults: temp_defaults.assumptions = ( assumption for assumption in defaults.assumptions if assumption not in skolem_assumptions) _P = Lambda(existential.instance_params, existential.instance_expr) if hasattr(existential, 'condition'): _Q = Lambda(existential.instance_params, existential.condition) else: # There is no condition but we still need to provide # something for _Q so we provide an empty conjunction, # And(). _Q = Lambda(existential.instance_params, And()) _alpha = judgment _n = existential.instance_params.num_elements() x_1_to__n = ExprTuple(x_1_to_n.basic_replaced({n: _n})) y_1_to__n = ExprTuple(y_1_to_n.basic_replaced({n: _n})) # express the judgment as an implication to match details of # the skolem_elim theorem being instantiated further below P_implies_alpha = _alpha.as_implication(hypothesis=_P.apply( *skolem_constants)) # the generalization to further match theorem details # can be handled through automation # P_implies_alpha.generalize( # skolem_constants, # conditions=[_Q.apply(*skolem_constants)]) return skolem_elim.instantiate( { n: _n, P: _P, Q: _Q, alpha: _alpha, x_1_to__n: skolem_constants, y_1_to__n: existential.instance_params }, preserve_all=True).derive_consequent()
absIsNonNeg = Forall(a, GreaterThanEquals(Abs(a), zero), domain=Complexes) absIsNonNeg absNotEqZero = Forall([a], NotEquals(Abs(a), zero), domain=Complexes, conditions=[NotEquals(a, zero)]) absNotEqZero absElim = Forall(x, Equals(Abs(x), x), domain=RealsPos) absElim absIneq = Forall((x, y), Iff(LessThanEquals(Abs(x), y), And(LessThanEquals(Neg(y), x), LessThanEquals(x, y))), domain=Reals, conditions=[GreaterThanEquals(y, zero)]) absIneq triangleInequality = Forall([a, b], LessThanEquals(Abs(Add(a, b)), Add(Abs(a), Abs(b))), domain=Complexes) triangleInequality absProd = Forall(xEtc, Equals(Abs(Mult(xEtc)), Mult(Etcetera(Abs(xMulti)))), domain=Complexes) absProd
from proveit.logic import Forall, Equals, And, InSet from proveit.number import Floor, Sub, IntervalCO, Integers, Reals from proveit.common import x from proveit.number.common import zero, one from proveit import beginAxioms, endAxioms beginAxioms(locals()) floorDef = Forall(x, And(InSet(Floor(x), Integers), InSet(Sub(x, Floor(x)), IntervalCO(zero, one))), domain=Reals) floorDef endAxioms(locals(), __package__)
true_eq_false_eval = Equals(Equals(TRUE, FALSE), FALSE) false_eq_true_eval = Equals(Equals(FALSE, TRUE), FALSE) true_conclusion = Forall(A, Implies(A, TRUE)) in_bool_equiv = Forall( A, Equals(in_bool(A), Or(Equals(A, TRUE), Equals(A, FALSE)))) true_is_bool = in_bool(TRUE) false_is_bool = in_bool(FALSE) unfold_forall_over_bool = Forall( P, Implies(Forall(A, PofA, domain=Boolean), And(PofTrue, PofFalse))) in_bool_if_true = Forall(A, in_bool(A), conditions=[A]) in_bool_if_false = Forall(A, in_bool(A), conditions=[Not(A)]) # This weak form requires B to be a Boolean by_cases_weak = Forall((A, B), B, domain=Boolean, conditions=[Implies(A, B), Implies(Not(A), B)]) # This is a stronger form that does not require B to be a Boolean by_cases = Forall(A, Forall(B, B, conditions=[Implies(A, B),
def conclude(self, **defaults_config): ''' Try to automatically conclude this disjunction. If any of its operands have pre-existing proofs, it will be proven via the or_if_any theorem. Otherwise, a reduction proof will be attempted (evaluating the operands). ''' from proveit.logic import And from . import true_or_true, true_or_false, false_or_true if self in {true_or_true.expr, true_or_false.expr, false_or_true.expr}: # should be proven via one of the imported theorems as a # simple special case return self.prove() # Prove that the disjunction is true by proving that ANY of its # operands is true. In the first attempt, don't use automation # to prove any of the operands so that we don't waste time # trying to prove operands when we already know one to be true. use_automation_possibilities = ([False, True] if defaults.automation else [False]) if self.operands.contains_range(): # There are ExprRange operands. if self.operands.num_entries() == 1: # Just a single ExprRange. if And(self.operands.entries[0]).proven(): # Trivally, if all of the operands are # True then any of them are, as long as # there is at list one (note, # Or() = FALSE, so we shouldn't try to # conclude Or() anyway). return self.conclude_any_via_all() # Use DeMorgan's law. return self.conclude_via_demorgans() # Group each ExprRange operand, call conclude_negation, # then disassociate the ExprRange operands. return prove_via_grouping_ranges( self, lambda expr, **kwargs: expr.conclude(**kwargs)) for use_automation_for_operand in use_automation_possibilities: proven_operand_indices = [] for _k, operand in enumerate(self.operands): try: operand.prove(automation=use_automation_for_operand) proven_operand_indices.append(_k) # possible way to prove it: self.conclude_via_example(operand) except ProofFailure: pass if self.operands.is_double() and len(proven_operand_indices) > 0: # One or both of the two operands were known to be true # (without automation). Try a possibly simpler proof # than conclude_via_example. try: if len(proven_operand_indices) == 2: return self.conclude_via_both() elif proven_operand_indices[0] == 0: return self.conclude_via_only_left() else: return self.conclude_via_only_right() except BaseException: pass if len(proven_operand_indices) > 0: try: # proven using conclude_via_example above # (unless or_if[Any,Left,Right] was not a usable # theorem, in which case this will fail and we # can simply try the default below) return self.prove(automation=False) except UnusableProof: # or_if[Any,Left,Right] must not have been a usable # theorem; use the default below. break raise ProofFailure( self, defaults.assumptions, "Or.conclude() has failed to find a proof for the " "disjunction: ({})".format(self))
from proveit.common import n, xMulti, xEtc, x, y, S from common import zero, one, two from proveit import beginAxioms, endAxioms beginAxioms(locals()) # Define the set of Naturals as, essentially, the minimum set that contains zero and all of its successors; # that is, n is in Naturals iff n is in all sets that contain zero and all successors. naturalsDef = Forall( n, Equals( InSet(n, Naturals), Forall( S, Implies( And(InSet(zero, S), Forall(x, InSet(Add(x, one), S), domain=S)), InSet(n, S))))) # Define the length of an ExpressionList inductively. exprListLengthDef = And( Equals(Len(), zero), Forall((xMulti, y), Equals(Len(xEtc, y), Add(Len(xEtc), one)))) naturalsPosDef = Forall(n, Iff(InSet(n, NaturalsPos), GreaterThanEquals(n, one)), domain=Naturals) naturalsPosDef integersDef = Equals(Integers, Union(Naturals, SetOfAll(n, Neg(n), domain=Naturals))) endAxioms(locals(), __package__)
from .common import zero, one, two from proveit import begin_axioms, end_axioms begin_axioms(locals()) # Define the set of Natural as, essentially, the minimum set that contains zero and all of its successors; # that is, n is in Natural iff n is in all sets that contain zero and all # successors. naturals_def = Forall( n, Equals( InSet(n, Natural), Forall( S, Implies( And(InSet(zero, S), Forall(x, InSet(Add(x, one), S), domain=S)), InSet(n, S))))) # Define the length of an ExpressionList inductively. expr_list_length_def = And( Equals(Len(), zero), Forall((x_multi, y), Equals(Len(x_etc, y), Add(Len(x_etc), one)))) naturals_pos_def = Forall(n, Iff(InSet(n, NaturalPos), GreaterThanEquals(n, one)), domain=Natural) naturals_pos_def integers_def = Equals(Integer, Union(Natural, SetOfAll(n, Neg(n), domain=Natural))) end_axioms(locals(), __package__)
from proveit.logic import Forall, InSet, NotInSet, NotEquals, And, Implies, Equals, Booleans from proveit.number import Integers, Naturals, NaturalsPos, Interval, Reals, RealsPos, Complexes from proveit.number import Add, GreaterThan, GreaterThanEquals, LessThan, LessThanEquals from proveit.number import Len from proveit.common import a, b, n, m, x, y, P, S, xMulti, xEtc, PxEtc from proveit.number import zero, one, two, three, four, five, six, seven, eight, nine from proveit.number.common import Pzero, Pm, P_mAddOne, Pn from proveit import beginTheorems, endTheorems beginTheorems(locals()) zeroInNats = InSet(zero, Naturals) successiveNats = Forall(n, InSet(Add(n, one), Naturals), domain=Naturals) inductionLemma = Forall(n, Forall(S, Implies(And(InSet(zero, S), Forall(x, InSet(Add(x,one), S), domain=S)), InSet(n, S))), domain=Naturals) induction = Forall(P, Implies(And(Pzero, Forall(m, P_mAddOne, domain=Naturals, conditions=[Pm])), Forall(n, Pn, Naturals))) zeroLenExprList = Equals(Len(), zero) multiVarInduction = Forall(P, Implies(Forall((xMulti, y), Implies(PxEtc, Operation(P, [xEtc, y]))), Forall(xMulti, PxEtc))) inIntsIsBool = Forall(a, InSet(InSet(a, Integers), Booleans)) inIntsIsBool notInIntsIsBool = Forall(a, InSet(NotInSet(a, Integers), Booleans)) notInIntsIsBool intsInReals = Forall(a, InSet(a, Reals), domain=Integers) intsInReals
NotEquals( a, zero)]) abs_not_eq_zero # transferred by wdc 3/11/2020 abs_elim = Forall(x, Equals(Abs(x), x), domain=RealPos) abs_elim # transferred by wdc 3/11/2020 abs_ineq = Forall( (x, y), Iff( LessThanEquals( Abs(x), y), And( LessThanEquals( Neg(y), x), LessThanEquals( x, y))), domain=Real, conditions=[ GreaterThanEquals( y, zero)]) abs_ineq # transferred by wdc 3/11/2020 triangle_inequality = Forall([a, b], LessThanEquals( Abs(Add(a, b)), Add(Abs(a), Abs(b))), domain=Complex) triangle_inequality # transferred by wdc 3/11/2020 abs_prod = Forall(x_etc, Equals(Abs(Mult(x_etc)), Mult(Etcetera(Abs(x_multi)))), domain=Complex)
def unbundle(expr, unbundle_thm, num_param_entries=(1, ), *, assumptions=USE_DEFAULTS): ''' Given a nested OperationOverInstances, derive or equate an equivalent form in which the parameter entries are split in number according to 'num_param_entries'. Use the given theorem specific to the particular OperationOverInstances. For example, \forall_{x, y, z | Q(x, y), R(z)} P(x, y, z) can become \forall_{x, y | Q(x, y)} \forall_{z | R(z)} P(x, y, z) via bundle with num_param_entries=(2, 1) or num_param_entries=(2,) -- the last number can be implied by the remaining number of parameters. For example of the form of the theorem required, see proveit.logic.boolean.quantification.unbundling or proveit.logic.boolean.quantification.bundling_equality. ''' from proveit.relation import TransRelUpdater from proveit.logic import Implies, Equals, And # Make a TransRelUpdater only if the bundle_thm yield an # equation, in which case we'll want the result to be an equation. eq = None unbundled = expr net_indicated_param_entries = sum(num_param_entries) num_actual_param_entries = len(expr.instanceParams) for n in num_param_entries: if not isinstance(n, int) or n <= 0: raise ValueError( "Each of 'num_param_entries', must be an " "integer greater than 0. %s fails this requirement." % (num_param_entries)) if net_indicated_param_entries > num_actual_param_entries: raise ValueError( "Sum of 'num_param_entries', %s=%d should not " "be greater than the number of parameter entries " "of %s for unbundling." % (num_param_entries, net_indicated_param_entries, expr)) if net_indicated_param_entries < num_actual_param_entries: diff = num_actual_param_entries - net_indicated_param_entries num_param_entries = list(num_param_entries) + [diff] else: num_param_entries = list(num_param_entries) while len(num_param_entries) > 1: n_last_entries = num_param_entries.pop(-1) first_params = ExprTuple(*unbundled.instanceParams[:-n_last_entries]) first_param_vars = {getParamVar(param) for param in first_params} remaining_params = \ ExprTuple(*unbundled.instanceParams[-n_last_entries:]) _m = first_params.length() _n = remaining_params.length() _P = unbundled.instanceExpr # Split up the conditions between the outer # OperationOverInstances and inner OperationOverInstances condition = unbundled.effectiveCondition() if isinstance(condition, And): _nQ = 0 for cond in condition.operands: cond_vars = free_vars(cond, err_inclusively=True) if first_param_vars.isdisjoint(cond_vars): break _nQ += 1 if _nQ == 0: _Q = And() elif _nQ == 1: _Q = condition.operands[0] else: _Q = And(*condition.operands[:_nQ]) _nR = len(condition.operands) - _nQ if _nR == 0: _R = And() elif _nR == 1: _R = condition.operands[-1] else: _R = And(*condition.operands[_nQ:]) elif first_param_vars.isdisjoint( free_vars(condition, err_inclusively=True)): _Q = condition _R = And() else: _Q = And() _R = condition m, n = unbundle_thm.instanceVars P, Q, R = unbundle_thm.instanceExpr.instanceVars correspondence = unbundle_thm.instanceExpr.instanceExpr if isinstance(correspondence, Implies): if (not isinstance(correspondence.antecedent, OperationOverInstances) or not len(correspondence.antecedent.instanceParams) == 2): raise ValueError("'unbundle_thm', %s, does not have the " "expected form with the bundled form as " "the antecedent of the implication, %s" % (unbundle_thm, correspondence)) x_1_to_m, y_1_to_n = correspondence.antecedent.instanceParams elif isinstance(correspondence, Equals): if not isinstance( correspondence.rhs, OperationOverInstances or not len(correspondence.antecedent.instanceParams) == 2): raise ValueError("'unbundle_thm', %s, does not have the " "expected form with the bundled form on " "right of the an equality, %s" % (unbundle_thm, correspondence)) x_1_to_m, y_1_to_n = correspondence.rhs.instanceParams else: raise ValueError("'unbundle_thm', %s, does not have the expected " "form with an equality or implication " "correspondence, %s" % (unbundle_thm, correspondence)) Qx = Function(Q, first_params) Rxy = Function(R, unbundled.instanceParams) Pxy = Function(P, unbundled.instanceParams) x_1_to_m = x_1_to_m.replaced({m: _m}) y_1_to_n = y_1_to_n.replaced({n: _n}) instantiation = unbundle_thm.instantiate( { m: _m, n: _n, ExprTuple(x_1_to_m): first_params, ExprTuple(y_1_to_n): remaining_params, Pxy: _P, Qx: _Q, Rxy: _R }, assumptions=assumptions) if isinstance(instantiation.expr, Implies): unbundled = instantiation.deriveConsequent() elif isinstance(instantiation.expr, Equals): if eq is None: eq = TransRelUpdater(unbundled) try: unbundled = eq.update(instantiation) except ValueError: raise ValueError( "Instantiation of bundle_thm %s is %s but " "should match %s on one side of the equation." % (unbundle_thm, instantiation, unbundled)) else: raise ValueError("Instantiation of bundle_thm %s is %s but " "should be an Implies or Equals expression." % (unbundle_thm, instantiation)) if eq is None: # Return the unbundled result. return unbundled else: # Return the equality between the original expression and # the unbundled result. return eq.relation
from proveit.numbers import zero, one, two, three, four, five, six, seven, eight, nine from proveit.numbers.common import Pzero, Pm, P_mAddOne, Pn from proveit import begin_theorems, end_theorems begin_theorems(locals()) zero_in_nats = InSet(zero, Natural) successive_nats = Forall(n, InSet(Add(n, one), Natural), domain=Natural) induction_lemma = Forall(n, Forall( S, Implies( And( InSet(zero, S), Forall(x, InSet(Add(x, one), S), domain=S)), InSet(n, S))), domain=Natural) induction = Forall( P, Implies(And(Pzero, Forall(m, P_mAddOne, domain=Natural, conditions=[Pm])), Forall(n, Pn, Natural))) zero_len_expr_tuple = Equals(Len(), zero) multi_var_induction = Forall( P, Implies(Forall((x_multi, y), Implies(Px_etc, Operation(P, [x_etc, y]))), Forall(x_multi, Px_etc)))
def deduce_equal_or_not(self, other_tuple, **defaults_config): ''' Prove and return that this ExprTuple is either equal or not equal to other_tuple or raises an UnsatisfiedPrerequisites or NotImplementedError if we cannot readily prove either of these. ''' from proveit import (ExprRange, safe_dummy_var, UnsatisfiedPrerequisites) from proveit.logic import ( And, Or, Equals, NotEquals, deduce_equal_or_not) if self == other_tuple: return Equals(self, other_tuple).conclude_via_reflexivity if not isinstance(other_tuple, ExprTuple): raise TypeError("Expecting 'other_tuple' to be an ExprTuple " "not a %s"%other_tuple.__class__) _i = self.num_elements() _j = other_tuple.num_elements() size_relation = deduce_equal_or_not(_i, _j) if isinstance(size_relation.expr, NotEquals): # Not equal because the lengths are different. return self.not_equal(other_tuple) def raise_non_corresponding(): raise NotImplementedError( "ExprTuple.deduce_equal_or_not is only " "implemented for the case when ExprRanges " "match up: %s vs %s"%self, other_tuple) if self.num_entries() == other_tuple.num_entries(): if self.num_entries()==1 and self.contains_range(): if not other_tuple.contains_range(): # One ExprTuple has a range but the other doesn't. # That case isn't handled. raise_non_corresponding() lhs_range = self.entries[0] rhs_range = other_tuple.entries[0] start_index = lhs_range.start_index end_index = lhs_range.end_index if ((start_index != rhs_range.start_index) or (end_index != rhs_range.end_index)): # Indices must match for a proper correspondence. raise_non_corresponding() if lhs_range.parameter != rhs_range.parameter: # Use a safe common parameter. param = safe_dummy_var(lhs_range.body, rhs_range.body) lhs_range_body = lhs_range.body.basic_replaced( {lhs_range.parameter: param}) rhs_range_body = rhs_range.body.basic_replaced( {rhs_range.parameter: param}) else: param = lhs_range.parameter lhs_range_body = lhs_range.body rhs_range_body = rhs_range.body inner_assumptions = defaults.assumptions + ( lhs_range.parameter_condition(),) try: body_relation = deduce_equal_or_not( lhs_range_body, rhs_range_body, assumptions=inner_assumptions) if isinstance(body_relation, Equals): # Every element is equal, so the ExprTuples # are equal. return self.deduce_equality( Equals(self, other_tuple)) else: # Every element is not equal, so the ExprTuples # are not equal. # This will enable "any" from "all". And(ExprRange( param, NotEquals(lhs_range_body, rhs_range_body), start_index, end_index)).prove() return self.not_equal(other_tuple) except (NotImplementedError, UnsatisfiedPrerequisites): pass if And(ExprRange(param, Equals(lhs_range_body, rhs_range_body), start_index, end_index)).proven(): # Every element is equal, so the ExprTuples # are equal. return self.deduce_equality( Equals(self, other_tuple)) elif Or(ExprRange(param, NotEquals(lhs_range_body, rhs_range_body), start_index, end_index)).proven(): # Some element pair is not equal, so the ExprTuples # are not equal. return self.not_equal(other_tuple) raise UnsatisfiedPrerequisites( "Could not determine whether %s = %s" %(self, other_tuple)) # Loop through each entry pair in correspondence and # see if we can readily prove whether or not they are # all equal. for idx, (_x, _y) in enumerate( zip(self.entries, other_tuple.entries)): if isinstance(_x, ExprRange) != isinstance(_y, ExprRange): raise_non_corresponding() if _x == _y: # The expressions are the same, so we know they # are equal. continue if isinstance(_x, ExprRange): # Wrap ExprRanges in ExprTuples and compare as # single entry tuples. _x = ExprTuple(_x) _y = ExprTuple(_y) _k = _x.num_elements() _l = _y.num_elements() size_relation = deduce_equal_or_not(_k, _l) if isinstance(size_relation.expr, NotEquals): # Not implemented when the ExprRanges don't # correspond in size. raise_non_corresponding() relation = deduce_equal_or_not(_x, _y) else: # Compare singular entries. relation = deduce_equal_or_not(_x, _y) if isinstance(relation.expr, NotEquals): # Aha! They are not equal. return self.not_equal(other_tuple) # They are equal! return self.deduce_equality(Equals(self, other_tuple)) raise NotImplementedError( "ExprTuple.deduce_equal_or_not is not implemented " "for ExprTuples that have a different number of " "elements.")