Persistent Hamiltonian categorical sampler / sampler_experiment.py

Failed on benchmark

Raw ⬇ ZIP
 1import json
 2import math
 3import numpy as np
 4
 5
 6def skew_balance_check(seed=0, n=10000):
 7    rng = np.random.default_rng(seed)
 8    errors = []
 9    for _ in range(n):
10        U = rng.normal(0, 5)
11        du = rng.normal(0, 4)
12        p = abs(rng.normal()) + 1e-12
13        qf = p * math.exp(-du / 2.0)
14        qr = p * math.exp(du / 2.0)
15        errors.append(abs((math.log(qf) - U) -
16                          (math.log(qr) - (U + du))))
17    return float(max(errors)), float(np.mean(errors))
18
19
20def simulate(rho, events=160000, burn=5000, seed=1, nring=128):
21    rng = np.random.default_rng(seed)
22    p = rng.normal()
23    x = 0
24    signs, waits, positions = [], [], []
25    for k in range(events + burn):
26        rate = abs(p)
27        dt = rng.exponential(1.0 / rate) if rate > 1e-14 else 0.0
28        direction = 1 if p >= 0 else -1
29        x = (x + direction) % nring
30        p = rho * p + math.sqrt(max(0.0, 1.0 - rho * rho)) * rng.normal()
31        if k >= burn:
32            signs.append(direction)
33            waits.append(dt)
34            positions.append(x)
35    signs, waits, positions = map(np.asarray, (signs, waits, positions))
36    corr = float(np.mean(signs[:-1] * signs[1:]))
37    run_length = float(1.0 / (1.0 - corr))
38    centered = signs - signs.mean()
39    var = np.mean(centered * centered)
40    ac = [float(np.mean(centered[:-lag] * centered[lag:]) / var)
41          for lag in range(1, 100)]
42    sign_tau = float(1.0 + 2.0 * sum(a for a in ac if a > 0))
43    angle = 2 * math.pi * positions / nring
44    obs = np.cos(angle)
45    c = obs - obs.mean()
46    v = np.mean(c*c)
47    acx = [float(np.mean(c[:-lag] * c[lag:]) / v)
48           for lag in range(1, 100)]
49    x_tau = float(1.0 + 2.0 * sum(a for a in acx if a > 0))
50    return {
51        "rho": rho, "sign_corr": corr, "run_length": run_length,
52        "sign_tau": sign_tau, "position_tau": x_tau,
53        "mean_wait": float(waits.mean()),
54        "event_rate_per_time": float(len(waits) / waits.sum())
55    }
56
57
58def main():
59    max_err, mean_err = skew_balance_check()
60    # For Gaussian OU-refresh momenta, corr(sign_t, sign_{t+1})
61    # = 2 asin(rho)/pi. Hence mean consecutive same-sign run length is
62    # 1/(1-corr). These are direct mechanism predictions.
63    rows = []
64    for rho in [0.0, 0.25, 0.5, 0.75, 0.9]:
65        out = simulate(rho, seed=100 + int(100 * rho))
66        out["pred_corr"] = 2.0 / math.pi * math.asin(rho)
67        out["pred_run_length"] = 1.0 / (1.0 - out["pred_corr"])
68        out["corr_abs_error"] = abs(out["sign_corr"] - out["pred_corr"])
69        rows.append(out)
70    result = {
71        "skew_balance_max_abs_log_error": max_err,
72        "skew_balance_mean_abs_log_error": mean_err,
73        "prediction_rows": rows,
74        "baseline_rho0_position_tau": rows[0]["position_tau"],
75        "idea_rho075_position_tau": rows[3]["position_tau"],
76        "note": "Event-rate is reported empirically; per-event Gaussian refresh has heavy-tailed waiting times, so E|p| is not the event rate."
77    }
78    with open("results.json", "w") as f:
79        json.dump(result, f, indent=2)
80    print(json.dumps(result, indent=2))
81
82
83if __name__ == "__main__":
84    main()