Fixed-Point Iteration Example — Cobweb Diagram

This example finds the fixed point of \(g(x) = \cos(x)\), known as the Dottie number (\(x^* \approx 0.7391\)), by solving:

\[h(x) = \cos(x) - x = 0, \quad x \in [0, 1]\]

via nonlinearequations.zero_univariate() (IMSL ZUNI).

After the root is located, the classic cobweb diagram of the fixed-point iteration \(x_{n+1} = \cos(x_n)\) is reconstructed from \(x_0 = 0\) to illustrate the spiral convergence of the iteration.

Example Code

"""Fixed-point iteration example using IMSL ZUNI (zero_univariate).

Finds the fixed point of g(x) = cos(x), i.e. the solution of g(x) - x = 0,
known as the Dottie number (x* ≈ 0.7390851332).

The fixed point is located by bracketing the equation h(x) = cos(x) - x = 0
with :func:`nonlinearequations.zero_univariate` (IMSL ZUNI).  The classic
cobweb diagram is then drawn to show how the fixed-point iteration
x_{n+1} = cos(x_n) converges to x*.

Outputs:
- Table printed to stdout
- SVG cobweb diagram of fixed-point convergence
- Saved to test_output/example_imsl_fixed_point.svg
"""

from __future__ import annotations

import math
from pathlib import Path
from typing import Dict, List

import matplotlib.pyplot as plt
import numpy as np

from nonlinearequations import zero_univariate


def run_demo_imsl_fixed_point() -> Dict[str, object]:
    """Run fixed-point iteration example via IMSL ZUNI (zero_univariate).

    Solves h(x) = cos(x) - x = 0 on [0, 1] with Brent/ZUNI, then
    reconstructs the classic cobweb diagram of x_{n+1} = cos(x_n).

    Args:
        None

    Returns:
        Dict[str, object]: Result dict with keys ``fixed_point`` (float),
            ``n_iter_cobweb`` (int), ``fval`` (float), ``n_fev`` (int), and
            ``plot_path`` (str).
    """

    def h(x: float) -> float:
        """Fixed-point equation h(x) = cos(x) - x.

        Args:
            x (float): Input value.

        Returns:
            float: cos(x) - x.
        """
        return math.cos(x) - x

    result = zero_univariate(h, 0.0, 1.0)
    x_star = float(result.x[0])

    # Build cobweb iteration history starting from x0 = 0.0
    x_current = 0.0
    iterates: List[float] = [x_current]
    max_cobweb_steps = 20
    for _ in range(max_cobweb_steps):
        x_next = math.cos(x_current)
        iterates.append(x_next)
        if abs(x_next - x_star) < 1e-6:
            break
        x_current = x_next
    n_iter_cobweb = len(iterates) - 1

    print("\nIMSL ZUNI Fixed-Point Example: g(x) = cos(x)")
    print("  Solving h(x) = cos(x) - x = 0  on [0, 1]")
    print("-" * 55)
    print(f"{'Parameter':<30} {'Value':>20}")
    print("-" * 55)
    print(f"{'Fixed point x*':<30} {x_star:>20.10f}")
    print(f"{'cos(x*) - x*':<30} {result.fval:>20.2e}")
    print(f"{'ZUNI function evals':<30} {result.n_fev:>20}")
    print(f"{'Converged (ZUNI)':<30} {str(result.success):>20}")
    print(f"{'Cobweb steps to 1e-6':<30} {n_iter_cobweb:>20}")
    print("-" * 55)

    # ------------------------------------------------------------------
    # Plot: cobweb diagram
    # ------------------------------------------------------------------
    output_dir = Path("test_output")
    output_dir.mkdir(parents=True, exist_ok=True)
    plot_path = output_dir / "example_imsl_fixed_point.svg"

    x_plot = np.linspace(0.0, 1.1, 400)
    g_plot = np.cos(x_plot)

    fig, ax = plt.subplots(figsize=(7, 6))

    # g(x) = cos(x) and the identity y = x
    ax.plot(x_plot, g_plot, color="#0e7490", linewidth=2.0, label=r"$g(x) = \cos(x)$")
    ax.plot(x_plot, x_plot, color="#888888", linewidth=1.4, linestyle="--", label=r"$y = x$")

    # Cobweb path
    cobweb_x: List[float] = []
    cobweb_y: List[float] = []
    x_n = iterates[0]
    cobweb_x.append(x_n)
    cobweb_y.append(0.0)
    for x_n1 in iterates[1:]:
        # Vertical: (x_n, x_n) → (x_n, g(x_n)) = (x_n, x_{n+1})
        cobweb_x.append(x_n)
        cobweb_y.append(x_n1)
        # Horizontal: (x_n, x_{n+1}) → (x_{n+1}, x_{n+1})
        cobweb_x.append(x_n1)
        cobweb_y.append(x_n1)
        x_n = x_n1

    ax.plot(cobweb_x, cobweb_y, color="#0891b2", linewidth=1.0, alpha=0.75, label="Cobweb path")

    ax.scatter([x_star], [x_star], color="#d62728", s=100, zorder=5,
               label=f"Fixed point x* ≈ {x_star:.6f}")
    ax.scatter([iterates[0]], [0.0], color="#2ca02c", s=60, zorder=5, label=f"Start x₀ = {iterates[0]:.1f}")

    ax.set_xlim(0.0, 1.1)
    ax.set_ylim(0.0, 1.1)
    ax.set_xlabel("x")
    ax.set_ylabel("g(x)  /  x")
    ax.set_title("Fixed-Point Iteration: g(x) = cos(x)  (cobweb diagram)")
    ax.legend(fontsize=9)
    ax.grid(True, alpha=0.25)
    fig.tight_layout()
    fig.savefig(plot_path, format="svg")
    plt.close(fig)

    return {
        "fixed_point": x_star,
        "n_iter_cobweb": n_iter_cobweb,
        "fval": result.fval,
        "n_fev": result.n_fev,
        "plot_path": str(plot_path),
    }


if __name__ == "__main__":
    run_demo_imsl_fixed_point()

Plot Output

Cobweb diagram of the fixed-point iteration x_{n+1} = cos(x_n)

Cobweb path of \(x_{n+1} = \cos(x_n)\) starting from \(x_0 = 0\) (green dot). The iteration spirals inward to the fixed point \(x^*\) (red dot) where \(y = g(x)\) meets the diagonal \(y = x\).

Console Output

IMSL ZUNI Fixed-Point Example: g(x) = cos(x)
  Solving h(x) = cos(x) - x = 0  on [0, 1]
-------------------------------------------------------
Parameter                                     Value
-------------------------------------------------------
Fixed point x*                         0.7390851346
cos(x*) - x*                               2.26e-09
ZUNI function evals                               9
Converged (ZUNI)                               True
Cobweb steps to 1e-6                             20
-------------------------------------------------------