Conversation
Remove shape-only and attribute-only tests in favour of the correctness and invariant tests already present. Reuse the shared `sample_4d_volume` fixture where a single recording suffices.
Contributor
There was a problem hiding this comment.
Pull request overview
Adds a new confusius.connectivity.CAP estimator implementing co-activation pattern (CAP) analysis via k-means clustering of fUSI volumes, including temporal dynamics metrics and cluster-count selection utilities.
Changes:
- Introduces
CAPclass with cosine/correlation (custom spherical k-means) and euclidean (sklearnKMeans) clustering backends. - Adds temporal metrics computation (fraction, counts, persistence, transitions) and
select_n_clusters()helper with multiple criteria. - Adds unit tests for
fit(),predict(), and temporal metrics; exportsCAPfromconfusius.connectivity.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 6 comments.
| File | Description |
|---|---|
| src/confusius/connectivity/cap.py | New CAP estimator, clustering routines, temporal metrics, and cluster selection helper. |
| src/confusius/connectivity/init.py | Exposes CAP in the connectivity public API. |
| tests/unit/test_connectivity/test_cap.py | Adds unit tests covering CAP fitting, prediction, and metrics. |
| docs/includes/abbreviations.md | Adds CAP/CAPs/SSQ abbreviations for documentation. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
- Fix empty-cluster zero-vector bug: mask empty centers to -inf before argmax so they cannot beat valid centers with negative similarity - Store _spatial_dims during fit; transpose recordings in predict to guarantee feature alignment regardless of input dim order - Fix docstring: caps_.attrs["long_name"] is "CAP" not "Co-activation patterns" - Rename frames → volumes throughout (function, units string, docstrings, variable name, test assertions and comments)
…or tests Add tests for update_rule='mean', metric='cosine', metric='euclidean' predict, NaN input, invalid update_rule, and all select_n_clusters methods and error conditions.
Two changes: 1. fit() and select_n_clusters(): replace xr.concat() with np.concatenate() on stacked numpy arrays, then preprocess in-place. This eliminates one full-data copy for correlation/cosine metrics (~2 GB savings at typical scale of 50 recordings × 300 volumes × 32k voxels). 2. predict() euclidean path: replace (n_samples × n_caps × n_features) 3D broadcast with the identity ||x-c||² = ||x||² + ||c||² - 2x·c, reducing peak allocation from ~236 MB to ~7 KB per recording.
Add 11 tests targeting previously uncovered paths: - empty cluster warning (also exercises coincident-center init branch, empty-mask branch in Lloyd loop, and empty-cluster cleanup) - mean update rule in the cosine k-means loop - cosine predict path (_preprocess in_place=False) - NaN input in predict() - integer n_init (multiple restarts) and invalid n_init - select_n_clusters(): euclidean KMeans path, invalid metric/update_rule, empty recordings list, NaN input Remaining 3% is unreachable code (RuntimeError guard, dead _find_elbow single-value branch, external Rich Progress integration).
- _run_multi_cosine_kmeans: replace None-sentinel initialization with direct assignment from the first seed; remove the unreachable RuntimeError guard that required best_centers to be None after at least one k-means run. - _find_elbow: remove n==1 early-return; select_n_clusters validates len(cluster_range) >= 2 before calling this function.
Add test_best_restart_selected: with n_clusters=10 and random_state=0, restart 1 produces lower inertia than restart 0, so n_init=2 gives different labels than n_init=1. The test would fail if the best-restart update assignment were dropped. Data is constructed locally with a fixed seed to be independent of the session-scoped rng fixture's advancing state.
- Add `scores_` attribute (list of DataArrays parallel to `labels_`) computed by `fit()`: cosine similarity for correlation/cosine metrics, negative L2 distance for euclidean (higher = stronger assignment) - Add `score_samples()` method mirroring `predict()` for scoring new data - Add `score_threshold` parameter to `compute_temporal_metrics()`: volumes below threshold are censored (treated as -1) so they act as episode breaks and are excluded from all metric numerators while the total-volume denominator stays fixed, matching the motion-scrubbing convention - Change default `update_rule` from "weighted" to "mean", the theoretically correct update for spherical k-means; update docstring accordingly
Add Parameters, Returns, and Notes sections to _relative_luminance and _auto_fg_color; include WCAG 2.1 spec link in _relative_luminance. Expand _create_deterministic_time_series docstring with Returns section.
This reverts commit e5c19f9.
…r_confounds A Dask array chunked along time produced silently wrong (all near-zero) variance values, causing an unhelpful "all voxels have zero variance" error instead of a clear indication of the root cause. validate_time_series already checks for time chunking, so passing the signal through it is sufficient.
…unds noise_mask.values.flatten() used the mask's own dimension order, which silently misaligned with signals.stack(space=spatial_dims) when the mask had its spatial dims in a different order. validate_mask catches name and coordinate mismatches but not ordering differences, so wrong (background, zero-variance) voxels were selected. Fixed by stacking the mask with the same spatial_dims as the signals before extracting values.
601802d to
c92fe20
Compare
59f4b05 to
a0a999d
Compare
Contributor
|
📖 Doc preview: https://confusius.tools/pr-preview/pr-116/ |
* feat: `cybis_pereira_2026` dataset should accept datatype filter * docs(changelog): add datatypes filter entry for cybis_pereira_2026 * docs(changelog): fill in PR number 141
Co-authored-by: sdiebolt <3774850+sdiebolt@users.noreply.github.com>
Two separate commits to confusius-docs (git push + gh api PUT for versions.json) raced with deploy-docs on PR merge, causing the API call to fail with a stale SHA. Silently swallowed by 2>/dev/null. Combines both into one git commit using a local jq edit, and adds set -euo pipefail for visibility.
* fix(plotting): keep hover manager alive after plotter is GC'd matplotlib's CallbackRegistry holds bound-method callbacks as WeakMethod, so the hover manager was collected as soon as the returning VolumePlotter went out of scope (e.g. `atlas.annotation[80:81].fusi.plot.volume().show()`), silently disabling status-bar hover. Anchor active managers in a module-level set and discard them on matplotlib's close_event (and on clear()), mirroring the pattern used by stateful mpl helpers such as MultiCursor. Also rename _RegionHoverManager to _HoverManager. * docs: add changelog entry * fix: add docstrings to module-level constant * add regressiont test for the hover manager
…ume (#139) * feat(registration): return diagnostics from register_volume Adds a RegistrationDiagnostics dataclass that carries the per-iteration metric values, final metric value, iteration count, optimizer stop condition, and the metric name. register_volume always attaches a sitkIterationEvent observer (regardless of show_progress) and returns the diagnostics as a third element of the result tuple, so callers can plot convergence curves, detect runs that failed to converge, and compare runs without re-instrumenting the optimizer. Closes #138 * feat(registration): propagate diagnostics through volumewise and accessor register_volumewise now collects the per-frame RegistrationDiagnostics and exposes them in two ways: as a list under result.attrs["registration_diagnostics"], and as new final_metric_value and n_iterations columns on the motion_params DataFrame. The xarray .fusi.register.to_volume accessor's return annotation is also extended to reflect the new 3-tuple shape. * docs(registration): add register_volume two-acquisitions example Walks through aligning two power Doppler volumes from different sessions of the same animal with register_volume, using confusius's plot_volume overlay pattern for the before/after composites and the new RegistrationDiagnostics to plot the convergence curve. Adds two callouts: a warning about the sensitivity of the optimizer arguments and a tip pointing at show_progress=True for the live progress plot. * docs(changelog): document registration diagnostics return value Adds a Breaking changes section to the 0.3.0.dev0 entry for the new register_volume 3-tuple return signature, a matching Enhancements bullet for the RegistrationDiagnostics dataclass and volumewise propagation, and a Documentation entry linking the new example page. * docs(changelog): point registration diagnostics entries to PR #139 * ci(docs): invalidate Nunez-Elizalde cache for the new registration example The example fetches CR020 sessions 20191120/22 that are not part of the files the existing cache key hashed, so CI kept restoring the stale cache subset. Add the example path to the hashFiles list so the key changes whenever the example changes, forcing a re-fetch that pulls in the sessions it needs. * ci(docs): pre-fetch dataset files before the gallery build The gallery builds each example twice (light + dark) in separate kernels. When the first run triggers an actual OSF download, its rich-progress bar lands in the cell's stream outputs while the second run finds the files already cached and emits nothing. The light/dark zip in tools/gallery/render.py then fails with "argument 2 is shorter than argument 1". Pre-fetch the specific subject/session/task/acq subsets used by the gallery examples before invoking build_gallery.py so both runs see a warm cache and produce identical output counts. The call is idempotent on already-cached files, so it is a no-op on subsequent runs. * docs: quick touchups in the registration notebook Consistency with the first 101 notebook, fix spaces around em-dashes, etc. * fix(gallery): keep executing past cell errors so tracebacks render inline NotebookClient previously ran with the default allow_errors=False and the gallery re-raised CellExecutionError as a RuntimeError, aborting the entire docs build the moment one example hit a runtime error. allow_errors=True lets execution continue: failing cells emit a regular 'error' output that the renderer (tools/gallery/render.py) already inlines into the rendered Markdown — exactly what a normal Jupyter notebook would show after a failed run. A broken example now degrades to "renders with a visible traceback in the docs" instead of breaking CI for every other example. * fix(gallery): pair light/dark cell outputs without strict-zip crash Light and dark builds run in independent kernels, so non-deterministic stream messages (one-time warnings, OSF download progress bars, etc.) can occasionally leave the two output lists at different lengths. zip(..., strict=True) then raised "argument 2 is shorter than argument 1" and aborted the entire gallery, even when the rendered page would have been fine without the offending output. Switch to a plain zip() that drops the trailing extras of the longer side and emits a warning so the asymmetry stays visible during local and CI builds. Keep a structural sanity check on the cell-count level: mismatched cell counts after filtering internal cells still raise, since that always indicates the two executions diverged at the source- notebook level. * test(gallery): cover continue-past-cell-errors behaviour Replace the old test that asserted CellExecutionError was re-raised as a RuntimeError with one that pins the new contract: a failing cell emits an `error` output (later inlined by the renderer) and execution continues to the next cell instead of aborting the gallery build. * test(gallery): cover light/dark output asymmetry Add a regression test pinning the new renderer contract: when one of the two themed builds emits an extra stream output (e.g. a one-time warning or download bar), the renderer pairs what it can, drops the trailing extras, and emits a UserWarning instead of crashing. Also drop a defensive cell-count pre-check on the way in — the outer strict-zip already raises on that internal invariant, and per CLAUDE.md we shouldn't validate scenarios the rest of the pipeline already guarantees. * docs(gallery): no need to pre-fetch data anymore * docs: remove duplicated information from changelog entries * feat(registration): gate full register_volumewise diagnostics behind keep_diagnostics Each per-frame RegistrationDiagnostics carries the full per-iteration metric trace, which adds up to substantial extra memory on long recordings. Make the diagnostics list opt-in via a new keep_diagnostics flag (default False) on register_volumewise (and the .fusi.register.volumewise xarray accessor), so the heavy payload is only attached to attrs["registration_diagnostics"] when explicitly requested. The cheap per-frame summaries (final_metric_value, n_iterations, one value each) are still appended to motion_params unconditionally — useful for spotting frames that failed to converge and inexpensive enough to ship by default. * docs(examples): rewrite registration walkthrough for plot_composite Switch the two-acquisitions registration example from the per-channel plot_volume + add_volume overlay onto plot_composite, and move the underlying data from Nunez-Elizalde 2022 (CR020 fUSI mean) to a pair of Cybis Pereira 2026 rat75 angio scans recorded on consecutive days. The markdown is rewritten to match: red/blue + blended purple language becomes red/cyan + desaturated grey, the resample_like/identity-matrix preamble is gone, and the new section explains why moving.z is forced onto fixed.z before plotting (resampling otherwise lands outside the fixed slab and returns an empty image). show_progress, the wider convergence window and the larger learning rate are kept inline so the optimizer reaches a stable fit on this dataset. * ci(docs): centralize dataset prefetching in a single script Add tools/prefetch_doc_datasets.py listing every fetch_* call made by the userguide image generators and the gallery examples, with args mirroring each call site exactly. Wire it into .github/workflows/docs.yml to run once before the image-generation and gallery-build steps so both consume warm caches, and switch the Nunez-Elizalde and Cybis Pereira dataset cache keys to hash only the prefetch script - it is now the single source of truth for what the docs pipeline downloads. Update AGENTS.md so contributors adding a new image generator or example know to add the matching fetch call to the prefetch script rather than touching the cache key directly. * refactor(docs): fail the gallery build on light/dark output mismatch The renderer used to warn and drop trailing outputs when a code cell produced different numbers of outputs in its light and dark passes - useful while debugging, but it silently hid real divergence between the two rendered notebooks. Replace the warn-and-drop with a ValueError naming the offending cell and the most common cause (one-shot side effects like dataset downloads or first-import warnings). The dataset-prefetch step added in the previous commit means all known sources of legitimate divergence are now eliminated before the gallery runs. * docs(gallery): remove show_progress=True from register_volume * test(gallery): expect ValueError for light/dark output mismatch Mirrors the renderer change in d302cd5: the test used to assert the warn-and-drop behavior, so it failed on the strict version. Rename the test, switch pytest.warns to pytest.raises, and drop the markdown assertion since the renderer no longer produces output for mismatched cells. * actually use show_progress * fix(ci): move gallery cache out of .cache/ to survive zensical build The 'Build documentation' step runs 'zensical build --clean --strict', and zensical's --clean wipes the entire .cache/ directory before populating it with its own files. That destroyed .cache/gallery/ between the gallery build and the cache action's post-step, leaving the cache empty at save time and triggering 'Path Validation Error: Path(s) specified in the action for caching do(es) not exist, hence no cache is being saved.' Move the gallery cache to .gallery-cache/ at the repo root so it sits outside zensical's reach. Also update the mkdir step, the cache action's path, and the .gitignore entry. * docs: link example in changelog to .py and not ephemeral .md * docs(gallery): harmonize plots facecolor * docs(gallery): rename example * fix(registration): use data min as default fill for out-of-FOV voxels When resampling moves a volume onto a larger fixed grid, voxels outside the original FOV were filled with 0.0. For dB-scaled data this is the maximum intensity, producing a bright artifact in the composite overlay and the final registered output. - register_volume: add fill_value param (default: moving.min()) applied to both the final sitk.Resample and the progress plotter composite - RegistrationProgressPlotter: replace fill_value with resample_kwargs dict forwarding default_value, interpolation, and sitk_threads - add_composite / plot_composite: replace fill_value with resample_kwargs forwarded to resample_like; auto-injects default_value=data2.min() * docs(gallery): remove fig.show() calls from registration example * fix(docs): affine -> rigid in example notebook * docs(changelog): document fill_value fix for out-of-FOV voxels in dB data * docs: minor touchups in registration example * refactor(registration): move fill_value default to resample_like/resample_volume Filling out-of-FOV voxels with data.min() is a resampling concern, not a plotting one. Shift the default from the plotting layer into resample_volume and resample_like (default_value: float | None = None → moving.min()). Remove the redundant auto-injection from add_composite; update changelog to credit the fix at the right layer. * fix(xarray): thread fill_value and resample_kwargs through xarray accessors * chore: rename example notebook * chore: minor docstring update --------- Co-authored-by: Samuel Le Meur-Diebolt <samuel@diebolt.io>
Compute moving.min() only when fill_value is actually needed for resampling/progress composites to avoid unnecessary work. Raise ValueError with supported interpolation names in progress plotting instead of bubbling KeyError. Also fix docs references to the renamed registration example in changelog and dataset prefetch comments.
* feat(decomposition): add FastICA transformer Wrap sklearn FastICA with the same xarray-aware decomposition API as PCA so component analyses preserve fUSI metadata. Tighten PCA and FastICA tests around sklearn parity and validation behavior to keep the wrappers aligned. * test(decomposition): stabilize FastICA wrapper tests Use deterministic FastICA test configurations and synthetic data where convergence on tiny random fixtures was flaky across CI platforms. Keep the assertions focused on wrapper behavior instead of solver luck. * docs: add FastICA to decomposition guide Document FastICA alongside PCA on the decomposition user guide placeholder page so users can discover both available transformers while the full guide is still under construction. * docs: enforce optional/boolean/private-helper docstring conventions - Replace `type or None, default: None` with `type, optional` throughout. - Use "If not provided, ..." instead of "If \`None\`, ...". - Use "Whether to ..." for boolean parameters instead of "If True/False, ...". - Require full NumPy docstrings on private helper methods. - Document all three rules in AGENTS.md. * refactor(decomposition): extract shared base class for PCA and FastICA Add _BaseFUSIDecomposer with all shared xarray bookkeeping: fit_transform, transform, inverse_transform, _prepare_data, _reshape_component_matrix, _reshape_mean, _store_fit_metadata, _store_feature_names, _get_time_coord, and __sklearn_is_fitted__. PCA and FastICA now only contain __init__ and fit; FastICA also keeps _reshape_feature_component_matrix for its mixing_ attribute. Drop the forced float64 cast in _prepare_data and inverse_transform — sklearn handles type promotion internally, and PCA accepts float32. * docs(decomposition): add FastICA changelog entry and PCA gallery example - Add FastICA entry to 0.3.0.dev0 changelog with PR #118 link. - Add decomposition gallery section with a PCA example covering scree plot, component maps, time courses, and variance-threshold denoising using the Nunez-Elizalde 2022 dataset. * feat(docs): add gallery ordering, standardize step, and transparent figures - Rename example dirs with numeric prefixes (01_io, 02_decomposition) so the gallery builder resolves sections in the correct order; discover.py strips the prefix so built URLs stay clean (io/, decomposition/). - Add _gallery_dark_theme bool to the gallery builder theme-setup cell and make figure/savefig facecolor transparent ('none') across all themes, removing the need for per-notebook dark-theme detection. - Update confusius_xarray_101.py to use _gallery_dark_theme from the builder instead of inspecting rcParams. - Rewrite pca_single_recording.py: standardize input with cf.signal.standardize, replace matplotlib image panels with cf.plotting.plot_volume, add thumbnail tag to component-maps cell, show a single frame (not the time mean) in the reconstruction comparison. - Add Decomposition section to zensical.toml nav after Getting Started. * fix(docs): detect dark theme from axes.facecolor, not an injected variable _gallery_dark_theme would be undefined when notebooks are run in Binder or downloaded directly. Instead, read axes.facecolor, which the builder still sets to a solid theme colour (#ffffff / #111720) while figure.facecolor is kept transparent. In standalone usage the matplotlib default (#ffffff) means dark_theme = False, which is correct. * docs(examples): remove redundant figure.patch.set_alpha(0) calls The gallery builder now sets figure.facecolor to 'none' globally, so explicit patch alpha resets are no longer needed. * feat(docs): enable glightbox on gallery output images Removed the skip-lightbox class from _image_tag so glightbox's auto mode picks up rendered cell outputs. The auto_themed setting in zensical.toml handles the #only-light / #only-dark dual images correctly. * fix(plotting): respect transparent rcParam in VolumePlotter; single-row comparison VolumePlotter._ensure_figure no longer overrides figure.patch.set_facecolor when rcParams['figure.facecolor'] is 'none', so the gallery builder's transparent background setting is respected without per-cell patch calls. PCA example: replace three separate plot_volume figures with a single pre-made 1x3 subplot figure, eliminating the single-panel padding artefact and putting all comparison panels in one row. * revert(plotting): undo VolumePlotter facecolor change; restore patch.set_alpha(0) Reverts the confusius-side rcParam check. Gallery scripts explicitly call plotter.figure.patch.set_alpha(0) (or fig.patch.set_alpha(0) for pre-made figures) after each plot_volume call instead. * fix(docs): fix ordering of the cards display in the examples page * docs(examples): explain theme color setup and simplify transparency handling * docs: improve docstrings for PCA/FastICA * docs(examples): split PCA analysis from SVD denoising notebooks * feat(decomposition): add spatial ICA mode and rename components_ to maps_ Add `mode` parameter to FastICA ("spatial"/"temporal", default "spatial"). Spatial mode fits on the transposed (voxels, time) matrix and finds spatially independent maps, matching FSL MELODIC's default behaviour and giving a better-determined problem for fUSI data where voxels >> time points. Rename `components_` to `maps_` across PCA and FastICA to reflect that the attribute stores spatial maps (loadings for PCA, IC maps or unmixing directions for FastICA), not the independent components themselves (which are returned by transform()). Update tests, docs, and user guide accordingly. Remove exploratory SVD denoising and Marchenko-Pastur heuristic example scripts that are still under investigation. * refactor(decomposition): drop mixing_ attribute and _reshape_feature_component_matrix from FastICA mixing_ was redundant in spatial mode (just maps_.T reshaped) and unused by any internal logic in both modes. The base class transform/inverse_transform delegate entirely to _estimator, so no caller needed it. * docs(examples): overhaul PCA example and add it to nav Rewrite the PCA example with richer explanations of the decomposition orientation (temporal PCA, voxels as features), orthogonality of maps, and uncorrelated time courses. Replace the separate time-course plot with a combined 6-row layout (spatial map + time course side by side) using plot_volume on per-row axes with symmetric color limits. Add the FastICA example to the zensical.toml nav. * docs(examples): add FastICA example and polish both decomposition notebooks Add the FastICA example with expanded prose that compares ICA to PCA, links back to the PCA notebook, and mirrors its section structure. Apply symmetric color limits (vmin=-vmax) to the 12-component overview grids in both notebooks. Fix broken cross-references: instance attributes and constructor parameters now link to the class, and the invalid fit(param) syntax is removed. * docs(changelog): add fontsize plotting API entry for #128 * docs(changelog): add maintenance section with GitHub Pages migration (#134) * test(decomposition): parametrize FastICA tests over both modes and replace shape checks with reference comparisons - Drop test_fit_transform_returns_dataarray from both test_fastica and test_pca — shape/dim assertions are covered implicitly by the reference implementation tests. - Parametrize test_wrapper_matches_sklearn_attributes and test_inverse_transform_matches_sklearn over ["spatial", "temporal"]. The spatial branch reconstructs the reference manually: fit sklearn on X.T, extract spatial maps via sklearn.transform(X.T).T, compute time courses as (X - mean) @ maps.T, and verify transform output, maps_, mean_, whitening_ absence, and n_iter_ against that reference. The inverse_transform branch verifies time_courses @ spatial_maps + mean. - Decomposition module coverage remains at 100%. * refactor: prefix example names by the order it will appear in the zensical sidebar menu * feat(decomposition): add temporal mode to PCA and FastICA Both PCA and FastICA now support mode="temporal" (fit on (time, voxels)) alongside the existing mode="spatial". The shared spatial projection logic (previously a _SpatialFastICAProxy shim in FastICA) is lifted into _BaseFUSIDecomposer as _spatial_transform / _spatial_inverse_transform, used by both classes. FastICA default hyperparameters are updated to align with FSL MELODIC: fun="cube" (pow3) and max_iter=500. Example scripts are expanded to demonstrate both modes side by side. * docs: adapt gallery plot colors to docs style * docs: restore axes facecolor in examples * fix(docs): rename examples for ordering * Update docs/examples/03_decomposition/02_fastica_single_recording.py Co-authored-by: Felipe Cybis Pereira <41338087+FelipeCybis@users.noreply.github.com> * Update docs/examples/02_registration/01_register_volume_same_subject.py Co-authored-by: Felipe Cybis Pereira <41338087+FelipeCybis@users.noreply.github.com> * Update docs/examples/03_decomposition/02_fastica_single_recording.py Co-authored-by: Felipe Cybis Pereira <41338087+FelipeCybis@users.noreply.github.com> * Update src/confusius/decomposition/fastica.py Co-authored-by: Felipe Cybis Pereira <41338087+FelipeCybis@users.noreply.github.com> * Update src/confusius/decomposition/fastica.py Co-authored-by: Felipe Cybis Pereira <41338087+FelipeCybis@users.noreply.github.com> * Update docs/examples/03_decomposition/01_pca_single_recording.py Co-authored-by: Felipe Cybis Pereira <41338087+FelipeCybis@users.noreply.github.com> * Update src/confusius/decomposition/fastica.py Co-authored-by: Felipe Cybis Pereira <41338087+FelipeCybis@users.noreply.github.com> * Update docs/examples/03_decomposition/02_fastica_single_recording.py Co-authored-by: Felipe Cybis Pereira <41338087+FelipeCybis@users.noreply.github.com> * Update docs/examples/03_decomposition/02_fastica_single_recording.py Co-authored-by: Felipe Cybis Pereira <41338087+FelipeCybis@users.noreply.github.com> --------- Co-authored-by: Felipe Cybis Pereira <felipe.cybispereira@gmail.com> Co-authored-by: Felipe Cybis Pereira <41338087+FelipeCybis@users.noreply.github.com>
* fix(docs): make gallery cache branch-agnostic Remove binder ref from gallery execution cache fingerprint so expensive gallery builds are reused across branches. Rewrite the Binder button URL on cached markdown during build so links still target the current branch/ref. * feat(docs): cache generated docs images in CI Add a dedicated Actions cache for docs/images outputs and skip image generation on cache hits to speed up docs builds.
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 4 to 8. - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](actions/download-artifact@v4...v8) --- updated-dependencies: - dependency-name: actions/download-artifact dependency-version: '8' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Samuel Le Meur-Diebolt <samuel@diebolt.io>
…158) Bumps [marocchino/sticky-pull-request-comment](https://github.com/marocchino/sticky-pull-request-comment) from 2 to 3. - [Release notes](https://github.com/marocchino/sticky-pull-request-comment/releases) - [Commits](marocchino/sticky-pull-request-comment@v2...v3) --- updated-dependencies: - dependency-name: marocchino/sticky-pull-request-comment dependency-version: '3' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Samuel Le Meur-Diebolt <samuel@diebolt.io>
* test(xarray): add wrapper forwarding tests * test(xarray): cover remaining accessor branches * test(xarray): split connectivity wrapper forwarding cases
Co-authored-by: sdiebolt <3774850+sdiebolt@users.noreply.github.com>
* feat(datasets): add Huang 2025 template fetcher * docs(changelog): add PR link for Huang template * docs(citing): harmonize citation style and licenses * cite peer-reviewed verion for Cybis Pereira 2026 --------- Co-authored-by: Felipe Cybis Pereira <felipe.cybispereira@gmail.com>
* feat(validation): add generic dataarray validators Introduce validate_fusi_dataarray for shared structural validation, refactor IQ validation to build on it, and rename validate_iq to validate_iq_dataarray. * test(iq): fix stale error message patterns and non-monotonic time fixtures Update three match strings to reflect new validate_fusi_dataarray messages, and fix two time coordinate arrays that were accidentally non-monotonic (duplicate/decreasing values), causing the general validator to fire before the Butterworth-specific regularity check. * fix(validation): import spacing helper from coordinates * refactor(validation): reuse fusi validator in registration * refactor(registration): enforce strict fusi validation * test(validation): clarify coord policy and enforce invariants * refactor(validation): require numeric core coordinates * docs(changelog): note validation API breaking changes * refactor(validation): scope regular-spacing checks Add regular_spacing_dims selector to validate_fusi_dataarray with spatial default. Update motion to enforce spatial-only regular spacing. Integrate validation in smooth_volume while preserving missing-coordinate behavior. Standardize shared test sample DataArrays with spatial voxdim metadata and migrate validation tests to shared 3D+t fixture with explicit 2D+t coverage. * refactor(validation): refine regular-spacing dim modes Support regular_spacing_dims values spatial/core/all/explicit sequence. Spatial remains default. Core checks canonical dims; all and explicit dims skip non-numeric coordinates. Update validation tests to use shared 3D+t fixture patterns and cover new mode semantics. * test(spatial): cover smooth validation re-raise path Add regression test for smooth_volume branch that re-raises non-missing-coordinate validation errors from validate_fusi_dataarray. * feat(validation): simplify regular-spacing dim selection * test(xarray): use 3dt fixtures in wrapper tests * refactor: put dimension coordinate checks inside `_validate_dimension_coordinate` * Explicify missing coordinates for dims when required spacing * test(validation): cover single-dim regular spacing selector --------- Co-authored-by: Felipe Cybis Pereira <felipe.cybispereira@gmail.com>
…el GLM (#155) * feat(connectivity): optimize masked SeedBasedMaps path * test(decomposition): cover masked reconstruction paths * refactor(decomposition): use unmask for masked reconstruction * test(decomposition): tolerate float drift in PCA noise variance * test(connectivity): cover masked SeedBasedMaps branches * refactor(glm): unify masked first-level handling * fix(masking): align masked dimension ordering * refactor(validation): add exact mask dim option Add to and use it in GLM and decomposition to remove duplicated full-spatial-dim checks. * fix(tests): fix assertion matching * refactor(decomposition): apply mask via extract_with_mask Replace manual stack/reindex/ravel masking in _prepare_data with the extract_with_mask helper, so the unmask round-trip uses its matching counterpart. require_exact_dims on validate_mask already guarantees mask/data dim order, making the explicit coordinate alignment redundant. Drop the X_stacked plumbing through _store_fit_metadata and remove the write-only _feature_coord_, _full_feature_coord_, and _feature_mask_ attributes; reconstruction relies solely on _reconstruction_mask_. * docs(changelog): add mask argument entry for #155 --------- Co-authored-by: Felipe Cybis Pereira <felipe.cybispereira@gmail.com>
* chore: prepare v0.3.0 release * chore: misc formatting
Bumps [actions/github-script](https://github.com/actions/github-script) from 8 to 9. - [Release notes](https://github.com/actions/github-script/releases) - [Commits](actions/github-script@v8...v9) --- updated-dependencies: - dependency-name: actions/github-script dependency-version: '9' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: sdiebolt <3774850+sdiebolt@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #114.
Summary
CAPclass implementing co-activation pattern analysis via k-means clustering of fUSI volumes"correlation"(Pearson, default),"cosine", and"euclidean"KMeansfor Euclideanfit()accepts a list of recordings (or a singleDataArray); labels are stored per-recording so recording boundaries are respected for temporal metricspredict()assigns new recordings to fitted CAPscompute_temporal_metrics()returns temporal fraction, episode counts, persistence (time-coord-aware, handles irregular sampling), transition frequency, and transition probability matrixselect_n_clusters()helper with elbow, silhouette, Davies-Bouldin, and variance-ratio criteria_prepare_datawith a clear error pointing to background-voxel z-scoring as the likely cause