Skip to content
The Lumenary

Math laboratory

Polar coordinates and the Jacobian factor r

Integrates exp(-(x^2 + y^2)) over discs by a Cartesian midpoint grid, by polar coordinates with the Jacobian factor r, and by polar coordinates without it, against the exact value pi (1 - exp(-R^2)). Forgetting r gives wrong answers. It also checks numerically that the one-dimensional Gaussian integral is close to sqrt(pi).

Back to the labNumerical approximation
Polar coordinates and the Jacobian factor rPythonNumerical approximationComputationally tested
"""Change of variables: why the Jacobian factor r matters.

We integrate e^(-(x^2 + y^2)) over discs of radius R three ways: on a
Cartesian grid (the honest but slow way), in polar coordinates with the
Jacobian factor r, and in polar coordinates *forgetting* r. The first two
agree with the exact value pi (1 - e^(-R^2)); the third does not. Letting R
grow gives pi, so (integral of e^(-x^2) over the line)^2 = pi - the Gaussian
integral.

Runs unchanged in the browser (Pyodide) using only the standard library.
"""

import math

def gauss2(x, y):
    """The two-dimensional Gaussian exp(-(x^2 + y^2))."""
    return math.exp(-(x * x + y * y))

def cartesian_disc(f, radius, n=400):
    """Midpoint rule on an n x n grid over [-R, R]^2, keeping cells in the disc."""
    h = 2 * radius / n
    total = 0.0
    for i in range(n):
        x = -radius + (i + 0.5) * h
        for j in range(n):
            y = -radius + (j + 0.5) * h
            if x * x + y * y <= radius * radius:
                total += f(x, y)
    return total * h * h

def polar_disc(f, radius, n_r=400, n_t=64, jacobian=True):
    """Midpoint rule in (r, theta); include the factor r unless jacobian=False."""
    hr, ht = radius / n_r, 2 * math.pi / n_t
    total = 0.0
    for i in range(n_r):
        r = (i + 0.5) * hr
        weight = r if jacobian else 1.0
        for j in range(n_t):
            t = (j + 0.5) * ht
            total += f(r * math.cos(t), r * math.sin(t)) * weight
    return total * hr * ht

def exact_disc(radius):
    """Exact integral of exp(-(x^2 + y^2)) over the disc: pi (1 - e^(-R^2))."""
    return math.pi * (1 - math.exp(-radius * radius))

def gaussian_line(limit=8.0, n=4000):
    """Midpoint-rule approximation of the integral of e^(-x^2) over [-L, L]."""
    h = 2 * limit / n
    return h * sum(math.exp(-((-limit + (i + 0.5) * h) ** 2)) for i in range(n))

def main():
    print("Integral of exp(-(x^2 + y^2)) over the disc of radius R")
    print("  R    exact      Cartesian   polar with r   polar without r")
    for radius in [0.5, 1.0, 2.0, 3.0]:
        exact = exact_disc(radius)
        cart = cartesian_disc(gauss2, radius, n=200)
        good = polar_disc(gauss2, radius)
        bad = polar_disc(gauss2, radius, jacobian=False)
        print(f"  {radius:<4} {exact:.6f}   {cart:.6f}    {good:.6f}       {bad:.6f}")
    print("The Cartesian grid has a jagged edge along the circle, which limits")
    print("its accuracy; polar coordinates with r match the exact value.")
    print("Without r the 'area element' dr dtheta is wrong: it overweights the")
    print("centre, where small r squeezes the polar rectangles together.")
    print()
    big = exact_disc(6.0)
    line = gaussian_line()
    print(f"As R grows the disc integral tends to pi: R = 6 gives {big:.12f}")
    print(f"Integral of e^(-x^2) over [-8, 8] (midpoint rule) = {line:.12f}")
    print(f"sqrt(pi)                                       = {math.sqrt(math.pi):.12f}")
    print()
    print("Epistemic status: numerical quadrature for a few radii - evidence that")
    print("dx dy = r dr dtheta, not a proof. The change of variables theorem and")
    print("the exact Gaussian integral sqrt(pi) are proved in the lesson (and in")
    print("Mathlib, cited in the lesson's Lean file).")

if __name__ == "__main__":
    main()
Runs entirely in your browser, in an isolated worker. Nothing is sent to a server.

Used in