-
Stéphane Adjemian authored
Allow variables extracted from a dseries object in the dynamic or static expression following the do keyword.
Stéphane Adjemian authoredAllow variables extracted from a dseries object in the dynamic or static expression following the do keyword.
from.m 15.93 KiB
function from(varargin)
% Copyright (C) 2014 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 <http://www.gnu.org/licenses/>.
lvarargin = lower(varargin);
to_id = strmatch('to',lvarargin);
do_id = strmatch('do',lvarargin);
if isempty(to_id) || isempty(do_id)
error(get_error_message_0())
end
if do_id<to_id
msg = sprinf('dseries::from: Wrong syntax! The TO keyword must preceed the DO keyword.\n');
error(get_error_message_0(msg))
end
if ~isdate(varargin{1})
% The first argument is not a string formatted date. Test if this argument refers to a dates object
% in the caller workspace.
try
d1 = evalin('caller', varargin{1});
if ~isdates(d1)
error(['dseries::from: Variable ' varargin{1} ' is not a dates object!'])
end
catch
error(['dseries::from: Variable ' varargin{1} ' is unknown!'])
end
if ~exist('d1')
msg = sprintf('dseries::from: Wrong syntax! The FROM statement must be followed by a string formatted date.\n');
error(get_error_message_0(msg))
end
else
d1 = dates(varargin{1}); % First date
end
if ~isequal(to_id,2)
msg = sprintf('dseries::from: Wrong syntax! The first dates object must be immediately followed by the TO keyword.\n');
error(get_error_message_0(msg))
end
if ~isdate(varargin{3})
% The third argument is not a string formatted date. Test if this argument refers to a dates object
% in the caller workspace.
try
d2 = evalin('caller', varargin{3});
if ~isdates(d2)
error(['dseries::from: Variable ' varargin{3} ' is not a dates object!'])
end
catch
error(['dseries::from: Variable ' varargin{3} ' is unknown!'])
end
if ~exist('d2')
msg = sprintf('dseries::from: Wrong syntax! The TO keyword must be followed by a second dates object.\n');
error(get_error_message_0(msg))
end
else
d2 = dates(varargin{3}); % Last date
end
if d1>d2
error('dseries::from: The first date must preceed the second one!')
end
if ~isequal(do_id,4)
msg = sprintf('dseries::from: Wrong syntax! The second dates object must be immediately followed by the DO keyword.\n');
error(get_error_message_0(msg))
end
% Build the recursive expression.
EXPRESSION = char([varargin{5:end}]);
% Get all the variables involved in the recursive expression.
variables = unique(regexpi(EXPRESSION, '\w*\(t\)|\w*\(t\-\d\)|\w*\(t\+\d\)|\w*\.\w*\(t\)|\w*\.\w*\(t\-\d\)|\w*\.\w*\(t\+\d\)','match'));
% Copy EXPRESSION in expression. In the next loop we will remove all indexed variables from expression.
expression = EXPRESSION;
% Build an incidence table (max lag/lead for each variable)
%
% Column 1: Name of the variable.
% Column 2: Maximum lag order.
% Column 3: Equal to 1 if the variable appears at the current period, 0 otherwise.
% Column 4: Maximum lead order.
% Column 5: Vector of effective lag orders.
% Column 6: Vector of effective lead orders.
%
% Initialization.
leadlagtable = cell(0,6);
% Loop over the variables (dseries objects).
for i=1:length(variables)
expression = strrep(expression,variables{i},'');
current = ~isempty(regexpi(variables{i},'\(t\)'));
lag = ~isempty(regexpi(variables{i},'\(t\-\d\)'));
lead = ~isempty(regexpi(variables{i},'\(t\+\d\)'));
start = regexpi(variables{i},'\(t\)|\(t\-\d\)|\(t\+\d\)');
index = variables{i}(start:end);
variables(i) = {variables{i}(1:start-1)};
if isempty(leadlagtable)
leadlagtable(1,1) = {variables{i}};
if current
leadlagtable(1,3) = {1};
else
leadlagtable(1,3) = {0};
end
if lag
tmp = regexpi(index,'\d','match');
leadlagtable(1,2) = {str2double(tmp{1})};
leadlagtable(1,5) = {str2double(tmp{1})};
else
leadlagtable(1,2) = {0};
leadlagtable(1,5) = {[]};
end
if lead
tmp = regexpi(index,'\d','match');
leadlagtable(1,4) = {str2double(tmp{1})};
leadlagtable(1,6) = {str2double(tmp{1})};
else
leadlagtable(1,4) = {0};
leadlagtable(1,6) = {[]};
end
else
linea = strmatch(variables{i},leadlagtable(:,1));
if isempty(linea)
% This is a new variable!
linea = size(leadlagtable,1)+1;
leadlagtable(linea,1) = {variables{i}};
leadlagtable(linea,2) = {0};
leadlagtable(linea,3) = {0};
leadlagtable(linea,4) = {0};
leadlagtable(linea,5) = {[]};
leadlagtable(linea,6) = {[]};
end
if current
leadlagtable(linea,3) = {1};
end
if lag
tmp = regexpi(index,'\d','match');
leadlagtable(linea,2) = {max(str2double(tmp{1}),leadlagtable{linea,2})};
leadlagtable(linea,5) = {sortrows([leadlagtable{linea,5}; str2double(tmp{1})])};
end
if lead
tmp = regexpi(index,'\d','match');
leadlagtable(linea,4) = {max(str2double(tmp{1}),leadlagtable{linea,4})};
leadlagtable(linea,6) = {sortrows([leadlagtable{linea,6}; str2double(tmp{1})])};
end
end
end
% Set the number of variables
number_of_variables = size(leadlagtable,1);
% Initialize a cell array containing the names of the variables.
variable_names = cell(1);
% Test that all the involved variables are available dseries objects. Also check that
% these time series are defined over the time range given by d1 and d2 (taking care of
% the lags and leads) and check that each object is a singleton
for i=1:number_of_variables
current_variable = leadlagtable{i,1};
idvar = strfind(current_variable,'.');
if isempty(idvar)
idvar = 0;
end
if idvar
current_variable_0 = current_variable(1:idvar-1);
else
current_variable_0 = current_variable;
end
try
var = evalin('caller',current_variable_0);
catch
error(['dseries::from: Variable ' current_variable_0 ' is unknown!'])
end
if idvar
try
eval(sprintf('var = var.%s;',current_variable(idvar+1:end)))
catch
error(sprintf('dseries::from: Variable %s is not a member of dseries oject %s!', current_variable(idvar+1:end), current_variable_0))
end
end
if ~isdseries(var)
error(['dseries::from: Variable ' current_variable ' is not a dseries object!'])
else
if ~var.vobs
msg = sprintf('dseries::from: Object %s must not be empty!\n',current_variable);
error(msg)
end
if var.vobs>1
msg = sprintf('dseries::from: Object %s must contain only one variable!\n',current_variable);
error(msg)
end
if i>1
if ismember(var.name,variable_names)
error('dseries::from: All the dseries objects should contain variables with different names!')
else
variable_names(i) = {var.name{1}};
end
else
variable_names(i) = {var.name{1}};
end
if d1<var.dates(1)+leadlagtable{i,2}
msg = sprintf('dseries::from: Initial date of the loop (%s) is inconsistent with %s''s range!\n',char(d1),current_variable);
msg = [msg, sprintf(' Initial date should be greater than or equal to %s.',char(var.dates(1)+leadlagtable{i,2}))];
error(msg)
end
if d2>var.dates(end)-leadlagtable{i,4}
msg = sprintf('dseries::from: Last date of the loop (%s) is inconsistent with %s''s range!\n',char(d2),current_variable);
msg = [msg, sprintf(' Last date should be less than or equal to %s.',char(var.dates(end)-leadlagtable{i,4}))];
error(msg)
end
eval(sprintf('%s = var;',current_variable));
end
end
% Check that the recursion is assigning something to a variable
equal_id = strfind(EXPRESSION,'=');
if isempty(equal_id)
error('dseries::from: Wrong syntax! The expression following the DO keyword must be an assignment (missing equal symbol).')
end
if isequal(length(equal_id),1)
% Get the name of the assigned variable (with time index)
assignedvariablename = regexpi(EXPRESSION(1:equal_id-1), '\w*\(t\)|\w*\(t\-\d\)|\w*\(t\+\d\)|\w*\.\w*\(t\)|\w*\.\w*\(t\-\d\)|\w*\.\w*\(t\+\d\)','match');
if isempty(assignedvariablename)
error('dseries::from: Wrong syntax! The expression following the DO keyword must be an assignment (missing variable before the equal symbol).')
end
if length(assignedvariablename)>1
error('dseries::from: No more than one variable can be assigned!')
end
% Check if the model is static
start = regexpi(assignedvariablename{1},'\(t\)|\(t\-\d\)|\(t\+\d\)');
index = assignedvariablename{1}(start:end);
assignedvariablename = assignedvariablename{1}(1:start-1);
indva = strmatch(assignedvariablename, leadlagtable(:,1));
dynamicmodel = ~isempty(regexpi(EXPRESSION(equal_id:end), ...
sprintf('%s\\(t\\)|%s\\(t\\-\\d\\)|%s\\(t\\+\\d\\)',assignedvariablename,assignedvariablename,assignedvariablename),'match'));
% Check that the dynamic model for the endogenous variable is not forward looking.
if dynamicmodel
indum = index2num(index);
if indum<leadlagtable{indva,4}
error('dseries::from: It is not possible to simulate a forward looking model!')
end
end
% Check that the assigned variable does not depend on itself (the assigned variable can depend on its past level but not on the current level).
if dynamicmodel
tmp = regexpi(EXPRESSION(equal_id+1:end), ...
sprintf('%s\\(t\\)|%s\\(t\\-\\d\\)|%s\\(t\\+\\d\\)',assignedvariablename,assignedvariablename,assignedvariablename),'match');
tmp = cellfun(@extractindex, tmp);
tmp = cellfun(@index2num, tmp);
if ~all(tmp(:)<indum)
error(sprintf('dseries::from: On the righthand side, the endogenous variable, %s, must be indexed by %s at most.',assignedvariablename,num2index(indum-1)))
end
end
else
error('dseries::from: Not yet implemented! Only one assignment is allowed in the FROM-TO-DO statement.')
end
% Put all the variables in a unique dseries object.
list_of_variables = leadlagtable{1,1};
for i=2:number_of_variables
list_of_variables = [list_of_variables, ',' leadlagtable{i,1}];
end
eval(sprintf('tmp = [%s];', list_of_variables));
% Get base time index
t1 = find(d1==tmp.dates);
t2 = find(d2==tmp.dates);
% Get data
data = tmp.data;
% Isolate the (potential) parameters in the expression to be evaluated
[~, TMP314] = strsplit(expression,'([0-9]*\.[0-9]*|\w*)','DelimiterType','RegularExpression','CollapseDelimiters',false);
% Here I remove the numbers (TMP314 -> TMP314159).
TMP3141 = regexp(TMP314,'(([0-9]*\.[0-9]*)|([0-9]*))','match');
TMP31415 = find(cellfun(@isempty,TMP3141));
TMP314159 = TMP314(TMP31415);
if dynamicmodel
% Transform EXPRESSION by replacing calls to the dseries objects by references to data.
for i=1:number_of_variables
EXPRESSION = regexprep(EXPRESSION,sprintf('%s\\(t\\)',leadlagtable{i,1}),sprintf('data(t,%s)',num2str(i)));
for j=1:length(leadlagtable{i,5})
lag = leadlagtable{i,5}(j);
EXPRESSION = regexprep(EXPRESSION,sprintf('%s\\(t-%s\\)',leadlagtable{i,1},num2str(lag)), ...
sprintf('data(t-%s,%s)',num2str(lag),num2str(i)));
end
for j=1:length(leadlagtable{i,6})
lead = leadlagtable{i,6}(j);
EXPRESSION = regexprep(EXPRESSION,sprintf('%s\\(t+%s\\)',leadlagtable{i,1},num2str(lead)), ...
sprintf('data(t+%s,%s)',num2str(lead),num2str(i)));
end
end
% Get values for the parameters (if any)
if ~isempty(TMP314159)
for i=1:length(TMP314159)
wordcandidate = TMP314159{i};
try % If succesful, word is a reference to a variable in the caller function/script.
thiswordisaparameter = evalin('caller', wordcandidate);
eval(sprintf('%s = thiswordisaparameter;',wordcandidate));
catch
% I assume that word is a reference to a function.
end
end
end
% Do the job. Evaluate the recursion.
eval(sprintf('for t=%s:%s, %s; end',num2str(t1),num2str(t2),EXPRESSION));
else
% Transform EXPRESSION by replacing calls to the dseries objects by references to data.
for i=1:number_of_variables
EXPRESSION = regexprep(EXPRESSION,sprintf('%s\\(t\\)',leadlagtable{i,1}), ...
sprintf('data(%s:%s,%s)',num2str(t1),num2str(t2),num2str(i)));
for j=1:length(leadlagtable{i,5})
lag = leadlagtable{i,5}(j);
EXPRESSION = regexprep(EXPRESSION,sprintf('%s\\(t-%s\\)',leadlagtable{i,1},num2str(lag)), ...
sprintf('data(%s:%s,%s)',num2str(t1-lag),num2str(t2-lag),num2str(i)));
end
for j=1:length(leadlagtable{i,6})
lead = leadlagtable{i,6}(j);
EXPRESSION = regexprep(EXPRESSION,sprintf('%s\\(t+%s\\)',leadlagtable{i,1},num2str(lead)), ...
sprintf('data(%s:%s,%s)',num2str(t1-lead),num2str(t2-lead),num2str(i)));
end
end
% Transform some operators (^ -> .^, / -> ./ and * -> .*)
EXPRESSION = strrep(EXPRESSION,'^','.^');
EXPRESSION = strrep(EXPRESSION,'*','.*');
EXPRESSION = strrep(EXPRESSION,'/','./');
% Get values for the parameters (if any)
if ~isempty(TMP314159)
for i=1:length(TMP314159)
wordcandidate = TMP314159{i};
try % If succesful, word is a reference to a variable in the caller function/script.
thiswordisaparameter = evalin('caller', wordcandidate);
eval(sprintf('%s = thiswordisaparameter;',wordcandidate));
catch
% I assume that word is a reference to a function.
end
end
end
% Do the job. Evaluate the static expression.
eval(sprintf('%s;',EXPRESSION));
end
% Put assigned variable back in the caller workspace...
if isempty(strfind(assignedvariablename,'.'))
eval(sprintf('assignin(''caller'', ''%s'', dseries(data(:,indva),%s.init,%s.name,%s.tex));', ...
assignedvariablename,assignedvariablename,assignedvariablename,assignedvariablename))
else
DATA = num2cell(data(:,indva));
strdata = sprintf('%f ', DATA{:});
evalin('caller',sprintf('%s = dseries(transpose([%s]),%s.init,%s.name,%s.tex)', ...
assignedvariablename,strdata,assignedvariablename,assignedvariablename,assignedvariablename))
end
function msg = get_error_message_0(msg)
if ~nargin
msg = sprintf('Wrong syntax! The correct syntax is:\n\n');
else
msg = [msg, sprintf('The correct syntax is:\n\n')];
end
msg = [msg, sprintf(' from d1 to d2 do SOMETHING\n\n')];
msg = [msg, sprintf('where d1<d2 are dates objects, and SOMETHING is a recursive expression involving dseries objects.')];
function index = extractindex(str)
index = regexpi(str,'\(t\)|\(t\-\d\)|\(t\+\d\)','match');
function i = index2num(id)
if isequal('(t)',id)
i = 0;
return
end
if isequal('-',id(3))
i = - str2num(id(4:end-1));
else
i = str2num(id(4:end-1));
end
function id = num2index(i)
if isequal(i,0)
id = '(t)';
return
end
if i<0
id = ['(t-' int2str(abs(i)) ')'];
else
id = ['(t+' int2str(i) ')'];
end