Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F386404
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Size
46 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/examples/agent-functionalities/agent-functionalities.cpp b/examples/agent-functionalities/agent-functionalities.cpp
index c1d77a1..47dd576 100644
--- a/examples/agent-functionalities/agent-functionalities.cpp
+++ b/examples/agent-functionalities/agent-functionalities.cpp
@@ -1,183 +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 <vector>
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 <typename T, typename... Funs>
static AgentHandle createMyAgent(MessagingSystem *S, const std::string &Name,
Funs &&... Fs) {
return ((SystemTester *)S)->createAgent<T>(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<atom("tick")>;
private:
enum class Categories { Bad, Normal, Good };
static const std::map<Categories, const char *> CategoryNames;
- History<uint8_t, 10, HistoryPolicy::FIFO> H;
+ StaticLengthHistory<uint8_t, 10, HistoryPolicy::FIFO> H;
Confidence<uint8_t> C;
RangeAbstraction<uint8_t, Categories> A;
PartialFunction<int, int> L;
RangeConfidence<float, Categories, float> RCL;
RangeConfidence<float, Categories, float> 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::map<Categories, float> res_lin = RCL(H.entry());
- for (auto i : res_lin){
- LOG_INFO_STREAM << " " << CategoryNames.at(i.first)
- << " " << i.second << "," ;
+ for (auto i : res_lin) {
+ LOG_INFO_STREAM << " " << CategoryNames.at(i.first) << " " << i.second
+ << ",";
}
LOG_INFO_STREAM << " range-confidence-sine: ";
std::map<Categories, float> res_sine = RCS(H.entry());
- for (auto i : res_sine){
- LOG_INFO_STREAM << " " << CategoryNames.at(i.first)
- << " " << i.second << "," ;
+ for (auto i : res_sine) {
+ LOG_INFO_STREAM << " " << CategoryNames.at(i.first) << " " << i.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<LinearFunction<int, int>>(0, 1)},
{{2, 4}, std::make_shared<LinearFunction<int, int>>(2, 0)},
{{4, 6}, std::make_shared<LinearFunction<int, int>>(6, -1)}},
0),
- RCL({
- {Categories::Bad, PartialFunction<float, float>({
- {{0, 3}, std::make_shared<LinearFunction<float, float>>
- (0, 1.0/3)},
- {{3, 6}, std::make_shared<LinearFunction<float, float>>
- (1, 0)},
- {{6, 9}, std::make_shared<LinearFunction<float, float>>
- (3.0, -1.0/3)},
- },0)},
- {Categories::Normal, PartialFunction<float, float>({
- {{6, 9}, std::make_shared<LinearFunction<float, float>>
- (-2, 1.0/3)},
- {{9, 12}, std::make_shared<LinearFunction<float, float>>
- (1, 0)},
- {{12, 15}, std::make_shared<LinearFunction<float, float>>
- (5, -1.0/3)},
- },0)},
- {Categories::Good, PartialFunction<float, float>({
- {{12, 15}, std::make_shared<LinearFunction<float, float>>
- (-4, 1.0/3)},
- {{15, 18}, std::make_shared<LinearFunction<float, float>>
- (1, 0)},
- {{18, 21}, std::make_shared<LinearFunction<float, float>>
- (7, -1.0/3)},
- },0)}
- }),
- RCS({
- {Categories::Bad, PartialFunction<float, float>({
- {{0, 3}, std::make_shared<SineFunction<float, float>>
- (M_PI/3, 0.5, -M_PI/2, 0.5)},
- {{3, 6}, std::make_shared<LinearFunction<float, float>>(1, 0)},
- {{6, 9}, std::make_shared<SineFunction<float, float>>
- (M_PI/3, 0.5, -M_PI/2 + 3, 0.5)},
- },0)},
- {Categories::Normal, PartialFunction<float, float>({
- {{6, 9}, std::make_shared<SineFunction<float, float>>
- (M_PI/3, 0.5, -M_PI/2, 0.5)},
- {{9, 12}, std::make_shared<LinearFunction<float, float>>(1, 0)},
- {{12, 15}, std::make_shared<SineFunction<float, float>>
- (M_PI/3, 0.5, -M_PI/2 + 3, 0.5)},
- },0)},
- {Categories::Good, PartialFunction<float, float>({
- {{12, 15}, std::make_shared<SineFunction<float, float>>
- (M_PI/3, 0.5, -M_PI/2, 0.5)},
- {{15, 18}, std::make_shared<LinearFunction<float, float>>(1, 0)},
- {{18, 21}, std::make_shared<SineFunction<float, float>>
- (M_PI/3, 0.5, -M_PI/2 + 3, 0.5)},
- },0)}
- }, true){}
+ RCL({{Categories::Bad,
+ PartialFunction<float, float>(
+ {
+ {{0, 3},
+ std::make_shared<LinearFunction<float, float>>(0,
+ 1.0 / 3)},
+ {{3, 6},
+ std::make_shared<LinearFunction<float, float>>(1, 0)},
+ {{6, 9},
+ std::make_shared<LinearFunction<float, float>>(
+ 3.0, -1.0 / 3)},
+ },
+ 0)},
+ {Categories::Normal,
+ PartialFunction<float, float>(
+ {
+ {{6, 9},
+ std::make_shared<LinearFunction<float, float>>(-2,
+ 1.0 / 3)},
+ {{9, 12},
+ std::make_shared<LinearFunction<float, float>>(1, 0)},
+ {{12, 15},
+ std::make_shared<LinearFunction<float, float>>(
+ 5, -1.0 / 3)},
+ },
+ 0)},
+ {Categories::Good,
+ PartialFunction<float, float>(
+ {
+ {{12, 15},
+ std::make_shared<LinearFunction<float, float>>(-4,
+ 1.0 / 3)},
+ {{15, 18},
+ std::make_shared<LinearFunction<float, float>>(1, 0)},
+ {{18, 21},
+ std::make_shared<LinearFunction<float, float>>(
+ 7, -1.0 / 3)},
+ },
+ 0)}}),
+ RCS({{Categories::Bad,
+ PartialFunction<float, float>(
+ {
+ {{0, 3},
+ std::make_shared<SineFunction<float, float>>(
+ M_PI / 3, 0.5, -M_PI / 2, 0.5)},
+ {{3, 6},
+ std::make_shared<LinearFunction<float, float>>(1, 0)},
+ {{6, 9},
+ std::make_shared<SineFunction<float, float>>(
+ M_PI / 3, 0.5, -M_PI / 2 + 3, 0.5)},
+ },
+ 0)},
+ {Categories::Normal,
+ PartialFunction<float, float>(
+ {
+ {{6, 9},
+ std::make_shared<SineFunction<float, float>>(
+ M_PI / 3, 0.5, -M_PI / 2, 0.5)},
+ {{9, 12},
+ std::make_shared<LinearFunction<float, float>>(1, 0)},
+ {{12, 15},
+ std::make_shared<SineFunction<float, float>>(
+ M_PI / 3, 0.5, -M_PI / 2 + 3, 0.5)},
+ },
+ 0)},
+ {Categories::Good,
+ PartialFunction<float, float>(
+ {
+ {{12, 15},
+ std::make_shared<SineFunction<float, float>>(
+ M_PI / 3, 0.5, -M_PI / 2, 0.5)},
+ {{15, 18},
+ std::make_shared<LinearFunction<float, float>>(1, 0)},
+ {{18, 21},
+ std::make_shared<SineFunction<float, float>>(
+ M_PI / 3, 0.5, -M_PI / 2 + 3, 0.5)},
+ },
+ 0)}},
+ true) {}
};
const std::map<MyAgent::Categories, const char *> 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<MessagingSystem> S = MessagingSystem::createSystem("Sys");
MessagingSystem *SP = S.get();
AgentHandle A = SystemTester::createMyAgent<MyAgent>(SP, "MyAgent");
- std::vector<uint8_t> Vs{0, 1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 13,
- 15, 14, 15, 16, 19, 20, 21};
+ std::vector<uint8_t> 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, uint8_t>(MyAgent::Tick::Value, *I);
}
SystemTester::destroyMyAgent(SP, A);
return 0;
}
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 <limits>
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 <typename T> class Confidence : public Functionality {
// Make sure the actual type argument is an arithmetic type.
STATIC_ASSERT(std::is_arithmetic<T>::value, "not arithmetic Confidence");
public:
/// Unsigned type corresponding to \p T.
using UT = unsigned_t<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<T>::value
? std::numeric_limits<T>::min()
: std::numeric_limits<T>::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<T>::value || !std::numeric_limits<T>::has_infinity)
? std::numeric_limits<T>::max()
: std::numeric_limits<T>::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<UT>::value || !std::numeric_limits<UT>::has_infinity)
? std::numeric_limits<UT>::max()
: std::numeric_limits<UT>::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 <size_t N, HistoryPolicy P>
- bool operator()(const History<T, N, P> &H) const noexcept {
+ bool operator()(const StaticLengthHistory<T, N, P> &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 9a7127a..59200de 100644
--- a/include/rosa/agent/FunctionAbstractions.hpp
+++ b/include/rosa/agent/FunctionAbstractions.hpp
@@ -1,224 +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 <algorithm>
-#include <vector>
#include <cmath>
#include <memory>
+#include <vector>
namespace rosa {
namespace agent {
/// 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 D type of the functions domain
/// \tparam R type of the functions range
-template <typename D, typename R> class LinearFunction :
- public Abstraction<D, R>{
+template <typename D, typename R>
+class LinearFunction : public Abstraction<D, R> {
// Make sure the actual type arguments are matching our expectations.
STATIC_ASSERT((std::is_arithmetic<D>::value),
- "LinearFunction not arithmetic T");
+ "LinearFunction not arithmetic T");
STATIC_ASSERT((std::is_arithmetic<R>::value),
- "LinearFunction not to arithmetic");
+ "LinearFunction not to arithmetic");
+
protected:
/// 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
LinearFunction(D Intercept, D Coefficient) noexcept
- : Abstraction<D, R>(Intercept),
- Intercept(Intercept),
+ : Abstraction<D, R>(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 Coefficient*X + Intercept
virtual R operator()(const D &X) const noexcept override {
- return Intercept + X*Coefficient;
+ return Intercept + X * Coefficient;
}
};
/// Implements \c rosa::agent::Abstraction as a sine function,
/// y = Amplitude * sin(Frequency * X + Phase) + Average.
///
/// \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 <typename D, typename R> class SineFunction :
- public Abstraction<D, R>{
+template <typename D, typename R>
+class SineFunction : public Abstraction<D, R> {
// Make sure the actual type arguments are matching our expectations.
STATIC_ASSERT((std::is_arithmetic<D>::value),
- "SineFunction not arithmetic T");
+ "SineFunction not arithmetic T");
STATIC_ASSERT((std::is_arithmetic<R>::value),
- "SineFunction not to arithmetic");
+ "SineFunction not to arithmetic");
+
protected:
/// 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
SineFunction(D Frequency, D Amplitude, D Phase, D Average) noexcept
- : Abstraction<D, R>(Average),
- Frequency(Frequency),
- Amplitude(Amplitude),
- Phase(Phase),
- Average(Average) {}
+ : Abstraction<D, R>(Average), Frequency(Frequency), Amplitude(Amplitude),
+ Phase(Phase), Average(Average) {}
/// Destroys \p this object.
~SineFunction(void) = default;
/// 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 value of the sine-function at X
virtual R operator()(const D &X) const noexcept override {
- return Amplitude*sin(Frequency * X + Phase) + Average;
+ return Amplitude * sin(Frequency * X + Phase) + Average;
}
};
/// Implements \c rosa::agent::Abstraction as a partial function from a domain
// /to a range.
///
/// \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.
///
/// 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 D type of the functions domain
/// \tparam R type of the functions range
template <typename D, typename R>
class PartialFunction : public Abstraction<D, R> {
// Make sure the actual type arguments are matching our expectations.
STATIC_ASSERT((std::is_arithmetic<D>::value), "abstracting not arithmetic");
STATIC_ASSERT((std::is_arithmetic<R>::value),
- "abstracting not to arithmetic");
+ "abstracting not to arithmetic");
private:
/// 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<D, std::shared_ptr<Abstraction<D, R>>> RA;
public:
/// 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::pair<D, D>,
- std::shared_ptr<Abstraction<D, R>>> &Map,
- const R Default)
+ PartialFunction(
+ const std::map<std::pair<D, D>, std::shared_ptr<Abstraction<D, R>>> &Map,
+ const R Default)
: Abstraction<D, R>(Default),
- RA(Map, std::shared_ptr<Abstraction<D, R>>
- (new Abstraction<D, R>(Default))) {
+ RA(Map,
+ std::shared_ptr<Abstraction<D, R>>(new Abstraction<D, R>(Default))) {
}
/// Destroys \p this object.
~PartialFunction(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 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
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..9676f47 100644
--- a/include/rosa/agent/History.hpp
+++ b/include/rosa/agent/History.hpp
@@ -1,292 +1,548 @@
//===-- 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 <array>
+#include <vector>
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 <typename T, size_t N, HistoryPolicy P>
-class History : public Functionality, private std::array<T, N + 1> {
+class StaticLengthHistory : public Functionality, private std::array<T, N + 1> {
// Bring into scope inherited functions that are used.
using std::array<T, N + 1>::max_size;
using std::array<T, N + 1>::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:
/// Creates an instances by initializing the indices for the circular buffer.
- History(void) noexcept : Data(0), Space(0) {}
+ StaticLengthHistory(void) noexcept : Data(0), Space(0) {}
/// Destroys \p this object.
- ~History(void) = default;
+ ~StaticLengthHistory(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; }
/// 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; }
/// 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;
}
/// 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; }
/// 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 {
ASSERT(0 <= I && I < numberOfEntries()); // Boundary check.
// Position counted back from the last recorded entry.
typename std::make_signed<const size_t>::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];
}
private:
/// Tells if the circular buffer is full.
///
/// \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 buffer is full.
///
/// \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();
}
}
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()) {
+ (*this)[(Space - 1) % max_size()] = 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<T>::value && std::is_signed<T>::value
/// \endcode Dynamically, \p D is a valid number of steps to take:\code
/// 0 <= D && D < N
/// \endcode
template <typename X = T>
typename std::enable_if<
std::is_arithmetic<X>::value && std::is_signed<X>::value, X>::type
trend(const size_t D = N - 1) const noexcept {
STATIC_ASSERT((std::is_same<X, T>::value), "not default template arg");
ASSERT(0 <= D && D < N); // 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<T>::value
/// \endcode Dynamically, \p D is a valid number of steps to take:\code
/// 0 <= D && D < N
/// \endcode
template <typename X = T>
typename std::enable_if<std::is_arithmetic<X>::value, size_t>::type
averageAbsDiff(const size_t D = N - 1) const noexcept {
STATIC_ASSERT((std::is_same<X, T>::value), "not default template arg");
ASSERT(0 <= D && D < N); // 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;
}
}
};
/// 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 <typename T, size_t N, HistoryPolicy P>
-History<T, N, P> &operator<<(History<T, N, P> &H, const T &V) noexcept {
+StaticLengthHistory<T, N, P> &operator<<(StaticLengthHistory<T, N, P> &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 <typename T, HistoryPolicy P>
+class DynamicLengthHistory : public Functionality, private std::vector<T> {
+
+ // Bring into scope inherited functions that are used.
+ using std::vector<T>::erase;
+ using std::vector<T>::begin;
+ using std::vector<T>::end;
+ using std::vector<T>::size;
+ using std::vector<T>::max_size;
+ using std::vector<T>::resize;
+ using std::vector<T>::push_back;
+ using std::vector<T>::operator[];
+
+ /// The current length of the DynamicLengthHistory.
+ size_t Length;
+
+public:
+ /// Creates an instances by setting an initial length
+ DynamicLengthHistory(size_t Length) noexcept : Length(Length) {
+ this->resize(Length);
+ }
+
+ /// Destroys \p this object.
+ ~DynamicLengthHistory(void) = default;
+
+ /// Tells the retention policy applied to \p this object.
+ ///
+ /// \return \c rosa::agent::DynamicLengthHistory::P
+ static constexpr HistoryPolicy policyOfDynamicLengthHistory(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::DynamicLengthHistory::N
+ static constexpr size_t lengthOfHistory(void) noexcept { return max_size(); }
+
+ /// 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(); }
+
+ /// 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; }
+
+ /// 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 {
+ ASSERT(0 <= I && I < numberOfEntries()); // Boundary check.
+ return this->operator[](size() - I - 1);
+ }
+
+private:
+ /// Tells if the buffer is full.
+ ///
+ /// \return if the buffer is full.
+ bool full(void) const noexcept { return numberOfEntries() == Length; }
+
+ /// 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 {
+ if (full()) {
+ erase(begin());
+ }
+ push_back(V);
+ }
+
+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()) {
+ erase(end());
+ }
+ 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 *DynamicLengthHistory*
+ ///
+ /// \return trend set by analyzed entries
+ ///
+ /// \pre Statically, \p this object stores signed arithmetic values:\code
+ /// std::is_arithmetic<T>::value && std::is_signed<T>::value
+ /// \endcode Dynamically, \p D is a valid number of steps to take:\code
+ /// 0 <= D && D < Length
+ /// \endcode
+ template <typename X = T>
+ typename std::enable_if<
+ std::is_arithmetic<X>::value && std::is_signed<X>::value, X>::type
+ trend(const size_t D) const noexcept {
+ STATIC_ASSERT((std::is_same<X, T>::value), "not default template arg");
+ ASSERT(0 <= D && D < Length); // 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 *DynamicLengthHistory*
+ ///
+ /// \pre Statically, \p this object stores arithmetic values:\code
+ /// std::is_arithmetic<T>::value
+ /// \endcode Dynamically, \p D is a valid number of steps to take:\code
+ /// 0 <= D && D < Length
+ /// \endcode
+ template <typename X = T>
+ typename std::enable_if<std::is_arithmetic<X>::value, size_t>::type
+ averageAbsDiff(const size_t D) const noexcept {
+ STATIC_ASSERT((std::is_same<X, T>::value), "not default template arg");
+ ASSERT(0 <= D && D < Length); // 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;
+ }
+ }
+};
+
+/// 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 <typename T, HistoryPolicy P>
+DynamicLengthHistory<T, P> &operator<<(DynamicLengthHistory<T, P> &H,
+ const T &V) noexcept {
+ H.addEntry(V);
+ return H;
+}
} // End namespace agent
} // End namespace rosa
#endif // ROSA_AGENT_HISTORY_HPP
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Thu, Jul 3, 11:41 AM (1 d, 16 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
157229
Default Alt Text
(46 KB)
Attached To
Mode
R20 SoC_Rosa_repo
Attached
Detach File
Event Timeline
Log In to Comment