This commit is contained in:
jverzani
2022-05-24 13:51:49 -04:00
parent 5c0fd1b6fe
commit 244f492f9e
240 changed files with 65211 additions and 0 deletions

11
CwJ/precalc/Project.toml Normal file
View File

@@ -0,0 +1,11 @@
[deps]
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
IntervalArithmetic = "d1acc4aa-44c8-5952-acd4-ba5d80a2a253"
Measures = "442fdcdd-2543-5da2-b0f3-8c86c306513e"
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
Polynomials = "f27b6e38-b328-58d1-80ce-0feddd5e7a45"
PyPlot = "d330b81b-6aea-500a-939a-2ce795aea3ee"
RealPolynomialRoots = "87be438c-38ae-47c4-9398-763eabe5c3be"
Richardson = "708f8203-808e-40c0-ba2d-98a6953ed40d"
Tables = "bd369af6-aec1-5ad0-b16a-f7cc5008161c"

BIN
CwJ/precalc/cache/calculator.cache vendored Normal file

Binary file not shown.

Binary file not shown.

BIN
CwJ/precalc/cache/functions.cache vendored Normal file

Binary file not shown.

BIN
CwJ/precalc/cache/inversefunctions.cache vendored Normal file

Binary file not shown.

BIN
CwJ/precalc/cache/julia_overview.cache vendored Normal file

Binary file not shown.

Binary file not shown.

BIN
CwJ/precalc/cache/numbers_types.cache vendored Normal file

Binary file not shown.

BIN
CwJ/precalc/cache/plotting.cache vendored Normal file

Binary file not shown.

BIN
CwJ/precalc/cache/polynomial.cache vendored Normal file

Binary file not shown.

BIN
CwJ/precalc/cache/polynomial_roots.cache vendored Normal file

Binary file not shown.

BIN
CwJ/precalc/cache/ranges.cache vendored Normal file

Binary file not shown.

Binary file not shown.

BIN
CwJ/precalc/cache/testing.cache vendored Normal file

Binary file not shown.

BIN
CwJ/precalc/cache/transformations.cache vendored Normal file

Binary file not shown.

BIN
CwJ/precalc/cache/trig_functions.cache vendored Normal file

Binary file not shown.

BIN
CwJ/precalc/cache/variables.cache vendored Normal file

Binary file not shown.

BIN
CwJ/precalc/cache/vector.cache vendored Normal file

Binary file not shown.

BIN
CwJ/precalc/cache/vectors.cache vendored Normal file

Binary file not shown.

