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.
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. Drag one onto the canvas for each data stream the pipeline accepts. It carries exactly one port, because each Input Block becomes exactly one input port on the compiled-pipeline block when this pipeline is dropped into a Workflow.
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 |
| subject_scope | select | per_subject | per_subject runs one job per subject; all pools every selected subject into one run, which is what makes cross-subject (LOSO) cross-validation possible |
File Path
Points at file(s) on disk and emits their paths. 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.
Both output shapes are filled for a single pick, so one file can feed either a scalar-path input or a list-shaped one with no adapter in between. Its first consumer is Cedalion SNIRF Reader, where a wired path always beats the reader's own file_path property.
Outputs
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| file_path | file | One file, and the common case | |
| file_paths | str | Several, separated by ";" or newlines | |
| 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 True Labels and Predictions outputs, or to a Cross-Validation block's equivalents.
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 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).
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 |
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 |