From 21a8a5794a83b5098cad0fe7beab93dcbe5ef6bf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=A9bastien=20Villemot?= <sebastien@dynare.org>
Date: Wed, 15 Dec 2021 17:31:46 +0100
Subject: [PATCH] Various new model editing features
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

– multiple “model” and “estimated_params” block are supported
– new “model_options” statement to set model options in a global fashion
– new “model_remove” command to remove equations
– new “model_replace” block to replace equations
– new “var_remove” command to remove variables (or parameters)
– new “estimated_params_remove” block to remove estimated parameters
---
 doc/manual/source/the-model-file.rst          |  97 +++++++++++++++++
 preprocessor                                  |   2 +-
 scripts/dynare.el                             |  12 +-
 tests/Makefile.am                             |   5 +
 .../fs2000_estimated_params_remove.mod        | 103 ++++++++++++++++++
 tests/ramst_model_edit.mod                    |  62 +++++++++++
 6 files changed, 274 insertions(+), 7 deletions(-)
 create mode 100644 tests/estimation/fs2000_estimated_params_remove.mod
 create mode 100644 tests/ramst_model_edit.mod

diff --git a/doc/manual/source/the-model-file.rst b/doc/manual/source/the-model-file.rst
index d49c15e7be..f24712da44 100644
--- a/doc/manual/source/the-model-file.rst
+++ b/doc/manual/source/the-model-file.rst
@@ -332,6 +332,11 @@ for declaring variables and parameters are described below.
         Here, in the whole model file, ``alpha`` and ``beta`` will be
         endogenous and ``y`` and ``w`` will be parameters.
 
+.. command:: var_remove VAR_NAME | PARAM_NAME...;
+
+    Removes the listed variables (or parameters) from the model. Removing a
+    variable that has already been used in a model equation or elsewhere will
+    lead to an error.
 
 .. command:: predetermined_variables VAR_NAME...;
 
@@ -972,6 +977,11 @@ The model is declared inside a ``model`` block:
 
     More information on tags is available at `<https://git.dynare.org/Dynare/dynare/-/wikis/Equations-Tags>`__.
 
+    There can be several ``model`` blocks, in which case they are simply
+    concatenated. The set of effective options is also the concatenation of the
+    options declared in all the blocks, but in that case you may rather want to
+    use the :comm:`model_options` command.
+
     *Options*
 
     .. option:: linear
@@ -1131,6 +1141,82 @@ The model is declared inside a ``model`` block:
          y = d*y(-1)+e_y;
          end;
 
+.. command:: model_options (OPTIONS...);
+
+    This command accepts the same options as the :bck:`model` block.
+
+    The purpose of this statement is to specify the options that apply to the
+    whole model, when there are several ``model`` blocks, so as to restore the
+    symmetry between those blocks (since otherwise one ``model`` block would
+    typically bear the options, while the other ones would typically have no
+    option).
+
+.. command:: model_remove (TAGS...);
+
+    This command removes equations that appeared in a previous :bck:`model`
+    block.
+
+    The equations must be specified by a list of tag values, separated by
+    commas. Each element of the list is either a simple quoted string, in which
+    case it designates an equation by its ``name`` tag; or a tag name (without
+    quotes), followed by an equal sign, then by the tag value (within quotes).
+
+    Each removed equation must either have an ``endogenous`` tag, or have a
+    left hand side containing a single endogenous variable. The corresponding
+    endogenous variable will be either turned into an exogenous (if it is still
+    used in somewhere in the model at that point), otherwise it will be removed
+    from the model.
+
+    *Example*
+
+        ::
+
+         var c k dummy1 dummy2;
+
+         model;
+           c + k - aa*x*k(-1)^alph - (1-delt)*k(-1) + dummy1;
+           c^(-gam) - (1+bet)^(-1)*(aa*alph*x(+1)*k^(alph-1) + 1 - delt)*c(+1)^(-gam);
+           [ name = 'eq:dummy1', endogenous = 'dummy1' ]
+           c*k = dummy1;
+           [ foo = 'eq:dummy2' ]
+           log(dummy2) = k + 2;
+         end;
+
+         model_remove('eq:dummy1', foo = 'eq:dummy2');
+
+        In the above example, the last two equations will be removed,
+        ``dummy1`` will be turned into an exogenous, and ``dummy2`` will be
+        removed.
+
+
+.. block:: model_replace (TAGS...);
+
+    This block replaces several equations in the model. It removes the
+    equations given by the tags list (with the same syntax as in
+    :comm:`model_remove`), and it adds equations given within the block (with
+    the same syntax as :bck:`model`).
+
+    No variable is removed or has its type changed in the process.
+
+    *Example*
+
+        ::
+
+         var c k;
+
+         model;
+           c + k - aa*x*k(-1)^alph - (1-delt)*k(-1);
+           [ name = 'dummy' ]
+           c*k = 1;
+         end;
+
+         model_replace('dummy');
+           c^(-gam) = (1+bet)^(-1)*(aa*alph*x(+1)*k^(alph-1) + 1 - delt)*c(+1)^(-gam);
+         end;
+
+        In the above example, the dummy equation is replaced by a proper
+        Euler equation.
+
 
 Dynare has the ability to output the original list of model equations
 to a LaTeX file, using the ``write_latex_original_model``
