-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathrun_demo.py
More file actions
126 lines (108 loc) · 4.13 KB
/
Copy pathrun_demo.py
File metadata and controls
126 lines (108 loc) · 4.13 KB
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
120
121
122
123
124
125
126
"""ERA-style code-mutation demo — exercises Crucible Phase 5.1 end-to-end.
What it does:
1. Builds an ``AstLocalEditPolicy`` aimed at ``baseline.py``.
2. Proposes three concrete mutations (one per knob: ACTIVATION,
LEARNING_RATE, HIDDEN_DIM).
3. Applies each in a sandboxed clone, scores via ``scorer.py``,
compares to the unmutated baseline.
4. Prints the leaderboard.
No LLM needed — the mutations are hand-picked. For a richer demo
that uses ``LlmDiffPolicy`` plus an orchestrator's LLM, replace
``_HAND_PICKED_MUTATIONS`` with diffs returned by your LLM via
``llm_diff_request_prompt`` / ``llm_diff_parse_response``.
Run:
PYTHONPATH=src python3 examples/code_mutation_era_replica/run_demo.py
"""
from __future__ import annotations
import json
from pathlib import Path
from crucible.researcher.code_mutation import (
AstLocalEditPolicy,
MutationProposal,
SandboxConfig,
SandboxRunner,
ScorerConfig,
score_stdout,
)
_HERE = Path(__file__).resolve().parent
_PROJECT_ROOT = _HERE # the example dir IS the sandboxed project for this demo
_HAND_PICKED_MUTATIONS = [
MutationProposal(
name="gelu_activation",
target_file="baseline.py",
diff=json.dumps({"kind": "swap_literal", "old": "relu", "new": "gelu"}),
hypothesis="GELU is smoother than ReLU near zero",
rationale="reduces dead-neuron failure mode on a 4-hidden-unit net",
mutation_scope=["baseline.py"],
),
MutationProposal(
name="lr_0p2",
target_file="baseline.py",
diff=json.dumps({"kind": "swap_literal", "old": 0.5, "new": 0.2}),
hypothesis="LR=0.5 may be too aggressive for 50 epochs",
rationale="smaller LR stabilises late-epoch oscillation",
mutation_scope=["baseline.py"],
),
MutationProposal(
name="hidden_dim_16",
target_file="baseline.py",
diff=json.dumps({"kind": "swap_literal", "old": 4, "new": 16}),
hypothesis="4 hidden units underfit the sin+cos target",
rationale="2x sinusoid superposition needs ≥8 units empirically",
mutation_scope=["baseline.py"],
),
]
def _baseline_score() -> float | None:
"""Score the unmutated baseline so the leaderboard has a reference."""
import subprocess # noqa: S404 — scoring helper, not user-controlled
result = subprocess.run(
["python3", str(_HERE / "scorer.py")],
capture_output=True,
text=True,
timeout=60,
)
return score_stdout(result.stdout, r"val_bpb:([0-9]+\.?[0-9]*)")
def main() -> int:
print(f"# ERA-style mutation demo — project: {_PROJECT_ROOT}")
baseline = _baseline_score()
print(f"baseline val_bpb: {baseline}")
scorer = ScorerConfig(
cmd=["python3", "scorer.py"],
score_pattern=r"val_bpb:([0-9]+\.?[0-9]*)",
direction="minimize",
)
sandbox = SandboxRunner(
_PROJECT_ROOT, sandbox_root=Path("/tmp") / "code_mutation_era_replica_sandbox"
)
policy = AstLocalEditPolicy(
project_root=_PROJECT_ROOT,
scorer=scorer,
sandbox=sandbox,
sandbox_config=SandboxConfig(timeout_seconds=60),
)
leaderboard: list[tuple[str, float | None, str | None]] = [
("baseline", baseline, None),
]
for proposal in _HAND_PICKED_MUTATIONS:
problems = policy.validate(proposal)
if problems:
print(f" [{proposal.name}] validate: {problems}")
leaderboard.append((proposal.name, None, "; ".join(problems)))
continue
result = policy.apply(proposal)
flag = "ok" if result.success else "fail"
print(f" [{proposal.name}] {flag} score={result.score} err={result.error}")
leaderboard.append((proposal.name, result.score, result.error))
print("\n# Leaderboard (lower val_bpb is better)")
ranked = sorted(
leaderboard,
key=lambda row: (row[1] is None, row[1] if row[1] is not None else 0.0),
)
for name, score, err in ranked:
if score is None:
print(f" {name:24s} FAIL {err}")
else:
print(f" {name:24s} {score:.6f}")
return 0
if __name__ == "__main__":
raise SystemExit(main())