file src/Backends/src/ini_functions.cpp

[No description available] More…

Namespaces

Name
Gambit
TODO: see if we can use this one:

Detailed Description

Functions specifically for triggering backend initialisation code.


Authors

(add name and date if you modify)

Pat Scott (p.scott@imperial.ac.uk)

2015 Feb

Tomas Gonzalo (t.e.gonzalo@fys.uio.no)

2016 Sep

Patrick Stoecker (stoecker@physik.rwth-aachen.de)

2023 Nov


Source code

//   GAMBIT: Global and Modular BSM Inference Tool
//   *********************************************
///  \file
///
///  Functions specifically for triggering
///  backend initialisation code.
///
///  *********************************************
///
///  Authors
///  =======
///
///  (add name and date if you modify)
///
///  \author Pat Scott
///          (p.scott@imperial.ac.uk)
///  \date 2015 Feb
///
///  \author Tomas Gonzalo
///          (t.e.gonzalo@fys.uio.no)
///  \date 2016 Sep
///
///  \author Patrick Stoecker
///          (stoecker@physik.rwth-aachen.de)
///  \date 2023 Nov
///
///  *********************************************

#include <regex>

#include <boost/algorithm/string/replace.hpp>

#include "gambit/cmake/cmake_variables.hpp"
#include "gambit/Elements/functors.hpp"
#include "gambit/Elements/ini_catch.hpp"
#include "gambit/Backends/backend_singleton.hpp"
#include "gambit/Backends/ini_functions.hpp"
#include "gambit/Models/claw_singleton.hpp"
#include "gambit/Utils/util_functions.hpp"
#include "gambit/Logs/logging.hpp"

#ifdef HAVE_MATHEMATICA
  #include MATHEMATICA_WSTP_H
#endif


namespace Gambit
{

  /// Get back the "::" from things that use NS_SEP instead
  str fixns(str s)
  {
    str ns = STRINGIFY(NS_SEP);
    const str cc = "::";
    std::regex rgx1(ns), rgx2("my_ns"+cc), rgx3(cc+"\\("), rgx4(cc+"$");
    s = std::regex_replace(s, rgx1, cc);
    s = std::regex_replace(s, rgx2, str(""));
    s = std::regex_replace(s, rgx3, str("("));
    s = std::regex_replace(s, rgx4, str(""));
    return s;
  }

  /// Call push back on a vector of strings
  int vectorstr_push_back(std::vector<str>& vec, str s)
  {
    try
    {
      vec.push_back(s);
    }
    catch (std::exception& e) { ini_catch(e); }
    return 0;
  }

  /// Notify a backend functor of which models it can be used with
  int set_allowed_models(functor& be_functor, std::vector<str>& allowed_at_be_level, str models_string)
  {
    try
    {
      // Strip out parentheses
      models_string = models_string.substr(1,models_string.length()-2);
      // Get the models explicitly allowed in this command
      std::vector<str> models = Utils::delimiterSplit(models_string, ",");
      // Harmonise any models declared as allowed for the backend as a whole with those exlicitly allowed in this command.
      if (not allowed_at_be_level.empty())
      {
        // If there are no models explicitly allowed, just inherit the allowed models from the backend as a whole.
        if (models.empty())
        {
          models.insert(models.end(), allowed_at_be_level.begin(), allowed_at_be_level.end());
        }
        // If there are models explicitly allowed, and models allowed at the whole-backend level, make sure their declarations are consistent.
        else
        {
          // Loop over all the models explicitly allowed here, and make sure they fit with at least one of those declared at the backend level.
          for (std::vector<str>::const_iterator it = models.begin(); it != models.end(); ++it)
          {
            bool found_match = false;
            for (std::vector<str>::const_iterator jt = allowed_at_be_level.begin(); jt != allowed_at_be_level.end(); ++jt)
            {
              found_match = Models::ModelDB().upstream_of(*jt,*it);
              if (found_match) break;
            }
            if (not found_match)
            {
              std::stringstream msg;
              msg << "Conflicting model compatibility information provided for backend function or variable" << endl
                  << be_functor.origin() << "::" << be_functor.name() << "." << endl
                  << "The frontend header for " << be_functor.origin() << " declares that this function or variable" << endl
                  << "can be used with model " << *it << ", but that model is not interpretable (via ancestry or friend" << endl
                  << "relationships) as any of the models declared as allowed for the entire backend with the BE_ALLOW_MODELS" << endl
                  << "directive.  If the current declarations were to be taken at face value, this function/variable would " << endl
                  << "*never* be activated, for any model.  Please correct one or the other of these declarations." << endl;
              backend_error().raise(LOCAL_INFO, msg.str());
            }
          }
        }
      }
      // Allow the models
      if (not models.empty())
      {
        for (std::vector<str>::const_iterator it = models.begin(); it != models.end(); ++it)
        {
          be_functor.setAllowedModel(*it);
        }
      }
    }
    catch (std::exception& e) { ini_catch(e); }
    return 0;
  }

