eval-toolkit 0.41.0__tar.gz → 0.42.0__tar.gz
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.
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/CHANGELOG.md +52 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/PKG-INFO +1 -1
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/src/eval_toolkit/__init__.py +1 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/src/eval_toolkit/_version.py +1 -1
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/src/eval_toolkit/calibration.py +78 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/golden/public_api/snapshot.json +7 -1
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_calibration_binary_adapters.py +97 -2
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/.gitignore +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/LICENSE +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/README.md +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/STYLE.md +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/docs/archive/README.md +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/docs/research/README.md +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/docs/research/datasets/README.md +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/docs/research/papers/data-integrity/README.md +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/docs/research/papers/eval-ecosystem/README.md +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/docs/research/papers/inference/README.md +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/docs/research/papers/prompt-injection/README.md +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/docs/source/methodology/README.md +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/pyproject.toml +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/src/eval_toolkit/__main__.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/src/eval_toolkit/_deprecated.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/src/eval_toolkit/_parallel.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/src/eval_toolkit/analysis.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/src/eval_toolkit/artifacts.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/src/eval_toolkit/bootstrap.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/src/eval_toolkit/claims.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/src/eval_toolkit/config.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/src/eval_toolkit/docs.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/src/eval_toolkit/embeddings.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/src/eval_toolkit/evidence.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/src/eval_toolkit/harness.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/src/eval_toolkit/leakage.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/src/eval_toolkit/loaders.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/src/eval_toolkit/manifest.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/src/eval_toolkit/metrics.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/src/eval_toolkit/operating_points.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/src/eval_toolkit/paths.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/src/eval_toolkit/plotting.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/src/eval_toolkit/protocols.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/src/eval_toolkit/provenance.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/src/eval_toolkit/py.typed +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/src/eval_toolkit/schemas/manifest.v1.json +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/src/eval_toolkit/schemas/manifest.v2.json +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/src/eval_toolkit/schemas/manifest.v3.json +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/src/eval_toolkit/schemas/results.v1.json +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/src/eval_toolkit/schemas/results_full.v1.json +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/src/eval_toolkit/seeds.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/src/eval_toolkit/splits.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/src/eval_toolkit/text_dedup.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/src/eval_toolkit/thresholds.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/baseline/test_plotting_visual/plot_bootstrap_distribution.png +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/baseline/test_plotting_visual/plot_confusion_matrix_grid.png +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/baseline/test_plotting_visual/plot_lift_ci.png +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/baseline/test_plotting_visual/plot_metric_bars.png +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/baseline/test_plotting_visual/plot_pareto_frontier.png +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/baseline/test_plotting_visual/plot_pr_curve.png +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/baseline/test_plotting_visual/plot_reliability_diagram.png +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/baseline/test_plotting_visual/plot_roc_curve.png +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/baseline/test_plotting_visual/plot_score_histograms.png +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/baseline/test_plotting_visual/plot_slice_metric_heatmap.png +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/benchmarks/__init__.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/benchmarks/test_kernel_benchmarks.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/conftest.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/golden/bootstrap_ci/cases.json +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/golden/data/dedup_holdout.jsonl +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/golden/data/dedup_holdout_expected.json +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/golden/data/dedup_holdout_provenance.md +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/golden/docs/expected.md +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/golden/docs/input.md +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/golden/docs/metrics.json +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/golden/test_dedup_holdout_calibration.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/strategies.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_analysis.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_artifacts.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_block_bootstrap_on_folds.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_bootstrap_calibration_mc.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_bootstrap_edge_cases.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_bootstrap_golden.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_bootstrap_njobs.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_bootstrap_props.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_bootstrap_research_grounded.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_bootstrap_unit.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_calibration_bootstrap_chain.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_calibration_determinism.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_calibration_optimization_failures.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_calibration_props.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_calibration_research_grounded.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_calibration_unit.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_claims.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_claims_coverage.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_claims_props.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_cli.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_config.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_coverage_bootstrap.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_coverage_calibration.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_coverage_harness.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_coverage_metrics.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_coverage_plotting.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_croissant_e2e.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_dedup_split_leakage_chain.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_deprecations.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_docs_golden.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_docs_props.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_embeddings.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_evidence_validators.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_harness_edge_cases.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_harness_fault_injection.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_harness_folded.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_harness_internals.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_harness_metric_options.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_harness_parallelism.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_harness_smoke.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_import_boundaries.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_is_metric_defined_for_slice.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_leakage.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_leakage_error_paths.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_leakage_props.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_loaders.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_loaders_coverage.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_loaders_props.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_logging.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_manifest.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_manifest_contamination_round_trip.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_manifest_props.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_manifest_validation.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_metrics_props.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_metrics_stratified_subsets.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_metrics_unit.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_misc_coverage.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_numeric_edge_cases.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_operating_points.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_operating_points_props.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_parallel.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_paths.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_pipeline_e2e.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_plotting_edge.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_plotting_smoke.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_plotting_visual.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_protocol_conformance.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_provenance.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_public_api.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_recall_at_fpr.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_reference_equivalence.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_reproducibility_integration.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_schemas.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_seeds.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_splits.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_splits_leakage_integration.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_splits_props.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_text_dedup.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_text_dedup_coverage.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_text_dedup_props.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_text_dedup_strategies.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_thresholds.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_thresholds_constant_score.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_thresholds_coverage.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_thresholds_props.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_thresholds_research_grounded.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_tokenization_leakage_check.py +0 -0
- {eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/test_v09_contracts.py +0 -0
|
@@ -7,6 +7,58 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.42.0] — 2026-05-19 — fit_isotonic_binary completes 4-calibrator family (closes #44)
|
|
11
|
+
|
|
12
|
+
Final element of the binary scalar-prob calibrator family started by
|
|
13
|
+
`fit_temperature_binary` (v0.35.0). All four now uniformly return
|
|
14
|
+
`(params, apply)`:
|
|
15
|
+
|
|
16
|
+
| Function | Params | Shipped |
|
|
17
|
+
|---|---|---|
|
|
18
|
+
| `fit_temperature_binary` | `(T,)` — single float | v0.35.0 |
|
|
19
|
+
| `fit_isotonic_binary` | `None` — non-parametric | **v0.42.0** |
|
|
20
|
+
| `fit_platt_binary` | `(a, b)` | v0.40.0 |
|
|
21
|
+
| `fit_beta_binary` | `(a, b, c)` | v0.40.0 |
|
|
22
|
+
|
|
23
|
+
Consumer code can now iterate the family with a single shape, used
|
|
24
|
+
to distinguish parametric from non-parametric via
|
|
25
|
+
`if params is not None`:
|
|
26
|
+
|
|
27
|
+
```text
|
|
28
|
+
CALIBRATORS = {
|
|
29
|
+
"temperature": fit_temperature_binary,
|
|
30
|
+
"isotonic": fit_isotonic_binary,
|
|
31
|
+
"platt": fit_platt_binary,
|
|
32
|
+
"beta": fit_beta_binary,
|
|
33
|
+
}
|
|
34
|
+
for name, fit_fn in CALIBRATORS.items():
|
|
35
|
+
params, apply = fit_fn(y_val, p_val)
|
|
36
|
+
calibrated = apply(p_test)
|
|
37
|
+
if params is not None:
|
|
38
|
+
manifest.record(f"{name}_params", params)
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
This matches the consumer's calibration-battery pattern in
|
|
42
|
+
`prompt-injection-detection-prototype` (their ADR-056 supersedes
|
|
43
|
+
ADR-023 to adopt the canonical `(params, apply)` shape across the
|
|
44
|
+
full 4-calibrator audit battery).
|
|
45
|
+
|
|
46
|
+
### Added
|
|
47
|
+
|
|
48
|
+
- **`eval_toolkit.fit_isotonic_binary(y_true, y_score) -> (None,
|
|
49
|
+
apply)`** — thin wrapper over `fit_isotonic_calibrator`. The
|
|
50
|
+
`None` in the params slot encodes "non-parametric" (isotonic
|
|
51
|
+
regression is a monotone step function, no scalar params to log).
|
|
52
|
+
- 6 new unit tests in `tests/test_calibration_binary_adapters.py`
|
|
53
|
+
including a 4-calibrator family-iteration integration test that
|
|
54
|
+
verifies the `None`-vs-tuple convention.
|
|
55
|
+
|
|
56
|
+
### Protocol stability
|
|
57
|
+
|
|
58
|
+
Additive only. No Tier-2 Protocol shape edits. v0.42 is minor 3 of
|
|
59
|
+
consecutive-without-Protocol-changes (v0.40 + v0.41 + v0.42). Gate 2
|
|
60
|
+
stays MET.
|
|
61
|
+
|
|
10
62
|
## [0.41.0] — 2026-05-18 — Croissant end-to-end (closes #42, v1.0 Gate 4 MET)
|
|
11
63
|
|
|
12
64
|
Closes v1.0 readiness Gate 4 — "Croissant interop verified end-to-end."
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: eval-toolkit
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.42.0
|
|
4
4
|
Summary: Reusable evaluation contracts for binary classification: metrics, bootstrap CIs, calibration, artifacts, and evidence gates.
|
|
5
5
|
Project-URL: Homepage, https://github.com/brandon-behring/eval-toolkit
|
|
6
6
|
Project-URL: Documentation, https://brandon-behring.github.io/eval-toolkit/
|
|
@@ -85,6 +85,7 @@ _EXPORTS: dict[str, str] = {
|
|
|
85
85
|
"bayes_optimal_threshold": "eval_toolkit.calibration",
|
|
86
86
|
"fit_beta_binary": "eval_toolkit.calibration",
|
|
87
87
|
"fit_beta_calibrator": "eval_toolkit.calibration",
|
|
88
|
+
"fit_isotonic_binary": "eval_toolkit.calibration",
|
|
88
89
|
"fit_isotonic_calibrator": "eval_toolkit.calibration",
|
|
89
90
|
"fit_platt_binary": "eval_toolkit.calibration",
|
|
90
91
|
"fit_platt_calibrator": "eval_toolkit.calibration",
|
|
@@ -55,6 +55,7 @@ __all__ = [
|
|
|
55
55
|
"bayes_optimal_threshold",
|
|
56
56
|
"fit_beta_binary",
|
|
57
57
|
"fit_beta_calibrator",
|
|
58
|
+
"fit_isotonic_binary",
|
|
58
59
|
"fit_isotonic_calibrator",
|
|
59
60
|
"fit_platt_binary",
|
|
60
61
|
"fit_platt_calibrator",
|
|
@@ -1294,6 +1295,83 @@ def fit_beta_binary(
|
|
|
1294
1295
|
return (a, b, c), apply
|
|
1295
1296
|
|
|
1296
1297
|
|
|
1298
|
+
def fit_isotonic_binary(
|
|
1299
|
+
y_true: np.ndarray, y_score: np.ndarray
|
|
1300
|
+
) -> tuple[None, Callable[[np.ndarray], np.ndarray]]:
|
|
1301
|
+
r"""Binary-probability adapter for :func:`fit_isotonic_calibrator`.
|
|
1302
|
+
|
|
1303
|
+
Mirror of :func:`fit_temperature_binary` / :func:`fit_platt_binary`
|
|
1304
|
+
/ :func:`fit_beta_binary`: returns ``(None, apply)``. Isotonic
|
|
1305
|
+
regression is non-parametric — there are no introspectable scalar
|
|
1306
|
+
parameters to log alongside the apply callable — so the params
|
|
1307
|
+
slot is :obj:`None`.
|
|
1308
|
+
|
|
1309
|
+
The ``None``-in-params slot makes "non-parametric" unambiguous
|
|
1310
|
+
while preserving the canonical ``(params_tuple, apply)`` shape
|
|
1311
|
+
shared by the four binary scalar-prob calibrators. Consumer code
|
|
1312
|
+
can iterate over the full family with one idiom:
|
|
1313
|
+
|
|
1314
|
+
.. code-block:: text
|
|
1315
|
+
|
|
1316
|
+
CALIBRATORS = {
|
|
1317
|
+
"temperature": fit_temperature_binary,
|
|
1318
|
+
"isotonic": fit_isotonic_binary,
|
|
1319
|
+
"platt": fit_platt_binary,
|
|
1320
|
+
"beta": fit_beta_binary,
|
|
1321
|
+
}
|
|
1322
|
+
for name, fit_fn in CALIBRATORS.items():
|
|
1323
|
+
params, apply = fit_fn(y_val, p_val)
|
|
1324
|
+
calibrated = apply(p_test)
|
|
1325
|
+
if params is not None:
|
|
1326
|
+
manifest.record(f"{name}_params", params)
|
|
1327
|
+
|
|
1328
|
+
Added v0.42.0 (closes #44) to complete the binary scalar-prob
|
|
1329
|
+
calibrator family started by ``fit_temperature_binary`` (v0.35.0).
|
|
1330
|
+
|
|
1331
|
+
Parameters
|
|
1332
|
+
----------
|
|
1333
|
+
y_true : np.ndarray, shape (n,)
|
|
1334
|
+
Binary validation labels in ``{0, 1}``.
|
|
1335
|
+
y_score : np.ndarray, shape (n,)
|
|
1336
|
+
Validation predicted probabilities of class 1, in [0, 1].
|
|
1337
|
+
|
|
1338
|
+
Returns
|
|
1339
|
+
-------
|
|
1340
|
+
tuple
|
|
1341
|
+
``(None, apply)`` — ``None`` in the params slot (isotonic is
|
|
1342
|
+
non-parametric); ``apply`` maps probabilities through the
|
|
1343
|
+
fitted monotonic step function.
|
|
1344
|
+
|
|
1345
|
+
Raises
|
|
1346
|
+
------
|
|
1347
|
+
ValueError
|
|
1348
|
+
On shape mismatch, empty input, non-finite scores, or
|
|
1349
|
+
single-class ``y_true`` (propagated from
|
|
1350
|
+
:func:`fit_isotonic_calibrator`).
|
|
1351
|
+
|
|
1352
|
+
Examples
|
|
1353
|
+
--------
|
|
1354
|
+
>>> import numpy as np
|
|
1355
|
+
>>> rng = np.random.default_rng(0)
|
|
1356
|
+
>>> n = 500
|
|
1357
|
+
>>> y_val = rng.binomial(1, 0.3, size=n).astype(int)
|
|
1358
|
+
>>> p_val = np.clip(y_val * 0.6 + rng.normal(0, 0.2, n), 0.01, 0.99)
|
|
1359
|
+
>>> params, apply = fit_isotonic_binary(y_val, p_val)
|
|
1360
|
+
>>> params is None
|
|
1361
|
+
True
|
|
1362
|
+
>>> apply(np.array([0.1, 0.5, 0.9])).shape == (3,)
|
|
1363
|
+
True
|
|
1364
|
+
|
|
1365
|
+
See Also
|
|
1366
|
+
--------
|
|
1367
|
+
fit_isotonic_calibrator : underlying non-parametric fitter.
|
|
1368
|
+
fit_temperature_binary : 1-parameter sibling.
|
|
1369
|
+
fit_platt_binary : 2-parameter sibling.
|
|
1370
|
+
fit_beta_binary : 3-parameter sibling.
|
|
1371
|
+
"""
|
|
1372
|
+
return None, fit_isotonic_calibrator(y_true, y_score)
|
|
1373
|
+
|
|
1374
|
+
|
|
1297
1375
|
def fit_temperature_oracle(
|
|
1298
1376
|
y_true: np.ndarray, y_score: np.ndarray
|
|
1299
1377
|
) -> tuple[float, Callable[[np.ndarray], np.ndarray]]:
|
|
@@ -136,6 +136,7 @@
|
|
|
136
136
|
"file_sha256",
|
|
137
137
|
"fit_beta_binary",
|
|
138
138
|
"fit_beta_calibrator",
|
|
139
|
+
"fit_isotonic_binary",
|
|
139
140
|
"fit_isotonic_calibrator",
|
|
140
141
|
"fit_operating_points",
|
|
141
142
|
"fit_platt_binary",
|
|
@@ -1036,7 +1037,7 @@
|
|
|
1036
1037
|
"doc_first_line": "str(object='') -> str",
|
|
1037
1038
|
"kind": "value",
|
|
1038
1039
|
"type": "str",
|
|
1039
|
-
"value": "'0.
|
|
1040
|
+
"value": "'0.42.0'"
|
|
1040
1041
|
},
|
|
1041
1042
|
"apply_operating_points": {
|
|
1042
1043
|
"doc_first_line": "Apply fitted thresholds to a mixed-class or single-class target slice.",
|
|
@@ -1208,6 +1209,11 @@
|
|
|
1208
1209
|
"kind": "function",
|
|
1209
1210
|
"signature": "(y_true: 'np.ndarray', y_score: 'np.ndarray') -> 'Callable[[np.ndarray], np.ndarray]'"
|
|
1210
1211
|
},
|
|
1212
|
+
"fit_isotonic_binary": {
|
|
1213
|
+
"doc_first_line": "Binary-probability adapter for :func:`fit_isotonic_calibrator`.",
|
|
1214
|
+
"kind": "function",
|
|
1215
|
+
"signature": "(y_true: 'np.ndarray', y_score: 'np.ndarray') -> 'tuple[None, Callable[[np.ndarray], np.ndarray]]'"
|
|
1216
|
+
},
|
|
1211
1217
|
"fit_isotonic_calibrator": {
|
|
1212
1218
|
"doc_first_line": "Niculescu-Mizil & Caruana 2005 [#nm05]_ isotonic regression.",
|
|
1213
1219
|
"kind": "function",
|
|
@@ -12,6 +12,8 @@ import pytest
|
|
|
12
12
|
from eval_toolkit import (
|
|
13
13
|
fit_beta_binary,
|
|
14
14
|
fit_beta_calibrator,
|
|
15
|
+
fit_isotonic_binary,
|
|
16
|
+
fit_isotonic_calibrator,
|
|
15
17
|
fit_platt_binary,
|
|
16
18
|
fit_platt_calibrator,
|
|
17
19
|
fit_temperature_binary,
|
|
@@ -160,6 +162,66 @@ def test_beta_binary_apply_rejects_non_finite_test_scores(
|
|
|
160
162
|
apply(np.array([0.5, np.nan, 0.8]))
|
|
161
163
|
|
|
162
164
|
|
|
165
|
+
# ---------- fit_isotonic_binary ----------
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
@pytest.mark.unit
|
|
169
|
+
def test_isotonic_binary_returns_none_params_and_apply(
|
|
170
|
+
synthetic_binary_data: tuple[np.ndarray, np.ndarray],
|
|
171
|
+
) -> None:
|
|
172
|
+
"""Isotonic is non-parametric → params slot is None."""
|
|
173
|
+
y, p = synthetic_binary_data
|
|
174
|
+
params, apply = fit_isotonic_binary(y, p)
|
|
175
|
+
assert params is None
|
|
176
|
+
assert callable(apply)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
@pytest.mark.unit
|
|
180
|
+
def test_isotonic_binary_apply_returns_same_shape(
|
|
181
|
+
synthetic_binary_data: tuple[np.ndarray, np.ndarray],
|
|
182
|
+
) -> None:
|
|
183
|
+
y, p = synthetic_binary_data
|
|
184
|
+
_, apply = fit_isotonic_binary(y, p)
|
|
185
|
+
test = np.array([0.1, 0.5, 0.9])
|
|
186
|
+
out = apply(test)
|
|
187
|
+
assert out.shape == test.shape
|
|
188
|
+
assert (out >= 0.0).all() and (out <= 1.0).all()
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
@pytest.mark.unit
|
|
192
|
+
def test_isotonic_binary_apply_matches_underlying_calibrator(
|
|
193
|
+
synthetic_binary_data: tuple[np.ndarray, np.ndarray],
|
|
194
|
+
) -> None:
|
|
195
|
+
"""fit_isotonic_binary apply should match fit_isotonic_calibrator output."""
|
|
196
|
+
y, p = synthetic_binary_data
|
|
197
|
+
_, apply = fit_isotonic_binary(y, p)
|
|
198
|
+
canonical_apply = fit_isotonic_calibrator(y, p)
|
|
199
|
+
test = np.linspace(0.05, 0.95, 20)
|
|
200
|
+
np.testing.assert_allclose(apply(test), canonical_apply(test))
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
@pytest.mark.unit
|
|
204
|
+
def test_isotonic_binary_rejects_single_class() -> None:
|
|
205
|
+
y_single = np.zeros(50, dtype=int)
|
|
206
|
+
p = np.random.default_rng(0).uniform(0.0, 1.0, 50)
|
|
207
|
+
with pytest.raises(ValueError):
|
|
208
|
+
fit_isotonic_binary(y_single, p)
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
@pytest.mark.unit
|
|
212
|
+
def test_isotonic_binary_monotone(
|
|
213
|
+
synthetic_binary_data: tuple[np.ndarray, np.ndarray],
|
|
214
|
+
) -> None:
|
|
215
|
+
"""Isotonic regression is monotone non-decreasing in the score."""
|
|
216
|
+
y, p = synthetic_binary_data
|
|
217
|
+
_, apply = fit_isotonic_binary(y, p)
|
|
218
|
+
test = np.linspace(0.05, 0.95, 50)
|
|
219
|
+
out = apply(test)
|
|
220
|
+
# Allow tiny numerical noise but enforce non-decreasing trend
|
|
221
|
+
deltas = np.diff(out)
|
|
222
|
+
assert (deltas >= -1e-12).all(), "isotonic should be non-decreasing"
|
|
223
|
+
|
|
224
|
+
|
|
163
225
|
# ---------- consistency across the calibrator family ----------
|
|
164
226
|
|
|
165
227
|
|
|
@@ -167,14 +229,15 @@ def test_beta_binary_apply_rejects_non_finite_test_scores(
|
|
|
167
229
|
def test_all_four_binary_adapters_have_consistent_shape(
|
|
168
230
|
synthetic_binary_data: tuple[np.ndarray, np.ndarray],
|
|
169
231
|
) -> None:
|
|
170
|
-
"""
|
|
232
|
+
"""temperature, isotonic, platt, beta all return ``(params, apply)``.
|
|
171
233
|
|
|
172
234
|
Documents the audit-battery contract for the 4-calibrator pattern.
|
|
173
235
|
"""
|
|
174
236
|
y, p = synthetic_binary_data
|
|
175
|
-
# All return (params, apply); apply is a callable taking (n,) -> (n,)
|
|
237
|
+
# All return (params, apply); apply is a callable taking (n,) -> (n,).
|
|
176
238
|
for name, fitter in [
|
|
177
239
|
("temperature", fit_temperature_binary),
|
|
240
|
+
("isotonic", fit_isotonic_binary),
|
|
178
241
|
("platt", fit_platt_binary),
|
|
179
242
|
("beta", fit_beta_binary),
|
|
180
243
|
]:
|
|
@@ -185,6 +248,38 @@ def test_all_four_binary_adapters_have_consistent_shape(
|
|
|
185
248
|
assert (out >= 0.0).all() and (out <= 1.0).all(), f"{name}: output not in [0,1]"
|
|
186
249
|
|
|
187
250
|
|
|
251
|
+
@pytest.mark.unit
|
|
252
|
+
def test_consumer_idiom_iterating_all_four_calibrators(
|
|
253
|
+
synthetic_binary_data: tuple[np.ndarray, np.ndarray],
|
|
254
|
+
) -> None:
|
|
255
|
+
"""End-to-end consumer idiom: iterate the 4-element family with one shape.
|
|
256
|
+
|
|
257
|
+
Matches the calibration-battery pattern in
|
|
258
|
+
``prompt-injection-detection-prototype/src/eval/calibration_battery.py``
|
|
259
|
+
(ADR-056). The ``params is not None`` check distinguishes parametric
|
|
260
|
+
from non-parametric in a single conditional.
|
|
261
|
+
"""
|
|
262
|
+
y, p = synthetic_binary_data
|
|
263
|
+
fitters = {
|
|
264
|
+
"temperature": fit_temperature_binary,
|
|
265
|
+
"isotonic": fit_isotonic_binary,
|
|
266
|
+
"platt": fit_platt_binary,
|
|
267
|
+
"beta": fit_beta_binary,
|
|
268
|
+
}
|
|
269
|
+
p_test = np.linspace(0.05, 0.95, 30)
|
|
270
|
+
recorded_params: dict[str, object] = {}
|
|
271
|
+
calibrated: dict[str, np.ndarray] = {}
|
|
272
|
+
for name, fit_fn in fitters.items():
|
|
273
|
+
params, apply = fit_fn(y, p)
|
|
274
|
+
calibrated[name] = apply(p_test)
|
|
275
|
+
if params is not None:
|
|
276
|
+
recorded_params[name] = params
|
|
277
|
+
# Three of four have inspectable params; isotonic is None.
|
|
278
|
+
assert set(recorded_params.keys()) == {"temperature", "platt", "beta"}
|
|
279
|
+
# All four produced calibrated outputs of matching shape.
|
|
280
|
+
assert all(out.shape == p_test.shape for out in calibrated.values())
|
|
281
|
+
|
|
282
|
+
|
|
188
283
|
@pytest.mark.unit
|
|
189
284
|
def test_platt_binary_params_are_pair(
|
|
190
285
|
synthetic_binary_data: tuple[np.ndarray, np.ndarray],
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/baseline/test_plotting_visual/plot_lift_ci.png
RENAMED
|
File without changes
|
{eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/baseline/test_plotting_visual/plot_metric_bars.png
RENAMED
|
File without changes
|
|
File without changes
|
{eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/baseline/test_plotting_visual/plot_pr_curve.png
RENAMED
|
File without changes
|
|
File without changes
|
{eval_toolkit-0.41.0 → eval_toolkit-0.42.0}/tests/baseline/test_plotting_visual/plot_roc_curve.png
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|