Skip to content

Precomputed Predictions

The COBRA subsystem can operate on a matrix that already contains model predictions instead of fitting and evaluating an internal estimator pool.

The main source-level switch is:

as_predictions=True

and it is supported directly by:

GradientCOBRA
MixCOBRARegressor
CombinedClassifier

through the shared:

resolve_training_context()

helper.

This mode is especially important for KFC integration because the KFC F-Step already produces a prediction matrix that can be passed directly into a COBRA combiner.


What counts as a precomputed prediction matrix?

Suppose you already have predictions from \(M\) models for \(n\) observations.

For regression:

\[ P = \begin{bmatrix} f_1(x_1) & \cdots & f_M(x_1)\\ \vdots & & \vdots\\ f_1(x_n) & \cdots & f_M(x_n) \end{bmatrix} \in \mathbb{R}^{n\times M}. \]

For classification, the current CombinedClassifier prediction space is based on hard class predictions, so a row can look like:

[0, 1, 1, 0]

rather than a probability vector.

The expected layout is:

rows
    samples

columns
    model predictions / prediction-space coordinates

Why use precomputed predictions?

Precomputed mode is useful when prediction features were generated by another part of a pipeline.

Examples include:

KFC F-Step predictions
external ensemble predictions
out-of-fold predictions from another framework
predictions loaded from disk
predictions generated by models managed outside kfc_procedure

With as_predictions=True, COBRA skips its normal internal base-estimator training stage and treats the supplied matrix directly as its aggregation representation.


Normal mode vs precomputed mode

Normal mode

raw X
    ↓
split into estimator-training and calibration subsets
    ↓
fit base estimators
    ↓
predict calibration data
    ↓
prediction space
    ↓
COBRA distance/kernel/aggregation

Precomputed mode

prediction matrix P
    ↓
as_predictions=True
    ↓
use P directly as calibration prediction space
    ↓
COBRA distance/kernel/aggregation

Shared training-context behavior

The central source logic is in:

resolve_training_context()

Its precomputed branch is:

if as_predictions:
    return TrainingContext(
        X_k=None,
        y_k=None,
        X_l=np.asarray(X),
        y_l=np.asarray(y),
        as_predictions=True,
    )

This branch runs before:

explicit X_l / y_l handling
pred_features handling
automatic data splitting

Resulting TrainingContext

With:

model.fit(
    P,
    y,
    as_predictions=True,
)

the resolver returns:

X_k = None
y_k = None

X_l = P
y_l = y

as_predictions = True

So there is no separate base-estimator training subset.


No automatic train/calibration split

Because the as_predictions branch returns immediately, parameters such as:

split_ratio
overlap

do not create an additional first-level COBRA split in this mode.

The entire supplied prediction matrix becomes the calibration-side data.


Input validation

Before the branch logic, the shared resolver calls:

check_X_y(
    X,
    y,
)

So in precomputed mode:

P
y

must have a compatible number of rows.

For example:

P.shape[0]
==
len(y)

must hold.


GradientCOBRA with precomputed predictions

GradientCOBRA.fit() accepts:

fit(
    X,
    y,
    X_l=None,
    y_l=None,
    split_ratio=0.5,
    overlap=0.0,
    as_predictions=False,
)

To supply a ready-made prediction matrix:

from kfc_procedure.cobra import (
    GradientCOBRA,
)

model = GradientCOBRA(
    random_state=42,
)

model.fit(
    P_train,
    y_train,
    as_predictions=True,
)

GradientCOBRA fit behavior

After resolving the training context, the source stores:

self.X_k_ = ctx.X_k
self.y_k_ = ctx.y_k
self.X_l_ = ctx.X_l
self.y_l_ = ctx.y_l
self.as_predictions_ = ctx.as_predictions

Then:

if not self.as_predictions_:
    self.estimators_ = self._fit_estimators(
        self.X_k_,
        self.y_k_,
    )

    prediction_space = self._load_predictions(
        self.X_l_,
    )
else:
    prediction_space = self.X_l_

So with:

as_predictions=True

