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 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 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 do_reduced_simplification(self, assumptions=USE_DEFAULTS): ''' For the case Abs(x) where the operand x is already known to be or assumed to be a non-negative real, derive and return this Abs expression equated with the operand itself: |- Abs(x) = x. For the case where x is already known or assumed to be a negative real, return the Abs expression equated with the negative of the operand: |- Abs(x) = -x. Assumptions may be necessary to deduce necessary conditions for the simplification. ''' from proveit.numbers import greater, greater_eq, Mult, Neg from proveit.numbers import (zero, Natural, NaturalPos, RealNeg, RealNonNeg, RealPos) # among other things, convert any assumptions=None # to assumptions=() (thus averting len(None) errors) assumptions = defaults.checked_assumptions(assumptions) #-- -------------------------------------------------------- --# #-- Case (1): Abs(x) where entire operand x is known or --# #-- assumed to be non-negative Real. --# #-- -------------------------------------------------------- --# if InSet(self.operand, RealNonNeg).proven(assumptions=assumptions): # Entire operand is known to be or assumed to be a # non-negative real, so we can return Abs(x) = x return self.abs_elimination(operand_type='non-negative', assumptions=assumptions) #-- -------------------------------------------------------- --# #-- Case (2): Abs(x) where entire operand x is known or --# #-- assumed to be a negative Real. --# #-- -------------------------------------------------------- --# if InSet(self.operand, RealNeg).proven(assumptions=assumptions): # Entire operand is known to be or assumed to be a # negative real, so we can return Abs(x) = -x return self.abs_elimination(operand_type='negative', assumptions=assumptions) #-- -------------------------------------------------------- --# # -- Case (3): Abs(x) where entire operand x is not yet known --* #-- to be a non-negative Real, but can easily be --# #-- proven to be a non-negative Real because it is --# # -- (a) known or assumed to be ≥ 0 or #-- (b) known or assumed to be in a subset of the --# #-- non-negative Real numbers, or --# #-- (c) the addition or product of operands, all --# #-- of which are known or assumed to be non- --# # -- negative real numbers. TBA! #-- -------------------------------------------------------- --# if (greater(self.operand, zero).proven(assumptions=assumptions) and not greater_eq(self.operand, zero).proven(assumptions=assumptions)): greater_eq(self.operand, zero).prove(assumptions=assumptions) # and then it will get picked up in the next if() below if greater_eq(self.operand, zero).proven(assumptions=assumptions): from proveit.numbers.number_sets.real_numbers import ( in_real_non_neg_if_greater_eq_zero) in_real_non_neg_if_greater_eq_zero.instantiate( {a: self.operand}, assumptions=assumptions) return self.abs_elimination(operand_type='non-negative', assumptions=assumptions) if self.operand in InSet.known_memberships.keys(): for kt in InSet.known_memberships[self.operand]: if kt.is_sufficient(assumptions): if is_equal_to_or_subset_eq_of( kt.expr.operands[1], equal_sets=[RealNonNeg, RealPos], subset_eq_sets=[Natural, NaturalPos, RealPos], assumptions=assumptions): InSet(self.operand, RealNonNeg).prove(assumptions=assumptions) return self.abs_elimination( operand_type='non-negative', assumptions=assumptions) if isinstance(self.operand, Add) or isinstance(self.operand, Mult): count_of_known_memberships = 0 count_of_known_relevant_memberships = 0 for op in self.operand.operands: if op in InSet.known_memberships.keys(): count_of_known_memberships += 1 if (count_of_known_memberships == self.operand.operands.num_entries()): for op in self.operand.operands: op_temp_known_memberships = InSet.known_memberships[op] for kt in op_temp_known_memberships: if (kt.is_sufficient(assumptions) and is_equal_to_or_subset_eq_of( kt.expr.operands[1], equal_sets=[RealNonNeg, RealPos], subset_eq_sets=[ Natural, NaturalPos, RealPos, RealNonNeg ], assumptions=assumptions)): count_of_known_relevant_memberships += 1 break if (count_of_known_relevant_memberships == self.operand.operands.num_entries()): # Prove that the sum or product is in # RealNonNeg and then instantiate abs_elimination. for op in self.operand.operands: InSet(op, RealNonNeg).prove(assumptions=assumptions) return self.abs_elimination(assumptions=assumptions) #-- -------------------------------------------------------- --# #-- Case (4): Abs(x) where operand x can easily be proven --# #-- to be a negative Real number because -x is --# #-- known to be in a subset of the positive Real --# #-- numbers. --# #-- -------------------------------------------------------- --# negated_op = None if isinstance(self.operand, Neg): negated_op = self.operand.operand else: negated_op = Neg(self.operand) negated_op_simp = negated_op.simplification( assumptions=assumptions).rhs if negated_op_simp in InSet.known_memberships.keys(): from proveit.numbers.number_sets.real_numbers import ( neg_is_real_neg_if_pos_is_real_pos) for kt in InSet.known_memberships[negated_op_simp]: if kt.is_sufficient(assumptions): if is_equal_to_or_subset_eq_of( kt.expr.operands[1], equal_sets=[RealNonNeg, RealPos], subset_sets=[NaturalPos, RealPos], subset_eq_sets=[NaturalPos, RealPos], assumptions=assumptions): InSet(negated_op_simp, RealPos).prove(assumptions=assumptions) neg_is_real_neg_if_pos_is_real_pos.instantiate( {a: negated_op_simp}, assumptions=assumptions) return self.abs_elimination(operand_type='negative', assumptions=assumptions) # for updating our equivalence claim(s) for the # remaining possibilities from proveit import TransRelUpdater eq = TransRelUpdater(self, assumptions) return eq.relation
def deduce_positive(self, **defaults_config): # Deduce that this absolute value is greater than zero # given its argument is not equal zero. from proveit.numbers import RealPos, zero, greater InSet(self, RealPos).prove() return greater(self, zero).prove()
def conclude(self, **defaults_config): from proveit.numbers import zero, greater if (InSet(self.element, Real).proven() and greater(self.element, zero).proven()): return self.conclude_as_last_resort() return NumberMembership.conclude(self)
def bound_via_operand_bound(self, operand_relation, **defaults_config): ''' For simple cases, deduce a bound on this Logarithmic (Log) object given a bound on its operand. For example, suppose x = Log(2, y) and we know that y >= 2. Then x.bound_via_operand_bound(y >= 2) returns x >= Log(2, 2) = 1. This method currently works MAINLY for expressions of the form Log(a, b) where a > 1, where we know something of the form b < y or b <= y or b > y or b >= y involving the 'antilog' argument b. b itself will also need to be in RealPos, which might need to be pre-proven or provided as an assumption. Future development should address cases where 0 < a < 1, which result in decreasing logarithmic functions. Also see NumberOperation.deduce_bound() and compare to the bound_via_operand_bound() method found in the Div, Neg, and Exp classes. ''' from proveit import Judgment from proveit import a, x, y from proveit.numbers import (zero, one, 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.antilog: raise ValueError( "In Log.bound_via_operand_bound(), the left side of " "the 'operand_relation' argument {0} is expected to " "match the Log antilog operand {1}.".format( operand_relation, self.antilog)) # assign x and y subs according to std Less or LessEq relations _x_sub = operand_relation.normal_lhs _y_sub = operand_relation.normal_rhs _a_sub = self.base # Multiple cases to eventually consider but for now # we only consider four related Log(a, x) vs. Log(a, y) # cases for the increasing (base a > 1) version of a Log fxn: # (1) base a > 1, 0 < x < y # (2) base a > 1, 0 < x <= y # (3) base a > 1, 0 < y < x # (4) base a > 1, 0 < y <= x # Cases (1)-(4): 0 < x < y or 0 < y < x if (greater(_a_sub, one).proven() and greater(_x_sub, zero).proven()): if isinstance(operand_relation, Less): from proveit.numbers.logarithms import (log_increasing_less) bound = log_increasing_less.instantiate({ x: _x_sub, y: _y_sub, a: _a_sub }) elif isinstance(operand_relation, LessEq): from proveit.numbers.logarithms import (log_increasing_less_eq) bound = log_increasing_less_eq.instantiate({ x: _x_sub, y: _y_sub, a: _a_sub }) else: raise TypeError( "In Log.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 Log.bound_via_operand_bound(), a " "specific matching case was not found for {0}. " "Check/confirm that {1} > 1 and {2} is a positive " "Real".format(self, _a_sub, _x_sub)) 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 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): if (InSet(self.element, Integer).proven() and greater(self.element, zero).proven()): return self.conclude_as_last_resort() return NumberMembership.conclude(self)
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