# Curve sketching


{{< include ../_common_code.qmd >}}

This section uses the following add-on packages:


```{julia}
using CalculusWithJulia
using Plots
plotly()
using SymPy
using Roots
import Polynomials: variable, Polynomial, RationalFunction # avoid name clash
```


---


Drawing a sketch of a function can be enhanced by identifying as many of the following as possible before filling in a curve representing the function:

* asymptotic behaviour (as $x \rightarrow \pm \infty$),

* periodic behaviour,

* vertical asymptotes,

* the $y$ intercept,

* any $x$ intercept(s),

* local peaks and valleys (relative extrema).

* concavity


@fig-animation-sketch-sin-plot and @fig-animation-sketch-rational-function-plot illustrate this approach for two different types of functions.

::: {#fig-animation-sketch-sin-plot}
```{julia}
#| hold: true
#| echo: false
#| cache: true
### {{{ sketch_sin_plot }}}
gr()

function sketch_sin_plot_graph(i)
    f(x) = 10*sin(pi/2*x)  # [0,4]
    deltax = 1/10
    deltay = 5/10

    zs = find_zeros(f, 0-deltax, 4+deltax)
    cps = find_zeros(D(f), 0-deltax, 4+deltax)
    xs = range(0, stop=4*(i-2)/6, length=50)
    if i == 1
        ## plot zeros
        title = "Plot the zeros"
        p = scatter(zs, 0*zs, title=title, xlim=(-deltax,4+deltax), ylim=(-10-deltay,10+deltay), legend=false)
    elseif i == 2
        ## plot extrema
        title = "Plot the local extrema"
        p = scatter(zs, 0*zs, title=title, xlim=(-deltax,4+deltax), ylim=(-10-deltay,10+deltay), legend=false)
        scatter!(p, cps, f.(cps))
    else
        ##  sketch graph
        title = "sketch the graph"
        p = scatter(zs, 0*zs, title=title, xlim=(-deltax,4+deltax), ylim=(-10-deltay,10+deltay), legend=false)
        scatter!(p, cps, f.(cps))
        plot!(p, xs, f.(xs))
    end
    p
end


caption = ""
n = 8
anim = @animate for i=1:n
    sketch_sin_plot_graph(i)
end

imgfile = tempname() * ".gif"
gif(anim, imgfile, fps = 1)
plotly()
ImageFile(imgfile, caption)
```

After identifying asymptotic behaviors, a curve sketch involves identifying the $y$ intercept, if applicable; the $x$ intercepts, if possible; the local extrema; and changes in concavity. From there a sketch fills in between the points. In this example, the periodic function $f(x) = 10\cdot\sin(\pi/2\cdot x)$ is sketched over $[0,4]$.
:::



::: {#fig-animation-sketch-rational-function-plot}
```{julia}
#| echo: false
#| cache: true
### {{{ sketch_sin_plot }}}
let
    gr()

    function sketch_rational_plot_graph(i)
        f(x) = 1/2 + (x-1)^2/((x-2)^2 * (x+1))

        plt = plot(; xlim=(-4, 5), ylim=(-5, 5), xticks = -4:5, legend=false, framestyle=:origin)

        title = "Sketch a rational function"
        # (1) asymptotic behaviour (as $x \rightarrow \pm \infty$),
        i == 1 && (title = L"A horizontal asymptote $y=1/2$")
        if i >= 1
            hline!(plt, [1/2]; line=(:dash, :gray50))
        end

        # (2)  periodic behaviour,
        i == 2 && (title = "No periodic behaviours")
        if i >= 2

        end

        # (3)  vertical asymptotes,
        i == 3 && (title = L"Vertical asyptotes at $x=-1$ and $x=2$")
        if i >= 3
            vline!(plt, [-1, 2]; line=(:dash, :gray50))
        end

        # (4)  the $y$ intercept,
        i == 4 && (title = L"f(0) = 3/4")
        if i >= 4
            scatter!(plt, [(0, f(0))]; marker=(5, :green))
        end

        # (5) any $x$ intercept(s)
        i == 5 && (title = L"Zero at $x=-2.1527576\cdots$")
        if i >= 5
            z = -2.1527576
            scatter!(plt, [(z, 0)]; marker=(5, :red))
        end

        # (6) local peaks and valleys (relative extrema).
        i == 6 && (title = L"Relative minimum at $(1, f(1))$")
        if i >= 6
            scatter!(plt, [(1,f(1))]; marker = (5, :blue))
        end

        # (7) concavity
        i == 7 && (title = L"Concave up on $(-1, 2)$, $(2, 3)$"        )
        if i == 7
            xs = range(-0.4, 0.4, 100)
            ys = 1 .+ sec.(xs.* pi)
            plot!(xs .- 2.5, -1 .* ys; line=(3, 0.25, :blue), arrow=true, side=:right)
            plot!(xs .+ 1.0, 1 .* ys;  line=(3, 0.25, :blue), arrow=true, side=:right)
            plot!(xs .+ 3.5, 1 .* ys;  line=(3, 0.25, :blue), arrow=true, side=:right)

        end

        # connect the dots
        if i >= 8
            title = "Sketch"
            plot!(plt, rangeclamp(f, 5); line = (1, :black))
        end
        title!(plt, title)
        plt

    end

    caption = ""
    n = 10
    anim = @animate for i=1:n
        sketch_rational_plot_graph(i)
    end

    imgfile = tempname() * ".gif"
    gif(anim, imgfile, fps = 1)
    plotly()

    ImageFile(imgfile, caption)
end
```

After identifying asymptotic behaviors, a curve sketch involves identifying the $y$ intercept, if applicable; the $x$ intercepts, if possible; the local extrema; and changes in concavity. From there a sketch fills in between the points. In this example, the rational function $f(x) = 1/2 + (x-1)^2/((x-2)^2 \cdot (x+1))$ is sketched.
:::


Though this approach is most useful for hand-sketches, the underlying concepts are important for properly framing graphs made with the computer.


We can easily make a graph of a function over a specified interval. What is not always so easy is to pick an interval that shows off the features of interest. In the section on [rational](../precalc/rational_functions.html) functions there was a discussion about how to draw graphs for rational functions so that horizontal and vertical asymptotes can be seen. These are properties of the "large." In this section, we build on this, but concentrate now on more local properties of a function.


##### Example


Produce a graph of the function $f(x) = x^4 -13x^3 + 56x^2-92x + 48$.


We identify this as a fourth-degree polynomial with positive leading coefficient. Hence it will eventually look $U$-shaped. If we graph over a too-wide interval, that is all we will see. Rather, we do some work to produce a graph that shows the zeros, peaks, and valleys of $f(x)$. To do so, we need to know the extent of the zeros. We can try some theory, but instead we just guess and if that fails, will work harder:


```{julia}
f(x) = x^4 - 13x^3 + 56x^2 -92x + 48
rts = find_zeros(f, -10, 10)
```

As we found $4$ roots, we know by the fundamental theorem of algebra we have them all. This means, our graph need not focus on values much larger than $6$ or much smaller than $1$.


To know where the peaks and valleys are, we look for the critical points:


```{julia}
cps = find_zeros(f', 1, 6)
```

Because we have the $4$ distinct zeros, we must have the peaks and valleys appear in an interleaving manner, so a search over $[1,6]$ finds all three critical points and without checking, they must correspond to relative extrema.


Next we identify the *inflection points* which are among the zeros of the second derivative (when defined):


```{julia}
ips = find_zeros(f'', 1, 6)
```

If there is no sign change for either $f'$ or $f''$ over $[a,b]$ then the sketch of $f$ on this interval must be one of:


  * increasing and concave up (if $f' > 0$ and $f'' > 0$)
  * increasing and concave down (if $f' > 0$ and $f'' < 0$)
  * decreasing and concave up (if $f' < 0$ and $f'' > 0$)
  * decreasing and concave down (if $f' < 0$ and $f'' < 0$)


This aids in sketching the graph between the critical points and inflection points.


We finally check that if we were to just use $[0,7]$ as a domain to plot over that the function doesn't get too large to mask the oscillations. This could happen if the $y$ values at the end points are too much larger than the $y$ values at the peaks and valleys, as only so many pixels can be used within a graph. For this we have:


```{julia}
f.([0, cps..., 7])
```

The values at $0$ and at $7$ are a bit large, as compared to the relative extrema, and since we know the graph is eventually $U$-shaped, this offers no insight. So we narrow the range a bit for the graph of @fig-plot-poly-x-4-minus-13-x-3-plus-56-x-2-minus92-x-plus-48.

::: {#fig-plot-poly-x-4-minus-13-x-3-plus-56-x-2-minus92-x-plus-48}
```{julia}
plot(f, 0.5, 6.5)
```

Plot of $f(x) = x^4 - 13x^3 + 56x^2 -92x + 48$ over $[0.5, 6.5]$
:::

---


This sort of analysis can be automated. The plot "recipe" for polynomials from the `Polynomials` package does similar considerations to choose a viewing window:

::: {#fig-plot-polynomial-f-of-x-not-f}
```{julia}
𝐱 = variable(Polynomial)
plot(f(𝐱))   # f(𝐱) of Polynomial type
```

Plot of polynomial `f(x)` produced by a plot recipe
:::

##### Example


Graph  the rational function


$$
f(x) = \frac{(x-1)\cdot(x-3)^2}{x \cdot (x-2)}.
$$

Not much to do here if you are satisfied with a graph that only gives insight into the asymptotes of this rational function (@fig-plot-x-minus-1-times-x-minus-3-squared-over-x-over-x-minus-2-bad-one).

::: {#fig-plot-x-minus-1-times-x-minus-3-squared-over-x-over-x-minus-2-bad-one}
```{julia}
f(x) = ( (x-1)*(x-3)^2 ) / (x * (x-2) )
plot(f, -50, 50)
```

Plot of rational function over $[-50, 50] shows some features, but not all that are commonly of interest
:::


We can see the slant asymptote and hints of vertical asymptotes, but, we'd like to see more of the basic features of the graph.


Previously, we have discussed rational functions and their asymptotes. This function has numerator of degree $3$ and denominator of degree $2$, so will have a slant asymptote. As well, the zeros of the denominator, $0$ and $2$, will lead to vertical asymptotes.


To identify how wide a viewing window should be, for the rational function the asymptotic behaviour is determined after the concavity is done changing and we are past all relative extrema, so we should take an interval that includes all potential inflection points and critical points:


```{julia}
cps = find_zeros(f', -10, 10)
poss_ips = find_zeros(f'', (-10, 10))
extrema(union(cps, poss_ips))
```

So a range over $[-5,5]$ should display the key features including the slant asymptote.


Previously we used the `rangeclamp` function defined in `CalculusWithJulia` to avoid the distortion that vertical asymptotes can have:

::: {#fig-plot-x-minus-1-times-x-minus-3-squared-over-x-over-x-minus-2-after-settling-on-viewing-window}
```{julia}
#| echo: false
plot(rangeclamp(f), -5, 5; legend=false)
```

Plot of rational function over identified domain $[-5, 5]$ and passing through `rangeclamp` to avoid spurious lines
:::

With the graph in @fig-plot-x-minus-1-times-x-minus-3-squared-over-x-over-x-minus-2-after-settling-on-viewing-window, we can now clearly see in the graph the two zeros at $x=1$ and $x=3$, the vertical asymptotes at $x=0$ and $x=2$, and the slant asymptote.


---


Again, this sort of analysis can be systematized. The rational function type in the `Polynomials` package takes a stab at that, but isn't quite so good at capturing the slant asymptote:

::: {#fig-plot-of-rational-function-using-plot-recipe}
```{julia}
x = variable(RationalFunction)
plot(f(x))  # f(x) of RationalFunction type
```

Plot of rational function using a plot recipe
:::

##### Example


Consider the function $V(t) = 170 \sin(2\pi\cdot 60 \cdot t)$, a model for the alternating current waveform for an outlet in the United States. Create a graph.


Blindly trying to graph this (@fig-plot-of-170-sin-2-pi-60-t-oscillates), we will see immediate issues.

::: {#fig-plot-of-170-sin-2-pi-60-t-oscillates}
```{julia}
V(t) = 170 * sin(2*pi*60*t)
plot(V, -2pi, 2pi)
```

Plot of current function, $V(t)$ showing it is highly oscillatory
:::

Ahh, this periodic function is *too* rapidly oscillating to be plotted without care. We recognize this as being of the form $V(t) = a\cdot\sin(c\cdot t)$, so where the sine function has a period of $2\pi$, this will have a period of $2\pi/c$, or $1/60$. So instead of using $(-2\pi, 2\pi)$ as the interval to plot over, we need something much smaller:

::: {#fig-plot-of-170-sin-2-pi-60-t-two-periods}
```{julia}
plot(V, -1/60, 1/60)
```

Plot of two periods of highly oscillatory function, $V(t)$
:::

##### Example


Plot the function $f(x) = \ln(x/100)/x$.


We guess that this function has a *vertical* asymptote at $x=0+$  and a horizontal asymptote as $x \rightarrow \infty$, we verify through:


```{julia}
@syms x
ex = log(x/100)/x
limit(ex, x=>0, dir="+"), limit(ex, x=>oo)
```

The $\ln(x/100)$ part of $f$ goes $-\infty$ as $x \rightarrow 0+$; yet $f(x)$ is eventually positive as $x \rightarrow \infty$. So a graph should


* not show too much of the vertical asymptote

* capture the point where $f(x)$ must cross $0$

* capture the point where $f(x)$ has a relative maximum

* show enough past this maximum to indicate to the reader the eventual horizontal asymptote.


For that, we need to get the $x$ intercepts and the critical points. The $x/100$ means this graph has some scaling to it, so we first look between $0$ and $200$:


```{julia}
find_zeros(ex, 0, 200)  # domain is (0, oo)
```

Trying the same for the critical points comes up empty. We know there is one, but it is past $200$. Scanning wider, we see:


```{julia}
find_zeros(diff(ex,x), 0, 500)
```

So maybe graphing over $[50, 300]$ will be a good start. A graph is shown in @fig-plot-log-x-over-100-over-x-take-1.

::: {#fig-plot-log-x-over-100-over-x-take-1}
```{julia}
#| echo: false
plot(ex, 50, 300)
```

Plot of $f(x) = \ln(x/100)/x$ over $[50, 200]$
:::

But it isn't! The function takes its time getting back towards $0$. We know that there must be a change of concavity as $x \rightarrow \infty$, as there is a horizontal asymptote. We look for the anticipated inflection point to ensure our graph includes that:


```{julia}
find_zeros(diff(ex, x, x), 1, 5000)
```

So a better plot is found by going well beyond that inflection point. In @fig-plot-log-x-over-100-over-x-take-1 we plot over $[75, 1500]$.

::: {#fig-plot-log-x-over-100-over-x-take-2}
```{julia}
#| echo: false
plot(ex, 75, 1500)
```

Plot of $f(x) = \ln(x/100)/x$ over $[75, 1500]$
:::

## Questions


###### Question


Consider the graph of a rational function  in @fig-rational-function-x-minus-2-times-x-2point5-times-x-minus-3-over-x-minus-1-x-plus-1.

::: {#fig-rational-function-x-minus-2-times-x-2point5-times-x-minus-3-over-x-minus-1-x-plus-1}
```{julia}
#| hold: true
#| echo: false
f(x) = (x-2)* (x-2.5)*(x-3) / ((x-1)*(x+1))
p = plot(f, -20, -1-.3, legend=false, xlim=(-15, 15), color=:blue)
plot!(p, f, -1 + .2, 1 - .02, color=:blue)
plot!(p, f, 1 + .05, 20, color=:blue)
```

Graph of rational function
:::

What kind of *asymptotes* does it appear to have?


```{julia}
#| hold: true
#| echo: false
choices = [
L"Just a horizontal asymptote, $y=0$",
L"Just vertical asymptotes at $x=-1$ and $x=1$",
L"Vertical asymptotes at $x=-1$ and $x=1$ and a horizontal asymptote $y=1$",
L"Vertical asymptotes at $x=-1$ and $x=1$ and a slant asymptote"
]
answer = 4
radioq(choices, answer)
```

###### Question


Consider the function $p(x) = x + 2x^2 + 3x^3 + 4x^4 + 5x^5 +6x^6$. Which interval shows more than a $U$-shaped graph that dominates for large $x$ due to the leading term being $6x^6$?


(Find an interval that contains the zeros, critical points, and inflection points.)


```{julia}
#| hold: true
#| echo: false
choices = ["``(-5,5)``, the default bounds of a calculator",
"``(-3.5, 3.5)``, the bounds given by Cauchy for the real roots of ``p``",
"``(-1, 1)``, as many special polynomials have their roots in this interval",
"``(-1.1, .25)``, as this contains all the roots, the critical points, and inflection points and just a bit more"
]
radioq(choices, 4, keep_order=true)
```

###### Question


Let $f(x) = x^3/(9-x^2)$.


What points are *not* in the domain of $f$?


```{julia}
#| echo: false
qchoices = [
    "The values of `find_zeros(f, -10, 10)`: `[-3, 0, 3]`",
    "The values of `find_zeros(f', -10, 10)`: `[-5.19615, 0, 5.19615]`",
    "The values of `find_zeros(f'', -10, 10)`: `[-3, 0, 3]`",
    "The zeros of the numerator: `[0]`",
    "The zeros of the denominator: `[-3, 3]`",
    "The value of `f(0)`: `0`",
    "None of these choices"
]
radioq(qchoices, 5, keep_order=true)
```

The $x$-intercepts are:


```{julia}
#| hold: true
#| echo: false
radioq(qchoices, 4, keep_order=true)
```

The $y$-intercept is:


```{julia}
#| hold: true
#| echo: false
radioq(qchoices, 6, keep_order=true)
```

There are *vertical asymptotes* at $x=\dots$?


```{julia}
#| hold: true
#| echo: false
radioq(qchoices, 5)
```

The *slant* asymptote has slope?


```{julia}
#| hold: true
#| echo: false
numericq(-1)
```

The function has critical points at


```{julia}
#| hold: true
#| echo: false
radioq(qchoices, 2, keep_order=true)
```

The function has relative extrema at


```{julia}
#| hold: true
#| echo: false
radioq(qchoices, 7, keep_order=true)
```

The function has inflection points at


```{julia}
#| hold: true
#| echo: false
radioq(qchoices, 7, keep_order=true)
```

###### Question


A function $f$ has

* zeros of $\{-0.7548\dots, 2.0\}$,

* critical points at $\{-0.17539\dots, 1.0, 1.42539\dots\}$,

* inflection points at $\{0.2712\dots,1.2287\}$.


Does  @fig-function-x-4-minus-3-x-3-plus-2-x-2-plus-x-minus-2 show a possible graph of $f$?

::: {#fig-function-x-4-minus-3-x-3-plus-2-x-2-plus-x-minus-2}
```{julia}
#| hold: true
#| echo: false
f(x) = x^4 - 3x^3 + 2x^2 + x - 2
plot(f, -1, 2.5, legend=false)
```

Plot of $f(x)$ over $[-1, 1.25]$
:::

```{julia}
#| hold: true
#| echo: false
yesnoq("yes")
```

###### Question


Two models for population growth are *exponential* growth: $P(t) = P_0 a^t$ and [logistic growth](https://en.wikipedia.org/wiki/Logistic_function#In_ecology:_modeling_population_growth): $P(t) = K P_0 a^t / (K + P_0(a^t - 1))$. The exponential growth model has growth rate proportional to the current population. The logistic model has growth rate depending on the current population *and* the available resources (which can limit growth).


Letting $K=50$, $P_0=5$, and $a= e^{1/4}$, the plots in @fig-plot-exponential-and-logistic-growth-over-0-5 $ shows somewhat similar behaviour  over $[0,5].

::: {#fig-plot-exponential-and-logistic-growth-over-0-5}
```{julia}
K, P0, a = 50, 5, exp(1/4)
exponential_growth(t) = P0 * a^t
logistic_growth(t) = K * P0 * a^t / (K + P0*(a^t-1))

plot(exponential_growth, 0, 5; label="exponential growth")
plot!(logistic_growth; label="logistic growth")
```

Plot of exponential growth model and logisitc growth model over $[0,5]$
:::

Does a plot over $[0,50]$ show qualitatively similar behaviour?


```{julia}
#| hold: true
#| echo: false
yesnoq("no")
```

Exponential growth has $P''(t) = P_0 a^t \log(a)^2 > 0$, so has no inflection point. By plotting over a sufficiently wide interval, can you answer: does the logistic growth model have an inflection point?


```{julia}
#| hold: true
#| echo: false
yesnoq(true)
```

If yes, find it numerically:


```{julia}
#| hold: true
#| echo: false
val = find_zero(D(logistic_growth,2), (0, 20))
numericq(val)
```

The available resources are quantified by $K$. As $K \rightarrow \infty$ what is the limit of the logistic growth model:


```{julia}
#| hold: true
#| echo: false
choices = [
"The exponential growth model",
"The limit does not exist",
"The limit is ``P_0``"]
answer = 1
radioq(choices, answer)
```

###### Question


The plotting algorithm for plotting functions  starts with a small initial set of points over the specified interval ($31$) and then refines those sub-intervals where the second derivative is determined to be large.


Why are sub-intervals where the second derivative is large different than those where the second derivative is small?


```{julia}
#| hold: true
#| echo: false
choices = [
"The function will increase (or decrease) rapidly when the second derivative is large, so there needs to be more points to capture the shape",
"The function will have more curvature when the second derivative is large, so there  needs to be more points to capture the shape",
"The function will be much larger (in absolute value) when the second derivative is large, so there needs to be more points to capture the shape",
]
answer = 2
radioq(choices, answer)
```

###### Question


Is there a nice algorithm to identify what domain a function should be plotted over to produce an informative graph? [Wilkinson](https://www.cs.uic.edu/~wilkinson/Publications/plotfunc.pdf) has some suggestions. (Wilkinson is well known to the `R` community as the specifier of the grammar of graphics.) It is mentioned that "finding an informative domain for a given function depends on at least three features: periodicity, asymptotics, and monotonicity."


Why would periodicity matter?


```{julia}
#| hold: true
#| echo: false
choices = [
"An informative graph only needs to show one or two periods, as others can be inferred.",
"An informative graph need only show a part of the period, as the rest can be inferred.",
L"An informative graph needs to show several periods, as that will allow proper computation for the $y$ axis range."]
answer = 1
radioq(choices, answer)
```

Why should asymptotics matter?


```{julia}
#| hold: true
#| echo: false
choices = [
L"A vertical asymptote can distort the $y$ range, so it is important to avoid too-large values",
L"A horizontal asymptote must be plotted from $-\infty$ to $\infty$",
"A slant asymptote must be plotted over a very wide domain so that it can be identified."
]
answer = 1
radioq(choices, answer)
```

Monotonicity means increasing or decreasing. This is important for what reason?


```{julia}
#| hold: true
#| echo: false
choices = [
"For monotonic regions, a large slope or very concave function might require more care to plot",
"For monotonic regions, a function is basically a straight line",
"For monotonic regions, the function will have a vertical asymptote, so the region should not be plotted"
]
answer = 1
radioq(choices, answer)
```
