research: add task bootstrap evaluation pipeline
This commit is contained in:
@@ -0,0 +1,620 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Build task-paired bootstrap and multi-tape diagnostics for Round 08."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
from collections import Counter
|
||||
from pathlib import Path
|
||||
from statistics import mean, median
|
||||
from typing import Any
|
||||
|
||||
import numpy as np
|
||||
|
||||
|
||||
PROTOCOL_ID = "llm-atlas-deepseek-chat-task-bootstrap-crn-v1"
|
||||
CONDITIONS = (
|
||||
"s0_eos",
|
||||
"s1_eos",
|
||||
"s0_period",
|
||||
"s1_period",
|
||||
)
|
||||
DOMAINS = ("code", "math")
|
||||
BOOTSTRAP_SEED = 1364512825
|
||||
BOOTSTRAP_RESAMPLES = 10000
|
||||
CONTRASTS = {
|
||||
"period_at_s0": ("s0_eos", "s0_period"),
|
||||
"period_at_s1": ("s1_eos", "s1_period"),
|
||||
"system_at_eos": ("s0_eos", "s1_eos"),
|
||||
"system_at_period": ("s0_period", "s1_period"),
|
||||
}
|
||||
METRICS = (
|
||||
"fixed_budget_success",
|
||||
"strict_complete_success",
|
||||
"natural_eos",
|
||||
"generated_tokens",
|
||||
)
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--sampling-json", type=Path, required=True)
|
||||
parser.add_argument("--evaluation-json", type=Path, required=True)
|
||||
parser.add_argument("--reproduction-json", type=Path)
|
||||
parser.add_argument("--output", type=Path, required=True)
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def sha256_file(path: Path) -> str:
|
||||
digest = hashlib.sha256()
|
||||
with path.open("rb") as handle:
|
||||
while chunk := handle.read(16 * 1024 * 1024):
|
||||
digest.update(chunk)
|
||||
return digest.hexdigest()
|
||||
|
||||
|
||||
def canonical_hash(value: Any) -> str:
|
||||
return hashlib.sha256(
|
||||
json.dumps(
|
||||
value,
|
||||
ensure_ascii=False,
|
||||
sort_keys=True,
|
||||
separators=(",", ":"),
|
||||
).encode()
|
||||
).hexdigest()
|
||||
|
||||
|
||||
def metric_value(row: dict[str, Any], metric: str) -> float:
|
||||
evaluation = row["task_evaluation"]
|
||||
if metric == "fixed_budget_success":
|
||||
key = (
|
||||
"fixed_budget_tests_pass"
|
||||
if row["domain"] == "code"
|
||||
else "fixed_budget_numeric_exact"
|
||||
)
|
||||
return float(evaluation[key])
|
||||
if metric == "strict_complete_success":
|
||||
key = (
|
||||
"strict_complete_tests_pass"
|
||||
if row["domain"] == "code"
|
||||
else "strict_complete_numeric_exact"
|
||||
)
|
||||
return float(evaluation[key])
|
||||
if metric == "natural_eos":
|
||||
return float(row["hit_eos"])
|
||||
if metric == "generated_tokens":
|
||||
return float(row["generated_tokens"])
|
||||
raise KeyError(metric)
|
||||
|
||||
|
||||
def summarize(values: list[float]) -> dict[str, Any]:
|
||||
return {
|
||||
"count": len(values),
|
||||
"mean": mean(values) if values else None,
|
||||
"median": median(values) if values else None,
|
||||
"min": min(values) if values else None,
|
||||
"max": max(values) if values else None,
|
||||
}
|
||||
|
||||
|
||||
def percentile_band(values: np.ndarray) -> dict[str, float]:
|
||||
quantiles = np.percentile(values, [2.5, 50.0, 97.5])
|
||||
return {
|
||||
"p2_5": float(quantiles[0]),
|
||||
"p50": float(quantiles[1]),
|
||||
"p97_5": float(quantiles[2]),
|
||||
}
|
||||
|
||||
|
||||
def direction_counts(values: list[float]) -> dict[str, int]:
|
||||
epsilon = 1e-12
|
||||
return {
|
||||
"positive": sum(value > epsilon for value in values),
|
||||
"zero": sum(abs(value) <= epsilon for value in values),
|
||||
"negative": sum(value < -epsilon for value in values),
|
||||
}
|
||||
|
||||
|
||||
def success_transition(
|
||||
left: list[float],
|
||||
right: list[float],
|
||||
) -> dict[str, int]:
|
||||
pairs = [(int(a), int(b)) for a, b in zip(left, right, strict=True)]
|
||||
return {
|
||||
"fail_to_fail": sum(a == 0 and b == 0 for a, b in pairs),
|
||||
"fail_to_pass": sum(a == 0 and b == 1 for a, b in pairs),
|
||||
"pass_to_fail": sum(a == 1 and b == 0 for a, b in pairs),
|
||||
"pass_to_pass": sum(a == 1 and b == 1 for a, b in pairs),
|
||||
}
|
||||
|
||||
|
||||
def common_prefix(left: list[int], right: list[int]) -> int:
|
||||
count = 0
|
||||
for left_id, right_id in zip(left, right):
|
||||
if left_id != right_id:
|
||||
break
|
||||
count += 1
|
||||
return count
|
||||
|
||||
|
||||
def tape_uint64(tape: str, source_id: str, step: int) -> int:
|
||||
payload = (
|
||||
f"{PROTOCOL_ID}\0uniform\0"
|
||||
f"{tape}\0{source_id}\0{step}"
|
||||
).encode()
|
||||
return int.from_bytes(hashlib.sha256(payload).digest()[:8], "big")
|
||||
|
||||
|
||||
def build_indices(
|
||||
sampling: dict[str, Any],
|
||||
evaluation: dict[str, Any],
|
||||
) -> tuple[
|
||||
dict[tuple[str, str, str], dict[str, Any]],
|
||||
dict[tuple[str, str, str], dict[str, Any]],
|
||||
]:
|
||||
sample_index = {
|
||||
(
|
||||
source["id"],
|
||||
run["tape_label"],
|
||||
output["condition"],
|
||||
): output
|
||||
for source in sampling["sources"]
|
||||
for run in source["runs"]
|
||||
for output in run["outputs"]
|
||||
}
|
||||
eval_index = {
|
||||
(
|
||||
row["source_id"],
|
||||
row["tape_label"],
|
||||
row["condition"],
|
||||
): row
|
||||
for row in evaluation["rows"]
|
||||
}
|
||||
if set(sample_index) != set(eval_index):
|
||||
raise RuntimeError("sampling/evaluation cell keys differ")
|
||||
return sample_index, eval_index
|
||||
|
||||
|
||||
def main_analysis(
|
||||
*,
|
||||
sampling: dict[str, Any],
|
||||
sample_index: dict[tuple[str, str, str], dict[str, Any]],
|
||||
eval_index: dict[tuple[str, str, str], dict[str, Any]],
|
||||
) -> dict[str, Any]:
|
||||
result = {}
|
||||
for domain in DOMAINS:
|
||||
sources = [
|
||||
source
|
||||
for source in sampling["sources"]
|
||||
if source["domain"] == domain
|
||||
]
|
||||
sources.sort(key=lambda row: row["within_domain_index"])
|
||||
if len(sources) != 32:
|
||||
raise RuntimeError(f"{domain}: main frame must have 32 tasks")
|
||||
source_ids = [source["id"] for source in sources]
|
||||
rng = np.random.default_rng(BOOTSTRAP_SEED)
|
||||
sampled_indices = rng.integers(
|
||||
0,
|
||||
len(source_ids),
|
||||
size=(BOOTSTRAP_RESAMPLES, len(source_ids)),
|
||||
endpoint=False,
|
||||
)
|
||||
conditions = {}
|
||||
for condition in CONDITIONS:
|
||||
rows = [
|
||||
eval_index[(source_id, "T0", condition)]
|
||||
for source_id in source_ids
|
||||
]
|
||||
conditions[condition] = {
|
||||
metric: summarize(
|
||||
[metric_value(row, metric) for row in rows]
|
||||
)
|
||||
for metric in METRICS
|
||||
} | {
|
||||
"task_outcomes": dict(
|
||||
sorted(
|
||||
Counter(row["task_outcome"] for row in rows).items()
|
||||
)
|
||||
)
|
||||
}
|
||||
contrasts = {}
|
||||
for name, (left_condition, right_condition) in CONTRASTS.items():
|
||||
metrics = {}
|
||||
for metric in METRICS:
|
||||
left = [
|
||||
metric_value(
|
||||
eval_index[(source_id, "T0", left_condition)],
|
||||
metric,
|
||||
)
|
||||
for source_id in source_ids
|
||||
]
|
||||
right = [
|
||||
metric_value(
|
||||
eval_index[(source_id, "T0", right_condition)],
|
||||
metric,
|
||||
)
|
||||
for source_id in source_ids
|
||||
]
|
||||
differences = np.asarray(right) - np.asarray(left)
|
||||
bootstrap_means = differences[
|
||||
sampled_indices
|
||||
].mean(axis=1)
|
||||
metric_result = {
|
||||
"right_minus_left_point": float(differences.mean()),
|
||||
"selected_task_resampling_band": percentile_band(
|
||||
bootstrap_means
|
||||
),
|
||||
"task_differences": summarize(
|
||||
differences.tolist()
|
||||
),
|
||||
"direction_counts": direction_counts(
|
||||
differences.tolist()
|
||||
),
|
||||
"by_source": [
|
||||
{
|
||||
"source_id": source_id,
|
||||
"domain_index": index,
|
||||
"left": float(left[index]),
|
||||
"right": float(right[index]),
|
||||
"right_minus_left": float(
|
||||
differences[index]
|
||||
),
|
||||
}
|
||||
for index, source_id in enumerate(source_ids)
|
||||
],
|
||||
}
|
||||
if metric in {
|
||||
"fixed_budget_success",
|
||||
"strict_complete_success",
|
||||
}:
|
||||
metric_result["transition"] = success_transition(
|
||||
left,
|
||||
right,
|
||||
)
|
||||
metrics[metric] = metric_result
|
||||
|
||||
trajectory_rows = []
|
||||
crn_exact = 0
|
||||
for source_id in source_ids:
|
||||
left_output = sample_index[
|
||||
(source_id, "T0", left_condition)
|
||||
]
|
||||
right_output = sample_index[
|
||||
(source_id, "T0", right_condition)
|
||||
]
|
||||
left_ids = left_output["generated_token_ids"]
|
||||
right_ids = right_output["generated_token_ids"]
|
||||
shared_steps = min(len(left_ids), len(right_ids))
|
||||
expected_left_uniforms = [
|
||||
tape_uint64("T0", source_id, step)
|
||||
for step in range(
|
||||
left_output["uniform_steps_consumed"]
|
||||
)
|
||||
]
|
||||
expected_right_uniforms = [
|
||||
tape_uint64("T0", source_id, step)
|
||||
for step in range(
|
||||
right_output["uniform_steps_consumed"]
|
||||
)
|
||||
]
|
||||
left_uniform_exact = (
|
||||
canonical_hash(expected_left_uniforms)
|
||||
== left_output["uniform_uint64_prefix_sha256"]
|
||||
)
|
||||
right_uniform_exact = (
|
||||
canonical_hash(expected_right_uniforms)
|
||||
== right_output["uniform_uint64_prefix_sha256"]
|
||||
)
|
||||
crn_exact += left_uniform_exact and right_uniform_exact
|
||||
trajectory_rows.append(
|
||||
{
|
||||
"source_id": source_id,
|
||||
"shared_active_steps": shared_steps,
|
||||
"common_prefix_tokens": common_prefix(
|
||||
left_ids,
|
||||
right_ids,
|
||||
),
|
||||
"token_ids_exact": left_ids == right_ids,
|
||||
"shared_uniform_prefix_exact": (
|
||||
left_uniform_exact and right_uniform_exact
|
||||
),
|
||||
}
|
||||
)
|
||||
contrasts[name] = {
|
||||
"left": left_condition,
|
||||
"right": right_condition,
|
||||
"metrics": metrics,
|
||||
"trajectory": {
|
||||
"sources": len(trajectory_rows),
|
||||
"shared_uniform_prefix_exact": crn_exact,
|
||||
"exact_trajectories": sum(
|
||||
row["token_ids_exact"]
|
||||
for row in trajectory_rows
|
||||
),
|
||||
"common_prefix_tokens": summarize(
|
||||
[
|
||||
float(row["common_prefix_tokens"])
|
||||
for row in trajectory_rows
|
||||
]
|
||||
),
|
||||
"rows": trajectory_rows,
|
||||
},
|
||||
}
|
||||
result[domain] = {
|
||||
"tasks": len(source_ids),
|
||||
"source_ids": source_ids,
|
||||
"tape": "T0",
|
||||
"conditions": conditions,
|
||||
"contrasts": contrasts,
|
||||
}
|
||||
return result
|
||||
|
||||
|
||||
def tape_diagnostic(
|
||||
*,
|
||||
sampling: dict[str, Any],
|
||||
eval_index: dict[tuple[str, str, str], dict[str, Any]],
|
||||
) -> dict[str, Any]:
|
||||
result = {}
|
||||
for domain in DOMAINS:
|
||||
sources = [
|
||||
source
|
||||
for source in sampling["sources"]
|
||||
if source["domain"] == domain and len(source["runs"]) == 4
|
||||
]
|
||||
sources.sort(key=lambda row: row["within_domain_index"])
|
||||
if [row["within_domain_index"] for row in sources] != [
|
||||
0,
|
||||
8,
|
||||
16,
|
||||
24,
|
||||
]:
|
||||
raise RuntimeError(
|
||||
f"{domain}: diagnostic task indices differ"
|
||||
)
|
||||
source_ids = [row["id"] for row in sources]
|
||||
tapes = ["T0", "T1", "T2", "T3"]
|
||||
by_tape_condition = {
|
||||
tape: {
|
||||
condition: {
|
||||
metric: summarize(
|
||||
[
|
||||
metric_value(
|
||||
eval_index[
|
||||
(source_id, tape, condition)
|
||||
],
|
||||
metric,
|
||||
)
|
||||
for source_id in source_ids
|
||||
]
|
||||
)
|
||||
for metric in METRICS
|
||||
}
|
||||
for condition in CONDITIONS
|
||||
}
|
||||
for tape in tapes
|
||||
}
|
||||
contrasts = {}
|
||||
for name, (left_condition, right_condition) in CONTRASTS.items():
|
||||
metric_results = {}
|
||||
for metric in METRICS:
|
||||
matrix = np.asarray(
|
||||
[
|
||||
[
|
||||
metric_value(
|
||||
eval_index[
|
||||
(
|
||||
source_id,
|
||||
tape,
|
||||
right_condition,
|
||||
)
|
||||
],
|
||||
metric,
|
||||
)
|
||||
- metric_value(
|
||||
eval_index[
|
||||
(
|
||||
source_id,
|
||||
tape,
|
||||
left_condition,
|
||||
)
|
||||
],
|
||||
metric,
|
||||
)
|
||||
for tape in tapes
|
||||
]
|
||||
for source_id in source_ids
|
||||
],
|
||||
dtype=float,
|
||||
)
|
||||
tape_means = matrix.mean(axis=0)
|
||||
task_means = matrix.mean(axis=1)
|
||||
task_ranges_within_tape = (
|
||||
matrix.max(axis=0) - matrix.min(axis=0)
|
||||
)
|
||||
tape_ranges_within_task = (
|
||||
matrix.max(axis=1) - matrix.min(axis=1)
|
||||
)
|
||||
metric_results[metric] = {
|
||||
"matrix_task_by_tape": matrix.tolist(),
|
||||
"tape_means": {
|
||||
tape: float(tape_means[index])
|
||||
for index, tape in enumerate(tapes)
|
||||
},
|
||||
"task_means": {
|
||||
source_id: float(task_means[index])
|
||||
for index, source_id in enumerate(source_ids)
|
||||
},
|
||||
"direction_by_tape": {
|
||||
tape: direction_counts(
|
||||
matrix[:, index].tolist()
|
||||
)
|
||||
for index, tape in enumerate(tapes)
|
||||
},
|
||||
"task_range_within_tape": summarize(
|
||||
task_ranges_within_tape.tolist()
|
||||
),
|
||||
"tape_range_within_task": summarize(
|
||||
tape_ranges_within_task.tolist()
|
||||
),
|
||||
"grand_mean_descriptive": float(matrix.mean()),
|
||||
}
|
||||
contrasts[name] = {
|
||||
"left": left_condition,
|
||||
"right": right_condition,
|
||||
"metrics": metric_results,
|
||||
}
|
||||
result[domain] = {
|
||||
"tasks": len(source_ids),
|
||||
"source_ids": source_ids,
|
||||
"tapes": tapes,
|
||||
"by_tape_condition": by_tape_condition,
|
||||
"contrasts": contrasts,
|
||||
"independence_warning": (
|
||||
"The 4 tasks x 4 tapes are crossed repeated measures, "
|
||||
"not 16 independent tasks."
|
||||
),
|
||||
}
|
||||
return result
|
||||
|
||||
|
||||
def main() -> None:
|
||||
args = parse_args()
|
||||
for path in (args.sampling_json, args.evaluation_json):
|
||||
if not path.is_file():
|
||||
raise FileNotFoundError(path)
|
||||
sampling = json.loads(
|
||||
args.sampling_json.read_text(encoding="utf-8")
|
||||
)
|
||||
evaluation = json.loads(
|
||||
args.evaluation_json.read_text(encoding="utf-8")
|
||||
)
|
||||
if (
|
||||
sampling["protocol_id"] != PROTOCOL_ID
|
||||
or evaluation["protocol_id"] != PROTOCOL_ID
|
||||
):
|
||||
raise RuntimeError("protocol ID differs")
|
||||
if sampling["summary"]["outputs"] != 352:
|
||||
raise RuntimeError("sampling formal output count differs")
|
||||
if len(evaluation["rows"]) != 352:
|
||||
raise RuntimeError("evaluation row count differs")
|
||||
sample_index, eval_index = build_indices(sampling, evaluation)
|
||||
reproduction = None
|
||||
if args.reproduction_json is not None:
|
||||
if not args.reproduction_json.is_file():
|
||||
raise FileNotFoundError(args.reproduction_json)
|
||||
reproduction_payload = json.loads(
|
||||
args.reproduction_json.read_text(encoding="utf-8")
|
||||
)
|
||||
if reproduction_payload["protocol_id"] != PROTOCOL_ID:
|
||||
raise RuntimeError("reproduction protocol ID differs")
|
||||
reproduction = {
|
||||
"path": str(args.reproduction_json),
|
||||
"sha256": sha256_file(args.reproduction_json),
|
||||
"summary": reproduction_payload["summary"],
|
||||
}
|
||||
result = {
|
||||
"schema_version": 1,
|
||||
"protocol_id": PROTOCOL_ID,
|
||||
"inputs": {
|
||||
"sampling": {
|
||||
"path": str(args.sampling_json),
|
||||
"sha256": sha256_file(args.sampling_json),
|
||||
"content_hash": sampling["content_hash"],
|
||||
},
|
||||
"evaluation": {
|
||||
"path": str(args.evaluation_json),
|
||||
"sha256": sha256_file(args.evaluation_json),
|
||||
"content_hash": evaluation["content_hash"],
|
||||
},
|
||||
"reproduction": reproduction,
|
||||
},
|
||||
"bootstrap_contract": {
|
||||
"resamples": BOOTSTRAP_RESAMPLES,
|
||||
"seed": BOOTSTRAP_SEED,
|
||||
"rng": "numpy.random.default_rng reset per domain",
|
||||
"unit": "selected task",
|
||||
"paired_conditions": True,
|
||||
"tape": "T0",
|
||||
"interval_label": (
|
||||
"selected-task resampling band for the fixed "
|
||||
"32-task frame and T0"
|
||||
),
|
||||
"not": [
|
||||
"benchmark-population confidence interval",
|
||||
"model-ability confidence interval",
|
||||
"generation-seed uncertainty interval",
|
||||
"causal-effect confidence interval",
|
||||
],
|
||||
},
|
||||
"main_t0_selected_task_analysis": main_analysis(
|
||||
sampling=sampling,
|
||||
sample_index=sample_index,
|
||||
eval_index=eval_index,
|
||||
),
|
||||
"multi_tape_diagnostic": tape_diagnostic(
|
||||
sampling=sampling,
|
||||
eval_index=eval_index,
|
||||
),
|
||||
"claim_boundary": [
|
||||
"HumanEval and GSM8K are analyzed separately.",
|
||||
(
|
||||
"T0 selected-task bands describe only this frozen "
|
||||
"32-task frame."
|
||||
),
|
||||
(
|
||||
"T1-T3 are sensitivity diagnostics and are not pooled "
|
||||
"into the T0 primary success rates."
|
||||
),
|
||||
(
|
||||
"Common random numbers align probability quantiles; "
|
||||
"they do not force identical sampled tokens."
|
||||
),
|
||||
"Period prompts are counterfactual, not official-valid chats.",
|
||||
],
|
||||
}
|
||||
result["content_hash"] = canonical_hash(
|
||||
{
|
||||
"bootstrap_contract": result["bootstrap_contract"],
|
||||
"main": result["main_t0_selected_task_analysis"],
|
||||
"diagnostic": result["multi_tape_diagnostic"],
|
||||
}
|
||||
)
|
||||
args.output.parent.mkdir(parents=True, exist_ok=True)
|
||||
args.output.write_text(
|
||||
json.dumps(result, ensure_ascii=False, indent=2) + "\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
payload = args.output.read_bytes()
|
||||
compact = {
|
||||
domain: {
|
||||
name: {
|
||||
metric: result[
|
||||
"main_t0_selected_task_analysis"
|
||||
][domain]["contrasts"][name]["metrics"][metric][
|
||||
"right_minus_left_point"
|
||||
]
|
||||
for metric in METRICS
|
||||
}
|
||||
for name in CONTRASTS
|
||||
}
|
||||
for domain in DOMAINS
|
||||
}
|
||||
print(
|
||||
json.dumps(
|
||||
{
|
||||
"output": str(args.output),
|
||||
"sha256": hashlib.sha256(payload).hexdigest(),
|
||||
"bytes": len(payload),
|
||||
"main_contrast_points": compact,
|
||||
},
|
||||
ensure_ascii=False,
|
||||
indent=2,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,364 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Evaluate Round 08 while keeping T0 and multi-tape diagnostics separate."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
from collections import Counter
|
||||
from pathlib import Path
|
||||
from statistics import mean
|
||||
from typing import Any
|
||||
|
||||
import v2_lite_chat_completion_evaluator as completion
|
||||
import v2_lite_chat_sampling_evaluator as shared
|
||||
|
||||
|
||||
PROTOCOL_ID = "llm-atlas-deepseek-chat-task-bootstrap-crn-v1"
|
||||
CONDITIONS = (
|
||||
"s0_eos",
|
||||
"s1_eos",
|
||||
"s0_period",
|
||||
"s1_period",
|
||||
)
|
||||
DOMAINS = ("code", "math")
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--sampling-json", type=Path, required=True)
|
||||
parser.add_argument("--human-eval", type=Path, required=True)
|
||||
parser.add_argument("--gsm8k", type=Path, required=True)
|
||||
parser.add_argument("--output", type=Path, required=True)
|
||||
parser.add_argument(
|
||||
"--sandbox-image",
|
||||
required=True,
|
||||
help="Pinned image reference including @sha256 digest.",
|
||||
)
|
||||
parser.add_argument("--timeout-seconds", type=float, default=5.0)
|
||||
parser.add_argument("--skip-code-execution", action="store_true")
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def task_outcome(
|
||||
domain: str,
|
||||
evaluation: dict[str, Any],
|
||||
) -> str:
|
||||
if domain == "math":
|
||||
if evaluation["fixed_budget_numeric_exact"]:
|
||||
return "passed"
|
||||
if evaluation["predicted_final"] is None:
|
||||
return "no_numeric_answer"
|
||||
return "wrong_numeric_answer"
|
||||
if not evaluation["contains_entry_point_definition"]:
|
||||
return "extract_failed"
|
||||
if not evaluation["python_ast_parse"]:
|
||||
return "syntax_error"
|
||||
status = evaluation["execution"]["status"]
|
||||
return {
|
||||
"passed": "passed",
|
||||
"timeout": "timeout",
|
||||
"runtime_error": "runtime_error",
|
||||
"assertion_failed": "assertion_failed",
|
||||
"not_run": "not_run",
|
||||
}.get(status, f"execution_{status}")
|
||||
|
||||
|
||||
def success(row: dict[str, Any], *, strict: bool = False) -> int:
|
||||
evaluation = row["task_evaluation"]
|
||||
if row["domain"] == "code":
|
||||
key = (
|
||||
"strict_complete_tests_pass"
|
||||
if strict
|
||||
else "fixed_budget_tests_pass"
|
||||
)
|
||||
else:
|
||||
key = (
|
||||
"strict_complete_numeric_exact"
|
||||
if strict
|
||||
else "fixed_budget_numeric_exact"
|
||||
)
|
||||
return int(evaluation[key])
|
||||
|
||||
|
||||
def compact_summary(rows: list[dict[str, Any]]) -> dict[str, Any]:
|
||||
result = {
|
||||
"outputs": len(rows),
|
||||
"sources": len({row["source_id"] for row in rows}),
|
||||
"tapes": sorted({row["tape_label"] for row in rows}),
|
||||
"natural_eos": sum(row["hit_eos"] for row in rows),
|
||||
"budget_truncated": sum(
|
||||
row["stopped_at_max_new_tokens"] for row in rows
|
||||
),
|
||||
"mean_generated_tokens": (
|
||||
mean(row["generated_tokens"] for row in rows)
|
||||
if rows
|
||||
else None
|
||||
),
|
||||
"fixed_budget_success": sum(success(row) for row in rows),
|
||||
"strict_complete_success": sum(
|
||||
success(row, strict=True) for row in rows
|
||||
),
|
||||
"task_outcomes": dict(
|
||||
sorted(Counter(row["task_outcome"] for row in rows).items())
|
||||
),
|
||||
"completion_classes": dict(
|
||||
sorted(
|
||||
Counter(
|
||||
row["completion_class"] for row in rows
|
||||
).items()
|
||||
)
|
||||
),
|
||||
}
|
||||
result["by_domain"] = {
|
||||
domain: compact_summary_no_recursion(
|
||||
[row for row in rows if row["domain"] == domain]
|
||||
)
|
||||
for domain in DOMAINS
|
||||
}
|
||||
return result
|
||||
|
||||
|
||||
def compact_summary_no_recursion(
|
||||
rows: list[dict[str, Any]],
|
||||
) -> dict[str, Any]:
|
||||
return {
|
||||
"outputs": len(rows),
|
||||
"sources": len({row["source_id"] for row in rows}),
|
||||
"tapes": sorted({row["tape_label"] for row in rows}),
|
||||
"natural_eos": sum(row["hit_eos"] for row in rows),
|
||||
"budget_truncated": sum(
|
||||
row["stopped_at_max_new_tokens"] for row in rows
|
||||
),
|
||||
"mean_generated_tokens": (
|
||||
mean(row["generated_tokens"] for row in rows)
|
||||
if rows
|
||||
else None
|
||||
),
|
||||
"fixed_budget_success": sum(success(row) for row in rows),
|
||||
"strict_complete_success": sum(
|
||||
success(row, strict=True) for row in rows
|
||||
),
|
||||
"task_outcomes": dict(
|
||||
sorted(Counter(row["task_outcome"] for row in rows).items())
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
def main() -> None:
|
||||
args = parse_args()
|
||||
for path in (
|
||||
args.sampling_json,
|
||||
args.human_eval,
|
||||
args.gsm8k,
|
||||
):
|
||||
if not path.is_file():
|
||||
raise FileNotFoundError(path)
|
||||
if "@sha256:" not in args.sandbox_image:
|
||||
raise ValueError("--sandbox-image must include immutable digest")
|
||||
if args.timeout_seconds <= 0:
|
||||
raise ValueError("--timeout-seconds must be positive")
|
||||
sampling = json.loads(
|
||||
args.sampling_json.read_text(encoding="utf-8")
|
||||
)
|
||||
if sampling["protocol_id"] != PROTOCOL_ID:
|
||||
raise RuntimeError("sampling protocol ID differs")
|
||||
if tuple(
|
||||
sampling["seed_contract"]["condition_row_order"]
|
||||
) != CONDITIONS:
|
||||
raise RuntimeError("sampling condition order differs")
|
||||
if sampling["summary"]["outputs"] != 352:
|
||||
raise RuntimeError("formal grid must contain 352 outputs")
|
||||
|
||||
human_eval, gsm8k = completion.load_tasks(
|
||||
args.human_eval,
|
||||
args.gsm8k,
|
||||
)
|
||||
code_cache: dict[
|
||||
tuple[str, str, str],
|
||||
dict[str, Any],
|
||||
] = {}
|
||||
rows = []
|
||||
for source in sampling["sources"]:
|
||||
for run in source["runs"]:
|
||||
for output in run["outputs"]:
|
||||
if source["domain"] == "math":
|
||||
evaluation = completion.evaluate_math(
|
||||
output["text"],
|
||||
output["hit_eos"],
|
||||
gsm8k[source["id"]]["answer"],
|
||||
)
|
||||
cache_hit = False
|
||||
else:
|
||||
evaluation, cache_hit = (
|
||||
shared.evaluate_code_cached(
|
||||
text=output["text"],
|
||||
hit_eos=output["hit_eos"],
|
||||
task=human_eval[source["id"]],
|
||||
image=args.sandbox_image,
|
||||
timeout_seconds=args.timeout_seconds,
|
||||
skip_execution=args.skip_code_execution,
|
||||
cache=code_cache,
|
||||
)
|
||||
)
|
||||
row = {
|
||||
"source_id": source["id"],
|
||||
"domain": source["domain"],
|
||||
"domain_index": source["within_domain_index"],
|
||||
"replicate_index": run["replicate_index"],
|
||||
"replicate_label": run["replicate_label"],
|
||||
"tape_label": run["tape_label"],
|
||||
"base_seed": run["base_seed"],
|
||||
"run_seed": run["run_seed"],
|
||||
"condition": output["condition"],
|
||||
"generated_tokens": output["generated_tokens"],
|
||||
"hit_eos": output["hit_eos"],
|
||||
"stopped_at_max_new_tokens": output[
|
||||
"stopped_at_max_new_tokens"
|
||||
],
|
||||
"prompt_token_ids_sha256": output[
|
||||
"prompt_token_ids_sha256"
|
||||
],
|
||||
"generated_token_ids_sha256": output[
|
||||
"generated_token_ids_sha256"
|
||||
],
|
||||
"text_sha256": output["text_sha256"],
|
||||
"uniform_uint64_prefix_sha256": output[
|
||||
"uniform_uint64_prefix_sha256"
|
||||
],
|
||||
"task_evaluation": evaluation,
|
||||
"completion_class": completion.completion_class(
|
||||
output,
|
||||
evaluation,
|
||||
),
|
||||
"code_execution_cache_hit": cache_hit,
|
||||
}
|
||||
row["task_outcome"] = task_outcome(
|
||||
source["domain"],
|
||||
evaluation,
|
||||
)
|
||||
rows.append(row)
|
||||
|
||||
main_rows = [
|
||||
row for row in rows if row["tape_label"] == "T0"
|
||||
]
|
||||
diagnostic_sources = {
|
||||
source["id"]
|
||||
for source in sampling["sources"]
|
||||
if len(source["runs"]) == 4
|
||||
}
|
||||
diagnostic_rows = [
|
||||
row
|
||||
for row in rows
|
||||
if row["source_id"] in diagnostic_sources
|
||||
]
|
||||
additional_rows = [
|
||||
row for row in rows if row["tape_label"] != "T0"
|
||||
]
|
||||
if (
|
||||
len(main_rows) != 256
|
||||
or len(diagnostic_rows) != 128
|
||||
or len(additional_rows) != 96
|
||||
):
|
||||
raise RuntimeError(
|
||||
"T0 / diagnostic grid counts differ from protocol"
|
||||
)
|
||||
result = {
|
||||
"schema_version": 1,
|
||||
"protocol_id": PROTOCOL_ID,
|
||||
"input": {
|
||||
"sampling_path": str(args.sampling_json),
|
||||
"sampling_sha256": completion.sha256_file(
|
||||
args.sampling_json
|
||||
),
|
||||
"sampling_content_hash": sampling["content_hash"],
|
||||
"human_eval_sha256": completion.sha256_file(
|
||||
args.human_eval
|
||||
),
|
||||
"gsm8k_sha256": completion.sha256_file(args.gsm8k),
|
||||
"model_revision": sampling["model"]["revision"],
|
||||
"max_new_tokens": sampling["generation_contract"][
|
||||
"max_new_tokens"
|
||||
],
|
||||
},
|
||||
"sandbox": {
|
||||
"image": args.sandbox_image,
|
||||
"timeout_seconds": args.timeout_seconds,
|
||||
"code_execution_skipped": args.skip_code_execution,
|
||||
"network": "none",
|
||||
"filesystem": "read-only",
|
||||
"user": "65534:65534",
|
||||
"capabilities": "ALL dropped",
|
||||
"no_new_privileges": True,
|
||||
"memory": "256m",
|
||||
"memory_swap": "256m",
|
||||
"pids_limit": 64,
|
||||
"cpus": 0.5,
|
||||
"tmpfs": "/tmp:rw,noexec,nosuid,size=16m",
|
||||
"host_mounts": 0,
|
||||
"unique_code_cache_entries": len(code_cache),
|
||||
},
|
||||
"rows": rows,
|
||||
"summary": {
|
||||
"formal_all_outputs": compact_summary(rows),
|
||||
"main_t0": compact_summary(main_rows),
|
||||
"diagnostic_all_four_tapes": compact_summary(
|
||||
diagnostic_rows
|
||||
),
|
||||
"diagnostic_additional_t1_t3": compact_summary(
|
||||
additional_rows
|
||||
),
|
||||
"main_t0_by_domain_condition": {
|
||||
domain: {
|
||||
condition: compact_summary_no_recursion(
|
||||
[
|
||||
row
|
||||
for row in main_rows
|
||||
if row["domain"] == domain
|
||||
and row["condition"] == condition
|
||||
]
|
||||
)
|
||||
for condition in CONDITIONS
|
||||
}
|
||||
for domain in DOMAINS
|
||||
},
|
||||
},
|
||||
"claim_boundary": [
|
||||
(
|
||||
"Primary summaries use T0 only; T1-T3 are isolated "
|
||||
"multi-tape sensitivity diagnostics."
|
||||
),
|
||||
"HumanEval and GSM8K are never pooled into one ability rate.",
|
||||
(
|
||||
"The frozen 32 tasks per domain are a selected task "
|
||||
"frame, not a full benchmark sample."
|
||||
),
|
||||
"Passing HumanEval tests is functional, not safety, evidence.",
|
||||
"Counterfactual period prompts are not official-valid chats.",
|
||||
],
|
||||
}
|
||||
result["content_hash"] = completion.canonical_hash(rows)
|
||||
args.output.parent.mkdir(parents=True, exist_ok=True)
|
||||
args.output.write_text(
|
||||
json.dumps(result, ensure_ascii=False, indent=2) + "\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
payload = args.output.read_bytes()
|
||||
print(
|
||||
json.dumps(
|
||||
{
|
||||
"output": str(args.output),
|
||||
"sha256": completion.sha256_bytes(payload),
|
||||
"bytes": len(payload),
|
||||
"rows": len(rows),
|
||||
"main_t0": result["summary"]["main_t0"],
|
||||
"unique_code_cache_entries": len(code_cache),
|
||||
},
|
||||
ensure_ascii=False,
|
||||
indent=2,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,224 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Compare the preregistered 64-cell Round 08 replay with the formal grid."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
|
||||
PROTOCOL_ID = "llm-atlas-deepseek-chat-task-bootstrap-crn-v1"
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--formal-json", type=Path, required=True)
|
||||
parser.add_argument("--rerun-json", type=Path, required=True)
|
||||
parser.add_argument("--output", type=Path, required=True)
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def sha256_file(path: Path) -> str:
|
||||
digest = hashlib.sha256()
|
||||
with path.open("rb") as handle:
|
||||
while chunk := handle.read(16 * 1024 * 1024):
|
||||
digest.update(chunk)
|
||||
return digest.hexdigest()
|
||||
|
||||
|
||||
def canonical_hash(value: Any) -> str:
|
||||
return hashlib.sha256(
|
||||
json.dumps(
|
||||
value,
|
||||
ensure_ascii=False,
|
||||
sort_keys=True,
|
||||
separators=(",", ":"),
|
||||
).encode()
|
||||
).hexdigest()
|
||||
|
||||
|
||||
def run_index(
|
||||
payload: dict[str, Any],
|
||||
) -> dict[
|
||||
tuple[str, str, str],
|
||||
tuple[dict[str, Any], dict[str, Any]],
|
||||
]:
|
||||
return {
|
||||
(
|
||||
source["id"],
|
||||
run["tape_label"],
|
||||
output["condition"],
|
||||
): (run, output)
|
||||
for source in payload["sources"]
|
||||
for run in source["runs"]
|
||||
for output in run["outputs"]
|
||||
}
|
||||
|
||||
|
||||
def main() -> None:
|
||||
args = parse_args()
|
||||
for path in (args.formal_json, args.rerun_json):
|
||||
if not path.is_file():
|
||||
raise FileNotFoundError(path)
|
||||
formal = json.loads(
|
||||
args.formal_json.read_text(encoding="utf-8")
|
||||
)
|
||||
rerun = json.loads(
|
||||
args.rerun_json.read_text(encoding="utf-8")
|
||||
)
|
||||
if (
|
||||
formal["protocol_id"] != PROTOCOL_ID
|
||||
or rerun["protocol_id"] != PROTOCOL_ID
|
||||
):
|
||||
raise RuntimeError("protocol ID differs")
|
||||
if formal["model"]["revision"] != rerun["model"]["revision"]:
|
||||
raise RuntimeError("model revision differs")
|
||||
if formal["generation_contract"] != rerun["generation_contract"]:
|
||||
raise RuntimeError("generation contract differs")
|
||||
if rerun["summary"]["outputs"] != 64:
|
||||
raise RuntimeError("replay grid must contain 64 outputs")
|
||||
formal_rows = run_index(formal)
|
||||
rerun_rows = run_index(rerun)
|
||||
if any(key[1] != "T0" for key in rerun_rows):
|
||||
raise RuntimeError("replay grid must use T0 only")
|
||||
missing = sorted(set(rerun_rows) - set(formal_rows))
|
||||
if missing:
|
||||
raise RuntimeError(
|
||||
f"{len(missing)} replay cells are absent from formal grid"
|
||||
)
|
||||
|
||||
rows = []
|
||||
for key in sorted(rerun_rows):
|
||||
formal_run, formal_output = formal_rows[key]
|
||||
rerun_run, rerun_output = rerun_rows[key]
|
||||
checks = {
|
||||
"run_seed_exact": (
|
||||
formal_run["run_seed"] == rerun_run["run_seed"]
|
||||
),
|
||||
"prompt_hash_exact": (
|
||||
formal_output["prompt_token_ids_sha256"]
|
||||
== rerun_output["prompt_token_ids_sha256"]
|
||||
),
|
||||
"uniform_run_hash_exact": (
|
||||
formal_run["uniform_uint64_sha256"]
|
||||
== rerun_run["uniform_uint64_sha256"]
|
||||
),
|
||||
"uniform_output_prefix_hash_exact": (
|
||||
formal_output["uniform_uint64_prefix_sha256"]
|
||||
== rerun_output["uniform_uint64_prefix_sha256"]
|
||||
),
|
||||
"uniform_steps_exact": (
|
||||
formal_output["uniform_steps_consumed"]
|
||||
== rerun_output["uniform_steps_consumed"]
|
||||
),
|
||||
"generated_token_ids_exact": (
|
||||
formal_output["generated_token_ids"]
|
||||
== rerun_output["generated_token_ids"]
|
||||
),
|
||||
"decoded_text_exact": (
|
||||
formal_output["text"] == rerun_output["text"]
|
||||
),
|
||||
"eos_state_exact": (
|
||||
formal_output["hit_eos"]
|
||||
== rerun_output["hit_eos"]
|
||||
),
|
||||
"truncation_state_exact": (
|
||||
formal_output["stopped_at_max_new_tokens"]
|
||||
== rerun_output["stopped_at_max_new_tokens"]
|
||||
),
|
||||
"cpu_rng_pre_state_exact": (
|
||||
formal_run["rng_state_before"]["cpu_sha256"]
|
||||
== rerun_run["rng_state_before"]["cpu_sha256"]
|
||||
),
|
||||
"cuda_rng_pre_state_exact": (
|
||||
formal_run["rng_state_before"][
|
||||
"cuda_combined_sha256"
|
||||
]
|
||||
== rerun_run["rng_state_before"][
|
||||
"cuda_combined_sha256"
|
||||
]
|
||||
),
|
||||
"torch_rng_unchanged_exact": (
|
||||
formal_run["torch_rng_unchanged"]
|
||||
and rerun_run["torch_rng_unchanged"]
|
||||
),
|
||||
}
|
||||
rows.append(
|
||||
{
|
||||
"source_id": key[0],
|
||||
"tape_label": key[1],
|
||||
"condition": key[2],
|
||||
**checks,
|
||||
"all_preregistered_fields_exact": all(
|
||||
checks.values()
|
||||
),
|
||||
}
|
||||
)
|
||||
check_names = [
|
||||
key
|
||||
for key in rows[0]
|
||||
if key.endswith("_exact")
|
||||
and key != "all_preregistered_fields_exact"
|
||||
]
|
||||
result = {
|
||||
"schema_version": 1,
|
||||
"protocol_id": PROTOCOL_ID,
|
||||
"formal": {
|
||||
"path": str(args.formal_json),
|
||||
"sha256": sha256_file(args.formal_json),
|
||||
"content_hash": formal["content_hash"],
|
||||
},
|
||||
"rerun": {
|
||||
"path": str(args.rerun_json),
|
||||
"sha256": sha256_file(args.rerun_json),
|
||||
"content_hash": rerun["content_hash"],
|
||||
},
|
||||
"rows": rows,
|
||||
"summary": {
|
||||
"cells": len(rows),
|
||||
"all_preregistered_fields_exact": sum(
|
||||
row["all_preregistered_fields_exact"]
|
||||
for row in rows
|
||||
),
|
||||
"by_field": {
|
||||
name: sum(row[name] for row in rows)
|
||||
for name in check_names
|
||||
},
|
||||
},
|
||||
"claim_boundary": [
|
||||
"Only the preregistered 16-source T0 subset is replayed.",
|
||||
(
|
||||
"Exact replay is scoped to the pinned checkpoint, "
|
||||
"software, sampler, precision, and hardware contract."
|
||||
),
|
||||
"Reproduction does not imply tape-invariant trajectories.",
|
||||
],
|
||||
}
|
||||
if result["summary"]["cells"] != 64:
|
||||
raise RuntimeError("reproduction comparison must contain 64 cells")
|
||||
result["content_hash"] = canonical_hash(rows)
|
||||
args.output.parent.mkdir(parents=True, exist_ok=True)
|
||||
args.output.write_text(
|
||||
json.dumps(result, ensure_ascii=False, indent=2) + "\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
payload = args.output.read_bytes()
|
||||
print(
|
||||
json.dumps(
|
||||
{
|
||||
"output": str(args.output),
|
||||
"sha256": hashlib.sha256(payload).hexdigest(),
|
||||
"bytes": len(payload),
|
||||
"summary": result["summary"],
|
||||
},
|
||||
ensure_ascii=False,
|
||||
indent=2,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user