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 divide_both_sides(self, divisor, *, simplify=True, assumptions=USE_DEFAULTS): ''' Divide both sides of the relation by the 'divisor'. ''' from proveit.numbers import Less, zero from proveit.numbers.division import (div_pos_lesseq, div_neg_lesseq) if Less(zero, divisor).proven(assumptions): new_rel = div_pos_lesseq.instantiate( { a: divisor, x: self.lower, y: self.upper }, assumptions=assumptions)._simplify_both_sides( simplify=simplify, assumptions=assumptions) elif Less(divisor, zero).proven(assumptions): new_rel = div_neg_lesseq.instantiate( { a: divisor, x: self.lower, y: self.upper }, assumptions=assumptions)._simplify_both_sides( simplify=simplify, assumptions=assumptions) else: raise Exception("Cannot 'divide' a LessEq relation without " "knowing whether the divisor is greater than " "or less than zero.") return new_rel.with_mimicked_style(self)
def right_mult_both_sides(self, multiplier, *, simplify=True, assumptions=USE_DEFAULTS): ''' Multiply both sides of the relation by the 'multiplier' on the right. ''' from proveit.numbers import LessEq, zero from proveit.numbers.multiplication import (right_mult_pos_less, right_mult_nonneg_less, right_mult_neg_less, right_mult_nonpos_less) if Less(zero, multiplier).proven(assumptions): new_rel = right_mult_pos_less.instantiate( { a: multiplier, x: self.lower, y: self.upper }, assumptions=assumptions)._simplify_both_sides( simplify=simplify, assumptions=assumptions) elif Less(multiplier, zero).proven(assumptions): new_rel = right_mult_neg_less.instantiate( { a: multiplier, x: self.lower, y: self.upper }, assumptions=assumptions)._simplify_both_sides( simplify=simplify, assumptions=assumptions) elif LessEq(zero, multiplier).proven(assumptions): new_rel = right_mult_nonneg_less.instantiate( { a: multiplier, x: self.lower, y: self.upper }, assumptions=assumptions)._simplify_both_sides( simplify=simplify, assumptions=assumptions) elif LessEq(multiplier, zero).proven(assumptions): new_rel = right_mult_nonpos_less.instantiate( { a: multiplier, x: self.lower, y: self.upper }, assumptions=assumptions)._simplify_both_sides( simplify=simplify, assumptions=assumptions) else: raise Exception( "Cannot 'right_mult_both_sides' a Less relation without " "knowing the multiplier's relation with zero.") return new_rel.with_mimicked_style(self)
def exponentiate_both_sides(self, exponent, *, simplify=True, assumptions=USE_DEFAULTS): ''' Exponentiate both sides of the relation by the 'exponent'. ''' from proveit.numbers import Less, zero from proveit.numbers.exponentiation import (exp_pos_lesseq, exp_nonneg_lesseq, exp_neg_lesseq, exp_nonpos_lesseq) if Less(exponent, zero).proven(assumptions): new_rel = exp_pos_lesseq.instantiate( { a: exponent, x: self.lower, y: self.upper }, assumptions=assumptions)._simplify_both_sides( simplify=simplify, assumptions=assumptions) elif Less(exponent, zero).proven(assumptions): new_rel = exp_neg_lesseq.instantiate( { a: exponent, x: self.lower, y: self.upper }, assumptions=assumptions)._simplify_both_sides( simplify=simplify, assumptions=assumptions) elif LessEq(exponent, zero).proven(assumptions): new_rel = exp_nonneg_lesseq.instantiate( { a: exponent, x: self.lower, y: self.upper }, assumptions=assumptions)._simplify_both_sides( simplify=simplify, assumptions=assumptions) elif LessEq(exponent, zero).proven(assumptions): new_rel = exp_nonpos_lesseq.instantiate( { a: exponent, x: self.lower, y: self.upper }, assumptions=assumptions)._simplify_both_sides( simplify=simplify, assumptions=assumptions) else: raise Exception("Cannot 'exponentiate' a Less relation without " "knowing the exponent's relation with zero") return new_rel.with_mimicked_style(self)
def conclude(self, assumptions): from proveit.logic import InSet, NotEquals from proveit.numbers import (Rational, RationalNonZero, RationalPos, RationalNeg, RationalNonNeg, Less, greater, greater_eq, zero) # If we known the element is in Q, we may be able to # prove that is in RationalNonZero, RationalPos, RationalNeg, or # RationalNonNeg if we know its relation to zero. if (self.number_set != Rational and InSet(self.element, Rational).proven(assumptions)): if self.number_set == RationalNonZero: if NotEquals(self.element, zero).proven(assumptions): from . import non_zero_rational_is_rational_non_zero return non_zero_rational_is_rational_non_zero.instantiate( {q: self.element}, assumptions=assumptions) if self.number_set == RationalPos: if greater(self.element, zero).proven(assumptions): from . import positive_rational_is_rational_pos return positive_rational_is_rational_pos.instantiate( {q: self.element}, assumptions=assumptions) if self.number_set == RationalNeg: if Less(self.element, zero).proven(): from . import negative_rational_is_rational_neg return negative_rational_is_rational_neg.instantiate( {q: self.element}, assumptions=assumptions) if self.number_set == RationalNonNeg: if greater_eq(self.element, zero).proven(): from . import non_neg_rational_in_rational_neg return non_neg_rational_in_rational_neg.instantiate( {q: self.element}, assumptions=assumptions) # Resort to the default NumberMembership.conclude strategies. return NumberMembership.conclude(self, assumptions)
def not_equal(self, other, **defaults_config): from proveit.numbers import Less from proveit.numbers.ordering import less_is_not_eq _a, _b = Less.sorted_items([self, other]) not_eq_stmt = less_is_not_eq.instantiate({a: _a, b: _b}) if not_eq_stmt.lhs != self: # We need to reverse the statement. return not_eq_stmt.derive_reversed() return not_eq_stmt
def derive_element_in_restricted_number_set(self, **defaults_config): ''' From (member in Interval(x, y)), where x ≥ 0 or y ≤ 0, deduce that the element is in Natural, NaturalPos, IntegerNeg, or IntegerNonPos as appropriate. ''' _a = self.domain.lower_bound _b = self.domain.upper_bound _n = self.element # We wish to deduce a fact based upon the following # membership fact: self.expr.prove() if (not InSet(_a, Natural).proven() and not InSet(_b, IntegerNonPos).proven()): # If we don't know that a ≥ 0 or b ≤ 0, we can't prove # the element is in either restricted number set # (NaturalPos or IntegerNeg). So, try to sort a, b, 0 # to work this out. LessEq.sort([_a, _b, zero]) if InSet(_a, Natural).proven(): try: _a.deduce_in_number_set(NaturalPos, automation=False) except Exception: pass if InSet(_a, NaturalPos).proven(): # member in N^{>0} lower_bounding = self.derive_element_lower_bound() a_bounding = greater(_a, zero) lower_bounding.apply_transitivity(a_bounding) return InSet(_n, NaturalPos).prove() else: # member in N lower_bounding = self.derive_element_lower_bound() a_bounding = greater_eq(_a, zero) lower_bounding.apply_transitivity(a_bounding) return InSet(_n, Natural).prove() if InSet(_b, IntegerNonPos).proven(): try: _b.deduce_in_number_set(IntegerNeg, automation=False) except Exception: pass if InSet(_b, IntegerNeg).proven(): # member in Z^{<0} upper_bounding = self.derive_element_upper_bound() b_bounding = Less(_b, zero) upper_bounding.apply_transitivity(b_bounding) return InSet(_n, IntegerNeg).prove() else: # member in Z^{≤0} upper_bounding = self.derive_element_upper_bound() b_bounding = LessEq(_b, zero) upper_bounding.apply_transitivity(b_bounding) return InSet(_n, IntegerNonPos).prove()
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 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 not_equal(self, other, assumptions=USE_DEFAULTS): from proveit.numbers import Less from proveit.numbers.ordering import less_is_not_eq _a, _b = Less.sorted_items([self, other], assumptions=assumptions) not_eq_stmt = less_is_not_eq.instantiate({ a: _a, b: _b }, assumptions=assumptions) if not_eq_stmt.lhs != self: # We need to reverse the statement. return not_eq_stmt.derive_reversed(assumptions) return not_eq_stmt
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 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 conclude(self, **defaults_config): from proveit.numbers import zero, Less if (InSet(self.element, Real).proven() and Less(self.element, zero).proven()): return self.conclude_as_last_resort() return NumberMembership.conclude(self)
def greater(a, b): ''' Return an expression representing a > b, internally represented as b < a but with a style that reverses the direction. ''' return Less(b, a).with_styles(direction='reversed')
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 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 derive_element_in_restricted_number_set(self, **defaults_config): ''' From (element in IntervalXX(x, y)), where x ≥ 0 or y ≤ 0, derive that the element is in RealPos, RealNeg, RealNonPos, or RealNonNeg as appropriate. ''' from proveit.numbers import (zero, RealPos, RealNonNeg, RealNeg, RealNonPos) from proveit.numbers import Less, LessEq, greater, greater_eq _a = self.domain.lower_bound _b = self.domain.upper_bound _n = self.element # We wish to deduce a fact based upon the following # membership fact: self.expr.prove() if (not InSet(_a, RealNonNeg).proven() and not InSet(_b, RealNonPos).proven()): try: _a.deduce_in_number_set(RealNonNeg, automation=False) except Exception: pass try: _b.deduce_in_number_set(RealNonPos, automation=False) except Exception: pass if (not InSet(_a, RealNonNeg).proven() and not InSet(_b, RealNonPos).proven()): # If we don't know that a ≥ 0 or b ≤ 0, we can't prove # the element is in either restricted number set # (NaturalPos or IntegerNeg). So, try to sort a, b, 0 # to work this out. LessEq.sort([_a, _b, zero]) if InSet(_a, RealNonNeg).proven(): try: _a.deduce_in_number_set(RealPos, automation=False) except Exception: pass lower_bound = self.derive_element_lower_bound() a_bound = greater_eq(_a, zero) if InSet(_a, RealPos).proven(): a_bound = greater(_a, zero) lower_bound.apply_transitivity(a_bound) if (isinstance(self, IntervalOO) or isinstance(self, IntervalOC) or InSet(_a, RealPos).proven()): # member in R^{>0} return InSet(_n, RealPos).prove() else: # member in R^{≥0} return InSet(_n, RealNonNeg).prove() if InSet(_b, RealNonPos).proven(): try: _b.deduce_in_number_set(RealNeg, automation=False) except Exception: pass upper_bound = self.derive_element_upper_bound() b_bound = LessEq(_b, zero) if InSet(_b, RealNeg).proven(): b_bound = Less(_b, zero) upper_bound.apply_transitivity(b_bound) if (isinstance(self, IntervalOO) or isinstance(self, IntervalCO) or InSet(_b, RealNeg).proven()): # member in R^{<0} return InSet(_n, RealNeg).prove() else: # member in R^{≤0} return InSet(_n, RealNonPos).prove()
def bound_via_operand_bound(self, operand_relation, **defaults_config): ''' For simple cases, deduce a bound on this Exp object given a bound on its operand. For example, suppose x = Exp(y, 2) and we know that y >= 2. Then x.bound_via_operand_bound(y >= 2) returns x >= 2^2 = 4. This method currently works MAINLY for expressions of the form Exp(x, a) for non-negative real x and real exponent 'a', where we know something of the form x < y (or x ≤ y, x > y, x ≥ y) involving the base of the exponential expression. The result also depends on knowing the relationship between the exponent 'a' and zero, which might need to be pre-proven or provided as an assumption (e.g. in the form 'a > 0' or InSet(a, RealNeg), etc). A special case also deals with a negative base raised to the power of 2. This method also works for special cases of the form Exp(a, x), where a > 1 and the operand_relation involve the exponent x. Future development will address operand_relations involving the exponent x in expressions of the form a^x when the base 0 < a < 1, and expand the special negative base case to include all even and odd exponent cases. Also see NumberOperation.deduce_bound and compare to the bound_via_operand_bound() method found in the Div and Neg classes. ''' from proveit import Judgment from proveit.numbers import ( two, greater, greater_eq, Less, LessEq, NumberOrderingRelation, RealNonNeg) if isinstance(operand_relation, Judgment): operand_relation = operand_relation.expr if not isinstance(operand_relation, NumberOrderingRelation): raise TypeError( "In Exp.bound_via_operand_bound(), the " "'operand_relation' argument is expected to be a number " "relation (<, >, ≤, or ≥), but instead was {}.". format(operand_relation)) lhs = operand_relation.lhs # should be able to generalize this later # no need to limit to just lhs, right? if lhs != self.base and lhs != self.exponent: raise ValueError( "In Exp.bound_via_operand_bound(), the left side of " "the 'operand_relation' argument {0} is expected to " "match either the Exp base operand {1} or the " "Exp exponent operand {2}.". format(operand_relation, self.base, self.exponent)) # assign x and y subs according to std Less or LessEq relations _x_sub = operand_relation.normal_lhs _y_sub = operand_relation.normal_rhs if lhs == self.base: _a_sub = self.exponent else: _a_sub = self.base # I. Default case: the user-supplied operand relation involves # the BASE of the Exp expression x^a if lhs == self.base: # Several cases to consider: # (1) a > 0, 0 ≤ x < y # (2) a > 0, 0 ≤ x ≤ y # (3) a ≥ 0, 0 < x < y # (4) a ≥ 0, 0 < x ≤ y # (5) a < 0, 0 < x < y # (6) a < 0, 0 < x ≤ y # (7) a ≤ 0, 0 < x < y # (8) a ≤ 0, 0 < x ≤ y # ===================== # (9) a = 2, y < x < 0 # (10) a = 2, y ≤ x < 0 # Cases (1) and (2): exponent a > 0 if (greater(_a_sub, zero).proven() and greater_eq(_x_sub, zero).proven()): if isinstance(operand_relation, Less): from proveit.numbers.exponentiation import exp_pos_less bound = exp_pos_less.instantiate( {x: _x_sub, y: _y_sub, a: _a_sub}) elif isinstance(operand_relation, LessEq): from proveit.numbers.exponentiation import exp_pos_lesseq bound = exp_pos_lesseq.instantiate( {x: _x_sub, y: _y_sub, a: _a_sub}) else: raise TypeError( "In Exp.bound_via_operand_bound(), the 'operand_relation' " "argument is expected to be a 'Less', 'LessEq', 'greater', " "or 'greater_eq' relation. Instead we have {}.". format(operand_relation)) # Cases (3) and (4): exponent a ≥ 0 elif (greater_eq(_a_sub, zero).proven() and greater(_x_sub, zero).proven()): if isinstance(operand_relation, Less): from proveit.numbers.exponentiation import exp_nonneg_less bound = exp_nonneg_less.instantiate( {x: _x_sub, y: _y_sub, a: _a_sub}) elif isinstance(operand_relation, LessEq): from proveit.numbers.exponentiation import exp_nonneg_lesseq bound = exp_nonneg_lesseq.instantiate( {x: _x_sub, y: _y_sub, a: _a_sub}) else: raise TypeError( "In Exp.bound_via_operand_bound(), the 'operand_relation' " "argument is expected to be a 'Less', 'LessEq', 'greater', " "or 'greater_eq' relation. Instead we have {}.". format(operand_relation)) # Cases (5) and (6): exponent a < 0 elif (Less(_a_sub, zero).proven() and greater(_x_sub, zero).proven()): if isinstance(operand_relation, Less): from proveit.numbers.exponentiation import exp_neg_less bound = exp_neg_less.instantiate( {x: _x_sub, y: _y_sub, a: _a_sub}) elif isinstance(operand_relation, LessEq): from proveit.numbers.exponentiation import exp_neg_lesseq bound = exp_neg_lesseq.instantiate( {x: _x_sub, y: _y_sub, a: _a_sub}) else: raise TypeError( "In Exp.bound_via_operand_bound(), the 'operand_relation' " "argument is expected to be a 'Less', 'LessEq', 'greater', " "or 'greater_eq' relation. Instead we have {}.". format(operand_relation)) # Cases (7) and (8): exponent a ≤ 0 elif (LessEq(_a_sub, zero).proven() and greater(_x_sub, zero).proven()): if isinstance(operand_relation, Less): from proveit.numbers.exponentiation import exp_nonpos_less bound = exp_nonpos_less.instantiate( {x: _x_sub, y: _y_sub, a: _a_sub}) elif isinstance(operand_relation, LessEq): from proveit.numbers.exponentiation import exp_nonpos_lesseq bound = exp_nonpos_lesseq.instantiate( {x: _x_sub, y: _y_sub, a: _a_sub}) else: raise TypeError( "In Exp.bound_via_operand_bound(), the 'operand_relation' " "argument is expected to be a 'Less', 'LessEq', 'greater', " "or 'greater_eq' relation. Instead we have {}.". format(operand_relation)) # Cases (9) and (10): exponent a = 2 # with x < y < 0 or x ≤ y < 0 elif (_a_sub == two and Less(_y_sub, zero).proven()): if isinstance(operand_relation, Less): from proveit.numbers.exponentiation import ( exp_even_neg_base_less) bound = exp_even_neg_base_less.instantiate( {x: _x_sub, y: _y_sub, a: _a_sub}) elif isinstance(operand_relation, LessEq): from proveit.numbers.exponentiation import ( exp_even_neg_base_lesseq) bound = exp_even_neg_base_lesseq.instantiate( {x: _x_sub, y: _y_sub, a: _a_sub}) else: raise TypeError( "In Exp.bound_via_operand_bound(), the 'operand_relation' " "argument is expected to be a 'Less', 'LessEq', 'greater', " "or 'greater_eq' relation. Instead we have {}.". format(operand_relation)) else: raise ValueError( "In calling Exp.bound_via_operand_bound(), a " "specific matching case was not found for {}.". format(self)) # II. 2nd main case: the user-supplied operand relation involves # the EXPONENT of the Exp expression a^x elif lhs == self.exponent: # Several cases to consider (others to be developed) # considering the Exp expression a^x with a, x in Real: # (1) a > 1, x < y # (2) a > 1, x ≤ y # (3) a > 1, y < x # (4) a > 1, y ≤ x # Other cases to be developed involving base a < 1, # which produces a monotonically-decreasing function. # Cases (1)-(4): base a > 1, a^x monotonically increasing if (greater(_a_sub, one).proven() and InSet(_x_sub, Real).proven()): if isinstance(operand_relation, Less): from proveit.numbers.exponentiation import ( exp_monotonicity_large_base_less) bound = exp_monotonicity_large_base_less.instantiate( {x: _x_sub, y: _y_sub, a: _a_sub}) elif isinstance(operand_relation, LessEq): from proveit.numbers.exponentiation import ( exp_monotonicity_large_base_less_eq) bound = exp_monotonicity_large_base_less_eq.instantiate( {x: _x_sub, y: _y_sub, a: _a_sub}) else: raise TypeError( "In Exp.bound_via_operand_bound(), the 'operand_relation' " "argument is expected to be a 'Less', 'LessEq', 'greater', " "or 'greater_eq' relation. Instead we have {}.". format(operand_relation)) else: raise ValueError( "In Exp.bound_via_operand_bound(), either the " "base {0} is not known to be greater than 1 and/or " "the operand {1} is not known to be Real.". format(_a_sub, _x_sub)) else: raise ValueError("OOOPS!") if bound.rhs == self: return bound.with_direction_reversed() return bound
def conclude(self, **defaults_config): if (InSet(self.element, Integer).proven() and Less(self.element, zero).proven()): return self.conclude_as_last_resort() return NumberMembership.conclude(self)
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