"""F5-conf-02: Finite-sample bias of OLS AR(1) coefficient. Reads only data/design.csv.""" import json, hashlib, os import numpy as np import pandas as pd ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) DATA = os.path.join(ROOT, "data", "design.csv") OUT = os.path.join(ROOT, "out") raw = open(DATA, "rb").read() sha = hashlib.sha256(raw).hexdigest() d = pd.read_csv(DATA) n_rows = int(len(d)) issues = [] if d["parameter"].duplicated().any(): issues.append({"issue": "duplicated parameter names", "handling": "first occurrence used"}) p = dict(zip(d["parameter"].str.strip(), d["value"].astype(str).str.strip())) required = ["n", "rho", "sigma", "intercept", "initial_condition", "n_replications", "seed"] missing = [k for k in required if k not in p] if missing: raise SystemExit(f"design missing parameters: {missing}") n = int(p["n"]); rho = float(p["rho"]); sigma = float(p["sigma"]) R = int(p["n_replications"]); seed = int(p["seed"]) intercept = p["intercept"].lower() == "yes" assert intercept, "design requires intercept" assert p["initial_condition"].lower() == "stationary" assert abs(rho) < 1 and sigma > 0 and n >= 3 and R >= 2 # ---- analytical approximation and baseline ---- bias_analytical = -(1 + 3 * rho) / n bias_analytical_n_minus_1 = -(1 + 3 * rho) / (n - 1) # sensitivity: effective sample size se_asymptotic = np.sqrt((1 - rho ** 2) / n) # ---- Monte Carlo (vectorised, c = 0) ---- rng = np.random.default_rng(seed) y = np.empty((R, n)) y[:, 0] = rng.normal(0.0, sigma / np.sqrt(1 - rho ** 2), size=R) eps = rng.normal(0.0, sigma, size=(R, n - 1)) for t in range(1, n): y[:, t] = rho * y[:, t - 1] + eps[:, t - 1] x = y[:, :-1]; z = y[:, 1:] # regress y_t on (1, y_{t-1}), t=2..n xc = x - x.mean(axis=1, keepdims=True) zc = z - z.mean(axis=1, keepdims=True) rho_hat = (xc * zc).sum(axis=1) / (xc ** 2).sum(axis=1) # OLS slope with intercept finite = np.isfinite(rho_hat) n_excl = int((~finite).sum()) rho_hat = rho_hat[finite] n_an = int(rho_hat.size) mean_rh = float(rho_hat.mean()) sd_rh = float(rho_hat.std(ddof=1)) bias_mc = mean_rh - rho bias_mc_se = float(sd_rh / np.sqrt(n_an)) disc = float((bias_mc - bias_analytical) / bias_mc_se) # extra diagnostics (not required keys; written to separate file) diag = { "design_sha256": sha, "bias_analytical_n_minus_1": bias_analytical_n_minus_1, "discrepancy_se_units_n_minus_1": float((bias_mc - bias_analytical_n_minus_1) / bias_mc_se), "bias_mc_ci95": [bias_mc - 1.96 * bias_mc_se, bias_mc + 1.96 * bias_mc_se], "rho_hat_median": float(np.median(rho_hat)), "share_rho_hat_below_rho": float((rho_hat < rho).mean()), "ratio_sd_mc_to_se_asymptotic": float(sd_rh / se_asymptotic), "relative_error_analytical_vs_mc": float((bias_analytical - bias_mc) / bias_mc), "n_nonfinite_excluded": n_excl, } results = { "task_id": "F5-conf-02", "bias_analytical": bias_analytical, "bias_mc": bias_mc, "bias_mc_se": bias_mc_se, "rho_hat_mean": mean_rh, "rho_hat_sd_mc": sd_rh, "discrepancy_se_units": disc, "se_asymptotic": float(se_asymptotic), "n_replications": R, "feasibility": "complete", "feasibility_reason": None, "data_issues": issues, "n_input_rows": {"design.csv": n_rows}, "n_analysed": n_an, "random_seed": seed, } os.makedirs(OUT, exist_ok=True) with open(os.path.join(OUT, "results.json"), "w") as f: json.dump(results, f, indent=2) with open(os.path.join(OUT, "diagnostics.json"), "w") as f: json.dump(diag, f, indent=2) pd.DataFrame([{"step": 1, "rule": "drop replications with non-finite rho_hat", "n_before": R, "n_after": n_an, "n_removed": n_excl, "rationale": "degenerate regressor (zero variance) would make OLS undefined"}] ).to_csv(os.path.join(OUT, "exclusions.csv"), index=False) print(json.dumps(results, indent=2)); print(json.dumps(diag, indent=2))