BCILattice Custom Blocks
All custom and built-in special blocks available in the ML Suite (MLFlow) and Workflow Designer canvases, injected at runtime by the training server.
Overview
In addition to the 5,400+ catalog blocks loaded from the CSV, BCILattice injects a set of custom built-in blocks at server startup. These cover analysis utilities, pipeline I/O, code execution, file selection, and Workflow graph controls.
New here? The Block Index lists every one of our blocks in a single line, grouped as the palette groups them.
Custom blocks are not classes; they run as functions or server-side routines and appear in the block palette alongside catalog blocks. They are marked is_custom: true internally.
| Block | Category | Canvas |
|---|---|---|
| Channel Selection | Analysis (BCILattice) | MLFlow + Workflow |
| Signal Projection | Analysis (BCILattice) | MLFlow + Workflow |
| Time-Frequency Features | Analysis (BCILattice) | MLFlow + Workflow |
| Custom Labels | Inputs (BCILattice) | MLFlow + Workflow |
| Input Block | Inputs (BCILattice) | MLFlow + Workflow |
| Output Block | Outputs (BCILattice) | MLFlow + Workflow |
| Loop Block | Flow Control (BCILattice) | MLFlow + Workflow |
| Custom Code | Pipeline Blocks | MLFlow + Workflow |
| Data Graph | Graphs (BCILattice) | MLFlow + Workflow |
| Confusion Matrix Graph | Graphs (BCILattice) | MLFlow + Workflow |
| Graph Output | Graphs (BCILattice) | MLFlow + Workflow |
| Graph Endpoint | Pipeline Blocks | Workflow only |
| Select Files | Inputs (BCILattice) | Workflow only |
| Compiled Pipeline | Model/Pipelines (Compiled) | Workflow (runtime) |
Analysis Blocks
These blocks call the local training server's analysis routes (/xlb9/…) and are available in both the ML Suite and Workflow Designer canvases.
Channel Selection
Ranks and filters EEG/fNIRS/EMG channels by statistical relevance. Calls /xlb9/channel_selection/run. The Workflow canvas exposes an extra data_type selector; the ML Suite version omits it.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| method_name | select | t-Value Method | Channel ranking algorithm |
| rest_label | int | 0 | Integer label for the rest/baseline class |
| percentile | int | 5 | Top-N percentile of channels to keep |
| ignore_classes | str | Comma-separated class labels to exclude | |
| data_type | select | fNIRS | Workflow only |
Custom Labels
Injects or overrides the label column used by downstream blocks. Accepts a path to a label CSV or a comma-separated string of manual labels.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| label_file | str | Path to a CSV file whose first column is used as labels | |
| manual_labels | str | Comma-separated label values (overrides label_file if set) |
Signal Projection
Converts 2-D time-series signals into 2-D images using time-frequency or recurrence transforms. Calls /xlb9/signal_projection/run. Useful for feeding EEG into CNN-based image classifiers.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| method_name | select | Gramian Angular Field (GAF) | Projection algorithm |
| image_size | int | 64 | Output image resolution in pixels (square) |
Time-Frequency Features
Turns the Analysis suite's time-frequency output into a trainable feature table. It reduces a per-trial ERSP cube — (epochs, channels, freqs, times) — down to X[n_trials, n_features] with labels that stay index-aligned, so a time-frequency analysis can actually train a decoder instead of only rendering a figure.
On fNIRS data, time-frequency peaks read roughly 30% low. That is a resolution limit of the transform in the very low fNIRS band, not a defect in this block — treat the peak frequency as approximate when interpreting fNIRS results.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| aggregation | select | band_means | How the freq × time cube collapses to features. band_means = one value per band per channel; flatten keeps everything (very wide) |
| tmin | float | 0 | Start of the analysis window in seconds. 0 with tmax 0 means the whole epoch |
| tmax | float | 0 | End of the analysis window in seconds |
| n_time_windows | int | 4 | Windows per band when aggregation is band_time_windows |
| bands | str | Custom bands as "name:low-high" pairs. Empty uses the standard delta/theta/alpha/beta/gamma set | |
| standardize | bool | false | z-score each feature across trials before returning |
Pipeline Blocks
Structural blocks that control data flow and code execution within a pipeline. Available in both canvases unless noted otherwise.
Input Block
Marks one entry point of the pipeline, and nothing else. Drag one onto the canvas for each data stream the pipeline accepts. Each Input Block becomes exactly one input port on the compiled-pipeline block when this pipeline is dropped into a Workflow, which tells you where to connect what. Which files arrive, how they split into runs and how they are filtered are set on the Workflow's Select Files.
The canvas port is labelled with port_name, so what you see here is exactly what you will wire to on the compiled block. Give each Input Block a distinct name — two blocks sharing one name compile to two identically named ports.
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| port_name | str | In_1 | Names both the port drawn on the canvas and the port exposed on the compiled block |
File Path
Points at one file on disk and emits its path. It reads nothing — that is the point: a block that does its own file reading needs the path, and Select Files cannot give it one usefully, because Select Files exists to load a processing stage into a table.
One file in, one path out. For several recordings use Select Files, whose File Paths port carries every recording you picked. A wired path always beats a reader's own file_path property.
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| file_path | file | The file to point at | |
| must_exist | bool | true | Refuse a path that is not on disk, naming it. Turn off for a file an earlier step produces |
Output Block
Marks one exit point of the pipeline. Whatever is connected here becomes one output port on the compiled-pipeline block. Every pipeline needs exactly one final output node — an Output Block, Graph Output or Graph Endpoint — or compiling fails.
It forwards the first non-null value arriving on a connected edge. Ambient values deliberately lose: an embedded pipeline runs with the caller's inputs pre-seeded, so a naive “first non-null” would forward the pipeline's own input and return the data unchanged while appearing to have run.
Inputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| port_name | str | Out_1 | Names both the port drawn on the canvas and the port exposed on the compiled block |
Loop Block
Repeats a sub-section of the pipeline for N iterations or epoch-by-epoch. Set start_node and end_node to the labels of the blocks that mark the loop boundary.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| iterations | int | 1 | Number of times to repeat the loop body |
| start_node | str | Label of the first block inside the loop | |
| end_node | str | Label of the last block inside the loop | |
| mode | select | Epoch | Sequential runs the whole loop N times; Epoch iterates once per data epoch |
Custom Code
Executes an arbitrary Python function inline inside the pipeline. The function receives the upstream data tensor as its first argument and must return the transformed result.
def custom_process(data):
# data is the input tensor from the previous block
# transform it here
return dataInputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| code | code | def custom_process(data): return data | Python function body: must accept data and return output |
Model Structure Blocks
The only two blocks that take models in and hand a model out. Everything else on the canvas moves data; these build the shape of one network.
Chaining layer blocks already composes them in order — these are for the case a chain cannot express: several branches over the same input, joined back together. Each wired Branch N receives the identical input inside the composed module's forward.
Do not confuse these with Feature Concatenator, Parallel Fan-out or Ensemble Voter in Data Routing and Flow Control. Those combine arrays — data between models, or predictions from several models. These combine models, inside one network. Wiring data into a Branch N port is refused with a message saying which block you wanted.
Model Concat
Runs every wired branch on the same input and concatenates the results into one tensor. This is how a multi-scale CNN or a two-stream model is drawn: three Conv1d branches with different kernel sizes, joined here, then a shared head.
A single wired branch collapses to that branch, so the block is safe to leave in place while you build the other paths.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| n_inputs | int | 3 | Number of Branch ports (1–12) |
| dim | int | 1 | Axis to join on. 1 is the feature axis of a (batch, features, …) tensor — joining the last axis of a conv output concatenates along time instead, which is rarely what you want |
Model Add
Runs every wired branch on the same input and sums the results. Put a transformed path on one branch and an Identity layer on the other, and you have a residual/skip connection.
Every branch must produce the same shape — addition has no broadcasting rule that would be safe to guess here.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| n_inputs | int | 2 | Number of Branch ports (1–12) |
Trainer Trigger
Hands one side of the trainer's train/test split to a block that fits on its own. Use it when a model block shows X and y ports — that block is a constructor and a fit call in one, and it must not see the rows the trainer will score against.
The source is chosen by name, not by a wire, so one Select Files can feed a dozen triggers without a dozen edges crossing the canvas.
It uses the trainer’s split, not its own. The split settings are read from the BCILattice Trainer in the same graph, and the partition is derived with the identical call the trainer makes — same strategy, same test_size, same seed, so the same rows. Every trigger in a run shares one partition, which is what keeps X and y index-aligned.
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| source_block | select | Which block the data comes from — the list is read from the canvas, so it is always what actually exists | |
| source_port | select | Which of its outputs — for example Select Files → X, or → Y | |
| split | select | train | Which side of the trainer’s split to hand over. train by default: a block that fits must not see the test rows |
Graph Blocks
Four ways to get a picture out of a run. The important distinction is tap vs. terminal: Data Graph passes its value straight through, so it can be spliced anywhere, any number of times. Graph Output and Graph Endpoint are terminal nodes, so they compete for the “exactly one final output” slot every compiled graph must have.
Data Graph
A general-purpose plot tap: it renders whatever reaches it and passes the same value out unchanged on its Data port. Because it is not a terminal node, you can drop it onto an existing connection to inspect what is flowing through mid-pipeline without rewiring anything downstream.
A value that cannot be drawn never silently disappears — the reason lands on the Graph Error port. Wire it somewhere, or read it in the node inspector, when a plot does not appear.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| graph_name | str | Data Graph | Titles the plot, so several taps stay distinguishable in the Training dashboard Graphs tab |
| plot_type | select | auto | auto picks by shape: curves for training history, an annotated matrix for a confusion matrix, lines/heatmap for arrays |
| save_path | str | Optional path to write the image | |
| image_format | select | png | png · jpg |
Confusion Matrix Graph
Draws a confusion matrix from true and predicted labels. Wire it to a trainer's Test Labels and Test Predictions outputs (out-of-fold when a Validation Scheme is wired).
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| title | str | Confusion Matrix | — |
| normalize | select | none | true = row-normalised (recall per class), pred = column-normalised (precision), all = over the whole matrix |
| colormap | str | Blues | Any matplotlib colormap name |
| display_labels | str | Comma-separated class names. Empty uses the raw label values |
Graph Output
Terminal plot node: renders the incoming value and saves it to disk. Counts as a pipeline's single final output, so it can end a graph in place of an Output Block.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| graph_name | str | Workflow Graph | Display title embedded in the image |
| save_path | str | File path to write the output image | |
| image_format | select | png | png · jpg · pdf |
Graph Endpoint
The other terminal plot node. Behaves like Graph Output but writes to its own output_path, and is the node the workflow HTTP route targets when a graph is executed over REST.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| graph_name | str | Workflow Graph | — |
| output_path | str | File path to write the rendered image |
Workflow-Only Blocks
Loading real recordings is a Workflow-canvas concern, so this block exists only there. An ML Suite pipeline takes its data through an Input Block instead, and is fed by whatever the Workflow wires into it.
Select Files
Loads real recordings from the Data Manager: pick a modality, a processing stage, a session and the subjects, and it emits the feature table X and labels Y. This is where a workflow actually gets its data.
The card on the canvas states the cohort in words, not raw properties —subjects: 20 — S01, S02, S03, S04 +16 more, features: all columns (never a bare selected_features: []), and runs: 20 (Per subject, combined sessions) — the exact number of jobs subject_scope above will fan the run out into, computed the same way the training server groups the recordings. Blank filters are left off the card instead of printing as filter_task:.
The extra output ports change with data_source: a recording publishes Channels, a reducer's output publishes Components, a feature table publishes Feature Names, epochs publish Epoch Index. X and Y are always present, and so is File Paths — the recordings you picked, for a block that does its own reading (see File Path).
Samples: one unit, one number. Pick seconds, Hz or rows and a single size row appears in that unit; the card says what one sample is in words (“model rate 5 Hz, rows averaged into one”). The rate comes from the recording itself, and a sample never spans two recordings. An already-epoched Data Source (Epochs, Features, Time-Frequency…) is one sample per trial, so the Samples choices are greyed out for it — data is never epoched twice. The same settings exist as the Epoching block, for data that did not come straight from Select Files.
Media. Tick Stimulus (and/or Response) on the Media row to publish the images, video, audio or text the study presented, paired with X by one index. With Samples on, each sample takes the stimulus shown during it, and a Sample Onsets port appears for cutting a video or audio stream with Media Windower.
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| modality | select | fNIRS | Signal modality filter |
| data_source | select | Clean Data | Which processing stage to load — clean, channel-selected, quality-checked, normalised, features, epochs, time-frequency, … |
| session | select | All Sessions | Session filter |
| chromophore | select | All | fNIRS only: keep only hbo or only hbr. "All" feeds every optode in twice, once per chromophore |
| selected_files | str | Serialised list of file records chosen in the picker | |
| selected_subjects | str | Subject IDs extracted from the selected files | |
| selected_features | list | [] | Optional column subset to keep — blank keeps every column |
| subject_scope | select | Per subject, combined sessions | How the selected recordings become training runs. Subjects and sessions are independent axes: kept apart (one run each) or pooled (one run for all of them). The BCILattice Trainer's cohort_mode mirrors this same choice, and the run refuses if the two disagree |
| filter_task | str | Comma-separated task tags to keep, e.g. "Motor Imagery". Blank matches everything | |
| filter_paradigm | str | Comma-separated paradigm tags to keep | |
| filter_dataset | str | Comma-separated source-dataset tags to keep | |
| filter_quality | str | Comma-separated quality tags to keep | |
| media_stimulus | bool | false | Publish the stimulus media (image, video, audio, text) per trial or sample |
| media_response | bool | false | Publish the participant's recorded response media |
| sample_by | select | every row | How many recorded rows make one sample for the model, in the unit you think in. On a 10 Hz device, 0.2 seconds, 5 Hz and 2 rows are the same answer. Only one unit at a time |
| sample_size | float | 1 | The number, in the unit sample_by names (seconds, Hz or rows) |
| combine | select | auto | stack keeps the rows as time steps (samples × channels × steps); average makes one row per sample (a lower rate). auto: average for Hz, stack otherwise |
| overlap_percent | float | 0 | How much each stacked sample shares with the next. Overlapping samples must be validated with whole recordings held out |
| window_label | select | skip mixed | The label of a sample whose rows carry different labels. skip mixed drops it rather than inventing one |
Training several models on what Select Files loads? A trainer's Model input takes one wire — the canvas refuses a second one and offers to insert a Model Collection instead, so every subject's recordings load once and each model gets its own results row. See Model Collection in the Advanced Blocks reference.
Compiled Pipeline Block
When you compile and register a pipeline from the ML Suite or Workflow Designer, BCILattice dynamically injects it as a reusable block into the Pipeline Blocks category. These blocks are created at runtime via POST /xlb9/register_compiled_pipeline or POST /xlb9/register_compiled_workflow.
Compiled Pipeline
A compiled and registered pipeline exposed as a single reusable block, collected in the Model/Pipelines (Compiled) palette group. It has one input port per Input Block and one output port per Output Block in the original pipeline, each named from that block's port_name.
Inputs
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| description | str | Compiled pipeline: … | Auto-filled with the pipeline name |
| block_role | select | Processing Pipeline | Determines training behaviour and loss function selection |
| optimizer | str | Adam | Optimiser name passed to the training loop |
| epochs | int | 10 | Training epochs when block_role is ML Model or DL Model |
| learning_rate | float | 0.001 | Learning rate for the optimiser |