> ## 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.

# Basic Examples

> Compact Nimbus BCI recipes for Personalizer, motor imagery, P300, streaming, calibration, diagnostics, and model comparison.

Use these recipes after installation and preprocessing are complete. Python examples lead with `Personalizer` → `BrainState`; classical heads remain available for research CV and Julia.

<Note>
  * Product path: [Personalizer](/personalizer/overview)
  * Paradigms: [Paradigms & profiles](/personalizer/paradigms)
  * Python quickstart: [Python SDK Quickstart](/python-sdk/quickstart)
  * Julia quickstart: [Julia SDK Quickstart](/julia-sdk/quickstart)
  * Feature preparation: [Preprocessing Requirements](/inference-configuration/preprocessing-requirements)
</Note>

## Personalizer (frozen encoder)

Wrap any `encode(X) → Z`, personalize online, consume `BrainState`.

```python theme={null}
from nimbus_bci import Personalizer, wrap

# Toy identity encoder — replace with partner / foundation encode(X) → Z
enc = wrap(lambda X: X, model_id="demo-encoder", embedding_dim=16)
adapter = Personalizer.for_deployment(enc, ["left", "right"])
adapter.fit(X_cal, y_cal)
states = adapter.predict(X_test)
print(states[0].intent, states[0].confidence, states[0].need_more_data)
```

Already have CSP / bandpower features? Use `Personalizer.for_features(["left", "right"])` — same app surface. Full contract: [Personalizer](/personalizer/overview).

## Motor imagery

Motor imagery workflows usually use CSP or bandpower features from 8–30 Hz EEG — or embeddings from a frozen trunk.

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    from nimbus_bci import Personalizer
    from nimbus_bci.compat import extract_csp_features
    from sklearn.model_selection import cross_val_score

    # epochs: MNE Epochs object, already filtered and artifact-cleaned
    X, csp = extract_csp_features(epochs, n_components=8)
    y = epochs.events[:, 2]

    adapter = Personalizer.for_features(["left", "right"], head="lda")
    adapter.fit(X, y)
    states = adapter.predict(X[:5])

    # Research CV still uses the head
    scores = cross_val_score(adapter.head, X, y, cv=5)
    print(states[0].intent, f"CV accuracy: {scores.mean():.2%}")
    ```
  </Tab>

  <Tab title="Julia">
    ```julia theme={null}
    using NimbusSDK
    using Statistics

    NimbusSDK.install_core("your-api-key")

    features = load_csp_features()  # (n_features, n_samples, n_trials)
    labels = load_labels()          # 1-indexed labels

    metadata = BCIMetadata(
        sampling_rate = 250.0,
        paradigm = :motor_imagery,
        feature_type = :csp,
        n_features = size(features, 1),
        n_classes = length(unique(labels)),
        chunk_size = nothing
    )

    data = BCIData(features, metadata, labels)
    model = train_model(NimbusLDA, data; iterations=50)
    results = predict_batch(model, data)

    accuracy = mean(results.predictions .== labels)
    println("Accuracy: $(round(accuracy * 100, digits=1))%")
    ```
  </Tab>
</Tabs>

Default head is LDA (`Personalizer(head="lda")`). Try `head="qda"` when class covariances differ substantially.

## P300 detection

Preferred product path (`pip install nimbus-bci[riemann]`): xDAWN → tangent via `Personalizer.for_p300`. Classical ERP amplitude features still work with `for_features`.

<Tabs>
  <Tab title="Python (for_p300)">
    ```python theme={null}
    from nimbus_bci import Personalizer

    # epochs: target/non-target MNE epochs (raw-ish ERP windows)
    p = Personalizer.for_p300(
        ["NonTarget", "Target"],
        tracking_alpha=0.05,  # opt-in geodesic tracking
    )
    p.fit(epochs_cal, y_cal)  # see fit_p300_session for day-2 bundles
    states = p.predict(epochs_test)
    print(states[0].intent, states[0].confidence)
    ```
  </Tab>

  <Tab title="Python (ERP features)">
    ```python theme={null}
    from nimbus_bci import Personalizer

    # epochs: target/non-target MNE epochs
    sfreq = epochs.info["sfreq"]
    start = int(0.3 * sfreq)
    stop = int(0.5 * sfreq)

    X = epochs.get_data()[:, :, start:stop].mean(axis=2)
    y = epochs.events[:, 2]

    adapter = Personalizer.for_features(["nontarget", "target"], head="qda")
    adapter.fit(X, y)
    states = adapter.predict(X[:10])
    print(states[0].intent, states[0].confidence)
    ```
  </Tab>

  <Tab title="Julia">
    ```julia theme={null}
    using NimbusSDK

    features = load_erp_features()  # (n_channels, n_samples, n_trials)
    labels = load_labels()

    metadata = BCIMetadata(
        sampling_rate = 250.0,
        paradigm = :p300,
        feature_type = :erp_amplitude,
        n_features = size(features, 1),
        n_classes = length(unique(labels)),
        chunk_size = nothing
    )

    data = BCIData(features, metadata, labels)
    model = train_model(NimbusQDA, data; iterations=50)
    results = predict_batch(model, data)
    ```
  </Tab>
</Tabs>

## Streaming

Streaming uses short feature chunks and aggregates chunk predictions into a trial decision. Bridge to `BrainState` for apps.

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    from nimbus_bci import (
        NimbusLDA,
        StreamingSession,
        brain_state_from_streaming_result,
    )
    from nimbus_bci.data import BCIMetadata

    clf = NimbusLDA().fit(X_train, y_train)

    metadata = BCIMetadata(
        sampling_rate=250.0,
        paradigm="motor_imagery",
        feature_type="csp",
        n_features=X_train.shape[1],
        n_classes=len(set(y_train)),
        chunk_size=250,
    )

    session = StreamingSession(clf.model_, metadata)

    for chunk in feature_stream:
        session.process_chunk(chunk)

    final = session.finalize_trial(method="weighted_vote")
    state = brain_state_from_streaming_result(final, classes=["left", "right"])
    print(state.intent, state.confidence, state.need_more_data)
    session.reset()
    ```
  </Tab>

  <Tab title="Julia">
    ```julia theme={null}
    session = init_streaming(model, metadata)

    for chunk in feature_stream
        result = process_chunk(session, chunk)
    end

    final = finalize_trial(session; method=:weighted_vote)
    ```
  </Tab>
