1515 one_to_all_connectivity_pairs ,
1616 read_connectivity ,
1717 restrict_forward_to_vertices ,
18+ dics_coherence_external
1819)
1920from conpy .connectivity import _BaseConnectivity , _get_vert_ind_from_label
2021from mne import BiHemiLabel , Label , SourceEstimate
22+ from mne .beamformer import make_dics
2123from mne .datasets import testing
2224from mne .time_frequency import csd_morlet
2325from mne .utils import _TempDir
@@ -60,7 +62,7 @@ def _load_restricted_forward(source_vertno1, source_vertno2):
6062 return fwd_free , fwd_fixed
6163
6264
63- def _simulate_data (fwd_fixed , source_vertno1 , source_vertno2 ):
65+ def _simulate_data (fwd_fixed , source_vertno1 , source_vertno2 , external = False ):
6466 """Simulate two oscillators on the cortex."""
6567 sfreq = 50.0 # Hz.
6668 base_freq = 10
@@ -86,18 +88,32 @@ def _simulate_data(fwd_fixed, source_vertno1, source_vertno2):
8688 signal2 += 1e-8 * np .random .randn (len (times ))
8789
8890 # Construct a SourceEstimate object
91+ if external :
92+ source_data = signal1 [np .newaxis , :]
93+ vertices = [np .array ([source_vertno1 ]), np .array ([])]
94+ # Create an info object that holds information about the sensors
95+ info = mne .create_info (
96+ fwd_fixed ["info" ]["ch_names" ] + ["external" ],
97+ sfreq ,
98+ ch_types = ["grad" ] * fwd_fixed ["info" ]["nchan" ] + ["misc" ],
99+ )
100+ else :
101+ source_data = np .vstack ((signal1 [np .newaxis , :], signal2 [np .newaxis , :]))
102+ vertices = [np .array ([source_vertno1 ]), np .array ([source_vertno2 ])]
103+ # Create an info object that holds information about the sensors
104+ info = mne .create_info (fwd_fixed ["info" ]["ch_names" ], sfreq ,
105+ ch_types = "grad" )
106+
89107 stc = mne .SourceEstimate (
90- np . vstack (( signal1 [ np . newaxis , :], signal2 [ np . newaxis , :])) ,
91- vertices = [ np . array ([ source_vertno1 ]), np . array ([ source_vertno2 ])] ,
108+ source_data ,
109+ vertices = vertices ,
92110 tmin = 0 ,
93111 tstep = 1 / sfreq ,
94112 subject = "sample" ,
95113 )
96-
97- # Create an info object that holds information about the sensors
98- info = mne .create_info (fwd_fixed ["info" ]["ch_names" ], sfreq , ch_types = "grad" )
99- with info ._unlock ():
100- info .update (fwd_fixed ["info" ]) # Merge in sensor position information
114+ # Merge in sensor position information
115+ for info_ch , fwd_ch in zip (info ["chs" ], fwd_fixed ["info" ]["chs" ]):
116+ info_ch .update (fwd_ch )
101117
102118 # Simulated sensor data.
103119 raw = mne .apply_forward_raw (fwd_fixed , stc , info )
@@ -106,6 +122,11 @@ def _simulate_data(fwd_fixed, source_vertno1, source_vertno2):
106122 noise = random .randn (* raw ._data .shape ) * 1e-14
107123 raw ._data += noise
108124
125+ if external :
126+ sensor_data = raw .get_data ()
127+ sensor_data = np .vstack ((sensor_data , np .atleast_2d (signal2 )))
128+ raw = mne .io .RawArray (sensor_data , info )
129+
109130 # Define a single epoch
110131 epochs = mne .Epochs (
111132 raw ,
@@ -118,7 +139,7 @@ def _simulate_data(fwd_fixed, source_vertno1, source_vertno2):
118139 )
119140
120141 # Compute the cross-spectral density matrix
121- csd = csd_morlet (epochs , frequencies = [10 , 20 ])
142+ csd = csd_morlet (epochs , picks = [ 'meg' , 'misc' ], frequencies = [10 , 20 ])
122143
123144 return csd
124145
@@ -173,43 +194,43 @@ def _generate_labels(vertices, n_labels):
173194def test_base_connectivity ():
174195 """Test construction of BaseConnectivity."""
175196 # Pairs and data shape don't match
176- baseCon = _BaseConnectivity ([0.5 , 0.5 , 0.5 ], [[1 , 1 , 2 ], [2 , 3 , 3 ]], 4 )
177- assert_array_equal (baseCon .data , [0.5 , 0.5 , 0.5 ])
197+ base_con = _BaseConnectivity ([0.5 , 0.5 , 0.5 ], [[1 , 1 , 2 ], [2 , 3 , 3 ]], 4 )
198+ assert_array_equal (base_con .data , [0.5 , 0.5 , 0.5 ])
178199
179200 with pytest .raises (ValueError ):
180- baseCon = _BaseConnectivity ([0.5 , 0.5 , 0.5 ], [[1 , 1 ], [2 , 3 ]], 3 )
201+ base_con = _BaseConnectivity ([0.5 , 0.5 , 0.5 ], [[1 , 1 ], [2 , 3 ]], 3 )
181202
182203 # Not enough sources
183204 with pytest .raises (ValueError ):
184- baseCon = _BaseConnectivity ([0.5 , 0.5 , 0.5 ], [[1 , 1 , 2 ], [2 , 3 , 3 ]], 2 )
205+ base_con = _BaseConnectivity ([0.5 , 0.5 , 0.5 ], [[1 , 1 , 2 ], [2 , 3 , 3 ]], 2 )
185206
186207 # Incorrecly shaped source degree
187208 with pytest .raises (ValueError ):
188- baseCon = _BaseConnectivity (
209+ base_con = _BaseConnectivity (
189210 [0.5 , 0.5 , 0.5 ],
190211 [[1 , 1 , 2 ], [2 , 3 , 3 ]],
191212 4 ,
192213 source_degree = ([0 , 2 , 1 ], [0 , 0 , 1 ]),
193214 )
194215
195- baseCon = _make_base_connectivity ()
216+ base_con = _make_base_connectivity ()
196217 assert_array_equal (
197- baseCon .source_degree ,
218+ base_con .source_degree ,
198219 np .array ([[0 , 2 , 2 , 1 , 0 , 0 , 0 , 0 , 0 , 0 ], [0 , 0 , 1 , 2 , 2 , 0 , 0 , 0 , 0 , 0 ]]),
199220 )
200221
201222 # Test properties
202- assert baseCon .n_connections == 5
223+ assert base_con .n_connections == 5
203224 state = {
204225 "data" : np .array ([1 , 1 , 1 ]),
205226 "pairs" : [[2 , 2 , 3 ], [3 , 4 , 4 ]],
206227 "n_sources" : 5 ,
207228 "subject" : None ,
208229 "directed" : False ,
209230 }
210- baseCon .__setstate__ (state )
211- assert baseCon .n_sources == 5
212- assert_array_equal (baseCon .source_degree [0 ], [0 , 0 , 2 , 1 , 0 ])
231+ base_con .__setstate__ (state )
232+ assert base_con .n_sources == 5
233+ assert_array_equal (base_con .source_degree [0 ], [0 , 0 , 2 , 1 , 0 ])
213234
214235
215236def test_alltoall_connectivity ():
@@ -269,8 +290,8 @@ def test_label_connnectivity():
269290
270291def test_connectivity_repr ():
271292 """Test string representation of connectivity classes."""
272- baseCon = _make_base_connectivity ()
273- assert str (baseCon ) == (
293+ base_con = _make_base_connectivity ()
294+ assert str (base_con ) == (
274295 "<_BaseConnectivity | n_sources=10, n_conns=5," " subject=None>"
275296 )
276297
@@ -334,58 +355,58 @@ def test_connectivity_save():
334355
335356def test_adjacency ():
336357 """Test adjacency matrix."""
337- baseCon = _make_base_connectivity ()
338- adjmat = baseCon .get_adjacency ()
339- assert adjmat .nnz == 2 * baseCon .n_connections
340- assert adjmat .shape == (baseCon .n_sources , baseCon .n_sources )
358+ base_con = _make_base_connectivity ()
359+ adjmat = base_con .get_adjacency ()
360+ assert adjmat .nnz == 2 * base_con .n_connections
361+ assert adjmat .shape == (base_con .n_sources , base_con .n_sources )
341362
342363 # Directed
343- baseCon .directed = True
344- adjmat = baseCon .get_adjacency ()
345- assert adjmat .nnz == baseCon .n_connections
346- assert adjmat .shape == (baseCon .n_sources , baseCon .n_sources )
364+ base_con .directed = True
365+ adjmat = base_con .get_adjacency ()
366+ assert adjmat .nnz == base_con .n_connections
367+ assert adjmat .shape == (base_con .n_sources , base_con .n_sources )
347368
348369
349370def test_connectivity_threshold ():
350371 """Test thresholding function of BaseConnectivity."""
351372 # Criterion = None
352- baseCon = _make_base_connectivity ()
353- threshCon = baseCon .threshold (2 , copy = True )
373+ base_con = _make_base_connectivity ()
374+ threshCon = base_con .threshold (2 , copy = True )
354375 assert threshCon .n_connections == 3
355- assert baseCon .n_connections == 5
376+ assert base_con .n_connections == 5
356377 assert_array_equal (threshCon .data , np .array ([3 , 4 , 5 ]))
357378
358- threshCon = baseCon .copy ()
379+ threshCon = base_con .copy ()
359380 threshCon .threshold (2 , direction = "below" , copy = False )
360381 assert threshCon .n_connections == 1
361382 assert_array_equal (threshCon .data , np .array ([1 ]))
362383
363384 # Incorrect direction
364385 with pytest .raises (ValueError ):
365- baseCon .threshold (1 , direction = "wrong" )
386+ base_con .threshold (1 , direction = "wrong" )
366387
367388 # Use criterion
368389 with pytest .raises (ValueError ):
369- baseCon .threshold (1 , crit = np .array ([0 , 0 , 1 , 2 ]))
390+ base_con .threshold (1 , crit = np .array ([0 , 0 , 1 , 2 ]))
370391
371392 pval = np .array ([0.04 , 0.7 , 0.001 , 0.1 , 0.06 ])
372- threshCon = baseCon .threshold (0.05 , crit = pval , copy = True )
393+ threshCon = base_con .threshold (0.05 , crit = pval , copy = True )
373394 assert_array_equal (threshCon .data , np .array ([2 , 4 , 5 ]))
374395
375396
376397def test_compatibility ():
377398 """Test _iscombatible function."""
378- baseCon = _make_base_connectivity ()
399+ base_con = _make_base_connectivity ()
379400 all_con = _make_alltoall_connectivity ()
380401 label_con = _make_label_connectivity ()
381402
382403 # Test BaseConnectivity
383- assert not baseCon .is_compatible (label_con )
384- assert not baseCon .is_compatible (all_con )
385- baseCon2 = _BaseConnectivity (
386- np .array ([6 , 7 , 8 , 9 , 10 ]), baseCon .pairs , baseCon .n_sources
404+ assert not base_con .is_compatible (label_con )
405+ assert not base_con .is_compatible (all_con )
406+ base_con2 = _BaseConnectivity (
407+ np .array ([6 , 7 , 8 , 9 , 10 ]), base_con .pairs , base_con .n_sources
387408 )
388- assert baseCon .is_compatible (baseCon2 )
409+ assert base_con .is_compatible (base_con2 )
389410
390411 # Test VertexConnectivity
391412 assert not all_con .is_compatible (label_con )
@@ -655,3 +676,73 @@ def test_dics_connectivity():
655676 fwd_tan = forward_to_tangential (fwd )
656677 con2 = dics_connectivity (pairs , fwd_tan , csd , reg = 1 )
657678 assert_array_equal (con2 .data , con .data )
679+
680+
681+ @testing .requires_testing_data
682+ def test_dics_coherence_external ():
683+ """Test dics_coherence_external function."""
684+ fwd = _load_forward ()
685+ fwd = mne .pick_types_forward (fwd , meg = "grad" , eeg = False )
686+ fwd_fixed = mne .convert_forward_solution (fwd , force_fixed = True )
687+ source_vert = 146374
688+ sfreq = 50.0
689+ csd = _simulate_data (fwd_fixed , source_vert , None , external = True )
690+ print (csd .ch_names )
691+ # Create an info object that holds information about the sensors (their
692+ # location, etc.). Make sure to include the external sensor!
693+ info = mne .create_info (
694+ fwd ["info" ]["ch_names" ] + ["external" ],
695+ sfreq ,
696+ ch_types = ["grad" ] * fwd ["info" ]["nchan" ] + ["misc" ],
697+ )
698+ # Copy grad positions from the forward solution
699+ for info_ch , fwd_ch in zip (info ["chs" ], fwd ["info" ]["chs" ]):
700+ info_ch .update (fwd_ch )
701+
702+ dics = make_dics (info .copy (), fwd , csd .copy (), reg = 1 ,
703+ inversion = "single" ,
704+ pick_ori = None )
705+ dics_matrix = make_dics (info .copy (), fwd , csd .copy (), reg = 1 ,
706+ inversion = "matrix" ,
707+ pick_ori = None )
708+ dics_fixed = make_dics (info .copy (), fwd , csd .copy (), reg = 1 ,
709+ inversion = "single" ,
710+ pick_ori = 'max-power' )
711+ # Tangential source space
712+ with pytest .raises (ValueError ):
713+ dics_tangential = dics .copy ()
714+ dics_tangential ["weights" ] = np .delete (
715+ dics_tangential ["weights" ],
716+ np .arange (0 , dics ["weights" ].shape [1 ], 3 ), axis = 1 )
717+ dics_coherence_external (csd , dics_tangential , info , fwd ,
718+ external = 'external' ,
719+ pick_ori = 'max-coherence' )
720+ # Max-power ori selected but dics has vector ori
721+ with pytest .raises (ValueError ):
722+ dics_coherence_external (csd , dics , info , fwd , external = 'external' ,
723+ pick_ori = 'max-power' )
724+ # Max-coherence ori selected but dics has fixed ori
725+ with pytest .raises (ValueError ):
726+ dics_coherence_external (csd , dics_fixed , info , fwd , external = 'external' ,
727+ pick_ori = 'max-coherence' )
728+ # Max-coherence ori selected but dics was computed using matrix inversion
729+ with pytest .raises (ValueError ):
730+ dics_coherence_external (csd , dics_matrix , info , fwd ,
731+ external = 'external' , pick_ori = 'max-coherence' )
732+ # Incorrect pick_ori
733+ with pytest .raises (ValueError ):
734+ dics_coherence_external (csd , dics , info , fwd , external = 'external' ,
735+ pick_ori = 'normal' )
736+ # Max power orientation
737+ coh_stc = dics_coherence_external (csd , dics_fixed , info , fwd ,
738+ external = 'external' , pick_ori = 'max-power' )
739+ assert isinstance (coh_stc , SourceEstimate )
740+ assert coh_stc .data .shape == (fwd ['nsource' ], 2 ) # 2 frequencies
741+ assert np .max (coh_stc .data ) <= 1 and np .min (coh_stc .data ) >= 0
742+ # Max coherence orientation
743+ coh_stc = dics_coherence_external (csd , dics , info , fwd ,
744+ external = 'external' ,
745+ pick_ori = 'max-coherence' )
746+ assert isinstance (coh_stc , SourceEstimate )
747+ assert coh_stc .data .shape == (fwd ['nsource' ], 2 ) # 2 frequencies
748+ assert np .max (coh_stc .data ) <= 1 and np .min (coh_stc .data ) >= 0
0 commit comments