8  Exploring first and second derivatives with Julia


A notebook for this material: ipynb

8.1 Introduction

We load the MTH229 package and plotting package for this section:

using MTH229
using Plots
plotly()
Plots.PlotlyBackend()

This project explores the relationship between a function, \(f(x)\) and its first and second derivatives.

The following definitions describe features of functions over an interval:

  • The function \(f(x)\) is positive on an interval \(I=(a,b)\) if \(f(x) > 0\) for any \(x\) in \((a,b)\).
  • The function \(f(x)\) is increasing (strictly) on an interval \(I=(a,b)\) if \(f(x) < f(y)\) whenever \(a < x < y < b\).
  • The function \(f(x)\) is concave up on an interval \(I=(a,b)\) if any secant line between values \(x\) and \(y\) with \(a < x < y < b\) lies above the graph of \(f(x)\).
Note

There are similar definitions for negative, decreasing, and concave down.

8.1.1 Consequences

You may be more familiar with these implications for functions with derivatives:

  • An increasing function will have the property that any secant line connecting \(x\) and \(y\) with \(a < x < y < b\) will have positive slope.
  • An increasing function on \(I\) that is differentiable has \(f'(x) \geq 0\) on \(I\).
  • If \(f'(x)\) exists and is positive on \(I=(a,b)\), then \(f(x)\) is increasing on \(I\).
  • If \(f'(x)\) exists and is increasing on \(I=(a,b)\), then \(f(x)\) is concave up on \(I\).
  • If \(f''(x)\) exists and is positive on \(I=(a,b)\), then \(f'(x)\) is increasing on \(I\), and so \(f(x)\) is concave up on \(I\).
Positive and negative

Pay attention to the difference between positive and non-negative. For example, an increasing, differentiable function on I=(a,b) is only guaranteed to have a non-negative derivative – not a positive derivative on I.

8.2 Identifying when a function is positive

A common task in this subject is identifying where a function is positive, negative, zero, or undefined.

A theoretical tool is the intermediate value theorem which can be used to say that a continuous function has only one sign (positive or negative) between adjacent zeros. That is, if \(a\) and \(b\) are zeros of \(f(x)\), a continuous function with say \(a < b\) and there is no \(c\) with \(a < c < b\) such that \(f(c) = 0\), then \(f(x)\) for \(x\) in \((a,b)\) is either always positive or always negative.

This gives a technique to identify where a function is positive:

  • find all the zeros of \(f(x)\) and sort them in increasing order
  • check the sign between adjacent zeros at one point, a test point (often chosen conveniently to make evaluation easier).
  • Also check the sign to the left of the smallest zero and the right of the largest zero by using two test points.

Wherever one of the test points is positive, the interval between the two adjacent zeros will correspond to an interval where the function is positive.

If the function is not continuous, say at a finite set of points, then include the points of discontinuity in the consideration. In fact, only discontinuities where the function jumps over the \(y\) axis need consideration.

8.2.1 Graphically identifying zeros of a function over \([a,b]\)

We can graphically identify when a function is positive with the strategy above. Consider the following function over the interval \([-2, 1]\):

\[ f(x) = e^{-x} \cdot (\sin(3x) + \sin(5x)). \]

A plot shows roughly that this function is positive between \(-2\) and \(-0.8\), save near \(-1.57\) where \(f\) is \(0\) and between \(0.0\) and \(0.8\).

f(x) = exp(-x) * (sin(3x) + sin(5x))
plot(f, -2, 1)
plot!(zero)

The MTH229 package loads a convenience function to color the graph of f when a second function, g is nonnegative, called plotif. In this case, we want g to be f:

plotif(f, f, -2, 1)

The plotif function show when g is non-negative, not just positive, so wouldn’t show a different color for the graph near \(-1.57\) where f is zero but doesn’t cross the \(x\) axis.

8.2.2 Numerically find zeros of a function over \([a,b]\); the find_zeros function

We have discussed two means to find zeros of a function \(f\):

  • when the zero is between two values which form a bracketing interval. The zero in the graph above between \(-1\) and \(-0.5\) is an example. Such zeros are readily identified through bisection

  • when a zero is near a value, such as these four which are near \(-1.5\), \(-0.75\), \(0.0\) and \(0.8\). Such zeros may typically be found with Newton’s method (or find_zero)

The Roots package, exported with MTH229, provides a convenience function, find_zeros, which uses bisection or fzero to identify heuristically all zeros in an interval \([a,b]\). It is easy to use: the interval is specified by pairing off the interval:

find_zeros(f, (-2, 1))  # note the s in find_zeros
4-element Vector{Float64}:
 -1.570796326120076
 -0.7853981633974483
  0.0
  0.7853981633974483

The algorithm isn’t guaranteed to always work the way bisection is, but works fairly well to identify all the zeros execept for cases where there are many zeros in the interval or the interval chosen is too wide. (A bracketing interval specified to bisection may be infinite, but such wide intervals can be problematic with find_zeros, though can work in some cases.)

Here is another example, find the zeros of \(f(x) = e^x - x^4\) over \([-10, 10]\):

f(x) = exp(x) - x^4
find_zeros(f, -10, 10)
3-element Vector{Float64}:
 -0.8155534188089606
  1.4296118247255556
  8.613169456441398

The above also shows the interval can also be specified using two values, as is done with plot.

A related problem might be find all zeros of \(f(x) = e^x - x^6\). Where should one look to ensure all of the zeros are identified? Too big an interval and the values of \(e^x\) will be infinite (numerically) and that may cause problems. Too small an interval and zeros may be excluded. In this case a bit of analysis can help: any zero must be bigger than \(-1\), as to the left of \(-1\) the exponential is smaller than \(1\) and the polynomial bigger than \(1\) in absolute value. Further, as exponentials eventually dominate polynomials, the interval need not be too large. We could quickly check that \(e^{20}\) is bigger than \(20^6\) but instead we just try a somewhat larger right limit:

f(x) = exp(x) - x^6
find_zeros(f, (-1, 50))
3-element Vector{Float64}:
 -0.8656497043253173
  1.2268886960394931
 16.99888735229605

Discontinuous functions

The algorithm used by find_zeros relies on both bisection and a method like Newton’s method. The bisection implementation will pick up on places where the function changes sign, even if not continuous. In the example below, the two vertical asymptotes at \(3\) and \(4\) are found:

f(x) = (x-1)*(x-2) / ((x-3)*(x-4))
zs = find_zeros(f, -10, 10)
4-element Vector{Float64}:
 1.0
 2.0
 2.9999999999999996
 3.9999999999999996

The values at the zs can easily be checked using broadcasting:

f.(zs)  # using the broadcasting syntax -- the dot.
4-element Vector{Float64}:
 -0.0
  0.0
  4.503599627370491e15
 -1.351079888211149e16

Programatically, the filter function might be used to screen these out, or in this example a comprehension:

[z for z in zs if abs(f(z)) <= 10]
2-element Vector{Float64}:
 1.0
 2.0

However, for our usage below it is quite helpful that these points are identified.

8.2.3 The sign_chart function

The technique above to find where a function is positive can be automated if find_zeros is used to find the zeros. This is done in sign_chart.

For example,

f(x) = exp(x) - x^6
sign_chart(f, -1, 20)
3-element Vector{NamedTuple{(:zero_oo_NaN, :sign_change)}}:
 (zero_oo_NaN = -0.865649704325, sign_change = - to +)
 (zero_oo_NaN = 1.22688869604, sign_change = + to -)
 (zero_oo_NaN = 16.9988873523, sign_change = - to +)

The output shows how the sign changes at the identified zeros (or other points). In this case we can infer that the function is positive on \((-0.8656\dots, 1.22688869\dots)\) and again on \((16.998887\dots, \infty)\).


Returning to the example function

f(x) = exp(-x) * (sin(3x) + sin(5x))
f (generic function with 1 method)

we apply sign_chart over \((-2,1)\):

sign_chart(f, -2, 1)
4-element Vector{NamedTuple{(:zero_oo_NaN, :sign_change)}}:
 (zero_oo_NaN = -1.57079632612, sign_change = + to +)
 (zero_oo_NaN = -0.785398163397, sign_change = + to -)
 (zero_oo_NaN = 0.0, sign_change = - to +)
 (zero_oo_NaN = 0.785398163397, sign_change = + to -)

The output matches the graph produced above with plotif. The zero at \(-1.57\dots\) does not have a sign change, the others do in agreement with the graph.

Question

How many zeros are found between \(-2\) and \(1\) for

\[ f(x) = e^{-x/2} \cdot (\sin(11x) + \sin(13x)) \]


Question

What is the largest value identified in \([-2,1]\) by find_zeros for the function

\[ f(x) = e^{-x/2} \cdot (\sin(11x) + \sin(13x)) \]


Question

For \(f(x) = e^{-x/2} \cdot (\sin(11x) + \sin(13x))\) Consider the output of sign_chart(f, -2, 1). What is the sign of \(f\) at \(-1\)?

Select an item

Question

Consider \(f(x) = \sin(3x) + \sin(5x)\) over \([-2, 1]\). What does sign_chart identify at the smallest zero?

Select an item

8.3 Graphically Identifying increasing, concave up, critical points, …

As seen, the plotif function loaded with the MTH229 package makes it easy for us to highlight when a function is non negative, as it shows in a separate color when the second function is non-negative.

For example, consider \(f(x) = x^3 - 2x - 1/2\).

This graph shows where \(f(x)\) is positive over \((-2,2)\):

f(x) = x^3 - 2x - 1/2
plotif(f, f, -2, 2)
plot!(zero)

In this graph, we estimate graphically that the intervals \((-1.2, 0.2)\) and \((1.5, 2)\) are where \(f\) is positive within this viewing window.

If we change the screening function to the derivative of \(f(x)\), then our graph will show when the function is increasing (or flat):

plotif(f, f', -2, 2)

Again, we eyeball from the graph to estimate that this occurs on \((-2, -0.8)\) and \((0.8, 2)\).

Using when the second derivative is non-negative indicates where f is concave up:

plotif(f,  f'', -2, 2)

We can see the function is concave up on \((0, 2)\) and changes concavity at the inflection point \(x=0\).

Question

Graphically identify when the function \(f(x) = x^x\) is increasing on \((0,2)\)

Select an item

Question

Graphically identify when the function \(f(x) = \sqrt{|1 - x^2|}\) is increasing on the interval \([-2, 2]\).

Select an item

Question

Graphically identify when the function \(f(x) = x^2 \cdot e^{-x}\) is concave up on the interval \((0,10)\).

Select an item

Question

Consider the function below:

f(x) = x < -1 ? -x - 1 : x > 1 ? x - 1 : 0

What does the output of plotif(f, f', -5, 5) show?

Select an item

8.3.1 Numerically finding zeros, critical points, and inflection point candidates

The find_zeros function can readily identify when some function is zero (or undefined). It can be used, as seen, to find zeros. However, if passed f' it will identify critical points. When passed f'' it will find when \(f''(x) = 0\) (or undefined). Such points are candidates for inflection points, though a check on whether the concavity changes is necessary to say such is an inflection point.

In the following we use scatter! to show these values on the graph.

f(x) = exp(-x/2) * (sin(3x) + sin(5x))
zs = find_zeros(f, -2, 1)
cps = find_zeros(f', -2, 1)
ips = find_zeros(f'', -2, 1)

plot(f, -2, 1)
scatter!(zs, f.(zs), markercolor="blue", markersize=7)
scatter!(cps, f.(cps), markercolor="red", markersize=3)
scatter!(ips, f.(ips), markercolor="green", markershape=:diamond)

An attempt to distinguish the points using color, marker size and shape is made, showing that the value at \(-1.5707963267\dots\) is both a zero and a critical point.

The graph also shows that the candidates for inflection points are indeed inflection points. This could also be verified with sign_chart where the second derivative changes sign at each:

sign_chart(f'', -2, 1)
5-element Vector{NamedTuple{(:zero_oo_NaN, :sign_change)}}:
 (zero_oo_NaN = -1.86089851657, sign_change = - to +)
 (zero_oo_NaN = -1.33370970092, sign_change = + to -)
 (zero_oo_NaN = -0.739296253922, sign_change = - to +)
 (zero_oo_NaN = -0.0524780565601, sign_change = + to -)
 (zero_oo_NaN = 0.645452569404, sign_change = - to +)

8.4 Relationships

Suppose we only know indirect things about a function \(f(x)\), how much can we say?

We previously mentioned two basic relationships:

If \(f'(x) > 0\) on an interval \((a,b)\) then the function \(f(x)\) is increasing on \((a,b)\). (It may also increase when \(f'(x)=0\), but it isn’t guaranteed.)

If \(f''(x) > 0\) on \((a,b)\) then the function \(f(x)\) is concave up on \((a,b)\).

Similar statements can be made for negative values of the derivative and second derivative.

For example, lets suppose we know that the derivative of \(f(x)\) is \(f'(x) = \exp(x)\). What can we say about \(f(x)\) on the interval \((0,4)\)?

We make two graphs:

fp(x) = exp(x)
plotif(fp, fp, 0, 4)
plotif(fp, fp', 0, 4)

From the graphs we see that \(f'(x)\) is always positive and increasing.

From the first fact (\(f'(x) > 0\)) we know that \(f(x)\) is increasing on this interval.

From the second fact (\(f'(x)\) is increasing) we know that \(f(x)\) is concave up on this interval.

Do we know any specific values, or even less ambitiously, when \(f(x)\) is positive? The answer must be no – we could always add a constant to \(f(x)\) and not effect its derivative.


Now suppose we have a different \(f(x)\). In this case all we know is the second derivative is \(f''(x) = x^2 - 2x\). What can we say about \(f(x)\) on the interval \((-1,3)\)?

A plot to see where the second derivative is positive will show that this \(f''(x)\) is positive on \((-1, 0)\) and \((2,3)\):

fpp(x) = x^2 - 2x
plotif(fpp, fpp, -1, 3)

This means \(f(x)\) is concave up on these same intervals.

Can we tell if our unknown \(f(x)\) is increasing? Nope, we have no such ability. We could always add a term \(mx\) to \(f(x)\) and keep the same second derivative. So we can’t tell if the function \(f(x)\) is increasing and we can’t tell where the function \(f(x)\) is positive, but we can say where it is concave up when we all we know is a second derivative.

Question

You know that \(f'(x) = |x|\). Over \([-1,1]\) where if \(f(x)\) increasing and where is it concave up?

Select an item

Question

Suppose \(f'(x) = (x^4 - x^2 + 2)/(x^4 - 2x^2 + 1)\). When is \(f(x)\) increasing on \((-2, 2)\)?

Select an item

Question

If \(f'(x) = \tan(x) - 3x/2\). When is \(f(x)\) concave down on the interval \((-\pi/3, \pi/3)\)?

Select an item

8.5 Classifying critical points

The first- and second-derivative tests are a means to classify if a critical point is also a local extrema. A local extrema will always correspond to a critical point – but not necessarily vice versa. There are two theorems that ensure a critical point will be a local extrema:

The first derivative test

If \(c\) is a critical point and \(f'(x)\) changes sign at \(x=c\), then \((c,f( c))\) will be a local extrema. (If the sign change is from positive to negative, it will be a local maximum.) If there is no sign change, the critical point is not a local extrema.

The second derivative test

If \(c\) is a critical point and \(f''(x) \neq 0\) then \((c,f( c))\) will be a local extrema. If \(f''( c) > 0\) this will be a local minimum and if \(f''( c) < 0\) a local maximum. (Nothing conclusive can be said when \(f''(c)=0\).)


For example, let \(f(x) = 2\sin(x) + \cos(2x)\). Find all critical points on \([0, 2\pi]\).

As \(f(x)\) is everywhere differentiable, these critical points would be where the derivative is \(0\). A plot helps us identify how many and roughly where:

f(x) = 2sin(x) + cos(2x)
plot(f', 0, 2pi)

We see four: one near \(0.8\), one near \(1.5\), one near \(2.5\) and one near \(5\).

As explained, we could use find_zeros to identify these more precisely:

cps = find_zeros(f', 0, 6)
4-element Vector{Float64}:
 0.5235987755982988
 1.5707963267948966
 2.617993877991494
 4.71238898038469

From the graph of f' we can see at the first critical point the derivative is changing sign from positive to negative at the first one (hence a local maximum), and this alternates as we go along.

However, we can do this numerically, using the sign_chart function.

sign_chart(f', 0, 4)
3-element Vector{NamedTuple{(:zero_oo_NaN, :sign_change)}}:
 (zero_oo_NaN = 0.523598775598, sign_change = + to -)
 (zero_oo_NaN = 1.57079632679, sign_change = - to +)
 (zero_oo_NaN = 2.61799387799, sign_change = + to -)

So by the first derivative test we have a max, min, max, min which we see when we plot f:

plot(f, 0, 2pi)

To get the same classification from the second derivative test, we evaluate \(f''(x)\) at these four critical points:

f''.(cps)
4-element Vector{Float64}:
 -3.0000000000000004
  2.0
 -2.9999999999999982
  6.0

Where using the dot broadcasts f'' over all the values in cps.

The following graphic is to illustrate why the second derivative test works as it does:

plot(f, 0, 2pi; legend=false, linewidth=4)
cps = find_zeros(f', 0, 2pi)
plot_parabola!(c, fc, fppc) = plot!(x -> fc + fppc * (x - c)^2/2, c-3/4, c+3/4; linewidth=2)
plot_parabola!.(cps, f.(cps), f''.(cps))
current()

Each critical point is matched with a parabola with quadratic term one-half the second derivative. We can see at a relative minimum, then the parabola opens upward just like the graph of f; at a relative maximum, the approximating parabola opens downward.

Question

Let \(f'(x) = x^4 - 4x^2 + 1\). Classify all relative extrema of \(f(x)\) on the interval \((-1, 1)\).

Select an item

Question

Let fp(x) = (2x-1)/cbrt(x^2 - x - 2)^2. Over the interval \((-1.5,1.5)\) identify all relative maxima and minima of \(f(x)\).

Select an item

Question

Suppose, \(f(x)\) has a critical at \(0\) and \(1\). If possible, classify them as relative maximum or minimum assuming \(f''(x) = 1 - 2x - \sin(x)\)

Select an item

Julia version:

VERSION
v"1.10.2"

Packages and versions:

using Pkg
Pkg.status()
Status `~/work/mth229.github.io/mth229.github.io/Project.toml`
  [a2e0e22d] CalculusWithJulia v0.2.1
  [7073ff75] IJulia v1.24.2
  [b964fa9f] LaTeXStrings v1.3.1
  [ebaf19f5] MTH229 v0.3.1
  [91a5bcdd] Plots v1.40.4
  [f27b6e38] Polynomials v4.0.7
  [438e738f] PyCall v1.96.4
  [612c44de] QuizQuestions v0.3.23
  [295af30f] Revise v3.5.14
  [f2b01f46] Roots v2.1.5
  [24249f21] SymPy v2.0.1
  [56ddb016] Logging