1046
CwJ/precalc/calculator.jmd Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,691 @@
# Exponential and logarithmic functions
This section uses the following add-on packages:
```julia
using CalculusWithJulia
using Plots
```
```julia; echo=false; results="hidden"
using CalculusWithJulia.WeaveSupport
const frontmatter = (
title = "Exponential and logarithmic functions",
description = "Calculus with julia",
tags = ["CalculusWithJulia", "precalc", "exponential and logarithmic functions"],
);
nothing
```
----
The family of exponential functions is used to model growth and
decay. The family of logarithmic functions is defined here as the
inverse of the exponential functions, but have reach far outside of that.
## Exponential functions
The family of exponential functions is defined by
$f(x) = a^x, -\infty< x < \infty$ and $a > 0$.
For $0 < a < 1$ these functions decay or decrease, for $a > 1$ the
functions grow or increase, and if $a=1$ the function is constantly $1$.
For a given $a$, defining $a^n$ for positive integers is
straightforward, as it means multiplying $n$ copies of $a.$ From this, for *integer powers*,
the key properties of exponents: $a^x \cdot a^y = a^{x+y}$, and
$(a^x)^y = a^{x \cdot y}$ are immediate consequences. For example with ``x=3`` and ``y=2``:
```math
\begin{align*}
a^3 \cdot a^2 &= (a\cdot a \cdot a) \cdot (a \cdot a) \\
&= (a \cdot a \cdot a \cdot a \cdot a) \\
&= a^5 = a^{3+2},\\
(a^3)^2 &= (a\cdot a \cdot a) \cdot (a\cdot a \cdot a)\\
&= (a\cdot a \cdot a \cdot a\cdot a \cdot a) \\
&= a^6 = a^{3\cdot 2}.
\end{align*}
```
For $a \neq 0$, $a^0$ is defined to be $1$.
For positive, integer values of $n$, we have by definition that $a^{-n} = 1/a^n$.
For $n$ a positive integer, we can
define $a^{1/n}$ to be the unique positive solution to $x^n=a$.
Using the key properties of exponents we can extend this to a definition
of $a^x$ for any rational $x$.
Defining $a^x$ for any real number requires some more sophisticated
mathematics.
One method is to use a
[theorem](http://tinyurl.com/zk86c8r) that says a *bounded*
monotonically increasing sequence will converge. (This uses the
[Completeness
Axiom](https://en.wikipedia.org/wiki/Completeness_of_the_real_numbers).)
Then for $a > 1$ we have if $q_n$ is a sequence of rational numbers
increasing to $x$, then $a^{q_n}$ will be a bounded sequence of
increasing numbers, so will converge to a number defined to be
$a^x$. Something similar is possible for the $0 < a < 1$ case.
This definition can be done to ensure the rules of exponents hold for
$a > 0$:
```math
a^{x + y} = a^x \cdot a^y, \quad (a^x)^y = a^{x \cdot y}.
```
In `Julia` these functions are implemented using `^`. A special value
of the base, ``e``, may be defined as well in terms of a limit. The
exponential function ``e^x`` is implemented in `exp`.
```julia;hold=true;
plot(x -> (1/2)^x, -2, 2, label="1/2")
plot!(x -> 1^x, label="1")
plot!(x -> 2^x, label="2")
plot!(x -> exp(x), label="e")
```
We see examples of some general properties:
* The domain is all real $x$ and the range is all *positive* $y$
(provided $a \neq 1$).
* For $0 < a < 1$ the functions are monotonically decreasing.
* For $a > 1$ the functions are monotonically increasing.
* If $1 < a < b$ and $x > 0$ we have $a^x < b^x$.
##### Example
[Continuously](http://tinyurl.com/gsy939y) compounded interest allows
an initial amount $P_0$ to grow over time according to
$P(t)=P_0e^{rt}$. Investigate the difference between investing $1,000$
dollars in an account which earns $2$% as opposed to an account which
earns $8$% over $20$ years.
The $r$ in the formula is the interest rate, so $r=0.02$ or
$r=0.08$. To compare the differences we have:
```julia;
r2, r8 = 0.02, 0.08
P0 = 1000
t = 20
P0 * exp(r2*t), P0 * exp(r8*t)
```
As can be seen, there is quite a bit of difference.
In ``1494``, [Pacioli](http://tinyurl.com/gsy939y) gave the "Rule of ``72``",
stating that to find the number of years it takes an investment to
double when continuously compounded one should divide the interest rate
into $72$.
This formula is not quite precise, but a rule of thumb, the number is
closer to $69$, but $72$ has many divisors which makes this an easy to
compute approximation. Let's see how accurate it is:
```julia;
t2, t8 = 72/2, 72/8
exp(r2*t2), exp(r8*t8)
```
So fairly close - after $72/r$ years the amount is $2.05...$ times more
than the initial amount.
##### Example
[Bacterial growth](https://en.wikipedia.org/wiki/Bacterial_growth)
(according to Wikipedia) is the asexual reproduction, or cell
division, of a bacterium into two daughter cells, in a process called
binary fission. During the log phase "the number of new bacteria
appearing per unit time is proportional to the present population."
The article states that "Under controlled conditions, *cyanobacteria*
can double their population four times a day..."
Suppose an initial population of $P_0$ bacteria, a formula for the
number after $n$ *hours* is $P(n) = P_0 2^{n/6}$ where $6 = 24/4$.
After two days what multiple of the initial amount is present if
conditions are appropriate?
```julia;hold=true;
n = 2 * 24
2^(n/6)
```
That would be an enormous growth. Don't worry: "Exponential growth
cannot continue indefinitely, however, because the medium is soon
depleted of nutrients and enriched with wastes."
```julia; echo=false
note("""
The value of `2^n` and `2.0^n` is different in `Julia`. The former remains an integer and is subject to integer overflow for `n > 62`. As used above, `2^(n/6)` will not overflow for larger `n`, as when the exponent is a floating point value, the base is promoted to a floating point value.
""")
```
##### Example
The famous [Fibonacci](https://en.wikipedia.org/wiki/Fibonacci_number)
numbers are $1,1,2,3,5,8,13,\dots$, where $F_{n+1}=F_n+F_{n-1}$. These numbers increase. To see how fast, if we *guess* that
the growth is eventually exponential and assume $F_n \approx c \cdot a^n$, then
our equation is approximately $ca^{n+1} = ca^n + ca^{n-1}$. Factoring out common terms gives ``ca^{n-1} \cdot (a^2 - a - 1) = 0``. The term ``a^{n-1}`` is always positive, so any solution would satisfy $a^2 - a -1 = 0$. The positve solution is
$(1 + \sqrt{5})/2 \approx 1.618$
That is evidence that the $F_n \approx c\cdot 1.618^n$. (See
[Relation to golden ratio](https://en.wikipedia.org/wiki/Fibonacci_number#Relation_to_the_golden_ratio)
for a related, but more explicit exact formula.
##### Example
In the previous example, the exponential family of functions is used
to describe growth. Polynomial functions also increase. Could these be
used instead? If so that would be great, as they are easier to reason
about.
The key fact is that exponential growth is much greater than
polynomial growth. That is for large enough $x$ and for any fixed
$a>1$ and positive integer $n$ it is true that $a^x \gg x^n$.
Later we will see an easy way to certify this statement.
##### The mathematical constant ``e``
Euler's number, ``e``, may be defined several ways. One way is to
define ``e^x`` by the limit ``(1+x/n)^n``. Then ``e=e^1``. The value
is an irrational number. This number turns up to be the natural base
to use for many problems arising in Calculus. In `Julia` there are a
few mathematical constants that get special treatment, so that when
needed, extra precision is available. The value `e` is not immediately
assigned to this value, rather `` is. This is typed
`\euler[tab]`. The label `e` is thought too important for other uses
to reserve the name for representing a single number. However, users
can issue the command `using Base.MathConstants` and `e` will be
available to represent this number. When the `CalculusWithJulia`
package is loaded, the value `e` is defined to be the floating point
number returned by `exp(1)`. This loses the feature of arbitrary
precision, but has other advantages.
A [cute](https://www.mathsisfun.com/numbers/e-eulers-number.html) appearance of ``e`` is in this problem: Let ``a>0``. Cut ``a`` into ``n`` equal pieces and then multiply them. What ``n`` will produce the largest value? Note that the formula is ``(a/n)^n`` for a given ``a`` and ``n``.
Suppose ``a=5`` then for ``n=1,2,3`` we get:
```julia; hold=true;
a = 5
(a/1)^1, (a/2)^2, (a/3)^3
```
We'd need to compare more, but at this point ``n=2`` is the winner when ``a=5``.
With calculus, we will be able to see that the function ``f(x) = (a/x)^x`` will be maximized at ``a/e``, but for now we approach this in an exploratory manner. Suppose ``a=5``, then we have:
```julia;hold=true;
a = 5
n = 1:10
f(n) = (a/n)^n
@. [n f(n) (a/n - e)] # @. just allows broadcasting
```
We can see more clearly that ``n=2`` is the largest value for ``f`` and ``a/2`` is the closest value to ``e``. This would be the case for any ``a>0``, pick ``n`` so that ``a/n`` is closest to ``e``.
##### Example: The limits to growth
The ``1972`` book [The limits to growth](https://donellameadows.org/wp-content/userfiles/Limits-to-Growth-digital-scan-version.pdf) by Meadows et. al. discusses the implications of exponential growth. It begins stating their conclusion (emphasis added): "If the present *growth* trends in world population, industrialization, pollution, food production, and resource depletion continue *unchanged*, the limits to growth on this planet will be reached sometime in the next *one hundred* years." They note it is possible to alter these growth trends. We are now half way into this time period.
Let's consider one of their examples, the concentration of carbon dioxide in the atmosphere. In their Figure ``15`` they show data from ``1860`` onward of CO``_2`` concentration extrapolated out to the year ``2000``. At [climate.gov](https://www.climate.gov/news-features/understanding-climate/climate-change-atmospheric-carbon-dioxide) we can see actual measurements from ``1960`` to ``2020``. Numbers from each graph are read from the graphs, and plotted in the code below:
```julia;
co2_1970 = [(1860, 293), (1870, 293), (1880, 294), (1890, 295), (1900, 297),
(1910, 298), (1920, 300), (1930, 303), (1940, 305), (1950, 310),
(1960, 313), (1970, 320), (1980, 330), (1990, 350), (2000, 380)]
co2_2021 = [(1960, 318), (1970, 325), (1980, 338), (1990, 358), (2000, 370),
(2010, 390), (2020, 415)]
xs,ys = unzip(co2_1970)
plot(xs, ys, legend=false)
𝒙s, 𝒚s = unzip(co2_2021)
plot!(𝒙s, 𝒚s)
r = 0.002
x₀, P₀ = 1960, 313
plot!(x -> P₀ * exp(r * (x - x₀)), 1950, 1990, linewidth=5, alpha=0.25)
𝒓 = 0.005
𝒙₀, 𝑷₀ = 2000, 370
plot!(x -> 𝑷₀ * exp(𝒓 * (x - 𝒙₀)), 1960, 2020, linewidth=5, alpha=0.25)
```
(The `unzip` function is from the `CalculusWithJulia` package and will be explained in a subsequent section.) We can see that the projections from the year ``1970`` hold up fairly well
On this plot we added two *exponential* models. at ``1960`` we added a *roughly* ``0.2`` percent per year growth (a rate mentioned in an accompanying caption) and at ``2000`` a roughly ``0.5`` percent per year growth. The former barely keeping up with the data.
The word **roughly** above could be made exact. Suppose we knew that between ``1960`` and ``1970`` the rate went from ``313`` to ``320``. If this followed an exponential model, then ``r`` above would satisfy:
```math
P_{1970} = P_{1960} e^{r * (1970 - 1960)}
```
or on division ``320/313 = e^{r\cdot 10}``. Solving for ``r`` can be done -- as explained next -- and yields ``0.002211\dots``.
## Logarithmic functions
As the exponential functions are strictly *decreasing* when $0 < a <
1$ and strictly *increasing* when $a>1,$ in both cases an inverse
function will exist. (When $a=1$ the function is a constant and is not
one-to-one.) The domain of an exponential function is all real $x$ and
the range is all *positive* $x$, so these are switched around for the
inverse function. Explicitly: the inverse function to ``f(x)=a^x`` will have domain ``(0,\infty)`` and range ``(-\infty, \infty)`` when ``a > 0, a \neq 1``.
The inverse function will solve for $x$ in the equation $a^x = y$. The
answer, formally, is the logarithm base $a$, written $\log_a(x)$.
That is $a^{\log_a(x)} = x$ for ``x > 0`` and $\log_a(a^x) = x$ for all ``x``.
To see how a logarithm is mathematically defined will have to wait,
though the family of functions - one for each $a>0$ - are implemented in `Julia` through the function
`log(a,x)`. There are special cases requiring just one argument: `log(x)` will compute the natural
log, base $e$ - the inverse of $f(x) = e^x$; `log2(x)` will compute the
log base $2$ - the inverse of $f(x) = 2^x$; and `log10(x)` will compute
the log base $10$ - the inverse of $f(x)=10^x$. (Also `log1p` computes an accurate value of ``\log(1 + p)`` when ``p \approx 0``.)
To see this in an example, we plot for base $2$ the exponential
function $f(x)=2^x$, its inverse, and the logarithm function with base
$2$:
```julia;hold=true;
f(x) = 2^x
xs = range(-2, stop=2, length=100)
ys = f.(xs)
plot(xs, ys, color=:blue, label="2ˣ") # plot f
plot!(ys, xs, color=:red, label="f⁻¹") # plot f^(-1)
xs = range(1/4, stop=4, length=100)
plot!(xs, log2.(xs), color=:green, label="log₂") # plot log2
```
Though we made three graphs, only two are seen, as the graph of `log2`
matches that of the inverse function.
Note that we needed a bit of care to plot the inverse function
directly, as the domain of $f$ is *not* the domain of $f^{-1}$. Again, in
this case the domain of $f$ is all $x$, but the domain of $f^{-1}$ is only all *positive* $x$ values.
Knowing that `log2` implements an inverse function allows us to solve
many problems involving doubling.
##### Example
An [old](https://en.wikipedia.org/wiki/Wheat_and_chessboard_problem) story about doubling is couched in terms of doubling grains of wheat. To simplify the story, suppose each day an amount of grain is doubled. How many days of doubling will it take ``1`` grain to become ``1`` million grains?
The number of grains after one day is $2$, two days is $4$, three days
is $8$ and so after $n$ days the number of grains is $2^n$. To answer
the question, we need to solve $2^x = 1,000,000$. The logarithm
function yields ``20`` days (after rounding up):
```julia;
log2(1_000_000)
```
##### Example
The half-life of a radioactive material is the time it takes for half the material to decay. Different materials have quite different half lives with some quite long, and others quite short. See [half lives](https://en.wikipedia.org/wiki/List_of_radioactive_isotopes_by_half-life) for some details.
The carbon ``14`` isotope is a naturally occurring isotope on Earth,
appearing in trace amounts. Unlike Carbon ``12`` and ``13`` it decays, in this
case with a half life of ``5730`` years (plus or minus ``40`` years). In a
[technique](https://en.wikipedia.org/wiki/Radiocarbon_dating) due to
Libby, measuring the amount of Carbon 14 present in an organic item
can indicate the time since death. The amount of Carbon ``14`` at death is
essentially that of the atmosphere, and this amount decays over
time. So, for example, if roughly half the carbon ``14`` remains, then the death occurred
about ``5730`` years ago.
A formula for the amount of carbon ``14`` remaining $t$ years after death would be $P(t) = P_0 \cdot 2^{-t/5730}$.
If $1/10$ of the original carbon ``14`` remains, how old is the item? This amounts to solving $2^{-t/5730} = 1/10$. We have: $-t/5730 = \log_2(1/10)$ or:
```julia;
-5730 * log2(1/10)
```
```julia; echo=false
note("""
(Historically) Libby and James Arnold proceeded to test the radiocarbon dating theory by analyzing samples with known ages. For example, two samples taken from the tombs of two Egyptian kings, Zoser and Sneferu, independently dated to ``2625`` BC plus or minus ``75`` years, were dated by radiocarbon measurement to an average of ``2800`` BC plus or minus ``250`` years. These results were published in Science in ``1949``. Within ``11`` years of their announcement, more than ``20`` radiocarbon dating laboratories had been set up worldwide. Source: [Wikipedia](http://tinyurl.com/p5msnh6).
""")
```
### Properties of logarithms
The basic graphs of logarithms ($a > 1$) are all similar, though as we see larger
bases lead to slower growing functions, though all satisfy $\log_a(1)
= 0$:
```julia;
plot(log2, 1/2, 10, label="2") # base 2
plot!(log, 1/2, 10, label="e") # base e
plot!(log10, 1/2, 10, label="10") # base 10
```
Now, what do the properties of exponents imply about logarithms?
Consider the sum $\log_a(u) + \log_a(v)$. If we raise $a$ to this
power, we have using the powers of exponents and the inverse nature of
$a^x$ and $\log_a(x)$ that:
```math
a^{\log_a(u) + \log_a(v)} = a^{\log_a(u)} \cdot a^{\log_a(v)} = u \cdot v.
```
Taking $\log_a$ of *both* sides yields $\log_a(u) + \log_a(v)=\log_a(u\cdot v)$. That is logarithms turn products into sums (of logs).
Similarly, the relation $(a^{x})^y =a^{x \cdot y}, a > 0$ can be used to see that
$\log_a(b^x) = x \cdot\log_a(b)$. This follows, as applying $a^x$ to each side yields the
same answer.
Due to inverse relationship between $a^x$ and $\log_a(x)$ we have:
```math
a^{\log_a(b^x)} = b^x.
```
Due to the rules of exponents, we have:
```math
a^{x \log_a(b)} = a^{\log_a(b) \cdot x} = (a^{\log_a(b)})^x = b^x.
```
Finally, since $a^x$ is one-to-one (when $a>0$ and $a \neq 1$), if $a^{\log_a(b^x)}=a^{x \log_a(b)}$ it must be that $\log_a(b^x) = x \log_a(b)$. That is, logarithms turn powers into products.
Finally, we use the inverse property of logarithms and powers to show that logarithms can be defined for any base. Say $a, b > 0$. Then $\log_a(x) = \log_b(x)/\log_b(a)$. Again, to verify this we apply $a^x$ to both sides to see we get the same answer:
```math
a^{\log_a(x)} = x,
```
this by the inverse property. Whereas, by expressing $a=b^{\log_b(a)}$ we have:
```math
a^{(\log_b(x)/\log_b(b))} = (b^{\log_b(a)})^{(\log_b(x)/\log_b(a))} =
b^{\log_b(a) \cdot \log_b(x)/\log_b(a) } = b^{\log_b(x)} = x.
```
In short, we have these three properties of logarithmic functions:
If $a, b$ are positive bases; $u,v$ are positive numbers; and $x$ is any real number then:
```math
\begin{align*}
\log_a(uv) &= \log_a(u) + \log_a(v), \\
\log_a(u^x) &= x \log_a(u), \text{ and} \\
\log_a(u) &= \log_b(u)/\log_b(a).
\end{align*}
```
##### Example
Before the ubiquity of electronic calculating devices, the need to
compute was still present. Ancient civilizations had abacuses to make
addition easier. For multiplication and powers a [slide
rule](https://en.wikipedia.org/wiki/Slide_rule) could be used.
It is easy to represent addition physically with two straight pieces
of wood - just represent a number with a distance and align the two
pieces so that the distances are sequentially arranged. To multiply
then was as easy: represent the logarithm of a number with a distance
then add the logarithms. The sum of the logarithms is the logarithm of
the *product* of the original two values. Converting back to a number
answers the question. The conversion back and forth is done by simply
labeling the wood using a logartithmic scale. The slide rule was
[invented](http://tinyurl.com/qytxo3e) soon after Napier's initial publication
on the logarithm in 1614.
##### Example
Returning to the Rule of ``72``, what should the exact number be?
The amount of time to double an investment that grows according to
$P_0 e^{rt}$ solves $P_0 e^{rt} = 2P_0$ or $rt = \log_e(2)$. So we get
$t=\log_e(2)/r$. As $\log_e(2)$ is
```julia;
log(e, 2)
```
We get the actual rule should be the "Rule of $69.314...$."
## Questions
###### Question
Suppose every $4$ days, a population doubles. If the population starts
with $2$ individuals, what is its size after $4$ weeks?
```julia; hold=true; echo=false
n = 4*7/4
val = 2 * 2^n
numericq(val)
```
###### Question
A bouncing ball rebounds to a height of $5/6$ of the previous peak
height. If the ball is droppet at a height of $3$ feet, how high will
it bounce after $5$ bounces?
```julia; hold=true; echo=false
val = 3 * (5/6)^5
numericq(val)
```
###### Question
Which is bigger $e^2$ or $2^e$?
```julia; hold=true; echo=false
choices = ["``e^2``", "``2^e``"]
ans = e^2 - 2^e > 0 ? 1 : 2
radioq(choices, ans)
```
###### Question
Which is bigger $\log_8(9)$ or $\log_9(10)$?
```julia; hold=true; echo=false
choices = [raw"``\log_8(9)``", raw"``\log_9(10)``"]
ans = log(8,9) > log(9,10) ? 1 : 2
radioq(choices, ans)
```
###### Question
If $x$, $y$, and $z$ satisfy $2^x = 3^y$ and $4^y = 5^z$, what is the
ratio $x/z$?
```julia; hold=true; echo=false
choices = [
raw"``\frac{\log(2)\log(3)}{\log(5)\log(4)}``",
raw"``2/5``",
raw"``\frac{\log(5)\log(4)}{\log(3)\log(2)}``"
]
ans = 1
radioq(choices, ans)
```
###### Question
Does $12$ satisfy $\log_2(x) + \log_3(x) = \log_4(x)$?
```julia; hold=true; echo=false
ans = log(2,12) + log(3,12) == log(4, 12)
yesnoq(ans)
```
###### Question
The [Richter](https://en.wikipedia.org/wiki/Richter_magnitude_scale)
magnitude is determined from the logarithm of the amplitude of waves
recorded by seismographs (Wikipedia). The formula is $M=\log(A) -
\log(A_0)$ where $A_0$ depends on the epicenter distance. Suppose an
event has $A=100$ and $A_0=1/100$. What is $M$?
```julia; hold=true; echo=false
A, A0 = 100, 1/100
val = M = log(A) - log(A0)
numericq(val)
```
If the magnitude of one earthquake is $9$ and the magnitude of another
earthquake is $7$, how many times stronger is $A$ if $A_0$ is the same
for each?
```julia; hold=true; echo=false
choices = ["``1000`` times", "``100`` times", "``10`` times", "the same"]
ans = 2
radioq(choices, ans, keep_order=true)
```
###### Question
The [Loudest band](https://en.wikipedia.org/wiki/Loudest_band) can
possibly be measured in [decibels](https://en.wikipedia.org/wiki/Decibel). In ``1976`` the Who recorded $126$
db and in ``1986`` Motorhead recorded $130$ db. Suppose both measurements
record power through the formula $db = 10 \log_{10}(P)$. What is the
ratio of the Motorhead $P$ to the $P$ for the Who?
```julia; hold=true; echo=false
db_who, db_motorhead = 126, 130
db2P(db) = 10^(db/10)
P_who, P_motorhead = db2P.((db_who, db_motorhead))
val = P_motorhead / P_who
numericq(val)
```
###### Question
Based on this graph:
```julia; hold=true;
plot(log, 1/4, 4, label="log")
f(x) = x - 1
plot!(f, 1/4, 4, label="x-1")
```
Which statement appears to be true?
```julia; hold=true; echo=false
choices = [
raw"``x \geq 1 + \log(x)``",
raw"``x \leq 1 + \log(x)``"
]
ans = 1
radioq(choices, ans)
```
###### Question
Consider this graph:
```julia; hold=true;
f(x) = log(1-x)
g(x) = -x - x^2/2
plot(f, -3, 3/4, label="f")
plot!(g, -3, 3/4, label="g")
```
What statement appears to be true?
```julia; hold=true; echo=false
choices = [
raw"``\log(1-x) \geq -x - x^2/2``",
raw"``\log(1-x) \leq -x - x^2/2``"
]
ans = 1
radioq(choices, ans)
```
###### Question
Suppose $a > 1$. If $\log_a(x) = y$ what is $\log_{1/a}(x)$? (The
reciprocal property of exponents, $a^{-x} = (1/a)^x$, is at play here.)
```julia; hold=true; echo=false
choices = ["``-y``", "``1/y``", "``-1/y``"]
ans = 1
radioq(choices, ans)
```
Based on this, the graph of $\log_{1/a}(x)$ is the graph of
$\log_a(x)$ under which transformation?
```julia; hold=true; echo=false
choices = [
L"Flipped over the $x$ axis",
L"Flipped over the $y$ axis",
L"Flipped over the line $y=x$"
]
ans = 1
radioq(choices, ans)
```
###### Question
Suppose $x < y$. Then for $a > 0$, $a^y - a^x$ is equal to:
```julia; hold=true; echo=false
choices = [
raw"``a^x \cdot (a^{y-x} - 1)``",
raw"``a^{y-x}``",
raw"``a^{y-x} \cdot (a^x - 1)``"
]
ans = 1
radioq(choices, ans)
```
Using $a > 1$ we have:
```julia; hold=true; echo=false
choices = [
L"as $a^{y-x} > 1$ and $y-x > 0$, $a^y > a^x$",
L"as $a^x > 1$, $a^y > a^x$",
"``a^{y-x} > 0``"
]
ans=1
radioq(choices, ans)
```
If $a < 1$ then:
```julia; hold=true; echo=false
choices = [
L"as $a^{y-x} < 1$ as $y-x > 0$, $a^y < a^x$",
L"as $a^x < 1$, $a^y < a^x$",
"``a^{y-x} < 0``"
]
ans = 1
radioq(choices, ans)
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 314 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 402 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

1280
CwJ/precalc/functions.jmd Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,662 @@
# The Inverse of a Function
In this section we will use these add-on packages:
```julia
using CalculusWithJulia
using Plots
```
```julia; echo=false; results="hidden"
using CalculusWithJulia.WeaveSupport
const frontmatter = (
title = "The Inverse of a Function",
description = "Calculus with Julia: The Inverse of a Function",
tags = ["CalculusWithJulia", "precalc", "the inverse of a function"],
);
nothing
```
----
A (univariate) mathematical function relates or associates values of
$x$ to values $y$ using the notation $y=f(x)$. A key point is a given
$x$ is associated with just one $y$ value, though a given $y$ value
may be associated with several different $x$ values. (Graphically, this is the vertical line test.)
We may conceptualize such a relation in many ways: through an algebraic
rule; through the graph of $f;$ through a description of what $f$
does; or through a table of paired values, say. For the moment, let's
consider a function as rule that takes in a value of $x$ and outputs a
value $y$. If a rule is given defining the function, the computation
of $y$ is straightforward. A different question is not so easy: for a
given value $y$ what value - or *values* - of $x$ (if any) produce an output of
$y$? That is, what $x$ value(s) satisfy $f(x)=y$?
*If* for each $y$ in some set of values there is just one $x$ value, then this operation associates to each value $y$ a single value $x$, so it too is a function. When that is the case we call this an *inverse* function.
Why is this useful? When available, it can help us solve equations. If we can write our equation as $f(x) = y$, then we can "solve" for $x$ through $x = g(y)$, where $g$ is this inverse function.
Let's explore when we can "solve" for an inverse function.
Consider the graph of the function $f(x) = 2^x$:
```julia;hold=true
f(x) = 2^x
plot(f, 0, 4, legend=false)
plot!([2,2,0], [0,f(2),f(2)])
```
The graph of a function is a representation of points $(x,f(x))$, so
to *find* $f(c)$ from the graph, we begin on the $x$ axis at $c$, move
vertically to the graph (the point $(c, f(c))$), and then move
horizontally to the $y$ axis, intersecting it at $f(c)$. The figure
shows this for $c=2$, from which we can read that $f(c)$ is about
$4$. This is how an $x$ is associated to a single $y$.
If we were to *reverse* the direction, starting at $f(c)$ on the $y$
axis and then moving horizontally to the graph, and then vertically to
the $x$-axis we end up at a value $c$ with the correct $f(c)$. This
operation will form a function **if** the initial movement
horizontally is guaranteed to find *no more than one* value on the graph. That
is, to have an inverse function, there can not be two $x$ values
corresponding to a given $y$ value. This observation is often
visualized through the "horizontal line test" - the graph of a function with an
inverse function can only intersect a horizontal line at most in one
place.
More formally, a function is called *one-to-one* *if* for any two $a
\neq b$, it must be that $f(a) \neq f(b)$. Many functions are
one-to-one, many are not. Familiar one-to-one functions are linear functions ($f(x)=a \cdot x + b$ with $a\neq 0$), odd powers of $x$ ($f(x)=x^{2k+1}$), and functions of the form $f(x)=x^{1/n}$ for $x \geq 0$. In contrast, all *even* functions are *not* one-to-one, as $f(x) = f(-x)$ for any nonzero $x$ in the domain of $f$.
A class of functions that are guaranteed to be one-to-one are the
*strictly* increasing functions (which satisfy $a < b$ implies $f(a) <
f(b)$). Similarly, strictly decreasing functions are one-to-one. The
term strictly *monotonic* is used to describe either strictly
increasing or strictly decreasing. By the above observations,
strictly monotonic function will have inverse functions.
The function $2^x$, graphed above, is strictly increasing, so it will
have an inverse function. That is we can solve for $x$ in an equation
like $2^x = 9$ using the inverse function of $f(x) = 2^x$, provided we
can identify the inverse function.
## How to solve for an inverse function?
If we know an inverse function exists, how can we find it?
If our function is given by a graph, the process above describes how to find the inverse function.
However, typically we have a rule describing our function. What is the
process then? A simple example helps illustrate. The *linear*
function $f(x) = 9/5\cdot x + 32$ is strictly increasing, hence has an
inverse function. What should it be? Let's describe the action of $f$:
it multiplies $x$ by $9/5$ and then adds $32$. To "invert" this we
*first* invert the adding of $32$ by subtracting $32$, then we would
"invert" multiplying by $9/5$ by *dividing* by $9/5$. Hence $g(x)=(x-32)/(9/5)$.
We would generally simplify this, but let's not for now. If we
view a function as a composition of many actions, then we find the
inverse by composing the inverse of these actions in **reverse**
order. The reverse order might seem confusing, but this is how we get
dressed and undressed: to dress we put on socks and then shoes. To
undress we take off the shoes and then take off the socks.
When we solve algebraically for $x$ in $y=9/5 \cdot x + 32$ we do the same thing as we do verbally: we subtract $32$ from each side, and then divide by $9/5$ to isolate $x$:
```math
\begin{align}
y &= 9/5 \cdot x + 32\\
y - 32 &= 9/5 \cdot x\\
(y-32) / (9/5) &= x.
\end{align}
```
From this, we have the function $g(y) = (y-32) / (9/5)$ is the inverse function of $f(x) = 9/5\cdot x + 32$.
*Usually* univariate functions are written with $x$ as the dummy variable, so it is typical to write $g(x) = (x-32) / (9/5)$ as the inverse function.
*Usually* we use the name $f^{-1}$ for the inverse function of $f$, so this would be most often [seen](http://tinyurl.com/qypbueb) as $f^{-1}(x) = (x-32)/(9/5)$ or after simplification $f^{-1}(x) = (5/9) \cdot (x-32)$.
```julia; echo=false
note(L"""The use of a negative exponent on the function name is *easily* confused for the notation for a reciprocal when it is used on a mathematical *expression*. An example might be the notation $(1/x)^{-1}$. As this is an expression this would simplify to $x$ and not the inverse of the *function* $f(x)=1/x$ (which is $f^{-1}(x) = 1/x$).
""")
```
##### Example
Suppose a transformation of $x$ is given by $y = f(x) = (ax + b)/(cx+d)$. This function is invertible for most choices of the parameters. Find the inverse and describe it's domain.
From the expression $y=f(x)$ we *algebraically* solve for $x$:
```math
\begin{align*}
y &= \frac{ax +b}{cx+d}\\
y \cdot (cx + d) &= ax + b\\
ycx - ax &= b - yd\\
(cy-a) \cdot x &= b - dy\\
x &= -\frac{dy - b}{cy-a}.
\end{align*}
```
We see that to solve for $x$ we need to divide by $cy-a$, so this expression can not be zero.
So, using $x$ as the dummy variable, we have
```math
f^{-1}(x) = -\frac{dx - b}{cx-a},\quad cx-a \neq 0.
```
##### Example
The function $f(x) = (x-1)^5 + 2$ is strictly increasing and so will have an inverse function. Find it.
Again, we solve algebraically starting with $y=(x-1)^5 + 2$ and solving for $x$:
```math
\begin{align*}
y &= (x-1)^5 + 2\\
y - 2 &= (x-1)^5\\
(y-2)^{1/5} &= x - 1\\
(y-2)^{1/5} + 1 &= x.
\end{align*}
```
We see that $f^{-1}(x) = 1 + (x - 2)^{1/5}$. The fact that the power $5$ is an odd power is important, as this ensures a unique (real) solution to the fifth root of a value, in the above $y-2$.
##### Example
The function $f(x) = x^x, x \geq 1/e$ is strictly
increasing. However, trying to algebraically solve for an inverse
function will quickly run into problems (without using specially
defined functions). The existence of an inverse does not imply there
will always be luck in trying to find a mathematical rule defining the
inverse.
## Functions which are not always invertible
Consider the function $f(x) = x^2$. The graph - a parabola - is clearly not *monotonic*. Hence no inverse function exists. Yet, we can solve equations $y=x^2$ quite easily: $y=\sqrt{x}$ *or* $y=-\sqrt{x}$. We know the square root undoes the squaring, but we need to be a little more careful to say the square root is the inverse of the squaring function.
The issue is there are generally *two* possible answers. To avoid this, we might choose to only take the *non-negative* answer. To make this all work as above, we restrict the domain of $f(x)$ and now consider the related function $f(x)=x^2, x \geq 0$. This is now a monotonic function, so will have an inverse function. This is clearly $f^{-1}(x) = \sqrt{x}$. (The ``\sqrt{x}`` being defined as the principle square root or the unique *non-negative* answer to ``u^2-x=0``.)
The [inverse function theorem](https://en.wikipedia.org/wiki/Inverse_function_theorem) basically says that if $f$ is *locally* monotonic, then an inverse function will exist *locally*. By "local" we mean in a neighborhood of $c$.
##### Example
Consider the function $f(x) = (1+x^2)^{-1}$. This bell-shaped function is even (symmetric about $0$), so can not possibly be one-to-one. However, if the domain is restricted to $[0,\infty)$ it is. The restricted function is strictly decreasing and its inverse is found, as follows:
```math
\begin{align*}
y &= \frac{1}{1 + x^2}\\
1+x^2 &= \frac{1}{y}\\
x^2 &= \frac{1}{y} - 1\\
x &= \sqrt{(1-y)/y}, \quad 0 \leq y \leq 1.
\end{align*}
```
Then $f^{-1}(x) = \sqrt{(1-x)/x}$ where $0 < x \leq 1$. The somewhat
complicated restriction for the the domain coincides with the range of
$f(x)$. We shall see next that this is no coincidence.
## Formal properties of the inverse function
Consider again the graph of a monotonic function, in this case $f(x) = x^2 + 2, x \geq 0$:
```julia;hold=true
f(x) = x^2 + 2
plot(f, 0, 4, legend=false)
plot!([2,2,0], [0,f(2),f(2)])
```
The graph is shown over the interval $(0,4)$, but the *domain* of $f(x)$ is all $x \geq 0$. The *range* of $f(x)$ is clearly $2 \leq x \leq \infty$.
The lines layered on the plot show how to associate an $x$ value to a $y$ value or vice versa (as $f(x)$ is one-to-one). The domain then of the inverse function is all the $y$ values for which a corresponding $x$ value exists: this is clearly all values bigger or equal to $2$. The *range* of the inverse function can be seen to be all the images for the values of $y$, which would be all $x \geq 0$. This gives the relationship:
> the *range* of $f(x)$ is the *domain* of $f^{-1}(x)$; furthermore the *domain* of $f(x)$ is the *range* for $f^{-1}(x)$;
From this we can see if we start at $x$, apply $f$ we get $y$, if we then apply $f^{-1}$ we will get back to $x$ so we have:
> For all $x$ in the domain of $f$: $f^{-1}(f(x)) = x$.
Similarly, were we to start on the $y$ axis, we would see:
> For all $x$ in the domain of $f^{-1}$: $f(f^{-1}(x)) = x$.
In short $f^{-1} \circ f$ and $f \circ f^{-1}$ are both identity functions, though on possibly different domains.
## The graph of the inverse function
The graph of $f(x)$ is a representation of all values $(x,y)$ where $y=f(x)$. As the inverse flips around the role of $x$ and $y$ we have:
> If $(x,y)$ is a point on the graph of $f(x)$, then $(y,x)$ will be a point on the graph of $f^{-1}(x)$.
Let's see this in action. Take the function $2^x$. We can plot it by generating points to plot as follows:
```julia; hold=true;
f(x) = 2^x
xs = range(0, 2, length=50)
ys = f.(xs)
plot(xs, ys, color=:blue, label="f")
plot!(ys, xs, color=:red, label="f⁻¹") # the inverse
```
By flipping around the $x$ and $y$ values in the `plot!` command, we
produce the graph of the inverse function - when viewed as a function
of $x$. We can see that the domain of the inverse function (in red) is
clearly different from that of the function (in blue).
The inverse function graph can be viewed as a symmetry of the graph of
the function. Flipping the graph for $f(x)$ around the line $y=x$ will
produce the graph of the inverse function: Here we see for the graph
of $f(x) = x^{1/3}$ and its inverse function:
```julia; hold=true;
f(x) = cbrt(x)
xs = range(-2, 2, length=150)
ys = f.(xs)
plot(xs, ys, color=:blue, aspect_ratio=:equal, legend=false)
plot!(ys, xs, color=:red)
plot!(identity, color=:green, linestyle=:dash)
x, y = 1/2, f(1/2)
plot!([x,y], [y,x], color=:green, linestyle=:dot)
```
We drew a line connecting $(1/2, f(1/2))$ to $(f(1/2),1/2)$. We can
see that it crosses the line $y=x$ perpendicularly, indicating that
points are symmetric about this line. (The plotting argument
`aspect_ratio=:equal` ensures that the $x$ and $y$ axes are on the
same scale, so that this type of line will look perpendicular.)
One consequence of this symmetry, is that if $f$ is strictly increasing, then so is its inverse.
```julia; echo=false
note(L"""In the above we used `cbrt(x)` and not `x^(1/3)`. The latter usage assumes that $x \geq 0$ as it isn't guaranteed that for all real exponents the answer will be a real number. The `cbrt` function knows there will always be a real answer and provides it.
""")
```
### Lines
The slope of $f(x) = 9/5 \cdot x + 32$ is clearly $9/5$ and the slope of the inverse function $f^{-1}(x) = 5/9 \cdot (x-32)$ is clearly $5/9$ - or the reciprocal. This makes sense, as the slope is the rise over the run, and by flipping the $x$ and $y$ values we merely flip over the rise and the run.
Now consider the graph of the *tangent line* to a function. This concept will be better defined later, for now, it is a line "tangent" to the graph of $f(x)$ at a point $x=c$.
For concreteness, we consider $f(x) = \sqrt{x}$ at $c=2$. The tangent line will have slope $1/(2\sqrt{2})$ and will go through the point $(2, f(2)$. We graph the function, its tangent line, and their inverses:
```julia; hold=true;
f(x) = sqrt(x)
c = 2
tl(x) = f(c) + 1/(2 * sqrt(2)) * (x - c)
xs = range(0, 3, length=150)
ys = f.(xs)
zs = tl.(xs)
plot(xs, ys, color=:blue, legend=false)
plot!(xs, zs, color=:blue) # the tangent line
plot!(ys, xs, color=:red) # the inverse function
plot!(zs, xs, color=:red) # inverse of tangent line
```
What do we see? In blue, we can see the familiar square root graph along with a "tangent" line through the point $(2, f(2))$. The red graph of $f^{-1}(x) = x^2, x \geq 0$ is seen and, perhaps surprisingly, a tangent line. This is at the point $(f(2), 2)$. We know the slope of this tangent line is the reciprocal of the slope of the red tangent line. This gives this informal observation:
> If the graph of $f(x)$ has a tangent line at $(c, f(c))$ with slope $m$, then the graph of $f^{-1}(x)$ will have a tangent line at $(f(c), c)$ with slope $1/m$.
This is reminiscent of the formula for the slope of a perpendicular line, $-1/m$, but quite different, as this formula implies the two lines have either both positive slopes or both negative slopes, unlike the relationship in slopes between a line and a perpendicular line.
The key here is that the shape of $f(x)$ near $x=c$ is somewhat related to the shape of $f^{-1}(x)$ at $f(c)$. In this case, if we use the tangent line as a fill in for how steep a function is, we see from the relationship that if $f(x)$ is "steep" at $x=c$, then $f^{-1}(x)$ will be "shallow" at $x=f(c)$.
## Questions
###### Question
Is it possible that a function have two different inverses?
```julia; hold=true; echo=false
choices = [L"No, for all $x$ in the domain an an inverse, the value of any inverse will be the same, hence all inverse functions would be identical.",
L"Yes, the function $f(x) = x^2, x \geq 0$ will have a different inverse than the same function $f(x) = x^2, x \leq 0$"]
ans = 1
radioq(choices, ans)
```
###### Question
A function takes a value $x$ adds $1$, divides by $2$, and then subtracts $1$. Is the function "one-to-one"?
```julia; hold=true; echo=false
choices = [L"Yes, the function is the linear function $f(x)=(x+1)/2 + 1$ and so is monotonic.",
L"No, the function is $1$ then $2$ then $1$, but not \"one-to-one\""
]
ans = 1
radioq(choices, ans)
```
###### Question
Is the function $f(x) = x^5 - x - 1$ one-to-one?
```julia; hold=true; echo=false
choices=[L"Yes, a graph over $(-100, 100)$ will show this.",
L"No, a graph over $(-2,2)$ will show this."
]
ans = 2
radioq(choices, ans)
```
###### Question
A function is given by the table
```verbatim
x | y
--------
1 | 3
2 | 4
3 | 5
4 | 3
5 | 4
6 | 5
```
Is the function one-to-one?
```julia; hold=true; echo=false
yesnoq(false)
```
###### Question
A function is defined by its graph.
```julia; hold=true; echo=false
f(x) = x - sin(x)
plot(f, 0, 6pi)
```
Over the domain shown, is the function one-to-one?
```julia; hold=true; echo=false
yesnoq(true)
```
###### Question
Suppose $f(x) = x^{-1}$.
What is $g(x) = (f(x))^{-1}$?
```julia; hold=true; echo=false
choices = ["``g(x) = x``", "``g(x) = x^{-1}``"]
ans = 1
radioq(choices, ans)
```
What is $g(x) = f^{-1}(x)$?
```julia; hold=true; echo=false
choices = ["``g(x) = x``", "``g(x) = x^{-1}``"]
ans = 2
radioq(choices, ans)
```
###### Question
A function, $f$, is given by its graph:
```julia; echo=false;
k(x) = sin(pi/4 * x)
plot(k, -2, 2)
```
What is the value of $f(1)$?
```julia; hold=true; echo=false
val = k(1)
numericq(val, 0.2)
```
What is the value of $f^{-1}(1)$?
```julia; hold=true; echo=false
val = 2
numericq(val, 0.2)
```
What is the value of $(f(1))^{-1}$?
```julia; hold=true; echo=false
val = 1/k(1)
numericq(val, 0.2)
```
What is the value of $f^{-1}(1/2)$?
```julia; hold=true; echo=false
val = 2/3
numericq(val, 0.2)
```
###### Question
A function is described as follows: for $x > 0$ it takes the square root, adds $1$ and divides by $2$.
What is the inverse of this function?
```julia; hold=true; echo=false
choices=[
L"The function that multiplies by $2$, subtracts $1$ and then squares the value.",
L"The function that divides by $2$, adds $1$, and then takes the square root of the value.",
L"The function that takes square of the value, then subtracts $1$, and finally multiplies by $2$."
]
ans = 1
radioq(choices, ans)
```
###### Question
A function, $f$, is specified by a table:
```verbatim
x | y
-------
1 | 2
2 | 3
3 | 5
4 | 8
5 | 13
```
What is $f(3)$?
```julia; hold=true; echo=false
numericq(5)
```
What is $f^{-1}(3)$?
```julia; hold=true; echo=false
numericq(2)
```
What is $f(5)^{-1}$?
```julia; hold=true; echo=false
numericq(1/13)
```
What is $f^{-1}(5)$?
```julia; hold=true; echo=false
numericq(3)
```
###### Question
Find the inverse function of $f(x) = (x^3 + 4)/5$.
```julia; hold=true; echo=false
choices = [
"``f^{-1}(x) = (5y-4)^{1/3}``",
"``f^{-1}(x) = (5y-4)^3``",
"``f^{-1}(x) = 5/(x^3 + 4)``"
]
ans = 1
radioq(choices, ans)
```
###### Question
Find the inverse function of $f(x) = x^\pi + e, x \geq 0$.
```julia; hold=true; echo=false
choices = [
raw"``f^{-1}(x) = (x-e)^{1/\pi}``",
raw"``f^{-1}(x) = (x-\pi)^{e}``",
raw"``f^{-1}(x) = (x-e)^{\pi}``"
]
ans = 1
radioq(choices, ans)
```
###### Question
What is the *domain* of the inverse function for $f(x) = x^2 + 7, x \geq 0$?
```julia; hold=true; echo=false
choices = [
raw"``[7, \infty)``",
raw"``(-\infty, \infty)``",
raw"``[0, \infty)``"]
ans = 1
radioq(choices, ans)
```
###### Question
What is the *range* of the inverse function for $f(x) = x^2 + 7, x \geq 0$?
```julia; hold=true; echo=false
choices = [
raw"``[7, \infty)``",
raw"``(-\infty, \infty)``",
raw"``[0, \infty)``"]
ans = 3
radioq(choices, ans)
```
###### Question
From the plot, are blue and red inverse functions?
```julia; hold=true; echo=false
f(x) = x^3
xs = range(0, 2, length=100)
ys = f.(xs)
plot(xs, ys, color=:blue, legend=false)
plot!(ys, xs, color=:red)
plot!(x->x, linestyle=:dash)
```
```julia; hold=true; echo=false
yesnoq(true)
```
From the plot, are blue and red inverse functions?
```julia; hold=true; echo=false
f(x) = x^3 - x - 1
xs = range(-2,2, length=100)
ys = f.(xs)
plot(xs, ys, color=:blue, legend=false)
plot!(-xs, -ys, color=:red)
plot!(x->x, linestyle=:dash)
```
```julia; hold=true; echo=false
yesnoq(false)
```
###### Question
The function $f(x) = (ax + b)/(cx + d)$ is known as a [Mobius](http://tinyurl.com/oemweyj) transformation and can be expressed as a composition of $4$ functions, $f_4 \circ f_3 \circ f_2 \circ f_1$:
* where $f_1(x) = x + d/c$ is a translation,
* where $f_2(x) = x^{-1}$ is inversion and reflection,
* where $f_3(x) = ((bc-ad)/c^2) \cdot x$ is scaling,
* and $f_4(x) = x + a/c$ is a translation.
For $x=10$, what is $f(10)$?
```julia; echo=false
𝒂,𝒃,𝒄,𝒅 = 1,2,3,5
f1(x) = x + 𝒅/𝒄; f2(x) = 1/x; f3(x) = (𝒃*𝒄-𝒂*𝒅)/𝒄^2 * x; f4(x)= x + 𝒂/𝒄
𝒇(x;a=𝒂,b=𝒃,c=𝒄,d=𝒅) = (a*x+b) / (c*x + d)
numericq(𝒇(10))
```
For $x=10$, what is $f_4(f_3(f_2(f_1(10))))$?
```julia; hold=true; echo=false
numericq(f4(f3(f2(f1(10)))))
```
The last two answers should be the same, why?
```julia; hold=true; echo=false
choices = [
L"As $f_4(f_3(f_2(f)_1(x))))=(f_4 \circ f_3 \circ f_2 \circ f_1)(x)$",
L"As $f_4(f_3(f_2(f_1(x))))=(f_1 \circ f_2 \circ f_3 \circ f_4)(x)$",
"As the latter is more complicated than the former."
]
ans=1
radioq(choices, ans)
```
Let $g_1$, $g_2$, $g_3$, and $g_4$ denote the inverse functions. Clearly, $g_1(x) = x- d/c$ and $g+4(x) = x - a/c$, as the inverse of adding a constant is subtracting the constant.
What is $g_2(x)=f_2^{-1}(x)$?
```julia; hold=true; echo=false
choices = ["``g_2(x) = x^{-1}``", "``g_2(x) = x``", "``g_2(x) = x -1``"]
ans = 1
radioq(choices, ans)
```
What is $g_3(x)=f_3^{-1}(x)$?
```julia; hold=true; echo=false
choices = [
raw"``c^2/(b\cdot c - a\cdot d) \cdot x``",
raw"``(b\cdot c-a\cdot d)/c^2 \cdot x``",
raw"``c^2 x``"]
ans = 1
radioq(choices, ans)
```
Given these, what is the value of $g_4(g_3(g_2(g_1(f_4(f_3(f_2(f_1(10))))))))$?
```julia; echo=false
g1(x) = x - 𝒅/𝒄; g2(x) = 1/x; g3(x) = 1/((𝒃*𝒄-𝒂*𝒅)/𝒄^2) *x; g4(x)= x - 𝒂/𝒄
val1 = g4(g3(g2(g1(f4(f3(f2(f1(10))))))))
numericq(val1)
```
What about the value of $g_1(g_2(g_3(g_4(f_4(f_3(f_2(f_1(10))))))))$?
```julia; hold=true; echo=false
val = g1(g2(g3(g4(f4(f3(f2(f1(10))))))))
numericq(val)
```

View File

@@ -0,0 +1,574 @@
# Overview of Julia commands
```julia; echo=false; results="hidden"
using CalculusWithJulia
using CalculusWithJulia.WeaveSupport
const frontmatter = (
title = "Overview of Julia commands",
description = "Calculus with Julia: Overview of Julia commands",
tags = ["CalculusWithJulia", "precalc", "overview of julia commands"],
);
nothing
```
The [`Julia`](http://www.julialang.org) programming language is well suited as a computer accompaniment while learning the concepts of calculus. The following overview covers the language-specific aspects of the pre-calculus part of the [Calculus with Julia](calculuswithjulia.github.io) notes.
## Installing `Julia`
`Julia` is an *open source* project which allows anyone with a supported computer to use it. To install locally, the [downloads](https://julialang.org/downloads/) page has several different binaries for installation. Additionally, the downloads page contains a link to a docker image. For Microsoft Windows, the new [juilaup](https://github.com/JuliaLang/juliaup) installer may be of interest; it is available from the Windows Store.
`Julia` can also be compiled from source.
`Julia` can also be run through the web. The [https://mybinder.org/](https://mybinder.org/) service in particular allows free access, though limited in terms of allotted memory and with a relatively short timeout for inactivity.
[Launch Binder](https://mybinder.org/v2/gh/CalculusWithJulia/CwJScratchPad.git/master)
## Interacting with `Julia`
The html version of the **Calculus With Julia** notes are formatted as
Pluto notebooks. `Pluto` is one of many different means for a user to
interact with a `Julia` process.
At a basic level, `Julia` provides a means to read commands or instructions, evaluate those commands, and then print or return those commands. At a user level, there are many different ways to interact with the reading and printing. For example:
* The REPL. The `Julia` terminal is the built-in means to interact with `Julia`. A `Julia` Terminal has a command prompt, after which commands are typed and then sent to be evaluated by the `enter` key. The terminal may look something like the following where `2+2` is evaluated:
----
```verbatim
$ julia
_
_ _ _(_)_ | Documentation: https://docs.julialang.org
(_) | (_) (_) |
_ _ _| |_ __ _ | Type "?" for help, "]?" for Pkg help.
| | | | | | |/ _` | |
| | |_| | | | (_| | | Version 1.7.0 (2021-11-30)
_/ |\__'_|_|_|\__'_| | Official https://julialang.org/ release
|__/ |
julia> 2 + 2
4
```
----
* An IDE. For programmers, an integrated development environment is often used to manage bigger projects. `Julia` has `Juno` and `VSCode`.
* A notebook. The [Project Juptyer](https://jupyter.org/) provides a notebook interface for interacting with `Julia` and a more `IDE` style `jupyterlab` interface. A jupyter notebook has cells where commands are typed and immediately following is the printed output returned by `Julia`. The output of a cell depends on the state of the kernel when the cell is computed, not the order of the cells in the notebook. Cells have a number attached, showing the execution order. The `Juypter` notebook is used by `binder` and can be used locally through the `IJulia` package. This notebook has the ability to display many different types of outputs in addition to plain text, such as images, marked up math text, etc.
* The [Pluto](https://github.com/fonsp/Pluto.jl) package provides a *reactive* notebook interface. Reactive means when one "cell" is modified and executed, the new values cascade to all other dependent cells which in turn are updated. This is very useful for exploring a parameter space, say. These html pages are formatted as `Pluto` notebooks, which makes them able to be easily run on the reader's desktop.
The `Pluto` interface has some idiosyncracies that need explanation:
* Cells can only have one command within them. Multiple-command cells must be contained in a `begin` block or a `let` block.
* By default, the cells are *reactive*. This means when a variable in one cell is changed, then any references to that variable are also updated -- like a spreadsheet. This is fantastic for updating several computations at once. However it means variable names can not be repeated within a page. Pedagogically, it is convenient to use variable names and function names (e.g., `x` and `f`) repeatedly, but this is only possible *if* they are within a `let` block or a function body.
* To not repeat names, but to be able to reference a value from cell-to-cell, some Unicode variants are used within a page. Visually these look familiar, but typing the names requires some understanding of Unicode input. The primary usages is *bold italic* (e.g., `\bix[tab]` or `\bif[tab]`) or *bold face* (e.g. `\bfx[tab]` or `bff[tab]`).
* The notebooks snapshot the packages they depend on, which is great for reproducability, but may mean older versions are silently used.
## Augmenting base `Julia`
The base `Julia` installation has many features, but leaves many others to `Julia`'s package ecosystem. These notes use packages to provide plotting, symbolic math, access to special functions, numeric routines, and more.
Within `Pluto`, using add-on packages is very simple, as `Pluto` downloads and installs packages when they are requested through a `using` or `import` directive.
----
For other interfaces to `Julia` some more detail is needed.
The `Julia` package manager makes add-on packages very easy to install.
Julia comes with just a few built-in packages, one being `Pkg` which manages subsequent package installation. To add more packages, we first must *load* the `Pkg` package. This is done by issuing the following command:
```julia
using Pkg
```
The `using` command loads the specified package and makes all its *exported* values available for direct use. There is also the `import` command which allows the user to select which values should be imported from the package, if any, and otherwise gives access to the new functionality through the dot syntax.
Packages need to be loaded just once per session.
To use `Pkg` to "add" another package, we would have a command like:
```julia; eval=false
Pkg.add("CalculusWithJulia")
```
This command instructs `Julia` to look at its *general registry* for the `CalculusWithJulia.jl` package, download it, then install it. Once installed, a package only needs to be brought into play with the `using` or `import` commands.
```julia; echo=false;
note("""
In a terminal setting, there is a package mode, entered by typing `]` as the leading character and exited by entering `<delete>` at a blank line. This mode allows direct access to `Pkg` with a simpler syntax. The command above would be just `add CalculusWithJulia`.)
""")
```
Packages can be updated through the command `Pkg.up()`, and removed with `Pkg.rm(pkgname)`.
By default packages are installed in a common area. It may be desirable to keep packages for projects isolated. For this the `Pkg.activate` command can be used. This feature allows a means to have reproducible environments even if `Julia` or the packages used are upgraded, possibly introducing incompatabilities.
For these notes, the following packages, among others, are used:
```julia; eval=false
Pkg.add("CalculusWithJulia") # for some simplifying functions and a few packages (SpecialFunctions, ForwardDiff)
Pkg.add("Plots") # for basic plotting
Pkg.add("SymPy") # for symbolic math
Pkg.add("Roots") # for solving `f(x)=0`
Pkg.add("QuadGk") # for integration
Pkg.add("HQuadrature") # for higher-dimensional integration
```
## `Julia` commands
In `Pluto`, commands are typed into a notebook cell:
```julia;
2 + 2 # use shift-enter to evaluate
```
Commands are executed by using `shift-enter` or the run button at the bottom right of a cell.
Multiple commands per cell are possible if a `begin` or `let` block is used.
Commands may be separated by new lines or semicolons.
On a given line, anything **after** a `#` is a *comment* and is not processed.
The results of the last command executed will be displayed in an
output area. Separating values by commas allows more than one value to be
displayed. Plots are displayed when the plot object is returned by the last executed command.
The state of a Pluto notebook is a result of all the cells in the notebook being executed. The cell order does not impact this and can be rearranged by the user.
## Numbers, variable types
`Julia` has many different number types beyond the floating point type employed by most calculators. These include
* Floating point numbers: `0.5`
* Integers: `2`
* Rational numbers: `1//2`
* Complex numbers `2 + 0im`
`Julia`'s parser finds the appropriate type for the value, when read in. The following all create the number ``1`` first as an integer, then a rational, then a floating point number, again as floating point number, and finally as a complex number:
```julia
1, 1//1, 1.0, 1e0, 1 + 0im
```
As much as possible, operations involving certain types of numbers will produce output of a given type. For example, both of these divisions produce a floating point answer, even though mathematically, they need not:
```julia;
2/1, 1/2
```
Some powers with negative bases, like `(-3.0)^(1/3)`, are not defined. However, `Julia` provides the special-case function `cbrt` (and `sqrt`) for handling these.
Integer operations may silently overflow, producing odd answers, at first glance:
```julia;
2^64
```
(Though the output is predictable, if overflow is taken into consideration appropriately.)
When different types of numbers are mixed, `Julia` will usually promote the values to a common type before the operation:
```julia;
(2 + 1//2) + 0.5
```
`Julia` will first add `2` and `1//2` promoting `2` to rational before doing so. Then add the result, `5//2` to `0.5` by promoting `5//2` to the floating point number `2.5` before proceeding.
`Julia` uses a special type to store a handful of irrational constants such as `pi`. The special type allows these constants to be treated without round off, until they mix with other floating point numbers. There are some functions that require these be explicitly promoted to floating point. This can be done by calling `float`.
The standard mathematical operations are implemented by `+`, `-`, `*`, `/`, `^`. Parentheses are used for grouping.
### Vectors
A vector is an indexed collection of similarly typed values. Vectors can be constructed with square brackets (syntax for concatenation):
```julia;
[1, 1, 2, 3, 5, 8]
```
Values will be promoted to a common type (or type `Any` if none exists). For example, this vector will have type `Float64` due to the `1/3` computation:
```julia
[1, 1//2, 1/3]
```
(Vectors are used as a return type from some functions, as such, some familiarity is needed.)
Regular arithmetic sequences can be defined by either:
* Range operations: `a:h:b` or `a:b` which produces a generator of values starting at `a` separated by `h` (`h` is `1` in the last form) until they reach `b`.
* The `range` function: `range(a, b, length=n)` which produces a generator of `n` values between `a` and `b`;
These constructs return range objects. A range object *compactly* stores the values it references. To see all the values, they can be collected with the `collect` function, though this is rarely needed in practice.
Random sequences are formed by `rand`, among others:
```julia
rand(3)
```
The call `rand()` returns a single random number (in ``[0,1)``.)
## Variables
Values can be assigned variable names, with `=`. There are some variants
```julia;
u = 2
a_really_long_name = 3
a0, b0 = 1, 2 # multiple assignment
a1 = a2 = 0 # chained assignment, sets a2 and a1 to 0
```
The names can be short, as above, or more verbose. Variable names can't start
with a number, but can include numbers. Variables can also include
[Unicode](../misc/unicode.html) or even be an emoji.
```julia;
α, β = π/3, π/4
```
We can then use the variables to reference the values:
```julia;
u + a_really_long_name + a0 - b0 + α
```
Within `Pluto`, names are idiosyncratic: within the global scope, only a single usage is possible per notebook; functions and variables can be freely renamed; structures can be redefined or renamed; ...
Outside of `Pluto`, names may be repurposed, even with values of
different types (`Julia` is a dynamic language), save for (generic) function
names, which have some special rules and can only be redefined as
another function. Generic functions are central to `Julia`'s
design. Generic functions use a method table to dispatch on, so once a
name is assigned to a generic function, it can not be used as a
variable name; the reverse is also true.
## Functions
Functions in `Julia` are first-class objects. In these notes, we often pass them as arguments to other functions. There are many built-in functions and it is easy to define new functions.
We "call" a function by passing argument(s) to it, grouped by parentheses:
```julia;
sqrt(10)
sin(pi/3)
log(5, 100) # log base 5 of 100
```
With out parentheses, the name (usually) refers to a generic name and the output lists the number of available implementations (methods).
```julia;
log
```
### Built-in functions
`Julia` has numerous built-in [mathematical](http://julia.readthedocs.io/) functions, we review a few here:
#### Powers logs and roots
Besides `^`, there are `sqrt` and `cbrt` for powers. In addition basic functions for exponential and logarithmic functions:
```verbatim
sqrt, cbrt
exp
log # base e
log10, log2, # also log(b, x)
```
#### Trigonometric functions
The ```6``` standard trig functions are implemented; their implementation for degree arguments; their inverse functions; and the hyperbolic analogs.
```verbatim
sin, cos, tan, csc, sec, cot
asin, acos, atan, acsc, asec, acot
sinh, cosh, tanh, csch, sech, coth
asinh, acosh, atanh, acsch, asech, acoth
```
If degrees are preferred, the following are defined to work with arguments in degrees:
```verbatim
sind, cosd, tand, cscd, secd, cotd
```
#### Useful functions
Other useful and familiar functions are defined:
- `abs`: absolute value
- `sign`: is ``\lvert x \rvert/x`` except at ``x=0``, where it is ``0``.
- `floor`, `ceil`: greatest integer less or least integer greater
- `max(a,b)`, `min(a,b)`: larger (or smaller) of `a` or `b`
- `maximum(xs)`, `minimum(xs)`: largest or smallest of the collection referred to by `xs`
----
In a Pluto session, the "Live docs" area shows inline documentation for the current object.
For other uses of `Julia`, the built-in documentation for an object is
accessible through a leading `?`, say, `?sign`. There is also the `@doc` macro, for example:
```julia; eval=false
@doc sign
```
----
### User-defined functions
Simple mathematical functions can be defined using standard mathematical notation:
```julia;
f(x) = -16x^2 + 100x + 2
```
The argument `x` is passed into the body of function.
Other values are found from the environment where defined:
```julia; hold=true;
a = 1
f(x) = 2*a + x
f(3) # 2 * 1 + 3
a = 4
f(3) # now 2 * 4 + 3
```
User-defined functions can have ``0``, ``1`` or more arguments:
```julia;
area(w, h) = w*h
```
Julia makes different *methods* for *generic* function names, so function definitions whose argument specification is different are for different uses, even if the name is the same. This is *polymorphism*. The practical use is that it means users need only remember a much smaller set of function names, as attempts are made to give common expectations to the same name. (That is, `+` should be used only for "add" ing objects, however defined.)
Functions can be defined with *keyword* arguments that may have defaults specified:
```julia; hold=true;
f(x; m=1, b=0) = m*x + b # note ";"
f(1) # uses m=1, b=0 -> 1 * 1 + 0
f(1, m=10) # uses m=10, b=0 -> 10 * 1 + 0
f(1, m=10, b=5) # uses m=10, b=5 -> 10 * 1 + 5
```
Longer functions can be defined using the `function` keyword, the last command executed is returned:
```julia;
function 𝒇(x)
y = x^2
z = y - 3
z
end
```
Functions without names, *anonymous functions*, are made with the `->` syntax as in:
```julia;
x -> cos(x)^2 - cos(2x)
```
These are useful when passing a function to another function or when
writing a function that *returns* a function.
## Conditional statements
`Julia` provides the traditional `if-else-end` statements, but more conveniently has a `ternary` operator for the simplest case:
```julia;
our_abs(x) = (x < 0) ? -x : x
```
## Looping
Iterating over a collection can be done with the traditional `for` loop. However, there are list comprehensions to mimic the definition of a set:
```julia;
[x^2 for x in 1:10]
```
Comprehensions can be filtered through the `if` keyword
```julia
[x^2 for x in 1:10 if iseven(x)]
```
This is more efficient than creating the collection then filtering, as is done with:
```julia
filter(iseven, [x^2 for x in 1:10])
```
## Broadcasting, mapping
A function can be applied to each element of a vector through mapping or broadcasting. The latter is implemented in a succinct notation. Calling a function with a "." before its opening "(` will apply the function to each individual value in the argument:
```julia;
xs = [1,2,3,4,5]
sin.(xs) # gives back [sin(1), sin(2), sin(3), sin(4), sin(5)]
```
For "infix" operators, the dot precedes the operator, as in this example instructing pointwise multiplication of each element in `xs`:
```juila;
xs .* xs
```
Alternatively, the more traditional `map` can be used:
```julia;
map(sin, xs)
```
## Plotting
Plotting is *not* built-in to `Julia`, rather added through add-on
packages. `Julia`'s `Plots` package is an interface to several
plotting packages. We mention `plotly` (built-in) for web based
graphics, `pyplot`, and `gr` (also built into `Plots`) for other graphics.
We must load `Plots` before we can plot (and it must be installed before we can load it):
```julia;
using Plots
```
With `Plots` loaded, we can plot a function by passing the function object by name to `plot`, specifying the range of ```x``` values to show, as follows:
```julia;
plot(sin, 0, 2pi) # plot a function - by name - over an interval [a,b]
```
```julia; echo=false
note("""
This is in the form of **the** basic pattern employed: `verb(function_object, arguments...)`. The verb in this example is `plot`, the object `sin`, the arguments `0, 2pi` to specify `[a,b]` domain to plot over.
""")
```
Plotting more than one function over ```[a,b]``` is achieved through the `plot!` function, which modifies the existing plot (`plot` creates a new one) by adding a new layer:
```julia;
plot(sin, 0, 2pi)
plot!(cos, 0, 2pi)
plot!(zero, 0, 2pi) # add the line y=0
```
Individual points are added with `scatter` or `scatter!`:
```julia;
plot(sin, 0, 2pi, legend=false)
plot!(cos, 0, 2pi)
scatter!([pi/4, pi+pi/4], [sin(pi/4), sin(pi + pi/4)])
```
(The extra argument `legend=false` suppresses the automatic legend drawing. There are many other useful arguments to adjust a graphic. For example, passing `markersize=10` to the `scatter!` command would draw the points larger than the default.)
Plotting an *anonymous* function is a bit more immediate than the two-step approach of defining a named function then calling `plot` with this as an argument:
```julia;
plot( x -> exp(-x/pi) * sin(x), 0, 2pi)
```
The `scatter!` function used above takes two vectors of values to describe the points to plot, one for the ``x`` values and one for the matching ``y`` values. The `plot` function can also produce plots with this interface. For example, here we use a comprehension to produce `y` values from the specified `x` values:
```julia; hold=true;
xs = range(0, 2pi, length=251)
ys = [sin(2x) + sin(3x) + sin(4x) for x in xs]
plot(xs, ys)
```
## Equations
Notation for `Julia` and math is *similar* for functions - but not for equations. In math, an equation might look like:
```math
x^2 + y^2 = 3
```
In `Julia` the equals sign is **only** for *assignment* and
*mutation*. The *left-hand* side of an equals sign in `Julia` is
reserved for a) variable assignment; b) function definition (via `f(x)
= ...`); c) indexed mutation of a vector or array; d) mutation of
fields in a structure. (Vectors are indexed by a number allowing
retrieval and mutation of the stored value in the container. The
notation mentioned here would be `xs[2] = 3` to mutate the 2nd element
of `xs` to the value `3`.
## Symbolic math
Symbolic math is available through an add-on package `SymPy` (among others). Once loaded, symbolic variables are created with the macro `@syms`:
```julia
using SymPy
```
```julia;
@syms x a b c
```
(A macro rewrites values into other commands before they are interpreted. Macros are prefixed with the `@` sign. In this use, the "macro" `@syms` translates `x a b c` into a command involving `SymPy`s `symbols` function.)
Symbolic expressions - unlike numeric expressions - are not immediately evaluated, though they may be simplified:
```julia;
p = a*x^2 + b*x + c
```
To substitute a value, we can use `Julia`'s `pair` notation (`variable=>value`):
```julia;
p(x=>2), p(x=>2, a=>3, b=>4, c=>1)
```
This is convenient notation for calling the `subs` function for `SymPy`.
SymPy expressions of a single free variable can be plotted directly:
```julia;
plot(64 - (1/2)*32 * x^2, 0, 2)
```
* SymPy has functions for manipulating expressions: `simplify`, `expand`, `together`, `factor`, `cancel`, `apart`, ``...``
* SymPy has functions for basic math: `factor`, `roots`, `solve`, `solveset`, ``\dots``
* SymPy has functions for calculus: `limit`, `diff`, `integrate`, ``\dots``

View File

@@ -0,0 +1,558 @@
# Inequalities, Logical expressions
In this section we use the following package:
```julia
using CalculusWithJulia # loads the `SpecialFunctions` package
```
```julia; echo=false; results="hidden"
using CalculusWithJulia.WeaveSupport
using Plots
const frontmatter = (
title = "Inequalities, Logical expressions",
description = "Calculus with Julia: Inequalities, Logical expressions",
tags = ["CalculusWithJulia", "precalc", "inequalities, logical expressions"],
);
nothing
```
## Boolean values
In mathematics it is common to test if an expression is true or
false. For example, is the point $(1,2)$ inside the disc $x^2 + y^2
\leq 1$? We would check this by substituting $1$ for $x$ and $2$ for
$y$, evaluating both sides of the inequality and then assessing if the
relationship is true or false. In this case, we end up with a comparison of $5 \leq
1$, which we of course know is false.
`Julia` provides numeric comparisons that allow this notation to be exactly mirrored:
```julia;hold=true
x, y = 1, 2
x^2 + y^2 <= 1
```
The response is `false`, as expected. `Julia` provides
[Boolean](http://en.wikipedia.org/wiki/Boolean_data_type) values
`true` and `false` for such questions. The same process is followed as was described mathematically.
The set of numeric comparisons is nearly the same as the mathematical
counterparts: `<`, `<=`, `==`, `>=`, `>`. The syntax for less than or
equal can also be represented with the Unicode `≤` (generated by
`\le[tab]`). Similarly, for greater than or equal, there is
`\ge[tab]`.
```julia; echo=false;
alert("The use of `==` is necessary, as `=` is used for assignment and mutation.")
```
The `!` operator takes a boolean value and negates it. It uses prefix notation:
```julia;
!true
```
For convenience, `a != b` can be used in place of `!(a == b)`.
## Algebra of inequalities
To illustrate, let's see that the algebra of expressions works as expected.
For example, if $a < b$ then for any $c$ it is also true that $a + c < b + c$.
We can't "prove" this through examples, but we can investigate it by
the choice of various values of $a$, $b$, and $c$. For example:
```julia;hold=true
a,b,c = 1,2,3
a < b, a + c < b + c
```
Or in reverse:
```julia;hold=true
a,b,c = 3,2,1
a < b, a + c < b + c
```
Trying other choices will show that the two answers are either both `false` or both `true`.
```julia; echo=false;
alert(""" Well, almost... When `Inf` or `NaN` are involved, this may not hold, for example `1 + Inf < 2 + Inf` is actually `false`. As would be `1 + (typemax(1)-1) < 2 + (typemax(1)-1)`.""")
```
So adding or subtracting most any finite value from an inequality will preserve the inequality, just as it does for equations.
What about addition and multiplication?
Consider the case $a < b$ and $c > 0$. Then $ca < cb$. Here we investigate using ``3`` random values (which will be positive):
```julia;hold=true
a,b,c = rand(3) # 3 random numbers in [0,1)
a < b, c*a < c*b
```
Whenever these two commands are run, the two logical values should be identical, even though the specific values of `a`, `b`, and `c` will vary.
The restriction that $c > 0$ is needed. For example, if $c = -1$, then we have $a < b$ if and only if $-a > -b$. That is the inequality is "flipped."
```julia;hold=true
a,b = rand(2)
a < b, -a > -b
```
Again, whenever this is run, the two logical values should be the same.
The values $a$ and $-a$ are the same distance from $0$, but on opposite sides. Hence if $0 < a < b$, then $b$ is farther from $0$ than $a$, so $-b$ will be farther from $0$ than $-a$, which in this case says $-b < -a$, as expected.
Finally, we have the case of division. The relation of $x$ and $1/x$
(for $x > 0$) is that the farther $x$ is from $0$, the closer $1/x$ is
to $0$. So large values of $x$ make small values of $1/x$. This leads
to this fact for $a,b > 0$: $a < b$ if and only if $1/a > 1/b$.
We can check with random values again:
```julia;hold=true
a,b = rand(2)
a < b, 1/a > 1/b
```
In summary we investigated numerically that the following hold:
- `a < b` if and only if `a + c < b + c` for all finite `a`, `b`, and `c`.
- `a < b` if and only if `c*a < c*b` for all finite `a` and `b`, and finite, positive `c`.
- `a < b` if and only if `-a > -b` for all finite `a` and `b`.
- `a < b` if and only if `1/a > 1/b` for all finite, positive `a` and `b`.
#### Examples
We now show some inequalities highlighted on this [Wikipedia](http://en.wikipedia.org/wiki/Inequality_%28mathematics%29) page.
Numerically investigate the fact $e^x \geq 1 + x$ by showing it is
true for three different values of $x$. We pick $x=-1$, $0$, and $1$:
```julia;hold=true;
x = -1; exp(x) >= 1 + x
x = 0; exp(x) >= 1 + x
x = 1; exp(x) >= 1 + x
```
Now, let's investigate that for any distinct real numbers, $a$ and $b$, that
```math
\frac{e^b - e^a}{b - a} > e^{(a+b)/2}
```
For this, we use `rand(2)` to generate two random numbers in $[0,1)$:
```julia;hold=true
a, b = rand(2)
(exp(b) - exp(a)) / (b-a) > exp((a+b)/2)
```
This should evaluate to `true` for any random choice of `a` and `b` returned by `rand(2)`.
Finally, let's investigate the fact that the harmonic mean, $2/(1/a +
1/b)$ is less than or equal to the geometric mean, $\sqrt{ab}$, which
is less than or equal to the quadratic mean, $\sqrt{a^2 +
b^2}/\sqrt{2}$, using two randomly chosen values:
```julia;hold=true
a, b = rand(2)
h = 2 / (1/a + 1/b)
g = (a * b) ^ (1 / 2)
q = sqrt((a^2 + b^2) / 2)
h <= g, g <= q
```
## Chaining, combining expressions: absolute values
The absolute value notation can be defined through cases:
```math
\lvert x\rvert = \begin{cases}
x & x \geq 0\\
-x & \text{otherwise}.
\end{cases}
```
The interpretation of $\lvert x\rvert$, as the distance on the number line of $x$
from $0$, means that many relationships are naturally expressed in
terms of absolute values. For example, a simple shift: $\lvert x -c\rvert$ is
related to the distance $x$ is from the number $c$. As common as they are, the concept can still be confusing when inequalities are involved.
For example, the expression $\lvert x - 5\rvert < 7$ has solutions which are all
values of $x$ within $7$ units of $5$. This would be the values $-2< x < 12$.
If this isn't immediately intuited, then formally $\lvert x - 5\rvert <7$
is a compact representation of a chain of inequalities: $-7 < x-5 < 7$.
(Which is really two combined inequalities: $-7 < x-5$ *and* $x-5 < 7$.)
We can "add" ``5`` to each side to get $-2 < x < 12$, using the
fact that adding by a finite number does not change the inequality
sign.
Julia's precedence for logical
expressions, allows such statements to mirror the mathematical
notation:
```julia;
x = 18
abs(x - 5) < 7
```
This is to be expected, but we could also have written:
```julia;
-7 < x - 5 < 7
```
Read aloud this would be "minus ``7`` is less than ``x`` minus ``5``
**and** ``x`` minus ``5`` is less than ``7``".
The "and" equations can be combined as above with a natural notation. However,
an equation like $\lvert x - 5\rvert > 7$ would emphasize
an **or** and be "``x`` minus ``5`` less than minus ``7`` **or** ``x`` minus ``5``
greater than ``7``". Expressing this requires some new notation.
The *boolean shortcut operators* `&&` and `||` implement "and" and "or." (There are also *bitwise* boolean operators `&` and `|`, but we only describe the former.)
Thus we could write $-7 < x-5 < 7$ as
```julia;
(-7 < x - 5) && (x - 5 < 7)
```
and could write $\lvert x-5\rvert > 7$ as
```julia;
(x - 5 < -7) || (x - 5 > 7)
```
(The first expression is false for $x=18$ and the second expression true, so the "or"ed result is `true` and the "and" result if `false`.)
##### Example
One of [DeMorgan's Laws](http://en.wikipedia.org/wiki/De_Morgan%27s_laws) states that
"not (A and B)" is the same as "(not A) or (not B)". This is a kind of
distributive law for "not", but note how the "and" changes to
"or". We can verify this law systematically. For example, the
following shows it true for ``1`` of the ``4`` possible cases for the pair
`A`, `B` to take:
```julia;
A,B = true, false ## also true, true; false, true; and false, false
!(A && B) == !A || !B
```
## Precedence
The question of when parentheses are needed and when they are not is answered by the
[precedence](http://julia.readthedocs.org/en/latest/manual/mathematical-operations/#operator-precedence) rules
implemented. Earlier, we wrote
```julia;
(x - 5 < -7) || (x - 5 > 7)
```
To represent $\lvert x-5\rvert > 7$. Were the parentheses necessary? Let's just check.
```julia;
x - 5 < -7 || x - 5 > 7
```
So no, they were not in this case.
An operator (such as `<`, `>`, `||` above) has an associated associativity and precedence. The associativity is whether an expression like `a - b - c` is `(a-b) - c` or `a - (b-c)`. The former being left associative, the latter right. Of issue here is *precedence*, as in with two or more different operations, which happens first, second, ``\dots``.
The table in the manual on [operator precedence and associativity](https://docs.julialang.org/en/v1/manual/mathematical-operations/#Operator-Precedence-and-Associativity) shows that for these operations "control flow" (the `&&` above) is lower than "comparisons" (the `<`, `>`), which are lower than "Addition" (the `-` above). So the expression without parentheses would be equivalent to:
```julia
((x-5) < -7) && ((x-5) > 7)
```
(This is different than the precedence of the bitwise boolean operators, which have `&` with "Multiplication" and `|` with "Addition", so `x-5 < 7 | x - 5 > 7` would need parentheses.)
A thorough understanding of the precedence rules can help eliminate
unnecessary parentheses, but in most cases it is easier just to put
them in.
## Arithmetic with
For convenience, basic arithmetic can be performed with Boolean
values, `false` becomes $0$ and true $1$. For example, both these
expressions make sense:
```julia;
true + true + false, false * 1000
```
The first example shows a common means used to count the number of
`true` values in a collection of Boolean values - just add them.
This can be cleverly exploited. For example, the following expression returns `x` when it is positive and $0$ otherwise:
```julia;
(x > 0) * x
```
There is a built in function, `max` that can be used for this: `max(0, x)`.
This expression returns `x` if it is between $-10$ and $10$ and otherwise $-10$ or $10$ depending on whether $x$ is negative or positive.
```julia;
(x < -10)*(-10) + (x >= -10)*(x < 10) * x + (x>=10)*10
```
The `clamp(x, a, b)` performs this task more generally, and is used as in `clamp(x, -10, 10)`.
## Questions
###### Question
Is `e^pi` or `pi^e` greater?
```julia; hold=true; echo=false;
choices = [
"`e^pi` is greater than `pi^e`",
"`e^pi` is equal to `pi^e`",
"`e^pi` is less than `pi^e`"
]
ans = 1
radioq(choices, ans)
```
###### Question
Is $\sin(1000)$ positive?
```julia; hold=true; echo=false;
ans = (sin(1000) > 0)
yesnoq(ans)
```
###### Question
Suppose you know $0 < a < b$. What can you say about the relationship between $-1/a$ and $-1/b$?
```julia; hold=true; echo=false;
choices = [
"``-1/a < -1/b``",
"``-1/a > -1/b``",
raw"``-1/a \geq -1/b``"]
ans = 3
radioq(choices, ans)
```
###### Question
Suppose you know $a < 0 < b$, is it true that $1/a > 1/b$?
```julia; hold=true; echo=false;
choices = ["Yes, it is always true.",
"It can sometimes be true, though not always.",
L"It is never true, as $1/a$ is negative and $1/b$ is positive"]
ans = 3
radioq(choices, ans)
```
###### Question
The `airyai` [function](http://en.wikipedia.org/wiki/Airy_function) is
a special function named after a British Astronomer who realized the
function's value in his studies of the rainbow. The `SpecialFunctions`
package must be loaded to include this function, which is done with the accompanying package `CalculusWithJulia`.
```julia;
airyai(0)
```
It is known that this function
is always positive for $x > 0$, though not so for negative values of
$x$. Which of these indicates the first negative value : `airyai(-1) <0`,
`airyai(-2) < 0`, ..., or `airyai(-5) < 0`?
```julia; hold=true; echo=false;
choices = ["`airyai($i) < 0`" for i in -1:-1:-5]
ans = 3
radioq(choices, ans, keep_order=true)
```
###### Question
By trying three different values of $x > 0$ which of these could possibly be always true:
```julia; hold=true; echo=false;
choices = ["`x^x <= (1/e)^(1/e)`",
"`x^x == (1/e)^(1/e)`",
"`x^x >= (1/e)^(1/e)`"]
ans = 3
radioq(choices, ans)
```
###### Question
Student logic says $(x+y)^p = x^p + y^p$. Of course, this isn't
correct for all $p$ and $x$. By trying a few points, which is true
when $x,y > 0$ and $0 < p < 1$:
```julia; hold=true; echo=false;
choices = ["`(x+y)^p < x^p + y^p`",
"`(x+y)^p == x^p + y^p`",
"`(x+y)^p > x^p + y^p`"]
ans = 1
radioq(choices, ans)
```
###### Question
According to Wikipedia, one of the following inequalities is always
true for $a, b > 0$ (as proved by I. Ilani in
JSTOR, AMM, Vol.97, No.1, 1990). Which one?
```julia; hold=true; echo=false;
choices = ["`a^a + b^b <= a^b + b^a`",
"`a^a + b^b >= a^b + b^a`",
"`a^b + b^a <= 1`"]
ans = 2
radioq(choices, ans)
```
###### Question
Is $3$ in the set $\lvert x - 2\rvert < 1/2$?
```julia; hold=true; echo=false;
val = abs(3-2) < 1/2
yesnoq(val)
```
###### Question
Which of the following is equivalent to $\lvert x - a\rvert > b$:
```julia; hold=true; echo=false;
choices = [raw"``-b < x - a < b``",
raw"`` -b < x-a \text{ and } x - a < b``",
raw"``x - a < -b \text{ or } x - a > b``"]
ans = 3
radioq(choices, ans)
```
###### Question
If $\lvert x - \pi\rvert < 1/10$ is $\lvert \sin(x) - \sin(\pi)\rvert < 1/10$?
Guess an answer based on a few runs of
```julia; hold=true; eval=false;
x = pi + 1/10 * (2rand()-1)
abs(x - pi) < 1/10, abs(sin(x) - sin(pi)) < 1/10
```
```julia; hold=true; echo=false;
booleanq(true)
```
###### Question
Does `12` satisfy $\lvert x - 3\rvert + \lvert x-9\rvert > 12$?
```julia; hold=true; echo=false;
x = 12
val = (abs(x -3) + abs(x-9) > 12)
yesnoq(val)
```
###### Question
Which of these will show DeMorgan's law holds when both values are `false`:
```julia; hold=true; echo=false;
choices = ["`!(false && false) == (!false && !false)`",
"`!(false && false) == (false || false)`",
"`!(false && false) == (!false || !false)`"]
ans = 3
radioq(choices, ans)
```
###### Question
For floating point numbers there are two special values `Inf` and `NaN`. For which of these is the answer always `false`:
```julia; hold=true; echo=false;
choices = ["`Inf < 3.0` and `3.0 <= Inf`",
"`NaN < 3.0` and `3.0 <= NaN`"]
ans = 2
radioq(choices, ans)
```
###### Question
The IEEE 754 standard is about floating point numbers, for which there are the special values `Inf`, `-Inf`, `NaN`, and, surprisingly, `-0.0` (as a floating point number and not `-0`, an integer). Here are 4 facts that seem reasonable:
- Positive zero is equal but not greater than negative zero.
- `Inf` is equal to itself and greater than everything else except `NaN`.
- `-Inf` is equal to itself and less then everything else except `NaN`.
- `NaN` is not equal to, not less than, and not greater than anything, including itself.
Do all four seem to be the case within `Julia`? Find your answer by trial and error.
```julia; hold=true; echo=false;
yesnoq(true)
```
###### Question
The `NaN` value is meant to signal an error in computation. `Julia` has value to indicate some data is missing or unavailable. This is `missing`. For `missing` values we have these computations:
```julia;
true && missing, true || missing
```
We see the value of `true || missing` is `true`. Why?
```julia; hold=true; echo=false;
choices = ["""
In the manual we can read that "In the expression `a || b`, the subexpression `b` is only evaluated if `a` evaluates to false." In this case `a` is `true` and so `a` is returned.
""",
"Since the second value is \"`missing`\", only the first is used. So `false || missing` would also be `false`"]
ans = 1
radioq(choices, ans)
```
The value for `true && missing` is `missing`, not a boolean value. What happens?
```julia; hold=true; echo=false;
choices = ["""
In the manual we can read that "In the expression `a && b`, the subexpression `b` is only evaluated if `a` evaluates to true." In this case, `a` is `false` so `b` is evaluated and returned. As `b` is just `missing` that is the return value.
""",
"Since the second value is \"`missing`\" all such answers would be missing."]
ans = 1
radioq(choices, ans)
```

View File

@@ -0,0 +1,665 @@
# Number systems
```julia; echo=false; results="hidden"
using CalculusWithJulia
using CalculusWithJulia.WeaveSupport
const frontmatter = (
title = "Number systems",
description = "Calculus with Julia: Number systems",
tags = ["CalculusWithJulia", "precalc", "number systems"],
);
nothing
```
In mathematics, there are many different number systems in common
use. For example by the end of pre-calculus, all of the following have
been introduced:
* The integers, $\{\dots, -3, -2, -1, 0, 1, 2, 3, \dots\}$;
* The rational numbers, $\{p/q: p, q \text{ are integers}, q \neq 0\}$;
* The real numbers, $\{x: -\infty < x < \infty\}$;
* The complex numbers, $\{a + bi: a,b \text{ are real numbers and } i^2=-1\}$.
On top of these, we have special subsets, such as the natural numbers $\{1, 2, \dots\}$ (sometimes including ``0``), the even numbers, the odd numbers, the positive numbers, the non-negative numbers, etc.
Mathematically, these number systems are naturally nested within each
other as integers are rational numbers which are real numbers, which
can be viewed as part of the complex numbers.
Calculators typically have just one type of number - floating point
values. These model the real numbers. `Julia`, on other other hand, has
a rich type system, and within that has many different number
types. There are types that model each of the four main systems above,
and within each type, specializations for how these values are stored.
Most of the details will not be of interest to all, and will be described later.
For now, let's consider the number ``1``. It can be viewed as either an integer, rational,
real, or complex number. To construct "``1``" in each type within `Julia`
we have these different styles:
```julia;
1, 1.0, 1//1, 1 + 0im
```
The basic number types in `Julia` are `Int`, `Float64`, `Rational` and `Complex`, though in fact there are many more, and the last two aren't even *concrete* types. This distinction is important, as the type of number dictates how it will be stored and how precisely the stored value can be expected to be to the mathematical value it models.
Though there are explicit constructors for these types, these notes
avoid them unless necessary, as `Julia`'s parser can distinguish these
types through an easy to understand syntax:
* integers have no decimal point;
* floating point numbers have a decimal point (or are in scientific notation);
* rationals are constructed from integers using the double division operator, `//`; and
* complex numbers are formed by including a term with the imaginary unit, `im`.
```julia; echo=false;
alert("""
Heads up, the difference between `1` and `1.0` is subtle.
Even more so, as `1.` will parse as `1.0`.
This means some expressions, such as `2.*3`, are ambigous, as the `.` might be part of the `2` (as in `2. * 3`) or the operation `*` (as in `2 .* 3`).
""")
```
Similarly, each type is printed slightly differently.
The key distinction is between integers and floating points. While
floating point values include integers, and so can be used exclusively
on the calculator, the difference is that an integer is guaranteed to
be an exact value, whereas a floating point value, while often an
exact representation of a number is also often just an
*approximate* value. This can be an advantage -- floating point values can
model a much wider range of numbers.
Now in nearly all cases the differences are not noticable. Take for instance this simple calculation involving mixed types.
```julia;
1 + 1.25 + 3//2
```
The sum of an integer, a floating point number and rational number returns a floating point number without a complaint.
This is because behind the scenes, `Julia` will often "promote" a type to match, so for example to compute `1 + 1.25` the integer `1` will be promoted to a floating point value and the two values are then added. Similarly, with `2.25 + 3//2`, where the fraction is promoted to the floating point value `1.5` and addition is carried out.
As floating point numbers may be approximations, some values are not quite what they would be mathematically:
```julia;
sqrt(2) * sqrt(2) - 2, sin(pi), 1/10 + 1/5 - 3/10
```
These values are *very* small numbers, but not exactly $0$, as they are mathematically.
----
The only common issue is with powers. `Julia` tries to keep a
predictable output from the input types (not their values). Here are the two main cases
that arise where this can cause unexpected results:
* integer bases and integer exponents can *easily* overflow. Not only `m^n` is always an integer, it is always an integer with a fixed storage size computed from the sizes of `m` and `n`. So the powers can quickly get too big. This can be especially noticeable on older $32$-bit machines, where too big is $2^{32} = 4,294,967,296$. On $64$-bit machines, this limit is present but much bigger.
Rather than give an error though, `Julia` gives seemingly arbitrary answers, as can be seen in this example on a $64$-bit machine:
```julia;
2^62, 2^63
```
(They aren't arbitrary, rather integer arithmetic is implemented as modular arithmetic.)
This could be worked around, as it is with some programming languages,
but it isn't, as it would slow down this basic computation. So, it is
up to the user to be aware of cases where their integer values can
grow to big. The suggestion is to use floating point numbers in this
domain, as they have more room, at the cost of sometimes being
approximate values.
* the `sqrt` function will give a domain error for negative values:
```julia;
sqrt(-1.0)
```
This is because for real-valued inputs `Julia` expects to return a
real-valued output. Of course, this is true in mathematics until the
complex numbers are introduced. Similarly in `Julia` - to take square
roots of negative numbers, start with complex numbers:
```julia;
sqrt(-1.0 + 0im)
```
* At one point, `Julia` had an issue with a third type of power:
integer bases and negative integer exponents. For example
`2^(-1)`. This is now special cased, though only for numeric
literals. If `z=-1`, `2^z` will throw a `DomainError`. Historically,
the desire to keep a predictable type for the output (integer) led to
defining this case as a domain error, but its usefulness led to
special casing.
## Additional details.
What follows is only needed for those seeking more background.
Julia has *abstract* number types `Integer`, `Real`, and `Number`. All
four types described above are of type `Number`, but `Complex` is not
of type `Real`.
However, a specific value is an instance of a *concrete* type. A
concrete type will also include information about how the value is
stored. For example, the *integer* `1` could be stored using $64$ bits
as a signed integers, or, should storage be a concern, as an $8$ bits
signed or even unsigned integer, etc.. If storage
isn't an issue, but exactness at all scales is, then it can be stored in a manner
that allows for the storage to grow using "big" numbers.
These distinctions can be seen in how `Julia` parses these three values:
* `1234567890` will be a $64$-bit integer (on newer machines), `Int64`
* `12345678901234567890` will be a $128$ bit integer, `Int128`
* `1234567890123456789012345678901234567890` will be a big integer, `BigInt`
Having abstract types allows programmers to write functions that will
work over a wide range of input values that are similar, but have
different implementation details.
### Integers
Integers are often used casually, as they come about from parsing. As with a calculator, floating point numbers *could* be used for integers, but in `Julia` - and other languages - it proves useful to have numbers known to have *exact* values. In `Julia` there are built-in number types for integers stored in $8$, $16$, $32$, $64$, and $128$ bits and `BigInt`s if the previous aren't large enough. ($8$ bits can hold $8$ binary values representing $1$ of $256=2^8$ possibilities, whereas the larger $128$ bit can hold one of $2^{128}$ possibilities.) Smaller values can be more efficiently used, and this is leveraged at the system level, but not a necessary distinction with calculus where the default size along with an occasional usage of `BigInt` suffice.
### Floating point numbers
[Floating point](http://en.wikipedia.org/wiki/Floating_point) numbers
are a computational model for the real numbers. For floating point
numbers, $64$ bits are used by default for both $32$- and $64$-bit systems, though other storage sizes can be requested. This gives
a large ranging - but still finite - set of real numbers that can be
represented. However, there are infinitely many real numbers just
between $0$ and $1$, so there is no chance that all can be represented
exactly on the computer with a floating point value. Floating point
then is *necessarily* an approximation for all but a subset of the
real numbers. Floating point values can be viewed in normalized
[scientific notation](http://en.wikipedia.org/wiki/Scientific_notation)
as $a\cdot 2^b$ where $a$ is the *significand* and $b$ is the
*exponent*. Save for special values, the significand $a$ is normalized to satisfy $1 \leq \lvert a\rvert <
2$, the exponent can be taken to be an integer, possibly negative.
As per IEEE Standard 754, the `Float64` type gives 52 bits to the precision (with an additional implied one), 11 bits to the exponent and the other bit is used to represent the sign. Positive, finite, floating point numbers have a range approximately between $10^{-308}$ and $10^{308}$, as 308 is about $\log_{10}\cdot 2^{1023}$. The numbers are not evenly spread out over this range, but, rather, are much more concentrated closer to $0$.
```julia; echo=false;
alert("""
You can discover more about the range of floating point values provided by calling a few different functions.
- `typemax(0.0)` gives the largest value for the type (`Inf` in this case).
- `prevfloat(Inf)` gives the largest finite one, in general `prevfloat` is the next smallest floating point value.
- `nextfloat(-Inf)`, similarly, gives the smallest finite floating point value, and in general returns the next largest floating point value.
- `nextfloat(0.0)` gives the closest positive value to 0.
- `eps()` gives the distance to the next floating point number bigger than `1.0`. This is sometimes referred to as machine precision.
""", title="More on floating point", label="More on the range of floating point values")
```
#### Scientific notation
Floating point numbers may print in a familiar manner:
```julia;
x = 1.23
```
or may be represented in scientific notation:
```julia;
6.23 * 10.0^23
```
The special coding `aeb` (or if the exponent is negative `ae-b`) is
used to represent the number $a \cdot 10^b$ ($1 \leq a < 10$). This
notation can be used directly to specify a floating point value:
```julia;
avagadro = 6.23e23
```
Here `e` is decidedly *not* the Euler number, rather syntax to separate the exponent from the mantissa.
The first way of representing this number required using `10.0` and not `10` as the integer power will return an integer and even for 64-bit systems is only valid up to `10^18`. Using scientific notation avoids having to concentrate on such limitations.
##### Example
Floating point values in scientific notation will always be normalized. This is easy for the computer to do, but tedious to do by hand. Here we see:
```julia;
4e30 * 3e40
```
```julia;
3e40 / 4e30
```
The power in the first is ``71``, not ``70 = 30+40``, as the product of ``3`` and ``4`` as ``12`` or `1.2e^1`. (We also see the artifact of `1.2` not being exactly representable in floating point.)
##### Example: 32-bit floating point
In some uses, such as using a GPU, ``32``-bit floating point (single precision) is also
common. These values may be specified with an `f` in place of the `e`
in scientific notation:
```julia
1.23f0
```
As with the use of `e`, some exponent is needed after the `f`, even if it is `0`.
#### Special values: Inf, -Inf, NaN
The coding of floating point numbers also allows for the special
values of `Inf`, `-Inf` to represent positive and negative
infinity. As well, a special value `NaN` ("not a number") is used to
represent a value that arises when an operation is not closed (e.g.,
$0.0/0.0$ yields `NaN`). (Technically `NaN` has several possible "values," a point ignored here.) Except for negative bases, the floating point
numbers with the addition of `Inf` and `NaN` are closed under the
operations `+`, `-`, `*`, `/`, and `^`. Here are some computations
that produce `NaN`:
```julia;
0/0, Inf/Inf, Inf - Inf, 0 * Inf
```
Whereas, these produce an infinity
```julia;
1/0, Inf + Inf, 1 * Inf
```
Finally, these are mathematically undefined, but still yield a finite value with `Julia`:
```julia;
0^0, Inf^0
```
#### Floating point numbers and real numbers
Floating point numbers are an abstraction for the real numbers. For
the most part this abstraction works in the background, though there
are cases where one needs to have it in mind. Here are a few:
* For real and rational numbers, between any two numbers $a < b$,
there is another real number in between. This is not so for floating
point numbers which have a finite precision. (Julia has some
functions for working with this distinction.)
* Floating point numbers are approximations for most values, even
simple rational ones like $1/3$. This leads to oddities such as this value
not being $0$:
```julia;
sqrt(2)*sqrt(2) - 2
```
It is no surprise that an irrational number, like $\sqrt{2}$, can't be represented **exactly** within floating point, but it is perhaps surprising that simple numbers can not be, so $1/3$, $1/5$, $\dots$ are approximated. Here is a surprising-at-first consequence:
```julia;
1/10 + 2/10 == 3/10
```
That is adding `1/10` and `2/10` is not exactly `3/10`, as expected mathematically.
Such differences are usually very small and are generally attributed to rounding error. The user needs to be mindful when testing for equality, as is done above with the `==` operator.
* Floating point addition is not necessarily associative, that is the property $a + (b+c) = (a+b) + c$ may not hold exactly. For example:
```julia;
1/10 + (2/10 + 3/10) == (1/10 + 2/10) + 3/10
```
* For real numbers subtraction of similar-sized numbers is not exceptional, for example $1 - \cos(x)$ is positive if $0 < x < \pi/2$, say. This will not be the case for floating point values. If $x$ is close enough to $0$, then $\cos(x)$ and $1$ will be so close, that they will be represented by the same floating point value, `1.0`, so the difference will be zero:
```julia;
1.0 - cos(1e-8)
```
### Rational numbers
Rational numbers can be used when the exactness of the number is more
important than the speed or wider range of values offered by floating
point numbers. In `Julia` a rational number is comprised of a
numerator and a denominator, each an integer of the same type, and
reduced to lowest terms. The operations of addition, subtraction,
multiplication, and division will keep their answers as rational
numbers. As well, raising a rational number to a positive, integer
value will produce a rational number.
As mentioned, these are constructed using double slashes:
```julia;
1//2, 2//1, 6//4
```
Rational numbers are exact, so the following are identical to their mathematical counterparts:
```julia;
1//10 + 2//10 == 3//10
```
and associativity:
```julia;
(1//10 + 2//10) + 3//10 == 1//10 + (2//10 + 3//10)
```
Here we see that the type is preserved under the basic operations:
```julia;
(1//2 + 1//3 * 1//4 / 1//5) ^ 6
```
For powers, a non-integer exponent is converted to floating point, so this operation is defined, though will always return a floating point value:
```julia;
(1//2)^(1//2) # the first parentheses are necessary as `^` will be evaluated before `//`.
```
##### Example: different types of real numbers
This table shows what attributes are implemented for the different types.
```julia; echo=false;
using DataFrames
attributes = ["construction", "exact", "wide range", "has infinity", "has `-0`", "fast", "closed under"]
integer = [q"1", "true", "false", "false", "false", "true", "`+`, `-`, `*`, `^` (non-negative exponent)"]
rational = ["`1//1`", "true", "false", "false", "false", "false", "`+`, `-`, `*`, `/` (non zero denominator),`^` (integer power)"]
float = [q"1.0", "not usually", "true", "true", "true", "true", "`+`, `-`, `*`, `/` (possibly `NaN`, `Inf`),`^` (non-negative base)"]
d = DataFrame(Attributes=attributes, Integer=integer, Rational=rational, FloatingPoint=float)
table(d)
```
### Complex numbers
Complex numbers in `Julia` are stored as two numbers, a real and imaginary part, each some type of `Real` number. The special constant `im` is used to represent $i=\sqrt{-1}$. This makes the construction of complex numbers fairly standard:
```julia;
1 + 2im, 3 + 4.0im
```
(These two aren't exactly the same, the `3` is promoted from an integer to a float to match the `4.0`. Each of the components must be of the same type of number.)
Mathematically, complex numbers are needed so that certain equations can be satisfied. For example $x^2 = -2$ has solutions $-\sqrt{2}i$ and $\sqrt{2}i$ over the complex numbers. Finding this in `Julia` requires some attention, as we have both `sqrt(-2)` and `sqrt(-2.0)` throwing a `DomainError`, as the `sqrt` function expects non-negative real arguments. However first creating a complex number does work:
```julia;
sqrt(-2 + 0im)
```
For complex arguments, the `sqrt` function will return complex values (even if the answer is a real number).
This means, if you wanted to perform the quadratic equation for any real inputs, your computations might involve something like the following:
```julia;
a,b,c = 1,2,3 ## x^2 + 2x + 3
discr = b^2 - 4a*c
(-b + sqrt(discr + 0im))/(2a), (-b - sqrt(discr + 0im))/(2a)
```
When learning calculus, the only common usage of complex numbers arises when solving polynomial equations for roots, or zeros, though they are very important for subsequent work using the concepts of calculus.
```julia;echo=false
note("""
Though complex numbers are stored as pairs of numbers, the imaginary unit, `im`, is of type `Complex{Bool}`, a type that can be promoted to more specific types when `im` is used with different number types.
""")
```
## Type stability
One design priority of `Julia` is that it should be fast. How can
`Julia` do this? In a simple model, `Julia` is an interface between
the user and the computer's processor(s). Processors consume a set of
instructions, the user issues a set of commands. `Julia` is in charge
of the translation between the two. Ultimately `Julia` calls a compiler to create
the instructions. A basic premise is the shorter the instructions, the
faster they are to process. Shorter instructions can come about by
being more explicit about what types of values the instructions
concern. Explicitness means, there is no need to reason about what a
value can be. When `Julia` can reason about the type of value involved
without having to reason about the values themselves, it can work with
the compiler to produce shorter lists of instructions.
So knowing the type of the output of a function based only on the type
of the inputs can be a big advantage. In `Julia` this is known as
*type stability*. In the standard `Julia` library, this is a primary
design consideration.
##### Example: closure
To motivate this a bit, we discuss how mathematics can be shaped by a
desire to stick to simple ideas. A desirable algebraic property of a
set of numbers and an operation is *closure*. That is, if one takes an
operation like `+` and then uses it to add two numbers in a set, will
that result also be in the set? If this is so for any pair of numbers,
then the set is closed with respect to the operation addition.
Lets suppose we start with the *natural numbers*: $1,2, \dots$. Natural, in that we can easily represent small values in terms of fingers.
This set is closed under addition - as a child learns when counting using their fingers. However, if we started with the odd natural numbers, this set would *not* be closed under addition - $3+3=6$.
The natural numbers are not all the numbers we need, as once a desire
for subtraction is included, we find the set isn't closed. There isn't
a $0$, needed as $n-n=0$ and there aren't negative numbers. The set of
integers are needed for closure under addition and subtraction.
The integers are also closed under multiplication, which for integer values can be seen as just regrouping into longer additions.
However, the integers are not closed under division - even if you put
aside the pesky issue of dividing by $0$. For that, the rational
numbers must be introduced. So aside from division by $0$, the rationals are closed under addition, subtraction, multiplication, and division. There is one more fundamental operation though, powers.
Powers are defined for positive integers in a simple enough manner
```math
a^n=a \cdot a \cdot a \cdots a \text{ (n times); } a, n \text{ are integers } n \text{ is positive}.
```
We can define $a^0$ to be $1$, except for the special case of $0^0$,
which is left undefined mathematically (though it is also defined as
`1` within `Julia`). We can extend the above to include negative
values of $a$, but what about negative values of $n$? We can't say the
integers are closed under powers, as the definition consistent with
the rules that $a^{(-n)} = 1/a^n$ requires rational numbers to be
defined.
Well, in the above `a` could be a rational number, is `a^n` closed for
rational numbers? No again. Though it is fine for $n$ as an integer
(save the odd case of $0$, simple definitions like $2^{1/2}$ are not
answered within the rationals. For this, we need to introduce the
*real* numbers. It is mentioned that
[Aristotle](http://tinyurl.com/bpqbkap) hinted at the irrationality of
the square root of $2$. To define terms like $a^{1/n}$ for integer
values $a,n > 0$ a reference to a solution to an equation $x^n-a$ is
used. Such solutions require the irrational numbers to have solutions
in general. Hence the need for the real numbers (well, algebraic
numbers at least, though once the exponent is no longer a rational
number, the full set of real numbers are needed.)
So, save the pesky cases, the real numbers will be closed under
addition, subtraction, multiplication, division, and powers - provided
the base is non-negative.
Finally for that last case, the complex numbers are introduced to give an answer to $\sqrt{-1}$.
----
How does this apply with `Julia`?
The point is, if we restrict our set of inputs, we can get more
precise values for the output of basic operations, but to get more
general inputs we need to have bigger output sets.
A similar thing happens in `Julia`. For addition say, the addition of
two integers of the same type will be an integer of that type. This
speed consideration is not solely for type stability, but also to
avoid checking for overflow.
Another example, the division of two integers will always be a number
of the same type - floating point, as that is the only type that
ensures the answer will always fit within. (The explicit use of
rationals notwithstanding.) So even if two integers are the input and
their answer *could* be an integer, in `Julia` it will be a floating
point number, (cf. `2/1`).
Hopefully this helps explain the subtle issues around powers: in
`Julia` an integer raised to an integer should be an integer, for
speed, though certain cases are special cased, like `2^(-1)`. However
since a real number raised to a real number makes sense always when
the base is non-negative, as long as real numbers are used as outputs,
the expressions `2.0^(-1)` and `2^(-1.0)` are computed and real
numbers (floating points) are returned. For type stability, even
though $2.0^1$ could be an integer, a floating point answer is
returned.
As for negative bases, `Julia` could always return complex numbers,
but in addition to this being slower, it would be irksome to users. So
user's must opt in. Hence `sqrt(-1.0)` will be an error, but the more
explicit - but mathematically equivalent - `sqrt(-1.0 + 0im)` will not
be a domain error, but rather a complex value will be returned.
## Questions
```julia; echo=false
choices = ["Integer", "Rational", "Floating point", "Complex", "None, an error occurs"]
nothing
```
###### Question
The number created by `pi/2` is?
```julia; hold=true; echo=false;
ans = 3
radioq(choices, ans, keep_order=true)
```
###### Question
The number created by `2/2` is?
```julia; hold=true; echo=false;
ans = 3
radioq(choices, ans, keep_order=true)
```
###### Question
The number created by `2//2` is?
```julia; hold=true; echo=false;
ans = 2
radioq(choices, ans, keep_order=true)
```
###### Question
The number created by `1 + 1//2 + 1/3` is?
```julia; hold=true; echo=false;
ans = 3
radioq(choices, ans, keep_order=true)
```
###### Question
The number created by `2^3` is?
```julia; hold=true; echo=false;
ans = 1
radioq(choices, ans, keep_order=true)
```
###### Question
The number created by `sqrt(im)` is?
```julia; hold=true; echo=false;
ans = 4
radioq(choices, ans, keep_order=true)
```
###### Question
The number created by `2^(-1)` is?
```julia; hold=true; echo=false;
ans = 3
radioq(choices, ans, keep_order=true)
```
###### Question
The "number" created by `1/0` is?
```julia; hold=true; echo=false;
ans = 3
radioq(choices, ans, keep_order=true)
```
###### Question
Is `(2 + 6) + 7` equal to `2 + (6 + 7)`?
```julia; hold=true; echo=false;
yesnoq(true)
```
###### Question
Is `(2/10 + 6/10) + 7/10` equal to `2/10 + (6/10 + 7/10)`?
```julia; hold=true; echo=false;
yesnoq(false)
```
###### Question
The following *should* compute `2^(-1)`, which if entered directly will return `0.5`. Does it?
```julia; eval=false
a, b = 2, -1
a^b
```
```julia; hold=true; echo=false;
yesnoq(false)
```
(This shows the special casing that is done when powers use literal numbers.)

968
CwJ/precalc/plotting.jmd Normal file
View File

@@ -0,0 +1,968 @@
# The Graph of a Function
This section will use the following packages:
```julia
using CalculusWithJulia
using Plots
```
```julia; echo=false; results="hidden"
using CalculusWithJulia.WeaveSupport
using Roots
using SymPy
using DataFrames
const frontmatter = (
title = "The Graph of a Function",
description = "Calculus with Julia: The Graph of a Function",
tags = ["CalculusWithJulia", "precalc", "the graph of a function"],
);
nothing
```
----
A scalar, univariate function, such as $f(x) = 1 - x^2/2$, can be thought of in many different ways. For example:
* It can be represented through a rule of what it does to $x$, as above. This is useful for computing numeric values.
* it can be interpreted verbally, as in *square* $x$, take half then *subtract* from
one. This can give clarity to what the function does.
* It can be thought of in terms of its properties: a polynomial, continuous, $U$-shaped, an approximation for $\cos(x)$ near $0$, $\dots$
* it can be visualized graphically. This is useful for seeing the qualitative behavior of a function.
The graph of a univariate function is just a set of points in the
Cartesian plane. These points come from the relation $(x,f(x))$ that
defines the function. Operationally, a sketch of the graph will
consider a handful of such pairs and then the rest of the points will
be imputed.
For example, a typical approach to plot $f(x) = 1 - x^2/2$ would be to choose some values for $x$ and find the corresponding values of $y$. This might be organized in a "T"-table:
```verbatim
x | y
--------
-2 | -1
-1 | 1/2
0 | 1
1 | 1/2
2 | -1
3 | -7/2
```
These pairs would be plotted in a Cartesian plane and then connected with curved lines. A good sketch is aided by knowing ahead of time that this function describes a parabola which is curving downwards.
We note that this sketch would not include *all* the pairs $(x,f(x))$, as their extent is infinite, rather a well chosen collection of points over some finite domain.
## Graphing a function with Julia
`Julia` has several different options for rendering graphs, all in
external packages. We will focus in these notes on the `Plots`
package, which provides a common interface to several different
plotting backends. (Click through for instructions for plotting with the [Makie](../alternatives/makie_plotting.html) package or the [PlotlyLight](alternatives/plotly_plotting.html) package.)
At the top of this section the accompanying `CalculusWithJulia` package and the `Plots` package were loaded with the `using` command, like this:
```julia; eval=false
using CalculusWithJulia
using Plots
```
```julia;echo=false
note("""
`Plots` is a frontend for one of several backends. `Plots` comes with a backend for web-based graphics (call `plotly()` to specify that); a backend for static graphs (call `gr()` for that). If the `PyPlot` package is installed, calling `pyplot()` will set that as a backend. For terminal usage, if the `UnicodePlots` package is installed, calling `unicodeplots()` will enable that usage. There are still other backends.""")
```
The `plotly` backend is part of the `Plots` package, as is `gr`. Other backends require installation, such as `PyPlot` and `PlotlyJS`.
We use `gr` in these notes, for the most part. (The `plotly` backend is also quite nice for interactive usage, but doesn't work as well with the static HTML pages.)
With `Plots` loaded, it is straightforward to graph a function.
For example, to graph $f(x) = 1 - x^2/2$ over the interval $[-3,3]$ we have:
```julia;
f(x) = 1 - x^2/2
plot(f, -3, 3)
```
The `plot` command does the hard work behind the scenes. It needs ``2`` pieces of information declared:
* **What** to plot. With this invocation, this detail is expressed by passing a function object to `plot`
* **Where** to plot; the `xmin` and `xmax` values. As with a sketch,
it is impossible in this case to render a graph with all possible
$x$ values in the domain of $f$, so we need to pick some viewing
window. In the example this is $[-3,3]$ which is expressed by
passing the two endpoints as the second and third arguments.
Plotting a function is then this simple: `plot(f, xmin, xmax)`.
> *A basic template:* Many operations we meet will take the form
> `action(function, args...)`, as the call to `plot` does. The
> template shifts the focus to the action to be performed. This is a
> [declarative](http://en.wikipedia.org/wiki/Declarative_programming)
> style, where the details to execute the action are only exposed as
> needed.
```julia; echo=false
note("""
The time to first plot can feel sluggish, but subsequent plots will be speedy. See the technical note at the end of this section for an explanation.
""")
```
Let's see some other graphs.
The `sin` function over one period is plotted through:
```julia;
plot(sin, 0, 2pi)
```
We can make a graph of $f(x) = (1+x^2)^{-1}$ over $[-3,3]$ with
```julia;hold=true
f(x) = 1 / (1 + x^2)
plot(f, -3, 3)
```
A graph of $f(x) = e^{-x^2/2}$ over $[-2,2]$ is produced with:
```julia;hold=true
f(x) = exp(-x^2/2)
plot(f, -2, 2)
```
We could skip the first step of defining a function by using an *anonymous function*. For example, to plot $f(x) = \cos(x) - x$ over $[0, \pi/2]$ we could do:
```julia;
plot(x -> cos(x) - x, 0, pi/2)
```
Anonymous functions are especially helpful when parameterized functions are involved:
```julia;hold=true
mxplusb(x; m=1, b=0) = m*x + b
plot(x -> mxplusb(x; m=-1, b=1), -1, 2)
```
Had we parameterized using the `f(x,p)` style, the result would be similar:
```julia
function mxplusb(x, p)
m, b = p.m, p.b
m * x + b
end
plot(x -> mxplusb(x, (m=-1, b=1)), -1, 2)
```
```julia;echo=false
note("""
The function object in the general pattern `action(function, args...)`
is commonly specified in one of three ways: by a name, as with `f`; as an
anonymous function; or as the return value of some other action
through composition.
""")
```
Anonymous functions are also created by `Julia's` `do` notation, which is useful when the first argument to function (like `plot`) accepts a function:
```julia
plot(0, pi/2) do x
cos(x) - x
end
```
The `do` notation can be a bit confusing to read when unfamiliar, though its convenience makes it appealing.
```julia; echo=false
note("""
Some types we will encounter, such as the one for symbolic values or the special polynomial one, have their own `plot` recipes that allow them to be plotted similarly as above, even though they are not functions.
""")
```
----
Making a graph with `Plots` is easy, but producing a graph that is
informative can be a challenge, as the choice of a viewing window can
make a big difference in what is seen. For example, trying to make a
graph of $f(x) = \tan(x)$, as below, will result in a bit of a mess - the
chosen viewing window crosses several places where the function blows up:
```julia;hold=true
f(x) = tan(x)
plot(f, -10, 10)
```
Though this graph shows the asymptote structure and periodicity, it
doesn't give much insight into each period or even into the fact that
the function is periodic.
## The details of graph making
The actual details of making a graph of $f$ over $[a,b]$ are pretty simple and follow the steps in making a "T"-table:
* A set of $x$ values are created between $a$ and $b$.
* A corresponding set of $y$ values are created.
* The pairs $(x,y)$ are plotted as points and connected with straight lines.
The only real difference is that when drawing by hand, we might know
to curve the lines connecting points based on an analysis of the
function. As `Julia` doesn't consider this, the points are connected
with straight lines -- like a dot-to-dot puzzle.
In general, the `x` values are often generated by `range` or the `colon` operator and the `y` values produced by mapping or broadcasting a function over the generated `x` values.
However, the plotting directive `plot(f, xmin, xmax)` calls an
adaptive algorithm to use more points where needed, as judged by
`PlotUtils.adapted_grid(f, (xmin, xmax))`. It computes both the `x`
and `y` values. This algorithm is wrapped up into the `unzip(f, xmin,
xmax)` function from `CalculusWithJulia`. The algorithm adds more
points where the function is more "curvy" and uses fewer points where
it is "straighter." Here we see the linear function is identified as
needing far fewer points than the oscillating function when plotted
over the same range:
```julia
pts_needed(f, xmin, xmax) = length(unzip(f, xmin, xmax)[1])
pts_needed(x -> 10x, 0, 10), pts_needed(x -> sin(10x), 0, 10)
```
(In fact, the `21` is the minimum number of points used for any function; a linear function only needs two.)
##### Example
This demo (which is interactive within a `Pluto` session) shows more points are needed as the function becomes more "curvy." There are the minimum of ``21`` for a straight line, ``37`` for a half period, ``45`` for a full period, etc.
```julia; echo=false
md"""
n = $(@bind 𝐧 Slider(0:20, default=1))
"""
```
```julia; hold=true;
xs,ys = unzip(x -> sin(𝐧*x*pi), 0, 1)
plot(xs, ys, title="n=$(length(xs))")
scatter!(xs, ys)
```
----
For instances where a *specific* set of ``x`` values is desired to be
used, the `range` function or colon operator can be used to create the
$x$ values and broadcasting used to create the $y$ values. For
example, if we were to plot $f(x) = \sin(x)$ over $[0,2\pi]$ using
$10$ points, we might do:
```julia;
𝒙s = range(0, 2pi, length=10)
𝒚s = sin.(𝒙s)
```
Finally, to plot the set of points and connect with lines, the ``x`` and ``y`` values are passed along as vectors:
```julia;
plot(𝒙s, 𝒚s)
```
This plots the points as pairs and then connects them in order using
straight lines. Basically, it creates a dot-to-dot graph. The above
graph looks primitive, as it doesn't utilize enough points.
##### Example: Reflections
The graph of a function may be reflected through a line, as those seen with a mirror. For example, a reflection through the $y$ axis takes a point $(x,y)$ to the point $(-x, y)$. We can easily see this graphically, when we have sets of $x$ and $y$ values through a judiciously placed minus sign.
For example, to plot $\sin(x)$ over $(-\pi,\pi)$ we might do:
```julia;
xs = range(-pi, pi, length=100)
ys = sin.(xs)
plot(xs, ys)
```
To reflect this graph through the $y$ axis, we only need to plot `-xs` and not `xs`:
```julia;
plot(-xs, ys)
```
Looking carefully we see there is a difference. (How?)
There are four very common reflections:
- reflection through the $y$-axis takes $(x,y)$ to $(-x, y)$.
- reflection through the $x$-axis takes $(x,y)$ to $(x, -y)$.
- reflection through the origin takes $(x,y)$ to $(-x, -y)$.
- reflection through the line $y=x$ takes $(x,y)$ to $(y,x)$.
For the $\sin(x)$ graph, we see that reflecting through the $x$ axis
produces the same graph as reflecting through the $y$ axis:
```julia;
plot(xs, -ys)
```
However, reflecting through the origin leaves this graph unchanged:
```julia;
plot(-xs, -ys)
```
> An *even function* is one where reflection through the $y$ axis
> leaves the graph unchanged. That is, $f(-x) = f(x)$. An *odd function*
> is one where a reflection through the origin leaves the
> graph unchanged, or symbolically $f(-x) = -f(x)$.
If we try reflecting the graph of $\sin(x)$ through the line $y=x$, we have:
```julia;
plot(ys, xs)
```
This is the graph of the equation $x = \sin(y)$, but is not the graph of a function as the same $x$ can map to more than one $y$ value. (The new graph does not pass the "vertical line" test.)
However, for the sine function we can get a function from this reflection if we choose a narrower viewing window:
```julia;hold=true
xs = range(-pi/2, pi/2, length=100)
ys = sin.(xs)
plot(ys, xs)
```
The graph is that of the "inverse function" for $\sin(x), x \text{ in } [-\pi/2, \pi/2]$.
#### The `plot(xs, f)` syntax
When plotting a univariate function there are three basic patterns that can be employed. We have examples above of:
* `plot(f, xmin, xmax)` uses an adaptive algorithm to identify values for ``x`` in the interval `[xmin, xmas]`,
* `plot(xs, f.(xs))` to manually choose the values of ``x`` to plot points for, and
Finally there is a merging of these following either of these patterns:
* `plot(f, xs)` *or* `plot(xs, f)`
Both require a manual choice of the values of the ``x``-values to
plot, but the broadcasting is carried out in the `plot` command. This
style is convenient, for example, to down sample the ``x`` range to
see the plotting mechanics, such as:
```julia;
plot(0:pi/4:2pi, sin)
```
#### NaN values
At times it is not desirable to draw lines between each succesive
point. For example, if there is a discontinuity in the function or if
there were a vertical asymptote, such as what happens at $0$ with
$f(x) = 1/x$.
The most straightforward plot is dominated by the vertical asymptote at ``x=0``:
```julia
q(x) = 1/x
plot(q, -1, 1)
```
We can attempt to improve this graph by adjusting the viewport. The
*viewport* of a graph is the $x$-$y$ range of the viewing window. By
default, the $y$-part of the viewport is determined by the range of
the function over the specified interval, $[a,b]$. As just seen, this
approach can produce poor graphs. The `ylims=(ymin, ymax)` argument
can modify what part of the ``y`` axis is shown. (Similarly
`xlims=(xmin, xmax)` will modify the viewport in the ``x`` direction.)
As we see, even with this adjustment, the spurious line connecting the
points with ``x`` values closest to ``0`` is still drawn:
```julia
plot(q, -1, 1, ylims=(-10,10))
```
The dot-to-dot algorithm, at some level, assumes the underlying function is continuous; here ``q(x)=1/x`` is not.
There is a convention for most plotting programs that **if** the $y$
value for a point is `NaN` that no lines will connect to that point,
`(x,NaN)`. `NaN` conveniently appears in many cases where a plot may
have an issue, though not with $1/x$ as `1/0` is `Inf` and not
`NaN`. (Unlike, say, `0/0` which is NaN.)
Here is one way to plot $q(x) = 1/x$ over $[-1,1]$ taking advantage of this convention:
```julia;hold=true
xs = range(-1, 1, length=251)
ys = q.(xs)
ys[xs .== 0.0] .= NaN
plot(xs, ys)
```
By using an odd number of points, we should have that $0.0$ is amongst the `xs`. The next to last line replaces the $y$ value that would be infinite with `NaN`.
As a recommended alternative, we might modify the function so that if it is too large, the values are replaced by `NaN`. Here is one such function consuming a function and returning a modified function put to use to make this graph:
```julia
rangeclamp(f, hi=20, lo=-hi; replacement=NaN) = x -> lo < f(x) < hi ? f(x) : replacement
plot(rangeclamp(x -> 1/x), -1, 1)
```
(The `clamp` function is a base `Julia` function which clamps a number between `lo` and `hi`, returning `lo` or `hi` if `x` is outside that range.)
## Layers
Graphing more than one function over the same viewing window is often
desirable. Though this is easily done in `Plots` by specifying a vector of
functions as the first argument to `plot` instead of a single function
object, we instead focus on building the graph layer by layer.
For example, to see that a polynomial and the cosine function are
"close" near $0$, we can plot *both* $\cos(x)$ and the function $f(x)
= 1 - x^2/2$ over $[-\pi/2,\pi/2]$:
```julia;hold=true
f(x) = 1 - x^2/2
plot(cos, -pi/2, pi/2, label="cos")
plot!(f, -pi/2, pi/2, label="f")
```
Another useful function to add to a plot is one to highlight the $x$
axis. This makes identifying zeros of the function easier. The
anonymous function `x -> 0` will do this. But, perhaps less cryptically,
so will the base function `zero`. For example
```julia;hold=true
f(x) = x^5 - x + 1
plot(f, -1.5, 1.4, label="f")
plot!(zero, label="zero")
```
(The job of `zero` is to return "``0``" in the appropriate type. There is also a similar `one` function in base `Julia`.)
The `plot!` call adds a layer. We could still specify the limits for the plot, though as this can be computed from the figure, to plot `zero` we let `Plots` do it.
For another example, suppose we wish to plot the function $f(x)=x\cdot(x-1)$
over the interval $[-1,2]$ and emphasize with points the fact that $0$
and $1$ are zeros. We can do this with three layers: the first to graph
the function, the second to emphasize the ``x`` axis, the third to graph the points.
```julia;hold=true
f(x) = x*(x-1)
plot(f, -1, 2, legend=false) # turn off legend
plot!(zero)
scatter!([0,1], [0,0])
```
The ``3`` main functions used in these notes for adding layers are:
* `plot!(f, a, b)` to add the graph of the function `f`; also `plot!(xs, ys)`
* `scatter!(xs, ys)` to add points $(x_1, y_1), (x_2, y_2), \dots$.
* `annotate!((x,y, label))` to add a label at $(x,y)$
```julia;echo=false
alert("""
Julia has a convention to use functions named with a `!` suffix to
indicate that they mutate some object. In this case, the object is the
current graph, though it is implicit. Both `plot!`, `scatter!`, and
`annotate!` (others too) do this by adding a layer.
""")
```
## Additional arguments
The `Plots` package provides many arguments for adjusting a graphic, here we mention just a few of the [attributes](https://docs.juliaplots.org/latest/attributes/):
* `plot(..., title="main title", xlab="x axis label", ylab="y axis label")`: add title and label information to a graphic
* `plot(..., color="green")`: this argument can be used to adjust the color of the drawn figure (color can be a string,`"green"`, or a symbol, `:green`, among other specifications)
* `plot(..., linewidth=5)`: this argument can be used to adjust the width of drawn lines
* `plot(..., xlims=(a,b), ylims=(c,d)`: either or both `xlims` and `ylims` can be used to control the viewing window
* `plot(..., linestyle=:dash)`: will change the line style of the plotted lines to dashed lines. Also `:dot`, ...
* `plot(..., aspect_ratio=:equal)`: will keep $x$ and $y$ axis on same scale so that squares look square.
* `plot(..., legend=false)`: by default, different layers will be indicated with a legend, this will turn off this feature
* `plot(..., label="a label")` the `label` attribute will show up when a legend is present. Using an empty string, `""`, will suppress add the layer to the legend.
For plotting points with `scatter`, or `scatter!` the markers can be adjusted via
* `scatter(..., markersize=5)`: increase marker size
* `scatter(..., marker=:square)`: change the marker (uses a symbol, not a string to specify)
Of course, zero, one, or more of these can be used on any given call to `plot`, `plot!`, `scatter` or `scatter!`.
## Graphs of parametric equations
If we have two functions $f(x)$ and $g(x)$ there are a few ways to
investigate their joint behavior. As just mentioned, we can graph
both $f$ and $g$ over the same interval using layers. Such a graph
allows an easy comparison of the shape of the two functions and can be
useful in solving $f(x) = g(x)$. For the latter, the graph of $h(x) =
f(x) - g(x)$ is also of value: solutions to $f(x)=g(x)$ appear as
crossing points on the graphs of `f` and `g`, whereas they appear as zeros
(crossings of the $x$-axis) when `h` is plotted.
A different graph can be made to compare the two functions side-by-side. This is
a parametric plot. Rather than plotting points $(x,f(x))$ and
$(x,g(x))$ with two separate graphs, the graph consists of points
$(f(x), g(x))$. We illustrate with some examples below:
##### Example
The most "famous" parametric graph is one that is likely already familiar, as it follows the parametrization of points on the unit circle by the angle made between the ``x`` axis and the ray from the origin through the point. (If not familiar, this will soon be discussed in these notes.)
```julia;
𝒇(x) = cos(x); 𝒈(x) = sin(x)
𝒕s = range(0, 2pi, length=100)
plot(𝒇.(𝒕s), 𝒈.(𝒕s), aspect_ratio=:equal) # make equal axes
```
Any point $(a,b)$ on this graph is represented by $(\cos(t), \sin(t))$
for some value of $t$, and in fact multiple values of $t$, since $t +
2k\pi$ will produce the same $(a,b)$ value as $t$ will.
Making the parametric plot is similar to creating a plot using lower
level commands. There a sequence of values is generated to
approximate the $x$ values in the graph (`xs`), a set of commands to create
the corresponding function values (e.g., `f.(xs)`), and some
instruction on how to represent the values, in this case with lines
connecting the points (the default for `plot` for two sets of numbers).
In this next plot, the angle values are chosen to be the familiar ones, so the mechanics of the graph can be emphasized. Only the upper half is plotted:
```julia; hold=true; echo=false;
θs =[0, PI/6, PI/4, PI/3, PI/2, 2PI/3, 3PI/4,5PI/6, PI]
DataFrame(θ=θs, x=cos.(θs), y=sin.(θs))
```
```julia;hold=true;
θs =[0, pi/6, pi/4, pi/3, pi/2, 2pi/3, 3pi/4, 5pi/6, pi]
plot(𝒇.(θs), 𝒈.(θs), legend=false, aspect_ratio=:equal)
scatter!(𝒇.(θs), 𝒈.(θs))
```
---
As with the plot of a univariate function, there is a convenience interface for these plots - just pass the two functions in:
```julia;
plot(𝒇, 𝒈, 0, 2pi, aspect_ratio=:equal)
```
##### Example
Looking at growth. Comparing $x^2$ with $x^3$ can run into issues, as the scale gets big:
```julia;
x²(x) = x^2
x³(x) = x^3
plot(x², 0, 25)
plot!(x³, 0, 25)
```
In the above, `x³` is already $25$ times larger on the scale of $[0,25]$
and this only gets worse if the viewing window were to get
larger. However, the parametric graph is quite different:
```julia;
plot(x², x³, 0, 25)
```
In this graph, as $x^3/x^2 = x$, as $x$ gets large, the ratio stays reasonable.
##### Example
Parametric plots are useful to compare the ratio of values near a
point. In the above example, we see how this is helpful for large
`x`. This example shows it is convenient for a fixed `x`, in this case
`x=0`.
Plot $f(x) = x^3$ and $g(x) = x - \sin(x)$ around $x=0$:
```julia;hold=true
f(x) = x^3
g(x) = x - sin(x)
plot(f, g, -pi/2, pi/2)
```
This graph is *nearly* a straight line. At the point $(0,0)=(g(0),
g(0))$, we see that both functions are behaving in a similar manner,
though the slope is not $1$, so they do not increase at exactly the
same rate.
##### Example: Etch A Sketch
[Etch A sketch](http://en.wikipedia.org/wiki/Etch_A_Sketch) is a
drawing toy where two knobs control the motion of a pointer, one
knob controlling the $x$ motion, the other the $y$ motion. The trace
of the movement of the pointer is recorded until the display is
cleared by shaking. Shake to clear is now a motion incorporated by some smart-phone apps.
Playing with the toy makes a few things become clear:
* Twisting just the left knob (the horizontal or $x$ motion) will move
the pointer left or right, leaving a horizontal
line. Parametrically, this would follow the equations $f(t) =
\xi(t)$ for some $\xi$ and $g(t) = c$.
* Twisting just the right knob (the vertical or $y$ motion) will move
the pointer up or down, leaving a vertical line. Parametrically, this would follow the
equations $f(t) = c$ and $g(t) = \psi(t)$ for some $\psi$.
* Drawing a line with a slope different from $0$ or $\infty$ requires
moving both knobs at the same time. A ``45``$^\circ$ line with slope $m=1$
can be made by twisting both at the same rate, say through $f(t) =
ct$, $g(t)=ct$. It doesn't matter how big $c$ is, just that it is
the same for both $f$ and $g$. Creating a different slope is done by
twisting at different rates, say $f(t)=ct$ and $g(t)=dt$. The slope
of the resulting line will be $d/c$.
* Drawing a curve is done by twisting the two knobs with varying rates.
These all apply to parametric plots, as the Etch A Sketch trace is no
more than a plot of $(f(t), g(t))$ over some range of values for $t$,
where $f$ describes the movement in time of the left knob and $g$ the
movement in time of the right.
Now, we revist the last problem in the context of this. We saw in
the last problem that the parametric graph was nearly a line - so
close the eye can't really tell otherwise. That means that the
growth in both $f(t) = t^3$ and $g(t)=t - \sin(t)$ for $t$ around
$0$ are in a nearly fixed ratio, as otherwise the graph would have more
curve in it.
##### Example: Spirograph
Parametric plots can describe a richer set of curves than can plots of
functions. Plots of functions must pass the "vertical-line test", as
there can be at most one $y$ value for a given $x$ value. This is not
so for parametric plots, as the circle example above shows. Plotting sines and cosines this
way is the basis for the once popular
[Spirograph](http://en.wikipedia.org/wiki/Spirograph#Mathematical_basis) toy. The curves
drawn there are parametric plots where the functions come from rolling
a smaller disc either around the outside or inside of a larger disc.
Here is an example using a parameterization provided on the Wikipedia
page where $R$ is the radius of the larger disc, $r$ the radius of the
smaller disc and $\rho < r$ indicating the position of the pencil
within the smaller disc.
```julia;hold=true
R, r, rho = 1, 1/4, 1/4
f(t) = (R-r) * cos(t) + rho * cos((R-r)/r * t)
g(t) = (R-r) * sin(t) - rho * sin((R-r)/r * t)
plot(f, g, 0, max((R-r)/r, r/(R-r))*2pi)
```
In the above, one can fix $R=1$. Then different values for `r` and
`rho` will produce different graphs. These graphs will be periodic if
$(R-r)/r$ is a rational. (Nothing about these equations requires $\rho < r$.)
## Questions
###### Question
Plot the function $f(x) = x^3 - x$. When is the function positive?
```julia; hold=true;echo=false
choices = ["`(-Inf, -1)` and `(0,1)`",
"`(-Inf, -0.577)` and `(0.577, Inf)`",
"`(-1, 0)` and `(1, Inf)`"
];
ans=3;
radioq(choices, ans)
```
###### Question
Plot the function $f(x) = 3x^4 + 8x^3 - 18x^2$. Where (what $x$ value)
is the smallest value? (That is, for which input $x$ is the output
$f(x)$ as small as possible.
```julia; hold=true;echo=false
f(x) = 3x^4 + 8x^3 - 18x^2
val = -3;
numericq(val, 0.25)
```
###### Question
Plot the function $f(x) = 3x^4 + 8x^3 - 18x^2$. When is the function increasing?
```julia; hold=true;echo=false
choices = ["`(-Inf, -3)` and `(0, 1)`",
"`(-3, 0)` and `(1, Inf)`",
"`(-Inf, -4.1)` and `(1.455, Inf)`"
];
ans=2;
radioq(choices, ans)
```
###### Question
Graphing both `f` and the line ``y=0`` helps focus on the *zeros* of `f`. When
`f(x)=log(x)-2`, plot `f` and the line ``y=0``. Identify the lone zero.
```julia; hold=true;echo=false
val = find_zero(x -> log(x) - 2, 8)
numericq(val, .5)
```
###### Question
Plot the function $f(x) = x^3 - x$ over $[-2,2]$. How many zeros are there?
```julia; hold=true;echo=false
val = 3;
numericq(val, 1e-16)
```
###### Question
The function $f(x) = (x^3 - 2x) / (2x^2 -10)$ is a rational function
with issues when $2x^2 = 10$, or $x = -\sqrt{5}$ or $\sqrt{5}$.
Plot this function from $-5$ to $5$. How many times does it cross the $x$ axis?
```julia; hold=true;echo=false
val = 3;
numericq(val, .2)
```
###### Question
A trash collection plan charges a flat rate of 35 dollars a month for
the first 10 bags of trash and is 4 dollars a bag thereafter. Which
function will model this:
```julia; hold=true;echo=false
choices = [
"`f(x) = x <= 35.0 ? 10.0 : 10.0 + 35.0 * (x-4)`",
"`f(x) = x <= 4 ? 35.0 : 35.0 + 10.0 * (x-4)`",
"`f(x) = x <= 10 ? 35.0 : 35.0 + 4.0 * (x-10)`"
]
ans = 3
radioq(choices, ans)
```
Make a plot of the model. Graphically estimate how many bags of trash will cost 55 dollars.
```julia; hold=true;echo=false
ans = 15
numericq(ans, .5)
```
###### Question
Plot the functions $f(x) = \cos(x)$ and $g(x) = x$. Estimate the $x$ value of where the two graphs intersect.
```julia; hold=true;echo=false
val = find_zero(x -> cos(x) -x, .7)
numericq(val, .25)
```
###### Question
The fact that only a finite number of points are used in a graph can
introduce artifacts. An example can appear when plotting
[sinusoidal](http://en.wikipedia.org/wiki/Aliasing#Sampling_sinusoidal_functions)
functions. An example is the graph of `f(x) = sin(500*pi*x)` over `[0,1]`.
Make its graph using 250 evenly spaced points, as follows:
```julia; hold=true;eval=false;results="hidden"
xs = range(0, 1, length=250)
f(x) = sin(500*pi*x)
plot(xs, f.(xs))
```
What is seen?
```julia; hold=true;echo=false
choices = [L"It oscillates wildly, as the period is $T=2\pi/(500 \pi)$ so there are 250 oscillations.",
"It should oscillate evenly, but instead doesn't oscillate very much near 0 and 1",
L"Oddly, it looks exactly like the graph of $f(x) = \sin(2\pi x)$."]
ans = 3
radioq(choices, ans)
```
The algorithm to plot a function works to avoid aliasing issues. Does the graph generated by `plot(f, 0, 1)` look the same, as the one above?
```julia; hold=true;echo=false
choices = ["Yes",
"No, but is still looks pretty bad, as fitting 250 periods into a too small number of pixels is a problem.",
"No, the graph shows clearly all 250 periods."
]
ans = 2
radioq(choices, ans)
```
###### Question
Make this parametric plot for the specific values of the parameters `k` and `l`. What shape best describes it?
```julia; hold=true;eval=false
R, r, rho = 1, 3/4, 1/4
f(t) = (R-r) * cos(t) + rho * cos((R-r)/r * t)
g(t) = (R-r) * sin(t) - rho * sin((R-r)/r * t)
plot(f, g, 0, max((R-r)/r, r/(R-r))*2pi, aspect_ratio=:equal)
```
```julia; hold=true;echo=false
choices = [
"Four sharp points, like a star",
"Four petals, like a flower",
"An ellipse",
"A straight line"
]
ans = 2
radioq(choices, ans)
```
###### Question
For these next questions, we use this function:
```julia;
function spirograph(R, r, rho)
f(t) = (R-r) * cos(t) + rho * cos((R-r)/r * t)
g(t) = (R-r) * sin(t) - rho * sin((R-r)/r * t)
plot(f, g, 0, max((R-r)/r, r/(R-r))*2pi, aspect_ratio=:equal)
end
```
Make this plot for the following specific values of the parameters `R`, `r`, and `rho`. What shape best describes it?
```julia; hold=true;eval=false
R, r, rho = 1, 3/4, 1/4
```
```julia; hold=true;echo=false
choices = [
"Four sharp points, like a star",
"Four petals, like a flower",
"An ellipse",
"A straight line",
"None of the above"
]
ans = 1
radioq(choices, ans, keep_order=true)
```
Make this plot for the following specific values of the parameters `R`, `r`, and `rho`. What shape best describes it?
```julia; hold=true;eval=false
R, r, rho = 1, 1/2, 1/4
```
```julia; hold=true;echo=false
choices = [
"Four sharp points, like a star",
"Four petals, like a flower",
"An ellipse",
"A straight line",
"None of the above"
]
ans = 3
radioq(choices, ans,keep_order=true)
```
Make this plot for the specific values of the parameters `R`, `r`, and `rho`. What shape best describes it?
```julia; hold=true;eval=false
R, r, rho = 1, 1/4, 1
```
```julia; hold=true;echo=false
choices = [
"Four sharp points, like a star",
"Four petals, like a flower",
"A circle",
"A straight line",
"None of the above"
]
ans = 2
radioq(choices, ans, keep_order=true)
```
Make this plot for the specific values of the parameters `R`, `r`, and `rho`. What shape best describes it?
```julia; hold=true;eval=false
R, r, rho = 1, 1/8, 1/4
```
```julia; hold=true;echo=false
choices = [
"Four sharp points, like a star",
"Four petals, like a flower",
"A circle",
"A straight line",
"None of the above"
]
ans = 5
radioq(choices, ans, keep_order=true)
```
----
## Technical note
The slow "time to first plot" in `Julia` is a well-known hiccup that is related to how `Julia` can be so fast. Loading Plots and the making the first plot are both somewhat time consuming, though the second and subsequent plots are speedy. Why?
`Julia` is an interactive language that attains its speed by compiling functions on the fly using the [llvm](llvm.org) compiler. When `Julia` encounters a new combination of a function method and argument types it will compile and cache a function for subsequent speedy execution. The first plot is slow, as there are many internal functions that get compiled. This has sped up of late, as excessive recompilations have been trimmed down, but still has a way to go. This is different from "precompilation" which also helps trim down time for initial executions. There are also some more technically challenging means to create `Julia` images for faster start up that can be pursued if needed.

879
CwJ/precalc/polynomial.jmd Normal file
View File

@@ -0,0 +1,879 @@
# Polynomials
In this section we use the following add-on packages:
```julia
using SymPy
using Plots
```
```julia; echo=false; results="hidden"
using CalculusWithJulia
using CalculusWithJulia.WeaveSupport
const frontmatter = (
title = "Polynomials",
description = "Calculus with Julia: Polynomials",
tags = ["CalculusWithJulia", "precalc", "polynomials"],
);
nothing
```
----
Polynomials are a particular class of expressions that are simple
enough to have many properties that can be analyzed. In particular,
the key concepts of calculus: limits, continuity, derivatives, and
integrals are all relatively trivial for polynomial
functions. However, polynomials are flexible enough that they can be
used to approximate a wide variety of functions. Indeed, though we
don't pursue this, we mention that `Julia`'s `ApproxFun` package
exploits this to great advantage.
Here we discuss some vocabulary and basic facts related to polynomials
and show how the add-on `SymPy` package can be used to model
polynomial expressions within `SymPy`.
`SymPy` provides a Computer Algebra System (CAS) for `Julia`. In this case, by leveraging a mature `Python` package [SymPy](https://www.sympy.org/).
Later we will discuss the `Polynomials` package
for polynomials.
For our purposes, a *monomial* is simply a non-negative integer power
of $x$ (or some other indeterminate symbol) possibly multiplied by a
scalar constant. For example, $5x^4$ is a monomial, as are constants,
such as $-2=-2x^0$ and the symbol itself, as $x = x^1$. In general,
one may consider restrictions on where the constants can come from,
and consider more than one symbol, but we won't pursue this here,
restricting ourselves to the case of a single variable and real
coefficients.
A *polynomial* is a sum of monomials. After
combining terms with same powers, a non-zero polynomial may be written uniquely
as:
```math
a_n x^n + a_{n-1}x^{n-1} + \cdots a_1 x + a_0, \quad a_n \neq 0
```
```julia; hold=true; echo=false; cache=true
##{{{ different_poly_graph }}}
fig_size = (400, 300)
anim = @animate for m in 2:2:10
fn = x -> x^m
plot(fn, -1, 1, size = fig_size, legend=false, title="graph of x^{$m}", xlims=(-1,1), ylims=(-.1,1))
end
imgfile = tempname() * ".gif"
gif(anim, imgfile, fps = 1)
caption = "Polynomials of varying even degrees over ``[-1,1]``."
ImageFile(imgfile, caption)
```
The numbers $a_0, a_1, \dots, a_n$ are the **coefficients** of the
polynomial in the standard basis. With the identifications that $x=x^1$ and $1 = x^0$, the
monomials above have their power match their coefficient's index,
e.g., $a_ix^i$. Outside of the coefficient $a_n$, the other
coefficients may be negative, positive, *or* $0$. Except for the zero
polynomial, the largest power $n$ is called the
[degree](https://en.wikipedia.org/wiki/Degree_of_a_polynomial). The
degree of the [zero](http://tinyurl.com/he6eg6s) polynomial is typically not
defined or defined to be $-1$, so as to make certain statements easier to express. The term
$a_n$ is called the **leading coefficient**. When the leading
coefficient is $1$, the polynomial is called a **monic polynomial**.
The monomial $a_n x^n$ is the **leading term**.
For example, the polynomial $-16x^2 - 32x + 100$ has degree $2$,
leading coefficient $-16$ and leading term $-16x^2$. It is not monic,
as the leading coefficient is not ``1``.
Lower degree polynomials have special names: a degree $0$ polynomial
($a_0$) is a non-zero constant, a degree ``1`` polynomial ($a_0+a_1x$) is called
linear, a degree $2$ polynomial is quadratic, and a degree $3$ polynomial is called cubic.
## Linear polynomials
A special place is reserved for polynomials with degree ``1``. These are
linear, as their graphs are straight lines. The general form,
```math
a_1 x + a_0, \quad a_1 \neq 0,
```
is often written as $mx + b$, which is the **slope-intercept** form. The slope of a line determines how steeply it rises. The value
of $m$ can be found from two points through the well-known formula:
```math
m = \frac{y_1 - y_0}{x_1 - x_0} = \frac{\text{rise}}{\text{run}}
```
```julia; hold=true, echo=false; cache=true
### {{{ lines_m_graph }}}
fig_size = (400, 300)
anim = @animate for m in [-5, -2, -1, 1, 2, 5, 10, 20]
fn = x -> m * x
plot(fn, -1, 1, size = fig_size, legend=false, title="m = $m", xlims=(-1,1), ylims=(-20, 20))
end
imgfile = tempname() * ".gif"
gif(anim, imgfile, fps = 1)
caption = "Graphs of y = mx for different values of m"
ImageFile(imgfile, caption)
```
The intercept, $b$, comes from the fact that when $x=0$ the expression
is $b$. That is the graph of the function $f(x) = mx + b$ will have
$(0,b)$ as a point on it.
More generally, we have the **point-slope** form of a line, written as a polynomial through
```math
y_0 + m \cdot (x - x_0).
```
The slope is $m$ and the point $(x_0, y_0)$. Again, the line graphing
this as a function of $x$ would have the point $(x_0,y_0)$ on it and
have slope $m$. This form is more useful in calculus, as the
information we have convenient is more likely to be related to a
specific value of $x$, not the special value $x=0$.
Thinking in terms of transformations, this looks like the function
$f(x) = x$ (whose graph is a line with slope ``1``) stretched in the $y$
direction by a factor of $m$ then shifted right by $x_0$ units, and
then shifted up by $y_0$ units. When $m>1$, this means the line grows
faster. When $m< 0$, the line $f(x)=x$ is flipped through the
$x$-axis so would head downwards, not upwards like $f(x) = x$.
## Symbolic math in Julia
The indeterminate value `x` (or some other symbol) in a polynomial, is
like a variable in a function and unlike a variable in `Julia`. Variables in `Julia`
are identifiers, just a means to look up a specific, already determined,
value. Rather, the symbol `x` is not yet determined, it is essentially a place holder for a future value. Although we have
seen that `Julia` makes it very easy to work with mathematical
functions, it is not the case that base `Julia` makes working with
expressions of algebraic symbols easy. This makes sense, `Julia` is
primarily designed for technical computing, where numeric approaches
rule the day. However, symbolic math can be used from within `Julia` through add-on packages.
Symbolic math programs include well-known ones like the commercial
programs Mathematica and Maple. Mathematica powers the popular
[WolframAlpha](www.wolframalpha.com) website, which turns "natural"
language into the specifics of a programming language. The open-source
Sage project is an alternative to these two commercial giants. It
includes a wide-range of open-source math projects available within
its umbrella framework. (`Julia` can even be run from within the free
service [cloud.sagemath.com](https://cloud.sagemath.com/projects).) A
more focused project for symbolic math, is the [SymPy](www.sympy.org)
Python library. SymPy is also used within Sage. However, SymPy
provides a self-contained library that can be used standalone within a
Python session. That is great for `Julia` users, as the `PyCall` and
`PythonCall` packages glue `Julia` to Python in a seamless
manner. This allows the `Julia` package `SymPy` to provide
functionality from SymPy within `Julia`.
```julia; echo=false
note("""
When `SymPy` is installed through the package manger, the underlying `Python`
libraries will also be installed.
""")
```
```julia; echo=false
note("""
The [`Symbolics`](../alternatives/symbolics) package is a rapidly
developing `Julia`-only packge that provides symbolic math options.
""")
```
----
To use `SymPy`, we create symbolic objects to be our indeterminate
symbols. The `symbols` function does this. However, we will use the more convenient `@syms` macro front end for `symbols`.
```julia;
@syms a, b, c, x::real, zs[1:10]
```
The above shows that multiple symbols can be defined at once. The
annotation `x::real` instructs `SymPy` to assume the `x` is real, as
otherwise it assumes it is possibly complex. There are many other
[assumptions](http://docs.sympy.org/dev/modules/core.html#module-sympy.core.assumptions)
that can be made. The `@syms` macro documentation lists them. The
`zs[1:10]` tensor notation creates a container with ``10`` different
symbols. The *macro* `@syms` does not need assignment, as the
variable(s) are created behind the scenes by the macro.
```julia;echo=false
note("""Macros in `Julia` are just transformations of the syntax into other syntax. The `@` indicates they behave differently than regular function calls.
""")
```
The `SymPy` package does three basic things:
- It imports some of the functionality provided by `SymPy`, including the ability to create symbolic variables.
- It overloads many `Julia` functions to work seamlessly with symbolic expressions. This makes working with polynomials quite natural.
- It gives access to a wide range of SymPy's functionality through the `sympy` object.
To illustrate, using the just defined `x`, here is how we can create the polynomial $-16x^2 + 100$:
```julia;
𝒑 = -16x^2 + 100
```
That is, the expression is created just as you would create it within
a function body. But here the result is still a symbolic object. We
have assigned this expression to a variable `p`, and have not defined
it as a function `p(x)`. Mentally keeping the distinction between symbolic
expressions and functions is very important.
The `typeof` function shows that `𝒑` is of a symbolic type (`Sym`):
```julia;
typeof(𝒑)
```
We can mix and match symbolic objects. This command creates an
arbitrary quadratic polynomial:
```julia;
quad = a*x^2 + b*x + c
```
Again, this is entered in a manner nearly identical to how we see such
expressions typeset ($ax^2 + bx+c$), though we must remember to
explicitly place the multiplication operator, as the symbols are not
numeric literals.
We can apply many of `Julia`'s mathematical functions and the result will still be symbolic:
```julia;
sin(a*(x - b*pi) + c)
```
Another example, might be the following combination:
```julia;
quad + quad^2 - quad^3
```
One way to create symbolic expressions is simply to call a `Julia` function with symbolic arguments. The first line in the next example defines a function, the second evaluates it at the symbols `x`, `a`, and `b` resulting in a symbolic expression `ex`:
```julia
f(x, m, b) = m*x + b
ex = f(x, a, b)
```
## Substitution: subs, replace
Algebraically working with symbolic expressions is straightforward. A
different symbolic task is substitution. For example, replacing each
instance of `x` in a polynomial, with, say, `(x-1)^2`. Substitution
requires three things to be specified: an expression to work on, a
variable to substitute, and a value to substitute in.
SymPy provides its `subs` function for this. This function is available in `Julia`, but it is easier to use notation reminiscent of function evaluation.
To illustrate, to do
the task above for the polynomial $-16x^2 + 100$ we could have:
```julia;
𝒑(x => (x-1)^2)
```
This "call" notation takes pairs (designated by `a=>b`) where the left-hand side is the variable to substitute for, and the right-hand side the new value.
The value to substitute can depend on the variable, as illustrated; be
a different variable; or be a numeric value, such as $2$:
```julia;
𝒚 = 𝒑(x=>2)
```
The result will always be of a symbolic type, even if the answer is
just a number:
```julia;
typeof(𝒚)
```
If there is just one free variable in an expression, the pair notation can be dropped:
```julia;
𝒑(4) # substitutes x=>4
```
##### Example
Suppose we have the polynomial $p = ax^2 + bx +c$. What would it look
like if we shifted right by $E$ units and up by $F$ units?
```julia;
@syms E F
p₂ = a*x^2 + b*x + c
p₂(x => x-E) + F
```
And expanded this becomes:
```julia;
expand(p₂(x => x-E) + F)
```
### Conversion of symbolic numbers to Julia numbers
In the above, we substituted `2` in for `x` to get `y`:
```julia; hold=true
p = -16x^2 + 100
y = p(2)
```
The value, $36$ is still symbolic, but clearly an integer. If we
are just looking at the output, we can easily translate from the
symbolic value to an integer, as they print similarly. However the
conversion to an integer, or another type of number, does not happen
automatically. If a number is needed to pass along to another `Julia`
function, it may need to be converted. In general, conversions between
different types are handled through various methods of
`convert`. However, with `SymPy`, the `N` function will attempt to do
the conversion for you:
```julia;hold=true
p = -16x^2 + 100
N(p(2))
```
Where `convert(T,x)` requires a specification of the type to convert `x` to, `N` attempts to match the data type used by SymPy to store the number. As such, the output type of `N` may vary (rational, a BigFloat, a float, etc.)
For getting more digits of accuracy, a
precision can be passed to `N`. The following command will take
the symbolic value for $\pi$, `PI`, and produce about ``60`` digits worth
as a `BigFloat` value:
```julia;
N(PI, 60)
```
Conversion by `N` will fail if the value to be converted contains free
symbols, as would be expected.
### Converting symbolic expressions into Julia functions
Evaluating a symbolic expression and returning a numeric value can be done by composing the two just discussed concepts. For example:
```julia;
𝐩 = 200 - 16x^2
N(𝐩(2))
```
This approach is direct, but can be slow *if* many such evaluations were needed (such as with a plot). An alternative is to turn the symbolic expression into a `Julia` function and then evaluate that as usual.
The `lambdify` function turns a symbolic expression into a `Julia` function
```julia;hold=true
pp = lambdify(𝐩)
pp(2)
```
The `lambdify` function uses the name of the similar `SymPy` function which is named after Pythons convention of calling anoynmous function "lambdas." The use above is straightforward. Only slightly more complicated is the use when there are multiple symbolic values. For example:
```julia; hold=true
p = a*x^2 + b
pp = lambdify(p)
pp(1,2,3)
```
This evaluation matches `a` with `1`, `b` with`2`, and `x` with `3` as that is the order returned by the function call `free_symbols(p)`. To adjust that, a second `vars` argument can be given:
```julia; hold=true
pp = lambdify(p, (x,a,b))
pp(1,2,3) # computes 2*1^2 + 3
```
## Graphical properties of polynomials
Consider the graph of the polynomial `x^5 - x + 1`:
```julia;
plot(x^5 - x + 1, -3/2, 3/2)
```
(Plotting symbolic expressions is similar to plotting a function, in
that the expression is passed in as the first argument. The expression
must have only one free variable, as above, or an error will occur.)
This graph illustrates the key features of polynomial graphs:
* there may be values for `x` where the graph crosses the $x$ axis
(real roots of the polynomial);
* there may be peaks and valleys (local maxima and local minima)
* except for constant polynomials, the ultimate behaviour for large
values of $\lvert x\rvert$ is either both sides of the graph going to positive
infinity, or negative infinity, or as in this graph one to the
positive infinity and one to negative infinity. In particular, there
is no *horizontal asymptote*.
To investigate this last point, let's consider the case of the
monomial $x^n$. When $n$ is even, the following animation shows that
larger values of $n$ have greater growth once outside of $[-1,1]$:
```julia; hold=true; echo=false; cache=true
### {{{ poly_growth_graph }}}
fig_size = (400, 300)
anim = @animate for m in 0:2:12
fn = x -> x^m
plot(fn, -1.2, 1.2, size = fig_size, legend=false, xlims=(-1.2, 1.2), ylims=(0, 1.2^12), title="x^{$m} over [-1.2, 1.2]")
end
imgfile = tempname() * ".gif"
gif(anim, imgfile, fps = 1)
caption = L"Demonstration that $x^{10}$ grows faster than $x^8$, ... and $x^2$ grows faster than $x^0$ (which is constant)."
ImageFile(imgfile, caption)
```
Of course, this is expected, as, for example, $2^2 < 2^4 < 2^6 <
\cdots$. The general shape of these terms is similar - $U$ shaped,
and larger powers dominate the smaller powers as $\lvert x\rvert$ gets big.
For odd powers of $n$, the graph of the monomial $x^n$ is no longer
$U$ shaped, but rather constantly increasing. This graph of $x^5$ is
typical:
```julia;
plot(x^5, -2, 2)
```
Again, for larger powers the shape is similar, but the growth is faster.
### Leading term dominates
To see the roots and/or the peaks and valleys of a polynomial requires a
judicious choice of viewing window, as ultimately the leading term
will dominate the graph. The following animation of the graph of
$(x-5)(x-3)(x-2)(x-1)$ illustrates. Subsequent images show a widening
of the plot window until the graph appears U-shaped.
```julia;hold=true; echo=false; cache=true
### {{{ leading_term_graph }}}
fig_size = (400, 300)
anim = @animate for n in 1:6
m = [1, .5, -1, -5, -20, -25]
M = [2, 4, 5, 10, 25, 30]
fn = x -> (x-1)*(x-2)*(x-3)*(x-5)
plt = plot(fn, m[n], M[n], size=fig_size, legend=false, linewidth=2, title ="Graph of on ($(m[n]), $(M[n]))")
if n > 1
plot!(plt, fn, m[n-1], M[n-1], color=:red, linewidth=4)
end
end
caption = "The previous graph is highlighted in red. Ultimately the leading term (\$x^4\$ here) dominates the graph."
imgfile = tempname() * ".gif"
gif(anim, imgfile, fps=1)
ImageFile(imgfile, caption)
```
The leading term in the animation is $x^4$, of even degree, so the graphic is
U-shaped, were the leading term of odd degree the left and right sides would
each head off to different signs of infinity.
To illustrate analytically why the leading term dominates, consider
the polynomial $2x^5 - x + 1$ and then factor out the largest power,
$x^5$, leaving a product:
```math
x^5 \cdot (2 - \frac{1}{x^4} + \frac{1}{x^5}).
```
For large $\lvert x\rvert$, the last two terms in the product on the right get
close to $0$, so this expression is *basically* just $2x^5$ - the
leading term.
----
The following graphic illustrates the ``4`` basic *overall* shapes that can result when plotting a polynomials as ``x`` grows without bound:
```julia; echo=false;
plot(; layout=4)
plot!(x -> x^4, -3,3, legend=false, xticks=false, yticks=false, subplot=1, title="n > even, aₙ > 0")
plot!(x -> x^5, -3,3, legend=false, xticks=false, yticks=false, subplot=2, title="n > odd, aₙ > 0")
plot!(x -> -x^4, -3,3, legend=false, xticks=false, yticks=false, subplot=3, title="n > even, aₙ < 0")
plot!(x -> -x^5, -3,3, legend=false, xticks=false, yticks=false, subplot=4, title="n > odd, aₙ < 0")
```
##### Example
Suppose $p = a_n x^n + \cdots + a_1 x + a_0$ with $a_n > 0$. Then by
the above, eventually for large $x > 0$ we have $p > 0$, as that is the
behaviour of $a_n x^n$. Were $a_n < 0$, then eventually for large
$x>0$, $p < 0$.
Now consider the related polynomial, $q$, where we multiply $p$ by $x^n$ and substitute in $1/x$ for $x$. This is the "reversed" polynomial, as we see in this illustration for $n=2$:
```julia;hold=true
p = a*x^2 + b*x + c
n = 2 # the degree of p
q = expand(x^n * p(x => 1/x))
```
In particular, from the reversal, the behavior of $q$ for large $x$
depends on the sign of $a_0$. As well, due to the $1/x$, the behaviour
of $q$ for large $x>0$ is the same as the behaviour of $p$ for small
*positive* $x$. In particular if $a_n > 0$ but $a_0 < 0$, then `p` is
eventually positive and `q` is eventually negative.
That is, if $p$ has $a_n > 0$ but $a_0 < 0$ then the graph of $p$ must cross the $x$ axis.
This observation is the start of Descartes' rule of
[signs](http://sepwww.stanford.edu/oldsep/stew/descartes.pdf), which
counts the change of signs of the coefficients in `p` to say something
about how many possible crossings there are of the $x$ axis by the
graph of the polynomial $p$.
## Factoring polynomials
Among numerous others, there are two common ways of representing a
non-zero polynomial:
* expanded form, as in $a_n x^n + a_{n-1}x^{n-1} + \cdots a_1 x + a_0, a_n \neq 0$; or
* factored form, as in $a\cdot(x-r_1)\cdot(x-r_2)\cdots(x-r_n), a \neq 0$.
The latter writes $p$ as a product of linear factors, though this is
only possible in general if we consider complex roots. With real roots
only, then the factors are either linear or quadratic, as will be
discussed later.
There are values to each representation. One value of the expanded
form is that polynomial addition and scalar multiplication is much easier than in factored form. For example, adding polynomials just requires matching
up the monomials of similar powers. For the factored form, polynomial multiplication is much easier than expanded form. For the factored form it is easy to read off *roots* of the polynomial (values of $x$ where $p$ is $0$), as
a product is $0$ only if a term is $0$, so any zero must be a zero of
a factor. Factored form has other technical advantages. For example,
the polynomial $(x-1)^{1000}$ can be compactly represented using the
factored form, but would require ``1001`` coefficients to store in expanded
form. (As well, due to floating point differences, the two would
evaluate quite differently as one would require over a ``1000`` operations
to compute, the other just two.)
Translating from factored form to expanded form can be done by
carefully following the distributive law of multiplication. For
example, with some care it can be shown that:
```math
(x-1) \cdot (x-2) \cdot (x-3) = x^3 - 6x^2 +11x - 6.
```
The `SymPy` function `expand` will perform these algebraic
manipulations without fuss:
```julia;
expand((x-1)*(x-2)*(x-3))
```
Factoring a polynomial is several weeks worth of lessons, as there is
no one-size-fits-all algorithm to follow. There are some tricks that
are taught: for example factoring differences of perfect squares,
completing the square, the rational root theorem, $\dots$. But in
general the solution is not automated. The `SymPy` function `factor`
will find all rational factors (terms like $(qx-p)$), but will leave
terms that do not have rational factors alone. For example:
```julia;
factor(x^3 - 6x^2 + 11x -6)
```
Or
```julia;
factor(x^5 - 5x^4 + 8x^3 - 8x^2 + 7x - 3)
```
But will not factor things that are not hard to see:
```julia;
x^2 - 2
```
The factoring $(x-\sqrt{2})\cdot(x + \sqrt{2})$ is not found, as
$\sqrt{2}$ is not rational.
(For those, it may be possible to solve to get the roots, which
can then be used to produce the factored form.)
### Polynomial functions and polynomials.
Our definition of a polynomial is in terms of algebraic expressions
which are easily represented by `SymPy` objects, but not objects from
base `Julia`. (Later we discuss the `Polynomials` package for representing polynomials. There is also the `AbstractAlbegra` package for a more algebraic treatment of polynomials.)
However, *polynomial functions* are easily represented by `Julia`, for
example,
```julia;
f(x) = -16x^2 + 100
```
The distinction is subtle, the expression is turned into a function
just by adding the `f(x) =` preface. But to `Julia` there is a big
distinction. The function form never does any computation until after a value
of $x$ is passed to it. Whereas symbolic expressions can be
manipulated quite freely before any numeric values are specified.
It is easy to create a symbolic expression from a function - just
evaluate the function on a symbolic value:
```julia;
f(x)
```
This is easy - but can also be confusing. The function object is `f`,
the expression is `f(x)` - the function evaluated on a symbolic
object. Moreover, as seen, the symbolic expression can be evaluated
using the same syntax as a function call:
```julia;
p = f(x)
p(2)
```
For many uses, the distinction is unnecessary to make, as the many functions will work with any callable expression. One such is `plot` -- either
`plot(f, a, b)` or `plot(f(x),a, b)` will produce the same plot using the
`Plots` package.
## Questions
###### Question
Let $p$ be the polynomial $3x^2 - 2x + 5$.
What is the degree of $p$?
```julia;hold=true; echo=false
numericq(2)
```
What is the leading coefficient of $p$?
```julia; echo=false
numericq(3)
```
The graph of $p$ would have what $y$-intercept?
```julia; echo=false
numericq(5)
```
Is $p$ a monic polynomial?
```julia; echo=false
booleanq(false, labels=["Yes", "No"])
```
Is $p$ a quadratic polynomial?
```julia; echo=false
booleanq(true, labels=["Yes", "No"])
```
The graph of $p$ would be $U$-shaped?
```julia; echo=false
booleanq(true, labels=["Yes", "No"])
```
What is the leading term of $p$?
```julia; hold=true; echo=false
choices = ["``3``", "``3x^2``", "``-2x``", "``5``"]
ans = 2
radioq(choices, ans)
```
###### Question
Let $p = x^3 - 2x^2 +3x - 4$.
What is $a_2$, using the standard numbering of coefficient?
```julia; echo=false
numericq(-2)
```
What is $a_n$?
```julia; echo=false
numericq(1)
```
What is $a_0$?
```julia; echo=false
numericq(-4)
```
###### Question
The linear polynomial $p = 2x + 3$ is written in which form:
```julia; hold=true; echo=false
choices = ["point-slope form", "slope-intercept form", "general form"]
ans = 2
radioq(choices, ans)
```
###### Question
The polynomial `p` is defined in `Julia` as follows:
```julia; hold=true; eval=false
@syms x
p = -16x^2 + 64
```
What command will return the value of the polynomial when $x=2$?
```julia; hold=true; echo=false
choices = [q"p*2", q"p[2]", q"p_2", q"p(x=>2)"]
ans = 4
radioq(choices, ans)
```
###### Question
In the large, the graph of $p=x^{101} - x + 1$ will
```julia; hold=true; echo=false
choices = [
L"Be $U$-shaped, opening upward",
L"Be $U$-shaped, opening downward",
L"Overall, go upwards from $-\infty$ to $+\infty$",
L"Overall, go downwards from $+\infty$ to $-\infty$"]
ans = 3
radioq(choices, ans, keep_order=true)
```
###### Question
In the large, the graph of $p=x^{102} - x^{101} + x + 1$ will
```julia; hold=true; echo=false
choices = [
L"Be $U$-shaped, opening upward",
L"Be $U$-shaped, opening downward",
L"Overall, go upwards from $-\infty$ to $+\infty$",
L"Overall, go downwards from $+\infty$ to $-\infty$"]
ans = 1
radioq(choices, ans, keep_order=true)
```
###### Question
In the large, the graph of $p=-x^{10} + x^9 + x^8 + x^7 + x^6$ will
```julia; hold=true; echo=false
choices = [
L"Be $U$-shaped, opening upward",
L"Be $U$-shaped, opening downward",
L"Overall, go upwards from $-\infty$ to $+\infty$",
L"Overall, go downwards from $+\infty$ to $-\infty$"]
ans = 2
radioq(choices, ans, keep_order=true)
```
###### Question
Use `SymPy` to factor the polynomial $x^{11} - x$. How many factors are found?
```julia;hold=true; echo=false
@syms x
ex = x^11 - x
nf = length(factor(ex).args)
numericq(nf)
```
###### Question
Use `SymPy` to factor the polynomial $x^{12} - 1$. How many factors are found?
```julia;hold=true; echo=false
@syms x
ex = x^12 - 1
nf = length(factor(ex).args)
numericq(nf)
```
###### Question
What is the monic polynomial with roots $x=-1$, $x=0$, and $x=2$?
```julia; hold=true; echo=false
choices = [q"x^3 - 3x^2 + 2x",
q"x^3 - x^2 - 2x",
q"x^3 + x^2 - 2x",
q"x^3 + x^2 + 2x"]
ans = 2
radioq(choices, 2)
```
###### Question
Use `expand` to expand the expression `((x-h)^3 - x^3) / h` where `x` and `h` are symbolic constants. What is the value:
```julia; hold=true; echo=false
choices = [
q"-h^2 + 3hx - 3x^2",
q"h^3 + 3h^2x + 3hx^2 + x^3 -x^3/h",
q"x^3 - x^3/h",
q"0"]
ans = 1
radioq(choices, ans)
```

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,520 @@
# The Polynomials package
This section will use the following add-on packages:
```julia
import CalculusWithJulia
using Plots
using Polynomials
using RealPolynomialRoots
import SymPy # imported only: some functions, e.g. degree, need qualification
```
```julia; echo=false; results="hidden"
using CalculusWithJulia.WeaveSupport
const frontmatter = (
title = "The Polynomials package",
description = "Calculus with Julia: The Polynomials package",
tags = ["CalculusWithJulia", "precalc", "the polynomials package"],
);
nothing
```
----
While `SymPy` can be used to represent polynomials, there are also
native `Julia` packages available for this and related tasks. These
packages include `Polynomials`, `MultivariatePolynomials`, and
`AbstractAlgebra`, among many others. (A search on
[juliahub.com](juliahub.com) found over ``50`` packages matching
"polynomial".) We will look at the `Polynomials` package in the
following, as it is straightforward to use and provides the features
we are looking at for univariate polynomials.
## Construction
The polynomial expression ``p = a_0 + a_1\cdot x + a_2\cdot x^2 +
\cdots + a_n\cdot x^n`` can be viewed mathematically as a vector of
numbers with respect to some "basis", which for standard polynomials,
as above, is just the set of monomials, ``1, x, x^2, \dots,
x^n``. With this viewpoint, the polynomial ``p`` can be identified
with the vector `[a0, a1, a2, ..., an]`. The `Polynomials` package
provides a wrapper for such an identification through the `Polynomial`
constructor. We have previously loaded this add-on package.
To illustrate, the polynomial ``p = 3 + 4x + 5x^2`` is constructed with
```julia;
p = Polynomial([3,4,5])
```
where the vector `[3,4,5]` represents the coefficients. The polynomial ``q = 3 + 5x^2 + 7x^4`` has some coefficients that are ``0``, these too must be indicated on construction, so we would have:
```julia;
q = Polynomial([3,0,5,0,7])
```
The `coeffs` function undoes `Polynomial`, returning the coefficients from a `Polynomial` object.
```julia;
coeffs(q)
```
Once defined, the usual arithmetic operations for polynomials follow:
```julia;
p + q
```
```julia;
p*q + p^2
```
A polynomial has several familiar methods, such as `degree`:
```julia;
degree(p), degree(q)
```
The zero polynomial has degree `-1`, by convention.
Polynomials may be evaluated using function notation, that is:
```julia;
p(1)
```
This blurs the distinction between a polynomial expression -- a formal object consisting of an indeterminate, coefficients, and the operations of addition, subtraction, multiplication, and non-negative integer powers -- and a polynomial function.
The polynomial variable, in this case `1x`, can be returned by `variable`:
```julia;
x = variable(p)
```
This variable is a `Polynomial` object, so can be manipulated as a polynomial; we can then construct polynomials through expressions like:
```julia;
r = (x-2)^3 * (x-1) * (x+1)
```
The product is expanded for storage by `Polynomials`, which may not be desirable for some uses.
A new variable can produced by calling `variable()`; so we could have constructed `p` by:
```julia; hold=true;
x = variable()
3 + 4x + 5x^2
```
A polynomial in factored form, as `r` above is, can be constructed from its roots. Above, `r` has roots ``2`` (twice), ``1``, and ``-1``. Passing these as a vector to `fromroots` re-produces `r`:
```julia;
fromroots([2,2,1,-1])
```
The `fromroots` function is basically the [factor thereom](https://en.wikipedia.org/wiki/Factor_theorem) which links the factored form of the polynomial with the roots of the polynomial: ``(x-k)`` is a factor of ``p`` if and only if ``k`` is a root of ``p``. By combining a factor of the type ``(x-k)`` for each specified root, the polynomial can be constructed by multiplying its factors. For example, using `prod` and a generarator, we would have:
```julia; hold=true;
x = variable()
prod(x - k for k in [2,2,1,-1])
```
The `Polynomials` package has different ways to represent polynomials, and a factored form can also be used. For example, the `fromroots` function constructs polynomials from the specified roots and `FactoredPolynomial` leaves these in a factored form:
```julia
fromroots(FactoredPolynomial, [2, 2, 1, -1])
```
This form is helpful for some operations, for example polynomial multiplication and positive integer exponentiation, but not others such as addition of polynomials, where such polynomials must first be converted to the standard basis to add and are then converted back into a factored form.
----
The indeterminate, or polynomial symbol is a related, but different
concept to `variable`. Polynomials are stored as a collection of
coefficients, an implicit basis, *and* a symbol, in the above this symbol is
`:x`. A polynomial's symbol is checked to ensure that polynomials with different
symbols are not algebraically combined, except for the special case of constant
polynomials. The symbol is specified through a second argument on
construction:
```julia;
s = Polynomial([1,2,3], "t")
```
As `r` uses "`x`", and `s` a "`t`" the two can not be added, say:
```julia;
r + s
```
## Graphs
Polynomial objects have a plot recipe defined -- plotting from the `Plots` package should be as easy as calling `plot`:
```julia;
plot(r, legend=false) # suppress the legend
```
The choice of domain is heuristically identified; it and can be manually adjusted, as with:
```julia;
plot(r, 1.5, 2.5, legend=false)
```
## Roots
The default `plot` recipe checks to ensure the real roots of the polynomial are included in the domain of the plot. To do this, it must identify the roots. This is done *numerically* by the `roots` function, as in this example:
```julia; hold=true;
x = variable()
p = x^5 - x - 1
roots(p)
```
A consequence of the fundamental theorem of algebra and the factor
theorem is that any fifth degree polynomial with integer coefficients
has ``5`` roots, where possibly some are complex. For real coefficients,
these complex values must come in conjugate pairs, which can be
observed from the output. The lone real root is approximately
`1.1673039782614187`. This value being a numeric approximation to the
irrational root.
```julia;echo=false
note("""
`SymPy` also has a `roots` function. If both `Polynomials` and `SymPy` are used together, calling `roots` must be qualified, as with `Polynomials.roots(...)`. Similarly, `degree` is provided in both, so it too must be qualified.
""")
```
The `roots` function numerically identifies roots. As such, it is susceptible to floating point issues. For example, the following polynomial has one root with multiplicity ``5``, but ``5`` distinct roots are numerically identified:
```julia; hold=true;
x = variable()
p = (x-1)^5
roots(p)
```
The `Polynomials` package has the `multroot` function to identify roots of polynomials when there are multiplicities expected. This function is not exported, so is called through:
```julia; hold=true;
x = variable()
p = (x-1)^5
Polynomials.Multroot.multroot(p)
```
Floating point error can also prevent the finding of real roots. For example, this polynomial has ``3`` real roots, but `roots` finds but ``1``, as the two nearby ones are identified as complex:
```julia; hold=true;
x = variable()
p = -1 + 254x - 16129x^2 + x^9
roots(p)
```
The `RealPolynomialRoots` package, loaded at the top of this section, can assist in the case of identifying real roots of square-free polynomials (no multiple roots). For example:
```julia; hold=true;
ps = coeffs(-1 + 254x - 16129x^2 + x^9)
st = ANewDsc(ps)
refine_roots(st)
```
## Fitting a polynomial to data
The fact that two distinct points determine a line is well known. Deriving the line is easy. Say we have two points ``(x_0, y_0)`` and ``(x_1, y_1)``. The *slope* is then
```math
m = \frac{y_1 - y_0}{x_1 - x_0}, \quad x_1 \neq x_0
```
The line is then given from the *point-slope* form by, say, ``y= y_0 + m\cdot (x-x_0)``. This all assumes, ``x_1 \neq x_0``, as were that the case the slope would be infinite (though the vertical line ``x=x_0`` would still be determined).
A line, ``y=mx+b`` can be a linear polynomial or a constant depending on ``m``, so we could say ``2`` points determine a polynomial of degree ``1`` or less. Similarly, ``3`` distinct points determine a degree ``2`` polynomial or less, ``\dots``, ``n+1`` distinct points determine a degree ``n`` or less polynomial. Finding a polynomial, ``p`` that goes through ``n+1`` points (i.e., ``p(x_i)=y_i`` for each ``i``) is called [polynomial interpolation](https://en.wikipedia.org/wiki/Polynomial_interpolation). The main theorem is:
> *Polynomial interpolation theorem*: There exists a unique polynomial of degree ``n`` or less that interpolates the points ``(x_0,y_0), (x_1,y_1), \dots, (x_n, y_n)`` when the ``x_i`` are distinct.
(Uniqueness follows as suppose ``p`` and ``q`` satisfy the above, then ``(p-q)(x) = 0`` at each of the ``x_i`` and is of degree ``n`` or less, so must be the ``0`` polynomial. Existence comes by construction. See the Lagrange basis in the questions.)
Knowing we can succeed, we approach the problem of ``3`` points, say ``(x_0, y_0)``, ``(x_1,y_1)``, and ``(x_2, y_2)``. There is a polynomial ``p = a\cdot x^2 + b\cdot x + c`` with ``p(x_i) = y_i``. This gives ``3`` equations for the ``3`` unknown values ``a``, ``b``, and ``c``:
```math
\begin{align*}
a\cdot x_0^2 + b\cdot x_0 + c &= y_0\\
a\cdot x_1^2 + b\cdot x_1 + c &= y_1\\
a\cdot x_2^2 + b\cdot x_2 + c &= y_2\\
\end{align*}
```
Solving this with `SymPy` is tractable. A comprehension is used below to create the ``3`` equations; the `zip` function is a simple means to iterate over ``2`` or more iterables simultaneously:
```julia
SymPy.@syms a b c xs[0:2] ys[0:2]
eqs = [a*xi^2 + b*xi + c ~ yi for (xi,yi) in zip(xs, ys)]
abc = SymPy.solve(eqs, [a,b,c])
```
As can be seen, the terms do get quite unwieldy when treated symbolically. Numerically, the `fit` function from the `Polynomials` package will return the interpolating polynomial. To compare,
```julia
fit(Polynomial, [1,2,3], [3,1,2])
```
and we can compare that the two give the same answer with, for example:
```julia
abc[b]((xs .=> [1,2,3])..., (ys .=> [3,1,2])...)
```
(Ignore the tricky way of substituting in each value of `xs` and `ys` for the symbolic values in `x` and `y`.)
##### Example Inverse quadratic interpolation
A related problem, that will arise when finding iterative means to solve for zeros of functions, is *inverse* quadratic interpolation. That is finding ``q`` that goes through the points ``(x_0,y_0), (x_1, y_1), \dots, (x_n, y_n)`` satisfying ``q(y_i) = x_i``. (That is ``x``
and ``y`` are reversed, as with inverse functions.) For the envisioned task, where the inverse quadratic function intersects the ``x`` axis is of interest, which is at the constant term of the polynomial (as it is like the ``y`` intercept of typical polynomial). Let's see what that is in general by replicating the above steps (though now the assumption is the ``y`` values are distinct):
```julia; hold=true;
SymPy.@syms a b c xs[0:2] ys[0:2]
eqs = [a*yi^2 + b*yi + c ~ xi for (xi, yi) in zip(xs,ys)]
abc = SymPy.solve(eqs, [a,b,c])
abc[c]
```
We can graphically see the result for the specific values of `xs` and `ys` as follows:
```julia; hold=true; echo=false
SymPy.@syms a b c xs[0:2] ys[0:2]
eqs = [a*yi^2 + b*yi + c ~ xi for (xi, yi) in zip(xs,ys)]
abc = SymPy.solve(eqs, [a,b,c])
abc[c]
𝒙s, 𝒚s = [1,2,3], [3,1,2]
q = fit(Polynomial, 𝒚s, 𝒙s) # reverse
# plot
us = range(-1/4, 4, length=100)
vs = q.(us)
plot(vs, us, legend=false)
scatter!(𝒙s, 𝒚s)
plot!(zero)
x0 = abc[c]((xs .=> 𝒙s)..., (ys .=> 𝒚s)...)
scatter!([SymPy.N(x0)], [0], markershape=:star)
```
## Questions
###### Question
Do the polynomials ``p = x^4`` and ``q = x^2 - 2`` intersect?
```julia; hold=true; echo=false;
x = variable()
p,q = x^4, x^2 - 2
st = ANewDsc(coeffs(p-q))
yesnoq(length(st) > 0)
```
###### Question
Do the polynomials ``p = x^4-4`` and ``q = x^2 - 2`` intersect?
```julia; hold=true; echo=false;
x = variable()
p,q = x^4-4, x^2 - 2
st = ANewDsc(coeffs(p-q))
yesnoq(length(st) > 0)
```
###### Question
How many real roots does ``p = 1 + x + x^2 + x^3 + x^4 + x^5`` have?
```julia; hold=true; echo=false;
x = variable()
p = 1 + x + x^2 + x^3 + x^4 + x^5
st = (ANewDsc∘coeffs)(p)
numericq(length(st))
```
###### Question
Mathematically we say the ``0`` polynomial has no degree. What convention does `Polynomials` use? (Look at `degree(zero(Polynomial))`.)
```julia; hold=true; echo=false;
choices = ["`nothing`", "`-1`", "`0`", "`Inf`", "`-Inf`"]
ans = 2
radioq(choices, ans, keep_order=true)
```
###### Question
Consider the polynomial ``p(x) = a_1 x - a_3 x^3 + a_5 x^5`` where
```math
\begin{align*}
a_1 &= 4(\frac{3}{\pi} - \frac{9}{16}) \\
a_3 &= 2a_1 -\frac{5}{2}\\
a_5 &= a_1 - \frac{3}{2}.
\end{align*}
```
* Form the polynomial `p` by first computing the ``a``s and forming `p=Polynomial([0,a1,0,-a3,0,a5])`
* Form the polynomial `q` by these commands `x=variable(); q=p(2x/pi)`
The polynomial `q`, a ``5``th-degree polynomial, is a good approximation of for the [sine](http://www.coranac.com/2009/07/sines/) function.
Make graphs of both `q` and `sin`. Over which interval is the approximation (visually) a good one?
```julia; hold=true; echo=false
choices = ["``[0,1]``",
"``[0,\\pi]``",
"``[0,2\\pi]``"]
radioq(choices, 1, keep_order=true)
```
(This [blog post](https://www.nullhardware.com/blog/fixed-point-sine-and-cosine-for-embedded-systems/) shows how this approximation is valuable under some specific circumstances.)
###### Question
The polynomial
```julia
fromroots([1,2,3,3,5])
```
has ``5`` sign changes and ``5`` real roots. For `x = variable()` use `div(p, x-3)` to find the result of dividing ``p`` by ``x-3``. How many sign changes are there in the new polynomial?
```julia; hold=true; echo=false;
numericq(4)
```
###### Question
The identification of a collection of coefficients with a polynomial depends on an understood **basis**. A basis for the polynomials of degree ``n`` or less, consists of a minimal collection of polynomials for which all the polynomials of degree ``n`` or less can be expressed through a combination of sums of terms, each of which is just a coefficient times a basis member. The typical basis is the ``n+`` polynomials ``1`, `x`, `x^2, \dots, x^n``. However, though every basis must have ``n+1`` members, they need not be these.
A basis used by [Lagrange](https://en.wikipedia.org/wiki/Lagrange_polynomial) is the following. Let there be ``n+1`` points distinct points ``x_0, x_1, \dots, x_n``. For each ``i`` in ``0`` to ``n`` define
```math
l_i(x) = \prod_{0 \leq j \leq n; j \ne i} \frac{x-x_j}{x_i - x_j} =
\frac{(x-x_1)\cdot(x-x_2)\cdot \cdots \cdot (x-x_{j-1}) \cdot (x-x_{j+1}) \cdot \cdots \cdot (x-x_n)}{(x_i-x_1)\cdot(x_i-x_2)\cdot \cdots \cdot (x_i-x_{j-1}) \cdot (x_i-x_{j+1}) \cdot \cdots \cdot (x_i-x_n)}.
```
That is ``l_i(x)`` is a product of terms like ``(x-x_j)/(x_i-x_j)`` *except* when ``j=i``.
What is is the value of ``l_0(x_0)``?
```julia; hold=true; echo=false
numericq(1)
```
Why?
```julia; hold=true; echo=false
choices = ["""
All terms like ``(x-x_j)/(x_0 - x_j)`` will be ``1`` when ``x=x_0`` and these are all the terms in the product defining ``l_0``.
""",
"The term ``(x_0-x_0)`` will be ``0``, so the product will be zero"
]
radioq(choices, 1)
```
What is the value of ``l_i(x_i)``?
```julia; hold=true; echo=false
numericq(1)
```
What is the value of ``l_0(x_1)``?
```julia; hold=true; echo=false
numericq(0)
```
Why?
```julia; hold=true; echo=false
choices = ["""
The term like ``(x-x_1)/(x_0 - x_1)`` will be ``0`` when ``x=x_1`` and so the product will be ``0``.
""",
"The term ``(x-x_1)/(x_0-x_1)`` is omitted from the product, so the answer is non-zero."
]
radioq(choices, 1)
```
What is the value of ``l_i(x_j)`` *if* ``i \ne j``?
```julia; hold=true; echo=false
numericq(0)
```
Suppose the ``x_0, x_1, \dots, x_n`` are the ``x`` coordinates of ``n`` distinct points ``(x_0,y_0)``, ``(x_1, y_1), \dots, (x_n,y_n).`` Form the polynomial with the above basis and coefficients being the ``y`` values. That is consider:
```math
p(x) = \sum_{i=0}^n y_i l_i(x) = y_0l_0(x) + y_1l_1(x) + \dots + y_nl_n(x)
```
What is the value of ``p(x_j)``?
```julia; hold=true; echo=false
choices = ["``0``", "``1``", "``y_j``"]
radioq(choices, 3)
```
This last answer is why ``p`` is called an *interpolating* polynomial and this question shows an alternative way to identify interpolating polynomials from solving a system of linear equations.
###### Question
The Chebyshev (``T``) polynomials are polynomials which use a different basis from the standard basis. Denote the basis elements ``T_0``, ``T_1``, ... where we have ``T_0(x) = 1``, ``T_1(x) = x``, and for bigger indices ``T_{i+1}(x) = 2xT_i(x) - T_{i-1}(x)``. The first others are then:
```math
\begin{align*}
T_2(x) &= 2xT_1(x) - T_0(x) = 2x^2 - 1\\
T_3(x) &= 2xT_2(x) - T_1(x) = 2x(2x^2-1) - x = 4x^3 - 3x\\
T_4(x) &= 2xT_3(x) - T_2(x) = 2x(4x^3-3x) - (2x^2-1) = 8x^4 - 8x^2 + 1
\end{align*}
```
With these definitions what is the polynomial associated to the coefficients ``[0,1,2,3]`` with this basis?
```julia; hold=true; echo=false
choices = [
raw"""
It is ``0\cdot 1 + 1 \cdot x + 2 \cdots x^2 + 3\cdot x^3 = x + 2x^2 + 3x^3``
""",
raw"""
It is ``0\cdot T_1(x) + 1\cdot T_1(x) + 2\cdot T_2(x) + 3\cdot T_3(x) = 0``
""",
raw"""
It is ``0\cdot T_1(x) + 1\cdot T_1(x) + 2\cdot T_2(x) + 3\cdot T_3(x) = -2 - 8\cdot x + 4\cdot x^2 + 12\cdot x^3```
"""]
radioq(choices, 3)
```
```julia; hold=true; echo=false
note("""
The `Polynomials` package has an implementation, so you can check your answer through `convert(Polynomial, ChebyshevT([0,1,2,3]))`. Similarly, the `SpecialPolynomials` package has these and many other polynomial bases represented.
The `ApproxFun` package is built on top of polynomials expressed in this basis, as the Chebyshev polynomials have special properties which make them very suitable when approximating functions with polynomials. The `ApproxFun` package uses easier-to-manipulate polynomials to approximate functions very accurately, thereby being useful for investigating properties of non-linear functions leveraging properties for polynomials.
""")
```

665
CwJ/precalc/ranges.jmd Normal file
View File

@@ -0,0 +1,665 @@
# Ranges and Sets
```julia; echo=false; results="hidden"
using CalculusWithJulia
using CalculusWithJulia.WeaveSupport
const frontmatter = (
title = "Ranges and Sets",
description = "Calculus with Julia: Ranges and Sets",
tags = ["CalculusWithJulia", "precalc", "ranges and sets"],
);
nothing
```
## Arithmetic sequences
Sequences of numbers are prevalent in math. A simple one is just counting by ones:
```math
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, \dots
```
Or counting by sevens:
```math
7, 14, 21, 28, 35, 42, 49, \dots
```
More challenging for humans is [counting backwards](http://www.psychpage.com/learning/library/assess/mse.htm) by 7:
```math
100, 93, 86, 79, \dots
```
These are examples of [arithmetic sequences](http://en.wikipedia.org/wiki/Arithmetic_progression). The form of the first $n+1$ terms in such a sequence is:
```math
a_0, a_0 + h, a_0 + 2h, a_0 + 3h, \dots, a_0 + nh
```
The formula for the $a_n$th term can be written in terms of $a_0$, or
any other $0 \leq m \leq n$ with $a_n = a_m + (n-m)\cdot h$.
A typical question might be: The first term of an arithmetic sequence
is equal to ``200`` and the common difference is equal to ``-10``. Find the
value of $a_{20}$. We could find this using $a_n = a_0 + n\cdot h$:
```julia;hold=true
a0, h, n = 200, -10, 20
a0 + n * h
```
More complicated questions involve an unknown first value, as
with: an arithmetic sequence has a common difference equal to ``10`` and
its ``6``th term is equal to ``52``. Find its ``15``th term, $a_{15}$. Here we have to
answer: $a_0 + 15 \cdot 10$. Either we could find $a_0$ (using $52 = a_0 +
6\cdot(10)$) or use the above formula
```julia;hold=true
a6, h, m, n = 52, 10, 6, 15
a15 = a6 + (n-m)*h
```
### The colon operator
Rather than express sequences by the $a_0$, $h$, and $n$, `Julia` uses
the starting point (`a`), the difference (`h`) and a *suggested*
stopping value (`b`). That is, we need three values to specify these
ranges of numbers: a `start`, a `step`, and an `endof`. `Julia` gives
a convenient syntax for this: `a:h:b`. When the difference is just $1$, all
numbers between the start and end are specified by `a:b`, as in
```julia;
1:10
```
But wait, nothing different printed? This is because `1:10` is efficiently
stored. Basically, a recipe to generate the next number from the previous number is created and `1:10` just stores the start and end point and that recipe is used to generate the set of all values. To expand the values, you have to ask for them
to be `collect`ed (though this typically isn't needed in practice):
```julia;
collect(1:10)
```
When a non-default step size is needed, it goes in the middle, as in
`a:h:b`. For example, counting by sevens from ``1`` to ``50`` is achieved by:
```julia;
collect(1:7:50)
```
Or counting down from 100:
```julia;
collect(100:-7:1)
```
In this last example, we said end with ``1``, but it ended with ``2``. The
ending value in the range is a suggestion to go up to, but not exceed. Negative values for `h` are used to make decreasing sequences.
### The range function
For generating points to make graphs, a natural set of points to
specify is $n$ evenly spaced points between $a$ and $b$. We can mimic
creating this set with the range operation by solving for the correct
step size. We have $a_0=a$ and $a_0 + (n-1) \cdot h = b$. (Why $n-1$
and not $n$?) Solving yields $h = (b-a)/(n-1)$. To be concrete we
might ask for ``9`` points between $-1$ and $1$:
```julia;hold=true
a, b, n = -1, 1, 9
h = (b-a)/(n-1)
collect(a:h:b)
```
Pretty neat. If we were doing this many times - such as once per plot - we'd want to encapsulate this into a function, for example:
```julia;
function evenly_spaced(a, b, n)
h = (b-a)/(n-1)
collect(a:h:b)
end
```
Great, let's try it out:
```julia;
evenly_spaced(0, 2pi, 5)
```
Now, our implementation was straightforward, but only because it avoids somethings. Look at something simple:
```julia;
evenly_spaced(1/5, 3/5, 3)
```
It seems to work as expected. But looking just at the algorithm it isn't quite so clear:
```julia;
1/5 + 2*1/5 # last value
```
Floating point roundoff leads to the last value *exceeding* `0.6`, so should it be included? Well, here it is pretty clear it *should* be, but
better to have something programmed that hits both `a` and `b` and adjusts `h` accordingly.
Enter the base function `range` which solves this seemingly
simple - but not really - task. It can use `a`, `b`, and `n`. Like the
range operation, this function returns a generator which can be
collected to realize the values.
The number of points is specified with keyword arguments, as in:
```julia;
xs = range(-1, 1, length=9) # or simply range(-1, 1, 9)
```
and
```julia;
collect(xs)
```
```julia;echo=false
note("""
There is also the `LinRange(a, b, n)` function which can be more performant than `range`, as it doesn't try to correct for floating point errors.
""")
```
## Modifying sequences
Now we concentrate on some more
general styles to modify a sequence to produce a new sequence.
### Filtering
For example, another way to get the values between ``0`` and ``100`` that are
multiples of ``7`` is to start with all ``101`` values and throw out those
that don't match. To check if a number is divisible by $7$, we could
use the `rem` function. It gives the remainder upon division.
Multiples of `7` match `rem(m, 7) == 0`. Checking for divisibility by
seven is unusual enough there is nothing built in for that, but
checking for division by $2$ is common, and for that, there is a
built-in function `iseven`.
The act of throwing out elements of a collection based on some
condition is called *filtering*. The `filter` function does this in
`Julia`; the basic syntax being
`filter(predicate_function, collection)`.
The "`predicate_function`" is one that returns either
`true` or `false`, such as `iseven`. The output of `filter` consists
of the new collection of values - those where the predicate returns `true`.
To see it used, lets start with the numbers between `0` and `25`
(inclusive) and filter out those that are even:
```julia;
filter(iseven, 0:25)
```
To get the numbers between ``1`` and ``100`` that are divisible by $7$
requires us to write a function akin to `iseven`, which isn't hard (e.g.,
`is_seven(x) = x%7 == 0` or if being fancy `Base.Fix2(iszero∘rem, 7)`), but isn't something we continue with just yet.
For another example, here is an inefficient way to list the prime
numbers between ``100`` and ``200``. This uses the `isprime`
function from the `Primes` package
```julia;
using Primes
```
```julia;
filter(isprime, 100:200)
```
Illustrating `filter` at this point is mainly a motivation to
illustrate that we can start with a regular set of numbers and then
modify or filter them. The function takes on more value once we discuss how
to write predicate functions.
### Comprehensions
Let's return to the case of the set of even numbers between ``0`` and ``100``. We have many ways to describe this set:
- The collection of numbers $0, 2, 4, 6 \dots, 100$, or the arithmetic
sequence with step size ``2``, which is returned by `0:2:100`.
- The numbers between ``0`` and ``100`` that are even, that is `filter(iseven, 0:100)`.
- The set of numbers $\{2k: k=0, \dots, 50\}$.
While `Julia` has a special type for dealing with sets, we will use a
vector for such a set. (Unlike a set, vectors can have repeated
values, but as vectors are more widely used, we demonstrate them.)
Vectors are described more fully in a previous section, but as a
reminder, vectors are constructed using square brackets: `[]` (a
special syntax for
[concatenation](http://docs.julialang.org/en/latest/manual/arrays/#concatenation)). Square
brackets are used in different contexts within `Julia`, in this
case we use them to create a *collection*. If we separate single
values in our collection by commas (or semicolons), we will create a
vector:
```julia;
x = [0, 2, 4, 6, 8, 10]
```
That is of course only part of the set of even numbers we want.
Creating more might be tedious were we to type them all out, as above. In such
cases, it is best to *generate* the values.
For this simple case, a range can be used, but more generally a
[comprehension](http://julia.readthedocs.org/en/latest/manual/arrays/#comprehensions)
provides this ability using a construct that closely mirrors a set definition, such as $\{2k:
k=0, \dots, 50\}$. The simplest use of a comprehension takes this
form (as we described in the section on vectors):
`[expr for variable in collection]`
The expression typically involves the variable specified after the keyword
`for`. The collection can be a range, a vector, or many other items
that are *iterable*. Here is how the mathematical set $\{2k: k=0,
\dots, 50\}$ may be generated by a comprehension:
```julia;
[2k for k in 0:50]
```
The expression is `2k`, the variable `k`, and the collection is the range
of values, `0:50`. The syntax is basically identical to how the math
expression is typically read aloud.
For some other examples, here is how we can create the first ``10`` numbers divisible by ``7``:
```julia;
[7k for k in 1:10]
```
Here is how we can square the numbers between ``1`` and ``10``:
```julia;
[x^2 for x in 1:10]
```
To generate other progressions, such as powers of ``2``, we could do:
```julia;
[2^i for i in 1:10]
```
Here are decreasing powers of ``2``:
```julia;
[1/2^i for i in 1:10]
```
Sometimes, the comprehension does not produce the type of output that
may be expected. This is related to `Julia`'s more limited abilities
to infer types at the command line. If the output type is important,
the extra prefix of `T[]` can be used, where `T` is the desired
type. We will see that this will be needed at times with symbolic math.
### Generators
A typical pattern would be to generate a collection of numbers and then apply a function to them. For example, here is one way to sum the powers of ``2``:
```julia;
sum([2^i for i in 1:10])
```
Conceptually this is easy to understand, but computationally it is a
bit inefficient. The generator syntax allows this type of task to be
done more efficiently. To use this syntax, we just need to drop the
`[]`:
```julia;
sum(2^i for i in 1:10)
```
(The difference being no intermediate object is created to store the collection of all values specified by the generator.)
### Filtering generated expressions
Both comprehensions and generators allow for filtering through the keyword `if`. The following shows *one* way to add the prime numbers in $[1,100]$:
```julia;
sum(p for p in 1:100 if isprime(p))
```
The value on the other side of `if` should be an expression that
evaluates to either `true` or `false` for a given `p` (like a
predicate function, but here specified as an expression). The value
returned by `isprime(p)` is such.
In this example, we use the fact that `rem(k, 7)` returns the
remainder found from dividing `k` by `7`, and so is `0` when `k` is a
multiple of `7`:
```julia;
sum(k for k in 1:100 if rem(k,7) == 0) ## add multiples of 7
```
The same `if` can be used in a comprehension. For example, this is an alternative to `filter` for identifying the numbers divisble by `7` in a range of numbers:
```julia;
[k for k in 1:100 if rem(k,7) == 0]
```
#### Example: Making change
This example of Stefan Karpinski comes from a
[blog](http://julialang.org/blog/2016/10/julia-0.5-highlights) post
highlighting changes to the `Julia` language with version
`v"0.5.0"`, which added features to comprehensions that made this example possible.
First, a simple question: using
pennies, nickels, dimes, and quarters how many different ways can we
generate one dollar? Clearly $100$ pennies, or $20$ nickels, or $10$ dimes,
or $4$ quarters will do this, so the answer is at least four, but how
much more than four?
Well, we can use a comprehension to enumerate the
possibilities. This example illustrates how comprehensions and generators can involve one or more variable for the iteration.
First, we either have $0,1,2,3$, or $4$ quarters, or $0$, $25$
cents, $50$ cents, $75$ cents, or a dollar's worth. If we have, say, $1$
quarter, then we need to make up $75$ cents with the rest. If we had $3$
dimes, then we need to make up $45$ cents out of nickels and pennies,
if we then had $6$ nickels, we know we must need $15$ pennies.
The following expression shows how counting this can be done through
enumeration. Here `q` is the amount contributed by quarters, `d` the
amount from dimes, `n` the amount from nickels, and `p` the amount from
pennies. `q` ranges over $0, 25, 50, 75, 100$ or `0:25:100`, etc. If
we know that the sum of quarters, dimes, nickels contributes a certain
amount, then the number of pennies must round things up to $100$.
```julia;
ways = [(q, d, n, p) for q = 0:25:100 for d = 0:10:(100 - q) for n = 0:5:(100 - q - d) for p = (100 - q - d - n)]
length(ways)
```
We see ``242`` cases, each distinct. The first $3$ are:
```julia;
ways[1:3]
```
The generating expression reads naturally. It introduces the use of
multiple `for` statements, each subsequent one depending on the value
of the previous (working left to right). Now suppose, we want to
ensure that the amount in pennies is less than the amount in nickels,
etc. We could use `filter` somehow to do this for our last answer, but
using `if` allows for filtering while the events are generating. Here
our condition is simply expressed: `q > d > n > p`:
```julia;
[(q, d, n, p) for q = 0:25:100
for d = 0:10:(100 - q)
for n = 0:5:(100 - q - d)
for p = (100 - q - d - n)
if q > d > n > p]
```
## Random numbers
We have been discussing structured sets of numbers. On the opposite
end of the spectrum are random numbers. `Julia` makes them easy to
generate, especially random numbers chosen uniformly from $[0,1)$.
- The `rand()` function returns a randomly chosen number in $[0,1)$.
- The `rand(n)` function returns a vector of `n` randomly chosen numbers in $[0,1)$.
To illustrate, this will command return a single number
```julia;
rand()
```
If the command is run again, it is almost certain that a different value will be returned:
```julia;
rand()
```
This call will return a vector of ``10`` such random numbers:
```julia;
rand(10)
```
The `rand` function is easy to use. The only common source of confusion is the subtle distinction between `rand()` and `rand(1)`, as the latter is a vector of ``1`` random number and the former just ``1`` random number.
## Questions
###### Question
Which of these will produce the odd numbers between ``1`` and ``99``?
```julia; hold=true;echo=false;
choices = [
q"1:99",
q"1:3:99",
q"1:2:99"
]
ans = 3
radioq(choices, ans)
```
###### Question
Which of these will create the sequence $2, 9, 16, 23, \dots, 72$?
```julia; hold=true;echo=false;
choices = [q"2:7:72", q"2:9:72", q"2:72", q"72:-7:2"]
ans = 1
radioq(choices, ans)
```
###### Question
How many numbers are in the sequence produced by `0:19:1000`?
```julia; hold=true;echo=false;
val = length(collect(0:19:1000))
numericq(val)
```
###### Question
The range operation (`a:h:b`) can also be used to countdown. Which of these will do so, counting down from `10` to `1`? (You can call `collect` to visualize the generated numbers.)
```julia; hold=true;echo=false;
choices = [
"`10:-1:1`",
"`10:1`",
"`1:-1:10`",
"`1:10`"
]
ans = 1
radioq(choices, ans)
```
###### Question
What is the last number generated by `1:4:7`?
```julia; hold=true;echo=false;
val = (1:4:7)[end]
numericq(val)
```
###### Question
While the range operation can generate vectors by collecting, do the objects themselves act like vectors?
Does scalar multiplication work as expected? In particular, is the result of `2*(1:5)` *basically* the same as `2 * [1,2,3,4,5]`?
```julia; hold=true;echo=false;
yesnoq(true)
```
Does vector addition work? as expected? In particular, is the result of `(1:4) + (2:5)` *basically* the same as `[1,2,3,4]` + `[2,3,4,5]`?
```julia; hold=true;echo=false;
yesnoq(true)
```
What if parenthese are left off? Explain the output of `1:4 + 2:5`?
```julia; hold=true;echo=false;
choices = ["It is just random",
"Addition happens prior to the use of `:` so this is like `1:(4+2):5`",
"It gives the correct answer, a generator for the vector `[3,5,7,9]`"
]
ans = 2
radioq(choices, ans)
```
###### Question
How is `a:b-1` interpreted:
```julia; hold=true;echo=false;
choices = ["as `a:(b-1)`", "as `(a:b) - 1`, which is `(a-1):(b-1)`"]
ans = 1
radioq(choices, ans)
```
###### Question
Create the sequence $10, 100, 1000, \dots, 1,000,000$ using a list comprehension. Which of these works?
```julia; hold=true;echo=false;
choices = [q"[10^i for i in 1:6]", q"[10^i for i in [10, 100, 1000]]", q"[i^10 for i in [1:6]]"]
ans = 1
radioq(choices, ans)
```
###### Question
Create the sequence $0.1, 0.01, 0.001, \dots, 0.0000001$ using a list comprehension. Which of these will work:
```julia; hold=true;echo=false;
choices = [
q"[10^-i for i in 1:7]",
q"[(1/10)^i for i in 1:7]",
q"[i^(1/10) for i in 1:7]"]
ans = 2
radioq(choices, ans)
```
###### Question
Evaluate the expression $x^3 - 2x + 3$ for each of the values $-5, -4, \dots, 4, 5$ using a comprehension. Which of these will work?
```julia; hold=true;echo=false;
choices = [q"[x^3 - 2x + 3 for i in -5:5]", q"[x^3 - 2x + 3 for x in -(5:5)]", q"[x^3 - 2x + 3 for x in -5:5]"]
ans = 3
radioq(choices, ans)
```
###### Question
How many prime numbers are there between $1100$ and $1200$? (Use `filter` and `isprime`)
```julia; hold=true;echo=false;
val = length(filter(isprime, 1100:1200))
numericq(val)
```
###### Question
Which has more prime numbers the range `1000:2000` or the range `11000:12000`?
```julia; hold=true;echo=false;
n1 = length(filter(isprime, 1000:2000))
n2 = length(filter(isprime, 11_000:12_000))
booleanq(n1 > n2, labels=[q"1000:2000", q"11000:12000"])
```
###### Question
We can easily add an arithmetic progression with the `sum`
function. For example, `sum(1:100)` will add the numbers $1, 2, ..., 100$.
What is the sum of the odd numbers between $0$ and $100$?
```julia; hold=true;echo=false;
val = sum(1:2:99)
numericq(val)
```
###### Question
The sum of the arithmetic progression $a, a+h, \dots, a+n\cdot h$ has
a simple formula. Using a few cases, can you tell if this is the
correct one:
```math
(n+1)\cdot a + h \cdot n(n+1)/2
```
```julia; hold=true;echo=false;
booleanq(true, labels=["Yes, this is true", "No, this is false"])
```
###### Question
A *geometric progression* is of the form $a^0, a^1, a^2, \dots,
a^n$. These are easily generated by comprehensions of the form
`[a^i for i in 0:n]`. Find the sum of the geometric progression $1,
2^1, 2^2, \dots, 2^{10}$.
```julia; hold=true;echo=false;
as = [2^i for i in 0:10]
val = sum(as)
numericq(val)
```
Is your answer of the form $(1 - a^{n+1}) / (1-a)$?
```julia; hold=true;echo=false;
yesnoq(true)
```
###### Question
The [product](http://en.wikipedia.org/wiki/Arithmetic_progression) of
the terms in an arithmetic progression has a known formula. The product
can be found by an expression of the form `prod(a:h:b)`. Find the product of the terms in the sequence $1,3,5,\dots,19$.
```julia; hold=true;echo=false;
val = prod(1:2:19)
numericq(val)
```

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,650 @@
# Function manipulations
In this section we will use these add-on packages:
```julia
using CalculusWithJulia
using Plots
```
```julia; echo=false; results="hidden"
using CalculusWithJulia.WeaveSupport
using DataFrames
const frontmatter = (
title = "Function manipulations",
description = "Calculus with Julia: Function manipulations",
tags = ["CalculusWithJulia", "precalc", "function manipulations"],
);
nothing
```
----
Thinking of functions as objects themselves that can be
manipulated - rather than just blackboxes for evaluation - is a major
abstraction of calculus. The main operations to come: the limit *of a
function*, the derivative *of a function*, and the integral *of a
function* all operate on functions. Hence the idea of an
[operator](http://tinyurl.com/n5gp6mf). Here we discuss manipulations of functions from pre-calculus that have proven
to be useful abstractions.
## The algebra of functions
We can talk about the algebra of functions. For example, the sum of functions $f$ and $g$ would be a function whose value at $x$ was just $f(x) + g(x)$. More formally, we would have:
```math
(f + g)(x) = f(x) + g(x),
```
We have given meaning to a new function $f+g$ by defining what is does
to $x$ with the rule on the right hand side. Similarly, we can define
operations for subtraction, multiplication, addition, and
powers.
These mathematical concepts aren't defined for functions in base
`Julia`, though they could be if desired, by a commands such as:
```julia;
import Base: +
f::Function + g::Function = x -> f(x) + g(x)
```
This adds a method to the generic `+` function for functions. The type annotations `::Function` ensure this applies only to functions. To see that it would work, we could do odd-looking things like:
```julia;
ss = sin + sqrt
ss(4)
```
Doing this works, as Julia treats functions as first class objects,
lending itself to
[higher](https://en.wikipedia.org/wiki/Higher-order_programming) order
programming. However, this definition in general is kind of limiting,
as functions in mathematics and Julia can be much more varied than
just the univariate functions we have defined addition for. We won't
pursue this further.
### Composition of functions
As seen, just like with numbers, it can make sense mathematically to define addition,
subtraction, multiplication and division of functions. Unlike numbers though,
we can also define a new operation on functions called **composition**
that involves chaining the output of one function to the input of
another. Composition is a common practice in life, where the result
of some act is fed into another process. For example, making a pie
from scratch involves first making a crust, then composing this with a
filling. A better abstraction might be how we "surf" the web. The
output of one search leads us to another search whose output then is a
composition.
Mathematically, a composition of univariate functions $f$ and $g$ is
written $f \circ g$ and defined by what it does to a value in the
domain of $g$ by:
```math
(f \circ g)(x) = f(g(x)).
```
The output of $g$ becomes the input of $f$.
Composition depends on the order of things. There is no guarantee that
$f \circ g$ should be the same as $g \circ f$. (Putting on socks then
shoes is quite different from putting on shoes then socks.) Mathematically, we can see
this quite clearly with the functions $f(x) = x^2$ and $g(x) =
\sin(x)$. Algebraically we have:
```math
(f \circ g)(x) = \sin(x)^2, \quad (g \circ f)(x) = \sin(x^2).
```
Though they may be *typographically* similar don't be fooled, the following
graph shows that the two functions aren't even close except for $x$
near $0$ (for example, one composition is always non-negative, whereas
the other is not):
```julia;hold=true
f(x) = x^2
g(x) = sin(x)
fg = f ∘ g # typed as f \circ[tab] g
gf = g ∘ f # typed as g \circ[tab] f
plot(fg, -2, 2, label="f∘g")
plot!(gf, label="g∘f")
```
```julia;echo=false
note("""
Unlike how the basic arithmetic operations are treated, `Julia` defines the infix
Unicode operator `\\circ[tab]` to represent composition of functions,
mirroring mathematical notation. This infix operations takes in two functions and returns an anonymous function. It
can be useful and will mirror standard mathematical usage up to issues
with precedence rules.
""")
```
Starting with two functions and composing them requires nothing more
than a solid grasp of knowing the rules of function evaluation. If
$f(x)$ is defined by some rule involving $x$, then $f(g(x))$ just
replaces each $x$ in the rule with a $g(x)$.
So if $f(x) = x^2 + 2x - 1$ and $g(x) = e^x - x$ then $f \circ g$ would be (before any simplification)
```math
(f \circ g)(x) = (e^x - x)^2 + 2(e^x - x) - 1.
```
If can be helpful to think of the argument to ``f`` as a "box" that gets filled in by ``g``:
```math
\begin{align*}
g(x) &=e^x - x\\
f(\square) &= (\square)^2 + 2(\square) - 1\\
f(g(x)) &= (g(x))^2 + 2(g(x)) - 1 = (e^x - x)^2 + 2(e^x - x) - 1.
\end{align*}
```
Here we look at a few compositions:
* The function $h(x) = \sqrt{1 - x^2}$ can be seen as $f\circ g$ with $f(x) = \sqrt{x}$ and $g(x) = 1-x^2$.
* The function $h(x) = \sin(x/3 + x^2)$ can be viewed as $f\circ g$ with $f(x) = \sin(x)$ and $g(x) = x/3 + x^2$.
* The function $h(x) = e^{-1/2 \cdot x^2}$ can be viewed as $f\circ g$ with $f(x) = e^{-x}$ and $g(x) = (1/2) \cdot x^2$.
Decomposing a function into a composition of functions is not unique,
other compositions could have been given above. For example, the last
function is also $f(x) = e^{-x/2}$ composed with $g(x) = x^2$.
```julia;echo=false
note("""
The real value of composition is to break down more complicated things into a sequence of easier steps. This is good mathematics, but also good practice more generally. For example, when we approach a problem with the computer, we generally use a smallish set of functions and piece them together (that is, compose them) to find a solution.
""")
```
### Shifting and scaling graphs
It is very useful to mentally categorize functions within
families. The difference between $f(x) = \cos(x)$ and $g(x) =
12\cos(2(x - \pi/4))$ is not that much - both are cosine functions,
one is just a simple enough transformation of the other. As such, we
expect bounded, oscillatory behaviour with the details of how large
and how fast the oscillations are to depend on the specifics of
the function. Similarly, both these functions $f(x) = 2^x$ and $g(x)=e^x$
behave like exponential growth, the difference being only in the rate
of growth. There are families of functions that are qualitatively
similar, but quantitatively different, linked together by a few basic transformations.
There is a set of operations of functions, which does not really
change the type of function. Rather, it basically moves and stretches
how the functions are graphed. We discuss these four main transformations of $f$:
```julia; echo=false
nms = ["*vertical shifts*","*horizontal shifts*","*stretching*","*scaling*"]
acts = [L"The function $h(x) = k + f(x)$ will have the same graph as $f$ shifted up by $k$ units.",
L"The function $h(x) = f(x - k)$ will have the same graph as $f$ shifted right by $k$ units.",
L"The function $h(x) = kf(x)$ will have the same graph as $f$ stretched by a factor of $k$ in the $y$ direction.",
L"The function $h(x) = f(kx)$ will have the same graph as $f$ compressed horizontally by a factor of $1$ over $k$."]
table(DataFrame(Transformation=nms, Description=acts))
```
The functions $h$ are derived from $f$ in a predictable way. To implement these transformations within `Julia`, we define operators (functions which transform one function into another). As these return functions, the function bodies are anonymous functions. The basic definitions are similar, save for the `x -> ...` part that signals the creation of an anonymous function to return:
```julia;
up(f, k) = x -> f(x) + k
over(f, k) = x -> f(x - k)
stretch(f, k) = x -> k * f(x)
scale(f, k) = x -> f(k * x)
```
To illustrate, let's define a hat-shaped function as follows:
```julia;
𝒇(x) = max(0, 1 - abs(x))
```
A plot over the interval ``[-2,2]`` is shown here:
```julia
plot(𝒇, -2,2)
```
The same graph of $f$ and its image shifted up by ``2`` units would be given by:
```julia;
plot(𝒇, -2, 2, label="f")
plot!(up(𝒇, 2), label="up")
```
A graph of $f$ and its shift over by $2$ units would be given by:
```julia;
plot(𝒇, -2, 4, label="f")
plot!(over(𝒇, 2), label="over")
```
A graph of $f$ and it being stretched by $2$ units would be given by:
```julia;
plot(𝒇, -2, 2, label="f")
plot!(stretch(𝒇, 2), label="stretch")
```
Finally, a graph of $f$ and it being scaled by $2$ would be given by:
```julia;
plot(𝒇, -2, 2, label="f")
plot!(scale(𝒇, 2), label="scale")
```
Scaling by $2$ shrinks the non-zero domain, scaling by $1/2$ would
stretch it. If this is not intuitive, the defintion `x-> f(x/c)` could
have been used, which would have opposite behaviour for scaling.
----
More exciting is what happens if we combine these operations.
A shift right by ``2`` and up by ``1`` is achieved through
```julia;
plot(𝒇, -2, 4, label="f")
plot!(up(over(𝒇,2), 1), label="over and up")
```
Shifting and scaling can be confusing. Here we graph `scale(over(𝒇,2),1/3)`:
```julia;
plot(𝒇, -1,9, label="f")
plot!(scale(over(𝒇,2), 1/3), label="over and scale")
```
This graph is over by $6$ with a width of $3$ on each side of the center. Mathematically, we have $h(x) = f((1/3)\cdot x - 2)$
Compare this to the same operations in opposite order:
```julia;
plot(𝒇, -1, 5, label="f")
plot!(over(scale(𝒇, 1/3), 2), label="scale and over")
```
This graph first scales the symmetric graph, stretching from $-3$ to $3$, then shifts over right by $2$. The resulting function is $f((1/3)\cdot (x-2))$.
As a last example, following up on the last example, a common transformation mathematically is
```math
h(x) = \frac{1}{a}f(\frac{x - b}{a}).
```
We can view this as a composition of "scale" by $1/a$, then "over" by $b$, and finally "stretch" by $1/a$:
```julia;hold=true
a = 2; b = 5
𝒉(x) = stretch(over(scale(𝒇, 1/a), b), 1/a)(x)
plot(𝒇, -1, 8, label="f")
plot!(𝒉, label="h")
```
(This transformation keeps the same amount of area in the triangles, can you tell from the graph?)
##### Example
A model for the length of a day in New York City must take into
account periodic seasonal effects. A simple model might be a sine
curve. However, there would need to be many modifications: Obvious
ones would be that the period would need to be about $365$ days, the
oscillation around ``12`` and the amplitude of the oscillations no more
than ``12``.
We can be more precise. According to
[dateandtime.info](http://dateandtime.info/citysunrisesunset.php?id=5128581)
in ``2015`` the longest day will be June ``21``st when there will be
``15``h ``5``m ``46``s of sunlight, the shortest day will be December ``21``st
when there will be ``9``h ``15``m ``19``s of sunlight. On January
``1``, there will be ``9``h ``19``m ``44``s of sunlight.
A model for a transformed sine curve is
```math
a + b\sin(d(x - c))
```
Where $b$ is related to the amplitude, $c$ the shift and the period is $T=2\pi/d$. We can find some of these easily from the above:
```julia;
a = 12
b = ((15 + 5/60 + 46/60/60) - (9 + 19/60 + 44/60/60)) / 2
d = 2pi/365
```
If we let January ``1`` be $x=0$ then the first day of spring, March ``21``,
is day ``80`` (`Date(2017, 3, 21) - Date(2017, 1, 1) + 1`). This day aligns
with the shift of the sine curve. This shift is ``80``:
```julia;
c = 80
```
Putting this together, we have our graph is "scaled" by $d$, "over" by
$c$, "stretched" by $b$ and "up" by $a$. Here we plot it over slightly more than one year so that we can see that the shortest day of light is in late December ($x \approx -10$ or $x \approx 355$).
```julia;
newyork(t) = up(stretch(over(scale(sin, d), c), b), a)(t)
plot(newyork, -20, 385)
```
To test, if we match up with the model powering
[dateandtime.info](http://dateandtime.info/citysunrisesunset.php?id=5128581)
we note that it predicts "``15``h ``0``m ``4``s" on July ``4``,
``2015``. This is day ``185`` (`Date(2015, 7, 4) - Date(2015, 1, 1) +
1`). Our model prediction has a difference of
```julia;
datetime = 15 + 0/60 + 4/60/60
delta = (newyork(185) - datetime) * 60
```
This is off by a fair amount - almost ``12`` minutes. Clearly a
trigonometric model, based on the assumption of circular motion of the
earth around the sun, is not accurate enough for precise work, but it does help one understand how summer days are longer than winter days and how the length of a day changes fastest at the spring and fall equinoxes.
##### Example: a growth model in fisheries
The von Bertanlaffy growth
[equation](http://www.fao.org/docrep/W5449e/w5449e05.htm) is
$L(t) =L_\infty \cdot (1 - e^{k\cdot(t-t_0)})$. This family of functions can
be viewed as a transformation of the exponential function $f(t)=e^t$.
Part is a scaling and shifting (the $e^{k \cdot (t - t_0)}$)
along with some shifting and stretching. The various parameters have
physical importance which can be measured: $L_\infty$ is a carrying
capacity for the species or organism, and $k$ is a rate of growth. These
parameters may be estimated from data by finding the "closest" curve
to a given data set.
##### Example: the pipeline operator
In the last example, we described our sequence as scale, over,
stretch, and up, but code this in reverse order, as the composition $f
\circ g$ is done from right to left. A more convenient notation would
be to have syntax that allows the composition of $g$ then $f$ to be
written $x \rightarrow g \rightarrow f$. `Julia` provides the
[pipeline](http://julia.readthedocs.org/en/latest/stdlib/base/#Base.|>)
operator for chaining function calls together.
For example, if $g(x) = \sqrt{x}$ and $f(x) =\sin(x)$ we could call $f(g(x))$ through:
```julia; hold=true
g(x) = sqrt(x)
f(x) = sin(x)
pi/2 |> g |> f
```
The output of the preceding expression is passed as the input to the
next. This notation is especially convenient when the enclosing
function is not the main focus. (Some programming languages have more
developed [fluent interfaces](https://en.wikipedia.org/wiki/Fluent_interface) for
chaining function calls. Julia has more powerful chaining macros provided in
packages, such as `DataPipes.jl` or `Chain.jl`.)
### Operators
The functions `up`, `over`, etc. are operators that take a function as
an argument and return a function. The use of operators fits in with
the template `action(f, args...)`. The action is what we are doing,
such as `plot`, `over`, and others to come. The function `f` here is
just an object that we are performing the action on. For example, a
plot takes a function and renders a graph using the additional
arguments to select the domain to view, etc.
Creating operators that return functions involves the use of anonymous
functions, using these operators is relatively straightforward. Two basic patterns are
- Storing the returned function, then calling it:
```julia; eval=false
l(x) = action1(f, args...)(x)
l(10)
```
- Composing two operators:
```julia; eval=false
action2( action1(f, args..), other_args...)
```
Composition like the above is convenient, but can get confusing if more than one composition is involved.
##### Example: two operators
(See [Krill](http://arxiv.org/abs/1403.5821) for background on this example.) Consider two operations on functions. The first takes the *difference* between adjacent points. We call this `D`:
```julia;
D(f::Function) = k -> f(k) - f(k-1)
```
To see that it works, we take a typical function
```julia;
𝐟(k) = 1 + k^2
```
and check:
```julia
D(𝐟)(3), 𝐟(3) - 𝐟(3-1)
```
That the two are the same value is no coincidence. (Again, pause for a
second to make sure you understand why `D(f)(3)` makes sense. If this
is unclear, you could name the function `D(f)` and then call this with
a value of `3`.)
Now we want a function to cumulatively *sum* the values $S(f)(k) =
f(1) + f(2) + \cdots + f(k-1) + f(k)$, as a function of $k$. Adding up
$k$ terms is easy to do with a generator and the function `sum`:
```julia;
S(f) = k -> sum(f(i) for i in 1:k)
```
To check if this works as expected, compare these two values:
```julia;
S(𝐟)(4), 𝐟(1) + 𝐟(2) + 𝐟(3) + 𝐟(4)
```
So one function adds, the other subtracts. Addition and subtraction
are somehow inverse to each other so should "cancel" out. This holds
for these two operations as well, in the following sense: subtracting
after adding leaves the function alone:
```julia;
k = 10 # some arbitrary value k >= 1
D(S(𝐟))(k), 𝐟(k)
```
Any positive integer value of `k` will give the same answer (up to
overflow). This says the difference of the accumulation process is
just the last value to accumulate.
Adding after subtracting also leaves the function alone, save for a vestige of $f(0)$. For example, `k=15`:
```julia;
S(D(𝐟))(15), 𝐟(15) - 𝐟(0)
```
That is the accumulation of differences is just the difference of the end values.
These two operations are discrete versions of the two main operations
of calculus - the derivative and the integral. This relationship will
be known as the "fundamental theorem of calculus."
## Questions
###### Question
If $f(x) = 1/x$ and $g(x) = x-2$, what is $g(f(x))$?
```julia; hold=true;echo=false
choices=["``1/(x-2)``", "``1/x - 2``", "``x - 2``", "``-2``"]
ans = 2
radioq(choices, ans)
```
###### Question
If $f(x) = e^{-x}$ and $g(x) = x^2$ and $h(x) = x-3$, what is $f \circ g \circ h$?
```julia; hold=true;echo=false
choices=["``e^{-x^2 - 3}``", "``(e^x -3)^2``",
"``e^{-(x-3)^2}``", "``e^x+x^2+x-3``"]
ans = 3
radioq(choices, ans)
```
###### Question
If $h(x) = (f \circ g)(x) = \sin^2(x)$ which is a possibility for $f$ and $g$:
```julia; hold=true;echo=false
choices = [raw"``f(x)=x^2; \quad g(x) = \sin^2(x)``",
raw"```f(x)=x^2; \quad g(x) = \sin(x)``",
raw"``f(x)=\sin(x); \quad g(x) = x^2``"]
ans = 2
radioq(choices, ans)
```
###### Question
Which function would have the same graph as the sine curve shifted over by 4 and up by 6?
```julia; hold=true;echo=false
choices = [
raw"``h(x) = 4 + \sin(6x)``",
raw"``h(x) = 6 + \sin(x + 4)``",
raw"``h(x) = 6 + \sin(x-4)``",
raw"``h(x) = 6\sin(x-4)``"]
ans = 3
radioq(choices, 3)
```
###### Question
Let $h(x) = 4x^2$ and $f(x) = x^2$. Which is **not** true:
```julia; hold=true;echo=false
choices = [L"The graph of $h(x)$ is the graph of $f(x)$ stretched by a factor of ``4``",
L"The graph of $h(x)$ is the graph of $f(x)$ scaled by a factor of ``2``",
L"The graph of $h(x)$ is the graph of $f(x) shifted up by ``4`` units"]
ans = 3
radioq(choices, ans)
```
###### Question
The transformation $h(x) = (1/a) \cdot f((x-b)/a)$ can be viewed in one sequence:
```julia; hold=true;echo=false
choices = [L"scaling by $1/a$, then shifting by $b$, then stretching by $1/a$",
L"shifting by $a$, then scaling by $b$, and then scaling by $1/a$",
L"shifting by $a$, then scaling by $a$, and then scaling by $b$" ]
ans=1
radioq(choices, ans)
```
###### Question
This is the graph of a transformed sine curve.
```julia;hold=true;echo=false
f(x) = 2*sin(pi*x)
p = plot(f, -2,2)
```
What is the period of the graph?
```julia; hold=true;echo=false
val = 2
numericq(val)
```
What is the amplitude of the graph?
```julia; hold=true;echo=false
val = 2
numericq(val)
```
What is the form of the function graphed?
```julia; hold=true;echo=false
choices = [
raw"``2 \sin(x)``",
raw"``\sin(2x)``",
raw"``\sin(\pi x)``",
raw"``2 \sin(\pi x)``"
]
ans = 4
radioq(choices, ans)
```
###### Question
Consider this expression
```math
\left(f(1) - f(0)\right) + \left(f(2) - f(1)\right) + \cdots + \left(f(n) - f(n-1)\right) =
-f(0) + f(1) - f(1) + f(2) - f(2) + \cdots + f(n-1) - f(n-1) + f(n) =
f(n) - f(0).
```
Referring to the definitions of `D` and `S` in the example on operators, which relationship does this support:
```julia; hold=true;echo=false
choices = [
q"D(S(f))(n) = f(n)",
q"S(D(f))(n) = f(n) - f(0)"
]
ans = 2
radioq(choices, ans, keep_order=true)
```
###### Question
Consider this expression:
```math
\left(f(1) + f(2) + \cdots + f(n-1) + f(n)\right) - \left(f(1) + f(2) + \cdots + f(n-1)\right) = f(n).
```
Referring to the definitions of `D` and `S` in the example on operators, which relationship does this support:
```julia; hold=true;echo=false
choices = [
q"D(S(f))(n) = f(n)",
q"S(D(f))(n) = f(n) - f(0)"
]
ans = 1
radioq(choices, ans, keep_order=true)
```

View File

@@ -0,0 +1,816 @@
# Trigonometric functions
This section uses the following add-on packages:
```julia
using CalculusWithJulia
using Plots
using SymPy
```
```julia; echo=false; results="hidden"
using CalculusWithJulia.WeaveSupport
const frontmatter = (
title = "Trigonometric functions",
description = "Calculus with Julia: Trigonometric functions",
tags = ["CalculusWithJulia", "precalc", "trigonometric functions"],
);
nothing
```
----
We have informally used some of the trigonometric functions in examples
so far. In this section we quickly review their definitions and some
basic properties.
The trigonometric functions are used to describe relationships between
triangles and circles as well as oscillatory motions. With such a wide range of
utility it is no wonder that they pop up in many places and their
[origins](https://en.wikipedia.org/wiki/Trigonometric_functions#History)
date to Hipparcus and Ptolemy over ``2000`` years ago.
## The 6 basic trigonometric functions
We measure angles in radians, where $360$ degrees is $2\pi$
radians. By proportions, $180$ degrees is $\pi$ radian, $90$ degrees is $\pi/2$ radians, $60$ degrees is $\pi/3$ radians, etc. In general, $x$ degrees is $2\pi \cdot x / 360$ radians (or, with cancellation, ``x \cdot \frac{\pi}{180}``).
For a right triangle with angles $\theta$, $\pi/2 - \theta$, and
$\pi/2$ (``0 < \theta < \pi/2``) we call the side opposite $\theta$ the "opposite" side, the
shorter adjacent side the "adjacent" side and the longer adjacent side
the hypotenuse.
```julia; hide=true; echo=false
p = plot(legend=false, xlim=(-1/4,5), ylim=(-1/2, 3),
xticks=nothing, yticks=nothing, border=:none)
plot!([0,4,4,0],[0,0,3,0], linewidth=3)
del = .25
plot!([4-del, 4-del,4], [0, del, del], color=:black, linewidth=3)
annotate!([(.75, .25, "θ"), (4.0, 1.25, "opposite"), (2, -.25, "adjacent"), (1.5, 1.25, "hypotenuse")])
```
With these, the basic definitions for the primary
trigonometric functions are
```math
\begin{align*}
\sin(\theta) &= \frac{\text{opposite}}{\text{hypotenuse}} &\quad(\text{the sine function})\\
\cos(\theta) &= \frac{\text{adjacent}}{\text{hypotenuse}} &\quad(\text{the cosine function})\\
\tan(\theta) &= \frac{\text{opposite}}{\text{adjacent}}. &\quad(\text{the tangent function})
\end{align*}
```
```julia; echo=false
note("""Many students remember these through [SOH-CAH-TOA](http://mathworld.wolfram.com/SOHCAHTOA.html).""")
```
Some algebra shows that $\tan(\theta) = \sin(\theta)/\cos(\theta)$. There are also ``3`` reciprocal functions, the cosecant, secant and cotangent.
These definitions in terms of sides only apply for $0 \leq \theta \leq \pi/2$. More generally, if we relate any angle taken in the counter clockwise direction for the $x$-axis with a point $(x,y)$ on the *unit* circle, then we can extend these definitions - the point $(x,y)$ is also $(\cos(\theta), \sin(\theta))$.
```julia; hold=true; echo=false; cache=true
## {{{radian_to_trig}}}
fig_size = (400, 300)
function plot_angle(m)
r = m*pi
ts = range(0, stop=2pi, length=100)
tit = "$m ⋅ pi -> ($(round(cos(r), digits=2)), $(round(sin(r), digits=2)))"
p = plot(cos.(ts), sin.(ts), legend=false, aspect_ratio=:equal,title=tit)
plot!(p, [-1,1], [0,0], color=:gray30)
plot!(p, [0,0], [-1,1], color=:gray30)
if r > 0
ts = range(0, stop=r, length=100)
else
ts = range(r, stop=0, length=100)
end
plot!(p, (1/2 .+ abs.(ts)/10pi).* cos.(ts), (1/2 .+ abs.(ts)/10pi) .* sin.(ts), color=:red, linewidth=3)
l = 1 #1/2 + abs(r)/10pi
plot!(p, [0,l*cos(r)], [0,l*sin(r)], color=:green, linewidth=4)
scatter!(p, [cos(r)], [sin(r)], markersize=5)
annotate!(p, [(1/4+cos(r), sin(r), "(x,y)")])
p
end
## different linear graphs
anim = @animate for m in -4//3:1//6:10//3
plot_angle(m)
end
imgfile = tempname() * ".gif"
gif(anim, imgfile, fps = 1)
caption = "An angle in radian measure corresponds to a point on the unit circle, whose coordinates define the sine and cosine of the angle. That is ``(x,y) = (\\cos(\\theta), \\sin(\\theta))``."
ImageFile(imgfile, caption)
```
### The trigonometric functions in Julia
Julia has the ``6`` basic trigonometric functions defined through the functions `sin`, `cos`, `tan`, `csc`, `sec`, and `cot`.
Two right triangles - the one with equal, $\pi/4$, angles; and the
one with angles $\pi/6$ and $\pi/3$ can have the ratio of their sides
computed from basic geometry. In particular, this leads to the following values, which are
usually committed to memory:
```math
\begin{align*}
\sin(0) &= 0, \quad \sin(\pi/6) = \frac{1}{2}, \quad \sin(\pi/4) = \frac{\sqrt{2}}{2}, \quad\sin(\pi/3) = \frac{\sqrt{3}}{2},\text{ and } \sin(\pi/2) = 1\\
\cos(0) &= 1, \quad \cos(\pi/6) = \frac{\sqrt{3}}{2}, \quad \cos(\pi/4) = \frac{\sqrt{2}}{2}, \quad\cos(\pi/3) = \frac{1}{2},\text{ and } \cos(\pi/2) = 0.
\end{align*}
```
Using the circle definition allows these basic values to inform us of
values throughout the unit circle.
These all follow from the definition involving the unit circle:
* If the angle $\theta$ corresponds to a point $(x,y)$ on the unit circle, then the angle $-\theta$ corresponds to $(x, -y)$. So $\sin(\theta) = - \sin(-\theta)$ (an odd function), but $\cos(\theta) = \cos(-\theta)$ (an even function).
* If the angle $\theta$ corresponds to a point $(x,y)$ on the unit circle, then rotating by $\pi$ moves the points to $(-x, -y)$. So $\cos(\theta) = x = - \cos(\theta + \pi)$, and $\sin(\theta) = y = -\sin(\theta + \pi)$.
* If the angle $\theta$ corresponds to a point $(x,y)$ on the unit circle, then rotating by $\pi/2$ moves the points to $(-y, x)$. So $\cos(\theta) = x = \sin(\theta + \pi/2)$.
The fact that $x^2 + y^2 = 1$ for the unit circle leads to the
"Pythagorean identity" for trigonometric functions:
```math
\sin(\theta)^2 + \cos(\theta)^2 = 1.
```
This basic fact can be manipulated many ways. For example, dividing through by $\cos(\theta)^2$ gives the related identity: $\tan(\theta)^2 + 1 = \sec(\theta)^2$.
`Julia`'s functions can compute values for any angles, including these fundamental ones:
```julia;
[cos(theta) for theta in [0, pi/6, pi/4, pi/3, pi/2]]
```
These are floating point approximations, as can be seen clearly in the last value. Symbolic math can be used if exactness matters:
```julia;
cos.([0, PI/6, PI/4, PI/3, PI/2])
```
The `sincos` function computes both `sin` and `cos` simultaneously, which can be more performant when both values are needed.
```juila
sincos(pi/3)
```
```julia; echo=false
note(L"""
For really large values, round off error can play a big role. For example, the *exact* value of $\sin(1000000 \pi)$ is $0$, but the returned value is not quite $0$ `sin(1_000_000 * pi) = -2.231912181360871e-10`. For exact multiples of $\pi$ with large multiples the `sinpi` and `cospi` functions are useful.
(Both functions are computed by first employing periodicity to reduce the problem to a smaller angle. However, for large multiples the floating-point roundoff becomes a problem with the usual functions.)
""")
```
##### Example
Measuring the height of a
[tree](https://lifehacker.com/5875184/is-there-an-easy-way-to-measure-the-height-of-a-tree)
may be a real-world task for some, but a typical task for nearly all trigonometry
students. How might it be done? If a right triangle can be formed
where the angle and adjacent side length are known, then the opposite
side (the height of the tree) can be solved for with the tangent
function. For example, if standing $100$ feet from the base of the
tree the tip makes a ``15`` degree angle the height is given by:
```julia; hold=true;
theta = 15 * pi / 180
adjacent = 100
opposite = adjacent * tan(theta)
```
Having some means to compute an angle and then a tangent of that angle
handy is not a given, so the linked to article provides a few other
methods taking advantage of similar triangles.
You can also measure distance with your
[thumb](http://www.vendian.org/mncharity/dir3/bodyruler_angle/) or
fist. How? The fist takes up about $10$ degrees of view when held
straight out. So, pacing off backwards until the fist completely
occludes the tree will give the distance of the adjacent side of a
right triangle. If that distance is $30$ paces what is the height of the tree?
Well, we need some facts. Suppose your pace is $3$ feet. Then the
adjacent length is $90$ feet. The multiplier is the tangent of $10$
degrees, or:
```julia;
tan(10 * pi/180)
```
Which for sake of memory we will say is $1/6$ (a $5$ percent error). So that answer is *roughly* $15$ feet:
```julia;
30 * 3 / 6
```
Similarly, you can use your thumb instead of your first. To use your first you can multiply by $1/6$ the adjacent side, to use your thumb about $1/30$ as this approximates the tangent of $2$ degrees:
```julia;
1/30, tan(2*pi/180)
```
This could be reversed. If you know the height of something a distance
away that is covered by your thumb or fist, then you would multiply
that height by the appropriate amount to find your distance.
### Basic properties
The sine function is defined for all real $\theta$ and has a range of $[-1,1]$. Clearly as $\theta$ winds around the $x$-axis, the position of the $y$ coordinate begins to repeat itself. We say the sine function is *periodic* with period $2\pi$. A graph will illustrate:
```julia;
plot(sin, 0, 4pi)
```
The graph shows two periods. The wavy aspect of the graph is why this function is used to model periodic motions, such as the amount of sunlight in a day, or the alternating current powering a computer.
From this graph - or considering when the $y$ coordinate is $0$ - we see that the sine function has zeros at any integer multiple of $\pi$, or $k\pi$, $k$ in $\dots,-2,-1, 0, 1, 2, \dots$.
The cosine function is similar, in that it has the same domain and range, but is "out of phase" with the sine curve. A graph of both shows the two are related:
```julia;
plot(sin, 0, 4pi, label="sin")
plot!(cos, 0, 4pi, label="cos")
```
The cosine function is just a shift of the sine function (or vice versa). We see that the zeros of the cosine function happen at points of the form $\pi/2 + k\pi$, $k$ in $\dots,-2,-1, 0, 1, 2, \dots.$
The tangent function does not have all $\theta$ for its domain, rather those points where division by $0$ occurs are excluded. These occur when the cosine is $0$, or, again, at $\pi/2 + k\pi$, $k$ in $\dots,-2,-1, 0, 1, 2, \dots.$ The range of the tangent function will be all real $y$.
The tangent function is also periodic, but not with period $2\pi$, but
rather just $\pi$. A graph will show this. Here we avoid the vertical
asymptotes using `rangeclamp`:
```julia;
plot(rangeclamp(tan), -10, 10, label="tan")
```
##### Example sums of sines
For the function ``f(x) = \sin(x)`` we have an understanding of the related family of functions defined by linear transformations:
```math
g(x) = a + b \sin((2\pi n)x)
```
That is ``g`` is shifted up by ``a`` units, scaled vertically by ``b`` units and has a period of ``1/n``. We see a simple plot here where we can verify the transformation:
```julia
g(x; b=1,n=1) = b*sin(2pi*n*x)
g1(x) = 1 + g(x, b=2, n=3)
plot(g1, 0, 1)
```
We can consider the sum of such functions, for example
```julia
g2(x) = 1 + g(x, b=2, n=3) + g(x, b=4, n=5)
plot(g2, 0, 1)
```
Though still periodic, we can see with this simple example that sums of different sine functions can have somewhat complicated graphs.
Sine functions can be viewed as the `x` position of a point traveling around a circle so `g(x, b=2, n=3)` is the `x` position of point traveling around a circle of radius ``2`` that completes a circuit in ``1/3`` units of time.
The superposition of the two sine functions that `g2` represents could be viewed as the position of a circle moving around a point that is moving around another circle. The following graphic, with ``b_1=1/3, n_1=3, b_2=1/4``, and ``n_2=4``, shows an example that produces the related cosine sum (moving right along the ``x`` axis), the sine sum (moving down along the ``y`` axis, *and* the trace of the position of the point generating these two plots.
```julia; hold=true; echo=false; cache=true
unzip(vs::Vector) = Tuple([[vs[i][j] for i in eachindex(vs)] for j in eachindex(vs[1])])
function makegraph(t, b₁,n₁, b₂=0, n₂=1)
f₁ = x -> b₁*[sin(2pi*n₁*x), cos(2pi*n₁*x)]
f₂ = x -> b₂*[sin(2pi*n₂*x), cos(2pi*n₂*x)]
h = x -> f₁(x) + f₂(x)
ts = range(0, 2pi, length=1000)
ylims = (-b₁-b₂-2, b₁ + b₂)
xlims = (-b₁-b₂, b₁ + b₂ + 2)
p = plot(; xlim=xlims, ylim=ylims,
legend=false,
aspect_ratio=:equal)
α = 0.3
# circle 1
plot!(p, unzip(f₁.(range(0, 2pi/n₁, length=100))), alpha=α)
scatter!(p, unzip([f₁(t)]), markersize=1, alpha=α)
# circle 2
us, vs = unzip(f₂.(range(0, 2pi/n₂, length=100)))
a,b = f₁(t)
plot!(p, a .+ us, b .+ vs, alpha=α)
scatter!(p, unzip([h(t)]), markersize=5)
# graph of (x,y) over [0,t]
ts = range(0, t, length=200)
plot!(p, unzip(h.(ts)), linewidth=1, alpha=0.5, linestyle=:dash)
# graph of x over [0,t]
ys = -ts
xs = unzip(h.(ts))[1]
plot!(p, xs, ys, linewidth=2)
# graph of y over [0,t]
xs = ts
ys = unzip(h.(ts))[2]
plot!(p, xs, ys, linewidth=2)
p
end
# create animoation
b₁=1/3; n₁=3; b₂=1/4; n₂=4
fig_size = (400, 300)
anim = @animate for t ∈ range(0, 2.5, length=50)
makegraph(t, b₁, n₁, b₂, n₂)
end
imgfile = tempname() * ".gif"
gif(anim, imgfile, fps = 5)
caption = "Superposition of sines and cosines represented by an epicycle"
ImageFile(imgfile, caption)
```
As can be seen, even a somewhat simple combination can produce complicated graphs (a fact known to [Ptolemy](https://en.wikipedia.org/wiki/Deferent_and_epicycle)) . How complicated can such a graph get? This won't be answered here, but for fun enjoy this video produced by the same technique using more moving parts from the [`Javis.jl`](https://github.com/Wikunia/Javis.jl/blob/master/examples/fourier.jl) package:
```julia; echo=false;
txt ="""
<iframe width="560" height="315" src="https://www.youtube.com/embed/rrmx2Q3sO1Y" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
"""
tpl = CalculusWithJulia.WeaveSupport.centered_content_tpl
txt = CalculusWithJulia.WeaveSupport.Mustache.render(tpl, content=txt, caption="Julia logo animated")
CalculusWithJulia.WeaveSupport.HTMLoutput(txt)
```
### Functions using degrees
Trigonometric function are functions of angles which have two common descriptions: in terms of degrees or radians. Degrees are common when right triangles are considered, radians much more common in general, as the relationship with arc-length holds in that $r\theta = l$, where $r$ is the radius of a circle and $l$ the length of the arc formed by angle $\theta$.
The two are related, as a circle has both $2\pi$ radians and ``360`` degrees. So to convert from degrees into radians it takes multiplying by $2\pi/360$ and to convert from radians to degrees it takes multiplying by $360/(2\pi)$. The `deg2rad` and `rad2deg` functions are available for this task.
In `Julia`, the functions `sind`, `cosd`, `tand`, `cscd`, `secd`, and `cotd` are available to simplify the task of composing the two operations (that is `sin(deg2rad(x))` is the essentially same as `sind(x)`).
## The sum-and-difference formulas
Consider the point on the unit circle $(x,y) = (\cos(\theta), \sin(\theta))$. In terms of $(x,y)$ (or $\theta$) is there a way to represent the angle found by rotating an additional $\theta$, that is what is $(\cos(2\theta), \sin(2\theta))$?
More generally, suppose we have two angles $\alpha$ and $\beta$, can we
represent the values of $(\cos(\alpha + \beta), \sin(\alpha + \beta))$
using the values just involving $\beta$ and $\alpha$ separately?
According to [Wikipedia](https://en.wikipedia.org/wiki/Trigonometric_functions#Identities) the following figure (from [mathalino.com](http://www.mathalino.com/reviewer/derivation-of-formulas/derivation-of-sum-and-difference-of-two-angles)) has ideas that date to Ptolemy:
```julia; echo=false
ImageFile(:precalc, "figures/summary-sum-and-difference-of-two-angles.jpg", "Relations between angles")
```
To read this, there are three triangles: the bigger (green with pink part) has hypotenuse $1$ (and adjacent and opposite sides that form the hypotenuses of the other two); the next biggest (yellow) hypotenuse $\cos(\beta)$, adjacent side (of angle $\alpha$) $\cos(\beta)\cdot \cos(\alpha)$, and opposite side $\cos(\beta)\cdot\sin(\alpha)$; and the smallest (pink) hypotenuse $\sin(\beta)$, adjacent side (of angle $\alpha$) $\sin(\beta)\cdot \cos(\alpha)$, and opposite side $\sin(\beta)\sin(\alpha)$.
This figure shows the following sum formula for sine and cosine:
```math
\begin{align*}
\sin(\alpha + \beta) &= \sin(\alpha)\cos(\beta) + \cos(\alpha)\sin(\beta), & (\overline{CE} + \overline{DF})\\
\cos(\alpha + \beta) &= \cos(\alpha)\cos(\beta) - \sin(\alpha)\sin(\beta). & (\overline{AC} - \overline{DE})
\end{align*}
```
Using the fact that $\sin$ is an odd function and $\cos$ an even function, related formulas for the difference $\alpha - \beta$ can be derived.
Taking $\alpha = \beta$ we immediately get the "double-angle" formulas:
```math
\begin{align*}
\sin(2\alpha) &= 2\sin(\alpha)\cos(\alpha)\\
\cos(2\alpha) &= \cos(\alpha)^2 - \sin(\alpha)^2.
\end{align*}
```
The latter looks like the Pythagorean identify, but has a minus sign. In fact, the Pythagorean identify is often used to rewrite this, for example $\cos(2\alpha) = 2\cos(\alpha)^2 - 1$ or $1 - 2\sin(\alpha)^2$.
Applying the above with $\alpha = \beta/2$, we get that $\cos(\beta) = 2\cos(\beta/2)^2 -1$, which rearranged yields the "half-angle" formula: $\cos(\beta/2)^2 = (1 + \cos(\beta))/2$.
##### Example
Consider the expressions $\cos((n+1)\theta)$ and $\cos((n-1)\theta)$. These can be re-expressed as:
```math
\begin{align*}
\cos((n+1)\theta) &= \cos(n\theta + \theta) = \cos(n\theta) \cos(\theta) - \sin(n\theta)\sin(\theta), \text{ and}\\
\cos((n-1)\theta) &= \cos(n\theta - \theta) = \cos(n\theta) \cos(-\theta) - \sin(n\theta)\sin(-\theta).
\end{align*}
```
But $\cos(-\theta) = \cos(\theta)$, whereas $\sin(-\theta) = -\sin(\theta)$. Using this, we add the two formulas above to get:
```math
\cos((n+1)\theta) = 2\cos(n\theta) \cos(\theta) - \cos((n-1)\theta).
```
That is the angle for a multiple of $n+1$ can be expressed in terms of the angle with a multiple of $n$ and $n-1$. This can be used recursively to find expressions for $\cos(n\theta)$ in terms of polynomials in $\cos(\theta)$.
## Inverse trigonometric functions
The trigonometric functions are all periodic. In particular they are not monotonic over their entire domain. This means there is no *inverse* function applicable. However, by restricting the domain to where the functions are monotonic, inverse functions can be defined:
* For $\sin(x)$, the restricted domain of $[-\pi/2, \pi/2]$ allows for the arcsine function to be defined. In `Julia` this is implemented with `asin`.
* For $\cos(x)$, the restricted domain of $[0,\pi]$ allows for the arccosine function to be defined. In `Julia` this is implemented with `acos`.
* For $\tan(x)$, the restricted domain of $(-\pi/2, \pi/2)$ allows for the arctangent function to be defined. In `Julia` this is implemented with `atan`.
For example, the arcsine function is defined for $-1 \leq x \leq 1$ and has a range of $-\pi/2$ to $\pi/2$:
```julia;
plot(asin, -1, 1)
```
The arctangent has domain of all real $x$. It has shape given by:
```julia;
plot(atan, -10, 10)
```
The horizontal asymptotes are $y=\pi/2$ and $y=-\pi/2$.
### Implications of a restricted domain
Notice that $\sin(\arcsin(x)) = x$ for any $x$ in $[-1,1]$, but, of course, not for all $x$, as the output of the sine function can't be arbitrarily large.
However, $\arcsin(\sin(x))$ is defined for all $x$, but only equals
$x$ when $x$ is in $[-\pi/2, \pi/2]$. The output, or range, of the
$\arcsin$ function is restricted to that interval.
This can be limiting at times. A common case is to find the angle in $[0, 2\pi)$ corresponding to a point $(x,y)$. In the simplest case (the first and fourth quadrants) this is just given by $\arctan(y/x)$. But with some work, the correct angle can be found for any pair $(x,y)$. As this is a common desire, the `atan` function with two arguments, `atan(y,x)`, is available. This function returns a value in $(-\pi, \pi]$.
For example, this will not give back $\theta$ without more work to identify the quadrant:
```julia;
theta = 3pi/4 # 2.35619...
x,y = (cos(theta), sin(theta)) # -0.7071..., 0.7071...
atan(y/x)
```
But,
```julia;
atan(y, x)
```
##### Example
A (white) light shining through a [prism](http://tinyurl.com/y8sczg4t) will be deflected depending on the material of the prism and the angles involved (refer to the link for a figure). The relationship can be analyzed by tracing a ray through the figure and utilizing Snell's law. If the prism has index of refraction $n$ then the ray will deflect by an amount $\delta$ that depends on the angle, $\alpha$ of the prism and the initial angle ($\theta_0$) according to:
```math
\delta = \theta_0 - \alpha + \arcsin(n \sin(\alpha - \arcsin(\frac{1}{n}\sin(\theta_0)))).
```
If $n=1.5$ (glass), $\alpha = \pi/3$ and $\theta_0=\pi/6$, find the deflection (in radians).
We have:
```julia; hold=true;
n, alpha, theta0 = 1.5, pi/3, pi/6
delta = theta0 - alpha + asin(n * sin(alpha - asin(sin(theta0)/n)))
```
For small $\theta_0$ and $\alpha$ the deviation is approximated by $(n-1)\alpha$. Compare this approximation to the actual value when $\theta_0 = \pi/10$ and $\alpha=\pi/15$.
We have:
```julia; hold=true;
n, alpha, theta0 = 1.5, pi/15, pi/10
delta = theta0 - alpha + asin(n * sin(alpha - asin(sin(theta0)/n)))
delta, (n-1)*alpha
```
The approximation error is about ``2.7`` percent.
##### Example
The AMS has an interesting column on
[rainbows](http://www.ams.org/publicoutreach/feature-column/fcarc-rainbows)
the start of which uses some formulas from the previous example. Click through to
see a ray of light passing through a spherical drop of water, as analyzed by Descartes. The
deflection of the ray occurs when the incident light hits the drop of
water, then there is an *internal* deflection of the light, and
finally when the light leaves, there is another deflection. The total
deflection (in radians) is $D = (i-r) + (\pi - 2r) + (i-r) = \pi - 2i - 4r$. However, the incident angle $i$ and the refracted angle $r$ are related by Snell's law: $\sin(i) = n \sin(r)$. The value $n$ is the index of refraction and is $4/3$ for water. (It was $3/2$ for glass in the previous example.) This gives
```math
D = \pi + 2i - 4 \arcsin(\frac{1}{n} \sin(i)).
```
Graphing this for incident angles between $0$ and $\pi/2$ we have:
```julia; hold=true;
n = 4/3
D(i) = pi + 2i - 4 * asin(sin(i)/n)
plot(D, 0, pi/2)
```
Descartes was interested in the minimum value of this graph, as it relates to where the light concentrates. This is roughly at $1$ radian or about $57$ degrees:
```julia;
rad2deg(1.0)
```
(Using calculus it can be seen to be $\arccos(((n^2-1)/3)^{1/2})$.)
##### Example: The Chebyshev Polynomials
Consider again this equation derived with the sum-and-difference formula:
```math
\cos((n+1)\theta) = 2\cos(n\theta) \cos(\theta) - \cos((n-1)\theta).
```
Let $T_n(x) = \cos(n \arccos(x))$. Calling $\theta = \arccos(x)$ for $-1 \leq x \leq x$ we get a relation between these functions:
```math
T_{n+1}(x) = 2x T_n(x) - T_{n-1}(x).
```
We can simplify a few: For example, when $n=0$ we see immediately that $T_0(x) = 1$, the constant function. Whereas with $n=1$ we get $T_1(x) = \cos(\arccos(x)) = x$. Things get more interesting as we get bigger $n$, for example using the equation above we get $T_2(x) = 2xT_1(x) - T_0(x) = 2x\cdot x - 1 = 2x^2 - 1$. Continuing, we'd get $T_3(x) = 2 x T_2(x) - T_1(x) = 2x(2x^2 - 1) - x = 4x^3 -3x$.
A few things become clear from the above two representations:
* Starting from $T_0(x) = 1$ and $T_1(x)=x$ and using the recursive defintion of $T_{n+1}$ we get a family of polynomials where $T_n(x)$ is a degree $n$ polynomial. These are defined for all $x$, not just $-1 \leq x \leq 1$.
* Using the initial definition, we see that the zeros of $T_n(x)$ all occur within $[-1,1]$ and happen when $n\arccos(x) = k\pi + \pi/2$, or $x=\cos((2k+1)/n \cdot \pi/2)$ for $k=0, 1, \dots, n-1$.
Other properties of this polynomial family are not at all obvious. One is that amongst all polynomials of degree $n$ with roots in $[-1,1]$, $T_n(x)$ will be the smallest in magnitude (after we divide by the leading coefficient to make all polynomials considered to be monic). We check this for one case. Take $n=4$, then we have: $T_4(x) = 8x^4 - 8x^2 + 1$. Compare this with $q(x) = (x+3/5)(x+1/5)(x-1/5)(x-3/5)$ (evenly spaced zeros):
```julia;
T4(x) = (8x^4 - 8x^2 + 1) / 8
q(x) = (x+3/5)*(x+1/5)*(x-1/5)*(x-3/5)
plot(abs ∘ T4, -1,1, label="|T₄|")
plot!(abs ∘ q, -1,1, label="|q|")
```
## Hyperbolic trigonometric functions
Related to the trigonometric functions are the hyperbolic
trigonometric functions. Instead of associating a point $(x,y)$ on the
unit circle with an angle $\theta$, we associate a point $(x,y)$ on
the unit *hyperbola* ($x^2 - y^2 = 1$). We define the hyperbolic
sine ($\sinh$) and hyperbolic cosine ($\cosh$) through $(\cosh(\theta),
\sinh(\theta)) = (x,y)$.
```julia; hold=true; echo=false
## inspired by https://en.wikipedia.org/wiki/Hyperbolic_function
# y^2 = x^2 - 1
top(x) = sqrt(x^2 - 1)
p = plot(; legend=false, aspect_ratio=:equal)
x₀ = 2
xs = range(1, x₀, length=100)
ys = top.(xs)
plot!(p, xs, ys, color=:red)
plot!(p, xs, -ys, color=:red)
xs = -reverse(xs)
ys = top.(xs)
plot!(p, xs, ys, color=:red)
plot!(p, xs, -ys, color=:red)
xs = range(-x₀, x₀, length=3)
plot!(p, xs, xs, linestyle=:dash, color=:blue)
plot!(p, xs, -xs, linestyle=:dash, color=:blue)
a = 1.2
plot!(p, [0,cosh(a)], [sinh(a), sinh(a)])
annotate!(p, sinh(a)/2, sinh(a)+0.25,"cosh(a)")
plot!(p, [cosh(a),cosh(a)], [sinh(a), 0])
annotate!(p, sinh(a) + 1, cosh(a)/2,"sinh(a)")
scatter!(p, [cosh(a)], [sinh(a)], markersize=5)
ts = range(0, a, length=100)
xs = cosh.(ts)
ys = sinh.(ts)
xs = [0, 1, xs..., 0]
ys = [0, 0, ys..., 0]
plot!(p, xs, ys, fillcolor=:red, fill=true, alpha=.3)
p
```
These values are more commonly expressed using the exponential function as:
```math
\begin{align*}
\sinh(x) &= \frac{e^x - e^{-x}}{2}\\
\cosh(x) &= \frac{e^x + e^{-x}}{2}.
\end{align*}
```
The hyperbolic tangent is then the ratio of $\sinh$ and $\cosh$. As well, three inverse hyperbolic functions can be defined.
The `Julia` functions to compute these values are named `sinh`, `cosh`, and `tanh`.
## Questions
###### Question
What is bigger $\sin(1.23456)$ or $\cos(6.54321)$?
```julia; hold=true; echo=false
a = sin(1.23456) > cos(6.54321)
choices = [raw"``\sin(1.23456)``", raw"``\cos(6.54321)``"]
ans = a ? 1 : 2
radioq(choices, ans, keep_order=true)
```
###### Question
Let $x=\pi/4$. What is bigger $\cos(x)$ or $x$?
```julia; hold=true; echo=false
x = pi/4
a = cos(x) > x
choices = [raw"``\cos(x)``", "``x``"]
ans = a ? 1 : 2
radioq(choices, ans, keep_order=true)
```
###### Question
The cosine function is a simple tranformation of the sine function. Which one?
```julia; hold=true; echo=false
choices = [
raw"``\cos(x) = \sin(x - \pi/2)``",
raw"``\cos(x) = \sin(x + \pi/2)``",
raw"``\cos(x) = \pi/2 \cdot \sin(x)``"]
ans = 2
radioq(choices, ans)
```
###### Question
Graph the secant function. The vertical asymptotes are at?
```julia; hold=true; echo=false
choices = [
L"The values $k\pi$ for $k$ in $\dots, -2, -1, 0, 1, 2, \dots$",
L"The values $\pi/2 + k\pi$ for $k$ in $\dots, -2, -1, 0, 1, 2, \dots$",
L"The values $2k\pi$ for $k$ in $\dots, -2, -1, 0, 1, 2, \dots$"]
ans = 2
radioq(choices, ans, keep_order=true)
```
###### Question
A formula due to [Bhaskara I](http://tinyurl.com/k89ux5q) dates to around 650AD and gives a rational function approximation to the sine function. In degrees, we have
```math
\sin(x^\circ) \approx \frac{4x(180-x)}{40500 - x(180-x)}, \quad 0 \leq x \leq 180.
```
Plot both functions over $[0, 180]$. What is the maximum difference between the two to two decimal points? (You may need to plot the difference of the functions to read off an approximate answer.)
```julia; hold=true; echo=false
numericq(.0015, .01)
```
###### Question
Solve the following equation for a value of $x$ using `acos`:
```math
\cos(x/3) = 1/3.
```
```julia; hold=true; echo=false
val = 3*acos(1/3)
numericq(val)
```
###### Question
For any postive integer $n$ the equation $\cos(x) - nx = 0$ has a solution in $[0, \pi/2]$. Graphically estimate the value when $n=10$.
```julia; hold=true; echo=false
val = 0.1
numericq(val)
```
###### Question
The sine function is an *odd* function.
* The hyperbolic sine is:
```julia; hold=true; echo=false
choices = ["odd", "even", "neither"]
ans = 1
radioq(choices, ans, keep_order=true)
```
* The hyperbolic cosine is:
```julia; hold=true; echo=false
choices = ["odd", "even", "neither"]
ans = 2
radioq(choices, ans, keep_order=true)
```
* The hyperbolic tangent is:
```julia; hold=true; echo=false
choices = ["odd", "even", "neither"]
ans = 1
radioq(choices, ans, keep_order=true)
```
###### Question
The hyperbolic sine satisfies this formula:
```math
\sinh(\theta + \beta) = \sinh(\theta)\cosh(\beta) + \sinh(\beta)\cosh(\theta).
```
Is this identical to the pattern for the regular sine function?
```julia; hold=true; echo=false
yesnoq(true)
```
The hyperbolic cosine satisfies this formula:
```math
\cosh(\theta + \beta) = \cosh(\theta)\cosh(\beta) + \sinh(\beta)\sinh(\theta).
```
Is this identical to the pattern for the regular sine function?
```julia; hold=true; echo=false
yesnoq(false)
```

502
CwJ/precalc/variables.jmd Normal file
View File

@@ -0,0 +1,502 @@
# Variables
## Assignment
```julia; echo=false; results="hidden"
using CalculusWithJulia
using CalculusWithJulia.WeaveSupport
const frontmatter = (
title = "Variables",
description = "Calculus with Julia: Variables",
tags = ["CalculusWithJulia", "precalc", "variables"],
);
nothing
```
```julia; echo=false;
imgfile = "figures/calculator.png"
caption = "Screenshot of a calculator provided by the Google search engine."
ImageFile(:precalc, imgfile, caption)
```
The Google calculator has a button `Ans` to refer to the answer to the
previous evaluation. This is a form of memory. The last answer is
stored in a specific place in memory for retrieval when `Ans` is
used. In some calculators, more advanced memory features are
possible. For some, it is possible to push values onto a stack of
values for them to be referred to at a later time. This proves useful
for complicated expressions, say, as the expression can be broken into
smaller intermediate steps to be computed. These values can then be
appropriately combined. This strategy is a good one, though the memory
buttons can make its implementation a bit cumbersome.
With `Julia`, as with other programming languages, it is very easy to
refer to past evaluations. This is done by *assignment* whereby a
computed value stored in memory is associated with a name. The name
can be used to look up the value later. Assignment does not change the value of the object being assigned, it only introduces a reference to it.
Assignment in `Julia` is handled by the equals sign and takes the general form `variable_name = value`. For example, here we assign values to the variables `x` and `y`
```julia;
x = sqrt(2)
y = 42
```
In an assignment, the right hand side is always returned, so it appears
nothing has happened. However, the values are there, as can be checked
by typing their name
```julia;
x
```
Just typing a variable name (without a trailing semicolon) causes the assigned value to be displayed.
Variable names can be reused, as here, where we redefine `x`:
```julia;hold=true
x = 2
```
```julia; echo=false
note("""
The `Pluto` interface for `Julia` is idiosyncratic, as variables are *reactive*. This interface allows changes to a variable `x` to propogate to all other cells referring to `x`. Consequently, the variable name can only be assigned *once* per notebook **unless** the name is in some other namespace, which can be arranged by including the assignment inside a function or a `let` block.
""")
```
`Julia` is referred to as a "dynamic language" which means (in most
cases) that a variable can be reassigned with a value of a different
type, as we did with `x` where first it was assigned to a floating
point value then to an integer value. (Though we meet some cases - generic functions - where `Julia`
balks at reassigning a variable if the type if different.)
More importantly than displaying a value, is the use of variables to
build up more complicated expressions. For example, to compute
```math
\frac{1 + 2 \cdot 3^4}{5 - 6/7}
```
we might break it into the grouped pieces implied by the mathematical notation:
```julia;
top = 1 + 2*3^4
bottom = 5 - 6/7
top/bottom
```
#### Examples
##### Example
Imagine we have the following complicated expression related to the trajectory of a [projectile](http://www.researchgate.net/publication/230963032_On_the_trajectories_of_projectiles_depicted_in_early_ballistic_woodcuts) with wind resistance:
```math
\left(\frac{g}{k v_0\cos(\theta)} + \tan(\theta) \right) t + \frac{g}{k^2}\ln\left(1 - \frac{k}{v_0\cos(\theta)} t \right)
```
Here $g$ is the gravitational constant $9.8$ and $v_0$, $\theta$ and
$k$ parameters, which we take to be $200$, $45$ degrees, and $1/2$
respectively. With these values, the above expression can be computed
when $s=100$:
```julia;
g = 9.8
v0 = 200
theta = 45
k = 1/2
t = 100
a = v0 * cosd(theta)
(g/(k*a) + tand(theta))* t + (g/k^2) * log(1 - (k/a)*t)
```
By defining a new variable `a` to represent a value that is repeated a few times in the expression, the last command is greatly simplified. Doing so makes it much easier to check for accuracy against the expression to compute.
##### Example
A common expression in mathematics is a polynomial expression, for example $-16s^2 + 32s - 12$. Translating this to `Julia` at $s =3$ we might have:
```julia;
s = 3
-16*s^2 + 32*s - 12
```
This looks nearly identical to the mathematical expression, but we inserted `*` to indicate multiplication between the constant and the variable. In fact, this step is not needed as Julia allows numeric literals to have an implied multiplication:
```julia;
-16s^2 + 32s - 12
```
## Where math and computer notations diverge
It is important to recognize that `=` to `Julia` is not in analogy to
how $=$ is used in mathematical notation. The following `Julia` code
is not an equation:
```julia;hold=true
x = 3
x = x^2
```
What happens instead? The right hand side is evaluated (`x` is
squared), the result is stored and bound to the variable `x` (so that
`x` will end up pointing to the new value, `9`, and not the original one, `3`); finally the value computed on the
right-hand side is returned and in this case displayed, as there is no
trailing semicolon to suppress the output.
This is completely unlike the mathematical equation $x = x^2$ which is
typically solved for values of $x$ that satisfy the equation ($0$ and
$1$).
##### Example
Having `=` as assignment is usefully exploited when modeling
sequences. For example, an application of Newton's method might end up
with this expression:
```math
x_{i+1} = x_i - \frac{x_i^2 - 2}{2x_i}
```
As a mathematical expression, for each $i$ this defines a new value
for $x_{i+1}$ in terms of a known value $x_i$. This can be used to
recursively generate a sequence, provided some starting point is
known, such as $x_0 = 2$.
The above might be written instead with:
```julia;hold=true
x = 2
x = x - (x^2 - 2) / (2x)
x = x - (x^2 - 2) / (2x)
```
Repeating this last line will generate new values of `x` based on the
previous one - no need for subscripts. This is exactly what the
mathematical notation indicates is to be done.
## Context
The binding of a value to a variable name happens within some
context. For our simple illustrations, we are assigning values, as
though they were typed at the command line. This stores the binding in
the `Main` module. `Julia` looks for variables in this module when it
encounters an expression and the value is substituted. Other uses, such as when variables are defined within a function, involve different contexts which may not be
visible within the `Main` module.
```julia; echo=false;
note("""
The `varinfo` function will list the variables currently defined in the
main workspace. There is no mechanism to delete a single variable.
""")
```
```julia; echo=false;
alert("""
**Shooting oneselves in the foot.** `Julia` allows us to locally
redefine variables that are built in, such as the value for `pi` or
the function object assigned to `sin`. For example, this is a
perfectly valid command `sin=3`. However, it will overwrite the
typical value of `sin` so that `sin(3)` will be an error. At the terminal, the
binding to `sin` occurs in the `Main` module. This shadows that
value of `sin` bound in the `Base` module. Even if redefined in
`Main`, the value in base can be used by fully qualifying the name,
as in `Base.sin(pi)`. This uses the notation
`module_name.variable_name` to look up a binding in a module.
""")
```
## Variable names
`Julia` has a very wide set of possible
[names](https://docs.julialang.org/en/stable/manual/variables/#Allowed-Variable-Names-1)
for variables. Variables are case sensitive and their names can
include many
[Unicode](http://en.wikipedia.org/wiki/List_of_Unicode_characters)
characters. Names must begin with a letter or an appropriate Unicode
value (but not a number). There are some reserved words, such as `try`
or `else` which can not be assigned to. However, many built-in names
can be locally overwritten. Conventionally, variable names are lower
case. For compound names, it is not unusual to see them squished
together, joined with underscores, or written in camelCase.
```julia;
value_1 = 1
a_long_winded_variable_name = 2
sinOfX = sind(45)
__private = 2 # a convention
```
### Unicode names
Julia allows variable names to use Unicode identifiers. Such
names allow `julia` notation to mirror that of many
mathematical texts. For example, in calculus the variable $\epsilon$
is often used to represent some small number. We can assign to a
symbol that looks like $\epsilon$ using `Julia`'s LaTeX input
mode. Typing `\epsilon[tab]` will replace the text with the symbol
within `IJulia` or the command line.
```julia;
ϵ = 1e-10
```
Entering Unicode names follows the pattern of
"slash" + LaTeX name + `[tab]` key. Some other ones that are useful
are `\delta[tab]`, `\alpha[tab]`, and `\beta[tab]`, though there are
[hundreds](https://github.com/JuliaLang/julia/blob/master/stdlib/REPL/src/latex_symbols.jl)
of other values defined.
For example, we could have defined `theta` (`\theta[tab]`) and `v0` (`v\_0[tab]`) using Unicode to make them match more closely the typeset math:
```julia;
θ = 45; v₀ = 200
```
These notes often use Unicode alternatives to avoid the `Pluto` requirement of a single use of assigning to a variable name in a notebook without placing the assignment in a `let` block or a function body.
```julia; echo=false;
alert("""
There is even support for tab-completion of
[emojis](https://github.com/JuliaLang/julia/blob/master/stdlib/REPL/src/emoji_symbols.jl)
such as `\\:snowman:[tab]` or `\\:koala:[tab]`
""")
```
##### Example
As mentioned the value of $e$ is bound to the Unicode value
`\euler[tab]` and not the letter `e`, so Unicode entry is required to
access this constant This isn't quite true. The `MathConstants` module
defines `e`, as well as a few other values accessed via Unicode. When
the `CalculusWithJulia` package is loaded, as will often be done in
these notes, a value of `exp(1)` is assigned to `e`.
## Tuple assignment
It is a common task to define more than one variable. Multiple definitions can be done in one line, using semicolons to break up the commands, as with:
```julia;hold=true
a = 1; b = 2; c=3
```
For convenience, `Julia` allows an alternate means to define more than one variable at a time. The syntax is similar:
```julia;hold=true
a, b, c = 1, 2, 3
b
```
This sets `a=1`, `b=2`, and `c=3`, as suggested. This construct relies
on *tuple destructuring*. The expression on the right hand side forms
a tuple of values. A tuple is a container for different types of
values, and in this case the tuple has 3 values. When the same number
of variables match on the left-hand side as those in the container on
the right, the names are assigned one by one.
The value on the right hand side is evaluated, then the assignment occurs. The following exploits this to swap the values assigned to `a` and `b`:
```julia;hold=true
a, b = 1, 2
a, b = b, a
```
#### Example, finding the slope
Find the slope of the line connecting the points $(1,2)$ and $(4,6)$. We begin by defining the values and then applying the slope formula:
```julia;
x0, y0 = 1, 2
x1, y1 = 4, 6
m = (y1 - y0) / (x1 - x0)
```
Of course, this could be computed directly with `(6-2) / (4-1)`, but by using familiar names for the values we can be certain we apply the formula properly.
## Questions
###### Question
Let $a=10$, $b=2.3$, and $c=8$. Find the value of $(a-b)/(a-c)$.
```julia; hold=true; echo=false;
a,b,c = 10, 2.3, 8;
numericq((a-b)/(a-c))
```
###### Question
Let `x = 4`. Compute $y=100 - 2x - x^2$. What is the value:
```julia; hold=true; echo=false;
x = 4
y =- 100 - 2x - x^2
numericq(y, 0.1)
```
###### Question
What is the answer to this computation?
```julia; eval=false
a = 3.2; b=2.3
a^b - b^a
```
```julia; hold=true; echo=false;
a = 3.2; b=2.3;
val = a^b - b^a;
numericq(val)
```
###### Question
For longer computations, it can be convenient to do them in parts, as
this makes it easier to check for mistakes.
For example, to compute
```math
\frac{p - q}{\sqrt{p(1-p)}}
```
for $p=0.25$ and $q=0.2$ we might do:
```julia; eval=false
p, q = 0.25, 0.2
top = p - q
bottom = sqrt(p*(1-p))
ans = top/bottom
```
What is the result of the above?
```julia; hold=true; echo=false;
p, q = 0.25, 0.2;
top = p - q;
bottom = sqrt(p*(1-p));
ans = top/bottom;
numericq(ans)
```
###### Question
Using variables to record the top and the bottom of the expression, compute the following for $x=3$:
```math
y = \frac{x^2 - 2x - 8}{x^2 - 9x - 20}.
```
```julia; hold=true; echo=false;
x = 3
val = (x^2 - 2x - 8)/(x^2 - 9x - 20)
numericq(val)
```
###### Question
Which if these is not a valid variable name (identifier) in `Julia`:
```julia; hold=true; echo=false;
choices = [
q"5degreesbelowzero",
q"some_really_long_name_that_is_no_fun_to_type",
q"aMiXeDcAsEnAmE",
q"fahrenheit451"
]
ans = 1
radioq(choices, ans)
```
###### Question
Which of these symbols is one of `Julia`'s built-in math constants?
```julia; hold=true; echo=false;
choices = [q"pi", q"oo", q"E", q"I"]
ans = 1
radioq(choices, ans)
```
###### Question
What key sequence will produce this assignment
```julia; eval=false
δ = 1/10
```
```julia; hold=true; echo=false;
choices=[
q"\delta[tab] = 1/10",
q"delta[tab] = 1/10",
q"$\\delta$ = 1/10"]
ans = 1
radioq(choices, ans)
```
###### Question
Which of these three statements will **not** be a valid way to assign three variables at once:
```julia; hold=true; echo=false;
choices = [
q"a=1, b=2, c=3",
q"a,b,c = 1,2,3",
q"a=1; b=2; c=3"]
ans = 1
radioq(choices, ans)
```
###### Question
The fact that assignment *always* returns the value of the right hand side *and* the fact that the `=` sign associates from right to left means that the following idiom:
```julia; eval=false
x = y = z = 3
```
Will always:
```julia; hold=true; echo=false;
choices = ["Assign all three variables at once to a value of `3`",
"Create ``3`` linked values that will stay synced when any value changes",
"Throw an error"
]
ans = 1
radioq(choices, ans)
```

1002
CwJ/precalc/vectors.jmd Normal file

File diff suppressed because it is too large Load Diff