{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "ceramic-linux",
   "metadata": {},
   "source": [
    "# The Composite Trapezoid Rule (and Composite Midpoint Rule)\n",
    "\n",
    "**Last updated on April 5 (after class)** with the example of the Composite Midpoint Rule."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "stable-dressing",
   "metadata": {},
   "source": [
    "Write a Python function that can approximate any definite integral $I = \\displaystyle \\int_a^b f(x)\\,dx$\n",
    "using the Composite Trapezoid Rule with $n$ sub-intervals of equal width, $T_n$.\n",
    "\n",
    "- The input should specify the function $f$ to integrate, the interval $[a,b]$, and the number $n$ of sub-intervals to use.\n",
    "\n",
    "- The output will be just the approximation value of the integral; error estimates and tolerances are coming later!\n",
    "\n",
    "- As usual add a code cell with testing of this on some examples, as suggested in the section on [Test Cases for Integration](test-cases-integration.ipynb)."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "former-hydrogen",
   "metadata": {},
   "source": [
    "## Update: The Composite Midpoint Rule\n",
    "\n",
    "As the Composite Midpoint Rule is used in Recursive Trapezeoid Rule and the Romberg Method, I will illustrate with it:\n",
    "\n",
    "$$\\int_a^b f(x)\\, dx \\approx M_n = \\sum_{i=1}^n f(a + (i-1/2)h)\n",
    "\\quad \\text{where } h=(b-a)/n\n",
    "$$\n",
    "\n",
    "Python has a function `sum`, and can get an array of the $x$ values with `numpy.linspace`, so ths can be done as folllows:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "seeing-discovery",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "global-check",
   "metadata": {},
   "outputs": [],
   "source": [
    "def compositeMidpoint(f, a, b, n, demoMode=False):\n",
    "    h = (b-a)/n\n",
    "    x = np.linspace(a+h/2, b-h/2, n)\n",
    "    if demoMode:\n",
    "        print(f\"With {a=}, {b=}, {n=}, {h=} and the nodes are {x}\")\n",
    "    M_n = sum(f(x)) * h\n",
    "    return M_n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "systematic-jerusalem",
   "metadata": {},
   "outputs": [],
   "source": [
    "def f(x): return 1/x**2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "residential-reference",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "With a=1, b=3, n=10, h=0.2 and the nodes are [1.1 1.3 1.5 1.7 1.9 2.1 2.3 2.5 2.7 2.9]\n",
      "M_10=0.6635018670317585, error=-0.003164799634908122\n",
      "\n",
      "M_100=0.6666345725472225, error=-3.20941194441593e-05\n"
     ]
    }
   ],
   "source": [
    "a=1\n",
    "b=3\n",
    "I_exact = 2.0/3.0\n",
    "\n",
    "M_10 = compositeMidpoint(f, a, b, 10, demoMode=True)\n",
    "error = M_10 - I_exact\n",
    "print(f\"{M_10=}, {error=}\")\n",
    "print()\n",
    "M_100 = compositeMidpoint(f, a, b, 100, demoMode=False)\n",
    "error = M_100 - I_exact\n",
    "print(f\"{M_100=}, {error=}\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "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.8.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
