"""
n14 — The classical channel, priced. Two quantum masses (slow, below-band:
the adiabatic/gravity regime) coupled linearly to a 1D scalar field, BMV-
delocalized (x-squeezed, spread x55). The vacuum "keeps records": local
dephasing D[phi] at rate kappa on the channel window.

MEASURED (July 2026):
 - force transmission is EXACTLY blind to monitoring (<x_A> identical to
   5 digits from kappa=0 to 0.3; first moments ignore D[x] -- a theorem);
 - the quantum channel entangles (E_N ~ 1.2, long-range d=8..20); the
   monitored channel kills E_N at finite kappa* with ~6% heating;
 - at the death threshold the record rate about the masses saturates the
   Kafri-Taylor-Milburn bound: Lambda* = |K_eff|/2 within 2% across the
   fiducial series (0.539/0.528/0.520/0.536 at d=8/12/16/20).
In real units (K = 2Gm^2/d^3) this floor is the regularized Diosi-Penrose
rate: {intact force, null BMV, DP-floor decoherence} is one channel
theorem, demonstrated end-to-end in an extended field with propagation.

Exact dynamics via diagonalization of the drift; exact NESS via Lyapunov.
Runtime: ~2 minutes.
"""

import numpy as np
from scipy.linalg import solve_continuous_lyapunov, eigh, eig

N       = 192
NT      = N + 2
MU2     = 2.5e-3          # mu = 0.05, portee 20
LABS    = 24
ETA_MAX = 0.5
ETA_B   = 0.005
ETA_M   = 1e-4            # amortissement residuel minuscule des masses
OMEGA_M = 0.04            # slow masses, below band: adiabatic (gravity) regime
SMEAR   = 2.0
CENTER  = N // 2

def cgrad(site):
    idx = np.arange(N)
    g_ = np.exp(-0.5 * ((idx - site) / SMEAR) ** 2)
    g_[np.abs(idx - site) > 4 * SMEAR] = 0.0
    c = np.zeros(N); c[:-1] = np.diff(g_)
    return c / np.linalg.norm(c)

def K_total(a, b, g):
    K = np.zeros((NT, NT))
    K[:N, :N] = np.diag(np.full(N, 2.0 + MU2)) \
        - np.diag(np.ones(N - 1), 1) - np.diag(np.ones(N - 1), -1)
    K[N, N] = K[N + 1, N + 1] = OMEGA_M ** 2
    ca, cb = cgrad(a), cgrad(b)
    K[:N, N] = K[N, :N] = g * ca
    K[:N, N + 1] = K[N + 1, :N] = g * cb
    return K

def K_eff(a, b, g):
    Kf = K_total(a, b, 0.0)[:N, :N]
    return g * g * (cgrad(a) @ np.linalg.solve(Kf, cgrad(b)))

def ground_cov_of(K):
    w2, U = eigh(K)
    if w2.min() <= 0:
        raise RuntimeError(f"instable: w2_min = {w2.min():.3e}")
    w = np.sqrt(w2)
    sig = np.zeros((2 * len(K), 2 * len(K)))
    n = len(K)
    sig[:n, :n] = U @ np.diag(0.5 / w) @ U.T
    sig[n:, n:] = U @ np.diag(0.5 * w) @ U.T
    return sig

def drift_diff(K, kappa, win):
    eta = np.full(NT, ETA_B)
    for j in range(LABS):
        s = ((LABS - j) / LABS) ** 2
        eta[j] += ETA_MAX * s
        eta[N - 1 - j] += ETA_MAX * s
    eta[N] = eta[N + 1] = ETA_M
    E = np.diag(np.concatenate([eta, eta]))
    A_H = np.zeros((2 * NT, 2 * NT))
    A_H[:NT, NT:] = np.eye(NT)
    A_H[NT:, :NT] = -K
    A = A_H - E / 2
    sgs = ground_cov_of(K)
    D = 0.5 * (E @ sgs + sgs @ E)
    if kappa > 0:
        for i in range(*win):
            D[NT + i, NT + i] += kappa
    return A, D

def sigma0_product(a, b, g, r=0.0):
    """Etat produit : champ dans SON vide (g=0), masses dans leur vide
    comprime en x (delocalisation BMV : etalement e^{2r})."""
    sig = np.zeros((2 * NT, 2 * NT))
    Kf = K_total(a, b, 0.0)[:N, :N]
    sf = ground_cov_of(Kf)
    ix = list(range(N)) + list(range(NT, NT + N))
    sig[np.ix_(ix, ix)] = sf
    for k in (N, N + 1):
        sig[k, k] = np.exp(2 * r) * 0.5 / OMEGA_M
        sig[NT + k, NT + k] = np.exp(-2 * r) * 0.5 * OMEGA_M
    return sig

