Skip to content

Conversation

@GioMarraffini
Copy link

Thanks for contributing. If this is your first time,
make sure to read contributing.md

PR Description

Add wsmi() function to compute weighted symbolic mutual information, a connectivity measure for studying markers of consciousness and conscious perception based on symbolic dynamics.

The implementation includes: - Symbolic transformation using ordinal patterns with optimizations - Mutual information computation between symbolic sequences using Numba JIT - Weighting based on pattern distance for enhanced sensitivity - Memory checks for large kernel sizes to prevent excessive memory usage - Comprehensive test suite with ground truth validation against reference data - Integration with MNE-Connectivity's EpochTemporalConnectivity class - Support for Current Source Density (CSD) preprocessing for EEG data - Configurable low-pass filtering with automatic frequency selection

The measure quantifies non-linear statistical dependencies between time series and is particularly useful for studying consciousness-related brain dynamics.
This PR closes <#301> (Issue number)

Merge checklist

Maintainer, please confirm the following before merging:

  • All comments resolved
  • This is not your own PR
  • All CIs are happy
  • PR title starts with [MRG]
  • whats_new.rst is updated
  • PR description includes phrase "closes <#issue-number>"

@GioMarraffini GioMarraffini reopened this Jun 17, 2025
@GioMarraffini GioMarraffini force-pushed the wsmi-feature branch 2 times, most recently from 907406d to e9cdf80 Compare June 17, 2025 14:10
@GioMarraffini GioMarraffini changed the title ENH: Add weighted symbolic mutual information (wSMI) connectivity measure [MRG] ENH: Add weighted symbolic mutual information (wSMI) connectivity measure Jun 17, 2025
Copy link
Collaborator

@tsbinns tsbinns left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for opening the PR! Below are a couple suggestions/comments.

