spm_powell.m 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. function [p,f] = spm_powell(p,xi,tolsc,func,varargin)
  2. % Powell optimisation method
  3. % FORMAT [p,f] = spm_powell(p,xi,tolsc,func,varargin)
  4. % p - Starting parameter values
  5. % xi - columns containing directions in which to begin searching
  6. % tolsc - stopping criteria, optimisation stops when
  7. % sqrt(sum(((p-p_prev)./tolsc).^2))<1
  8. % func - name of evaluated function
  9. % varargin - remaining arguments to func (after p)
  10. %
  11. % p - final parameter estimates
  12. % f - function value at minimum
  13. %__________________________________________________________________________
  14. %
  15. % Method is based on Powell's optimisation method described in
  16. % Numerical Recipes (Press, Flannery, Teukolsky & Vetterling).
  17. %__________________________________________________________________________
  18. % Copyright (C) 2001-2017 Wellcome Trust Centre for Neuroimaging
  19. % John Ashburner
  20. % $Id: spm_powell.m 7252 2018-01-31 15:56:56Z john $
  21. p = p(:);
  22. f = feval(func,p,varargin{:});
  23. for iter=1:512
  24. %if numel(p)>1, fprintf('iteration %d...\n', iter); end; %-#
  25. ibig = numel(p);
  26. pp = p;
  27. fp = f;
  28. del = 0;
  29. for i=1:length(p)
  30. ft = f;
  31. [p,junk,f] = min1d(p,xi(:,i),func,f,tolsc,varargin{:});
  32. if abs(ft-f) > del
  33. del = abs(ft-f);
  34. ibig = i;
  35. end
  36. end
  37. if numel(p)==1 || sqrt(sum(((p(:)-pp(:))./tolsc(:)).^2))<1 || abs((f-fp)/(f+fp))<1e-6, return; end
  38. ft = feval(func,2.0*p-pp,varargin{:});
  39. if ft < f
  40. [p,xi(:,ibig),f] = min1d(p,p-pp,func,f,tolsc,varargin{:});
  41. end
  42. end
  43. warning('Too many optimisation iterations');
  44. %==========================================================================
  45. % function [p,pi,f] = min1d(p,pi,func,f,tolsc,varargin)
  46. %==========================================================================
  47. function [p,pi,f] = min1d(p,pi,func,f,tolsc,varargin)
  48. % Line search for minimum.
  49. global lnm % used in funeval
  50. lnm = struct('p',p,'pi',pi,'func',func,'args',[]);
  51. lnm.args = varargin;
  52. min1d_plot('Init', 'Line Minimisation','Function','Parameter Value');
  53. min1d_plot('Set', 0, f);
  54. tol = 1/sqrt(sum((pi(:)./tolsc(:)).^2));
  55. t = bracket(f);
  56. [f,pmin] = search(t,tol);
  57. pi = pi*pmin;
  58. p = p + pi;
  59. %if length(p)<12,
  60. % for i=1:length(p), fprintf('%-8.4g ', p(i)); end; %-#
  61. % fprintf('| %.5g\n', f); %-#
  62. %else
  63. % fprintf('%.5g\n', f); %-#
  64. %end
  65. min1d_plot('Clear');
  66. %==========================================================================
  67. % function f = funeval(p)
  68. %==========================================================================
  69. function f = funeval(p)
  70. % Reconstruct parameters and evaluate.
  71. global lnm % defined in min1d
  72. pt = lnm.p+p.*lnm.pi;
  73. f = feval(lnm.func,pt,lnm.args{:});
  74. min1d_plot('Set',p,f);
  75. %==========================================================================
  76. % function t = bracket(f)
  77. %==========================================================================
  78. function t = bracket(f)
  79. % Bracket the minimum (t(2)) between t(1) and t(3)
  80. gold = (1+sqrt(5))/2; % Golden ratio
  81. t(1) = struct('p',0,'f',f);
  82. t(2).p = 1;
  83. t(2).f = funeval(t(2).p);
  84. % if t(2) not better than t(1) then swap
  85. if t(2).f > t(1).f
  86. t(3) = t(1);
  87. t(1) = t(2);
  88. t(2) = t(3);
  89. end
  90. t(3).p = t(2).p + gold*(t(2).p-t(1).p);
  91. t(3).f = funeval(t(3).p);
  92. while t(2).f > t(3).f
  93. % fit a polynomial to t
  94. tmp = cat(1,t.p)-t(2).p;
  95. pol = pinv([ones(3,1) tmp tmp.^2])*cat(1,t.f);
  96. % minimum is when gradient of polynomial is zero
  97. % sign of pol(3) (the 2nd deriv) should be +ve
  98. if pol(3)>0
  99. % minimum is when gradient of polynomial is zero
  100. d = -pol(2)/(2*pol(3)+eps);
  101. % A very conservative constraint on the displacement
  102. if d > (1+gold)*(t(3).p-t(2).p)
  103. d = (1+gold)*(t(3).p-t(2).p);
  104. end
  105. u.p = t(2).p+d;
  106. else
  107. % sign of pol(3) (the 2nd deriv) is not +ve
  108. % so extend out by golden ratio instead
  109. u.p = t(3).p+gold*(t(3).p-t(2).p);
  110. end
  111. % FUNCTION EVALUATION
  112. u.f = funeval(u.p);
  113. if (t(2).p < u.p) == (u.p < t(3).p)
  114. % u is between t(2) and t(3)
  115. if u.f < t(3).f
  116. % minimum between t(2) and t(3) - done
  117. t(1) = t(2);
  118. t(2) = u;
  119. return
  120. elseif u.f > t(2).f
  121. % minimum between t(1) and u - done
  122. t(3) = u;
  123. return;
  124. end
  125. end
  126. % Move all 3 points along
  127. t(1) = t(2);
  128. t(2) = t(3);
  129. t(3) = u;
  130. end
  131. %==========================================================================
  132. % function [f,p] = search(t, tol)
  133. %==========================================================================
  134. function [f,p] = search(t, tol)
  135. % Brent's method for line searching - given that minimum is bracketed
  136. gold1 = 1-(sqrt(5)-1)/2;
  137. % Current and previous displacements
  138. d = Inf;
  139. pd = Inf;
  140. % sort t into best first order
  141. [junk,ind] = sort(cat(1,t.f));
  142. t = t(ind);
  143. brk = [min(cat(1,t.p)) max(cat(1,t.p))];
  144. for iter=1:128
  145. % check stopping criterion
  146. if abs(t(1).p - 0.5*(brk(1)+brk(2)))+0.5*(brk(2)-brk(1)) <= 2*tol
  147. p = t(1).p;
  148. f = t(1).f;
  149. return;
  150. end
  151. % keep last two displacents
  152. ppd = pd;
  153. pd = d;
  154. % fit a polynomial to t
  155. tmp = cat(1,t.p)-t(1).p;
  156. pol = pinv([ones(3,1) tmp tmp.^2])*cat(1,t.f);
  157. % minimum is when gradient of polynomial is zero
  158. d = -pol(2)/(2*pol(3)+eps);
  159. u.p = t(1).p+d;
  160. % check so that displacement is less than the last but two,
  161. % that the displaced point is between the brackets
  162. % and that the solution is a minimum rather than a maximum
  163. eps2 = 2*eps*abs(t(1).p)+eps;
  164. if abs(d) > abs(ppd)/2 || u.p < brk(1)+eps2 || u.p > brk(2)-eps2 || pol(3)<=0
  165. % if criteria are not met, then golden search into the larger part
  166. if t(1).p >= 0.5*(brk(1)+brk(2))
  167. d = gold1*(brk(1)-t(1).p);
  168. else
  169. d = gold1*(brk(2)-t(1).p);
  170. end
  171. u.p = t(1).p+d;
  172. end
  173. % FUNCTION EVALUATION
  174. u.f = funeval(u.p);
  175. % Insert the new point into the appropriate position and update
  176. % the brackets if necessary
  177. if u.f <= t(1).f
  178. if u.p >= t(1).p, brk(1)=t(1).p; else brk(2)=t(1).p; end
  179. t(3) = t(2);
  180. t(2) = t(1);
  181. t(1) = u;
  182. else
  183. if u.p < t(1).p, brk(1)=u.p; else brk(2)=u.p; end
  184. if u.f <= t(2).f
  185. t(3) = t(2);
  186. t(2) = u;
  187. elseif u.f <= t(3).f
  188. t(3) = u;
  189. end
  190. end
  191. end
  192. %==========================================================================
  193. % function min1d_plot(action,arg1,arg2,arg3)
  194. %==========================================================================
  195. function min1d_plot(action,arg1,arg2,arg3)
  196. % Visual output for line minimisation
  197. persistent min1dplot
  198. if ~nargin, action = 'Init'; end
  199. % Find the Interactive window and exit if not
  200. %--------------------------------------------------------------------------
  201. fg = spm_figure('FindWin','Interactive');
  202. if isempty(fg), return; end
  203. %-Initialize
  204. %--------------------------------------------------------------------------
  205. if strcmpi(action,'init')
  206. if nargin<4, arg3 = 'Function'; end
  207. if nargin<3, arg2 = 'Value'; end
  208. if nargin<2, arg1 = 'Line minimisation'; end
  209. min1dplot = struct('pointer',get(fg,'Pointer'),...
  210. 'name', get(fg,'Name'),...
  211. 'ax', []);
  212. min1d_plot('Clear');
  213. set(fg,'Pointer','Watch');
  214. min1dplot.ax = axes('Position', [0.15 0.1 0.8 0.75],...
  215. 'Box', 'on',...
  216. 'Parent', fg);
  217. lab = get(min1dplot.ax,'Xlabel');
  218. set(lab,'string',arg3,'FontSize',10);
  219. lab = get(min1dplot.ax,'Ylabel');
  220. set(lab,'string',arg2,'FontSize',10);
  221. lab = get(min1dplot.ax,'Title');
  222. set(lab,'string',arg1);
  223. line('Xdata',[], 'Ydata',[],...
  224. 'LineWidth',2,'Tag','LinMinPlot',...
  225. 'LineStyle','-','Marker','o',...
  226. 'Parent',min1dplot.ax);
  227. drawnow;
  228. %-Reset
  229. %--------------------------------------------------------------------------
  230. elseif strcmpi(action,'set')
  231. br = findobj(fg,'Tag','LinMinPlot');
  232. if ~isempty(br)
  233. [xd,indx] = sort([get(br,'Xdata') arg1]);
  234. yd = [get(br,'Ydata') arg2];
  235. yd = yd(indx);
  236. set(br,'Ydata',yd,'Xdata',xd);
  237. drawnow;
  238. end
  239. %-Clear
  240. %--------------------------------------------------------------------------
  241. elseif strcmpi(action,'clear')
  242. fg = spm_figure('FindWin','Interactive');
  243. if isstruct(min1dplot)
  244. if ishandle(min1dplot.ax), delete(min1dplot.ax); end
  245. set(fg,'Pointer',min1dplot.pointer);
  246. set(fg,'Name',min1dplot.name);
  247. end
  248. spm_figure('Clear',fg);
  249. drawnow;
  250. end