15 lines
51 KiB
JSON
15 lines
51 KiB
JSON
{
|
||
"hash": "b52cd445072d7151097e17b50798b1ee",
|
||
"result": {
|
||
"markdown": "# Quick introduction to Calculus with Julia\n\n\n\nThe `Julia` programming language with a design that makes it well suited as a supplement for the learning of calculus, as this collection of notes is intended to illustrate.\n\n\nAs `Julia` is open source, it can be downloaded and used like many other programming languages.\n\n\nJulia can be used through the internet for free using the [mybinder.org](https://mybinder.org) service. This link: [launch binder](https://mybinder.org/v2/gh/CalculusWithJulia/CwJScratchPad.git/master) will take you to website that allows this. Just click on the `CalcululsWithJulia.ipynb` file after launching Binder by clicking on the badge. Binder provides the Jupyter interface.\n\n---\n\n\nHere are some `Julia` usages to create calculus objects.\n\n\nThe `Julia` packages loaded below are all loaded when the `CalculusWithJulia` package is loaded.\n\n\nA `Julia` package is loaded with the `using` command:\n\n``` {.julia .cell-code}\nusing LinearAlgebra\n```\n\n\nThe `LinearAlgebra` package comes with a `Julia` installation. Other packages can be added. Something like:\n\n``` {.julia .cell-code}\nusing Pkg\nPkg.add(\"SomePackageName\")\n```\n\n\nThese notes have an accompanying package, `CalculusWithJulia`, that when installed, as above, also installs most of the necessary packages to perform the examples.\n\n\nPackages need only be installed once, but they must be loaded into *each* session for which they will be used.\n\n``` {.julia .cell-code}\nusing CalculusWithJulia\n```\n\n\nPackages can also be loaded through `import PackageName`. Importing does not add the exported objects of a function into the namespace, so is used when there are possible name collisions.\n\n\n## Types\n\n\nObjects in `Julia` are \"typed.\" Common numeric types are `Float64`, `Int64` for floating point numbers and integers. Less used here are types like `Rational{Int64}`, specifying rational numbers with a numerator and denominator as `Int64`; or `Complex{Float64}`, specifying a comlex number with floating point components. Julia also has `BigFloat` and `BigInt` for arbitrary precision types. Typically, operations use \"promotion\" to ensure the combination of types is appropriate. Other useful types are `Function`, an abstract type describing functions; `Bool` for true and false values; `Sym` for symbolic values (through `SymPy`); and `Vector{Float64}` for vectors with floating point components.\n\n\nFor the most part the type will not be so important, but it is useful to know that for some function calls the type of the argument will decide what method ultimately gets called. (This allows symbolic types to interact with Julia functions in an idiomatic manner.)\n\n\n## Functions\n\n\n### Definition\n\n\nFunctions can be defined four basic ways:\n\n\n * one statement functions follow traditional mathematics notation:\n\n::: {.cell hold='true' execution_count=5}\n``` {.julia .cell-code}\nf(x) = exp(x) * 2x\n```\n\n::: {.cell-output .cell-output-display execution_count=5}\n```\nf (generic function with 1 method)\n```\n:::\n:::\n\n\n * multi-statement functions are defined with the `function` keyword. The `end` statement ends the definition. The last evaluated command is returned. There is no need for explicit `return` statement, though it can be useful for control flow.\n\n::: {.cell execution_count=6}\n``` {.julia .cell-code}\nfunction g(x)\n a = sin(x)^2\n a + a^2 + a^3\nend\n```\n\n::: {.cell-output .cell-output-display execution_count=6}\n```\ng (generic function with 1 method)\n```\n:::\n:::\n\n\n * Anonymous functions, useful for example, as arguments to other functions or as return values, are defined using an arrow, `->`, as follows:\n\n::: {.cell execution_count=7}\n``` {.julia .cell-code}\nfn = x -> sin(2x)\nfn(pi/2)\n```\n\n::: {.cell-output .cell-output-display execution_count=7}\n```\n1.2246467991473532e-16\n```\n:::\n:::\n\n\nIn the following, the defined function, `Derivative`, returns an anonymously defined function that uses a `Julia` package, loaded with `CalculusWithJulia`, to take a derivative:\n\n::: {.cell execution_count=8}\n``` {.julia .cell-code}\nDerivatve(f::Function) = x -> ForwardDiff.derivative(f, x) # ForwardDiff is loaded in CalculusWithJulia\n```\n\n::: {.cell-output .cell-output-display execution_count=8}\n```\nDerivatve (generic function with 1 method)\n```\n:::\n:::\n\n\n(The `D` function of `CalculusWithJulia` implements something similar.)\n\n\n * Anonymous function may also be created using the `function` keyword.\n\n\nFor mathematical functions $f: R^n \\rightarrow R^m$ when $n$ or $m$ is bigger than 1 we have:\n\n\n * When $n =1$ and $m > 1$ we use a \"vector\" for the return value\n\n::: {.cell hold='true' execution_count=9}\n``` {.julia .cell-code}\nr(t) = [sin(t), cos(t), t]\n```\n\n::: {.cell-output .cell-output-display execution_count=9}\n```\nr (generic function with 1 method)\n```\n:::\n:::\n\n\n(An alternative would be to create a vector of functions.)\n\n\n * When $n > 1$ and $m=1$ we use multiple arguments or pass the arguments in a container. This pattern is common, as it allows both calling styles.\n\n::: {.cell hold='true' execution_count=10}\n``` {.julia .cell-code}\nf(x, y, z) = x*y + y*z + z*x\nf(v) = f(v...)\n```\n\n::: {.cell-output .cell-output-display execution_count=10}\n```\nf (generic function with 2 methods)\n```\n:::\n:::\n\n\nSome functions need to pass in a container of values, for this the last definition is useful to expand the values. Splatting takes a container and treats the values like individual arguments.\n\n\nAlternatively, indexing can be used directly, as in:\n\n::: {.cell hold='true' execution_count=11}\n``` {.julia .cell-code}\nf(x) = x[1]*x[2] + x[2]*x[3] + x[3]*x[1]\n```\n\n::: {.cell-output .cell-output-display execution_count=11}\n```\nf (generic function with 2 methods)\n```\n:::\n:::\n\n\n * For vector fields ($n,m > 1$) a combination is used:\n\n::: {.cell hold='true' execution_count=12}\n``` {.julia .cell-code}\nF(x,y,z) = [-y, x, z]\nF(v) = F(v...)\n```\n\n::: {.cell-output .cell-output-display execution_count=12}\n```\nF (generic function with 2 methods)\n```\n:::\n:::\n\n\n### Calling a function\n\n\nFunctions are called using parentheses to group the arguments.\n\n::: {.cell hold='true' execution_count=13}\n``` {.julia .cell-code}\nf(t) = sin(t)*sqrt(t)\nsin(1), sqrt(1), f(1)\n```\n\n::: {.cell-output .cell-output-display execution_count=13}\n```\n(0.8414709848078965, 1.0, 0.8414709848078965)\n```\n:::\n:::\n\n\nWhen a function has multiple arguments, yet the value passed in is a container holding the arguments, splatting is used to expand the arguments, as is done in the definition `F(v) = F(v...)`, above.\n\n\n### Multiple dispatch\n\n\n`Julia` can have many methods for a single generic function. (E.g., it can have many different implementations of addiion when the `+` sign is encountered.) The *type*s of the arguments and the number of arguments are used for dispatch.\n\n\nHere the number of arguments is used:\n\n::: {.cell execution_count=14}\n``` {.julia .cell-code}\nArea(w, h) = w * h # area of rectangle\nArea(w) = Area(w, w) # area of square using area of rectangle defintion\n```\n\n::: {.cell-output .cell-output-display execution_count=14}\n```\nArea (generic function with 2 methods)\n```\n:::\n:::\n\n\nCalling `Area(5)` will call `Area(5,5)` which will return `5*5`.\n\n\nSimilarly, the definition for a vector field:\n\n::: {.cell hold='true' execution_count=15}\n``` {.julia .cell-code}\nF(x,y,z) = [-y, x, z]\nF(v) = F(v...)\n```\n\n::: {.cell-output .cell-output-display execution_count=15}\n```\nF (generic function with 2 methods)\n```\n:::\n:::\n\n\ntakes advantage of multiple dispatch to allow either a vector argument or individual arguments.\n\n\nType parameters can be used to restrict the type of arguments that are permitted. The `Derivative(f::Function)` definition illustrates how the `Derivative` function, defined above, is restricted to `Function` objects.\n\n\n### Keyword arguments\n\n\nOptional arguments may be specified with keywords, when the function is defined to use them. Keywords are separated from positional arguments using a semicolon, `;`:\n\n::: {.cell execution_count=16}\n``` {.julia .cell-code}\ncircle(x; r=1) = sqrt(r^2 - x^2)\ncircle(0.5), circle(0.5, r=10)\n```\n\n::: {.cell-output .cell-output-display execution_count=16}\n```\n(0.8660254037844386, 9.987492177719089)\n```\n:::\n:::\n\n\nThe main (but not sole) use of keyword arguments will be with plotting, where various plot attribute are passed as `key=value` pairs.\n\n\n## Symbolic objects\n\n\nThe add-on `SymPy` package allows for symbolic expressions to be used. Symbolic values are defined with `@syms`, as below.\n\n``` {.julia .cell-code}\nusing SymPy\n```\n\n\n::: {.cell execution_count=18}\n``` {.julia .cell-code}\n@syms x y z\nx^2 + y^3 + z\n```\n\n::: {.cell-output .cell-output-display execution_count=18}\n```{=html}\n<span class=\"math-left-align\" style=\"padding-left: 4px; width:0; float:left;\"> \n\\[\nx^{2} + y^{3} + z\n\\]\n</span>\n```\n:::\n:::\n\n\nAssumptions on the variables can be useful, particularly with simplification, as in\n\n::: {.cell hold='true' execution_count=19}\n``` {.julia .cell-code}\n@syms x::real y::integer z::positive\n```\n\n::: {.cell-output .cell-output-display execution_count=19}\n```\n(x, y, z)\n```\n:::\n:::\n\n\nSymbolic expressions flow through `Julia` functions symbolically\n\n::: {.cell execution_count=20}\n``` {.julia .cell-code}\nsin(x)^2 + cos(x)^2\n```\n\n::: {.cell-output .cell-output-display execution_count=20}\n```{=html}\n<span class=\"math-left-align\" style=\"padding-left: 4px; width:0; float:left;\"> \n\\[\n\\sin^{2}{\\left(x \\right)} + \\cos^{2}{\\left(x \\right)}\n\\]\n</span>\n```\n:::\n:::\n\n\nNumbers are symbolic once `SymPy` interacts with them:\n\n::: {.cell execution_count=21}\n``` {.julia .cell-code}\nx - x + 1 # 1 is now symbolic\n```\n\n::: {.cell-output .cell-output-display execution_count=21}\n```{=html}\n<span class=\"math-left-align\" style=\"padding-left: 4px; width:0; float:left;\"> \n\\[\n1\n\\]\n</span>\n```\n:::\n:::\n\n\nThe number `PI` is a symbolic `pi`.\n\n::: {.cell execution_count=22}\n``` {.julia .cell-code}\nsin(PI), sin(pi)\n```\n\n::: {.cell-output .cell-output-display execution_count=22}\n```\n(0, 1.2246467991473532e-16)\n```\n:::\n:::\n\n\nUse `Sym` to create symbolic numbers, `N` to find a `Julia` number from a symbolic number:\n\n::: {.cell execution_count=23}\n``` {.julia .cell-code}\n1 / Sym(2)\n```\n\n::: {.cell-output .cell-output-display execution_count=23}\n```{=html}\n<span class=\"math-left-align\" style=\"padding-left: 4px; width:0; float:left;\"> \n\\[\n\\frac{1}{2}\n\\]\n</span>\n```\n:::\n:::\n\n\n::: {.cell execution_count=24}\n``` {.julia .cell-code}\nN(PI)\n```\n\n::: {.cell-output .cell-output-display execution_count=24}\n```\nπ = 3.1415926535897...\n```\n:::\n:::\n\n\nMany generic `Julia` functions will work with symbolic objects through multiple dispatch (e.g., `sin`, `cos`, ...). Sympy functions that are not in `Julia` can be accessed through the `sympy` object using dot-call notation:\n\n::: {.cell execution_count=25}\n``` {.julia .cell-code}\nsympy.harmonic(10)\n```\n\n::: {.cell-output .cell-output-display execution_count=25}\n```{=html}\n<span class=\"math-left-align\" style=\"padding-left: 4px; width:0; float:left;\"> \n\\[\n\\frac{7381}{2520}\n\\]\n</span>\n```\n:::\n:::\n\n\nSome Sympy methods belong to the object and a called via the pattern `object.method(...)`. This too is the case using SymPy with `Julia`. For example:\n\n::: {.cell hold='true' execution_count=26}\n``` {.julia .cell-code}\nA = [x 1; x 2]\nA.det() # determinant of symbolic matrix A\n```\n\n::: {.cell-output .cell-output-display execution_count=26}\n```{=html}\n<span class=\"math-left-align\" style=\"padding-left: 4px; width:0; float:left;\"> \n\\[\nx\n\\]\n</span>\n```\n:::\n:::\n\n\n## Containers\n\n\nWe use a few different containers:\n\n\n * Tuples. These are objects grouped together using parentheses. They need not be of the same type\n\n::: {.cell execution_count=27}\n``` {.julia .cell-code}\nx1 = (1, \"two\", 3.0)\n```\n\n::: {.cell-output .cell-output-display execution_count=27}\n```\n(1, \"two\", 3.0)\n```\n:::\n:::\n\n\nTuples are useful for programming. For example, they are uesd to return multiple values from a function.\n\n\n * Vectors. These are objects of the same type (typically) grouped together using square brackets, values separated by commas:\n\n::: {.cell execution_count=28}\n``` {.julia .cell-code}\nx2 = [1, 2, 3.0] # 3.0 makes theses all floating point\n```\n\n::: {.cell-output .cell-output-display execution_count=28}\n```\n3-element Vector{Float64}:\n 1.0\n 2.0\n 3.0\n```\n:::\n:::\n\n\nUnlike tuples, the expected arithmatic from Linear Algebra is implemented for vectors.\n\n\n * Matrices. Like vectors, combine values of the same type, only they are 2-dimensional. Use spaces to separate values along a row; semicolons to separate rows:\n\n::: {.cell execution_count=29}\n``` {.julia .cell-code}\nx3 = [1 2 3; 4 5 6; 7 8 9]\n```\n\n::: {.cell-output .cell-output-display execution_count=29}\n```\n3×3 Matrix{Int64}:\n 1 2 3\n 4 5 6\n 7 8 9\n```\n:::\n:::\n\n\n * Row vectors. A vector is 1 dimensional, though it may be identified as a column of two dimensional matrix. A row vector is a two-dimensional matrix with a single row:\n\n::: {.cell execution_count=30}\n``` {.julia .cell-code}\nx4 = [1 2 3.0]\n```\n\n::: {.cell-output .cell-output-display execution_count=30}\n```\n1×3 Matrix{Float64}:\n 1.0 2.0 3.0\n```\n:::\n:::\n\n\nThese have *indexing* using square brackets:\n\n::: {.cell execution_count=31}\n``` {.julia .cell-code}\nx1[1], x2[2], x3[3]\n```\n\n::: {.cell-output .cell-output-display execution_count=31}\n```\n(1, 2.0, 7)\n```\n:::\n:::\n\n\nMatrices are usually indexed by row and column:\n\n::: {.cell execution_count=32}\n``` {.julia .cell-code}\nx3[1,2] # row one column two\n```\n\n::: {.cell-output .cell-output-display execution_count=32}\n```\n2\n```\n:::\n:::\n\n\nFor vectors and matrices - but not tuples, as they are immutable - indexing can be used to change a value in the container:\n\n::: {.cell execution_count=33}\n``` {.julia .cell-code}\nx2[1], x3[1,1] = 2, 2\n```\n\n::: {.cell-output .cell-output-display execution_count=33}\n```\n(2, 2)\n```\n:::\n:::\n\n\nVectors and matrices are arrays. As hinted above, arrays have mathematical operations, such as addition and subtraction, defined for them. Tuples do not.\n\n\nDestructuring is an alternative to indexing to get at the entries in certain containers:\n\n::: {.cell hold='true' execution_count=34}\n``` {.julia .cell-code}\na,b,c = x2\n```\n\n::: {.cell-output .cell-output-display execution_count=34}\n```\n3-element Vector{Float64}:\n 2.0\n 2.0\n 3.0\n```\n:::\n:::\n\n\n### Structured collections\n\n\nAn arithmetic progression, $a, a+h, a+2h, ..., b$ can be produced *efficiently* using the range operator `a:h:b`:\n\n::: {.cell execution_count=35}\n``` {.julia .cell-code}\n5:10:55 # an object that describes 5, 15, 25, 35, 45, 55\n```\n\n::: {.cell-output .cell-output-display execution_count=35}\n```\n5:10:55\n```\n:::\n:::\n\n\nIf `h=1` it can be omitted:\n\n::: {.cell execution_count=36}\n``` {.julia .cell-code}\n1:10 # an object that describes 1,2,3,4,5,6,7,8,9,10\n```\n\n::: {.cell-output .cell-output-display execution_count=36}\n```\n1:10\n```\n:::\n:::\n\n\nThe `range` function can *efficiently* describe $n$ evenly spaced points between `a` and `b`:\n\n::: {.cell execution_count=37}\n``` {.julia .cell-code}\nrange(0, pi, length=5) # range(a, stop=b, length=n) for version 1.0\n```\n\n::: {.cell-output .cell-output-display execution_count=37}\n```\n0.0:0.7853981633974483:3.141592653589793\n```\n:::\n:::\n\n\nThis is useful for creating regularly spaced values needed for certain plots.\n\n\n## Iteration\n\n\nThe `for` keyword is useful for iteration, Here is a traditional for loop, as `i` loops over each entry of the vector `[1,2,3]`:\n\n::: {.cell execution_count=38}\n``` {.julia .cell-code}\nfor i in [1,2,3]\n println(i)\nend\n```\n\n::: {.cell-output .cell-output-stdout}\n```\n1\n2\n3\n```\n:::\n:::\n\n\n:::{.callout-note}\n## Note\nTechnical aside: For assignment within a for loop at the global level, a `global` declaration may be needed to ensure proper scoping.\n\n:::\n\nList comprehensions are similar, but are useful as they perform the iteration and collect the values:\n\n::: {.cell execution_count=39}\n``` {.julia .cell-code}\n[i^2 for i in [1,2,3]]\n```\n\n::: {.cell-output .cell-output-display execution_count=39}\n```\n3-element Vector{Int64}:\n 1\n 4\n 9\n```\n:::\n:::\n\n\nComprehesions can also be used to make matrices\n\n::: {.cell execution_count=40}\n``` {.julia .cell-code}\n[1/(i+j) for i in 1:3, j in 1:4]\n```\n\n::: {.cell-output .cell-output-display execution_count=40}\n```\n3×4 Matrix{Float64}:\n 0.5 0.333333 0.25 0.2\n 0.333333 0.25 0.2 0.166667\n 0.25 0.2 0.166667 0.142857\n```\n:::\n:::\n\n\n(The three rows are for `i=1`, then `i=2`, and finally for `i=3`.)\n\n\nComprehensions apply an *expression* to each entry in a container through iteration. Applying a function to each entry of a container can be facilitated by:\n\n\n * Broadcasting. Using `.` before an operation instructs `Julia` to match up sizes (possibly extending to do so) and then apply the operation element by element:\n\n::: {.cell execution_count=41}\n``` {.julia .cell-code}\nxs = [1,2,3]\nsin.(xs) # sin(1), sin(2), sin(3)\n```\n\n::: {.cell-output .cell-output-display execution_count=41}\n```\n3-element Vector{Float64}:\n 0.8414709848078965\n 0.9092974268256817\n 0.1411200080598672\n```\n:::\n:::\n\n\nThis example pairs off the value in `bases` and `xs`:\n\n::: {.cell execution_count=42}\n``` {.julia .cell-code}\nbases = [5,5,10]\nlog.(bases, xs) # log(5, 1), log(5,2), log(10, 3)\n```\n\n::: {.cell-output .cell-output-display execution_count=42}\n```\n3-element Vector{Float64}:\n 0.0\n 0.43067655807339306\n 0.47712125471966244\n```\n:::\n:::\n\n\nThis example broadcasts the scalar value for the base with `xs`:\n\n::: {.cell execution_count=43}\n``` {.julia .cell-code}\nlog.(5, xs)\n```\n\n::: {.cell-output .cell-output-display execution_count=43}\n```\n3-element Vector{Float64}:\n 0.0\n 0.43067655807339306\n 0.6826061944859854\n```\n:::\n:::\n\n\nRow and column vectors can fill in:\n\n::: {.cell execution_count=44}\n``` {.julia .cell-code}\nys = [4 5] # a row vector\nh(x,y) = (x,y)\nh.(xs, ys) # broadcasting a column and row vector makes a matrix, then applies f.\n```\n\n::: {.cell-output .cell-output-display execution_count=44}\n```\n3×2 Matrix{Tuple{Int64, Int64}}:\n (1, 4) (1, 5)\n (2, 4) (2, 5)\n (3, 4) (3, 5)\n```\n:::\n:::\n\n\nThis should be contrasted to the case when both `xs` and `ys` are (column) vectors, as then they pair off (and here cause a dimension mismatch as they have different lengths):\n\n::: {.cell execution_count=45}\n``` {.julia .cell-code}\nh.(xs, [4,5])\n```\n\n::: {.cell-output .cell-output-error}\n```\nLoadError: DimensionMismatch(\"arrays could not be broadcast to a common size; got a dimension with lengths 3 and 2\")\n```\n:::\n:::\n\n\n * The `map` function is similar, it applies a function to each element:\n\n::: {.cell execution_count=46}\n``` {.julia .cell-code}\nmap(sin, [1,2,3])\n```\n\n::: {.cell-output .cell-output-display execution_count=46}\n```\n3-element Vector{Float64}:\n 0.8414709848078965\n 0.9092974268256817\n 0.1411200080598672\n```\n:::\n:::\n\n\n:::{.callout-note}\n## Note\nMany different computer languages implement `map`, broadcasting is less common. `Julia`'s use of the dot syntax to indicate broadcasting is reminiscent of MATLAB, but is quite different.\n\n:::\n\n## Plots\n\n\nThe following commands use the `Plots` package. The `Plots` package expects a choice of backend. We will use `gr` unless, but other can be substituted by calling an appropriate command, suchas `pyplot()` or `plotly()`.\n\n``` {.julia .cell-code}\nusing Plots\n```\n\n\n:::{.callout-note}\n## Note\nThe `plotly` backend and `gr` backends are available by default. The `plotly` backend is has some interactivity, `gr` is for static plots. The `pyplot` package is used for certain surface plots, when `gr` can not be used.\n\n:::\n\n### Plotting a univariate function $f:R \\rightarrow R$\n\n\n * using `plot(f, a, b)`\n\n::: {.cell execution_count=48}\n``` {.julia .cell-code}\nplot(sin, 0, 2pi)\n```\n\n::: {.cell-output .cell-output-display execution_count=48}\n{}\n:::\n:::\n\n\nOr\n\n::: {.cell hold='true' execution_count=49}\n``` {.julia .cell-code}\nf(x) = exp(-x/2pi)*sin(x)\nplot(f, 0, 2pi)\n```\n\n::: {.cell-output .cell-output-display execution_count=49}\n{}\n:::\n:::\n\n\nOr with an anonymous function\n\n::: {.cell execution_count=50}\n``` {.julia .cell-code}\nplot(x -> sin(x) + sin(2x), 0, 2pi)\n```\n\n::: {.cell-output .cell-output-display execution_count=50}\n{}\n:::\n:::\n\n\n:::{.callout-note}\n## Note\nThe time to first plot can be lengthy! This can be removed by creating a custom `Julia` image, but that is not introductory level stuff. As well, standalone plotting packages offer quicker first plots, but the simplicity of `Plots` is preferred. Subsequent plots are not so time consuming, as the initial time is spent compiling functions so their re-use is speedy.\n\n:::\n\nArguments of interest include\n\n\n| Attribute | Value |\n|:--------------:|:------------------------------------------------------:|\n| `legend` | A boolean, specify `false` to inhibit drawing a legend |\n| `aspect_ratio` | Use `:equal` to have x and y axis have same scale |\n| `linewidth` | Ingters greater than 1 will thicken lines drawn |\n| `color` | A color may be specified by a symbol (leading `:`). |\n| | E.g., `:black`, `:red`, `:blue` |\n\n\n * using `plot(xs, ys)`\n\n\nThe lower level interface to `plot` involves directly creating x and y values to plot:\n\n::: {.cell hold='true' execution_count=51}\n``` {.julia .cell-code}\nxs = range(0, 2pi, length=100)\nys = sin.(xs)\nplot(xs, ys, color=:red)\n```\n\n::: {.cell-output .cell-output-display execution_count=51}\n{}\n:::\n:::\n\n\n * plotting a symbolic expression\n\n\nA symbolic expression of single variable can be plotted as a function is:\n\n::: {.cell hold='true' execution_count=52}\n``` {.julia .cell-code}\n@syms x\nplot(exp(-x/2pi)*sin(x), 0, 2pi)\n```\n\n::: {.cell-output .cell-output-display execution_count=52}\n{}\n:::\n:::\n\n\n * Multiple functions\n\n\nThe `!` Julia convention to modify an object is used by the `plot` command, so `plot!` will add to the existing plot:\n\n::: {.cell hold='true' execution_count=53}\n``` {.julia .cell-code}\nplot(sin, 0, 2pi, color=:red)\nplot!(cos, 0, 2pi, color=:blue)\nplot!(zero, color=:green) # no a, b then inherited from graph.\n```\n\n::: {.cell-output .cell-output-display execution_count=53}\n{}\n:::\n:::\n\n\nThe `zero` function is just 0 (more generally useful when the type of a number is important, but used here to emphasize the $x$ axis).\n\n\n### Plotting a parameterized (space) curve function $f:R \\rightarrow R^n$, $n = 2$ or $3$\n\n\n * Using `plot(xs, ys)`\n\n\nLet $f(t) = e^{t/2\\pi} \\langle \\cos(t), \\sin(t)\\rangle$ be a parameterized function. Then the $t$ values can be generated as follows:\n\n::: {.cell hold='true' execution_count=54}\n``` {.julia .cell-code}\nts = range(0, 2pi, length = 100)\nxs = [exp(t/2pi) * cos(t) for t in ts]\nys = [exp(t/2pi) * sin(t) for t in ts]\nplot(xs, ys)\n```\n\n::: {.cell-output .cell-output-display execution_count=54}\n{}\n:::\n:::\n\n\n * using `plot(f1, f2, a, b)`. If the two functions describing the components are available, then\n\n::: {.cell hold='true' execution_count=55}\n``` {.julia .cell-code}\nf1(t) = exp(t/2pi) * cos(t)\nf2(t) = exp(t/2pi) * sin(t)\nplot(f1, f2, 0, 2pi)\n```\n\n::: {.cell-output .cell-output-display execution_count=55}\n{}\n:::\n:::\n\n\n * Using `plot_parametric`. If the curve is described as a function of `t` with a vector output, then the `CalculusWithJulia` package provides `plot_parametric` to produce a plot:\n\n::: {.cell execution_count=56}\n``` {.julia .cell-code}\nr(t) = exp(t/2pi) * [cos(t), sin(t)]\nplot_parametric(0..2pi, r)\n```\n\n::: {.cell-output .cell-output-display execution_count=56}\n{}\n:::\n:::\n\n\nThe low-level approach doesn't quite work as easily as desired:\n\n::: {.cell hold='true' execution_count=57}\n``` {.julia .cell-code}\nts = range(0, 2pi, length = 4)\nvs = r.(ts)\n```\n\n::: {.cell-output .cell-output-display execution_count=57}\n```\n4-element Vector{Vector{Float64}}:\n [1.0, 0.0]\n [-0.6978062125430444, 1.2086358139617603]\n [-0.9738670205273388, -1.6867871593690715]\n [2.718281828459045, -6.657870280805568e-16]\n```\n:::\n:::\n\n\nAs seen, the values are a vector of vectors. To plot a reshaping needs to be done:\n\n::: {.cell hold='true' execution_count=58}\n``` {.julia .cell-code}\nts = range(0, 2pi, length = 100)\nvs = r.(ts)\nxs = [vs[i][1] for i in eachindex(vs)]\nys = [vs[i][2] for i in eachindex(vs)]\nplot(xs, ys)\n```\n\n::: {.cell-output .cell-output-display execution_count=58}\n{}\n:::\n:::\n\n\nThis approach is faciliated by the `unzip` function in `CalculusWithJulia` (and used internally by `plot_parametric`):\n\n::: {.cell execution_count=59}\n``` {.julia .cell-code}\nts = range(0, 2pi, length = 100)\nplot(unzip(r.(ts))...)\n```\n\n::: {.cell-output .cell-output-display execution_count=59}\n{}\n:::\n:::\n\n\n * Plotting an arrow\n\n\nAn arrow in 2D can be plotted with the `quiver` command. We show the `arrow(p, v)` (or `arrow!(p,v)` function) from the `CalculusWithJulia` package, which has an easier syntax (`arrow!(p, v)`, where `p` is a point indicating the placement of the tail, and `v` the vector to represent):\n\n::: {.cell hold='true' execution_count=60}\n``` {.julia .cell-code}\nplot_parametric(0..2pi, r)\nt0 = pi/8\narrow!(r(t0), r'(t0))\n```\n\n::: {.cell-output .cell-output-display execution_count=60}\n{}\n:::\n:::\n\n\n### Plotting a scalar function $f:R^2 \\rightarrow R$\n\n\nThe `surface` and `contour` functions are available to visualize a scalar function of $2$ variables:\n\n\n * A surface plot\n\n::: {.cell hold='true' execution_count=61}\n``` {.julia .cell-code}\nf(x, y) = 2 - x^2 + y^2\nxs = ys = range(-2,2, length=25)\nsurface(xs, ys, f)\n```\n\n::: {.cell-output .cell-output-display execution_count=61}\n{}\n:::\n:::\n\n\nThe function generates the $z$ values, this can be done by the user and then passed to the `surface(xs, ys, zs)` format:\n\n::: {.cell hold='true' execution_count=62}\n``` {.julia .cell-code}\nf(x, y) = 2 - x^2 + y^2\nxs = ys = range(-2,2, length=25)\nsurface(xs, ys, f.(xs, ys'))\n```\n\n::: {.cell-output .cell-output-display execution_count=62}\n{}\n:::\n:::\n\n\n * A contour plot\n\n\nThe `contour` function is like the `surface` function.\n\n::: {.cell hold='true' execution_count=63}\n``` {.julia .cell-code}\nxs = ys = range(-2,2, length=25)\nf(x, y) = 2 - x^2 + y^2\ncontour(xs, ys, f)\n```\n\n::: {.cell-output .cell-output-display execution_count=63}\n{}\n:::\n:::\n\n\nThe values can be computed easily enough, being careful where the transpose is needed:\n\n::: {.cell hold='true' execution_count=64}\n``` {.julia .cell-code}\nxs = ys = range(-2,2, length=25)\nf(x, y) = 2 - x^2 + y^2\ncontour(xs, ys, f.(xs, ys'))\n```\n\n::: {.cell-output .cell-output-display execution_count=64}\n{}\n:::\n:::\n\n\n * An implicit equation. The constraint $f(x,y)=c$ generates an implicit equation. While `contour` can be used for this type of plot - by adjusting the requested contours - the `ImplicitPlots` package does this to make a plot of the equations $f(x,y) = 0$\"\n\n::: {.cell hold='true' execution_count=65}\n``` {.julia .cell-code}\nusing ImplicitPlots\nf(x,y) = sin(x*y) - cos(x*y)\nimplicit_plot(f)\n```\n\n::: {.cell-output .cell-output-display execution_count=65}\n{}\n:::\n:::\n\n\n### Plotting a parameterized surface $f:R^2 \\rightarrow R^3$\n\n\nThe `pyplot` (and `plotly`) backends allow plotting of parameterized surfaces.\n\n\nThe low-level `surface(xs,ys,zs)` is used, and can be specified directly as follows:\n\n::: {.cell hold='true' execution_count=66}\n``` {.julia .cell-code}\nX(theta, phi) = sin(phi)*cos(theta)\nY(theta, phi) = sin(phi)*sin(theta)\nZ(theta, phi) = cos(phi)\nthetas = range(0, pi/4, length=20)\nphis = range(0, pi, length=20)\nsurface(X.(thetas, phis'), Y.(thetas, phis'), Z.(thetas, phis'))\n```\n\n::: {.cell-output .cell-output-display execution_count=66}\n{}\n:::\n:::\n\n\n### Plotting a vector field $F:R^2 \\rightarrow R^2$.\n\n\nThe `CalculusWithJulia` package provides `vectorfieldplot`, used as:\n\n::: {.cell hold='true' execution_count=67}\n``` {.julia .cell-code}\nF(x,y) = [-y, x]\nvectorfieldplot(F, xlim=(-2, 2), ylim=(-2,2), nx=10, ny=10)\n```\n\n::: {.cell-output .cell-output-display execution_count=67}\n{}\n:::\n:::\n\n\nThere is also `vectorfieldplot3d`.\n\n\n## Limits\n\n\nLimits can be investigated numerically by forming tables, eg.:\n\n::: {.cell hold='true' execution_count=68}\n``` {.julia .cell-code}\nxs = [1, 1/10, 1/100, 1/1000]\nf(x) = sin(x)/x\n[xs f.(xs)]\n```\n\n::: {.cell-output .cell-output-display execution_count=68}\n```\n4×2 Matrix{Float64}:\n 1.0 0.841471\n 0.1 0.998334\n 0.01 0.999983\n 0.001 1.0\n```\n:::\n:::\n\n\nSymbolically, `SymPy` provides a `limit` function:\n\n::: {.cell hold='true' execution_count=69}\n``` {.julia .cell-code}\n@syms x\nlimit(sin(x)/x, x => 0)\n```\n\n::: {.cell-output .cell-output-display execution_count=69}\n```{=html}\n<span class=\"math-left-align\" style=\"padding-left: 4px; width:0; float:left;\"> \n\\[\n1\n\\]\n</span>\n```\n:::\n:::\n\n\nOr\n\n::: {.cell hold='true' execution_count=70}\n``` {.julia .cell-code}\n@syms h x\nlimit((sin(x+h) - sin(x))/h, h => 0)\n```\n\n::: {.cell-output .cell-output-error}\n```\nLoadError: invalid redefinition of constant h\n```\n:::\n:::\n\n\n## Derivatives\n\n\nThere are numeric and symbolic approaches to derivatives. For the numeric approach we use the `ForwardDiff` package, which performs automatic differentiation.\n\n\n### Derivatives of univariate functions\n\n\nNumerically, the `ForwardDiff.derivative(f, x)` function call will find the derivative of the function `f` at the point `x`:\n\n::: {.cell execution_count=71}\n``` {.julia .cell-code}\nForwardDiff.derivative(sin, pi/3) - cos(pi/3)\n```\n\n::: {.cell-output .cell-output-display execution_count=71}\n```\n0.0\n```\n:::\n:::\n\n\nThe `CalculusWithJulia` package overides the `'` (`adjoint`) syntax for functions to provide a derivative which takes a function and returns a function, so its usage is familiar\n\n::: {.cell hold='true' execution_count=72}\n``` {.julia .cell-code}\nf(x) = sin(x)\nf'(pi/3) - cos(pi/3) # or just sin'(pi/3) - cos(pi/3)\n```\n\n::: {.cell-output .cell-output-display execution_count=72}\n```\n0.0\n```\n:::\n:::\n\n\nHigher order derivatives are possible as well,\n\n::: {.cell hold='true' execution_count=73}\n``` {.julia .cell-code}\nf(x) = sin(x)\nf''''(pi/3) - f(pi/3)\n```\n\n::: {.cell-output .cell-output-display execution_count=73}\n```\n0.0\n```\n:::\n:::\n\n\n---\n\nSymbolically, the `diff` function of `SymPy` finds derivatives.\n\n::: {.cell hold='true' execution_count=74}\n``` {.julia .cell-code}\n@syms x\nf(x) = exp(-x)*sin(x)\nex = f(x) # symbolic expression\ndiff(ex, x) # or just diff(f(x), x)\n```\n\n::: {.cell-output .cell-output-display execution_count=74}\n```{=html}\n<span class=\"math-left-align\" style=\"padding-left: 4px; width:0; float:left;\"> \n\\[\n- e^{- x} \\sin{\\left(x \\right)} + e^{- x} \\cos{\\left(x \\right)}\n\\]\n</span>\n```\n:::\n:::\n\n\nHigher order derivatives can be specified as well\n\n::: {.cell hold='true' execution_count=75}\n``` {.julia .cell-code}\n@syms x\nex = exp(-x)*sin(x)\n\ndiff(ex, x, x)\n```\n\n::: {.cell-output .cell-output-display execution_count=75}\n```{=html}\n<span class=\"math-left-align\" style=\"padding-left: 4px; width:0; float:left;\"> \n\\[\n- 2 e^{- x} \\cos{\\left(x \\right)}\n\\]\n</span>\n```\n:::\n:::\n\n\nOr with a number:\n\n::: {.cell hold='true' execution_count=76}\n``` {.julia .cell-code}\n@syms x\nex = exp(-x)*sin(x)\n\ndiff(ex, x, 5)\n```\n\n::: {.cell-output .cell-output-display execution_count=76}\n```{=html}\n<span class=\"math-left-align\" style=\"padding-left: 4px; width:0; float:left;\"> \n\\[\n4 \\left(\\sin{\\left(x \\right)} - \\cos{\\left(x \\right)}\\right) e^{- x}\n\\]\n</span>\n```\n:::\n:::\n\n\nThe variable is important, as this allows parameters to be symbolic\n\n::: {.cell hold='true' execution_count=77}\n``` {.julia .cell-code}\n@syms mu sigma x\ndiff(exp(-((x-mu)/sigma)^2/2), x)\n```\n\n::: {.cell-output .cell-output-display execution_count=77}\n```{=html}\n<span class=\"math-left-align\" style=\"padding-left: 4px; width:0; float:left;\"> \n\\[\n- \\frac{\\left(- 2 \\mu + 2 x\\right) e^{- \\frac{\\left(- \\mu + x\\right)^{2}}{2 \\sigma^{2}}}}{2 \\sigma^{2}}\n\\]\n</span>\n```\n:::\n:::\n\n\n### Partial derivatives\n\n\nThere is no direct partial derivative function provided by `ForwardDiff`, rather we use the result of the `ForwardDiff.gradient` function, which finds the partial derivatives for each variable. To use this, the function must be defined in terms of a point or vector.\n\n::: {.cell hold='true' execution_count=78}\n``` {.julia .cell-code}\nf(x,y,z) = x*y + y*z + z*x\nf(v) = f(v...) # this is needed for ForwardDiff.gradient\nForwardDiff.gradient(f, [1,2,3])\n```\n\n::: {.cell-output .cell-output-display execution_count=78}\n```\n3-element Vector{Int64}:\n 5\n 4\n 3\n```\n:::\n:::\n\n\nWe can see directly that $\\partial{f}/\\partial{x} = \\langle y + z\\rangle$. At the point $(1,2,3)$, this is $5$, as returned above.\n\n---\n\n\nSymbolically, `diff` is used for partial derivatives:\n\n::: {.cell hold='true' execution_count=79}\n``` {.julia .cell-code}\n@syms x y z\nex = x*y + y*z + z*x\ndiff(ex, x) # ∂f/∂x\n```\n\n::: {.cell-output .cell-output-display execution_count=79}\n```{=html}\n<span class=\"math-left-align\" style=\"padding-left: 4px; width:0; float:left;\"> \n\\[\ny + z\n\\]\n</span>\n```\n:::\n:::\n\n\n> Gradient\n\n\n\nAs seen, the `ForwardDiff.gradient` function finds the gradient at a point. In `CalculusWithJulia`, the gradient is extended to return a function when called with no additional arguments:\n\n::: {.cell hold='true' execution_count=80}\n``` {.julia .cell-code}\nf(x,y,z) = x*y + y*z + z*x\nf(v) = f(v...)\ngradient(f)(1,2,3) - gradient(f, [1,2,3])\n```\n\n::: {.cell-output .cell-output-display execution_count=80}\n```\n3-element Vector{Int64}:\n 0\n 0\n 0\n```\n:::\n:::\n\n\nThe `∇` symbol, formed by entering `\\nabla[tab]`, is mathematical syntax for the gradient, and is defined in `CalculusWithJulia`.\n\n::: {.cell hold='true' execution_count=81}\n``` {.julia .cell-code}\nf(x,y,z) = x*y + y*z + z*x\nf(x) = f(x...)\n∇(f)(1,2,3) # same as gradient(f, [1,2,3])\n```\n\n::: {.cell-output .cell-output-display execution_count=81}\n```\n3-element Vector{Int64}:\n 5\n 4\n 3\n```\n:::\n:::\n\n\n---\n\nIn `SymPy`, there is no gradient function, though finding the gradient is easy through broadcasting:\n\n::: {.cell hold='true' execution_count=82}\n``` {.julia .cell-code}\n@syms x y z\nex = x*y + y*z + z*x\ndiff.(ex, [x,y,z]) # [diff(ex, x), diff(ex, y), diff(ex, z)]\n```\n\n::: {.cell-output .cell-output-display execution_count=82}\n```\n3-element Vector{Sym}:\n y + z\n x + z\n x + y\n```\n:::\n:::\n\n\nThe `CalculusWithJulia` package provides a method for `gradient`:\n\n::: {.cell hold='true' execution_count=83}\n``` {.julia .cell-code}\n@syms x y z\nex = x*y + y*z + z*x\n\ngradient(ex, [x,y,z])\n```\n\n::: {.cell-output .cell-output-display execution_count=83}\n```\n3-element Vector{Sym}:\n y + z\n x + z\n x + y\n```\n:::\n:::\n\n\nThe `∇` symbol is an alias. It can guess the order of the free symbols, but generally specifying them is needed. This is done with a tuple:\n\n::: {.cell hold='true' execution_count=84}\n``` {.julia .cell-code}\n@syms x y z\nex = x*y + y*z + z*x\n\n∇((ex, [x,y,z])) # for this, ∇(ex) also works\n```\n\n::: {.cell-output .cell-output-display execution_count=84}\n```\n3-element Vector{Sym}:\n y + z\n x + z\n x + y\n```\n:::\n:::\n\n\n### Jacobian\n\n\nThe Jacobian of a function $f:R^n \\rightarrow R^m$ is a $m\\times n$ matrix of partial derivatives. Numerically, `ForwardDiff.jacobian` can find the Jacobian of a function at a point:\n\n::: {.cell hold='true' execution_count=85}\n``` {.julia .cell-code}\nF(u,v) = [u*cos(v), u*sin(v), u]\nF(v) = F(v...) # needed for ForwardDiff.jacobian\npt = [1, pi/4]\nForwardDiff.jacobian(F , pt)\n```\n\n::: {.cell-output .cell-output-display execution_count=85}\n```\n3×2 Matrix{Float64}:\n 0.707107 -0.707107\n 0.707107 0.707107\n 1.0 0.0\n```\n:::\n:::\n\n\n---\n\n\nSymbolically, the `jacobian` function is a method of a *matrix*, so the calling pattern is different. (Of the form `object.method(arguments...)`.)\n\n::: {.cell hold='true' execution_count=86}\n``` {.julia .cell-code}\n@syms u v\nF(u,v) = [u*cos(v), u*sin(v), u]\nF(v) = F(v...)\n\nex = F(u,v)\nex.jacobian([u,v])\n```\n\n::: {.cell-output .cell-output-display execution_count=86}\n```\n3×2 Matrix{Sym}:\n cos(v) -u⋅sin(v)\n sin(v) u⋅cos(v)\n 1 0\n```\n:::\n:::\n\n\nAs the Jacobian can be identified as the matrix with rows given by the transpose of the gradient of the component, it can be computed directly, but it is more difficult:\n\n::: {.cell hold='true' execution_count=87}\n``` {.julia .cell-code}\n@syms u::real v::real\nF(u,v) = [u*cos(v), u*sin(v), u]\nF(v) = F(v...)\n\nvcat([diff.(ex, [u,v])' for ex in F(u,v)]...)\n```\n\n::: {.cell-output .cell-output-display execution_count=87}\n```\n3×2 Matrix{Sym}:\n cos(v) -u⋅sin(v)\n sin(v) u⋅cos(v)\n 1 0\n```\n:::\n:::\n\n\n### Divergence\n\n\nNumerically, the divergence can be computed from the Jacobian by adding the diagonal elements. This is a numerically inefficient, as the other partial derivates must be found and discarded, but this is generally not an issue for these notes. The following uses `tr` (the trace from the `LinearAlgebra` package) to find the sum of a diagonal.\n\n::: {.cell hold='true' execution_count=88}\n``` {.julia .cell-code}\nF(x,y,z) = [-y, x, z]\nF(v) = F(v...)\npt = [1,2,3]\ntr(ForwardDiff.jacobian(F , pt))\n```\n\n::: {.cell-output .cell-output-display execution_count=88}\n```\n1\n```\n:::\n:::\n\n\nThe `CalculusWithJulia` package provides `divergence` to compute the divergence and provides the `∇ ⋅` notation (`\\nabla[tab]\\cdot[tab]`):\n\n::: {.cell hold='true' execution_count=89}\n``` {.julia .cell-code}\nF(x,y,z) = [-y, x, z]\nF(v) = F(v...)\n\ndivergence(F, [1,2,3])\n(∇⋅F)(1,2,3) # not ∇⋅F(1,2,3) as that evaluates F(1,2,3) before the divergence\n```\n\n::: {.cell-output .cell-output-display execution_count=89}\n```\n1.0\n```\n:::\n:::\n\n\n---\n\nSymbolically, the divergence can be found directly:\n\n::: {.cell hold='true' execution_count=90}\n``` {.julia .cell-code}\n@syms x y z\nex = [-y, x, z]\n\nsum(diff.(ex, [x,y,z])) # sum of [diff(ex[1], x), diff(ex[2],y), diff(ex[3], z)]\n```\n\n::: {.cell-output .cell-output-display execution_count=90}\n```{=html}\n<span class=\"math-left-align\" style=\"padding-left: 4px; width:0; float:left;\"> \n\\[\n1\n\\]\n</span>\n```\n:::\n:::\n\n\nThe `divergence` function can be used for symbolic expressions:\n\n::: {.cell hold='true' execution_count=91}\n``` {.julia .cell-code}\n@syms x y z\nex = [-y, x, z]\n\ndivergence(ex, [x,y,z])\n∇⋅(ex, [x,y,z]) # For this, ∇ ⋅ F(x,y,z) also works\n```\n\n::: {.cell-output .cell-output-display execution_count=91}\n```{=html}\n<span class=\"math-left-align\" style=\"padding-left: 4px; width:0; float:left;\"> \n\\[\n1\n\\]\n</span>\n```\n:::\n:::\n\n\n### Curl\n\n\nThe curl can be computed from the off-diagonal elements of the Jacobian. The calculation follows the formula. The `CalculusWithJulia` package provides `curl` to compute this:\n\n::: {.cell hold='true' execution_count=92}\n``` {.julia .cell-code}\nF(x,y,z) = [-y, x, 1]\nF(v) = F(v...)\n\ncurl(F, [1,2,3])\n```\n\n::: {.cell-output .cell-output-display execution_count=92}\n```\n3-element Vector{Float64}:\n 0.0\n -0.0\n 2.0\n```\n:::\n:::\n\n\nAs well, if no point is specified, a function is returned for which a point may be specified using 3 coordinates or a vector\n\n::: {.cell hold='true' execution_count=93}\n``` {.julia .cell-code}\nF(x,y,z) = [-y, x, 1]\nF(v) = F(v...)\n\ncurl(F)(1,2,3), curl(F)([1,2,3])\n```\n\n::: {.cell-output .cell-output-display execution_count=93}\n```\n([0.0, -0.0, 2.0], [0.0, -0.0, 2.0])\n```\n:::\n:::\n\n\nFinally, the `∇ ×` (`\\nabla[tab]\\times[tab]` notation is available)\n\n::: {.cell ohld='true' execution_count=94}\n``` {.julia .cell-code}\nF(x,y,z) = [-y, x, 1]\nF(v) = F(v...)\n\n(∇×F)(1,2,3)\n```\n\n::: {.cell-output .cell-output-display execution_count=94}\n```\n3-element Vector{Float64}:\n 0.0\n -0.0\n 2.0\n```\n:::\n:::\n\n\nFor symbolic expressions, we have the `∇ ×` times notation is available **if** the symbolic vector contains all $3$ variables\n\n::: {.cell hold='true' execution_count=95}\n``` {.julia .cell-code}\n@syms x y z\nF = [-y, x, z] # but not [-y, x, 1] which errs; use `curl` with variables specified\n\ncurl([-y, x, 1], (x,y,z)), ∇×F\n```\n\n::: {.cell-output .cell-output-error}\n```\nLoadError: invalid redefinition of constant F\n```\n:::\n:::\n\n\n## Integrals\n\n\nNumeric integration is provided by the `QuadGK` package, for univariate integrals, and the `HCubature` package for higher dimensional integrals.\n\n``` {.julia .cell-code}\nusing QuadGK, HCubature\n```\n\n\n### Integrals of univariate functions\n\n\nA definite integral may be computed numerically using `quadgk`\n\n::: {.cell execution_count=97}\n``` {.julia .cell-code}\nquadgk(sin, 0, pi)\n```\n\n::: {.cell-output .cell-output-display execution_count=97}\n```\n(2.0, 1.7905676941154525e-12)\n```\n:::\n:::\n\n\nThe answer and an estimate for the worst case error is returned.\n\n\nIf singularities are avoided, improper integrals are computed as well:\n\n::: {.cell execution_count=98}\n``` {.julia .cell-code}\nquadgk(x->1/x^(1/2), 0, 1)\n```\n\n::: {.cell-output .cell-output-display execution_count=98}\n```\n(1.9999999845983916, 2.3762511924588765e-8)\n```\n:::\n:::\n\n\n---\n\n\nSymPy provides the `integrate` function to compute both definite and indefinite integrals.\n\n::: {.cell hold='true' execution_count=99}\n``` {.julia .cell-code}\n@syms a::real x::real\nintegrate(exp(a*x)*sin(x), x)\n```\n\n::: {.cell-output .cell-output-display execution_count=99}\n```{=html}\n<span class=\"math-left-align\" style=\"padding-left: 4px; width:0; float:left;\"> \n\\[\n\\frac{a e^{a x} \\sin{\\left(x \\right)}}{a^{2} + 1} - \\frac{e^{a x} \\cos{\\left(x \\right)}}{a^{2} + 1}\n\\]\n</span>\n```\n:::\n:::\n\n\nLike `diff` the variable to integrate is specified.\n\n\nDefinite integrals use a tuple, `(variable, a, b)`, to specify the variable and range to integrate over:\n\n::: {.cell hold='true' execution_count=100}\n``` {.julia .cell-code}\n@syms a::real x::real\nintegrate(sin(a + x), (x, 0, PI)) # ∫_0^PI sin(a+x) dx\n```\n\n::: {.cell-output .cell-output-display execution_count=100}\n```{=html}\n<span class=\"math-left-align\" style=\"padding-left: 4px; width:0; float:left;\"> \n\\[\n2 \\cos{\\left(a \\right)}\n\\]\n</span>\n```\n:::\n:::\n\n\n### 2D and 3D iterated integrals\n\n\nTwo and three dimensional integrals over box-like regions are computed numerically with the `hcubature` function from the `HCubature` package. If the box is $[x_1, y_1]\\times[x_2,y_2]\\times\\cdots\\times[x_n,y_n]$ then the limits are specified through tuples of the form $(x_1,x_2,\\dots,x_n)$ and $(y_1,y_2,\\dots,y_n)$.\n\n::: {.cell hold='true' execution_count=101}\n``` {.julia .cell-code}\nf(x,y) = x*y^2\nf(v) = f(v...)\n\nhcubature(f, (0,0), (1, 2)) # computes ∫₀¹∫₀² f(x,y) dy dx\n```\n\n::: {.cell-output .cell-output-display execution_count=101}\n```\n(1.333333333333333, 4.440892098500626e-16)\n```\n:::\n:::\n\n\nThe calling pattern for more dimensions is identical.\n\n::: {.cell hold='true' execution_count=102}\n``` {.julia .cell-code}\nf(x,y,z) = x*y^2*z^3\nf(v) = f(v...)\n\nhcubature(f, (0,0,0), (1, 2,3)) # computes ∫₀¹∫₀²∫₀³ f(x,y,z) dz dy dx\n```\n\n::: {.cell-output .cell-output-display execution_count=102}\n```\n(27.0, 0.0)\n```\n:::\n:::\n\n\nThe box-like region requirement means a change of variables may be necessary. For example, to integrate over the region $x^2 + y^2 \\leq 1; x \\geq 0$, polar coordinates can be used with $(r,\\theta)$ in $[0,1]\\times[-\\pi/2,\\pi/2]$. When changing variables, the Jacobian enters into the formula, through\n\n\n\n$$\n~\n\\iint_{G(S)} f(\\vec{x}) dV = \\iint_S (f \\circ G)(\\vec{u}) |\\det(J_G)(\\vec{u})| dU.\n~\n$$\n\n\nHere we implement this:\n\n::: {.cell hold='true' execution_count=103}\n``` {.julia .cell-code}\nf(x,y) = x*y^2\nf(v) = f(v...)\nPhi(r, theta) = r * [cos(theta), sin(theta)]\nPhi(rtheta) = Phi(rtheta...)\nintegrand(rtheta) = f(Phi(rtheta)) * det(ForwardDiff.jacobian(Phi, rtheta))\nhcubature(integrand, (0.0,-pi/2), (1.0, pi/2))\n```\n\n::: {.cell-output .cell-output-display execution_count=103}\n```\n(0.13333333333904918, 1.9853799966359355e-9)\n```\n:::\n:::\n\n\n---\n\nSymbolically, the `integrate` function allows additional terms to be specified. For example, the above could be done through:\n\n::: {.cell hold='true' execution_count=104}\n``` {.julia .cell-code}\n@syms x::real y::real\nintegrate(x * y^2, (y, -sqrt(1-x^2), sqrt(1-x^2)), (x, 0, 1))\n```\n\n::: {.cell-output .cell-output-display execution_count=104}\n```{=html}\n<span class=\"math-left-align\" style=\"padding-left: 4px; width:0; float:left;\"> \n\\[\n\\frac{2}{15}\n\\]\n</span>\n```\n:::\n:::\n\n\n### Line integrals\n\n\nA line integral of $f$ parameterized by $\\vec{r}(t)$ is computed by:\n\n\n\n$$\n~\n\\int_a^b (f\\circ\\vec{r})(t) \\| \\frac{dr}{dt}\\| dt.\n~\n$$\n\n\nFor example, if $f(x,y) = 2 - x^2 - y^2$ and $r(t) = 1/t \\langle \\cos(t), \\sin(t) \\rangle$, then the line integral over $[1,2]$ is given by:\n\n::: {.cell hold='true' execution_count=105}\n``` {.julia .cell-code}\nf(x,y) = 2 - x^2 - y^2\nf(v) = f(v...)\nr(t) = [cos(t), sin(t)]/t\n\nintegrand(t) = (f∘r)(t) * norm(r'(t))\nquadgk(integrand, 1, 2)\n```\n\n::: {.cell-output .cell-output-display execution_count=105}\n```\n(1.2399213772953277, 4.525271268818187e-9)\n```\n:::\n:::\n\n\nTo integrate a line integral through a vector field, say $\\int_C F \\cdot\\hat{T} ds=\\int_C F\\cdot \\vec{r}'(t) dt$ we have, for example,\n\n::: {.cell hold='true' execution_count=106}\n``` {.julia .cell-code}\nF(x,y) = [-y, x]\nF(v) = F(v...)\nr(t) = [cos(t), sin(t)]/t\nintegrand(t) = (F∘r)(t) ⋅ r'(t)\nquadgk(integrand, 1, 2)\n```\n\n::: {.cell-output .cell-output-display execution_count=106}\n```\n(0.5, 2.1134927141730486e-10)\n```\n:::\n:::\n\n\n---\n\n\nSymbolically, there is no real difference from a 1-dimensional integral. Let $\\phi = 1/\\|r\\|$ and integrate the gradient field over one turn of the helix $\\vec{r}(t) = \\langle \\cos(t), \\sin(t), t\\rangle$.\n\n::: {.cell hold='true' execution_count=107}\n``` {.julia .cell-code}\n@syms x::real y::real z::real t::real\nphi(x,y,z) = 1/sqrt(x^2 + y^2 + z^2)\nr(t) = [cos(t), sin(t), t]\n∇phi = diff.(phi(x,y,z), [x,y,z])\n∇phi_r = subs.(∇phi, x.=> r(t)[1], y.=>r(t)[2], z.=>r(t)[3])\nrp = diff.(r(t), t)\nglobal helix = simplify(∇phi_r ⋅ rp )\n```\n\n::: {.cell-output .cell-output-display execution_count=107}\n```{=html}\n<span class=\"math-left-align\" style=\"padding-left: 4px; width:0; float:left;\"> \n\\[\n- \\frac{t}{\\left(t^{2} + 1\\right)^{\\frac{3}{2}}}\n\\]\n</span>\n```\n:::\n:::\n\n\nThen\n\n::: {.cell execution_count=108}\n``` {.julia .cell-code}\n@syms t::real\nintegrate(helix, (t, 0, 2PI))\n```\n\n::: {.cell-output .cell-output-display execution_count=108}\n```{=html}\n<span class=\"math-left-align\" style=\"padding-left: 4px; width:0; float:left;\"> \n\\[\n-1 + \\frac{1}{\\sqrt{1 + 4 \\pi^{2}}}\n\\]\n</span>\n```\n:::\n:::\n\n\n### Surface integrals\n\n\nThe surface integral for a parameterized surface involves a surface element $\\|\\partial\\Phi/\\partial{u} \\times \\partial\\Phi/\\partial{v}\\|$. This can be computed numerically with:\n\n::: {.cell execution_count=109}\n``` {.julia .cell-code}\nPhi(u,v) = [u*cos(v), u*sin(v), u]\nPhi(v) = Phi(v...)\n\nfunction SE(Phi, pt)\n J = ForwardDiff.jacobian(Phi, pt)\n J[:,1] × J[:,2]\nend\n\nnorm(SE(Phi, [1,2]))\n```\n\n::: {.cell-output .cell-output-display execution_count=109}\n```\n1.4142135623730951\n```\n:::\n:::\n\n\nTo find the surface integral ($f=1$) for this surface over $[0,1] \\times [0,2\\pi]$, we have:\n\n::: {.cell execution_count=110}\n``` {.julia .cell-code}\nhcubature(pt -> norm(SE(Phi, pt)), (0.0,0.0), (1.0, 2pi))\n```\n\n::: {.cell-output .cell-output-display execution_count=110}\n```\n(4.442882938158366, 2.6645352591003757e-15)\n```\n:::\n:::\n\n\nSymbolically, the approach is similar:\n\n::: {.cell execution_count=111}\n``` {.julia .cell-code}\n@syms u::real v::real\nexₚ = Phi(u,v)\nJₚ = exₚ.jacobian([u,v])\nSurfEl = norm(Jₚ[:,1] × Jₚ[:,2]) |> simplify\n```\n\n::: {.cell-output .cell-output-display execution_count=111}\n```{=html}\n<span class=\"math-left-align\" style=\"padding-left: 4px; width:0; float:left;\"> \n\\[\n\\sqrt{2} \\left|{u}\\right|\n\\]\n</span>\n```\n:::\n:::\n\n\nThen\n\n::: {.cell execution_count=112}\n``` {.julia .cell-code}\nintegrate(SurfEl, (u, 0, 1), (v, 0, 2PI))\n```\n\n::: {.cell-output .cell-output-display execution_count=112}\n```{=html}\n<span class=\"math-left-align\" style=\"padding-left: 4px; width:0; float:left;\"> \n\\[\n\\sqrt{2} \\pi\n\\]\n</span>\n```\n:::\n:::\n\n\nIntegrating a vector field over the surface, would be similar:\n\n::: {.cell hold='true' execution_count=113}\n``` {.julia .cell-code}\nF(x,y,z) = [x, y, z]\nex = F(Phi(u,v)...) ⋅ (Jₚ[:,1] × Jₚ[:,2])\nintegrate(ex, (u,0,1), (v, 0, 2PI))\n```\n\n::: {.cell-output .cell-output-display execution_count=113}\n```{=html}\n<span class=\"math-left-align\" style=\"padding-left: 4px; width:0; float:left;\"> \n\\[\n0\n\\]\n</span>\n```\n:::\n:::\n\n\n",
|
||
"supporting": [
|
||
"quick_notes_files"
|
||
],
|
||
"filters": [],
|
||
"includes": {
|
||
"include-in-header": [
|
||
"<script src=\"https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.6/require.min.js\" integrity=\"sha512-c3Nl8+7g4LMSTdrm621y7kf9v3SDPnhxLNhcjFJbKECVnmZHTdo+IRO05sNLTH/D3vA6u1X32ehoLC7WFVdheg==\" crossorigin=\"anonymous\"></script>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js\" integrity=\"sha512-bLT0Qm9VnAYZDflyKcBaQ2gg0hSYNQrJ8RilYldYQ1FxQYoCLtUjuuRuZo+fjqhx/qtq/1itJ0C2ejDxltZVFg==\" crossorigin=\"anonymous\"></script>\n<script type=\"application/javascript\">define('jquery', [],function() {return window.jQuery;})</script>\n"
|
||
]
|
||
}
|
||
}
|
||
} |