function s = cosmo_squareform(x, varargin)
% converts pair-wise distances between matrix and vector form
%
% s=cosmo_squareform(x[, direction])
%
% Inputs:
% x One of:
% - NxN distance matrix; x must be symmetric and have zeros
% on the diagonal
% - 1xM distance vector
% direction Optional. If provided it must be 'tovector' (if x is a
% matrix) or 'tomatrix' (if x is a vector). If not provided
% it is set to 'tovector' if x is a matrix and to 'tomatrix'
% if x is a vector
%
% Returns:
% s One of:
% - NxN distance matrix, if direction=='tomatrix'
% - 1xM distance vector, if direction=='tovector'
% it must hold that N*(N-1)/2=M
%
% Notes:
% - this function provides the same functionality as the built-in function
% ''squareform'' in the matlab stats toolbox.
%
% # For CoSMoMVPA's copyright information and license terms, #
% # see the COPYING file distributed with CoSMoMVPA. #
check_input(x);
direction = get_direction(x, varargin{:});
switch direction
case 'tomatrix'
s = to_matrix(x);
case 'tovector'
s = to_vector(x);
otherwise
error(['illegal direction argument, must be one of ', ...
'''tomatrix'',''tovector''']);
end
function check_input(x)
if numel(size(x)) ~= 2
error('first input must be matrix or vector');
end
if ~(islogical(x) || isnumeric(x))
error(['Unsupported data type ''%s''; only numeric '...
'and logical arrays are supported'], class(x));
end
function s = to_vector(x)
[n_rows, n_columns] = size(x);
if n_rows ~= n_columns
error('direction ''to_vector'' requires a square matrix as input');
end
dg = diag(x);
if any(dg)
error('square matrix must be all zero on diagonal');
end
if ~cosmo_isequaln(x, x')
error('square matrix must be symmetric');
end
msk = bsxfun(@gt, (1:n_rows)', 1:n_rows);
s = x(msk);
s = s(:)';
function s = to_matrix(x)
if isempty(x)
s = [];
return
end
if ~isvector(x)
error('direction ''to_matrix'' requires a vector as input');
end
n = numel(x);
% side*(side+1)/2=n, solve for side>0
side = (1 + sqrt(1 + 8 * n)) / 2;
if ~isequal(side, round(side))
error(['size %d of input vector is not correct for '...
'the number of elements below diagonal of a '...
'square matrix'], n);
end
msk = bsxfun(@gt, (1:side)', 1:side);
if islogical(x)
s = false(side);
s(msk) = x;
s = s | s';
elseif isnumeric(x)
s = zeros(side);
s(msk) = x;
s = s + s';
end
function direction = get_direction(x, varargin)
if numel(varargin) < 1
sz = size(x);
if sz(1) == sz(2) && sz(1) ~= 1
direction = 'tovector';
else
direction = 'tomatrix';
end
elseif ischar(varargin{1})
direction = varargin{1};
else
error('second argument must be a string');
end