Typically, new features like this would also include an example. This can be just a very short demonstration of using the function (e.g., https://mne.tools/mne-connectivity/dev/auto_examples/mne_inverse_envelope_correlation_volume.html) or it could be a more detailed tutorial with an explanation of the method (e.g., https://mne.tools/mne-connectivity/dev/auto_examples/dpli_wpli_pli.html).

I haven't checked the unit tests extensively yet, will revisit based on the discussions. From a first glance they look extensive and the regression tests are great to have.

@GioMarraffini
Copy link
Author

Just added the changes to a new commit with the example in mne-connectivity/examples/wsmi_example.py. I dont know if this is correct please let me know

@tsbinns
Copy link
Collaborator

tsbinns commented Jun 27, 2025

@GioMarraffini @Laouen Thanks both for responding to my initial suggestion so quickly. The changes with the anti-aliasing parameter look very reasonable.
Unfortunately the next 2 weeks at work are super busy for me so I will likely be unable to properly review these latest changes before then, but after that I will try to respond ASAP!

@GioMarraffini
Copy link
Author

@GioMarraffini @Laouen Thanks both for responding to my initial suggestion so quickly. The changes with the anti-aliasing parameter look very reasonable. Unfortunately the next 2 weeks at work are super busy for me so I will likely be unable to properly review these latest changes before then, but after that I will try to respond ASAP!

@tsbinns Hi just wanted to ask if you had time to review it and if there were any news. We are happy to make any necessary changes.

Copy link
Collaborator

@tsbinns tsbinns left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the delay. Altogether I think the changes were very good and I trust your judgement for adapting the anti-aliasing parameter. Here are a few more comments from me.

@tsbinns
Copy link
Collaborator

tsbinns commented Jul 17, 2025

Rerunning the failing test; should not be a problem from your end.

@GioMarraffini
Copy link
Author

@tsbinns Thanks for the revisions and additional comments! I've just pushed the new updates. let me know if you spot anything else.

Copy link
Collaborator

@tsbinns tsbinns left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the revisions! Some very minor points here. The main outstanding things are:

  • Supporting arrays as data inputs from before.
  • Triaging numba to be an optional dependency.
  • Switching to catching memory errors rather than a user-specified memory limit (pending input from others).

It's really great work so far! Let me know if you need a hand bringing any of this over the line.

@tsbinns
Copy link
Collaborator

tsbinns commented Jul 30, 2025

Something I also didn't consider before is the size of the testing data. The existing test data is 0.9 MB, but the wSMI data is 5.3 MB. Without the wSMI test data the source directory is only ~3 MB.
Would it be possible to reduce the size of the test data? Or could some of this be offloaded to simulating the data in the tests at runtime?

@Laouen
Copy link

Laouen commented Jul 30, 2025

The problem with generating test data online is that we have no ground thruth for that data. Currently, the ground truth is the value obtained by the original function. Maybe we can do some tests with estimated result patterns and if we see those partterns then assume is correct even do we don't compare with exact results.

@tsbinns
Copy link
Collaborator

tsbinns commented Jul 31, 2025

The problem with generating test data online is that we have no ground thruth for that data. Currently, the ground truth is the value obtained by the original function.

So the way we have handled this in the past when porting methods from other toolboxes is to have a regression test involving some arbitrary connectivity data of only a few channels/epochs and comparing the results from the original and our implementations. This way the data we need to save is very small.

  • Example of the data: https://github.com/mne-tools/mne-connectivity/tree/main/mne_connectivity/spectral/tests/data
  • Example of the test:
    def test_multivariate_spectral_connectivity_epochs_regression():
    """Test multivar. spectral connectivity over epochs for regression.
    The multivariate methods were originally implemented in MATLAB by their
    respective authors. To show that this Python implementation is identical
    and to avoid any future regressions, we compare the results of the Python
    and MATLAB implementations on some example data (randomly generated).
    As the MNE code for computing the cross-spectral density matrix is not
    available in MATLAB, the CSD matrix was computed using MNE and then loaded
    into MATLAB to compute the connectivity from the original implementations
    using the same processing settings in MATLAB and Python.
    It is therefore important that no changes are made to the settings for
    computing the CSD or the final connectivity scores!
    """
    fpath = os.path.dirname(os.path.realpath(__file__))
    data = pd.read_pickle(os.path.join(fpath, "data", "example_multivariate_data.pkl"))
    methods = ["cacoh", "mic", "mim", "gc", "gc_tr"]
    con = spectral_connectivity_epochs(
    data,
    method=methods,
    indices=([[0, 1]], [[2, 3]]),
    mode="multitaper",
    sfreq=100,
    fskip=0,
    faverage=False,
    tmin=0,
    tmax=None,
    mt_bandwidth=4,
    mt_low_bias=True,
    mt_adaptive=False,
    gc_n_lags=20,
    rank=tuple([[2], [2]]),
    n_components=1,
    n_jobs=1,
    )
    mne_results = {}
    for this_con in con:
    # must take the absolute of the MIC scores, as the MATLAB
    # implementation returns the absolute values.
    if this_con.method == "mic":
    mne_results[this_con.method] = np.abs(this_con.get_data())
    else:
    mne_results[this_con.method] = this_con.get_data()
    matlab_results = pd.read_pickle(
    os.path.join(fpath, "data", "example_multivariate_matlab_results.pkl")
    )
    for method in methods:
    assert_allclose(matlab_results[method], mne_results[method], 1e-5)

Maybe we can do some tests with estimated result patterns and if we see those partterns then assume is correct even do we don't compare with exact results.

This is what we have done for evaluating whether ported methods are working as intended. We simulate test data in the scripts with various connectivity profiles and validate the results based on whether the connectivity values are in a reasonable range. We don't have to check that they match the original implementation exactly since the regression test is handling that side of things.

  • Example of testing that connectivity is in a reasonable range:
    # Check connectivity scores are in expected range
    if method in ["cacoh", "mic", "mim"]:
    if method in ["cacoh", "mic"]:
    lower_t = 0.2
    upper_t = 0.5
    if method == "mim": # MIM will have lower strength
    lower_t = 0.1
    upper_t = 0.3
    assert np.abs(con.get_data())[0, freqs_con].mean() > upper_t
    assert np.abs(con.get_data())[0, freqs_noise].mean() < lower_t

@tsbinns
Copy link
Collaborator

tsbinns commented Aug 19, 2025

Hi @GioMarraffini, do you think those latest commits in a8d8263 are ready for another round of reviews?

@GioMarraffini
Copy link
Author

Hi @GioMarraffini, do you think those latest commits in a8d8263 are ready for another round of reviews?

Hi @tsbinns. Yes, Thanks! If you see something to change please let me know

Copy link
Collaborator

@tsbinns tsbinns left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @GioMarraffini, overall the changes were great and adressed the previous comments. There are just a few things regarding the array inputs.

Also, we don't get notifications for commits, so just add a comment or ping us once you think the code is ready for review.

@GioMarraffini
Copy link
Author

Hi @tsbinns , I've addressed all the feedback from your last review. The code is ready for another check.

Copy link
Collaborator

@tsbinns tsbinns left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@GioMarraffini Just some very minor things again.

@tsbinns
Copy link
Collaborator

tsbinns commented Aug 19, 2025

@larsoner and @drammock, please can I ask one of you to check through this.
All previous comments pending these very minor recent ones have been addressed. The new function is documented with a nice example, and test coverage is comprehensive.

@GioMarraffini
Copy link
Author

Hi @tsbinns , I've addressed your latest feedback. The implementation now follows the pattern you suggested. Ready for review!

@tsbinns
Copy link
Collaborator

tsbinns commented Aug 20, 2025

Hi @tsbinns , I've addressed your latest feedback. The implementation now follows the pattern you suggested. Ready for review!

Looks good!

Copy link
Member

@drammock drammock left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will resume review next week.

)

# Results should be identical
assert_allclose(conn1.get_data(), conn2.get_data())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if they should be truly identical, use assert_array_equal

assert conn.method == "wSMI"


def test_wsmi_all_channels_as_bad():
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you're already testing what happens if 1 channel is bad, and since the behavior is apparently to ignore bad channels, we shouldn't bother also testing when 2, 3, 4, or all channels are bad.

Comment on lines 388 to 390
assert conn.method == "wSMI"
assert conn.n_nodes == n_channels
assert np.all(np.isfinite(conn.get_data()))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A test that parametrizes over kernel and tau values should test that the different kernel or tau values have different effects. There are at least 2 ways to do that:

  1. use non-random channel data, compute from first principles what the conn values should be for different kernel/tau values, pass them in as part of the parametrization, and assert that you get what you expect
  2. don't parametrize, run the wsmi a few times (with different kernel/tau values) within the test function, and assert that the resulting conn.get_data() arrays are actually different.

Comment on lines 435 to 440
# Values should be different (wSMI uses weights, SMI doesn't)
# They shouldn't be exactly equal due to the weighting
# The methods differ in their weighting, but with random data
# they might produce similar results (this is expected behavior)
# So we just check that both produce finite values
pass
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the right lesson to draw from this is that we shouldn't be using random data in tests like this one.

Comment on lines 594 to 596
# =============================================================================
# Ground Truth Validation Tests (New Test Data System)
# =============================================================================
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah, I wish I'd known this was coming! I'm going to stop reviewing for now because I'm out of time today. I haven't yet reviewed the tests that follow, so take this with a grain of salt, but most of the preceding tests can probably be removed or consolidated. E.g, there could maybe be 1 test using fake/random data (could even be np.zeros) that checks all the expected errors/warnings given different degenerate input values; maybe 1 test to verify a bunch of basic shape/object properties, and then everything else should probably be these "ground truth" type of tests (if done correctly, they ought to hit most/all of the variations you hit above, e.g., different channel types, weighting, antialias, averaging, time-windowing, or indexing).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @drammock ! thanks for all the feedback! just one question should I wait until these following reviews on this file or should I start deleting previous tests? I already made the necesary adjustment to the example file. Please let me know if it needs further modifications. Thanks!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes feel free to start simplifying the tests now. I'll try to take another look on Friday

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I simplifyed enough now but please tell me what you think! Also if there is anything else you see please let me know @tsbinns @drammock Thanks!

