pyntbci.classifiers.UnsupervisedRCCA

class pyntbci.classifiers.UnsupervisedRCCA(stimulus: NDArray, fs: int, event: str = 'duration', onset_event: bool = True, decoding_length: float = None, decoding_stride: float = None, encoding_length: float | list[float] = 0.3, encoding_stride: float | list[float] = None, latency: NDArray = None, tmin: float = 0, n_components: int = 1, gamma_x: float | list[float] | NDArray = None, gamma_m: float | list[float] | NDArray = None, alpha_x: float = None, alpha_m: float = None, cumulative: bool = True, confidence: bool = False, posthoc: bool = False, response_prior: NDArray = None, response_prior_gamma: float = 1.0)[source]

Unsupervised adaptive reconvolution CCA classifier for calibration-free c-VEP decoding [6].

Instead of a supervised calibration, each trial is decoded by fitting a separate rCCA per candidate stimulus (as a hypothesis) and selecting the stimulus whose model best fits the trial, i.e. yields the highest correlation between the spatially filtered EEG and the temporally filtered stimulus structure matrix. This is the instantaneous mode (cumulative=False), which treats every trial independently.

Three cumulative extensions build a model from previously decoded trials, using their predicted labels as pseudo-labels (there are no ground-truth labels in a calibration-free setting):

  • cumulative=True: every hypothesis is fit on all previously decoded trials (at their pseudo-labels) plus the current trial (hypothesized as each candidate). This is mathematically identical to refitting from scratch on the full history every trial, but is done efficiently by keeping a single running covariance of the pseudo-labeled history (see RunningCovariance in utilities) shared across all hypotheses, so each trial only adds its own (bounded) contribution rather than reprocessing the whole history. The first trial, with no history, reduces to the instantaneous mode.

  • confidence=True (implies cumulative): each trial is weighted by a confidence, so that high-confidence trials drive the model updates and low-confidence trials are suppressed. The confidence is the normalized correlation margin (rho_winner - rho_runner_up) / std(rho_except_winner), estimated from an instantaneous pass (as in [6]), and used as a per-trial weight in the running covariance.

  • posthoc=True (implies cumulative): after each trial, all previously decoded trials are re-decoded with the just-updated (presumably better) model and their pseudo-labels are corrected, which then affects subsequent updates. This is the only mode that must retain the past trials’ EEG (in X_hist_), since re-decoding needs the raw data; a changed label is applied to the running covariance as an exact remove-then-re-add, avoiding a full refit. The other modes keep no raw data (only the running covariance and the list of pseudo-labels).

These flags reproduce the four variants of [6]: instantaneous (all False), cumulative (cumulative), confidence-weighted cumulative (cumulative, confidence), and confidence-weighted cumulative with post hoc re-analysis (cumulative, confidence, posthoc).

Note, decoding is inherently online and stateful: predict() streams trials in their given (chronological) order, decoding each with the model learned from the ones before it, and the internal session persists across calls. Decoding trials one at a time therefore accumulates exactly as decoding them in one call does (as needed for real-time use where trials arrive one by one): [predict(X[[i]]) for i in range(n_trials)] gives the same result as predict(X). Because state persists, predict()/decision_function() are not pure functions; call fit() (or pass reset=True) to start a fresh session, e.g. before an independent replay. The single-trial partial_fit_predict() is the same online step exposed directly. The structure-matrix machinery of rCCA (event and encoding matrices, latency correction, optional spatio-spectral decoding matrix) is reused, and the core CCA is solved with the same _solve_cca as CCA in transformers. With short trials or a wide encoding matrix, the per-hypothesis covariances can be ill-conditioned; set gamma_x/gamma_m (or alpha_x/alpha_m) to regularize, as for supervised rCCA.

