Source code for quantum_mind.evaluation.metrics

"""Model-agnostic probabilistic evaluation metrics."""
from __future__ import annotations
import numpy as np

def _validate_binary(y_true, y_prob):
    y, p = np.asarray(y_true, float), np.asarray(y_prob, float)
    if y.ndim != 1 or p.ndim != 1 or len(y) != len(p):
        raise ValueError("y_true and y_prob must be one-dimensional arrays of equal length")
    if not np.all(np.isin(y, (0.0, 1.0))):
        raise ValueError("y_true must contain only 0 and 1")
    if not np.all(np.isfinite(p)) or np.any((p < 0) | (p > 1)):
        raise ValueError("y_prob must contain finite probabilities in [0, 1]")
    if not len(y):
        raise ValueError("at least one observation is required")
    return y, np.clip(p, 1e-15, 1 - 1e-15)

[docs] def log_loss(y_true, y_prob): """Mean binary log loss; lower is better.""" y, p = _validate_binary(y_true, y_prob) return float(-np.mean(y*np.log(p) + (1-y)*np.log1p(-p)))
[docs] def brier_score(y_true, y_prob): """Binary Brier score; lower is better.""" y, p = _validate_binary(y_true, y_prob) return float(np.mean((p-y)**2))
[docs] def expected_calibration_error(y_true, y_prob, bins=10): """Equal-width expected calibration error; lower is better.""" if bins < 1: raise ValueError("bins must be positive") y, p = _validate_binary(y_true, y_prob) edges = np.linspace(0, 1, bins+1) return float(sum(np.mean(mask)*abs(np.mean(y[mask])-np.mean(p[mask])) for i in range(bins) for mask in [(p >= edges[i]) & ((p < edges[i+1]) if i < bins-1 else (p <= edges[i+1]))] if np.any(mask)))
[docs] def bootstrap_ci(values, statistic=np.mean, confidence=0.95, n_boot=2000, rng=None): """Percentile bootstrap confidence interval for a statistic.""" x = np.asarray(values, float) if x.ndim != 1 or len(x) < 2 or not np.all(np.isfinite(x)): raise ValueError("values must be a finite one-dimensional array with at least two entries") if not 0 < confidence < 1 or n_boot < 100: raise ValueError("confidence must be in (0, 1) and n_boot must be at least 100") rng = np.random.default_rng(rng) samples = rng.integers(0, len(x), size=(n_boot, len(x))) estimates = np.asarray([statistic(x[idx]) for idx in samples]) alpha = (1-confidence)/2 return float(statistic(x)), float(np.quantile(estimates, alpha)), float(np.quantile(estimates, 1-alpha))
[docs] def classification_report(y_true, y_prob, bins=10): """Return a JSON-friendly probabilistic evaluation summary.""" y, p = _validate_binary(y_true, y_prob) return {"n": int(len(y)), "log_loss": log_loss(y,p), "brier": brier_score(y,p), "ece": expected_calibration_error(y,p,bins), "accuracy_at_0.5": float(np.mean((p >= .5) == y))}