def empty_range(_i, _j, _f, assumptions): # If the start and end are literal ints and form an # empty range, then it should be straightforward to # prove that the range is empty. from proveit.numbers import is_literal_int, Add from proveit.logic import Equals from proveit import m _m = entries[0].start_index _n = entries[0].end_index empty_req = Equals(Add(_n, one), _m) if is_literal_int(_m) and is_literal_int(_n): if _n.as_int() + 1 == _m.as_int(): empty_req.prove() if empty_req.proven(assumptions): _f = Lambda( (entries[0].parameter, entries[0].body.parameter), entries[0].body.body) _i = entry_map(_i) _j = entry_map(_j) return len_of_empty_range_of_ranges.instantiate( { m: _m, n: _n, f: _f, i: _i, j: _j }, assumptions=assumptions)
def conclude(self, assumptions=USE_DEFAULTS): ''' Attempt to conclude that the element is in the exponentiated set. ''' from proveit.logic import InSet from proveit.logic.sets.membership import (exp_set_0, exp_set_1, exp_set_2, exp_set_3, exp_set_4, exp_set_5, exp_set_6, exp_set_7, exp_set_8, exp_set_9) from proveit.numbers import zero, is_literal_int, DIGITS element = self.element domain = self.domain elem_in_set = InSet(element, domain) if not isinstance(element, ExprTuple): raise ProofFailure( elem_in_set, assumptions, "Can only automatically deduce membership in exponentiated " "sets for an element that is a list") exponent_eval = domain.exponent.evaluation(assumptions=assumptions) exponent = exponent_eval.rhs base = domain.base #print(exponent, base, exponent.as_int(),element, domain, len(element)) if is_literal_int(exponent): if exponent == zero: return exp_set_0.instantiate({S: base}, assumptions=assumptions) if element.num_entries() != exponent.as_int(): raise ProofFailure( elem_in_set, assumptions, "Element not a member of the exponentiated set; " "incorrect list length") elif exponent in DIGITS: # thm = forall_S forall_{a, b... in S} (a, b, ...) in S^n thm = locals()['exp_set_%d' % exponent.as_int()] expr_map = {S: base} # S is the base # map a, b, ... to the elements of element. expr_map.update({ proveit.__getattr__(chr(ord('a') + k)): elem_k for k, elem_k in enumerate(element) }) elem_in_set = thm.instantiate(expr_map, assumptions=assumptions) else: raise ProofFailure( elem_in_set, assumptions, "Automatic deduction of membership in exponentiated sets " "is not supported beyond an exponent of 9") else: raise ProofFailure( elem_in_set, assumptions, "Automatic deduction of membership in exponentiated sets is " "only supported for an exponent that is a literal integer") if exponent_eval.lhs != exponent_eval.rhs: # after proving that the element is in the set taken to # the evaluation of the exponent, substitute back in the # original exponent. return exponent_eval.sub_left_side_into(elem_in_set, assumptions=assumptions) return elem_in_set
def sorted_items(cls, items, reorder=True, assumptions=USE_DEFAULTS): ''' Return the given items in sorted order with respect to this TransitivityRelation class (cls) under the given assumptions using known transitive relations. If reorder is False, raise a TransitivityException if the items are not in sorted order as provided. Weak relations (e.g., <=) are only considered when calling this method on a weak relation class (otherwise, only equality and strong relations are used in the sorting). ''' from proveit.numbers import is_literal_int assumptions = defaults.checked_assumptions(assumptions) if all(is_literal_int(item) for item in items): # All the items are integers. Use efficient n log(n) sorting to # get them in the proper order and then use fixed_transitivity_sort # to efficiently prove this order. items = sorted(items, key=lambda item: item.as_int()) reorder = False if reorder: sorter = TransitivitySorter(cls, items, assumptions=assumptions) return list(sorter) else: return cls._fixed_transitivity_sort( items, assumptions=assumptions).operands
def _computation(self, assumptions=USE_DEFAULTS, must_evaluate=False): # Currently not doing anything with must_evaluate # What it should do is make sure it evaluates to a number # and can circumvent any attempt that will not evaluate to # number. from proveit.numbers import one if not isinstance(self.operand, ExprTuple): # Don't know how to compute the length if the operand is # not a tuple. For example, it could be a variable that # represent a tuple. So just return the self equality. from proveit.logic import Equals return Equals(self, self).prove() entries = self.operand.entries has_range = any(isinstance(entry, ExprRange) for entry in entries) if (len(entries) == 1 and has_range and not isinstance(entries[0].body, ExprRange)): # Compute the length of a single range. Examples: # |(f(1), ..., f(n))| = n # |(f(i), ..., f(j))| = j-i+1 range_entry = entries[0] start_index = range_entry.start_index end_index = range_entry.end_index lambda_map = range_entry.lambda_map if start_index == one: from proveit.core_expr_types.tuples import \ range_from1_len return range_from1_len.instantiate( { f: lambda_map, i: end_index }, assumptions=assumptions) else: from proveit.core_expr_types.tuples import range_len return range_len.instantiate( { f: lambda_map, i: start_index, j: end_index }, assumptions=assumptions) elif not has_range: # Case of all non-range entries. if len(entries) == 0: # zero length. from proveit.core_expr_types.tuples import tuple_len_0 return tuple_len_0 elif len(entries) < 10: # Automatically get the count and equality with # the length of the proper iteration starting from # 1. For example, # |(a, b, c)| = 3 # |(a, b, c)| = |(1, .., 3)| import proveit.numbers.numerals.decimals _n = len(entries) len_thm = proveit.numbers.numerals.decimals\ .__getattr__('tuple_len_%d' % _n) repl_map = dict() for param, entry in zip(len_thm.explicit_instance_params(), entries): repl_map[param] = entry return len_thm.instantiate(repl_map) else: # raise NotImplementedError("Can't handle length computation " # ">= 10 for %s"%self) from proveit.core_expr_types.tuples import tuple_len_incr from proveit.numbers import num from proveit.logic import Equals eq = tuple_len_incr.instantiate( { i: num(len(entries) - 1), a: entries[:-1], b: entries[-1] }, assumptions=assumptions) rhs_simp = eq.rhs._integerBinaryEval(assumptions=assumptions) return rhs_simp.sub_right_side_into(eq, assumptions=assumptions) # return Equals(eq.lhs, eq.rhs._integerBinaryEval(assumptions=assumptions).rhs).prove(assumptions=assumptions) # raise NotImplementedError("Can't handle length computation " # ">= 10 for %s"%self) elif (len(entries) == 2 and not isinstance(entries[1], ExprRange) and not isinstance(entries[0].body, ExprRange)): # Case of an extended range: # |(a_1, ..., a_n, b| = n+1 from proveit.core_expr_types.tuples import \ extended_range_len, extended_range_from1_len assert isinstance(entries[0], ExprRange) range_lambda = entries[0].lambda_map range_start = entries[0].start_index range_end = entries[0].end_index if range_start == one: return extended_range_from1_len.instantiate( { f: range_lambda, b: entries[1], i: range_end }, assumptions=assumptions) else: return extended_range_len.instantiate( { f: range_lambda, b: entries[1], i: range_start, j: range_end }, assumptions=assumptions) else: # Handle the general cases via general_len_val, # len_of_ranges_with_repeated_indices, # len_of_ranges_with_repeated_indices_from_1, # or len_of_empty_range_of_range from proveit.core_expr_types.tuples import ( general_len, len_of_ranges_with_repeated_indices, len_of_ranges_with_repeated_indices_from_1, len_of_empty_range_of_ranges) _x = safe_dummy_var(self) def entry_map(entry): if isinstance(entry, ExprRange): if isinstance(entry.body, ExprRange): # Return an ExprRange of lambda maps. return ExprRange(entry.parameter, entry.body.lambda_map, entry.start_index, entry.end_index) else: # Use the ExprRange entry's map. return entry.lambda_map # For individual elements, just map to the # elemental entry. return Lambda(_x, entry) def entry_start(entry): if isinstance(entry, ExprRange): if isinstance(entry.body, ExprRange): # Return an ExprRange of lambda maps. return ExprRange(entry.parameter, entry.body.start_index, entry.start_index, entry.end_index) else: return entry.start_index return one # for individual elements, use start=end=1 def entry_end(entry): if isinstance(entry, ExprRange): if isinstance(entry.body, ExprRange): # Return an ExprRange of lambda maps. return ExprRange(entry.parameter, entry.body.end_index, entry.start_index, entry.end_index) else: return entry.end_index return one # for individual elements, use start=end=1 def empty_range(_i, _j, _f, assumptions): # If the start and end are literal ints and form an # empty range, then it should be straightforward to # prove that the range is empty. from proveit.numbers import is_literal_int, Add from proveit.logic import Equals from proveit import m _m = entries[0].start_index _n = entries[0].end_index empty_req = Equals(Add(_n, one), _m) if is_literal_int(_m) and is_literal_int(_n): if _n.as_int() + 1 == _m.as_int(): empty_req.prove() if empty_req.proven(assumptions): _f = Lambda( (entries[0].parameter, entries[0].body.parameter), entries[0].body.body) _i = entry_map(_i) _j = entry_map(_j) return len_of_empty_range_of_ranges.instantiate( { m: _m, n: _n, f: _f, i: _i, j: _j }, assumptions=assumptions) _f = [entry_map(entry) for entry in entries] _i = [entry_start(entry) for entry in entries] _j = [entry_end(entry) for entry in entries] _n = Len(_i).computed(assumptions=assumptions, simplify=False) from proveit.numbers import is_literal_int if len(entries) == 1 and isinstance(entries[0], ExprRange): if (is_literal_int(entries[0].start_index) and is_literal_int(entries[0].end_index)): if (entries[0].end_index.as_int() + 1 == entries[0].start_index.as_int()): return empty_range(_i[0], _j[0], _f, assumptions) if all(_ == _i[0] for _ in _i) and all(_ == _j[0] for _ in _j): if isinstance(_i[0], ExprRange): if _i[0].is_parameter_independent: # A parameter independent range means they # are all the same. _i = [_i[0].body] if isinstance(_j[0], ExprRange): if _j[0].is_parameter_independent: # A parameter independent range means they # are all the same. _j = [_j[0].body] if (not isinstance(_i[0], ExprRange) and not isinstance(_j[0], ExprRange)): # special cases where the indices are repeated if _i[0] == one: thm = len_of_ranges_with_repeated_indices_from_1 return thm.instantiate({ n: _n, f: _f, i: _j[0] }, assumptions=assumptions) else: thm = len_of_ranges_with_repeated_indices return thm.instantiate( { n: _n, f: _f, i: _i[0], j: _j[0] }, assumptions=assumptions) return general_len.instantiate({ n: _n, f: _f, i: _i, j: _j }, assumptions=assumptions)
def computation(self, **defaults_config): ''' Compute this Len expression, returning the equality between self and the expression for its computed value. Examples: |(a, b, c)| = 3 |(x_1, ..., x_n, y)| = n+1 |(f(i), ..., f(j), x_1, ..., x_n)| = (j-i+1) + (n-1+1) |x| = |x| In the last case, the 'x' represents an unknown tuple, so there is not anything we can do to compute it. ''' # Currently not doing anything with must_evaluate # What it should do is make sure it evaluates to a number # and can circumvent any attempt that will not evaluate to # number. from proveit.numbers import one #print(self.operands.entries[0].__class__) if not isinstance(self.operands, ExprTuple): # Don't know how to compute the length if the operand is # not a tuple. For example, it could be a variable that # represent a tuple. So just return the self equality. from proveit.logic import Equals return Equals(self, self).conclude_via_reflexivity() entries = self.operands.entries has_range = any(isinstance(entry, ExprRange) for entry in entries) if (len(entries) == 1 and has_range and not isinstance(entries[0].body, ExprRange)): # Compute the length of a single range. Examples: # |(f(1), ..., f(n))| = n # |(f(i), ..., f(j))| = j-i+1 range_entry = entries[0] start_index = range_entry.true_start_index end_index = range_entry.true_end_index lambda_map = range_entry.lambda_map if start_index == one: from proveit.core_expr_types.tuples import range_from1_len len_comp = range_from1_len.instantiate( { f: lambda_map, i: end_index }, auto_simplify=False) else: from proveit.core_expr_types.tuples import range_len len_comp = range_len.instantiate( { f: lambda_map, i: start_index, j: end_index }, auto_simplify=False) elif not has_range: # Case of all non-range entries. if len(entries) == 0: # zero length. from proveit.core_expr_types.tuples import tuple_len_0 return tuple_len_0 elif len(entries) < 10: # Automatically get the count and equality with # the length of the proper iteration starting from # 1. For example, # |(a, b, c)| = 3 # |(a, b, c)| = |(1, .., 3)| import proveit.numbers.numerals.decimals _n = len(entries) len_thm = proveit.numbers.numerals.decimals\ .__getattr__('tuple_len_%d' % _n) repl_map = dict() for param, entry in zip(len_thm.explicit_instance_params(), entries): repl_map[param] = entry return len_thm.instantiate(repl_map, auto_simplify=False) else: # raise NotImplementedError("Can't handle length computation " # ">= 10 for %s"%self) from proveit.core_expr_types.tuples import tuple_len_incr from proveit.numbers import num from proveit.logic import Equals # We turn on automation because this length equality should # be true (we know the number of elements is equal to the # number of entries since there are no ExprRange entries). # Since we know it's true, why not commit ourselves to # proving it? return tuple_len_incr.instantiate( { i: num(len(entries) - 1), a: entries[:-1], b: entries[-1] }, automation=True) # return Equals(eq.lhs, eq.rhs._integerBinaryEval(assumptions=assumptions).rhs).prove(assumptions=assumptions) # raise NotImplementedError("Can't handle length computation " # ">= 10 for %s"%self) elif (len(entries) == 2 and not isinstance(entries[1], ExprRange) and not isinstance(entries[0].body, ExprRange)): # Case of an extended range: # |(a_1, ..., a_n, b| = n+1 from proveit.core_expr_types.tuples import \ extended_range_len, extended_range_from1_len assert isinstance(entries[0], ExprRange) range_lambda = entries[0].lambda_map range_start = entries[0].true_start_index range_end = entries[0].true_end_index if range_start == one: len_comp = extended_range_from1_len.instantiate({ f: range_lambda, x: entries[1], i: range_end }) else: len_comp = extended_range_len.instantiate({ f: range_lambda, x: entries[1], i: range_start, j: range_end }) else: # Handle the general cases via general_len_val, # len_of_ranges_with_repeated_indices, # len_of_ranges_with_repeated_indices_from_1, # or len_of_empty_range_of_range from proveit.core_expr_types.tuples import ( general_len, len_of_ranges_with_repeated_indices, len_of_ranges_with_repeated_indices_from_1, len_of_empty_range_of_ranges) _x = safe_dummy_var(self) preserved_exprs = defaults.preserved_exprs def entry_map(entry): # Don't auto-simplify the entry. preserved_exprs.add(entry) if isinstance(entry, ExprRange): if isinstance(entry.body, ExprRange): # Return an ExprRange of lambda maps. return ExprRange(entry.parameter, entry.body.lambda_map, entry.true_start_index, entry.true_end_index) else: return entry.lambda_map # For individual elements, just map to the # elemental entry. return Lambda(_x, entry) def entry_start(entry): if isinstance(entry, ExprRange): if isinstance(entry.body, ExprRange): # Return an ExprRange of lambda maps. return ExprRange(entry.parameter, entry.body.true_start_index, entry.true_start_index, entry.true_end_index) else: return entry.true_start_index return one # for individual elements, use start=end=1 def entry_end(entry): if isinstance(entry, ExprRange): if isinstance(entry.body, ExprRange): # Return an ExprRange of lambda maps. return ExprRange(entry.parameter, entry.body.true_end_index, entry.true_start_index, entry.true_end_index) else: return entry.true_end_index return one # for individual elements, use start=end=1 def empty_range(_i, _j, _f): # If the start and end are literal ints and form an # empty range, then it should be straightforward to # prove that the range is empty. from proveit.numbers import is_literal_int, Add from proveit.logic import Equals from proveit import m _m = entries[0].true_start_index _n = entries[0].true_end_index empty_req = Equals(Add(_n, one), _m) if is_literal_int(_m) and is_literal_int(_n): if _n.as_int() + 1 == _m.as_int(): empty_req.prove() if empty_req.proven(): _f = Lambda( (entries[0].parameter, entries[0].body.parameter), entries[0].body.body) _i = entry_map(_i) _j = entry_map(_j) return len_of_empty_range_of_ranges.instantiate({ m: _m, n: _n, f: _f, i: _i, j: _j }).with_wrapping_at() _f = [entry_map(entry) for entry in entries] _i = [entry_start(entry) for entry in entries] _j = [entry_end(entry) for entry in entries] _n = Len(_i).computed() from proveit.numbers import is_literal_int if len(entries) == 1 and isinstance(entries[0], ExprRange): if (is_literal_int(entries[0].true_start_index) and is_literal_int(entries[0].true_end_index)): if (entries[0].true_end_index.as_int() + 1 == entries[0].true_start_index.as_int()): return empty_range(_i[0], _j[0], _f) len_comp = None if all(_ == _i[0] for _ in _i) and all(_ == _j[0] for _ in _j): if isinstance(_i[0], ExprRange): if _i[0].is_parameter_independent: # A parameter independent range means they # are all the same. _i = [_i[0].body] if isinstance(_j[0], ExprRange): if _j[0].is_parameter_independent: # A parameter independent range means they # are all the same. _j = [_j[0].body] if (not isinstance(_i[0], ExprRange) and not isinstance(_j[0], ExprRange)): # special cases where the indices are repeated if _i[0] == one: thm = len_of_ranges_with_repeated_indices_from_1 len_comp = thm.instantiate({ n: _n, f: _f, i: _j[0] }).with_wrapping_at() else: thm = len_of_ranges_with_repeated_indices len_comp = thm.instantiate({ n: _n, f: _f, i: _i[0], j: _j[0] }).with_wrapping_at() if len_comp is None: len_comp = general_len.instantiate( { n: _n, f: _f, i: _i, j: _j }, preserved_exprs=preserved_exprs).with_wrapping_at() if not defaults.auto_simplify and len_comp.lhs != self: # Make sure the left side is reduced to the # original expression (self) which is preserved. return len_comp.inner_expr().lhs.shallow_simplify( preserved_exprs=preserved_exprs) if defaults.auto_simplify: # Ensure the right side is simplified which may have # been prevented due to automatic preservation of # instatiation expressions. return len_comp.inner_expr().rhs.simplify() return len_comp
def shallow_simplification(self, *, must_evaluate=False, **defaults_config): ''' Returns a proven simplification equation for this Exp expression assuming the operands have been simplified. Handles the following evaluations: a^0 = 1 for any complex a 0^x = 0 for any positive x 1^x = 1 for any complex x a^(Log(a, x)) = x for RealPos a and x, a != 1. x^n = x*x*...*x = ? for a natural n and irreducible x. Handles a zero or one exponent or zero or one base as simplifications. ''' from proveit.relation import TransRelUpdater from proveit.logic import EvaluationError, is_irreducible_value from proveit.logic import InSet from proveit.numbers import (zero, one, two, is_literal_int, is_literal_rational, Log, Rational, Abs) from . import (exp_zero_eq_one, exponentiated_zero, exponentiated_one, exp_nat_pos_expansion) if self.is_irreducible_value(): # already irreducible return Equals(self, self).conclude_via_reflexivity() if must_evaluate: if not all(is_irreducible_value(operand) for operand in self.operands): for operand in self.operands: if not is_irreducible_value(operand): # The simplification of the operands may not have # worked hard enough. Let's work harder if we # must evaluate. operand.evaluation() return self.evaluation() if self.exponent == zero: return exp_zero_eq_one.instantiate({a: self.base}) # =1 elif self.base == zero: # Will fail if the exponent is not positive, but this # is the only sensible thing to try. return exponentiated_zero.instantiate({x: self.exponent}) # =0 elif self.exponent == one: return self.power_of_one_reduction() elif self.base == one: return exponentiated_one.instantiate({x: self.exponent}) # =1 elif (isinstance(self.base, Exp) and isinstance(self.base.exponent, Div) and self.base.exponent.numerator == one and self.base.exponent.denominator == self.exponent): from . import nth_power_of_nth_root _n, _x = nth_power_of_nth_root.instance_params return nth_power_of_nth_root.instantiate( {_n: self.exponent, _x: self.base.base}) elif (isinstance(self.base, Exp) and isinstance(self.exponent, Div) and self.exponent.numerator == one and self.exponent.denominator == self.base.exponent): from . import nth_root_of_nth_power, sqrt_of_square _n = self.base.exponent _x = self.base.base if _n == two: return sqrt_of_square.instantiate({x: _x}) return nth_root_of_nth_power.instantiate({n: _n, x: _x}) elif (is_literal_rational(self.base) and is_literal_int(self.exponent) and self.exponent.as_int() > 1): expr = self eq = TransRelUpdater(expr) expr = eq.update(exp_nat_pos_expansion.instantiate( {x:self.base, n:self.exponent}, preserve_all=True)) # We should come up with a better way of reducing # ExprRanges representing repetitions: _n = self.exponent.as_int() if _n <= 0 or _n > 9: raise NotImplementedError("Currently only implemented for 1-9") repetition_thm = proveit.numbers.numerals.decimals \ .__getattr__('reduce_%s_repeats' % _n) rep_reduction = repetition_thm.instantiate({x: self.base}) expr = eq.update(expr.inner_expr().operands.substitution( rep_reduction.rhs, preserve_all=True)) expr = eq.update(expr.evaluation()) return eq.relation elif (isinstance(self.exponent, Log) and self.base == self.exponent.base): # base_ns = self.base.deduce_number_set() # antilog_ns = self.exponent.antilog.deduce_number_set() if (InSet(self.base, RealPos).proven() and InSet(self.exponent.antilog, RealPos).proven() and NotEquals(self.base, one).proven()): return self.power_of_log_reduction() expr = self # for convenience updating our equation: eq = TransRelUpdater(expr) if self.exponent == two and isinstance(self.base, Abs): from . import (square_abs_rational_simp, square_abs_real_simp) # |a|^2 = a if a is real try: deduce_number_set(self.base) except UnsatisfiedPrerequisites: pass rational_base = InSet(self.base, Rational).proven() real_base = InSet(self.base, Real).proven() thm = None if rational_base: thm = square_abs_rational_simp elif real_base: thm = square_abs_real_simp if thm is not None: simp = thm.instantiate({a: self.base.operand}) expr = eq.update(simp) # A further simplification may be possible after # eliminating the absolute value. expr = eq.update(expr.simplification()) return eq.relation