the supplied matrix is assigned directly to:

prediction_space

and:

estimators_

is not created by this branch.


GradientCOBRA normalization still applies

Precomputed predictions do not bypass GradientCOBRA's scalar normalization.

The source computes:

self.normalize_constant_ = (
    compute_normalization_constant(
        y=y,
        norm_constant=self.norm_constant,
        scale_factor=30.0,
        M=prediction_space.shape[1],
    )
)

then:

self.Y_l_norm_ = (
    prediction_space
    *
    self.normalize_constant_
)

So the workflow is:

P_train
    ↓
scalar normalization
    ↓
Y_l_norm_
    ↓
distance matrix
    ↓
CV / optimization

GradientCOBRA prediction is symmetrical

GradientCOBRA.predict() explicitly checks:

if self.as_predictions_:
    prediction_space = X
else:
    prediction_space = self._load_predictions(X)

Therefore, if fitting used:

as_predictions=True

prediction should also receive a prediction matrix:

y_pred = model.predict(
    P_test
)

The supplied P_test is interpreted directly as new prediction-space coordinates.


Full GradientCOBRA example

from kfc_procedure.cobra import (
    GradientCOBRA,
)

cobra = GradientCOBRA(
    distance="euclidean",
    kernel="rbf",
    random_state=42,
)

cobra.fit(
    P_train,
    y_train,
    as_predictions=True,
)

y_pred = cobra.predict(
    P_test
)

Required column consistency

The source does not store or validate semantic column names.

Therefore the caller is responsible for ensuring that:

P_train[:, 0]

and:

P_test[:, 0]

represent predictions from the same underlying model or prediction source.

The same requirement applies to every column.


Inspect GradientCOBRA precomputed state

After fitting:

print(
    model.as_predictions_
)

should be:

True

and:

print(
    model.X_k_
)

print(
    model.y_k_
)

should show:

None
None

while:

print(
    model.X_l_.shape
)

print(
    model.Y_l_norm_.shape
)

should match the precomputed training-matrix row and column counts.


No estimators_ in GradientCOBRA precomputed mode

Because the source skips:

self._fit_estimators(...)

when:

as_predictions_ == True

the fitted object does not rely on an internal estimator pool for prediction.

This is why:

predict(
    P_test
)

works directly.


CombinedClassifier with precomputed predictions

CombinedClassifier.fit() supports the same switch:

classifier.fit(
    P_train,
    y_train,
    as_predictions=True,
)

CombinedClassifier fit branch

The current source does:

if not self.as_predictions_:
    self.classes_ = np.unique(
        self.y_k_
    )

    self.estimators_ = self._fit_estimators(
        self.X_k_,
        self.y_k_,
    )

    self.pred_l_ = self._load_predictions(
        self.X_l_
    )
else:
    self.classes_ = np.unique(
        self.y_l_
    )

    self.pred_l_ = self.X_l_

So with precomputed mode:

P_train
    becomes pred_l_

classes_
    are derived from y_l_

internal estimators
    are not fitted

CombinedClassifier prediction space

The calibration distance matrix is computed directly from:

self.pred_l_

using:

self.distance_.matrix(
    self.pred_l_,
    self.pred_l_,
)

The current default distance is:

hamming

which fits hard class-prediction vectors.


CombinedClassifier prediction is symmetrical

At prediction time:

if self.as_predictions_:
    preds_space = X
else:
    preds_space = self._load_predictions(X)

Therefore:

classifier.predict(
    P_test
)

treats P_test directly as prediction-space data after a precomputed fit.


CombinedClassifier probabilities

predict_proba() has the same source branch:

if self.as_predictions_:
    preds_space = X
else:
    preds_space = self._load_predictions(X)

So:

proba = classifier.predict_proba(
    P_test
)

also accepts precomputed prediction-space rows directly.


Probability caveat

The current default:

WeightedVoteAggregator.aggregate_proba()

does not use the supplied kernel weights.

Therefore query-specific probabilities from the current default CombinedClassifier.predict_proba() are affected by that implementation limitation.

See:

Aggregators

for the exact behavior.


