Buckets:
| """Claim 5, second half: does 5-permutation derandomisation raise power? | |
| In the paper's own masked-correlation configuration (n = 300, p = 50) our | |
| reproduction saturates: SKO_Wcx, SKO_Wcx_p5 and HRT all reach power 1.000, so | |
| the derandomisation gain cannot be measured there. This script sweeps the | |
| sample size down into the non-saturated regime and measures the SKO_Wcx -> | |
| SKO_Wcx_p5 power gain (paired over replicates) as a function of n, and also | |
| sweeps the number of permutations at a fixed n. | |
| """ | |
| from __future__ import annotations | |
| import json | |
| import os | |
| import sys | |
| import time | |
| import numpy as np | |
| from joblib import Parallel, delayed | |
| from scipy import stats | |
| from sklearn.neural_network import MLPRegressor | |
| sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)))) | |
| from semiknockoffs import fit_nu_rho, gen_masked, sko_pvalue, sq_loss # noqa: E402 | |
| OUT = os.path.join( | |
| os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "outputs" | |
| ) | |
| SEED0 = 20260725 | |
| ALPHA = 0.05 | |
| PERMS = [1, 2, 5, 10, 20] | |
| def _rep(rep, n, p=50): | |
| rng = np.random.default_rng(SEED0 + 61_000_000 + 1231 * rep + 13 * n) | |
| X, y, l, supp = gen_masked(n, p, rng) | |
| truth = np.zeros(p, bool) | |
| truth[supp] = True | |
| m = MLPRegressor(random_state=rep).fit(X, y) | |
| P = {k: np.ones(p) for k in PERMS} | |
| for j in range(p): | |
| nu, rho = fit_nu_rho(X, y, j, alpha=1.0) | |
| for k in PERMS: | |
| P[k][j] = sko_pvalue(X, y, j, nu, rho, m.predict, sq_loss, rng, n_perm=k) | |
| return { | |
| k: (float(np.mean(P[k][truth] <= ALPHA)), float(np.mean(P[k][~truth] <= ALPHA))) | |
| for k in PERMS | |
| } | |
| def run(n, reps=100, n_jobs=100): | |
| t0 = time.time() | |
| got = Parallel(n_jobs=n_jobs)(delayed(_rep)(r, n) for r in range(reps)) | |
| res = { | |
| "n": n, | |
| "p": 50, | |
| "replicates": reps, | |
| "alpha": ALPHA, | |
| "seconds": round(time.time() - t0, 1), | |
| } | |
| base = np.array([g[1][0] for g in got]) | |
| for k in PERMS: | |
| pw = np.array([g[k][0] for g in got]) | |
| t1 = np.array([g[k][1] for g in got]) | |
| res[f"perm{k}"] = { | |
| "power": float(pw.mean()), | |
| "power_se": float(pw.std(ddof=1) / np.sqrt(reps)), | |
| "type_I": float(t1.mean()), | |
| "type_I_se": float(t1.std(ddof=1) / np.sqrt(reps)), | |
| } | |
| if k > 1: | |
| d = pw - base | |
| res[f"perm{k}"]["paired_gain_over_perm1"] = float(d.mean()) | |
| res[f"perm{k}"]["paired_gain_pvalue"] = float( | |
| stats.ttest_rel(pw, base).pvalue | |
| ) | |
| print( | |
| f"[n={n}] perms={k:<3} power={pw.mean():.3f} typeI={t1.mean():.3f}" | |
| + ( | |
| f" gain vs 1 perm = {(pw-base).mean():+.4f} " | |
| f"(p={stats.ttest_rel(pw, base).pvalue:.2e})" | |
| if k > 1 | |
| else "" | |
| ), | |
| flush=True, | |
| ) | |
| return res | |
| if __name__ == "__main__": | |
| os.makedirs(OUT, exist_ok=True) | |
| res = { | |
| "seed0": SEED0, | |
| "setting": "masked correlation (Figure 5), NN black box", | |
| "permutation_grid": PERMS, | |
| } | |
| for n in (50, 75, 100, 150, 300): | |
| res[f"n{n}"] = run(n) | |
| with open(os.path.join(OUT, "claim5b_derandomization.json"), "w") as f: | |
| json.dump(res, f, indent=2) | |
| print("wrote", os.path.join(OUT, "claim5b_derandomization.json")) | |
Xet Storage Details
- Size:
- 3.36 kB
- Xet hash:
- 3ebfdaa396b8da69522de456f8f999f3b25bbe75413aed18b634b3fbc04c6428
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.