"""F4-conf-02: verify six assertions against their 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, os, re DATA = "data" SRC = {"arxiv-2609.27732v1": os.path.join(DATA, "sources", "arxiv-2609.27732v1.txt"), "arxiv-2606.14621v1": os.path.join(DATA, "sources", "arxiv-2606.14621v1.txt")} Z, Z_ALT = 1.96, 2.0 def lines(path): with open(path, encoding="utf-8") as f: return f.read().split("\n") def wc_l(path): with open(path, "rb") as f: return f.read().count(b"\n") assertions = json.load(open(os.path.join(DATA, "assertions.json"), encoding="utf-8")) L = {k: lines(v) for k, v in SRC.items()} DCR, RES = L["arxiv-2609.27732v1"], L["arxiv-2606.14621v1"] def find(src, pattern, start=0): """Return (lineno 1-based, line) of first line matching regex at/after start.""" for i in range(start, len(src)): if re.search(pattern, src[i]): return i + 1, src[i] raise ValueError(pattern) def cells(line): return [c.strip() for c in line.split("|")] def num(s): return float(re.sub(r"[^0-9.\-]", "", s.replace("−", "-"))) def show(tag, ln, text): print(f" [{tag} line {ln}] {text}") def pct(b): return 100 * (math.exp(b) - 1) # ---- parse DCR Table 6 ---- t6, _ = find(DCR, r"^Table 6:") ln_att, att_line = find(DCR, r"^ATT \|", t6) ln_se, se_line = ln_att + 2, DCR[ln_att + 1] ln_pc, pc_line = find(DCR, r"^% change \|", t6) t6_att = cells(att_line)[1:] # 1 km, 3 km, 5 km t6_se = [num(c) for c in cells(se_line)[1:]] t6_pc = cells(pc_line)[1:] # ---- parse DCR Table 7 ---- t7, _ = find(DCR, r"^Table 7:") def t7row(name): ln, row = find(DCR, r"^" + re.escape(name) + r" \|", t7) se = DCR[ln + 1] return ln, row, cells(row)[1:], [num(c) for c in cells(se)[1:]], se verd, loc, reas, detail = {}, {}, {}, {} print("A1 -- DCR Table 6, 5 km column") show("Table 6", ln_att, att_line); show("Table 6 SE", ln_se, se_line); show("Table 6", ln_pc, pc_line) b5 = num(t6_att[2]); se5 = t6_se[2]; stars5 = t6_att[2].count("*") z5 = abs(b5) / se5 ok = (t6_att[2].startswith("-0.431") and se5 == 0.210 and stars5 == 2 and t6_pc[2] == "-35.0%" and round(pct(b5), 1) == -35.0) print(f" b={b5}, SE={se5}, stars={stars5} (** = p<0.05), |b|/SE={z5:.3f}, 100(exp(b)-1)={pct(b5):.2f}%") verd["A1"] = "supported" if ok else "not_supported" loc["A1"] = 'arxiv-2609.27732v1 | Table 6 | row "ATT" | column "5 km"' reas["A1"] = ("Table 6 prints the 5 km ATT as -0.431** with SE (0.210), ** denoting p<0.05, and a % change of -35.0% " "(100(exp(-0.431)-1) = -35.0%), matching every element of the assertion.") detail["A1"] = {"b": b5, "se": se5, "z": round(z5, 4), "pct": round(pct(b5), 2)} print("A2 -- DCR Table 7, HIV row, 1 km column") ln, row, c, se, seline = t7row("HIV") show("Table 7", ln, row); show("Table 7 SE", ln + 2, seline) hiv1 = num(c[0]) print(f" HIV 1 km ATT = {c[0]} (sign {'positive' if hiv1 > 0 else 'negative'}), SE={se[0]}") verd["A2"] = "not_supported" if hiv1 > 0 else "supported" loc["A2"] = 'arxiv-2609.27732v1 | Table 7 | row "HIV" | column "1 km"' reas["A2"] = ("Table 7 prints the 1 km HIV ATT as +0.063 (SE 0.801), positive and insignificant, so the claimed " "negative sign (-0.063) and the claimed fall in HIV hospitalisations are contradicted.") detail["A2"] = {"b": hiv1, "se": se[0]} print("A3 -- DCR Table 6, 3 km column") show("Table 6", ln_att, att_line); show("Table 6", ln_pc, pc_line) b3 = num(t6_att[1]) print(f" b={b3}, printed % change={t6_pc[1]}, 100(exp(b)-1)={pct(b3):.2f}% (assertion: -28.3%)") verd["A3"] = "not_supported" if abs(pct(b3) - (-28.3)) > 0.5 else "supported" loc["A3"] = 'arxiv-2609.27732v1 | Table 6 | row "% change" | column "3 km"' reas["A3"] = ("Table 6 converts the 3 km ATT of -0.283 to a -24.7% change (100(exp(-0.283)-1) = -24.7%), not 28.3%, " "so the stated magnitude is contradicted (28.3% is the log-point coefficient read directly as a percentage).") detail["A3"] = {"b": b3, "se": t6_se[1], "pct": round(pct(b3), 2), "printed_pct": t6_pc[1]} print("A4 -- DCR Table 7, Hepatitis row, 5 km column") ln, row, c, se, seline = t7row("Hepatitis") show("Table 7", ln, row); show("Table 7 SE", ln + 2, seline) bh, sh = num(c[2]), se[2] ci = (bh - Z * sh, bh + Z * sh); ci2 = (bh - Z_ALT * sh, bh + Z_ALT * sh) crit_needed = abs(bh) / sh print(f" b={bh}, SE={sh}; 95% CI (1.96) = [{ci[0]:.3f}, {ci[1]:.3f}]; (2.0) = [{ci2[0]:.3f}, {ci2[1]:.3f}]; " f"asserted [-0.66,-0.16] = b +/- {(-0.16 - -0.66)/2:.2f} (=1.0 SE); CI excludes 0 only if crit < {crit_needed:.3f}") verd["A4"] = "not_supported" if ci[1] > 0 and ci2[1] > 0 else "supported" loc["A4"] = 'arxiv-2609.27732v1 | Table 7 | row "Hepatitis" | column "5 km"' reas["A4"] = ("With ATT -0.406 and SE (0.250), the 95% CI is -0.406 +/- 1.96*0.250 = [-0.896, 0.084], which includes zero " "(the asserted [-0.66, -0.16] is only +/- 1 SE), and the estimate carries no significance star.") detail["A4"] = {"b": bh, "se": sh, "ci_196": [round(ci[0], 3), round(ci[1], 3)], "ci_200": [round(ci2[0], 3), round(ci2[1], 3)], "crit_to_exclude_zero": round(crit_needed, 3)} print("A5 -- sex heterogeneity in DCR extract") hits = [(i + 1, l) for i, l in enumerate(DCR) if re.search(r"\b(men|women|male|female|sex|gender)\b", l, re.I)] print(f" lines mentioning men/women/male/female/sex/gender: {len(hits)}") for h in hits: show("match", *h) verd["A5"] = "not_assessable" if not hits else "REVIEW" loc["A5"] = "not reported in arxiv-2609.27732v1" reas["A5"] = ("The extract reports ATTs only by buffer (Table 6), outcome category (Table 7) and estimator (Table A19) " "and never by sex, so a men-versus-women comparison can be neither confirmed nor refuted.") detail["A5"] = {"n_sex_mentions": len(hits)} print("A6 -- RESEA Table 2, Weeks on UI row") ln, row = find(RES, r"^Weeks on UI \|") show("Table 2", ln, row) lt, txt = find(RES, r"11\.5% decrease") show("Results text", lt, txt[txt.find("a 1.99 week"):txt.find("17.3 weeks.") + 11]) cc = cells(row) ctrl = num(cc[2].split("weeks")[0]); ate = cc[4] ate_b = num(ate.split(";")[0].replace("weeks", "")) ci6 = [num(x) for x in re.search(r"\[([^\]]+)\]", ate).group(1).split(",")] rel = 100 * abs(ate_b) / ctrl print(f" control mean={ctrl}, ATE={ate_b}, CI={ci6}, |ATE|/control = {rel:.2f}%") ok6 = ate_b == -1.99 and ci6 == [-2.24, -1.75] and ctrl == 17.3 and round(rel, 1) == 11.5 verd["A6"] = "supported" if ok6 else "not_supported" loc["A6"] = ('arxiv-2606.14621v1 | Table 2 | row "Weeks on UI" | column "Average treatment effect estimate"') reas["A6"] = ("Table 2 gives the ATE of RESEA selection on weeks on UI as -1.99 weeks (95% CI [-2.24, -1.75], p<0.001) " "with a control mean of 17.3 weeks, and 1.99/17.3 = 11.5% as also stated in the results text.") detail["A6"] = {"ate": ate_b, "ci": ci6, "control_mean": ctrl, "rel_pct": round(rel, 2)} # ---- naive baseline: do all numbers in the assertion appear verbatim in the cited source? ---- baseline = {} for a in assertions: srctext = "\n".join(L[a["cites"]]).replace("−", "-") nums = re.findall(r"\d+\.\d+", a["text"]) baseline[a["id"]] = "all_numbers_found" if all(n in srctext for n in nums) else "some_number_missing" ids = [a["id"] for a in assertions] assert ids == ["A1", "A2", "A3", "A4", "A5", "A6"] assert all(v in ("supported", "not_supported", "not_assessable") for v in verd.values()) data_issues = [ {"issue": "arxiv-2609.27732v1: Table A19 CS-DR column prints SE (0.218) for the same ATT -0.431 for which Table 6 (5 km) prints SE (0.210).", "handling": "A1 judged against Table 6 (the table the assertion describes); significance at 5% holds under either SE (|b|/SE = 2.05 or 1.98 > 1.96), noted in report."}, {"issue": "arxiv-2609.27732v1: Table A19 note says 640 never-treated comparison regions, whereas Tables 6, 7, A19 body and Figure 6 note say 608.", "handling": "Recorded; not decisive for any assertion."}, {"issue": "arxiv-2609.27732v1: abstract says fatal hospitalisations fall by 35%, but Table 7 5 km fatal ATT -0.354 implies -29.8%, matching Section 6.1 ('approximately 30%').", "handling": "Recorded; no assertion concerns fatal outcomes; tables preferred over abstract text."}, {"issue": "arxiv-2609.27732v1: text announces five diagnostic sub-categories, Figure 7 note says four, Table 7 note gives ICD codes for four.", "handling": "Recorded; not decisive for any assertion."}, {"issue": "arxiv-2606.14621v1: rendering defect 'p ¡ 0.001' in the short-term weeks-on-UI sentence (should read p < 0.001).", "handling": "Read as p < 0.001 (consistent with Table 2); not decisive."}, {"issue": "arxiv-2606.14621v1: results text prints the weeks-on-UI CI as reduction magnitudes [1.75, 2.24] while Tables 2 and 3 print signed [-2.24, -1.75].", "handling": "Treated as equivalent sign conventions; A6 judged against Table 2."}, ] res = { "task_id": "F4-conf-02", "verdicts": verd, "locators": loc, "reasons": reas, "n_supported": sum(v == "supported" for v in verd.values()), "n_not_supported": sum(v == "not_supported" for v in verd.values()), "n_not_assessable": sum(v == "not_assessable" for v in verd.values()), "feasibility": "complete", "feasibility_reason": None, "data_issues": data_issues, "n_input_rows": {"assertions.json": len(assertions), "README.md": wc_l(os.path.join(DATA, "README.md")), "sources/arxiv-2609.27732v1.txt": wc_l(SRC["arxiv-2609.27732v1"]), "sources/arxiv-2606.14621v1.txt": wc_l(SRC["arxiv-2606.14621v1"])}, "n_analysed": len(assertions), "random_seed": None, "baseline_verdicts_number_match": baseline, "computation_details": detail, "conventions": {"pct_change": "100*(exp(b)-1)", "ci_crit": Z, "ci_crit_alt": Z_ALT}, } os.makedirs("out", exist_ok=True) with open(os.path.join("out", "results.json"), "w", encoding="utf-8") as f: json.dump(res, f, indent=2, ensure_ascii=False) print("verdicts:", verd) print("counts: supported=%d not_supported=%d not_assessable=%d" % (res["n_supported"], res["n_not_supported"], res["n_not_assessable"]))