Skip to content

[FIX] ASR: make nonlinear_eigenspace reproducible - #118

Merged
nbara merged 1 commit into
nbara:masterfrom
sappelhoff:fix/asr-riemann-reproducible
Jul 16, 2026
Merged

[FIX] ASR: make nonlinear_eigenspace reproducible#118
nbara merged 1 commit into
nbara:masterfrom
sappelhoff:fix/asr-riemann-reproducible

Conversation

@sappelhoff

Copy link
Copy Markdown
Collaborator

nonlinear_eigenspace drew its optimization starting point from an unseeded module-level np.random.default_rng(), so the riemann ASR path produced slightly different results on every call. Because the manifold optimizer converges to the same eigenspace regardless of the starting point, this only injected run-to-run numerical noise — but that noise makes the riemann results non-reproducible and destabilizes the incremental-vs-bulk equivalence check in test_asr_class (its < 6µV tolerance can be exceeded depending on dependency versions and the covariance regime).

Seed the starting point with a fixed value so the riemann path is reproducible run-to-run. The mathematical result is unchanged; only the previously-random initialization is now deterministic.

Testing

Adds test_nonlinear_eigenspace_reproducible asserting repeated calls return identical results (fails on the unseeded code, passes after). Full tests/test_cov.py and tests/test_asr.py pass; ruff clean.

@codecov

codecov Bot commented Jul 13, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 83.10%. Comparing base (3721224) to head (fa2ad8d).

Additional details and impacted files
@@            Coverage Diff             @@
##           master     #118      +/-   ##
==========================================
- Coverage   83.10%   83.10%   -0.01%     
==========================================
  Files          25       25              
  Lines        2818     2817       -1     
==========================================
- Hits         2342     2341       -1     
  Misses        476      476              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

nonlinear_eigenspace drew its optimization starting point from an unseeded
module-level Generator, so the riemann ASR path produced slightly different
results on every call. The starting point only affects convergence, not the
resulting eigenspace, so seed it with a fixed value. This makes the riemann
ASR output reproducible run-to-run and stabilizes the incremental-vs-bulk
equivalence check in test_asr_class.

Adds a regression test asserting repeated calls return identical results.
@nbara
nbara merged commit 752f7b2 into nbara:master Jul 16, 2026
7 checks passed
@sappelhoff
sappelhoff deleted the fix/asr-riemann-reproducible branch July 16, 2026 08:55
nbara added a commit that referenced this pull request Jul 18, 2026
…ut guards (#121)

A small bundle of documentation and defensive-hardening fixes:

- **`fit_eeg_distribution` docstring:** the `min_clean_fraction` /
`max_dropout_fraction` default values were transposed relative to the
signature (`0.25` / `0.1`); corrected. Also `X` was described as a 2-D
`(n_channels, n_samples)` array, but the function operates on a **1-D**
vector of amplitude values (it does `np.sort(X); n=len(X)`) —
description fixed.
- **`asr_calibrate` non-finite guard:** a `NaN`/`Inf` in the calibration
data previously propagated silently through the filter into `M`/`T` (an
all-`NaN` threshold, no error). Non-finite samples are now zeroed before
filtering so calibration degrades gracefully.
- **`asr_calibrate` insufficient-data guards:** raise a clear
`ValueError` when the calibration data is too short to form one analysis
window, or yields fewer than two threshold-estimation windows — instead
of an obscure downstream error.

### Testing
Adds tests that non-finite calibration input yields finite `M`/`T`, and
that too-short calibration raises a clear `ValueError`. Full
`tests/test_asr.py` + `tests/test_cov.py` pass; `ruff` clean.

---
**Note:** includes the `nonlinear_eigenspace` reproducibility fix from
#118 so the riemann CI test is stable regardless of what this PR
changes. Once #118 merges first, that commit drops out on rebase.

Co-authored-by: Nicolas Barascud <10333715+nbara@users.noreply.github.com>
nbara pushed a commit that referenced this pull request Jul 20, 2026
…120)

`fit_eeg_distribution`'s vectorized grid-search histogram divides by a
per-column baseline-relative value that is **zero** when a run of
samples is tied (e.g. a flatlined / disconnected electrode): `cols =
nbins / newX[mcurr]` → `inf`, then `H = newX[:m] * cols` → `nan`
(0·inf). Casting `nan` to int yields `INT64_MIN`, and `np.bincount` then
raises `ValueError: 'list' argument must have no negative elements`.

A flat segment is exactly the dropout case `max_dropout_fraction` is
meant to tolerate, so the fit should degrade gracefully rather than
crash. This makes the histogram binning ignore non-finite entries (count
them in no bin), so a degenerate grid column just yields a poor — but
finite — objective and the grid search continues. Results on finite
input are unchanged.

The crash is reachable through the primary public entry point:
`ASR().fit(X)` with a single dead channel currently raises `ValueError`.

### Testing
Adds three tests: the direct `fit_eeg_distribution` crash on a tied/flat
vector; `ASR().fit()` with a dead channel; and a guard that the result
on clean data is numerically identical (same `mu, sig, alpha, beta`).
Full `tests/test_asr.py` + `tests/test_cov.py` pass; `ruff` clean.

---
**Note:** includes the `nonlinear_eigenspace` reproducibility fix from
#118 so the riemann CI test (`test_asr_class[*-riemann]`) is stable
regardless of what this PR changes. Once #118 merges first, that commit
drops out on rebase.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants