def shallow_simplification(self, *, must_evaluate=False, **defaults_config): ''' For the simple binary case Max(a, b), returns a proven simplification equation for this Max expression assuming the operands 'a' and 'b' have been simplified and that we know or have assumed that either a >= b or b > a. If such relational knowledge is not to be had, we simply return the equation of the Max expression with itself. Cases with more than 2 operands are not yet handled. ''' from proveit.logic import Equals from proveit.numbers import greater_eq # We're only set up to deal with binary operator version if not self.operands.is_double(): # Default is no simplification if not a binary operation. return Equals(self, self).prove() # If binary and we know how operands 'a' and 'b' are related ... op_01, op_02 = self.operands[0], self.operands[1] if (greater_eq(op_01, op_02).proven() or greater_eq(op_02, op_01).proven()): from proveit import x, y from proveit.numbers.ordering import max_def_bin return max_def_bin.instantiate({x: op_01, y: op_02}) # Otherwise still no simplification. return Equals(self, self).prove()
def left_mult_both_sides(self, multiplier, *, simplify=True, assumptions=USE_DEFAULTS): ''' Multiply both sides of the relation by the 'multiplier' on the left. ''' from proveit.numbers import greater_eq, zero from proveit.numbers.multiplication import (left_mult_pos_lesseq, left_mult_neg_lesseq) if greater_eq(multiplier, zero).proven(assumptions): new_rel = left_mult_pos_lesseq.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 = left_mult_neg_lesseq.instantiate( { a: multiplier, x: self.lower, y: self.upper }, assumptions=assumptions)._simplify_both_sides( simplify=simplify, assumptions=assumptions) else: raise Exception( "Cannot 'left_mult_both_sides' a LessEq relation without " "knowing the multiplier'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 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 left_mult_both_sides(self, multiplier, **defaults_config): ''' Multiply both sides of the relation by the 'multiplier' on the left. ''' from proveit.numbers import greater_eq, zero, deduce_number_set from proveit.numbers.multiplication import ( weak_bound_via_right_factor_bound, reversed_weak_bound_via_right_factor_bound) was_reversed = False deduce_number_set(multiplier) if greater_eq(multiplier, zero).proven(): new_rel = weak_bound_via_right_factor_bound.instantiate({ a: multiplier, x: self.lower, y: self.upper }) elif LessEq(multiplier, zero).proven(): new_rel = reversed_weak_bound_via_right_factor_bound.instantiate({ a: multiplier, x: self.lower, y: self.upper }) was_reversed = True else: raise Exception( "Cannot left-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 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 _generateCoordOrderAssumptions(coords): from proveit.numbers import LessEq, greater_eq for prev_coord, next_coord in zip(coords[:-1], coords[1:]): yield LessEq(prev_coord, next_coord) yield greater_eq(next_coord, prev_coord)
def conclude(self, **defaults_config): from proveit.numbers import zero, greater_eq if (InSet(self.element, Real).proven() and greater_eq(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
def conclude(self, **defaults_config): if (InSet(self.element, Integer).proven() and greater_eq(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