From a477075825066063c10199072024898a86f31e31 Mon Sep 17 00:00:00 2001 From: Houtan Bastani Date: Fri, 11 Jan 2019 11:26:13 +0100 Subject: [PATCH] ols: further simplifications --- .../{pooled_sur_common.m => common_parsing.m} | 52 ++++++++++++-- matlab/ols/dyn_ols.m | 71 +++++-------------- matlab/ols/get_ast_jsonmodel.m | 52 ++++++++++++++ matlab/ols/get_ols_start_end_dates.m | 30 ++++++++ 4 files changed, 147 insertions(+), 58 deletions(-) rename matlab/ols/{pooled_sur_common.m => common_parsing.m} (82%) create mode 100644 matlab/ols/get_ast_jsonmodel.m create mode 100644 matlab/ols/get_ols_start_end_dates.m diff --git a/matlab/ols/pooled_sur_common.m b/matlab/ols/common_parsing.m similarity index 82% rename from matlab/ols/pooled_sur_common.m rename to matlab/ols/common_parsing.m index a0835d2b7..891ff20e4 100644 --- a/matlab/ols/pooled_sur_common.m +++ b/matlab/ols/common_parsing.m @@ -1,15 +1,19 @@ -function [X, Y, startdates, enddates, startidxs, residnames, pbeta, vars, surpidxs, surconstrainedparams] = pooled_sur_common(ds, jsonmodel) -%function [X, Y, startdates, enddates, startidxs, residnames, pbeta, vars, surpidxs, surconstrainedparams] = pooled_sur_common(ds, jsonmodel) +function [Y, lhssub, X, startdates, enddates, startidxs, residnames, pbeta, vars, surpidxs, surconestrainedparams] = common_parsing(ds, ast, jsonmodel, overlapping_dates) +%function [Y, lhssub, X, startdates, enddates, startidxs, residnames, pbeta, vars, surpidxs, surconstrainedparams] = common_parsing(ds, ast, jsonmodel, overlapping_dates) % % Code common to sur.m and pooled_ols.m % % INPUTS % ds [dseries] dataset +% ast [cell array] JSON representation of abstract syntax tree % jsonmodel [cell array] JSON representation of model block +% overlapping_dates [boolean] if true, dates are same across +% equations % % OUTPUTS -% X [matrix] regressors -% Y [vector] dependent variables +% Y [cell array] dependent variables +% lhssub [cell array] RHS to subtract from Y +% X [cell array] regressors % startdates [cell array] first observed period for each % equation % enddates [cell array] last observed period for each @@ -29,7 +33,7 @@ function [X, Y, startdates, enddates, startidxs, residnames, pbeta, vars, surpid % SPECIAL REQUIREMENTS % none -% Copyright (C) 2017-2018 Dynare Team +% Copyright (C) 2019 Dynare Team % % This file is part of Dynare. % @@ -46,7 +50,43 @@ function [X, Y, startdates, enddates, startidxs, residnames, pbeta, vars, surpid % You should have received a copy of the GNU General Public License % along with Dynare. If not, see . -global M_ + +%% Initialize variables +Y = cell(length(ast), 1); +lhssub = cell(length(ast), 1); +X = cell(length(ast), 1); +startdates = cell(length(ast), 1); +enddates = cell(length(ast), 1); + +%% Loop over equations +neqs = length(ast); +for i = 1:neqs + %% Parse equation i + [Y{i}, lhssub{i}, X{i}] = parse_ols_style_equation(ds, ast{i}, jsonmodel{i}.line); + + %% Set start and end dates + if overlapping_dates + [startdates{i}, enddates{i}] = get_ols_start_end_dates(Y{i}, lhssub{i}, X{i}, jsonmodel{i}); + end +end + +if overlapping_dates + maxfp = max([startdates{:}]); + minlp = min([enddates{:}]); + for i = 1:neqs + Y{i} = Y{i}(maxfp:minlp); + X{i} = X{i}(maxfp:minlp); + if ~isempty(lhssub{i}) + lhssub{i} = lhssub{i}(maxfp:minlp); + end + end +end + + + +return +%% + M_endo_exo_names_trim = [M_.endo_names; M_.exo_names]; [junk, idxs] = sort(cellfun(@length, M_endo_exo_names_trim), 'descend'); diff --git a/matlab/ols/dyn_ols.m b/matlab/ols/dyn_ols.m index 9a5c1d8fb..a52e2d9cc 100644 --- a/matlab/ols/dyn_ols.m +++ b/matlab/ols/dyn_ols.m @@ -41,7 +41,7 @@ function varargout = dyn_ols(ds, fitted_names_dict, eqtags) global M_ oo_ options_ -assert(nargin >= 1 && nargin <= 3, 'dyn_ols: takes between 1 and 3 arguments'); +assert(nargin >= 1 && nargin <= 3, 'dyn_ols() takes between 1 and 3 arguments'); assert(~isempty(ds) && isdseries(ds), 'dyn_ols: the first argument must be a dseries'); jsonfile = [M_.fname filesep() 'model' filesep() 'json' filesep() 'modfile-original.json']; @@ -77,54 +77,21 @@ if strcmp(st(1).name, 'olsgibbs') assert(length(ast) == 1); end +%% Parse equations +[Y, lhssub, X, fp, lp ] = common_parsing(ds, ast, jsonmodel, true); + %% Loop over equations -for i = 1:length(ast) - %% Parse equation i - [Y, lhssub, X] = parse_ols_style_equation(ds, ast{i}, jsonmodel{i}.line); - - %% Set dates - fp = max(Y.firstobservedperiod, X.firstobservedperiod); - lp = min(Y.lastobservedperiod, X.lastobservedperiod); - if ~isempty(lhssub) - fp = max(fp, lhssub.firstobservedperiod); - lp = min(lp, lhssub.lastobservedperiod); - end - if isfield(jsonmodel{i}, 'tags') ... - && isfield(jsonmodel{i}.tags, 'sample') ... - && ~isempty(jsonmodel{i}.tags.sample) - colon_idx = strfind(jsonmodel{i}.tags.sample, ':'); - fsd = dates(jsonmodel{i}.tags.sample(1:colon_idx-1)); - lsd = dates(jsonmodel{i}.tags.sample(colon_idx+1:end)); - if fp > fsd - warning(['The sample over which you want to estimate contains NaNs. '... - 'Adjusting estimation range to begin on: ' fp.char]) - else - fp = fsd; - end - if lp < lsd - warning(['The sample over which you want to estimate contains NaNs. '... - 'Adjusting estimation range to end on: ' lp.char]) - else - lp = lsd; - end - end - - Y = Y(fp:lp); - if ~isempty(lhssub) - lhssub = lhssub(fp:lp); - end - pnames = X.name; - X = X(fp:lp).data; - - [nobs, nvars] = size(X); +for i = 1:length(Y) + pnames = X{i}.name; + [nobs, nvars] = size(X{i}.data); if called_from_olsgibbs varargout{1} = nobs; varargout{2} = pnames; - varargout{3} = X; - varargout{4} = Y.data; + varargout{3} = X{i}.data; + varargout{4} = Y{i}.data; varargout{5} = jsonmodel; - varargout{6} = fp; - varargout{7} = lp; + varargout{6} = fp{i}; + varargout{7} = lp{i}; return end @@ -140,9 +107,9 @@ for i = 1:length(ast) oo_.ols.(tag).dof = nobs - nvars; % Estimated Parameters - [q, r] = qr(X, 0); + [q, r] = qr(X{i}.data, 0); xpxi = (r'*r)\eye(nvars); - oo_.ols.(tag).beta = r\(q'*Y.data); + oo_.ols.(tag).beta = r\(q'*Y{i}.data); oo_.ols.(tag).param_idxs = zeros(length(pnames), 1); for j = 1:length(pnames) if ~strcmp(pnames{j}, 'intercept') @@ -160,14 +127,14 @@ for i = 1:length(ast) yhatname = fitted_names_dict{idx, 2}; end end - oo_.ols.(tag).Yhat = dseries(X*oo_.ols.(tag).beta, fp, yhatname); + oo_.ols.(tag).Yhat = dseries(X{i}.data*oo_.ols.(tag).beta, fp{i}, yhatname); % Residuals - oo_.ols.(tag).resid = Y - oo_.ols.(tag).Yhat; + oo_.ols.(tag).resid = Y{i} - oo_.ols.(tag).Yhat; % Correct Yhat reported back to user - Y = Y + lhssub; - oo_.ols.(tag).Yhat = oo_.ols.(tag).Yhat + lhssub; + Y{i} = Y{i} + lhssub{i}; + oo_.ols.(tag).Yhat = oo_.ols.(tag).Yhat + lhssub{i}; % Apply correcting function for Yhat if it was passed if any(idx) ... @@ -184,7 +151,7 @@ for i = 1:length(ast) oo_.ols.(tag).s2 = SS_res/oo_.ols.(tag).dof; % R^2 - ym = Y.data - mean(Y); + ym = Y{i}.data - mean(Y{i}); SS_tot = ym'*ym; oo_.ols.(tag).R2 = 1 - SS_res/SS_tot; @@ -211,7 +178,7 @@ for i = 1:length(ast) preamble = {sprintf('Dependent Variable: %s', jsonmodel{i}.lhs), ... sprintf('No. Independent Variables: %d', nvars), ... - sprintf('Observations: %d from %s to %s\n', nobs, fp.char, lp.char)}; + sprintf('Observations: %d from %s to %s\n', nobs, fp{i}.char, lp{i}.char)}; afterward = {sprintf('R^2: %f', oo_.ols.(tag).R2), ... sprintf('R^2 Adjusted: %f', oo_.ols.(tag).adjR2), ... diff --git a/matlab/ols/get_ast_jsonmodel.m b/matlab/ols/get_ast_jsonmodel.m new file mode 100644 index 000000000..05ea70df5 --- /dev/null +++ b/matlab/ols/get_ast_jsonmodel.m @@ -0,0 +1,52 @@ +function [ast, jsonmodel] = get_ast_jsonmodel(eqtags) +% function [ast, jsonmodel] = get_ast_jsonmodel(eqtags) +% return AST and jsonmodel from json output of preprocessor for the given +% equation tags. +% +% INPUTS +% eqtags [cellstr] names of equation tags for which to get info. +% If empty, get all +% +% OUTPUTS +% ast [cell array] JSON representation of the abstract syntax tree +% jsonmodel [cell array] JSON representation of the model block +% +% SPECIAL REQUIREMENTS +% none + +% Copyright (C) 2019 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_ + +if ~isempty(eqtags) && ~(ischar(eqtags) || iscell(eqtags)) + error('get_ast_jsonmodel: eqtags not passed correctly') +end + +jsonfile = [M_.fname filesep() 'model' filesep() 'json' filesep() 'modfile-original.json']; +if exist(jsonfile, 'file') ~= 2 + error('Could not find %s! Please use the json=compute option (See the Dynare invocation section in the reference manual).', jsonfile); +end + +jsonmodel = loadjson(jsonfile); +ast = jsonmodel.abstract_syntax_tree; +jsonmodel = jsonmodel.model; +if ~isempty(eqtags) + ast = getEquationsByTags(ast, 'name', eqtags); + jsonmodel = getEquationsByTags(jsonmodel, 'name', eqtags); +end +end \ No newline at end of file diff --git a/matlab/ols/get_ols_start_end_dates.m b/matlab/ols/get_ols_start_end_dates.m new file mode 100644 index 000000000..d4fb9b313 --- /dev/null +++ b/matlab/ols/get_ols_start_end_dates.m @@ -0,0 +1,30 @@ +function [fp, lp] = get_ols_start_end_dates(Y, lhssub, X, jsonmodel) +% function [fp, lp] = get_ols_start_end_dates(Y, lhssub, X, jsonmodel) + +fp = max(Y.firstobservedperiod, X.firstobservedperiod); +lp = min(Y.lastobservedperiod, X.lastobservedperiod); +if ~isempty(lhssub) + fp = max(fp, lhssub.firstobservedperiod); + lp = min(lp, lhssub.lastobservedperiod); +end +if isfield(jsonmodel, 'tags') ... + && isfield(jsonmodel.tags, 'sample') ... + && ~isempty(jsonmodel.tags.sample) + colon_idx = strfind(jsonmodel.tags.sample, ':'); + fsd = dates(jsonmodel.tags.sample(1:colon_idx-1)); + lsd = dates(jsonmodel.tags.sample(colon_idx+1:end)); + if fp > fsd + warning(['The sample over which you want to estimate contains NaNs. '... + 'Adjusting estimation range to begin on: ' fp.char]) + else + fp = fsd; + end + if lp < lsd + warning(['The sample over which you want to estimate contains NaNs. '... + 'Adjusting estimation range to end on: ' lp.char]) + else + lp = lsd; + end +end + +end