diff --git a/doc/dseries-and-reporting/dseriesReporting.tex b/doc/dseries-and-reporting/dseriesReporting.tex index 32155c32c..441888cca 100644 --- a/doc/dseries-and-reporting/dseriesReporting.tex +++ b/doc/dseries-and-reporting/dseriesReporting.tex @@ -1,6 +1,6 @@ \documentclass[10pt]{beamer} -\usepackage{tikz} +\usepackage{tikz, hyperref, alltt} \usetikzlibrary{positioning,shapes,shadows,arrows} \tikzstyle{abstract}=[rectangle, rounded corners, draw=black, anchor=north, fill=blue!10, text centered, minimum height={height("Gp")+2pt}, minimum width=3cm, font=\footnotesize] @@ -32,10 +32,12 @@ \setbeamertemplate{frametitle continuation}[from second] \title{Dynare Time Series \& Reporting} -\author{Houtan Bastani} +\author[Houtan Bastani]{Houtan Bastani\newline\href{mailto:houtan@dynare.org}{houtan@dynare.org}} \institute{CEPREMAP} \date{13 June 2014} +\newcommand{\myitem}{\item[$\bullet$]} + \begin{document} \begin{frame} @@ -56,11 +58,95 @@ \begin{frame}[fragile,t] \frametitle{Overview} \begin{itemize} - \item Provide support for time series in Dynare - \item Introduced in Dynare 4.4 - \item Currently only used for reporting - \item Use will increase with time (\textit{e.g.,} to be included in new estimation code) - \item NB: More complete information is included in the Dynare manual + \myitem Provide support for time series (\texttt{dseries}) + \begin{itemize} + \myitem Based on an implementation for handling dates (\texttt{dates}) + \end{itemize} + \myitem Beta version in Dynare 4.4. Mature version ready in Dynare 4.5 + \myitem Currently only used for reporting, though use will increase with time + (\textit{e.g.,} to be included in new estimation code) + \myitem Compatible with all setups that are supported by Dynare + \begin{itemize} + \myitem Windows, Mac OS X, Linux + \myitem Matlab 7.5 (R2007b) or later, Octave + \end{itemize} + \myitem NB: More complete information is included in the Dynare manual + \end{itemize} +\end{frame} + + +\subsection{A Programming Note} +\begin{frame}[fragile,t] + \frametitle{A Programming Note (1/2)} + \begin{itemize} + \myitem Time series and dates are implemented as Matlab/Octave classes + \myitem Inplace modification of instantiated objects not supported. Let me explain \dots + \begin{itemize} + \myitem A class is a template for defining objects, defining their member + variables and methods. + \begin{itemize} + \myitem \textit{e.g.,} The \texttt{dates} class defines 3 member + variables--\texttt{ndat}, \texttt{freq}, and \texttt{time}--and many + methods (analogous to functions) + \end{itemize} + \myitem An object is an instance of a specific class. For exemplary + purposes, imagine an object \texttt{X}, which is an instantiation of the + class ``Integer''. The object has value \texttt{1}: +\begin{verbatim} +>> X + +X = + + 1 +\end{verbatim} + \myitem You can call any method defined for the integer class on integer + object \texttt{X}. Imagine such a method is called + \texttt{multiplyByTwo()}. + \myitem In most object-oriented + languages, writing \texttt{X.multiplyByTwo();} will change the value + contained in \texttt{X} to \texttt{2/2} + \end{itemize} + \end{itemize} +\end{frame} + + +\begin{frame}[fragile,t] + \frametitle{A Programming Note (2)} + \begin{itemize} + \item[] \begin{itemize} + \myitem But! For Matlab/Octave's implementation of classes this is not the + case as it does not support inplace modification +\begin{verbatim} +>> X.multiplyByTwo() + +ans = + + 2 + +>> X + +X = + + 1 +\end{verbatim} + \end{itemize} + \end{itemize} +\end{frame} + +\begin{frame}[fragile,t] + \frametitle{A Programming Note (concluded)} + \begin{itemize} + \item[] \begin{itemize} + \myitem To get the desired change, you must overwrite \texttt{X} +\begin{verbatim} +>> X=X.multiplyByTwo() + +X = + + 2 +\end{verbatim} +\myitem Keep this in mind when using Dynare dates, time series, and reporting + \end{itemize} \end{itemize} \end{frame} @@ -69,10 +155,10 @@ \begin{frame}[fragile,t] \frametitle{Syntax} \begin{itemize} - \item Two components of a time series: + \myitem Two components of a time series: \begin{itemize} - \item A time component. In Dynare: \texttt{dates} - \item A data component mapped to time. In Dynare: \texttt{dseries} + \myitem A time component. In Dynare: \texttt{dates} + \myitem A data component mapped to time. In Dynare: \texttt{dseries} \end{itemize} \end{itemize} \end{frame} @@ -81,160 +167,222 @@ \begin{frame}[fragile,t] \frametitle{\texttt{dates} Syntax} \begin{itemize} - \item The \texttt{dates} command creates an object that represents at least one date at a given frequency + \myitem The \texttt{dates} command creates an object that represents at least one date at a given frequency + \myitem A \texttt{dates} object contains 3 members (fields): \begin{itemize} - \item Yearly: \texttt{`Y', `y', 1} - \item Quarterly: \texttt{`Q', `q', 4} - \item Monthly: \texttt{`M', `m', 12} - \item Weekly: \texttt{`W', `w', 52} - \end{itemize} - \item It has two slightly different syntaxes - \begin{itemize} - \item One for inclusion in \texttt{.m} files - \item One for inclusion in \texttt{.mod} files (simplified using the preprocessor) - \begin{itemize} - \item To prevent date translation, escape the date with `\texttt{\$}' (\textit{e.g.,} \texttt{\$2020y}) - \end{itemize} - \end{itemize} - \item Minimal restrictions on dates. Can be - \begin{itemize} - \item Negative - \item Empty - \item Noncontiguous - \end{itemize} - \item Can index a \texttt{dates} object. If \texttt{t} is a \texttt{dates} object, then - \begin{itemize} - \item \texttt{t(1) \% refers to the first date} - \item \texttt{t(1:3) \% refers to the first three dates} - \item \texttt{t([1,4,5]) \% refers to the first, fourth, and fifth dates} + \myitem{\textbf{\texttt{freq}}}: 1 (Annual), 4 (Quarterly), 12 (Monthly), 52 (Weekly) + \myitem{\textbf{\texttt{ndat}}}: The number of dates + \myitem{\textbf{\texttt{time}}}: An \texttt{ndat$\times$2} matrix; the 1\textsuperscript{st} col is the year and the 2\textsuperscript{nd} col is the period \end{itemize} + \myitem \texttt{dseries} members cannot be modified. Thus, this is not allowed: +\begin{alltt} + >> dd.freq = 12; +\end{alltt} \end{itemize} \end{frame} \begin{frame}[fragile,t] - \frametitle{Creating a new \texttt{dates} object} + \frametitle{Creating a new \texttt{dates} object in Matlab/Octave} \begin{itemize} - \item A single date: - \begin{itemize} - \item In a \texttt{.m} file: \texttt{t = dates(`1999y');} - \item In a \texttt{.mod} file: \texttt{t = 1999y;} - \end{itemize} - \item A date range: - \begin{itemize} - \item In a \texttt{.m} file: \texttt{dr = dates(`1999y'):dates(`2020y');} - \item In a \texttt{.mod} file: \texttt{dr = 1999y:2020y;} - \end{itemize} - \item In what follows, \texttt{t} and \texttt{dr} are as above. Operations that assign to \texttt{t} and \texttt{dr} keep the value thereafter. + \myitem{A single date} +\begin{alltt} + >> t = dates(`1999y'); +\end{alltt} + \myitem{Multiple dates} +\begin{alltt} + >> t = dates(`1999q1', `2020q2', `-190q3'); +\end{alltt} +Notice that noncontiguous and negative dates are possible + \myitem{A date range} +\begin{alltt} + >> dr = dates(`1999y'):dates(`2020y'); +\end{alltt} + \myitem Can also create \texttt{dates} programatically +\begin{alltt} + >> t = dates(4, [1990; 1990; 1978], [1; 2; 3]) + t = +\end{alltt} + \myitem Can specify an empty \texttt{dates}\dots +\begin{alltt} + >> qq = dates(`Q'); + >> qq = dates(4); +\end{alltt} + \myitem \dots and use it to instantiate new \texttt{dates} +\begin{alltt} + >> t = qq(1990, 1); + t = +\end{alltt} +\begin{alltt} + >> t = qq([1990; 1990; 1978], [1; 2; 3]) + t = +\end{alltt} + \end{itemize} +\end{frame} + + + +\begin{frame}[fragile,t] + \frametitle{Creating a new \texttt{dates} object in a \texttt{.mod} file} + \begin{itemize} + \myitem The preprocessor allows for syntax simplification when + \texttt{dates} are in \texttt{.mod} files + \myitem{A single date} \texttt{t = 1999y;} +\begin{alltt} + \(\Rightarrow\) t = dates(`1999y'); +\end{alltt} + \myitem{Multiple dates} \texttt{t = [1999q1 2020q2 1960q3];} +\begin{alltt} + \(\Rightarrow\) t = [dates(`1999q1') dates(`2020q2') dates(`1960q3')]; +\end{alltt} + \myitem{A date range} \texttt{dr = 1999y:2020y;} +\begin{alltt} + \(\Rightarrow\) dr = dates(`1999y'):dates(`2020y'); +\end{alltt} + \myitem NB: This can cause problems when dates are included in strings. \textit{e.g.,} +\begin{alltt} + disp(`In 1999q1, ...') +\end{alltt} +would be transformed into +\begin{alltt} + disp(`In dates(`1999q1'), ...') +\end{alltt} + \myitem To fix this, simply escape any date that you don't want transformed by the preprocessor with a `\texttt{\$}' +\begin{alltt} + disp(`In $1999q1, ...') +\end{alltt} + \end{itemize} \end{frame} \begin{frame}[fragile,t] - \frametitle{Modifying \texttt{dates}} + \frametitle{Collections and Ranges of \texttt{dates} in a \texttt{.mod} file} \begin{itemize} - \item \texttt{append}: appends a date to the date - \begin{itemize} - \item \texttt{t=t.append(dates(`1900y')); \% } - \end{itemize} - \item \texttt{horzcat}: horizontal concatenation - \begin{itemize} - \item \texttt{[t t]; \% }; - \end{itemize} - \item \texttt{minus}: either the distance between two \texttt{dates} or lag one \texttt{dates} - \begin{itemize} - \item \texttt{t-t \% [0 0]'} - \item \texttt{t-[3 3]' \% } - \end{itemize} - \item \texttt{plus}: either combine two \texttt{dates} or forward one \texttt{dates} - \begin{itemize} - \item \texttt{t+t \% } - \item \texttt{t+[3 3]' \% } - \end{itemize} - \item \texttt{pop}: remove last element - \begin{itemize} - \item \texttt{t.pop(); \% } - \end{itemize} - \item \texttt{sort}: sort dates in ascending order - \begin{itemize} - \item \texttt{t.sort(); \% } - \end{itemize} - \item \texttt{uminus}: shifts dates back one period - \begin{itemize} - \item \texttt{-t; \% } - \end{itemize} - \item \texttt{unique}: removes repetitions (keeping last unique value) - \begin{itemize} - \item \texttt{t.append(dates(`1999y')).unique() \% } - \end{itemize} - \item \texttt{uplus}: shifts dates forward one period - \begin{itemize} - \item \texttt{++t; \% } - \end{itemize} + \myitem A collection of \texttt{dates} + \begin{alltt} + a = 1990Q1; b = 1957Q1; c = -52Q1; + d = [a b c]; + \(\Rightarrow\) d = + \end{alltt} + \myitem A Range of \texttt{dates} + \begin{alltt} + a = 1990Q3:1991Q2 + \(\Rightarrow\) a = + \end{alltt} + \myitem A set of regularly-spaced \texttt{dates} + \begin{alltt} + a = 1990Q3:2:1991Q2 + \(\Rightarrow\) a = + \end{alltt} \end{itemize} \end{frame} \begin{frame}[fragile,t] - \frametitle{Getting info about \texttt{dates}} + \frametitle{Comparing \texttt{dates}} \begin{itemize} - \item \texttt{char}: returns a single date as a string - \begin{itemize} - \item \texttt{t(1).char() \% 1999Y} - \end{itemize} - \item \texttt{double}: returns a floating point representation of the date - \begin{itemize} - \item \texttt{t.double() \% [1999 1900]'} - \end{itemize} - \item \texttt{freq}: returns the frequency - \begin{itemize} - \item \texttt{t.freq; \% 1} - \end{itemize} - \item \texttt{isequal}: returns true if the two arguments are equal - \begin{itemize} - \item \texttt{isequal(t,t) \% 1} - \end{itemize} - \item \texttt{length}: returns the number of dates - \begin{itemize} - \item \texttt{t.length() \% 2} - \end{itemize} - \item \texttt{max}: returns the maximum \texttt{dates} in the arguments - \begin{itemize} - \item \texttt{max(t,dr) \% } - \end{itemize} - \item \texttt{min}: returns the minimum \texttt{dates} in the arguments - \begin{itemize} - \item \texttt{min(t,dr) \% } - \end{itemize} - \item \texttt{eq, ge, gt, le, lt, ne}: returns boolean value of comparison - \begin{itemize} - \item \texttt{t==t \% [1 1]'} - \item \texttt{t>=dates(`1950y') \% [1 0]'} - \item \texttt{t$\thicksim$=dates(`1999y') \% [0 1]'} - \end{itemize} + \myitem The following comparison operators are supported: $==$, $\backsim=$, $<$, $\leq$, $>$, and $\geq$ + \myitem Compared objects must have the same frequency + \myitem Compared objects must have the same number of elements (except for singletons) + \myitem Returns the result of an element-wise comparison + \myitem Let \texttt{a=[1999W1 2020W3]}, \texttt{b=1999W1}, and \texttt{c=[1888w1 2020w3]}. Then + \begin{alltt} + >> a == b >> a > b >> c < a + + ans = ans = ans = + + 1 0 1 + 0 1 0 + \end{alltt} \end{itemize} \end{frame} + + +\begin{frame}[fragile,t] + \frametitle{Arithmetic Operations on \texttt{dates}} + \begin{itemize} + \myitem The unary \texttt{+} and \texttt{-} operators + \begin{alltt} + >> a = dates(`1999q4'); + >> +a \(\Rightarrow\) ans = 2000q1 + >> -a \(\Rightarrow\) ans = 1999q3 + >> ++-+a \(\Rightarrow\) ans = 2000q2 + \end{alltt} + \myitem The binary \texttt{+} and \texttt{-} operators + \begin{itemize} + \myitem Objects must have the same frequency + \myitem Objects must have the same number of elements (except for singletons) + \myitem \texttt{1999q4 + 2} $\Rightarrow$ \texttt{ans = 2000q2} + \myitem \texttt{1999w4 + 2} $\Rightarrow$ \texttt{ans = 1999w6} + \myitem \texttt{1999m4 - 2} $\Rightarrow$ \texttt{ans = 1999m2} + \myitem \texttt{1999m4 - 1999m2} $\Rightarrow$ \texttt{ans = 2} + \myitem \texttt{1999m4 + 1999m2} $\Rightarrow$ \texttt{ans = } + \end{itemize} + \myitem The binary \texttt{*} operator + \begin{alltt} + >> a*3 + ans = + \end{alltt} + \end{itemize} +\end{frame} + + + \begin{frame}[fragile,t] \frametitle{Set Operations on \texttt{dates}} \begin{itemize} - \item \texttt{intersect}: returns the intersection of the arguments - \begin{itemize} - \item \texttt{intersect(t,dr) \% } - \end{itemize} - \item \texttt{isempty}: returns true if the argument is empty - \begin{itemize} - \item \texttt{isempty(t) \% 0} - \item \texttt{isempty(dates()) \% 1} - \end{itemize} - \item \texttt{setdiff}: returns dates present in first arg but not in second - \begin{itemize} - \item \texttt{setdiff(t,dr) \% } - \end{itemize} - \item \texttt{union}: - \begin{itemize} - \item \texttt{union(dr,t) \% } - \end{itemize} + \myitem Let \texttt{a = [1990Q1:1991Q1 1990Q1]; b = [1990Q3:1991Q3];} + \myitem \texttt{intersect}: returns the intersection of the arguments + \begin{alltt} + >> intersect(a, b) + ans = + \end{alltt} + \myitem \texttt{setdiff}: returns dates present in first arg but not in second + \begin{alltt} + >> setdiff(a, b) + ans = + \end{alltt} + \myitem \texttt{union}: returns the union of two sets (repititions removed) + \begin{alltt} + union(a, b) + ans = + \end{alltt} + \myitem \texttt{unique()}: removes repititions from set (keeps last unique value) + \begin{alltt} + >> a.unique() + ans = + \end{alltt} + \end{itemize} +\end{frame} + + + +\begin{frame}[fragile,t] + \frametitle{Misc \texttt{dates} operations} + \begin{itemize} + \myitem Can index a \texttt{dates} object. + \begin{alltt} + >> a = dates(`2000y'):dates(`2009y'); + >> a(1) + ans = + >> a(1:3) + ans = + >> a([1,4,5]) + ans = + \end{alltt} + \myitem{\textbf{\texttt{pop()}}}: Remove last element + \begin{alltt} + >> a.pop() + ans = + \end{alltt} + \myitem{\textbf{\texttt{char()}}}: Return string representation + \begin{alltt} + >> a(1).char() + ans = 2000Y + \end{alltt} + \myitem More in the Dynare manual \end{itemize} \end{frame} @@ -246,200 +394,238 @@ \begin{frame}[fragile,t] \frametitle{\texttt{dseries} Syntax} \begin{itemize} - \item A \texttt{dseries} is composed of one or more individual series - \item All time series in a dseries must have the same frequency - \item A \texttt{dseries} runs from the earliest date to the latest date, with \texttt{NaN}'s inserted to pad the shorter series + \myitem A \texttt{dseries} is composed of one or more individual time series + \myitem All time series in a \texttt{dseries} must have the same frequency + \myitem A \texttt{dseries} runs from the earliest date to the latest date, + with \texttt{NaN}'s inserted to pad the shorter series + \myitem A \texttt{dseries} object contains 3 members: + \begin{itemize} + \myitem{\textbf{\texttt{data}}}: The data points + \myitem{\textbf{\texttt{dates}}}: The \texttt{dates} of the sample + \myitem{\textbf{\texttt{name}}}: Names of the variables + \myitem{\textbf{\texttt{nobs}}}: The number of observations + \myitem{\textbf{\texttt{vobs}}}: The number of variables + \myitem{\textbf{\texttt{tex}}}: \LaTeX\ names of the variables + \end{itemize} + \myitem \texttt{dseries} members cannot be modified \end{itemize} \end{frame} \begin{frame}[fragile,t] - \frametitle{Creating a new \texttt{dseries} object} + \frametitle{Creating a new \texttt{dseries} object (1/2)} + Load data directly \begin{itemize} - \item Load series from data file (\texttt{.csv, .xls}). Dates in first column, optional variable names in first row - \begin{itemize} - \item \texttt{ts=dseries(`data.csv');} - \end{itemize} - \item Load series from a Matlab/Octave file (\texttt{.m, .mat}). Must define the variables \texttt{INIT\_\_}, \texttt{NAMES\_\_}, and, optionally, \texttt{TEX\_\_}. - \begin{itemize} - \item \texttt{ts=dseries(`data.m');} - \end{itemize} - \item Load data directly. Here: one variable, `MyVar1', with three annual observations starting in 1999. Only first argument is required. - \begin{itemize} - \item \texttt{ts=dseries([1;2;3], `1999y', \{`MyVar1'\}, \{`MyVar\_1'\});} - \end{itemize} - \item Create an empty time series. Usefull for programatically creating a series. - \begin{itemize} - \item \texttt{ts=dseries();} - \item \texttt{ts=dseries(dates(`1999y'));} - \end{itemize} + \myitem Syntax: + \begin{alltt} + ts = dseries(DATA, INITIAL_PERIOD, NAMES, TEX_NAMES) + \end{alltt} + \myitem \textit{e.g.,} 2 variables, `MyVar1' and `MyVar2', with + 3 annual observations starting in 1999: +\small{ + \begin{alltt} + >> ts = dseries([1 2;3 4;5 6], `1999y', ... + \{`MyVar1', `MyVar2'\}, \{`MyVar\_1', 'MyVar\_2'\}); + + ts is a dseries object: + + | MyVar1 | MyVar2 + 1999Y | 1 | 2 + 2000Y | 3 | 4 + 2001Y | 5 | 6 + \end{alltt}} + \end{itemize} +\end{frame} + +\begin{frame}[fragile,t] + \frametitle{Creating a new \texttt{dseries} object (2/2)} + Load series from CSV/spreadsheet (\texttt{.csv, .xls}) or Matlab file (\texttt{.m, .mat}) + \begin{itemize} + \myitem Syntax: + \begin{alltt} + >> ts = dseries(FILENAME); + \end{alltt} + \myitem File format (\texttt{.csv, .xls}): dates in first column (using the standard format: + 199Q1 for quarterly data, 1990Y for annual data, \dots) and (optional) + variable names in the first row + \myitem File format (\texttt{.m, .mat}): must define the variables \texttt{INIT\_\_}, + \texttt{NAMES\_\_}, and, optionally, \texttt{TEX\_\_}. More info in the + manual. Data are vectors. + \begin{alltt} + INIT__ = `1999q1’; + NAMES__ = \{`cons'\}; + cons = randn(100,1); + \end{alltt} + \end{itemize} + Create an empty time series. Usefull for programatically creating a series. + \begin{itemize} + \myitem \texttt{ts=dseries();} + \myitem \texttt{ts=dseries(dates(`1999y'));} + \end{itemize} +\end{frame} + + + +\begin{frame}[fragile,t] + \frametitle{Creating subsamples from a \texttt{dseries}} + \begin{itemize} + \myitem Let \texttt{ts} be a \texttt{dseries} with $N$ variables and 5 + observations from \texttt{2000Y} to \texttt{2004Y} + \myitem To obtain a subsample from \texttt{2001Y} to \texttt{2003Y} + \begin{alltt} + ts(2001Y:2003Y) + \end{alltt} + \myitem Can also use integer indices + \begin{alltt} + start = find(ts.dates==2001Y); + ts(start:end); + \end{alltt} + \myitem NB: Integer indices cannot be used to extract a single observation + because of lag/lead notation (see below) + \end{itemize} +\end{frame} + + + +\begin{frame}[fragile,t] + \frametitle{Extracting variables from a \texttt{dseries}} + \begin{itemize} + \myitem Let + \begin{alltt} + >> ts = dseries(randn(5,6), `2000q1', ... + \{`GDP_US', `GDP_FR', `GDP_JA', ... + `CPI_US', `CPI_FR', `CPI_JA'\}); + \end{alltt} + \myitem We can extract one variable using syntax like + \begin{alltt} + >> ts.GDP_US + \end{alltt} + \myitem To get all the \texttt{GDP} variables + \begin{alltt} + >> ts\{`GDP_US', `GDP_FR', `GDP_JA'\} + \end{alltt} + \myitem A shorter way to do the same thing + \begin{alltt} + >> ts\{`GDP_@US,FR,JA@'\} + \end{alltt} + \myitem To get \texttt{GDP} \& \texttt{CPI} (NB: max 2 implicit loops) + \begin{alltt} + >> ts\{`@GDP,CPI@_@US,FR,JA@'\} + \end{alltt} \end{itemize} \end{frame} \begin{frame}[fragile,t] - \frametitle{Referencing data from a \texttt{dseries}} + \frametitle{Applying methods to \texttt{dseries}} \begin{itemize} - \item For the following, let - \begin{itemize} - \item \texttt{ts1=dseries([1;2;3], `1999y', \{`MyVar1'\}, \{`MyVar\_1'\})} - \item \texttt{ts2=dseries([4;5;6], `2000y', \{`MyVar2'\}, \{`MyVar\_2'\})} - \item \texttt{ts3=[ts1 ts2]} - \end{itemize} - \item To get \texttt{MyVar1} from \texttt{t3}, you have three options - \begin{itemize} - \item \texttt{ts3\{1\}} - \item \texttt{ts3.MyVar1} - \item \texttt{ts3\{`MyVar1'\}} - \end{itemize} - \item To select a subsample of MyVar2 from 2001 to 2002: - \begin{itemize} - \item \texttt{ts3\{2\}(dates(`2001y'):dates(`2002y'))} - \item \texttt{ts3.MyVar2(dates(`2001y'):dates(`2002y'))} - \item \texttt{ts3\{`MyVar2'\}(dates(`2001y'):dates(`2002y'))} - \end{itemize} - \item To see the data for both variables in 2000: - \begin{itemize} - \item \texttt{ts3(`2000y')} - \item \texttt{ts3\{[1 2]\}(`2000y')} - \item \texttt{ts3\{`MyVar1',`MyVar2'\}(`2000y')} - \item \texttt{ts3\{`MyVar@1,2@'\}(`2000y')} - \item \texttt{ts3\{`MyVar[1-2]'\}(`2000y')} - \end{itemize} + \myitem Suppose \texttt{ts} is as above. Then, to apply a method + (\textit{e.g.,} \texttt{log()}) to \texttt{GDP}: + \begin{alltt} + ts\{`GDP_@US,FR,JA@'\}=ts\{`GDP_@US,FR,JA@'\}.log() + \end{alltt} + \myitem To apply a method to a subsample of all the variables: + \begin{alltt} + ts(2000Q2:2000Q4) = ts(2000Q2:2000Q4).log() + \end{alltt} + \myitem To apply a method to a subsample of some of the variables: + \begin{alltt} + ts(2000Q2:2000Q4)\{`GDP_@US,FR,JA@'\} = ... + ts(2000Q2:2000Q4)\{`GDP_@US,FR,JA@'\}.log() + \end{alltt} \end{itemize} \end{frame} \begin{frame}[fragile,t] - \frametitle{Getting info about \texttt{dseries}} + \frametitle{Merging \texttt{dseries}} \begin{itemize} - \item \texttt{eq, ne}: returns boolean value of element-wise comparison; series must have same start dates and number of observations - \begin{itemize} - \item \texttt{ts1==ts3.MyVar1(dates(`1999y'):dates(`2001y')) \% [1 1 1]'} - \item \texttt{ts1$\thicksim$=ts1 \% [0 0 0]'} - \end{itemize} - \item \texttt{isempty}: returns true if series is empty - \begin{itemize} - \item \texttt{isempty(dseries()) \% 1} - \end{itemize} - \item \texttt{isequal}: returns true if the series are equal - \begin{itemize} - \item \texttt{isequal(ts1,ts1) \% 1} - \end{itemize} - \item \texttt{size}: returns number of observations by number of variables - \begin{itemize} - \item \texttt{ts3.size() \% [4 2]} - \end{itemize} + \myitem Suppose that \verb+ts+ and \verb+ds+ are two \texttt{dseries} objects + with the same variables observed on different time ranges. These + \texttt{dseries} objects can be merged using the following syntax: +\begin{verbatim} + vs = [ts; ds]; +\end{verbatim} +\myitem Suppose that \verb+ts+ and \verb+ds+ are two \texttt{dseries} objects +with different variables observed on the same or different time ranges. These +\texttt{dseries} objects can be merged using the following syntax: +\begin{verbatim} + vs = [ts, ds]; +\end{verbatim} +If \verb+ts+ and \verb+ds+ are not defined over the same time range, +the time range of \verb+vs+ will be the union of \verb+ts.dates+ and +\verb+ds.dates+, \verb+NaN+s will be added for the missing observations. \end{itemize} \end{frame} \begin{frame}[fragile,t] - \frametitle{Working with \texttt{dseries}} + \frametitle{Arithmetic Operations on \texttt{dseries}} \begin{itemize} - \item \texttt{baxter\_king\_filter}: the Baxter and King (1999) band pass filter + \myitem The binary operators \texttt{+}, \texttt{-}, \texttt{*}, + \texttt{/}, and \texttt{\string^} perform element-wise arithmetic operations on + \texttt{dseries} + \myitem They can be used with two \texttt{dseries} or one \texttt{dseries} + and a real number + \myitem Two \texttt{dseries} can have different date ranges. Non overlapping dates will be filled with \texttt{NaNs} + \myitem Take addition for example \begin{itemize} - \item \texttt{ts1.baxter\_king\_filter(high freq, low freq, K)} - \end{itemize} - \item \texttt{hpcycle}: HP Filters the \texttt{dseries}, returning the business cycle component - \begin{itemize} - \item \texttt{ts1.hptrend(lambda) \% lambda = 1600 by default} - \end{itemize} - \item \texttt{hptrend}: HP Filters the \texttt{dseries}, returning the trend component - \begin{itemize} - \item \texttt{ts1.hptrend(lambda) \% lambda = 1600 by default} - \end{itemize} - \item \texttt{qdiff}: Quarterly difference; works on quarterly, monthly, and weekly series - \begin{itemize} - \item \texttt{ts1.qdiff()} - \end{itemize} - \item \texttt{qgrowth}: Quarterly growth rate: $\frac{ts_t-ts_{t-1}}{ts_{t-1}}$. Works on quarterly, monthly, and weekly series - \begin{itemize} - \item \texttt{ts1.qgrowth()} - \end{itemize} - \item \texttt{yidff}: Yearly difference; works on yearly, quarterly, monthly, and weekly series - \begin{itemize} - \item \texttt{ts1.ydiff()} - \end{itemize} - \item \texttt{ygrowth}: Annual growth rate: $\frac{ts_t-ts_{t-1}}{ts_{t-1}}$. Works on yearly ($t-1$), quarterly ($t-4$), monthly ($t-12$), and weekly ($t-52$) series - \begin{itemize} - \item \texttt{ts1.ygrowth()} + \myitem Let + \begin{alltt} +>> ts0 = dseries(ones(2,2)*2, `2000W1', \{`MyVar1', `MyVar2'\}); +>> ts1 = dseries(ones(3,2)*3, `2000W2', \{`YrVar1', `YrVar2'\}); +>> ds = ts0.MyVar1; + \end{alltt} + \myitem \texttt{ts0+3} will add \texttt{3} to every element in \texttt{ts0} + \myitem \texttt{ts0+[3 4]} will add \texttt{3} to every element in \texttt{ts0.MyVar1} and \texttt{4} to every element in \texttt{ts0.MyVar2} + \myitem \texttt{ts0+ts1} will add \texttt{ts0.MyVar1} to \texttt{ts1.YrVar1} and \texttt{ts0.MyVar2} to \texttt{ts1.YrVar2}. Only \texttt{2000W2} will contain the value \texttt{5} for both variables. All other points will contain \texttt{NaN} + \myitem \texttt{ts0+ds} will add \texttt{ds} to \texttt{ts0.MyVar1} and \texttt{ts0.MyVar2}. All values will contain \texttt{4} \end{itemize} \end{itemize} \end{frame} + +\begin{frame}[fragile,t] + \frametitle{Leads and Lags with \texttt{dseries} (1/2)} + \begin{itemize} + \myitem Let + \begin{alltt} + >> ts = dseries([1:4]'); + \end{alltt} + \myitem Then + \begin{alltt} + >> ts.lead() >> ts.lag() + + ans is a dseries object: ans is a dseries object: + + | lead(Variable_1,1) | lag(Variable_1,1) + 1Y | 2 1Y | NaN + 2Y | 3 2Y | 1 + 3Y | 4 3Y | 2 + 4Y | NaN 4Y | 3 + \end{alltt} + \end{itemize} +\end{frame} + + \begin{frame}[fragile,t] - \frametitle{Operations on \texttt{dseries}} + \frametitle{Leads and Lags with \texttt{dseries} (2/2)} \begin{itemize} - \item \texttt{abs}: The absolute value of each data point - \item \texttt{cumprod}/\texttt{cumsum}: Cumulative product/sum - \item \texttt{exp}/\texttt{log}: The exponential/log for each data point - \item \texttt{mrdivide}/\texttt{mtimes}: Division/Multiplication - \item \texttt{minus}/\texttt{plus}: Subtraction/Addition - \item \texttt{mpower}: Power + \myitem You can lead/lag a \texttt{dseries} by more than one period \begin{itemize} - \item \texttt{ts1-ts2} - \item \texttt{ts1-3} - \item \texttt{ts1-[1:3]'} + \myitem \texttt{ts.lead(k)} where $k\in\mathbb{Z}$ + \myitem \texttt{ts.lag(k)} where $k\in\mathbb{Z}$ \end{itemize} - \item \texttt{lag}/\texttt{lead}: lag/lead the series + \myitem A shorthand syntax is available as well \begin{itemize} - \item \texttt{ts1(-1)}/\texttt{ts1(2)} - \item \texttt{ts1.lag()}/\texttt{ts1.lead(2)} - \end{itemize} - \item \texttt{uminus}: Equivalent to multiplying by $-1$. - \begin{itemize} - \item \texttt{-ts1} + \myitem Lead: \texttt{ts(k)} where $k\in\mathbb{Z}$ + \myitem Lag: \texttt{ts(-k)} where $k\in\mathbb{Z}$ \end{itemize} \end{itemize} \end{frame} -\begin{frame}[fragile,t] - \frametitle{Modifying \texttt{dates}} - \begin{itemize} - \item \texttt{align}: Makes both series cover the same time range by padding with \texttt{NaN}'s - \begin{itemize} - \item \texttt{[ts1,ts2]=align(ts1,ts2)} - \end{itemize} - \item \texttt{chain}: - \item \texttt{horzcat}: Join two or more \texttt{dseries} - \begin{itemize} - \item \texttt{ts3=[ts1 ts2]} - \end{itemize} - \item \texttt{insert}: Inserts variables from one \texttt{dseries} into another - \begin{itemize} - \item \texttt{ts1.insert(ts2, 2)} - \end{itemize} - \item \texttt{merge}: Merge two series - \item \texttt{pop}: remove the last variable from \texttt{dseries} - \item \texttt{plot}: plot a \texttt{dseries} - \item \texttt{rename}: Rename a variable in a dseries - \begin{itemize} - \item \texttt{ts1.rename(`MyVar1',`MyFirstVar')} - \end{itemize} - \item \texttt{save}: Save a \texttt{dseries} in either \texttt{.csv} (default), \texttt{.m}, or \texttt{.mat} - \item \texttt{set\_names}: Rename all variables in a \texttt{dseries} - \begin{itemize} - \item \texttt{ts3.set\_names(`NewName1',`NewName2')} - \end{itemize} - \item \texttt{tex\_rename}: Rename the \LaTeX\ name for a given variable - \begin{itemize} - \item \texttt{ts1.tex\_rename(`MyVar1',`MyVar\textbackslash\_1')} - \end{itemize} - \item \texttt{vertcat}: Add more observations to existing \texttt{dseries} - \begin{itemize} - \item \texttt{[ts1; dseries(4,`2002y',`MyVar1')]} - \end{itemize} - \end{itemize} -\end{frame} - - - -\subsection{Examples} - - % @@ -447,67 +633,68 @@ % \section{Reporting} \subsection{Overview} -\begin{frame} +\begin{frame}[fragile,t] \frametitle{Overview} \begin{itemize} - \item Introduced in Dynare 4.4 - \item Introduce reporting functionality to Dynare + \myitem Beta version in Dynare 4.4. Mature version ready in Dynare 4.5 + \myitem Introduce reporting functionality to Dynare \begin{itemize} - \item Input: \texttt{dseries} - \item Output: \LaTeX\ report \& compiled \texttt{.pdf} + \myitem Input: \texttt{dseries} + \myitem Output: \LaTeX\ report \& compiled \texttt{.pdf} \end{itemize} - \item Graphs and Tables are modular + \myitem Graphs and Tables are modular \begin{itemize} - \item Can easily be included in another document + \item[$\Rightarrow$] Can easily be included in another document \end{itemize} - \item Graphs are produced in Ti$k$Z/PGFPlots (standard in a TeX distribution) + \myitem Graphs are produced in Ti$k$Z/PGFPlots (standard in a \TeX\ distribution) \begin{itemize} - \item Scales well - \item Formating follows that of enclosing document + \myitem Scales well + \myitem Formating follows that of enclosing document \end{itemize} - \item Dynare provides a subset of the many Ti$k$Z options + \myitem Dynare provides a subset of the many Ti$k$Z options \begin{itemize} - \item You can easily modify the Ti$k$Z graph if the option you want is not in Dynare + \myitem You can easily modify the Ti$k$Z graph if the option you want is not in Dynare \end{itemize} - \item Works with Matlab \& Octave - \item Works approximately 5 times faster than Iris reporting - \item NB: Must install a \LaTeX\ distribution to compile reports + \myitem Works with Matlab \& Octave + \myitem Works much faster than similar softawre + \myitem NB: Must install a \LaTeX\ distribution to compile reports \begin{itemize} - \item Windows: MiKTeX \url{http://miktex.org} - \item Mac OS X: MacTeX \url{http://tug.org/mactex} - \item Linux: TeX Live (from your package manager) + \myitem On Windows use MiKTeX: \url{http://miktex.org} + \myitem On Mac OS X use MacTeX: \url{http://tug.org/mactex} + \myitem On Linux use \TeX\ Live: available from your package manager \end{itemize} \end{itemize} \end{frame} -\begin{frame} +\begin{frame}[fragile,t] \frametitle{How Reporting Works} \begin{itemize} - \item Reports are created command by command + \myitem Reports are created command by command \begin{itemize} - \item Hence the order of commands matters + \myitem Hence the order of commands matters \end{itemize} - \item All reporting commands act on the previously added object until an object of greater or equal hierarchy is added (see next slide) + \myitem All reporting commands act on the previously added object until an object of greater or equal hierarchy is added (see next slide) \begin{itemize} - \item \textit{e.g.,} Once you add a \texttt{Page} to your report with the \texttt{addPage()} command, every \texttt{Section} you add via the \texttt{addSection()} command will be placed on this page. Only when you add another \texttt{Page} will items go on a new page. - \item This will become more clear with an example + \myitem \textit{e.g.,} Once you add a \texttt{Page} to your report with the \texttt{addPage()} command, every \texttt{Section} you add via the \texttt{addSection()} command will be placed on this page. Only when you add another \texttt{Page} will items go on a new page. + \myitem This will become more clear with an example \end{itemize} - \item Options to reporting commands are passed in option name/value pairs + \myitem Options to reporting commands are passed in option name/value pairs \begin{itemize} - \item \textit{e.g.,} \texttt{addPage(`title', \{`Page Title', `Page Subtitle'\})} + \myitem \textit{e.g.,} \texttt{addPage(`title', \{`Page Title', `Page Subtitle'\})} \end{itemize} \end{itemize} \end{frame} -\begin{frame} +\begin{frame}[fragile,t] \frametitle{Reporting Class Hierarchy} \begin{itemize} - \item Class names on the top half of the box, constructor names on the bottom - \item Arrows represent what the new object can be added to + \myitem Class names on the top half of the box, constructor names on the bottom + \myitem Arrows represent what the new object can be added to; objects in ellipses are treated a bit differently (explained below) \end{itemize} \begin{center} + \scriptsize{ \begin{tikzpicture}[ node distance = .45cm, auto, @@ -528,22 +715,30 @@ \textbf{Section} \nodepart{second}\texttt{addSection(...);} }; - \node (Vspace) [abstract, rectangle split, rectangle split parts=2, below=of Section] + \node (Paragraph) [abstract, ellipse split, rectangle split parts=2, right=of Section] + { + \textbf{Paragraph} + \nodepart{lower}\texttt{addParagraph(...);} + }; + \node (Vspace) [abstract, ellipse split, rectangle split parts=2, left=of Section] { \textbf{Vspace} - \nodepart{second}\texttt{addVspace(...);} + \nodepart{lower}\texttt{addVspace(...);} }; - \node (Graph) [abstract, rectangle split, rectangle split parts=2, left=of Vspace] + \node (Spacer) [abstract, rectangle split, rectangle split parts=2, opacity=0, below=of Section] + { + }; + \node (Graph) [abstract, rectangle split, rectangle split parts=2, left=of Spacer] { \textbf{Graph} \nodepart{second}\texttt{addGraph(...);} }; - \node (Table) [abstract, rectangle split, rectangle split parts=2, right=of Vspace, text height=] + \node (Table) [abstract, rectangle split, rectangle split parts=2, right=of Spacer, text height=] { \textbf{Table} \nodepart{second}\texttt{addTable(...);} }; - \node (Series) [abstract, rectangle split, rectangle split parts=2, below=of Vspace] + \node (Series) [abstract, rectangle split, rectangle split parts=2, below=of Spacer] { \textbf{Series} \nodepart{second}\texttt{addSeries(...);} @@ -552,74 +747,122 @@ \draw [line] (Series) to node { } (Graph); \draw [line] (Table) to node { } (Section); \draw [line] (Graph) to node { } (Section); + \draw [line] (Paragraph) to node { } (Section); \draw [line] (Vspace) to node { } (Section); \draw [line] (Section) to node { } (Page); \draw [line] (Page) to node { } (Report); - \end{tikzpicture} + \end{tikzpicture}} \end{center} \end{frame} \subsection{Syntax} -\begin{frame} - \frametitle{Reporting Syntax} +\begin{frame}[fragile,t] + \frametitle{Reporting Syntax (1/3)} \begin{itemize} - \item \texttt{report(\ldots)}: Create a report + \myitem \texttt{report(\ldots)}: Create a report \begin{itemize} - \item Options: \texttt{compiler}, \texttt{showDate}, \texttt{fileName}, \texttt{margin}, \texttt{marginUnit}, \texttt{orientation}, \texttt{paper}, \texttt{title} + \myitem \textbf{Options}: \texttt{compiler}, \texttt{showDate}, \texttt{fileName}, \texttt{margin}, \texttt{marginUnit}, \ldots + \begin{alltt} +>> rep = report(`title', `Dynare Summer School 2014', ... + `fileName', `myDynareReport.tex'); + \end{alltt} \end{itemize} - \item \texttt{addPage(\ldots)}: Add a page to the \texttt{Report} + \myitem \texttt{addPage(\ldots)}: Add a page to the \texttt{Report} \begin{itemize} - \item Options: \texttt{footnote}, \texttt{orientation}, \texttt{paper}, \texttt{title}, \texttt{titleFormat} + \myitem \textbf{Options}: \texttt{footnote}, \texttt{orientation}, \texttt{paper}, \texttt{title}, \texttt{titleFormat} + \begin{alltt} +>> rep = rep.addPage(`title', \{`Page Title', `Page Subtitle'\}, ... + `titleFormat', \{`\textbackslash{}large\textbackslash{}bfseries', `\textbackslash{}large'\}); + \end{alltt} \end{itemize} - \item \texttt{addSection(\ldots)}: Add a section to the current \texttt{Page} + \myitem \texttt{addSection(\ldots)}: Add a section to the current \texttt{Page} \begin{itemize} - \item You can think of a section as a matrix. As graphs and/or tables are added section, it fills up from left to right. Once you have added \texttt{cols} objects, a new row is started. - \item Options: \texttt{cols}, \texttt{height} + \myitem You can think of a section as a matrix. As graphs and/or tables are added to a section, it fills up from left to right. Once you have added \texttt{cols} objects, a new row is started. + \myitem \textbf{Options}: \texttt{cols}, \texttt{height} + \begin{alltt} +>> rep = rep.addSection(`cols', 3); + \end{alltt} \end{itemize} \end{itemize} \end{frame} -\begin{frame} - \frametitle{Reporting Syntax (continued)} +\begin{frame}[fragile,t] + \frametitle{Reporting Syntax (2/3)} \begin{itemize} - \item \texttt{addGraph(\ldots)}: Add a graph to the current \texttt{Section} + \myitem \texttt{addVspace(\ldots)}: Add a vertical space to the current \texttt{Section}. \begin{itemize} - \item Options: \texttt{data}, \texttt{graphDirName}, \texttt{graphName}, \texttt{graphSize}, \texttt{height}, \texttt{showGrid}, \texttt{showLegend}, \texttt{showLegendBox}, \texttt{legendLocation}, \texttt{legendOrientation}, \texttt{legendFontSize}, \texttt{seriesToUse}, \texttt{shade}, \texttt{shadeColor}, \texttt{shadeOpacity}, \texttt{title}, \texttt{titleFormat}, \texttt{width}, \texttt{xlabel}, \texttt{ylabel}, \texttt{xAxisTight}, \texttt{xrange}, \texttt{xTicks}, \texttt{xTickLabels}, \texttt{xTickLabelAnchor}, \texttt{xTickLabelRotation}, \texttt{yAxisTight}, \texttt{yrange}, \texttt{showZeroLine} + \myitem If the row has been completely filled in, this adds space between this row and the next row. If not, this adds space at the end of the \texttt{Section} and closes it; in other words, in this situation if you want to add more graphs, you'd have to create a new \texttt{Section} first. + \myitem \textbf{Options}: \texttt{hline}, \texttt{number} + \begin{alltt} +>> rep = rep.addVspace(`hline', 2, `number', 3); + \end{alltt} \end{itemize} - \item \texttt{addTable(\ldots)}: Add a table to the current \texttt{Section} + \myitem \texttt{addParagraph(\ldots)}: Add text to the current \texttt{Section} \begin{itemize} - \item Options: \texttt{data}, \texttt{showHlines}, \texttt{precision}, \texttt{range}, \texttt{seriesToUse}, \texttt{tableDirName}, \texttt{tableName}, \texttt{title}, \texttt{titleFormat}, \texttt{vlineAfter}, \texttt{vlineAfterEndOfPeriod}, \texttt{showVlines} - \end{itemize} - \item \texttt{addSeries(\ldots)}: Add a series to the current \texttt{Graph} or \texttt{Table} - \begin{itemize} - \item \texttt{data}, \texttt{graphLineStyle}, \texttt{graphLineWidth}, \texttt{graphMarker}, \texttt{graphMarkerEdgeColor}, \texttt{graphMarkerFaceColor}, \texttt{graphMarkerSize}, \texttt{tableDataRhs}, \texttt{tableRowColor}, \texttt{tableRowIndent}, \texttt{tableShowMarkers}, \texttt{tableAlignRight}, \texttt{tableMarkerLimit}, \texttt{tableNegColor}, \texttt{tablePosColor}, \texttt{tableSubSectionHeader}, \texttt{zeroTol} - \end{itemize} - \item Options: \texttt{addVspace(\ldots)}: Add a vertical space to the current \texttt{Section} - \begin{itemize} - \item \texttt{hline}, \texttt{number} + \myitem To add anything other than a paragraph (or multiple paragraphs) to a \texttt{Section}, you must add a new \texttt{Section} to the page + \myitem \textbf{Options}: \texttt{balancedCols}, \texttt{cols}, \texttt{heading}, \texttt{index}, \texttt{text} + \begin{alltt} +>> rep = rep.addParagraph(`text', `Lorem ipsum\textbackslash{}ldots\textbackslash{}newline'); + \end{alltt} \end{itemize} \end{itemize} \end{frame} -\begin{frame} +\begin{frame}[fragile,t] + \frametitle{Reporting Syntax (3/3)} + \begin{itemize} + \myitem \texttt{addGraph(\ldots)}: Add a graph to the current \texttt{Section} + \begin{itemize} + \myitem \textbf{Options}: \texttt{data}, \texttt{graphDirName}, \texttt{graphName}, \texttt{graphSize}, \texttt{height}, \ldots + \begin{alltt} +>> rep = rep.addGraph(`title', `Headline Inflation (y/y)', ... + `xrange', prange, ... + `shade', srange, ... + `showZeroline', true); + \end{alltt} + \end{itemize} + \myitem \texttt{addTable(\ldots)}: Add a table to the current \texttt{Section} + \begin{itemize} + \myitem \textbf{Options}: \texttt{data}, \texttt{showHlines}, \texttt{precision}, \texttt{range}, \texttt{seriesToUse}, \ldots + \begin{alltt} +>> rep = rep.addTable(`title', \{`Real GDP Growth',`subtitle 1'\}, ... + `range', larange, ... + `vlineAfter', dates(`2011y')); + \end{alltt} + \end{itemize} + \myitem \texttt{addSeries(\ldots)}: Add a series to the current \texttt{Graph} or \texttt{Table} + \begin{itemize} + \myitem \textbf{Options}: \texttt{data}, \texttt{graphHline}, \texttt{graphLegendName}, \texttt{graphLineColor}, \ldots + \begin{alltt} +>> rep = rep.addSeries(`data', db\_q.LRPOIL\_WORLD, ... + `graphLineColor', `blue', ... + `graphLineWidth', 1.5, ... + `graphMarker', `triangle*'); + \end{alltt} + \end{itemize} + \end{itemize} +\end{frame} + + +\begin{frame}[fragile,t] \frametitle{Output} To create a report: \begin{itemize} - \item \texttt{write()}: Writes the report to a \LaTeX\ file - \item \texttt{compile(\ldots)}: Compiles the report + \myitem \texttt{write()}: Writes the report to a \LaTeX\ file + \myitem \texttt{compile(\ldots)}: Compiles the report \begin{itemize} - \item Options: \texttt{compiler} + \myitem \textbf{Options}: \texttt{compiler} \end{itemize} \end{itemize} Report Output \begin{itemize} - \item Unless you pass the \texttt{fileName} option to \texttt{report(\ldots)}, the report will be located in your working directory with the name \texttt{report.tex}. The compiled version will be called \texttt{report.pdf}. - \item Unless you pass the \texttt{graphDirName} or \texttt{graphName} options to \texttt{addGraph(\ldots)}, your graphs will be in a subdirectory of your working directory called \texttt{tmpRepDir}. The default name will take the form \texttt{graph\_pg9\_sec1\_row1\_col5.tex} - \item The same holds for the tables (substituting `table' for `graph' above). - \item Thus you can easily modify these files and include them in another report. + \myitem Unless you pass the \texttt{fileName} option to \texttt{report(\ldots)}, the report will be located in your working directory with the name \texttt{report.tex}. The compiled version will be called \texttt{report.pdf}. + \myitem Unless you pass the \texttt{graphDirName} or \texttt{graphName} options to \texttt{addGraph(\ldots)}, your graphs will be in a subdirectory of your working directory called \texttt{tmpRepDir}. The default name will take the form \texttt{graph\_pg9\_sec1\_row1\_col5.tex} + \myitem The same holds for the tables (substituting `table' for `graph' above). + \myitem Thus you can easily modify these files and include them in another report. \end{itemize} \end{frame} @@ -627,10 +870,11 @@ \subsection{Examples} \section{Putting it All Together} -\begin{frame}[fragile=singleslide] +\begin{frame}[fragile=singleslide,t] \frametitle{Create Report of IRFs from \texttt{example1.mod}} \begin{itemize} - \item \texttt{example1.mod} is located in the Dynare \texttt{examples} directory + \myitem \texttt{example1.mod} is located in the Dynare \texttt{examples} directory + \myitem The lines below can be added at the end of that file. \end{itemize} \begin{block}{Create \texttt{dseries} from IRFs} \begin{verbatim} @@ -643,9 +887,9 @@ \end{block} \end{frame} -\begin{frame}[fragile=singleslide] +\begin{frame}[fragile=singleslide,t] \frametitle{Create Report of IRFs from \texttt{example1.mod}} - \begin{block}{Populate Report} + \begin{block}{Populate Report (1/2)} \small{ \begin{verbatim} @#for shock in ["e", "u"] @@ -660,8 +904,21 @@ `showGrid', false, ... `showZeroLine', true); @# endfor +\end{verbatim} +} + \end{block} +\end{frame} + + +\begin{frame}[fragile=singleslide,t] + \frametitle{Create Report of IRFs from \texttt{example1.mod}} + \begin{block}{Populate Report (2/2)} +\small{ +\begin{verbatim} + report = report.addVspace(`number', 2); report = report.addSection(`cols', 1); - report = report.addTable(); + report = report.addTable(`range', 2022q1:2024q1, `precision', 5); + @# for var in endovars report = report.addSeries(`data', shock@{shock}.@{var}); @# endfor @@ -669,16 +926,14 @@ \end{verbatim} } \end{block} -\end{frame} - -\begin{frame}[fragile=singleslide] - \frametitle{Create Report of IRFs from \texttt{example1.mod}} \begin{block}{Compile Report} +\small{ \begin{verbatim} report.write(); report.compile(); \end{verbatim} +} \end{block} \end{frame} \end{document} diff --git a/doc/dynare.texi b/doc/dynare.texi index ec7ab1a24..da47c7a88 100644 --- a/doc/dynare.texi +++ b/doc/dynare.texi @@ -10965,6 +10965,7 @@ ans = @sp 1 +@anchor{tex_rename} @deftypefn{dseries} {@var{B} =} tex_rename (@var{A}, @var{name}, @var{newtexname}) Redefines the tex name of variable @var{name} to @var{newtexname} @@ -11037,13 +11038,15 @@ Computes yearly differences or growth rates. @node Reporting @chapter Reporting -Dynare provides a simple interface for creating @LaTeX{} reports, -comprised of @LaTeX{} tables and @code{PGFPLOTS/TikZ/PGF} graphs. You -can use the report as created through Dynare or pick out the pieces -(tables and graphs) you want for inclusion in your own paper. Though -Dynare provides a subset of options available through @code{PGFPLOTS}, -you can easily modify the graphs created by Dynare using the options -available in the @code{PGFPLOTS} manual. +Dynare provides a simple interface for creating @LaTeX{} reports, comprised of +@LaTeX{} tables and @code{PGFPLOTS/Ti}@i{k}@code{Z} graphs. You can use the +report as created through Dynare or pick out the pieces (tables and graphs) you +want for inclusion in your own paper. Though Dynare provides a subset of +options available through @code{PGFPLOTS/Ti}@i{k}@code{Z}, you can easily +modify the graphs created by Dynare using the options available in the +@code{PGFPLOTS/Ti}@i{k}@code{Z} manual. You can either do this manually or by +passing the options to @ref{miscTikzAxisOptions}, @ref{miscTikzAxisOptions}, or +@ref{graphMiscTikzAddPlotOptions}. Reports are created and modified by calling methods on class objects. The objects are hierarchical, with the following order (from @@ -11159,7 +11162,8 @@ command. Default: @code{`!'} @end table @end defmethod -@defmethod Report addGraph data, graphDirName, graphName, graphSize, height, showGrid, showLegend, showLegendBox, legendLocation, legendOrientation, legendFontSize, seriesToUse, shade, shadeColor, shadeOpacity, title, width, xlabel, ylabel, xAxisTight, xrange, xTicks, xTickLabels, xTickLabelAnchor, xTickLabelRotation, yAxisTight, yrange, showZeroline +@anchor{addGraph} +@defmethod Report addGraph data, graphDirName, graphName, graphSize, height, showGrid, showLegend, showLegendBox, legendLocation, legendOrientation, legendFontSize, miscTikzAxisOptions, miscTikzPictureOptions, seriesToUse, shade, shadeColor, shadeOpacity, title, width, xlabel, ylabel, xAxisTight, xrange, xTicks, xTickLabels, xTickLabelAnchor, xTickLabelRotation, yAxisTight, yrange, showZeroline Adds a @code{Graph} to a @code{Section}. @optionshead @table @code @@ -11189,8 +11193,12 @@ The height of the graph, in inches. Default: @code{4.5} Whether or not to display the major grid on the graph. Default: @code{true} +@anchor{showLegend} @item showLegend, @code{BOOLEAN} -Whether or not to display the legend. Default: @code{false} +Whether or not to display the legend. NB: Unless you use the +@ref{graphLegendName} option, the name displayed in the legend is the +@code{tex} name associated with the @code{dseries}. You can modify this +@code{tex} name by using @ref{tex_rename}. Default: @code{false} @item showLegendBox, @code{BOOLEAN} Whether or not to display a box around the legend. Default: @@ -11205,6 +11213,24 @@ Orientation of the legend. Default: @code{`horizontal'} @item legendFontSize, @code{`tiny'} | @code{`scriptsize'} | @code{`footnotesize'} | @code{`small'} | @code{`normalsize'} | @code{`large'} | @code{`Large'} | @code{`LARGE'} | @code{`huge'} | @code{`Huge'} The font size for legend entries. Default: @code{tiny} +@anchor{miscTikzAxisOptions} +@item miscTikzAxisOptions, @code{STRING} +If you are comfortable with @code{PGFPLOTS/Ti}@i{k}@code{Z}, you can use this +option to pass arguments directly to the @code{PGFPLOTS/Ti}@i{k}@code{Z} +@code{axis} environment command. Specifically to be used for desired +@code{PGFPLOTS/Ti}@i{k}@code{Z} options that have not been incorporated into +Dynare Reproting. Default: @code{empty} + +@anchor{miscTikzPictureOptions} +@item miscTikzPictureOptions, @code{STRING} +If you are comfortable with @code{PGFPLOTS/Ti}@i{k}@code{Z}, you can use this +option to pass arguments directly to the @code{PGFPLOTS/Ti}@i{k}@code{Z} +@code{tikzpicture} environment command. (@i{e.g.,} to scale the graph in the x +and y dimensions, you can pass following to this option: @code{`xscale=2.5, +yscale=0.5'}). Specifically to be used for desired +@code{PGFPLOTS/Ti}@i{k}@code{Z} options that have not been incorporated into +Dynare Reproting. Default: @code{empty} + @anchor{seriesToUse} @item seriesToUse, @code{CELL_ARRAY_STRINGS} The names of the series contained in the @code{dseries} provided to @@ -11242,8 +11268,8 @@ The x-axis label. Default: @code{none} The y-axis label. Default: @code{none} @item xAxisTight, @code{BOOLEAN} -Use a tight x axis. If false, uses pgfplots @code{enlarge x limits} to -choose appropriate axis size. Default: @code{true} +Use a tight x axis. If false, uses @code{PGFPLOTS/Ti}@i{k}@code{Z} +@code{enlarge x limits} to choose appropriate axis size. Default: @code{true} @item xrange, @code{dates} The boundary on the x-axis to display in the graph. Default: all @@ -11269,8 +11295,8 @@ Where to anchor the x tick label. Default: @code{`south'} The amount to rotate the x tick labels by. Default: @code{0} @item yAxisTight, @code{BOOLEAN} -Use a tight y axis. If false, uses pgfplots @code{enlarge y limits} to -choose appropriate axis size. Default: @code{false} +Use a tight y axis. If false, uses @code{PGFPLOTS/Ti}@i{k}@code{Z} +@code{enlarge y limits} to choose appropriate axis size. Default: @code{false} @item yrange, @code{NUMERICAL_VECTOR} The boundary on the y-axis to display in the graph, represented as a @@ -11331,14 +11357,25 @@ Whether or not to show vertical lines separating the columns. Default: @code{fal @end defmethod @anchor{addSeries} -@defmethod Report addSeries data, graphLineColor, graphLineStyle, graphLineWidth, graphMarker, graphMarkerEdgeColor, graphMarkerFaceColor, graphMarkerSize, tableDataRhs, tableRowColor, tableRowIndent, tableShowMarkers, tableAlignRight, tableNegColor, tablePosColor, tableSubSectionHeader, zeroTol -Adds a @code{Series} to a @code{Graph} or a @code{Table}. +@defmethod Report addSeries data, graphHline, graphLegendName, graphLineColor, graphLineStyle, graphLineWidth, graphMarker, graphMarkerEdgeColor, graphMarkerFaceColor, graphMarkerSize, graphMiscTikzAddPlotOptions, graphShowInLegend, graphVline, tableDataRhs, tableRowColor, tableRowIndent, tableShowMarkers, tableAlignRight, tableNegColor, tablePosColor, tableSubSectionHeader, zeroTol +Adds a @code{Series} to a @code{Graph} or a @code{Table}. NB: Options specific +to graphs begin with `@code{graph}' while options specific to tables begin with +`@code{table}'. @optionshead @table @code @item data, @code{dseries} @xref{data}. +@item graphHline, @code{DOUBLE} +Use this option to draw a horizontal line at the given value. Default: +@code{empty} + +@anchor{graphLegendName} +@item graphLegendName, @code{STRING} +The name to display in the legend for this series. Will be displayed only if +the @ref{data} option has been set. Default: the @code{tex} name of the series + @item graphLineColor, @code{`red'} | @code{`green'} | @code{`blue'} | @code{`cyan'} | @code{`magenta'} | @code{`yellow'} | @code{`black'} | @code{`gray'} | @code{`darkgray'} | @code{`lightgray'} | @code{`brown'} | @code{`lime'} | @code{`olive'} | @code{`orange'} | @code{`pink'} | @code{`purple'} | @code{`teal'} | @code{`violet'} | @code{`white'} Color to use for the series in a graph. Default: @code{`black'} @@ -11360,6 +11397,23 @@ The face color of the graph marker. Default: @code{graphLineColor} @item graphMarkerSize, @code{DOUBLE} The size of the graph marker. Default: @code{1} +@anchor{graphMiscTikzAddPlotOptions} +@item graphMiscTikzAddPlotOptions, @code{STRING} +If you are comfortable with @code{PGFPLOTS/Ti}@i{k}@code{Z}, you can use this +option to pass arguments directly to the @code{PGFPLOTS/Ti}@i{k}@code{Z} +@code{addPlots} command. (@i{e.g.,} Instead of passing the marker options +above, you can pass a string such as the following to this option: +@code{`mark=halfcircle*,mark options=@{rotate=90,scale=3@}'}). Specifically to be +used for desired @code{PGFPLOTS/Ti}@i{k}@code{Z} options that have not been +incorporated into Dynare Reproting. Default: @code{empty} + +@item graphShowInLegend, @code{BOOLEAN} +Whether or not to show this series in the legend, given that the +@ref{showLegend} option was passed to @ref{addGraph}. Default: @code{true} + +@item graphVline, @code{dates} +Use this option to draw a vertical line at a given date. Default: @code{empty} + @item tableDataRhs, @code{dseries} A series to be added to the right of the current series. Usefull for displaying aggregate data for a series. @i{e.g} if the series is @@ -11406,9 +11460,36 @@ with it. It is equivalent to adding an empty series with a name. Default: @code{''} @item zeroTol, @code{DOUBLE} -The zero tolerance. Anything smaller than @code{zeroTol} and larger -than @code{-zeroTol} will be set to zero before being -graphed. Default: @math{1e-6} +The zero tolerance. Anything smaller than @code{zeroTol} and larger than +@code{-zeroTol} will be set to zero before being graphed or written to the +table. Default: @math{1e-6} + +@end table +@end defmethod + +@defmethod Report addParagraph balancedCols, cols, heading, indent, text +Adds a @code{Paragraph} to a @code{Section}. NB: The @code{Section} can only be +comprised of @code{Paragraphs} and must only have 1 column. +@optionshead +@table @code + +@item balancedCols, @code{BOOLEAN} +Determines whether the text is spread out evenly across the columns when the +@code{Paragraph} has more than one column. Default: @code{true} + +@item cols, @code{INTEGER} +The number of columns for the @code{Paragraph}. Default: @code{1} + +@item heading, @code{STRING} +The heading for the Paragraph (like a section heading). The string must be +correct @LaTeX{} code. Default: @code{empty} + +@item indent, @code{BOOLEAN} +Whether or not to indent the paragraph. Default: @code{true} + +@item text, @code{STRING} +The paragraph itself. The string must be correct @LaTeX{} code. Default: +@code{empty} @end table @end defmethod @@ -11468,16 +11549,16 @@ rep = rep.addSection(`cols', 2); rep = rep.addGraph(`title', `Graph (1,1)', `showLegend', true, ... `xrange', dates(`2007q1'):dates(`2013q4'), ... `shade', dates(`2012q2'):dates(`2013q4')); -rep = rep.addSeries(`data', dsq@{`SERIES1'@}, `color', `b', ... +rep = rep.addSeries(`data', dsq@{`SERIES1'@}, `graphLineColor', `blue', ... `graphLineWidth', 1); -rep = rep.addSeries(`data', dsq@{`SERIES2'@}, `color', `g', ... +rep = rep.addSeries(`data', dsq@{`SERIES2'@}, `graphLineColor', `green', ... `graphLineStyle', '--', `graphLineWidth', 1.5); rep = rep.addGraph(`title', `Graph (1,2)', `showLegend', true, ... `xrange', dates(`2007q1'):dates(`2013q4'), ... `shade', dates(`2012q2'):dates(`2013q4')); -rep = rep.addSeries(`data', dsq@{`SERIES3'@}, `color', `b', ... +rep = rep.addSeries(`data', dsq@{`SERIES3'@}, `graphLineColor', `blue', ... `graphLineWidth', 1); -rep = rep.addSeries(`data', dsq@{`SERIES4'@}, `color', `g', ... +rep = rep.addSeries(`data', dsq@{`SERIES4'@}, `graphLineColor', `green', ... `graphLineStyle', '--', `graphLineWidth', 1.5); % Section 2 diff --git a/matlab/dynare_config.m b/matlab/dynare_config.m index 2ca4a6794..9701f3a88 100644 --- a/matlab/dynare_config.m +++ b/matlab/dynare_config.m @@ -57,6 +57,7 @@ addpath([dynareroot '/parallel/']) addpath([dynareroot '/particle/']) addpath([dynareroot '/gsa/']) addpath([dynareroot '/ep/']) +addpath([dynareroot '/lmmcp/']) addpath([dynareroot '/utilities/doc/']) addpath([dynareroot '/utilities/tests/']) addpath([dynareroot '/utilities/dates/']) diff --git a/matlab/dynare_solve.m b/matlab/dynare_solve.m index 3033f1143..ec2f2c946 100644 --- a/matlab/dynare_solve.m +++ b/matlab/dynare_solve.m @@ -164,6 +164,14 @@ elseif options_.solve_algo == 3 else [x,info] = csolve(func,x,[],1e-6,500,varargin{:}); end +elseif options_.solve_algo == 10 + olmmcp = options_.lmmcp; + [x,fval,exitflag] = lmmcp(func,x,olmmcp.lb,olmmcp.ub,olmmcp,varargin{:}); + if exitflag == 1 + info = 0; + else + info = 1; + end else error('DYNARE_SOLVE: option solve_algo must be one of [0,1,2,3,4,9]') end diff --git a/matlab/ep/ep_problem_2.m b/matlab/ep/ep_problem_2.m index 634ae918c..f10ccc942 100644 --- a/matlab/ep/ep_problem_2.m +++ b/matlab/ep/ep_problem_2.m @@ -26,6 +26,7 @@ i_cols_1 = pm.i_cols_1; i_cols_j = pm.i_cols_j; icA = pm.icA; i_cols_T = pm.i_cols_T; +eq_index = pm.eq_index; i_cols_p = i_cols(1:nyp); i_cols_s = i_cols(nyp+(1:ny)); @@ -92,15 +93,18 @@ for i = 1:order+1 % in first period we don't keep track of % predetermined variables i_cols_A = [i_cols_As - ny; i_cols_Af]; - A1(i_rows,i_cols_A) = A1(i_rows,i_cols_A) + weights(k)*jacobian(:,i_cols_1); + A1(i_rows,i_cols_A) = A1(i_rows,i_cols_A) + weights(k)*jacobian(eq_index,i_cols_1); else i_cols_A = [i_cols_Ap; i_cols_As; i_cols_Af]; - A1(i_rows,i_cols_A) = A1(i_rows,i_cols_A) + weights(k)*jacobian(:,i_cols_j); + A1(i_rows,i_cols_A) = A1(i_rows,i_cols_A) + weights(k)*jacobian(eq_index,i_cols_j); end else d1 = dynamic_model(z,innovation,params,steady_state,i+1); end - res(:,i,1) = res(:,i,1)+weights(k)*d1; + if any(isnan(d1)) + pause + end + res(:,i,1) = res(:,i,1)+weights(k)*d1(eq_index); end if nargout > 1 [ir,ic,v] = find(A1); @@ -121,12 +125,15 @@ for i = 1:order+1 if nargout > 1 [d1,jacobian] = dynamic_model(z,innovation,params,steady_state,i+1); i_cols_A = [i_cols_Ap; i_cols_As; i_cols_Af]; - [ir,ic,v] = find(jacobian(:,i_cols_j)); + [ir,ic,v] = find(jacobian(eq_index,i_cols_j)); nzA{i,j} = [i_rows(ir),i_cols_A(ic), v]'; else d1 = dynamic_model(z,innovation,params,steady_state,i+1); end - res(:,i,j) = d1; + if any(isnan(d1)) + pause + end + res(:,i,j) = d1(eq_index); if nargout > 1 i_cols_Af = i_cols_Af + ny; end @@ -142,13 +149,16 @@ for i = 1:order+1 if nargout > 1 [d1,jacobian] = dynamic_model(z,innovation,params,steady_state,i+1); i_cols_A = [i_cols_Ap; i_cols_As; i_cols_Af]; - [ir,ic,v] = find(jacobian(:,i_cols_j)); + [ir,ic,v] = find(jacobian(eq_index,i_cols_j)); nzA{i,j} = [i_rows(ir),i_cols_A(ic),v]'; i_cols_Af = i_cols_Af + ny; else d1 = dynamic_model(z,innovation,params,steady_state,i+1); end - res(:,i,j) = d1; + if any(isnan(d1)) + pause + end + res(:,i,j) = d1(eq_index); end i_rows = i_rows + ny; if mod(j,nnodes) == 0 @@ -172,16 +182,19 @@ for j=1:world_nbr [d1,jacobian] = dynamic_model(Y(i_rows_y,j),x,params, ... steady_state,i+1); if i < periods - [ir,ic,v] = find(jacobian(:,i_cols_j)); + [ir,ic,v] = find(jacobian(eq_index,i_cols_j)); else - [ir,ic,v] = find(jacobian(:,i_cols_T)); + [ir,ic,v] = find(jacobian(eq_index,i_cols_T)); end nzA{i,j} = [offset_r+ir,offset_c+icA(ic), v]'; else d1 = dynamic_model(Y(i_rows_y,j),x,params, ... steady_state,i+1); end - res(:,i,j) = d1; + if any(isnan(d1)) + pause + end + res(:,i,j) = d1(eq_index); i_rows_y = i_rows_y + ny; offset_c = offset_c + world_nbr*ny; offset_r = offset_r + world_nbr*ny; diff --git a/matlab/ep/extended_path.m b/matlab/ep/extended_path.m index cacf0530c..bc1b5020c 100644 --- a/matlab/ep/extended_path.m +++ b/matlab/ep/extended_path.m @@ -150,6 +150,14 @@ if options_.ep.stochastic.order > 0 pfm.nnodes = nnodes; end +% compute number of blocks +[block_nbr,pfm.world_nbr] = get_block_world_nbr(options_.ep.stochastic.algo,nnodes,options_.ep.ut.k,options_.ep.periods); +% set boundaries if mcp +[lb,ub,pfm.eq_index] = get_complementarity_conditions(M_); +options_.lmmcp.lb = repmat(lb,block_nbr,1); +options_.lmmcp.ub = repmat(ub,block_nbr,1); +pfm.block_nbr = block_nbr; + % Main loop. while (t. + + +switch algo + case 0 + world_nbr = nnodes^order; + block_nbr = 1+(nnodes^(order+1)-nnodes)/(nnodes-1)+(periods-order)*world_nbr; + case 1 + world_nbr = 1+(nnodes-1)*order; + block_nbr = (order+(nnodes-1)*(order-1)*order/2+(periods-order)* ... + world_nbr); + otherwise + error('This case is not supposed to happen') +end \ No newline at end of file diff --git a/matlab/ep/solve_stochastic_perfect_foresight_model_1.m b/matlab/ep/solve_stochastic_perfect_foresight_model_1.m index 50fb79299..d27b50d28 100644 --- a/matlab/ep/solve_stochastic_perfect_foresight_model_1.m +++ b/matlab/ep/solve_stochastic_perfect_foresight_model_1.m @@ -84,7 +84,7 @@ end % The second row block is ny x nnodes % The third row block is ny x nnodes^2 % and so on until size ny x nnodes^order -world_nbr = 1+(nnodes-1)*order; +world_nbr = pfm.world_nbr; Y = endo_simul(:,2:end-1); Y = repmat(Y,1,world_nbr); pfm.y0 = endo_simul(:,1); @@ -92,10 +92,9 @@ pfm.y0 = endo_simul(:,1); % The columns of A map the elements of Y such that % each block of Y with ny rows are unfolded column wise % number of blocks -block_nbr = (order+(nnodes-1)*(order-1)*order/2+(periods-order)*world_nbr); +block_nbr = pfm.block_nbr; % dimension of the problem dimension = ny*block_nbr; -pfm.block_nbr = block_nbr; pfm.dimension = dimension; if order == 0 i_upd_r = (1:ny*periods); @@ -143,11 +142,13 @@ pfm.i_cols_T = i_cols_T; pfm.i_upd_r = i_upd_r; pfm.i_upd_y = i_upd_y; - -options_.solve_algo = 9; options_.steady.maxit = 100; y = repmat(steady_state,block_nbr,1); +old_options = options_; +options_.solve_algo = options_.ep.solve_algo; +options_.steady.maxit = options_.ep.maxit; [y,info] = dynare_solve(@ep_problem_2,y,1,exo_simul,pfm); +options_ = old_options; if info flag = 1; err = info; diff --git a/matlab/lmmcp/catstruct.m b/matlab/lmmcp/catstruct.m new file mode 100644 index 000000000..e29d36469 --- /dev/null +++ b/matlab/lmmcp/catstruct.m @@ -0,0 +1,148 @@ +function A = catstruct(varargin) +% CATSTRUCT Concatenate or merge structures with different fieldnames +% X = CATSTRUCT(S1,S2,S3,...) merges the structures S1, S2, S3 ... +% into one new structure X. X contains all fields present in the various +% structures. An example: +% +% A.name = 'Me' ; +% B.income = 99999 ; +% X = catstruct(A,B) +% % -> X.name = 'Me' ; +% % X.income = 99999 ; +% +% If a fieldname is not unique among structures (i.e., a fieldname is +% present in more than one structure), only the value from the last +% structure with this field is used. In this case, the fields are +% alphabetically sorted. A warning is issued as well. An axample: +% +% S1.name = 'Me' ; +% S2.age = 20 ; S3.age = 30 ; S4.age = 40 ; +% S5.honest = false ; +% Y = catstruct(S1,S2,S3,S4,S5) % use value from S4 +% +% The inputs can be array of structures. All structures should have the +% same size. An example: +% +% C(1).bb = 1 ; C(2).bb = 2 ; +% D(1).aa = 3 ; D(2).aa = 4 ; +% CD = catstruct(C,D) % CD is a 1x2 structure array with fields bb and aa +% +% The last input can be the string 'sorted'. In this case, +% CATSTRUCT(S1,S2, ..., 'sorted') will sort the fieldnames alphabetically. +% To sort the fieldnames of a structure A, you could use +% CATSTRUCT(A,'sorted') but I recommend ORDERFIELDS for doing that. +% +% When there is nothing to concatenate, the result will be an empty +% struct (0x0 struct array with no fields). +% +% NOTE: To concatenate similar arrays of structs, you can use simple +% concatenation: +% A = dir('*.mat') ; B = dir('*.m') ; C = [A ; B] ; +% +% See also CAT, STRUCT, FIELDNAMES, STRUCT2CELL, ORDERFIELDS + +% for Matlab R13 and up +% version 3.0 (mar 2013) +% (c) Jos van der Geest +% email: jos@jasen.nl + +% (C) 2013 Christophe Gouel + +% History +% Created in 2005 +% Revisions +% 2.0 (sep 2007) removed bug when dealing with fields containing cell +% arrays (Thanks to Rene Willemink) +% 2.1 (sep 2008) added warning and error identifiers +% 2.2 (oct 2008) fixed error when dealing with empty structs (Thanks to +% Lars Barring) +% 3.0 (mar 2013) fixed problem when the inputs were array of structures +% (thanks to Tor Inge Birkenes for pointing this out). +% Rephrased the help section as well. + +error(nargchk(1,Inf,nargin)) ; +N = nargin ; + +if ~isstruct(varargin{end}), + if isequal(varargin{end},'sorted'), + sorted = 1 ; + N = N-1 ; + error(nargchk(1,Inf,N)) ; + else + error('catstruct:InvalidArgument','Last argument should be a structure, or the string "sorted".') ; + end +else + sorted = 0 ; +end + +sz0 = [] ; % used to check that all inputs have the same size + +% used to check for a few trivial cases +NonEmptyInputs = false(N,1) ; +NonEmptyInputsN = 0 ; + +% used to collect the fieldnames and the inputs +FN = cell(N,1) ; +VAL = cell(N,1) ; + +% parse the inputs +for ii=1:N, + X = varargin{ii} ; + if ~isstruct(X), + error('catstruct:InvalidArgument',['Argument #' num2str(ii) ' is not a structure.']) ; + end + + if ~isempty(X), + % empty structs are ignored + if ii > 1 && ~isempty(sz0) + if ~isequal(size(X), sz0) + error('catstruct:UnequalSizes','All structures should have the same size.') ; + end + else + sz0 = size(X) ; + end + NonEmptyInputsN = NonEmptyInputsN + 1 ; + NonEmptyInputs(ii) = true ; + FN{ii} = fieldnames(X) ; + VAL{ii} = struct2cell(X) ; + end +end + +if NonEmptyInputsN == 0 + % all structures were empty + A = struct([]) ; +elseif NonEmptyInputsN == 1, + % there was only one non-empty structure + A = varargin{NonEmptyInputs} ; + if sorted, + A = orderfields(A) ; + end +else + % there is actually something to concatenate + FN = cat(1,FN{:}) ; + VAL = cat(1,VAL{:}) ; + FN = squeeze(FN) ; + VAL = squeeze(VAL) ; + MatlabVersion = version; + if isoctave || str2double(MatlabVersion(end-5:end-2))<2013 % Equivalent to, but faster than if verLessThan('matlab','8.1') + [UFN,ind] = unique(FN) ; + else + [UFN,ind] = unique(FN,'legacy') ; + end + + if numel(UFN) ~= numel(FN), + warning('catstruct:DuplicatesFound','Fieldnames are not unique between structures.') ; + sorted = 1 ; + end + + if sorted, + VAL = VAL(ind,:) ; + FN = FN(ind,:) ; + end + + A = cell2struct(VAL, FN); + A = reshape(A, sz0) ; % reshape into original format +end + + + diff --git a/matlab/lmmcp/dyn_lmmcp.m b/matlab/lmmcp/dyn_lmmcp.m new file mode 100644 index 000000000..ce3db1c7b --- /dev/null +++ b/matlab/lmmcp/dyn_lmmcp.m @@ -0,0 +1,77 @@ +function [endo_simul,info] = dyn_lmmcp(M,options,oo) + +% 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 . + +[lb,ub,eq_index] = get_complementarity_conditions(M); + +lead_lag_incidence = M.lead_lag_incidence; + +ny = M.endo_nbr; + +max_lag = M.maximum_endo_lag; + +nyp = nnz(lead_lag_incidence(1,:)) ; +iyp = find(lead_lag_incidence(1,:)>0) ; +ny0 = nnz(lead_lag_incidence(2,:)) ; +iy0 = find(lead_lag_incidence(2,:)>0) ; +nyf = nnz(lead_lag_incidence(3,:)) ; +iyf = find(lead_lag_incidence(3,:)>0) ; + +nd = nyp+ny0+nyf; +nrc = nyf+1 ; +isp = [1:nyp] ; +is = [nyp+1:ny+nyp] ; +isf = iyf+nyp ; +isf1 = [nyp+ny+1:nyf+nyp+ny+1] ; +stop = 0 ; +iz = [1:ny+nyp+nyf]; + +periods = options.periods; +steady_state = oo.steady_state; +params = M.params; +endo_simul = oo.endo_simul; +exo_simul = oo.exo_simul; +i_cols_1 = nonzeros(lead_lag_incidence(2:3,:)'); +i_cols_A1 = find(lead_lag_incidence(2:3,:)'); +i_cols_T = nonzeros(lead_lag_incidence(1:2,:)'); +i_cols_j = 1:nd; +i_upd = ny+(1:periods*ny); + +x = endo_simul(:); + +model_dynamic = str2func([M.fname,'_dynamic']); +z = x(find(lead_lag_incidence')); +[res,A] = model_dynamic(z, exo_simul, params, steady_state,2); +nnzA = nnz(A); + +LB = repmat(lb,periods,1); +UB = repmat(ub,periods,1); + +Y0 = endo_simul(:,1); +YT = endo_simul(:,end); +x = endo_simul(:,2:end-1); +x = x(:); + +func_handle = @(x) dyn_lmmcp_func(x,model_dynamic, Y0, YT, exo_simul, ... + params, steady_state, periods, ny, ... + lead_lag_incidence, i_cols_A1, i_cols_1, ... + i_cols_T, i_cols_j,nnzA,eq_index); + +[x, info] = lmmcp(func_handle,x,LB,UB); + +endo_simul = [Y0 reshape(x,ny,periods) YT]; \ No newline at end of file diff --git a/matlab/lmmcp/dyn_lmmcp_func.m b/matlab/lmmcp/dyn_lmmcp_func.m new file mode 100644 index 000000000..936f98306 --- /dev/null +++ b/matlab/lmmcp/dyn_lmmcp_func.m @@ -0,0 +1,56 @@ +function [F,A] = dyn_lmmcp_func(x, model_dynamic, Y0, YT, exo_simul, params, ... + steady_state, periods, ny, lead_lag_incidence, ... + i_cols_A1, i_cols_1, i_cols_T, i_cols_j, ... + nnzA,eq_index) + +% 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 . + + Y = [Y0; x; YT]; + + F = zeros(periods*ny,1); + if nargout == 2 + A = sparse([],[],[],periods*ny,periods*ny,periods*nnzA); + end + + i_rows = 1:ny; + i_cols = find(lead_lag_incidence'); + i_cols_A = i_cols; + + for it = 2:(periods+1) + + [res,jacobian] = model_dynamic(Y(i_cols),exo_simul, params, ... + steady_state,it); + F(i_rows) = res(eq_index); + + if nargout == 2 + if it == 2 + A(i_rows,i_cols_A1) = jacobian(eq_index,i_cols_1); + elseif it == periods+1 + A(i_rows,i_cols_A(i_cols_T)) = jacobian(eq_index,i_cols_T); + else + A(i_rows,i_cols_A) = jacobian(eq_index,i_cols_j); + end + end + + i_rows = i_rows + ny; + i_cols = i_cols + ny; + if nargout == 2 && it > 2 + i_cols_A = i_cols_A + ny; + end + end + diff --git a/matlab/lmmcp/get_complementarity_conditions.m b/matlab/lmmcp/get_complementarity_conditions.m new file mode 100644 index 000000000..95ef094f9 --- /dev/null +++ b/matlab/lmmcp/get_complementarity_conditions.m @@ -0,0 +1,56 @@ +function [lb,ub,eq_index] = get_complementarity_conditions(M) + +% 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 . + + +etags = M.equations_tags; +ub = inf(M.endo_nbr,1); +lb = -ub; +eq_index = (1:M.endo_nbr)'; +for i=1:size(etags,1) + if strcmp(etags{i,2},'mcp') + str = etags{i,3}; + kop = strfind(etags{i,3},'<'); + if ~isempty(kop) + k = find(strcmp(strtrim(str(1:kop-1)),cellstr(M.endo_names))); + if isempty(k) + error(sprintf(['Complementarity condition %s: variable %s is ' ... + 'not recognized',etags{i,3},b{1}])) + end + ub(k) = str2num(str(kop+1:end)); + eq_index(etags{i,1}) = k; + eq_index(k) = etags{i,1}; + else + kop = strfind(etags{i,3},'>'); + if ~isempty(kop) + k = find(strcmp(strtrim(str(1:kop-1)),cellstr(M.endo_names))); + if isempty(k) + error(sprintf(['Complementarity condition %s: variable %s is ' ... + 'not recognized',etags{i},b{1}])) + end + lb(k) = str2num(str(kop+1:end)); + eq_index(etags{i,1}) = k; + eq_index(k) = etags{i,1}; + else + error(sprintf(['Complementarity condition %s can''t be ' ... + 'parsed'],etags{i,3})) + end + end + end +end + diff --git a/matlab/lmmcp/lmmcp.m b/matlab/lmmcp/lmmcp.m new file mode 100644 index 000000000..f6c0864b0 --- /dev/null +++ b/matlab/lmmcp/lmmcp.m @@ -0,0 +1,614 @@ +function [x,FVAL,EXITFLAG,OUTPUT,JACOB] = lmmcp(FUN,x,lb,ub,options,varargin) +% LMMCP solves a mixed complementarity problem. +% +% LMMCP uses a semismooth least squares formulation. The method applies a +% Levenberg-Marquardt/Gauss-Newton algorithm to a least-squares formulation. +% +% X = LMMCP(FUN,X0) tries to solve the system of nonlinear equations F(X)=0 and +% starts at the vector X0. FUN accepts a vector X and return a vector F of equation +% values F evaluated at X and, as second output if required, a matrix J, the +% Jacobian evaluated at X. +% +% X = LMMCP(FUN,X0,LB,UB) solves the mixed complementarity problem of the form: +% LB =X => F(X)>0, +% LB<=X<=UB => F(X)=0, +% X =UB => F(X)<0. +% +% X = LMMCP(FUN,X0,LB,UB,OPTIONS) solves the MCP problem using the options +% defined in the structure OPTIONS. Main fields are +% Display : control the display of iterations, 'none' (default), +% 'iter-detailed' or 'final-detailed' +% Switch from phase I to phase II +% preprocess : activate preprocessor for phase I (default = 1) +% presteps : number of iterations in phase I (default = 20) +% Termination parameters +% MaxIter : Maximum number of iterations (default = 500) +% tmin : safeguard stepsize (default = 1E-12) +% TolFun : Termination tolerance on the function value, a positive +% scalar (default = sqrt(eps)) +% Stepsize parameters +% m : number of previous function values to use in the nonmonotone +% line search rule (default = 10) +% kwatch : maximum number of steps (default = 20 and should not be +% smaller than m) +% watchdog : activate the watchdog strategy (default = 1) +% Ther are other minor parameters. Please see the code for their default values +% and interpretation. +% +% [X,FVAL] = LMMCP(FUN,X0,...) returns the value of the equations FUN at X. +% +% [X,FVAL,EXITFLAG] = LMMCP(FUN,X0,...) returns EXITFLAG that describes the exit +% conditions. Possible values are +% 1 : LMMCP converged to a root +% 0 : Too many iterations +% -1 : +% +% [X,FVAL,EXITFLAG,OUTPUT] = LMMCP(FUN,X0,...) returns the structure OUTPUT that +% contains the number of iterations (OUTPUT.iterations), the value of the merit +% function (OUTPUT.Psix), and the norm of the derivative of the merit function +% (OUTPUT.normDPsix). +% +% [X,FVAL,EXITFLAG,OUTPUT,JACOB] = LMMCP(FUN,X0,...) returns JACOB the Jacobian +% of FUN evaluated at X. +% +% More details of the main program may be found in the following paper: +% +% Christian Kanzow and Stefania Petra: On a semismooth least squares formulation of +% complementarity problems with gap reduction. Optimization Methods and Software +% 19, 2004, pp. 507-525. +% +% In addition, the current implementation uses a preprocessor which is the +% projected Levenberg-Marquardt step from the following preprint: +% +% Christian Kanzow and Stefania Petra: Projected filter trust region methods for a +% semismooth least squares formulation of mixed complementarity +% problems. Optimization Methods and Software +% 22, 2007, pp. 713-735. +% +% A user's guide is also available: +% +% Christian Kanzow and Stefania Petra (2005). +% LMMCP --- A Levenberg-Marquardt-type MATLAB Solver for Mixed Complementarity Problems. +% University of Wuerzburg. +% http://www.mathematik.uni-wuerzburg.de/~kanzow/software/UserGuide.pdf +% +% This is a modification by Christophe Gouel of the original files, which can be +% downloaded from: +% http://www.mathematik.uni-wuerzburg.de/~kanzow/software/LMMCP.zip +% +% copyright: Christian Kanzow and Stefania Petra +% Institute of Applied Mathematics and Statistics +% University of Wuerzburg +% Am Hubland +% 97074 Wuerzburg +% GERMANY +% +% e-mail: kanzow@mathematik.uni-wuerzburg.de +% petra@mathematik.uni-wuerzburg.de + +%% Initialization +defaultopt = struct(... + 'beta', 0.55,... + 'Big', 1e10,... + 'delta', 5,... + 'deltamin', 1,... + 'Display', 'none',... + 'epsilon1', 1e-6,... + 'eta', 0.95,... + 'kwatch', 20,... + 'lambda1', 0.1,... + 'm', 10,... + 'MaxIter', 500,... + 'null', 1e-10,... + 'preprocess', 1,... + 'presteps', 20,... + 'sigma', 1e-4,... + 'sigma1', 0.5,... + 'sigma2', 2,... + 'tmin', 1e-12,... + 'TolFun', sqrt(eps),... + 'watchdog', 1); + +if nargin < 4 + ub = inf(size(x)); + if nargin < 3 + lb = -inf(size(x)); + end +end + +if nargin < 5 || isempty(options) || ~isstruct(options) + options = defaultopt; +else + warning('off','catstruct:DuplicatesFound') + options = catstruct(defaultopt,options); +end + +warning('off','MATLAB:rankDeficientMatrix') + +switch options.Display + case {'off','none'} + verbosity = 0; + case {'iter','iter-detailed'} + verbosity = 2; + case {'final','final-detailed'} + verbosity = 1; + otherwise + verbosity = 0; +end + +% parameter settings +eps1 = options.epsilon1; +eps2 = 0.5*options.TolFun^2; +null = options.null; +Big = options.Big; + +% maximal number of iterations +kmax = options.MaxIter; + +% choice of lambda +lambda1 = options.lambda1; +lambda2 = 1-lambda1; + +% steplength parameters +beta = options.beta; +sigma = options.sigma; +tmin = options.tmin; + +% parameters watchdog and nonmonotone line search; redefined later +m = options.m; +kwatch = options.kwatch; +watchdog = options.watchdog; % 1=watchdog strategy active, otherwise not + +% parameters for preprocessor +preprocess = options.preprocess; % 1=preprocessor used, otherwise not +presteps = options.presteps; % maximum number of preprocessing steps + +% trust-region parameters for preprocessor +delta = options.delta; +deltamin = options.deltamin; +sigma1 = options.sigma1; +sigma2 = options.sigma2; +eta = options.eta; + +% initializations +k = 0; +k_main = 0; + +% compute a feasible starting point by projection +x = max(lb,min(x,ub)); + +n = length(x); +OUTPUT.Dim = n; + +% definition of index sets I_l, I_u and I_lu +Indexset = zeros(n,1); +I_l = lb>-Big & ub>Big; +I_u = lb<-Big & ub-Big & ub 1 + fprintf(' k Psi(x) || DPsi(x) || stepsize\n'); + disp('====================================================================') + disp('********************* Output at starting point *********************') + fprintf('%4.0f %24.5e %24.5e\n',k,Psix,normDPsix); +end + +%% Preprocessor using local method + +if preprocess==1 + + if verbosity > 1 + disp('************************** Preprocessor ****************************') + end + + normpLM=1; + while (k < presteps) && (Psix > eps2) && (normpLM>null) + k = k+1; + + % choice of Levenberg-Marquardt parameter, note that we do not use + % the condition estimator for large-scale problems, although this + % may cause numerical problems in some examples + + i = false; + mu = 0; + if n<100 + i = true; + mu = 1e-16; + if condest(DPhix'*DPhix)>1e25 + mu = 1e-6/(k+1); + end + end + if i + pLM = [DPhix; sqrt(mu)*speye(n)]\[-Phix; zeros(n,1)]; + else + pLM = -DPhix\Phix; + end + normpLM = norm(pLM); + + % compute the projected Levenberg-Marquard step onto box Xk + lbnew = max(min(lb-x,0),-delta); + ubnew = min(max(ub-x,0),delta); + d = max(lbnew,min(pLM,ubnew)); + xnew = x+d; + + % function evaluations etc. + [Fxnew,DFxnew] = feval(FUN,xnew,varargin{:}); + Phixnew = Phi(xnew,Fxnew,lb,ub,lambda1,lambda2,n,Indexset); + Psixnew = 0.5*(Phixnew'*Phixnew); + normPhixnew = norm(Phixnew); + + % update of delta + if normPhixnew<=eta*normPhix + delta = max(deltamin,sigma2*delta); + elseif normPhixnew>5*eta*normPhix + delta = max(deltamin,sigma1*delta); + end + + % update + x = xnew; + Fx = Fxnew; + DFx = DFxnew; + Phix = Phixnew; + Psix = Psixnew; + normPhix = normPhixnew; + DPhix = DPhi(x,Fx,DFx,lb,ub,lambda1,lambda2,n,Indexset); + DPsix = DPhix'*Phix; + normDPsix = norm(DPsix,inf); + + % output at each iteration + t=1; + if verbosity > 1 + fprintf('%4.0f %24.5e %24.5e %11.7g\n',k,Psix,normDPsix,t); + end + end +end + +% terminate program or redefine current iterate as original initial point +if preprocess==1 && Psix 0 + fprintf('Psix = %1.4e\nnormDPsix = %1.4e\n',Psix,normDPsix); + disp('Approximate solution found.') + end + EXITFLAG = 1; + FVAL = Fx; + OUTPUT.iterations = k; + OUTPUT.Psix = Psix; + OUTPUT.normDPsix = normDPsix; + JACOB = DFx; + return +elseif preprocess==1 && Psix>=eps2 + x=x0; + Phix=Phix0; + Psix=Psix0; + DPhix=DPhix0; + DPsix=DPsix0; + if verbosity > 1 + disp('******************** Restart with initial point ********************') + fprintf('%4.0f %24.5e %24.5e\n',k_main,Psix0,normDPsix0); + end +end + +%% Main algorithm + +if verbosity > 1 + disp('************************** Main program ****************************') +end + +while (k < kmax) && (Psix > eps2) + + % choice of Levenberg-Marquardt parameter, note that we do not use + % the condition estimator for large-scale problems, although this + % may cause numerical problems in some examples + + i = false; + if n<100 + i = true; + mu = 1e-16; + if condest(DPhix'*DPhix)>1e25 + mu = 1e-1/(k+1); + end + end + + % compute a Levenberg-Marquard direction + + if i + d = [DPhix; sqrt(mu)*speye(n)]\[-Phix; zeros(n,1)]; + else + d = -DPhix\Phix; + end + + % computation of steplength t using the nonmonotone Armijo-rule + % starting with the 6-th iteration + + % computation of steplength t using the monotone Armijo-rule if + % d is a 'good' descent direction or k<=5 + + t = 1; + xnew = x+d; + Fxnew = feval(FUN,xnew,varargin{:}); + Phixnew = Phi(xnew,Fxnew,lb,ub,lambda1,lambda2,n,Indexset); + Psixnew = 0.5*(Phixnew'*Phixnew); + const = sigma*DPsix'*d; + + while (Psixnew > MaxPsi + const*t) && (t > tmin) + t = t*beta; + xnew = x+t*d; + Fxnew = feval(FUN,xnew,varargin{:}); + Phixnew = Phi(xnew,Fxnew,lb,ub,lambda1,lambda2,n,Indexset); + Psixnew = 0.5*(Phixnew'*Phixnew); + end + + % updatings + x = xnew; + Fx = Fxnew; + Phix = Phixnew; + Psix = Psixnew; + [~,DFx] = feval(FUN,x,varargin{:}); + DPhix = DPhi(x,Fx,DFx,lb,ub,lambda1,lambda2,n,Indexset); + DPsix = DPhix'*Phix; + normDPsix = norm(DPsix); + k = k+1; + k_main = k_main+1; + + if k_main<=5 + aux(mod(k_main,m)+1) = Psix; + MaxPsi = Psix; + else + aux(mod(k_main,m)+1) = Psix; + MaxPsi = max(aux); + end + + % updatings for the watchdog strategy + if watchdog ==1 + if Psixkwatch + x=xbest; + Phix=Phibest; + Psix=Psibest; + DPhix=DPhibest; + DPsix=DPsibest; + normDPsix=normDPsibest; + MaxPsi=Psix; + end + end + + if verbosity > 1 + % output at each iteration + fprintf('%4.0f %24.5e %24.5e %11.7g\n',k,Psix,normDPsix,t); + end +end + +%% Final output +if Psix<=eps2 + EXITFLAG = 1; + if verbosity > 0, disp('Approximate solution found.'); end +elseif k>=kmax + EXITFLAG = 0; + if verbosity > 0, disp('Maximum iteration number reached.'); end +elseif normDPsix<=eps1 + EXITFLAG = -1; % Provisoire + if verbosity > 0, disp('Approximate stationary point found.'); end +else + EXITFLAG = -1; % Provisoire + if verbosity > 0, disp('No solution found.'); end +end + +FVAL = Fx; +OUTPUT.iterations = k; +OUTPUT.Psix = Psix; +OUTPUT.normDPsix = normDPsix; +JACOB = DFx; + +%% Subfunctions + +function y = Phi(x,Fx,lb,ub,lambda1,lambda2,n,Indexset) +%% PHI + +y = zeros(2*n,1); +phi_u = sqrt((ub-x).^2+Fx.^2)-ub+x+Fx; +LZ = false(n,1); % logical zero + +I0 = Indexset==0; +y(I0) = -lambda1*Fx(I0); +y([LZ; I0]) = -lambda2*Fx(I0); + +I1 = Indexset==1; +y(I1) = lambda1*(-x(I1)+lb(I1)-Fx(I1)+sqrt((x(I1)-lb(I1)).^2+Fx(I1).^2)); +y([LZ; I1]) = lambda2*max(0,x(I1)-lb(I1)).*max(0,Fx(I1)); + +I2 = Indexset==2; +y(I2) = -lambda1*phi_u(I2); +y([LZ; I2]) = lambda2*max(0,ub(I2)-x(I2)).*max(0,-Fx(I2)); + +I3 = Indexset==3; +y(I3) = lambda1*(sqrt((x(I3)-lb(I3)).^2+phi_u(I3).^2)-x(I3)+lb(I3)-phi_u(I3)); +y([LZ; I3]) = lambda2*(max(0,x(I3)-lb(I3)).*max(0,Fx(I3))+max(0,ub(I3)-x(I3)).*max(0,-Fx(I3))); + + +function H = DPhi(x,Fx,DFx,lb,ub,lambda1,lambda2,n,Indexset) +%% DPHI evaluates an element of the C-subdifferential of operator Phi + +null = 1e-8; +beta_l = zeros(n,1); +beta_u = zeros(n,1); +alpha_l = zeros(n,1); +alpha_u = zeros(n,1); + + +z = zeros(n,1); +H2 = sparse(n,n); + +I = abs(x-lb)<=null & abs(Fx)<=null; +beta_l(I) = 1; +z(I) = 1; + +I = abs(ub-x)<=null & abs(Fx)<=null; +beta_u(I) = 1; +z(I) = 1; + +I = x-lb>=-null & Fx>=-null; +alpha_l(I) = 1; + +I = ub-x>=-null & Fx<=null; +alpha_u(I) = 1; + +Da = zeros(n,1); +Db = zeros(n,1); + +I = 1:n; + +I0 = Indexset==0; +Da(I0) = 0; +Db(I0) = -1; +H2(I0,:) = -DFx(I0,:); + +I1 = Indexset==1; +denom1 = zeros(n,1); +denom2 = zeros(n,1); +if any(I1) + denom1(I1) = max(null,sqrt((x(I1)-lb(I1)).^2+Fx(I1).^2)); + denom2(I1) = max(null,sqrt(z(I1).^2+(DFx(I1,:)*z).^2)); +end + +I1b = Indexset==1 & beta_l==0; +Da(I1b) = (x(I1b)-lb(I1b))./denom1(I1b)-1; +Db(I1b) = Fx(I1b)./denom1(I1b)-1; +I1b = Indexset==1 & beta_l~=0; +if any(I1b) + Da(I1b) = z(I1b)./denom2(I1b)-1; + Db(I1b) = (DFx(I1b,:)*z)./denom2(I1b)-1; +end + +I1a = I(Indexset==1 & alpha_l==1); +if any(I1a) + H2(I1a,:) = bsxfun(@times,x(I1a)-lb(I1a),DFx(I1a,:))+... + sparse(1:length(I1a),I1a,Fx(I1a),length(I1a),n,length(I1a)); +end + +I2 = Indexset==2; +denom1 = zeros(n,1); +denom2 = zeros(n,1); +if any(I2) + denom1(I2) = max(null,sqrt((ub(I2)-x(I2)).^2+Fx(I2).^2)); + denom2(I2) = max(null,sqrt(z(I2).^2+(DFx(I2,:)*z).^2)); +end + +I2b = Indexset==2 & beta_u==0; +Da(I2b) = (ub(I2b)-x(I2b))./denom1(I2b)-1; +Db(I2b) = -Fx(I2b)./denom1(I2b)-1; +I2b = Indexset==2 & beta_u~=0; +if any(I2b) + Da(I2b) = -z(I2b)./denom2(I2b)-1; + Db(I2b) = -(DFx(I2b,:)*z)./denom2(I2b)-1; +end + +I2a = I(Indexset==2 & alpha_u==1); +if any(I2a) + H2(I2a,:) = bsxfun(@times,x(I2a)-ub(I2a),DFx(I2a,:))+... + sparse(1:length(I2a),I2a,Fx(I2a),length(I2a),n,length(I2a)); +end + +I3 = Indexset==3; +phi = zeros(n,1); +ai = zeros(n,1); +bi = zeros(n,1); +ci = zeros(n,1); +di = zeros(n,1); +denom1 = zeros(n,1); +denom2 = zeros(n,1); +denom3 = zeros(n,1); +denom4 = zeros(n,1); +if any(I3) + phi(I3) = -ub(I3)+x(I3)+Fx(I3)+sqrt((ub(I3)-x(I3)).^2+Fx(I3).^2); + denom1(I3) = max(null,sqrt((x(I3)-lb(I3)).^2+phi(I3).^2)); + denom2(I3) = max(null,sqrt(z(I3).^2+(DFx(I3,:)*z).^2)); + denom3(I3) = max(null,sqrt((ub(I3)-x(I3)).^2+Fx(I3).^2)); + denom4(I3) = max(null,sqrt(z(I3).^2)); +end + +I3bu = Indexset==3 & beta_u==0; +ci(I3bu) = (x(I3bu)-ub(I3bu))./denom3(I3bu)+1; +di(I3bu) = Fx(I3bu)./denom3(I3bu)+1; +I3bu = Indexset==3 & beta_u~=0; +if any(I3bu) + ci(I3bu) = 1+z(I3bu)./denom2(I3bu); + di(I3bu) = 1+(DFx(I3bu,:)*z)./denom2(I3bu); +end + +I3bl = Indexset==3 & beta_l==0; +ai(I3bl) = (x(I3bl)-lb(I3bl))./denom1(I3bl)-1; +bi(I3bl) = phi(I3bl)./denom1(I3bl)-1; +I3bl = Indexset==3 & beta_l~=0; +if any(I3bl) + ai(I3bl) = z(I3bl)./denom4(I3bl)-1; + bi(I3bl) = (ci(I3bl).*z(I3bl)+(di(I3bl,ones(1,n)).*DFx(I3bl,:))*z)./denom4(I3bl)-1; +end + +Da(I3) = ai(I3)+bi(I3).*ci(I3); +Db(I3) = bi(I3).*di(I3); + +I3a = I(Indexset==3 & alpha_l==1 & alpha_u==1); +if any(I3a) + H2(I3a,:) = bsxfun(@times,-lb(I3a)-ub(I3a)+2*x(I3a),DFx(I3a,:))+... + 2*sparse(1:length(I3a),I3a,Fx(I3a),length(I3a),n,length(I3a)); +end +I3a = I(Indexset==3 & alpha_l==1 & alpha_u~=1); +if any(I3a) + H2(I3a,:) = bsxfun(@times,x(I3a)-lb(I3a),DFx(I3a,:))+... + sparse(1:length(I3a),I3a,Fx(I3a),length(I3a),n,length(I3a)); +end +I3a = I(Indexset==3 & alpha_l~=1 & alpha_u==1); +if any(I3a) + H2(I3a,:) = bsxfun(@times,x(I3a)-ub(I3a),DFx(I3a,:))+... + sparse(1:length(I3a),I3a,Fx(I3a),length(I3a),n,length(I3a)); +end + +H1 = bsxfun(@times,Db,DFx); +H1 = spdiags(diag(H1)+Da,0,H1); + +H = [lambda1*H1; lambda2*H2]; diff --git a/matlab/perfect_foresight_problem.m b/matlab/perfect_foresight_problem.m new file mode 100644 index 000000000..48a6e054e --- /dev/null +++ b/matlab/perfect_foresight_problem.m @@ -0,0 +1,71 @@ +function [residuals,JJacobian] = perfect_foresight_problem(y, dynamic_function, Y0, YT, ... + exo_simul, params, steady_state, ... + T, ny, i_cols, ... + i_cols_J1, i_cols_1, i_cols_T, ... + i_cols_j,nnzJ) +% function perfect_foresight_problem(x, model_dynamic, Y0, YT,exo_simul, +% params, steady_state, periods, ny, i_cols,i_cols_J1, i_cols_1, +% i_cols_T, i_cols_j, nnzA) +% computes the residuals and th Jacobian matrix +% for a perfect foresight problem over T periods. +% +% INPUTS +% ... +% OUTPUTS +% ... +% ALGORITHM +% ... +% +% SPECIAL REQUIREMENTS +% None. + +% Copyright (C) 1996-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 . + + + YY = [Y0; y; YT]; + + residuals = zeros(T*ny,1); + if nargout == 2 + JJacobian = sparse([],[],[],T*ny,T*ny,T*nnzJ); + end + + i_rows = 1:ny; + i_cols_J = i_cols; + + for it = 2:(T+1) + if nargout == 1 + residuals(i_rows) = dynamic_function(YY(i_cols),exo_simul, params, ... + steady_state,it); + elseif nargout == 2 + [residuals(i_rows),jacobian] = dynamic_function(YY(i_cols),exo_simul, params, ... + steady_state,it); + + if it == 2 + JJacobian(i_rows,i_cols_J1) = jacobian(:,i_cols_1); + elseif it == T + 1 + JJacobian(i_rows,i_cols_J(i_cols_T)) = jacobian(:,i_cols_T); + else + JJacobian(i_rows,i_cols_J) = jacobian(:,i_cols_j); + i_cols_J = i_cols_J + ny; + end + end + + i_rows = i_rows + ny; + i_cols = i_cols + ny; + end + diff --git a/matlab/perfect_foresight_solver.m b/matlab/perfect_foresight_solver.m index 9c80f0e91..819b3b667 100644 --- a/matlab/perfect_foresight_solver.m +++ b/matlab/perfect_foresight_solver.m @@ -31,12 +31,12 @@ function perfect_foresight_solver() global M_ options_ oo_ -if options_.stack_solve_algo < 0 || options_.stack_solve_algo > 6 - error('PERFECT_FORESIGHT_SOLVER: stack_solve_algo must be between 0 and 6') +if options_.stack_solve_algo < 0 || options_.stack_solve_algo > 7 + error('PERFECT_FORESIGHT_SOLVER: stack_solve_algo must be between 0 and 7') end if ~options_.block && ~options_.bytecode && options_.stack_solve_algo ~= 0 ... - && options_.stack_solve_algo ~= 6 + && options_.stack_solve_algo ~= 6 && options_.stack_solve_algo ~= 7 error('PERFECT_FORESIGHT_SOLVER: you must use stack_solve_algo=0 or stack_solve_algo=6 when not using block nor bytecode option') end @@ -185,8 +185,37 @@ else else % General case if options_.stack_solve_algo == 0 sim1; - else % stack_solve_algo = 6 + elseif options_.stack_solve_algo == 6 sim1_lbj; + elseif options_.stack_solve_algo == 7 + periods = options_.periods; + if ~isfield(options_.lmmcp,'lb') + [lb,ub,pfm.eq_index] = get_complementarity_conditions(M_); + options_.lmmcp.lb = repmat(lb,periods,1); + options_.lmmcp.ub = repmat(ub,periods,1); + end + + y = oo_.endo_simul; + y0 = y(:,1); + yT = y(:,periods+2); + z = y(:,2:periods+1); + illi = M_.lead_lag_incidence'; + [i_cols,~,i_cols_j] = find(illi(:)); + illi = illi(:,2:3); + [i_cols_J1,~,i_cols_1] = find(illi(:)); + i_cols_T = nonzeros(M_.lead_lag_incidence(1:2,:)'); + [y,info] = dynare_solve(@perfect_foresight_problem,z(:),1, ... + str2func([M_.fname '_dynamic']),y0,yT, ... + oo_.exo_simul,M_.params,oo_.steady_state, ... + options_.periods,M_.endo_nbr,i_cols, ... + i_cols_J1, i_cols_1, i_cols_T, i_cols_j, ... + M_.NNZDerivatives(1)); + oo_.endo_simul = [y0 reshape(y,M_.endo_nbr,periods) yT]; + if info == 1 + oo_.deterministic_simulation.status = 0; + else + oo_.deterministic_simulation.status = 1; + end; end end end diff --git a/matlab/reports/@graph/graph.m b/matlab/reports/@graph/graph.m index 5be0ee926..687304397 100644 --- a/matlab/reports/@graph/graph.m +++ b/matlab/reports/@graph/graph.m @@ -72,6 +72,9 @@ o.xTickLabelAnchor = 'east'; o.width = 6; o.height = 4.5; +o.miscTikzPictureOptions = ''; +o.miscTikzAxisOptions = ''; + if nargin == 1 assert(isa(varargin{1}, 'graph'),['@graph.graph: with one arg you ' ... 'must pass a graph object']); @@ -87,7 +90,7 @@ elseif nargin > 1 % overwrite default values for pair = reshape(varargin, 2, []) - ind = strmatch(lower(pair{1}), lower(optNames), 'exact'); + ind = find(strcmpi(optNames, pair{1})); assert(isempty(ind) || length(ind) == 1); if ~isempty(ind) o.(optNames{ind}) = pair{2}; @@ -105,6 +108,8 @@ assert(iscellstr(o.title), '@graph.graph: title must be a cell array of string(s assert(ischar(o.titleFormat), '@graph.graph: titleFormat file must be a string'); assert(ischar(o.xlabel), '@graph.graph: xlabel file must be a string'); assert(ischar(o.ylabel), '@graph.graph: ylabel file must be a string'); +assert(ischar(o.miscTikzPictureOptions), '@graph.graph: miscTikzPictureOptions file must be a string'); +assert(ischar(o.miscTikzAxisOptions), '@graph.graph: miscTikzAxisOptions file must be a string'); assert(ischar(o.graphName), '@graph.graph: graphName must be a string'); assert(ischar(o.graphDirName), '@graph.graph: graphDirName must be a string'); assert(islogical(o.showGrid), '@graph.graph: showGrid must be either true or false'); @@ -141,17 +146,17 @@ valid_legend_orientations = {'vertical', 'horizontal'}; assert(any(strcmp(o.legendOrientation, valid_legend_orientations)), ... ['@graph.graph: legendOrientation must be one of ' strjoin(valid_legend_orientations, ' ')]); -assert(isempty(o.shade) || (isa(o.shade, 'dates') && o.shade.ndat >= 2), ... +assert(isempty(o.shade) || (isdates(o.shade) && o.shade.ndat >= 2), ... ['@graph.graph: shade is specified as a dates range, e.g. ' ... '''dates(''1999q1''):dates(''1999q3'')''.']); -assert(isempty(o.xrange) || (isa(o.xrange, 'dates') && o.xrange.ndat >= 2), ... +assert(isempty(o.xrange) || (isdates(o.xrange) && o.xrange.ndat >= 2), ... ['@graph.graph: xrange is specified as a dates range, e.g. ' ... '''dates(''1999q1''):dates(''1999q3'')''.']); assert(isempty(o.yrange) || (isfloat(o.yrange) && length(o.yrange) == 2 && ... o.yrange(1) < o.yrange(2)), ... ['@graph.graph: yrange is specified an array with two float entries, ' ... 'the lower bound and upper bound.']); -assert(isempty(o.data) || isa(o.data, 'dseries'), ['@graph.graph: data must ' ... +assert(isempty(o.data) || isdseries(o.data), ['@graph.graph: data must ' ... 'be a dseries']); assert(isempty(o.seriesToUse) || iscellstr(o.seriesToUse), ['@graph.graph: ' ... 'seriesToUse must be a cell array of string(s)']); diff --git a/matlab/reports/@graph/writeGraphFile.m b/matlab/reports/@graph/writeGraphFile.m index 4fa888ce4..2b706a8fe 100644 --- a/matlab/reports/@graph/writeGraphFile.m +++ b/matlab/reports/@graph/writeGraphFile.m @@ -49,7 +49,11 @@ if fid == -1 error(['@graph.writeGraphFile: ' msg]); end -fprintf(fid, '\\begin{tikzpicture}[baseline]'); +fprintf(fid, '\\begin{tikzpicture}[baseline'); +if ~isempty(o.miscTikzPictureOptions) + fprintf(fid, ',%s', o.miscTikzPictureOptions); +end +fprintf(fid, ']'); if isempty(o.xrange) dd = getMaxRange(o.series); @@ -141,6 +145,7 @@ if isempty(o.yrange) else fprintf(fid, 'ymin=%f,\nymax=%f,\n',o.yrange(1),o.yrange(2)); end +fprintf(fid, 'xmin = 1,\nxmax = %d,\n', length(dd)); if o.showLegend fprintf(fid, 'legend style={'); @@ -165,6 +170,10 @@ end if ~isempty(o.ylabel) fprintf(fid, 'ylabel=%s,\n', o.ylabel); end + +if ~isempty(o.miscTikzAxisOptions) + fprintf(fid, '%s', o.miscTikzAxisOptions); +end fprintf(fid, ']\n'); if o.showZeroline @@ -174,7 +183,10 @@ end for i=1:ne o.series{i}.writeSeriesForGraph(fid, dd); if o.showLegend - fprintf(fid, '\\addlegendentry{%s}\n', o.series{i}.getTexName()); + le = o.series{i}.getNameForLegend(); + if ~isempty(le) + fprintf(fid, '\\addlegendentry{%s}\n', le); + end end end @@ -186,18 +198,18 @@ if ~isempty(o.shade) date2string(o.shade(1)) ' or ' date2string(o.shade(end)) ' is not in the date ' ... 'range of data selected.']); if x1 == 1 - fprintf(fid,['\\begin{pgfonlayer}{background}\n\\fill[%s!%f]\n(axis ' ... + fprintf(fid,['\\begin{pgfonlayer}{background0}\n\\fill[%s!%f]\n(axis ' ... 'cs:\\pgfkeysvalueof{/pgfplots/xmin},\\pgfkeysvalueof{/pgfplots/ymin})\nrectangle (axis ' ... 'cs:%f,\\pgfkeysvalueof{/pgfplots/ymax});\n\\end{pgfonlayer}\n'], ... o.shadeColor, o.shadeOpacity, x2); elseif x2 == dd.ndat - fprintf(fid,['\\begin{pgfonlayer}{background}\n\\fill[%s!%f]\n(axis ' ... + fprintf(fid,['\\begin{pgfonlayer}{background0}\n\\fill[%s!%f]\n(axis ' ... 'cs:%f,\\pgfkeysvalueof{/pgfplots/ymin})\nrectangle (axis ' ... 'cs:\\pgfkeysvalueof{/pgfplots/xmax},\\pgfkeysvalueof{/' ... 'pgfplots/ymax});\n\\end{pgfonlayer}\n'], ... o.shadeColor, o.shadeOpacity, x1); else - fprintf(fid,['\\begin{pgfonlayer}{background}\n\\fill[%s!%f]\n(axis ' ... + fprintf(fid,['\\begin{pgfonlayer}{background0}\n\\fill[%s!%f]\n(axis ' ... 'cs:%f,\\pgfkeysvalueof{/pgfplots/ymin})\nrectangle (axis ' ... 'cs:%f,\\pgfkeysvalueof{/pgfplots/ymax});\n\\end{pgfonlayer}\n'], ... o.shadeColor, o.shadeOpacity, x1, x2); diff --git a/matlab/reports/@page/page.m b/matlab/reports/@page/page.m index 7bcc26c5d..99442c777 100644 --- a/matlab/reports/@page/page.m +++ b/matlab/reports/@page/page.m @@ -53,7 +53,7 @@ elseif nargin > 1 % overwrite default values for pair = reshape(varargin, 2, []) - ind = strmatch(lower(pair{1}), lower(optNames), 'exact'); + ind = find(strcmpi(optNames, pair{1})); assert(isempty(ind) || length(ind) == 1); if ~isempty(ind) o.(optNames{ind}) = pair{2}; diff --git a/matlab/reports/@paragraph/display.m b/matlab/reports/@paragraph/display.m new file mode 100644 index 000000000..f731c9c5d --- /dev/null +++ b/matlab/reports/@paragraph/display.m @@ -0,0 +1,32 @@ +function display(o) +%function display(o) +% Display a Paragraph object +% +% INPUTS +% o [paragraph] paragraph object +% +% OUTPUTS +% none +% +% SPECIAL REQUIREMENTS +% none + +% 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 . + +display_reporting_object(o); +end \ No newline at end of file diff --git a/matlab/reports/@paragraph/paragraph.m b/matlab/reports/@paragraph/paragraph.m new file mode 100644 index 000000000..4cf6106b2 --- /dev/null +++ b/matlab/reports/@paragraph/paragraph.m @@ -0,0 +1,63 @@ +function o = paragraph(varargin) +%function o = paragraph(varargin) +% Instantiates a paragraph object + +% 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 . + +o = struct; + +o.balancedCols = false; +o.cols = 1; +o.heading = ''; +o.indent = true; +o.text = ''; + +if nargin == 1 + assert(isa(varargin{1}, 'paragraph'),['With one arg to Paragraph constructor, ' ... + 'you must pass a paragraph object']); + o = varargin{1}; + return; +elseif nargin > 1 + if round(nargin/2) ~= nargin/2 + error(['@paragraph.paragraph: options must be supplied in name/value ' ... + 'pairs.']); + end + + optNames = fieldnames(o); + + % overwrite default values + for pair = reshape(varargin, 2, []) + ind = find(strcmpi(optNames, pair{1})); + assert(isempty(ind) || length(ind) == 1); + if ~isempty(ind) + o.(optNames{ind}) = pair{2}; + else + error('@paragraph.paragraph: %s is not a recognized option.', pair{1}); + end + end +end + +assert(islogical(o.indent), '@paragraph.paragraph: indent must be either true or false'); +assert(islogical(o.balancedCols), '@paragraph.paragraph: balancedCols must be either true or false'); +assert(isint(o.cols), '@paragraph.paragraph: cols must be an integer'); +assert(ischar(o.text), '@paragraph.paragraph: text must be a string'); +assert(ischar(o.heading), '@paragraph.paragraph: heading must be a string'); + +% Create paragraph object +o = class(o, 'paragraph'); +end diff --git a/matlab/reports/@paragraph/subsasgn.m b/matlab/reports/@paragraph/subsasgn.m new file mode 100644 index 000000000..429c48ccd --- /dev/null +++ b/matlab/reports/@paragraph/subsasgn.m @@ -0,0 +1,50 @@ +function B = subsasgn(A, S, V) +% function B = subsasgn(A, S, V) + +% 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 . + +B = A; +if length(S) > 1 + for i=1:(length(S)-1) + B = subsref(B, S(i)); + end + B = subsasgn(B, S(end), V); + B = subsasgn(A, S(1:(end-1)), B); + return +end + +switch S.type + case '()' + index = S.subs{:}; + assert(isnumeric(index)); + B.elements{index} = V; + case '{}' + index = S.subs{:}; + assert(isnumeric(index)); + B{index} = V; + case '.' + switch S.subs + case fieldnames(A) + B.(S.subs) = V; + otherwise + error(['@paragraph.subsasgn: field ' S.subs 'does not exist']); + end + otherwise + error('@paragraph.subsasgn: syntax error'); +end +end \ No newline at end of file diff --git a/matlab/reports/@paragraph/subsref.m b/matlab/reports/@paragraph/subsref.m new file mode 100644 index 000000000..f3dd2c55b --- /dev/null +++ b/matlab/reports/@paragraph/subsref.m @@ -0,0 +1,48 @@ +function A = subsref(A, S) +%function A = subsref(A, S) + +% 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 . + +switch S(1).type + case '.' + switch S(1).subs + case fieldnames(A) + A = A.(S(1).subs); + case methods(A) + if areParensNext(S) + A = feval(S(1).subs, A, S(2).subs{:}); + S = shiftS(S,1); + else + A = feval(S(1).subs, A); + end + otherwise + error(['@paragraph.subsref: unknown field or method: ' S(1).subs]); + end + case '()' + A = A.elements{S(1).subs{:}}; + case '{}' + error(['@paragraph.subsref: ' S(1).type ' indexing not supported.']); + otherwise + error('@paragraph.subsref: impossible case') +end + +S = shiftS(S,1); +if length(S) >= 1 + A = subsref(A, S); +end +end diff --git a/matlab/reports/@paragraph/write.m b/matlab/reports/@paragraph/write.m new file mode 100644 index 000000000..3397b44a1 --- /dev/null +++ b/matlab/reports/@paragraph/write.m @@ -0,0 +1,70 @@ +function o = write(o, fid) +%function o = write(o, fid) +% Write Paragraph object +% +% INPUTS +% o [paragraph] paragraph object +% fid [integer] file id +% +% OUTPUTS +% o [paragraph] paragraph object +% +% SPECIAL REQUIREMENTS +% none + +% 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 . + +assert(fid ~= -1); +fprintf(fid, '%% Paragraph Object\n\\multicolumn{1}{p{\\textwidth}}{%%\n'); +if o.cols ~= 1 + bc = ''; + if o.balancedCols + bc = '*'; + end + fprintf(fid, '\\begin{multicols%s}{%d}%%\n', bc, o.cols); +end + +if ~isempty(o.heading) + if o.cols ~= 1 + fprintf(fid, '[%s\n]\n', o.heading); + else + fprintf(fid, '%s\\newline \\newline\n', o.heading); + end +end + +if o.indent + fprintf(fid, '\\hspace{4ex}'); +end + +fprintf(fid, '%s', o.text); + +if o.cols ~= 1 + fprintf(fid, '\\end{multicols%s}\n', bc); +end +fprintf(fid, '}\n%% End Paragraph Object\n\n'); +end + + +%\multicolumn{1}{p{\textwidth}} +%{\begin{multicols}{2} +%Hello, here is some text without a meaning. This text should show what +%a printed text will look like at this place. +%\columnbreak +%If you read this text, you will get no information. Really? Is there +%no information? Is there... +%\end{multicols}}\\ \ No newline at end of file diff --git a/matlab/reports/@report/addParagraph.m b/matlab/reports/@report/addParagraph.m new file mode 100644 index 000000000..4ec7209a3 --- /dev/null +++ b/matlab/reports/@report/addParagraph.m @@ -0,0 +1,34 @@ +function o = addParagraph(o, varargin) +%function o = addParagraph(o, varargin) +% Add a paragraph to the current section of the current page in the report +% +% INPUTS +% o [report] report object +% varargin arguments to @section/addGraph.m +% +% OUTPUTS +% o [report] updated report object +% +% SPECIAL REQUIREMENTS +% none + +% Copyright (C) 2013-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 . + +o.pages{end}.sections{end} = ... + o.pages{end}.sections{end}.addParagraph(varargin{:}); +end diff --git a/matlab/reports/@report/compile.m b/matlab/reports/@report/compile.m index 10d08bda5..6cb02c7bd 100644 --- a/matlab/reports/@report/compile.m +++ b/matlab/reports/@report/compile.m @@ -79,7 +79,7 @@ if status ~= 0 error(['@report.compile: There was an error in compiling ' rfn '.pdf.' ... ' ' compiler ' returned the error code: ' num2str(status)]); end -fprintf(1, '\n\nDone.\n') +fprintf(1, '\n\nDone.\n'); disp('Your compiled report is located here:'); disp([' ' pwd filesep rfn '.pdf']); diff --git a/matlab/reports/@report/report.m b/matlab/reports/@report/report.m index 4aabeb5a8..07340f9c4 100644 --- a/matlab/reports/@report/report.m +++ b/matlab/reports/@report/report.m @@ -57,7 +57,7 @@ elseif nargin > 1 % overwrite default values for pair = reshape(varargin, 2, []) - ind = strmatch(lower(pair{1}), lower(optNames), 'exact'); + ind = find(strcmpi(optNames, pair{1})); assert(isempty(ind) || length(ind) == 1); if ~isempty(ind) o.(optNames{ind}) = pair{2}; diff --git a/matlab/reports/@report/write.m b/matlab/reports/@report/write.m index 060f8ccda..6d1e187d1 100644 --- a/matlab/reports/@report/write.m +++ b/matlab/reports/@report/write.m @@ -41,7 +41,7 @@ if strcmpi(o.orientation, 'landscape') fprintf(fid, ',landscape'); end fprintf(fid, ']{geometry}\n'); -fprintf(fid, '\\usepackage{pdflscape, booktabs, pgfplots, colortbl, adjustbox}\n'); +fprintf(fid, '\\usepackage{pdflscape, booktabs, pgfplots, colortbl, adjustbox, multicol}\n'); fprintf(fid, '\\pgfplotsset{compat=1.5.1}'); fprintf(fid, ['\\makeatletter\n' ... '\\def\\blfootnote{\\gdef\\@thefnmark{}\\@footnotetext}\n' ... @@ -71,9 +71,9 @@ fprintf(fid, '\\renewcommand{\\bottomfraction}{0.8}\n'); fprintf(fid, '\\setlength{\\parindent}{0in}\n'); fprintf(fid, '\\newlength\\sectionheight\n'); fprintf(fid, '\\begin{document}\n'); -fprintf(fid, '\\pgfdeclarelayer{background}\n'); -fprintf(fid, '\\pgfdeclarelayer{foreground}\n'); -fprintf(fid, '\\pgfsetlayers{background,main,foreground}\n'); +fprintf(fid, '\\pgfdeclarelayer{background0}\n'); +fprintf(fid, '\\pgfdeclarelayer{background1}\n'); +fprintf(fid, '\\pgfsetlayers{background0,background1,main}\n'); fprintf(fid, '\\centering\n'); nps = length(o.pages); diff --git a/matlab/reports/@report_series/getNameForLegend.m b/matlab/reports/@report_series/getNameForLegend.m new file mode 100644 index 000000000..0748a8de2 --- /dev/null +++ b/matlab/reports/@report_series/getNameForLegend.m @@ -0,0 +1,31 @@ +function s = getNameForLegend(o) +%function s = getNameForLegend(o) + +% 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 . + +if isempty(o.data) || ~o.graphShowInLegend + % for the case when there is no data in the series + % e.g. graphVline was passed + % or when the user does not want this series shown in + % the legend + s = ''; +else + assert(size(o.data,2) == 1); + s = o.data.tex{:}; +end +end \ No newline at end of file diff --git a/matlab/reports/@report_series/getRange.m b/matlab/reports/@report_series/getRange.m index bf5b816bd..a0d1fd807 100644 --- a/matlab/reports/@report_series/getRange.m +++ b/matlab/reports/@report_series/getRange.m @@ -18,6 +18,10 @@ function dd = getRange(o) % You should have received a copy of the GNU General Public License % along with Dynare. If not, see . -assert(~isempty(o.data) && size(o.data, 2) == 1); -dd = o.data.dates; +if isempty(o.data) + dd = dates(); +else + assert(size(o.data, 2) == 1); + dd = o.data.dates; +end end \ No newline at end of file diff --git a/matlab/reports/@report_series/getTexName.m b/matlab/reports/@report_series/getTexName.m index 90e6ba233..0672508f9 100644 --- a/matlab/reports/@report_series/getTexName.m +++ b/matlab/reports/@report_series/getTexName.m @@ -1,7 +1,7 @@ function s = getTexName(o) %function s = getTexName(o) -% Copyright (C) 2013 Dynare Team +% Copyright (C) 2013-2014 Dynare Team % % This file is part of Dynare. % @@ -18,6 +18,12 @@ function s = getTexName(o) % You should have received a copy of the GNU General Public License % along with Dynare. If not, see . -assert(~isempty(o.data) && size(o.data, 2) == 1); -s = o.data.tex{:}; +if isempty(o.data) + % for the case when there is no data in the series + % e.g. graphVline was passed + s = ''; +else + assert(size(o.data,2) == 1); + s = o.data.tex{:}; +end end \ No newline at end of file diff --git a/matlab/reports/@report_series/printSeries.m b/matlab/reports/@report_series/printSeries.m index 15867a068..e1a6f0b49 100644 --- a/matlab/reports/@report_series/printSeries.m +++ b/matlab/reports/@report_series/printSeries.m @@ -36,7 +36,7 @@ dataString = sprintf('%%.%df', precision); precision = 10^precision; data = dser(dates); -data = data.data; +data = setDataToZeroFromZeroTol(o, data); for i=1:size(data,1) fprintf(fid, '&'); if o.tableShowMarkers diff --git a/matlab/reports/@report_series/report_series.m b/matlab/reports/@report_series/report_series.m index 41d6286b0..0fcb61137 100644 --- a/matlab/reports/@report_series/report_series.m +++ b/matlab/reports/@report_series/report_series.m @@ -35,15 +35,24 @@ o = struct; o.data = ''; +o.graphLegendName = ''; + o.graphLineColor = 'black'; o.graphLineStyle = 'solid'; o.graphLineWidth = 0.5; +o.graphShowInLegend = true; + o.graphMarker = ''; o.graphMarkerEdgeColor = ''; o.graphMarkerFaceColor = ''; o.graphMarkerSize = 1; +o.graphMiscTikzAddPlotOptions = ''; + +o.graphHline = {}; +o.graphVline = dates(); + o.tableShowMarkers = false; o.tableNegColor = 'red'; o.tablePosColor = 'blue'; @@ -74,7 +83,7 @@ elseif nargin > 1 % overwrite default values for pair = reshape(varargin, 2, []) - ind = strmatch(lower(pair{1}), lower(optNames), 'exact'); + ind = find(strcmpi(optNames, pair{1})); assert(isempty(ind) || length(ind) == 1); if ~isempty(ind) o.(optNames{ind}) = pair{2}; @@ -84,6 +93,10 @@ elseif nargin > 1 end end +if ~isempty(o.graphLegendName) + o.data = o.data.tex_rename(o.graphLegendName); +end + % Create report_series object o = class(o, 'report_series'); end \ No newline at end of file diff --git a/matlab/reports/@report_series/setDataToZeroFromZeroTol.m b/matlab/reports/@report_series/setDataToZeroFromZeroTol.m new file mode 100644 index 000000000..25daeb15f --- /dev/null +++ b/matlab/reports/@report_series/setDataToZeroFromZeroTol.m @@ -0,0 +1,28 @@ +function d = setDataToZeroFromZeroTol(o, ds) +%function d = setDataToZeroFromZeroTol(o, ds) + +% 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 . + +d = ds.data; +stz = bsxfun(@and, ... + bsxfun(@lt, d, o.zeroTol), ... + bsxfun(@gt, d, -o.zeroTol)); +if any(stz) + d(stz) = 0; +end +end \ No newline at end of file diff --git a/matlab/reports/@report_series/writeSeriesForGraph.m b/matlab/reports/@report_series/writeSeriesForGraph.m index 45f98f507..8628dd30d 100644 --- a/matlab/reports/@report_series/writeSeriesForGraph.m +++ b/matlab/reports/@report_series/writeSeriesForGraph.m @@ -30,8 +30,14 @@ function o = writeSeriesForGraph(o, fid, xrange) % along with Dynare. If not, see . %% Validate options provided by user -assert(~isempty(o.data) && isa(o.data, 'dseries'), ['@report_series.writeSeriesForGraph: must ' ... - 'provide data as a dseries']); +if isempty(o.graphVline) && isempty(o.graphHline) + assert(~isempty(o.data) && isdseries(o.data), ['@report_series.writeSeriesForGraph: must ' ... + 'provide data as a dseries']); +end + +assert(ischar(o.graphMiscTikzAddPlotOptions), ['@report_series.writeSeriesForGraph: ' ... + 'graphMiscTikzAddPlotOptions file must be a string']); +assert(islogical(o.graphShowInLegend), '@graph.graph: graphShowInLegend must be either true or false'); % Line valid_graphLineColor = {'red', 'green', 'blue', 'cyan ', 'magenta', 'yellow', ... @@ -61,12 +67,37 @@ assert(isfloat(o.graphMarkerSize) && o.graphMarkerSize > 0, ... assert(~(strcmp(o.graphLineStyle, 'none') && isempty(o.graphMarker)), ['@report_series.writeSeriesForGraph: ' ... 'you must provide at least one of graphLineStyle and graphMarker']); -% Validate xrange - +% Validate graphVline +assert(isempty(o.graphVline) || (isdates(o.graphVline) && o.graphVline.ndat == 1), ... + '@report_series.writeSeriesForGraph: graphVline must be a dates of size one'); +assert(isempty(o.graphHline) || isnumeric(o.graphHline), ... + '@report_series.writeSeriesForGraph: graphHline must a single numeric value'); % Zero tolerance assert(isfloat(o.zeroTol), '@report_series.write: zeroTol must be a float'); +%% graphVline && graphHline +if ~isempty(o.graphVline) + fprintf(fid, '%%Vertical Line\n\\begin{pgfonlayer}{background1}\n\\draw'); + writeLineOptions(o, fid); + stringsdd = strings(xrange); + x = find(strcmpi(date2string(o.graphVline), stringsdd)); + fprintf(fid, ['(axis cs:%d,\\pgfkeysvalueof{/pgfplots/ymin}) -- (axis ' ... + 'cs:%d,\\pgfkeysvalueof{/pgfplots/ymax});\n\\end{pgfonlayer}\n'], ... + x, x); +end +if ~isempty(o.graphHline) + fprintf(fid, '%%Horizontal Line\n\\begin{pgfonlayer}{background1}\n\\addplot'); + writeLineOptions(o, fid); + fprintf(fid, ['coordinates {(\\pgfkeysvalueof{/pgfplots/xmin},%f)' ... + '(\\pgfkeysvalueof{/pgfplots/xmax},%f)};\n\\end{pgfonlayer}\n'], ... + o.graphHline, o.graphHline); +end +if ~isempty(o.graphVline) || ~isempty(o.graphHline) + % return since the code below assumes that o.data exists + return +end + %% if isempty(xrange) || all(xrange == o.data.dates) ds = o.data; @@ -74,18 +105,20 @@ else ds = o.data(xrange); end -% if graphing data that is within zeroTol, set to zero, create report_series and -% get line: -thedata = ds.data; -stz = bsxfun(@and, ... - bsxfun(@lt, thedata, o.zeroTol), ... - bsxfun(@gt, thedata, -o.zeroTol)); -if any(stz) - thedata(stz) = 0; +thedata = setDataToZeroFromZeroTol(o, ds); +fprintf(fid, '%%series %s\n\\addplot', o.data.name{:}); +writeLineOptions(o, fid); +fprintf(fid,'\ntable[row sep=crcr]{\nx y\\\\\n'); +for i=1:ds.dates.ndat + fprintf(fid, '%d %f\\\\\n', i, thedata(i)); +end +fprintf(fid,'};\n'); end -fprintf(fid, '%%series %s\n\\addplot[color=%s,%s,line width=%fpt,line join=round',... - o.data.name{:}, o.graphLineColor, o.graphLineStyle, o.graphLineWidth); +function writeLineOptions(o, fid) +fprintf(fid, '[color=%s,%s,line width=%fpt,line join=round',... + o.graphLineColor, o.graphLineStyle, o.graphLineWidth); + if ~isempty(o.graphMarker) if isempty(o.graphMarkerEdgeColor) o.graphMarkerEdgeColor = o.graphLineColor; @@ -94,11 +127,10 @@ if ~isempty(o.graphMarker) o.graphMarkerFaceColor = o.graphLineColor; end fprintf(fid, ',mark=%s,mark size=%f,every mark/.append style={draw=%s,fill=%s}',... - o.graphMarker,o.graphMarkerSize,o.graphMarkerEdgeColor,o.graphMarkerFaceColor); + o.graphMarker,o.graphMarkerSize,o.graphMarkerEdgeColor,o.graphMarkerFaceColor); end -fprintf(fid,']\ntable[row sep=crcr]{\nx y\\\\\n'); -for i=1:ds.dates.ndat - fprintf(fid, '%d %f\\\\\n', i, thedata(i)); +if ~isempty(o.graphMiscTikzAddPlotOptions) + fprintf(fid, ',%s', o.graphMiscTikzAddPlotOptions); end -fprintf(fid,'};\n'); +fprintf(fid,']'); end diff --git a/matlab/reports/@report_series/writeSeriesForTable.m b/matlab/reports/@report_series/writeSeriesForTable.m index a35d0531a..2ef3e8845 100644 --- a/matlab/reports/@report_series/writeSeriesForTable.m +++ b/matlab/reports/@report_series/writeSeriesForTable.m @@ -35,18 +35,18 @@ function o = writeSeriesForTable(o, fid, dates, precision) %% Validate options passed to function assert(fid ~= -1); for i=1:length(dates) - assert(isa(dates{i}, 'dates')); + assert(isdates(dates{i})); end assert(isint(precision)); %% Validate options provided by user assert(ischar(o.tableSubSectionHeader), '@report_series.writeSeriesForTable: tableSubSectionHeader must be a string'); if isempty(o.tableSubSectionHeader) - assert(~isempty(o.data) && isa(o.data, 'dseries'), ... + assert(~isempty(o.data) && isdseries(o.data), ... '@report_series.writeSeriesForTable: must provide data as a dseries'); if ~isempty(o.tableDataRhs) - assert(~isempty(o.tableDataRhs) && isa(o.tableDataRhs, 'dseries'), ... + assert(~isempty(o.tableDataRhs) && isdseries(o.tableDataRhs), ... '@report_series.writeSeriesForTable: must provide tableDataRhs as a dseries'); assert(iscell(dates) && length(dates) == 2, ... '@report_series.writeSeriesForTable: must provide second range with tableDataRhs'); diff --git a/matlab/reports/@report_table/report_table.m b/matlab/reports/@report_table/report_table.m index c49ca5fb6..3fc1e2a7d 100644 --- a/matlab/reports/@report_table/report_table.m +++ b/matlab/reports/@report_table/report_table.m @@ -65,7 +65,7 @@ elseif nargin > 1 % overwrite default values for pair = reshape(varargin, 2, []) - ind = strmatch(lower(pair{1}), lower(optNames), 'exact'); + ind = find(strcmpi(optNames, pair{1})); assert(isempty(ind) || length(ind) == 1); if ~isempty(ind) o.(optNames{ind}) = pair{2}; @@ -78,7 +78,7 @@ if ~iscell(o.range) o.range = {o.range}; end -if isa(o.vlineAfter, 'dates') +if isdates(o.vlineAfter) o.vlineAfter = {o.vlineAfter}; end @@ -98,7 +98,7 @@ assert(isint(o.precision), '@report_table.report_table: precision must be an int assert(isempty(o.range) || length(o.range) <=2 && allCellsAreDatesRange(o.range), ... ['@report_table.report_table: range is specified as a dates range, e.g. ' ... '''dates(''1999q1''):dates(''1999q3'')''.']); -assert(isempty(o.data) || isa(o.data, 'dseries'), ... +assert(isempty(o.data) || isdseries(o.data), ... '@report_table.report_table: data must be a dseries'); assert(isempty(o.seriesToUse) || iscellstr(o.seriesToUse), ... '@report_table.report_table: seriesToUse must be a cell array of string(s)'); diff --git a/matlab/reports/@section/addGraph.m b/matlab/reports/@section/addGraph.m index cebfd9b6f..c3675764f 100644 --- a/matlab/reports/@section/addGraph.m +++ b/matlab/reports/@section/addGraph.m @@ -30,5 +30,9 @@ function o = addGraph(o, varargin) % You should have received a copy of the GNU General Public License % along with Dynare. If not, see . +for i=1:length(o.elements) + assert(~isa(o.elements{i}, 'paragraph'), ... + '@addGraph: A Section that contains a Paragraph cannot contain a Graph'); +end o.elements{end+1} = graph(varargin{:}); end diff --git a/matlab/reports/@section/addParagraph.m b/matlab/reports/@section/addParagraph.m new file mode 100644 index 000000000..11c228de2 --- /dev/null +++ b/matlab/reports/@section/addParagraph.m @@ -0,0 +1,42 @@ +function o = addParagraph(o, varargin) +%function o = addParagraph(o, varargin) +% Add a paragraph to the Cell Array of elements in this section +% +% INPUTS +% 1 args => add empty paragraph +% 2 args => add given paragraph +% 3 args => add paragraph at index +% +% OUTPUTS +% updated page object +% +% SPECIAL REQUIREMENTS +% none + +% 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 . + +assert(o.cols == 1, ... + ['@addParagraph: you can only add a paragraph to a Section that ' ... + 'contains one column']); +for i=1:length(o.elements) + assert(isa(o.elements{i}, 'paragraph'), ... + ['@addParagraph: you can only add a paragraph to a Section that ' ... + 'contains only paragraphs']); +end +o.elements{end+1} = paragraph(varargin{:}); +end diff --git a/matlab/reports/@section/addTable.m b/matlab/reports/@section/addTable.m index e941e8d92..30f79c73d 100644 --- a/matlab/reports/@section/addTable.m +++ b/matlab/reports/@section/addTable.m @@ -30,5 +30,9 @@ function o = addTable(o, varargin) % You should have received a copy of the GNU General Public License % along with Dynare. If not, see . +for i=1:length(o.elements) + assert(~isa(o.elements{i}, 'paragraph'), ... + '@addTable: A Section that contains a Paratable cannot contain a Table'); +end o.elements{end+1} = report_table(varargin{:}); end diff --git a/matlab/reports/@section/addVspace.m b/matlab/reports/@section/addVspace.m index 374e4642b..be40daab2 100644 --- a/matlab/reports/@section/addVspace.m +++ b/matlab/reports/@section/addVspace.m @@ -29,5 +29,9 @@ function o = addVspace(o, varargin) % You should have received a copy of the GNU General Public License % along with Dynare. If not, see . +for i=1:length(o.elements) + assert(~isa(o.elements{i}, 'paragraph'), ... + '@addVspace: A Section that contains a Paragraph cannot contain a Vspace'); +end o.elements{end+1} = vspace(varargin{:}); end diff --git a/matlab/reports/@section/section.m b/matlab/reports/@section/section.m index 4b756de7f..5042d94d0 100644 --- a/matlab/reports/@section/section.m +++ b/matlab/reports/@section/section.m @@ -40,7 +40,7 @@ elseif nargin > 1 % overwrite default values for pair = reshape(varargin, 2, []) - ind = strmatch(lower(pair{1}), lower(optNames), 'exact'); + ind = find(strcmpi(optNames, pair{1})); assert(isempty(ind) || length(ind) == 1); if ~isempty(ind) o.(optNames{ind}) = pair{2}; diff --git a/matlab/reports/@section/write.m b/matlab/reports/@section/write.m index 7a9808e8a..00a34f75c 100644 --- a/matlab/reports/@section/write.m +++ b/matlab/reports/@section/write.m @@ -33,6 +33,7 @@ function o = write(o, fid, pg, sec) assert(fid ~= -1); fprintf(fid, '%% Section Object\n'); + if ~isempty(o.height) fprintf(fid, '\\setlength\\sectionheight{%s}%%\n', o.height); end @@ -45,22 +46,34 @@ end fprintf(fid, '}{%%\n'); fprintf(fid, '\\begin{tabular}[t]{'); for i=1:o.cols - fprintf(fid, 'c'); + if ~isa(o.elements{1}, 'paragraph') + % if one element is a paragraph, they all are + % due to check in add*.m functions + fprintf(fid, 'l'); + else + fprintf(fid, 'c'); + end end fprintf(fid, '}\n'); + ne = numElements(o); -nlcounter = 0; row = 1; col = 1; for i=1:ne if isa(o.elements{i}, 'vspace') - assert(col == o.cols, ['@section.write: must place ' ... - 'vspace command after a linebreak in the table ' ... - 'or series of charts']); o.elements{i}.write(fid); fprintf(fid, '\\\\\n'); + if col ~= o.cols + fprintf(fid, '\\end{tabular}}\\\\\n'); + fprintf(fid, '%% End Section Object\n\n'); + return; + end else - o.elements{i}.write(fid, pg, sec, row, col); + if isa(o.elements{i}, 'paragraph') + o.elements{i}.write(fid); + else + o.elements{i}.write(fid, pg, sec, row, col); + end if col ~= o.cols fprintf(fid, ' & '); col = col + 1; diff --git a/matlab/reports/@vspace/vspace.m b/matlab/reports/@vspace/vspace.m index e61ca8149..a3aa809da 100644 --- a/matlab/reports/@vspace/vspace.m +++ b/matlab/reports/@vspace/vspace.m @@ -12,7 +12,7 @@ function o = vspace(varargin) % SPECIAL REQUIREMENTS % none -% Copyright (C) 2013 Dynare Team +% Copyright (C) 2013-2014 Dynare Team % % This file is part of Dynare. % @@ -48,7 +48,7 @@ elseif nargin > 1 % overwrite default values for pair = reshape(varargin, 2, []) - ind = strmatch(lower(pair{1}), lower(optNames), 'exact'); + ind = find(strcmpi(optNames, pair{1})); assert(isempty(ind) || length(ind) == 1); if ~isempty(ind) o.(optNames{ind}) = pair{2}; diff --git a/matlab/reports/allCellsAreDates.m b/matlab/reports/allCellsAreDates.m index 533ff49e4..7beca1b00 100644 --- a/matlab/reports/allCellsAreDates.m +++ b/matlab/reports/allCellsAreDates.m @@ -31,7 +31,7 @@ function tf = allCellsAreDates(dcell) assert(iscell(dcell)); tf = true; for i=1:length(dcell) - if ~isa(dcell{i}, 'dates') + if ~isdates(dcell{i}) tf = false; return; end diff --git a/matlab/reports/allCellsAreDatesRange.m b/matlab/reports/allCellsAreDatesRange.m index 4a12ac573..fd3e111d8 100644 --- a/matlab/reports/allCellsAreDatesRange.m +++ b/matlab/reports/allCellsAreDatesRange.m @@ -31,7 +31,7 @@ function tf = allCellsAreDatesRange(dcell) assert(iscell(dcell)); tf = true; for i=1:length(dcell) - if ~(isa(dcell{i}, 'dates') && dcell{i}.ndat >= 2) + if ~(isdates(dcell{i}) && dcell{i}.ndat >= 2) tf = false; return; end diff --git a/matlab/reports/display_reporting_object.m b/matlab/reports/display_reporting_object.m index e0c52d8e6..fc7ba46d0 100644 --- a/matlab/reports/display_reporting_object.m +++ b/matlab/reports/display_reporting_object.m @@ -69,10 +69,14 @@ for i=1:length(fields) fprintf('false'); end elseif isobject(val) - if isa(val, 'dates') - fprintf('', ... + if isdates(val) + if isempty(val) + fprintf(''); + else + fprintf('', ... date2string(val(1)), date2string(val(end))); - elseif isa(val, 'dseries') + end + elseif isdseries(val) if numel(val) == 1 fprintf('', val.name{1}); else diff --git a/matlab/reports/getMaxRange.m b/matlab/reports/getMaxRange.m index 59a3ac643..1a578083c 100644 --- a/matlab/reports/getMaxRange.m +++ b/matlab/reports/getMaxRange.m @@ -23,12 +23,14 @@ ddmax = dates(); ne = length(cellser); for i=1:ne ddt = cellser{i}.getRange(); - if isempty(ddmin) - ddmin = ddt(1); - ddmax = ddt(end); - else - ddmin = min(ddt(1), ddmin); - ddmax = max(ddt(end), ddmax); + if ~isempty(ddt) + if isempty(ddmin) + ddmin = ddt(1); + ddmax = ddt(end); + else + ddmin = min(ddt(1), ddmin); + ddmax = max(ddt(end), ddmax); + end end end dd = ddmin:ddmax; \ No newline at end of file diff --git a/tests/ep/rs2.mod b/tests/ep/rs2.mod new file mode 100644 index 000000000..666ddb68e --- /dev/null +++ b/tests/ep/rs2.mod @@ -0,0 +1,163 @@ +var V lL Vkp Valphaexp lzn lzd lp0 lpi MC lwreal lY lDisp lC lpiavg Int lA lDZ lG pistar Intr +lsdf Int1 lpi1; +// pricebond pricebondrn ytm ytmrn termprem ehpr slope; +varexo epsA epsZ epsG epsPistar epsInt; + +parameters theta xi beta phi alpha eta KBar chi0 LMax chi IBar rhoinflavg taylrho DZBar taylpi tayly YBar rhoa rhoz rhog rhopistar piBar gssload consoldelta VAIMSS GBar; + + DZBar = 1.0025; + eta = 2/3; + IES = .11; + phi = 1/IES; + beta = .99 *DZBar^phi; + delta = .02; + + Frisch = .28; + CRRA = 110; + + LMax = 3; +// chi0 = exp(wrealAIMSS -phi*CAIMSS) *(LMax-1)^chi; // normalize L in ss = 1 + chi0 = 1/3; + chi = 1/Frisch *(LMax-1); + alpha = (CRRA - 1/(1/phi + 1/chi *(LMax-1))) *(1/(1-phi) + 1/(1-chi) *(LMax-1)); + + theta = .2; + xi = .78; + + K_Y = 10; + YBar = K_Y^((1-eta)/eta); + GBar = .17 *YBar; + KBar = K_Y *YBar; + IBar = (delta +(DZBar-1)) *KBar; + piBar = 1.0; + + taylrho = .73; + taylpi = .53; + tayly = .93; + + rhoa = .96; + rhoz = 0; + rhog = .95; + rhopistar = 0; + epsafl = 1; // these flags turn on or off each corresponding shock in the model + epsapermfl = 0; + epsgfl = 1; + epsifl = 1; + epspistarfl = 0; + rhoinflavg = .7; // for price level targeting; set rhoinflavg = .99 or .999 + gssload = 0; // this is the GSS theta parameter in the long-run nominal risks section of the paper + + VAIMSS = 1; + consoldelta = 1; + +model;//(use_dll); +// Value function and Euler equation +V = exp(lC)^(1-phi) /(1-phi) + chi0 *(LMax-exp(lL))^(1-chi) /(1-chi) + beta *Vkp; +Int1 = Int/10; +lpi1 = lpi/10; +exp(lC)^-phi = beta *exp(10*Int1-10*lpi1(+1)) *exp(lC(+1))^-phi *exp(lDZ(+1))^-phi + *(V(+1) *exp(lDZ(+1))^(1-phi) /Vkp)^-alpha; +// The following two equations define the E-Z-W-K-P certainty equivalent term +// Vkp = (E_t V(+1)^(1-alpha))^(1/(1-alpha)). It takes two equations to do this because +// Perturbation AIM sets the expected value of all equations equal to zero; E_t F(variables) = 0. +// Thus; the first equation below defines Valphaexp = E_t V(+1)^(1-alpha). The second +// equation then takes the (1-alpha)th root of this expectation. +// Finally; the scaling and unscaling of Valphaexp by the constant VAIMSS and DZBar improves the +// numerical behavior of the model; without it; the steady-state value of Valphaexp can be minuscule +// (e.g.; 10^-50); which requires Mathematica to use astronomical levels of precision to solve. *) +[mcp = 'Valphaexp > 1e-5'] +Valphaexp = (V(+1) *exp(lDZ(+1))^(1-phi) /VAIMSS /DZBar^(1-phi))^(1-alpha); +Vkp = VAIMSS *DZBar^(1-phi) *Valphaexp^(1/(1-alpha)); + +exp(lzn) = (1+theta) *MC *exp(lY) + xi *beta *exp(lC(+1)-lC)^-phi *exp(lDZ(+1))^-phi + *(V(+1) *exp(lDZ(+1))^(1-phi) /Vkp)^-alpha *exp(lpi(+1))^((1+theta)/theta/eta) *exp(lzn(+1)); +exp(lzd) = exp(lY) + xi *beta *exp(lC(+1)-lC)^-phi*exp(lDZ(+1))^-phi + *(V(+1) *exp(lDZ(+1))^(1-phi) /Vkp)^-alpha *exp(lpi(+1))^(1/theta) *exp(lzd(+1)); +exp(lp0)^(1+(1+theta)/theta *(1-eta)/eta) = exp(lzn-lzd); +exp(lpi)^(-1/theta) = (1-xi) *exp(lp0+lpi)^(-1/theta) + xi; +// Marginal cost and real wage +MC = exp(lwreal) /eta *exp(lY)^((1-eta)/eta) /exp(lA)^(1/eta) /KBar^((1-eta)/eta); +[mcp = 'lL < 1.0986'] +chi0 *(LMax-exp(lL))^-chi /exp(lC)^-phi = exp(lwreal); +// Output equations +exp(lY) = exp(lA) *KBar^(1-eta) *exp(lL)^eta /exp(lDisp); +exp(lDisp)^(1/eta) = (1-xi) *exp(lp0)^(-(1+theta)/theta/eta) + + xi *exp(lpi)^((1+theta)/theta/eta) *exp(lDisp(-1))^(1/eta); +exp(lC) = exp(lY)-exp(lG)-IBar; // aggregate resource constraint; no adj costs +// Monetary Policy Rule +lpiavg = rhoinflavg *lpiavg(-1) + (1-rhoinflavg) *lpi; +4*Int = (1-taylrho) * ( 4*log(1/beta *DZBar^phi) + 4*lpiavg + + taylpi * (4*lpiavg-pistar) + tayly * (exp(lY)-YBar)/YBar ) + + taylrho * 4*Int(-1) + epsInt; // multiply Int; infl by 4 to put at annual rate +// Exogenous Shocks +lA = rhoa * lA(-1) + epsA; +lDZ = (1-rhoz)*log(DZBar) + rhoz * lDZ(-1) + epsZ; +lG = (1-rhog)*log(GBar) + rhog * lG(-1) + epsG; +pistar = (1-rhopistar) *log(piBar) + rhopistar *pistar(-1) + gssload *(4*lpiavg-pistar) + + epsPistar; +// Term premium and other auxiliary finance equations +Intr = Int(-1) - lpi; // ex post real short rate +exp(lsdf) = beta *exp(lC(+1)-lC)^-phi *exp(lDZ(+1))^-phi + *(V(+1) *exp(lDZ(+1))^(1-phi) /Vkp)^-alpha /exp(lpi(+1)); +//pricebond = 1 + consoldelta *beta *exp(lC(+1)-lC)^-phi *exp(lDZ(+1))^-phi +// *(V(+1) *exp(lDZ(+1))^(1-phi) /Vkp)^-alpha /pi(+1) *pricebond(+1); +//pricebondrn = 1 + consoldelta *pricebondrn(+1) /exp(Int); +//ytm = log(consoldelta*pricebond/(pricebond-1)) *400; // yield in annualized pct +//ytmrn = log(consoldelta*pricebondrn/(pricebondrn-1)) *400; +//termprem = 100 * (ytm–ytmrn); // term prem in annualized basis points +//ehpr = ( (consoldelta *pricebond + exp(Int(-1))) /pricebond(-1)–exp(Int(-1)) *400; +//slope = ytm–Int*400; +end; + +steady_state_model; +lA = 0; +lDZ = log(DZBar); +lG = log(GBar); +pistar = log(piBar); +lpi = log(piBar); +lpiavg = lpi; +lDisp = 0; +Int = log(DZBar^phi/beta)+lpi; +Int1 = Int/10; +lpi1 = lpi/10; +lL = 0; +lY = log(YBar); +lC = log(exp(lY)-exp(lG)-IBar); +lp0 = log(((exp(lpi)^(-1/theta)-xi)/((1-xi)*exp(lpi)^(-1/theta)))^(-theta)); +lzd = log(exp(lY)/(1-xi *beta *exp(lDZ)^-phi + *exp(lpi)^(1/theta))); +lzn = log(exp(lp0)^(1+(1+theta)/theta *(1-eta)/eta)*exp(lzd)); +MC = (exp(lzn)-xi *beta *DZBar^-phi*piBar^((1+theta)/theta/eta)*exp(lzn))/((1+theta)*YBar); +lwreal = log(MC*eta /(exp(lY)^((1-eta)/eta) / KBar^((1-eta)/eta))); + +chi0 = exp(lwreal)*exp(lC)^(-phi)/(LMax-exp(lL))^(-chi); +V = (exp(lC)^(1-phi) /(1-phi) + chi0 *(LMax-exp(lL))^(1-chi) /(1-chi))/(1-beta*DZBar^(1-phi)); +k = 1.05; +VAIMSS = k*V; +Valphaexp = k^(alpha-1); +Vkp = DZBar^(1-phi)*V; +Intr = Int - lpi; +lsdf = log(beta *exp(lDZ)^-phi/exp(lpi)); +end; + +steady; + +shocks; +var epsA; stderr 0.005; +var epsZ; stderr 0.001; +var epsG; stderr 0.004; +var epsPistar; stderr 0.0005; +var epsInt; stderr 0.003; +end; + +//stoch_simul(order=3,periods=50000,pruning); + +options_.ep.verbosity=0; +options_.ep.stochastic.algo=1; +options_.ep.solve_algo = 10; +options_.ep.maxit = 100; +options_.ep.IntegrationAlgorithm='UT_2p+1'; +options_.ep.ut.k = 1; +options_.solve_tolf = 1e-12; + +extended_path(order=1,periods=3); \ No newline at end of file diff --git a/tests/reporting/runDynareReport.m b/tests/reporting/runDynareReport.m index 5a08c73d6..305e2736d 100644 --- a/tests/reporting/runDynareReport.m +++ b/tests/reporting/runDynareReport.m @@ -144,7 +144,7 @@ rep = rep.addSection(); rep = CommResidTablePage(rep, db_q, dc_q, trange, dates('2012q2')); %% Commodities Graphs -%Page 1 +%Page 24 rep = rep.addPage('title', 'Jan1 vs Jan2', ... 'titleFormat', '\large\bfseries'); rep = rep.addSection(); @@ -184,12 +184,25 @@ rep = rep.addSeries('data', db_q{'LRPFOOD_BAR_WORLD'}, ... 'graphLineColor', 'green', ... 'graphLineStyle', 'solid', ... 'graphLineWidth', 1.5); +rep = rep.addSeries('graphVline', dates('2009q2'), ... + 'graphLineColor', 'red', ... + 'graphLineWidth', 1.5); -% Pae 2 +% Page 25 rep = rep.addPage('title', {'Jan1 vs Jan2', 'World Oil and Food Prices'}, ... 'titleFormat', {'\large\bfseries', '\large'}); -rep = rep.addSection('cols', 2); +rep = rep.addSection('cols', 1); +rep = rep.addParagraph('text', 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.', ... + 'cols', 2, ... + 'heading', '\textbf{My First Paragraph Has Two Columns}'); +rep = rep.addSection('cols', 1); +rep = rep.addParagraph('text', 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.\newline', ... + 'heading', '\textbf{My Next Paragraphs Only Have One}', ... + 'indent', false); +rep = rep.addParagraph('text', 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\newline'); + +rep = rep.addSection('cols', 2); rep = rep.addGraph('title', 'World Real Oil Price', ... 'xrange', prange, ... @@ -231,6 +244,10 @@ rep = rep.addSeries('data', dc_q{'LRPFOOD_WORLD'}, ... 'graphLineColor', 'blue', ... 'graphLineStyle', 'dashed', ... 'graphLineWidth', 1.5); +rep = rep.addSeries('graphHline', 460, ... + 'graphLineColor', 'red', ... + 'graphLineWidth', 1.5); + rep = rep.addGraph('title', 'Equilibrium World Real Food Price', ... 'xrange', prange, ... @@ -239,11 +256,15 @@ rep = rep.addGraph('title', 'Equilibrium World Real Food Price', ... 'xTickLabelRotation', 0); rep = rep.addSeries('data', db_q{'LRPFOOD_BAR_WORLD'}, ... 'graphLineColor', 'blue', ... - 'graphLineWidth', 1.5); + 'graphLineWidth', 1.5, ... + 'graphLegendName', 'baseline', ... + 'graphMiscTikzAddPlotOptions', 'mark=halfcircle*,color=red'); rep = rep.addSeries('data', dc_q{'LRPFOOD_BAR_WORLD'}, ... 'graphLineColor', 'blue', ... 'graphLineStyle', 'dashed', ... - 'graphLineWidth', 1.5); + 'graphLineWidth', 1.5, ... + 'graphLegendName', 'control', ... + 'graphMiscTikzAddPlotOptions', 'mark=halfcircle*,mark options={rotate=90,scale=3}'); %% Write & Compile Report rep.write();