Full CombinedClassifier example

from kfc_procedure.cobra import (
    CombinedClassifier,
)

classifier = CombinedClassifier(
    distance="hamming",
    kernel="rbf",
    random_state=42,
)

classifier.fit(
    P_train,
    y_train,
    as_predictions=True,
)

y_pred = classifier.predict(
    P_test
)

proba = classifier.predict_proba(
    P_test
)

Hard predictions, not probabilities

When CombinedClassifier builds prediction space internally, it uses:

est.predict(X)

rather than:

est.predict_proba(X)

Therefore a precomputed matrix that mirrors the built-in semantics should normally contain hard class predictions.

Example:

P_train = np.array([
    [0, 0, 1, 0],
    [1, 1, 1, 1],
    [2, 1, 2, 2],
])

rather than concatenated probability vectors.


MixCOBRA with precomputed predictions

MixCOBRARegressor.fit() also accepts:

as_predictions=True

and its fit branch initially behaves similarly.

if not self.as_predictions_:
    self.estimators_ = self._fit_estimators(
        self.X_k_,
        self.y_k_,
    )

    prediction_space = self._load_predictions(
        self.X_l_
    )
else:
    prediction_space = self.X_l_

So internal base-estimator fitting is skipped.


MixCOBRA stores the same matrix in two roles

In precomputed mode, the shared resolver gives:

X_l_ = supplied P

and the fit branch sets:

prediction_space = supplied P.

MixCOBRA then computes:

self.X_l_norm_ = (
    self.X_l_
    *
    self.normalize_constant_x_
)

self.Y_l_norm_ = (
    prediction_space
    *
    self.normalize_constant_y_
)

Thus the same supplied prediction matrix participates as both:

input-space representation
prediction-space representation

under two different scalar normalizations.


MixCOBRA one-parameter mode

If:

one_parameter=True

the source concatenates:

self.X_l_norm_
self.Y_l_norm_

into:

self.mix_features_

even though both originated from the same supplied matrix in as_predictions=True mode.


MixCOBRA two-parameter mode

If:

one_parameter=False

the source computes:

distance_matrix_x_
distance_matrix_y_

from two differently scaled copies of the same supplied prediction matrix.


Important MixCOBRA prediction limitation

The current MixCOBRARegressor.predict() does not mirror the as_predictions_ branch used by GradientCOBRA and CombinedClassifier.

Its code begins with:

if pred_X is None:
    pred_X = self._load_predictions(
        X
    )

There is no:

if self.as_predictions_:
    pred_X = X

branch.


Consequence

When MixCOBRARegressor is fitted with:

as_predictions=True

the fit method skips creation of:

self.estimators_

but later:

predict(
    P_test
)

still tries to execute:

self._load_predictions(
    P_test
)

when pred_X is omitted.

That path expects internal estimators that were never fitted.

Current source limitation

MixCOBRARegressor(as_predictions=True) is not symmetrical between fit() and the default predict() path.

Unlike GradientCOBRA and CombinedClassifier, precomputed fit mode does not automatically make the predict(X) argument act as prediction space.


Why pred_X= does not fully solve standalone precomputed MixCOBRA

MixCOBRARegressor.predict() accepts:

pred_X=None

and if pred_X is supplied it skips:

self._load_predictions(X)

However, it still separately computes:

X_norm = (
    X
    *
    self.normalize_constant_x_
)

Y_norm = (
    pred_X
    *
    self.normalize_constant_y_
)

So the method expects:

X
    an input-space matrix

pred_X
    a prediction-space matrix

This matches ordinary MixCOBRA's two-space design.

In as_predictions=True fit mode, however, the training code used the same supplied matrix for both roles.


Source-aligned workaround

To reproduce the same source geometry during prediction, one can supply the same precomputed test matrix in both arguments:

y_pred = model.predict(
    P_test,
    pred_X=P_test,
)

This causes:

X_norm
    to come from P_test

Y_norm
    also to come from P_test

which mirrors the two differently scaled copies created during precomputed fitting.

Caution

This is a workaround inferred directly from the current implementation.

