14  Visual Demos

Somewhat experimental, and better on a larger screen.

14.1 Cauchy’s Mean Value Theorem

#| '!! shinylive warning !!': |
#|   shinylive does not work in self-contained HTML documents.
#|   Please set `embed-resources: false` in your metadata.
#| standalone: true

import matplotlib.pyplot as plt
import numpy as np
from shiny.express import ui, input, render

sin = np.sin

with ui.tags.head():
    ui.tags.link(
        rel="stylesheet",
        href="https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/katex.min.css"
    ),
    ui.tags.script(src="https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/katex.min.js"),
    ui.tags.script(src="https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/contrib/auto-render.min.js"),
    ui.tags.script("""
        document.addEventListener('DOMContentLoaded', function() {
            renderMathInElement(document.body, {
                    delimiters: [
                    {left: "$$", right: "$$", display: true},
                    {left: "\\[", right: "\\]", display: true},
                    {left: "$", right: "$", display: false},
                    {left: "\\(", right: "\\)", display: false}
                ]
            });
        });
    """)

ui.tags.style(
    """
    body { background: #C8E0FF !important; }
    .irs-bar { background: #0054A9 !important; }
    .irs-handle { background: #0054A9 !important; }
    .irs-single { background: #0054A9 !important; color: #FFFFFF !important; }
    .irs-min { background:#99CBFF !important; color: #000000 !important; }
    .irs-max { background:#99CBFF !important; color: #000000 !important; }
    .control-label { display: block; text-align: center; }
    .shiny-input-container { margin:auto; }
    """
)

a = -0.5
b = 10

npts = 400

ui.input_slider(id="t",label="$t$ slider",min=a,max=b,value=2,step=0.25)

@render.plot(alt="Graph of the tangent to the parametric curve at $t$.")
def ball():
    t = input.t()

    def f(x):
        return (x+2)/(x**2+1)
    def g(x):
        return (x**2+20*x-2)/(x**2+30)

    def df(x):
        return -(-1 + 4*x + x**2)/(1 + x**2)**2
    def dg(x):
        return (600 + 64*x - 20*x**2)/(30 + x**2)**2

    U = [a + (b-a)*i/npts for i in range(0,npts+1)]
    X = [g(u) for u in U]
    Y = [f(u) for u in U]

    plt.figure(facecolor='#C8E0FF')

    plt.plot(X,Y,color="#0054A9",label="parametric curve $(g(t),f(t))$")
    plt.plot([g(a),g(b)],[f(a),f(b)],color="#00A954",label="secant line")
    plt.plot(g(a),f(a),color="#0054A9",markersize=5,marker="o")
    plt.plot(g(b),f(b),color="#0054A9",markersize=5,marker="o")

    ax = plt.gca()
    ax.set_aspect('equal')
    ax.set_facecolor('#C8E0FF')

    ax.axline((g(t),f(t)),slope=df(t)/dg(t),color="#FF9933",label="tangent at $(g(t),f(t))$")
    plt.plot(g(t),f(t),color="#FF9933",markersize=5,marker="o")
    ax.legend(facecolor='#C8E0FF',frameon=False)

14.2 \(L_p\) Norms

#| '!! shinylive warning !!': |
#|   shinylive does not work in self-contained HTML documents.
#|   Please set `embed-resources: false` in your metadata.
#| standalone: true

import matplotlib.pyplot as plt
import numpy as np
from shiny.express import ui, input, render

with ui.tags.head():
    ui.tags.link(
        rel="stylesheet",
        href="https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/katex.min.css"
    ),
    ui.tags.script(src="https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/katex.min.js"),
    ui.tags.script(src="https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/contrib/auto-render.min.js"),
    ui.tags.script("""
        document.addEventListener('DOMContentLoaded', function() {
            renderMathInElement(document.body, {
                    delimiters: [
                    {left: "$$", right: "$$", display: true},
                    {left: "\\[", right: "\\]", display: true},
                    {left: "$", right: "$", display: false},
                    {left: "\\(", right: "\\)", display: false}
                ]
            });
        });
    """)

ui.tags.style(
    """
    body { background: #C8E0FF !important; }
    .irs-bar { background: #0054A9 !important; }
    .irs-handle { background: #0054A9 !important; }
    .irs-single { background: #0054A9 !important; color: #FFFFFF !important; }
    .irs-min { background:#99CBFF !important; color: #000000 !important; }
    .irs-max { background:#99CBFF !important; color: #000000 !important; }
    .control-label { display: block; text-align: center; }
    """
)

ui.input_slider(id="p",label="Ball and sphere for the $L_p$ norm.",min=1,max=8,value=2,step=0.5).add_style("margin:auto;") # center the slider

