r"""TNFR χ-twisted Lyapunov-spectral positivity certificate (P44 program).
L-track analogue of the ζ-track Lyapunov-spectral positivity
certificate (P26,
:mod:`tnfr.riemann.lyapunov_spectral_positivity`). The construction
mirrors P26 line-for-line, replacing the untwisted prime-ladder
Hamiltonian (P14) by the χ-twisted prime-ladder Hamiltonian (P34)
of :mod:`tnfr.riemann.twisted_prime_ladder_hamiltonian`.
Goal
----
Produce an explicit, quantitative certificate that the χ-twisted
prime-ladder Hamiltonian is
1. **self-adjoint**,
2. **strictly positive** with an explicit spectral gap,
3. has **trace-class resolvent** :math:`(\hat H + c\hat I)^{-1}`
for any :math:`c > 0`,
4. compatible with the structural Lyapunov functional of
:mod:`tnfr.physics.conservation`: the dynamical flow
:math:`U(t) = e^{-it\hat H}` preserves the unit-norm sphere and
the spectral energy :math:`\|H\psi\|^2`.
Analytic anatomy of the certificate
-----------------------------------
Let :math:`\hat H = \hat H_{\mathrm{freq}} + J_0\,
\hat H_{\mathrm{coupling}}` on the χ-twisted prime-ladder Hilbert
space
.. math::
\mathcal{H}_{\mathrm{PL},\chi}
= \bigoplus_{p \in \mathcal{P},\; p \nmid q}
\bigoplus_{k=1}^{K}\, \mathbb{C}\, |p,k\rangle,
where :math:`q` is the conductor of :math:`\chi` (primes dividing
:math:`q` are excluded because :math:`\chi(p^k) = 0` for those :math:`p`).
* ``H_freq`` is real-diagonal with entries
:math:`\nu_{f,(p,k)} = k\log p` for :math:`p \nmid q`, :math:`k \ge 1`.
The minimum is therefore
.. math::
\Delta_0^{(\chi)}
= \min_{p \nmid q,\, k \ge 1}\, k\log p
= \log\!\bigl(\min\{p \text{ prime} : p \nmid q\}\bigr).
For the three primitive real characters used in the L-track this
evaluates to:
* :math:`\chi_3` (:math:`q=3`): :math:`\Delta_0 = \log 2`
* :math:`\chi_4` (:math:`q=4`): :math:`\Delta_0 = \log 3`
(since :math:`2 \mid 4`)
* :math:`\chi_5` (:math:`q=5`): :math:`\Delta_0 = \log 2`
* ``H_coupling`` is real-symmetric (verified at construction in
:class:`tnfr.operators.hamiltonian.InternalHamiltonian`). The
**Kato-Rellich (Weyl) perturbation theorem** applied to bounded
symmetric perturbations of a self-adjoint diagonal operator gives
.. math::
\lambda_{\min}(\hat H)
\;\ge\; \Delta_0^{(\chi)}
- |J_0|\,\|\hat H_{\mathrm{coupling}}\|_{\mathrm{op}}.
When this number is positive, the perturbed χ-twisted Hamiltonian
is rigorously strictly positive on :math:`\mathcal{H}_{\mathrm{PL},\chi}`.
* Trace-class resolvent. On a finite-dimensional space every
operator is trivially trace-class. The certificate exposes the
Schatten-1 and Hilbert-Schmidt norms of
:math:`(\hat H + c\hat I)^{-1}` so that growth with
:math:`(N_{\mathrm{primes}}, K)` can be tracked exactly as for P26.
* Structural Lyapunov compatibility. Under the unitary flow
:math:`U(t) = e^{-it\hat H}` generated by the self-adjoint
:math:`\hat H`, the spectral energy
:math:`E_{\mathrm{spec}}[\psi] = \langle\psi|\hat H^2|\psi\rangle`
is exactly conserved. The certificate verifies this conservation
numerically on a battery of random initial states.
Honest scope
------------
What this module **does** establish, rigorously and quantitatively:
* a sufficient quantitative condition
.. math::
|J_0|\,\|\hat H_{\mathrm{coupling}}\|_{\mathrm{op}}
\;<\; \Delta_0^{(\chi)}
under which the **finite-dimensional** χ-twisted prime-ladder
Hamiltonian has strictly positive real spectrum with explicit
gap, real eigenvectors, unitary evolution, and trace-class
resolvent;
* a numerical certification routine identical to P26 returning the
actual spectral bottom, spectral gap, Schatten-1 and
Hilbert-Schmidt norms, and unitary conservation drifts.
What this module **does not** claim:
* it does **not** close gap G4-χ (GRH localisation for
:math:`L(s,\chi)`);
* the certificate is a statement about the finite-dimensional
χ-twisted prime-ladder Hamiltonian at fixed
:math:`(N_{\mathrm{primes}}, K)` for a single primitive real
character; passing to the analytic continuation introduces a
non-finite-dimensional limit whose spectrum (in particular the
localisation of resonance poles on :math:`\operatorname{Re}(s) = 1/2`)
is not addressed here;
* the Kato-Rellich bound is sharp only for small couplings —
beyond
:math:`|J_0|\,\|\hat H_{\mathrm{coupling}}\| \ge \Delta_0^{(\chi)}`
the certificate explicitly reports ``perturbation_safe = False``;
* the χ-twisted weight operator :math:`\hat W^{(\chi)}` is **not**
involved in the Lyapunov/positivity statement: the certificate is
about :math:`\hat H_{\mathrm{int}}` itself. The role of
:math:`\hat W^{(\chi)}` is exclusively spectral
(:math:`Z_{\mathrm{TNFR}}(s,\chi)`); positivity of
:math:`\hat H_{\mathrm{int}}` is independent of the character.
Status: EXPERIMENTAL — Research prototype for TNFR-Riemann P44 program
(χ-twisted structural positivity certificate, May 2026).
"""
from __future__ import annotations
import math
from dataclasses import dataclass
from ..mathematics.unified_numerical import np
from .lyapunov_spectral_positivity import (
_matrix_exponential_skew,
operator_norm,
resolvent_schatten_norms,
)
from .twisted_prime_ladder_hamiltonian import TwistedPrimeLadderHamiltonian
__all__ = [
"TwistedLyapunovSpectralCertificate",
"twisted_compute_spectrum",
"twisted_kato_rellich_lower_bound",
"twisted_verify_unitary_flow",
"compute_twisted_lyapunov_spectral_certificate",
]
# ---------------------------------------------------------------------------
# Atomic spectral primitives (χ-twisted)
# ---------------------------------------------------------------------------
def twisted_compute_spectrum(
bundle: TwistedPrimeLadderHamiltonian,
) -> np.ndarray:
r"""Eigenvalues of the P34 χ-twisted Hamiltonian, sorted ascending.
Identical machinery to :func:`compute_spectrum` for P14: forces
exact Hermitian symmetry then calls :func:`numpy.linalg.eigvalsh`.
Parameters
----------
bundle : TwistedPrimeLadderHamiltonian
Returns
-------
np.ndarray
Real array of eigenvalues, sorted ascending, of shape
``(N,)`` where ``N = n_active_primes * max_power``.
"""
H = bundle.hamiltonian.H_int
H_sym = 0.5 * (H + H.conj().T)
return np.linalg.eigvalsh(H_sym)
def twisted_kato_rellich_lower_bound(
bundle: TwistedPrimeLadderHamiltonian,
) -> dict[str, float]:
r"""Quantitative Kato-Rellich lower bound on the perturbed χ-twisted spectrum.
Returns the explicit numbers entering
.. math::
\lambda_{\min}(\hat H)
\;\ge\;
\min_n \nu_{f,n}
\;-\;|J_0|\,\|\hat H_{\mathrm{coupling}}\|_{\mathrm{op}}.
For the χ-twisted ladder the minimum of :math:`\nu_{f,n}` is
:math:`\log(\min\{p \nmid q\})` (in particular :math:`\log 3` for
:math:`\chi_4`, :math:`\log 2` for :math:`\chi_3` and
:math:`\chi_5`). The routine re-extracts it directly from
``H_freq`` so it remains agnostic to the character.
Parameters
----------
bundle : TwistedPrimeLadderHamiltonian
Returns
-------
dict
Keys ``unperturbed_gap``, ``coupling_norm``,
``perturbation_bound``, ``guaranteed_gap``,
``perturbation_safe``.
"""
H_freq = bundle.hamiltonian.H_freq
H_coup = bundle.hamiltonian.H_coupling
diag = np.asarray(np.diag(H_freq)).real.astype(float)
unperturbed_gap = float(diag.min())
coupling_norm = operator_norm(H_coup) if H_coup.size > 0 else 0.0
# H_coupling already has J_0 baked in (via graph attribute
# H_COUPLING_STRENGTH), exactly as in the untwisted P14 case.
perturbation_bound = coupling_norm
guaranteed_gap = unperturbed_gap - perturbation_bound
perturbation_safe = perturbation_bound < unperturbed_gap
return {
"unperturbed_gap": unperturbed_gap,
"coupling_norm": coupling_norm,
"perturbation_bound": perturbation_bound,
"guaranteed_gap": guaranteed_gap,
"perturbation_safe": bool(perturbation_safe),
}
def twisted_verify_unitary_flow(
bundle: TwistedPrimeLadderHamiltonian,
*,
n_states: int = 8,
times: tuple[float, ...] = (0.5, 1.0, 2.0),
seed: int = 0,
) -> dict[str, float]:
r"""Numerically certify unitarity of :math:`U(t) = e^{-it\hat H}` for the χ-twisted Hamiltonian.
For a self-adjoint :math:`\hat H`, the propagator is unitary at
every real :math:`t`, so for any unit-norm :math:`\psi_0` the
propagated state :math:`\psi(t) = U(t)\psi_0` satisfies
:math:`\|\psi(t)\| = 1` exactly, and the spectral energy
:math:`\langle\psi(t)|\hat H^2|\psi(t)\rangle` is preserved.
Parameters
----------
bundle : TwistedPrimeLadderHamiltonian
n_states : int, default 8
Number of random initial states.
times : tuple of float, default (0.5, 1.0, 2.0)
Propagation times to test.
seed : int, default 0
RNG seed.
Returns
-------
dict
Keys ``max_norm_drift``, ``max_energy_drift``, ``unitary``
(bool, both drifts below ``1e-10``), ``n_states``, ``n_times``.
"""
H = bundle.hamiltonian.H_int
H_sym = 0.5 * (H + H.conj().T)
H2 = H_sym @ H_sym
rng = np.random.default_rng(int(seed))
N = H_sym.shape[0]
max_norm_drift = 0.0
max_energy_drift = 0.0
for _ in range(int(n_states)):
re = rng.standard_normal(N)
im = rng.standard_normal(N)
psi0 = (re + 1j * im).astype(complex)
psi0 /= np.linalg.norm(psi0)
e0 = float(np.real(psi0.conj() @ H2 @ psi0))
for t in times:
U = _matrix_exponential_skew(H_sym, -float(t))
psi_t = U @ psi0
n_t = float(np.linalg.norm(psi_t))
e_t = float(np.real(psi_t.conj() @ H2 @ psi_t))
max_norm_drift = max(max_norm_drift, abs(n_t - 1.0))
denom = abs(e0) + 1e-30
max_energy_drift = max(max_energy_drift, abs(e_t - e0) / denom)
unitary = (max_norm_drift < 1.0e-10) and (max_energy_drift < 1.0e-10)
return {
"max_norm_drift": max_norm_drift,
"max_energy_drift": max_energy_drift,
"unitary": bool(unitary),
"n_states": int(n_states),
"n_times": int(len(times)),
}
# ---------------------------------------------------------------------------
# Certificate dataclass + driver
# ---------------------------------------------------------------------------
@dataclass(frozen=True)
class TwistedLyapunovSpectralCertificate:
r"""χ-twisted analogue of :class:`LyapunovSpectralCertificate`.
Same four ingredients as P26 (self-adjointness, positivity,
trace-class resolvent, unitary flow), reported on the χ-twisted
prime-ladder Hamiltonian.
Attributes
----------
character_name, character_modulus : str, int
Identity of the primitive real Dirichlet character used at
construction (:math:`\chi_3`, :math:`\chi_4`, :math:`\chi_5`).
n_primes, max_power, coupling, dimension : structural parameters
``n_primes`` is the number of **active** primes
(excluding primes dividing the modulus); ``dimension =
n_primes * max_power``.
spectrum_min, spectrum_max, spectral_gap : spectral extrema
all_real, all_positive : booleans
max_imaginary_part : float
Machine-precision residual.
schatten_1_norm, schatten_2_norm : float
:math:`\|(\hat H + I)^{-1}\|_1` and
:math:`\|(\hat H + I)^{-1}\|_2`.
unperturbed_gap, coupling_norm, perturbation_bound,
guaranteed_gap, perturbation_safe : Kato-Rellich data
max_norm_drift, max_energy_drift, unitary : unitary-flow data
structural_positivity : bool
``True`` iff ``spectrum_min > 0`` **and**
``perturbation_safe`` **and** ``unitary``.
notes : str
"""
character_name: str
character_modulus: int
n_primes: int
max_power: int
coupling: float
dimension: int
spectrum_min: float
spectrum_max: float
spectral_gap: float
all_real: bool
all_positive: bool
max_imaginary_part: float
schatten_1_norm: float
schatten_2_norm: float
operator_norm_inverse: float
shift: float
unperturbed_gap: float
coupling_norm: float
perturbation_bound: float
guaranteed_gap: float
perturbation_safe: bool
max_norm_drift: float
max_energy_drift: float
unitary: bool
structural_positivity: bool
notes: str = ""
def summary(self) -> str:
return (
"TwistedLyapunovSpectralCertificate("
f"chi={self.character_name}, q={self.character_modulus}, "
f"n_primes={self.n_primes}, "
f"max_power={self.max_power}, "
f"coupling={self.coupling:.3e}, "
f"dim={self.dimension}, "
f"min(lambda)={self.spectrum_min:.6e}, "
f"gap={self.spectral_gap:.6e}, "
f"||R||_1={self.schatten_1_norm:.6e}, "
f"||R||_2={self.schatten_2_norm:.6e}, "
f"unperturbed_gap={self.unperturbed_gap:.6e}, "
f"||V||={self.coupling_norm:.6e}, "
f"guaranteed_gap={self.guaranteed_gap:.6e}, "
f"perturbation_safe={self.perturbation_safe}, "
f"unitary={self.unitary}, "
f"structural_positivity={self.structural_positivity})"
)
def compute_twisted_lyapunov_spectral_certificate(
bundle: TwistedPrimeLadderHamiltonian,
*,
shift: float = 1.0,
n_unitary_states: int = 8,
unitary_times: tuple[float, ...] = (0.5, 1.0, 2.0),
seed: int = 0,
positivity_threshold: float = 1.0e-12,
) -> TwistedLyapunovSpectralCertificate:
r"""Driver: assemble all four positivity ingredients for the χ-twisted Hamiltonian.
Parameters
----------
bundle : TwistedPrimeLadderHamiltonian
shift : float, default 1.0
Shift :math:`c` for the resolvent Schatten norms. Must
dominate ``-spectrum.min()``.
n_unitary_states, unitary_times, seed : forwarded to
:func:`twisted_verify_unitary_flow`.
positivity_threshold : float, default 1e-12
Threshold under which a negative eigenvalue is treated as a
round-off zero.
Returns
-------
TwistedLyapunovSpectralCertificate
"""
spectrum = twisted_compute_spectrum(bundle)
N = int(spectrum.size)
spectrum_min = float(spectrum.min())
spectrum_max = float(spectrum.max())
spectral_gap = spectrum_min
all_real = True
all_positive = bool(spectrum_min > -positivity_threshold)
max_imag = 0.0
schatten = resolvent_schatten_norms(spectrum, shift=shift)
kr = twisted_kato_rellich_lower_bound(bundle)
unit = twisted_verify_unitary_flow(
bundle,
n_states=n_unitary_states,
times=unitary_times,
seed=seed,
)
structural_positivity = bool(
all_positive and kr["perturbation_safe"] and unit["unitary"]
)
notes = (
f"Decoupled limit (J_0=0): H_int = diag(k log p) for p not "
f"dividing q={bundle.character_modulus}; spectrum trivially "
"strictly positive with explicit gap log(min p not dividing q). "
"Perturbed regime covered by Kato-Rellich envelope reported in "
"(unperturbed_gap, coupling_norm, guaranteed_gap). "
"Does NOT close gap G4-chi (GRH localisation on Re(s)=1/2 for "
"L(s, chi))."
)
coupling = float(bundle.coupling)
n_primes_active = int(bundle.spectrum.primes_active.size)
max_power = int(bundle.spectrum.max_power)
return TwistedLyapunovSpectralCertificate(
character_name=str(bundle.character_name),
character_modulus=int(bundle.character_modulus),
n_primes=n_primes_active,
max_power=max_power,
coupling=coupling,
dimension=N,
spectrum_min=spectrum_min,
spectrum_max=spectrum_max,
spectral_gap=spectral_gap,
all_real=all_real,
all_positive=all_positive,
max_imaginary_part=max_imag,
schatten_1_norm=schatten["schatten_1_norm"],
schatten_2_norm=schatten["schatten_2_norm"],
operator_norm_inverse=schatten["operator_norm_inverse"],
shift=schatten["shift"],
unperturbed_gap=kr["unperturbed_gap"],
coupling_norm=kr["coupling_norm"],
perturbation_bound=kr["perturbation_bound"],
guaranteed_gap=kr["guaranteed_gap"],
perturbation_safe=kr["perturbation_safe"],
max_norm_drift=unit["max_norm_drift"],
max_energy_drift=unit["max_energy_drift"],
unitary=unit["unitary"],
structural_positivity=structural_positivity,
notes=notes,
)
# Suppress unused-import lint on ``math``: kept for parity with P26
# (the reference threshold log(2) is reported by callers, not here).
_ = math