evalsuite-python 0.5.0__tar.gz → 0.5.1__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.
Files changed (108) hide show
  1. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/CHANGELOG.md +11 -0
  2. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/PKG-INFO +34 -28
  3. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/README.md +33 -27
  4. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/pyproject.toml +1 -1
  5. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/src/evalsuite/_bench_metrics.py +1 -1
  6. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/src/evalsuite/benchmarks.py +116 -3
  7. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/src/evalsuite/cli/main.py +3 -3
  8. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/src/evalsuite/llmsys/_common.py +9 -1
  9. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/src/evalsuite/llmsys/uncertainty.py +6 -2
  10. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/src/evalsuite/version.py +1 -1
  11. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/tests/output/test_benchmarks.py +11 -2
  12. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/.gitignore +0 -0
  13. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/CONTRIBUTING.md +0 -0
  14. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/LICENSE +0 -0
  15. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/src/evalsuite/__init__.py +0 -0
  16. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/src/evalsuite/__main__.py +0 -0
  17. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/src/evalsuite/api.py +0 -0
  18. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/src/evalsuite/calibration.py +0 -0
  19. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/src/evalsuite/classification/__init__.py +0 -0
  20. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/src/evalsuite/classification/_common.py +0 -0
  21. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/src/evalsuite/classification/metrics.py +0 -0
  22. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/src/evalsuite/cli/__init__.py +0 -0
  23. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/src/evalsuite/clinical/__init__.py +0 -0
  24. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/src/evalsuite/clinical/metrics.py +0 -0
  25. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/src/evalsuite/clinical/report.py +0 -0
  26. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/src/evalsuite/core/__init__.py +0 -0
  27. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/src/evalsuite/core/context.py +0 -0
  28. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/src/evalsuite/core/exceptions.py +0 -0
  29. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/src/evalsuite/core/export.py +0 -0
  30. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/src/evalsuite/core/registry.py +0 -0
  31. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/src/evalsuite/core/result.py +0 -0
  32. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/src/evalsuite/core/types.py +0 -0
  33. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/src/evalsuite/core/validation.py +0 -0
  34. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/src/evalsuite/llmsys/__init__.py +0 -0
  35. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/src/evalsuite/llmsys/agents.py +0 -0
  36. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/src/evalsuite/llmsys/code.py +0 -0
  37. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/src/evalsuite/llmsys/efficiency.py +0 -0
  38. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/src/evalsuite/llmsys/longcontext.py +0 -0
  39. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/src/evalsuite/llmsys/multilingual.py +0 -0
  40. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/src/evalsuite/llmsys/robustness.py +0 -0
  41. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/src/evalsuite/llmsys/safety.py +0 -0
  42. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/src/evalsuite/plot.py +0 -0
  43. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/src/evalsuite/py.typed +0 -0
  44. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/src/evalsuite/regression/__init__.py +0 -0
  45. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/src/evalsuite/regression/metrics.py +0 -0
  46. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/src/evalsuite/reporting.py +0 -0
  47. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/src/evalsuite/stats/__init__.py +0 -0
  48. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/src/evalsuite/stats/_resolve.py +0 -0
  49. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/src/evalsuite/stats/compare.py +0 -0
  50. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/src/evalsuite/stats/effect.py +0 -0
  51. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/src/evalsuite/stats/hypothesis.py +0 -0
  52. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/src/evalsuite/stats/intervals.py +0 -0
  53. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/src/evalsuite/stats/paired.py +0 -0
  54. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/src/evalsuite/stats/results.py +0 -0
  55. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/src/evalsuite/text/__init__.py +0 -0
  56. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/src/evalsuite/text/_common.py +0 -0
  57. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/src/evalsuite/text/factuality.py +0 -0
  58. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/src/evalsuite/text/generation.py +0 -0
  59. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/src/evalsuite/text/judge.py +0 -0
  60. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/src/evalsuite/text/qa.py +0 -0
  61. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/src/evalsuite/text/rag.py +0 -0
  62. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/src/evalsuite/text/reasoning.py +0 -0
  63. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/src/evalsuite/text/report.py +0 -0
  64. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/src/evalsuite/text/retrieval.py +0 -0
  65. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/src/evalsuite/text/semantic.py +0 -0
  66. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/src/evalsuite/text/spice.py +0 -0
  67. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/src/evalsuite/text/structured.py +0 -0
  68. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/src/evalsuite/vision/__init__.py +0 -0
  69. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/src/evalsuite/vision/detection.py +0 -0
  70. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/src/evalsuite/vision/segmentation.py +0 -0
  71. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/tests/__init__.py +0 -0
  72. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/tests/classification/__init__.py +0 -0
  73. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/tests/classification/test_against_sklearn.py +0 -0
  74. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/tests/clinical/__init__.py +0 -0
  75. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/tests/clinical/test_outputs.py +0 -0
  76. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/tests/clinical/test_v020.py +0 -0
  77. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/tests/conftest.py +0 -0
  78. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/tests/integration/__init__.py +0 -0
  79. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/tests/integration/test_qa.py +0 -0
  80. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/tests/integration/test_readme_examples.py +0 -0
  81. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/tests/integration/test_repo_hygiene.py +0 -0
  82. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/tests/llmsys/__init__.py +0 -0
  83. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/tests/llmsys/test_code_longcontext_efficiency.py +0 -0
  84. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/tests/llmsys/test_edges.py +0 -0
  85. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/tests/llmsys/test_safety_robustness.py +0 -0
  86. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/tests/llmsys/test_uncertainty_agents_multilingual.py +0 -0
  87. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/tests/output/__init__.py +0 -0
  88. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/tests/output/test_cli.py +0 -0
  89. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/tests/output/test_plot.py +0 -0
  90. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/tests/output/test_reporting.py +0 -0
  91. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/tests/regression/__init__.py +0 -0
  92. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/tests/regression/test_against_sklearn.py +0 -0
  93. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/tests/stats/__init__.py +0 -0
  94. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/tests/stats/test_branches.py +0 -0
  95. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/tests/stats/test_compare.py +0 -0
  96. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/tests/stats/test_reference.py +0 -0
  97. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/tests/text/__init__.py +0 -0
  98. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/tests/text/test_against_references.py +0 -0
  99. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/tests/text/test_text_edges.py +0 -0
  100. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/tests/text/test_v040.py +0 -0
  101. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/tests/unit/__init__.py +0 -0
  102. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/tests/unit/test_core.py +0 -0
  103. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/tests/unit/test_edges.py +0 -0
  104. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/tests/vision/__init__.py +0 -0
  105. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/tests/vision/test_detection.py +0 -0
  106. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/tests/vision/test_edges.py +0 -0
  107. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/tests/vision/test_outputs.py +0 -0
  108. {evalsuite_python-0.5.0 → evalsuite_python-0.5.1}/tests/vision/test_segmentation.py +0 -0
