updates
This commit is contained in:
parent
ff0f8a060d
commit
92f4cba496
@ -1,4 +1,4 @@
|
||||
version: "0.22"
|
||||
version: "0.24"
|
||||
engine: julia
|
||||
|
||||
project:
|
||||
@ -14,7 +14,6 @@ book:
|
||||
search: true
|
||||
repo-url: https://github.com/jverzani/CalculusWithJuliaNotes.jl
|
||||
repo-subdir: quarto/
|
||||
downloads: [pdf]
|
||||
repo-actions: [edit, issue]
|
||||
navbar:
|
||||
background: light
|
||||
@ -23,7 +22,7 @@ book:
|
||||
pinned: false
|
||||
sidebar:
|
||||
collapse-level: 1
|
||||
page-footer: "Copyright 2022-24, John Verzani"
|
||||
page-footer: "Copyright 2022-25, John Verzani"
|
||||
chapters:
|
||||
- index.qmd
|
||||
- part: precalc.qmd
|
||||
|
@ -229,13 +229,24 @@ function secant_line_tangent_line_graph(n)
|
||||
m = (f(c+h) - f(c))/h
|
||||
|
||||
xs = range(0, stop=pi, length=50)
|
||||
plt = plot(f, 0, pi, legend=false, size=fig_size)
|
||||
fig_size=(800, 600)
|
||||
plt = plot(f, 0, pi, legend=false, size=fig_size,
|
||||
line=(2,),
|
||||
axis=([],false),
|
||||
ylims=(-.1,1.5)
|
||||
)
|
||||
plot!([0, 1.1* pi],[0,0], line=(3, :black))
|
||||
plot!([0, 0], [0,2*1], line=(3, :black))
|
||||
|
||||
plot!(plt, xs, f(c) .+ cos(c)*(xs .- c), color=:orange)
|
||||
plot!(plt, xs, f(c) .+ m*(xs .- c), color=:black)
|
||||
scatter!(plt, [c,c+h], [f(c), f(c+h)], color=:orange, markersize=5)
|
||||
|
||||
plot!(plt, [c, c+h, c+h], [f(c), f(c), f(c+h)], color=:gray30)
|
||||
annotate!(plt, [(c+h/2, f(c), text("h", :top)), (c + h + .05, (f(c) + f(c + h))/2, text("f(c+h) - f(c)", :left))])
|
||||
|
||||
annotate!(plt, [(c+h/2, f(c), text("h", :top)),
|
||||
(c + h + .05, (f(c) + f(c + h))/2, text("f(c+h) - f(c)", :left))
|
||||
])
|
||||
|
||||
plt
|
||||
end
|
||||
|
@ -773,6 +773,73 @@ answ = 4
|
||||
radioq(choices, answ)
|
||||
```
|
||||
|
||||
###### Question
|
||||
|
||||
Consider the following figure of a graph of $f$:
|
||||
|
||||
```{julia}
|
||||
#| echo: false
|
||||
ex(x) = x * tanh(exp(x))
|
||||
a, b = -5, 1
|
||||
plot(ex, a, b, legend=false,
|
||||
axis=([], false),
|
||||
color = :royalblue
|
||||
)
|
||||
plot!([a-.1, b+.1], [0,0], line=(3, :black))
|
||||
|
||||
zs = find_zeros(ex, (a, b))
|
||||
cps = find_zeros(ex', (a, b))
|
||||
ips = find_zeros(ex'', (a, b))
|
||||
|
||||
scatter!(zs, ex.(zs), marker=(5, "black", :circle))
|
||||
scatter!(cps, ex.(cps), marker=(5, "forestgreen", :diamond))
|
||||
scatter!(ips, ex.(ips), marker=(5, :brown3, :star5))
|
||||
```
|
||||
|
||||
The black circle denotes what?
|
||||
|
||||
```{julia}
|
||||
choices = [raw"A zero of $f$",
|
||||
raw"A critical point of $f$",
|
||||
raw"An inflection point of $f$"]
|
||||
answ = 1
|
||||
radioq(choices, answ)
|
||||
```
|
||||
|
||||
|
||||
The black circle denotes what?
|
||||
|
||||
```{julia}
|
||||
choices = [raw"A zero of $f$",
|
||||
raw"A critical point of $f$",
|
||||
raw"An inflection point of $f$"]
|
||||
answ = 1
|
||||
radioq(choices, answ)
|
||||
```
|
||||
|
||||
|
||||
The green diamond denotes what?
|
||||
|
||||
```{julia}
|
||||
choices = [raw"A zero of $f$",
|
||||
raw"A critical point of $f$",
|
||||
raw"An inflection point of $f$"]
|
||||
answ = 2
|
||||
radioq(choices, answ)
|
||||
```
|
||||
|
||||
|
||||
The red stars denotes what?
|
||||
|
||||
```{julia}
|
||||
choices = [raw"Zeros of $f$",
|
||||
raw"Critical points of $f$",
|
||||
raw"Inflection points of $f$"]
|
||||
answ = 3
|
||||
radioq(choices, answ)
|
||||
```
|
||||
|
||||
|
||||
###### Question
|
||||
|
||||
|
||||
@ -1040,7 +1107,8 @@ This accurately summarizes how the term is used outside of math books. Does it a
|
||||
#| echo: false
|
||||
choices = ["Yes. Same words, same meaning",
|
||||
"""No, but it is close. An inflection point is when the *acceleration* changes from positive to negative, so if "results" are about how a company's rate of change is changing, then it is in the ballpark."""]
|
||||
radioq(choices, 2)
|
||||
answ = 2
|
||||
radioq(choices, answ)
|
||||
```
|
||||
|
||||
###### Question
|
||||
@ -1054,5 +1122,6 @@ The function $f(x) = x^3 + x^4$ has a critical point at $0$ and a second derivat
|
||||
#| echo: false
|
||||
choices = ["As ``x^3`` has no extrema at ``x=0``, neither will ``f``",
|
||||
"As ``x^4`` is of higher degree than ``x^3``, ``f`` will be ``U``-shaped, as ``x^4`` is."]
|
||||
radioq(choices, 1)
|
||||
answ = 1
|
||||
radioq(choices, answ)
|
||||
```
|
||||
|
@ -11,6 +11,7 @@ using CalculusWithJulia
|
||||
using Plots
|
||||
plotly()
|
||||
using SymPy
|
||||
using Roots
|
||||
using TaylorSeries
|
||||
using DualNumbers
|
||||
```
|
||||
@ -183,18 +184,30 @@ In each of these cases, a more complicated non-linear function is well approxim
|
||||
```{julia}
|
||||
#| hold: true
|
||||
#| echo: false
|
||||
#| label: fig-tangent-dy-dx
|
||||
#| fig-cap: "Graph with tangent line layered on"
|
||||
f(x) = sin(x)
|
||||
a, b = -1/4, pi/2
|
||||
|
||||
p = plot(f, a, b, legend=false);
|
||||
p = plot(f, a, b, legend=false,
|
||||
line=(3, :royalblue),
|
||||
axis=([], false)
|
||||
);
|
||||
|
||||
plot!(p, x->x, a, b);
|
||||
plot!(p, [0,1,1], [0, 0, 1], color=:brown);
|
||||
|
||||
plot!(p, [1,1], [0, sin(1)], color=:green, linewidth=4);
|
||||
annotate!(p, collect(zip([1/2, 1+.075, 1/2-1/8], [.05, sin(1)/2, .75], ["Δx", "Δy", "m=dy/dx"])));
|
||||
|
||||
scatter!([0], [0], marker=(5, :mediumorchid3))
|
||||
annotate!(p, [(0, f(0), text("(c,f(c))", :bottom,:right))])
|
||||
annotate!(p, collect(zip([1/2, 1+.075, 1/2-1/8],
|
||||
[.05, sin(1)/2, .75],
|
||||
["Δx", "Δy", "m=dy/dx"])));
|
||||
p
|
||||
```
|
||||
|
||||
The plot shows the tangent line with slope $dy/dx$ and the actual change in $y$, $\Delta y$, for some specified $\Delta x$. The small gap above the sine curve is the error were the value of the sine approximated using the drawn tangent line. We can see that approximating the value of $\Delta y = \sin(c+\Delta x) - \sin(c)$ with the often easier to compute $(dy/dx) \cdot \Delta x = f'(c)\Delta x$ - for small enough values of $\Delta x$ - is not going to be too far off provided $\Delta x$ is not too large.
|
||||
The plot in @fig-tangent-dy-dx shows a tangent line with slope $dy/dx$ and the actual change in $y$, $\Delta y$, for some specified $\Delta x$ at a point $(c,f(c))$. The small gap above the sine curve is the error were the value of the sine approximated using the drawn tangent line. We can see that approximating the value of $\Delta y = \sin(c+\Delta x) - \sin(c)$ with the often easier to compute $(dy/dx) \cdot \Delta x = f'(c)\Delta x$ - for small enough values of $\Delta x$ - is not going to be too far off provided $\Delta x$ is not too large.
|
||||
|
||||
|
||||
This approximation is known as linearization. It can be used both in theoretical computations and in practical applications. To see how effective it is, we look at some examples.
|
||||
@ -643,6 +656,151 @@ x = Dual(1, 1)
|
||||
|
||||
We can see the derivative again reflects the chain rule, it being given by `1/x * xp` where `xp` acts like `dx` (from assignments `%5` and `%4`). Comparing the two outputs, we see only the assignment to `%5` differs, it reflecting the derivative of the function.
|
||||
|
||||
## Curvature
|
||||
|
||||
The curvature of a function will be a topic in a later section on differentiable vector calculus, but the concept of linearization can be used to give an earlier introduction.
|
||||
|
||||
|
||||
The tangent line linearizes the function, it begin the best linear approximation to the graph of the function at the point. The slope of the tangent line is the limi of the slopes of different secant lines. Consdider now, the orthogonal concept, the *normal line* at a point. This is a line perpendicular to the tangent line that goes through the point on the curve.
|
||||
|
||||
At a point $(c,f(c))$ the slope of the normal line is $-1/f'(c)$.
|
||||
|
||||
Following [Kirby C. Smith](https://doi.org/10.2307/2687102), consider two nearby points on the curve of $f$ and suppose we take the two normal lines at $x=c$ and $x=c+h$. These two curves will intersect if the lines are not parallel. To ensure this, assueme that in some neighborhood of $c$, $f'(c)$ is increasing.
|
||||
|
||||
The two normal lines are:
|
||||
|
||||
$$
|
||||
\begin{align*}
|
||||
y &= f(c) - \frac{1}{f'(c)}(x-c)\\
|
||||
y &= f(c+h) - \frac{1}{f'(c+h)}(x-(c+h))\\
|
||||
\end{align*}
|
||||
$$
|
||||
|
||||
Rearranging, we have
|
||||
|
||||
$$
|
||||
\begin{align*}
|
||||
-f'(c)(y-f(c) &= x-c\\
|
||||
-f'(c+h)(y-f(c+h)) &= x-(c+h)
|
||||
\end{align*}
|
||||
$$
|
||||
|
||||
|
||||
Call $R$ the intersection point of the two normal lines:
|
||||
|
||||
```{julia}
|
||||
#| echo: false
|
||||
using Roots
|
||||
let
|
||||
f(x) = x^4
|
||||
fp(x) = 4x^3
|
||||
c = 1/4
|
||||
h = 1/4
|
||||
nlc(x) = f(c) - 1/fp(c) * (x - c)
|
||||
nlch(x) = f(c+h) - 1/fp(c+h) * (x-(c+h))
|
||||
canvas() = plot(axis=([],false), legend=false, aspect_ratio=:equal)
|
||||
canvas()
|
||||
plot!(f, 0, 3/4; line=(3,))
|
||||
plot!(nlc; ylim=(-1/4, 1))
|
||||
plot!(nlch; ylim=(-1/4, 1))
|
||||
Rx = find_zero(x -> nlc(x) - nlch(x), (-10, 10))
|
||||
scatter!([c,c+h], f.([c, c+h]))
|
||||
scatter!([Rx], [nlc(Rx)])
|
||||
annotate!([(c, f(c), "(c,f(c))",:top),
|
||||
(c+h, f(c+h), "(c+h, f(c+h))",:bottom),
|
||||
(Rx, nlc(Rx), "R",:left)])
|
||||
end
|
||||
```
|
||||
|
||||
|
||||
|
||||
What happens to $R$ as $h \rightarrow 0$?
|
||||
|
||||
We can symbolically solve to see:
|
||||
|
||||
```{julia}
|
||||
@syms 𝑓() 𝑓p() 𝑓pp() x y c ℎ
|
||||
n1 = -𝑓p(c)*(y-𝑓(c)) ~ x - c
|
||||
n2 = -𝑓p(c+ℎ)*(y-𝑓(c+ℎ)) ~ x - (c+ℎ)
|
||||
R = solve((n1, n2), (x, y))
|
||||
```
|
||||
|
||||
|
||||
Taking limits of each term as $h$ goes to zero we have after some notation-simplfying substitution:
|
||||
|
||||
```{julia}
|
||||
R = Dict(k => limit(R[k], ℎ=>0) for k in (x,y))
|
||||
Rx = R[x](limit((𝑓(c+ℎ)-𝑓(c))/ℎ, ℎ=>0) => 𝑓p(c),
|
||||
limit((𝑓p(c+ℎ)-𝑓p(c))/ℎ, ℎ=>0) => 𝑓pp(c))
|
||||
```
|
||||
|
||||
|
||||
and
|
||||
|
||||
```{julia}
|
||||
Ry = R[y](limit((𝑓(c+ℎ)-𝑓(c))/ℎ, ℎ=>0) => 𝑓p(c),
|
||||
limit((𝑓p(c+ℎ)-𝑓p(c))/ℎ, ℎ=>0) => 𝑓pp(c))
|
||||
```
|
||||
|
||||
The squared distance, $r^2$, of $R$ to $(c,f(c))$ is then:
|
||||
|
||||
```{julia}
|
||||
simplify((Rx-c)^2 + (Ry-𝑓(c))^2)
|
||||
```
|
||||
|
||||
Or
|
||||
|
||||
$$
|
||||
r^2 = \frac{(f'(c)^2 + 1)^3}{f''(c)^2}.
|
||||
$$
|
||||
|
||||
|
||||
This formula for $r$ is known as the radius of curvature of $f$ -- the radius of the *circle* that best approximates the function at the point. That is, this value reflects the curvature of $f$ supplementing the tangent line or best *linear* approximation to the graph of $f$ at the point.
|
||||
|
||||
```{julia}
|
||||
#| echo: false
|
||||
let
|
||||
f(x) = x^4
|
||||
fp(x) = 4x^3
|
||||
fpp(x) = 12x^2
|
||||
c = 1/4
|
||||
h = 1/4
|
||||
nlc(x) = f(c) - 1/fp(c) * (x - c)
|
||||
nlch(x) = f(c+h) - 1/fp(c+h) * (x-(c+h))
|
||||
canvas() = plot(axis=([],false), legend=false, aspect_ratio=:equal)
|
||||
canvas()
|
||||
plot!(f, -1/4, 3/4; line=(3,))
|
||||
tl(x) = f(c) + f'(c)*(x-c)
|
||||
plot!(tl, ylim=(-1/4, 3/2); line=(2, :dot))
|
||||
|
||||
|
||||
Rx, Ry = c - fp(c)^3 / fpp(c) - fp(c)/fpp(c), f(c) + (fp(c)^2+1)/fpp(c)
|
||||
r = (fp(c)^2 + 1)^(3/2) / abs(fpp(c))
|
||||
|
||||
scatter!([c], f.([c]))
|
||||
scatter!([Rx], [nlc(Rx)])
|
||||
annotate!([(c, f(c), "(c,f(c))",:top),
|
||||
(Rx, nlc(Rx), "R",:left)])
|
||||
|
||||
|
||||
Delta = pi/10
|
||||
theta = range(3pi/2 - Delta, 2pi - 3Delta, length=100)
|
||||
xs, ys = cos.(theta), sin.(theta)
|
||||
|
||||
|
||||
plot!(Rx .+ r.*xs, Ry .+ r.*ys)
|
||||
|
||||
x0s, y0s = [Rx,Rx .+ r * first(xs)],[Ry,Ry .+ r * first(ys)]
|
||||
xns, yns = [Rx,Rx .+ r * last(xs)],[Ry,Ry .+ r * last(ys)]
|
||||
xcs, ycs = [Rx,c],[Ry,f(c)]
|
||||
sty = (2, :0.25, :dash)
|
||||
plot!(x0s, y0s; line=sty);
|
||||
plot!(xcs, ycs; line=sty);
|
||||
plot!(xns, yns; line=sty)
|
||||
|
||||
end
|
||||
```
|
||||
|
||||
|
||||
## Questions
|
||||
|
||||
|
@ -330,26 +330,35 @@ Let $f(x)$ be differentiable on $(a,b)$ and continuous on $[a,b]$. Then there ex
|
||||
This says for any secant line between $a < b$ there will be a parallel tangent line at some $c$ with $a < c < b$ (all provided $f$ is differentiable on $(a,b)$ and continuous on $[a,b]$).
|
||||
|
||||
|
||||
This graph illustrates the theorem. The orange line is the secant line. A parallel line tangent to the graph is guaranteed by the mean value theorem. In this figure, there are two such lines, rendered using red.
|
||||
Figure @fig-mean-value-theorem illustrates the theorem. The orange line is the secant line. A parallel line tangent to the graph is guaranteed by the mean value theorem. In this figure, there are two such lines, rendered using red.
|
||||
|
||||
|
||||
```{julia}
|
||||
#| hold: true
|
||||
#| echo: false
|
||||
#| label: fig-mean-value-theorem
|
||||
f(x) = x^3 - x
|
||||
a, b = -2, 1.75
|
||||
m = (f(b) - f(a)) / (b-a)
|
||||
cps = find_zeros(x -> f'(x) - m, a, b)
|
||||
|
||||
p = plot(f, a-1, b+1, linewidth=3, legend=false)
|
||||
plot!(x -> f(a) + m*(x-a), a-1, b+1, linewidth=3, color=:orange)
|
||||
p = plot(f, a-0.75, b+1,
|
||||
color=:mediumorchid3,
|
||||
linewidth=3, legend=false,
|
||||
axis=([],false),
|
||||
)
|
||||
|
||||
|
||||
plot!(x -> f(a) + m*(x-a), a-1, b+1, linewidth=5, color=:royalblue)
|
||||
scatter!([a,b], [f(a), f(b)])
|
||||
annotate!([(a, f(a), text("a", :bottom)),
|
||||
(b, f(b), text("b", :bottom))])
|
||||
|
||||
|
||||
for cp in cps
|
||||
plot!(x -> f(cp) + f'(cp)*(x-cp), a-1, b+1, color=:red)
|
||||
plot!(x -> f(cp) + f'(cp)*(x-cp), a-1, b+1, color=:brown3)
|
||||
end
|
||||
|
||||
scatter!(cps, f.(cps))
|
||||
subsscripts = collect("₀₁₂₃₄₅₆₇₈₉")
|
||||
annotate!([(cp, f(cp), text("c"*subsscripts[i], :bottom)) for (i,cp) ∈ enumerate(cps)])
|
||||
|
@ -224,40 +224,49 @@ Raphson (1690) proposed a simplification avoiding the computation of new polynom
|
||||
##### Example: visualizing convergence
|
||||
|
||||
|
||||
This graphic demonstrates the method and the rapid convergence:
|
||||
@fig-newtons-method demonstrates the method and the rapid convergence:
|
||||
|
||||
|
||||
```{julia}
|
||||
#| echo: false
|
||||
function newtons_method_graph(n, f, a, b, c)
|
||||
nothing
|
||||
function newtons_method_graph(n, f, a, b, c; label=false)
|
||||
|
||||
xstars = [c]
|
||||
xs = [c]
|
||||
ys = [0.0]
|
||||
|
||||
plt = plot(f, a, b, legend=false, size=fig_size)
|
||||
plt = plot(f, a, b, legend=false, size=fig_size,
|
||||
line = (:royalblue, 3),
|
||||
axis = ([], false)
|
||||
)
|
||||
plot!(plt, [a, b], [0,0], color=:black)
|
||||
|
||||
|
||||
ts = range(a, stop=b, length=50)
|
||||
for i in 1:n
|
||||
x0 = xs[end]
|
||||
x1 = x0 - f(x0)/D(f)(x0)
|
||||
x1 = x0 - f(x0)/f'(x0)
|
||||
push!(xstars, x1)
|
||||
append!(xs, [x0, x1])
|
||||
append!(ys, [f(x0), 0])
|
||||
end
|
||||
plot!(plt, xs, ys, color=:orange)
|
||||
scatter!(plt, xstars, 0*xstars, color=:orange, markersize=5)
|
||||
if label
|
||||
subs = collect("₁₂₃₄₅₆₇₈₉")
|
||||
labs = ["x$(subs[i])" for i in eachindex(xstars)]
|
||||
annotate!(collect(zip(xstars, 0*xstars, labs,[:bottom for _ in xstars])))
|
||||
end
|
||||
plt
|
||||
end
|
||||
nothing
|
||||
```
|
||||
|
||||
```{julia}
|
||||
#| hold: true
|
||||
#| echo: false
|
||||
#| cache: true
|
||||
#| label: fig-newtons-method
|
||||
### {{{newtons_method_example}}}
|
||||
gr()
|
||||
caption = """
|
||||
@ -270,7 +279,7 @@ n = 6
|
||||
fn, a, b, c = x->log(x), .15, 2, .2
|
||||
|
||||
anim = @animate for i=1:n
|
||||
newtons_method_graph(i-1, fn, a, b, c)
|
||||
newtons_method_graph(i-1, fn, a, b, c; label=true)
|
||||
end
|
||||
|
||||
imgfile = tempname() * ".gif"
|
||||
|
@ -76,7 +76,7 @@ ImageFile(imgfile, caption)
|
||||
## The secant line and the tangent line
|
||||
|
||||
|
||||
We approach this general problem **much** more indirectly than is needed. We introducing notations that are attributed to Newton and proceed from there. By leveraging `SymPy` we avoid tedious computations and *hopefully* gain some insight.
|
||||
We approach this general problem **much** more indirectly than is needed. We introduce notations that are attributed to Newton and proceed from there. By leveraging `SymPy` we avoid tedious computations and *hopefully* gain some insight.
|
||||
|
||||
|
||||
Suppose $f(x)$ is a function which is defined in a neighborhood of $c$ and has as many continuous derivatives as we care to take at $c$.
|
||||
@ -102,7 +102,7 @@ $$
|
||||
tl(x) = f(c) + f'(c) \cdot(x - c).
|
||||
$$
|
||||
|
||||
The key is the term multiplying $(x-c)$ for the secant line is an approximation to the related term for the tangent line. That is, the secant line approximates the tangent line, which is the linear function that best approximates the function at the point $(c, f(c))$. This is quantified by the *mean value theorem* which states under our assumptions on $f(x)$ that there exists some $\xi$ between $x$ and $c$ for which:
|
||||
The key is the term multiplying $(x-c)$ for the secant line this is an approximation to the related term for the tangent line. That is, the secant line approximates the tangent line, which is the linear function that best approximates the function at the point $(c, f(c))$. This is quantified by the *mean value theorem* which states under our assumptions on $f(x)$ that there exists some $\xi$ between $x$ and $c$ for which:
|
||||
|
||||
|
||||
$$
|
||||
@ -153,7 +153,7 @@ As in the linear case, there is flexibility in the exact points chosen for the i
|
||||
---
|
||||
|
||||
|
||||
Now, we take a small detour to define some notation. Instead of writing our two points as $c$ and $c+h,$ we use $x_0$ and $x_1$. For any set of points $x_0, x_1, \dots, x_n$, define the **divided differences** of $f$ inductively, as follows:
|
||||
Now, we take a small detour to define some notation. Instead of writing our two points as $c$ and $c+h,$ we use $x_0$ and $x_1$. For any set of points $x_0, x_1, \dots, x_n$, recursively define the Newton **divided differences** of $f$ inductively, as follows:
|
||||
|
||||
$$
|
||||
\begin{align*}
|
||||
|
@ -13,4 +13,5 @@ PlotlyKaleido = "f2990250-8cf9-495f-b13a-cce12b45703c"
|
||||
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
|
||||
QuadGK = "1fd47b50-473d-5c70-9696-f719f8f3bcdc"
|
||||
Roots = "f2b01f46-fcfa-551c-844a-d8ac1e96c665"
|
||||
ScatteredInterpolation = "3f865c0f-6dca-5f4d-999b-29fe1e7e3c92"
|
||||
SymPy = "24249f21-da20-56a4-8eb1-6a02cf4ae2e6"
|
||||
|
@ -15,6 +15,7 @@ using SymPy
|
||||
using Roots
|
||||
using QuadGK
|
||||
using JSON
|
||||
using ScatteredInterpolation
|
||||
```
|
||||
|
||||
Also, these methods from the `Contour` package:
|
||||
@ -530,6 +531,91 @@ contourf(xs, ys, f)
|
||||
|
||||
This function has a prominent peak and a prominent valley, around the middle of the viewing window. The nested contour lines indicate this, and the color key can be used to identify which is the peak and which the valley.
|
||||
|
||||
##### Example
|
||||
|
||||
The description of a function for the contour function is in terms of a grid of $x-y$ values and a function $f$ which gives the height, $z$. In other situations, it might make more sense to have a stream of $x-y-z$ values describing a surface. This might be the case, say, with trying to piece together a topography using a series of GPS track. To do so, one way is to take a regular grid of points and then *interpolate* $z$ values from the existing values.
|
||||
|
||||
The `ScatteredInterpolation.jl` package can be used to create a structure that can be used to interpolate points. The necessary pieces are the points, the sampled heights, and a method for the interpolation.
|
||||
|
||||
|
||||
A simple example follows (inspired by a [discourse post](https://discourse.julialang.org/t/plots-contourf-and-plotlyjs-contour-behaviour-with-regard-to-the-x-y-z-input/122897/2)) where the true surface is known so that a comparison can be made is given. The two figures show the contour described by 4 paths through the space, is not as detailed but captures the general shape reasonably well.
|
||||
|
||||
```{julia}
|
||||
f(x,y) = 3*(1-x)^2*exp(-(x^2) - (y+1)^2) -
|
||||
10*(x/5 - x^3 - y^5)*exp(-x^2-y^2) -
|
||||
1/3*exp(-(x+1)^2 - y^2)
|
||||
|
||||
r(t, a=2) = [a*cbrt(sinpi(t)), a * cbrt(cospi(t))]
|
||||
ts = range(0, 2, 30)[1:end-1]
|
||||
pts = vcat([[r(t,a) for t in ts] for a in [1/2, 1, 3/2, 2]]...)
|
||||
samples = [f(pt...) for pt in pts]
|
||||
first(zip(pts, samples),5)
|
||||
```
|
||||
|
||||
```{julia}
|
||||
using ScatteredInterpolation
|
||||
itp = interpolate(Multiquadratic(), stack(pts), samples)
|
||||
|
||||
# make a grid
|
||||
(xm,xM), (ym,yM) = extrema.(eachrow(stack(pts)))
|
||||
n, m = 25, 40
|
||||
xg, yg = range(xm,xM,n), range(ym, yM, m)
|
||||
X = [s for t in yg, s in xg] #size(X) is (m,n)
|
||||
Y = [t for t in yg, s in xg] # size(Y) is also (m,n)
|
||||
gridP = stack(vec([[x, y] for (x,y) in zip(X, Y)]))
|
||||
gridP = stack(vec([[x, y] for (x,y) in zip(X, Y)])) #2 x m*n - matrix; each column is a grid point
|
||||
interpolated = evaluate(itp, gridP)
|
||||
zg = reshape(interpolated, m, n)
|
||||
|
||||
p = Plots.contourf(xg, yg, zg; levels=6)
|
||||
q = Plots.contourf(xg, yg, f)
|
||||
plot(p,q)
|
||||
```
|
||||
|
||||
##### Example
|
||||
|
||||
```{julia}
|
||||
using Plots
|
||||
```
|
||||
##### Example
|
||||
|
||||
The arrangement of the data in a heatmap or contour plot depends on the underlying plotting package. A [discourse post](https://discourse.julialang.org/t/wrong-heatmap-orientation-with-plots-jl/124822/9) used this example to illustrate.
|
||||
|
||||
The data is a matrix
|
||||
|
||||
```{julia}
|
||||
xy = [1 2; 3 4]
|
||||
```
|
||||
|
||||
which has these colors mapped to their values:
|
||||
|
||||
```{julia}
|
||||
cmap =[:red, :green, :blue, :orange]
|
||||
cmap[xy]
|
||||
```
|
||||
|
||||
@fig-plots-makie-heatmap shows on the left the image created by this command in `Plots`, and on the right the image created with the same command using `Makie`:
|
||||
|
||||
|
||||
|
||||
```{julia}
|
||||
#| eval: false
|
||||
heatmap(xy; colormap = cols, title="Plots", legend=false)
|
||||
```
|
||||
|
||||
```{julia}
|
||||
#| echo: false
|
||||
#| layout-ncol: 2
|
||||
#| label: fig-plots-makie-heatmap
|
||||
#| fig-cap: "Orientation of heatmap may vary by plotting package."
|
||||
p = heatmap(xy; colormap = cmap, title="Plots",
|
||||
legend=false)
|
||||
q = heatmap(xy'; colormap = cmap, title="Makie",
|
||||
legend=false)
|
||||
plot(p,q,layout=(1,2))
|
||||
```
|
||||
|
||||
`Makie` uses the first dimension for `x` and the second for `y` (the first dimension is down the columns, then across); and `Plots` plots `x` values on the `x` axis etc. with values rising upwards and towards the left.
|
||||
|
||||
## Limits
|
||||
|
||||
|
@ -224,6 +224,208 @@ The two dimensions are different so for each value of `xs` the vector of `ys` is
|
||||
|
||||
At times using the "apply" notation: `x |> f`, in place of using `f(x)` is useful, as it can move the wrapping function to the right of the expression. To broadcast, `.|>` is available.
|
||||
|
||||
## Aside: simplifying calculations using containers and higher-order functions
|
||||
|
||||
Storing data in a container, be it a vector or a tuple, can seem at first more complicated, but in fact can lead to much simpler computations.
|
||||
|
||||
A simple example might be to add up a sequence of numbers. A direct way might be:
|
||||
|
||||
```{julia}
|
||||
x1, x2, x3, x4, x5, x6 = 1, 2, 3, 4, 5, 6
|
||||
x1 + x2 + x3 + x4 + x5 + x6
|
||||
```
|
||||
|
||||
Someone doesn't need to know `Julia`'s syntax to guess what this computes, save for the idiosyncratic tuple assignment used, which could have been bypassed at the cost of even more typing.
|
||||
|
||||
A more efficient means to do, as each componenent isn't named, this would be to store the data in a container:
|
||||
|
||||
```{julia}
|
||||
xs = [1, 2, 3, 4, 5, 6] # as a vector
|
||||
sum(xs)
|
||||
```
|
||||
|
||||
Sometimes tuples are used for containers. The difference to `sum` is not noticeable (though a different code path for the computation is taken behind the scenes):
|
||||
|
||||
```{julia}
|
||||
xs = (1, 2, 3, 4, 5, 6)
|
||||
sum(xs)
|
||||
```
|
||||
|
||||
(Tuples and vectors are related, but tuples don't have built-in arithmetic defined. Several popular packages, such as `Plots`, draw a distinction between the two basic containers, but most generic functions just need to be able to iterate over the values.)
|
||||
|
||||
The `sum` function has a parallel `prod` function for finding the product of the entries:
|
||||
|
||||
```{julia}
|
||||
prod(xs)
|
||||
```
|
||||
|
||||
Both `sum` and `prod` will error if the container is empty.
|
||||
|
||||
These two functions are *reductions*. There are others, such as `maximum` and `minimum`. They reduce the dimensionality of the container, in this case from a vector to a scalar. When applied to higher-dimensional containers, dimenensions to reduce over are specified. The higher-order `reduce` function can be used as a near alternate to `sum`:
|
||||
|
||||
```{julia}
|
||||
reduce(+, xs; init=0) # sum(xs)
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```{julia}
|
||||
reduce(*, xs; init=1) # prod(xs)
|
||||
```
|
||||
|
||||
The functions (`+` and `*`) are binary operators and are serially passed the running value (or `init`) and the new term from the iterator.
|
||||
|
||||
The initial value above is the unit for the operation (which could be found programatically by `zero(eltype(xs))` or `one(eltype(xs))` where the type is useful for better performance).
|
||||
|
||||
The `foldl` and `foldr` functions are similar to `reduce` only left (and right) associativity is guaranteed. This example uses the binary, infix `Pair` operator, `=>`, to illustrate the difference:
|
||||
|
||||
```{julia}
|
||||
foldl(=>, xs)
|
||||
```
|
||||
|
||||
and
|
||||
|
||||
```{julia}
|
||||
foldr(=>, xs)
|
||||
```
|
||||
|
||||
Next, we do a slighlty more complicated problem.
|
||||
|
||||
Recall the distance formula between two points, also called the *norm*. It is written here with the square root on the other side: $d^2 = (x_1-y_1)^2 + (x_0 - y_0)^2$. This computation can be usefully generalized to higher dimensional points (with $n$ components each).
|
||||
|
||||
This first example shows how the value for $d^2$ can be found using broadcasting and `sum`:
|
||||
|
||||
```{julia}
|
||||
xs = [1, 2, 3, 4, 5]
|
||||
ys = [1, 3, 5, 7, 3]
|
||||
|
||||
sum((xs - ys).^2)
|
||||
```
|
||||
|
||||
|
||||
This formula is a sum after applying an operation to the paired off values. Using a geneator that sum would look like:
|
||||
|
||||
```{julia}
|
||||
sum((xi - yi)^2 for (xi, yi) in zip(xs, ys))
|
||||
```
|
||||
|
||||
The `zip` function, used above, produces an iterator over tuples of the paired off values in the two (or more) containers passed to it.
|
||||
|
||||
|
||||
This pattern -- where a reduction follows a function's application to the components -- is implemented in `mapreduce`.
|
||||
|
||||
|
||||
```{julia}
|
||||
f(xy) = (xy[1] - xy[2])^2
|
||||
mapreduce(f, +, zip(xs, ys))
|
||||
```
|
||||
|
||||
In the generator example above, the components of the tuple are destructured into `(xi, yi)`; in the function `f` above $1$-based indexing is used to access the first and second components.
|
||||
|
||||
The `mapreduce` function can take more than one iterator to reduce over, When used this way, the function takes multiple arguments. Unlike the above example, where `f` was first defined and then used, we use an anonymous function below, to make the example a one-liner:
|
||||
|
||||
```{julia}
|
||||
mapreduce((xi,yi) -> (xi-yi)^2, +, xs, ys)
|
||||
```
|
||||
|
||||
|
||||
(The `mapreduce` form is more performant than broadcasting where the vectors are traversed more times.)
|
||||
|
||||
|
||||
### Extracting pieces of a container
|
||||
|
||||
At times, extracting all but the first or last value can be of interest. For example, a polygon comprised of $n$ points (the vertices), might be stored using a vector for the $x$ and $y$ values with an additional point that mirrors the first. Here are the points:
|
||||
|
||||
```{julia}
|
||||
xs = [1, 3, 4, 2]
|
||||
ys = [1, 1, 2, 3]
|
||||
pts = zip(xs, ys) # recipe for [(x1,y1), (x2,y2), (x3,y3), (x4,y4)]
|
||||
```
|
||||
|
||||
To *add* the additional point we might just use `push!`:
|
||||
|
||||
```{julia}
|
||||
push!(xs, first(xs))
|
||||
push!(ys, first(ys))
|
||||
```
|
||||
|
||||
(Though this approach won't work with `pts`, only with mutable containers.)
|
||||
|
||||
The `first` and `last` methods refer to the specific elements in the indexed collection. To get the rest of the values can be done a few ways. For example, this pattern peels off the first and leaves a new container to hold the rest:
|
||||
|
||||
```{julia}
|
||||
a, bs... = xs
|
||||
a, bs
|
||||
```
|
||||
|
||||
The splatting operation for `bs....` is usually seen inside a function, so this is a bit unusual. The `Iterators.peel` method also can also do this task (the `Iterators` module and its methods are not exported, so `peel` is necessarily qualified if not imported):
|
||||
|
||||
```{julia}
|
||||
a, bs = Iterators.peel(xs)
|
||||
bs
|
||||
```
|
||||
|
||||
|
||||
The `bs` are represented with an iterator and can be collected to yield the values, though often this is unecessary and possibly a costly step:
|
||||
|
||||
```{julia}
|
||||
collect(bs), sum(bs)
|
||||
```
|
||||
|
||||
The iterators shown here are *lazy* and only construct a recipe to produce the points one after another. Reductions like `sum` and `prod` can use this recipe to produce an answer without needing to realize in memory at one time the entire collection of values being represented.
|
||||
|
||||
The `Iterators.rest` method can be used to take the rest of the container starting a given index. This command also finds the `bs` above:
|
||||
|
||||
```{julia}
|
||||
bs = Iterators.rest(xs, 2)
|
||||
collect(bs)
|
||||
```
|
||||
|
||||
The `Iterators.take` method can be used to take values at the beginning of a container. This command takes all but the last value of `xs`:
|
||||
|
||||
```{julia}
|
||||
as = Iterators.take(xs, length(xs) - 1)
|
||||
collect(as)
|
||||
```
|
||||
|
||||
The `take` method could be used to remove the padded value from the `xs` and `ys`. Between `Iterators.take` and `Iterators.rest` the iterable object can be split into a head and tail.
|
||||
|
||||
##### Example: Riemann sums
|
||||
|
||||
In the computation of a Riemann sum, the interval $[a,b]$ is partitioned using $n+1$ points $a=x_0 < x_1 < \cdots < x_{n-1} < x_n = b$.
|
||||
|
||||
```{julia}
|
||||
a, b, n = 0, 1, 4
|
||||
xs = range(a, b, n+1) # n + 1 points gives n subintervals [xᵢ, xᵢ₊₁]
|
||||
```
|
||||
|
||||
To grab these points as adjacent pairs can be done by combining the first $n$ points and the last $n$ points, as follows:
|
||||
|
||||
```{julia}
|
||||
partitions = zip(Iterators.take(xs, n), Iterators.rest(xs, 1))
|
||||
collect(partitions)
|
||||
```
|
||||
|
||||
A left-hand Riemann sum for `f` could then be done with:
|
||||
|
||||
```{julia}
|
||||
f(x) = x^2
|
||||
sum(f ∘ first, partitions)
|
||||
```
|
||||
|
||||
This uses a few things: like `mapreduce`, `sum` allows a function to
|
||||
be applied to each element in the `partitions` collection. (Indeed, the default method to compute `sum(xs)` for an arbitrary container resolves to `mapreduce(identity, add_sum, xs)` where `add_sum` is basically `+`.)
|
||||
|
||||
In this case, the
|
||||
values come as tuples to the function to apply to each component.
|
||||
The function above uses `first` to find the left-endpoint value and then calls `f`. The composition (through `∘`) implements this.
|
||||
|
||||
Alternatively, `zip` can be avoided with:
|
||||
|
||||
```{julia}
|
||||
mapreduce((l, r) -> l^2, +, Iterators.take(xs, n), Iterators.rest(xs, 1))
|
||||
```
|
||||
|
||||
|
||||
## The dot product
|
||||
|
||||
|
@ -3,6 +3,7 @@ CalculusWithJulia = "a2e0e22d-7d4c-5312-9169-8b992201a882"
|
||||
ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210"
|
||||
HCubature = "19dc6840-f33b-545b-b366-655c7e3ffd49"
|
||||
IJulia = "7073ff75-c697-5162-941a-fcdaad2a7d2a"
|
||||
ImplicitIntegration = "bc256489-3a69-4a66-afc4-127cc87e6182"
|
||||
PlotlyBase = "a03496cd-edff-5a9b-9e67-9cda94a718b5"
|
||||
PlotlyKaleido = "f2990250-8cf9-495f-b13a-cce12b45703c"
|
||||
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
|
||||
|
@ -13,6 +13,7 @@ plotly()
|
||||
using QuadGK
|
||||
using SymPy
|
||||
using HCubature
|
||||
import ImplicitIntegration
|
||||
```
|
||||
|
||||
---
|
||||
@ -786,6 +787,120 @@ Compare to
|
||||
sin(1)/2
|
||||
```
|
||||
|
||||
### Integrating over implicitly defined regions
|
||||
|
||||
To use `HCubature` to find an integral over some region, that region is transformed into a rectanguler region and the Jacobian is used to modify the integrand. The `ImplicitIntegration` package allows the region to be implicitly defined (it need not be rectangular) and uses an algorithm to integrate over the region as given. It can integrate regions of the form $\phi(x) \leq 0$, that is it computes:
|
||||
|
||||
$$
|
||||
\iint_{(x,y): \phi(x,y) \leq 0} f(x,y) dx dy.
|
||||
$$
|
||||
|
||||
It can also integrate over over boundaries of the form $\phi(x) = 0$. The latter can be visualized through `implicit_plot`.
|
||||
|
||||
The main function from `ImplicitIntegration` is `integrate`. The package is imported below to avoid naming conflicts with `SymPy`'s `integrate` function:
|
||||
|
||||
```{julia}
|
||||
import ImplicitIntegration
|
||||
```
|
||||
|
||||
The unit circle (with radius $r=1$) can be parameterized with:
|
||||
|
||||
```{julia}
|
||||
r = 1.0
|
||||
phi(x) = sqrt(sum(xi^2 for xi in x)) - r
|
||||
x0s, x1s = (-1.0, -1.0), (1.0, 1.0)
|
||||
```
|
||||
|
||||
When a point is inside the disk centered at the origin of radius $r$, $\phi$ will be negative. The `phi` function takes a container describing a point.
|
||||
|
||||
|
||||
We can visualize this region, with `plot_implicit`, though we need to make a function that takes two arguments to specify $x$ and $y$ and
|
||||
rework the specification of the viewing window, as `ImplicitEquations.integrate` expects limits to be specified in a manner that readily accommodates higher dimensions, and the plotting function uses a more mathematical specification.
|
||||
|
||||
```{julia}
|
||||
𝑖(xs...) = xs # turn (x,y) arguments into a container
|
||||
xlims, ylims = collect(zip(x0s, x1s))
|
||||
|
||||
implicit_plot(phi∘𝑖; xlims, ylims, legend=false)
|
||||
scatter!([pt for pt ∈ tuple.(range(xlims..., 40), range(ylims...,40)')
|
||||
if phi(pt) < 0],
|
||||
marker=(1,:blue))
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
The area of the unit circle is identified by integrating a function that is constantly $1$ over the region. This is computed by:
|
||||
|
||||
```{julia}
|
||||
res = ImplicitIntegration.integrate(x -> 1.0, phi, x0s, x1s)
|
||||
```
|
||||
|
||||
The result, `res`, is a structure containing details of the algorithm. The `val` property contains the result.
|
||||
|
||||
This compares, approximately, the computed value to the known value ($\pi \cdot r^2$):
|
||||
|
||||
```{julia}
|
||||
res.val ≈ pi * r^2
|
||||
```
|
||||
|
||||
To find the *perimeter* we use the fact that it is the surface of the disc:
|
||||
|
||||
```{julia}
|
||||
res = ImplicitIntegration.integrate(x -> 1.0, phi, x0s, x1s; surface=true)
|
||||
res.val ≈ 2pi * r
|
||||
```
|
||||
|
||||
For two-dimensional regions, this provides an alternate means to calculate arc-lengths.
|
||||
|
||||
|
||||
Extending the above, we can find the surface area of the upper hemisphere of the unit sphere. To do this, we integrate a different function over $\phi$, one that describes the sphere. This being the following
|
||||
|
||||
```{julia}
|
||||
f(x) = sqrt(sum(xi^2 for xi in x)) # or LinearAlgebra.norm
|
||||
res = ImplicitIntegration.integrate(f, phi, x0s, x1s; surface=true)
|
||||
res.val ≈ (1/2) * 4pi * r^2
|
||||
```
|
||||
|
||||
The volume would be similarly done, only without the `surface` call:
|
||||
|
||||
|
||||
```{julia}
|
||||
f(x) = sqrt(sum(xi^2 for xi in x))
|
||||
res = ImplicitIntegration.integrate(f, phi, x0s, x1s;)
|
||||
res.val ≈ (1/2) * 4/3 * pi * r^2
|
||||
```
|
||||
|
||||
Of course more complicated functions could be used.
|
||||
|
||||
Now consider a more complicated region over $[-2\pi, 2\pi] \times [-2\pi, 2\pi]$:
|
||||
|
||||
```{julia}
|
||||
function phi(x)
|
||||
x1,x2 = x
|
||||
x1*cos(x2)*cos(x1*x2) + x2*cos(x1)*cos(x1*x2) + x1*x2*cos(x1)*cos(x2)
|
||||
end
|
||||
x0s, x1s = (-2pi, -2pi), (2pi, 2pi)
|
||||
|
||||
xlims, ylims = collect(zip(x0s, x1s))
|
||||
implicit_plot(phi∘𝑖; xlims, ylims, legend=false)
|
||||
scatter!([pt for pt ∈ tuple.(range(xlims..., 40), range(ylims...,40)')
|
||||
if phi(pt) < 0],
|
||||
marker=(1,:blue))
|
||||
```
|
||||
|
||||
In a slightly tricky way, a grid of points is created to indicate where `phi` is negative.
|
||||
|
||||
The area of the negative part of this function can be found by integrating the constant function with value $1$:
|
||||
|
||||
```{julia}
|
||||
res = ImplicitIntegration.integrate(x -> 1.0, phi, x0s, x1s)
|
||||
(val=res.val, proportion=res.val / (4pi)^2)
|
||||
```
|
||||
|
||||
|
||||
## Triple integrals
|
||||
|
||||
|
||||
@ -1678,9 +1793,9 @@ The Jacobian can be computed to be $\rho^2\sin(\phi)$.
|
||||
|
||||
```{julia}
|
||||
#| hold: true
|
||||
@syms ρ theta phi
|
||||
G(ρ, theta, phi) = ρ * [sin(phi)*cos(theta), sin(phi)*sin(theta), cos(phi)]
|
||||
det(G(ρ, theta, phi).jacobian([ρ, theta, phi])) |> simplify |> abs
|
||||
@syms ρ θ ϕ
|
||||
G(ρ, θ, ϕ) = ρ * [sin(ϕ)*cos(θ), sin(ϕ)*sin(θ), cos(ϕ)]
|
||||
det(G(ρ, θ, ϕ).jacobian([ρ, θ, ϕ])) |> simplify |> abs
|
||||
```
|
||||
|
||||
##### Example
|
||||
|
@ -26,12 +26,14 @@ $$
|
||||
\int_a^b (f(x) - g(x)) dx
|
||||
$$
|
||||
|
||||
can be interpreted as the "signed" area between $f(x)$ and $g(x)$ over $[a,b]$. If on this interval $[a,b]$ it is true that $f(x) \geq g(x)$, then this would just be the area, as seen in this figure. The rectangle in the figure has area: $(f(a)-g(a)) \cdot (b-a)$ which could be a term in a left Riemann sum of the integral of $f(x) - g(x)$:
|
||||
can be interpreted as the "signed" area between $f(x)$ and $g(x)$ over $[a,b]$. If on this interval $[a,b]$ it is true that $f(x) \geq g(x)$, then this would just be the area, as seen in this figure. The rectangle in the figure has area: $(f(x_i)-g(x_i)) \cdot (x_{i+1}-x_i)$ for some $x_i, x_{i+1}$ suggestive of a term in a left Riemann sum of the integral of $f(x) - g(x)$:
|
||||
|
||||
|
||||
```{julia}
|
||||
#| hold: true
|
||||
#| echo: false
|
||||
#| label: fig-area-between-f-g
|
||||
#| fig-cap: "Area between two functions"
|
||||
f1(x) = x^2
|
||||
g1(x) = sqrt(x)
|
||||
a,b = 1/4, 3/4
|
||||
@ -42,9 +44,9 @@ ts = vcat(f1.(xs), g1.(reverse(xs)))
|
||||
|
||||
plot(f1, 0, 1, legend=false)
|
||||
plot!(g1)
|
||||
plot!(ss, ts, fill=(0, :red))
|
||||
plot!(xs, f1.(xs), linewidth=5, color=:green)
|
||||
plot!(xs, g1.(xs), linewidth=5, color=:green)
|
||||
plot!(ss, ts, fill=(0, :forestgreen, 0.25))
|
||||
plot!(xs, f1.(xs), linewidth=5, color=:royalblue)
|
||||
plot!(xs, g1.(xs), linewidth=5, color=:royalblue)
|
||||
|
||||
|
||||
plot!(xs, f1.(xs), legend=false, linewidth=5, color=:blue)
|
||||
@ -53,7 +55,7 @@ u,v = .4, .5
|
||||
plot!([u,v,v,u,u], [f1(u), f1(u), g1(u), g1(u), f1(u)], color=:black, linewidth=3)
|
||||
```
|
||||
|
||||
For the figure, we have $f(x) = \sqrt{x}$, $g(x)= x^2$ and $[a,b] = [1/4, 3/4]$. The shaded area is then found by:
|
||||
In @fig-area-between-f-g we have $f(x) = \sqrt{x}$, $g(x)= x^2$ and $[a,b] = [1/4, 3/4]$. The shaded area is then found by:
|
||||
|
||||
|
||||
$$
|
||||
@ -156,12 +158,14 @@ summation(1/(n+1)/(n+2), (n, 1, oo))
|
||||
##### Example
|
||||
|
||||
|
||||
Verify [Archimedes'](http://en.wikipedia.org/wiki/The_Quadrature_of_the_Parabola) finding that the area of the parabolic segment is $4/3$rds that of the triangle joining $a$, $(a+b)/2$ and $b$.
|
||||
Verify [Archimedes'](http://en.wikipedia.org/wiki/The_Quadrature_of_the_Parabola) finding that the area of the parabolic segment is $4/3$rds that of the triangle joining $a$, $(a+b)/2$ and $b$. @fig-area-between-f-g clearly shows the bigger parabolic segment area.
|
||||
|
||||
|
||||
```{julia}
|
||||
#| hold: true
|
||||
#| echo: false
|
||||
#| label: fig-archimedes-triangle
|
||||
#| fig-cap: "Area of parabolic segment and triangle"
|
||||
f(x) = 2 - x^2
|
||||
a,b = -1, 1/2
|
||||
c = (a + b)/2
|
||||
@ -169,10 +173,15 @@ xs = range(-sqrt(2), stop=sqrt(2), length=50)
|
||||
rxs = range(a, stop=b, length=50)
|
||||
rys = map(f, rxs)
|
||||
|
||||
|
||||
plot(f, a, b, legend=false, linewidth=3)
|
||||
plot(f, a, b, legend=false,
|
||||
line=(3, :royalblue),
|
||||
axis=([], false)
|
||||
)
|
||||
plot!([a,b], [f(a),f(b)], line=(3, :royalblue))
|
||||
xs = [a,c,b,a]
|
||||
plot!(xs, f.(xs), linewidth=3)
|
||||
triangle = Shape(xs, f.(xs))
|
||||
plot!(triangle, fill=(:forestgreen, 3, 0.25))
|
||||
|
||||
```
|
||||
|
||||
For concreteness, let $f(x) = 2-x^2$ and $[a,b] = [-1, 1/2]$, as in the figure. Then the area of the triangle can be computed through:
|
||||
@ -515,6 +524,81 @@ a, b = 0, 1
|
||||
quadgk(y -> f(y) - g(y), a, b)[1]
|
||||
```
|
||||
|
||||
## The area enclosed in a simple polygon
|
||||
|
||||
A simple polygon is comprised of several non-intersecting line segments, save for the last segment ends where the first begins. These have an orientation, which we take to be counterclockwise. Polygons, as was seen when computing areas related to Archimedes efforts, can be partioned into simple geometric shapes, for which known areas apply.
|
||||
|
||||
### The trapezoid formula
|
||||
|
||||
In this example, we see how trapezoids can be used to find the interior area encolosed by a simply polygon, avoiding integration.
|
||||
|
||||
The trapezoid formula to compute the area of a simple polygon is
|
||||
|
||||
$$
|
||||
A = - \sum_{i=1}^n \frac{y_{i+1} + y_i}{2} \cdot (x_{i+1} - x_i).
|
||||
$$
|
||||
|
||||
Where the polygon is described by points $(x_1,y_1), (x_2,y_2), \cdots, (x_n, y_n), (x_{n+1}, y_{n+1})$ *with* $(x_1,y_1) = (x_{n+1}, y_{n+1})$.
|
||||
|
||||
Each term describes the area of a trapezoid, possibly signed.
|
||||
|
||||
This figure illustrates for a simple case:
|
||||
|
||||
```{julia}
|
||||
using Plots
|
||||
xs = [1, 3, 4, 2, 1] # n = 4 to give 5=n+1 values
|
||||
ys = [1, 1, 2, 3, 1]
|
||||
p = plot(xs, ys; line=(3, :black), ylims=(0,4), legend=false)
|
||||
scatter!(p, xs, ys; marker=(7, :circle))
|
||||
```
|
||||
|
||||
Going further, we draw the four trapezoids using different colors depending on the sign of the `xs[i+1] - xs[[i]` terms:
|
||||
|
||||
```{julia}
|
||||
for i in 1:4
|
||||
col = xs[i+1] - xs[i] > 0 ? :yellow : :blue
|
||||
S = Shape([(xs[i],0), (xs[i+1],0), (xs[i+1],ys[i+1]), (xs[i], ys[i])])
|
||||
plot!(p, S, fill=(col, 0.25))
|
||||
end
|
||||
p
|
||||
```
|
||||
|
||||
The yellow trapezoids appear to be colored green, as they completely overlap with parts of the blue trapezoids and blue and yellow make green. As the signs of the differences of the $x$ values is different, these areas add to $0$ in the sum, leaving just the area of the interior when the sum is computed.
|
||||
|
||||
For this particular figure, the enclosed area is
|
||||
|
||||
```{julia}
|
||||
- sum((ys[i+1] + ys[i]) / 2 * (xs[i+1] - xs[i]) for i in 1:length(xs)-1)
|
||||
```
|
||||
|
||||
### The triangle formula
|
||||
|
||||
Similarly, we can create triangles to partition the polygon. The *signed* area of a triangle with vertices $(0,0), (x_i, y_i), (x_{i+1}, y_{i+1})$ can be computed by $\frac{1}{2} \cdot (x_i \cdot y_{i+1} - x_{i+1}\cdot y_i)$. (A formula that can be derived from a related one for the area of a parallelogram.
|
||||
|
||||
Visualizing, as before, we have the shape and the triangles after centering around the origin:
|
||||
|
||||
```{julia}
|
||||
S = Shape(xs, ys)
|
||||
c = Plots.center(S) # find centroid of the polygon
|
||||
xs, ys = xs .- c[1], ys .- c[2]
|
||||
|
||||
p = plot(xs, ys; line=(3, :black), legend=false)
|
||||
scatter!(p, xs, ys; marker=(7, :circle))
|
||||
for i in 1:4
|
||||
col = xs[i]*ys[i+1] - xs[i+1]*ys[i] > 0 ? :yellow : :blue
|
||||
S = Shape([(0,0), (xs[i],ys[i]), (xs[i+1],ys[i+1])])
|
||||
plot!(p, S, fill=(col, 0.25))
|
||||
end
|
||||
p
|
||||
```
|
||||
|
||||
Here the triangles are all yellow, as each has a positive area to contribute to the following sum:
|
||||
|
||||
```{julia}
|
||||
(1/2) * sum(xs[i]*ys[i+1] - xs[i+1]*ys[i] for i in 1:4)
|
||||
```
|
||||
|
||||
|
||||
## Questions
|
||||
|
||||
|
||||
@ -833,3 +917,56 @@ nothing
|
||||
|
||||

|
||||
|
||||
|
||||
###### Question
|
||||
|
||||
```{julia}
|
||||
#| echo: false
|
||||
#| label: fig-cavalieri-example
|
||||
#| fig-cap: "Cavalieri example"
|
||||
let
|
||||
squareplus(x, b=2) = x/2 + sqrt(x^2 + b)/2
|
||||
TeLU(x) = x * tanh(exp(x))
|
||||
Δ(x) = squareplus(x) - TeLU(x)
|
||||
|
||||
a,b = -3, 3
|
||||
xs = range(a, b, 10)
|
||||
|
||||
c = 3
|
||||
Shift(f,c) = x -> c + f(x)
|
||||
p = plot(Shift(squareplus, c), a, b;
|
||||
legend=false,
|
||||
line=(3, :royalblue),
|
||||
axis=([], false))
|
||||
plot!(Shift(TeLU, c),
|
||||
line=(3, :royalblue)
|
||||
)
|
||||
|
||||
|
||||
plot!(Δ, line=(3, :forestgreen))
|
||||
plot!(zero, line=(3, :forestgreen))
|
||||
|
||||
n = 20
|
||||
xs = range(a, b, n+1)
|
||||
for i in 1:n
|
||||
S = Shape([xs[i],xs[i]],
|
||||
[0, Δ(xs[i])])
|
||||
plot!(Plots.translate(S, 0, TeLU(xs[i]) + c), fill=(:royalblue, 0.25))
|
||||
plot!(S, fill=(:forestgreen, 0.25))
|
||||
end
|
||||
p
|
||||
end
|
||||
```
|
||||
|
||||
@fig-cavalieri-example shows on same scale the graphs of $f(x)$ and $g(x)$ and the graphs of $f(x) - g(x)$ and $0$ (the lower figure). Twenty lines were drawn with height $f(x) - g(x)$ on the lower figure and these were translated to the upper figure by an amount $g(x)$. All to illustrate that any parallel line in the $y$ direction intersects the two figures with the same length.
|
||||
|
||||
What does this imply:
|
||||
|
||||
```{julia}
|
||||
#| echo: false
|
||||
choices = ["The two enclosed areas should be equal",
|
||||
"The two enclosed areas are clearly different, as they do not overap"],
|
||||
radioq(choices, 1)
|
||||
```
|
||||
"
|
||||
|
@ -27,56 +27,90 @@ So far we have seen that the *derivative* rules lead to *integration rules*. In
|
||||
|
||||
|
||||
* The sum rule $[au(x) + bv(x)]' = au'(x) + bv'(x)$ gives rise to an integration rule: $\int (au(x) + bv(x))dx = a\int u(x)dx + b\int v(x))dx$. (That is, the linearity of the derivative means the integral has linearity.)
|
||||
|
||||
* The chain rule $[f(g(x))]' = f'(g(x)) g'(x)$ gives $\int_a^b f(g(x))g'(x)dx=\int_{g(a)}^{g(b)}f(x)dx$. That is, substitution reverses the chain rule.
|
||||
|
||||
|
||||
Now we turn our attention to the implications of the *product rule*: $[uv]' = u'v + uv'$. The resulting technique is called integration by parts.
|
||||
|
||||
::: {.callout-note}
|
||||
## Integration by parts
|
||||
|
||||
The following illustrates integration by parts of the integral $(uv)'$ over $[a,b]$ [original](http://en.wikipedia.org/wiki/Integration_by_parts#Visualization).
|
||||
By the fundamental theorem of calculus:
|
||||
|
||||
$$
|
||||
[u(x)\cdot v(x)]\big|_a^b = \int_a^b [u(x) v(x)]' dx = \int_a^b u'(x) \cdot v(x) dx + \int_a^b u(x) \cdot v'(x) dx.
|
||||
$$
|
||||
|
||||
Or,
|
||||
|
||||
$$
|
||||
\int_a^b u(x) v'(x) dx = [u(x)v(x)]\big|_a^b - \int_a^b v(x) u'(x)dx.
|
||||
$$
|
||||
:::
|
||||
|
||||
|
||||
The following visually illustrates integration by parts:
|
||||
|
||||
|
||||
```{julia}
|
||||
#| echo: false
|
||||
#| label: fig-integration-by-parts
|
||||
#| fig-cap: "Integration by parts figure ([original](http://en.wikipedia.org/wiki/Integration_by_parts#Visualization))"
|
||||
let
|
||||
## parts picture
|
||||
u(x) = sin(x*pi/2)
|
||||
v(x) = x
|
||||
xs = range(0, stop=1, length=50)
|
||||
a,b = 1/4, 3/4
|
||||
p = plot(u, v, 0, 1, legend=false)
|
||||
plot!(p, zero, 0, 1)
|
||||
scatter!(p, [u(a), u(b)], [v(a), v(b)], color=:orange, markersize=5)
|
||||
|
||||
p = plot(u, v, 0, 1, legend=false, axis=([], false))
|
||||
plot!([0, u(1)], [0,0], line=(:black, 3))
|
||||
plot!([0, 0], [0, v(1) ], line=(:black, 3))
|
||||
plot!(p, zero, 0, 1)
|
||||
|
||||
xs′ = range(a, b, length=50)
|
||||
plot!(Shape(vcat(u.(xs′), reverse(u.(xs′))),
|
||||
vcat(zero.(xs′), v.(reverse(xs′)))),
|
||||
fill=(:red, 0.15),
|
||||
xlims=(-0.07, 1)
|
||||
)
|
||||
plot!(Shape([0,u(a),u(a),0],[0,0,v(a),v(a)]), fill=(:royalblue, 0.5))
|
||||
scatter!(p, [u(a), u(b)], [v(a), v(b)], color=:mediumorchid3, markersize=5)
|
||||
plot!(p, [u(a),u(a),0, 0, u(b),u(b),u(a)],
|
||||
[0, v(a), v(a), v(b), v(b), 0, 0],
|
||||
linetype=:polygon, fillcolor=:orange, alpha=0.25)
|
||||
annotate!(p, [(0.65, .25, "A"), (0.4, .55, "B")])
|
||||
annotate!(p, [(u(a),v(a) + .08, "(u(a),v(a))"), (u(b),v(b)+.08, "(u(b),v(b))")])
|
||||
linetype=:polygon, fill=(:brown3, 0.25))
|
||||
|
||||
annotate!(p, [(0.65, .25, "A"),
|
||||
(0.4, .55, "B"),
|
||||
(u(a),v(a) + .08, "(u(a),v(a))"),
|
||||
(u(b),v(b)+.08, "(u(b),v(b))"),
|
||||
(u(a),0, "u(a)",:top),
|
||||
(u(b),0, "u(b)",:top),
|
||||
(0, v(a), "v(a) ",:right),
|
||||
(0, v(b), "v(b) ",:right)
|
||||
])
|
||||
end
|
||||
```
|
||||
|
||||
The figure is a parametric plot of $(u,v)$ with the points $(u(a), v(a))$ and $(u(b), v(b))$ marked. The difference $u(b)v(b) - u(a)v(a) = u(x)v(x) \mid_a^b$ is shaded. This area breaks into two pieces, $A$ and $B$, partitioned by the curve. If $u$ is increasing and the curve is parameterized by $t \rightarrow u^{-1}(t)$, then $A=\int_{u^{-1}(a)}^{u^{-1}(b)} v(u^{-1}(t))dt$. A $u$-substitution with $t = u(x)$ changes this into the integral $\int_a^b v(x) u'(x) dx$. Similarly, for increasing $v$, it can be seen that $B=\int_a^b u(x) v'(x) dx$. This suggests a relationship between the integral of $u v'$, the integral of $u' v$ and the value $u(b)v(b) - u(a)v(a)$.
|
||||
@fig-integration-by-parts shows a parametric plot of $(u(t),v(t))$ for $a \leq t \leq b$..
|
||||
|
||||
The total shaded area, a rectangle, is $u(b)v(b)$, the area of $A$ and $B$ combined is just $u(b)v(b) - u(a)v(a)$ or $[u(x)v(x)]\big|_a^b$. We will show that that $A$ is $\int_a^b v(x)u'(x)dx$ and $B$ is $\int_a^b u(x)v'(x)dx$ giving the formula
|
||||
|
||||
In terms of formulas, by the fundamental theorem of calculus:
|
||||
|
||||
We can compute $A$ by a change of variables with $x=u^{-1}(t)$ (so $u'(x)dx = dt$):
|
||||
|
||||
$$
|
||||
u(x)\cdot v(x)\big|_a^b = \int_a^b [u(x) v(x)]' dx = \int_a^b u'(x) \cdot v(x) dx + \int_a^b u(x) \cdot v'(x) dx.
|
||||
\begin{align*}
|
||||
A &= \int_{u(a)}^{u(b)} v(u^{-1}(t)) dt & \text{let } x = u^{-1}(t) \text{ or }u(x) = t \\
|
||||
&= \int_{u^{-1}(u(a))}^{u^{-1}(u(b))} v(x) u'(x) dx \\
|
||||
&= \int_a^b v(x) u'(x) dx.
|
||||
\end{align*}
|
||||
$$
|
||||
|
||||
This is re-expressed as
|
||||
$B$ is similar with the roles of $u$ and $v$ reversed.
|
||||
|
||||
|
||||
$$
|
||||
\int_a^b u(x) \cdot v'(x) dx = u(x) \cdot v(x)\big|_a^b - \int_a^b v(x) \cdot u'(x) dx,
|
||||
$$
|
||||
|
||||
Or, more informally, as $\int udv = uv - \int v du$.
|
||||
|
||||
|
||||
This can sometimes be confusingly written as:
|
||||
Informally, the integration by parts formula is sometimes seen as $\int udv = uv - \int v du$, as well can be somewhat confusingly written as:
|
||||
|
||||
|
||||
$$
|
||||
@ -86,8 +120,7 @@ $$
|
||||
(The confusion coming from the fact that the indefinite integrals are only defined up to a constant.)
|
||||
|
||||
|
||||
How does this help? It allows us to differentiate parts of an integral in hopes it makes the result easier to integrate.
|
||||
|
||||
How does this formula help? It allows us to differentiate parts of an integral in hopes it makes the result easier to integrate.
|
||||
|
||||
An illustration can clarify.
|
||||
|
||||
|
@ -66,14 +66,18 @@ $$
|
||||
\text{average} = \frac{1}{\pi-0} \int_0^\pi \sin(x) dx = \frac{1}{\pi} (-\cos(x)) \big|_0^\pi = \frac{2}{\pi}
|
||||
$$
|
||||
|
||||
Visually, we have:
|
||||
Visually:
|
||||
|
||||
|
||||
```{julia}
|
||||
plot(sin, 0, pi)
|
||||
plot!(x -> 2/pi)
|
||||
#| label: fig-integral-mean-value
|
||||
#| fig-cap: "Area under sine curve is equal to area of rectangle"
|
||||
plot(sin, 0, pi, legend=false, fill=(:forestgreen, 0.25, 0))
|
||||
plot!(x -> 2/pi, fill=(:royalblue, 0.25, 0))
|
||||
```
|
||||
|
||||
In @fig-integral-mean-value the area under the sine curve ($2 = (-\cos(\pi)) - (-\cos(0))$) is equal to the area under the average (also $2 = 2/\pi \cdot \pi$).
|
||||
|
||||
##### Example
|
||||
|
||||
|
||||
@ -91,7 +95,7 @@ What is the average value of the function $e^{-x}$ between $0$ and $\log(2)$?
|
||||
|
||||
$$
|
||||
\begin{align*}
|
||||
\text{average} = \frac{1}{\log(2) - 0} \int_0^{\log(2)} e^{-x} dx\\
|
||||
\text{average} &= \frac{1}{\log(2) - 0} \int_0^{\log(2)} e^{-x} dx\\
|
||||
&= \frac{1}{\log(2)} (-e^{-x}) \big|_0^{\log(2)}\\
|
||||
&= -\frac{1}{\log(2)} (\frac{1}{2} - 1)\\
|
||||
&= \frac{1}{2\log(2)}.
|
||||
@ -103,8 +107,8 @@ Visualizing, we have
|
||||
|
||||
|
||||
```{julia}
|
||||
plot(x -> exp(-x), 0, log(2))
|
||||
plot!(x -> 1/(2*log(2)))
|
||||
plot(x -> exp(-x), 0, log(2), legend=false, fill=(:forestgreen, 0.25, 0))
|
||||
plot!(x -> 1/(2*log(2)), fill=(:royalblue, 0.25, 0))
|
||||
```
|
||||
|
||||
## The mean value theorem for integrals
|
||||
|
@ -213,7 +213,7 @@ surface(ws..., zlims=(-6,6), legend=false)
|
||||
plot!([0,0], [0,0], [-3,3], color=:red, linewidth=5) # y axis emphasis
|
||||
```
|
||||
|
||||
The `unzip` function is not part of base `Julia`, rather part of `CalculusWithJulia`. This function rearranges data into a form consumable by the plotting methods like `surface`. In this case, the result of `S.(us,vs')` is a grid (matrix) of points, the result of `unzip` is three grids of values, one for the $x$ values, one for the $y$ values, and one for the $z$ values. A manual adjustment to the `zlims` is used, as `aspect_ratio` does not have an effect with the `plotly()` backend and errors on 3d graphics with `pyplot()`.
|
||||
The `unzip` function is not part of base `Julia`, rather part of `CalculusWithJulia` (it is really `SplitApplyCombine`'s `invert` function). This function rearranges data into a form consumable by the plotting methods like `surface`. In this case, the result of `S.(us,vs')` is a grid (matrix) of points, the result of `unzip` is three grids of values, one for the $x$ values, one for the $y$ values, and one for the $z$ values. A manual adjustment to the `zlims` is used, as `aspect_ratio` does not have an effect with the `plotly()` backend and errors on 3d graphics with `pyplot()`.
|
||||
|
||||
|
||||
To rotate this about the $x$ axis, we have this pattern:
|
||||
|
@ -26,13 +26,16 @@ function make_plot(a₀=7/8, q₀=-a₀ - 1/2a₀)
|
||||
aspect_ratio=:equal, border=:none, legend=false)
|
||||
f(x) = x^2
|
||||
fp(x) = 2x
|
||||
plot!(f, -1.5, 1.5)
|
||||
plot!(zero)
|
||||
|
||||
plot!(f, -1.5, 1.5, line=(1, :royalblue))
|
||||
plot!(zero, line=(1, :black))
|
||||
|
||||
tl = x -> f(a₀) + fp(a₀) * (x-a₀)
|
||||
nl = x -> f(a₀) - 1/(fp(a₀)) * (x-a₀)
|
||||
plot!(tl, -0.02, 1.6; linecolor=:black)
|
||||
plot!(nl, -1.6, 1; linecolor=:black)
|
||||
|
||||
plot!(tl, -0.02, 1.6; line=(1, :forestgreen))
|
||||
plot!(nl, -1.6, 1; line=(1, :forestgreen))
|
||||
|
||||
# add in right triangle
|
||||
scatter!([a₀, q₀], f.([a₀, q₀]), markersize=5)
|
||||
Δ = 0.01
|
||||
|
@ -207,6 +207,7 @@ The authors note in a supplement to their paper that over 15,700 species and sub
|
||||
(3.02 * 10^15 + 1.34*10^15) * 100 / 22
|
||||
```
|
||||
|
||||
The answer is in *scientific notation$ and reads as $1.89\dots \cdot 10^16$.
|
||||
Shifting the decimal point, this gives a value rounded to $20\cdot 10^{15}$ ants.
|
||||
|
||||
The authors used a value for the *dry weight* of an average (and representative) single ant. What was that value? (Which they indicate is perhaps unreliable,
|
||||
@ -216,7 +217,7 @@ as, for example, small-bodied ants may be much more abundant than large-bodied a
|
||||
(12 * 1_000_000 * 1_000 * 1_000) / 20_000_000_000_000_000
|
||||
```
|
||||
|
||||
Which translates to an *average* dry *carbon* weight of $0.6/1000$ grams, that is $0.6$ milligrams ($0.62$ mg C was actually used).
|
||||
Which translates to an *average* dry *carbon* weight of $0.6/1000$ grams, that is $0.6$ milligrams ($0.62$ mg C was actually used). The underscores in `20_000_000_000_000_000` are *ignored* when parsed and are only for readability. This is a readable alternate to scientific notation for large numbers.
|
||||
|
||||
The authors write that insects are generally considered to have a dry weight of 30% wet weight, and a carbon weight of 50% dry weight, so the weight in grams of an *average* living ant would be multiplied by $2$ and then $10/3$:
|
||||
|
||||
@ -228,8 +229,6 @@ That is 4 milligrams, or 250 ants per gram on average.
|
||||
|
||||
Numeric combinations, as above, will be easier to check for correctness when variable names are assigned to the respective values.
|
||||
|
||||
Using the underscore, as above, to separate groups of digits, is helpful, as an alternate to scientific notation, when working with large numbers.
|
||||
|
||||
|
||||
|
||||
|
||||
@ -326,7 +325,7 @@ $$
|
||||
\frac{1 + 2}{3 + 4}?
|
||||
$$
|
||||
|
||||
It would have to be computed through $(1 + 2) / (3 + 4)$. This is because unlike `/`, the implied order of operation in the mathematical notation with the *horizontal division symbol* (the [vincula](http://tinyurl.com/y9tj6udl)) is to compute the top and the bottom and then divide. That is, the vincula is a grouping notation like parentheses, only implicitly so. Thus the above expression really represents the more verbose:
|
||||
It would have to be computed through $(1 + 2) / (3 + 4)$. This is because unlike `/`, the implied order of operation in the mathematical notation with the *horizontal division symbol* (the [vinculum](http://tinyurl.com/y9tj6udl)) is to compute the top and the bottom and then divide. That is, the vincula is a grouping notation like parentheses, only implicitly so. Thus the above expression really represents the more verbose:
|
||||
|
||||
|
||||
$$
|
||||
@ -368,7 +367,7 @@ In `Julia` many infix operations can be done using a prefix manner. For example
|
||||
## Constants
|
||||
|
||||
|
||||
The Google calculator has two built in constants, `e` and `π`. Julia provides these as well, though not quite as easily. First, `π` is just `pi`:
|
||||
The Google calculator has two built in constants, `e` and `π`. Julia provides these as well, though not quite as easily, as they have names and not dedicated buttons. First, `π` is just `pi`:
|
||||
|
||||
|
||||
```{julia}
|
||||
@ -407,7 +406,7 @@ In most cases. There are occasional (basically rare) spots where using `pi` by i
|
||||
### Numeric literals
|
||||
|
||||
|
||||
For some special cases, Julia parses *multiplication* without a multiplication symbol. This is when the value on the left is a number, as in `2pi`, which has an equivalent value to `2*pi`. *However* the two are not equivalent, in that multiplication with *numeric literals* does not have the same precedence as regular multiplication - it is higher. This has practical importance when used in division or powers. For instance, these two are **not** the same:
|
||||
For some special cases, Julia parses *multiplication* without a multiplication symbol. One case is when the value on the left is a number, as in `2pi`, which has an equivalent value to `2*pi`. *However* the two are not equivalent, in that multiplication with *numeric literals* does not have the same precedence as regular multiplication - it is higher. This has practical importance when used in division or powers. For instance, these two expressions are **not** the same:
|
||||
|
||||
|
||||
```{julia}
|
||||
@ -485,7 +484,9 @@ Using a function is very straightforward. A function is called using parentheses
|
||||
sqrt(4), sqrt(5)
|
||||
```
|
||||
|
||||
The function is referred to by name (`sqrt`) and called with parentheses. Any arguments are passed into the function using commas to separate values, should there be more than one. When there are numerous values for a function, the arguments may need to be given in a specific order or may possibly be specified with *keywords*. (A semicolon can be used instead of a comma to separate keyword arguments.)
|
||||
The function is referred to by name (`sqrt`) and called with parentheses.
|
||||
|
||||
Any arguments are passed into the function using commas to separate values, should there be more than one. When there are numerous values for a function, the arguments may need to be given in a specific order or may possibly be specified with *keywords*. (A semicolon can be used instead of a comma to separate keyword arguments from positional arguments.)
|
||||
|
||||
|
||||
Some more examples:
|
||||
|
@ -129,7 +129,7 @@ x = -40
|
||||
y = 5/9 * (x - 32)
|
||||
```
|
||||
|
||||
will evaluate the right-hand side with the value of `x` bound at the time of assignment to `y`, whereas assignment to a function
|
||||
will evaluate the expression on the right-hand side with the value of `x` bound at the time of assignment to `y`, whereas assignment to a function
|
||||
|
||||
|
||||
```{julia}
|
||||
@ -144,7 +144,7 @@ will create a function object with a value of `x` determined at a later time - t
|
||||
Within `Julia`, we make note of the distinction between a function object versus a function call. In the definition `f(x)=cos(x)`, the variable `f` refers to a function object, whereas the expression `f(pi)` is a function call. This mirrors the math notation where an $f$ is used when properties of a function are being emphasized (such as $f \circ g$ for composition) and $f(x)$ is used when the values related to the function are being emphasized (such as saying "the plot of the equation $y=f(x)$).
|
||||
|
||||
|
||||
Distinguishing these three related but different concepts (equations, function objects, and function calls) is important when modeling on the computer.
|
||||
Distinguishing these three related but different concepts -- equations and expressions, function objects, and function calls -- is important when modeling mathematics on the computer.
|
||||
|
||||
|
||||
### Cases
|
||||
@ -548,6 +548,13 @@ v0, theta
|
||||
The *big* advantage of bundling parameters into a container is consistency – the function is always called in an identical manner regardless of the number of parameters (or variables).
|
||||
|
||||
|
||||
::: {.callout-note}
|
||||
## Avoid global variables
|
||||
|
||||
Referring to a global parameter is common in math, but has a significant performance impact in `Julia`. Save for the simplest usage, it is much better to pass parameters to the function through one of several means that too rely on the value of the global state.
|
||||
|
||||
:::
|
||||
|
||||
## Multiple dispatch
|
||||
|
||||
|
||||
@ -576,7 +583,7 @@ Multiple dispatch is very common in mathematics. For example, we learn different
|
||||
|
||||
:::
|
||||
|
||||
`Julia` is similarly structured. `Julia` terminology would be to call the operation "`+`" a *generic function* and the different implementations *methods* of "`+`". This allows the user to just need to know a smaller collection of generic concepts yet still have the power of detail-specific implementations. To see how many different methods are defined in the base `Julia` language for the `+` operator, we can use the command `methods(+)`. As there are so many ($\approx 200$) and that number is growing, we illustrate how many different logarithm methods are implemented for "numbers:"
|
||||
`Julia` is similarly structured. `Julia` terminology would be to call the operation "`+`" a *generic function* and the different implementations *methods* of "`+`". This allows the user to just need to know a smaller collection of generic concepts yet still have the power of detail-specific implementations. To see how many different methods are defined in the base `Julia` language for the `+` operator, we can use the command `methods(+)`. As there are so many (well over $200$ when `Julia` is started), we illustrate how many different logarithm methods are implemented for "numbers:"
|
||||
|
||||
|
||||
```{julia}
|
||||
@ -599,7 +606,7 @@ twotox(x::Complex) = (2.0 + 0.0im)^x
|
||||
|
||||
This is for illustration purposes -- the latter two are actually already done through `Julia`'s *promotion* mechanism -- but we see that `twotox` will return a rational number when `x` is an integer unlike `Julia` which, when `x` is non-negative will return an integer and will otherwise will error or return a float (when `x` is a numeric literal, like `2^(-3)`).
|
||||
|
||||
The key to reading the above is the type annotation acts like a gatekeeper allowing in only variables of that type.
|
||||
The key to reading the above is the type annotation acts like a gatekeeper allowing in only variables of that type or a subtype of that type.
|
||||
|
||||
For example, the number `2` is parsed as a 64-bit integer (typically) and has concrete type `Int64` which is a subtype of `Integer`. So `twotox(2)` will use the first definition, and return a rational number. Whereas, the number `2.0` is parsed as a floating point number with concrete type `Float64` which is a subtype of `Real`, not `Integer`, so `twotox(2.0)` will use the second method defined above.
|
||||
|
||||
@ -1283,6 +1290,82 @@ radioq(choices, answ)
|
||||
###### Question
|
||||
|
||||
|
||||
Nasa has some learning materials on [stars](https://spacemath.gsfc.nasa.gov/stars.html) including one that describes how to count the number of stars brighter than some level. This formula is from [https://spacemath.gsfc.nasa.gov/stars/6Page103.pdf](https://spacemath.gsfc.nasa.gov/stars/6Page103.pdf):
|
||||
|
||||
$$
|
||||
\log_{10}(N(m)) = -0.0003 m^3 + 0.0019 m^2 + 0.484 m -3.82, \quad 4.0 \leq m \leq 25.0.
|
||||
$$
|
||||
|
||||
Where $N(m)$ counts the number of stars brighter than magnitude $m$ *per* square degree of space.
|
||||
|
||||
A [square degree](https://en.wikipedia.org/wiki/Square_degree) is a unit of a solid angle. An entire sphere has a solid angle of $4\pi$ and $4\pi \cdot (180/\pi)^2$ square degrees.
|
||||
|
||||
|
||||
With this we can answer agequestions, such as:
|
||||
|
||||
> How many stars can we see in the sky?
|
||||
|
||||
Star [magnitude](https://en.wikipedia.org/wiki/Magnitude_(astronomy)) measures the brightness of celestial objects, with the sun on a log scale so that a magnitude $1$ star is $100$ times brighter than a magnitude $6$ star. The sun has a value around $-27$, Sirius (the brightest visible star) around $-1.46), Venus around $-5$. We will take a magnitude of $6$ or brighter for visibility. (magnitudes less than $6$). The value of $N(6)$ is then
|
||||
|
||||
```{julia}
|
||||
q(m) = -0.0003*m^3 + 0.0019*m^2 + 0.484*m - 3.82
|
||||
N(m) = 10.0^(q(m))
|
||||
n_6 = N(6)
|
||||
```
|
||||
|
||||
The number of square degrees in the sky is
|
||||
|
||||
```{julia}
|
||||
total_square_degrees = 4pi * (180/pi)^2
|
||||
```
|
||||
|
||||
So the formula estimates this many visible stars in the entire sky:
|
||||
|
||||
```{julia}
|
||||
n_6 * total_square_degrees
|
||||
```
|
||||
|
||||
With a telescope, it is estimated that stars with brightness at magnitude $10.0$ can be seen. How many stars are visible in the sky with such a telescope?
|
||||
|
||||
```{julia}
|
||||
#| echo: false
|
||||
val = N(10) * total_square_degrees
|
||||
atol = 10
|
||||
numericq(val, atol)
|
||||
```
|
||||
|
||||
A photograph is taken of a faint star cluster covering 5 square-degrees of space. The cluster contains stars of magnitude between $11.0$ and $15.0$. The stars seen in the photograph number $5237$. The astronomer estimates there are $5237$ *minus* the expected number of stars in this cluster. What did she estimate?
|
||||
|
||||
```{julia}
|
||||
#| echo: false
|
||||
expected_count = (N(15.0) - N(11.0)) * 5
|
||||
actual_count = 5237
|
||||
cluster = actual_count - expected_count
|
||||
atol = 2
|
||||
hint = """
|
||||
The total expected number of stars in the one degree is `N(15.0) - N(11.0)`.
|
||||
The count is then `5237 - 5*(N(15.0) - N(11.0))`.
|
||||
"""
|
||||
numericq(cluster, atol; hint)
|
||||
```
|
||||
|
||||
|
||||
If a star of magnitude $5$ difference is $100$ times brighter, what is the scale of the logarithm?
|
||||
|
||||
```{julia}
|
||||
#| echo: false
|
||||
explanation = raw"""
|
||||
The base $a$ solve $\log_a(x + 5) / \log_a(x) = 100$. The logs can be combined and then $a$ can be solved for.
|
||||
"""
|
||||
choices = [raw"$5$",raw"$\sqrt[5]{100}$", raw"$\sqrt{100}$"]
|
||||
answer = 2
|
||||
buttonq(choices, answer; explanation)
|
||||
```
|
||||
|
||||
|
||||
###### Question
|
||||
|
||||
|
||||
Identifying the range of a function can be a difficult task. We see in this question that in some cases, a package can be of assistance.
|
||||
|
||||
|
||||
|
@ -18,7 +18,7 @@ plotly()
|
||||
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$?
|
||||
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 a 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.
|
||||
|
@ -19,7 +19,7 @@ The [`Julia`](http://www.julialang.org) programming language is well suited as a
|
||||
|
||||
`Julia` is an *open source* project which allows anyone with a supported computer to use it free of charge.
|
||||
|
||||
To install locally, the [downloads](https://julialang.org/downloads/) page has directions to use the `Juliaup` utility for managing an installation. There are also links to several different binaries for manual installation. Additionally, the downloads page contains a link to a docker image. `Julia` can also be compiled from source.
|
||||
To install locally, the [downloads](https://julialang.org/downloads/) page has directions to use the `juliaup` utility for managing an installation. There are also links to several different binaries for manual installation. Additionally, the downloads page contains a link to a docker image. `Julia` can also be compiled from source.
|
||||
|
||||
|
||||
`Julia` can also be run through the web.
|
||||
@ -280,7 +280,7 @@ Values will be promoted to a common type (or type `Any` if none exists). For exa
|
||||
(Vectors are used as a return type from some functions, as such, some familiarity is needed.)
|
||||
|
||||
|
||||
Other common container types are variables of vectors (higher-dimensional arrarys, offset arrays, etc.) tuples (for heterogeneous, immutable, indexed values); named tuples (which add a name to each value in a tuple); and dictionaries (for associative relationships between a key and a value).
|
||||
Other common container types are variations of vectors (higher-dimensional arrarys, offset arrays, etc.) tuples (for heterogeneous, immutable, indexed values); named tuples (which add a name to each value in a tuple); and dictionaries (for associative relationships between a key and a value).
|
||||
|
||||
|
||||
Regular arithmetic sequences can be defined by either:
|
||||
|
@ -174,12 +174,12 @@ You can discover more about the range of floating point values provided by calli
|
||||
* `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.
|
||||
|
||||
:::
|
||||
|
||||
|
||||
#### Scientific notation
|
||||
|
||||
@ -298,7 +298,7 @@ That is adding `1/10` and `2/10` is not exactly `3/10`, as expected mathematical
|
||||
1/10 + (2/10 + 3/10) == (1/10 + 2/10) + 3/10
|
||||
```
|
||||
|
||||
* Mathematically, or 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:
|
||||
* Mathematically, 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}
|
||||
|
@ -439,7 +439,7 @@ 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.
|
||||
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.
|
||||
@ -492,6 +492,8 @@ For plotting points with `scatter`, or `scatter!` the markers can be adjusted vi
|
||||
|
||||
Of course, zero, one, or more of these can be used on any given call to `plot`, `plot!`, `scatter`, or `scatter!`.
|
||||
|
||||
There are also several *shorthands* in `Plots` that allows several related attributes to be specified to a single argument that is disambiguated using the type of the value. (Eg. `line=(5, 0.25, "blue")` will specify the line have widht `5`, color `blue`, and alpha-transparency `0.25`.)
|
||||
|
||||
#### Example: Bresenham's algorithm
|
||||
|
||||
|
||||
|
@ -1,6 +1,9 @@
|
||||
# Polynomials
|
||||
|
||||
|
||||
Now that basic properties of functions have been discussed, we move to various types of related functions beginning with polynomial functions.
|
||||
|
||||
|
||||
{{< include ../_common_code.qmd >}}
|
||||
|
||||
In this section we use the following add-on packages:
|
||||
@ -22,6 +25,7 @@ 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.
|
||||
|
||||
|
||||
@ -128,7 +132,7 @@ Symbolic math programs include well-known ones like the commercial programs Math
|
||||
|
||||
The [Symbolics](https://github.com/JuliaSymbolics/Symbolics.jl) package for `Julia` provides a "fast and modern CAS for fast and modern language." It is described further in [Symbolics.jl](../alternatives/symbolics.qmd).
|
||||
|
||||
As `SymPy` has some features not yet implemented in `Symbolics`, we use that here. The `PyCall` and `PythonCall` packages are available to glue `Julia` to Python in a seamless manner. These allow the `Julia` package `SymPy` to provide functionality from SymPy within `Julia`.
|
||||
As `SymPy` has some features not yet implemented in `Symbolics`, we use `SymPy` in these notes. The `PyCall` and `PythonCall` packages are available to glue `Julia` to Python in a seamless manner. These allow the `Julia` package `SymPy` (or `SymPyPythonCall`) to provide functionality from SymPy within `Julia`.
|
||||
|
||||
|
||||
:::{.callout-note}
|
||||
|
@ -140,11 +140,11 @@ Floating point roundoff leads to the last value *exceeding* `0.6`, so should it
|
||||
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:
|
||||
The number of points is specified as a third argument (though keyword arguments can be given):
|
||||
|
||||
|
||||
```{julia}
|
||||
xs = range(-1, 1, length=9) # or simply range(-1, 1, 9) as of v"1.7"
|
||||
xs = range(-1, 1,9)
|
||||
```
|
||||
|
||||
and
|
||||
|
@ -160,7 +160,7 @@ This basic fact can be manipulated many ways. For example, dividing through by $
|
||||
[cos(theta) for theta in [0, pi/6, pi/4, pi/3, pi/2]]
|
||||
```
|
||||
|
||||
To compute $\sin^2(\theta)$, the power is applied to the value of $\sin(\theta)$ and not the `sin` function. (Think of $\sin^2(\theta)$ as $(sin(\theta))^2$:
|
||||
To compute $\sin^2(\theta)$, the power is applied to the value of $\sin(\theta)$ and not the `sin` function. (Think of $\sin^2(\theta)$ as $(\sin(\theta))^2$:
|
||||
|
||||
```{julia}
|
||||
theta = pi/8
|
||||
@ -982,6 +982,18 @@ answ = 1
|
||||
radioq(choices, answ, keep_order=true)
|
||||
```
|
||||
|
||||
###### Question
|
||||
|
||||
The function `f(x) = x * tanh(exp(x))` has a shape akin to `max(0,x)` but is smoooth. Graphically finds its smallest $y$ value.
|
||||
|
||||
```{julia}
|
||||
#| echo: false
|
||||
f(x) = x * tanh(exp(x))
|
||||
val = -0.35328577784821125
|
||||
numericq(val, 1e-1)
|
||||
```
|
||||
|
||||
|
||||
###### Question
|
||||
|
||||
|
||||
@ -1016,3 +1028,4 @@ Is this identical to the pattern for the regular sine function?
|
||||
#| echo: false
|
||||
yesnoq(false)
|
||||
```
|
||||
|
||||
|
@ -111,6 +111,26 @@ a = v0 * cosd(theta)
|
||||
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
|
||||
```
|
||||
|
||||
##### Example
|
||||
|
||||
A [grass swale](https://stormwater.pca.state.mn.us/index.php?title=Design_criteria_for_dry_swale_(grass_swale)) is a design to manage surface water flow resulting from a storm. Swales detain, filter, and infiltrate runoff limiting erosion in the process.
|
||||
@ -158,25 +178,6 @@ R = A / P
|
||||
Q = R^(2/3) * S^(1/2) / n * A
|
||||
```
|
||||
|
||||
|
||||
##### 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
|
||||
|
||||
|
||||
@ -243,7 +244,7 @@ The `varinfo` function will list the variables currently defined in the main wor
|
||||
|
||||
:::{.callout-warning}
|
||||
## Warning
|
||||
**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`. This is called shadowing. 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.
|
||||
**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`. This is called shadowing. For example, this is a perfectly valid command `x + y = 3`. However, it doesn't specify an equation, rather it *redefines* addition. At the terminal, this binding to `+` occurs in the `Main` module. This shadows that value of `+` 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.:+(2, 3)`. This uses the notation `module_name.variable_name` to look up a binding in a module.
|
||||
|
||||
:::
|
||||
|
||||
@ -265,7 +266,7 @@ __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` 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}
|
||||
|
@ -359,9 +359,12 @@ fibs = [1, 1, 2, 3, 5, 8, 13]
|
||||
Later we will discuss different ways to modify the values of a vector to create new ones, similar to how scalar multiplication does.
|
||||
|
||||
|
||||
As mentioned, vectors in `Julia` are comprised of elements of a similar type, but the type is not limited to numeric values. Some examples:,
|
||||
As mentioned, vectors in `Julia` are comprised of elements of a similar type, but the type is not limited to numeric values. Some examples:
|
||||
|
||||
* a vector of strings might be useful for text processing, For example, the `WordTokenizers.jl` package takes text and produces tokens from the words.
|
||||
|
||||
* a vector of Boolean values can naturally arise and is widely used within Julia's `DataFrames.jl` package.
|
||||
|
||||
* some applications are even naturally represented in terms of vectors of vectors (such as happens when plotting a collection points).
|
||||
|
||||
|
||||
@ -378,7 +381,7 @@ Look at the output of these two vectors, in particular how the underlying type o
|
||||
|
||||
|
||||
|
||||
Finally, we mention that if `Julia` has values of different types it will promote them to a common type if possible. Here we combine three types of numbers, and see that each is promoted to `Float64`:
|
||||
Finally, we mention that if `Julia` has values of different types it will promote them to a common type, as possible. Here we combine three types of numbers, and see that each is promoted to `Float64`:
|
||||
|
||||
|
||||
```{julia}
|
||||
@ -415,6 +418,8 @@ vs[2]
|
||||
|
||||
The last value of a vector is usually denoted by $v_n$. In `Julia`, the `length` function will return $n$, the number of items in the container. So `v[length(v)]` will refer to the last component. However, the special keyword `end` will do so as well, when put into the context of indexing. So `v[end]` is more idiomatic. (Similarly, there is a `begin` keyword that is useful when the vector is not $1$-based, as is typical but not mandatory.)
|
||||
|
||||
The functions `first` and `last` refer to the first and last components of a collection. An additional argument can be specified to take the first (or last) $n$ components. The function `only` will return the only component of a vector, if it has length $1$ and error otherwise.
|
||||
|
||||
|
||||
:::{.callout-note}
|
||||
## More on indexing
|
||||
@ -837,7 +842,7 @@ d["two"]
|
||||
|
||||
Named tuples are associative arrays where the keys are restricted to symbols. There are other types of associative arrays, specialized cases of the `AbstractDict` type with performance benefits for specific use cases. In these notes, dictionaries appear as output in some function calls.
|
||||
|
||||
Unlike vectors and tuples, named tuples and dictionaries are not currently supported by broadcasting. This causes no loss in usefulness, as the values can easily be iterated over, but the convenience of the dot notation is lost.
|
||||
Unlike vectors and tuples, dictionaries are not currently supported by broadcasting. This causes no loss in usefulness, as the values can easily be iterated over, but the convenience of the dot notation is lost.
|
||||
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user