From 2d83b2216d7b9518718efd83808a1d7c47c488dd Mon Sep 17 00:00:00 2001
From: sebastien <sebastien@ac1d8469-bf42-47a9-8791-bf33cf982152>
Date: Tue, 23 Sep 2008 09:06:03 +0000
Subject: [PATCH] 4.0: merged changesets 2078, 2089 and 2090 from trunk * new
 debug messages in dynare_solve.m * solve_algo=2 exits immediately after
 failure to solve one block * new solve_algo=4 with different way of dealing
 with badly scaled or nearly singular Jacobian * solve_algo=0 fails if
 Matlab's Optimization Toolbox not present * dynare_solve fails if solve_algo
 not in [0,1,2,3,4]

git-svn-id: https://www.dynare.org/svn/dynare/branches/4.0@2095 ac1d8469-bf42-47a9-8791-bf33cf982152
---
 matlab/dynare_solve.m |  80 +++++++++++---------
 matlab/solve2.m       | 171 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 215 insertions(+), 36 deletions(-)
 create mode 100644 matlab/solve2.m

diff --git a/matlab/dynare_solve.m b/matlab/dynare_solve.m
index c376a4f1aa..2619c4cc25 100644
--- a/matlab/dynare_solve.m
+++ b/matlab/dynare_solve.m
@@ -38,33 +38,31 @@ function [x,info] = dynare_solve(func,x,jacobian_flag,varargin)
   options_ = set_default_option(options_,'solve_algo',2);
   info = 0;
   if options_.solve_algo == 0
-    if ~isempty(which('fsolve'))
-      options=optimset('fsolve');
-      options.MaxFunEvals = 50000;
-      options.MaxIter = 2000;
-      options.TolFun=1e-8;
-      options.Display = 'iter';
-      if jacobian_flag
-	options.Jacobian = 'on';
-      else
-	options.Jacobian = 'off';
-      end
-      [x,fval,exitval,output] = fsolve(func,x,options,varargin{:});
-      if exitval > 0
-	info = 0;
-      else
-	info = 1;
-      end
-      return
-    else 
-      options_.solve_algo = 1;
+    if exist('OCTAVE_VERSION') || isempty(ver('optim'))
+      % Note that fsolve() exists under Octave, but has a different syntax
+      % So we fail for the moment under Octave, until we add the corresponding code
+      error('DYNARE_SOLVE: you can''t use solve_algo=0 since you don''t have Matlab''s Optimization Toolbox')
     end
-  end
-
-  if options_.solve_algo == 1
+    options=optimset('fsolve');
+    options.MaxFunEvals = 50000;
+    options.MaxIter = 2000;
+    options.TolFun=1e-8;
+    options.Display = 'iter';
+    if jacobian_flag
+      options.Jacobian = 'on';
+    else
+      options.Jacobian = 'off';
+    end
+    [x,fval,exitval,output] = fsolve(func,x,options,varargin{:});
+    if exitval > 0
+      info = 0;
+    else
+      info = 1;
+    end
+  elseif options_.solve_algo == 1
     nn = size(x,1);
     [x,info]=solve1(func,x,1:nn,1:nn,jacobian_flag,varargin{:});
-  elseif options_.solve_algo == 2
+  elseif options_.solve_algo == 2 || options_.solve_algo == 4
     nn = size(x,1) ;
     tolf = options_.solve_tolf ;
 
@@ -84,8 +82,6 @@ function [x,info] = dynare_solve(func,x,jacobian_flag,varargin)
       error('exiting ...')
     end
     
-%    f = 0.5*fvec'*fvec ;
-
     if max(abs(fvec)) < tolf
       return ;
     end
@@ -102,25 +98,37 @@ function [x,info] = dynare_solve(func,x,jacobian_flag,varargin)
 
     [j1,j2,r,s] = dmperm(fjac);
     
+    if options_.debug
+      disp(['DYNARE_SOLVE (solve_algo=2|4): number of blocks = ' num2str(length(r))]);
+    end
+    
     for i=length(r)-1:-1:1
