diff --git a/apps/ccam/ccam.cpp b/apps/ccam/ccam.cpp index ba19135..8e941c0 100644 --- a/apps/ccam/ccam.cpp +++ b/apps/ccam/ccam.cpp @@ -1,26 +1,36 @@ //===-- apps/ccam/ccam.cpp --------------------------------------*- C++ -*-===// // // The RoSA Framework -- Application CCAM // //===----------------------------------------------------------------------===// /// /// \file apps/ccam/ccam.cpp /// /// \author Maximilian Goetzinger (maximilian.goetzinger@tuwien.ac.at) /// /// \date 2019 /// /// \brief The application CCAM implements the case study from the paper: /// M. Goetzinger, N. TaheriNejad, H. A. Kholerdi, A. Jantsch, E. Willegger, /// T. Glatzl, A.M. Rahmani, T.Sauter, P. Liljeberg: Model - Free Condition /// Monitoring with Confidence //===----------------------------------------------------------------------===// #include "rosa/agent/StateDetector.hpp" #include int main(void) { - std::cout << "Test 123\n"; + + // Just some tests :D + std::vector vec = {7, 3, 5, 1, 9}; + + std::sort(vec.rbegin(), vec.rend()); + // std::reverse(vec.begin(), vec.end()); + + for (auto it = vec.cbegin(); it != vec.cend(); ++it) { + std::cout << *it << ' '; + } + return 0; } diff --git a/include/rosa/agent/State.hpp b/include/rosa/agent/State.hpp index ad47872..c722c21 100644 --- a/include/rosa/agent/State.hpp +++ b/include/rosa/agent/State.hpp @@ -1,312 +1,367 @@ //===-- 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 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), "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 /// confidence how bad the new sample matches another sample in the sample /// history. PartFuncPointer FuzzyFunctionSampleMismatches; /// The FuzzyFunctionSampleMatches 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 /// 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: /// Creates an instance by setting all parameters 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()) { DABDATATYPE AvgOfDAB = DAB.template average(); DABHistory.addEntry(AvgOfDAB); DAB.clear(); } - // TODO @Benedikt: stepfunction auch andere richtung + // @Benedikt: Passt das bei beiden so, wie es aufrufe (also links und + // rechts veränderung)? 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 } - // TODO: check this function again + /// 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) { + confSampleMatchesState(INDATATYPE Sample) { - DISTDATATYPE RelDist; - CONFDATATYPE HighestConf = 0; + CONFDATATYPE ConfidenceOfBestCase = 0; - // QUESTION: should I also use a history (array) for that? - std::vector RelDistHistValuesAndSampleValue; + // @benedikt: should I also use your history or an normal array for that? + std::vector RelativeDistanceHistory; - for (auto &hs : SampleHistory) { - RelDist = relativeDistance(sample, hs); - RelDistHistValuesAndSampleValue.push_back(RelDist); + // calculate distances to all history samples + for (auto &HistorySample : SampleHistory) { + DISTDATATYPE RelativeDistance = relativeDistance(Sample, HistorySample); + RelativeDistanceHistory.push_back(RelativeDistance); } - sort(begin(RelDistHistValuesAndSampleValue), - end(RelDistHistValuesAndSampleValue)); + // 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. - for (unsigned int numOfHistSamplesIncluded = 1; - numOfHistSamplesIncluded <= RelDistHistValuesAndSampleValue.size(); - numOfHistSamplesIncluded++) { + for (auto RelativeDistance = RelativeDistanceHistory.cbegin(); + RelativeDistance != RelativeDistanceHistory.cend(); + RelativeDistance++, Case++) { - CONFDATATYPE LowestConfOfSamplesIncluded = 1; - unsigned int HistSampleCounter = 0; + CONFDATATYPE ConfidenceFromRelativeDistance; - for (auto &D : RelDistHistValuesAndSampleValue) { - if (HistSampleCounter >= numOfHistSamplesIncluded) - break; + if (std::isinf(Case)) { + ConfidenceFromRelativeDistance = 0; + } else { + ConfidenceFromRelativeDistance = FuzzyFunctionSampleMatches(Case); + } - CONFDATATYPE confRelDist; + ConfidenceOfWorstFittingSample = fuzzyAND(ConfidenceOfWorstFittingSample, + ConfidenceFromRelativeDistance); - if (std::isinf(D) == false) { - confRelDist = FuzzyFunctionSampleMatches(D); - } else { - confRelDist = 0; - } + // @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, ConfidenceOfWorstFittingSample, + FuzzyFunctionNumOfSamplesMatches((CONFDATATYPE)Case)); + } + + return ConfidenceOfBestCase; + } - // std::min is equal to Fuzzy AND -> maybe using - LowestConfOfSamplesIncluded = - fuzzyAND(LowestConfOfSamplesIncluded, confRelDist); - HistSampleCounter++; + /// 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; + + // @benedikt: should I also use your history or an normal array for that? + 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); } - // @benedikt: do you think my fuzzyOR function does the same as the next - // out-commented lines? - // HighestConf = std::max(HighestConf, - // std::max(LowestConfOfSamplesIncluded, - // FuzzyFunctionNumOfSamplesMatches((CONFDATATYPE)HistSampleCounter))); - // @benedikt: TODO: change old-style cast to one of these - // reinterpret_cast, + 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? - fuzzyOR( - HighestConf, LowestConfOfSamplesIncluded, - FuzzyFunctionNumOfSamplesMatches((CONFDATATYPE)HistSampleCounter)); + ConfidenceOfWorstCase = fuzzyAND( + ConfidenceOfWorstCase, + fuzzyOR(ConfidenceOfBestFittingSample, + FuzzyFunctionNumOfSamplesMismatches((CONFDATATYPE)Case))); } - } - CONFDATATYPE - confSampleMismatchesState(INDATATYPE sample) { - // TODO: do it in the same way as confSampleMatchesState(INDATATYPE sample) + 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(CONFDATATYPE n_args, ...) { va_list ap; va_start(ap, n_args); CONFDATATYPE min = va_arg(ap, CONFDATATYPE); for (CONFDATATYPE i = 2; i <= n_args; i++) { CONFDATATYPE a = va_arg(ap, CONFDATATYPE); //@benedikt: instead of the if, I could use "std::min" if (a < min) min = a; } va_end(ap); return min; } // copied from the internet // (https://stackoverflow.com/questions/1657883/variable-number-of-arguments-in-c) CONFDATATYPE fuzzyOR(CONFDATATYPE n_args, ...) { va_list ap; va_start(ap, n_args); CONFDATATYPE max = va_arg(ap, CONFDATATYPE); for (CONFDATATYPE i = 2; i <= n_args; i++) { CONFDATATYPE a = va_arg(ap, CONFDATATYPE); //@benedikt: instead of the if, I could use "std::max" if (a > max) max = a; } va_end(ap); return max; } - // 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 { 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