NimbusSDK.jl - Julia SDK Reference
The NimbusSDK.jl Julia package provides production-ready Bayesian inference for Brain-Computer Interface (BCI) applications. Built on RxInfer.jl, it offers two models (Bayesian LDA and Bayesian GMM) with batch and streaming inference capabilities.
Installation
NimbusSDK.jl is now available in the public Julia General Registry:
using Pkg
# Install the public wrapper (registered package)
Pkg.add("NimbusSDK")
After installing the wrapper, install the proprietary core with your license key:
using NimbusSDK
# Install the commercial core (one-time setup)
NimbusSDK.install_core("your-api-key-here")
Get your API key at: nimbusbci.com/dashboard
Requirements
- Julia ≥ 1.9
- Valid NimbusSDK license key
- Preprocessed EEG features (CSP, bandpower, etc.) - not raw EEG
What changed? NimbusSDK.jl is now a public wrapper package in the Julia General Registry. The proprietary inference core (NimbusSDKCore) is automatically installed when you provide your API key. No more private registry setup needed!
Quick Start
using NimbusSDK
# One-time setup: Install core with your API key
NimbusSDK.install_core("your-api-key")
# Load model
model = load_model(RxLDAModel, "motor_imagery_4class_v1")
# Prepare data
data = BCIData(features, metadata, labels)
# Run inference
results = predict_batch(model, data)
Setup
install_core()
Install the proprietary NimbusSDKCore with your API key. This is a one-time setup that downloads and configures the commercial inference engine.
install_core(api_key::String) -> Bool
Parameters:
api_key::String - Your NimbusSDK API key (format: nbci_live_... or nbci_test_...)
Returns: true if installation successful
Example:
using NimbusSDK
# One-time setup (downloads and installs core)
NimbusSDK.install_core("nbci_live_...")
# After installation, you can use the SDK in any project
using NimbusSDK
model = load_model(RxLDAModel, "motor_imagery_4class_v1")
The core installation is persistent. You only need to run install_core() once per machine. After that, simply using NimbusSDK will work in any Julia project.
check_installation()
Verify that the core is installed and working correctly.
check_installation() -> Bool
Returns: true if core is installed and operational
Models
NimbusSDK provides two Bayesian inference models:
RxLDAModel
Primary Name: Bayesian LDA (Bayesian Linear Discriminant Analysis)
API Name: RxLDAModel
Mathematical Model: Pooled Gaussian Classifier (PGC)
Linear Discriminant Analysis with shared precision matrix. Fast inference with good performance for well-separated classes.
Fields:
means::Vector{Vector{Float64}} - Class means [m1, m2, …, mk]
precisions::Vector{Matrix{Float64}} - Class precision matrices
global_precision::Matrix{Float64} - Shared precision matrix
metadata::ModelMetadata - Model metadata
RxGMMModel
Primary Name: Bayesian GMM (Bayesian Gaussian Mixture Model)
API Name: RxGMMModel
Mathematical Model: Heteroscedastic Gaussian Classifier (HGC)
Gaussian Mixture Model with class-specific covariance matrices. More flexible, handles overlapping distributions.
Fields:
means::Vector{Vector{Float64}} - Class means [m1, m2, …, mk]
precisions::Vector - Vector of precision matrices or Wishart distributions
metadata::ModelMetadata - Model metadata
load_model()
Load a pre-trained or custom model.
load_model(ModelType, model_name::String) -> Model
load_model(ModelType, filepath::String) -> Model
Parameters:
ModelType - RxLDAModel or RxGMMModel
model_name::String - Model identifier or filepath
Example:
# Load from Nimbus model zoo
model = load_model(RxLDAModel, "motor_imagery_4class_v1")
# Load custom model
model = load_model(RxLDAModel, "my_model.jld2")
save_model()
Save a trained or loaded model to disk.
save_model(model, filepath::String)
Parameters:
model - RxLDAModel or RxGMMModel
filepath::String - File path to save model (typically .jld2 format)
train_model()
Train a new model on labeled data.
train_model(
ModelType,
train_data::BCIData;
iterations::Int = 50,
showprogress::Bool = false,
name::String = "untitled_model",
description::String = ""
) -> Model
Parameters:
ModelType - RxLDAModel or RxGMMModel
train_data::BCIData - Labeled training data (must include labels!)
iterations::Int - Number of inference iterations (default: 50)
showprogress::Bool - Show training progress (default: false)
name::String - Model name (default: “untitled_model”)
description::String - Model description (default: "")
Example:
model = train_model(
RxLDAModel,
train_data;
iterations = 50,
showprogress = true,
name = "my_motor_imagery_model",
description = "4-class motor imagery with CSP"
)
calibrate_model()
Fine-tune a pre-trained model with subject-specific data (faster than training from scratch).
calibrate_model(
base_model,
calib_data::BCIData;
iterations::Int = 20
) -> Model
Parameters:
base_model - Pre-trained model to calibrate
calib_data::BCIData - Calibration data with labels
iterations::Int - Number of calibration iterations (default: 20)
Example:
base_model = load_model(RxLDAModel, "motor_imagery_baseline_v1")
personalized_model = calibrate_model(base_model, calib_data; iterations=20)
Data Structures
BCIData
Container for BCI data with features, metadata, and optional labels.
struct BCIData
features::Array{Float64, 3} # (n_features × n_samples × n_trials)
metadata::BCIMetadata
labels::Union{Nothing, Vector{Int}} # Optional labels for training/evaluation
end
Example:
data = BCIData(
csp_features, # (16, 250, 20) - 16 features, 250 samples, 20 trials
BCIMetadata(...),
labels # [1, 2, 3, 4, 1, 2, ...] - trial labels (1-indexed)
)
Metadata describing the BCI data properties.
struct BCIMetadata
sampling_rate::Float64 # Hz (e.g., 250.0)
paradigm::Symbol # :motor_imagery, :p300, :ssvep, :erp
feature_type::Symbol # :csp, :bandpower, :time_domain, :frequency_domain, :raw_filtered
n_features::Int # Number of features (e.g., 16 for CSP)
n_classes::Int # Number of classes (e.g., 4)
chunk_size::Union{Nothing, Int} # For streaming: samples per chunk
temporal_aggregation::Symbol # :mean, :median, :logvar, :none
end
Example:
metadata = BCIMetadata(
sampling_rate = 250.0,
paradigm = :motor_imagery,
feature_type = :csp,
n_features = 16,
n_classes = 4,
chunk_size = nothing, # Batch mode
temporal_aggregation = :logvar # For CSP features in MI
)
Inference Functions
predict_batch()
Perform batch inference on multiple trials.
predict_batch(
model,
data::BCIData;
iterations::Int = 10
) -> BatchResult
Parameters:
model - RxLDAModel or RxGMMModel
data::BCIData - Data to predict (labels optional)
iterations::Int - Number of inference iterations (default: 10)
Returns:
struct BatchResult
predictions::Vector{Int} # Predicted class for each trial
confidences::Vector{Float64} # Confidence (max posterior) for each trial
posteriors::Matrix{Float64} # Full posterior distributions (n_trials × n_classes)
free_energy::Union{Float64, Nothing} # RxInfer free energy if available
end
Example:
results = predict_batch(model, data)
println("Predictions: ", results.predictions)
println("Mean confidence: ", mean(results.confidences))
# Calculate accuracy if labels available
accuracy = sum(results.predictions .== data.labels) / length(data.labels)
println("Accuracy: $(round(accuracy * 100, digits=1))%")
init_streaming()
Initialize a streaming session for chunk-by-chunk inference.
init_streaming(model, metadata::BCIMetadata) -> StreamingSession
Parameters:
model - Loaded model
metadata::BCIMetadata - Metadata with chunk_size set
Returns: StreamingSession for processing chunks
Example:
metadata = BCIMetadata(
sampling_rate = 250.0,
paradigm = :motor_imagery,
feature_type = :csp,
n_features = 16,
n_classes = 4,
chunk_size = 250 # 1 second chunks at 250 Hz
)
session = init_streaming(model, metadata)
process_chunk()
Process a single chunk of data during streaming.
process_chunk(session, chunk::Array{Float64, 2}) -> StreamingResult
Parameters:
session::StreamingSession - Active streaming session
chunk::Array{Float64, 2} - Chunk data (n_features × chunk_size)
Returns: Named tuple (prediction, confidence, posterior)
(prediction=1, confidence=0.85, posterior=[0.1, 0.85, 0.05]) # Example
Example:
for chunk in eeg_stream
result = process_chunk(session, chunk)
println("Prediction: $(result.prediction), Confidence: $(result.confidence)")
end
finalize_trial()
Finalize a trial by aggregating results from all processed chunks.
finalize_trial(
session::StreamingSession;
method::Symbol = :weighted_vote,
temporal_weighting::Bool = true
) -> StreamingResult
Parameters:
session::StreamingSession - Active streaming session
method::Symbol - Aggregation method (:weighted_vote, :max_confidence, :unanimous)
temporal_weighting::Bool - Apply paradigm-specific temporal weights (default: true)
Returns: StreamingResult with final prediction and confidence
Example:
# Process trial
for chunk in trial_chunks
process_chunk(session, chunk)
end
# Get final prediction
final_result = finalize_trial(session; method=:weighted_vote, temporal_weighting=true)
println("Final prediction: $(final_result.prediction)")
println("Confidence: $(final_result.confidence)")
println("Aggregation method: $(final_result.aggregation_method)")
Utility Functions
calculate_ITR()
Calculate Information Transfer Rate (ITR) in bits/minute.
calculate_ITR(accuracy::Float64, n_classes::Int, trial_duration::Float64) -> Float64
Parameters:
accuracy::Float64 - Classification accuracy (0.0 to 1.0)
n_classes::Int - Number of classes
trial_duration::Float64 - Trial duration in seconds
Example:
accuracy = 0.85
n_classes = 4
trial_duration = 4.0 # seconds
itr = calculate_ITR(accuracy, n_classes, trial_duration)
println("ITR: $(round(itr, digits=1)) bits/minute")
should_reject_trial()
Check if a trial should be rejected based on confidence threshold.
should_reject_trial(confidence::Float64, threshold::Float64 = 0.7) -> Bool
assess_trial_quality()
Assess the quality of inference results.
assess_trial_quality(result::BatchResult) -> TrialQuality
Returns:
struct TrialQuality
overall_score::Float64
confidence_acceptable::Bool
recommendation::String
end
diagnose_preprocessing()
Diagnose preprocessing quality and provide recommendations.
diagnose_preprocessing(data::BCIData) -> PreprocessingReport
Returns:
struct PreprocessingReport
quality_score::Float64
errors::Vector{String}
warnings::Vector{String}
recommendations::Vector{String}
end
Example:
report = diagnose_preprocessing(data)
if !isempty(report.errors)
@error "Preprocessing issues: $(report.errors)"
end
println("Quality score: $(round(report.quality_score * 100, digits=1))%")
compute_fisher_score()
Compute Fisher discriminant scores for feature selection.
compute_fisher_score(features::Matrix{Float64}, labels::Vector{Int}) -> Vector{Float64}
Parameters:
features::Matrix{Float64} - Feature matrix (n_features × n_samples)
labels::Vector{Int} - Class labels
Returns: Fisher scores for each feature (higher scores indicate better discriminability)
Example:
# Compute discriminability of each feature
fisher_scores = compute_fisher_score(features, labels)
# Find most discriminative features
for (i, score) in enumerate(fisher_scores)
println("Feature $i: Fisher score = $(round(score, digits=3))")
end
rank_features_by_discriminability()
Rank features by their discriminability using Fisher scores.
rank_features_by_discriminability(features::Matrix{Float64}, labels::Vector{Int}) -> Vector{Int}
Parameters:
features::Matrix{Float64} - Feature matrix (n_features × n_samples)
labels::Vector{Int} - Class labels
Returns: Indices of features sorted by discriminability (most discriminative first)
Example:
# Get feature ranking
ranked_indices = rank_features_by_discriminability(features, labels)
println("Most discriminative features:")
for (i, idx) in enumerate(ranked_indices[1:5])
println("$i. Feature $idx (Fisher score: $(round(fisher_scores[idx], digits=3)))")
end
aggregate_chunks()
Aggregate chunk-level predictions and confidences into a final trial result.
aggregate_chunks(
predictions::Vector{Int},
confidences::Vector{Float64},
n_classes::Int;
method::Symbol = :weighted_vote
) -> NamedTuple
Parameters:
predictions::Vector{Int} - Predictions from each chunk
confidences::Vector{Float64} - Confidences from each chunk
n_classes::Int - Number of classes
method::Symbol - Aggregation method (:weighted_vote, :majority_vote, :max_confidence)
Returns: Named tuple with prediction, confidence, and posterior
Example:
# Aggregate chunk results
final_result = aggregate_chunks(
chunk_predictions,
chunk_confidences,
4; # 4 classes
method = :weighted_vote
)
println("Final prediction: $(final_result.prediction)")
println("Final confidence: $(round(final_result.confidence, digits=3))")
get_paradigm_defaults()
Get default parameters for a specific BCI paradigm.
get_paradigm_defaults(paradigm::Symbol) -> NamedTuple
Parameters:
paradigm::Symbol: Paradigm name (:motor_imagery, :p300, :ssvep, :erp)
Returns: Named tuple with default parameters for the paradigm
Example:
# Get motor imagery defaults
defaults = get_paradigm_defaults(:motor_imagery)
println("Chunk size: $(defaults.chunk_size) samples")
println("Confidence threshold: $(defaults.confidence_threshold)")
println("Aggregation method: $(defaults.aggregation_method)")
# Use in metadata
metadata = BCIMetadata(
sampling_rate = 250.0,
paradigm = :motor_imagery,
feature_type = :csp,
n_features = 16,
n_classes = 4,
chunk_size = defaults.chunk_size,
temporal_aggregation = :logvar
)
Data Validation
validate_data()
Validate BCI data for common issues before inference.
validate_data(data::BCIData) -> Bool
Description:
Validates data for NaN/Inf values, correct dimensions, and provides warnings for suspicious data patterns.
Returns: true if validation passes
Throws: Error if validation fails
Example:
# Validate data before inference
try
validate_data(data)
println("✓ Data validation passed")
results = predict_batch(model, data)
catch e
if isa(e, DataValidationError)
@error "Data validation failed: $(error_msg(e))"
else
rethrow(e)
end
end
validate_chunk()
Validate a single chunk for streaming inference.
validate_chunk(chunk::Matrix{Float64}, metadata::BCIMetadata) -> Bool
Description:
Validates chunk dimensions and data quality for streaming inference.
Container for BCI performance metrics.
struct BCIPerformanceMetrics
accuracy::Float64
information_transfer_rate::Float64 # bits/minute
mean_confidence::Float64
mean_latency_ms::Float64
end
Track performance metrics in real-time.
struct OnlinePerformanceTracker
window_size::Int
# ... internal state
end
OnlinePerformanceTracker(; window_size::Int = 50) -> tracker
update_and_report!(tracker, prediction, true_label, confidence) -> metrics
get_metrics(tracker, n_classes, trial_duration) -> BCIPerformanceMetrics
Example:
tracker = OnlinePerformanceTracker(window_size=50)
for (pred, true_lbl, conf) in results
metrics = update_and_report!(tracker, pred, true_lbl, conf)
println("Running accuracy: $(round(metrics.accuracy * 100, digits=1))%")
end
full_metrics = get_metrics(tracker, n_classes=4, trial_duration=4.0)
println("ITR: $(full_metrics.information_transfer_rate) bits/min")
Error Handling
Common exceptions:
AuthenticationError - Invalid or expired API key
DataValidationError - Invalid data format or dimensions
ModelCompatibilityError - Model incompatible with data
QuotaExceededError - API quota limit exceeded
try
results = predict_batch(model, data)
catch e
if isa(e, DataValidationError)
@error "Data validation failed" error_msg(e)
elseif isa(e, AuthenticationError)
@error "Authentication failed - check API key"
else
@error "Inference failed" e
end
end
Configuration
Model Compatibility
Check if a model is compatible with your data:
is_compatible(model, metadata::BCIMetadata) -> Bool
check_model_compatibility(model, data::BCIData) -> Bool
Data Validation
Validate data before inference:
validate_data(data::BCIData) -> (Bool, Vector{String})
Returns validation status and any error messages.
Next Steps
Support