Skip to content

Rule, reports and errors

Quantizer dataclass

Quantizer(
    transform: FisherTransform,
    centers: ndarray,
    metric: ndarray | None,
    schema: ScoreSchema | None,
    provenance: ScoreProvenance,
    criterion: Criterion,
    execution: ExecutionConfig,
)

A frozen hard rule on score space.

This is what a fit is for: the object that turns a future score vector into a bin. It holds no training labels, no reports and no history -- those stay on :class:~scorequant.results.QuantizerResult, which exposes the rule as result.quantizer.

Attributes:

  • transform (FisherTransform) –

    Projection onto the informative score subspace the rule was fitted in.

  • centers (ndarray) –

    Cell centers in transformed coordinates, shape [K, R].

  • metric (ndarray | None) –

    Optional common Mahalanobis metric, shape [R, R]. None means the ordinary Euclidean nearest-center rule in transformed coordinates.

  • schema (ScoreSchema | None) –

    Optional names of the raw score coordinates the rule expects.

  • provenance (ScoreProvenance) –

    How the scores the rule was fitted on were obtained.

  • criterion (Criterion) –

    The objective the rule was optimized for, recorded so a loaded artifact can still say what it was built to preserve.

  • execution (ExecutionConfig) –

    The backend prediction runs on unless overridden. A fitted rule keeps the execution it was fitted under; a loaded rule takes the loading environment's default, because the artifact does not record one.

n_bins property

n_bins: int

Return the number of hard output labels.

rank property

rank: int

Return the numerically informative score-space rank.

input_dim property

input_dim: int

Return the raw score dimension the rule accepts.

information_kind property

information_kind: InformationKind

Describe whether supplied-score matrices justify exact Fisher language.

predict_scores

predict_scores(
    scores: ArrayLike,
    *,
    execution: ExecutionConfig | None = None,
) -> ndarray

Assign raw score rows with the frozen rule.

Rows are assigned in memory-bounded chunks, so predicting on a large sample never materializes the full [n_rows, n_bins, rank] distance tensor at once. Each row's nearest-center argmin is independent of every other row, so chunking is bit-identical to the unchunked computation.

evaluate_scores

evaluate_scores(
    scores: ArrayLike,
    weights: ArrayLike | None = None,
    *,
    rank_rtol: float | None = None,
    execution: ExecutionConfig | None = None,
) -> InformationReport

Measure the information this rule retains on a new weighted sample.

This is a property of the rule rather than of the fit that produced it, so a loaded artifact can be scored against fresh data without the training result being present.

Parameters:

  • rank_rtol (float | None, default: None ) –

    Relative rank threshold for the report. Defaults to the one the rule's own transform was built with.

to_dict

to_dict() -> dict[str, JsonValue]

Return JSON-ready rule state.

Unlike the diagnostic to_dict on the result types, this one is the readable face of a versioned format: :meth:save writes exactly these fields, with the arrays moved out to .npy members.

save

save(path: str | Path) -> Path

Write the rule to a versioned, non-pickle artifact.

The file is a zip archive holding one manifest.json and one .npy member per array. Nothing is pickled, so loading it executes no code from the file, and reading it back needs neither JAX nor the solver that produced it.

The fitting execution is not saved: a rule fitted on a GPU must still load on a CPU-only or JAX-less machine. The manifest carries a default execution object only so 0.2 readers, which require the field, can read the file; :meth:load ignores it.

Parameters:

  • path (str | Path) –

    Destination path. .sqz is appended when the path has no suffix.

Returns:

  • Path –

    The path actually written.

load classmethod

load(path: str | Path) -> Quantizer

Read a rule written by :meth:save.

Raises:

  • ValueError –

    When the file is not a ScoreQuant artifact, or declares a format_version this build does not know. An unknown version is refused by name rather than partially interpreted.

information_report

information_report(
    scores: ArrayLike,
    assignments: ArrayLike,
    weights: ArrayLike | None = None,
    *,
    n_bins: int | None = None,
    rank_rtol: float | None = None,
    execution: ExecutionConfig | None = None,
) -> InformationReport

Build retained-information and occupancy diagnostics for hard bins.

Parameters:

  • scores (ArrayLike) –

    Finite score matrix with shape [N, P].

  • assignments (ArrayLike) –

    Integer bin label for every input row, with shape [N].

  • weights (ArrayLike | None, default: None ) –

    Optional finite, nonnegative weights with shape [N].

  • n_bins (int | None, default: None ) –

    Total number of bins, including empty bins. Inferred when omitted.

  • rank_rtol (float | None, default: None ) –

    Relative threshold used to select informative Fisher directions.

