diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 5856ecc6609..c6a8ceee797 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -44,7 +44,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v3 + uses: github/codeql-action/init@v4 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -58,7 +58,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v3 + uses: github/codeql-action/autobuild@v4 # â„šī¸ Command-line programs to run using the OS shell. # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun @@ -71,4 +71,4 @@ jobs: # ./location_of_script_within_repo/buildscript.sh - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3 + uses: github/codeql-action/analyze@v4 diff --git a/doc/changes/dev/13445.newfeature.rst b/doc/changes/dev/13445.newfeature.rst new file mode 100644 index 00000000000..3b915207222 --- /dev/null +++ b/doc/changes/dev/13445.newfeature.rst @@ -0,0 +1 @@ +Improve precision of fiducial clicking in ``mne coreg``, by `Eric Larson`_. diff --git a/doc/install/installers.rst b/doc/install/installers.rst index 5a16cc2ca66..e82084cf393 100644 --- a/doc/install/installers.rst +++ b/doc/install/installers.rst @@ -17,7 +17,7 @@ Platform-specific installers :class-content: text-center :name: install-linux - .. button-link:: https://github.com/mne-tools/mne-installers/releases/download/v1.10.1/MNE-Python-1.10.1_0-Linux.sh + .. button-link:: https://github.com/mne-tools/mne-installers/releases/download/v1.10.2/MNE-Python-1.10.2_0-Linux.sh :ref-type: ref :color: primary :shadow: @@ -31,14 +31,14 @@ Platform-specific installers .. code-block:: console - $ sh ./MNE-Python-1.10.1_0-Linux.sh + $ sh ./MNE-Python-1.10.2_0-Linux.sh .. tab-item:: macOS (Intel) :class-content: text-center :name: install-macos-intel - .. button-link:: https://github.com/mne-tools/mne-installers/releases/download/v1.10.1/MNE-Python-1.10.1_0-macOS_Intel.pkg + .. button-link:: https://github.com/mne-tools/mne-installers/releases/download/v1.10.2/MNE-Python-1.10.2_0-macOS_Intel.pkg :ref-type: ref :color: primary :shadow: @@ -54,7 +54,7 @@ Platform-specific installers :class-content: text-center :name: install-macos-apple - .. button-link:: https://github.com/mne-tools/mne-installers/releases/download/v1.10.1/MNE-Python-1.10.1_0-macOS_M1.pkg + .. button-link:: https://github.com/mne-tools/mne-installers/releases/download/v1.10.2/MNE-Python-1.10.2_0-macOS_M1.pkg :ref-type: ref :color: primary :shadow: @@ -70,7 +70,7 @@ Platform-specific installers :class-content: text-center :name: install-windows - .. button-link:: https://github.com/mne-tools/mne-installers/releases/download/v1.10.1/MNE-Python-1.10.1_0-Windows.exe + .. button-link:: https://github.com/mne-tools/mne-installers/releases/download/v1.10.2/MNE-Python-1.10.2_0-Windows.exe :ref-type: ref :color: primary :shadow: @@ -156,7 +156,7 @@ To remove the MNE-Python distribution provided by our installers above: .. code-block:: bash $ which python - /home/username/mne-python/1.10.1_0/bin/python + /home/username/mne-python/1.10.2_0/bin/python $ rm -Rf /home/$USER/mne-python $ rm /home/$USER/.local/share/applications/mne-python-*.desktop @@ -170,7 +170,7 @@ To remove the MNE-Python distribution provided by our installers above: .. code-block:: bash $ which python - /Users/username/Applications/MNE-Python/1.10.1_0/.mne-python/bin/python + /Users/username/Applications/MNE-Python/1.10.2_0/.mne-python/bin/python $ rm -Rf /Users/$USER/Applications/MNE-Python # if user-specific $ rm -Rf /Applications/MNE-Python # if system-wide diff --git a/mne/beamformer/tests/test_dics.py b/mne/beamformer/tests/test_dics.py index 7d5800a597c..cebc0bb4057 100644 --- a/mne/beamformer/tests/test_dics.py +++ b/mne/beamformer/tests/test_dics.py @@ -705,6 +705,7 @@ def test_apply_dics_timeseries(_load_forward, idx): apply_dics_epochs(epochs, filters_vol) +@pytest.mark.slowtest @testing.requires_testing_data @pytest.mark.parametrize("return_generator", (True, False)) def test_apply_dics_tfr(return_generator): @@ -849,6 +850,7 @@ def test_localization_bias_free( assert lower <= perc <= upper +@pytest.mark.slowtest @pytest.mark.parametrize( "weight_norm, lower, upper, lower_ori, upper_ori, real_filter", [ diff --git a/mne/beamformer/tests/test_lcmv.py b/mne/beamformer/tests/test_lcmv.py index 9ae5473e190..35b9943d54e 100644 --- a/mne/beamformer/tests/test_lcmv.py +++ b/mne/beamformer/tests/test_lcmv.py @@ -809,6 +809,7 @@ def test_lcmv_reg_proj(proj, weight_norm): assert_allclose(stc_cov.data.std(), 0.187, rtol=0.2) +@pytest.mark.slowtest @pytest.mark.parametrize( "reg, weight_norm, use_cov, depth, lower, upper", [ @@ -851,6 +852,7 @@ def test_localization_bias_fixed( # Changes here should be synced with test_dics.py +@pytest.mark.slowtest @pytest.mark.parametrize( "reg, pick_ori, weight_norm, use_cov, depth, lower, upper, lower_ori, upper_ori", [ @@ -1044,7 +1046,7 @@ def test_orientation_max_power( "weight_norm, pick_ori", [ pytest.param("nai", "max-power", marks=pytest.mark.slowtest), - ("unit-noise-gain", "vector"), + pytest.param("unit-noise-gain", "vector", marks=pytest.mark.slowtest), ("unit-noise-gain", "max-power"), pytest.param("unit-noise-gain", None, marks=pytest.mark.slowtest), ], diff --git a/mne/gui/_coreg.py b/mne/gui/_coreg.py index 7480a50231d..bd2decc4a3f 100644 --- a/mne/gui/_coreg.py +++ b/mne/gui/_coreg.py @@ -918,19 +918,10 @@ def _on_pick(self, vtk_picker, event): if not any(mesh is target() for target in self._picking_targets): return pos = np.array(vtk_picker.GetPickPosition()) - vtk_cell = mesh.GetCell(cell_id) - cell = [ - vtk_cell.GetPointId(point_id) - for point_id in range(vtk_cell.GetNumberOfPoints()) - ] - vertices = mesh.points[cell] - idx = np.argmin(abs(vertices - pos), axis=0) - vertex_id = cell[idx[0]] - fiducials = [s.lower() for s in self._defaults["fiducials"]] idx = fiducials.index(self._current_fiducial.lower()) # XXX: add coreg.set_fids - self.coreg._fid_points[idx] = self._surfaces["head"].points[vertex_id] + self.coreg._fid_points[idx] = pos self.coreg._reset_fiducials() self._update_fiducials() self._update_plot("mri_fids") diff --git a/mne/gui/tests/test_coreg.py b/mne/gui/tests/test_coreg.py index 9c0db7164c3..4800bea29e1 100644 --- a/mne/gui/tests/test_coreg.py +++ b/mne/gui/tests/test_coreg.py @@ -45,7 +45,6 @@ class TstVTKPicker: def __init__(self, mesh, cell_id, event_pos): self.mesh = mesh self.cell_id = cell_id - self.point_id = None self.event_pos = event_pos def GetCellId(self): @@ -59,12 +58,7 @@ def GetDataSet(self): def GetPickPosition(self): """Return the picked position.""" vtk_cell = self.mesh.GetCell(self.cell_id) - cell = [ - vtk_cell.GetPointId(point_id) - for point_id in range(vtk_cell.GetNumberOfPoints()) - ] - self.point_id = cell[0] - return self.mesh.points[self.point_id] + return self.mesh.points[vtk_cell.GetPointId(0)] def GetEventPosition(self): """Return event position.""" diff --git a/mne/minimum_norm/tests/test_inverse.py b/mne/minimum_norm/tests/test_inverse.py index 88578b20023..d5eec6e7f91 100644 --- a/mne/minimum_norm/tests/test_inverse.py +++ b/mne/minimum_norm/tests/test_inverse.py @@ -244,6 +244,7 @@ def _compare_io(inv_op, *, out_file_ext=".fif", tmp_path): _compare(inv_init, inv_op) +@pytest.mark.slowtest def test_warn_inverse_operator(evoked, noise_cov): """Test MNE inverse warning without average EEG projection.""" bad_info = evoked.info @@ -390,12 +391,22 @@ def test_inverse_operator_channel_ordering(evoked, noise_cov): @pytest.mark.parametrize( "method, lower, upper, depth", [ - ("MNE", 54, 57, dict(limit=None, combine_xyz=False, exp=1.0)), # DICS def - ("MNE", 75, 80, dict(limit_depth_chs=False)), # ancient MNE default + pytest.param( + "MNE", + 54, + 57, + dict(limit=None, combine_xyz=False, exp=1.0), + marks=pytest.mark.slowtest, + ), # DICS default + pytest.param( + "MNE", 75, 80, dict(limit_depth_chs=False), marks=pytest.mark.slowtest + ), # ancient MNE default ("MNE", 83, 87, 0.8), # MNE default - ("MNE", 89, 92, dict(limit_depth_chs="whiten")), # sparse default - ("dSPM", 96, 98, 0.8), - ("sLORETA", 100, 100, 0.8), + pytest.param( + "MNE", 89, 92, dict(limit_depth_chs="whiten"), marks=pytest.mark.slowtest + ), # sparse default + pytest.param("dSPM", 96, 98, 0.8, marks=pytest.mark.slowtest), + pytest.param("sLORETA", 100, 100, 0.8, marks=pytest.mark.slowtest), pytest.param("eLORETA", 100, 100, None, marks=pytest.mark.slowtest), pytest.param("eLORETA", 100, 100, 0.8, marks=pytest.mark.slowtest), ], @@ -418,11 +429,25 @@ def test_localization_bias_fixed(bias_params_fixed, method, lower, upper, depth) @pytest.mark.parametrize( "method, lower, upper, depth, loose", [ - ("MNE", 32, 37, dict(limit=None, combine_xyz=False, exp=1.0), 0.2), # DICS + pytest.param( + "MNE", + 32, + 37, + dict(limit=None, combine_xyz=False, exp=1.0), + 0.2, + marks=pytest.mark.slowtest, + ), # DICS ("MNE", 78, 81, 0.8, 0.2), # MNE default - ("MNE", 89, 92, dict(limit_depth_chs="whiten"), 0.2), # sparse default - ("dSPM", 85, 87, 0.8, 0.2), - ("sLORETA", 100, 100, 0.8, 0.2), + pytest.param( + "MNE", + 89, + 92, + dict(limit_depth_chs="whiten"), + 0.2, + marks=pytest.mark.slowtest, + ), # sparse default + pytest.param("dSPM", 85, 87, 0.8, 0.2, marks=pytest.mark.slowtest), + pytest.param("sLORETA", 100, 100, 0.8, 0.2, marks=pytest.mark.slowtest), pytest.param("eLORETA", 99, 100, None, 0.2, marks=pytest.mark.slowtest), pytest.param("eLORETA", 99, 100, 0.8, 0.2, marks=pytest.mark.slowtest), pytest.param("eLORETA", 99, 100, 0.8, 0.001, marks=pytest.mark.slowtest), @@ -461,7 +486,7 @@ def test_localization_bias_loose( @pytest.mark.parametrize( "method, lower, upper, lower_ori, upper_ori, kwargs, depth, loose", [ - ( + pytest.param( "MNE", 21, 24, @@ -470,8 +495,9 @@ def test_localization_bias_loose( {}, dict(limit=None, combine_xyz=False, exp=1.0), 1, + marks=pytest.mark.slowtest, ), - ( + pytest.param( "MNE", 35, 40, @@ -480,6 +506,7 @@ def test_localization_bias_loose( {}, dict(limit_depth_chs=False), 1, + marks=pytest.mark.slowtest, ), # ancient default ("MNE", 45, 55, 0.94, 0.95, {}, 0.8, 1), # MNE default ( @@ -493,7 +520,9 @@ def test_localization_bias_loose( 1, ), # sparse default ("dSPM", 40, 45, 0.96, 0.97, {}, 0.8, 1), - ("sLORETA", 93, 95, 0.95, 0.96, {}, 0.8, 1), + pytest.param( + "sLORETA", 93, 95, 0.95, 0.96, {}, 0.8, 1, marks=pytest.mark.slowtest + ), pytest.param( "eLORETA", 93, @@ -567,6 +596,7 @@ def test_apply_inverse_sphere(evoked, tmp_path): assert_array_equal(np.argmax(stc.data, axis=0), np.repeat(np.arange(101), 3)) +@pytest.mark.slowtest @pytest.mark.parametrize("loose", [0.0, 0.2, 1.0]) @pytest.mark.parametrize("lambda2", [1.0 / 9.0, 0.0]) def test_apply_inverse_eLORETA_MNE_equiv(bias_params_free, loose, lambda2): @@ -764,6 +794,7 @@ def assert_var_exp_log(log, lower, upper): return exp_var +@pytest.mark.slowtest @pytest.mark.parametrize("method", INVERSE_METHODS) @pytest.mark.parametrize("pick_ori", (None, "vector")) def test_inverse_residual(evoked, method, pick_ori): @@ -936,6 +967,7 @@ def test_make_inverse_operator_vector(evoked, noise_cov): assert_allclose(stc_diff.data, (stc_vec0 - stc_vec1).magnitude().data, atol=1e-20) +@pytest.mark.slowtest def test_make_inverse_operator_diag(evoked, noise_cov, tmp_path, azure_windows): """Test MNE inverse computation with diagonal noise cov.""" noise_cov = noise_cov.as_diag() @@ -1056,6 +1088,7 @@ def test_io_inverse_operator(tmp_path): _fast_methods.pop(_fast_methods.index("eLORETA")) +@pytest.mark.slowtest @testing.requires_testing_data @pytest.mark.parametrize("method", _fast_methods) @pytest.mark.parametrize("pick_ori", ["normal", None]) @@ -1118,6 +1151,7 @@ def test_apply_inverse_cov(method, pick_ori): ) +@pytest.mark.slowtest @testing.requires_testing_data def test_apply_mne_inverse_raw(): """Test MNE with precomputed inverse operator on Raw.""" diff --git a/mne/minimum_norm/tests/test_resolution_matrix.py b/mne/minimum_norm/tests/test_resolution_matrix.py index 73c6977f61d..b5b41e2611d 100644 --- a/mne/minimum_norm/tests/test_resolution_matrix.py +++ b/mne/minimum_norm/tests/test_resolution_matrix.py @@ -26,6 +26,7 @@ fname_label = data_path / "subjects" / "sample" / "label" / "lh.V1.label" +@pytest.mark.slowtest @testing.requires_testing_data @pytest.mark.parametrize("src_type", ("surface", "volume")) def test_resolution_matrix_free(src_type, fwd_volume_small): @@ -131,6 +132,7 @@ def test_resolution_matrix_free(src_type, fwd_volume_small): assert_array_almost_equal(stc_psf_label_free.data, stc_ctf_label_free.data) +@pytest.mark.slowtest @testing.requires_testing_data def test_resolution_matrix_fixed(): """Test resolution matrices with fixed orientations.""" diff --git a/mne/minimum_norm/tests/test_time_frequency.py b/mne/minimum_norm/tests/test_time_frequency.py index dabb18433ff..a6cdc78dbae 100644 --- a/mne/minimum_norm/tests/test_time_frequency.py +++ b/mne/minimum_norm/tests/test_time_frequency.py @@ -271,6 +271,7 @@ def test_tfr_multi_label(): assert multi_lab_pow.shape == (2, n_freqs, n_times) +@pytest.mark.slowtest @testing.requires_testing_data @pytest.mark.parametrize("method", INVERSE_METHODS) @pytest.mark.parametrize("pick_ori", (None, "normal")) # XXX vector someday? diff --git a/mne/tests/test_surface.py b/mne/tests/test_surface.py index 86074f417c1..eeaedbb5e05 100644 --- a/mne/tests/test_surface.py +++ b/mne/tests/test_surface.py @@ -333,6 +333,7 @@ def test_voxel_neighbors(): assert true_volume.difference(volume) == set() +@pytest.mark.slowtest @testing.requires_testing_data @pytest.mark.parametrize("ret_nn", (False, True)) @pytest.mark.parametrize("method", ("accurate", "nearest"))