logo

Demonstrations for the theory of proveit.numbers.absolute_value

In [1]:
import proveit
from proveit import ExprRange, IndexedVar, used_vars, free_vars
from proveit import a, b, c, k, n, r, x, y, theta
from proveit.core_expr_types import x_1_to_n
from proveit.logic import And, Equals, InSet, NotEquals
from proveit.numbers import zero, one, two, three, e, i, pi
from proveit.numbers import (Abs, Add, frac, Exp, greater,
                            greater_eq, Less, LessEq, Mult, Neg)
from proveit.numbers import (Complex, Integer, Natural, NaturalPos,
                            Real, RealPos, RealNeg, RealNonNeg, RealNonPos)
from proveit.numbers.absolute_value  import abs_val_def_sqrt
%begin demonstrations

Absolute Value (and norm) $|a|$

Introduction

The concept of the absolute value of a real number, and its generalization to the norm if a complex number, is ubiquitous in mathematics and often serves a key role in various proofs. The absolute value of a real number $x$, or the norm of a complex number $x$, is captured with the operation `Abs(x)`, along with a standard axiomatic definition and a variety of related theorems.

Simple Expressions Involving Absolute Value $|a|$

It is straightforward to construct absolute value expressions. Here are some basic examples of such expressions:

In [2]:
# basic absolute value expression
abs_value_a = Abs(a)
abs_value_a:
In [3]:
# a version of the triangle inequality, where Abs() occurs repeatedly
# inside a larger expression
LessEq(Abs(Add(a, b)), Add(Abs(a),Abs(b)))

Common Attributes of an Absolute Value Abs Expression

Let's define a simple absolute value expression, $|a+b|$, and look at some of its attributes.

In [4]:
abs_value_a_plus_b = Abs(Add(a, b))
abs_value_a_plus_b:

We can use the `expr_info()` method to look at how the expression is constructed:

In [5]:
abs_value_a_plus_b.expr_info()
 core typesub-expressionsexpression
0Operationoperator: 1
operand: 3
1Literal
2ExprTuple3
3Operationoperator: 4
operands: 5
4Literal
5ExprTuple6, 7
6Variable
7Variable

The string version of the absolute value operator is simply the `Abs` string:

In [6]:
abs_value_a_plus_b.operator

And we can test for instances of `Abs()` in the usual way, by testing for the `Abs` class:

In [7]:
if isinstance(abs_value_a_plus_b, Abs):
    print("{} is an instance of Abs".format(abs_value_a_plus_b))
|a + b| is an instance of Abs

We can obtain the operand (the expression inside the absolute value) apart from the `Abs()` expression. We can also get a list of the variables and a separate list of the *free* variables in the expression (of course, in this expression, all the variables are also free variables):

In [8]:
# the "bare" operand
abs_value_a_plus_b.operand
In [9]:
# the operand in a (1-element) tuple
abs_value_a_plus_b.operands
In [10]:
# the variables appearing in the expression
used_vars(abs_value_a_plus_b)
In [11]:
# the free variables appearing in the expression
free_vars(abs_value_a_plus_b)

And of course, we can reach into the expression and substitute for variables with other variables in the usual way:

In [12]:
abs_value_c_plus_b = abs_value_a_plus_b.basic_replaced({a:c})
abs_value_c_plus_b:

Axioms

At this time, there is a single axiom for absolute value, defining $|x|=\sqrt{x^2}$ for real-valued $x$:

In [13]:
abs_val_def_sqrt

When the `Conditional` class has been established, the axiomatic definition will use the standard piece-wise definition, and the “square root of the square” could then be a theorem instead. The definition for $|a+bi|$, the norm of complex number $z=a+bi$, is still under development.

Theorems

Some typical absolute value-related theorems are illustrated below. The full complement of theorems appears in the `proveit.numbers/absolute_value/_theorems_.ipynb` notebook.

In [14]:
from proveit.numbers.absolute_value import (
        abs_nonzero_closure, abs_is_non_neg, abs_non_neg_elim, triangle_inequality)