Parameters:
  • stimulus (NDArray) – The stimulus used for stimulation of shape (n_classes, n_samples). Should be sampled at fs. One cycle (i.e., one stimulus-repetition) is sufficient.

  • fs (int) – The sampling frequency of the EEG data in Hz.

  • event (str (default: "duration")) – The event definition to map stimulus to events.

  • onset_event (bool (default: True)) – Whether to add an event for the onset of stimulation. Added as last event.

  • decoding_length (float (default: None)) – The length of the spectral filter for each data channel in seconds. If None, it is set to 1/fs, equivalent to 1 sample, such that no phase-shifting is performed and thus no (spatio-)spectral filter is learned.

  • decoding_stride (float (default: None)) – The stride of the spectral filter for each data channel in seconds. If None, it is set to 1/fs.

  • encoding_length (float | list[float] (default: 0.3)) – The length of the transient response(s) for each of the events in seconds.

  • encoding_stride (float | list[float] (default: None)) – The stride of the transient response(s) for each of the events in seconds. If None, it is set to 1/fs.

  • latency (NDArray (default: None)) – The raster latencies of each of the classes of shape (n_classes,) that the templates need to be corrected for.

  • tmin (float (default: 0)) – The start of stimulation in seconds. Can be used if there was a delay in the marker.

  • n_components (int (default: 1)) – The number of CCA components to use. Decoding and confidence use the first component only, matching [6].

  • gamma_x (float | list[float] | NDArray (default: None)) – Regularization on the covariance matrix for CCA along X (channels), see rCCA.

  • gamma_m (float | list[float] | NDArray (default: None)) – Regularization on the covariance matrix for CCA along M (samples), see rCCA.

  • alpha_x (float (default: None)) – Amount of variance to retain in computing the inverse of the covariance matrix of X. If None, all variance.

  • alpha_m (float (default: None)) – Amount of variance to retain in computing the inverse of the covariance matrix of M. If None, all variance.

  • cumulative (bool (default: True)) – Whether to learn cumulatively from previously decoded trials (using their pseudo-labels). If False, each trial is decoded instantaneously and independently.

  • confidence (bool (default: False)) – Whether to weight each trial by its confidence during cumulative updates. Implies cumulative.

  • posthoc (bool (default: False)) – Whether to re-decode and relabel all previous trials after each update. Implies cumulative, and retains the past trials’ EEG in X_hist_.

  • response_prior (NDArray (default: None)) – A prior on the expected transient response (e.g. a flash-VEP: a negative peak near 75 ms, a positive peak near 100 ms, and a negative peak near 125 ms), sampled at fs. Either one response of length n_event_samples (applied to every event) or the full concatenation of the per-event responses of length n_features (matching the temporal filter r_; see encoding_length). If given, the learned response is softly regularized toward it (see response_prior_gamma), which anchors the response’s absolute phase. This is what makes decoding work for circularly-shifted codes (e.g. shifted m-sequences): without it, an unconstrained response can circularly slide to make every candidate stimulus fit equally well (the more so the longer encoding_length), so the classes become indistinguishable. If None (default), no prior is used.

  • response_prior_gamma (float (default: 1.0)) – The strength of the soft regularization toward response_prior, ranging from 0 (ignore the prior, purely data-driven) upwards (larger pulls the response more strongly toward the prior; in the limit the response equals the prior). Only used if response_prior is not None.

classes_

The class labels of shape (n_classes,).

Type:

NDArray

events_

The list of events used to map the stimulus to, as set by the internal rCCA.

Type:

list

labels_

The pseudo-labels (predicted labels) of the decoded trials, in order.

Type:

list

confidences_

The confidence of each decoded trial, in order.

Type:

list

w_

The spatial filter of the most recently winning model of shape (n_channels, n_components).

Type:

NDArray

r_

The temporal filter of the most recently winning model of shape (n_features, n_components).

Type:

NDArray

cov_

The running covariance of the pseudo-labeled history (only populated if cumulative).

Type:

RunningCovariance

X_hist_

The (decoded) EEG of the decoded trials, retained only if posthoc, for re-decoding.

Type:

list

References

decision_function(X: NDArray, reset: bool = False) NDArray[source]

Decode a sequence of trials online and return the per-trial per-class correlation scores.

Stateful and online, see predict() (of which this is the score-returning counterpart).

Parameters:
  • X (NDArray) – The EEG data of shape (n_trials, n_channels, n_samples), in chronological order.

  • reset (bool (default: False)) – Whether to discard the current online session and start fresh before decoding X, see predict().

Returns:

scores – The per-trial per-class correlation scores of shape (n_trials, n_classes).

Return type:

NDArray

fit(X: NDArray = None, y: NDArray = None) ClassifierMixin[source]

Set up the classifier (calibration-free: no training data or labels are used).

Parameters:
  • X (NDArray (default: None)) – Not used, present for scikit-learn API consistency.

  • y (NDArray (default: None)) – Not used, present for scikit-learn API consistency.

Returns:

self – Returns the instance itself.

Return type:

ClassifierMixin

partial_fit_predict(X: NDArray) tuple[int, float, NDArray][source]

Decode a single trial online, updating the model (if cumulative) with its pseudo-label.

Parameters:

X (NDArray) – The EEG data of a single trial of shape (n_channels, n_samples) or (1, n_channels, n_samples).

