evalsuite-python 0.5.0__py3-none-any.whl → 0.5.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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)}
evalsuite/_lazy.py ADDED
@@ -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)
evalsuite/api.py CHANGED
@@ -63,7 +63,8 @@ def _classification_registry(
63
63
 
64
64
  def _hamming(ctx: ClassificationContext) -> MetricResult:
65
65
  if ctx.target_type == "multilabel":
66
- assert ctx.y_pred is not None # noqa: S101
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)
evalsuite/benchmarks.py CHANGED
@@ -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:
evalsuite/calibration.py CHANGED
@@ -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
- from scipy import stats
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
- assert ctx.y_pred is not None # noqa: S101 - ensured by context for label metrics
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
- assert ctx.y_pred is not None # noqa: S101
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)
evalsuite/cli/main.py CHANGED
@@ -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)
@@ -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
- from scipy import special
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:
evalsuite/llmsys/code.py CHANGED
@@ -40,6 +40,10 @@ __all__ = [
40
40
  ]
41
41
 
42
42
  _C = "code"
43
+
44
+ # Errors CPython raises for source it cannot parse: syntax errors, null bytes (ValueError), and input nested
45
+ # too deeply for the parser (MemoryError "parser stack overflowed") or for the compiler (RecursionError).
46
+ _PARSE_ERRORS = (SyntaxError, ValueError, MemoryError, RecursionError)
43
47
  _REF_HUMANEVAL = (
44
48
  "Chen M, Tworek J, Jun H, et al. Evaluating large language models trained on code. arXiv:2107.03374. 2021."
45
49
  )