In [15]:
# abs value of a non-zero value is a positive real number
abs_nonzero_closure
In [16]:
# abs value of any number is non-negative
abs_is_non_neg
In [17]:
# we can eliminate the abs value wrapper around a non-negative real number
abs_non_neg_elim
In [18]:
# abs value version of a triangle inequality thm
triangle_inequality

Demonstrations

1. Simplifying $|x+1|$ and Deducing Number Sets

Consider the very simple absolute value expression $|x+1|$, where the operand is a sum of a variable and known constant:

In [19]:
demo_expr_01 = Abs(Add(x, one))
demo_expr_01:

The general expression simplification() method can eliminate the absolute value wrapper under the right conditions — for example, when we know that $x$ is a positive real number:

In [20]:
demo_expr_01
In [21]:
demo_expr_01.simplification(assumptions=[InSet(x, RealPos)])

We can deduce that the absolute value expression is in a specific number set (given the right assumptions):

In [22]:
demo_expr_01.deduce_in_number_set(RealNonNeg, assumptions=[InSet(x, Real)])

Now when $x \in \mathbb{R}^{+}$, we should have $|x+1|>1$ and thus $|x+1| \in \mathbb{R}^{+}$. Interestingly, this requires a little more work before Prove-It agrees:

In [23]:
from proveit import InstantiationFailure
try:
    demo_expr_01.deduce_in_number_set(RealPos, assumptions=[InSet(x, RealPos)])
except InstantiationFailure as _e:
    print("SpecializationFailure: " + str(_e))
    

We could include the information the system seems to want, i.e., that $x+1 \ne 0$, and Prove-It is happy:

In [24]:
demo_expr_01.deduce_in_number_set(
    RealPos,
    assumptions=[InSet(x, RealPos), NotEquals(Add(x, one), zero)])

But that seems a little clunky, with that additional (and redundant) assumption appearing in the result. Instead we can nudge Prove-It by first having it prove that the operand $x+1$ is itself a positive real value, and then it has no problem proving that the absolute value expression $|x+1|$ itself is also a positive real value:

In [25]:
InSet(Add(x, one), RealPos).prove(assumptions=[InSet(x, RealPos)])
In [26]:
InSet(demo_expr_01, RealPos).prove(assumptions=[InSet(x, RealPos)])

2. Simplifying $|a+b+2+3|$ and Deducing Numbers Sets

Now consider a simple absolute value expression, where the operand is a sum of variables and known constants: $|a+b+2+3|$

In [27]:
demo_expr_02 = Abs(Add(a, b, two, three))
demo_expr_02:

We can obtain simplification equivalences, depending on our assumptions about the nature of the variables $a$ and $b$. If $a$ and $b$ are complex numbers or real numbers, for example, Prove-It simplifies the $2+3$ portion to $5$ and keeps the absolute value:

In [28]:
# variables a and b are complex, notated as a tuple
demo_expr_02.simplification(assumptions=[InSet(a, Complex), InSet(b, Complex)])
In [29]:
# variables a and b are real, notated individually
demo_expr_02.simplification(assumptions=[InSet(a, Real), InSet(b, Real)])

We can simplify further if we know that $a$ and $b$ are non-negative reals, with `simplification()` combining the numerical constants *and* removing the absolute value:

In [30]:
demo_expr_02.simplification(
    assumptions=[InSet(a, RealNonNeg), InSet(b, RealNonNeg)])

Similarly, given the assumption that $a$ and $b$ are non-negative reals, we can explicitly deduce that the entire expression is also a non-negative real:

In [31]:
demo_expr_02.deduce_in_number_set(
        RealNonNeg,
        assumptions=[InSet(a, RealNonNeg), InSet(b, RealNonNeg)])

In fact, as long as we explicitly establish that $a$ and $b$ each belong to some defined subset of the complex numbers, the `deduce_in_number_set()` will be able to deduce that our absolute value expression is an element of the non-negative real numbers:

