"""F4-conf-02: verdicts on six assertions against cited source extracts. Run from workspace root: ~/wp29/venv/bin/python out/analysis.py Reads only data/; writes out/results.json deterministically. No randomness.""" import json, math, re, os D = "data" S1, S2 = "arxiv-2609.27732v1", "arxiv-2606.14621v1" src = {s: open(f"{D}/sources/{s}.txt", encoding="utf-8").read().split("\n") for s in (S1, S2)} assertions = json.load(open(f"{D}/assertions.json", encoding="utf-8")) def wcl(path): with open(path, "rb") as f: return f.read().count(b"\n") def find(s, pattern): """Return (index, line) of first line matching regex; print it; fail loudly if absent.""" for i, l in enumerate(src[s]): if re.search(pattern, l): print(f" [{s} L{i+1}] {l.strip()[:170]}") return i, l raise RuntimeError(f"pattern not found in {s}: {pattern}") def cells(line): return [c.strip() for c in line.split("|")] def num(c): return float(re.sub(r"[^0-9.\-]", "", c.replace("−", "-"))) def coef_row(s, pattern): """Coefficient row and the SE row that follows it (next non-blank line).""" i, l = find(s, pattern) j = i + 1 while src[s][j].strip() == "": j += 1 print(f" [{s} L{j+1}] {src[s][j].strip()}") return cells(l), cells(src[s][j]) pct = lambda b: 100 * (math.exp(b) - 1) V, L, R = {}, {}, {} # P2 baseline: do all decimal numbers of the assertion appear verbatim in the cited extract? baseline = {} for a in assertions: nums = re.findall(r"\d+\.\d+", a["text"]) txt = "\n".join(src[a["cites"]]) baseline[a["id"]] = all(n in txt for n in nums) print("A1") c, se = coef_row(S1, r"^ATT \| -0\.399") _, pc = find(S1, r"^% change \| -32\.9%") b5, se5, p5 = c[3], se[3], cells(pc)[3] b = num(b5); s_ = num(se5); stars = b5.count("*") ok = (b == -0.431 and s_ == 0.210 and stars >= 2 and p5 == "-35.0%" and round(pct(b), 1) == -35.0) V["A1"] = "supported" if ok else "not_supported" L["A1"] = f'{S1} | Table 6 | row "ATT" | column "5 km"' R["A1"] = (f"Table 6 prints the 5 km ATT as {b5} with SE {se5} (** = p<0.05) and a % change of {p5} " f"(100(exp({b})-1) = {pct(b):.2f}%), matching every element of the assertion.") print("A2") c, se = coef_row(S1, r"^HIV \|") hiv1 = num(c[1]) V["A2"] = "supported" if hiv1 < 0 else "not_supported" L["A2"] = f'{S1} | Table 7 | row "HIV" | column "1 km"' R["A2"] = (f"Table 7 prints the 1 km HIV ATT as {c[1]} (positive, SE {se[1]}), not -0.063, so the sign " f"and the claimed fall in HIV hospitalisations are contradicted.") print("A3") c, se = coef_row(S1, r"^ATT \| -0\.399") _, pc = find(S1, r"^% change \| -32\.9%") b3 = num(c[2]); p3 = cells(pc)[2] V["A3"] = "supported" if abs(pct(b3) - (-28.3)) < 0.05 else "not_supported" L["A3"] = f'{S1} | Table 6 | row "% change" | column "3 km"' R["A3"] = (f"The 3 km ATT {c[2]} log points converts to 100(exp({b3})-1) = {pct(b3):.1f}% and Table 6 prints " f"{p3}, not -28.3% (the assertion equates log points with percent; the estimate is also insignificant, SE {se[2]}).") print("A4") c, se = coef_row(S1, r"^Hepatitis \|") bh, sh = num(c[3]), num(se[3]) lo196, hi196 = bh - 1.96 * sh, bh + 1.96 * sh lo2, hi2 = bh - 2 * sh, bh + 2 * sh excl = hi196 < 0 or lo196 > 0 V["A4"] = "supported" if excl and abs(lo196 + 0.66) < 0.02 else "not_supported" L["A4"] = f'{S1} | Table 7 | row "Hepatitis" | column "5 km"' R["A4"] = (f"With ATT {c[3]} and SE {se[3]} the 95% CI is [{lo196:.3f}, {hi196:.3f}] (±1.96·SE), which includes zero " f"(and the estimate carries no star); [-0.66, -0.16] is only ±1 SE.") print("A5") hits = [i + 1 for i, l in enumerate(src[S1]) if re.search(r"\b(men|women|male|female|gender|sex)\b", l, re.I)] print(f" lines in {S1} mentioning men/women/male/female/gender/sex: {hits}") for t in ("Table 6", "Table 7", "Table A19"): find(S1, rf"^{t}:") V["A5"] = "not_assessable" if not hits else "REVIEW" L["A5"] = f"not reported in {S1}" R["A5"] = ("The extract reports ATTs only by buffer (Table 6), outcome category (Table 7) and estimator (Table A19) " "and contains no estimate by sex, so the men-versus-women comparison can be neither confirmed nor refuted.") print("A6") i, l = find(S2, r"^Weeks on UI \|") c = cells(l); ate = c[4]; ctrl = c[2] find(S2, r"11\.5% decrease") m = re.search(r"(-?\d+\.\d+) weeks; p<0\.001; 95% CI: \[(-?\d+\.\d+), (-?\d+\.\d+)\]", ate) est, lo, hi = map(float, m.groups()); cm = num(ctrl.split("weeks")[0]) rel = 100 * abs(est) / cm ok = (est == -1.99 and lo == -2.24 and hi == -1.75 and cm == 17.3 and round(rel, 1) == 11.5) V["A6"] = "supported" if ok else "not_supported" L["A6"] = f'{S2} | Table 2 | row "Weeks on UI" | column "Average treatment effect estimate"' R["A6"] = (f"Table 2 gives the ATE as {ate.split(';')[0]} with 95% CI [{lo}, {hi}] and a control mean of {cm} weeks; " f"1.99/17.3 = {rel:.2f}%, matching the 11.5% stated in the Section 3 text.") assert "REVIEW" not in V.values() vals = list(V.values()) data_issues = [ {"issue": f"{S1}: Table A19 prints the CS-DR 5 km SE as (0.218) whereas Table 6 prints (0.210) for the same ATT -0.431.", "handling": "A1 is judged against Table 6 (the table whose values the assertion quotes); 5% significance holds under either SE with the 1.96 critical value (t=2.05 or 1.98)."}, {"issue": f"{S1}: Table A19 note states 640 never-treated comparison regions, while its body and Tables 6-7 state 608.", "handling": "Recorded only; no assertion depends on the control-region count."}, {"issue": f"{S1}: abstract says fatal outcomes fall by 35%, but Table 7 5 km fatal ATT -0.354 implies -29.8% and Section 6.1 says approximately 30%.", "handling": "Recorded only; no assertion concerns fatal outcomes. Tables treated as authoritative over prose."}, {"issue": f"{S1}: text describes HIV estimates as close to zero, while Table 7 shows -0.340 (0.294) at 5 km and -0.239 (0.337) at 3 km.", "handling": "A2 is decided from the Table 7 1 km cell (0.063), not from the prose."}, {"issue": f"{S2}: results text renders 'p ¡ 0.001' (character-encoding defect for 'p < 0.001').", "handling": "Interpreted as p < 0.001; does not affect A6, which relies on Table 2."}, {"issue": f"{S2}: text reports the weeks-on-UI CI in magnitude form [1.75, 2.24] while Table 2 reports [-2.24, -1.75].", "handling": "Treated as the same interval under a sign convention; A6 uses Table 2."}, ] n_input_rows = { "assertions.json": len(assertions), "README.md": wcl(f"{D}/README.md"), f"{S1}.txt": wcl(f"{D}/sources/{S1}.txt"), f"{S2}.txt": wcl(f"{D}/sources/{S2}.txt"), } res = { "task_id": "F4-conf-02", "verdicts": V, "locators": L, "reasons": R, "n_supported": vals.count("supported"), "n_not_supported": vals.count("not_supported"), "n_not_assessable": vals.count("not_assessable"), "feasibility": "complete", "feasibility_reason": None, "data_issues": data_issues, "n_input_rows": n_input_rows, "n_analysed": len(assertions), "random_seed": None, "baseline_literal_number_match": baseline, "conversions": { "A1_pct_change_5km": round(pct(-0.431), 4), "A1_t_stat_table6": round(0.431 / 0.210, 4), "A1_t_stat_tableA19": round(0.431 / 0.218, 4), "A3_pct_change_3km": round(pct(b3), 4), "A4_ci95_196": [round(lo196, 4), round(hi196, 4)], "A4_ci95_200": [round(lo2, 4), round(hi2, 4)], "A6_relative_reduction_pct": round(rel, 4), }, } os.makedirs("out", exist_ok=True) with open("out/results.json", "w", encoding="utf-8") as f: json.dump(res, f, indent=2, ensure_ascii=False) print("verdicts:", V)