@@ -5381,6 +5467,9 @@ block decomposition of the model (see :opt:`block`).
             end;
 
 
+    It is possible to have several ``estimated_params`` blocks, in which case
+    they will be concatenated.
+
 .. block:: estimated_params_init ;
            estimated_params_init (OPTIONS...);
 
@@ -5420,6 +5509,14 @@ block decomposition of the model (see :opt:`block`).
     See :bck:`estimated_params`, for the meaning and syntax of the
     various components.
 
+.. block:: estimated_params_remove ;
+
+    |br| This block partially undoes the effect of a previous
+    :bck:`estimated_params` block, by removing some parameters from the estimation.
+
+    Each line has the following syntax::
+
+        stderr VARIABLE_NAME | corr VARIABLE_NAME_1, VARIABLE_NAME_2 | PARAMETER_NAME;
 
 .. _estim-comm:
 
diff --git a/preprocessor b/preprocessor
index a73b0d911a..5ffbc5bad3 160000
--- a/preprocessor
+++ b/preprocessor
@@ -1 +1 @@
-Subproject commit a73b0d911a6ac6dc4628aca1cd614732d639f555
+Subproject commit 5ffbc5bad31c306f561d673cde36d460cb5906c1
diff --git a/scripts/dynare.el b/scripts/dynare.el
index d1ec1f786c..39ae6dd11c 100644
--- a/scripts/dynare.el
+++ b/scripts/dynare.el
@@ -79,7 +79,7 @@
     "smoother2histval" "perfect_foresight_setup" "perfect_foresight_solver"
     "perfect_foresight_with_expectation_errors_setup"
     "perfect_foresight_with_expectation_errors_solver"
-    "compilation_setup"
+    "compilation_setup" "model_remove" "model_options" "var_remove"
     "std" "corr" "prior_function" "posterior_function" "end")
   "Dynare statement keywords.")
 
@@ -98,11 +98,11 @@
   (defvar dynare-blocks
     '("model" "steady_state_model" "initval" "endval" "histval" "shocks" "heteroskedastic_shocks"
       "shock_groups" "init2shocks" "mshocks" "estimated_params" "epilogue" "priors"
-      "estimated_params_init" "estimated_params_bounds" "osr_params_bounds"
-      "observation_trends" "deterministic_trends" "optim_weights" "homotopy_setup"
-      "conditional_forecast_paths" "svar_identification" "moment_calibration"
-      "irf_calibration" "ramsey_constraints" "generate_irfs"
-      "matched_moments" "occbin_constraints" "verbatim")
+      "estimated_params_init" "estimated_params_bounds" "estimated_params_remove"
+      "osr_params_bounds" "observation_trends" "deterministic_trends" "optim_weights"
+      "homotopy_setup" "conditional_forecast_paths" "svar_identification"
+      "moment_calibration" "irf_calibration" "ramsey_constraints" "generate_irfs"
+      "matched_moments" "occbin_constraints" "model_replace" "verbatim")
     "Dynare block keywords."))
 
 ;; Mathematical functions and operators used in model equations (see "hand_side" in Bison file)
diff --git a/tests/Makefile.am b/tests/Makefile.am
index c186004b96..9bff02c43d 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -49,6 +49,7 @@ MODFILES = \
 	estimation/fs2000_with_weibull_prior.mod \
 	estimation/fs2000_initialize_from_calib.mod	\
 	estimation/fs2000_estimated_params_init.mod	\
