function result=cosmo_dim_generalization_measure(ds,varargin)
% measure generalization across pairwise combinations over time (or any other dimension)
%
% result=cosmo_dim_generalization_measure(ds,varargin)
%
% Inputs:
% ds dataset struct with d being a sample dimension, and
% with ds.sa.chunks==1 for samples to use for
% training and ds.sa.chunks==2 for those to use for
% testing. Other values for chunks are not allowed.
% 'measure',m function handle to apply to combinations of samples
% in the input dataset ds, such as
% - @cosmo_correlation_measure
% - @cosmo_crossvalidation_measure
% - @cosmo_target_dsm_corr_measure
% 'dimension',d dimension along which to generalize. Typically this
% will be 'time' for MEEG data
% 'radius',r radius used for the d dimension. For example, when
% set to r=4 with d='time', then 4*2+1 time points
% are used to asses generalization, (except on the
% edges). Note that when using a radius>0, it is
% assumed that splits of the dataset by dimension d
% have corresponding elements in the same order
% (such as provided by cosmo_dim_transpose).
% 'nproc', np Use np parallel threads. (Multiple threads may
% speed up computations). If parallel processing is
% not available, or if this option is not provided,
% then a single thread is used.
% K,V any other key-value pairs necessary for the measure
% m, for example 'classifier' if
% m=@cosmo_crossvalidation_measure.
%
% Output:
% result dataset with ['train_' d] and ['test_' d] as sample
% dimensions, i.e. these are in ds.a.sdim.labels
% result.samples is Nx1, where N=K*J is the number of
% combinations of (1) the K points in ds with
% chunks==1 and different values in dimension d, and
% (2) the J points in ds with chunks==2 and different
% values in dimension d.
%
% Examples:
% % Generalization over time
% sz='big';
% train_ds=cosmo_synthetic_dataset('type','timelock','size',sz,...
% 'nchunks',2,'seed',1);
% test_ds=cosmo_synthetic_dataset('type','timelock','size',sz,...
% 'nchunks',3,'seed',2);
% % set chunks
% train_ds.sa.chunks(:)=1;
% test_ds.sa.chunks(:)=2;
% %
% % construct the dataset
% ds=cosmo_stack({train_ds, test_ds});
% %
% % make time a sample dimension
% dim_label='time';
% ds_time=cosmo_dim_transpose(ds,dim_label,1);
% %
% % set measure and its arguments
% measure_args=struct();
% %
% % use correlation measure
% measure_args.measure=@cosmo_correlation_measure;
% % dimension of interest is 'time'
% measure_args.dimension=dim_label;
% %
% % run time-by-time generalization analysis
% dgm_ds=cosmo_dim_generalization_measure(ds_time,measure_args,...
% 'progress',false);
% %
% % the output has train_time and test_time as sample dimensions
% cosmo_disp(dgm_ds.a)
% %|| .sdim
% %|| .labels
% %|| { 'train_time' 'test_time' }
% %|| .values
% %|| { [ -0.2 [ -0.2
% %|| -0.15 -0.15
% %|| -0.1 -0.1
% %|| : :
% %|| 0 0
% %|| 0.05 0.05
% %|| 0.1 ]@7x1 0.1 ]@7x1 }
%
%
% % Searchlight example
% % (This example requires FieldTrip)
% cosmo_skip_test_if_no_external('fieldtrip');
% %
% sz='big';
% train_ds=cosmo_synthetic_dataset('type','timelock','size',sz,...
% 'nchunks',2,'seed',1);
% test_ds=cosmo_synthetic_dataset('type','timelock','size',sz,...
% 'nchunks',3,'seed',2);
% % set chunks
% train_ds.sa.chunks(:)=1;
% test_ds.sa.chunks(:)=2;
% %
% % construct the dataset
% ds=cosmo_stack({train_ds, test_ds});
% %
% % make time a sample dimension
% dim_label='time';
% ds_time=cosmo_dim_transpose(ds,dim_label,1);
% %
% % set measure and its arguments
% measure_args=struct();
% %
% % use correlation measure
% measure_args.measure=@cosmo_correlation_measure;
% % dimension of interest is 'time'
% measure_args.dimension=dim_label;
% %
% % only to make this example run fast, most channels are eliminated
% % (there is no other reason to do this step)
% ds_time=cosmo_slice(ds_time,ds_time.fa.chan<=20,2);
% ds_time=cosmo_dim_prune(ds_time);
% %
% % define neighborhood for channels
% nbrhood=cosmo_meeg_chan_neighborhood(ds_time,...
% 'chantype','meg_combined_from_planar',...
% 'count',5,'label','dataset');
% %
% % run searchlight with generalization measure
% measure=@cosmo_dim_generalization_measure;
% dgm_sl_ds=cosmo_searchlight(ds_time,nbrhood,measure,measure_args,...
% 'progress',false);
% %
% % the output has train_time and test_time as sample dimensions,
% % and chan as feature dimension
% cosmo_disp(dgm_sl_ds.a,'edgeitems',1)
% %|| .fdim
% %|| .labels
% %|| { 'chan' }
% %|| .values
% %|| { { 'MEG0112+0113' ... 'MEG0712+0713' }@1x7 }
% %|| .meeg
% %|| .samples_type
% %|| 'timelock'
% %|| .samples_field
% %|| 'trial'
% %|| .samples_label
% %|| 'rpt'
% %|| .sdim
% %|| .labels
% %|| { 'train_time' 'test_time' }
% %|| .values
% %|| { [ -0.2 [ -0.2
% %|| : :
% %|| 0.1 ]@7x1 0.1 ]@7x1 }
%
%
% Notes:
% - this function can be used together with searchlight
% - to make a dimension d a sample dimension from a feature dimension
% (usually necessary before running this function), or the other way
% around (usually necessary after running this function), use
% cosmo_dim_transpose.
% - a 'partition' argument should not be provided, because this function
% generates them itself. The partitions are generated so that there
% is a single fold; samples with chunks==1 are always used for training
% and those with chunks==2 are used for testing (e.g. when using
% m=@cosmo_crossvalidation_measure). In the case of using
% m=@cosmo_correlation_measure, this amounts to split-half
% correlations.
%
% See also: cosmo_correlation_measure, cosmo_crossvalidation_measure
% cosmo_target_dsm_corr_measure, cosmo_searchlight,
% cosmo_dim_transpose
%
% # For CoSMoMVPA's copyright information and license terms, #
% # see the COPYING file distributed with CoSMoMVPA. #