spm_FcUtil.m 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933
  1. function varargout = spm_FcUtil(varargin)
  2. % Contrast utilities
  3. % FORMAT varargout = spm_FcUtil(action,varargin)
  4. %_______________________________________________________________________
  5. %
  6. % spm_FcUtil is a multi-function function containing various utilities
  7. % for contrast construction and manipulation. In general, it accepts
  8. % design matrices as plain matrices or as space structures setup by
  9. % spm_sp (that is preferable in general).
  10. %
  11. % The use of spm_FcUtil should help with robustness issues and
  12. % maintainability of SPM. % Note that when space structures are passed
  13. % as arguments is is assummed that their basic fields are filled in.
  14. % See spm_sp for details of (design) space structures and their
  15. % manipulation.
  16. %
  17. %
  18. % ======================================================================
  19. % case 'fconfields' %- fields of F contrast
  20. % Fc = spm_FcUtil('FconFields')
  21. %
  22. %- simply returns the fields of a contrast structure.
  23. %
  24. %=======================================================================
  25. % case 'set' %- Create an F contrast
  26. % Fc = spm_FcUtil('Set',name, STAT, set_action, value, sX)
  27. %
  28. %- Set will fill in the contrast structure, in particular
  29. %- c (in the contrast space), X1o (the space actually tested) and
  30. %- X0 (the space left untested), such that space([X1o X0]) == sX.
  31. %- STAT is either 'F' or 'T';
  32. %- name is a string descibing the contrast.
  33. %
  34. %- There are three ways to set a contrast :
  35. %- set_action is 'c','c+' : value can then be zeros.
  36. %- dimensions are in X',
  37. %- f c+ is used, value is projected onto sX';
  38. %- iX0 is set to 'c' or 'c+';
  39. %- set_action is 'iX0' : defines the indices of the columns
  40. %- that will not be tested. Can be empty.
  41. %- set_action is 'X0' : defines the space that will remain
  42. %- unchanged. The orthogonal complement is
  43. %- tested; iX0 is set to 'X0';
  44. %-
  45. %=======================================================================
  46. % case 'isfcon' %- Is it an F contrast ?
  47. % b = spm_FcUtil('IsFcon',Fc)
  48. %
  49. %=======================================================================
  50. % case 'fconedf' %- F contrast edf
  51. % [edf_tsp edf_Xsp] = spm_FcUtil('FconEdf', Fc, sX [, V])
  52. %
  53. %- compute the effective degrees of freedom of the numerator edf_tsp
  54. %- and (optionally) the denominator edf_Xsp of the contrast.
  55. %
  56. %=======================================================================
  57. % case 'hsqr' %-Extra sum of squares sqr matrix for beta's from contrast
  58. % hsqr = spm_FcUtil('Hsqr',Fc, sX)
  59. %
  60. %- This computes the matrix hsqr such that a the numerator of an F test
  61. %- will be beta'*hsqr'*hsqr*beta
  62. %
  63. %=======================================================================
  64. % case 'h' %-Extra sum of squares matrix for beta's from contrast
  65. % H = spm_FcUtil('H',Fc, sX)
  66. %
  67. %- This computes the matrix H such that a the numerator of an F test
  68. %- will be beta'*H*beta
  69. %-
  70. %=======================================================================
  71. % case 'yc' %- Fitted data corrected for confounds defined by Fc
  72. % Yc = spm_FcUtil('Yc',Fc, sX, b)
  73. %
  74. %- Input : b : the betas
  75. %- Returns the corrected data Yc for given contrast. Y = Yc + Y0 + error
  76. %
  77. %=======================================================================
  78. % case 'y0' %- Confounds data defined by Fc
  79. % Y0 = spm_FcUtil('Y0',Fc, sX, b)
  80. %
  81. %- Input : b : the betas
  82. %- Returns the confound data Y0 for a given contrast. Y = Yc + Y0 + error
  83. %
  84. %=======================================================================
  85. % case {'|_'} %- Fc orthogonalisation
  86. % Fc = spm_FcUtil('|_',Fc1, sX, Fc2)
  87. %
  88. %- Orthogonolise a (list of) contrasts Fc1 wrt a (list of) contrast Fc2
  89. %- such that the space these contrasts test are orthogonal.
  90. %- If contrasts are not estimable contrasts, works with the estimable
  91. %- part. In any case, returns estimable contrasts.
  92. %
  93. %=======================================================================
  94. % case {'|_?'} %- Are contrasts orthogonals
  95. % b = spm_FcUtil('|_?',Fc1, sX [, Fc2])
  96. %
  97. %- Tests whether a (list of) contrast is orthogonal. Works with the
  98. %- estimable part if they are not estimable. With only one argument,
  99. %- tests whether the list is made of orthogonal contrasts. With Fc2
  100. %- provided, tests whether the two (list of) contrast are orthogonal.
  101. %
  102. %=======================================================================
  103. % case 'in' %- Fc1 is in list of contrasts Fc2
  104. % [iFc2 iFc1] = spm_FcUtil('In', Fc1, sX, Fc2)
  105. %
  106. %- Tests wether a (list of) contrast Fc1 is in a list of contrast Fc2.
  107. %- returns the indices iFc2 where element of Fc1 have been found
  108. %- in Fc2 and the indices iFc1 of the element of Fc1 found in Fc2.
  109. %- These indices are not necessarily unique.
  110. %
  111. %=======================================================================
  112. % case '~unique' %- Fc list unique
  113. % idx = spm_FcUtil('~unique', Fc, sX)
  114. %
  115. %- returns indices ofredundant contrasts in Fc
  116. %- such that Fc(idx) = [] makes Fc unique.
  117. %
  118. %=======================================================================
  119. % case {'0|[]','[]|0'} %- Fc is null or empty
  120. % b = spm_FcUtil('0|[]', Fc, sX)
  121. %
  122. %- NB : for the "null" part, checks if the contrast is in the null space
  123. %- of sX (completely non estimable !)
  124. %=======================================================================
  125. %
  126. %_______________________________________________________________________
  127. % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging
  128. % Jean-Baptiste Poline
  129. % $Id: spm_FcUtil.m 5219 2013-01-29 17:07:07Z spm $
  130. %-Format arguments
  131. %-----------------------------------------------------------------------
  132. if nargin==0, error('do what? no arguments given...')
  133. else action = varargin{1}; end
  134. switch lower(action),
  135. case 'fconfields' %- fields of F contrast
  136. %=======================================================================
  137. % Fc = spm_FcUtil('FconFields')
  138. if nargout > 1, error('Too many output arguments: FconFields'), end
  139. if nargin > 1, error('Too many input arguments: FconFields'), end
  140. varargout = {sf_FconFields};
  141. case {'set','v1set'} %- Create an F contrast
  142. %=======================================================================
  143. % Fc = spm_FcUtil('Set',name, STAT, set_action, value, sX)
  144. %
  145. % Sets the contrast structure with set_action either 'c', 'X0' or 'iX0'
  146. % resp. for a contrast, the null hyp. space or the indices of which.
  147. % STAT can be 'T' or 'F'.
  148. %
  149. % If not set by iX0 (in which case field .iX0 containes the indices),
  150. % field .iX0 is set as a string containing the set_action: {'X0','c','c+','ukX0'}
  151. %
  152. % if STAT is T, then set_action should be 'c' or 'c+'
  153. % (at the moment, just a warning...)
  154. % if STAT is T and set_action is 'c' or 'c+', then
  155. % checks whether it is a real T.
  156. %
  157. % 'v1set' is NOT provided for backward compatibility so far ...
  158. %-check # arguments...
  159. %--------------------------------------------------------------------------
  160. if nargin<6, error('insufficient arguments'), end
  161. if nargout > 1, error('Too many output arguments Set'), end
  162. %-check arguments...
  163. %--------------------------------------------------------------------------
  164. if ~ischar(varargin{2}), error('~ischar(name)'), end
  165. if ~(varargin{3}=='F'||varargin{3}=='T'||varargin{3}=='P'),
  166. error('~(STAT==F|STAT==T|STAT==P)'), end
  167. if ~ischar(varargin{4}), error('~ischar(varargin{4})');
  168. else set_action = varargin{4}; end
  169. sX = varargin{6};
  170. if ~spm_sp('isspc',sX), sX = spm_sp('set',sX); end
  171. if isempty(sX.X), error('Empty space X in Set'); end
  172. Fc = sf_FconFields;
  173. %- use the name as a flag to insure that F-contrast has been
  174. %- properly created;
  175. Fc.name = varargin{2};
  176. Fc.STAT = varargin{3};
  177. if Fc.STAT=='T' && ~(any(strcmp(set_action,{'c+','c'})))
  178. warning('enter T stat with contrast - here no check rank == 1');
  179. end
  180. [sC,sL] = spm_sp('size',sX);
  181. %- allow to define the contrast the old (version 1) way ?
  182. %- NO. v1 = strcmp(action,'v1set');
  183. switch set_action,
  184. case {'c','c+'}
  185. Fc.iX0 = set_action;
  186. c = spm_sp(':', sX, varargin{5});
  187. if isempty(c)
  188. [Fc.X1o.ukX1o,Fc.X0.ukX0] = spm_SpUtil('+c->Tsp',sX,[]);
  189. %- v1 [Fc.X1o Fc.X0] = spm_SpUtil('c->Tsp',sX,[]);
  190. Fc.c = c;
  191. elseif size(c,1) ~= sL,
  192. error(['not contrast dim. in ' mfilename ' ' set_action]);
  193. else
  194. if strcmp(set_action,'c+')
  195. if ~spm_sp('isinspp',sX,c), c = spm_sp('oPp:',sX,c); end
  196. end;
  197. if Fc.STAT=='T' && ~sf_is_T(sX,c)
  198. %- Could be make more self-correcting by giving back an F
  199. error('trying to define a t that looks like an F');
  200. end
  201. Fc.c = c;
  202. [Fc.X1o.ukX1o,Fc.X0.ukX0] = spm_SpUtil('+c->Tsp',sX,c);
  203. %- v1 [Fc.X1o Fc.X0] = spm_SpUtil('c->Tsp',sX,c);
  204. end
  205. %~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  206. % 'option given for completeness - not for SPM use'
  207. case {'X0'}
  208. warning('option given for completeness - not for SPM use');
  209. Fc.iX0 = set_action;
  210. X0 = spm_sp(':', sX, varargin{5});
  211. if isempty(X0),
  212. Fc.c = spm_sp('xpx',sX);
  213. Fc.X1o.ukX1o = spm_sp('cukx',sX);
  214. Fc.X0.ukX0 = [];
  215. elseif size(X0,1) ~= sC,
  216. error('dimension of X0 wrong in Set');
  217. else
  218. Fc.c = spm_SpUtil('X0->c',sX,X0);
  219. Fc.X0.ukX0 = spm_sp('ox',sX)'*X0;
  220. Fc.X1o.ukX1o = spm_SpUtil('+c->Tsp',sX,Fc.c);
  221. end
  222. case 'ukX0'
  223. warning('option given for completeness - not for SPM use');
  224. Fc.iX0 = set_action;
  225. if isempty(ukX0),
  226. Fc.c = spm_sp('xpx',sX);
  227. Fc.X1o.ukX1o = spm_sp('cukx',sX);
  228. Fc.X0.ukX0 = [];
  229. elseif size(ukX0,1) ~= spm_sp('rk',sX),
  230. error('dimension of cukX0 wrong in Set');
  231. else
  232. Fc.c = spm_SpUtil('+X0->c',sX,ukX0);
  233. Fc.X0.ukX0 = ukX0;
  234. Fc.X1o.ukX1o = spm_SpUtil('+c->Tsp',sX,Fc.c);
  235. end
  236. %~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  237. case 'iX0'
  238. iX0 = varargin{5};
  239. iX0 = spm_SpUtil('iX0check',iX0,sL);
  240. Fc.iX0 = iX0;
  241. Fc.X0.ukX0 = spm_sp('ox',sX)' * spm_sp('Xi',sX,iX0);
  242. if isempty(iX0),
  243. Fc.c = spm_sp('xpx',sX);
  244. Fc.X1o.ukX1o = spm_sp('cukx',sX);
  245. else
  246. Fc.c = spm_SpUtil('i0->c',sX,iX0);
  247. Fc.X1o.ukX1o = spm_SpUtil('+c->Tsp',sX,Fc.c);
  248. end
  249. otherwise
  250. error('wrong action in Set ');
  251. end
  252. varargout = {Fc};
  253. case 'x0' % spm_FcUtil('X0',Fc,sX)
  254. %=======================================================================
  255. if nargin ~= 3,
  256. error('too few/many input arguments - need 2');
  257. else
  258. Fc = varargin{2}; sX = varargin{3};
  259. end
  260. if nargout ~= 1, error('too few/many output arguments - need 1'), end
  261. if ~sf_IsFcon(Fc), error('argument is not a contrast struct'), end
  262. if ~spm_sp('isspc',sX), sX = spm_sp('set',sX); end
  263. varargout = {sf_X0(Fc,sX)};
  264. case 'x1o' % spm_FcUtil('X1o',Fc,sX)
  265. %=======================================================================
  266. if nargin ~= 3,
  267. error('too few/many input arguments - need 2');
  268. else
  269. Fc = varargin{2}; sX = varargin{3};
  270. end
  271. if nargout ~= 1, error('too few/many output arguments - need 1'), end
  272. if ~sf_IsFcon(Fc), error('argument is not a contrast struct'), end
  273. if ~spm_sp('isspc',sX), sX = spm_sp('set',sX); end
  274. varargout = {sf_X1o(Fc,sX)};
  275. case 'isfcon' %- Is it an F contrast ?
  276. %=======================================================================
  277. % yes_no = spm_FcUtil('IsFcon',Fc)
  278. if nargin~=2, error('too few/many input arguments - need 2'), end
  279. if ~isstruct(varargin{2}), varargout={0};
  280. else varargout = {sf_IsFcon(varargin{2})};
  281. end
  282. case 'fconedf' %- F contrast edf
  283. %=======================================================================
  284. % [edf_tsp edf_Xsp] = spm_FcUtil('FconEdf', Fc, sX [, V])
  285. if nargin<3, error('Insufficient arguments'), end
  286. if nargout >= 3, error('Too many output argument.'), end
  287. Fc = varargin{2};
  288. sX = varargin{3};
  289. if nargin == 4, V = varargin{4}; else V = []; end
  290. if ~sf_IsFcon(Fc), error('Fc must be Fcon'), end
  291. if ~spm_sp('isspc',sX)
  292. sX = spm_sp('set',sX); end
  293. if ~sf_isempty_X1o(Fc)
  294. [trMV, trMVMV] = spm_SpUtil('trMV',sf_X1o(Fc,sX),V);
  295. else
  296. trMV = 0;
  297. trMVMV = 0;
  298. end
  299. if ~trMVMV, edf_tsp = 0; warning('edf_tsp = 0'),
  300. else edf_tsp = trMV^2/trMVMV; end;
  301. if nargout == 2
  302. [trRV, trRVRV] = spm_SpUtil('trRV',sX,V);
  303. if ~trRVRV, edf_Xsp = 0; warning('edf_Xsp = 0'),
  304. else edf_Xsp = trRV^2/trRVRV; end;
  305. varargout = {edf_tsp, edf_Xsp};
  306. else
  307. varargout = {edf_tsp};
  308. end
  309. %=======================================================================
  310. %=======================================================================
  311. % parts that use F contrast
  312. %=======================================================================
  313. %=======================================================================
  314. %
  315. % Quick reference : L : can be lists of ...
  316. %-------------------------
  317. % ('Hsqr',Fc, sX) : Out: Hsqr / ESS = b' * Hsqr' * Hsqr * b
  318. % ('H',Fc, sX) : Out: H / ESS = b' * H * b
  319. % ('Yc',Fc, sX, b) : Out: Y corrected = X*b - X0*X0- *Y
  320. % ('Y0',Fc, sX, b) : Out: Y0 = X0*X0- *Y
  321. % ('|_',LFc1, sX, LFc2) : Out: Fc1 orthog. wrt Fc2
  322. % ('|_?',LFc1,sX [,LFc2]): Out: is Fc2 ortho to Fc1 or is Fc1 ortho ?
  323. % ('In', LFc1, sX, LFc2) : Out: indices of Fc2 if "in", 0 otherwise
  324. % ('~unique', LFc, sX) : Out: indices of redundant contrasts
  325. % ('0|[]', Fc, sX) : Out: 1 if Fc is zero or empty, 0 otherwise
  326. case 'hsqr' %-Extra sum of squares matrix for beta's from contrast
  327. %=======================================================================
  328. % hsqr = spm_FcUtil('Hsqr',Fc, sX)
  329. if nargin<3, error('Insufficient arguments'), end
  330. if nargout>1, error('Too many output argument.'), end
  331. Fc = varargin{2};
  332. sX = varargin{3};
  333. if ~sf_IsFcon(Fc), error('Fc must be F-contrast'), end
  334. if ~sf_IsSet(Fc), error('Fcon must be set'); end; %-
  335. if ~spm_sp('isspc',sX), sX = spm_sp('set',sX); end;
  336. if sf_isempty_X1o(Fc)
  337. if ~sf_isempty_X0(Fc)
  338. %- assumes that X0 is sX.X
  339. %- warning(' Empty X1o in spm_FcUtil(''Hsqr'',Fc,sX) ');
  340. varargout = { zeros(1,spm_sp('size',sX,2)) };
  341. else
  342. error(' Fc must be set ');
  343. end
  344. else
  345. varargout = { sf_Hsqr(Fc,sX) };
  346. end
  347. case 'h' %-Extra sum of squares matrix for beta's from contrast
  348. %=======================================================================
  349. % H = spm_FcUtil('H',Fc, sX)
  350. % Empty and zeros dealing :
  351. % This routine never returns an empty matrix.
  352. % If sf_isempty_X1o(Fc) | isempty(Fc.c) it explicitly
  353. % returns a zeros projection matrix.
  354. if nargin<2, error('Insufficient arguments'), end
  355. if nargout>1, error('Too many output argument.'), end
  356. Fc = varargin{2};
  357. sX = varargin{3};
  358. if ~sf_IsFcon(Fc), error('Fc must be F-contrast'), end
  359. if ~sf_IsSet(Fc), error('Fcon must be set'); end; %-
  360. if ~spm_sp('isspc',sX), sX = spm_sp('set',sX); end;
  361. if sf_isempty_X1o(Fc)
  362. if ~sf_isempty_X0(Fc)
  363. %- assumes that X0 is sX.X
  364. %- warning(' Empty X1o in spm_FcUtil(''H'',Fc,sX) ');
  365. varargout = { zeros(spm_sp('size',sX,2)) };
  366. else
  367. error(' Fc must be set ');
  368. end
  369. else
  370. varargout = { sf_H(Fc,sX) };
  371. end
  372. case 'yc' %- Fitted data corrected for confounds defined by Fc
  373. %=======================================================================
  374. % Yc = spm_FcUtil('Yc',Fc, sX, b)
  375. if nargin < 4, error('Insufficient arguments'), end
  376. if nargout > 1, error('Too many output argument.'), end
  377. Fc = varargin{2}; sX = varargin{3}; b = varargin{4};
  378. if ~sf_IsFcon(Fc), error('Fc must be F-contrast'), end
  379. if ~sf_IsSet(Fc), error('Fcon must be set'); end;
  380. if ~spm_sp('isspc',sX), sX = spm_sp('set',sX); end;
  381. % if ~spm_FcUtil('Rcompatible',Fc,sX), ...
  382. % error('sX and Fc must be compatible'), end;
  383. if spm_sp('size',sX,2) ~= size(b,1),
  384. error('sX and b must be compatible'), end;
  385. if sf_isempty_X1o(Fc)
  386. if ~sf_isempty_X0(Fc)
  387. %- if space of interest empty or null, returns zeros !
  388. varargout = { zeros(spm_sp('size',sX,1),size(b,2)) };
  389. else
  390. error(' Fc must be set ');
  391. end
  392. else
  393. varargout = { sf_Yc(Fc,sX,b) };
  394. end
  395. case 'y0' %- Fitted data corrected for confounds defined by Fc
  396. %=======================================================================
  397. % Y0 = spm_FcUtil('Y0',Fc, sX, b)
  398. if nargin < 4, error('Insufficient arguments'), end
  399. if nargout > 1, error('Too many output argument.'), end
  400. Fc = varargin{2}; sX = varargin{3}; b = varargin{4};
  401. if ~sf_IsFcon(Fc), error('Fc must be F-contrast'), end
  402. if ~sf_IsSet(Fc), error('Fcon must be set'); end;
  403. if ~spm_sp('isspc',sX), sX = spm_sp('set',sX); end;
  404. if spm_sp('size',sX,2) ~= size(b,1),
  405. error('sX and b must be compatible'), end;
  406. if sf_isempty_X1o(Fc)
  407. if ~sf_isempty_X0(Fc)
  408. %- if space of interest empty or null, returns zeros !
  409. varargout = { sX.X*b };
  410. else
  411. error(' Fc must be set ');
  412. end
  413. else
  414. varargout = { sf_Y0(Fc,sX,b) };
  415. end
  416. case {'|_'} %- Fc orthogonalisation
  417. %=======================================================================
  418. % Fc = spm_FcUtil('|_',Fc1, sX, Fc2)
  419. % returns Fc1 orthogonolised wrt Fc2
  420. if nargin < 4, error('Insufficient arguments'), end
  421. if nargout > 1, error('Too many output argument.'), end
  422. Fc1 = varargin{2}; sX = varargin{3}; Fc2 = varargin{4};
  423. %-check arguments
  424. %-----------------------------------------------------------------------
  425. L1 = length(Fc1);
  426. if ~L1, warning('no contrast given to |_'); varargout = {[]}; return; end
  427. for i=1:L1
  428. if ~sf_IsFcon(Fc1(i)), error('Fc1(i) must be a contrast'), end
  429. end
  430. L2 = length(Fc2);
  431. if ~L2, error('must have at least a contrast in Fc2'); end
  432. for i=1:L2
  433. if ~sf_IsFcon(Fc2(i)), error('Fc2(i) must be a contrast'), end
  434. end
  435. if ~spm_sp('isspc',sX), sX = spm_sp('set',sX); end;
  436. %-create an F-contrast for all the Fc2
  437. %--------------------------------------------------------------------------
  438. str = Fc2(1).name; for i=2:L2, str = [str ' ' Fc2(i).name]; end;
  439. Fc2 = spm_FcUtil('Set',str,'F','c+',cat(2,Fc2(:).c),sX);
  440. if sf_isempty_X1o(Fc2) || sf_isnull(Fc2,sX)
  441. varargout = {Fc1};
  442. else
  443. for i=1:L1
  444. if sf_isempty_X1o(Fc1(i)) || sf_isnull(Fc1(i),sX)
  445. %- Fc1(i) is an [] or 0 contrast : ortho to anything;
  446. out(i) = Fc1(i);
  447. else
  448. out(i) = sf_fcortho(Fc1(i), sX, Fc2);
  449. end
  450. end
  451. varargout = {out};
  452. end
  453. case {'|_?'} %- Are contrasts orthogonals
  454. %=======================================================================
  455. % b = spm_FcUtil('|_?',Fc1, sX [, Fc2])
  456. if nargin < 3, error('Insufficient arguments'), end
  457. Fc1 = varargin{2}; sX = varargin{3};
  458. if nargin > 3, Fc2 = varargin{4}; else Fc2 = []; end;
  459. if isempty(Fc1), error('give at least one non empty contrast'), end;
  460. if ~spm_sp('isspc',sX), sX = spm_sp('set',sX); end;
  461. for i=1:length(Fc1)
  462. if ~sf_IsFcon(Fc1(i)), error('Fc1(i) must be a contrast'), end
  463. end
  464. for i=1:length(Fc2)
  465. if ~sf_IsFcon(Fc2(i)), error('Fc2(i) must be a contrast'), end
  466. end
  467. varargout = { sf_Rortho(Fc1,sX,Fc2) };
  468. case 'in' %- Fc1 is in list of contrasts Fc2
  469. %=======================================================================
  470. % [iFc2 iFc1] = spm_FcUtil('In', Fc1, sX, Fc2)
  471. % returns indice of Fc2 if "in", 0 otherwise
  472. % NB : If T- stat, the routine checks whether Fc.c is of
  473. % size one. This is ensure if contrast is set
  474. % or manipulated (ortho ..) with spm_FcUtil
  475. % note that the algorithmn works \emph{only because} Fc2(?).c
  476. % and Fc1.c are in space(X')
  477. if nargin < 4, error('Insufficient arguments'), end
  478. if nargout > 2, error('Too many output argument.'), end
  479. Fc1 = varargin{2}; Fc2 = varargin{4}; sX = varargin{3};
  480. L1 = length(Fc1);
  481. if ~L1, warning('no contrast given to in');
  482. if nargout == 2, varargout = {[] []};
  483. else varargout = {[]}; end;
  484. return;
  485. end
  486. for i=1:L1
  487. if ~sf_IsFcon(Fc1(i)), error('Fc1(i) must be a contrast'), end
  488. end
  489. L2 = length(Fc2);
  490. if ~L2, error('must have at least a contrast in Fc2'); end
  491. for i=1:L2
  492. if ~sf_IsFcon(Fc2(i)), error('Fc2(i) must be F-contrast'), end
  493. end
  494. if ~spm_sp('isspc',sX), sX = spm_sp('set',sX); end;
  495. [idxFc2,idxFc1] = sf_in(Fc1, sX, Fc2);
  496. if isempty(idxFc2), idxFc2 = 0; end
  497. if isempty(idxFc1), idxFc1 = 0; end
  498. switch nargout
  499. case {0,1}
  500. varargout = { idxFc2 };
  501. case 2
  502. varargout = { idxFc2 idxFc1 };
  503. otherwise
  504. error('Too many or not enough output arguments');
  505. end
  506. case '~unique' %- Fc list unique
  507. %=======================================================================
  508. % idx = spm_FcUtil('~unique', Fc, sX)
  509. %- returns indices of redundant contrasts in Fc
  510. %- such that Fc(idx) = [] makes Fc unique.
  511. %- if already unique returns []
  512. if nargin ~= 3, error('Insufficient/too many arguments'), end
  513. Fc = varargin{2}; sX = varargin{3};
  514. %----------------------------
  515. L1 = length(Fc);
  516. if ~L1, warning('no contrast given '); varargout = {[]}; return; end
  517. for i=1:L1
  518. if ~sf_IsFcon(Fc(i)), error('Fc(i) must be a contrast'), end
  519. end
  520. if ~spm_sp('isspc',sX), sX = spm_sp('set',sX); end;
  521. %----------------------------
  522. varargout = { unique(sf_notunique(Fc, sX))};
  523. case {'0|[]','[]|0'} %- Fc is null or empty
  524. %=======================================================================
  525. % b = spm_FcUtil('0|[]', Fc, sX)
  526. % returns 1 if F-contrast is empty or null; assumes the contrast is set.
  527. if nargin ~= 3, error('Insufficient/too many arguments'), end
  528. Fc = varargin{2}; sX = varargin{3};
  529. %----------------------------
  530. L1 = length(Fc);
  531. if ~L1, warning('no contrast given to |_'); varargout = {[]}; return; end
  532. for i=1:L1
  533. if ~sf_IsFcon(Fc(i)), error('Fc(i) must be a contrast'), end
  534. end
  535. if ~spm_sp('isspc',sX), sX = spm_sp('set',sX); end;
  536. %----------------------------
  537. idx = [];
  538. for i=1:L1
  539. if sf_isempty_X1o(Fc(i)) || sf_isnull(Fc(i),sX), idx = [idx i]; end
  540. end
  541. if isempty(idx)
  542. varargout = {0};
  543. else
  544. varargout = {idx};
  545. end
  546. %=======================================================================
  547. otherwise
  548. %=======================================================================
  549. error('Unknown action string in spm_FcUtil')
  550. end; %---- switch lower(action),
  551. %=======================================================================
  552. %=======================================================================
  553. % Sub Functions
  554. %=======================================================================
  555. %=======================================================================
  556. %=======================================================================
  557. % Fcon = spm_FcUtil('FconFields')
  558. function Fc = sf_FconFields
  559. Fc = struct(...
  560. 'name', '',...
  561. 'STAT', '',...
  562. 'c', [],...
  563. 'X0', struct('ukX0',[]),...
  564. 'iX0', [],...
  565. 'X1o', struct('ukX1o',[]),...
  566. 'eidf', [],...
  567. 'Vcon', [],...
  568. 'Vspm', []);
  569. %=======================================================================
  570. % used internally. Minimum contrast structure
  571. function minFc = sf_MinFcFields
  572. minFc = struct(...
  573. 'name', '',...
  574. 'STAT', '',...
  575. 'c', [],...
  576. 'X0', [],...
  577. 'X1o', []);
  578. %=======================================================================
  579. % yes_no = spm_FcUtil('IsFcon',Fc)
  580. function b = sf_IsFcon(Fc)
  581. %- check that minimum fields of a contrast are in Fc
  582. b = 1;
  583. minnames = fieldnames(sf_MinFcFields);
  584. FCnames = fieldnames(Fc);
  585. for str = minnames'
  586. b = b & any(strcmp(str,FCnames));
  587. if ~b, break, end
  588. end
  589. %=======================================================================
  590. % used internally; To be set, a contrast structure should have
  591. % either X1o or X0 non empty. X1o can be non empty because c
  592. % is non empty.
  593. function b = sf_IsSet(Fc)
  594. b = ~sf_isempty_X0(Fc) | ~sf_isempty_X1o(Fc);
  595. %=======================================================================
  596. % used internally
  597. %
  598. function v = sf_ver(Fc)
  599. if isstruct(Fc.X0), v = 2; else v = 1; end
  600. %=======================================================================
  601. % used internally
  602. function b = sf_isempty_X1o(Fc)
  603. if sf_ver(Fc) > 1,
  604. b = isempty(Fc.X1o.ukX1o);
  605. %- consistency check
  606. if b ~= isempty(Fc.c),
  607. Fc.c, Fc.X1o.ukX1o, error('Contrast internally not consistent');
  608. end
  609. else
  610. b = isempty(Fc.X1o);
  611. %- consistency check
  612. if b ~= isempty(Fc.c),
  613. Fc.c, Fc.X1o, error('Contrast internally not consistent');
  614. end
  615. end
  616. %=======================================================================
  617. % used internally
  618. function b = sf_X1o(Fc,sX)
  619. if sf_ver(Fc) > 1,
  620. b = spm_sp('ox',sX)*Fc.X1o.ukX1o;
  621. else
  622. b = Fc.X1o;
  623. end
  624. %=======================================================================
  625. % used internally
  626. function b = sf_X0(Fc,sX)
  627. if sf_ver(Fc) > 1,
  628. b = spm_sp('ox',sX)*Fc.X0.ukX0;
  629. else
  630. b = Fc.X0;
  631. end
  632. %=======================================================================
  633. % used internally
  634. function b = sf_isempty_X0(Fc)
  635. if sf_ver(Fc) > 1,
  636. b = isempty(Fc.X0.ukX0);
  637. else
  638. b = isempty(Fc.X0);
  639. end
  640. %=======================================================================
  641. % Hsqr = spm_Fcutil('Hsqr',Fc,sX)
  642. function hsqr = sf_Hsqr(Fc,sX)
  643. %
  644. % Notations : H equiv to X1o, H = uk*a1, X = uk*ax, r = rk(sX),
  645. % sX.X is (n,p), H is (n,q), a1 is (r,q), ax is (r,p)
  646. % oxa1 is an orthonormal basis for a1, oxa1 is (r,qo<=q)
  647. % Algorithm :
  648. % v1 : Y'*H*(H'*H)-*H'*Y = b'*X'*H*(H'*H)-*H'*X*b
  649. % = b'*X'*oxH*oxH'*X*b
  650. % so hsrq is set to oxH'*X, a (q,n)*(n,p) op. + computation of oxH
  651. % v2 : X'*H*(H'*H)-*H'*X = ax'*uk'*uk*a1*(a1'*uk'*uk*a1)-*a1'*uk'*uk*ax
  652. % = ax'*a1*(a1'*a1)-*a1'*ax
  653. % = ax'*oxa1*oxa1'*ax
  654. %
  655. % so hsrq is set to oxa1'*ax : a (qo,r)*(r,p) operation! -:))
  656. % + computation of oxa1.
  657. %-**** fprintf('v%d\n',sf_ver(Fc));
  658. if sf_ver(Fc) > 1,
  659. hsqr = spm_sp('ox',spm_sp('set',Fc.X1o.ukX1o))' * spm_sp('cukx',sX);
  660. else
  661. hsqr = spm_sp('ox',spm_sp('set',Fc.X1o))'*spm_sp('x',sX);
  662. end
  663. %=======================================================================
  664. % H = spm_FcUtil('H',Fc)
  665. function H = sf_H(Fc,sX)
  666. %
  667. % Notations : H equiv to X1o, H = uk*a1, X = uk*ax
  668. % Algorithm :
  669. % v1 : Y'*H*(H'*H)-*H'*Y = b'*X'*H*(H'*H)-*H'*X*b
  670. % = b'*c*(H'*H)-*c'*b
  671. % = b'*c*(c'*(X'*X)-*c)-*c'*b
  672. %- v1 : Note that pinv(Fc.X1o' * Fc.X1o) is not too bad
  673. %- because dimensions are only (q,q). See sf_hsqr for notations.
  674. %- Fc.c and Fc.X1o should match. This is ensure by using FcUtil.
  675. %-**** fprintf('v%d\n',sf_ver(Fc));
  676. if sf_ver(Fc) > 1,
  677. hsqr = sf_Hsqr(Fc,sX);
  678. H = hsqr' * hsqr;
  679. else
  680. H = Fc.c * pinv(Fc.X1o' * Fc.X1o) * Fc.c';
  681. % H = {c*spm_sp('x-',spm_sp('Set',c'*spm_sp('xpx-',sX)*c) )*c'}
  682. end
  683. %=======================================================================
  684. % Yc = spm_FcUtil('Yc',Fc,sX,b)
  685. function Yc = sf_Yc(Fc,sX,b)
  686. Yc = sX.X*spm_sp('xpx-',sX)*sf_H(Fc,sX)*b;
  687. %=======================================================================
  688. % Y0 = spm_FcUtil('Y0',Fc,sX,b)
  689. function Y0 = sf_Y0(Fc,sX,b)
  690. Y0 = sX.X*(eye(spm_sp('size',sX,2)) - spm_sp('xpx-',sX)*sf_H(Fc,sX))*b;
  691. %=======================================================================
  692. % Fc = spm_FcUtil('|_',Fc1, sX, Fc2)
  693. function Fc1o = sf_fcortho(Fc1, sX, Fc2)
  694. %--- use the space facility to ensure the proper tolerance dealing...
  695. c1_2 = Fc1.c - sf_H(Fc2,sX)*spm_sp('xpx-:',sX,Fc1.c);
  696. Fc1o = spm_FcUtil('Set',['(' Fc1.name ' |_ (' Fc2.name '))'], ...
  697. Fc1.STAT, 'c+',c1_2,sX);
  698. %- In the large (scans) dimension :
  699. %- c = sX.X'*spm_sp('r:',spm_sp('set',Fc2.X1o),Fc1.X1o);
  700. %- Fc1o = spm_FcUtil('Set',['(' Fc1.name ' |_ (' Fc2.name '))'], ...
  701. %- Fc1.STAT, 'c',c,sX);
  702. %=======================================================================
  703. function b = sf_Rortho(Fc1,sX,Fc2)
  704. if isempty(Fc2)
  705. if length(Fc1) <= 1, b = 0;
  706. else
  707. c1 = cat(2,Fc1(:).c);
  708. b = ~any(any( abs(triu(c1'*spm_sp('xpx-:',sX,c1), 1)) > sX.tol));
  709. end
  710. else
  711. c1 = cat(2,Fc1(:).c); c2 = cat(2,Fc2(:).c);
  712. b = ~any(any( abs(c1'*spm_sp('xpx-:',sX,c2)) > sX.tol ));
  713. end
  714. %=======================================================================
  715. % b = spm_FcUtil('0|[]', Fc, sX)
  716. %- returns 1 if F-contrast is empty or null; assumes the contrast is set.
  717. %- Assumes that if Fc.c contains only zeros, so does Fc.X1o.
  718. %- this is ensured if spm_FcUtil is used
  719. function boul = sf_isnull(Fc,sX)
  720. %
  721. boul = ~any(any(spm_sp('oPp:',sX,Fc.c)));
  722. %=======================================================================
  723. % Fc = spm_FcUtil('Set',name, STAT, set_action, value, sX)
  724. function boul = sf_is_T(sX,c)
  725. %- assumes that the dimensions are OK
  726. %- assumes c is not empty
  727. %- Does NOT assumes that c is space of sX'
  728. %- A rank of zero can be defined
  729. %- if the rank == 1, checks whether same directions
  730. boul = 1;
  731. if ~spm_sp('isinspp',sX,c), c = spm_sp('oPp:',sX,c); end;
  732. if rank(c) > 1 || any(any(c'*c < 0)), boul = 0; end;
  733. %=======================================================================
  734. function [idxFc2, idxFc1] = sf_in(Fc1, sX, Fc2)
  735. L2 = length(Fc2);
  736. L1 = length(Fc1);
  737. idxFc1 = []; idxFc2 = [];
  738. for j=1:L1
  739. %- project Fc1(j).c if not estimable
  740. if ~spm_sp('isinspp',sX,Fc1(j).c), %- warning ?
  741. c1 = spm_sp('oPp:',sX,Fc1(j).c);
  742. else
  743. c1 = Fc1(j).c;
  744. end
  745. sc1 = spm_sp('Set',c1);
  746. S = Fc1(j).STAT;
  747. boul = 0;
  748. for i =1:L2
  749. if Fc2(i).STAT == S
  750. %- the same statistics. else just go on to the next contrast
  751. boul = spm_sp('==',sc1,spm_sp('oPp',sX,Fc2(i).c));
  752. %- if they are the same space and T stat (same direction),
  753. %- then check wether they are in the same ORIENTATION
  754. %- works because size(X1o,2) == 1, else .* with (Fc1(j).c'*Fc2(i).c)
  755. if boul && S == 'T'
  756. atmp = sf_X1o(Fc1(j),sX);
  757. btmp = sf_X1o(Fc2(i),sX);
  758. boul = ~any(any( (atmp' * btmp) < 0 ));
  759. end
  760. %- note the indices
  761. if boul, idxFc1 = [idxFc1 j]; idxFc2 = [idxFc2 i]; end
  762. end
  763. end
  764. end %- for j=1:L1
  765. %=======================================================================
  766. function idx = sf_notunique(Fc, sX)
  767. %- works recursively ...
  768. %- and use the fact that [] + i == []
  769. %- quite long for large sets ...
  770. l = length(Fc);
  771. if l == 1, idx = [];
  772. else
  773. idx = [ (1+sf_in(Fc(1),sX,Fc(2:l))) (1+sf_notunique(Fc(2:l), sX))];
  774. end