pdf files; edits

This commit is contained in:
jverzani 2024-10-15 17:17:25 -04:00
parent c1629e4f1a
commit 30086f9517
50 changed files with 1307 additions and 86 deletions

5
.gitignore vendored
View File

@ -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

11
quarto/ODEs/Project.toml Normal file
View 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"

View File

@ -0,0 +1 @@
# ODEs with Julia

16
quarto/ODEs/make_pdf.jl Normal file
View 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

View File

@ -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
View 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
View 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)
---
[![](misc/logo.png){}](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")
```

View 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"

View File

@ -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}

View File

@ -0,0 +1 @@
../alternatives.qmd

View 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

View File

@ -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$

View File

@ -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
```

View 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"

View File

@ -0,0 +1 @@
../derivatives.qmd

View 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

View 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"

View File

@ -0,0 +1 @@
../differentiable_vector_calculus.qmd

View 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

View File

@ -68,6 +68,10 @@ 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).
-->

View 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"

View File

@ -0,0 +1 @@
../integral_vector_calculus.qmd

View 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

View 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"

View File

@ -0,0 +1 @@
../integrals.qmd

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

View 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

View 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"

View File

@ -0,0 +1 @@
../limits.qmd

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 450 KiB

View File

@ -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))
```
:::
[![Intersection of two curves as illustrated by Canadian artist Kapwani Kiwanga.](figures/intersection-biennale.jpg)](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
```
[![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.
](figures/hardrock-100.png)](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
```
![Trajectories of potential cannonball fires with air-resistance included. (http://ej.iop.org/images/0143-0807/33/1/149/Full/ejp405251f1_online.jpg)
](./figures/cannonball.jpg)
](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
View 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
View 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
View File

@ -0,0 +1,3 @@
# Miscellaneous
This section has several small notes on `Julia`.

1
quarto/misc/_pdf_index.qmd Symbolic link
View File

@ -0,0 +1 @@
../misc.qmd

16
quarto/misc/make_pdf.jl Normal file
View 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

View 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

View 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"

View File

@ -0,0 +1 @@
../precalc.qmd

View File

@ -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",

View File

@ -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)
```

View File

@ -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

View 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

View File

@ -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.

View File

@ -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}

View File

@ -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 Alogorithm](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)
```

357
quarto/test.typ Normal file
View 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 Apples 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];.

View 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