← back to dhilgart__NCAA_eval

Function bodies 354 total

All specs Real LLM only Function bodies
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.Seri
run_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}.
        conso
ReliabilityData 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 result
brier_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 result
About: 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 result
expected_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)

    # Bi
reliability_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 must
plot_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 = [team
plot_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=0
Methodology: 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: bool
BracketNode 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 None
BracketStructure 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 in
matchup_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 probabili
Same 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 result
EloProvider 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 = model
matchup_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 result
batch_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 P
Repobility · 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 decorator
get_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 None
list_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.0
CustomScoring 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_name
points_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_name
Methodology: 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 FibonacciSco
SimulationResult 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
‹ prevpage 2 / 8next ›