Measure differentiation and integration error
Compare tabulated Gaussian curves, finite differences and trapezoidal integration with analytic references.
Goal and how to run
This lesson assumes basic Python knowledge. Read each method and caption before changing parameters. AI exercises are optional; no AI account is needed for the core workflow. Data lives in notebook metadata, so there is no companion data download. Keep the notebook saved in the active working folder.
Your goal is to understand the research question, execute transparent calculations, check the results, and state the limits of the conclusion.
Setup and parameters
from pathlib import Path
import base64, gzip, hashlib, json, platform
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from IPython.display import display, HTML
import math
from scipy.integrate import quad
from IPython.display import display as _display
def display(value):
if isinstance(value,pd.DataFrame):
table=value.to_html(max_rows=12,escape=True)
style="<style>.vibeit-readable-table{max-width:100%;overflow-x:auto}.vibeit-readable-table table{width:max-content;max-width:none;border-collapse:collapse}.vibeit-readable-table td,.vibeit-readable-table th{white-space:nowrap!important;word-break:normal!important;overflow-wrap:normal!important}</style>"
return _display(HTML(style+'<div class="vibeit-readable-table">'+table+'</div>'))
return _display(value)
RECIPE_ID='ma01-numerical-calculus'
OUTPUT_DIR=Path.cwd()/"results"/RECIPE_ID
plt.rcParams.update({"figure.dpi":120,"font.size":10,"axes.spines.top":False,"axes.spines.right":False,
"axes.prop_cycle":plt.cycler(color=["#18785f","#416aa6","#bc6541","#9974a5"])})
print("Python:",platform.python_version(),"; NumPy:",np.__version__,"; Pandas:",pd.__version__)
print("Working folder:",Path.cwd())
print("Core data mode: embedded snapshot; no network requests")
Python: 3.13.13 ; NumPy: 2.5.3 ; Pandas: 3.0.6 Working folder: /Users/zhiluo/Developer/vibeit-cookbook/cookbook/downloads Core data mode: embedded snapshot; no network requests
Load the embedded snapshot
def load_snapshot(recipe_id, expected_sha256, snapshot_key):
"""Read embedded data from this notebook, including a renamed copy."""
for path in sorted(Path.cwd().glob("*.ipynb")):
try:
notebook = json.loads(path.read_text(encoding="utf-8"))
metadata = notebook.get("metadata", {}).get("vibeit_cookbook", {})
blob = metadata.get("snapshots", {}).get(snapshot_key)
if metadata.get("recipe_id") != recipe_id or not blob:
continue
if blob.get("sha256") != expected_sha256:
continue
raw = gzip.decompress(base64.b64decode(blob["gzip_base64"], validate=True))
if hashlib.sha256(raw).hexdigest() != expected_sha256:
raise ValueError("Embedded snapshot checksum mismatch: " + snapshot_key)
return json.loads(raw)
except (OSError, json.JSONDecodeError):
continue
raise FileNotFoundError(
"Save/open the complete .ipynb in the active working folder. "
"The embedded snapshot could not be located for " + recipe_id)
SNAPSHOT_HASHES={'models': '48fd2e6e706650fe2ee0c0ad80b6d794a546a1dec78dd9d370ac09db3cc3adbf'}
def snapshot(key):
return load_snapshot(RECIPE_ID,SNAPSHOT_HASHES[key],key)
print("Embedded snapshots:",list(SNAPSHOT_HASHES))
Embedded snapshots: ['models']
Read the method functions
def trapezoid_integral(x,y):
"""Integrate a tabulated curve with explicitly increasing abscissae."""
x=np.asarray(x,dtype=float);y=np.asarray(y,dtype=float)
if x.ndim!=1 or x.shape!=y.shape or len(x)<2 or not np.isfinite(x+y).all():raise ValueError('Invalid curve')
widths=np.diff(x)
if np.any(widths<=0):raise ValueError('x must be strictly increasing')
return float(np.sum(widths*(y[:-1]+y[1:])/2))
print("Method ready: trapezoid_integral")
Method ready: trapezoid_integral
Data and model boundary
Analytic functions and Monte Carlo points are mathematical teaching models, not experimental measurements. When Iris observations are used, row exchangeability and the sampling design remain assumptions; convenience records alone do not establish population representativeness. Numerical discretization error is distinct from sampling uncertainty.
Steps
1. Declare the function and reference integral
Use f(x)=exp(−x²) on the finite interval [−2,2]. x and f are dimensionless in this benchmark. The exact finite-interval integral is sqrt(pi)·erf(2); do not compare it with the infinite-interval value sqrt(pi).
model_inputs=snapshot("models");seed=model_inputs["seed"]
LOWER,UPPER=-2.0,2.0;POINTS=161
x=np.linspace(LOWER,UPPER,POINTS);y=np.exp(-x*x)
reference=math.sqrt(math.pi)*math.erf(2)
quad_reference,quad_error=quad(lambda value:np.exp(-value*value),LOWER,UPPER)
assert np.isclose(reference,quad_reference,rtol=1e-12)
fig,ax=plt.subplots(figsize=(7,4));ax.plot(x,y,label="exp(−x²)");ax.fill_between(x,y,alpha=.2)
ax.set(xlabel="x (dimensionless)",ylabel="f(x)",title="Finite-interval integration problem");ax.legend();plt.tight_layout();plt.show()
2. Differentiate the tabulated curve
Interior central differences are second-order for this smooth function. Endpoint rules can have a different error order; calculate interior error separately. np.gradient receives the actual x grid rather than assuming a unit spacing.
derivative=np.gradient(y,x,edge_order=2);exact_derivative=-2*x*np.exp(-x*x)
fig,ax=plt.subplots(figsize=(7,4));ax.plot(x,exact_derivative,label="Analytic",lw=2);ax.plot(x,derivative,"--",label="Tabulated finite difference")
ax.set(xlabel="x",ylabel="df/dx",title="Derivative from explicit grid spacing");ax.legend();plt.tight_layout();plt.show()
print("Maximum interior derivative error:",float(np.max(np.abs(derivative[1:-1]-exact_derivative[1:-1]))))
Maximum interior derivative error: 0.00040651967031690717
3. Refine the integration grid
Trapezoid error should decrease approximately like h² while rounding error is small. Both axes of the convergence plot are logarithmic. A falling error curve here concerns a known smooth model; it is not an uncertainty interval for noisy measurements.
intervals=np.array([20,40,80,160,320]);estimates=[];widths=[]
for count in intervals:
grid=np.linspace(LOWER,UPPER,count+1);widths.append((UPPER-LOWER)/count)
estimates.append(trapezoid_integral(grid,np.exp(-grid*grid)))
error=np.abs(np.array(estimates)-reference)
fig,ax=plt.subplots(figsize=(7,4));ax.loglog(widths,error,"o-",label="Observed absolute error")
ax.loglog(widths,error[0]*(np.array(widths)/widths[0])**2,"--",label="h² comparison")
ax.set(xlabel="Grid spacing h",ylabel="Absolute integral error",title="Trapezoid grid refinement");ax.legend();plt.tight_layout();plt.show()
4. Reconcile and export the numerical audit
Compare the readable implementation with NumPy’s tabulated trapezoid rule, and check grid-order validation. Keep analytic values, quadrature tolerances and discretization errors in different columns.
assert np.isclose(trapezoid_integral(x,y),np.trapezoid(y,x))
assert error[-1]<error[0] and np.all(error>0)
try:trapezoid_integral(x[::-1],y[::-1])
except ValueError:print("Reversed abscissae rejected")
else:raise AssertionError("Invalid grid accepted")
OUTPUT_DIR.mkdir(parents=True,exist_ok=True)
pd.DataFrame({"x":x,"f":y,"finite_difference":derivative,"analytic_derivative":exact_derivative}).to_csv(OUTPUT_DIR/"curve-and-derivative.csv",index=False)
pd.DataFrame({"intervals":intervals,"h":widths,"integral":estimates,"absolute_error":error}).to_csv(OUTPUT_DIR/"grid-refinement.csv",index=False)
(OUTPUT_DIR/"references.json").write_text(json.dumps({"finite_interval":[LOWER,UPPER],"analytic_integral":reference,"quad_reference":quad_reference,"quad_error_estimate":quad_error},indent=2))
print("Reference checks passed; exports:",OUTPUT_DIR)
Reversed abscissae rejected Reference checks passed; exports: /Users/zhiluo/Developer/vibeit-cookbook/cookbook/downloads/results/ma01-numerical-calculus
Exercises and reference explanation
- Explain the finite- versus infinite-interval Gaussian integral.
- Compare interior and endpoint derivative errors.
Reference explanation. The tails outside [−2,2] are excluded. Interior central differences and endpoint formulas need separate error analysis.
Keep a copy before changing parameters. Compare old and new figures and record which inputs, calculations and conclusions changed.
Optional AI coding exercises
Prompt 1
Using NumPy/SciPy/Matplotlib, double every integration grid and report observed error ratios without changing the analytic reference.
Prompt 2
With the same packages, compare edge_order=1 and 2 and plot endpoint versus interior errors.
Human acceptance. Check strictly increasing grids, agreement with NumPy trapezoid and the exact finite-interval erf expression.
Review the edited source, then manually run affected cells and subsequent checks. Ask the assistant to explain the purpose, packages and data provenance of its changes, and reconcile that explanation with actual output.
Optional online extension
RUN_ONLINE_UPDATE=False
if RUN_ONLINE_UPDATE:
import requests
try:
response=requests.get('https://docs.scipy.org/doc/scipy/tutorial/integrate.html',timeout=20)
response.raise_for_status()
print("Source:",response.url)
print(response.text[:700])
except requests.RequestException as error:
print("Online extension unavailable:",type(error).__name__,str(error)[:160])
else:
print("Online extension skipped; embedded core data unchanged")
Online extension skipped; embedded core data unchanged
Sources, snapshots and next steps
IAPWS saturation reference release — IAPWS SR1-86(1992): unrestricted publication allowed in all countries (cover page); retrieved 2026-10-09. SHA-256
a10d38d73339b0713c86cfac88f9adcba137202167ccdfa1cd58238c438361d7. Reference equations and numerical coefficients transcribed from release pages 2–3; pedagogical model values are not new experiments.Authored numerical teaching model parameters — Original teaching parameters/code MIT; reference equation coefficients attributed to IAPWS SR1-86(1992); retrieved 2026-10-09. SHA-256
4350c4305a83c772fa295a8be0ef86a247f2bfaae930a081720d3cd0c753abe0. Declared parameters and seed; generated trajectories and calibration observations must be labelled simulated.
Next steps. Transfer these methods to your own public or authorized data while retaining input checks, recorded parameters and result validation. Document the new experimental design and method limits; this example does not validate a new experiment. Full data licenses and generation instructions accompany the cookbook.