evalsuite-python 0.5.0__tar.gz → 0.5.2__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.
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/CHANGELOG.md +30 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/PKG-INFO +34 -28
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/README.md +33 -27
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/pyproject.toml +1 -1
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/src/evalsuite/_bench_metrics.py +1 -1
- evalsuite_python-0.5.2/src/evalsuite/_lazy.py +32 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/src/evalsuite/api.py +2 -1
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/src/evalsuite/benchmarks.py +116 -3
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/src/evalsuite/calibration.py +7 -1
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/src/evalsuite/classification/metrics.py +4 -2
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/src/evalsuite/cli/main.py +3 -3
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/src/evalsuite/clinical/report.py +7 -1
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/src/evalsuite/llmsys/_common.py +9 -1
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/src/evalsuite/llmsys/code.py +42 -33
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/src/evalsuite/llmsys/safety.py +1 -1
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/src/evalsuite/llmsys/uncertainty.py +6 -2
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/src/evalsuite/stats/effect.py +8 -2
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/src/evalsuite/stats/hypothesis.py +8 -2
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/src/evalsuite/stats/intervals.py +9 -2
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/src/evalsuite/stats/paired.py +9 -2
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/src/evalsuite/text/judge.py +8 -2
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/src/evalsuite/text/structured.py +5 -1
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/src/evalsuite/version.py +1 -1
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/src/evalsuite/vision/segmentation.py +7 -1
- evalsuite_python-0.5.2/tests/llmsys/test_hardening.py +52 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/tests/output/test_benchmarks.py +11 -2
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/.gitignore +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/CONTRIBUTING.md +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/LICENSE +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/src/evalsuite/__init__.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/src/evalsuite/__main__.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/src/evalsuite/classification/__init__.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/src/evalsuite/classification/_common.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/src/evalsuite/cli/__init__.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/src/evalsuite/clinical/__init__.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/src/evalsuite/clinical/metrics.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/src/evalsuite/core/__init__.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/src/evalsuite/core/context.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/src/evalsuite/core/exceptions.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/src/evalsuite/core/export.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/src/evalsuite/core/registry.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/src/evalsuite/core/result.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/src/evalsuite/core/types.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/src/evalsuite/core/validation.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/src/evalsuite/llmsys/__init__.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/src/evalsuite/llmsys/agents.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/src/evalsuite/llmsys/efficiency.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/src/evalsuite/llmsys/longcontext.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/src/evalsuite/llmsys/multilingual.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/src/evalsuite/llmsys/robustness.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/src/evalsuite/plot.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/src/evalsuite/py.typed +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/src/evalsuite/regression/__init__.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/src/evalsuite/regression/metrics.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/src/evalsuite/reporting.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/src/evalsuite/stats/__init__.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/src/evalsuite/stats/_resolve.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/src/evalsuite/stats/compare.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/src/evalsuite/stats/results.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/src/evalsuite/text/__init__.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/src/evalsuite/text/_common.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/src/evalsuite/text/factuality.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/src/evalsuite/text/generation.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/src/evalsuite/text/qa.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/src/evalsuite/text/rag.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/src/evalsuite/text/reasoning.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/src/evalsuite/text/report.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/src/evalsuite/text/retrieval.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/src/evalsuite/text/semantic.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/src/evalsuite/text/spice.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/src/evalsuite/vision/__init__.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/src/evalsuite/vision/detection.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/tests/__init__.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/tests/classification/__init__.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/tests/classification/test_against_sklearn.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/tests/clinical/__init__.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/tests/clinical/test_outputs.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/tests/clinical/test_v020.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/tests/conftest.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/tests/integration/__init__.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/tests/integration/test_qa.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/tests/integration/test_readme_examples.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/tests/integration/test_repo_hygiene.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/tests/llmsys/__init__.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/tests/llmsys/test_code_longcontext_efficiency.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/tests/llmsys/test_edges.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/tests/llmsys/test_safety_robustness.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/tests/llmsys/test_uncertainty_agents_multilingual.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/tests/output/__init__.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/tests/output/test_cli.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/tests/output/test_plot.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/tests/output/test_reporting.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/tests/regression/__init__.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/tests/regression/test_against_sklearn.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/tests/stats/__init__.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/tests/stats/test_branches.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/tests/stats/test_compare.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/tests/stats/test_reference.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/tests/text/__init__.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/tests/text/test_against_references.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/tests/text/test_text_edges.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/tests/text/test_v040.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/tests/unit/__init__.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/tests/unit/test_core.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/tests/unit/test_edges.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/tests/vision/__init__.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/tests/vision/test_detection.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/tests/vision/test_edges.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/tests/vision/test_outputs.py +0 -0
- {evalsuite_python-0.5.0 → evalsuite_python-0.5.2}/tests/vision/test_segmentation.py +0 -0
|
@@ -6,6 +6,36 @@ All notable changes to this project are documented here. The format follows
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.5.2] - 2026-11-07
|
|
10
|
+
|
|
11
|
+
Security and performance hardening. No API changes.
|
|
12
|
+
|
|
13
|
+
### Security
|
|
14
|
+
- The built-in PII e-mail detector no longer backtracks quadratically: `detect_pii` / `pii_leakage_rate`
|
|
15
|
+
on 60 kB of adversarial text went from ~6 s to ~10 ms.
|
|
16
|
+
- `codebleu`, `code_complexity` and `syntax_validity_rate` handle source nested too deeply for CPython's
|
|
17
|
+
parser (MemoryError / RecursionError) instead of crashing: invalid for syntax validity, an
|
|
18
|
+
`InputValidationError` for complexity, no syntax match for CodeBLEU.
|
|
19
|
+
- Internal `assert` type checks replaced by explicit errors, so behaviour is the same under `python -O`.
|
|
20
|
+
- CI and release workflows pin every action to a commit SHA; Dependabot keeps them and the dependencies current.
|
|
21
|
+
- Documented that user-supplied regular expressions (`format_compliance`) run on a backtracking engine.
|
|
22
|
+
|
|
23
|
+
### Performance
|
|
24
|
+
- `import evalsuite` no longer imports SciPy; it is loaded on first use (import time ~0.6 s → ~0.07 s).
|
|
25
|
+
- CodeBLEU syntax match uses interned subtree ids instead of serialised strings: linear in the size of the
|
|
26
|
+
program rather than quadratic.
|
|
27
|
+
|
|
28
|
+
## [0.5.1] - 2026-10-10
|
|
29
|
+
|
|
30
|
+
### Added
|
|
31
|
+
- `evalsuite benchmark --suite llmsys` (and `run_benchmarks(suite="llmsys")`): v0.5 LLM-systems workloads
|
|
32
|
+
against radon, the codebleu package, scikit-learn, jsonschema, SciPy and NumPy; included in `--suite all`
|
|
33
|
+
(27 cases, 81 measurements, all agreeing with their reference).
|
|
34
|
+
|
|
35
|
+
### Changed
|
|
36
|
+
- LLM-systems metrics accept NumPy arrays without converting them to Python lists first (much faster on
|
|
37
|
+
large inputs); `confidence_accuracy_correlation` computes Spearman from the ranks it already has.
|
|
38
|
+
|
|
9
39
|
## [0.5.0] - 2026-10-10
|
|
10
40
|
|
|
11
41
|
LLM systems (the v0.5.0 roadmap: 8 areas, 85 items) and SPICE. No existing API changed.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: evalsuite-python
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.2
|
|
4
4
|
Summary: A unified Python framework for machine-learning, clinical, statistical, segmentation, object-detection, uncertainty, and model evaluation.
|
|
5
5
|
Project-URL: Homepage, https://evalsuite-nine.vercel.app
|
|
6
6
|
Project-URL: Documentation, https://evalsuite-nine.vercel.app/docs
|
|
@@ -83,7 +83,7 @@ Description-Content-Type: text/markdown
|
|
|
83
83
|
EvalSuite brings classification, regression, clinical, statistical, segmentation and object-detection
|
|
84
84
|
evaluation into one consistent, validated, documented framework.
|
|
85
85
|
|
|
86
|
-
> **Status: stable (0.5.
|
|
86
|
+
> **Status: stable (0.5.2).** Every item on the 0.1.0–0.5.0 roadmaps is implemented and verified, including SPICE.
|
|
87
87
|
> **LLM systems arrive in 0.5.0**: safety, robustness, calibration and uncertainty, agents and tool use,
|
|
88
88
|
> multilingual, code generation, long context and summarization, and inference efficiency and cost.
|
|
89
89
|
|
|
@@ -339,41 +339,47 @@ Input files can be CSV, TSV, Parquet or JSON. Output format follows `--format` o
|
|
|
339
339
|
|
|
340
340
|
## Performance
|
|
341
341
|
|
|
342
|
-
Benchmarked against reference implementations on the same data (fastest of 5 runs;
|
|
343
|
-
Linux x86_64). **Overall:
|
|
344
|
-
difference
|
|
345
|
-
|
|
342
|
+
Benchmarked against reference implementations on the same data (fastest of 5 runs; EvalSuite 0.5.1, Python 3.13,
|
|
343
|
+
NumPy 2.5, Linux x86_64). **Overall: 81 of 81 rows agree with the reference** (largest
|
|
344
|
+
difference 2.5e-14), 56 are faster, and the geometric-mean speed-up across all
|
|
345
|
+
27 cases and three sizes is **1.89×**. At 1,000,000 samples (pixels for
|
|
346
346
|
segmentation; 1,000 images for detection and 50 for Hausdorff; 10,000 examples for the LLM cases), in
|
|
347
347
|
alphabetical order:
|
|
348
348
|
|
|
349
349
|
| Case | Reference | EvalSuite (ms) | Reference (ms) | Speed-up |
|
|
350
350
|
| --- | --- | ---: | ---: | ---: |
|
|
351
|
-
| 10 classes: macro F1 | scikit-learn |
|
|
352
|
-
| agreement: Krippendorff's alpha, interval (4 raters × 10000 items) | krippendorff |
|
|
353
|
-
| binary: 8 label metrics via evaluate() | scikit-learn |
|
|
354
|
-
| binary: ROC AUC | scikit-learn |
|
|
355
|
-
| calibration: slope and intercept | statsmodels |
|
|
356
|
-
| clinical: diagnostic report (7 CIs) | statsmodels |
|
|
357
|
-
| clinical: sensitivity, specificity, LR+, LR− | scikit-learn |
|
|
358
|
-
| decision curve: 99 thresholds | NumPy loop |
|
|
359
|
-
| detection: COCO evaluation (1000 images) | pycocotools |
|
|
360
|
-
| multiple testing: Hochberg (n p-values) | statsmodels |
|
|
361
|
-
| regression: MAE, MSE, RMSE, R² via evaluate() | scikit-learn |
|
|
362
|
-
| retrieval: MRR, MAP@20, NDCG@10 (10000 queries) | ranx |
|
|
363
|
-
| segmentation: Dice and IoU per class (n = pixels) | scikit-learn |
|
|
364
|
-
| segmentation: Hausdorff distance (50 images) | SciPy |
|
|
365
|
-
| statistics: Cramér's V (5×5 table) | SciPy | 0.
|
|
366
|
-
| statistics: Mann–Whitney U | SciPy |
|
|
367
|
-
| statistics: Welch t-test | SciPy |
|
|
368
|
-
| structured: JSON Schema compliance (10000 documents) | jsonschema |
|
|
369
|
-
|
|
|
370
|
-
|
|
|
371
|
-
|
|
|
351
|
+
| 10 classes: macro F1 | scikit-learn | 41.5 | 137.6 | **3.3×** |
|
|
352
|
+
| agreement: Krippendorff's alpha, interval (4 raters × 10000 items) | krippendorff | 4.5 | 4.5 | **1.0×** |
|
|
353
|
+
| binary: 8 label metrics via evaluate() | scikit-learn | 49.4 | 1056.8 | **21.4×** |
|
|
354
|
+
| binary: ROC AUC | scikit-learn | 200.3 | 338.5 | **1.7×** |
|
|
355
|
+
| calibration: slope and intercept | statsmodels | 344.3 | 1541.7 | **4.5×** |
|
|
356
|
+
| clinical: diagnostic report (7 CIs) | statsmodels | 33.5 | 9.6 | 0.29× |
|
|
357
|
+
| clinical: sensitivity, specificity, LR+, LR− | scikit-learn | 137.9 | 438.1 | **3.2×** |
|
|
358
|
+
| decision curve: 99 thresholds | NumPy loop | 172.1 | 236.2 | **1.4×** |
|
|
359
|
+
| detection: COCO evaluation (1000 images) | pycocotools | 1147.9 | 1351.0 | **1.2×** |
|
|
360
|
+
| multiple testing: Hochberg (n p-values) | statsmodels | 91.9 | 98.4 | **1.1×** |
|
|
361
|
+
| regression: MAE, MSE, RMSE, R² via evaluate() | scikit-learn | 32.0 | 22.6 | 0.71× |
|
|
362
|
+
| retrieval: MRR, MAP@20, NDCG@10 (10000 queries) | ranx | 248.6 | 897.0 | **3.6×** |
|
|
363
|
+
| segmentation: Dice and IoU per class (n = pixels) | scikit-learn | 51.6 | 289.5 | **5.6×** |
|
|
364
|
+
| segmentation: Hausdorff distance (50 images) | SciPy | 31.9 | 23.8 | 0.75× |
|
|
365
|
+
| statistics: Cramér's V (5×5 table) | SciPy | 0.3 | 0.3 | 0.95× |
|
|
366
|
+
| statistics: Mann–Whitney U | SciPy | 390.3 | 425.8 | **1.1×** |
|
|
367
|
+
| statistics: Welch t-test | SciPy | 37.7 | 16.3 | 0.43× |
|
|
368
|
+
| structured: JSON Schema compliance (10000 documents) | jsonschema | 159.4 | 371.7 | **2.3×** |
|
|
369
|
+
| systems: CodeBLEU n-gram terms (1000 programs) | codebleu | 740.3 | 791.9 | **1.1×** |
|
|
370
|
+
| systems: confidence AUROC (n answers) | scikit-learn | 194.4 | 327.7 | **1.7×** |
|
|
371
|
+
| systems: distribution-shift drop, Welch CI (n scores) | SciPy | 13.3 | 16.9 | **1.3×** |
|
|
372
|
+
| systems: invalid tool calls vs JSON Schema (10000 tasks) | jsonschema | 145.0 | 333.2 | **2.3×** |
|
|
373
|
+
| systems: latency p50 / p95 / p99 (n requests) | NumPy | 41.0 | 19.7 | 0.48× |
|
|
374
|
+
| systems: maintainability index (1000 programs) | radon | 138.3 | 168.7 | **1.2×** |
|
|
375
|
+
| text: corpus BLEU and chrF (10000 sentences) | sacreBLEU | 2726.2 | 3121.0 | **1.1×** |
|
|
376
|
+
| text: METEOR, exact and stem matches (10000 sentences) | NLTK | 507.4 | 590.3 | **1.2×** |
|
|
377
|
+
| text: ROUGE-1, ROUGE-2, ROUGE-L (10000 sentences) | rouge-score | 1147.3 | 1010.7 | 0.88× |
|
|
372
378
|
|
|
373
379
|
**Every metric is benchmarked too** (`evalsuite benchmark --suite metrics`): all 225 registered metrics and
|
|
374
380
|
statistics functions at 10,000 and 100,000 samples. 86 are compared with a reference library, 118 with an
|
|
375
381
|
independent textbook formula and 21 (learned, judge-dependent or randomised) are timed alone; **all 204
|
|
376
|
-
comparisons agree**, and against the libraries the geometric-mean speed-up is **2.
|
|
382
|
+
comparisons agree**, and against the libraries the geometric-mean speed-up is **2.67×** (139 of 172
|
|
377
383
|
measurements faster). Per-metric table in BENCHMARKS.md.
|
|
378
384
|
|
|
379
385
|
`evaluate()` validates inputs once and builds the confusion matrix once for all metrics, which is where most
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
EvalSuite brings classification, regression, clinical, statistical, segmentation and object-detection
|
|
11
11
|
evaluation into one consistent, validated, documented framework.
|
|
12
12
|
|
|
13
|
-
> **Status: stable (0.5.
|
|
13
|
+
> **Status: stable (0.5.2).** Every item on the 0.1.0–0.5.0 roadmaps is implemented and verified, including SPICE.
|
|
14
14
|
> **LLM systems arrive in 0.5.0**: safety, robustness, calibration and uncertainty, agents and tool use,
|
|
15
15
|
> multilingual, code generation, long context and summarization, and inference efficiency and cost.
|
|
16
16
|
|
|
@@ -266,41 +266,47 @@ Input files can be CSV, TSV, Parquet or JSON. Output format follows `--format` o
|
|
|
266
266
|
|
|
267
267
|
## Performance
|
|
268
268
|
|
|
269
|
-
Benchmarked against reference implementations on the same data (fastest of 5 runs;
|
|
270
|
-
Linux x86_64). **Overall:
|
|
271
|
-
difference
|
|
272
|
-
|
|
269
|
+
Benchmarked against reference implementations on the same data (fastest of 5 runs; EvalSuite 0.5.1, Python 3.13,
|
|
270
|
+
NumPy 2.5, Linux x86_64). **Overall: 81 of 81 rows agree with the reference** (largest
|
|
271
|
+
difference 2.5e-14), 56 are faster, and the geometric-mean speed-up across all
|
|
272
|
+
27 cases and three sizes is **1.89×**. At 1,000,000 samples (pixels for
|
|
273
273
|
segmentation; 1,000 images for detection and 50 for Hausdorff; 10,000 examples for the LLM cases), in
|
|
274
274
|
alphabetical order:
|
|
275
275
|
|
|
276
276
|
| Case | Reference | EvalSuite (ms) | Reference (ms) | Speed-up |
|
|
277
277
|
| --- | --- | ---: | ---: | ---: |
|
|
278
|
-
| 10 classes: macro F1 | scikit-learn |
|
|
279
|
-
| agreement: Krippendorff's alpha, interval (4 raters × 10000 items) | krippendorff |
|
|
280
|
-
| binary: 8 label metrics via evaluate() | scikit-learn |
|
|
281
|
-
| binary: ROC AUC | scikit-learn |
|
|
282
|
-
| calibration: slope and intercept | statsmodels |
|
|
283
|
-
| clinical: diagnostic report (7 CIs) | statsmodels |
|
|
284
|
-
| clinical: sensitivity, specificity, LR+, LR− | scikit-learn |
|
|
285
|
-
| decision curve: 99 thresholds | NumPy loop |
|
|
286
|
-
| detection: COCO evaluation (1000 images) | pycocotools |
|
|
287
|
-
| multiple testing: Hochberg (n p-values) | statsmodels |
|
|
288
|
-
| regression: MAE, MSE, RMSE, R² via evaluate() | scikit-learn |
|
|
289
|
-
| retrieval: MRR, MAP@20, NDCG@10 (10000 queries) | ranx |
|
|
290
|
-
| segmentation: Dice and IoU per class (n = pixels) | scikit-learn |
|
|
291
|
-
| segmentation: Hausdorff distance (50 images) | SciPy |
|
|
292
|
-
| statistics: Cramér's V (5×5 table) | SciPy | 0.
|
|
293
|
-
| statistics: Mann–Whitney U | SciPy |
|
|
294
|
-
| statistics: Welch t-test | SciPy |
|
|
295
|
-
| structured: JSON Schema compliance (10000 documents) | jsonschema |
|
|
296
|
-
|
|
|
297
|
-
|
|
|
298
|
-
|
|
|
278
|
+
| 10 classes: macro F1 | scikit-learn | 41.5 | 137.6 | **3.3×** |
|
|
279
|
+
| agreement: Krippendorff's alpha, interval (4 raters × 10000 items) | krippendorff | 4.5 | 4.5 | **1.0×** |
|
|
280
|
+
| binary: 8 label metrics via evaluate() | scikit-learn | 49.4 | 1056.8 | **21.4×** |
|
|
281
|
+
| binary: ROC AUC | scikit-learn | 200.3 | 338.5 | **1.7×** |
|
|
282
|
+
| calibration: slope and intercept | statsmodels | 344.3 | 1541.7 | **4.5×** |
|
|
283
|
+
| clinical: diagnostic report (7 CIs) | statsmodels | 33.5 | 9.6 | 0.29× |
|
|
284
|
+
| clinical: sensitivity, specificity, LR+, LR− | scikit-learn | 137.9 | 438.1 | **3.2×** |
|
|
285
|
+
| decision curve: 99 thresholds | NumPy loop | 172.1 | 236.2 | **1.4×** |
|
|
286
|
+
| detection: COCO evaluation (1000 images) | pycocotools | 1147.9 | 1351.0 | **1.2×** |
|
|
287
|
+
| multiple testing: Hochberg (n p-values) | statsmodels | 91.9 | 98.4 | **1.1×** |
|
|
288
|
+
| regression: MAE, MSE, RMSE, R² via evaluate() | scikit-learn | 32.0 | 22.6 | 0.71× |
|
|
289
|
+
| retrieval: MRR, MAP@20, NDCG@10 (10000 queries) | ranx | 248.6 | 897.0 | **3.6×** |
|
|
290
|
+
| segmentation: Dice and IoU per class (n = pixels) | scikit-learn | 51.6 | 289.5 | **5.6×** |
|
|
291
|
+
| segmentation: Hausdorff distance (50 images) | SciPy | 31.9 | 23.8 | 0.75× |
|
|
292
|
+
| statistics: Cramér's V (5×5 table) | SciPy | 0.3 | 0.3 | 0.95× |
|
|
293
|
+
| statistics: Mann–Whitney U | SciPy | 390.3 | 425.8 | **1.1×** |
|
|
294
|
+
| statistics: Welch t-test | SciPy | 37.7 | 16.3 | 0.43× |
|
|
295
|
+
| structured: JSON Schema compliance (10000 documents) | jsonschema | 159.4 | 371.7 | **2.3×** |
|
|
296
|
+
| systems: CodeBLEU n-gram terms (1000 programs) | codebleu | 740.3 | 791.9 | **1.1×** |
|
|
297
|
+
| systems: confidence AUROC (n answers) | scikit-learn | 194.4 | 327.7 | **1.7×** |
|
|
298
|
+
| systems: distribution-shift drop, Welch CI (n scores) | SciPy | 13.3 | 16.9 | **1.3×** |
|
|
299
|
+
| systems: invalid tool calls vs JSON Schema (10000 tasks) | jsonschema | 145.0 | 333.2 | **2.3×** |
|
|
300
|
+
| systems: latency p50 / p95 / p99 (n requests) | NumPy | 41.0 | 19.7 | 0.48× |
|
|
301
|
+
| systems: maintainability index (1000 programs) | radon | 138.3 | 168.7 | **1.2×** |
|
|
302
|
+
| text: corpus BLEU and chrF (10000 sentences) | sacreBLEU | 2726.2 | 3121.0 | **1.1×** |
|
|
303
|
+
| text: METEOR, exact and stem matches (10000 sentences) | NLTK | 507.4 | 590.3 | **1.2×** |
|
|
304
|
+
| text: ROUGE-1, ROUGE-2, ROUGE-L (10000 sentences) | rouge-score | 1147.3 | 1010.7 | 0.88× |
|
|
299
305
|
|
|
300
306
|
**Every metric is benchmarked too** (`evalsuite benchmark --suite metrics`): all 225 registered metrics and
|
|
301
307
|
statistics functions at 10,000 and 100,000 samples. 86 are compared with a reference library, 118 with an
|
|
302
308
|
independent textbook formula and 21 (learned, judge-dependent or randomised) are timed alone; **all 204
|
|
303
|
-
comparisons agree**, and against the libraries the geometric-mean speed-up is **2.
|
|
309
|
+
comparisons agree**, and against the libraries the geometric-mean speed-up is **2.67×** (139 of 172
|
|
304
310
|
measurements faster). Per-metric table in BENCHMARKS.md.
|
|
305
311
|
|
|
306
312
|
`evaluate()` validates inputs once and builds the confusion matrix once for all metrics, which is where most
|
|
@@ -112,7 +112,7 @@ ignore_missing_imports = true
|
|
|
112
112
|
|
|
113
113
|
[[tool.mypy.overrides]]
|
|
114
114
|
# optional reference libraries used only by the benchmarks and METEOR's default stemmer
|
|
115
|
-
module = ["sacrebleu.*", "rouge_score.*", "nltk.*", "ranx.*", "krippendorff.*", "jsonschema.*", "pycocotools.*"]
|
|
115
|
+
module = ["sacrebleu.*", "rouge_score.*", "nltk.*", "ranx.*", "krippendorff.*", "jsonschema.*", "pycocotools.*", "codebleu.*", "radon.*", "pycocoevalcap.*", "ot.*", "choix.*"]
|
|
116
116
|
ignore_missing_imports = true
|
|
117
117
|
follow_untyped_imports = false
|
|
118
118
|
|
|
@@ -903,7 +903,7 @@ def _llm(c: _Cases, n: int, rng: np.random.Generator) -> None:
|
|
|
903
903
|
|
|
904
904
|
cider_ref: Optional[Callable[[], float]] = None
|
|
905
905
|
with contextlib.suppress(ImportError):
|
|
906
|
-
from pycocoevalcap.cider.cider import Cider
|
|
906
|
+
from pycocoevalcap.cider.cider import Cider
|
|
907
907
|
|
|
908
908
|
def _cider() -> float:
|
|
909
909
|
res = {i: [p] for i, p in enumerate(preds)}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""Deferred imports for heavy optional-at-import-time dependencies (SciPy submodules).
|
|
2
|
+
|
|
3
|
+
``import evalsuite`` should not pay for SciPy until a function that needs it runs.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import importlib
|
|
9
|
+
from types import ModuleType
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class LazyModule(ModuleType):
|
|
14
|
+
"""Module proxy that imports the real module on first attribute access."""
|
|
15
|
+
|
|
16
|
+
def __init__(self, name: str) -> None:
|
|
17
|
+
super().__init__(name)
|
|
18
|
+
self.__dict__["_mod"] = None
|
|
19
|
+
|
|
20
|
+
def _load(self) -> ModuleType:
|
|
21
|
+
mod = self.__dict__["_mod"]
|
|
22
|
+
if mod is None:
|
|
23
|
+
mod = importlib.import_module(self.__name__)
|
|
24
|
+
self.__dict__["_mod"] = mod
|
|
25
|
+
return mod # type: ignore[no-any-return]
|
|
26
|
+
|
|
27
|
+
def __getattr__(self, attr: str) -> Any:
|
|
28
|
+
return getattr(self._load(), attr)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def lazy_module(name: str) -> Any:
|
|
32
|
+
return LazyModule(name)
|
|
@@ -63,7 +63,8 @@ def _classification_registry(
|
|
|
63
63
|
|
|
64
64
|
def _hamming(ctx: ClassificationContext) -> MetricResult:
|
|
65
65
|
if ctx.target_type == "multilabel":
|
|
66
|
-
|
|
66
|
+
if ctx.y_pred is None:
|
|
67
|
+
raise InputValidationError("y_pred is required for this metric.")
|
|
67
68
|
wrong = (ctx.y_true != ctx.y_pred).mean(axis=1)
|
|
68
69
|
else:
|
|
69
70
|
wrong = (ctx.true_idx != ctx.pred_idx).astype(float)
|
|
@@ -524,6 +524,117 @@ def _clinical_cases(n: int, rng: np.random.Generator) -> list[Case]:
|
|
|
524
524
|
return [(name, ref, es_fn, own if own is not None else refs.get(name)) for name, ref, es_fn, own in cases]
|
|
525
525
|
|
|
526
526
|
|
|
527
|
+
def _llmsys_cases(n: int, rng: np.random.Generator) -> list[Case]:
|
|
528
|
+
"""v0.5.0: LLM systems workloads (code quality, selective prediction, agent tool calls, shift, latency)."""
|
|
529
|
+
import json as _json
|
|
530
|
+
|
|
531
|
+
import evalsuite as es
|
|
532
|
+
|
|
533
|
+
from ._bench_metrics import _CODE_SAMPLES
|
|
534
|
+
|
|
535
|
+
m = max(10, n // 100)
|
|
536
|
+
progs = [str(rng.choice(np.array(_CODE_SAMPLES))) for _ in range(max(4, m // 10))]
|
|
537
|
+
refs_c = progs
|
|
538
|
+
preds_c = [p.replace("a", "x").replace("s", "t") for p in progs]
|
|
539
|
+
conf = rng.uniform(0, 1, n)
|
|
540
|
+
correct = rng.random(n) < conf
|
|
541
|
+
tools = {
|
|
542
|
+
t: {"type": "object", "properties": {"q": {"type": "string"}, "k": {"type": "integer"}}, "required": ["q"]}
|
|
543
|
+
for t in ("search", "calc", "email")
|
|
544
|
+
}
|
|
545
|
+
trajs = [
|
|
546
|
+
[
|
|
547
|
+
{
|
|
548
|
+
"name": str(rng.choice(["search", "calc", "email", "bad"])),
|
|
549
|
+
"arguments": {"q": "x"} if rng.random() > 0.1 else {"k": 1},
|
|
550
|
+
}
|
|
551
|
+
for _ in range(int(rng.integers(1, 6)))
|
|
552
|
+
]
|
|
553
|
+
for _ in range(m)
|
|
554
|
+
]
|
|
555
|
+
src, tgt = rng.normal(0.8, 0.1, n), rng.normal(0.75, 0.12, n)
|
|
556
|
+
lat = rng.lognormal(0, 0.5, n)
|
|
557
|
+
|
|
558
|
+
def es_shift() -> list[float]:
|
|
559
|
+
r = es.distribution_shift_drop(src, tgt)
|
|
560
|
+
return [r.params["ci_low"], r.params["ci_high"]]
|
|
561
|
+
|
|
562
|
+
def es_lat() -> list[float]:
|
|
563
|
+
r = es.latency_percentiles(lat)
|
|
564
|
+
return [r.params["p50"], r.params["p95"], r.params["p99"]]
|
|
565
|
+
|
|
566
|
+
cases: list[Case] = [
|
|
567
|
+
(
|
|
568
|
+
f"systems: maintainability index ({len(progs)} programs)",
|
|
569
|
+
"radon",
|
|
570
|
+
lambda: [float(es.code_complexity(progs).params["maintainability_index"])],
|
|
571
|
+
None,
|
|
572
|
+
),
|
|
573
|
+
(
|
|
574
|
+
f"systems: CodeBLEU n-gram terms ({len(progs)} programs)",
|
|
575
|
+
"codebleu",
|
|
576
|
+
lambda: [es.codebleu(refs_c, preds_c).params[k] for k in ("ngram_match", "weighted_ngram_match")],
|
|
577
|
+
None,
|
|
578
|
+
),
|
|
579
|
+
(
|
|
580
|
+
"systems: confidence AUROC (n answers)",
|
|
581
|
+
"scikit-learn",
|
|
582
|
+
lambda: [float(es.confidence_accuracy_correlation(correct, conf))],
|
|
583
|
+
None,
|
|
584
|
+
),
|
|
585
|
+
(
|
|
586
|
+
f"systems: invalid tool calls vs JSON Schema ({m} tasks)",
|
|
587
|
+
"jsonschema",
|
|
588
|
+
lambda: [float(es.invalid_tool_call_rate(trajs, tools))],
|
|
589
|
+
None,
|
|
590
|
+
),
|
|
591
|
+
("systems: distribution-shift drop, Welch CI (n scores)", "SciPy", es_shift, None),
|
|
592
|
+
(
|
|
593
|
+
"systems: latency p50 / p95 / p99 (n requests)",
|
|
594
|
+
"NumPy",
|
|
595
|
+
es_lat,
|
|
596
|
+
lambda: np.percentile(lat, [50, 95, 99]).tolist(),
|
|
597
|
+
),
|
|
598
|
+
]
|
|
599
|
+
refs: dict[str, Callable[[], Any]] = {}
|
|
600
|
+
with contextlib.suppress(ImportError):
|
|
601
|
+
from radon.metrics import mi_visit
|
|
602
|
+
|
|
603
|
+
refs[cases[0][0]] = lambda: [float(np.mean([mi_visit(p, True) for p in progs]))]
|
|
604
|
+
with contextlib.suppress(Exception):
|
|
605
|
+
from codebleu import calc_codebleu
|
|
606
|
+
|
|
607
|
+
def cb() -> list[float]:
|
|
608
|
+
r = calc_codebleu([[x] for x in refs_c], preds_c, lang="python")
|
|
609
|
+
return [r["ngram_match_score"], r["weighted_ngram_match_score"]]
|
|
610
|
+
|
|
611
|
+
cb()
|
|
612
|
+
refs[cases[1][0]] = cb
|
|
613
|
+
with contextlib.suppress(ImportError):
|
|
614
|
+
import sklearn.metrics as skm
|
|
615
|
+
|
|
616
|
+
refs[cases[2][0]] = lambda: [float(skm.roc_auc_score(correct, conf))]
|
|
617
|
+
with contextlib.suppress(ImportError):
|
|
618
|
+
import jsonschema
|
|
619
|
+
|
|
620
|
+
def js_ref() -> list[float]:
|
|
621
|
+
flags = []
|
|
622
|
+
for traj in trajs:
|
|
623
|
+
for c in traj:
|
|
624
|
+
sch = tools.get(str(c["name"]))
|
|
625
|
+
flags.append(sch is None or not jsonschema.Draft202012Validator(sch).is_valid(c["arguments"]))
|
|
626
|
+
return [float(np.mean(flags))]
|
|
627
|
+
|
|
628
|
+
refs[cases[3][0]] = js_ref
|
|
629
|
+
from scipy import stats as _st
|
|
630
|
+
|
|
631
|
+
res = _st.ttest_ind(src[:3], tgt[:3], equal_var=False)
|
|
632
|
+
if hasattr(res, "confidence_interval"):
|
|
633
|
+
refs[cases[4][0]] = lambda: list(_st.ttest_ind(src, tgt, equal_var=False).confidence_interval(0.95))
|
|
634
|
+
del _json
|
|
635
|
+
return [(name, ref, es_fn, own if own is not None else refs.get(name)) for name, ref, es_fn, own in cases]
|
|
636
|
+
|
|
637
|
+
|
|
527
638
|
_GROUPS = (
|
|
528
639
|
("Classification and regression", ("binary", "10 classes", "regression", "classification")),
|
|
529
640
|
("Clinical, calibration and statistics", ("clinical", "calibration", "decision", "statistics", "multiple")),
|
|
@@ -532,6 +643,7 @@ _GROUPS = (
|
|
|
532
643
|
(
|
|
533
644
|
"LLM systems",
|
|
534
645
|
(
|
|
646
|
+
"systems:",
|
|
535
647
|
"safety.",
|
|
536
648
|
"robustness.",
|
|
537
649
|
"uncertainty.",
|
|
@@ -765,8 +877,8 @@ def run_benchmarks(
|
|
|
765
877
|
|
|
766
878
|
if repeat < 1:
|
|
767
879
|
raise ValueError("repeat must be at least 1.")
|
|
768
|
-
if suite not in ("all", "core", "clinical", "vision", "llm", "metrics"):
|
|
769
|
-
raise ValueError("suite must be 'all', 'core', 'clinical', 'vision', 'llm' or 'metrics'.")
|
|
880
|
+
if suite not in ("all", "core", "clinical", "vision", "llm", "llmsys", "metrics"):
|
|
881
|
+
raise ValueError("suite must be 'all', 'core', 'clinical', 'vision', 'llm', 'llmsys' or 'metrics'.")
|
|
770
882
|
rng = np.random.default_rng(random_state)
|
|
771
883
|
rows: list[dict[str, Any]] = []
|
|
772
884
|
versions: dict[str, Optional[str]] = {"sklearn": None, "statsmodels": None, "pycocotools": None}
|
|
@@ -782,7 +894,8 @@ def run_benchmarks(
|
|
|
782
894
|
"clinical": [_clinical_cases],
|
|
783
895
|
"vision": [_vision_cases],
|
|
784
896
|
"llm": [_llm_cases],
|
|
785
|
-
"
|
|
897
|
+
"llmsys": [_llmsys_cases],
|
|
898
|
+
"all": [_core_cases, _clinical_cases, _vision_cases, _llm_cases, _llmsys_cases],
|
|
786
899
|
"metrics": [metric_cases],
|
|
787
900
|
}[suite]
|
|
788
901
|
for n in sizes:
|
|
@@ -14,7 +14,13 @@ from typing import TYPE_CHECKING, Any, Literal, Optional, cast
|
|
|
14
14
|
|
|
15
15
|
import numpy as np
|
|
16
16
|
from numpy.typing import NDArray
|
|
17
|
-
|
|
17
|
+
|
|
18
|
+
if TYPE_CHECKING:
|
|
19
|
+
from scipy import stats
|
|
20
|
+
else:
|
|
21
|
+
from ._lazy import lazy_module
|
|
22
|
+
|
|
23
|
+
stats = lazy_module("scipy.stats")
|
|
18
24
|
|
|
19
25
|
from .classification.metrics import (
|
|
20
26
|
_binary_target,
|
|
@@ -109,7 +109,8 @@ def accuracy(y_true: ArrayLike, y_pred: ArrayLike, *, sample_weight: Optional[Ar
|
|
|
109
109
|
|
|
110
110
|
def _accuracy(ctx: ClassificationContext) -> MetricResult:
|
|
111
111
|
if ctx.target_type == "multilabel":
|
|
112
|
-
|
|
112
|
+
if ctx.y_pred is None:
|
|
113
|
+
raise InputValidationError("y_pred is required for this metric.")
|
|
113
114
|
correct = (ctx.y_true == ctx.y_pred).all(axis=1)
|
|
114
115
|
else:
|
|
115
116
|
correct = ctx.true_idx == ctx.pred_idx
|
|
@@ -491,7 +492,8 @@ def hamming_loss(
|
|
|
491
492
|
"""Hamming loss."""
|
|
492
493
|
ctx = _ctx(y_true, y_pred, sample_weight=sample_weight)
|
|
493
494
|
if ctx.target_type == "multilabel":
|
|
494
|
-
|
|
495
|
+
if ctx.y_pred is None:
|
|
496
|
+
raise InputValidationError("y_pred is required for this metric.")
|
|
495
497
|
wrong = (ctx.y_true != ctx.y_pred).mean(axis=1)
|
|
496
498
|
else:
|
|
497
499
|
wrong = (ctx.true_idx != ctx.pred_idx).astype(float)
|
|
@@ -559,11 +559,11 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
559
559
|
p.add_argument("--repeat", type=int, default=5, help="timed repetitions per case; the fastest is reported")
|
|
560
560
|
p.add_argument(
|
|
561
561
|
"--suite",
|
|
562
|
-
choices=("all", "core", "clinical", "vision", "llm", "metrics"),
|
|
562
|
+
choices=("all", "core", "clinical", "vision", "llm", "llmsys", "metrics"),
|
|
563
563
|
default="all",
|
|
564
564
|
help="core: classification/regression; clinical: clinical, calibration, tests; vision: segmentation, "
|
|
565
|
-
"detection; llm: text, retrieval, agreement, structured output;
|
|
566
|
-
"statistics function (default all)",
|
|
565
|
+
"detection; llm: text, retrieval, agreement, structured output; llmsys: v0.5.0 LLM systems; "
|
|
566
|
+
"metrics: one row per metric and statistics function (default all)",
|
|
567
567
|
)
|
|
568
568
|
p.add_argument("--no-sklearn", action="store_true", help="time EvalSuite only (skip reference libraries)")
|
|
569
569
|
p.add_argument("--seed", type=int, default=0)
|
|
@@ -10,7 +10,13 @@ from typing import TYPE_CHECKING, Any, Optional, cast
|
|
|
10
10
|
|
|
11
11
|
import numpy as np
|
|
12
12
|
from numpy.typing import NDArray
|
|
13
|
-
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from scipy import special
|
|
16
|
+
else:
|
|
17
|
+
from .._lazy import lazy_module
|
|
18
|
+
|
|
19
|
+
special = lazy_module("scipy.special")
|
|
14
20
|
|
|
15
21
|
from ..core.exceptions import InputValidationError
|
|
16
22
|
from ..core.export import PathLike, csv_text, html_document, html_table, save_as
|
|
@@ -31,6 +31,14 @@ def seq(x: Any, name: str) -> list[Any]:
|
|
|
31
31
|
|
|
32
32
|
def bools(x: Any, name: str) -> np.ndarray:
|
|
33
33
|
"""A 1-D boolean array from booleans or 0/1 values (anything else is rejected, never coerced)."""
|
|
34
|
+
if isinstance(x, np.ndarray) and x.ndim == 1 and x.size and x.dtype.kind in "biuf":
|
|
35
|
+
if x.dtype.kind == "b":
|
|
36
|
+
return x.astype(bool, copy=False)
|
|
37
|
+
bad = ~np.isin(x, (0, 1))
|
|
38
|
+
if bad.any():
|
|
39
|
+
i = int(np.flatnonzero(bad)[0])
|
|
40
|
+
raise InputValidationError(f"{name}[{i}] must be a boolean (or 0/1); got {x[i]!r}.")
|
|
41
|
+
return x.astype(bool)
|
|
34
42
|
items = seq(x, name)
|
|
35
43
|
out = np.empty(len(items), dtype=bool)
|
|
36
44
|
for i, v in enumerate(items):
|
|
@@ -45,7 +53,7 @@ def bools(x: Any, name: str) -> np.ndarray:
|
|
|
45
53
|
|
|
46
54
|
def floats(x: Any, name: str, *, lo: Optional[float] = None, hi: Optional[float] = None) -> np.ndarray:
|
|
47
55
|
"""A finite 1-D float array, optionally bounded."""
|
|
48
|
-
items = seq(x, name)
|
|
56
|
+
items: Any = x if isinstance(x, np.ndarray) and x.size and x.dtype.kind in "biuf" else seq(x, name)
|
|
49
57
|
try:
|
|
50
58
|
a = np.asarray(items, dtype=np.float64)
|
|
51
59
|
except (TypeError, ValueError) as exc:
|