The source does not contain a dedicated high-level helper documenting this as the intended public precomputed-prediction API for MixCOBRA.


MixCOBRA example with current source behavior

from kfc_procedure.cobra import (
    MixCOBRARegressor,
)

model = MixCOBRARegressor(
    one_parameter=False,
    random_state=42,
)

model.fit(
    P_train,
    y_train,
    as_predictions=True,
)

y_pred = model.predict(
    P_test,
    pred_X=P_test,
)

This avoids the internal-estimator lookup in predict().


The separate pred_features argument

MixCOBRA also exposes:

pred_features=None

in:

fit()

Its docstring describes this as:

Pre-computed model predictions to use instead of internal estimator outputs.

However, the current resolver and fit implementation do not use that branch in the straightforward way the description suggests.


Resolver behavior for pred_features

When:

pred_features is not None

and:

as_predictions=False

the resolver returns:

TrainingContext(
    X_k=np.asarray(X),
    y_k=np.asarray(y),
    X_l=pred_features,
    y_l=np.asarray(y),
    as_predictions=False,
)

So:

X_k
    original X

X_l
    pred_features

as_predictions
    False

MixCOBRA fit after pred_features

Because:

as_predictions_ == False

the current MixCOBRA fit branch runs:

self.estimators_ = self._fit_estimators(
    self.X_k_,
    self.y_k_,
)

prediction_space = self._load_predictions(
    self.X_l_
)

But:

self.X_l_

now contains:

pred_features

rather than ordinary calibration feature rows.

Therefore the source attempts to feed those precomputed prediction rows into the newly fitted base estimators.


Consequence

The current implementation does not directly assign:

prediction_space = pred_features

in the pred_features branch.

So pred_features is not presently a clean substitute for as_predictions=True.

Current source inconsistency

The MixCOBRA docstring describes pred_features as precomputed model predictions.

The actual current fit path passes those values back through internal base estimators.

This page documents the implementation rather than silently assuming the docstring's intended behavior.


Prefer as_predictions=True when the whole matrix is already prediction space

For the current source:

GradientCOBRA
CombinedClassifier

have clean, symmetrical precomputed-prediction workflows through:

as_predictions=True

For MixCOBRA, the fit side supports that mode but the prediction side requires special care as described above.


KFC integration

KFC's C-Step wrappers use precomputed mode automatically.

The F-Step already produces:

P_l

so the wrappers call their COBRA estimator with:

as_predictions=True

rather than fitting another base-estimator pool.


GradientCOBRA KFC wrapper

The current wrapper is:

class GradientCOBRACombiner(
    BaseCombiner
):

    def __init__(
        self,
        **cobra_params,
    ):
        self.cobra = GradientCOBRA(
            **cobra_params
        )

    def fit(
        self,
        X,
        y,
    ):
        self.cobra.fit(
            X,
            y,
            as_predictions=True,
        )

        return self

    def combine(
        self,
        X,
    ):
        return self.cobra.predict(
            X
        )

This works naturally because GradientCOBRA.predict() honors as_predictions_.


CombinedClassifier KFC wrapper

The classifier wrapper similarly uses:

self.cobra.fit(
    X,
    y,
    as_predictions=True,
)

and later:

self.cobra.predict(
    X
)

which is symmetrical in the current CombinedClassifier source.


MixCOBRA KFC wrapper caveat

The current MixCOBRACombiner uses:

self.cobra.fit(
    X,
    y,
    as_predictions=True,
)

but its combine() method simply calls:

self.cobra.predict(
    X
)

without supplying:

pred_X=X.

Because MixCOBRARegressor.predict() does not branch on as_predictions_, this wrapper can encounter the same missing-internal-estimator problem.

Current KFC integration issue

The current MixCOBRA C-Step wrapper fits with as_predictions=True but predicts through the default MixCOBRARegressor.predict(X) path.

Those two code paths are not currently symmetrical.


Precomputed regression predictions from external models

Suppose you train three external regressors and create:

P_train = np.column_stack([
    pred_a_train,
    pred_b_train,
    pred_c_train,
])

