Skip to content
8 changes: 8 additions & 0 deletions _subfiles/vector-calc/_exm-newton-quadratic.qmd
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,12 @@ $$

the minimizer.

@fig-newton-quadratic shows the step on the [level curves](vector-calculus.qmd#def-level-set) of $f$.

{{< include mds/_subfiles/vector-calc/_fig-newton-quadratic.qmd >}}

@fig-newton-quadratic-3d shows the same [function](functions.qmd#def-function) as a surface.

{{< include mds/_subfiles/vector-calc/_fig-newton-quadratic-3d.qmd >}}

:::
119 changes: 119 additions & 0 deletions _subfiles/vector-calc/_fig-newton-quadratic-3d.qmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
::: {#fig-newton-quadratic-3d}

::: {.panel-tabset group="language"}

### R

```{r}
#| label: newton-quadratic-3d-r
#| echo: !expr knitr::is_html_output()
#| eval: !expr knitr::is_html_output()
#| fig-alt: >-
#| Surface plot of f(x) = (x1^2 + 3 x2^2) / 2 for x1 from -4 to 4 and x2
#| from -2 to 2. The surface is a bowl with its lowest point at the origin,
#| rising more steeply along x2 than along x1. A vermilion point marks the
#| start (3, 1) on the bowl's side and a black point marks the minimizer
#| at the origin.
nq_x1_sf <- seq(-4, 4, length.out = 61)
nq_x2_sf <- seq(-2, 2, length.out = 61)
nq_grid_sf <- tidyr::expand_grid(x1 = nq_x1_sf, x2 = nq_x2_sf)
nq_value_sf <- (nq_grid_sf$x1^2 + 3 * nq_grid_sf$x2^2) / 2
nq_n2_sf <- length(nq_x2_sf)
nq_z_sf <- matrix(nq_value_sf, nrow = nq_n2_sf)
nq_start_sf <- tibble::tibble(x1 = 3, x2 = 1, z = 6)
nq_min_sf <- tibble::tibble(x1 = 0, x2 = 0, z = 0)
nq_red_sf <- list(color = "#D55E00", size = 5)
nq_black_sf <- list(color = "#000000", size = 5)
nq_stops_sf <- c(0, 1)
nq_cols_sf <- c("#CCE0F0", "#0072B2")
nq_scale_sf <- list(nq_stops_sf, nq_cols_sf)
nq_eye_sf <- list(x = 1.6, y = -1.6, z = 0.9)
nq_camera_sf <- list(eye = nq_eye_sf)
nq_xaxis_sf <- list(title = "x1")
nq_yaxis_sf <- list(title = "x2")
nq_zaxis_sf <- list(title = "f(x)")
nq_scene_sf <- list(
xaxis = nq_xaxis_sf,
yaxis = nq_yaxis_sf,
zaxis = nq_zaxis_sf,
camera = nq_camera_sf
)
nq_legend_sf <- list(orientation = "h")
plotly::plot_ly() |>
plotly::add_surface(
x = nq_x1_sf,
y = nq_x2_sf,
z = nq_z_sf,
colorscale = nq_scale_sf,
showscale = FALSE,
opacity = 0.8
) |>
plotly::add_trace(
data = nq_start_sf,
x = ~x1,
y = ~x2,
z = ~z,
type = "scatter3d",
mode = "markers",
marker = nq_red_sf,
name = "start"
) |>
plotly::add_trace(
data = nq_min_sf,
x = ~x1,
y = ~x2,
z = ~z,
type = "scatter3d",
mode = "markers",
marker = nq_black_sf,
name = "minimizer"
) |>
plotly::layout(scene = nq_scene_sf, legend = nq_legend_sf)
```

```{r}
#| echo: false
#| eval: !expr isFALSE(knitr::is_html_output())
#| results: asis
cat(
"*(This 3D surface is interactive; see the HTML or slide version",
"of this page to view it.)*"
)
```

### Python

```{python}
#| label: newton-quadratic-3d-python
#| fig-alt: >-
#| Surface plot of f(x) = (x1^2 + 3 x2^2) / 2 for x1 from -4 to 4 and x2
#| from -2 to 2. The surface is a bowl with its lowest point at the origin,
#| rising more steeply along x2 than along x1. A vermilion point marks the
#| start (3, 1) on the bowl's side and a black point marks the minimizer
#| at the origin.
#| fig-height: 4.5
#| fig-width: 6
import matplotlib.pyplot as plt
import numpy as np

nq_x1_3d, nq_x2_3d = np.meshgrid(np.linspace(-4, 4, 61),
np.linspace(-2, 2, 61))
nq_z_3d = (nq_x1_3d**2 + 3 * nq_x2_3d**2) / 2
fig = plt.figure(figsize=(6, 4.5))
ax = fig.add_subplot(projection="3d")
ax.plot_surface(nq_x1_3d, nq_x2_3d, nq_z_3d, color="#0072B2", alpha=0.6)
ax.scatter([3], [1], [6], color="#D55E00", label="start")
ax.scatter([0], [0], [0], color="black", label="minimizer")
ax.set_xlabel("x1")
ax.set_ylabel("x2")
ax.set_zlabel("f(x)")
ax.legend(loc="upper right")
plt.show();
```

:::

The [graph](functions.qmd#def-graph) of $f$ in @exm-newton-quadratic is a bowl
whose lowest point is the origin.

:::
92 changes: 92 additions & 0 deletions _subfiles/vector-calc/_fig-newton-quadratic.qmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
::: {#fig-newton-quadratic}

::: {.panel-tabset group="language"}

### R

```{r}
#| label: newton-quadratic-fig-r
#| fig-alt: >-
#| Contour plot of f(x) = (x1^2 + 3 x2^2) / 2 for x1 from -4 to 4 and x2
#| from -2 to 2. The level curves are ellipses centred at the origin,
#| wider along x1 than along x2. A point at (3, 1) lies on one ellipse. A
#| vermilion arrow runs from that point straight to the origin, which is
#| the Newton step. A grey arrow of the same length points along the
#| negative gradient, down and left at 45 degrees, and does not reach the
#| origin.
#| fig-height: 3.5
#| fig-width: 6
nq_x1_nq <- seq(-4, 4, length.out = 101)
nq_x2_nq <- seq(-2, 2, length.out = 101)
nq_grid_nq <- tidyr::expand_grid(x1 = nq_x1_nq, x2 = nq_x2_nq)
nq_grid_nq$z <- (nq_grid_nq$x1^2 + 3 * nq_grid_nq$x2^2) / 2
nq_newton_nq <- tibble::tibble(x = 3, y = 1, xend = 0, yend = 0)
nq_grad_nq <- tibble::tibble(x = 3, y = 1, xend = 0.764, yend = -1.236)
nq_start_nq <- tibble::tibble(x1 = 3, x2 = 1)
nq_head_nq <- grid::unit(0.15, "inches")
nq_arrow_nq <- grid::arrow(length = nq_head_nq)
nq_aes_nq <- ggplot2::aes(x1, x2)
nq_z_aes_nq <- ggplot2::aes(z = z)
nq_seg_aes_nq <- ggplot2::aes(x = x, y = y, xend = xend, yend = yend)
nq_grid_nq |>
ggplot2::ggplot() +
nq_aes_nq +
ggplot2::geom_contour(nq_z_aes_nq, colour = "#0072B2", bins = 10) +
ggplot2::geom_segment(
nq_seg_aes_nq,
data = nq_grad_nq,
colour = "grey50",
arrow = nq_arrow_nq
) +
ggplot2::geom_segment(
nq_seg_aes_nq,
data = nq_newton_nq,
colour = "#D55E00",
arrow = nq_arrow_nq
) +
ggplot2::geom_point(data = nq_start_nq, size = 2) +
ggplot2::coord_fixed() +
ggplot2::labs(x = "x1", y = "x2")
```

### Python

```{python}
#| label: newton-quadratic-fig-python
#| fig-alt: >-
#| Contour plot of f(x) = (x1^2 + 3 x2^2) / 2 for x1 from -4 to 4 and x2
#| from -2 to 2. The level curves are ellipses centred at the origin,
#| wider along x1 than along x2. A point at (3, 1) lies on one ellipse. A
#| vermilion arrow runs from that point straight to the origin, which is
#| the Newton step. A grey arrow of the same length points along the
#| negative gradient, down and left at 45 degrees, and does not reach the
#| origin.
#| fig-height: 3.5
#| fig-width: 6
import matplotlib.pyplot as plt
import numpy as np

nq_xx_py, nq_yy_py = np.meshgrid(np.linspace(-4, 4, 101),
np.linspace(-2, 2, 101))
nq_z_py = (nq_xx_py**2 + 3 * nq_yy_py**2) / 2
fig, ax = plt.subplots(figsize=(6, 3.5), layout="constrained")
ax.contour(nq_xx_py, nq_yy_py, nq_z_py, levels=10, colors="#0072B2")
ax.annotate("", xy=(0.764, -1.236), xytext=(3, 1),
arrowprops={"arrowstyle": "->", "color": "grey"})
ax.annotate("", xy=(0, 0), xytext=(3, 1),
arrowprops={"arrowstyle": "->", "color": "#D55E00"})
ax.plot([3], [1], "ko")
ax.set_aspect("equal")
ax.set_xlabel("x1")
ax.set_ylabel("x2")
plt.show();
```

:::

The [level curves](vector-calculus.qmd#def-level-set) of $f$ in @exm-newton-quadratic are ellipses.
From the start $\tp{(3, 1)}$, the [Newton step](#def-newton-step) (vermilion arrow) goes straight to the [minimizer](algebra.qmd#def-global-minimizer) at the origin.
The negative [gradient](vector-calculus.qmd#def-vector-derivative) $-\tp{(3, 3)}$ (grey arrow, drawn as long as the Newton step)
points across the narrow side of the ellipses and misses the origin.

:::
Loading