@render.plot(alt="Unit ball and sphere for the $L_p$ norm.")
def ball():
    p = input.p()
    npts = 200

    X = [-1 + 2*x/npts for x in range(0,npts+1)]
    Ytop = [(1-abs(x)**p)**(1/p) for x in X]
    Ybot = [-(1-abs(x)**p)**(1/p) for x in X]

    plt.figure(facecolor='#C8E0FF')
    plt.plot(X,Ytop,color="#0054A9")
    plt.plot(X,Ybot,color="#0054A9")
    plt.fill_between(X,Ytop,color="#99CBFF")
    plt.fill_between(X,Ybot,color="#99CBFF")
    plt.xlim(-1.4,1.4)
    plt.ylim(-1.4,1.4)
    ax = plt.gca()
    ax.set_aspect('equal')
    ax.set_facecolor('#C8E0FF')

14.3 Newton’s Method

Applied to \(f(x) = x^3 - 4x - 2\) starting from \(x_0 = 5\). It will converge quite quickly to a root.

#| '!! shinylive warning !!': |
#|   shinylive does not work in self-contained HTML documents.
#|   Please set `embed-resources: false` in your metadata.
#| standalone: true
import matplotlib.pyplot as plt
import numpy as np

from shiny import reactive, render
from shiny.express import input, ui

ui.tags.style(
    """
    body { background: #C8E0FF !important; }
    .shiny-input-container { margin:auto; }
    .action-button { background-color: #99CBFF; color: black; border: none;}
    .action-button:hover { background-color: #0054A9; color: white; border-color: none;}
    """
)

with ui.layout_columns(col_widths=(-2,3,-2,3,-2)):
    ui.input_action_button("step", "Step")
    ui.input_action_button("reset", "Reset")

rxcur = reactive.value(5)

@reactive.effect
@reactive.event(input.reset, ignore_none=False)
def _():
    rxcur.set(5)

@render.plot(alt="Newton's method step from $x_n$ to $x_{n+1}$")
@reactive.event(input.step,input.reset, ignore_none=False)
def nmplot():
    def f(x):
        return x**3 - 4*x - 2
    def df(x):
        return 3*x**2 - 4

    xcur = rxcur.get()

    xnew = xcur - f(xcur)/df(xcur)

    # plotting
    fig, ax = plt.subplots()
    xs = list(np.linspace(xnew-0.5,xcur+0.5,100))

    # the function
    ax.plot(xs,[f(x) for x in xs],color="#0054A9",label="$f(x)=x^3 - 4x - 2$")

    # current point and tangent
    ax.plot(xcur,f(xcur),color="#FF9933",markersize=5,marker="o")
    ax.plot(xs, [f(xcur) + df(xcur)*(x-xcur) for x in xs],color="#FF9933",label="tangent at $(x_n,f(x_n))$")

    # informational text
    ax.scatter([],[],marker="",color="black", label=f"$x_n = {xcur:.4f}$")
    ax.scatter([],[],marker="",color="black", label=f"$x_{{n+1}} = {xnew:.4f}$")

    # formatting
    fig.set_facecolor('#C8E0FF')
    ax.set_facecolor('#C8E0FF')
    ax.spines.right.set_color('none')
    ax.spines.bottom.set_position('zero')
    ax.spines.top.set_color('none')
    ax.xaxis.set_ticks_position('bottom')
    ax.yaxis.set_ticks_position('left')
    ax.set_xticks([xcur,xnew],["$x_n$","$x_{n+1}$"])
    ax.legend(facecolor='#C8E0FF',frameon=False)

    rxcur.set(xnew)
    return fig

On the other hand, we could apply it to \(\arctan(x)\). This will not converge for all initial values – in fact, it will diverge extremely quickly (can you see why?). In this example, it starts from \(x_0 = 1.5\) and reaches what the computer considers \(\infty\) in just a couple steps.

#| '!! shinylive warning !!': |
#|   shinylive does not work in self-contained HTML documents.
#|   Please set `embed-resources: false` in your metadata.
#| standalone: true
import matplotlib.pyplot as plt
import numpy as np

from shiny import reactive, render
from shiny.express import input, ui

ui.tags.style(
    """
    body { background: #C8E0FF !important; }
    .shiny-input-container { margin:auto; }
    .action-button { background-color: #99CBFF; color: black; border: none;}
    .action-button:hover { background-color: #0054A9; color: white; border-color: none;}
    """
)

with ui.layout_columns(col_widths=(-2,3,-2,3,-2)):
    ui.input_action_button("step", "Step")
    ui.input_action_button("reset", "Reset")

rxcur = reactive.value(1.5)

@reactive.effect
@reactive.event(input.reset, ignore_none=False)
def _():
    rxcur.set(1.5)

