Skip to main content

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)
)

BCIMetadata

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.

Performance Metrics

BCIPerformanceMetrics

Container for BCI performance metrics.
struct BCIPerformanceMetrics
    accuracy::Float64
    information_transfer_rate::Float64  # bits/minute
    mean_confidence::Float64
    mean_latency_ms::Float64
end

OnlinePerformanceTracker

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