P_test = np.column_stack([
    pred_a_test,
    pred_b_test,
    pred_c_test,
])

Then GradientCOBRA can use them directly:

model = GradientCOBRA(
    random_state=42,
)

model.fit(
    P_train,
    y_train,
    as_predictions=True,
)

y_pred = model.predict(
    P_test
)

No estimator objects need to be passed into GradientCOBRA.


Precomputed classification predictions from external models

P_train = np.column_stack([
    clf_a.predict(
        X_train
    ),
    clf_b.predict(
        X_train
    ),
    clf_c.predict(
        X_train
    ),
])

P_test = np.column_stack([
    clf_a.predict(
        X_test
    ),
    clf_b.predict(
        X_test
    ),
    clf_c.predict(
        X_test
    ),
])

Then:

classifier = CombinedClassifier(
    distance="hamming",
    random_state=42,
)

classifier.fit(
    P_train,
    y_train,
    as_predictions=True,
)

y_pred = classifier.predict(
    P_test
)

Train/test column alignment

The package does not retain metadata such as:

model name per column
column semantic identifier
feature provenance

So you should preserve column order explicitly.

For example:

prediction_sources = [
    "ridge",
    "random_forest",
    "svr",
]

and construct both matrices in that order.


Row alignment

Every training row must correspond to the same target row.

assert (
    P_train.shape[0]
    ==
    len(
        y_train
    )
)

Likewise, every row in:

P_test

must correspond to the output row you expect in:

y_pred.

Prediction-space width

The training and prediction matrices should normally have the same number of columns:

assert (
    P_train.shape[1]
    ==
    P_test.shape[1]
)

Otherwise the configured distance operation is likely to fail because query and calibration vectors have incompatible coordinate dimensions.


Numeric dtype

GradientCOBRA and MixCOBRA eventually perform numerical scaling and distance calculations, so their precomputed regression matrices should be numeric.

CombinedClassifier with Hamming distance can naturally work with discrete class prediction values, subject to the rest of the pipeline's dtype behavior.


Missing values

The precomputed mode does not add a dedicated missing-value cleaning stage.

Downstream behavior depends on:

check_array
distance implementation
normalization
kernel
aggregator

For regression prediction matrices, verify finite values before fitting:

import numpy as np

print(
    np.isfinite(
        P_train
    ).all()
)

Out-of-fold predictions

The source supports any matrix supplied through:

as_predictions=True

and does not inspect how those predictions were generated.

If the matrix was created externally, the responsibility for avoiding leakage or inappropriate reuse lies outside the COBRA implementation.

The package does not tag rows as:

in-sample
out-of-fold
held-out

or verify provenance.


No internal estimator refitting in precomputed mode

A central benefit of as_predictions=True is that the COBRA estimator does not fit its configured estimator pool.

That means settings such as:

estimators
estimators_params
n_jobs used for estimator fitting

are not used for the skipped internal fit branch.

The downstream COBRA components are still resolved and optimized.


Cross-validation still runs

Precomputed mode bypasses the first-level train/calibration split, but it does not bypass COBRA hyperparameter cross-validation.

For GradientCOBRA:

self.cv_folds_ = list(
    self.cv_.split(
        self.X_l_,
        self.Y_l_norm_,
    )
)

still executes.

CombinedClassifier and MixCOBRA likewise construct their internal CV folds.


Optimization still runs

After distance matrices and folds are built, the normal optimization stage still selects parameters such as:

bandwidth
alpha
beta.

So:

as_predictions=True

means:

skip base-estimator construction

not:

skip COBRA training.

Inspect whether precomputed mode was used

print(
    model.as_predictions_
)

Expected:

True

for a model fitted with:

as_predictions=True.

Inspect calibration representation

GradientCOBRA:

print(
    model.X_l_
)

print(
    model.Y_l_norm_
)

CombinedClassifier:

print(
    classifier.pred_l_
)

MixCOBRA:

print(
    model.X_l_norm_
)

print(
    model.Y_l_norm_
)

Verify GradientCOBRA did not fit estimators

