def test_apply_dics_csd(_load_forward): """Test applying a DICS beamformer to a CSD matrix.""" fwd_free, fwd_surf, fwd_fixed, _, label = _load_forward epochs, _, csd, source_vertno = _simulate_data(fwd_fixed) vertices = np.intersect1d(label.vertices, fwd_free['src'][0]['vertno']) source_ind = vertices.tolist().index(source_vertno) reg = 1 # Lots of regularization for our toy dataset with pytest.raises(RuntimeError, match='several sensor types'): make_dics(epochs.info, fwd_free, csd) epochs.pick_types(meg='grad') # Try different types of forward models assert label.hemi == 'lh' assert vertices[source_ind] == source_vertno rr_want = fwd_free['src'][0]['rr'][source_vertno] for fwd in [fwd_free, fwd_surf, fwd_fixed]: filters = make_dics(epochs.info, fwd, csd, label=label, reg=reg, inversion='single') power, f = apply_dics_csd(csd, filters) assert f == [10, 20] # Did we find the true source at 20 Hz? idx = np.argmax(power.data[:, 1]) rr_got = fwd_free['src'][0]['rr'][vertices[idx]] dist = np.linalg.norm(rr_got - rr_want) assert dist == 0. # Is the signal stronger at 20 Hz than 10? assert power.data[source_ind, 1] > power.data[source_ind, 0]
def test_apply_dics_csd(_load_forward, idx, inversion, weight_norm): """Test applying a DICS beamformer to a CSD matrix.""" fwd_free, fwd_surf, fwd_fixed, _ = _load_forward epochs, _, csd, source_vertno, label, vertices, source_ind = \ _simulate_data(fwd_fixed, idx) reg = 1 # Lots of regularization for our toy dataset with pytest.raises(ValueError, match='several sensor types'): make_dics(epochs.info, fwd_free, csd) epochs.pick_types(meg='grad') # Try different types of forward models assert label.hemi == 'lh' for fwd in [fwd_free, fwd_surf, fwd_fixed]: filters = make_dics(epochs.info, fwd, csd, label=label, reg=reg, inversion=inversion, weight_norm=weight_norm) power, f = apply_dics_csd(csd, filters) assert f == [10, 20] # Did we find the true source at 20 Hz? dist = _fwd_dist(power, fwd_free, vertices, source_ind) assert dist == 0. # Is the signal stronger at 20 Hz than 10? assert power.data[source_ind, 1] > power.data[source_ind, 0]
def test_make_dics_rank(_load_forward, idx, whiten): """Test making DICS beamformer filters with rank param.""" _, fwd_surf, fwd_fixed, _ = _load_forward epochs, _, csd, _, label, _, _ = _simulate_data(fwd_fixed, idx) if whiten: noise_csd, want_rank = _make_rand_csd(epochs.info, csd) kind = 'mag + grad' else: noise_csd = None epochs.pick_types(meg='grad') want_rank = len(epochs.ch_names) assert want_rank == 41 kind = 'grad' with catch_logging() as log: filters = make_dics(epochs.info, fwd_surf, csd, label=label, noise_csd=noise_csd, verbose=True) log = log.getvalue() assert f'Estimated rank ({kind}): {want_rank}' in log, log stc, _ = apply_dics_csd(csd, filters) other_rank = want_rank - 1 # shouldn't make a huge difference use_rank = dict(meg=other_rank) if not whiten: # XXX it's a bug that our rank functions don't treat "meg" # properly here... use_rank['grad'] = use_rank.pop('meg') with catch_logging() as log: filters_2 = make_dics(epochs.info, fwd_surf, csd, label=label, noise_csd=noise_csd, rank=use_rank, verbose=True) log = log.getvalue() assert f'Computing rank from covariance with rank={use_rank}' in log, log stc_2, _ = apply_dics_csd(csd, filters_2) corr = np.corrcoef(stc_2.data.ravel(), stc.data.ravel())[0, 1] assert 0.8 < corr < 0.99999 # degenerate conditions if whiten: # make rank deficient data = noise_csd.get_data(0.) data[0] = data[:0] = 0 noise_csd._data[:, 0] = _sym_mat_to_vector(data) with pytest.raises(ValueError, match='meg data rank.*the noise rank'): filters = make_dics(epochs.info, fwd_surf, csd, label=label, noise_csd=noise_csd, verbose=True)
def test_real(_load_forward): """Test using a real-valued filter.""" fwd_free, fwd_surf, fwd_fixed, fwd_vol, label = _load_forward epochs, _, csd, source_vertno = _simulate_data(fwd_fixed) vertices = np.intersect1d(label.vertices, fwd_free['src'][0]['vertno']) source_ind = vertices.tolist().index(source_vertno) rr_want = fwd_free['src'][0]['rr'][source_vertno] epochs.pick_types('grad') reg = 1 # Lots of regularization for our toy dataset filters_real = make_dics(epochs.info, fwd_surf, csd, label=label, reg=reg, real_filter=True) # Also test here that no warings are thrown - implemented to check whether # src should not be None warning occurs: with pytest.warns(None) as w: power, f = apply_dics_csd(csd, filters_real) assert len(w) == 0 assert f == [10, 20] assert np.argmax(power.data[:, 1]) == source_ind assert power.data[source_ind, 1] > power.data[source_ind, 0] # Test rank reduction filters_real = make_dics(epochs.info, fwd_surf, csd, label=label, reg=5, pick_ori='max-power', inversion='matrix', reduce_rank=True) power, f = apply_dics_csd(csd, filters_real) assert f == [10, 20] idx = np.argmax(power.data[:, 1]) rr_got = fwd_free['src'][0]['rr'][vertices[idx]] dist = np.linalg.norm(rr_got - rr_want) assert dist <= 0.02 assert power.data[source_ind, 1] > power.data[source_ind, 0] # Test computing source power on a volume source space filters_vol = make_dics(epochs.info, fwd_vol, csd, reg=reg) power, f = apply_dics_csd(csd, filters_vol) vol_source_ind = 3851 # FIXME: not make this hardcoded assert f == [10, 20] assert np.argmax(power.data[:, 1]) == vol_source_ind assert power.data[vol_source_ind, 1] > power.data[vol_source_ind, 0] # check whether a filters object without src_type throws expected warning del filters_vol['src_type'] # emulate 0.16 behaviour to cause warning with pytest.warns(RuntimeWarning, match='spatial filter does not contain ' 'src_type'): apply_dics_csd(csd, filters_vol)
def test_real(_load_forward, idx, mat_tol, vol_tol): """Test using a real-valued filter.""" fwd_free, fwd_surf, fwd_fixed, fwd_vol = _load_forward epochs, _, csd, source_vertno, label, vertices, source_ind = \ _simulate_data(fwd_fixed, idx) epochs.pick_types('grad') reg = 1 # Lots of regularization for our toy dataset filters_real = make_dics(epochs.info, fwd_surf, csd, label=label, reg=reg, real_filter=True) # Also test here that no warings are thrown - implemented to check whether # src should not be None warning occurs: with pytest.warns(None) as w: power, f = apply_dics_csd(csd, filters_real) assert len(w) == 0 assert f == [10, 20] dist = _fwd_dist(power, fwd_surf, vertices, source_ind) assert dist == 0 assert power.data[source_ind, 1] > power.data[source_ind, 0] # Test rank reduction filters_real = make_dics(epochs.info, fwd_surf, csd, label=label, reg=5, pick_ori='max-power', inversion='matrix', reduce_rank=True) power, f = apply_dics_csd(csd, filters_real) assert f == [10, 20] dist = _fwd_dist(power, fwd_surf, vertices, source_ind) assert dist == 0 assert power.data[source_ind, 1] > power.data[source_ind, 0] # Test computing source power on a volume source space filters_vol = make_dics(epochs.info, fwd_vol, csd, reg=reg) power, f = apply_dics_csd(csd, filters_vol) vol_source_ind = _nearest_vol_ind(fwd_vol, fwd_surf, vertices, source_ind) assert f == [10, 20] dist = _fwd_dist(power, fwd_vol, fwd_vol['src'][0]['vertno'], vol_source_ind) assert dist <= vol_tol assert power.data[vol_source_ind, 1] > power.data[vol_source_ind, 0] # check whether a filters object without src_type throws expected warning del filters_vol['src_type'] # emulate 0.16 behaviour to cause warning with pytest.warns(RuntimeWarning, match='spatial filter does not contain ' 'src_type'): apply_dics_csd(csd, filters_vol)
def test_apply_dics_ori_inv(_load_forward, pick_ori, inversion): """Testpicking different orientations and inversion modes.""" fwd_free, fwd_surf, fwd_fixed, fwd_vol, label = _load_forward epochs, _, csd, source_vertno = _simulate_data(fwd_fixed) epochs.pick_types('grad') vertices = np.intersect1d(label.vertices, fwd_free['src'][0]['vertno']) source_ind = vertices.tolist().index(source_vertno) rr_want = fwd_free['src'][0]['rr'][source_vertno] reg_ = 5 if inversion == 'matrix' else 1 filters = make_dics(epochs.info, fwd_surf, csd, label=label, reg=reg_, pick_ori=pick_ori, inversion=inversion, normalize_fwd=False, weight_norm='unit-noise-gain') power, f = apply_dics_csd(csd, filters) assert f == [10, 20] idx = np.argmax(power.data[:, 1]) rr_got = fwd_free['src'][0]['rr'][vertices[idx]] dist = np.linalg.norm(rr_got - rr_want) assert dist <= (0.03 if inversion == 'matrix' else 0.) assert power.data[source_ind, 1] > power.data[source_ind, 0] # Test unit-noise-gain weighting csd_noise = csd.copy() inds = np.triu_indices(csd.n_channels) csd_noise._data[...] = np.eye(csd.n_channels)[inds][:, np.newaxis] noise_power, f = apply_dics_csd(csd_noise, filters) assert np.allclose(noise_power.data, 1) # Test filter with forward normalization instead of weight # normalization filters = make_dics(epochs.info, fwd_surf, csd, label=label, reg=reg_, pick_ori=pick_ori, inversion=inversion, weight_norm=None, normalize_fwd=True) power, f = apply_dics_csd(csd, filters) assert f == [10, 20] idx = np.argmax(power.data[:, 1]) rr_got = fwd_free['src'][0]['rr'][vertices[idx]] dist = np.linalg.norm(rr_got - rr_want) assert dist <= (0.035 if inversion == 'matrix' else 0.) assert power.data[source_ind, 1] > power.data[source_ind, 0]
def test_apply_dics_ori_inv(_load_forward, pick_ori, inversion, idx): """Test picking different orientations and inversion modes.""" fwd_free, fwd_surf, fwd_fixed, fwd_vol = _load_forward epochs, _, csd, source_vertno, label, vertices, source_ind = \ _simulate_data(fwd_fixed, idx) epochs.pick_types(meg='grad') reg_ = 5 if inversion == 'matrix' else 1 filters = make_dics(epochs.info, fwd_surf, csd, label=label, reg=reg_, pick_ori=pick_ori, inversion=inversion, depth=None, weight_norm='unit-noise-gain') power, f = apply_dics_csd(csd, filters) assert f == [10, 20] dist = _fwd_dist(power, fwd_surf, vertices, source_ind) # This is 0. for unit-noise-gain-invariant: assert dist <= (0.02 if inversion == 'matrix' else 0.) assert power.data[source_ind, 1] > power.data[source_ind, 0] # Test unit-noise-gain weighting csd_noise = csd.copy() inds = np.triu_indices(csd.n_channels) csd_noise._data[...] = np.eye(csd.n_channels)[inds][:, np.newaxis] noise_power, f = apply_dics_csd(csd_noise, filters) want_norm = 3 if pick_ori is None else 1. assert_allclose(noise_power.data, want_norm, atol=1e-7) # Test filter with forward normalization instead of weight # normalization filters = make_dics(epochs.info, fwd_surf, csd, label=label, reg=reg_, pick_ori=pick_ori, inversion=inversion, weight_norm=None, depth=1.) power, f = apply_dics_csd(csd, filters) assert f == [10, 20] dist = _fwd_dist(power, fwd_surf, vertices, source_ind) mat_tol = {0: 0.055, 100: 0.20, 200: 0.015, 233: 0.035}[idx] max_ = (mat_tol if inversion == 'matrix' else 0.) assert 0 <= dist <= max_ assert power.data[source_ind, 1] > power.data[source_ind, 0]
def test_apply_dics_ori_inv(_load_forward, pick_ori, inversion, idx, mat_tol, vol_tol): """Testpicking different orientations and inversion modes.""" fwd_free, fwd_surf, fwd_fixed, fwd_vol = _load_forward epochs, _, csd, source_vertno, label, vertices, source_ind = \ _simulate_data(fwd_fixed, idx) epochs.pick_types('grad') reg_ = 5 if inversion == 'matrix' else 1 filters = make_dics(epochs.info, fwd_surf, csd, label=label, reg=reg_, pick_ori=pick_ori, inversion=inversion, normalize_fwd=False, weight_norm='unit-noise-gain') power, f = apply_dics_csd(csd, filters) assert f == [10, 20] dist = _fwd_dist(power, fwd_surf, vertices, source_ind) assert dist <= (0.03 if inversion == 'matrix' else 0.) assert power.data[source_ind, 1] > power.data[source_ind, 0] # Test unit-noise-gain weighting csd_noise = csd.copy() inds = np.triu_indices(csd.n_channels) csd_noise._data[...] = np.eye(csd.n_channels)[inds][:, np.newaxis] noise_power, f = apply_dics_csd(csd_noise, filters) assert np.allclose(noise_power.data, 1) # Test filter with forward normalization instead of weight # normalization filters = make_dics(epochs.info, fwd_surf, csd, label=label, reg=reg_, pick_ori=pick_ori, inversion=inversion, weight_norm=None, normalize_fwd=True) power, f = apply_dics_csd(csd, filters) assert f == [10, 20] dist = _fwd_dist(power, fwd_surf, vertices, source_ind) max_ = (mat_tol if inversion == 'matrix' else 0.) assert 0 <= dist <= max_ assert power.data[source_ind, 1] > power.data[source_ind, 0]
def test_localization_bias_free(bias_params_free, reg, pick_ori, weight_norm, use_cov, depth, lower, upper, real_filter): """Test localization bias for free-orientation DICS.""" evoked, fwd, noise_cov, data_cov, want = bias_params_free noise_csd = _cov_as_csd(noise_cov, evoked.info) data_csd = _cov_as_csd(data_cov, evoked.info) del noise_cov, data_cov if not use_cov: evoked.pick_types(meg='grad') noise_csd = None loc = apply_dics( evoked, make_dics(evoked.info, fwd, data_csd, reg, noise_csd, pick_ori=pick_ori, weight_norm=weight_norm, depth=depth, real_filter=real_filter)).data loc = np.linalg.norm(loc, axis=1) if pick_ori == 'vector' else np.abs(loc) # Compute the percentage of sources for which there is no loc bias: perc = (want == np.argmax(loc, axis=0)).mean() * 100 assert lower <= perc <= upper
def _gen_dics(active_win, baseline_win, epochs): freqs = np.logspace(np.log10(12), np.log10(30), 9) csd = csd_morlet(epochs, freqs, tmin=-1, tmax=1.5, decim=20) csd_baseline = csd_morlet(epochs, freqs, tmin=baseline_win[0], tmax=baseline_win[1], decim=20) csd_ers = csd_morlet(epochs, freqs, tmin=active_win[0], tmax=active_win[1], decim=20) filters = make_dics(epochs.info, fwd, csd.mean(), pick_ori='max-power', reduce_rank=True, real_filter=True) stc_base, freqs = apply_dics_csd(csd_baseline.mean(), filters) stc_act, freqs = apply_dics_csd(csd_ers.mean(), filters) stc_act /= stc_base return stc_act
def test_orientation_max_power(bias_params_fixed, bias_params_free, weight_norm, lower, upper, lower_ori, upper_ori, real_filter): """Test orientation selection for bias for max-power DICS.""" # we simulate data for the fixed orientation forward and beamform using # the free orientation forward, and check the orientation match at the end evoked, _, noise_cov, data_cov, want = bias_params_fixed noise_csd = _cov_as_csd(noise_cov, evoked.info) data_csd = _cov_as_csd(data_cov, evoked.info) del data_cov, noise_cov fwd = bias_params_free[1] filters = make_dics(evoked.info, fwd, data_csd, 0.05, noise_csd, pick_ori='max-power', weight_norm=weight_norm, depth=None, real_filter=real_filter) loc = np.abs(apply_dics(evoked, filters).data) ori = filters['max_power_ori'][0] assert ori.shape == (246, 3) loc = np.abs(loc) # Compute the percentage of sources for which there is no loc bias: max_idx = np.argmax(loc, axis=0) mask = want == max_idx # ones that localized properly perc = mask.mean() * 100 assert lower <= perc <= upper # Compute the dot products of our forward normals and # assert we get some hopefully reasonable agreement assert fwd['coord_frame'] == FIFF.FIFFV_COORD_HEAD nn = np.concatenate( [s['nn'][v] for s, v in zip(fwd['src'], filters['vertices'])]) nn = nn[want] nn = apply_trans(invert_transform(fwd['mri_head_t']), nn, move=False) assert_allclose(np.linalg.norm(nn, axis=1), 1, atol=1e-6) assert_allclose(np.linalg.norm(ori, axis=1), 1, atol=1e-12) dots = np.abs((nn[mask] * ori[mask]).sum(-1)) assert_array_less(dots, 1) assert_array_less(0, dots) got = np.mean(dots) assert lower_ori < got < upper_ori
def test_apply_dics_timeseries(): """Test DICS applied to timeseries data.""" fwd_free, fwd_surf, fwd_fixed, fwd_vol, label = _load_forward() epochs, evoked, csd, source_vertno = _simulate_data(fwd_fixed) vertices = np.intersect1d(label.vertices, fwd_free['src'][0]['vertno']) source_ind = vertices.tolist().index(source_vertno) reg = 5 # Lots of regularization for our toy dataset multiple_filters = make_dics(evoked.info, fwd_surf, csd, label=label, reg=reg) # Sanity checks on the resulting STC after applying DICS on evoked stcs = apply_dics(evoked, multiple_filters) assert isinstance(stcs, list) assert len(stcs) == len(multiple_filters['weights']) assert_array_equal(stcs[0].vertices[0], multiple_filters['vertices'][0]) assert_array_equal(stcs[0].vertices[1], multiple_filters['vertices'][1]) assert_allclose(stcs[0].times, evoked.times) # Applying filters for multiple frequencies on epoch data should fail raises(ValueError, apply_dics_epochs, epochs, multiple_filters) # From now on, only apply filters with a single frequency (20 Hz). csd20 = csd.pick_frequency(20) filters = make_dics(evoked.info, fwd_surf, csd20, label=label, reg=reg) # Sanity checks on the resulting STC after applying DICS on epochs. # Also test here that no warnings are thrown - implemented to check whether # src should not be None warning occurs with pytest.warns(None) as w: stcs = apply_dics_epochs(epochs, filters) assert len(w) == 0 assert isinstance(stcs, list) assert len(stcs) == 1 assert_array_equal(stcs[0].vertices[0], filters['vertices'][0]) assert_array_equal(stcs[0].vertices[1], filters['vertices'][1]) assert_allclose(stcs[0].times, epochs.times) # Did we find the source? stc = (stcs[0] ** 2).mean() assert np.argmax(stc.data) == source_ind # Apply filters to evoked stc = apply_dics(evoked, filters) stc = (stc ** 2).mean() assert np.argmax(stc.data) == source_ind # Test if wrong channel selection is detected in application of filter evoked_ch = cp.deepcopy(evoked) evoked_ch.pick_channels(evoked_ch.ch_names[:-1]) raises(ValueError, apply_dics, evoked_ch, filters) # Test whether projections are applied, by adding a custom projection filters_noproj = make_dics(evoked.info, fwd_surf, csd20, label=label) stc_noproj = apply_dics(evoked, filters_noproj) evoked_proj = evoked.copy() p = compute_proj_evoked(evoked_proj, n_grad=1, n_mag=0, n_eeg=0) proj_matrix = make_projector(p, evoked_proj.ch_names)[0] evoked_proj.info['projs'] += p filters_proj = make_dics(evoked_proj.info, fwd_surf, csd20, label=label) assert_array_equal(filters_proj['proj'], proj_matrix) stc_proj = apply_dics(evoked_proj, filters_proj) assert np.any(np.not_equal(stc_noproj.data, stc_proj.data)) # Test detecting incompatible projections filters_proj['proj'] = filters_proj['proj'][:-1, :-1] raises(ValueError, apply_dics, evoked_proj, filters_proj) # Test returning a generator stcs = apply_dics_epochs(epochs, filters, return_generator=False) stcs_gen = apply_dics_epochs(epochs, filters, return_generator=True) assert_array_equal(stcs[0].data, next(stcs_gen).data) # Test computing timecourses on a volume source space filters_vol = make_dics(evoked.info, fwd_vol, csd20, reg=reg) stc = apply_dics(evoked, filters_vol) stc = (stc ** 2).mean() assert np.argmax(stc.data) == 3851 # TODO: don't make this hard coded # check whether a filters object without src_type throws expected warning del filters_vol['src_type'] # emulate 0.16 behaviour to cause warning with pytest.warns(RuntimeWarning, match='spatial filter does not contain ' 'src_type'): apply_dics_epochs(epochs, filters_vol)
def test_apply_dics_csd(): """Test applying a DICS beamformer to a CSD matrix.""" fwd_free, fwd_surf, fwd_fixed, fwd_vol, label = _load_forward() epochs, _, csd, source_vertno = _simulate_data(fwd_fixed) vertices = np.intersect1d(label.vertices, fwd_free['src'][0]['vertno']) source_ind = vertices.tolist().index(source_vertno) reg = 1 # Lots of regularization for our toy dataset # Construct an identity "noise" CSD, which we will use to test the # 'unit-noise-gain' setting. csd_noise = csd.copy() inds = np.triu_indices(csd.n_channels) # Using [:, :] syntax for in-place broadcasting csd_noise._data[:, :] = np.eye(csd.n_channels)[inds][:, np.newaxis] # Try different types of forward models for fwd in [fwd_free, fwd_surf, fwd_fixed]: filters = make_dics(epochs.info, fwd, csd, label=label, reg=reg, inversion='single') power, f = apply_dics_csd(csd, filters) assert f == [10, 20] # Did we find the true source at 20 Hz? assert np.argmax(power.data[:, 1]) == source_ind # Is the signal stronger at 20 Hz than 10? assert power.data[source_ind, 1] > power.data[source_ind, 0] # Try picking different orientations and inversion modes for pick_ori in [None, 'normal', 'max-power']: for inversion in ['single', 'matrix']: # Matrix inversion mode needs more regularization for this toy # dataset. if inversion == 'matrix': reg_ = 5 else: reg_ = reg filters = make_dics(epochs.info, fwd_surf, csd, label=label, reg=reg_, pick_ori=pick_ori, inversion=inversion, weight_norm='unit-noise-gain') power, f = apply_dics_csd(csd, filters) assert f == [10, 20] assert np.argmax(power.data[:, 1]) == source_ind assert power.data[source_ind, 1] > power.data[source_ind, 0] # Test unit-noise-gain weighting noise_power, f = apply_dics_csd(csd_noise, filters) assert np.allclose(noise_power.data, 1) # Test filter with forward normalization instead of weight # normalization filters = make_dics(epochs.info, fwd_surf, csd, label=label, reg=reg_, pick_ori=pick_ori, inversion=inversion, weight_norm=None, normalize_fwd=True) power, f = apply_dics_csd(csd, filters) assert f == [10, 20] assert np.argmax(power.data[:, 1]) == source_ind assert power.data[source_ind, 1] > power.data[source_ind, 0] # Test using a real-valued filter filters_real = make_dics(epochs.info, fwd_surf, csd, label=label, reg=reg, real_filter=True) # Also test here that no warings are thrown - implemented to check whether # src should not be None warning occurs: with pytest.warns(None) as w: power, f = apply_dics_csd(csd, filters_real) assert len(w) == 0 assert f == [10, 20] assert np.argmax(power.data[:, 1]) == source_ind assert power.data[source_ind, 1] > power.data[source_ind, 0] # Test rank reduction filters_real = make_dics(epochs.info, fwd_surf, csd, label=label, reg=5, pick_ori='max-power', inversion='matrix', reduce_rank=True) power, f = apply_dics_csd(csd, filters_real) assert f == [10, 20] assert np.argmax(power.data[:, 1]) == source_ind assert power.data[source_ind, 1] > power.data[source_ind, 0] # Test computing source power on a volume source space filters_vol = make_dics(epochs.info, fwd_vol, csd, reg=reg) power, f = apply_dics_csd(csd, filters_vol) vol_source_ind = 3851 # FIXME: not make this hardcoded assert f == [10, 20] assert np.argmax(power.data[:, 1]) == vol_source_ind assert power.data[vol_source_ind, 1] > power.data[vol_source_ind, 0] # check whether a filters object without src_type throws expected warning del filters_vol['src_type'] # emulate 0.16 behaviour to cause warning with pytest.warns(RuntimeWarning, match='spatial filter does not contain ' 'src_type'): apply_dics_csd(csd, filters_vol)
def test_make_dics(tmpdir): """Test making DICS beamformer filters.""" # We only test proper handling of parameters here. Testing the results is # done in test_apply_dics_timeseries and test_apply_dics_csd. fwd_free, fwd_surf, fwd_fixed, fwd_vol, label = _load_forward() epochs, _, csd, _ = _simulate_data(fwd_fixed) raises(ValueError, make_dics, epochs.info, fwd_fixed, csd, pick_ori="notexistent") with raises(ValueError, match='rank, if str'): make_dics(epochs.info, fwd_fixed, csd, rank='foo') with raises(TypeError, match='rank must be'): make_dics(epochs.info, fwd_fixed, csd, rank=1.) # Test if fixed forward operator is detected when picking normal # orientation raises(ValueError, make_dics, epochs.info, fwd_fixed, csd, pick_ori="normal") # Test if non-surface oriented forward operator is detected when picking # normal orientation raises(ValueError, make_dics, epochs.info, fwd_free, csd, pick_ori="normal") # Test if volume forward operator is detected when picking normal # orientation raises(ValueError, make_dics, epochs.info, fwd_vol, csd, pick_ori="normal") # Test invalid combinations of parameters raises(NotImplementedError, make_dics, epochs.info, fwd_free, csd, reduce_rank=True, pick_ori=None) raises(NotImplementedError, make_dics, epochs.info, fwd_free, csd, reduce_rank=True, pick_ori='max-power', inversion='single') # Sanity checks on the returned filters n_freq = len(csd.frequencies) vertices = np.intersect1d(label.vertices, fwd_free['src'][0]['vertno']) n_verts = len(vertices) n_orient = 3 n_channels = csd.n_channels # Test return values filters = make_dics(epochs.info, fwd_surf, csd, label=label, pick_ori=None, weight_norm='unit-noise-gain') assert filters['weights'].shape == (n_freq, n_verts * n_orient, n_channels) assert np.iscomplexobj(filters['weights']) assert filters['csd'] == csd assert filters['ch_names'] == csd.ch_names assert_array_equal(filters['proj'], np.eye(n_channels)) assert_array_equal(filters['vertices'][0], vertices) assert_array_equal(filters['vertices'][1], []) # Label was on the LH assert filters['subject'] == fwd_free['src'][0]['subject_his_id'] assert filters['pick_ori'] is None assert filters['n_orient'] == n_orient assert filters['inversion'] == 'single' assert filters['normalize_fwd'] assert filters['weight_norm'] == 'unit-noise-gain' assert 'DICS' in repr(filters) assert 'subject "sample"' in repr(filters) assert '13' in repr(filters) assert '62' in repr(filters) assert 'rank' not in repr(filters) _test_weight_norm(filters) # Test picking orientations. Also test weight norming under these different # conditions. filters = make_dics(epochs.info, fwd_surf, csd, label=label, pick_ori='normal', weight_norm='unit-noise-gain') n_orient = 1 assert filters['weights'].shape == (n_freq, n_verts * n_orient, n_channels) assert filters['n_orient'] == n_orient _test_weight_norm(filters) filters = make_dics(epochs.info, fwd_surf, csd, label=label, pick_ori='max-power', weight_norm='unit-noise-gain') n_orient = 1 assert filters['weights'].shape == (n_freq, n_verts * n_orient, n_channels) assert filters['n_orient'] == n_orient _test_weight_norm(filters) # From here on, only work on a single frequency csd = csd[0] # Test using a real-valued filter filters = make_dics(epochs.info, fwd_surf, csd, label=label, pick_ori='normal', real_filter=True) assert not np.iscomplexobj(filters['weights']) # Test forward normalization. When inversion='single', the power of a # unit-noise CSD should be 1, even without weight normalization. csd_noise = csd.copy() inds = np.triu_indices(csd.n_channels) # Using [:, :] syntax for in-place broadcasting csd_noise._data[:, :] = np.eye(csd.n_channels)[inds][:, np.newaxis] filters = make_dics(epochs.info, fwd_surf, csd_noise, label=label, weight_norm=None, normalize_fwd=True) w = filters['weights'][0][:3] assert_allclose(np.diag(w.dot(w.T)), 1.0, rtol=1e-6, atol=0) # Test turning off both forward and weight normalization filters = make_dics(epochs.info, fwd_surf, csd, label=label, weight_norm=None, normalize_fwd=False) w = filters['weights'][0][:3] assert not np.allclose(np.diag(w.dot(w.T)), 1.0, rtol=1e-2, atol=0) # Test neural-activity-index weight normalization. It should be a scaled # version of the unit-noise-gain beamformer. filters_nai = make_dics(epochs.info, fwd_surf, csd, label=label, weight_norm='nai', normalize_fwd=False) w_nai = filters_nai['weights'][0] filters_ung = make_dics(epochs.info, fwd_surf, csd, label=label, weight_norm='unit-noise-gain', normalize_fwd=False) w_ung = filters_ung['weights'][0] assert np.allclose(np.corrcoef(np.abs(w_nai).ravel(), np.abs(w_ung).ravel()), 1) # Test whether spatial filter contains src_type assert 'src_type' in filters fname = op.join(str(tmpdir), 'filters-dics.h5') filters.save(fname) filters_read = read_beamformer(fname) assert isinstance(filters, Beamformer) assert isinstance(filters_read, Beamformer) for key in ['tmin', 'tmax']: # deal with strictness of object_diff setattr(filters['csd'], key, np.float(getattr(filters['csd'], key))) assert object_diff(filters, filters_read) == ''
csd_pos_theta = mne.time_frequency.read_csd("{dir}nc_{meg}-csd_pos_theta.h5".format(dir=meg_dir,meg=meg)) csd_pos_theta = csd_pos_theta.mean() csd_pos_beta_low = mne.time_frequency.read_csd("{dir}nc_{meg}-csd_pos_beta_low.h5".format(dir=meg_dir,meg=meg)) csd_pos_beta_low = csd_pos_beta_low.mean() csd_pos_beta_high = mne.time_frequency.read_csd("{dir}nc_{meg}-csd_pos_beta_high.h5".format(dir=meg_dir,meg=meg)) csd_pos_beta_high = csd_pos_beta_high.mean() csd_pos_gamma = mne.time_frequency.read_csd("{dir}nc_{meg}-csd_pos_gamma.h5".format(dir=meg_dir,meg=meg)) csd_pos_gamma = csd_pos_gamma.mean() # csd_rest_alpha = mne.time_frequency.read_csd("{dir}nc_{meg}-csd_rest_alpha.h5".format(dir=meg_dir,meg=meg)) # csd_rest_theta = mne.time_frequency.read_csd("{dir}nc_{meg}-csd_rest_theta.h5".format(dir=meg_dir,meg=meg)) # csd_rest_beta_low = mne.time_frequency.read_csd("{dir}nc_{meg}-csd_rest_beta_low.h5".format(dir=meg_dir,meg=meg)) # csd_rest_beta_high = mne.time_frequency.read_csd("{dir}nc_{meg}-csd_rest_beta_high.h5".format(dir=meg_dir,meg=meg)) # csd_rest_gamma = mne.time_frequency.read_csd("{dir}nc_{meg}-csd_rest_gamma.h5".format(dir=meg_dir,meg=meg)) # make DICS beamformers / filters (common filters) for different freq bands filters_exp_alpha = make_dics(epo_exp.info,fwd_exp,csd_exp_alpha,pick_ori='max-power',rank=None,inversion='single',weight_norm=None,normalize_fwd=True,real_filter=False) filters_exp_theta = make_dics(epo_exp.info,fwd_exp,csd_exp_theta,pick_ori='max-power',rank=None,inversion='single',weight_norm=None,normalize_fwd=True,real_filter=False) filters_exp_beta_low = make_dics(epo_exp.info,fwd_exp,csd_exp_beta_low,pick_ori='max-power',rank=None,inversion='single',weight_norm=None,normalize_fwd=True,real_filter=False) filters_exp_beta_high = make_dics(epo_exp.info,fwd_exp,csd_exp_beta_high,pick_ori='max-power',rank=None,inversion='single',weight_norm=None,normalize_fwd=True,real_filter=False) filters_exp_gamma = make_dics(epo_exp.info,fwd_exp,csd_exp_gamma,pick_ori='max-power',rank=None,inversion='single',weight_norm=None,normalize_fwd=True,real_filter=False) # apply the DICS beamformers to get source Estimates for ton, neg, pos using common filters & save to file stc_ton_alpha, freqs_ton_alpha = apply_dics_csd(csd_ton_alpha,filters_exp_alpha) stc_ton_theta, freqs_ton_theta = apply_dics_csd(csd_ton_theta,filters_exp_theta) stc_ton_beta_low, freqs_ton_beta_low = apply_dics_csd(csd_ton_beta_low,filters_exp_beta_low) stc_ton_beta_high, freqs_ton_beta_high = apply_dics_csd(csd_ton_beta_high,filters_exp_beta_high) stc_ton_gamma, freqs_ton_gamma = apply_dics_csd(csd_ton_gamma,filters_exp_gamma) stc_ton_alpha.save(fname=meg_dir+"nc_{}_stc_ton_alpha".format(meg)) stc_ton_theta.save(fname=meg_dir+"nc_{}_stc_ton_theta".format(meg)) stc_ton_beta_low.save(fname=meg_dir+"nc_{}_stc_ton_beta_low".format(meg)) stc_ton_beta_high.save(fname=meg_dir+"nc_{}_stc_ton_beta_high".format(meg))
csd_ers = csd_morlet(epochs, freqs, tmin=0.5, tmax=1.5, decim=20) info = epochs.info del epochs # %% # To compute the source power for a frequency band, rather than each frequency # separately, we average the CSD objects across frequencies. csd = csd.mean() csd_baseline = csd_baseline.mean() csd_ers = csd_ers.mean() # %% # Computing DICS spatial filters using the CSD that was computed on the entire # timecourse. fwd = mne.read_forward_solution(fname_fwd) filters = make_dics(info, fwd, csd, noise_csd=csd_baseline, pick_ori='max-power', reduce_rank=True, real_filter=True) del fwd # %% # Applying DICS spatial filters separately to the CSD computed using the # baseline and the CSD computed during the ERS activity. baseline_source_power, freqs = apply_dics_csd(csd_baseline, filters) beta_source_power, freqs = apply_dics_csd(csd_ers, filters) # %% # Visualizing source power during ERS activity relative to the baseline power. stc = beta_source_power / baseline_source_power message = 'DICS source power in the 12-30 Hz frequency band' brain = stc.plot(hemi='both', views='axial', subjects_dir=subjects_dir, subject=subject, time_label=message)
def test_apply_dics_timeseries(): """Test DICS applied to timeseries data.""" fwd_free, fwd_surf, fwd_fixed, fwd_vol, label = _load_forward() epochs, evoked, csd, source_vertno = _simulate_data(fwd_fixed) vertices = np.intersect1d(label.vertices, fwd_free['src'][0]['vertno']) source_ind = vertices.tolist().index(source_vertno) reg = 5 # Lots of regularization for our toy dataset multiple_filters = make_dics(evoked.info, fwd_surf, csd, label=label, reg=reg) # Sanity checks on the resulting STC after applying DICS on evoked stcs = apply_dics(evoked, multiple_filters) assert isinstance(stcs, list) assert len(stcs) == len(multiple_filters['weights']) assert_array_equal(stcs[0].vertices[0], multiple_filters['vertices'][0]) assert_array_equal(stcs[0].vertices[1], multiple_filters['vertices'][1]) assert_allclose(stcs[0].times, evoked.times) # Applying filters for multiple frequencies on epoch data should fail raises(ValueError, apply_dics_epochs, epochs, multiple_filters) # From now on, only apply filters with a single frequency (20 Hz). csd20 = csd.pick_frequency(20) filters = make_dics(evoked.info, fwd_surf, csd20, label=label, reg=reg) # Sanity checks on the resulting STC after applying DICS on epochs. # Also test here that no warnings are thrown - implemented to check whether # src should not be None warning occurs with pytest.warns(None) as w: stcs = apply_dics_epochs(epochs, filters) assert len(w) == 0 assert isinstance(stcs, list) assert len(stcs) == 1 assert_array_equal(stcs[0].vertices[0], filters['vertices'][0]) assert_array_equal(stcs[0].vertices[1], filters['vertices'][1]) assert_allclose(stcs[0].times, epochs.times) # Did we find the source? stc = (stcs[0] ** 2).mean() assert np.argmax(stc.data) == source_ind # Apply filters to evoked stc = apply_dics(evoked, filters) stc = (stc ** 2).mean() assert np.argmax(stc.data) == source_ind # Test if wrong channel selection is detected in application of filter evoked_ch = cp.deepcopy(evoked) evoked_ch.pick_channels(evoked_ch.ch_names[:-1]) raises(ValueError, apply_dics, evoked_ch, filters) # Test whether projections are applied, by adding a custom projection filters_noproj = make_dics(evoked.info, fwd_surf, csd20, label=label) stc_noproj = apply_dics(evoked, filters_noproj) evoked_proj = evoked.copy() p = compute_proj_evoked(evoked_proj, n_grad=1, n_mag=0, n_eeg=0) proj_matrix = make_projector(p, evoked_proj.ch_names)[0] evoked_proj.info['projs'] += p filters_proj = make_dics(evoked_proj.info, fwd_surf, csd20, label=label) assert_array_equal(filters_proj['proj'], proj_matrix) stc_proj = apply_dics(evoked_proj, filters_proj) assert np.any(np.not_equal(stc_noproj.data, stc_proj.data)) # Test detecting incompatible projections filters_proj['proj'] = filters_proj['proj'][:-1, :-1] raises(ValueError, apply_dics, evoked_proj, filters_proj) # Test returning a generator stcs = apply_dics_epochs(epochs, filters, return_generator=False) stcs_gen = apply_dics_epochs(epochs, filters, return_generator=True) assert_array_equal(stcs[0].data, advance_iterator(stcs_gen).data) # Test computing timecourses on a volume source space filters_vol = make_dics(evoked.info, fwd_vol, csd20, reg=reg) stc = apply_dics(evoked, filters_vol) stc = (stc ** 2).mean() assert np.argmax(stc.data) == 3851 # TODO: don't make this hard coded # check whether a filters object without src_type throws expected warning del filters_vol['src_type'] # emulate 0.16 behaviour to cause warning with pytest.warns(RuntimeWarning, match='spatial filter does not contain ' 'src_type'): apply_dics_epochs(epochs, filters_vol)
info = epochs_grad.info elif sensor_type == 'mag': info = epochs_mag.info elif sensor_type == 'joint': info = epochs_joint.info else: raise ValueError('Invalid sensor type: %s', sensor_type) info_eq, fwd_eq, csd_eq = mne.channels.equalize_channels( [info, fwd, csd]) filters = make_dics(info_eq, fwd_eq, csd_eq, reg=reg, pick_ori=pick_ori, inversion=inversion, weight_norm=weight_norm, noise_csd=csd_baseline if use_noise_cov else None, normalize_fwd=normalize_fwd, real_filter=real_filter, reduce_rank=reduce_rank) # Compute source power stc_baseline, _ = apply_dics_csd(csd_baseline, filters) stc_power, _ = apply_dics_csd(csd_ers, filters) # Normalize with baseline power. stc_power /= stc_baseline stc_power.data = np.log(stc_power.data) peak_vertex, _ = stc_power.get_peak(vert_as_index=True)
# Rotate the view and add a title. mlab.view(0, 0, 550, [0, 0, 0]) mlab.title('MNE-dSPM inverse (RMS)', height=0.9) ############################################################################### # Computing a cortical power map at 10 Hz. using a DICS beamformer: # Estimate the cross-spectral density (CSD) matrix on the trial containing the # signal. csd_signal = csd_morlet(epochs['signal'], frequencies=[10]) # Compute the DICS powermap. For this simulated dataset, we need a lot of # regularization for the beamformer to behave properly. For real recordings, # this amount of regularization is probably too much. filters = make_dics(epochs.info, fwd, csd_signal, reg=1, pick_ori='max-power') power, f = apply_dics_csd(csd_signal, filters) # Plot the DICS power map. brain = power.plot('sample', subjects_dir=subjects_dir, hemi='both', figure=2, size=400) # Indicate the true locations of the source activity on the plot. brain.add_foci(source_vert1, coords_as_verts=True, hemi='lh') brain.add_foci(source_vert2, coords_as_verts=True, hemi='rh') # Rotate the view and add a title. mlab.view(0, 0, 550, [0, 0, 0])
def test_tf_dics(): """Test 5D time-frequency beamforming based on DICS.""" fwd_free, fwd_surf, fwd_fixed, fwd_vol, label = _load_forward() epochs, evoked, _, source_vertno = _simulate_data(fwd_fixed) vertices = np.intersect1d(label.vertices, fwd_free['src'][0]['vertno']) source_ind = vertices.tolist().index(source_vertno) reg = 1 # Lots of regularization for our toy dataset tmin = 0 tmax = 9 tstep = 4 win_lengths = [5, 5] frequencies = [10, 20] freq_bins = [(8, 12), (18, 22)] # Compute DICS for two time windows and two frequencies for mode in ['fourier', 'multitaper', 'cwt_morlet']: stcs = tf_dics(epochs, fwd_surf, None, tmin, tmax, tstep, win_lengths, mode=mode, freq_bins=freq_bins, frequencies=frequencies, decim=10, reg=reg, label=label) # Did we find the true source at 20 Hz? assert np.argmax(stcs[1].data[:, 0]) == source_ind assert np.argmax(stcs[1].data[:, 1]) == source_ind # 20 Hz power should decrease over time assert stcs[1].data[source_ind, 0] > stcs[1].data[source_ind, 1] # 20 Hz power should be more than 10 Hz power at the true source assert stcs[1].data[source_ind, 0] > stcs[0].data[source_ind, 0] # Manually compute source power and compare with the last tf_dics result. source_power = [] time_windows = [(0, 5), (4, 9)] for time_window in time_windows: csd = csd_morlet(epochs, frequencies=[frequencies[1]], tmin=time_window[0], tmax=time_window[1], decim=10) csd = csd.sum() csd._data /= csd.n_fft filters = make_dics(epochs.info, fwd_surf, csd, reg=reg, label=label) stc_source_power, _ = apply_dics_csd(csd, filters) source_power.append(stc_source_power.data) # Comparing tf_dics results with dics_source_power results assert_allclose(stcs[1].data, np.array(source_power).squeeze().T, atol=0) # Test using noise csds. We're going to use identity matrices. That way, # since we're using unit-noise-gain weight normalization, there should be # no effect. stcs = tf_dics(epochs, fwd_surf, None, tmin, tmax, tstep, win_lengths, mode='cwt_morlet', frequencies=frequencies, decim=10, reg=reg, label=label, normalize_fwd=False, weight_norm='unit-noise-gain') noise_csd = csd.copy() inds = np.triu_indices(csd.n_channels) # Using [:, :] syntax for in-place broadcasting noise_csd._data[:, :] = 2 * np.eye(csd.n_channels)[inds][:, np.newaxis] noise_csd.n_fft = 2 # Dividing by n_fft should yield an identity CSD noise_csds = [noise_csd, noise_csd] # Two frequency bins stcs_norm = tf_dics(epochs, fwd_surf, noise_csds, tmin, tmax, tstep, win_lengths, mode='cwt_morlet', frequencies=frequencies, decim=10, reg=reg, label=label, normalize_fwd=False, weight_norm='unit-noise-gain') assert_allclose(stcs_norm[0].data, stcs[0].data, atol=0) assert_allclose(stcs_norm[1].data, stcs[1].data, atol=0) # Test invalid parameter combinations raises(ValueError, tf_dics, epochs, fwd_surf, None, tmin, tmax, tstep, win_lengths, mode='fourier', freq_bins=None) raises(ValueError, tf_dics, epochs, fwd_surf, None, tmin, tmax, tstep, win_lengths, mode='cwt_morlet', frequencies=None) # Test if incorrect number of noise CSDs is detected raises(ValueError, tf_dics, epochs, fwd_surf, [noise_csds[0]], tmin, tmax, tstep, win_lengths, freq_bins=freq_bins) # Test if freq_bins and win_lengths incompatibility is detected raises(ValueError, tf_dics, epochs, fwd_surf, None, tmin, tmax, tstep, win_lengths=[0, 1, 2], freq_bins=freq_bins) # Test if time step exceeding window lengths is detected raises(ValueError, tf_dics, epochs, fwd_surf, None, tmin, tmax, tstep=0.15, win_lengths=[0.2, 0.1], freq_bins=freq_bins) # Test if incorrent number of n_ffts is detected raises(ValueError, tf_dics, epochs, fwd_surf, None, tmin, tmax, tstep, win_lengths, freq_bins=freq_bins, n_ffts=[1]) # Test if incorrect number of mt_bandwidths is detected raises(ValueError, tf_dics, epochs, fwd_surf, None, tmin, tmax, tstep, win_lengths=win_lengths, freq_bins=freq_bins, mode='multitaper', mt_bandwidths=[20]) # Test if subtracting evoked responses yields NaN's, since we only have one # epoch. Suppress division warnings. with pytest.warns(RuntimeWarning, match='[invalid|empty]'): stcs = tf_dics(epochs, fwd_surf, None, tmin, tmax, tstep, win_lengths, mode='cwt_morlet', frequencies=frequencies, subtract_evoked=True, reg=reg, label=label, decim=20) assert np.all(np.isnan(stcs[0].data))
# # 1. The approach as described in :footcite:`vanVlietEtAl2018`, which first # normalizes the forward solution and computes a vector beamformer. # 2. The scalar beamforming approach based on # :footcite:`SekiharaNagarajan2008`, which uses weight normalization # instead of normalizing the forward solution. # Estimate the cross-spectral density (CSD) matrix on the trial containing the # signal. csd_signal = csd_morlet(epochs['signal'], frequencies=[10]) # Compute the spatial filters for each vertex, using two approaches. filters_approach1 = make_dics(info, fwd, csd_signal, reg=0.05, pick_ori='max-power', depth=1., inversion='single', weight_norm=None) print(filters_approach1) filters_approach2 = make_dics(info, fwd, csd_signal, reg=0.05, pick_ori='max-power', depth=None, inversion='matrix', weight_norm='unit-noise-gain') print(filters_approach2)
x.info["dev_head_t"] = epos[0].info["dev_head_t"] epo = mne.concatenate_epochs(epos) csd = csd_morlet(epo, frequencies=freqs, n_jobs=n_jobs, n_cycles=c, decim=3) #csd = csd.mean() fwd_name = "{dir}nc_{sub}_{sp}-fwd.fif".format(dir=proc_dir, sub=sub, sp=spacing) fwd = mne.read_forward_solution(fwd_name) filters = make_dics(epo.info, fwd, csd, real_filter=True, weight_norm="nai", reduce_rank=False, pick_ori="max-power") del epo, csd, fwd print("\n\n") print("\n\n") # if not doTones: # epos = epo_conds # epo_names = epo_cond_names # for epo,epo_name in zip(epos,epo_names): # epo_csd = csd_morlet(epo, frequencies=freqs, # n_jobs=n_jobs, n_cycles=c, decim=3) # #epo_csd = epo_csd.mean() # stc, freqs = apply_dics_csd(epo_csd,filters)
for setting in settings: (reg, sensor_type, pick_ori, inversion, weight_norm, normalize_fwd, real_filter) = setting try: if sensor_type == 'grad': info = epochs_grad.info elif sensor_type == 'mag': info = epochs_mag.info else: raise ValueError('Invalid sensor type: %s', sensor_type) filters = make_dics(info, fwd, csd, reg=reg, pick_ori=pick_ori, inversion=inversion, weight_norm=weight_norm, normalize_fwd=normalize_fwd, real_filter=real_filter) stc, freqs = apply_dics_csd(csd, filters) # Compute distance between true and estimated source dip_true = make_dipole(stc_signal, fwd['src']) dip_est = make_dipole(stc, fwd['src']) dist = np.linalg.norm(dip_true.pos - dip_est.pos) # Fancy evaluation metric ev = evaluate_stc(stc, stc_signal) except Exception as e: print(e)
try: if sensor_type == 'grad': info = epochs_grad.info elif sensor_type == 'mag': info = epochs_mag.info elif sensor_type == 'joint': info = epochs_joint.info else: raise ValueError('Invalid sensor type: %s', sensor_type) filters = make_dics(info, fwd, csd, reg=reg, pick_ori=pick_ori, inversion=inversion, weight_norm=weight_norm, noise_csd=noise_csd if use_noise_cov else None, normalize_fwd=normalize_fwd, reduce_rank=reduce_rank, real_filter=real_filter) stc, freqs = apply_dics_csd(csd, filters) vert1_idx = np.searchsorted(src_sel, config.vertex) vert2_idx = np.searchsorted(src_sel, nb_vertex) ratio1 = stc.data[vert1_idx, 1] / stc.data[vert1_idx, 0] ratio2 = stc.data[vert2_idx, 0] / stc.data[vert2_idx, 1] ratio = np.sqrt(ratio1 * ratio2) corrs.append(list(setting) + [nb_vertex, nb_dist, ratio])
# Set picks picks = mne.pick_types(raw.info, meg=True, eeg=False, eog=False, stim=False, exclude='bads') # Read epochs event_id, tmin, tmax = 1, -0.2, 0.5 events = mne.read_events(event_fname) epochs = mne.Epochs(raw, events, event_id, tmin, tmax, proj=True, picks=picks, baseline=(None, 0), preload=True, reject=dict(grad=4000e-13, mag=4e-12)) evoked = epochs.average() # Read forward operator forward = mne.read_forward_solution(fname_fwd) ############################################################################### # Computing the cross-spectral density matrix at 4 evenly spaced frequencies # from 6 to 10 Hz. We use a decim value of 20 to speed up the computation in # this example at the loss of accuracy. csd = csd_morlet(epochs, tmin=0, tmax=0.5, decim=20, frequencies=np.linspace(6, 10, 4)) # Compute DICS spatial filter and estimate source power. filters = make_dics(epochs.info, forward, csd, reg=0.5) stc, freqs = apply_dics_csd(csd, filters) message = 'DICS source power in the 8-12 Hz frequency band' brain = stc.plot(surface='inflated', hemi='rh', subjects_dir=subjects_dir, time_label=message)
def test_tf_dics(_load_forward, idx, mat_tol, vol_tol): """Test 5D time-frequency beamforming based on DICS.""" fwd_free, fwd_surf, fwd_fixed, _ = _load_forward epochs, _, _, source_vertno, label, vertices, source_ind = \ _simulate_data(fwd_fixed, idx) reg = 1 # Lots of regularization for our toy dataset tmin = 0 tmax = 9 tstep = 4 win_lengths = [5, 5] frequencies = [10, 20] freq_bins = [(8, 12), (18, 22)] with pytest.raises(RuntimeError, match='several sensor types'): stcs = tf_dics(epochs, fwd_surf, None, tmin, tmax, tstep, win_lengths, freq_bins=freq_bins, frequencies=frequencies, decim=10, reg=reg, label=label) epochs.pick_types(meg='grad') # Compute DICS for two time windows and two frequencies for mode in ['fourier', 'multitaper', 'cwt_morlet']: stcs = tf_dics(epochs, fwd_surf, None, tmin, tmax, tstep, win_lengths, mode=mode, freq_bins=freq_bins, frequencies=frequencies, decim=10, reg=reg, label=label) # Did we find the true source at 20 Hz? dist = _fwd_dist(stcs[1], fwd_surf, vertices, source_ind, tidx=0) assert dist == 0 dist = _fwd_dist(stcs[1], fwd_surf, vertices, source_ind, tidx=1) assert dist == 0 # 20 Hz power should decrease over time assert stcs[1].data[source_ind, 0] > stcs[1].data[source_ind, 1] # 20 Hz power should be more than 10 Hz power at the true source assert stcs[1].data[source_ind, 0] > stcs[0].data[source_ind, 0] # Manually compute source power and compare with the last tf_dics result. source_power = [] time_windows = [(0, 5), (4, 9)] for time_window in time_windows: csd = csd_morlet(epochs, frequencies=[frequencies[1]], tmin=time_window[0], tmax=time_window[1], decim=10) csd = csd.sum() csd._data /= csd.n_fft filters = make_dics(epochs.info, fwd_surf, csd, reg=reg, label=label) stc_source_power, _ = apply_dics_csd(csd, filters) source_power.append(stc_source_power.data) # Comparing tf_dics results with dics_source_power results assert_allclose(stcs[1].data, np.array(source_power).squeeze().T, atol=0) # Test using noise csds. We're going to use identity matrices. That way, # since we're using unit-noise-gain weight normalization, there should be # no effect. stcs = tf_dics(epochs, fwd_surf, None, tmin, tmax, tstep, win_lengths, mode='cwt_morlet', frequencies=frequencies, decim=10, reg=reg, label=label, normalize_fwd=False, weight_norm='unit-noise-gain') noise_csd = csd.copy() inds = np.triu_indices(csd.n_channels) # Using [:, :] syntax for in-place broadcasting noise_csd._data[:, :] = 2 * np.eye(csd.n_channels)[inds][:, np.newaxis] noise_csd.n_fft = 2 # Dividing by n_fft should yield an identity CSD noise_csds = [noise_csd, noise_csd] # Two frequency bins stcs_norm = tf_dics(epochs, fwd_surf, noise_csds, tmin, tmax, tstep, win_lengths, mode='cwt_morlet', frequencies=frequencies, decim=10, reg=reg, label=label, normalize_fwd=False, weight_norm='unit-noise-gain') assert_allclose(stcs_norm[0].data, stcs[0].data, atol=0) assert_allclose(stcs_norm[1].data, stcs[1].data, atol=0) # Test invalid parameter combinations with pytest.raises(ValueError, match='fourier.*freq_bins" parameter'): tf_dics(epochs, fwd_surf, None, tmin, tmax, tstep, win_lengths, mode='fourier', freq_bins=None) with pytest.raises(ValueError, match='cwt_morlet.*frequencies" param'): tf_dics(epochs, fwd_surf, None, tmin, tmax, tstep, win_lengths, mode='cwt_morlet', frequencies=None) # Test if incorrect number of noise CSDs is detected with pytest.raises(ValueError, match='One noise CSD object expected per'): tf_dics(epochs, fwd_surf, [noise_csds[0]], tmin, tmax, tstep, win_lengths, freq_bins=freq_bins) # Test if freq_bins and win_lengths incompatibility is detected with pytest.raises(ValueError, match='One time window length expected'): tf_dics(epochs, fwd_surf, None, tmin, tmax, tstep, win_lengths=[0, 1, 2], freq_bins=freq_bins) # Test if time step exceeding window lengths is detected with pytest.raises(ValueError, match='Time step should not be larger'): tf_dics(epochs, fwd_surf, None, tmin, tmax, tstep=0.15, win_lengths=[0.2, 0.1], freq_bins=freq_bins) # Test if incorrect number of n_ffts is detected with pytest.raises(ValueError, match='When specifying number of FFT'): tf_dics(epochs, fwd_surf, None, tmin, tmax, tstep, win_lengths, freq_bins=freq_bins, n_ffts=[1]) # Test if incorrect number of mt_bandwidths is detected with pytest.raises(ValueError, match='When using multitaper mode and'): tf_dics(epochs, fwd_surf, None, tmin, tmax, tstep, win_lengths=win_lengths, freq_bins=freq_bins, mode='multitaper', mt_bandwidths=[20]) # Test if subtracting evoked responses yields NaN's, since we only have one # epoch. Suppress division warnings. assert len(epochs) == 1, len(epochs) with np.errstate(invalid='ignore'): stcs = tf_dics(epochs, fwd_surf, None, tmin, tmax, tstep, win_lengths, mode='cwt_morlet', frequencies=frequencies, subtract_evoked=True, reg=reg, label=label, decim=20) assert np.all(np.isnan(stcs[0].data))
# approaches here: # # 1. The approach as described in [2]_, which first normalizes the forward # solution and computes a vector beamformer. # 2. The scalar beamforming approach based on [3]_, which uses weight # normalization instead of normalizing the forward solution. # Estimate the cross-spectral density (CSD) matrix on the trial containing the # signal. csd_signal = csd_morlet(epochs['signal'], frequencies=[10]) # Compute the spatial filters for each vertex, using two approaches. filters_approach1 = make_dics(info, fwd, csd_signal, reg=0.05, pick_ori='max-power', normalize_fwd=True, inversion='single', weight_norm=None) print(filters_approach1) filters_approach2 = make_dics(info, fwd, csd_signal, reg=0.1, pick_ori='max-power', normalize_fwd=False, inversion='matrix', weight_norm='unit-noise-gain') print(filters_approach2)
event_id, tmin, tmax = 1, -0.2, 0.5 events = mne.read_events(event_fname) epochs = mne.Epochs(raw, events, event_id, tmin, tmax, proj=True, picks=picks, baseline=(None, 0), preload=True, reject=dict(grad=4000e-13, mag=4e-12)) evoked = epochs.average() # Read forward operator forward = mne.read_forward_solution(fname_fwd) ############################################################################### # Computing the cross-spectral density matrix at 4 evenly spaced frequencies # from 6 to 10 Hz. We use a decim value of 20 to speed up the computation in # this example at the loss of accuracy. # # .. warning:: The use of several sensor types with the DICS beamformer is # not heavily tested yet. Here we use verbose='error' to # suppress a warning along these lines. csd = csd_morlet(epochs, tmin=0, tmax=0.5, decim=20, frequencies=np.linspace(6, 10, 4), n_cycles=2.5) # short signals, must live with few cycles # Compute DICS spatial filter and estimate source power. filters = make_dics(epochs.info, forward, csd, reg=0.5, verbose='error') print(filters) stc, freqs = apply_dics_csd(csd, filters) message = 'DICS source power in the 8-12 Hz frequency band' brain = stc.plot(surface='inflated', hemi='rh', subjects_dir=subjects_dir, time_label=message)
def test_make_dics(): """Test making DICS beamformer filters.""" # We only test proper handling of parameters here. Testing the results is # done in apply_dics_timeseries and apply_dics_csd. fwd_free, fwd_surf, fwd_fixed, fwd_vol, label = _load_forward() epochs, _, csd, _ = _simulate_data(fwd_fixed) raises(ValueError, make_dics, epochs.info, fwd_fixed, csd, pick_ori="notexistent") # Test if fixed forward operator is detected when picking normal # orientation raises(ValueError, make_dics, epochs.info, fwd_fixed, csd, pick_ori="normal") # Test if non-surface oriented forward operator is detected when picking # normal orientation raises(ValueError, make_dics, epochs.info, fwd_free, csd, pick_ori="normal") # Test if volume forward operator is detected when picking normal # orientation raises(ValueError, make_dics, epochs.info, fwd_vol, csd, pick_ori="normal") # Test invalid combinations of parameters raises(NotImplementedError, make_dics, epochs.info, fwd_free, csd, reduce_rank=True, pick_ori=None) raises(NotImplementedError, make_dics, epochs.info, fwd_free, csd, reduce_rank=True, pick_ori='max-power', inversion='single') # Sanity checks on the returned filters n_freq = len(csd.frequencies) vertices = np.intersect1d(label.vertices, fwd_free['src'][0]['vertno']) n_verts = len(vertices) n_orient = 3 n_channels = csd.n_channels # Test return values filters = make_dics(epochs.info, fwd_surf, csd, label=label, pick_ori=None, weight_norm='unit-noise-gain') assert filters['weights'].shape == (n_freq, n_verts * n_orient, n_channels) assert np.iscomplexobj(filters['weights']) assert filters['csd'] == csd assert filters['ch_names'] == csd.ch_names assert_array_equal(filters['proj'], np.eye(n_channels)) assert_array_equal(filters['vertices'][0], vertices) assert_array_equal(filters['vertices'][1], []) # Label was on the LH assert filters['subject'] == fwd_free['src'][0]['subject_his_id'] assert filters['pick_ori'] is None assert filters['n_orient'] == n_orient assert filters['inversion'] == 'single' assert filters['normalize_fwd'] assert filters['weight_norm'] == 'unit-noise-gain' _test_weight_norm(filters) # Test picking orientations. Also test weight norming under these different # conditions. filters = make_dics(epochs.info, fwd_surf, csd, label=label, pick_ori='normal', weight_norm='unit-noise-gain') n_orient = 1 assert filters['weights'].shape == (n_freq, n_verts * n_orient, n_channels) assert filters['n_orient'] == n_orient _test_weight_norm(filters) filters = make_dics(epochs.info, fwd_surf, csd, label=label, pick_ori='max-power', weight_norm='unit-noise-gain') n_orient = 1 assert filters['weights'].shape == (n_freq, n_verts * n_orient, n_channels) assert filters['n_orient'] == n_orient _test_weight_norm(filters) # Test using a real-valued filter filters = make_dics(epochs.info, fwd_surf, csd, label=label, pick_ori='normal', real_filter=True) assert not np.iscomplexobj(filters['weights']) # Test forward normalization. When inversion='single', the power of a # unit-noise CSD should be 1, even without weight normalization. csd_noise = csd.copy() inds = np.triu_indices(csd.n_channels) # Using [:, :] syntax for in-place broadcasting csd_noise._data[:, :] = np.eye(csd.n_channels)[inds][:, np.newaxis] filters = make_dics(epochs.info, fwd_surf, csd_noise, label=label, weight_norm=None, normalize_fwd=True) w = filters['weights'][0][:3] assert_allclose(np.diag(w.dot(w.T)), 1.0, rtol=1e-6, atol=0) # Test turning off both forward and weight normalization filters = make_dics(epochs.info, fwd_surf, csd_noise, label=label, weight_norm=None, normalize_fwd=False) w = filters['weights'][0][:3] assert not np.allclose(np.diag(w.dot(w.T)), 1.0, rtol=1e-2, atol=0) # Test whether spatial filter contains src_type assert 'src_type' in filters
def test_make_dics(): """Test making DICS beamformer filters.""" # We only test proper handling of parameters here. Testing the results is # done in apply_dics_timeseries and apply_dics_csd. fwd_free, fwd_surf, fwd_fixed, fwd_vol, label = _load_forward() epochs, _, csd, _ = _simulate_data(fwd_fixed) raises(ValueError, make_dics, epochs.info, fwd_fixed, csd, pick_ori="notexistent") # Test if fixed forward operator is detected when picking normal # orientation raises(ValueError, make_dics, epochs.info, fwd_fixed, csd, pick_ori="normal") # Test if non-surface oriented forward operator is detected when picking # normal orientation raises(ValueError, make_dics, epochs.info, fwd_free, csd, pick_ori="normal") # Test if volume forward operator is detected when picking normal # orientation raises(ValueError, make_dics, epochs.info, fwd_vol, csd, pick_ori="normal") # Test invalid combinations of parameters raises(NotImplementedError, make_dics, epochs.info, fwd_free, csd, reduce_rank=True, pick_ori=None) raises(NotImplementedError, make_dics, epochs.info, fwd_free, csd, reduce_rank=True, pick_ori='max-power', inversion='single') # Sanity checks on the returned filters n_freq = len(csd.frequencies) vertices = np.intersect1d(label.vertices, fwd_free['src'][0]['vertno']) n_verts = len(vertices) n_orient = 3 n_channels = csd.n_channels # Test return values filters = make_dics(epochs.info, fwd_surf, csd, label=label, pick_ori=None, weight_norm='unit-noise-gain') assert filters['weights'].shape == (n_freq, n_verts * n_orient, n_channels) assert np.iscomplexobj(filters['weights']) assert filters['csd'] == csd assert filters['ch_names'] == csd.ch_names assert_array_equal(filters['proj'], np.eye(n_channels)) assert_array_equal(filters['vertices'][0], vertices) assert_array_equal(filters['vertices'][1], []) # Label was on the LH assert filters['subject'] == fwd_free['src'][0]['subject_his_id'] assert filters['pick_ori'] is None assert filters['n_orient'] == n_orient assert filters['inversion'] == 'single' assert filters['normalize_fwd'] assert filters['weight_norm'] == 'unit-noise-gain' _test_weight_norm(filters) # Test picking orientations. Also test weight norming under these different # conditions. filters = make_dics(epochs.info, fwd_surf, csd, label=label, pick_ori='normal', weight_norm='unit-noise-gain') n_orient = 1 assert filters['weights'].shape == (n_freq, n_verts * n_orient, n_channels) assert filters['n_orient'] == n_orient _test_weight_norm(filters) filters = make_dics(epochs.info, fwd_surf, csd, label=label, pick_ori='max-power', weight_norm='unit-noise-gain') n_orient = 1 assert filters['weights'].shape == (n_freq, n_verts * n_orient, n_channels) assert filters['n_orient'] == n_orient _test_weight_norm(filters) # Test using a real-valued filter filters = make_dics(epochs.info, fwd_surf, csd, label=label, pick_ori='normal', real_filter=True) assert not np.iscomplexobj(filters['weights']) # Test forward normalization. When inversion='single', the power of a # unit-noise CSD should be 1, even without weight normalization. csd_noise = csd.copy() inds = np.triu_indices(csd.n_channels) # Using [:, :] syntax for in-place broadcasting csd_noise._data[:, :] = np.eye(csd.n_channels)[inds][:, np.newaxis] filters = make_dics(epochs.info, fwd_surf, csd_noise, label=label, weight_norm=None, normalize_fwd=True) w = filters['weights'][0][:3] assert_allclose(np.diag(w.dot(w.T)), 1.0, rtol=1e-6, atol=0) # Test turning off both forward and weight normalization filters = make_dics(epochs.info, fwd_surf, csd_noise, label=label, weight_norm=None, normalize_fwd=False) w = filters['weights'][0][:3] assert not np.allclose(np.diag(w.dot(w.T)), 1.0, rtol=1e-2, atol=0)
def test_make_dics(tmpdir, _load_forward, idx, whiten): """Test making DICS beamformer filters.""" # We only test proper handling of parameters here. Testing the results is # done in test_apply_dics_timeseries and test_apply_dics_csd. fwd_free, fwd_surf, fwd_fixed, fwd_vol = _load_forward epochs, _, csd, _, label, vertices, source_ind = \ _simulate_data(fwd_fixed, idx) with pytest.raises(ValueError, match='several sensor types'): make_dics(epochs.info, fwd_surf, csd, label=label, pick_ori=None) if whiten: noise_csd, rank = _make_rand_csd(epochs.info, csd) assert rank == len(epochs.info['ch_names']) == 62 else: noise_csd = None epochs.pick_types(meg='grad') with pytest.raises(ValueError, match="Invalid value for the 'pick_ori'"): make_dics(epochs.info, fwd_fixed, csd, pick_ori="notexistent", noise_csd=noise_csd) with pytest.raises(ValueError, match='rank, if str'): make_dics(epochs.info, fwd_fixed, csd, rank='foo', noise_csd=noise_csd) with pytest.raises(TypeError, match='rank must be'): make_dics(epochs.info, fwd_fixed, csd, rank=1., noise_csd=noise_csd) # Test if fixed forward operator is detected when picking normal # orientation with pytest.raises(ValueError, match='forward operator with free ori'): make_dics(epochs.info, fwd_fixed, csd, pick_ori="normal", noise_csd=noise_csd) # Test if non-surface oriented forward operator is detected when picking # normal orientation with pytest.raises(ValueError, match='oriented in surface coordinates'): make_dics(epochs.info, fwd_free, csd, pick_ori="normal", noise_csd=noise_csd) # Test if volume forward operator is detected when picking normal # orientation with pytest.raises(ValueError, match='oriented in surface coordinates'): make_dics(epochs.info, fwd_vol, csd, pick_ori="normal", noise_csd=noise_csd) # Test invalid combinations of parameters with pytest.raises(ValueError, match='reduce_rank cannot be used with'): make_dics(epochs.info, fwd_free, csd, inversion='single', reduce_rank=True, noise_csd=noise_csd) # TODO: Restore this? # with pytest.raises(ValueError, match='not stable with depth'): # make_dics(epochs.info, fwd_free, csd, weight_norm='unit-noise-gain', # inversion='single', depth=None) # Sanity checks on the returned filters n_freq = len(csd.frequencies) vertices = np.intersect1d(label.vertices, fwd_free['src'][0]['vertno']) n_verts = len(vertices) n_orient = 3 n_channels = len(epochs.ch_names) # Test return values weight_norm = 'unit-noise-gain' inversion = 'single' filters = make_dics(epochs.info, fwd_surf, csd, label=label, pick_ori=None, weight_norm=weight_norm, depth=None, real_filter=False, noise_csd=noise_csd, inversion=inversion) assert filters['weights'].shape == (n_freq, n_verts * n_orient, n_channels) assert np.iscomplexobj(filters['weights']) assert filters['csd'].ch_names == epochs.ch_names assert isinstance(filters['csd'], CrossSpectralDensity) assert filters['ch_names'] == epochs.ch_names assert_array_equal(filters['proj'], np.eye(n_channels)) assert_array_equal(filters['vertices'][0], vertices) assert_array_equal(filters['vertices'][1], []) # Label was on the LH assert filters['subject'] == fwd_free['src']._subject assert filters['pick_ori'] is None assert filters['is_free_ori'] assert filters['inversion'] == inversion assert filters['weight_norm'] == weight_norm assert 'DICS' in repr(filters) assert 'subject "sample"' in repr(filters) assert str(len(vertices)) in repr(filters) assert str(n_channels) in repr(filters) assert 'rank' not in repr(filters) _, noise_cov = _prepare_noise_csd(csd, noise_csd, real_filter=False) _, _, _, _, G, _, _, _ = _prepare_beamformer_input( epochs.info, fwd_surf, label, 'vector', combine_xyz=False, exp=None, noise_cov=noise_cov) G.shape = (n_channels, n_verts, n_orient) G = G.transpose(1, 2, 0).conj() # verts, orient, ch _assert_weight_norm(filters, G) inversion = 'matrix' filters = make_dics(epochs.info, fwd_surf, csd, label=label, pick_ori=None, weight_norm=weight_norm, depth=None, noise_csd=noise_csd, inversion=inversion) _assert_weight_norm(filters, G) weight_norm = 'unit-noise-gain-invariant' inversion = 'single' filters = make_dics(epochs.info, fwd_surf, csd, label=label, pick_ori=None, weight_norm=weight_norm, depth=None, noise_csd=noise_csd, inversion=inversion) _assert_weight_norm(filters, G) # Test picking orientations. Also test weight norming under these different # conditions. weight_norm = 'unit-noise-gain' filters = make_dics(epochs.info, fwd_surf, csd, label=label, pick_ori='normal', weight_norm=weight_norm, depth=None, noise_csd=noise_csd, inversion=inversion) n_orient = 1 assert filters['weights'].shape == (n_freq, n_verts * n_orient, n_channels) assert not filters['is_free_ori'] _assert_weight_norm(filters, G) filters = make_dics(epochs.info, fwd_surf, csd, label=label, pick_ori='max-power', weight_norm=weight_norm, depth=None, noise_csd=noise_csd, inversion=inversion) n_orient = 1 assert filters['weights'].shape == (n_freq, n_verts * n_orient, n_channels) assert not filters['is_free_ori'] _assert_weight_norm(filters, G) # From here on, only work on a single frequency csd = csd[0] # Test using a real-valued filter filters = make_dics(epochs.info, fwd_surf, csd, label=label, pick_ori='normal', real_filter=True, noise_csd=noise_csd) assert not np.iscomplexobj(filters['weights']) # Test forward normalization. When inversion='single', the power of a # unit-noise CSD should be 1, even without weight normalization. if not whiten: csd_noise = csd.copy() inds = np.triu_indices(csd.n_channels) # Using [:, :] syntax for in-place broadcasting csd_noise._data[:, :] = np.eye(csd.n_channels)[inds][:, np.newaxis] filters = make_dics(epochs.info, fwd_surf, csd_noise, label=label, weight_norm=None, depth=1., noise_csd=noise_csd, inversion='single') w = filters['weights'][0][:3] assert_allclose(np.diag(w.dot(w.conjugate().T)), 1.0, rtol=1e-6, atol=0) # Test turning off both forward and weight normalization filters = make_dics(epochs.info, fwd_surf, csd, label=label, weight_norm=None, depth=None, noise_csd=noise_csd) w = filters['weights'][0][:3] assert not np.allclose(np.diag(w.dot(w.conjugate().T)), 1.0, rtol=1e-2, atol=0) # Test neural-activity-index weight normalization. It should be a scaled # version of the unit-noise-gain beamformer. filters_nai = make_dics( epochs.info, fwd_surf, csd, label=label, pick_ori='max-power', weight_norm='nai', depth=None, noise_csd=noise_csd) w_nai = filters_nai['weights'][0] filters_ung = make_dics( epochs.info, fwd_surf, csd, label=label, pick_ori='max-power', weight_norm='unit-noise-gain', depth=None, noise_csd=noise_csd) w_ung = filters_ung['weights'][0] assert_allclose(np.corrcoef(np.abs(w_nai).ravel(), np.abs(w_ung).ravel()), 1, atol=1e-7) # Test whether spatial filter contains src_type assert 'src_type' in filters fname = op.join(str(tmpdir), 'filters-dics.h5') filters.save(fname) filters_read = read_beamformer(fname) assert isinstance(filters, Beamformer) assert isinstance(filters_read, Beamformer) for key in ['tmin', 'tmax']: # deal with strictness of object_diff setattr(filters['csd'], key, np.float64(getattr(filters['csd'], key))) assert object_diff(filters, filters_read) == ''
# log scale, from 12 to 30 Hz. freqs = np.logspace(np.log10(12), np.log10(30), 9) ############################################################################### # Computing the cross-spectral density matrix for the beta frequency band, for # different time intervals. We use a decim value of 20 to speed up the # computation in this example at the loss of accuracy. csd = csd_morlet(epochs, freqs, tmin=-1, tmax=1.5, decim=20) csd_baseline = csd_morlet(epochs, freqs, tmin=-1, tmax=0, decim=20) # ERS activity starts at 0.5 seconds after stimulus onset csd_ers = csd_morlet(epochs, freqs, tmin=0.5, tmax=1.5, decim=20) ############################################################################### # Computing DICS spatial filters using the CSD that was computed on the entire # timecourse. filters = make_dics(epochs.info, fwd, csd.mean(), pick_ori='max-power') ############################################################################### # Applying DICS spatial filters separately to the CSD computed using the # baseline and the CSD computed during the ERS activity. baseline_source_power, freqs = apply_dics_csd(csd_baseline.mean(), filters) beta_source_power, freqs = apply_dics_csd(csd_ers.mean(), filters) ############################################################################### # Visualizing source power during ERS activity relative to the baseline power. stc = beta_source_power / baseline_source_power message = 'DICS source power in the 12-30 Hz frequency band' brain = stc.plot(hemi='both', views='par', subjects_dir=subjects_dir, subject=subject,
def test_apply_dics_timeseries(_load_forward, idx): """Test DICS applied to timeseries data.""" fwd_free, fwd_surf, fwd_fixed, fwd_vol = _load_forward epochs, evoked, csd, source_vertno, label, vertices, source_ind = \ _simulate_data(fwd_fixed, idx) reg = 5 # Lots of regularization for our toy dataset with pytest.raises(ValueError, match='several sensor types'): make_dics(evoked.info, fwd_surf, csd) evoked.pick_types(meg='grad') multiple_filters = make_dics(evoked.info, fwd_surf, csd, label=label, reg=reg) # Sanity checks on the resulting STC after applying DICS on evoked stcs = apply_dics(evoked, multiple_filters) assert isinstance(stcs, list) assert len(stcs) == len(multiple_filters['weights']) assert_array_equal(stcs[0].vertices[0], multiple_filters['vertices'][0]) assert_array_equal(stcs[0].vertices[1], multiple_filters['vertices'][1]) assert_allclose(stcs[0].times, evoked.times) # Applying filters for multiple frequencies on epoch data should fail with pytest.raises(ValueError, match='computed for a single frequency'): apply_dics_epochs(epochs, multiple_filters) # From now on, only apply filters with a single frequency (20 Hz). csd20 = csd.pick_frequency(20) filters = make_dics(evoked.info, fwd_surf, csd20, label=label, reg=reg, inversion='single') # Sanity checks on the resulting STC after applying DICS on epochs. # Also test here that no warnings are thrown - implemented to check whether # src should not be None warning occurs with pytest.warns(None) as w: stcs = apply_dics_epochs(epochs, filters) assert len(w) == 0 assert isinstance(stcs, list) assert len(stcs) == 1 assert_array_equal(stcs[0].vertices[0], filters['vertices'][0]) assert_array_equal(stcs[0].vertices[1], filters['vertices'][1]) assert_allclose(stcs[0].times, epochs.times) # Did we find the source? stc = (stcs[0] ** 2).mean() dist = _fwd_dist(stc, fwd_surf, vertices, source_ind, tidx=0) assert dist == 0 # Apply filters to evoked stc = apply_dics(evoked, filters) stc = (stc ** 2).mean() dist = _fwd_dist(stc, fwd_surf, vertices, source_ind, tidx=0) assert dist == 0 # Test if wrong channel selection is detected in application of filter evoked_ch = cp.deepcopy(evoked) evoked_ch.pick_channels(evoked_ch.ch_names[:-1]) with pytest.raises(ValueError, match='MEG 2633 which is not present'): apply_dics(evoked_ch, filters) # Test whether projections are applied, by adding a custom projection filters_noproj = make_dics(evoked.info, fwd_surf, csd20, label=label) stc_noproj = apply_dics(evoked, filters_noproj) evoked_proj = evoked.copy() p = compute_proj_evoked(evoked_proj, n_grad=1, n_mag=0, n_eeg=0) proj_matrix = make_projector(p, evoked_proj.ch_names)[0] evoked_proj.info['projs'] += p filters_proj = make_dics(evoked_proj.info, fwd_surf, csd20, label=label) assert_array_equal(filters_proj['proj'], proj_matrix) stc_proj = apply_dics(evoked_proj, filters_proj) assert np.any(np.not_equal(stc_noproj.data, stc_proj.data)) # Test detecting incompatible projections filters_proj['proj'] = filters_proj['proj'][:-1, :-1] with pytest.raises(ValueError, match='operands could not be broadcast'): apply_dics(evoked_proj, filters_proj) # Test returning a generator stcs = apply_dics_epochs(epochs, filters, return_generator=False) stcs_gen = apply_dics_epochs(epochs, filters, return_generator=True) assert_array_equal(stcs[0].data, next(stcs_gen).data) # Test computing timecourses on a volume source space filters_vol = make_dics(evoked.info, fwd_vol, csd20, reg=reg, inversion='single') stc = apply_dics(evoked, filters_vol) stc = (stc ** 2).mean() assert stc.data.shape[1] == 1 vol_source_ind = _nearest_vol_ind(fwd_vol, fwd_surf, vertices, source_ind) dist = _fwd_dist(stc, fwd_vol, fwd_vol['src'][0]['vertno'], vol_source_ind, tidx=0) vol_tols = {100: 0.008, 200: 0.015} vol_tol = vol_tols.get(idx, 0.) assert dist <= vol_tol # check whether a filters object without src_type throws expected warning del filters_vol['src_type'] # emulate 0.16 behaviour to cause warning with pytest.warns(RuntimeWarning, match='filter does not contain src_typ'): apply_dics_epochs(epochs, filters_vol)
# over the way the filter weights are computed. Currently, there is no clear # consensus regarding the best approach. This is why we will demonstrate two # approaches here: # # 1. The approach as described in [2]_, which first normalizes the forward # solution and computes a vector beamformer. # 2. The scalar beamforming approach based on [3]_, which uses weight # normalization instead of normalizing the forward solution. # Estimate the cross-spectral density (CSD) matrix on the trial containing the # signal. csd_signal = csd_morlet(epochs['signal'], frequencies=[10]) # Compute the spatial filters for each vertex, using two approaches. filters_approach1 = make_dics( info, fwd, csd_signal, reg=0.05, pick_ori='max-power', normalize_fwd=True, inversion='single', weight_norm=None) filters_approach2 = make_dics( info, fwd, csd_signal, reg=0.05, pick_ori='max-power', normalize_fwd=False, inversion='matrix', weight_norm='unit-noise-gain') # Compute the DICS power map by applying the spatial filters to the CSD matrix. power_approach1, f = apply_dics_csd(csd_signal, filters_approach1) power_approach2, f = apply_dics_csd(csd_signal, filters_approach2) # Plot the DICS power maps for both approaches. for approach, power in enumerate([power_approach1, power_approach2], 1): brain = power.plot('sample', subjects_dir=subjects_dir, hemi='both', figure=approach + 1, size=600) # Indicate the true locations of the source activity on the plot.
def test_apply_dics_csd(): """Test applying a DICS beamformer to a CSD matrix.""" fwd_free, fwd_surf, fwd_fixed, fwd_vol, label = _load_forward() epochs, _, csd, source_vertno = _simulate_data(fwd_fixed) vertices = np.intersect1d(label.vertices, fwd_free['src'][0]['vertno']) source_ind = vertices.tolist().index(source_vertno) reg = 1 # Lots of regularization for our toy dataset # Construct an identity "noise" CSD, which we will use to test the # 'unit-noise-gain' setting. csd_noise = csd.copy() inds = np.triu_indices(csd.n_channels) # Using [:, :] syntax for in-place broadcasting csd_noise._data[:, :] = np.eye(csd.n_channels)[inds][:, np.newaxis] # Try different types of forward models for fwd in [fwd_free, fwd_surf, fwd_fixed]: filters = make_dics(epochs.info, fwd, csd, label=label, reg=reg, inversion='single') power, f = apply_dics_csd(csd, filters) assert f == [10, 20] # Did we find the true source at 20 Hz? assert np.argmax(power.data[:, 1]) == source_ind # Is the signal stronger at 20 Hz than 10? assert power.data[source_ind, 1] > power.data[source_ind, 0] # Try picking different orientations and inversion modes for pick_ori in [None, 'normal', 'max-power']: for inversion in ['single', 'matrix']: # Matrix inversion mode needs more regularization for this toy # dataset. if inversion == 'matrix': reg_ = 5 else: reg_ = reg filters = make_dics(epochs.info, fwd_surf, csd, label=label, reg=reg_, pick_ori=pick_ori, inversion=inversion, weight_norm='unit-noise-gain') power, f = apply_dics_csd(csd, filters) assert f == [10, 20] assert np.argmax(power.data[:, 1]) == source_ind assert power.data[source_ind, 1] > power.data[source_ind, 0] # Test unit-noise-gain weighting noise_power, f = apply_dics_csd(csd_noise, filters) assert np.allclose(noise_power.data, 1) # Test filter with forward normalization instead of weight # normalization filters = make_dics(epochs.info, fwd_surf, csd, label=label, reg=reg_, pick_ori=pick_ori, inversion=inversion, weight_norm=None, normalize_fwd=True) power, f = apply_dics_csd(csd, filters) assert f == [10, 20] assert np.argmax(power.data[:, 1]) == source_ind assert power.data[source_ind, 1] > power.data[source_ind, 0] # Test using a real-valued filter filters_real = make_dics(epochs.info, fwd_surf, csd, label=label, reg=reg, real_filter=True) # Also test here that no warings are thrown - implemented to check whether # src should not be None warning occurs: with pytest.warns(None) as w: power, f = apply_dics_csd(csd, filters_real) assert len(w) == 0 assert f == [10, 20] assert np.argmax(power.data[:, 1]) == source_ind assert power.data[source_ind, 1] > power.data[source_ind, 0] # Test rank reduction filters_real = make_dics(epochs.info, fwd_surf, csd, label=label, reg=5, pick_ori='max-power', inversion='matrix', reduce_rank=True) power, f = apply_dics_csd(csd, filters_real) assert f == [10, 20] assert np.argmax(power.data[:, 1]) == source_ind assert power.data[source_ind, 1] > power.data[source_ind, 0] # Test computing source power on a volume source space filters_vol = make_dics(epochs.info, fwd_vol, csd, reg=reg) power, f = apply_dics_csd(csd, filters_vol) vol_source_ind = 3851 # FIXME: not make this hardcoded assert f == [10, 20] assert np.argmax(power.data[:, 1]) == vol_source_ind assert power.data[vol_source_ind, 1] > power.data[vol_source_ind, 0] # check whether a filters object without src_type throws expected warning del filters_vol['src_type'] # emulate 0.16 behaviour to cause warning with pytest.warns(RuntimeWarning, match='spatial filter does not contain ' 'src_type'): apply_dics_csd(csd, filters_vol)
# ERS activity starts at 0.5 seconds after stimulus onset csd_ers = csd_morlet(epochs, freqs, tmin=0.5, tmax=1.5, decim=20) ############################################################################### # To compute the source power for a frequency band, rather than each frequency # separately, we average the CSD objects across frequencies. csd = csd.mean() csd_baseline = csd_baseline.mean() csd_ers = csd_ers.mean() ############################################################################### # Computing DICS spatial filters using the CSD that was computed on the entire # timecourse. filters = make_dics(epochs.info, fwd, csd, noise_csd=csd_baseline, pick_ori='max-power') ############################################################################### # Applying DICS spatial filters separately to the CSD computed using the # baseline and the CSD computed during the ERS activity. baseline_source_power, freqs = apply_dics_csd(csd_baseline, filters) beta_source_power, freqs = apply_dics_csd(csd_ers, filters) ############################################################################### # Visualizing source power during ERS activity relative to the baseline power. stc = beta_source_power / baseline_source_power message = 'DICS source power in the 12-30 Hz frequency band' brain = stc.plot(hemi='both', views='par',
def test_tf_dics(): """Test 5D time-frequency beamforming based on DICS.""" fwd_free, fwd_surf, fwd_fixed, fwd_vol, label = _load_forward() epochs, evoked, _, source_vertno = _simulate_data(fwd_fixed) vertices = np.intersect1d(label.vertices, fwd_free['src'][0]['vertno']) source_ind = vertices.tolist().index(source_vertno) reg = 1 # Lots of regularization for our toy dataset tmin = 0 tmax = 9 tstep = 4 win_lengths = [5, 5] frequencies = [10, 20] freq_bins = [(8, 12), (18, 22)] # Compute DICS for two time windows and two frequencies for mode in ['fourier', 'multitaper', 'cwt_morlet']: stcs = tf_dics(epochs, fwd_surf, None, tmin, tmax, tstep, win_lengths, mode=mode, freq_bins=freq_bins, frequencies=frequencies, decim=10, reg=reg, label=label) # Did we find the true source at 20 Hz? assert np.argmax(stcs[1].data[:, 0]) == source_ind assert np.argmax(stcs[1].data[:, 1]) == source_ind # 20 Hz power should decrease over time assert stcs[1].data[source_ind, 0] > stcs[1].data[source_ind, 1] # 20 Hz power should be more than 10 Hz power at the true source assert stcs[1].data[source_ind, 0] > stcs[0].data[source_ind, 0] # Manually compute source power and compare with the last tf_dics result. source_power = [] time_windows = [(0, 5), (4, 9)] for time_window in time_windows: csd = csd_morlet(epochs, frequencies=[frequencies[1]], tmin=time_window[0], tmax=time_window[1], decim=10) csd = csd.sum() csd._data /= csd.n_fft filters = make_dics(epochs.info, fwd_surf, csd, reg=reg, label=label) stc_source_power, _ = apply_dics_csd(csd, filters) source_power.append(stc_source_power.data) # Comparing tf_dics results with dics_source_power results assert_allclose(stcs[1].data, np.array(source_power).squeeze().T, atol=0) # Test using noise csds. We're going to use identity matrices. That way, # since we're using unit-noise-gain weight normalization, there should be # no effect. stcs = tf_dics(epochs, fwd_surf, None, tmin, tmax, tstep, win_lengths, mode='cwt_morlet', frequencies=frequencies, decim=10, reg=reg, label=label, normalize_fwd=False, weight_norm='unit-noise-gain') noise_csd = csd.copy() inds = np.triu_indices(csd.n_channels) # Using [:, :] syntax for in-place broadcasting noise_csd._data[:, :] = 2 * np.eye(csd.n_channels)[inds][:, np.newaxis] noise_csd.n_fft = 2 # Dividing by n_fft should yield an identity CSD noise_csds = [noise_csd, noise_csd] # Two frequency bins stcs_norm = tf_dics(epochs, fwd_surf, noise_csds, tmin, tmax, tstep, win_lengths, mode='cwt_morlet', frequencies=frequencies, decim=10, reg=reg, label=label, normalize_fwd=False, weight_norm='unit-noise-gain') assert_allclose(stcs_norm[0].data, stcs[0].data, atol=0) assert_allclose(stcs_norm[1].data, stcs[1].data, atol=0) # Test invalid parameter combinations raises(ValueError, tf_dics, epochs, fwd_surf, None, tmin, tmax, tstep, win_lengths, mode='fourier', freq_bins=None) raises(ValueError, tf_dics, epochs, fwd_surf, None, tmin, tmax, tstep, win_lengths, mode='cwt_morlet', frequencies=None) # Test if incorrect number of noise CSDs is detected raises(ValueError, tf_dics, epochs, fwd_surf, [noise_csds[0]], tmin, tmax, tstep, win_lengths, freq_bins=freq_bins) # Test if freq_bins and win_lengths incompatibility is detected raises(ValueError, tf_dics, epochs, fwd_surf, None, tmin, tmax, tstep, win_lengths=[0, 1, 2], freq_bins=freq_bins) # Test if time step exceeding window lengths is detected raises(ValueError, tf_dics, epochs, fwd_surf, None, tmin, tmax, tstep=0.15, win_lengths=[0.2, 0.1], freq_bins=freq_bins) # Test if incorrent number of n_ffts is detected raises(ValueError, tf_dics, epochs, fwd_surf, None, tmin, tmax, tstep, win_lengths, freq_bins=freq_bins, n_ffts=[1]) # Test if incorrect number of mt_bandwidths is detected raises(ValueError, tf_dics, epochs, fwd_surf, None, tmin, tmax, tstep, win_lengths=win_lengths, freq_bins=freq_bins, mode='multitaper', mt_bandwidths=[20]) # Test if subtracting evoked responses yields NaN's, since we only have one # epoch. Suppress division warnings. with pytest.warns(RuntimeWarning, match='[invalid|empty]'): stcs = tf_dics(epochs, fwd_surf, None, tmin, tmax, tstep, win_lengths, mode='cwt_morlet', frequencies=frequencies, subtract_evoked=True, reg=reg, label=label, decim=20) assert np.all(np.isnan(stcs[0].data))
# We are interested in the beta band. Define a range of frequencies, using a # log scale, from 12 to 30 Hz. freqs = np.logspace(np.log10(12), np.log10(30), 9) ############################################################################### # Computing the cross-spectral density matrix for the beta frequency band, for # different time intervals. We use a decim value of 20 to speed up the # computation in this example at the loss of accuracy. csd = csd_morlet(epochs, freqs, tmin=-1, tmax=1.5, decim=20) csd_baseline = csd_morlet(epochs, freqs, tmin=-1, tmax=0, decim=20) # ERS activity starts at 0.5 seconds after stimulus onset csd_ers = csd_morlet(epochs, freqs, tmin=0.5, tmax=1.5, decim=20) ############################################################################### # Computing DICS spatial filters using the CSD that was computed on the entire # timecourse. filters = make_dics(epochs.info, fwd, csd.mean(), pick_ori='max-power') ############################################################################### # Applying DICS spatial filters separately to the CSD computed using the # baseline and the CSD computed during the ERS activity. baseline_source_power, freqs = apply_dics_csd(csd_baseline.mean(), filters) beta_source_power, freqs = apply_dics_csd(csd_ers.mean(), filters) ############################################################################### # Visualizing source power during ERS activity relative to the baseline power. stc = beta_source_power / baseline_source_power message = 'DICS source power in the 12-30 Hz frequency band' brain = stc.plot(hemi='both', views='par', subjects_dir=subjects_dir, time_label=message)