file src/Elements/src/functors.cpp
[No description available] More…
Namespaces
Name |
---|
Gambit TODO: see if we can use this one: |
Detailed Description
Author:
- Pat Scott (patscott@physics.mcgill.ca)
- Anders Kvellestad (anders.kvellestad@fys.uio.no)
- Christoph Weniger (c.weniger@uva.nl)
- Ben Farmer (benjamin.farmer@monash.edu.au)
- Lars A. Dal (l.a.dal@fys.uio.no)
- Tomas Gonzalo (gonzalo@physik.rwth-aachen.de)
- Patrick Stoecker (stoecker@physik.rwth-aachen.de)
Date:
- 2013 Apr-July, Dec
- 2014 Jan, Mar-May, Sep
- 2015 Apr
- 2013 Apr, Nov
- 2013 May, June, July
- 2013 July, Sep
- 2014 Jan
- 2015 Nov
- 2015 Jan
- 2021 Sep
- 2023 May, Nov
Functor base class definitions.
Authors (add name and date if you modify):
Source code
// GAMBIT: Global and Modular BSM Inference Tool
// *********************************************
/// \file
///
/// Functor base class definitions.
///
/// *********************************************
///
/// Authors (add name and date if you modify):
///
/// \author Pat Scott
/// (patscott@physics.mcgill.ca)
/// \date 2013 Apr-July, Dec
/// \date 2014 Jan, Mar-May, Sep
/// \date 2015 Apr
///
/// \author Anders Kvellestad
/// (anders.kvellestad@fys.uio.no)
/// \date 2013 Apr, Nov
///
/// \author Christoph Weniger
/// (c.weniger@uva.nl)
/// \date 2013 May, June, July
///
/// \author Ben Farmer
/// (benjamin.farmer@monash.edu.au)
/// \date 2013 July, Sep
/// \date 2014 Jan
/// \date 2015 Nov
///
/// \author Lars A. Dal
/// (l.a.dal@fys.uio.no)
/// \date 2015 Jan
///
/// \author Tomas Gonzalo
/// (gonzalo@physik.rwth-aachen.de)
/// \date 2021 Sep
///
/// \author Patrick Stoecker
/// (stoecker@physik.rwth-aachen.de)
/// \date 2023 May, Nov
///
/// *********************************************
#include <chrono>
#include "gambit/Elements/functors.hpp"
#include "gambit/Elements/functor_definitions.hpp"
#include "gambit/Elements/type_equivalency.hpp"
#include "gambit/Utils/standalone_error_handlers.hpp"
#include "gambit/Models/models.hpp"
#include "gambit/Logs/logger.hpp"
#include "gambit/Logs/logging.hpp"
#include <boost/preprocessor/seq/for_each.hpp>
#include <boost/io/ios_state.hpp>
namespace Gambit
{
using namespace LogTags;
// Functor class methods
/// Constructor
functor::functor (str func_name,
str func_capability,
str result_type,
str origin_name,
Models::ModelFunctorClaw &claw) :
myName (func_name),
myCapability (func_capability),
myType (Utils::fix_type(result_type)),
myOrigin (origin_name),
myClaw (&claw),
myLabel ("#"+func_capability+" @"+origin_name+"::"+func_name),
myTimingLabel ("Runtime(ns) for "+myLabel),
myStatus (FunctorStatus::Model_incompatible),
myVertexID (-1), // (Note: myVertexID = -1 is intended to mean that no vertexID has been assigned)
myTimingVertexID(-1), // Not actually a graph vertex; ID assigned by "get_main_param_id" function.
verbose (false) // For debugging.
{}
/// Virtual calculate(); needs to be redefined in daughters.
void functor::calculate() {}
/// Interfaces for runtime optimization
/// Need to be implemented by daughters
/// @{
double functor::getRuntimeAverage() { return 0; }
double functor::getInvalidationRate() { return 0; }
void functor::setFadeRate(double) {}
void functor::notifyOfInvalidation(const str&) {}
void functor::reset() {}
void functor::reset(int) {}
/// @}
/// Reset-then-recalculate method
void functor::reset_and_calculate() { this->reset(omp_get_thread_num()); this->calculate(); }
/// Setter for purpose (relevant only for next-to-output functors)
void functor::setPurpose(str purpose) { myPurpose = purpose; }
/// Setter for vertex ID (used in printer system)
void functor::setVertexID(int ID) { myVertexID = ID; }
/// Acquire ID for timing 'vertex' (used in printer system)
void functor::setTimingVertexID(int ID) { myTimingVertexID = ID; }
/// Setter for status
void functor::setStatus(FunctorStatus stat)
{
myStatus = stat;
setInUse(myStatus == FunctorStatus::Active);
}
/// Getter for the wrapped function's name
str functor::name() const { return myName; }
/// Getter for the wrapped function's reported capability
str functor::capability() const { return myCapability; }
/// Getter for the wrapped function's reported return type
str functor::type() const { return myType; }
/// Getter for the wrapped function's origin (module or backend name)
str functor::origin() const { return myOrigin; }
/// Getter for the version of the wrapped function's origin (module or backend)
str functor::version() const { utils_error().raise(LOCAL_INFO,"The version method is only defined for backend functors."); return ""; }
/// Getter for the 'safe' incarnation of the version of the wrapped function's origin (module or backend)
str functor::safe_version()const { utils_error().raise(LOCAL_INFO,"The safe_version method is only defined for backend functors."); return ""; }
/// Getter for the functors current status
FunctorStatus functor::status() const { return myStatus; }
/// Checks whether the functor is available (or even already activate)
bool functor::isAvailable() const { return myStatus > 0; }
/// Checks whether the functor is active (or even hyperactive)
bool functor::isActive() const { return myStatus >= 2; }
/// Checks whether the functor is disabled (discriminant is negative)
bool functor::isDisabled() const { return myStatus < 0; }
/// Checks whether the functor is enabled (discriminant is non negative)
bool functor::isEnabled() const { return myStatus >= 0; }
/// Getter for the overall quantity provided by the wrapped function (capability-type pair)
sspair functor::quantity() const { return std::make_pair(myCapability, myType); }
/// Getter for purpose (relevant for output nodes, aka helper structures for the dep. resolution)
str functor::purpose() const { return myPurpose; }
/// Getter for citation key
str functor::citationKey() const { return myCitationKey; }
/// Getter for vertex ID
int functor::vertexID() const { return myVertexID; }
/// Getter for timing vertex ID
int functor::timingVertexID() const { return myTimingVertexID; }
/// Getter indicating if the wrapped function's result should to be printed
bool functor::requiresPrinting() const { return false; }
/// Getter indicating if the timing data for this function's execution should be printed
bool functor::requiresTimingPrinting() const { return false; }
/// Getter for the printer label
str functor::label() const { return myLabel; }
/// Getter for the printer timing label
str functor::timingLabel() const { return myTimingLabel; }
/// Setter for indicating if the wrapped function's result should to be printed
void functor::setPrintRequirement(bool flag)
{
if (flag)
{
utils_error().raise(LOCAL_INFO,"The setPrintRequirement method has not been defined in this class.");
}
}
/// Setter for indicating if the timing data for this functor should be printed
void functor::setTimingPrintRequirement(bool flag)
{
if (flag)
{
utils_error().raise(LOCAL_INFO,"The setTimingPrintRequirement method has not been defined in this class.");
}
}
/// Set the ordered list of pointers to other functors that should run nested in a loop managed by this one
void functor::setNestedList (std::vector<functor*>&)
{
utils_error().raise(LOCAL_INFO,"The setNestedList method has not been defined in this class.");
}
/// Set the iteration number in a loop in which this functor runs
void functor::setIteration (long long)
{
utils_error().raise(LOCAL_INFO,"The setIteration method has not been defined in this class.");
}
/// Getter for revealing whether this is permitted to be a manager functor
bool functor::canBeLoopManager()
{
utils_error().raise(LOCAL_INFO,"The canBeLoopManager method has not been defined in this class.");
return false;
}
/// Getter for revealing whether this functor needs a loop manager
bool functor::needsLoopManager()
{
utils_error().raise(LOCAL_INFO,"The needsLoopManager method has not been defined in this class.");
return false;
}
/// Getter for revealing the required capability of the wrapped function's loop manager
str functor::loopManagerCapability()
{
utils_error().raise(LOCAL_INFO,"The loopManagerCapability method has not been defined in this class.");
return "none";
}
/// Getter for revealing the required type of the wrapped function's loop manager
str functor::loopManagerType()
{
utils_error().raise(LOCAL_INFO,"The loopManagerType method has not been defined in this class.");
return "none";
}
/// Getter for revealing the name of the wrapped function's assigned loop manager
str functor::loopManagerName()
{
utils_error().raise(LOCAL_INFO,"The loopManagerName method has not been defined in this class.");
return "none";
}
/// Getter for revealing the module of the wrapped function's assigned loop manager
str functor::loopManagerOrigin()
{
utils_error().raise(LOCAL_INFO,"The loopManagerOrigin method has not been defined in this class.");
return "none";
}
/// Getter for listing currently activated dependencies
std::set<sspair> functor::dependencies()
{
utils_error().raise(LOCAL_INFO,"The dependencies method has not been defined in this class.");
std::set<sspair> empty;
return empty;
}
/// Getter for listing backends that require class loading
std::set<sspair> functor::backendclassloading()
{
utils_error().raise(LOCAL_INFO,"The backendclassloading method has not been defined in this class.");
std::set<sspair> empty;
return empty;
}
/// Getter for listing backend requirement groups
std::set<str> functor::backendgroups()
{
utils_error().raise(LOCAL_INFO,"The backendgroups method has not been defined in this class.");
std::set<str> empty;
return empty;
}
/// Getter for listing all backend requirements
std::set<sspair> functor::backendreqs()
{
utils_error().raise(LOCAL_INFO,"The backendreqs method has not been defined in this class.");
std::set<sspair> empty;
return empty;
}
/// Getter for listing backend requirements from a specific group
std::set<sspair> functor::backendreqs(str)
{
utils_error().raise(LOCAL_INFO,"The backendreqs method has not been defined in this class.");
std::set<sspair> empty;
return empty;
}
/// Getter for listing permitted backends
std::set<sspair> functor::backendspermitted(sspair)
{
utils_error().raise(LOCAL_INFO,"The backendspermitted method has not been defined in this class.");
std::set<sspair> empty;
return empty;
}
/// Getter for listing tags associated with backend requirements
std::set<str> functor::backendreq_tags(sspair)
{
utils_error().raise(LOCAL_INFO,"The backendreq_tags method has not been defined in this class.");
std::set<str> empty;
return empty;
}
/// Getter for listing backend requirements that must be resolved from the same backend
std::set<sspair> functor::forcematchingbackend(str)
{
utils_error().raise(LOCAL_INFO,"The forcematchingbackend method has not been defined in this class.");
std::set<sspair> empty;
return empty;
}
/// Getter for listing backend-specific conditional dependencies (4-string version)
std::set<sspair> functor::backend_conditional_dependencies (str, str, str, str)
{
utils_error().raise(LOCAL_INFO,"The backend_conditional_dependencies method has not been defined in this class.");
std::set<sspair> empty;
return empty;
}
/// Tell the functor that the loop it manages should break now.
void functor::breakLoop()
{
utils_error().raise(LOCAL_INFO,"The breakLoop method has not been defined in this class.");
}
/// Getter for backend-specific conditional dependencies (3-string version)
std::set<sspair> functor::backend_conditional_dependencies (str req, str type, str be)
{
return backend_conditional_dependencies(req, type, be, "any");
}
/// Getter for backend-specific conditional dependencies (backend functor pointer version)
std::set<sspair> functor::backend_conditional_dependencies (functor* be_functor)
{
return backend_conditional_dependencies (be_functor->capability(), be_functor->type(),
be_functor->origin(), be_functor->version());
}
/// Getter for listing model-specific conditional dependencies (matches also on parents and friends)
std::set<sspair> functor::model_conditional_dependencies (str)
{
utils_error().raise(LOCAL_INFO,"The model_conditional_dependencies method has not been defined in this class.");
std::set<sspair> empty;
return empty;
}
/// Getter for listing model-specific conditional dependencies (matches on the exact model)
std::set<sspair> functor::model_conditional_dependencies_exact (str)
{
utils_error().raise(LOCAL_INFO,"The model_conditional_dependencies_exact method has not been defined in this class.");
std::set<sspair> empty;
return empty;
}
/// Getter for listing model-specific conditional backend requirements (matches also on parents and friends)
std::set<sspair> functor::model_conditional_backend_reqs (str)
{
utils_error().raise(LOCAL_INFO,"The model_conditional_backend_reqs method has not been defined in this class.");
std::set<sspair> empty;
return empty;
}
/// Getter for listing model-specific conditional backend requirements (matches on the exact model)
std::set<sspair> functor::model_conditional_backend_reqs_exact (str)
{
utils_error().raise(LOCAL_INFO,"The model_conditional_backend_reqs_exact method has not been defined in this class.");
std::set<sspair> empty;
return empty;
}
/// Resolve a dependency using a pointer to another functor object
void functor::resolveDependency (functor*)
{
utils_error().raise(LOCAL_INFO,"The resolveDependency method has not been defined in this class.");
}
/// Set this functor's loop manager (if it has one)
void functor::resolveLoopManager (functor*)
{
utils_error().raise(LOCAL_INFO,"The resolveLoopManager method has not been defined in this class.");
}
/// Resolve a backend requirement using a pointer to another functor object
void functor::resolveBackendReq (functor*)
{
utils_error().raise(LOCAL_INFO,"The resolveBackendReq method has not been defined in this class.");
}
/// Notify the functor that a certain model is being scanned, so that it can activate its dependencies accordingly.
void functor::notifyOfModel(str)
{
utils_error().raise(LOCAL_INFO,"The notifyOfModel method has not been defined in this class.");
}
/// Notify the functor that it is being used to fill a dependency of another functor
void functor::notifyOfDependee (functor*)
{
utils_error().raise(LOCAL_INFO,"The notifyOfDependee method has not been defined in this class.");
}
/// Indicate to the functor which backends are actually loaded and working
void functor::notifyOfBackends(std::map<str, std::set<str> >)
{
utils_error().raise(LOCAL_INFO,"The notifyOfBackends method has not been defined in this class.");
}
#ifndef NO_PRINTERS
/// Print function
void functor::print(Printers::BasePrinter*, const int, int)
{
str warn_msg = "This is the functor base class print function! This should not\n";
warn_msg += "be used; the print function should be redefined in daughter\n"
"functor classes. If this is running there is a problem somewhere.\n"
"Currently only functors derived from module_functor_common<!=void>\n"
"are allowed to try to print themselves; i.e. backend and void\n"
"functors may not do this (they inherit this default message).";
utils_warning().raise(LOCAL_INFO,warn_msg);
}
/// Printer function (no-thread-index short-circuit)
void functor::print(Printers::BasePrinter* printer, const int pointID)
{
print(printer,pointID,0);
}
#endif
/// Notify the functor about an instance of the options class that contains
/// information from its corresponding ini-file entry in the auxiliaries or
/// observables section.
void functor::notifyOfIniOptions(const Options& opt)
{
myOptions = opt;
}
/// Return a safe pointer to the options that this functor is supposed to run with (e.g. from the ini file).
safe_ptr<Options> functor::getOptions()
{
return safe_ptr<Options>(&myOptions);
}
/// Notify the functor about a string in YAML that contains the sub-capability information (for use in standalones)
void functor::notifyOfSubCaps(const str& subcap_string)
{
YAML::Node subcap_node_simple = YAML::Load(subcap_string);
YAML::Node subcap_node_complex;
for (auto x : subcap_node_simple) subcap_node_complex[x.as<str>()] = YAML::Node();
notifyOfSubCaps(subcap_node_complex);
}
/// Notify the functor about an instance of the options class that contains sub-capability information
void functor::notifyOfSubCaps(const Options& subcaps)
{
for (auto entry : subcaps)
{
str key = entry.first.as<std::string>();
if (not mySubCaps.hasKey(key)) mySubCaps.setValue(key, entry.second);
else
{
std::ostringstream ss1, ss2;
ss1 << mySubCaps.getValue<YAML::Node>(key);
ss2 << entry.second.as<YAML::Node>();
if (not (ss1.str() == ss2.str()))
{
std::ostringstream ss;
ss << "Duplicate sub_capability clash. " << endl
<< "Your ObsLike section of the YAML file contains both " << endl
<< key << ": " << ss1.str() << endl << "and" << endl << key << ": " << ss2.str() << endl
<< "GAMBIT does not know which value to choose when trying to determine the sub-capabilities" << endl
<< "served by " << myOrigin << "::" << myName << ", as both ObsLike entries depend on this function.";
utils_error().raise(LOCAL_INFO, ss.str());
}
}
}
}
/// Return a safe pointer to the subcaps that this functor realises it is supposed to facilitate downstream calculation of.
safe_ptr<Options> functor::getSubCaps()
{
return safe_ptr<Options>(&mySubCaps);
}
/// Return a safe pointer to the vector of all capability,type pairs of functors arranged downstream of this one in the dependency tree.
safe_ptr<std::set<sspair>> functor::getDependees()
{
return safe_ptr<std::set<sspair>>(&myDependees);
}
/// Getter for listing allowed models
const std::set<str>& functor::getAllowedModels()
{
return allowedModels;
}
/// Getter for listing conditional models
const std::set<str>& functor::getConditionalModels()
{
return conditionalModels;
}
/// Getter for map of model groups and the set of models in each group
const std::map<str, std::set<str>>& functor::getModelGroups()
{
return modelGroups;
}
/// Test whether the functor is allowed (either explicitly or implicitly) to be used with a given model
bool functor::modelAllowed(str model)
{
bool allowed = false;
if (verbose)
{
std::cout << "Checking allowedModels set for functor "<<myLabel<<std::endl;
for(std::set<str>::iterator it = allowedModels.begin(); it != allowedModels.end(); ++it)
{
std::cout << " "<< *it << std::endl;
}
}
if (allowedModels.empty() and allowedGroupCombos.empty()) allowed=true;
if (allowed_parent_or_friend_exists(model)) allowed=true;
if (verbose) std::cout << " Allowed to be used with model "<<model<<"? "<<allowed<<std::endl;
return allowed;
}
/// Test whether the functor has been explictly allowed to be used with a given model
bool functor::modelExplicitlyAllowed(str model)
{
if (allowedModels.find(model) != allowedModels.end()) return true;
return false;
}
/// Test whether the functor is allowed to be used with all models
bool functor::allModelsAllowed()
{
return allowedModels.empty() and allowedGroupCombos.empty();
}
/// Add a model to the internal list of models for which this functor is allowed to be used.
void functor::setAllowedModel(str model) { allowedModels.insert(model); }
/// Test whether the functor is allowed (either explicitly or implicitly) to be used with a given combination of models
bool functor::modelComboAllowed(std::set<str> combo)
{
// If any model in the combo is always allowed, then give the combo a thumbs up.
for(std::set<str>::const_iterator model = combo.begin(); model != combo.end(); model++)
{
if (modelAllowed(*model)) return true;
}
// Loop over the allowed combinations, and check if the passed combo matches any of them
for(std::set<std::set<str> >::const_iterator group_combo = allowedGroupCombos.begin(); group_combo != allowedGroupCombos.end(); group_combo++)
{
bool matches = true;
//Loop over each group in the allowed group combination, and check if one of the entries in the passed model combination matches it somehow.
for(std::set<str>::const_iterator group = group_combo->begin(); group != group_combo->end(); group++)
{
matches = matches and contains_anything_interpretable_as_member_of(combo, *group);
if (not matches) break;
}
//Return true immediately if all entries in the allowed group combination have been matched.
if (matches) return true;
}
return false;
}
/// Test whether the functor has been explictly allowed to be used with a given combination of models
bool functor::modelComboExplicitlyAllowed(std::set<str> combo)
{
// If any model in the combo is always explicitly allowed, then give the combo a thumbs up.
for(std::set<str>::const_iterator model = combo.begin(); model != combo.end(); model++)
{
if (modelExplicitlyAllowed(*model)) return true;
}
// Loop over the allowed combinations, and check if the passed combo matches any of them
for(std::set<std::set<str> >::const_iterator group_combo = allowedGroupCombos.begin(); group_combo != allowedGroupCombos.end(); group_combo++)
{
bool matches = true;
//Loop over each group in the allowed group combination, and check if one of the entries in the passed model combination matches it explicitly.
for(std::set<str>::const_iterator group = group_combo->begin(); group != group_combo->end(); group++)
{
matches = matches and has_common_elements(combo, *group);
if (not matches) break;
}
//Return true immediately if all entries in the allowed group combination have been matched.
if (matches) return true;
}
return false;
}
/// Add a model group definition to the internal list of model groups.
void functor::setModelGroup(str group, str contents)
{
//Strip the group contents of its parentheses, then split it, turn it into a set and save it in the map
Utils::strip_parentheses(contents);
std::vector<str> v = Utils::delimiterSplit(contents, ",");
std::set<str> combo(v.begin(), v.end());
modelGroups[group] = combo;
}
/// Add a model group combination to the internal list of combinations for which this functor is allowed to be used.
void functor::setAllowedModelGroupCombo(str groups)
{
//Strip the group combo of its parentheses and whitespace, then split it and save it in the vector of allowed combos
Utils::strip_parentheses(groups);
Utils::strip_whitespace_except_after_const(groups);
std::vector<str> v = Utils::delimiterSplit(groups, ",");
std::set<str> group_combo(v.begin(), v.end());
allowedGroupCombos.insert(group_combo);
}
/// Add an observable to the set of those that this functor matches.
void functor::addMatchedObservable(const DRes::Observable* obs) { matched_observables.insert(obs); }
/// Retrieve the set of observables that this functor matches.
const std::set<const DRes::Observable*>& functor::getMatchedObservables() { return matched_observables; }
/// Add a module rule to the set of those against which this functor has been tested and found to match.
void functor::addMatchedModuleRule(const DRes::ModuleRule* r) { matched_module_rules.insert(r); }
/// Add a backend rule to the set of those against which this functor has been tested and found to match.
void functor::addMatchedBackendRule(const DRes::BackendRule* r) { matched_backend_rules.insert(r); }
/// Retrieve the set of module rules against which this functor has been tested and found to match.
const std::set<const DRes::ModuleRule*>& functor::getMatchedModuleRules() { return matched_module_rules; }
/// Retrieve the set of backend rules against which this functor has been tested and found to match.
const std::set<const DRes::BackendRule*>& functor::getMatchedBackendRules() { return matched_backend_rules; }
// Retrieve matched rules by type.
template<>
const std::set<const DRes::ModuleRule*>& functor::getMatchedRules() { return getMatchedModuleRules(); }
template<>
const std::set<const DRes::BackendRule*>& functor::getMatchedRules() { return getMatchedBackendRules(); }
/// Attempt to retrieve a dependency or model parameter that has not been resolved
void functor::failBigTime(str method)
{
str error_msg = "Attempted to use a functor method from a null pointer.";
error_msg += "\nProbably you tried to use a conditional dependency that has"
"\nnot been activated, or a model parameter that is not defined"
"\nin the model for which this function has been invoked."
"\nPlease check your module function source code."
"\nMethod invoked: " + method + ".";
utils_error().raise(LOCAL_INFO,error_msg);
}
/// Test if there is a model in the functor's allowedModels list as which this model can be interpreted
inline bool functor::allowed_parent_or_friend_exists(str model)
{
for (std::set<str>::reverse_iterator it = allowedModels.rbegin() ; it != allowedModels.rend(); ++it)
{
if (myClaw->model_exists(*it))
{
if (myClaw->downstream_of(model, *it)) return true;
}
}
return false;
}
/// Check that a model is actually part of a combination that is allowed to be used with this functor.
inline bool functor::in_allowed_combo(str model)
{
// If the model is allowed on its own, just give the thumbs up immediately.
if (modelAllowed(model)) return true;
// Loop over the allowed combinations, and check if the passed model matches anything in any of them
for(std::set<std::set<str> >::const_iterator group_combo = allowedGroupCombos.begin(); group_combo != allowedGroupCombos.end(); group_combo++)
{
//Loop over each group in the allowed group combination, and check if the model is interpretable as a model in the group.
for(std::set<str>::const_iterator group = group_combo->begin(); group != group_combo->end(); group++)
{
// Work through the members of the model group
std::set<str> models = modelGroups.at(*group);
for (std::set<str>::reverse_iterator it = models.rbegin() ; it != models.rend(); ++it)
{
if (myClaw->model_exists(*it))
{
if (myClaw->downstream_of(model, *it)) return true;
}
}
}
}
return false;
}
/// Test whether any of the entries in a given model group is a valid interpretation of any members in a given combination
inline bool functor::contains_anything_interpretable_as_member_of(std::set<str> combo, str group)
{
// Work through the members of the model group
std::set<str> models = modelGroups.at(group);
for (std::set<str>::const_iterator it = models.begin() ; it != models.end(); ++it)
{
if (myClaw->model_exists(*it))
{
// Work through the members of the combination
for (std::set<str>::const_iterator jt = combo.begin() ; jt != combo.end(); ++jt)
{
if (myClaw->model_exists(*jt))
{
if (myClaw->downstream_of(*jt, *it)) return true;
}
}
}
}
return false;
}
/// Work out whether a given combination of models and a model group have any elements in common
inline bool functor::has_common_elements(std::set<str> combo, str group)
{
// Work through the members of the model group
std::set<str> models = modelGroups.at(group);
for (std::set<str>::reverse_iterator it = models.rbegin() ; it != models.rend(); ++it)
{
if ( std::find(combo.begin(), combo.end(), *it) == combo.end() ) return true;
}
return false;
}
/// Try to find a parent or friend model in some user-supplied map from models to sspair vectors
/// Preferentially returns the 'least removed' parent or friend, i.e. less steps back in the model lineage.
str functor::find_friend_or_parent_model_in_map(str model, std::map< str, std::set<sspair> > karta)
{
std::vector<str> candidates;
for (std::map< str, std::set<sspair> >::reverse_iterator it = karta.rbegin() ; it != karta.rend(); ++it)
{
if (myClaw->model_exists(it->first))
{
if (myClaw->downstream_of(model, it->first)) candidates.push_back(it->first);
}
}
// If found no candidates, return the empty string.
if (candidates.empty()) return "";
// If found just one, return it with no further questions.
if (candidates.size() == 1) return candidates[0];
// If found more than one, choose the one closest to the model passed in.
str result = candidates.front();
for (std::vector<str>::iterator it = candidates.begin()+1; it != candidates.end(); ++it)
{
if (myClaw->downstream_of(*it, result)) result = *it;
}
return result;
}
/// Retrieve the previously saved exception generated when this functor invalidated the current point in model space.
invalid_point_exception* functor::retrieve_invalid_point_exception() { return NULL; }
// Module_functor_common class methods
/// Constructor
module_functor_common::module_functor_common(str func_name,
str func_capability,
str result_type,
str origin_name,
Models::ModelFunctorClaw &claw)
: functor (func_name, func_capability, result_type, origin_name, claw),
myTimingPrintFlag (false),
start (NULL),
end (NULL),
point_exception_raised (false),
runtime_average (FUNCTORS_RUNTIME_INIT), // default 1 micro second
fadeRate (FUNCTORS_FADE_RATE), // can be set individually for each functor
pInvalidation (FUNCTORS_BASE_INVALIDATION_RATE),
needs_recalculating (NULL),
already_printed (NULL),
already_printed_timing (NULL),
iCanManageLoops (false),
iRunNested (false),
myLoopManagerCapability ("none"),
myLoopManagerType ("none"),
myLoopManager (NULL),
myCurrentIteration (NULL),
globlMaxThreads (omp_get_max_threads()),
myLogTag (-1)
{
if (globlMaxThreads == 0) utils_error().raise(LOCAL_INFO,"Cannot determine number of hardware threads available on this system.");
// Determine LogTag number
myLogTag = Logging::str2tag(myOrigin);
if (not claw.model_exists(origin_name)) check_missing_LogTag();
}
/// Destructor
module_functor_common::~module_functor_common()
{
if (start != NULL) delete [] start;
if (end != NULL) delete [] end;
if (needs_recalculating != NULL) delete [] needs_recalculating;
if (already_printed != NULL) delete [] already_printed;
if (already_printed_timing != NULL) delete [] already_printed_timing;
if (myCurrentIteration != NULL) delete [] myCurrentIteration;
}
/// Check if an appropriate LogTag for this functor is missing from the logging system.
void module_functor_common::check_missing_LogTag()
{
if(myLogTag==-1)
{
std::ostringstream ss;
ss << "Cannot retrieve LogTag number; no match for module name in tag2str map." << endl
<< "Module: " << myOrigin << endl
<< "Function: " << myName << endl
<< "This is probably because there is no log specified for " << myOrigin << " in your yaml file.";
utils_warning().raise(LOCAL_INFO,ss.str());
}
}
/// Getter for averaged runtime
double module_functor_common::getRuntimeAverage()
{
return runtime_average;
}
/// Setter for indicating if the timing data for this function's execution should be printed
void module_functor_common::setTimingPrintRequirement(bool flag)
{
myTimingPrintFlag = flag;
}
/// Getter indicating if the timing data for this function's execution should be printed
bool module_functor_common::requiresTimingPrinting() const
{
return myTimingPrintFlag;
}
/// Reset functor for all threads
void module_functor_common::reset()
{
init_memory();
int n = (iRunNested ? globlMaxThreads : 1);
std::fill(needs_recalculating, needs_recalculating+n, true);
std::fill(already_printed, already_printed+n, false);
std::fill(already_printed_timing, already_printed_timing+n, false);
if (iCanManageLoops) resetLoop();
point_exception_raised = false;
}
/// Reset functor for one thread only
void module_functor_common::reset(int thread_num)
{
init_memory();
needs_recalculating[thread_num] = true;
already_printed[thread_num] = false;
already_printed_timing[thread_num] = false;
if (iCanManageLoops) resetLoop();
}
/// Tell the functor that it invalidated the current point in model space, pass a message explaining why, and throw an exception.
void module_functor_common::notifyOfInvalidation(const str& msg)
{
acknowledgeInvalidation(invalid_point());
retrieve_invalid_point_exception()->raise(msg);
}
/// Acknowledge that this functor invalidated the current point in model space.
void module_functor_common::acknowledgeInvalidation(invalid_point_exception& e, functor* f)
{
#pragma omp atomic
pInvalidation += fadeRate*(1-FUNCTORS_BASE_INVALIDATION_RATE);
if (f==NULL) f = this;
#pragma omp critical (raised_point_exception)
{
e.set_thrower(f);
raised_point_exception = e;
point_exception_raised = true;
}
if (omp_get_level()!=0) breakLoop();
}
/// Retrieve the previously saved exception generated when this functor invalidated the current point in model space.
invalid_point_exception* module_functor_common::retrieve_invalid_point_exception()
{
if (point_exception_raised) return &raised_point_exception;
for (auto f = myNestedFunctorList.begin(); f != myNestedFunctorList.end(); ++f)
{
invalid_point_exception* e = (*f)->retrieve_invalid_point_exception();
if (e != NULL) return e;
}
return NULL;
}
/// Getter for invalidation rate
double module_functor_common::getInvalidationRate()
{
return pInvalidation;
}
/// Setter for the fade rate
void module_functor_common::setFadeRate(double new_rate)
{
fadeRate = new_rate;
}
/// Indicate whether or not a known model is activated or not.
bool module_functor_common::getActiveModelFlag(str model)
{
if (activeModelFlags.find(model) == activeModelFlags.end())
{
std::ostringstream ss;
ss << "Problem with ModelInUse(\"" << model << "\")." << endl
<< "This model is not known by " << myOrigin << "::" << myName << "." << endl
<< "Please make sure that it has been mentioned in some context in the" << endl
<< "rollcall header declaration of this function.";
model_error().raise(LOCAL_INFO,ss.str());
}
return activeModelFlags.at(model);
}
/// Return a safe pointer to a string indicating which backend requirement has been activated for a given backend group.
safe_ptr<str> module_functor_common::getChosenReqFromGroup(str group)
{
chosenReqsFromGroups[group] = "none";
return safe_ptr<str>(&chosenReqsFromGroups[group]);
}
/// Execute a single iteration in the loop managed by this functor.
void module_functor_common::iterate(long long iteration)
{
if (not myNestedFunctorList.empty())
{
for (std::vector<functor*>::iterator it = myNestedFunctorList.begin();
it != myNestedFunctorList.end(); ++it)
{
(*it)->setIteration(iteration); // Tell the nested functor what iteration this is.
try
{
(*it)->reset_and_calculate(); // Reset the nested functor so that it recalculates, then set it off
}
catch (invalid_point_exception& e)
{
acknowledgeInvalidation(e,*it);
if (omp_get_level()==0) throw(e); // If not in an OpenMP parallel block, inform of invalidation and throw onwards
}
catch (halt_loop_exception& e)
{
// Skip the rest of the iteration, without trying to evaluate the rest of the loop, and wrap it up.
breakLoop();
break;
}
catch (invalid_loop_iteration_exception& e)
{
// Just skip on to the next iteration, without trying to evaluate the rest of the loop.
break;
}
}
}
}
// Initialise the array holding the current iteration(s) of this functor.
void module_functor_common::init_myCurrentIteration_if_NULL()
{
if(myCurrentIteration==NULL)
{
#pragma omp critical(module_functor_init_myCurrentIteration_if_NULL)
{
if(myCurrentIteration==NULL) // Check again in case two threads managed to get this far in sequence.
{
// Set the number of slots to the max number of threads allowed iff this functor can run in parallel
int nslots = (iRunNested ? globlMaxThreads : 1);
// Reserve enough space to hold as many iteration numbers as there are slots (threads) allowed
myCurrentIteration = new long long[nslots];
// Zero them to start off
std::fill(myCurrentIteration, myCurrentIteration+nslots, 0);
}
}
}
}
/// Tell the manager of the loop in which this functor runs that it is time to break the loop.
void module_functor_common::breakLoopFromManagedFunctor()
{
if (myLoopManager == NULL)
{
str errmsg = "Problem whilst attempting to break out of loop:";
errmsg += "\n Loop Manager not properly defined."
"\n This is " + this->name() + " in " + this->origin() + ".";
utils_error().raise(LOCAL_INFO,errmsg);
}
else
{
myLoopManager->breakLoop();
}
}
/// Tell the functor that the loop it manages should break now.
void module_functor_common::breakLoop()
{
#pragma omp critical (myLoopIsDone)
{
myLoopIsDone = true;
}
}
/// Return a safe pointer to the flag indicating that a loop managed by this functor should break now.
safe_ptr<bool> module_functor_common::loopIsDone()
{
return safe_ptr<bool>(&myLoopIsDone);
}
/// Provide a way to reset the flag indicating that a loop managed by this functor should break.
void module_functor_common::resetLoop()
{
#pragma omp critical (myLoopIsDone)
{
myLoopIsDone = false;
}
}
/// Setter for setting the iteration number in the loop in which this functor runs
void module_functor_common::setIteration (long long iteration)
{
init_myCurrentIteration_if_NULL(); // Init memory if this is the first run through.
myCurrentIteration[omp_get_thread_num()] = iteration;
}
/// Return a safe pointer to the iteration number in the loop in which this functor runs.
omp_safe_ptr<long long> module_functor_common::iterationPtr()
{
init_myCurrentIteration_if_NULL(); // Init memory if this is the first run through.
return omp_safe_ptr<long long>(myCurrentIteration);
}
/// Setter for specifying whether this is permitted to be a manager functor, which runs other functors nested in a loop.
void module_functor_common::setCanBeLoopManager (bool canManage) { iCanManageLoops = canManage; }
/// Getter for revealing whether this is permitted to be a manager functor
bool module_functor_common::canBeLoopManager() { return iCanManageLoops; }
/// Setter for specifying the capability and type required of a manager functor, if it is to run this functor nested in a loop.
void module_functor_common::setLoopManagerCapType (str cap, str t)
{
iRunNested = true;
myLoopManagerCapability = cap;
myLoopManagerType = t;
}
/// Getter for revealing whether this functor needs a loop manager
bool module_functor_common::needsLoopManager() { return iRunNested; }
/// Getter for revealing the required capability of the wrapped function's loop manager
str module_functor_common::loopManagerCapability() { return myLoopManagerCapability; }
/// Getter for revealing the required type of the wrapped function's loop manager
str module_functor_common::loopManagerType() { return myLoopManagerType; }
/// Getter for revealing the name of the wrapped function's assigned loop manager
str module_functor_common::loopManagerName() { return (myLoopManager == NULL ? "none" : myLoopManager->name()); }
/// Getter for revealing the module of the wrapped function's assigned loop manager
str module_functor_common::loopManagerOrigin() { return (myLoopManager == NULL ? "none" : myLoopManager->origin()); }
/// Getter for listing currently activated dependencies
std::set<sspair> module_functor_common::dependencies() { return myDependencies; }
/// Getter for listing backends that require class loading
std::set<sspair> module_functor_common::backendclassloading()
{
std::set<sspair> backends;
for(auto backend : required_classloading_backends)
{
for(auto version : backend.second)
{
backends.insert(sspair(backend.first, version));
}
}
return backends;
}
/// Getter for listing backend requirement groups
std::set<str> module_functor_common::backendgroups() { return myGroups; }
/// Getter for listing all backend requirements
std::set<sspair> module_functor_common::backendreqs() { return myResolvableBackendReqs; }
/// Getter for listing backend requirements from a specific group
std::set<sspair> module_functor_common::backendreqs(str group)
{
if (myGroupedBackendReqs.find(group) != myGroupedBackendReqs.end())
{
return myGroupedBackendReqs[group];
}
else
{
std::set<sspair> empty;
return empty;
}
}
/// Getter for listing permitted backends
std::set<sspair> module_functor_common::backendspermitted(sspair quant)
{
if (permitted_map.find(quant) != permitted_map.end())
{
return permitted_map[quant];
}
else
{
std::set<sspair> empty;
return empty;
}
}
/// Getter for listing tags associated with backend requirements
std::set<str> module_functor_common::backendreq_tags(sspair quant)
{
if (backendreq_tagmap.find(quant) != backendreq_tagmap.end())
{
return backendreq_tagmap[quant];
}
else
{
std::set<str> empty;
return empty;
}
}
/// Getter for listing backend requirements that must be resolved from the same backend
std::set<sspair> module_functor_common::forcematchingbackend(str tag)
{
if (myForcedMatches.find(tag) != myForcedMatches.end())
{
return myForcedMatches[tag];
}
else
{
std::set<sspair> empty;
return empty;
}
}
/// Getter for listing backend-specific conditional dependencies (4-string version)
std::set<sspair> module_functor_common::backend_conditional_dependencies (str req, str type, str be, str ver)
{
std::set<sspair> generic_deps, specific_deps, total_deps;
std::vector<str> quad;
quad.push_back(req);
quad.push_back(type);
quad.push_back(be);
quad.push_back(ver);
//Check first what conditional dependencies exist for all versions of this backend
if (ver != "any")
{
generic_deps = backend_conditional_dependencies(req, type, be, "any");
}
//Now see if there are any for this specific version
if (myBackendConditionalDependencies.find(quad) != myBackendConditionalDependencies.end())
{
specific_deps = myBackendConditionalDependencies[quad];
}
//Now put them together
total_deps.insert(generic_deps.begin(), generic_deps.end());
total_deps.insert(specific_deps.begin(), specific_deps.end());
return total_deps;
}
/// Getter for backend-specific conditional dependencies (3-string version)
std::set<sspair> module_functor_common::backend_conditional_dependencies (str req, str type, str be)
{
return backend_conditional_dependencies(req, type, be, "any");
}
/// Getter for backend-specific conditional dependencies (backend functor pointer version)
std::set<sspair> module_functor_common::backend_conditional_dependencies (functor* be_functor)
{
return backend_conditional_dependencies (be_functor->capability(), be_functor->type(),
be_functor->origin(), be_functor->version());
}
/// Getter for listing model-specific conditional dependencies (matches also on parents and friends)
std::set<sspair> module_functor_common::model_conditional_dependencies (str model)
{
str parent = find_friend_or_parent_model_in_map(model,myModelConditionalDependencies);
if (parent != "") return myModelConditionalDependencies[parent];
std::set<sspair> empty;
return empty;
}
/// Getter for listing model-specific conditional dependencies (matches on the exact model)
std::set<sspair> module_functor_common::model_conditional_dependencies_exact (str model)
{
if (myModelConditionalDependencies.count(model) != 0) return myModelConditionalDependencies[model];
std::set<sspair> empty;
return empty;
}
/// Getter for listing model-specific conditional backend requirements (matches also on parents and friends)
std::set<sspair> module_functor_common::model_conditional_backend_reqs (str model)
{
str parent = find_friend_or_parent_model_in_map(model,myModelConditionalBackendReqs);
if (parent != "") return myModelConditionalBackendReqs[parent];
std::set<sspair> empty;
return empty;
}
/// Getter for listing model-specific conditional backend requirements (matches on the exact model)
std::set<sspair> module_functor_common::model_conditional_backend_reqs_exact (str model)
{
if (myModelConditionalBackendReqs.count(model) != 0) return myModelConditionalBackendReqs[model];
std::set<sspair> empty;
return empty;
}
/// Add and activate unconditional dependencies.
void module_functor_common::setDependency(str dep, str dep_type, void(*resolver)(functor*, module_functor_common*), str purpose)
{
sspair key (dep, Utils::fix_type(dep_type));
myDependencies.insert(key);
dependency_map[key] = resolver;
this->myPurpose = purpose; // only relevant for output nodes
}
/// Add conditional dependency-type pairs in advance of later conditions.
void module_functor_common::setConditionalDependency(str dep, str dep_type)
{
myConditionalDependencies[dep] = dep_type;
}
/// Retrieve full conditional dependency-type pair from conditional dependency only
sspair module_functor_common::retrieve_conditional_dep_type_pair(str dep)
{
if (myConditionalDependencies.find(dep) == myConditionalDependencies.end())
{
str errmsg = "Problem whilst attempting to set conditional dependency:";
errmsg += "\nThe conditional dependency " + dep + " appears not to have"
"\nbeen fully declared; START_CONDITIONAL_DEPENDENCY() is missing."
"\nThis is " + this->name() + " in " + this->origin() + ".";
utils_error().raise(LOCAL_INFO,errmsg);
}
return sspair(dep, myConditionalDependencies.at(dep));
}
/// Add a backend conditional dependency for multiple backend versions
void module_functor_common::setBackendConditionalDependency
(str req, str be, str ver, str dep, void(*resolver)(functor*, module_functor_common*))
{
// Split the version string and send each version to be registered
std::vector<str> versions = Utils::delimiterSplit(ver, ",");
for (std::vector<str>::iterator it = versions.begin() ; it != versions.end(); ++it)
{
setBackendConditionalDependencySingular(req, be, *it, dep, resolver);
}
}
/// Add a backend conditional dependency for a single backend version
void module_functor_common::setBackendConditionalDependencySingular
(str req, str be, str ver, str dep, void(*resolver)(functor*, module_functor_common*))
{
sspair key = retrieve_conditional_dep_type_pair(dep);
std::vector<str> quad;
if (backendreq_types.find(req) != backendreq_types.end())
{
quad.push_back(req);
quad.push_back(backendreq_types[req]);
quad.push_back(be);
quad.push_back(ver);
}
else
{
str errmsg = "Problem whilst attempting to set backend-conditional dependency:";
errmsg += "\nThe type of the backend requirement " + req + "on which the"
"\ndependency " + dep + " is conditional has not been set. This"
"\nis " + this->name() + " in " + this->origin() + ".";
utils_error().raise(LOCAL_INFO,errmsg);
}
if (myBackendConditionalDependencies.find(quad) == myBackendConditionalDependencies.end())
{
std::set<sspair> newvec;
myBackendConditionalDependencies[quad] = newvec;
}
myBackendConditionalDependencies[quad].insert(key);
dependency_map[key] = resolver;
}
/// Add a model conditional dependency for multiple models
void module_functor_common::setModelConditionalDependency
(str model, str dep, void(*resolver)(functor*, module_functor_common*))
{
// Split the model string and send each model to be registered
std::vector<str> models = Utils::delimiterSplit(model, ",");
for (std::vector<str>::iterator it = models.begin() ; it != models.end(); ++it)
{
setModelConditionalDependencySingular(*it, dep, resolver);
}
}
/// Add a model conditional dependency for a single model
void module_functor_common::setModelConditionalDependencySingular
(str model, str dep, void(*resolver)(functor*, module_functor_common*))
{
sspair key = retrieve_conditional_dep_type_pair(dep);
if (myModelConditionalDependencies.find(model) == myModelConditionalDependencies.end())
{
std::set<sspair> newvec;
myModelConditionalDependencies[model] = newvec;
}
myModelConditionalDependencies[model].insert(key);
dependency_map[key] = resolver;
// Add the model to the list of conditional models
conditionalModels.insert(model);
}
/// Add an unconditional backend requirement
/// The info gets updated later if this turns out to be conditional on a model.
void module_functor_common::setBackendReq(str group, str req, str tags, str type, void(*resolver)(functor*))
{
type = Utils::fix_type(type);
sspair key (req, type);
backendreq_types[req] = type;
myBackendReqs.insert(key);
myResolvableBackendReqs.insert(key);
if ( std::find(myGroups.begin(), myGroups.end(), group) == myGroups.end() )
{
myGroups.insert(group);
std::set<sspair> empty;
myGroupedBackendReqs[group] = empty;
}
myGroupedBackendReqs[group].insert(key);
backendreq_map[key] = resolver;
Utils::strip_parentheses(tags);
std::vector<str> v = Utils::delimiterSplit(tags, ",");
backendreq_tagmap[key] = std::set<str>(v.begin(), v.end());
backendreq_groups[key] = group;
}
/// Add a rule for activating backend requirements according to the model being scanned.
void module_functor_common::makeBackendRuleForModel(str model, str tag)
{
//Strip the tag and model strings of their parentheses
Utils::strip_parentheses(tag);
Utils::strip_parentheses(model);
//Split the tag string and sort it.
std::vector<str> v = Utils::delimiterSplit(tag, ",");
std::set<str> tags(v.begin(), v.end());
//Find all declared backend requirements that fit one of the tags within the passed tag set.
for (std::set<sspair>::iterator it = myBackendReqs.begin(); it != myBackendReqs.end(); ++it)
{
std::set<str> tagset = backendreq_tagmap[*it];
if (not Utils::is_disjoint(tags, tagset))
{
// Make each of the matching backend requirements conditional on the models passed in.
setModelConditionalBackendReq(model,it->first,it->second);
}
}
}
/// Add a model conditional backend requirement for multiple models
void module_functor_common::setModelConditionalBackendReq
(str model, str req, str type)
{
// Split the model string and send each model to be registered
std::vector<str> models = Utils::delimiterSplit(model, ",");
for (std::vector<str>::iterator it = models.begin() ; it != models.end(); ++it)
{
setModelConditionalBackendReqSingular(*it, req, type);
}
}
/// Add a model conditional backend requirement for a single model
void module_functor_common::setModelConditionalBackendReqSingular
(str model, str req, str type)
{
sspair key (req, Utils::fix_type(type));
// Remove the entry from the resolvable backend reqs list...
myResolvableBackendReqs.erase(key);
// Remove the entry from the grouped backend reqs list...
myGroupedBackendReqs.at(backendreq_groups.at(key)).erase(key);
// Check that the model is not already in the conditional backend reqs list, then add it
if (myModelConditionalBackendReqs.find(model) == myModelConditionalBackendReqs.end())
{
std::set<sspair> newvec;
myModelConditionalBackendReqs[model] = newvec;
}
myModelConditionalBackendReqs[model].insert(key);
// Add the model to the list of conditional models
conditionalModels.insert(model);
}
/// Add a rule for dictating which backends can be used to fulfill which backend requirements.
void module_functor_common::makeBackendOptionRule(str be_and_ver, str tag)
{
//Strip the tag and be-ver strings of their parentheses, then split them
Utils::strip_parentheses(tag);
Utils::strip_parentheses(be_and_ver);
std::vector<str> v = Utils::delimiterSplit(tag, ",");
std::set<str> tags(v.begin(), v.end());
std::vector<str> be_plus_versions = Utils::delimiterSplit(be_and_ver, ",");
//Die if no backend and/or no tags were given.
if (tags.empty() or be_plus_versions.empty())
{
str errmsg = "Error whilst attempting to set permitted backends:";
errmsg += "\nA BACKEND_OPTION must include a backend name and at least one tag."
"\nThis is " + this->name() + " in " + this->origin() + ".";
utils_error().raise(LOCAL_INFO,errmsg);
}
//Seperate the backend from the versions
str be = be_plus_versions.at(0);
std::vector<str> versions(be_plus_versions.begin()+1,be_plus_versions.end());
if (versions.empty()) versions.push_back("any");
//Find all declared backend requirements that fit one of the tags within the passed tag set.
for (std::set<sspair>::iterator it = myBackendReqs.begin(); it != myBackendReqs.end(); ++it)
{
std::set<str> tagset = backendreq_tagmap[*it];
if (not Utils::is_disjoint(tags, tagset))
{
// For each of the matching backend requirements, set the chosen backend-version pairs as permitted
for (std::vector<str>::iterator vit = versions.begin() ; vit != versions.end(); ++vit)
{
setPermittedBackend(it->first, be, *vit);
}
}
}
}
/// Add a single permitted backend version
void module_functor_common::setPermittedBackend(str req, str be, str ver)
{
sspair key;
if (backendreq_types.find(req) != backendreq_types.end())
{
key = std::make_pair(req, backendreq_types[req]);
}
else
{
str errmsg = "Error whilst attempting to set permitted backend:";
errmsg += "\nThe return type of the backend requirement " + req + "is not set."
"\nThis probably means the backend requirement has not been declared."
"\nThis is " + this->name() + " in " + this->origin() + ".";
utils_error().raise(LOCAL_INFO,errmsg);
}
sspair vector_entry (be, ver);
if (permitted_map.find(key) == permitted_map.end())
{
std::set<sspair> newvec;
permitted_map[key] = newvec;
}
permitted_map[key].insert(vector_entry);
}
/// Add one or more rules that force backends reqs with the same tag to always be resolved from the same backend.
void module_functor_common::makeBackendMatchingRule(str tag)
{
//Strip the tag string of any parentheses, then split it
Utils::strip_parentheses(tag);
std::vector<str> tags = Utils::delimiterSplit(tag, ",");
//Die if no tags were given.
if (tags.empty())
{
str errmsg = "Problem whilst attempting to set backend-matching rule:";
errmsg += "\nA call to FORCE_SAME_BACKEND must include at least one tag!"
"\nThis is " + this->name() + " in " + this->origin() + ".";
utils_error().raise(LOCAL_INFO,errmsg);
}
//Work with one tag at a time
for (std::vector<str>::iterator tagit = tags.begin(); tagit != tags.end(); ++tagit)
{
std::set<sspair> matches;
//Find all declared backend requirements that fit the current tag
for (std::set<sspair>::iterator it = myBackendReqs.begin(); it != myBackendReqs.end(); ++it)
{
//Test if the current tag is amongst the tags listed for the current backend requirement
std::set<str> its_tags = backendreq_tagmap[*it];
if ( std::find(its_tags.begin(), its_tags.end(), *tagit) != its_tags.end() )
{
//It is; now place the current backend requirement into the set of matches
matches.insert(*it);
}
}
//Save the matched set of backend requirements as needing to be filled using the same backend.
myForcedMatches[*tagit] = matches;
}
//Note that overlapping sets are explicitly left unmerged, as the common elements
//may or may not be activated in a given scan, making the disjointedness of the
//different sets determinable only at resolution time, not initialisation time.
}
/// Add a rule indicating that classes from a given backend must be available
void module_functor_common::setRequiredClassloader(str be, str ver, str safe_ver)
{
// Add the rule.
required_classloading_backends[be].insert(ver);
// Add a dependency on the backend's initialisation function.
sspair be_ini_quantity(be + "_" + safe_ver + "_init", "void");
if (std::find(myDependencies.begin(), myDependencies.end(), be_ini_quantity) == myDependencies.end())
{
myDependencies.insert(be_ini_quantity);
}
}
/// Indicate to the functor which backends are actually loaded and working
void module_functor_common::notifyOfBackends(std::map<str, std::set<str> > be_ver_map)
{
missing_backends.clear();
// Loop over all the backends that are needed for this functor to work.
for (auto it = required_classloading_backends.begin(); it != required_classloading_backends.end(); ++it)
{
// Check to make sure some version of the backend in question is connected.
if (be_ver_map.find(it->first) == be_ver_map.end())
{
this->myStatus = FunctorStatus::Classes_missing;
missing_backends.push_back(it->first);
}
else
{ // Loop over all the versions of the backend that are needed for this functor to work.
for (auto jt = it->second.begin(); jt != it->second.end(); ++jt)
{
std::set<str> versions = be_ver_map.at(it->first);
// Check that the specific version needed is connected.
if (versions.find(*jt) == versions.end())
{
this->myStatus = FunctorStatus::Classes_missing;
missing_backends.push_back(it->first + ", v" + *jt);
}
}
}
}
}
/// Set the ordered list of pointers to other functors that should run nested in a loop managed by this one
void module_functor_common::setNestedList (std::vector<functor*> &newNestedList)
{
if (iCanManageLoops)
{
myNestedFunctorList = newNestedList;
}
else
{
str errmsg = "This module functor is not permitted to manage";
errmsg += "\nloops, so you cannot set its nested functor list."
"\nThis is " + this->name() + " in " + this->origin() + ".";
utils_error().raise(LOCAL_INFO,errmsg);
}
}
/// Resolve a dependency using a pointer to another functor object
void module_functor_common::resolveDependency (functor* dep_functor)
{
sspair key (dep_functor->quantity());
if (std::find(myDependencies.begin(), myDependencies.end(), key) == myDependencies.end())
{
str errmsg = "Cannot resolve dependency:";
errmsg += "\nFunction " + myName + " in " + myOrigin + " does not depend on"
"\ncapability " + key.first + " with type = " + key.second + ".";
utils_error().raise(LOCAL_INFO,errmsg);
}
else
{
// resolve the dependency
if (dependency_map.find(key) != dependency_map.end()) (*dependency_map[key])(dep_functor,this);
// propagate purpose from next to next-to-output nodes
dep_functor->setPurpose(this->myPurpose);
// propagate this functor's dependees and subcaps on to the resolving functor
dep_functor->notifyOfDependee(this);
// save the pointer to the resolving functor to allow this functor to notify it of future dependees
dependency_functor_map[key] = dep_functor;
}
}
/// Notify the functor that another functor depends on it
void module_functor_common::notifyOfDependee (functor* dependent_functor)
{
// Add the dependent functor's capability-type pair to the list of dependees
myDependees.insert(dependent_functor->quantity());
// Inherit the dependent functor's own dependees
for (const sspair& q : *(dependent_functor->getDependees())) { myDependees.insert(q); }
// Inherit the dependent functor's subcaps
notifyOfSubCaps(*(dependent_functor->getSubCaps()));
// Notify all functors on which this one depends that they also now have a new dependent
for (auto entry : dependency_functor_map) entry.second->notifyOfDependee(dependent_functor);
}
/// Set this functor's loop manager (if it has one)
void module_functor_common::resolveLoopManager (functor* dep_functor)
{
if (dep_functor->capability() != myLoopManagerCapability or not dep_functor->canBeLoopManager())
{
utils_error().raise(LOCAL_INFO, "Cannot set loop manager for nested functor:\n"
"Function " + myName + " in " + myOrigin + " does not need a loop manager with\n"
"capability " + dep_functor->capability() + ".");
}
// Do type checking only if the need for a manger was declared with a specific type
if (myLoopManagerType != "any")
{
if (dep_functor->type() != myLoopManagerType)
{
utils_error().raise(LOCAL_INFO, "Cannot set loop manager for nested functor:\n"
"Function " + myName + " in " + myOrigin + " requires a manager with\n"
"type " + dep_functor->type() + ".");
}
resolveDependency(dep_functor);
}
myLoopManager = dep_functor;
}
/// Resolve a backend requirement using a pointer to another functor object
void module_functor_common::resolveBackendReq (functor* be_functor)
{
sspair key (be_functor->quantity());
//First make sure that the proposed backend function fulfills a known requirement of the module function.
if (backendreq_map.find(key) != backendreq_map.end())
{
sspair proposal (be_functor->origin(), be_functor->version()); //Proposed backend-version pair
sspair generic_proposal (be_functor->origin(), "any"); //Proposed backend, any version
if ( //Check for a condition under which the proposed backend function is an acceptable fit for this requirement.
//Check whether there are have been any permitted backends stated for this requirement (if not, anything goes).
(permitted_map.find(key) == permitted_map.end()) ||
//Iterate over the vector of backend-version pairs for this requirement to see if all versions of the
//proposed backend have been explicitly permitted.
(std::find(permitted_map[key].begin(), permitted_map[key].end(), generic_proposal) != permitted_map[key].end()) ||
//Iterate over the vector of backend-version pairs again to see if the specific backend version
//proposed had been explicitly permitted.
(std::find(permitted_map[key].begin(), permitted_map[key].end(), proposal) != permitted_map[key].end()) )
{
//One of the conditions was met, so do the resolution.
(*backendreq_map[key])(be_functor);
//Set this backend functor's status to active.
be_functor->setStatus(FunctorStatus::Active);
//If this is also the condition under which any backend-conditional dependencies should be activated, do it.
std::set<sspair> deps_to_activate = backend_conditional_dependencies(be_functor);
for (std::set<sspair>::iterator it = deps_to_activate.begin() ; it != deps_to_activate.end(); ++it)
{
myDependencies.insert(*it);
}
// Add a dependency on the initialisation function of the backend that this backend function hails from (if not done already).
sspair be_ini_quantity(be_functor->origin() + "_" + be_functor->safe_version() + "_init", "void");
if (std::find(myDependencies.begin(), myDependencies.end(), be_ini_quantity) == myDependencies.end())
{
myDependencies.insert(be_ini_quantity);
}
//Check if this backend requirement is part of a group.
str group = backendreq_groups[key];
if (group != "none")
{
//If it is part of a group, make sure that group has actually been declared.
if (chosenReqsFromGroups.find(group) != chosenReqsFromGroups.end() )
{
//If it is part of a group, make sure another backend requirement from the same group has not already been activated.
if (chosenReqsFromGroups[group] == "none")
{
//If not, then this this specific backend requirement is now the active one within its group.
chosenReqsFromGroups[group] = key.first;
}
else //This backend requirement is not the first from its group to be resolved.
{
str errmsg = "Cannot resolve backend requirement in group " + group + ":";
errmsg += "\nFunction " + myName + " in " + myOrigin + " has already had backend "
"\nrequirement " + chosenReqsFromGroups[group] + " from the same group filled.";
utils_error().raise(LOCAL_INFO,errmsg);
}
}
else //This backend requirement is part of group that was not declared.
{
str errmsg = "Cannot resolve backend requirement in group " + group + ":";
errmsg += "\nThis group has not been declared. Please add"
"\n BACKEND_GROUP("+group+")"
"\nTo the rollcall declaration of " + myName + " in " + myOrigin + ".";
utils_error().raise(LOCAL_INFO,errmsg);
}
}
}
else //The proposed backend function is not an acceptable fit for this requirement.
{
str errmsg = "Cannot resolve backend requirement:";
errmsg += "\nBackend capability " + key.first + " with type " + key.second +
"\nrequired by function " + myName + " in " + myOrigin + " is not permitted"
"\nto use " + proposal.first + ", version " + proposal.second + ".";
utils_error().raise(LOCAL_INFO,errmsg);
}
}
else //The proposed backend function does not fulfill any known requirement of the module function.
{
str errmsg = "Cannot resolve backend requirement:";
errmsg += "\nFunction " + myName + " in " + myOrigin + " does not require"
"\nbackend capability " + key.first + " with type " + key.second + ".";
utils_error().raise(LOCAL_INFO,errmsg);
}
}
/// Construct the list of known models only if it doesn't yet exist
void module_functor_common::fill_activeModelFlags()
{
// Construct the list of known models only if it doesn't yet exist
if (activeModelFlags.empty())
{
// First get all the explicitly allowed models.
for (auto it = allowedModels.begin(); it != allowedModels.end(); ++it) { activeModelFlags[*it] = false; }
// Next get all the models in groups
for (auto it = modelGroups.begin(); it != modelGroups.end(); ++it)
{ for (auto jt = it->second.begin(); jt != it->second.end(); ++jt) { activeModelFlags[*jt] = false; } }
// Next get all the models mentioned in conditional dependencies and backend reqs
for (auto it = myModelConditionalDependencies.begin(); it != myModelConditionalDependencies.end(); ++it) { activeModelFlags[it->first] = false; }
for (auto it = myModelConditionalBackendReqs.begin(); it != myModelConditionalBackendReqs.end(); ++it) { activeModelFlags[it->first] = false; }
}
}
/// Notify the functor that a certain model is being scanned, so that it can activate its dependencies and backend reqs accordingly.
void module_functor_common::notifyOfModel(str model)
{
// If activeModels hasn't been populated yet, make sure it is.
fill_activeModelFlags();
// Now activate the flags for the models that are being used.
for (auto it = activeModelFlags.begin(); it != activeModelFlags.end(); ++it)
{
str activation_candidate = it->first;
if (myClaw->model_exists(activation_candidate))
{
if (myClaw->downstream_of(model, activation_candidate))
{
// Found an activation candidate that the model being scanned can be cast to.
// Assume for now that the candidate will indeed be activated.
it->second = true;
// Compare with models that have already been activated, to avoid activating multiple models of the same lineage.
for (auto jt = activeModelFlags.begin(); jt != activeModelFlags.end(); ++jt)
{
str active_model = jt->first;
if (activation_candidate != active_model and myClaw->model_exists(active_model) and jt->second)
{
// If the already active model can be upcast to the activation candidate, abort the activiation of the candidate.
if (myClaw->downstream_of(active_model, activation_candidate)) it->second = false;
// If the candidate can be upcast to the already active model, activate the candidate instead of the already active model.
if (myClaw->downstream_of(activation_candidate, active_model)) jt->second = false;
if (verbose)
{
cout << "model: " << model << " " << "model to be activated: " << activation_candidate << "(" << it->second << ") active model: " << active_model << "(" << jt->second << ")" << endl;
cout << "active model lives below:" << myClaw->downstream_of(active_model, activation_candidate) << endl;
cout << "activation candidate lives below:" << myClaw->downstream_of(activation_candidate, active_model) << endl;
}
}
}
if (verbose) cout << "Activate candidate " << activation_candidate << "?" << it->second << endl;
}
}
}
// If this model fits any conditional dependencies (or descended from one that can be interpreted as one that fits any), then activate them.
std::set<sspair> deps_to_activate = model_conditional_dependencies(model);
for (std::set<sspair>::iterator it = deps_to_activate.begin() ; it != deps_to_activate.end(); ++it)
{
myDependencies.insert(*it);
}
// If this model fits any conditional backend requirements (or descended from one that can be interpreted as one that fits any), then activate them.
std::set<sspair> backend_reqs_to_activate = model_conditional_backend_reqs(model);
for (std::set<sspair>::iterator it = backend_reqs_to_activate.begin() ; it != backend_reqs_to_activate.end(); ++it)
{
if (verbose) cout << "req: " << it->first << " " << it->second << endl;
myResolvableBackendReqs.insert(*it);
myGroupedBackendReqs.at(backendreq_groups.at(*it)).insert(*it);
}
}
// Initialise the memory of this functor.
void module_functor_common::init_memory()
{
int n = (iRunNested ? globlMaxThreads : 1);
// Reserve enough space to hold as many timing points and recalculation flags as there are slots (threads) allowed
if(start==NULL)
{
#pragma omp critical(module_functor_common_init_memory_start)
{
if(start==NULL) start = new std::chrono::time_point<std::chrono::system_clock>[n];
}
}
if(end==NULL)
{
#pragma omp critical(module_functor_common_init_memory_end)
{
if(end==NULL) end = new std::chrono::time_point<std::chrono::system_clock>[n];
}
}
if(needs_recalculating==NULL)
{
#pragma omp critical(module_functor_common_init_memory_needs_recalculating)
{
if(needs_recalculating==NULL)
{
needs_recalculating = new bool[n];
std::fill(needs_recalculating, needs_recalculating+n, true);
}
}
}
if(already_printed==NULL)
{
#pragma omp critical(module_functor_common_init_memory_already_printed)
{
if(already_printed==NULL)
{
already_printed = new bool[n];
std::fill(already_printed, already_printed+n, false);
}
}
}
if(already_printed_timing==NULL)
{
#pragma omp critical(module_functor_common_init_memory_already_printed_timing)
{
if(already_printed_timing==NULL)
{
already_printed_timing = new bool[n];
std::fill(already_printed_timing, already_printed_timing+n, false);
}
}
}
}
/// Do pre-calculate timing things
void module_functor_common::startTiming(int thread_num)
{
start[thread_num] = std::chrono::system_clock::now();
}
/// Do post-calculate timing things
void module_functor_common::finishTiming(int thread_num)
{
end[thread_num] = std::chrono::system_clock::now();
std::chrono::duration<double> runtime = end[thread_num] - start[thread_num];
#pragma omp critical(module_functor_common_finishTiming)
{
runtime_average = runtime_average*(1-fadeRate) + fadeRate*runtime.count();
pInvalidation = pInvalidation*(1-fadeRate) + fadeRate*FUNCTORS_BASE_INVALIDATION_RATE;
}
needs_recalculating[thread_num] = false;
}
/// Class methods for actual module functors for TYPE=void.
/// Constructor
module_functor<void>::module_functor(void (*inputFunction)(),
str func_name,
str func_capability,
str result_type,
str origin_name,
Models::ModelFunctorClaw &claw)
: module_functor_common(func_name, func_capability, result_type, origin_name, claw),
myFunction (inputFunction) {}
/// Calculate method
/// The "void" specialisation can potentially manage loops,
/// so there are some extra switches in here to let the signal
/// handler know that it needs to run in threadsafe mode during
/// execution of this functor.
void module_functor<void>::calculate()
{
if (myStatus == FunctorStatus::Classes_missing) // Do an explicit status check to hold standalone writers' hands
{
std::ostringstream ss;
ss << "Sorry, the function " << origin() << "::" << name()
<< " cannot be used" << endl << "because it requires classes from a backend that you do not have installed."
<< endl << "Missing backends: ";
for (auto it = missing_backends.begin(); it != missing_backends.end(); ++it) ss << endl << " " << *it;
backend_error().raise(LOCAL_INFO, ss.str());
}
else if (myStatus == FunctorStatus::Backend_missing)
{
std::ostringstream ss;
ss << "Sorry, the backend initialisation function " << name()
<< " cannot be used" << endl << "because it initialises a backend that you do not have installed!";
backend_error().raise(LOCAL_INFO, ss.str());
}
boost::io::ios_flags_saver ifs(cout); // Don't allow module functions to change the output precision of cout
int thread_num = omp_get_thread_num();
fill_activeModelFlags(); // If activeModels hasn't been populated yet, make sure it is.
init_memory(); // Init memory if this is the first run through.
if (needs_recalculating[thread_num])
{
entering_multithreaded_region();
logger().entering_module(myLogTag);
this->startTiming(thread_num);
try
{
this->myFunction();
}
catch (invalid_point_exception& e)
{
if (not point_exception_raised) acknowledgeInvalidation(e);
if (omp_get_level()==0) // If not in an OpenMP parallel block, throw onwards
{
this->finishTiming(thread_num);
leaving_multithreaded_region();
throw(e);
}
}
this->finishTiming(thread_num);
logger().leaving_module();
leaving_multithreaded_region();
}
}
/// Blank print methods
#ifndef NO_PRINTERS
void module_functor<void>::print(Printers::BasePrinter*, const int, int) {}
void module_functor<void>::print(Printers::BasePrinter*, const int) {}
#endif
/// @{ Model functor class method definitions
/// Constructor
model_functor::model_functor(void (*inputFunction)(ModelParameters &),
str func_name,
str func_capability,
str result_type,
str origin_name,
Models::ModelFunctorClaw &claw)
: module_functor<ModelParameters>(inputFunction, func_name, func_capability, result_type, origin_name, claw)
{
init_memory();
}
/// Function for adding a new parameter to the map inside the ModelParameters object
void model_functor::addParameter(str parname)
{
myValue->_definePar(parname);
}
/// Function for setting the model name for a ModelParameters object. Mainly for better error messages.
void model_functor::setModelName(str model_name)
{
myValue->setModelName(model_name);
}
/// Function for handing over parameter identities to another model_functor
void model_functor::donateParameters(model_functor &receiver)
{
for(std::map<std::string,double>::const_iterator it = myValue->begin();
it != myValue->end();
it++)
{
receiver.addParameter(it->first);
}
/// Copy the model name as well
receiver.setModelName(myValue->getModelName());
}
/// @}
/// @{ Primary model functor class method definitions
/// Constructor
primary_model_functor::primary_model_functor(void (*inputFunction)(ModelParameters &),
str func_name,
str func_capability,
str result_type,
str origin_name,
Models::ModelFunctorClaw &claw)
: model_functor(inputFunction, func_name, func_capability, result_type, origin_name, claw) {}
/// Functor contents raw pointer "get" function
/// Returns a raw pointer to myValue, so that the contents may be
/// modified (intended for setting parameter values in primary
/// ModelParameters objects)
ModelParameters* primary_model_functor::getcontentsPtr()
{
return myValue;
}
/// @}
}
Updated on 2024-07-18 at 13:53:34 +0000