PageRenderTime 22ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/newtonraphson.m

http://github.com/mikofski/IAPWS_IF97
Objective C | 251 lines | 247 code | 4 blank | 0 comment | 39 complexity | b4844100767d95886ec8208676f15a10 MD5 | raw file
  1. function [x, resnorm, F, exitflag, output, jacob] = newtonraphson(fun, x0, options)
  2. % NEWTONRAPHSON Solve set of non-linear equations using Newton-Raphson method.
  3. %
  4. % [X, RESNORM, F, EXITFLAG, OUTPUT, JACOB] = NEWTONRAPHSON(FUN, X0, OPTIONS)
  5. % FUN is a function handle that returns a vector of residuals equations, F,
  6. % and takes a vector, x, as its only argument. When the equations are
  7. % solved by x, then F(x) == zeros(size(F(:), 1)).
  8. %
  9. % Optionally FUN may return the Jacobian, Jij = dFi/dxj, as an additional
  10. % output. The Jacobian must have the same number of rows as F and the same
  11. % number of columns as x. The columns of the Jacobians correspond to d/dxj and
  12. % the rows correspond to dFi/d.
  13. %
  14. % EG: J23 = dF2/dx3 is the 2nd row ad 3rd column.
  15. %
  16. % If FUN only returns one output, then J is estimated using a center
  17. % difference approximation,
  18. %
  19. % Jij = dFi/dxj = (Fi(xj + dx) - Fi(xj - dx))/2/dx.
  20. %
  21. % NOTE: If the Jacobian is not square the system is either over or under
  22. % constrained.
  23. %
  24. % X0 is a vector of initial guesses.
  25. %
  26. % OPTIONS is a structure of solver options created using OPTIMSET.
  27. % EG: options = optimset('TolX', 0.001).
  28. %
  29. % The following options can be set:
  30. % * OPTIONS.TOLFUN is the maximum tolerance of the norm of the residuals.
  31. % [1e-6]
  32. % * OPTIONS.TOLX is the minimum tolerance of the relative maximum stepsize.
  33. % [1e-6]
  34. % * OPTIONS.MAXITER is the maximum number of iterations before giving up.
  35. % [100]
  36. % * OPTIONS.DISPLAY sets the level of display: {'off', 'iter'}.
  37. % ['iter']
  38. %
  39. % X is the solution that solves the set of equations within the given tolerance.
  40. % RESNORM is norm(F) and F is F(X). EXITFLAG is an integer that corresponds to
  41. % the output conditions, OUTPUT is a structure containing the number of
  42. % iterations, the final stepsize and exitflag message and JACOB is the J(X).
  43. %
  44. % See also OPTIMSET, OPTIMGET, FMINSEARCH, FZERO, FMINBND, FSOLVE, LSQNONLIN
  45. %
  46. % References:
  47. % * http://en.wikipedia.org/wiki/Newton's_method
  48. % * http://en.wikipedia.org/wiki/Newton's_method_in_optimization
  49. % * 9.7 Globally Convergent Methods for Nonlinear Systems of Equations 383,
  50. % Numerical Recipes in C, Second Edition (1992),
  51. % http://www.nrbook.com/a/bookcpdf.php
  52. % Version 0.5
  53. % * allow sparse matrices, replace cond() with condest()
  54. % * check if Jstar has NaN or Inf, return NaN or Inf for cond() and return
  55. % exitflag: -1, matrix is singular.
  56. % * fix bug: max iteration detection and exitflag reporting typos
  57. % Version 0.4
  58. % * allow lsq curve fitting type problems, IE non-square matrices
  59. % * exit if J is singular or if dx is NaN or Inf
  60. % Version 0.3
  61. % * Display RCOND each step.
  62. % * Replace nargout checking in funwrapper with ducktypin.
  63. % * Remove Ftyp and F scaling b/c F(typx)->0 & F/Ftyp->Inf!
  64. % * User Numerical Recipies minimum Newton step, backtracking line search
  65. % with alpha = 1e-4, min_lambda = 0.1 and max_lambda = 0.5.
  66. % * Output messages, exitflag and min relative step.
  67. % Version 0.2
  68. % * Remove `options.FinDiffRelStep` and `options.TypicalX` since not in MATLAB.
  69. % * Set `dx = eps^(1/3)` in `jacobian` function.
  70. % * Remove `options` argument from `funwrapper` & `jacobian` functions
  71. % since no longer needed.
  72. % * Set typx = x0; typx(x0==0) = 1; % use initial guess as typx, if 0 use 1.
  73. % * Replace `feval` with `evalf` since `feval` is builtin.
  74. %% initialize
  75. % There are no argument checks!
  76. x0 = x0(:); % needs to be a column vector
  77. % set default options
  78. oldopts = optimset( ...
  79. 'TolX', 1e-12, 'TolFun', 1e-6, 'MaxIter', 100, 'Display', 'iter');
  80. if nargin<3
  81. options = oldopts; % use defaults
  82. else
  83. options = optimset(oldopts, options); % update default with user options
  84. end
  85. FUN = @(x)funwrapper(fun, x); % wrap FUN so it always returns J
  86. %% get options
  87. TOLX = optimget(options, 'TolX'); % relative max step tolerance
  88. TOLFUN = optimget(options, 'TolFun'); % function tolerance
  89. MAXITER = optimget(options, 'MaxIter'); % max number of iterations
  90. DISPLAY = strcmpi('iter', optimget(options, 'Display')); % display iterations
  91. TYPX = max(abs(x0), 1); % x scaling value, remove zeros
  92. ALPHA = 1e-4; % criteria for decrease
  93. MIN_LAMBDA = 0.1; % min lambda
  94. MAX_LAMBDA = 0.5; % max lambda
  95. %% set scaling values
  96. % TODO: let user set weights
  97. weight = ones(numel(FUN(x0)),1);
  98. J0 = weight*(1./TYPX'); % Jacobian scaling matrix
  99. %% set display
  100. if DISPLAY
  101. fprintf('\n%10s %10s %10s %10s %10s %12s\n', 'Niter', 'resnorm', 'stepnorm', ...
  102. 'lambda', 'rcond', 'convergence')
  103. for n = 1:67,fprintf('-'),end,fprintf('\n')
  104. fmtstr = '%10d %10.4g %10.4g %10.4g %10.4g %12.4g\n';
  105. printout = @(n, r, s, l, rc, c)fprintf(fmtstr, n, r, s, l, rc, c);
  106. end
  107. %% check initial guess
  108. x = x0; % initial guess
  109. [F, J] = FUN(x); % evaluate initial guess
  110. Jstar = J./J0; % scale Jacobian
  111. if any(isnan(Jstar(:))) || any(isinf(Jstar(:)))
  112. exitflag = -1; % matrix may be singular
  113. else
  114. exitflag = 1; % normal exit
  115. end
  116. if issparse(Jstar)
  117. rc = 1/condest(Jstar);
  118. else
  119. if any(isnan(Jstar(:)))
  120. rc = NaN;
  121. elseif any(isinf(Jstar(:)))
  122. rc = Inf;
  123. else
  124. rc = 1/cond(Jstar); % reciprocal condition
  125. end
  126. end
  127. resnorm = norm(F); % calculate norm of the residuals
  128. dx = zeros(size(x0));convergence = Inf; % dummy values
  129. %% solver
  130. Niter = 0; % start counter
  131. lambda = 1; % backtracking
  132. if DISPLAY,printout(Niter, resnorm, norm(dx), lambda, rc, convergence);end
  133. while (resnorm>TOLFUN || lambda<1) && exitflag>=0 && Niter<=MAXITER
  134. if lambda==1
  135. %% Newton-Raphson solver
  136. Niter = Niter+1; % increment counter
  137. dx_star = -Jstar\F; % calculate Newton step
  138. % NOTE: use isnan(f) || isinf(f) instead of STPMAX
  139. dx = dx_star.*TYPX; % rescale x
  140. g = F'*Jstar; % gradient of resnorm
  141. slope = g*dx_star; % slope of gradient
  142. fold = F'*F; % objective function
  143. xold = x; % initial value
  144. lambda_min = TOLX/max(abs(dx)./max(abs(xold), 1));
  145. end
  146. if lambda<lambda_min
  147. exitflag = 2; % x is too close to XOLD
  148. break
  149. elseif any(isnan(dx)) || any(isinf(dx))
  150. exitflag = -1; % matrix may be singular
  151. break
  152. end
  153. x = xold+dx*lambda; % next guess
  154. [F, J] = FUN(x); % evaluate next residuals
  155. Jstar = J./J0; % scale next Jacobian
  156. f = F'*F; % new objective function
  157. %% check for convergence
  158. lambda1 = lambda; % save previous lambda
  159. if f>fold+ALPHA*lambda*slope
  160. if lambda==1
  161. lambda = -slope/2/(f-fold-slope); % calculate lambda
  162. else
  163. A = 1/(lambda1 - lambda2);
  164. B = [1/lambda1^2,-1/lambda2^2;-lambda2/lambda1^2,lambda1/lambda2^2];
  165. C = [f-fold-lambda1*slope;f2-fold-lambda2*slope];
  166. coeff = num2cell(A*B*C);
  167. [a,b] = coeff{:};
  168. if a==0
  169. lambda = -slope/2/b;
  170. else
  171. discriminant = b^2 - 3*a*slope;
  172. if discriminant<0
  173. lambda = MAX_LAMBDA*lambda1;
  174. elseif b<=0
  175. lambda = (-b+sqrt(discriminant))/3/a;
  176. else
  177. lambda = -slope/(b+sqrt(discriminant));
  178. end
  179. end
  180. lambda = min(lambda,MAX_LAMBDA*lambda1); % minimum step length
  181. end
  182. elseif isnan(f) || isinf(f)
  183. % limit undefined evaluation or overflow
  184. lambda = MAX_LAMBDA*lambda1;
  185. else
  186. lambda = 1; % fraction of Newton step
  187. end
  188. if lambda<1
  189. lambda2 = lambda1;f2 = f; % save 2nd most previous value
  190. lambda = max(lambda,MIN_LAMBDA*lambda1); % minimum step length
  191. continue
  192. end
  193. %% display
  194. resnorm0 = resnorm; % old resnorm
  195. resnorm = norm(F); % calculate new resnorm
  196. convergence = log(resnorm0/resnorm); % calculate convergence rate
  197. stepnorm = norm(dx); % norm of the step
  198. if any(isnan(Jstar(:))) || any(isinf(Jstar(:)))
  199. exitflag = -1; % matrix may be singular
  200. break
  201. end
  202. if issparse(Jstar)
  203. rc = 1/condest(Jstar);
  204. else
  205. rc = 1/cond(Jstar); % reciprocal condition
  206. end
  207. if DISPLAY,printout(Niter, resnorm, stepnorm, lambda1, rc, convergence);end
  208. end
  209. %% output
  210. output.iterations = Niter; % final number of iterations
  211. output.stepsize = dx; % final stepsize
  212. output.lambda = lambda; % final lambda
  213. if Niter>=MAXITER
  214. exitflag = 0;
  215. output.message = 'Number of iterations exceeded OPTIONS.MAXITER.';
  216. elseif exitflag==2
  217. output.message = 'May have converged, but X is too close to XOLD.';
  218. elseif exitflag==-1
  219. output.message = 'Matrix may be singular. Step was NaN or Inf.';
  220. else
  221. output.message = 'Normal exit.';
  222. end
  223. jacob = J;
  224. end
  225. function [F, J] = funwrapper(fun, x)
  226. % if nargout<2 use finite differences to estimate J
  227. try
  228. [F, J] = fun(x);
  229. catch
  230. F = fun(x);
  231. J = jacobian(fun, x); % evaluate center diff if no Jacobian
  232. end
  233. F = F(:); % needs to be a column vector
  234. end
  235. function J = jacobian(fun, x)
  236. % estimate J
  237. dx = eps^(1/3); % finite difference delta
  238. nx = numel(x); % degrees of freedom
  239. nf = numel(fun(x)); % number of functions
  240. J = zeros(nf,nx); % matrix of zeros
  241. for n = 1:nx
  242. % create a vector of deltas, change delta_n by dx
  243. delta = zeros(nx, 1); delta(n) = delta(n)+dx;
  244. dF = fun(x+delta)-fun(x-delta); % delta F
  245. J(:, n) = dF(:)/dx/2; % derivatives dF/d_n
  246. end
  247. end