commit
3e7e3a9727
5
.gitignore
vendored
5
.gitignore
vendored
@ -5,6 +5,11 @@ docs/site
|
||||
test/benchmarks.json
|
||||
Manifest.toml
|
||||
TODO.md
|
||||
/*/_pdf_index.pdf
|
||||
/*/*/_pdf_index.pdf
|
||||
/*/_pdf_index.typ
|
||||
/*/*/_pdf_index.typ
|
||||
/*/CalculusWithJulia.pdf
|
||||
default.profraw
|
||||
/quarto/default.profraw
|
||||
/*/*/default.profraw
|
@ -9,6 +9,7 @@ ABD = "ABD"
|
||||
DNE = "DNE"
|
||||
|
||||
Hass = "Hass"
|
||||
Strang = "Strang"
|
||||
|
||||
multline = "multline"
|
||||
|
||||
|
11
quarto/ODEs/Project.toml
Normal file
11
quarto/ODEs/Project.toml
Normal file
@ -0,0 +1,11 @@
|
||||
[deps]
|
||||
CalculusWithJulia = "a2e0e22d-7d4c-5312-9169-8b992201a882"
|
||||
IJulia = "7073ff75-c697-5162-941a-fcdaad2a7d2a"
|
||||
ModelingToolkit = "961ee093-0014-501f-94e3-6117800e7a78"
|
||||
MonteCarloMeasurements = "0987c9cc-fe09-11e8-30f0-b96dd679fdca"
|
||||
OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed"
|
||||
PlotlyBase = "a03496cd-edff-5a9b-9e67-9cda94a718b5"
|
||||
PlotlyKaleido = "f2990250-8cf9-495f-b13a-cce12b45703c"
|
||||
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
|
||||
Roots = "f2b01f46-fcfa-551c-844a-d8ac1e96c665"
|
||||
SymPy = "24249f21-da20-56a4-8eb1-6a02cf4ae2e6"
|
1
quarto/ODEs/_pdf_index.qmd
Normal file
1
quarto/ODEs/_pdf_index.qmd
Normal file
@ -0,0 +1 @@
|
||||
# ODEs with Julia
|
16
quarto/ODEs/make_pdf.jl
Normal file
16
quarto/ODEs/make_pdf.jl
Normal file
@ -0,0 +1,16 @@
|
||||
module Main
|
||||
# makefile for generating typst pdfs
|
||||
# per directory usage
|
||||
|
||||
dir = "ODEs"
|
||||
files = (
|
||||
"odes",
|
||||
"euler",
|
||||
"solve",
|
||||
"differential_equations"
|
||||
|
||||
)
|
||||
include("../_make_pdf.jl")
|
||||
|
||||
main()
|
||||
end
|
@ -5,6 +5,7 @@ Short cut. Run first command until happy, then run second to publish
|
||||
```
|
||||
quarto render
|
||||
#julia adjust_plotly.jl # <-- no longer needed
|
||||
# maybe git config --global http.postBuffer 157286400
|
||||
quarto publish gh-pages --no-render
|
||||
```
|
||||
|
||||
|
65
quarto/_make_pdf.jl
Normal file
65
quarto/_make_pdf.jl
Normal file
@ -0,0 +1,65 @@
|
||||
# code to turn a qmd file into a pdf file
|
||||
# then stictch them all together
|
||||
# include, then load main
|
||||
using PDFmerger
|
||||
using Mustache
|
||||
|
||||
index = "_pdf_index"
|
||||
|
||||
|
||||
typst_tpl = mt"""
|
||||
---
|
||||
title: {{:title}}
|
||||
date: today
|
||||
jupyter: julia-1.11
|
||||
execute:
|
||||
daemon: false
|
||||
format:
|
||||
typst:
|
||||
toc: false
|
||||
section-numbering: "1."
|
||||
keep-typ: false
|
||||
include-before-body:
|
||||
- text: |
|
||||
#set figure(placement: auto)
|
||||
---
|
||||
"""
|
||||
|
||||
index = "_pdf_index"
|
||||
tmp = "XXX.qmd"
|
||||
function process_file(qmd)
|
||||
f = "$qmd.qmd"
|
||||
open(tmp, "w") do io
|
||||
print(io, typst_tpl(title=qmd))
|
||||
for r ∈ readlines(f)
|
||||
# fixup
|
||||
r = replace(r, "($dir/figures/"=>"(figures/")
|
||||
println(io, r)
|
||||
end
|
||||
end
|
||||
run(`quarto render $tmp --to typst`)
|
||||
end
|
||||
|
||||
# run
|
||||
function (@main)(args...)
|
||||
nothing_to_do = mtime("_pdf_index.pdf") > maximum(mtime, (filter(endswith(".qmd"), readdir("."))))
|
||||
|
||||
if !("--force" ∈ args) && nothing_to_do
|
||||
@info "Nothing to update"
|
||||
return nothing
|
||||
end
|
||||
|
||||
@info "process $index.qmd"
|
||||
run(`quarto render $index.qmd --to typst`)
|
||||
for p in files
|
||||
@info "Process $p"
|
||||
process_file(p)
|
||||
@info "Merge $p into $index.qmd file"
|
||||
append_pdf!("$index.pdf", "XXX.pdf")
|
||||
end
|
||||
@warn "cleanup files"
|
||||
rm("XXX.qmd", force=true)
|
||||
rm("XXX.pdf", force=true)
|
||||
rm("XXX.typ", force=true)
|
||||
rm("XXX_files", force=true, recursive=true)
|
||||
end
|
92
quarto/_pdf_index.qmd
Normal file
92
quarto/_pdf_index.qmd
Normal file
@ -0,0 +1,92 @@
|
||||
---
|
||||
title: Calculus with Julia
|
||||
date: today
|
||||
jupyter: julia-1.11
|
||||
execute:
|
||||
daemon: false
|
||||
format:
|
||||
typst:
|
||||
toc: false
|
||||
section-numbering: "1."
|
||||
keep-typ: false
|
||||
include-before-body:
|
||||
- text: |
|
||||
#set figure(placement: auto)
|
||||
---
|
||||
|
||||
|
||||
[{}](https://calculuswithjulia.github.io/)
|
||||
|
||||
This is a set of notes for learning
|
||||
[calculus](http://en.wikipedia.org/wiki/Calculus) using the
|
||||
[`Julia`](https://julialang.org) language. `Julia` is an open-source
|
||||
programming language with an easy to learn syntax that is well suited
|
||||
for this task.
|
||||
|
||||
Read "[Getting started with Julia](./misc/getting_started_with_julia.html)" to learn how to install and customize `Julia` for following along with these notes. Read "[Julia interfaces](./misc/julia_interfaces.html)" to review different ways to interact with a `Julia` installation.
|
||||
|
||||
Since the mid 90s there has been a push to teach calculus using many
|
||||
different points of view. The
|
||||
[Harvard](http://www.math.harvard.edu/~knill/pedagogy/harvardcalculus/)
|
||||
style rule of four says that as much as possible the conversation
|
||||
should include a graphical, numerical, algebraic, and verbal
|
||||
component. These notes use the programming language
|
||||
[Julia](http://julialang.org) to illustrate the graphical, numerical,
|
||||
and, at times, the algebraic aspects of calculus.
|
||||
|
||||
There are many examples of integrating a computer algebra system (such
|
||||
as `Mathematica`, `Maple`, or `Sage`) into the calculus
|
||||
conversation. Computer algebra systems can be magical. The popular
|
||||
[WolframAlpha](http://www.wolframalpha.com/) website calls the full
|
||||
power of `Mathematica` while allowing an informal syntax that is
|
||||
flexible enough to be used as a backend for Apple's Siri
|
||||
feature. ("Siri what is the graph of x squared minus 4?") For
|
||||
learning purposes, computer algebra systems model very well the
|
||||
algebraic/symbolic treatment of the material while providing means to
|
||||
illustrate the numeric aspects. These notes are a bit different in
|
||||
that `Julia` is primarily used for the numeric style of computing and
|
||||
the algebraic/symbolic treatment is added on. Doing the symbolic
|
||||
treatment by hand can be very beneficial while learning, and computer
|
||||
algebra systems make those exercises seem kind of redundant, as the
|
||||
finished product can be produced much more easily.
|
||||
|
||||
Our real goal is to get at the concepts using technology as much as
|
||||
possible without getting bogged down in the mechanics of the computer
|
||||
language. We feel `Julia` has a very natural syntax that makes the
|
||||
initial start up not so much more difficult than using a calculator,
|
||||
but with a language that has a tremendous upside. The notes restrict
|
||||
themselves to a reduced set of computational concepts. This set is
|
||||
sufficient for working many of the problems in calculus, but do not
|
||||
cover thoroughly many aspects of programming. (Those who are
|
||||
interested can go off on their own and `Julia` provides a rich
|
||||
opportunity to do so.) Within this restricted set, are operators that
|
||||
make many of the computations of calculus reduce to a function call of
|
||||
the form `action(function, arguments...)`. With a small collection of
|
||||
actions that can be composed, many of the problems associated with
|
||||
introductory calculus can be attacked.
|
||||
|
||||
|
||||
These notes are accompanied by a `Julia` package [`CalculusWithJulia`](https://github.com/jverzani/CalculusWithJulia.jl)
|
||||
that provides some simple functions to streamline some common tasks
|
||||
and loads some useful packages that will be used repeatedly.
|
||||
|
||||
----
|
||||
|
||||
The [online book](https://calculuswithjulia.github.io/) is compiled using the excellent [Quarto](https://quarto.org/) publishing system. Quarto does not support both [typst](https://typst.app/) and the book format, so this pdf file is pieced together section by section. This is useful, but less than optimal:
|
||||
|
||||
* there is no table of contents
|
||||
* there is no index of sections, rather search must be utilized to navigate
|
||||
* in HTML, the questions are interactive and self-grading; with the pdf they are rendered in plain text, with some residual formatting. Also the questions in the pdf have *really* long section numbers.
|
||||
* there is little attention paid to page layout, as HTML is the preferred presentation format.
|
||||
|
||||
|
||||
----
|
||||
|
||||
```{julia}
|
||||
#| echo: false
|
||||
using YAML, Dates, Markdown
|
||||
cfg = YAML.load_file("_quarto.yml")
|
||||
version = VersionNumber(cfg["version"])
|
||||
yr=Dates.year(now())
|
||||
Markdown.parse("[CalculusWithJulia](https://calculuswithjulia.github.io/) version $version; Copyright 2022-$yr, John Verzani")
|
||||
```
|
@ -138,7 +138,9 @@ website:
|
||||
|
||||
format:
|
||||
html:
|
||||
theme: lux # spacelab # lux # sketchy # cosmo # https://quarto.org/docs/output-formats/html-themes.html
|
||||
theme:
|
||||
light: lux # spacelab # lux # sketchy # cosmo # https://quarto.org/docs/output-formats/html-themes.html
|
||||
dark: darkly
|
||||
number-depth: 3
|
||||
toc-depth: 3
|
||||
link-external-newwindow: true
|
||||
|
28
quarto/alternatives/Project.toml
Normal file
28
quarto/alternatives/Project.toml
Normal file
@ -0,0 +1,28 @@
|
||||
[deps]
|
||||
BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf"
|
||||
CalculusWithJulia = "a2e0e22d-7d4c-5312-9169-8b992201a882"
|
||||
ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210"
|
||||
GLMakie = "e9467ef8-e4e7-5192-8a1a-b1aee30e663a"
|
||||
GeometryBasics = "5c1252a2-5f33-56bf-86c9-59e7332b4326"
|
||||
IJulia = "7073ff75-c697-5162-941a-fcdaad2a7d2a"
|
||||
Implicit3DPlotting = "d997a800-832a-4a4c-b340-7dddf3c1ad50"
|
||||
Integrals = "de52edbc-65ea-441a-8357-d3a637375a31"
|
||||
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
|
||||
Meshing = "e6723b4c-ebff-59f1-b4b7-d97aa5274f73"
|
||||
ModelingToolkit = "961ee093-0014-501f-94e3-6117800e7a78"
|
||||
NonlinearSolve = "8913a72c-1f9b-4ce2-8d82-65094dcecaec"
|
||||
Optimization = "7f7a1694-90dd-40f0-9382-eb1efda571ba"
|
||||
OptimizationOptimJL = "36348300-93cb-4f02-beb5-3c3902f8871e"
|
||||
PlotUtils = "995b91a9-d308-5afd-9ec6-746e21dbc043"
|
||||
PlotlyBase = "a03496cd-edff-5a9b-9e67-9cda94a718b5"
|
||||
PlotlyKaleido = "f2990250-8cf9-495f-b13a-cce12b45703c"
|
||||
PlotlyLight = "ca7969ec-10b3-423e-8d99-40f33abb42bf"
|
||||
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
|
||||
QuadGK = "1fd47b50-473d-5c70-9696-f719f8f3bcdc"
|
||||
Roots = "f2b01f46-fcfa-551c-844a-d8ac1e96c665"
|
||||
SplitApplyCombine = "03a91e81-4c3e-53e1-a0a4-9c0c8f19dd66"
|
||||
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
|
||||
SymPy = "24249f21-da20-56a4-8eb1-6a02cf4ae2e6"
|
||||
SymbolicLimits = "19f23fe9-fdab-4a78-91af-e7b7767979c3"
|
||||
SymbolicNumericIntegration = "78aadeae-fbc0-11eb-17b6-c7ec0477ba9e"
|
||||
Symbolics = "0c5d862f-8b57-4792-8d23-62f2024744c7"
|
@ -304,11 +304,11 @@ x0 = [4.0]
|
||||
prob = OptimizationProblem(F, x0)
|
||||
```
|
||||
|
||||
The problem is solved through the common interface with a specified method, in this case `Newton`:
|
||||
The problem is solved through the common interface with a specified method, in this case `NelderMead`:
|
||||
|
||||
|
||||
```{julia}
|
||||
soln = solve(prob, Newton())
|
||||
soln = solve(prob, NelderMead())
|
||||
```
|
||||
|
||||
:::{.callout-note}
|
||||
|
1
quarto/alternatives/_pdf_index.qmd
Symbolic link
1
quarto/alternatives/_pdf_index.qmd
Symbolic link
@ -0,0 +1 @@
|
||||
../alternatives.qmd
|
18
quarto/alternatives/make_pdf.jl
Normal file
18
quarto/alternatives/make_pdf.jl
Normal file
@ -0,0 +1,18 @@
|
||||
module Make
|
||||
|
||||
# makefile for generating typst pdfs
|
||||
# per directory usage
|
||||
|
||||
dir = "alternatives"
|
||||
files = (
|
||||
"symbolics",
|
||||
"SciML",
|
||||
"plotly_plotting",
|
||||
"makie_plotting",
|
||||
|
||||
)
|
||||
|
||||
include("../_make_pdf.jl")
|
||||
|
||||
main()
|
||||
end
|
@ -867,52 +867,31 @@ end
|
||||
|
||||
To plot the equation $F(x,y,z)=0$, for $F$ a scalar-valued function, again the implicit function theorem says that, under conditions, near any solution $(x,y,z)$, $z$ can be represented as a function of $x$ and $y$, so the graph will look likes surfaces stitched together. The `Implicit3DPlotting` package takes an approach like `ImplicitPlots` to represent these surfaces. It replaces the `Contour` package computation with a $3$-dimensional alternative provided through the `Meshing` and `GeometryBasics` packages.
|
||||
|
||||
|
||||
The `Implicit3DPlotting` package needs some maintenance, so we borrow the main functionality and wrap it into a function:
|
||||
|
||||
|
||||
```{julia}
|
||||
import Meshing
|
||||
import GeometryBasics
|
||||
|
||||
function make_mesh(xlims, ylims, zlims, f,
|
||||
M = Meshing.MarchingCubes(); # or Meshing.MarchingTetrahedra()
|
||||
samples=(35, 35, 35),
|
||||
)
|
||||
|
||||
lims = extrema.((xlims, ylims, zlims))
|
||||
Δ = xs -> last(xs) - first(xs)
|
||||
xs = Vec(first.(lims))
|
||||
Δxs = Vec(Δ.(lims))
|
||||
|
||||
GeometryBasics.Mesh(f, Rect(xs, Δxs), M; samples = samples)
|
||||
end
|
||||
using Implicit3DPlotting
|
||||
```
|
||||
|
||||
The `make_mesh` function creates a mesh that can be visualized with the `wireframe` or `mesh` plotting functions.
|
||||
|
||||
|
||||
This example, plotting an implicitly defined sphere, comes from the documentation of `Implicit3DPlotting`. The `f` in `make_mesh` is a scalar-valued function of a vector:
|
||||
This example, plotting an implicitly defined sphere, comes from the documentation of `Implicit3DPlotting`. The `f` to be plotted is a scalar-valued function of a vector:
|
||||
|
||||
|
||||
```{julia}
|
||||
f(x) = sum(x.^2) - 1
|
||||
xs = ys = zs = (-5, 5)
|
||||
m = make_mesh(xs, ys, zs, f)
|
||||
wireframe(m)
|
||||
xlims = ylims = zlims = (-5, 5)
|
||||
plot_implicit_surface(f; xlims, ylims, zlims)
|
||||
```
|
||||
|
||||
|
||||
|
||||
Here we visualize an intersection of a sphere with another figure:
|
||||
|
||||
|
||||
```{julia}
|
||||
r₂(x) = sum(x.^2) - 5/4 # a sphere
|
||||
r₄(x) = sum(x.^4) - 1
|
||||
xs = ys = zs = -2:2
|
||||
m2,m4 = make_mesh(xs, ys, zs, r₂), make_mesh(xs, ys, zs, r₄)
|
||||
|
||||
wireframe(m4, color=:yellow)
|
||||
wireframe!(m2, color=:red)
|
||||
xlims = ylims = zlims = (-2, 2)
|
||||
p = plot_implicit_surface(r₂; xlims, ylims, zlims, color=:yellow)
|
||||
plot_implicit_surface!(p, r₄; xlims, ylims, zlims, color=:red)
|
||||
current_figure()
|
||||
```
|
||||
|
||||
@ -921,9 +900,8 @@ This example comes from [Wikipedia](https://en.wikipedia.org/wiki/Implicit_surfa
|
||||
|
||||
```{julia}
|
||||
f(x,y,z) = 2y*(y^2 -3x^2)*(1-z^2) + (x^2 +y^2)^2 - (9z^2-1)*(1-z^2)
|
||||
zs = ys = xs = range(-5/2, 5/2, length=100)
|
||||
m = make_mesh(xs, ys, zs, x -> f(x...))
|
||||
wireframe(m)
|
||||
xlims = ylims = zlims = (-5/2, 5/2)
|
||||
plot_implicit_surface(x -> f(x...); xlims, ylims, zlims)
|
||||
```
|
||||
|
||||
(This figure does not render well through `contour(xs, ys, zs, f, levels=[0])`, as the hole is not shown.)
|
||||
@ -937,9 +915,8 @@ function cassini(λ, ps = ((1,0,0), (-1, 0, 0)))
|
||||
n = length(ps)
|
||||
x -> prod(norm(x .- p) for p ∈ ps) - λ^n
|
||||
end
|
||||
xs = ys = zs = range(-2, 2, length=100)
|
||||
m = make_mesh(xs, ys, zs, cassini(1.05))
|
||||
wireframe(m)
|
||||
xlims = ylims = zlims = (-2, 2)
|
||||
plot_implicit_surface(cassini(1.05); xlims, ylims, zlims)
|
||||
```
|
||||
|
||||
## Vector fields. Visualizations of $f:R^2 \rightarrow R^2$
|
||||
|
@ -140,8 +140,8 @@ typeof(sin(x)), typeof(Symbolics.value(sin(x)))
|
||||
The `TermInterface` package is used by `SymbolicUtils` to explore the tree structure of an expression. The main methods are (cf. [SymbolicUtils.jl](https://symbolicutils.juliasymbolics.org/#expression_interface)):
|
||||
|
||||
|
||||
* `istree(ex)`: `true` if `ex` is not a *leaf* node (like a symbol or numeric literal)
|
||||
* `operation(ex)`: the function being called (if `istree` returns `true`)
|
||||
* `iscall(ex)`: `true` if `ex` is not a *leaf* node (like a symbol or numeric literal). The old name was `istree`.
|
||||
* `operation(ex)`: the function being called (if `iscall` returns `true`)
|
||||
* `arguments(ex)`: the arguments to the function being called
|
||||
* `symtype(ex)`: the inferred type of the expression
|
||||
|
||||
@ -163,7 +163,7 @@ arguments(ex) # `+` is n-ary, in this case with 3 arguments
|
||||
```
|
||||
|
||||
```{julia}
|
||||
ex1 = arguments(ex)[3] # terms have been reordered
|
||||
ex1 = arguments(ex)[2] # terms have been reordered
|
||||
operation(ex1) # operation for `x^2` is `^`
|
||||
```
|
||||
|
||||
@ -172,10 +172,10 @@ a, b = arguments(ex1)
|
||||
```
|
||||
|
||||
```{julia}
|
||||
istree(ex1), istree(a)
|
||||
iscall(ex1), iscall(a)
|
||||
```
|
||||
|
||||
Here `a` is not a "tree", as it has no operation or arguments, it is just a variable (the `x` variable).
|
||||
Here `a` is not a call, as it has no operation or arguments, it is just a variable (the `x` variable).
|
||||
|
||||
|
||||
The value of `symtype` is the *inferred* type of an expression, which may not match the actual type. For example,
|
||||
@ -199,7 +199,7 @@ As an example, we write a function to find the free symbols in a symbolic expres
|
||||
import Symbolics.SymbolicUtils: issym
|
||||
free_symbols(ex) = (s=Set(); free_symbols!(s, ex); s)
|
||||
function free_symbols!(s, ex)
|
||||
if istree(ex)
|
||||
if iscall(ex)
|
||||
for a ∈ arguments(ex)
|
||||
free_symbols!(s, a)
|
||||
end
|
||||
@ -660,11 +660,11 @@ or
|
||||
eqs = [5x + 2y, 6x + 3y] .~ [1, 2]
|
||||
```
|
||||
|
||||
The `Symbolics.solve_for` function can solve *linear* equations. For example,
|
||||
The `Symbolics.symbolic_linear_solve` function can solve *linear* equations. For example,
|
||||
|
||||
|
||||
```{julia}
|
||||
Symbolics.solve_for(eqs, [x, y])
|
||||
Symbolics.symbolic_linear_solve(eqs, [x, y])
|
||||
```
|
||||
|
||||
The coefficients can be symbolic. Two examples could be:
|
||||
@ -673,7 +673,7 @@ The coefficients can be symbolic. Two examples could be:
|
||||
```{julia}
|
||||
@variables m b x y
|
||||
eq = y ~ m*x + b
|
||||
Symbolics.solve_for(eq, x)
|
||||
Symbolics.symbolic_linear_solve(eq, x)
|
||||
```
|
||||
|
||||
```{julia}
|
||||
@ -683,13 +683,30 @@ eqs = R*X .~ b
|
||||
```
|
||||
|
||||
```{julia}
|
||||
Symbolics.solve_for(eqs, [x,y])
|
||||
Symbolics.symbolic_linear_solve(eqs, [x,y])
|
||||
```
|
||||
|
||||
### Limits
|
||||
|
||||
Many symbolic limits involving exponentials and logarithms can be
|
||||
computed in Symbolics, as of recent versions. The underlying package
|
||||
is `SymbolicLimits`. This package is in development. Below we use the
|
||||
unwrapped version of the variable. We express limits as $x$ goes to
|
||||
infinity, which can be achieved by rewriting:
|
||||
|
||||
As of writing, there is no extra functionality provided by `Symbolics` for computing limits.
|
||||
```{julia}
|
||||
@variables x
|
||||
𝑥 = x.val # unwrapped
|
||||
F(x) = exp(x+exp(-x))-exp(x)
|
||||
limit(F(𝑥), 𝑥, Inf)
|
||||
```
|
||||
|
||||
Or
|
||||
|
||||
```{julia}
|
||||
F(x) = log(log(x*exp(x*exp(x))+1))-exp(exp(log(log(x))+1/x))
|
||||
limit(F(𝑥), 𝑥, Inf)
|
||||
```
|
||||
|
||||
|
||||
### Derivatives
|
||||
@ -708,7 +725,7 @@ Or to find a critical point:
|
||||
|
||||
|
||||
```{julia}
|
||||
Symbolics.solve_for(yp ~ 0, x) # linear equation to solve
|
||||
Symbolics.symbolic_linear_solve(yp ~ 0, x) # linear equation to solve
|
||||
```
|
||||
|
||||
The derivative computation can also be broken up into an expression indicating the derivative and then a function to apply the derivative rules:
|
||||
@ -726,7 +743,8 @@ and then
|
||||
expand_derivatives(D(y))
|
||||
```
|
||||
|
||||
Using `Differential`, differential equations can be specified. An example was given in [ODEs](../ODEs/differential_equations.html), using `ModelingToolkit`.
|
||||
Using `Differential`, differential equations can be specified. An example was given in [ODEs](../ODEs/differential_equations.html),
|
||||
using `ModelingToolkit`.
|
||||
|
||||
|
||||
Higher order derivatives can be done through composition:
|
||||
@ -774,12 +792,6 @@ Symbolics.jacobian(eqs, [x,y])
|
||||
|
||||
### Integration
|
||||
|
||||
::: {.callout-note}
|
||||
#### This area is very much WIP
|
||||
|
||||
The use of `SymbolicNumericIntegration` below is currently broken.
|
||||
:::
|
||||
|
||||
The `SymbolicNumericIntegration` package provides a means to integrate *univariate* expressions through its `integrate` function.
|
||||
|
||||
|
||||
@ -838,18 +850,10 @@ substitute(ΣqᵢΘᵢ, d)
|
||||
The package provides an algorithm for the creation of candidates and the means to solve when possible. The `integrate` function is the main entry point. It returns three values: `solved`, `unsolved`, and `err`. The `unsolved` is the part of the integrand which can not be solved through this package. It is `0` for a given problem when `integrate` is successful in identifying an antiderivative, in which case `solved` is the answer. The value of `err` is a bound on the numerical error introduced by the algorithm.
|
||||
|
||||
|
||||
::: {.callout-note}
|
||||
### This is currently broken
|
||||
|
||||
The following isn't working with `SymbolicNumericIntegration` version `v"1.4.0"`. For now, the cells are not evaluated.
|
||||
|
||||
:::
|
||||
|
||||
To see, we have:
|
||||
|
||||
|
||||
```{julia}
|
||||
#| eval: false
|
||||
using SymbolicNumericIntegration
|
||||
@variables x
|
||||
|
||||
@ -871,7 +875,6 @@ Powers of trig functions have antiderivatives, as can be deduced using integrati
|
||||
|
||||
|
||||
```{julia}
|
||||
#| eval: false
|
||||
u,v,w = integrate(sin(x)^5)
|
||||
```
|
||||
|
||||
@ -879,7 +882,6 @@ The derivative of `u` matches up to some numeric tolerance:
|
||||
|
||||
|
||||
```{julia}
|
||||
#| eval: false
|
||||
Symbolics.derivative(u, x) - sin(x)^5
|
||||
```
|
||||
|
||||
|
16
quarto/derivatives/Project.toml
Normal file
16
quarto/derivatives/Project.toml
Normal file
@ -0,0 +1,16 @@
|
||||
[deps]
|
||||
CalculusWithJulia = "a2e0e22d-7d4c-5312-9169-8b992201a882"
|
||||
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
|
||||
DualNumbers = "fa6b7ba4-c1ee-5f82-b5fc-ecf0adba8f74"
|
||||
ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210"
|
||||
IJulia = "7073ff75-c697-5162-941a-fcdaad2a7d2a"
|
||||
ImplicitEquations = "95701278-4526-5785-aba3-513cca398f19"
|
||||
PlotlyBase = "a03496cd-edff-5a9b-9e67-9cda94a718b5"
|
||||
PlotlyKaleido = "f2990250-8cf9-495f-b13a-cce12b45703c"
|
||||
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
|
||||
Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
|
||||
SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b"
|
||||
SymPy = "24249f21-da20-56a4-8eb1-6a02cf4ae2e6"
|
||||
TaylorSeries = "6aa5eb33-94cf-58f4-a9d0-e4b2c4fc25ea"
|
||||
TermInterface = "8ea1fca8-c5ef-4a55-8b96-4e9afe9c9a3c"
|
||||
Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d"
|
1
quarto/derivatives/_pdf_index.qmd
Symbolic link
1
quarto/derivatives/_pdf_index.qmd
Symbolic link
@ -0,0 +1 @@
|
||||
../derivatives.qmd
|
86
quarto/derivatives/condition.qmd
Normal file
86
quarto/derivatives/condition.qmd
Normal file
@ -0,0 +1,86 @@
|
||||
## Conditioning and stability
|
||||
|
||||
In Part III of @doi:10.1137/1.9781611977165 we find language of numerical analysis useful to formally describe the zero-finding problem. Key concepts are errors, conditioning, and stability.
|
||||
|
||||
Abstractly a *problem* is a mapping, or function, from a domain $X$ of data to a range $Y$ of solutions. Both $X$ and $Y$ have a sense of distance given by a *norm*. A norm is a generalization of the absolute value.
|
||||
|
||||
|
||||
> A *well-conditioned* problem is one with the property that all small perturbations of $x$ lead to only small changes in $f(x)$.
|
||||
|
||||
This sense of "small" is quantified through a *condition number*.
|
||||
|
||||
If we let $\delta_x$ be a small perturbation of $x$ then $\delta_f = f(x + \delta_x) - f(x)$.
|
||||
|
||||
The *forward error* is $\lVert\delta_f\rVert = \lVert f(x+\delta_x) - f(x)\rVert$, the *relative forward error* is $\lVert\delta_f\rVert/\lVert f\rVert = \lVert f(x+\delta_x) - f(x)\rVert/ \lVert f(x)\rVert$.
|
||||
|
||||
The *backward error* is $\lVert\delta_x\rVert$, the *relative backward error* is $\lVert\delta_x\rVert / \lVert x\rVert$.
|
||||
|
||||
The *absolute condition number* $\hat{\kappa}$ is worst case of this ratio $\lVert\delta_f\rVert/ \lVert\delta_x\rVert$ as the perturbation size shrinks to $0$.
|
||||
The relative condition number $\kappa$ divides $\lVert\delta_f\rVert$ by $\lVert f(x)\rVert$ and $\lVert\delta_x\rVert$ by $\lVert x\rVert$ before taking the ratio.
|
||||
|
||||
|
||||
A *problem* is a mathematical concept, an *algorithm* the computational version. Algorithms may differ for many reasons, such as floating point errors, tolerances, etc. We use notation $\tilde{f}$ to indicate the algorithm.
|
||||
|
||||
The absolute error in the algorithm is $\lVert\tilde{f}(x) - f(x)\rVert$, the relative error divides by $\lVert f(x)\rVert$. A good algorithm would have smaller relative errors.
|
||||
|
||||
An algorithm is called *stable* if
|
||||
|
||||
$$
|
||||
\frac{\lVert\tilde{f}(x) - f(\tilde{x})\rVert}{\lVert f(\tilde{x})\rVert}
|
||||
$$
|
||||
|
||||
is *small* for *some* $\tilde{x}$ relatively near $x$, $\lVert\tilde{x}-x\rVert/\lVert x\rVert$.
|
||||
|
||||
> "A *stable* algorithm gives nearly the right answer to nearly the right question."
|
||||
|
||||
(The answer it gives is $\tilde{f}(x)$, the nearly right question is $f(\tilde{x})$.)
|
||||
|
||||
A related concept is an algorithm $\tilde{f}$ for a problem $f$ is *backward stable* if for each $x \in X$,
|
||||
|
||||
$$
|
||||
\tilde{f}(x) = f(\tilde{x})
|
||||
$$
|
||||
|
||||
for some $\tilde{x}$ with $\lVert\tilde{x} - x\rVert/\lVert x\rVert$ is small.
|
||||
|
||||
> "A backward stable algorithm gives exactly the right answer to nearly the right question."
|
||||
|
||||
|
||||
The concepts are related by Trefethen and Bao's Theorem 15.1 which says for a backward stable algorithm the relative error $\lVert\tilde{f}(x) - f(x)\rVert/\lVert f(x)\rVert$ is small in a manner proportional to the relative condition number.
|
||||
|
||||
Applying this to the zero-finding we follow @doi:10.1137/1.9781611975086.
|
||||
|
||||
To be specific, the problem is finding a zero of $f$ starting at an initial point $x_0$. The data is $(f, x_0)$, the solution is $r$ a zero of $f$.
|
||||
|
||||
Take the algorithm as Newton's method. Any implementation must incorporate tolerances, so this is a computational approximation to the problem. The data is the same, but technically we use $\tilde{f}$ for the function, as any computation is dependent on machine implementations. The output is $\tilde{r}$ an *approximate* zero.
|
||||
|
||||
Suppose for sake of argument that $\tilde{f}(x) = f(x) + \epsilon$ and $r$ is a root of $f$ and $\tilde{r}$ is a root of $\tilde{f}$. Then by linearization:
|
||||
|
||||
\begin{align*}
|
||||
0 &= \tilde{f}(\tilde r) \\
|
||||
&= f(r + \delta) + \epsilon\\
|
||||
&\approx f(r) + f'(r)\delta + \epsilon\\
|
||||
&= 0 + f'(r)\delta + \epsilon
|
||||
\end{align*}
|
||||
|
||||
Rearranging gives $\lVert\delta/\epsilon\rVert \approx 1/\lVert f'(r)\rVert$ leading to:
|
||||
|
||||
> The absolute condition number is $\hat{\kappa}_r = |f'(r)|^{-1}$.
|
||||
|
||||
|
||||
The error formula in Newton's method includes the derivative in the denominator, so we see large condition numbers are tied into larger errors.
|
||||
|
||||
Now consider $g(x) = f(x) - f(\tilde{r})$. Call $f(\tilde{r})$ the residual. We have $g$ is near $f$ if the residual is small. The algorithm will solve $(g, x_0)$ with $\tilde{r}$, so with a small residual an exact solution to an approximate question will be found. Driscoll and Braun state
|
||||
|
||||
> The backward error in a root estimate is equal to the residual.
|
||||
|
||||
|
||||
Practically these two observations lead to
|
||||
|
||||
* If there is a large condition number, it may not be possible to find an approximate root near the real root.
|
||||
|
||||
* A tolerance in an algorithm should consider both the size of $x_{n} - x_{n-1}$ and the residual $f(x_n)$.
|
||||
|
||||
For the first observation, the example of Wilkinson's polynomial is often used where $f(x) = (x-1)\cdot(x-2)\cdot \cdots\cdot(x-20)$. When expanded this function has exactness issues of typical floating point values, the condition number is large and some of the roots found are quite different from the mathematical values.
|
||||
|
||||
The second observation helps explain why a problem like finding the zero of $f(x) = x \cdot \exp(x)$ using Newton's method starting at $2$ might return a value like $5.89\dots$. The residual is checked to be zero in a *relative* manner which would basically use a tolerance of `atol + abs(xn)*rtol`. Functions with asymptotes of $0$ will eventually be smaller than this value.
|
@ -12,7 +12,7 @@ using Plots
|
||||
plotly()
|
||||
using SymPy
|
||||
using Roots
|
||||
using Polynomials # some name clash with SymPy
|
||||
import Polynomials: variable, Polynomial, RationalFunction # avoid name clash
|
||||
```
|
||||
|
||||
|
||||
@ -162,8 +162,8 @@ This sort of analysis can be automated. The plot "recipe" for polynomials from t
|
||||
|
||||
|
||||
```{julia}
|
||||
xₚ = variable(Polynomial)
|
||||
plot(f(xₚ)) # f(xₚ) of Polynomial type
|
||||
𝐱 = variable(Polynomial)
|
||||
plot(f(𝐱)) # f(𝐱) of Polynomial type
|
||||
```
|
||||
|
||||
##### Example
|
||||
@ -219,8 +219,8 @@ Again, this sort of analysis can be systematized. The rational function type in
|
||||
|
||||
|
||||
```{julia}
|
||||
xᵣ = variable(RationalFunction)
|
||||
plot(f(xᵣ)) # f(x) of RationalFunction type
|
||||
𝐱 = variable(RationalFunction)
|
||||
plot(f(𝐱)) # f(x) of RationalFunction type
|
||||
```
|
||||
|
||||
##### Example
|
||||
|
@ -730,13 +730,13 @@ $$
|
||||
\frac{f(g(x+h)) - f(g(x))}{h} = \frac{f(g(x+h)) - f(g(x))}{g(x+h) - g(x)} \cdot \frac{g(x+h) - g(x)}{h}.
|
||||
$$
|
||||
|
||||
The left hand side will converge to the derivative of $u(x)$ or $[f(g(x))]'$.
|
||||
The left-hand side will converge to the derivative of $u(x)$ or $[f(g(x))]'$.
|
||||
|
||||
|
||||
The right most part of the right side would have a limit $g'(x)$, were we to let $h$ go to $0$.
|
||||
The right-most part of the right-hand side would have a limit $g'(x)$, were we to let $h$ go to $0$.
|
||||
|
||||
|
||||
It isn't obvious, but the left part of the right side has the limit $f'(g(x))$. This would be clear if *only* $g(x+h) = g(x) + h$, for then the expression would be exactly the limit expression with $c=g(x)$. But, alas, except to some hopeful students and some special cases, it is definitely not the case in general that $g(x+h) = g(x) + h$ - that right parentheses actually means something. However, it is *nearly* the case that $g(x+h) = g(x) + kh$ for some $k$ and this can be used to formulate a proof (one of the two detailed [here](http://en.wikipedia.org/wiki/Chain_rule#Proofs) and [here](http://kruel.co/math/chainrule.pdf)).
|
||||
It isn't obvious, but the left part of the right-hand side has the limit $f'(g(x))$. This would be clear if *only* $g(x+h) = g(x) + h$, for then the expression would be exactly the limit expression with $c=g(x)$. But, alas, except to some hopeful students and some special cases, it is definitely not the case in general that $g(x+h) = g(x) + h$ - that right parentheses actually means something. However, it is *nearly* the case that $g(x+h) = g(x) + kh$ for some $k$ and this can be used to formulate a proof (one of the two detailed [here](http://en.wikipedia.org/wiki/Chain_rule#Proofs) and [here](http://kruel.co/math/chainrule.pdf)).
|
||||
|
||||
|
||||
Combined, we would end up with:
|
||||
|
@ -162,9 +162,10 @@ This question can be answered by considering the first derivative.
|
||||
|
||||
> *The first derivative test*: If $c$ is a critical point for $f(x)$ and *if* $f'(x)$ changes sign at $x=c$, then $f(c)$ will be either a relative maximum or a relative minimum.
|
||||
>
|
||||
> * It will be a relative maximum if the derivative changes sign from $+$ to $-$.
|
||||
> * It will be a relative minimum if the derivative changes sign from $-$ to $+$.
|
||||
> * If $f'(x)$ does not change sign at $c$, then $f(c)$ is *not* a relative maximum or minimum.
|
||||
> * $f$ will have a relative maximum at $c$ if the derivative changes sign from $+$ to $-$.
|
||||
> * $f$ will have a relative minimum at $c$ if the derivative changes sign from $-$ to $+$.
|
||||
>
|
||||
> Further, If $f'(x)$ does *not* change sign at $c$, then $f$ will *not* have a relative maximum or minimum at $c$.
|
||||
|
||||
|
||||
|
||||
@ -185,7 +186,7 @@ f(x) = exp(-abs(x)) * cos(pi * x)
|
||||
plotif(f, f', -3, 3)
|
||||
```
|
||||
|
||||
We can see the first derivative test in action: at the peaks and valleys – the relative extrema – the color changes. This is because $f'$ is changing sign as the function changes from increasing to decreasing or vice versa.
|
||||
We can see the first derivative test in action: at the peaks and valleys – the relative extrema – the highlighting changes. This is because $f'$ is changing sign as the function changes from increasing to decreasing or vice versa.
|
||||
|
||||
|
||||
This function has a critical point at $0$, as can be seen. It corresponds to a point where the derivative does not exist. It is still identified through `find_zeros`, which picks up zeros and in case of discontinuous functions, like `f'`, zero crossings:
|
||||
@ -493,8 +494,8 @@ Concave up functions are "opening" up, and often clearly $U$-shaped, though that
|
||||
|
||||
> The **second derivative test**: If $c$ is a critical point of $f(x)$ with $f''(c)$ existing in a neighborhood of $c$, then
|
||||
>
|
||||
> * The value $f(c)$ will be a relative maximum if $f''(c) > 0$,
|
||||
> * The value $f(c)$ will be a relative minimum if $f''(c) < 0$, and
|
||||
> * $f$ will have a relative maximum at the critical point $c$ if $f''(c) > 0$,
|
||||
> * $f$ will have a relative minimum at the critical point $c$ if $f''(c) < 0$, and
|
||||
> * *if* $f''(c) = 0$ the test is *inconclusive*.
|
||||
|
||||
|
||||
|
@ -40,7 +40,11 @@ tangent(f, c) = x -> f(c) + f'(c) * (x - c)
|
||||
(Recall, the `->` indicates that an anonymous function is being generated.)
|
||||
|
||||
|
||||
This function along with the `f'` notation for automatic derivatives is defined in the `CalculusWithJulia` package.
|
||||
This function along with the `f'` notation for automatic derivatives is defined in the `CalculusWithJulia` package, though a bit differently:
|
||||
|
||||
```{julia}
|
||||
tangent(sin, pi/4)
|
||||
```
|
||||
|
||||
|
||||
We make some graphs with tangent lines:
|
||||
|
25
quarto/derivatives/make_pdf.jl
Normal file
25
quarto/derivatives/make_pdf.jl
Normal file
@ -0,0 +1,25 @@
|
||||
module Make
|
||||
# makefile for generating typst pdfs
|
||||
# per directory usage
|
||||
|
||||
dir = "derivatives"
|
||||
files = (
|
||||
"derivatives",
|
||||
"numeric_derivatives",
|
||||
"symbolic_derivatives",
|
||||
"mean_value_theorem",
|
||||
"optimization",
|
||||
"first_second_derivatives",
|
||||
"curve_sketching",
|
||||
"linearization",
|
||||
"newtons_method",
|
||||
"more_zeros",
|
||||
"lhospitals_rule",
|
||||
"implicit_differentiation",
|
||||
"related_rates",
|
||||
"taylor_series_polynomials",
|
||||
)
|
||||
|
||||
include("../_make_pdf.jl")
|
||||
main()
|
||||
end
|
@ -306,7 +306,7 @@ annotate!([(0,j₀,text("a", :bottom)),
|
||||
## The mean value theorem
|
||||
|
||||
|
||||
We are driving south and in one hour cover 70 miles. If the speed limit is 65 miles per hour, were we ever speeding? We'll we averaged more than the speed limit so we know the answer is yes, but why? Speeding would mean our instantaneous speed was more than the speed limit, yet we only know for sure our *average* speed was more than the speed limit. The mean value tells us that if some conditions are met, then at some point (possibly more than one) we must have that our instantaneous speed is equal to our average speed.
|
||||
We are driving south and in one hour cover 70 miles. If the speed limit is 65 miles per hour, were we ever speeding? Well we averaged more than the speed limit so we know the answer is yes, but why? Speeding would mean our instantaneous speed was more than the speed limit, yet we only know for sure our *average* speed was more than the speed limit. The mean value tells us that if some conditions are met, then at some point (possibly more than one) we must have that our instantaneous speed is equal to our average speed.
|
||||
|
||||
|
||||
The mean value theorem is a direct generalization of Rolle's theorem.
|
||||
@ -404,7 +404,7 @@ board.create('tangent', [r], {strokeColor:'#ff0000'});
|
||||
line = board.create('line',[p[0],p[1]],{strokeColor:'#ff0000',dash:1});
|
||||
```
|
||||
|
||||
This interactive example can also be found at [jsxgraph](http://jsxgraph.uni-bayreuth.de/wiki/index.php?title=Mean_Value_Theorem). It shows a cubic polynomial fit to the $4$ adjustable points labeled A through D. The secant line is drawn between points A and B with a dashed line. A tangent line – with the same slope as the secant line – is identified at a point $(\alpha, f(\alpha))$ where $\alpha$ is between the points A and B. That this can always be done is a conseuqence of the mean value theorem.
|
||||
This interactive example can also be found at [jsxgraph](http://jsxgraph.uni-bayreuth.de/wiki/index.php?title=Mean_Value_Theorem). It shows a cubic polynomial fit to the $4$ adjustable points labeled A through D. The secant line is drawn between points A and B with a dashed line. A tangent line – with the same slope as the secant line – is identified at a point $(\alpha, f(\alpha))$ where $\alpha$ is between the points A and B. That this can always be done is a consequence of the mean value theorem.
|
||||
|
||||
|
||||
##### Example
|
||||
|
@ -864,7 +864,7 @@ The calculation that produces the quadratic convergence now becomes:
|
||||
|
||||
$$
|
||||
\begin{align*}
|
||||
x_{i+1} - \alpha &= (x_i - \alpha) - \frac{1}{k}(x_i-\alpha - \frac{f''(\xi)}{2f'(x_i)}(x_i-\alpha)^2)
|
||||
x_{i+1} - \alpha &= (x_i - \alpha) - \frac{1}{k}(x_i-\alpha - \frac{f''(\xi)}{2f'(x_i)}(x_i-\alpha)^2) \\
|
||||
&= \frac{k-1}{k} (x_i-\alpha) + \frac{f''(\xi)}{2kf'(x_i)}(x_i-\alpha)^2.
|
||||
\end{align*}
|
||||
$$
|
||||
|
@ -195,11 +195,10 @@ We pass in the function object, `k''`, and not the evaluated function.
|
||||
## Recap on derivatives in Julia
|
||||
|
||||
|
||||
A quick summary for finding derivatives in `Julia`, as there are $3$ different manners:
|
||||
|
||||
A quick summary of the $3$ different ways for finding derivatives in `Julia` presented in these notes:
|
||||
|
||||
* Symbolic derivatives are found using `diff` from `SymPy`
|
||||
* Automatic derivatives are found using the notation `f'` using `ForwardDiff.derivative`
|
||||
* Automatic derivatives are found using the notation `f'` which utilizes `ForwardDiff.derivative`
|
||||
* approximate derivatives at a point, `c`, for a given `h` are found with `(f(c+h)-f(c))/h`.
|
||||
|
||||
|
||||
|
@ -38,7 +38,7 @@ The main tool is the extreme value theorem of Bolzano and Fermat's theorem about
|
||||
Though not all of our problems lend themselves to a description of a continuous function on a closed interval, if they do, we have an algorithmic prescription to find the absolute extrema of a function:
|
||||
|
||||
|
||||
1. Find the critical points. For this we can use a root-finding algorithm like `find_zero`.
|
||||
1. Find the critical points. For this we can use a root-finding algorithm like `find_zero` or `find_zeros`.
|
||||
2. Evaluate the function values at the critical points and at the end points.
|
||||
3. Identify the largest and smallest values.
|
||||
|
||||
@ -65,7 +65,9 @@ gr()
|
||||
function perimeter_area_graphic_graph(n)
|
||||
h = 1 + 2n
|
||||
w = 10-h
|
||||
plt = plot([0,0,w,w,0], [0,h,h,0,0], legend=false, size=fig_size,
|
||||
plt = plot(Shape([0,0,w,w,0], [0,h,h,0,0]), legend=false,
|
||||
fill=(:gray, 0.1),
|
||||
size=fig_size,
|
||||
xlim=(0,10), ylim=(0,10))
|
||||
scatter!(plt, [w], [h], color=:orange, markersize=5)
|
||||
annotate!(plt, [(w/2, h/2, "Area=$(round(w*h,digits=1))")])
|
||||
@ -153,12 +155,16 @@ Now we can solve graphically as before, or numerically, such as here where we se
|
||||
find_zeros(A', 0, 10) # find_zeros in `Roots`,
|
||||
```
|
||||
|
||||
(As a reminder, the notation `A'` is defined in `CalculusWithJulia` using the `derivative` function from the `ForwardDiff` package.)
|
||||
|
||||
|
||||
:::{.callout-note}
|
||||
## Note
|
||||
Look at the last definition of `A`. The function `A` appears on both sides, though on the left side with one argument and on the right with two. These are two "methods" of a *generic* function, `A`. `Julia` allows multiple definitions for the same name as long as the arguments (their number and type) can disambiguate which to use. In this instance, when one argument is passed in then the last definition is used (`A(b,h(b))`), whereas if two are passed in, then the method that multiplies both arguments is used. The advantage of multiple dispatch is illustrated: the same concept - area - has one function name, though there may be different ways to compute the area, so there is more than one implementation.
|
||||
Look at the last definition of `A`. The function `A` appears on both sides, though on the left side with one argument and on the right with two. These are two "methods" of a *generic* function, `A`. `Julia` allows multiple definitions (methods) for the same function name (a generic function) as long as the arguments (their number and type) can disambiguate which to use. In this instance, when one argument is passed in then the last definition is used (`A(b, h(b))`), whereas if two are passed in, then the method that multiplies both arguments is used. The advantage of multiple dispatch is illustrated: the same concept - area - has one function name, though there may be different ways to compute the area, so there is more than one implementation.
|
||||
|
||||
:::
|
||||
|
||||
:::{.callout-note}
|
||||
## Alternatively ...
|
||||
If using composition for substitution is too complicated, the step of creating `A(b,h)` can be skipped by just using `h(b)` when defining `A`: `A(b) = b * h(b)`.
|
||||
|
||||
:::
|
||||
|
||||
@ -203,7 +209,7 @@ In `Julia` this is
|
||||
|
||||
|
||||
```{julia}
|
||||
Aᵣ(x, y) = x*y + pi*(x/2)^2 / 2
|
||||
A(x, y) = x*y + pi*(x/2)^2 / 2
|
||||
```
|
||||
|
||||
The perimeter consists of $3$ sides of the rectangle and the perimeter of half a circle ($\pi r$, with $r=x/2$):
|
||||
@ -224,7 +230,7 @@ And then we substitute in `y(x)` for `y` in the area formula through:
|
||||
|
||||
|
||||
```{julia}
|
||||
Aᵣ(x) = Aᵣ(x, y(x))
|
||||
A(x) = A(x, y(x))
|
||||
```
|
||||
|
||||
Of course both $x$ and $y$ are non-negative. The latter forces $x$ to be no more than $x=20/(1+\pi/2)$.
|
||||
@ -237,7 +243,7 @@ We begin by simply graphing and estimating the values of the maximum and where i
|
||||
|
||||
|
||||
```{julia}
|
||||
plot(Aᵣ, 0, 20/(1+pi/2))
|
||||
plot(A, 0, 20/(1+pi/2))
|
||||
```
|
||||
|
||||
The naked eye sees that maximum value is somewhere around $27$ and occurs at $x\approx 5.6$. Clearly from the graph, we know the maximum value happens at the critical point and there is only one such critical point.
|
||||
@ -247,7 +253,7 @@ As reading the maximum from the graph is more difficult than reading a $0$ of a
|
||||
|
||||
|
||||
```{julia}
|
||||
plot(Aᵣ', 5.5, 5.7)
|
||||
plot(A', 5.5, 5.7)
|
||||
```
|
||||
|
||||
We confirm that the critical point is around $5.6$.
|
||||
@ -259,18 +265,21 @@ We confirm that the critical point is around $5.6$.
|
||||
Rather than zoom in graphically, we now use a root-finding algorithm, to find a more precise value of the zero of $A'$. We know that the maximum will occur at a critical point, a zero of the derivative. The `find_zero` function from the `Roots` package provides a non-linear root-finding algorithm based on the bisection method. The only thing to keep track of is that solving $f'(x) = 0$ means we use the derivative and not the original function.
|
||||
|
||||
|
||||
We see from the graph that $[0, 20/(1+\pi/2)]$ will provide a bracket, as there is only one relative maximum:
|
||||
In a few sections we will see how to use `find_zero` to find a zero *near* an initial guess, but for now we will use `find_zero` to find a zero *between* two values which form a bracketing interval.
|
||||
|
||||
|
||||
From the graph, it is clear that $[0, 20/(1+\pi/2)]$ will provide a bracket for the derivative, as there is only one relative maximum:
|
||||
|
||||
|
||||
```{julia}
|
||||
x′ = find_zero(Aᵣ', (0, 20/(1+pi/2)))
|
||||
x′ = find_zero(A', (0, 20/(1+pi/2)))
|
||||
```
|
||||
|
||||
This value is the lone critical point, and in this case gives the position of the value that will maximize the function. The value and maximum area are then given by:
|
||||
|
||||
|
||||
```{julia}
|
||||
(x′, Aᵣ(x′))
|
||||
(x′, A(x′))
|
||||
```
|
||||
|
||||
(Compare this answer to the previous, is the square the figure of greatest area for a fixed perimeter, or just the figure amongst all rectangles? See [Isoperimetric inequality](https://en.wikipedia.org/wiki/Isoperimetric_inequality) for an answer.)
|
||||
@ -279,11 +288,11 @@ This value is the lone critical point, and in this case gives the position of th
|
||||
### Using `argmax` to identify where a function is maximized
|
||||
|
||||
|
||||
This value that maximizes a function is sometimes referred to as the *argmax*, or argument which maximizes the function. In `Julia` the `argmax(f,domain)` function is defined to "Return a value $x$ in the domain of $f$ for which $f(x)$ is maximized. If there are multiple maximal values for $f(x)$ then the first one will be found." The domain is some iterable collection. In the mathematical world this would be an interval $[a,b]$, but on the computer it is an approximation, such as is returned by `range` below. Without having to take a derivative, as above, but sacrificing some accuracy, the task of identifying `x` for where `A` is maximum, could be done with
|
||||
This value that maximizes a function is sometimes referred to as the *argmax*, or argument which maximizes the function. In `Julia` the `argmax(f, domain)` function is defined to "Return a value $x$ in the domain of $f$ for which $f(x)$ is maximized. If there are multiple maximal values for $f(x)$ then the first one will be found." The domain is some iterable collection. In the mathematical world this would be an interval $[a,b]$, but on the computer it is an approximation, such as is returned by `range` below. Without having to take a derivative, as above, but sacrificing some accuracy, the task of identifying `x` for where `A` is maximum, could be done with
|
||||
|
||||
|
||||
```{julia}
|
||||
argmax(Aᵣ, range(0, 20/(1+pi/2), length=10000))
|
||||
argmax(A, range(0, 20/(1+pi/2), length=10000))
|
||||
```
|
||||
|
||||
#### A symbolic approach
|
||||
@ -293,20 +302,20 @@ We could also do the above problem symbolically with the aid of `SymPy`. Here ar
|
||||
|
||||
|
||||
```{julia}
|
||||
@syms 𝒘::real 𝒉::real
|
||||
@syms 𝐰::real 𝐡::real
|
||||
|
||||
𝑨₀ = 𝒘 * 𝒉 + pi * (𝒘/2)^2 / 2
|
||||
𝑷erim = 2*𝒉 + 𝒘 + pi * 𝒘/2
|
||||
𝒉₀ = solve(𝑷erim - 20, 𝒉)[1]
|
||||
𝑨₁ = 𝑨₀(𝒉 => 𝒉₀)
|
||||
𝒘₀ = solve(diff(𝑨₁,𝒘), 𝒘)[1]
|
||||
𝐀₀ = 𝐰 * 𝐡 + pi * (𝐰/2)^2 / 2
|
||||
𝐏erim = 2*𝐡 + 𝐰 + pi * 𝐰/2
|
||||
𝐡₀ = solve(𝐏erim - 20, 𝐡)[1]
|
||||
𝐀₁ = 𝐀₀(𝐡 => 𝐡₀)
|
||||
𝐰₀ = solve(diff(𝐀₁,𝐰), 𝐰)[1]
|
||||
```
|
||||
|
||||
We know that `𝒘₀` is the maximum in this example from our previous work. We shall see soon, that just knowing that the second derivative is negative at `𝒘₀` would suffice to know this. Here we check that condition:
|
||||
We know that `𝐰₀` is the maximum in this example from our previous work. We shall see soon, that just knowing that the second derivative is negative at `𝐰₀` would suffice to know this. Here we check that condition:
|
||||
|
||||
|
||||
```{julia}
|
||||
diff(𝑨₁, 𝒘, 𝒘)(𝒘 => 𝒘₀)
|
||||
diff(𝐀₁, 𝐰, 𝐰)(𝐰 => 𝐰₀)
|
||||
```
|
||||
|
||||
As an aside, compare the steps involved above for a symbolic solution to those of previous work for a numeric solution:
|
||||
@ -314,9 +323,9 @@ As an aside, compare the steps involved above for a symbolic solution to those o
|
||||
|
||||
```{julia}
|
||||
#| hold: true
|
||||
Aᵣ(w, h) = w*h + pi*(w/2)^2 / 2
|
||||
h(w) = (20 - w - pi * w/2) / 2
|
||||
Aᵣ(w) = A(w, h(w))
|
||||
A(w, h) = w * h + pi*(w/2)^2 / 2
|
||||
h(w) = (20 - w - pi * w/2) / 2
|
||||
A(w) = A(w, h(w))
|
||||
find_zero(A', (0, 20/(1+pi/2))) # 40 / (pi + 4)
|
||||
```
|
||||
|
||||
@ -496,7 +505,7 @@ It appears to be around $8.3$. We now use `find_zero` to refine our guess at the
|
||||
α = find_zero(T', (7, 9))
|
||||
```
|
||||
|
||||
Okay, got it. Around$8.23$. So is our minimum time
|
||||
Okay, got it. Around $8.23$. So is our minimum time.
|
||||
|
||||
|
||||
```{julia}
|
||||
@ -527,9 +536,9 @@ caption = L"""
|
||||
Image number $43$ from l'Hospital's calculus book (the first). A
|
||||
traveler leaving location $C$ to go to location $F$ must cross two
|
||||
regions separated by the straight line $AEB$. We suppose that in the
|
||||
region on the side of $C$, he covers distance $a$ in time $c$, and
|
||||
region on the side of $C$, they cover distance $a$ in time $c$, and
|
||||
that on the other, on the side of $F$, distance $b$ in the same time
|
||||
$c$. We ask through which point $E$ on the line $AEB$ he should pass,
|
||||
$c$. We ask through which point $E$ on the line $AEB$ they should pass,
|
||||
so as to take the least possible time to get from $C$ to $F$? (From
|
||||
http://www.ams.org/samplings/feature-column/fc-2016-05.)
|
||||
|
||||
@ -675,7 +684,9 @@ Here traveling directly to the point $(L,0)$ is fastest. Though travel is slower
|
||||
Maximize the function $xe^{-(1/2) x^2}$ over the interval $[0, \infty)$.
|
||||
|
||||
|
||||
Here the extreme value theorem doesn't technically apply, as we don't have a closed interval. However, **if** we can eliminate the endpoints as candidates, then we should be able to convince ourselves the maximum must occur at a critical point of $f(x)$. (If not, then convince yourself for all sufficiently large $M$ the maximum over $[0,M]$ occurs at a critical point, not an endpoint. Then let $M$ go to infinity. In general, for an optimization problem of a continuous function on the interval $(a,b)$ if the right limit at $a$ and left limit at $b$ can be ruled out as candidates, the optimal value must occur at a critical point.)
|
||||
Here the extreme value theorem doesn't technically apply, as we don't have a closed interval. However, **if** we can eliminate the endpoints as candidates, then we should be able to convince ourselves the maximum must occur at a critical point of $f(x)$. (If not, then convince yourself for all sufficiently large $M$ the maximum over $[0,M]$ occurs at a critical point, not an endpoint. Then let $M$ go to infinity.
|
||||
|
||||
In general, for an optimization problem of a continuous function on the interval $(a,b)$ if the right limit at $a$ and left limit at $b$ can be ruled out as candidates, the optimal value must occur at a critical point.)
|
||||
|
||||
|
||||
So to approach this problem we first graph it over a wide interval.
|
||||
@ -762,14 +773,14 @@ The minimum looks to be around $4$cm and is clearly between $2$cm and $6$cm. We
|
||||
|
||||
|
||||
```{julia}
|
||||
rₛₐ = find_zero(SA', (2, 6))
|
||||
r = find_zero(SA', (2, 6))
|
||||
```
|
||||
|
||||
Okay, $3.837...$ is our answer for $r$. Use this to get $h$:
|
||||
|
||||
|
||||
```{julia}
|
||||
canheight(rₛₐ)
|
||||
canheight(r)
|
||||
```
|
||||
|
||||
This produces a can which is about square in profile. This is not how most cans look though. Perhaps our model is too simple, or the cans are optimized for some other purpose than minimizing materials.
|
||||
@ -1417,7 +1428,7 @@ radioq(choices, 1)
|
||||
###### Question
|
||||
|
||||
|
||||
In [Hall](https://www.maa.org/sites/default/files/hall03010308158.pdf) we can find a dozen optimization problems related to the following figure of the parabola $y=x^2$ a point $P=(a,a^2)$, $a > 0$, and its normal line. We will do two.
|
||||
In [Hall](https://www.maa.org/sites/default/files/hall03010308158.pdf) we can find a dozen optimization problems related to the following figure of the parabola $y=x^2$ a point $P=(a,a^2)$, $a > 0$, and its normal line. We will do two here, the full set will be looked at later.
|
||||
|
||||
|
||||
```{julia}
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
{{< include ../_common_code.qmd >}}
|
||||
|
||||
This section uses this add-on package:
|
||||
This section uses the `TermInterface` add-on package.
|
||||
|
||||
|
||||
```{julia}
|
||||
@ -54,16 +54,7 @@ arguments(:(-x)), arguments(:(pi^2)), arguments(:(1 + x + x^2))
|
||||
(The last one may be surprising, but all three arguments are passed to the `+` function.)
|
||||
|
||||
|
||||
Here we define a function to decide the *arity* of an expression based on the number of arguments it is called with:
|
||||
|
||||
|
||||
```{julia}
|
||||
function arity(ex)
|
||||
n = length(arguments(ex))
|
||||
n == 1 ? Val(:unary) :
|
||||
n == 2 ? Val(:binary) : Val(:nary)
|
||||
end
|
||||
```
|
||||
`TermInterface` has an `arity` function defined by `length(arguments(ex))` that will be used for dispatch below.
|
||||
|
||||
Differentiation must distinguish between expressions, variables, and numbers. Mathematically expressions have an "outer" function, whereas variables and numbers can be directly differentiated. The `isexpr` function in `TermInterface` returns `true` when passed an expression, and `false` when passed a symbol or numeric literal. The latter two may be distinguished by `isa(..., Symbol)`.
|
||||
|
||||
@ -75,8 +66,8 @@ Here we create a function, `D`, that when it encounters an expression it *dispat
|
||||
function D(ex, var=:x)
|
||||
if isexpr(ex)
|
||||
op, args = operation(ex), arguments(ex)
|
||||
D(Val(op), arity(ex), args, var)
|
||||
elseif isa(ex, Symbol) && ex == :x
|
||||
D(Val(op), Val(arity(ex)), args, var)
|
||||
elseif isa(ex, Symbol) && ex == var
|
||||
1
|
||||
else
|
||||
0
|
||||
@ -84,6 +75,8 @@ function D(ex, var=:x)
|
||||
end
|
||||
```
|
||||
|
||||
(The use of `Val` is an idiom of `Julia` allowing dispatch on certain values such as function names and numbers.)
|
||||
|
||||
Now to develop methods for `D` for different "outside" functions and arities.
|
||||
|
||||
|
||||
@ -91,31 +84,31 @@ Addition can be unary (`:(+x)` is a valid quoting, even if it might simplify to
|
||||
|
||||
|
||||
```{julia}
|
||||
D(::Val{:+}, ::Val{:unary}, args, var) = D(first(args), var)
|
||||
D(::Val{:+}, ::Val{1}, args, var) = D(first(args), var)
|
||||
|
||||
function D(::Val{:+}, ::Val{:binary}, args, var)
|
||||
function D(::Val{:+}, ::Val{2}, args, var)
|
||||
a′, b′ = D.(args, var)
|
||||
:($a′ + $b′)
|
||||
end
|
||||
|
||||
function D(::Val{:+}, ::Val{:nary}, args, var)
|
||||
function D(::Val{:+}, ::Any, args, var)
|
||||
a′s = D.(args, var)
|
||||
:(+($a′s...))
|
||||
end
|
||||
```
|
||||
|
||||
The `args` are always held in a container, so the unary method must pull out the first one. The binary case should read as: apply `D` to each of the two arguments, and then create a quoted expression containing the sum of the results. The dollar signs interpolate into the quoting. (The "primes" are unicode notation achieved through `\prime[tab]` and not operations.) The *nary* case does something similar, only using splatting to produce the sum.
|
||||
The `args` are always held in a container, so the unary method must pull out the first one. The binary case should read as: apply `D` to each of the two arguments, and then create a quoted expression containing the sum of the results. The dollar signs interpolate into the quoting. (The "primes" are unicode notation achieved through `\prime[tab]` and not operations.) The *nary* method (which catches *any* arity besides `1` and `2`) does something similar, only using splatting to produce the sum.
|
||||
|
||||
|
||||
Subtraction must also be implemented in a similar manner, but not for the *nary* case:
|
||||
Subtraction must also be implemented in a similar manner, but not for the *nary* case, as subtraction is not associative:
|
||||
|
||||
|
||||
```{julia}
|
||||
function D(::Val{:-}, ::Val{:unary}, args, var)
|
||||
function D(::Val{:-}, ::Val{1}, args, var)
|
||||
a′ = D(first(args), var)
|
||||
:(-$a′)
|
||||
end
|
||||
function D(::Val{:-}, ::Val{:binary}, args, var)
|
||||
function D(::Val{:-}, ::Val{2}, args, var)
|
||||
a′, b′ = D.(args, var)
|
||||
:($a′ - $b′)
|
||||
end
|
||||
@ -125,15 +118,15 @@ The *product rule* is similar to addition, in that $3$ cases are considered:
|
||||
|
||||
|
||||
```{julia}
|
||||
D(op::Val{:*}, ::Val{:unary}, args, var) = D(first(args), var)
|
||||
D(op::Val{:*}, ::Val{1}, args, var) = D(first(args), var)
|
||||
|
||||
function D(::Val{:*}, ::Val{:binary}, args, var)
|
||||
function D(::Val{:*}, ::Val{2}, args, var)
|
||||
a, b = args
|
||||
a′, b′ = D.(args, var)
|
||||
:($a′ * $b + $a * $b′)
|
||||
end
|
||||
|
||||
function D(op::Val{:*}, ::Val{:nary}, args, var)
|
||||
function D(op::Val{:*}, ::Any, args, var)
|
||||
a, bs... = args
|
||||
b = :(*($(bs...)))
|
||||
a′ = D(a, var)
|
||||
@ -149,7 +142,7 @@ Division is only a binary operation, so here we have the *quotient rule*:
|
||||
|
||||
|
||||
```{julia}
|
||||
function D(::Val{:/}, ::Val{:binary}, args, var)
|
||||
function D(::Val{:/}, ::Val{2}, args, var)
|
||||
u,v = args
|
||||
u′, v′ = D(u, var), D(v, var)
|
||||
:( ($u′*$v - $u*$v′)/$v^2 )
|
||||
@ -160,7 +153,7 @@ Powers are handled a bit differently. The power rule would require checking if t
|
||||
|
||||
|
||||
```{julia}
|
||||
function D(::Val{:^}, ::Val{:binary}, args, var)
|
||||
function D(::Val{:^}, ::Val{2}, args, var)
|
||||
a, b = args
|
||||
D(:(exp($b*log($a))), var) # a > 0 assumed here
|
||||
end
|
||||
@ -170,26 +163,26 @@ That leaves the task of defining a rule to differentiate both `exp` and `log`. W
|
||||
|
||||
|
||||
```{julia}
|
||||
function D(::Val{:exp}, ::Val{:unary}, args, var)
|
||||
a = first(args)
|
||||
function D(::Val{:exp}, ::Val{1}, args, var)
|
||||
a = only(args)
|
||||
a′ = D(a, var)
|
||||
:(exp($a) * $a′)
|
||||
end
|
||||
|
||||
function D(::Val{:log}, ::Val{:unary}, args, var)
|
||||
a = first(args)
|
||||
function D(::Val{:log}, ::Val{1}, args, var)
|
||||
a = only(args)
|
||||
a′ = D(a, var)
|
||||
:(1/$a * $a′)
|
||||
end
|
||||
|
||||
function D(::Val{:sin}, ::Val{:unary}, args, var)
|
||||
a = first(args)
|
||||
function D(::Val{:sin}, ::Val{1}, args, var)
|
||||
a = only(args)
|
||||
a′ = D(a, var)
|
||||
:(cos($a) * $a′)
|
||||
end
|
||||
|
||||
function D(::Val{:cos}, ::Val{:unary}, args, var)
|
||||
a = first(args)
|
||||
function D(::Val{:cos}, ::Val{1}, args, var)
|
||||
a = only(args)
|
||||
a′ = D(a, var)
|
||||
:(-sin($a) * $a′)
|
||||
end
|
||||
@ -207,16 +200,16 @@ More functions could be included, but for this example the above will suffice, a
|
||||
|
||||
|
||||
```{julia}
|
||||
ex₁ = :(x + 2/x)
|
||||
D(ex₁, :x)
|
||||
ex = :(x + 2/x)
|
||||
D(ex, :x)
|
||||
```
|
||||
|
||||
The output does not simplify, so some work is needed to identify `1 - 2/x^2` as the answer.
|
||||
|
||||
|
||||
```{julia}
|
||||
ex₂ = :( (x + sin(x))/sin(x))
|
||||
D(ex₂, :x)
|
||||
ex = :( (x + sin(x))/sin(x))
|
||||
D(ex, :x)
|
||||
```
|
||||
|
||||
Again, simplification is not performed.
|
||||
@ -226,8 +219,8 @@ Finally, we have a second derivative taken below:
|
||||
|
||||
|
||||
```{julia}
|
||||
ex₃ = :(sin(x) - x - x^3/6)
|
||||
D(D(ex₃, :x), :x)
|
||||
ex = :(sin(x) - x - x^3/6)
|
||||
D(D(ex, :x), :x)
|
||||
```
|
||||
|
||||
The length of the expression should lead to further appreciation for simplification steps taken when doing such a computation by hand.
|
||||
|
16
quarto/differentiable_vector_calculus/Project.toml
Normal file
16
quarto/differentiable_vector_calculus/Project.toml
Normal file
@ -0,0 +1,16 @@
|
||||
[deps]
|
||||
CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b"
|
||||
CalculusWithJulia = "a2e0e22d-7d4c-5312-9169-8b992201a882"
|
||||
Contour = "d38c429a-6771-53c6-b99e-75d170b6e991"
|
||||
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
|
||||
DifferentialEquations = "0c46a032-eb83-5123-abaf-570d42b7fbaa"
|
||||
ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210"
|
||||
IJulia = "7073ff75-c697-5162-941a-fcdaad2a7d2a"
|
||||
JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6"
|
||||
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
|
||||
PlotlyBase = "a03496cd-edff-5a9b-9e67-9cda94a718b5"
|
||||
PlotlyKaleido = "f2990250-8cf9-495f-b13a-cce12b45703c"
|
||||
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
|
||||
QuadGK = "1fd47b50-473d-5c70-9696-f719f8f3bcdc"
|
||||
Roots = "f2b01f46-fcfa-551c-844a-d8ac1e96c665"
|
||||
SymPy = "24249f21-da20-56a4-8eb1-6a02cf4ae2e6"
|
1
quarto/differentiable_vector_calculus/_pdf_index.qmd
Symbolic link
1
quarto/differentiable_vector_calculus/_pdf_index.qmd
Symbolic link
@ -0,0 +1 @@
|
||||
../differentiable_vector_calculus.qmd
|
18
quarto/differentiable_vector_calculus/make_pdf.jl
Normal file
18
quarto/differentiable_vector_calculus/make_pdf.jl
Normal file
@ -0,0 +1,18 @@
|
||||
module Make
|
||||
# makefile for generating typst pdfs
|
||||
# per directory usage
|
||||
dir = "differentiable_vector_calculus"
|
||||
files = (
|
||||
"polar_coordinates",
|
||||
"vectors",
|
||||
"vector_valued_functions",
|
||||
"scalar_functions",
|
||||
"scalar_functions_applications",
|
||||
"vector_fields",
|
||||
"plots_plotting",
|
||||
)
|
||||
|
||||
include("../_make_pdf.jl")
|
||||
|
||||
main()
|
||||
end
|
@ -68,11 +68,15 @@ and loads some useful packages that will be used repeatedly.
|
||||
These notes are presented as a Quarto book. To learn more about Quarto
|
||||
books visit <https://quarto.org/docs/books>.
|
||||
|
||||
Compiling these notes into a `pdf` file with Quarto is possible with some piecing together of parts. The result is less than optimal and also quite large.
|
||||
|
||||
[Download pdf version](https://www.dropbox.com/scl/fi/vv8ee9a5xb9qbsw87f3bl/CalculusWithJulia.pdf?rlkey=u7ambk19sowelxtgiaosjozzo&st=i0hxwx29&dl=0)
|
||||
|
||||
<!--
|
||||
These notes may be compiled into a `pdf` file through Quarto. As the result is rather large, we do not provide that file for download. For the interested reader, downloading the repository, instantiating the environment, and running `quarto` to render to `pdf` in the `quarto` subdirectory should produce that file (after some time).
|
||||
-->
|
||||
|
||||
To *contribute* -- say by suggesting addition topics, correcting a
|
||||
To *contribute* -- say by suggesting additional topics, correcting a
|
||||
mistake, or fixing a typo -- click the "Edit this page" link and join the list of [contributors](https://github.com/jverzani/CalculusWithJuliaNotes.jl/graphs/contributors). Thanks to all contributors and a *very* special thanks to `@fangliu-tju` for their careful and most-appreciated proofreading.
|
||||
|
||||
## Running Julia
|
||||
|
11
quarto/integral_vector_calculus/Project.toml
Normal file
11
quarto/integral_vector_calculus/Project.toml
Normal file
@ -0,0 +1,11 @@
|
||||
[deps]
|
||||
CalculusWithJulia = "a2e0e22d-7d4c-5312-9169-8b992201a882"
|
||||
ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210"
|
||||
HCubature = "19dc6840-f33b-545b-b366-655c7e3ffd49"
|
||||
IJulia = "7073ff75-c697-5162-941a-fcdaad2a7d2a"
|
||||
PlotlyBase = "a03496cd-edff-5a9b-9e67-9cda94a718b5"
|
||||
PlotlyKaleido = "f2990250-8cf9-495f-b13a-cce12b45703c"
|
||||
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
|
||||
QuadGK = "1fd47b50-473d-5c70-9696-f719f8f3bcdc"
|
||||
Roots = "f2b01f46-fcfa-551c-844a-d8ac1e96c665"
|
||||
SymPy = "24249f21-da20-56a4-8eb1-6a02cf4ae2e6"
|
1
quarto/integral_vector_calculus/_pdf_index.qmd
Symbolic link
1
quarto/integral_vector_calculus/_pdf_index.qmd
Symbolic link
@ -0,0 +1 @@
|
||||
../integral_vector_calculus.qmd
|
@ -669,7 +669,7 @@ integrate(rho(x,y), (y, h(x), g(x)), (x, -a, a))
|
||||
##### Example (Strang)
|
||||
|
||||
|
||||
Integrate $\int_0^1 \int_y^1 \cos(x^2) dx dy$ avoiding the *impossible* integral of $\cos(x^2)$. As the integrand is continuous, Fubini's Theorem allows the interchange of the variable of integraton. The region, $R$, is a triangle in the first quadrant below the line $y=x$ and left of the line $x=1$. So we have:
|
||||
Integrate $\int_0^1 \int_y^1 \cos(x^2) dx dy$ avoiding the *impossible* integral of $\cos(x^2)$. As the integrand is continuous, Fubini's Theorem allows the interchange of the variable of integration. The region, $R$, is a triangle in the first quadrant below the line $y=x$ and left of the line $x=1$. So we have:
|
||||
|
||||
|
||||
$$
|
||||
|
17
quarto/integral_vector_calculus/make_pdf.jl
Normal file
17
quarto/integral_vector_calculus/make_pdf.jl
Normal file
@ -0,0 +1,17 @@
|
||||
module Make
|
||||
# makefile for generating typst pdfs
|
||||
# per directory usage
|
||||
|
||||
dir = "integral_vector_calculus"
|
||||
files = (
|
||||
"double_triple_integrals",
|
||||
"line_integrals",
|
||||
"div_grad_curl",
|
||||
"stokes_theorem",
|
||||
"review",
|
||||
)
|
||||
|
||||
include("../_make_pdf.jl")
|
||||
|
||||
main()
|
||||
end
|
13
quarto/integrals/Project.toml
Normal file
13
quarto/integrals/Project.toml
Normal file
@ -0,0 +1,13 @@
|
||||
[deps]
|
||||
CalculusWithJulia = "a2e0e22d-7d4c-5312-9169-8b992201a882"
|
||||
IJulia = "7073ff75-c697-5162-941a-fcdaad2a7d2a"
|
||||
PlotlyBase = "a03496cd-edff-5a9b-9e67-9cda94a718b5"
|
||||
PlotlyKaleido = "f2990250-8cf9-495f-b13a-cce12b45703c"
|
||||
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
|
||||
QuadGK = "1fd47b50-473d-5c70-9696-f719f8f3bcdc"
|
||||
Roots = "f2b01f46-fcfa-551c-844a-d8ac1e96c665"
|
||||
SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b"
|
||||
SplitApplyCombine = "03a91e81-4c3e-53e1-a0a4-9c0c8f19dd66"
|
||||
SymPy = "24249f21-da20-56a4-8eb1-6a02cf4ae2e6"
|
||||
Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d"
|
||||
UnitfulUS = "7dc9378f-8956-57ef-a780-aa31cc70ff3d"
|
1
quarto/integrals/_pdf_index.qmd
Symbolic link
1
quarto/integrals/_pdf_index.qmd
Symbolic link
@ -0,0 +1 @@
|
||||
../integrals.qmd
|
BIN
quarto/integrals/figures/integration-glass.jpeg
Normal file
BIN
quarto/integrals/figures/integration-glass.jpeg
Normal file
Binary file not shown.
After Width: | Height: | Size: 65 KiB |
Binary file not shown.
Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 65 KiB |
25
quarto/integrals/make_pdf.jl
Normal file
25
quarto/integrals/make_pdf.jl
Normal file
@ -0,0 +1,25 @@
|
||||
module Make
|
||||
# makefile for generating typst pdfs
|
||||
# per directory usage
|
||||
|
||||
dir = "integrals"
|
||||
files = (
|
||||
"area",
|
||||
"ftc",
|
||||
"substitution",
|
||||
"integration_by_parts",
|
||||
"partial_fractions",
|
||||
"improper_integrals",
|
||||
"mean_value_theorem",
|
||||
"area_between_curves",
|
||||
"center_of_mass",
|
||||
"volumes_slice",
|
||||
"arc_length",
|
||||
"surface_area",
|
||||
"twelve-qs",
|
||||
)
|
||||
|
||||
include("../_make_pdf.jl")
|
||||
|
||||
main()
|
||||
end
|
8
quarto/limits/Project.toml
Normal file
8
quarto/limits/Project.toml
Normal file
@ -0,0 +1,8 @@
|
||||
[deps]
|
||||
CalculusWithJulia = "a2e0e22d-7d4c-5312-9169-8b992201a882"
|
||||
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
|
||||
IJulia = "7073ff75-c697-5162-941a-fcdaad2a7d2a"
|
||||
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
|
||||
Richardson = "708f8203-808e-40c0-ba2d-98a6953ed40d"
|
||||
Roots = "f2b01f46-fcfa-551c-844a-d8ac1e96c665"
|
||||
SymPy = "24249f21-da20-56a4-8eb1-6a02cf4ae2e6"
|
1
quarto/limits/_pdf_index.qmd
Symbolic link
1
quarto/limits/_pdf_index.qmd
Symbolic link
@ -0,0 +1 @@
|
||||
../limits.qmd
|
BIN
quarto/limits/figures/hardrock-100.jpeg
Normal file
BIN
quarto/limits/figures/hardrock-100.jpeg
Normal file
Binary file not shown.
After Width: | Height: | Size: 179 KiB |
BIN
quarto/limits/figures/intersection-biennale.jpg
Normal file
BIN
quarto/limits/figures/intersection-biennale.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 450 KiB |
@ -322,6 +322,26 @@ It appears (and a plot over $[0,1]$ verifies) that there is one zero between $-2
|
||||
find_zero(x^3 - x + 1, (-2, -1))
|
||||
```
|
||||
|
||||
##### Example
|
||||
|
||||
Solve for a value of $x$ where `erfc(x)` is equal to `0.5`.
|
||||
|
||||
This is of the form $f(x) = c$ where $c$ is non-zero. To make this something equal to $0$, we re express as $f(x) - c = 0$. Our function to pass to `find_zero` is then `h(x) = f(x) - c`.
|
||||
|
||||
```{julia}
|
||||
f(x) = erfc(x)
|
||||
c = 0.5
|
||||
h(x) = f(x) - c # or just define f(x) = erfc(x) - 0.5
|
||||
```
|
||||
|
||||
Now the `erfc` function is always defined and decreasing from $2$ to $0$, so will cross $c = 0.5$ once. We can take a wide interval to solve this, as there is only one zero:
|
||||
|
||||
```{julia}
|
||||
find_zero(h, (-Inf, Inf)) # as wide as possible in this case
|
||||
```
|
||||
|
||||
|
||||
|
||||
##### Example
|
||||
|
||||
|
||||
@ -349,9 +369,9 @@ find_zero(h, (0, 2))
|
||||
::: {.callout-note}
|
||||
### Solving `f(x) = g(x)` and `f(x) = c`
|
||||
|
||||
The above shows a means to translate a given problem into one that can be solved with `find_zero`. Basically to solve either when a function is a non-zero constant or when a function is equal to some other function, the difference between the two sides is formed and turned into a function, called `h` above.
|
||||
The above examples show a means to translate a given problem into one that can be solved with `find_zero`. Basically to solve either when a function is a non-zero constant (`f(x) = c`) or when a function is equal to some other function (`f(x) = g(x)`), the difference between the two sides is formed and turned into a function, called `h` above.
|
||||
|
||||
If using symbolic expressions, as below, then an equation (formed by `~`) can be passed to `find_zero`:
|
||||
For symbolic expressions, as below, then, as a convenience, an equation (formed by `~`) can be passed to `find_zero`:
|
||||
|
||||
```{julia}
|
||||
@syms x
|
||||
@ -359,6 +379,8 @@ solve(cos(x) ~ x, (0, 2))
|
||||
```
|
||||
:::
|
||||
|
||||
[](https://www.gallery.ca/whats-on/touring-exhibitions-and-loans/around-the-world/canada-pavilion-at-the-venice-biennale/kapwani-kiwanga-trinket)
|
||||
|
||||
##### Example: Inverse functions
|
||||
|
||||
If $f(x)$ is *monotonic* and *continuous* over an interval $[a,b]$ then it has an *inverse function*. That is for any $y$ between $f(a)$ and $f(b)$ we can find an $x$ satisfying $y = f(x)$ with $a \leq x \leq b$. This is due, of course, to both the intermediate value theorem (which guarantees an $x$) and monotonicity (which guarantees just one $x$).
|
||||
@ -710,7 +732,7 @@ This chart of the [Hardrock 100](http://hardrock100.com/) illustrates the two co
|
||||
```{julia}
|
||||
#| echo: false
|
||||
###{{{hardrock_profile}}}
|
||||
imgfile = "figures/hardrock-100.png"
|
||||
imgfile = "figures/hardrock-100.jpeg"
|
||||
caption = """
|
||||
Elevation profile of the Hardrock 100 ultramarathon. Treating the elevation profile as a function, the absolute maximum is just about 14,000 feet and the absolute minimum about 7600 feet. These are of interest to the runner for different reasons. Also of interest would be each local maxima and local minima - the peaks and valleys of the graph - and the total elevation climbed - the latter so important/unforgettable its value makes it into the chart's title.
|
||||
"""
|
||||
@ -720,7 +742,7 @@ nothing
|
||||
```
|
||||
|
||||
[](https://hardrock100.com)
|
||||
](limits/figures/hardrock-100.jpeg)](https://hardrock100.com)
|
||||
|
||||
The extreme value theorem discusses an assumption that ensures absolute maximum and absolute minimum values exist.
|
||||
|
||||
@ -991,7 +1013,7 @@ nothing
|
||||
```
|
||||
|
||||

|
||||
](figures/cannonball.jpg)
|
||||
|
||||
In 1638, according to Amir D. [Aczel](http://books.google.com/books?id=kvGt2OlUnQ4C&pg=PA28&lpg=PA28&dq=mersenne+cannon+ball+tests&source=bl&ots=wEUd7e0jFk&sig=LpFuPoUvODzJdaoug4CJsIGZZHw&hl=en&sa=X&ei=KUGcU6OAKJCfyASnioCoBA&ved=0CCEQ6AEwAA#v=onepage&q=mersenne%20cannon%20ball%20tests&f=false), an experiment was performed in the French Countryside. A monk, Marin Mersenne, launched a cannonball straight up into the air in an attempt to help Descartes prove facts about the rotation of the earth. Though the experiment was not successful, Mersenne later observed that the time for the cannonball to go up was greater than the time to come down. ["Vertical Projection in a Resisting Medium: Reflections on Observations of Mersenne".](http://www.maa.org/publications/periodicals/american-mathematical-monthly/american-mathematical-monthly-contents-junejuly-2014)
|
||||
|
||||
|
14
quarto/limits/make_pdf.jl
Normal file
14
quarto/limits/make_pdf.jl
Normal file
@ -0,0 +1,14 @@
|
||||
module Make
|
||||
# makefile for generating typst pdfs
|
||||
# per directory usage
|
||||
|
||||
dir = "limits"
|
||||
files = (
|
||||
"limits",
|
||||
"limits_extensions",
|
||||
"continuity",
|
||||
"intermediate_value_theorem",
|
||||
)
|
||||
include("../_make_pdf.jl")
|
||||
main()
|
||||
end
|
32
quarto/make_pdf.jl
Normal file
32
quarto/make_pdf.jl
Normal file
@ -0,0 +1,32 @@
|
||||
# make pdf files
|
||||
# should run in each director julia make.jl
|
||||
|
||||
using PDFmerger
|
||||
|
||||
dirs = (
|
||||
"precalc",
|
||||
"limits",
|
||||
"derivatives",
|
||||
"integrals",
|
||||
"ODEs",
|
||||
"differentiable_vector_calculus",
|
||||
"integral_vector_calculus",
|
||||
"alternatives",
|
||||
"misc"
|
||||
)
|
||||
|
||||
function (@main)(args...)
|
||||
@info "Making index pages"
|
||||
run(`quarto render _pdf_index.qmd --to typst`)
|
||||
for d in dirs
|
||||
cd(d)
|
||||
@info "Making files in $d"
|
||||
include("make_pdf.jl")
|
||||
cd("..")
|
||||
end
|
||||
|
||||
@info "Stitch together pdfs"
|
||||
pieces = ["_pdf_index.pdf"]
|
||||
append!(pieces, dirs .* "/_pdf_index.pdf")
|
||||
merge_pdfs(pieces, "CalculusWithJulia.pdf")
|
||||
end
|
3
quarto/misc.qmd
Normal file
3
quarto/misc.qmd
Normal file
@ -0,0 +1,3 @@
|
||||
# Miscellaneous
|
||||
|
||||
This section has several small notes on `Julia`.
|
1
quarto/misc/_pdf_index.qmd
Symbolic link
1
quarto/misc/_pdf_index.qmd
Symbolic link
@ -0,0 +1 @@
|
||||
../misc.qmd
|
@ -114,7 +114,7 @@ The notebook interface has "cells" where one or more commands can be entered.
|
||||
In `IJulia`, a block of commands is sent to the kernel (the `Julia` interpreter) by typing "shift+return" or clicking on a "run" button. The output is printed below a cell, including graphics.
|
||||
|
||||
|
||||
When a cell is evaluating, the leading `[]` has an asterick (`[*]`) showing the notebook is awaiting the results of the calculation.
|
||||
When a cell is evaluating, the leading `[]` has an asterisk (`[*]`) showing the notebook is awaiting the results of the calculation.
|
||||
|
||||
|
||||
Once a cell is evaluated, the leading `[]` has a number inserted (e.g., `[1]`, as in the figure). This number indicates the order of cell evaluation. Once a notebook is interacted with, the state of the namespace need not reflect the top-to-bottom order of the notebook, but rather reflects the order of cell evaluations.
|
||||
|
16
quarto/misc/make_pdf.jl
Normal file
16
quarto/misc/make_pdf.jl
Normal file
@ -0,0 +1,16 @@
|
||||
module Make
|
||||
# makefile for generating typst pdfs
|
||||
# per directory usage
|
||||
dir = "misc"
|
||||
files = (
|
||||
"getting_started_with_julia",
|
||||
"julia_interfaces",
|
||||
"calculus_with_julia",
|
||||
"unicode",
|
||||
"quick_notes",
|
||||
)
|
||||
|
||||
include("../_make_pdf.jl")
|
||||
|
||||
main()
|
||||
end
|
307
quarto/precalc/Precalculus.typ
Normal file
307
quarto/precalc/Precalculus.typ
Normal file
@ -0,0 +1,307 @@
|
||||
// Some definitions presupposed by pandoc's typst output.
|
||||
#let blockquote(body) = [
|
||||
#set text( size: 0.92em )
|
||||
#block(inset: (left: 1.5em, top: 0.2em, bottom: 0.2em))[#body]
|
||||
]
|
||||
|
||||
#let horizontalrule = [
|
||||
#line(start: (25%,0%), end: (75%,0%))
|
||||
]
|
||||
|
||||
#let endnote(num, contents) = [
|
||||
#stack(dir: ltr, spacing: 3pt, super[#num], contents)
|
||||
]
|
||||
|
||||
#show terms: it => {
|
||||
it.children
|
||||
.map(child => [
|
||||
#strong[#child.term]
|
||||
#block(inset: (left: 1.5em, top: -0.4em))[#child.description]
|
||||
])
|
||||
.join()
|
||||
}
|
||||
|
||||
// Some quarto-specific definitions.
|
||||
|
||||
#show raw.where(block: true): set block(
|
||||
fill: luma(230),
|
||||
width: 100%,
|
||||
inset: 8pt,
|
||||
radius: 2pt
|
||||
)
|
||||
|
||||
#let block_with_new_content(old_block, new_content) = {
|
||||
let d = (:)
|
||||
let fields = old_block.fields()
|
||||
fields.remove("body")
|
||||
if fields.at("below", default: none) != none {
|
||||
// TODO: this is a hack because below is a "synthesized element"
|
||||
// according to the experts in the typst discord...
|
||||
fields.below = fields.below.amount
|
||||
}
|
||||
return block.with(..fields)(new_content)
|
||||
}
|
||||
|
||||
#let unescape-eval(str) = {
|
||||
return eval(str.replace("\\", ""))
|
||||
}
|
||||
|
||||
#let empty(v) = {
|
||||
if type(v) == "string" {
|
||||
// two dollar signs here because we're technically inside
|
||||
// a Pandoc template :grimace:
|
||||
v.matches(regex("^\\s*$")).at(0, default: none) != none
|
||||
} else if type(v) == "content" {
|
||||
if v.at("text", default: none) != none {
|
||||
return empty(v.text)
|
||||
}
|
||||
for child in v.at("children", default: ()) {
|
||||
if not empty(child) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Subfloats
|
||||
// This is a technique that we adapted from https://github.com/tingerrr/subpar/
|
||||
#let quartosubfloatcounter = counter("quartosubfloatcounter")
|
||||
|
||||
#let quarto_super(
|
||||
kind: str,
|
||||
caption: none,
|
||||
label: none,
|
||||
supplement: str,
|
||||
position: none,
|
||||
subrefnumbering: "1a",
|
||||
subcapnumbering: "(a)",
|
||||
body,
|
||||
) = {
|
||||
context {
|
||||
let figcounter = counter(figure.where(kind: kind))
|
||||
let n-super = figcounter.get().first() + 1
|
||||
set figure.caption(position: position)
|
||||
[#figure(
|
||||
kind: kind,
|
||||
supplement: supplement,
|
||||
caption: caption,
|
||||
{
|
||||
show figure.where(kind: kind): set figure(numbering: _ => numbering(subrefnumbering, n-super, quartosubfloatcounter.get().first() + 1))
|
||||
show figure.where(kind: kind): set figure.caption(position: position)
|
||||
|
||||
show figure: it => {
|
||||
let num = numbering(subcapnumbering, n-super, quartosubfloatcounter.get().first() + 1)
|
||||
show figure.caption: it => {
|
||||
num.slice(2) // I don't understand why the numbering contains output that it really shouldn't, but this fixes it shrug?
|
||||
[ ]
|
||||
it.body
|
||||
}
|
||||
|
||||
quartosubfloatcounter.step()
|
||||
it
|
||||
counter(figure.where(kind: it.kind)).update(n => n - 1)
|
||||
}
|
||||
|
||||
quartosubfloatcounter.update(0)
|
||||
body
|
||||
}
|
||||
)#label]
|
||||
}
|
||||
}
|
||||
|
||||
// callout rendering
|
||||
// this is a figure show rule because callouts are crossreferenceable
|
||||
#show figure: it => {
|
||||
if type(it.kind) != "string" {
|
||||
return it
|
||||
}
|
||||
let kind_match = it.kind.matches(regex("^quarto-callout-(.*)")).at(0, default: none)
|
||||
if kind_match == none {
|
||||
return it
|
||||
}
|
||||
let kind = kind_match.captures.at(0, default: "other")
|
||||
kind = upper(kind.first()) + kind.slice(1)
|
||||
// now we pull apart the callout and reassemble it with the crossref name and counter
|
||||
|
||||
// when we cleanup pandoc's emitted code to avoid spaces this will have to change
|
||||
let old_callout = it.body.children.at(1).body.children.at(1)
|
||||
let old_title_block = old_callout.body.children.at(0)
|
||||
let old_title = old_title_block.body.body.children.at(2)
|
||||
|
||||
// TODO use custom separator if available
|
||||
let new_title = if empty(old_title) {
|
||||
[#kind #it.counter.display()]
|
||||
} else {
|
||||
[#kind #it.counter.display(): #old_title]
|
||||
}
|
||||
|
||||
let new_title_block = block_with_new_content(
|
||||
old_title_block,
|
||||
block_with_new_content(
|
||||
old_title_block.body,
|
||||
old_title_block.body.body.children.at(0) +
|
||||
old_title_block.body.body.children.at(1) +
|
||||
new_title))
|
||||
|
||||
block_with_new_content(old_callout,
|
||||
block(below: 0pt, new_title_block) +
|
||||
old_callout.body.children.at(1))
|
||||
}
|
||||
|
||||
// 2023-10-09: #fa-icon("fa-info") is not working, so we'll eval "#fa-info()" instead
|
||||
#let callout(body: [], title: "Callout", background_color: rgb("#dddddd"), icon: none, icon_color: black) = {
|
||||
block(
|
||||
breakable: false,
|
||||
fill: background_color,
|
||||
stroke: (paint: icon_color, thickness: 0.5pt, cap: "round"),
|
||||
width: 100%,
|
||||
radius: 2pt,
|
||||
block(
|
||||
inset: 1pt,
|
||||
width: 100%,
|
||||
below: 0pt,
|
||||
block(
|
||||
fill: background_color,
|
||||
width: 100%,
|
||||
inset: 8pt)[#text(icon_color, weight: 900)[#icon] #title]) +
|
||||
if(body != []){
|
||||
block(
|
||||
inset: 1pt,
|
||||
width: 100%,
|
||||
block(fill: white, width: 100%, inset: 8pt, body))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
|
||||
#let article(
|
||||
title: none,
|
||||
subtitle: none,
|
||||
authors: none,
|
||||
date: none,
|
||||
abstract: none,
|
||||
abstract-title: none,
|
||||
cols: 1,
|
||||
margin: (x: 1.25in, y: 1.25in),
|
||||
paper: "us-letter",
|
||||
lang: "en",
|
||||
region: "US",
|
||||
font: "linux libertine",
|
||||
fontsize: 11pt,
|
||||
title-size: 1.5em,
|
||||
subtitle-size: 1.25em,
|
||||
heading-family: "linux libertine",
|
||||
heading-weight: "bold",
|
||||
heading-style: "normal",
|
||||
heading-color: black,
|
||||
heading-line-height: 0.65em,
|
||||
sectionnumbering: none,
|
||||
toc: false,
|
||||
toc_title: none,
|
||||
toc_depth: none,
|
||||
toc_indent: 1.5em,
|
||||
doc,
|
||||
) = {
|
||||
set page(
|
||||
paper: paper,
|
||||
margin: margin,
|
||||
numbering: "1",
|
||||
)
|
||||
set par(justify: true)
|
||||
set text(lang: lang,
|
||||
region: region,
|
||||
font: font,
|
||||
size: fontsize)
|
||||
set heading(numbering: sectionnumbering)
|
||||
if title != none {
|
||||
align(center)[#block(inset: 2em)[
|
||||
#set par(leading: heading-line-height)
|
||||
#if (heading-family != none or heading-weight != "bold" or heading-style != "normal"
|
||||
or heading-color != black or heading-decoration == "underline"
|
||||
or heading-background-color != none) {
|
||||
set text(font: heading-family, weight: heading-weight, style: heading-style, fill: heading-color)
|
||||
text(size: title-size)[#title]
|
||||
if subtitle != none {
|
||||
parbreak()
|
||||
text(size: subtitle-size)[#subtitle]
|
||||
}
|
||||
} else {
|
||||
text(weight: "bold", size: title-size)[#title]
|
||||
if subtitle != none {
|
||||
parbreak()
|
||||
text(weight: "bold", size: subtitle-size)[#subtitle]
|
||||
}
|
||||
}
|
||||
]]
|
||||
}
|
||||
|
||||
if authors != none {
|
||||
let count = authors.len()
|
||||
let ncols = calc.min(count, 3)
|
||||
grid(
|
||||
columns: (1fr,) * ncols,
|
||||
row-gutter: 1.5em,
|
||||
..authors.map(author =>
|
||||
align(center)[
|
||||
#author.name \
|
||||
#author.affiliation \
|
||||
#author.email
|
||||
]
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
if date != none {
|
||||
align(center)[#block(inset: 1em)[
|
||||
#date
|
||||
]]
|
||||
}
|
||||
|
||||
if abstract != none {
|
||||
block(inset: 2em)[
|
||||
#text(weight: "semibold")[#abstract-title] #h(1em) #abstract
|
||||
]
|
||||
}
|
||||
|
||||
if toc {
|
||||
let title = if toc_title == none {
|
||||
auto
|
||||
} else {
|
||||
toc_title
|
||||
}
|
||||
block(above: 0em, below: 2em)[
|
||||
#outline(
|
||||
title: toc_title,
|
||||
depth: toc_depth,
|
||||
indent: toc_indent
|
||||
);
|
||||
]
|
||||
}
|
||||
|
||||
if cols == 1 {
|
||||
doc
|
||||
} else {
|
||||
columns(cols, doc)
|
||||
}
|
||||
}
|
||||
|
||||
#set table(
|
||||
inset: 6pt,
|
||||
stroke: none
|
||||
)
|
||||
|
||||
#show: doc => article(
|
||||
title: [Precalculus topics with Julia],
|
||||
date: [2024-10-13],
|
||||
sectionnumbering: "1.",
|
||||
toc_title: [Table of contents],
|
||||
toc_depth: 3,
|
||||
cols: 1,
|
||||
doc,
|
||||
)
|
||||
|
||||
|
||||
Various pre-calculus topics
|
15
quarto/precalc/Project.toml
Normal file
15
quarto/precalc/Project.toml
Normal file
@ -0,0 +1,15 @@
|
||||
[deps]
|
||||
CalculusWithJulia = "a2e0e22d-7d4c-5312-9169-8b992201a882"
|
||||
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
|
||||
IJulia = "7073ff75-c697-5162-941a-fcdaad2a7d2a"
|
||||
IntervalArithmetic = "d1acc4aa-44c8-5952-acd4-ba5d80a2a253"
|
||||
LaTeXStrings = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f"
|
||||
Measures = "442fdcdd-2543-5da2-b0f3-8c86c306513e"
|
||||
PlotlyBase = "a03496cd-edff-5a9b-9e67-9cda94a718b5"
|
||||
PlotlyKaleido = "f2990250-8cf9-495f-b13a-cce12b45703c"
|
||||
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
|
||||
Polynomials = "f27b6e38-b328-58d1-80ce-0feddd5e7a45"
|
||||
Primes = "27ebfcd6-29c5-5fa9-bf4b-fb8fc14df3ae"
|
||||
RealPolynomialRoots = "87be438c-38ae-47c4-9398-763eabe5c3be"
|
||||
Roots = "f2b01f46-fcfa-551c-844a-d8ac1e96c665"
|
||||
SymPy = "24249f21-da20-56a4-8eb1-6a02cf4ae2e6"
|
1
quarto/precalc/_pdf_index.qmd
Symbolic link
1
quarto/precalc/_pdf_index.qmd
Symbolic link
@ -0,0 +1 @@
|
||||
../precalc.qmd
|
@ -440,7 +440,7 @@ This follows usual mathematical convention, but is a source of potential confusi
|
||||
On the Google calculator, the square root button has a single purpose: for the current value find a square root if possible, and if not signal an error (such as what happens if the value is negative). For more general powers, the $x^y$ key can be used.
|
||||
|
||||
|
||||
In `Julia`, functions are used to perform the actions that a specialized button may do on the calculator. `Julia` provides many standard mathematical functions - more than there could be buttons on a calculator - and allows the user to easily define their own functions. For example, `Julia` provides the same set of functions as on Google's calculator, though with different names. For logarithms, $\ln$ becomes `log` and $\log$ is `log10` (computer programs almost exclusively reserve `log` for the natural log); for factorials, $x!$, there is `factorial`; for powers $\sqrt{}$ becomes `sqrt`, $EXP$ becomes `exp`, and $x^y$ is computed with the infix operator `^`. For the trigonometric functions, the basic names are similar: `sin`, `cos`, `tan`. These expect radians. For angles in degrees, the convenience functions `sind`, `cosd`, and `tand` are provided. On the calculator, inverse functions like $\sin^{-1}(x)$ are done by combining $Inv$ with $\sin$. With `Julia`, the function name is `asin`, an abbreviation for "arcsine." (Which is a good thing, as the notation using a power of $-1$ is often a source of confusion and is not supported by `Julia` without work.) Similarly, there are `asind`, `acos`, `acosd`, `atan`, and `atand` functions available to the `Julia` user.
|
||||
In `Julia`, functions are used to perform the actions that a specialized button may do on the calculator. `Julia` provides many standard mathematical functions - more than there could be buttons on a calculator - and allows the user to easily define their own functions. For example, `Julia` provides the same set of functions as on Google's calculator, though with different names. For logarithms, $\ln$ becomes `log` and $\log$ is `log10` (computer programs almost exclusively reserve `log` for the natural log); for factorials, $x!$, there is `factorial`; for powers $\sqrt{...}$ becomes `sqrt`, $EXP$ becomes `exp`, and $x^y$ is computed with the infix operator `^`. For the trigonometric functions, the basic names are similar: `sin`, `cos`, `tan`. These expect radians. For angles in degrees, the convenience functions `sind`, `cosd`, and `tand` are provided. On the calculator, inverse functions like $\sin^{-1}(x)$ are done by combining $Inv$ with $\sin$. With `Julia`, the function name is `asin`, an abbreviation for "arcsine." (Which is a good thing, as the notation using a power of $-1$ is often a source of confusion and is not supported by `Julia` without work.) Similarly, there are `asind`, `acos`, `acosd`, `atan`, and `atand` functions available to the `Julia` user.
|
||||
|
||||
|
||||
The following table summarizes the above:
|
||||
@ -452,7 +452,7 @@ using DataFrames
|
||||
calc = [
|
||||
L" $+$, $-$, $\times$, $\div$",
|
||||
L"x^y",
|
||||
L"\sqrt{}, \sqrt[3]{}",
|
||||
L"\sqrt{...}, \sqrt[3]{...}",
|
||||
L"e^x",
|
||||
L" $\ln$, $\log$",
|
||||
L"\sin, \cos, \tan, \sec, \csc, \cot",
|
||||
|
@ -1289,11 +1289,11 @@ import IntervalArithmetic
|
||||
```
|
||||
|
||||
```{julia}
|
||||
I1 = IntervalArithmetic.Interval(-Inf, Inf)
|
||||
I1 = IntervalArithmetic.interval(-Inf, Inf)
|
||||
```
|
||||
|
||||
```{julia}
|
||||
I2 = IntervalArithmetic.Interval(0, Inf)
|
||||
I2 = IntervalArithmetic.interval(0, Inf)
|
||||
```
|
||||
|
||||
The main feature of the package is not to construct intervals, but rather to *rigorously* bound with an interval the output of the image of a closed interval under a function. That is, for a function $f$ and *closed* interval $[a,b]$, a bound for the set $\{f(x) \text{ for } x \text{ in } [a,b]\}$. When `[a,b]` is the domain of $f$, then this is a bound for the range of $f$.
|
||||
@ -1303,7 +1303,7 @@ For example the function $f(x) = x^2 + 2$ had a domain of all real $x$, the rang
|
||||
|
||||
|
||||
```{julia}
|
||||
ab = IntervalArithmetic.Interval(-Inf, Inf)
|
||||
ab = IntervalArithmetic.interval(-Inf, Inf)
|
||||
u(x) = x^2 + 2
|
||||
u(ab)
|
||||
```
|
||||
@ -1344,7 +1344,7 @@ Now consider the evaluation
|
||||
```{julia}
|
||||
#| hold: true
|
||||
f(x) = x^x
|
||||
I = IntervalArithmetic.Interval(0, Inf)
|
||||
I = IntervalArithmetic.interval(0, Inf)
|
||||
f(I)
|
||||
```
|
||||
|
||||
|
@ -182,11 +182,13 @@ Finally, let's investigate the fact that the harmonic mean, $2/(1/a + 1/b)$ is l
|
||||
|
||||
```{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
|
||||
let
|
||||
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
|
||||
end
|
||||
```
|
||||
|
||||
## Chaining, combining expressions: absolute values
|
||||
|
27
quarto/precalc/make_pdf.jl
Normal file
27
quarto/precalc/make_pdf.jl
Normal file
@ -0,0 +1,27 @@
|
||||
module Make
|
||||
# makefile for generating typst pdfs
|
||||
# per directory usage
|
||||
dir = "precalc"
|
||||
files = ("calculator",
|
||||
"variables",
|
||||
"numbers_types",
|
||||
"logical_expressions",
|
||||
"vectors",
|
||||
"ranges",
|
||||
"functions",
|
||||
"plotting",
|
||||
"transformations",
|
||||
"inversefunctions",
|
||||
"polynomial",
|
||||
"polynomial_roots",
|
||||
"polynomials_package",
|
||||
"rational_functions",
|
||||
"exp_log_functions",
|
||||
"trig_functions",
|
||||
"julia_overview"
|
||||
)
|
||||
|
||||
include("../_make_pdf.jl")
|
||||
main()
|
||||
|
||||
end
|
@ -481,7 +481,7 @@ Now consider the related polynomial, $q$, where we multiply $p$ by $x^n$ and sub
|
||||
#| hold: true
|
||||
p = a*x^2 + b*x + c
|
||||
n = 2 # the degree of p
|
||||
q = expand(x^n * p(x => 1/x))
|
||||
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.
|
||||
|
@ -381,8 +381,8 @@ For a polynomial with symbolic coefficients, the difference between the symbol a
|
||||
#| hold: true
|
||||
@syms a b c
|
||||
p = a*x^2 + b*x + c
|
||||
q = sympy.Poly(p, x) # identify `x` as indeterminate; alternatively p.as_poly(x)
|
||||
roots(q)
|
||||
q1 = sympy.Poly(p, x) # identify `x` as indeterminate; alternatively p.as_poly(x)
|
||||
roots(q1)
|
||||
```
|
||||
|
||||
:::{.callout-note}
|
||||
|
@ -660,3 +660,51 @@ The [product](http://en.wikipedia.org/wiki/Arithmetic_progression) of the terms
|
||||
val = prod(1:2:19)
|
||||
numericq(val)
|
||||
```
|
||||
|
||||
##### Question
|
||||
|
||||
Credit card numbers have a check digit to ensure data entry of a 16-digit number is correct. How does it work? The [Luhn Algorithm](https://en.wikipedia.org/wiki/Luhn_algorithm).
|
||||
|
||||
Let's see if `4137 8947 1175 5804` is a valid credit card number?
|
||||
|
||||
First, we enter it as a value and immediately break the number into its digits:
|
||||
|
||||
```{julia}
|
||||
x = 4137_8947_1175_5904 # _ in a number is ignored by parser
|
||||
xs = digits(x)
|
||||
```
|
||||
|
||||
We reverse the order, so the first number in digits is the largest place value in `xs`
|
||||
|
||||
```{julia}
|
||||
reverse!(xs)
|
||||
```
|
||||
|
||||
Now, the 1st, 3rd, 5th, ... digit is doubled. We do this through indexing:
|
||||
|
||||
```{julia}
|
||||
for i in 1:2:length(xs)
|
||||
xs[i] = 2 * xs[i]
|
||||
end
|
||||
```
|
||||
|
||||
Number greater than 9, have their digits added, then all the resulting numbers are added. This can be done with a generator:
|
||||
|
||||
|
||||
```{julia}
|
||||
z = sum(sum(digits(xi)) for xi in xs)
|
||||
```
|
||||
|
||||
If this sum has a remainder of 0 when dividing by 10, the credit card number is possibly valid, if not it is definitely invalid. (The check digit is the last number and is set so that the above applied to the first 15 digits *plus* the check digit results in a multiple of 10.)
|
||||
|
||||
```{julia}
|
||||
iszero(rem(z,10))
|
||||
```
|
||||
|
||||
Darn. A typo. is `4137 8947 1175 5804` a possible credit card number?
|
||||
|
||||
```{julia}
|
||||
#| hold: true
|
||||
#| echo: false
|
||||
booleanq(true)
|
||||
```
|
||||
|
@ -63,3 +63,27 @@
|
||||
pages = {97–111},
|
||||
numpages = {15}
|
||||
}
|
||||
|
||||
@book{doi:10.1137/1.9781611977165,
|
||||
author = {Trefethen, Lloyd N. and Bau, David},
|
||||
title = {Numerical Linear Algebra, Twenty-fifth Anniversary Edition},
|
||||
publisher = {Society for Industrial and Applied Mathematics},
|
||||
year = {2022},
|
||||
doi = {10.1137/1.9781611977165},
|
||||
address = {Philadelphia, PA},
|
||||
edition = {},
|
||||
URL = {https://epubs.siam.org/doi/abs/10.1137/1.9781611977165},
|
||||
eprint = {https://epubs.siam.org/doi/pdf/10.1137/1.9781611977165}
|
||||
}
|
||||
|
||||
@book{doi:10.1137/1.9781611975086,
|
||||
author = {Driscoll, Tobin A. and Braun, Richard J.},
|
||||
title = {Fundamentals of Numerical Computation},
|
||||
publisher = {Society for Industrial and Applied Mathematics},
|
||||
year = {2017},
|
||||
doi = {10.1137/1.9781611975086},
|
||||
address = {Philadelphia, PA},
|
||||
edition = {Julia adaptation},
|
||||
URL = {https://tobydriscoll.net/fnc-julia/frontmatter.html},
|
||||
eprint = {https://epubs.siam.org/doi/pdf/10.1137/1.9781611975086}
|
||||
}
|
||||
|
357
quarto/test.typ
Normal file
357
quarto/test.typ
Normal file
@ -0,0 +1,357 @@
|
||||
// Some definitions presupposed by pandoc's typst output.
|
||||
#let blockquote(body) = [
|
||||
#set text( size: 0.92em )
|
||||
#block(inset: (left: 1.5em, top: 0.2em, bottom: 0.2em))[#body]
|
||||
]
|
||||
|
||||
#let horizontalrule = [
|
||||
#line(start: (25%,0%), end: (75%,0%))
|
||||
]
|
||||
|
||||
#let endnote(num, contents) = [
|
||||
#stack(dir: ltr, spacing: 3pt, super[#num], contents)
|
||||
]
|
||||
|
||||
#show terms: it => {
|
||||
it.children
|
||||
.map(child => [
|
||||
#strong[#child.term]
|
||||
#block(inset: (left: 1.5em, top: -0.4em))[#child.description]
|
||||
])
|
||||
.join()
|
||||
}
|
||||
|
||||
// Some quarto-specific definitions.
|
||||
|
||||
#show raw.where(block: true): set block(
|
||||
fill: luma(230),
|
||||
width: 100%,
|
||||
inset: 8pt,
|
||||
radius: 2pt
|
||||
)
|
||||
|
||||
#let block_with_new_content(old_block, new_content) = {
|
||||
let d = (:)
|
||||
let fields = old_block.fields()
|
||||
fields.remove("body")
|
||||
if fields.at("below", default: none) != none {
|
||||
// TODO: this is a hack because below is a "synthesized element"
|
||||
// according to the experts in the typst discord...
|
||||
fields.below = fields.below.amount
|
||||
}
|
||||
return block.with(..fields)(new_content)
|
||||
}
|
||||
|
||||
#let unescape-eval(str) = {
|
||||
return eval(str.replace("\\", ""))
|
||||
}
|
||||
|
||||
#let empty(v) = {
|
||||
if type(v) == "string" {
|
||||
// two dollar signs here because we're technically inside
|
||||
// a Pandoc template :grimace:
|
||||
v.matches(regex("^\\s*$")).at(0, default: none) != none
|
||||
} else if type(v) == "content" {
|
||||
if v.at("text", default: none) != none {
|
||||
return empty(v.text)
|
||||
}
|
||||
for child in v.at("children", default: ()) {
|
||||
if not empty(child) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Subfloats
|
||||
// This is a technique that we adapted from https://github.com/tingerrr/subpar/
|
||||
#let quartosubfloatcounter = counter("quartosubfloatcounter")
|
||||
|
||||
#let quarto_super(
|
||||
kind: str,
|
||||
caption: none,
|
||||
label: none,
|
||||
supplement: str,
|
||||
position: none,
|
||||
subrefnumbering: "1a",
|
||||
subcapnumbering: "(a)",
|
||||
body,
|
||||
) = {
|
||||
context {
|
||||
let figcounter = counter(figure.where(kind: kind))
|
||||
let n-super = figcounter.get().first() + 1
|
||||
set figure.caption(position: position)
|
||||
[#figure(
|
||||
kind: kind,
|
||||
supplement: supplement,
|
||||
caption: caption,
|
||||
{
|
||||
show figure.where(kind: kind): set figure(numbering: _ => numbering(subrefnumbering, n-super, quartosubfloatcounter.get().first() + 1))
|
||||
show figure.where(kind: kind): set figure.caption(position: position)
|
||||
|
||||
show figure: it => {
|
||||
let num = numbering(subcapnumbering, n-super, quartosubfloatcounter.get().first() + 1)
|
||||
show figure.caption: it => {
|
||||
num.slice(2) // I don't understand why the numbering contains output that it really shouldn't, but this fixes it shrug?
|
||||
[ ]
|
||||
it.body
|
||||
}
|
||||
|
||||
quartosubfloatcounter.step()
|
||||
it
|
||||
counter(figure.where(kind: it.kind)).update(n => n - 1)
|
||||
}
|
||||
|
||||
quartosubfloatcounter.update(0)
|
||||
body
|
||||
}
|
||||
)#label]
|
||||
}
|
||||
}
|
||||
|
||||
// callout rendering
|
||||
// this is a figure show rule because callouts are crossreferenceable
|
||||
#show figure: it => {
|
||||
if type(it.kind) != "string" {
|
||||
return it
|
||||
}
|
||||
let kind_match = it.kind.matches(regex("^quarto-callout-(.*)")).at(0, default: none)
|
||||
if kind_match == none {
|
||||
return it
|
||||
}
|
||||
let kind = kind_match.captures.at(0, default: "other")
|
||||
kind = upper(kind.first()) + kind.slice(1)
|
||||
// now we pull apart the callout and reassemble it with the crossref name and counter
|
||||
|
||||
// when we cleanup pandoc's emitted code to avoid spaces this will have to change
|
||||
let old_callout = it.body.children.at(1).body.children.at(1)
|
||||
let old_title_block = old_callout.body.children.at(0)
|
||||
let old_title = old_title_block.body.body.children.at(2)
|
||||
|
||||
// TODO use custom separator if available
|
||||
let new_title = if empty(old_title) {
|
||||
[#kind #it.counter.display()]
|
||||
} else {
|
||||
[#kind #it.counter.display(): #old_title]
|
||||
}
|
||||
|
||||
let new_title_block = block_with_new_content(
|
||||
old_title_block,
|
||||
block_with_new_content(
|
||||
old_title_block.body,
|
||||
old_title_block.body.body.children.at(0) +
|
||||
old_title_block.body.body.children.at(1) +
|
||||
new_title))
|
||||
|
||||
block_with_new_content(old_callout,
|
||||
block(below: 0pt, new_title_block) +
|
||||
old_callout.body.children.at(1))
|
||||
}
|
||||
|
||||
// 2023-10-09: #fa-icon("fa-info") is not working, so we'll eval "#fa-info()" instead
|
||||
#let callout(body: [], title: "Callout", background_color: rgb("#dddddd"), icon: none, icon_color: black) = {
|
||||
block(
|
||||
breakable: false,
|
||||
fill: background_color,
|
||||
stroke: (paint: icon_color, thickness: 0.5pt, cap: "round"),
|
||||
width: 100%,
|
||||
radius: 2pt,
|
||||
block(
|
||||
inset: 1pt,
|
||||
width: 100%,
|
||||
below: 0pt,
|
||||
block(
|
||||
fill: background_color,
|
||||
width: 100%,
|
||||
inset: 8pt)[#text(icon_color, weight: 900)[#icon] #title]) +
|
||||
if(body != []){
|
||||
block(
|
||||
inset: 1pt,
|
||||
width: 100%,
|
||||
block(fill: white, width: 100%, inset: 8pt, body))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
|
||||
#let article(
|
||||
title: none,
|
||||
subtitle: none,
|
||||
authors: none,
|
||||
date: none,
|
||||
abstract: none,
|
||||
abstract-title: none,
|
||||
cols: 1,
|
||||
margin: (x: 1.25in, y: 1.25in),
|
||||
paper: "us-letter",
|
||||
lang: "en",
|
||||
region: "US",
|
||||
font: "linux libertine",
|
||||
fontsize: 11pt,
|
||||
title-size: 1.5em,
|
||||
subtitle-size: 1.25em,
|
||||
heading-family: "linux libertine",
|
||||
heading-weight: "bold",
|
||||
heading-style: "normal",
|
||||
heading-color: black,
|
||||
heading-line-height: 0.65em,
|
||||
sectionnumbering: none,
|
||||
toc: false,
|
||||
toc_title: none,
|
||||
toc_depth: none,
|
||||
toc_indent: 1.5em,
|
||||
doc,
|
||||
) = {
|
||||
set page(
|
||||
paper: paper,
|
||||
margin: margin,
|
||||
numbering: "1",
|
||||
)
|
||||
set par(justify: true)
|
||||
set text(lang: lang,
|
||||
region: region,
|
||||
font: font,
|
||||
size: fontsize)
|
||||
set heading(numbering: sectionnumbering)
|
||||
if title != none {
|
||||
align(center)[#block(inset: 2em)[
|
||||
#set par(leading: heading-line-height)
|
||||
#if (heading-family != none or heading-weight != "bold" or heading-style != "normal"
|
||||
or heading-color != black or heading-decoration == "underline"
|
||||
or heading-background-color != none) {
|
||||
set text(font: heading-family, weight: heading-weight, style: heading-style, fill: heading-color)
|
||||
text(size: title-size)[#title]
|
||||
if subtitle != none {
|
||||
parbreak()
|
||||
text(size: subtitle-size)[#subtitle]
|
||||
}
|
||||
} else {
|
||||
text(weight: "bold", size: title-size)[#title]
|
||||
if subtitle != none {
|
||||
parbreak()
|
||||
text(weight: "bold", size: subtitle-size)[#subtitle]
|
||||
}
|
||||
}
|
||||
]]
|
||||
}
|
||||
|
||||
if authors != none {
|
||||
let count = authors.len()
|
||||
let ncols = calc.min(count, 3)
|
||||
grid(
|
||||
columns: (1fr,) * ncols,
|
||||
row-gutter: 1.5em,
|
||||
..authors.map(author =>
|
||||
align(center)[
|
||||
#author.name \
|
||||
#author.affiliation \
|
||||
#author.email
|
||||
]
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
if date != none {
|
||||
align(center)[#block(inset: 1em)[
|
||||
#date
|
||||
]]
|
||||
}
|
||||
|
||||
if abstract != none {
|
||||
block(inset: 2em)[
|
||||
#text(weight: "semibold")[#abstract-title] #h(1em) #abstract
|
||||
]
|
||||
}
|
||||
|
||||
if toc {
|
||||
let title = if toc_title == none {
|
||||
auto
|
||||
} else {
|
||||
toc_title
|
||||
}
|
||||
block(above: 0em, below: 2em)[
|
||||
#outline(
|
||||
title: toc_title,
|
||||
depth: toc_depth,
|
||||
indent: toc_indent
|
||||
);
|
||||
]
|
||||
}
|
||||
|
||||
if cols == 1 {
|
||||
doc
|
||||
} else {
|
||||
columns(cols, doc)
|
||||
}
|
||||
}
|
||||
|
||||
#set table(
|
||||
inset: 6pt,
|
||||
stroke: none
|
||||
)
|
||||
|
||||
#show: doc => article(
|
||||
toc_title: [Table of contents],
|
||||
toc_depth: 3,
|
||||
cols: 1,
|
||||
doc,
|
||||
)
|
||||
|
||||
#block[
|
||||
#heading(
|
||||
level:
|
||||
1
|
||||
,
|
||||
numbering:
|
||||
none
|
||||
,
|
||||
[
|
||||
Preface
|
||||
]
|
||||
)
|
||||
]
|
||||
#figure([
|
||||
#box(image("misc/logo.png"))
|
||||
], caption: figure.caption(
|
||||
position: bottom,
|
||||
[
|
||||
Calculus with Julia
|
||||
]),
|
||||
kind: "quarto-float-fig",
|
||||
supplement: "Figure",
|
||||
)
|
||||
|
||||
#horizontalrule
|
||||
|
||||
This is a set of notes for learning #link("http://en.wikipedia.org/wiki/Calculus")[calculus] using the #link("https://julialang.org")[`Julia`] language. `Julia` is an open-source programming language with an easy to learn syntax that is well suited for this task.
|
||||
|
||||
Read "#link("./misc/getting_started_with_julia.html")[Getting started with Julia];" to learn how to install and customize `Julia` for following along with these notes. Read "#link("./misc/julia_interfaces.html")[Julia interfaces];" to review different ways to interact with a `Julia` installation.
|
||||
|
||||
Since the mid 90s there has been a push to teach calculus using many different points of view. The #link("http://www.math.harvard.edu/~knill/pedagogy/harvardcalculus/")[Harvard] style rule of four says that as much as possible the conversation should include a graphical, numerical, algebraic, and verbal component. These notes use the programming language #link("http://julialang.org")[Julia] to illustrate the graphical, numerical, and, at times, the algebraic aspects of calculus.
|
||||
|
||||
There are many examples of integrating a computer algebra system (such as `Mathematica`, `Maple`, or `Sage`) into the calculus conversation. Computer algebra systems can be magical. The popular #link("http://www.wolframalpha.com/")[WolframAlpha] website calls the full power of `Mathematica` while allowing an informal syntax that is flexible enough to be used as a backend for Apple’s Siri feature. ("Siri what is the graph of x squared minus 4?") For learning purposes, computer algebra systems model very well the algebraic/symbolic treatment of the material while providing means to illustrate the numeric aspects. These notes are a bit different in that `Julia` is primarily used for the numeric style of computing and the algebraic/symbolic treatment is added on. Doing the symbolic treatment by hand can be very beneficial while learning, and computer algebra systems make those exercises seem kind of redundant, as the finished product can be produced much more easily.
|
||||
|
||||
Our real goal is to get at the concepts using technology as much as possible without getting bogged down in the mechanics of the computer language. We feel `Julia` has a very natural syntax that makes the initial start up not so much more difficult than using a calculator, but with a language that has a tremendous upside. The notes restrict themselves to a reduced set of computational concepts. This set is sufficient for working many of the problems in calculus, but do not cover thoroughly many aspects of programming. (Those who are interested can go off on their own and `Julia` provides a rich opportunity to do so.) Within this restricted set, are operators that make many of the computations of calculus reduce to a function call of the form `action(function, arguments...)`. With a small collection of actions that can be composed, many of the problems associated with introductory calculus can be attacked.
|
||||
|
||||
These notes are presented in pages covering a fairly focused concept, in a spirit similar to a section of a book. Just like a book, there are try-it-yourself questions at the end of each page. All have a limited number of self-graded answers. These notes borrow ideas from many sources, for example #cite(<Strang>, form: "prose");, #cite(<Knill>, form: "prose");, #cite(<Schey>, form: "prose");, #cite(<Thomas>, form: "prose");, #cite(<RogawskiAdams>, form: "prose");, several Wikipedia pages, and other sources.
|
||||
|
||||
These notes are accompanied by a `Julia` package `CalculusWithJulia` that provides some simple functions to streamline some common tasks and loads some useful packages that will be used repeatedly.
|
||||
|
||||
These notes are presented as a Quarto book. To learn more about Quarto books visit #link("https://quarto.org/docs/books");.
|
||||
|
||||
To #emph[contribute] – say by suggesting additional topics, correcting a mistake, or fixing a typo – click the "Edit this page" link and join the list of #link("https://github.com/jverzani/CalculusWithJuliaNotes.jl/graphs/contributors")[contributors];. Thanks to all contributors and a #emph[very] special thanks to `@fangliu-tju` for their careful and most-appreciated proofreading.
|
||||
|
||||
= Running Julia
|
||||
<running-julia>
|
||||
`Julia` is installed quite easily with the `juliaup` utility. There are some brief installation notes in the overview of `Julia` commands. To run `Julia` through the web (though in a resource-constrained manner), these links resolve to `binder.org` instances:
|
||||
|
||||
- #link("https://mybinder.org/v2/gh/jverzani/CalculusWithJuliaBinder.jl/main?labpath=blank-notebook.ipynb")[#box(image("test_files/mediabag/badge_logo.svg"))] (Image without SymPy)
|
||||
|
||||
- #link("https://mybinder.org/v2/gh/jverzani/CalculusWithJuliaBinder.jl/sympy?labpath=blank-notebook.ipynb")[#box(image("test_files/mediabag/badge_logo.svg"))] (Image with SymPy, longer to load)
|
||||
|
||||
#horizontalrule
|
||||
|
||||
Calculus with Julia version #strong[?meta:version];, produced on #strong[?meta:date];.
|
1
quarto/test_files/mediabag/badge_logo.svg
Normal file
1
quarto/test_files/mediabag/badge_logo.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="109" height="20"><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="a"><rect width="109" height="20" fill="#fff" rx="3"/></clipPath><g clip-path="url(#a)"><path fill="#555" d="M0 0h64v20H0z"/><path fill="#579aca" d="M64 0h45v20H64z"/><path fill="url(#b)" d="M0 0h109v20H0z"/></g><g fill="#fff" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110" text-anchor="middle"><image width="14" height="14" x="5" y="3" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFkAAABZCAMAAABi1XidAAAB8lBMVEX///9XmsrmZYH1olJXmsr1olJXmsrmZYH1olJXmsr1olJXmsrmZYH1olL1olJXmsr1olJXmsrmZYH1olL1olJXmsrmZYH1olJXmsr1olL1olJXmsrmZYH1olL1olJXmsrmZYH1olL1olL0nFf1olJXmsrmZYH1olJXmsq8dZb1olJXmsrmZYH1olJXmspXmspXmsr1olL1olJXmsrmZYH1olJXmsr1olL1olJXmsrmZYH1olL1olLeaIVXmsrmZYH1olL1olL1olJXmsrmZYH1olLna31Xmsr1olJXmsr1olJXmsrmZYH1olLqoVr1olJXmsr1olJXmsrmZYH1olL1olKkfaPobXvviGabgadXmsqThKuofKHmZ4Dobnr1olJXmsr1olJXmspXmsr1olJXmsrfZ4TuhWn1olL1olJXmsqBi7X1olJXmspZmslbmMhbmsdemsVfl8ZgmsNim8Jpk8F0m7R4m7F5nLB6jbh7jbiDirOEibOGnKaMhq+PnaCVg6qWg6qegKaff6WhnpKofKGtnomxeZy3noG6dZi+n3vCcpPDcpPGn3bLb4/Mb47UbIrVa4rYoGjdaIbeaIXhoWHmZYHobXvpcHjqdHXreHLroVrsfG/uhGnuh2bwj2Hxk17yl1vzmljzm1j0nlX1olL3AJXWAAAAbXRSTlMAEBAQHx8gICAuLjAwMDw9PUBAQEpQUFBXV1hgYGBkcHBwcXl8gICAgoiIkJCQlJicnJ2goKCmqK+wsLC4usDAwMjP0NDQ1NbW3Nzg4ODi5+3v8PDw8/T09PX29vb39/f5+fr7+/z8/Pz9/v7+zczCxgAABC5JREFUeAHN1ul3k0UUBvCb1CTVpmpaitAGSLSpSuKCLWpbTKNJFGlcSMAFF63iUmRccNG6gLbuxkXU66JAUef/9LSpmXnyLr3T5AO/rzl5zj137p136BISy44fKJXuGN/d19PUfYeO67Znqtf2KH33Id1psXoFdW30sPZ1sMvs2D060AHqws4FHeJojLZqnw53cmfvg+XR8mC0OEjuxrXEkX5ydeVJLVIlV0e10PXk5k7dYeHu7Cj1j+49uKg7uLU61tGLw1lq27ugQYlclHC4bgv7VQ+TAyj5Zc/UjsPvs1sd5cWryWObtvWT2EPa4rtnWW3JkpjggEpbOsPr7F7EyNewtpBIslA7p43HCsnwooXTEc3UmPmCNn5lrqTJxy6nRmcavGZVt/3Da2pD5NHvsOHJCrdc1G2r3DITpU7yic7w/7Rxnjc0kt5GC4djiv2Sz3Fb2iEZg41/ddsFDoyuYrIkmFehz0HR2thPgQqMyQYb2OtB0WxsZ3BeG3+wpRb1vzl2UYBog8FfGhttFKjtAclnZYrRo9ryG9uG/FZQU4AEg8ZE9LjGMzTmqKXPLnlWVnIlQQTvxJf8ip7VgjZjyVPrjw1te5otM7RmP7xm+sK2Gv9I8Gi++BRbEkR9EBw8zRUcKxwp73xkaLiqQb+kGduJTNHG72zcW9LoJgqQxpP3/Tj//c3yB0tqzaml05/+orHLksVO+95kX7/7qgJvnjlrfr2Ggsyx0eoy9uPzN5SPd86aXggOsEKW2Prz7du3VID3/tzs/sSRs2w7ovVHKtjrX2pd7ZMlTxAYfBAL9jiDwfLkq55Tm7ifhMlTGPyCAs7RFRhn47JnlcB9RM5T97ASuZXIcVNuUDIndpDbdsfrqsOppeXl5Y+XVKdjFCTh+zGaVuj0d9zy05PPK3QzBamxdwtTCrzyg/2Rvf2EstUjordGwa/kx9mSJLr8mLLtCW8HHGJc2R5hS219IiF6PnTusOqcMl57gm0Z8kanKMAQg0qSyuZfn7zItsbGyO9QlnxY0eCuD1XL2ys/MsrQhltE7Ug0uFOzufJFE2PxBo/YAx8XPPdDwWN0MrDRYIZF0mSMKCNHgaIVFoBbNoLJ7tEQDKxGF0kcLQimojCZopv0OkNOyWCCg9XMVAi7ARJzQdM2QUh0gmBozjc3Skg6dSBRqDGYSUOu66Zg+I2fNZs/M3/f/Grl/XnyF1Gw3VKCez0PN5IUfFLqvgUN4C0qNqYs5YhPL+aVZYDE4IpUk57oSFnJm4FyCqqOE0jhY2SMyLFoo56zyo6becOS5UVDdj7Vih0zp+tcMhwRpBeLyqtIjlJKAIZSbI8SGSF3k0pA3mR5tHuwPFoa7N7reoq2bqCsAk1HqCu5uvI1n6JuRXI+S1Mco54YmYTwcn6Aeic+kssXi8XpXC4V3t7/ADuTNKaQJdScAAAAAElFTkSuQmCC"/><text x="415" y="150" fill="#010101" fill-opacity=".3" textLength="370" transform="scale(.1)">launch</text><text x="415" y="140" textLength="370" transform="scale(.1)">launch</text><text x="855" y="150" fill="#010101" fill-opacity=".3" textLength="350" transform="scale(.1)">binder</text><text x="855" y="140" textLength="350" transform="scale(.1)">binder</text></g></svg>
|
After Width: | Height: | Size: 3.3 KiB |
Loading…
x
Reference in New Issue
Block a user