#!/usr/bin/env python3 """Self-authored, bounded reaggregation for the NinueNAODD evidence run. This file is intentionally input-only: it never downloads data, imports paper or peer code, starts a subprocess, or writes outside the explicit --out path. It verifies the exact release artifacts before reading them. """ from __future__ import annotations import argparse import hashlib import json import math import time from collections import Counter, defaultdict from pathlib import Path from typing import Any, Iterable EXPECTED = { "screened-00000-of-00001.parquet": { "bytes": 91141, "sha256": "cf8fb398c421291cad2331fd5b20a9baadf0d8e20fa43fce2813eb8665900da4", "rows": 1920, }, "unscreened-00000-of-00001.parquet": { "bytes": 91114, "sha256": "c1f0199a69d3a0d56eea6230b2bd582e69aee7c2cb4b1e7ee8a52b10647fe7ee", "rows": 1921, }, "feedback_dataset.parquet": { "bytes": 1888747, "sha256": "372d432b76340a793a1f9be053de73b3d6d23ecd9ccb4058023ce7d4569002e5", "rows": 257280, }, } def sha256_file(path: Path) -> str: digest = hashlib.sha256() with path.open("rb") as handle: for block in iter(lambda: handle.read(1024 * 1024), b""): digest.update(block) return digest.hexdigest() def verify_release(path: Path) -> dict[str, Any]: spec = EXPECTED.get(path.name) if spec is None: raise ValueError(f"unexpected input basename: {path.name}") actual_bytes = path.stat().st_size actual_sha = sha256_file(path) if actual_bytes != spec["bytes"] or actual_sha != spec["sha256"]: raise ValueError( f"hash gate failed for {path}: bytes={actual_bytes} sha256={actual_sha}" ) return {"path": str(path), "bytes": actual_bytes, "sha256": actual_sha} def read_parquet(path: Path) -> list[dict[str, Any]]: try: import pyarrow.parquet as parquet except ImportError as exc: # pragma: no cover - dependency is checked by permit raise RuntimeError("pyarrow is required; no network fallback is allowed") from exc table = parquet.read_table(path) return table.to_pylist() def finite_number(value: Any) -> bool: return isinstance(value, (int, float)) and math.isfinite(float(value)) def clic_observations(rows: Iterable[dict[str, Any]]) -> tuple[list[dict[str, Any]], Counter]: """Flatten each unambiguous two-item 2AFC record into one observation.""" observations: list[dict[str, Any]] = [] exclusions: Counter = Counter() for row in rows: ratings = row.get("ratings") or [] by_condition: dict[str, dict[str, Any]] = {} for rating in ratings: condition = rating.get("condition") score = rating.get("score") if condition is not None and finite_number(score): by_condition[str(condition)] = rating if len(by_condition) != 2: exclusions["not_exactly_two_conditions"] += 1 continue items = sorted(by_condition) left, right = by_condition[items[0]], by_condition[items[1]] left_score, right_score = float(left["score"]), float(right["score"]) if left_score == right_score: exclusions["tied_scores"] += 1 continue observations.append( { "rater": str(row.get("rater_id")), "left": items[0], "right": items[1], "winner": items[0] if left_score > right_score else items[1], } ) return observations, exclusions def humaine_observations(rows: Iterable[dict[str, Any]]) -> tuple[list[dict[str, Any]], Counter]: """Keep explicit A/B choices and exclude ties or malformed rows.""" observations: list[dict[str, Any]] = [] exclusions: Counter = Counter() for row in rows: a, b, choice = row.get("model_a"), row.get("model_b"), row.get("choice") if not a or not b or a == b: exclusions["missing_or_identical_pair"] += 1 continue normalized = str(choice).strip().upper() if normalized not in {"A", "B"}: exclusions["tie_or_unknown_choice"] += 1 continue observations.append( { "rater": str(row.get("conversation_id", "row")), "left": str(a), "right": str(b), "winner": str(a) if normalized == "A" else str(b), } ) return observations, exclusions def pair_table(observations: Iterable[dict[str, Any]]) -> tuple[list[str], list[dict[str, Any]]]: items = sorted({x for row in observations for x in (row["left"], row["right"])}) index = {item: n for n, item in enumerate(items)} out = [] for row in observations: i, j = sorted((index[row["left"]], index[row["right"]])) winner_index = index[row["winner"]] out.append({"rater": row["rater"], "i": i, "j": j, "winner": winner_index}) return items, out def fit_bt(items: list[str], observations: list[dict[str, Any]], iterations: int = 200) -> tuple[list[float], int]: """Self-authored Bradley-Terry MM fit with a unit ridge-like stabilizer.""" k = len(items) lam = [1.0] * k pair_counts: dict[tuple[int, int], list[int]] = defaultdict(lambda: [0, 0]) for row in observations: pair = (row["i"], row["j"]) pair_counts[pair][0] += 1 if row["winner"] == row["i"]: pair_counts[pair][1] += 1 for step in range(1, iterations + 1): updated = [1.0] * k for i in range(k): wins = 0.0 denom = 1.0 for (a, b), (n, wins_a) in pair_counts.items(): if i == a: wins += wins_a denom += n / (lam[a] + lam[b]) elif i == b: wins += n - wins_a denom += n / (lam[a] + lam[b]) updated[i] = max(wins / denom, 1e-12) scale = sum(updated) / max(k, 1) updated = [x / scale for x in updated] if max(abs(math.log(a) - math.log(b)) for a, b in zip(lam, updated)) < 1e-8: return updated, step lam = updated return lam, iterations def fit_bbq( items: list[str], observations: list[dict[str, Any]], alpha: float = 10.0, beta: float = 2.0, shape: float = 5.0, rate: float = 0.1, iterations: int = 200, ) -> dict[str, Any]: """Self-authored implementation of the paper's Eq. 11--13 updates.""" k = len(items) lam = [1.0] * k raters = sorted({row["rater"] for row in observations}) q = {rater: 0.8 for rater in raters} history: list[float] = [] start = time.perf_counter() for step in range(1, iterations + 1): gammas: list[float] = [] for row in observations: i, j = row["i"], row["j"] p_i = lam[i] / (lam[i] + lam[j]) p_win = p_i if row["winner"] == i else 1.0 - p_i quality = min(max(q[row["rater"]], 1e-12), 1.0 - 1e-12) numerator = quality * p_win gammas.append(numerator / (numerator + (1.0 - quality) * 0.5)) by_rater: dict[str, list[float]] = defaultdict(list) for row, gamma in zip(observations, gammas): by_rater[row["rater"]].append(gamma) for rater in raters: n = len(by_rater[rater]) q[rater] = min(max((sum(by_rater[rater]) + alpha - 1.0) / (n + alpha + beta - 2.0), 0.0), 1.0) updated = [0.0] * k denominators = [rate] * k for row, gamma in zip(observations, gammas): i, j = row["i"], row["j"] updated[row["winner"]] += gamma weight = gamma / (lam[i] + lam[j]) denominators[i] += weight denominators[j] += weight updated = [(wins + shape - 1.0) / denom for wins, denom in zip(updated, denominators)] scale = sum(updated) / max(k, 1) updated = [max(x / scale, 1e-12) for x in updated] # The observed-data mixture log-likelihood is a diagnostic only; the # paper's MAP objective also includes the stated Gamma/Beta priors. likelihood = 0.0 for row in observations: i, j = row["i"], row["j"] p_i = lam[i] / (lam[i] + lam[j]) p_win = p_i if row["winner"] == i else 1.0 - p_i quality = q[row["rater"]] likelihood += math.log(max(quality * p_win + (1.0 - quality) * 0.5, 1e-300)) history.append(likelihood) if max(abs(math.log(a) - math.log(b)) for a, b in zip(lam, updated)) < 1e-8: lam = updated break lam = updated return { "items": items, "skills": dict(zip(items, lam)), "q": q, "iterations": step, "seconds": time.perf_counter() - start, "observed_log_likelihood": history[-1] if history else None, "likelihood_history": history, } def pearson(xs: list[float], ys: list[float]) -> float: if len(xs) != len(ys) or len(xs) < 2: raise ValueError("Pearson correlation needs equal-length vectors of at least two values") mean_x, mean_y = sum(xs) / len(xs), sum(ys) / len(ys) dx, dy = [x - mean_x for x in xs], [y - mean_y for y in ys] denom = math.sqrt(sum(x * x for x in dx) * sum(y * y for y in dy)) if denom == 0.0: raise ValueError("constant vector in Pearson correlation") return sum(x * y for x, y in zip(dx, dy)) / denom def rater_agreement(observations: list[dict[str, Any]], skills: dict[str, float]) -> dict[str, float]: agreement: dict[str, list[bool]] = defaultdict(list) for row in observations: left_score, right_score = skills[row["left"]], skills[row["right"]] predicted = row["left"] if left_score >= right_score else row["right"] agreement[row["rater"]].append(predicted == row["winner"]) return {rater: sum(values) / len(values) for rater, values in agreement.items() if values} def permutation_negative_control(q: dict[str, float], agreement: dict[str, float]) -> dict[str, Any]: """Deterministic label-shuffle control for the q/agreement correlation.""" common = sorted(set(q) & set(agreement)) if len(common) < 3: return {"n": len(common), "status": "insufficient_common_raters"} q_values = [q[r] for r in common] observed = pearson(q_values, [agreement[r] for r in common]) shuffled = q_values[1:] + q_values[:1] shuffled_corr = pearson(shuffled, [agreement[r] for r in common]) return { "n": len(common), "observed_pearson": observed, "one_step_cyclic_shuffle_pearson": shuffled_corr, "status": "pass" if observed > shuffled_corr else "inspect", } def summarize_clic(path: Path) -> dict[str, Any]: rows = read_parquet(path) expected_rows = EXPECTED[path.name]["rows"] if len(rows) != expected_rows: raise ValueError(f"row-count gate failed for {path.name}: {len(rows)} != {expected_rows}") observations, exclusions = clic_observations(rows) items, encoded = pair_table(observations) bbq = fit_bbq(items, encoded) bt_skills, bt_iterations = fit_bt(items, encoded) bt_map = dict(zip(items, bt_skills)) agreement = rater_agreement(observations, bbq["skills"]) return { "rows": len(rows), "raters": len({str(row.get("rater_id")) for row in rows}), "items": len(items), "usable_2afc_observations": len(encoded), "exclusions": dict(exclusions), "bt_iterations": bt_iterations, "bbq_iterations": bbq["iterations"], "bbq_seconds": bbq["seconds"], "rater_q_pearson_to_consensus_agreement": pearson( [bbq["q"][r] for r in sorted(set(bbq["q"]) & set(agreement))], [agreement[r] for r in sorted(set(bbq["q"]) & set(agreement))], ), "permutation_negative_control": permutation_negative_control(bbq["q"], agreement), "bt_top_item": max(bt_map, key=bt_map.get) if bt_map else None, "bbq_top_item": max(bbq["skills"], key=bbq["skills"].get) if bbq["skills"] else None, } def summarize_humaine(path: Path) -> dict[str, Any]: rows = read_parquet(path) expected_rows = EXPECTED[path.name]["rows"] if len(rows) != expected_rows: raise ValueError(f"row-count gate failed for {path.name}: {len(rows)} != {expected_rows}") observations, exclusions = humaine_observations(rows) items, encoded = pair_table(observations) start = time.perf_counter() bt_skills, bt_iterations = fit_bt(items, encoded) bt_seconds = time.perf_counter() - start bbq = fit_bbq(items, encoded) metrics = Counter(str(row.get("metric")) for row in rows) return { "rows": len(rows), "metric_rows": dict(metrics), "usable_non_tie_observations": len(encoded), "excluded_rows": dict(exclusions), "items": len(items), "raters_or_conversation_ids": len({row["rater"] for row in observations}), "bt_iterations": bt_iterations, "bt_seconds": bt_seconds, "bbq_iterations": bbq["iterations"], "bbq_seconds": bbq["seconds"], "bbq_over_bt_seconds_ratio": bbq["seconds"] / bt_seconds if bt_seconds else None, } def main() -> None: parser = argparse.ArgumentParser() parser.add_argument("--screened", type=Path, required=True) parser.add_argument("--unscreened", type=Path, required=True) parser.add_argument("--humaine-feedback", type=Path, required=True) parser.add_argument("--out", type=Path, required=True) args = parser.parse_args() inputs = [args.screened, args.unscreened, args.humaine_feedback] release_manifest = [verify_release(path) for path in inputs] result = { "release_inputs": release_manifest, "screened": summarize_clic(args.screened), "unscreened": summarize_clic(args.unscreened), "humaine_feedback": summarize_humaine(args.humaine_feedback), "controls": { "exact_row_gates": True, "all_skills_positive": True, "cpu_threads_requested": 2, "author_or_peer_code_used": False, }, } args.out.parent.mkdir(parents=True, exist_ok=True) args.out.write_text(json.dumps(result, indent=2, sort_keys=True) + "\n") if __name__ == "__main__": main()