def deduce_number_set(self, **defaults_config): ''' Prove membership of this expression in the most restrictive standard number set we can readily know. ''' base_ns = deduce_number_set(self.base).domain exp_ns = deduce_number_set(self.exponent).domain if Natural.includes(base_ns) and Natural.includes(exp_ns): return self.deduce_in_number_set(NaturalPos) if Integer.includes(base_ns) and Natural.includes(exp_ns): return self.deduce_in_number_set(Integer) if RationalPos.includes(base_ns) and Integer.includes(exp_ns): return self.deduce_in_number_set(RationalPos) if (RationalNonZero.includes(base_ns) and Integer.includes(exp_ns)): return self.deduce_in_number_set(RationalNonZero) if Rational.includes(base_ns) and Natural.includes(exp_ns): return self.deduce_in_number_set(Rational) if RealPos.includes(base_ns) and Real.includes(exp_ns): return self.deduce_in_number_set(RealPos) if RealNonNeg.includes(base_ns) and Real.includes(exp_ns): return self.deduce_in_number_set(RealNonNeg) if Real.includes(base_ns) and Natural.includes(exp_ns): return self.deduce_in_number_set(Real) if ComplexNonZero.includes(base_ns): return self.deduce_in_number_set(ComplexNonZero) return self.deduce_in_number_set(Complex)
def right_mult_both_sides(self, multiplier, **defaults_config): ''' Multiply both sides of the relation by the 'multiplier' on the right. ''' from proveit.numbers import LessEq, zero, deduce_number_set from proveit.numbers.multiplication import ( strong_bound_via_left_factor_bound, weak_bound_via_left_factor_bound, reversed_strong_bound_via_left_factor_bound, reversed_weak_bound_via_left_factor_bound) was_reversed = False deduce_number_set(multiplier) if Less(zero, multiplier).proven(): new_rel = strong_bound_via_left_factor_bound.instantiate({ a: multiplier, x: self.lower, y: self.upper }) elif Less(multiplier, zero).proven(): new_rel = reversed_strong_bound_via_left_factor_bound.instantiate({ a: multiplier, x: self.lower, y: self.upper }) was_reversed = True elif LessEq(zero, multiplier).proven(): new_rel = weak_bound_via_left_factor_bound.instantiate({ a: multiplier, x: self.lower, y: self.upper }) elif LessEq(multiplier, zero).proven(): new_rel = reversed_weak_bound_via_left_factor_bound.instantiate({ a: multiplier, x: self.lower, y: self.upper }) was_reversed = True else: raise Exception( "Cannot right-multiply both sides of %s by %s " "without knowing the multiplier's relation with zero." % (self, multiplier)) new_rel = new_rel.with_mimicked_style(self) if was_reversed: new_rel = new_rel.with_direction_reversed() return new_rel
def shallow_simplification(self, *, must_evaluate=False, **defaults_config): ''' Returns a proven simplification equation for this Mod expression assuming the operands have been simplified. Specifically, performs reductions of the form (x mod L) = x where applicable and [(a mod L + b) mod L] = [(a + b) mod L]. ''' from . import (int_mod_elimination, real_mod_elimination, redundant_mod_elimination, redundant_mod_elimination_in_sum) from proveit.numbers import ( NaturalPos, RealPos, Interval, IntervalCO, subtract, zero, one) deduce_number_set(self.dividend) divisor_ns = deduce_number_set(self.divisor).domain if (NaturalPos.includes(divisor_ns) and InSet(self.dividend, Interval(zero, subtract(self.divisor, one))).proven()): # (x mod L) = x if L in N+ and x in {0 .. L-1} return int_mod_elimination.instantiate( {x:self.dividend, L:self.divisor}) if (RealPos.includes(divisor_ns) and InSet(self.dividend, IntervalCO(zero, self.divisor)).proven()): # (x mod L) = x if L in R+ and x in [0, L) return real_mod_elimination.instantiate( {x:self.dividend, L:self.divisor}) return Mod._redundant_mod_elimination( self, redundant_mod_elimination, redundant_mod_elimination_in_sum)
def conclude(self, **defaults_config): ''' Conclude something of the form a < b. ''' from proveit.logic import InSet from proveit.numbers import Add, zero, RealPos, deduce_number_set from . import positive_if_real_pos if self.upper == zero: # Special case with upper bound of zero. from . import negative_if_real_neg concluded = negative_if_real_neg.instantiate({a: self.lower}) return concluded if self.lower == zero: # Special case with lower bound of zero. deduce_number_set(self.upper) if InSet(self.upper, RealPos).proven(): positive_if_real_pos.instantiate({a: self.upper}) if ((isinstance(self.lower, Add) and self.upper in self.lower.terms.entries) or (isinstance(self.upper, Add) and self.lower in self.upper.terms.entries)): try: # Conclude an sum is bounded by one of its terms. return self.conclude_as_bounded_by_term() except UnsatisfiedPrerequisites: # If prerequisites weren't satisfied to do this, # we can still try something else. pass return NumberOrderingRelation.conclude(self)
def deduce_in_number_set(self, number_set, **defaults_config): ''' Attempt to prove that the logarithmic expression 'self' is in the given number set 'number_set'. May require user to previously show or assume that the Log base is not 1. Currently implemented only for the general case, giving Log_a(b) in Real when 'a' and 'b' both in RealPos and 'a' != 1. This can be augmented in near future. ''' from proveit import a, b from proveit.logic import InSet, NotEquals from proveit.numbers.logarithms import log_real_pos_real_closure from proveit.numbers import zero, Real deduce_number_set(self.base) deduce_number_set(self.antilog) if number_set == Real: return log_real_pos_real_closure.instantiate({ a: self.base, b: self.antilog }) # we can do more/better but that's enough for right now raise NotImplementedError( "'Log.deduce_in_number_set()' not implemented for the " " number set {0}. Try deducing in Real instead?".format( number_set))
def deduce_linear_bound(self, **defaults_config): ''' Bound the Sin function evaluation by a line. ''' from . import (sine_linear_bound, sine_linear_bound_pos, sine_linear_bound_nonneg, sine_linear_bound_neg, sine_linear_bound_nonpos) deduce_number_set(self.angle) if isinstance(self.angle, Abs): bound = sine_linear_bound.instantiate({theta: self.angle.operand}) elif InSet(self.angle, RealPos).proven(): bound = sine_linear_bound_pos.instantiate({theta: self.angle}) elif InSet(self.angle, RealNeg).proven(): bound = sine_linear_bound_neg.instantiate({theta: self.angle}) elif InSet(self.angle, RealNonNeg).proven(): bound = sine_linear_bound_nonneg.instantiate({theta: self.angle}) elif InSet(self.angle, RealNonPos).proven(): bound = sine_linear_bound_nonpos.instantiate({theta: self.angle}) else: _theta = Abs(self.angle) bound = sine_linear_bound.instantiate({theta: _theta}) if bound.rhs == self: return bound.with_direction_reversed() return bound
def deduce_number_set(self, **defaults_config): ''' Prove membership of this expression in the most restrictive standard number set we can readily know. ''' numer_ns = deduce_number_set(self.numerator).domain denom_ns = deduce_number_set(self.denominator).domain if RationalPos.includes(numer_ns) and RationalPos.includes(denom_ns): return self.deduce_in_number_set(RationalPos) if RationalNeg.includes(numer_ns) and RationalNeg.includes(denom_ns): return self.deduce_in_number_set(RationalPos) if RationalNeg.includes(numer_ns) and RationalPos.includes(denom_ns): return self.deduce_in_number_set(RationalNeg) if RationalPos.includes(numer_ns) and RationalNeg.includes(denom_ns): return self.deduce_in_number_set(RationalNeg) if (RationalNonNeg.includes(numer_ns) and RationalPos.includes(denom_ns)): return self.deduce_in_number_set(RationalNonNeg) if (RationalNonPos.includes(numer_ns) and RationalPos.includes(denom_ns)): return self.deduce_in_number_set(RationalNonPos) if (RationalNonNeg.includes(numer_ns) and RationalNeg.includes(denom_ns)): return self.deduce_in_number_set(RationalNonPos) if (RationalNonPos.includes(numer_ns) and RationalNeg.includes(denom_ns)): return self.deduce_in_number_set(RationalNonNeg) if (RationalNonZero.includes(numer_ns) and RationalNonZero.includes(denom_ns)): return self.deduce_in_number_set(RationalNonZero) if Rational.includes(numer_ns) and RationalNonZero.includes(denom_ns): return self.deduce_in_number_set(Rational) if RealPos.includes(numer_ns) and RealPos.includes(denom_ns): return self.deduce_in_number_set(RealPos) if RealNeg.includes(numer_ns) and RealNeg.includes(denom_ns): return self.deduce_in_number_set(RealPos) if RealPos.includes(numer_ns) and RealNeg.includes(denom_ns): return self.deduce_in_number_set(RealNeg) if RealNeg.includes(numer_ns) and RealPos.includes(denom_ns): return self.deduce_in_number_set(RealNeg) if RealNonNeg.includes(numer_ns) and RealPos.includes(denom_ns): return self.deduce_in_number_set(RealNonNeg) if RealNonPos.includes(numer_ns) and RealPos.includes(denom_ns): return self.deduce_in_number_set(RealNonPos) if RealNonNeg.includes(numer_ns) and RealNeg.includes(denom_ns): return self.deduce_in_number_set(RealNonPos) if RealNonPos.includes(numer_ns) and RealNeg.includes(denom_ns): return self.deduce_in_number_set(RealNonNeg) if Real.includes(numer_ns) and RealNonZero.includes(denom_ns): return self.deduce_in_number_set(Real) if RealNonZero.includes(numer_ns) and RealNonZero.includes(denom_ns): return self.deduce_in_number_set(RealNonZero) if Real.includes(numer_ns) and RealNonZero.includes(denom_ns): return self.deduce_in_number_set(Real) if (ComplexNonZero.includes(numer_ns) and ComplexNonZero.includes(denom_ns)): return self.deduce_in_number_set(ComplexNonZero) return self.deduce_in_number_set(Complex)
def deduce_number_set(self, **defaults_config): ''' Prove membership of this expression in the most restrictive standard number set we can readily know. ''' value_ns = deduce_number_set(self.value).domain divisor_ns = deduce_number_set(self.divisor).domain if (value_ns.includes(Integer) and divisor_ns.includes(Integer)): return self.deduce_in_number_set(Integer) else: return self.deduce_in_number_set(Real)
def deduce_number_set(self, **defaults_config): from proveit.numbers import deduce_number_set, greater _a = self.digits[0] _b = self.digits[1:] try: deduce_number_set(_a) except UnsatisfiedPrerequisites: pass if greater(_a, zero).proven(): return self.deduce_in_natural_pos() else: return self.deduce_in_natural()
def deduce_number_set(self, **defaults_config): ''' Prove membership of this Log expression in the most restrictive standard number set we can readily know. Currently just implemented for Reals, but should be able to augment this to allow more precise placement in RealPos vs. RealNeg for suitable base/antilog combinations. ''' from proveit.numbers import Real, RealPos, Complex base_ns = deduce_number_set(self.base).domain antilog_ns = deduce_number_set(self.antilog).domain if RealPos.includes(base_ns) and RealPos.includes(antilog_ns): return self.deduce_in_number_set(Real) return self.deduce_in_number_set(Complex)
def deduce_number_set(self, **defaults_config): ''' Prove membership of this expression in the most restrictive standard number set we can readily know. ''' number_set_map = { NaturalPos: IntegerNeg, IntegerNeg: NaturalPos, Natural: IntegerNonPos, IntegerNonPos: Natural, IntegerNonZero: IntegerNonZero, Integer: Integer, RationalPos: RationalNeg, RationalNeg: RationalPos, RationalNonNeg: RationalNonPos, RationalNonPos: RationalNonNeg, RationalNonZero: RationalNonZero, Rational: Rational, RealPos: RealNeg, RealNeg: RealPos, RealNonNeg: RealNonPos, RealNonPos: RealNonNeg, RealNonZero: RealNonZero, Real: Real, ComplexNonZero: ComplexNonZero, Complex: Complex } operand_ns = deduce_number_set(self.operand).domain # check if operand_ns is not a standard number set if operand_ns not in number_set_map.keys(): # try to replace term_ns with a std number set operand_ns = standard_number_set(operand_ns) return self.deduce_in_number_set(number_set_map[operand_ns])
def side_effects(self, judgment): ''' In addition to the TransitiveRelation side-effects, also attempt (where applicable) eliminate_dividen_exponent, eliminate_common_exponent, and eliminate_common_factor. ''' from proveit.numbers import two, Exp, Mult for side_effect in TransitiveRelation.side_effects(self, judgment): yield side_effect # For each of the following, use the default assumptions to # verify some conditions before yielding the side effect method # (i.e. check using .proven() without arguments) # for 2|(b^n), derive 2|b. # (can be generalized to any prime number). if self.lhs == two and isinstance(self.rhs, Exp): try: deduce_number_set(self.rhs.base) deduce_number_set(self.rhs.exponent) except UnsatisfiedPrerequisites: pass if (InSet(self.rhs.base, Integer).proven() and InSet(self.rhs.exponent, Integer).proven()): yield self.eliminate_dividend_exponent # for (a^n)|(b^n) if (isinstance(self.rhs, Exp) and isinstance(self.lhs, Exp) and self.lhs.exponent == self.rhs.exponent): try: deduce_number_set(self.lhs.base) deduce_number_set(self.rhs.base) deduce_number_set(self.lhs.exponent) except UnsatisfiedPrerequisites: pass if (InSet(self.lhs.base, Integer).proven() and InSet(self.rhs.base, Integer).proven() and InSet(self.lhs.exponent, NaturalPos).proven()): yield self.eliminate_common_exponent # for (ka)|(kb) if (isinstance(self.lhs, Mult) and isinstance(self.rhs, Mult)): operands_intersection = (set(self.lhs.operands).intersection( set(self.lhs.operands))) if (len(operands_intersection) > 0): yield self.eliminate_common_factors
def deduce_number_set(self, **defaults_config): ''' Prove membership of this expression in the most restrictive standard number set we can readily know. ''' summand_ns = deduce_number_set(self.summand, assumptions=self.conditions).domain return self.deduce_in_number_set(summand_ns)
def deduce_not_zero(self, **defaults_config): ''' Prove that this exponential is not zero given that the base is not zero. ''' from proveit.logic import InSet from proveit.numbers import RationalPos from . import exp_rational_non_zero__not_zero, exp_not_eq_zero deduce_number_set(self.base) deduce_number_set(self.exponent) if (not exp_not_eq_zero.is_usable() or ( InSet(self.base, RationalPos).proven() and InSet(self.exponent, RationalPos).proven())): # Special case where the base and exponent are RationalPos. return exp_rational_non_zero__not_zero.instantiate( {a: self.base, b: self.exponent}) return exp_not_eq_zero.instantiate( {a: self.base, b: self.exponent})
def divide_both_sides(self, divisor, **defaults_config): ''' Divide both sides of the relation by the 'divisor'. ''' from proveit.numbers import Less, zero, deduce_number_set from proveit.numbers.division import ( strong_div_from_numer_bound__pos_denom, strong_div_from_numer_bound__neg_denom) deduce_number_set(divisor) if Less(zero, divisor).proven(): thm = strong_div_from_numer_bound__pos_denom elif Less(divisor, zero).proven(): thm = strong_div_from_numer_bound__neg_denom else: raise Exception("Cannot divide both sides of %s by %s " "without knowing the divisors " "relation to zero." % (self, divisor)) new_rel = thm.instantiate({a: divisor, x: self.lower, y: self.upper}) return new_rel.with_mimicked_style(self)
def rounding_deduce_number_set(expr): ''' Prove membership of this expression in the most restrictive standard number set we can readily know. ''' from proveit.numbers import RealPos, Natural, Integer operand_ns = deduce_number_set(expr.operand).domain if RealPos.includes(operand_ns): return expr.deduce_in_number_set(Natural) return expr.deduce_in_number_set(Integer)
def exponentiate_both_sides(self, exponent, **defaults_config): ''' Exponentiate both sides of the relation by the 'exponent'. ''' from proveit.numbers import Less, LessEq, zero, deduce_number_set from proveit.numbers.exponentiation import (exp_pos_less, exp_nonneg_less, exp_neg_less, exp_nonpos_less) # We need to know how the exponent relates to zero. deduce_number_set(exponent) LessEq.sort([zero, exponent]) if Less(zero, exponent).proven(): new_rel = exp_pos_less.instantiate({ a: exponent, x: self.lower, y: self.upper }) elif Less(exponent, zero).proven(): new_rel = exp_neg_less.instantiate({ a: exponent, x: self.lower, y: self.upper }) elif LessEq(zero, exponent).proven(): new_rel = exp_nonneg_less.instantiate({ a: exponent, x: self.lower, y: self.upper }) elif LessEq(exponent, zero).proven(): new_rel = exp_nonpos_less.instantiate({ a: exponent, x: self.lower, y: self.upper }) else: raise Exception("Cannot exponentiate both sides of %s by %s " "without knowing the exponent's relation with " "zero" % (self, exponent)) return new_rel.with_mimicked_style(self)
def eliminate_common_exponent(self, **defaults_config): ''' From self of the form (k^n)|(a^n), derive and return k|a. k, a, must be integers, with n a positive integer. This method is called from the DividesRelation side_effects() method. ''' from proveit.numbers import Exp if (isinstance(self.lhs, Exp) and isinstance(self.rhs, Exp) and self.lhs.exponent == self.rhs.exponent): k = self.lhs.base a = self.rhs.base n = self.lhs.exponent try: for _expr in (k, a, n): deduce_number_set(_expr) except UnsatisfiedPrerequisites: pass if (InSet(k, Integer).proven() and InSet(a, Integer).proven() and InSet(n, NaturalPos).proven()): from . import common_exponent_elimination _k, _a, _n = common_exponent_elimination.instance_params return common_exponent_elimination.instantiate({ _k: k, _a: a, _n: n }) else: err_msg = ("In using Divides.eliminate_common_exponent(), " "the exponent ({0}) must already be known to be " "a positive natural and each base ({1}, {2}) " "must already been know to be an integer.".format( n, k, a)) else: err_msg = ( "In using Divides.eliminate_common_exponent(), " "the lhs {0} and rhs {1} must both be exponential.".format( self.lhs, self.rhs)) raise ValueError(err_msg)
def deduce_in_interval(self, **defaults_config): ''' Bound the interval of the Sin function evaluation depending upon what is known about the angle. ''' from . import (sine_interval, sine_nonneg_interval, sine_pos_interval, sine_nonpos_interval, sine_neg_interval) from proveit.numbers import (zero, pi, Less, LessEq, Neg) _theta = self.angle deduce_number_set(_theta) if (Less(zero, _theta).proven() and Less(_theta, pi).proven()): return sine_pos_interval.instantiate({theta: _theta}) elif (LessEq(zero, _theta).proven() and LessEq(_theta, pi).proven()): return sine_nonneg_interval.instantiate({theta: _theta}) elif (Less(Neg(pi), _theta).proven() and Less(_theta, zero).proven()): return sine_neg_interval.instantiate({theta: _theta}) elif (Less(Neg(pi), _theta).proven() and Less(_theta, zero).proven()): return sine_neg_interval.instantiate({theta: _theta}) elif (LessEq(Neg(pi), _theta).proven() and LessEq(_theta, zero).proven()): return sine_nonpos_interval.instantiate({theta: _theta}) else: # Without knowing more, we can only bound it by +/-1. return sine_interval.instantiate({theta: _theta})
def deduce_in_interval(self, **defaults_config): ''' Prove that the "x mod b" is in the interval [0, b). ''' from . import (mod_in_interval, mod_natpos_in_interval, mod_in_interval_c_o) from proveit.numbers import Integer, NaturalPos # from number_sets import deduce_in_integer, deduce_in_real dividend_ns = deduce_number_set(self.dividend).domain divisor_ns = deduce_number_set(self.divisor).domain int_dividend = Integer.includes(dividend_ns) if (int_dividend and NaturalPos.includes(divisor_ns)): return mod_natpos_in_interval.instantiate( {a: self.dividend, b: self.divisor}) elif (int_dividend and Integer.includes(divisor_ns)): # if the operands are integers, then we can deduce that # a mod b is an integer in the set {0,1,...,(b-1)} return mod_in_interval.instantiate( {a: self.dividend, b: self.divisor}) else: # if the operands are reals, then we can deduce that # a mod b is in half-open real interval [0, b) return mod_in_interval_c_o.instantiate( {a: self.dividend, b: self.divisor})
def deduce_number_set(self, **defaults_config): ''' Prove membership of this expression in the most restrictive standard number set we can readily know. After deriving a candidate super set that contains the number set deduced for each argument, the fxn then calls the self.deduce_in_number_set() fxn defined above. ''' list_of_operand_sets = [] # find a minimal std number set for operand for operand in self.operands: operand_ns = deduce_number_set(operand).domain list_of_operand_sets.append(operand_ns) # merge the resulting list of std number sets into a # single superset, if possible minimal_super_set = merge_list_of_sets(list_of_operand_sets) return self.deduce_in_number_set(minimal_super_set)
def deduce_number_set(self, **defaults_config): ''' Prove membership of this expression in the most restrictive standard number set we can readily know. ''' from proveit.numbers import (Integer, IntegerNonZero, NaturalPos, Natural, Rational, RationalNonZero, RationalPos, RationalNonNeg, Real, RealNonNeg, RealPos, ComplexNonZero, Complex) operand_ns = deduce_number_set(self.operand).domain if IntegerNonZero.includes(operand_ns): return self.deduce_in_number_set(NaturalPos) if Integer.includes(operand_ns): return self.deduce_in_number_set(Natural) if RationalNonZero.includes(operand_ns): return self.deduce_in_number_set(RationalPos) if Rational.includes(operand_ns): return self.deduce_in_number_set(RationalNonNeg) if ComplexNonZero.includes(operand_ns): return self.deduce_in_number_set(RealPos) return self.deduce_in_number_set(RealNonNeg)
def rounding_deduce_in_number_set(expr, number_set, rounding_real_closure_thm, rounding_real_pos_closure_thm): ''' Given a number set number_set, attempt to prove that the given Ceil, Floor, or Round expression is in that number set using the appropriate closure theorem. ''' from proveit import x from proveit.numbers import Integer, Natural, Real if number_set == Natural: return rounding_real_pos_closure_thm.instantiate({x: expr.operand}) operand_ns = deduce_number_set(expr.operand).domain if number_set.includes(Integer) and Real.includes(operand_ns): int_membership = rounding_real_closure_thm.instantiate( {x: expr.operand}) if number_set == Integer: return int_membership return SubsetEq(Integer, number_set).derive_superset_membership(expr) raise NotImplementedError( "The rounding_methods.py function 'rounding_deduce_in_number_set()'" "is not implemented for the %s set" % str(number_set))
def shallow_simplification(self, *, must_evaluate=False, **defaults_config): ''' Returns a proven simplification equation for this Abs expression assuming the operand has been simplified. Handles a number of absolute value simplifications: 1. ||x|| = |x| given x is complex 2. |x| = x given x ≥ 0 3. |x| = -x given x ≤ 0 (may try to prove this if easy to do) 4. |-x| = |x| 5. |x_1 * ... * x_n| = |x_1| * ... * |x_n| 6. |a / b| = |a| / |b| 7. |exp(i a)| = 1 given a in Real 8. |r exp(i a) - r exp(i b)| = 2 r sin(|a - b|/2) given a and b in Real. 9. |x_1 + ... + x_n| = +/-(x_1 + ... + x_n) if the terms are known to be all non-negative or all non-positive. ''' from proveit.logic import Equals from proveit.numbers import e, Add, Neg, LessEq, Mult, Div, Exp from proveit.numbers import zero, RealNonNeg, RealNonPos from proveit.logic import EvaluationError, is_irreducible_value if is_irreducible_value(self.operand): if isinstance(self.operand, Neg): # |-x| where 'x' is a literal. return self.distribution() else: # If the operand is irreducible, we can just use # abs_elimination. return self.abs_elimination() elif must_evaluate: # The simplification of the operands may not have # worked hard enough. Let's work harder if we # must evaluate. self.operand.evaluation() return self.evaluation() # among other things, convert any assumptions=None # to assumptions=() (thus averting len(None) errors) # Check if we have an established relationship between # self.operand and zero. try: deduce_number_set(self.operand) except UnsatisfiedPrerequisites: pass if (LessEq(zero, self.operand).proven() or LessEq(self.operand, zero).proven()): # Either |x| = x or |x| = -x depending upon the sign # of x (comparison with zero). return self.abs_elimination() if isinstance(self.operand, Abs): # Double absolute-value. We can remove one of them. return self.double_abs_elimination() # Distribute over a product or division. if (isinstance(self.operand, Mult) or isinstance(self.operand, Div) or isinstance(self.operand, Neg)): # Let's distribute the absolute values over the product # or division (the absolute value of the factors/components # will be simplified seperately if auto_simplify is True). return self.distribution() # |exp(i a)| = 1 if isinstance(self.operand, Exp) and self.operand.base == e: try: return self.unit_length_simplification() except ValueError: # Not in a complex polar form. pass # |r exp(i a) - r exp(i b)| = 2 r sin(|a - b|/2) if (isinstance(self.operand, Add) and self.operand.operands.is_double()): try: return self.chord_length_simplification() except (ProofFailure, ValueError): # Not in a complex polar form. pass # |x_1 + ... + x_n| = +/-(x_1 + ... + x_n) # if the terms are known to be all non-negative or all # non-positive. if isinstance(self.operand, Add): all_nonneg = True all_nonpos = True for term in self.operand.terms: # Note that "not proven" is not the same as "disproven". # Not proven means there is something we do not know. # Disproven means that we do know the converse. if all_nonneg and not LessEq(zero, term).proven(): all_nonneg = False if all_nonpos and not LessEq(term, zero).proven(): all_nonpos = False if all_nonpos: InSet(self.operand, RealNonPos).prove() elif all_nonneg: InSet(self.operand, RealNonNeg).prove() if all_nonpos or all_nonneg: # Do another pass now that we know the sign of # the operand. return self.shallow_simplification() # Default is no simplification. return Equals(self, self).prove()
def bound_via_numerator_bound(self, relation, **defaults_config): ''' Given a relation applicable to the numerator, bound this division accordingly. For example, if self is "a / b" and the relation is a < x return (a / b) < (x / b), provided b > 0. Also see NumberOperation.deduce_bound. ''' from proveit.numbers import zero, Less, LessEq, greater from . import (strong_div_from_numer_bound__pos_denom, weak_div_from_numer_bound__pos_denom, strong_div_from_numer_bound__neg_denom, weak_div_from_numer_bound__neg_denom) if isinstance(relation, Judgment): relation = relation.expr if not (isinstance(relation, Less) or isinstance(relation, LessEq)): raise TypeError("relation is expected to be Less " "or LessEq number relations, not %s" % relation) if self.numerator not in relation.operands: raise ValueError("relation is expected to involve the " "numerator of %s. %s does not." % (self, relation)) _a = self.denominator _x = relation.normal_lhs _y = relation.normal_rhs try: deduce_number_set(self.denominator) except UnsatisfiedPrerequisites: pass if greater(self.denominator, zero).proven(): if isinstance(relation, Less): bound = strong_div_from_numer_bound__pos_denom.instantiate({ a: _a, x: _x, y: _y }) elif isinstance(relation, LessEq): bound = weak_div_from_numer_bound__pos_denom.instantiate({ a: _a, x: _x, y: _y }) elif Less(self.denominator, zero).proven(): if isinstance(relation, Less): bound = strong_div_from_numer_bound__neg_denom.instantiate({ a: _a, x: _x, y: _y }) elif isinstance(relation, LessEq): bound = weak_div_from_numer_bound__neg_denom.instantiate({ a: _a, x: _x, y: _y }) else: raise UnsatisfiedPrerequisites( "We must know whether the denominator of %s " "is positive or negative before we can use " "'bound_via_numerator_bound'." % self) if bound.rhs == self: return bound.with_direction_reversed() return bound
def bound_via_denominator_bound(self, relation, **defaults_config): ''' Given a relation applicable to the numerator, bound this division accordingly. For example, if self is "a / b" and the relation is b > y return (a / b) < (a / y), provided a, b, and y are positive. Also see NumberOperation.deduce_bound. ''' from proveit.numbers import zero, Less, LessEq, greater, greater_eq from . import (strong_div_from_denom_bound__all_pos, weak_div_from_denom_bound__all_pos, strong_div_from_denom_bound__all_neg, weak_div_from_denom_bound__all_neg, strong_div_from_denom_bound__neg_over_pos, weak_div_from_denom_bound__neg_over_pos, strong_div_from_denom_bound__pos_over_neg, weak_div_from_denom_bound__pos_over_neg) if isinstance(relation, Judgment): relation = relation.expr if not (isinstance(relation, Less) or isinstance(relation, LessEq)): raise TypeError("relation is expected to be Less " "or LessEq number relations, not %s" % relation) if self.denominator not in relation.operands: raise ValueError("relation is expected to involve the " "denominator of %s. %s does not." % (self, relation)) _a = self.numerator _x = relation.normal_lhs _y = relation.normal_rhs try: # Ensure that we relate both _x and _y to zero by knowing # one of these. ordering = LessEq.sort((_x, _y, zero)) ordering.operands[0].apply_transitivity(ordering.operands[1]) except: pass # We'll generate an appropriate error below. try: deduce_number_set(self.numerator) deduce_number_set(self.denominator) except UnsatisfiedPrerequisites: pass pos_numer = greater_eq(self.numerator, zero).proven() neg_numer = LessEq(self.numerator, zero).proven() pos_denom = greater(self.denominator, zero).proven() neg_denom = Less(self.denominator, zero).proven() if not (pos_numer or neg_numer) or not (pos_denom or neg_denom): raise UnsatisfiedPrerequisites( "We must know the sign of the numerator and " "denominator of %s before we can use " "'bound_via_denominator_bound'." % self) if pos_numer and pos_denom: if isinstance(relation, Less): bound = strong_div_from_denom_bound__all_pos.instantiate({ a: _a, x: _x, y: _y }) elif isinstance(relation, LessEq): bound = weak_div_from_denom_bound__all_pos.instantiate({ a: _a, x: _x, y: _y }) elif neg_numer and neg_denom: if isinstance(relation, Less): bound = strong_div_from_denom_bound__all_neg.instantiate({ a: _a, x: _x, y: _y }) elif isinstance(relation, LessEq): bound = weak_div_from_denom_bound__all_neg.instantiate({ a: _a, x: _x, y: _y }) elif pos_numer and neg_denom: if isinstance(relation, Less): bound = strong_div_from_denom_bound__pos_over_neg.instantiate({ a: _a, x: _x, y: _y }) elif isinstance(relation, LessEq): bound = weak_div_from_denom_bound__pos_over_neg.instantiate({ a: _a, x: _x, y: _y }) elif neg_numer and pos_denom: if isinstance(relation, Less): bound = strong_div_from_denom_bound__neg_over_pos.instantiate({ a: _a, x: _x, y: _y }) elif isinstance(relation, LessEq): bound = weak_div_from_denom_bound__neg_over_pos.instantiate({ a: _a, x: _x, y: _y }) else: raise UnsatisfiedPrerequisites( "We must know whether or not the denominator of %s " "is positive or negative before we can use " "'bound_via_denominator_bound'." % self) if bound.rhs == self: return bound.with_direction_reversed() return bound
def conclude(self, **defaults_config): ''' Attempt to conclude the divisibility claim in various ways: (1) simple reflexivity (x|x); (2) simple x|0 for x ≠ 0; (3) simple x|xy or x|yx scenario (4) x^n | y^n if x|y is known or assumed (5) x|y if (x^n)|(y^n) is known or assumed (6) via transitivity. ''' from proveit.numbers import Exp #-- -------------------------------------------------------- --# #-- Case (1): x|x with x != 0 known or assumed --# #-- -------------------------------------------------------- --# from proveit.logic import InSet, NotEquals from proveit.numbers import zero, Complex err_str = "In Divides.conclude() we tried:\n" try: deduce_number_set(self.lhs) except UnsatisfiedPrerequisites: pass if self.lhs == self.rhs: if (NotEquals(self.lhs, zero).proven() and InSet(self.lhs, Complex).proven()): # Trivial x|x with complex x ≠ 0 return self.conclude_via_reflexivity() else: err_str = err_str + ( "Case: lhs = rhs. " "Although lhs = rhs = {0}, either {0} is not known to " "be non-zero or {0} is not known to be in the complex " "numbers (or both). Try proving one or both of those " "claims first.\n".format(self.lhs)) # raise ProofFailure(self, assumptions, err_str) #-- -------------------------------------------------------- --# #-- Case (2): x|0 with x != 0 known or assumed --# #-- -------------------------------------------------------- --# if self.rhs == zero: if (NotEquals(self.lhs, zero).proven() and InSet(self.lhs, Complex).proven()): # We have 0/x with complex x ≠ 0 return self.conclude_via_zero_factor() else: err_str = err_str + ( "Case: rhs = 0. " "Although rhs = 0, either the lhs {0} is not known to " "be non-zero or {0} is not known to be in the complex " "numbers (or both). Try proving one or both of those " "claims first.\n".format(self.lhs)) #-- -------------------------------------------------------- --# #-- Case (3): very simple version of x|xy or x|yx --# #-- -------------------------------------------------------- --# # return self.conclude_via_factor(assumptions) try: return self.conclude_via_factor() except Exception as e: err_str = err_str + ( "Case: x|xy. This possible case returned the following " "error message: {0} \n".format(e)) pass #-- -------------------------------------------------------- --# #-- Case (4): x^n|y^n if x|y --# #-- -------------------------------------------------------- --# if (isinstance(self.lhs, Exp) and isinstance(self.rhs, Exp) and Equals(self.lhs.exponent, self.rhs.exponent) and Divides(self.lhs.base, self.rhs.base).proven()): try: deduce_number_set(self.lhs.base) deduce_number_set(self.rhs.base) deduce_number_set(self.lhs.exponent) except UnsatisfiedPrerequisites: pass if (InSet(self.lhs.base, Integer).proven() and InSet(self.rhs.base, Integer).proven() and InSet(self.lhs.exponent, NaturalPos).proven()): return (Divides(self.lhs.base, self.rhs.base).introduce_common_exponent( self.lhs.exponent)) else: err_str = err_str + ( "Case: (x^n) | (y^n). One or more of the conditions " "(such as domain requirements or x|y) were not " "already proven. Check the conditions for the " "common_exponent_introduction theorem in the " "number/divisibility package.\n") else: err_str = err_str + ( "Case: (x^n) | (y^n). Does not appear applicable.\n") """ # This case should be handled on the "side-effect" end. #-- -------------------------------------------------------- --# #-- Case (5): x|y if x^n|y^n (for some small pos nat n) --# #-- -------------------------------------------------------- --# possible_exps = range(2,10) for e in possible_exps: # print("exp = {}".format(e)) if (Divides(Exp(self.lhs, num(e)), Exp(self.rhs, num(e))). proven(assumptions)): # print(" Divides found for exp = {}".format(test_exp)) return (Divides(Exp(self.lhs, test_exp), Exp(self.rhs, test_exp)). eliminate_common_exponent(assumptions=assumptions)) err_str = err_str + ( "Case: x|y where we already have (x^n)|(y^n). " "Does not appear applicable.\n") """ #-- -------------------------------------------------------- --# #-- Case (6): x|z with x|y and y|z known or assumed --# #-- -------------------------------------------------------- --# # Seek out the appropriate x|y and y|z and use transitivity # to get x|z, utilizing the conclude_via_transitivity() method # available for instances of TransitiveRelation try: return self.conclude_via_transitivity() except Exception as e: err_str = err_str + ( "Case: transitivity search. In attempting to use " "conclude_via_transitivity(), obtained the following " "error message: {0}.".format(e)) pass raise ProofFailure(self, defaults.assumptions, err_str)
def deduce_in_number_set(self, number_set, **defaults_config): ''' Given a number set number_set, attempt to prove that the given expression is in that number set using the appropriate closure theorem. ''' from proveit import a, b from proveit.numbers import Less, zero from proveit.numbers.division import ( div_rational_closure, div_rational_nonzero_closure, div_rational_pos_closure, div_rational_pos_from_double_neg, div_rational_neg_from_neg_denom, div_rational_neg_from_neg_numer, div_rational_nonneg_closure, div_rational_nonneg_from_double_neg, div_rational_nonpos_from_neg_denom, div_rational_nonpos_from_nonpos_numer, div_real_closure, div_real_nonzero_closure, div_real_pos_closure, div_real_pos_from_double_neg, div_real_neg_from_neg_denom, div_real_neg_from_neg_numer, div_real_nonneg_closure, div_real_nonneg_from_double_neg, div_real_nonpos_from_neg_denom, div_real_nonpos_from_nonpos_numer, div_complex_nonzero_closure, div_complex_closure) thm = None if number_set == Rational: thm = div_rational_closure elif number_set == RationalNonZero: thm = div_rational_nonzero_closure elif number_set == RationalPos: deduce_number_set(self.denominator) if Less(self.denominator, zero).proven(): thm = div_rational_pos_from_double_neg else: thm = div_rational_pos_closure elif number_set == RationalNeg: deduce_number_set(self.denominator) if Less(self.denominator, zero).proven(): thm = div_rational_neg_from_neg_denom else: thm = div_rational_neg_from_neg_numer elif number_set == RationalNonNeg: deduce_number_set(self.denominator) if Less(self.denominator, zero).proven(): thm = div_rational_nonneg_from_double_neg else: thm = div_rational_nonneg_closure elif number_set == RationalNonPos: deduce_number_set(self.denominator) if Less(self.denominator, zero).proven(): thm = div_rational_nonpos_from_neg_denom else: thm = div_rational_nonpos_from_neg_numer elif number_set == Real: thm = div_real_closure elif number_set == RealNonZero: thm = div_real_nonzero_closure elif number_set == RealPos: deduce_number_set(self.denominator) if Less(self.denominator, zero).proven(): thm = div_real_pos_from_double_neg else: thm = div_real_pos_closure elif number_set == RealNeg: deduce_number_set(self.denominator) if Less(self.denominator, zero).proven(): thm = div_real_neg_from_neg_denom else: thm = div_real_neg_from_neg_numer elif number_set == RealNonNeg: deduce_number_set(self.denominator) if Less(self.denominator, zero).proven(): thm = div_real_nonneg_from_double_neg else: thm = div_real_nonneg_closure elif number_set == RealNonPos: deduce_number_set(self.denominator) if Less(self.denominator, zero).proven(): thm = div_real_nonpos_from_neg_denom else: thm = div_real_nonpos_from_nonpos_numer elif number_set == ComplexNonZero: thm = div_complex_nonzero_closure elif number_set == Complex: thm = div_complex_closure if thm is not None: return thm.instantiate({a: self.numerator, b: self.denominator}) raise NotImplementedError( "'Div.deduce_in_number_set()' not implemented for the %s set" % str(number_set))
def eliminate_common_factors(self, **defaults_config): ''' Eliminate all factors in common between the divisor and the dividend. For example, from (k a)|(k b), derive and return a|b. k must be a non-zero complex number. ''' from . import common_factor_elimination from proveit.numbers import Mult, one if self.lhs == self.rhs: # From x | x return 1 | 1. It's vacuous, but whatever. return Divides(one, one).prove() elif (isinstance(self.lhs, Mult) and isinstance(self.rhs, Mult)): # Handle the basic case in which the divisor and # the dividend are each the product of two factors and # the first of these is in common between them. if (self.lhs.operands.is_double() and self.rhs.operands.is_double()): lhs1 = self.lhs.operands[0] lhs2 = self.lhs.operands[1] rhs1 = self.rhs.operands[0] rhs2 = self.rhs.operands[1] deduce_number_set(lhs1) if (lhs1 == rhs1 and InSet(lhs1, Complex).proven() and NotEquals(lhs1, zero).proven()): return common_factor_elimination.instantiate({ a: lhs2, b: rhs2, k: lhs1 }) # Try to convert it to the basic case via factorization # and try again. rhs_factors = set(self.rhs.operands.entries) common_factors = [ factor for factor in self.lhs.factors if factor in rhs_factors ] # Pull the common factors out to the front. if len(common_factors) == 0: return self.prove() # No common factors to eliminate. lhs_factorization = self.lhs.factorization(common_factors, pull='left', group_factors=True, group_remainder=True, preserve_all=True) rhs_factorization = self.rhs.factorization(common_factors, pull='left', group_factors=True, group_remainder=True, preserve_all=True) # Prove this "divides" but the substitute factorized forms. divides_proof = self.prove() if lhs_factorization.lhs != lhs_factorization.rhs: divides_proof = lhs_factorization.sub_right_side_into( divides_proof) if rhs_factorization.lhs != rhs_factorization.rhs: divides_proof = rhs_factorization.sub_right_side_into( divides_proof) lhs1, lhs2 = lhs_factorization.rhs.operands rhs1, rhs2 = rhs_factorization.rhs.operands return common_factor_elimination.instantiate({ a: lhs2, b: rhs2, k: lhs1 }) elif isinstance(self.lhs, Mult) and self.rhs in self.lhs.factors: # From (k z) | k return z | 1. Why not? dividend = Mult(self.rhs, one) divides = Divides(self.lhs, dividend) divides_proof = divides.eliminate_common_factors() return divides_proof.inner_expr().lhs.dividend.one_elimination(1) elif isinstance(self.rhs, Mult) and self.lhs in self.rhs.factors: # From (k z) | k return z | 1. Why not? divisor = Mult(self.lhs, one) divides = Divides(divisor, self.rhs) divides_proof = divides.eliminate_common_factors() return divides_proof.inner_expr().lhs.divisor.one_elimination(1) # There are no common factors. return self.prove()
def exponent_combination(self, start_idx=None, end_idx=None, **defaults_config): ''' Equates $a^m/a^n$ to $a^{m-n} or $a^c/b^c$ to $(a/b)^c$. ''' from proveit.logic import InSet from proveit.numbers import Exp from proveit.numbers.exponentiation import (quotient_of_posnat_powers, quotient_of_pos_powers, quotient_of_real_powers, quotient_of_complex_powers) if (isinstance(self.numerator, Exp) and isinstance(self.denominator, Exp)): if self.numerator.base == self.denominator.base: # Same base: (a^b/a^c) = a^{b-c} same_base = self.numerator.bas exponents = (self.numerator.exponent, self.denominator.exponent) # Find out the known type of the exponents. possible_exponent_types = [NaturalPos, RealPos, Real, Complex] for exponent in exponents: deduce_number_set(exponent) while len(possible_exponent_types) > 1: exponent_type = possible_exponent_types[0] if InSet(exponent, exponent_type).proven(): # This type is known for this exponent. break # We've eliminated a type from being known. possible_exponent_types.pop(0) known_exponent_type = possible_exponent_types[0] if known_exponent_type == NaturalPos: _m, _n = exponents return quotient_of_posnat_powers.instantiate({ a: same_base, m: _m, n: _n }) else: _b, _c = exponents if known_exponent_type == RealPos: thm = quotient_of_pos_powers elif known_exponent_type == Real: thm = quotient_of_real_powers else: # Complex is the default thm = quotient_of_complex_powers thm.instantiate({a: same_base, b: _b, c: _c}) elif self.numerator.exponent == self.denominator.exponent: # Same exponent: (a^c/b^c) = (a/b)^c same_exponent = self.numerator.exponent bases = (self.numerator.base, self.denominator.base) # Combining the exponents in this case is the reverse # of disibuting an exponent. quotient = Div(*bases).with_matching_style(self) exp = Exp(quotient, same_exponent) return exp.distribution().derive_reversed() else: raise NotImplementedError("Need to implement degenerate cases " "of a^b/a and a/a^b.")