@GioMarraffini
Copy link
Author

@drammock @tsbinns Just to let you know, my insternship in this lab will end soon. Do you think we are close to merging or you think we still have a long way to go?

@tsbinns
Copy link
Collaborator

tsbinns commented Oct 14, 2025

Hi @GioMarraffini, I am in the middle of double checking the bad channel handling behaviour, which I will (really) try and finish this week, but I would push any needed changes so that would not require more time from you.
I haven't looked too much at the test/example changes. I will speak with Dan tomorrow and ask if this is something they would like me to re-review.

@Laouen
Copy link

Laouen commented Oct 14, 2025

Hi @GioMarraffini, I am in the middle of double checking the bad channel handling behaviour, which I will (really) try and finish this week, but I would push any needed changes so that would not require more time from you. I haven't looked too much at the test/example changes. I will speak with Dan tomorrow and ask if this is something they would like me to re-review.

Thanks @tsbinns . Sorry for the rush, but since @GioMarraffini internship at the lab will finish soon, it would be a shame not to be able to merge this PR.

Thanks again for all your time invested on this PR!

Copy link
Member

@drammock drammock left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @GioMarraffini and @Laouen,
Apologies for the very long delay in reviewing. @tsbinns and I just synced up about this PR, and have concluded that there's a fair bit of work left to get this PR into a state where we're comfortable merging it. As far as we can tell the implementation is fine, and you've done a thorough job generating the example and the tests. However, we feel the tests are too long/numerous and also not stringent enough. There are also some testing "tricks" we'd like to use (parametrizing test inputs, possibly fixtures for the simulated data) that would make this more maintainable in the long-term.

I understand that your internship ends soon and you would like to have this all-the-way done before then, but we simply cannot guarantee that timeline; reviewing large PRs takes a long time, and we have to be judicious about the added maintenance burden we're taking on here. If you want to keep working on it (now or after your internship) you can of course do so, but FYI our plan is for Thomas and I to push some commits in the coming week or so, to get things to a mergable state, so you can rest assured that the work will get merged in eventually (hopefully fairly soon!) even if you want/need to stop working on it now.

