File size: 4,319 Bytes
81f30c1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
"""Fases 0 (tarea 1) y 3 de docs/PLAN_MEJORA_RAG.md — recall de premisas con el gold contra Lean.

1. Por GOAL (`premises_gold_goals.jsonl`, unidad de RF-2): canal A (prior estático y filtrado por el
   goal), canal C (índice denso, la meta del goal como consulta) y la fusión que usa CLOSE.
2. Por PROBLEMA (`premises_gold_lean.jsonl`): la consulta de eval-v1 (enunciado NL / formal, índice
   denso) vs. el prior — la tabla 2.2 del plan, ahora con el gold que incluye el núcleo de Lean.
Recall@k = fracción de las premisas gold del goal/problema que aparecen en el top-k (media).
Uso: RAGSOLVER_MATHLIB_TABLE=mathlib_full python scripts/eval_premises_goals.py [--sample 600]
"""

from __future__ import annotations

import argparse
import json
import random
import tempfile
from pathlib import Path

KS = (8, 16, 32)


def recall(rows: list[tuple[set, list[str]]], k: int) -> float:
    return round(sum(len(g & set(r[:k])) / len(g) for g, r in rows) / len(rows), 3) if rows else 0.0


def main() -> None:
    ap = argparse.ArgumentParser()
    ap.add_argument("--goals", default="data/eval/premises_gold_goals.jsonl")
    ap.add_argument("--problems", default="data/eval/premises_gold_lean.jsonl")
    ap.add_argument("--sample", type=int, default=600, help="goals con canal C (embedder en CPU); el canal A corre sobre todos")
    ap.add_argument("--seed", type=int, default=13)
    ap.add_argument("--out", default="data/eval/results/premises_goals.json")
    args = ap.parse_args()

    from ragsolver.orchestrator.graph import select_premises
    from ragsolver.rag import premises as P
    from ragsolver.rag.retrieve import Retriever
    from ragsolver.rag.schema import RetrievalRequest

    goals = [json.loads(l) for l in open(args.goals, encoding="utf-8") if l.strip()]
    probs = [json.loads(l) for l in open(args.problems, encoding="utf-8") if l.strip()]
    out: dict = {"n_goals": len(goals), "n_problems": len(probs)}

    # 1a. canal A sobre todos los goals
    for name, fn in (("A_static", lambda g: P.prior_channel(g, k=max(KS), filtered=False)), ("A_filtered", lambda g: P.prior_channel(g, k=max(KS)))):
        rows = [(set(x["premises"]), fn(x["goal"])) for x in goals]
        out[f"goal_{name}"] = {f"recall@{k}": recall(rows, k) for k in KS}
        print(name, out[f"goal_{name}"], flush=True)

    # 1b. canal C y fusión sobre una muestra fija
    r = Retriever()
    r.trace_dir = tempfile.mkdtemp(prefix="ragsolver-eval-")
    sample = random.Random(args.seed).sample(goals, min(args.sample, len(goals)))
    rows_a, rows_c, rows_f = [], [], []
    for x in sample:
        gold = set(x["premises"])
        names, dense = select_premises(r, x["goal"], k=max(KS), ctx={"problem_id": x["problem_id"], "agent": "eval"}, dense_channel=True)
        rows_c.append((gold, list(dense)))
        rows_f.append((gold, names))
        rows_a.append((gold, P.prior_channel(x["goal"], k=max(KS))))
    out["goal_sample"] = len(sample)
    for name, rows in (("A_filtered_sample", rows_a), ("C_dense_goal", rows_c), ("fused_A_C", rows_f)):
        out[f"goal_{name}"] = {f"recall@{k}": recall(rows, k) for k in KS}
        print(name, out[f"goal_{name}"], flush=True)

    # 2. por problema: consulta de eval-v1 vs prior, gold contra Lean
    for mode, field in (("nl", "informal_statement"), ("formal", "formal_statement")):
        rows = []
        for p in probs:
            res = r.search(RetrievalRequest(queries=[p[field][:2000]], mode=mode, corpora=["mathlib"], k=max(KS), n_candidates=50, rerank=False, include_figures=False, context={"problem_id": p["problem_id"], "agent": "eval"}))
            rows.append((set(p["premises"]), [h.metadata.get("decl_name") for h in res.hits]))
        out[f"problem_dense_{mode}"] = {f"recall@{k}": recall(rows, k) for k in KS}
        print("problem", mode, out[f"problem_dense_{mode}"], flush=True)
    for name, filtered in (("problem_prior_static", False), ("problem_prior_filtered", True)):
        rows = [(set(p["premises"]), P.prior_channel(p["formal_statement"], k=max(KS), filtered=filtered)) for p in probs]
        out[name] = {f"recall@{k}": recall(rows, k) for k in KS}
        print(name, out[name], flush=True)
    Path(args.out).write_text(json.dumps(out, indent=2), encoding="utf-8")


if __name__ == "__main__":
    main()