# iEEG Toolkit: Goals & Specifications (Pre-Implementation) This document turns the “Future directions” in `ieeg-toolkit.md` into a concrete, testable set of goals and specifications for evolving the iEEG GUI toolkit. ## Scope Interactive exploration of iEEG recorded from a **2D electrode grid**, using: - Panel for app composition - Bokeh/HoloViews for plotting - `cogpy.plot.*` as the implementation home The core UX is: *select electrodes on a grid, inspect time series, and link into richer views (spectrogram/topomap) without losing interactivity on large recordings*. ## Goals - **Deterministic dev fixtures**: every viewer has a “known-good” example dataset for quick debugging and for responsiveness checks on large inputs. - **Explicit schemas**: GUIs consume a small number of named entities with stable dims/coords/attrs (no hidden transposes). - **Composable widgets**: selection, transforms, and viewers remain modular and are linkable via shared state (e.g. time cursor). - **Performance by construction**: - render budgets (downsampling), - avoid full materialization where possible (windowed compute), - predictable latency during selection changes. - **Minimal IO assumptions**: GUIs operate on in-memory `xarray`/`numpy` objects; IO belongs elsewhere. ## Non-goals - Full Neuroscope2 parity (spike sorting, complete annotation systems, file browsing). - A universal viewer for all electrode layouts (start with 2D grids + multichannel). - Making decisions about canonical storage formats (this is GUI/data-model oriented). ## Inputs: expected entities (summary) This toolkit should standardize on a small set of entities (schemas defined in the datasets docs): - `IEEGGridTimeSeries`: `xr.DataArray` with dims `("time","AP","ML")` (canonical target). - `MultichannelTimeSeries`: `xr.DataArray` with dims `("channel","time")`. - Optional: `GridSpectrogram4D` + `BurstPeaksTable` for linked time–frequency views. For GUI development, these are expected to come from bundle constructors (see `explanation/datasets/gui-bundles.md`). ## Component responsibilities (current + planned) ### Current components (implemented) - `ChannelGrid`: selection state + modes; *no rendering*. - `ChannelGridWidget`: electrode grid visualization + interaction; optional atlas image and background scalar. - `MultichannelViewer`: stacked time series viewer with overview strip and downsampling. - `IEEGViewer`: wiring layer: xarray → numpy, z-score, optional grid-driven selection + apply button. ### Planned cross-cutting components (spec only) - `TimeCursor` (shared state): - single “current time” and/or `(t0, t1)` window shared by traces/spectrogram/topomap. - `SelectionPolicy` (pure compute): - converts raw metrics and user intents (“top-N variance”) into a `ChannelGrid.selected` set. - `TransformPipeline` (view-time transforms): - optional reref/filter/standardization applied as a *view* (not necessarily written back). ## Feature specifications derived from “Future directions” Each feature below includes a minimal contract and acceptance criteria. ### 1) Signal-driven selection **User story** - “Given the current visible time window, select the top-N channels by variance (or by correlation to a seed channel).” **Inputs** - `sig_tc` as `xr.DataArray` or numpy view of shape `(channel, time)` (canonical for compute). - Current time window `(t0, t1)` from viewer state. - Strategy parameters: - `metric`: `"variance"` | `"correlation"` - `n`: int - `seed_channel`: optional (for correlation) **Outputs** - A set of `(ap, ml)` pairs (or flat channel indices) that can be applied to `ChannelGrid`. **Acceptance criteria** - Deterministic on fixed input + seed (if randomness is used at all). - Uses only the current window (no full-recording scan unless explicitly requested). - If `n` exceeds available channels, clamps gracefully. ### 2) Sorting (reordering displayed channels) **User story** - “Reorder the stacked traces by AP, ML, variance, or correlation.” **Inputs** - Current selected channel set. - Sorting key: - `"AP"`, `"ML"`, `"variance"`, `"correlation_to_seed"` **Outputs** - Ordered list of flat channel indices passed to `MultichannelViewer.show_channels(...)`. **Acceptance criteria** - Sorting does not change which channels are selected, only their display order. - Sorting is stable (ties deterministic). ### 3) Atlas placement improvements (`atlas_mode="full"`) **User story** - “Show the full atlas image and place the electrode grid in correct physical location, including asymmetric AP extent.” **Inputs** - `ap_coords`, `ml_coords` (physical coords) and atlas placement metadata. **Outputs** - Correct placement parameters (image origin + extent) used by `ChannelGridWidget`. **Acceptance criteria** - The same physical coordinate at two places (grid tick labels and hover labels) maps consistently. - Documented coordinate conventions: which direction is positive AP/ML, and image orientation assumptions. ### 4) Bad channel detection overlay (grid widget) **User story** - “Flag likely bad channels on the grid (e.g., high kurtosis) and allow quick exclusion.” **Inputs** - Metric computed per channel from the current window or full data: - e.g. kurtosis, RMS, line noise power proxy. **Outputs** - A per-cell flag and/or scalar displayed on the grid: - color outline, hatch, or icon-like overlay (implementation-dependent). **Acceptance criteria** - Bad-channel flags do not break selection interaction. - “Exclude bads” is reversible and does not destroy the underlying selection state. ### 5) Linked views (shared time cursor) **User story** - “Move a cursor in the trace view and see the same time reflected in spectrogram and topomap.” **Inputs** - Shared `TimeCursor` state. **Outputs** - Linked updates across all views. **Acceptance criteria** - Cursor updates do not trigger full recompute of unrelated views. - With large mode data, cursor movement remains responsive (bounded redraw cost). ### 6) Lazy loading / windowed materialization **User story** - “Long recordings should not require full `.compute()` to be viewable.” **Inputs** - Potentially Dask-backed xarray arrays. - Current visible time window and rendering budgets (`detail_px`, `overview_px`). **Outputs** - View-time window extraction and downsampling. **Acceptance criteria** - Large mode can run without materializing the full array in memory. - Window changes only compute what is needed for the view. - Explicitly document any “overview strip” assumptions (e.g., precomputed mean trace vs streaming). ## Performance targets (guidance) - “Small mode”: viewer construction + first render should be near-instant. - “Large mode”: interactions should remain bounded: - selection changes should not rebuild the entire HoloViews object graph, - time window changes should not allocate full-resolution traces. Bundle sizing guidance is specified in `explanation/datasets/modes.md`. ## Compatibility notes / known tensions - Current examples and some code paths use `("time","ML","AP")` in places. The **spec target** is `("time","AP","ML")` for grid-aware signals. - The flattening convention (AP-major vs ML-major) must be defined once and tested because it affects: - `ChannelGrid.flat_indices` - `stack(channel=("AP","ML"))` ordering - wiring between grid selection and trace display ## Proposed roadmap (phased) 1. **Schema hardening**: choose canonical grid dims/flattening; add validation helpers. 2. **Selection improvements**: signal-driven selection + sorting + presets. 3. **QC overlays**: bad-channel metrics and grid overlays. 4. **Linked views**: shared time cursor + spectrogram + topomap. 5. **Scalability**: windowed compute + optional Dask-friendly paths.