From e1b01d922581ceb1bf1fed543aff228109215971 Mon Sep 17 00:00:00 2001 From: Houtan Bastani Date: Tue, 22 Jan 2019 00:57:24 +0100 Subject: [PATCH] common parsing: jsonmodel not necessary as tag info is in AST --- matlab/ols/common_parsing.m | 7 +- matlab/ols/dyn_ols.m | 14 ++-- matlab/ols/getEquationsByTags.m | 58 +++++++------ matlab/ols/{get_ast_jsonmodel.m => get_ast.m} | 23 +++--- matlab/ols/handle_constant_eqs.m | 81 +++++-------------- matlab/ols/parse_ols_style_equation.m | 27 +++---- matlab/ols/pooled_ols.m | 8 +- matlab/ols/sur.m | 10 +-- matlab/olsgibbs.m | 4 +- 9 files changed, 94 insertions(+), 138 deletions(-) rename matlab/ols/{get_ast_jsonmodel.m => get_ast.m} (66%) diff --git a/matlab/ols/common_parsing.m b/matlab/ols/common_parsing.m index 3fc3d459c..736ef1108 100644 --- a/matlab/ols/common_parsing.m +++ b/matlab/ols/common_parsing.m @@ -1,12 +1,11 @@ -function [Y, lhssub, X, startdates, enddates, residnames] = common_parsing(ds, ast, jsonmodel, overlapping_dates) -%function [Y, lhssub, X, startdates, enddates, residnames] = common_parsing(ds, ast, jsonmodel, overlapping_dates) +function [Y, lhssub, X, startdates, enddates, residnames] = common_parsing(ds, ast, overlapping_dates) +%function [Y, lhssub, X, startdates, enddates, residnames] = common_parsing(ds, ast, 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 @@ -54,7 +53,7 @@ residnames = cell(neqs, 1); %% Loop over equations for i = 1:neqs [Y{i}, lhssub{i}, X{i}, residnames{i}, startdates{i}, enddates{i}] = ... - parse_ols_style_equation(ds, ast{i}, jsonmodel{i}); + parse_ols_style_equation(ds, ast{i}); end if overlapping_dates diff --git a/matlab/ols/dyn_ols.m b/matlab/ols/dyn_ols.m index 83b98df8f..0011815c0 100644 --- a/matlab/ols/dyn_ols.m +++ b/matlab/ols/dyn_ols.m @@ -64,21 +64,21 @@ if nargin < 3 end %% Get Equation(s) -[ast, jsonmodel] = get_ast_jsonmodel(eqtags); +ast = get_ast(eqtags); %% Parse equations -[Y, lhssub, X, fp, lp] = common_parsing(ds, ast, jsonmodel, true); +[Y, lhssub, X, fp, lp] = common_parsing(ds, ast, true); %% Loop over equations for i = 1:length(Y) pnames = X{i}.name; [nobs, nvars] = size(X{i}.data); - if isfield(jsonmodel{i}, 'tags') && ... - isfield(jsonmodel{i}.tags, 'name') - tag = jsonmodel{i}.tags.('name'); + if isfield(ast{i}, 'tags') && ... + isfield(ast{i}.tags, 'name') + tag = ast{i}.tags.('name'); else - tag = ['eq_line_no_' num2str(jsonmodel{i}.line)]; + tag = ['eq_line_no_' num2str(ast{i}.line)]; end %% Estimation @@ -155,7 +155,7 @@ for i = 1:length(Y) title = ['OLS Estimation of equation ''' tag '''']; end - preamble = {sprintf('Dependent Variable: %s', jsonmodel{i}.lhs), ... + preamble = {['Dependent Variable: ' Y{i}.name{:}], ... sprintf('No. Independent Variables: %d', nvars), ... sprintf('Observations: %d from %s to %s\n', nobs, fp{i}.char, lp{i}.char)}; diff --git a/matlab/ols/getEquationsByTags.m b/matlab/ols/getEquationsByTags.m index 0fa7971dd..29edee832 100644 --- a/matlab/ols/getEquationsByTags.m +++ b/matlab/ols/getEquationsByTags.m @@ -1,23 +1,23 @@ -function [jsonmodel] = getEquationsByTags(jsonmodel, tagname, tagvalue) -%function [jsonmodel] = getEquationsByTags(jsonmodel, tagname, tagvalue) -% Return the jsonmodel structure with the matching tags +function [ast] = getEquationsByTags(ast, tagname, tagvalue) +%function [ast] = getEquationsByTags(ast, tagname, tagvalue) +% Return the ast structure with the matching tags % % INPUTS -% jsonmodel [cell array] JSON representation of model block -% tagname [string] The name of the tag whos values are to -% be selected -% tagvalue [string] The values to be selected for the -% provided tagname +% ast [cell array] JSON representation of model block +% tagname [string] The name of the tag whos values are to +% be selected +% tagvalue [string] The values to be selected for the +% provided tagname % % OUTPUTS -% jsonmodel [cell array] JSON representation of model block, -% with equations removed that don't match -% eqtags +% ast [cell array] JSON representation of model block, +% with equations removed that don't match +% eqtags % % SPECIAL REQUIREMENTS % none -% Copyright (C) 2017-2018 Dynare Team +% Copyright (C) 2017-2019 Dynare Team % % This file is part of Dynare. % @@ -34,24 +34,34 @@ function [jsonmodel] = getEquationsByTags(jsonmodel, tagname, tagvalue) % You should have received a copy of the GNU General Public License % along with Dynare. If not, see . -assert(nargin == 3, 'Incorrect number of arguments passed to getEquationsByTags'); -assert(iscell(jsonmodel) && ~isempty(jsonmodel), ... - 'the first argument must be a cell array of structs'); -assert(ischar(tagname), 'Tag name must be a string'); -assert(ischar(tagvalue) || iscell(tagvalue), 'Tag value must be a string or a cell string array'); +if nargin ~= 3 + error('Incorrect number of arguments passed to function') +end + +if isempty(ast) || ~iscell(ast) + error('the first argument must be a cell array of structs'); +end + +if ~ischar(tagname) + error('Tag name must be a string'); +end + +if ~ischar(tagvalue) && ~iscell(tagvalue) + error('Tag value must be a string or a cell string array'); +end if ischar(tagvalue) tagvalue = {tagvalue}; end idx2keep = []; -for i=1:length(tagvalue) +for i = 1:length(tagvalue) found = false; - for j=1:length(jsonmodel) - assert(isstruct(jsonmodel{j}), 'Every entry in jsonmodel must be a struct'); - if isfield(jsonmodel{j}, 'tags') && ... - isfield(jsonmodel{j}.tags, tagname) && ... - strcmp(jsonmodel{j}.tags.(tagname), tagvalue{i}) + for j=1:length(ast) + assert(isstruct(ast{j}), 'Every entry in the ast must be a struct'); + if isfield(ast{j}, 'tags') && ... + isfield(ast{j}.tags, tagname) && ... + strcmp(ast{j}.tags.(tagname), tagvalue{i}) idx2keep = [idx2keep; j]; found = true; break @@ -62,5 +72,5 @@ for i=1:length(tagvalue) end end assert(~isempty(idx2keep), 'getEquationsByTags: no equations selected'); -jsonmodel = jsonmodel(unique(idx2keep, 'stable')); +ast = ast(unique(idx2keep, 'stable')); end diff --git a/matlab/ols/get_ast_jsonmodel.m b/matlab/ols/get_ast.m similarity index 66% rename from matlab/ols/get_ast_jsonmodel.m rename to matlab/ols/get_ast.m index 05ea70df5..4d7e27a64 100644 --- a/matlab/ols/get_ast_jsonmodel.m +++ b/matlab/ols/get_ast.m @@ -1,15 +1,14 @@ -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. +function ast = get_ast(eqtags) +%function ast = get_ast(eqtags) +% return the Abstract Syntax Tree the JSON output of preprocessor for the +% given equation tags. Equations are ordered in eqtag order % % INPUTS % eqtags [cellstr] names of equation tags for which to get info. -% If empty, get all +% If empty, get all equations % % OUTPUTS % ast [cell array] JSON representation of the abstract syntax tree -% jsonmodel [cell array] JSON representation of the model block % % SPECIAL REQUIREMENTS % none @@ -39,14 +38,14 @@ 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); + error(['Could not find ' jsonfile '! ' ... + 'Please use the json=compute option ' ... + '(See the Dynare invocation section in the reference manual).']); end -jsonmodel = loadjson(jsonfile); -ast = jsonmodel.abstract_syntax_tree; -jsonmodel = jsonmodel.model; +ast = loadjson(jsonfile); +ast = ast.abstract_syntax_tree; if ~isempty(eqtags) ast = getEquationsByTags(ast, 'name', eqtags); - jsonmodel = getEquationsByTags(jsonmodel, 'name', eqtags); end -end \ No newline at end of file +end diff --git a/matlab/ols/handle_constant_eqs.m b/matlab/ols/handle_constant_eqs.m index 240ff1420..2ad9a382f 100644 --- a/matlab/ols/handle_constant_eqs.m +++ b/matlab/ols/handle_constant_eqs.m @@ -1,18 +1,16 @@ -function [ast, jsonmodel, ds] = handle_constant_eqs(ast, jsonmodel, ds) -%function [ast, jsonmodel, ds] = handle_constant_eqs(ast, jsonmodel, ds) +function [ast, ds] = handle_constant_eqs(ast, ds) +%function [ast, ds] = handle_constant_eqs(ast, ds) % % Code to handle equations of type `X = 0;` in AST. Returns equation(s) % removed from AST and ds.X == 0. % % INPUTS -% ast [cell array] JSON representation of abstract syntax tree -% jsonmodel [cell array] JSON representation of model block -% ds [dseries] data to be updated +% ast [cell array] JSON representation of abstract syntax tree +% ds [dseries] data to be updated % % OUTPUTS -% ast [cell array] updated JSON representation of abstract syntax tree -% jsonmodel [cell array] JSON representation of model block -% ds [dseries] data to be updated +% ast [cell array] updated JSON representation of abstract syntax tree +% ds [dseries] data to be updated % % SPECIAL REQUIREMENTS % none @@ -34,6 +32,18 @@ function [ast, jsonmodel, ds] = handle_constant_eqs(ast, jsonmodel, ds) % You should have received a copy of the GNU General Public License % along with Dynare. If not, see . +if nargin ~= 2 + error('Incorrect number of arguments to function') +end + +if isempty(ast) || ~iscell(ast) + error('ast must be a cell') +end + +if isempty(ds) || ~isdseries(ds) + error('ds must be a nonempty dseries') +end + for i = length(ast):-1:1 assert(strcmp(ast{i}.AST.node_type, 'BinaryOpNode') && strcmp(ast{i}.AST.op, '='), 'Expecting equation'); if strcmp(ast{i}.AST.arg2.node_type, 'NumConstNode') @@ -43,61 +53,6 @@ for i = length(ast):-1:1 ds.(ast{i}.AST.arg1.name) = ... dseries(ast{i}.AST.arg2.value*ones(ds.nobs, 1), ds.firstdate, ast{i}.AST.arg1.name); ast = ast([1:i-1, i+1:end]); - jsonmodel = jsonmodel([1:i-1, i+1:end]); end end -% for i = 1:length(ast) -% ast{i}.AST.arg2 = replace_in_equtaion_recursive(ast{i}.AST.arg2, vars_and_vals); -% end end - -% function node = replace_in_equtaion_recursive(node, vars_and_vals) -% if strcmp(node.node_type, 'NumConstNode') -% elseif strcmp(node.node_type, 'VariableNode') -% if strcmp(node.type, 'endogenous') -% if any(strcmp(vars_and_vals(:,1), node.name)) -% node = struct('node_type', 'NumConstNode', ... -% 'value', vars_and_vals{strcmp(vars_and_vals(:,1), node.name), 2}); -% end -% end -% elseif strcmp(node.node_type, 'UnaryOpNode') -% node.arg = replace_in_equtaion_recursive(node.arg, vars_and_vals); -% if strcmp(node.arg.node_type, 'NumConstNode') -% switch node.op -% case 'log' -% node = struct('node_type', 'NumConstNode', ... -% 'value', log(node.arg.value)); -% case 'exp' -% node = struct('node_type', 'NumConstNode', ... -% 'value', exp(node.arg.value)); -% case 'uminus' -% node = struct('node_type', 'NumConstNode', ... -% 'value', -node.arg.value); -% end -% end -% elseif strcmp(node.node_type, 'BinaryOpNode') -% node.arg1 = replace_in_equtaion_recursive(node.arg1, vars_and_vals); -% node.arg2 = replace_in_equtaion_recursive(node.arg2, vars_and_vals); -% if strcmp(node.arg1.node_type, 'NumConstNode') -% if strcmp(node.arg2.node_type, 'NumConstNode') -% node = struct('node_type', 'NumConstNode', ... -% 'value', eval(['node.arg1.value' node.op 'node.arg1.value'])); -% elseif node.arg1.value == 0 -% if strcmp(node.op, 'minus') -% node = struct('node_type', 'UnaryOpNode', ... -% 'op', 'uminus', ... -% 'arg', node.arg2); -% elseif strcmp(node.op, 'plus') -% node = node.arg2; -% else -% error(['Node type not supported ' node.op]); -% end -% end -% end -% if strcmp(node.arg2.node_type, 'NumConstNode') && node.arg2.value == 0 -% node = node.arg1; -% end -% else -% error(['Node type not supported: ' node.node_type]) -% end -% end diff --git a/matlab/ols/parse_ols_style_equation.m b/matlab/ols/parse_ols_style_equation.m index c4ea9a3e5..d5f87945a 100644 --- a/matlab/ols/parse_ols_style_equation.m +++ b/matlab/ols/parse_ols_style_equation.m @@ -1,12 +1,11 @@ -function [Y, lhssub, X, residual, fp, lp] = parse_ols_style_equation(ds, ast, jsonmodel) -%function X = parse_ols_style_equation() +function [Y, lhssub, X, residual, fp, lp] = parse_ols_style_equation(ds, ast) +%function [Y, lhssub, X, residual, fp, lp] = parse_ols_style_equation(ds, ast) % Run OLS on chosen model equations; unlike olseqs, allow for time t % endogenous variables on LHS % % INPUTS % ds [dseries] data % ast [struct] AST representing the equation to be parsed -% jsonmodel [cell array] JSON representation of model block % % OUTPUTS % Y [dseries] LHS of the equation (with lhssub subtracted) @@ -37,8 +36,8 @@ function [Y, lhssub, X, residual, fp, lp] = parse_ols_style_equation(ds, ast, js % along with Dynare. If not, see . %% Check inputs -if nargin ~= 3 - error('parse_ols_style_equation takes 3 arguments') +if nargin ~= 2 + error('parse_ols_style_equation takes 2 arguments') end if isempty(ds) || ~isdseries(ds) @@ -46,7 +45,7 @@ if isempty(ds) || ~isdseries(ds) end if isempty(ast) || ~isstruct(ast) - error('ast must be a struct'); + error('parse_ols_style_equation: arg 2 must be a struct'); end line = ast.line; @@ -65,10 +64,6 @@ else end end -if isempty(jsonmodel) || ~isstruct(jsonmodel) - error('jsonmodel must be a struct'); -end - %% Set LHS (Y) lhssub = dseries(); Y = evalNode(ds, ast.AST.arg1, line, dseries()); @@ -161,12 +156,12 @@ if ~isempty(lhssub) end % If it exists, account for tag set in mod file -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 isfield(ast, 'tags') ... + && isfield(ast.tags, 'sample') ... + && ~isempty(ast.tags.sample) + colon_idx = strfind(ast.tags.sample, ':'); + fsd = dates(ast.tags.sample(1:colon_idx-1)); + lsd = dates(ast.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]) diff --git a/matlab/ols/pooled_ols.m b/matlab/ols/pooled_ols.m index 84f3cc995..f1945f09b 100644 --- a/matlab/ols/pooled_ols.m +++ b/matlab/ols/pooled_ols.m @@ -67,8 +67,8 @@ else end %% Get Equation(s) -[ast, jsonmodel] = get_ast_jsonmodel(eqtags); -neqs = length(jsonmodel); +ast = get_ast(eqtags); +neqs = length(ast); %% Replace parameter names in equations country_name = param_common{1}; @@ -76,8 +76,8 @@ regexcountries = ['(' strjoin(param_common(2:end),'|') ')']; ast = replace_parameters(ast, country_name, regexcountries, param_regex); %% Find parameters and variable names in every equation & Setup estimation matrices -[Y, ~, X, ~, ~, residnames] = common_parsing(ds, ast, jsonmodel, overlapping_dates); -clear ast jsonmodel; +[Y, ~, X, ~, ~, residnames] = common_parsing(ds, ast, overlapping_dates); +clear ast nobs = zeros(length(Y), 1); nobs(1) = Y{1}.nobs; fp = Y{1}.firstobservedperiod; diff --git a/matlab/ols/sur.m b/matlab/ols/sur.m index 970c62ace..7497f9846 100644 --- a/matlab/ols/sur.m +++ b/matlab/ols/sur.m @@ -47,13 +47,13 @@ else end %% Get Equation(s) -[ast, jsonmodel] = get_ast_jsonmodel(eqtags); -[ast, jsonmodel, ds] = handle_constant_eqs(ast, jsonmodel, ds); -neqs = length(jsonmodel); +ast = get_ast(eqtags); +[ast, ds] = handle_constant_eqs(ast, ds); +neqs = length(ast); %% Find parameters and variable names in equations and setup estimation matrices -[Y, ~, X] = common_parsing(ds, ast, jsonmodel, true); -clear ast jsonmodel; +[Y, ~, X] = common_parsing(ds, ast, true); +clear ast nobs = Y{1}.nobs; [Y, X, constrained] = put_in_sur_form(Y, X); diff --git a/matlab/olsgibbs.m b/matlab/olsgibbs.m index 1142be17d..9a7ccd051 100644 --- a/matlab/olsgibbs.m +++ b/matlab/olsgibbs.m @@ -123,9 +123,7 @@ else end %% Parse equation -[ast, jsonmodel] = get_ast_jsonmodel({eqtag}); -[Y, ~, X, fp, lp] = common_parsing(ds, ast, jsonmodel, true); -clear ast jsonmodel +[Y, ~, X, fp, lp] = common_parsing(ds, get_ast({eqtag}), true); lhsname = Y{1}.name; Y = Y{1}.data; X = X{1};