diff --git a/src/storm-dft/simulator/DFTTraceSimulator.cpp b/src/storm-dft/simulator/DFTTraceSimulator.cpp index 11f49c588..9172e76ad 100644 --- a/src/storm-dft/simulator/DFTTraceSimulator.cpp +++ b/src/storm-dft/simulator/DFTTraceSimulator.cpp @@ -85,25 +85,24 @@ namespace storm { } template - double DFTTraceSimulator::randomStep() { + std::pair DFTTraceSimulator::randomStep() { auto retTuple = this->randomNextFailure(); storm::dft::storage::FailableElements::const_iterator nextFailable = std::get<0>(retTuple); double time = std::get<1>(retTuple); bool successful = std::get<2>(retTuple); - //double time = pairNextFailure.second; if (time < 0) { - return -1; + return std::make_pair(SimulationResult::UNSUCCESSFUL, -1); } else { // Apply next failure - bool res = step(nextFailable, successful); - return res ? time : -1; + return std::make_pair(step(nextFailable, successful), time); } } template - bool DFTTraceSimulator::step(storm::dft::storage::FailableElements::const_iterator nextFailElement, bool dependencySuccessful) { + SimulationResult DFTTraceSimulator::step(storm::dft::storage::FailableElements::const_iterator nextFailElement, bool dependencySuccessful) { if (nextFailElement == state->getFailableElements().end()) { - return false; + // No next failure possible + return SimulationResult::UNSUCCESSFUL; } auto nextBEPair = nextFailElement.getFailBE(dft); @@ -111,40 +110,73 @@ namespace storm { 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; + return SimulationResult::INVALID; } state = newState; - return true; + return SimulationResult::SUCCESSFUL; } template - bool DFTTraceSimulator::simulateCompleteTrace(double timebound) { + SimulationResult DFTTraceSimulator::simulateCompleteTrace(double timebound) { resetToInitial(); + + // Check whether DFT is initially already failed. + if (state->hasFailed(dft.getTopLevelIndex())) { + STORM_LOG_TRACE("DFT is initially failed"); + return SimulationResult::SUCCESSFUL; + } + 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 failure + auto retTuple = randomNextFailure(); + storm::dft::storage::FailableElements::const_iterator nextFailable = std::get<0>(retTuple); + double addTime = std::get<1>(retTuple); + bool successfulDependency = std::get<2>(retTuple); + if (addTime < 0) { + // No next state can be reached, because no element can fail anymore. + STORM_LOG_TRACE("No next state possible in state " << dft.getStateString(state) << " because no element can fail anymore"); + return SimulationResult::UNSUCCESSFUL; } - // Generate next state - double res = randomStep(); + // TODO: exit if time would be up after this failure + // This is only correct if no invalid states are possible! (no restrictors and no transient failures) + + // Apply next failure + auto stepResult = step(nextFailable, successfulDependency); 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; + + // Check whether state is invalid + if (stepResult != SimulationResult::SUCCESSFUL) { + STORM_LOG_ASSERT(stepResult == SimulationResult::INVALID, "Result of simulation step should be invalid."); + // No next state can be reached, because the state is invalid. + STORM_LOG_TRACE("No next state possible in state " << dft.getStateString(state) << " because simulation was invalid"); + STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Handling of invalid states is not supported for simulation"); + return SimulationResult::INVALID; + } + + // Check whether time is up + // Checking whether the time is up must be performed after checking if a state is invalid. + // Otherwise we would erroneously mark invalid traces as unsucessful. + time += addTime; + if (time > timebound) { + STORM_LOG_TRACE("Time limit" << timebound << " exceeded: " << time); + return SimulationResult::UNSUCCESSFUL; + } + + // Check whether DFT is failed + if (state->hasFailed(dft.getTopLevelIndex())) { + STORM_LOG_TRACE("DFT has failed after " << time); + return SimulationResult::SUCCESSFUL; } - time += res; } - // Time is up - return false; + STORM_LOG_ASSERT(false, "Should not be reachable"); + return SimulationResult::UNSUCCESSFUL; } template<> - bool DFTTraceSimulator::simulateCompleteTrace(double timebound) { + SimulationResult DFTTraceSimulator::simulateCompleteTrace(double timebound) { STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Simulation not support for parametric DFTs."); } diff --git a/src/storm-dft/simulator/DFTTraceSimulator.h b/src/storm-dft/simulator/DFTTraceSimulator.h index 6ef4e1ea0..92eecbf5d 100644 --- a/src/storm-dft/simulator/DFTTraceSimulator.h +++ b/src/storm-dft/simulator/DFTTraceSimulator.h @@ -10,6 +10,12 @@ namespace storm { namespace dft { namespace simulator { + /*! + * Simulation result. + * + */ + enum class SimulationResult { SUCCESSFUL, UNSUCCESSFUL, INVALID }; + /*! * Simulator for DFTs. * A step in the simulation corresponds to the failure of one BE (either on its own or triggered by a dependency) @@ -19,6 +25,7 @@ namespace storm { template class DFTTraceSimulator { using DFTStatePointer = std::shared_ptr>; + public: /*! * Constructor. @@ -54,9 +61,9 @@ namespace storm { * @param nextFailElement Iterator giving the next element which should fail. * @param dependencySuccessful Whether the triggering dependency was successful. * If the dependency is unsuccessful, no BE fails and only the depedendy is marked as failed. - * @return True iff step could be performed successfully. + * @return Successful if step could be performed, unsuccesful if no element can fail or invalid if the next state is invalid (due to a restrictor). */ - bool step(storm::dft::storage::FailableElements::const_iterator nextFailElement, bool dependencySuccessful = true); + SimulationResult step(storm::dft::storage::FailableElements::const_iterator nextFailElement, bool dependencySuccessful = true); /*! * Randomly pick an element which fails next (either a BE or a dependency which triggers a BE) and the time after which it fails. @@ -70,20 +77,22 @@ namespace storm { /*! * 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. + * @return Pair of the simulation result (successful, unsuccesful, invalid) and the time which progessed between the last step and this step. */ - double randomStep(); + std::pair 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. + * If an invalid state (due to a restrictor) was reached, the simulated trace is invalid. * * @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. + * @return Simulation result is (1) successful if a system failure occurred for the generated trace within the time bound, + * (2) unsuccesfull, if no system failure occurred within the time bound, or + * (3) invalid, if an invalid state (due to a restrictor) was reached during the trace generation. */ - bool simulateCompleteTrace(double timebound); + SimulationResult simulateCompleteTrace(double timebound); protected: diff --git a/src/test/storm-dft/simulator/DftSimulatorTest.cpp b/src/test/storm-dft/simulator/DftSimulatorTest.cpp index bfb07e3fa..ff34b2880 100644 --- a/src/test/storm-dft/simulator/DftSimulatorTest.cpp +++ b/src/test/storm-dft/simulator/DftSimulatorTest.cpp @@ -10,8 +10,8 @@ namespace { - // Helper function - double simulateDft(std::string const& file, double timebound, size_t noRuns) { + // Helper functions + std::pair simulateDft(std::string const& file, double timebound, size_t noRuns) { // Load, build and prepare DFT storm::transformations::dft::DftTransformator dftTransformator = storm::transformations::dft::DftTransformator(); std::shared_ptr> dft = dftTransformator.transformBinaryFDEPs(*(storm::api::loadDFTGalileoFile(file))); @@ -27,104 +27,162 @@ namespace { storm::storage::DFTStateGenerationInfo stateGenerationInfo(dft->buildStateGenerationInfo(symmetries)); // Init random number generator + //storm::utility::setLogLevel(l3pp::LogLevel::TRACE); boost::mt19937 gen(5u); storm::dft::simulator::DFTTraceSimulator simulator(*dft, stateGenerationInfo, gen); - size_t count = 0;; - bool res; + size_t count = 0; + size_t invalid = 0; + storm::dft::simulator::SimulationResult res; for (size_t i=0; ihasFailed(dft->getTopLevelIndex())); + storm::dft::simulator::SimulationResult res; + double timebound; // First random step - double timebound = simulator.randomStep(); + std::tie(res, timebound) = simulator.randomStep(); + EXPECT_EQ(res, storm::dft::simulator::SimulationResult::SUCCESSFUL); #if BOOST_VERSION > 106400 // Older Boost versions yield different value EXPECT_FLOAT_EQ(timebound, 0.522079); @@ -182,7 +185,8 @@ namespace { state = simulator.getCurrentState(); EXPECT_FALSE(state->hasFailed(dft->getTopLevelIndex())); - timebound = simulator.randomStep(); + std::tie(res, timebound) = simulator.randomStep(); + EXPECT_EQ(res, storm::dft::simulator::SimulationResult::SUCCESSFUL); #if BOOST_VERSION > 106400 // Older Boost versions yield different value EXPECT_FLOAT_EQ(timebound, 0.9497214);