File size: 3,160 Bytes
fc329a3 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 | """Set size metrics."""
import numpy as np
def mean_radius(radius: np.ndarray) -> float:
return radius.mean()
def radius_by_strata(
radius: np.ndarray,
strata: np.ndarray,
) -> dict[int, float]:
result = {}
for s in np.unique(strata):
mask = strata == s
if mask.sum() > 0:
result[int(s)] = radius[mask].mean()
return result
def _distance_to_center(
samples: np.ndarray,
center: np.ndarray,
score: str,
) -> np.ndarray:
if score == "aitchison":
from src.utils.simplex import aitchison_dist
tiled_center = np.repeat(center[None, :], len(samples), axis=0)
return aitchison_dist(samples, tiled_center)
if score in {"tv", "total_variation", "l1"}:
return 0.5 * np.sum(np.abs(samples - center[None, :]), axis=1)
raise ValueError(f"Unsupported score for volume ratio: {score}")
def volume_ratio(
center: np.ndarray,
radius: float,
*,
score: str = "aitchison",
n_mc: int = 20000,
rng: np.random.Generator | None = None,
) -> float:
"""Monte Carlo estimate of simplex volume covered by the score ball.
The estimate is the fraction of uniformly sampled simplex points whose
distance to `center` is at most `radius`.
"""
center = np.asarray(center, dtype=float)
if rng is None:
rng = np.random.default_rng(0)
samples = rng.dirichlet(np.ones(center.shape[-1]), size=n_mc)
d = _distance_to_center(samples, center, score)
return float(np.mean(d <= radius))
def mean_volume_ratio(
centers: np.ndarray,
radius: np.ndarray,
*,
score: str = "aitchison",
n_mc: int = 20000,
max_points: int | None = None,
rng: np.random.Generator | None = None,
) -> float:
"""Average Monte Carlo simplex-volume ratio across test points."""
centers = np.asarray(centers, dtype=float)
radius = np.asarray(radius, dtype=float)
if rng is None:
rng = np.random.default_rng(0)
idx = np.arange(len(centers))
if max_points is not None and len(idx) > max_points:
idx = rng.choice(idx, size=max_points, replace=False)
vals = [
volume_ratio(centers[i], radius[i], score=score, n_mc=n_mc, rng=rng)
for i in idx
]
return float(np.mean(vals))
def volume_ratio_by_strata(
centers: np.ndarray,
radius: np.ndarray,
strata: np.ndarray,
*,
score: str = "aitchison",
n_mc: int = 20000,
max_points: int | None = None,
rng: np.random.Generator | None = None,
) -> dict[int, float]:
"""Average simplex-volume ratio per prediction-space stratum."""
centers = np.asarray(centers, dtype=float)
radius = np.asarray(radius, dtype=float)
strata = np.asarray(strata)
if rng is None:
rng = np.random.default_rng(0)
result = {}
for s in np.unique(strata):
mask = strata == s
centers_s = centers[mask]
radius_s = radius[mask]
result[int(s)] = mean_volume_ratio(
centers_s,
radius_s,
score=score,
n_mc=n_mc,
max_points=max_points,
rng=rng,
)
return result
|