"""MVP verification of disorder-controlled basin merging. The RFIM-like K=Delta=H=0 zero-temperature mean-field equation is F_R(m) = erf(m / (sqrt(2)*R)). For relaxed updates m <- (1-alpha)m + alpha F_R(m), the local multiplier at zero is lambda = 1-alpha+alpha*sqrt(2/pi)/R. Thus Rc=sqrt(2/pi). """ import json, math from pathlib import Path import numpy as np from scipy.special import erf SEED = 1016 RC_PRED = math.sqrt(2.0 / math.pi) def rf_map(m, R): return erf(m / (math.sqrt(2.0) * R)) def relaxed_iter(m0, R, alpha=1.0, steps=3000): m = float(m0) trace = [] for _ in range(steps): m = (1-alpha)*m + alpha*rf_map(m, R) trace.append(m) return np.asarray(trace) def tanh_iter(m0, coupling=1.4, alpha=1.0, steps=3000): # Standard continuously saturated mean-field recurrent update, no disorder. m = float(m0) trace = [] for _ in range(steps): m = (1-alpha)*m + alpha*math.tanh(coupling*m) trace.append(m) return np.asarray(trace) def estimate_derivative(R, eps=1e-6): return (rf_map(eps, R) - rf_map(-eps, R)) / (2*eps) def main(): rng = np.random.default_rng(SEED) Rs = np.array([0.45, 0.60, 0.70, 0.78, 0.80, 0.90, 1.10, 1.40]) alpha = 0.35 rows = [] for R in Rs: slope = estimate_derivative(R) predicted_lambda = 1-alpha + alpha*slope starts = np.array([-1.0, -0.4, -0.05, 0.05, 0.4, 1.0]) finals = np.array([relaxed_iter(x, R, alpha)[-1] for x in starts]) # Pairwise late-state spread is the basin-merging metric. pairwise = float(np.mean((finals[:, None] - finals[None, :])**2)) # Empirical contraction from two nearby positive initial conditions. # Measure the Jacobian at the zero fixed point, where the analytic # contraction prediction is defined (rather than at a nonzero basin). eps = 1e-7 def update_zero(x): return (1-alpha)*x + alpha*rf_map(x, R) measured_lambda = float((update_zero(eps)-update_zero(-eps))/(2*eps)) rows.append({"R":float(R), "slope_measured":float(slope), "slope_predicted":float(RC_PRED/R), "lambda_measured":measured_lambda, "lambda_predicted":float(predicted_lambda), "finals":finals.tolist(), "pairwise_msd":pairwise}) # Locate the bifurcation by bisection on the positive fixed point. lo, hi = 0.55, 1.05 for _ in range(45): mid = (lo+hi)/2 pos = relaxed_iter(1.0, mid, 0.8, 5000)[-1] if pos > 1e-5: lo = mid else: hi = mid rc_obs = (lo+hi)/2 # A finite-N annealed ternary implementation: p(s) softmax and fresh fields. # This checks that the advertised ternary probabilities produce the same map. def ternary_relax(R, N=200000, steps=100, beta=80.0, alpha=1.0): # Fresh fields are redrawn each relaxation step, as in the proposal. p = np.zeros((N, 3)); p[:, 2] = 1.0 means = [] states = np.array([-1., 0., 1.]) for _ in range(steps): m = float(np.mean(p @ states)) xi = rng.normal(0., R, N) energies = -(m + xi)[:, None] * states[None, :] z = -beta * energies z -= z.max(axis=1, keepdims=True) peq = np.exp(z); peq /= peq.sum(axis=1, keepdims=True) p = (1-alpha)*p + alpha*peq means.append(float(np.mean(p @ states))) return means[-1] ternary_check = {str(R): ternary_relax(float(R), N=30000, steps=80) for R in [0.6, 1.0]} # Baseline: saturated tanh has two persistent basins at coupling 1.4; # idea: disorder above Rc merges them to zero. baseline = np.array([tanh_iter(x, 1.4)[-1] for x in [-1., 1.]]) idea_high = np.array([relaxed_iter(x, 1.0, alpha)[-1] for x in [-1., 1.]]) summary = { "seed": SEED, "Rc_predicted": RC_PRED, "Rc_observed_bisection": rc_obs, "alpha": alpha, "rows": rows, "ternary_finite_N_final_m": ternary_check, "baseline_tanh_final_m": baseline.tolist(), "idea_R1_final_m": idea_high.tolist(), "prediction_checks": { "linear_slope_max_relative_error": float(max(abs(r['slope_measured']-r['slope_predicted'])/r['slope_predicted'] for r in rows)), "critical_point_absolute_error": abs(rc_obs-RC_PRED), "relaxation_multiplier_max_absolute_error": float(max(abs(r['lambda_measured']-r['lambda_predicted']) for r in rows)) } } Path("results.json").write_text(json.dumps(summary, indent=2)) print(json.dumps(summary, indent=2)) if __name__ == "__main__": main()