File size: 5,075 Bytes
646ccfc | 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 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | #!/usr/bin/env python3
"""Audit chronological TRAIN vulnerable/fixed pairs for pairwise objectives.
This is a data-structure diagnostic only. It does not read VALID/TEST labels
or train a model. Similarity uses sets and is not a semantic equivalence test.
"""
from __future__ import annotations
import argparse
import hashlib
import json
import re
import statistics
from collections import Counter
from pathlib import Path
import pandas as pd
def sha256(path: Path) -> str:
h = hashlib.sha256()
with path.open("rb") as handle:
for block in iter(lambda: handle.read(1 << 20), b""):
h.update(block)
return h.hexdigest()
def audit(corpus_path: Path, train_split: Path) -> dict:
pair_ids = set(json.loads(train_split.read_text()))
corpus = pd.read_parquet(corpus_path)
train = corpus[corpus["pair_id"].isin(pair_ids)]
group_sizes = train.groupby("pair_id").size()
bad_pairs = []
label_sets = Counter()
line_jaccards, token_jaccards, line_deltas = [], [], []
exact_equal = nonempty_both = 0
token_re = re.compile(r"[A-Za-z_][A-Za-z0-9_]*|0[xX][0-9a-fA-F]+|\d+|[^\s]")
for pair_id, group in train.groupby("pair_id", sort=False):
labels = sorted(int(x) for x in group["target"])
label_sets[tuple(labels)] += 1
if len(group) != 2 or labels != [0, 1]:
bad_pairs.append({"pair_id": pair_id, "n_rows": len(group), "labels": labels})
continue
row = group.iloc[0]
vuln = str(row["func_vuln"] or "")
fixed = str(row["func_patch"] or "")
vuln_lines = {line.strip() for line in vuln.splitlines() if line.strip()}
fixed_lines = {line.strip() for line in fixed.splitlines() if line.strip()}
line_union = vuln_lines | fixed_lines
line_jaccards.append(len(vuln_lines & fixed_lines) / len(line_union) if line_union else 1.0)
line_deltas.append(len(vuln_lines ^ fixed_lines))
vuln_tokens = set(token_re.findall(vuln[:200_000]))
fixed_tokens = set(token_re.findall(fixed[:200_000]))
token_union = vuln_tokens | fixed_tokens
token_jaccards.append(len(vuln_tokens & fixed_tokens) / len(token_union)
if token_union else 1.0)
exact_equal += int(vuln == fixed)
nonempty_both += int(bool(vuln.strip() and fixed.strip()))
n_pairs = len(pair_ids)
if len(train) != 2 * n_pairs:
# Report and fail: split integrity is a prerequisite for interpreting pairs.
raise ValueError(f"expected {2*n_pairs} rows for {n_pairs} pair IDs, found {len(train)}")
def summary(values: list[float]) -> dict:
ordered = sorted(values)
return {"mean": statistics.mean(values), "median": statistics.median(values),
"p10": ordered[int(0.10 * len(ordered))],
"p90": ordered[int(0.90 * len(ordered))]}
return {
"analysis": "chronological TRAIN pair structure for exploratory pairwise objective",
"split": str(train_split),
"test_touched": False,
"train_pair_ids": n_pairs,
"train_rows": len(train),
"pair_group_sizes": {str(k): int(v) for k, v in group_sizes.value_counts().items()},
"label_pair_counts": {"/".join(map(str, k)): v for k, v in label_sets.items()},
"invalid_pair_count": len(bad_pairs),
"invalid_pair_examples": bad_pairs[:10],
"code_pair_similarity": {
"exact_function_equal_count": exact_equal,
"both_code_nonempty_fraction": nonempty_both / n_pairs,
"normalized_line_set_jaccard": summary(line_jaccards),
"normalized_token_set_jaccard": summary(token_jaccards),
"line_set_symmetric_difference": {
"median": statistics.median(line_deltas),
"p90": sorted(line_deltas)[int(0.90 * len(line_deltas))],
"fraction_at_most_two": sum(x <= 2 for x in line_deltas) / n_pairs,
},
},
"method_caveat": (
"Line/token set Jaccard ignores token order, repeated tokens, semantics, and patch correctness; "
"it only establishes structural relatedness, not that pairwise supervision will help."
),
"input_sha256": {str(corpus_path): sha256(corpus_path),
str(train_split): sha256(train_split)},
}
def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument("--corpus", type=Path, default=Path("data/processed/corpus.parquet"))
parser.add_argument("--train-split", type=Path,
default=Path("data/splits/chronological/train.json"))
parser.add_argument("--output", type=Path,
default=Path("results/phase6/pairwise_train_data_audit.json"))
args = parser.parse_args()
report = audit(args.corpus, args.train_split)
args.output.parent.mkdir(parents=True, exist_ok=True)
args.output.write_text(json.dumps(report, indent=2) + "\n")
print(json.dumps(report, indent=2))
if __name__ == "__main__":
main()
|