{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# \n", "\n", "# Exploring first and second derivatives with Julia\n", "\n", "To get started, we load the `MTH229` package:" ], "id": "4e014869-ad10-4694-8f42-d325f6eb508c" }, { "cell_type": "code", "execution_count": 0, "metadata": {}, "outputs": [], "source": [ "using MTH229\n", "using Plots\n", "plotly()" ], "id": "2" }, { "cell_type": "markdown", "metadata": {}, "source": [ "Recall, when `MTH229` is loaded, the same prime notation of mathematics\n", "is available in `Julia` for indicating derivatives of functions (through\n", "`ForwardDiff`).\n", "\n", "## Quick background\n", "\n", "Read about this material here: [Exploring first and second derivatives\n", "with Julia](http://mth229.github.io/first-second-derivatives.html).\n", "\n", "This assignment looks at the relationship between a function, $f(x)$,\n", "and its first and second derivatives: $f'(x)$ and $f''(x)$. The basic\n", "relationship can be summarized (though the devil is in the details) by:\n", "\n", "- If the first derivative is *positive* on $(a,b)$ then the function\n", " is *increasing* on $(a,b)$.\n", "\n", "- If the second derivative is *positive* on $(a,b)$ then the function\n", " is *concave up* on $(a,b)$.\n", "\n", "(The “devil” here is that the converse statements are usually - but not\n", "always - true.)\n", "\n", "Some key definitions are:\n", "\n", "- A **critical** point of $f$ is a value in the domain of $f(x)$ for\n", " which the derivative is $0$ or undefined. These are often—but **not\n", " always**—where $f(x)$ has a local maximum or minimum.\n", "\n", "- An **inflection point** of $f$ is a value in the domain of $f(x)$\n", " where the concavity of $f$ *changes*. (These are *often*—but **not\n", " always**—where $f''(x)=0$.)\n", "\n", "These two relationships and definitions are put to use to characterize\n", "*local extrema* of a function via one of two “derivative” tests:\n", "\n", "- The **first derivative test**: This states that if $c$ is a critical\n", " point of $f(x)$ then if $f'(x)$ changes sign from $+$ to $-$ at $c$\n", " then $f(c)$ is a local maximum and if $f'(x)$ changes sign from $-$\n", " to $+$ then $f(c)$ is a local minimum. If there is no sign change,\n", " then $f(c)$ is neither a local minimum or maximum.\n", "\n", "- The **second derivative test**: This states that if $c$ is a\n", " critical point of $f(x)$ and $f''(c) > 0$ then $f(c)$ is a local\n", " minimum and if $f''(c) < 0$ then $f(c)$ is a local maximum. The test\n", " says nothing about the case $f''(c) = 0$.\n", "\n", "------------------------------------------------------------------------\n", "\n", "We investigate these concepts in `Julia` both graphically and\n", "numerically.\n", "\n", "For the graphical exploration, the `plotif` function from the `MTH229`\n", "package is quite useful.\n", "\n", "It is used to plot a function `f` using two colors; the color choice\n", "depending on whether the second function, `g` is non-negative or not.\n", "(Basically it does\n", "`plot(f, a, b); plot!(x -> g(x) >= 0.0 ? f(x) : NaN, a, b)`.)\n", "\n", "This function can be used to graphically illustrate where the graph of\n", "`f` is *positive*, *increasing*, or *concave up*: `plotif(f, f, a, b)`\n", "will show a different color when $f(x)$ is *positive*,\n", "`plotif(f, f', a, b)` will show a different color when $f(x)$ is\n", "*increasing*, and `plotif(f, f'', a, b)` will show a different color\n", "when $f(x)$ is *concave up*. For example, here we see where the\n", "following `f` is increasing:" ], "id": "1ce130c3-21ed-437f-a7d5-9e863a0646ae" }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "output_type": "display_data", "metadata": {}, "data": { "text/html": [ "" ] } } ], "source": [ "f(x) = 1 + cos(x) + cos(2x)\n", "plotif(f, f', 0, 2pi) # color increasing\n", "plot!(zero)" ], "id": "6" }, { "cell_type": "markdown", "metadata": {}, "source": [ "### `fzeros`\n", "\n", "Once eyes are trained to identify zeros, critical points, or inflection\n", "points of a function, we can use numeric methods to zoom in on more\n", "accurate values.\n", "\n", "Recall, `fzero(f, a, b)` will find a zero of `f` **if** `[a,b]` is a\n", "*bracketing* interval (`f` takes different signs at the endpoints); and\n", "`fzero(f, c)` will try to find a zero *near* `c`.\n", "\n", "These two methods are incorporated into the convenience function\n", "`fzeros(f, a, b)` which *attempts* to identify all zeros within\n", "`[a, b]`.\n", "\n", "It is called like `plot` with a function and two values indicating the\n", "interval to scan:" ], "id": "9feac28b-fbbc-4c6b-831d-43845dd20817" }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "output_type": "display_data", "metadata": {}, "data": { "text/plain": [ "4-element Vector{Float64}:\n", " 1.5707963267948966\n", " 2.0943951023931953\n", " 4.1887902047863905\n", " 4.71238898038469" ] } } ], "source": [ "zs = fzeros(f, 0, 2pi)" ], "id": "8" }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `fzeros` function returns a container of values, which may, of\n", "course, be empty. Above we assigned the name `zs` to these values.\n", "\n", "There are times when applying a function to the returned values is\n", "desired. The “dot” makes this easy. Here we apply `f` to the values in\n", "`zs` and expect to see values near `0`:" ], "id": "156dc634-9ae8-4fde-8a62-d4f665c0827c" }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "output_type": "display_data", "metadata": {}, "data": { "text/plain": [ "4-element Vector{Float64}:\n", " 0.0\n", " -2.220446049250313e-16\n", " 3.3306690738754696e-16\n", " -2.220446049250313e-16" ] } } ], "source": [ "f.(zs)" ], "id": "10" }, { "cell_type": "markdown", "metadata": {}, "source": [ "------------------------------------------------------------------------\n", "\n", "If our task was to get *all* critical points of `f` in the interval\n", "$(0, 2\\pi)$, then `fzeros` is an easy-to-use choice: As `f` is\n", "continuously differentiable, all critical points are given by solving\n", "$f'(x)=0$:" ], "id": "6515e439-61d1-4b47-96bf-a60f82cc11f4" }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "output_type": "display_data", "metadata": {}, "data": { "text/plain": [ "5-element Vector{Float64}:\n", " 0.0\n", " 1.8234765819369751\n", " 3.141592653589793\n", " 4.459708725242611\n", " 6.283185307179586" ] } } ], "source": [ "fzeros(f', 0, 2pi)" ], "id": "12" }, { "cell_type": "markdown", "metadata": {}, "source": [ "### `sign_chart`\n", "\n", "These same values are also identified by `sign_chart` (which uses\n", "`fzeros` behind the scenes) and indicates whether the function changes\n", "sign at the identified zeros (or undefined values):" ], "id": "45b292ac-cbc0-4557-8d64-2d64dc71fa74" }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "output_type": "display_data", "metadata": {}, "data": { "text/html": [ "
5-element Vector{NamedTuple{(:zero_oo_NaN, :sign_change)}}:\n",
              " (zero_oo_NaN = 0.0, sign_change = an endpoint)\n",
              " (zero_oo_NaN = 1.82347658194, sign_change = - to +)\n",
              " (zero_oo_NaN = 3.14159265359, sign_change = + to -)\n",
              " (zero_oo_NaN = 4.45970872524, sign_change = - to +)\n",
              " (zero_oo_NaN = 6.28318530718, sign_change = an endpoint)
" ] } } ], "source": [ "sign_chart(f', 0, 2pi)" ], "id": "14" }, { "cell_type": "markdown", "metadata": {}, "source": [ "This output shows, for example, that $f'(x)$ is positive on\n", "$(1.82\\dots, 3.14\\dots)$ and again on $(4.45\\dots, 6.28\\dots)$. This\n", "could be used to conclude where $f(x)$ is *increasing*.\n", "\n", "------------------------------------------------------------------------\n", "\n", "If our task is to get all *inflection* points of `f` in the interval,\n", "then the `sign_chart` function is helpful, as inflection points are\n", "points in the domain where the concavity changes:" ], "id": "60beda6d-e35c-4fdc-a715-e2544a3c4203" }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "output_type": "display_data", "metadata": {}, "data": { "text/html": [ "
4-element Vector{NamedTuple{(:zero_oo_NaN, :sign_change)}}:\n",
              " (zero_oo_NaN = 0.866676087105, sign_change = - to +)\n",
              " (zero_oo_NaN = 2.45335016275, sign_change = + to -)\n",
              " (zero_oo_NaN = 3.82983514443, sign_change = - to +)\n",
              " (zero_oo_NaN = 5.41650922007, sign_change = + to -)
" ] } } ], "source": [ "sign_chart(f'', 0, 2pi)" ], "id": "16" }, { "cell_type": "markdown", "metadata": {}, "source": [ "As the sign changes at each of the identified values, all `4` are\n", "inflection points.\n", "\n", "### Careful\n", "\n", "The convenient `fzeros` and `sign_chart` functions are only *pretty*\n", "good at finding all the zeros over the interval. For some functions –\n", "especially those cooked up by clever math professors – the choice of\n", "interval can lead to `fzeros` finding fewer than is mathematically\n", "possible. The functions should be used in combination with a plot and\n", "with as narrow an interval specified, as reasonable.\n", "\n", "------------------------------------------------------------------------" ], "id": "97a4b0b9-c48b-4767-8329-f36575b18b98" }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "# Your commands go here\n" ], "id": "18" } ], "nbformat": 4, "nbformat_minor": 5, "metadata": { "kernel_info": { "name": "julia" }, "kernelspec": { "name": "julia", "display_name": "Julia", "language": "julia" }, "language_info": { "name": "julia", "codemirror_mode": "julia", "version": "1.10.0" } } }