Mathematics as an experimental science
Around 1731 Euler computed to many decimal places — no easy task, since the series converges painfully slowly — and obtained . He recognised the number as , announced the result in 1735 with a daring argument treating as an infinite polynomial, and spent years making it rigorous. As a teenager in the 1790s Gauss counted primes in tables and conjectured that their density near is about ; the prime number theorem was proved a century later. Birch and Swinnerton-Dyer's famous conjecture came out of computations on Cambridge's EDSAC computer in the early 1960s.
Computation lets mathematicians see patterns, guess closed forms, and refute conjectures with a single counterexample. It also misleads: primes lead primes for every below , and the inequality holds for every ever checked — yet both patterns fail. This lesson is about using computation as a laboratory: generating conjectures, designing searches with honest bounds, detecting integer relations at high precision, and judging computer-assisted proofs.
The experimental cycle
Experimental mathematics follows a cycle:
- Explore. Compute many cases, plot, tabulate; look sequences up in the On-Line Encyclopedia of Integer Sequences (OEIS).
- Conjecture. Formulate a precise statement that fits the data.
- Test. Try hard to break it: extend the range, look at extreme cases, compute to higher precision. A conjecture that survives honest attempts at refutation earns credibility — but not truth.
- Prove or refute. A counterexample settles the question at once; a proof settles it for ever. Often the experiment also suggests the proof, by revealing the structure behind the pattern.
Different statements interact with computation differently. A statement "for all , " with decidable — such as Goldbach's conjecture — can be refuted by a finite computation but never confirmed by one. A closed form such as "this constant equals " can be tested to a thousand digits, but agreement to a thousand digits is still not a proof. And an existence statement can be settled by exhibiting an object, which the computer can then certify.
The lesson assumes high-precision and floating-point computation from numerical methods and search techniques such as meet-in-the-middle from algorithm design; for how to package and report such computations, see computational reproducibility.
Definitions
Definition— integer relation
An integer relation among real numbers is a non-zero vector with . For instance is an integer relation for , where is the golden ratio. A number is algebraic of degree at most exactly when has an integer relation.
Definition— exhaustive search up to a bound
A counterexample search for a statement is exhaustive up to the bound if it checks every case with (or every case in an explicitly described finite set). Its report must state the bound, the exact set searched, and how completeness of the search was ensured.
Definition— computer-assisted proof
A computer-assisted proof is a proof in which some steps are carried out by computation — typically a reduction, proved by hand, of the theorem to finitely many cases, followed by a computer check of those cases. Its reliability depends on the reduction, the code, and the computing environment.
In this lesson a conjecture is a precise statement that has been proposed but neither proved nor refuted, and numerical evidence for an identity means agreement to a stated number of digits.
Patterns that failed, and one that did not
Example— Fermat numbers
Fermat (1640) observed that is prime for — namely — and conjectured that all are prime. Euler (1732) found . No further Fermat prime is known: are all known to be composite. Five confirming cases did not make a theorem, and the opposite conjecture — that only finitely many are prime — is also open.
Example— Euler's sum-of-powers conjecture
Euler (1769) conjectured that a -th power is never the sum of fewer than positive -th powers (true for , by Fermat's last theorem for cubes). In 1966 Lander and Parkin published, in a two-sentence paper, a counterexample found by a computer search:
For fourth powers Elkies (1988) found using elliptic curves, and Frye then found the smallest example, , by a search guided by Elkies's analysis. The first experiment below repeats Lander and Parkin's search exhaustively for .
Example— A prime race
Let be the number of primes with . For every we have , and the lead of the "3" team looks permanent. At the "1" team goes ahead (Leech, 1957) — for two values of ; it leads again from . Littlewood (1914) proved that the lead changes infinitely often.
Example— π(x) and li(x)
The logarithmic integral (a principal value) is an excellent approximation to the prime-counting function , and for every that has ever been checked — at least up to (Kotnik, 2008). For a long time it was widely believed always to hold. Littlewood (1914) proved that changes sign infinitely often; the first sign change is known to occur below about (Bays and Hudson, 2000, with later refinements), far beyond direct computation.
Example— Borwein integrals
For ,
and the pattern seems certain. For the integral is approximately — exactly . The reason (D. and J. Borwein, 2001) is that the value is precisely while , and that sum first exceeds at . A numerical integrator working to eight digits would have "confirmed" the pattern.
Example— A closed form found by computer
In 1995 Simon Plouffe, using David Bailey's implementation of the PSLQ algorithm, searched for an integer relation among and the eight constants , , computed to high precision. The relation found,
is the Bailey–Borwein–Plouffe (BBP) formula (published 1997). It was then proved by elementary calculus (Theorem 2), and it has a striking consequence: hexadecimal digits of can be computed far after the point without computing the earlier ones (Theorem 3).
Proofs behind the experiments
Theorem— Theorem 1 (Euler, 1732)
divides .
Proof
Note . Modulo , the first form gives , hence ; the second gives . Therefore , i.e. .
∎
Euler did not stumble on : he had shown that every prime factor of has the form (later sharpened to by Lucas), so for only primes need testing — , , , , — a small search guided by theory.
Theorem— Theorem 2 (Bailey, Borwein and Plouffe, 1997)
Proof
For , expand as a geometric series, which converges uniformly on (there ), and integrate term by term:
Hence
Substitute . The integrand becomes , and factoring and gives
by partial fractions. On an antiderivative is , because . So the integral is .
∎
Theorem— Theorem 3 (BBP digit extraction)
Let denote the fractional part of , and . Then
so the hexadecimal digits of in positions can be computed with operations on numbers of bits, without computing the first digits.
Proof
Multiply the BBP formula by ; fractional parts are additive modulo integers, which gives the first identity. In , the terms with are , whose integer part does not affect the fractional part, so may be reduced modulo ; the remaining terms form a rapidly convergent tail. Each modular power costs multiplications by binary exponentiation, and there are of them.
∎
In practice the sums are carried in floating point, so only the first several digits after position are reliable, and the computation below checks them against an independent exact calculation. No analogous formula is known for the decimal digits of .
How are such relations found? An integer relation algorithm takes approximations of and either returns a candidate relation or certifies that none exists with small coefficients.
Theorem— Theorem 4 (integer relation detection; Ferguson, Bailey and Arno 1999) — stated, not proved here
Let . The PSLQ algorithm, run in exact real arithmetic, maintains a lower-trapezoidal matrix with the property that every integer relation for satisfies . If has an integer relation, PSLQ finds one after a number of iterations bounded by a polynomial in and , where is a shortest relation.
Integer relations can also be found with the LLL lattice-basis reduction algorithm (Lenstra, Lenstra and Lovász, 1982), as shown in polynomial time by Håstad, Just, Lagarias and Schnorr (1989). In real life the input is only known to finite precision, so two warnings apply. A "relation" with coefficients of size among numbers known to digits is meaningful only if comfortably exceeds : otherwise PSLQ finds spurious relations that disappear at higher precision. And the lower bound for relations is trustworthy only if the working precision was sufficient. A relation that persists when the precision is doubled is strong evidence — and the signal to look for a proof.
Theorem— Theorem 5 (Littlewood 1914) — stated, not proved here
Both and change sign infinitely often as .
The proof uses the zeros of the Riemann zeta function and of the Dirichlet -function for the character modulo (Ingham, 1932, chapter V). Rubinstein and Sarnak (1994) explained why the data mislead: assuming the generalised Riemann hypothesis and a linear-independence hypothesis on the zeros, the set of with has logarithmic density about — the "3" team leads almost always, but not always. Note the three levels in that sentence: a theorem (Littlewood), a conditional theorem (Rubinstein–Sarnak), and numerical evidence (the race up to below).
Assessing computer-assisted proofs
A computer-assisted proof is as good as three things: the mathematical reduction to finitely many cases, the correctness of the computation, and the environment it ran in. The great examples show the ways in which each has been secured.
- The four colour theorem (Appel and Haken, 1976). A hand-proved reduction showed that it suffices to check that each configuration in an unavoidable set of 1,936 is reducible, which a computer did in about 1,200 hours. Doubts about the unrefereeable computation (and some errors in the hand part, later corrected) led Robertson, Sanders, Seymour and Thomas (1997) to a new proof with 633 configurations and new, independently written programs; Gonthier (2005) then checked a complete proof in the Coq proof assistant — reduction and computation together.
- The Kepler conjecture (Hales, with Ferguson, 1998). The densest packing of equal balls in space has density . The proof reduced the problem to several thousand configurations, described by planar graphs, and to many nonlinear inequalities, verified with interval arithmetic and linear programming. The referees reported that they were "99% certain" of its correctness but could not check every computation; the paper appeared in the Annals of Mathematics in 2005. Hales then led the Flyspeck project, which completed a formal proof in HOL Light and Isabelle in 2014.
- Non-existence of a projective plane of order 10 (Lam, Thiel and Swiercz, 1989) used thousands of hours on a Cray supercomputer; it has since been re-verified independently, including with SAT solvers that produce checkable certificates.
- SAT-solver proofs such as the Boolean Pythagorean triples theorem (Heule, Kullmann and Marek, 2016: can be split into two classes with no Pythagorean triple inside a class, but cannot) and Keller's conjecture in dimension 7 (Brakensiek, Heule, Mackey and Narváez, 2020) produce proof certificates — for unsatisfiability, a DRAT proof — that are checked by independent, sometimes formally verified, checkers.
- Validated numerics turns floating-point computation into proof by computing with intervals and outward rounding: Tucker's 2002 proof that the Lorenz equations have a strange attractor, and the numerical parts of Helfgott's 2013 proof of the ternary Goldbach conjecture (every odd number greater than is a sum of three primes), whose small cases were checked by a separate computation with Platt.
Questions to ask of any computer-assisted proof: Is the reduction to finitely many cases proved, and where? Is the code available, and has it been independently reimplemented? Does the computation emit certificates that a simpler program can check? Were floating-point computations made rigorous? Has any part been formally verified? Compare computational reproducibility and formalising published mathematics.
Experiments and their proofs in Lean
Fermat's evidence, Euler's refutation by his own argument (no is computed), and the conclusion:
/-- The Fermat numbers `Fₙ = 2^(2^n) + 1`: 3, 5, 17, 257, 65537, … -/
def fermat (n : ℕ) : ℕ := 2 ^ (2 ^ n) + 1/-- The evidence Fermat had: `F₀, …, F₄` are all prime (checked here by `norm_num`'s
certified primality test). -/
theorem fermat_prime_le_four (n : ℕ) (hn : n ≤ 4) : (fermat n).Prime := by
interval_cases n <;> norm_num [fermat]/-- **Euler (1732): `641 ∣ 2³² + 1`**, by Euler's argument rather than by computing `2³²`.
Since `641 = 5·2⁷ + 1 = 5⁴ + 2⁴`, modulo 641 we have `5·2⁷ = -1` and `5⁴ = -2⁴`. Hence
`2³² = 2⁴·2²⁸ = -5⁴·2²⁸ = -(5·2⁷)⁴ = -1`. -/
theorem euler_641 : (2 : ZMod 641) ^ 32 + 1 = 0 := by
have h1 : (5 : ZMod 641) * 2 ^ 7 = -1 := by decide
have h2 : (5 : ZMod 641) ^ 4 = -(2 ^ 4) := by decide
calc (2 : ZMod 641) ^ 32 + 1 = -(5 ^ 4) * 2 ^ 28 + 1 := by rw [h2]; ring
_ = -((5 * 2 ^ 7) ^ 4) + 1 := by ring
_ = 0 := by rw [h1]; ring/-- **Fermat's conjecture is false**: not every Fermat number is prime, because
`F₅ = 641 · 6700417`. Five cases of evidence did not make a theorem. -/
theorem fermat_conjecture_false : ¬ ∀ n, (fermat n).Prime := by
intro h
have h5 : fermat 5 = 641 * 6700417 := by norm_num [fermat]
have := h 5
rw [h5] at this
exact Nat.not_prime_mul (by norm_num) (by norm_num) thisEuler's sum-of-powers conjecture is defined as a proposition, and refuted by the counterexample:
/-- Euler's sum-of-powers conjecture for fifth powers (1769): no fifth power is a sum of four
positive fifth powers. Defined as a proposition — stating it is not asserting it. -/
def EulerConjectureFive : Prop :=
∀ a b c d e : ℕ, 0 < a → 0 < b → 0 < c → 0 < d → a ^ 5 + b ^ 5 + c ^ 5 + d ^ 5 ≠ e ^ 5/-- **Lander and Parkin (1966)**, found by a computer search on a CDC 6600:
`27⁵ + 84⁵ + 110⁵ + 133⁵ = 144⁵`. -/
theorem lander_parkin : 27 ^ 5 + 84 ^ 5 + 110 ^ 5 + 133 ^ 5 = 144 ^ 5 := by norm_num/-- Hence Euler's conjecture for fifth powers is false. One counterexample suffices. -/
theorem euler_conjecture_five_false : ¬ EulerConjectureFive := fun h =>
h 27 84 110 133 144 (by norm_num) (by norm_num) (by norm_num) (by norm_num) lander_parkinInteger relations: a relation that exists, and a proof that none exists for — the kind of statement no search can establish:
/-- An **integer relation** for real numbers `x₀, …, xₙ₋₁` is a non-zero integer vector `a`
with `a₀x₀ + ⋯ + aₙ₋₁xₙ₋₁ = 0`. Algorithms such as PSLQ search for them numerically. -/
def IsIntegerRelation {n : ℕ} (x : Fin n → ℝ) (a : Fin n → ℤ) : Prop :=
a ≠ 0 ∧ ∑ i, (a i : ℝ) * x i = 0/-- `(1, -1, -1)` is an integer relation for `(φ², φ, 1)`: the golden ratio satisfies
`φ² - φ - 1 = 0` (Mathlib's `goldenRatio_sq`). -/
theorem golden_relation :
IsIntegerRelation ![goldenRatio ^ 2, goldenRatio, 1] ![1, -1, -1] := by
refine ⟨fun h => by simpa using congrFun h 0, ?_⟩
simp [Fin.sum_univ_three, goldenRatio_sq]/-- **No integer relation exists for `(√2, 1)`.** If `a₀√2 + a₁ = 0` with `a₀ ≠ 0` then `√2` would
be rational; if `a₀ = 0` then `a₁ = 0` too. Absence of relations is an irrationality statement:
a search can only rule out relations up to a size bound, while this needs a proof
(Mathlib's `irrational_sqrt_two`). -/
theorem no_relation_sqrt_two (a : Fin 2 → ℤ) : ¬ IsIntegerRelation ![√2, 1] a := by
rintro ⟨hne, hsum⟩
simp only [Fin.sum_univ_two, Matrix.cons_val_zero, Matrix.cons_val_one, mul_one] at hsum
by_cases h0 : a 0 = 0
· have h1 : a 1 = 0 := by
rw [h0] at hsum
simpa using hsum
exact hne (funext fun i => by fin_cases i <;> simp [h0, h1])
· have hq : √2 = ((-(a 1) / a 0 : ℚ) : ℝ) := by
have ha0 : (a 0 : ℝ) ≠ 0 := by exact_mod_cast h0
push_cast
field_simp
linarith
exact irrational_sqrt_two ⟨_, hq.symm⟩The analytic heart of Theorem 2 — partial fractions and the integral equal to , proved with the fundamental theorem of calculus exactly as above:
/-- The integrand to which the BBP series reduces after writing `1/(8k+j)` as an integral,
summing the geometric series and substituting `y = √2 x`. -/
noncomputable def bbpIntegrand (y : ℝ) : ℝ := (16 * y - 16) / (y ^ 4 - 2 * y ^ 3 + 4 * y - 4)/-- Partial fractions: since `y⁴ - 2y³ + 4y - 4 = (y² - 2)(y² - 2y + 2)`,
`(16y - 16)/(y⁴ - 2y³ + 4y - 4) = 4y/(y² - 2) - (4y - 8)/(y² - 2y + 2)` whenever `y² ≠ 2`. -/
theorem bbp_partial_fractions (y : ℝ) (hy : y ^ 2 ≠ 2) :
bbpIntegrand y = 4 * y / (y ^ 2 - 2) - (4 * y - 8) / (y ^ 2 - 2 * y + 2) := by
have h1 : y ^ 2 - 2 ≠ 0 := sub_ne_zero.2 hy
have h2 : y ^ 2 - 2 * y + 2 ≠ 0 := by nlinarith [sq_nonneg (y - 1)]
have h3 : y ^ 4 - 2 * y ^ 3 + 4 * y - 4 = (y ^ 2 - 2) * (y ^ 2 - 2 * y + 2) := by ring
rw [bbpIntegrand, h3, div_sub_div _ _ h1 h2]
congr 1
ring/-- **`∫₀¹ (16y - 16)/(y⁴ - 2y³ + 4y - 4) dy = π`.** An antiderivative on `[0, 1]` is
`F(y) = 2 log(2 - y²) - 2 log(y² - 2y + 2) + 4 arctan(y - 1)`; its derivative is the integrand
by `bbp_partial_fractions`, and `F(1) - F(0) = 0 - 4 arctan(-1) = π`. Together with the
(unformalised) conversion of the series into this integral, this proves the BBP formula
`π = Σₖ 16⁻ᵏ (4/(8k+1) - 2/(8k+4) - 1/(8k+5) - 1/(8k+6))`. -/
theorem bbp_integral : ∫ y in (0 : ℝ)..1, bbpIntegrand y = π := by
set F : ℝ → ℝ := fun y => 2 * log (2 - y ^ 2) - 2 * log (y ^ 2 - 2 * y + 2)
+ 4 * arctan (y - 1) with hF
have hderiv : ∀ y ∈ Set.uIcc (0 : ℝ) 1, HasDerivAt F (bbpIntegrand y) y := by
intro y hy
rw [Set.uIcc_of_le zero_le_one] at hy
obtain ⟨hy0, hy1⟩ := hy
have hA : 0 < 2 - y ^ 2 := by nlinarith
have hB : 0 < y ^ 2 - 2 * y + 2 := by nlinarith [sq_nonneg (y - 1)]
have dA : HasDerivAt (fun y : ℝ => 2 - y ^ 2) (-(2 * y)) y := by
simpa using (hasDerivAt_pow 2 y).const_sub (2 : ℝ)
have dB : HasDerivAt (fun y : ℝ => y ^ 2 - 2 * y + 2) (2 * y - 2) y := by
have := ((hasDerivAt_pow 2 y).sub ((hasDerivAt_id y).const_mul 2)).add_const (2 : ℝ)
simpa using this
have dC : HasDerivAt (fun y : ℝ => arctan (y - 1)) (1 / (1 + (y - 1) ^ 2) * 1) y :=
((hasDerivAt_id y).sub_const 1).arctan
have dF := (((dA.log hA.ne').const_mul 2).sub ((dB.log hB.ne').const_mul 2)).add
(dC.const_mul 4)
convert dF using 1
rw [bbp_partial_fractions y (by nlinarith)]
have hC : 1 + (y - 1) ^ 2 = y ^ 2 - 2 * y + 2 := by ring
have hA' : 2 - y ^ 2 ≠ 0 := hA.ne'
have hA'' : y ^ 2 - 2 ≠ 0 := by linarith
have hB' : y ^ 2 - 2 * y + 2 ≠ 0 := hB.ne'
rw [hC]
field_simp
ring
have hcont : ContinuousOn bbpIntegrand (Set.uIcc (0 : ℝ) 1) := by
apply ContinuousOn.div (by fun_prop) (by fun_prop)
intro y hy
rw [Set.uIcc_of_le zero_le_one] at hy
obtain ⟨hy0, hy1⟩ := hy
have h3 : y ^ 4 - 2 * y ^ 3 + 4 * y - 4 = (y ^ 2 - 2) * (y ^ 2 - 2 * y + 2) := by ring
rw [h3]
apply mul_ne_zero <;> nlinarith [sq_nonneg (y - 1)]
rw [intervalIntegral.integral_eq_sub_of_hasDerivAt hderiv hcont.intervalIntegrable]
simp only [hF]
norm_num [arctan_neg, arctan_one]
ringEuler's numerically discovered sum, from Mathlib:
/-- **The Basel problem (Euler, 1734/35):** `Σ_{n ≥ 1} 1/n² = π²/6`. Euler first computed the sum
to several decimals with acceleration techniques and recognised `π²/6`; this applies Mathlib's
`hasSum_zeta_two`. (The `n = 0` term is `1/0 = 0` in Lean's conventions.) -/
theorem basel : HasSum (fun n : ℕ => 1 / (n : ℝ) ^ 2) (π ^ 2 / 6) := hasSum_zeta_twoFinally the difference between a bounded check and a conjecture, made precise. The Collatz conjecture is stated as a definition; a bounded search is proved sound; and the search, run by Lean's kernel, proves the conjecture for — and nothing more:
/-- One step of the Collatz map: halve even numbers, send odd `n` to `3n + 1`. -/
def collatzStep (n : ℕ) : ℕ := if n % 2 = 0 then n / 2 else 3 * n + 1/-- The **Collatz conjecture**: every positive integer reaches `1`. Stated, not asserted — it is
open. -/
def CollatzConjecture : Prop := ∀ n, 0 < n → ∃ k, collatzStep^[k] n = 1/-- A bounded search: does `n` reach `1` within `fuel` steps? -/
def reachesOne : ℕ → ℕ → Bool
| 0, n => n == 1
| fuel + 1, n => n == 1 || reachesOne fuel (collatzStep n)/-- The search is sound: if it says yes, `n` really reaches `1`. Induction on the fuel. -/
theorem reachesOne_sound (fuel n : ℕ) (h : reachesOne fuel n = true) :
∃ k, collatzStep^[k] n = 1 := by
induction fuel generalizing n with
| zero => exact ⟨0, by simpa [reachesOne] using h⟩
| succ fuel ih =>
simp only [reachesOne, Bool.or_eq_true, beq_iff_eq] at h
rcases h with h | h
· exact ⟨0, h⟩
· obtain ⟨k, hk⟩ := ih _ h
exact ⟨k + 1, by rw [Function.iterate_succ_apply]; exact hk⟩/-- **An exhaustive check with a stated bound:** every `n` with `1 ≤ n ≤ 500` reaches `1`.
The bounded search is run by the kernel (`decide +kernel`) and turned into a theorem by
`reachesOne_sound`. This says nothing about `n > 500`; `CollatzConjecture` stays open. -/
theorem collatz_up_to_500 (n : ℕ) (h0 : 0 < n) (h : n ≤ 500) :
∃ k, collatzStep^[k] n = 1 := by
have hall : ∀ m < 500, reachesOne 150 (m + 1) = true := by decide +kernel
obtain ⟨m, rfl⟩ : ∃ m, n = m + 1 := ⟨n - 1, by omega⟩
exact reachesOne_sound 150 _ (hall m (by omega))Not formalised: the first step of Theorem 2 (exchanging the series and the integral, and the substitution), so the BBP formula itself is proved here only on paper; Theorems 3–5; and the PSLQ algorithm. Mathlib does not yet contain Littlewood's theorem.
Experiments
The first experiment rediscovers the BBP formula: it computes and to digits with exact rational arithmetic, runs PSLQ, and rechecks the relation at digits. It then shows a spurious relation found at digits, a PSLQ lower bound when no relation is found, and BBP digit extraction checked against an exact computation.
"""Rediscovering the Bailey-Borwein-Plouffe formula, and using it.
The constants S_j = sum_k 16^-k / (8k + j), j = 1..8, and pi are computed to
60 digits with exact rational arithmetic (pi by Machin's formula). The PSLQ
integer-relation algorithm (Ferguson and Bailey), implemented here with
Python's decimal module, finds the relation pi = 4 S_1 - 2 S_4 - S_5 - S_6,
which is then re-checked at 100 digits. Two cautionary runs follow: with only
8 digits PSLQ "finds" a spurious relation for (pi, e, 1), and at 40 digits it
only bounds the size of any relation. Finally the formula is used to compute
hexadecimal digits of pi far after the point without the earlier ones, and
these are compared with an independent exact computation. A found relation is
numerical evidence; the lesson gives the proof.
"""
from decimal import Decimal, localcontext, ROUND_HALF_EVEN
from fractions import Fraction
import math
HEX = "0123456789ABCDEF"
def arctan_inv(x, scale):
"""floor-ish of arctan(1/x) * scale with integer arithmetic (error < terms)."""
total = term = scale // x
x2, k, sign = x * x, 1, -1
while term:
term //= x2
total += sign * (term // (2 * k + 1))
sign, k = -sign, k + 1
return total
def pi_fraction(digits):
"""pi to about `digits` decimal digits (Machin: pi = 16 atan 1/5 - 4 atan 1/239)."""
scale = 10 ** (digits + 10)
return Fraction(4 * (4 * arctan_inv(5, scale) - arctan_inv(239, scale)), scale)
def e_fraction(digits):
"""e to well beyond `digits` digits, as an exact partial sum of 1/k!."""
terms = 10
while math.lgamma(terms + 1) / math.log(10) < digits + 5:
terms += 5
return sum(Fraction(1, math.factorial(k)) for k in range(terms))
def bbp_sum(j, digits):
"""S_j = sum_k 16^-k / (8k + j), truncated with error below 10^-digits."""
terms = int(digits / 1.2) + 5
return sum(Fraction(1, 16 ** k * (8 * k + j)) for k in range(terms))
def pslq(xs, digits, max_norm, max_iter=1000):
"""PSLQ on the reals xs (given as Fractions) at `digits` digits.
Returns (relation or None, iterations, bound): if a relation is found it is
an integer vector; otherwise every relation has norm at least `bound`.
"""
n = len(xs)
with localcontext() as ctx:
ctx.prec = digits + 10
x = [Decimal(f.numerator) / Decimal(f.denominator) for f in xs]
eps = Decimal(10) ** (2 - digits)
gamma = (Decimal(4) / 3).sqrt()
A = [[int(i == j) for j in range(n)] for i in range(n)]
B = [[int(i == j) for j in range(n)] for i in range(n)]
s = [sum((v * v for v in x[k:]), Decimal(0)).sqrt() for k in range(n)]
y = [v / s[0] for v in x]
s = [v / s[0] for v in s]
H = [[Decimal(0)] * (n - 1) for _ in range(n)]
for i in range(n):
if i < n - 1:
H[i][i] = s[i + 1] / s[i]
for j in range(min(i, n - 1)):
H[i][j] = -y[i] * y[j] / (s[j] * s[j + 1])
def reduce(i, j):
t = int((H[i][j] / H[j][j]).to_integral_value(ROUND_HALF_EVEN))
if t:
y[j] += t * y[i]
for k in range(j + 1):
H[i][k] -= t * H[j][k]
for k in range(n):
A[i][k] -= t * A[j][k]
B[k][j] += t * B[k][i]
for i in range(1, n):
for j in range(i - 1, -1, -1):
reduce(i, j)
bound = Decimal(0)
for it in range(1, max_iter + 1):
m = max(range(n - 1), key=lambda i: gamma ** (i + 1) * abs(H[i][i]))
y[m], y[m + 1] = y[m + 1], y[m]
A[m], A[m + 1] = A[m + 1], A[m]
H[m], H[m + 1] = H[m + 1], H[m]
for row in B:
row[m], row[m + 1] = row[m + 1], row[m]
if m < n - 2:
t0 = (H[m][m] ** 2 + H[m][m + 1] ** 2).sqrt()
t1, t2 = H[m][m] / t0, H[m][m + 1] / t0
for i in range(m, n):
t3, t4 = H[i][m], H[i][m + 1]
H[i][m] = t1 * t3 + t2 * t4
H[i][m + 1] = -t2 * t3 + t1 * t4
for i in range(m + 1, n):
for j in range(min(i - 1, m + 1), -1, -1):
reduce(i, j)
jmin = min(range(n), key=lambda j: abs(y[j]))
if abs(y[jmin]) < eps:
rel = [B[k][jmin] for k in range(n)]
sign = 1 if next(c for c in rel if c) > 0 else -1
return [sign * c for c in rel], it, None
bound = 1 / max(abs(H[i][i]) for i in range(n - 1))
if bound > max_norm:
break
return None, it, float(bound)
def format_combination(rel):
"""Write -(rel_1 S_1 + ... + rel_8 S_8) as text, e.g. '4 S_1 - 2 S_4'."""
text = ""
for k, c in enumerate(rel):
coeff = -c
if k == 0 or coeff == 0:
continue
sign = "-" if coeff < 0 else "+"
size = "" if abs(coeff) == 1 else f"{abs(coeff)} "
text += f" {sign} {size}S_{k}"
text = text.strip()
return text[2:] if text.startswith("+ ") else text
def residual(rel, xs):
"""|sum rel_i * x_i| computed exactly from the Fractions xs."""
return abs(sum(c * v for c, v in zip(rel, xs)))
def bbp_hex_digits(d, count):
"""Hex digits of pi at positions d+1, ..., d+count (BBP digit extraction)."""
def frac_series(j):
s = 0.0
for k in range(d + 1):
s = (s + pow(16, d - k, 8 * k + j) / (8 * k + j)) % 1.0
k = d + 1
while True:
term = 16.0 ** (d - k) / (8 * k + j)
if term < 1e-17:
return s
s, k = s + term, k + 1
x = (4 * frac_series(1) - 2 * frac_series(4)
- frac_series(5) - frac_series(6)) % 1.0
out = ""
for _ in range(count):
x *= 16
digit = int(x)
out += HEX[digit]
x -= digit
return out
def exact_hex_digits(d, count):
"""The same digits from an exact integer computation (Machin, base 16)."""
guard = 8
scale = 16 ** (d + count + guard)
pi_scaled = 4 * (4 * arctan_inv(5, scale) - arctan_inv(239, scale))
block = (pi_scaled // 16 ** guard) % 16 ** count
return format(block, "X").rjust(count, "0")
def main():
digits = 60
xs = [pi_fraction(digits)] + [bbp_sum(j, digits) for j in range(1, 9)]
rel, its, _ = pslq(xs, digits, max_norm=10 ** 12)
print("PSLQ on (pi, S_1, ..., S_8) at 60 digits, "
"S_j = sum_k 16^-k/(8k+j):")
print(f" relation after {its} iterations: {tuple(rel)}")
print(f" i.e. pi = {format_combination(rel)}")
xs100 = [pi_fraction(100)] + [bbp_sum(j, 100) for j in range(1, 9)]
print(" residual with 100-digit values: "
f"{float(residual(rel, xs100)):.1e}")
print()
low = [pi_fraction(8), e_fraction(8), Fraction(1)]
fake, _, _ = pslq(low, 8, max_norm=10 ** 6)
high = [pi_fraction(40), e_fraction(40), Fraction(1)]
print(f"(pi, e, 1) at 8 digits: 'relation' {tuple(fake)}, but at 40 digits")
print(f" its residual is {float(residual(fake, high)):.1e}: spurious.")
none, its, bound = pslq(high, 40, max_norm=10 ** 10)
print(f"(pi, e, 1) at 40 digits: no relation; after {its} iterations every")
print(f" integer relation would need norm > {bound:.1e}.")
print()
d = 5000
fast, exact = bbp_hex_digits(d, 8), exact_hex_digits(d, 8)
print(f"Hex digits of pi after the point: {bbp_hex_digits(0, 8)} ...")
print(f"Positions {d + 1}-{d + 8} by BBP digit extraction: {fast}")
print(f"Same positions by exact integer arithmetic: {exact}")
print()
print("Epistemic status: the relation is numerical evidence (60 digits, with")
print("the residual rechecked at 100); its proof is in the lesson. The 'no")
print("relation' bound is PSLQ's guarantee only if 40 digits were enough;")
print("whether pi + e is irrational is an open problem. The hex digits agree")
print("between two independent methods: an exact check of those 8 digits.")
if __name__ == "__main__":
main()The second experiment runs four searches with stated bounds: Euler's conjecture for fifth powers (exhaustive for , by a meet-in-the-middle search over sums of two fifth powers), the prime race modulo , against , and the exact values of the Borwein integrals.
"""Patterns that fail: four searches with stated bounds.
1. Euler's sum-of-powers conjecture for fifth powers: an exhaustive search
over all a <= b <= c <= d < e <= 150 (meet in the middle: sums of two fifth
powers, looked up with NumPy) finds Lander and Parkin's counterexample.
2. The prime race modulo 4: primes 3 mod 4 lead primes 1 mod 4 for every x
below 26861, then fall behind (Leech, 1957).
3. pi(x) < li(x) for every real x in [2, 10^6] (and far beyond), although
Littlewood proved that the inequality reverses infinitely often.
4. The Borwein integrals: an exact rational computation shows when the
pattern "the integral equals pi/2" breaks, and by how little.
Searches 1-3 are exhaustive up to the stated bounds; li(x) is computed in
floating point with a convergent series.
"""
import math
from fractions import Fraction
import numpy as np
EULER_GAMMA = 0.5772156649015329
def euler_fifth_power_search(limit):
"""All (a, b, c, d, e) with a<=b<=c<=d<e<=limit and a^5+b^5+c^5+d^5 = e^5."""
fifth = np.arange(limit + 1, dtype=np.int64) ** 5
a, b = np.triu_indices(limit + 1, k=0)
keep = (a >= 1)
a, b = a[keep], b[keep]
sums = fifth[a] + fifth[b]
order = np.argsort(sums, kind="stable")
sums, a, b = sums[order], a[order], b[order]
found = set()
for e in range(2, limit + 1):
target = fifth[e] - sums # e^5 - (c^5 + d^5)
ok = (target > 0)
idx = np.searchsorted(sums, target[ok])
idx = np.minimum(idx, len(sums) - 1)
hits = np.nonzero(sums[idx] == target[ok])[0]
cd = np.nonzero(ok)[0][hits]
for i, j in zip(idx[hits], cd):
quad = tuple(sorted((int(a[i]), int(b[i]), int(a[j]), int(b[j]))))
if quad[3] < e:
found.add(quad + (e,))
return sorted(found)
def prime_sieve(limit):
"""Boolean array is_prime[0..limit]."""
is_prime = np.ones(limit + 1, dtype=bool)
is_prime[:2] = False
for p in range(2, int(limit ** 0.5) + 1):
if is_prime[p]:
is_prime[p * p::p] = False
return is_prime
def race_mod_4(limit):
"""First x where #primes = 1 mod 4 exceeds #primes = 3 mod 4, and the
number of integers x <= limit at which it does."""
is_prime = prime_sieve(limit)
n = np.arange(limit + 1)
ones = np.cumsum(is_prime & (n % 4 == 1))
threes = np.cumsum(is_prime & (n % 4 == 3))
ahead = np.nonzero(ones > threes)[0]
return int(ahead[0]), int(len(ahead))
def li(x):
"""Logarithmic integral li(x) = gamma + ln ln x + sum (ln x)^k / (k k!)."""
t = math.log(x)
total, term, k = 0.0, 1.0, 1
while True:
term *= t / k
add = term / k
total += add
if add < 1e-17 * total:
break
k += 1
return EULER_GAMMA + math.log(t) + total
def li_array(xs):
"""li for a NumPy array of x > 1, by the same series (vectorised)."""
t = np.log(np.asarray(xs, dtype=float))
total, term = np.zeros_like(t), np.ones_like(t)
for k in range(1, 200):
term = term * t / k
total += term / k
if np.all(term / k < 1e-17 * total):
break
return EULER_GAMMA + np.log(t) + total
def li_exceeds_pi_up_to(limit):
"""Is li(x) > pi(x) for every real 2 <= x <= limit? Since li increases and
pi is constant between primes, it suffices to check x = each prime."""
is_prime = prime_sieve(limit)
primes = np.nonzero(is_prime)[0]
margin = li_array(primes) - np.arange(1, len(primes) + 1)
return bool(np.all(margin > 0)), float(margin.min())
def borwein_pattern(max_n):
"""For n = 1..max_n: (s_n, exact value of the n-th Borwein integral / pi).
The n-th integral is that of prod_{k=0}^{n} sinc(x/(2k+1)) over (0, inf).
It equals pi/2 while s_n = 1/3 + ... + 1/(2n+1) < 1; at the first n with
s_n > 1 it equals (pi/2)(1 - (s_n - 1)^n / (2^(n-1) n! prod 1/(2k+1)))
(D. and J. Borwein, 2001).
"""
rows = []
for n in range(1, max_n + 1):
a = [Fraction(1, 2 * k + 1) for k in range(1, n + 1)]
s = sum(a)
if s < 1:
rows.append((s, Fraction(1, 2)))
else:
prod = math.prod(a)
deficit = (s - 1) ** n / (2 ** (n - 1) * math.factorial(n) * prod)
rows.append((s, Fraction(1, 2) * (1 - deficit)))
break
return rows
def main():
sols = euler_fifth_power_search(150)
print("1. a^5 + b^5 + c^5 + d^5 = e^5, all 1 <= a<=b<=c<=d < e <= 150:")
for a, b, c, d, e in sols:
print(f" {a}^5 + {b}^5 + {c}^5 + {d}^5 = {e}^5")
print(f" {len(sols)} solution(s): Euler's conjecture (1769) is false.")
first, count = race_mod_4(10 ** 5)
print("2. Prime race mod 4, x <= 100000: primes 1 mod 4 first lead at")
print(f" x = {first}; they lead at only {count} of the 100000 values of x.")
is_prime = prime_sieve(10 ** 6)
counts = np.cumsum(is_prime)
print("3. pi(x) versus li(x):")
for k in range(2, 7):
x = 10 ** k
print(f" x = 10^{k}: pi(x) = {int(counts[x]):>6}, "
f"li(x) = {li(x):>10.1f}, li(x) - pi(x) = {li(x) - counts[x]:6.1f}")
holds, margin = li_exceeds_pi_up_to(10 ** 6)
print(f" li(x) > pi(x) for all real 2 <= x <= 10^6: {holds} "
f"(smallest gap {margin:.3f})")
print("4. Borwein integrals, exact values divided by pi:")
for n, (s, value) in enumerate(borwein_pattern(10), start=1):
tag = "= 1/2" if value == Fraction(1, 2) else \
f"= 1/2 - {float(Fraction(1, 2) - value):.3e}"
print(f" n = {n}: 1/3 + ... + 1/{2 * n + 1} = {float(s):.4f}, "
f"integral/pi {tag}")
print()
print("Epistemic status: searches 1 and 2 are exhaustive and exact up to their")
print("stated bounds, and 4 is exact rational arithmetic on a proved formula.")
print("Check 3 uses floating-point li(x) at every prime up to 10^6: numerical")
print("evidence only - and Littlewood's theorem shows that this evidence,")
print("however far it is extended, points the wrong way.")
if __name__ == "__main__":
main()Notice how each report is phrased: what was searched, how the search is known to be complete, and what kind of evidence the result is. That is the standard to apply to your own experiments, and to those you read.
Explore: Euler's experiment and the four colour theorem
Euler's problem: the partial sums of creep towards with an error of about after terms, so a thousand terms give only three correct digits. Euler needed acceleration (the Euler–Maclaurin formula) to get enough digits to recognise the limit. Increase and watch how slowly the gap closes.
Euler's experiment: 1 + 1/4 + 1/9 + ⋯
Add the terms one by one. Do the partial sums Sₙ settle down, or keep growing?
- partial sums Sₙ
- terms aₙ
- Known sum
S1000 = a1 + … + a1000 = 1.643935
Known sum: L = π²/6 ≈ 1.644934 · still missing: L − S1000 ≈ 0.001
Partial sums on a screen are evidence, never proof: a series can look settled and still diverge, as the harmonic series does.
The colouring widget computes chromatic numbers of small graphs by exhaustive search — a finite computation that settles a finite question. The four colour theorem is a statement about infinitely many planar graphs; its proof needed a theorem reducing it to finitely many configurations before any computer could help. Try the wheel and the Petersen graph, then , which is not planar.
Graph explorer
Dots joined by lines: count degrees, walk along edges, colour vertices, grow spanning trees and count walks with a matrix.
0
colours used
0
clashes
6
uncoloured
Give every vertex a colour so that the two ends of each edge differ.
This graph is planar: it can be drawn with no edges crossing. The Four Colour Theorem says every planar graph can be coloured with at most four colours. Kenneth Appel and Wolfgang Haken proved it in 1976 with essential help from a computer, which checked well over a thousand cases; in 2005 Georges Gonthier completed a fully computer-checked proof in the Coq proof assistant.
Exercises
1. Experimental mathematics: conjectures and counterexamples
Warm-upHumanThe Fermat numbers are . What is the smallest for which is not prime?
2. Experimental mathematics: conjectures and counterexamples
Warm-upHumanLet . Which vector is an integer relation for ?
3. Experimental mathematics: conjectures and counterexamples
CoreHumanWhich of these are honest, adequately specified reports of a counterexample search for Goldbach's conjecture? (Choose all that apply.)
4. Experimental mathematics: conjectures and counterexamples
CoreHumanAt 100 digits, PSLQ finds a relation with coefficients of size below among six constants, with residual ; at 200 digits the same relation holds to . What may you claim?
5. Experimental mathematics: conjectures and counterexamples
CoreComputationalAmong the primes , let be the number with and the number with . Compute .
6. Experimental mathematics: conjectures and counterexamples
CoreHumanIn the proof of the BBP formula, which step needs an analytic justification rather than algebra alone?
7. Experimental mathematics: conjectures and counterexamples
CoreFormalWith fermat n = 2 ^ (2 ^ n) + 1, which Lean statement says that Fermat's conjecture is false?
8. Experimental mathematics: conjectures and counterexamples
CoreHumanWhich conjecture would be refuted by a finite counterexample that a computer can check, whatever form the counterexample takes?
9. Experimental mathematics: conjectures and counterexamples
StretchComputationalWrite first_counterexample(pred, start, bound) that checks pred(n) for every integer n with start <= n <= bound in increasing order and returns the first n for which pred(n) is false, or None if there is none (an exhaustive search up to bound). Then use it to find the first prime p for which is not prime. The helper is_prime is provided.
Common misconceptions
Misconception
"A pattern that holds in many cases is established." The prime race holds for consecutive values; holds beyond ; the Borwein pattern holds for seven integrals; Fermat had five primes. All fail. Evidence makes a conjecture worth trying to prove; only a proof (or a counterexample) settles it.
Misconception
"We searched and found no counterexample" is a result. Not without its bounds. A report must say exactly what was searched (which , which parameters), how completeness was ensured (for example, why the meet-in-the-middle search misses no case), and that a negative result says nothing beyond the bound.
Misconception
"PSLQ proved the formula." An integer relation found numerically is a conjecture, however many digits support it — and at low precision it may be spurious. The BBP formula became a theorem when it was proved by calculus.
Misconception
"Computer-assisted proofs are less reliable than human proofs." Human proofs also contain errors, some found only decades later. What matters is whether each part can be checked: hand-proved reductions, open and independently reimplemented code, certificates, validated numerics and formal verification can make a computer-assisted proof among the most reliable in mathematics.
Many ways of seeing
Number theory— Relations, irrationality and transcendence
Finding an integer relation is easy to verify and hard to rule out. A proof that no relation exists is an irrationality or transcendence theorem: has no relation iff is irrational, and has none for every iff is transcendental. Lindemann (1882) proved transcendental, and Apéry (1978) proved irrational — his proof itself began with mysterious, experimentally checked recurrences. But it is unknown whether , or Euler's constant is irrational. Integer-relation searches give bounds: PSLQ can certify that satisfies no polynomial of small degree with small coefficients — evidence, not proof. See algebraic number theory.
Geometry— Lattices and the geometry of numbers
An integer relation is a short vector in a lattice. Scale the numbers by a large and consider the lattice in spanned by the vectors ; a relation gives the lattice vector , whose last coordinate is unusually small. Minkowski's convex body theorem guarantees that short vectors exist; LLL finds a vector within a factor of the shortest in polynomial time, by a reduction process that makes the basis nearly orthogonal. Geometry is also the subject of the Kepler conjecture, where the computer's task was to rule out every competing local arrangement of spheres.
Analysis— Why the numbers mislead
Riemann's explicit formula writes as minus a correction from prime squares, about , plus oscillating terms from the zeros of (assuming the Riemann hypothesis). The oscillations have size about times , and they vary on the scale of , not : to overcome the bias the zeros must align, which happens first only for astronomically large . Prime races have the same anatomy, with Dirichlet -functions. The lesson for experiments: asymptotic behaviour that lives on a logarithmic scale cannot be seen in data on a linear one. See analytic number theory.
Probability— Heuristics from random models
Cramér's model treats each as "prime" independently with probability . It predicts, for example, that the number of twin primes up to is about — with the correct constant only after Hardy and Littlewood corrected for divisibility by small primes — and the numerical agreement is spectacular. Such random models are heuristics: they suggest conjectures and the right size of answers, but they are not proofs, and Maier (1985) showed that Cramér's model fails in short intervals. Experimental mathematics often tests such predictions; the honest report distinguishes the heuristic, the data and the theorem.
Logic— What a search can and cannot do
Goldbach's conjecture is a statement: "for all , a decidable property holds". If it is false, a finite computation exhibits the counterexample, and Peano arithmetic proves the refutation. So if Goldbach's conjecture is independent of PA it is true. The Collatz conjecture is ("for all there exists …"): a counterexample could be a diverging trajectory, which no finite computation can display. Conway (1972) proved that a natural generalisation of the Collatz problem is undecidable. Logic thus classifies what experiments can achieve: statements are refutable by search, statements confirmable by search, and anything more complex needs proof. See the incompleteness theorems.
History and global context
- Before computers. Euler's numerical work on the Basel problem (1730s) and his factor of (1732); Gauss's prime tables (1790s); Ramanujan's notebooks, written in India in the 1900s–1910s, full of formulas found by extraordinary calculation and insight, many proved only decades later by Berndt and others.
- Machines. D. N. and D. H. Lehmer built mechanical and photoelectric sieves for number theory in the 1920s and 1930s. Turing computed zeros of the Riemann zeta function on the Manchester Mark 1 (published 1953). Birch and Swinnerton-Dyer's computations on EDSAC led to their conjecture (published 1963–65). Lander and Parkin refuted Euler's conjecture in 1966.
- Counterexamples to famous conjectures. Haselgrove (1958) disproved Pólya's conjecture on the parity of prime factors; Tanaka (1980) found the smallest counterexample, . Odlyzko and te Riele (1985) disproved the Mertens conjecture without exhibiting a counterexample.
- Algorithms. Ferguson and Forcade gave an integer relation algorithm (1979); Lenstra, Lenstra and Lovász gave LLL (1982); Ferguson and Bailey gave PSLQ (1992); in 2000 the journal Computing in Science and Engineering listed integer relation detection among the top ten algorithms of the twentieth century. Wu Wenjun in China developed a mechanical method for proving geometry theorems (1977), a landmark of automated reasoning.
- A discipline. Sloane began collecting integer sequences in 1964; the OEIS has been online since 1996. The journal Experimental Mathematics was founded in 1992. Computer-assisted proofs — the four colour theorem (1976), the Kepler conjecture (1998; formal proof 2014), and Helfgott's ternary Goldbach theorem (2013), by a mathematician from Peru working in France and Germany — changed what mathematicians accept as proof and how they check it.
Further reading
- Jonathan Borwein and David Bailey, Mathematics by Experiment: Plausible Reasoning in the 21st Century (2004; 2nd ed. 2008), with its companion David Bailey, Jonathan Borwein and Roland Girgensohn, Experimentation in Mathematics (2004).
- Richard K. Guy, "The strong law of small numbers", American Mathematical Monthly (1988).
- Andrew Granville and Greg Martin, "Prime number races", American Mathematical Monthly (2006).
- David Bailey, Peter Borwein and Simon Plouffe, "On the rapid computation of various polylogarithmic constants", Mathematics of Computation (1997); Helaman Ferguson, David Bailey and Steve Arno, "Analysis of PSLQ, an integer relation finding algorithm", Mathematics of Computation (1999).
- Georges Gonthier, "Formal proof — the four-color theorem", Notices of the AMS (2008); Thomas Hales et al., "A formal proof of the Kepler conjecture", Forum of Mathematics, Pi (2017).
- A. E. Ingham, The Distribution of Prime Numbers (1932), for Littlewood's theorem.