function test_suite = test_meeg_chan_neighbors()
% tests for cosmo_meeg_chan_neighbors
%
% # 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_neighbors()
if cosmo_skip_test_if_no_external('fieldtrip')
return
end
% switch off warnings by fieldtrip
warning_state = warning('query', 'all');
cleaner = onCleanup(@()warning(warning_state));
warning('off', 'all');
props = get_props();
n = numel(props);
% test a subset
ntest = round(n * .5);
% test a subset for fieldtrip
% (fieldtrip is very slow, so testing all would take too long)
ntest_fieldtrip = 1;
% visit in random order
rp = randperm(n);
prev_sens = '';
for k = 1:ntest
prop = props{rp(k)};
sens = prop{1};
layout_name = prop{2};
args = prop{3};
nchan = prop{4};
chan_stats = prop{5};
chan_labels = prop{6};
if ~isequal(sens, prev_sens)
ds = cosmo_synthetic_dataset('type', 'meeg', ...
'sens', sens, ...
'size', 'big');
end
nbrs = cosmo_meeg_chan_neighbors(ds, args{:});
assertEqual(numel(nbrs), nchan);
chan_count = cellfun(@numel, {nbrs.neighblabel});
stats = [min(chan_count), max(chan_count), mean(chan_count)];
assertElementsAlmostEqual(stats, chan_stats, 'relative', 1e-3);
% test neighbor labels
for j = 1:numel(chan_labels)
chan_label = chan_labels{j};
center = chan_label{1};
around = chan_label{2};
% little optimization for finding first and last channel
if j == 1
pos = 1;
else
pos = nchan;
end
if ~strcmp(center, nbrs(pos).label)
pos = find(cosmo_match({nbrs.label}, center));
assert(numel(pos) == 1);
end
assertEqual(sort(around(:)), sort(nbrs(pos).neighblabel));
end
can_test_fieldtrip = ~isempty(layout_name) && ...
isequal(args{4}, 'layout') && ...
~strcmp(args{1}, 'count');
if can_test_fieldtrip && ntest_fieldtrip > 0
cfg = struct();
cfg.layout = layout_name;
switch args{1}
case 'delaunay'
cfg.method = 'triangulation';
case 'radius'
cfg.method = 'distance';
cfg.neighbourdist = args{2};
otherwise
assert(false);
end
y = ft_prepare_neighbours(cfg);
% reorder the labels if necessary
nbrs_cell = cellfun(@(x){x}, {nbrs.label}, 'UniformOutput', false);
y_cell = cellfun(@(x){x}, {y.label}, 'UniformOutput', false);
p = cosmo_overlap(nbrs_cell, y_cell);
[i, j] = find(p == 1);
y = y(i);
assertEqual({nbrs.label}, {y.label});
[p, q] = cosmo_overlap({nbrs.neighblabel}, {y.neighblabel});
dp = diag(p);
dq = diag(q);
assert(mean(dp(isfinite(dp))) > .9);
assert(mean(dq(isfinite(dq))) > .8);
ntest_fieldtrip = ntest_fieldtrip - 1;
end
end
function props = get_props()
% properties to test; each cell has these elements:
% - dataset type
% - arguments for neighbors
% - number of neighbors
% - min, max, mean number of channels
% - a few channel labels and their neighbors
props = {{'neuromag306_all', ...
'neuromag306mag.lay', ...
{'delaunay', true, 'label', 'layout', 'chantype', ...
'meg_axial'}, ...
102, [5 11 7.9412], ...
{{'MEG0111', ...
{'MEG0111', 'MEG0121', 'MEG0131', ...
'MEG0141', 'MEG0341', 'MEG0511'}}, ...
{'MEG2641', ...
{'MEG1331', 'MEG2421', 'MEG2431', ...
'MEG2441', 'MEG2521', 'MEG2611', ...
'MEG2621', 'MEG2631', 'MEG2641'}}}}, ...
{'neuromag306_all', ...
'neuromag306planar.lay', ...
{'delaunay', true, 'label', 'layout', 'chantype', ...
'meg_planar'}, ...
204, [4 12 8.0392], ...
{{'MEG0113', ...
{'MEG0113', 'MEG0112', 'MEG0122', ...
'MEG0132', 'MEG0133', 'MEG0142', ...
'MEG0343'}}, ...
{'MEG2643', ...
{'MEG1333', 'MEG2423', 'MEG2422', ...
'MEG2612', 'MEG2623', 'MEG2622', ...
'MEG2642', 'MEG2643'}}}}, ...
{'neuromag306_planar', ...
'neuromag306planar.lay', ...
{'radius', 0.1, 'label', 'dataset', 'chantype', ...
'meg_planar'}, ...
204, [4 14 8.9118], ...
{{'MEG0113', ...
{'MEG0113', 'MEG0112', 'MEG0122', ...
'MEG0132', 'MEG0133'}}, ...
{'MEG2643', ...
{'MEG2423', 'MEG2422', 'MEG2623', ...
'MEG2622', 'MEG2632', 'MEG2642', ...
'MEG2643'}}}}, ...
{'neuromag306_planar', ...
'neuromag306planar.lay', ...
{'radius', 0.1, 'label', 'dataset', 'chantype', ...
'meg_combined_from_planar'}, ...
102, [4 16 8.9804], ...
{{'MEG0112+0113', ...
{'MEG0112', 'MEG0113', 'MEG0122', ...
'MEG0123', 'MEG0132', 'MEG0133'}}, ...
{'MEG2642+2643', ...
{'MEG2422', 'MEG2423', 'MEG2622', ...
'MEG2623', 'MEG2632', 'MEG2633', ...
'MEG2642', 'MEG2643'}}}}, ...
{'neuromag306_planar_combined', ...
'neuromag306cmb.lay', ...
{'count', 5, 'label', 'layout', 'chantype', ...
'meg_planar_combined'}, ...
102, [5 5 5], ...
{{'MEG0112+0113', ...
{'MEG0112+0113', 'MEG0122+0123', ...
'MEG0132+0133', 'MEG0212+0213', ...
'MEG0342+0343'}}, ...
{'MEG2642+2643', ...
{'MEG2422+2423', 'MEG2432+2433', ...
'MEG2622+2623', 'MEG2632+2633', ...
'MEG2642+2643'}}}}, ...
{'ctf151', ...
'CTF151.lay', ...
{'delaunay', true, 'label', 'dataset', 'chantype', ...
'meg_axial'}, ...
151, [5 11 8.404], ...
{{'MLC11', ...
{'MLC11', 'MLC12', 'MLC21', 'MLF41', ...
'MLF51', 'MLF52', 'MRC11', 'MZC01', ...
'MZF03'}}, ...
{'MZP02', ...
{'MLO11', 'MLP21', 'MLP31', 'MRO11', ...
'MRP21', 'MRP31', 'MZO01', 'MZP01', ...
'MZP02'}}}}, ...
{'ctf151', ...
'CTF151.lay', ...
{'delaunay', true, 'label', 'dataset', 'chantype', ...
'meg_planar_combined'}, ...
151, [5 11 8.404], ...
{{'MLC11', ...
{'MLC11', 'MLC12', 'MLC21', 'MLF41', ...
'MLF51', 'MLF52', 'MRC11', 'MZC01', ...
'MZF03'}}, ...
{'MZP02', ...
{'MLO11', 'MLP21', 'MLP31', 'MRO11', ...
'MRP21', 'MRP31', 'MZO01', 'MZP01', ...
'MZP02'}}}}, ...
{'ctf151_planar', ...
[], ...
{'radius', 0.1, 'label', ...
{'MLC11_dH', 'MLC12_dH', 'MLC13_dH', ...
'MLC14_dH', 'MLC15_dH', 'MLC21_dH', ...
'MRT41_dV', 'MRT42_dV', 'MRT43_dV', ...
'MRT44_dV', 'MZC01_dV', 'MZC02_dV'}, ...
'chantype', 'meg_planar'}, ...
12, [1 4 2.3333], ...
{{'MLC11_dH', ...
{'MLC11_dH', 'MLC12_dH', 'MLC21_dH', ...
'MZC01_dV'}}, ...
{'MZC02_dV', {'MZC02_dV'}}}}, ...
{'ctf151_planar', ...
[], ...
{'radius', 0.1, 'label', ...
{'MLC11_dH', 'MLC12_dH', 'MLC13_dH', ...
'MLC14_dH', 'MLC15_dH', 'MLC21_dH', ...
'MRT41_dV', 'MRT42_dV', 'MRT43_dV', ...
'MRT44_dV', 'MZC01_dV', 'MZC02_dV'}, ...
'chantype', 'meg_combined_from_planar'}, ...
12, [1 4 2.3333], ...
{{'MLC11', ...
{'MLC11_dH', 'MLC12_dH', 'MLC21_dH', ...
'MZC01_dV'}}, ...
{'MZC02', { 'MZC02_dV' }}}}, ...
{'ctf151_planar_combined', ...
'CTF151.lay', ...
{'count', 5, 'label', 'dataset', 'chantype', ...
'meg_axial'}, ...
151, [5 5 5], ...
{{'MLC11', ...
{'MLC11', 'MLC12', 'MLF51', 'MRC11', ...
'MZF03'}}, ...
{'MZP02', ...
{'MLO11', 'MLP21', 'MRO11', 'MRP21', ...
'MZP02'}}}}, ...
{'ctf151_planar_combined', ...
'CTF151.lay', ...
{'count', 5, 'label', 'dataset', 'chantype', ...
'meg_planar_combined'}, ...
151, [5 5 5], ...
{{'MLC11', ...
{'MLC11', 'MLC12', 'MLF51', 'MRC11', ...
'MZF03'}}, ...
{'MZP02', ...
{'MLO11', 'MLP21', 'MRO11', 'MRP21', ...
'MZP02'}}}}, ...
{'4d148', ...
'4D148.lay', ...
{'delaunay', true, 'label', ...
{'A148', 'A147', 'A146', 'A145', 'A144', ...
'A143', 'A13', 'A12', 'A11', 'A10', 'A9', ...
'A8'}, ...
'chantype', 'meg_axial'}, ...
12, [4 9 6.8333], ...
{{'A8', {'A8', 'A9', 'A10', 'A11', 'A13', 'A143'}}, ...
{'A148', ...
{'A10', 'A11', 'A12', 'A13', 'A143', ...
'A147', 'A148'}}}}, ...
{'4d148', ...
'4D148.lay', ...
{'delaunay', true, 'label', ...
{'A148', 'A147', 'A146', 'A145', 'A144', ...
'A143', 'A13', 'A12', 'A11', 'A10', 'A9', ...
'A8'}, ...
'chantype', 'meg_planar_combined'}, ...
12, [4 9 6.8333], ...
{{'A8', {'A8', 'A9', 'A10', 'A11', 'A13', 'A143'}}, ...
{'A148', ...
{'A10', 'A11', 'A12', 'A13', 'A143', ...
'A147', 'A148'}}}}, ...
{'4d148_planar', ...
[], ...
{'radius', 0.1, 'label', 'layout', 'chantype', ...
'meg_planar'}, ...
296, [4 20 13.1351], ...
{{'A1_dH', ...
{'A1_dH', 'A1_dV', 'A2_dH', 'A2_dV', ...
'A3_dH', 'A3_dV', 'A5_dH', 'A5_dV', ...
'A6_dH', 'A6_dV', 'A7_dH', 'A7_dV', ...
'A10_dH', 'A10_dV', 'A11_dH', 'A11_dV', ...
'A12_dH', 'A12_dV'}}, ...
{'A148_dV', ...
{'A129_dH', 'A129_dV', 'A130_dH', ...
'A130_dV', 'A148_dH', 'A148_dV'}}}}, ...
{'4d148_planar', ...
[], ...
{'radius', 0.1, 'label', 'layout', 'chantype', ...
'meg_combined_from_planar'}, ...
148, [4 20 13.1351], ...
{{'A1', ...
{'A10_dH', 'A10_dV', 'A11_dH', 'A11_dV', ...
'A12_dH', 'A12_dV', 'A1_dH', 'A1_dV', ...
'A2_dH', 'A2_dV', 'A3_dH', 'A3_dV', ...
'A5_dH', 'A5_dV', 'A6_dH', 'A6_dV', ...
'A7_dH', 'A7_dV'}}, ...
{'A148', ...
{'A129_dH', 'A129_dV', 'A130_dH', ...
'A130_dV', 'A148_dH', 'A148_dV'}}}}, ...
{'4d148_planar_combined', ...
'4D148.lay', ...
{'count', 5, 'label', ...
{'A148', 'A147', 'A146', 'A145', 'A144', ...
'A143', 'A13', 'A12', 'A11', 'A10', 'A9', ...
'A8'}, ...
'chantype', 'meg_axial'}, ...
12, [5 5 5], ...
{{'A8', {'A8', 'A9', 'A10', 'A11', 'A12'}}, ...
{'A148', {'A12', 'A145', 'A146', 'A147', 'A148'}}}}, ...
{'4d148_planar_combined', ...
'4D148.lay', ...
{'count', 5, 'label', ...
{'A148', 'A147', 'A146', 'A145', 'A144', ...
'A143', 'A13', 'A12', 'A11', 'A10', 'A9', ...
'A8'}, ...
'chantype', 'meg_planar_combined'}, ...
12, [5 5 5], ...
{{'A8', {'A8', 'A9', 'A10', 'A11', 'A12'}}, ...
{'A148', {'A12', 'A145', 'A146', 'A147', 'A148'}}}}, ...
{'4d248', ...
'4D248.lay', ...
{'delaunay', true, 'label', 'layout', 'chantype', ...
'meg_axial'}, ...
248, [5 12 8.2903], ...
{{'A1', ...
{'A1', 'A2', 'A9', 'A10', 'A11', 'A12', ...
'A13', 'A14'}}, ...
{'A248', ...
{'A151', 'A152', 'A194', 'A195', 'A227', ...
'A228', 'A246', 'A247', 'A248'}}}}, ...
{'4d248', ...
'4D248.lay', ...
{'delaunay', true, 'label', 'layout', 'chantype', ...
'meg_planar_combined'}, ...
248, [5 12 8.2903], ...
{{'A1', ...
{'A1', 'A2', 'A9', 'A10', 'A11', 'A12', ...
'A13', 'A14'}}, ...
{'A248', ...
{'A151', 'A152', 'A194', 'A195', 'A227', ...
'A228', 'A246', 'A247', 'A248'}}}}, ...
{'4d248_planar', ...
[], ...
{'radius', 0.1, 'label', 'dataset', 'chantype', ...
'meg_planar'}, ...
496, [8 32 22.1452], ...
{{'A1_dH', ...
{'A1_dH', 'A1_dV', 'A2_dH', 'A2_dV', ...
'A9_dH', 'A9_dV', 'A10_dH', 'A10_dV', ...
'A11_dH', 'A11_dV', 'A12_dH', 'A12_dV', ...
'A13_dH', 'A13_dV', 'A14_dH', 'A14_dV', ...
'A15_dH', 'A15_dV', 'A25_dH', 'A25_dV', ...
'A28_dH', 'A28_dV', 'A30_dH', 'A30_dV', ...
'A31_dH', 'A31_dV'}}, ...
{'A248_dV', ...
{'A194_dH', 'A194_dV', 'A227_dH', ...
'A227_dV', 'A228_dH', 'A228_dV', ...
'A247_dH', 'A247_dV', 'A248_dH', ...
'A248_dV'}}}}, ...
{'4d248_planar', ...
[], ...
{'radius', 0.1, 'label', 'dataset', 'chantype', ...
'meg_combined_from_planar'}, ...
248, [8 32 22.1452], ...
{{'A1', ...
{'A10_dH', 'A10_dV', 'A11_dH', 'A11_dV', ...
'A12_dH', 'A12_dV', 'A13_dH', 'A13_dV', ...
'A14_dH', 'A14_dV', 'A15_dH', 'A15_dV', ...
'A1_dH', 'A1_dV', 'A25_dH', 'A25_dV', ...
'A28_dH', 'A28_dV', 'A2_dH', 'A2_dV', ...
'A30_dH', 'A30_dV', 'A31_dH', 'A31_dV', ...
'A9_dH', 'A9_dV'}}, ...
{'A248', ...
{'A194_dH', 'A194_dV', 'A227_dH', ...
'A227_dV', 'A228_dH', 'A228_dV', ...
'A247_dH', 'A247_dV', 'A248_dH', ...
'A248_dV'}}}}, ...
{'4d248_planar_combined', ...
'4D248.lay', ...
{'count', 5, 'label', 'layout', 'chantype', ...
'meg_axial'}, ...
248, [5 5 5], ...
{{'A1', {'A1', 'A2', 'A10', 'A12', 'A14'}}, ...
{'A248', {'A194', 'A227', 'A228', 'A247', 'A248'}}}}, ...
{'4d248_planar_combined', ...
'4D248.lay', ...
{'count', 5, 'label', 'layout', 'chantype', ...
'meg_planar_combined'}, ...
248, [5 5 5], ...
{{'A1', {'A1', 'A2', 'A10', 'A12', 'A14'}}, ...
{'A248', {'A194', 'A227', 'A228', 'A247', 'A248'}}}}, ...
{'eeg1005', ...
'EEG1005.lay', ...
{'delaunay', true, 'label', 'dataset', 'chantype', ...
'eeg'}, ...
335, [5 11 8.3851], ...
{{'Fp1', ...
{'Fp1', 'AFp9h', 'AFp7h', 'AFp5h', 'Fp1h', ...
'AFp9', 'AFp7', 'AFp5', 'AFp3'}}, ...
{'OI2', ...
{'O2', 'I2', 'POO8h', 'POO10h', 'OI2h', ...
'O2h', 'I2h', 'POO6', 'POO8', 'POO10', ...
'OI2'}}}}, ...
{'eeg1010', ...
'EEG1010.lay', ...
{'radius', 0.1, 'label', ...
{'TP10', 'TP7', 'TP8', 'TP9', 'CP1', 'CP2', ...
'PO8', 'PO9', 'POz', 'O1', 'O2', 'Oz'}, ...
'chantype', 'eeg'}, ...
12, [1 2 1.5], {{'TP9', {'TP9', 'TP7'}}, {'O2', {'O2'}}}}, ...
{'eeg1020', ...
'EEG1020.lay', ...
{'count', 5, 'label', 'layout', 'chantype', ...
'eeg'}, ...
21, [5 6 5.0952], ...
{{'Fp1', {'Fp1', 'Fpz', 'F7', 'F3', 'Fz'}}, ...
{'O2', {'Pz', 'P4', 'O1', 'Oz', 'O2'}}}}
};