In [32]:
demo_expr_02.deduce_in_number_set(
        RealNonNeg,
        assumptions=[InSet(a, Complex), InSet(b, RealNeg)])

From this, it is straighforward to derive $|a + b + 2 + 3| \geq 0$ exploiting some automation:

In [33]:
demo_expr_02_ge_zero = greater_eq(demo_expr_02, zero).prove(
    assumptions=[InSet(a, Complex), InSet(b, RealNeg)])
demo_expr_02_ge_zero: ,  ⊢  

The detailed proof includes a number of steps that establish that all the sub-operands can be found in the complex numbers, but we see toward the top of the proof table the explicit use of the modus ponens-like `right_from_iff` theorem (cited in line 1) and the `in_real_non_neg_iff_nonNegative` theorem (cited in line 5), which we were nudging Prove-It toward by our few steps above:

In [34]:
demo_expr_02_ge_zero.proof()
 step typerequirementsstatement
0instantiation1, 2,  ⊢  
  :
1conjecture  ⊢  
 proveit.numbers.number_sets.real_numbers.nonneg_if_in_real_nonneg
2instantiation3, 4,  ⊢  
  :
3conjecture  ⊢  
 proveit.numbers.absolute_value.abs_complex_closure
4instantiation5, 6, 7, 8, 9, 10, 11,  ⊢  
  : , :
5conjecture  ⊢  
 proveit.numbers.addition.add_complex_closure
6conjecture  ⊢  
 proveit.numbers.numerals.decimals.nat4
7instantiation12  ⊢  
  : , : , : , :
8assumption  ⊢  
9instantiation26, 15, 13  ⊢  
  : , : , :
10instantiation26, 15, 14  ⊢  
  : , : , :
11instantiation26, 15, 16  ⊢  
  : , : , :
12conjecture  ⊢  
 proveit.numbers.numerals.decimals.tuple_len_4_typical_eq
13instantiation26, 17, 18  ⊢  
  : , : , :
14instantiation26, 20, 19  ⊢  
  : , : , :
15conjecture  ⊢  
 proveit.numbers.number_sets.complex_numbers.real_within_complex
16instantiation26, 20, 21  ⊢  
  : , : , :
17conjecture  ⊢  
 proveit.numbers.number_sets.real_numbers.real_neg_within_real
18assumption  ⊢  
19instantiation26, 23, 22  ⊢  
  : , : , :
20conjecture  ⊢  
 proveit.numbers.number_sets.real_numbers.rational_within_real
21instantiation26, 23, 24  ⊢  
  : , : , :
22instantiation26, 27, 25  ⊢  
  : , : , :
23conjecture  ⊢  
 proveit.numbers.number_sets.rational_numbers.int_within_rational
24instantiation26, 27, 28  ⊢  
  : , : , :
25conjecture  ⊢  
 proveit.numbers.numerals.decimals.nat2
26theorem  ⊢  
 proveit.logic.sets.inclusion.superset_membership_from_proper_subset
27conjecture  ⊢  
 proveit.numbers.number_sets.integers.nat_within_int
28conjecture  ⊢  
 proveit.numbers.numerals.decimals.nat3


3. Simplifying $1 + |x+1|$


Consider the simple expression $1 + |x+1|$ involving the sum of a constant and an absolute value expression.

In [35]:
demo_expr_03 = Add(one, Abs(Add(x, one)))
demo_expr_03:

If we know that $x\in \mathbb{R}^{+}$, simplification should be able to eliminate the absolute value and commute and combine the known constants:

In [36]:
demo_expr_03.simplification(assumptions=[InSet(x, RealPos)])

Misc Testing

The material below was developed to test various absolute_value methods. Some of this material could be integrated into the `_demonstrations_` page eventually and/or deleted as development continues.

Some Example Abs Expressions For Testing