</Tabs>

For frozen-encoder product apps, prefer [Personalizer](/personalizer/overview) (batch `predict` → `BrainState`) over a raw streaming session. API detail: [Python Streaming Inference](/python-sdk/streaming-inference), [Streaming inference configuration](/inference-configuration/streaming-inference).

## Calibration and normalization

Estimate normalization parameters from calibration or training data only, then reuse them for test and deployment data.

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    from nimbus_bci import estimate_normalization_params, apply_normalization

    norm = estimate_normalization_params(X_train, method="zscore")
    X_train_norm = apply_normalization(X_train, norm)
    X_test_norm = apply_normalization(X_test, norm)
    ```
  </Tab>

  <Tab title="Julia">
    ```julia theme={null}
    norm = estimate_normalization_params(train_features; method=:zscore)
    train_norm = apply_normalization(train_features, norm)
    test_norm = apply_normalization(test_features, norm)
    ```
  </Tab>
</Tabs>

See [Feature Normalization](/inference-configuration/feature-normalization) for cross-session details.

## Diagnostics

Run diagnostics when confidence is unexpectedly low or accuracy drops across sessions.

```julia theme={null}
report = diagnose_preprocessing(bci_data)

if !isempty(report.errors)
    error("Preprocessing errors: $(report.errors)")
end

println("Quality score: $(round(report.quality_score * 100, digits=1))%")
```

For Python trial-level quality checks, see the [Python SDK API Reference](/python-sdk/api-reference#metrics) and [BrainState](/personalizer/brain-state) gates.

## Choosing a starting point

| Path | Use first when | Notes |
| - | - | - |
| `Personalizer.for_deployment` + `wrap` | Frozen / foundation encoder | Product wedge → `BrainState` |
| `Personalizer.for_features` | Classical CSP / bandpower / ERP features | Same app surface; pick `head=` |
| `Personalizer.for_riemann` / `for_p300` | Weak-Z / P300 geometry (`[riemann]`) | Geodesic tracking + session bundles |
| Standalone `NimbusLDA` / … | Research CV, sklearn pipelines | Heads power Personalizer under the hood |
| Julia `NimbusLDA` / `NimbusQDA` | Julia SDK workflows | No Personalizer yet |

| Head | Typical use |
| - | - |
| `lda` | Fast motor-imagery baseline |
| `qda` | Overlapping distributions (e.g. P300) |
| `softmax` | Nonlinear multinomial (Python extra) |
| STS / Probit | Long-session drift (Python) / Julia multinomial |

Full comparison: [Model Specification](/model-specification).

## Next read

<Columns cols={2}>
  <Card title="Personalizer" icon="layers" href="/personalizer/overview">
    Encoder contract and BrainState
  </Card>

  <Card title="Paradigms & profiles" icon="route" href="/personalizer/paradigms">
    MI / P300 session bundles and day-2 profiles
  </Card>

  <Card title="Python SDK Quickstart" icon="braces" href="/python-sdk/quickstart">
    First Python workflow from install to inference
  </Card>

  <Card title="Advanced Applications" icon="rocket" href="/examples/advanced-applications">
    Higher-level deployment patterns
  </Card>
</Columns>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.