mvpure-py 2.3.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mvpure_py-2.3.2/.github/workflows/pypi_release.yml +66 -0
- mvpure_py-2.3.2/.gitignore +10 -0
- mvpure_py-2.3.2/LICENSE +28 -0
- mvpure_py-2.3.2/PKG-INFO +143 -0
- mvpure_py-2.3.2/README.md +111 -0
- mvpure_py-2.3.2/mvpure_py/__init__.py +3 -0
- mvpure_py-2.3.2/mvpure_py/_version.py +24 -0
- mvpure_py-2.3.2/mvpure_py/localizer/__init__.py +21 -0
- mvpure_py-2.3.2/mvpure_py/localizer/_utils.py +178 -0
- mvpure_py-2.3.2/mvpure_py/localizer/localizer_utils.py +345 -0
- mvpure_py-2.3.2/mvpure_py/localizer/mvpure_localizer.py +783 -0
- mvpure_py-2.3.2/mvpure_py/simulation/__init__.py +16 -0
- mvpure_py-2.3.2/mvpure_py/simulation/simulate.py +722 -0
- mvpure_py-2.3.2/mvpure_py/simulation/validate.py +487 -0
- mvpure_py-2.3.2/mvpure_py/tests/__init__.py +0 -0
- mvpure_py-2.3.2/mvpure_py/tests/test_algebra.py +65 -0
- mvpure_py-2.3.2/mvpure_py/tests/test_forward_subset.py +229 -0
- mvpure_py-2.3.2/mvpure_py/tests/test_helper.py +82 -0
- mvpure_py-2.3.2/mvpure_py/tests/test_translation.py +155 -0
- mvpure_py-2.3.2/mvpure_py/tests/test_utils.py +53 -0
- mvpure_py-2.3.2/mvpure_py/utils/__init__.py +18 -0
- mvpure_py-2.3.2/mvpure_py/utils/_helper.py +36 -0
- mvpure_py-2.3.2/mvpure_py/utils/_utils.py +68 -0
- mvpure_py-2.3.2/mvpure_py/utils/algebra.py +14 -0
- mvpure_py-2.3.2/mvpure_py/utils/translation.py +503 -0
- mvpure_py-2.3.2/mvpure_py/viz/__init__.py +19 -0
- mvpure_py-2.3.2/mvpure_py/viz/group_viz.py +201 -0
- mvpure_py-2.3.2/mvpure_py/viz/viz.py +219 -0
- mvpure_py-2.3.2/mvpure_py.egg-info/PKG-INFO +143 -0
- mvpure_py-2.3.2/mvpure_py.egg-info/SOURCES.txt +40 -0
- mvpure_py-2.3.2/mvpure_py.egg-info/dependency_links.txt +1 -0
- mvpure_py-2.3.2/mvpure_py.egg-info/requires.txt +14 -0
- mvpure_py-2.3.2/mvpure_py.egg-info/scm_file_list.json +36 -0
- mvpure_py-2.3.2/mvpure_py.egg-info/scm_version.json +8 -0
- mvpure_py-2.3.2/mvpure_py.egg-info/top_level.txt +1 -0
- mvpure_py-2.3.2/pyproject.toml +55 -0
- mvpure_py-2.3.2/setup.cfg +4 -0
- mvpure_py-2.3.2/tutorials/Preprocessing data from oddball paradigm.ipynb +909 -0
- mvpure_py-2.3.2/tutorials/Simulating and Localizing Multi-Source EEG Activity with MVPURE_py.ipynb +1094 -0
- mvpure_py-2.3.2/tutorials/Simulating and Localizing Multi-Source EEG Activity /342/200/223 Multiple Runs.ipynb" +2425 -0
- mvpure_py-2.3.2/tutorials/Source localization and reconstruction on data from oddball paradigm.ipynb +1696 -0
- mvpure_py-2.3.2/tutorials/preprocessing_utils.py +298 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
|
|
2
|
+
name: Publish to PyPI
|
|
3
|
+
|
|
4
|
+
on:
|
|
5
|
+
release:
|
|
6
|
+
types: [published]
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
build:
|
|
13
|
+
name: Build package
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
|
|
16
|
+
steps:
|
|
17
|
+
- name: Check out repository
|
|
18
|
+
uses: actions/checkout@v6
|
|
19
|
+
with:
|
|
20
|
+
fetch-depth: 0
|
|
21
|
+
persist-credentials: false
|
|
22
|
+
|
|
23
|
+
- name: Set up Python
|
|
24
|
+
uses: actions/setup-python@v6
|
|
25
|
+
with:
|
|
26
|
+
python-version: "3.12"
|
|
27
|
+
|
|
28
|
+
- name: Install build tools
|
|
29
|
+
run: python -m pip install --upgrade build
|
|
30
|
+
|
|
31
|
+
- name: Build distributions
|
|
32
|
+
run: python -m build
|
|
33
|
+
|
|
34
|
+
- name: Check distributions
|
|
35
|
+
run: |
|
|
36
|
+
python -m pip install --upgrade twine
|
|
37
|
+
python -m twine check dist/*
|
|
38
|
+
|
|
39
|
+
- name: Upload build artifacts
|
|
40
|
+
uses: actions/upload-artifact@v5
|
|
41
|
+
with:
|
|
42
|
+
name: python-package-distributions
|
|
43
|
+
path: dist/
|
|
44
|
+
if-no-files-found: error
|
|
45
|
+
|
|
46
|
+
publish:
|
|
47
|
+
name: Publish to PyPI
|
|
48
|
+
needs: build
|
|
49
|
+
runs-on: ubuntu-latest
|
|
50
|
+
|
|
51
|
+
environment:
|
|
52
|
+
name: pypi
|
|
53
|
+
url: https://pypi.org/project/mvpure-py/
|
|
54
|
+
|
|
55
|
+
permissions:
|
|
56
|
+
id-token: write
|
|
57
|
+
|
|
58
|
+
steps:
|
|
59
|
+
- name: Download build artifacts
|
|
60
|
+
uses: actions/download-artifact@v6
|
|
61
|
+
with:
|
|
62
|
+
name: python-package-distributions
|
|
63
|
+
path: dist/
|
|
64
|
+
|
|
65
|
+
- name: Publish distributions
|
|
66
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
mvpure_py-2.3.2/LICENSE
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026, Julia Jurkowska
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
|
7
|
+
|
|
8
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
9
|
+
list of conditions and the following disclaimer.
|
|
10
|
+
|
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
and/or other materials provided with the distribution.
|
|
14
|
+
|
|
15
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
16
|
+
contributors may be used to endorse or promote products derived from
|
|
17
|
+
this software without specific prior written permission.
|
|
18
|
+
|
|
19
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
20
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
21
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
22
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
23
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
24
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
25
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
26
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
27
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
28
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
mvpure_py-2.3.2/PKG-INFO
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mvpure-py
|
|
3
|
+
Version: 2.3.2
|
|
4
|
+
Summary: EEG/MEG multi-source spatial filters and source localization tools
|
|
5
|
+
Author: Julia Jurkowska
|
|
6
|
+
License-Expression: BSD-3-Clause
|
|
7
|
+
Project-URL: Homepage, https://github.com/julia-jurkowska/mvpure-tools
|
|
8
|
+
Project-URL: Repository, https://github.com/julia-jurkowska/mvpure-tools
|
|
9
|
+
Project-URL: Documentation, https://julia-jurkowska.github.io/mvpure-tools/
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: Intended Audience :: Science/Research
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Topic :: Scientific/Engineering
|
|
14
|
+
Classifier: Topic :: Software Development
|
|
15
|
+
Requires-Python: >=3.13
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
Requires-Dist: joblib==1.5.1
|
|
19
|
+
Requires-Dist: matplotlib==3.11.2
|
|
20
|
+
Requires-Dist: mne==1.13.2
|
|
21
|
+
Requires-Dist: numpy==2.5.4
|
|
22
|
+
Requires-Dist: pandas==3.1.0
|
|
23
|
+
Requires-Dist: scikit-learn==1.9.1
|
|
24
|
+
Requires-Dist: scipy==1.18.1
|
|
25
|
+
Requires-Dist: termcolor==3.3.0
|
|
26
|
+
Requires-Dist: tqdm==4.67.1
|
|
27
|
+
Requires-Dist: nibabel==5.4.2
|
|
28
|
+
Requires-Dist: PyQt5==5.15.11
|
|
29
|
+
Provides-Extra: test
|
|
30
|
+
Requires-Dist: pytest==9.1.1; extra == "test"
|
|
31
|
+
Dynamic: license-file
|
|
32
|
+
|
|
33
|
+
# MVPURE-PY package
|
|
34
|
+
<div style="text-align: center;">
|
|
35
|
+
<img width="256" height="256" alt="mvpure_py logo" src="https://github.com/user-attachments/assets/ee880cfc-bb19-486b-be65-8c59f61c9e87" />
|
|
36
|
+
</div>
|
|
37
|
+
|
|
38
|
+
EEG/MEG Multi-Source Localizers with **mvpure-py**
|
|
39
|
+
Extends **MNE-Python** with multi-source neural activity indices and spatial filters.
|
|
40
|
+
Based on *“Multi-Source Neural Activity Indices for EEG/MEG Localization: A Two-Stage Spatial Filtering Framework and Extension to MNE-Python”*.
|
|
41
|
+
|
|
42
|
+
**Repository:** [GitHub](https://github.com/julia-jurkowska/mvpure-tools) \
|
|
43
|
+
**Tutorial & online docs:** [mvpure_py documentation](https://julia-jurkowska.github.io/mvpure-tools)
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Table of Contents
|
|
48
|
+
|
|
49
|
+
1. [Overview](#overview)
|
|
50
|
+
2. [Features](#features)
|
|
51
|
+
3. [Dependencies](#dependencies)
|
|
52
|
+
4. [Installation](#installation)
|
|
53
|
+
- From source
|
|
54
|
+
5. [Tutorial & Examples](#tutorial--examples)
|
|
55
|
+
6. [Citation](#citation)
|
|
56
|
+
---
|
|
57
|
+
## Overview
|
|
58
|
+
|
|
59
|
+
The **mvpure-tools** package implements multi-source neural activity indices for EEG / MEG source localization, extending MNE-Python’s API. It enables improved reconstruction when sources may be correlated, overcoming limitations of single-source beamforming methods (e.g. LCMV) in certain settings.
|
|
60
|
+
|
|
61
|
+
Key contributions include:
|
|
62
|
+
|
|
63
|
+
- Algebraic, compact forms for multi-source neural activity indices
|
|
64
|
+
- Seamless integration into MNE-Python workflows (covariance, forward/inverse handling, visualization)
|
|
65
|
+
- A step-by-step tutorial using real EEG data (oddball paradigm, synthetic data)
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## Features
|
|
70
|
+
|
|
71
|
+
- Neural activity indices (MAI_MVP) in the multi-source setting
|
|
72
|
+
- Iterative localization algorithms compatible with large candidate source sets
|
|
73
|
+
- Built on top of MNE-Python, so you can combine it with standard EEG / MEG toolchains
|
|
74
|
+
- Tutorial and example scripts included
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
## Dependencies
|
|
78
|
+
|
|
79
|
+
Before installing, make sure you have:
|
|
80
|
+
|
|
81
|
+
- Python
|
|
82
|
+
- **MNE-Python**
|
|
83
|
+
- NumPy, SciPy
|
|
84
|
+
- (Other dependencies listed in `requirements.txt`)
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## Installation
|
|
89
|
+
### From Source (recommended)
|
|
90
|
+
To install from GitHub:
|
|
91
|
+
|
|
92
|
+
1. Clone the repository
|
|
93
|
+
```bash
|
|
94
|
+
git clone https://github.com/julia-jurkowska/mvpure-tools.git
|
|
95
|
+
cd mvpure-tools
|
|
96
|
+
```
|
|
97
|
+
2. (Optional) create a virtual environment
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
python3 -m venv venv
|
|
101
|
+
source venv/bin/activate
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
3. Install dependencies
|
|
105
|
+
```bash
|
|
106
|
+
pip install -r requirements.txt
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
4. Install the package
|
|
110
|
+
```bash
|
|
111
|
+
pip install .
|
|
112
|
+
```
|
|
113
|
+
or for development mode:
|
|
114
|
+
```bash
|
|
115
|
+
pip install -e .
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
5. Verify installation:
|
|
119
|
+
|
|
120
|
+
```python
|
|
121
|
+
import mvpure_py
|
|
122
|
+
from mvpure_py import localizer
|
|
123
|
+
```
|
|
124
|
+
---
|
|
125
|
+
## Tutorial & Examples
|
|
126
|
+
See tutorials on documentation site: [documentation](https://julia-jurkowska.github.io/mvpure-tools).
|
|
127
|
+
|
|
128
|
+
The tutorials walk you through:
|
|
129
|
+
|
|
130
|
+
1. Preprocessing (filtering, artifact rejection, ICA, epoching)
|
|
131
|
+
2. Estimation of number of sources and rank
|
|
132
|
+
3. Localization of sources using neural activity indices
|
|
133
|
+
4. Reconstruction of time courses
|
|
134
|
+
5. Visualization of estimated sources
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## Citation
|
|
138
|
+
|
|
139
|
+
If you use **mvpure_py** in your work, please cite:
|
|
140
|
+
|
|
141
|
+
> Jurkowska, J., Dreszer, J., Lewandowska, M., Tolpa, K., Piotrowski, T.,
|
|
142
|
+
> *Multi-source neural activity indices for EEG/MEG localization: A two-stage spatial filtering framework and extension to MNE-Python*, NeuroImage, Volume 339, 2026, 122126, <https://doi.org/10.1016/j.neuroimage.2026.122126>.
|
|
143
|
+
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# MVPURE-PY package
|
|
2
|
+
<div style="text-align: center;">
|
|
3
|
+
<img width="256" height="256" alt="mvpure_py logo" src="https://github.com/user-attachments/assets/ee880cfc-bb19-486b-be65-8c59f61c9e87" />
|
|
4
|
+
</div>
|
|
5
|
+
|
|
6
|
+
EEG/MEG Multi-Source Localizers with **mvpure-py**
|
|
7
|
+
Extends **MNE-Python** with multi-source neural activity indices and spatial filters.
|
|
8
|
+
Based on *“Multi-Source Neural Activity Indices for EEG/MEG Localization: A Two-Stage Spatial Filtering Framework and Extension to MNE-Python”*.
|
|
9
|
+
|
|
10
|
+
**Repository:** [GitHub](https://github.com/julia-jurkowska/mvpure-tools) \
|
|
11
|
+
**Tutorial & online docs:** [mvpure_py documentation](https://julia-jurkowska.github.io/mvpure-tools)
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Table of Contents
|
|
16
|
+
|
|
17
|
+
1. [Overview](#overview)
|
|
18
|
+
2. [Features](#features)
|
|
19
|
+
3. [Dependencies](#dependencies)
|
|
20
|
+
4. [Installation](#installation)
|
|
21
|
+
- From source
|
|
22
|
+
5. [Tutorial & Examples](#tutorial--examples)
|
|
23
|
+
6. [Citation](#citation)
|
|
24
|
+
---
|
|
25
|
+
## Overview
|
|
26
|
+
|
|
27
|
+
The **mvpure-tools** package implements multi-source neural activity indices for EEG / MEG source localization, extending MNE-Python’s API. It enables improved reconstruction when sources may be correlated, overcoming limitations of single-source beamforming methods (e.g. LCMV) in certain settings.
|
|
28
|
+
|
|
29
|
+
Key contributions include:
|
|
30
|
+
|
|
31
|
+
- Algebraic, compact forms for multi-source neural activity indices
|
|
32
|
+
- Seamless integration into MNE-Python workflows (covariance, forward/inverse handling, visualization)
|
|
33
|
+
- A step-by-step tutorial using real EEG data (oddball paradigm, synthetic data)
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Features
|
|
38
|
+
|
|
39
|
+
- Neural activity indices (MAI_MVP) in the multi-source setting
|
|
40
|
+
- Iterative localization algorithms compatible with large candidate source sets
|
|
41
|
+
- Built on top of MNE-Python, so you can combine it with standard EEG / MEG toolchains
|
|
42
|
+
- Tutorial and example scripts included
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
## Dependencies
|
|
46
|
+
|
|
47
|
+
Before installing, make sure you have:
|
|
48
|
+
|
|
49
|
+
- Python
|
|
50
|
+
- **MNE-Python**
|
|
51
|
+
- NumPy, SciPy
|
|
52
|
+
- (Other dependencies listed in `requirements.txt`)
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## Installation
|
|
57
|
+
### From Source (recommended)
|
|
58
|
+
To install from GitHub:
|
|
59
|
+
|
|
60
|
+
1. Clone the repository
|
|
61
|
+
```bash
|
|
62
|
+
git clone https://github.com/julia-jurkowska/mvpure-tools.git
|
|
63
|
+
cd mvpure-tools
|
|
64
|
+
```
|
|
65
|
+
2. (Optional) create a virtual environment
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
python3 -m venv venv
|
|
69
|
+
source venv/bin/activate
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
3. Install dependencies
|
|
73
|
+
```bash
|
|
74
|
+
pip install -r requirements.txt
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
4. Install the package
|
|
78
|
+
```bash
|
|
79
|
+
pip install .
|
|
80
|
+
```
|
|
81
|
+
or for development mode:
|
|
82
|
+
```bash
|
|
83
|
+
pip install -e .
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
5. Verify installation:
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
import mvpure_py
|
|
90
|
+
from mvpure_py import localizer
|
|
91
|
+
```
|
|
92
|
+
---
|
|
93
|
+
## Tutorial & Examples
|
|
94
|
+
See tutorials on documentation site: [documentation](https://julia-jurkowska.github.io/mvpure-tools).
|
|
95
|
+
|
|
96
|
+
The tutorials walk you through:
|
|
97
|
+
|
|
98
|
+
1. Preprocessing (filtering, artifact rejection, ICA, epoching)
|
|
99
|
+
2. Estimation of number of sources and rank
|
|
100
|
+
3. Localization of sources using neural activity indices
|
|
101
|
+
4. Reconstruction of time courses
|
|
102
|
+
5. Visualization of estimated sources
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## Citation
|
|
106
|
+
|
|
107
|
+
If you use **mvpure_py** in your work, please cite:
|
|
108
|
+
|
|
109
|
+
> Jurkowska, J., Dreszer, J., Lewandowska, M., Tolpa, K., Piotrowski, T.,
|
|
110
|
+
> *Multi-source neural activity indices for EEG/MEG localization: A two-stage spatial filtering framework and extension to MNE-Python*, NeuroImage, Volume 339, 2026, 122126, <https://doi.org/10.1016/j.neuroimage.2026.122126>.
|
|
111
|
+
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# file generated by vcs-versioning
|
|
2
|
+
# don't change, don't track in version control
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
__all__ = [
|
|
6
|
+
"__version__",
|
|
7
|
+
"__version_tuple__",
|
|
8
|
+
"version",
|
|
9
|
+
"version_tuple",
|
|
10
|
+
"__commit_id__",
|
|
11
|
+
"commit_id",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
version: str
|
|
15
|
+
__version__: str
|
|
16
|
+
__version_tuple__: tuple[int | str, ...]
|
|
17
|
+
version_tuple: tuple[int | str, ...]
|
|
18
|
+
commit_id: str | None
|
|
19
|
+
__commit_id__: str | None
|
|
20
|
+
|
|
21
|
+
__version__ = version = '2.3.2'
|
|
22
|
+
__version_tuple__ = version_tuple = (2, 3, 2)
|
|
23
|
+
|
|
24
|
+
__commit_id__ = commit_id = 'g8b490f09b'
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""
|
|
2
|
+
References
|
|
3
|
+
----------
|
|
4
|
+
.. [1] Jurkowska, J., Dreszer, J., Lewandowska, M., Tolpa, K., & Piotrowski, T. J. (2025).
|
|
5
|
+
Multi-Source Neural Activity Indices and Spatial Filters for EEG/MEG Inverse Problem: An Extension to MNE-Python. bioRxiv, 2025-09.
|
|
6
|
+
"""
|
|
7
|
+
from .mvpure_localizer import (
|
|
8
|
+
localize,
|
|
9
|
+
Localized,
|
|
10
|
+
read_localized
|
|
11
|
+
)
|
|
12
|
+
from .localizer_utils import (
|
|
13
|
+
get_activity_index,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
from ._utils import (
|
|
17
|
+
_check_localize_params,
|
|
18
|
+
_prepare_localize_params,
|
|
19
|
+
_check_vertices_and_indices,
|
|
20
|
+
_check_n_sources_param
|
|
21
|
+
)
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
from ..utils import _check_hemi_param
|
|
2
|
+
|
|
3
|
+
import mne
|
|
4
|
+
import numpy as np
|
|
5
|
+
from termcolor import colored
|
|
6
|
+
import logging
|
|
7
|
+
logging.basicConfig(
|
|
8
|
+
level=logging.WARNING,
|
|
9
|
+
format='[%(levelname)s] %(message)s'
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _check_localize_params(
|
|
14
|
+
forward: mne.Forward,
|
|
15
|
+
leadfield: np.array) -> None:
|
|
16
|
+
"""
|
|
17
|
+
Checking user input for mvpure_py.localizer.localize() function.
|
|
18
|
+
"""
|
|
19
|
+
if forward is None and leadfield is None:
|
|
20
|
+
raise ValueError("One of the parameters 'forward' and 'leadfield' should not be None")
|
|
21
|
+
|
|
22
|
+
if forward is not None and leadfield is not None:
|
|
23
|
+
if np.array_equal(forward['sol']['data'], leadfield):
|
|
24
|
+
raise ValueError("Both parameters 'forward' and 'leadfield' were specified. "
|
|
25
|
+
"They should be equal, but they are not.")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _prepare_localize_params(
|
|
29
|
+
forward: mne.Forward,
|
|
30
|
+
leadfield: np.array) -> np.array:
|
|
31
|
+
"""
|
|
32
|
+
Adjusting input parameters in mvpure_py.localizer.localize() function.
|
|
33
|
+
Handling forward/leadfield options.
|
|
34
|
+
"""
|
|
35
|
+
if forward is not None:
|
|
36
|
+
leadfield = forward["sol"]["data"]
|
|
37
|
+
|
|
38
|
+
return leadfield
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _check_n_sources_param(n_sources: int) -> None:
|
|
42
|
+
"""
|
|
43
|
+
Checks type of 'n_sources_to_localize' from mvpure_py.localizer.localize() function.
|
|
44
|
+
Should be an integer.
|
|
45
|
+
"""
|
|
46
|
+
if not isinstance(n_sources, (int, np.integer)):
|
|
47
|
+
raise TypeError(f"Parameter 'n_sources_to_localize' should be an integer. Got type: {type(n_sources)} instead.")
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _check_rank(
|
|
51
|
+
r: int | str | float | list,
|
|
52
|
+
n_sources_to_localize: int):
|
|
53
|
+
"""
|
|
54
|
+
Checking correctness of rank optimization ('r') parameter.
|
|
55
|
+
"""
|
|
56
|
+
if not isinstance(r, (int, np.integer, str, float, np.floating, list)):
|
|
57
|
+
raise ValueError(f"Optimization parameter 'r' (set to: {r}) should be of type: int, float, string or list. "
|
|
58
|
+
f"Got {type(r)} instead.")
|
|
59
|
+
|
|
60
|
+
# if 'r' is a list, it should contain integers / floats
|
|
61
|
+
# and all elements should be equal or less to n_sources_to_localize,
|
|
62
|
+
if isinstance(r, list):
|
|
63
|
+
if not all(
|
|
64
|
+
isinstance(x, (int, np.integer, float, np.floating)) and
|
|
65
|
+
n_sources_to_localize >= x >= 1
|
|
66
|
+
for x in r
|
|
67
|
+
):
|
|
68
|
+
raise ValueError("If `r` is a list, all elements should be int or float"
|
|
69
|
+
" and equal or less than number of sources to localize.")
|
|
70
|
+
|
|
71
|
+
# 'r' should be less or equal to number of sources to localize
|
|
72
|
+
if isinstance(r, (int, np.integer)) and r > n_sources_to_localize:
|
|
73
|
+
raise ValueError(f"Optimization parameter 'r' (set to {r}) should be equal or less than"
|
|
74
|
+
f" number of sources to localize ('n_sources_to_localize' set to {n_sources_to_localize}).")
|
|
75
|
+
|
|
76
|
+
# only option for 'r' as string is 'full'
|
|
77
|
+
if isinstance(r, str) and r not in ["full"]:
|
|
78
|
+
raise ValueError(f"If parameter 'r' is a string it should be set to 'full'. "
|
|
79
|
+
f"Got {r} instead.")
|
|
80
|
+
|
|
81
|
+
# if 'r' == 0, use full rank
|
|
82
|
+
if isinstance(r, (int, np.integer)) and r == 0:
|
|
83
|
+
return "full"
|
|
84
|
+
|
|
85
|
+
# if 'r' is smaller than zero, take n_sources_to_localize - abs(r)
|
|
86
|
+
if isinstance(r, (int, np.integer)) and r < 0 and (n_sources_to_localize - abs(r)) > 0:
|
|
87
|
+
adjusted_value = n_sources_to_localize - abs(r)
|
|
88
|
+
logging.warning(
|
|
89
|
+
f"Got rank lower than zero. Taking n_sources_to_localize - {abs(r)} = {adjusted_value} instead."
|
|
90
|
+
)
|
|
91
|
+
return adjusted_value
|
|
92
|
+
|
|
93
|
+
# if 'r' is smaller than zero and n_sources_to_localize - abs(r) == 0, use full rank
|
|
94
|
+
if isinstance(r, (int, np.integer)) and r < 0 and (n_sources_to_localize - abs(r)) == 0:
|
|
95
|
+
return "full"
|
|
96
|
+
|
|
97
|
+
# if 'r' is smaller than zero and n_sources_to_localize - abs(r) == 0, raise error
|
|
98
|
+
if isinstance(r, (int, np.integer)) and r < 0 and (n_sources_to_localize - abs(r)) < 0:
|
|
99
|
+
raise ValueError(f"Optimization parameter 'r' (set to {r}) should be equal or higher than 0.")
|
|
100
|
+
|
|
101
|
+
# if using float and float can not be converted to integer - raise error
|
|
102
|
+
if isinstance(r, (float, np.floating)) and int(r) != r:
|
|
103
|
+
raise TypeError(f"Parameter 'r' should be type an integer (if numeric). Got float instead.")
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _prepare_localizer_to_use(localizer_to_use: str | list[str]):
|
|
107
|
+
"""
|
|
108
|
+
Checking and adjusting 'localizer_to_use' parameter from mvpure_py.localizer.localize() function.
|
|
109
|
+
"""
|
|
110
|
+
# list of all localizers options
|
|
111
|
+
all_localizers = ['mai', 'mai_mvp']
|
|
112
|
+
# if localizer_to_use is a string
|
|
113
|
+
if isinstance(localizer_to_use, str):
|
|
114
|
+
# if localizer_to_use == 'all' use all localizers
|
|
115
|
+
if localizer_to_use == 'all':
|
|
116
|
+
param_localizer_to_use = all_localizers
|
|
117
|
+
# can be single name of localizers
|
|
118
|
+
elif localizer_to_use in all_localizers:
|
|
119
|
+
param_localizer_to_use = [localizer_to_use]
|
|
120
|
+
# otherwise raise ValueError
|
|
121
|
+
else:
|
|
122
|
+
raise ValueError(f"If 'localizer_to_use' is a string it should be either 'all' or "
|
|
123
|
+
f"name of one of the localizers to use. Possible options are: {all_localizers}. "
|
|
124
|
+
f"Got '{localizer_to_use}' instead.")
|
|
125
|
+
elif isinstance(localizer_to_use, list):
|
|
126
|
+
param_localizer_to_use = [loc for loc in localizer_to_use if loc in all_localizers]
|
|
127
|
+
if len(param_localizer_to_use) < len(localizer_to_use):
|
|
128
|
+
print(colored(
|
|
129
|
+
f"WARNING: One or more of localizers' names are not valid."
|
|
130
|
+
f" Possible options are: {all_localizers}, got {localizer_to_use}.",
|
|
131
|
+
"yellow"
|
|
132
|
+
))
|
|
133
|
+
if len(param_localizer_to_use) == 0:
|
|
134
|
+
raise ValueError(f"None of the localizers' name are valid."
|
|
135
|
+
f" Possible options are: {all_localizers}, got {localizer_to_use}")
|
|
136
|
+
else:
|
|
137
|
+
raise ValueError(f"'localizer_to_use' should be either list of valid localizers' names "
|
|
138
|
+
f"or string with single localizer name")
|
|
139
|
+
return param_localizer_to_use
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def _define_hemi_based_on_vertices(localized) -> str:
|
|
143
|
+
"""
|
|
144
|
+
Define hemi (`both`, `rh`, `lh`) based on vertices stored in `localized` object.
|
|
145
|
+
"""
|
|
146
|
+
if hasattr(localized['stc'], 'lh_vertno') and hasattr(localized['stc'], 'rh_vertno'):
|
|
147
|
+
hemi = "both"
|
|
148
|
+
elif hasattr(localized['stc'], 'lh_vertno'):
|
|
149
|
+
hemi = "lh"
|
|
150
|
+
elif hasattr(localized['stc'], 'rh_vertno'):
|
|
151
|
+
hemi = "rh"
|
|
152
|
+
|
|
153
|
+
return hemi
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def _check_norm_param(norm):
|
|
157
|
+
if norm not in ["max", "sum"]:
|
|
158
|
+
raise ValueError(f"'norm' can be equal to one of the following: "
|
|
159
|
+
f" 'max' (max normalization), 'sum' (L1 normalization. "
|
|
160
|
+
f"Got {norm} instead.")
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def _check_vertices_and_indices(lh_vertices: list, lh_indices: list, rh_vertices: list, rh_indices: list):
|
|
164
|
+
if (lh_indices is not None and lh_vertices is None) or (lh_indices is None and lh_vertices is not None):
|
|
165
|
+
raise ValueError(f"Both 'lh_vertices' and 'lh_indices' should be specified. "
|
|
166
|
+
f"Got lh_vertices={lh_vertices} and lh_indices={lh_indices}.")
|
|
167
|
+
|
|
168
|
+
if (rh_indices is not None and rh_vertices is None) or (rh_indices is None and rh_vertices is not None):
|
|
169
|
+
raise ValueError(f"Both 'rh_vertices' and 'rh_indices' should be specified. "
|
|
170
|
+
f"Got rh_vertices={rh_vertices} and rh_indices={rh_indices}.")
|
|
171
|
+
|
|
172
|
+
if lh_vertices is not None and lh_indices is not None and len(lh_vertices) != len(lh_indices):
|
|
173
|
+
raise ValueError(f"Can not add vertices to Localized object because length of vertices list ({len(lh_vertices)})"
|
|
174
|
+
f"does not match length of indices list ({len(lh_indices)}).")
|
|
175
|
+
|
|
176
|
+
if rh_vertices is not None and rh_indices is not None and len(rh_vertices) != len(rh_indices):
|
|
177
|
+
raise ValueError(f"Can not add vertices to Localized object because length of vertices list ({len(rh_vertices)})"
|
|
178
|
+
f"does not match length of indices list ({len(rh_indices)}).")
|