brainpatch 1.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. brainpatch/__init__.py +92 -0
  2. brainpatch/backends/__init__.py +19 -0
  3. brainpatch/backends/llamacpp.py +383 -0
  4. brainpatch/backends/mlx_backend.py +213 -0
  5. brainpatch/backends/transformers_backend.py +473 -0
  6. brainpatch/backends/vllm_backend.py +299 -0
  7. brainpatch/backends/vllm_worker.py +129 -0
  8. brainpatch/cli.py +825 -0
  9. brainpatch/config.py +245 -0
  10. brainpatch/datasets/__init__.py +20 -0
  11. brainpatch/datasets/contrast_sets.py +64 -0
  12. brainpatch/evaluation/__init__.py +28 -0
  13. brainpatch/evaluation/metrics.py +223 -0
  14. brainpatch/patch/__init__.py +64 -0
  15. brainpatch/patch/compiler.py +324 -0
  16. brainpatch/patch/format.py +489 -0
  17. brainpatch/patch/loader.py +312 -0
  18. brainpatch/patch/registry.py +300 -0
  19. brainpatch/patch/tensors.py +236 -0
  20. brainpatch/patch/validation.py +157 -0
  21. brainpatch/paths.py +184 -0
  22. brainpatch/py.typed +0 -0
  23. brainpatch/research/__init__.py +16 -0
  24. brainpatch/research/antisycophancy.py +348 -0
  25. brainpatch/research/behaviour_eval.py +711 -0
  26. brainpatch/research/generation_eval.py +346 -0
  27. brainpatch/research/ml/__init__.py +35 -0
  28. brainpatch/research/ml/activation_store.py +232 -0
  29. brainpatch/research/ml/causal.py +386 -0
  30. brainpatch/research/ml/corpus.py +165 -0
  31. brainpatch/research/ml/evaluation.py +188 -0
  32. brainpatch/research/ml/extraction.py +464 -0
  33. brainpatch/research/ml/feature_analysis.py +317 -0
  34. brainpatch/research/ml/generation.py +109 -0
  35. brainpatch/research/ml/hooks.py +183 -0
  36. brainpatch/research/ml/intervention.py +274 -0
  37. brainpatch/research/ml/model.py +219 -0
  38. brainpatch/research/ml/patch_search.py +337 -0
  39. brainpatch/research/ml/runtime.py +343 -0
  40. brainpatch/research/ml/sae.py +383 -0
  41. brainpatch/research/ml/training.py +376 -0
  42. brainpatch/research/stance_rubric.py +170 -0
  43. brainpatch/research/sycophancy_data.py +982 -0
  44. brainpatch/research/sycophancy_data_r1.py +1701 -0
  45. brainpatch/research/sycophancy_data_v2.py +1649 -0
  46. brainpatch/research/sycophancy_data_v3.py +2288 -0
  47. brainpatch/research/sycophancy_v2_build.py +362 -0
  48. brainpatch/research/sycophancy_v3_build.py +188 -0
  49. brainpatch/research/utility_probe.py +139 -0
  50. brainpatch/runtime/__init__.py +50 -0
  51. brainpatch/runtime/auto.py +157 -0
  52. brainpatch/runtime/base.py +311 -0
  53. brainpatch/runtime/capabilities.py +96 -0
  54. brainpatch/runtime/model.py +260 -0
  55. brainpatch/runtime/scheduling.py +13 -0
  56. brainpatch/schemas/__init__.py +35 -0
  57. brainpatch/schemas/contrast.py +161 -0
  58. brainpatch/schemas/feature.py +193 -0
  59. brainpatch/schemas/manifest.py +167 -0
  60. brainpatch/schemas/patch.py +379 -0
  61. brainpatch/schemas/patch_io.py +88 -0
  62. brainpatch/schemas/sae.py +146 -0
  63. brainpatch/server/__init__.py +11 -0
  64. brainpatch/server/app.py +269 -0
  65. brainpatch/steering/__init__.py +13 -0
  66. brainpatch/steering/plan.py +177 -0
  67. brainpatch/steering/schedule.py +138 -0
  68. brainpatch/ui/__init__.py +11 -0
  69. brainpatch/ui/app.py +201 -0
  70. brainpatch/verify/__init__.py +66 -0
  71. brainpatch/verify/behavioural.py +156 -0
  72. brainpatch/verify/checks.py +204 -0
  73. brainpatch/verify/corruptions.py +335 -0
  74. brainpatch/verify/report.py +133 -0
  75. brainpatch/verify/vectors.py +95 -0
  76. brainpatch/verify/workflow.py +331 -0
  77. brainpatch-1.2.0.dist-info/METADATA +556 -0
  78. brainpatch-1.2.0.dist-info/RECORD +82 -0
  79. brainpatch-1.2.0.dist-info/WHEEL +5 -0
  80. brainpatch-1.2.0.dist-info/entry_points.txt +2 -0
  81. brainpatch-1.2.0.dist-info/licenses/LICENSE +190 -0
  82. brainpatch-1.2.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,204 @@