@render.plot(alt="Newton's method step from $x_n$ to $x_{n+1}$")
@reactive.event(input.step,input.reset, ignore_none=False)
def nmplot():
    def f(x):
        return np.arctan(x)
    def df(x):
        return 1/(1+x**2)

    xcur = rxcur.get()

    xnew = xcur - f(xcur)/df(xcur)

    # plotting
    fig, ax = plt.subplots()
    bd = 24
    xs = list(np.linspace(-bd-1,bd+1,200))

    # the function
    ax.plot(xs,[f(x) for x in xs],color="#0054A9",label="$f(x)=\\arctan(x)$")

    # current point and tangent
    ax.plot(xcur,f(xcur),color="#FF9933",markersize=5,marker="o")
    ax.plot(xs, [f(xcur) + df(xcur)*(x-xcur) for x in xs],color="#FF9933",label="tangent at $(x_n,f(x_n))$")

    # informational text
    ax.scatter([],[],marker="",color="black", label=f"$x_n = {xcur:.4f}$")
    ax.scatter([],[],marker="",color="black", label=f"$x_{{n+1}} = {xnew:.4f}$")

    # formatting
    fig.set_facecolor('#C8E0FF')
    ax.set_facecolor('#C8E0FF')
    ax.set_ylim(-2, 2)
    ax.set_xlim(-bd, bd)
    ax.spines.right.set_color('none')
    ax.spines.top.set_color('none')
    ax.spines.bottom.set_position('zero')
    ax.xaxis.set_ticks_position('bottom')
    ax.yaxis.set_ticks_position('left')
    tx = []
    tl = []
    if abs(xcur) < 23:
        tx += [xcur]
        tl += ["$x_n$"]
    if abs(xnew) < 23:
        tx += [xnew]
        tl += ["$x_{n+1}$"]
    ax.set_xticks(tx,tl)
    ax.legend(facecolor='#C8E0FF',frameon=False)

    rxcur.set(xnew)
    return fig

However, we know that our convergence theorem should apply somewhere near an actual root. From \(x_0 = 1.25\), it rapidly converges to the root at \(x=0\).

#| '!! shinylive warning !!': |
#|   shinylive does not work in self-contained HTML documents.
#|   Please set `embed-resources: false` in your metadata.
#| standalone: true
import matplotlib.pyplot as plt
import numpy as np

from shiny import reactive, render
from shiny.express import input, ui

ui.tags.style(
    """
    body { background: #C8E0FF !important; }
    .shiny-input-container { margin:auto; }
    .action-button { background-color: #99CBFF; color: black; border: none;}
    .action-button:hover { background-color: #0054A9; color: white; border-color: none;}
    """
)

with ui.layout_columns(col_widths=(-2,3,-2,3,-2)):
    ui.input_action_button("step", "Step")
    ui.input_action_button("reset", "Reset")

rxcur = reactive.value(1.25)

@reactive.effect
@reactive.event(input.reset, ignore_none=False)
def _():
    rxcur.set(1.25)

@render.plot(alt="Newton's method step from $x_n$ to $x_{n+1}$")
@reactive.event(input.step,input.reset, ignore_none=False)
def nmplot():
    def f(x):
        return np.arctan(x)
    def df(x):
        return 1/(1+x**2)

    xcur = rxcur.get()

    xnew = xcur - f(xcur)/df(xcur)

    # plotting
    fig, ax = plt.subplots()
    bd = 1.5
    xs = list(np.linspace(-bd-1,bd+1,200))

    # the function
    ax.plot(xs,[f(x) for x in xs],color="#0054A9",label="$f(x)=\\arctan(x)$")

    # current point and tangent
    ax.plot(xcur,f(xcur),color="#FF9933",markersize=5,marker="o")
    ax.plot(xs, [f(xcur) + df(xcur)*(x-xcur) for x in xs],color="#FF9933",label="tangent at $(x_n,f(x_n))$")

    # informational text
    ax.scatter([],[],marker="",color="black", label=f"$x_n = {xcur:.4f}$")
    ax.scatter([],[],marker="",color="black", label=f"$x_{{n+1}} = {xnew:.4f}$")

    # formatting
    fig.set_facecolor('#C8E0FF')
    ax.set_facecolor('#C8E0FF')
    ax.set_ylim(-1.5, 1.5)
    ax.set_xlim(-bd, bd)
    ax.spines.right.set_color('none')
    ax.spines.top.set_color('none')
    ax.spines.bottom.set_position('zero')
    ax.xaxis.set_ticks_position('bottom')
    ax.yaxis.set_ticks_position('left')
    tx = []
    tl = []
    if abs(xcur) < 23:
        tx += [xcur]
        tl += ["$x_n$"]
    if abs(xnew) < 23:
        tx += [xnew]
        tl += ["$x_{n+1}$"]
    ax.set_xticks(tx,tl)
    ax.legend(facecolor='#C8E0FF',frameon=False)

    rxcur.set(xnew)
    return fig