In [37]:
abs_value_1_plus_2 = Abs(Add(one, two))
abs_value_1_plus_2:
In [38]:
abs_value_a_plus_b = Abs(Add(a, b))
abs_value_a_plus_b:
In [39]:
abs_value_a_plus_2 = Abs(Add(a, two))
abs_value_a_plus_2:
In [40]:
abs_value_a_times_b = Abs(Mult(a, b))
abs_value_a_times_b:
In [41]:
abs_value_a_div_b = Abs(frac(a,b))
abs_value_a_div_b:
In [42]:
from proveit.trigonometry import Sin
abs_value_sin_x = Abs(Sin(x))
abs_value_sin_x:
In [43]:
from proveit.trigonometry import Cos
abs_value_cos_x = Abs(Cos(x))
abs_value_cos_x:
In [ ]:
 

Testing the deduce_in_number_set() method

In [44]:
abs_value_1_plus_2.deduce_in_number_set(Real)
In [45]:
abs_value_1_plus_2.deduce_in_number_set(RealPos)
In [46]:
# Abs(x) is real when x is complex
abs_value_a_plus_b.deduce_in_number_set(
    Real, assumptions=[InSet(Add(a,b), Complex)])
In [47]:
# Abs(a+b) is real if a is real and b a positive natural, etc
abs_value_a_plus_b.deduce_in_number_set(
    Real, assumptions=[InSet(a, Real), InSet(b, NaturalPos)])
In [48]:
# Abs(a+2) is real if a is real
abs_value_a_plus_2.deduce_in_number_set(
    Real, assumptions=[InSet(a, Real)])
In [49]:
abs_value_a_plus_b.deduce_in_number_set(RealPos,
    assumptions=[InSet(a, Complex),
                 InSet(b, Complex),
                 NotEquals(Add(a,b), zero)])
, ,  ⊢  
In [50]:
abs_value_1_plus_2.deduce_in_number_set(RealNonNeg)
In [51]:
abs_value_a_plus_b.deduce_in_number_set(RealNonNeg,
    assumptions=[InSet(a, Complex), InSet(b, Complex)])
In [52]:
# what happens if we try to deduce into an incorrect set?
try:
    abs_value_a_plus_b.deduce_in_number_set(RealNeg,
        assumptions=[InSet(Add(a,b), Complex)])
    assert False
except Exception as _e:
    print("Expected Exception: ", _e)
Expected Exception:  Proof step failed assuming {(a + b) in Complex}:
Attempting to instantiate |- forall_{a in Real | a < 0} (a in RealNeg) with {a: |a + b|}:
Unsatisfied condition: |a + b| < 0. For debugging purposes, this is accessible via Instantiation.unsatisfied_condition with applicable assumptions in Instantiation.condition_assumptions.
In [53]:
# but of course, the Complex numbers contain the non-negative Real numbers …
# so it would be nice to be able to be able to deduce_in_number_set(X) if
# X contains a set Y for which deduce_in_number_set(Y) is possible …
abs_value_a_plus_b.deduce_in_number_set(Complex,
    assumptions=[InSet(Add(a,b), Complex)])

Testing the Abs.deduce_not_equal() method

In [54]:
# recall our Abs(Add(a, b)) expression
abs_value_a_plus_b
In [55]:
abs_value_a_plus_b.deduce_not_equal(
    zero,
    assumptions=[InSet(a, Complex),
                 InSet(b, Complex),
                 NotEquals(Add(a,b), zero)])
, ,  ⊢  
In [56]:
# And automation will generally be able to prove
# Abs(x) ≠ 0 when x in a defined set with x ≠ 0.
NotEquals(abs_value_a_plus_b, zero).prove(
    assumptions=[InSet(Add(a,b), Real), NotEquals(Add(a,b), zero)])

Testing the Abs.abs_elimination() method

In [57]:
# abs_elimination() by itself often needs some
# pre-processing of the expression operand
try:
    abs_value_1_plus_2.abs_elimination()
except Exception as _e:
    print("EXCEPTION: ", _e)
In [58]:
# So prove 1+2 is non-negative real first
InSet(Add(one, two), RealPos).prove()
In [59]:
# And then we should be able to use the basic Abs.abs_elimination()
abs_value_1_plus_2.abs_elimination()
In [60]:
abs_value_a_plus_b.abs_elimination(
    assumptions = [InSet(Add(a,b), RealPos)])