Returns:

  • InformationReport –

    Unregularized Fisher matrices, normalized retention, spectrum, and per-bin occupancy diagnostics.

Notes

The between-cell algebra is exact for the supplied vectors, and every moment is uncentred: fisher_binned is the weighted second moment sum_b W_b c_b c_b.T of the cell means about the score-space origin, never a covariance. When the scores are estimates s_hat rather than the model score s, the report measures the between-cell second moment of E[s_hat | q], not that of E[s | q], and is a surrogate for the model's own Fisher information.

efficient_score_bound

efficient_score_bound(
    scores: ArrayLike,
    *,
    interest: tuple[int, ...],
    weights: ArrayLike | None = None,
    n_bins: int,
    config: ScalarDPConfig | None = None,
    execution: ExecutionConfig | None = None,
) -> EfficientScoreBound

Certify a ceiling on profiled information by quantizing the efficient score.

The full-data efficient score \(\hat s=s_\psi-B^\ast s_\lambda\) uses the nuisance regression of the unbinned information matrix. Efficient-score domination bounds the same-label profiled information of every hard rule \(q\) with at most n_bins cells by the between-cell information of \(\hat s\) under that rule, so the best n_bins-cell rule of the efficient score certifies a ceiling for the whole score space. For one parameter of interest that best rule has ordered interval cells and is found exactly by weighted interval dynamic programming, which makes the returned ceiling both certified and cheap.

The returned labels are also the natural initializer for optimize_partition with ProfiledDOptimality: they already solve the relaxed upper problem, so profiled exchange starts inside the efficient-score geometry instead of at generic k-means seeding.

Parameters:

  • scores (ArrayLike) –

    Finite score matrix with shape [N, P] in the declared parameter order.

  • interest (tuple[int, ...]) –

    Unique nonnegative score-column indices for parameters of interest. Only one interest column is supported.

  • weights (ArrayLike | None, default: None ) –

    Optional finite, nonnegative weights with shape [N].

  • n_bins (int) –

    Cell budget the bound is certified for.

  • config (ScalarDPConfig | None, default: None ) –

    Exact scalar solver settings. Whitening a scalar coordinate is a strictly positive rescaling, so it changes neither the labels nor the bound; rank_rtol still rejects a numerically vanishing efficient score and max_rows still bounds the exact quadratic recursion.

Returns:

  • EfficientScoreBound –

    Certified log-scale ceiling, the interval labels attaining it, and the efficient scores it was computed from.

Raises:

  • NotImplementedError –

    When more than one interest column is requested. A multivariate efficient score needs a genuine multivariate D solver, which would make the returned value a heuristic rather than a certificate.

Notes

The bound follows the uncentered convention of binned_fisher_information: scores are never mean-centered, so the between-cell quantity is a weighted second moment of cell means about the score-space origin. This matches PartitionResult.objective for the profiled criterion exactly, which is what makes the reported gap meaningful.

retention_uncertainty

retention_uncertainty(
    scores: ArrayLike,
    assignments: ArrayLike,
    *,
    n_bins: int | None = None,
    confidence_level: float = 0.95,
    rank_rtol: float | None = None,
    execution: ExecutionConfig | None = None,
) -> RetentionUncertainty

Estimate the sampling uncertainty of a frozen rule's held-out retention.

The rows are an evaluation sample the rule never saw: independent, equally weighted draws from the reference law at which the model's true scores are evaluated, whose scores are those true scores and whose assignments are the frozen rule's labels for them. The estimate is the uncentred plug-in geometric-mean retention \(\hat\eta_D=(\det\hat I_Z/\det\hat V)^{1/d}\), the same number information_report reports on a full-rank sample. Its standard error is the influence-function estimate of the conditional central limit theorem RETENTION-PLUGIN-CLT-FROZEN-VECTOR (scalar case RETENTION-PLUGIN-CLT-FROZEN-SCALAR),

.. math::

\psi_i=\frac{\hat\eta_D}{d}\Big[2s_i^\top\hat I_Z^{-1}\hat c_{z_i}
-\hat c_{z_i}^\top\hat I_Z^{-1}\hat c_{z_i}-s_i^\top\hat V^{-1}s_i\Big],
\qquad
\widehat{\mathrm{SE}}=\sqrt{\tfrac{1}{N}\cdot\tfrac{1}{N}\sum_i\psi_i^2},

