diff --git a/include/rosa/agent/State.hpp b/include/rosa/agent/State.hpp index 2afd52f..aee717d 100644 --- a/include/rosa/agent/State.hpp +++ b/include/rosa/agent/State.hpp @@ -1,370 +1,415 @@ //===-- 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" #include namespace rosa { namespace agent { /// State conditions defining how the condition of a \c rosa::agent::State is /// saved in \c rosa::agent::StateInformation. enum class VariableStateCondition { 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. 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; }; // @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 PROCDATATYPEtype of /// the relative distance and 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 = std::shared_ptr>; using StepFuncPointer = std::shared_ptr>; using StateInfoPtr = std::shared_ptr>; /// StateInfo is a struct StateInformation that contains information about the /// current state. StateInfoPtr StateInfo; /// 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 FuzzyFunctionSampleMatches is the fuzzy function that gives the + + /// 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 FuzzyFunctionSampleMatches is the fuzzy function that gives the + + /// The FuzzyFunctionNumOfSamplesMatches is the fuzzy function that gives the /// confidence how many samples from the sampe history match the new sample. StepFuncPointer FuzzyFunctionNumOfSamplesMatches; - /// The FuzzyFunctionSampleMatches is the fuzzy function that gives the - /// confidence how many samples from the sampe history mismatch the new + + /// 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 SampleHistory; /// DAB is a (usually) small history of the last sample values of which a /// average is calculated if the DAB is full. DynamicLengthHistory DAB; /// DABHistory is a history in that the last DABs (to be exact, the averages /// of the last DABs) are stored. 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: + // @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 StateID The Id of the Stateinfo \c StateInformation + /// + /// \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. + /// 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, VariableStateCondition::UNKNOWN, false, false), SampleHistory(sampleHistorySize), DAB(DABSize), DABHistory(DABHistorySize), 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; } void insertSample(INDATATYPE Sample) { SampleHistory.addEntry(Sample); DAB.addEntry(Sample); if (DAB.full()) { PROCDATATYPE AvgOfDAB = DAB.template average(); DABHistory.addEntry(AvgOfDAB); DAB.clear(); } FuzzyFunctionNumOfSamplesMatches->setRightLimit( SampleHistory->numberOfEntries()); 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 } /// Gives the confidence how likely the new sample matches the state. /// /// \param Sample is the actual sample of the observed signal. /// /// \return the confidence of the new sample is matching the state. CONFDATATYPE confSampleMatchesState(INDATATYPE Sample) { CONFDATATYPE ConfidenceOfBestCase = 0; // TODO: history std::vector RelativeDistanceHistory; // calculate distances to all history samples for (auto &HistorySample : SampleHistory) { PROCDATATYPE RelativeDistance = relativeDistance(Sample, HistorySample); RelativeDistanceHistory.push_back(RelativeDistance); } // sort all calculated distances so that the lowest distance (will get the // highest confidence) is at the beginning. sort(RelativeDistanceHistory.begin(), RelativeDistanceHistory.end()); CONFDATATYPE ConfidenceOfWorstFittingSample = 1; unsigned int Case = 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. // TODO: history // TODO: iterate thorugh case and use [] // TODO: for (auto RelativeDistance = RelativeDistanceHistory.cbegin(); RelativeDistance != RelativeDistanceHistory.cend(); RelativeDistance++, Case++) { CONFDATATYPE ConfidenceFromRelativeDistance; // CHECK if I should use * for RelativeDistance if (std::isinf(RelativeDistance)) { // 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 ConfidenceFromRelativeDistance = 0; } else { ConfidenceFromRelativeDistance = FuzzyFunctionSampleMatches(RelativeDistance); } 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))); } return ConfidenceOfBestCase; } /// Gives the confidence how likely the new sample mismatches the state. /// /// \param Sample is the actual sample of the observed signal. /// /// \return the confidence of the new sample is mismatching the state. CONFDATATYPE confSampleMismatchesState(INDATATYPE Sample) { float ConfidenceOfWorstCase = 1; // TODO: history! std::vector RelativeDistanceHistory; // calculate distances to all history samples for (auto &HistorySample : SampleHistory) { RelativeDistanceHistory.push_back( relativeDistance(Sample, HistorySample)); } // sort all calculated distances so that the highest distance (will get the // lowest confidence) is at the beginning. sort(RelativeDistanceHistory.rbegin(), RelativeDistanceHistory.rend()); 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 // confSampleMatchesState(INDATATYPE Sample)". for (unsigned int RelativeDistance = RelativeDistanceHistory.cbegin(); RelativeDistance != RelativeDistanceHistory.cend(); RelativeDistance++, Case++) { CONFDATATYPE ConfidenceFromRelativeDistance; if (std::isinf(RelativeDistance)) { ConfidenceFromRelativeDistance = 1; } else { ConfidenceFromRelativeDistance = FuzzyFunctionSampleMismatches(RelativeDistance); } 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))); } return ConfidenceOfWorstCase; } /// Gives information about the current state. /// /// \return a struct StateInformation that contains information about the /// current state. StateInfoPtr stateInformation(void) { return StateInfo; } 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, ...) { 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, ...) { 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) { 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_STATE_HPP