function iterative_ols(eqname, params, data, range) % Estimates the parameters of a PAC equation by Iterative Ordinary Least Squares. % % INPUTS % - eqname [string] Name of the pac equation. % - params [struct] Describes the parameters to be estimated. % - data [dseries] Database for the estimation % - range [dates] Range of dates for the estimation. % % OUTPUTS % - none % % REMARKS % [1] The estimation results are printed in the command line window, and the % parameters are updated accordingly in M_.params. % [2] The second input is a structure. Each fieldname corresponds to the % name of an estimated parameter, the value of the field is the initial % guess used for the estimation (by NLS). % [3] The third input is a dseries object which must at least contain all % the variables appearing in the estimated equation. The residual of the % equation must have NaN values in the object. % [4] It is assumed that the residual is additive. % Copyright © 2018-2021 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 . global M_ oo_ options_ [pacmodl, ~, rhs, ~, ~, ~, rname, ~, ~, ~, ~, ipnames_, params, data] = ... pac.estimate.init(M_, oo_, eqname, params, data, range); % Set initial condition. params0 = cell2mat(struct2cell(params)); % Set flag for models with non optimizing agents. is_non_optimizing_agents = isfield(M_.pac.(pacmodl), 'non_optimizing_behaviour'); % Set flag for models with exogenous variables (outside of non optimizing agents part) if isfield(M_.pac.(pacmodl), 'additive') is_exogenous_variables = length(M_.pac.(pacmodl).additive.vars)>1; else is_exogenous_variables = false; end % Set flag for models with exogenous variables (in the optimizing agents part) if isfield(M_.pac.(pacmodl), 'optim_additive') is_optim_exogenous_variables = length(M_.pac.(pacmodl).optim_additive.vars)>0; else is_optim_exogenous_variables = false; end if is_non_optimizing_agents non_optimizing_behaviour = M_.pac.(pacmodl).non_optimizing_behaviour; non_optimizing_behaviour_params = NaN(length(non_optimizing_behaviour.params), 1); noparams = isnan(non_optimizing_behaviour.params); if ~all(noparams) % Find estimated non optimizing behaviour parameters (if any). non_optimizing_behaviour_estimated_params = ismember(M_.param_names(non_optimizing_behaviour.params), fieldnames(params)); if any(non_optimizing_behaviour_estimated_params) error('The estimation of non optimizing behaviour parameters is not yet allowed.') else non_optimizing_behaviour_params(noparams) = 1.0; non_optimizing_behaviour_params(~noparams) = M_.params(non_optimizing_behaviour.params(~noparams)); end else non_optimizing_behaviour_params(noparams) = 1.0; end non_optimizing_behaviour_params = non_optimizing_behaviour_params.*transpose(non_optimizing_behaviour.scaling_factor); % Set flag for the estimation of the share of non optimizing agents. estimate_non_optimizing_agents_share = ismember(M_.param_names(M_.pac.(pacmodl).share_of_optimizing_agents_index), fieldnames(params)); if ~estimate_non_optimizing_agents_share share_of_optimizing_agents = M_.params(M_.pac.(pacmodl).share_of_optimizing_agents_index); if share_of_optimizing_agents>1 || share_of_optimizing_agents<0 error('The share of optimizing agents shoud be in (0,1).') end end share_of_optimizing_agents_index = M_.pac.(pacmodl).share_of_optimizing_agents_index; else share_of_optimizing_agents = 1.0; share_of_optimizing_agents_index = []; estimate_non_optimizing_agents_share = false; end if is_exogenous_variables additive = M_.pac.(pacmodl).additive; residual_id = find(strcmp(rname, M_.exo_names)); residual_jd = find(additive.vars==residual_id & ~additive.isendo); additive.params(residual_jd) = []; additive.vars(residual_jd) = []; additive.isendo(residual_jd) = []; additive.lags(residual_jd) = []; additive.scaling_factor(residual_jd) = []; additive.estimation = ismember(additive.params, ipnames_); else additive = struct('params', [], 'vars', [], 'isendo', [], 'lags', [], 'scaling_factor', [], 'estimation', []); end if is_optim_exogenous_variables optim_additive = M_.pac.(pacmodl).optim_additive; optim_additive.estimation = ismember(optim_additive.params, ipnames_); else optim_additive = struct('params', [], 'vars', [], 'isendo', [], 'lags', [], 'scaling_factor', [], 'estimation', []); end % Build PAC expectation matrix expression. dataForPACExpectation = dseries(); listofvariables = {}; isconstant = false; for i=1:length(M_.pac.(pacmodl).h_param_indices) match = regexp(rhs, sprintf('(?((\\w*)|\\w*\\(-1\\)))\\*%s', M_.param_names{M_.pac.(pacmodl).h_param_indices(i)}), 'names'); if isempty(match) match = regexp(rhs, sprintf('%s\\*(?((\\w*\\(-1\\))|(\\w*)))', M_.param_names{M_.pac.(pacmodl).h_param_indices(i)}), 'names'); end if ~isempty(match) if isempty(strfind(match.var, '(-1)')) listofvariables{end+1} = match.var; dataForPACExpectation = [dataForPACExpectation, data{listofvariables{i}}]; else listofvariables{end+1} = match.var(1:end-4); dataForPACExpectation = [dataForPACExpectation, data{match.var(1:end-4)}.lag(1)]; end else if strcmp(M_.param_names{M_.pac.(pacmodl).h_param_indices(i)}, sprintf('h_%s_constant', pacmodl)) isconstant = true; end end end dataPAC = dataForPACExpectation{listofvariables{:}}(range).data; if isconstant dataPAC = [ones(rows(dataPAC),1), dataPAC]; end % Build data for non optimizing behaviour if is_non_optimizing_agents dataForNonOptimizingBehaviour = dseries(); for i=1:length(non_optimizing_behaviour.vars) if non_optimizing_behaviour.isendo(i) variable = M_.endo_names{non_optimizing_behaviour.vars(i)}; else variable = M_.exo_names{non_optimizing_behaviour.vars(i)}; end if non_optimizing_behaviour.lags(i) dataForNonOptimizingBehaviour = [dataForNonOptimizingBehaviour, data{variable}.lag(non_optimizing_behaviour.lags(i))]; else dataForNonOptimizingBehaviour = [dataForNonOptimizingBehaviour, data{variable}]; end end else dataForNonOptimizingBehaviour = dseries(); end % Build data for exogenous variables (out of non optimizing behaviour term). if is_exogenous_variables listofvariables2 = {}; j = 0; dataForExogenousVariables = dseries(); % Estimated parameters dataForExogenousVariables_ = 0; % Calibrated parameters is_any_calibrated_parameter_x = false; is_any_estimated_parameter_x = false; for i=1:length(additive.vars) if additive.isendo(i) variable = M_.endo_names{additive.vars(i)}; else variable = M_.exo_names{additive.vars(i)}; end if additive.estimation(i) j = j+1; is_any_estimated_parameter_x = true; listofvariables2{j} = variable; dataForExogenousVariables = [dataForExogenousVariables, additive.scaling_factor(i)*data{variable}.lag(-additive.lags(i))]; else is_any_calibrated_parameter_x = true; tmp = data{variable}.lag(-additive.lags(i)).data; if ~isnan(additive.params(i)) tmp = M_.params(additive.params(i))*tmp; end tmp = additive.scaling_factor(i)*tmp; dataForExogenousVariables_ = dataForExogenousVariables_+tmp; end end if is_any_calibrated_parameter_x dataForExogenousVariables_ = dseries(dataForExogenousVariables_, data.dates(1), 'exogenous_variables_associated_with_calibrated_parameters'); end else listofvariables2 = {}; dataForExogenousVariables = dseries(); dataForExogenousVariables_ = dseries(); end % Build data for exogenous variables (in the optimizing behaviour term). if is_optim_exogenous_variables listofvariables4 = {}; j = 0; dataForOptimExogenousVariables = dseries(); % Estimated parameters dataForOptimExogenousVariables_ = 0; % Calibrated parameters is_any_calibrated_parameter_optim_x = false; is_any_estimated_parameter_optim_x = false; for i=1:length(optim_additive.vars) if optim_additive.isendo(i) variable = M_.endo_names{optim_additive.vars(i)}; else variable = M_.exo_names{optim_additive.vars(i)}; end if optim_additive.estimation(i) j = j+1; is_any_estimated_parameter_optim_x = true; listofvariables4{j} = variable; dataForOptimExogenousVariables = [dataForOptimExogenousVariables, optim_additive.scaling_factor(i)*data{variable}.lag(-optim_additive.lags(i))]; else is_any_calibrated_parameter_optim_x = true; tmp = data{variable}.lag(-optim_additive.lags(i)).data; if ~isnan(optim_additive.params(i)) tmp = M_.params(optim_additive.params(i))*tmp; end tmp = optim_additive.scaling_factor(i)*tmp; dataForOptimExogenousVariables_ = dataForOptimExogenousVariables_+tmp; end end if is_any_calibrated_parameter_optim_x dataForOptimExogenousVariables_ = dseries(dataForOptimExogenousVariables_, data.dates(1), 'exogenous_variables_associated_with_calibrated_parameters'); end else listofvariables4 = {}; dataForOptimExogenousVariables = dseries(); dataForOptimExogenousVariables_ = dseries(); end % Reorder ec.vars locally if necessary. Second variable must be the % endogenous variable, while the first must be the associated trend. if M_.pac.(pacmodl).ec.isendo(2) ecvars = M_.pac.(pacmodl).ec.vars; else ecvars = flip(M_.pac.(pacmodl).ec.vars); end %% Build matrix for EC and AR terms. DataForOLS = dseries(); % Error correction term is trend minus the level of the endogenous variable. DataForOLS{'ec-term'} = data{M_.endo_names{ecvars(1)}}.lag(1)-data{M_.endo_names{ecvars(2)}}.lag(1); listofvariables3 = {'ec-term'}; xparm = { M_.param_names(M_.pac.(pacmodl).ec.params(1))}; for i = 1:length(M_.pac.(pacmodl).ar.params) if islagof(M_.pac.(pacmodl).ar.vars(i), M_.pac.(pacmodl).lhs_var) DataForOLS = [DataForOLS, data{M_.endo_names{M_.pac.(pacmodl).ar.vars(i)}}]; listofvariables3{i+1} = M_.endo_names{M_.pac.(pacmodl).ar.vars(i)}; xparm{i+1} = M_.param_names(M_.pac.(pacmodl).ar.params(i)); end end XDATA = DataForOLS{listofvariables3{:}}(range).data; if is_optim_exogenous_variables && is_any_estimated_parameter_optim_x XDATA = [XDATA, dataForOptimExogenousVariables{listofvariables4{:}}(range).data]; end if is_exogenous_variables && is_any_estimated_parameter_x XDATA = [XDATA, dataForExogenousVariables{listofvariables2{:}}(range).data]; end % Get index in params0 for share of optimizing agents parameter (if % not estimated, params_id_0 is empty). if is_non_optimizing_agents params_id_0 = find(ipnames_==share_of_optimizing_agents_index); else params_id_0 = []; end % Get indices in params0 for EC and AR parameters [~, params_id_1] = setdiff(ipnames_, [share_of_optimizing_agents_index, optim_additive.params, additive.params, ]); % Get indices in params0 for EC and AR parameters plus parameters related to exogenous variables in the optimal part. [~, params_id_5] = setdiff(ipnames_, [share_of_optimizing_agents_index, additive.params]); % Get indices in params0 for other parameters (optimizing agents share plus parameters related to exogenous variables). [~, params_id_2] = setdiff(1:length(ipnames_), params_id_1); % Get indices in params0 for the parameters associated to the exogenous variables. params_id_3 = setdiff(params_id_2, params_id_0); [~, params_id_3_o] = ismember(optim_additive.params, ipnames_); params_id_3_no = setdiff(params_id_3, params_id_3_o); % Get indices in params0 for EC/AR parameters and parameters associated to the exogenous variables (if any). params_id_4 = [params_id_1; params_id_3]; % Get values for EC-AR parameters plus the parameters associated to the exogenous variables (if any). params0_ = params0([params_id_1; params_id_3]); % Get value of the share of optimizing agents. if estimate_non_optimizing_agents_share share_of_optimizing_agents = params0(params_id_0); end % Check that the share is in (0,1) if share_of_optimizing_agents>1 || share_of_optimizing_agents<0 error('Initial value for the share of optimizing agents shoud be in (0,1).') end % Update the vector of parameters. M_.params(ipnames_) = params0; % Update the reduced form PAC expectation parameters and compute the expectations. [PacExpectations, M_] = UpdatePacExpectationsData(dataPAC, data, range, pacmodl, M_, oo_); noconvergence = true; counter = 0; while noconvergence counter = counter+1; % Set vector for left handside variable YDATA = data{M_.endo_names{M_.pac.(pacmodl).lhs_var}}(range).data; if is_non_optimizing_agents YDATA = YDATA-share_of_optimizing_agents*PacExpectations; YDATA = YDATA-(1-share_of_optimizing_agents)*(dataForNonOptimizingBehaviour(range).data*non_optimizing_behaviour_params); else YDATA = YDATA-PacExpectations; end if is_exogenous_variables && is_any_calibrated_parameter_x YDATA = YDATA-dataForExogenousVariables_(range).data; end if is_optim_exogenous_variables && is_any_calibrated_parameter_optim_x YDATA = YDATA-share_of_optimizing_agents*dataForOptimExogenousVariables_(range).data; end % Run OLS to estimate PAC parameters (autoregressive parameters and error correction parameter). params1_ = (XDATA\YDATA); params1_(1:length(params_id_5)) = params1_(1:length(params_id_5))/share_of_optimizing_agents; % Compute residuals and sum of squareed residuals. r = YDATA-XDATA(:,1:length(params_id_5))*params1_(1:length(params_id_5))*share_of_optimizing_agents; if is_optim_exogenous_variables && is_any_estimated_parameter_optim_x r = r-XDATA(:,length(params_id_5)+1:end)*params1_(length(params_id_5)+1:end); end ssr = r'*r; % Update convergence dummy variable and display iteration info. noconvergence = max(abs(params0_-params1_))>1e-6; fprintf('Iter. %u,\t crit: %10.5f\t ssr: %10.8f\n', counter, max(abs(params0_-params1_)), ssr) % Updatevector of estimated parameters. params0_ = params1_; % Update the value of the share of non optimizing agents (if estimated) if estimate_non_optimizing_agents_share % First update the parameters and compute the PAC expectation reduced form parameters. M_.params(ipnames_(params_id_4)) = params0_; [PacExpectations, M_] = UpdatePacExpectationsData(dataPAC, data, range, pacmodl, M_, oo_); % Set vector for left handside variable. YDATA = data{M_.endo_names{M_.pac.(pacmodl).lhs_var}}(range).data; YDATA = YDATA-dataForNonOptimizingBehaviour(range).data*non_optimizing_behaviour_params; if is_exogenous_variables if is_any_calibrated_parameter_x YDATA = YDATA-dataForExogenousVariables_(range).data; end if is_any_estimated_parameter_x YDATA = YDATA-XDATA(:, length(params_id_1):end)*params0_(length(params_id_1):end); end end % Set vector for regressor. ZDATA = XDATA(:,1:length(params_id_5))*params0_(1:length(params_id_5))+PacExpectations-dataForNonOptimizingBehaviour(range).data*non_optimizing_behaviour_params; if is_optim_exogenous_variables && is_any_calibrated_parameter_optim_x ZDATA = ZDATA+dataForOptimExogenousVariables_(range).data; end % Update the (estimated) share of optimizing agents by running OLS share_of_optimizing_agents = (ZDATA\YDATA); % Force the share of optimizing agents to be in [0,1]. share_of_optimizing_agents = max(min(share_of_optimizing_agents, options_.pac.estimation.ols.share_of_optimizing_agents.ub), ... options_.pac.estimation.ols.share_of_optimizing_agents.lb); % Issue an error if the share is nor strictly positive if share_of_optimizing_agents 0 GrowthVariable = GrowthVariable*Model.params(Model.pac.(pacmodl).growth_linear_comb(iter).param_id); end if Model.pac.(pacmodl).growth_linear_comb(iter).exo_id > 0 GrowthVariable = GrowthVariable*data{Model.exo_names{Model.pac.(pacmodl).growth_linear_comb(iter).exo_id}}.lag(abs(Model.pac.(pacmodl).growth_linear_comb(iter).lag)); GrowthVariable = GrowthVariable(range).data; elseif Model.pac.(pacmodl).growth_linear_comb(iter).endo_id > 0 GrowthVariable = GrowthVariable*data{Model.endo_names{Model.pac.(pacmodl).growth_linear_comb(iter).endo_id}}.lag(abs(Model.pac.(pacmodl).growth_linear_comb(iter).lag)); GrowthVariable = GrowthVariable(range).data; end correction = correction + GrowthVariable; end correction = correction*Model.params(Model.pac.(pacmodl).growth_neutrality_param_index); end PacExpectations = PacExpectations+correction;