and the interval is the untruncated two-sided Wald interval \(\hat\eta_D\pm z_{1-\alpha/2}\widehat{\mathrm{SE}}\).

Parameters:

  • scores (ArrayLike) –

    Finite true-score matrix with shape [N, P], N >= 2. Rows are never centered: the score-space origin has statistical meaning.

  • assignments (ArrayLike) –

    Integer label of the frozen rule for every row, with shape [N].

  • n_bins (int | None, default: None ) –

    Total number of cells, including empty ones. Inferred when omitted. Empty cells contribute nothing.

  • confidence_level (float, default: 0.95 ) –

    Two-sided nominal level, strictly between zero and one.

  • rank_rtol (float | None, default: None ) –

    Relative eigenvalue threshold for the informative directions of the full moment. A dtype-aware default is used when omitted. It moves neither the between-cell singularity test, which is judged at the dtype default as in information_report, nor the cancellation threshold of the degenerate_variance guard.

Returns:

  • RetentionUncertainty –

    Estimate, standard error, interval and the status that says which of them are available.

Notes

The interval covers sampling variability of the evaluation draw for a frozen rule under the theorem's conditions: positive cell probabilities, finite fourth moments of the score, positive definite full and between-cell moments, and positive asymptotic variance. None of these can be verified from an array, and a positive empirical rank does not certify a population eigenvalue floor. The theorem is asymptotic: an infinite fourth moment puts a law outside it, and heavy tails, including laws that satisfy every condition, can cause material undercoverage at moderate sample sizes. Reading the number as the model's Fisher retention needs the evaluation law to be the reference law, where the true score has mean zero; under another law the same arithmetic estimates an uncentred determinant ratio that is not that retention. Weighted samples, rules refitted on the evaluation rows, profiled \(D_s\) retention and any statement without true scores are outside this diagnostic (OPEN-RETENTION-UNCERTAINTY).

Scores from a classifier or another estimator are not the true scores. Their reported retention carries a separate proxy bias that the score-error budget SCORE-ERROR-RETENTION-BUDGET bounds only under truth-dependent error and conditioning assumptions; AUC or calibration alone does not certify that bias, and this error bar never measures it.

Four outcomes withhold the interval rather than report an unsupported one. insufficient_cells: the rule declares at most \(d\) cells, so FI-RANK-CEILING makes the population retention zero at the reference law and the plug-in is the upward-biased endpoint estimator; nothing is reported. singular_full_information: the full moment is rank deficient at the threshold. singular_retained_information: the between-cell moment is numerically rank deficient on this sample, so the diagnostic reports zero by its numerical-rank convention and withholds the interval. The exact plug-in can still be positive below the threshold; this sample-side refusal does not identify the population rank. Population singularity is a separate boundary of the Wald theorem (RETENTION-PLUGIN-SINGULAR-ENDPOINT-RATE). degenerate_variance: the influence values cancel to rounding noise, the sample-side face of CE-O7-ELLIPSOID-ZERO-VARIANCE-001. The test is \(\mathrm{rms}(B)\le\tau\,\mathrm{rms}(M)\) with \(B_i\) the bracket of \(\psi_i\), \(M_i\) the sum of the magnitudes of its three terms and \(\tau\) the dtype's rank threshold (\(10^{-10}\) in float64, about \(1.2\times10^{-5}\) in float32, not moved by rank_rtol); it is a numerical guard that can suppress small positive influence variance and never asserts that the population variance is zero. The endpoints of an ok interval are not clipped to [0, 1].

ScoreQuantError

Bases: Exception

Base of every exception ScoreQuant raises deliberately.

ContractError

Bases: ScoreQuantError, ValueError

The caller violated an input, shape, range, or pairing contract.

Detectable from the arguments alone; the remedy is to change the call. Subclasses ValueError so existing except ValueError handlers keep working.

RefusalError

RefusalError(message: str, counterexample: str)

Bases: ScoreQuantError, RuntimeError

The library declines a valid request because a theorem-backed condition fails on the data.

Parameters:

  • message (str) –

    Plain-English refusal, unchanged from the pre-hierarchy message text.

  • counterexample (str) –

    Id of the exact counterexample that forces the refusal; the repository keeps it as tests/fixtures/counterexamples/<id>.json. Always a string literal at the raise site.