+	estimation/fs2000_estimated_params_remove.mod	\
 	estimation/fs2000_calibrated_covariance.mod \
 	estimation/fs2000_model_comparison.mod \
 	estimation/fs2000_fast.mod \
@@ -93,6 +94,7 @@ MODFILES = \
 	ramst_static_tag.mod \
 	ramst_static_tag_block.mod \
 	ramst_mshocks.mod \
+	ramst_model_edit.mod \
 	on-the-fly/ex1.mod \
 	on-the-fly/ex2.mod \
 	on-the-fly/ex3.mod \
@@ -904,6 +906,9 @@ particle/local_state_space_iteration_k_test.o.trs: particle/first_spec.o.trs
 pruning/AS_pruned_state_space_red_shock.m.trs: pruning/AnSchorfheide_pruned_state_space.m.trs
 pruning/AS_pruned_state_space_red_shock.o.trs: pruning/AnSchorfheide_pruned_state_space.o.trs
 
+ramst_model_edit.m.trs: ramst.m.trs
+ramst_model_edit.o.trs: ramst.o.trs
+
 observation_trends_and_prefiltering/MCMC: m/observation_trends_and_prefiltering/MCMC o/observation_trends_and_prefiltering/MCMC
 m/observation_trends_and_prefiltering/MCMC: $(patsubst %.mod, %.m.trs, $(filter observation_trends_and_prefiltering/MCMC/%.mod, $(MODFILES)))
 o/observation_trends_and_prefiltering/MCMC: $(patsubst %.mod, %.o.trs, $(filter observation_trends_and_prefiltering/MCMC/%.mod, $(MODFILES)))
