Function bodies 354 total
BacktestResult class · python · L113-L124 (12 LOC)src/ncaa_eval/evaluation/backtest.py
class BacktestResult:
"""Aggregated result of a full backtest across all folds.
Attributes:
fold_results: Per-fold evaluation results, sorted by year.
summary: DataFrame with year as index, metric columns + elapsed_seconds.
elapsed_seconds: Total wall-clock time for the entire backtest.
"""
fold_results: tuple[FoldResult, ...]
summary: pd.DataFrame
elapsed_seconds: float_evaluate_fold function · python · L127-L196 (70 LOC)src/ncaa_eval/evaluation/backtest.py
def _evaluate_fold(
fold: CVFold,
model: Model,
metric_fns: Mapping[
str,
Callable[[npt.NDArray[np.float64], npt.NDArray[np.float64]], float],
],
) -> FoldResult:
"""Train model on fold.train, predict on fold.test, compute metrics.
Args:
fold: A single CV fold with train/test DataFrames.
model: A deep-copied model instance (caller is responsible for copying).
metric_fns: Mapping of metric name to callable(y_true, y_prob) returning float.
Returns:
FoldResult with predictions, actuals, computed metrics, and timing.
"""
start = time.perf_counter()
if fold.test.empty:
elapsed = time.perf_counter() - start
return FoldResult(
year=fold.year,
predictions=pd.Series(dtype=np.float64),
actuals=pd.Series(dtype=np.float64),
metrics={name: float("nan") for name in metric_fns},
elapsed_seconds=elapsed,
test_game_ids=pd.Serirun_backtest function · python · L199-L316 (118 LOC)src/ncaa_eval/evaluation/backtest.py
def run_backtest( # noqa: PLR0913
model: Model,
feature_server: StatefulFeatureServer,
*,
seasons: Sequence[int],
mode: str = "batch",
n_jobs: int = -1,
metric_fns: Mapping[
str,
Callable[[npt.NDArray[np.float64], npt.NDArray[np.float64]], float],
]
| None = None,
console: Console | None = None,
progress: bool = False,
) -> BacktestResult:
"""Run parallelized walk-forward cross-validation backtest.
Args:
model: Model instance to evaluate (will be deep-copied per fold).
feature_server: Configured feature server for building CV folds.
seasons: Season years to include (passed to walk_forward_splits).
mode: Feature serving mode (``"batch"`` or ``"stateful"``).
n_jobs: Number of parallel workers. -1 = all cores, 1 = sequential.
metric_fns: Metric functions to compute per fold. Defaults to
{log_loss, brier_score, roc_auc, expected_calibration_error}.
consoReliabilityData class · python · L25-L48 (24 LOC)src/ncaa_eval/evaluation/metrics.py
class ReliabilityData:
"""Structured return type for reliability diagram data.
Attributes
----------
fraction_of_positives
Observed fraction of positives per bin (from calibration_curve).
mean_predicted_value
Mean predicted probability per bin (from calibration_curve).
bin_counts
Number of samples in each non-empty bin.
bin_edges
Full bin edge array of shape ``(n_bins + 1,)``, i.e.
``np.linspace(0.0, 1.0, n_bins + 1)``. Includes both the lower (0.0)
and upper (1.0) boundaries so callers do not need to recompute them.
n_bins
Requested number of bins.
"""
fraction_of_positives: npt.NDArray[np.float64]
mean_predicted_value: npt.NDArray[np.float64]
bin_counts: npt.NDArray[np.int64]
bin_edges: npt.NDArray[np.float64]
n_bins: int_validate_inputs function · python · L51-L67 (17 LOC)src/ncaa_eval/evaluation/metrics.py
def _validate_inputs(
y_true: npt.NDArray[np.float64],
y_prob: npt.NDArray[np.float64],
) -> None:
"""Validate metric inputs: non-empty, matching lengths, binary y_true, probs in [0, 1]."""
if len(y_true) == 0 or len(y_prob) == 0:
msg = "y_true and y_prob must be non-empty arrays."
raise ValueError(msg)
if len(y_true) != len(y_prob):
msg = f"y_true and y_prob must have the same length, " f"got {len(y_true)} and {len(y_prob)}."
raise ValueError(msg)
if not np.all((y_true == 0) | (y_true == 1)):
msg = "y_true must contain only binary values (0 or 1)."
raise ValueError(msg)
if np.any(y_prob < 0.0) or np.any(y_prob > 1.0):
msg = "y_prob values must be in [0, 1]."
raise ValueError(msg)log_loss function · python · L70-L97 (28 LOC)src/ncaa_eval/evaluation/metrics.py
def log_loss(
y_true: npt.NDArray[np.float64],
y_prob: npt.NDArray[np.float64],
) -> float:
"""Compute Log Loss (cross-entropy loss) for binary predictions.
Parameters
----------
y_true
Binary labels (0 or 1).
y_prob
Predicted probabilities for the positive class.
Returns
-------
float
Log Loss value.
Raises
------
ValueError
If inputs are empty, mismatched, or probabilities are outside [0, 1].
"""
from sklearn.metrics import log_loss as sklearn_log_loss # type: ignore[import-untyped]
_validate_inputs(y_true, y_prob)
result: float = float(sklearn_log_loss(y_true, y_prob, labels=[0, 1]))
return resultbrier_score function · python · L100-L127 (28 LOC)src/ncaa_eval/evaluation/metrics.py
def brier_score(
y_true: npt.NDArray[np.float64],
y_prob: npt.NDArray[np.float64],
) -> float:
"""Compute Brier Score for binary predictions.
Parameters
----------
y_true
Binary labels (0 or 1).
y_prob
Predicted probabilities for the positive class.
Returns
-------
float
Brier Score value (lower is better).
Raises
------
ValueError
If inputs are empty, mismatched, or probabilities are outside [0, 1].
"""
from sklearn.metrics import brier_score_loss
_validate_inputs(y_true, y_prob)
result: float = float(brier_score_loss(y_true, y_prob))
return resultAbout: code-quality intelligence by Repobility · https://repobility.com
roc_auc function · python · L130-L162 (33 LOC)src/ncaa_eval/evaluation/metrics.py
def roc_auc(
y_true: npt.NDArray[np.float64],
y_prob: npt.NDArray[np.float64],
) -> float:
"""Compute ROC-AUC for binary predictions.
Parameters
----------
y_true
Binary labels (0 or 1).
y_prob
Predicted probabilities for the positive class.
Returns
-------
float
ROC-AUC value.
Raises
------
ValueError
If inputs are empty, mismatched, probabilities are outside [0, 1],
or ``y_true`` contains only one class (AUC is undefined).
"""
from sklearn.metrics import roc_auc_score
_validate_inputs(y_true, y_prob)
unique_classes = np.unique(y_true)
if len(unique_classes) < 2:
msg = "roc_auc requires both positive and negative samples in y_true."
raise ValueError(msg)
result: float = float(roc_auc_score(y_true, y_prob))
return resultexpected_calibration_error function · python · L165-L223 (59 LOC)src/ncaa_eval/evaluation/metrics.py
def expected_calibration_error(
y_true: npt.NDArray[np.float64],
y_prob: npt.NDArray[np.float64],
*,
n_bins: int = 10,
) -> float:
"""Compute Expected Calibration Error (ECE) using vectorized numpy.
ECE measures how well predicted probabilities match observed frequencies.
Predictions are binned into ``n_bins`` equal-width bins on [0, 1], and
ECE is the weighted average of per-bin |accuracy - confidence| gaps.
Parameters
----------
y_true
Binary labels (0 or 1).
y_prob
Predicted probabilities for the positive class.
n_bins
Number of equal-width bins (default 10).
Returns
-------
float
ECE value in [0, 1] (lower is better).
Raises
------
ValueError
If inputs are empty, mismatched, or probabilities are outside [0, 1].
"""
if n_bins < 1:
msg = f"n_bins must be >= 1, got {n_bins}."
raise ValueError(msg)
_validate_inputs(y_true, y_prob)
# Bireliability_diagram_data function · python · L226-L286 (61 LOC)src/ncaa_eval/evaluation/metrics.py
def reliability_diagram_data(
y_true: npt.NDArray[np.float64],
y_prob: npt.NDArray[np.float64],
*,
n_bins: int = 10,
) -> ReliabilityData:
"""Generate reliability diagram data for calibration visualization.
Uses ``sklearn.calibration.calibration_curve`` for bin statistics and
augments with per-bin sample counts.
Parameters
----------
y_true
Binary labels (0 or 1).
y_prob
Predicted probabilities for the positive class.
n_bins
Number of bins (default 10).
Returns
-------
ReliabilityData
Structured data containing fraction of positives, mean predicted
values, bin counts, bin edges, and requested number of bins.
Raises
------
ValueError
If inputs are empty, mismatched, ``n_bins < 1``, or probabilities are
outside [0, 1].
"""
from sklearn.calibration import calibration_curve # type: ignore[import-untyped]
if n_bins < 1:
msg = f"n_bins mustplot_reliability_diagram function · python · L48-L125 (78 LOC)src/ncaa_eval/evaluation/plotting.py
def plot_reliability_diagram(
y_true: npt.NDArray[np.float64],
y_prob: npt.NDArray[np.float64],
*,
n_bins: int = 10,
title: str | None = None,
) -> go.Figure:
"""Reliability diagram: predicted vs. actual probability with bin counts.
Args:
y_true: Binary labels (0 or 1).
y_prob: Predicted probabilities for the positive class.
n_bins: Number of calibration bins (default 10).
title: Optional figure title.
Returns:
Interactive Plotly Figure with calibration curve, diagonal
reference, and bar overlay of per-bin sample counts.
"""
data = reliability_diagram_data(y_true, y_prob, n_bins=n_bins)
fig = go.Figure()
# Bar trace: bin counts on secondary y-axis
fig.add_trace(
go.Bar(
x=data.mean_predicted_value,
y=data.bin_counts,
name="Bin Count",
marker_color=COLOR_NEUTRAL,
opacity=0.3,
yaxis="y2",
)
)
plot_backtest_summary function · python · L128-L176 (49 LOC)src/ncaa_eval/evaluation/plotting.py
def plot_backtest_summary(
result: BacktestResult,
*,
metrics: Sequence[str] | None = None,
) -> go.Figure:
"""Per-year metric values from a backtest result.
Args:
result: Backtest result containing the summary DataFrame.
metrics: Metric column names to include. Defaults to all
metric columns (excludes ``elapsed_seconds``).
Returns:
Interactive Plotly Figure with one line per metric, x=year.
"""
summary = result.summary
if metrics is None:
metric_cols = [c for c in summary.columns if c != "elapsed_seconds"]
else:
metric_cols = list(metrics)
if not metric_cols:
msg = "No metric columns to plot. BacktestResult.summary has no columns besides 'elapsed_seconds'."
raise ValueError(msg)
years = summary.index.tolist()
fig = go.Figure()
for i, col in enumerate(metric_cols):
color = _PALETTE[i % len(_PALETTE)]
fig.add_trace(
go.Scatter(
plot_metric_comparison function · python · L179-L221 (43 LOC)src/ncaa_eval/evaluation/plotting.py
def plot_metric_comparison(
results: Mapping[str, BacktestResult],
metric: str,
) -> go.Figure:
"""Multi-model overlay: one line per model for a given metric across years.
Args:
results: Mapping of model name to BacktestResult.
metric: Metric column name to compare.
Returns:
Interactive Plotly Figure with one line per model.
"""
fig = go.Figure()
for i, (model_name, bt) in enumerate(results.items()):
if metric not in bt.summary.columns:
available = [c for c in bt.summary.columns if c != "elapsed_seconds"]
msg = f"metric {metric!r} not found in results[{model_name!r}].summary. Available: {available}"
raise ValueError(msg)
color = _PALETTE[i % len(_PALETTE)]
years = bt.summary.index.tolist()
values = bt.summary[metric].tolist()
fig.add_trace(
go.Scatter(
x=years,
y=values,
mode="lines+markers",
plot_advancement_heatmap function · python · L224-L272 (49 LOC)src/ncaa_eval/evaluation/plotting.py
def plot_advancement_heatmap(
result: SimulationResult,
team_labels: Mapping[int, str] | None = None,
) -> go.Figure:
"""Heatmap of per-team advancement probabilities by round.
Args:
result: Simulation result with ``advancement_probs`` array.
team_labels: Optional mapping of **team index** (0..n-1, bracket
position order) to display name. When ``None``, team indices
are shown as-is. Note: keys are bracket indices, not canonical
team IDs — use ``BracketStructure.team_index_map`` to translate
from team IDs to indices before passing this argument.
Returns:
Interactive Plotly Figure showing a heatmap with teams on
y-axis and rounds on x-axis.
"""
adv = result.advancement_probs # shape (n_teams, n_rounds)
n_teams = adv.shape[0]
n_rounds = min(adv.shape[1], N_ROUNDS)
round_labels = list(_ROUND_LABELS[:n_rounds])
if team_labels is not None:
y_labels = [teamplot_score_distribution function · python · L275-L341 (67 LOC)src/ncaa_eval/evaluation/plotting.py
def plot_score_distribution(
dist: BracketDistribution,
*,
title: str | None = None,
) -> go.Figure:
"""Histogram of bracket score distribution with percentile markers.
Args:
dist: Bracket distribution with pre-computed histogram data
and percentile values.
title: Optional figure title.
Returns:
Interactive Plotly Figure with histogram bars and vertical
percentile lines at 5th, 25th, 50th, 75th, and 95th.
"""
# Convert bin edges to bin centers for the bar chart
bin_centers = (dist.histogram_bins[:-1] + dist.histogram_bins[1:]) / 2.0
bin_width = (
float(dist.histogram_bins[1] - dist.histogram_bins[0]) if len(dist.histogram_bins) >= 2 else 1.0
)
fig = go.Figure()
# Histogram bars
fig.add_trace(
go.Bar(
x=bin_centers.tolist(),
y=dist.histogram_counts.tolist(),
width=bin_width,
marker_color=COLOR_GREEN,
opacity=0Methodology: Repobility · https://repobility.com/research/state-of-ai-code-2026/
MatchupContext class · python · L72-L87 (16 LOC)src/ncaa_eval/evaluation/simulation.py
class MatchupContext:
"""Context for a hypothetical matchup probability query.
Passed to :class:`ProbabilityProvider` so that stateless models can
construct the correct feature row for a hypothetical pairing. Stateful
models (Elo) typically ignore context and use internal ratings.
Attributes:
season: Tournament season year (e.g. 2024).
day_num: Tournament day number (e.g. 136 for Round of 64).
is_neutral: ``True`` for all tournament games (neutral site).
"""
season: int
day_num: int
is_neutral: boolBracketNode class · python · L91-L113 (23 LOC)src/ncaa_eval/evaluation/simulation.py
class BracketNode:
"""Node in a tournament bracket tree.
A leaf node represents a single team; an internal node represents a
game whose winner advances.
Attributes:
round_index: Round number (0-indexed). Leaves have ``round_index=-1``.
team_index: Index into the bracket's ``team_ids`` tuple for leaf
nodes. ``-1`` for internal nodes.
left: Left child (``None`` for leaves).
right: Right child (``None`` for leaves).
"""
round_index: int
team_index: int = -1
left: BracketNode | None = None
right: BracketNode | None = None
@property
def is_leaf(self) -> bool:
"""Return ``True`` if this is a leaf (team) node."""
return self.left is None and self.right is NoneBracketStructure class · python · L117-L130 (14 LOC)src/ncaa_eval/evaluation/simulation.py
class BracketStructure:
"""Immutable tournament bracket.
Attributes:
root: Root :class:`BracketNode` of the bracket tree.
team_ids: Tuple of team IDs in bracket-position order (leaf order).
team_index_map: Mapping of ``team_id → index`` into ``team_ids``.
seed_map: Mapping of ``team_id → seed_num`` for seed-aware scoring.
"""
root: BracketNode
team_ids: tuple[int, ...]
team_index_map: dict[int, int]
seed_map: dict[int, int] = field(default_factory=dict)_build_subtree function · python · L133-L153 (21 LOC)src/ncaa_eval/evaluation/simulation.py
def _build_subtree(
team_indices: list[int],
round_offset: int,
) -> BracketNode:
"""Recursively build a balanced binary bracket subtree.
Args:
team_indices: List of team indices for this sub-bracket (must be
power-of-2 length).
round_offset: Round index for games at this level.
Returns:
Root :class:`BracketNode` of the subtree.
"""
if len(team_indices) == 1:
return BracketNode(round_index=-1, team_index=team_indices[0])
mid = len(team_indices) // 2
left = _build_subtree(team_indices[:mid], round_offset - 1)
right = _build_subtree(team_indices[mid:], round_offset - 1)
return BracketNode(round_index=round_offset, left=left, right=right)build_bracket function · python · L156-L210 (55 LOC)src/ncaa_eval/evaluation/simulation.py
def build_bracket(seeds: list[TourneySeed], season: int) -> BracketStructure:
"""Construct a 64-team bracket tree from tournament seeds.
Play-in teams (``is_play_in=True``) are excluded. Exactly 64 non-play-in
seeds are required.
Args:
seeds: List of :class:`TourneySeed` objects for the given season.
season: Season year to filter seeds.
Returns:
Fully constructed :class:`BracketStructure`.
Raises:
ValueError: If the number of non-play-in seeds for *season* is not 64.
"""
season_seeds = [s for s in seeds if s.season == season and not s.is_play_in]
# Build lookup: (region, seed_num) → team_id
seed_lookup: dict[tuple[str, int], int] = {}
seed_num_map: dict[int, int] = {}
for s in season_seeds:
seed_lookup[(s.region, s.seed_num)] = s.team_id
seed_num_map[s.team_id] = s.seed_num
# Determine team ordering following bracket structure
team_ids_ordered: list[int] = []
for region inmatchup_probability method · python · L226-L242 (17 LOC)src/ncaa_eval/evaluation/simulation.py
def matchup_probability(
self,
team_a_id: int,
team_b_id: int,
context: MatchupContext,
) -> float:
"""Return P(team_a beats team_b).
Args:
team_a_id: First team's canonical ID.
team_b_id: Second team's canonical ID.
context: Matchup context (season, day_num, neutral).
Returns:
Probability in ``[0, 1]``.
"""
...batch_matchup_probabilities method · python · L244-L260 (17 LOC)src/ncaa_eval/evaluation/simulation.py
def batch_matchup_probabilities(
self,
team_a_ids: Sequence[int],
team_b_ids: Sequence[int],
context: MatchupContext,
) -> npt.NDArray[np.float64]:
"""Return P(a_i beats b_i) for all pairs.
Args:
team_a_ids: Sequence of first-team IDs.
team_b_ids: Sequence of second-team IDs (same length).
context: Matchup context.
Returns:
1-D float64 array of shape ``(len(team_a_ids),)``.
"""
...MatrixProvider class · python · L263-L300 (38 LOC)src/ncaa_eval/evaluation/simulation.py
class MatrixProvider:
"""Wraps a pre-computed probability matrix as a :class:`ProbabilityProvider`.
Args:
prob_matrix: n×n pairwise probability matrix.
team_ids: Sequence of team IDs matching matrix indices.
"""
def __init__(
self,
prob_matrix: npt.NDArray[np.float64],
team_ids: Sequence[int],
) -> None:
self._P = prob_matrix
self._index = {tid: i for i, tid in enumerate(team_ids)}
def matchup_probability(
self,
team_a_id: int,
team_b_id: int,
context: MatchupContext,
) -> float:
"""Return P(team_a beats team_b) from the stored matrix."""
i = self._index[team_a_id]
j = self._index[team_b_id]
return float(self._P[i, j])
def batch_matchup_probabilities(
self,
team_a_ids: Sequence[int],
team_b_ids: Sequence[int],
context: MatchupContext,
) -> npt.NDArray[np.float64]:
"""Return batch probabiliSame scanner, your repo: https://repobility.com — Repobility
__init__ method · python · L271-L277 (7 LOC)src/ncaa_eval/evaluation/simulation.py
def __init__(
self,
prob_matrix: npt.NDArray[np.float64],
team_ids: Sequence[int],
) -> None:
self._P = prob_matrix
self._index = {tid: i for i, tid in enumerate(team_ids)}matchup_probability method · python · L279-L288 (10 LOC)src/ncaa_eval/evaluation/simulation.py
def matchup_probability(
self,
team_a_id: int,
team_b_id: int,
context: MatchupContext,
) -> float:
"""Return P(team_a beats team_b) from the stored matrix."""
i = self._index[team_a_id]
j = self._index[team_b_id]
return float(self._P[i, j])batch_matchup_probabilities method · python · L290-L300 (11 LOC)src/ncaa_eval/evaluation/simulation.py
def batch_matchup_probabilities(
self,
team_a_ids: Sequence[int],
team_b_ids: Sequence[int],
context: MatchupContext,
) -> npt.NDArray[np.float64]:
"""Return batch probabilities from the stored matrix."""
rows = np.array([self._index[a] for a in team_a_ids])
cols = np.array([self._index[b] for b in team_b_ids])
result: npt.NDArray[np.float64] = self._P[rows, cols].astype(np.float64)
return resultEloProvider class · python · L303-L341 (39 LOC)src/ncaa_eval/evaluation/simulation.py
class EloProvider:
"""Wraps a :class:`StatefulModel` as a :class:`ProbabilityProvider`.
Uses the model's ``_predict_one`` method for probability computation.
Args:
model: Any :class:`StatefulModel` instance with ``_predict_one``.
"""
def __init__(self, model: Any) -> None:
if not hasattr(model, "_predict_one"):
msg = "model must have a _predict_one(team_a_id, team_b_id) method"
raise TypeError(msg)
self._model: Any = model
def matchup_probability(
self,
team_a_id: int,
team_b_id: int,
context: MatchupContext,
) -> float:
"""Return P(team_a beats team_b) via the model's ``_predict_one``."""
result: float = self._model._predict_one(team_a_id, team_b_id)
return result
def batch_matchup_probabilities(
self,
team_a_ids: Sequence[int],
team_b_ids: Sequence[int],
context: MatchupContext,
) -> npt.NDArray[np.float64]:
__init__ method · python · L312-L316 (5 LOC)src/ncaa_eval/evaluation/simulation.py
def __init__(self, model: Any) -> None:
if not hasattr(model, "_predict_one"):
msg = "model must have a _predict_one(team_a_id, team_b_id) method"
raise TypeError(msg)
self._model: Any = modelmatchup_probability method · python · L318-L326 (9 LOC)src/ncaa_eval/evaluation/simulation.py
def matchup_probability(
self,
team_a_id: int,
team_b_id: int,
context: MatchupContext,
) -> float:
"""Return P(team_a beats team_b) via the model's ``_predict_one``."""
result: float = self._model._predict_one(team_a_id, team_b_id)
return resultbatch_matchup_probabilities method · python · L328-L341 (14 LOC)src/ncaa_eval/evaluation/simulation.py
def batch_matchup_probabilities(
self,
team_a_ids: Sequence[int],
team_b_ids: Sequence[int],
context: MatchupContext,
) -> npt.NDArray[np.float64]:
"""Return batch probabilities by looping ``_predict_one``.
Elo is O(1) per pair so looping is acceptable.
"""
return np.array(
[self._model._predict_one(a, b) for a, b in zip(team_a_ids, team_b_ids)],
dtype=np.float64,
)build_probability_matrix function · python · L344-L372 (29 LOC)src/ncaa_eval/evaluation/simulation.py
def build_probability_matrix(
provider: ProbabilityProvider,
team_ids: Sequence[int],
context: MatchupContext,
) -> npt.NDArray[np.float64]:
"""Build n×n pairwise win probability matrix.
Uses upper-triangle batch call, then fills ``P[j,i] = 1 - P[i,j]``
via the complementarity contract.
Args:
provider: Probability provider implementing the protocol.
team_ids: Team IDs in bracket order.
context: Matchup context.
Returns:
Float64 array of shape ``(n, n)``. Diagonal is zero.
"""
n = len(team_ids)
rows, cols = np.triu_indices(n, k=1)
a_ids = [team_ids[int(i)] for i in rows]
b_ids = [team_ids[int(j)] for j in cols]
probs = provider.batch_matchup_probabilities(a_ids, b_ids, context)
P = np.zeros((n, n), dtype=np.float64)
P[rows, cols] = probs
P[cols, rows] = 1.0 - probs
return PRepobility · code-quality intelligence platform · https://repobility.com
points_per_round method · python · L389-L398 (10 LOC)src/ncaa_eval/evaluation/simulation.py
def points_per_round(self, round_idx: int) -> float:
"""Return points awarded for a correct pick in round *round_idx*.
Args:
round_idx: Zero-indexed round number (0=R64 through 5=NCG).
Returns:
Points as a float.
"""
...register_scoring function · python · L414-L434 (21 LOC)src/ncaa_eval/evaluation/simulation.py
def register_scoring(name: str) -> Callable[[_ST], _ST]:
"""Class decorator that registers a scoring rule class.
Args:
name: Registry key for the scoring rule.
Returns:
Decorator that registers the class and returns it unchanged.
Raises:
ValueError: If *name* is already registered.
"""
def decorator(cls: _ST) -> _ST:
if name in _SCORING_REGISTRY:
msg = f"Scoring name {name!r} is already registered to {_SCORING_REGISTRY[name].__name__}"
raise ValueError(msg)
_SCORING_REGISTRY[name] = cls
return cls
return decoratorget_scoring function · python · L437-L447 (11 LOC)src/ncaa_eval/evaluation/simulation.py
def get_scoring(name: str) -> type:
"""Return the scoring class registered under *name*.
Raises:
ScoringNotFoundError: If *name* is not registered.
"""
try:
return _SCORING_REGISTRY[name]
except KeyError:
msg = f"No scoring registered with name {name!r}. Available: {list_scorings()}"
raise ScoringNotFoundError(msg) from Nonelist_scorings function · python · L450-L452 (3 LOC)src/ncaa_eval/evaluation/simulation.py
def list_scorings() -> list[str]:
"""Return all registered scoring names (sorted)."""
return sorted(_SCORING_REGISTRY)StandardScoring class · python · L461-L473 (13 LOC)src/ncaa_eval/evaluation/simulation.py
class StandardScoring:
"""ESPN-style scoring: 1-2-4-8-16-32 (192 total for perfect bracket)."""
_POINTS: tuple[float, ...] = (1.0, 2.0, 4.0, 8.0, 16.0, 32.0)
@property
def name(self) -> str:
"""Return ``'standard'``."""
return "standard"
def points_per_round(self, round_idx: int) -> float:
"""Return standard scoring points for *round_idx*."""
return self._POINTS[round_idx]points_per_round method · python · L471-L473 (3 LOC)src/ncaa_eval/evaluation/simulation.py
def points_per_round(self, round_idx: int) -> float:
"""Return standard scoring points for *round_idx*."""
return self._POINTS[round_idx]FibonacciScoring class · python · L477-L489 (13 LOC)src/ncaa_eval/evaluation/simulation.py
class FibonacciScoring:
"""Fibonacci-style scoring: 2-3-5-8-13-21 (231 total for perfect bracket)."""
_POINTS: tuple[float, ...] = (2.0, 3.0, 5.0, 8.0, 13.0, 21.0)
@property
def name(self) -> str:
"""Return ``'fibonacci'``."""
return "fibonacci"
def points_per_round(self, round_idx: int) -> float:
"""Return Fibonacci scoring points for *round_idx*."""
return self._POINTS[round_idx]points_per_round method · python · L487-L489 (3 LOC)src/ncaa_eval/evaluation/simulation.py
def points_per_round(self, round_idx: int) -> float:
"""Return Fibonacci scoring points for *round_idx*."""
return self._POINTS[round_idx]About: code-quality intelligence by Repobility · https://repobility.com
SeedDiffBonusScoring class · python · L493-L540 (48 LOC)src/ncaa_eval/evaluation/simulation.py
class SeedDiffBonusScoring:
"""Base points + seed-difference bonus when lower seed wins.
Uses same base as StandardScoring (1-2-4-8-16-32). When the lower
seed (higher seed number) wins, adds ``|seed_a - seed_b|`` bonus.
Note: This scoring rule's ``points_per_round`` returns only the base
points. Full EP computation for seed-diff scoring (which requires
per-matchup seed information) is deferred to Story 6.6, which will add
a dedicated ``compute_expected_points_seed_diff`` function.
Args:
seed_map: Mapping of ``team_id → seed_num``.
"""
_BASE_POINTS: tuple[float, ...] = (1.0, 2.0, 4.0, 8.0, 16.0, 32.0)
def __init__(self, seed_map: dict[int, int]) -> None:
self._seed_map = seed_map
@property
def name(self) -> str:
"""Return ``'seed_diff_bonus'``."""
return "seed_diff_bonus"
def points_per_round(self, round_idx: int) -> float:
"""Return base points (excludes seed-diff bonus)."""
points_per_round method · python · L518-L520 (3 LOC)src/ncaa_eval/evaluation/simulation.py
def points_per_round(self, round_idx: int) -> float:
"""Return base points (excludes seed-diff bonus)."""
return self._BASE_POINTS[round_idx]seed_diff_bonus method · python · L522-L535 (14 LOC)src/ncaa_eval/evaluation/simulation.py
def seed_diff_bonus(self, seed_a: int, seed_b: int) -> float:
"""Return bonus points when the lower seed wins.
Args:
seed_a: Winner's seed number.
seed_b: Loser's seed number.
Returns:
``|seed_a - seed_b|`` if winner has higher seed number
(lower seed = upset), else 0.
"""
if seed_a > seed_b:
return float(abs(seed_a - seed_b))
return 0.0CustomScoring class · python · L543-L562 (20 LOC)src/ncaa_eval/evaluation/simulation.py
class CustomScoring:
"""User-defined scoring rule wrapping a callable.
Args:
scoring_fn: Callable mapping ``round_idx`` → points.
scoring_name: Name for this custom rule.
"""
def __init__(self, scoring_fn: Callable[[int], float], scoring_name: str) -> None:
self._fn = scoring_fn
self._name = scoring_name
@property
def name(self) -> str:
"""Return the custom rule name."""
return self._name
def points_per_round(self, round_idx: int) -> float:
"""Return points from the wrapped callable."""
return self._fn(round_idx)__init__ method · python · L551-L553 (3 LOC)src/ncaa_eval/evaluation/simulation.py
def __init__(self, scoring_fn: Callable[[int], float], scoring_name: str) -> None:
self._fn = scoring_fn
self._name = scoring_namepoints_per_round method · python · L560-L562 (3 LOC)src/ncaa_eval/evaluation/simulation.py
def points_per_round(self, round_idx: int) -> float:
"""Return points from the wrapped callable."""
return self._fn(round_idx)DictScoring class · python · L565-L593 (29 LOC)src/ncaa_eval/evaluation/simulation.py
class DictScoring:
"""Scoring rule from a dict mapping round_idx to points.
Args:
points: Mapping of ``round_idx → points`` for rounds 0–5.
scoring_name: Name for this rule.
Raises:
ValueError: If *points* does not contain exactly 6 entries (rounds 0–5).
"""
def __init__(self, points: dict[int, float], scoring_name: str) -> None:
if len(points) != N_ROUNDS:
msg = f"DictScoring requires exactly 6 entries (rounds 0–5), got {len(points)}"
raise ValueError(msg)
if set(points) != set(range(N_ROUNDS)):
msg = f"DictScoring requires keys 0–5, got {sorted(points.keys())}"
raise ValueError(msg)
self._points = points
self._name = scoring_name
@property
def name(self) -> str:
"""Return the rule name."""
return self._name
def points_per_round(self, round_idx: int) -> float:
"""Return points for *round_idx*."""
return self._point__init__ method · python · L576-L584 (9 LOC)src/ncaa_eval/evaluation/simulation.py
def __init__(self, points: dict[int, float], scoring_name: str) -> None:
if len(points) != N_ROUNDS:
msg = f"DictScoring requires exactly 6 entries (rounds 0–5), got {len(points)}"
raise ValueError(msg)
if set(points) != set(range(N_ROUNDS)):
msg = f"DictScoring requires keys 0–5, got {sorted(points.keys())}"
raise ValueError(msg)
self._points = points
self._name = scoring_nameMethodology: Repobility · https://repobility.com/research/state-of-ai-code-2026/
points_per_round method · python · L591-L593 (3 LOC)src/ncaa_eval/evaluation/simulation.py
def points_per_round(self, round_idx: int) -> float:
"""Return points for *round_idx*."""
return self._points[round_idx]scoring_from_config function · python · L596-L640 (45 LOC)src/ncaa_eval/evaluation/simulation.py
def scoring_from_config(config: dict[str, Any]) -> ScoringRule:
"""Create a scoring rule from a configuration dict.
Dispatches on ``config["type"]``:
* ``"standard"`` → :class:`StandardScoring`
* ``"fibonacci"`` → :class:`FibonacciScoring`
* ``"seed_diff_bonus"`` → :class:`SeedDiffBonusScoring` (requires ``seed_map``)
* ``"dict"`` → :class:`DictScoring` (requires ``points`` and ``name``)
* ``"custom"`` → :class:`CustomScoring` (requires ``callable`` and ``name``)
Args:
config: Configuration dict with at least a ``"type"`` key.
Returns:
Instantiated scoring rule.
Raises:
ValueError: If ``type`` is unknown or required keys are missing.
"""
if "type" not in config:
msg = "scoring config must contain a 'type' key"
raise ValueError(msg)
scoring_type = config["type"]
if scoring_type == "standard":
return StandardScoring()
if scoring_type == "fibonacci":
return FibonacciScoSimulationResult class · python · L649-L684 (36 LOC)src/ncaa_eval/evaluation/simulation.py
class SimulationResult:
"""Result of tournament simulation for one season.
Both the analytical path and MC path produce a ``SimulationResult``.
Attributes:
season: Tournament season year.
advancement_probs: Per-team advancement probabilities,
shape ``(n_teams, n_rounds)``.
expected_points: Mapping of ``scoring_rule_name → per-team EP``,
each shape ``(n_teams,)``.
method: ``"analytical"`` or ``"monte_carlo"``.
n_simulations: ``None`` for analytical; N for MC.
confidence_intervals: Optional mapping of
``rule_name → (lower, upper)`` arrays.
score_distribution: Optional mapping of
``rule_name → per-sim scores`` array, shape ``(n_simulations,)``.
bracket_distributions: Optional mapping of
``rule_name → BracketDistribution`` (MC only; ``None`` for analytical).
Note: distributions are computed from the chalk-bracket score (how many
p