Pisot-Orbit Deterministic JL Layer / experiment.py

Mechanism failed

Raw ⬇ ZIP
  1import json, math, hashlib
  2from pathlib import Path
  3import numpy as np
  4
  5SEED = 12345
  6rng = np.random.default_rng(SEED)
  7
  8# Golden ratio is a Pisot number (its conjugate is -1/phi).
  9PHI = (1.0 + math.sqrt(5.0)) / 2.0
 10
 11
 12def beta_orbit(n, u0, beta=PHI):
 13    u = float(u0)
 14    out = np.empty(n, dtype=np.float64)
 15    for k in range(n):
 16        out[k] = u
 17        u = beta * u
 18        u -= math.floor(u)
 19    return out
 20
 21
 22def orbit_coefficients(n, u0, burn=10000):
 23    # Estimate empirical centering/scaling on a separate long orbit, as proposed.
 24    calib = beta_orbit(burn + n, u0, PHI)[burn:]
 25    mu, sigma = float(calib.mean()), float(calib.std())
 26    if sigma == 0:
 27        raise ValueError('degenerate orbit')
 28    vals = beta_orbit(n + burn, u0, PHI)[burn:]
 29    return (vals - mu) / sigma, mu, sigma
 30
 31
 32def make_projection(d, m, u0, gap=1, offset=0, coeff_n=300000):
 33    # Rows are z[offset + r*gap + i], exactly matching the proposed construction.
 34    need = offset + (m - 1) * gap + d + 1
 35    z, mu, sigma = orbit_coefficients(max(need, coeff_n), u0)
 36    P = np.empty((m, d), dtype=np.float64)
 37    for r in range(m):
 38        start = offset + r * gap
 39        P[r] = z[start:start+d] / math.sqrt(m)
 40    return P, mu, sigma
 41
 42
 43def pair_distortion(P, X):
 44    D = X[:, None, :] - X[None, :, :]
 45    orig = np.sum(D * D, axis=2)
 46    proj = np.einsum('nkd,md->nkm', D, P)
 47    proj = np.sum(proj * proj, axis=2)
 48    mask = orig > 1e-12
 49    ratios = proj[mask] / orig[mask]
 50    return float(np.max(np.abs(ratios - 1))), float(np.mean(np.abs(ratios - 1)))
 51
 52
 53def alpha_calibrate(P, X):
 54    a = np.sum(X * X)
 55    b = np.sum((X @ P.T) ** 2)
 56    return math.sqrt(a / b)
 57
 58
 59def corr_decay(z, maxlag=1000):
 60    z = z - z.mean()
 61    den = np.dot(z, z)
 62    cs = np.array([np.dot(z[:-k], z[k:]) / den for k in range(1, maxlag+1)])
 63    # Fit log envelope to lag 10..maxlag, robustly excluding zeros.
 64    lags = np.arange(1, maxlag+1)
 65    env = np.maximum.accumulate(np.abs(cs)[::-1])[::-1] # conservative not useful for fit
 66    sel = (lags >= 10) & (np.abs(cs) > 1e-5)
 67    slope = np.polyfit(lags[sel], np.log(np.abs(cs[sel])), 1)[0]
 68    return cs, float(math.exp(slope))
 69
 70
 71def variance_sweep(d=32, ms=(8,16,32,64,128), trials=3000):
 72    # Random isotropic vectors make the norm estimator's variance a direct JL diagnostic.
 73    out=[]
 74    for m in ms:
 75        vals=[]
 76        for t in range(trials):
 77            u0 = (0.017 + (t+1)*0.61803398875) % 1.0
 78            P,_,_ = make_projection(d,m,u0,gap=1,coeff_n=5000)
 79            x = rng.normal(size=d); x /= np.linalg.norm(x)
 80            vals.append(np.sum((P @ x)**2))
 81        vals=np.asarray(vals)
 82        out.append((m,float(vals.mean()),float(vals.var()),float(vals.var()*m)))
 83    return out
 84
 85
 86def main():
 87    d,m=24,16
 88    u0=0.3141592653
 89    z,mu,sigma=orbit_coefficients(120000,u0)
 90    cs,rho=corr_decay(z,500)
 91    # Prediction 1: gap-dependent correlation should decrease geometrically.
 92    gaps=[1,2,4,8,16,32]
 93    gap_corr=[]
 94    for g in gaps:
 95        # Correlation of same-column coefficients in adjacent rows.
 96        vals=np.array([np.corrcoef(z[:-g],z[g:])[0,1]])
 97        gap_corr.append((g,float(abs(vals[0])),float(abs(cs[g-1]))))
 98
 99    # Calibration search prediction: optimize finite seed/gap and compare to fixed choice.
100    X=rng.normal(size=(18,d)); X -= X.mean(0); X /= X.std(0)
101    candidates=[]
102    for si in range(30):
103        seed=(0.0314159 + si*0.2718281) % 1.0
104        for g in [1,2,4,8,16,32]:
105            P,_,_=make_projection(d,m,seed,gap=g,coeff_n=3000)
106            a=alpha_calibrate(P,X)
107            md,ad=pair_distortion(a*P,X)
108            candidates.append((md,ad,si,g,a))
109    candidates.sort()
110    best=candidates[0]
111    fixed=[x for x in candidates if x[1] == min(y[1] for y in candidates)][:1]
112    # fixed is not a fair baseline; use first seed gap=1 explicitly.
113    P0,_,_=make_projection(d,m,0.0314159,gap=1,coeff_n=3000)
114    a0=alpha_calibrate(P0,X); base=pair_distortion(a0*P0,X)
115
116    var=variance_sweep()
117    result={
118      'seed':SEED,'beta':PHI,'orbit_mu':mu,'orbit_sigma':sigma,
119      'prediction_correlation':{'claim':'|corr(z_t,z_{t+g})| decreases approximately geometrically with gap','fit_rho_per_step':rho,'observed':gap_corr},
120      'prediction_variance':{'claim':'Var(||Px||^2) approximately proportional to 1/m','observed_m_mean_var_mvar':var,'loglog_slope':float(np.polyfit(np.log([x[0] for x in var]),np.log([x[2] for x in var]),1)[0])},
121      'prediction_search':{'claim':'seed/gap calibration search lowers worst pairwise distortion','fixed_seed_gap1':{'max':base[0],'mean':base[1]},'best':{'max':best[0],'mean':best[1],'seed_index':best[2],'gap':best[3],'alpha':best[4]}},
122      'reproducibility_hash':hashlib.sha256(np.asarray(make_projection(d,m,0.123456,gap=7,coeff_n=3000)[0],dtype=np.float64).tobytes()).hexdigest()
123    }
124    Path('results.json').write_text(json.dumps(result,indent=2))
125    print(json.dumps(result,indent=2))
126
127if __name__=='__main__': main()