function test_suite = test_meeg_source()
% tests for MEEG datasets in source space
%
% # For CoSMoMVPA's copyright information and license terms, #
% # see the COPYING file distributed with CoSMoMVPA. #
try % assignment of 'localfunctions' is necessary in Matlab >= 2016
test_functions = localfunctions();
catch % no problem; early Matlab versions can use initTestSuite fine
end
initTestSuite;
function test_meeg_dataset()
tps = {'freq_pow', 'time_mom', 'rpt_trial_mom', 'rpt_trial_mom_1d', ...
'time_mom_1d', ...
'rpt_trial_pow', 'strange_ft_lcmv_pow'};
for j = 1:numel(tps)
is_ft_strange_lcmv_avg_pow = isequal(cosmo_strsplit(tps{j}, '_', 1), ...
'strange');
[ft, fdim, data_label] = generate_ft_source(tps{j});
ds = cosmo_meeg_dataset(ft);
% check fdim
assertEqual(ds.a.fdim, fdim);
key = data_label{1};
sub_key = data_label{2};
ft_first_sample_all = ft.(key).(sub_key);
if is_ft_strange_lcmv_avg_pow
ft_first_sample = ft_first_sample_all(:, ft.inside);
else
ft_first_sample = ft_first_sample_all(ft.inside, :);
end
switch sub_key
case 'mom'
% choose a random sensor
inside_idxs = find(ft.inside);
pos = ceil(rand() * numel(inside_idxs));
inside_idx = inside_idxs(pos);
assertEqual(ds.samples(1, ds.fa.pos == inside_idx), ...
ft_first_sample{pos}(:)');
otherwise
assertEqual(ds.samples(1, :), ft_first_sample(:)');
end
[ft_arr, ft_labels, ft_values] = cosmo_unflatten(ds, 2, ...
'matrix_labels', 'pos');
if is_ft_strange_lcmv_avg_pow
ft_arr = ft_arr';
end
assertEqual(ft_arr(ft_arr ~= 0), ds.samples(:));
assertEqual(ft_labels, fdim.labels);
assertEqual(ft_values, fdim.values);
% select single element, and ensure it is the same in the
% fieldtrip struct as in the dataset struct
dim_sizes = cellfun(@(x)size(x, 2), fdim.values);
ndim = numel(dim_sizes);
rp = ceil(rand(1, ndim) .* dim_sizes(:)');
[nsamples, nfeatures] = size(ds.samples);
ds_msk = false(1, nfeatures);
ft_idx = cell(1, 1 + ndim);
ft_idx{1} = randperm(nsamples);
for k = 1:ndim
dim_label = fdim.labels{k};
ds_msk = ds_msk | rp(k) ~= ds.fa.(dim_label);
switch dim_label
case 'mom'
ft_idx{k + 1} = rp(k);
case 'pos'
ft_values = ft.(dim_label);
ft_idx{k + 1} = find(all(bsxfun(@eq, ...
ft_values(rp(k), :), ft_values), 2));
otherwise
ft_values = ft.(dim_label);
ft_idx{k + 1} = find(ft_values(rp(k)) == ft_values);
end
end
ds_sel = cosmo_slice(cosmo_slice(ds, ~ds_msk, 2), ft_idx{1});
if is_ft_strange_lcmv_avg_pow
ft_idx = ft_idx(2:end);
end
ft_sel = ft_arr(ft_idx{:});
if ft_sel == 0
assertTrue(isempty(ds_sel.samples));
else
assertEqual(ds_sel.samples, ft_sel);
end
% re-order features
nfeatures = size(ds.samples, 2);
ds2 = cosmo_slice(ds, randperm(nfeatures), 2);
ft2 = cosmo_map2meeg(ds2);
if is_ft_strange_lcmv_avg_pow
ft2.avg.pow = ft2.avg.pow';
ft2.time = ft.time;
end
assertEqual(ft, ft2);
if j == 1
% test compatibility with old fieldtrip
ft2.inside = find(ft2.inside);
ds3 = cosmo_meeg_dataset(ft2);
assertEqual(ds, ds3);
ft2.inside = struct();
assertExceptionThrown(@()cosmo_meeg_dataset(ft2), '');
end
end
function test_meeg_fmri_dataset()
ds = cosmo_synthetic_dataset('type', 'source');
ds_fmri = cosmo_fmri_dataset(ds);
ft = cosmo_map2meeg(ds);
ds_ft_fmri = cosmo_fmri_dataset(ft);
ds_vol = cosmo_vol_grid_convert(ds, 'tovol');
assertEqual(ds_vol, ds_fmri);
assertTrue(isempty(fieldnames(ds_ft_fmri.sa)));
ds_vol = rmfield(ds_vol, 'sa');
ds_ft_fmri = rmfield(ds_ft_fmri, 'sa');
assertEqual(ds_vol, ds_ft_fmri);
function test_irregular_source_grid()
ft = generate_ft_source('freq_pow');
ft.pos = ft.pos + randn(size(ft.pos));
ds = cosmo_meeg_dataset(ft);
ft2 = cosmo_map2meeg(ds);
assertEqual(ft, ft2);
function [ft, fdim, data_label] = generate_ft_source(tp)
ft = struct();
dim_pos_range = {-3:3, -4:4, -5:5};
nsamples = 2;
freq = [3 5 7 9];
time = [-1 0 1 2];
mom_labels_3d = {'x', 'y', 'z'};
mom_labels_1d = {'xyz'};
ft.dim = cellfun(@numel, dim_pos_range);
ft.pos = cosmo_cartprod(dim_pos_range);
ft.inside = sum(ft.pos.^2, 2) < 30;
fdim = struct();
switch tp
case 'freq_pow'
ft.freq = freq;
ft.method = 'average';
ft.avg = generate_data(ft.inside, numel(freq), 1, 'pow');
fdim.labels = {'pos'; 'freq'};
fdim.values = {ft.pos'; freq(:)'};
data_label = {'avg', 'pow'};
case 'time_mom'
ft.time = time;
ft.method = 'average';
ft.avg = generate_data(ft.inside, numel(time), 1, 'mom3d');
fdim.labels = {'pos'; 'mom'; 'time'};
fdim.values = {ft.pos'; mom_labels_3d; time(:)'};
data_label = {'avg', 'mom'};
case 'time_mom_1d'
ft.time = time;
ft.method = 'average';
ft.avg = generate_data(ft.inside, numel(time), 1, 'mom1d');
fdim.labels = {'pos'; 'mom'; 'time'};
fdim.values = {ft.pos'; mom_labels_1d; time(:)'};
data_label = {'avg', 'mom'};
case 'rpt_trial_pow'
ft.time = time;
ft.method = 'rawtrial';
ft.trial = generate_data(ft.inside, numel(time), nsamples, 'pow');
fdim.labels = {'pos'; 'time'};
fdim.values = {ft.pos'; time(:)'};
data_label = {'trial', 'pow'};
case 'rpt_trial_mom'
ft.time = time;
ft.method = 'rawtrial';
ft.trial = generate_data(ft.inside, numel(time), nsamples, 'mom3d');
fdim.labels = {'pos'; 'mom'; 'time'};
fdim.values = {ft.pos'; mom_labels_3d; time(:)'};
data_label = {'trial', 'mom'};
case 'rpt_trial_mom_1d'
ft.time = time;
ft.method = 'rawtrial';
ft.trial = generate_data(ft.inside, numel(time), nsamples, 'mom1d');
fdim.labels = {'pos'; 'mom'; 'time'};
fdim.values = {ft.pos'; mom_labels_1d; time(:)'};
data_label = {'trial', 'mom'};
case 'strange_ft_lcmv_pow'
% FT with LCMV source puts .avg data in a 1xNSOURCE matrix,
% even with NTIME>1 timepoints. In this case, the output
% dataset structure has the time points averaged.
ft.time = time;
ft.method = 'average';
ft.avg = generate_data(ft.inside, 1, 1, 'pow');
ft.avg.pow = ft.avg.pow';
fdim.labels = {'pos'; 'time'};
fdim.values = {ft.pos'; mean(time)};
data_label = {'avg', 'pow'};
otherwise
error('unsupported type %s', tp);
end
function d = generate_data(inside, nfeatures, nsamples, fld)
is_single_trial = nargin < 3;
if is_single_trial
nsamples = 1;
end
switch fld
case 'mom3d'
d = generate_mom(inside, nfeatures, nsamples, 3);
case 'mom1d'
d = generate_mom(inside, nfeatures, nsamples, 1);
case 'pow'
d = generate_pow(inside, nfeatures, nsamples);
otherwise
error('not supported: %s', fld);
end
function all_trials = generate_pow(inside, nfeatures, nsamples)
nf = numel(inside);
ni = sum(inside);
all_data = NaN(nf, nfeatures);
trial_data = cosmo_rand(ni, nfeatures);
all_trials_cell = cell(nsamples, 1);
for j = 1:nsamples
data = all_data;
data(inside, :) = trial_data + j;
all_trials_cell{j}.pow = data;
end
all_trials = cat(1, all_trials_cell{:})';
function all_trials = generate_mom(inside, nfeatures, nsamples, ndim)
nf = numel(inside);
i = find(inside);
ni = numel(i);
data = cosmo_rand(ndim, nfeatures, ni);
all_trials_cell = cell(1, nsamples);
for j = 1:nsamples
one_trial = cell(nf, 1);
for k = 1:ni
one_trial{i(k)} = data(:, :, k) + j;
end
all_trials_cell{j} = one_trial;
end
all_trials = struct('mom', all_trials_cell);