Download mb_stats.py from ChatterjeeLab/ScooBDoob: direct link, hf CLI and curl.
- Browser
- Download file 4.54 kB
-
https://huggingface.co/ChatterjeeLab/ScooBDoob/resolve/main/mb_stats.py
- Command line
-
hf download hf://ChatterjeeLab/ScooBDoob/mb_stats.py
-
curl -L -o mb_stats.py https://huggingface.co/ChatterjeeLab/ScooBDoob/resolve/main/mb_stats.py
4.54 kB
| import numpy as np, json | |
| from scoobdoob.validation import msm_build as mb, tpt, bridge | |
| from scoobdoob.validation import mb_ground_truth as gt | |
| from scoobdoob.validation import mb_data as V | |
| import make_mb_panels as MP | |
| LAG, N, TL, ALPHA = MP.LAG_OPT, MP.N_OPT, MP.TICA_LAG, 1.5 | |
| CLUST_SEEDS = list(range(20)) | |
| PATH_SEEDS = [0, 1, 2] | |
| NPATH = 2000 | |
| sad = gt.saddle_points() | |
| S1 = sad[np.argmin(sad[:, 0])] | |
| trajs = V.load_trajs(str(V.DEFAULT_DATA)) | |
| raw_m, fk_m, within = [], [], [] | |
| for cs in CLUST_SEEDS: | |
| model = mb.build_full_msm( | |
| trajs, trajs, lag=LAG, tica_lag=TL, k=V.K, n_comp=2, seed=cs, use_tica=True | |
| ) | |
| A, B, _ = mb.q2_sign_endpoints(model) | |
| start, end = V.orient_endpoints(model, A, B) | |
| xy = model.state_xy | |
| _, q, _ = tpt.committor_agreement(model, start, end) | |
| rf = tpt.reactive_flux(model, start, end) | |
| thru = np.asarray(rf.gross_flux).sum(1) | |
| share = thru / thru.sum() | |
| on = share > 0.5 / model.n_active | |
| on[list(start)] = True | |
| on[list(end)] = True | |
| Axy = model.active_to_xy(start).mean(0) | |
| Bxy = model.active_to_xy(end).mean(0) | |
| land = np.vstack([Axy, Bxy, sad]) | |
| lo = land.min(0) - 0.35 | |
| hi = land.max(0) + 0.35 | |
| on = on & ( | |
| (xy[:, 0] >= lo[0]) | |
| & (xy[:, 0] <= hi[0]) | |
| & (xy[:, 1] >= lo[1]) | |
| & (xy[:, 1] <= hi[1]) | |
| ) | |
| labels = np.concatenate(model.dtrajs) | |
| counts = np.bincount(labels, minlength=model.n_active)[: model.n_active].astype( | |
| float | |
| ) | |
| rho = np.maximum(counts, 1) / counts.mean() | |
| react = (q > 0.05) & (q < 0.95) | |
| wfk = np.exp(-LAG * ALPHA * np.maximum(rho, np.percentile(rho[react], 25))) | |
| wfk[~on] = 0.0 | |
| wraw = np.ones_like(wfk) | |
| wraw[~on] = 0.0 | |
| n1 = np.linalg.norm(xy - S1, axis=1) < 0.25 | |
| Kraw = bridge.doob_kernels(model.P, end, N, w=wraw) | |
| Kfk = bridge.doob_kernels(model.P, end, N, w=wfk) | |
| r_ps, f_ps = [], [] | |
| for psd in PATH_SEEDS: | |
| pr = bridge.sample_bridge_paths(Kraw, start, n_paths=NPATH, seed=psd) | |
| pf = bridge.sample_bridge_paths(Kfk, start, n_paths=NPATH, seed=psd) | |
| r_ps.append(np.mean([any(n1[s] for s in p) for p in pr]) * 100) | |
| f_ps.append(np.mean([any(n1[s] for s in p) for p in pf]) * 100) | |
| raw_m.append(np.mean(r_ps)) | |
| fk_m.append(np.mean(f_ps)) | |
| within.append((np.std(r_ps), np.std(f_ps))) | |
| print( | |
| f" [clust seed {cs:2d}] raw {raw_m[-1]:5.1f}% fk {fk_m[-1]:5.1f}% " | |
| f"diff {fk_m[-1] - raw_m[-1]:+5.1f} (path-seed sd {within[-1][0]:.1f}/{within[-1][1]:.1f})" | |
| ) | |
| raw = np.array(raw_m) | |
| fk = np.array(fk_m) | |
| d = fk - raw | |
| n = len(d) | |
| print( | |
| f"\n--- variance decomposition (n={n} clustering seeds, {len(PATH_SEEDS)} path seeds, " | |
| f"{NPATH} paths each) ---" | |
| ) | |
| wr = np.mean([w[0] for w in within]) | |
| wf = np.mean([w[1] for w in within]) | |
| print(f"between-clustering sd : raw {raw.std(ddof=1):.1f} fk {fk.std(ddof=1):.1f}") | |
| print(f"within (path-sampling) sd : raw {wr:.1f} fk {wf:.1f}") | |
| print( | |
| f"-> clustering dominates by {fk.std(ddof=1) / max(wf, 1e-9):.0f}x; Monte-Carlo error is negligible" | |
| ) | |
| print(f"\n--- paired comparison, FK vs raw ---") | |
| print(f"raw {raw.mean():.1f} +- {raw.std(ddof=1):.1f}") | |
| print(f"fk {fk.mean():.1f} +- {fk.std(ddof=1):.1f}") | |
| se = d.std(ddof=1) / np.sqrt(n) | |
| print(f"paired diff {d.mean():+.1f} (sd {d.std(ddof=1):.1f}, se {se:.1f})") | |
| print( | |
| f" min diff over seeds {d.min():+.1f}, max {d.max():+.1f} " | |
| f"(sign consistent: {'yes' if (d > 0).all() else 'NO'})" | |
| ) | |
| print(f"Cohen's d_z = {d.mean() / d.std(ddof=1):.2f}") | |
| try: | |
| from scipy import stats | |
| t, pt = stats.ttest_rel(fk, raw) | |
| w, pw = stats.wilcoxon(fk, raw) | |
| ci = stats.t.interval(0.95, n - 1, loc=d.mean(), scale=se) | |
| print(f"paired t: t={t:.1f}, p={pt:.2e}") | |
| print( | |
| f"Wilcoxon signed-rank: W={w:.0f}, p={pw:.2e} " | |
| f"(floor for n={n} is {2 / 2**n:.1e})" | |
| ) | |
| print(f"95% CI of the mean difference: [{ci[0]:.1f}, {ci[1]:.1f}]") | |
| except ImportError: | |
| print("scipy unavailable; bootstrap only") | |
| rng = np.random.default_rng(0) | |
| bs = np.array([rng.choice(d, n, replace=True).mean() for _ in range(20000)]) | |
| print( | |
| f"bootstrap 95% CI of mean diff: [{np.percentile(bs, 2.5):.1f}, {np.percentile(bs, 97.5):.1f}]" | |
| ) | |
| json.dump( | |
| { | |
| "raw": raw.tolist(), | |
| "fk": fk.tolist(), | |
| "clust_seeds": CLUST_SEEDS, | |
| "path_seeds": PATH_SEEDS, | |
| "n_paths": NPATH, | |
| "tica_lag": TL, | |
| "lag": LAG, | |
| "N": N, | |
| "alpha": ALPHA, | |
| }, | |
| open("data/mb_stats.json", "w"), | |
| indent=2, | |
| ) | |
| print("[done]") | |