(NB: inline comments below are somewhat terse, as they're meant more as notes-to-self than as feedback directed at the authors)

examples/wsmi.py Outdated

for epoch in range(n_epochs):
# Set random seed for reproducibility within epoch variation
rng = np.random.default_rng(42 + epoch)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move outside loop

Comment on lines 106 to 110
# 4. Since channel 1 is identical to channel 0, its coupling to channel 2
# should be the same as channel 0's coupling to channel 2
assert abs(coupled_wsmi - identical_coupled_wsmi) < 1e-10, (
"wSMI should be identical for identical source channels"
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't it be possible to assert true equality here? the signals in ch1 and ch2 are identical... is WSMI somehow non-deterministic?

Comment on lines 115 to 118
# 6. Test reproducibility across epochs
assert np.all(np.std(conn_data, axis=0) < 0.5), (
"wSMI should be stable across epochs"
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO check stringency is appropriate

# Create test data with clear nonlinear coupling
data = np.zeros((n_epochs, 3, n_times))

for epoch in range(n_epochs):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be able to simulate without a for-loop?

Comment on lines 149 to 178
# Test tau=1 vs tau=2
conn_tau1 = wsmi(epochs, kernel=3, tau=1)
conn_tau2 = wsmi(epochs, kernel=3, tau=2)

data_tau1 = np.mean(conn_tau1.get_data(), axis=0)
data_tau2 = np.mean(conn_tau2.get_data(), axis=0)

# Connection indices: (0,1)=0, (0,2)=1, (1,2)=2
nonlinear_tau1 = data_tau1[0] # base-nonlinear with tau=1
independent_tau1 = data_tau1[1] # base-independent with tau=1
nonlinear_tau2 = data_tau2[0] # base-nonlinear with tau=2
independent_tau2 = data_tau2[1] # base-independent with tau=2

# Test that tau=2 shows better discrimination than tau=1
if independent_tau1 > 0 and independent_tau2 > 0:
ratio_tau1 = nonlinear_tau1 / independent_tau1
ratio_tau2 = nonlinear_tau2 / independent_tau2

# tau=2 should show better discrimination (higher ratio)
assert ratio_tau2 > ratio_tau1, (
f"tau=2 should show better nonlinear discrimination: "
f"tau=1 ratio={ratio_tau1:.2f}, tau=2 ratio={ratio_tau2:.2f}"
)

# tau=2 should show substantial improvement (at least 2x better)
assert ratio_tau2 > 2 * ratio_tau1, (
f"tau=2 should show substantial improvement over tau=1: "
f"tau=2 ratio should be > 2x tau=1 ratio"
f"tau=2 ratio / tau=1 ratio: {ratio_tau2 / ratio_tau1:.2f}"
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tighten up

assert np.all(np.isfinite(data_matrix))

# Test with different channel types (suppress unit change warning)
import warnings
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

import should be at top of file... but don't do it that way either. Use with pytest.warns(...)

Comment on lines 296 to 301
with warnings.catch_warnings():
warnings.simplefilter("ignore", RuntimeWarning)
epochs.set_channel_types({"Fz": "eeg", "Cz": "eeg", "Pz": "mag", "Oz": "mag"})
conn_mixed = wsmi(epochs, kernel=3, tau=1)
assert conn_mixed.n_nodes == 4
assert np.all(np.isfinite(conn_mixed.get_data()))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move this check into test_wsmi_input_validation_and_errors

Comment on lines 282 to 291
# Check basic properties
assert conn.method == "wSMI"
assert conn.n_nodes == 4
assert conn.n_epochs_used == 2

# Check data shape and validity
data_matrix = conn.get_data()
expected_connections = 4 * 3 // 2 # 4 choose 2 = 6 connections
assert data_matrix.shape == (2, expected_connections)
assert np.all(np.isfinite(data_matrix))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think all this is basic enough that we can skip checking it, or move it into the input_validation_and_errors test (possibly renamed to input_and_output_validation or so)

assert np.all(np.isfinite(conn_mixed.get_data()))


def test_wsmi_averaging_and_indices():
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this whole test can go away, except for the "invalid indices" error checks.

@tsbinns
Copy link
Collaborator

tsbinns commented Oct 15, 2025

@GioMarraffini One comment looking at the example, what would the interpretation be of negative values? There are some occurring in the simulated data.

image

@tsbinns
Copy link
Collaborator

tsbinns commented Oct 16, 2025

To summarise those changes pushed in e93edca:

  • Before, if indices=None, all-to-all connectivity would be computed, the connectivity values were stored as a (connections, ) array corresponding to the lower-triangular entries. The indices in the connectivity object were also stored as these lower-triangular indices. In other indices-accepting functions (e.g., with specral_connectivity_epochs/time), connectivity values for indices=None are stored as a raveled form of the lower-triangular (chans, chans) array, and indices in the connectivity object are set to None. This behaviour was updated for consistency, and changes were made in the tests and example to accomodate this.

  • Before, if indices was specified, only the connectivity values for those channels would be returned, but computations would still be performed on the non-selected channels. To improve efficiency, these unwanted channels are now dropped before connectivity calculations take place.

  • Before, bad channel handling drew from the envelope_correlation function, however the behaviour is slightly different as that function contains no indices handling. The handling was updated for more consistency with indices-accepting functions (e.g., specral_connectivity_epochs/time). Some indexing changes were also made to avoid extra looping over channel indices.

  • The bad channel unit tests were merged into one and follow the bad channel unit tests for other functions more closely.

@GioMarraffini
Copy link
Author

@GioMarraffini One comment looking at the example, what would the interpretation be of negative values? There are some occurring in the simulated data.

image

Individual terms can be negative: For a specific symbol pair (i,j), when the observed joint probability P(X=i, Y=j) is less than what you'd expect under independence P(X=i) * P(Y=j), that term contributes negatively to MI:
If P(X=i, Y=j) < P(X=i) × P(Y=j), then log(P(X=i, Y=j)) - log(P(X=i)) - log(P(Y=j)) < 0

Standard MI is always non-negative because when you sum over ALL symbol pairs, positive and negative contributions balance out (by Jensen's inequality) to give a non-negative result. However, wSMI breaks this balance: The weighting matrix (from _get_weights_matrix) selectively zeros out certain symbol pairs:
Identical symbol pairs (diagonal elements)
Opposed symbol pairs (anti-diagonal elements)
This selective exclusion can make the sum negative: If the excluded pairs were contributing more positive MI than the remaining pairs, the weighted sum becomes negative.

What Negative wSMI Values Mean for Connectivity
After removing potential common-source artifacts (identical and opposed ordinal patterns), the remaining pattern co-occurrences show less information sharing than expected under independence.

TL;DR:

In the original sense, wSMI isn’t a signed “positive vs. negative connectivity” measure. Standard mutual information is ≥ 0, but wSMI applies a weighting that drops “trivial” symbol pairings (identical or opposite-sign) to reduce volume-conduction effects. That weighting breaks the usual KL-divergence guarantee, so small negative values can appear—they don’t mean “anticoupling”; they usually mean “no information sharing above chance (and/or estimator noise).

@tsbinns
Copy link
Collaborator

tsbinns commented Oct 20, 2025

@GioMarraffini Thanks for the detailed answer! That makes sense.

I think it would be good to include a short note about that in the example, perhaps after the results are shown in the Visualizing wSMI Connectivity Results section.

Could be something like:

Note: Small negative values can occur for wSMI, reflecting estimator noise or a lack of information sharing above chance, rather than an anti-coupling of signals.

What do you think?

@GioMarraffini
Copy link
Author

@GioMarraffini Thanks for the detailed answer! That makes sense.

I think it would be good to include a short note about that in the example, perhaps after the results are shown in the Visualizing wSMI Connectivity Results section.

Could be something like:

Note: Small negative values can occur for wSMI, reflecting estimator noise or a lack of information sharing above chance, rather than an anti-coupling of signals.

What do you think?

Great, I think that would help

@Laouen
Copy link

Laouen commented Oct 28, 2025

Hello @tsbinns here I answer regarding your tests comment:

So the way we have handled this in the past when porting methods from other toolboxes is to have a regression test involving some arbitrary connectivity data of only a few channels/epochs and comparing the results from the original and our implementations. This way the data we need to save is very small.

I think we are aligned and your description of regresion tests is what we have currently impemented, with the unique distinction that we've used the whole original data instead of a minimum required tests set as you have in the data you point out.

Maybe we can do some tests with estimated result patterns and if we see those partterns then assume is correct even do we don't compare with exact results.

This is what we have done for evaluating whether ported methods are working as intended. We simulate test data in the scripts with various connectivity profiles and validate the results based on whether the connectivity values are in a reasonable range. We don't have to check that they match the original implementation exactly since the regression test is handling that side of things.

  • Example of testing that connectivity is in a reasonable range:
    # Check connectivity scores are in expected range
    if method in ["cacoh", "mic", "mim"]:
    if method in ["cacoh", "mic"]:
    lower_t = 0.2
    upper_t = 0.5
    if method == "mim": # MIM will have lower strength
    lower_t = 0.1
    upper_t = 0.3
    assert np.abs(con.get_data())[0, freqs_con].mean() > upper_t
    assert np.abs(con.get_data())[0, freqs_noise].mean() < lower_t

perfect, we can do this kind of tests as well. We will implement a tests in this direction and push a new commit.

@Laouen
Copy link

Laouen commented Oct 28, 2025

@tsbinns @drammock

Hi @GioMarraffini and @Laouen, Apologies for the very long delay in reviewing. @tsbinns and I just synced up about this PR, and have concluded that there's a fair bit of work left to get this PR into a state where we're comfortable merging it. As far as we can tell the implementation is fine, and you've done a thorough job generating the example and the tests. However, we feel the tests are too long/numerous and also not stringent enough. There are also some testing "tricks" we'd like to use (parametrizing test inputs, possibly fixtures for the simulated data) that would make this more maintainable in the long-term.

I understand that your internship ends soon and you would like to have this all-the-way done before then, but we simply cannot guarantee that timeline; reviewing large PRs takes a long time, and we have to be judicious about the added maintenance burden we're taking on here. If you want to keep working on it (now or after your internship) you can of course do so, but FYI our plan is for Thomas and I to push some commits in the coming week or so, to get things to a mergable state, so you can rest assured that the work will get merged in eventually (hopefully fairly soon!) even if you want/need to stop working on it now.

(NB: inline comments below are somewhat terse, as they're meant more as notes-to-self than as feedback directed at the authors)

Thanks a lot for your detailed feedback and for taking such careful attention with this PR — it’s great to see how much care goes into maintaining the library’s standards. We completely agree that ensuring quality is the top priority, even if it takes more time.

That said, since @GioMarraffini’s internship is ending soon, we were wondering if it might be possible to organize a short “PR party” or Zoom call in the coming days. This could help us go through the code together and speed up the review process a bit — without, of course, compromising the quality and rigor of the review.

@tsbinns
Copy link
Collaborator

tsbinns commented Oct 29, 2025

@Laouen @GioMarraffini We can arrange a call to go through the PR together. The best time would be during MNE's Office Hours held every other Friday. @drammock would be there, and I could also join. The next session is 7th Nov.
If that's too late, we can arrange a separate time to meet, but @drammock would not be able to attend.

@Laouen
Copy link

Laouen commented Nov 4, 2025

@Laouen @GioMarraffini We can arrange a call to go through the PR together. The best time would be during MNE's Office Hours held every other Friday. @drammock would be there, and I could also join. The next session is 7th Nov. If that's too late, we can arrange a separate time to meet, but @drammock would not be able to attend.

@tsbinns @drammock This friday 7th at 17h00 is good for us as we see that is your Office Hours. we'are looking forward to see you this friday!

Best

@tsbinns
Copy link
Collaborator

tsbinns commented Nov 7, 2025

Hey @Laouen @GioMarraffini, we are around at the moment in the office hours. Feel free to drop by anytime until the end of the hour.

@Laouen
Copy link

Laouen commented Dec 9, 2025

Hey @Laouen @GioMarraffini, we are around at the moment in the office hours. Feel free to drop by anytime until the end of the hour.

Hi @tsbinns sorry for not being able to connect in your last office hours. Sadly @GioMarraffini is currently on a conference so we can't yet have a time.

Can you give us the calendar with the next office hours so we organize ourself to make a slot and finally do that call ?

Comment on lines +107 to +113
# Plot a sample of the data
fig = epochs.plot(
n_epochs=1,
scalings="auto",
show_scrollbars=False,
title="Synthetic EEG Data with Different Connectivity Patterns",
)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Plot not rendering in docs, will need to resolve before merge.

@tsbinns
Copy link
Collaborator

tsbinns commented Dec 9, 2025

Hi @tsbinns sorry for not being able to connect in your last office hours. Sadly @GioMarraffini is currently on a conference so we can't yet have a time.

Can you give us the calendar with the next office hours so we organize ourself to make a slot and finally do that call ?

Hi @Laouen, the next office hours isn't until the 19th (calendar link here: https://mne.discourse.group/t/mne-office-hours-on-discord/7439).
I finally have time this week free, so I will work on the tests and get things into a more mergeable state.

@Laouen
Copy link

Laouen commented Dec 10, 2025

Hi @tsbinns sorry for not being able to connect in your last office hours. Sadly @GioMarraffini is currently on a conference so we can't yet have a time.
Can you give us the calendar with the next office hours so we organize ourself to make a slot and finally do that call ?

Hi @Laouen, the next office hours isn't until the 19th (calendar link here: https://mne.discourse.group/t/mne-office-hours-on-discord/7439). I finally have time this week free, so I will work on the tests and get things into a more mergeable state.

@tsbinns Perfect, thanks for the calendar link. @GioMarraffini and I are going to connect in the next office hours (12/19) to the discor so we can have the meeting with you. Looking forward to discuss this PR with you!

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.

5 participants