Testing the Abs.deduce_greater_than_equals_zero() method

In [61]:
abs_value_1_plus_2.deduce_greater_than_equals_zero()
In [62]:
abs_value_a_plus_b.deduce_greater_than_equals_zero(
    assumptions=[InSet(Add(a,b), Complex)])

Testing the Abs.distribution() method over products and fractions

In [63]:
abs_value_a_times_b
In [64]:
abs_value_a_div_b
In [65]:
abs_value_a_times_b.distribution(
     assumptions=[InSet(a, Complex), InSet(b, Complex)])
In [66]:
# distribution over a fraction works fine
abs_value_a_div_b.distribution(
    assumptions=[InSet(a, Complex),
                 InSet(b, Complex),
                 NotEquals(b, zero)])
, ,  ⊢  
In [67]:
abs_value_sin_x.distribution(assumptions=[InSet(x, Real)])
In [68]:
abs_value_cos_x.distribution(assumptions=[InSet(x, Integer)])

Absolute value is an even function which is exploited in its canonical form

In [69]:
Abs(Add(a, Neg(b), Neg(c))).canonical_form() == Abs(Add(b, c, Neg(a))).canonical_form() 
True
In [70]:
Equals(Abs(Add(a, Neg(b), Neg(c))), Abs(Add(b, c, Neg(a)))).prove(
    assumptions=[InSet(a, Complex), InSet(b, Complex), InSet(c, Complex)])
, ,  ⊢  

Testing Expression simplification() on Abs() Objects

In [71]:
abs_value_1_plus_2
In [72]:
abs_value_1_plus_2.simplification()
In [73]:
abs_value_neg_three = Abs(Neg(three))
abs_value_neg_three:
In [74]:
# an example suggesting we modify the do_reduced_simplification()
# to be on the lookout for double negation results?
abs_value_neg_three.simplification()
In [75]:
# we can simplify the double negation manually with a little work
abs_value_neg_three.simplification().inner_expr().rhs.simplify()
In [76]:
# after which the system now knows:
abs_value_neg_three.simplification()
In [77]:
# see if we can catch these in the do_reduced_simplification …
# add a greater(x, zero) or greater_eq(x, zero) to the routine
abs_value_a_plus_b.simplification(
    assumptions=[InSet(Add(a, b), Real), greater(Add(a, b), zero)])
In [78]:
abs_value_a_plus_b.simplification(
    assumptions=[InSet(a, RealPos), InSet(b, RealPos)])
In [79]:
abs_value_a_plus_b.simplification(
    assumptions=[InSet(Add(a,b), RealPos)])
In [80]:
abs_value_a_times_b
In [81]:
abs_value_a_times_b.simplification(
    assumptions=[InSet(Mult(a,b), RealPos)])
In [82]:
abs_value_a_times_b.simplification(
    assumptions=[InSet(a, RealPos), InSet(b, RealPos)])
In [83]:
# But if we don't know anything about a and b, 'simplication' will raise an exception.
from proveit import InstantiationFailure
try:
    abs_value_a_times_b.simplification()
    assert False
except InstantiationFailure as _e:
    print("Expected exception:", _e)
Expected exception: Proof step failed:
Attempting to instantiate |- forall_{n in NaturalPos} [forall_{x_{1}, x_{2}, ..., x_{n} in Complex} (|x_{1} *  x_{2} *  ... *  x_{n}| = (|x_{1}| *  |x_{2}| *  ... *  |x_{n}|))] with {n: 2, x: (a, b)}:
Unsatisfied condition: a in Complex. For debugging purposes, this is accessible via Instantiation.unsatisfied_condition with applicable assumptions in Instantiation.condition_assumptions.
In [84]:
abs_value_a_times_b.simplification(
    assumptions=[InSet(Mult(a,b), Natural)])
In [85]:
abs_value_a_times_b.simplification(
    assumptions=[InSet(a, RealNonNeg), InSet(b, RealNonNeg)])
