function test_suite=test_tiedrank
% tests for cosmo_tiedrank
%
% # 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_exceptions()
aet=@(varargin)assertExceptionThrown(@()...
cosmo_tiedrank(varargin{:}),'');
% illegal first argument
aet('foo');
aet({1,2});
aet(false);
% illegal second argument
aet([1 2],'foo');
aet([1 2],.5);
aet([1 2],-1);
function test_tiedrank_simple_input()
args_results={{rand()},1;...
{[3 2 1]},[1 1 1];...
{[3 2 1],1},[1 1 1];...
{[3 2 1],2},[3 2 1];...
{[5 NaN 3],2},[2 NaN 1];...
};
n=size(args_results,1);
for k=1:n
row=args_results(k,:);
args=row{1};
result=row{2};
assertElementsAlmostEqual(cosmo_tiedrank(args{:}),result);
end
function test_tiedrank_vector_input()
wrapper_func=@(x)cosmo_tiedrank(x,1);
helper_test_tiedrank_vector_with(wrapper_func);
function test_tiedrank_ndim_input()
for ndim=2:5
data_size=zeros(1,ndim);
for dim=1:ndim
data_size(dim)=randint(3)+1;
end
for dim=1:ndim
func=@(x)cosmo_tiedrank(x,dim);
helper_test_tiedrank_ndim_with_size(func, data_size, dim);
end
end
function test_tiedrank_singleton_input()
sizes={[1 2 3], [1 1 3], [3 1 1], [1 3 3], [1 1 3 3], ...
[1,1+randint(10)], [1+randint(10),1]};
for k=1:numel(sizes)
data_size=sizes{k};
ndim=numel(data_size);
for dim=1:ndim
func=@(x)cosmo_tiedrank(x,dim);
helper_test_tiedrank_ndim_with_size(func, data_size, dim);
end
end
function test_builtin_matlab()
% sanity check that tests whether our test
if cosmo_skip_test_if_no_external('!tiedrank')
return;
end
if ~cosmo_wtf('is_matlab')
cosmo_notify_test_skipped('not running Matlab');
return;
end
helper_test_tiedrank_vector_with(@tiedrank);
function r=randint(n)
r=ceil(rand()*n);
function helper_test_tiedrank_vector_with(func)
dim=1;
nsamples=randint(100)+100;
data_size=[1,1];
data_size(dim)=nsamples;
helper_test_tiedrank_ndim_with_size(func, data_size, dim)
function helper_test_tiedrank_ndim_with_size(func, data_size, dim)
data=generate_random_tied_data(data_size);
expected_result=func(data);
assert_result_matches(data,dim,expected_result);
function data=generate_random_tied_data(data_size)
nan_ratio=.1;
tied_ratio_min=.5;
tied_ratio_max=.6;
data=rand(data_size);
n=numel(data);
tied_ratio=tied_ratio_min+rand()*(tied_ratio_max-tied_ratio_min);
tied_c=0;
while tied_c*n<tied_ratio
p=randint(n);
q=randint(n);
data(p)=data(q);
tied_c=tied_c+1;
end
nan_msk=rand(data_size)<nan_ratio;
nan_msk(randint(n))=true; % at least one NaN
data(nan_msk)=NaN;
function result=builtin_matlab_tiedrank_wrapper(data)
if cosmo_skip_test_if_no_external('!tiedrank') || ...
~cosmo_wtf('is_matlab')
return;
end
result=tiedrank(data);
function assert_result_matches(data, dim, result)
% data and result are both N-dimensional arrays and must be of the same
% size. result should be equal to the tiedrank output from data
assertEqual(size(data),size(result));
nan_msk=isnan(data);
assertEqual(isnan(result),nan_msk);
if dim>numel(size(data))
% all non-NaN values must be 1
assertTrue(all(result(~nan_msk)==1))
return;
end
% make the dimension along which tiedrank is applied the first
% dimension in the *_sh variables
shift_count=dim-1;
data_sh=shiftdim(data,shift_count);
result_sh=shiftdim(result,shift_count);
% reshape in matrix form so that first dimension is the output for
% each feature and the second dimension represents all other features
nsamples=size(data_sh,1);
nfeatures=numel(data)/nsamples;
data_mat=reshape(data_sh,nsamples,nfeatures);
result_mat=reshape(result_sh,nsamples,nfeatures);
for k=1:nfeatures
% test k-th feature
d=data_mat(:,k);
r=result_mat(:,k);
% only consider non-nan values
dkeep=d(~isnan(d));
rkeep=r(~isnan(r));
% sort the values
[s,idx]=sort(dkeep,1);
nkeep=numel(idx);
% allocate space for expected output for k-th feature
expected_rkeep=zeros(nkeep,1);
m=0;
while m<nkeep
m=m+1;
% index of first value in a possible row of equal values
first=m;
c=0;
while m<nkeep && s(m)==s(m+1)
c=c+1;
m=m+1;
if c>numel(d)
error('no convergence');
end
end
% the number of equal values starting at first is equal to c+1
expected_rkeep(idx(first+(0:c)))=c/2+first;
end
if isempty(expected_rkeep)
assert(isempty(rkeep));
else
assertElementsAlmostEqual(expected_rkeep,rkeep);
end
end