common parsing: jsonmodel not necessary as tag info is in AST

time-shift
Houtan Bastani 2019-01-22 00:57:24 +01:00
parent 407ac26f5b
commit e1b01d9225
No known key found for this signature in database
GPG Key ID: 000094FB955BE169
9 changed files with 94 additions and 138 deletions

View File

@ -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, 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)
% %
% Code common to sur.m and pooled_ols.m % Code common to sur.m and pooled_ols.m
% %
% INPUTS % INPUTS
% ds [dseries] dataset % ds [dseries] dataset
% ast [cell array] JSON representation of abstract syntax tree % 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 % overlapping_dates [boolean] if true, dates are same across equations
% %
% OUTPUTS % OUTPUTS
@ -54,7 +53,7 @@ residnames = cell(neqs, 1);
%% Loop over equations %% Loop over equations
for i = 1:neqs for i = 1:neqs
[Y{i}, lhssub{i}, X{i}, residnames{i}, startdates{i}, enddates{i}] = ... [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 end
if overlapping_dates if overlapping_dates

View File

@ -64,21 +64,21 @@ if nargin < 3
end end
%% Get Equation(s) %% Get Equation(s)
[ast, jsonmodel] = get_ast_jsonmodel(eqtags); ast = get_ast(eqtags);
%% Parse equations %% 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 %% Loop over equations
for i = 1:length(Y) for i = 1:length(Y)
pnames = X{i}.name; pnames = X{i}.name;
[nobs, nvars] = size(X{i}.data); [nobs, nvars] = size(X{i}.data);
if isfield(jsonmodel{i}, 'tags') && ... if isfield(ast{i}, 'tags') && ...
isfield(jsonmodel{i}.tags, 'name') isfield(ast{i}.tags, 'name')
tag = jsonmodel{i}.tags.('name'); tag = ast{i}.tags.('name');
else else
tag = ['eq_line_no_' num2str(jsonmodel{i}.line)]; tag = ['eq_line_no_' num2str(ast{i}.line)];
end end
%% Estimation %% Estimation
@ -155,7 +155,7 @@ for i = 1:length(Y)
title = ['OLS Estimation of equation ''' tag '''']; title = ['OLS Estimation of equation ''' tag ''''];
end end
preamble = {sprintf('Dependent Variable: %s', jsonmodel{i}.lhs), ... preamble = {['Dependent Variable: ' Y{i}.name{:}], ...
sprintf('No. Independent Variables: %d', nvars), ... sprintf('No. Independent Variables: %d', nvars), ...
sprintf('Observations: %d from %s to %s\n', nobs, fp{i}.char, lp{i}.char)}; sprintf('Observations: %d from %s to %s\n', nobs, fp{i}.char, lp{i}.char)};

View File

@ -1,23 +1,23 @@
function [jsonmodel] = getEquationsByTags(jsonmodel, tagname, tagvalue) function [ast] = getEquationsByTags(ast, tagname, tagvalue)
%function [jsonmodel] = getEquationsByTags(jsonmodel, tagname, tagvalue) %function [ast] = getEquationsByTags(ast, tagname, tagvalue)
% Return the jsonmodel structure with the matching tags % Return the ast structure with the matching tags
% %
% INPUTS % INPUTS
% jsonmodel [cell array] JSON representation of model block % ast [cell array] JSON representation of model block
% tagname [string] The name of the tag whos values are to % tagname [string] The name of the tag whos values are to
% be selected % be selected
% tagvalue [string] The values to be selected for the % tagvalue [string] The values to be selected for the
% provided tagname % provided tagname
% %
% OUTPUTS % OUTPUTS
% jsonmodel [cell array] JSON representation of model block, % ast [cell array] JSON representation of model block,
% with equations removed that don't match % with equations removed that don't match
% eqtags % eqtags
% %
% SPECIAL REQUIREMENTS % SPECIAL REQUIREMENTS
% none % none
% Copyright (C) 2017-2018 Dynare Team % Copyright (C) 2017-2019 Dynare Team
% %
% This file is part of Dynare. % 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 % You should have received a copy of the GNU General Public License
% along with Dynare. If not, see <http://www.gnu.org/licenses/>. % along with Dynare. If not, see <http://www.gnu.org/licenses/>.
assert(nargin == 3, 'Incorrect number of arguments passed to getEquationsByTags'); if nargin ~= 3
assert(iscell(jsonmodel) && ~isempty(jsonmodel), ... error('Incorrect number of arguments passed to function')
'the first argument must be a cell array of structs'); end
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 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) if ischar(tagvalue)
tagvalue = {tagvalue}; tagvalue = {tagvalue};
end end
idx2keep = []; idx2keep = [];
for i=1:length(tagvalue) for i = 1:length(tagvalue)
found = false; found = false;
for j=1:length(jsonmodel) for j=1:length(ast)
assert(isstruct(jsonmodel{j}), 'Every entry in jsonmodel must be a struct'); assert(isstruct(ast{j}), 'Every entry in the ast must be a struct');
if isfield(jsonmodel{j}, 'tags') && ... if isfield(ast{j}, 'tags') && ...
isfield(jsonmodel{j}.tags, tagname) && ... isfield(ast{j}.tags, tagname) && ...
strcmp(jsonmodel{j}.tags.(tagname), tagvalue{i}) strcmp(ast{j}.tags.(tagname), tagvalue{i})
idx2keep = [idx2keep; j]; idx2keep = [idx2keep; j];
found = true; found = true;
break break
@ -62,5 +72,5 @@ for i=1:length(tagvalue)
end end
end end
assert(~isempty(idx2keep), 'getEquationsByTags: no equations selected'); assert(~isempty(idx2keep), 'getEquationsByTags: no equations selected');
jsonmodel = jsonmodel(unique(idx2keep, 'stable')); ast = ast(unique(idx2keep, 'stable'));
end end

View File

@ -1,15 +1,14 @@
function [ast, jsonmodel] = get_ast_jsonmodel(eqtags) function ast = get_ast(eqtags)
% function [ast, jsonmodel] = get_ast_jsonmodel(eqtags) %function ast = get_ast(eqtags)
% return AST and jsonmodel from json output of preprocessor for the given % return the Abstract Syntax Tree the JSON output of preprocessor for the
% equation tags. % given equation tags. Equations are ordered in eqtag order
% %
% INPUTS % INPUTS
% eqtags [cellstr] names of equation tags for which to get info. % eqtags [cellstr] names of equation tags for which to get info.
% If empty, get all % If empty, get all equations
% %
% OUTPUTS % OUTPUTS
% ast [cell array] JSON representation of the abstract syntax tree % ast [cell array] JSON representation of the abstract syntax tree
% jsonmodel [cell array] JSON representation of the model block
% %
% SPECIAL REQUIREMENTS % SPECIAL REQUIREMENTS
% none % none
@ -39,14 +38,14 @@ end
jsonfile = [M_.fname filesep() 'model' filesep() 'json' filesep() 'modfile-original.json']; jsonfile = [M_.fname filesep() 'model' filesep() 'json' filesep() 'modfile-original.json'];
if exist(jsonfile, 'file') ~= 2 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 end
jsonmodel = loadjson(jsonfile); ast = loadjson(jsonfile);
ast = jsonmodel.abstract_syntax_tree; ast = ast.abstract_syntax_tree;
jsonmodel = jsonmodel.model;
if ~isempty(eqtags) if ~isempty(eqtags)
ast = getEquationsByTags(ast, 'name', eqtags); ast = getEquationsByTags(ast, 'name', eqtags);
jsonmodel = getEquationsByTags(jsonmodel, 'name', eqtags);
end end
end end

View File

@ -1,18 +1,16 @@
function [ast, jsonmodel, ds] = handle_constant_eqs(ast, jsonmodel, ds) function [ast, ds] = handle_constant_eqs(ast, ds)
%function [ast, jsonmodel, ds] = handle_constant_eqs(ast, jsonmodel, ds) %function [ast, ds] = handle_constant_eqs(ast, ds)
% %
% Code to handle equations of type `X = 0;` in AST. Returns equation(s) % Code to handle equations of type `X = 0;` in AST. Returns equation(s)
% removed from AST and ds.X == 0. % removed from AST and ds.X == 0.
% %
% INPUTS % INPUTS
% ast [cell array] JSON representation of abstract syntax tree % ast [cell array] JSON representation of abstract syntax tree
% jsonmodel [cell array] JSON representation of model block % ds [dseries] data to be updated
% ds [dseries] data to be updated
% %
% OUTPUTS % OUTPUTS
% ast [cell array] updated JSON representation of abstract syntax tree % ast [cell array] updated JSON representation of abstract syntax tree
% jsonmodel [cell array] JSON representation of model block % ds [dseries] data to be updated
% ds [dseries] data to be updated
% %
% SPECIAL REQUIREMENTS % SPECIAL REQUIREMENTS
% none % 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 % You should have received a copy of the GNU General Public License
% along with Dynare. If not, see <http://www.gnu.org/licenses/>. % along with Dynare. If not, see <http://www.gnu.org/licenses/>.
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 for i = length(ast):-1:1
assert(strcmp(ast{i}.AST.node_type, 'BinaryOpNode') && strcmp(ast{i}.AST.op, '='), 'Expecting equation'); assert(strcmp(ast{i}.AST.node_type, 'BinaryOpNode') && strcmp(ast{i}.AST.op, '='), 'Expecting equation');
if strcmp(ast{i}.AST.arg2.node_type, 'NumConstNode') 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) = ... ds.(ast{i}.AST.arg1.name) = ...
dseries(ast{i}.AST.arg2.value*ones(ds.nobs, 1), ds.firstdate, 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]); ast = ast([1:i-1, i+1:end]);
jsonmodel = jsonmodel([1:i-1, i+1:end]);
end 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 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

View File

@ -1,12 +1,11 @@
function [Y, lhssub, X, residual, fp, lp] = parse_ols_style_equation(ds, ast, jsonmodel) function [Y, lhssub, X, residual, fp, lp] = parse_ols_style_equation(ds, ast)
%function X = parse_ols_style_equation() %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 % Run OLS on chosen model equations; unlike olseqs, allow for time t
% endogenous variables on LHS % endogenous variables on LHS
% %
% INPUTS % INPUTS
% ds [dseries] data % ds [dseries] data
% ast [struct] AST representing the equation to be parsed % ast [struct] AST representing the equation to be parsed
% jsonmodel [cell array] JSON representation of model block
% %
% OUTPUTS % OUTPUTS
% Y [dseries] LHS of the equation (with lhssub subtracted) % 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 <http://www.gnu.org/licenses/>. % along with Dynare. If not, see <http://www.gnu.org/licenses/>.
%% Check inputs %% Check inputs
if nargin ~= 3 if nargin ~= 2
error('parse_ols_style_equation takes 3 arguments') error('parse_ols_style_equation takes 2 arguments')
end end
if isempty(ds) || ~isdseries(ds) if isempty(ds) || ~isdseries(ds)
@ -46,7 +45,7 @@ if isempty(ds) || ~isdseries(ds)
end end
if isempty(ast) || ~isstruct(ast) if isempty(ast) || ~isstruct(ast)
error('ast must be a struct'); error('parse_ols_style_equation: arg 2 must be a struct');
end end
line = ast.line; line = ast.line;
@ -65,10 +64,6 @@ else
end end
end end
if isempty(jsonmodel) || ~isstruct(jsonmodel)
error('jsonmodel must be a struct');
end
%% Set LHS (Y) %% Set LHS (Y)
lhssub = dseries(); lhssub = dseries();
Y = evalNode(ds, ast.AST.arg1, line, dseries()); Y = evalNode(ds, ast.AST.arg1, line, dseries());
@ -161,12 +156,12 @@ if ~isempty(lhssub)
end end
% If it exists, account for tag set in mod file % If it exists, account for tag set in mod file
if isfield(jsonmodel, 'tags') ... if isfield(ast, 'tags') ...
&& isfield(jsonmodel.tags, 'sample') ... && isfield(ast.tags, 'sample') ...
&& ~isempty(jsonmodel.tags.sample) && ~isempty(ast.tags.sample)
colon_idx = strfind(jsonmodel.tags.sample, ':'); colon_idx = strfind(ast.tags.sample, ':');
fsd = dates(jsonmodel.tags.sample(1:colon_idx-1)); fsd = dates(ast.tags.sample(1:colon_idx-1));
lsd = dates(jsonmodel.tags.sample(colon_idx+1:end)); lsd = dates(ast.tags.sample(colon_idx+1:end));
if fp > fsd if fp > fsd
warning(['The sample over which you want to estimate contains NaNs. '... warning(['The sample over which you want to estimate contains NaNs. '...
'Adjusting estimation range to begin on: ' fp.char]) 'Adjusting estimation range to begin on: ' fp.char])

View File

@ -67,8 +67,8 @@ else
end end
%% Get Equation(s) %% Get Equation(s)
[ast, jsonmodel] = get_ast_jsonmodel(eqtags); ast = get_ast(eqtags);
neqs = length(jsonmodel); neqs = length(ast);
%% Replace parameter names in equations %% Replace parameter names in equations
country_name = param_common{1}; country_name = param_common{1};
@ -76,8 +76,8 @@ regexcountries = ['(' strjoin(param_common(2:end),'|') ')'];
ast = replace_parameters(ast, country_name, regexcountries, param_regex); ast = replace_parameters(ast, country_name, regexcountries, param_regex);
%% Find parameters and variable names in every equation & Setup estimation matrices %% Find parameters and variable names in every equation & Setup estimation matrices
[Y, ~, X, ~, ~, residnames] = common_parsing(ds, ast, jsonmodel, overlapping_dates); [Y, ~, X, ~, ~, residnames] = common_parsing(ds, ast, overlapping_dates);
clear ast jsonmodel; clear ast
nobs = zeros(length(Y), 1); nobs = zeros(length(Y), 1);
nobs(1) = Y{1}.nobs; nobs(1) = Y{1}.nobs;
fp = Y{1}.firstobservedperiod; fp = Y{1}.firstobservedperiod;

View File

@ -47,13 +47,13 @@ else
end end
%% Get Equation(s) %% Get Equation(s)
[ast, jsonmodel] = get_ast_jsonmodel(eqtags); ast = get_ast(eqtags);
[ast, jsonmodel, ds] = handle_constant_eqs(ast, jsonmodel, ds); [ast, ds] = handle_constant_eqs(ast, ds);
neqs = length(jsonmodel); neqs = length(ast);
%% Find parameters and variable names in equations and setup estimation matrices %% Find parameters and variable names in equations and setup estimation matrices
[Y, ~, X] = common_parsing(ds, ast, jsonmodel, true); [Y, ~, X] = common_parsing(ds, ast, true);
clear ast jsonmodel; clear ast
nobs = Y{1}.nobs; nobs = Y{1}.nobs;
[Y, X, constrained] = put_in_sur_form(Y, X); [Y, X, constrained] = put_in_sur_form(Y, X);

View File

@ -123,9 +123,7 @@ else
end end
%% Parse equation %% Parse equation
[ast, jsonmodel] = get_ast_jsonmodel({eqtag}); [Y, ~, X, fp, lp] = common_parsing(ds, get_ast({eqtag}), true);
[Y, ~, X, fp, lp] = common_parsing(ds, ast, jsonmodel, true);
clear ast jsonmodel
lhsname = Y{1}.name; lhsname = Y{1}.name;
Y = Y{1}.data; Y = Y{1}.data;
X = X{1}; X = X{1};