From db8c5439f7d0e82837d645bf3d52b8489b66563c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=A9bastien=20Villemot?= <sebastien@dynare.org>
Date: Tue, 23 Jun 2020 17:50:50 +0200
Subject: [PATCH] use_dll: higher order derivatives are now returned as sparse
 matrices by static/dynamic files

Previously they were returned as 3-column matrices. But this was inconsistent
with the M-file mode.
---
 src/DynamicModel.cc | 104 +++++++++++++++++++++++++++++---------------
 src/ModelTree.cc    |  11 -----
 src/ModelTree.hh    |   4 --
 src/StaticModel.cc  |  87 ++++++++++++++++++++++--------------
 4 files changed, 124 insertions(+), 82 deletions(-)

diff --git a/src/DynamicModel.cc b/src/DynamicModel.cc
index fb809507..ea237b54 100644
--- a/src/DynamicModel.cc
+++ b/src/DynamicModel.cc
@@ -1476,18 +1476,40 @@ DynamicModel::writeDynamicCFile(const string &basename) const
          << endl
          << "  if (nlhs >= 3)" << endl
          << "    {" << endl
-         << "      plhs[2] = mxCreateDoubleMatrix(" << NNZDerivatives[2] << ", " << 3 << ", mxREAL);" << endl
-         << "      double *v2 = mxGetPr(plhs[2]);" << endl
+         << "      mxArray *g2_i = mxCreateDoubleMatrix(" << NNZDerivatives[2] << ", " << 1 << ", mxREAL);" << endl
+         << "      mxArray *g2_j = mxCreateDoubleMatrix(" << NNZDerivatives[2] << ", " << 1 << ", mxREAL);" << endl
+         << "      mxArray *g2_v = mxCreateDoubleMatrix(" << NNZDerivatives[2] << ", " << 1 << ", mxREAL);" << endl
          << "      dynamic_g2_tt(y, x, nb_row_x, params, steady_state, it_, T);" << endl
-         << "      dynamic_g2(y, x, nb_row_x, params, steady_state, it_, T, v2);" << endl
+         << "      dynamic_g2(y, x, nb_row_x, params, steady_state, it_, T, mxGetPr(g2_i), mxGetPr(g2_j), mxGetPr(g2_v));" << endl
+         << "      mxArray *m = mxCreateDoubleScalar(" << equations.size() << ");" << endl
+         << "      mxArray *n = mxCreateDoubleScalar(" << dynJacobianColsNbr*dynJacobianColsNbr << ");" << endl
+         << "      mxArray *plhs_sparse[1], *prhs_sparse[5] = { g2_i, g2_j, g2_v, m, n };" << endl
+         << R"(      mexCallMATLAB(1, plhs_sparse, 5, prhs_sparse, "sparse");)" << endl
+         << "      plhs[2] = plhs_sparse[0];" << endl
+         << "      mxDestroyArray(g2_i);" << endl
+         << "      mxDestroyArray(g2_j);" << endl
+         << "      mxDestroyArray(g2_v);" << endl
+         << "      mxDestroyArray(m);" << endl
+         << "      mxDestroyArray(n);" << endl
          << "    }" << endl
          << endl
          << "  if (nlhs >= 4)" << endl
          << "    {" << endl
-         << "      plhs[3] = mxCreateDoubleMatrix(" << NNZDerivatives[3] << ", " << 3 << ", mxREAL);" << endl
-         << "      double *v3 = mxGetPr(plhs[3]);" << endl
+         << "      mxArray *g3_i = mxCreateDoubleMatrix(" << NNZDerivatives[3] << ", " << 1 << ", mxREAL);" << endl
+         << "      mxArray *g3_j = mxCreateDoubleMatrix(" << NNZDerivatives[3] << ", " << 1 << ", mxREAL);" << endl
+         << "      mxArray *g3_v = mxCreateDoubleMatrix(" << NNZDerivatives[3] << ", " << 1 << ", mxREAL);" << endl
          << "      dynamic_g3_tt(y, x, nb_row_x, params, steady_state, it_, T);" << endl
-         << "      dynamic_g3(y, x, nb_row_x, params, steady_state, it_, T, v3);" << endl
+         << "      dynamic_g3(y, x, nb_row_x, params, steady_state, it_, T, mxGetPr(g3_i), mxGetPr(g3_j), mxGetPr(g3_v));" << endl
+         << "      mxArray *m = mxCreateDoubleScalar(" << equations.size() << ");" << endl
+         << "      mxArray *n = mxCreateDoubleScalar(" << dynJacobianColsNbr*dynJacobianColsNbr*dynJacobianColsNbr << ");" << endl
+         << "      mxArray *plhs_sparse[1], *prhs_sparse[5] = { g3_i, g3_j, g3_v, m, n };" << endl
+         << R"(      mexCallMATLAB(1, plhs_sparse, 5, prhs_sparse, "sparse");)" << endl
+         << "      plhs[3] = plhs_sparse[0];" << endl
+         << "      mxDestroyArray(g3_i);" << endl
+         << "      mxDestroyArray(g3_j);" << endl
+         << "      mxDestroyArray(g3_v);" << endl
+         << "      mxDestroyArray(m);" << endl
+         << "      mxDestroyArray(n);" << endl
          << "    }" << endl
          << endl
          << "  free(T);" << endl
@@ -1951,7 +1973,7 @@ DynamicModel::writeDynamicModel(const string &basename, ostream &DynamicOutput,
            then the third. This gives a significant performance boost in use_dll
            mode (at both compilation and runtime), because it facilitates memory
            accesses and expression reusage. */
-        ostringstream col0_output, col1_output, col2_output;
+        ostringstream i_output, j_output, v_output;
 
         int k = 0; // Current line index in the 3-column matrix
         for (const auto &[vidx, d] : derivatives[i])
@@ -1973,16 +1995,19 @@ DynamicModel::writeDynamicModel(const string &basename, ostream &DynamicOutput,
               }
             else
               {
-                sparseHelper(i, col0_output, k, 0, output_type);
-                col0_output << "=" << eq + 1 << ";" << endl;
-
-                sparseHelper(i, col1_output, k, 1, output_type);
-                col1_output << "=" << col_idx + 1 << ";" << endl;
-
-                sparseHelper(i, col2_output, k, 2, output_type);
-                col2_output << "=";
-                d->writeOutput(col2_output, output_type, temp_term_union, temporary_terms_idxs, tef_terms);
-                col2_output << ";" << endl;
+                i_output << "g" << i << "_i" << LEFT_ARRAY_SUBSCRIPT(output_type)
+                         << k + ARRAY_SUBSCRIPT_OFFSET(output_type)
+                         << RIGHT_ARRAY_SUBSCRIPT(output_type)
+                         << "=" << eq + 1 << ";" << endl;
+                j_output << "g" << i << "_j" << LEFT_ARRAY_SUBSCRIPT(output_type)
+                         << k + ARRAY_SUBSCRIPT_OFFSET(output_type)
+                         << RIGHT_ARRAY_SUBSCRIPT(output_type)
+                         << "=" << col_idx + 1 << ";" << endl;
+                v_output << "g" << i << "_v" << LEFT_ARRAY_SUBSCRIPT(output_type)
+                         << k + ARRAY_SUBSCRIPT_OFFSET(output_type)
+                         << RIGHT_ARRAY_SUBSCRIPT(output_type) << "=";
+                d->writeOutput(v_output, output_type, temp_term_union, temporary_terms_idxs, tef_terms);
+                v_output << ";" << endl;
 
                 k++;
               }
@@ -1997,23 +2022,27 @@ DynamicModel::writeDynamicModel(const string &basename, ostream &DynamicOutput,
                               << "g2[" << eq + 1 << "," << col_idx + 1 << "]" << endl;
                 else
                   {
-                    sparseHelper(2, col0_output, k, 0, output_type);
-                    col0_output << "=" << eq + 1 << ";" << endl;
-
-                    sparseHelper(2, col1_output, k, 1, output_type);
-                    col1_output << "=" << col_idx_sym + 1 << ";" << endl;
-
-                    sparseHelper(2, col2_output, k, 2, output_type);
-                    col2_output << "=";
-                    sparseHelper(2, col2_output, k-1, 2, output_type);
-                    col2_output << ";" << endl;
+                    i_output << "g" << i << "_i" << LEFT_ARRAY_SUBSCRIPT(output_type)
+                             << k + ARRAY_SUBSCRIPT_OFFSET(output_type)
+                             << RIGHT_ARRAY_SUBSCRIPT(output_type)
+                             << "=" << eq + 1 << ";" << endl;
+                    j_output << "g" << i << "_j" << LEFT_ARRAY_SUBSCRIPT(output_type)
+                             << k + ARRAY_SUBSCRIPT_OFFSET(output_type)
+                             << RIGHT_ARRAY_SUBSCRIPT(output_type)
+                             << "=" << col_idx_sym + 1 << ";" << endl;
+                    v_output << "g" << i << "_v" << LEFT_ARRAY_SUBSCRIPT(output_type)
+                             << k + ARRAY_SUBSCRIPT_OFFSET(output_type)
+                             << RIGHT_ARRAY_SUBSCRIPT(output_type) << "="
+                             << "g" << i << "_v" << LEFT_ARRAY_SUBSCRIPT(output_type)
+                             << k-1 + ARRAY_SUBSCRIPT_OFFSET(output_type)
+                             << RIGHT_ARRAY_SUBSCRIPT(output_type) << ";" << endl;
 
                     k++;
                   }
               }
           }
         if (output_type != ExprNodeOutputType::juliaDynamicModel)