-      [x,info]=solve1(func,x,j1(r(i):r(i+1)-1),j2(r(i):r(i+1)-1),jacobian_flag,varargin{:});
-      if info & options_.debug
-	error(sprintf('Solve block = %d check = %d\n',i,info));
+      if options_.debug
+        disp(['DYNARE_SOLVE (solve_algo=2|4): solving block ' num2str(i) ', of size ' num2str(r(i+1)-r(i)) ]);
+      end
+      if options_.solve_algo == 2
+        [x,info]=solve1(func,x,j1(r(i):r(i+1)-1),j2(r(i):r(i+1)-1),jacobian_flag,varargin{:});
+      else % solve_algo=4
+        [x,info]=solve2(func,x,j1(r(i):r(i+1)-1),j2(r(i):r(i+1)-1),jacobian_flag,varargin{:});
+      end
+      if info
+        return
       end
     end
     fvec = feval(func,x,varargin{:});
     if max(abs(fvec)) > tolf
-      [x,info]=solve1(func,x,1:nn,1:nn,jacobian_flag,varargin{:});
+      if options_.solve_algo == 2
+        [x,info]=solve1(func,x,1:nn,1:nn,jacobian_flag,varargin{:});
+      else % solve_algo=4
+        [x,info]=solve2(func,x,1:nn,1:nn,jacobian_flag,varargin{:});
+      end
     end
   elseif options_.solve_algo == 3
     if jacobian_flag
       [x,info] = csolve(func,x,func,1e-6,500,varargin{:});
     else
       [x,info] = csolve(func,x,[],1e-6,500,varargin{:});
-    end 
+    end
+  else
+    error('DYNARE_SOLVE: option solve_algo must be one of [0,1,2,3,4]')
   end
