Reproducibility
Reproducibility in kfc_procedure depends on several independent sources of
randomness.
The most important user-facing control is:
but a single seed does not automatically control every stochastic component in the package.
The current source uses several different random-number APIs:
sklearn.utils.check_random_state
np.random.RandomState
np.random.default_rng
sklearn.model_selection.train_test_split
global np.random
and different components receive the estimator seed in different ways.
This page documents the current source behavior so you can make experiments as repeatable as possible.
Reproducibility map
flowchart TD
R["random_state"]
S["Top-level / COBRA split"]
K["K-Step clustering"]
C["COBRA CV folds"]
F["F-Step local models"]
CS["C-Step combiner"]
E["COBRA base estimators"]
O["Optimizer"]
R --> S
R --> K
R --> C
R --> F
R --> CS
E -. "not automatically seeded by shared COBRA helper" .- R
O -. "SPSA uses global np.random" .- R
The estimator seed is propagated to several structural components, but not to all model-specific or optimizer-specific randomness.
What random_state controls
At a high level, the current source uses random_state for:
KFC's top-level train/calibration split
K-Step Bregman clustering
COBRA automatic overlap/holdout splitting
COBRA K-fold cross-validation
some F-Step local-model construction
some C-Step combiner construction
some wrapped COBRA components
It does not provide one package-wide global RNG object shared by all of those components.
Reproducibility in KFCProcedure
The top-level constructor stores:
and passes it into:
KFC data split
KFCProcedure.fit() performs:
X_k, X_l, y_k, y_l = train_test_split(
X,
y,
test_size=0.5,
random_state=self.random_state,
stratify=(
y
if self.task == "classification"
else None
),
)
So a fixed:
reproduces the same top-level KFC split for the same data order and compatible scikit-learn behavior.
Classification stratification
For:
the top-level split uses:
whereas regression uses:
Therefore the KFC classification split has a different sampling constraint from the standalone COBRA automatic splitter.
K-Step reproducibility
KFC passes the same seed into:
KStep then forwards that seed to each:
instance it constructs.
BregmanKMeans RNG
Inside:
the current source creates:
from scikit-learn.
This produces the RNG used by the internal clustering initialization and reinitialization logic.
Multiple initializations
BregmanKMeans can run multiple initialization attempts through:
The current default is:
unless an explicit initial centroid matrix is provided.
The same seeded RNG object is reused across those attempts.
For a fixed seed and identical input ordering, this makes the sequence of initializations repeatable.
Explicit clustering initialization
If:
is passed directly to:
the source sets:
for that call.
That removes random centroid initialization from the clustering fit itself, although other parts of a surrounding pipeline can still be stochastic.
K-Step fitted results to compare
For reproducibility checks, useful fitted attributes include:
and for each underlying Bregman model:
If these differ between otherwise identical runs, later local-model and combination stages can also differ.
F-Step seed propagation
KFC creates:
When the configured local model is a string, FStep builds:
params = dict(
self.local_model_params
)
if "random_state" not in params:
params[
"random_state"
] = self.random_state
then calls:
Explicit local-model seed wins
If you provide:
FStep does not overwrite it with the KFC-level seed.
So:
takes precedence when explicitly present.
SklearnLocalModel parameter filtering
String-based scikit-learn local models are typically wrapped by:
which inspects the estimator constructor signature and retains supported arguments.
Therefore the injected:
is passed through only when the target estimator accepts it.
For deterministic estimators without a random-state parameter, the key can be filtered out.
F-Step custom object caveat
If:
is a pre-built non-string estimator object, FStep's _resolve() returns that
object directly.
The source does not clone it and does not inject the KFC seed.
Therefore reproducibility of that custom object depends on how you configured the object itself.
Same object is reused
The current F-Step source reuses the same non-string local-model object across cluster fits rather than cloning a fresh copy per cluster.
This is primarily a model-construction behavior, but it also matters for reproducibility because state is repeatedly overwritten on the same object.
C-Step seed propagation
KFC also constructs:
For string-based combiners, CStep performs:
params = dict(
self.combiner_params
)
if "random_state" not in params:
params[
"random_state"
] = self.random_state
and passes those parameters into:
C-Step constructor compatibility caveat
Not every built-in combiner constructor accepts:
Because CStep injects it for every string-based combiner when absent, some string combiner paths can fail with an unexpected keyword argument.
This is a current source issue rather than a reproducibility guarantee.
Pre-instantiated combiner objects bypass that injection.
Standalone COBRA reproducibility
The standalone estimators:
also expose:
and use it mainly for:
Automatic COBRA split
The shared:
creates:
SplitterFactory.create(
"split_overlap",
split_ratio=split_ratio,
overlap=overlap,
random_state=random_state,
)
when no explicit aggregation dataset or precomputed prediction matrix is provided.
OverlapSplitter RNG
The default automatic splitter uses:
and then:
So the first-level standalone COBRA split is repeatable for a fixed seed and identical row order.
Different RNG API from KFC clustering
This matters if you compare random sequences across components.
OverlapSplitter uses:
while BregmanKMeans uses:
which is based on the older RandomState style.
Even with the same integer seed, these generators do not produce the same random sequence.
The seed is a reproducibility control within each component, not a promise that every component draws matching numbers.
RandomHoldoutSplitter
The optional holdout splitter stores:
and passes it into:
train_test_split(
indices,
test_size=self.calibration_size,
random_state=self.random_state,
shuffle=True,
)
So its partition is reproducible for a fixed seed.
COBRA K-fold reproducibility
The main standalone COBRA estimators build:
through the factory.
KFoldCV RNG
The current custom K-fold implementation uses:
and:
when shuffling is enabled.
This is another RNG API distinct from the overlap splitter's
default_rng.
Fold assignment is deterministic after permutation
Once the index permutation is generated, the source distributes indices round-robin:
So the combination of:
determines the current fold assignment.
Stored folds
The main estimators materialize CV folds in:
You can compare them directly across runs.
Example:
StratifiedKFoldCV uses another RNG API
The registered stratified cross-validator uses:
and shuffles each class-specific index array.
So its sequence differs from the custom ordinary KFoldCV even when both receive the same integer seed.
The main COBRA estimators currently hard-code ordinary kfold, so this matters
mostly when using or extending the lower-level CV components directly.
TimeSeriesCV is deterministic
The current time-series cross-validator does not shuffle data and has no
random_state parameter.
For a fixed row order, n_splits, and test_size, it produces deterministic
expanding windows.
Explicit aggregation data improve split reproducibility
Supplying:
bypasses the automatic first-level COBRA splitter.
So if you preserve the exact external split arrays or row indices, the
train/calibration boundary becomes independent of the estimator's
random_state.
Other stochastic components, such as CV folds or base estimators, may still vary.
Precomputed predictions remove estimator-pool randomness
With:
GradientCOBRA and CombinedClassifier skip internal base-estimator fitting.
This can remove a major source of stochastic variation when the prediction matrix itself is fixed.
However, internal cross-validation and optimizer behavior can still depend on other settings.
COBRA base estimators are not globally reseeded
The shared helper:
builds estimators from their specification and parameters, but does not automatically inject the parent COBRA estimator's:
into every base estimator.
This is a crucial current source detail.
Example
This:
does not guarantee that every stochastic estimator in its default pool is constructed with:
If a base estimator needs its own fixed seed, pass it through:
Configure estimator-specific seeds
Example:
model = GradientCOBRA(
random_state=42,
estimators_params={
"random_forest_regressor": {
"random_state": 42,
},
},
)
For several stochastic estimators:
model = GradientCOBRA(
random_state=42,
estimators_params={
"random_forest_regressor": {
"random_state": 42,
},
"some_other_estimator": {
"random_state": 42,
},
},
)
Only parameters supported by each estimator constructor should be supplied.
Tuple estimator specifications
The shared estimator helper also supports:
so a seed can travel with the individual specification:
Pre-built estimator objects
If you pass an already-created estimator object, the COBRA helper uses that object directly.
Its reproducibility depends on its own configuration.
The parent COBRA random_state is not automatically written into the object.
Parallel execution and reproducibility
The package can fit and predict base estimators in parallel using Joblib.
For current built-in helper logic:
Parallel execution does not automatically reseed base estimators.
So reproducibility still depends on the estimators' own deterministic behavior and seed configuration.
Prediction column order
The shared prediction helper uses:
over predictions returned in estimator-list order.
To reproduce prediction-space geometry, preserve:
A different column order changes distances even when the same models are present.
Store estimator specifications
For repeatable experiments, keep an explicit ordered list:
and a matching parameter dictionary:
Do not rely on an unordered external configuration source to reconstruct the prediction-space columns.
Optimization reproducibility
Grid search is deterministic when its candidate sequence and objective are deterministic.
The current grid optimizer enumerates:
and evaluates candidates in that order.
Grid-search ties
If multiple candidates have exactly the same minimum risk, the source selects the middle matching candidate index:
This tie rule is deterministic for a fixed candidate order.
Preserve candidate ordering
Because parameter-vector meaning follows dictionary insertion order when the grid is built, preserve the construction order of custom multi-parameter grids.
For two-parameter MixCOBRA, the intended order is:
A different mapping order can change how candidate vectors are interpreted.
Default generated grids
When custom candidate arrays are absent, the current source uses deterministic NumPy grids such as:
So the default grid itself contains no randomness.
Gradient optimization
The deterministic status of gradient optimization depends on the configured gradient estimator.
Current methods are:
Deterministic numerical gradient methods
For a deterministic objective, these source methods are normally deterministic:
The parallel variant changes execution strategy but computes the same coordinate-wise central-difference formula.
SPSA is a reproducibility exception
The current SPSA helper uses:
from NumPy's global random state.
It does not accept:
and is not seeded from:
or:
Current source limitation
A fixed estimator random_state does not make
gradient_method="spsa" reproducible by itself.
Reproducing SPSA manually
If you choose to use the current SPSA implementation and need repeatable global NumPy draws, the source itself does not provide a dedicated estimator argument for that seed.
The observable implementation uses the global:
state.
A more robust package-level solution would require a source change to pass a dedicated RNG or seed into the SPSA helper.
This documentation does not assume such a change exists today.
Gradient initialization
The high-level GradientCOBRA path supplies:
and two-parameter MixCOBRA supplies:
so those starting points are deterministic.
Learning-rate schedules
The current learning-rate schedules are deterministic mathematical functions of iteration count and the initial gradient norm.
Therefore they do not introduce randomness themselves.
Randomness from the objective still matters
Even a deterministic optimizer becomes non-reproducible if the objective changes from call to call.
For COBRA, that can happen if stochastic components are refitted between objective evaluations.
The current optimization path normally works with already-fitted estimators, stored calibration geometry, and stored CV folds, so it does not refit the base estimator pool on every optimization step.
That design helps keep the objective stable within one fitted run.
Random state in GradientCOBRA
A single:
currently influences at least:
automatic first-level splitter
KFoldCV construction
optimizer parameter dictionaries where forwarded.
It does not automatically seed every base estimator or SPSA.
Random state in MixCOBRA
Similarly:
is passed into:
but base-estimator and SPSA reproducibility remain separate concerns.
Random state in CombinedClassifier
CombinedClassifier.random_state is used for:
Its base classifier pool must still be considered separately if any configured classifier is stochastic.
Reproducibility with custom split data
If you create a split externally, save the indices.
Example:
Then reconstruct:
and fit with:
This is more robust than relying only on a remembered random seed if external data ordering might later change.
Data order is part of reproducibility
A fixed seed does not compensate for changing input row order.
Several random splitters begin with:
and then shuffle those indices.
If the rows associated with those indices change, the same shuffled index sequence selects different observations.
Preserve both:
Feature order is part of reproducibility
Distance-based COBRA behavior depends on coordinate order and meaning.
For precomputed predictions, preserve model-column order.
For raw input features, preserve feature order and preprocessing.
The package often converts tabular data to NumPy arrays, so external DataFrame column names are not a persistent source of alignment inside the fitted model.
Precomputed prediction matrices
For reproducible precomputed workflows, save:
Example:
and separately record:
Prediction-space width affects normalization
GradientCOBRA and MixCOBRA scale prediction space using a denominator that contains:
So adding, removing, or reordering estimator columns can change more than just the raw geometry.
It can also change the normalization constant.
Preserve the exact estimator/prediction-space definition across runs.
Preserve normalization configuration
GradientCOBRA reproducibility depends on:
MixCOBRA depends on:
Record those values explicitly if they differ from defaults.
Explicit calibration arrays and normalization
The current source computes some normalization constants from the main X or
y fit arguments even when explicit X_l/y_l calibration data are supplied.
So reproducibility requires preserving both:
Changing only the training target scale can change the normalized calibration prediction geometry.
Loss configuration
The optimizer's selected parameter can change when:
change.
For example:
produce different CV objectives.
Record the exact configured loss and parameters.
Kernel configuration
Preserve:
because the same distances can produce different weights.
Also preserve the adapter-related settings:
Distance configuration
Preserve:
including parameters such as:
for Minkowski distance.
Different geometry changes CV scores and selected optimization parameters even with identical random seeds.
Cross-validation configuration
At minimum, preserve:
For lower-level custom CV use, also preserve:
Number of folds and data size
Changing:
changes fold membership and therefore the objective.
Even with the same seed, a different fold count gives a different optimization problem.
Software versions matter
The project source relies on:
in different components.
The source does not pin runtime versions inside fitted model objects.
For repeatable experiments across machines or time, preserve the environment outside the estimator.
Examples include:
Floating-point reproducibility
The source performs many floating-point operations:
The package does not promise bit-for-bit identical floating-point results across all hardware, BLAS libraries, thread counts, or dependency versions.
Use tolerance-based comparisons when validating numeric results:
rather than expecting byte-identical arrays in every environment.
Parallel execution can affect practical reproducibility
The package parallelizes base-estimator tasks through Joblib when:
The returned prediction-space order follows estimator-list order, but underlying estimators or numerical libraries can have their own concurrency behavior.
For the most controlled debugging setup, use:
and explicitly seed stochastic estimators.
Hamming distance uses Numba parallelism
The current Hamming implementation uses a Numba function with:
for its primary path.
That execution mode is independent of the estimator's:
For ordinary integer/discrete mismatch counts, the computation is structurally simple, but this is another example of package-wide computation not controlled by one central concurrency setting.
Reproducibility checklist for KFCProcedure
A source-aligned configuration might look like:
model = KFCRegressor(
divergences=[
"euclidean",
],
local_model="random_forest_regressor",
local_model_params={
"random_state": 42,
},
combiner=prebuilt_combiner,
random_state=42,
)
Important pieces to preserve:
input row order
random_state
divergence order
local model name
local model parameters
combiner configuration
n_clusters
max_iter
tol.
Reproducibility checklist for GradientCOBRA
model = GradientCOBRA(
estimators=[
"linear_regression",
"random_forest_regressor",
"svr",
],
estimators_params={
"random_forest_regressor": {
"random_state": 42,
},
},
distance="euclidean",
kernel="rbf",
loss="mse",
optimizer="grid",
opt_method="grid",
n_cv=5,
n_jobs=1,
random_state=42,
)
This controls the package-level split/CV seed and explicitly seeds the stochastic base estimator in this example.
Reproducibility checklist for MixCOBRA
Preserve:
estimator list and order
estimator-specific seeds
random_state
distance
kernel
normalization settings
one_parameter
alpha/beta candidate arrays
optimizer and opt_method
n_cv
n_jobs.
For gradient optimization, also preserve:
Reproducibility checklist for CombinedClassifier
Preserve:
classifier pool and order
classifier-specific seeds
random_state
distance
kernel
aggregator
loss
bandwidth candidate list
n_cv
n_jobs
class encoding.
Class encoding matters especially because the current default loss is:
on hard class labels.
Class encoding is part of reproducibility
For the current CombinedClassifier default objective, class labels are used numerically by MSE.
Changing encoding from:
to:
changes the CV loss geometry even if class membership is otherwise identical.
Preserve the exact target representation.
Inspect seeds after construction
KFC:
Standalone COBRA:
Fitted components can also be inspected individually where the source stores their seed.
Compare KFC splits indirectly
KFCProcedure.fit() does not retain:
as top-level fitted attributes.
They are local variables inside fit().
Therefore the current source does not provide direct post-fit access to the exact top-level split arrays.
For strict experiment auditing, preserve external data and reproduce the same
train_test_split() call yourself if you need explicit split indices.
Compare COBRA splits directly
Standalone COBRA estimators do retain:
So you can compare resolved datasets between runs:
Compare prediction spaces
GradientCOBRA:
CombinedClassifier:
MixCOBRA:
np.allclose(
model_a.X_l_norm_,
model_b.X_l_norm_,
)
np.allclose(
model_a.Y_l_norm_,
model_b.Y_l_norm_,
)
Compare CV folds
for a, b in zip(
model_a.cv_folds_,
model_b.cv_folds_,
):
assert np.array_equal(
a.train_idx,
b.train_idx,
)
assert np.array_equal(
a.eval_idx,
b.eval_idx,
)
This isolates split/fold reproducibility from later optimization.
Compare optimization results
GradientCOBRA:
MixCOBRA:
print(
model_a.optimization_outputs_[
"params"
]
)
print(
model_b.optimization_outputs_[
"params"
]
)
Also compare:
when debugging differences.
Compare final predictions
For numeric regression:
For hard classification:
Debugging a reproducibility difference
A useful source-aligned sequence is:
1. compare input row and column order
2. compare first-level split or explicit calibration data
3. compare fitted base estimators / local models
4. compare prediction-space matrices
5. compare normalization constants
6. compare distance matrices
7. compare CV folds
8. compare optimizer history
9. compare selected parameters
10. compare final predictions.
This locates the first stage at which two runs diverge.
Stage 1 — input identity
Check:
or use tolerance-based comparison for floating-point input pipelines.
Stage 2 — resolved split
Standalone COBRA:
Stage 3 — prediction space
GradientCOBRA:
If this fails while the split matches, investigate base-estimator randomness or column configuration.
Stage 4 — normalization constants
or for MixCOBRA:
print(
model_a.normalize_constant_x_,
model_b.normalize_constant_x_,
)
print(
model_a.normalize_constant_y_,
model_b.normalize_constant_y_,
)
Stage 5 — distance geometry
GradientCOBRA:
MixCOBRA can compare its separate X/Y distance matrices.
Stage 6 — CV folds
Inspect:
as shown above.
If these differ with identical calibration data, investigate:
Stage 7 — optimization history
For grid search, identical objective geometry should normally give identical candidate history.
For gradient mode, compare:
If using SPSA, remember the global NumPy RNG exception.
random_state=None
When the user leaves:
different components follow the behavior of their underlying RNG APIs.
In practical terms, this does not provide the same reproducibility guarantee as a fixed integer seed.
For repeatable experiments, use an explicit integer wherever the source exposes one.
Same integer does not imply one shared random stream
The package passes the same integer seed into several independently initialized random-number generators.
That means each component starts its own deterministic sequence.
It does not mean:
This is often desirable because changing one component does not necessarily consume random numbers from another component's stream.
Source-level RNG summary
| Component | Current RNG mechanism | Uses estimator seed? |
|---|---|---|
| KFC top-level split | sklearn train_test_split |
Yes |
| BregmanKMeans | check_random_state |
Yes |
| OverlapSplitter | np.random.default_rng |
Yes |
| RandomHoldoutSplitter | sklearn train_test_split |
Yes |
| KFoldCV | np.random.RandomState |
Yes |
| StratifiedKFoldCV | np.random.default_rng |
Yes |
| TimeSeriesCV | none | not needed |
| Grid search | deterministic enumeration | not needed |
| central/forward/complex gradient | deterministic if objective is | not needed |
| parallel central gradient | deterministic formula if objective is | not needed |
| SPSA gradient | global np.random.choice |
No |
| COBRA base estimators | estimator-specific | not automatically |
| F-Step string local models | injected when supported | Yes |
| C-Step string combiners | injected, constructor-dependent | Yes |
Current-source caveats
| Area | Current behavior |
|---|---|
| one global package RNG | No |
| same RNG API everywhere | No |
| COBRA base estimators auto-seeded | No |
| F-Step string model seed injection | Yes |
| custom F-Step object auto-seeded | No |
| C-Step string combiner seed injection | Yes |
SPSA tied to model random_state |
No |
| explicit aggregation split reproducible by stored arrays | Yes |
| precomputed mode removes internal estimator fitting | Yes |
| KFC stores split arrays after fit | No |
| standalone COBRA stores split arrays | Yes |
| grid search candidate order deterministic | Yes |
| data order influences seeded splits | Yes |
| prediction-column order influences result | Yes |
Minimal reproducibility recipe
A compact source-aligned recipe is:
1. set an explicit model random_state;
2. explicitly seed stochastic base/local estimators;
3. use n_jobs=1 while debugging;
4. preserve exact row and feature/prediction-column order;
5. save explicit train/calibration indices when important;
6. preserve estimator/kernel/distance/loss/optimizer configuration;
7. avoid SPSA when model-level seed reproducibility is required, or account for
its separate global NumPy RNG behavior;
8. record package and dependency versions;
9. compare intermediate fitted state when runs differ.
Mental model
random_state makes several important package components repeatable, but
reproducibility is a pipeline property rather than a single-seed switch.
For KFC:
For standalone COBRA:
while:
and:
remain separate concerns in the current source.