-          d_output[i] << col0_output.str() << col1_output.str() << col2_output.str();
+          d_output[i] << i_output.str() << j_output.str() << v_output.str();
       }
 
   if (output_type == ExprNodeOutputType::matlabDynamicModel)
@@ -2052,18 +2081,17 @@ DynamicModel::writeDynamicModel(const string &basename, ostream &DynamicOutput,
           ncols *= dynJacobianColsNbr;
           ntt += temporary_terms_derivatives[i].size();
           string gname = "g" + to_string(i);
-          string vname = "v" + to_string(i);
           string gprevname = "g" + to_string(i-1);
 
           init_output.str("");
           end_output.str("");
           if (derivatives[i].size())
             {
-              init_output << vname << " = zeros(" << NNZDerivatives[i] << ",3);";
+              init_output << gname << "_i = zeros(" << NNZDerivatives[i] << ",1);" << endl
+                          << gname << "_j = zeros(" << NNZDerivatives[i] << ",1);" << endl
+                          << gname << "_v = zeros(" << NNZDerivatives[i] << ",1);" << endl;
               end_output << gname << " = sparse("
-                         << vname << "(:,1),"
-                         << vname << "(:,2),"
-                         << vname << "(:,3),"
+                         << gname << "_i," << gname << "_j," << gname << "_v,"
                          << nrows << "," << ncols << ");";
             }
           else
@@ -2085,13 +2113,19 @@ DynamicModel::writeDynamicModel(const string &basename, ostream &DynamicOutput,
       for (size_t i = 0; i < d_output.size(); i++)
         {
           string funcname = i == 0 ? "resid" : "g" + to_string(i);
-          string argname = i == 0 ? "residual" : i == 1 ? "g1" : "v" + to_string(i);
           DynamicOutput << "void dynamic_" << funcname << "_tt(const double *y, const double *x, int nb_row_x, const double *params, const double *steady_state, int it_, double *T)" << endl
                         << "{" << endl
                         << tt_output[i].str()
                         << "}" << endl
                         << endl
-                        << "void dynamic_" << funcname << "(const double *y, const double *x, int nb_row_x, const double *params, const double *steady_state, int it_, const double *T, double *" << argname << ")" << endl
+                        << "void dynamic_" << funcname << "(const double *y, const double *x, int nb_row_x, const double *params, const double *steady_state, int it_, const double *T, ";
+          if (i == 0)
+            DynamicOutput << "double *residual";
+          else if (i == 1)
+            DynamicOutput << "double *g1";
+          else
+            DynamicOutput << "double *" << funcname << "_i, double *" << funcname << "_j, double *" << funcname << "_v";
+          DynamicOutput << ")" << endl
                         << "{" << endl;
           if (i == 0)
             DynamicOutput << "  double lhs, rhs;" << endl;
diff --git a/src/ModelTree.cc b/src/ModelTree.cc
index 0774c321..856c2260 100644
--- a/src/ModelTree.cc
+++ b/src/ModelTree.cc
@@ -1792,17 +1792,6 @@ ModelTree::jacobianHelper(ostream &output, int eq_nb, int col_nb, ExprNodeOutput
   output << RIGHT_ARRAY_SUBSCRIPT(output_type);
 }
 
-void
-ModelTree::sparseHelper(int order, ostream &output, int row_nb, int col_nb, ExprNodeOutputType output_type) const
-{
-  output << "v" << order << LEFT_ARRAY_SUBSCRIPT(output_type);
-  if (isMatlabOutput(output_type) || isJuliaOutput(output_type))
-    output << row_nb + 1 << "," << col_nb + 1;
-  else
-    output << row_nb + col_nb * NNZDerivatives[order];
-  output << RIGHT_ARRAY_SUBSCRIPT(output_type);
-}
-
 void
 ModelTree::computeParamsDerivatives(int paramsDerivsOrder)
 {
diff --git a/src/ModelTree.hh b/src/ModelTree.hh
index 518d3bdb..0ea39d5c 100644
--- a/src/ModelTree.hh
+++ b/src/ModelTree.hh
@@ -456,10 +456,6 @@ public:
   //! Helper for writing the Jacobian elements in MATLAB and C
   /*! Writes either (i+1,j+1) or [i+j*no_eq] */
   void jacobianHelper(ostream &output, int eq_nb, int col_nb, ExprNodeOutputType output_type) const;
-  //! Helper for writing the sparse Hessian or third derivatives in MATLAB and C
-  /*! If order=2, writes either v2(i+1,j+1) or v2[i+j*NNZDerivatives[2]]
-    If order=3, writes either v3(i+1,j+1) or v3[i+j*NNZDerivatives[3]] */
-  void sparseHelper(int order, ostream &output, int row_nb, int col_nb, ExprNodeOutputType output_type) const;
 
   //! Returns all the equation tags associated to an equation
   inline map<string, string>
diff --git a/src/StaticModel.cc b/src/StaticModel.cc
index 0cfab15e..ae5ff71c 100644
--- a/src/StaticModel.cc
+++ b/src/StaticModel.cc
@@ -1301,7 +1301,7 @@ StaticModel::writeStaticModel(const string &basename,
            then the third. This gives a significant performance boost in use_dll
            mode (at both compilation and runtime), because it facilitates memory
            accesses and expression reusage. */
-        ostringstream col0_output, col1_output, col2_output;
+        ostringstream i_output, j_output, v_output;
 
         int k = 0; // Current line index in the 3-column matrix
         for (const auto &[vidx, d] : derivatives[i])
@@ -1323,16 +1323,19 @@ StaticModel::writeStaticModel(const string &basename,
               }
             else
               {
-                sparseHelper(i, col0_output, k, 0, output_type);
-                col0_output << "=" << eq + 1 << ";" << endl;
-
-                sparseHelper(i, col1_output, k, 1, output_type);
-                col1_output << "=" << col_idx + 1 << ";" << endl;
-
-                sparseHelper(i, col2_output, k, 2, output_type);
-                col2_output << "=";
-                d->writeOutput(col2_output, output_type, temp_term_union, temporary_terms_idxs, tef_terms);
-                col2_output << ";" << endl;
+                i_output << "g" << i << "_i" << LEFT_ARRAY_SUBSCRIPT(output_type)
+                         << k + ARRAY_SUBSCRIPT_OFFSET(output_type)
+                         << RIGHT_ARRAY_SUBSCRIPT(output_type)
+                         << "=" << eq + 1 << ";" << endl;
+                j_output << "g" << i << "_j" << LEFT_ARRAY_SUBSCRIPT(output_type)
+                         << k + ARRAY_SUBSCRIPT_OFFSET(output_type)
+                         << RIGHT_ARRAY_SUBSCRIPT(output_type)
+                         << "=" << col_idx + 1 << ";" << endl;
+                v_output << "g" << i << "_v" << LEFT_ARRAY_SUBSCRIPT(output_type)
+                         << k + ARRAY_SUBSCRIPT_OFFSET(output_type)
+                         << RIGHT_ARRAY_SUBSCRIPT(output_type) << "=";
+                d->writeOutput(v_output, output_type, temp_term_union, temporary_terms_idxs, tef_terms);
+                v_output << ";" << endl;
 
                 k++;
               }
@@ -1347,23 +1350,27 @@ StaticModel::writeStaticModel(const string &basename,
                               << "g2[" << eq + 1 << "," << col_idx + 1 << "]" << endl;
                 else
                   {
-                    sparseHelper(2, col0_output, k, 0, output_type);
-                    col0_output << "=" << eq + 1 << ";" << endl;
-
-                    sparseHelper(2, col1_output, k, 1, output_type);
-                    col1_output << "=" << col_idx_sym + 1 << ";" << endl;
-
-                    sparseHelper(2, col2_output, k, 2, output_type);
-                    col2_output << "=";
-                    sparseHelper(2, col2_output, k-1, 2, output_type);
-                    col2_output << ";" << endl;
+                    i_output << "g" << i << "_i" << LEFT_ARRAY_SUBSCRIPT(output_type)
+                             << k + ARRAY_SUBSCRIPT_OFFSET(output_type)
+                             << RIGHT_ARRAY_SUBSCRIPT(output_type)
+                             << "=" << eq + 1 << ";" << endl;
+                    j_output << "g" << i << "_j" << LEFT_ARRAY_SUBSCRIPT(output_type)
+                             << k + ARRAY_SUBSCRIPT_OFFSET(output_type)
+                             << RIGHT_ARRAY_SUBSCRIPT(output_type)
+                             << "=" << col_idx_sym + 1 << ";" << endl;
+                    v_output << "g" << i << "_v" << LEFT_ARRAY_SUBSCRIPT(output_type)
+                             << k + ARRAY_SUBSCRIPT_OFFSET(output_type)
+                             << RIGHT_ARRAY_SUBSCRIPT(output_type) << "="
+                             << "g" << i << "_v" << LEFT_ARRAY_SUBSCRIPT(output_type)
+                             << k-1 + ARRAY_SUBSCRIPT_OFFSET(output_type)
+                             << RIGHT_ARRAY_SUBSCRIPT(output_type) << ";" << endl;
 
                     k++;
                   }
               }
           }
         if (output_type != ExprNodeOutputType::juliaStaticModel)
-          d_output[i] << col0_output.str() << col1_output.str() << col2_output.str();
+          d_output[i] << i_output.str() << j_output.str() << v_output.str();
       }
 
   if (output_type == ExprNodeOutputType::matlabStaticModel)
@@ -1407,18 +1414,17 @@ StaticModel::writeStaticModel(const string &basename,
           ncols *= JacobianColsNbr;
           ntt += temporary_terms_derivatives[i].size();
           string gname = "g" + to_string(i);
-          string vname = "v" + to_string(i);
           string gprevname = "g" + to_string(i-1);
 
           init_output.str("");
           end_output.str("");
           if (derivatives[i].size())
             {
-              init_output << vname << " = zeros(" << NNZDerivatives[i] << ",3);";
+              init_output << gname << "_i = zeros(" << NNZDerivatives[i] << ",1);" << endl
+                          << gname << "_j = zeros(" << NNZDerivatives[i] << ",1);" << endl
+                          << gname << "_v = zeros(" << NNZDerivatives[i] << ",1);" << endl;
               end_output << gname << " = sparse("
-                         << vname << "(:,1),"
-                         << vname << "(:,2),"
-                         << vname << "(:,3),"
+                         << gname << "_i," << gname << "_j," << gname << "_v,"
                          << nrows << "," << ncols << ");";
             }
           else
@@ -1440,13 +1446,19 @@ StaticModel::writeStaticModel(const string &basename,
       for (size_t i = 0; i < d_output.size(); i++)
         {
           string funcname = i == 0 ? "resid" : "g" + to_string(i);
-          string argname = i == 0 ? "residual" : i == 1 ? "g1" : "v" + to_string(i);
           StaticOutput << "void static_" << funcname << "_tt(const double *y, const double *x, int nb_row_x, const double *params, double *T)" << endl
                        << "{" << endl
                        << tt_output[i].str()
                        << "}" << endl
                        << endl
-                       << "void static_" << funcname << "(const double *y, const double *x, int nb_row_x, const double *params, const double *T, double *" << argname << ")" << endl
+                       << "void static_" << funcname << "(const double *y, const double *x, int nb_row_x, const double *params, const double *T, ";
+          if (i == 0)
+            StaticOutput << "double *residual";
+          else if (i == 1)
+            StaticOutput << "double *g1";
+          else
+            StaticOutput << "double *" << funcname << "_i, double *" << funcname << "_j, double *" << funcname << "_v";
+          StaticOutput << ")" << endl
                        << "{" << endl;
           if (i == 0)
             StaticOutput << "  double lhs, rhs;" << endl;
@@ -1732,10 +1744,21 @@ StaticModel::writeStaticCFile(const string &basename) const
          << endl
          << "  if (nlhs >= 3)" << endl
          << "    {" << endl
-         << "      plhs[2] = mxCreateDoubleMatrix(" << NNZDerivatives[2] << ", " << 3 << ", mxREAL);" << endl
-         << "      double *v2 = mxGetPr(plhs[2]);" << endl
+         << "      mxArray *g2_i = mxCreateDoubleMatrix(" << NNZDerivatives[2] << ", " << 1 << ", mxREAL);" << endl
+         << "      mxArray *g2_j = mxCreateDoubleMatrix(" << NNZDerivatives[2] << ", " << 1 << ", mxREAL);" << endl
+         << "      mxArray *g2_v = mxCreateDoubleMatrix(" << NNZDerivatives[2] << ", " << 1 << ", mxREAL);" << endl
          << "      static_g2_tt(y, x, nb_row_x, params, T);" << endl
-         << "      static_g2(y, x, nb_row_x, params, T, v2);" << endl
+         << "      static_g2(y, x, nb_row_x, params, T, mxGetPr(g2_i), mxGetPr(g2_j), mxGetPr(g2_v));" << endl
+         << "      mxArray *m = mxCreateDoubleScalar(" << equations.size() << ");" << endl
+         << "      mxArray *n = mxCreateDoubleScalar(" << symbol_table.endo_nbr()*symbol_table.endo_nbr() << ");" << endl
+         << "      mxArray *plhs_sparse[1], *prhs_sparse[5] = { g2_i, g2_j, g2_v, m, n };" << endl
+         << R"(      mexCallMATLAB(1, plhs_sparse, 5, prhs_sparse, "sparse");)" << endl
+         << "      plhs[2] = plhs_sparse[0];" << endl
+         << "      mxDestroyArray(g2_i);" << endl
+         << "      mxDestroyArray(g2_j);" << endl
+         << "      mxDestroyArray(g2_v);" << endl
+         << "      mxDestroyArray(m);" << endl
+         << "      mxDestroyArray(n);" << endl
          << "    }" << endl
          << endl
          << "  free(T);" << endl
-- 
GitLab