spm_diff.m 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. function [varargout] = spm_diff(varargin)
  2. % matrix high-order numerical differentiation
  3. % FORMAT [dfdx] = spm_diff(f,x,...,n)
  4. % FORMAT [dfdx] = spm_diff(f,x,...,n,V)
  5. % FORMAT [dfdx] = spm_diff(f,x,...,n,'q')
  6. %
  7. % f - [inline] function f(x{1},...)
  8. % x - input argument[s]
  9. % n - arguments to differentiate w.r.t.
  10. %
  11. % V - cell array of matrices that allow for differentiation w.r.t.
  12. % to a linear transformation of the parameters: i.e., returns
  13. %
  14. % df/dy{i}; x = V{i}y{i}; V = dx(i)/dy(i)
  15. %
  16. % q - (char) flag to preclude default concatenation of dfdx
  17. %
  18. % dfdx - df/dx{i} ; n = i
  19. % dfdx{p}...{q} - df/dx{i}dx{j}(q)...dx{k}(p) ; n = [i j ... k]
  20. %
  21. %
  22. % This routine has the same functionality as spm_ddiff, however it
  23. % uses one sample point to approximate gradients with numerical (finite)
  24. % differences:
  25. %
  26. % dfdx = (f(x + dx)- f(x))/dx
  27. %__________________________________________________________________________
  28. % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging
  29. % Karl Friston
  30. % $Id: spm_diff.m 7143 2017-07-29 18:50:38Z karl $
  31. % step size for numerical derivatives
  32. %--------------------------------------------------------------------------
  33. global GLOBAL_DX
  34. if ~isempty(GLOBAL_DX)
  35. dx = GLOBAL_DX;
  36. else
  37. dx = exp(-8);
  38. end
  39. % create inline object
  40. %--------------------------------------------------------------------------
  41. f = spm_funcheck(varargin{1});
  42. % parse input arguments
  43. %--------------------------------------------------------------------------
  44. if iscell(varargin{end})
  45. x = varargin(2:(end - 2));
  46. n = varargin{end - 1};
  47. V = varargin{end};
  48. q = 1;
  49. elseif isnumeric(varargin{end})
  50. x = varargin(2:(end - 1));
  51. n = varargin{end};
  52. V = cell(1,length(x));
  53. q = 1;
  54. elseif ischar(varargin{end})
  55. x = varargin(2:(end - 2));
  56. n = varargin{end - 1};
  57. V = cell(1,length(x));
  58. q = 0;
  59. else
  60. error('improper call')
  61. end
  62. % check transform matrices V = dxdy
  63. %--------------------------------------------------------------------------
  64. for i = 1:length(x)
  65. try
  66. V{i};
  67. catch
  68. V{i} = [];
  69. end
  70. if isempty(V{i}) && any(n == i);
  71. V{i} = speye(spm_length(x{i}));
  72. end
  73. end
  74. % initialise
  75. %--------------------------------------------------------------------------
  76. m = n(end);
  77. xm = spm_vec(x{m});
  78. J = cell(1,size(V{m},2));
  79. % proceed to derivatives
  80. %==========================================================================
  81. if length(n) == 1
  82. % dfdx
  83. %----------------------------------------------------------------------
  84. f0 = f(x{:});
  85. for i = 1:length(J)
  86. xi = x;
  87. xi{m} = spm_unvec(xm + V{m}(:,i)*dx,x{m});
  88. J{i} = spm_dfdx(f(xi{:}),f0,dx);
  89. end
  90. % return numeric array for first-order derivatives
  91. %======================================================================
  92. % vectorise f
  93. %----------------------------------------------------------------------
  94. f = spm_vec(f0);
  95. % if there are no arguments to differentiate w.r.t. ...
  96. %----------------------------------------------------------------------
  97. if isempty(xm)
  98. J = sparse(length(f),0);
  99. % or there are no arguments to differentiate
  100. %----------------------------------------------------------------------
  101. elseif isempty(f)
  102. J = sparse(0,length(xm));
  103. end
  104. % differentiation of a scalar or vector
  105. %----------------------------------------------------------------------
  106. if isnumeric(f0) && iscell(J) && q
  107. J = spm_dfdx_cat(J);
  108. end
  109. % assign output argument and return
  110. %----------------------------------------------------------------------
  111. varargout{1} = J;
  112. varargout{2} = f0;
  113. else
  114. % dfdxdxdx....
  115. %----------------------------------------------------------------------
  116. f0 = cell(1,length(n));
  117. [f0{:}] = spm_diff(f,x{:},n(1:end - 1),V);
  118. p = true;
  119. for i = 1:length(J)
  120. xi = x;
  121. xmi = xm + V{m}(:,i)*dx;
  122. xi{m} = spm_unvec(xmi,x{m});
  123. fi = spm_diff(f,xi{:},n(1:end - 1),V);
  124. J{i} = spm_dfdx(fi,f0{1},dx);
  125. p = p & isnumeric(J{i});
  126. end
  127. % or differentiation of a scalar or vector
  128. %----------------------------------------------------------------------
  129. if p && q
  130. J = spm_dfdx_cat(J);
  131. end
  132. varargout = [{J} f0];
  133. end
  134. function dfdx = spm_dfdx(f,f0,dx)
  135. % cell subtraction
  136. %__________________________________________________________________________
  137. if iscell(f)
  138. dfdx = f;
  139. for i = 1:length(f(:))
  140. dfdx{i} = spm_dfdx(f{i},f0{i},dx);
  141. end
  142. elseif isstruct(f)
  143. dfdx = (spm_vec(f) - spm_vec(f0))/dx;
  144. else
  145. dfdx = (f - f0)/dx;
  146. end
  147. return
  148. function J = spm_dfdx_cat(J)
  149. % concatenate into a matrix
  150. %--------------------------------------------------------------------------
  151. if isvector(J{1})
  152. if size(J{1},2) == 1
  153. J = spm_cat(J);
  154. else
  155. J = spm_cat(J')';
  156. end
  157. end