function varargout = spm_sp(varargin) % Orthogonal (design) matrix space setting & manipulation % FORMAT varargout = spm_spc(action,varargin) % % This function computes the different projectors related to the row % and column spaces X. It should be used to avoid redundant computation % of svd on large X matrix. It is divided into actions that set up the % space, (Create,Set,...) and actions that compute projections (pinv, % pinvXpX, pinvXXp, ...) This is motivated by the problem of rounding % errors that can invalidate some computation and is a tool to work % with spaces. % % The only thing that is not easily computed is the null space of % the line of X (assuming size(X,1) > size(X,2)). % To get this space (a basis of it or a projector on it) use spm_sp on X'. % % The only restriction on the use of the space structure is when X is % so big that you can't fit X and its svd in memory at the same time. % Otherwise, the use of spm_sp will generally speed up computations and % optimise memory use. % % Note that since the design matrix is stored in the space structure, % there is no need to keep a separate copy of it. % % ---------------- % % The structure is: % x = struct(... % 'X', [],... % Mtx % 'tol', [],... % tolerance % 'ds', [],... % vectors of singular values % 'u', [],... % u as in X = u*diag(ds)*v' % 'v', [],... % v as in X = u*diag(ds)*v' % 'rk', [],... % rank % 'oP', [],... % orthogonal projector on X % 'oPp', [],... % orthogonal projector on X' % 'ups', [],... % space in which this one is embeded % 'sus', []); % subspace % % The basic required fields are X, tol, ds, u, v, rk. % % ====================================================================== % % FORMAT x = spm_sp('Set',X) % Set up space structure, storing matrix, singular values, rank & tolerance % X - a (design) matrix (2D) % x - the corresponding space structure, with basic fields filled in % The SVD is an "economy size" svd, using MatLab's svd(X,0) % % % FORMAT r = spm_sp('oP',x[,Y]) % FORMAT r = spm_sp('oPp',x[,Y]) % Return orthogonal projectors, or orthogonal projection of data Y (if passed) % x - space structure of matrix X % r - ('oP' usage) ortho. projection matrix projecting into column space of x.X % - ('oPp' usage) ortho. projection matrix projecting into row space of x.X % Y - data (optional) % - If data are specified then the corresponding projection of data is % returned. This is usually more efficient that computing and applying % the projection matrix directly. % % % FORMAT pX = spm_sp('pinv',x) % Returns a pseudo-inverse of X - pinv(X) - computed efficiently % x - space structure of matrix X % pX - pseudo-inverse of X % This is the same as MatLab's pinv - the Moore-Penrose pseudoinverse % ( Note that because size(pinv(X)) == size(X'), it is not generally ) % ( useful to compute pinv(X)*Data sequentially (as is the case for ) % ( 'res' or 'oP') ) % % % FORMAT pXpX = spm_sp('pinvxpx',x) % Returns a pseudo-inverse of X'X - pinv(X'*X) - computed efficiently % x - space structure of matrix X % pXpX - pseudo-inverse of (X'X) % ( Note that because size(pinv(X'*X)) == [size(X,2) size(X,2)], ) % ( it is not useful to compute pinv(X'X)*Data sequentially unless ) % ( size(X,1) < size(X,2) ) % % % FORMAT XpX = spm_sp('xpx',x) % Returns (X'X) - computed efficiently % x - space structure of matrix X % XpX - (X'X) % % % FORMAT pXXp = spm_sp('pinvxxp',x) % Returns a pseudo-inverse of XX' - pinv(X*X') - computed efficiently % x - space structure of matrix X % pXXp - pseudo-inverse of (XX') % % % FORMAT XXp = spm_sp('xxp',x) % Returns (XX') - computed efficiently % x - space structure of matrix X % XXp - (XX') % % % FORMAT b = spm_sp('isinsp',x,c[,tol]) % FORMAT b = spm_sp('isinspp',x,c[,tol]) % Check whether vectors c are in the column/row space of X % x - space structure of matrix X % c - vector(s) (Multiple vectors passed as a matrix) % tol - (optional) tolerance (for rounding error) % [defaults to tolerance specified in space structure: x.tol] % b - ('isinsp' usage) true if c is in the column space of X % - ('isinspp' usage) true if c is in the column space of X % % FORMAT b = spm_sp('eachinsp',x,c[,tol]) % FORMAT b = spm_sp('eachinspp',x,c[,tol]) % Same as 'isinsp' and 'isinspp' but returns a logical row vector of % length size(c,2). % % FORMAT N = spm_sp('n',x) % Simply returns the null space of matrix X (same as matlab NULL) % (Null space = vectors associated with zero eigenvalues) % x - space structure of matrix X % N - null space % % % FORMAT r = spm_sp('nop',x[,Y]) % Orthogonal projector onto null space of X, or projection of data Y (if passed) % x - space structure of matrix X % Y - (optional) data % r - (if no Y passed) orthogonal projection matrix into the null space of X % - (if Y passed ) orthogonal projection of data into the null space of X % ( Note that if xp = spm_sp('set',x.X'), we have: ) % ( spm_sp('nop',x) == spm_sp('res',xp) ) % ( or, equivalently: ) % ( spm_sp('nop',x) + spm_sp('oP',xp) == eye(size(xp.X,1)); ) % % % FORMAT r = spm_sp('res',x[,Y]) % Returns residual formaing matrix wirit column space of X, or residuals (if Y) % x - space structure of matrix X % Y - (optional) data % r - (if no Y passed) residual forming matrix for design matrix X % - (if Y passed ) residuals, i.e. residual forming matrix times data % ( This will be more efficient than % ( spm_sp('res',x)*Data, when size(X,1) > size(X,2) % Note that this can also be seen as the orthogonal projector onto the % null space of x.X' (which is not generally computed in svd, unless % size(X,1) < size(X,2)). % % % FORMAT oX = spm_sp('ox', x) % FORMAT oXp = spm_sp('oxp',x) % Returns an orthonormal basis for X ('ox' usage) or X' ('oxp' usage) % x - space structure of matrix X % oX - orthonormal basis for X - same as orth(x.X) % xOp - *an* orthonormal for X' (but not the same as orth(x.X')) % % % FORMAT b = spm_sp('isspc',x) % Check a variable is a structure with the right fields for a space structure % x - candidate variable % b - true if x is a structure with fieldnames corresponding to spm_sp('create') % % % FORMAT [b,e] = spm_sp('issetspc',x) % Test whether a variable is a space structure with the basic fields set % x - candidate variable % b - true is x is a structure with fieldnames corresponding to % spm_sp('Create'), which has it's basic fields filled in. % e - string describing why x fails the issetspc test (if it does) % This is simply a gateway function combining spm_sp('isspc',x) with % the internal subfunction sf_isset, which checks that the basic fields % are not empty. See sf_isset (below). % %----------------------------------------------------------------------- % SUBFUNCTIONS: % % FORMAT b = sf_isset(x) % Checks that the basic fields are non-empty (doesn't check they're right!) % x - space structure % b - true if the basic fields are non-empty %_______________________________________________________________________ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Jean-Baptiste Poline % $Id: spm_sp.m 5219 2013-01-29 17:07:07Z spm $ if nargin==0 error('Do what? no arguments given...') else action = varargin{1}; end %- check the very basics arguments switch lower(action), case {'create','set','issetspc','isspc'} %- do nothing otherwise, if nargin==1, error('No space : can''t do much!'), end [ok,str] = spm_sp('issetspc',varargin{2}); if ~ok, error(str), else, sX = varargin{2}; end; end; switch lower(action), case 'create' %-Create space structure %======================================================================= % x = spm_sp('Create') varargout = {sf_create}; case 'set' %-Set singular values, space basis, rank & tolerance %======================================================================= % x = spm_sp('Set',X) if nargin==1, error('No design matrix : can''t do much!'), else X = varargin{2}; end if isempty(X), varargout = {sf_create}; return, end %- only sets plain matrices %- check X has 2 dim only if max(size(size(X))) > 2, error('Too many dim in the set'), end if ~isnumeric(X), error('only sets numeric matrices'), end varargout = {sf_set(X)}; case {'p', 'transp'} %-Transpose space of X %======================================================================= switch nargin case 2 varargout = {sf_transp(sX)}; otherwise error('too many input argument in spm_sp'); end % switch nargin case {'op', 'op:'} %-Orthogonal projectors on space of X %======================================================================= % r = spm_sp('oP', sX[,Y]) % r = spm_sp('oP:', sX[,Y]) %- set to 0 less than tolerence values % % if isempty(Y) returns as if Y not given %----------------------------------------------------------------------- switch nargin case 2 switch lower(action), case 'op' varargout = {sf_op(sX)}; case 'op:' varargout = {sf_tol(sf_op(sX),sX.tol)}; end %- switch lower(action), case 3 Y = varargin{3}; if isempty(Y), varargout = {spm_sp(action,sX)}; return, end if size(Y,1) ~= sf_s1(sX), error('Dim dont match'); end; switch lower(action), case 'op' varargout = {sf_op(sX)*Y}; case 'op:' varargout = {sf_tol(sf_op(sX)*Y,sX.tol)}; end % switch lower(action) otherwise error('too many input argument in spm_sp'); end % switch nargin case {'opp', 'opp:'} %-Orthogonal projectors on space of X' %======================================================================= % r = spm_sp('oPp',sX[,Y]) % r = spm_sp('oPp:',sX[,Y]) %- set to 0 less than tolerence values % % if isempty(Y) returns as if Y not given %----------------------------------------------------------------------- switch nargin case 2 switch lower(action), case 'opp' varargout = {sf_opp(sX)}; case 'opp:' varargout = {sf_tol(sf_opp(sX),sX.tol)}; end %- switch lower(action), case 3 Y = varargin{3}; if isempty(Y), varargout = {spm_sp(action,sX)}; return, end if size(Y,1) ~= sf_s2(sX), error('Dim dont match'); end; switch lower(action), case 'opp' varargout = {sf_opp(sX)*Y}; case 'opp:' varargout = {sf_tol(sf_opp(sX)*Y,sX.tol)}; end % switch lower(action) otherwise error('too many input argument in spm_sp'); end % switch nargin case {'x-','x-:'} %-Pseudo-inverse of X - pinv(X) %======================================================================= % = spm_sp('x-',x) switch nargin case 2 switch lower(action), case {'x-'} varargout = { sf_pinv(sX) }; case {'x-:'} varargout = {sf_tol( sf_pinv(sX), sf_t(sX) )}; end case 3 %- check dimensions of Y Y = varargin{3}; if isempty(Y), varargout = {spm_sp(action,sX)}; return, end if size(Y,1) ~= sf_s1(sX), error(['Dim dont match ' action]); end switch lower(action), case {'x-'} varargout = { sf_pinv(sX)*Y }; case {'x-:'} varargout = {sf_tol( sf_pinv(sX)*Y, sf_t(sX) )}; end otherwise error(['too many input argument in spm_sp ' action]); end % switch nargin case {'xp-','xp-:','x-p','x-p:'} %- Pseudo-inverse of X' %======================================================================= % pX = spm_sp('xp-',x) switch nargin case 2 switch lower(action), case {'xp-','x-p'} varargout = { sf_pinvxp(sX) }; case {'xp-:','x-p:'} varargout = {sf_tol( sf_pinvxp(sX), sf_t(sX) )}; end case 3 %- check dimensions of Y Y = varargin{3}; if isempty(Y), varargout = {spm_sp(action,sX)}; return, end if size(Y,1) ~= sf_s2(sX), error(['Dim dont match ' action]); end switch lower(action), case {'xp-','x-p'} varargout = { sf_pinvxp(sX)*Y }; case {'xp-:','x-p:'} varargout = {sf_tol( sf_pinvxp(sX)*Y, sf_t(sX) )}; end otherwise error(['too many input argument in spm_sp ' action]); end % switch nargin case {'cukxp-','cukxp-:'} %- Coordinates of pinv(X') in the base of uk %======================================================================= % pX = spm_sp('cukxp-',x) switch nargin case 2 switch lower(action), case {'cukxp-'} varargout = { sf_cukpinvxp(sX) }; case {'cukxp-:'} varargout = {sf_tol(sf_cukpinvxp(sX),sX.tol)}; end case 3 %- check dimensions of Y Y = varargin{3}; if isempty(Y), varargout = {spm_sp(action,sX)}; return, end if size(Y,1) ~= sf_s2(sX), error(['Dim dont match ' action]); end switch lower(action), case {'cukxp-'} varargout = { sf_cukpinvxp(sX)*Y }; case {'cukxp-:'} varargout = {sf_tol(sf_cukpinvxp(sX)*Y,sX.tol)}; end otherwise error(['too many input argument in spm_sp ' action]); end % switch nargin case {'cukx','cukx:'} %- Coordinates of X in the base of uk %======================================================================= % pX = spm_sp('cukx',x) switch nargin case 2 switch lower(action), case {'cukx'} varargout = { sf_cukx(sX) }; case {'cukx:'} varargout = {sf_tol(sf_cukx(sX),sX.tol)}; end case 3 %- check dimensions of Y Y = varargin{3}; if isempty(Y), varargout = {spm_sp(action,sX)}; return, end if size(Y,1) ~= sf_s2(sX), error(['Dim dont match ' action]); end switch lower(action), case {'cukx'} varargout = { sf_cukx(sX)*Y }; case {'cukx:'} varargout = {sf_tol(sf_cukx(sX)*Y,sX.tol)}; end otherwise error(['too many input argument in spm_sp ' action]); end % switch nargin case {'rk'} %- Returns rank %======================================================================= varargout = { sf_rk(sX) }; case {'ox', 'oxp'} %-Orthonormal basis sets for X / X' %======================================================================= % oX = spm_sp('ox', x) % oXp = spm_sp('oxp',x) if sf_rk(sX) > 0 switch lower(action) case 'ox' varargout = {sf_uk(sX)}; case 'oxp' varargout = {sf_vk(sX)}; end else switch lower(action) case 'ox' varargout = {zeros(sf_s1(sX),1)}; case 'oxp' varargout = {zeros(sf_s2(sX),1)}; end end case {'x', 'xp'} %- X / X' robust to spm_sp changes %======================================================================= % X = spm_sp('x', x) % X' = spm_sp('xp',x) switch lower(action) case 'x', varargout = {sX.X}; case 'xp', varargout = {sX.X'}; end case {'xi', 'xpi'} %- X(:,i) / X'(:,i) robust to spm_sp changes %======================================================================= % X = spm_sp('xi', x) % X' = spm_sp('xpi',x) i = varargin{3}; % NO CHECKING on i !!! assumes correct switch lower(action) case 'xi', varargout = {sX.X(:,i)}; case 'xpi', varargout = {sX.X(i,:)'}; end case {'uk','uk:'} %- Returns u(:,1:r) %======================================================================= % pX = spm_sp('uk',x) % Notice the difference with 'ox' : 'ox' always returns a basis of the % proper siwe while this returns empty if rank is null warning('can''t you use ox ?'); switch nargin case 2 switch lower(action), case {'uk'} varargout = { sf_uk(sX) }; case {'uk:'} varargout = { sf_tol(sf_uk(sX),sX.tol) }; end case 3 %- check dimensions of Y Y = varargin{3}; if isempty(Y), varargout = {spm_sp(action,sX)}; return, end if size(Y,1) ~= sf_rk(sX), error(['Dim dont match ' action]); end switch lower(action), case {'uk'} varargout = { sf_uk(sX)*Y }; case {'uk:'} varargout = {sf_tol(sf_uk(sX)*Y,sX.tol)}; end otherwise error(['too many input argument in spm_sp ' action]); end % switch nargin case {'pinvxpx', 'xpx-', 'pinvxpx:', 'xpx-:',} %- Pseudo-inv of (X'X) %======================================================================= % pXpX = spm_sp('pinvxpx',x [,Y]) switch nargin case 2 switch lower(action), case {'xpx-','pinvxpx'} varargout = {sf_pinvxpx(sX)}; case {'xpx-:','pinvxpx:'} varargout = {sf_tol(sf_pinvxpx(sX),sX.tol)}; end %- case 3 %- check dimensions of Y Y = varargin{3}; if isempty(Y), varargout = {spm_sp(action,sX)}; return, end if size(Y,1) ~= sf_s2(sX), error('Dim dont match'); end; switch lower(action), case {'xpx-','pinvxpx'} varargout = {sf_pinvxpx(sX)*Y}; case {'xpx-:','pinvxpx:'} varargout = {sf_tol(sf_pinvxpx(sX)*Y,sX.tol)}; end %- otherwise error('too many input argument in spm_sp'); end % switch nargin case {'xpx','xpx:'} %-Computation of (X'*X) %======================================================================= % XpX = spm_sp('xpx',x [,Y]) switch nargin case 2 switch lower(action), case {'xpx'} varargout = {sf_xpx(sX)}; case {'xpx:'} varargout = {sf_tol(sf_xpx(sX),sX.tol)}; end %- case 3 %- check dimensions of Y Y = varargin{3}; if isempty(Y), varargout = {spm_sp(action,sX)}; return, end if size(Y,1) ~= sf_s2(sX), error('Dim dont match'); end; switch lower(action), case {'xpx'} varargout = {sf_xpx(sX)*Y}; case {'xpx:'} varargout = {sf_tol(sf_xpx(sX)*Y,sX.tol)}; end %- otherwise error('too many input argument in spm_sp'); end % switch nargin case {'cx->cu','cx->cu:'} %-coordinates in the basis of X -> basis u %======================================================================= % % returns cu such that sX.X*cx == sX.u*cu switch nargin case 2 switch lower(action), case {'cx->cu'} varargout = {sf_cxtwdcu(sX)}; case {'cx->cu:'} varargout = {sf_tol(sf_cxtwdcu(sX),sX.tol)}; end %- case 3 %- check dimensions of Y Y = varargin{3}; if isempty(Y), varargout = {spm_sp(action,sX)}; return, end if size(Y,1) ~= sf_s2(sX), error('Dim dont match'); end; switch lower(action), case {'cx->cu'} varargout = {sf_cxtwdcu(sX)*Y}; case {'cx->cu:'} varargout = {sf_tol(sf_cxtwdcu(sX)*Y,sX.tol)}; end %- otherwise error('too many input argument in spm_sp'); end % switch nargin case {'xxp-','xxp-:','pinvxxp','pinvxxp:'} %-Pseudo-inverse of (XX') %======================================================================= % pXXp = spm_sp('pinvxxp',x [,Y]) switch nargin case 2 switch lower(action), case {'xxp-','pinvxxp'} varargout = {sf_pinvxxp(sX)}; case {'xxp-:','pinvxxp:'} varargout = {sf_tol(sf_pinvxxp(sX),sX.tol)}; end %- case 3 %- check dimensions of Y Y = varargin{3}; if isempty(Y), varargout = {spm_sp(action,sX)}; return, end if size(Y,1) ~= sf_s1(sX), error('Dim dont match'); end; switch lower(action), case {'xxp-','pinvxxp'} varargout = {sf_pinvxxp(sX)*Y}; case {'xxp-:','pinvxxp:'} varargout = {sf_tol(sf_pinvxxp(sX)*Y,sX.tol)}; end %- otherwise error('too many input argument in spm_sp'); end % switch nargin case {'xxp','xxp:'} %-Computation of (X*X') %======================================================================= % XXp = spm_sp('xxp',x) switch nargin case 2 switch lower(action), case {'xxp'} varargout = {sf_xxp(sX)}; case {'xxp:'} varargout = {sf_tol(sf_xxp(sX),sX.tol)}; end %- case 3 %- check dimensions of Y Y = varargin{3}; if isempty(Y), varargout = {spm_sp(action,sX)}; return, end if size(Y,1) ~= sf_s1(sX), error('Dim dont match'); end; switch lower(action), case {'xxp'} varargout = {sf_xxpY(sX,Y)}; case {'xxp:'} varargout = {sf_tol(sf_xxpY(sX,Y),sX.tol)}; end %- otherwise error('too many input argument in spm_sp'); end % switch nargin case {'^p','^p:'} %-Computation of v*(diag(s.^n))*v' %======================================================================= switch nargin case {2,3} if nargin==2, n = 1; else n = varargin{3}; end; if ~isnumeric(n), error('~isnumeric(n)'), end; switch lower(action), case {'^p'} varargout = {sf_jbp(sX,n)}; case {'^p:'} varargout = {sf_tol(sf_jbp(sX,n),sX.tol)}; end %- case 4 n = varargin{3}; if ~isnumeric(n), error('~isnumeric(n)'), end; Y = varargin{4}; if isempty(Y), varargout = {spm_sp(action,sX,n)}; return, end if size(Y,1) ~= sf_s2(sX), error('Dim dont match'); end; switch lower(action), case {'^p'} varargout = {sf_jbp(sX,n)*Y}; case {'^p:'} varargout = {sf_tol(sf_jbp(sX,n)*Y,sX.tol)}; end %- otherwise error('too many input argument in spm_sp'); end % switch nargin case {'^','^:'} %-Computation of v*(diag(s.^n))*v' %======================================================================= switch nargin case {2,3} if nargin==2, n = 1; else n = varargin{3}; end; if ~isnumeric(n), error('~isnumeric(n)'), end; switch lower(action), case {'^'} varargout = {sf_jb(sX,n)}; case {'^:'} varargout = {sf_tol(sf_jb(sX,n),sX.tol)}; end %- case 4 n = varargin{3}; if ~isnumeric(n), error('~isnumeric(n)'), end; Y = varargin{4}; if isempty(Y), varargout = {spm_sp(action,sX,n)}; return, end if size(Y,1) ~= sf_s1(sX), error('Dim dont match'); end; switch lower(action), case {'^'} varargout = {sf_jbY(sX,n,Y)}; case {'^:'} varargout = {sf_tol(sf_jbY(sX,n,Y),sX.tol)}; end %- otherwise error('too many input argument in spm_sp'); end % switch nargin case {'n'} %-Null space of sX %======================================================================= switch nargin case 2 varargout = {sf_n(sX)}; otherwise error('too many input argument in spm_sp'); end % switch nargin case {'np'} %-Null space of sX' %======================================================================= switch nargin case 2 varargout = {sf_n(sf_transp(sX))}; otherwise error('too many input argument in spm_sp'); end % switch nargin case {'nop', 'nop:'} %- project(or)(ion) into null space %======================================================================= % % % switch nargin case 2 switch lower(action), case {'nop'} n = sf_n(sX); varargout = {n*n'}; case {'nop:'} n = sf_n(sX); varargout = {sf_tol(n*n',sX.tol)}; end %- case 3 %- check dimensions of Y Y = varargin{3}; if isempty(Y), varargout = {spm_sp(action,sX)}; return, end if size(Y,1) ~= sf_s2(sX), error('Dim dont match'); end; switch lower(action), case {'nop'} n = sf_n(sX); varargout = {n*(n'*Y)}; case {'nop:'} n = sf_n(sX); varargout = {sf_tol(n*(n'*Y),sX.tol)}; end %- otherwise error('too many input argument in spm_sp'); end % switch nargin case {'nopp', 'nopp:'} %- projector(ion) into null space of X' %======================================================================= % % switch nargin case 2 switch lower(action), case {'nopp'} varargout = {spm_sp('nop',sf_transp(sX))}; case {'nopp:'} varargout = {spm_sp('nop:',sf_transp(sX))}; end %- case 3 switch lower(action), case {'nopp'} varargout = {spm_sp('nop',sf_transp(sX),varargin{3})}; case {'nopp:'} varargout = {spm_sp('nop:',sf_transp(sX),varargin{3})}; end %- otherwise error('too many input argument in spm_sp'); end % switch nargin case {'res', 'r','r:'} %-Residual formaing matrix / residuals %======================================================================= % r = spm_sp('res',sX[,Y]) % %- 'res' will become obsolete : use 'r' or 'r:' instead %- At some stage, should be merged with 'nop' switch nargin case 2 switch lower(action) case {'r','res'} varargout = {sf_r(sX)}; case {'r:','res:'} varargout = {sf_tol(sf_r(sX),sX.tol)}; end %- case 3 %- check dimensions of Y Y = varargin{3}; if isempty(Y), varargout = {spm_sp(action,sX)}; return, end if size(Y,1) ~= sf_s1(sX), error('Dim dont match'); end; switch lower(action) case {'r','res'} varargout = {sf_rY(sX,Y)}; case {'r:','res:'} varargout = {sf_tol(sf_rY(sX,Y),sX.tol)}; end %- otherwise error('too many input argument in spm_sp'); end % switch nargin case {':'} %======================================================================= % spm_sp(':',sX [,Y [,tol]]) %- Sets Y and tol according to arguments if nargin > 4 error('too many input argument in spm_sp'); else if nargin > 3 if isnumeric(varargin{4}), tol = varargin{4}; else error('tol must be numeric'); end else tol = sX.tol; end if nargin > 2 Y = varargin{3}; %- if isempty, returns empty, end else Y = sX.X; end end varargout = {sf_tol(Y,tol)}; case {'isinsp', 'isinspp'} %- is in space or is in dual space %======================================================================= % b = spm_sp('isinsp',x,c[,tol]) % b = spm_sp('isinspp',x,c[,tol]) %-Check whether vectors are in row/column space of X %-Check arguments %----------------------------------------------------------------------- if nargin<3, error('insufficient arguments - action,x,c required'), end c = varargin{3}; %- if isempty(c), dim wont match exept for empty sp. if nargin<4, tol=sX.tol; else, tol = varargin{4}; end %-Compute according to case %----------------------------------------------------------------------- switch lower(action) case 'isinsp' %-Check dimensions if size(sX.X,1) ~= size(c,1) warning('Vector dim don''t match col. dim : not in space !'); varargout = { 0 }; return; end varargout = {all(all( abs(sf_op(sX)*c - c) <= tol ))}; case 'isinspp' %- check dimensions if size(sX.X,2) ~= size(c,1) warning('Vector dim don''t match row dim : not in space !'); varargout = { 0 }; return; end varargout = {all(all( abs(sf_opp(sX)*c - c) <= tol ))}; end case {'eachinsp', 'eachinspp'} %- each column of c in space or in dual space %======================================================================= % b = spm_sp('eachinsp',x,c[,tol]) % b = spm_sp('eachinspp',x,c[,tol]) %-Check whether vectors are in row/column space of X %-Check arguments %----------------------------------------------------------------------- if nargin<3, error('insufficient arguments - action,x,c required'), end c = varargin{3}; %- if isempty(c), dim wont match exept for empty sp. if nargin<4, tol=sX.tol; else, tol = varargin{4}; end %-Compute according to case %----------------------------------------------------------------------- switch lower(action) case 'eachinsp' %-Check dimensions if size(sX.X,1) ~= size(c,1) warning('Vector dim don''t match col. dim : not in space !'); varargout = { 0 }; return; end varargout = {all( abs(sf_op(sX)*c - c) <= tol )}; case 'eachinspp' %- check dimensions if size(sX.X,2) ~= size(c,1) warning('Vector dim don''t match row dim : not in space !'); varargout = { 0 }; return; end varargout = {all( abs(sf_opp(sX)*c - c) <= tol )}; end case '==' % test wether two spaces are the same %======================================================================= % b = spm_sp('==',x1,X2) if nargin~=3, error('too few/many input arguments - need 3'); else X2 = varargin{3}; end; if isempty(sX.X) if isempty(X2), warning('Both spaces empty'); varargout = { 1 }; else warning('one space empty'); varargout = { 0 }; end; else x2 = spm_sp('Set',X2); maxtol = max(sX.tol,x2.tol); varargout = { all( spm_sp('isinsp',sX,X2,maxtol)) & ... all( spm_sp('isinsp',x2,sX.X,maxtol) ) }; %- I have encountered one case where the max of tol was needed. end; case 'isspc' %-Space structure check %======================================================================= % [b,str] = spm_sp('isspc',x) if nargin~=2, error('too few/many input arguments - need 2'), end %-Check we've been passed a structure if ~isstruct(varargin{2}), varargout={0}; return, end %-Go through required field names checking their existance % (Get fieldnames once and compare: isfield doesn't work for multiple ) % (fields, and repeated calls to isfield would result in unnecessary ) % (replicate calls to fieldnames(varargin{2}). ) b = 1; fnames = fieldnames(varargin{2}); for str = fieldnames(sf_create)' b = b & any(strcmp(str,fnames)); if ~b, break, end end if nargout > 1, if b, str = 'ok'; else, str = 'not a space'; end; varargout = {b,str}; else, varargout = {b}; end; case 'issetspc' %-Is this a completed space structure? %======================================================================= % [b,e] = spm_sp('issetspc',x) if nargin~=2, error('too few/many input arguments - need 2'), end if ~spm_sp('isspc',varargin{2}) varargout = {0,'not a space structure (wrong fieldnames)'}; elseif ~sf_isset(varargin{2}) %-Basic fields aren't filled in varargout = {0,'space not defined (use ''set'')'}; else varargout = {1,'OK!'}; end case 'size' %- gives the size of sX %======================================================================= % size = spm_sp('size',x,dim) % if nargin > 3, error('too many input arguments'), end if nargin == 2, dim = []; else dim = varargin{3}; end if ~isempty(dim) switch dim case 1, varargout = { sf_s1(sX) }; case 2, varargout = { sf_s2(sX) }; otherwise, error(['unknown dimension in ' action]); end else %- assumes want both dim switch nargout case {0,1} varargout = { sf_si(sX) }; case 2 varargout = { sf_s1(sX), sf_s2(sX) }; otherwise error(['too many output arg in ' mfilename ' ' action]); end end otherwise %======================================================================= error(['Invalid action (',action,')']) %======================================================================= end % (case lower(action)) %======================================================================= %- S U B - F U N C T I O N S %======================================================================= % % The rule I tried to follow is that the space structure is accessed % only in this sub function part : any sX.whatever should be % prohibited elsewhere .... still a lot to clean !!! function x = sf_create %======================================================================= x = struct(... 'X', [],... % Matrix 'tol', [],... % tolerance 'ds', [],... % vectors of singular values 'u', [],... % u as in X = u*diag(ds)*v' 'v', [], ... % v as in X = u*diag(ds)*v' 'rk', [],... % rank 'oP', [],... % orthogonal projector on X 'oPp', [],... % orthogonal projector on X' 'ups', [],... % space in which this one is embeded 'sus', []); % subspace function x = sf_set(X) %======================================================================= x = sf_create; x.X = X; %-- Compute the svd with svd(X,0) : find all the singular values of x.X %-- SVD(FULL(A)) will usually perform better than SVDS(A,MIN(SIZE(A))) %- if more column that lines, performs on X' if size(X,1) < size(X,2) [x.v, s, x.u] = svd(full(X'),0); else [x.u, s, x.v] = svd(full(X),0); end x.ds = diag(s); clear s; %-- compute the tolerance x.tol = max(size(x.X))*max(abs(x.ds))*eps; %-- compute the rank x.rk = sum(x.ds > x.tol); function x = sf_transp(x) %======================================================================= % %- Tranpspose the space : note that tmp is not touched, therefore %- only contains the address, no duplication of data is performed. x.X = x.X'; tmp = x.v; x.v = x.u; x.u = tmp; tmp = x.oP; x.oP = x.oPp; x.oPp = tmp; clear tmp; function b = sf_isset(x) %======================================================================= b = ~( isempty(x.X) |... isempty(x.u) |... isempty(x.v) |... isempty(x.ds) |... isempty(x.tol) |... isempty(x.rk) ); function s1 = sf_s1(x) %======================================================================= s1 = size(x.X,1); function s2 = sf_s2(x) %======================================================================= s2 = size(x.X,2); function si = sf_si(x) %======================================================================= si = size(x.X); function r = sf_rk(x) %======================================================================= r = x.rk; function uk = sf_uk(x) %======================================================================= uk = x.u(:,1:sf_rk(x)); function vk = sf_vk(x) %======================================================================= vk = x.v(:,1:sf_rk(x)); function sk = sf_sk(x) %======================================================================= sk = x.ds(1:sf_rk(x)); function t = sf_t(x) %======================================================================= t = x.tol; function x = sf_tol(x,t) %======================================================================= x(abs(x) < t) = 0; function op = sf_op(sX) %======================================================================= if sX.rk > 0 op = sX.u(:,[1:sX.rk])*sX.u(:,[1:sX.rk])'; else op = zeros( size(sX.X,1) ); end; %!!!! to implement : a clever version of sf_opY (see sf_rY) function opp = sf_opp(sX) %======================================================================= if sX.rk > 0 opp = sX.v(:,[1:sX.rk])*sX.v(:,[1:sX.rk])'; else opp = zeros( size(sX.X,2) ); end; %!!!! to implement : a clever version of sf_oppY (see sf_rY) function px = sf_pinv(sX) %======================================================================= r = sX.rk; if r > 0 px = sX.v(:,1:r)*diag( ones(r,1)./sX.ds(1:r) )*sX.u(:,1:r)'; else px = zeros(size(sX.X,2),size(sX.X,1)); end function px = sf_pinvxp(sX) %======================================================================= r = sX.rk; if r > 0 px = sX.u(:,1:r)*diag( ones(r,1)./sX.ds(1:r) )*sX.v(:,1:r)'; else px = zeros(size(sX.X)); end function px = sf_pinvxpx(sX) %======================================================================= r = sX.rk; if r > 0 px = sX.v(:,1:r)*diag( sX.ds(1:r).^(-2) )*sX.v(:,1:r)'; else px = zeros(size(sX.X,2)); end function px = sf_jbp(sX,n) %======================================================================= r = sX.rk; if r > 0 px = sX.v(:,1:r)*diag( sX.ds(1:r).^(n) )*sX.v(:,1:r)'; else px = zeros(size(sX.X,2)); end function x = sf_jb(sX,n) %======================================================================= r = sX.rk; if r > 0 x = sX.u(:,1:r)*diag( sX.ds(1:r).^(n) )*sX.u(:,1:r)'; else x = zeros(size(sX.X,1)); end function y = sf_jbY(sX,n,Y) %======================================================================= r = sX.rk; if r > 0 y = ( sX.u(:,1:r)*diag(sX.ds(1:r).^n) )*(sX.u(:,1:r)'*Y); else y = zeros(size(sX.X,1),size(Y,2)); end %!!!! to implement : a clever version of sf_jbY (see sf_rY) function x = sf_cxtwdcu(sX) %======================================================================= %- coordinates in sX.X -> coordinates in sX.u(:,1:rk) x = diag(sX.ds)*sX.v'; function x = sf_cukpinvxp(sX) %======================================================================= %- coordinates of pinv(sX.X') in the basis sX.u(:,1:rk) r = sX.rk; if r > 0 x = diag( ones(r,1)./sX.ds(1:r) )*sX.v(:,1:r)'; else x = zeros( size(sX.X,2) ); end function x = sf_cukx(sX) %======================================================================= %- coordinates of sX.X in the basis sX.u(:,1:rk) r = sX.rk; if r > 0 x = diag( sX.ds(1:r) )*sX.v(:,1:r)'; else x = zeros( size(sX.X,2) ); end function x = sf_xpx(sX) %======================================================================= r = sX.rk; if r > 0 x = sX.v(:,1:r)*diag( sX.ds(1:r).^2 )*sX.v(:,1:r)'; else x = zeros(size(sX.X,2)); end function x = sf_xxp(sX) %======================================================================= r = sX.rk; if r > 0 x = sX.u(:,1:r)*diag( sX.ds(1:r).^2 )*sX.u(:,1:r)'; else x = zeros(size(sX.X,1)); end function x = sf_xxpY(sX,Y) %======================================================================= r = sX.rk; if r > 0 x = sX.u(:,1:r)*diag( sX.ds(1:r).^2 )*(sX.u(:,1:r)'*Y); else x = zeros(size(sX.X,1),size(Y,2)); end function x = sf_pinvxxp(sX) %======================================================================= r = sX.rk; if r > 0 x = sX.u(:,1:r)*diag( sX.ds(1:r).^(-2) )*sX.u(:,1:r)'; else x = zeros(size(sX.X,1)); end function n = sf_n(sX) %======================================================================= % if the null space is in sX.v, returns it % otherwise, performs Gramm Schmidt orthogonalisation. % % r = sX.rk; [q,p]= size(sX.X); if r > 0 if q >= p %- the null space is entirely in v if r == p, n = zeros(p,1); else n = sX.v(:,r+1:p); end else %- only part of n is in v: same as computing the null sp of sX' n = null(sX.X); %----- BUG !!!! in that part ---------------------------------------- %- v = zeros(p,p-q); j = 1; i = 1; z = zeros(p,1); %- while i <= p %- o = z; o(i) = 1; vpoi = [sX.v(i,:) v(i,1:j-1)]'; %- o = sf_tol(o - [sX.v v(:,1:j-1)]*vpoi,sX.tol); %- if any(o), v(:,j) = o/((o'*o)^(1/2)); j = j + 1; end; %- i = i + 1; %- if i>p, error('gramm shmidt error'); end; %- end %- n = [sX.v(:,r+1:q) v]; %-------------------------------------------------------------------- end else n = eye(p); end function r = sf_r(sX) %======================================================================= %- %- returns the residual forming matrix for the space sX %- for internal use. doesn't Check whether oP exist. r = eye(size(sX.X,1)) - sf_op(sX) ; function Y = sf_rY(sX,Y) %======================================================================= % r = spm_sp('r',sX[,Y]) % %- tries to minimise the computation by looking whether we should do %- I - u*(u'*Y) or n*(n'*Y) as in 'nop' r = sX.rk; [q,p]= size(sX.X); if r > 0 %- else returns the input; if r < q-r %- we better do I - u*u' Y = Y - sX.u(:,[1:r])*(sX.u(:,[1:r])'*Y); % warning('route1'); else %- is it worth computing the n ortho basis ? if size(Y,2) < 5*q Y = sf_r(sX)*Y; % warning('route2'); else n = sf_n(sf_transp(sX)); % warning('route3'); Y = n*(n'*Y); end end end