Returns:

  • label (int) – The predicted (pseudo-) label of the trial.

  • confidence (float) – The confidence of the prediction.

  • scores (NDArray) – The per-class correlation scores of shape (n_classes,).

predict(X: NDArray, reset: bool = False) NDArray[source]

Decode a sequence of trials online, in the given (chronological) order.

Each trial is decoded with the model learned from all trials decoded so far, and then folds into that model (using its own prediction as a pseudo-label) if cumulative. This is a stateful, online operation: the internal session persists across calls, so decoding trials one at a time is equivalent to decoding them in a single call, i.e. [predict(X[[i]]) for i in range(n_trials)] gives the same result as predict(X), as needed for real-time use where trials arrive one by one. Because it persists state, predict() is not a pure function: call fit() (or pass reset=True) to start a fresh session, e.g. before an independent replay.

Parameters:
  • X (NDArray) – The EEG data of shape (n_trials, n_channels, n_samples), in chronological order.

  • reset (bool (default: False)) – Whether to discard the current online session and start fresh before decoding X. Use reset=True (or a fresh instance, or fit()) for a self-contained replay; leave False to continue an ongoing session.

Returns:

y – The predicted labels of shape (n_trials,).

Return type:

NDArray

set_decision_function_request(*, reset: bool | None | str = '$UNCHANGED$') UnsupervisedRCCA

Configure whether metadata should be requested to be passed to the decision_function method.

Note that this method is only relevant when this estimator is used as a sub-estimator within a meta-estimator and metadata routing is enabled with enable_metadata_routing=True (see sklearn.set_config()). Please check the User Guide on how the routing mechanism works.

The options for each parameter are:

  • True: metadata is requested, and passed to decision_function if provided. The request is ignored if metadata is not provided.

  • False: metadata is not requested and the meta-estimator will not pass it to decision_function.

  • None: metadata is not requested, and the meta-estimator will raise an error if the user provides it.

  • str: metadata should be passed to the meta-estimator with this given alias instead of the original name.

The default (sklearn.utils.metadata_routing.UNCHANGED) retains the existing request. This allows you to change the request for some parameters and not others.

Added in version 1.3.

Parameters:

reset (str, True, False, or None, default=sklearn.utils.metadata_routing.UNCHANGED) – Metadata routing for reset parameter in decision_function.

Returns:

self – The updated object.

Return type:

object

set_predict_request(*, reset: bool | None | str = '$UNCHANGED$') UnsupervisedRCCA

Configure whether metadata should be requested to be passed to the predict method.

Note that this method is only relevant when this estimator is used as a sub-estimator within a meta-estimator and metadata routing is enabled with enable_metadata_routing=True (see sklearn.set_config()). Please check the User Guide on how the routing mechanism works.

The options for each parameter are:

  • True: metadata is requested, and passed to predict if provided. The request is ignored if metadata is not provided.

  • False: metadata is not requested and the meta-estimator will not pass it to predict.

  • None: metadata is not requested, and the meta-estimator will raise an error if the user provides it.

  • str: metadata should be passed to the meta-estimator with this given alias instead of the original name.

The default (sklearn.utils.metadata_routing.UNCHANGED) retains the existing request. This allows you to change the request for some parameters and not others.

Added in version 1.3.

Parameters:

reset (str, True, False, or None, default=sklearn.utils.metadata_routing.UNCHANGED) – Metadata routing for reset parameter in predict.

Returns:

self – The updated object.

Return type:

object

set_score_request(*, sample_weight: bool | None | str = '$UNCHANGED$') UnsupervisedRCCA

Configure whether metadata should be requested to be passed to the score method.

Note that this method is only relevant when this estimator is used as a sub-estimator within a meta-estimator and metadata routing is enabled with enable_metadata_routing=True (see sklearn.set_config()). Please check the User Guide on how the routing mechanism works.

The options for each parameter are:

  • True: metadata is requested, and passed to score if provided. The request is ignored if metadata is not provided.

  • False: metadata is not requested and the meta-estimator will not pass it to score.

  • None: metadata is not requested, and the meta-estimator will raise an error if the user provides it.

  • str: metadata should be passed to the meta-estimator with this given alias instead of the original name.

The default (sklearn.utils.metadata_routing.UNCHANGED) retains the existing request. This allows you to change the request for some parameters and not others.

Added in version 1.3.

Parameters:

sample_weight (str, True, False, or None, default=sklearn.utils.metadata_routing.UNCHANGED) – Metadata routing for sample_weight parameter in score.

Returns:

self – The updated object.

Return type:

object