From 5b42483e81cd6a217f25a45b2d39d68462546f37 Mon Sep 17 00:00:00 2001
From: Michel Juillard <michel.juillard@mjui.fr>
Date: Sun, 10 May 2015 18:16:11 +0200
Subject: [PATCH] adding feature to recompile MEX files only if the model has
 changed (not activated yet) detail of compilation code moved from
 preprocessor (ModFile.cc) to ./matlab/utilities/general/dyn_mex.m

---
 DynamicModel.cc |  94 ++++++++++++++++++++++++
 DynamicModel.hh |   3 +
 ModFile.cc      | 191 +++++++++++++++++++++---------------------------
 ModFile.hh      |   9 +++
 4 files changed, 188 insertions(+), 109 deletions(-)

diff --git a/DynamicModel.cc b/DynamicModel.cc
index f4957557..74d7a2c6 100644
--- a/DynamicModel.cc
+++ b/DynamicModel.cc
@@ -4221,5 +4221,99 @@ DynamicModel::dynamicOnlyEquationsNbr() const
   return eqs.size();
 }
 
+#ifndef PRIVATE_BUFFER_SIZE
+#define PRIVATE_BUFFER_SIZE 1024
+#endif
+
+bool
+DynamicModel::isChecksumMatching(const string &basename) const
+{
+  boost::crc_32_type result;
+
+  std::stringstream buffer;
+
+  // Write equation tags
+  for (size_t i = 0; i < equation_tags.size(); i++)
+    buffer << "  " << equation_tags[i].first + 1
+           << equation_tags[i].second.first
+           << equation_tags[i].second.second;
+
+  ExprNodeOutputType buffer_type = oCDynamicModel;
+
+  for (int eq = 0; eq < (int) equations.size(); eq++)
+    {
+      BinaryOpNode *eq_node = equations[eq];
+      expr_t lhs = eq_node->get_arg1();
+      expr_t rhs = eq_node->get_arg2();
+
+      // Test if the right hand side of the equation is empty.
+      double vrhs = 1.0;
+      try
+        {
+          vrhs = rhs->eval(eval_context_t());
+        }
+      catch (ExprNode::EvalException &e)
+        {
+        }
+
+      if (vrhs != 0) // The right hand side of the equation is not empty ==> residual=lhs-rhs;
+        {
+          buffer << "lhs =";
+          lhs->writeOutput(buffer, buffer_type, temporary_terms);
+          buffer << ";" << endl;
+
+          buffer << "rhs =";
+          rhs->writeOutput(buffer, buffer_type, temporary_terms);
+          buffer << ";" << endl;
+
+          buffer << "residual" << LEFT_ARRAY_SUBSCRIPT(buffer_type)
+                 << eq + ARRAY_SUBSCRIPT_OFFSET(buffer_type)
+                 << RIGHT_ARRAY_SUBSCRIPT(buffer_type)
+                 << "= lhs-rhs;" << endl;
+        }
+      else // The right hand side of the equation is empty ==> residual=lhs;
+        {
+          buffer << "residual" << LEFT_ARRAY_SUBSCRIPT(buffer_type)
+                 << eq + ARRAY_SUBSCRIPT_OFFSET(buffer_type)
+                 << RIGHT_ARRAY_SUBSCRIPT(buffer_type)
+                 << " = ";
+          lhs->writeOutput(buffer, buffer_type, temporary_terms);
+          buffer << ";" << endl;
+        }
+    }
 
+  char private_buffer[PRIVATE_BUFFER_SIZE];
+  while(buffer)
+    {
+      buffer.get(private_buffer,PRIVATE_BUFFER_SIZE);
+      result.process_bytes(private_buffer,strlen(private_buffer));
+    }
 
+  fstream checksum_file;
+
+  string filename = basename + "/checksum";
+  
+  //  checksum_file.open(filename.c_str(), ios::in | ios::out | ios::binary);
+  checksum_file.open(filename.c_str(), ios::in | ios::binary);
+  unsigned int old_checksum = 0;
+  if (checksum_file.is_open())
+    {
+      checksum_file >> old_checksum;
+      std::cout << "old_checksum " << old_checksum << endl;
+    }
+  if ((!checksum_file.is_open()) || (old_checksum != result.checksum())) 
+	{
+	  checksum_file.close();
+	  checksum_file.open(filename.c_str(), ios::out | ios::binary);
+	  if (!checksum_file.is_open())
+	    {
+	      cerr << "ERROR: Can't open file " << filename << endl;
+	      exit(EXIT_FAILURE);
+	    }
+	  checksum_file << result.checksum();
+	  checksum_file.close();
+	  return false;
+	}
+  
+  return true;
+}
diff --git a/DynamicModel.hh b/DynamicModel.hh
index 8efbc5d3..d96a7d50 100644
--- a/DynamicModel.hh
+++ b/DynamicModel.hh
@@ -24,6 +24,7 @@ using namespace std;
 #define ZERO_BAND 1e-8
 
 #include <fstream>
