BCILattice Advanced Blocks
Complex Structures, Domain Adaptation, Domain Incremental Learning, and DL Training blocks available in the ML Suite (MLFlow) and Workflow Designer canvases.
Overview
Beyond the general-purpose Custom Blocks, BCILattice injects a much larger set of advanced structural and research blocks covering graph control flow, cross-subject/cross-session domain adaptation, continual (domain-incremental) learning, and full PyTorch-style deep-learning training primitives. These are the blocks that let the ML Suite and Workflow Designer express arbitrary research pipelines — branching, looping, ensembling, adapting between subjects, and training deep models — without writing code.
Like the blocks in the Custom Blocks reference, these are marked is_custom: true and run as server-side routines rather than catalog classes/functions. Click a block on the canvas and use the Documentation link in its property panel to jump straight back to the matching section below.
The block family at a glance
Counting the everyday palette blocks alongside the advanced ones, BCILattice declares and maintains 119 app-owned blocks across 14 categories. These are distinct from the ~11,300 catalog entries introspected out of third-party libraries: an app-owned block is code this application writes, tests and ships.
| Category | Blocks | core | advanced |
|---|---|---|---|
| DL Training | 33 | 8 | 25 |
| Training | 17 | 8 | 9 |
| Complex Structures | 12 | 0 | 12 |
| Evaluation | 11 | 2 | 9 |
| Pipeline Blocks | 8 | 4 | 4 |
| Domain Adaptation | 8 | 4 | 4 |
| Domain Incremental Learning | 8 | 0 | 8 |
| Model Structure | 7 | 1 | 6 |
| Preprocessing | 4 | 4 | 0 |
| Analysis | 3 | 2 | 1 |
| Reinforcement Learning | 3 | 0 | 3 |
| Graph Learning | 2 | 0 | 2 |
| Generative Models | 2 | 0 | 2 |
| Inputs | 1 | 1 | 0 |
core blocks sit at the front of their palette category on first run; advanced ones stay behind the Advanced toggle and remain searchable. The split is a design-time declaration, not a runtime guess. Four blocks are additionally marked community and two legacy; both are counted under “advanced” above.
Engines and plan gates
116 of the 119 run on both engines — the ML Flow canvas and the Workflow Designer. The three exceptions are workflow-only by design: Select Files, Model Set and Sub-Workflow describe how a workflow is assembled rather than what a pipeline computes.
118 of the 119 are free on every plan. The single gated block is Custom Code, which executes arbitrary Python you supply. Domain adaptation, continual learning, federated averaging, conformal prediction, NAS and the RL trainers are not upsells — they ship to everyone.
Why the count is trustworthy
A block on the canvas is not one artefact, it is five, and they live in five different files:
- a name constant
- a schema, which defines its ports and settings
- an executor branch that actually runs it
- a palette row, so it can be placed at all
- a test that runs it end to end
Nothing used to tie those five together, so a block could have four of them for months and still be impossible to place or impossible to run — which is exactly what happened to the eight Domain Adaptation blocks and the eight Domain Incremental ones, and independently to the DL Training optimizers, losses and schedulers.
A declaration a test can check
Every app-owned block is declared in one contract module, and a contract test probes the running server — the real executor dispatch and the real palette — rather than trusting the declaration. A block whose reality and declaration disagree fails the build in either direction: an undeclared block is as much a defect as an undelivered one. The allow-list of known-incomplete blocks is a shrink-only ratchet, and it currently stands at zero.
Complex Structures
Graph control-flow blocks: branching, merging, splitting, ensembling and looping. Available in both the ML Suite and Workflow Designer canvases. Ports on the "n_inputs / n_outputs" family of blocks below (Data Merger, Ensemble Voter, Feature Concatenator, Model Selector, Stacking Assembler, Data Splitter, Parallel Fan-out, Multi-Head Output, Pipeline Switch, Metrics Bundle) are dynamic — changing the corresponding count setting adds or removes numbered ports (1–12) on the block.
Conditional Branch
Routes incoming data down one of two branches based on a condition evaluated against a value. Supports simple threshold/comparison modes as well as a free-form expression.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| condition | str | value > 0.5 | Expression evaluated when mode is "expression" |
| mode | select | threshold | threshold · equals · contains · greater · less · expression |
| threshold | float | 0.5 | — |
Data Splitter
Splits a single incoming array/dataset into 1–12 output splits by ratio, equal partition, or explicit index.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| n_splits | int | 2 | Number of output splits (dynamic port count) |
| split_ratios | str | 0.5,0.5 | Comma-separated ratios, used when strategy is "ratio" |
| axis | int | 0 | — |
| strategy | select | ratio | ratio · equal · index |
| shuffle | bool | false | — |
Data Merger
Combines 1–12 upstream data sources into one output using concatenation, stacking, or an elementwise reduction (mean/sum/max/min). The classic block for fusing multiple channels, sessions, or feature sets back into a single stream.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| n_inputs | int | 4 | Number of input ports (dynamic, 1–12) |
| axis | int | 0 | — |
| method | select | concatenate | concatenate · stack · mean · sum · max · min |
Ensemble Voter
Aggregates predictions from up to 12 models into one final prediction via majority vote, soft (probability-averaged) vote, weighted vote, or a stacking meta-combination. Also reports a confidence score.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| n_inputs | int | 4 | — |
| strategy | select | soft | majority · soft · weighted · stacking |
| weights | str | — | Comma-separated weights, used when strategy is "weighted" |
| n_classes | int | 0 | — |
Pipeline Switch
Routes data to one of up to 12 output routes based on a lookup of a config key against a case table — effectively a graph-level switch/case statement.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| n_routes | int | 3 | — |
| switch_key | str | mode | Key read from Config to decide the route |
| default_route | select | 1 | 1 · 2 · 3 · 4 · 5 · 6 |
| cases | str | a->1,b->2,c->3 | value->route mapping |
Parallel Fan-out
Duplicates a single input into up to 12 identical output copies so the same data can feed several independent downstream branches.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| n_copies | int | 3 | — |
Feature Concatenator
Concatenates up to 12 feature vectors/arrays along a chosen axis, with an option to flatten every input first.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| n_inputs | int | 5 | — |
| axis | int | -1 | — |
| flatten_all | bool | false | — |
Model Selector
Picks the best of up to 12 candidate models by a metric score, using a max or min comparison against the chosen evaluation set.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| n_inputs | int | 3 | — |
| metric_mode | select | max | max · min |
| selection_criteria | select | validation | validation · test · cross-val |
Stacking Assembler
Builds a stacked ensemble: trains a meta-learner on the out-of-fold predictions of up to 12 base models. Standard technique for squeezing extra accuracy out of several already-trained models.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| n_inputs | int | 4 | — |
| cv_folds | int | 5 | — |
| use_proba | bool | true | — |
| passthrough | bool | false | Also feed original features to the meta-learner |
Multi-Head Output
Splits a shared feature trunk into up to 12 independent output heads, each with its own hidden size and activation — for multi-task models (e.g. predicting class + confidence + subject ID together).
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| n_heads | int | 3 | — |
| head_dims | str | 128,64,32 | Comma-separated hidden size per head |
| activation | select | relu | relu · tanh · sigmoid · none |
| dropout | float | 0 | — |
ForEach
Re-runs everything wired downstream of this block once per item in an incoming list (e.g. once per model in a Model Set, or once per hyperparameter combination). Each iteration binds item_var to the current item, so downstream blocks can reference it. Supports parallel workers, per-item timeouts, and stop-on-error or continue-on-error handling. Pair with ForEach Collect to rank the per-item results.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| item_var | str | model | Variable name each downstream block can reference for the current item |
| on_error | select | stop | stop · continue |
| body_workers | int | 0 | 0 = sequential; >0 runs iterations concurrently |
| item_timeout_s | float | 0 | 0 = no timeout |
ForEach Collect
Collects the per-iteration outputs of a ForEach loop, ranks them by a chosen metric key, and surfaces the best result plus a full comparison table.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| metric_key | str | accuracy | Key inside each item used to rank results |
| sort | select | desc | desc · asc |
Workflow Structure
Workflow-only structural blocks for composing larger graphs out of existing experiments and already-published workflows.
Model Set
Selects one or more existing MLflow/Experiment Hub experiments and emits them as a list of items — designed to feed directly into a ForEach loop so you can, for example, run the same evaluation pipeline once per selected model.
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| experiment_ids | str | — | Serialised list of chosen experiment IDs |
Sub-Workflow
Embeds another workflow's compiled, published graph as a single reusable block. It always references the published snapshot — never the other workflow's live/in-progress canvas — so editing that other workflow later never silently changes a graph that already embeds it.
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| workflow_name | str | — | Name of the published workflow to embed |
Domain Adaptation
Blocks for aligning data or models across subjects, sessions, or recording devices — the core problem in BCI transfer learning, where a model trained on one subject/session often degrades on another.
MMD Aligner
Minimizes Maximum Mean Discrepancy between source and target feature distributions in a kernel space, reducing the statistical gap between two subjects/sessions before training.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| kernel | select | rbf | rbf · linear · poly |
| n_components | int | 64 | — |
| lambda_reg | float | 1 | — |
| bandwidth | float | 1 | — |
CORAL Adapter
Correlation Alignment: re-colors the source feature covariance to match the target's second-order statistics, a lightweight closed-form domain adaptation technique.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| lambda_reg | float | 1 | — |
Transfer Component Analysis
Learns a shared low-dimensional projection where source and target distributions become similar, using kernel-based transfer components (TCA).
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| n_components | int | 30 | — |
| mu | float | 1 | — |
| kernel | select | rbf | rbf · linear · poly |
| gamma | float | 1 | — |
DANN Discriminator
Domain-Adversarial Neural Network discriminator: a gradient-reversal classifier trained to fail at telling source and target features apart, forcing the upstream feature extractor to learn domain-invariant representations.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| hidden_dim | int | 256 | — |
| n_layers | int | 2 | — |
| lambda_grl | float | 1 | Gradient reversal strength |
| dropout | float | 0.5 | — |
Feature Alignment
Simple statistical feature alignment (z-score, whitening, centering, Procrustes, or affine) between source and target — a fast baseline before reaching for MMD/CORAL/DANN.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| method | select | z-score | z-score · whitening · centering · procrustes · affine |
| n_components | int | 0 | — |
Subject Adaptation
Adapts a base model to a new subject using a small amount of that subject's calibration data, picking from several adaptation strategies (fine-tuning, DANN, TCA, Euclidean alignment, or CORAL).
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| method | select | fine-tune | fine-tune · DANN · TCA · euclidean-alignment · CORAL |
| epochs | int | 10 | — |
| lr | float | 0.0001 | — |
| n_shots | int | 20 | Number of calibration samples used |
| device | select | auto | auto · cpu · cuda |
Euclidean Alignment
Whitens trial covariance matrices toward a reference (mean/median/first trial), a fast and widely used EEG-specific alignment technique that requires no labels.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| reference_mode | select | mean | mean · median · first |
Session Transfer
Transfers features across recording sessions of the same or different subjects via reweighting, projection, normalization, or optimal transport, and outputs per-sample importance weights alongside the transferred features.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| method | select | projection | reweighting · projection · normalization · optimal-transport |
| n_components | int | 0 | — |
Domain Incremental Learning
Continual-learning blocks that let a model keep learning from new subjects/sessions/tasks over time without catastrophically forgetting what it already knew.
EWC Regularizer
Elastic Weight Consolidation: penalizes changes to parameters that were important for previous tasks, estimated via the Fisher information matrix, so the model resists forgetting while learning a new task.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| lambda_ewc | float | 400 | Strength of the consolidation penalty |
| fisher_samples | int | 200 | — |
| consolidate_every | int | 1 | — |
| online | bool | false | Use Online EWC (single accumulated penalty) instead of per-task terms |
| gamma | float | 1 | — |
Replay Buffer
Maintains a fixed-size memory of past examples (random, herding, exemplar, or gradient-based selection) that can be replayed alongside new data to reduce forgetting.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| buffer_size | int | 500 | — |
| strategy | select | random | random · herding · exemplar · gradient |
| n_classes | int | 4 | — |
| update_policy | select | reservoir | reservoir · ring · fifo |
Continual Trainer
Trains a model on a new domain while mixing in replayed data and an optional regularizer (EWC, L2, or Synaptic Intelligence) to control forgetting — the main training loop for incremental-learning pipelines.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| epochs | int | 5 | — |
| device | select | auto | auto · cpu · cuda |
| freeze_layers | int | 0 | — |
| replay_fraction | float | 0.5 | — |
| lr | float | 0.0001 | — |
| regularizer | select | none | none · ewc · l2 · synaptic-intel |
| lambda_reg | float | 1 | — |
Knowledge Distillation
Trains a student model to match a teacher model's softened output distribution (temperature-scaled) blended with the true labels — used to compress a large model or preserve old-task behavior while learning something new.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| temperature | float | 4 | Softens the teacher output distribution |
| alpha | float | 0.5 | Balance between distillation loss and true-label loss |
| epochs | int | 10 | — |
| lr | float | 0.0001 | — |
| device | select | auto | auto · cpu · cuda |
| distill_layers | str | — | Optional comma-separated intermediate layers to also match |
Task Manager
Maintains a set of per-domain/per-task output heads on top of a shared model and routes each sample to the right head, either from a known domain ID or inferred via entropy/prototype matching.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| n_domains | int | 5 | — |
| head_type | select | linear | linear · mlp · attention |
| shared_layers | int | -2 | Number of trunk layers shared across tasks |
| task_inference | select | known | known · entropy · prototype |
PackNet Freezer
Implements PackNet-style continual learning: prunes a fraction of a model's weights, retrains the pruned subnetwork on a new task, and freezes it, packing multiple tasks into disjoint weight subsets of one network.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| prune_ratio | float | 0.5 | — |
| task_id | int | 0 | — |
| retrain_epochs | int | 5 | — |
Progressive Layers
Expands a base model with new units/layers for a new domain while freezing the original network and adding lateral connections back to it, following the Progressive Neural Networks approach.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| n_new_units | int | 64 | — |
| lateral_connections | bool | true | — |
| freeze_previous | bool | true | — |
| n_layers_to_expand | int | 1 | — |
Domain Drift Detector
Monitors incoming data for statistical drift relative to a reference distribution (MMD, KS-test, covariate shift, ADWIN, or Page-Hinkley), flagging when it's time to trigger adaptation or retraining.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| method | select | mmd | mmd · ks-test · covariate-shift · adwin · page-hinkley |
| threshold | float | 0.05 | — |
| window_size | int | 100 | — |
| alpha | float | 0.05 | — |
DL Training
A full set of PyTorch-backed deep-learning primitives — optimizers, loss functions, LR schedulers, data loading, metrics, and a trainer block — so deep models can be assembled and trained entirely on the canvas.
Optimizers
Every optimizer block takes a Model input and produces an Optimizer output to wire into BCILattice Trainer.
Adam Optimizer
Adaptive moment estimation — maintains per-parameter running averages of the gradient and its square. The default choice for most DL Training pipelines.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| lr | float | 0.001 | Learning rate |
| beta1 | float | 0.9 | Exponential decay rate for the 1st moment estimate |
| beta2 | float | 0.999 | Exponential decay rate for the 2nd moment estimate |
| eps | float | 1e-8 | Numerical stability term |
| weight_decay | float | 0 | L2 penalty coefficient |
| amsgrad | bool | false | Use the AMSGrad variant |
AdamW Optimizer
Adam with decoupled weight decay — the decay term is applied directly to the weights instead of being folded into the gradient, which generalizes better for large models.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| lr | float | 0.001 | — |
| beta1 | float | 0.9 | — |
| beta2 | float | 0.999 | — |
| eps | float | 1e-8 | — |
| weight_decay | float | 0.01 | Decoupled L2 penalty (higher default than Adam) |
SGD Optimizer
Stochastic gradient descent with momentum and optional Nesterov acceleration. Slower to converge than Adam variants but often generalizes better on smaller EEG/fNIRS datasets.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| lr | float | 0.01 | — |
| momentum | float | 0.9 | — |
| weight_decay | float | 0 | — |
| nesterov | bool | true | Use Nesterov momentum |
| dampening | float | 0 | — |
RMSprop Optimizer
Divides the learning rate by a running average of recent gradient magnitudes. Well suited to recurrent/sequence models.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| lr | float | 0.01 | — |
| alpha | float | 0.99 | Smoothing constant |
| eps | float | 1e-8 | — |
| weight_decay | float | 0 | — |
| momentum | float | 0 | — |
Adamax Optimizer
Adam variant based on the infinity norm, which is more stable with sparse gradients or embeddings.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| lr | float | 0.002 | — |
| beta1 | float | 0.9 | — |
| beta2 | float | 0.999 | — |
| eps | float | 1e-8 | — |
| weight_decay | float | 0 | — |
NAdam Optimizer
Adam with Nesterov momentum baked into the moment update, often converging slightly faster than plain Adam.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| lr | float | 0.002 | — |
| beta1 | float | 0.9 | — |
| beta2 | float | 0.999 | — |
| eps | float | 1e-8 | — |
| weight_decay | float | 0 | — |
Loss Functions
Loss blocks take no data inputs — they configure and emit a Loss Fn to wire into BCILattice Trainer.
CrossEntropy Loss
Standard multi-class classification loss; combines log-softmax and negative log-likelihood in one block.
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| label_smoothing | float | 0 | Smooths hard 0/1 targets toward uniform |
| reduction | select | mean | mean · sum · none |
| ignore_index | int | -1 | Target value excluded from the loss |
BCE Loss
Binary cross-entropy for two-class problems, expects the model output already passed through a sigmoid.
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| reduction | select | mean | mean · sum · none |
BCEWithLogits Loss
Numerically stable binary cross-entropy that applies the sigmoid internally — use this instead of BCE Loss whenever the model outputs raw logits.
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| reduction | select | mean | mean · sum · none |
| pos_weight | float | 1 | Up-weights the positive class for imbalanced data |
MSE Loss
Mean squared error for continuous-target regression tasks (e.g. predicting a continuous BCI control signal).
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| reduction | select | mean | mean · sum · none |
L1 Loss
Mean absolute error — less sensitive to outliers than MSE, at the cost of a non-smooth gradient at zero.
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| reduction | select | mean | mean · sum · none |
Huber Loss
Quadratic near zero and linear beyond a threshold — a robust middle ground between MSE and L1 for noisy regression targets.
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| reduction | select | mean | mean · sum · none |
| delta | float | 1 | Threshold where the loss switches from quadratic to linear |
NLL Loss
Negative log-likelihood loss for use when the model already outputs log-probabilities (e.g. after a manual log-softmax).
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| reduction | select | mean | mean · sum · none |
| ignore_index | int | -1 | — |
Focal Loss
Down-weights easy, well-classified examples so training focuses on hard/rare examples — useful for imbalanced class distributions common in BCI event detection.
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| alpha | float | 0.25 | Balances the positive/negative class contribution |
| gamma | float | 2 | Focusing parameter; higher values down-weight easy examples more |
| reduction | select | mean | mean · sum · none |
LR Schedulers
Scheduler blocks wrap an incoming Optimizer and pass it through alongside the new Scheduler.
StepLR
Decays the learning rate by a fixed factor every N epochs.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| step_size | int | 10 | Epochs between each decay step |
| gamma | float | 0.1 | Multiplicative decay factor |
CosineAnnealingLR
Smoothly anneals the learning rate along a cosine curve down to eta_min over T_max epochs.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| T_max | int | 50 | Number of epochs for one full cosine cycle |
| eta_min | float | 0 | Minimum learning rate |
ReduceLROnPlateau
Reduces the learning rate when a monitored metric stops improving — the standard choice when you do not know a good decay schedule in advance.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| mode | select | min | min · max |
| factor | float | 0.1 | — |
| patience | int | 10 | Epochs to wait before reducing |
| threshold | float | 0.0001 | — |
| min_lr | float | 0 | — |
OneCycleLR
Ramps the learning rate up then back down within a single training run, often enabling faster convergence ("super-convergence").
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| max_lr | float | 0.01 | — |
| steps_per_epoch | int | 100 | — |
| epochs | int | 10 | — |
| pct_start | float | 0.3 | Fraction of the cycle spent increasing the LR |
ExponentialLR
Decays the learning rate by a constant multiplicative factor every epoch.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| gamma | float | 0.95 | Per-epoch decay factor |
CyclicLR
Cycles the learning rate between a lower and upper bound, which can help escape saddle points during training.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| base_lr | float | 0.001 | — |
| max_lr | float | 0.01 | — |
| step_size_up | int | 2000 | — |
| mode | select | triangular | triangular · triangular2 · exp_range |
CosineWarmRestarts
Cosine annealing with periodic warm restarts — the cycle length can grow by T_mult after each restart.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| T_0 | int | 10 | Epochs until the first restart |
| T_mult | int | 2 | Cycle-length multiplier after each restart |
| eta_min | float | 0 | — |
PyTorch DataLoader
Wraps a dataset in a PyTorch DataLoader with batching, shuffling, worker processes, and pinned memory — the standard entry point for feeding batched data into a deep-learning training run.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| batch_size | int | 32 | — |
| shuffle | bool | true | — |
| num_workers | int | 0 | — |
| pin_memory | bool | false | — |
| drop_last | bool | false | — |
| prefetch_factor | int | 2 | — |
Train/Val Splitter
Splits a dataset into train and validation sets using a random, stratified, subject-wise, session-wise, or leave-one-subject-out policy — important for BCI data where naive random splits leak subject identity between train and val.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| val_split | float | 0.2 | — |
| seed | int | 42 | — |
| stratify | bool | false | — |
| split_policy | select | random | random · stratified · subject_wise · session_wise · leave_one_subject_out |
Metric Calculator
Turns predictions and true labels into numbers, on the canvas. Wire Predictions and True Labels (Evaluate Model emits both under those names) and it computes accuracy, F1, precision, recall and the confusion matrix — no metric blocks required. Wire Probabilities too and it adds ROC AUC and Log Loss, which are the only two metrics that need them. Everything lands on one Metrics port as a single {name: value} dict, so an Output Block wired to it reports every metric by name rather than one number, and the Execute page picks the whole set up whether or not you wire the output at all. For finer control, connect individual metric blocks, a Metrics Bundle or a Standard Metrics preset into any Metric N port and those are used instead.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| preset | select | auto | auto: use the metric blocks you wired if there are any, otherwise the standard set — extended with ROC AUC and Log Loss when Probabilities is connected. wired: use only the connected metric blocks. |
| n_metrics | int | 4 | How many Metric N ports the node shows (1–12) |
| prefix | str | — | Prefixes every metric name, so two calculators in one graph land as separate rows instead of overwriting each other |
| average | select | weighted | Averaging for F1 / precision / recall when the preset supplies them |
| multi_class | select | ovr | ovr · ovo |
| normalize | select | none | none · true · pred · all |
| print_in_terminal | bool | true | Print the confusion matrix to the run log |
Nothing is dropped silently. A metric the calculator could not compute is named in Report and on the Skipped port. If you explicitly wired a ROC AUC Metric or Log Loss Metric block and left Probabilities empty, the run stops with an error instead — you asked for that metric by placing the node, so its absence is a mistake rather than a preference.
The Metrics dict also carries an evaluation_protocol entry — the number of scored rows, the class balance, the majority-class baseline and a confidence interval on accuracy. It deliberately does not carry a split_policy: this block scores the arrays handed to it and has no splitter of its own, so it never labels your numbers with another node's split.
Standard Metrics
The whole metric suite as one node. Pick a preset and wire its Metrics output into BCILattice Trainer's Metrics port — or into a Metric Calculator — instead of placing seven metric blocks and a Metrics Bundle every time. It emits exactly the configs those blocks would have, so nothing downstream can tell the difference; place the individual blocks when you need two different averages for two different metrics.
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| preset | select | standard | standard: accuracy, F1, precision, recall, confusion matrix — everything computable from predictions alone. probabilistic / everything: adds ROC AUC and Log Loss, which need per-class probabilities. |
| average | select | weighted | weighted · macro · micro · binary · samples |
| multi_class | select | ovr | ovr · ovo |
| normalize | select | none | none · true · pred · all |
| print_in_terminal | bool | true | — |
Metric Blocks
Each metric block takes no inputs and emits a single Metric output — aspecification of a metric, not the metric itself. Wire one into Metric Calculator to compute it on a set of predictions, or into BCILattice Trainer (directly, or via Metrics Bundle) to track it during training. If you just want the usual set, reach for Standard Metrics instead of placing all seven.
Accuracy Metric
Fraction of predictions that exactly match the true label. No configurable settings.
Outputs
F1 Score Metric
Harmonic mean of precision and recall — a better single-number summary than accuracy for imbalanced classes.
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| average | select | weighted | weighted · macro · micro · binary · samples |
Precision Metric
Of everything predicted positive, the fraction that was actually positive.
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| average | select | weighted | weighted · macro · micro · binary · samples |
Recall Metric
Of everything actually positive, the fraction the model correctly found.
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| average | select | weighted | weighted · macro · micro · binary · samples |
ROC AUC Metric
Area under the ROC curve — threshold-independent measure of separability between classes.
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| average | select | macro | macro · weighted · micro · samples |
| multi_class | select | ovr | ovr · ovo |
Confusion Matrix Metric
Full per-class prediction breakdown, optionally normalized and printed to the training log.
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| normalize | select | none | none · true · pred · all |
| print_in_terminal | bool | true | — |
Log Loss Metric
Cross-entropy of the predicted class probabilities against the true labels — penalizes confident wrong predictions heavily.
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| eps | float | 1e-15 | Clipping bound to avoid log(0) |
What the trainer can compute, and what it can't. Metric blocks wired into BCILattice Trainer reach the training backend as metric ids, and the backends differ: the scikit-learn backend computes accuracy, balanced accuracy, F1, precision and recall; the PyTorch backend computes accuracy. ROC AUC, Log Loss and the confusion matrix are declined there by name — use Metric Calculator on the trainer's predictions to get them.
Metrics Bundle
Bundles 1–12 individual metric blocks into a single Metrics output for BCILattice Trainer, so multiple metrics (accuracy, F1, ROC AUC, …) can be tracked from one training run.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| n_inputs | int | 6 | — |
Statistical Comparison
Runs a rigorous pairwise significance test between two models: McNemar's exact test for paired classification predictions against shared true labels, or a paired t-test / Wilcoxon signed-rank test for paired numeric scores (e.g. per-fold or per-subject accuracy). 'auto' picks the right test based on what's wired in.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| test | select | auto | auto · mcnemar · paired_ttest · wilcoxon |
| alpha | float | 0.05 | Significance threshold |
BCILattice Trainer
The single trainer block in the palette, and the one to reach for. It covers both classical ML and deep learning behind one node: wire a Dataset, a Model and Labels, pick a training_strategy, and it handles the split, the training loop and the evaluation. Optimizer and loss default to 'auto' and are chosen from the model and task, so a working pipeline needs no optimizer/loss/dataloader wiring at all.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| training_strategy | select | standard | Changes the block’s own input ports — e.g. dann adds Source/Target Dataset and Domain Discriminator, fine_tuning adds Base Model and Frozen Layers |
| task_type | select | auto | auto infers from the labels |
| split_strategy | select | auto | Use subject_wise or leave_one_subject_out for a result that generalises across people — a random split over multi-subject data leaks subject identity |
| test_size | float | 0.2 | — |
| metrics | str | — | Comma-separated metric names, as an alternative to wiring a Metrics Bundle |
| seed | int | 42 | — |
| deterministic | bool | true | Pins seeds so a rerun reproduces the run |
| epochs | int | 10 | Deep-learning strategies only |
| batch_size | int | 32 | — |
| learning_rate | float | 0.001 | — |
| optimizer | select | auto | custom_input adds an Optimizer input port for an external optimizer block |
| loss | select | auto | custom_input adds a Loss Fn input port |
| device | select | auto | auto · cpu · cuda · mps |
Cross-Validation
Trains nothing itself: it splits, hands each fold to the estimator you wire in, and reports per-fold and aggregate metrics with a confidence interval. Supports k-fold, stratified, grouped, and leave-one-subject-out schemes, and it carries a leakage guard — with strict_leakage_check on (the default), a non-grouped scheme over data spanning more than one subject or session STOPS the run with an actionable message rather than quietly reporting an inflated score.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| cv_strategy | select | stratified_k_fold | leave_one_group_out is LOSO / leave-one-session-out, depending on group_by |
| group_by | select | auto | Where group ids come from when the Groups port is not wired: auto prefers whichever of subject/session actually has more than one distinct value |
| n_splits | int | 5 | — |
| n_repeats | int | 10 | repeated_stratified_k_fold only |
| task_type | select | auto | auto · classification · regression |
| strict_leakage_check | bool | true | Stop on a leakage-risk configuration instead of warning. Turn off only deliberately |
| shuffle | bool | true | — |
| seed | int | 42 | — |
| test_size | float | 0.2 | shuffle_split only |
| confidence | float | 0.95 | Confidence level for the interval on the aggregate score |
Advanced Training
Eight blocks for the situations an ordinary BCI session actually produces: not enough labelled trials, data that cannot leave its site, classes with no examples, and labels that disagree with each other. Every one of them reports its result against a floor — a chance level, a random baseline, a held-out split — because on a two-hundred-trial cohort a number without its floor is not a result.
Federated Averaging
Trains one shared model across several sites without pooling their rows: every site fits locally from the current global model, and the parameters are averaged (weighted by row count for FedAvg, unweighted for uniform, coordinate-wise median for Byzantine robustness). It only accepts models whose parameters are a vector space — a torch module's state_dict, or a linear estimator's coef_/intercept_ — and refuses a random forest by name, because the coordinate-wise mean of two forests' trees is not a model of anything. Always reports the centralised score on the pooled rows beside the federated one: the gap between them is the number a reader wants, and a federated score alone is unreadable. dp_noise_sigma adds Gaussian noise to the aggregate and is recorded as NOT a calibrated (ε, δ) guarantee.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| rounds | int | 3 | Each round restarts every site from the averaged model — one round is 'fit everywhere and average once', which is a different method |
| local_epochs | int | 1 | Gradient steps per site per round (torch models; an sklearn site refits) |
| aggregation | select | fedavg | fedavg weights by site row count · uniform weights equally · median is coordinate-wise and ignores the weights, which is what makes it robust |
| learning_rate | float | 0.01 | — |
| dp_noise_sigma | float | 0 | Gaussian noise on the aggregate, relative to its own scale. NOT differential privacy — no ε is calibrated |
| min_site_rows | int | 5 | Sites below this are dropped and listed in the report |
| report_centralised | bool | true | Also fit on the pooled rows, for the gap |
| seed | int | 0 | — |
Episode Sampler
Turns a labelled cohort into episodes: an n-way k-shot support set and a query set drawn from the same classes, disjoint by row. This is the unit every few-shot method is defined over, and Data Splitter cannot produce it — that block cuts by ratio and knows nothing about classes. The four data ports carry the FIRST episode, which is what a classifier downstream wants; the Episodes port carries every episode's row indices, which is what Meta-Learner wants. Support/query disjointness is asserted, not assumed.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| n_way | int | 0 | 0 means every class — the right default for BCI, where sampling 2 of 4 motor-imagery classes would report an easier problem than the user has |
| k_shot | int | 5 | Support trials per class |
| n_query | int | 15 | Query trials per class (capped by what remains after the support draw) |
| n_episodes | int | 1 | More than one is required by Meta-Learner |
| seed | int | 0 | — |
Prototypical Adapter
Class prototypes in an embedding space and nearest-prototype classification of the query set (Snell et al., 2017). Non-parametric — no gradients, no training loop, no torch — which is what makes it the right first few-shot block. The Encoder port is optional: without one the features themselves are the embedding. Reports accuracy against BOTH floors (uniform 1/n and the query set's majority-class rate) plus a permutation null over shuffled support labels, because with k=5 a class prototype is one noisy point and beating 1/n on a single episode happens by luck often enough to matter.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| distance | select | euclidean | euclidean is the published choice · cosine ignores magnitude · mahalanobis uses one shared support covariance, because a per-class one from k=5 trials is singular by construction |
| temperature | float | 1 | Softmax temperature over the negative distances. Changes the emitted probabilities and NOT the argmax |
| n_permutations | int | 200 | Draws for the null band over shuffled support labels |
| seed | int | 0 | — |
Meta-Learner
Reptile (Nichol et al., 2018) or first-order MAML over the sampler's episodes: adapt on the support set, measure on the query set, and move the initial parameters toward what adaptation found. Both are the same loop with a different meta-update, and which ran is recorded in the provenance. Refuses a single episode — adapting to one task and reporting the result is fine-tuning, and labelling it meta-learning makes the number indistinguishable from a real meta-learned initialisation. Reports the adaptation gain, not only the final accuracy: an initialisation that adapts no better than the one it started from has not learned to learn. Needs PyTorch. Requires PyTorch — the block refuses with the install command when it is absent.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| algorithm | select | reptile | reptile moves the initial parameters toward the adapted ones · fomaml moves them along the query-set gradient measured after adaptation |
| inner_steps | int | 5 | Gradient steps on each episode's support set |
| inner_lr | float | 0.01 | Step size for the inner (task) loop |
| meta_lr | float | 0.1 | Step size for the outer (initialisation) update |
| meta_epochs | int | 1 | Passes over the whole episode set |
| seed | int | 0 | — |
Zero-Shot Classifier
Predicts classes that have no training trial of their own, by describing them with a semantic vector instead of with examples. Learns a ridge compatibility map from neural features into the semantic space on the SEEN classes, then ranks every candidate — seen and unseen — for a query trial (the ESZSL / DeViSE family). Different from Embed & Retrieve, which ranks candidate items rather than classes. The unseen classes are declared explicitly and the block refuses when one of them appears in the training labels: that is ordinary classification under a zero-shot label, and it is the easiest such claim to make by accident. Seen and unseen accuracy are reported separately — a pooled 'generalised zero-shot' number is dominated by the seen classes.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| unseen_classes | str | — | Comma-separated class names withheld from training. Required — a block that inferred them would confirm whatever the graph happened to do |
| similarity | select | cosine | How a projected trial is scored against each class vector |
| alpha | float | 1 | Ridge penalty. Unregularised, the map memorises the seen classes, which is exactly the direction that flatters zero-shot |
| top_k | int | 3 | Size of the reported ranking |
| n_permutations | int | 200 | Draws for the null band over shuffled class-to-semantic assignment |
| seed | int | 0 | — |
Active Learning Sampler
Selects which unlabelled trials are worth a person's time. Pseudo Label's mirror — both consume the Probabilities port Evaluate Model already emits, and together they are the whole label-efficiency loop: confident rows become labels for free, uncertain rows become a labelling queue. Six strategies that genuinely disagree, including random, which is a real strategy because an honest comparison needs it. Every run computes the same selection statistics for a random batch of the same size; with Pool Labels wired (a simulation) it also reports the model's error rate on the selected rows against a random batch, which is the direct statement of whether the strategy found the hard trials.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| strategy | select | margin | margin (top-1 minus top-2) is strongest on multi-class · entropy suits a well-calibrated many-class model · qbc_vote_entropy needs a Committee · core_set is greedy k-centre and ignores the model, so it is the cold-start choice · random is the baseline |
| batch_size | int | 10 | Rows to query. Selecting the whole pool is refused |
| diversity_weight | float | 0 | 0 is pure uncertainty ranking. Above 0, blends in distance from what is already chosen — the k most uncertain trials of a session are frequently the same artefact seen k times |
| seed | int | 0 | — |
Label Model
Combines several unreliable labellers into one probabilistic label. The situation is ordinary in a BCI lab and had no expression on the canvas: ground truth is not a column but several columns that disagree — a stimulus marker, a button press, a rater's annotation, a threshold rule over EMG, an earlier model. Custom Labels takes one column; Pseudo Label takes a model's own belief; neither combines sources. Dawid-Skene estimates a per-source confusion matrix and a class prior jointly with the labels, from the sources' agreement pattern alone. Abstention is first class — a labeller that fires on 12% of trials and is always right is more valuable than one that always fires and is right 70% of the time — and the best single source is reported as the floor.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| method | select | dawid_skene | dawid_skene is the 1979 EM estimator · majority_vote counts sources equally, which is wrong when one dominates · weighted_vote is the one-pass approximation |
| abstain_value | str | -1 | The sentinel for 'this source declined'. -1 is scikit-learn's unlabelled convention across its whole semi-supervised module |
| max_iter | int | 100 | EM iteration cap |
| tol | float | 0.0001 | EM convergence threshold on the posterior |
| min_coverage | float | 0 | Drop sources that fire on less than this fraction of rows |
| seed | int | 0 | — |
Architecture Search
Searches depth, width, dropout, activation and learning rate, and reports the result honestly — which is the whole problem. A search that evaluates 40 architectures on one validation split and reports the best is reporting the maximum of 40 noisy estimates, biased upward by roughly their spread; it has found the architecture that got lucky, not a better one. So selection happens on an inner split and the reported number comes from rows neither the search nor the training touched (the Held-Out ports, or an outer split cut before the search begins), with the difference named selection_bias. It also reports the spread over its own candidates: a winner inside that spread means the search found sampling noise. Needs PyTorch. Requires PyTorch — the block refuses with the install command when it is absent.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| strategy | select | random | random is the strong baseline the literature keeps rediscovering · evolution mutates the best · successive_halving trains everything briefly, keeps the top half, trains longer |
| n_candidates | int | 16 | Architectures evaluated |
| epochs | int | 40 | Full-budget training steps per candidate |
| depths | str | 1,2,3 | Comma-separated option list. A discrete list is what the search enumerates, and it lets an axis be pinned by giving it one value |
| widths | str | 16,32,64,128 | Comma-separated hidden widths |
| dropouts | str | 0.0,0.25,0.5 | Comma-separated dropout rates |
| learning_rates | str | 0.001,0.003,0.01 | Comma-separated learning rates |
| activations | str | relu,gelu,tanh | Comma-separated from relu · gelu · tanh · elu |
| holdout_fraction | float | 0.25 | Used only when the Held-Out ports are empty |
| seed | int | 0 | — |
Uncertainty & Causality
How sure the model is, whether that number can be believed, and what leads what. The first two turn a softmax into something a clinical decision can rest on; the third answers a question the symmetric connectivity metrics cannot ask, and is careful about what it does not claim.
Uncertainty Estimator
Per-row uncertainty, plus the calibration report that gives it meaning. A softmax output is a number between 0 and 1 that a reader treats as a probability, and on a small BCI cohort it is routinely 0.99 while being wrong a third of the time — so every run reports ECE, MCE, a reliability curve and the Brier score, and offers temperature scaling to fix what it finds. Three sources: a single model's Probabilities, an Ensemble of probability matrices, or Model + Data for MC-dropout (which refuses a network with no dropout layer, since that would report an epistemic variance of exactly zero). A sampled source separates aleatoric from epistemic uncertainty. Also reports the AUC with which the uncertainty ranks errors above correct rows — a calibration number can look fine while the uncertainty carries no information about which predictions are wrong.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| method | select | entropy | entropy is total predictive uncertainty · max_probability and margin are the cheap proxies · mutual_information isolates the epistemic part and needs several forward passes |
| mc_samples | int | 20 | Forward passes with dropout left on |
| calibrate | bool | false | Fit a temperature and emit calibrated probabilities |
| allow_in_sample | bool | false | Required to fit and report a temperature on the same rows. Off by default, and stamped into the provenance when on |
| n_bins | int | 10 | Reliability-curve bins on the confidence axis |
| seed | int | 0 | — |
Conformal Predictor
Split conformal prediction: a SET of classes with a finite-sample, distribution-free coverage guarantee. The only construct in the suite that can say 'the true class is in here 90% of the time' and mean it, which is what a clinical BCI decision needs and what a calibrated softmax still does not provide. Separate Calibration and Test ports and no single-port mode at all, because a guarantee fitted on the rows it is measured on is not a guarantee and on a canvas that mistake is one wire. Refuses a calibration set too small for the requested α — with n calibration points the smallest achievable miscoverage is 1/(n+1). Reports the empirical coverage beside the requested one; empty sets are repaired to their top-1 class and the repair is counted, never silent.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| method | select | lac | lac gives the smallest average set · aps adapts the set size per trial and is better conditioned on hard examples |
| alpha | float | 0.1 | Miscoverage rate: 0.1 requests 90% coverage |
| randomised | bool | true | APS only. Without it the sets are conservative by up to one class |
| seed | int | 0 | — |
Causal Connectivity
Directed influence between channels — the question Channel Graph Builder's symmetric metrics cannot ask. Granger causality (an F-test between a restricted and an unrestricted autoregression), transfer entropy (model-free, so it catches nonlinear coupling Granger misses), or partial correlation as the symmetric control. The caveat is part of the output rather than a footnote: these are statements about PREDICTION, not intervention. A common driver, volume conduction, or one channel having a better signal-to-noise ratio all produce significant directed edges between channels with no causal relationship, and on scalp EEG all three are normal — so every run stamps interventional: false and the block will not print the word 'causes'. Every edge clears a time-shifted surrogate null with a Benjamini–Hochberg correction; an uncorrected matrix on 32 channels runs 992 tests and finds ~50 edges in noise.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| method | select | granger | granger is linear and directed · transfer_entropy is model-free and needs far more samples · partial_correlation is symmetric and removes third-channel edges |
| max_lag | int | 5 | Autoregression order / history length. The block refuses epochs too short to support it |
| n_bins | int | 4 | Transfer entropy only. Raising it explodes the history state space and the estimator returns the noise of an empty histogram |
| n_surrogates | int | 50 | Circularly time-shifted source draws. Too few is refused, because the smallest achievable p-value is 1/(n+1) and an empty matrix reads as 'no directed structure' |
| alpha | float | 0.05 | Benjamini–Hochberg level over the n(n−1) tests |
| seed | int | 0 | — |
Fusion, Flows & Rules
Three blocks that change the shape of the model rather than the data reaching it: combining modalities, replacing discrete depth with a continuous flow, and putting task knowledge back on top of the decoder.
Modality Fusion
Combines EEG with fNIRS (or any two to four modalities) honestly. Feature Concatenator puts two arrays side by side, which is a fusion strategy and not multi-modal learning — it answers none of the questions that make this hard: the modalities live on wildly different scales, they carry different amounts of information, and a concatenation dominated by the wider or louder one reports a 'multi-modal' result the other did not contribute to. So every run with Labels reports a cross-validated probe accuracy for EACH modality alone and for the fusion, plus the lift over the best single modality. On EEG + fNIRS the honest answer is frequently that one modality was carrying it. Row alignment is checked, never assumed: a mismatch means the modalities came from different epoching runs.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| strategy | select | concat | concat standardises per modality then concatenates · reliability_weighted scales each by how far its own probe rises above chance · pca_shared builds a joint subspace when the concatenated space is wider than the trial count · cca finds the directions in which exactly two modalities covary |
| n_components | int | 0 | 0 lets the strategy pick |
| standardise | bool | true | Z-score each modality before fusing. Off only when the modalities are already on one scale |
| folds | int | 5 | Cross-validation folds for the per-modality ablation probe |
| seed | int | 0 | — |
Neural ODE Block
Continuous depth: a learned vector field dh/dt = f(h, t) integrated over an interval, instead of a stack of residual layers. For neural time series the appeal is concrete — the hidden state evolves in continuous time, so irregular sampling and variable epoch lengths stop being a preprocessing problem. Fixed-step Euler, midpoint and RK4 in plain torch rather than against torchdiffeq, which keeps the determinism claim checkable and avoids a dependency for thirty lines of Runge–Kutta. Every run reports a solver-consistency number — the same field integrated again at double the step count — because an ODE with too few steps IS a residual network and the 'continuous' claim is decoration. It also reports how far the state travels: h(T) ≈ h(0) is an array of the right shape carrying the input unchanged. Needs PyTorch. Requires PyTorch — the block refuses with the install command when it is absent.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| solver | select | rk4 | rk4 is fourth-order and the default · midpoint second · euler first, and the one that shows a large consistency gap |
| n_steps | int | 10 | Integration steps over [0, t_end] |
| t_end | float | 1 | End of the integration interval |
| hidden_dim | int | 64 | Width of the vector field's MLP |
| n_layers | int | 2 | Depth of the vector field's MLP |
| activation | select | tanh | Vector-field nonlinearity. tanh keeps the field bounded, which is what most ODE work uses |
| time_dependent | bool | true | Append t as an input channel, making the field non-autonomous |
| augment_dim | int | 0 | Augmented Neural ODEs. At 0 the flow cannot cross itself, so a class surrounded by another is unreachable at any width |
| in_features | int | 0 | 0 takes it from the Data port |
| out_features | int | 0 | 0 emits the flow's end state instead of a classification head |
| seed | int | 0 | — |
Rule Constraint Layer
Task knowledge as a symbolic layer over the decoder's probabilities. A BCI decoder does not emit predictions into a vacuum: a wheelchair cannot turn left immediately after turning right, a state must be held for a minimum dwell before it counts as intentional. This block decodes the most likely sequence the rules PERMIT — a constrained Viterbi over the whole sequence, not a filter over argmax, which is why it can repair a violation by changing an earlier prediction. Five rule forms, one per line: forbid A · forbid A after B · require A after B · min_dwell A n · max_run A n. Anything else is refused rather than ignored. Rows are read as consecutive time steps. Constraints can only lower the model's own likelihood; whether they raise accuracy is empirical, so with Labels wired the block reports accuracy before and after and splits the changes into corrections and new errors.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| rules | text | — | A multi-line field. One rule per line; # starts a comment. An unparseable line is refused, because a rule you believe is running while it is not is worse than no rule |
| renormalise | bool | true | Redistribute probability mass off classes the automaton could not have chosen, so a downstream calibration or conformal block sees the reachable distribution |
| fail_on_no_rules | bool | true | Refuse an empty rule set instead of recording that a symbolic layer ran and did nothing |
Graph Learning
The montage as a graph. Channels have a known neighbourhood structure — spatial and functional — and a dense layer has to rediscover it from the few hundred trials a BCI session provides.
Channel Graph Builder
An EEG cap is a graph, and every block in the suite treated it as a flat feature vector. This builds the channel × channel adjacency — from the data (correlation, magnitude-squared coherence, phase-locking value) or from the montage (Gaussian kernel over electrode distance) — sparsified by k-nearest-neighbour or a threshold. Useful on its own: the adjacency it emits is a connectivity matrix. Which metric is chosen decides what can be seen at all: zero-lag correlation is blind to a purely lagged coupling that coherence and PLV both find. The data-driven metrics are compared against a phase-randomised surrogate null, because a connectivity matrix on band-limited neural data has strong structure by construction and 'these channels are connected' is not a finding until it beats that.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| metric | select | correlation | correlation is zero-lag and blind to lagged coupling · coherence and plv are not · distance uses the Positions port · precomputed takes a square matrix on Data |
| sparsify | select | knn | knn keeps each node's k strongest edges (symmetrised) · threshold cuts by weight · none keeps the dense matrix |
| k_neighbours | int | 4 | Neighbours per node for knn |
| threshold | float | 0.3 | Minimum edge weight for threshold |
| distance_sigma | float | 0 | 0 uses the median pairwise distance, which adapts to whatever units the montage is in |
| n_surrogates | int | 20 | Phase-randomised draws for the null. 0 skips it |
| seed | int | 0 | — |
Graph Neural Encoder
Symmetric-normalised graph convolution, Â = D^-½(A+I)D^-½ (Kipf & Welling), over a Channel Graph Builder adjacency. Two modes and the choice is deliberate: 'embeddings' propagates node features in pure numpy and needs no torch at all — propagation over the montage is a spatial filter and a perfectly good preprocessing step — while 'model' emits a trainable nn.Module whose adjacency is registered as a BUFFER, so a saved model still knows its montage. Reports the Dirichlet energy before and after: repeated convolution drives it to zero, at which point every node holds the same vector and the output is a constant of the right shape. The output rank is reported for the same reason.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| output | select | embeddings | embeddings is numpy and needs the Data port · model is torch-gated and emits an untrained module for a trainer |
| n_layers | int | 2 | Propagation steps. More smooths harder — watch the Dirichlet energy |
| hidden_dim | int | 32 | Width per layer (model mode) |
| pooling | select | mean | How node features become a trial vector: mean · max · concat · none keeps the node axis |
| activation | bool | true | ReLU between propagation steps |
| seed | int | 0 | — |
Generative Models
Synthesising trials, for augmentation and class rebalancing. Neither block returns samples without the same three-part audit, because a generative model is the easiest thing in the suite to fool yourself with.
GAN Trainer
A conditional MLP generator and discriminator over trial features — synthesising trials for augmentation, class rebalancing and sample-efficiency questions. The other direction from Neural Prefix Encoder, which goes from neural activity to text. Samples never come back without the audit: nearest-neighbour distance to the real trials against the distance real trials sit from each other (a memorised generator produces beautiful samples and zero new information), a held-out classifier two-sample test where 0.5 means indistinguishable, and — with Labels — train-on-synthetic/test-on-real accuracy against train-on-real, which is the only number that says the samples are USEFUL rather than realistic-looking. Refuses fewer than 20 trials, where a GAN simply memorises. Needs PyTorch. Requires PyTorch — the block refuses with the install command when it is absent.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| loss | select | nonsaturating | nonsaturating is the standard GAN objective · wgan_gp is the most stable on small tabular cohorts · lsgan is the least-squares variant |
| latent_dim | int | 32 | Noise dimension |
| hidden_dim | int | 128 | Generator and discriminator width |
| epochs | int | 300 | Alternating generator/discriminator steps |
| batch_size | int | 64 | Rows per step |
| learning_rate | float | 0.0002 | Adam step size for both networks (β₁ = 0.5) |
| gp_weight | float | 10 | Gradient-penalty weight for wgan_gp |
| n_samples | int | 0 | 0 draws as many samples as there are real trials |
| seed | int | 0 | — |
Diffusion Generator
DDPM over trial features: a noise schedule forward and a learned denoiser back. Slower to sample than a GAN and far more stable to train, which on a 200-trial cohort is usually the trade that matters. Conditional on the class when Labels are wired. Ships the same three-part audit as GAN Trainer — memorisation distance, classifier two-sample test, and train-on-synthetic/test-on-real utility — from the same shared implementation, because two copies of an audit drift and the drift always goes in the flattering direction. Requires PyTorch — the block refuses with the install command when it is absent.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| schedule | select | cosine | cosine (Nichol & Dhariwal) — linear betas destroy a low-dimensional feature vector's signal in the first few steps and the model then learns to denoise pure noise |
| timesteps | int | 200 | Diffusion steps. Sampling costs one denoiser pass each |
| hidden_dim | int | 128 | Denoiser width |
| epochs | int | 400 | Training steps |
| batch_size | int | 64 | Rows per step |
| learning_rate | float | 0.001 | Adam step size |
| n_samples | int | 0 | 0 draws as many samples as there are real trials |
| seed | int | 0 | — |
Reinforcement Learning
The closed half of the loop: the decoder chooses, the user reacts, and the reaction is the training signal. The environment is a contextual bandit over the trial stream — the one a BCI lab has, rather than a simulator this application cannot honestly provide.
RL Agent Trainer
A BCI is a closed loop and the suite could only describe the open half. This learns online over the trial stream as a contextual bandit — deliberately the environment a BCI lab has, not a simulator: each row is a state, the agent picks an action, and the reward comes from the Rewards port (a real logged interaction) or is derived from the labels for a simulation. Three floors make the reward readable: the random policy's reward, the oracle's, and the agent's position between them. States are z-scored inside the block and the Q update is normalised-LMS, so learning_rate means a fraction of the step to the exact fit rather than something that depends on whether the features are microvolts or band-power ratios. Warns when the policy collapses to one action, which still beats random on an unbalanced stream.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| algorithm | select | epsilon_greedy_q | epsilon_greedy_q is linear Q with ε exploration · linucb adds an optimism bonus and is usually strongest here · reinforce is a linear policy gradient |
| epsilon | float | 0.1 | Exploration rate for epsilon_greedy_q |
| learning_rate | float | 0.1 | Fraction of the normalised-LMS step to the exact fit, over z-scored states |
| alpha_ucb | float | 1 | Confidence-bonus width for linucb |
| n_passes | int | 3 | Passes over the stream, each in a fresh shuffled order |
| reward_correct | float | 1 | Simulated reward for the right action, when Rewards is not wired |
| reward_incorrect | float | -1 | Simulated reward for a wrong one |
| seed | int | 0 | — |
Offline Policy Trainer
Learns a policy from LOGGED interactions with no ability to try anything new — the setting every retrospective BCI dataset is in. Behaviour cloning reproduces the logging policy and ignores the rewards, which is the honest baseline; conservative_q adds the CQL penalty that pushes down the value of actions the log never took in that state, which is the whole reason offline RL fails without it. The evaluation is the hard part: 'what would the new policy have earned' is an importance-weighted average whose variance explodes when the new policy disagrees with the logging one. So the block reports the effective sample size (Kish's, routinely single digits) and a bootstrap confidence interval beside the estimate, and declares the estimate unusable when it collapses. Without logged propensities it substitutes the empirical action frequency and says loudly that every off-policy number is then biased.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| method | select | behaviour_cloning | behaviour_cloning reproduces the log · conservative_q is fitted Q with the CQL penalty · importance_weighted reweights by inverse propensity |
| conservative_weight | float | 1 | CQL penalty strength — higher keeps the policy closer to the log, which is what makes its evaluation trustworthy |
| learning_rate | float | 0.05 | Step size |
| n_epochs | int | 200 | Full-batch passes |
| clip_weight | float | 10 | Importance-weight cap. Uncapped weights make the estimate a single row |
| min_effective_sample | int | 30 | Below this the estimate is reported but declared unusable |
| seed | int | 0 | — |
Multi-Agent Arena
Several policies acting on the same stream with an arbiter, and the question that makes it worth having: does the group beat its best member? Reports per-agent reward, the joint reward, the lift over the best single agent, and the mean pairwise agreement — because combining policies pays only when they make DIFFERENT mistakes, and agents that agree on 95% of steps are one policy with different seeds. The reward-weighted and best-agent arbiters judge on a recent window rather than the whole history, so they can notice an agent that became the good one halfway through the session.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| arbiter | select | majority | majority is a vote over hard choices · confidence lets the most peaked agent decide each step · reward_weighted blends scores by recent reward · best_agent defers to the current leader |
| reward_correct | float | 1 | Simulated reward for the right action, when Rewards is not wired |
| reward_incorrect | float | -1 | Simulated reward for a wrong one |
| window | int | 50 | Recent steps the reward-weighted and best-agent arbiters judge on |
| seed | int | 0 | — |
Need a library that is not on this page? Extensions covers turning any Python package — Cedalion, or anything on the curated shelf — into blocks that wire into these same canvases.