def evolve_setup(A):
    lam, V = eig(A)
    Vinv = np.linalg.inv(V)
    return lam, V, Vinv

def sigma_t(t, lam, V, Vinv, dsig0, siginf):
    P = V @ np.diag(np.exp(lam * t)) @ Vinv
    return (P @ dsig0 @ P.T).real + siginf

def log_negativity(sig):
    idx = [N, N + 1, NT + N, NT + N + 1]
    s = sig[np.ix_(idx, idx)]
    Amat = s[np.ix_([0, 2], [0, 2])]
    Bmat = s[np.ix_([1, 3], [1, 3])]
    Cmat = s[np.ix_([0, 2], [1, 3])]
    dA, dB, dC, dS = map(np.linalg.det, (Amat, Bmat, Cmat, s))
    delta = dA + dB - 2 * dC
    disc = delta ** 2 - 4 * dS
    if disc < 0: return 0.0
    nu2 = (delta - np.sqrt(disc)) / 2
    if nu2 <= 0: return 0.0
    return max(0.0, -np.log(2 * np.sqrt(nu2)))

def run(d, g, kappa, tmax=120.0, nt=40, r=0.0):
    a, b = CENTER - d // 2, CENTER - d // 2 + d
    K = K_total(a, b, g)
    win = (a - 10, b + 10)
    A, D = drift_diff(K, kappa, win)
    siginf = solve_continuous_lyapunov(A, -D)
    sig0 = sigma0_product(a, b, g, r)
    lam, V, Vinv = evolve_setup(A)
    dsig0 = sig0 - siginf
    ts = np.linspace(0, tmax, nt + 1)[1:]
    EN, heatA = [], []
    for t in ts:
        s = sigma_t(t, lam, V, Vinv, dsig0, siginf)
        EN.append(log_negativity(s))
        heatA.append(s[NT + N, NT + N] - 0.5 * OMEGA_M
                     + OMEGA_M ** 2 * (s[N, N] - 0.5 / OMEGA_M))
    # transmission de force : B deplace de x0, acceleration initiale de A
    X0 = np.zeros(2 * NT); X0[N + 1] = 1.0     # <x_B> = 1
    t_f = 30.0
    P = (V @ np.diag(np.exp(lam * t_f)) @ Vinv).real
    xA = (P @ X0)[N]
    return np.array(ts), np.array(EN), np.array(heatA), xA


if __name__ == "__main__":
    import numpy as np
    G_FID, r = 0.00960, 2.0

    print("=== quantum channel (kappa=0): long-range entanglement ===")
    for d in (8, 12, 16, 20):
        a, b = CENTER - d // 2, CENTER - d // 2 + d
        ts, EN, hA, xA = run(d, G_FID, 0.0, tmax=600.0, nt=50, r=r)
        print(f"d={d:2d}  K_eff={K_eff(a,b,G_FID):+.3e}  maxE_N={EN.max():.3f}")

    print("\n=== monitored channel (d=12): force blind, E_N dies ===")
    for kap in (0.0, 1e-4, 3e-4, 1e-3, 3e-3, 1e-2):
        ts, EN, hA, xA = run(12, G_FID, kap, tmax=600.0, nt=50, r=r)
        print(f"kappa={kap:<7} maxE_N={EN.max():.4f}  heat_A={hA[-1]:+.3e}  <x_A>={xA:+.6f}")

    print("\n=== the KTM bound: Lambda* vs |K_eff|/2 across 8 configs ===")
    def lam_record(d, g, kappa):
        a, b = CENTER - d//2, CENTER - d//2 + d
        Kf = K_total(a, b, 0.0)[:N, :N]
        imprint = g * np.linalg.solve(Kf, cgrad(a))
        return kappa * np.sum(imprint[a-10:b+10]**2)
    def maxEN(d, g, kappa):
        return run(d, g, kappa, tmax=600.0, nt=50, r=r)[1].max()
    def kstar(d, g, eps=1e-3):
        lo, hi = 0.0, 4e-3
        while maxEN(d, g, hi) > eps: hi *= 2
        for _ in range(14):
            mid = 0.5*(lo+hi)
            if maxEN(d, g, mid) > eps: lo = mid
            else: hi = mid
        return 0.5*(lo+hi)
    for d, g in ((8, G_FID), (12, G_FID), (16, G_FID), (20, G_FID),
                 (12, 0.7*G_FID), (12, 0.5*G_FID), (16, 0.7*G_FID), (8, 0.5*G_FID)):
        a, b = CENTER - d//2, CENTER - d//2 + d
        Keff = abs(K_eff(a, b, g))
        ks = kstar(d, g)
        lam = lam_record(d, g, ks)
        print(f"d={d:2d} g={g:.5f}  |K|={Keff:.3e}  kappa*={ks:.3e}  Lambda*/|K|={lam/Keff:.3f}")