+#include <boost/crc.hpp>
 
 #include "StaticModel.hh"
 
@@ -479,6 +480,8 @@ public:
   void writeSecondDerivativesC_csr(const string &basename, bool cuda) const;
   //! Writes C file containing third order derivatives of model evaluated at steady state (compressed sparse column)
   void writeThirdDerivativesC_csr(const string &basename, bool cuda) const;
+
+  bool isChecksumMatching(const string &basename) const;
 };
 
 //! Classes to re-order derivatives for various sparse storage formats 
diff --git a/ModFile.cc b/ModFile.cc
index 6d9eca0e..9f8f73f5 100644
--- a/ModFile.cc
+++ b/ModFile.cc
@@ -469,55 +469,55 @@ ModFile::computingPass(bool no_tmp_terms, FileOutputType output)
       // Compute static model and its derivatives
       dynamic_model.toStatic(static_model);
       if (!no_static)
-        {
-          if (mod_file_struct.stoch_simul_present
-              || mod_file_struct.estimation_present || mod_file_struct.osr_present
-              || mod_file_struct.ramsey_model_present || mod_file_struct.identification_present
-              || mod_file_struct.calib_smoother_present)
-            static_model.set_cutoff_to_zero();
-
-          const bool static_hessian = mod_file_struct.identification_present
-            || mod_file_struct.estimation_analytic_derivation;
-          const bool paramsDerivatives = mod_file_struct.identification_present
-            || mod_file_struct.estimation_analytic_derivation;
-          static_model.computingPass(global_eval_context, no_tmp_terms, static_hessian,
-                                     false, paramsDerivatives, block, byte_code);
-        }
+	{
+	  if (mod_file_struct.stoch_simul_present
+	      || mod_file_struct.estimation_present || mod_file_struct.osr_present
+	      || mod_file_struct.ramsey_model_present || mod_file_struct.identification_present
+	      || mod_file_struct.calib_smoother_present)
+	    static_model.set_cutoff_to_zero();
+
+	  const bool static_hessian = mod_file_struct.identification_present
+	    || mod_file_struct.estimation_analytic_derivation;
+	  const bool paramsDerivatives = mod_file_struct.identification_present
+	    || mod_file_struct.estimation_analytic_derivation;
+	  static_model.computingPass(global_eval_context, no_tmp_terms, static_hessian,
+				     false, paramsDerivatives, block, byte_code);
+	}
       // Set things to compute for dynamic model
       if (mod_file_struct.perfect_foresight_solver_present || mod_file_struct.check_present
-          || mod_file_struct.stoch_simul_present
-          || mod_file_struct.estimation_present || mod_file_struct.osr_present
-          || mod_file_struct.ramsey_model_present || mod_file_struct.identification_present
-          || mod_file_struct.calib_smoother_present)
-        {
-          if (mod_file_struct.perfect_foresight_solver_present)
-            dynamic_model.computingPass(true, false, false, false, global_eval_context, no_tmp_terms, block, use_dll, byte_code);
-          else
-            {
-              if (mod_file_struct.stoch_simul_present
-                  || mod_file_struct.estimation_present || mod_file_struct.osr_present
-                  || mod_file_struct.ramsey_model_present || mod_file_struct.identification_present
-                  || mod_file_struct.calib_smoother_present)
-                dynamic_model.set_cutoff_to_zero();
-              if (mod_file_struct.order_option < 1 || mod_file_struct.order_option > 3)
-                {
-                  cerr << "ERROR: Incorrect order option..." << endl;
-                  exit(EXIT_FAILURE);
-                }
-              bool hessian = mod_file_struct.order_option >= 2 
-		|| mod_file_struct.identification_present 
-		|| mod_file_struct.estimation_analytic_derivation
-		|| output == second 
-		|| output == third;
-              bool thirdDerivatives = mod_file_struct.order_option == 3 
-		|| mod_file_struct.estimation_analytic_derivation
-		|| output == third;
-              bool paramsDerivatives = mod_file_struct.identification_present || mod_file_struct.estimation_analytic_derivation;
-              dynamic_model.computingPass(true, hessian, thirdDerivatives, paramsDerivatives, global_eval_context, no_tmp_terms, block, use_dll, byte_code);
-            }
-        }
-      else // No computing task requested, compute derivatives up to 2nd order by default
-        dynamic_model.computingPass(true, true, false, false, global_eval_context, no_tmp_terms, block, use_dll, byte_code);
+	  || mod_file_struct.stoch_simul_present
+	  || mod_file_struct.estimation_present || mod_file_struct.osr_present
+	  || mod_file_struct.ramsey_model_present || mod_file_struct.identification_present
+	  || mod_file_struct.calib_smoother_present)
+	{
+	  if (mod_file_struct.perfect_foresight_solver_present)
+	    dynamic_model.computingPass(true, false, false, false, global_eval_context, no_tmp_terms, block, use_dll, byte_code);
+	      else
+		{
+		  if (mod_file_struct.stoch_simul_present
+		      || mod_file_struct.estimation_present || mod_file_struct.osr_present
+		      || mod_file_struct.ramsey_model_present || mod_file_struct.identification_present
+		      || mod_file_struct.calib_smoother_present)
+		    dynamic_model.set_cutoff_to_zero();
+		  if (mod_file_struct.order_option < 1 || mod_file_struct.order_option > 3)
+		    {
+		      cerr << "ERROR: Incorrect order option..." << endl;
+		      exit(EXIT_FAILURE);
+		    }
+		  bool hessian = mod_file_struct.order_option >= 2 
+		    || mod_file_struct.identification_present 
+		    || mod_file_struct.estimation_analytic_derivation
+		    || output == second 
+		    || output == third;
+		  bool thirdDerivatives = mod_file_struct.order_option == 3 
+		    || mod_file_struct.estimation_analytic_derivation
+		    || output == third;
+		  bool paramsDerivatives = mod_file_struct.identification_present || mod_file_struct.estimation_analytic_derivation;
+		  dynamic_model.computingPass(true, hessian, thirdDerivatives, paramsDerivatives, global_eval_context, no_tmp_terms, block, use_dll, byte_code);
+		}
+	    }
+	  else // No computing task requested, compute derivatives up to 2nd order by default
+	    dynamic_model.computingPass(true, true, false, false, global_eval_context, no_tmp_terms, block, use_dll, byte_code);
     }
 
   for (vector<Statement *>::iterator it = statements.begin();
@@ -649,18 +649,23 @@ ModFile::writeOutputFiles(const string &basename, bool clear_all, bool clear_glo
                 << "  error('DYNARE: Can''t find bytecode DLL. Please compile it or remove the ''bytecode'' option.')" << endl
                 << "end" << endl;
 
-  // Erase possible remnants of previous runs
-  unlink((basename + "_dynamic.m").c_str());
-  unlink((basename + "_dynamic.cod").c_str());
-  unlink((basename + "_dynamic.bin").c_str());
-
-  unlink((basename + "_static.m").c_str());
-  unlink((basename + "_static.cod").c_str());
-  unlink((basename + "_static.bin").c_str());
+  bool hasModelChanged = !dynamic_model.isChecksumMatching(basename);
+      
+  if (hasModelChanged)
+    {
+      // Erase possible remnants of previous runs
+      unlink((basename + "_dynamic.m").c_str());
+      unlink((basename + "_dynamic.cod").c_str());
+      unlink((basename + "_dynamic.bin").c_str());
 
-  unlink((basename + "_steadystate2.m").c_str());
-  unlink((basename + "_set_auxiliary_variables.m").c_str());
+      unlink((basename + "_static.m").c_str());
+      unlink((basename + "_static.cod").c_str());
+      unlink((basename + "_static.bin").c_str());
 
+      unlink((basename + "_steadystate2.m").c_str());
+      unlink((basename + "_set_auxiliary_variables.m").c_str());
+    }
+  
   if (!use_dll)
     {
       mOutputFile << "erase_compiled_function('" + basename + "_static');" << endl;
@@ -697,53 +702,19 @@ ModFile::writeOutputFiles(const string &basename, bool clear_all, bool clear_glo
   // Compile the dynamic MEX file for use_dll option
   if (use_dll)
     {
-      mOutputFile << "if ~exist('OCTAVE_VERSION')" << endl;
-      // Some mex commands are enclosed in an eval(), because otherwise it will make Octave fail
 #if defined(_WIN32) || defined(__CYGWIN32__)
       if (msvc)
         // MATLAB/Windows + Microsoft Visual C++
-        mOutputFile << "    eval('mex -O LINKFLAGS=\"$LINKFLAGS /export:Dynamic\" " << basename << "_dynamic.c " << basename << "_dynamic_mex.c')" << endl
-                    << "    eval('mex -O LINKFLAGS=\"$LINKFLAGS /export:Static\" " << basename << "_static.c "<< basename << "_static_mex.c')" << endl;
+	mOutputFile << "dyn_mex('msvc', '" << basename << ", 1)" <<  endl;
       else if (cygwin)
         // MATLAB/Windows + Cygwin g++
-        mOutputFile << "    eval('mex -O PRELINK_CMDS1=\"echo EXPORTS > mex.def & echo mexFunction >> mex.def & echo Dynamic >> mex.def\" " << basename << "_dynamic.c " << basename << "_dynamic_mex.c')" << endl
-                    << "    eval('mex -O PRELINK_CMDS1=\"echo EXPORTS > mex.def & echo mexFunction >> mex.def & echo Static >> mex.def\" " << basename << "_static.c "<< basename << "_static_mex.c')" << endl;
+	mOutputFile << "dyn_mex('cygwin', '" << basename << "', 1)" << endl;
       else
         mOutputFile << "    error('When using the USE_DLL option, you must give either ''cygwin'' or ''msvc'' option to the ''dynare'' command')" << endl;
 #else
-# ifdef __linux__
-      // MATLAB/Linux
-      mOutputFile << "    if matlab_ver_less_than('8.3')" << endl
-                  << "        eval('mex -O LDFLAGS=''-pthread -shared -Wl,--no-undefined'' " << basename << "_dynamic.c " << basename << "_dynamic_mex.c')" << endl
-                  << "        eval('mex -O LDFLAGS=''-pthread -shared -Wl,--no-undefined'' " << basename << "_static.c "<< basename << "_static_mex.c')" << endl
-                  << "    else" << endl
-                  << "        eval('mex -O LINKEXPORT='''' " << basename << "_dynamic.c " << basename << "_dynamic_mex.c')" << endl
-                  << "        eval('mex -O LINKEXPORT='''' " << basename << "_static.c "<< basename << "_static_mex.c')" << endl
-                  << "    end" << endl;
-# else // MacOS
-      // MATLAB/MacOS
-      mOutputFile << "    if matlab_ver_less_than('8.3')" << endl
-                  << "        if matlab_ver_less_than('8.1')" << endl
-                  << "            eval('mex -O LDFLAGS=''-Wl,-twolevel_namespace -undefined error -arch \\$ARCHS -Wl,-syslibroot,\\$SDKROOT -mmacosx-version-min=\\$MACOSX_DEPLOYMENT_TARGET -bundle'' "
-                  << basename << "_dynamic.c " << basename << "_dynamic_mex.c')" << endl
-                  << "            eval('mex -O LDFLAGS=''-Wl,-twolevel_namespace -undefined error -arch \\$ARCHS -Wl,-syslibroot,\\$SDKROOT -mmacosx-version-min=\\$MACOSX_DEPLOYMENT_TARGET -bundle'' "
-                  << basename << "_static.c " << basename << "_static_mex.c')" << endl
-                  << "        else" << endl
-                  << "            eval('mex -O LDFLAGS=''-Wl,-twolevel_namespace -undefined error -arch \\$ARCHS -Wl,-syslibroot,\\$MW_SDKROOT -mmacosx-version-min=\\$MACOSX_DEPLOYMENT_TARGET -bundle'' "
-                  << basename << "_dynamic.c " << basename << "_dynamic_mex.c')" << endl
-                  << "            eval('mex -O LDFLAGS=''-Wl,-twolevel_namespace -undefined error -arch \\$ARCHS -Wl,-syslibroot,\\$MW_SDKROOT -mmacosx-version-min=\\$MACOSX_DEPLOYMENT_TARGET -bundle'' "
-                  << basename << "_static.c " << basename << "_static_mex.c')" << endl
-                  << "        end" << endl
-                  << "    else" << endl
-                  << "        eval('mex -O LINKEXPORT='''' " << basename << "_dynamic.c " << basename << "_dynamic_mex.c')" << endl
-                  << "        eval('mex -O LINKEXPORT='''' " << basename << "_static.c "<< basename << "_static_mex.c')" << endl
-                  << "    end" << endl;
-# endif
+	// other configurations
+	mOutputFile << "dyn_mex('', '" << basename << "', 1)" << endl;
 #endif
-      mOutputFile << "else" << endl // Octave
-                  << "    mex " << basename << "_dynamic.c " << basename << "_dynamic_mex.c" << endl
-                  << "    mex " << basename << "_static.c " << basename << "_static_mex.c" << endl
-                  << "end" << endl;
     }
 
   // Add path for block option with M-files
@@ -824,22 +795,24 @@ ModFile::writeOutputFiles(const string &basename, bool clear_all, bool clear_glo
 
   mOutputFile.close();
 
-  // Create static and dynamic files
-  if (dynamic_model.equation_number() > 0)
+  if (hasModelChanged)
     {
-      if (!no_static)
-        {
-          static_model.writeStaticFile(basename, block, byte_code, use_dll);
-          static_model.writeParamsDerivativesFile(basename);
-        }
+      // Create static and dynamic files
+      if (dynamic_model.equation_number() > 0)
+	{
+	  if (!no_static)
+	    {
+	      static_model.writeStaticFile(basename, block, byte_code, use_dll);
+	      static_model.writeParamsDerivativesFile(basename);
+	    }
 
-      dynamic_model.writeDynamicFile(basename, block, byte_code, use_dll, mod_file_struct.order_option);
-      dynamic_model.writeParamsDerivativesFile(basename);
-    }
-
-  // Create steady state file
-  steady_state_model.writeSteadyStateFile(basename, mod_file_struct.ramsey_model_present);
+	  dynamic_model.writeDynamicFile(basename, block, byte_code, use_dll, mod_file_struct.order_option);
+	  dynamic_model.writeParamsDerivativesFile(basename);
+	}
 
+      // Create steady state file
+      steady_state_model.writeSteadyStateFile(basename, mod_file_struct.ramsey_model_present);
+    }
+  
   cout << "done" << endl;
 }
-
diff --git a/ModFile.hh b/ModFile.hh
index 722ea534..0a384931 100644
--- a/ModFile.hh
+++ b/ModFile.hh
@@ -24,6 +24,8 @@ using namespace std;
 
 #include <ostream>
 #include <ctime>
+#include <iostream>
+#include <sstream>
 
 #include "SymbolTable.hh"
 #include "NumericalConstants.hh"
@@ -37,6 +39,11 @@ using namespace std;
 #include "WarningConsolidation.hh"
 #include "ExtendedPreprocessorTypes.hh"
 
+// for checksum computation
+#ifndef PRIVATE_BUFFER_SIZE
+#define PRIVATE_BUFFER_SIZE 1024
+#endif
+
 //! The abstract representation of a "mod" file
 class ModFile
 {
@@ -153,6 +160,8 @@ public:
   //! Writes Cpp output files only => No further Matlab processing
   void writeCCOutputFiles(const string &basename) const;
   void writeModelCC(const string &basename) const;
+
+  void computeChecksum();
 };
 
 #endif // ! MOD_FILE_HH
-- 
GitLab