Skip to content

Commit bd970af

Browse files
author
TNFR AI Agent
committed
Use the canonical L_rw/L_sym relaxation spectrum in the conservation/Lyapunov spectral analysis
The structural-conservation theorem characterised the diffusive relaxation rate via the combinatorial Laplacian L = D - A (lyapunov.analyze_spectral_gap, conservation.compute_spectral_conservation, theorem section 8.5), but the canonical TNFR diffusion operator is the random-walk Laplacian L_rw = I - D^-1 W (structural_diffusion; dEPI/dt = -nu_f L_rw EPI). These differ on irregular graphs and by the degree factor 1/d on regular graphs; section 9.1 of the same theorem already defines the divergence as (1/d_i)(L J) = L_rw J, an internal contradiction. Additive fix (keeps the correctly-named combinatorial algebraic connectivity, which test_complete_graph encodes as K_n: lambda_1 = n): analyze_spectral_gap adds diffusion_gap = lambda_2 of the symmetric normalized Laplacian L_sym = I - D^-1/2 W D^-1/2 (same spectrum as L_rw), while spectral_gap/fiedler_value remain the combinatorial Fiedler value; analyze_operator_convergence bounds the stabiliser convergence rate with diffusion_gap; compute_spectral_conservation decomposes in the L_sym eigenbasis; theorem 8.5 + 9.1 now state L_rw/L_sym as the canonical relaxation operator, resolving the 8.5<->9.1 contradiction; test_effective_rate asserts against diffusion_gap. Validated: lyapunov 94/94, core_physics 220/220, riemann_topology 88/88; flake8 only pre-existing E302/E305.
1 parent 59c62ec commit bd970af

4 files changed

Lines changed: 91 additions & 33 deletions

File tree

