Nonlinear Noise-Tightening Drift / run_experiment.py

Failed on benchmark

Raw ⬇ ZIP
 1import json
 2import numpy as np
 3from scipy.integrate import quad
 4from nonlinear_drift import scalar_pairwise_rate
 5
 6LAM, D = 1.0, 0.5
 7
 8
 9def exact_variance(beta):
10    def unnorm(x):
11        return np.exp(-(LAM*x*x/2.0 + beta*x**4/4.0)/D)
12    z = quad(unnorm, -np.inf, np.inf, epsabs=1e-12, epsrel=1e-12)[0]
13    m2 = quad(lambda x: x*x*unnorm(x), -np.inf, np.inf,
14              epsabs=1e-12, epsrel=1e-12)[0] / z
15    return m2
16
17
18def simulate(beta, seed=123, n=4000, burn=3000, sample_steps=7000, dt=.005):
19    rng = np.random.default_rng(seed)
20    x = np.zeros(n)
21    for _ in range(burn):
22        x += (-LAM*x - beta*x**3)*dt + np.sqrt(2*D*dt)*rng.normal(size=n)
23    vals = []
24    for k in range(sample_steps):
25        x += (-LAM*x - beta*x**3)*dt + np.sqrt(2*D*dt)*rng.normal(size=n)
26        if k % 10 == 0:
27            vals.append(np.mean(x*x))
28    return float(np.mean(vals)), float(np.std(vals) / np.sqrt(len(vals)))
29
30
31def main():
32    wide_betas = np.array([0., .02, .05, .1, .2, .5, 1.0])
33    exact_wide = np.array([exact_variance(b) for b in wide_betas])
34    baseline = D / LAM
35    # First-order perturbation: Var/baseline = 1 - 3 beta D/lambda^2 + O(beta^2).
36    tiny_betas = np.array([0., .0001, .0005, .001, .002, .005])
37    exact_tiny = np.array([exact_variance(b) for b in tiny_betas])
38    relative = exact_tiny / baseline - 1.0
39    slope = float(np.polyfit(tiny_betas, relative, 1)[0])
40    predicted_slope = -3 * D / LAM**2
41
42    # Prediction 1: pairwise rate is exactly >= lambda, equality only at x=y=0.
43    rng = np.random.default_rng(7)
44    x, y = rng.normal(size=100000), rng.normal(size=100000)
45    rates = scalar_pairwise_rate(x, y, LAM, .5)
46    contraction = {
47        "predicted_min_rate": LAM,
48        "observed_min_rate": float(rates.min()),
49        "observed_fraction_below_lambda": float(np.mean(rates < LAM - 1e-12)),
50        "rate_at_equal_radius_1": float(scalar_pairwise_rate(1., 1., LAM, .5)),
51        "predicted_rate_at_equal_radius_1": LAM + 3*.5
52    }
53    sims = {str(b): simulate(float(b), seed=100 + i) for i, b in enumerate([0., .5])}
54    out = {
55        "parameters": {"lambda": LAM, "D": D, "baseline_variance": baseline},
56        "prediction_1_contraction": contraction,
57        "prediction_2_small_beta_slope": {
58            "prediction": "relative variance slope = -3D/lambda^2 + O(beta)",
59            "predicted_relative_slope": predicted_slope,
60            "observed_relative_slope": slope,
61            "relative_error": abs(slope - predicted_slope) / abs(predicted_slope),
62            "betas": tiny_betas.tolist(),
63            "exact_variances": exact_tiny.tolist()
64        },
65        "prediction_3_monotonic_variance": {
66            "prediction": "variance strictly decreases as beta increases",
67            "betas": wide_betas.tolist(), "exact_variances": exact_wide.tolist(),
68            "strictly_decreasing": bool(np.all(np.diff(exact_wide) < 0)),
69            "relative_gap_at_beta_0.5": float(1 - exact_wide[5] / baseline)
70        },
71        "matched_euler_maruyama": {
72            "linear_beta_0": sims["0.0"], "cubic_beta_0.5": sims["0.5"],
73            "simulated_relative_gap": float(1 - sims["0.5"][0] / sims["0.0"][0]),
74            "dt": .005, "burn_steps": 3000, "sample_steps": 7000, "n_parallel": 4000
75        }
76    }
77    with open("results.json", "w") as f:
78        json.dump(out, f, indent=2)
79    print(json.dumps(out, indent=2))
80
81if __name__ == "__main__":
82    main()