diff --git a/matlab/backward/backward_model_forecast.m b/matlab/backward/backward_model_forecast.m index d3c7c6bb5..4b12b5ba2 100644 --- a/matlab/backward/backward_model_forecast.m +++ b/matlab/backward/backward_model_forecast.m @@ -55,6 +55,13 @@ end start = initialcondition.dates(end)+1; +% Set default initial conditions for the innovations. +for i=1:M_.exo_nbr + if ~ismember(M_.exo_names{i}, initialcondition.name) + initialcondition{M_.exo_names{i}} = dseries(zeros(initialcondition.nobs, 1), initialcondition.dates(1), M_.exo_names{i}); + end +end + % Set up initial conditions [initialcondition, periods, innovations, DynareOptions, DynareModel, DynareOutput, endonames, exonames, nx, ny1, iy1, jdx, model_dynamic, y] = ... simul_backward_model_init(initialcondition, periods, options_, M_, oo_, zeros(periods, M_.exo_nbr)); diff --git a/matlab/backward/backward_model_inversion.m b/matlab/backward/backward_model_inversion.m index b9ce336f1..1af466be8 100644 --- a/matlab/backward/backward_model_inversion.m +++ b/matlab/backward/backward_model_inversion.m @@ -88,7 +88,7 @@ model_dtransf = str2func('dynamic_backward_model_for_inversion'); % Initialization of the returned simulations (endogenous variables). Y = NaN(DynareModel.endo_nbr, nobs(constraints)); -Y = [transpose(initialconditions(constraints.dates(1)-1).data(1:DynareModel.endo_nbr)), Y]; +Y = [transpose(initialconditions{DynareModel.endo_names{:}}(constraints.dates(1)-1).data), Y]; for i=1:nyctrl Y(controlledendogenousvariables_id(i),2:end) = transpose(constraints.data(:,i)); end diff --git a/matlab/backward/backward_model_irf.m b/matlab/backward/backward_model_irf.m index 19b552aea..d4cba386d 100644 --- a/matlab/backward/backward_model_irf.m +++ b/matlab/backward/backward_model_irf.m @@ -40,13 +40,12 @@ global M_ options_ oo_ % Check that the model is actually backward if M_.maximum_lead - error(['simul_model_irf:: The specified model is not backward looking!']) + error('backward_model_irf:: The specified model is not backward looking!') end % Set default value for the fourth input argument. if nargin<5 periods = 40; - notransform = true; else periods = varargin{1}; end @@ -89,7 +88,13 @@ else end end -baselineflag = false; +% Set default initial conditions for the innovations. +for i=1:M_.exo_nbr + if ~ismember(M_.exo_names{i}, initialcondition.name) + initialcondition{M_.exo_names{i}} = dseries(zeros(initialcondition.nobs, 1), initialcondition.dates(1), M_.exo_names{i}); + end +end + % Set default values for the baseline paths. % % TODO zero for all variables is probably a poor choice. It should be @@ -114,7 +119,6 @@ if ~isempty(innovationbaseline) Innovations(:,i) = innovationbaseline{exonames{i}}.data(1:periods); end end - baselineflag = true; end % Set up initial conditions @@ -159,7 +163,7 @@ for i=1:length(listofshocks) shock = listofshocks{i}; timid = shock.dates-initialconditionperiod; for j=1:shock.vobs - k = find(strcmp(shock.name{i}, exonames)); + k = find(strcmp(shock.name{j}, exonames)); for l=1:length(timid) innovations(timid(l),k) = innovations(timid(l),k) + shock.data(l,j); end diff --git a/matlab/backward/calibrateresiduals.m b/matlab/backward/calibrateresiduals.m new file mode 100644 index 000000000..e69148418 --- /dev/null +++ b/matlab/backward/calibrateresiduals.m @@ -0,0 +1,125 @@ +function [residuals, info] = calibrateresiduals(dbase, info, DynareModel) + +% Compute residuals in a backward model. Residuals are unobserved exogenous +% variables appearing additively in equations and without lags. An equation +% cannot have more than one residual, and a residual cannot appear in more +% than one equation. +% +% INPUTS +% - dbase [dseries] Object containing all the endogenous and observed exogenous variables. +% - info [struct] Informations about the residuals. +% - DynareModel [struct] M_ as produced by the preprocessor. +% +% OUTPUTS +% - residuals [dseries] Object containing the identified residuals. +% - info [struct] Informations about the residuals. +% +% REMARKS +% The first two input arguments are the output of checkdatabaseforinversion +% routine. + +% Copyright (C) 2017 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 . + +% Get function handle for the dynamic model +model_dynamic = str2func([DynareModel.fname,'_dynamic']); + +% Get data for all the endogenous variables. +ydata = dbase{info.endonames{:}}.data; + +% Get data for all the exogenous variables. Missing exogenous variables, to be solved for, have NaN values. +exogenousvariablesindbase = intersect(info.exonames, dbase.name); +residuals = dseries(NaN(dbase.nobs, length(info.residuals)), dbase.init, info.residuals); +allexogenousvariables = [dbase{exogenousvariablesindbase{:}}, residuals]; +allexogenousvariables = allexogenousvariables{info.exonames{:}}; +xdata = allexogenousvariables.data; + +% Evaluate the dynamic equation +n = size(ydata, 2); +c = find(DynareModel.lead_lag_incidence'); +y = [ydata(1,:)'; ydata(2,:)']; +y = y(c); +r = model_dynamic(y, xdata, DynareModel.params, zeros(n, 1), 2); + +% Check that the number of equations evaluating to NaN matches the number of residuals +idr = find(isnan(r)); +if ~isequal(length(idr), residuals.vobs) + error('Each residual should appear in only one equation, and an equation cannot have more than one residual!') +end + +% Check that the non NaN equations have zero residuals (model and data consistency). +ido = setdiff(1:n, idr); +if ~isempty(find(abs(r(ido))>1e-6)) + disp('Provided data and model are not consistent in equations:') + idx = find(abs(r)>1e-6); + c1 = 'Equation'; + c1 = strvcat(c1, '--------'); + for i = 1:length(idx) + c1 = strvcat(c1, sprintf(' %s', num2str(idx(i)))); + end + c2 = 'Residual'; + c2 = strvcat(c2, '--------'); + for i = 1:length(idx) + c2 = strvcat(c2, sprintf('%s', num2str(r(idx(i))))); + end + c2 = strvcat(c2(1, :), repmat('-', 1, size(c2, 2)), c2(3:end,:)); + c3 = repmat(' | ', size(c2, 1), 1); + c4 = repmat(' ', size(c2, 1), 1); + cc = [c4, c1, c3, c2]; + skipline() + disp(cc) + skipline() + disp('Please check model and dataset.') +end + +% Associate the residuals with equations equations evaluating to NaNs. +info.equations = cell(residuals.vobs, 1); +info.residualindex = cell(residuals.vobs, 1); +for i = 1:residuals.vobs + residualname = residuals.name{i}; + info.residualindex(i) = {strmatch(residualname, allexogenousvariables.name, 'exact')}; + tmpxdata = xdata; + tmpxdata(2, info.residualindex{i}) = 0; + r = model_dynamic(y, tmpxdata, DynareModel.params, zeros(n, 1), 2); + info.equations(i) = { idr(find(~isnan(r(idr))))}; +end +c1 = 'Residual'; +for i=1:length(info.residuals) + c1 = strvcat(c1, sprintf('%s', info.residuals{i})); +end +c1 = strvcat(c1(1,:), repmat('-', 1, size(c1, 2)), c1(2:end,:)); +c2 = 'Equation'; +for i=1:length(info.residuals) + c2 = strvcat(c2, sprintf(' %s', num2str(info.equations{i}))); +end +c2 = strvcat(c2(1,:), repmat('-', 1, size(c2, 2)), c2(2:end,:)); +c3 = repmat(' | ', size(c2, 1), 1); +c4 = repmat(' ', size(c2, 1), 1); +cc = [c4, c1, c3, c2]; +skipline() +disp(cc) +skipline() + +% Compute residuals +xdata(:,cell2mat(info.residualindex)) = 0; +rdata = NaN(residuals.nobs, residuals.vobs); +for t=2:size(xdata, 1) + y = transpose([ydata(t-1,:); ydata(t,:)]); + r = model_dynamic(y(c), xdata, DynareModel.params, zeros(n, 1), t); + rdata(t,:) = transpose(r(cell2mat(info.equations))); +end +residuals = dseries(rdata, dbase.init, info.residuals); \ No newline at end of file diff --git a/matlab/backward/checkdatabase.m b/matlab/backward/checkdatabase.m new file mode 100644 index 000000000..e66d7fe2c --- /dev/null +++ b/matlab/backward/checkdatabase.m @@ -0,0 +1,98 @@ +function [dbase, info] = checkdatabase(dbase, DynareModel, inversionflag) + +% Check that dbase contains all the endogenous variables of the model, and +% reorder the endogenous variables as declared in the mod file. If Dynare +% adds auxiliary variables, for lags greater than 1 on endogenous variables, +% endogenous variables in difference (which may be lagged), or lags on the +% exogenous variables, then thee routine complete the database. + +% Copyright (C) 2018 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 . + +if DynareModel.maximum_endo_lead + error('The model (%s) is assumed to be backward!', DynareModel.fname) +end + +if nargin<3 + inversionflag = false; +end + +listoflaggedexogenousvariables = {}; + +info = struct; + +k = 0; +for i = DynareModel.orig_endo_nbr+1:DynareModel.endo_nbr + k = k+1; + if DynareModel.aux_vars(k).type==1 + if ismember(DynareModel.endo_names{DynareModel.aux_vars(k).orig_index}, dbase.name) + dbase{DynareModel.endo_names{DynareModel.aux_vars(k).endo_index}} = dbase{DynareModel.endo_names{DynareModel.aux_vars(k).orig_index}}.lag(abs(DynareModel.aux_vars(k).orig_lead_lag)); + else + error('%s not available in dbase!', DynareModel.endo_names{DynareModel.aux_vars(k).orig_index}); + end + elseif DynareModel.aux_vars(k).type==3 + dbase{DynareModel.endo_names{DynareModel.aux_vars(k).endo_index}} = dbase{DynareModel.exo_names{DynareModel.aux_vars(k).orig_index}}.lag(abs(DynareModel.aux_vars(k).orig_lead_lag)); + listoflaggedexogenousvariables = vertcat(listoflaggedexogenousvariables, DynareModel.exo_names{DynareModel.aux_vars(k).orig_index}); + elseif DynareModel.aux_vars(k).type==8 + dbase{DynareModel.endo_names{DynareModel.aux_vars(k).endo_index}} = dbase{DynareModel.exo_names{DynareModel.aux_vars(k).orig_index}}.diff.lag(abs(DynareModel.aux_vars(k).orig_lead_lag)); + else + warning('Please contact Dynare Team!') + end +end + +info.endonames = DynareModel.endo_names; +info.exonames = DynareModel.exo_names; +info.computeresiduals = false; + +% Check that all the endogenous variables are defined in dbase. +missingendogenousvariables = setdiff(info.endonames, dbase.name); +if ~isempty(missingendogenousvariables) + disp('Some endognous variables are missing:') + missingendogenousvariables + error() +end + +if inversionflag + % If some exogenous variables are missing, check that they can be + % interpreted as residuals. + missingexogenousvariables = setdiff(info.exonames, dbase.name); + if ~isempty(missingexogenousvariables) + disp(sprintf('%s exogenous variables are missing in the database...', num2str(length(missingexogenousvariables)))) + listofmissinglaggedexognousvariables = intersect(listoflaggedexogenousvariables, missingexogenousvariables); + if isempty(listofmissinglaggedexognousvariables) + info.residuals = missingexogenousvariables; + info.computeresiduals = true; + disp('These variables can be calibrated by calling calibrateresiduals routine.') + else + info.residuals = setdiff(missingexogenousvariables, listofmissinglaggedexognousvariables); + disp('The following exogenous variables:') + listofmissinglaggedexognousvariables + disp('are not residuals, and cannot be calibrated by calling calibrateresiduals.') + end + else + disp('All the endogenous and exogenous variables are calibrated!') + end +else + % Check that all the exogenous variables are defined in dbase + missingexogenousvariables = setdiff(info.exonames, dbase.name); + if ~isempty(missingexogenousvariables) + disp('Some exognous variables are missing:') + missingexogenousvariables + error() + end + info.residuals = []; +end \ No newline at end of file diff --git a/matlab/backward/checkdatabaseforinversion.m b/matlab/backward/checkdatabaseforinversion.m new file mode 100644 index 000000000..cee3e4732 --- /dev/null +++ b/matlab/backward/checkdatabaseforinversion.m @@ -0,0 +1,25 @@ +function [dbase, info] = checkdatabaseforinversion(dbase, DynareModel) + +% Check that dbase contains all the endogenous variables of the model, and +% reorder the endogenous variables as declared in the mod file. If Dynare +% adds auxiliary variables, for lags greater than 1 on endogebnous variables +% or lags on the exogenous variables. + +% Copyright (C) 2017-2018 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 . + +[dbase, info] = checkdatabase(dbase, DynareModel, true); \ No newline at end of file diff --git a/matlab/backward/simul_backward_model_init.m b/matlab/backward/simul_backward_model_init.m index d284438be..4afc61ac1 100644 --- a/matlab/backward/simul_backward_model_init.m +++ b/matlab/backward/simul_backward_model_init.m @@ -18,13 +18,8 @@ function [initialconditions, samplesize, innovations, DynareOptions, DynareModel % % You should have received a copy of the GNU General Public License % along with Dynare. If not, see . - + initialconditions = varargin{1}; - -if ~isdseries(initialconditions) - error('First input argument must be a dseries object') -end - samplesize = varargin{2}; DynareOptions = varargin{3}; DynareModel = varargin{4}; @@ -36,10 +31,39 @@ if DynareModel.maximum_lead end % Test if the first argument is a dseries object. -if ~isdseries(initialconditions) - error('First input argument must be a dseries object!') +if ~(isdseries(initialconditions) || isempty(initialconditions)) + error('First input argument must be a dseries object or an empty array!') end +% If initialconditions is empty instantiates a dseries object with the informations available in DynareModel.endo_histval. +if isempty(initialconditions) + yinitdata = zeros(DynareModel.orig_endo_nbr, DynareModel.max_lag_orig); + yinitdata(:,1) = DynareModel.endo_histval(1:DynareModel.orig_endo_nbr); + xinitdata = zeros(DynareModel.exo_nbr, DynareModel.max_lag_orig); + if DynareModel.max_endo_lag_orig>1 + for i = 1:length(DynareModel.aux_vars) + if DynareModel.aux_vars(i).type==1 + yinitdata(DynareModel.aux_vars(i).orig_index, abs(DynareModel.aux_vars(i).orig_lead_lag)+1) = ... + DynareModel.endo_histval(DynareModel.orig_endo_nbr+i); + end + end + yinitdata = flip(yinitdata, 2); + end + if DynareModel.max_exo_lag_orig>0 + for i = 1:length(DynareModel.aux_vars) + if DynareModel.aux_vars(i).type==3 + xinitdata(DynareModel.aux_vars(i).orig_index, abs(DynareModel.aux_vars(i).orig_lead_lag)+1) = ... + DynareModel.endo_histval(DynareModel.orig_endo_nbr+i); + end + end + xinitdata = flip(xinitdata, 2); + end + initialconditions = dseries([transpose(yinitdata) transpose(xinitdata)], '1Y', ... + vertcat(DynareModel.endo_names(1:DynareModel.orig_endo_nbr), DynareModel.exo_names)); +end + +[initialconditions, info] = checkdatabase(initialconditions, DynareModel); + % Test if the first argument contains all the lagged endogenous variables endonames = DynareModel.endo_names; missingendogenousvariables = setdiff(endonames, initialconditions.name); @@ -144,11 +168,18 @@ for i = DynareModel.orig_endo_nbr+1:DynareModel.endo_nbr else error('This is a bug. Please contact Dynare Team!'); end + elseif DynareModel.aux_vars(k).type == 8 + if ismember(DynareModel.endo_names{DynareModel.aux_vars(k).orig_index}, initialconditions.name) + initialconditions{DynareModel.endo_names{DynareModel.aux_vars(k).endo_index}} = ... + initialconditions{DynareModel.endo_names{DynareModel.aux_vars(k).orig_index}}.diff.lag(abs(DynareModel.aux_vars(k).orig_lead_lag)); + else + error('This is a bug. Please contact Dynare Team!'); + end else error('Cannot simulate the model with this type of auxiliary variables!') end end - + if nargin<6 || isempty(varargin{6}) % Set the covariance matrix of the structural innovations. variances = diag(DynareModel.Sigma_e); diff --git a/matlab/model_inversion.m b/matlab/model_inversion.m index 17e8267dd..bfef2ac59 100644 --- a/matlab/model_inversion.m +++ b/matlab/model_inversion.m @@ -68,6 +68,31 @@ if exogenousvariables.vobs>constraints.vobs observed_exogenous_variables_flag = true; end +% Add auxiliary variables in initialconditions object. +for i=1:length(DynareModel.aux_vars) + if ~ismember(DynareModel.endo_names{DynareModel.aux_vars(i).endo_index}, initialconditions.name) + switch DynareModel.aux_vars(i).type + case 1 % lag on endogenous variable. + initialconditions{DynareModel.endo_names{DynareModel.aux_vars(i).endo_index}} = ... + initialconditions{DynareModel.endo_names{DynareModel.aux_vars(i).orig_index}}.lag(abs(DynareModel.aux_vars(i).orig_lead_lag)); + case 8 % diff on endogenous variable. + initialconditions{DynareModel.endo_names{DynareModel.aux_vars(i).endo_index}} = ... + initialconditions{DynareModel.endo_names{DynareModel.aux_vars(i).orig_index}}.diff.lag(abs(DynareModel.aux_vars(i).orig_lead_lag)); + case 3 % lag on exogenous variable. + initialconditions{DynareModel.endo_names{DynareModel.aux_vars(i).endo_index}} = ... + initialconditions{DynareModel.exo_names{DynareModel.aux_vars(i).orig_index}}.lag(abs(DynareModel.aux_vars(i).orig_lead_lag)); + case 0 % lead on endogenous variable. + initialconditions{DynareModel.endo_names{DynareModel.aux_vars(i).endo_index}} = ... + initialconditions{DynareModel.endo_names{DynareModel.aux_vars(i).orig_index}}.lead(abs(DynareModel.aux_vars(i).orig_lead_lag)); + case 2 % lead on exogenous variable. + initialconditions{DynareModel.endo_names{DynareModel.aux_vars(i).endo_index}} = ... + initialconditions{DynareModel.exo_names{DynareModel.aux_vars(i).orig_index}}.lead(abs(DynareModel.aux_vars(i).orig_lead_lag)); + otherwise + error("This is a bug! PLease report to Dynare developpers.") + end + end +end + % Get the list of endogenous and exogenous variables. endo_names = DynareModel.endo_names; exo_names = DynareModel.exo_names;