src/tnfr/physics/conservation.py

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1067,7 +1067,9 @@ class SpectralConservation:
10671067
r"""Spectral decomposition of the conservation fields.
10681068
10691069
Expands charge density ρ and current divergence in the eigenbasis of the
1070-
graph Laplacian L: ρ(i) = Σ_k ρ̂_k ψ_k(i).
1070+
symmetric normalized Laplacian L_sym = I − D^{-1/2} W D^{-1/2}, whose
1071+
spectrum is that of the canonical TNFR diffusion operator L_rw = I − D⁻¹W
1072+
(the EPI channel of the nodal equation): ρ(i) = Σ_k ρ̂_k ψ_k(i).
10711073
10721074
The continuity equation mode-by-mode reads:
10731075
dρ̂_k/dt + λ_k Ĵ_k = Ŝ_k
@@ -1078,7 +1080,8 @@ class SpectralConservation:
10781080
Attributes
10791081
----------
10801082
eigenvalues : np.ndarray
1081-
Laplacian eigenvalues λ_k (sorted ascending).
1083+
L_sym eigenvalues λ_k (sorted ascending) — the canonical L_rw
1084+
relaxation spectrum.
10821085
rho_spectrum : np.ndarray
10831086
Charge density coefficients ρ̂_k in the eigenbasis.
10841087
div_spectrum : np.ndarray
@@ -1102,9 +1105,9 @@ def compute_spectral_conservation(
11021105
G: Any,
11031106
snapshot: ConservationSnapshot | None = None,
11041107
) -> SpectralConservation:
1105-
r"""Decompose conservation fields in the graph Laplacian eigenbasis.
1108+
r"""Decompose conservation fields in the normalized Laplacian eigenbasis.
11061109
1107-
Connects TNFR conservation to spectral graph theory. The Laplacian
1110+
Connects TNFR conservation to spectral graph theory. The L_sym
11081111
eigenvalues determine at which structural scales conservation holds
11091112
most precisely:
11101113
@@ -1131,21 +1134,29 @@ def compute_spectral_conservation(
11311134
nodes = sorted(snapshot.charge_density.keys())
11321135
n = len(nodes)
11331136

1134-
# Build graph Laplacian
1137+
# Build the symmetric normalized Laplacian L_sym = I − D^{-1/2} W D^{-1/2},
1138+
# whose spectrum is that of the canonical TNFR diffusion operator
1139+
# L_rw = I − D⁻¹W (the EPI channel; see ``structural_diffusion``). This is
1140+
# consistent with the §9.1 normalized divergence (∇·J = L_rw·J).
11351141
if nx is not None and isinstance(G, nx.Graph):
1136-
L = nx.laplacian_matrix(G).toarray().astype(float)
1142+
L = nx.normalized_laplacian_matrix(G).toarray().astype(float)
11371143
else:
1138-
# Fallback: build from adjacency
1139-
L = np.zeros((n, n))
1144+
# Fallback: build the combinatorial Laplacian then symmetric-normalize
1145+
Lc = np.zeros((n, n))
11401146
node_idx = {nd: i for i, nd in enumerate(nodes)}
11411147
for u, v in G.edges():
11421148
i, j = node_idx[u], node_idx[v]
1143-
L[i, j] = -1.0
1144-
L[j, i] = -1.0
1145-
L[i, i] += 1.0
1146-
L[j, j] += 1.0
1147-
1148-
# Eigendecomposition
1149+
Lc[i, j] -= 1.0
1150+
Lc[j, i] -= 1.0
1151+
Lc[i, i] += 1.0
1152+
Lc[j, j] += 1.0
1153+
deg = Lc.diagonal().astype(float)
1154+
with np.errstate(divide="ignore"):
1155+
d_inv_sqrt = np.where(deg > 0.0, 1.0 / np.sqrt(deg), 0.0)
1156+
adjacency = np.diag(deg) - Lc
1157+
L = np.eye(n) - d_inv_sqrt[:, None] * adjacency * d_inv_sqrt[None, :]
1158+
1159+
# Eigendecomposition (orthonormal eigenbasis of L_sym)
11491160
eigvals, eigvecs = np.linalg.eigh(L)
11501161

11511162
# Project charge density and divergence into eigenbasis

src/tnfr/physics/lyapunov.py

Lines changed: 47 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -40,15 +40,19 @@
4040
4141
Spectral Gap Characterisation
4242
-----------------------------
43-
The algebraic connectivity λ₁ of the graph Laplacian controls the
44-
*relaxation time* τ = 1/λ₁ for diffusive modes. This module provides
45-
``analyze_spectral_gap`` which computes:
43+
``analyze_spectral_gap`` reports two distinct, both-meaningful quantities:
4644
47-
- λ₁ (algebraic connectivity)
48-
- Relaxation time τ_relax = 1/λ₁
49-
- Mixing time estimate t_mix ~ ln(N)/λ₁
50-
- Cheeger-type lower bound h²/(2d_max) ≤ λ₁
51-
- Convergence rate for stabiliser-dominated sequences
45+
- the **combinatorial algebraic connectivity** λ₁ (Fiedler value) of
46+
L = D − A — a graph-topology measure; and
47+
- the **canonical diffusion relaxation gap** λ₂ of the symmetric normalized
48+
Laplacian L_sym = I − D^{-1/2} W D^{-1/2}, which shares the spectrum of the
49+
canonical TNFR diffusion operator L_rw = I − D⁻¹W (``structural_diffusion``).
50+
51+
The *diffusive* relaxation time-scale is set by the **diffusion gap**: the EPI
52+
field relaxes as exp(−ν_f·λ₂·t). The two gaps coincide only up to the degree
53+
normalisation (λ₁/d on a d-regular graph) and differ on irregular graphs.
54+
Derived: relaxation time, mixing-time estimate, Cheeger-type bound, and the
55+
stabiliser convergence rate (which uses the canonical diffusion gap).
5256
5357
References
5458
----------
@@ -540,9 +544,18 @@ class SpectralGapAnalysis:
540544
Attributes
541545
----------
542546
spectral_gap : float
543-
λ₁ — algebraic connectivity of the graph Laplacian.
547+
λ₁ — combinatorial algebraic connectivity (Fiedler value): the
548+
second-smallest eigenvalue of L = D − A. A graph-topology measure.
544549
fiedler_value : float
545550
Same as spectral_gap (alternative name from spectral graph theory).
551+
diffusion_gap : float
552+
λ₂ of the symmetric normalized Laplacian L_sym = I − D^{-1/2} W D^{-1/2},
553+
which shares the spectrum of the canonical TNFR diffusion operator
554+
L_rw = I − D⁻¹W. This is the **canonical structural relaxation rate**
555+
(per unit ν_f): the EPI field relaxes as exp(−ν_f·λ₂·t). Use this — not
556+
the combinatorial ``spectral_gap`` — for the diffusive relaxation
557+
time-scale. Equals ``spectral_gap``/d on a d-regular graph; differs on
558+
irregular graphs.
546559
relaxation_time : float
547560
τ_relax = 1/λ₁ — time for the slowest non-trivial mode to decay
548561
by factor e. ``inf`` if graph is disconnected (λ₁ = 0).
@@ -568,6 +581,7 @@ class SpectralGapAnalysis:
568581

569582
spectral_gap: float
570583
fiedler_value: float
584+
diffusion_gap: float
571585
relaxation_time: float
572586
convergence_rate: float
573587
mixing_time_bound: float
@@ -605,6 +619,7 @@ def analyze_spectral_gap(G: Any) -> SpectralGapAnalysis:
605619
return SpectralGapAnalysis(
606620
spectral_gap=0.0,
607621
fiedler_value=0.0,
622+
diffusion_gap=0.0,
608623
relaxation_time=float("inf"),
609624
convergence_rate=0.0,
610625
mixing_time_bound=float("inf"),
@@ -633,12 +648,26 @@ def analyze_spectral_gap(G: Any) -> SpectralGapAnalysis:
633648
eigvals = np.linalg.eigvalsh(L)
634649
eigvals = np.sort(eigvals)
635650

636-
# λ₁ = second-smallest eigenvalue
651+
# λ₁ = second-smallest eigenvalue (combinatorial algebraic connectivity)
637652
lambda_1 = float(eigvals[1]) if n > 1 else 0.0
638653
lambda_1 = max(0.0, lambda_1) # numerical safety
639654

640655
lambda_max = float(eigvals[-1])
641656

657+
# Canonical structural relaxation rate: λ₂ of the symmetric normalized
658+
# Laplacian L_sym = I − D^{-1/2} W D^{-1/2}, which shares the spectrum of the
659+
# canonical TNFR diffusion operator L_rw = I − D⁻¹W (structural_diffusion).
660+
if nx is not None and isinstance(G, nx.Graph):
661+
L_sym = nx.normalized_laplacian_matrix(G).toarray().astype(float)
662+
else:
663+
deg = L.diagonal().astype(float)
664+
with np.errstate(divide="ignore"):
665+
d_inv_sqrt = np.where(deg > 0.0, 1.0 / np.sqrt(deg), 0.0)
666+
adjacency = np.diag(deg) - L # A = D − L recovers the weight matrix
667+
L_sym = np.eye(n) - d_inv_sqrt[:, None] * adjacency * d_inv_sqrt[None, :]
668+
sym_eigs = np.sort(np.linalg.eigvalsh(L_sym))
669+
diffusion_gap = max(0.0, float(sym_eigs[1])) if n > 1 else 0.0
670+
642671
is_connected = lambda_1 > 1e-10
643672
tau = 1.0 / lambda_1 if is_connected else float("inf")
644673
mixing = math.log(n) / lambda_1 if is_connected else float("inf")
@@ -652,6 +681,7 @@ def analyze_spectral_gap(G: Any) -> SpectralGapAnalysis:
652681
return SpectralGapAnalysis(
653682
spectral_gap=lambda_1,
654683
fiedler_value=lambda_1,
684+
diffusion_gap=diffusion_gap,
655685
relaxation_time=tau,
656686
convergence_rate=lambda_1,
657687
mixing_time_bound=mixing,
@@ -698,7 +728,9 @@ def analyze_operator_convergence(
698728
r"""Combine per-operator Lyapunov bound with spectral gap analysis.
699729
700730
For stabilisers, the effective convergence rate is the tighter of
701-
the operator's contraction rate ρ and the graph's spectral gap λ₁.
731+
the operator's contraction rate ρ and the graph's canonical diffusion
732+
relaxation gap λ₂(L_sym) (``diffusion_gap``, the structural_diffusion
733+
relaxation rate — not the combinatorial algebraic connectivity).
702734
The number of steps to halve energy is ln(2)/rate.
703735
704736
Parameters
@@ -716,7 +748,10 @@ def analyze_operator_convergence(
716748
spectral = analyze_spectral_gap(G)
717749

718750
if bound.energy_class == EnergyClass.STABILISER:
719-
rate = min(bound.contraction_rate, spectral.spectral_gap)
751+
# The diffusive relaxation bound is the CANONICAL diffusion gap
752+
# λ₂(L_sym) (= the structural_diffusion relaxation rate), not the
753+
# combinatorial algebraic connectivity.
754+
rate = min(bound.contraction_rate, spectral.diffusion_gap)
720755
steps = math.log(2) / rate if rate > 1e-15 else float("inf")
721756
else:
722757
rate = 0.0

tests/core_physics/test_lyapunov_operators.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -480,7 +480,7 @@ def test_effective_rate_is_min_of_rho_and_lambda(self, ws_graph):
480480
summary = analyze_operator_convergence(ws_graph, "IL")
481481
bound = get_bound("IL")
482482
spectral = analyze_spectral_gap(ws_graph)
483-
expected = min(bound.contraction_rate, spectral.spectral_gap)
483+
expected = min(bound.contraction_rate, spectral.diffusion_gap)
484484
assert abs(summary.effective_convergence_rate - expected) < 1e-12
485485

486486
def test_steps_to_half_uses_ln2(self, ws_graph):

theory/STRUCTURAL_CONSERVATION_THEOREM.md

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -599,9 +599,17 @@ $(1 + 6.857) \times (1 - 0.457)^4 = 7.857 \times 0.087 \approx 0.68 < 1$ ✓
599599

600600
### 8.5 Spectral Gap Characterisation
601601

602-
The **algebraic connectivity** $\lambda_1$ (smallest non-zero eigenvalue of
603-
the graph Laplacian $L = D - A$) controls the diffusive relaxation time-scale
604-
and provides a topology-dependent convergence rate.
602+
The **diffusive relaxation time-scale** is controlled by the canonical TNFR
603+
diffusion operator $L_{\mathrm{rw}} = I - D^{-1}W$ (the EPI channel of the nodal
604+
equation; see `structural_diffusion`): the field relaxes as
605+
$e^{-\nu_f \lambda_2 t}$, where $\lambda_2$ is the spectral gap of the symmetric
606+
normalized Laplacian $L_{\mathrm{sym}} = I - D^{-1/2} W D^{-1/2}$ (same spectrum as
607+
$L_{\mathrm{rw}}$, orthonormal eigenbasis). The **combinatorial algebraic
608+
connectivity** $\lambda_1$ of $L = D - A$ is the related graph-topology measure;
609+
the two coincide only up to the degree normalisation ($\lambda_1/d$ on a
610+
$d$-regular graph) and differ on irregular graphs. The convergence rate below
611+
uses the **normalized** (canonical) gap; `analyze_spectral_gap` exposes it as
612+
`diffusion_gap`.
605613

606614
**Spectral Quantities**
607615

@@ -639,10 +647,14 @@ bounds, spectral gap analysis, and sequence contractiveness proofs.
639647

640648
### 9.1 Graph Laplacian Connection
641649

642-
The discrete divergence used in TNFR conservation relates to the
643-
**graph Laplacian** $L = D - A$:
650+
The discrete divergence used in TNFR conservation is the **random-walk graph
651+
Laplacian** $L_{\mathrm{rw}} = I - D^{-1}W$ — the $1/d_i$ normalisation turns the
652+
combinatorial Laplacian $L = D - A$ into $L_{\mathrm{rw}}$:
644653

645-
$$(\nabla \cdot \mathbf{J})(i) \approx \frac{1}{d_i} \sum_{j \sim i} [J(j) - J(i)] = \frac{1}{d_i} (L \cdot \mathbf{J})_i$$
654+
$$(\nabla \cdot \mathbf{J})(i) \approx \frac{1}{d_i} \sum_{j \sim i} [J(j) - J(i)] = \frac{1}{d_i} (L \cdot \mathbf{J})_i = (L_{\mathrm{rw}} \mathbf{J})_i$$
655+
656+
so the relaxation spectrum governing conservation is that of $L_{\mathrm{rw}}$
657+
(equivalently the symmetric $L_{\mathrm{sym}}$), consistent with §8.5.
646658

647659
This connects conservation on graphs to spectral graph theory.
648660

0 commit comments

Comments
 (0)