sway(core): NullCalibratedBackend protocol for z-score calibration
- SHA
fadb0ae0a14970b20dfdcc78ffb72a71c3bc1031- Parents
-
a4b94dc - Tree
d99d373
fadb0ae
fadb0ae0a14970b20dfdcc78ffb72a71c3bc1031a4b94dc
d99d373| Status | File | + | - |
|---|---|---|---|
| M |
src/dlm_sway/__init__.py
|
2 | 0 |
| M |
src/dlm_sway/core/scoring.py
|
24 | 0 |
src/dlm_sway/__init__.pymodified@@ -12,6 +12,7 @@ from dlm_sway.core.model import LoadedModel, Model, ModelSpec | ||
| 12 | 12 | from dlm_sway.core.result import ProbeResult, SuiteResult, SwayScore, Verdict |
| 13 | 13 | from dlm_sway.core.scoring import ( |
| 14 | 14 | DifferentialBackend, |
| 15 | + NullCalibratedBackend, | |
| 15 | 16 | RollingLogprob, |
| 16 | 17 | ScalableDifferentialBackend, |
| 17 | 18 | ScoringBackend, |
@@ -24,6 +25,7 @@ __all__ = [ | ||
| 24 | 25 | "LoadedModel", |
| 25 | 26 | "Model", |
| 26 | 27 | "ModelSpec", |
| 28 | + "NullCalibratedBackend", | |
| 27 | 29 | "ProbeError", |
| 28 | 30 | "ProbeResult", |
| 29 | 31 | "RollingLogprob", |
src/dlm_sway/core/scoring.pymodified@@ -161,6 +161,30 @@ class ScalableDifferentialBackend(DifferentialBackend, Protocol): | ||
| 161 | 161 | def as_scaled_adapter(self, lam: float) -> AbstractContextManager[_ScoringModel]: ... |
| 162 | 162 | |
| 163 | 163 | |
| 164 | +@runtime_checkable | |
| 165 | +class NullCalibratedBackend(DifferentialBackend, Protocol): | |
| 166 | + """A differential backend that can produce a "null adapter" view. | |
| 167 | + | |
| 168 | + A null adapter has the *same structure* (rank, alpha, target modules) | |
| 169 | + as the real adapter but with weights drawn from a zero-mean Gaussian. | |
| 170 | + Running probes against this view yields the baseline "how much | |
| 171 | + signal does random noise produce" distribution — the denominator in | |
| 172 | + every numeric probe's z-score. | |
| 173 | + | |
| 174 | + The context manager takes a ``seed`` so calibration runs can be | |
| 175 | + reproduced and multiple independent null samples can be drawn to | |
| 176 | + estimate ``std``. | |
| 177 | + | |
| 178 | + Implementations MUST restore the real adapter on exit, including | |
| 179 | + on exceptions, so a caller can freely interleave null and real | |
| 180 | + calibrations within the same backend lifetime. | |
| 181 | + """ | |
| 182 | + | |
| 183 | + def as_null_adapter( | |
| 184 | + self, seed: int, *, init_scale: float = 0.02 | |
| 185 | + ) -> AbstractContextManager[_ScoringModel]: ... | |
| 186 | + | |
| 187 | + | |
| 164 | 188 | # Helper Protocol for type-checking the yielded context object: it |
| 165 | 189 | # must satisfy both Model and ScoringBackend. mypy doesn't support |
| 166 | 190 | # intersection types, so we spell it out explicitly. |