diff --git a/include/rosa/agent/State.hpp b/include/rosa/agent/State.hpp index 9675d62..39ef8af 100644 --- a/include/rosa/agent/State.hpp +++ b/include/rosa/agent/State.hpp @@ -1,230 +1,243 @@ //===-- 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; - +// 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 { 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 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 +// 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 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: // For the convinience to write a shorter data type name - using partFuncPointer = + using PartFuncPointer = std::shared_ptr>; - using stateInfoPtr = std::shared_ptr>; + using StateInfoPtr = std::shared_ptr>; - stateInfoPtr StateInfo; + StateInfoPtr StateInfo; - partFuncPointer PartialFunctionSampleMatches; - partFuncPointer PartialFunctionSampleMismatches; - partFuncPointer PartialFunctionNumOfSamplesMatches; - partFuncPointer PartialFunctionNumOfSamplesMismatches; + // TODO: rename fuzzy function + PartFuncPointer PartialFunctionSampleMatches; + PartFuncPointer PartialFunctionSampleMismatches; + std::shared_ptr> + StepFunctionNumOfSamplesMatches; + std::shared_ptr> + StepFunctionNumOfSamplesMismatches; + // TODO: partFunc Drift, NoDrift DynamicLengthHistory SampleHistory; DynamicLengthHistory DAB; 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, + State(unsigned int StateID, PartFuncPointer PartialFunctionSampleMatches, + PartFuncPointer PartialFunctionSampleMismatches, + std::shared_ptr> + StepFunctionNumOfSamplesMatches, + std::shared_ptr> + StepFunctionNumOfSamplesMismatches, unsigned int sampleHistorySize, unsigned int DABSize, unsigned int DABHistorySize) noexcept - : StateInfo(nextID, 0, StateCondition::UNKNOWN, false, false), + : StateInfo(StateID, 0, StateCondition::UNKNOWN, false, false), SampleHistory(sampleHistorySize), DAB(DABSize), DABHistory(DABHistorySize), PartialFunctionSampleMatches(PartialFunctionSampleMatches), PartialFunctionSampleMismatches(PartialFunctionSampleMismatches), - PartialFunctionNumOfSamplesMatches(PartialFunctionNumOfSamplesMatches), - PartialFunctionNumOfSamplesMismatches( - PartialFunctionNumOfSamplesMismatches) {} + StepFunctionNumOfSamplesMatches(StepFunctionNumOfSamplesMatches), + StepFunctionNumOfSamplesMismatches(StepFunctionNumOfSamplesMismatches) { + } /// 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.average(); + CONFTYPE AvgOfDAB = DAB.template average(); DABHistory.addEntry(AvgOfDAB); DAB.clear(); } - // TODO: calculate whether state is valid + //@Benedikt: stepfunction auch andere richtung + StepFunctionNumOfSamplesMatches->setRightLimit( + SampleHistory->numberOfEntries()); + StepFunctionNumOfSamplesMismatches->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 confSampleMatchesState(INDATATYPE sample) { CONFTYPE RelDiff; CONFTYPE HighestConf = 0; // QUESTION: should I also use a history (array) for that? std::vector RelDiffHistValuesAndSampleValue; for (auto &hs : SampleHistory) { RelDiff = relativeDifference(sample, hs); RelDiffHistValuesAndSampleValue.push_back(RelDiff); } sort(begin(RelDiffHistValuesAndSampleValue), end(RelDiffHistValuesAndSampleValue)); + // TODO (future): to accelerate -> don't start with 1 for (unsigned int numOfHistSamplesIncluded = 1; numOfHistSamplesIncluded <= RelDiffHistValuesAndSampleValue.size(); numOfHistSamplesIncluded++) { CONFTYPE LowestConfOfSamplesIncluded = 1; unsigned int HistSampleCounter = 0; 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); + confRelDiff = PartialFunctionSampleMatches(D); } else { confRelDiff = 0; } - // std::min is equal to Fuzzy AND + // std::min is equal to Fuzzy AND -> maybe using LowestConfOfSamplesIncluded = std::min(LowestConfOfSamplesIncluded, confRelDiff); HistSampleCounter++; } // std::max is equal to Fuzzy OR - HighestConf = std::max(HighestConf, - std::max(LowestConfOfSamplesIncluded, - PartialFunctionNumOfSamplesMatches->getY( - (CONFTYPE)HistSampleCounter))); + HighestConf = + std::max(HighestConf, std::max(LowestConfOfSamplesIncluded, + StepFunctionNumOfSamplesMatches( + (CONFTYPE)HistSampleCounter))); } } CONFTYPE 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) { - // TODO: check what happens when currentStateInformation(void) is called in - // the beginning when no state has recognized at all. - return StateInfo; - } + 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) { 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 9b9182b..7f31f53 100644 --- a/include/rosa/agent/StateDetector.hpp +++ b/include/rosa/agent/StateDetector.hpp @@ -1,234 +1,267 @@ //===-- 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 = + using PartFuncPointer = std::shared_ptr>; - using statePtr = std::shared_ptr>; - using stateInfoPtr = 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. - std::shared_ptr CurrentState; + StatePtr CurrentState; /// The DetectedStates is vector in that all detected states are saved. - std::vector DetectedStates; + std::vector DetectedStates; - /// The PartialFunctionSampleMatches is the fuzzy function that gives the + /// The FuzzyFunctionSampleMatches 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 + 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 PartialFunctionSampleMismatches; - /// The PartialFunctionSampleMatches is the fuzzy function that gives the + PartFuncPointer FuzzyFunctionSampleMismatches; + /// The FuzzyFunctionSampleMatches 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 + PartFuncPointer FuzzyFunctionNumOfSamplesMatches; + /// The FuzzyFunctionSampleMatches is the fuzzy function that gives the /// convidence how many samples from the sampe history mismatch the new /// sample. - partFuncPointer PartialFunctionNumOfSamplesMismatches; + PartFuncPointer FuzzyFunctionNumOfSamplesMismatches; /// 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, + StateDetector(PartFuncPointer FuzzyFunctionSampleMatches, + PartFuncPointer FuzzyFunctionSampleMismatches, + PartFuncPointer FuzzyFunctionNumOfSamplesMatches, + PartFuncPointer FuzzyFunctionNumOfSamplesMismatches, unsigned int SampleHistorySize, unsigned int DABSize, unsigned int DABHistorySize) noexcept - : StateHasChanged(false), CurrentState(NULL), - PartialFunctionSampleMatches(PartialFunctionSampleMatches), - PartialFunctionSampleMismatches(PartialFunctionSampleMismatches), - PartialFunctionNumOfSamplesMatches(PartialFunctionNumOfSamplesMatches), - PartialFunctionNumOfSamplesMismatches( - PartialFunctionNumOfSamplesMismatches), + : NextStateID(1), StateHasChanged(false), CurrentState(NULL), + FuzzyFunctionSampleMatches(FuzzyFunctionSampleMatches), + FuzzyFunctionSampleMismatches(FuzzyFunctionSampleMismatches), + FuzzyFunctionNumOfSamplesMatches(FuzzyFunctionNumOfSamplesMatches), + FuzzyFunctionNumOfSamplesMismatches( + FuzzyFunctionNumOfSamplesMismatches), 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 + /// \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) - stateInfoPtr detectState(INDATATYPE Sample) { + /// parameters). + // TODO: return something const.. cannot remember exactly (ask benedikt) + StateInfoPtr detectState__debug(INDATATYPE Sample) { - if (CurrentState == NULL) { + if (!CurrentState) { 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(). - } + 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 = NULL; + 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) { - 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(). - } + StatePtr S = createNewState(); + CurrentState = S; } } } - CurrentState->insertSample(Sample); + StateInfoPtr StateInfo = CurrentState->insertSample(Sample); + + if (StateInfo->StateJustGotValid) { + NextStateID++; + } - return CurrentState->stateInfo(); + 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(void) { + StateInfoPtr currentStateInformation__debug(void) { 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; + return CurrentState->stateInformation(); } else { return NULL; } } }; } // End namespace agent } // End namespace rosa #endif // ROSA_AGENT_STATEDETECTOR_HPP