spm_sp.m 39 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415
  1. function varargout = spm_sp(varargin)
  2. % Orthogonal (design) matrix space setting & manipulation
  3. % FORMAT varargout = spm_spc(action,varargin)
  4. %
  5. % This function computes the different projectors related to the row
  6. % and column spaces X. It should be used to avoid redundant computation
  7. % of svd on large X matrix. It is divided into actions that set up the
  8. % space, (Create,Set,...) and actions that compute projections (pinv,
  9. % pinvXpX, pinvXXp, ...) This is motivated by the problem of rounding
  10. % errors that can invalidate some computation and is a tool to work
  11. % with spaces.
  12. %
  13. % The only thing that is not easily computed is the null space of
  14. % the line of X (assuming size(X,1) > size(X,2)).
  15. % To get this space (a basis of it or a projector on it) use spm_sp on X'.
  16. %
  17. % The only restriction on the use of the space structure is when X is
  18. % so big that you can't fit X and its svd in memory at the same time.
  19. % Otherwise, the use of spm_sp will generally speed up computations and
  20. % optimise memory use.
  21. %
  22. % Note that since the design matrix is stored in the space structure,
  23. % there is no need to keep a separate copy of it.
  24. %
  25. % ----------------
  26. %
  27. % The structure is:
  28. % x = struct(...
  29. % 'X', [],... % Mtx
  30. % 'tol', [],... % tolerance
  31. % 'ds', [],... % vectors of singular values
  32. % 'u', [],... % u as in X = u*diag(ds)*v'
  33. % 'v', [],... % v as in X = u*diag(ds)*v'
  34. % 'rk', [],... % rank
  35. % 'oP', [],... % orthogonal projector on X
  36. % 'oPp', [],... % orthogonal projector on X'
  37. % 'ups', [],... % space in which this one is embeded
  38. % 'sus', []); % subspace
  39. %
  40. % The basic required fields are X, tol, ds, u, v, rk.
  41. %
  42. % ======================================================================
  43. %
  44. % FORMAT x = spm_sp('Set',X)
  45. % Set up space structure, storing matrix, singular values, rank & tolerance
  46. % X - a (design) matrix (2D)
  47. % x - the corresponding space structure, with basic fields filled in
  48. % The SVD is an "economy size" svd, using MatLab's svd(X,0)
  49. %
  50. %
  51. % FORMAT r = spm_sp('oP',x[,Y])
  52. % FORMAT r = spm_sp('oPp',x[,Y])
  53. % Return orthogonal projectors, or orthogonal projection of data Y (if passed)
  54. % x - space structure of matrix X
  55. % r - ('oP' usage) ortho. projection matrix projecting into column space of x.X
  56. % - ('oPp' usage) ortho. projection matrix projecting into row space of x.X
  57. % Y - data (optional)
  58. % - If data are specified then the corresponding projection of data is
  59. % returned. This is usually more efficient that computing and applying
  60. % the projection matrix directly.
  61. %
  62. %
  63. % FORMAT pX = spm_sp('pinv',x)
  64. % Returns a pseudo-inverse of X - pinv(X) - computed efficiently
  65. % x - space structure of matrix X
  66. % pX - pseudo-inverse of X
  67. % This is the same as MatLab's pinv - the Moore-Penrose pseudoinverse
  68. % ( Note that because size(pinv(X)) == size(X'), it is not generally )
  69. % ( useful to compute pinv(X)*Data sequentially (as is the case for )
  70. % ( 'res' or 'oP') )
  71. %
  72. %
  73. % FORMAT pXpX = spm_sp('pinvxpx',x)
  74. % Returns a pseudo-inverse of X'X - pinv(X'*X) - computed efficiently
  75. % x - space structure of matrix X
  76. % pXpX - pseudo-inverse of (X'X)
  77. % ( Note that because size(pinv(X'*X)) == [size(X,2) size(X,2)], )
  78. % ( it is not useful to compute pinv(X'X)*Data sequentially unless )
  79. % ( size(X,1) < size(X,2) )
  80. %
  81. %
  82. % FORMAT XpX = spm_sp('xpx',x)
  83. % Returns (X'X) - computed efficiently
  84. % x - space structure of matrix X
  85. % XpX - (X'X)
  86. %
  87. %
  88. % FORMAT pXXp = spm_sp('pinvxxp',x)
  89. % Returns a pseudo-inverse of XX' - pinv(X*X') - computed efficiently
  90. % x - space structure of matrix X
  91. % pXXp - pseudo-inverse of (XX')
  92. %
  93. %
  94. % FORMAT XXp = spm_sp('xxp',x)
  95. % Returns (XX') - computed efficiently
  96. % x - space structure of matrix X
  97. % XXp - (XX')
  98. %
  99. %
  100. % FORMAT b = spm_sp('isinsp',x,c[,tol])
  101. % FORMAT b = spm_sp('isinspp',x,c[,tol])
  102. % Check whether vectors c are in the column/row space of X
  103. % x - space structure of matrix X
  104. % c - vector(s) (Multiple vectors passed as a matrix)
  105. % tol - (optional) tolerance (for rounding error)
  106. % [defaults to tolerance specified in space structure: x.tol]
  107. % b - ('isinsp' usage) true if c is in the column space of X
  108. % - ('isinspp' usage) true if c is in the column space of X
  109. %
  110. % FORMAT b = spm_sp('eachinsp',x,c[,tol])
  111. % FORMAT b = spm_sp('eachinspp',x,c[,tol])
  112. % Same as 'isinsp' and 'isinspp' but returns a logical row vector of
  113. % length size(c,2).
  114. %
  115. % FORMAT N = spm_sp('n',x)
  116. % Simply returns the null space of matrix X (same as matlab NULL)
  117. % (Null space = vectors associated with zero eigenvalues)
  118. % x - space structure of matrix X
  119. % N - null space
  120. %
  121. %
  122. % FORMAT r = spm_sp('nop',x[,Y])
  123. % Orthogonal projector onto null space of X, or projection of data Y (if passed)
  124. % x - space structure of matrix X
  125. % Y - (optional) data
  126. % r - (if no Y passed) orthogonal projection matrix into the null space of X
  127. % - (if Y passed ) orthogonal projection of data into the null space of X
  128. % ( Note that if xp = spm_sp('set',x.X'), we have: )
  129. % ( spm_sp('nop',x) == spm_sp('res',xp) )
  130. % ( or, equivalently: )
  131. % ( spm_sp('nop',x) + spm_sp('oP',xp) == eye(size(xp.X,1)); )
  132. %
  133. %
  134. % FORMAT r = spm_sp('res',x[,Y])
  135. % Returns residual formaing matrix wirit column space of X, or residuals (if Y)
  136. % x - space structure of matrix X
  137. % Y - (optional) data
  138. % r - (if no Y passed) residual forming matrix for design matrix X
  139. % - (if Y passed ) residuals, i.e. residual forming matrix times data
  140. % ( This will be more efficient than
  141. % ( spm_sp('res',x)*Data, when size(X,1) > size(X,2)
  142. % Note that this can also be seen as the orthogonal projector onto the
  143. % null space of x.X' (which is not generally computed in svd, unless
  144. % size(X,1) < size(X,2)).
  145. %
  146. %
  147. % FORMAT oX = spm_sp('ox', x)
  148. % FORMAT oXp = spm_sp('oxp',x)
  149. % Returns an orthonormal basis for X ('ox' usage) or X' ('oxp' usage)
  150. % x - space structure of matrix X
  151. % oX - orthonormal basis for X - same as orth(x.X)
  152. % xOp - *an* orthonormal for X' (but not the same as orth(x.X'))
  153. %
  154. %
  155. % FORMAT b = spm_sp('isspc',x)
  156. % Check a variable is a structure with the right fields for a space structure
  157. % x - candidate variable
  158. % b - true if x is a structure with fieldnames corresponding to spm_sp('create')
  159. %
  160. %
  161. % FORMAT [b,e] = spm_sp('issetspc',x)
  162. % Test whether a variable is a space structure with the basic fields set
  163. % x - candidate variable
  164. % b - true is x is a structure with fieldnames corresponding to
  165. % spm_sp('Create'), which has it's basic fields filled in.
  166. % e - string describing why x fails the issetspc test (if it does)
  167. % This is simply a gateway function combining spm_sp('isspc',x) with
  168. % the internal subfunction sf_isset, which checks that the basic fields
  169. % are not empty. See sf_isset (below).
  170. %
  171. %-----------------------------------------------------------------------
  172. % SUBFUNCTIONS:
  173. %
  174. % FORMAT b = sf_isset(x)
  175. % Checks that the basic fields are non-empty (doesn't check they're right!)
  176. % x - space structure
  177. % b - true if the basic fields are non-empty
  178. %_______________________________________________________________________
  179. % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging
  180. % Jean-Baptiste Poline
  181. % $Id: spm_sp.m 5219 2013-01-29 17:07:07Z spm $
  182. if nargin==0
  183. error('Do what? no arguments given...')
  184. else
  185. action = varargin{1};
  186. end
  187. %- check the very basics arguments
  188. switch lower(action),
  189. case {'create','set','issetspc','isspc'}
  190. %- do nothing
  191. otherwise,
  192. if nargin==1, error('No space : can''t do much!'), end
  193. [ok,str] = spm_sp('issetspc',varargin{2});
  194. if ~ok, error(str), else, sX = varargin{2}; end;
  195. end;
  196. switch lower(action),
  197. case 'create' %-Create space structure
  198. %=======================================================================
  199. % x = spm_sp('Create')
  200. varargout = {sf_create};
  201. case 'set' %-Set singular values, space basis, rank & tolerance
  202. %=======================================================================
  203. % x = spm_sp('Set',X)
  204. if nargin==1, error('No design matrix : can''t do much!'),
  205. else X = varargin{2}; end
  206. if isempty(X), varargout = {sf_create}; return, end
  207. %- only sets plain matrices
  208. %- check X has 2 dim only
  209. if max(size(size(X))) > 2, error('Too many dim in the set'), end
  210. if ~isnumeric(X), error('only sets numeric matrices'), end
  211. varargout = {sf_set(X)};
  212. case {'p', 'transp'} %-Transpose space of X
  213. %=======================================================================
  214. switch nargin
  215. case 2
  216. varargout = {sf_transp(sX)};
  217. otherwise
  218. error('too many input argument in spm_sp');
  219. end % switch nargin
  220. case {'op', 'op:'} %-Orthogonal projectors on space of X
  221. %=======================================================================
  222. % r = spm_sp('oP', sX[,Y])
  223. % r = spm_sp('oP:', sX[,Y]) %- set to 0 less than tolerence values
  224. %
  225. % if isempty(Y) returns as if Y not given
  226. %-----------------------------------------------------------------------
  227. switch nargin
  228. case 2
  229. switch lower(action),
  230. case 'op'
  231. varargout = {sf_op(sX)};
  232. case 'op:'
  233. varargout = {sf_tol(sf_op(sX),sX.tol)};
  234. end %- switch lower(action),
  235. case 3
  236. Y = varargin{3};
  237. if isempty(Y), varargout = {spm_sp(action,sX)}; return, end
  238. if size(Y,1) ~= sf_s1(sX), error('Dim dont match'); end;
  239. switch lower(action),
  240. case 'op'
  241. varargout = {sf_op(sX)*Y};
  242. case 'op:'
  243. varargout = {sf_tol(sf_op(sX)*Y,sX.tol)};
  244. end % switch lower(action)
  245. otherwise
  246. error('too many input argument in spm_sp');
  247. end % switch nargin
  248. case {'opp', 'opp:'} %-Orthogonal projectors on space of X'
  249. %=======================================================================
  250. % r = spm_sp('oPp',sX[,Y])
  251. % r = spm_sp('oPp:',sX[,Y]) %- set to 0 less than tolerence values
  252. %
  253. % if isempty(Y) returns as if Y not given
  254. %-----------------------------------------------------------------------
  255. switch nargin
  256. case 2
  257. switch lower(action),
  258. case 'opp'
  259. varargout = {sf_opp(sX)};
  260. case 'opp:'
  261. varargout = {sf_tol(sf_opp(sX),sX.tol)};
  262. end %- switch lower(action),
  263. case 3
  264. Y = varargin{3};
  265. if isempty(Y), varargout = {spm_sp(action,sX)}; return, end
  266. if size(Y,1) ~= sf_s2(sX), error('Dim dont match'); end;
  267. switch lower(action),
  268. case 'opp'
  269. varargout = {sf_opp(sX)*Y};
  270. case 'opp:'
  271. varargout = {sf_tol(sf_opp(sX)*Y,sX.tol)};
  272. end % switch lower(action)
  273. otherwise
  274. error('too many input argument in spm_sp');
  275. end % switch nargin
  276. case {'x-','x-:'} %-Pseudo-inverse of X - pinv(X)
  277. %=======================================================================
  278. % = spm_sp('x-',x)
  279. switch nargin
  280. case 2
  281. switch lower(action),
  282. case {'x-'}
  283. varargout = { sf_pinv(sX) };
  284. case {'x-:'}
  285. varargout = {sf_tol( sf_pinv(sX), sf_t(sX) )};
  286. end
  287. case 3
  288. %- check dimensions of Y
  289. Y = varargin{3};
  290. if isempty(Y), varargout = {spm_sp(action,sX)}; return, end
  291. if size(Y,1) ~= sf_s1(sX), error(['Dim dont match ' action]); end
  292. switch lower(action),
  293. case {'x-'}
  294. varargout = { sf_pinv(sX)*Y };
  295. case {'x-:'}
  296. varargout = {sf_tol( sf_pinv(sX)*Y, sf_t(sX) )};
  297. end
  298. otherwise
  299. error(['too many input argument in spm_sp ' action]);
  300. end % switch nargin
  301. case {'xp-','xp-:','x-p','x-p:'} %- Pseudo-inverse of X'
  302. %=======================================================================
  303. % pX = spm_sp('xp-',x)
  304. switch nargin
  305. case 2
  306. switch lower(action),
  307. case {'xp-','x-p'}
  308. varargout = { sf_pinvxp(sX) };
  309. case {'xp-:','x-p:'}
  310. varargout = {sf_tol( sf_pinvxp(sX), sf_t(sX) )};
  311. end
  312. case 3
  313. %- check dimensions of Y
  314. Y = varargin{3};
  315. if isempty(Y), varargout = {spm_sp(action,sX)}; return, end
  316. if size(Y,1) ~= sf_s2(sX), error(['Dim dont match ' action]); end
  317. switch lower(action),
  318. case {'xp-','x-p'}
  319. varargout = { sf_pinvxp(sX)*Y };
  320. case {'xp-:','x-p:'}
  321. varargout = {sf_tol( sf_pinvxp(sX)*Y, sf_t(sX) )};
  322. end
  323. otherwise
  324. error(['too many input argument in spm_sp ' action]);
  325. end % switch nargin
  326. case {'cukxp-','cukxp-:'} %- Coordinates of pinv(X') in the base of uk
  327. %=======================================================================
  328. % pX = spm_sp('cukxp-',x)
  329. switch nargin
  330. case 2
  331. switch lower(action),
  332. case {'cukxp-'}
  333. varargout = { sf_cukpinvxp(sX) };
  334. case {'cukxp-:'}
  335. varargout = {sf_tol(sf_cukpinvxp(sX),sX.tol)};
  336. end
  337. case 3
  338. %- check dimensions of Y
  339. Y = varargin{3};
  340. if isempty(Y), varargout = {spm_sp(action,sX)}; return, end
  341. if size(Y,1) ~= sf_s2(sX), error(['Dim dont match ' action]); end
  342. switch lower(action),
  343. case {'cukxp-'}
  344. varargout = { sf_cukpinvxp(sX)*Y };
  345. case {'cukxp-:'}
  346. varargout = {sf_tol(sf_cukpinvxp(sX)*Y,sX.tol)};
  347. end
  348. otherwise
  349. error(['too many input argument in spm_sp ' action]);
  350. end % switch nargin
  351. case {'cukx','cukx:'} %- Coordinates of X in the base of uk
  352. %=======================================================================
  353. % pX = spm_sp('cukx',x)
  354. switch nargin
  355. case 2
  356. switch lower(action),
  357. case {'cukx'}
  358. varargout = { sf_cukx(sX) };
  359. case {'cukx:'}
  360. varargout = {sf_tol(sf_cukx(sX),sX.tol)};
  361. end
  362. case 3
  363. %- check dimensions of Y
  364. Y = varargin{3};
  365. if isempty(Y), varargout = {spm_sp(action,sX)}; return, end
  366. if size(Y,1) ~= sf_s2(sX), error(['Dim dont match ' action]); end
  367. switch lower(action),
  368. case {'cukx'}
  369. varargout = { sf_cukx(sX)*Y };
  370. case {'cukx:'}
  371. varargout = {sf_tol(sf_cukx(sX)*Y,sX.tol)};
  372. end
  373. otherwise
  374. error(['too many input argument in spm_sp ' action]);
  375. end % switch nargin
  376. case {'rk'} %- Returns rank
  377. %=======================================================================
  378. varargout = { sf_rk(sX) };
  379. case {'ox', 'oxp'} %-Orthonormal basis sets for X / X'
  380. %=======================================================================
  381. % oX = spm_sp('ox', x)
  382. % oXp = spm_sp('oxp',x)
  383. if sf_rk(sX) > 0
  384. switch lower(action)
  385. case 'ox'
  386. varargout = {sf_uk(sX)};
  387. case 'oxp'
  388. varargout = {sf_vk(sX)};
  389. end
  390. else
  391. switch lower(action)
  392. case 'ox'
  393. varargout = {zeros(sf_s1(sX),1)};
  394. case 'oxp'
  395. varargout = {zeros(sf_s2(sX),1)};
  396. end
  397. end
  398. case {'x', 'xp'} %- X / X' robust to spm_sp changes
  399. %=======================================================================
  400. % X = spm_sp('x', x)
  401. % X' = spm_sp('xp',x)
  402. switch lower(action)
  403. case 'x', varargout = {sX.X};
  404. case 'xp', varargout = {sX.X'};
  405. end
  406. case {'xi', 'xpi'} %- X(:,i) / X'(:,i) robust to spm_sp changes
  407. %=======================================================================
  408. % X = spm_sp('xi', x)
  409. % X' = spm_sp('xpi',x)
  410. i = varargin{3}; % NO CHECKING on i !!! assumes correct
  411. switch lower(action)
  412. case 'xi', varargout = {sX.X(:,i)};
  413. case 'xpi', varargout = {sX.X(i,:)'};
  414. end
  415. case {'uk','uk:'} %- Returns u(:,1:r)
  416. %=======================================================================
  417. % pX = spm_sp('uk',x)
  418. % Notice the difference with 'ox' : 'ox' always returns a basis of the
  419. % proper siwe while this returns empty if rank is null
  420. warning('can''t you use ox ?');
  421. switch nargin
  422. case 2
  423. switch lower(action),
  424. case {'uk'}
  425. varargout = { sf_uk(sX) };
  426. case {'uk:'}
  427. varargout = { sf_tol(sf_uk(sX),sX.tol) };
  428. end
  429. case 3
  430. %- check dimensions of Y
  431. Y = varargin{3};
  432. if isempty(Y), varargout = {spm_sp(action,sX)}; return, end
  433. if size(Y,1) ~= sf_rk(sX), error(['Dim dont match ' action]); end
  434. switch lower(action),
  435. case {'uk'}
  436. varargout = { sf_uk(sX)*Y };
  437. case {'uk:'}
  438. varargout = {sf_tol(sf_uk(sX)*Y,sX.tol)};
  439. end
  440. otherwise
  441. error(['too many input argument in spm_sp ' action]);
  442. end % switch nargin
  443. case {'pinvxpx', 'xpx-', 'pinvxpx:', 'xpx-:',} %- Pseudo-inv of (X'X)
  444. %=======================================================================
  445. % pXpX = spm_sp('pinvxpx',x [,Y])
  446. switch nargin
  447. case 2
  448. switch lower(action),
  449. case {'xpx-','pinvxpx'}
  450. varargout = {sf_pinvxpx(sX)};
  451. case {'xpx-:','pinvxpx:'}
  452. varargout = {sf_tol(sf_pinvxpx(sX),sX.tol)};
  453. end %-
  454. case 3
  455. %- check dimensions of Y
  456. Y = varargin{3};
  457. if isempty(Y), varargout = {spm_sp(action,sX)}; return, end
  458. if size(Y,1) ~= sf_s2(sX), error('Dim dont match'); end;
  459. switch lower(action),
  460. case {'xpx-','pinvxpx'}
  461. varargout = {sf_pinvxpx(sX)*Y};
  462. case {'xpx-:','pinvxpx:'}
  463. varargout = {sf_tol(sf_pinvxpx(sX)*Y,sX.tol)};
  464. end %-
  465. otherwise
  466. error('too many input argument in spm_sp');
  467. end % switch nargin
  468. case {'xpx','xpx:'} %-Computation of (X'*X)
  469. %=======================================================================
  470. % XpX = spm_sp('xpx',x [,Y])
  471. switch nargin
  472. case 2
  473. switch lower(action),
  474. case {'xpx'}
  475. varargout = {sf_xpx(sX)};
  476. case {'xpx:'}
  477. varargout = {sf_tol(sf_xpx(sX),sX.tol)};
  478. end %-
  479. case 3
  480. %- check dimensions of Y
  481. Y = varargin{3};
  482. if isempty(Y), varargout = {spm_sp(action,sX)}; return, end
  483. if size(Y,1) ~= sf_s2(sX), error('Dim dont match'); end;
  484. switch lower(action),
  485. case {'xpx'}
  486. varargout = {sf_xpx(sX)*Y};
  487. case {'xpx:'}
  488. varargout = {sf_tol(sf_xpx(sX)*Y,sX.tol)};
  489. end %-
  490. otherwise
  491. error('too many input argument in spm_sp');
  492. end % switch nargin
  493. case {'cx->cu','cx->cu:'} %-coordinates in the basis of X -> basis u
  494. %=======================================================================
  495. %
  496. % returns cu such that sX.X*cx == sX.u*cu
  497. switch nargin
  498. case 2
  499. switch lower(action),
  500. case {'cx->cu'}
  501. varargout = {sf_cxtwdcu(sX)};
  502. case {'cx->cu:'}
  503. varargout = {sf_tol(sf_cxtwdcu(sX),sX.tol)};
  504. end %-
  505. case 3
  506. %- check dimensions of Y
  507. Y = varargin{3};
  508. if isempty(Y), varargout = {spm_sp(action,sX)}; return, end
  509. if size(Y,1) ~= sf_s2(sX), error('Dim dont match'); end;
  510. switch lower(action),
  511. case {'cx->cu'}
  512. varargout = {sf_cxtwdcu(sX)*Y};
  513. case {'cx->cu:'}
  514. varargout = {sf_tol(sf_cxtwdcu(sX)*Y,sX.tol)};
  515. end %-
  516. otherwise
  517. error('too many input argument in spm_sp');
  518. end % switch nargin
  519. case {'xxp-','xxp-:','pinvxxp','pinvxxp:'} %-Pseudo-inverse of (XX')
  520. %=======================================================================
  521. % pXXp = spm_sp('pinvxxp',x [,Y])
  522. switch nargin
  523. case 2
  524. switch lower(action),
  525. case {'xxp-','pinvxxp'}
  526. varargout = {sf_pinvxxp(sX)};
  527. case {'xxp-:','pinvxxp:'}
  528. varargout = {sf_tol(sf_pinvxxp(sX),sX.tol)};
  529. end %-
  530. case 3
  531. %- check dimensions of Y
  532. Y = varargin{3};
  533. if isempty(Y), varargout = {spm_sp(action,sX)}; return, end
  534. if size(Y,1) ~= sf_s1(sX), error('Dim dont match'); end;
  535. switch lower(action),
  536. case {'xxp-','pinvxxp'}
  537. varargout = {sf_pinvxxp(sX)*Y};
  538. case {'xxp-:','pinvxxp:'}
  539. varargout = {sf_tol(sf_pinvxxp(sX)*Y,sX.tol)};
  540. end %-
  541. otherwise
  542. error('too many input argument in spm_sp');
  543. end % switch nargin
  544. case {'xxp','xxp:'} %-Computation of (X*X')
  545. %=======================================================================
  546. % XXp = spm_sp('xxp',x)
  547. switch nargin
  548. case 2
  549. switch lower(action),
  550. case {'xxp'}
  551. varargout = {sf_xxp(sX)};
  552. case {'xxp:'}
  553. varargout = {sf_tol(sf_xxp(sX),sX.tol)};
  554. end %-
  555. case 3
  556. %- check dimensions of Y
  557. Y = varargin{3};
  558. if isempty(Y), varargout = {spm_sp(action,sX)}; return, end
  559. if size(Y,1) ~= sf_s1(sX), error('Dim dont match'); end;
  560. switch lower(action),
  561. case {'xxp'}
  562. varargout = {sf_xxpY(sX,Y)};
  563. case {'xxp:'}
  564. varargout = {sf_tol(sf_xxpY(sX,Y),sX.tol)};
  565. end %-
  566. otherwise
  567. error('too many input argument in spm_sp');
  568. end % switch nargin
  569. case {'^p','^p:'} %-Computation of v*(diag(s.^n))*v'
  570. %=======================================================================
  571. switch nargin
  572. case {2,3}
  573. if nargin==2, n = 1; else n = varargin{3}; end;
  574. if ~isnumeric(n), error('~isnumeric(n)'), end;
  575. switch lower(action),
  576. case {'^p'}
  577. varargout = {sf_jbp(sX,n)};
  578. case {'^p:'}
  579. varargout = {sf_tol(sf_jbp(sX,n),sX.tol)};
  580. end %-
  581. case 4
  582. n = varargin{3};
  583. if ~isnumeric(n), error('~isnumeric(n)'), end;
  584. Y = varargin{4};
  585. if isempty(Y), varargout = {spm_sp(action,sX,n)}; return, end
  586. if size(Y,1) ~= sf_s2(sX), error('Dim dont match'); end;
  587. switch lower(action),
  588. case {'^p'}
  589. varargout = {sf_jbp(sX,n)*Y};
  590. case {'^p:'}
  591. varargout = {sf_tol(sf_jbp(sX,n)*Y,sX.tol)};
  592. end %-
  593. otherwise
  594. error('too many input argument in spm_sp');
  595. end % switch nargin
  596. case {'^','^:'} %-Computation of v*(diag(s.^n))*v'
  597. %=======================================================================
  598. switch nargin
  599. case {2,3}
  600. if nargin==2, n = 1; else n = varargin{3}; end;
  601. if ~isnumeric(n), error('~isnumeric(n)'), end;
  602. switch lower(action),
  603. case {'^'}
  604. varargout = {sf_jb(sX,n)};
  605. case {'^:'}
  606. varargout = {sf_tol(sf_jb(sX,n),sX.tol)};
  607. end %-
  608. case 4
  609. n = varargin{3};
  610. if ~isnumeric(n), error('~isnumeric(n)'), end;
  611. Y = varargin{4};
  612. if isempty(Y), varargout = {spm_sp(action,sX,n)}; return, end
  613. if size(Y,1) ~= sf_s1(sX), error('Dim dont match'); end;
  614. switch lower(action),
  615. case {'^'}
  616. varargout = {sf_jbY(sX,n,Y)};
  617. case {'^:'}
  618. varargout = {sf_tol(sf_jbY(sX,n,Y),sX.tol)};
  619. end %-
  620. otherwise
  621. error('too many input argument in spm_sp');
  622. end % switch nargin
  623. case {'n'} %-Null space of sX
  624. %=======================================================================
  625. switch nargin
  626. case 2
  627. varargout = {sf_n(sX)};
  628. otherwise
  629. error('too many input argument in spm_sp');
  630. end % switch nargin
  631. case {'np'} %-Null space of sX'
  632. %=======================================================================
  633. switch nargin
  634. case 2
  635. varargout = {sf_n(sf_transp(sX))};
  636. otherwise
  637. error('too many input argument in spm_sp');
  638. end % switch nargin
  639. case {'nop', 'nop:'} %- project(or)(ion) into null space
  640. %=======================================================================
  641. %
  642. %
  643. %
  644. switch nargin
  645. case 2
  646. switch lower(action),
  647. case {'nop'}
  648. n = sf_n(sX);
  649. varargout = {n*n'};
  650. case {'nop:'}
  651. n = sf_n(sX);
  652. varargout = {sf_tol(n*n',sX.tol)};
  653. end %-
  654. case 3
  655. %- check dimensions of Y
  656. Y = varargin{3};
  657. if isempty(Y), varargout = {spm_sp(action,sX)}; return, end
  658. if size(Y,1) ~= sf_s2(sX), error('Dim dont match'); end;
  659. switch lower(action),
  660. case {'nop'}
  661. n = sf_n(sX);
  662. varargout = {n*(n'*Y)};
  663. case {'nop:'}
  664. n = sf_n(sX);
  665. varargout = {sf_tol(n*(n'*Y),sX.tol)};
  666. end %-
  667. otherwise
  668. error('too many input argument in spm_sp');
  669. end % switch nargin
  670. case {'nopp', 'nopp:'} %- projector(ion) into null space of X'
  671. %=======================================================================
  672. %
  673. %
  674. switch nargin
  675. case 2
  676. switch lower(action),
  677. case {'nopp'}
  678. varargout = {spm_sp('nop',sf_transp(sX))};
  679. case {'nopp:'}
  680. varargout = {spm_sp('nop:',sf_transp(sX))};
  681. end %-
  682. case 3
  683. switch lower(action),
  684. case {'nopp'}
  685. varargout = {spm_sp('nop',sf_transp(sX),varargin{3})};
  686. case {'nopp:'}
  687. varargout = {spm_sp('nop:',sf_transp(sX),varargin{3})};
  688. end %-
  689. otherwise
  690. error('too many input argument in spm_sp');
  691. end % switch nargin
  692. case {'res', 'r','r:'} %-Residual formaing matrix / residuals
  693. %=======================================================================
  694. % r = spm_sp('res',sX[,Y])
  695. %
  696. %- 'res' will become obsolete : use 'r' or 'r:' instead
  697. %- At some stage, should be merged with 'nop'
  698. switch nargin
  699. case 2
  700. switch lower(action)
  701. case {'r','res'}
  702. varargout = {sf_r(sX)};
  703. case {'r:','res:'}
  704. varargout = {sf_tol(sf_r(sX),sX.tol)};
  705. end %-
  706. case 3
  707. %- check dimensions of Y
  708. Y = varargin{3};
  709. if isempty(Y), varargout = {spm_sp(action,sX)}; return, end
  710. if size(Y,1) ~= sf_s1(sX), error('Dim dont match'); end;
  711. switch lower(action)
  712. case {'r','res'}
  713. varargout = {sf_rY(sX,Y)};
  714. case {'r:','res:'}
  715. varargout = {sf_tol(sf_rY(sX,Y),sX.tol)};
  716. end %-
  717. otherwise
  718. error('too many input argument in spm_sp');
  719. end % switch nargin
  720. case {':'}
  721. %=======================================================================
  722. % spm_sp(':',sX [,Y [,tol]])
  723. %- Sets Y and tol according to arguments
  724. if nargin > 4
  725. error('too many input argument in spm_sp');
  726. else
  727. if nargin > 3
  728. if isnumeric(varargin{4}), tol = varargin{4};
  729. else error('tol must be numeric');
  730. end
  731. else
  732. tol = sX.tol;
  733. end
  734. if nargin > 2
  735. Y = varargin{3}; %- if isempty, returns empty, end
  736. else
  737. Y = sX.X;
  738. end
  739. end
  740. varargout = {sf_tol(Y,tol)};
  741. case {'isinsp', 'isinspp'} %- is in space or is in dual space
  742. %=======================================================================
  743. % b = spm_sp('isinsp',x,c[,tol])
  744. % b = spm_sp('isinspp',x,c[,tol])
  745. %-Check whether vectors are in row/column space of X
  746. %-Check arguments
  747. %-----------------------------------------------------------------------
  748. if nargin<3, error('insufficient arguments - action,x,c required'), end
  749. c = varargin{3}; %- if isempty(c), dim wont match exept for empty sp.
  750. if nargin<4, tol=sX.tol; else, tol = varargin{4}; end
  751. %-Compute according to case
  752. %-----------------------------------------------------------------------
  753. switch lower(action)
  754. case 'isinsp'
  755. %-Check dimensions
  756. if size(sX.X,1) ~= size(c,1)
  757. warning('Vector dim don''t match col. dim : not in space !');
  758. varargout = { 0 }; return;
  759. end
  760. varargout = {all(all( abs(sf_op(sX)*c - c) <= tol ))};
  761. case 'isinspp'
  762. %- check dimensions
  763. if size(sX.X,2) ~= size(c,1)
  764. warning('Vector dim don''t match row dim : not in space !');
  765. varargout = { 0 }; return;
  766. end
  767. varargout = {all(all( abs(sf_opp(sX)*c - c) <= tol ))};
  768. end
  769. case {'eachinsp', 'eachinspp'} %- each column of c in space or in dual space
  770. %=======================================================================
  771. % b = spm_sp('eachinsp',x,c[,tol])
  772. % b = spm_sp('eachinspp',x,c[,tol])
  773. %-Check whether vectors are in row/column space of X
  774. %-Check arguments
  775. %-----------------------------------------------------------------------
  776. if nargin<3, error('insufficient arguments - action,x,c required'), end
  777. c = varargin{3}; %- if isempty(c), dim wont match exept for empty sp.
  778. if nargin<4, tol=sX.tol; else, tol = varargin{4}; end
  779. %-Compute according to case
  780. %-----------------------------------------------------------------------
  781. switch lower(action)
  782. case 'eachinsp'
  783. %-Check dimensions
  784. if size(sX.X,1) ~= size(c,1)
  785. warning('Vector dim don''t match col. dim : not in space !');
  786. varargout = { 0 }; return;
  787. end
  788. varargout = {all( abs(sf_op(sX)*c - c) <= tol )};
  789. case 'eachinspp'
  790. %- check dimensions
  791. if size(sX.X,2) ~= size(c,1)
  792. warning('Vector dim don''t match row dim : not in space !');
  793. varargout = { 0 }; return;
  794. end
  795. varargout = {all( abs(sf_opp(sX)*c - c) <= tol )};
  796. end
  797. case '==' % test wether two spaces are the same
  798. %=======================================================================
  799. % b = spm_sp('==',x1,X2)
  800. if nargin~=3, error('too few/many input arguments - need 3');
  801. else X2 = varargin{3}; end;
  802. if isempty(sX.X)
  803. if isempty(X2),
  804. warning('Both spaces empty');
  805. varargout = { 1 };
  806. else
  807. warning('one space empty');
  808. varargout = { 0 };
  809. end;
  810. else
  811. x2 = spm_sp('Set',X2);
  812. maxtol = max(sX.tol,x2.tol);
  813. varargout = { all( spm_sp('isinsp',sX,X2,maxtol)) & ...
  814. all( spm_sp('isinsp',x2,sX.X,maxtol) ) };
  815. %- I have encountered one case where the max of tol was needed.
  816. end;
  817. case 'isspc' %-Space structure check
  818. %=======================================================================
  819. % [b,str] = spm_sp('isspc',x)
  820. if nargin~=2, error('too few/many input arguments - need 2'), end
  821. %-Check we've been passed a structure
  822. if ~isstruct(varargin{2}), varargout={0}; return, end
  823. %-Go through required field names checking their existance
  824. % (Get fieldnames once and compare: isfield doesn't work for multiple )
  825. % (fields, and repeated calls to isfield would result in unnecessary )
  826. % (replicate calls to fieldnames(varargin{2}). )
  827. b = 1;
  828. fnames = fieldnames(varargin{2});
  829. for str = fieldnames(sf_create)'
  830. b = b & any(strcmp(str,fnames));
  831. if ~b, break, end
  832. end
  833. if nargout > 1,
  834. if b, str = 'ok'; else, str = 'not a space'; end;
  835. varargout = {b,str};
  836. else, varargout = {b}; end;
  837. case 'issetspc' %-Is this a completed space structure?
  838. %=======================================================================
  839. % [b,e] = spm_sp('issetspc',x)
  840. if nargin~=2, error('too few/many input arguments - need 2'), end
  841. if ~spm_sp('isspc',varargin{2})
  842. varargout = {0,'not a space structure (wrong fieldnames)'};
  843. elseif ~sf_isset(varargin{2})
  844. %-Basic fields aren't filled in
  845. varargout = {0,'space not defined (use ''set'')'};
  846. else
  847. varargout = {1,'OK!'};
  848. end
  849. case 'size' %- gives the size of sX
  850. %=======================================================================
  851. % size = spm_sp('size',x,dim)
  852. %
  853. if nargin > 3, error('too many input arguments'), end
  854. if nargin == 2, dim = []; else dim = varargin{3}; end
  855. if ~isempty(dim)
  856. switch dim
  857. case 1, varargout = { sf_s1(sX) };
  858. case 2, varargout = { sf_s2(sX) };
  859. otherwise, error(['unknown dimension in ' action]);
  860. end
  861. else %- assumes want both dim
  862. switch nargout
  863. case {0,1}
  864. varargout = { sf_si(sX) };
  865. case 2
  866. varargout = { sf_s1(sX), sf_s2(sX) };
  867. otherwise
  868. error(['too many output arg in ' mfilename ' ' action]);
  869. end
  870. end
  871. otherwise
  872. %=======================================================================
  873. error(['Invalid action (',action,')'])
  874. %=======================================================================
  875. end % (case lower(action))
  876. %=======================================================================
  877. %- S U B - F U N C T I O N S
  878. %=======================================================================
  879. %
  880. % The rule I tried to follow is that the space structure is accessed
  881. % only in this sub function part : any sX.whatever should be
  882. % prohibited elsewhere .... still a lot to clean !!!
  883. function x = sf_create
  884. %=======================================================================
  885. x = struct(...
  886. 'X', [],... % Matrix
  887. 'tol', [],... % tolerance
  888. 'ds', [],... % vectors of singular values
  889. 'u', [],... % u as in X = u*diag(ds)*v'
  890. 'v', [], ... % v as in X = u*diag(ds)*v'
  891. 'rk', [],... % rank
  892. 'oP', [],... % orthogonal projector on X
  893. 'oPp', [],... % orthogonal projector on X'
  894. 'ups', [],... % space in which this one is embeded
  895. 'sus', []); % subspace
  896. function x = sf_set(X)
  897. %=======================================================================
  898. x = sf_create;
  899. x.X = X;
  900. %-- Compute the svd with svd(X,0) : find all the singular values of x.X
  901. %-- SVD(FULL(A)) will usually perform better than SVDS(A,MIN(SIZE(A)))
  902. %- if more column that lines, performs on X'
  903. if size(X,1) < size(X,2)
  904. [x.v, s, x.u] = svd(full(X'),0);
  905. else
  906. [x.u, s, x.v] = svd(full(X),0);
  907. end
  908. x.ds = diag(s); clear s;
  909. %-- compute the tolerance
  910. x.tol = max(size(x.X))*max(abs(x.ds))*eps;
  911. %-- compute the rank
  912. x.rk = sum(x.ds > x.tol);
  913. function x = sf_transp(x)
  914. %=======================================================================
  915. %
  916. %- Tranpspose the space : note that tmp is not touched, therefore
  917. %- only contains the address, no duplication of data is performed.
  918. x.X = x.X';
  919. tmp = x.v;
  920. x.v = x.u;
  921. x.u = tmp;
  922. tmp = x.oP;
  923. x.oP = x.oPp;
  924. x.oPp = tmp;
  925. clear tmp;
  926. function b = sf_isset(x)
  927. %=======================================================================
  928. b = ~( isempty(x.X) |...
  929. isempty(x.u) |...
  930. isempty(x.v) |...
  931. isempty(x.ds) |...
  932. isempty(x.tol) |...
  933. isempty(x.rk) );
  934. function s1 = sf_s1(x)
  935. %=======================================================================
  936. s1 = size(x.X,1);
  937. function s2 = sf_s2(x)
  938. %=======================================================================
  939. s2 = size(x.X,2);
  940. function si = sf_si(x)
  941. %=======================================================================
  942. si = size(x.X);
  943. function r = sf_rk(x)
  944. %=======================================================================
  945. r = x.rk;
  946. function uk = sf_uk(x)
  947. %=======================================================================
  948. uk = x.u(:,1:sf_rk(x));
  949. function vk = sf_vk(x)
  950. %=======================================================================
  951. vk = x.v(:,1:sf_rk(x));
  952. function sk = sf_sk(x)
  953. %=======================================================================
  954. sk = x.ds(1:sf_rk(x));
  955. function t = sf_t(x)
  956. %=======================================================================
  957. t = x.tol;
  958. function x = sf_tol(x,t)
  959. %=======================================================================
  960. x(abs(x) < t) = 0;
  961. function op = sf_op(sX)
  962. %=======================================================================
  963. if sX.rk > 0
  964. op = sX.u(:,[1:sX.rk])*sX.u(:,[1:sX.rk])';
  965. else
  966. op = zeros( size(sX.X,1) );
  967. end;
  968. %!!!! to implement : a clever version of sf_opY (see sf_rY)
  969. function opp = sf_opp(sX)
  970. %=======================================================================
  971. if sX.rk > 0
  972. opp = sX.v(:,[1:sX.rk])*sX.v(:,[1:sX.rk])';
  973. else
  974. opp = zeros( size(sX.X,2) );
  975. end;
  976. %!!!! to implement : a clever version of sf_oppY (see sf_rY)
  977. function px = sf_pinv(sX)
  978. %=======================================================================
  979. r = sX.rk;
  980. if r > 0
  981. px = sX.v(:,1:r)*diag( ones(r,1)./sX.ds(1:r) )*sX.u(:,1:r)';
  982. else
  983. px = zeros(size(sX.X,2),size(sX.X,1));
  984. end
  985. function px = sf_pinvxp(sX)
  986. %=======================================================================
  987. r = sX.rk;
  988. if r > 0
  989. px = sX.u(:,1:r)*diag( ones(r,1)./sX.ds(1:r) )*sX.v(:,1:r)';
  990. else
  991. px = zeros(size(sX.X));
  992. end
  993. function px = sf_pinvxpx(sX)
  994. %=======================================================================
  995. r = sX.rk;
  996. if r > 0
  997. px = sX.v(:,1:r)*diag( sX.ds(1:r).^(-2) )*sX.v(:,1:r)';
  998. else
  999. px = zeros(size(sX.X,2));
  1000. end
  1001. function px = sf_jbp(sX,n)
  1002. %=======================================================================
  1003. r = sX.rk;
  1004. if r > 0
  1005. px = sX.v(:,1:r)*diag( sX.ds(1:r).^(n) )*sX.v(:,1:r)';
  1006. else
  1007. px = zeros(size(sX.X,2));
  1008. end
  1009. function x = sf_jb(sX,n)
  1010. %=======================================================================
  1011. r = sX.rk;
  1012. if r > 0
  1013. x = sX.u(:,1:r)*diag( sX.ds(1:r).^(n) )*sX.u(:,1:r)';
  1014. else
  1015. x = zeros(size(sX.X,1));
  1016. end
  1017. function y = sf_jbY(sX,n,Y)
  1018. %=======================================================================
  1019. r = sX.rk;
  1020. if r > 0
  1021. y = ( sX.u(:,1:r)*diag(sX.ds(1:r).^n) )*(sX.u(:,1:r)'*Y);
  1022. else
  1023. y = zeros(size(sX.X,1),size(Y,2));
  1024. end
  1025. %!!!! to implement : a clever version of sf_jbY (see sf_rY)
  1026. function x = sf_cxtwdcu(sX)
  1027. %=======================================================================
  1028. %- coordinates in sX.X -> coordinates in sX.u(:,1:rk)
  1029. x = diag(sX.ds)*sX.v';
  1030. function x = sf_cukpinvxp(sX)
  1031. %=======================================================================
  1032. %- coordinates of pinv(sX.X') in the basis sX.u(:,1:rk)
  1033. r = sX.rk;
  1034. if r > 0
  1035. x = diag( ones(r,1)./sX.ds(1:r) )*sX.v(:,1:r)';
  1036. else
  1037. x = zeros( size(sX.X,2) );
  1038. end
  1039. function x = sf_cukx(sX)
  1040. %=======================================================================
  1041. %- coordinates of sX.X in the basis sX.u(:,1:rk)
  1042. r = sX.rk;
  1043. if r > 0
  1044. x = diag( sX.ds(1:r) )*sX.v(:,1:r)';
  1045. else
  1046. x = zeros( size(sX.X,2) );
  1047. end
  1048. function x = sf_xpx(sX)
  1049. %=======================================================================
  1050. r = sX.rk;
  1051. if r > 0
  1052. x = sX.v(:,1:r)*diag( sX.ds(1:r).^2 )*sX.v(:,1:r)';
  1053. else
  1054. x = zeros(size(sX.X,2));
  1055. end
  1056. function x = sf_xxp(sX)
  1057. %=======================================================================
  1058. r = sX.rk;
  1059. if r > 0
  1060. x = sX.u(:,1:r)*diag( sX.ds(1:r).^2 )*sX.u(:,1:r)';
  1061. else
  1062. x = zeros(size(sX.X,1));
  1063. end
  1064. function x = sf_xxpY(sX,Y)
  1065. %=======================================================================
  1066. r = sX.rk;
  1067. if r > 0
  1068. x = sX.u(:,1:r)*diag( sX.ds(1:r).^2 )*(sX.u(:,1:r)'*Y);
  1069. else
  1070. x = zeros(size(sX.X,1),size(Y,2));
  1071. end
  1072. function x = sf_pinvxxp(sX)
  1073. %=======================================================================
  1074. r = sX.rk;
  1075. if r > 0
  1076. x = sX.u(:,1:r)*diag( sX.ds(1:r).^(-2) )*sX.u(:,1:r)';
  1077. else
  1078. x = zeros(size(sX.X,1));
  1079. end
  1080. function n = sf_n(sX)
  1081. %=======================================================================
  1082. % if the null space is in sX.v, returns it
  1083. % otherwise, performs Gramm Schmidt orthogonalisation.
  1084. %
  1085. %
  1086. r = sX.rk;
  1087. [q,p]= size(sX.X);
  1088. if r > 0
  1089. if q >= p %- the null space is entirely in v
  1090. if r == p, n = zeros(p,1); else n = sX.v(:,r+1:p); end
  1091. else %- only part of n is in v: same as computing the null sp of sX'
  1092. n = null(sX.X);
  1093. %----- BUG !!!! in that part ----------------------------------------
  1094. %- v = zeros(p,p-q); j = 1; i = 1; z = zeros(p,1);
  1095. %- while i <= p
  1096. %- o = z; o(i) = 1; vpoi = [sX.v(i,:) v(i,1:j-1)]';
  1097. %- o = sf_tol(o - [sX.v v(:,1:j-1)]*vpoi,sX.tol);
  1098. %- if any(o), v(:,j) = o/((o'*o)^(1/2)); j = j + 1; end;
  1099. %- i = i + 1; %- if i>p, error('gramm shmidt error'); end;
  1100. %- end
  1101. %- n = [sX.v(:,r+1:q) v];
  1102. %--------------------------------------------------------------------
  1103. end
  1104. else
  1105. n = eye(p);
  1106. end
  1107. function r = sf_r(sX)
  1108. %=======================================================================
  1109. %-
  1110. %- returns the residual forming matrix for the space sX
  1111. %- for internal use. doesn't Check whether oP exist.
  1112. r = eye(size(sX.X,1)) - sf_op(sX) ;
  1113. function Y = sf_rY(sX,Y)
  1114. %=======================================================================
  1115. % r = spm_sp('r',sX[,Y])
  1116. %
  1117. %- tries to minimise the computation by looking whether we should do
  1118. %- I - u*(u'*Y) or n*(n'*Y) as in 'nop'
  1119. r = sX.rk;
  1120. [q,p]= size(sX.X);
  1121. if r > 0 %- else returns the input;
  1122. if r < q-r %- we better do I - u*u'
  1123. Y = Y - sX.u(:,[1:r])*(sX.u(:,[1:r])'*Y); % warning('route1');
  1124. else
  1125. %- is it worth computing the n ortho basis ?
  1126. if size(Y,2) < 5*q
  1127. Y = sf_r(sX)*Y; % warning('route2');
  1128. else
  1129. n = sf_n(sf_transp(sX)); % warning('route3');
  1130. Y = n*(n'*Y);
  1131. end
  1132. end
  1133. end