| """Size-stratified coverage metrics.""" |
| import numpy as np |
|
|
|
|
| def size_stratified_coverage( |
| covered: np.ndarray, |
| radius: np.ndarray, |
| n_bins: int = 5, |
| ) -> dict[int, float]: |
| """Coverage within radius/size bins. |
| |
| Bins are quantile-based to avoid empty tails when set sizes are skewed. |
| """ |
| covered = np.asarray(covered, dtype=bool) |
| radius = np.asarray(radius, dtype=float) |
|
|
| if len(radius) == 0: |
| return {} |
|
|
| if np.allclose(radius, radius[0]): |
| return {0: float(covered.mean())} |
|
|
| quantiles = np.linspace(0.0, 1.0, n_bins + 1) |
| edges = np.unique(np.quantile(radius, quantiles)) |
| if len(edges) <= 1: |
| return {0: float(covered.mean())} |
|
|
| bin_ids = np.digitize(radius, edges[1:-1], right=True) |
| result = {} |
| for b in np.unique(bin_ids): |
| mask = bin_ids == b |
| if mask.any(): |
| result[int(b)] = float(covered[mask].mean()) |
| return result |
|
|
|
|
| def size_stratified_coverage_violation( |
| covered: np.ndarray, |
| radius: np.ndarray, |
| alpha: float, |
| n_bins: int = 5, |
| ) -> float: |
| """Maximum coverage deviation across prediction-size bins.""" |
| sc = size_stratified_coverage(covered, radius, n_bins=n_bins) |
| target = 1.0 - alpha |
| return max(abs(v - target) for v in sc.values()) |
|
|