"""Fresh CPU audit of the Semi-knockoffs Section 5.1 comparison. This is an independent execution, not a read of the authors' CSV outputs. It uses the paper's adjacent-support design (Sigma_ij=0.6**abs(i-j), first quarter of beta nonzero, gradient boosting) and a separately implemented HRT holdout. A second exact construction exercises the masked-correlation five-permutation assertion. """ import json import sys from pathlib import Path import numpy as np from sklearn.base import clone from sklearn.ensemble import GradientBoostingRegressor from scipy.stats import wilcoxon sys.path.insert(0, str(Path(__file__).resolve().parents[1])) from skocore import ar1_design ALPHA = 0.05 OUT = {"alpha": ALPHA, "design": "independent_cpu_claim5_scope_audit"} def source_design(n, p, seed): rng = np.random.default_rng(seed) # ar1_design is the exact Gaussian construction for Sigma_ij=rho^|i-j|. X = ar1_design(n, p, 0.6, rng) beta = np.zeros(p) beta[: p // 4] = rng.uniform(1.0, 2.0, size=p // 4) y = np.einsum("ij,j->i", X, beta) + rng.normal(size=n) return X, y, rng def ridge_predict(Z_train, target, Z_eval, alpha=1.0): """Closed-form finite Ridge prediction using einsum, avoiding BLAS matmul.""" gram = np.einsum("ij,ik->jk", Z_train, Z_train) rhs = np.einsum("ij,i->j", Z_train, target) coef = np.linalg.solve(gram + alpha * np.eye(Z_train.shape[1]), rhs) return np.einsum("ij,j->i", Z_eval, coef) def semi_pvalue(X, y, j, model, rng, seed, n_perm=1): """Standalone Algorithm-1 p-value with closed-form Ridge nuisances.""" Xmj = np.delete(X, j, axis=1) xj = X[:, j] nu = ridge_predict(Xmj, xj, Xmj) rho = ridge_predict(np.column_stack([Xmj, y]), xj, np.column_stack([Xmj, y])) e1 = xj - nu e2 = xj - rho diffs = [] for _ in range(n_perm): x1 = X.copy() x2 = X.copy() x1[:, j] = nu + e1[rng.permutation(len(y))] x2[:, j] = rho + e2[rng.permutation(len(y))] diffs.append((model.predict(x1) - y) ** 2 - (model.predict(x2) - y) ** 2) d = np.concatenate(diffs) if np.allclose(d, 0.0): return 1.0 return float(wilcoxon(d, alternative="greater", zero_method="zsplit").pvalue) def hrt_pvalue(X, y, j, seed, permutations=100): """One HRT p-value with one train/test split and a conditional ridge draw.""" rng = np.random.default_rng(seed) order = rng.permutation(len(y)) n_train = len(y) // 2 train, test = order[:n_train], order[n_train:] model = GradientBoostingRegressor(random_state=seed).fit(X[train], y[train]) xm = np.delete(X, j, axis=1) mu = ridge_predict(xm[train], X[train, j], xm[test]) residual = X[train, j] - ridge_predict(xm[train], X[train, j], xm[train]) observed = np.mean((model.predict(X[test]) - y[test]) ** 2) count = 0 for _ in range(permutations): xp = X[test].copy() xp[:, j] = mu + rng.choice(residual, size=len(test), replace=True) count += float(np.mean((model.predict(xp) - y[test]) ** 2) <= observed) return (1.0 + count) / (1.0 + permutations) def adjacent_grid(ns=(200, 300, 400), reps=15): rows = [] for n in ns: sko_alt = [] sko_null = [] hrt_alt = [] hrt_null = [] for r in range(reps): seed = 18000 + 100 * n + r X, y, rng = source_design(n, 50, seed) model = GradientBoostingRegressor(random_state=seed).fit(X, y) # j=0 is in the first-quarter adjacent support; j=30 is null. sko_alt.append(semi_pvalue(X, y, 0, model, rng, seed=seed) <= ALPHA) sko_null.append(semi_pvalue(X, y, 30, model, rng, seed=seed + 1) <= ALPHA) hrt_alt.append(hrt_pvalue(X, y, 0, seed + 2) <= ALPHA) hrt_null.append(hrt_pvalue(X, y, 30, seed + 3) <= ALPHA) row = { "n": n, "reps": reps, "sko_power": float(np.mean(sko_alt)), "sko_type_I": float(np.mean(sko_null)), "hrt_power": float(np.mean(hrt_alt)), "hrt_type_I": float(np.mean(hrt_null)), } row["power_gap"] = row["sko_power"] - row["hrt_power"] rows.append(row) print("adjacent", row, flush=True) return rows def masked_grid(reps=15, amplitudes=(0.25, 0.5, 1.0)): rows = [] for amplitude in amplitudes: cells = [] for r in range(reps): seed = 24000 + 100 * int(amplitude * 100) + r rng = np.random.default_rng(seed) X = ar1_design(300, 50, 0.6, rng) signal = 25 null = signal - 1 y = amplitude * X[:, signal] + 0.5 * rng.normal(size=300) # Replace the adjacent coordinate by a correlated but label-null copy. X[:, null] = X[:, signal] + 0.5 * rng.normal(size=300) model = GradientBoostingRegressor(random_state=seed).fit(X, y) one = semi_pvalue(X, y, signal, model, rng, seed=seed, n_perm=1) <= ALPHA five = semi_pvalue(X, y, signal, model, rng, seed=seed + 10, n_perm=5) <= ALPHA null_p = semi_pvalue(X, y, null, model, rng, seed=seed + 30, n_perm=5) cells.append({ "one_reject": bool(one), "five_reject": bool(five), "five_null_reject": bool(null_p <= ALPHA), }) row = { "signal_amplitude": amplitude, "reps": reps, "single_power": float(np.mean([r["one_reject"] for r in cells])), "five_power": float(np.mean([r["five_reject"] for r in cells])), "five_null_type_I": float(np.mean([r["five_null_reject"] for r in cells])), } row["power_gain"] = row["five_power"] - row["single_power"] rows.append(row) print("masked", row, flush=True) out = {"reps": reps, "amplitudes": list(amplitudes), "cells": rows} print("masked", out, flush=True) return out def main(): OUT["adjacent"] = adjacent_grid() OUT["masked"] = masked_grid() out = Path("outputs/claim5_scope_audit.json") out.write_text(json.dumps(OUT, indent=2) + "\n") print("saved", out, flush=True) if __name__ == "__main__": main()