diff --git a/include/rosa/agent/FunctionAbstractions.hpp b/include/rosa/agent/FunctionAbstractions.hpp index 59200de..b12ce98 100644 --- a/include/rosa/agent/FunctionAbstractions.hpp +++ b/include/rosa/agent/FunctionAbstractions.hpp @@ -1,222 +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/Abstraction.hpp" #include "rosa/agent/Functionality.h" #include "rosa/support/debug.hpp" #include #include #include #include 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 class LinearFunction : public Abstraction { // Make sure the actual type arguments are matching our expectations. STATIC_ASSERT((std::is_arithmetic::value), "LinearFunction not arithmetic T"); STATIC_ASSERT((std::is_arithmetic::value), "LinearFunction not to arithmetic"); 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(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; } }; /// 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 class SineFunction : public Abstraction { // Make sure the actual type arguments are matching our expectations. STATIC_ASSERT((std::is_arithmetic::value), "SineFunction not arithmetic T"); STATIC_ASSERT((std::is_arithmetic::value), "SineFunction not to arithmetic"); 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(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::Abstraction as a partial function from a domain -// /to a range. +/// 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 class PartialFunction : public Abstraction { // Make sure the actual type arguments are matching our expectations. STATIC_ASSERT((std::is_arithmetic::value), "abstracting not arithmetic"); STATIC_ASSERT((std::is_arithmetic::value), "abstracting not to arithmetic"); 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>> 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::shared_ptr>> &Map, const R Default) : Abstraction(Default), RA(Map, std::shared_ptr>(new Abstraction(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/State.hpp b/include/rosa/agent/State.hpp index 04b8584..99365de 100644 --- a/include/rosa/agent/State.hpp +++ b/include/rosa/agent/State.hpp @@ -1,128 +1,165 @@ //===-- rosa/agent/State.hpp ----------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file rosa/agent/State.hpp /// /// \author Maximilian Götzinger (maximilian.goetzinger@tuwien.ac.at) /// /// \date 2019 /// /// \brief Definition of *state* *functionality*. /// //===----------------------------------------------------------------------===// #ifndef ROSA_AGENT_STATE_HPP #define ROSA_AGENT_STATE_HPP #include "rosa/agent/FunctionAbstractions.hpp" #include "rosa/agent/Functionality.h" #include "rosa/agent/History.hpp" namespace rosa { namespace agent { -// QUESTION: I think a global variable is not fine, I think it would be better -// to have that in private in the class. However, I have no idea how to -// initialize it then. -static unsigned int StateIdCounter = 0; +// QUESTION: I would like to define the variable nextID as static variable +// inside the struct or the class but it is not possible because I cannot define +// it there (initialize it with 0) because "no const variable". However, using a +// global variable is not that nice in my opinion. +unsigned int nextID = 0; + +/// State conditions defining how the condition of a \c rosa::agent::State is +/// saved in \c rosa::agent::StateInformation. +enum class StateCondition { + STABLE, ///< The state is STABLE + DRIFTING ///< The state is drifting +}; + +template struct StateInformation { + // Make sure the actual type arguments are matching our expectations. + STATIC_ASSERT((std::is_arithmetic::value), + "confidence type is not to arithmetic"); + + /// The state ID saved as an unsigned integer number + unsigned int StateID; + /// The StateConfidence shows the overall confidence value of the state. + CONFTYPE StateConfidence; + /// The StateCondition shows the condition of a state (stable or drifting) + StateCondition StateCondition; + /// The StateIsValid shows whether a state is valid or invalid. In this + /// context, valid means that enough samples which are in close proximitry + /// have been inserted into the state. + bool StateIsValid; + /// The StateIsValidAfterReentrance shows whether a state is valid after the + /// variable changed back to it again. + bool StateIsValidAfterReentrance; +}; + +// TODO (1/2): change name of DABSTORETYPE to something else /// \tparam INDATATYPE type of input data, \tparam DABSTORETYPE type of data /// in which DABs are saved, template class State : public Functionality { // Make sure the actual type arguments are matching our expectations. STATIC_ASSERT((std::is_arithmetic::value), "input data type not arithmetic"); STATIC_ASSERT((std::is_arithmetic::value), "DAB storage type is not to arithmetic"); private: - using PartFuncPointer = + using partFuncPointer = std::shared_ptr>; - unsigned int StateId; + // TODO (2/2): because here, DABSTORETYPE makes no sense + StateInformation StateInfo; + + partFuncPointer PartialFunctionSampleMatches; + partFuncPointer PartialFunctionSampleMismatches; + partFuncPointer PartialFunctionNumOfSamplesMatches; + partFuncPointer PartialFunctionNumOfSamplesMismatches; DynamicLengthHistory SampleHistory; DynamicLengthHistory DAB; DynamicLengthHistory DABHistory; - PartFuncPointer PartialFunctionSampleMatches; - PartFuncPointer PartialFunctionSampleMismatches; - PartFuncPointer PartialFunctionNumOfSamplesMatches; - PartFuncPointer PartialFunctionNumOfSamplesMismatches; - + /// The StateIsValid shows whether a state is valid or invalid. In this + /// context, valid means that enough samples which are in close proximitry + /// have been inserted into the state. bool StateIsValid; + /// The StateIsValidAfterReentrance shows whether a state is valid after the + /// variable changed back to it again. bool StateIsValidAfterReentrance; public: - State(unsigned int sampleHistorySize, unsigned int DABSize, - unsigned int DABHistorySize, - PartFuncPointer PartialFunctionSampleMatches, - PartFuncPointer PartialFunctionSampleMismatches, - PartFuncPointer PartialFunctionNumOfSamplesMatches, - PartFuncPointer PartialFunctionNumOfSamplesMismatches) noexcept + State(partFuncPointer PartialFunctionSampleMatches, + partFuncPointer PartialFunctionSampleMismatches, + partFuncPointer PartialFunctionNumOfSamplesMatches, + partFuncPointer PartialFunctionNumOfSamplesMismatches, + unsigned int sampleHistorySize, unsigned int DABSize, + unsigned int DABHistorySize) noexcept : SampleHistory(sampleHistorySize), DAB(DABSize), DABHistory(DABHistorySize), PartialFunctionSampleMatches(PartialFunctionSampleMatches), PartialFunctionSampleMismatches(PartialFunctionSampleMismatches), PartialFunctionNumOfSamplesMatches(PartialFunctionNumOfSamplesMatches), PartialFunctionNumOfSamplesMismatches( PartialFunctionNumOfSamplesMismatches) { /* StateIsValid = false; StateIsValidAfterReentrance = false; */ } + /// Destroys \p this object. ~State(void) = default; void leaveState(void) { DAB.clear(); StateIsValidAfterReentrance = false; } bool insertSample(INDATATYPE Sample) { bool workedForAll; // maybe ignor that //auc hnicht abchecken einfach kübeln workedForAll = SampleHistory.addEntry(Sample); // QUESTION: is it important to have this flag (workedForAll). What should I // do if it does not work at some point? if (workedForAll) { workedForAll &= DAB.addEntry(Sample); if (workedForAll) { if (DAB.full()) { DABSTORETYPE AvgOfDAB = DAB.average(); workedForAll &= DABHistory.addEntry(AvgOfDAB); if (workedForAll) { DAB.clear(); } // QUESTION: - what should be done if it has not worked? } if (workedForAll) { // TODO: calculate whether state is valid } } } return workedForAll; } - // DDAB confSampleMatchesState(DIN sample) { return NULL; } + DABSTORETYPE confSampleMatchesState(INDATATYPE sample) {} - // DDAB confSampleMismatchesState() {} + DABSTORETYPE confSampleMismatchesState(INDATATYPE sample) {} - unsigned int stateId(void) { return StateId; } + // unsigned int stateId(void) { return StateId; } }; } // End namespace agent } // End namespace rosa #endif // ROSA_AGENT_STATE_HPP diff --git a/include/rosa/agent/StateDetector.hpp b/include/rosa/agent/StateDetector.hpp index fc1ee37..18486d5 100644 --- a/include/rosa/agent/StateDetector.hpp +++ b/include/rosa/agent/StateDetector.hpp @@ -1,208 +1,226 @@ //===-- rosa/agent/StateDetector.hpp ----------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file rosa/agent/StateDetector.hpp /// /// \author Maximilian Götzinger (maximilian.goetzinger@tuwien.ac.at) /// /// \date 2019 /// /// \brief Definition of *state detection* *functionality*. /// //===----------------------------------------------------------------------===// #ifndef ROSA_AGENT_STATEDETECTOR_HPP #define ROSA_AGENT_STATEDETECTOR_HPP #include "rosa/agent/FunctionAbstractions.hpp" #include "rosa/agent/Functionality.h" #include "rosa/agent/State.hpp" -#include #include namespace rosa { namespace agent { -template struct StateInformation { - unsigned int statenumber; - CONFTYPE confidences; - bool stable; // maybe: conf - bool drift; // maybe: conf - bool stateUnchanged; // check name -}; - -// DIN = Input Datatype, CONF = Confidence Datatype +/// Implements \c rosa::agent::StateDetector as a functionality that detects +/// 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 CONFTYPE is type of /// data in that the confidence values are given template class StateDetector : public Functionality { // Make sure the actual type arguments are matching our expectations. STATIC_ASSERT((std::is_arithmetic::value), "input data type not arithmetic"); STATIC_ASSERT((std::is_arithmetic::value), "confidence abstraction type is not to arithmetic"); private: + // For the convinience to write a shorter data type name using partFuncPointer = std::shared_ptr>; using statePtr = std::shared_ptr>; + using stateInfoPtr = std::shared_ptr>; - unsigned int discreteAveragePartitionCounter; + /// The StateHasChanged is a flag that show whether a state change has + /// happened. + bool StateHasChanged; - std::shared_ptr ActualState; + /// The CurrentState is a pointer to the (saved) state in which the actual + /// variable (signal) of the observed system is. + std::shared_ptr CurrentState; + /// The DetectedStates is vector in that all detected states are saved. std::vector DetectedStates; - bool stateIsValid; - bool stateIsValidAfterReentrance; - + /// The PartialFunctionSampleMatches is the fuzzy function that gives the + /// convidence how good the new sample matches another sample in the sample + /// history. partFuncPointer PartialFunctionSampleMatches; + /// The PartialFunctionSampleMatches is the fuzzy function that gives the + /// convidence how bad the new sample matches another sample in the sample + /// history. partFuncPointer PartialFunctionSampleMismatches; + /// The PartialFunctionSampleMatches is the fuzzy function that gives the + /// convidence how many samples from the sampe history match the new sample. partFuncPointer PartialFunctionNumOfSamplesMatches; + /// The PartialFunctionSampleMatches is the fuzzy function that gives the + /// convidence how many samples from the sampe history mismatch the new + /// sample. partFuncPointer PartialFunctionNumOfSamplesMismatches; + /// 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; - //@Benedikt: partialfunction/linearfunction: steigung/domain ändern - //@Benedikt: linearfunction set k,d or x1/y1 and x2/y2 - - // QUESTION: Do we need the following to flag - bool StateHasChanged; - public: - // TODO: finish constructor - StateDetector() noexcept { - - ActualState = NULL; // MAYBE CHANGE TO CURRENT oder Active - } - - /// A sample is input of the function, state detector is looking for a - /// matching state. If it finds a matching one, it inserts the sample, - /// otherwiese it creates a new state. - std::shared_ptr> - detectState(INDATATYPE Sample) { // return tuple oder struct (statenumber, - // confidences, stable, drift, changed/unchanged) - - std::shared_ptr> StateInfo; - - if (ActualState == NULL) { + /// Creates an instance by setting all parameters + StateDetector(partFuncPointer PartialFunctionSampleMatches, + partFuncPointer PartialFunctionSampleMismatches, + partFuncPointer PartialFunctionNumOfSamplesMatches, + partFuncPointer PartialFunctionNumOfSamplesMismatches, + unsigned int SampleHistorySize, unsigned int DABSize, + unsigned int DABHistorySize) noexcept + : StateHasChanged(false), CurrentState(NULL), + PartialFunctionSampleMatches(PartialFunctionSampleMatches), + PartialFunctionSampleMismatches(PartialFunctionSampleMismatches), + PartialFunctionNumOfSamplesMatches(PartialFunctionNumOfSamplesMatches), + PartialFunctionNumOfSamplesMismatches( + PartialFunctionNumOfSamplesMismatches), + SampleHistorySize(SampleHistorySize), DABSize(DABSize), + DABHistorySize(DABHistorySize) {} + + /// Destroys \p this object. + ~StateDetector(void) = default; + + /// Detects the state to which the new sample belongs or create a new state if + /// the new sample does not match to any of the saved states. + /// + /// \param Sample + /// + /// \return the information of the actual state (state ID and other + /// parameters) + stateInfoPtr detectState(INDATATYPE Sample) { + + if (CurrentState == NULL) { ASSERT(DetectedStates.empty()); - if (statePtr S = createNewState()) { - - ActualState = S; - - } // QUESTION: should there an output (log) when it was not successfull? + statePtr S = createNewState(); + if (S) { + CurrentState = S; + } else { + // TODO: handle (or at least log) if now state could be created + } } else { CONFTYPE ConfidenceSampleMatchesState = - ActualState->confSampleMatchesState(Sample); + CurrentState->confSampleMatchesState(Sample); CONFTYPE ConfidenceSampleMismatchesState = - ActualState->confSampleMismatchesState(Sample); + CurrentState->confSampleMismatchesState(Sample); if (ConfidenceSampleMatchesState > ConfidenceSampleMismatchesState) { - StateHasChanged = false; - } else { - StateHasChanged = true; // TODO: check whether ActiveState is valid + // TODO: if invalid -> deleteState (einfach pop weil shared pointer) + // a.erase(std::find(a.begin(),a.end(),2)); // TODO: else leaveState // TODO FAR AWAY FUTURE: additionally save averages to enable fast // iteration through recorded state vector //- maybe sort vector based on these average values - ActualState = NULL; + CurrentState = NULL; + for (auto &SavedState : DetectedStates) { - if (SavedState != ActualState) { + if (SavedState != CurrentState) { CONFTYPE ConfidenceSampleMatchesState = SavedState->confSampleMatchesState(Sample); CONFTYPE ConfidenceSampleMismatchesState = SavedState->confSampleMismatchesState(Sample); if (ConfidenceSampleMatchesState > ConfidenceSampleMismatchesState) { // QUESTION: maybe it would be better to compare // ConfidenceSampleMatchesState // of all states in the vector - ActualState = SavedState; + CurrentState = SavedState; break; } } } - if (!ActualState) { + if (!CurrentState) { if (statePtr S = createNewState()) { - ActualState = S; + CurrentState = S; } // QUESTION: should there an output (log) when it was not // successfull? } } } - ActualState->insertSample(Sample); + CurrentState->insertSample(Sample); // TODO: check actual state whether it drifts // TODO: write in StateInfo - return StateInfo; + return CurrentState->stateInfo(); } bool stateHasChanged(void) { return StateHasChanged; } bool stateIsUnchanged(void) { return !StateHasChanged; } // TODO: get confidences, get drift/stable get, get active state number, // TODO: maybe just return struct with everything private: - statePtr createNewState() { - - bool CreatingWorked; + /// Creates a new state and adds this state to the 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 PartialFunctionSampleMatches the + /// \param PartialFunctionSampleMismatches + /// \param PartialFunctionNumOfSamplesMatches + /// \param PartialFunctionNumOfSamplesMismatches + /// + /// \return the new created state or NULL if no state could be created. + statePtr createNewState(void) { statePtr S = new (std::nothrow) - State(StateIdCounter, SampleHistorySize, DABSize, DABHistorySize, + State(SampleHistorySize, DABSize, DABHistorySize, PartialFunctionSampleMatches, PartialFunctionSampleMismatches, PartialFunctionNumOfSamplesMatches, PartialFunctionNumOfSamplesMismatches); if (S) { - DetectedStates.push_back( - S); // QUESTION: how to make this without any exception? - - CreatingWorked = true; - // TODO: if push_back did not work -> CreatingWorked = false; - } else { - // delete S; - - CreatingWorked = false; - } - - if (CreatingWorked) { - - StateIdCounter++; - + DetectedStates.push_back(S); return S; } else { return NULL; } } }; } // End namespace agent } // End namespace rosa #endif // ROSA_AGENT_STATEDETECTOR_HPP