diff --git a/tests/estimation/fs2000_estimated_params_remove.mod b/tests/estimation/fs2000_estimated_params_remove.mod
new file mode 100644
index 0000000000..bd7e1157e2
--- /dev/null
+++ b/tests/estimation/fs2000_estimated_params_remove.mod
@@ -0,0 +1,103 @@
+// Tests multiple estimated_params blocks, and the estimated_params_remove block
+
+var m P c e W R k d n l gy_obs gp_obs y dA;
+varexo e_a e_m;
+
+parameters alp bet gam mst rho psi del;
+
+alp = 0.33;
+bet = 0.99;
+gam = 0.003;
+mst = 1.011;
+rho = 0.7;
+psi = 0.787;
+del = 0.02;
+
+model;
+dA = exp(gam+e_a);
+log(m) = (1-rho)*log(mst) + rho*log(m(-1))+e_m;
+-P/(c(+1)*P(+1)*m)+bet*P(+1)*(alp*exp(-alp*(gam+log(e(+1))))*k^(alp-1)*n(+1)^(1-alp)+(1-del)*exp(-(gam+log(e(+1)))))/(c(+2)*P(+2)*m(+1))=0;
+W = l/n;
+-(psi/(1-psi))*(c*P/(1-n))+l/n = 0;
+R = P*(1-alp)*exp(-alp*(gam+e_a))*k(-1)^alp*n^(-alp)/W;
+1/(c*P)-bet*P*(1-alp)*exp(-alp*(gam+e_a))*k(-1)^alp*n^(1-alp)/(m*l*c(+1)*P(+1)) = 0;
+c+k = exp(-alp*(gam+e_a))*k(-1)^alp*n^(1-alp)+(1-del)*exp(-(gam+e_a))*k(-1);
+P*c = m;
+m-1+d = l;
+e = exp(e_a);
+y = k(-1)^alp*n^(1-alp)*exp(-alp*(gam+e_a));
+gy_obs = dA*y/y(-1);
+gp_obs = (P/P(-1))*m(-1)/dA;
+end;
+
+steady_state_model;
+  dA = exp(gam);
+  gst = 1/dA;
+  m = mst;
+  khst = ( (1-gst*bet*(1-del)) / (alp*gst^alp*bet) )^(1/(alp-1));
+  xist = ( ((khst*gst)^alp - (1-gst*(1-del))*khst)/mst )^(-1);
+  nust = psi*mst^2/( (1-alp)*(1-psi)*bet*gst^alp*khst^alp );
+  n  = xist/(nust+xist);
+  P  = xist + nust;
+  k  = khst*n;
+
+  l  = psi*mst*n/( (1-psi)*(1-n) );
+  c  = mst/P;
+  d  = l - mst + 1;
+  y  = k^alp*n^(1-alp)*gst^alp;
+  R  = mst/bet;
+  W  = l/n;
+  ist  = y-c;
+  q  = 1 - d;
+
+  e = 1;
+  
+  gp_obs = m/dA;
+  gy_obs = dA;
+end;
+
+
+shocks;
+var e_a; stderr 0.014;
+var e_m; stderr 0.005;
+end;
+
+steady;
+
+check;
+
+estimated_params;
+alp, beta_pdf, 0.356, 0.02;
+bet, beta_pdf, 0.993, 0.002;
+gam, normal_pdf, 0.0085, 0.003;
+mst, normal_pdf, 1.0002, 0.007;
+stderr e, normal_pdf, 0, 1;
+corr e, m, normal_pdf, 0, 1;
+corr e_a, e_m, normal_pdf, 0, 1;
+end;
+
+estimated_params_remove;
+stderr e;
+corr e, m;
+corr e_a, e_m;
+end;
+
+estimated_params;
+rho, beta_pdf, 0.129, 0.223;
+psi, beta_pdf, 0.65, 0.05;
+del, beta_pdf, 0.01, 0.005;
+stderr e_a, inv_gamma_pdf, 0.035449, inf;
+stderr e_m, inv_gamma_pdf, 0.008862, inf;
+end;
+
+varobs gp_obs gy_obs;
+
+options_.solve_tolf = 1e-12;
+
+estimation(order=1,datafile=fsdat_simul,nobs=192,loglinear,mh_replic=0) y m;
+
+if size(estim_params_.var_exo, 1) ~= 2 || size(estim_params_.param_vals, 1) ~= 7 ...
+   || size(estim_params_.var_endo, 1) ~= 0 || size(estim_params_.corrn, 1) ~= 0 ...
+   || size(estim_params_.corrx, 1) ~= 0
+    error('Error in multiple estimated_params or in estimated_params_remove')
+end
diff --git a/tests/ramst_model_edit.mod b/tests/ramst_model_edit.mod
new file mode 100644
index 0000000000..a5600d6e6b
--- /dev/null
+++ b/tests/ramst_model_edit.mod
@@ -0,0 +1,62 @@
+/* Test for multiple model blocks, model_remove, model_options and var_remove commands,
+   and model_replace block.
+   It should give the same results as ramst.mod. */
+
+var c k;
+varexo x;
+
+var dummy1 dummy2 dummy3;
+
+parameters alph gam delt bet aa;
+alph=0.5;
+gam=0.5;
+delt=0.02;
+bet=0.05;
+aa=0.5;
+
+model;
+  c + k - aa*x*k(-1)^alph - (1-delt)*k(-1);
+end;
+
+model;
+  [ name = 'eq:dummy1', endogenous = 'dummy1' ]
+  dummy1 = c + 1;
+  [ foo = 'eq:dummy2' ] // Since dummy2 is alone on the LHS, it is considered as the variable set by this equation
+  log(dummy2) = k + 2;
+  [ name = 'eq:dummy3' ]
+  c(+1) = c;
+end;
+
+model_options(block);
+
+model_remove('eq:dummy1', foo = 'eq:dummy2');
+
+model_replace('eq:dummy3');
+c^(-gam) - (1+bet)^(-1)*(aa*alph*x(+1)*k^(alph-1) + 1 - delt)*c(+1)^(-gam);
+end;
+
+var_remove dummy3;
+
+initval;
+x = 1;
+k = ((delt+bet)/(1.0*aa*alph))^(1/(alph-1));
+c = aa*k^alph-delt*k;
+end;
+
+steady;
+
+check;
+
+shocks;
+var x;
+periods 1;
+values 1.2;
+end;
+
+perfect_foresight_setup(periods=200);
+perfect_foresight_solver;
+
+S = load('ramst/Output/ramst_results.mat');
+if any(size(oo_.endo_simul) ~= size(S.oo_.endo_simul)) || any(any(abs(oo_.endo_simul - S.oo_.endo_simul) > 1e-10))
+  error('Model editing failure')
+end
-- 
GitLab