diff --git a/include/rosa/agent/State.hpp b/include/rosa/agent/State.hpp index ef5b85a..9675d62 100644 --- a/include/rosa/agent/State.hpp +++ b/include/rosa/agent/State.hpp @@ -1,179 +1,230 @@ //===-- 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 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 + STABLE, ///< The state is stable + DRIFTING, ///< The state is drifting + UNKNOWN ///< The state is unknown }; 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 +// TODO: check whethter name of CONFTYPE is good for each context here +/// \tparam INDATATYPE type of input data, \tparam CONFTYPE is type of +/// data in that the confidence values are given. It is also the type of data /// in which DABs are saved, -template +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), + STATIC_ASSERT((std::is_arithmetic::value), "DAB storage type is not to arithmetic"); private: // For the convinience to write a shorter data type name using partFuncPointer = - std::shared_ptr>; - // TODO (2/2): because here, DABSTORETYPE makes no sense - using stateInfoPtr = std::shared_ptr>; + std::shared_ptr>; + using stateInfoPtr = std::shared_ptr>; stateInfoPtr StateInfo; partFuncPointer PartialFunctionSampleMatches; partFuncPointer PartialFunctionSampleMismatches; partFuncPointer PartialFunctionNumOfSamplesMatches; partFuncPointer PartialFunctionNumOfSamplesMismatches; DynamicLengthHistory SampleHistory; DynamicLengthHistory DAB; - DynamicLengthHistory DABHistory; + DynamicLengthHistory DABHistory; /// 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: + /// Creates an instance by setting all parameters State(partFuncPointer PartialFunctionSampleMatches, partFuncPointer PartialFunctionSampleMismatches, partFuncPointer PartialFunctionNumOfSamplesMatches, partFuncPointer PartialFunctionNumOfSamplesMismatches, unsigned int sampleHistorySize, unsigned int DABSize, unsigned int DABHistorySize) noexcept - : SampleHistory(sampleHistorySize), DAB(DABSize), + : StateInfo(nextID, 0, StateCondition::UNKNOWN, false, false), + SampleHistory(sampleHistorySize), DAB(DABSize), DABHistory(DABHistorySize), PartialFunctionSampleMatches(PartialFunctionSampleMatches), PartialFunctionSampleMismatches(PartialFunctionSampleMismatches), PartialFunctionNumOfSamplesMatches(PartialFunctionNumOfSamplesMatches), PartialFunctionNumOfSamplesMismatches( - PartialFunctionNumOfSamplesMismatches) { - /* - StateIsValid = false; - StateIsValidAfterReentrance = false; - */ - } + PartialFunctionNumOfSamplesMismatches) {} /// Destroys \p this object. ~State(void) = default; void leaveState(void) { DAB.clear(); StateIsValidAfterReentrance = false; } - bool insertSample(INDATATYPE Sample) { + void insertSample(INDATATYPE Sample) { + // TODO: think about some error handling in case of not beeing able to add + // an entry or create a new object + SampleHistory.addEntry(Sample); + + DAB.addEntry(Sample); + if (DAB.full()) { + CONFTYPE AvgOfDAB = DAB.average(); + DABHistory.addEntry(AvgOfDAB); + DAB.clear(); + } + + // TODO: calculate whether state is valid + + // TODO: check actual state whether it drifts + // TODO: write in StateInfo + } + + // TODO: check this function again + CONFTYPE + confSampleMatchesState(INDATATYPE sample) { - bool workedForAll; // maybe ignor that //auc hnicht abchecken einfach kübeln + CONFTYPE RelDiff; + CONFTYPE HighestConf = 0; - workedForAll = SampleHistory.addEntry(Sample); + // QUESTION: should I also use a history (array) for that? + std::vector RelDiffHistValuesAndSampleValue; - // 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(); + for (auto &hs : SampleHistory) { + RelDiff = relativeDifference(sample, hs); + RelDiffHistValuesAndSampleValue.push_back(RelDiff); + } - workedForAll &= DABHistory.addEntry(AvgOfDAB); + sort(begin(RelDiffHistValuesAndSampleValue), + end(RelDiffHistValuesAndSampleValue)); - if (workedForAll) { - DAB.clear(); - } // QUESTION: - what should be done if it has not worked? - } + for (unsigned int numOfHistSamplesIncluded = 1; + numOfHistSamplesIncluded <= RelDiffHistValuesAndSampleValue.size(); + numOfHistSamplesIncluded++) { - if (workedForAll) { - // TODO: calculate whether state is valid + CONFTYPE LowestConfOfSamplesIncluded = 1; + unsigned int HistSampleCounter = 0; - // TODO: check actual state whether it drifts - // TODO: write in StateInfo + for (auto &D : RelDiffHistValuesAndSampleValue) { + if (HistSampleCounter >= numOfHistSamplesIncluded) + break; + + CONFTYPE confRelDiff; + + // TODO: check if infinity check is needed here? + if (std::isinf(D) == false) { + confRelDiff = PartialFunctionSampleMatches->getY(D); + } else { + confRelDiff = 0; } + + // std::min is equal to Fuzzy AND + LowestConfOfSamplesIncluded = + std::min(LowestConfOfSamplesIncluded, confRelDiff); + HistSampleCounter++; } - } - return workedForAll; + // std::max is equal to Fuzzy OR + HighestConf = std::max(HighestConf, + std::max(LowestConfOfSamplesIncluded, + PartialFunctionNumOfSamplesMatches->getY( + (CONFTYPE)HistSampleCounter))); + } } - DABSTORETYPE confSampleMatchesState(INDATATYPE sample) {} - - DABSTORETYPE confSampleMismatchesState(INDATATYPE sample) {} + CONFTYPE + confSampleMismatchesState(INDATATYPE sample) { + // TODO: do it in the same way as confSampleMatchesState(INDATATYPE sample) + } - // unsigned int stateId(void) { return StateId; } /// Gives information about the current state. /// /// \return a struct StateInformation that contains information about the /// current state. stateInfoPtr stateInformation(void) { // TODO: check what happens when currentStateInformation(void) is called in // the beginning when no state has recognized at all. return StateInfo; } + +private: + // QUESTION: is such a function already implemented? If not where should put + // this function? + CONFTYPE relativeDifference(INDATATYPE SampleValue, INDATATYPE HistoryValue) { + CONFTYPE Diff = HistoryValue - SampleValue; + + if (Diff == 0) { + return 0; + } else { + Diff = Diff / SampleValue; + if (Diff < 0) { + Diff = Diff * (-1); + } + return (Diff); + } + } }; } // 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 052a60e..9b9182b 100644 --- a/include/rosa/agent/StateDetector.hpp +++ b/include/rosa/agent/StateDetector.hpp @@ -1,232 +1,234 @@ //===-- 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 detector* *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 namespace rosa { namespace agent { /// 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>; /// The StateHasChanged is a flag that show whether a state change has /// happened. bool StateHasChanged; /// 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; /// 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; public: /// 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()); statePtr S = createNewState(); if (S) { CurrentState = S; } else { // TODO: handle (or at least log) if now state could be created. // However, this handling/logging could be also done in // createNewState(). } } else { CONFTYPE ConfidenceSampleMatchesState = CurrentState->confSampleMatchesState(Sample); CONFTYPE ConfidenceSampleMismatchesState = CurrentState->confSampleMismatchesState(Sample); if (ConfidenceSampleMatchesState > ConfidenceSampleMismatchesState) { StateHasChanged = false; } else { StateHasChanged = true; if (CurrentState->stateInformation()->StateIsValid) { - leaveState(); + CurrentState->leaveState(); } else { DetectedStates.erase(std::find(DetectedStates.begin(), DetectedStates.end(), CurrentState)); } // TODO (future): additionally save averages to enable fast // iteration through recorded state vector (maybe sort vector based on // these average values) CurrentState = NULL; for (auto &SavedState : DetectedStates) { if (SavedState != CurrentState) { CONFTYPE ConfidenceSampleMatchesState = SavedState->confSampleMatchesState(Sample); CONFTYPE ConfidenceSampleMismatchesState = SavedState->confSampleMismatchesState(Sample); if (ConfidenceSampleMatchesState > ConfidenceSampleMismatchesState) { // TODO (future): maybe it would be better to compare // ConfidenceSampleMatchesState of all states in the vector in // order to find the best matching state. CurrentState = SavedState; break; } } } if (!CurrentState) { if (statePtr S = createNewState()) { CurrentState = S; } else { // TODO: handle (or at least log) if now state could be created. // However, this handling/logging could be also done in // createNewState(). } } } } CurrentState->insertSample(Sample); return CurrentState->stateInfo(); } /// Gives information about the current state. /// /// \return a struct StateInformation that contains information about the - /// current state. + /// current state or NULL if no current state exists. stateInfoPtr currentStateInformation(void) { - // TODO: check what happens when currentStateInformation(void) is called in - // the beginning when no state has recognized at all. - return CurrentState->stateInfo(); + if (CurrentState) { + return CurrentState->stateInfo(); + } else { + return NULL; + } } /// Gives information whether a state change has happened or not. /// /// \return true if a state change has happened, and false if not. bool stateHasChanged(void) { return StateHasChanged; } private: /// 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(SampleHistorySize, DABSize, DABHistorySize, PartialFunctionSampleMatches, PartialFunctionSampleMismatches, PartialFunctionNumOfSamplesMatches, PartialFunctionNumOfSamplesMismatches); if (S) { DetectedStates.push_back(S); return S; } else { return NULL; } } }; } // End namespace agent } // End namespace rosa #endif // ROSA_AGENT_STATEDETECTOR_HPP