Esempio n. 1
0
def test_sym_mat_to_vector():
    """Test converting between vectors and symmetric matrices."""
    mat = np.array([[0, 1, 2, 3],
                    [1, 4, 5, 6],
                    [2, 5, 7, 8],
                    [3, 6, 8, 9]])
    assert_array_equal(_sym_mat_to_vector(mat),
                       [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

    vec = np.arange(10)
    assert_array_equal(_vector_to_sym_mat(vec),
                       [[0, 1, 2, 3],
                        [1, 4, 5, 6],
                        [2, 5, 7, 8],
                        [3, 6, 8, 9]])

    # Test complex values: diagonals should be complex conjugates
    comp_vec = np.arange(3) + 1j
    assert_array_equal(_vector_to_sym_mat(comp_vec),
                       [[0. + 0.j, 1. + 1.j],
                        [1. - 1.j, 2. + 0.j]])

    # Test preservation of data type
    assert _sym_mat_to_vector(mat.astype(np.int8)).dtype == np.int8
    assert _vector_to_sym_mat(vec.astype(np.int8)).dtype == np.int8
    assert _sym_mat_to_vector(mat.astype(np.float16)).dtype == np.float16
    assert _vector_to_sym_mat(vec.astype(np.float16)).dtype == np.float16
Esempio n. 2
0
def test_sym_mat_to_vector():
    """Test converting between vectors and symmetric matrices."""
    mat = np.array([[0, 1, 2, 3],
                    [1, 4, 5, 6],
                    [2, 5, 7, 8],
                    [3, 6, 8, 9]])
    assert_array_equal(_sym_mat_to_vector(mat),
                       [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

    vec = np.arange(10)
    assert_array_equal(_vector_to_sym_mat(vec),
                       [[0, 1, 2, 3],
                        [1, 4, 5, 6],
                        [2, 5, 7, 8],
                        [3, 6, 8, 9]])

    # Test complex values: diagonals should be complex conjugates
    comp_vec = np.arange(3) + 1j
    assert_array_equal(_vector_to_sym_mat(comp_vec),
                       [[0. + 0.j,  1. + 1.j],
                        [1. - 1.j,  2. + 0.j]])

    # Test preservation of data type
    assert _sym_mat_to_vector(mat.astype(np.int8)).dtype == np.int8
    assert _vector_to_sym_mat(vec.astype(np.int8)).dtype == np.int8
    assert _sym_mat_to_vector(mat.astype(np.float16)).dtype == np.float16
    assert _vector_to_sym_mat(vec.astype(np.float16)).dtype == np.float16
Esempio n. 3
0
def _cov_as_csd(cov, info):
    rng = np.random.RandomState(0)
    assert cov['data'].ndim == 2
    assert len(cov['data']) == len(cov['names'])
    # we need to make this have at least some complex structure
    data = cov['data'] + 1e-1 * _rand_csd(rng, info)
    assert data.dtype == np.complex128
    return CrossSpectralDensity(_sym_mat_to_vector(data), cov['names'], 0., 16)
Esempio n. 4
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)
Esempio n. 5
0
def _make_rand_csd(info, csd):
    rng = np.random.RandomState(0)
    data = _rand_csd(rng, info)
    # now we need to have the same null space as the data csd
    s, u = np.linalg.eigh(csd.get_data(csd.frequencies[0]))
    mask = np.abs(s) >= s[-1] * 1e-7
    rank = mask.sum()
    assert rank == len(data) == len(info['ch_names'])
    noise_csd = CrossSpectralDensity(
        _sym_mat_to_vector(data), info['ch_names'], 0., csd.n_fft)
    return noise_csd, rank
Esempio n. 6
0
    def __init__(
        self,
        data,
        info,
        metadata=None,
        comment=None,
        method=None,
        fmin=None,
        fmax=None,
    ):
        if data.ndim != 3:
            raise ValueError("data should be 3d. Got %d." % data.ndim)
        n_channels, _, n_times = data.shape

        if n_channels != len(info["chs"]):
            raise ValueError("Number of channels and data size don't match"
                             " (%d != %d)." % (n_channels, len(info["chs"])))
        if fmin is not None and fmax is not None:
            if fmin > fmax:
                raise ValueError(f"'fmin' ({fmin}) should not be "
                                 f"greater then 'fmax' ({fmax})")

        self.data = data
        self.fmin = fmin
        self.fmax = fmax
        self.comment = comment
        self.method = method
        self.preload = True

        data = np.asarray(data)
        if data.ndim == 3:
            _data = []
            for itime in range(data.shape[-1]):
                _data.append(_sym_mat_to_vector(data[..., itime]))
            data = np.asarray(_data).T
        elif data.ndim != 3:
            raise ValueError("`data` should be a 3D array.")
        self._data = data

        ch_names = info["ch_names"]
        if len(ch_names) != _n_dims_from_triu(data.shape[0]):
            raise ValueError(
                "Number of ch_names does not match the number of "
                f"time series in the CSD matrix {_n_dims_from_triu(len(data))}."
            )
        self.info = ResultInfo(source_info=info, **metadata)
        self.times = np.arange(
            self.n_times) / self.metadata["model_params"]["stepsize"]
Esempio n. 7
0
def pick_channels_connectivity(csd,
                               include=[],
                               exclude=[],
                               ordered=False,
                               copy=True):
    """Pick channels from connectivity matrix.
    Parameters
    ----------
    csd : instance of ResultConnectivity
        The Result object to select the channels from.
    include : list of str
        List of channels to include (if empty, include all available).
    exclude : list of str
        Channels to exclude (if empty, do not exclude any).
    ordered : bool
        If True (default False), ensure that the order of the channels in the
        modified instance matches the order of ``include``.
        .. versionadded:: 0.20.0
    copy : bool
        If True (the default), return a copy of the CSD matrix with the
        modified channels. If False, channels are modified in-place.
        .. versionadded:: 0.20.0
    Returns
    -------
    res : instance of CrossSpectralDensity
        Cross-spectral density restricted to selected channels.
    """
    if copy:
        csd = csd.copy()

    sel = pick_channels(csd.ch_names,
                        include=include,
                        exclude=exclude,
                        ordered=ordered)
    data = []
    for vec in csd._data.T:
        mat = _vector_to_sym_mat(vec)
        mat = mat[sel, :][:, sel]
        data.append(_sym_mat_to_vector(mat))
    ch_names = [csd.ch_names[i] for i in sel]

    csd._data = np.array(data).T
    csd.ch_names = ch_names
    return csd
Esempio n. 8
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:
        rng = np.random.RandomState(0)
        scales = mne.make_ad_hoc_cov(epochs.info).data
        n = scales.size
        # Some random complex correlation structure (with channel scalings)
        data = rng.randn(n, n) + 1j * rng.randn(n, n)
        data = data @ data.conj().T
        data *= scales
        data *= scales[:, np.newaxis]
        data.flat[::n + 1] = scales
        noise_csd = CrossSpectralDensity(_sym_mat_to_vector(data),
                                         epochs.ch_names, 0., csd.n_fft)
    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,
                        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) == ''