Causal Dynamical Triangulations
Scrollable formula. Use Left and Right arrow keys to reveal hidden content.
Scrollable table. Use Left and Right arrow keys to reveal additional columns.
Scrollable figure. Use Left and Right arrow keys to inspect hidden content.
Causal dynamical triangulations define a Lorentzian sum over piecewise-flat histories with a preferred discrete proper-time slicing, fixed spatial topology, and no spatial branching. A configuration is a gluing of standard simplices, not a perturbation around one background metric. Four-dimensional simulations exhibit an extended phase with semiclassical large-scale observables, but a continuum theory requires a continuous transition, diverging physical correlation length in lattice units, finite-size scaling, and regulator-independent observables. The executable calculation below validates only the exact 1+1-dimensional transfer combinatorics used as a method control.
Helpful background. Flat-Spacetime Quantum Field Theory supplies lattice sums and critical phenomena; Einstein Equations supplies the continuum target; Quantum Information supplies transfer matrices and random walks; Quantum Gravity supplies evidence standards; Causality and Global Structure supplies foliations and topology; and Computational Relativity supplies finite-size, autocorrelation, and convergence methods.
The four-dimensional ensemble
Section titled “The four-dimensional ensemble”Fix spatial topology and integer proper time. Spacelike edges have squared length , timelike edges have squared length , and allowed four-simplices connect adjacent slices without branching the spatial hypersurface. The causal gluing rules admit a configuration-wise analytic continuation to a Euclideanized ensemble for the permitted range of Ambjørn, Jurkiewicz, and Loll 2001, §§2–4.
After continuation, a commonly used four-dimensional Regge action is
where both time orientations are included in each simplex count. The bare inverse Newton coupling is encoded in , controls four-volume, and records the spacelike–timelike asymmetry. Simulations sample
with local ergodic moves, automorphism factor , and usually a declared volume-fixing term. Acceptance rule, thermalization, integrated autocorrelation time, move balance, topology, and finite-volume window are part of the result.
Which critical behavior would be enough?
Section titled “Which critical behavior would be enough?”The extended phase has an ensemble-average spatial-volume profile compatible, after finite-size rescaling, with a Euclidean de Sitter solution and a macroscopic Hausdorff dimension near four. Diffusion observables give a scale-dependent spectral dimension,
where is the ensemble-averaged return probability after diffusion time . The short-distance reduction first reported in four-dimensional CDT is a numerical observable, not a proof that topology or metric dimension literally changes Ambjørn, Jurkiewicz, and Loll 2005, Eqs. (1)–(5).
A viable continuum limit would require a higher-order transition bordering an extended phase, with a correlation length as bare couplings approach criticality. At increasing four-volume, susceptibility peaks should scale consistently, pseudocritical couplings should approach a limit, hysteresis should disappear for a continuous transition, and renormalized observables should become insensitive to volume fixing, move set, and lattice details. The phase diagram contains candidate higher-order behavior, especially near the bifurcation and collapsed phases, but the identification of a unique ultraviolet fixed point and a complete set of continuum observables remains open Loll 2020, §§4–6.
Exact rooted-strip benchmark
Section titled “Exact rooted-strip benchmark”For the method check, specialize to one 1+1-dimensional cylindrical strip. The entrance and exit loops have positive integer lengths and . There are future-pointing and past-pointing triangles, spatial topology is periodic, topology change is forbidden, and one entrance position is marked. Reading from that root gives a word whose first symbol is ; choosing the remaining up-triangle positions yields
The asymmetry but is intentional: only the entrance is marked, and . This is the convention behind the marked transfer function in Ambjørn and Loll 1998, §2, Eqs. (9)–(17), pp. 5–6.
Assign weight with . The program enumerates every rooted word and compares it with an independent math.comb implementation for all . It builds both count and exactly rational weighted transfer matrices, then compares formula-based matrix multiplication with a separate intermediate-length sum assembled from the enumerated one-step counts. It also evaluates a normalized outgoing-row mean at cutoffs , , and .
Because , normalizing this row over gives the geometric distribution . This is a deliberately normalized diagnostic, not a claim that the full transfer matrix is stochastic. Its infinite-cutoff mean and exact cutoff errors are
These follow by differentiating the finite geometric sum . They independently predict every row of the convergence table, including the limiting mean .
Complete CPython reference program
Section titled “Complete CPython reference program”The program supports CPython 3.11 or later and was rerun with CPython 3.12.14. It uses only the standard library, with deterministic exhaustive enumeration and no random seed. Integer counts remain exact; rational weights use fractions.Fraction; conversion to binary64 occurs only for printed diagnostics after a safety check. Final tolerances are for the mean error and for discarded boundary weight. Save the following program as cdt_rooted_strip_benchmark.py. The script emits count, composition, convergence, failure, and scope diagnostics.
#!/usr/bin/env python3from fractions import Fractionfrom itertools import combinationsfrom math import comb, isfiniteimport platformimport sys
MINIMUM_MAJOR_MINOR = (3, 11)MAX_LENGTH = 8LEVELS = (4, 6, 8)G = Fraction(2, 5)MEAN_ERROR_TOL = Fraction(3, 500)BOUNDARY_TOL = Fraction(1, 1000)FLOAT_SAFE_INTEGER = 2**53
def stop(message): raise SystemExit("FAIL: " + message)
def validate_lengths(entrance, exit_): if not isinstance(entrance, int) or not isinstance(exit_, int): stop("boundary lengths must be integers") if entrance < 1 or exit_ < 1: stop("boundary lengths must be positive")
def rooted_words(entrance, exit_): """Rooted entrance: position zero is an up triangle.""" validate_lengths(entrance, exit_) total = entrance + exit_ words = set() for extra_up in combinations(range(1, total), entrance - 1): up_positions = set(extra_up) word = "U" + "".join( "U" if position in up_positions else "D" for position in range(1, total) ) if ( len(word) != total or word[0] != "U" or word.count("U") != entrance or word.count("D") != exit_ or set(word) != {"U", "D"} ): stop("illegal rooted-strip word") if word in words: stop("duplicate cyclic word after fixing the root") words.add(word) return words
def brute_count(entrance, exit_): return len(rooted_words(entrance, exit_))
def binomial_count(entrance, exit_): validate_lengths(entrance, exit_) return comb(entrance + exit_ - 1, entrance - 1)
def matmul(left, right): rows = len(left) inner = len(right) columns = len(right[0]) if any(len(row) != inner for row in left): stop("matrix shape mismatch") return [ [ sum(left[i][k] * right[k][j] for k in range(inner)) for j in range(columns) ] for i in range(rows) ]
def exact_controls(): maximum = 0 enumerated_count_matrix = [] for entrance in range(1, MAX_LENGTH + 1): enumerated_row = [] for exit_ in range(1, MAX_LENGTH + 1): brute = brute_count(entrance, exit_) formula = binomial_count(entrance, exit_) if brute != formula: stop(f"count mismatch at ({entrance}, {exit_})") reverse = binomial_count(exit_, entrance) if exit_ * formula != entrance * reverse: stop("marked-boundary relation failed") maximum = max(maximum, formula) enumerated_row.append(brute) enumerated_count_matrix.append(enumerated_row)
if binomial_count(2, 3) != 4 or binomial_count(3, 2) != 6: stop("entrance marking was accidentally symmetrized") if maximum > FLOAT_SAFE_INTEGER: stop("integer-to-float conversion would lose count information")
count_matrix = [ [ binomial_count(entrance, exit_) for exit_ in range(1, MAX_LENGTH + 1) ] for entrance in range(1, MAX_LENGTH + 1) ] squared = matmul(count_matrix, count_matrix) direct = [ [ sum( enumerated_count_matrix[entrance - 1][middle - 1] * enumerated_count_matrix[middle - 1][exit_ - 1] for middle in range(1, MAX_LENGTH + 1) ) for exit_ in range(1, MAX_LENGTH + 1) ] for entrance in range(1, MAX_LENGTH + 1) ] gap = max( abs(squared[i][j] - direct[i][j]) for i in range(MAX_LENGTH) for j in range(MAX_LENGTH) ) if gap != 0: stop("two-step transfer-matrix composition failed")
weighted_matrix = [ [ Fraction(binomial_count(entrance, exit_)) * G ** (entrance + exit_) for exit_ in range(1, MAX_LENGTH + 1) ] for entrance in range(1, MAX_LENGTH + 1) ] weighted_squared = matmul(weighted_matrix, weighted_matrix) weighted_direct = [ [ sum( Fraction(enumerated_count_matrix[entrance - 1][middle - 1]) * G ** (entrance + middle) * Fraction(enumerated_count_matrix[middle - 1][exit_ - 1]) * G ** (middle + exit_) for middle in range(1, MAX_LENGTH + 1) ) for exit_ in range(1, MAX_LENGTH + 1) ] for entrance in range(1, MAX_LENGTH + 1) ] weighted_gap = max( abs(weighted_squared[i][j] - weighted_direct[i][j]) for i in range(MAX_LENGTH) for j in range(MAX_LENGTH) ) if weighted_gap != 0: stop("weighted transfer-matrix composition failed") return ( maximum, gap, squared[0][0], weighted_gap, weighted_squared[0][0], )
def normalized_row_observable(cutoff): weights = [ Fraction(binomial_count(1, exit_)) * G ** (1 + exit_) for exit_ in range(1, cutoff + 1) ] mass = sum(weights, Fraction(0)) mean_exit = sum( Fraction(exit_) * weight for exit_, weight in enumerate(weights, start=1) ) / mass infinite_mean = 1 / (1 - G) mean_error = abs(infinite_mean - mean_exit) discarded_boundary_weight = G**cutoff return mean_exit, mean_error, discarded_boundary_weight
def main(): if ( sys.implementation.name != "cpython" or sys.version_info[:2] < MINIMUM_MAJOR_MINOR ): stop("requires CPython 3.11 or later; found " + platform.python_version())
maximum, gap, sample, weighted_gap, weighted_sample = exact_controls() rows = [] previous_error = None previous_boundary = None for cutoff in LEVELS: row = normalized_row_observable(cutoff) mean_exit, mean_error, boundary_weight = row if not all(isfinite(float(value)) for value in row): stop("non-finite normalized observable") if previous_error is not None and mean_error >= previous_error: stop("mean-exit cutoff error did not decrease") if previous_boundary is not None and boundary_weight >= previous_boundary: stop("discarded boundary weight did not decrease") previous_error = mean_error previous_boundary = boundary_weight rows.append((cutoff, mean_exit, mean_error, boundary_weight))
if rows[-1][2] > MEAN_ERROR_TOL: stop("final mean-exit cutoff error exceeded tolerance") if rows[-1][3] > BOUNDARY_TOL: stop("upper cutoff still dominates the normalized row")
print("CPython", platform.python_version(), "standard-library-only") print("convention rooted-entrance periodic-strip no-branching") print( "count_pairs", MAX_LENGTH**2, "max_count", maximum, "marking_check PASS", ) print( "composition_cutoff", MAX_LENGTH, "max_exact_gap", gap, "T2_1_1", sample, ) print( "weighted_exact_gap", weighted_gap, "weighted_T2_1_1", f"{float(weighted_sample):.12e}", ) print( "cutoff mean_exit_length mean_error " "discarded_boundary_weight" ) for cutoff, mean_exit, mean_error, boundary_weight in rows: print( f"{cutoff:d} {float(mean_exit):.12f} " f"{float(mean_error):.12e} " f"{float(boundary_weight):.12e}" ) print( "PASS: counts, marking, composition, cutoff convergence, and scope" ) print("scope: exact finite 1+1 control; no four-dimensional inference")
if __name__ == "__main__": main()Use this exact save, compile, and run recipe. The final line is the clean rerun command.
python3 --versionpython3 -m py_compile cdt_rooted_strip_benchmark.pypython3 cdt_rooted_strip_benchmark.pyThe source above was compiled and executed with CPython 3.12.14 during the 2026-09-27 check, with no third-party package. It enumerated 64 boundary pairs, found maximum count 6435, obtained zero discrepancy between the formula-matrix and enumerated-composition routes for both exact transfers, and ended with PASS. The values agree with the earlier 2026-08-20 calculation on CPython 3.11.4. No random seed or external data enters.
Static convergence output
Section titled “Static convergence output”| Maximum length cutoff | Mean exit-length observable | Absolute mean error | Discarded boundary weight |
|---|---|---|---|
| 4 | 1.561576354680 | 1.050903119869e-01 | 2.560000000000e-02 |
| 6 | 1.641989589358 | 2.467707730866e-02 | 4.096000000000e-03 |
| 8 | 1.661420348440 | 5.246318227113e-03 | 6.553600000000e-04 |
The mean approaches , its error decreases at every refinement, and the final boundary weight is below . The composition control compares matrix multiplication of the binomial formula with direct composition of enumerated one-step counts; it gives at cutoff eight and zero discrepancy for every entry. The weighted rational routes also agree exactly; the displayed is only a checksum.
Failure modes and scope
Section titled “Failure modes and scope”The program exits nonzero for a nonpositive length, brute-force/binomial mismatch, duplicate rooted word, accidental symmetrization of the marked convention, unsafe integer conversion, matrix-composition mismatch, nonfinite observable, nonmonotone cutoff error, or excessive final boundary weight. The word representation fixes a cylindrical strip and excludes branching and topology change by construction. Changing to unmarked loops requires new symmetry factors rather than silently dividing this matrix.
This is an exact finite 1+1-dimensional control of enumeration, normalization, marking, and composition. It does not sample four-dimensional triangulations, locate a phase transition, estimate a four-dimensional spectral dimension, or demonstrate an extended continuum geometry. In production CDT, spectral-dimension analysis must first be validated on known finite graphs, then tested across diffusion windows, volumes, couplings, walk conventions, and autocorrelation bins. Likewise, a de Sitter volume profile at one bare point cannot replace finite-size critical scaling.
The direct answer to the governing question is therefore conditional: a continuous transition adjacent to an extended four-dimensional phase, accompanied by a diverging correlation length and stable renormalized observables, would support a continuum limit. Existing numerical evidence motivates that search but does not yet complete it.
Exercises
Section titled “Exercises”Derive for a rooted strip. Then compute , , and the cutoff- two-step count from length one back to length one.
Solution
The root fixes the first of the up triangles. Among the remaining cyclic positions, choose for the other up triangles, giving
Hence and ; the mismatch records which boundary is marked. Since and ,
At this is , matching the independent matrix product. This proves the finite transfer-composition identity under the stated convention, not any four-dimensional continuum claim.
References
Section titled “References”- Ambjørn, Jan, and Renate Loll. “Non-Perturbative Lorentzian Quantum Gravity, Causality and Topology Change.” Nuclear Physics B 536 (1998): 407–434. Open manuscript.
- Ambjørn, Jan, Jerzy Jurkiewicz, and Renate Loll. “Dynamically Triangulating Lorentzian Quantum Gravity.” Nuclear Physics B 610 (2001): 347–382. Open manuscript.
- Ambjørn, Jan, Jerzy Jurkiewicz, and Renate Loll. “Spectral Dimension of the Universe.” Physical Review Letters 95 (2005): 171301. Open manuscript.
- Loll, Renate. “Quantum Gravity from Causal Dynamical Triangulations: A Review.” Classical and Quantum Gravity 37 (2020): 013002. Open review.