1
+ """The detection layers, cheapest first.
2
+
3
+ Each layer answers a different question, and each is blind to something. The
4
+ point of running all of them against every corruption is to find out *what each
5
+ is blind to*, empirically, rather than assuming.
6
+
7
+ `unsigned_cosine` is included deliberately and is **not** a recommended check.
8
+ It is here because it is the check this project actually ran, which reported
9
+ cosine = 1.0 on an artifact that was behaviourally the sign control. Its column
10
+ in the detection matrix is the argument.
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ from dataclasses import dataclass
16
+ from pathlib import Path
17
+ from typing import Any
18
+
19
+ from brainpatch.verify import vectors as vec
20
+
21
+ #: Ordered cheapest to most expensive. Behavioural reproduction is run
22
+ #: separately -- it needs a GPU and a frozen evaluation set.
23
+ DETECTION_LAYERS: tuple[str, ...] = (
24
+ "schema",
25
+ "checksum",
26
+ "shape_dtype",
27
+ "model_compatibility",
28
+ "unsigned_cosine",
29
+ "signed_cosine",
30
+ "delta_norm",
31
+ "execution_trace",
32
+ "schedule_trace",
33
+ "runtime_smoke",
34
+ )
35
+
36
+ #: A layer flags a defect when the quantity it inspects differs from the
37
+ #: reference by more than this. Deliberately loose: the question is whether a
38
+ #: layer can see a defect *at all*, not how finely it can measure.
39
+ COSINE_TOLERANCE = 1e-3
40
+ NORM_TOLERANCE = 1e-3
41
+
42
+
43
+ @dataclass
44
+ class Reference:
45
+ """Everything the static layers compare a candidate against."""
46
+
47
+ delta: list[float]
48
+ layer: int
49
+ site: str
50
+ model_id: str
51
+ revision: str | None
52
+ shape: list[int]
53
+ schedule: dict[str, float] | None
54
+
55
+
56
+ def reference_from(path: Path) -> Reference:
57
+ from brainpatch.patch.loader import load_patch
58
+
59
+ loaded = load_patch(path)
60
+ intervention = loaded.manifest.interventions[0]
61
+ values = [float(v) for v in loaded.vectors[intervention.vector].data]
62
+ return Reference(
63
+ delta=vec.scaled(values, intervention.coefficient),
64
+ layer=int(intervention.layer),
65
+ site=str(intervention.site),
66
+ model_id=loaded.manifest.base_model.model_id,
67
+ revision=loaded.manifest.base_model.revision,
68
+ shape=list(loaded.vectors[intervention.vector].shape),
69
+ schedule=loaded.manifest.schedule,
70
+ )
71
+
72
+
73
+ def run_static_layers(path: Path, reference: Reference) -> dict[str, Any]:
74
+ """Run every layer that does not need a GPU.
75
+
76
+ Returns ``{layer: caught}`` plus the measured quantities, so the matrix can
77
+ be audited rather than trusted.
78
+ """
79
+ from brainpatch.patch.format import PatchFormatError
80
+ from brainpatch.patch.loader import PatchLoadError, load_patch
81
+
82
+ caught: dict[str, bool] = {}
83
+ measured: dict[str, Any] = {}
84
+
85
+ # -- checksum: load_patch verifies internally and raises on mismatch -------
86
+ try:
87
+ loaded = load_patch(path, verify_checksums=True)
88
+ caught["checksum"] = False
89
+ except PatchLoadError as exc:
90
+ measured["checksum_error"] = str(exc)
91
+ return {"caught": {layer: layer == "checksum" for layer in DETECTION_LAYERS},
92
+ "measured": measured, "loadable": False}
93
+
94
+ # -- schema ---------------------------------------------------------------
95
+ try:
96
+ loaded.manifest.validate()
97
+ caught["schema"] = False
98
+ except PatchFormatError as exc:
99
+ caught["schema"] = True
100
+ measured["schema_error"] = str(exc)
101
+
102
+ intervention = loaded.manifest.interventions[0]
103
+ tensor = loaded.vectors[intervention.vector]
104
+ delta = vec.scaled([float(v) for v in tensor.data], intervention.coefficient)
105
+
106
+ # -- shape / dtype --------------------------------------------------------
107
+ caught["shape_dtype"] = list(tensor.shape) != reference.shape
108
+ measured["shape"] = list(tensor.shape)
109
+
110
+ # -- model compatibility --------------------------------------------------
111
+ caught["model_compatibility"] = (
112
+ loaded.manifest.base_model.model_id != reference.model_id
113
+ or loaded.manifest.base_model.revision != reference.revision
114
+ )
115
+
116
+ # -- direction, signed and unsigned --------------------------------------
117
+ ref_norm = vec.norm(reference.delta)
118
+ cand_norm = vec.norm(delta)
119
+ cosine = vec.cosine(delta, reference.delta)
120
+ measured["signed_cosine"] = cosine
121
+ measured["unsigned_cosine"] = abs(cosine)
122
+ caught["signed_cosine"] = abs(cosine - 1.0) > COSINE_TOLERANCE
123
+ # The blind spot, made explicit: |cos| cannot distinguish v from -v.
124
+ caught["unsigned_cosine"] = abs(abs(cosine) - 1.0) > COSINE_TOLERANCE
125
+
126
+ # -- magnitude ------------------------------------------------------------
127
+ ratio = cand_norm / ref_norm
128
+ measured["delta_norm"] = cand_norm
129
+ measured["norm_ratio"] = ratio
130
+ caught["delta_norm"] = abs(ratio - 1.0) > NORM_TOLERANCE
131
+
132
+ # -- declared structure ---------------------------------------------------
133
+ measured["layer"] = int(intervention.layer)
134
+ measured["site"] = str(intervention.site)
135
+ measured["schedule"] = loaded.manifest.schedule
136
+ caught["schedule_trace"] = loaded.manifest.schedule != reference.schedule
137
+
138
+ return {"caught": caught, "measured": measured, "loadable": True}
139
+
140
+
141
+ def run_execution_trace(
142
+ backend: Any, path: Path, reference: Reference, num_layers: int
143
+ ) -> dict[str, Any]:
144
+ """Ask the *runtime* where and when it would intervene.
145
+
146
+ Reads ``resolve_edits``, not the manifest. A manifest declaring
147
+ ``site: prompt`` and a runtime steering every generated token is exactly the
148
+ disagreement this layer exists to find, and no amount of manifest inspection
149
+ would surface it.
150
+ """
151
+ from brainpatch.patch.loader import load_patch
152
+
153
+ loaded = load_patch(path)
154
+ active = backend.install_patch(loaded)
155
+ try:
156
+ # Which layers carry an edit, on which kind of pass?
157
+ prompt_layers = [
158
+ layer
159
+ for layer in range(num_layers)
160
+ if backend.resolve_edits(0, layer=layer, is_prompt_pass=True)
161
+ ]
162
+ continuation_layers = [
163
+ layer
164
+ for layer in range(num_layers)
165
+ if backend.resolve_edits(0, layer=layer, is_prompt_pass=False)
166
+ ]
167
+ finally:
168
+ backend.remove_patch(active.patch.manifest.name)
169
+
170
+ expected_prompt = reference.site in ("prompt", "all")
171
+ expected_continuation = reference.site in ("continuation", "all")
172
+
173
+ caught = (
174
+ prompt_layers != ([reference.layer] if expected_prompt else [])
175
+ or continuation_layers != ([reference.layer] if expected_continuation else [])
176
+ )
177
+ return {
178
+ "caught": bool(caught),
179
+ "prompt_layers": prompt_layers,
180
+ "continuation_layers": continuation_layers,
181
+ "expected_layer": reference.layer,
182
+ "expected_site": reference.site,
183
+ }
184
+
185
+
186
+ def run_runtime_smoke(backend: Any, path: Path, prompt: str) -> dict[str, Any]:
187
+ """Does it load, install and produce non-degenerate text at all?
188
+
189
+ The weakest possible behavioural check, and included precisely to show how
190
+ little it catches. Almost every corruption here will pass it.
191
+ """
192
+ from brainpatch.patch.loader import load_patch
193
+ from brainpatch.runtime.base import GenerationConfig
194
+
195
+ loaded = load_patch(path)
196
+ active = backend.install_patch(loaded)
197
+ try:
198
+ text = backend.generate(prompt, GenerationConfig(max_new_tokens=24, temperature=0.0))
199
+ finally:
200
+ backend.remove_patch(active.patch.manifest.name)
201
+ stripped = text.strip()
202
+ words = stripped.split()
203
+ degenerate = not stripped or (len(words) > 6 and len(set(words)) <= 2)
204
+ return {"caught": bool(degenerate), "sample": text[:160]}
@@ -0,0 +1,335 @@
1
+ """Controlled, deterministic corruptions of a compiled `.brainpatch` artifact.
2
+
3
+ Every corruption starts from a known-correct reference and changes exactly one
4
+ thing. Every product is a **well-formed archive with valid checksums** -- that is
5
+ deliberate and it is the whole design. A variant that fails to load teaches
6
+ nothing about which validation layer catches it, and the class this suite exists
7
+ to expose is precisely the one that survives loading, schema validation and
8
+ checksum verification and is still behaviourally wrong.
9
+
10
+ No corruption introduces executable content. The `.brainpatch` format is inert
11
+ data by construction; a corruption suite that weakened that would be testing a
12
+ different, worse format.
13
+
14
+ **No torch.** Everything here transforms a manifest and a list of floats, so it
15
+ runs in the fast local test suite where `tests/conftest.py` blocks the ML stack.
16
+ Vector arithmetic lives in `brainpatch.verify.vectors` and is done in Python doubles, which is
17
+ more precise than the float32 path it replaces. Generation and execution tracing
18
+ genuinely need a model; those stay in `brainpatch.verify.behavioural`, Modal-only.
19
+
20
+ Corruptions are declarative and recorded: each returns a description of exactly
21
+ what it did, so `corruptions.json` is a reproduction recipe rather than a label.
22
+ """
23
+
24
+ from __future__ import annotations
25
+
26
+ from dataclasses import dataclass, field
27
+ from pathlib import Path
28
+ from typing import Any, Callable
29
+
30
+ from brainpatch.patch.format import Intervention, Manifest
31
+ from brainpatch.patch.loader import LoadedPatch, load_patch, save_patch
32
+ from brainpatch.verify import vectors as vec
33
+
34
+
35
+ @dataclass
36
+ class Corruption:
37
+ """One defect, its rationale, and where we predict it will be caught."""
38
+
39
+ key: str
40
+ #: What real-world mistake this stands in for.
41
+ defect: str
42
+ #: Pre-registered prediction, so the detection matrix can contradict it.
43
+ expected_detection: str
44
+ apply: Callable[[Manifest, dict[str, Any]], tuple[Manifest, dict[str, Any]]]
45
+ #: Filled in when the variant is built.
46
+ detail: dict[str, Any] = field(default_factory=dict)
47
+
48
+
49
+ def _clone(manifest: Manifest, **changes: Any) -> Manifest:
50
+ """A manifest identical to the original except for named fields."""
51
+ fields = {
52
+ "name": manifest.name,
53
+ "base_model": manifest.base_model,
54
+ "interventions": list(manifest.interventions),
55
+ "description": manifest.description,
56
+ "format_version": manifest.format_version,
57
+ "evidence_level": manifest.evidence_level,
58
+ "evaluation": dict(manifest.evaluation),
59
+ "compatibility": dict(manifest.compatibility),
60
+ "provenance": dict(manifest.provenance),
61
+ "max_abs_strength": manifest.max_abs_strength,
62
+ "default_strength": manifest.default_strength,
63
+ "license": manifest.license,
64
+ "authors": list(manifest.authors),
65
+ "schedule": dict(manifest.schedule) if manifest.schedule else None,
66
+ }
67
+ fields.update(changes)
68
+ return Manifest(**fields)
69
+
70
+
71
+ def _with_intervention(manifest: Manifest, **changes: Any) -> Manifest:
72
+ original = manifest.interventions[0]
73
+ fields = {
74
+ "layer": original.layer,
75
+ "vector": original.vector,
76
+ "coefficient": original.coefficient,
77
+ "site": original.site,
78
+ }
79
+ fields.update(changes)
80
+ return _clone(manifest, interventions=[Intervention(**fields)])
81
+
82
+
83
+ def _values(vectors: dict[str, Any], key: str) -> list[float]:
84
+ """The stored vector as plain Python floats."""
85
+ return [float(v) for v in vectors[key].data]
86
+
87
+
88
+ def _store(vectors: dict[str, Any], key: str, values: list[float]) -> dict[str, Any]:
89
+ """Rebuild the vector map with one vector replaced, preserving dtype and shape."""
90
+ from brainpatch.patch import tensors as ts
91
+
92
+ out = dict(vectors)
93
+ out[key] = ts.Tensor(
94
+ dtype=vectors[key].dtype,
95
+ shape=list(vectors[key].shape),
96
+ data=[float(v) for v in values],
97
+ )
98
+ return out
99
+
100
+
101
+ # -- the corruptions -----------------------------------------------------------
102
+
103
+
104
+ def flip_sign(manifest: Manifest, vectors: dict[str, Any]):
105
+ """The defect this project actually shipped.
106
+
107
+ Discovery selected the negated decoder column; the compiler emits the
108
+ unsigned column and carries sign in the coefficient; the coefficient was
109
+ written positive. Behaviourally the sign control.
110
+ """
111
+ return _with_intervention(
112
+ manifest, coefficient=-manifest.interventions[0].coefficient
113
+ ), vectors
114
+
115
+
116
+ def shift_layer(delta: int):
117
+ """Off-by-one in a layer index. Plausible in any hand-written manifest."""
118
+
119
+ def apply(manifest: Manifest, vectors: dict[str, Any]):
120
+ layer = manifest.interventions[0].layer + delta
121
+ return _with_intervention(manifest, layer=layer), vectors
122
+
123
+ return apply
124
+
125
+
126
+ def scale_coefficient(factor: float):
127
+ """Calibration drift: right direction, wrong dose."""
128
+
129
+ def apply(manifest: Manifest, vectors: dict[str, Any]):
130
+ coefficient = manifest.interventions[0].coefficient * factor
131
+ return _with_intervention(manifest, coefficient=coefficient), vectors
132
+
133
+ return apply
134
+
135
+
136
+ def replace_site(site: str):
137
+ """Injection site changed -- e.g. prompt-only becomes always-on.
138
+
139
+ The `site` field exists because v3's validated configuration was prompt-token
140
+ only and the format could not express it. Losing it silently re-runs the
141
+ intervention on every generated token, a configuration with no test evidence.
142
+ """
143
+
144
+ def apply(manifest: Manifest, vectors: dict[str, Any]):
145
+ return _with_intervention(manifest, site=site), vectors
146
+
147
+ return apply
148
+
149
+
150
+ def precision_roundtrip(dtype: str):
151
+ """Store the vector through a lower-precision type and back.
152
+
153
+ Models the real cost of serialization precision. Round-to-nearest-even in
154
+ both cases, matching what a real store does.
155
+ """
156
+
157
+ def apply(manifest: Manifest, vectors: dict[str, Any]):
158
+ key = manifest.interventions[0].vector
159
+ return manifest, _store(vectors, key, vec.round_to(_values(vectors, key), dtype))
160
+
161
+ return apply
162
+
163
+
164
+ def renormalize_vector(scale: float):
165
+ """Vector rescaled while the coefficient is left alone.
166
+
167
+ The applied delta is `coefficient x vector`, so rescaling the tensor changes
168
+ the dose without changing any declared number. A reader of the manifest sees
169
+ the validated coefficient and is wrong about what gets applied.
170
+ """
171
+
172
+ def apply(manifest: Manifest, vectors: dict[str, Any]):
173
+ key = manifest.interventions[0].vector
174
+ return manifest, _store(vectors, key, vec.scaled(_values(vectors, key), scale))
175
+
176
+ return apply
177
+
178
+
179
+ def stored_vector_wrong(seed: int = 0):
180
+ """Metadata correct, tensor wrong.
181
+
182
+ Everything a reader can inspect in the manifest is right. The archive is
183
+ consistent and its checksums verify -- they are computed over whatever is
184
+ stored. Only a comparison against an external reference, or behaviour, sees it.
185
+ """
186
+
187
+ def apply(manifest: Manifest, vectors: dict[str, Any]):
188
+ key = manifest.interventions[0].vector
189
+ original = _values(vectors, key)
190
+ replacement = vec.unit(vec.gaussian(len(original), seed=seed))
191
+ return manifest, _store(
192
+ vectors, key, vec.scaled(replacement, vec.norm(original))
193
+ )
194
+
195
+ return apply
196
+
197
+
198
+ def manifest_coefficient_wrong(factor: float = 0.5):
199
+ """Tensor correct, declared coefficient wrong.
200
+
201
+ The mirror image of `renormalize_vector`, and the reason numerical fidelity
202
+ must be computed on the *applied delta* rather than on either half alone.
203
+ """
204
+
205
+ def apply(manifest: Manifest, vectors: dict[str, Any]):
206
+ coefficient = manifest.interventions[0].coefficient * factor
207
+ return _with_intervention(manifest, coefficient=coefficient), vectors
208
+
209
+ return apply
210
+
211
+
212
+ def orthogonal_direction(seed: int = 7):
213
+ """A direction orthogonal to the validated one, same norm.
214
+
215
+ The strongest test of "numerically plausible, semantically absent": shape,
216
+ dtype, norm and coefficient are all exactly right, and the intervention is
217
+ unrelated to the one that was validated.
218
+ """
219
+
220
+ def apply(manifest: Manifest, vectors: dict[str, Any]):
221
+ key = manifest.interventions[0].vector
222
+ original = _values(vectors, key)
223
+ candidate = vec.orthogonalise(vec.gaussian(len(original), seed=seed), original)
224
+ return manifest, _store(
225
+ vectors, key, vec.scaled(vec.unit(candidate), vec.norm(original))
226
+ )
227
+
228
+ return apply
229
+
230
+
231
+ #: The registry. Ordered so the detection matrix reads from crudest to subtlest.
232
+ CORRUPTIONS: tuple[Corruption, ...] = (
233
+ Corruption(
234
+ "sign_flip", "coefficient sign flipped (the defect this project shipped)",
235
+ "signed cosine and behaviour; NOT unsigned cosine", flip_sign,
236
+ ),
237
+ Corruption(
238
+ "layer_plus_1", "layer index off by one, upward",
239
+ "execution trace and behaviour", shift_layer(+1),
240
+ ),
241
+ Corruption(
242
+ "layer_minus_1", "layer index off by one, downward",
243
+ "execution trace and behaviour", shift_layer(-1),
244
+ ),
245
+ Corruption(
246
+ "coefficient_half", "coefficient scaled x0.5 (under-dosed)",
247
+ "delta norm and behaviour", scale_coefficient(0.5),
248
+ ),
249
+ Corruption(
250
+ "coefficient_double", "coefficient scaled x2 (over-dosed)",
251
+ "delta norm and behaviour", scale_coefficient(2.0),
252
+ ),
253
+ Corruption(
254
+ "site_all", "prompt-only schedule replaced with always-on",
255
+ "execution trace and behaviour", replace_site("all"),
256
+ ),
257
+ Corruption(
258
+ "site_continuation", "injection moved to generated tokens only",
259
+ "execution trace and behaviour", replace_site("continuation"),
260
+ ),
261
+ # NEGATIVE CONTROL, not a defect. The v1 format already stores vectors as
262
+ # F16, so an F16 round-trip is bit-exact identity. It is retained on purpose:
263
+ # a suite of thirteen defects with no null case cannot distinguish "this
264
+ # layer detects defects" from "this layer fires at everything". Any layer
265
+ # that flags this one is producing a false positive.
266
+ Corruption(
267
+ "f16_roundtrip", "NEGATIVE CONTROL: vector stored through float16 and back "
268
+ "(identity -- the format already stores F16)",
269
+ "nothing: this is a no-op and must be caught by no layer",
270
+ precision_roundtrip("float16"),
271
+ ),
272
+ Corruption(
273
+ "bf16_roundtrip", "vector stored through bfloat16 and back "
274
+ "(real perturbation: bf16 trades mantissa for exponent)",
275
+ "nothing cheap; too small for any cosine or norm threshold",
276
+ precision_roundtrip("bfloat16"),
277
+ ),
278
+ Corruption(
279
+ "vector_rescaled", "vector rescaled x2, coefficient untouched",
280
+ "delta norm and behaviour; NOT cosine", renormalize_vector(2.0),
281
+ ),
282
+ Corruption(
283
+ "vector_wrong", "manifest correct, stored vector replaced by a random direction",
284
+ "signed cosine and behaviour", stored_vector_wrong(),
285
+ ),
286
+ Corruption(
287
+ "coefficient_declared_wrong", "vector correct, declared coefficient halved",
288
+ "delta norm and behaviour", manifest_coefficient_wrong(0.5),
289
+ ),
290
+ Corruption(
291
+ "orthogonal_direction", "same norm and coefficient, direction orthogonal to validated",
292
+ "signed cosine and behaviour; NOT norm, shape or schema", orthogonal_direction(),
293
+ ),
294
+ )
295
+
296
+
297
+ def build_variants(reference: Path, out_dir: Path) -> list[dict[str, Any]]:
298
+ """Write every corrupted variant. Returns the `corruptions.json` payload."""
299
+ import hashlib
300
+
301
+ loaded: LoadedPatch = load_patch(reference)
302
+ out_dir.mkdir(parents=True, exist_ok=True)
303
+
304
+ key = loaded.manifest.interventions[0].vector
305
+ reference_delta = vec.scaled(
306
+ _values(loaded.vectors, key), loaded.manifest.interventions[0].coefficient
307
+ )
308
+
309
+ records: list[dict[str, Any]] = []
310
+ for corruption in CORRUPTIONS:
311
+ manifest, vectors = corruption.apply(loaded.manifest, loaded.vectors)
312
+ manifest = _clone(manifest, name=f"corrupt-{corruption.key.replace('_', '-')}")
313
+ path = out_dir / f"{corruption.key}.brainpatch"
314
+ save_patch(manifest, vectors, path, overwrite=True)
315
+
316
+ intervention = manifest.interventions[0]
317
+ delta = vec.scaled(_values(vectors, intervention.vector), intervention.coefficient)
318
+ cos = vec.cosine(delta, reference_delta)
319
+ records.append(
320
+ {
321
+ "key": corruption.key,
322
+ "defect": corruption.defect,
323
+ "expected_detection": corruption.expected_detection,
324
+ "artifact": path.name,
325
+ "sha256": hashlib.sha256(path.read_bytes()).hexdigest(),
326
+ "bytes": path.stat().st_size,
327
+ "layer": intervention.layer,
328
+ "site": intervention.site,
329
+ "coefficient": intervention.coefficient,
330
+ "delta_norm": vec.norm(delta),
331
+ "signed_cosine_to_reference": cos,
332
+ "unsigned_cosine_to_reference": abs(cos),
333
+ }
334
+ )
335
+ return records
@@ -0,0 +1,133 @@
1
+ """Human-readable artifact-fidelity reports and the detection matrix.
2
+
3
+ The report always prints all four components, including the ones that passed. The
4
+ interesting artifact is the one that passes three and fails the fourth, and that
5
+ is only legible if all four are shown.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from typing import Any
11
+
12
+ COMPONENTS = ("structural", "numerical", "execution", "behavioural")
13
+
14
+ #: Column labels for the matrix, short enough to fit side by side.
15
+ LAYER_ABBREV = {
16
+ "schema": "schm",
17
+ "checksum": "csum",
18
+ "shape_dtype": "shape",
19
+ "model_compatibility": "model",
20
+ "unsigned_cosine": "ucos",
21
+ "signed_cosine": "scos",
22
+ "delta_norm": "norm",
23
+ "execution_trace": "exec",
24
+ "schedule_trace": "sched",
25
+ "runtime_smoke": "smoke",
26
+ "behavioural_reproduction": "BEHAV",
27
+ }
28
+
29
+ _CHECK_LABEL = {
30
+ "schema": "Schema",
31
+ "checksum": "Checksums",
32
+ "model_compatibility": "Model compatibility",
33
+ "signed_cosine": "Signed direction",
34
+ "delta_norm": "Delta norm",
35
+ "execution_trace": "Layer/site execution",
36
+ "behavioural_reproduction": "Behavior reproduction",
37
+ }
38
+
39
+
40
+ def render_artifact_report(variant: dict[str, Any], *, tolerance: float) -> str:
41
+ """The per-artifact report from the spec.
42
+
43
+ ``caught = True`` means the layer detected a defect, so it renders as FAIL.
44
+ """
45
+ caught = dict(variant["caught"])
46
+ caught["behavioural_reproduction"] = variant.get("caught_behavioural", False)
47
+
48
+ lines = [f"Artifact: {variant['key']}", ""]
49
+ lines.append("Artifact Fidelity")
50
+ lines.append("")
51
+ width = max(len(v) for v in _CHECK_LABEL.values())
52
+ for layer, label in _CHECK_LABEL.items():
53
+ if layer not in caught:
54
+ continue
55
+ state = "FAIL" if caught[layer] else "PASS"
56
+ lines.append(f"{label.ljust(width)} {state}")
57
+
58
+ delta = variant.get("behavioural_delta")
59
+ if delta is not None and delta == delta: # not NaN
60
+ lines.append("")
61
+ lines.append(f"behavioural_delta = {delta:.4f} (tolerance {tolerance})")
62
+
63
+ lines += ["", "Components:"]
64
+ fidelity = variant.get("fidelity", {})
65
+ for component in COMPONENTS:
66
+ lines.append(f" {component.ljust(12)} {fidelity.get(component, 'not measured')}")
67
+
68
+ lines += ["", "Overall:", variant.get("verdict", "UNKNOWN")]
69
+ return "\n".join(lines)
70
+
71
+
72
+ def render_detection_matrix(
73
+ variants: list[dict[str, Any]], layers: list[str]
74
+ ) -> str:
75
+ """Rows = corruptions, columns = layers. ``X`` means the layer caught it."""
76
+ header = f"{'corruption':<28}{'delta':>9} " + " ".join(
77
+ f"{LAYER_ABBREV.get(layer, layer[:5]):>5}" for layer in layers
78
+ )
79
+ lines = [header, "-" * len(header)]
80
+ for variant in variants:
81
+ caught = dict(variant["caught"])
82
+ caught["behavioural_reproduction"] = variant.get("caught_behavioural", False)
83
+ delta = variant.get("behavioural_delta", float("nan"))
84
+ cells = " ".join(
85
+ f"{('X' if caught.get(layer) else '.'):>5}" for layer in layers
86
+ )
87
+ lines.append(f"{variant['key']:<28}{delta:>9.4f} {cells}")
88
+ return "\n".join(lines)
89
+
90
+
91
+ def to_csv(variants: list[dict[str, Any]], layers: list[str]) -> str:
92
+ rows = [",".join(["corruption", "verdict", "behavioural_delta"] + layers)]
93
+ for variant in variants:
94
+ caught = dict(variant["caught"])
95
+ caught["behavioural_reproduction"] = variant.get("caught_behavioural", False)
96
+ cells = [
97
+ variant["key"],
98
+ variant.get("verdict", ""),
99
+ f"{variant.get('behavioural_delta', float('nan')):.4f}",
100
+ ]
101
+ cells += ["caught" if caught.get(layer) else "-" for layer in layers]
102
+ rows.append(",".join(cells))
103
+ return "\n".join(rows) + "\n"
104
+
105
+
106
+ def summarise_claim(
107
+ variants: list[dict[str, Any]], layers: list[str]
108
+ ) -> dict[str, Any]:
109
+ """Does the behavioural layer catch anything the cheap layers miss?
110
+
111
+ ``unsigned_cosine`` is excluded from "cheap layers" on purpose. It is in the
112
+ matrix as the check that failed historically, not as one a validator should
113
+ rely on; counting it as a legitimate catcher would let the very blind spot
114
+ under study argue that nothing is blind.
115
+ """
116
+ cheap = [
117
+ layer for layer in layers
118
+ if layer not in ("behavioural_reproduction", "unsigned_cosine")
119
+ ]
120
+ behaviour_only, false_positives = [], []
121
+ for variant in variants:
122
+ caught = dict(variant["caught"])
123
+ failed_behaviour = variant.get("caught_behavioural", False)
124
+ hits = [layer for layer in cheap if caught.get(layer)]
125
+ if failed_behaviour and not hits:
126
+ behaviour_only.append(variant["key"])
127
+ if "NEGATIVE CONTROL" in variant.get("defect", "") and (hits or failed_behaviour):
128
+ false_positives.append(variant["key"])
129
+ return {
130
+ "claim_supported": bool(behaviour_only),
131
+ "caught_only_by_behaviour": behaviour_only,
132
+ "negative_control_false_positives": false_positives,
133
+ }