A source-aligned check is:

print(
    hasattr(
        model,
        "estimators_",
    )
)

In a fresh model fitted only through:

as_predictions=True

the internal estimator-fit branch does not create that attribute.


Verify CombinedClassifier did not fit estimators

Likewise:

print(
    hasattr(
        classifier,
        "estimators_",
    )
)

should normally be false for a fresh precomputed-mode fit.


Reusing externally saved matrices

A simple workflow can save:

np.save(
    "P_train.npy",
    P_train,
)

np.save(
    "P_test.npy",
    P_test,
)

and later load:

P_train = np.load(
    "P_train.npy"
)

P_test = np.load(
    "P_test.npy"
)

before fitting a COBRA estimator.

The package itself does not define a custom persistence format specifically for prediction-space matrices.


DataFrame input

check_X_y() and check_array() convert compatible tabular input into NumPy arrays.

Therefore a pandas DataFrame can be supplied, but column labels are not retained as semantic prediction-source metadata inside the fitted COBRA estimator.

If names matter, keep them separately.


Precomputed probabilities are not the default classification representation

A matrix like:

[p(class=0), p(class=1), ...]

can be numeric input mathematically, but that is not the representation generated by the current CombinedClassifier base-estimator helper.

Its internal helper uses:

est.predict(X)

to build hard-label columns.

So a probability matrix would represent a custom prediction space rather than the current source's default semantics.


Precomputed predictions and normalization

GradientCOBRA

Still applies:

one scalar normalization constant

derived from:

y
prediction-space width M.

MixCOBRA

Still applies:

normalize_constant_x_
normalize_constant_y_.

CombinedClassifier

Applies no internal normalization before distance computation.

See:

Normalization

for the exact formulas.


Precomputed predictions and distances

Once accepted, the matrix is just a coordinate representation.

The configured distance decides how rows are compared.

Examples:

GradientCOBRA
    default euclidean

CombinedClassifier
    default hamming

MixCOBRA
    default euclidean

See:

Distances


Precomputed predictions and kernels

The prediction matrix itself does not determine neighbor weights directly.

The flow remains:

prediction matrix
    ↓
distance matrix
    ↓
adapter
    ↓
kernel
    ↓
aggregation weights.

So all normal kernel and optimization settings remain relevant in precomputed mode.


KFC F-Step as precomputed prediction space

The KFC Procedure naturally creates a matrix:

P_l = model.fstep_.predict(
    X_l,
    clusters_l,
)

where each column is a divergence-specific prediction stream.

That matrix is already in the exact structural form expected by the COBRA precomputed mode:

rows
    observations

columns
    prediction sources.

This is why the COBRA C-Step wrappers call:

fit(
    P_l,
    y_l,
    as_predictions=True,
)

Manual KFC-to-GradientCOBRA workflow

You can reproduce the idea explicitly.

clusters_train = (
    kfc_model
    .kstep_
    .predict(
        X_train
    )
)

P_train = (
    kfc_model
    .fstep_
    .predict(
        X_train,
        clusters_train,
    )
)

Then:

cobra = GradientCOBRA()

cobra.fit(
    P_train,
    y_train,
    as_predictions=True,
)

For test data:

clusters_test = (
    kfc_model
    .kstep_
    .predict(
        X_test
    )
)

P_test = (
    kfc_model
    .fstep_
    .predict(
        X_test,
        clusters_test,
    )
)

y_pred = cobra.predict(
    P_test
)

Manual KFC-to-CombinedClassifier workflow

The same concept applies to classification:

classifier = CombinedClassifier(
    distance="hamming",
)

classifier.fit(
    P_train,
    y_train,
    as_predictions=True,
)

y_pred = classifier.predict(
    P_test
)

The F-Step rows become hard-label prediction-space coordinates.


Precomputed mode is not the same as explicit calibration data

These two interfaces have different meanings.

Explicit calibration data

model.fit(
    X_k,
    y_k,
    X_l=X_l,
    y_l=y_l,
)

means:

fit internal base estimators on X_k/y_k
generate their predictions on X_l
use those predictions as calibration prediction space.

Precomputed mode

model.fit(
    P,
    y,
    as_predictions=True,
)

means:

do not fit internal base estimators
treat P itself as calibration prediction space.

Precomputed mode is not the same as pred_features

For current MixCOBRA source:

as_predictions=True

and:

pred_features=...

follow different code paths.

as_predictions=True directly assigns the supplied matrix to prediction space during fit.

pred_features currently becomes X_l while as_predictions remains false, after which internal estimators are still applied.

Do not treat these two arguments as equivalent in the current implementation.


Common errors

Mismatched sample count

ValueError

from the shared check_X_y() path can occur if:

P_train.shape[0]
!=
len(
    y_train
)

Mismatched prediction width

If:

P_train.shape[1]
!=
P_test.shape[1]

distance computation can fail because query and calibration coordinates have different dimensions.


Reordered prediction columns

This may not raise an error because shapes still match.

But it changes the geometry.

For example:

training:
    [ridge, random_forest, svr]

test:
    [svr, ridge, random_forest]

is semantically incorrect even though both matrices have three columns.


Calling MixCOBRA predict(P_test) after precomputed fit

The current source can try to use missing internal estimators.

Use the source-aware workaround:

model.predict(
    P_test,
    pred_X=P_test,
)

if you intentionally use this current precomputed MixCOBRA mode.


Validation checklist

Before fitting:

assert (
    P_train.ndim
    ==
    2
)

assert (
    len(
        P_train
    )
    ==
    len(
        y_train
    )
)

Before prediction:

assert (
    P_test.ndim
    ==
    2
)

assert (
    P_test.shape[1]
    ==
    P_train.shape[1]
)

For numeric regression spaces:

assert np.isfinite(
    P_train
).all()

assert np.isfinite(
    P_test
).all()

Recommended metadata to keep externally

Because COBRA does not store prediction-column names, keep something like:

prediction_sources = [
    "ridge",
    "random_forest_regressor",
    "svr",
]

alongside saved matrices.

For reproducibility, also keep:

model versions
training split identifiers
column ordering
class encoding
data preprocessing version

outside the COBRA estimator.


Current source comparison

Estimator as_predictions=True fit predict(X) treats X as prediction space Notes
GradientCOBRA Yes Yes clean symmetric path
CombinedClassifier Yes Yes hard-label prediction space by default
MixCOBRARegressor Yes No current predict path still derives pred_X unless supplied

KFC wrapper comparison

KFC combiner Fits wrapped model with as_predictions=True Current prediction compatibility
GradientCOBRACombiner Yes compatible
CobraClassifierCombiner Yes compatible
MixCOBRACombiner Yes current wrapper affected by MixCOBRA predict asymmetry

Quick reference

Goal Current source path
GradientCOBRA from external predictions fit(P, y, as_predictions=True)
GradientCOBRA new prediction matrix predict(P_test)
CombinedClassifier from hard predictions fit(P, y, as_predictions=True)
CombinedClassifier new prediction matrix predict(P_test)
CombinedClassifier probabilities predict_proba(P_test)
MixCOBRA precomputed fit fit(P, y, as_predictions=True)
MixCOBRA current-source prediction workaround predict(P_test, pred_X=P_test)
Skip automatic data split use as_predictions=True
Skip internal base estimators use as_predictions=True
Keep COBRA CV/optimization happens automatically

Mental model

Precomputed-prediction mode says: “the prediction space already exists; start the COBRA procedure from that matrix instead of fitting another ensemble to create it.”

For GradientCOBRA:

\[ \boxed{ P \rightarrow \text{normalize} \rightarrow \text{distance} \rightarrow \text{kernel} \rightarrow \text{weighted target aggregation} } \]

For CombinedClassifier:

\[ \boxed{ P_{\text{hard labels}} \rightarrow \text{distance} \rightarrow \text{kernel} \rightarrow \text{weighted class vote} } \]

The current MixCOBRA source also accepts the matrix during fitting, but its prediction API does not yet mirror that precomputed mode automatically.