diff --git a/examples/agent-functionalities/agent-functionalities.cpp b/examples/agent-functionalities/agent-functionalities.cpp index 2da67b8..8f111db 100644 --- a/examples/agent-functionalities/agent-functionalities.cpp +++ b/examples/agent-functionalities/agent-functionalities.cpp @@ -1,170 +1,211 @@ //===-- examples/agent-functionalities/agent-functionalities.cpp *-- C++-*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file examples/agent-functionalities/agent-functionalities.cpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief A simple example on defining \c rosa::Agent instances using /// \c rosa::agent::Functionality object as components. /// //===----------------------------------------------------------------------===// #include "rosa/agent/Abstraction.hpp" +#include "rosa/agent/Confidence.hpp" #include "rosa/agent/FunctionAbstractions.hpp" #include "rosa/agent/RangeConfidence.hpp" -#include "rosa/agent/Confidence.hpp" #include "rosa/config/version.h" #include "rosa/core/Agent.hpp" #include "rosa/core/MessagingSystem.hpp" #include "rosa/support/log.h" #include "rosa/support/terminal_colors.h" #include using namespace rosa; using namespace rosa::agent; using namespace rosa::terminal; /// A dummy wrapper for testing \c rosa::MessagingSystem. /// /// \note Since we test \c rosa::MessagingSystem directly here, we need to get /// access to its protected members. That we do by imitating to be a decent /// subclass of \c rosa::MessagingSystem, while calling protected member /// functions on an object of a type from which we actually don't inherit. struct SystemTester : protected MessagingSystem { template static AgentHandle createMyAgent(MessagingSystem *S, const std::string &Name, Funs &&... Fs) { return ((SystemTester *)S)->createAgent(Name, std::move(Fs)...); } static void destroyMyAgent(MessagingSystem *S, const AgentHandle &H) { ((SystemTester *)S)->destroyUnit(unwrapAgent(H)); } }; /// A special \c rosa::Agent with its own state. class MyAgent : public Agent { public: using Tick = AtomConstant; private: enum class Categories { Bad, Normal, Good }; static const std::map CategoryNames; - History H; + StaticLengthHistory H; Confidence C; RangeAbstraction A; PartialFunction L; - RangeConfidence RCL; - RangeConfidence RCS; + RangeConfidence RCL; + RangeConfidence RCS; public: void handler(Tick, uint8_t V) noexcept { // Record \p V to the \c rosa::agent::History, then print state info. H << V; ASSERT(H.entry() == V); // Sanity check. LOG_INFO_STREAM << "\nNext value: " << PRINTABLE(V) << ", confidence: " << C(H) << ", category: " << CategoryNames.at(A(H.entry())) << ", partial: " << int(L(H.entry())) << ", range-confidence-linear: "; - std::vector res_lin = RCL(H.entry()); - for (auto i : res_lin){ - LOG_INFO_STREAM << " " << i; + + std::map ResLin = RCL(H.entry()); + for (auto Con : ResLin) { + LOG_INFO_STREAM << " " << CategoryNames.at(Con.first) << " " << Con.second + << ","; } - LOG_INFO_STREAM << ", range-confidence-sine: "; - std::vector res_sine = RCS(H.entry()); - for (auto i : res_sine){ - LOG_INFO_STREAM << " " << i; + LOG_INFO_STREAM << " range-confidence-sine: "; + std::map ResSine = RCS(H.entry()); + for (auto Con : ResSine) { + LOG_INFO_STREAM << " " << CategoryNames.at(Con.first) << " " << Con.second + << ","; } LOG_INFO_STREAM << '\n'; } MyAgent(const AtomValue Kind, const rosa::id_t Id, const std::string &Name, MessagingSystem &S) : Agent(Kind, Id, Name, S, THISMEMBER(handler)), H(), C(5, 20, 1), A({{{(uint8_t)10, (uint8_t)14}, Categories::Normal}, {{(uint8_t)15, (uint8_t)17}, Categories::Good}, {{(uint8_t)18, (uint8_t)19}, Categories::Normal}}, Categories::Bad), L({{{0, 2}, std::make_shared>(0, 1)}, {{2, 4}, std::make_shared>(2, 0)}, {{4, 6}, std::make_shared>(6, -1)}}, 0), - RCL({ - PartialFunction({ - {{0, 3}, std::make_shared>(0, 1.0/3)}, - {{3, 6}, std::make_shared>(1, 0)}, - {{6, 9}, std::make_shared>(3.0, -1.0/3)}, - },0), - PartialFunction({ - {{6, 9}, std::make_shared>(-2, 1.0/3)}, - {{9, 12}, std::make_shared>(1, 0)}, - {{12, 15}, std::make_shared>(5, -1.0/3)}, - },0), - PartialFunction({ - {{12, 15}, std::make_shared>(-4, 1.0/3)}, - {{15, 18}, std::make_shared>(1, 0)}, - {{18, 21}, std::make_shared>(7, -1.0/3)}, - },0) - }), - RCS({ - PartialFunction({ - {{0, 3}, std::make_shared> - (M_PI/3, 0.5, -M_PI/2, 0.5)}, - {{3, 6}, std::make_shared>(1, 0)}, - {{6, 9}, std::make_shared> - (M_PI/3, 0.5, -M_PI/2 + 3, 0.5)}, - },0), - PartialFunction({ - {{6, 9}, std::make_shared> - (M_PI/3, 0.5, -M_PI/2, 0.5)}, - {{9, 12}, std::make_shared>(1, 0)}, - {{12, 15}, std::make_shared> - (M_PI/3, 0.5, -M_PI/2 + 3, 0.5)}, - },0), - PartialFunction({ - {{12, 15}, std::make_shared> - (M_PI/3, 0.5, -M_PI/2, 0.5)}, - {{15, 18}, std::make_shared>(1, 0)}, - {{18, 21}, std::make_shared> - (M_PI/3, 0.5, -M_PI/2 + 3, 0.5)}, - },0) - }){} + RCL({{Categories::Bad, + PartialFunction( + { + {{0, 3}, + std::make_shared>(0, + 1.0 / 3)}, + {{3, 6}, + std::make_shared>(1, 0)}, + {{6, 9}, + std::make_shared>( + 3.0, -1.0 / 3)}, + }, + 0)}, + {Categories::Normal, + PartialFunction( + { + {{6, 9}, + std::make_shared>(-2, + 1.0 / 3)}, + {{9, 12}, + std::make_shared>(1, 0)}, + {{12, 15}, + std::make_shared>( + 5, -1.0 / 3)}, + }, + 0)}, + {Categories::Good, + PartialFunction( + { + {{12, 15}, + std::make_shared>(-4, + 1.0 / 3)}, + {{15, 18}, + std::make_shared>(1, 0)}, + {{18, 21}, + std::make_shared>( + 7, -1.0 / 3)}, + }, + 0)}}), + RCS({{Categories::Bad, + PartialFunction( + { + {{0, 3}, + std::make_shared>( + M_PI / 3, 0.5, -M_PI / 2, 0.5)}, + {{3, 6}, + std::make_shared>(1, 0)}, + {{6, 9}, + std::make_shared>( + M_PI / 3, 0.5, -M_PI / 2 + 3, 0.5)}, + }, + 0)}, + {Categories::Normal, + PartialFunction( + { + {{6, 9}, + std::make_shared>( + M_PI / 3, 0.5, -M_PI / 2, 0.5)}, + {{9, 12}, + std::make_shared>(1, 0)}, + {{12, 15}, + std::make_shared>( + M_PI / 3, 0.5, -M_PI / 2 + 3, 0.5)}, + }, + 0)}, + {Categories::Good, + PartialFunction( + { + {{12, 15}, + std::make_shared>( + M_PI / 3, 0.5, -M_PI / 2, 0.5)}, + {{15, 18}, + std::make_shared>(1, 0)}, + {{18, 21}, + std::make_shared>( + M_PI / 3, 0.5, -M_PI / 2 + 3, 0.5)}, + }, + 0)}}, + true) {} }; const std::map MyAgent::CategoryNames{ {Categories::Bad, "Bad"}, {Categories::Normal, "Normal"}, {Categories::Good, "Good"}}; int main(void) { - LOG_INFO_STREAM << library_string() << " -- " << Color::Red - << "agent-functionalities example" << Color::Default - << '\n'; + LOG_INFO_STREAM << library_string() << " -- " << Color::Red + << "agent-functionalities example" << Color::Default << '\n'; std::unique_ptr S = MessagingSystem::createSystem("Sys"); MessagingSystem *SP = S.get(); AgentHandle A = SystemTester::createMyAgent(SP, "MyAgent"); - std::vector Vs{0, 1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 13, - 15, 14, 15, 16, 19, 20, 21}; + std::vector Vs{0, 1, 2, 3, 4, 5, 6, 7, 9, 10, + 11, 13, 15, 14, 15, 16, 19, 20, 21}; for (auto I = Vs.begin(); I != Vs.end(); ++I) { A.send(MyAgent::Tick::Value, *I); } SystemTester::destroyMyAgent(SP, A); return 0; } diff --git a/include/rosa/agent/Abstraction.hpp b/include/rosa/agent/Abstraction.hpp index fa9e03e..b44b2de 100644 --- a/include/rosa/agent/Abstraction.hpp +++ b/include/rosa/agent/Abstraction.hpp @@ -1,193 +1,238 @@ //===-- rosa/agent/Abstraction.hpp ------------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file rosa/agent/Abstraction.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Definition of *abstraction* *functionality*. /// //===----------------------------------------------------------------------===// #ifndef ROSA_AGENT_ABSTRACTION_HPP #define ROSA_AGENT_ABSTRACTION_HPP #include "rosa/agent/Functionality.h" #include "rosa/support/debug.hpp" #include #include namespace rosa { namespace agent { /// Abstracts values from a type to another one. /// /// \tparam T type to abstract from /// \tparam A type to abstract to template class Abstraction : public Functionality { protected: /// Value to abstract to by default. const A Default; - public: /// Creates an instance. /// /// \param Default value to abstract to by default Abstraction(const A Default) noexcept : Default(Default) {} /// Destroys \p this object. ~Abstraction(void) = default; + /// Checks wether the Abstraction evaluates to default at the given position + /// + /// \param V the value at which to check if the function falls back to it's + /// default value. + /// \return true, the default implementation always falls back to the default + /// value + virtual bool isDefaultAt(const T &V) const noexcept{ + (void)V; + return true; + } + /// Abstracts a value from type \p T to type \p A. /// /// \note The default implementation always returns /// \c rosa::agent::Abstraction::Default, hence the actual argument is /// ignored. /// /// \return the abstracted value virtual A operator()(const T &) const noexcept { return Default; } }; /// Implements \c rosa::agent::Abstraction as a \c std::map from a type to /// another one. /// /// \note This implementation is supposed to be used to abstract between /// enumeration types, which is statically enforced. /// /// \tparam T type to abstract from /// \tparam A type to abstract to template class MapAbstraction : public Abstraction, private std::map { // Make sure the actual type arguments are enumerations. STATIC_ASSERT((std::is_enum::value && std::is_enum::value), "mapping not enumerations"); // Bringing into scope inherited members. using Abstraction::Default; using std::map::end; using std::map::find; public: /// Creates an instance by initializing the underlying \c std::map. /// /// \param Map the mapping to do abstraction according to /// \param Default value to abstract to by default MapAbstraction(const std::map &Map, const A Default) noexcept : Abstraction(Default), std::map(Map) {} /// Destroys \p this object. ~MapAbstraction(void) = default; + /// Checks wether the Abstraction evaluates to default at the given position + /// + /// \param V the value at which to check if the function falls back to it's + /// default value. + /// \return true if the Abstraction falls back to the default value + bool isDefaultAt(const T &V) const noexcept override { + const auto I = find(V); + return I == end() ? true : false; + } + /// Abstracts a value from type \p T to type \p A based on the set mapping. /// /// Results in the value associated by the set mapping to the argument, or /// \c rosa::agent::MapAbstraction::Default if the actual argument is not /// associated with anything by the set mapping. /// /// \param V value to abstract /// /// \return the abstracted value based on the set mapping A operator()(const T &V) const noexcept override { const auto I = find(V); return I == end() ? Default : *I; } }; /// Implements \c rosa::agent::Abstraction as a \c std::map from ranges of a /// type to values of another type. /// /// \note This implementation is supposed to be used to abstract ranges of /// arithmetic types into enumerations, which is statically enforced. /// /// \invariant The keys in the underlying \c std::map define valid ranges /// such that `first <= second` and there are no overlapping ranges defined by /// the keys. /// /// \tparam T type to abstract from /// \tparam A type to abstract to template class RangeAbstraction : public Abstraction, private std::map, A> { // Make sure the actual type arguments are matching our expectations. STATIC_ASSERT((std::is_arithmetic::value), "abstracting not arithmetic"); /// \todo check if this compiles with the definition of abstractions as /// self-aware properties //STATIC_ASSERT((std::is_enum::value), "abstracting not to enumeration"); // Bringing into scope inherited members. using Abstraction::Default; using std::map, A>::begin; using std::map, A>::end; using std::map, A>::find; public: /// Creates an instance by Initializing the unserlying \c std::map. /// /// \param Map the mapping to do abstraction according to /// \param Default value to abstract to by default /// /// \pre Each key defines a valid range such that `first <= second` and /// there are no overlapping ranges defined by the keys. RangeAbstraction(const std::map, A> &Map, const A &Default) : Abstraction(Default), std::map, A>(Map) { // Sanity check. ASSERT(std::all_of( begin(), end(), [this](const std::pair, A> &P) { return P.first.first <= P.first.second && std::all_of(++find(P.first), end(), [&P](const std::pair, A> &R) { // \note Values in \c Map are sorted. return P.first.first < P.first.second && P.first.second <= R.first.first || P.first.first == P.first.second && P.first.second < R.first.first; }); })); } /// Destroys \p this object. ~RangeAbstraction(void) = default; + /// Checks wether the Abstraction evaluates to default at the given position + /// + /// \param V the value at which to check if the function falls back to it's + /// default value. + /// \return true if the Abstraction falls back to the default value + bool isDefaultAt(const T &V) const noexcept override { + auto I = begin(); + bool Found = false; // Indicates if \c I refers to a matching range. + bool Failed = false; // Indicates if it is pointless to continue searching. + while (!Found && !Failed && I != end()) { + if (V < I->first.first) { + // No match so far and \p V is below the next range, never will match. + // \note Keys are sorted in the map. + return true; + } else if (I->first.first <= V && V < I->first.second) { + // Matching range found. + return false; + } else { + // Cannot conclude in this step, move to the next range. + ++I; + } + } + return true; + } + /// Abstracts a value from type \p T to type \p A based on the set mapping. /// /// Results in the value associated by the set mapping to the argument, or /// \c rosa::agent::RangeAbstraction::Default if the actual argument is not /// included in any of the ranges in the set mapping. /// /// \param V value to abstract /// /// \return the abstracted value based on the set mapping A operator()(const T &V) const noexcept override { auto I = begin(); bool Found = false; // Indicates if \c I refers to a matching range. bool Failed = false; // Indicates if it is pointless to continue searching. while (!Found && !Failed && I != end()) { if (V < I->first.first) { // No match so far and \p V is below the next range, never will match. // \note Keys are sorted in the map. Failed = true; } else if (I->first.first <= V && V < I->first.second) { // Matching range found. Found = true; } else { // Cannot conclude in this step, move to the next range. ++I; } } ASSERT(!Found || I != end()); return Found ? I->second : Default; } }; } // End namespace agent } // End namespace rosa #endif // ROSA_AGENT_ABSTRACTION_HPP diff --git a/include/rosa/agent/Confidence.hpp b/include/rosa/agent/Confidence.hpp index 3d07606..35b7496 100644 --- a/include/rosa/agent/Confidence.hpp +++ b/include/rosa/agent/Confidence.hpp @@ -1,204 +1,202 @@ //===-- rosa/agent/Confidence.hpp -------------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file rosa/agent/Confidence.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Definition of *confidence* *functionality*. /// //===----------------------------------------------------------------------===// #ifndef ROSA_AGENT_CONFIDENCE_HPP #define ROSA_AGENT_CONFIDENCE_HPP #include "rosa/agent/History.hpp" #include "rosa/support/debug.hpp" #include namespace rosa { namespace agent { /// Confidence validator. /// /// Checks the plausibility of given values by validating if a valid region /// contains them. It also capable of checking consistency by validating the /// rate of change recorded by a \c rosa::agent::History object against a /// maximal absolute valid rate of change. /// /// \tparam T type of values to validate /// /// \note The template is defined only for arithmetic types. /// /// \note The lower bound is inclusive and the upper bound is exclusive. /// /// \invariant The bounds are defined in a meaningful way:\code /// LowerBound <= UpperBound /// \endcode template class Confidence : public Functionality { // Make sure the actual type argument is an arithmetic type. STATIC_ASSERT(std::is_arithmetic::value, "not arithmetic Confidence"); public: /// Unsigned type corresponding to \p T. using UT = unsigned_t; /// The minimal value of type \p T. /// \note Not exist \c V of type \p T such that `V < Min`. static constexpr T Min = std::is_integral::value ? std::numeric_limits::min() : std::numeric_limits::lowest(); /// The maximal value of type \p T. /// \note Not exist \c V of type \p T such that `V > Max`. static constexpr T Max = (std::is_integral::value || !std::numeric_limits::has_infinity) ? std::numeric_limits::max() : std::numeric_limits::infinity(); /// The maximal value of type \c UT. /// \note Not exist \c V of type \c UT such that `V > UnsignedMax`. static constexpr UT UnsignedMax = (std::is_integral::value || !std::numeric_limits::has_infinity) ? std::numeric_limits::max() : std::numeric_limits::infinity(); private: /// The inclusive lower bound for plausibility check. T LowerBound; /// The exclusive upper bound for plausibility check. T UpperBound; /// The maximal absolute rate of change for consistency check. UT ChangeRate; public: /// Creates an instance by setting the validator variables. /// /// \param LowerBound the lower bound for plausability check /// \param UpperBound the upper bound for plausability check /// \param ChangeRate maximal absolute rate of change for consistency check /// /// \pre The bounds are defined in a meaningful way:\code /// LowerBound <= UpperBound /// \endcode Confidence(const T LowerBound = Min, const T UpperBound = Max, const UT ChangeRate = UnsignedMax) noexcept - : LowerBound(LowerBound), - UpperBound(UpperBound), - ChangeRate(ChangeRate) { + : LowerBound(LowerBound), UpperBound(UpperBound), ChangeRate(ChangeRate) { // Make sure Confidence is created in a valid state. if (LowerBound > UpperBound) { ROSA_CRITICAL("Confidence with LowerBound higher than UpperBound"); } } /// Destroys \p this object. ~Confidence(void) = default; /// Gives a snapshot of the current state of the validator variables. /// /// \param [out] LowerBound to copy \c rosa::agent::Confidence::LowerBound /// into /// \param [out] UpperBound to copy \c rosa::agent::Confidence::UpperBound /// into /// \param [out] ChangeRate to copy \c rosa::agent::Confidence::ChangeRate /// into void getParameters(T &LowerBound, T &UpperBound, UT &ChangeRate) const noexcept { // Copy members to the given references. LowerBound = this->LowerBound; UpperBound = this->UpperBound; ChangeRate = this->ChangeRate; } /// Sets the lower bound for plausability check. /// /// Beyond setting the lower bound, the function also adjusts the upper bound /// to the given lower bound if the new lower bound would be higher than the /// upper bound. /// /// \param LowerBound the new lower bound to set void setLowerBound(const T LowerBound) noexcept { // Adjust UpperBound if necessary, then set LowerBound. if (UpperBound < LowerBound) { UpperBound = LowerBound; } this->LowerBound = LowerBound; } /// Sets the upper bound for plausability check. /// /// Beyond setting the upper bound, the function also adjusts the lower bound /// to the given upper bound if the new upper bound would be lower than the /// lower bound. /// /// \param UpperBound the new upper bound to set void setUpperBound(const T UpperBound) noexcept { // Adjust LowerBound if necessary, then set UpperBound. if (UpperBound < LowerBound) { LowerBound = UpperBound; } this->UpperBound = UpperBound; } /// Sets the maximal rate of change for consistency check. /// /// \param ChangeRate the new rate of change to set void setChangeRate(const UT ChangeRate) noexcept { // Set ChangeRate. this->ChangeRate = ChangeRate; } /// Tells the binary confidence on the plausibility of a value. /// /// \param V value to check /// /// \return whether \c V is within the range defined by /// \c rosa::agent::Confidence::LowerBound and /// \c rosa::agent::Confidence::UpperBound. bool operator()(const T V) const noexcept { // Return if \c V is plausible. return LowerBound <= V && V < UpperBound; } /// Tells the binary confidence on the plausibility and consistency of the /// last value recorded by a \c rosa::agent::History instance. /// /// Consistency of the last value is checked by validating the difference with /// its preceding entry. /// /// \note The \c rosa::agent::History instance needs to store values of type /// \p T. /// /// \note An empty \c rosa::agent::History instance results in full /// confidence. /// /// \tparam N number of values \p H is able to store /// \tparam P retention policy followed by \p H when capacity is reached /// /// \param H *history* whose last entry to check template - bool operator()(const History &H) const noexcept { + bool operator()(const StaticLengthHistory &H) const noexcept { if (H.empty()) { // No entry to validate. return true; } else { // Validate the last entry and the one step average absolute difference. return (*this)(H.entry()) && H.averageAbsDiff(1) <= ChangeRate; } } }; } // End namespace agent } // End namespace rosa #endif // ROSA_AGENT_CONFIDENCE_HPP diff --git a/include/rosa/agent/FunctionAbstractions.hpp b/include/rosa/agent/FunctionAbstractions.hpp index 1cd0d10..59200de 100644 --- a/include/rosa/agent/FunctionAbstractions.hpp +++ b/include/rosa/agent/FunctionAbstractions.hpp @@ -1,175 +1,222 @@ //===-- rosa/agent/FunctionAbstractions.hpp ---------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file rosa/agent/FunctionAbstractions.hpp /// /// \author Benedikt Tutzer (benedikt.tutzer@tuwien.ac.at) /// /// \date 2019 /// /// \brief Definition of *FunctionAbstractions* *functionality*. /// //===----------------------------------------------------------------------===// #ifndef ROSA_AGENT_FUNCTIONABSTRACTIONS_HPP #define ROSA_AGENT_FUNCTIONABSTRACTIONS_HPP -#include "rosa/agent/Functionality.h" #include "rosa/agent/Abstraction.hpp" +#include "rosa/agent/Functionality.h" #include "rosa/support/debug.hpp" #include -#include #include #include +#include namespace rosa { namespace agent { -/// Evaluates a linear function at a given value. +/// Implements \c rosa::agent::Abstraction as a linear function, +/// y = Coefficient * X + Intercept. +/// +/// \note This implementation is supposed to be used to represent a linear +/// function from an arithmetic domain to an arithmetic range. This is enforced +/// statically. /// -/// \tparam T type of the functions domain -/// \tparam A type of the functions range -template class LinearFunction : - public Abstraction{ +/// \tparam D type of the functions domain +/// \tparam R type of the functions range +template +class LinearFunction : public Abstraction { // Make sure the actual type arguments are matching our expectations. - STATIC_ASSERT((std::is_arithmetic::value), - "LinearFunction not arithmetic T"); - STATIC_ASSERT((std::is_arithmetic::value), - "LinearFunction not to arithmetic"); + STATIC_ASSERT((std::is_arithmetic::value), + "LinearFunction not arithmetic T"); + STATIC_ASSERT((std::is_arithmetic::value), + "LinearFunction not to arithmetic"); + protected: - const T Intercept; - const T Coefficient; + /// The Intercept of the linear function + const D Intercept; + /// The Coefficient of the linear function + const D Coefficient; public: /// Creates an instance. /// /// \param Intercept the intercept of the linear function /// \param Coefficient the coefficient of the linear function - /// domain - LinearFunction(T Intercept, T Coefficient) noexcept - : Abstraction(Intercept), - Intercept(Intercept), + LinearFunction(D Intercept, D Coefficient) noexcept + : Abstraction(Intercept), Intercept(Intercept), Coefficient(Coefficient) {} /// Destroys \p this object. ~LinearFunction(void) = default; + /// Checks wether the Abstraction evaluates to default at the given position + /// As LinearFunctions can be evaluated everythwere, this is always false + /// + /// \param V the value at which to check if the function falls back to it's + /// default value. + /// + /// \return false + bool isDefaultAt(const D &V) const noexcept override { + (void)V; + return false; + } + /// Evaluates the linear function /// /// \param X the value at which to evaluate the function - /// \return the result - virtual A operator()(const T &X) const noexcept override { - return Intercept + X*Coefficient; + /// + /// \return Coefficient*X + Intercept + virtual R operator()(const D &X) const noexcept override { + return Intercept + X * Coefficient; } }; -/// Evaluates a sine function at a given value. +/// Implements \c rosa::agent::Abstraction as a sine function, +/// y = Amplitude * sin(Frequency * X + Phase) + Average. /// -/// \tparam T type of the functions domain -/// \tparam A type of the functions range -template class SineFunction : - public Abstraction{ +/// \note This implementation is supposed to be used to represent a sine +/// function from an arithmetic domain to an arithmetic range. This is enforced +/// statically. +/// +/// \tparam D type of the functions domain +/// \tparam R type of the functions range +template +class SineFunction : public Abstraction { // Make sure the actual type arguments are matching our expectations. - STATIC_ASSERT((std::is_arithmetic::value), - "SineFunction not arithmetic T"); - STATIC_ASSERT((std::is_arithmetic::value), - "SineFunction not to arithmetic"); + STATIC_ASSERT((std::is_arithmetic::value), + "SineFunction not arithmetic T"); + STATIC_ASSERT((std::is_arithmetic::value), + "SineFunction not to arithmetic"); + protected: - const T Frequency; - const T Amplitude; - const T Phase; - const T Average; + /// The frequency of the sine wave + const D Frequency; + /// The Ampiltude of the sine wave + const D Amplitude; + /// The Phase-shift of the sine wave + const D Phase; + /// The y-shift of the sine wave + const D Average; public: /// Creates an instance. /// /// \param Frequency the frequency of the sine wave /// \param Amplitude the amplitude of the sine wave /// \param Phase the phase of the sine wave /// \param Average the average of the sine wave - /// domain - SineFunction(T Frequency, T Amplitude, T Phase, T Average) noexcept - : Abstraction(Average), - Frequency(Frequency), - Amplitude(Amplitude), - Phase(Phase), - Average(Average) {} + SineFunction(D Frequency, D Amplitude, D Phase, D Average) noexcept + : Abstraction(Average), Frequency(Frequency), Amplitude(Amplitude), + Phase(Phase), Average(Average) {} /// Destroys \p this object. ~SineFunction(void) = default; - /// Evaluates the linear function + /// Checks wether the Abstraction evaluates to default at the given position + /// As SineFunctions can be evaluated everythwere, this is always false + /// + /// \param V the value at which to check if the function falls back to it's + /// default value. + /// + /// \return false + bool isDefaultAt(const D &V) const noexcept override { + (void)V; + return false; + } + + /// Evaluates the sine function /// /// \param X the value at which to evaluate the function - /// \return the result - virtual A operator()(const T &X) const noexcept override { - return Amplitude*sin(Frequency * X + Phase) + Average; + /// \return the value of the sine-function at X + virtual R operator()(const D &X) const noexcept override { + return Amplitude * sin(Frequency * X + Phase) + Average; } }; - -/// Implements \c rosa::agent::RangeAbstraction as an abstraction from -/// \c std::map from ranges of a type to abstractions of that type to another -/// type. The resulting abstractions are evaluated for the given values. +/// Implements \c rosa::agent::Abstraction as a partial function from a domain +// /to a range. /// -/// \note This implementation is supposed to be used to abstract ranges of -/// arithmetic types into abstractions from that type to another arithmetic -/// type, which is statically enforced. +/// \note This implementation is supposed to be used to represent a partial +/// function from an arithmetic domain to an arithmetic range. This is enforced +/// statically. /// -/// \invariant The keys in the underlying \c std::map define valid ranges -/// such that `first <= second` and there are no overlapping ranges defined by -/// the keys. +/// A partial function is defined as a list of abstractions, where each +/// abstraction is associated a range in which it is defined. These ranges must +/// be mutually exclusive. /// -/// \tparam T type to abstract from -/// \tparam A type to abstract to -template -class PartialFunction : private Abstraction { +/// \tparam D type of the functions domain +/// \tparam R type of the functions range +template +class PartialFunction : public Abstraction { // Make sure the actual type arguments are matching our expectations. - STATIC_ASSERT((std::is_arithmetic::value), "abstracting not arithmetic"); - STATIC_ASSERT((std::is_arithmetic::value), - "abstracting not to arithmetic"); + STATIC_ASSERT((std::is_arithmetic::value), "abstracting not arithmetic"); + STATIC_ASSERT((std::is_arithmetic::value), + "abstracting not to arithmetic"); private: - RangeAbstraction>> RA; + /// A \c rosa::agent::RangeAbstraction RA is used to represent the association + /// from ranges to Abstractions. + /// This returns the Abstraction that is defined for any given value, or + /// a default Abstraction if no Abstraction is defined for that value. + RangeAbstraction>> RA; public: - /// Creates an instance by Initializing the underlying \c RangeAbstraction. + /// Creates an instance by Initializing the underlying \c Abstraction. /// /// \param Map the mapping to do abstraction according to /// \param Default abstraction to abstract to by default /// /// \pre Each key defines a valid range such that `first <= second` and /// there are no overlapping ranges defined by the keys. - PartialFunction(const std::map, - std::shared_ptr>> &Map, - const A Default) - : Abstraction(Default), - RA(Map, std::shared_ptr> - (new Abstraction(Default))) { + PartialFunction( + const std::map, std::shared_ptr>> &Map, + const R Default) + : Abstraction(Default), + RA(Map, + std::shared_ptr>(new Abstraction(Default))) { } /// Destroys \p this object. ~PartialFunction(void) = default; - /// Evaluates an Abstraction from type \p T to type \p A based on the set - /// mapping. + /// Checks wether the Abstraction evaluates to default at the given position + /// + /// \param V the value at which to check if the function falls back to it's + /// default value. /// - /// Results in the value associated by the set mapping to the argument, or - /// \c rosa::agent::RangeAbstraction::Default if the actual argument is not - /// included in any of the ranges in the set mapping. + /// \return false if the value falls into a defined range and the Abstraction + /// defined for that range does not fall back to it's default value. + bool isDefaultAt(const D &V) const noexcept override { + return RA.isDefaultAt(V) ? true : RA(V)->isDefaultAt(V); + } + + /// Searches for an Abstraction for the given value and executes it for that + /// value, if such an Abstraction is found. The default Abstraction is + /// evaluated otherwise. /// /// \param V value to abstract /// /// \return the abstracted value based on the set mapping - A operator()(const T &V) const noexcept override { + R operator()(const D &V) const noexcept override { return RA(V)->operator()(V); } }; } // End namespace agent } // End namespace rosa #endif // ROSA_AGENT_FUNCTIONABSTRACTIONS_HPP diff --git a/include/rosa/agent/History.hpp b/include/rosa/agent/History.hpp index 1097ad0..5593cb7 100644 --- a/include/rosa/agent/History.hpp +++ b/include/rosa/agent/History.hpp @@ -1,292 +1,528 @@ //===-- rosa/agent/History.hpp ----------------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file rosa/agent/History.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Definition of *history* *functionality*. /// //===----------------------------------------------------------------------===// #ifndef ROSA_AGENT_HISTORY_HPP #define ROSA_AGENT_HISTORY_HPP #include "rosa/agent/Functionality.h" #include "rosa/config/config.h" #include "rosa/support/debug.hpp" #include "rosa/support/type_helper.hpp" #include +#include namespace rosa { namespace agent { /// Retention policies defining what a \c rosa::agent::History instance should /// do when the number of recorded entries reached its capacity. enum class HistoryPolicy { SRWF, ///< Stop Recording When Full -- no new entry is recorded when full - FIFO ///< First In First Out -- overwrite the earliest entry with a new one + FIFO, ///< First In First Out -- overwrite the earliest entry with a new one + LIFO ///< Last In First Out -- overwrite the latest entry with a new one }; -/// Implements *history* by recording and storing values. -/// -/// \note Not thread-safe implementation, which should not be a problem as any -/// instance of \c rosa::agent::Functionality is an internal component of a -/// \c rosa::Agent, which is the basic unit of concurrency. -/// -/// \tparam T type of values to store -/// \tparam N number of values to store at most -/// \tparam P retention policy to follow when capacity is reached -/// -/// \invariant The size of the underlying \c std::array is `N + 1`:\code -/// max_size() == N + 1 && N == max_size() - 1 -/// \endcode -template -class History : public Functionality, private std::array { - - // Bring into scope inherited functions that are used. - using std::array::max_size; - using std::array::operator[]; - - /// The index of the first data element in the circular buffer. - size_t Data; - - /// The index of the first empty slot in the circular buffer. - size_t Space; +template class History : public Functionality { public: - /// Creates an instances by initializing the indices for the circular buffer. - History(void) noexcept : Data(0), Space(0) {} + History(void) noexcept {} /// Destroys \p this object. - ~History(void) = default; + virtual ~History(void) = default; /// Tells the retention policy applied to \p this object. /// /// \return \c rosa::agent::History::P - static constexpr HistoryPolicy policyOfHistory(void) noexcept { return P; } + static constexpr HistoryPolicy policy(void) noexcept { return P; } /// Tells how many entries may be recorded by \c this object. /// /// \note The number of entries that are actually recorded may be smaller. /// - /// \return \c rosa::agent::History::N - static constexpr size_t lengthOfHistory(void) noexcept { return N; } + /// \return The max number of entries that may be recorded + virtual size_t maxLength(void) const noexcept = 0; /// Tells how many entries are currently recorded by \p this object. /// /// \return number of entries currently recorded by \p this object. /// /// \post The returned value cannot be larger than the capacity of \p this /// object:\code /// 0 <= numberOfEntries() && numberOfEntries <= lengthOfHistory() /// \endcode - size_t numberOfEntries(void) const noexcept { - return Data <= Space ? Space - Data : max_size() - Data + Space; - } + virtual size_t numberOfEntries(void) const noexcept = 0; /// Tells if \p this object has not recorded anything yet. /// /// \return if \p this object has no entries recorded bool empty(void) const noexcept { return numberOfEntries() == 0; } + /// Tells if the history reached it's maximum length + /// + /// \return if the history reached it's maximum length. + bool full(void) const noexcept { return numberOfEntries() == maxLength(); } + /// Gives a constant lvalue reference to an entry stored in \p this object. /// /// \note The recorded entries are indexed starting from the latest one. /// /// \param I the index at which the stored entry to take from /// /// \pre \p I is a valid index:\code - /// 0 <= I && I <= numberOfEntries() + /// 0 <= I && I < numberOfEntries() /// \endcode - const T &entry(const size_t I = 0) const noexcept { - ASSERT(0 <= I && I < numberOfEntries()); // Boundary check. - // Position counted back from the last recorded entry. - typename std::make_signed::type Pos = Space - (1 + I); - // Actual index wrapped around to the end of the buffer if negative. - return (*this)[Pos >= 0 ? Pos : max_size() + Pos]; - } + virtual const T &entry(const size_t I = 0) const noexcept = 0; + + /// Removes all entries recorded in \p this object. + virtual void clear() noexcept = 0; private: - /// Tells if the circular buffer is full. + /// Pushes a new entry into the history. /// - /// \return if the circular buffer is full. - bool full(void) const noexcept { return numberOfEntries() == N; } - - /// Pushes a new entry into the circular buffer. + /// \note The earliest entry gets overwritten if the history is full. /// - /// \note The earliest entry gets overwritten if the buffer is full. + /// \param V value to push into the history + virtual void pushBack(const T &V) noexcept = 0; + + /// Replaces the most recent entry in the history. /// - /// \param V value to push into the buffer - void pushBack(const T &V) noexcept { - // Store value to the first empty slot and step Space index. - (*this)[Space] = V; - Space = (Space + 1) % max_size(); - if (Data == Space) { - // Buffer was full, step Data index. - Data = (Data + 1) % max_size(); - } - } + /// \param V value to replace the most current value with + virtual void replaceFront(const T &V) noexcept = 0; public: /// Adds a new entry to \p this object and tells if the operation was /// successful. /// /// \note Success of the operation depends on the actual policy. /// /// \param V value to store /// /// \return if \p V was successfully stored bool addEntry(const T &V) noexcept { switch (P) { default: ROSA_CRITICAL("unkown HistoryPolicy"); + case HistoryPolicy::LIFO: + if (full()) { + replaceFront(V); + return true; + } case HistoryPolicy::SRWF: if (full()) { return false; } // \note Fall through to FIFO which unconditionally pushes the new entry. case HistoryPolicy::FIFO: // FIFO and SRWF not full. pushBack(V); return true; } } /// Tells the trend set by the entries recorded by \p this object. /// /// The number of steps to go back when calculating the trend is defined as /// argument to the function. /// /// \note The number of steps that can be made is limited by the number of /// entries recorded by \p this object. /// /// \note The function is made a template only to be able to use /// \c std::enable_if. /// /// \tparam X always use the default! /// /// \param D number of steps to go back in *history* /// /// \return trend set by analyzed entries /// /// \pre Statically, \p this object stores signed arithmetic values:\code /// std::is_arithmetic::value && std::is_signed::value /// \endcode Dynamically, \p D is a valid number of steps to take:\code - /// 0 <= D && D < N + /// 0 <= D && D < lengthOfHistory() /// \endcode template typename std::enable_if< std::is_arithmetic::value && std::is_signed::value, X>::type - trend(const size_t D = N - 1) const noexcept { + trend(const size_t D) const noexcept { STATIC_ASSERT((std::is_same::value), "not default template arg"); - ASSERT(0 <= D && D < N); // Boundary check. + ASSERT(0 <= D && D < maxLength()); // Boundary check. if (numberOfEntries() < 2 || D < 1) { // No entries for computing trend. return {}; // Zero element of \p T } else { // Here at least two entries. // \c S is the number of steps that can be done. const size_t S = std::min(numberOfEntries() - 1, D); size_t I = S; // Compute trend with linear regression. size_t SumIndices = 0; T SumEntries = {}; T SumSquareEntries = {}; T SumProduct = {}; while (I > 0) { // \note Indexing for the regression starts in the past. const size_t Index = S - I; const T Entry = entry(--I); SumIndices += Index; SumEntries += Entry; SumSquareEntries += Entry * Entry; SumProduct += Entry * Index; } return (SumProduct * S - SumEntries * SumIndices) / (SumSquareEntries * S - SumEntries * SumEntries); } } /// Tells the average absolute difference between consecutive entries recorded /// by \p this object /// The number of steps to go back when calculating the average is defined as /// argument to the function. /// /// \note The number of steps that can be made is limited by the number of /// entries recorded by \p this object. /// /// \note The function is made a template only to be able to use /// \c std::enable_if. /// /// \tparam X always use the default! /// /// \param D number of steps to go back in *history* /// /// \pre Statically, \p this object stores arithmetic values:\code /// std::is_arithmetic::value /// \endcode Dynamically, \p D is a valid number of steps to take:\code - /// 0 <= D && D < N + /// 0 <= D && D < lengthOfHistory() /// \endcode template typename std::enable_if::value, size_t>::type - averageAbsDiff(const size_t D = N - 1) const noexcept { + averageAbsDiff(const size_t D) const noexcept { STATIC_ASSERT((std::is_same::value), "not default template arg"); - ASSERT(0 <= D && D < N); // Boundary check. + ASSERT(0 <= D && D < maxLength()); // Boundary check. if (numberOfEntries() < 2 || D < 1) { // No difference to average. return {}; // Zero element of \p T } else { // Here at least two entries. // \c S is the number of steps that can be done. const size_t S = std::min(numberOfEntries() - 1, D); // Sum up differences as non-negative values only, hence using an // unsigned variable for that. size_t Diffs = {}; // Init to zero. // Count down entry indices and sum up all the absolute differences. size_t I = S; T Last = entry(I); while (I > 0) { T Next = entry(--I); Diffs += Last < Next ? Next - Last : Last - Next; Last = Next; } // Return the average of the summed differences. return Diffs / S; } } + + /// Tells the average of all entries recorded by \p this object + /// + /// \tparam R type of the result + template R average() const noexcept { + R Average = 0; + for (size_t I = 0; I < numberOfEntries(); I++) { + Average += entry(I); + } + Average /= numberOfEntries(); + return Average; + } +}; + +/// Implements *history* by recording and storing values. +/// The length of the underlying std::array is static and must be set at +/// compile-time +/// +/// \note Not thread-safe implementation, which should not be a problem as any +/// instance of \c rosa::agent::Functionality is an internal component of a +/// \c rosa::Agent, which is the basic unit of concurrency. +/// +/// \tparam T type of values to store +/// \tparam N number of values to store at most +/// \tparam P retention policy to follow when capacity is reached +/// +/// \invariant The size of the underlying \c std::array is `N + 1`:\code +/// max_size() == N + 1 && N == max_size() - 1 +/// \endcode +template +class StaticLengthHistory : public History, private std::array { + + // Bring into scope inherited functions that are used. + using std::array::max_size; + using std::array::operator[]; + + /// The index of the first data element in the circular buffer. + size_t Data; + + /// The index of the first empty slot in the circular buffer. + size_t Space; + +public: + using History::policy; + using History::empty; + using History::full; + using History::addEntry; + using History::trend; + using History::averageAbsDiff; + + /// Creates an instances by initializing the indices for the circular buffer. + StaticLengthHistory(void) noexcept : Data(0), Space(0) {} + + /// Destroys \p this object. + ~StaticLengthHistory(void) override = default; + + /// Tells how many entries may be recorded by \c this object. + /// + /// \note The number of entries that are actually recorded may be smaller. + /// + /// \return \c rosa::agent::History::N + size_t maxLength(void) const noexcept override { return N; } + + /// Tells how many entries are currently recorded by \p this object. + /// + /// \return number of entries currently recorded by \p this object. + /// + /// \post The returned value cannot be larger than the capacity of \p this + /// object:\code + /// 0 <= numberOfEntries() && numberOfEntries <= lengthOfHistory() + /// \endcode + size_t numberOfEntries(void) const noexcept override { + return Data <= Space ? Space - Data : max_size() - Data + Space; + } + + /// Gives a constant lvalue reference to an entry stored in \p this object. + /// + /// \note The recorded entries are indexed starting from the latest one. + /// + /// \param I the index at which the stored entry to take from + /// + /// \pre \p I is a valid index:\code + /// 0 <= I && I < numberOfEntries() + /// \endcode + const T &entry(const size_t I = 0) const noexcept override { + ASSERT(0 <= I && I < numberOfEntries()); // Boundary check. + // Position counted back from the last recorded entry. + typename std::make_signed::type Pos = Space - (1 + I); + // Actual index wrapped around to the end of the buffer if negative. + return (*this)[Pos >= 0 ? Pos : max_size() + Pos]; + } + + /// Removes all entries recorded in \p this object. + void clear() noexcept override { + Data = 0; + Space = 0; + } + +private: + /// Pushes a new entry into the circular buffer. + /// + /// \note The earliest entry gets overwritten if the buffer is full. + /// + /// \param V value to push into the buffer + void pushBack(const T &V) noexcept override { + // Store value to the first empty slot and step Space index. + (*this)[Space] = V; + Space = (Space + 1) % max_size(); + if (Data == Space) { + // Buffer was full, step Data index. + Data = (Data + 1) % max_size(); + } + } + + /// Replaces the most recent entry in the history. + /// + /// \param V value to replace the most current value with + void replaceFront(const T &V) noexcept override { + (*this)[(Space - 1) % max_size()] = V; + } }; /// Adds a new entry to a \c rosa::agent::History instance. /// /// \note The result of \c rosa::agent::History::addEntry is ignored. /// /// \tparam T type of values stored in \p H /// \tparam N number of values \p H is able to store /// \tparam P retention policy followed by \p H when capacity is reached /// /// \param H to add a new entry to /// \param V value to add to \p H /// /// \return \p H after adding \p V to it template -History &operator<<(History &H, const T &V) noexcept { +StaticLengthHistory &operator<<(StaticLengthHistory &H, + const T &V) noexcept { H.addEntry(V); return H; } +/// Implements *DynamicLengthHistory* by recording and storing values. +/// +/// \note Not thread-safe implementation, which should not be a problem as any +/// instance of \c rosa::agent::Functionality is an internal component of a +/// \c rosa::Agent, which is the basic unit of concurrency. +/// +/// \tparam T type of values to store +/// \tparam P retention policy to follow when capacity is reached +template +class DynamicLengthHistory : public History, private std::vector { + + // Bring into scope inherited functions that are used. + using std::vector::erase; + using std::vector::begin; + using std::vector::end; + using std::vector::size; + using std::vector::max_size; + using std::vector::resize; + using std::vector::push_back; + using std::vector::pop_back; + using std::vector::operator[]; + + /// The current length of the DynamicLengthHistory. + size_t Length; + +public: + using History::policy; + using History::empty; + using History::full; + using History::addEntry; + using History::trend; + using History::averageAbsDiff; + + /// Creates an instances by setting an initial length + DynamicLengthHistory(size_t Length) noexcept : Length(Length) { + this->resize(Length); + } + + /// Destroys \p this object. + ~DynamicLengthHistory(void) override = default; + + /// Tells how many entries may be recorded by \c this object. + /// + /// \note The number of entries that are actually recorded may be smaller. + /// + /// \return \c rosa::agent::DynamicLengthHistory::N + size_t maxLength(void) const noexcept override { return Length; } + + /// Tells how many entries are currently recorded by \p this object. + /// + /// \return number of entries currently recorded by \p this object. + /// + /// \post The returned value cannot be larger than the capacity of \p this + /// object:\code + /// 0 <= numberOfEntries() && numberOfEntries <= + /// lengthOfHistory() \endcode + size_t numberOfEntries(void) const noexcept { return size(); } + + /// Gives a constant lvalue reference to an entry stored in \p this object. + /// + /// \note The recorded entries are indexed starting from the latest one. + /// + /// \param I the index at which the stored entry to take from + /// + /// \pre \p I is a valid index:\code + /// 0 <= I && I < numberOfEntries() + /// \endcode + const T &entry(const size_t I = 0) const noexcept override { + ASSERT(0 <= I && I < numberOfEntries()); // Boundary check. + return this->operator[](size() - I - 1); + } + + /// Removes all entries recorded in \p this object. + void clear() noexcept override { erase(begin(), end()); } + +private: + /// Pushes a new entry into the circular buffer. + /// + /// \note The earliest entry gets overwritten if the buffer is full. + /// + /// \param V value to push into the buffer + void pushBack(const T &V) noexcept override { + if (full()) { + erase(begin()); + } + push_back(V); + } + + /// Replaces the most recent entry in the history. + /// + /// \param V value to replace the most current value with + void replaceFront(const T &V) noexcept override { + (void)pop_back(); + push_back(V); + } + +public: + /// Resizes the History length. If the new length is smaller than the number + /// of currently stored values, values are deleted according to the + /// HistoryPolicy. + /// + /// @param NewLength The new Length of the History. + void setLength(size_t NewLength) noexcept { + Length = NewLength; + if (NewLength < numberOfEntries()) { + switch (P) { + default: + ROSA_CRITICAL("unkown HistoryPolicy"); + case HistoryPolicy::LIFO: + case HistoryPolicy::SRWF: + // Delete last numberOfEntries() - NewLength items from the back + erase(begin() + NewLength, end()); + break; + case HistoryPolicy::FIFO: + // Delete last numberOfEntries() - NewLength items from the front + erase(begin(), begin() + (numberOfEntries() - NewLength)); + break; + } + } + this->resize(Length); + } +}; + +/// Adds a new entry to a \c rosa::agent::DynamicLengthHistory instance. +/// +/// \note The result of \c rosa::agent::DynamicLengthHistory::addEntry is +/// ignored. +/// +/// \tparam T type of values stored in \p H +/// \tparam P retention policy followed by \p H when capacity is reached +/// +/// \param H to add a new entry to +/// \param V value to add to \p H +/// +/// \return \p H after adding \p V to it +template +DynamicLengthHistory &operator<<(DynamicLengthHistory &H, + const T &V) noexcept { + H.addEntry(V); + return H; +} } // End namespace agent } // End namespace rosa #endif // ROSA_AGENT_HISTORY_HPP diff --git a/include/rosa/agent/RangeConfidence.hpp b/include/rosa/agent/RangeConfidence.hpp index 0966a87..60a53c1 100644 --- a/include/rosa/agent/RangeConfidence.hpp +++ b/include/rosa/agent/RangeConfidence.hpp @@ -1,89 +1,108 @@ //===-- rosa/agent/RangeConfidence.hpp --------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file rosa/agent/RangeConfidence.hpp /// /// \author Benedikt Tutzer (benedikt.tutzer@tuwien.ac.at) /// /// \date 2019 /// /// \brief Definition of *RangeConfidence* *functionality*. /// //===----------------------------------------------------------------------===// #ifndef ROSA_AGENT_RANGECONFIDENCE_HPP #define ROSA_AGENT_RANGECONFIDENCE_HPP -#include "rosa/agent/Functionality.h" #include "rosa/agent/Abstraction.hpp" #include "rosa/agent/FunctionAbstractions.hpp" +#include "rosa/agent/Functionality.h" #include "rosa/support/debug.hpp" #include -#include #include #include +#include namespace rosa { namespace agent { -/// Evaluates a vector of Abstractions at a given value and returns the results -/// as a vector +/// Evaluates a map of ID's to Abstractions at a given value and returns the +/// results as a map from ID's to results of the corresponding Abstraction /// /// \note This implementation is supposed to be used to abstract ranges of -/// arithmetic types into vectors of another arithmetic type, which is -/// statically enforced. +/// arithmetic types into maps whose values are of another arithmetic type, +/// which is statically enforced. /// -/// \tparam T type to abstract from -/// \tparam A type to abstract a vector of to -template -class RangeConfidence : public Abstraction>, - private std::vector>{ +/// \tparam D type to abstract from +/// \tparam I type the type of the ID's +/// \tparam R type of the range +template +class RangeConfidence : protected Abstraction>, + private std::map> { // Make sure the actual type arguments are matching our expectations. - STATIC_ASSERT((std::is_arithmetic::value), "abstracting not arithmetic"); - STATIC_ASSERT((std::is_arithmetic::value), - "abstracting not to arithmetic"); + STATIC_ASSERT((std::is_arithmetic::value), "abstracting not arithmetic"); + STATIC_ASSERT((std::is_arithmetic::value), + "abstracting not to arithmetic"); - // Bringing into scope inherited members. - using std::vector>::size; - using std::vector>::begin; - using std::vector>::end; +private: + /// Wether to include default results in the result-map or not + bool IgnoreDefaults; public: - /// Creates an instance by Initializing the underlying \c RangeAbstraction. + /// Creates an instance by Initializing the underlying \c Abstraction and + /// \c std::map. /// /// \param Abstractions the Abstractions to be evaluated - RangeConfidence(const std::vector> &Abstractions) - : Abstraction>({}), - std::vector>(Abstractions) { - } + /// \param IgnoreDefaults wether to include default results in the result-map + /// or not (defaults to false). + RangeConfidence(const std::map> &Abstractions, + bool IgnoreDefaults = false) + : Abstraction>({}), std::map>( + Abstractions), + IgnoreDefaults(IgnoreDefaults) {} /// Destroys \p this object. ~RangeConfidence(void) = default; - /// Evaluates an Abstraction from type \p T to type \p A based on the set - /// mapping. + /// Checks wether all Abstractions evaluate to default at the given position /// - /// Results in the value associated by the set mapping to the argument, or - /// \c rosa::agent::RangeAbstraction::Default if the actual argument is not - /// included in any of the ranges in the set mapping. + /// \param V the value at which to check if the functions falls back to it's + /// default value. + /// + /// \return true, if all Abstractions evaluate to default + bool isDefaultAt(const D &V) const noexcept override { + for (auto const &P : ((std::map>)*this)) { + if (!P.second.isDefaultAt(V)) + return false; + } + return true; + } + + /// All Abstractions stored in the underlying \c std::map are evaluated for + /// the given value. Their results are stored in another map, with + /// corresponding keys. + /// If IgnoreDefaults is set, Abstractions that default for that value are not + /// evaluated and inserted into the resulting \c std::map /// /// \param V value to abstract /// - /// \return the abstracted value based on the set mapping - std::vector operator()(const T &V) const noexcept override { - std::vector ret; - for (auto const& func : ((std::vector>)*this)){ - ret.push_back(func(V)); + /// \return a \c std::map containing the results of the stored Abstractions, + /// indexable by the key's the Abstractions are associated with + std::map operator()(const D &V) const noexcept override { + std::map Ret; + for (auto const &P : ((std::map>)*this)) { + if (!IgnoreDefaults || !P.second.isDefaultAt(V)) + Ret.insert(std::pair(P.first, P.second(V))); } - return ret; + return Ret; } }; } // End namespace agent } // End namespace rosa #endif // ROSA_AGENT_RANGECONFIDENCE_HPP diff --git a/include/rosa/core/SystemBase.hpp b/include/rosa/core/SystemBase.hpp index a5ca0d2..4b6720e 100644 --- a/include/rosa/core/SystemBase.hpp +++ b/include/rosa/core/SystemBase.hpp @@ -1,138 +1,138 @@ //===-- rosa/core/SystemBase.hpp -----------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file rosa/core/SystemBase.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Base implementation of the \c rosa::System interface. /// //===----------------------------------------------------------------------===// #ifndef ROSA_CORE_SYSTEMBASE_HPP #define ROSA_CORE_SYSTEMBASE_HPP #include "rosa/core/System.hpp" #include namespace rosa { /// Base implementation of the \c rosa::System interface. /// /// This implementation provides only equality checking and *name* for /// \c rosa::System, identifiers for \c rosa::Unit instances, and marking the /// \c rosa::System cleaned for destruction. /// /// \note Actual implementations of \c rosa::System and derived interfaces are /// supposed to inherit from this implementation. class SystemBase : public System { protected: /// Creates an instance. /// /// \note Protected constructor restrict instantiation for subclasses. /// /// \param Name name of the new instance SystemBase(const std::string &Name) noexcept; public: /// Destroys \p this object. /// /// \pre \p this object is marked cleaned:\code /// isSystemCleaned() /// \endcode ~SystemBase(void); /// Tells whether \p this object is the same as \p Other. /// /// Two \c rosa::System instances are considered equal if they share a common /// \c rosa::SystemBase::Name member field. That should do among various /// subclasses. /// /// \param Other another \c rosa::System instance to compare to /// /// \return whether \p this object and \p Other is the same bool operator==(const System &Other) const noexcept override; protected: /// The textual name of \p this object implementing \c rosa::System. const std::string Name; private: /// Number of \c rosa::Unit instances constructed by \p this object. /// /// \note Should never be decremented! std::atomic UnitCount; /// Indicates that \p this object has been cleaned and is ready for /// destruction. /// /// The field is initialized as \c false and can be set by /// \c rosa::SystemBase::markCleaned. /// /// \note Subclasses must set the flag upon destructing their instances, which /// indicates to the destructor of the base-class that all the managed /// resources has been properly released. std::atomic SystemIsCleaned; public: /// Tells the name of \p this object /// /// \note The returned reference remains valid as long as \p this object is /// not destroyed. /// /// \return reference to \c rosa::SystemBase::Name const std::string &name(void) const noexcept override; protected: /// Tells the next unique identifier to be used for a newly created /// \c rosa::Unit. /// /// The functions takes the current value of the internal counter /// \c rosa::SystemBase::UnitCount and then increments it. /// /// \note This is the only function modifying /// \c rosa::SystemBase::UnitCount. /// /// \return \c rosa::id_t which is unique within the context of \p this /// object. id_t nextId(void) noexcept override; /// Tells if \p this object has been marked cleaned and is ready for /// destruction. /// /// \return if \p this object is marked clean. bool isSystemCleaned(void) const noexcept override; /// Marks \p this object cleaned by setting /// \c rosa::SystemBase::SystemIsCleaned. /// /// \note Can be called only once when the System does not have any live /// \c rosa::Unit instances. /// /// \pre \p this object has not yet been marked as cleaned and it has no /// \c rosa::Unit instances registered:\code /// !isSystemCleaned() && empty() /// \endcode /// /// \post \p this object is marked cleaned:\code /// isSystemCleaned() /// \endcode void markCleaned(void) noexcept override; /// Tells the number of \c rosa::Unit instances constructed in the context of /// \p this object so far, including those being already destroyed. /// /// \return current value of \c rosa::SystemBase::UnitCount that is the number /// of \c rosa::Unit instances created so far size_t numberOfConstructedUnits(void) const noexcept override; }; } // End namespace rosa -#endif // ROSA_LIB_CORE_SYSTEMBASE_HPP +#endif // ROSA_CORE_SYSTEMBASE_HPP diff --git a/include/rosa/deluxe/DeluxeSystem.hpp b/include/rosa/deluxe/DeluxeSystem.hpp index 4388a4e..1be3bbd 100755 --- a/include/rosa/deluxe/DeluxeSystem.hpp +++ b/include/rosa/deluxe/DeluxeSystem.hpp @@ -1,211 +1,210 @@ //===-- rosa/deluxe/DeluxeSystem.hpp ----------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file rosa/deluxe/DeluxeSystem.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017-2019 /// /// \brief Specialization of \c rosa::MessagingSystem for the *deluxe /// interface*. /// /// \see \c rosa::deluxe::DeluxeContext /// //===----------------------------------------------------------------------===// #ifndef ROSA_DELUXE_DELUXESYSTEM_HPP #define ROSA_DELUXE_DELUXESYSTEM_HPP #include "rosa/core/MessagingSystem.hpp" #include "rosa/deluxe/DeluxeAgent.hpp" #include "rosa/deluxe/DeluxeSensor.hpp" namespace rosa { namespace deluxe { /// Implements and extends the \c rosa::MessagingSystem interface to be /// used by \c rosa::deluxe::DeluxeContext. /// /// The class is a specialization of \c rosa::MessagingSystem, where objects /// of two specialized subtypes of \c rosa::Agent, \c rosa::deluxe::DeluxeSensor /// and \c rosa::deluxe::DeluxeAgent, constitute a system. The class extends the /// \c rosa::MessagingSystem interface with features required to implement the /// *deluxe interface*. /// /// \see rosa::deluxe::DeluxeContext class DeluxeSystem : public MessagingSystem { friend class DeluxeContext; public: /// Returns an object implementing the \c rosa::deluxe::DeluxeSystem /// interface. /// /// \param Name name of the new instance /// /// \return \c std::unique_ptr for the new instance of /// \c rosa::DeluxeSystem static std::unique_ptr createSystem(const std::string &Name) noexcept; protected: /// Creates a new instance. /// /// \note Protected constructor restricts instantiation for subclasses. DeluxeSystem(void) noexcept = default; public: /// Creates a \c rosa::deluxe::DeluxeSensor instance owned by \p this object /// and returns a \p rosa::AgentHandle for it. /// /// \tparam T type of data the new \c rosa::deluxe::DeluxeSensor operates on /// /// \param Name name of the new \c rosa::deluxe::DeluxeSensor /// \param F function to generate the next value with during normal operation /// /// \return \c rosa::AgentHandle for new \c rosa::deluxe::DeluxeSensor template AgentHandle createSensor(const std::string &Name, DeluxeSensor::D &&F) noexcept; /// Creates a \c rosa::deluxe::DeluxeAgent instance owned by \p this object /// and returns a \c rosa::AgentHandle for it. /// /// \tparam T type of data the new \c rosa::deluxe::DeluxeAgent outputs /// \tparam As types of inputs the new \c rosa::deluxe::DeluxeAgent takes /// /// \param Name name of the new \c rosa::deluxe::DeluxeAgent /// \param F function for the new \c rosa::deluxe::DeluxeAgent to process /// input values and generate output with /// /// \return \c rosa::AgentHandle for new \c rosa::deluxe::DeluxeAgent template AgentHandle createAgent(const std::string &Name, DeluxeAgent::D &&F) noexcept; protected: /// Tells whether a \c rosa::AgentHandle refers to a /// \c rosa::deluxe::DeluxeSensor owned by \p this object. /// /// \param H \c rosa::AgentHandle to check /// /// \return whether \p H refers to a \c rosa::deluxe::DeluxeSensor owned by /// \p this object virtual bool isDeluxeSensor(const AgentHandle &H) const noexcept = 0; /// Extracts a const qualified \c rosa::deluxe::DeluxeSensor reference from a /// const qualified \c rosa::AgentHandle if possible. /// /// The function returns a \c rosa::Optional object containing a const /// qualified reference to a \c rosa::deluxe::DeluxeSensor object extracted /// from a const qualified \c rosa::AgentHandle instance if the referred /// object is of type \c rosa::deluxeDeluxeSensor and owned by \p this object. /// The returned \c rosa::Optional object is empty otherwise. /// /// \see rosa::deluxe::DeluxeSystem::isDeluxeSensor /// /// \param H \c rosa::AgentHandle to extract a \c rosa::deluxe::DeluxeSensor /// from /// /// \return const qualified reference to \c rosa::deluxe::DeluxeSensor if /// \p H refers to an object which is of that type and is owned by \p this /// object Optional getDeluxeSensor(const AgentHandle &H) const noexcept; /// Extracts a \c rosa::deluxe::DeluxeSensor reference from a /// \c rosa::AgentHandle if possible. /// /// The function returns a \c rosa::Optional object containing a reference to /// a \c rosa::deluxe::DeluxeSensor object extracted from a /// \c rosa::AgentHandle instance if the referred object is of type /// \c rosa::deluxeDeluxeSensor and owned by \p this object. The returned /// \c rosa::Optional object is empty otherwise. /// /// \see rosa::deluxe::DeluxeSystem::isDeluxeSensor /// /// \param H \c rosa::AgentHandle to extract a \c rosa::deluxe::DeluxeSensor /// from /// /// \return reference to \c rosa::deluxe::DeluxeSensor if \p H refers to an /// object which is of that type and is owned by \p this object Optional getDeluxeSensor(AgentHandle &H) const noexcept; /// Tells whether a \c rosa::AgentHandle refers to a /// \c rosa::deluxe::DeluxeAgent owned by \p this object. /// /// \param H \c rosa::AgentHandle to check /// /// \return whether \p H refers to a \c rosa::deluxe::DeluxeAgent owned by /// \p this object virtual bool isDeluxeAgent(const AgentHandle &H) const noexcept = 0; /// Extracts a const qualified \c rosa::deluxe::DeluxeAgent reference from a /// const qualified \c rosa::AgentHandle if possible. /// /// The function returns a \c rosa::Optional object containing a const /// qualified reference to a \c rosa::deluxe::DeluxeAgent object extracted /// from a const qualified \c rosa::AgentHandle instance if the referred /// object is of type \c rosa::deluxeDeluxeAgent and owned by \p this object. /// The returned \c rosa::Optional object is empty otherwise. /// /// \see rosa::deluxe::DeluxeSystem::isDeluxeAgent /// /// \param H \c rosa::AgentHandle to extract a \c rosa::deluxe::DeluxeAgent /// from /// /// \return const qualified reference to \c rosa::deluxe::DeluxeAgent if \p H /// refers to an object which is of that type and is owned by \p this object Optional getDeluxeAgent(const AgentHandle &H) const noexcept; /// Extracts a \c rosa::deluxe::DeluxeAgent reference from a /// \c rosa::AgentHandle if possible. /// /// The function returns a \c rosa::Optional object containing a reference to /// a \c rosa::deluxe::DeluxeAgent object extracted from a /// \c rosa::AgentHandle instance if the referred object is of type /// \c rosa::deluxeDeluxeAgent and owned by \p this object. The returned /// \c rosa::Optional object is empty otherwise. /// /// \see rosa::deluxe::DeluxeSystem::isDeluxeAgent /// /// \param H \c rosa::AgentHandle to extract a \c rosa::deluxe::DeluxeAgent /// from /// /// \return reference to \c rosa::deluxe::DeluxeAgent if \p H refers to an /// object which is of that type and is owned by \p this object Optional getDeluxeAgent(AgentHandle &H) const noexcept; }; template AgentHandle DeluxeSystem::createSensor(const std::string &Name, DeluxeSensor::D &&F) noexcept { Agent &DS = createUnit( [&](const id_t Id, MessagingSystem &S) { return new DeluxeSensor(atoms::SensorKind, Id, Name, S, std::move(F)); }); return {DS}; } template -AgentHandle -DeluxeSystem::createAgent(const std::string &Name, - DeluxeAgent::D &&F) noexcept { - +AgentHandle DeluxeSystem::createAgent(const std::string &Name, + DeluxeAgent::D &&F) noexcept { + Agent &DA = createUnit( [&](const id_t Id, DeluxeSystem &S) { return new DeluxeAgent(atoms::AgentKind, Id, Name, S, std::move(F)); }); return {DA}; } } // End namespace deluxe } // End namespace rosa -#endif // ROSA_LIB_DELUXE_DELUXESYSTEM_HPP +#endif // ROSA_DELUXE_DELUXESYSTEM_HPP diff --git a/lib/core/MessagingSystemImpl.hpp b/lib/core/MessagingSystemImpl.hpp index 485711a..89a366b 100644 --- a/lib/core/MessagingSystemImpl.hpp +++ b/lib/core/MessagingSystemImpl.hpp @@ -1,109 +1,110 @@ //===-- core/MessagingSystemImpl.hpp ----------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file core/MessagingSystemImpl.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Declaration of a basic implementation of the \c rosa::MessagingSystem /// interface. /// //===----------------------------------------------------------------------===// #ifndef ROSA_LIB_CORE_MESSAGINGSYSTEMIMPL_HPP #define ROSA_LIB_CORE_MESSAGINGSYSTEMIMPL_HPP #include "SystemImpl.hpp" #include "rosa/core/MessagingSystem.hpp" namespace rosa { /// Implements \c rosa::MessagingSystem by extending \c rosa::SystemImpl with /// adding a simple implementation of sending messages: directly invoking /// \c rosa::Agent instances with given \c rosa::Message objects. /// /// \note Keep in mind that sending a \c rosa::Message object with this /// implementation translates into a direct function call. class MessagingSystemImpl : public MessagingSystem, public SystemImpl { /// Alies for the base-class \c rosa::SystemImpl. using Base = SystemImpl; public: /// Creates an instance. /// /// \param Name name of the new instance MessagingSystemImpl(const std::string &Name) noexcept; - /// \defgroup MessagingSystemImplCallForwarding Call forwardings of rosa::MessagingSystemImpl + /// \defgroup MessagingSystemImplCallForwarding Call forwardings of + /// rosa::MessagingSystemImpl /// /// \c rosa::MessagingSystemImpl call forwardings /// /// \note Simply forwarding calls to implementations provided by /// \c rosa::MessagingSystem::Base for the \c rosa::System interface. /// /// \todo How could we use the inherited implementations in a simpler way? ///@{ bool operator==(const System &Other) const noexcept override { return Base::operator==(Other); } protected: id_t nextId(void) noexcept override { return Base::nextId(); } bool isSystemCleaned(void) const noexcept override { return Base::isSystemCleaned(); } void markCleaned(void) noexcept override { Base::markCleaned(); } void registerUnit(Unit &U) noexcept override { Base::registerUnit(U); } void destroyUnit(Unit &U) noexcept override { Base::destroyUnit(U); } bool isUnitRegistered(const Unit &U) const noexcept override { return Base::isUnitRegistered(U); } public: const std::string &name(void) const noexcept override { return Base::name(); } size_t numberOfConstructedUnits(void) const noexcept override { return Base::numberOfConstructedUnits(); } size_t numberOfLiveUnits(void) const noexcept override { return Base::numberOfLiveUnits(); } bool empty(void) const noexcept override { return Base::empty(); } ///@} /// Sends a \c rosa::message_t instance to the \c rosa::Agent instance /// referred by a \c rosa::AgentHandle -- by directly invoking the /// \c rosa::Agent instance with the \c rosa::Message object. /// /// \note If the given \c rosa::Message object cannot be handled by the /// referred \c rosa::Agent instance, the \c rosa::Message object is simply /// ignored. /// /// \param H refers to the \c rosa::Agent instance to send to /// \param M message to send /// /// \pre The referred \c rosa::Agent instance is owned by \p this object and /// also registered: \code /// &unwrapSystem(H) == this && isUnitRegistered(unwrapAgent(H)) /// \endcode void send(const AgentHandle &H, message_t &&M) noexcept override; }; } // End namespace rosa #endif // ROSA_LIB_CORE_MESSAGINGSYSTEMIMPL_HPP