> ## Documentation Index
> Fetch the complete documentation index at: https://docs.nimbusbci.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Model Specification

> Technical overview of Nimbus probabilistic models, including NimbusLDA, NimbusQDA, NimbusSoftmax, NimbusProbit, and adaptive NimbusSTS.

# Probabilistic Model Specification

NimbusSDK provides **pre-built probabilistic models** powered by [RxInfer.jl](https://github.com/ReactiveBayes/RxInfer.jl), a reactive message passing framework for efficient Bayesian inference.

<Info>
  **Nimbus Models Available:**

  * [NimbusLDA](/models/rxlda) - Fast, shared covariance (**Python + Julia**)
  * [NimbusQDA](/models/rxgmm) - Flexible, class-specific covariances (**Python + Julia**)
  * [NimbusSoftmax](/models/rxpolya) - Non-Gaussian static model (**Python only**)
  * [NimbusProbit](/models/nimbusprobit) - Non-Gaussian static model (**Julia only**)
  * [NimbusSTS](/models/rxsts) - Stateful adaptive model (**Python only**)

  For detailed implementation, see the individual model pages above.
</Info>

***

## Start Here

<CardGroup cols={3}>
  <Card title="Bayesian LDA" icon="brain" href="/models/rxlda">
    Start with fast shared-covariance classification.
  </Card>

  <Card title="Bayesian QDA" icon="brain" href="/models/rxgmm">
    Use class-specific covariance for overlapping classes.
  </Card>

  <Card title="Bayesian STS" icon="brain" href="/models/rxsts">
    Choose adaptive stateful modeling for non-stationarity.
  </Card>
</CardGroup>

## Model Architecture

The static models (LDA, QDA, Softmax, Probit) are built on **factor graphs** with **reactive message passing**:

```
        Prior               Likelihood            Posterior
    p(class)  ────────────────────────────────►  p(class|data)
                          ▲
                          │
                      p(data|class)
                   (Gaussian or Softmax)
```

### Factor Graphs

Factor graphs represent the joint probability distribution:

$$
p(\text{class}, \text{data}) = p(\text{class}) \cdot p(\text{data}|\text{class})
$$

**Components:**

1. **Prior**: $p(\text{class})$ - uniform or learned class probabilities
2. **Likelihood**: $p(\text{data}|\text{class})$ - Gaussian (LDA/QDA) or a non-Gaussian classifier (Softmax/Probit)
3. **Posterior**: $p(\text{class}|\text{data})$ - computed via message passing

### Reactive Message Passing

RxInfer.jl uses **reactive programming** for efficient inference:

```
Data Stream ──► Factor Graph ──► Message Passing ──► Posterior Updates
                     │
                     ▼
              (Incremental Updates)
```

**Benefits:**

* **Incremental processing**: Process data chunks as they arrive
* **Low latency**: 10-25ms per chunk
* **Memory efficient**: Constant memory usage
* **Real-time capable**: Streaming inference without buffering

## Model Comparison

| Feature               | NimbusLDA                 | NimbusQDA                 | NimbusSoftmax (Py)        | NimbusSTS (Py)                                |
| --------------------- | ------------------------- | ------------------------- | ------------------------- | --------------------------------------------- |
| **Covariance**        | Shared across classes     | Class-specific            | N/A (logistic)            | N/A (state-space)                             |
| **Decision Boundary** | Linear                    | Quadratic                 | Non-linear (flexible)     | Dynamic (time-varying)                        |
| **Temporal Model**    | Static                    | Static                    | Static                    | **Stateful (EKF)**                            |
| **Flexibility**       | Lower                     | Higher                    | Highest                   | Adaptive                                      |
| **Speed**             | Fastest (10-15ms)         | Fast (15-25ms)            | Fast (15-25ms)            | Real-time (20-30ms)                           |
| **Best For**          | Motor imagery, stationary | P300, overlapping classes | Complex multinomial       | **Non-stationary, long sessions**             |
| **State Management**  | None                      | None                      | None                      | **`propagate_state()`, `get_latent_state()`** |
| **Online Learning**   | `partial_fit()`           | `partial_fit()`           | `partial_fit()`           | **Continuous with delayed feedback**          |
| **Use Case**          | Short sessions            | Short sessions            | Short sessions            | **Long sessions, cross-day, adaptive BCI**    |
| **Training Time**     | Fast                      | Moderate                  | Moderate                  |                                               |
| **Parameters**        | Fewer (efficient)         | More (flexible)           | Most (very flexible)      |                                               |
| **Best For**          | Well-separated classes    | Overlapping distributions | Complex multinomial tasks |                                               |
| **Overfitting Risk**  | Lowest                    | Moderate                  | Higher (more parameters)  |                                               |
| **Data Requirements** | 40+ trials/class          | 60+ trials/class          | 80+ trials/class          |                                               |

***

## BCI Paradigm Applications

### Motor Imagery

**Recommended Model**: Bayesian LDA (NimbusLDA)

Motor imagery classes are typically well-separated in CSP feature space, making NimbusLDA ideal:

* **2-class** (left/right hand): 75-90% accuracy
* **4-class** (hands/feet/tongue): 70-85% accuracy
* **Inference**: 10-15ms per trial
* **ITR**: 15-25 bits/minute

**Why NimbusLDA?**

* Fast inference for real-time control
* Shared covariance assumption holds well
* Lowest data requirements

See [Basic Examples - Motor Imagery](/examples/basic-examples#motor-imagery) for implementation.

### P300 Event-Related Potential

**Recommended Model**: Bayesian QDA (NimbusQDA)

P300 target and non-target ERPs have overlapping distributions, requiring flexible modeling:

* **Binary detection**: 85-95% accuracy (with averaging)
* **Inference**: 15-25ms per epoch
* **ITR**: 10-20 bits/minute

**Why NimbusQDA?**

* Class-specific covariances capture ERP morphology
* Better for overlapping distributions
* Handles individual differences

See [Basic Examples - P300 Detection](/examples/basic-examples#p300-detection) for implementation.

### SSVEP (Steady-State Visual Evoked Potential)

**Recommended Model**: NimbusLDA or NimbusQDA

* **4-target**: 85-95% accuracy, use NimbusLDA
* **6+ target**: 80-90% accuracy, use NimbusQDA
* **Inference**: 10-20ms per trial
* **ITR**: 30-50 bits/minute

**Model Selection:**

* **NimbusLDA**: For 2-4 targets with well-separated frequencies
* **NimbusQDA**: For 6+ targets with overlapping harmonics

See [Advanced Applications](/examples/advanced-applications) for deployment patterns and model-combination guidance.

### Paradigm Comparison

| Paradigm                   | Recommended Model                          | Typical Accuracy                 | ITR            | User Training |
| -------------------------- | ------------------------------------------ | -------------------------------- | -------------- | ------------- |
| **Motor Imagery**          | NimbusLDA (or NimbusSTS for long sessions) | 70-85% (4-class)                 | 15-25 bits/min | High          |
| **P300**                   | NimbusQDA                                  | 85-95% (with reps)               | 10-20 bits/min | Low           |
| **SSVEP**                  | NimbusLDA / NimbusQDA                      | 85-95% (4-class)                 | 30-50 bits/min | Low           |
| **Long-duration Adaptive** | **NimbusSTS (Py)**                         | **75-90% (maintains over time)** | **Varies**     | **Medium**    |

<Note>
  **New: Use NimbusSTS (Python-only)** for sessions >30 minutes or when you observe accuracy degradation over time. It's the only model that explicitly handles temporal drift and non-stationarity.
</Note>

***

## Advanced Techniques

### Hyperparameter Optimization

Use grid search or Bayesian optimization to find optimal hyperparameters:

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    from sklearn.model_selection import GridSearchCV
    from nimbus_bci import NimbusLDA

    param_grid = {
        'mu_scale': [1.0, 3.0, 5.0, 7.0],
        'class_prior_alpha': [0.5, 1.0, 2.0]
    }

    grid = GridSearchCV(NimbusLDA(), param_grid, cv=5, n_jobs=-1)
    grid.fit(X_train, y_train)

    print(f"Best params: {grid.best_params_}")
    best_clf = grid.best_estimator_
    ```
  </Tab>

  <Tab title="Julia">
    ```julia theme={null}
    # Grid search over hyperparameters
    mean_prior_precisions = [0.001, 0.01, 0.05, 0.1]
    best_accuracy = 0.0
    best_params = nothing

    for mean_prior_precision in mean_prior_precisions
        model = train_model(
            NimbusLDA,
            train_data;
            mean_prior_precision=mean_prior_precision
        )
        results = predict_batch(model, val_data)
        accuracy = sum(results.predictions .== val_data.labels) / length(val_data.labels)
        
        if accuracy > best_accuracy
            best_accuracy = accuracy
            best_params = (mean_prior_precision=mean_prior_precision,)
        end
    end
    ```
  </Tab>
</Tabs>

See [Python SDK - sklearn Integration](/python-sdk/sklearn-integration) for more tuning examples.

### Cross-Subject Transfer Learning

Train on multiple subjects for better generalization:

```python theme={null}
from nimbus_bci import NimbusLDA, estimate_normalization_params, apply_normalization
import numpy as np

# Collect data from multiple subjects
all_X = [load_subject_data(s) for s in subjects]
X_combined = np.vstack(all_X)

# Normalize across subjects
norm_params = estimate_normalization_params(X_combined, method="zscore")
X_norm = apply_normalization(X_combined, norm_params)

# Train with higher regularization
clf = NimbusLDA(mu_scale=7.0)  # Higher than single-subject
clf.fit(X_norm, y_combined)

# Calibrate for new subject
clf.partial_fit(X_new_subject[:20], y_new[:20])
```

### Ensemble Methods

Combine multiple models for improved robustness:

```python theme={null}
from nimbus_bci import NimbusLDA, NimbusQDA
import numpy as np

# Train multiple models
clf_lda = NimbusLDA()
clf_lda.fit(X_train, y_train)

clf_qda = NimbusQDA()
clf_qda.fit(X_train, y_train)

# Ensemble prediction (weighted average)
probs_lda = clf_lda.predict_proba(X_test)
probs_qda = clf_qda.predict_proba(X_test)

ensemble_probs = 0.6 * probs_lda + 0.4 * probs_qda
ensemble_predictions = ensemble_probs.argmax(axis=1)
```

### Confidence Calibration

Ensure predicted probabilities match actual accuracy:

```python theme={null}
from sklearn.calibration import CalibratedClassifierCV
from nimbus_bci import NimbusLDA, compute_calibration_metrics

# Train and calibrate
clf = NimbusLDA()
clf.fit(X_train, y_train)

clf_calibrated = CalibratedClassifierCV(clf, method='isotonic', cv='prefit')
clf_calibrated.fit(X_val, y_val)

# Check calibration improvement
calib_metrics = compute_calibration_metrics(preds, confidences, y_test)
print(f"ECE: {calib_metrics.ece:.3f}")
```

***

## Model Limitations

<Warning>
  **General Limitations:**

  * **Static models**: No built-in temporal dynamics (use preprocessing for temporal features)
  * **Supervised only**: Require labeled training data
  * **Fixed structure**: Cannot modify factor graph structure at runtime

  **Model-Specific:**

  * **NimbusLDA**: Assumes Gaussian distributions with shared covariance, linear decision boundaries
  * **NimbusQDA**: Assumes Gaussian distributions with class-specific covariances, higher overfitting risk with limited data
  * **NimbusSoftmax / NimbusProbit**: May require more training data than LDA/QDA for complex tasks
</Warning>

## Troubleshooting

<AccordionGroup>
  <Accordion title="Model Overfitting" icon="alert-triangle">
    **Symptoms**: High training accuracy, low test accuracy

    **Solutions**:

    * Increase `mu_scale` (stronger regularization)
    * Use cross-validation for hyperparameter tuning
    * Collect more training data
    * Apply ensemble methods
  </Accordion>

  <Accordion title="Poor Cross-Subject Generalization" icon="users">
    **Symptoms**: Good within-subject, poor across-subject performance

    **Solutions**:

    * Train on multi-subject data
    * Increase regularization (`mu_scale`)
    * Normalize features consistently
    * Use subject-specific calibration
  </Accordion>

  <Accordion title="Class Imbalance" icon="scale">
    **Symptoms**: Model biased toward majority class

    **Solutions**:

    * Use class weighting
    * Apply SMOTE or other resampling
    * Adjust decision threshold
    * Use stratified cross-validation
  </Accordion>
</AccordionGroup>

***

## Next Read

<CardGroup cols={2}>
  <Card title="NimbusLDA" icon="brain" href="/models/rxlda">
    Fast, shared covariance classifier
  </Card>

  <Card title="NimbusQDA" icon="brain" href="/models/rxgmm">
    Flexible, class-specific covariances
  </Card>

  <Card title="NimbusSTS (Python)" icon="brain" href="/models/rxsts">
    Adaptive, for non-stationary data
  </Card>

  <Card title="NimbusSoftmax (Python)" icon="brain" href="/models/rxpolya">
    Non-Gaussian static classification
  </Card>

  <Card title="NimbusProbit (Julia)" icon="brain" href="/models/nimbusprobit">
    Non-Gaussian static classification
  </Card>

  <Card title="Basic Examples" icon="play" href="/examples/basic-examples">
    Compact Python and Julia recipes
  </Card>

  <Card title="Advanced Applications" icon="rocket" href="/examples/advanced-applications">
    Calibration, adaptation, hybrid systems
  </Card>
</CardGroup>

***

<Note>
  **Development Philosophy**: Nimbus provides **battle-tested, production-ready models** (NimbusLDA, NimbusQDA, NimbusSoftmax (Py), NimbusProbit (Jl), NimbusSTS (Py)) that are proven effective for BCI applications. These models cover the majority of BCI use cases with fast inference, uncertainty quantification, and online learning. NimbusSTS adds adaptive capabilities for non-stationary data and long-duration sessions.
</Note>

<script
  type="application/ld+json"
  dangerouslySetInnerHTML={{
__html: JSON.stringify({
  '@context': 'https://schema.org',
  '@type': 'TechArticle',
  headline: 'Model Specification',
  description:
    'Overview of probabilistic models in Nimbus. Learn about NimbusLDA/NimbusQDA plus non-Gaussian models (NimbusSoftmax, NimbusProbit) and adaptive NimbusSTS with factor graphs and reactive message passing.',
  author: {
    '@type': 'Organization',
    name: 'Nimbus BCI',
    url: 'https://nimbusbci.com',
  },
  publisher: {
    '@type': 'Organization',
    name: 'Nimbus BCI',
    url: 'https://nimbusbci.com',
  },
  about: {
    '@type': 'Thing',
    name: 'Brain-Computer Interface',
    description: 'Probabilistic model specification for Bayesian BCI inference',
  },
  keywords:
    'model specification, probabilistic models, Bayesian models, factor graphs, message passing, RxInfer, BCI models, Bayesian inference, Nimbus models',
  inLanguage: 'en-US',
  isAccessibleForFree: true,
}),
}}
/>
