diff --git a/src/storm-dft/simulator/DFTTraceSimulator.cpp b/src/storm-dft/simulator/DFTTraceSimulator.cpp new file mode 100644 index 000000000..fd0af4b14 --- /dev/null +++ b/src/storm-dft/simulator/DFTTraceSimulator.cpp @@ -0,0 +1,124 @@ +#include "DFTTraceSimulator.h" + +namespace storm { + namespace dft { + namespace simulator { + + template + DFTTraceSimulator::DFTTraceSimulator(storm::storage::DFT const& dft, storm::storage::DFTStateGenerationInfo const& stateGenerationInfo, boost::mt19937& randomGenerator) : dft(dft), stateGenerationInfo(stateGenerationInfo), generator(dft, stateGenerationInfo), randomGenerator(randomGenerator) { + // Set initial state + state = generator.createInitialState(); + } + + template + void DFTTraceSimulator::setRandomNumberGenerator(boost::mt19937& randomNumberGenerator) { + this->randomGenerator = randomNumberGenerator; + } + + template + void DFTTraceSimulator::resetToInitial() { + state = generator.createInitialState();; + } + + template + typename DFTTraceSimulator::DFTStatePointer DFTTraceSimulator::getCurrentState() const { + return state; + } + + template + double DFTTraceSimulator::randomStep() { + auto iterFailable = state->getFailableElements().begin(); + + // Check for absorbing state: + // - either no relevant event remains (i.e., all relevant events have failed already), or + // - no BE can fail + if (!state->hasOperationalRelevantEvent() || iterFailable == state->getFailableElements().end()) { + STORM_LOG_TRACE("No sucessor states available for " << state->getId()); + return -1; + } + + // Get all failable elements + if (iterFailable.isFailureDueToDependency()) { + if (iterFailable.isConflictingDependency()) { + // We take the first dependeny to resolve the non-determinism + STORM_LOG_WARN("Non-determinism present! We take the dependency with the lowest id"); + } + STORM_LOG_TRACE("Let dependency " << *iterFailable.getFailBE(dft).second << " fail"); + bool res = step(iterFailable); + return res ? 0 : -1; + } else { + // Consider all "normal" BE failures + // Initialize with first BE + storm::dft::storage::FailableElements::const_iterator nextFail = iterFailable; + double rate = state->getBERate(iterFailable.getFailBE(dft).first->id()); + storm::utility::ExponentialDistributionGenerator rateGenerator(rate); + double smallestTimebound = rateGenerator.random(randomGenerator); + ++iterFailable; + + // Consider all other BEs and find the one which fails first + for (; iterFailable != state->getFailableElements().end(); ++iterFailable) { + auto nextBE = iterFailable.getFailBE(dft).first; + rate = state->getBERate(nextBE->id()); + rateGenerator = storm::utility::ExponentialDistributionGenerator(rate); + double timebound = rateGenerator.random(randomGenerator); + if (timebound < smallestTimebound) { + // BE fails earlier -> use as nextFail + nextFail = iterFailable; + smallestTimebound = timebound; + } + } + STORM_LOG_TRACE("Let BE " << *nextFail.getFailBE(dft).first << "fail after time " << smallestTimebound); + bool res = step(nextFail); + return res ? smallestTimebound : -1; + } + } + + template + bool DFTTraceSimulator::step(storm::dft::storage::FailableElements::const_iterator nextFailElement) { + if (nextFailElement == state->getFailableElements().end()) { + return false; + } + + auto nextBEPair = nextFailElement.getFailBE(dft); + auto newState = generator.createSuccessorState(state, nextBEPair.first, nextBEPair.second); + + // TODO handle PDEP + + if(newState->isInvalid() || newState->isTransient()) { + STORM_LOG_TRACE("Step is invalid because new state " << (newState->isInvalid() ? "it is invalid" : "the transient fault is ignored")); + return false; + } + + state = newState; + return true; + } + + template + bool DFTTraceSimulator::simulateCompleteTrace(double timebound) { + resetToInitial(); + double time = 0; + while (time <= timebound) { + // Check whether DFT failed within timebound + if (state->hasFailed(dft.getTopLevelIndex())) { + STORM_LOG_TRACE("DFT has failed after " << time); + return true; + } + + // Generate next state + double res = randomStep(); + STORM_LOG_TRACE("Current state: " << dft.getStateString(state)); + if (res < 0) { + // No next state can be reached + STORM_LOG_TRACE("No next state possible in state " << dft.getStateString(state)); + return false; + } + time += res; + } + // Time is up + return false; + } + + template class DFTTraceSimulator; + } + } +} diff --git a/src/storm-dft/simulator/DFTTraceSimulator.h b/src/storm-dft/simulator/DFTTraceSimulator.h new file mode 100644 index 000000000..ac54d87a4 --- /dev/null +++ b/src/storm-dft/simulator/DFTTraceSimulator.h @@ -0,0 +1,97 @@ +#include "storm-dft/generator/DftNextStateGenerator.h" +#include "storm-dft/storage/dft/DFT.h" +#include "storm-dft/storage/dft/DFTState.h" +#include "storm-dft/storage/dft/FailableElements.h" + +#include "storm/utility/random.h" + + +namespace storm { + namespace dft { + namespace simulator { + + /*! + * Simulator for DFTs. + * A step in the simulation corresponds to the failure of one BE (either on its own or triggered by a dependency) + * and the failure propagation through the DFT. + * The simulator also allows to randomly generate a next failure according to the failure rates. + */ + template + class DFTTraceSimulator { + using DFTStatePointer = std::shared_ptr>; + public: + /*! + * Constructor. + * + * @param dft DFT. + * @param stateGenerationInfo Info for state generation. + * @param randomGenerator Random number generator. + */ + DFTTraceSimulator(storm::storage::DFT const& dft, storm::storage::DFTStateGenerationInfo const& stateGenerationInfo, boost::mt19937& randomGenerator); + + /*! + * Set the random number generator. + * + * @param randomNumberGenerator Random number generator. + */ + void setRandomNumberGenerator(boost::mt19937& randomNumberGenerator); + + /*! + * Set the current state back to the intial state in order to start a new simulation. + */ + void resetToInitial(); + + /*! + * Get the current DFT state. + * + * @return DFTStatePointer DFT state. + */ + DFTStatePointer getCurrentState() const; + + /*! + * Perform one simulation step by letting the next element fail. + * + * @param nextFailElement Iterator giving the next element which should fail. + * @return True iff step could be performed successfully. + */ + bool step(storm::dft::storage::FailableElements::const_iterator nextFailElement); + + /*! + * Perform a random step by using the random number generator. + * + * @return double The time which progessed between the last step and this step. + * Returns -1 if no next step could be created. + */ + double randomStep(); + + /*! + * Perform a complete simulation of a failure trace by using the random number generator. + * The simulation starts in the initial state and tries to reach a state where the top-level event of the DFT has failed. + * If this target state can be reached within the given timebound, the simulation was successful. + * + * @param timebound Time bound in which the system failure should occur. + * @return True iff a system failure occurred for the generated trace within the time bound. + */ + bool simulateCompleteTrace(double timebound); + + protected: + + // The DFT used for the generation of next states. + storm::storage::DFT const& dft; + + // General information for the state generation. + storm::storage::DFTStateGenerationInfo const& stateGenerationInfo; + + // Generator for creating next state in DFT + storm::generator::DftNextStateGenerator generator; + + // Current state + DFTStatePointer state; + + // Random number generator + boost::mt19937& randomGenerator; + }; + } + } +} +