111 lines
4.5 KiB
Python
111 lines
4.5 KiB
Python
#!/usr/bin/env python3
|
|
"""exp15 — assemble the FINAL 30-节 trap set: merge deterministic straddles (straddle_ledger.json:
|
|
23 T-ent control + 7 T-ana) with kimi enrichment (ana_enrichment.json: T-ana/T-ell), dedup by
|
|
(type, boundary), require each to STRADDLE a flat-A0-30 greedy cut (common denominator), and map each
|
|
to the dependent chunk (i+1) so the judge can score each arm's rendering of that chunk.
|
|
|
|
Output final_traps.json: the trap set for Q1b (oracle vs flat) and Q3a (carryover) judging.
|
|
Run: python assemble_traps.py
|
|
"""
|
|
from __future__ import annotations
|
|
import json
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
|
from chunker import greedy_chunks
|
|
|
|
SD = Path("/home/ubuntu/books/gu-zhenren/exp15")
|
|
FLAT = SD / "S2prime_big_flat.txt"
|
|
OUT = SD / "final_traps.json"
|
|
|
|
|
|
def flat_boundaries(text):
|
|
ch = greedy_chunks(text)
|
|
offs, starts, pos = [], [0], 0
|
|
for i, c in enumerate(ch):
|
|
idx = text.find(c[:25], max(0, pos - 5))
|
|
if idx < 0:
|
|
idx = text.find(c[:25])
|
|
if i > 0 and idx >= 0:
|
|
offs.append(idx); starts.append(idx)
|
|
pos = idx + len(c) if idx >= 0 else pos + len(c)
|
|
return ch, offs, starts
|
|
|
|
|
|
def chunk_of_offset(starts, off):
|
|
c = 0
|
|
for i, s in enumerate(starts):
|
|
if off >= s:
|
|
c = i
|
|
return c
|
|
|
|
|
|
def straddled_boundary(text, offs, a_span, d_span):
|
|
ai = text.find(a_span[:25]); di = text.find(d_span[:25])
|
|
if ai < 0 or di < 0:
|
|
return None, None, None
|
|
lo, hi = min(ai, di), max(ai + len(a_span), di + len(d_span))
|
|
crossed = [k for k, cut in enumerate(offs) if lo < cut < hi]
|
|
if not crossed:
|
|
return None, ai, di
|
|
return crossed[0], ai, di # boundary index (0-based cut = between chunk k and k+1)
|
|
|
|
|
|
def main():
|
|
text = FLAT.read_text(encoding="utf-8")
|
|
ch, offs, starts = flat_boundaries(text)
|
|
traps = []
|
|
seen = set() # (type, boundary_cut)
|
|
|
|
def add(t, typ, control, a_span, d_span, phen, exp):
|
|
bcut, ai, di = straddled_boundary(text, offs, a_span, d_span)
|
|
if bcut is None:
|
|
return "not_straddling"
|
|
key = (typ, bcut)
|
|
if key in seen:
|
|
return "dup"
|
|
seen.add(key)
|
|
dep_chunk = chunk_of_offset(starts, di) # chunk containing the dependent (scored chunk)
|
|
anc_chunk = chunk_of_offset(starts, ai)
|
|
traps.append({"id": f"{typ}-{len(traps)+1}", "type": typ, "control": control,
|
|
"boundary_cut": bcut, "anc_chunk": anc_chunk, "dep_chunk": dep_chunk,
|
|
"anchor_span": a_span, "dependent_span": d_span,
|
|
"phenomenon": phen, "expected_ru": exp})
|
|
return "ok"
|
|
|
|
stats = {"ok": 0, "dup": 0, "not_straddling": 0}
|
|
# deterministic
|
|
det = json.load(open(SD / "straddle_ledger.json"))["traps"]
|
|
for t in det:
|
|
r = add(t, t["type"], t.get("control", False), t["anchor_span"], t["dependent_span"],
|
|
t["phenomenon"], t["expected_ru"])
|
|
stats[r] += 1
|
|
# enrichment
|
|
enr = json.load(open(SD / "ana_enrichment.json"))["straddles"]
|
|
for t in enr:
|
|
r = add(t, t.get("type", "T-ana"), False, t.get("antecedent_span", ""), t.get("dependent_span", ""),
|
|
t.get("phenomenon", ""), t.get("expected_ru", ""))
|
|
stats[r] += 1
|
|
|
|
from collections import Counter
|
|
by = Counter(t["type"] for t in traps)
|
|
prim = Counter(t["type"] for t in traps if not t["control"])
|
|
OUT.write_text(json.dumps({
|
|
"n": len(traps), "by_type": dict(by),
|
|
"quota": {"T-ana": {"have": prim.get("T-ana", 0), "need": 20, "meets": prim.get("T-ana", 0) >= 20},
|
|
"T-ent": {"have": by.get("T-ent", 0), "need": 20, "meets": by.get("T-ent", 0) >= 20, "role": "negative-control"},
|
|
"T-ell": {"have": by.get("T-ell", 0), "need": 10, "meets": by.get("T-ell", 0) >= 10, "note": "secondary; short = honest-power"}},
|
|
"undecidable_a_priori": ["T-tense", "T-cat", "T-gen", "T-reg"],
|
|
"merge_stats": stats,
|
|
"traps": traps,
|
|
}, ensure_ascii=False, indent=2), encoding="utf-8")
|
|
print(f"final trap set: {dict(by)} (total {len(traps)}) merge {stats}")
|
|
print(f" T-ana (primary): {prim.get('T-ana',0)} {'MEETS>=20' if prim.get('T-ana',0)>=20 else 'SHORT'}")
|
|
print(f" T-ent (control): {by.get('T-ent',0)} {'MEETS>=20' if by.get('T-ent',0)>=20 else 'SHORT'}")
|
|
print(f" T-ell (secondary): {by.get('T-ell',0)} (>=10? {by.get('T-ell',0)>=10})")
|
|
print(f" undecidable a-priori: T-tense/T-cat/T-gen/T-reg -> {OUT}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|