In [86]:
abs_value_a_times_b.simplification(
    assumptions=[InSet(a, Natural), InSet(b, Natural)])
In [87]:
abs_value_a_times_b.simplification(
    assumptions=[InSet(a, RealNonNeg), InSet(b, Natural)])
In [88]:
# an extra test expression, slightly more interesting
test_neg = Abs(Neg(Add(Abs(a), two)))
test_neg:
In [89]:
# this one is interesting: reaching down inside an Abs to
# simplify another Abs, but not clear how it knows to do that
test_neg_simp01 = test_neg.simplification(
    assumptions=[InSet(a, RealPos)])
test_neg_simp01:  ⊢  
In [90]:
test_neg_simp02 = test_neg.simplification(
    assumptions=[InSet(a, RealNonNeg),
                 InSet(Add(a, two),
                 RealNonNeg)])
test_neg_simp02: ,  ⊢  
In [91]:
# then it takes a little work to then eliminate the double negative
test_neg_simp02.inner_expr().rhs.simplify(
    assumptions=[InSet(a, RealNonNeg)])
In [92]:
abs_value_sin_x.simplification(assumptions=[InSet(x, NaturalPos)])
In [93]:
abs_value_sin_x.simplification(assumptions=[InSet(x, Real)])
In [94]:
abs_value_cos_x.simplification(assumptions=[InSet(x, Natural)])
In [95]:
abs_value_cos_x.simplification(assumptions=[InSet(x, RealNeg)])

Bounding an absolute value

In [96]:
Abs(x).deduce_strict_upper_bound(y, assumptions=[Less(Neg(y), x), Less(x, y), ])
In [97]:
Abs(x).deduce_weak_upper_bound(y, assumptions=[LessEq(Neg(y), x), LessEq(x, y)])

Absolute values involving complex numbers in polar form

In [98]:
Abs(Mult(r, Exp(e, Mult(i, theta)))).simplification(assumptions=[InSet(r, RealNonNeg), InSet(theta, Real)])
In [99]:
Abs(Mult(r, Exp(e, Neg(Mult(i, theta))))).simplification(assumptions=[InSet(r, RealNonNeg), InSet(theta, Real)])
In [100]:
Abs(Mult(r, Exp(e, Mult(i, Neg(theta))))).simplification(assumptions=[InSet(r, RealNonNeg), InSet(theta, Real)])
In [101]:
Abs(Mult(r, Exp(e, Mult(Neg(theta), i)))).simplification(assumptions=[InSet(r, RealNonNeg), InSet(theta, Real)])
In [102]:
Abs(Mult(Neg(r), Exp(e, Mult(Neg(theta), i)))).simplification(
    assumptions=[InSet(r, RealNonNeg), InSet(theta, Real)])
In [103]:
Abs(Mult(r, Exp(e, Mult(i, theta)))).simplification(assumptions=[InSet(r, RealNonPos), InSet(theta, Real)])
In [104]:
Abs(Mult(a, Exp(e, Mult(i, theta)), b)).simplification(
    assumptions=[InSet(a, RealPos), InSet(b, RealPos), InSet(theta, Real)])
, ,  ⊢  
In [105]:
# Since we don't know the sign of 'a', we must take the absolute value.
Abs(Mult(a, b, Exp(e, Mult(i, theta)), c)).simplification(
    assumptions=[InSet(a, Real), InSet(b, RealPos), InSet(c, RealPos), InSet(theta, Real)])
, , ,  ⊢  

Triangle inequality

In [106]:
Abs(Add(a, b)).deduce_triangle_bound(assumptions=[InSet(a, Complex), InSet(b, Complex)])
In [107]:
Abs(Add(a, b, x_1_to_n, c)).deduce_triangle_bound(
    assumptions=[InSet(a, Complex), InSet(b, Complex), InSet(c, Complex),
                 ExprRange(k, InSet(IndexedVar(x, k), Complex), one, n), 
                 InSet(n, Natural)])
, , , ,  ⊢  
In [108]:
%end demonstrations