2022-04-10 15:51:55 +02:00
function [x, errorflag, fvec, fjac, errorcode] = dynare_solve ( f, x, maxit, tolf, tolx, options, varargin)
2020-02-20 13:07:54 +01:00
% Solves a nonlinear system of equations, f(x) = 0 with n unknowns
% and n equations.
2008-01-03 13:12:07 +01:00
%
% INPUTS
2020-02-20 13:07:54 +01:00
% - f [char, fhandle] function to be solved
% - x [double] n× 1 vector, initial guess.
% - options [struct] Dynare options, aka options_.
% - varargin list of additional arguments to be passed to func.
2014-09-10 16:30:17 +02:00
%
2008-01-03 13:12:07 +01:00
% OUTPUTS
2020-02-20 13:07:54 +01:00
% - x [double] n× 1 vector, solution.
% - errorflag [logical] scalar, true iff the model can not be solved.
% - fvec [double] n× 1 vector, function value at x (f(x), used for debugging when errorflag is true).
% - fjac [double] n× n matrix, Jacobian value at x (J(x), used for debugging when errorflag is true).
2022-04-09 16:03:37 +02:00
% - errorcode [integer] scalar.
%
% REMARKS
% Interpretation of the error code depends on the algorithm, except if value of errorcode is
%
% -10 -> System of equation ill-behaved at the initial guess (Inf, Nans or complex numbers).
% -11 -> Initial guess is a solution of the system of equations.
2020-09-25 23:45:21 +02:00
2023-06-20 15:32:37 +02:00
% Copyright © 2001-2023 Dynare Team
2008-08-01 14:40:33 +02:00
%
% 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
2021-06-09 17:33:48 +02:00
% along with Dynare. If not, see <https://www.gnu.org/licenses/>.
2008-01-03 13:12:07 +01:00
2020-02-20 13:07:54 +01:00
jacobian_flag = options . jacobian_flag ; % true iff Jacobian is returned by f routine (as a second output argument).
2009-12-16 18:17:34 +01:00
2022-04-09 16:03:37 +02:00
errorflag = false ; % Let's be optimistic!
2012-11-01 09:26:05 +01:00
nn = size ( x , 1 ) ;
2012-10-31 11:08:22 +01:00
2022-04-04 15:51:53 +02:00
% Keep a copy of the initial guess.
x0 = x ;
2017-05-10 23:27:41 +02:00
% Get status of the initial guess (default values?)
if any ( x )
% The current initial guess is not the default for all the variables.
2020-02-20 13:07:54 +01:00
idx = find ( x ) ; % Indices of the variables with default initial guess values.
2017-05-10 23:27:41 +02:00
in0 = length ( idx ) ;
else
% The current initial guess is the default for all the variables.
idx = transpose ( 1 : nn ) ;
in0 = nn ;
end
2012-10-31 11:08:22 +01:00
% checking initial values
if jacobian_flag
2022-11-30 14:42:54 +01:00
[ fvec , fjac ] = feval ( f , x , varargin { : } ) ;
2017-05-10 23:27:41 +02:00
wrong_initial_guess_flag = false ;
2022-04-04 15:35:32 +02:00
if ~ all ( isfinite ( fvec ) ) || any ( isinf ( fjac ( : ) ) ) || any ( isnan ( ( fjac ( : ) ) ) ) || any ( ~ isreal ( fvec ) ) || any ( ~ isreal ( fjac ( : ) ) )
2023-06-20 15:32:37 +02:00
if ~ ismember ( options . solve_algo , [ 10 , 11 ] ) && ~ any ( isnan ( fvec ) ) && max ( abs ( fvec ) ) < tolf
2022-04-04 16:08:11 +02:00
% return if initial value solves the problem except if a mixed complementarity problem is to be solved (complementarity conditions may not be satisfied)
2023-06-20 15:32:37 +02:00
% max([NaN, 0])=0, so explicitly exclude the case where fvec contains a NaN
2022-04-09 16:03:37 +02:00
errorcode = - 11 ;
2019-12-12 18:50:46 +01:00
return ;
2019-12-20 16:28:06 +01:00
end
2023-08-27 13:32:45 +02:00
if options . solve_randomize_initial_guess
if any ( ~ isreal ( fvec ) ) || any ( ~ isreal ( fjac ( : ) ) )
disp_verbose ( ' dynare_solve: starting value results in complex values. Randomize initial guess...' , options . verbosity )
else
disp_verbose ( ' dynare_solve: starting value results in nonfinite/NaN value. Randomize initial guess...' , options . verbosity )
end
% Let's try random numbers for the variables initialized with the default value.
wrong_initial_guess_flag = true ;
% First try with positive numbers.
tentative_number = 0 ;
while wrong_initial_guess_flag && tentative_number < = in0 * 10
tentative_number = tentative_number + 1 ;
x ( idx ) = rand ( in0 , 1 ) * 10 ;
[ fvec , fjac ] = feval ( f , x , varargin { : } ) ;
2023-08-27 13:37:16 +02:00
wrong_initial_guess_flag = ~ all ( isfinite ( fvec ) ) || any ( isinf ( fjac ( : ) ) ) || any ( isnan ( ( fjac ( : ) ) ) ) || any ( ~ isreal ( fvec ) ) || any ( ~ isreal ( fjac ( : ) ) ) ;
2023-08-27 13:32:45 +02:00
end
% If all previous attempts failed, try with real numbers.
tentative_number = 0 ;
while wrong_initial_guess_flag && tentative_number < = in0 * 10
tentative_number = tentative_number + 1 ;
x ( idx ) = randn ( in0 , 1 ) * 10 ;
[ fvec , fjac ] = feval ( f , x , varargin { : } ) ;
2023-08-27 13:37:16 +02:00
wrong_initial_guess_flag = ~ all ( isfinite ( fvec ) ) || any ( isinf ( fjac ( : ) ) ) || any ( isnan ( ( fjac ( : ) ) ) ) || any ( ~ isreal ( fvec ) ) || any ( ~ isreal ( fjac ( : ) ) ) ;
2023-08-27 13:32:45 +02:00
end
% Last tentative, ff all previous attempts failed, try with negative numbers.
tentative_number = 0 ;
while wrong_initial_guess_flag && tentative_number < = in0 * 10
tentative_number = tentative_number + 1 ;
x ( idx ) = - rand ( in0 , 1 ) * 10 ;
[ fvec , fjac ] = feval ( f , x , varargin { : } ) ;
2023-08-27 13:37:16 +02:00
wrong_initial_guess_flag = ~ all ( isfinite ( fvec ) ) || any ( isinf ( fjac ( : ) ) ) || any ( isnan ( ( fjac ( : ) ) ) ) || any ( ~ isreal ( fvec ) ) || any ( ~ isreal ( fjac ( : ) ) ) ;
2023-08-27 13:32:45 +02:00
end
2017-05-10 23:27:41 +02:00
end
2013-06-20 18:53:35 +02:00
end
2012-10-31 11:08:22 +01:00
else
2022-11-30 14:42:54 +01:00
fvec = feval ( f , x , varargin { : } ) ;
2020-02-20 13:07:54 +01:00
fjac = zeros ( nn , nn ) ;
2023-06-20 15:32:37 +02:00
if ~ ismember ( options . solve_algo , [ 10 , 11 ] ) && ~ any ( isnan ( fvec ) ) && max ( abs ( fvec ) ) < tolf
2022-04-04 16:08:11 +02:00
% return if initial value solves the problem except if a mixed complementarity problem is to be solved (complementarity conditions may not be satisfied)
2023-06-20 15:32:37 +02:00
% max([NaN, 0])=0, so explicitly exclude the case where fvec contains a NaN
2022-04-09 16:03:37 +02:00
errorcode = - 11 ;
2022-04-04 15:35:32 +02:00
return ;
end
2017-05-10 23:27:41 +02:00
wrong_initial_guess_flag = false ;
if ~ all ( isfinite ( fvec ) )
% Let's try random numbers for the variables initialized with the default value.
wrong_initial_guess_flag = true ;
% First try with positive numbers.
tentative_number = 0 ;
while wrong_initial_guess_flag && tentative_number < = in0 * 10
tentative_number = tentative_number + 1 ;
x ( idx ) = rand ( in0 , 1 ) * 10 ;
2022-11-30 14:42:54 +01:00
fvec = feval ( f , x , varargin { : } ) ;
2017-05-10 23:27:41 +02:00
wrong_initial_guess_flag = ~ all ( isfinite ( fvec ) ) ;
end
% If all previous attempts failed, try with real numbers.
tentative_number = 0 ;
while wrong_initial_guess_flag && tentative_number < = in0 * 10
tentative_number = tentative_number + 1 ;
x ( idx ) = randn ( in0 , 1 ) * 10 ;
2022-11-30 14:42:54 +01:00
fvec = feval ( f , x , varargin { : } ) ;
2017-05-10 23:27:41 +02:00
wrong_initial_guess_flag = ~ all ( isfinite ( fvec ) ) ;
end
% Last tentative, ff all previous attempts failed, try with negative numbers.
tentative_number = 0 ;
while wrong_initial_guess_flag && tentative_number < = in0 * 10
tentative_number = tentative_number + 1 ;
x ( idx ) = - rand ( in0 , 1 ) * 10 ;
2022-11-30 14:42:54 +01:00
fvec = feval ( f , x , varargin { : } ) ;
2017-05-10 23:27:41 +02:00
wrong_initial_guess_flag = ~ all ( isfinite ( fvec ) ) ;
end
end
2012-10-31 11:08:22 +01:00
end
2017-05-10 23:27:41 +02:00
% Exit with error if no initial guess has been found.
if wrong_initial_guess_flag
2022-04-09 16:03:37 +02:00
errorcode = - 10 ;
2020-02-20 13:07:54 +01:00
errorflag = true ;
2022-04-04 15:51:53 +02:00
x = x0 ;
2017-05-10 23:27:41 +02:00
return
2012-10-31 11:08:22 +01:00
end
2015-05-25 10:06:13 +02:00
if options . solve_algo == 0
2013-11-04 10:54:45 +01:00
if ~ isoctave
2012-01-09 11:06:26 +01:00
if ~ user_has_matlab_license ( ' optimization_toolbox' )
2011-12-19 12:36:35 +01:00
error ( ' You can' ' t use solve_algo=0 since you don' ' t have MATLAB' ' s Optimization Toolbox' )
end
2005-02-18 20:54:39 +01:00
end
2022-07-22 14:09:35 +02:00
if isoctave
options4fsolve = optimset ( ' fsolve' ) ;
2016-10-09 22:15:48 +02:00
else
2022-07-22 14:09:35 +02:00
options4fsolve = optimoptions ( ' fsolve' ) ;
end
2023-11-22 13:24:52 +01:00
if isoctave
2022-07-22 14:09:35 +02:00
options4fsolve . MaxFunEvals = 50000 ;
options4fsolve . MaxIter = maxit ;
options4fsolve . TolFun = tolf ;
options4fsolve . TolX = tolx ;
if jacobian_flag
options4fsolve . Jacobian = ' on' ;
else
options4fsolve . Jacobian = ' off' ;
end
else
options4fsolve . MaxFunctionEvaluations = 50000 ;
options4fsolve . MaxIterations = maxit ;
options4fsolve . FunctionTolerance = tolf ;
options4fsolve . StepTolerance = tolx ;
options4fsolve . SpecifyObjectiveGradient = jacobian_flag ;
2016-10-09 22:15:48 +02:00
end
2022-07-22 14:09:35 +02:00
%% NB: The Display option is accepted but not honoured under Octave (as of version 7)
if options . debug
options4fsolve . Display = ' final' ;
2008-09-22 15:13:04 +02:00
else
2022-07-22 14:09:35 +02:00
options4fsolve . Display = ' off' ;
2008-09-22 15:13:04 +02:00
end
2022-07-22 14:34:02 +02:00
%% This one comes last, so that the user can override Dynare
if ~ isempty ( options . fsolve_options )
if isoctave
eval ( [ ' options4fsolve = optimset(options4fsolve,' options . fsolve_options ' );' ] ) ;
else
eval ( [ ' options4fsolve = optimoptions(options4fsolve,' options . fsolve_options ' );' ] ) ;
end
end
2013-11-04 10:54:45 +01:00
if ~ isoctave
2022-11-30 14:42:54 +01:00
[ x , fvec , errorcode , ~ , fjac ] = fsolve ( f , x , options4fsolve , varargin { : } ) ;
2010-10-28 12:07:33 +02:00
else
% Under Octave, use a wrapper, since fsolve() does not have a 4th arg
2020-02-20 13:07:54 +01:00
if ischar ( f )
f2 = str2func ( f ) ;
2016-08-12 11:59:57 +02:00
else
2020-02-20 13:07:54 +01:00
f2 = f ;
2016-08-12 11:59:57 +02:00
end
2022-11-30 14:42:54 +01:00
[ x , fvec , errorcode , ~ , fjac ] = fsolve ( @ ( x ) f2 ( x , varargin { : } ) , x , options4fsolve ) ;
2010-10-28 12:07:33 +02:00
end
2022-04-09 16:03:37 +02:00
if errorcode == 1
2020-02-20 13:07:54 +01:00
errorflag = false ;
2022-04-09 16:03:37 +02:00
elseif errorcode > 1
if max ( abs ( fvec ) ) > tolf
2020-02-20 13:07:54 +01:00
errorflag = true ;
2015-11-16 11:11:58 +01:00
else
2020-02-20 13:07:54 +01:00
errorflag = false ;
2015-11-16 11:11:58 +01:00
end
2008-09-22 15:13:04 +02:00
else
2020-02-20 13:07:54 +01:00
errorflag = true ;
2008-09-22 15:13:04 +02:00
end
2022-11-30 14:42:54 +01:00
elseif ismember ( options . solve_algo , [ 1 , 12 ] )
%% NB: It is the responsibility of the caller to deal with the block decomposition if solve_algo=12
[ x , errorflag , errorcode ] = solve1 ( f , x , 1 : nn , 1 : nn , jacobian_flag , options . gstep , tolf , tolx , maxit , [ ] , options . debug , varargin { : } ) ;
[ fvec , fjac ] = feval ( f , x , varargin { : } ) ;
2020-02-20 13:07:54 +01:00
elseif options . solve_algo == 9
2022-11-30 14:42:54 +01:00
[ x , errorflag , errorcode ] = trust_region ( f , x , 1 : nn , 1 : nn , jacobian_flag , options . gstep , tolf , tolx , maxit , options . trust_region_initial_step_bound_factor , options . debug , varargin { : } ) ;
[ fvec , fjac ] = feval ( f , x , varargin { : } ) ;
elseif ismember ( options . solve_algo , [ 2 , 4 ] )
if options . solve_algo == 2
2014-02-04 17:55:55 +01:00
solver = @ solve1 ;
else
solver = @ trust_region ;
end
2006-01-18 17:50:33 +01:00
if ~ jacobian_flag
2009-12-16 18:17:34 +01:00
fjac = zeros ( nn , nn ) ;
2020-02-20 13:07:54 +01:00
dh = max ( abs ( x ) , options . gstep ( 1 ) * ones ( nn , 1 ) ) * eps ^( 1 / 3 ) ;
2009-12-16 18:17:34 +01:00
for j = 1 : nn
xdh = x ;
xdh ( j ) = xdh ( j ) + dh ( j ) ;
2022-11-30 14:42:54 +01:00
fjac ( : , j ) = ( feval ( f , xdh , varargin { : } ) - fvec ) ./ dh ( j ) ;
2009-12-16 18:17:34 +01:00
end
2006-01-18 17:50:33 +01:00
end
2020-02-27 22:08:21 +01:00
[ j1 , j2 , r , s ] = dmperm ( fjac ) ;
2015-05-25 10:06:13 +02:00
if options . debug
2022-11-30 14:42:54 +01:00
disp ( [ ' DYNARE_SOLVE (solve_algo=2|4): number of blocks = ' num2str ( length ( r ) - 1 ) ] ) ;
2008-09-16 19:24:11 +02:00
end
2005-02-18 20:54:39 +01:00
for i = length ( r ) - 1 : - 1 : 1
2020-02-20 13:07:54 +01:00
blocklength = r ( i + 1 ) - r ( i ) ;
j = r ( i ) : r ( i + 1 ) - 1 ;
2022-06-27 15:29:48 +02:00
blockcolumns = s ( i + 1 ) - s ( i ) ;
if blockcolumns ~= blocklength
%non-square-block in DM; check whether initial value is solution
2022-11-30 14:42:54 +01:00
[ fval_check , fjac ] = feval ( f , x , varargin { : } ) ;
2022-06-27 15:29:48 +02:00
if norm ( fval_check ( j1 ( j ) ) ) < tolf
errorflag = false ;
errorcode = 0 ;
continue
end
end
if blockcolumns > = blocklength
%(under-)determined block
[ x , errorflag , errorcode ] = solver ( f , x , j1 ( j ) , j2 ( j ) , jacobian_flag , ...
options . gstep , ...
tolf , options . solve_tolx , maxit , ...
options . trust_region_initial_step_bound_factor , ...
2022-11-30 14:42:54 +01:00
options . debug , varargin { : } ) ;
2022-06-27 15:29:48 +02:00
else
2022-11-30 14:42:54 +01:00
fprintf ( ' \nDYNARE_SOLVE (solve_algo=2|4): the Dulmage-Mendelsohn decomposition returned a non-square block. This means that the Jacobian is singular. You may want to try another value for solve_algo.\n' )
2022-06-27 15:29:48 +02:00
%overdetermined block
errorflag = true ;
errorcode = 0 ;
end
2020-02-20 13:07:54 +01:00
if errorflag
2009-12-16 18:17:34 +01:00
return
end
2005-02-18 20:54:39 +01:00
end
2022-11-30 14:42:54 +01:00
fvec = feval ( f , x , varargin { : } ) ;
2020-02-20 13:07:54 +01:00
if max ( abs ( fvec ) ) > tolf
2021-11-23 12:41:35 +01:00
disp_verbose ( ' Call solver on the full nonlinear problem.' , options . verbosity )
2022-04-09 16:03:37 +02:00
[ x , errorflag , errorcode ] = solver ( f , x , 1 : nn , 1 : nn , jacobian_flag , ...
options . gstep , tolf , options . solve_tolx , maxit , ...
options . trust_region_initial_step_bound_factor , ...
2022-11-30 14:42:54 +01:00
options . debug , varargin { : } ) ;
2007-01-04 15:42:27 +01:00
end
2022-11-30 14:42:54 +01:00
[ fvec , fjac ] = feval ( f , x , varargin { : } ) ;
2020-02-20 13:07:54 +01:00
elseif options . solve_algo == 3
2007-11-27 15:16:13 +01:00
if jacobian_flag
2022-11-30 14:42:54 +01:00
[ x , errorcode ] = csolve ( f , x , f , tolf , maxit , varargin { : } ) ;
2007-11-27 15:16:13 +01:00
else
2022-11-30 14:42:54 +01:00
[ x , errorcode ] = csolve ( f , x , [ ] , tolf , maxit , varargin { : } ) ;
2022-04-09 16:03:37 +02:00
end
if errorcode == 0
errorflag = false ;
else
errorflag = true ;
2008-09-22 15:13:04 +02:00
end
2022-11-30 14:42:54 +01:00
[ fvec , fjac ] = feval ( f , x , varargin { : } ) ;
2020-02-20 13:07:54 +01:00
elseif options . solve_algo == 10
2016-05-05 20:40:14 +02:00
% LMMCP
2015-05-25 10:06:13 +02:00
olmmcp = options . lmmcp ;
2022-11-30 14:42:54 +01:00
[ x , fvec , errorcode , ~ , fjac ] = lmmcp ( f , x , olmmcp . lb , olmmcp . ub , olmmcp , varargin { : } ) ;
2021-06-22 12:29:39 +02:00
eq_to_check = find ( isfinite ( olmmcp . lb ) | isfinite ( olmmcp . ub ) ) ;
eq_to_ignore = eq_to_check ( x ( eq_to_check , : ) < = olmmcp . lb ( eq_to_check ) + eps | x ( eq_to_check , : ) > = olmmcp . ub ( eq_to_check ) - eps ) ;
fvec ( eq_to_ignore ) = 0 ;
2022-04-09 16:03:37 +02:00
if errorcode == 1
2020-02-20 13:07:54 +01:00
errorflag = false ;
2014-05-12 17:15:49 +02:00
else
2020-02-20 13:07:54 +01:00
errorflag = true ;
2014-05-12 17:15:49 +02:00
end
2016-05-05 20:40:14 +02:00
elseif options . solve_algo == 11
% PATH mixed complementary problem
% PATH linear mixed complementary problem
if ~ exist ( ' mcppath' )
error ( [ ' PATH can' ' t be provided with Dynare. You need to install it ' ...
' yourself and add its location to Matlab/Octave path before ' ...
' running Dynare' ] )
end
omcppath = options . mcppath ;
global mcp_data
2020-02-20 13:07:54 +01:00
mcp_data . func = f ;
2022-11-30 14:42:54 +01:00
mcp_data . args = varargin ;
2016-07-04 10:29:45 +02:00
try
2020-02-20 13:07:54 +01:00
[ x , fval , jac , mu ] = pathmcp ( x , omcppath . lb , omcppath . ub , ' mcp_func' , omcppath . A , omcppath . b , omcppath . t , omcppath . mu0 ) ;
2016-07-04 10:29:45 +02:00
catch
2020-02-20 13:07:54 +01:00
errorflag = true ;
2016-07-04 10:29:45 +02:00
end
2022-04-09 16:03:37 +02:00
errorcode = nan ; % There is no error code for this algorithm, as PATH is closed source it is unlikely we can fix that.
2021-06-22 12:29:39 +02:00
eq_to_check = find ( isfinite ( omcppath . lb ) | isfinite ( omcppath . ub ) ) ;
eq_to_ignore = eq_to_check ( x ( eq_to_check , : ) < = omcppath . lb ( eq_to_check ) + eps | x ( eq_to_check , : ) > = omcppath . ub ( eq_to_check ) - eps ) ;
fvec ( eq_to_ignore ) = 0 ;
2020-07-16 18:20:07 +02:00
elseif ismember ( options . solve_algo , [ 13 , 14 ] )
2022-11-30 14:42:54 +01:00
%% NB: It is the responsibility of the caller to deal with the block decomposition if solve_algo=14
2020-07-16 18:20:07 +02:00
if ~ jacobian_flag
2022-11-30 14:42:54 +01:00
error ( ' DYNARE_SOLVE: option solve_algo=13 needs computed Jacobian' )
2020-07-16 18:20:07 +02:00
end
2022-11-30 14:42:54 +01:00
[ x , errorflag , errorcode ] = block_trust_region ( f , x , tolf , options . solve_tolx , maxit , ...
options . trust_region_initial_step_bound_factor , ...
options . solve_algo == 13 , ... % Only block-decompose with Dulmage-Mendelsohn for 13, not for 14
options . debug , varargin { : } ) ;
[ fvec , fjac ] = feval ( f , x , varargin { : } ) ;
2009-12-16 18:17:34 +01:00
else
2020-07-16 18:20:07 +02:00
error ( ' DYNARE_SOLVE: option solve_algo must be one of [0,1,2,3,4,9,10,11,12,13,14]' )
2009-12-16 18:17:34 +01:00
end