  /// Register a backend with the logging system
  int register_backend_with_log(str s)
  {
    try
    {
      int mytag = Logging::getfreetag();
      Logging::tag2str()[mytag] = s;
      Logging::components().insert(mytag);
    }
    catch (std::exception& e) { ini_catch(e); }
    return 0;
  }

  /// Register a bossed type with the rollcall system
  int register_type(str bever, str classname)
  {
    try
    {
      Utils::strip_whitespace_except_after_const(classname);
      Backends::backendInfo().classes[bever].insert(classname);
    }
    catch (std::exception& e) { ini_catch(e); }
    return 0;
  }

  /// Disable a C, C++ or Fortran backend functor if its library is missing or the symbol cannot be found.
  void set_backend_functor_status_C_CXX_Fortran(functor& be_functor, const std::vector<str>& symbol_names)
  {
    bool present = Backends::backendInfo().works.at(be_functor.origin() + be_functor.version());
    if (not present)
    {
      be_functor.setStatus(FunctorStatus::Origin_missing);
    }
    else if(dlerror() != NULL and symbol_names[0] != "no_symbol")
    {
      std::ostringstream err;
      err << "None of the library symbols (";
      for(str smb : symbol_names) { err << smb << ", "; }
      err.seekp(-2, std::ios_base::end);
      err << ") was found." << std::endl
          << "The backend function from this symbol will be disabled (i.e. get status = -2)" << std::endl;
      backend_warning().raise(LOCAL_INFO, err.str());
      be_functor.setStatus(FunctorStatus::Function_missing);
    }
  }

  #ifdef HAVE_MATHEMATICA
  /// Disable a Mathematica backend functor if its package is missing or the function is not found in the package
  void set_backend_functor_status_Mathematica(functor& be_functor, str symbol_name)
  {
    const str be = be_functor.origin() + be_functor.version();
    bool present = Backends::backendInfo().works.at(be);
    if (not present)
    {
      be_functor.setStatus(FunctorStatus::Origin_missing);
    }
    else if(symbol_name != "no_symbol")
    {
      WSLINK pHandle = Backends::backendInfo().loaded_mathematica_backends.at(be);
      std::ostringstream err;
      // Replace \[ for \\[ so that names can have non-ASCII characters
      boost::replace_all(symbol_name, "\\[", "\\\\[");
      if(!WSPutFunction(pHandle, "NameQ", 1) or
         !WSPutFunction(pHandle, "StringDrop",2) or
         !WSPutFunction(pHandle, "StringDrop",2) or
         !WSPutFunction(pHandle, "ToString", 1) or
         !WSPutFunction(pHandle, "ToExpression", 3) or
         !WSPutString(pHandle, symbol_name.c_str()) or
         !WSPutSymbol(pHandle, "StandardForm") or
         !WSPutSymbol(pHandle, "Hold") or
         !WSPutInteger(pHandle, 5) or
         !WSPutInteger(pHandle, -1))
      {
        err << "Error sending packet through WSTP." << std::endl;
        backend_warning().raise(LOCAL_INFO, err.str());
        be_functor.setStatus(FunctorStatus::Function_missing);
      }

      int pkt;
      while( (pkt = WSNextPacket(pHandle), pkt) && pkt != RETURNPKT)
      {
        WSNewPacket(pHandle);
        if (WSError(pHandle))
        {
          err << "Error reading packet from WSTP" << std::endl;
          backend_warning().raise(LOCAL_INFO, err.str());
          be_functor.setStatus(FunctorStatus::Function_missing);
        }
      }

      const char *symbol_exists;
      if(!WSGetString(pHandle, &symbol_exists))
      {
        err << "Error retrieving packet from WSTP." << std::endl;
        backend_warning().raise(LOCAL_INFO, err.str());
        be_functor.setStatus(FunctorStatus::Function_missing);
      }

      if(str(symbol_exists) == "False")
      {
        err << "Mathematica function " << symbol_name << " not found."  << std::endl
            << "The backend function from this symbol will be disabled (i.e. get status = -2)" << std::endl;
        backend_warning().raise(LOCAL_INFO, err.str());
        be_functor.setStatus(FunctorStatus::Function_missing);
      }
    }
  }
  #endif

  #ifdef HAVE_PYBIND11
  /// Disable a Python backend functor if its module is missing or the function is not found in the module
  void set_backend_functor_status_Python(functor& be_functor, const str& symbol_name)
  {
    const str be = be_functor.origin() + be_functor.version();
    bool present = Backends::backendInfo().works.at(be);
    if (not present)
    {
      be_functor.setStatus(FunctorStatus::Origin_missing);
    }
    else if(symbol_name != "no_symbol")
    {
      if (Backends::backendInfo().dlerrors[be] == symbol_name) be_functor.setStatus(FunctorStatus::Function_missing);
    }
  }
  #endif