-%    fvec1 = feval(func,x,varargin{:})
-
-  % 08/28/03 MJ add a final call to solve1 for solve_algo == 1 in case
-  %             initvals generates 'false' zeros in the Jacobian
-  
\ No newline at end of file
diff --git a/matlab/solve2.m b/matlab/solve2.m
new file mode 100644
index 0000000000..db0012fcd6
--- /dev/null
+++ b/matlab/solve2.m
@@ -0,0 +1,171 @@
+function [x,check] = solve2(func,x,j1,j2,jacobian_flag,varargin)
+% function [x,check] = solve1(func,x,j1,j2,jacobian_flag,varargin)
+% Solves systems of non linear equations of several variables
+%
+% This solver is used for solve_algo = ...
+%
+% This is a modified version of solve1:
+% In solve1, before proceeding to the Newton step, we first check
+% that the condition number is reasonable, and we use an alternate
+% step formula if it is not the case.
+% Here, we first do the Newton step, then we check if the left
+% matrix division returned a warning (in case of badly scale or
+% nearly singular jacobian) in which case we use the alternate
+% step formula.
+%
+% INPUTS
+%    func:            name of the function to be solved
+%    x:               guess values
+%    j1:              equations index for which the model is solved
+%    j2:              unknown variables index
+%    jacobian_flag=1: jacobian given by the 'func' function
+%    jacobian_flag=0: jacobian obtained numerically
+%    varargin:        list of arguments following jacobian_flag
+%    
+% OUTPUTS
+%    x:               results
+%    check=1:         the model can not be solved
+%
+% SPECIAL REQUIREMENTS
+%    none
+
+% Copyright (C) 2001-2008 Dynare Team
+%
+% This file is part of Dynare.
+%
+% Dynare is free software: you can redistribute it and/or modify
+% it under the terms of the GNU General Public License as published by
+% the Free Software Foundation, either version 3 of the License, or
+% (at your option) any later version.
+%
+% Dynare is distributed in the hope that it will be useful,
+% but WITHOUT ANY WARRANTY; without even the implied warranty of
+% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+% GNU General Public License for more details.
+%
+% You should have received a copy of the GNU General Public License
+% along with Dynare.  If not, see <http://www.gnu.org/licenses/>.
+
+  global M_ options_ fjac  
+
+  nn = length(j1);
+  fjac = zeros(nn,nn) ;
+  g = zeros(nn,1) ;
+
+  tolf = options_.solve_tolf ;
+  tolx = options_.solve_tolx;
+  tolmin = tolx ;
+
+  stpmx = 100 ;
+  maxit = options_.solve_maxit ;
+
+  check = 0 ;
+
+  fvec = feval(func,x,varargin{:});
+  fvec = fvec(j1);
+  
+  i = find(~isfinite(fvec));
+  
+  if ~isempty(i)
+    disp(['STEADY:  numerical initial values incompatible with the following' ...
+	  ' equations'])
+    disp(j1(i)')
+  end
+  
+  f = 0.5*fvec'*fvec ;
+
+  if max(abs(fvec)) < tolf
+    return ;
+  end
+
+  stpmax = stpmx*max([sqrt(x'*x);nn]) ;
+  first_time = 1;
+  for its = 1:maxit
+    if jacobian_flag
+      [fvec,fjac] = feval(func,x,varargin{:});
+      fvec = fvec(j1);
+      fjac = fjac(j1,j2);
+    else
+      dh = max(abs(x(j2)),options_.gstep*ones(nn,1))*eps^(1/3);
+      
+      for j = 1:nn
+	xdh = x ;
+	xdh(j2(j)) = xdh(j2(j))+dh(j) ;
+	t = feval(func,xdh,varargin{:});
+	fjac(:,j) = (t(j1) - fvec)./dh(j) ;
+	g(j) = fvec'*fjac(:,j) ;
+      end
+    end
+
+    g = (fvec'*fjac)';
+    if options_.debug
+      disp(['cond(fjac) ' num2str(cond(fjac))])
+    end
+    M_.unit_root = 0;
+    if M_.unit_root
+      if first_time
+	    first_time = 0;
+	    [q,r,e]=qr(fjac);
+	    n = sum(abs(diag(r)) < 1e-12);
+	    fvec = q'*fvec;
+	    p = e*[-r(1:end-n,1:end-n)\fvec(1:end-n);zeros(n,1)];
+	    disp(' ')
+	    disp('STEADY with unit roots:')
+	    disp(' ')
+	    if n > 0
+	      disp(['   The following variable(s) kept their value given in INITVAL' ...
+		    ' or ENDVAL'])
+	      disp(char(e(:,end-n+1:end)'*M_.endo_names))
+        else
+	      disp('   STEADY can''t find any unit root!')
+	    end
+      else
+	    [q,r]=qr(fjac*e);
+	    fvec = q'*fvec;
+	    p = e*[-r(1:end-n,1:end-n)\fvec(1:end-n);zeros(n,1)];
+      end	
+    else
+      lastwarn('');
+      p = -fjac\fvec;
+      if ~isempty(lastwarn)
+        fjac2=fjac'*fjac;
+        p=-(fjac2+sqrt(nn*eps)*max(sum(abs(fjac2)))*eye(nn))\(fjac'*fvec);
+      end
+    end
+    xold = x ;
+    fold = f ;
+
+    [x,f,fvec,check]=lnsrch1(xold,fold,g,p,stpmax,func,j1,j2,varargin{:});
+
+    if options_.debug
+      disp([its f])
+      disp([xold x])
+    end
+      
+    if check > 0
+      den = max([f;0.5*nn]) ;
+      if max(abs(g).*max([abs(x(j2)') ones(1,nn)])')/den < tolmin
+	    return
+      else
+	    disp (' ')
+	    disp (['SOLVE: Iteration ' num2str(its)])
+	    disp (['Spurious convergence.'])
+	    disp (x)
+	    return
+      end
+
+      if max(abs(x(j2)-xold(j2))./max([abs(x(j2)') ones(1,nn)])') < tolx
+	disp (' ')
+	disp (['SOLVE: Iteration ' num2str(its)])
+	disp (['Convergence on dX.'])
+	disp (x)
+	return
+      end
+    elseif max(abs(fvec)) < tolf
+      return
+    end
+  end
+  
+  check = 1;
+  disp(' ')
+  disp('SOLVE: maxit has been reached')
-- 
GitLab