{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "56889fcd-f08a-4475-9928-af817748f5c8",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-5e2b58ff19f0997c",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "## Numerical Analysis - Fall semester 2025\n",
    "# Serie 08 - Interpolation"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5d0c899b-58da-49e8-ace0-828c8d5b60ca",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-e56f5ef08590e711",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "First, we will need to import some of the usual packages. You will have to run this cell every time you restart your notebook."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7d640d3e-d688-47ad-95b6-96eed7152684",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-6bbe893666203fcf",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "import scipy as sp"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e28c8be9-80d5-4168-8c0e-2bb5d3925c06",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-21702acd622409d7",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "<hr style=\"clear:both\">\n",
    "\n",
    "### Simple polynomial interpolation"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e24a1318-bbc7-4c35-a333-f49a837ea8e6",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-4cb2537f66cd892a",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "We have the following data\n",
    "\n",
    "\\begin{align*}\n",
    "x_0=0 \\quad  & y_0=12, \\\\\n",
    "x_1=1 \\quad  & y_1=18, \\\\\n",
    "x_2=2 \\quad  & y_2=6.\n",
    "\\end{align*}"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "83ec0ada-c0ee-4eff-b09f-91a9fefa1fe4",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-eae9b3444545bcc0",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "<div class=\"alert alert-success\">\n",
    "\n",
    "**Exercise 1:** Find the second order polynomial interpolating the data by setting up and solving the linear system with the Vandermonde matrix.\n",
    "</div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e872398c-41f4-49f5-b779-6945c9ffeecb",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-9d483aa33a68eb6d",
     "locked": false,
     "schema_version": 3,
     "solution": true,
     "task": false
    }
   },
   "outputs": [],
   "source": [
    "### BEGIN SOLUTION\n",
    "V = np.array([[1, 0, 0],\n",
    "              [1, 1, 1],\n",
    "              [1, 2, 4]])\n",
    "\n",
    "y = np.array([12, 18, 6])\n",
    "a = np.linalg.solve(V, y)\n",
    "print(f\"the interpolating polynomial is p₂(x) = {a[0]} + {a[1]} * x + {a[2]} * x^2\")\n",
    "### END SOLUTION"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "eaee1e7b-ede4-44ca-aa7a-d95bbd5e3a61",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-7e65398ebdd6886a",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "<div class=\"alert alert-success\">\n",
    "\n",
    "**Exercise 2:** Find the second order polynomial interpolating the data by using `np.polyfit`, evaluate the resulting polynomial at $100$ evenly spaced points in the interval $[-1, 3]$ by using `np.polyval`, and use them to visualize the polynomial on the same plot with the data.\n",
    "\n",
    "*Hint:* To plot individual data points, use `plt.scatter`. To find out more about a function's inputs and outputs, use `help(function_name)`.\n",
    "</div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "29a9caf6-f274-48b7-89fb-e93a4c42cdfa",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-9b9bb5f23dec69e1",
     "locked": false,
     "schema_version": 3,
     "solution": true,
     "task": false
    }
   },
   "outputs": [],
   "source": [
    "### BEGIN SOLUTION\n",
    "x = np.array([0, 1, 2])\n",
    "a = np.polyfit(x, y, 2)\n",
    "x_lin = np.linspace(-1, 3, 100)\n",
    "y_lin = np.polyval(a, x_lin)\n",
    "plt.scatter(x, y, label=r\"data $(x_i, y_i)$\")\n",
    "plt.plot(x_lin, y_lin, label=r\"interpolant $p_2(x)$\")\n",
    "plt.grid()\n",
    "plt.ylabel(r\"$y$\")\n",
    "plt.xlabel(r\"$x$\")\n",
    "plt.legend()\n",
    "plt.show()\n",
    "### END SOLUTION"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c3bacfe2-119a-4fae-abf4-5b39aa701ba3",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-c76fe18176498d71",
     "locked": true,
     "points": 0,
     "schema_version": 3,
     "solution": false,
     "task": true
    }
   },
   "source": [
    "<div class=\"alert alert-success\">\n",
    "\n",
    "**Exercise 3 (Theoretical):** Find the three polynomials of the Lagrangian basis corresponding to the points $x_0=0, x_1=1$ and $x_2=2$.\n",
    "</div>\n",
    "\n",
    "=== BEGIN MARK SCHEME ===\n",
    "\n",
    "Using the formula from Definition 2.2\n",
    "\n",
    "\\begin{align*}\n",
    "\\phi_i(x)=\\prod_{\\stackrel{j=0}{j\\neq i}}^2 \\frac{x-x_j}{x_i-x_j}, \\quad \\quad i=0,1,2.\n",
    "\\end{align*}\n",
    "\n",
    "we find the Lagrangian polynomials by replacing the values of $x_0 = 0$, $x_1 = 1$, and $x_2 = 2$ to get\n",
    "\n",
    "\\begin{align*}\n",
    "& \\phi_0(x)=\\frac{x-x_1}{x_0-x_1}\\cdot \\frac{x-x_2}{x_0-x_2}= \\frac{(x-1)(x-2)}{2}\\\\\n",
    "& \\phi_1(x)=\\frac{x-x_0}{x_1-x_0}\\cdot \\frac{x-x_2}{x_1-x_2}= -x(x-2)\\\\\n",
    "& \\phi_2(x)=\\frac{x-x_0}{x_2-x_0}\\cdot \\frac{x-x_1}{x_2-x_1}= \\frac{x(x-1)}{2}.  \n",
    "\\end{align*}\n",
    "\n",
    "=== END MARK SCHEME ==="
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5e3345ca-ba08-4698-b584-c539039a087e",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-947dc5151412a6ab",
     "locked": true,
     "points": 0,
     "schema_version": 3,
     "solution": false,
     "task": true
    }
   },
   "source": [
    "<div class=\"alert alert-success\">\n",
    "\n",
    "**Exercise 4 (Theoretical):** Using your result from the previous exercise, write down the second order polynomial interpolating the data. Compare the resulting polynomial to what you have found with the Vandermonde matrix at the beginning of this section.\n",
    "</div>\n",
    "\n",
    "=== BEGIN MARK SCHEME ===\n",
    "\n",
    "The sought polynomial is given by\n",
    "\n",
    "$$\n",
    "p_2(x)= y_0 \\phi_0(x) + y_1 \\phi_1(x) + y_2 \\phi_2(x).\n",
    "$$\n",
    "\n",
    "Inserting the Lagrange polynomials from the previous exercise and the $y$-data, we get\n",
    "\n",
    "\\begin{align*}\n",
    "p_2(x)=& 6(x-1)(x-2)-18 x(x-2)+3x(x-1)\\\\\n",
    "=& -9 x^2 + 15 x + 12.\n",
    "\\end{align*}\n",
    "\n",
    "This is the same polynomial as we have found earlier in the section.\n",
    "\n",
    "=== END MARK SCHEME ==="
   ]
  },
  {
   "cell_type": "markdown",
   "id": "171e46ca-4033-43d5-aa35-918e83028b80",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-231501d536ca7f10",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "<hr style=\"clear:both\">\n",
    "\n",
    "### Interpolating functions at uniform and non-uniform nodes"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "20cecddd-9da9-4501-8b33-1b0b9c58b2b7",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-cf07ec01b52c57c1",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "We are given the data $(x_i,y_i)$, $i=0,1,\\ldots,n$, where the $x_i$ are $n+1$ nodes in the interval $[a,b]=[-5,5]$ and\n",
    "the values $y_i$ come from evaluation of the *Runge* function\n",
    "$$\n",
    "  g(x)= \\frac{1}{1+x^2}\n",
    "$$\n",
    "without any measurement error, namely $y_i = g(x_i)$.  We want\n",
    "to apply polynomial interpolation to the function $g$ from the data $(x_i,y_i)$."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "44ec24f9-28db-486c-aede-8d7843df0fac",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-3713f2f8c72dc60b",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "<div class=\"alert alert-success\">\n",
    "\n",
    "**Exercise 5:** For degrees $n=6$ and $n=12$, compute the polynomial $p_n$ interpolating the data $(x_i,y_i)$, $i=0,1,...,n$ with uniformly spaced nodes\n",
    "\n",
    "\\begin{equation*}\n",
    "    x_i = a + \\frac{i}{n} (b - a), ~i=0,1,...,n.\n",
    "\\end{equation*}\n",
    "\n",
    "Visualize the function and the interpolating polynomial at $1'000$ evenly spaced points in the interval $[-5, 5]$. What do you observe?\n",
    "</div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a4403392-5586-4756-ba1f-310ebd20a19d",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-7ef3e42056d8af80",
     "locked": false,
     "schema_version": 3,
     "solution": true,
     "task": false
    }
   },
   "outputs": [],
   "source": [
    "### BEGIN SOLUTION\n",
    "def g(x):\n",
    "    return 1 / (1 + x ** 2)\n",
    "\n",
    "x_lin = np.linspace(-5, 5, 1000)\n",
    "y_lin = g(x_lin)\n",
    "\n",
    "x_6_unif = - 5 + np.arange(6 + 1) / 6 * (5 - (- 5))\n",
    "y_6_unif = g(x_6_unif)\n",
    "a_6_unif = np.polyfit(x_6_unif, y_6_unif, 6)\n",
    "y_lin_6_unif = np.polyval(a_6_unif, x_lin)\n",
    "\n",
    "x_12_unif = - 5 + np.arange(10 + 1) / 10 * (5 - (- 5))\n",
    "y_12_unif = g(x_12_unif)\n",
    "a_12_unif = np.polyfit(x_12_unif, y_12_unif, 10)\n",
    "y_lin_12_unif = np.polyval(a_12_unif, x_lin)\n",
    "\n",
    "plt.scatter(x_6_unif, y_6_unif, label=r\"$(x_i, y_i)$ for $n = 5$\")\n",
    "plt.plot(x_lin, y_lin_6_unif, label=r\"interpolant $p_6(x)$\")\n",
    "plt.scatter(x_12_unif, y_12_unif, label=r\"$(x_i, y_i)$ for $n = 12$\")\n",
    "plt.plot(x_lin, y_lin_12_unif, label=r\"interpolant $p_{12}(x)$\")\n",
    "plt.plot(x_lin, y_lin, label=r\"function $g(x)$\", color=\"black\")\n",
    "plt.grid()\n",
    "plt.ylabel(r\"$y$\")\n",
    "plt.xlabel(r\"$x$\")\n",
    "plt.legend()\n",
    "plt.show()\n",
    "### END SOLUTION"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "25ec3b7a-b5d1-424c-9bac-d084594c286f",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-cea090203d5bd4f8",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "<div class=\"alert alert-success\">\n",
    "\n",
    "**Exercise 6:** Repeat the previous exercise for the Chebyshev nodes, i.e. nodes which are given by\n",
    "\n",
    "\\begin{equation*}\n",
    "    x_i = \\frac{b+a}{2} - \\frac{b-a}{2} \\cos\\left(\\frac{(i+1/2)\\pi}{n+1}\\right),  ~i=0,1,...,n.\n",
    "\\end{equation*}\n",
    "\n",
    "Compare the result to the previous exercise. Explain the difference using what you have learned in the lecture.\n",
    "\n",
    "</div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c80748c6-96b9-4063-b385-6e14cee9f702",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-72d8d1630a517dcb",
     "locked": false,
     "schema_version": 3,
     "solution": true,
     "task": false
    }
   },
   "outputs": [],
   "source": [
    "### BEGIN SOLUTION\n",
    "x_6_cc = (5 + (- 5)) / 2 - (5 - (- 5)) / 2 * np.cos(np.pi * (np.arange(6 + 1) + 0.5) / 7) \n",
    "y_6_cc = g(x_6_cc)\n",
    "a_6_cc = np.polyfit(x_6_cc, y_6_cc, 6)\n",
    "y_lin_6_cc = np.polyval(a_6_cc, x_lin)\n",
    "\n",
    "x_12_cc = (5 + (- 5)) / 2 - (5 - (- 5)) / 2 * np.cos(np.pi * (np.arange(12 + 1) + 0.5) / 13) \n",
    "y_12_cc = g(x_12_cc)\n",
    "a_12_cc = np.polyfit(x_12_cc, y_12_cc, 12)\n",
    "y_lin_12_cc = np.polyval(a_12_cc, x_lin)\n",
    "\n",
    "plt.scatter(x_6_cc, y_6_cc, label=r\"$(x_i, y_i)$ for $n = 6$\")\n",
    "plt.plot(x_lin, y_lin_6_cc, label=r\"interpolant $p_6(x)$\")\n",
    "plt.scatter(x_12_cc, y_12_cc, label=r\"$(x_i, y_i)$ for $n = 12$\")\n",
    "plt.plot(x_lin, y_lin_12_cc, label=r\"interpolant $p_{12}(x)$\")\n",
    "plt.plot(x_lin, y_lin, label=r\"function $g(x)$\", color=\"black\")\n",
    "plt.grid()\n",
    "plt.ylabel(r\"$y$\")\n",
    "plt.xlabel(r\"$x$\")\n",
    "plt.legend()\n",
    "plt.show()\n",
    "\n",
    "# Difference: Much better approximation for Chebyshev nodes.\n",
    "# Explanation: Chebyshev nodes have a slower growing Lebesgue constant than uniformly spaced nodes.\n",
    "# Therefore, by Definition 2.4, the interpolation is much more stable.\n",
    "\n",
    "### END SOLUTION"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f01a8636-d4a0-49e9-9d89-c094396475c4",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-edc009ca3aeda977",
     "locked": true,
     "points": 0,
     "schema_version": 3,
     "solution": false,
     "task": true
    }
   },
   "source": [
    "<div class=\"alert alert-success\">\n",
    "    \n",
    "**Exercise 7 (Theoretical):** Let $a, b \\in \\mathbb{R}$ such that $a < b$. Given $n \\in \\mathbb{N} \\setminus \\{0\\}$, define $h = (b-a)/n$ and, for all $i \\in \\{0, \\dots, n\\}$, $x_i = a+hi$. Prove that, for all $x \\in [a, b]$\n",
    "\n",
    "$$\n",
    "\\left|\\prod_{i=0}^n (x-x_i)\\right| \\le n! h^{n+1} / 4.\n",
    "$$\n",
    "</div>\n",
    "\n",
    "=== BEGIN MARK SCHEME ===\n",
    "\n",
    "Fix any $j \\in \\{0, \\dots, n-1\\}$ and assume that $x \\in (x_j, x_{j+1})$. Then\n",
    "$$\n",
    "\\prod_{i=0}^n (x-x_i) = (x - x_j)(x - x_{j+1}) \\prod_{i=0}^{j-1} (x-x_i) \\prod_{i=j + 2}^{n} (x-x_i)\n",
    "$$\n",
    "The polynomial $(x - x_j)(x - x_{j+1})$ takes its maximum at $x = (x_j + x_{j+1})/2$, which can be shown by determining where its derivative vanishes. Hence, $|(x - x_j)(x - x_{j+1})| \\leq h^2 / 4$. The remaining factors are bound by $h^{n-1} n!$, since $|x - x_i| \\leq (|j - i| + 1) h,~\\forall i$. Consequently, we have  \n",
    "$$\n",
    "\\left| \\prod_{i=0}^n (x-x_i) \\right| \\leq \\frac{n! h^{n+1}}{4}\n",
    "$$\n",
    "as was to be shown.\n",
    "\n",
    "\n",
    "=== END MARK SCHEME ==="
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ad0cc246-9347-4104-bfe4-f3a0e19dfc2a",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-3270c931b3a927db",
     "locked": true,
     "points": 0,
     "schema_version": 3,
     "solution": false,
     "task": true
    }
   },
   "source": [
    "<div class=\"alert alert-success\">\n",
    "    \n",
    "**Exercise 8 (Theoretical):** Use the bound from the previous exercise and a theorem seen in a lecture that if $f \\in \\mathrm{C}^{n+1}([a, b], \\mathbb{R})$ and $p_n : \\mathbb{R} \\to \\mathbb{R}$ is the polynomial function of degree at most $n$ such that $p_n(x_i) = f(x_i)$ for all $i \\in \\{0, \\dots, n\\}$, then\n",
    "\\begin{equation*}\n",
    "\\max_{x \\in [a, b]} |f(x)-p_n(x)| \\le \\frac{1}{4(n+1)} \\left(\\frac{b-a}{n}\\right)^{n+1} \\max_{x \\in [a, b]} |f^{(n+1)}(x)|.\n",
    "\\end{equation*}\n",
    "</div>\n",
    "\n",
    "=== BEGIN MARK SCHEME ===\n",
    "\n",
    "From a theorem in the lectures, we know that for such a function $f$ and an interpolating polynomial $p_n$ it holds\n",
    "\n",
    "$$\n",
    "f(x) = p_n(x) + \\frac{f^{(n+1)}(c)}{(n+1)!} \\prod_{i=0}^{n} (x - x_i)\n",
    "$$\n",
    "\n",
    "for some $c \\in (a, b)$. Therefore, rearranging this expression, taking its magnitude, and finally the maximum with respect to $x$ gives\n",
    "\n",
    "$$\n",
    "\\max_{x \\in [a, b]} |f(x) - p_n(x)| = \\frac{1}{(n+1)!} \\left| f^{(n+1)}(c) \\right| \\max_{x \\in [a, b]} \\left|\\prod_{i=0}^{n} (x - x_i)\\right|\n",
    "$$\n",
    "\n",
    "The second term on the right-hand side can be bound using the previous exercise and the first one by taking the maximum with respect to $c$\n",
    "\n",
    "$$\n",
    "\\max_{x \\in [a, b]} |f(x) - p_n(x)| \\leq \\frac{1}{(n+1)!} \\max_{x \\in [a,b]} \\left| f^{(n+1)}(x) \\right| \\frac{n! h^{n+1}}{4} = \\frac{1}{4(n+1)} \\left(\\frac{b-a}{n}\\right)^{n+1} \\max_{x \\in [a, b]} |f^{(n+1)}(x)|,\n",
    "$$\n",
    "\n",
    "where we used the definition $h = (b - a)/n$.\n",
    "\n",
    "=== END MARK SCHEME ==="
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0be30898-1de3-4750-b510-87c88ca004ed",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-8cc7f4e29978a25b",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "<hr style=\"clear:both\">\n",
    "\n",
    "### Interpolation with cubic splines"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a2399b12-afe7-46b8-8de0-afc31f306fe4",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-09a88f08fb9c51f5",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "We consider the functions\n",
    "\n",
    "$$\n",
    "g_1(x) = \\sin(5 x)\n",
    "$$\n",
    "\n",
    "and \n",
    "\n",
    "$$\n",
    "g_2(x) = x |x|\n",
    "$$\n",
    "\n",
    "on the interval $[-1, 1]$."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5fef24d8-2835-4b97-a2cd-f4359ba9bd68",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-227a5ecccdbaacf4",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "<div class=\"alert alert-success\">\n",
    "    \n",
    "**Exercise 9:** Use cubic splines to interpolate the functions $g_1$ and $g_2$ at $n + 1$ equidistant nodes for $n = 2^k$ and $k = 4, 5, \\dots, 8$ in the interval $[-1, 1]$. For every $h = 2 / n$, approximate the maximum absolute error\n",
    "\n",
    "$$\n",
    "E_{3, h} = \\max_{x \\in [-1, 1]} |g(x) - s_{3, h}(x)|\n",
    "$$\n",
    "\n",
    "by evaluating the spline and the function in $100$ equally spaced points within the interval $[-1, 1]$ and computing the maximum absolute distance attained in these points. Plot $E_{3, h}$ against all $h$ for both functions in a log-log plot (use `plt.loglog` for this).\n",
    "\n",
    "*Hint:* The SciPy function `spline = sp.interpolate.CubicSpline(x, y)` interpolates some data `x` and `y` with a cubic spline, and returns a a function `spline`, which can be called to evaluate the spline at some points. \n",
    "</div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9ee591b5-ba1f-404e-977c-cd61efc57ca2",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-16828d478bb8ef2c",
     "locked": false,
     "schema_version": 3,
     "solution": true,
     "task": false
    }
   },
   "outputs": [],
   "source": [
    "### BEGIN SOLUTION\n",
    "def g_1(x):\n",
    "    return np.sin(5 * x)\n",
    "\n",
    "def g_2(x):\n",
    "    return x * np.abs(x)\n",
    "\n",
    "x_lin = np.linspace(-1, 1, 100)  # fine grid\n",
    "errors_g_1 = []\n",
    "errors_g_2 = []\n",
    "n_list = 2 ** np.arange(4, 9)\n",
    "h_list = 2 / n_list\n",
    "for n in n_list:\n",
    "    x_interp = np.linspace(-1, 1, n + 1)  # interpolation nodes\n",
    "    y_g_1 = g_1(x_interp) \n",
    "    y_g_2 = g_2(x_interp)\n",
    "    s3h_g_1 = sp.interpolate.CubicSpline(x_interp, y_g_1)\n",
    "    s3h_g_2 = sp.interpolate.CubicSpline(x_interp, y_g_2)\n",
    "    errors_g_1.append(np.max(np.abs(g_1(x_lin) - s3h_g_1(x_lin))))\n",
    "    errors_g_2.append(np.max(np.abs(g_2(x_lin) - s3h_g_2(x_lin))))\n",
    "\n",
    "plt.loglog(h_list, errors_g_1, label=r\"error $E_{3, h}$ for $g_1$\")\n",
    "plt.loglog(h_list, np.array(h_list)**5, label=r\"$\\mathcal{O}(h^5)$\", linestyle=\"--\", color=\"tab:blue\")\n",
    "plt.loglog(h_list, errors_g_2, label=r\"error $E_{3, h}$ for $g_2$\")\n",
    "plt.loglog(h_list, np.array(h_list)**2, label=r\"$\\mathcal{O}(h^2)$\", linestyle=\"--\", color=\"tab:orange\")\n",
    "plt.xlabel(r\"$h$\")\n",
    "plt.grid()\n",
    "plt.legend()\n",
    "plt.show()\n",
    "\n",
    "# The maximum absolute interpolation error of the interpolating spline for $g_1$\n",
    "# goes down approximately as the line $h^5$. Therefore, we observe an even faster\n",
    "# convergence than what is guaranteed by Theorem 2.4. \n",
    "# For $g_2$ the error goes down with $h^2$. As expected, the bound from Theorem 2.4\n",
    "# does not hold in this case.\n",
    "\n",
    "### END SOLUTION"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1ba42156-1c02-4a00-b8b1-fcc789dd6435",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-5b39b5b957674ceb",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "<hr style=\"clear:both\">\n",
    "\n",
    "## The end\n",
    "\n",
    "Splendid! You have reached the end of the eighth exercise notebook."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
