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

# Python SDK Quickstart

> Build your first nimbus-bci Personalizer in minutes — wrap an encoder, adapt online, ship BrainState — plus classical sklearn heads.

Get started with nimbus-bci: wrap any frozen encoder, personalize with a Bayesian head, and consume `BrainState` outputs.

<Info>
  **Time to complete:** \~10 minutes

  This guide covers installation, Personalizer (product path), classical heads, and online updates. Deeper topics link out below.
</Info>

## Prerequisites

Before you begin:

* **Python ≥ 3.11** installed
* **Embeddings** from a frozen trunk, or **preprocessed features** (CSP, bandpower, etc.) — see [Encoder contract](/personalizer/encoder-contract) and [preprocessing requirements](/inference-configuration/preprocessing-requirements)

<Tip>
  nimbus-bci expects **embeddings or preprocessed features**, not raw EEG. Plug a frozen trunk via `wrap()`, or extract CSP / bandpower with MNE-Python.
</Tip>

***

<Steps>
  <Step title="Install nimbus-bci">
    Install from PyPI:

    ```bash theme={null}
    pip install nimbus-bci
    ```

    For MNE-Python integration:

    ```bash theme={null}
    pip install nimbus-bci[mne]
    ```

    Verify installation:

    ```python theme={null}
    from nimbus_bci import Personalizer, wrap, BrainState
    import nimbus_bci
    print(f"nimbus-bci version: {nimbus_bci.__version__}")
    ```
  </Step>

  <Step title="Personalizer (product path)">
    Wrap a frozen encoder and adapt online:

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

    np.random.seed(42)
    n_samples, n_features = 100, 16
    X_cal = np.random.randn(n_samples, n_features)
    y_cal = np.random.randint(0, 2, n_samples)
    X_test = np.random.randn(20, n_features)

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

    <Info>
      Prefer factories: `for_deployment` (strict gating), `for_research` (permissive + knobs), `for_features` (no trunk). Full contract: [Personalizer & Middleware](/personalizer/overview).
    </Info>
  </Step>

  <Step title="Classical features (optional)">
    Already have CSP / bandpower vectors? Use a head directly or `Personalizer.for_features`:

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

    np.random.seed(42)
    n_samples, n_features = 100, 16
    X_train = np.random.randn(n_samples, n_features)
    y_train = np.random.randint(0, 4, n_samples)

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

    X_test = np.random.randn(20, n_features)
    predictions = clf.predict(X_test)
    probabilities = clf.predict_proba(X_test)

    print(f"Predictions: {predictions}")
    print(f"Probabilities shape: {probabilities.shape}")  # (20, 4)

    # Same app surface without a trunk:
    # p = Personalizer.for_features(["a", "b", "c", "d"]).fit(X_train, y_train)
    ```
  </Step>

  <Step title="sklearn pipeline">
    Use nimbus-bci with sklearn pipelines:

    ```python theme={null}
    from sklearn.pipeline import make_pipeline
    from sklearn.preprocessing import StandardScaler
    from sklearn.model_selection import cross_val_score
    from nimbus_bci import NimbusLDA

    pipe = make_pipeline(
        StandardScaler(),
        NimbusLDA(mu_scale=3.0),
    )
    pipe.fit(X_train, y_train)
    predictions = pipe.predict(X_test)
    scores = cross_val_score(pipe, X_train, y_train, cv=5)
    print(f"CV Accuracy: {scores.mean():.2%} (+/- {scores.std():.2%})")
    ```

    <Tip>
      Heads are sklearn-compatible. Use them with `Pipeline`, `GridSearchCV`, and `cross_val_score`. See [sklearn Integration](/python-sdk/sklearn-integration).
    </Tip>
  </Step>

  <Step title="Online updates">
    Update with new labeled trials via `adapt` / `partial_fit`, and optionally gate with `recommend_adapt`:

    ```python theme={null}
    from nimbus_bci import NimbusLDA

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

    X_new = np.random.randn(10, n_features)
    y_new = np.random.randint(0, 4, 10)
    clf.partial_fit(X_new, y_new)

    # Personalizer path
    # adapter.adapt(X_lab, y_lab)           # preferred gateway
    # adapter.partial_fit(X_lab, y_lab)     # head-only conjugate update
    # dec = adapter.recommend_adapt(X_stream, tau=your_tau)
    # if dec.should_adapt:
    #     adapter.partial_fit(X_lab, y_lab)
    ```

    <Info>
      Trial rejection (`BrainState.rejected` / `need_more_data`) is a different gate from adapt recommendations. Presets are `strict` / `permissive` (0.6 rename). See [BrainState](/personalizer/brain-state) and [Migration to 0.6](/python-sdk/migration-0.6).
    </Info>
  </Step>
</Steps>

***

## Available heads

| Model | Best for | Notes |
| - | - | - |
| **NimbusLDA** | Motor imagery, well-separated classes | Default `Personalizer(head="lda")` |
| **NimbusQDA** | Overlapping distributions (e.g. P300) | Class-specific covariances |
| **NimbusSoftmax** | Multiclass nonlinear boundaries | Optional: `pip install nimbus-bci[softmax]` |
| **NimbusSTS** | Non-stationary / long sessions | Stateful; see [Bayesian STS](/models/rxsts) |

## Continue with

<Columns cols={2}>
  <Card title="Personalizer & Middleware" icon="layers" href="/personalizer/overview">
    Encoder contract, adapt helper, BrainState
  </Card>

  <Card title="Active Learning" icon="target" href="/python-sdk/active-learning">
    `CalibrationSession` and label-efficient calibration
  </Card>

  <Card title="Streaming Inference" icon="activity" href="/python-sdk/streaming-inference">
    Chunk-by-chunk real-time processing
  </Card>

  <Card title="MNE Integration" icon="brain" href="/python-sdk/mne-integration">
    Epochs → features or frozen encoder
  </Card>

  <Card title="sklearn Integration" icon="code" href="/python-sdk/sklearn-integration">
    Pipelines, CV, and GridSearch
  </Card>

  <Card title="API Reference" icon="book" href="/python-sdk/api-reference">
    Full symbol reference
  </Card>
</Columns>

## Quickstart FAQ

<AccordionGroup>
  <Accordion title="Can I use nimbus-bci with raw EEG?" icon="circle-question-mark">
    Not directly. Expect embeddings or preprocessed features (CSP, bandpower, ERP). See [Preprocessing Requirements](/inference-configuration/preprocessing-requirements).
  </Accordion>

  <Accordion title="Which classifier should I start with?" icon="brain">
    Start with `NimbusLDA` (default Personalizer head). Try `NimbusQDA` for overlapping distributions, and `NimbusSTS` for non-stationary sessions.
  </Accordion>

  <Accordion title="Where do Riemannian / P300 recipes live?" icon="code">
    Optional extra: `pip install nimbus-bci[riemann]`, then `Personalizer.for_riemann(...)`, `Personalizer.for_p300(...)`, or `make_riemann_nimbus_pipeline(...)`. See [sklearn Integration](/python-sdk/sklearn-integration) and the API reference.
  </Accordion>
</AccordionGroup>

## Support

* **Email**: [hello@nimbusbci.com](mailto:hello@nimbusbci.com)
* **Docs**: [docs.nimbusbci.com](https://docs.nimbusbci.com)
* **PyPI**: [pypi.org/project/nimbus-bci](https://pypi.org/project/nimbus-bci/)


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