diff --git a/include/rosa/agent/State.hpp b/include/rosa/agent/State.hpp index 39ef8af..e703a41 100644 --- a/include/rosa/agent/State.hpp +++ b/include/rosa/agent/State.hpp @@ -1,243 +1,256 @@ //===-- 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 { -// TODO: think about call it VariableStateInformation /// State conditions defining how the condition of a \c rosa::agent::State is /// saved in \c rosa::agent::StateInformation. -enum class StateCondition { +enum class VariableStateCondition { STABLE, ///< The state is stable DRIFTING, ///< The state is drifting UNKNOWN ///< The state is unknown }; -template struct StateInformation { +template struct StateInformation { // Make sure the actual type arguments are matching our expectations. - STATIC_ASSERT((std::is_arithmetic::value), + 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; + CONFDATATYPE StateConfidence; + /// The VariableStateCondition shows the condition of a state (stable or + /// drifting) + VariableStateCondition VariableStateCondition; /// 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 StateJustGotValid shows whether a state got valid (toggled from /// invalid to valid) during the current inserted sample. bool StateJustGotValid; /// The StateIsValidAfterReentrance shows whether a state is valid after the /// variable changed back to it again. bool StateIsValidAfterReentrance; }; -// TODO: check whethter name of CONFTYPE is good for each context here -> mach -// einen dritten datatype -/// \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 +// @Benedikt: now there are 4 datatypes. Do you think we can merge DISTDATATYPE +// and DABDATATYPE somehow? +/// \tparam INDATATYPE type of input data, \tparam CONFDATATYPE type of +/// data in that the confidence values are given, \param DISTDATATYPE type of +/// the relative distance, \tparam DABDATATYPE 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), + 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>; - using StateInfoPtr = std::shared_ptr>; + std::shared_ptr>; + using StepFuncPointer = + std::shared_ptr>; + using StateInfoPtr = std::shared_ptr>; + /// XXX - explain StateInfoPtr StateInfo; - // TODO: rename fuzzy function - PartFuncPointer PartialFunctionSampleMatches; - PartFuncPointer PartialFunctionSampleMismatches; - std::shared_ptr> - StepFunctionNumOfSamplesMatches; - std::shared_ptr> - StepFunctionNumOfSamplesMismatches; - // TODO: partFunc Drift, NoDrift - + /// XXX - explain + PartFuncPointer FuzzyFunctionSampleMatches; + /// XXX - explain + PartFuncPointer FuzzyFunctionSampleMismatches; + /// XXX - explain + StepFuncPointer FuzzyFunctionNumOfSamplesMatches; + /// XXX - explain + StepFuncPointer FuzzyFunctionNumOfSamplesMismatches; + + /// XXX - explain + PartFuncPointer FuzzyFunctionSignalIsDrifting; + /// XXX - explain + PartFuncPointer FuzzyFunctionSignalIsStable; + + /// XXX - explain DynamicLengthHistory SampleHistory; + /// XXX - explain DynamicLengthHistory DAB; - DynamicLengthHistory DABHistory; + /// XXX - explain + 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(unsigned int StateID, PartFuncPointer PartialFunctionSampleMatches, - PartFuncPointer PartialFunctionSampleMismatches, - std::shared_ptr> - StepFunctionNumOfSamplesMatches, - std::shared_ptr> - StepFunctionNumOfSamplesMismatches, + State(unsigned int StateID, PartFuncPointer FuzzyFunctionSampleMatches, + PartFuncPointer FuzzyFunctionSampleMismatches, + StepFuncPointer FuzzyFunctionNumOfSamplesMatches, + StepFuncPointer FuzzyFunctionNumOfSamplesMismatches, + PartFuncPointer FuzzyFunctionSignalIsDrifting, + PartFuncPointer FuzzyFunctionSignalIsStable, unsigned int sampleHistorySize, unsigned int DABSize, unsigned int DABHistorySize) noexcept - : StateInfo(StateID, 0, StateCondition::UNKNOWN, false, false), + : StateInfo(StateID, 0, VariableStateCondition::UNKNOWN, false, false), SampleHistory(sampleHistorySize), DAB(DABSize), DABHistory(DABHistorySize), - PartialFunctionSampleMatches(PartialFunctionSampleMatches), - PartialFunctionSampleMismatches(PartialFunctionSampleMismatches), - StepFunctionNumOfSamplesMatches(StepFunctionNumOfSamplesMatches), - StepFunctionNumOfSamplesMismatches(StepFunctionNumOfSamplesMismatches) { - } + FuzzyFunctionSampleMatches(FuzzyFunctionSampleMatches), + FuzzyFunctionSampleMismatches(FuzzyFunctionSampleMismatches), + FuzzyFunctionNumOfSamplesMatches(FuzzyFunctionNumOfSamplesMatches), + FuzzyFunctionNumOfSamplesMismatches( + FuzzyFunctionNumOfSamplesMismatches), + FuzzyFunctionSignalIsDrifting(FuzzyFunctionSignalIsDrifting), + FuzzyFunctionSignalIsStable(FuzzyFunctionSignalIsStable) {} /// Destroys \p this object. ~State(void) = default; void leaveState(void) { DAB.clear(); StateIsValidAfterReentrance = false; } - // TODO: maybe return immediatly StateInfo 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.template average(); + DABDATATYPE AvgOfDAB = DAB.template average(); DABHistory.addEntry(AvgOfDAB); DAB.clear(); } - //@Benedikt: stepfunction auch andere richtung - StepFunctionNumOfSamplesMatches->setRightLimit( + // TODO @Benedikt: stepfunction auch andere richtung + FuzzyFunctionNumOfSamplesMatches->setRightLimit( SampleHistory->numberOfEntries()); - StepFunctionNumOfSamplesMismatches->setRightLimit( + FuzzyFunctionNumOfSamplesMismatches->setRightLimit( SampleHistory->numberOfEntries()); // TODO: calculate whether state is valid and properly set StateIsValid, // StateJustGotValid, StateIsValidAfterReentrance // TODO: check actual state whether it drifts // TODO: write in StateInfo } // TODO: check this function again - CONFTYPE + CONFDATATYPE confSampleMatchesState(INDATATYPE sample) { - CONFTYPE RelDiff; - CONFTYPE HighestConf = 0; + DISTDATATYPE RelDist; + CONFDATATYPE HighestConf = 0; // QUESTION: should I also use a history (array) for that? - std::vector RelDiffHistValuesAndSampleValue; + std::vector RelDistHistValuesAndSampleValue; for (auto &hs : SampleHistory) { - RelDiff = relativeDifference(sample, hs); - RelDiffHistValuesAndSampleValue.push_back(RelDiff); + RelDist = relativeDistance(sample, hs); + RelDistHistValuesAndSampleValue.push_back(RelDist); } - sort(begin(RelDiffHistValuesAndSampleValue), - end(RelDiffHistValuesAndSampleValue)); + sort(begin(RelDistHistValuesAndSampleValue), + end(RelDistHistValuesAndSampleValue)); // TODO (future): to accelerate -> don't start with 1 for (unsigned int numOfHistSamplesIncluded = 1; - numOfHistSamplesIncluded <= RelDiffHistValuesAndSampleValue.size(); + numOfHistSamplesIncluded <= RelDistHistValuesAndSampleValue.size(); numOfHistSamplesIncluded++) { - CONFTYPE LowestConfOfSamplesIncluded = 1; + CONFDATATYPE LowestConfOfSamplesIncluded = 1; unsigned int HistSampleCounter = 0; - for (auto &D : RelDiffHistValuesAndSampleValue) { + for (auto &D : RelDistHistValuesAndSampleValue) { if (HistSampleCounter >= numOfHistSamplesIncluded) break; - CONFTYPE confRelDiff; + CONFDATATYPE confRelDist; // TODO: check if infinity check is needed here? if (std::isinf(D) == false) { - confRelDiff = PartialFunctionSampleMatches(D); + confRelDist = FuzzyFunctionSampleMatches(D); } else { - confRelDiff = 0; + confRelDist = 0; } // std::min is equal to Fuzzy AND -> maybe using LowestConfOfSamplesIncluded = - std::min(LowestConfOfSamplesIncluded, confRelDiff); + std::min(LowestConfOfSamplesIncluded, confRelDist); HistSampleCounter++; } // std::max is equal to Fuzzy OR + // TODO: 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? HighestConf = std::max(HighestConf, std::max(LowestConfOfSamplesIncluded, - StepFunctionNumOfSamplesMatches( - (CONFTYPE)HistSampleCounter))); + FuzzyFunctionNumOfSamplesMatches( + (CONFDATATYPE)HistSampleCounter))); } } - CONFTYPE + CONFDATATYPE confSampleMismatchesState(INDATATYPE sample) { // TODO: do it in the same way as confSampleMatchesState(INDATATYPE sample) } /// Gives information about the current state. /// /// \return a struct StateInformation that contains information about the /// current state. StateInfoPtr stateInformation(void) { return StateInfo; } private: - // QUESTION: is such a function already implemented? If not where should put - // this function? - // TODO: rename in distance - // TODO: ask david where to move that - CONFTYPE relativeDifference(INDATATYPE SampleValue, INDATATYPE HistoryValue) { - CONFTYPE Diff = HistoryValue - SampleValue; - - if (Diff == 0) { + // TODO: Ask David where to move that function (or if it should stay here). + DISTDATATYPE relativeDistance(INDATATYPE SampleValue, + INDATATYPE HistoryValue) { + DISTDATATYPE Dist = HistoryValue - SampleValue; + + if (Dist == 0) { return 0; } else { - Diff = Diff / SampleValue; - if (Diff < 0) { - Diff = Diff * (-1); + Dist = Dist / SampleValue; + if (Dist < 0) { + Dist = Dist * (-1); } - return (Diff); + return (Dist); } } }; } // 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 7f31f53..2885471 100644 --- a/include/rosa/agent/StateDetector.hpp +++ b/include/rosa/agent/StateDetector.hpp @@ -1,267 +1,277 @@ //===-- 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 StepFuncPointer = std::shared_ptr>; using StatePtr = std::shared_ptr>; using StateInfoPtr = std::shared_ptr>; /// The NextStateID is a counter variable which stores the ID which the next /// state shall have. unsigned int NextStateID; /// 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. StatePtr CurrentState; /// The DetectedStates is vector in that all detected states are saved. std::vector DetectedStates; /// The FuzzyFunctionSampleMatches is the fuzzy function that gives the /// convidence how good the new sample matches another sample in the sample /// history. PartFuncPointer FuzzyFunctionSampleMatches; /// The FuzzyFunctionSampleMatches is the fuzzy function that gives the /// convidence how bad the new sample matches another sample in the sample /// history. PartFuncPointer FuzzyFunctionSampleMismatches; /// The FuzzyFunctionSampleMatches is the fuzzy function that gives the /// convidence how many samples from the sampe history match the new sample. - PartFuncPointer FuzzyFunctionNumOfSamplesMatches; + StepFuncPointer FuzzyFunctionNumOfSamplesMatches; /// The FuzzyFunctionSampleMatches is the fuzzy function that gives the /// convidence how many samples from the sampe history mismatch the new /// sample. - PartFuncPointer FuzzyFunctionNumOfSamplesMismatches; + StepFuncPointer FuzzyFunctionNumOfSamplesMismatches; + + /// XXX - explain + PartFuncPointer FuzzyFunctionSignalIsDrifting; + /// XXX - explain + 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 StateDetector(PartFuncPointer FuzzyFunctionSampleMatches, PartFuncPointer FuzzyFunctionSampleMismatches, - PartFuncPointer FuzzyFunctionNumOfSamplesMatches, - PartFuncPointer FuzzyFunctionNumOfSamplesMismatches, + StepFuncPointer FuzzyFunctionNumOfSamplesMatches, + StepFuncPointer FuzzyFunctionNumOfSamplesMismatches, + PartFuncPointer FuzzyFunctionSignalIsDrifting, + PartFuncPointer FuzzyFunctionSignalIsStable, unsigned int SampleHistorySize, unsigned int DABSize, unsigned int DABHistorySize) noexcept : NextStateID(1), StateHasChanged(false), CurrentState(NULL), FuzzyFunctionSampleMatches(FuzzyFunctionSampleMatches), FuzzyFunctionSampleMismatches(FuzzyFunctionSampleMismatches), FuzzyFunctionNumOfSamplesMatches(FuzzyFunctionNumOfSamplesMatches), FuzzyFunctionNumOfSamplesMismatches( FuzzyFunctionNumOfSamplesMismatches), + FuzzyFunctionSignalIsDrifting(FuzzyFunctionSignalIsDrifting), + FuzzyFunctionSignalIsStable(FuzzyFunctionSignalIsStable), 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 is the actual sample of the observed signal. /// /// \return the state ID as unsigend integer type. State IDs start with number /// 1; that means if there is no current state, the return value is 0. unsigned int detectState(INDATATYPE Sample) { StateInfoPtr StateInfo = detectState__debug(Sample); return StateInfo->StateID; } /// Gives information about the current state. /// /// \return a the State ID (as unsigned integer type) of the current state. /// State IDs start with number 1; that means if there is no current state, /// the return value is 0. unsigned int currentStateInformation(void) { StateInfoPtr StateInfo = currentStateInformation__debug(); if (StateInfo) { return StateInfo->StateID; } else { return 0; } } /// 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 FuzzyFunctionSampleMatches the /// \param FuzzyFunctionSampleMismatches /// \param FuzzyFunctionNumOfSamplesMatches /// \param FuzzyFunctionNumOfSamplesMismatches /// /// \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, FuzzyFunctionSampleMatches, FuzzyFunctionSampleMismatches, FuzzyFunctionNumOfSamplesMatches, FuzzyFunctionNumOfSamplesMismatches); // TODO: maybe assert which checks if push_back worked (ask benedikt) DetectedStates.push_back(S); return S; } #ifdef STATEDETECTORDEBUGMODE public: #else private: #endif // STATEDETECTORDEBUGMODE /// 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 is the actual sample of the observed signal. /// /// \return the information of the actual state (state ID and other /// parameters). // TODO: return something const.. cannot remember exactly (ask benedikt) StateInfoPtr detectState__debug(INDATATYPE Sample) { if (!CurrentState) { ASSERT(DetectedStates.empty()); StatePtr S = createNewState(); CurrentState = S; } else { CONFTYPE ConfidenceSampleMatchesState = CurrentState->confSampleMatchesState(Sample); CONFTYPE ConfidenceSampleMismatchesState = CurrentState->confSampleMismatchesState(Sample); if (ConfidenceSampleMatchesState > ConfidenceSampleMismatchesState) { StateHasChanged = false; } else { StateHasChanged = true; if (CurrentState->stateInformation()->StateIsValid) { 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 = nullptr; 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) { StatePtr S = createNewState(); CurrentState = S; } } } StateInfoPtr StateInfo = CurrentState->insertSample(Sample); if (StateInfo->StateJustGotValid) { NextStateID++; } return CurrentState->stateInformation(); } #ifdef STATEDETECTORDEBUGMODE public: #else private: #endif // STATEDETECTORDEBUGMODE /// Gives information about the current state. /// /// \return a struct StateInformation that contains information about the /// current state or NULL if no current state exists. StateInfoPtr currentStateInformation__debug(void) { if (CurrentState) { return CurrentState->stateInformation(); } else { return NULL; } } }; } // End namespace agent } // End namespace rosa #endif // ROSA_AGENT_STATEDETECTOR_HPP