Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F386294
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Size
77 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/apps/ccam/ccam.cpp b/apps/ccam/ccam.cpp
index 7ae44de..76fc676 100644
--- a/apps/ccam/ccam.cpp
+++ b/apps/ccam/ccam.cpp
@@ -1,58 +1,116 @@
//===-- apps/ccam/ccam.cpp --------------------------------------*- C++ -*-===//
//
// The RoSA Framework -- Application CCAM
//
//===----------------------------------------------------------------------===//
///
/// \file apps/ccam/ccam.cpp
///
/// \author Maximilian Goetzinger (maximilian.goetzinger@tuwien.ac.at)
///
/// \date 2019
///
/// \brief The application CCAM implements the case study from the paper:
/// M. Goetzinger, N. TaheriNejad, H. A. Kholerdi, A. Jantsch, E. Willegger,
/// T. Glatzl, A.M. Rahmani, T.Sauter, P. Liljeberg: Model - Free Condition
/// Monitoring with Confidence
//===----------------------------------------------------------------------===//
#include "rosa/agent/FunctionAbstractions.hpp"
#include "rosa/agent/SignalStateDetector.hpp"
#include <iostream>
+using namespace rosa::agent;
+
int main(void) {
// Just some tests :D
std::vector vec = {7, 3, 5, 1, 9};
std::sort(vec.rbegin(), vec.rend());
// std::reverse(vec.begin(), vec.end());
for (auto it = vec.cbegin(); it != vec.cend(); ++it) {
std::cout << *it << ' ';
}
- std::shared_ptr<rosa::agent::PartialFunction<float, float>> PartFunc(
- new rosa::agent::PartialFunction<float, float>(
+ std::shared_ptr<PartialFunction<float, float>> PartFunc(
+ new PartialFunction<float, float>(
{
{{0.f, 3.f},
- std::make_shared<rosa::agent::LinearFunction<float, float>>(
- 0.f, 1.f / 3)},
+ std::make_shared<LinearFunction<float, float>>(0.f, 1.f / 3)},
{{3.f, 6.f},
- std::make_shared<rosa::agent::LinearFunction<float, float>>(
- 1.f, 0.f)},
+ std::make_shared<LinearFunction<float, float>>(1.f, 0.f)},
{{6.f, 9.f},
- std::make_shared<rosa::agent::LinearFunction<float, float>>(
- 3.f, -1.f / 3)},
+ std::make_shared<LinearFunction<float, float>>(3.f, -1.f / 3)},
},
0));
- std::shared_ptr<rosa::agent::StepFunction<float, float>> StepFunc(
- new rosa::agent::StepFunction<float, float>(1 / 10));
+ std::shared_ptr<StepFunction<float, float>> StepFunc(
+ new StepFunction<float, float>(1 / 10));
+
+ SignalStateDetector<float, float, float, HistoryPolicy::SRWF> TestSigSD(
+ 10000, PartFunc, PartFunc, StepFunc, StepFunc, PartFunc, PartFunc, 10, 5,
+ 1000);
- rosa::agent::SignalStateDetector<float, float> TestSigSD(
- PartFunc, PartFunc, StepFunc, StepFunc, PartFunc, PartFunc, 10, 5, 1000);
+ TestSigSD.detectSignalState(50.3f);
+ TestSigSD.detectSignalState(50.3f);
+ TestSigSD.detectSignalState(50.3f);
+ TestSigSD.detectSignalState(50.3f);
+ TestSigSD.detectSignalState(50.3f);
+ TestSigSD.detectSignalState(50.3f);
+ TestSigSD.detectSignalState(50.3f);
+ TestSigSD.detectSignalState(50.3f);
+ TestSigSD.detectSignalState(50.3f);
+ TestSigSD.detectSignalState(50.3f);
+ TestSigSD.detectSignalState(50.3f);
+ TestSigSD.detectSignalState(50.3f);
+ TestSigSD.detectSignalState(50.3f);
+ TestSigSD.detectSignalState(50.3f);
+ TestSigSD.detectSignalState(50.3f);
+ TestSigSD.detectSignalState(50.3f);
+ TestSigSD.detectSignalState(50.3f);
+ TestSigSD.detectSignalState(50.3f);
+ TestSigSD.detectSignalState(50.3f);
+ TestSigSD.detectSignalState(50.3f);
+ TestSigSD.detectSignalState(50.3f);
+ TestSigSD.detectSignalState(50.3f);
+ TestSigSD.detectSignalState(50.3f);
+ TestSigSD.detectSignalState(50.3f);
+ TestSigSD.detectSignalState(50.3f);
+ TestSigSD.detectSignalState(50.3f);
+ TestSigSD.detectSignalState(50.3f);
+ TestSigSD.detectSignalState(50.3f);
+ TestSigSD.detectSignalState(50.3f);
+ TestSigSD.detectSignalState(100.6f);
+ TestSigSD.detectSignalState(100.6f);
+ TestSigSD.detectSignalState(100.6f);
+ TestSigSD.detectSignalState(100.6f);
+ TestSigSD.detectSignalState(100.6f);
+ TestSigSD.detectSignalState(100.6f);
+ TestSigSD.detectSignalState(100.6f);
+ TestSigSD.detectSignalState(100.6f);
+ TestSigSD.detectSignalState(100.6f);
+ TestSigSD.detectSignalState(100.6f);
+ TestSigSD.detectSignalState(100.6f);
+ TestSigSD.detectSignalState(100.6f);
+ TestSigSD.detectSignalState(100.6f);
+ TestSigSD.detectSignalState(100.6f);
+ TestSigSD.detectSignalState(100.6f);
+ TestSigSD.detectSignalState(100.6f);
+ TestSigSD.detectSignalState(100.6f);
+ TestSigSD.detectSignalState(100.6f);
+ TestSigSD.detectSignalState(100.6f);
+ TestSigSD.detectSignalState(100.6f);
+ TestSigSD.detectSignalState(100.6f);
+ TestSigSD.detectSignalState(100.6f);
+ TestSigSD.detectSignalState(100.6f);
+ TestSigSD.detectSignalState(100.6f);
+ TestSigSD.detectSignalState(100.6f);
+ TestSigSD.detectSignalState(100.6f);
+ TestSigSD.detectSignalState(100.6f);
+ TestSigSD.detectSignalState(100.6f);
return 0;
}
diff --git a/include/rosa/agent/FunctionAbstractions.hpp b/include/rosa/agent/FunctionAbstractions.hpp
index bebfd77..a0f8585 100644
--- a/include/rosa/agent/FunctionAbstractions.hpp
+++ b/include/rosa/agent/FunctionAbstractions.hpp
@@ -1,351 +1,354 @@
//===-- 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/Abstraction.hpp"
#include "rosa/agent/Functionality.h"
#include "rosa/support/debug.hpp"
#include <algorithm>
#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> {
// Make sure the actual type arguments are matching our expectations.
STATIC_ASSERT((std::is_arithmetic<D>::value),
"LinearFunction not arithmetic T");
STATIC_ASSERT((std::is_arithmetic<R>::value),
"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 given the intercept and the coefficient of a linear
/// function.
///
/// \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),
Coefficient(Coefficient) {}
/// Creates an instance given the two points on a linear function.
///
/// \param x1 The x-value of the first point
/// \param y1 The x-value of the first point
/// \param x2 The y-value of the second point
/// \param y2 The y-value of the second point
LinearFunction(D x1, R y1, D x2, R y2) noexcept
: Abstraction<D, R>(y1 - x1 * (y1 - y2) / (x1 - x2),
(y1 - y2) / (x1 - x2)) {}
/// Creates an instance given the two points on a linear function.
///
/// \param p1 The coordinates of the first point
/// \param p2 The coordinates of the second point
LinearFunction(std::pair<D, R> p1, std::pair<D, R> p2) noexcept
: LinearFunction<D, R>(p1.first, p1.second, p2.first, p2.second) {}
/// 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;
}
/// Getter for member variable Intercept
///
/// \return Intercept
D getIntercept() const { return Intercept; }
/// Setter for member variable Intercept
///
/// \param Intercept the new Intercept
void setIntercept(const D &Intercept) { this->Intercept = Intercept; }
/// Getter for member variable Coefficient
///
/// \return Coefficient
D getCoefficient() const { return Coefficient; }
/// Setter for member variable Coefficient
///
/// \param Coefficient the new Intercept
void setCoefficient(const D &Coefficient) { this->Coefficient = Coefficient; }
/// Set Intercept and Coefficient from two points on the linear function
///
/// \param x1 The x-value of the first point
/// \param y1 The x-value of the first point
/// \param x2 The y-value of the second point
/// \param y2 The y-value of the second point
void setFromPoints(D x1, R y1, D x2, R y2) {
Coefficient = (y1 - y2) / (x1 - x2);
Intercept = y1 - Coefficient * x1;
}
/// Set Intercept and Coefficient from two points on the linear function
///
/// \param p1 The coordinates of the first point
/// \param p2 The coordinates of the second point
inline void setFromPoints(std::pair<D, R> p1, std::pair<D, R> p2) {
setFromPoints(p1.first, p1.second, p2.first, p2.second);
}
/// 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;
}
};
/// 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> {
// Make sure the actual type arguments are matching our expectations.
STATIC_ASSERT((std::is_arithmetic<D>::value),
"SineFunction not arithmetic T");
STATIC_ASSERT((std::is_arithmetic<R>::value),
"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) {}
/// 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;
}
};
/// Implements \c rosa::agent::PartialFunction as a step function from 0 to 1
/// with a ramp in between
///
/// \tparam D type of the functions domain
/// \tparam R type of the functions range
template <typename D, typename R>
class StepFunction : 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");
private:
D Coefficient;
D RightLimit;
public:
/// Creates an instance by Initializing the underlying \c Abstraction.
///
/// \param Coefficient Coefficient of the ramp
///
/// \pre Coefficient > 0
StepFunction(D Coefficient)
: Abstraction<D, R>(0), Coefficient(Coefficient),
RightLimit(1.0f / Coefficient) {
ASSERT(Coefficient > 0);
}
/// Destroys \p this object.
~StepFunction(void) = default;
/// Setter for Coefficient
///
/// \param Coefficient the new Coefficient
void setCoefficient(const D &Coefficient) {
ASSERT(Coefficient > 0);
this->Coefficient = Coefficient;
this->RightLimit = 1 / Coefficient;
}
/// Setter for RightLimit
///
/// \param RightLimit the new RightLimit
- void setRightLimit(const D &RightLimit) {
- ASSERT(RightLimit > 0);
- this->RightLimit = RightLimit;
- this->Coefficient = 1 / RightLimit;
+ //@Benedikt: I had to change the name of the parameter from RightLimit to
+ // RightLimit_, because otherwise there was a "warning treaded as error:
+ // warning: C4458: declaration of 'RightLimit' hides class member"
+ void setRightLimit(const D &RightLimit_) {
+ ASSERT(RightLimit_ > 0);
+ this->RightLimit = RightLimit_;
+ this->Coefficient = 1 / RightLimit_;
}
/// 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 is negative, true otherwise
bool isDefaultAt(const D &V) const noexcept override { return V > 0; }
/// Executes the Abstraction
///
/// \param V value to abstract
///
/// \return the abstracted value
R operator()(const D &V) const noexcept override {
if (V <= 0)
return 0;
if (V >= RightLimit)
return 1;
return V * Coefficient;
}
};
/// 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");
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)
: 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 2426f9b..f8d66d1 100644
--- a/include/rosa/agent/History.hpp
+++ b/include/rosa/agent/History.hpp
@@ -1,536 +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
LIFO ///< Last In First Out -- overwrite the latest entry with a new one
};
template <typename T, HistoryPolicy P> class History : public Functionality {
public:
History(void) noexcept {}
/// Destroys \p this object.
virtual ~History(void) = default;
/// Tells the retention policy applied to \p this object.
///
/// \return \c rosa::agent::History::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 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
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()
/// \endcode
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:
/// Pushes a new entry into the history.
///
/// \note The earliest entry gets overwritten if the history 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 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<T>::value && std::is_signed<T>::value
/// \endcode Dynamically, \p D is a valid number of steps to take:\code
/// 0 <= D && D < lengthOfHistory()
/// \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 < 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<T>::value
/// \endcode Dynamically, \p D is a valid number of steps to take:\code
/// 0 <= D && D < lengthOfHistory()
/// \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 < 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 <typename R> 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 <typename T, size_t N, HistoryPolicy P>
class StaticLengthHistory : public History<T, P>, 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:
using History<T, P>::policy;
using History<T, P>::empty;
using History<T, P>::full;
using History<T, P>::addEntry;
using History<T, P>::trend;
using History<T, P>::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<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];
}
/// 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 <typename T, size_t N, HistoryPolicy P>
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 History<T, P>, private std::vector<T> {
+ //@benedikt: if i dont make these public, I cannot iterate from outside
+ // through the history. E.g., "for (auto &SavedSignalState :
+ // DetectedSignalStates)" at line ~297 in "SignalStateDetector.hpp". Do you
+ // have an idea to make this in a better/more beautiful way?
+public:
// 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>::rbegin;
using std::vector<T>::rend;
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>::pop_back;
using std::vector<T>::operator[];
/// The current length of the DynamicLengthHistory.
size_t Length;
public:
using History<T, P>::policy;
using History<T, P>::empty;
using History<T, P>::full;
using History<T, P>::addEntry;
using History<T, P>::trend;
using History<T, P>::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()); }
/// Sort all entries in ascending order.
void sortAscending(void) noexcept { std::sort(begin(), end()); }
/// Sort all entries in descending order.
void sortDescending(void) noexcept { std::sort(rbegin(), rend()); }
+ /// Delets one element of the history.
+ ///
+ /// \param the element which shall be deleted.
+ // @benedikt: is this ok like that? should there be some "error handling"?
+ // checking if V is not null, or if V is member of the vector?
+ void deleteEntry(T &V) { erase(std::find(begin(), end(), V)); }
+
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 <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
diff --git a/include/rosa/agent/SignalState.hpp b/include/rosa/agent/SignalState.hpp
index dcbcba4..3a68820 100644
--- a/include/rosa/agent/SignalState.hpp
+++ b/include/rosa/agent/SignalState.hpp
@@ -1,420 +1,380 @@
//===-- rosa/agent/SignalState.hpp ------------------------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file rosa/agent/SignalState.hpp
///
/// \author Maximilian Götzinger (maximilian.goetzinger@tuwien.ac.at)
///
/// \date 2019
///
/// \brief Definition of *signal state* *functionality*.
///
//===----------------------------------------------------------------------===//
#ifndef ROSA_AGENT_SIGNALSTATE_HPP
#define ROSA_AGENT_SIGNALSTATE_HPP
#include "rosa/agent/FunctionAbstractions.hpp"
#include "rosa/agent/Functionality.h"
#include "rosa/agent/History.hpp"
+#include "rosa/support/math.hpp"
#include <cstdarg>
namespace rosa {
namespace agent {
/// Signal state conditions defining how the condition of a \c
/// rosa::agent::SignalState is saved in \c rosa::agent::SignalStateInformation.
enum class SignalStateCondition {
STABLE, ///< The signal state is stable
DRIFTING, ///< The signal state is drifting
UNKNOWN ///< The signal state is unknown
};
template <typename CONFDATATYPE> struct SignalStateInformation {
// Make sure the actual type arguments are matching our expectations.
STATIC_ASSERT((std::is_arithmetic<CONFDATATYPE>::value),
"confidence type is not to arithmetic");
/// The signal state ID saved as an unsigned integer number
unsigned int SignalStateID;
/// The SignalStateConfidence shows the overall confidence value of the signal
/// state.
CONFDATATYPE SignalStateConfidence;
/// The SignalStateCondition shows the condition of a signal state (stable or
/// drifting)
SignalStateCondition SignalStateCondition;
/// The SignalStateIsValid shows whether a signal state is valid or invalid.
/// In this context, valid means that enough samples which are in close
/// proximitry have been inserted into the signal state.
bool SignalStateIsValid;
/// The SignalStateJustGotValid shows whether a signal state got valid
/// (toggled from invalid to valid) during the current inserted sample.
bool SignalStateJustGotValid;
/// The SignalStateIsValidAfterReentrance shows whether a signal state is
/// valid after the variable changed back to it again.
bool SignalStateIsValidAfterReentrance;
};
-// @Benedikt: now there are 4 datatypes. Do you think we can merge PROCDATATYPE
-// and PROCDATATYPE somehow?
/// \tparam INDATATYPE type of input data, \tparam CONFDATATYPE type of
/// data in that the confidence values are given, \param PROCDATATYPE type of
/// the relative distance and the type of data in which DABs are saved.
template <typename INDATATYPE, typename CONFDATATYPE, typename PROCDATATYPE>
class SignalState : public Functionality {
// Make sure the actual type arguments are matching our expectations.
STATIC_ASSERT((std::is_arithmetic<INDATATYPE>::value),
"input data type not arithmetic");
STATIC_ASSERT((std::is_arithmetic<CONFDATATYPE>::value),
"confidence data type is not to arithmetic");
STATIC_ASSERT(
(std::is_arithmetic<PROCDATATYPE>::value),
"process data type (DAB and Relative Distance) is not to arithmetic");
private:
// For the convinience to write a shorter data type name
using PartFuncPointer =
std::shared_ptr<PartialFunction<INDATATYPE, CONFDATATYPE>>;
+ // @Benedikt: are INDATATYPE, CONFDATATYPE right here?
using StepFuncPointer =
std::shared_ptr<StepFunction<INDATATYPE, CONFDATATYPE>>;
/// SignalStateInfo is a struct SignalStateInformation that contains
/// information about the current state.
SignalStateInformation<CONFDATATYPE> SignalStateInfo;
/// The FuzzyFunctionSampleMatches is the fuzzy function that gives the
/// confidence how good the new sample matches another sample in the sample
/// history.
PartFuncPointer FuzzyFunctionSampleMatches;
/// The FuzzyFunctionSampleMismatches is the fuzzy function that gives the
/// confidence how bad the new sample matches another sample in the sample
/// history.
PartFuncPointer FuzzyFunctionSampleMismatches;
/// The FuzzyFunctionNumOfSamplesMatches is the fuzzy function that gives the
/// confidence how many samples from the sampe history match the new sample.
StepFuncPointer FuzzyFunctionNumOfSamplesMatches;
/// The FuzzyFunctionNumOfSamplesMismatches is the fuzzy function that gives
/// the confidence how many samples from the sampe history mismatch the new
/// sample.
StepFuncPointer FuzzyFunctionNumOfSamplesMismatches;
/// The FuzzyFunctionSignalIsDrifting is the fuzzy function that gives the
/// confidence how likely it is that the signal (resp. the state of a signal)
/// is drifting.
PartFuncPointer FuzzyFunctionSignalIsDrifting;
/// The FuzzyFunctionSignalIsStable is the fuzzy function that gives the
/// confidence how likely it is that the signal (resp. the state of a signal)
/// is stable (not drifting).
PartFuncPointer FuzzyFunctionSignalIsStable;
/// SampleHistory is a history in that the last sample values are stored.
DynamicLengthHistory<INDATATYPE, HistoryPolicy::FIFO> SampleHistory;
/// DAB is a (usually) small history of the last sample values of which a
/// average is calculated if the DAB is full.
DynamicLengthHistory<INDATATYPE, HistoryPolicy::SRWF> DAB;
/// DABHistory is a history in that the last DABs (to be exact, the averages
/// of the last DABs) are stored.
DynamicLengthHistory<PROCDATATYPE, HistoryPolicy::LIFO> DABHistory;
/// The SignalStateIsValid shows whether a signal state is valid or invalid.
/// In this context, valid means that enough samples which are in close
/// proximitry have been inserted into the signal state.
bool SignalStateIsValid;
/// The SignalStateIsValidAfterReentrance shows whether a signal state is
/// valid after the variable changed back to it again.
bool SignalStateIsValidAfterReentrance;
public:
// @Maxi doxygen per default doesn't display private attributes of a class. So
// I copied them to the constructor. So the user has more information.
/// Creates an instance by setting all parameters
/// \param SignalStateID The Id of the SignalStateinfo \c
/// SignalStateInformation.
///
/// \param FuzzyFunctionSampleMatches The FuzzyFunctionSampleMatches is the
/// fuzzy function that gives the confidence how good the new sample matches
/// another sample in the sample history.
///
/// \param FuzzyFunctionSampleMismatches The FuzzyFunctionSampleMismatches is
/// the fuzzy function that gives the confidence how bad the new sample
/// matches another sample in the sample history.
///
/// \param FuzzyFunctionNumOfSamplesMatches The
/// FuzzyFunctionNumOfSamplesMatches is the fuzzy function that gives the
/// confidence how many samples from the sampe history match the new sample.
///
/// \param FuzzyFunctionNumOfSamplesMismatches The
/// FuzzyFunctionNumOfSamplesMismatches is the fuzzy function that gives the
/// confidence how many samples from the sampe history mismatch the new
/// sample.
///
/// \param FuzzyFunctionSignalIsDrifting The FuzzyFunctionSignalIsDrifting is
/// the fuzzy function that gives the confidence how likely it is that the
/// signal (resp. the state of a signal) is drifting.
///
/// \param FuzzyFunctionSignalIsStable The FuzzyFunctionSignalIsStable is the
/// fuzzy function that gives the confidence how likely it is that the signal
/// (resp. the state of a signal) is stable (not drifting).
///
/// \param SampleHistorySize Size of the Sample History \c
/// DynamicLengthHistory . SampleHistory is a history in that the last sample
/// values are stored.
///
/// \param DABSize Size of DAB \c DynamicLengthHistory . DAB is a (usually)
/// small history of the last sample values of which a average is calculated
/// if the DAB is full.
///
/// \param DABHistorySize Size of the DABHistory \c DynamicLengthHistory .
/// DABHistory is a history in that the last DABs (to be exact, the averages
/// of the last DABs) are stored.
///
- SignalState(unsigned int SignalStateID,
+ SignalState(unsigned int SignalStateID, unsigned int SampleHistorySize,
+ unsigned int DABSize, unsigned int DABHistorySize,
PartFuncPointer FuzzyFunctionSampleMatches,
PartFuncPointer FuzzyFunctionSampleMismatches,
StepFuncPointer FuzzyFunctionNumOfSamplesMatches,
StepFuncPointer FuzzyFunctionNumOfSamplesMismatches,
PartFuncPointer FuzzyFunctionSignalIsDrifting,
- PartFuncPointer FuzzyFunctionSignalIsStable,
- unsigned int SampleHistorySize, unsigned int DABSize,
- unsigned int DABHistorySize) noexcept
- : SignalStateInfo(SignalStateID, 0, SignalStateCondition::UNKNOWN, false,
- false),
+ PartFuncPointer FuzzyFunctionSignalIsStable) noexcept
+ : SignalStateInfo{SignalStateID, 0, SignalStateCondition::UNKNOWN,
+ false, false, false},
SampleHistory(SampleHistorySize), DAB(DABSize),
DABHistory(DABHistorySize),
FuzzyFunctionSampleMatches(FuzzyFunctionSampleMatches),
FuzzyFunctionSampleMismatches(FuzzyFunctionSampleMismatches),
FuzzyFunctionNumOfSamplesMatches(FuzzyFunctionNumOfSamplesMatches),
FuzzyFunctionNumOfSamplesMismatches(
FuzzyFunctionNumOfSamplesMismatches),
FuzzyFunctionSignalIsDrifting(FuzzyFunctionSignalIsDrifting),
FuzzyFunctionSignalIsStable(FuzzyFunctionSignalIsStable) {}
/// Destroys \p this object.
~SignalState(void) = default;
void leaveSignalState(void) noexcept {
DAB.clear();
SignalStateIsValidAfterReentrance = false;
}
SignalStateInformation<CONFDATATYPE>
insertSample(INDATATYPE Sample) noexcept {
SampleHistory.addEntry(Sample);
DAB.addEntry(Sample);
if (DAB.full()) {
PROCDATATYPE AvgOfDAB = DAB.template average<PROCDATATYPE>();
DABHistory.addEntry(AvgOfDAB);
DAB.clear();
}
+ //@Benedikt: Do I really have to cast here?
FuzzyFunctionNumOfSamplesMatches->setRightLimit(
- SampleHistory->numberOfEntries());
+ static_cast<INDATATYPE>(SampleHistory.numberOfEntries()));
FuzzyFunctionNumOfSamplesMismatches->setRightLimit(
- SampleHistory->numberOfEntries());
+ static_cast<INDATATYPE>(SampleHistory.numberOfEntries()));
// TODO: calculate whether signal state is valid and properly set
// SignalStateIsValid, SignalStateJustGotValid,
// SignalStateIsValidAfterReentrance
// TODO: check current signal state whether it drifts
// TODO: write in SignalStateInfo
return SignalStateInfo;
}
/// Gives the confidence how likely the new sample matches the signal state.
///
/// \param Sample is the actual sample of the observed signal.
///
/// \return the confidence of the new sample is matching the signal state.
CONFDATATYPE
confidenceSampleMatchesSignalState(INDATATYPE Sample) noexcept {
CONFDATATYPE ConfidenceOfBestCase = 0;
DynamicLengthHistory<PROCDATATYPE, HistoryPolicy::FIFO>
- RelativeDistanceHistory;
+ RelativeDistanceHistory(SampleHistory.maxLength());
// calculate distances to all history samples
for (auto &HistorySample : SampleHistory) {
- PROCDATATYPE RelativeDistance = relativeDistance(Sample, HistorySample);
+ PROCDATATYPE RelativeDistance =
+ relativeDistance<INDATATYPE, PROCDATATYPE>(Sample, HistorySample);
RelativeDistanceHistory.addEntry(RelativeDistance);
}
// sort all calculated distances so that the lowest distance (will get the
// highest confidence) is at the beginning.
RelativeDistanceHistory.sortAscending();
CONFDATATYPE ConfidenceOfWorstFittingSample = 1;
// Case 1 means that one (the best fitting) sample of the history is
// compared with the new sample. Case 2 means the two best history samples
// are compared with the new sample. And so on.
// TODO (future): to accelerate -> don't start with 1 start with some higher
// number because a low number (i guess lower than 5) will definetely lead
// to a low confidence. except the history is not full.
for (unsigned int Case = 0;
Case < RelativeDistanceHistory.numberOfEntries(); Case++) {
CONFDATATYPE ConfidenceFromRelativeDistance;
if (std::isinf(RelativeDistanceHistory[Case])) {
// TODO (future) if fuzzy is defined in a way that infinity is not 0 it
// would be a problem
//@benedikt: check if your partialfunctions can take infinity as
// argument
+ //@benedikt: same as before "->operator()"
ConfidenceFromRelativeDistance = 0;
} else {
- ConfidenceFromRelativeDistance =
- FuzzyFunctionSampleMatches(RelativeDistanceHistory[Case]);
+ ConfidenceFromRelativeDistance = FuzzyFunctionSampleMatches->operator()(
+ RelativeDistanceHistory[Case]);
}
- ConfidenceOfWorstFittingSample = fuzzyAND(ConfidenceOfWorstFittingSample,
- ConfidenceFromRelativeDistance);
-
- // @benedikt: change old-style cast to one of these: reinterpret_cast,
- // static_cast, dynamic_cast or const_cast. Which should I use? Or should
- // the HistSampleCounter variable already be CONFDATATYPE type?
- ConfidenceOfBestCase = fuzzyOR(
- ConfidenceOfBestCase,
- fuzzyAND(ConfidenceOfWorstFittingSample,
- FuzzyFunctionNumOfSamplesMatches((CONFDATATYPE)Case + 1)));
+ ConfidenceOfWorstFittingSample = fuzzyAND<CONFDATATYPE>(
+ 2, ConfidenceOfWorstFittingSample, ConfidenceFromRelativeDistance);
+ //@benedikt: do i have to pass the number 2 to tell the function how many
+ // arguments are following?
+ //@benedikt: same as before with "->operator()"
+ ConfidenceOfBestCase = fuzzyOR<CONFDATATYPE>(
+ 2, ConfidenceOfBestCase,
+ fuzzyAND<CONFDATATYPE>(2, ConfidenceOfWorstFittingSample,
+ FuzzyFunctionNumOfSamplesMatches->operator()(
+ static_cast<CONFDATATYPE>(Case) + 1)));
}
return ConfidenceOfBestCase;
}
/// Gives the confidence how likely the new sample mismatches the signal
/// state.
///
/// \param Sample is the actual sample of the observed signal.
///
/// \return the confidence of the new sample is mismatching the signal state.
CONFDATATYPE
confidenceSampleMismatchesSignalState(INDATATYPE Sample) noexcept {
float ConfidenceOfWorstCase = 1;
DynamicLengthHistory<PROCDATATYPE, HistoryPolicy::FIFO>
- RelativeDistanceHistory;
+ RelativeDistanceHistory(SampleHistory.maxLength());
// calculate distances to all history samples
for (auto &HistorySample : SampleHistory) {
- RelativeDistanceHistory.addEntry(relativeDistance(Sample, HistorySample));
+ RelativeDistanceHistory.addEntry(
+ relativeDistance<INDATATYPE, PROCDATATYPE>(Sample, HistorySample));
}
// sort all calculated distances so that the highest distance (will get the
// lowest confidence) is at the beginning.
RelativeDistanceHistory.sortDescending();
CONFDATATYPE ConfidenceOfBestFittingSample = 0;
- unsigned int Case = 1;
// Case 1 means that one (the worst fitting) sample of the history is
// compared with the new sample. Case 2 means the two worst history samples
// are compared with the new sample. And so on.
// TODO (future): to accelerate -> don't go until end. Confidences will only
// get higher. See comment in "CONFDATATYPE
// confidenceSampleMatchesSignalState(INDATATYPE Sample)".
for (unsigned int Case = 0;
Case < RelativeDistanceHistory.numberOfEntries(); Case++) {
CONFDATATYPE ConfidenceFromRelativeDistance;
if (std::isinf(RelativeDistanceHistory[Case])) {
ConfidenceFromRelativeDistance = 1;
} else {
+ //@benedikt: I had to change the following line. The outcommented line
+ // was the original one. I think it is ugly like that (new line). Do you
+ // have an idea how to make it better/more beautiful?
ConfidenceFromRelativeDistance =
- FuzzyFunctionSampleMismatches(RelativeDistanceHistory[Case]);
+ FuzzyFunctionSampleMismatches->operator()(
+ RelativeDistanceHistory[Case]);
+ // FuzzyFunctionSampleMismatches(RelativeDistanceHistory[Case]);
}
- ConfidenceOfBestFittingSample = fuzzyOR(ConfidenceOfBestFittingSample,
- ConfidenceFromRelativeDistance);
-
- // @benedikt: change old-style cast to one of these: reinterpret_cast,
- // static_cast, dynamic_cast or const_cast. Which should I use? Or should
- // the HistSampleCounter variable already be CONFDATATYPE type?
- ConfidenceOfWorstCase = fuzzyAND(
- ConfidenceOfWorstCase,
- fuzzyOR(ConfidenceOfBestFittingSample,
- FuzzyFunctionNumOfSamplesMismatches((CONFDATATYPE)Case + 1)));
+ //@benedikt: do i have to pass the number 2 to tell the function how many
+ // arguments are following?
+ ConfidenceOfBestFittingSample = fuzzyOR<CONFDATATYPE>(
+ 2, ConfidenceOfBestFittingSample, ConfidenceFromRelativeDistance);
+
+ //@benedikt: do i have to pass the number 2 to tell the function how many
+ // arguments are following?
+ //@benedikt: same as before with "->operator()"
+ ConfidenceOfWorstCase = fuzzyAND<CONFDATATYPE>(
+ 2, ConfidenceOfWorstCase,
+ fuzzyOR<CONFDATATYPE>(2, ConfidenceOfBestFittingSample,
+ FuzzyFunctionNumOfSamplesMismatches->operator()(
+ static_cast<CONFDATATYPE>(Case) + 1)));
}
return ConfidenceOfWorstCase;
}
/// Gives information about the current signal state.
///
/// \return a struct SignalStateInformation that contains information about
/// the current signal state.
SignalStateInformation<CONFDATATYPE> signalStateInformation(void) noexcept {
return SignalStateInfo;
}
-
-private:
- // @David: Where should these next functions (fuzzyAND, fuzzyOR,
- // relativeDistance) moved to (I guess we will use them also somewhere else)?
-
- // copied from the internet and adapted
- // (https://stackoverflow.com/questions/1657883/variable-number-of-arguments-in-c)
- CONFDATATYPE fuzzyAND(int n_args, ...) noexcept {
- va_list ap;
- va_start(ap, n_args);
- CONFDATATYPE min = va_arg(ap, CONFDATATYPE);
- for (int i = 2; i <= n_args; i++) {
- CONFDATATYPE a = va_arg(ap, CONFDATATYPE);
- min = std::min(a, min);
- }
- va_end(ap);
- return min;
- }
-
- // copied from the internet
- // (https://stackoverflow.com/questions/1657883/variable-number-of-arguments-in-c)
- CONFDATATYPE fuzzyOR(int n_args, ...) noexcept {
- va_list ap;
- va_start(ap, n_args);
- CONFDATATYPE max = va_arg(ap, CONFDATATYPE);
- for (int i = 2; i <= n_args; i++) {
- CONFDATATYPE a = va_arg(ap, CONFDATATYPE);
- std::max(a, max);
- }
- va_end(ap);
- return max;
- }
-
- PROCDATATYPE relativeDistance(INDATATYPE SampleValue,
- INDATATYPE HistoryValue) noexcept {
- PROCDATATYPE Dist = HistoryValue - SampleValue;
-
- if (Dist == 0) {
- return 0;
- } else {
- Dist = Dist / SampleValue;
- if (Dist < 0) {
- //@benedikt: I guess this multiplication here should not be done because
- // it could be that the distance fuzzy functions are not symetrical
- //(negative and positive side)
- Dist = Dist * (-1);
- }
- return (Dist);
- }
- }
};
} // End namespace agent
} // End namespace rosa
#endif // ROSA_AGENT_SIGNALSTATE_HPP
diff --git a/include/rosa/agent/SignalStateDetector.hpp b/include/rosa/agent/SignalStateDetector.hpp
index b306ede..d1ebec5 100644
--- a/include/rosa/agent/SignalStateDetector.hpp
+++ b/include/rosa/agent/SignalStateDetector.hpp
@@ -1,367 +1,357 @@
//===-- rosa/agent/SignalStateDetector.hpp ----------------------------*- C++
//-*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file rosa/agent/SignalStateDetector.hpp
///
/// \author Maximilian Götzinger (maximilian.goetzinger@tuwien.ac.at)
///
/// \date 2019
///
/// \brief Definition of *signal state detector* *functionality*.
///
//===----------------------------------------------------------------------===//
#ifndef ROSA_AGENT_SIGNALSTATEDETECTOR_HPP
#define ROSA_AGENT_SIGNALSTATEDETECTOR_HPP
#include "rosa/agent/FunctionAbstractions.hpp"
#include "rosa/agent/Functionality.h"
#include "rosa/agent/SignalState.hpp"
#include <vector>
namespace rosa {
namespace agent {
/// Implements \c rosa::agent::SignalStateDetector as a functionality that
/// detects signal states given on input samples.
///
/// \note This implementation is supposed to be used for samples of an
/// arithmetic type.
///
-/// \tparam INDATATYPE is the type of input data, \tparam CONFDATATYPE is type
-/// of
-/// data in that the confidence values are given
-template <typename INDATATYPE, typename CONFDATATYPE>
+/// \tparam INDATATYPE type of input data, \tparam CONFDATATYPE type of
+/// data in that the confidence values are given, \param PROCDATATYPE type of
+/// the relative distance and the type of data in which DABs are saved.
+template <typename INDATATYPE, typename CONFDATATYPE, typename PROCDATATYPE,
+ HistoryPolicy HP>
class SignalStateDetector : public Functionality {
// Make sure the actual type arguments are matching our expectations.
STATIC_ASSERT((std::is_arithmetic<INDATATYPE>::value),
"input data type not arithmetic");
STATIC_ASSERT((std::is_arithmetic<CONFDATATYPE>::value),
"confidence abstraction type is not to arithmetic");
private:
// For the convinience to write a shorter data type name
using PartFuncPointer =
std::shared_ptr<PartialFunction<INDATATYPE, CONFDATATYPE>>;
using StepFuncPointer =
std::shared_ptr<StepFunction<INDATATYPE, CONFDATATYPE>>;
- using SignalStatePtr = std::shared_ptr<SignalStateInformation<CONFDATATYPE>>;
- using SignalStateInfoPtr =
- std::shared_ptr<SignalStateInformation<CONFDATATYPE>>;
+ using SignalStatePtr =
+ std::shared_ptr<SignalState<INDATATYPE, CONFDATATYPE, PROCDATATYPE>>;
/// The NextSignalStateID is a counter variable which stores the ID which the
/// next signal state shall have.
unsigned int NextSignalStateID;
/// The SignalStateHasChanged is a flag that show whether a signal has changed
/// its state.
bool SignalStateHasChanged;
/// The CurrentSignalState is a pointer to the (saved) signal state in which
/// the actual variable (signal) of the observed system is.
SignalStatePtr CurrentSignalState;
/// The DetectedSignalStates is vector in that all detected signal states are
/// saved.
- // TODO: make it to history
- DynamicLengthHistory<SignalStatePtr, HistoryPolicy::SRWF>
- DetectedSignalStates;
- // std::vector<SignalStatePtr> DetectedSignalStates;
+ DynamicLengthHistory<SignalStatePtr, HP> DetectedSignalStates;
/// The FuzzyFunctionSampleMatches is the fuzzy function that gives the
/// confidence how good the new sample matches another sample in the sample
/// history.
PartFuncPointer FuzzyFunctionSampleMatches;
/// The FuzzyFunctionSampleMismatches is the fuzzy function that gives the
/// confidence how bad the new sample matches another sample in the sample
/// history.
PartFuncPointer FuzzyFunctionSampleMismatches;
/// The FuzzyFunctionNumOfSamplesMatches is the fuzzy function that gives the
/// confidence how many samples from the sampe history match the new sample.
StepFuncPointer FuzzyFunctionNumOfSamplesMatches;
/// The FuzzyFunctionNumOfSamplesMismatches is the fuzzy function that gives
/// the confidence how many samples from the sampe history mismatch the new
/// sample.
StepFuncPointer FuzzyFunctionNumOfSamplesMismatches;
/// The FuzzyFunctionSignalIsDrifting is the fuzzy function that gives the
/// confidence how likely it is that the signal is drifting.
PartFuncPointer FuzzyFunctionSignalIsDrifting;
/// The FuzzyFunctionSignalIsStable is the fuzzy function that gives the
/// confidence how likely it is that the signal is stable (not drifting).
PartFuncPointer FuzzyFunctionSignalIsStable;
/// SampleHistorySize is the (maximum) size of the sample history.
unsigned int SampleHistorySize;
/// DABSize the size of a DAB (Discrete Average Block).
unsigned int DABSize;
/// DABHistorySize is the (maximum) size of the DAB history.
unsigned int DABHistorySize;
public:
/// Creates an instance by setting all parameters
/// \param FuzzyFunctionSampleMatches The FuzzyFunctionSampleMatches is the
/// fuzzy function that gives the confidence how good the new sample matches
/// another sample in the sample history.
///
/// \param FuzzyFunctionSampleMismatches The FuzzyFunctionSampleMismatches is
/// the fuzzy function that gives the confidence how bad the new sample
/// matches another sample in the sample history.
///
/// \param FuzzyFunctionNumOfSamplesMatches The
/// FuzzyFunctionNumOfSamplesMatches is the fuzzy function that gives the
/// confidence how many samples from the sampe history match the new sample.
///
/// \param FuzzyFunctionNumOfSamplesMismatches The
/// FuzzyFunctionNumOfSamplesMismatches is the fuzzy function that gives the
/// confidence how many samples from the sampe history mismatch the new
/// sample.
///
/// \param FuzzyFunctionSignalIsDrifting The FuzzyFunctionSignalIsDrifting is
/// the fuzzy function that gives the confidence how likely it is that the
/// signal (resp. the state of a signal) is drifting.
///
/// \param FuzzyFunctionSignalIsStable The FuzzyFunctionSignalIsStable is the
/// fuzzy function that gives the confidence how likely it is that the signal
/// (resp. the state of a signal) is stable (not drifting).
///
/// \param SampleHistorySize Sets the History size which will be used by \c
/// SignalState.
///
/// \param DABSize Sets the DAB size which will be used by \c SignalState.
///
/// \param DABHistorySize Sets the size which will be used by \c SignalState.
///
- SignalStateDetector(PartFuncPointer FuzzyFunctionSampleMatches,
+ SignalStateDetector(unsigned int MaximumNumberOfSignalStates,
+ PartFuncPointer FuzzyFunctionSampleMatches,
PartFuncPointer FuzzyFunctionSampleMismatches,
StepFuncPointer FuzzyFunctionNumOfSamplesMatches,
StepFuncPointer FuzzyFunctionNumOfSamplesMismatches,
PartFuncPointer FuzzyFunctionSignalIsDrifting,
PartFuncPointer FuzzyFunctionSignalIsStable,
unsigned int SampleHistorySize, unsigned int DABSize,
unsigned int DABHistorySize) noexcept
: NextSignalStateID(1), SignalStateHasChanged(false),
CurrentSignalState(NULL),
FuzzyFunctionSampleMatches(FuzzyFunctionSampleMatches),
FuzzyFunctionSampleMismatches(FuzzyFunctionSampleMismatches),
FuzzyFunctionNumOfSamplesMatches(FuzzyFunctionNumOfSamplesMatches),
FuzzyFunctionNumOfSamplesMismatches(
FuzzyFunctionNumOfSamplesMismatches),
FuzzyFunctionSignalIsDrifting(FuzzyFunctionSignalIsDrifting),
FuzzyFunctionSignalIsStable(FuzzyFunctionSignalIsStable),
SampleHistorySize(SampleHistorySize), DABSize(DABSize),
- DABHistorySize(DABHistorySize) {}
+ DABHistorySize(DABHistorySize),
+ DetectedSignalStates(MaximumNumberOfSignalStates) {}
/// Destroys \p this object.
~SignalStateDetector(void) = default;
/// Detects a signal state to which the new sample belongs or create a new
/// signal state if the new sample does not match to any of the saved signal
/// states.
///
/// \param Sample is the actual sample of the observed signal.
///
/// \return the signal state ID as unsigend integer type. Signal state IDs
/// start with number 1; that means if there is no current signal state, the
/// return value is 0.
unsigned int detectSignalState(INDATATYPE Sample) noexcept {
- SignalStateInfoPtr SignalStateInfo = detectSignalState__debug(Sample);
- return SignalStateInfo->SignalStateID;
+ SignalStateInformation<CONFDATATYPE> SignalStateInfo =
+ detectSignalState__debug(Sample);
+ return SignalStateInfo.SignalStateID;
}
/// Gives information about the current signal state.
///
/// \return a the signal state ID (as unsigned integer type) of the current
/// signal state. Signal state IDs start with number 1; that means if there is
/// no current signal state, the return value is 0.
unsigned int currentSignalStateInformation(void) noexcept {
- SignalStateInfoPtr SignalStateInfo = currentSignalStateInformation__debug();
+ SignalStateInformation<CONFDATATYPE> SignalStateInfo =
+ currentSignalStateInformation__debug();
if (SignalStateInfo) {
- return SignalStateInfo->SignalStateID;
+ return SignalStateInfo.SignalStateID;
} else {
return 0;
}
}
/// Gives information whether a signal state change has happened or not.
///
/// \return true if a signal state change has happened, and false if not.
bool signalStateHasChanged(void) noexcept { return SignalStateHasChanged; }
private:
- // TODO: change exlaination! it is not totally right
- //@maxi \param is there to Document a specific parameter of a method/function
- // this method doesn't have any parameters.
/// Creates a new signal state and adds it to the signal state vector in which
/// all known states are saved.
///
- /// \param SampleHistorySize the (maximum) size of the sample history.
- /// \param DABSize the size of a DAB.
- /// \param DABHistorySize the (maximum) size of the DAB history.
- /// \param FuzzyFunctionSampleMatches the
- /// \param FuzzyFunctionSampleMismatches
- /// \param FuzzyFunctionNumOfSamplesMatches
- /// \param FuzzyFunctionNumOfSamplesMismatches
- ///
/// \return a pointer to the newly created signal state or NULL if no state
/// could be created.
SignalStatePtr createNewSignalState(void) noexcept {
- SignalStatePtr S = new (std::nothrow) SignalState(
+ SignalStatePtr S(new SignalState<INDATATYPE, CONFDATATYPE, PROCDATATYPE>(
NextSignalStateID, SampleHistorySize, DABSize, DABHistorySize,
FuzzyFunctionSampleMatches, FuzzyFunctionSampleMismatches,
- FuzzyFunctionNumOfSamplesMatches, FuzzyFunctionNumOfSamplesMismatches);
+ FuzzyFunctionNumOfSamplesMatches, FuzzyFunctionNumOfSamplesMismatches,
+ FuzzyFunctionSignalIsDrifting, FuzzyFunctionSignalIsStable));
// @benedikt: todo: assert in history, which checks if push_back worked
DetectedSignalStates.addEntry(S);
return S;
}
#ifdef SIGNALSTATEDETECTORDEBUGMODE
public:
#else
private:
#endif // SIGNALSTATEDETECTORDEBUGMODE
// @maxi is this a debug method or is it a method that will be used and
// you simply want to have access to it in debug mode?
// debug -> extend the preprocessor around the function
// access -> remove the __debug from the name ( it is confusing)
// if you want to have it marked as a debug method for auto
// complete you can do something like this :
//
//#ifdef STATEDETECTORDEBUGMODE
// public:
// StateInfoPtr debug_detectState(INDATATYPE Sample) {
// return detectState(Sample);
// }
//#endif // STATEDETECTORDEBUGMODE
// private :
// StateInfoPtr detectState(INDATATYPE Sample) { ...
//
/// Detects the signal state to which the new sample belongs or create a new
/// signal state if the new sample does not match to any of the saved states.
///
/// \param Sample is the actual sample of the observed signal.
///
/// \return the information of the current signal state (signal state ID and
/// other
/// parameters).
// TODO: return something const.. cannot remember exactly (ask benedikt)
//
// maybe: you are returning a pointer to the state info so who ever has that
// pointer can actually change the information if you want to return only the
// *current info* return a copy of the state info
// like this:
//
// StateInfoPtr detectState__debug(INDATATYPE Sample) ->
// StateInformation<CONFTYPE> detectState__debug(INDATATYPE Sample)
//
// return CurrentState->stateInformation(); ->
// return *(CurrentState->stateInformation());
- SignalStateInfoPtr detectSignalState__debug(INDATATYPE Sample) noexcept {
+ SignalStateInformation<CONFDATATYPE>
+ detectSignalState__debug(INDATATYPE Sample) noexcept {
if (!CurrentSignalState) {
ASSERT(DetectedSignalStates.empty());
SignalStatePtr S = createNewSignalState();
CurrentSignalState = S;
} else {
CONFDATATYPE ConfidenceSampleMatchesSignalState =
- CurrentSignalState->confSampleMatchesSignalState(Sample);
+ CurrentSignalState->confidenceSampleMatchesSignalState(Sample);
CONFDATATYPE ConfidenceSampleMismatchesSignalState =
- CurrentSignalState->confSampleMismatchesSignalState(Sample);
+ CurrentSignalState->confidenceSampleMismatchesSignalState(Sample);
if (ConfidenceSampleMatchesSignalState >
ConfidenceSampleMismatchesSignalState) {
SignalStateHasChanged = false;
} else {
SignalStateHasChanged = true;
- if (CurrentSignalState->signalStateInformation()->SignalStateIsValid) {
+ if (CurrentSignalState->signalStateInformation().SignalStateIsValid) {
CurrentSignalState->leaveSignalState();
} else {
//@benedikt: changed from vector to history. can i still do the next
// line?
- DetectedSignalStates.erase(std::find(DetectedSignalStates.begin(),
- DetectedSignalStates.end(),
- CurrentSignalState));
+ DetectedSignalStates.deleteEntry(CurrentSignalState);
}
// TODO (future): additionally save averages to enable fast iteration
// through recorded signl state history (maybe sort vector based on
// these
// average values)
CurrentSignalState = nullptr;
//@benedikt: same question
for (auto &SavedSignalState : DetectedSignalStates) {
if (SavedSignalState != CurrentSignalState) {
- CONFDATATYPE ConfidenceSampleMatchesSignalState =
- SavedSignalState->confSampleMatchesSignalState(Sample);
- CONFDATATYPE ConfidenceSampleMismatchesSignalState =
- SavedSignalState->confSampleMismatchesSignalState(Sample);
+ ConfidenceSampleMatchesSignalState =
+ SavedSignalState->confidenceSampleMatchesSignalState(Sample);
+ ConfidenceSampleMismatchesSignalState =
+ SavedSignalState->confidenceSampleMismatchesSignalState(Sample);
if (ConfidenceSampleMatchesSignalState >
ConfidenceSampleMismatchesSignalState) {
// TODO (future): maybe it would be better to compare
// ConfidenceSampleMatchesSignalState of all signal states in the
// vector in order to find the best matching signal state.
CurrentSignalState = SavedSignalState;
break;
}
}
}
if (!CurrentSignalState) {
SignalStatePtr S = createNewSignalState();
CurrentSignalState = S;
}
}
}
SignalStateInformation<CONFDATATYPE> SignalStateInfo =
CurrentSignalState->insertSample(Sample);
if (SignalStateInfo.SignalStateJustGotValid) {
NextSignalStateID++;
}
return SignalStateInfo;
}
#ifdef SIGNALSTATEDETECTORDEBUGMODE
public:
#else
private:
#endif // SIGNALSTATEDETECTORDEBUGMODE
/// Gives information about the current signal state.
///
/// \return a struct SignalStateInformation that contains information about
/// the
/// current signal state or NULL if no current signal state exists.
SignalStateInformation<CONFDATATYPE>
currentSignalStateInformation__debug(void) noexcept {
if (CurrentSignalState) {
return CurrentSignalState->signalStateInformation();
} else {
return NULL;
}
}
};
} // End namespace agent
} // End namespace rosa
#endif // ROSA_AGENT_SIGNALSTATEDETECTOR_HPP
diff --git a/include/rosa/support/math.hpp b/include/rosa/support/math.hpp
index f88a13d..705a08c 100644
--- a/include/rosa/support/math.hpp
+++ b/include/rosa/support/math.hpp
@@ -1,57 +1,131 @@
//===-- rosa/support/math.hpp -----------------------------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file rosa/support/math.hpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017
///
/// \brief Math helpers.
///
//===----------------------------------------------------------------------===//
#ifndef ROSA_SUPPORT_MATH_HPP
#define ROSA_SUPPORT_MATH_HPP
+#include <algorithm>
#include <cmath>
+#include <cstdarg>
#include <cstdlib>
#include <limits>
#include <type_traits>
namespace rosa {
/// Computes log base 2 of a number.
///
/// \param N the number to compute log base 2 for
///
/// \return log base 2 of \p N
constexpr size_t log2(const size_t N) {
return ((N < 2) ? 1 : 1 + log2(N / 2));
}
/// Tells the next representable floating point value.
///
/// \tparam T type to operate on
///
/// \note The second type argument enforces \p T being a floating point type,
/// always use the default value!
///
/// \param V value to which find the next representable one
///
/// \return the next representable value of type \p T after value \p V
///
/// \pre Type \p T must be a floating point type, which is enforced by
/// `std::enable_if` in the second type argument.
template <typename T,
typename = std::enable_if_t<std::is_floating_point<T>::value>>
T nextRepresentableFloatingPoint(const T V) {
return std::nextafter(V, std::numeric_limits<T>::infinity());
}
+// copied from the internet and adapted
+// (https://stackoverflow.com/questions/1657883/variable-number-of-arguments-in-c)
+/// Conjuncts two or more values with each other.
+///
+/// \param two or more values of the same datatype
+///
+/// \return the conjunction of the values given as parameter.
+template <typename CONFDATATYPE>
+CONFDATATYPE fuzzyAND(int n_args, ...) noexcept {
+ // TODO: check datatype, if there are at least two arguments, and if they are
+ // between 0 and 1
+ // David suggests: nstead of a variadic argument, you could pass the values as
+ // an std::array (with a template argument for the length). When you pass the
+ // values as a container, you can simply use std::max_element and
+ // std::min_element to have a one-liner implementation of the these fuzzy
+ // functions.
+ va_list ap;
+ va_start(ap, n_args);
+ CONFDATATYPE min = va_arg(ap, CONFDATATYPE);
+ for (int i = 2; i <= n_args; i++) {
+ CONFDATATYPE a = va_arg(ap, CONFDATATYPE);
+ min = std::min(a, min);
+ }
+ va_end(ap);
+ return min;
+}
+
+/// Disjuncts two or more values with each other.
+///
+/// \param two or more values of the same datatype
+///
+/// \return the disjunction of the values given as parameter.
+// copied from the internet
+// (https://stackoverflow.com/questions/1657883/variable-number-of-arguments-in-c)
+template <typename CONFDATATYPE>
+CONFDATATYPE fuzzyOR(int n_args, ...) noexcept {
+ // TODO: check datatype and if they are between 0 and 1
+ // David suggests: nstead of a variadic argument, you could pass the values as
+ // an std::array (with a template argument for the length). When you pass the
+ // values as a container, you can simply use std::max_element and
+ // std::min_element to have a one-liner implementation of the these fuzzy
+ // functions.
+ va_list ap;
+ va_start(ap, n_args);
+ CONFDATATYPE max = va_arg(ap, CONFDATATYPE);
+ for (int i = 2; i <= n_args; i++) {
+ CONFDATATYPE a = va_arg(ap, CONFDATATYPE);
+ max = std::max(a, max);
+ }
+ va_end(ap);
+ return max;
+}
+
+template <typename INDATATYPE, typename PROCDATATYPE>
+PROCDATATYPE relativeDistance(INDATATYPE NewValue,
+ INDATATYPE HistoryValue) noexcept {
+ PROCDATATYPE Dist = HistoryValue - NewValue;
+
+ if (Dist == 0) {
+ return 0;
+ } else {
+ Dist = Dist / NewValue;
+ if (Dist < 0) {
+ // TODO: I guess this multiplication here should not be done because
+ // it could be that the distance fuzzy functions are not symetrical
+ //(negative and positive side)
+ Dist = Dist * (-1);
+ }
+ return (Dist);
+ }
+}
+
} // End namespace rosa
#endif // ROSA_SUPPORT_MATH_HPP
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Wed, Jul 2, 2:02 PM (16 h, 3 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
157143
Default Alt Text
(77 KB)
Attached To
Mode
R20 SoC_Rosa_repo
Attached
Detach File
Event Timeline
Log In to Comment