  /// Disable a backend functor if its library is missing or the symbol cannot be found.
  int set_backend_functor_status(functor& be_functor, const std::vector<str>& symbol_names)
  {
    // Extract the backend that we're dealing with from the functor metadata.
    str be = be_functor.origin() + be_functor.version();
    try
    {
      // Now switch according to the language of the backend
      if (Backends::backendInfo().needsMathematica.at(be))
      {
        if (symbol_names.size() != 1) backend_error().raise(LOCAL_INFO, be+" is a Mathematica backend; "
         +be_functor.origin()+"::"+be_functor.name()+" can have only one symbol.");
        // And switch according to whether the language has its dependencies met or not
        #ifdef HAVE_MATHEMATICA
          set_backend_functor_status_Mathematica(be_functor, symbol_names[0]);
        #else
          std::ostringstream err;
          err << "Mathematica is not found or it is disabled. " << std::endl
              << "The backend function for the symbol " << symbol_names[0] << " will be disabled  (i.e. get status = -5)" << endl;
          be_functor.setStatus(FunctorStatus::Mathematica_missing);
          backend_warning().raise(LOCAL_INFO, err.str());
        #endif
      }
      // and so on.
      else if (Backends::backendInfo().needsPython.at(be))
      {
        if (symbol_names.size() != 1) backend_error().raise(LOCAL_INFO, be+" is a Python backend; "
         +be_functor.origin()+"::"+be_functor.name()+" can have only one symbol.");
        #ifdef HAVE_PYBIND11
          set_backend_functor_status_Python(be_functor, symbol_names[0]);
        #else
          std::ostringstream err;
          err << "Pybind11 for interfacing with Python backends is not found or disabled. " << std::endl
              << "The backend function for the symbol " << symbol_names[0] << " will be disabled  (i.e. get status = -6)" << endl;
          be_functor.setStatus(FunctorStatus::Pybind_missing);
          backend_warning().raise(LOCAL_INFO, err.str());
        #endif
      }
      else
      {
        set_backend_functor_status_C_CXX_Fortran(be_functor, symbol_names);
      }

    }
    catch (std::exception& e) { ini_catch(e); }
    return 0;
  }


  /// Disable a backend initialisation function if the backend is missing.
  int set_BackendIniBit_functor_status(functor& ini_functor, str be, str v)
  {
    bool present = Backends::backendInfo().works.at(be + v);
    try
    {
      if (not present)
      {
        ini_functor.setStatus(FunctorStatus::Backend_missing);
      }
    }
    catch (std::exception& e) { ini_catch(e); }
    return 0;
  }

  /// Get the status of a factory pointer to a BOSSed type's wrapper constructor.
  int get_ctor_status(str be, str ver, str name, str barename, str args, const std::vector<str>& symbol_names)
  {
    bool present = Backends::backendInfo().works.at(be+ver);
    try
    {
      const str path = Backends::backendInfo().corrected_path(be,ver);
      Backends::backendInfo().factory_args[be+ver+fixns(barename)].insert(args);
      if (not present)
      {
        std::ostringstream err;
        Backends::backendInfo().classes_OK[be+ver] = false;
        Backends::backendInfo().constructor_status[be+ver+fixns(barename+args)] = "lib absent";
        return -1;
      }
      else if (dlerror() != NULL)
      {
        std::ostringstream err;
        Backends::backendInfo().classes_OK[be+ver] = false;
        Backends::backendInfo().constructor_status[be+ver+fixns(barename+args)] = "broken";
        err << "None of the library symbols (";
        for(str smb : symbol_names) { err << smb << ", "; }
        err.seekp(-2, std::ios_base::end);
        err << ") was found in " << path << "."
            << std::endl << "The BOSSed type relying on factory " << name << args
            << " will be unavailable." << std::endl;
        backend_warning().raise(LOCAL_INFO, err.str());
        return -2;
      }
      else
      {
        logger() << "Succeeded in loading constructor " << fixns(barename+args) << " from "<< std::endl
                 << path << "." << LogTags::backends << LogTags::info << EOM;
        Backends::backendInfo().constructor_status[be+ver+fixns(barename+args)] = "OK";
      }
    }
    catch (std::exception& e) { ini_catch(e); }
    return 0;
  }


  /// Set a backend rule for one or more models.
  int set_backend_rule_for_model(module_functor_common& f, str models, str tags)
  {
    try
    {
      f.makeBackendRuleForModel(models, tags);
    }
    catch (std::exception& e) { ini_catch(e); }
    return 0;
  }


  /// Set the classloading requirements of a given functor.
  int set_classload_requirements(module_functor_common& f, str be, str verstr, str default_ver)
  {
    try
    {
      // Split up the passed version string into individual versions
      std::vector<str> versions = Utils::delimiterSplit(verstr, ",");
      // Add each version individually as required for classloading
      for (auto it = versions.begin() ; it != versions.end(); ++it)
      {
        // Retrieve the version corresponding to the default if needed
        if (*it == "default") *it = Backends::backendInfo().version_from_safe_version(be, default_ver);
        // Retrieve the safe version corresponding to this version
        str sv = Backends::backendInfo().safe_version_from_version(be, *it);
        // Set the requirement in the functor
        f.setRequiredClassloader(be,*it,sv);
      }
    }
    catch (std::exception& e) { ini_catch(e); }
    return 0;
  }

}

Updated on 2024-07-18 at 13:53:36 +0000