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 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 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, LessEq, zero from proveit.numbers.exponentiation import (exp_pos_less, exp_nonneg_less, exp_neg_less, exp_nonpos_less) if Less(zero, exponent).proven(assumptions): new_rel = exp_pos_less.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_less.instantiate( { a: exponent, x: self.lower, y: self.upper }, assumptions=assumptions)._simplify_both_sides( simplify=simplify, assumptions=assumptions) elif LessEq(zero, exponent).proven(assumptions): new_rel = exp_nonneg_less.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_less.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 abs_elimination(self, operand_type=None, **defaults_config): ''' For some |x| expression, deduce either |x| = x (the default) OR |x| = -x (for operand_type = 'negative'). Assumptions may be needed to deduce x >= 0 or x < 0, respectively. ''' from proveit.numbers import LessEq, zero from . import abs_non_neg_elim, abs_neg_elim if operand_type is None: # LessEq.sort uses a bidirectional search which should # be fairly efficient, as long as there aren't too # many known relationship directly or indirectly involving # self.operand or zero. relation_with_zero = LessEq.sort([zero, self.operand]) if relation_with_zero.normal_lhs == zero: operand_type = 'non-negative' else: operand_type = 'negative' # deduce_non_neg(self.operand, assumptions) # NOT YET IMPLEMENTED if operand_type is None or operand_type == 'non-negative': return abs_non_neg_elim.instantiate({x: self.operand}) elif operand_type == 'negative': return abs_neg_elim.instantiate({x: self.operand}) else: raise ValueError( "Unsupported operand type for Abs.abs_elimination() " "method; operand type should be omitted or specified " "as 'negative' or 'non-negative', but instead was " "given as operand_type = {}.".format(operand_type))
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 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 conclude(self, **defaults_config): if (InSet(self.element, Integer).proven() and LessEq(self.element, zero).proven()): return self.conclude_as_last_resort() return NumberMembership.conclude(self)
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 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 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 conclude(self, **defaults_config): from proveit.numbers import zero, LessEq if (InSet(self.element, Real).proven() and LessEq(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 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