@@ -141,7 +145,7 @@ def syntax_validity_rate(
141
145
  with warnings.catch_warnings():
142
146
  warnings.simplefilter("ignore", SyntaxWarning)
143
147
  compile(src, "<generated>", "exec", dont_inherit=True) # parses only; nothing runs
144
- except (SyntaxError, ValueError):
148
+ except _PARSE_ERRORS:
145
149
  return False
146
150
  return True
147
151
 
@@ -562,12 +566,16 @@ def code_complexity(code: Any) -> MetricResult:
562
566
  with warnings.catch_warnings():
563
567
  warnings.simplefilter("ignore", SyntaxWarning)
564
568
  tree = ast.parse(src)
569
+ visitor = _Complexity.run(tree)
570
+ v = _halstead_volume(tree)
565
571
  except SyntaxError as exc:
566
572
  raise InputValidationError(f"code[{i}] is not valid Python: {exc.msg} (line {exc.lineno}).") from exc
567
- visitor = _Complexity.run(tree)
573
+ except _PARSE_ERRORS as exc:
574
+ raise InputValidationError(
575
+ f"code[{i}] is too deeply nested to analyse ({type(exc).__name__})."
576
+ ) from exc
568
577
  blocks = visitor.blocks
569
578
  funcs += [c for _, c in blocks]
570
- v = _halstead_volume(tree)
571
579
  g = visitor.total
572
580
  lloc, sloc, comments = _raw(src)
573
581
  c_pct = comments / sloc * 100 if sloc else 0.0
@@ -720,38 +728,38 @@ def _parse(src: str) -> Optional[ast.AST]:
720
728
  with warnings.catch_warnings():
721
729
  warnings.simplefilter("ignore", SyntaxWarning)
722
730
  return ast.parse(_strip(src))
723
- except SyntaxError:
731
+ except _PARSE_ERRORS:
724
732
  return None
725
733
 
726
734
 
727
- def _sexp(node: ast.AST, memo: dict[int, str]) -> str:
728
- key = id(node)
729
- if key in memo:
730
- return memo[key]
731
- kids = [
732
- c
733
- for c in ast.iter_child_nodes(node)
734
- if not isinstance(c, (ast.expr_context, ast.operator, ast.unaryop, ast.boolop, ast.cmpop))
735
- ]
736
- tag = type(node).__name__
737
- if isinstance(node, (ast.BinOp, ast.UnaryOp, ast.BoolOp, ast.AugAssign)):
738
- tag += ":" + type(node.op).__name__
739
- s = f"({tag}{''.join(' ' + _sexp(c, memo) for c in kids)})"
740
- memo[key] = s
741
- return s
742
-
743
-
744
- def _subtrees(tree: ast.AST) -> list[str]:
745
- memo: dict[int, str] = {}
746
- out = []
747
- for node in ast.walk(tree):
748
- kids = [
749
- c
750
- for c in ast.iter_child_nodes(node)
751
- if not isinstance(c, (ast.expr_context, ast.operator, ast.unaryop, ast.boolop, ast.cmpop))
752
- ]
735
+ _SKIP_NODES = (ast.expr_context, ast.operator, ast.unaryop, ast.boolop, ast.cmpop)
736
+
737
+
738
+ def _kids(node: ast.AST) -> list[ast.AST]:
739
+ return [c for c in ast.iter_child_nodes(node) if not isinstance(c, _SKIP_NODES)]
740
+
741
+
742
+ def _subtrees(tree: ast.AST, table: dict[tuple[Any, ...], int]) -> list[int]:
743
+ """Every subtree with children (and the root), as ids that are equal exactly when the subtrees are
744
+ structurally equal. Built bottom-up without recursion: ``table`` interns (node type, child ids), so the
745
+ cost is linear in the tree size (comparing serialised subtrees would be quadratic)."""
746
+ ids: dict[int, int] = {}
747
+ out: list[int] = []
748
+ stack: list[tuple[ast.AST, bool]] = [(tree, False)]
749
+ while stack:
750
+ node, ready = stack.pop()
751
+ kids = _kids(node)
752
+ if not ready:
753
+ stack.append((node, True))
754
+ stack.extend((k, False) for k in kids)
755
+ continue
756
+ tag = type(node).__name__
757
+ if isinstance(node, (ast.BinOp, ast.UnaryOp, ast.BoolOp, ast.AugAssign)):
758
+ tag += ":" + type(node.op).__name__
759
+ sid = table.setdefault((tag, tuple(ids[id(k)] for k in kids)), len(table))
760
+ ids[id(node)] = sid
753
761
  if kids or node is tree:
754
- out.append(_sexp(node, memo))
762
+ out.append(sid)
755
763
  return out
756
764
 
757
765
 
@@ -878,15 +886,16 @@ def codebleu(
878
886
  ngram = _corpus_bleu(t_refs, t_hyps, None)
879
887
  weighted = _corpus_bleu(t_refs, t_hyps, kw)
880
888
  match = total = df_match = df_total = 0
889
+ table: dict[tuple[Any, ...], int] = {}
881
890
  for rs, p in zip(refs, preds):
882
891
  cand = _parse(p)
883
- cand_sub = set(_subtrees(cand)) if cand is not None else set()
892
+ cand_sub = set(_subtrees(cand, table)) if cand is not None else set()
884
893
  cand_flow = _normalize_flow(_dataflow(cand)) if cand is not None else []
885
894
  for r in rs:
886
895
  ref = _parse(r)
887
896
  if ref is None:
888
897
  continue
889
- ref_sub = _subtrees(ref)
898
+ ref_sub = _subtrees(ref, table)
890
899
  match += sum(s in cand_sub for s in ref_sub)
891
900
  total += len(ref_sub)
892
901
  ref_flow = _normalize_flow(_dataflow(ref))
@@ -362,7 +362,7 @@ def _luhn(digits: str) -> bool:
362
362
 
363
363
 
364
364
  PII_PATTERNS: dict[str, str] = {
365
- "email": r"[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}",
365
+ "email": r"(?<![A-Za-z0-9._%+-])[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}",
366
366
  "phone": r"(?<!\d[\s.-])(?<!\w)(?:\+?\d{1,3}[\s.-]?)?(?:\(\d{2,4}\)[\s.-]?)?\d{3,4}[\s.-]\d{3,4}"
367
367
  r"(?:[\s.-]\d{2,4})?(?![\s.-]?\d)(?!\w)",
368
368
  "credit_card": r"(?<!\d)\d(?:[ -]?\d){12,18}(?!\d)",
@@ -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",
evalsuite/stats/effect.py CHANGED
@@ -2,11 +2,17 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from typing import Literal
5
+ from typing import TYPE_CHECKING, Literal
6
6
 
7
7
  import numpy as np
8
8
  from numpy.typing import NDArray
9
- from scipy import special
9
+
10
+ if TYPE_CHECKING:
11
+ from scipy import special
12
+ else:
13
+ from .._lazy import lazy_module
14
+
15
+ special = lazy_module("scipy.special")
10
16
 
11
17
  from ..core.exceptions import InputValidationError, StatisticalTestError
12
18
  from ..core.types import ArrayLike
@@ -8,11 +8,17 @@ implementations are the reference; EvalSuite adds input validation, effect sizes
8
8
  from __future__ import annotations
9
9
 
10
10
  import math
11
- from typing import Literal
11
+ from typing import TYPE_CHECKING, Literal
12
12
 
13
13
  import numpy as np
14
14
  from numpy.typing import NDArray
15
- from scipy import stats
15
+
16
+ if TYPE_CHECKING:
17
+ from scipy import stats
18
+ else:
19
+ from .._lazy import lazy_module
20
+
21
+ stats = lazy_module("scipy.stats")
16
22
 
17
23
  from ..core.exceptions import InputValidationError, StatisticalTestError
18
24
  from ..core.types import ArrayLike
@@ -3,11 +3,18 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import warnings
6
- from typing import Any, Literal, Optional
6
+ from typing import TYPE_CHECKING, Any, Literal, Optional
7
7
 
8
8
  import numpy as np
9
9
  from numpy.typing import NDArray
10
- from scipy import special, stats
10
+
11
+ if TYPE_CHECKING:
12
+ from scipy import special, stats
13
+ else:
14
+ from .._lazy import lazy_module
15
+
16
+ special = lazy_module("scipy.special")
17
+ stats = lazy_module("scipy.stats")
11
18
 
12
19
  from ..core.exceptions import EvalSuiteError, InputValidationError, StatisticalTestError
13
20
  from ..core.types import ArrayLike
evalsuite/stats/paired.py CHANGED
@@ -2,10 +2,17 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from typing import Any, Literal, Optional
5
+ from typing import TYPE_CHECKING, Any, Literal, Optional
6
6
 
7
7
  import numpy as np
8
- from scipy import special, stats
8
+
9
+ if TYPE_CHECKING:
10
+ from scipy import special, stats
11
+ else:
12
+ from .._lazy import lazy_module
13
+
14
+ special = lazy_module("scipy.special")
15
+ stats = lazy_module("scipy.stats")
9
16
 
10
17
  from ..core.exceptions import InputValidationError
11
18
  from ..core.types import ArrayLike
evalsuite/text/judge.py CHANGED
@@ -9,10 +9,16 @@ from __future__ import annotations
9
9
 
10
10
  import math
11
11
  from collections.abc import Sequence
12
- from typing import Any, Optional
12
+ from typing import TYPE_CHECKING, Any, Optional
13
13
 
14
14
  import numpy as np
15
- from scipy import stats
15
+
16
+ if TYPE_CHECKING:
17
+ from scipy import stats
18
+ else:
19
+ from .._lazy import lazy_module
20
+
21
+ stats = lazy_module("scipy.stats")
16
22
 
17
23
  from ..core.exceptions import InputValidationError
18
24
  from ..core.registry import register
@@ -712,7 +712,11 @@ def constraint_satisfaction_rate(checks: Any) -> MetricResult:
712
712
  def format_compliance(
713
713
  outputs: Any, pattern: str, *, flags: int = re.S, average: Optional[str] = "mean"
714
714
  ) -> MetricResult:
715
- """``re.fullmatch(pattern, output.strip())`` per output."""
715
+ """``re.fullmatch(pattern, output.strip())`` per output.
716
+
717
+ The pattern is compiled with Python's backtracking ``re`` engine; patterns with nested quantifiers such as
718
+ ``(a+)+$`` can take exponential time on some outputs, so only pass patterns you trust.
719
+ """
716
720
  outs = _outputs(outputs)
717
721
  try:
718
722
  rx = re.compile(pattern, flags)
evalsuite/version.py CHANGED
@@ -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.2"
@@ -27,7 +27,13 @@ from typing import TYPE_CHECKING, Any, Literal, Optional, Union, cast
27
27
 
28
28
  import numpy as np
29
29
  from numpy.typing import NDArray
30
- from scipy import ndimage
30
+
31
+ if TYPE_CHECKING:
32
+ from scipy import ndimage
33
+ else:
34
+ from .._lazy import lazy_module
35
+
36
+ ndimage = lazy_module("scipy.ndimage")
31
37
 
32
38
  from ..core.exceptions import InputValidationError, UndefinedMetricWarning
33
39
  from ..core.export import PathLike, csv_text, html_document, html_table, save_as
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: evalsuite-python
3
- Version: 0.5.0
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.0).** Every item on the 0.1.0–0.5.0 roadmaps is implemented and verified, including SPICE.
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; 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
@@ -1,21 +1,22 @@
1
1
  evalsuite/__init__.py,sha256=nMwLJq_Ni8plZZmq1olG5HwDlBppn0pW-RJyFBzPF7o,5630
2
2
  evalsuite/__main__.py,sha256=Nq90_8VlUN1mif-AVgX-kZRL63bd94F5-F7F9UkVZD4,117
3
- evalsuite/_bench_metrics.py,sha256=lSqirR5rKSzu0Qr4KM8FGT4AtjKfPEpFqI3k0_hKwQo,75824
4
- evalsuite/api.py,sha256=TU49b4DolAm6dwU3ypJprOEzv5hJOsnRiXLwccyaGew,10170
5
- evalsuite/benchmarks.py,sha256=zZ8dWCI_kUqFY17SQ9gcckvtlf01Nzf7vlQz3xwZELk,33648
6
- evalsuite/calibration.py,sha256=sP7XO29Lk6wNp7qS5INNDtpz6SuVGGZhYUxSrTUxj94,14979
3
+ evalsuite/_bench_metrics.py,sha256=GLLa9OhCNmu6IyWkxC0AcNf7p-CTpoOwWUS99C1o2o8,75792
4
+ evalsuite/_lazy.py,sha256=Ddq0uWuiyxtsT0jBtCSITQPvttiQ7znXAbjkMIOVefk,901
5
+ evalsuite/api.py,sha256=DzWwmCgyEiqO2HvPrNg2RIDzqwap4e_UvbxA7PkJ3fE,10227
6
+ evalsuite/benchmarks.py,sha256=TWXIhdYPbdDPanCOFL1lRCo3ppRYaDF58PB-o3ZZyTA,37846
7
+ evalsuite/calibration.py,sha256=jhCPOmCFGEkUkcx-hSAIb-JCMq9oF6JZt4uDGvg5NaU,15083
7
8
  evalsuite/plot.py,sha256=7eOyzoRbg6Aad6OhtIQG3laIioQ8fs2K0YzodjG5rHA,26049
8
9
  evalsuite/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
10
  evalsuite/reporting.py,sha256=-LISv9vvEnltT26GtzLjVlxOCcAwmjv4-HyB8_1UF5o,9615
10
- evalsuite/version.py,sha256=Eww8OsmDsBUNsEa1juWDR77joR6scCsY21vQJUxBtks,98
11
+ evalsuite/version.py,sha256=XXivYAbOhDT-ji15jWK1TxpgAXnosxn_wRb7rCcTkJc,98
11
12
  evalsuite/classification/__init__.py,sha256=ErBIXbBGnhfW6v-1Cn5SegiERoyxNlmO1tEKcLM15mA,833
12
13
  evalsuite/classification/_common.py,sha256=3ANV33ix_mnOzvOAG8glyJO-iYVhhTGO0S3YG0ZddB8,4543
13
- evalsuite/classification/metrics.py,sha256=8-RPjzuFUKJYlXndrDv6kXlzRdx6aR2lHp7mzKsas2g,36969
14
+ evalsuite/classification/metrics.py,sha256=h-DYF2wwdN8LA1bPbyIgkfuG4ovwBBgtGZFObEmiHzE,37044
14
15
  evalsuite/cli/__init__.py,sha256=1x48hORLcg_Is2X08A7CPBT6VDJ1QL94zaJP1HTFxUY,96
15
- evalsuite/cli/main.py,sha256=LWy3ThWiGUHgN-3HVwd25jqEEPpqGN6edVSdAqxJUHY,23956
16
+ evalsuite/cli/main.py,sha256=ktvy_5D-KfYevKH5ZxO5f0aVFtNXbGeN8zker0tguT0,23994
16
17
  evalsuite/clinical/__init__.py,sha256=xaXLvNvTJsuP525fLb2FyVVNlopIrZczoPrwmEQIvzE,549
17
18
  evalsuite/clinical/metrics.py,sha256=Abz4rA9FfOIWn7MXAMnxAH_C3XICY9x-nPQhP1-79xU,13453
18
- evalsuite/clinical/report.py,sha256=6Pp0NDm24nccmRbicebUfUOyb37U-JtHPD48QeP-h6k,14887
19
+ evalsuite/clinical/report.py,sha256=JaS9PKjUHkiKl_AURC1TwNiDc_V-6B2KNhV4kzHZhdE,14996
19
20
  evalsuite/core/__init__.py,sha256=oDHl5Dc2GoZf1tCtJueMQ2KcmOq7zhzMZT9XF4uVC6U,93
20
21
  evalsuite/core/context.py,sha256=gvRpH3b-S2oAgAGW9W0XWnBVbuydQP3UB9rgWQNM60g,7874
21
22
  evalsuite/core/exceptions.py,sha256=9eZI_Lmgwc9fjkSUY9s3fwjAb7KoqgAlcYetrHRhbBg,1503
@@ -25,30 +26,30 @@ evalsuite/core/result.py,sha256=L_fQZRGRgVgzII2jZb_jpRBK2jy-CdO5u9s-6pUk9fE,1492
25
26
  evalsuite/core/types.py,sha256=r6bhUk7CYAcrlQzjmf-6xWQsNqO3EOipZD-e-2yAJTI,720
26
27
  evalsuite/core/validation.py,sha256=nWWyISLdW2TtzxUs3gT-Qx2uh6euDOlYMvbnCrFRYME,9975
27
28
  evalsuite/llmsys/__init__.py,sha256=NA-kVMc3hdmBSW-ufPKrgfd2NHI15wCXJRn3ZhIVFsE,4653
28
- evalsuite/llmsys/_common.py,sha256=4V6Yjs7b3_QI8eC9OR3_uKnc53O7a8s0Vi-Yyn5Zdu8,4908
29
+ evalsuite/llmsys/_common.py,sha256=zUJ4R_1fSW79HJ6QvZlSJ9-_VF0Xa7fglyq_fNFJB-U,5380
29
30
  evalsuite/llmsys/agents.py,sha256=giiHv3c179fFR6aFUp4VbBjzbdNotS941tZVG7W0UFE,15355
30
- evalsuite/llmsys/code.py,sha256=bJVpesBy9Vk_xkLBo7FoAVlDPpTRLG30BFeWIb2eJoE,36151
31
+ evalsuite/llmsys/code.py,sha256=6Im0Km5qPHLFTF4S4dnN05OucFFNRixRchDSGHqtJaQ,37004
31
32
  evalsuite/llmsys/efficiency.py,sha256=Z6ZrcoT80D3OxNi-1iKHuVe5OQiDnPJthwHEDD84cO0,13884
32
33
  evalsuite/llmsys/longcontext.py,sha256=koX58FMot3ZwUTPPiv9tM8fGvBY2ijYg0npA8pG278A,15145
33
34
  evalsuite/llmsys/multilingual.py,sha256=IDdV5O5krDQdTximeJbYB3Kmh62qeSbLL--ElM7CGvc,13726
34
35
  evalsuite/llmsys/robustness.py,sha256=OPe84s1H7IEX5GcuRVcAp_UA29tx_Qq8WlA88khY2cg,20377
35
- evalsuite/llmsys/safety.py,sha256=r21haKLDzCU193KCYUYEBMu4cuhS5mO3lDywdQtYZno,22529
36
- evalsuite/llmsys/uncertainty.py,sha256=ZIcniUdmIAOQpNxpWzM6gL7ibplH4bftxIVE9PKbLsU,9183
36
+ evalsuite/llmsys/safety.py,sha256=7Kcv9zJBhIWHS6GeAa0BedCncFOmsbIpnmUQCMvRMPU,22550
37
+ evalsuite/llmsys/uncertainty.py,sha256=Brm_LT8qHb-EtcPe7YBXq7HYMSTG93OkT3Cnx2SYBts,9367
37
38
  evalsuite/regression/__init__.py,sha256=t2XEHUw6e5tyWBHcEeiog2WWd-wwaxf4m5AIX8dJyng,571
38
39
  evalsuite/regression/metrics.py,sha256=5i3hmiFfZGfmqtFDaDOxGHRYCXzdt48-GNfgvVOX7CM,22914
39
40
  evalsuite/stats/__init__.py,sha256=mtnEqpvlvscQ3C6ceRr8xLKoSHPQWsWB24pEVlvp6fs,1187
40
41
  evalsuite/stats/_resolve.py,sha256=oauINI97AairuOB_fvlH5VlkiQnW5aIlR-bQT_zzhQ8,6119
41
42
  evalsuite/stats/compare.py,sha256=TzBYHDbfHU1Uz2FtgXbMATTnmHuaDGUbrV9Eq4IphZY,18311
42
- evalsuite/stats/effect.py,sha256=Esw1Di0BSabiErcsxoUinB9V0-ByEQ5fTLzAptf-dBo,6943
43
- evalsuite/stats/hypothesis.py,sha256=Sd3vcPUYVs_QwZ0IKKgQEFqtFcFan_CTy0EHjHEgIF4,12773
44
- evalsuite/stats/intervals.py,sha256=w2bLuFt3p-TTN_ALH1uKPmDMLcTzBBoV2YvcP-ENAOk,13709
45
- evalsuite/stats/paired.py,sha256=YK3AFndChKeCEpK512u56VtRlKvhlGdkFLHHYe77zmc,8244
43
+ evalsuite/stats/effect.py,sha256=PCzpj_Lf1CdBAJdL8DoL0gEsNVVcwk-gjmlRyrcGAPM,7067
44
+ evalsuite/stats/hypothesis.py,sha256=n-8bUYWwur8ZElhDqRksA8MK09vLuwYd90Vzv1gI9hY,12893
45
+ evalsuite/stats/intervals.py,sha256=0lW87qZsejY8r2xXw0agWasudOGq-rEU1v-9u8OuiGA,13872
46
+ evalsuite/stats/paired.py,sha256=hF4qBAxdYOrKD8JnJJOa9A_Xn8xw08qpMkworXssUuU,8407
46
47
  evalsuite/stats/results.py,sha256=nqXHUiW8l0fQJkbMiunU6Gz-2be1NApZ6Fif2B1K_Yw,4407
47
48
  evalsuite/text/__init__.py,sha256=_r0Qi6X7cBcaYMAPRyC15CsTj9Tv8VcqMCtLiSJPogY,3632
48
49
  evalsuite/text/_common.py,sha256=NeJroFlIFu_1HXNHgnTVZEyaeVScX-o5C0Qbfe3INe8,3659
49
50
  evalsuite/text/factuality.py,sha256=Id8LnGjqvaVDYC5qMn4H0UJdjrwp1VrZ_zr_0jct8SM,20772
50
51
  evalsuite/text/generation.py,sha256=6eHWccspuQeaup8qP8Pv3k2nx6IejG9UECYHwDlWuHE,42572
51
- evalsuite/text/judge.py,sha256=AtKZtvrfxeheGAiFOt74IwamR6_XV67uPHOyimAv2jw,22542
52
+ evalsuite/text/judge.py,sha256=ugqsaVih_n_h0jO5GrvIGHqq7XjqGnP7cKd4QbObEw0,22662
52
53
  evalsuite/text/qa.py,sha256=rU0ya8TzV5mp7UiWq7w9IqSN9B0tDv8xymSaRzzbzMw,4097
53
54
  evalsuite/text/rag.py,sha256=GF8GUp6Vnt9peBCIcdvFUapLUL36_NDApZisYG_rcqg,9641
54
55
  evalsuite/text/reasoning.py,sha256=ydaTgAY--Tz_zEXm-Q_jLxJjTLnPgRlv39OzXWXD9yA,8885
@@ -56,12 +57,12 @@ evalsuite/text/report.py,sha256=nUAgt24rcQsnPLiNTVn3PlzZO3Y7fW_ch_lSelgfEc8,6241
56
57
  evalsuite/text/retrieval.py,sha256=otQvOTbbR79kYtMum6o2HOT9UoDlSj0-JYLsFddkV1k,8969
57
58
  evalsuite/text/semantic.py,sha256=V1Wc4bvcli0_6ZlA58guTGA65zGoCFMthbPeCh_IDjc,21408
58
59
  evalsuite/text/spice.py,sha256=rdPCeilqKkXLMiSAE-rYOn0Zt2nKFum5Tf1oJdE6k-U,6847
59
- evalsuite/text/structured.py,sha256=mNpTGKJUWXewV-fiQ3TuCnLXjss9MeduTXpyCNbRivk,31773
60
+ evalsuite/text/structured.py,sha256=x08yYf2rdlmiA_D_dI1L-fc9quHiBPMKgAGQbG3kJr8,31981
60
61
  evalsuite/vision/__init__.py,sha256=9gpOBTPveM_srE6BM-SgCssdQurya2LPHhoMLGOh2A0,991
61
62
  evalsuite/vision/detection.py,sha256=eUnt5iFUVLEoUl1RB51q-vfr5juDCD3YH8ttLFrqxFg,27781
62
- evalsuite/vision/segmentation.py,sha256=7h0AirBU0G6XMWzdA7MQNO52hXz8oboNAX9s55y6SVQ,38647
63
- evalsuite_python-0.5.0.dist-info/METADATA,sha256=YKZIpS-l_rI2YePmzDH2LwRq5kK9tzQtfBm1JeVUE94,24620
64
- evalsuite_python-0.5.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
65
- evalsuite_python-0.5.0.dist-info/entry_points.txt,sha256=YYIwisn4L4sQt3ntzbM0_Z4yZE2KCu3GJ6KfWL1kIwc,54
66
- evalsuite_python-0.5.0.dist-info/licenses/LICENSE,sha256=cRRQ6Kk8w2qCqtDSSUJQGy-0rqjmjJG09cZ6iIiMvdc,1095
67
- evalsuite_python-0.5.0.dist-info/RECORD,,
63
+ evalsuite/vision/segmentation.py,sha256=k8uGUsIrJByVygguFsiY3CsrLNhL5MZJ4xDEy6jcgjE,38756
64
+ evalsuite_python-0.5.2.dist-info/METADATA,sha256=5bTTH1ciXStnesbV5utt8-XpEPBikZn7kQB190hyXKM,25181
65
+ evalsuite_python-0.5.2.dist-info/WHEEL,sha256=2iZrJkDIkeDh0k9SikKMqFuRP-ua6mb4Ir1AsER4BEw,87
66
+ evalsuite_python-0.5.2.dist-info/entry_points.txt,sha256=YYIwisn4L4sQt3ntzbM0_Z4yZE2KCu3GJ6KfWL1kIwc,54
67
+ evalsuite_python-0.5.2.dist-info/licenses/LICENSE,sha256=cRRQ6Kk8w2qCqtDSSUJQGy-0rqjmjJG09cZ6iIiMvdc,1095
68
+ evalsuite_python-0.5.2.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.32.4
2
+ Generator: hatchling 1.33.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any