From c7fc3b3946a4d33799fb0b76b22bc327747c9816 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Adjemian=20=28Argos=29?= <stepan@adjemian.eu> Date: Sun, 6 Oct 2024 22:33:11 +0200 Subject: [PATCH] WIP MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [skip ci] + New routine get_user_defined_boundaries() + Add @dprior method for plotting prior densities. - check_mode_file() signature change (do not pass options_ as an argument, remove bayestopt_), bug fixes (wrong indexing of cell arrays) and add unit tests. + set of private functions under the estimation subfolder (code factorization). + initialguess() returns the initial guess for the estimated parameters + @dprior/isempty() method, return true iff object is empty + derror() Throws an error without the stack trace under MATLAB. Behaviour unchanged under Octave. Ideally,we should print the stack trace when in debug mode. Can we test if we are in debug mode without passing options_? + isbayesian() returns tre iff bayesian approach is considered. - set_prior returns a dprior object. - set_prior ne devrait en principe pas être appelé quand on ne considère pas une approche bayésienne... C'est pourtant le cas car cette fonction retourne aussi la condition initiale et les bornes de l'optimisateur. Il faut casser cette fonction. uniform_specification() was throwing an error if the uniform prior is defined with the expectation and variance. --- matlab/+identification/analysis.m | 7 +- matlab/@dprior/bounds.m | 11 +- matlab/@dprior/data_for_plotting_density.m | 106 ++++ matlab/@dprior/hyperparameters.m | 117 +++++ matlab/@dprior/isempty.m | 96 ++++ matlab/@dprior/subsref.m | 14 +- matlab/default_option_values.m | 2 +- matlab/derror.m | 38 ++ matlab/dynare.m | 12 + .../estimation/PlotPosteriorDistributions.m | 25 +- ...check_bounds_and_definiteness_estimation.m | 27 +- matlab/estimation/check_prior_bounds.m | 7 +- matlab/estimation/dsge_likelihood.m | 81 ++- matlab/estimation/dsge_var_likelihood.m | 72 ++- matlab/estimation/dynare_estimation_1.m | 18 +- matlab/estimation/dynare_estimation_init.m | 205 ++++---- .../estimation/get_user_defined_boundaries.m | 77 +++ matlab/estimation/initial_estimation_checks.m | 20 +- matlab/estimation/initialguess.m | 80 +++ matlab/estimation/isbayesian.m | 150 ++++++ .../estimation/non_linear_dsge_likelihood.m | 8 +- matlab/estimation/plot_priors.m | 31 +- matlab/estimation/posterior_sampler.m | 4 +- .../posterior_sampler_initialization.m | 4 +- ...ize_measurement_errors_covariance_matrix.m | 30 ++ .../set_corrn_observable_correspondence.m | 30 ++ matlab/estimation/private/set_jscale.m | 64 +++ .../set_number_of_estimated_parameters.m | 56 +++ .../set_nvn_observable_correspondence.m | 31 ++ .../private/set_optimization_boundaries.m | 66 +++ .../estimation/private/set_parameter_names.m | 68 +++ matlab/estimation/set_boundaries.m | 68 +++ matlab/estimation/set_prior.m | 419 +++++++-------- matlab/estimation/uniform_specification.m | 45 +- matlab/isdebug.m | 39 ++ .../select_qz_criterium_value.m | 2 +- matlab/utilities/estimation/check_mode_file.m | 475 +++++++++++++++--- .../estimation/check_prior_stderr_corr.m | 13 +- .../check_varobs_are_endo_and_declared_once.m | 18 +- tests/kalman_filter_fast/swk_0.mod | 2 +- 40 files changed, 2048 insertions(+), 590 deletions(-) create mode 100644 matlab/@dprior/data_for_plotting_density.m create mode 100644 matlab/@dprior/hyperparameters.m create mode 100644 matlab/@dprior/isempty.m create mode 100644 matlab/derror.m create mode 100644 matlab/estimation/get_user_defined_boundaries.m create mode 100644 matlab/estimation/initialguess.m create mode 100644 matlab/estimation/isbayesian.m create mode 100644 matlab/estimation/private/initialize_measurement_errors_covariance_matrix.m create mode 100644 matlab/estimation/private/set_corrn_observable_correspondence.m create mode 100644 matlab/estimation/private/set_jscale.m create mode 100644 matlab/estimation/private/set_number_of_estimated_parameters.m create mode 100644 matlab/estimation/private/set_nvn_observable_correspondence.m create mode 100644 matlab/estimation/private/set_optimization_boundaries.m create mode 100644 matlab/estimation/private/set_parameter_names.m create mode 100644 matlab/estimation/set_boundaries.m create mode 100644 matlab/isdebug.m diff --git a/matlab/+identification/analysis.m b/matlab/+identification/analysis.m index 7e7933b862..be686f4ee6 100644 --- a/matlab/+identification/analysis.m +++ b/matlab/+identification/analysis.m @@ -297,11 +297,10 @@ if info(1) == 0 %no errors in solution options_.analytic_derivation = -2; %this sets asy_Hess=1 in dsge_likelihood.m [info, oo_, options_, M_] = stoch_simul(M_, options_, oo_, options_.varobs); dataset_ = dseries(oo_.endo_simul(options_.varobs_id,100+1:end)',dates('1Q1'), options_.varobs); %get information on moments + Prior = dprior(bayestopt_, options_.prior_trunc, false); derivatives_info.no_DLIK = 1; - bounds = prior_bounds(bayestopt_, options_.prior_trunc); %reset bounds as lb and ub must only be operational during mode-finding - %note that for order>1 we do not provide any information on DT,DYss,DOM in derivatives_info, such that dsge_likelihood creates an error. Therefore the computation will be based on simulated_moment_uncertainty for order>1. - [~, info, ~, ~, AHess, ~, ~, M_, options_, ~, oo_.dr] = dsge_likelihood(params', dataset_, dataset_info, options_, M_, estim_params_, bayestopt_, bounds, oo_.dr, oo_.steady_state,oo_.exo_steady_state, oo_.exo_det_steady_state, derivatives_info); %non-used output variables need to be set for octave for some reason - %note that for the order of parameters in AHess we have: stderr parameters come first, corr parameters second, model parameters third. the order within these blocks corresponds to the order specified in the estimated_params block + [~, info, ~, ~, AHess, ~, ~, M_, options_, ~, oo_.dr] = dsge_likelihood(params', dataset_, dataset_info, options_, M_, Prior, estim_params_, bayestopt_, Prior.bounds, oo_.dr, oo_.steady_state,oo_.exo_steady_state, oo_.exo_det_steady_state, derivatives_info); %non-used output variables need to be set for octave for some reason + %note that for the order of parameters in AHess we have: stderr parameters come first, corr parameters second, model parameters third. the order within these blocks corresponds to the order specified in the estimated_params block options_.analytic_derivation = analytic_derivation; %reset option AHess = -AHess; %take negative of hessian if min(eig(AHess))<-tol_rank diff --git a/matlab/@dprior/bounds.m b/matlab/@dprior/bounds.m index d54cc7821d..ab8ca30e3c 100644 --- a/matlab/@dprior/bounds.m +++ b/matlab/@dprior/bounds.m @@ -41,8 +41,7 @@ if inplace end else if ~isempty(o.lb) && (nargin<2 || isempty(truncation) || abs(truncation-o.trunc)<eps(1)) - lub = [o.lb, o.ub]; - o = lub; + o = struct('lb', o.lb, 'ub', o.ub); return end end @@ -125,7 +124,7 @@ if inplace o.lb = lub(:,1); o.ub = lub(:,2); else - o = lub; + o = struct('lb', lub(:,1), 'ub', lub(:,2)); end return % --*-- Unit tests --*-- @@ -209,7 +208,7 @@ try lb = o.lb; ub = o.ub; % Call the tested method - lub = o.bounds(.1, false); + boundaries = o.bounds(.1, false); t(1) = true; catch t(1) = false; @@ -218,8 +217,8 @@ end if t(1) t(2) = all(abs(o.lb-lb)<2*eps(1)); t(3) = all(abs(o.ub-ub)<2*eps(1)); - t(4) = all(lub(:,1)>=lb); - t(5) = all(lub(:,2)<=ub); + t(4) = all(boundaries.lb>=lb); + t(5) = all(boundaries.ub<=ub); end T = all(t); diff --git a/matlab/@dprior/data_for_plotting_density.m b/matlab/@dprior/data_for_plotting_density.m new file mode 100644 index 0000000000..35ddf3dac9 --- /dev/null +++ b/matlab/@dprior/data_for_plotting_density.m @@ -0,0 +1,106 @@ +function [x, f, xlb, xub] = data_for_plotting_density(o, idx, trunc, steps) + +% Evaluate the logged prior density at specified points. +% +% INPUTS +% - o [dprior] object containing prior distribution information. +% - idx [integer] scalar, index of the parameter (based on o.name order). +% - trunc [double] scalar, the probability mass to trim from distribution tails (default: 1e-3). +% - steps [integer] scalar, the number of evaluation points for the density (default: 200). +% +% OUTPUTS +% - x [double] steps×1 vector representing the grid for density evaluation. +% - f [double] steps×1 vector, density. +% - xlb [double] scalar, lower bound of x. +% - xub [double] scalar, upper bound of x. + +% Copyright © 2024 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 <https://www.gnu.org/licenses/>. + + if nargin<4 || isempty(steps) + steps = 200; + end + + if nargin<3 || isempty(trunc) + trunc = 1e-3; + end + + if ismember(idx, o.idbeta) + density = @(x, a, b, aa, bb) betapdf((x-aa)/(bb-aa), a, b)/(bb-aa); + infbound = betainv(trunc, o.p6(idx), o.p7(idx))*(o.p4(idx)-o.p3(idx))+o.p3(idx); + supbound = betainv(1-trunc, o.p6(idx), o.p7(idx))*(o.p4(idx)-o.p3(idx))+o.p3(idx); + nargs = 4; + elseif ismember(idx, o.idgaussian) + density = @(x, a, b) normpdf(x, a, b); + infbound = max(o.p3(idx), norminv(trunc, o.p6(idx), o.p7(idx))); + supbound = min(o.p4(idx), norminv(1-trunc, o.p6(idx), o.p7(idx))); + nargs = 2; + elseif ismember(idx, o.idgamma) + density = @(x,a,b,c) gampdf(x-c, a, b); + infbound = gaminv(trunc, o.p6(idx), o.p7(idx))+o.p3(idx); + supbound = gaminv(1-trunc, o.p6(idx), o.p7(idx))+o.p3(idx); + nargs = 3; + elseif ismember(idx, o.idinvgamma1) + density = @(x, a, b, c) exp(lpdfig1(x-c, a, b)); + % TODO: Check out why we increase the value of trunc here... + infbound = 1.0/sqrt(gaminv(1.0-10.0*trunc, o.p7(idx)/2.0, 2.0/o.p6(idx)))+o.p3(idx); + supbound = 1.0/sqrt(gaminv(10.0*trunc, o.p7(idx)/2.0, 2.0/o.p6(idx)))+o.p3(idx); + nargs = 3; + elseif ismember(idx, o.idinvgamma2) + density = @(x, a, b, c) xp(lpdfig2(x-c, a, b)); + % TODO: Check out why we increase the value of trunc here... + infbound = 1.0/(gaminv(1.0-10.0*trunc, o.p7(idx)/2.0, 2.0/o.p6(idx)))+p3(idx); + supbound = 1.0/(gaminv(10.0*trunc, o.p7(idx)/2.0, 2.0/o.p6(idx)))+o.p3(idx); + nargs = 3; + elseif ismember(idx, o.iduniform) + density = @(x, a, b) x/(b-a); + infbound = o.p6(idx); + supbound = o.p7(idx); + nargs = 2; + elseif ismember(idx, o.idweibull) + density = @(x, a, b, c) exp(lpdfgweibull(x, a, b, c)); + infbound = o.p3(idx)+wblinv(trunc, o.p6(idx), o.p7(idx)); + supbound = o.p3(idx)+wblinv(1-trunc, o.p6(idx), o.p7(idx)); + nargs = 3; + else + derror('Estimation:Prior:Bug', 'Unknown prior shape.') + end + + x = linspace(infbound, supbound, steps); + + xlb = x(1); + xub = x(end); + + switch nargs + case 2 + f = density(x, o.p6(idx), o.p7(idx)); + case 3 + f = density(x, o.p6(idx), o.p7(idx), o.p3(idx)); + case 4 + f = density(x, o.p6(idx), o.p7(idx), o.p3(idx), o.p4(idx)); + otherwise + end + + if ~ismember(idx, o.iduniform) + % Cut densities with mode at the lower or upper bound + [~, k] = max(f); + if k==1 || k==length(x) + f(f>10) = NaN; + end + end + +end diff --git a/matlab/@dprior/hyperparameters.m b/matlab/@dprior/hyperparameters.m new file mode 100644 index 0000000000..72b80985ea --- /dev/null +++ b/matlab/@dprior/hyperparameters.m @@ -0,0 +1,117 @@ +function o = hyperparameters(o, varargin) + +% Compute hyperparameters of the prior distributions +% +% INPUTS +% - o [dprior] Distribution specification for a n×1 vector of independent continuous random variables +% - truncation [double] scalar, prior mass excluded in left and right parts of the prior distribution. Defaut: 0 if inplace==true. +% - inplace [logical] scalar, if true modifies o in place. +% +% OUTPUTS +% - lub [double] n×2 matrix, lower and upper bounds of the prior support +% +% REMARKS +% o.p3 and o.p4 hold the lower and upper natural boundaries of the prior supports, o.lb and o.ub hold the lower and upper bounds +% of the truncated prior distributions (excluding probability mass in the tails). + +% Copyright © 2024 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 <https://www.gnu.org/licenses/>. + + if mod(number,2) ~= 0 + derror('Prior:hyperparameters', 'Input arguments must come by key/value pairs.') + end + + if o.isbeta + if truncation<eps(1) + lub(o.idbeta,1) = o.p3(o.idbeta); + lub(o.idbeta,2) = o.p4(o.idbeta); + else + lub(o.idbeta,1) = betainv(truncation, o.p6(o.idbeta), o.p7(o.idbeta)).*(o.p4(o.idbeta)-o.p3(o.idbeta))+o.p3(o.idbeta); + lub(o.idbeta,2) = betainv(1.0-truncation, o.p6(o.idbeta), o.p7(o.idbeta)).*(o.p4(o.idbeta)-o.p3(o.idbeta))+o.p3(o.idbeta); + end + end + + if o.isgamma + if truncation<eps(1) + lub(o.idgamma,1) = o.p3(o.idgamma); + lub(o.idgamma,2) = Inf; + else + lub(o.idgamma,1) = gaminv(truncation, o.p6(o.idgamma), o.p7(o.idgamma))+o.p3(o.idgamma); + lub(o.idgamma,2) = gaminv(1.0-truncation, o.p6(o.idgamma), o.p7(o.idgamma))+o.p3(o.idgamma); + end + end + + if o.isgaussian + if truncation<eps(1) + lub(o.idgaussian,1) = max(-Inf, o.p3(o.idgaussian)); + lub(o.idgaussian,2) = min(Inf, o.p4(o.idgaussian)); + else + lub(o.idgaussian,1) = max(norminv(truncation, o.p6(o.idgaussian), o.p7(o.idgaussian)), o.p3(o.idgaussian)); + lub(o.idgaussian,2) = min(norminv(1-truncation, o.p6(o.idgaussian), o.p7(o.idgaussian)), o.p4(o.idgaussian)); + end + end + + if o.isinvgamma1 + if truncation<eps(1) + lub(o.idinvgamma1,1) = o.p3(o.idinvgamma1); + lub(o.idinvgamma1,2) = Inf; + else + lub(o.idinvgamma1,1) = 1.0./sqrt(gaminv(1.0-truncation, o.p7(o.idinvgamma1)/2.0, 2.0./o.p6(o.idinvgamma1)))+o.p3(o.idinvgamma1); + lub(o.idinvgamma1,2) = 1.0./sqrt(gaminv(truncation, o.p7(o.idinvgamma1)/2.0, 2.0./o.p6(o.idinvgamma1)))+o.p3(o.idinvgamma1); + end + end + + if o.isuniform + if truncation<eps(1) + lub(o.iduniform,1) = o.p6(o.iduniform); + lub(o.iduniform,2) = o.p7(o.iduniform); + else + lub(o.iduniform,1) = o.p6(o.iduniform)+(o.p7(o.iduniform)-o.p6(o.iduniform))*truncation; + lub(o.iduniform,2) = o.p7(o.iduniform)-(o.p7(o.iduniform)-o.p6(o.iduniform))*truncation; + end + end + + if o.isinvgamma2 + if truncation<eps(1) + lub(o.idinvgamma2,1) = o.p3(o.idinvgamma2); + lub(o.idinvgamma2,2) = Inf; + else + lub(o.idinvgamma2,1) = 1.0./gaminv(1.0-truncation, o.p7(o.idinvgamma2)/2.0, 2.0./o.p6(o.idinvgamma2))+o.p3(o.idinvgamma2); + lub(o.idinvgamma2,2) = 1.0./gaminv(truncation, o.p7(o.idinvgamma2)/2.0, 2.0./o.p6(o.idinvgamma2))+o.p3(o.idinvgamma2); + end + end + + if o.isweibull + if truncation<eps(1) + lub(o.idweibull,1) = o.p3(o.idweibull); + lub(o.idweibull,2) = Inf; + else + lub(o.idweibull,1) = o.p3(o.idweibull)+wblinv(truncation, o.p6(o.idweibull), o.p7(o.idweibull)); + lub(o.idweibull,2) = o.p3(o.idweibull)+wblinv(1.0-truncation, o.p6(o.idweibull), o.p7(o.idweibull)); + end + end + + if inplace + o.lb = lub(:,1); + o.ub = lub(:,2); + else + o = lub; + end + + return % --*-- Unit tests --*-- + +end diff --git a/matlab/@dprior/isempty.m b/matlab/@dprior/isempty.m new file mode 100644 index 0000000000..e2a631db54 --- /dev/null +++ b/matlab/@dprior/isempty.m @@ -0,0 +1,96 @@ +function b = isempty(o) + +% Return true iff dprior object is empty. +% +% INPUTS +% - o [dprior] +% +% OUTPUTS +% - b [logical] scalar, true if o is empty + +% Copyright © 2024 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 <https://www.gnu.org/licenses/>. + + b = ~o.isbeta && ~o.isgaussian && ~o.isuniform && ~o.isgamma && ~o.isinvgamma1 && ~o.isinvgamma2 && ~o.isweibull; + + return % --*-- Unit tests --*-- + + %@test:1 + o = dprior(); + + try + b = o.isempty(); + t(1) = true; + catch + t(1) = false; + end + + if t(1) + t(2) = b; + end + + T = all(t); + %@eof:1 + + + %@test:2 + prior_trunc = 1e-10; + p0 = [1; 2]; % Prior shape + p1 = .4*ones(2,1); % Prior mean + p2 = .2*ones(2,1); % Prior std. + p3 = NaN(2,1); + p4 = NaN(2,1); + p5 = NaN(2,1); + p6 = NaN(2,1); + p7 = NaN(2,1); + + p3(1) = 0; + p4(1) = 1; + [p6(1), p7(1)] = beta_specification(p1(1), p2(1)^2, p3(1), p4(1)); + p5(1) = compute_prior_mode([p6(1) p7(1)], 1); + + p3(2) = 0; + p4(2) = Inf; + [p6(2), p7(2)] = gamma_specification(p1(2), p2(2)^2, p3(2), p4(2)); + p5(2) = compute_prior_mode([p6(2) p7(2)], 2); + + BayesInfo.pshape = p0; + BayesInfo.p1 = p1; + BayesInfo.p2 = p2; + BayesInfo.p3 = p3; + BayesInfo.p4 = p4; + BayesInfo.p5 = p5; + BayesInfo.p6 = p6; + BayesInfo.p7 = p7; + + o = dprior(BayesInfo, prior_trunc); + + try + b = o.isempty(); + t(1) = true; + catch + t(2) = false; + end + + if t(1) + t(2) = ~b; + end + + T = all(t); + %@eof:2 + +end diff --git a/matlab/@dprior/subsref.m b/matlab/@dprior/subsref.m index be49924f8f..5bab04aa3c 100644 --- a/matlab/@dprior/subsref.m +++ b/matlab/@dprior/subsref.m @@ -1,4 +1,4 @@ -function p = subsref(o, S) +function varargout = subsref(o, S) % Overload subsref method. @@ -56,16 +56,16 @@ switch S(1).type derror('dprior:wrongInputArguments', 'First input argument to ishape must be the name of a distribution (row char array).') end elseif isequal(S(1).subs, 'bounds') && length(S)==1 - p = bounds(o, [], false); + varargout{1} = bounds(o, [], false); elseif ismember(S(1).subs, {'deviate','length','isempty'}) - p = feval(S(1).subs, o); - elseif ismember(S(1).subs, {'sample', 'density', 'densities', 'moments', 'admissible', 'bounds'}) - p = feval(S(1).subs, o , S(2).subs{:}); + varargout{1} = feval(S(1).subs, o); + elseif ismember(S(1).subs, {'sample', 'density', 'densities', 'moments', 'admissible', 'bounds', 'data_for_plotting_density'}) + [varargout{1:nargout}] = feval(S(1).subs, o , S(2).subs{:}); elseif ismember(S(1).subs, {'mean', 'median', 'variance', 'mode'}) if (length(S)==2 && isempty(S(2).subs)) || length(S)==1 - p = feval(S(1).subs, o); + varargout{1} = feval(S(1).subs, o); else - p = feval(S(1).subs, o , S(2).subs{:}); + varargout{1} = feval(S(1).subs, o , S(2).subs{:}); end else error('dprior::subsref: unknown method (%s).', S(1).subs) diff --git a/matlab/default_option_values.m b/matlab/default_option_values.m index c991a120f4..d006d5fe9f 100644 --- a/matlab/default_option_values.m +++ b/matlab/default_option_values.m @@ -439,7 +439,7 @@ options_.occbin.smoother.status=false; options_.silent_optimizer = false; % Prior restrictions -options_.prior_restrictions.status = 0; +options_.prior_restrictions.status = false; options_.prior_restrictions.routine = []; options_.mode_compute = 5; diff --git a/matlab/derror.m b/matlab/derror.m new file mode 100644 index 0000000000..2c5b50b890 --- /dev/null +++ b/matlab/derror.m @@ -0,0 +1,38 @@ +function derror(varargin) + +% Throws an error message, without stacktrace under MATLAB. +% +% INPUTS +% - id [char] 1×m array, error id (of the form 'xyz:abcdef') +% - msg [char] error message +% +% OUTPUTS +% None. +% +% REMARKS +% - First argument is optional. + +% Copyright © 2024 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 <https://www.gnu.org/licenses/>. + + if isoctave() || isdebug() + error(varargin{:}) + else + throwAsCaller(MException(varargin{:})) + end + +end diff --git a/matlab/dynare.m b/matlab/dynare.m index ce973cd45e..cffe619875 100644 --- a/matlab/dynare.m +++ b/matlab/dynare.m @@ -185,6 +185,7 @@ nolog = ismember('nolog', varargin) || ismember('nolog', file_opts); onlymacro = ismember('onlymacro', varargin) || ismember('onlymacro', file_opts); onlyjson = ismember('onlyjson', varargin) || ismember('onlyjson', file_opts); fast = ismember('fast', varargin) || ismember('fast', file_opts); +verbose = ismember('verbose', varargin) || ismember('verbose', file_opts); % TODO: Find another name for the option. % Start journal diary off @@ -196,6 +197,17 @@ if ~nolog diary(logfile) end +if verbose + setenv('DYNARE_DEBUG_MODE', 'true') +else + warning('off', 'backtrace'); + if isoctave() || ~matlab_ver_less_than('9.13') + unsetenv('DYNARE_DEBUG_MODE'); + else + setenv('DYNARE_DEBUG_MODE', 'false'); + end +end + if preprocessoroutput fprintf(['Starting Dynare (version ' dynare_version() ').\n']); fprintf('Calling Dynare with arguments: '); diff --git a/matlab/estimation/PlotPosteriorDistributions.m b/matlab/estimation/PlotPosteriorDistributions.m index 633ab1cf56..e60925155a 100644 --- a/matlab/estimation/PlotPosteriorDistributions.m +++ b/matlab/estimation/PlotPosteriorDistributions.m @@ -1,21 +1,18 @@ -function oo_ = PlotPosteriorDistributions(estim_params_, M_, options_, bayestopt_, oo_) -% oo_ = PlotPosteriorDistributions(estim_params_, M_, options_, bayestopt_, oo_) -% plots posterior distributions +function oo_ = PlotPosteriorDistributions(estim_params_, M_, options_, Prior, oo_) + +% Plot posterior densities % % INPUTS -% estim_params_ [structure] information on estimated paramters -% M_ [structure] information on model -% options_ [structure] information on options -% bayestopt_ [structure] information on priors -% oo_ [structure] results +% - estim_params_ [struct] description of the estimated paramters. +% - M_ [struct] description of the model. +% - options_ [struct] Dynare's options. +% - Prior [dprior] object containing information about the prior distribution. +% - oo_ [struct] results. % % OUTPUTS -% oo_ [structure] updated results -% -% SPECIAL REQUIREMENTS -% none +% - oo_ [struct] updated results. -% Copyright © 2005-2023 Dynare Team +% Copyright © 2005-2024 Dynare Team % % This file is part of Dynare. % @@ -65,7 +62,7 @@ for i=1:npar hh_fig=dyn_figure(options_.nodisplay, 'Name', figurename); end [nam,texnam] = get_the_name(i, TeX, M_, estim_params_, options_.varobs); - [x2, f2, ~, ~, binf2, bsup2] = draw_prior_density(i, bayestopt_); + [x2, f2, ~, ~, binf2, bsup2] = Prior.data_for_plotting_density(i); top2 = max(f2); if i <= nvx name = M_.exo_names{estim_params_.var_exo(i,1)}; diff --git a/matlab/estimation/check_bounds_and_definiteness_estimation.m b/matlab/estimation/check_bounds_and_definiteness_estimation.m index a25478026c..fd3195d9a2 100644 --- a/matlab/estimation/check_bounds_and_definiteness_estimation.m +++ b/matlab/estimation/check_bounds_and_definiteness_estimation.m @@ -33,16 +33,17 @@ function [fval,info,exit_flag,Q,H]=check_bounds_and_definiteness_estimation(xpar % You should have received a copy of the GNU General Public License % along with Dynare. If not, see <https://www.gnu.org/licenses/>. -fval = []; -exit_flag = 1; -info = zeros(4,1); -Q=[]; -H=[]; +fval = []; +exit_flag = true; +info = zeros(4,1); +Q = []; +H = []; + % Return, with endogenous penalty, if some parameters are smaller than the lower bound of the prior domain. if any(xparam1<bounds.lb) k = find(xparam1(:) < bounds.lb); fval = Inf; - exit_flag = 0; + exit_flag = false; info(1) = 41; info(4) = sum((bounds.lb(k)-xparam1(k)).^2); return @@ -52,7 +53,7 @@ end if any(xparam1>bounds.ub) k = find(xparam1(:)>bounds.ub); fval = Inf; - exit_flag = 0; + exit_flag = false; info(1) = 42; info(4) = sum((xparam1(k)-bounds.ub(k)).^2); return @@ -62,10 +63,10 @@ Q = M_.Sigma_e; H = M_.H; if ~issquare(Q) || estim_params_.ncx || isfield(estim_params_,'calibrated_covariances') - [Q_is_positive_definite, penalty] = ispd(Q(estim_params_.Sigma_e_entries_to_check_for_positive_definiteness,estim_params_.Sigma_e_entries_to_check_for_positive_definiteness)); + [Q_is_positive_definite, penalty] = ispd(Q(estim_params_.Sigma_e_entries_to_check_for_positive_definiteness, estim_params_.Sigma_e_entries_to_check_for_positive_definiteness)); if ~Q_is_positive_definite fval = Inf; - exit_flag = 0; + exit_flag = false; info(1) = 43; info(4) = penalty; return @@ -75,7 +76,7 @@ if ~issquare(Q) || estim_params_.ncx || isfield(estim_params_,'calibrated_covari if ~correct_flag penalty = sum(Q(estim_params_.calibrated_covariances.position).^2); fval = Inf; - exit_flag = 0; + exit_flag = false; info(1) = 71; info(4) = penalty; return @@ -85,10 +86,10 @@ if ~issquare(Q) || estim_params_.ncx || isfield(estim_params_,'calibrated_covari end if ~issquare(H) || estim_params_.ncn || isfield(estim_params_,'calibrated_covariances_ME') - [H_is_positive_definite, penalty] = ispd(H(estim_params_.H_entries_to_check_for_positive_definiteness,estim_params_.H_entries_to_check_for_positive_definiteness)); + [H_is_positive_definite, penalty] = ispd(H(estim_params_.H_entries_to_check_for_positive_definiteness, estim_params_.H_entries_to_check_for_positive_definiteness)); if ~H_is_positive_definite fval = Inf; - exit_flag = 0; + exit_flag = false; info(1) = 44; info(4) = penalty; return @@ -98,7 +99,7 @@ if ~issquare(H) || estim_params_.ncn || isfield(estim_params_,'calibrated_covari if ~correct_flag penalty = sum(H(estim_params_.calibrated_covariances_ME.position).^2); fval = Inf; - exit_flag = 0; + exit_flag = false; info(1) = 72; info(4) = penalty; return diff --git a/matlab/estimation/check_prior_bounds.m b/matlab/estimation/check_prior_bounds.m index bf77952d63..d677f161a5 100644 --- a/matlab/estimation/check_prior_bounds.m +++ b/matlab/estimation/check_prior_bounds.m @@ -37,7 +37,10 @@ if ~isempty(outside_bound_pars) end error(['Initial value(s) of ', disp_string ,' are outside parameter bounds. Potentially, you should set prior_trunc=0. If you used the mode_file-option, check whether your mode-file is consistent with the priors.']) end -inadmissible_inverse_gamma_values=find(bayestopt_.pshape==4 & xparam1 == 0); + +% TODO: Check the test below. If the inverse gamma is shifted, it probably doesn't make much sense... Instead, we should test whether the initial guess is equal to the natural lower bound of the inverse gamma (in the case where the prior mode is at the lower bound). +% TODO: Should we not check the same for densities with an asymptote at the lower or upper natural boundary (eg for the beta distribution)? +inadmissible_inverse_gamma_values=find(bayestopt_.pshape & xparam1 == 0); if ~isempty(inadmissible_inverse_gamma_values) for ii=1:length(inadmissible_inverse_gamma_values) inadmissible_inverse_gamma_par_names{ii,1}=get_the_name(inadmissible_inverse_gamma_values(ii),0,M_,estim_params_,options_.varobs); @@ -47,4 +50,4 @@ if ~isempty(inadmissible_inverse_gamma_values) disp_string=[disp_string,', ',inadmissible_inverse_gamma_par_names{ii,:}]; end error(['Initial value(s) of ', disp_string ,' is zero. This is not allowed when using an inverse gamma prior.\n']) -end \ No newline at end of file +end diff --git a/matlab/estimation/dsge_likelihood.m b/matlab/estimation/dsge_likelihood.m index 265373e1a9..fb65dfbfd2 100644 --- a/matlab/estimation/dsge_likelihood.m +++ b/matlab/estimation/dsge_likelihood.m @@ -1,24 +1,23 @@ -function [fval,info,exit_flag,DLIK,Hess,SteadyState,trend_coeff,M_,options_,bayestopt_,dr] = dsge_likelihood(xparam1,dataset_,dataset_info,options_,M_,estim_params_,bayestopt_,BoundsInfo,dr, endo_steady_state, exo_steady_state, exo_det_steady_state,derivatives_info) -% [fval,info,exit_flag,DLIK,Hess,SteadyState,trend_coeff,M_,options_,bayestopt_,oo_] = dsge_likelihood(xparam1,dataset_,dataset_info,options_,M_,estim_params_,bayestopt_,BoundsInfo,oo_,derivatives_info) -% Evaluates the negative of the posterior kernel of a DSGE model using the specified -% kalman_algo; the resulting posterior includes the 2*pi constant of the +function [fval,info,exit_flag,DLIK,Hess,SteadyState,trend_coeff,M_,options_,bayestopt_,dr] = dsge_likelihood(xparam1,dataset_,dataset_info,options_,M_,Prior,estim_params_,bayestopt_,BoundsInfo,dr, endo_steady_state, exo_steady_state, exo_det_steady_state,derivatives_info) + +% Evaluates the negative of the posterior kernel of a DSGE model using the specified kalman_algo; the resulting posterior includes the 2*pi constant of the % likelihood function % % INPUTS -% - xparam1 [double] current values for the estimated parameters. -% - dataset_ [structure] dataset after transformations -% - dataset_info [structure] storing informations about the -% sample; not used but required for interface -% - options_ [structure] Matlab's structure describing the current options -% - M_ [structure] Matlab's structure describing the model -% - estim_params_ [structure] characterizing parameters to be estimated -% - bayestopt_ [structure] describing the priors -% - BoundsInfo [structure] containing prior bounds -% - dr [structure] Reduced form model. -% - endo_steady_state [vector] steady state value for endogenous variables -% - exo_steady_state [vector] steady state value for exogenous variables -% - exo_det_steady_state [vector] steady state value for exogenous deterministic variables -% - derivatives_info [structure] derivative info for identification +% - xparam1 [double] current values for the estimated parameters. +% - dataset_ [struct] dataset after transformations +% - dataset_info [struct] storing informations about the sample; not used but required for interface +% - options_ [struct] Matlab's structure describing the current options +% - M_ [struct] Matlab's structure describing the model +% - Prior [dprior] Definition of the prior density +% - estim_params_ [struct] characterizing parameters to be estimated +% - bayestopt_ [struct] describing the priors +% - BoundsInfo [struct] containing prior bounds +% - dr [struct] Reduced form model. +% - endo_steady_state [double] steady state value for endogenous variables +% - exo_steady_state [double] steady state value for exogenous variables +% - exo_det_steady_state [double] steady state value for exogenous deterministic variables +% - derivatives_info [struct] derivative info for identification % % OUTPUTS % - fval [double] scalar, value of minus the likelihood or posterior kernel. @@ -35,7 +34,7 @@ function [fval,info,exit_flag,DLIK,Hess,SteadyState,trend_coeff,M_,options_,baye % % This function is called by: dynare_estimation_1, mode_check % This function calls: dynare_resolve, lyapunov_symm, lyapunov_solver, compute_Pinf_Pstar, kalman_filter_d, missing_observations_kalman_filter_d, -% univariate_kalman_filter_d, kalman_steady_state, get_perturbation_params_deriv, kalman_filter, missing_observations_kalman_filter, univariate_kalman_filter, priordens +% univariate_kalman_filter_d, kalman_steady_state, get_perturbation_params_deriv, kalman_filter, missing_observations_kalman_filter, univariate_kalman_filter % Copyright © 2004-2024 Dynare Team % @@ -57,17 +56,17 @@ function [fval,info,exit_flag,DLIK,Hess,SteadyState,trend_coeff,M_,options_,baye % Initial author: stephane DOT adjemian AT univ DASH lemans DOT FR % Initialization of the returned variables and others... -fval = []; +fval = []; SteadyState = []; trend_coeff = []; -exit_flag = 1; -info = zeros(4,1); +exit_flag = false; +info = zeros(4,1); if options_.analytic_derivation - DLIK = NaN(1,length(xparam1)); + DLIK = NaN(1,length(xparam1)); else - DLIK = []; + DLIK = []; end -Hess = []; +Hess = []; % Ensure that xparam1 is a column vector. % (Don't do the transformation if xparam1 is empty, otherwise it would become a @@ -90,19 +89,19 @@ if analytic_derivation end if nargout==1 - analytic_derivation=0; + analytic_derivation = 0; end if analytic_derivation - kron_flag=options_.analytic_derivation_mode; + kron_flag = options_.analytic_derivation_mode; end %------------------------------------------------------------------------------ % 1. Get the structural parameters & define penalties %------------------------------------------------------------------------------ -M_ = set_all_parameters(xparam1,estim_params_,M_); +M_ = set_all_parameters(xparam1, estim_params_, M_); -[fval,info,exit_flag,Q,H]=check_bounds_and_definiteness_estimation(xparam1, M_, estim_params_, BoundsInfo); +[fval, info, exit_flag, Q, H] = check_bounds_and_definiteness_estimation(xparam1, M_, estim_params_, BoundsInfo); if info(1) return end @@ -135,7 +134,7 @@ if options_.occbin.likelihood.status occbin_.info= {options_, dr,endo_steady_state,exo_steady_state,exo_det_steady_state, M_, occbin_options, TTx, RRx, CCx,T0,R0}; else % Linearize the model around the deterministic steady state and extract the matrices of the state equation (T and R). - [T,R,SteadyState,info,dr, M_.params] = dynare_resolve(M_,options_,dr, endo_steady_state, exo_steady_state, exo_det_steady_state,'restrict'); + [T, R, SteadyState, info, dr, M_.params] = dynare_resolve(M_, options_, dr, endo_steady_state, exo_steady_state, exo_det_steady_state, 'restrict'); occbin_.status = false; end @@ -153,30 +152,30 @@ if info(1) else info(4) = info(2); end - exit_flag = 0; + exit_flag = false; if analytic_derivation - DLIK=ones(length(xparam1),1); + DLIK = ones(length(xparam1), 1); end return else fval = Inf; info(4) = 0.1; - exit_flag = 0; + exit_flag = false; if analytic_derivation - DLIK=ones(length(xparam1),1); + DLIK = ones(length(xparam1), 1); end return end end % check endogenous prior restrictions -info=endogenous_prior_restrictions(T,R,M_,options_,dr,endo_steady_state,exo_steady_state,exo_det_steady_state); +info = endogenous_prior_restrictions(T, R, M_, options_, dr, endo_steady_state, exo_steady_state, exo_det_steady_state); if info(1) fval = Inf; - info(4)=info(2); - exit_flag = 0; + info(4) = info(2); + exit_flag = false; if analytic_derivation - DLIK=ones(length(xparam1),1); + DLIK = ones(length(xparam1), 1); end return end @@ -804,10 +803,10 @@ likelihood = LIK; % ------------------------------------------------------------------------------ if analytic_derivation if full_Hess - [lnprior, dlnprior, d2lnprior] = priordens(xparam1,bayestopt_.pshape,bayestopt_.p6,bayestopt_.p7,bayestopt_.p3,bayestopt_.p4); + [lnprior, dlnprior, d2lnprior] = Prior.density(xparam1); Hess = Hess - d2lnprior; else - [lnprior, dlnprior] = priordens(xparam1,bayestopt_.pshape,bayestopt_.p6,bayestopt_.p7,bayestopt_.p3,bayestopt_.p4); + [lnprior, dlnprior] = Prior.density(xparam1); end if no_DLIK==0 DLIK = DLIK - dlnprior'; @@ -818,7 +817,7 @@ if analytic_derivation Hess = dlik'*dlik; end else - lnprior = priordens(xparam1,bayestopt_.pshape,bayestopt_.p6,bayestopt_.p7,bayestopt_.p3,bayestopt_.p4); + lnprior = Prior.density(xparam1); end if options_.endogenous_prior==1 diff --git a/matlab/estimation/dsge_var_likelihood.m b/matlab/estimation/dsge_var_likelihood.m index 39eace525f..aa74e6cd48 100644 --- a/matlab/estimation/dsge_var_likelihood.m +++ b/matlab/estimation/dsge_var_likelihood.m @@ -1,45 +1,40 @@ function [fval,info,exit_flag,grad,hess,SteadyState,trend_coeff,PHI_tilde,SIGMA_u_tilde,iXX,prior] = dsge_var_likelihood(xparam1,dataset_,dataset_info,options_,M_,estim_params_,bayestopt_,BoundsInfo,dr, endo_steady_state, exo_steady_state, exo_det_steady_state) -% [fval,info,exit_flag,grad,hess,SteadyState,trend_coeff,PHI_tilde,SIGMA_u_tilde,iXX,prior] = dsge_var_likelihood(xparam1,dataset_,dataset_info,options_,M_,estim_params_,bayestopt_,BoundsInfo,dr, endo_steady_state, exo_steady_state, exo_det_steady_state) + % Evaluates the posterior kernel of the BVAR-DSGE model. % % INPUTS -% o xparam1 [double] Vector of model's parameters. -% o gend [integer] Number of observations (without conditionning observations for the lags). -% o dataset_ [dseries] object storing the dataset -% o dataset_info [structure] storing informations about the sample. -% o options_ [structure] describing the options -% o M_ [structure] decribing the model -% o estim_params_ [structure] characterizing parameters to be estimated -% o bayestopt_ [structure] describing the priors -% o BoundsInfo [structure] containing prior bounds -% o dr [structure] Reduced form model. -% o endo_steady_state [vector] steady state value for endogenous variables -% o exo_steady_state [vector] steady state value for exogenous variables -% o exo_det_steady_state [vector] steady state value for exogenous deterministic variables +% - xparam1 [double] Vector of model's parameters. +% - gend [integer] Number of observations (without conditionning observations for the lags). +% - dataset_ [dseries] object storing the dataset +% - dataset_info [struct] storing informations about the sample. +% - options_ [struct] describing the options +% - M_ [struct] decribing the model +% - Prior [dprior] definition of the prior density +% - estim_params_ [struct] characterizing parameters to be estimated +% - bayestopt_ [struct] describing the priors +% - BoundsInfo [struct] containing prior bounds +% - dr [struct] Reduced form model. +% - endo_steady_state [double] steady state value for endogenous variables +% - exo_steady_state [double] steady state value for exogenous variables +% - exo_det_steady_state [double] steady state value for exogenous deterministic variables % % OUTPUTS -% o fval [double] Value of the posterior kernel at xparam1. -% o info [integer] Vector of informations about the penalty. -% o exit_flag [integer] Zero if the function returns a penalty, one otherwise. -% o grad [double] place holder for gradient of the likelihood -% currently not supported by dsge_var -% o hess [double] place holder for hessian matrix of the likelihood -% currently not supported by dsge_var -% o SteadyState [double] Steady state vector possibly recomputed -% by call to dynare_resolve() -% o trend_coeff [double] place holder for trend coefficients, -% currently not supported by dsge_var -% o PHI_tilde [double] Stacked BVAR-DSGE autoregressive matrices (at the mode associated to xparam1); -% formula (28), DS (2004) -% o SIGMA_u_tilde [double] Covariance matrix of the BVAR-DSGE (at the mode associated to xparam1), -% formula (29), DS (2004) -% o iXX [double] inv(lambda*T*Gamma_XX^*+ X'*X) -% o prior [double] a matlab structure describing the dsge-var prior -% - SIGMA_u_star: prior covariance matrix, formula (23), DS (2004) -% - PHI_star: prior autoregressive matrices, formula (22), DS (2004) -% - ArtificialSampleSize: number of artificial observations from the prior (T^* in DS (2004)) -% - DF = prior.ArtificialSampleSize - NumberOfParameters - NumberOfObservedVariables; -% - iGXX_star: theoretical covariance of regressors ({\Gamma_{XX}^*}^{-1} in DS (2004)) +% - fval [double] Value of the posterior kernel at xparam1. +% - info [integer] Vector of informations about the penalty. +% - exit_flag [integer] Zero if the function returns a penalty, one otherwise. +% - grad [double] place holder for gradient of the likelihood, currently not supported by dsge_var +% - hess [double] place holder for hessian matrix of the likelihood, currently not supported by dsge_var +% - SteadyState [double] Steady state vector possibly recomputed by call to dynare_resolve() +% - trend_coeff [double] place holder for trend coefficients, currently not supported by dsge_var +% - PHI_tilde [double] Stacked BVAR-DSGE autoregressive matrices (at the mode associated to xparam1); formula (28), DS (2004) +% - SIGMA_u_tilde [double] Covariance matrix of the BVAR-DSGE (at the mode associated to xparam1); formula (29), DS (2004) +% - iXX [double] inv(lambda*T*Gamma_XX^*+ X'*X) +% - prior [double] a matlab structure describing the dsge-var prior +% - SIGMA_u_star: prior covariance matrix, formula (23), DS (2004) +% - PHI_star: prior autoregressive matrices, formula (22), DS (2004) +% - ArtificialSampleSize: number of artificial observations from the prior (T^* in DS (2004)) +% - DF = prior.ArtificialSampleSize - NumberOfParameters - NumberOfObservedVariables; +% - iGXX_star: theoretical covariance of regressors ({\Gamma_{XX}^*}^{-1} in DS (2004)) % % ALGORITHMS % Follows the computations outlined in Del Negro/Schorfheide (2004): @@ -49,7 +44,7 @@ function [fval,info,exit_flag,grad,hess,SteadyState,trend_coeff,PHI_tilde,SIGMA_ % SPECIAL REQUIREMENTS % None. -% Copyright © 2006-2023Dynare Team +% Copyright © 2006-2024 Dynare Team % % This file is part of Dynare. % @@ -284,8 +279,7 @@ if imag(lik)~=0 end % Add the (logged) prior density for the dsge-parameters. -lnprior = priordens(xparam1,bayestopt_.pshape,bayestopt_.p6,bayestopt_.p7,bayestopt_.p3,bayestopt_.p4); -fval = (lik-lnprior); +fval = lik-Prior.density(xparam1); if isnan(fval) fval = Inf; diff --git a/matlab/estimation/dynare_estimation_1.m b/matlab/estimation/dynare_estimation_1.m index 0962b4c884..650e1eef89 100644 --- a/matlab/estimation/dynare_estimation_1.m +++ b/matlab/estimation/dynare_estimation_1.m @@ -120,7 +120,7 @@ else objective_function = str2func('dsge_var_likelihood'); end -[dataset_, dataset_info, xparam1, hh, M_, options_, oo_, estim_params_, bayestopt_, bounds] = ... +[dataset_, dataset_info, xparam1, hh, M_, options_, oo_, Prior, estim_params_, bayestopt_, bounds] = ... dynare_estimation_init(var_list_, dname, [], M_, options_, oo_, estim_params_, bayestopt_); if options_.dsge_var @@ -166,7 +166,7 @@ end % try - oo_ = initial_estimation_checks(objective_function,xparam1,dataset_,dataset_info,M_,estim_params_,options_,bayestopt_,bounds,oo_); + initial_estimation_checks(objective_function, xparam1, dataset_, dataset_info, M_, Prior, estim_params_, options_, bayestopt_, bounds, oo_); catch e % if check fails, provide info on using calibration if present if estim_params_.full_calibration_detected %calibrated model present and no explicit starting values skipline(1); @@ -237,7 +237,7 @@ if ~isequal(options_.mode_compute,0) && ~options_.mh_posterior_mode_estimation & optimizer_vec = [options_.mode_compute;num2cell(options_.additional_optimizer_steps)]; for optim_iter = 1:length(optimizer_vec) current_optimizer = optimizer_vec{optim_iter}; - [xparam1, fval, ~, hh, options_, Scale, new_rat_hess_info] = dynare_minimize_objective(objective_function,xparam1,current_optimizer,options_,[bounds.lb bounds.ub],bayestopt_.name,bayestopt_,hh,dataset_,dataset_info,options_,M_,estim_params_,bayestopt_,bounds,oo_.dr, oo_.steady_state,oo_.exo_steady_state,oo_.exo_det_steady_state); + [xparam1, fval, ~, hh, options_, Scale, new_rat_hess_info] = dynare_minimize_objective(objective_function,xparam1,current_optimizer,options_,[bounds.lb bounds.ub],bayestopt_.name,bayestopt_,hh,dataset_,dataset_info,options_,M_,Prior,estim_params_,bayestopt_,bounds,oo_.dr, oo_.steady_state,oo_.exo_steady_state,oo_.exo_det_steady_state); fprintf('\nFinal value of minus the log posterior (or likelihood):%f \n', fval); if isnumeric(current_optimizer) @@ -266,8 +266,8 @@ if ~isequal(options_.mode_compute,0) && ~options_.mh_posterior_mode_estimation & penalized_objective_function = str2func('penalty_objective_function'); hh = hessian(penalized_objective_function, xparam1, options_.gstep, objective_function, fval, dataset_, dataset_info, options_, M_, estim_params_, bayestopt_, bounds,oo_.dr,oo_.steady_state,oo_.exo_steady_state,oo_.exo_det_steady_state); else - hh = hessian(objective_function, xparam1, options_.gstep, dataset_, dataset_info, options_, M_, estim_params_, bayestopt_, bounds,oo_.dr,oo_.steady_state,oo_.exo_steady_state,oo_.exo_det_steady_state); - end + hh = hessian(objective_function, xparam1, options_.gstep, dataset_, dataset_info, options_, M_, Prior, estim_params_, bayestopt_, bounds,oo_.dr,oo_.steady_state,oo_.exo_steady_state,oo_.exo_det_steady_state); + end hh = reshape(hh, nx, nx); elseif isnumeric(current_optimizer) && isequal(current_optimizer,5) % other numerical hessian options available with optimizer @@ -322,7 +322,7 @@ if options_.mode_check.status && ~options_.mh_posterior_mode_estimation && ~issm ana_deriv_old = options_.analytic_derivation; options_.analytic_derivation = 0; mode_check(objective_function,xparam1,hh,options_,M_,estim_params_,bayestopt_,bounds,false,... - dataset_, dataset_info, options_, M_, estim_params_, bayestopt_, bounds,oo_.dr,oo_.steady_state,oo_.exo_steady_state,oo_.exo_det_steady_state); + dataset_, dataset_info, options_, M_, Prior, estim_params_, bayestopt_, bounds,oo_.dr,oo_.steady_state,oo_.exo_steady_state,oo_.exo_det_steady_state); options_.analytic_derivation = ana_deriv_old; end @@ -369,7 +369,7 @@ if ~issmc(options_) && any(bayestopt_.pshape > 0) && ~options_.mh_posterior_mode estim_params_nbr = size(xparam1,1); if ispd(invhess) log_det_invhess = log(det(invhess./(stdh*stdh')))+2*sum(log(stdh)); - likelihood = feval(objective_function,xparam1,dataset_,dataset_info,options_,M_,estim_params_,bayestopt_,bounds,oo_.dr,oo_.steady_state,oo_.exo_steady_state,oo_.exo_det_steady_state); + likelihood = feval(objective_function,xparam1,dataset_,dataset_info,options_,M_,Prior,estim_params_,bayestopt_,bounds,oo_.dr,oo_.steady_state,oo_.exo_steady_state,oo_.exo_det_steady_state); oo_.MarginalDensity.LaplaceApproximation = .5*estim_params_nbr*log(2*pi) + .5*log_det_invhess - likelihood; else oo_.MarginalDensity.LaplaceApproximation = NaN; @@ -439,7 +439,7 @@ if issmc(options_) || (any(bayestopt_.pshape>0) && options_.mh_replic) || (any( if options_.mh_replic ana_deriv_old = options_.analytic_derivation; options_.analytic_derivation = 0; - posterior_sampler(objective_function,posterior_sampler_options.proposal_distribution,xparam1,posterior_sampler_options,bounds,dataset_,dataset_info,options_,M_,estim_params_,bayestopt_,oo_,dispString); + posterior_sampler(objective_function,posterior_sampler_options.proposal_distribution,xparam1,posterior_sampler_options,bounds,dataset_,dataset_info,options_,M_, Prior, estim_params_,bayestopt_,oo_,dispString); options_.analytic_derivation = ana_deriv_old; end end @@ -478,7 +478,7 @@ if issmc(options_) || (any(bayestopt_.pshape>0) && options_.mh_replic) || (any( % Store posterior statistics by parameter name oo_ = GetPosteriorParametersStatistics(estim_params_, M_, options_, bayestopt_, oo_, prior_dist_names); if ~options_.nograph - oo_ = PlotPosteriorDistributions(estim_params_, M_, options_, bayestopt_, oo_); + oo_ = PlotPosteriorDistributions(estim_params_, M_, options_, Prior, oo_); end % Store posterior mean in a vector and posterior variance in % a matrix diff --git a/matlab/estimation/dynare_estimation_init.m b/matlab/estimation/dynare_estimation_init.m index 0f15716239..6229e95215 100644 --- a/matlab/estimation/dynare_estimation_init.m +++ b/matlab/estimation/dynare_estimation_init.m @@ -1,4 +1,4 @@ -function [dataset_, dataset_info, xparam1, hh, M_, options_, oo_, estim_params_,bayestopt_, bounds] = dynare_estimation_init(var_list_, dname, gsa_flag, M_, options_, oo_, estim_params_, bayestopt_) +function [dataset_, dataset_info, xparam1, hh, M_, options_, oo_, Prior, estim_params_,bayestopt_, bounds] = dynare_estimation_init(var_list_, dname, gsa_flag, M_, options_, oo_, estim_params_, bayestopt_) % Performs initialization tasks before estimation or global sensitivity analysis % @@ -91,31 +91,30 @@ end % Check the perturbation order for pruning (k order perturbation based nonlinear filters are not yet implemented for k>3). if options_.order>3 && options_.particle.pruning - error('Higher order nonlinear filters are not compatible with pruning option.') + derror('Estimation:nonlinear', 'Higher order nonlinear filters are not compatible with pruning option.') end % analytical derivation is not yet available for kalman_filter_fast if options_.analytic_derivation && options_.fast_kalman_filter - error(['estimation option conflict: analytic_derivation isn''t available ' ... - 'for fast_kalman_filter']) + derror('Estimation:kalman', 'estimation option conflict: analytic_derivation isn''t available for fast_kalman_filter.') end % fast kalman filter is only available with kalman_algo == 0,1,3 if options_.fast_kalman_filter && ~ismember(options_.kalman_algo, [0,1,3]) - error(['estimation option conflict: fast_kalman_filter is only available ' ... - 'with kalman_algo = 0, 1 or 3']) + derror('Estimation:kalman', 'estimation option conflict: fast_kalman_filter is only available with kalman_algo = 0, 1 or 3.') end % Set options_.lik_init equal to 3 if diffuse filter is used or kalman_algo refers to a diffuse filter algorithm. if isequal(options_.diffuse_filter,1) || (options_.kalman_algo>2) if isequal(options_.lik_init,2) - error('options diffuse_filter, lik_init and/or kalman_algo have contradictory settings') + derror('Estimation:kalman', 'options diffuse_filter, lik_init and/or kalman_algo have contradictory settings.') else options_.lik_init = 3; end end -options_=select_qz_criterium_value(options_); +% Set the value of qz_criterium +options_ = select_qz_criterium_value(options_); % Set options related to filtered variables. if isequal(options_.filtered_vars,0) && ~isempty(options_.filter_step_ahead) %require filtered var because filter_step_ahead was set @@ -135,23 +134,40 @@ else M_.dname = dname; end -% Set priors over the estimated parameters. -if ~isempty(estim_params_) && ~(isfield(estim_params_,'nvx') && (size(estim_params_.var_exo,1)+size(estim_params_.var_endo,1)+size(estim_params_.corrx,1)+size(estim_params_.corrn,1)+size(estim_params_.param_vals,1))==0) - [xparam1,estim_params_,bayestopt_,lb,ub,M_] = set_prior(estim_params_,M_,options_); -end +estim_params_ = set_number_of_estimated_parameters(estim_params_); + +estim_params_ = set_nvn_observable_correspondence(estim_params_, M_.endo_names, options_.varobs); + +estim_params_ = set_corrn_observable_correspondence(estim_params_, M_.endo_names, options_.varobs); + +M_ = initialize_measurement_errors_covariance_matrix(estim_params_, M_, options_); -if ~isempty(bayestopt_) && any(bayestopt_.pshape==0) && any(bayestopt_.pshape~=0) - error('Estimation must be either fully ML or fully Bayesian. Maybe you forgot to specify a prior distribution.') +bayesianestimation = isbayesian(estim_params_); + +if bayesianestimation + Prior = set_prior(estim_params_, M_, options_); + estim_params_ = set_jscale(estim_params_, true); + estim_params_ = set_parameter_names(estim_params_, M_.param_names, M_.exo_names, M_.endo_names, options_.varobs); +else + estim_params_ = set_parameter_names(estim_params_, M_.param_names, M_.exo_names, M_.endo_names, options_.varobs); + Prior = dprior(); end + +xparam1 = initialguess(estim_params_, options_.initialize_estimated_parameters_with_the_prior_mode, Prior); + +[lb, ub] = get_user_defined_boundaries(estim_params_, M_, options_); +bounds.lb = lb; +bounds.ub = ub; + % Check if a _prior_restrictions.m file exists if isfile(sprintf('%s_prior_restrictions.m', M_.fname)) - options_.prior_restrictions.status = 1; + options_.prior_restrictions.status = true; options_.prior_restrictions.routine = str2func([M_.fname '_prior_restrictions']); end % Check that the provided mode_file is compatible with the current estimation settings. if ~isempty(estim_params_) && ~(isfield(estim_params_,'nvx') && sum(estim_params_.nvx+estim_params_.nvn+estim_params_.ncx+estim_params_.ncn+estim_params_.np)==0) && ~isempty(options_.mode_file) && ~options_.mh_posterior_mode_estimation - [xparam1, hh] = check_mode_file(xparam1, hh, options_, bayestopt_); + [xparam1, hh] = check_mode_file(xparam1, hh, options_.mode_compute>0, options_.mode_file, estim_params_.names); end %check for calibrated covariances before updating parameters @@ -174,26 +190,21 @@ if options_.use_calibration_initialization %set calibration as starting values end end -if ~isempty(bayestopt_) && all(bayestopt_.pshape==0) && any(isnan(xparam1)) +if estim_params_.estimation && ~bayesianestimation && any(isnan(xparam1)) error('ML estimation requires all estimated parameters to be initialized, either in an estimated_params or estimated_params_init-block ') end if ~isempty(estim_params_) && ~(all(strcmp(fieldnames(estim_params_),'full_calibration_detected')) || (isfield(estim_params_,'nvx') && sum(estim_params_.nvx+estim_params_.nvn+estim_params_.ncx+estim_params_.ncn+estim_params_.np)==0)) - if ~isempty(bayestopt_) && any(bayestopt_.pshape > 0) + if bayesianestimation % Plot prior densities. if ~options_.nograph && options_.plot_priors - plot_priors(bayestopt_,M_,estim_params_,options_) + plot_priors(Prior, M_, estim_params_, options_) end - % Set prior bounds - bounds = prior_bounds(bayestopt_, options_.prior_trunc); - bounds.lb = max(bounds.lb,lb); - bounds.ub = min(bounds.ub,ub); - else % estimated parameters but no declared priors - % No priors are declared so Dynare will estimate the model by - % maximum likelihood with inequality constraints for the parameters. + else + % estimated parameters but no declared priors + % No priors are declared so Dynare will estimate the model by + % maximum likelihood options_.mh_replic = 0;% No metropolis. - bounds.lb = lb; - bounds.ub = ub; end % Test if initial values of the estimated parameters are all between the prior lower and upper bounds. if options_.use_calibration_initialization @@ -473,7 +484,7 @@ end estim_params_= get_matrix_entries_for_psd_check(M_,estim_params_); if options_.load_results_after_load_mh - if ~isfile(sprintf('%s%sOutput%s%s_results.mat', M_.dname, filesep, filesep, M°.fname)) + if ~isfile(sprintf('%s%sOutput%s%s_results.mat', M_.dname, filesep, filesep, M_.fname)) fprintf('\ndynare_estimation_init:: You specified the load_results_after_load_mh, but no _results.mat-file\n') fprintf('dynare_estimation_init:: was found. Results will be recomputed.\n') options_.load_results_after_load_mh=0; @@ -488,73 +499,73 @@ end %% set heteroskedastic shocks if options_.heteroskedastic_filter if options_.fast_kalman_filter - error('estimation option conflict: "heteroskedastic_filter" incompatible with "fast_kalman_filter"') - end - if options_.analytic_derivation - error(['estimation option conflict: analytic_derivation isn''t available ' ... - 'for heteroskedastic_filter']) - end - - M_.heteroskedastic_shocks.Qvalue = NaN(M_.exo_nbr,options_.nobs+1); - M_.heteroskedastic_shocks.Qscale = NaN(M_.exo_nbr,options_.nobs+1); - - for k=1:length(M_.heteroskedastic_shocks.Qvalue_orig) - v = M_.heteroskedastic_shocks.Qvalue_orig(k); - temp_periods=v.periods(v.periods<options_.nobs+options_.first_obs+1); - temp_periods=temp_periods(temp_periods>=options_.first_obs); - M_.heteroskedastic_shocks.Qvalue(v.exo_id, temp_periods-(options_.first_obs-1)) = v.value^2; - end - for k=1:length(M_.heteroskedastic_shocks.Qscale_orig) - v = M_.heteroskedastic_shocks.Qscale_orig(k); - temp_periods=v.periods(v.periods<options_.nobs+options_.first_obs+1); - temp_periods=temp_periods(temp_periods>=options_.first_obs); - M_.heteroskedastic_shocks.Qscale(v.exo_id, temp_periods-(options_.first_obs-1)) = v.scale^2; - end - - if any(any(~isnan(M_.heteroskedastic_shocks.Qvalue) & ~isnan(M_.heteroskedastic_shocks.Qscale))) - fprintf('\ndynare_estimation_init: With the option "heteroskedastic_shocks" you cannot define\n') - fprintf('dynare_estimation_init: the scale and the value for the same shock \n') - fprintf('dynare_estimation_init: in the same period!\n') - error('Scale and value defined for the same shock in the same period with "heteroskedastic_shocks".') - end -end - -if (options_.occbin.likelihood.status && options_.occbin.likelihood.inversion_filter) || (options_.occbin.smoother.status && options_.occbin.smoother.inversion_filter) - if isempty(options_.occbin.likelihood.IVF_shock_observable_mapping) - options_.occbin.likelihood.IVF_shock_observable_mapping=find(diag(M_local.Sigma_e)~=0); - else - zero_var_shocks=find(diag(M_local.Sigma_e)==0); - if any(ismember(options_.occbin.likelihood.IVF_shock_observable_mapping,zero_var_shocks)) - error('IVF-filter: an observable is mapped to a zero variance shock.') - end - end - if ~isequal(M_.H,0) + error('estimation option conflict: "heteroskedastic_filter" incompatible with "fast_kalman_filter"') + end + if options_.analytic_derivation + error(['estimation option conflict: analytic_derivation isn''t available ' ... + 'for heteroskedastic_filter']) + end + + M_.heteroskedastic_shocks.Qvalue = NaN(M_.exo_nbr,options_.nobs+1); + M_.heteroskedastic_shocks.Qscale = NaN(M_.exo_nbr,options_.nobs+1); + + for k=1:length(M_.heteroskedastic_shocks.Qvalue_orig) + v = M_.heteroskedastic_shocks.Qvalue_orig(k); + temp_periods=v.periods(v.periods<options_.nobs+options_.first_obs+1); + temp_periods=temp_periods(temp_periods>=options_.first_obs); + M_.heteroskedastic_shocks.Qvalue(v.exo_id, temp_periods-(options_.first_obs-1)) = v.value^2; + end + for k=1:length(M_.heteroskedastic_shocks.Qscale_orig) + v = M_.heteroskedastic_shocks.Qscale_orig(k); + temp_periods=v.periods(v.periods<options_.nobs+options_.first_obs+1); + temp_periods=temp_periods(temp_periods>=options_.first_obs); + M_.heteroskedastic_shocks.Qscale(v.exo_id, temp_periods-(options_.first_obs-1)) = v.scale^2; + end + + if any(any(~isnan(M_.heteroskedastic_shocks.Qvalue) & ~isnan(M_.heteroskedastic_shocks.Qscale))) + fprintf('\ndynare_estimation_init: With the option "heteroskedastic_shocks" you cannot define\n') + fprintf('dynare_estimation_init: the scale and the value for the same shock \n') + fprintf('dynare_estimation_init: in the same period!\n') + error('Scale and value defined for the same shock in the same period with "heteroskedastic_shocks".') + end + end + + if (options_.occbin.likelihood.status && options_.occbin.likelihood.inversion_filter) || (options_.occbin.smoother.status && options_.occbin.smoother.inversion_filter) + if isempty(options_.occbin.likelihood.IVF_shock_observable_mapping) + options_.occbin.likelihood.IVF_shock_observable_mapping=find(diag(M_local.Sigma_e)~=0); + else + zero_var_shocks=find(diag(M_local.Sigma_e)==0); + if any(ismember(options_.occbin.likelihood.IVF_shock_observable_mapping,zero_var_shocks)) + error('IVF-filter: an observable is mapped to a zero variance shock.') + end + end + if ~isequal(M_.H,0) error('IVF-filter: Measurement erros are not allowed with the inversion filter.') end -end - -if options_.occbin.smoother.status && options_.occbin.smoother.inversion_filter - if ~isempty(options_.nk) - fprintf('dynare_estimation_init: the inversion filter does not support filter_step_ahead and filtered_variables. Disabling the option.\n') - options_.nk=[]; - options_.filter_step_ahead=[]; - options_.filtered_vars=0; - end - if options_.filter_covariance - fprintf('dynare_estimation_init: the inversion filter does not support filter_covariance. Disabling the option.\n') - options_.filter_covariance=false; - end - if options_.smoothed_state_uncertainty - fprintf('dynare_estimation_init: the inversion filter does not support smoothed_state_uncertainty. Disabling the option.\n') - options_.smoothed_state_uncertainty=false; - end -end - -if options_.occbin.smoother.status || options_.occbin.likelihood.status - if isfield(M_,'surprise_shocks') && ~isempty(M_.surprise_shocks) - fprintf('dynare_estimation_init: OccBin smoother/filter: previous shocks(surprise) block detected. Deleting its content.\n') - options_.occbin.simul.SHOCKS=zeros(1,M_.exo_nbr); - options_.occbin.simul.exo_pos=1:M_.exo_nbr; - M_.surprise_shocks=[]; - end -end + end + + if options_.occbin.smoother.status && options_.occbin.smoother.inversion_filter + if ~isempty(options_.nk) + fprintf('dynare_estimation_init: the inversion filter does not support filter_step_ahead and filtered variables. Disabling the option.\n') + options_.nk=[]; + options_.filter_step_ahead=[]; + options_.filtered_vars=0; + end + if options_.filter_covariance + fprintf('dynare_estimation_init: the inversion filter does not support filter_covariance. Disabling the option.\n') + options_.filter_covariance=false; + end + if options_.smoothed_state_uncertainty + fprintf('dynare_estimation_init: the inversion filter does not support smoothed_state_uncertainty. Disabling the option.\n') + options_.smoothed_state_uncertainty=false; + end + end + + if options_.occbin.smoother.status || options_.occbin.likelihood.status + if isfield(M_,'surprise_shocks') && ~isempty(M_.surprise_shocks) + fprintf('dynare_estimation_init: OccBin smoother/filter: previous shocks(surprise) block detected. Deleting its content.\n') + options_.occbin.simul.SHOCKS=zeros(1,M_.exo_nbr); + options_.occbin.simul.exo_pos=1:M_.exo_nbr; + M_.surprise_shocks=[]; + end + end diff --git a/matlab/estimation/get_user_defined_boundaries.m b/matlab/estimation/get_user_defined_boundaries.m new file mode 100644 index 0000000000..1d2bb517fc --- /dev/null +++ b/matlab/estimation/get_user_defined_boundaries.m @@ -0,0 +1,77 @@ +function [lb, ub] = get_user_defined_boundaries(estim_params_, M_, options_) + +% Get optimization boundaries on the estimated parameters. +% +% INPUTS +% - estim_params_ [struct] characterizing parameters to be estimated. +% - M_ [struct] characterizing the model. +% - options_ [struct] characterizing the options. +% +% OUTPUTS +% - lb [double] n×1 vector, lower bounds. +% - ub [double] n×1 vector, upper bounds. + +% Copyright © 2024 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 <https://www.gnu.org/licenses/>. + + estim_params_ = set_number_of_estimated_parameters(estim_params_); + + nn = estim_params_.nn; % Cumulated sum of estimated parameters by type (structural innovations std., measurement errors std., structural innovations correlations, measurement errors correlations, and deep parameters) + mm = estim_params_.mm; % Total number of estimated parameters + + lb = -Inf(mm, 1); + ub = Inf(mm, 1); + + if estim_params_.nvx + lb(1:nn(1)) = max(estim_params_.var_exo(:,3), 0); + ub(1:nn(1)) = estim_params_.var_exo(:,4); + check_interval(lb(1:nn(1)), ub(1:nn(1)), 1:nn(1), estim_params_.names) + end + + if estim_params_.nvn + lb(nn(1)+1:nn(2)) = max(estim_params_.var_endo(:,3), 0); + ub(nn(1)+1:nn(2)) = estim_params_.var_endo(:,4); + check_interval(lb(nn(1)+1:nn(2)), ub(nn(1)+1:nn(2)), nn(1)+1:nn(2), estim_params_.names) + end + + if estim_params_.ncx + lb(nn(2)+1:nn(3)) = min(max(estim_params_.corrx(:,4), -1), 1); + ub(nn(2)+1:nn(3)) = max(min(estim_params_.corrx(:,5), 1), -1); + check_interval(lb(nn(2)+1:nn(3)), ub(nn(2)+1:nn(3)), nn(2)+1:nn(3), estim_params_.names) + end + + if estim_params_.ncn + lb(nn(3)+1:nn(4)) = min(max(estim_params_.corrn(:,4), -1), 1); + ub(nn(3)+1:nn(4)) = max(min(estim_params_.corrn(:,5), 1), -1); + check_interval(lb(nn(3)+1:nn(4)), ub(nn(3)+1:nn(4)), nn(3)+1:nn(4), estim_params_.names) + end + + if estim_params_.np + lb(nn(4)+1:mm) = estim_params_.param_vals(:,3); + ub(nn(4)+1:mm) = estim_params_.param_vals(:,4); + check_interval(lb(nn(4)+1:mm), ub(nn(4)+1:mm), nn(4)+1:mm, estim_params_.names) + end + + function check_interval(a, b, idx, names) + problem = a>b; + if any(problem) + names = names(idx(problem)); + derror('Estimation:wrongBoundaries', sprintf('Check the declared bounds on (%s), the lower bound must be smaller than the upper bound.', regexprep(sprintf('%s, ', names{:}), ', $', ''))) + end + end + +end diff --git a/matlab/estimation/initial_estimation_checks.m b/matlab/estimation/initial_estimation_checks.m index bab50ffecd..1dcf31cfac 100644 --- a/matlab/estimation/initial_estimation_checks.m +++ b/matlab/estimation/initial_estimation_checks.m @@ -166,8 +166,8 @@ end [oo_.steady_state] = check_steady_state_changes_parameters(M_,estim_params_,oo_,options_, [options_.diffuse_filter==0 options_.diffuse_filter==0] ); % check and display warning if negative values of stderr or corr params are outside unit circle for Bayesian estimation -if any(bayestopt_.pshape) - check_prior_stderr_corr(estim_params_,bayestopt_); +if ~isempty(Prior) + check_prior_stderr_corr(estim_params_, Prior); end % display warning if some parameters are still NaN @@ -199,13 +199,11 @@ end % Evaluate the likelihood. ana_deriv = options_.analytic_derivation; options_.analytic_derivation=0; -if ~isequal(options_.mode_compute,11) || ... - (isequal(options_.mode_compute,11) && isequal(options_.order,1)) - %shut off potentially automatic switch to diffuse filter for the - %purpose of checking stochastic singularity - use_univariate_filters_if_singularity_is_detected_old=options_.use_univariate_filters_if_singularity_is_detected; - options_.use_univariate_filters_if_singularity_is_detected=0; - [fval,info] = feval(objective_function,xparam1,dataset_,dataset_info,options_,M_,estim_params_,bayestopt_,BoundsInfo,oo_.dr,oo_.steady_state,oo_.exo_steady_state,oo_.exo_det_steady_state); +if ~isequal(options_.mode_compute,11) || (isequal(options_.mode_compute,11) && isequal(options_.order,1)) + % Shut off potentially automatic switch to diffuse filter for the purpose of checking stochastic singularity + use_univariate_filters_if_singularity_is_detected_old = options_.use_univariate_filters_if_singularity_is_detected; + options_.use_univariate_filters_if_singularity_is_detected = false; + [fval, info] = feval(objective_function, xparam1, dataset_, dataset_info, options_, M_, Prior, estim_params_, bayestopt_, BoundsInfo, oo_.dr, oo_.steady_state, oo_.exo_steady_state, oo_.exo_det_steady_state); if info(1)==50 fprintf('\ninitial_estimation_checks:: The forecast error variance in the multivariate Kalman filter became singular.\n') fprintf('initial_estimation_checks:: This is often a sign of stochastic singularity, but can also sometimes happen by chance\n') @@ -217,8 +215,8 @@ if ~isequal(options_.mode_compute,11) || ... message=get_error_message(info,options_); error('initial_estimation_checks:: %s.',message) end - %reset options - options_.use_univariate_filters_if_singularity_is_detected=use_univariate_filters_if_singularity_is_detected_old; + % Reset options + options_.use_univariate_filters_if_singularity_is_detected = use_univariate_filters_if_singularity_is_detected_old; else info=0; fval = 0; diff --git a/matlab/estimation/initialguess.m b/matlab/estimation/initialguess.m new file mode 100644 index 0000000000..020518b22b --- /dev/null +++ b/matlab/estimation/initialguess.m @@ -0,0 +1,80 @@ +function [xparam1, estim_params_] = initialguess(estim_params_, usepriormode, Prior) + +% Return the initial guess for the estimated parameters +% +% INPUTS +% - estim_params_ [struct] characterizing parameters to be estimated +% - Prior [dprior] definition of the prior (if any) +% +% OUTPUTS +% - xparam1 [double] mm×1 vector, initial guess +% - estim_params_ [struct] characterizing parameters to be estimated (updated) +% +% REMARKS +% - This routine should be called after the instanciation of the dprior object + +% Copyright © 2024 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 <https://www.gnu.org/licenses/>. + + estim_params_ = set_number_of_estimated_parameters(estim_params_); + + nn = estim_params_.nn; % Cumulated sum of estimated parameters by type (structural innovations std., measurement errors std., structural innovations correlations, measurement errors correlations, and deep parameters) + mm = estim_params_.mm; % Total number of estimated parameters + + xparam1 = NaN(mm, 1); + + if estim_params_.nvx + xparam1(1:nn(1)) = estim_params_.var_exo(:,2); + end + + if estim_params_.nvn + xparam1(nn(1)+1:nn(2)) = estim_params_.var_endo(:,2); + end + + if estim_params_.ncx + xparam1(nn(2)+1:nn(3)) = estim_params_.corrx(:,3); + end + + if estim_params_.ncn + xparam1(nn(3)+1:nn(4)) = estim_params_.corrn(:,3); + end + + if estim_params_.np + xparam1(nn(4)+1:mm) = estim_params_.param_vals(:,2); + end + + if ~Prior.isempty() + declared_initial_guess = ~isnan(xparam1); + if ~usepriormode + % Initialize the estimated parameters with the prior mean (unless an initial guess has been explicitly defined). + xparam1(~declared_initial_guess) = Prior.p1(~declared_initial_guess); + else + % Initialize the estimated parameters with the prior mode (unless an initial guess has been explicitly defined). + xparam1(~declared_initial_guess) = Prior.p5(~declared_initial_guess); + no_prior_mode = isnan(xparam1); + if any(no_prior_mode) + % If the prior mode does not exist, use the prior mean as an initial guess. + xparam1(no_prior_mode) = Prior.p1(no_prior_mode); + end + end + end + + if any(isnan(xparam1)) + derror('Estimation:initialguess', 'Initial guess for all the estimated parameters must be provided.'); + end + +end diff --git a/matlab/estimation/isbayesian.m b/matlab/estimation/isbayesian.m new file mode 100644 index 0000000000..46804e5e30 --- /dev/null +++ b/matlab/estimation/isbayesian.m @@ -0,0 +1,150 @@ +function c = isbayesian(estim_params_) + +% Return true iff bayesian approach is used. +% +% INPUTS +% - estim_params_ [struct] Output of the preprocessor related to the estimation. +% +% OUTPUTS +% - c [logical] scalar. + +% Copyright © 2024 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 <https://www.gnu.org/licenses/>. + + c = false; + + b = false(5, 1); + i = true(5, 1); + + [b, i] = ispriorinblock(1, estim_params_.var_exo(:,5), b, i); + [b, i] = ispriorinblock(2, estim_params_.var_endo(:,5), b, i); + [b, i] = ispriorinblock(3, estim_params_.corrx(:,6), b, i); + [b, i] = ispriorinblock(4, estim_params_.corrn(:,6), b, i); + [b, i] = ispriorinblock(5, estim_params_.param_vals(:,5), b, i); + + b = b(i); + + if all(b) + c = true; + elseif any(b) + throwAsCaller(MException('Prior:specificationError', 'All estimated parameters must have a prior or none must have a prior (ML).')); + end + + return % --*-- Unit tests --*-- + + + %@test:1 + estimparams.var_exo = zeros(7, 10); + estimparams.var_exo(:,5) = [1;2;3;4;5;6;8]; + estimparams.var_endo = zeros(7, 10); + estimparams.var_endo(:,5) = [1;2;3;4;5;6;8]; + estimparams.corrx = zeros(7, 10); + estimparams.corrx(:,6) = [1;2;3;4;5;6;8]; + estimparams.corrn = zeros(7, 10); + estimparams.corrn(:,6) = [1;2;3;4;5;6;8]; + estimparams.param_vals = zeros(7, 10); + estimparams.param_vals(:,5) = [1;2;3;4;5;6;8]; + + try + b = isbayesian(estimparams); + t(1) = true; + catch + t(1) = false; + end + + if t(1) + t(2) = b; + end + + T = all(t); + %@eof:1 + + + %@test:2 + estimparams.var_exo = zeros(7, 10); + estimparams.var_endo = zeros(7, 10); + estimparams.var_endo(:,5) = [1;2;3;4;5;6;8]; + estimparams.corrx = zeros(7, 10); + estimparams.corrx(:,6) = [1;2;3;4;5;6;8]; + estimparams.corrn = zeros(7, 10); + estimparams.corrn(:,6) = [1;2;3;4;5;6;8]; + estimparams.param_vals = zeros(7, 10); + estimparams.param_vals(:,5) = [1;2;3;4;5;6;8]; + + try + b = isbayesian(estimparams); + t(1) = false; + catch + t(1) = true; + end + + T = all(t); + %@eof:2 + + + %@test:3 + estimparams.var_exo = zeros(7, 10); + estimparams.var_exo(:,5) = [1;2;3;4;5;6;8]; + estimparams.param_vals = zeros(7, 10); + estimparams.param_vals(:,5) = [1;2;3;4;5;6;8]; + + try + b = isbayesian(estimparams); + t(1) = true; + catch + t(1) = false; + end + + if t(1) + t(2) = b; + end + + T = all(t); + %@eof:3 + + + %@test:4 + estimparams.var_exo = zeros(7, 10); + estimparams.var_exo(:,5) = [1;2;3;4;5;6;8]; + estimparams.param_vals = zeros(7, 10); + estimparams.param_vals(:,5) = [1;2;3;4;5;6;8]; + estimparams.param_vals(2:3,5) = 0; + + try + b = isbayesian(estimparams); + t(1) = false; + catch + t(1) = true; + end + + T = all(t); + %@eof:4 + +end + + +function [b, i] = ispriorinblock(type, data, b, i) + if isempty(data) + i(type) = false; + else + if all(data) + b(type) = true; + elseif any(data) + throwAsCaller(MException('Prior:specificationError', 'All estimated parameters must have a prior or none must have a prior (ML).')); + end + end +end diff --git a/matlab/estimation/non_linear_dsge_likelihood.m b/matlab/estimation/non_linear_dsge_likelihood.m index a7b13c1798..5e7dcd3bca 100644 --- a/matlab/estimation/non_linear_dsge_likelihood.m +++ b/matlab/estimation/non_linear_dsge_likelihood.m @@ -1,4 +1,4 @@ -function [fval,info,exit_flag,DLIK,Hess,ys,trend_coeff,M_,options_,bayestopt_,dr] = non_linear_dsge_likelihood(xparam1,dataset_,dataset_info,options_,M_,estim_params_,bayestopt_,BoundsInfo,dr, endo_steady_state, exo_steady_state, exo_det_steady_state) +function [fval,info,exit_flag,DLIK,Hess,ys,trend_coeff,M_,options_,bayestopt_,dr] = non_linear_dsge_likelihood(xparam1,dataset_,dataset_info,options_,M_,Prior,estim_params_,bayestopt_,BoundsInfo,dr, endo_steady_state, exo_steady_state, exo_det_steady_state) % [fval,info,exit_flag,DLIK,Hess,ys,trend_coeff,M_,options_,bayestopt_,dr] = non_linear_dsge_likelihood(xparam1,dataset_,dataset_info,options_,M_,estim_params_,bayestopt_,BoundsInfo,dr, endo_steady_state, exo_steady_state, exo_det_steady_state) % Evaluates the posterior kernel of a dsge model using a non linear filter. % @@ -8,6 +8,7 @@ function [fval,info,exit_flag,DLIK,Hess,ys,trend_coeff,M_,options_,bayestopt_,dr % - dataset_info [struct] Matlab's structure describing the dataset % - options_ [struct] Matlab's structure describing the options % - M_ [struct] Matlab's structure describing the M_ +% - Prior [dprior] Definition of the prior density. % - estim_params_ [struct] Matlab's structure describing the estimated_parameters % - bayestopt_ [struct] Matlab's structure describing the priors % - BoundsInfo [struct] Matlab's structure specifying the bounds on the paramater values @@ -29,7 +30,7 @@ function [fval,info,exit_flag,DLIK,Hess,ys,trend_coeff,M_,options_,bayestopt_,dr % - bayestopt_ [struct] See INPUTS section. % - dr [struct] decision rule structure described in INPUTS section. -% Copyright © 2010-2023 Dynare Team +% Copyright © 2010-2024 Dynare Team % % This file is part of Dynare. % @@ -223,8 +224,7 @@ options_.warning_for_steadystate = 1; % ------------------------------------------------------------------------------ % Adds prior if necessary % ------------------------------------------------------------------------------ -lnprior = priordens(xparam1(:),bayestopt_.pshape,bayestopt_.p6,bayestopt_.p7,bayestopt_.p3,bayestopt_.p4); -fval = (likelihood-lnprior); +fval = likelihood-Prior.density(xparam1); if isnan(fval) fval = Inf; diff --git a/matlab/estimation/plot_priors.m b/matlab/estimation/plot_priors.m index 63b811fd8b..f2eea81e98 100644 --- a/matlab/estimation/plot_priors.m +++ b/matlab/estimation/plot_priors.m @@ -1,21 +1,18 @@ -function plot_priors(bayestopt_,M_,estim_params_,options_,optional_title) -% plot_priors(bayestopt_,M_,estim_params_,options_,optional_title) -% plots prior density +function plot_priors(Prior, M_, estim_params_, options_, optional_title) + +% Plot prior densities % % INPUTS -% o bayestopt_ [structure] -% o M_ [structure] -% o estim_params_ [structure] -% o options_ [structure] -% o optional_title [string] - -% OUTPUTS -% None +% - Prior [dprior] object containing prior distribution information. +% - M_ [struct] description of the model. +% - estim_params_ [struct] description of the estimated parameters. +% - options_ [struct] Dynare's options. +% - optional_title [char] 1×n array, title. % -% SPECIAL REQUIREMENTS -% None +% OUTPUTS +% None -% Copyright © 2004-2023 Dynare Team +% Copyright © 2004-2024 Dynare Team % % This file is part of Dynare. % @@ -40,7 +37,7 @@ if nargin<5 else figurename = optional_title; end -npar = length(bayestopt_.p1); +npar = Prior.length(); [nbplt,nr,nc,~,~,nstar] = pltorg(npar); if TeX && any(strcmp('eps',cellstr(options_.graph_format))) @@ -59,7 +56,7 @@ for plt = 1:nbplt for index=1:nstar0 names = []; i = (plt-1)*nstar + index; - [x,f] = draw_prior_density(i,bayestopt_); + [x, f] = Prior.data_for_plotting_density(i); [nam,texnam] = get_the_name(i,TeX,M_,estim_params_,options_.varobs); subplot(nr,nc,index) hh_plt = plot(x,f,'-k','linewidth',2); @@ -86,4 +83,4 @@ if TeX && any(strcmp('eps',cellstr(options_.graph_format))) fprintf(fidTeX,' \n'); fprintf(fidTeX,'%% End of TeX file.\n'); fclose(fidTeX); -end \ No newline at end of file +end diff --git a/matlab/estimation/posterior_sampler.m b/matlab/estimation/posterior_sampler.m index 2668ee1a58..b4bc463d3f 100644 --- a/matlab/estimation/posterior_sampler.m +++ b/matlab/estimation/posterior_sampler.m @@ -1,4 +1,4 @@ -function posterior_sampler(TargetFun,ProposalFun,xparam1,sampler_options,mh_bounds,dataset_,dataset_info,options_,M_,estim_params_,bayestopt_,oo_,dispString) +function posterior_sampler(TargetFun,ProposalFun,xparam1,sampler_options,mh_bounds,dataset_,dataset_info,options_,M_,Prior,estim_params_,bayestopt_,oo_,dispString) % function posterior_sampler(TargetFun,ProposalFun,xparam1,sampler_options,mh_bounds,dataset_,dataset_info,options_,M_,estim_params_,bayestopt_,oo_,dispString) % Random Walk Metropolis-Hastings algorithm. % @@ -57,7 +57,7 @@ function posterior_sampler(TargetFun,ProposalFun,xparam1,sampler_options,mh_boun vv = sampler_options.invhess; % Initialization of the sampler [ ix2, ilogpo2, ModelName, MetropolisFolder, fblck, fline, npar, nblck, nruns, NewFile, MAX_nruns, d, bayestopt_] = ... - posterior_sampler_initialization(TargetFun, xparam1, vv, mh_bounds,dataset_,dataset_info,options_,M_,estim_params_,bayestopt_,oo_, dispString); + posterior_sampler_initialization(TargetFun, xparam1, vv, mh_bounds,dataset_,dataset_info,options_,M_,Prior,estim_params_,bayestopt_,oo_, dispString); InitSizeArray = min([repmat(MAX_nruns,nblck,1) fline+nruns-1],[],2); diff --git a/matlab/estimation/posterior_sampler_initialization.m b/matlab/estimation/posterior_sampler_initialization.m index 354b00916f..9526e60463 100644 --- a/matlab/estimation/posterior_sampler_initialization.m +++ b/matlab/estimation/posterior_sampler_initialization.m @@ -1,5 +1,5 @@ function [ ix2, ilogpo2, ModelName, MetropolisFolder, FirstBlock, FirstLine, npar, NumberOfBlocks, nruns, NewFile, MAX_nruns, d, bayestopt_] = ... - posterior_sampler_initialization(TargetFun, xparam1, vv, mh_bounds, dataset_, dataset_info, options_, M_, estim_params_, bayestopt_, oo_, dispString) + posterior_sampler_initialization(TargetFun, xparam1, vv, mh_bounds, dataset_, dataset_info, options_, M_, Prior, estim_params_, bayestopt_, oo_, dispString) % Posterior sampler initialization. % @@ -207,7 +207,7 @@ if ~options_.load_mh_file && ~options_.mh_recover end if all(candidate(:) >= mh_bounds.lb) && all(candidate(:) <= mh_bounds.ub) ix2(j,new_estimated_parameters) = candidate(new_estimated_parameters); - ilogpo2(j) = - feval(TargetFun,ix2(j,:)',dataset_,dataset_info,options_,M_,estim_params_,bayestopt_,mh_bounds,oo_.dr, oo_.steady_state,oo_.exo_steady_state,oo_.exo_det_steady_state); + ilogpo2(j) = - feval(TargetFun,ix2(j,:)',dataset_,dataset_info,options_,M_,Prior,estim_params_,bayestopt_,mh_bounds,oo_.dr, oo_.steady_state,oo_.exo_steady_state,oo_.exo_det_steady_state); if isfinite(ilogpo2(j)) % log density is finite fprintf(fidlog,[' Blck ' int2str(j) ':\n']); for i=1:length(ix2(1,:)) diff --git a/matlab/estimation/private/initialize_measurement_errors_covariance_matrix.m b/matlab/estimation/private/initialize_measurement_errors_covariance_matrix.m new file mode 100644 index 0000000000..f2bc931f8b --- /dev/null +++ b/matlab/estimation/private/initialize_measurement_errors_covariance_matrix.m @@ -0,0 +1,30 @@ +function M_ = initialize_measurement_errors_covariance_matrix(estim_params_, M_, options_) + +% If no previously set measurement error, initialize the measurement error covariance matrix H. + +% Copyright © 2024 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 <https://www.gnu.org/licenses/>. + + estim_params_ = set_number_of_estimated_parameters(estim_params_); + + if estim_params_.nvn && isequal(M_.H, 0) + n = length(options_.varobs); + M_.H = zeros(n, n); + M_.Correlation_matrix_ME = eye(n); + end + +end diff --git a/matlab/estimation/private/set_corrn_observable_correspondence.m b/matlab/estimation/private/set_corrn_observable_correspondence.m new file mode 100644 index 0000000000..2b3f7d8426 --- /dev/null +++ b/matlab/estimation/private/set_corrn_observable_correspondence.m @@ -0,0 +1,30 @@ +function estim_params_ = set_corrn_observable_correspondence(estim_params_, endo_names, varobs) + +% Stores indices, in options_.varobs, of the corresponding observed variables + +% Copyright © 2024 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 <https://www.gnu.org/licenses/>. + + if ~isfield(estim_params_, 'corrn_observable_correspondence') + estim_params_.corrn_observable_correspondence = NaN(estim_params_.ncn, 2); + for i=1:estim_params_.ncn + estim_params_.corrn_observable_correspondence(i,1) = find(strcmp(endo_names{estim_params_.corrn(i,1)}, varobs)); + estim_params_.corrn_observable_correspondence(i,2) = find(strcmp(endo_names{estim_params_.corrn(i,2)}, varobs)); + end + end + +end diff --git a/matlab/estimation/private/set_jscale.m b/matlab/estimation/private/set_jscale.m new file mode 100644 index 0000000000..b31c2fd799 --- /dev/null +++ b/matlab/estimation/private/set_jscale.m @@ -0,0 +1,64 @@ +function estim_params_ = set_jscale(estim_params_, overwrite) + +% Set the estimated parameter specific MH scaling parameter. +% +% INPUTS +% - estim_params_ [struct] characterizing parameters to be estimated +% - overwrite [logical] scalar, overwrite jscale field if true (default is false) +% +% OUTPUTS +% - estim_params_ [struct] Updated characterization of the parameters to be estimated. + +% Copyright © 2024 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 <https://www.gnu.org/licenses/>. + + if nargin<2 + overwrite = false; + end + + if ~isfield(estim_params_, 'jscale') || overwrite + + estim_params_ = set_number_of_estimated_parameters(estim_params_); + + nn = estim_params_.nn; % Cumulated sum of estimated parameters by type (structural innovations std., measurement errors std., structural innovations correlations, measurement errors correlations, and deep parameters) + mm = estim_params_.mm; % Total number of estimated parameters + + estim_params_.jscale = ones(mm, 1); + + if estim_params_.nvx + estim_params_.jscale(1:nn(1)) = estim_params_.var_exo(:,10); + end + + if estim_params_.nvn + estim_params_.jscale(nn(1)+1:nn(2)) = estim_params_.var_endo(:,10); + end + + if estim_params_.ncx + estim_params_.jscale(nn(2)+1:nn(3)) = estim_params_.corrx(:,11); + end + + if estim_params_.ncn + estim_params_.jscale((nn(3)+1:nn(4))) = estim_params_.corrn(:,11); + end + + if estim_params_.np + estim_params_.jscale(nn(4)+1:mm) = estim_params_.param_vals(:,10); + end + + end + +end diff --git a/matlab/estimation/private/set_number_of_estimated_parameters.m b/matlab/estimation/private/set_number_of_estimated_parameters.m new file mode 100644 index 0000000000..342c17f444 --- /dev/null +++ b/matlab/estimation/private/set_number_of_estimated_parameters.m @@ -0,0 +1,56 @@ +function estim_params_ = set_number_of_estimated_parameters(estim_params_) + +% Add fields to estim_params_ structure (number of estimated parameters). +% +% INPUTS +% - estim_params_ [structure] characterizing parameters to be estimated. +% +% OUTPUTS +% - estim_params_ [structure] characterizing parameters to be estimated + +% Copyright © 2024 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 <https://www.gnu.org/licenses/>. + + if isempty(estim_params_) + estim_params_.estimation = false; + return + end + + if ~isfield(estim_params_, 'nvx') + nvx = size(estim_params_.var_exo,1); + nvn = size(estim_params_.var_endo,1); + ncx = size(estim_params_.corrx,1); + ncn = size(estim_params_.corrn,1); + np = size(estim_params_.param_vals,1); + nn = cumsum([nvx; nvn; ncx; ncn; np]); + mm = nn(end); + estim_params_.nvx = nvx; % exogenous shock variances + estim_params_.nvn = nvn; % endogenous variances, i.e. measurement error + estim_params_.ncx = ncx; % exogenous shock correlations + estim_params_.ncn = ncn; % correlation between endogenous variables, i.e. measurement error. + estim_params_.np = np; % other parameters of the model + estim_params_.nn = nn; % cumulated sum of parameter # by type + estim_params_.mm = mm; % total number of estimated parameters + end + + if isfield(estim_params_, 'mm') + estim_params_.estimation = estim_params_.mm>0; + else + estim_params_.estimation = false; + end + +end diff --git a/matlab/estimation/private/set_nvn_observable_correspondence.m b/matlab/estimation/private/set_nvn_observable_correspondence.m new file mode 100644 index 0000000000..5adab22e51 --- /dev/null +++ b/matlab/estimation/private/set_nvn_observable_correspondence.m @@ -0,0 +1,31 @@ +function estim_params_ = set_nvn_observable_correspondence(estim_params_, endo_names, varobs) + +% Stores indices, in options_.varobs, of the corresponding observed variables + +% Copyright © 2024 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 <https://www.gnu.org/licenses/>. + + estim_params_.nvn_observable_correspondence = NaN(estim_params_.nvn, 1); + for i=1:estim_params_.nvn + obs = strcmp(endo_names{estim_params_.var_endo(i,1)}, varobs); + if ~any(obs) + derror('Estimation:wrongMeasurementEquation', sprintf('The variable %s has to be declared as observable since you assume a measurement error on it.', M_.endo_names{estim_params_.var_endo(i,1)})) + end + estim_params_.nvn_observable_correspondence(i) = find(obs); + end + +end diff --git a/matlab/estimation/private/set_optimization_boundaries.m b/matlab/estimation/private/set_optimization_boundaries.m new file mode 100644 index 0000000000..486a1f636f --- /dev/null +++ b/matlab/estimation/private/set_optimization_boundaries.m @@ -0,0 +1,66 @@ +function [lb, ub] = set_optimization_boundaries(estim_params_, Prior) + +% Set optimization boundaries on the estimated parameters +% +% INPUTS: +% - estim_params_ [struct] characterizing parameters to be estimated. +% - Prior [dprior] Prior distributon (if any) +% +% OUTPUTS: +% - lb [double] mm×1 vector, lower bounds +% - ub [double] mm×1 vector, lower bounds + +% Copyright © 2024 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 <https://www.gnu.org/licenses/>. + + nn = estim_params_.nn; % Cumulated sum of estimated parameters by type (structural innovations std., measurement errors std., structural innovations correlations, measurement errors correlations, and deep parameters) + mm = estim_params_.mm; % Total number of estimated parameters + + ub = Inf(mm, 1); % Upper bound imposed for optimization. + lb = -Inf(mm, 1); % Lower bound imposed for optimization. + + if estim_params_.nvx + ub(1:nn(1)) = estim_params_.var_exo(:,4); + lb(1:nn(1)) = estim_params_.var_exo(:,3); + end + + if estim_params_.nvn + ub(nn(1)+1:nn(2)) = estim_params_.var_endo(:,4); + lb(nn(1)+1:nn(2)) = estim_params_.var_endo(:,3); + end + + if estim_params_.ncx + ub(nn(2)+1:nn(3)) = max(min(estim_params_.corrx(:,5),1),-1); + lb(nn(2)+1:nn(3)) = min(max(estim_params_.corrx(:,4),-1),1); + end + + if estim_params_.ncn + ub(nn(3)+1:nn(4)) = max(min(estim_params_.corrn(:,5),1),-1); + lb(nn(3)+1:nn(4)) = min(max(estim_params_.corrn(:,4),-1),1); + end + + if estim_params_.np + ub(nn(4)+1:mm) = estim_params_.param_vals(:,4); + lb(nn(4)+1:mm) = estim_params_.param_vals(:,3); + end + + if ~isempty(Prior) + ub = min(Prior.ub, ub); + lb = max(Prior.lb, lb); + end + +end diff --git a/matlab/estimation/private/set_parameter_names.m b/matlab/estimation/private/set_parameter_names.m new file mode 100644 index 0000000000..52a299d81b --- /dev/null +++ b/matlab/estimation/private/set_parameter_names.m @@ -0,0 +1,68 @@ +function estim_params_ = set_parameter_names(estim_params_, paramnames, exonames, endonames, varobs) + +% Set names of the estimated parameters under a new field of estim_params_. +% +% INPUTS +% - estim_params_ [struct] +% - paramnames [cell] parameter names +% - exonames [cell] names of the exogenous variables +% - endonames [cell] names of the endogenous variables +% - varobs [cell] names of the observed variables +% +% OUTPUTS +% - estim_params_ [struct] + +% Copyright © 2024 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 <https://www.gnu.org/licenses/>. + + estim_params_ = set_number_of_estimated_parameters(estim_params_); + + if ~isfield('names', estim_params_) + + nn = estim_params_.nn; % Cumulated sum of estimated parameters by type (structural innovations std., measurement errors std., structural innovations correlations, measurement errors correlations, and deep parameters) + mm = estim_params_.mm; % Total number of estimated parameters + + estim_params_.names = cell(mm, 1); % names of the estimated parameters + + if estim_params_.nvx + estim_params_.names(1:nn(1)) = exonames(estim_params_.var_exo(:,1)); + end + + if estim_params_.nvn + estim_params_.names(nn(1)+1:nn(2)) = varobs(estim_params_.nvn_observable_correspondence); + end + + if estim_params_.ncx + for i = 1:estim_params_.ncx + estim_params_.names(nn(2)+i) = {sprintf('corr %s, %s', exonames{estim_params_.corrx(i,1)}, exonames{estim_params_.corrx(i,2)})}; + end + end + + if estim_params_.ncn + for i=1:estim_params_.ncn + id = estim_params_.corrn_observable_correspondence(i,:); + estim_params_.names(nn(3)+i) = {sprintf('corr %s, %s', varobs{id(1)}, varobs{id(2)})}; + end + end + + if estim_params_.np + estim_params_.names(nn(4)+1:mm) = paramnames(estim_params_.param_vals(:,1)); + end + + end + +end diff --git a/matlab/estimation/set_boundaries.m b/matlab/estimation/set_boundaries.m new file mode 100644 index 0000000000..a1a1e3da8c --- /dev/null +++ b/matlab/estimation/set_boundaries.m @@ -0,0 +1,68 @@ +function [lb, ub] = set_boundaries(estim_params_, M_, options_) + + +% Set optimization boundaries on the estimated parameters. +% +% INPUTS +% - estim_params_ [struct] characterizing parameters to be estimated. +% - M_ [struct] characterizing the model. +% - options_ [struct] characterizing the options. +% +% OUTPUTS +% - lb [double] n×1 vector, lower bounds. +% - ub [double] n×1 vector, upper bounds. + +% Copyright © 2024 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 <https://www.gnu.org/licenses/>. + + estim_params_ = set_number_of_estimated_parameters(estim_params_); + + nn = estim_params_.nn; % Cumulated sum of estimated parameters by type (structural innovations std., measurement errors std., structural innovations correlations, measurement errors correlations, and deep parameters) + mm = estim_params_.mm; % Total number of estimated parameters + + lb = -Inf(mm, 1); + ub = Inf(mm, 1); + + if estim_params_.nvx + lb(1:nn(1)) = max(estim_params_.var_exo(:,3), 0); + ub(1:nn(1)) = estim_params_.var_exo(:,4); + problem = lb(1:nn(1))>ub(1:nn(1)); + if any(problem) + derror('Estimation:wrongBoundaries', sprintf('Check the declared bounds on (%s), the lower bound must be smaller than the upper bound.', regexprep(sprintf('%s, ', p{[1, 3]}), ', $', ''))) + end + end + + if estim_params_.nvn + lb(nn(1)+1:nn(2)) = max(estim_params_.var_endo(:,3), 0); + ub(nn(1)+1:nn(2)) = estim_params_.var_endo(:,4); + end + + if estim_params_.ncx + lb(nn(2)+1:nn(3)) = min(max(estim_params_.corrx(:,4), -1), 1); + ub(nn(2)+1:nn(3)) = max(min(estim_params_.corrx(:,5), 1), -1); + end + + if estim_params_.ncn + lb(nn(3)+1:nn(4)) = min(max(estim_params_.corrn(:,4), -1), 1); + ub(nn(3)+1:nn(4)) = max(min(estim_params_.corrn(:,5), 1), -1); + end + + if estim_params_.np + lb(nn(4)+1:mm) = estim_params_.param_vals(:,3); + ub(nn(4)+1:mm) = estim_params_.param_vals(:,4); + end +end diff --git a/matlab/estimation/set_prior.m b/matlab/estimation/set_prior.m index 238aca5cd3..198c96496c 100644 --- a/matlab/estimation/set_prior.m +++ b/matlab/estimation/set_prior.m @@ -1,24 +1,16 @@ -function [xparam1, estim_params_, bayestopt_, lb, ub, M_]=set_prior(estim_params_, M_, options_) -% function [xparam1,estim_params_,bayestopt_,lb,ub, M_]=set_prior(estim_params_, M_, options_) +function Prior = set_prior(estim_params_, M_, options_) + % sets prior distributions % % INPUTS -% o estim_params_ [structure] characterizing parameters to be estimated. -% o M_ [structure] characterizing the model. -% o options_ [structure] characterizing the options. +% - estim_params_ [struct] characterizing parameters to be estimated. +% - M_ [struct] characterizing the model. +% - options_ [struct] characterizing the options. % % OUTPUTS -% o xparam1 [double] vector of parameters to be estimated (initial values) -% o estim_params_ [structure] characterizing parameters to be estimated -% o bayestopt_ [structure] characterizing priors -% o lb [double] vector of lower bounds for the estimated parameters. -% o ub [double] vector of upper bounds for the estimated parameters. -% o M_ [structure] characterizing the model. -% -% SPECIAL REQUIREMENTS -% None +% - Prior [dprior] -% Copyright © 2003-2023 Dynare Team +% Copyright © 2003-2024 Dynare Team % % This file is part of Dynare. % @@ -35,254 +27,233 @@ function [xparam1, estim_params_, bayestopt_, lb, ub, M_]=set_prior(estim_params % You should have received a copy of the GNU General Public License % along with Dynare. If not, see <https://www.gnu.org/licenses/>. -nvx = size(estim_params_.var_exo,1); -nvn = size(estim_params_.var_endo,1); -ncx = size(estim_params_.corrx,1); -ncn = size(estim_params_.corrn,1); -np = size(estim_params_.param_vals,1); +estim_params_ = set_number_of_estimated_parameters(estim_params_); -estim_params_.nvx = nvx; %exogenous shock variances -estim_params_.nvn = nvn; %endogenous variances, i.e. measurement error -estim_params_.ncx = ncx; %exogenous shock correlations -estim_params_.ncn = ncn; % correlation between endogenous variables, i.e. measurement error. -estim_params_.np = np; % other parameters of the model +nn = estim_params_.nn; % Cumulated sum of estimated parameters by type (structural innovations std., measurement errors std., structural innovations correlations, measurement errors correlations, and deep parameters) +mm = estim_params_.mm; % Total number of estimated parameters -xparam1 = []; -ub = []; % Upper bound imposed for optimization. -lb = []; % Lower bound imposed for optimization. -bayestopt_.pshape = []; -bayestopt_.p1 = []; % prior mean -bayestopt_.p2 = []; % prior standard deviation -bayestopt_.p3 = []; % lower bound of the distribution, only considering whether a generalized distribution is used, not when the prior is truncated -bayestopt_.p4 = []; % upper bound of the distribution, only considering whether a generalized distribution is used, not when the prior is truncated -bayestopt_.p5 = zeros(nvx+nvn+ncx+ncn+np,1); % prior mode -bayestopt_.p6 = []; % first hyper-parameter (\alpha for the BETA and GAMMA distributions, s for the INVERSE GAMMAs, expectation for the GAUSSIAN distribution, lower bound for the UNIFORM distribution). -bayestopt_.p7 = []; % second hyper-parameter (\beta for the BETA and GAMMA distributions, \nu for the INVERSE GAMMAs, standard deviation for the GAUSSIAN distribution, upper bound for the UNIFORM distribution). +prior.pshape = NaN(mm, 1); % Index for the prior distribution name: + % + % 1 → (Generalized) Beta distribution, + % 2 → Gamma distribution (possibly shifted) + % 3 → Gaussian distribution + % 4 → Inverse gamma, type I distribution (possibly shifted) + % 5 → Uniform distribution + % 6 → Inverse gamma, type II distribution (possibly shifted) + % 8 → Weibull distribution (possibly shifted) + % +prior.p1 = NaN(mm, 1); % prior mean +prior.p2 = NaN(mm, 1); % prior standard deviation +prior.p3 = NaN(mm, 1); % lower bound of the distribution, only considering whether a generalized distribution is used, not when the prior is truncated +prior.p4 = NaN(mm, 1); % upper bound of the distribution, only considering whether a generalized distribution is used, not when the prior is truncated +prior.p5 = NaN(mm, 1); % prior mode +prior.p6 = NaN(mm, 1); % first hyper-parameter (\alpha for the BETA and GAMMA distributions, s for the INVERSE GAMMAs, expectation for the GAUSSIAN distribution, lower bound for the UNIFORM distribution). +prior.p7 = NaN(mm, 1); % second hyper-parameter (\beta for the BETA and GAMMA distributions, \nu for the INVERSE GAMMAs, standard deviation for the GAUSSIAN distribution, upper bound for the UNIFORM distribution). +prior.name = cell(mm, 1); % names of the estimated parameters -bayestopt_.jscale = []; %jscale might subsequently be overwritten by mode_compute=6 or check_posterior_sampler_options -bayestopt_.name = {}; -if nvx - xparam1 = estim_params_.var_exo(:,2); - ub = estim_params_.var_exo(:,4); - lb = estim_params_.var_exo(:,3); - bayestopt_.pshape = estim_params_.var_exo(:,5); - bayestopt_.p1 = estim_params_.var_exo(:,6); - bayestopt_.p2 = estim_params_.var_exo(:,7); - bayestopt_.p3 = estim_params_.var_exo(:,8); %take generalized distribution into account - bayestopt_.p4 = estim_params_.var_exo(:,9); %take generalized distribution into account - bayestopt_.jscale = estim_params_.var_exo(:,10); - bayestopt_.name = M_.exo_names(estim_params_.var_exo(:,1)); +if estim_params_.nvx + prior.pshape(1:nn(1)) = estim_params_.var_exo(:,5); + prior.p1(1:nn(1)) = estim_params_.var_exo(:,6); + prior.p2(1:nn(1)) = estim_params_.var_exo(:,7); + prior.p3(1:nn(1)) = estim_params_.var_exo(:,8); + prior.p4(1:nn(1)) = estim_params_.var_exo(:,9); + prior.name(1:nn(1)) = M_.exo_names(estim_params_.var_exo(:,1)); end -if nvn - estim_params_.nvn_observable_correspondence=NaN(nvn,1); % stores number of corresponding observable - if isequal(M_.H,0) %if no previously set measurement error, initialize H - nvarobs = length(options_.varobs); - M_.H = zeros(nvarobs,nvarobs); - M_.Correlation_matrix_ME = eye(nvarobs); - end - for i=1:nvn - obsi_ = strmatch(M_.endo_names{estim_params_.var_endo(i,1)}, options_.varobs, 'exact'); - if isempty(obsi_) - error(['The variable ' M_.endo_names{estim_params_.var_endo(i,1)} ' has to be declared as observable since you assume a measurement error on it.']) - end - estim_params_.nvn_observable_correspondence(i,1)=obsi_; - end - xparam1 = [xparam1; estim_params_.var_endo(:,2)]; - ub = [ub; estim_params_.var_endo(:,4)]; - lb = [lb; estim_params_.var_endo(:,3)]; - bayestopt_.pshape = [ bayestopt_.pshape; estim_params_.var_endo(:,5)]; - bayestopt_.p1 = [ bayestopt_.p1; estim_params_.var_endo(:,6)]; - bayestopt_.p2 = [ bayestopt_.p2; estim_params_.var_endo(:,7)]; - bayestopt_.p3 = [ bayestopt_.p3; estim_params_.var_endo(:,8)]; %take generalized distribution into account - bayestopt_.p4 = [ bayestopt_.p4; estim_params_.var_endo(:,9)]; %take generalized distribution into account - bayestopt_.jscale = [ bayestopt_.jscale; estim_params_.var_endo(:,10)]; - bayestopt_.name = [ bayestopt_.name; options_.varobs(estim_params_.nvn_observable_correspondence)]; + +if estim_params_.nvn + prior.pshape(nn(1)+1:nn(2)) = estim_params_.var_endo(:,5); + prior.p1(nn(1)+1:nn(2)) = estim_params_.var_endo(:,6); + prior.p2(nn(1)+1:nn(2)) = estim_params_.var_endo(:,7); + prior.p3(nn(1)+1:nn(2)) = estim_params_.var_endo(:,8); + prior.p4(nn(1)+1:nn(2)) = estim_params_.var_endo(:,9); + prior.name(nn(1)+1:nn(2)) = options_.varobs(estim_params_.nvn_observable_correspondence); end -if ncx - xparam1 = [xparam1; estim_params_.corrx(:,3)]; - ub = [ub; max(min(estim_params_.corrx(:,5),1),-1)]; - lb = [lb; min(max(estim_params_.corrx(:,4),-1),1)]; - bayestopt_.pshape = [ bayestopt_.pshape; estim_params_.corrx(:,6)]; - bayestopt_.p1 = [ bayestopt_.p1; estim_params_.corrx(:,7)]; - bayestopt_.p2 = [ bayestopt_.p2; estim_params_.corrx(:,8)]; - bayestopt_.p3 = [ bayestopt_.p3; estim_params_.corrx(:,9)]; %take generalized distribution into account - bayestopt_.p4 = [ bayestopt_.p4; estim_params_.corrx(:,10)]; %take generalized distribution into account - bayestopt_.jscale = [ bayestopt_.jscale; estim_params_.corrx(:,11)]; - baseid = length(bayestopt_.name); - bayestopt_.name = [bayestopt_.name; cell(ncx, 1)]; - for i = 1:ncx - bayestopt_.name(baseid+i) = {sprintf('corr %s, %s', ... - M_.exo_names{estim_params_.corrx(i,1)}, ... - M_.exo_names{estim_params_.corrx(i,2)})}; + +if estim_params_.ncx + prior.pshape(nn(2)+1:nn(3)) = estim_params_.corrx(:,6); + prior.p1(nn(2)+1:nn(3)) = estim_params_.corrx(:,7); + prior.p2(nn(2)+1:nn(3)) = estim_params_.corrx(:,8); + prior.p3(nn(2)+1:nn(3)) = estim_params_.corrx(:,9); + prior.p4(nn(2)+1:nn(3)) = estim_params_.corrx(:,10); + for i = 1:estim_params_.ncx + prior.name(nn(2)+i) = {sprintf('corr %s, %s', M_.exo_names{estim_params_.corrx(i,1)}, M_.exo_names{estim_params_.corrx(i,2)})}; end end -if ncn - estim_params_.corrn_observable_correspondence=NaN(ncn,2); - if isequal(M_.H,0) - nvarobs = length(options_.varobs); - M_.H = zeros(nvarobs,nvarobs); - M_.Correlation_matrix_ME = eye(nvarobs); - end - xparam1 = [xparam1; estim_params_.corrn(:,3)]; - ub = [ub; max(min(estim_params_.corrn(:,5),1),-1)]; - lb = [lb; min(max(estim_params_.corrn(:,4),-1),1)]; - bayestopt_.pshape = [ bayestopt_.pshape; estim_params_.corrn(:,6)]; - bayestopt_.p1 = [ bayestopt_.p1; estim_params_.corrn(:,7)]; - bayestopt_.p2 = [ bayestopt_.p2; estim_params_.corrn(:,8)]; - bayestopt_.p3 = [ bayestopt_.p3; estim_params_.corrn(:,9)]; %take generalized distribution into account - bayestopt_.p4 = [ bayestopt_.p4; estim_params_.corrn(:,10)]; %take generalized distribution into account - bayestopt_.jscale = [ bayestopt_.jscale; estim_params_.corrn(:,11)]; - baseid = length(bayestopt_.name); - bayestopt_.name = [bayestopt_.name; cell(ncn, 1)]; - for i=1:ncn - k1 = estim_params_.corrn(i,1); - k2 = estim_params_.corrn(i,2); - bayestopt_.name(baseid+i) = {sprintf('corr %s, %s', M_.endo_names{k1}, M_.endo_names{k2})}; - % find correspondence to varobs to construct H in set_all_paramters - obsi1 = strmatch(M_.endo_names{k1}, options_.varobs, 'exact'); - obsi2 = strmatch(M_.endo_names{k2}, options_.varobs, 'exact'); - % save correspondence - estim_params_.corrn_observable_correspondence(i,:)=[obsi1, obsi2]; + +if estim_params_.ncn + prior.pshape(nn(3)+1:nn(4)) = estim_params_.corrn(:,6); + prior.p1(nn(3)+1:nn(4)) = estim_params_.corrn(:,7); + prior.p2(nn(3)+1:nn(4)) = estim_params_.corrn(:,8); + prior.p3(nn(3)+1:nn(4)) = estim_params_.corrn(:,9); + prior.p4(nn(3)+1:nn(4)) = estim_params_.corrn(:,10); + for i=1:estim_params_.ncn + id = estim_params_.corrn_observable_correspondence(i,:); + prior.name(nn(3)+i) = {sprintf('corr %s, %s', options_.varobs{id(1)}, options_.varobs{id(2)})}; end end -if np - xparam1 = [xparam1; estim_params_.param_vals(:,2)]; - ub = [ub; estim_params_.param_vals(:,4)]; - lb = [lb; estim_params_.param_vals(:,3)]; - bayestopt_.pshape = [ bayestopt_.pshape; estim_params_.param_vals(:,5)]; - bayestopt_.p1 = [ bayestopt_.p1; estim_params_.param_vals(:,6)]; - bayestopt_.p2 = [ bayestopt_.p2; estim_params_.param_vals(:,7)]; - bayestopt_.p3 = [ bayestopt_.p3; estim_params_.param_vals(:,8)]; %take generalized distribution into account - bayestopt_.p4 = [ bayestopt_.p4; estim_params_.param_vals(:,9)]; %take generalized distribution into account - bayestopt_.jscale = [ bayestopt_.jscale; estim_params_.param_vals(:,10)]; - bayestopt_.name = [bayestopt_.name; M_.param_names(estim_params_.param_vals(:,1))]; + +if estim_params_.np + prior.pshape(nn(4)+1:mm) = estim_params_.param_vals(:,5); + prior.p1(nn(4)+1:mm) = estim_params_.param_vals(:,6); + prior.p2(nn(4)+1:mm) = estim_params_.param_vals(:,7); + prior.p3(nn(4)+1:mm) = estim_params_.param_vals(:,8); %take generalized distribution into account + prior.p4(nn(4)+1:mm) = estim_params_.param_vals(:,9); %take generalized distribution into account + prior.name(nn(4)+1:mm) = M_.param_names(estim_params_.param_vals(:,1)); end -bayestopt_.p6 = NaN(size(bayestopt_.p1)) ; -bayestopt_.p7 = bayestopt_.p6 ; +% +% Throw an error if a prior standard deviation is negative +% -%% check for point priors and disallow them as they do not work with MCMC -if any(bayestopt_.p2 ==0) - error('Error in prior for %s: you cannot use a point prior in estimation. Either increase the prior standard deviation',... - ' or fix the parameter completely.', bayestopt_.name{bayestopt_.p2 ==0}) +negative_std = prior.p2<0; + +if any(negative_std) + for i=1:mm + dprintf('Prior std. on %s is negative.', prior.name{i}) + end + skipline() + derror('Prior:wrongDefinition', 'Check your priors. Negative prior standard deviation is not allowed.') end -% generalized location parameters by default for beta distribution -k = find(bayestopt_.pshape == 1); -k1 = find(isnan(bayestopt_.p3(k))); -bayestopt_.p3(k(k1)) = zeros(length(k1),1); -k1 = find(isnan(bayestopt_.p4(k))); -bayestopt_.p4(k(k1)) = ones(length(k1),1); +% +% Throw an error if a prior has zero variance (degenerate priors will not work with MCMC) +% + +zero_std = prior.p2<eps(1); + +if any(zero_std) + for i=1:mm + dprintf('Prior std. on %s is zero.', prior.name{i}) + end + skipline() + derror('Prior:wrongDefinition', 'Check your priors. Degenerate priors are not allowed (increase the prior variance or calibrate the parameter).') +end + +% +% Set hyperparameters +% +% TODO: This should be done in the constructor of the dprior class. + +% [1] Beta distrubution +k = find(prior.pshape == 1); +% Set default boundaries +k1 = find(isnan(prior.p3(k))); +prior.p3(k(k1)) = zeros(length(k1),1); +k1 = find(isnan(prior.p4(k))); +prior.p4(k(k1)) = ones(length(k1),1); +% Compute the mode and set hyperparameters for i=1:length(k) - [bayestopt_.p6(k(i)), bayestopt_.p7(k(i))] = beta_specification(bayestopt_.p1(k(i)), bayestopt_.p2(k(i))^2, bayestopt_.p3(k(i)), bayestopt_.p4(k(i)), bayestopt_.name{k(i)}); - if bayestopt_.p6(k(i))<1 || bayestopt_.p7(k(i))<1 - fprintf('Prior distribution for parameter %s has unbounded density!\n',bayestopt_.name{k(i)}) + [prior.p6(k(i)), prior.p7(k(i))] = beta_specification(prior.p1(k(i)), prior.p2(k(i))^2, prior.p3(k(i)), prior.p4(k(i)), prior.name{k(i)}); + if prior.p6(k(i))<1 || prior.p7(k(i))<1 + dprintf('Prior distribution for parameter %s has unbounded density.', prior.name{k(i)}) end - m = compute_prior_mode([ bayestopt_.p6(k(i)) , bayestopt_.p7(k(i)) , bayestopt_.p3(k(i)) , bayestopt_.p4(k(i)) ],1); - if length(m)==1 - bayestopt_.p5(k(i)) = m; + m = compute_prior_mode([prior.p6(k(i)), prior.p7(k(i)), prior.p3(k(i)) , prior.p4(k(i))], 1); + if isscalar(m) + prior.p5(k(i)) = m; else - disp(['Prior distribution for parameter ' bayestopt_.name{k(i)} ' has two modes!']) - bayestopt_.p5(k(i)) = m(1); + dprintf('Prior distribution for parameter %s has two modes.', prior.name{k(i)}) + prior.p5(k(i)) = m(1); end end -% generalized location parameter by default for gamma distribution -k = find(bayestopt_.pshape == 2); -k1 = find(isnan(bayestopt_.p3(k))); -k2 = find(isnan(bayestopt_.p4(k))); -bayestopt_.p3(k(k1)) = zeros(length(k1),1); -bayestopt_.p4(k(k2)) = Inf(length(k2),1); +% [2] Gamma distribution +k = find(prior.pshape == 2); +% Set default boundaries +k1 = find(isnan(prior.p3(k))); +k2 = find(isnan(prior.p4(k))); +prior.p3(k(k1)) = zeros(length(k1), 1); +prior.p4(k(k2)) = Inf(length(k2), 1); +% Compute the mode and set hyperparameters for i=1:length(k) - [bayestopt_.p6(k(i)), bayestopt_.p7(k(i))] = gamma_specification(bayestopt_.p1(k(i)), bayestopt_.p2(k(i))^2, bayestopt_.p3(k(i)), bayestopt_.name{k(i)}); - if bayestopt_.p6(k(i))<1 - fprintf('Prior distribution for parameter %s has unbounded density!\n',bayestopt_.name{k(i)}) + [prior.p6(k(i)), prior.p7(k(i))] = gamma_specification(prior.p1(k(i)), prior.p2(k(i))^2, prior.p3(k(i)), prior.name{k(i)}); + if prior.p6(k(i))<1 + dprintf('Prior distribution for parameter %s has unbounded density.', prior.name{k(i)}) end - bayestopt_.p5(k(i)) = compute_prior_mode([ bayestopt_.p6(k(i)) , bayestopt_.p7(k(i)) , bayestopt_.p3(k(i)) ], 2) ; + prior.p5(k(i)) = compute_prior_mode([prior.p6(k(i)), prior.p7(k(i)), prior.p3(k(i))], 2); end -% truncation parameters by default for normal distribution -k = find(bayestopt_.pshape == 3); -k1 = find(isnan(bayestopt_.p3(k))); -k2 = find(isnan(bayestopt_.p4(k))); -bayestopt_.p3(k(k1)) = -Inf*ones(length(k1),1); -bayestopt_.p4(k(k2)) = Inf*ones(length(k2),1); -bayestopt_.p6(k) = bayestopt_.p1(k); -bayestopt_.p7(k) = bayestopt_.p2(k); -bayestopt_.p5(k) = bayestopt_.p1(k); +% [3] Gaussian distribution +k = find(prior.pshape == 3); +k1 = find(isnan(prior.p3(k))); +k2 = find(isnan(prior.p4(k))); +prior.p3(k(k1)) = -Inf*ones(length(k1),1); +prior.p4(k(k2)) = Inf*ones(length(k2),1); +prior.p6(k) = prior.p1(k); +prior.p7(k) = prior.p2(k); +prior.p5(k) = prior.p1(k); -% inverse gamma distribution (type 1) -k = find(bayestopt_.pshape == 4); -k1 = find(isnan(bayestopt_.p3(k))); -k2 = find(isnan(bayestopt_.p4(k))); -bayestopt_.p3(k(k1)) = zeros(length(k1),1); -bayestopt_.p4(k(k2)) = Inf(length(k2),1); +% [4] Inverse gamma distribution (type I) +k = find(prior.pshape == 4); +% Set default boundaries +k1 = find(isnan(prior.p3(k))); +k2 = find(isnan(prior.p4(k))); +prior.p3(k(k1)) = zeros(length(k1),1); +prior.p4(k(k2)) = Inf(length(k2),1); +% Compute the mode and set hyperparameters for i=1:length(k) - [bayestopt_.p6(k(i)),bayestopt_.p7(k(i))] = inverse_gamma_specification(bayestopt_.p1(k(i)), bayestopt_.p2(k(i))^2, bayestopt_.p3(k(i)), 1, false, bayestopt_.name{k(i)}); - bayestopt_.p5(k(i)) = compute_prior_mode([ bayestopt_.p6(k(i)) , bayestopt_.p7(k(i)) , bayestopt_.p3(k(i)) ], 4); + [prior.p6(k(i)), prior.p7(k(i))] = inverse_gamma_specification(prior.p1(k(i)), prior.p2(k(i))^2, prior.p3(k(i)), 1, false, prior.name{k(i)}); + prior.p5(k(i)) = compute_prior_mode([prior.p6(k(i)), prior.p7(k(i)), prior.p3(k(i))], 4); end -% uniform distribution -k = find(bayestopt_.pshape == 5); -problem_parameters=''; +% [5] Uniform distribution +k = find(prior.pshape == 5); +problem_prior_specification = false(length(k), 1); for i=1:length(k) - [bayestopt_.p1(k(i)),bayestopt_.p2(k(i)),bayestopt_.p6(k(i)),bayestopt_.p7(k(i)),error_indicator] = ... - uniform_specification(bayestopt_.p1(k(i)),bayestopt_.p2(k(i)),bayestopt_.p3(k(i)),bayestopt_.p4(k(i))); - if error_indicator - if isempty(problem_parameters) - problem_parameters=[bayestopt_.name{k(i)}]; - else - problem_parameters=[problem_parameters ', ' bayestopt_.name{k(i)}]; - end - end - bayestopt_.p3(k(i)) = bayestopt_.p6(k(i)) ; - bayestopt_.p4(k(i)) = bayestopt_.p7(k(i)) ; - bayestopt_.p5(k(i)) = NaN ; + [prior.p1(k(i)), prior.p2(k(i)), prior.p6(k(i)), prior.p7(k(i)), errorflag] = ... + uniform_specification(prior.p1(k(i)), prior.p2(k(i)), prior.p3(k(i)), prior.p4(k(i)), prior.name{k(i)}); + problem_prior_specification(i) = errorflag; + prior.p3(k(i)) = prior.p6(k(i)) ; + prior.p4(k(i)) = prior.p7(k(i)) ; + prior.p5(k(i)) = NaN; end -if ~isempty(problem_parameters) - error(['uniform_specification: You defined lower and upper bounds for parameter ', problem_parameters, '. In this case, you need to leave mean and standard deviation empty.']) +if any(problem_prior_specification) + names = prior.name{k(problem_prior_specification)}; + if isscalar(k) + errmsg = sprintf('uniform_specification: You defined lower and upper bounds for parameter %s. In this case, you need to leave mean and standard deviation empty.', names{1}) + else + errmsg = sprintf('uniform_specification: You defined lower and upper bounds for parameters %s. In this case, you need to leave mean and standard deviation empty.', strrep(strtrim(sprintf('%s ', names{:})), ' ', ', ')) + end + derror('Prior:wrongDefinition', errmsg) end -% inverse gamma distribution (type 2) -k = find(bayestopt_.pshape == 6); -k1 = find(isnan(bayestopt_.p3(k))); -k2 = find(isnan(bayestopt_.p4(k))); -bayestopt_.p3(k(k1)) = zeros(length(k1),1); -bayestopt_.p4(k(k2)) = Inf(length(k2),1); +% [6] Inverse gamma distribution (type II) +k = find(prior.pshape == 6); +k1 = find(isnan(prior.p3(k))); +k2 = find(isnan(prior.p4(k))); +prior.p3(k(k1)) = zeros(length(k1),1); +prior.p4(k(k2)) = Inf(length(k2),1); for i=1:length(k) - [bayestopt_.p6(k(i)),bayestopt_.p7(k(i))] = ... - inverse_gamma_specification(bayestopt_.p1(k(i)), bayestopt_.p2(k(i))^2, bayestopt_.p3(k(i)), 2, false, bayestopt_.name{k(i)}); - bayestopt_.p5(k(i)) = compute_prior_mode([ bayestopt_.p6(k(i)) , bayestopt_.p7(k(i)) , bayestopt_.p3(k(i)) ], 6) ; + [prior.p6(k(i)), prior.p7(k(i))] = ... + inverse_gamma_specification(prior.p1(k(i)), prior.p2(k(i))^2, prior.p3(k(i)), 2, false, prior.name{k(i)}); + prior.p5(k(i)) = compute_prior_mode([ prior.p6(k(i)) , prior.p7(k(i)) , prior.p3(k(i)) ], 6) ; end -% Weibull distribution -k = find(bayestopt_.pshape == 8); -k1 = find(isnan(bayestopt_.p3(k))); -k2 = find(isnan(bayestopt_.p4(k))); -bayestopt_.p3(k(k1)) = zeros(length(k1),1); -bayestopt_.p4(k(k2)) = Inf(length(k2),1); +% [8] Weibull distribution +k = find(prior.pshape == 8); +k1 = find(isnan(prior.p3(k))); +k2 = find(isnan(prior.p4(k))); +prior.p3(k(k1)) = zeros(length(k1),1); +prior.p4(k(k2)) = Inf(length(k2),1); for i=1:length(k) - [bayestopt_.p6(k(i)),bayestopt_.p7(k(i))] = weibull_specification(bayestopt_.p1(k(i)), bayestopt_.p2(k(i))^2, bayestopt_.p3(k(i)), bayestopt_.name{k(i)}); - bayestopt_.p5(k(i)) = compute_prior_mode([ bayestopt_.p6(k(i)) , bayestopt_.p7(k(i)) , bayestopt_.p3(k(i)) ], 8) ; + [prior.p6(k(i)),prior.p7(k(i))] = weibull_specification(prior.p1(k(i)), prior.p2(k(i))^2, prior.p3(k(i)), prior.name{k(i)}); + prior.p5(k(i)) = compute_prior_mode([ prior.p6(k(i)) , prior.p7(k(i)) , prior.p3(k(i)) ], 8) ; end -k = find(isnan(xparam1)); -if ~isempty(k) - xparam1(k) = bayestopt_.p1(k); -end +% +% Return dprior object +% -if options_.initialize_estimated_parameters_with_the_prior_mode - xparam1 = bayestopt_.p5; - k = find(isnan(xparam1));% Because the uniform density do not have a mode! - if ~isempty(k) - xparam1(k) = bayestopt_.p1(k); - end -end +Prior = dprior(prior, options_.prior_trunc, false); +% +% Save prior specification on disk +% +% TODO: Check if we really need this anymore. +return % I create subfolder M_.dname/prior if needed. -CheckPath('prior',M_.dname); +CheckPath('prior', M_.dname); % I save the prior definition if the prior has changed. priorfile = sprintf('%s/prior/definition.mat', M_.dname); @@ -306,12 +277,12 @@ if isfile(priorfile) prior_has_changed = true; end else - prior_has_changed = 1; + prior_has_changed = true; end if prior_has_changed - delete([M_.dname '/prior/definition.mat']); - save([M_.dname '/prior/definition.mat'],'bayestopt_'); + delete(priorfile); + save(priorfile, 'prior'); end else - save([M_.dname '/prior/definition.mat'],'bayestopt_'); + save(priorfile, 'prior'); end diff --git a/matlab/estimation/uniform_specification.m b/matlab/estimation/uniform_specification.m index 0689b3028d..038871deec 100644 --- a/matlab/estimation/uniform_specification.m +++ b/matlab/estimation/uniform_specification.m @@ -1,23 +1,25 @@ -function [m,s,p6,p7,error_indicator] = uniform_specification(m,s,p3,p4) +function [m, s, p6, p7, errorflag] = uniform_specification(m, s, p3, p4, name) + % Specification of the uniform density function parameters % % INPUTS -% m: mean -% s: standard deviation -% p3: lower bound -% p4: upper bound - +% - m [double] scalar, mean of the uniform random variable +% - s [double] scalar, standard deviation of the uniform random variable +% - p3 [double] scalar, lower bound of the unidorm random variable domain +% - p4 [double] scalar, upper bound of the unidorm random variable domain +% - name [char] 1×n array, name of the parameter +% % OUTPUTS -% m: mean -% s: standard deviation -% p6: lower bound -% p7: upper bound -% error_indicator: whether inconsistent prior specification was used +% - m [double] scalar, mean +% - s [double] scalar, standard deviation +% - p6 [double] scalar, lower bound +% - p7 [double] scalar, upper bound +% - errorflag [logical] scalar, true if inconsistent prior specification was used % % SPECIAL REQUIREMENTS -% none +% none -% Copyright © 2004-2017 Dynare Team +% Copyright © 2004-2024 Dynare Team % % This file is part of Dynare. % @@ -33,17 +35,22 @@ function [m,s,p6,p7,error_indicator] = uniform_specification(m,s,p3,p4) % % You should have received a copy of the GNU General Public License % along with Dynare. If not, see <https://www.gnu.org/licenses/>. -error_indicator=0; + +errorflag = false; + if ~(isnan(p3) || isnan(p4)) p6 = p3; p7 = p4; if ~isnan(m) || ~isnan(s) - error_indicator=1; + errorflag = true; end - m = (p3+p4)/2; - s = (p4-p3)/(sqrt(12)); + % m and s are overwritten if (wrongly) already defined. + m = (p3+p4)/2; + s = (p4-p3)/sqrt(12); else + if (isnan(m) || isnan(s)) + error('Cannot compute the hyperparameters of the uniform distribution (missing prior moments for %s)', name) + end p6 = m-s*sqrt(3); p7 = m+s*sqrt(3); - -end \ No newline at end of file +end diff --git a/matlab/isdebug.m b/matlab/isdebug.m new file mode 100644 index 0000000000..ac597c1f98 --- /dev/null +++ b/matlab/isdebug.m @@ -0,0 +1,39 @@ +function b = isdebug() + +% Return true iff we are in debug mode. +% +% INPUTS +% None +% +% OUTPUTS +% - b [logical] scalar. +% +% REMARKS +% - First argument is optional. + +% Copyright © 2024 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 <https://www.gnu.org/licenses/>. + + debug = getenv('DYNARE_DEBUG_MODE'); + + if isempty(debug) + b = false; + else + b = isequal(debug, 'true'); + end + +end diff --git a/matlab/stochastic_solver/select_qz_criterium_value.m b/matlab/stochastic_solver/select_qz_criterium_value.m index 36ce832da7..bb4d3bb48d 100644 --- a/matlab/stochastic_solver/select_qz_criterium_value.m +++ b/matlab/stochastic_solver/select_qz_criterium_value.m @@ -72,4 +72,4 @@ else end end end -end \ No newline at end of file +end diff --git a/matlab/utilities/estimation/check_mode_file.m b/matlab/utilities/estimation/check_mode_file.m index 040c74a80e..84704042b3 100644 --- a/matlab/utilities/estimation/check_mode_file.m +++ b/matlab/utilities/estimation/check_mode_file.m @@ -1,23 +1,19 @@ -function [xparam1, hh] = check_mode_file(xparam1, hh, options_, bayestopt_) -% function [xparam1, hh] = check_mode_file(xparam1, hh, options_, bayestopt_) -% ------------------------------------------------------------------------- +function [xparam1, hh] = check_mode_file(xparam1, hh, modecompute, modefile, enames) + % Check that the provided mode_file is compatible with the current estimation settings. -% ------------------------------------------------------------------------- +% % INPUTS -% o xparam1: [vector] current vector of parameter values at the mode -% o hh: [matrix] current hessian matrix at the mode -% o options_: [structure] information about options -% o bayestopt_: [structure] information about priors -% ------------------------------------------------------------------------- +% - xparam1 [double] current vector of parameter values at the mode +% - hh [double] current hessian matrix at the mode +% - modecompute [logical] information about options +% - modefile [char] row char array, name of the mode file +% - enames [cell] list of estimated parameters (row char arrays) +% % OUTPUTS -% o xparam1: [vector] updated vector of parameter values at the mode -% o hh: [matrix] updated hessian matrix at the mode -% ------------------------------------------------------------------------- -% This function is called by -% o dynare_estimation_init.m -% ------------------------------------------------------------------------- - -% Copyright © 2023 Dynare Team +% - xparam1: [vector] updated vector of parameter values at the mode +% - hh: [matrix] updated hessian matrix at the mode + +% Copyright © 2023-2024 Dynare Team % % This file is part of Dynare. % @@ -35,61 +31,83 @@ function [xparam1, hh] = check_mode_file(xparam1, hh, options_, bayestopt_) % along with Dynare. If not, see <https://www.gnu.org/licenses/>. number_of_estimated_parameters = length(xparam1); -mode_file = load(options_.mode_file); + +if ~isfile(modefile) + derror('Estimation:modeFileMissing', sprintf('Cannot find %s (mode file).', modefile)) +else + mode_file = load(modefile); +end + +if ~(issquare(hh) && rows(hh)==number_of_estimated_parameters && length(enames)==number_of_estimated_parameters) + derror('Estimation:WrongInputs', 'Wrong call to check_mode_file(). This is a bug.') +end + +if isfield(mode_file, 'parameter_names') && length(mode_file.parameter_names)~=length(mode_file.xparam1) + derror('Estimation:modeFileCorrupted', sprintf('The ''mode_file'' (%s) is not valid (number of elements in xparam1 must match the number of declared parameters)', modefile)) +end + +if isfield(mode_file, 'hh') && ~issquare(mode_file.hh) + derror('Estimation:modeFileCorrupted', sprintf('The ''mode_file'' (%s) is not valid (hh must be a square matrix)', modefile)) +end + +if isfield(mode_file, 'hh') && rows(mode_file.hh)~=length(mode_file.xparam1) + derror('Estimation:modeFileCorrupted', sprintf('The ''mode_file'' (%s) is not valid (number of elements in xparam1 must match the number of rows in hh)', modefile)) +end + if number_of_estimated_parameters>length(mode_file.xparam1) % More estimated parameters than parameters in the mode_file. skipline() - disp(['The ''mode_file'' ' options_.mode_file ' has been generated using another specification of the model or another model!']) - disp(['Your file contains estimates for ' int2str(length(mode_file.xparam1)) ' parameters, while you are attempting to estimate ' int2str(number_of_estimated_parameters) ' parameters:']) + dprintf('The ''mode_file'' %s has been generated using another specification of the model or another model.', modefile) + dprintf('Your file contains estimates for %u parameters, while you are attempting to estimate %u parameters:', length(mode_file.xparam1), number_of_estimated_parameters) md = []; xd = []; for i=1:number_of_estimated_parameters - id = strmatch(deblank(bayestopt_.name(i,:)),mode_file.parameter_names,'exact'); - if isempty(id) - disp(['--> Estimated parameter ' bayestopt_.name{i} ' is not present in the loaded ''mode_file'' (prior mean or initialized values will be used, if possible).']) + id = strcmp(enames{i}, mode_file.parameter_names); + if ~any(id) + dprintf('--> Estimated parameter %s is not present in the loaded ''mode_file'' (prior mean or initialized values will be used, if possible).', enames{i}) else xd = [xd; i]; - md = [md; id]; + md = [md; find(id)]; end end for i=1:length(mode_file.xparam1) - id = strmatch(mode_file.parameter_names{i},bayestopt_.name,'exact'); - if isempty(id) - disp(['--> Parameter ' mode_file.parameter_names{i} ' is not estimated according to the current mod file.']) + id = strcmp(mode_file.parameter_names{i}, enames); + if ~any(id) + dprintf('--> Parameter %s is not estimated according to the current mod file.', mode_file.parameter_names{i}) end end - if ~options_.mode_compute + if ~modecompute % The mode is not estimated. - error('Please change the ''mode_file'' option, the list of estimated parameters or set ''mode_compute''>0.') + derror('Estimation:modeFileIssue', 'Please change the ''mode_file'' option, the list of estimated parameters or set ''mode_compute''>0.') else % The mode is estimated, the Hessian evaluated at the mode is not needed so we set values for the parameters missing in the mode file using the prior mean or initialized values. if ~isempty(xd) xparam1(xd) = mode_file.xparam1(md); else - error('Please remove the ''mode_file'' option.') + derror('Estimation:modeFileIssue', 'Please remove the ''mode_file'' option.') end end elseif number_of_estimated_parameters<length(mode_file.xparam1) % Less estimated parameters than parameters in the mode_file. skipline() - disp(['The ''mode_file'' ' options_.mode_file ' has been generated using another specification of the model or another model!']) - disp(['Your file contains estimates for ' int2str(length(mode_file.xparam1)) ' parameters, while you are attempting to estimate only ' int2str(number_of_estimated_parameters) ' parameters:']) + dprintf('The ''mode_file'' %s has been generated using another specification of the model or another model.', modefile) + dprintf('Your file contains estimates for %u parameters, while you are attempting to estimate only %u parameters.', length(mode_file.xparam1), number_of_estimated_parameters) md = []; xd = []; for i=1:number_of_estimated_parameters - id = strmatch(deblank(bayestopt_.name(i,:)),mode_file.parameter_names,'exact'); - if isempty(id) - disp(['--> Estimated parameter ' deblank(bayestopt_.name(i,:)) ' is not present in the loaded ''mode_file'' (prior mean or initialized values will be used, if possible).']) + id = strcmp(enames{i}, mode_file.parameter_names); + if ~any(id) + dprintf('--> Estimated parameter %s is not present in the loaded ''mode_file'' (prior mean or initialized values will be used, if possible).', enames{i}) else xd = [xd; i]; - md = [md; id]; + md = [md; find(id)]; end end for i=1:length(mode_file.xparam1) - id = strmatch(mode_file.parameter_names{i},bayestopt_.name,'exact'); - if isempty(id) - disp(['--> Parameter ' mode_file.parameter_names{i} ' is not estimated according to the current mod file.']) + id = strcmp(mode_file.parameter_names{i}, enames); + if ~any(id) + dprintf('--> Parameter %s is not estimated according to the current mod file.', mode_file.parameter_names{i}) end end - if ~options_.mode_compute + if ~modecompute % The posterior mode is not estimated. If possible, fix the mode_file. if isequal(length(xd),number_of_estimated_parameters) disp('==> Fix mode_file (remove unused parameters).') @@ -98,7 +116,7 @@ elseif number_of_estimated_parameters<length(mode_file.xparam1) hh = mode_file.hh(md,md); end else - error('Please change the ''mode_file'' option, the list of estimated parameters or set ''mode_compute''>0.') + derror('Estimation:modeFileIssue', 'Please change the ''mode_file'' option, the list of estimated parameters or set ''mode_compute''>0.') end else % The mode is estimated, the Hessian evaluated at the mode is not needed so we set values for the parameters missing in the mode_file using the prior mean or initialized values. @@ -106,46 +124,47 @@ elseif number_of_estimated_parameters<length(mode_file.xparam1) xparam1(xd) = mode_file.xparam1(md); else % None of the estimated parameters are present in the mode_file. - error('Please remove the ''mode_file'' option.') + derror('Estimation:modeFileIssue', 'Please remove the ''mode_file'' option.') end end else % The number of declared estimated parameters match the number of parameters in the mode file. % Check that the parameters in the mode file and according to the current mod file are identical. if ~isfield(mode_file,'parameter_names') - disp(['The ''mode_file'' ' options_.mode_file ' has been generated using an older version of Dynare. It cannot be verified if it matches the present model. Proceed at your own risk.']) - mode_file.parameter_names=deblank(bayestopt_.name); %set names + dprintf('The ''mode_file'' %s has been generated using an older version of Dynare. It cannot be verified if it matches the present model. Proceed at your own risk.', modefile) + mode_file.parameter_names = enames; %set names end - if isequal(mode_file.parameter_names, bayestopt_.name) + if isequal(mode_file.parameter_names, enames) xparam1 = mode_file.xparam1; if isfield(mode_file,'hh') hh = mode_file.hh; end + elseif isempty(setdiff(mode_file.parameter_names, enames)) + % Estimated parameters are the same but ordering is different + if isdebug() + dprintf('The parameters in the provided ''mode file'' (%s) are not in the same order as the declared estimated parameters.', modefile) + dprintf('Reorder parameters from %s are reordered...', modefile) + end + [~, id] = ismember(mode_file.parameter_names, enames); + xparam1 = mode_file.xparam1(id); + hh = mode_file.hh(id,id); else + % Parameters are not the same skipline() - disp(['The ''mode_file'' ' options_.mode_file ' has been generated using another specification of the model or another model!']) + dprintf('The ''mode_file'' %s has been generated using another specification of the model or another model.', modefile) % Check if this is only an ordering issue or if the missing parameters can be initialized with the prior mean. md = []; xd = []; for i=1:number_of_estimated_parameters - id = strmatch(deblank(bayestopt_.name(i,:)), mode_file.parameter_names,'exact'); - if isempty(id) - disp(['--> Estimated parameter ' bayestopt_.name{i} ' is not present in the loaded ''mode_file''.']) + id = strcmp(enames{i}, mode_file.parameter_names); + if ~any(id) + dprintf('--> Estimated parameter %s is not present in the loaded ''mode_file''.', enames{i}) else xd = [xd; i]; - md = [md; id]; + md = [md; find(id)]; end end - if ~options_.mode_compute - % The mode is not estimated - if isequal(length(xd), number_of_estimated_parameters) - % This is an ordering issue. - xparam1 = mode_file.xparam1(md); - if isfield(mode_file,'hh') - hh = mode_file.hh(md,md); - end - else - error('Please change the ''mode_file'' option, the list of estimated parameters or set ''mode_compute''>0.') - end + if ~modecompute + derror('Estimation:modeFileIssue', 'Please change the ''mode_file'' option, the list of estimated parameters or set ''mode_compute''>0.') else % The mode is estimated, the Hessian evaluated at the mode is not needed so we set values for the parameters missing in the mode_file using the prior mean or initialized values. if ~isempty(xd) @@ -155,9 +174,339 @@ else end else % None of the estimated parameters are present in the mode_file. - error('Please remove the ''mode_file'' option.') + derror('Estimation:modeFileIssue', 'Please remove the ''mode_file'' option.') end end end end -skipline() \ No newline at end of file +skipline() + +return % --*-- Unit tests --*-- + +%@test:1 +xparam1 = ones(3,1); +hh = randn(3,3); +parameter_names = {'a2'; 'a3'}; +save('modefile1.mat', 'xparam1', 'hh', 'parameter_names') +clear('xparam1', 'hh', 'parameter_names') + +try + [xparam1, hh] = check_mode_file(rand(3, 1), randn(3, 3), false, 'modefile1.mat', {'a1'; 'a2'; 'a3'}); + t(1) = false; +catch ME + t(1) = true; +end + +if t(1) + t(2) = strcmp(ME.identifier, 'Estimation:modeFileCorrupted'); + delete modefile1.mat +end + +T = all(t); +%@eof:1 + +%@test:2 +xparam1 = ones(3,1); +hh = randn(3,3); +parameter_names = {'a1'; 'a2'; 'a3'}; +save('modefile1.mat', 'xparam1', 'hh', 'parameter_names') +clear('xparam1', 'hh', 'parameter_names') + +try + [xparam1, hh] = check_mode_file(rand(3, 1), randn(3, 3), false, 'modefile2.mat', {'a1'; 'a2'; 'a3'}); + t(1) = false; +catch ME + t(1) = true; +end + +if t(1) + t(2) = strcmp(ME.identifier, 'Estimation:modeFileMissing'); + delete modefile1.mat +end + +T = all(t); +%@eof:2 + +%@test:3 +xparam1 = ones(3,1); +hh = randn(2,3); +parameter_names = {'a1'; 'a2'; 'a3'}; +save('modefile1.mat', 'xparam1', 'hh', 'parameter_names') +clear('xparam1', 'hh', 'parameter_names') + +try + [xparam1, hh] = check_mode_file(rand(3, 1), randn(3, 3), false, 'modefile1.mat', {'a1'; 'a2'; 'a3'}); + t(1) = false; +catch ME + t(1) = true; +end + +if t(1) + t(2) = strcmp(ME.identifier, 'Estimation:modeFileCorrupted'); + delete modefile1.mat +end + +T = all(t); +%@eof:3 + +%@test:4 +xparam1 = ones(3,1); +hh = randn(2,2); +parameter_names = {'a1'; 'a2'; 'a3'}; +save('modefile1.mat', 'xparam1', 'hh', 'parameter_names') +clear('xparam1', 'hh', 'parameter_names') + +try + [xparam1, hh] = check_mode_file(rand(3, 1), randn(3, 3), false, 'modefile1.mat', {'a1'; 'a2'; 'a3'}); + t(1) = false; +catch ME + t(1) = true; +end + +if t(1) + t(2) = strcmp(ME.identifier, 'Estimation:modeFileCorrupted'); + delete modefile1.mat +end + +T = all(t); +%@eof:4 + + +%@test:5 +xparam1 = ones(3,1); +hh = randn(3,3); +parameter_names = {'a1'; 'a2'; 'a3'}; +save('modefile1.mat', 'xparam1', 'hh', 'parameter_names') +clear('xparam1', 'hh', 'parameter_names') + +xparam1 = 2*ones(4,1); +hh = 2*ones(4,4); + +try + [xparam1, hh] = check_mode_file(xparam1, hh, false, 'modefile1.mat', {'a1'; 'a0'; 'a2'; 'a3'}); + t(1) = false; +catch ME + t(1) = true; +end + +if t(1) + t(2) = strcmp(ME.identifier, 'Estimation:modeFileIssue'); + delete modefile1.mat +end + +T = all(t); +%@eof:5 + + +%@test:6 +xparam1 = ones(3,1); +hh = randn(3,3); +parameter_names = {'a1'; 'a2'; 'a3'}; +save('modefile1.mat', 'xparam1', 'hh', 'parameter_names') +clear('xparam1', 'hh', 'parameter_names') + +xparam1 = 2*ones(4,1); +hh = 2*ones(4,4); + +try + [xparam1, hh] = check_mode_file(xparam1, hh, true, 'modefile1.mat', {'a1'; 'a0'; 'a2'; 'a3'}); + t(1) = true; +catch ME + t(1) = false; +end + +if t(1) + t(2) = isequal(xparam1, [1; 2; 1; 1]); + t(3) = isequal(hh, 2*ones(4, 4)); + delete modefile1.mat +end + +T = all(t); +%@eof:6 + + +%@test:7 +xparam1 = ones(3,1); +hh = randn(3,3); +parameter_names = {'a1'; 'a2'; 'b3'}; +save('modefile1.mat', 'xparam1', 'hh', 'parameter_names') +clear('xparam1', 'hh', 'parameter_names') + +xparam1 = 2*ones(4,1); +hh = 2*ones(4,4); + +try + [xparam1, hh] = check_mode_file(xparam1, hh, true, 'modefile1.mat', {'a1'; 'a0'; 'a2'; 'a3'}); + t(1) = true; +catch ME + t(1) = false; +end + +if t(1) + t(2) = isequal(xparam1, [1; 2; 1; 2]); + t(3) = isequal(hh, 2*ones(4, 4)); + delete modefile1.mat +end + +T = all(t); +%@eof:7 + + +%@test:8 +xparam1 = [1; 2; 3]; +hh = [1 2 3; 1 2 3; 1 2 3]; +parameter_names = {'a1'; 'a2'; 'b3'}; +save('modefile1.mat', 'xparam1', 'hh', 'parameter_names') +clear('xparam1', 'hh', 'parameter_names') + +xparam1 = [-1; -2]; +hh = 2*ones(2, 2); + +try + [xparam1, hh] = check_mode_file(xparam1, hh, true, 'modefile1.mat', {'a1'; 'a2'}); + t(1) = true; +catch ME + t(1) = false; +end + +if t(1) + t(2) = isequal(xparam1, [1; 2]); + t(3) = isequal(hh, 2*ones(2,2)); + delete modefile1.mat +end + +T = all(t); +%@eof:8 + + +%@test:9 +xparam1 = [1; 2; 3]; +hh = [1 2 3; 1 2 3; 1 2 3]; +parameter_names = {'a1'; 'a2'; 'b3'}; +save('modefile1.mat', 'xparam1', 'hh', 'parameter_names') +clear('xparam1', 'hh', 'parameter_names') + +xparam1 = [-1; -2]; +hh = 2*ones(2, 2); + +try + [xparam1, hh] = check_mode_file(xparam1, hh, true, 'modefile1.mat', {'a2'; 'a1'}); + t(1) = true; +catch ME + t(1) = false; +end + +if t(1) + t(2) = isequal(xparam1, [2; 1]); + t(3) = isequal(hh, 2*ones(2,2)); + delete modefile1.mat +end + +T = all(t); +%@eof:9 + + +%@test:10 +xparam1 = [1; 2; 3]; +hh = [1 2 3; 1 2 3; 1 2 3]; +parameter_names = {'a1'; 'a2'; 'b3'}; +save('modefile1.mat', 'xparam1', 'hh', 'parameter_names') +clear('xparam1', 'hh', 'parameter_names') + +xparam1 = [-1; -2]; +hh = 2*ones(2, 2); + +try + [xparam1, hh] = check_mode_file(xparam1, hh, true, 'modefile1.mat', {'a2'; 'b1'}); + t(1) = true; +catch ME + t(1) = false; +end + +if t(1) + t(2) = isequal(xparam1, [2; -2]); + t(3) = isequal(hh, 2*ones(2,2)); + delete modefile1.mat +end + +T = all(t); +%@eof:10 + + +%@test:11 +xparam1 = [1; 2; 3]; +hh = [1 2 3; 1 2 3; 1 2 3]; +parameter_names = {'a1'; 'a2'; 'b3'}; +save('modefile1.mat', 'xparam1', 'hh', 'parameter_names') +clear('xparam1', 'hh', 'parameter_names') + +xparam1 = [-1; -2]; +hh = 2*ones(2, 2); + +try + [xparam1, hh] = check_mode_file(xparam1, hh, false, 'modefile1.mat', {'a2'; 'b1'}); + t(1) = false; +catch ME + t(1) = true; +end + +if t(1) + t(2) = strcmp(ME.identifier, 'Estimation:modeFileIssue'); + delete modefile1.mat +end + +T = all(t); +%@eof:11 + + +%@test:12 +xparam1 = [1; 2; 3]; +hh = [1 2 3; 1 2 3; 1 2 3]; +parameter_names = {'a1'; 'a2'; 'a3'}; +save('modefile1.mat', 'xparam1', 'hh', 'parameter_names') +clear('xparam1', 'hh', 'parameter_names') + +xparam1 = [-1; -2; -3]; +hh = 2*ones(3, 3); + +try + [xparam1, hh] = check_mode_file(xparam1, hh, true, 'modefile1.mat', {'a3'; 'a2'; 'a1'}); + t(1) = true; +catch ME + t(1) = false; +end + +if t(1) + t(2) = isequal(xparam1, [3; 2; 1]); + t(3) = isequal(hh, [3 2 1; 3 2 1; 3 2 1]); + delete modefile1.mat +end + +T = all(t); +%@eof:12 + +%@test:13 +xparam1 = [1; 2; 3]; +hh = [1 2 3; 1 2 3; 1 2 3]; +parameter_names = {'a1'; 'a2'; 'a3'}; +save('modefile1.mat', 'xparam1', 'hh', 'parameter_names') +clear('xparam1', 'hh', 'parameter_names') + +xparam1 = [-1; -2; -3]; +hh = 2*ones(3, 3); + +try + [xparam1, hh] = check_mode_file(xparam1, hh, true, 'modefile1.mat', {'a3'; 'a2'; 'b1'}); + t(1) = true; +catch ME + t(1) = false; +end + +if t(1) + t(2) = isequal(xparam1, [3; 2; -3]); + t(3) = isequal(hh, [3 2 2; 3 2 2; 2 2 2]); + delete modefile1.mat +end + +T = all(t); +%@eof:13 diff --git a/matlab/utilities/estimation/check_prior_stderr_corr.m b/matlab/utilities/estimation/check_prior_stderr_corr.m index c724390cf2..b5a7485cd0 100644 --- a/matlab/utilities/estimation/check_prior_stderr_corr.m +++ b/matlab/utilities/estimation/check_prior_stderr_corr.m @@ -27,21 +27,24 @@ function check_prior_stderr_corr(estim_params_, Prior) % along with Dynare. If not, see <https://www.gnu.org/licenses/>. nvx = estim_params_.nvx; % number of stderr parameters for structural shocks -if nvx && any(bayestopt_.p3(1:nvx)<0) +if nvx && any(Prior.p3(1:nvx)<0) warning('Your prior allows for negative standard deviations for structural shocks. Due to working with variances, Dynare will be able to continue, but it is recommended to change your prior.') end offset = nvx; + nvn = estim_params_.nvn; % number of stderr parameters for measurement errors -if nvn && any(bayestopt_.p3(1+offset:offset+nvn)<0) +if nvn && any(Prior.p3(1+offset:offset+nvn)<0) warning('Your prior allows for negative standard deviations for measurement error. Due to working with variances, Dynare will be able to continue, but it is recommended to change your prior.') end offset = nvx+nvn; + ncx = estim_params_.ncx; % number of corr parameters for structural shocks -if ncx && (any(bayestopt_.p3(1+offset:offset+ncx)<-1) || any(bayestopt_.p4(1+offset:offset+ncx)>1)) +if ncx && (any(Prior.p3(1+offset:offset+ncx)<-1) || any(Prior.p4(1+offset:offset+ncx)>1)) warning('Your prior allows for correlations between structural shocks larger than +-1 and will not integrate to 1 due to truncation. Please change your prior') end offset = nvx+nvn+ncx; + ncn = estim_params_.ncn; % number of corr parameters for measurement errors -if ncn && (any(bayestopt_.p3(1+offset:offset+ncn)<-1) || any(bayestopt_.p4(1+offset:offset+ncn)>1)) +if ncn && (any(Prior.p3(1+offset:offset+ncn)<-1) || any(Prior.p4(1+offset:offset+ncn)>1)) warning('Your prior allows for correlations between measurement errors larger than +-1 and will not integrate to 1 due to truncation. Please change your prior') -end \ No newline at end of file +end diff --git a/matlab/utilities/estimation/check_varobs_are_endo_and_declared_once.m b/matlab/utilities/estimation/check_varobs_are_endo_and_declared_once.m index 1cb92635da..b9b0e2ceac 100644 --- a/matlab/utilities/estimation/check_varobs_are_endo_and_declared_once.m +++ b/matlab/utilities/estimation/check_varobs_are_endo_and_declared_once.m @@ -16,7 +16,7 @@ function check_varobs_are_endo_and_declared_once(varobs,endo_names) % o dynare_estimation_init.m % ------------------------------------------------------------------------- -% Copyright © 2023 Dynare Team +% Copyright © 2023-2024 Dynare Team % % This file is part of Dynare. % @@ -33,18 +33,20 @@ function check_varobs_are_endo_and_declared_once(varobs,endo_names) % You should have received a copy of the GNU General Public License % along with Dynare. If not, see <https://www.gnu.org/licenses/>. -number_of_observed_variables = length(varobs); -for i = 1:number_of_observed_variables - if ~any(strcmp(varobs{i},endo_names)) - error(['VAROBS: unknown variable (' varobs{i} ')!']) - end +list_of_missing_endogenous_variables = setdiff(varobs, endo_names); + +if ~isempty(list_of_missing_endogenous_variables) + throwAsCaller(MException('Estimation:varobsError', sprintf('Observed variables have to be declared as endogenous variables: %s.', strrep(strtrim(sprintf('%s ', list_of_missing_endogenous_variables{:})), ' ', ', ')))); end + + % Check that a variable is not declared as observed more than once. +number_of_observed_variables = length(varobs); if length(unique(varobs))<length(varobs) for i = 1:number_of_observed_variables - if sum(strcmp(varobs{i},varobs)) > 1 + if sum(strcmp(varobs{i},varobs)) > 1 error(['VAROBS: a variable cannot be declared as observed more than once (' varobs{i} ')!']) end end -end \ No newline at end of file +end diff --git a/tests/kalman_filter_fast/swk_0.mod b/tests/kalman_filter_fast/swk_0.mod index e1f1c35419..700cf83124 100644 --- a/tests/kalman_filter_fast/swk_0.mod +++ b/tests/kalman_filter_fast/swk_0.mod @@ -133,7 +133,7 @@ options_.riccati_tol = 0; // actual computation for timing tic; for i=1:100; - fval = dsge_likelihood(xparam1,dataset_,options_,M_,estim_params_,bayestopt_,oo_,[]); + fval = dsge_likelihood(xparam1,dataset_,options_,M_,Prior,estim_params_,bayestopt_,oo_,[]); end; toc; -- GitLab