@@ -6,6 +6,17 @@ All notable changes to this project are documented here. The format follows
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.5.1] - 2026-10-10
10
+
11
+ ### Added
12
+ - `evalsuite benchmark --suite llmsys` (and `run_benchmarks(suite="llmsys")`): v0.5 LLM-systems workloads
13
+ against radon, the codebleu package, scikit-learn, jsonschema, SciPy and NumPy; included in `--suite all`
14
+ (27 cases, 81 measurements, all agreeing with their reference).
15
+
16
+ ### Changed
17
+ - LLM-systems metrics accept NumPy arrays without converting them to Python lists first (much faster on
18
+ large inputs); `confidence_accuracy_correlation` computes Spearman from the ranks it already has.
19
+
9
20
  ## [0.5.0] - 2026-10-10
10
21
 
11
22
  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.0
3
+ Version: 0.5.1
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.0).** Every item on the 0.1.0–0.5.0 roadmaps is implemented and verified, including SPICE.
86
+ > **Status: stable (0.5.1).** 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; Python 3.12, NumPy 2.5,
343
- Linux x86_64). **Overall: 63 of 63 rows agree with the reference** (largest
344
- difference 1.8e-14), 42 are faster, and the geometric-mean speed-up across all
345
- 21 cases and three sizes is **2.12×**. At 1,000,000 samples (pixels for
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 | 23.8 | 135.5 | **5.7×** |
352
- | agreement: Krippendorff's alpha, interval (4 raters × 10000 items) | krippendorff | 3.5 | 3.4 | 0.96× |
353
- | binary: 8 label metrics via evaluate() | scikit-learn | 28.5 | 1023.8 | **36.0×** |
354
- | binary: ROC AUC | scikit-learn | 187.4 | 327.3 | **1.7×** |
355
- | calibration: slope and intercept | statsmodels | 205.7 | 1173.7 | **5.7×** |
356
- | clinical: diagnostic report (7 CIs) | statsmodels | 19.6 | 4.9 | 0.25× |
357
- | clinical: sensitivity, specificity, LR+, LR− | scikit-learn | 75.2 | 411.5 | **5.5×** |
358
- | decision curve: 99 thresholds | NumPy loop | 165.0 | 201.9 | **1.2×** |
359
- | detection: COCO evaluation (1000 images) | pycocotools | 838.7 | 848.6 | **1.0×** |
360
- | multiple testing: Hochberg (n p-values) | statsmodels | 72.1 | 79.0 | **1.1×** |
361
- | regression: MAE, MSE, RMSE, R² via evaluate() | scikit-learn | 22.9 | 10.6 | 0.47× |
362
- | retrieval: MRR, MAP@20, NDCG@10 (10000 queries) | ranx | 170.4 | 861.0 | **5.1×** |
363
- | segmentation: Dice and IoU per class (n = pixels) | scikit-learn | 34.3 | 256.0 | **7.5×** |
364
- | segmentation: Hausdorff distance (50 images) | SciPy | 24.2 | 19.8 | 0.82× |
365
- | statistics: Cramér's V (5×5 table) | SciPy | 0.2 | 0.2 | **1.0×** |
366
- | statistics: Mann–Whitney U | SciPy | 353.5 | 340.6 | 0.96× |
367
- | statistics: Welch t-test | SciPy | 16.1 | 8.3 | 0.52× |
368
- | structured: JSON Schema compliance (10000 documents) | jsonschema | 144.6 | 330.5 | **2.3×** |
369
- | text: corpus BLEU and chrF (10000 sentences) | sacreBLEU | 2789.8 | 3051.8 | **1.1×** |
370
- | text: METEOR, exact and stem matches (10000 sentences) | NLTK | 488.1 | 626.7 | **1.3×** |
371
- | text: ROUGE-1, ROUGE-2, ROUGE-L (10000 sentences) | rouge-score | 1290.1 | 1095.6 | 0.85× |
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.65×** (137 of 172
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.0).** Every item on the 0.1.0–0.5.0 roadmaps is implemented and verified, including SPICE.
13
+ > **Status: stable (0.5.1).** 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; Python 3.12, NumPy 2.5,
270
- Linux x86_64). **Overall: 63 of 63 rows agree with the reference** (largest
271
- difference 1.8e-14), 42 are faster, and the geometric-mean speed-up across all
272
- 21 cases and three sizes is **2.12×**. At 1,000,000 samples (pixels for
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 | 23.8 | 135.5 | **5.7×** |
279
- | agreement: Krippendorff's alpha, interval (4 raters × 10000 items) | krippendorff | 3.5 | 3.4 | 0.96× |
280
- | binary: 8 label metrics via evaluate() | scikit-learn | 28.5 | 1023.8 | **36.0×** |
281
- | binary: ROC AUC | scikit-learn | 187.4 | 327.3 | **1.7×** |
282
- | calibration: slope and intercept | statsmodels | 205.7 | 1173.7 | **5.7×** |
283
- | clinical: diagnostic report (7 CIs) | statsmodels | 19.6 | 4.9 | 0.25× |
284
- | clinical: sensitivity, specificity, LR+, LR− | scikit-learn | 75.2 | 411.5 | **5.5×** |
285
- | decision curve: 99 thresholds | NumPy loop | 165.0 | 201.9 | **1.2×** |
286
- | detection: COCO evaluation (1000 images) | pycocotools | 838.7 | 848.6 | **1.0×** |
287
- | multiple testing: Hochberg (n p-values) | statsmodels | 72.1 | 79.0 | **1.1×** |
288
- | regression: MAE, MSE, RMSE, R² via evaluate() | scikit-learn | 22.9 | 10.6 | 0.47× |
289
- | retrieval: MRR, MAP@20, NDCG@10 (10000 queries) | ranx | 170.4 | 861.0 | **5.1×** |
290
- | segmentation: Dice and IoU per class (n = pixels) | scikit-learn | 34.3 | 256.0 | **7.5×** |
291
- | segmentation: Hausdorff distance (50 images) | SciPy | 24.2 | 19.8 | 0.82× |
292
- | statistics: Cramér's V (5×5 table) | SciPy | 0.2 | 0.2 | **1.0×** |
293
- | statistics: Mann–Whitney U | SciPy | 353.5 | 340.6 | 0.96× |
294
- | statistics: Welch t-test | SciPy | 16.1 | 8.3 | 0.52× |
295
- | structured: JSON Schema compliance (10000 documents) | jsonschema | 144.6 | 330.5 | **2.3×** |
296
- | text: corpus BLEU and chrF (10000 sentences) | sacreBLEU | 2789.8 | 3051.8 | **1.1×** |
297
- | text: METEOR, exact and stem matches (10000 sentences) | NLTK | 488.1 | 626.7 | **1.3×** |
298
- | text: ROUGE-1, ROUGE-2, ROUGE-L (10000 sentences) | rouge-score | 1290.1 | 1095.6 | 0.85× |
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.65×** (137 of 172
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 # type: ignore[import-untyped]
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)}
@@ -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
- "all": [_core_cases, _clinical_cases, _vision_cases, _llm_cases],
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:
@@ -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; metrics: one row per metric and "
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)
@@ -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:
@@ -215,8 +215,12 @@ def confidence_accuracy_correlation(correct: Any, confidence: Any) -> MetricResu
215
215
  if np.ptp(p) == 0:
216
216
  pb = sp = float("nan")
217
217
  else:
218
- pb = float(stats.pearsonr(c.astype(float), p)[0])
219
- sp = float(stats.spearmanr(c.astype(float), p)[0])
218
+ cf = c.astype(float)
219
+ pb = float(np.corrcoef(cf, p)[0, 1])
220
+ # Spearman = Pearson correlation of average ranks; the ranks of p are already computed
221
+ n0 = c.size - n1
222
+ rc = np.where(c, n0 + (n1 + 1) / 2, (n0 + 1) / 2)
223
+ sp = float(np.corrcoef(rc, ranks)[0, 1])
220
224
  return MetricResult(
221
225
  "confidence_accuracy_correlation",
222
226
  "Confidence–accuracy AUROC",
@@ -1,3 +1,3 @@
1
1
  """Package version (single source of truth, read by the build backend)."""
2
2
 
3
- __version__ = "0.5.0"
3
+ __version__ = "0.5.1"
@@ -45,7 +45,7 @@ def test_v020_benchmarks_agree_with_their_references() -> None:
45
45
  for row in b.rows:
46
46
  assert row["reference_ms"] > 0 and row["max_abs_diff"] < 1e-9, row["case"]
47
47
  assert "statsmodels" in b.summary().splitlines()[0]
48
- assert len(run_benchmarks(sizes=(300,), repeat=1, suite="all").rows) == 21
48
+ assert len(run_benchmarks(sizes=(300,), repeat=1, suite="all").rows) == 27
49
49
 
50
50
 
51
51
  def test_overall_summary_and_alphabetical_rows() -> None:
@@ -55,11 +55,12 @@ def test_overall_summary_and_alphabetical_rows() -> None:
55
55
  "Classification and regression",
56
56
  "Clinical, calibration and statistics",
57
57
  "LLM evaluation",
58
+ "LLM systems",
58
59
  "Segmentation and object detection",
59
60
  "Overall",
60
61
  ]
61
62
  total = overall[-1]
62
- assert total["rows"] == 21 == sum(o["rows"] for o in overall[:-1])
63
+ assert total["rows"] == 27 == sum(o["rows"] for o in overall[:-1])
63
64
  assert total["matching"] == total["compared"] # every compared case agrees with its reference
64
65
  if total["compared"]:
65
66
  assert total["min_speedup"] <= total["geomean_speedup"] <= total["max_speedup"]
@@ -114,3 +115,11 @@ def test_cases_needing_a_missing_optional_dependency_are_skipped(monkeypatch) ->
114
115
  monkeypatch.setattr(es, "meteor", missing)
115
116
  b = benchmarks.run_benchmarks(sizes=(500,), repeat=1, suite="llm")
116
117
  assert not any("METEOR" in r["case"] for r in b.rows) and b.rows
118
+
119
+
120
+ def test_llmsys_benchmarks_agree_with_their_references() -> None:
121
+ b = run_benchmarks(sizes=(2000,), repeat=1, suite="llmsys")
122
+ assert len(b.rows) == 6
123
+ for row in b.rows:
124
+ if row["max_abs_diff"] is not None:
125
+ assert row["max_abs_diff"] < 1e-9, row["case"]