Calculus Essentials
Calculus is the mathematics of continuous change — and the hidden engine inside every gradient descent, physics simulation, probability distribution, and rendering algorithm in computer science.
Overview
Calculus is often described as the mathematics of change and accumulation, but for the CS student, a better frame is this: calculus is the language in which optimization, physics, probability, and signal processing are written. When you train a neural network, you are running gradient descent — pure calculus. When a game engine resolves a collision, it solves a differential equation. When you sample from a distribution, the probability mass under a curve is an integral. Calculus is not an abstract detour from programming; it is load-bearing infrastructure.
This module covers the two great pillars of single-variable calculus — differential calculus (rates of change, derivatives) and integral calculus (accumulation, area, antiderivatives) — plus the theorem that ties them together. We build every concept from the ground up: starting with the intuition of limits, moving through formal definitions, deriving the rules, and then always asking why does a programmer care?
The historical context is worth one sentence: Newton invented calculus to describe planetary orbits; Leibniz invented the same calculus (independently, a few years later) with better notation. Computer scientists inherit Leibniz's notation — dx, dy, ∫ — because it has better algebraic manipulation properties, as you will see when you apply the chain rule.
By the end of this module you will have the vocabulary and toolkit to read papers in machine learning, understand backpropagation from first principles, evaluate integrals in probability theory, and reason precisely about algorithmic approximations of continuous mathematics on discrete hardware.
Every major subfield of computer science that touches the real world — machine learning, computer graphics, robotics, scientific computing, audio/image processing, computer vision, compilers for numerical code — is built on calculus. The gradient of a loss function IS a vector of partial derivatives. The softmax and sigmoid activations have derivative formulas that flow directly into backpropagation. Bezier curves used in fonts and SVG graphics are defined by polynomials, and their smoothness properties are calculus. Monte Carlo integration in rendering engines, Fast Fourier Transforms in audio codecs, PID controllers in robotics: all calculus.
Beyond direct application, calculus trains a style of thinking that is indispensable in CS: taking a discrete quantity, imagining the limit as steps shrink to zero, reasoning about that idealized continuous process, and then understanding the discrete algorithm as an approximation with quantifiable error. This is how numerical methods work, how complexity analysis of iterative algorithms works (you are often solving a recurrence by analogy with a differential equation), and how you reason about floating-point error accumulation. A CS student who skips calculus will perpetually be stopped at the gates of entire subfields.
Chain Rule Through a Neural Network Forward Pass
Forward pass (solid arrows) computes the output; backward pass (dashed arrows) propagates gradients via the chain rule — each arrow multiplies the upstream gradient by the local derivative. This is backpropagation.
The concepts
01Limits: The Foundation of Everything
A limit asks: as an input value approaches some target, what value does the function approach? It does not ask what the function equals at that point — it asks about the trend as we get arbitrarily close.
Formally, we write: $$\lim_{x \to a} f(x) = L$$ This means: for every $\varepsilon > 0$, there exists a $\delta > 0$ such that whenever $0 < |x - a| < \delta$, we have $|f(x) - L| < \varepsilon$. This is the epsilon-delta definition (Weierstrass, 1850s), and it is the rigorous underpinning of everything in calculus.
The epsilon-delta definition sounds mechanical but embodies a profound idea: we can reason about infinite processes by making the error provably small. This is exactly what numerical analysts do when they prove a floating-point algorithm is accurate to within machine epsilon.
Intuition via a CS analogy. Imagine binary search. As you double the number of iterations, your error halves. The limit of the error as iterations go to infinity is 0. You never literally run infinitely many iterations, but the limit tells you the trend. Calculus formalizes this kind of reasoning.
Key limit laws:
- Limits of sums, products, and quotients factor through (when the denominator limit is nonzero)
- The Squeeze Theorem: if $g(x) \le f(x) \le h(x)$ near $a$, and both $g$ and $h$ approach $L$, then $f$ also approaches $L$
- The famous limit $\lim_{x \to 0} \frac{\sin x}{x} = 1$ (proved by squeezing between $\cos x$ and 1)
L'Hopital's Rule (previewing derivatives): if both numerator and denominator approach 0, the limit of their ratio equals the limit of the ratio of their derivatives — a huge computational shortcut.
Why CS cares. Every asymptotic analysis in algorithms (Big-O, $\Theta$, $\Omega$) is implicitly about limits: $f(n) = O(g(n))$ means $\limsup_{n\to\infty} f(n)/g(n) < \infty$. You use limits every time you talk about convergence of an iterative algorithm.
import math
def limit_sin_over_x(epsilon=1e-9):
"""Numerically verify lim_{x->0} sin(x)/x = 1.
We approach from both sides with decreasing step."""
results = []
for exp in range(1, 15):
x = 10 ** (-exp)
val = math.sin(x) / x
results.append((x, val))
print(f"x={x:.1e} sin(x)/x = {val:.15f}")
return results
limit_sin_over_x()
02Continuity: When Functions Behave Nicely
A function $f$ is continuous at a point $a$ if three conditions hold simultaneously:
- $f(a)$ is defined.
- $\lim_{x \to a} f(x)$ exists.
- The limit equals the value: $\lim_{x \to a} f(x) = f(a)$.
Intuitively: you can draw the function without lifting your pencil. Continuity means no sudden jumps, holes, or vertical asymptotes at that point.
Types of discontinuities:
- Removable: the limit exists but the function has a hole (e.g., $f(x) = \frac{\sin x}{x}$ at $x = 0$ before we define $f(0) = 1$)
- Jump: left and right limits both exist but differ (e.g., the floor function at integers)
- Infinite: the function blows up (e.g., $1/x$ at 0)
The Intermediate Value Theorem (IVT): if $f$ is continuous on $[a,b]$ and $f(a) < y < f(b)$, then there exists $c \in (a,b)$ with $f(c) = y$. This sounds obvious but is deep: it depends on the completeness of the real numbers. The IVT is the theoretical engine behind bisection root-finding — you know a root exists in an interval, so you keep halving. Every numerical root finder in a scientific computing library is implicitly applying the IVT.
The Extreme Value Theorem: a continuous function on a closed bounded interval $[a,b]$ attains both its maximum and minimum. This is why gradient descent can find a minimum for a continuous loss function on compact domain.
CS relevance. Activation functions in neural networks must be continuous (and usually differentiable) to allow smooth gradient flow. The step function is discontinuous — this is why it was abandoned for sigmoid and ReLU. Hash functions are deliberately discontinuous (a tiny input change gives a totally different output).
# Numerical IVT: bisection root-finding for f(x) = x^3 - x - 2
# The IVT guarantees a root in [1, 2] because f(1) = -2 < 0 < f(2) = 4
def f(x):
return x**3 - x - 2
def bisect(a, b, tol=1e-10):
assert f(a) * f(b) < 0, "IVT requires opposite signs at endpoints"
while (b - a) > tol:
mid = (a + b) / 2
if f(mid) == 0:
return mid
elif f(a) * f(mid) < 0:
b = mid
else:
a = mid
return (a + b) / 2
root = bisect(1, 2)
print(f"Root: {root:.10f}") # ≈ 1.5213797068
03The Derivative: Instantaneous Rate of Change
The derivative of $f$ at $a$ is defined as the limit of the difference quotient: $$f'(a) = \lim_{h \to 0} \frac{f(a+h) - f(a)}{h}$$
Geometrically, $(f(a+h)-f(a))/h$ is the slope of the secant line through $(a, f(a))$ and $(a+h, f(a+h))$. As $h \to 0$, the secant line pivots until it becomes the tangent line at $a$. The derivative is the slope of that tangent line.
Physically: if $f(t)$ is position at time $t$, then $f'(t)$ is instantaneous velocity — not average velocity over an interval, but the exact rate at one instant.
Differentiability implies continuity (but not vice versa — the absolute value function $|x|$ is continuous everywhere but not differentiable at 0).
Basic differentiation rules (derived from the limit definition):
- Power rule: $(x^n)' = nx^{n-1}$
- Sum rule: $(f+g)' = f' + g'$
- Product rule: $(fg)' = f'g + fg'$
- Quotient rule: $(f/g)' = (f'g - fg') / g^2$
- Chain rule: see next concept
Notation. Leibniz's $\frac{df}{dx}$ (or $\frac{dy}{dx}$) is preferred in applied math and physics because it makes the chain rule look like fraction cancellation: $\frac{dy}{dt} = \frac{dy}{dx} \cdot \frac{dx}{dt}$. Newton's dot notation $\dot{f}$ is common in physics for time derivatives. Lagrange's $f'$ is common in pure math.
Common derivatives to memorize:
| Function | Derivative |
|---|---|
| $x^n$ | $nx^{n-1}$ |
| $e^x$ | $e^x$ |
| $\ln x$ | $1/x$ |
| $\sin x$ | $\cos x$ |
| $\cos x$ | $-\sin x$ |
| $\sigma(x) = \frac{1}{1+e^{-x}}$ | $\sigma(x)(1-\sigma(x))$ |
That last one — the sigmoid — is something every ML practitioner derives. It is a beautiful special case of the chain rule and quotient rule combined.
Numerical differentiation approximates the derivative using the centered difference: $f'(x) \approx \frac{f(x+h) - f(x-h)}{2h}$ for small $h$. This has error $O(h^2)$ vs $O(h)$ for the one-sided formula.
# Compare analytical vs numerical derivative of f(x) = x^3 - 3x
# f'(x) = 3x^2 - 3 (by power rule)
def f(x):
return x**3 - 3*x
def f_prime_analytical(x):
return 3*x**2 - 3
def f_prime_numerical(x, h=1e-7):
return (f(x + h) - f(x - h)) / (2 * h) # centered difference
for x in [-2, 0, 1, 2]:
analytic = f_prime_analytical(x)
numeric = f_prime_numerical(x)
print(f"x={x:4d} analytic={analytic:8.4f} numeric={numeric:8.4f} err={abs(analytic-numeric):.2e}")
04The Chain Rule: The Heart of Backpropagation
The chain rule tells us how to differentiate a composition of functions. If $y = f(g(x))$, then: $$\frac{dy}{dx} = \frac{dy}{du} \cdot \frac{du}{dx} \quad \text{where } u = g(x)$$
Or equivalently: $(f \circ g)'(x) = f'(g(x)) \cdot g'(x)$.
In Leibniz notation, it looks like fractions canceling — though this is a useful mnemonic, not a rigorous proof. The proof uses the limit definition and a careful squeeze argument.
Intuition. Imagine you're converting temperature: $T_{\text{Kelvin}} = f(\text{Celsius}) = C + 273$, and $C$ changes with altitude $h$ via $C = g(h)$. How fast does Kelvin change with altitude? The chain rule says: multiply the rates. If altitude rises 1 m and that drops Celsius by 0.0065°, then Kelvin drops by exactly the same 0.0065°/m because $dT/dC = 1$.
Extended chain rule (for deep compositions): $$\frac{d}{dx} f_n(f_{n-1}(\cdots f_1(x) \cdots)) = f_n'(\cdots) \cdot f_{n-1}'(\cdots) \cdots f_1'(x)$$ This is literally backpropagation in a neural network with $n$ layers. Each layer contributes a multiplicative factor to the gradient.
Why gradients vanish or explode. If each $|f_i'| < 1$, that product of $n$ terms shrinks exponentially — vanishing gradient. If each $|f_i'| > 1$, it grows exponentially — exploding gradient. The entire research area of residual networks (ResNets), gradient clipping, and careful weight initialization exists to manage this chain-rule phenomenon.
Automatic Differentiation (AD) is the algorithmic application of the chain rule. Modern ML frameworks (PyTorch, JAX, TensorFlow) maintain a computational graph of operations and apply the chain rule symbolically via either forward-mode AD or reverse-mode AD (which is exactly backpropagation). This gives exact (to floating-point precision) gradients, not numerical approximations.
# Manual chain rule: d/dx[ sin(x^2 + 1) ]
# Let u = x^2 + 1, y = sin(u)
# dy/dx = cos(u) * 2x = cos(x^2 + 1) * 2x
import math
def chain_rule_example(x):
u = x**2 + 1
# dy/du = cos(u), du/dx = 2x
return math.cos(u) * 2 * x
# Verify numerically
def f(x): return math.sin(x**2 + 1)
def numeric_deriv(x, h=1e-7): return (f(x+h) - f(x-h)) / (2*h)
for x in [0.5, 1.0, 2.0]:
analytic = chain_rule_example(x)
numeric = numeric_deriv(x)
print(f"x={x} chain-rule={analytic:.8f} numerical={numeric:.8f}")
# --- PyTorch shows how AD does this automatically ---
# import torch
# x = torch.tensor(1.0, requires_grad=True)
# y = torch.sin(x**2 + 1)
# y.backward() # applies chain rule via reverse-mode AD
# print(x.grad) # = cos(x^2+1) * 2x at x=1
05Higher Derivatives, Optimization, and Critical Points
The derivative of a derivative is the second derivative, written $f''(x)$ or $\frac{d^2f}{dx^2}$. It measures how the rate of change is itself changing — i.e., concavity and acceleration.
Critical points and optimization. If $f'(a) = 0$, then $a$ is a critical point — the tangent is horizontal. By the First Derivative Test: if $f'$ changes from positive to negative at $a$, then $a$ is a local maximum; if from negative to positive, a local minimum.
Second Derivative Test: if $f'(a) = 0$ and $f''(a) > 0$, then $a$ is a local minimum (concave up — like a bowl). If $f''(a) < 0$, local maximum (concave down — like a hill). If $f''(a) = 0$, the test is inconclusive.
Gradient Descent. In machine learning with a loss function $L(\theta)$ over parameters $\theta$: $$\theta_{t+1} = \theta_t - \eta \cdot L'(\theta_t)$$ We move opposite to the gradient because we want to descend. $\eta$ is the learning rate. The second derivative (or the Hessian in multiple dimensions) tells us the curvature — too large a step on a highly curved region will overshoot. Adaptive optimizers like Adam estimate the second moment of gradients to adjust step size per-parameter.
Taylor's Theorem — the crown jewel. Any smooth function can be approximated near $a$ by a polynomial: $$f(x) = f(a) + f'(a)(x-a) + \frac{f''(a)}{2!}(x-a)^2 + \cdots + \frac{f^{(n)}(a)}{n!}(x-a)^n + R_n$$ The remainder $R_n$ is bounded by $(x-a)^{n+1}$ times some constant. This is how computers evaluate $\sin$, $\cos$, $e^x$ — via truncated Taylor/Maclaurin series. It is also why Newton's method for root-finding converges quadratically: it fits a linear (tangent line) approximation at each step, using $f$ and $f'$.
# Newton's Method: finds roots of f using f and f'
# Quadratic convergence: error roughly squares each step
def newton(f, f_prime, x0, tol=1e-12, max_iter=50):
x = x0
for i in range(max_iter):
fx = f(x)
if abs(fx) < tol:
print(f"Converged in {i} iterations")
return x
x = x - fx / f_prime(x) # x_{n+1} = x_n - f/f'
return x
import math
root = newton(
f = lambda x: math.cos(x) - x, # find x = cos(x)
f_prime = lambda x: -math.sin(x) - 1,
x0 = 0.5
)
print(f"Fixed point of cos: {root:.15f}") # Dottie number ≈ 0.739085
06The Integral: Accumulation and Area
The definite integral $\int_a^b f(x),dx$ is the signed area between the curve $y = f(x)$ and the $x$-axis, from $a$ to $b$. The definition is constructive:
- Divide $[a,b]$ into $n$ subintervals of width $\Delta x = (b-a)/n$.
- Pick a sample point $x_i^*$ in each subinterval.
- Form the Riemann sum: $\sum_{i=1}^n f(x_i^*) \Delta x$.
- The integral is the limit of this sum as $n \to \infty$ (and $\Delta x \to 0$).
This limit exists (and is the same regardless of where we pick $x_i^*$) whenever $f$ is Riemann integrable — which includes all continuous functions and many discontinuous ones.
Interpretations beyond area:
- If $v(t)$ is velocity, then $\int_a^b v(t),dt$ is net displacement.
- If $p(x)$ is a probability density function, then $\int_a^b p(x),dx$ is the probability the random variable falls in $[a,b]$.
- If $r(t)$ is a data rate (bytes/second), then $\int_0^T r(t),dt$ is total bytes transferred.
Numerical integration. On a computer, you approximate integrals using quadrature rules:
- Left/right Riemann sums: error $O(h)$
- Midpoint rule: error $O(h^2)$
- Trapezoid rule: $\int_a^b f(x),dx \approx \frac{h}{2}[f(a) + 2f(a+h) + \cdots + f(b)]$, error $O(h^2)$
- Simpson's rule: fits parabolas, error $O(h^4)$
- Gaussian quadrature: chooses sample points and weights optimally, spectacularly accurate for smooth functions
The antiderivative (indefinite integral). $\int f(x),dx = F(x) + C$ means $F'(x) = f(x)$. Finding antiderivatives is in general harder than differentiation — there is no product rule analogue (there are integration by parts and substitution instead), and many elementary functions have no closed-form antiderivative (e.g., $e^{-x^2}$, which defines the error function $\text{erf}$).
# Trapezoid rule integration: estimate int_0^pi sin(x) dx = 2
import math
def trapezoid(f, a, b, n):
h = (b - a) / n
total = f(a) + f(b)
for i in range(1, n):
total += 2 * f(a + i * h)
return total * h / 2
for n in [10, 100, 1000, 10000]:
approx = trapezoid(math.sin, 0, math.pi, n)
error = abs(approx - 2.0)
print(f"n={n:6d} approx={approx:.10f} error={error:.2e}")
# Error decreases as O(h^2) = O(1/n^2): 10x more points -> 100x less error
07The Fundamental Theorem of Calculus
The Fundamental Theorem of Calculus (FTC) is one of the greatest theorems in all of mathematics. It reveals that differentiation and integration are inverse operations — two seemingly different geometric ideas (slope vs. area) are in fact two sides of the same coin.
FTC Part 1 (The derivative of an integral). Define the accumulation function $A(x) = \int_a^x f(t),dt$. Then $A'(x) = f(x)$.
In words: the rate at which area accumulates is exactly the height of the function. If you pile up a little more area from $x$ to $x + \Delta x$, it's approximately $f(x) \cdot \Delta x$, and dividing by $\Delta x$ and taking the limit gives exactly $f(x)$.
FTC Part 2 (Evaluation theorem). If $F$ is any antiderivative of $f$ (meaning $F' = f$), then: $$\int_a^b f(x),dx = F(b) - F(a)$$ This is the computational engine: to evaluate a definite integral exactly, find the antiderivative and evaluate it at the two endpoints. This reduces integration from an infinite limiting process (Riemann sums) to algebraic evaluation.
Example. $\int_0^1 x^2 ,dx$. The antiderivative of $x^2$ is $\frac{x^3}{3}$. So the integral is $\frac{1^3}{3} - \frac{0^3}{3} = \frac{1}{3}$.
Why this is miraculous. These are very different operations. Integration counts area via limits of sums; differentiation computes local slope via a limit of a ratio. That they are exact inverses is not at all obvious geometrically, and took millennia of mathematical development to nail down rigorously (the rigorous proof had to wait for Cauchy and Riemann in the 1800s). The theorem also provides the proof that antiderivatives are unique up to a constant.
CS implication. The FTC justifies analytical formulas for integrals, which appear everywhere: the normalization constant of a Gaussian distribution, the cumulative distribution function, the expected value of a continuous random variable, the energy in a signal (Parseval's theorem via Fourier analysis), and more. Numerical integration is needed when there's no closed-form antiderivative.
# FTC Part 1: accumulation function and its derivative
import math
def accumulate(f, a, x, n=10000):
"""Numerically compute A(x) = integral_a^x f(t) dt"""
if x == a: return 0.0
h = (x - a) / n
total = sum(f(a + (i + 0.5) * h) for i in range(n))
return total * h # midpoint Riemann sum
f = math.sin # f(x) = sin(x), antiderivative is -cos(x)
a = 0.0
# FTC Part 2: exact answer via antiderivative F(x) = -cos(x)
def F(x): return -math.cos(x)
for b in [0.5, 1.0, 1.5, math.pi]:
numerical = accumulate(f, a, b)
analytical = F(b) - F(a) # FTC Part 2
print(f"int_0^{b:.4f} sin(x)dx: numerical={numerical:.8f} FTC={analytical:.8f}")
08Integration Techniques: Substitution and By Parts
The FTC reduces integration to finding antiderivatives. But how do you find antiderivatives? There is no single algorithm (unlike differentiation), but there are powerful techniques.
U-Substitution is the chain rule in reverse. If the integral has the form $\int f(g(x)) g'(x),dx$, substitute $u = g(x)$, $du = g'(x),dx$: $$\int f(g(x)) g'(x),dx = \int f(u),du$$
Example: $\int 2x \cos(x^2),dx$. Let $u = x^2$, $du = 2x,dx$. Becomes $\int \cos(u),du = \sin(u) + C = \sin(x^2) + C$.
Integration by Parts is the product rule in reverse: $$\int u,dv = uv - \int v,du$$ Choose $u$ and $dv$ such that $\int v,du$ is easier. The mnemonic LIATE helps choose $u$ (Logarithm, Inverse trig, Algebraic, Trigonometric, Exponential — pick the first applicable type).
Example: $\int x e^x,dx$. Let $u = x$ (algebraic, so it will simplify: $du = dx$), $dv = e^x dx$ ($v = e^x$). Then $\int xe^x,dx = xe^x - \int e^x,dx = xe^x - e^x + C = e^x(x-1) + C$.
Important integrals to know for CS/ML:
- $\int e^{-x^2},dx$ has no elementary closed form — it defines $\text{erf}(x)$, crucial for the Gaussian distribution
- $\int_0^\infty x^{n-1} e^{-x},dx = \Gamma(n)$ — the Gamma function, the continuous extension of factorial
- $\int_0^1 x^{a-1}(1-x)^{b-1},dx = B(a,b)$ — the Beta function, the normalization of the Beta distribution (conjugate prior for Bernoulli trials)
Symbolic integration in software (SymPy, Mathematica, WolframAlpha) automates these techniques. Knowing them helps you verify, recognize patterns, and understand what the computer is doing.
# SymPy: symbolic integration demonstrating substitution and IBP
from sympy import *
x = symbols('x')
# U-substitution: int 2x*cos(x^2) dx
expr1 = 2*x * cos(x**2)
print("int 2x*cos(x^2) dx =", integrate(expr1, x))
# Integration by parts: int x*exp(x) dx
expr2 = x * exp(x)
print("int x*exp(x) dx =", integrate(expr2, x))
# Definite integral: Gaussian normalization int_{-inf}^{inf} exp(-x^2) dx
expr3 = exp(-x**2)
result = integrate(expr3, (x, -oo, oo))
print("int_{-inf}^{inf} exp(-x^2) dx =", result) # sqrt(pi)
09Calculus in Machine Learning: Gradients and Backpropagation
This concept ties everything together in the context most CS students will use calculus most intensively: training neural networks.
Loss function and gradient. Given a neural network $\hat{y} = f(x; \theta)$ parameterized by weights $\theta$, and a loss $L(\hat{y}, y)$ measuring error, we want to minimize $L$ over $\theta$. The gradient $\nabla_\theta L$ is the vector of all partial derivatives — the generalization of $f'$ to many variables. It points in the direction of steepest increase; we step in the opposite direction.
Forward pass and reverse-mode AD. In a neural network with layers $z_1 = f_1(x)$, $z_2 = f_2(z_1)$, ..., $\hat{y} = f_n(z_{n-1})$, computing $\nabla_\theta L$ requires the chain rule across all layers: $$\frac{\partial L}{\partial \theta_k} = \frac{\partial L}{\partial \hat{y}} \cdot \frac{\partial \hat{y}}{\partial z_{n-1}} \cdots \frac{\partial z_{k+1}}{\partial z_k} \cdot \frac{\partial z_k}{\partial \theta_k}$$ Backpropagation is the dynamic-programming algorithm that computes all these partial derivatives in one backward pass by reusing intermediate computations. It has $O(n)$ complexity instead of $O(n^2)$ from naive re-computation.
Deriving the sigmoid derivative (a must-know exercise): $$\sigma(x) = \frac{1}{1+e^{-x}}$$ $$\sigma'(x) = \frac{e^{-x}}{(1+e^{-x})^2} = \sigma(x)(1 - \sigma(x))$$ This elegant form means you can compute the derivative from the output alone — no need to store the input during forward pass.
ReLU's derivative is the Heaviside step function: $\text{ReLU}'(x) = 1$ if $x > 0$, $0$ if $x < 0$ (undefined at 0, handled by convention). The clean derivative is why ReLU almost entirely replaced sigmoid for hidden-layer activations.
Integrals in ML. Expected values, KL divergence, entropy — all integrals (or sums for discrete distributions). The reparameterization trick in VAEs converts a non-differentiable sampling step into a differentiable one by pushing randomness into a parameter-free noise variable, preserving the gradient flow.
import math
def sigmoid(x):
return 1 / (1 + math.exp(-x))
def sigmoid_deriv(x):
s = sigmoid(x)
return s * (1 - s) # chain rule + quotient rule combined
def relu(x):
return max(0.0, x)
def relu_deriv(x):
return 1.0 if x > 0 else 0.0
# Mini backprop through 2-layer net: y = sigmoid(w2 * relu(w1 * x))
# dL/dw1 via chain rule (L = (y - y_true)^2 / 2)
def forward_backward(x, w1, w2, y_true):
# Forward
z1 = w1 * x
a1 = relu(z1)
z2 = w2 * a1
y_hat = sigmoid(z2)
loss = 0.5 * (y_hat - y_true) ** 2
# Backward (chain rule)
dL_dy = y_hat - y_true
dL_dz2 = dL_dy * sigmoid_deriv(z2)
dL_dw2 = dL_dz2 * a1
dL_da1 = dL_dz2 * w2
dL_dz1 = dL_da1 * relu_deriv(z1)
dL_dw1 = dL_dz1 * x
return loss, dL_dw1, dL_dw2
loss, g1, g2 = forward_backward(x=1.0, w1=0.5, w2=0.8, y_true=1.0)
print(f"Loss={loss:.4f} dL/dw1={g1:.6f} dL/dw2={g2:.6f}")
Key insights
- ✦The derivative and the integral are inverse operations (FTC) — this unification is one of the deepest theorems in mathematics, connecting slope (local) to area (global).
- ✦The chain rule is not just a calculus trick — it is the mathematical foundation of backpropagation, automatic differentiation, and every gradient-based learning algorithm.
- ✦Limits are the rigorous foundation beneath all of calculus — without epsilon-delta, 'infinitely small' is just hand-waving; with it, we can prove numerical algorithms converge.
- ✦Most integrals have no closed form — e^{-x^2} is the canonical example. Numerical integration and special functions (erf, Gamma, Beta) are how CS deals with this.
- ✦Taylor's theorem turns any smooth function into an approximating polynomial — this is how computers evaluate sin, cos, exp, and why linear approximation (gradient) works in optimization.
- ✦Vanishing and exploding gradients in deep learning are direct consequences of the chain rule applied across many layers — understanding calculus lets you understand why ResNets and LSTMs were designed the way they were.
- ✦Continuity and differentiability are distinct: |x| is continuous but not differentiable at 0. ReLU exploits this: it is non-differentiable at exactly one point, which is fine in practice.
- ✦Numerical differentiation is O(h^2) error with centered differences — understanding why requires Taylor's theorem. This is why automatic differentiation (exact) is preferred over finite differences in ML.
Common pitfalls
- ⚠Confusing the limit existing with the function being defined at that point — the whole power of limits is reasoning about what happens *near* a point, not *at* it.
- ⚠Applying the power rule to e^x: the power rule gives nx^{n-1} for x^n, but e^x has the variable in the exponent. The derivative of e^x is e^x, a completely different rule.
- ⚠Forgetting the chain rule when differentiating composed functions — writing d/dx[sin(x^2)] = cos(x^2) instead of cos(x^2) * 2x is one of the most common errors.
- ⚠Treating antiderivatives as unique — antiderivatives differ by an arbitrary constant C; forgetting the +C in indefinite integrals is wrong and matters when solving initial-value problems.
- ⚠Using a step size h that is too small in numerical differentiation — floating-point cancellation error dominates when h < ~1e-8, making the approximation worse, not better.
- ⚠Interpreting gradient descent convergence as finding a global minimum — for non-convex loss functions (all deep networks), gradient descent finds a local minimum or saddle point.
- ⚠Confusing the definite integral (a number) with the indefinite integral (a family of functions) — they are related by the FTC but are conceptually different objects.
- ⚠Forgetting that 'differentiable implies continuous' but not the converse — this trips up proofs and reasoning about activation functions like ReLU.
Practice
- 01easy
Prove from the epsilon-delta definition that lim_{x->2} (3x + 1) = 7. That is, given epsilon > 0, explicitly construct delta and verify the definition holds.
- 02easy
Derive the derivative of sigmoid(x) = 1/(1 + e^{-x}) from scratch using the quotient rule and chain rule. Then verify your result with a numerical centered-difference approximation in Python for x in {-2, 0, 1, 3}.
- 03medium
Implement Simpson's rule for numerical integration and verify it achieves O(h^4) error. Test on int_0^1 e^x dx = e - 1. Plot log(error) vs log(n) and measure the slope — it should be approximately -4.
- 04medium
Implement Newton's method to find the square root of 2 (solve f(x) = x^2 - 2 = 0, starting at x=1.5). Print the error at each iteration and verify that the number of correct decimal digits roughly doubles each step (quadratic convergence).
- 05hard
Manually implement forward-mode automatic differentiation for a dual-number system. A dual number is (a, b) where b tracks the derivative. Implement +, *, sin, exp for dual numbers, then use them to compute the derivative of f(x) = sin(x^2 + exp(x)) at x = 1.0. Verify against numerical differentiation.
- 06hard
Derive the formula for the expected value E[X] = integral_{-inf}^{inf} x * p(x) dx for an Exponential(lambda) distribution with p(x) = lambda*e^{-lambda*x} for x >= 0. Use integration by parts. Then verify numerically by sampling 10^6 values and computing the sample mean.
Resources
The canonical free MIT calculus course — full lecture notes, problem sets, and exams. Covers everything in this module rigorously.
Extraordinary visual intuition for every core concept. Watch before or alongside the formal treatment.
If you want the most rigorous self-contained treatment, this is it.
A 1910 classic that is genuinely the most intuitive introduction ever written. Free on Project Gutenberg. Read for the intuition, supplement with a rigorous text.
Bridges single-variable calculus to the multivariable matrix calculus actually used in ML. Essential companion once you have the basics.
Comprehensive with many worked examples. The 'Early Transcendentals' edition is most common.
Excellent free online notes with worked examples covering limits, derivatives, integrals at the right level for this module.
Stanford's deep learning course notes on backprop — the direct application of the chain rule to neural networks, with excellent diagrams.
Checkpoints — you can now…
- ✓You can state the epsilon-delta definition of a limit, and use it to prove a simple limit like lim_{x->3}(2x+1) = 7.
- ✓You can identify whether a function is continuous at a point and classify discontinuities as removable, jump, or infinite.
- ✓You can differentiate any combination of polynomials, exponentials, trig functions, and their compositions using the power rule, chain rule, product rule, and quotient rule — and verify numerically.
- ✓You can derive the sigmoid derivative sigma*(1-sigma) from scratch using the chain and quotient rules.
- ✓You can explain the Fundamental Theorem of Calculus and use it to evaluate definite integrals analytically (e.g., int_0^1 x^2 dx = 1/3).
- ✓You can implement the trapezoid rule or Simpson's rule in code and verify the expected convergence rate experimentally.
- ✓You can implement Newton's method for root-finding and explain why it converges quadratically based on Taylor's theorem.
- ✓You can trace the chain rule through a simple 2-layer neural network forward-backward pass and compute the gradient of the loss with respect to each weight.