From 80fc8fb56ba5eaad73492d0a6d5b1149d701ec00 Mon Sep 17 00:00:00 2001 From: Alexander Bork Date: Fri, 17 May 2019 13:30:34 +0200 Subject: [PATCH 01/47] Fix for error that checkbound may be large than number of Markovian states --- src/storm-dft/modelchecker/dft/DFTASFChecker.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/storm-dft/modelchecker/dft/DFTASFChecker.cpp b/src/storm-dft/modelchecker/dft/DFTASFChecker.cpp index 0ed94401d..87cd93a17 100644 --- a/src/storm-dft/modelchecker/dft/DFTASFChecker.cpp +++ b/src/storm-dft/modelchecker/dft/DFTASFChecker.cpp @@ -565,6 +565,7 @@ namespace storm { DFTASFChecker::checkFailsLeqWithEqNonMarkovianState(uint64_t checkbound, uint64_t nrNonMarkovian) { STORM_LOG_ASSERT(solver, "SMT Solver was not initialized, call toSolver() before checking queries"); std::vector markovianIndices; + checkbound = std::min(checkbound, markovianVariables.size()); // Get Markovian variable indices up until given timepoint for (uint64_t i = 0; i < checkbound; ++i) { markovianIndices.push_back(markovianVariables.at(i)); @@ -576,7 +577,6 @@ namespace storm { timePointVariables.at(dft.getTopLevelIndex()), checkbound); std::shared_ptr manager = solver->getManager().getSharedPointer(); solver->add(tleFailedConstr->toExpression(varNames, manager)); - // Constraint that a given number of non-Markovian states are visited std::shared_ptr nonMarkovianConstr = std::make_shared( markovianIndices, nrNonMarkovian); From 38ccd51ae1ca1bb8a9e3ee86b5b6feff3c70277a Mon Sep 17 00:00:00 2001 From: Alexander Bork Date: Fri, 17 May 2019 13:31:46 +0200 Subject: [PATCH 02/47] Added check for conflicts between dependencies in the DFT --- .../modelchecker/dft/DFTASFChecker.cpp | 73 +++++++++++++++++++ .../modelchecker/dft/DFTASFChecker.h | 21 ++++++ .../modelchecker/dft/SmtConstraint.cpp | 46 ++++++++++++ 3 files changed, 140 insertions(+) diff --git a/src/storm-dft/modelchecker/dft/DFTASFChecker.cpp b/src/storm-dft/modelchecker/dft/DFTASFChecker.cpp index 87cd93a17..1583f3867 100644 --- a/src/storm-dft/modelchecker/dft/DFTASFChecker.cpp +++ b/src/storm-dft/modelchecker/dft/DFTASFChecker.cpp @@ -610,6 +610,49 @@ namespace storm { return res; } + storm::solver::SmtSolver::CheckResult + DFTASFChecker::checkDependencyConflict(uint64_t dep1Index, uint64_t dep2Index, uint64_t timeout) { + std::vector> andConstr; + std::vector> orConstr; + STORM_LOG_DEBUG( + "Check " << dft.getElement(dep1Index)->name() << " and " << dft.getElement(dep2Index)->name()); + andConstr.clear(); + // FDEP2 was triggered before dependent elements have failed + andConstr.push_back(std::make_shared( + timePointVariables.at(dep2Index), dependencyVariables.at(dep2Index))); + // AND FDEP1 is triggered before FDEP2 is resolved + andConstr.push_back(std::make_shared( + timePointVariables.at(dep1Index), timePointVariables.at(dep2Index))); + andConstr.push_back(std::make_shared( + timePointVariables.at(dep1Index), dependencyVariables.at(dep2Index))); + std::shared_ptr betweenConstr1 = std::make_shared(andConstr); + + andConstr.clear(); + // FDEP1 was triggered before dependent elements have failed + andConstr.push_back(std::make_shared( + timePointVariables.at(dep1Index), dependencyVariables.at(dep1Index))); + // AND FDEP2 is triggered before FDEP1 is resolved + andConstr.push_back(std::make_shared( + timePointVariables.at(dep2Index), timePointVariables.at(dep1Index))); + andConstr.push_back(std::make_shared( + timePointVariables.at(dep2Index), dependencyVariables.at(dep1Index))); + std::shared_ptr betweenConstr2 = std::make_shared(andConstr); + + orConstr.clear(); + // Either one of the above constraints holds + orConstr.push_back(betweenConstr1); + orConstr.push_back(betweenConstr2); + std::shared_ptr checkConstr = std::make_shared(orConstr); + std::shared_ptr manager = solver->getManager().getSharedPointer(); + solver->push(); + solver->add(checkConstr->toExpression(varNames, manager)); + setSolverTimeout(timeout * 1000); + storm::solver::SmtSolver::CheckResult res = solver->check(); + unsetSolverTimeout(); + solver->pop(); + return res; + } + uint64_t DFTASFChecker::correctLowerBound(uint64_t bound, uint_fast64_t timeout) { STORM_LOG_ASSERT(solver, "SMT Solver was not initialized, call toSolver() before checking queries"); STORM_LOG_DEBUG("Lower bound correction - try to correct bound " << std::to_string(bound)); @@ -741,5 +784,35 @@ namespace storm { } return bound; } + + std::vector> DFTASFChecker::getDependencyConflicts(uint_fast64_t timeout) { + STORM_LOG_ASSERT(solver, "SMT Solver was not initialized, call toSolver() before checking queries"); + std::vector> res; + uint64_t dep1Index; + uint64_t dep2Index; + for (size_t i = 0; i < dft.getDependencies().size(); ++i) { + dep1Index = dft.getDependencies().at(i); + for (size_t j = i + 1; j < dft.getDependencies().size(); ++j) { + dep2Index = dft.getDependencies().at(j); + switch (checkDependencyConflict(dep1Index, dep2Index, timeout)) { + case storm::solver::SmtSolver::CheckResult::Sat: + STORM_LOG_DEBUG("Conflict between " << dft.getElement(dep1Index)->name() << " and " + << dft.getElement(dep2Index)->name()); + res.emplace_back(std::pair(dep1Index, dep2Index)); + break; + case storm::solver::SmtSolver::CheckResult::Unknown: + STORM_LOG_DEBUG("Unknown: Conflict between " << dft.getElement(dep1Index)->name() << " and " + << dft.getElement(dep2Index)->name()); + res.emplace_back(std::pair(dep1Index, dep2Index)); + break; + default: + STORM_LOG_DEBUG("No conflict between " << dft.getElement(dep1Index)->name() << " and " + << dft.getElement(dep2Index)->name()); + break; + } + } + } + return res; + } } } diff --git a/src/storm-dft/modelchecker/dft/DFTASFChecker.h b/src/storm-dft/modelchecker/dft/DFTASFChecker.h index 84310b775..75eef8c64 100644 --- a/src/storm-dft/modelchecker/dft/DFTASFChecker.h +++ b/src/storm-dft/modelchecker/dft/DFTASFChecker.h @@ -79,6 +79,19 @@ namespace storm { */ storm::solver::SmtSolver::CheckResult checkTleFailsWithLeq(uint64_t bound); + /** + * Check if two given dependencies are conflicting in their resolution, i.e. check if non-determinism may occur. + * Note that this is a very conservative check using SMT formulae. + * We only check if sequences exist, where one of the dependencies is triggered before the other is completely resolved + * + * @param dep1Index Index of the first dependency + * @param dep2Index Index of the second dependency + * @param timeout timeout for the solver + * @return "Sat" if the dependencies are conflicting, "Unsat" if they are not, otherwise "Unknown" + */ + storm::solver::SmtSolver::CheckResult + checkDependencyConflict(uint64_t dep1Index, uint64_t dep2Index, uint64_t timeout = 10); + /** * Get the minimal number of BEs necessary for the TLE to fail (lower bound for number of failures to check) * @@ -96,6 +109,14 @@ namespace storm { */ uint64_t getAlwaysFailedBound(uint_fast64_t timeout = 10); + /** + * Get a vector of index pairs of FDEPs which are conflicting according to a conservative definition + * + * @param timeout timeout for each query in seconds, defaults to 10 seconds + * @return a vector of pairs of FDEP indices which are conflicting + */ + std::vector> getDependencyConflicts(uint_fast64_t timeout = 10); + /** * Set the timeout of the solver * diff --git a/src/storm-dft/modelchecker/dft/SmtConstraint.cpp b/src/storm-dft/modelchecker/dft/SmtConstraint.cpp index 78e963d05..ef16d580a 100644 --- a/src/storm-dft/modelchecker/dft/SmtConstraint.cpp +++ b/src/storm-dft/modelchecker/dft/SmtConstraint.cpp @@ -493,6 +493,52 @@ namespace storm { uint64_t var2Index; }; + class IsLessEqual : public SmtConstraint { + public: + IsLessEqual(uint64_t varIndex1, uint64_t varIndex2) : var1Index(varIndex1), var2Index(varIndex2) { + } + + virtual ~IsLessEqual() { + } + + std::string toSmtlib2(std::vector const &varNames) const override { + return "(<= " + varNames.at(var1Index) + " " + varNames.at(var2Index) + ")"; + } + + storm::expressions::Expression toExpression(std::vector const &varNames, + std::shared_ptr manager) const override { + return manager->getVariableExpression(varNames.at(var1Index)) <= + manager->getVariableExpression(varNames.at(var2Index)); + } + + private: + uint64_t var1Index; + uint64_t var2Index; + }; + + class IsGreaterEqual : public SmtConstraint { + public: + IsGreaterEqual(uint64_t varIndex1, uint64_t varIndex2) : var1Index(varIndex1), var2Index(varIndex2) { + } + + virtual ~IsGreaterEqual() { + } + + std::string toSmtlib2(std::vector const &varNames) const override { + return "(>= " + varNames.at(var1Index) + " " + varNames.at(var2Index) + ")"; + } + + storm::expressions::Expression toExpression(std::vector const &varNames, + std::shared_ptr manager) const override { + return manager->getVariableExpression(varNames.at(var1Index)) >= + manager->getVariableExpression(varNames.at(var2Index)); + } + + private: + uint64_t var1Index; + uint64_t var2Index; + }; + class PairwiseDifferent : public SmtConstraint { public: From 576582478273109ca0665da7707c0b09f7d92517 Mon Sep 17 00:00:00 2001 From: Alexander Bork Date: Fri, 17 May 2019 13:33:30 +0200 Subject: [PATCH 03/47] Reworked SMT result interface --- src/storm-dft/api/storm-dft.cpp | 35 ++++++++++++++++++--------------- src/storm-dft/api/storm-dft.h | 8 +++++++- 2 files changed, 26 insertions(+), 17 deletions(-) diff --git a/src/storm-dft/api/storm-dft.cpp b/src/storm-dft/api/storm-dft.cpp index 623d75a16..6f9943768 100644 --- a/src/storm-dft/api/storm-dft.cpp +++ b/src/storm-dft/api/storm-dft.cpp @@ -43,33 +43,36 @@ namespace storm { } template<> - std::vector + storm::api::SMTResult analyzeDFTSMT(storm::storage::DFT const &dft, bool printOutput) { storm::modelchecker::DFTASFChecker smtChecker(dft); smtChecker.toSolver(); - std::vector results; + storm::api::SMTResult results; - results.push_back(smtChecker.checkTleNeverFailed()); - uint64_t lower_bound = smtChecker.getLeastFailureBound(); - uint64_t upper_bound = smtChecker.getAlwaysFailedBound(); + results.lowerBEBound = smtChecker.getLeastFailureBound(); + results.upperBEBound = smtChecker.getAlwaysFailedBound(); if (printOutput) { - // TODO add suitable output function, maybe add query descriptions for better readability - for (size_t i = 0; i < results.size(); ++i) { - std::string tmp = "unknown"; - if (results.at(i) == storm::solver::SmtSolver::CheckResult::Sat) { - tmp = "SAT"; - } else if (results.at(i) == storm::solver::SmtSolver::CheckResult::Unsat) { - tmp = "UNSAT"; - } + STORM_PRINT("BE FAILURE BOUNDS" << std::endl << + "========================================" << std::endl << + "Lower bound: " << std::to_string(results.lowerBEBound) << std::endl << + "Upper bound: " << std::to_string(results.upperBEBound) << std::endl) + } + results.fdepConflicts = smtChecker.getDependencyConflicts(); + if (printOutput) { + STORM_PRINT("========================================" << std::endl << + "FDEP CONFLICTS" << std::endl << + "========================================" + << std::endl) + for (auto pair: results.fdepConflicts) { + STORM_PRINT("Conflict between " << dft.getElement(pair.first)->name() << " and " + << dft.getElement(pair.second)->name() << std::endl) } - std::cout << "Lower bound: " << std::to_string(lower_bound) << std::endl; - std::cout << "Upper bound: " << std::to_string(upper_bound) << std::endl; } return results; } template<> - std::vector + storm::api::SMTResult analyzeDFTSMT(storm::storage::DFT const &dft, bool printOutput) { STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Analysis by SMT not supported for this data type."); diff --git a/src/storm-dft/api/storm-dft.h b/src/storm-dft/api/storm-dft.h index 0388e9610..6ff06c9c9 100644 --- a/src/storm-dft/api/storm-dft.h +++ b/src/storm-dft/api/storm-dft.h @@ -13,6 +13,12 @@ namespace storm { namespace api { + struct SMTResult { + uint64_t lowerBEBound; + uint64_t upperBEBound; + std::vector> fdepConflicts; + }; + /*! * Load DFT from Galileo file. @@ -100,7 +106,7 @@ namespace storm { * @return Result result vector */ template - std::vector + storm::api::SMTResult analyzeDFTSMT(storm::storage::DFT const &dft, bool printOutput); /*! From 965c54b76d01af4f37ac694af78581d5dca6ac15 Mon Sep 17 00:00:00 2001 From: Alexander Bork Date: Fri, 17 May 2019 15:21:15 +0200 Subject: [PATCH 04/47] Fixed error that only one FDEP was required to be active for a possible conflict to be detected --- src/storm-dft/modelchecker/dft/DFTASFChecker.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/storm-dft/modelchecker/dft/DFTASFChecker.cpp b/src/storm-dft/modelchecker/dft/DFTASFChecker.cpp index 1583f3867..beecbc805 100644 --- a/src/storm-dft/modelchecker/dft/DFTASFChecker.cpp +++ b/src/storm-dft/modelchecker/dft/DFTASFChecker.cpp @@ -341,7 +341,7 @@ namespace storm { auto const &trigger = dependency->triggerEvent(); std::vector dependentIndices; for (size_t j = 0; j < dependentEvents.size(); ++j) { - dependentIndices.push_back(dependentEvents[j]->id()); + dependentIndices.push_back(timePointVariables.at(dependentEvents[j]->id())); } constraints.push_back(std::make_shared(dependencyVariables.at(i), dependentIndices)); @@ -617,7 +617,9 @@ namespace storm { STORM_LOG_DEBUG( "Check " << dft.getElement(dep1Index)->name() << " and " << dft.getElement(dep2Index)->name()); andConstr.clear(); - // FDEP2 was triggered before dependent elements have failed + // Both FDEPs were triggered before dependent elements have failed + andConstr.push_back(std::make_shared( + timePointVariables.at(dep1Index), dependencyVariables.at(dep1Index))); andConstr.push_back(std::make_shared( timePointVariables.at(dep2Index), dependencyVariables.at(dep2Index))); // AND FDEP1 is triggered before FDEP2 is resolved @@ -628,9 +630,11 @@ namespace storm { std::shared_ptr betweenConstr1 = std::make_shared(andConstr); andConstr.clear(); - // FDEP1 was triggered before dependent elements have failed + // Both FDEPs were triggered before dependent elements have failed andConstr.push_back(std::make_shared( timePointVariables.at(dep1Index), dependencyVariables.at(dep1Index))); + andConstr.push_back(std::make_shared( + timePointVariables.at(dep2Index), dependencyVariables.at(dep2Index))); // AND FDEP2 is triggered before FDEP1 is resolved andConstr.push_back(std::make_shared( timePointVariables.at(dep2Index), timePointVariables.at(dep1Index))); From baa8a6dbcbcdbac45e3eb6b59473ebd02b155b66 Mon Sep 17 00:00:00 2001 From: Alexander Bork Date: Fri, 17 May 2019 16:56:12 +0200 Subject: [PATCH 05/47] Improved conflict search by directly capturing DEPs with same trigger --- src/storm-dft/api/storm-dft.cpp | 10 +++-- .../modelchecker/dft/DFTASFChecker.cpp | 37 +++++++++++-------- 2 files changed, 29 insertions(+), 18 deletions(-) diff --git a/src/storm-dft/api/storm-dft.cpp b/src/storm-dft/api/storm-dft.cpp index 6f9943768..16b6f4d00 100644 --- a/src/storm-dft/api/storm-dft.cpp +++ b/src/storm-dft/api/storm-dft.cpp @@ -45,19 +45,23 @@ namespace storm { template<> storm::api::SMTResult analyzeDFTSMT(storm::storage::DFT const &dft, bool printOutput) { + uint64_t solverTimeout = 10; + storm::modelchecker::DFTASFChecker smtChecker(dft); smtChecker.toSolver(); storm::api::SMTResult results; - results.lowerBEBound = smtChecker.getLeastFailureBound(); - results.upperBEBound = smtChecker.getAlwaysFailedBound(); + results.lowerBEBound = smtChecker.getLeastFailureBound(solverTimeout); + results.upperBEBound = smtChecker.getAlwaysFailedBound(solverTimeout); if (printOutput) { STORM_PRINT("BE FAILURE BOUNDS" << std::endl << "========================================" << std::endl << "Lower bound: " << std::to_string(results.lowerBEBound) << std::endl << "Upper bound: " << std::to_string(results.upperBEBound) << std::endl) } - results.fdepConflicts = smtChecker.getDependencyConflicts(); + + results.fdepConflicts = smtChecker.getDependencyConflicts(solverTimeout); + if (printOutput) { STORM_PRINT("========================================" << std::endl << "FDEP CONFLICTS" << std::endl << diff --git a/src/storm-dft/modelchecker/dft/DFTASFChecker.cpp b/src/storm-dft/modelchecker/dft/DFTASFChecker.cpp index beecbc805..85326c8b2 100644 --- a/src/storm-dft/modelchecker/dft/DFTASFChecker.cpp +++ b/src/storm-dft/modelchecker/dft/DFTASFChecker.cpp @@ -798,21 +798,28 @@ namespace storm { dep1Index = dft.getDependencies().at(i); for (size_t j = i + 1; j < dft.getDependencies().size(); ++j) { dep2Index = dft.getDependencies().at(j); - switch (checkDependencyConflict(dep1Index, dep2Index, timeout)) { - case storm::solver::SmtSolver::CheckResult::Sat: - STORM_LOG_DEBUG("Conflict between " << dft.getElement(dep1Index)->name() << " and " - << dft.getElement(dep2Index)->name()); - res.emplace_back(std::pair(dep1Index, dep2Index)); - break; - case storm::solver::SmtSolver::CheckResult::Unknown: - STORM_LOG_DEBUG("Unknown: Conflict between " << dft.getElement(dep1Index)->name() << " and " - << dft.getElement(dep2Index)->name()); - res.emplace_back(std::pair(dep1Index, dep2Index)); - break; - default: - STORM_LOG_DEBUG("No conflict between " << dft.getElement(dep1Index)->name() << " and " - << dft.getElement(dep2Index)->name()); - break; + if (dft.getDependency(dep1Index)->triggerEvent() == dft.getDependency(dep2Index)->triggerEvent()) { + STORM_LOG_DEBUG("Conflict between " << dft.getElement(dep1Index)->name() << " and " + << dft.getElement(dep2Index)->name() << ": Same trigger"); + res.emplace_back(std::pair(dep1Index, dep2Index)); + } else { + switch (checkDependencyConflict(dep1Index, dep2Index, timeout)) { + case storm::solver::SmtSolver::CheckResult::Sat: + STORM_LOG_DEBUG("Conflict between " << dft.getElement(dep1Index)->name() << " and " + << dft.getElement(dep2Index)->name()); + res.emplace_back(std::pair(dep1Index, dep2Index)); + break; + case storm::solver::SmtSolver::CheckResult::Unknown: + STORM_LOG_DEBUG( + "Unknown: Conflict between " << dft.getElement(dep1Index)->name() << " and " + << dft.getElement(dep2Index)->name()); + res.emplace_back(std::pair(dep1Index, dep2Index)); + break; + default: + STORM_LOG_DEBUG("No conflict between " << dft.getElement(dep1Index)->name() << " and " + << dft.getElement(dep2Index)->name()); + break; + } } } } From e06bb99cc46ea5637d88d9bd9cbbc8ba60118f74 Mon Sep 17 00:00:00 2001 From: Alexander Bork Date: Wed, 22 May 2019 11:02:47 +0200 Subject: [PATCH 06/47] Changed DEP conflict constraint to avoid double check --- .../modelchecker/dft/DFTASFChecker.cpp | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/storm-dft/modelchecker/dft/DFTASFChecker.cpp b/src/storm-dft/modelchecker/dft/DFTASFChecker.cpp index 85326c8b2..a5633bb56 100644 --- a/src/storm-dft/modelchecker/dft/DFTASFChecker.cpp +++ b/src/storm-dft/modelchecker/dft/DFTASFChecker.cpp @@ -617,11 +617,6 @@ namespace storm { STORM_LOG_DEBUG( "Check " << dft.getElement(dep1Index)->name() << " and " << dft.getElement(dep2Index)->name()); andConstr.clear(); - // Both FDEPs were triggered before dependent elements have failed - andConstr.push_back(std::make_shared( - timePointVariables.at(dep1Index), dependencyVariables.at(dep1Index))); - andConstr.push_back(std::make_shared( - timePointVariables.at(dep2Index), dependencyVariables.at(dep2Index))); // AND FDEP1 is triggered before FDEP2 is resolved andConstr.push_back(std::make_shared( timePointVariables.at(dep1Index), timePointVariables.at(dep2Index))); @@ -630,11 +625,6 @@ namespace storm { std::shared_ptr betweenConstr1 = std::make_shared(andConstr); andConstr.clear(); - // Both FDEPs were triggered before dependent elements have failed - andConstr.push_back(std::make_shared( - timePointVariables.at(dep1Index), dependencyVariables.at(dep1Index))); - andConstr.push_back(std::make_shared( - timePointVariables.at(dep2Index), dependencyVariables.at(dep2Index))); // AND FDEP2 is triggered before FDEP1 is resolved andConstr.push_back(std::make_shared( timePointVariables.at(dep2Index), timePointVariables.at(dep1Index))); @@ -646,7 +636,17 @@ namespace storm { // Either one of the above constraints holds orConstr.push_back(betweenConstr1); orConstr.push_back(betweenConstr2); - std::shared_ptr checkConstr = std::make_shared(orConstr); + + // Both FDEPs were triggered before dependent elements have failed + andConstr.clear(); + andConstr.push_back(std::make_shared( + timePointVariables.at(dep1Index), dependencyVariables.at(dep1Index))); + andConstr.push_back(std::make_shared( + timePointVariables.at(dep2Index), dependencyVariables.at(dep2Index))); + andConstr.push_back(std::make_shared(orConstr)); + + std::shared_ptr checkConstr = std::make_shared(andConstr); + std::shared_ptr manager = solver->getManager().getSharedPointer(); solver->push(); solver->add(checkConstr->toExpression(varNames, manager)); From 31f46830940fe4dd4f8a72042db4052eba19a54a Mon Sep 17 00:00:00 2001 From: Alexander Bork Date: Wed, 22 May 2019 11:10:38 +0200 Subject: [PATCH 07/47] Added activation for experimental DFT SMT analysis --- src/storm-dft-cli/storm-dft.cpp | 4 +++- src/storm-dft/api/storm-dft.cpp | 8 ++++++-- src/storm-dft/api/storm-dft.h | 2 +- src/storm-dft/modelchecker/dft/DFTASFChecker.cpp | 10 +++++++++- src/storm-dft/modelchecker/dft/DFTASFChecker.h | 8 ++++++++ 5 files changed, 27 insertions(+), 5 deletions(-) diff --git a/src/storm-dft-cli/storm-dft.cpp b/src/storm-dft-cli/storm-dft.cpp index 591f2ee2b..871502586 100644 --- a/src/storm-dft-cli/storm-dft.cpp +++ b/src/storm-dft-cli/storm-dft.cpp @@ -7,6 +7,7 @@ #include "storm-dft/settings/modules/FaultTreeSettings.h" #include #include "storm/settings/modules/GeneralSettings.h" +#include "storm/settings/modules/DebugSettings.h" #include "storm/settings/modules/IOSettings.h" #include "storm/settings/modules/ResourceSettings.h" #include "storm/utility/initialize.h" @@ -80,9 +81,10 @@ void processOptions() { #ifdef STORM_HAVE_Z3 if (faultTreeSettings.solveWithSMT()) { + auto const& debug = storm::settings::getModule(); // Solve with SMT STORM_LOG_DEBUG("Running DFT analysis with use of SMT"); - storm::api::analyzeDFTSMT(*dft, true); + storm::api::analyzeDFTSMT(*dft, true, debug.isTestSet()); return; } #endif diff --git a/src/storm-dft/api/storm-dft.cpp b/src/storm-dft/api/storm-dft.cpp index 16b6f4d00..ed20e5c69 100644 --- a/src/storm-dft/api/storm-dft.cpp +++ b/src/storm-dft/api/storm-dft.cpp @@ -44,10 +44,13 @@ namespace storm { template<> storm::api::SMTResult - analyzeDFTSMT(storm::storage::DFT const &dft, bool printOutput) { + analyzeDFTSMT(storm::storage::DFT const &dft, bool printOutput, bool experimentalMode) { uint64_t solverTimeout = 10; storm::modelchecker::DFTASFChecker smtChecker(dft); + if (experimentalMode) { + smtChecker.activateExperimentalMode(); + } smtChecker.toSolver(); storm::api::SMTResult results; @@ -77,7 +80,8 @@ namespace storm { template<> storm::api::SMTResult - analyzeDFTSMT(storm::storage::DFT const &dft, bool printOutput) { + analyzeDFTSMT(storm::storage::DFT const &dft, bool printOutput, + bool experimentalMode) { STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Analysis by SMT not supported for this data type."); } diff --git a/src/storm-dft/api/storm-dft.h b/src/storm-dft/api/storm-dft.h index 6ff06c9c9..fcaab98b1 100644 --- a/src/storm-dft/api/storm-dft.h +++ b/src/storm-dft/api/storm-dft.h @@ -107,7 +107,7 @@ namespace storm { */ template storm::api::SMTResult - analyzeDFTSMT(storm::storage::DFT const &dft, bool printOutput); + analyzeDFTSMT(storm::storage::DFT const &dft, bool printOutput, bool experimentalMode); /*! * Export DFT to JSON file. diff --git a/src/storm-dft/modelchecker/dft/DFTASFChecker.cpp b/src/storm-dft/modelchecker/dft/DFTASFChecker.cpp index a5633bb56..4ea6ba0aa 100644 --- a/src/storm-dft/modelchecker/dft/DFTASFChecker.cpp +++ b/src/storm-dft/modelchecker/dft/DFTASFChecker.cpp @@ -18,6 +18,11 @@ namespace storm { // Intentionally left empty. } + void DFTASFChecker::activateExperimentalMode() { + STORM_LOG_WARN("DFT-SMT-Checker now runs in experimental mode, no guarantee for correct results is given!"); + experimentalMode = true; + } + uint64_t DFTASFChecker::getClaimVariableIndex(uint64_t spare, uint64_t child) const { return claimVariables.at(SpareAndChildPair(spare, child)); } @@ -36,7 +41,9 @@ namespace storm { beVariables.push_back(varNames.size() - 1); break; case storm::storage::DFTElementType::BE_CONST: - STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Constant BEs are not supported in SMT translation."); + STORM_LOG_THROW(experimentalMode, storm::exceptions::NotSupportedException, + "Constant BEs are not supported in SMT translation."); + STORM_LOG_WARN("Constant BEs are only experimentally supported"); break; case storm::storage::DFTElementType::SPARE: { @@ -612,6 +619,7 @@ namespace storm { storm::solver::SmtSolver::CheckResult DFTASFChecker::checkDependencyConflict(uint64_t dep1Index, uint64_t dep2Index, uint64_t timeout) { + //TODO make constraints easier? std::vector> andConstr; std::vector> orConstr; STORM_LOG_DEBUG( diff --git a/src/storm-dft/modelchecker/dft/DFTASFChecker.h b/src/storm-dft/modelchecker/dft/DFTASFChecker.h index 75eef8c64..db3a04aa6 100644 --- a/src/storm-dft/modelchecker/dft/DFTASFChecker.h +++ b/src/storm-dft/modelchecker/dft/DFTASFChecker.h @@ -44,6 +44,13 @@ namespace storm { using ValueType = double; public: DFTASFChecker(storm::storage::DFT const&); + + /** + * Activates the experimental support for constant BEs and possibly other not thoroughly tested features + * + */ + void activateExperimentalMode(); + /** * Generate general variables and constraints for the DFT and store them in the corresponding maps and vectors * @@ -264,6 +271,7 @@ namespace storm { std::unordered_map markovianVariables; std::vector tmpTimePointVariables; uint64_t notFailed; + bool experimentalMode = false; }; } } From ca4dceaae14d580514de9cbb860d9aff99072190 Mon Sep 17 00:00:00 2001 From: Alexander Bork Date: Wed, 22 May 2019 14:26:51 +0200 Subject: [PATCH 08/47] Added experimental support for constant BEs --- src/storm-dft-cli/storm-dft.cpp | 6 +- src/storm-dft/api/storm-dft.cpp | 8 ++- src/storm-dft/api/storm-dft.h | 2 +- .../modelchecker/dft/DFTASFChecker.cpp | 70 ++++++++++++++++++- .../modelchecker/dft/SmtConstraint.cpp | 25 +++++++ 5 files changed, 102 insertions(+), 9 deletions(-) diff --git a/src/storm-dft-cli/storm-dft.cpp b/src/storm-dft-cli/storm-dft.cpp index 871502586..38a905999 100644 --- a/src/storm-dft-cli/storm-dft.cpp +++ b/src/storm-dft-cli/storm-dft.cpp @@ -27,6 +27,7 @@ void processOptions() { storm::settings::modules::FaultTreeSettings const& faultTreeSettings = storm::settings::getModule(); storm::settings::modules::IOSettings const& ioSettings = storm::settings::getModule(); storm::settings::modules::DftGspnSettings const& dftGspnSettings = storm::settings::getModule(); + auto const &debug = storm::settings::getModule(); if (!dftIOSettings.isDftFileSet() && !dftIOSettings.isDftJsonFileSet()) { @@ -53,8 +54,8 @@ void processOptions() { } if (dftIOSettings.isExportToSmt()) { - // Export to json - storm::api::exportDFTToSMT(*dft, dftIOSettings.getExportSmtFilename()); + // Export to smtlib2 + storm::api::exportDFTToSMT(*dft, dftIOSettings.getExportSmtFilename(), debug.isTestSet()); return; } @@ -81,7 +82,6 @@ void processOptions() { #ifdef STORM_HAVE_Z3 if (faultTreeSettings.solveWithSMT()) { - auto const& debug = storm::settings::getModule(); // Solve with SMT STORM_LOG_DEBUG("Running DFT analysis with use of SMT"); storm::api::analyzeDFTSMT(*dft, true, debug.isTestSet()); diff --git a/src/storm-dft/api/storm-dft.cpp b/src/storm-dft/api/storm-dft.cpp index ed20e5c69..2cb36853f 100644 --- a/src/storm-dft/api/storm-dft.cpp +++ b/src/storm-dft/api/storm-dft.cpp @@ -31,14 +31,18 @@ namespace storm { } template<> - void exportDFTToSMT(storm::storage::DFT const& dft, std::string const& file) { + void exportDFTToSMT(storm::storage::DFT const &dft, std::string const &file, bool experimentalMode) { storm::modelchecker::DFTASFChecker asfChecker(dft); + if (experimentalMode) { + asfChecker.activateExperimentalMode(); + } asfChecker.convert(); asfChecker.toFile(file); } template<> - void exportDFTToSMT(storm::storage::DFT const& dft, std::string const& file) { + void exportDFTToSMT(storm::storage::DFT const &dft, std::string const &file, + bool experimentalMode) { STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Export to SMT does not support this data type."); } diff --git a/src/storm-dft/api/storm-dft.h b/src/storm-dft/api/storm-dft.h index fcaab98b1..2af6a43e1 100644 --- a/src/storm-dft/api/storm-dft.h +++ b/src/storm-dft/api/storm-dft.h @@ -134,7 +134,7 @@ namespace storm { * @param file File. */ template - void exportDFTToSMT(storm::storage::DFT const& dft, std::string const& file); + void exportDFTToSMT(storm::storage::DFT const &dft, std::string const &file, bool experimentalMode); /*! * Transform DFT to GSPN. diff --git a/src/storm-dft/modelchecker/dft/DFTASFChecker.cpp b/src/storm-dft/modelchecker/dft/DFTASFChecker.cpp index 4ea6ba0aa..f6c955d56 100644 --- a/src/storm-dft/modelchecker/dft/DFTASFChecker.cpp +++ b/src/storm-dft/modelchecker/dft/DFTASFChecker.cpp @@ -29,6 +29,8 @@ namespace storm { void DFTASFChecker::convert() { std::vector beVariables; + std::vector failedBeVariables; + std::vector failsafeBeVariables; notFailed = dft.nrBasicElements() + 1; // Value indicating the element is not failed // Initialize variables @@ -40,11 +42,19 @@ namespace storm { case storm::storage::DFTElementType::BE_EXP: beVariables.push_back(varNames.size() - 1); break; - case storm::storage::DFTElementType::BE_CONST: + case storm::storage::DFTElementType::BE_CONST: { STORM_LOG_THROW(experimentalMode, storm::exceptions::NotSupportedException, "Constant BEs are not supported in SMT translation."); STORM_LOG_WARN("Constant BEs are only experimentally supported"); + // Constant BEs are initially either failed or failsafe, treat them differently + auto be = std::static_pointer_cast const>(element); + if (be->failed()) { + failedBeVariables.push_back(varNames.size() - 1); + } else { + failsafeBeVariables.push_back(varNames.size() - 1); + } break; + } case storm::storage::DFTElementType::SPARE: { auto spare = std::static_pointer_cast const>(element); @@ -73,13 +83,29 @@ namespace storm { // Generate constraints - // All BEs have to fail (first part of constraint 12) + // All exponential BEs have to fail (first part of constraint 12) for (auto const &beV : beVariables) { constraints.push_back(std::make_shared(beV, 1, dft.nrBasicElements())); } + // Constantly failsafe BEs may also be fail-safe + for (auto const &beV : failsafeBeVariables) { + constraints.push_back(std::make_shared(beV, 1, notFailed)); + } + + // Constantly failed BEs fail before other types + for (auto const &beV : failedBeVariables) { + constraints.push_back(std::make_shared(beV, 1, failedBeVariables.size())); + } + + std::vector allBeVariables; + allBeVariables.insert(std::end(allBeVariables), std::begin(beVariables), std::end(beVariables)); + allBeVariables.insert(std::end(allBeVariables), std::begin(failedBeVariables), std::end(failedBeVariables)); + allBeVariables.insert(std::end(allBeVariables), std::begin(failsafeBeVariables), + std::end(failsafeBeVariables)); + // No two BEs fail at the same time (second part of constraint 12) - constraints.push_back(std::make_shared(beVariables)); + constraints.push_back(std::make_shared(allBeVariables)); constraints.back()->setDescription("No two BEs fail at the same time"); // Initialize claim variables in [1, |BE|+1] @@ -142,6 +168,44 @@ namespace storm { // Handle dependencies addMarkovianConstraints(); + + // Failsafe BEs may only fail in non-Markovian states (i.e. if they were triggered) + std::vector> failsafeNotIConstr; + for (uint64_t i = 0; i < dft.nrBasicElements(); ++i) { + failsafeNotIConstr.clear(); + for (auto const &beV : failsafeBeVariables) { + failsafeNotIConstr.push_back(std::make_shared(beV, i)); + } + constraints.push_back( + std::make_shared(std::make_shared(markovianVariables.at(i), true), + std::make_shared(failsafeNotIConstr))); + if (i == 0) { + constraints.back()->setDescription("Failsafe BEs fail only if they are triggered"); + } + } + + + // A failsafe BE only stays failsafe if no trigger has been triggered + std::vector> triggerConstraints; + for (size_t i = 0; i < dft.nrElements(); ++i) { + std::shared_ptr const> element = dft.getElement(i); + if (element->type() == storm::storage::DFTElementType::BE_CONST) { + auto be = std::static_pointer_cast const>(element); + triggerConstraints.clear(); + for (auto const &dependency : be->ingoingDependencies()) { + triggerConstraints.push_back(std::make_shared( + timePointVariables.at(dependency->triggerEvent()->id()), notFailed)); + } + if (!triggerConstraints.empty()) { + constraints.push_back(std::make_shared( + std::make_shared(timePointVariables.at(be->id()), notFailed), + std::make_shared(triggerConstraints))); + constraints.back()->setDescription( + "Failsafe BE " + be->name() + " stays failsafe if no trigger fails"); + } + } + } + } // Constraint Generator Functions diff --git a/src/storm-dft/modelchecker/dft/SmtConstraint.cpp b/src/storm-dft/modelchecker/dft/SmtConstraint.cpp index ef16d580a..b2b367893 100644 --- a/src/storm-dft/modelchecker/dft/SmtConstraint.cpp +++ b/src/storm-dft/modelchecker/dft/SmtConstraint.cpp @@ -344,6 +344,31 @@ namespace storm { uint64_t value; }; + class IsNotConstantValue : public SmtConstraint { + public: + IsNotConstantValue(uint64_t varIndex, uint64_t val) : varIndex(varIndex), value(val) { + } + + virtual ~IsNotConstantValue() { + } + + std::string toSmtlib2(std::vector const &varNames) const override { + std::stringstream sstr; + assert(varIndex < varNames.size()); + sstr << "(distinct " << varNames.at(varIndex) << " " << value << ")"; + return sstr.str(); + } + + storm::expressions::Expression toExpression(std::vector const &varNames, + std::shared_ptr manager) const override { + return manager->getVariableExpression(varNames.at(varIndex)) != manager->integer(value); + } + + private: + uint64_t varIndex; + uint64_t value; + }; + class IsLessConstant : public SmtConstraint { public: From b3cf06d6dd6764de2f7d1b1286fe53ad27fc502c Mon Sep 17 00:00:00 2001 From: Alexander Bork Date: Wed, 22 May 2019 17:09:18 +0200 Subject: [PATCH 09/47] Check in SMT checker that only one BE is constantly failed --- .../modelchecker/dft/DFTASFChecker.cpp | 33 ++++++++++++++----- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/src/storm-dft/modelchecker/dft/DFTASFChecker.cpp b/src/storm-dft/modelchecker/dft/DFTASFChecker.cpp index f6c955d56..88a383970 100644 --- a/src/storm-dft/modelchecker/dft/DFTASFChecker.cpp +++ b/src/storm-dft/modelchecker/dft/DFTASFChecker.cpp @@ -29,8 +29,9 @@ namespace storm { void DFTASFChecker::convert() { std::vector beVariables; - std::vector failedBeVariables; + uint64_t failedBeVariables; std::vector failsafeBeVariables; + bool failedBeIsSet = false; notFailed = dft.nrBasicElements() + 1; // Value indicating the element is not failed // Initialize variables @@ -49,7 +50,10 @@ namespace storm { // Constant BEs are initially either failed or failsafe, treat them differently auto be = std::static_pointer_cast const>(element); if (be->failed()) { - failedBeVariables.push_back(varNames.size() - 1); + STORM_LOG_THROW(!failedBeIsSet, storm::exceptions::NotSupportedException, + "DFTs containing more than one constantly failed BE are not supported"); + failedBeVariables = varNames.size() - 1; + failedBeIsSet = true; } else { failsafeBeVariables.push_back(varNames.size() - 1); } @@ -94,13 +98,13 @@ namespace storm { } // Constantly failed BEs fail before other types - for (auto const &beV : failedBeVariables) { - constraints.push_back(std::make_shared(beV, 1, failedBeVariables.size())); + if (failedBeIsSet) { + constraints.push_back(std::make_shared(failedBeVariables, 0)); } + std::vector allBeVariables; allBeVariables.insert(std::end(allBeVariables), std::begin(beVariables), std::end(beVariables)); - allBeVariables.insert(std::end(allBeVariables), std::begin(failedBeVariables), std::end(failedBeVariables)); allBeVariables.insert(std::end(allBeVariables), std::begin(failsafeBeVariables), std::end(failsafeBeVariables)); @@ -676,6 +680,8 @@ namespace storm { std::shared_ptr manager = solver->getManager().getSharedPointer(); solver->add(countConstr->toExpression(varNames, manager)); solver->add(timepointConstr->toExpression(varNames, manager)); + STORM_PRINT(countConstr->toSmtlib2(varNames) << std::endl); + STORM_PRINT(timepointConstr->toSmtlib2(varNames) << std::endl); storm::solver::SmtSolver::CheckResult res = solver->check(); solver->pop(); return res; @@ -683,7 +689,6 @@ namespace storm { storm::solver::SmtSolver::CheckResult DFTASFChecker::checkDependencyConflict(uint64_t dep1Index, uint64_t dep2Index, uint64_t timeout) { - //TODO make constraints easier? std::vector> andConstr; std::vector> orConstr; STORM_LOG_DEBUG( @@ -731,6 +736,8 @@ namespace storm { uint64_t DFTASFChecker::correctLowerBound(uint64_t bound, uint_fast64_t timeout) { STORM_LOG_ASSERT(solver, "SMT Solver was not initialized, call toSolver() before checking queries"); + if (experimentalMode) + STORM_LOG_WARN("DFT-SMT-Checker now runs in experimental mode, bound correction is prone to errors!"); STORM_LOG_DEBUG("Lower bound correction - try to correct bound " << std::to_string(bound)); uint64_t boundCandidate = bound; uint64_t nrDepEvents = 0; @@ -747,7 +754,7 @@ namespace storm { } } // Only need to check as long as bound candidate + nr of non-Markovians to check is smaller than number of dependent events - while (nrNonMarkovian <= nrDepEvents && boundCandidate > 0) { + while (nrNonMarkovian <= nrDepEvents && boundCandidate >= 0) { STORM_LOG_TRACE( "Lower bound correction - check possible bound " << std::to_string(boundCandidate) << " with " << std::to_string(nrNonMarkovian) @@ -761,6 +768,11 @@ namespace storm { /* If SAT, there is a sequence where only boundCandidate-many BEs fail directly and rest is nonMarkovian. * Bound candidate is vaild, therefore check the next one */ STORM_LOG_TRACE("Lower bound correction - SAT"); + // Prevent integer underflow + if (boundCandidate == 0) { + STORM_LOG_DEBUG("Lower bound correction - corrected bound to 0"); + return 0; + } --boundCandidate; break; case storm::solver::SmtSolver::CheckResult::Unknown: @@ -781,9 +793,13 @@ namespace storm { uint64_t DFTASFChecker::correctUpperBound(uint64_t bound, uint_fast64_t timeout) { STORM_LOG_ASSERT(solver, "SMT Solver was not initialized, call toSolver() before checking queries"); + if (experimentalMode) + STORM_LOG_WARN("DFT-SMT-Checker now runs in experimental mode, bound correction is prone to errors!"); + STORM_LOG_DEBUG("Upper bound correction - try to correct bound " << std::to_string(bound)); - while (bound > 1) { + while (bound > 0) { + STORM_LOG_TRACE("Upper bound correction - check possible bound " << std::to_string(bound)); setSolverTimeout(timeout * 1000); storm::solver::SmtSolver::CheckResult tmp_res = checkFailsAtTimepointWithOnlyMarkovianState(bound); @@ -796,6 +812,7 @@ namespace storm { STORM_LOG_DEBUG("Upper bound correction - Solver returned 'Unknown', corrected to "); return bound; default: + STORM_LOG_TRACE("Upper bound correction - UNSAT"); --bound; break; From f258afa8a2cf034c3a3d5e718addb76622bfe172 Mon Sep 17 00:00:00 2001 From: Alexander Bork Date: Wed, 22 May 2019 18:29:41 +0200 Subject: [PATCH 10/47] Added basis for DFT transformator --- .../transformations/DftTransformator.cpp | 34 +++++++++++++++++++ .../transformations/DftTransformator.h | 30 ++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 src/storm-dft/transformations/DftTransformator.cpp create mode 100644 src/storm-dft/transformations/DftTransformator.h diff --git a/src/storm-dft/transformations/DftTransformator.cpp b/src/storm-dft/transformations/DftTransformator.cpp new file mode 100644 index 000000000..cb89c336a --- /dev/null +++ b/src/storm-dft/transformations/DftTransformator.cpp @@ -0,0 +1,34 @@ +#include "DftTransformator.h" + +namespace storm { + namespace transformations { + namespace dft { + template + DftTransformator::DftTransformator(storm::storage::DFT const &dft) : mDft(dft) {} + + template + storm::storage::DFT DftTransformator::transformUniqueFailedBe() { + // For now, this only creates an empty DFT + storm::builder::DFTBuilder builder; + + for (size_t i = 0; i < mDft.nrElements(); ++i) { + std::shared_ptr const> element = mDft.getElement(i); + //TODO SWITCH OVER ELEMENTS + } + //builder.setTopLevel(mDft.getTopLevelGate()->name()); + return builder.build(); + } + + // Explicitly instantiate the class. + template + class DftTransformator; + +#ifdef STORM_HAVE_CARL + + template + class DftTransformator; + +#endif + } + } +} diff --git a/src/storm-dft/transformations/DftTransformator.h b/src/storm-dft/transformations/DftTransformator.h new file mode 100644 index 000000000..59872ec51 --- /dev/null +++ b/src/storm-dft/transformations/DftTransformator.h @@ -0,0 +1,30 @@ +#include "storm-dft/storage/dft/DFT.h" +#include "storm-dft/builder/DFTBuilder.h" +#include "storm/utility/macros.h" + +namespace storm { + namespace transformations { + namespace dft { + + /*! + * Transformator for DFT -> DFT. + */ + template + class DftTransformator { + + public: + /*! + * Constructor. + * + * @param dft DFT + */ + DftTransformator(storm::storage::DFT const &dft); + + storm::storage::DFT transformUniqueFailedBe(); + + private: + storm::storage::DFT const &mDft; + }; + } + } +} From 69987cc76cce9419531687ef3c9626abc1b6e99b Mon Sep 17 00:00:00 2001 From: Alexander Bork Date: Fri, 24 May 2019 13:37:46 +0200 Subject: [PATCH 11/47] Copying of original DFT and changing all constant BEs to be failsafe --- .../transformations/DftTransformator.cpp | 94 ++++++++++++++++++- .../transformations/DftTransformator.h | 3 + 2 files changed, 93 insertions(+), 4 deletions(-) diff --git a/src/storm-dft/transformations/DftTransformator.cpp b/src/storm-dft/transformations/DftTransformator.cpp index cb89c336a..8b000ac4d 100644 --- a/src/storm-dft/transformations/DftTransformator.cpp +++ b/src/storm-dft/transformations/DftTransformator.cpp @@ -1,4 +1,5 @@ #include "DftTransformator.h" +#include "storm/exceptions/NotImplementedException.h" namespace storm { namespace transformations { @@ -8,17 +9,102 @@ namespace storm { template storm::storage::DFT DftTransformator::transformUniqueFailedBe() { - // For now, this only creates an empty DFT - storm::builder::DFTBuilder builder; + storm::builder::DFTBuilder builder = storm::builder::DFTBuilder(true, false); + // NOTE: if probabilities for constant BEs are introduced, change this to vector of tuples (name, prob) + std::vector failedBEs; for (size_t i = 0; i < mDft.nrElements(); ++i) { std::shared_ptr const> element = mDft.getElement(i); - //TODO SWITCH OVER ELEMENTS + STORM_LOG_DEBUG("Transform " + element->name()); + switch (element->type()) { + case storm::storage::DFTElementType::BE_EXP: { + auto be_exp = std::static_pointer_cast const>( + element); + builder.addBasicElementExponential(be_exp->name(), be_exp->activeFailureRate(), + be_exp->dormancyFactor()); + break; + } + case storm::storage::DFTElementType::BE_CONST: { + auto be_const = std::static_pointer_cast const>( + element); + if (be_const->canFail()) { + failedBEs.push_back(be_const->name()); + } + // All original constant BEs are set to failsafe, failed BEs are later triggered by a new element + builder.addBasicElementConst(be_const->name(), false); + break; + } + case storm::storage::DFTElementType::AND: + builder.addAndElement(element->name(), getChildrenVector(element)); + break; + case storm::storage::DFTElementType::OR: + builder.addOrElement(element->name(), getChildrenVector(element)); + break; + case storm::storage::DFTElementType::VOT: { + auto vot = std::static_pointer_cast const>(element); + builder.addVotElement(vot->name(), vot->threshold(), getChildrenVector(vot)); + break; + } + case storm::storage::DFTElementType::PAND: { + auto pand = std::static_pointer_cast const>(element); + builder.addPandElement(pand->name(), getChildrenVector(pand), pand->isInclusive()); + break; + } + case storm::storage::DFTElementType::POR: { + auto por = std::static_pointer_cast const>(element); + builder.addPandElement(por->name(), getChildrenVector(por), por->isInclusive()); + break; + } + case storm::storage::DFTElementType::SPARE: + builder.addSpareElement(element->name(), getChildrenVector(element)); + break; + case storm::storage::DFTElementType::PDEP: { + auto dep = std::static_pointer_cast const>( + element); + builder.addDepElement(dep->name(), getChildrenVector(element), dep->probability()); + break; + } + case storm::storage::DFTElementType::SEQ: + builder.addSequenceEnforcer(element->name(), getChildrenVector(element)); + break; + default: + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, + "DFT type '" << element->type() << "' not known."); + break; + } + } - //builder.setTopLevel(mDft.getTopLevelGate()->name()); + // At this point the DFT is an exact copy of the original, except for all constant failure probabilities being 0 + + builder.setTopLevel(mDft.getTopLevelGate()->name()); + + STORM_LOG_DEBUG("Transformation complete!"); return builder.build(); } + template + std::vector DftTransformator::getChildrenVector( + std::shared_ptr const> element) { + STORM_LOG_DEBUG("Get children for " + element->name()); + std::vector res; + if (element->isDependency()) { + // Dependencies have to be handled separately + auto dependency = std::static_pointer_cast const>(element); + res.push_back(dependency->triggerEvent()->name()); + for (auto const &depEvent : dependency->dependentEvents()) { + res.push_back(depEvent->name()); + } + } else { + auto elementWithChildren = std::static_pointer_cast const>( + element); + for (auto const &child : elementWithChildren->children()) { + res.push_back(child->name()); + STORM_LOG_DEBUG("Got child " + child->name()); + } + } + return res; + } + // Explicitly instantiate the class. template class DftTransformator; diff --git a/src/storm-dft/transformations/DftTransformator.h b/src/storm-dft/transformations/DftTransformator.h index 59872ec51..39dfa6acf 100644 --- a/src/storm-dft/transformations/DftTransformator.h +++ b/src/storm-dft/transformations/DftTransformator.h @@ -23,6 +23,9 @@ namespace storm { storm::storage::DFT transformUniqueFailedBe(); private: + std::vector + getChildrenVector(std::shared_ptr const> element); + storm::storage::DFT const &mDft; }; } From 12c0a6d72c1a75e6ce18c13f9b8a09e8faf1e77f Mon Sep 17 00:00:00 2001 From: Alexander Bork Date: Fri, 24 May 2019 14:44:42 +0200 Subject: [PATCH 12/47] Added unique constant failure in transformation --- .../transformations/DftTransformator.cpp | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/storm-dft/transformations/DftTransformator.cpp b/src/storm-dft/transformations/DftTransformator.cpp index 8b000ac4d..d70c15ec0 100644 --- a/src/storm-dft/transformations/DftTransformator.cpp +++ b/src/storm-dft/transformations/DftTransformator.cpp @@ -15,9 +15,9 @@ namespace storm { for (size_t i = 0; i < mDft.nrElements(); ++i) { std::shared_ptr const> element = mDft.getElement(i); - STORM_LOG_DEBUG("Transform " + element->name()); switch (element->type()) { case storm::storage::DFTElementType::BE_EXP: { + STORM_LOG_DEBUG("Transform " + element->name() + " [BE (exp)]"); auto be_exp = std::static_pointer_cast const>( element); builder.addBasicElementExponential(be_exp->name(), be_exp->activeFailureRate(), @@ -28,43 +28,58 @@ namespace storm { auto be_const = std::static_pointer_cast const>( element); if (be_const->canFail()) { + STORM_LOG_DEBUG("Transform " + element->name() + " [BE (const failed)]"); failedBEs.push_back(be_const->name()); + } else { + STORM_LOG_DEBUG("Transform " + element->name() + " [BE (const failsafe)]"); } // All original constant BEs are set to failsafe, failed BEs are later triggered by a new element builder.addBasicElementConst(be_const->name(), false); break; } case storm::storage::DFTElementType::AND: + STORM_LOG_DEBUG("Transform " + element->name() + " [AND]"); builder.addAndElement(element->name(), getChildrenVector(element)); break; case storm::storage::DFTElementType::OR: + STORM_LOG_DEBUG("Transform " + element->name() + " [OR]"); builder.addOrElement(element->name(), getChildrenVector(element)); break; case storm::storage::DFTElementType::VOT: { + STORM_LOG_DEBUG("Transform " + element->name() + " [VOT]"); auto vot = std::static_pointer_cast const>(element); builder.addVotElement(vot->name(), vot->threshold(), getChildrenVector(vot)); break; } case storm::storage::DFTElementType::PAND: { + STORM_LOG_DEBUG("Transform " + element->name() + " [PAND]"); auto pand = std::static_pointer_cast const>(element); builder.addPandElement(pand->name(), getChildrenVector(pand), pand->isInclusive()); break; } case storm::storage::DFTElementType::POR: { + STORM_LOG_DEBUG("Transform " + element->name() + " [POR]"); auto por = std::static_pointer_cast const>(element); builder.addPandElement(por->name(), getChildrenVector(por), por->isInclusive()); break; } case storm::storage::DFTElementType::SPARE: + STORM_LOG_DEBUG("Transform " + element->name() + " [SPARE]"); builder.addSpareElement(element->name(), getChildrenVector(element)); break; case storm::storage::DFTElementType::PDEP: { auto dep = std::static_pointer_cast const>( element); + if (dep->isFDEP()) { + STORM_LOG_DEBUG("Transform " + element->name() + " [FDEP]"); + } else { + STORM_LOG_DEBUG("Transform " + element->name() + " [PDEP]"); + } builder.addDepElement(dep->name(), getChildrenVector(element), dep->probability()); break; } case storm::storage::DFTElementType::SEQ: + STORM_LOG_DEBUG("Transform " + element->name() + " [SEQ]"); builder.addSequenceEnforcer(element->name(), getChildrenVector(element)); break; default: @@ -75,6 +90,11 @@ namespace storm { } // At this point the DFT is an exact copy of the original, except for all constant failure probabilities being 0 + if (!failedBEs.empty()) { + builder.addBasicElementConst("Unique_Constant_Failure", true); + failedBEs.insert(std::begin(failedBEs), "Unique_Constant_Failure"); + builder.addDepElement("Failure_Trigger", failedBEs, storm::utility::one()); + } builder.setTopLevel(mDft.getTopLevelGate()->name()); @@ -85,7 +105,6 @@ namespace storm { template std::vector DftTransformator::getChildrenVector( std::shared_ptr const> element) { - STORM_LOG_DEBUG("Get children for " + element->name()); std::vector res; if (element->isDependency()) { // Dependencies have to be handled separately @@ -99,7 +118,6 @@ namespace storm { element); for (auto const &child : elementWithChildren->children()) { res.push_back(child->name()); - STORM_LOG_DEBUG("Got child " + child->name()); } } return res; From 74aa93d23d6b074b606ef2dd62fc970cf45f97f1 Mon Sep 17 00:00:00 2001 From: Alexander Bork Date: Fri, 24 May 2019 18:30:44 +0200 Subject: [PATCH 13/47] Moved elimination of non-binary dependencies from builder to the DFT transformator --- src/storm-dft-cli/storm-dft.cpp | 16 +- src/storm-dft/builder/DFTBuilder.cpp | 1 - src/storm-dft/builder/DFTBuilder.h | 47 ++---- src/storm-dft/parser/DFTGalileoParser.cpp | 5 +- src/storm-dft/parser/DFTGalileoParser.h | 3 +- .../transformations/DftTransformator.cpp | 147 +++++++++++++++++- .../transformations/DftTransformator.h | 10 +- .../storm-dft/api/DftModelCheckerTest.cpp | 9 +- 8 files changed, 176 insertions(+), 62 deletions(-) diff --git a/src/storm-dft-cli/storm-dft.cpp b/src/storm-dft-cli/storm-dft.cpp index 38a905999..aed36288c 100644 --- a/src/storm-dft-cli/storm-dft.cpp +++ b/src/storm-dft-cli/storm-dft.cpp @@ -13,6 +13,7 @@ #include "storm/utility/initialize.h" #include "storm-cli-utilities/cli.h" #include "storm-parsers/api/storm-parsers.h" +#include "storm-dft/transformations/DftTransformator.h" /*! @@ -29,6 +30,8 @@ void processOptions() { storm::settings::modules::DftGspnSettings const& dftGspnSettings = storm::settings::getModule(); auto const &debug = storm::settings::getModule(); + auto dftTransformator = storm::transformations::dft::DftTransformator(); + if (!dftIOSettings.isDftFileSet() && !dftIOSettings.isDftJsonFileSet()) { STORM_LOG_THROW(false, storm::exceptions::InvalidSettingsException, "No input model given."); @@ -53,11 +56,8 @@ void processOptions() { storm::api::exportDFTToJsonFile(*dft, dftIOSettings.getExportJsonFilename()); } - if (dftIOSettings.isExportToSmt()) { - // Export to smtlib2 - storm::api::exportDFTToSMT(*dft, dftIOSettings.getExportSmtFilename(), debug.isTestSet()); - return; - } + // Eliminate non-binary dependencies + dft = dftTransformator.transformBinaryFDEPs(*dft); // Check well-formedness of DFT std::stringstream stream; @@ -79,6 +79,12 @@ void processOptions() { return; } + // SMT + if (dftIOSettings.isExportToSmt()) { + // Export to smtlib2 + storm::api::exportDFTToSMT(*dft, dftIOSettings.getExportSmtFilename(), debug.isTestSet()); + return; + } #ifdef STORM_HAVE_Z3 if (faultTreeSettings.solveWithSMT()) { diff --git a/src/storm-dft/builder/DFTBuilder.cpp b/src/storm-dft/builder/DFTBuilder.cpp index 9bb40a8b6..c02fb3364 100644 --- a/src/storm-dft/builder/DFTBuilder.cpp +++ b/src/storm-dft/builder/DFTBuilder.cpp @@ -77,7 +77,6 @@ namespace storm { childElement->addOutgoingDependency(elem.first); } } - STORM_LOG_ASSERT(!binaryDependencies || dependencies.size() == 1, "Dependency '" << elem.first->name() << "' should only have one dependent element."); for (auto& be : dependencies) { elem.first->addDependentEvent(be); be->addIngoingDependency(elem.first); diff --git a/src/storm-dft/builder/DFTBuilder.h b/src/storm-dft/builder/DFTBuilder.h index 9ce85db0f..31764e2b8 100644 --- a/src/storm-dft/builder/DFTBuilder.h +++ b/src/storm-dft/builder/DFTBuilder.h @@ -43,7 +43,8 @@ namespace storm { std::unordered_map mLayoutInfo; public: - DFTBuilder(bool defaultInclusive = true, bool binaryDependencies = true) : pandDefaultInclusive(defaultInclusive), porDefaultInclusive(defaultInclusive), binaryDependencies(binaryDependencies) { + DFTBuilder(bool defaultInclusive = true) : pandDefaultInclusive(defaultInclusive), + porDefaultInclusive(defaultInclusive) { } @@ -107,41 +108,13 @@ namespace storm { std::string trigger = children[0]; //TODO: collect constraints for SMT solving - //0 <= probability <= 1 - if (binaryDependencies && !storm::utility::isOne(probability) && children.size() > 2) { - // Introduce additional element for first capturing the probabilistic dependency - std::string nameAdditional = name + "_additional"; - addBasicElementConst(nameAdditional, false); - // First consider probabilistic dependency - addDepElement(name + "_pdep", {children.front(), nameAdditional}, probability); - // Then consider dependencies to the children if probabilistic dependency failed - std::vector newChildren = children; - newChildren[0] = nameAdditional; - addDepElement(name, newChildren, storm::utility::one()); - return true; - } else { - // Add dependencies - if(binaryDependencies) { - for (size_t i = 1; i < children.size(); ++i) { - std::string nameDep = name + "_" + std::to_string(i); - if (nameInUse(nameDep)) { - STORM_LOG_ERROR("Element with name '" << name << "' already exists."); - return false; - } - STORM_LOG_ASSERT(storm::utility::isOne(probability) || children.size() == 2, "PDep with multiple children supported."); - DFTDependencyPointer element = std::make_shared>(mNextId++, nameDep, probability); - mElements[element->name()] = element; - mDependencyChildNames[element] = {trigger, children[i]}; - mDependencies.push_back(element); - } - } else { - DFTDependencyPointer element = std::make_shared>(mNextId++, name, probability); - mElements[element->name()] = element; - mDependencyChildNames[element] = children; - mDependencies.push_back(element); - } - return true; - } + DFTDependencyPointer element = std::make_shared>(mNextId++, + name, + probability); + mElements[element->name()] = element; + mDependencyChildNames[element] = children; + mDependencies.push_back(element); + return true; } bool addVotElement(std::string const& name, unsigned threshold, std::vector const& children) { @@ -268,8 +241,6 @@ namespace storm { bool pandDefaultInclusive; // If true, the standard gate adders make a pand inclusive, and exclusive otherwise. bool porDefaultInclusive; - - bool binaryDependencies; }; } diff --git a/src/storm-dft/parser/DFTGalileoParser.cpp b/src/storm-dft/parser/DFTGalileoParser.cpp index 8d44637f9..0062abce6 100644 --- a/src/storm-dft/parser/DFTGalileoParser.cpp +++ b/src/storm-dft/parser/DFTGalileoParser.cpp @@ -32,8 +32,9 @@ namespace storm { } template - storm::storage::DFT DFTGalileoParser::parseDFT(const std::string& filename, bool defaultInclusive, bool binaryDependencies) { - storm::builder::DFTBuilder builder(defaultInclusive, binaryDependencies); + storm::storage::DFT + DFTGalileoParser::parseDFT(const std::string &filename, bool defaultInclusive) { + storm::builder::DFTBuilder builder(defaultInclusive); ValueParser valueParser; // Regular expression to detect comments // taken from: https://stackoverflow.com/questions/9449887/removing-c-c-style-comments-using-boostregex diff --git a/src/storm-dft/parser/DFTGalileoParser.h b/src/storm-dft/parser/DFTGalileoParser.h index 0c280fd88..f5f259f5f 100644 --- a/src/storm-dft/parser/DFTGalileoParser.h +++ b/src/storm-dft/parser/DFTGalileoParser.h @@ -26,11 +26,10 @@ namespace storm { * * @param filename File. * @param defaultInclusive Flag indicating if priority gates are inclusive by default. - * @param binaryDependencies Flag indicating if dependencies should be converted to binary dependencies. * * @return DFT. */ - static storm::storage::DFT parseDFT(std::string const& filename, bool defaultInclusive = true, bool binaryDependencies = true); + static storm::storage::DFT parseDFT(std::string const &filename, bool defaultInclusive = true); private: /*! diff --git a/src/storm-dft/transformations/DftTransformator.cpp b/src/storm-dft/transformations/DftTransformator.cpp index d70c15ec0..a6ba46f17 100644 --- a/src/storm-dft/transformations/DftTransformator.cpp +++ b/src/storm-dft/transformations/DftTransformator.cpp @@ -5,16 +5,18 @@ namespace storm { namespace transformations { namespace dft { template - DftTransformator::DftTransformator(storm::storage::DFT const &dft) : mDft(dft) {} + DftTransformator::DftTransformator() { + } template - storm::storage::DFT DftTransformator::transformUniqueFailedBe() { - storm::builder::DFTBuilder builder = storm::builder::DFTBuilder(true, false); + std::shared_ptr> + DftTransformator::transformUniqueFailedBe(storm::storage::DFT const &dft) { + storm::builder::DFTBuilder builder = storm::builder::DFTBuilder(true); // NOTE: if probabilities for constant BEs are introduced, change this to vector of tuples (name, prob) std::vector failedBEs; - for (size_t i = 0; i < mDft.nrElements(); ++i) { - std::shared_ptr const> element = mDft.getElement(i); + for (size_t i = 0; i < dft.nrElements(); ++i) { + std::shared_ptr const> element = dft.getElement(i); switch (element->type()) { case storm::storage::DFTElementType::BE_EXP: { STORM_LOG_DEBUG("Transform " + element->name() + " [BE (exp)]"); @@ -75,13 +77,17 @@ namespace storm { } else { STORM_LOG_DEBUG("Transform " + element->name() + " [PDEP]"); } - builder.addDepElement(dep->name(), getChildrenVector(element), dep->probability()); + builder.addDepElement(dep->name(), getChildrenVector(dep), dep->probability()); break; } case storm::storage::DFTElementType::SEQ: STORM_LOG_DEBUG("Transform " + element->name() + " [SEQ]"); builder.addSequenceEnforcer(element->name(), getChildrenVector(element)); break; + case storm::storage::DFTElementType::MUTEX: + STORM_LOG_DEBUG("Transform " + element->name() + " [MUTEX]"); + builder.addMutex(element->name(), getChildrenVector(element)); + break; default: STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "DFT type '" << element->type() << "' not known."); @@ -96,10 +102,135 @@ namespace storm { builder.addDepElement("Failure_Trigger", failedBEs, storm::utility::one()); } - builder.setTopLevel(mDft.getTopLevelGate()->name()); + builder.setTopLevel(dft.getTopLevelGate()->name()); + + STORM_LOG_DEBUG("Transformation complete!"); + return std::make_shared>(builder.build()); + } + + template + std::shared_ptr> + DftTransformator::transformBinaryFDEPs(storm::storage::DFT const &dft) { + storm::builder::DFTBuilder builder = storm::builder::DFTBuilder(true); + + for (size_t i = 0; i < dft.nrElements(); ++i) { + std::shared_ptr const> element = dft.getElement(i); + switch (element->type()) { + case storm::storage::DFTElementType::BE_EXP: { + STORM_LOG_DEBUG("Transform " + element->name() + " [BE (exp)]"); + auto be_exp = std::static_pointer_cast const>( + element); + builder.addBasicElementExponential(be_exp->name(), be_exp->activeFailureRate(), + be_exp->dormancyFactor()); + break; + } + case storm::storage::DFTElementType::BE_CONST: { + auto be_const = std::static_pointer_cast const>( + element); + STORM_LOG_DEBUG("Transform " + element->name() + " [BE (const)]"); + // All original constant BEs are set to failsafe, failed BEs are later triggered by a new element + builder.addBasicElementConst(be_const->name(), be_const->canFail()); + break; + } + case storm::storage::DFTElementType::AND: + STORM_LOG_DEBUG("Transform " + element->name() + " [AND]"); + builder.addAndElement(element->name(), getChildrenVector(element)); + break; + case storm::storage::DFTElementType::OR: + STORM_LOG_DEBUG("Transform " + element->name() + " [OR]"); + builder.addOrElement(element->name(), getChildrenVector(element)); + break; + case storm::storage::DFTElementType::VOT: { + STORM_LOG_DEBUG("Transform " + element->name() + " [VOT]"); + auto vot = std::static_pointer_cast const>(element); + builder.addVotElement(vot->name(), vot->threshold(), getChildrenVector(vot)); + break; + } + case storm::storage::DFTElementType::PAND: { + STORM_LOG_DEBUG("Transform " + element->name() + " [PAND]"); + auto pand = std::static_pointer_cast const>(element); + builder.addPandElement(pand->name(), getChildrenVector(pand), pand->isInclusive()); + break; + } + case storm::storage::DFTElementType::POR: { + STORM_LOG_DEBUG("Transform " + element->name() + " [POR]"); + auto por = std::static_pointer_cast const>(element); + builder.addPandElement(por->name(), getChildrenVector(por), por->isInclusive()); + break; + } + case storm::storage::DFTElementType::SPARE: + STORM_LOG_DEBUG("Transform " + element->name() + " [SPARE]"); + builder.addSpareElement(element->name(), getChildrenVector(element)); + break; + case storm::storage::DFTElementType::PDEP: { + auto dep = std::static_pointer_cast const>( + element); + auto children = getChildrenVector(dep); + if (!storm::utility::isOne(dep->probability())) { + STORM_LOG_DEBUG("Transform " + element->name() + " [PDEP]"); + if (children.size() > 2) { + // Introduce additional element for first capturing the probabilistic dependency + std::string nameAdditional = dep->name() + "_additional"; + builder.addBasicElementConst(nameAdditional, false); + // First consider probabilistic dependency + builder.addDepElement(dep->name() + "_pdep", {children.front(), nameAdditional}, + dep->probability()); + // Then consider dependencies to the children if probabilistic dependency failed + children.erase(children.begin()); + size_t i = 1; + for (auto const &child : children) { + std::string nameDep = dep->name() + "_" + std::to_string(i); + if (builder.nameInUse(nameDep)) { + STORM_LOG_ERROR("Element with name '" << nameDep << "' already exists."); + } + builder.addDepElement(nameDep, {dep->name() + "_additional", child}, + storm::utility::one()); + ++i; + } + } else { + builder.addDepElement(dep->name(), children, dep->probability()); + } + } else { + STORM_LOG_DEBUG("Transform " + element->name() + " [FDEP]"); + // Add dependencies + for (size_t i = 1; i < children.size(); ++i) { + std::string nameDep; + if (children.size() == 2) { + nameDep = dep->name(); + } else { + nameDep = dep->name() + "_" + std::to_string(i); + } + if (builder.nameInUse(nameDep)) { + STORM_LOG_ERROR("Element with name '" << nameDep << "' already exists."); + } + STORM_LOG_ASSERT(storm::utility::isOne(dep->probability()) || children.size() == 2, + "PDEP with multiple children supported."); + builder.addDepElement(nameDep, {children[0], children[i]}, + storm::utility::one()); + } + } + break; + } + case storm::storage::DFTElementType::SEQ: + STORM_LOG_DEBUG("Transform " + element->name() + " [SEQ]"); + builder.addSequenceEnforcer(element->name(), getChildrenVector(element)); + break; + case storm::storage::DFTElementType::MUTEX: + STORM_LOG_DEBUG("Transform " + element->name() + " [MUTEX]"); + builder.addMutex(element->name(), getChildrenVector(element)); + break; + default: + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, + "DFT type '" << element->type() << "' not known."); + break; + } + + } + + builder.setTopLevel(dft.getTopLevelGate()->name()); STORM_LOG_DEBUG("Transformation complete!"); - return builder.build(); + return std::make_shared>(builder.build()); } template diff --git a/src/storm-dft/transformations/DftTransformator.h b/src/storm-dft/transformations/DftTransformator.h index 39dfa6acf..c8e832f1b 100644 --- a/src/storm-dft/transformations/DftTransformator.h +++ b/src/storm-dft/transformations/DftTransformator.h @@ -18,15 +18,17 @@ namespace storm { * * @param dft DFT */ - DftTransformator(storm::storage::DFT const &dft); + DftTransformator(); - storm::storage::DFT transformUniqueFailedBe(); + std::shared_ptr> + transformUniqueFailedBe(storm::storage::DFT const &dft); + + std::shared_ptr> + transformBinaryFDEPs(storm::storage::DFT const &dft); private: std::vector getChildrenVector(std::shared_ptr const> element); - - storm::storage::DFT const &mDft; }; } } diff --git a/src/test/storm-dft/api/DftModelCheckerTest.cpp b/src/test/storm-dft/api/DftModelCheckerTest.cpp index 96f9263c6..bb37c29c7 100644 --- a/src/test/storm-dft/api/DftModelCheckerTest.cpp +++ b/src/test/storm-dft/api/DftModelCheckerTest.cpp @@ -2,6 +2,7 @@ #include "storm-config.h" #include "storm-dft/api/storm-dft.h" +#include "storm-dft/transformations/DftTransformator.h" #include "storm-parsers/api/storm-parsers.h" namespace { @@ -73,7 +74,9 @@ namespace { } double analyzeMTTF(std::string const& file) { - std::shared_ptr> dft = storm::api::loadDFTGalileoFile(file); + storm::transformations::dft::DftTransformator dftTransformator = storm::transformations::dft::DftTransformator(); + std::shared_ptr> dft = dftTransformator.transformBinaryFDEPs( + *(storm::api::loadDFTGalileoFile(file))); EXPECT_TRUE(storm::api::isWellFormed(*dft)); std::string property = "Tmin=? [F \"failed\"]"; std::vector> properties = storm::api::extractFormulasFromProperties(storm::api::parseProperties(property)); @@ -87,7 +90,9 @@ namespace { } double analyzeReliability(std::string const &file, double bound) { - std::shared_ptr> dft = storm::api::loadDFTGalileoFile(file); + storm::transformations::dft::DftTransformator dftTransformator = storm::transformations::dft::DftTransformator(); + std::shared_ptr> dft = dftTransformator.transformBinaryFDEPs( + *(storm::api::loadDFTGalileoFile(file))); EXPECT_TRUE(storm::api::isWellFormed(*dft)); std::string property = "Pmin=? [F<=" + std::to_string(bound) + " \"failed\"]"; std::vector> properties = storm::api::extractFormulasFromProperties( From dde18d45eb4cc2fb7f8865c1d40fa12788f587d1 Mon Sep 17 00:00:00 2001 From: Alexander Bork Date: Wed, 29 May 2019 12:14:34 +0200 Subject: [PATCH 14/47] Added tests for DFT transformator --- .../examples/testfiles/dft/const_be_test.dft | 8 +++ .../storm-dft/api/DftTransformatorTest.cpp | 69 +++++++++++++++++++ 2 files changed, 77 insertions(+) create mode 100644 resources/examples/testfiles/dft/const_be_test.dft create mode 100644 src/test/storm-dft/api/DftTransformatorTest.cpp diff --git a/resources/examples/testfiles/dft/const_be_test.dft b/resources/examples/testfiles/dft/const_be_test.dft new file mode 100644 index 000000000..1f5721275 --- /dev/null +++ b/resources/examples/testfiles/dft/const_be_test.dft @@ -0,0 +1,8 @@ +toplevel "A"; +"A" and "I" "J" "K" "L"; +"DEP1" fdep "I" "J" "K"; + +"I" prob=1; +"J" prob=1; +"K" prob=1; +"L" lambda=1.0 dorm=0.5; diff --git a/src/test/storm-dft/api/DftTransformatorTest.cpp b/src/test/storm-dft/api/DftTransformatorTest.cpp new file mode 100644 index 000000000..97646fd83 --- /dev/null +++ b/src/test/storm-dft/api/DftTransformatorTest.cpp @@ -0,0 +1,69 @@ +#include "gtest/gtest.h" +#include "storm-config.h" + +#include "storm-dft/api/storm-dft.h" +#include "storm-dft/transformations/DftTransformator.h" + +namespace { + + TEST(DftTransformatorTest, UniqueConstantFailedTest) { + std::string file = STORM_TEST_RESOURCES_DIR "/dft/const_be_test.dft"; + std::shared_ptr> originalDft = storm::api::loadDFTGalileoFile(file); + auto dftTransformator = storm::transformations::dft::DftTransformator(); + std::shared_ptr> transformedDft = dftTransformator.transformUniqueFailedBe( + *originalDft); + + auto bes = transformedDft->getBasicElements(); + uint64_t constBeFailedCount = 0; + uint64_t constBeFailsafeCount = 0; + for (auto &be : bes) { + if (be->type() == storm::storage::DFTElementType::BE_CONST) { + if (be->canFail()) { + ++constBeFailedCount; + } else { + ++constBeFailsafeCount; + } + } + } + + EXPECT_EQ(1ul, constBeFailedCount); + EXPECT_EQ(3ul, constBeFailsafeCount); + } + + TEST(DftTransformatorTest, BinaryFDEPTest) { + std::string file = STORM_TEST_RESOURCES_DIR "/dft/fdep5.dft"; + std::shared_ptr> originalDft = storm::api::loadDFTGalileoFile(file); + auto dftTransformator = storm::transformations::dft::DftTransformator(); + std::shared_ptr> transformedDft = dftTransformator.transformBinaryFDEPs( + *originalDft); + + uint64_t dependencyCount = transformedDft->getDependencies().size(); + + EXPECT_EQ(2ul, dependencyCount); + } + + TEST(DftTransformatorTest, PDEPTransformTest) { + std::string file = STORM_TEST_RESOURCES_DIR "/dft/pdep4.dft"; + std::shared_ptr> originalDft = storm::api::loadDFTGalileoFile(file); + auto dftTransformator = storm::transformations::dft::DftTransformator(); + std::shared_ptr> transformedDft = dftTransformator.transformBinaryFDEPs( + *originalDft); + + uint64_t fdepCount = 0; + uint64_t pdepCount = 0; + + for (auto depIndex : transformedDft->getDependencies()) { + auto dep = transformedDft->getDependency(depIndex); + if (dep->probability() == 1) { + ++fdepCount; + } else { + ++pdepCount; + } + } + + EXPECT_EQ(1ul, pdepCount); + EXPECT_EQ(2ul, fdepCount); + EXPECT_EQ(4ul, transformedDft->nrBasicElements()); + } + +} \ No newline at end of file From 1d505d2ee009d58a3caea9cfbff93277bcbcbeb7 Mon Sep 17 00:00:00 2001 From: Alexander Bork Date: Wed, 29 May 2019 12:32:02 +0200 Subject: [PATCH 15/47] Added check if DFT transformation is needed --- src/storm-dft-cli/storm-dft.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/storm-dft-cli/storm-dft.cpp b/src/storm-dft-cli/storm-dft.cpp index aed36288c..1843a3f53 100644 --- a/src/storm-dft-cli/storm-dft.cpp +++ b/src/storm-dft-cli/storm-dft.cpp @@ -57,8 +57,9 @@ void processOptions() { } // Eliminate non-binary dependencies - dft = dftTransformator.transformBinaryFDEPs(*dft); - + if (dft->getDependencies().size() > 0) { + dft = dftTransformator.transformBinaryFDEPs(*dft); + } // Check well-formedness of DFT std::stringstream stream; if (!dft->checkWellFormedness(stream)) { @@ -88,6 +89,7 @@ void processOptions() { #ifdef STORM_HAVE_Z3 if (faultTreeSettings.solveWithSMT()) { + dft = dftTransformator.transformUniqueFailedBe(*dft); // Solve with SMT STORM_LOG_DEBUG("Running DFT analysis with use of SMT"); storm::api::analyzeDFTSMT(*dft, true, debug.isTestSet()); From a0c42fa6302fa978af50bd63a93a8f60a126b41a Mon Sep 17 00:00:00 2001 From: Alexander Bork Date: Wed, 5 Jun 2019 16:24:35 +0200 Subject: [PATCH 16/47] Added debugging messages for transformations --- src/storm-dft/transformations/DftTransformator.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/storm-dft/transformations/DftTransformator.cpp b/src/storm-dft/transformations/DftTransformator.cpp index a6ba46f17..97428b6b6 100644 --- a/src/storm-dft/transformations/DftTransformator.cpp +++ b/src/storm-dft/transformations/DftTransformator.cpp @@ -96,6 +96,8 @@ namespace storm { } // At this point the DFT is an exact copy of the original, except for all constant failure probabilities being 0 + + // Introduce new constantly failed BE and FDEPs to trigger all failures if (!failedBEs.empty()) { builder.addBasicElementConst("Unique_Constant_Failure", true); failedBEs.insert(std::begin(failedBEs), "Unique_Constant_Failure"); @@ -104,7 +106,7 @@ namespace storm { builder.setTopLevel(dft.getTopLevelGate()->name()); - STORM_LOG_DEBUG("Transformation complete!"); + STORM_LOG_DEBUG("Transformation UniqueFailedBe complete!"); return std::make_shared>(builder.build()); } @@ -229,7 +231,7 @@ namespace storm { builder.setTopLevel(dft.getTopLevelGate()->name()); - STORM_LOG_DEBUG("Transformation complete!"); + STORM_LOG_DEBUG("Transformation BinaryFDEPs complete!"); return std::make_shared>(builder.build()); } From 583a8806208a56830b0846f6a8736e2636e0d7e7 Mon Sep 17 00:00:00 2001 From: Alexander Bork Date: Wed, 5 Jun 2019 16:29:27 +0200 Subject: [PATCH 17/47] Adjusted DFT to SMT conversion to deal with constant failures --- .../modelchecker/dft/DFTASFChecker.cpp | 40 ++++++++++++++++--- .../modelchecker/dft/SmtConstraint.cpp | 23 +++++++++++ 2 files changed, 57 insertions(+), 6 deletions(-) diff --git a/src/storm-dft/modelchecker/dft/DFTASFChecker.cpp b/src/storm-dft/modelchecker/dft/DFTASFChecker.cpp index 88a383970..196793fee 100644 --- a/src/storm-dft/modelchecker/dft/DFTASFChecker.cpp +++ b/src/storm-dft/modelchecker/dft/DFTASFChecker.cpp @@ -52,6 +52,7 @@ namespace storm { if (be->failed()) { STORM_LOG_THROW(!failedBeIsSet, storm::exceptions::NotSupportedException, "DFTs containing more than one constantly failed BE are not supported"); + notFailed = dft.nrBasicElements(); failedBeVariables = varNames.size() - 1; failedBeIsSet = true; } else { @@ -89,7 +90,7 @@ namespace storm { // All exponential BEs have to fail (first part of constraint 12) for (auto const &beV : beVariables) { - constraints.push_back(std::make_shared(beV, 1, dft.nrBasicElements())); + constraints.push_back(std::make_shared(beV, 1, notFailed - 1)); } // Constantly failsafe BEs may also be fail-safe @@ -108,9 +109,29 @@ namespace storm { allBeVariables.insert(std::end(allBeVariables), std::begin(failsafeBeVariables), std::end(failsafeBeVariables)); - // No two BEs fail at the same time (second part of constraint 12) - constraints.push_back(std::make_shared(allBeVariables)); - constraints.back()->setDescription("No two BEs fail at the same time"); + // No two exponential BEs fail at the same time (second part of constraint 12) + if (beVariables.size() > 1) { + constraints.push_back(std::make_shared(beVariables)); + constraints.back()->setDescription("No two BEs fail at the same time"); + } + + bool descFlag = true; + for (auto const &failsafeBe : failsafeBeVariables) { + std::vector > unequalConstraints; + for (auto const &otherBe: allBeVariables) { + if (otherBe != failsafeBe) { + unequalConstraints.push_back(std::make_shared(failsafeBe, otherBe)); + } + } + constraints.push_back( + std::make_shared(std::make_shared(failsafeBe, notFailed), + std::make_shared(unequalConstraints))); + if (descFlag) { + constraints.back()->setDescription( + "Initially failsafe BEs don't fail at the same time as other BEs"); + descFlag = false; + } + } // Initialize claim variables in [1, |BE|+1] for (auto const &claimVariable : claimVariables) { @@ -178,8 +199,9 @@ namespace storm { for (uint64_t i = 0; i < dft.nrBasicElements(); ++i) { failsafeNotIConstr.clear(); for (auto const &beV : failsafeBeVariables) { - failsafeNotIConstr.push_back(std::make_shared(beV, i)); + failsafeNotIConstr.push_back(std::make_shared(beV, i + 1)); } + // If state i+1 is Markovian (i.e. m_i = true), all failsafeBEVariables are not equal to i+1 constraints.push_back( std::make_shared(std::make_shared(markovianVariables.at(i), true), std::make_shared(failsafeNotIConstr))); @@ -588,7 +610,6 @@ namespace storm { for (auto const &constraint : constraints) { solver->add(constraint->toExpression(varNames, manager)); } - } storm::solver::SmtSolver::CheckResult DFTASFChecker::checkTleFailsWithEq(uint64_t bound) { @@ -753,12 +774,18 @@ namespace storm { } } } + // Only need to check as long as bound candidate + nr of non-Markovians to check is smaller than number of dependent events while (nrNonMarkovian <= nrDepEvents && boundCandidate >= 0) { + if (nrNonMarkovian == 0 and boundCandidate == 0) { + nrNonMarkovian = 1; + } STORM_LOG_TRACE( "Lower bound correction - check possible bound " << std::to_string(boundCandidate) << " with " << std::to_string(nrNonMarkovian) << " non-Markovian states"); + // The uniqueness transformation for constantly failed BEs guarantees that a DFT never fails + // in step 0 without intermediate non-Markovians, thus forcibly set nrNonMarkovian setSolverTimeout(timeout * 1000); storm::solver::SmtSolver::CheckResult tmp_res = checkFailsLeqWithEqNonMarkovianState(boundCandidate + nrNonMarkovian, nrNonMarkovian); @@ -846,6 +873,7 @@ namespace storm { } } + return bound; } diff --git a/src/storm-dft/modelchecker/dft/SmtConstraint.cpp b/src/storm-dft/modelchecker/dft/SmtConstraint.cpp index b2b367893..9cc092718 100644 --- a/src/storm-dft/modelchecker/dft/SmtConstraint.cpp +++ b/src/storm-dft/modelchecker/dft/SmtConstraint.cpp @@ -494,6 +494,29 @@ namespace storm { uint64_t var2Index; }; + class IsUnequal : public SmtConstraint { + public: + IsUnequal(uint64_t varIndex1, uint64_t varIndex2) : var1Index(varIndex1), var2Index(varIndex2) { + } + + virtual ~IsUnequal() { + } + + std::string toSmtlib2(std::vector const &varNames) const override { + return "(distinct " + varNames.at(var1Index) + " " + varNames.at(var2Index) + ")"; + } + + storm::expressions::Expression toExpression(std::vector const &varNames, + std::shared_ptr manager) const override { + return manager->getVariableExpression(varNames.at(var1Index)) != + manager->getVariableExpression(varNames.at(var2Index)); + } + + private: + uint64_t var1Index; + uint64_t var2Index; + }; + class IsLess : public SmtConstraint { public: From 9bfc7858d0a3d75f2f3a00fa60ffb075a5868001 Mon Sep 17 00:00:00 2001 From: Alexander Bork Date: Wed, 5 Jun 2019 16:30:34 +0200 Subject: [PATCH 18/47] Added improved upper bound correction --- src/storm-dft-cli/storm-dft.cpp | 8 ++ .../modelchecker/dft/DFTASFChecker.cpp | 99 ++++++++++++------- .../modelchecker/dft/DFTASFChecker.h | 5 +- 3 files changed, 77 insertions(+), 35 deletions(-) diff --git a/src/storm-dft-cli/storm-dft.cpp b/src/storm-dft-cli/storm-dft.cpp index 1843a3f53..87c6aafa9 100644 --- a/src/storm-dft-cli/storm-dft.cpp +++ b/src/storm-dft-cli/storm-dft.cpp @@ -82,6 +82,10 @@ void processOptions() { // SMT if (dftIOSettings.isExportToSmt()) { + dft = dftTransformator.transformUniqueFailedBe(*dft); + if (dft->getDependencies().size() > 0) { + dft = dftTransformator.transformBinaryFDEPs(*dft); + } // Export to smtlib2 storm::api::exportDFTToSMT(*dft, dftIOSettings.getExportSmtFilename(), debug.isTestSet()); return; @@ -90,6 +94,10 @@ void processOptions() { #ifdef STORM_HAVE_Z3 if (faultTreeSettings.solveWithSMT()) { dft = dftTransformator.transformUniqueFailedBe(*dft); + if (dft->getDependencies().size() > 0) { + // Making the constantly failed BE unique may introduce non-binary FDEPs + dft = dftTransformator.transformBinaryFDEPs(*dft); + } // Solve with SMT STORM_LOG_DEBUG("Running DFT analysis with use of SMT"); storm::api::analyzeDFTSMT(*dft, true, debug.isTestSet()); diff --git a/src/storm-dft/modelchecker/dft/DFTASFChecker.cpp b/src/storm-dft/modelchecker/dft/DFTASFChecker.cpp index 196793fee..668c75757 100644 --- a/src/storm-dft/modelchecker/dft/DFTASFChecker.cpp +++ b/src/storm-dft/modelchecker/dft/DFTASFChecker.cpp @@ -683,26 +683,25 @@ namespace storm { } storm::solver::SmtSolver::CheckResult - DFTASFChecker::checkFailsAtTimepointWithOnlyMarkovianState(uint64_t timepoint) { + DFTASFChecker::checkFailsAtTimepointWithEqNonMarkovianState(uint64_t timepoint, uint64_t nrNonMarkovian) { STORM_LOG_ASSERT(solver, "SMT Solver was not initialized, call toSolver() before checking queries"); std::vector markovianIndices; - // Get Markovian variable indices + timepoint = std::min(timepoint, markovianVariables.size()); + // Get Markovian variable indices up until given timepoint for (uint64_t i = 0; i < timepoint; ++i) { markovianIndices.push_back(markovianVariables.at(i)); } // Set backtracking marker to check several properties without reconstructing DFT encoding solver->push(); - // Constraint that toplevel element can fail with less than 'checkNumber' Markovian states visited - std::shared_ptr countConstr = std::make_shared( - markovianIndices, timepoint); - // Constraint that TLE fails at timepoint - std::shared_ptr timepointConstr = std::make_shared( + // Constraint that TLE fails before or during given timepoint + std::shared_ptr tleFailedConstr = std::make_shared( timePointVariables.at(dft.getTopLevelIndex()), timepoint); std::shared_ptr manager = solver->getManager().getSharedPointer(); - solver->add(countConstr->toExpression(varNames, manager)); - solver->add(timepointConstr->toExpression(varNames, manager)); - STORM_PRINT(countConstr->toSmtlib2(varNames) << std::endl); - STORM_PRINT(timepointConstr->toSmtlib2(varNames) << std::endl); + solver->add(tleFailedConstr->toExpression(varNames, manager)); + // Constraint that a given number of non-Markovian states are visited + std::shared_ptr nonMarkovianConstr = std::make_shared( + markovianIndices, nrNonMarkovian); + solver->add(nonMarkovianConstr->toExpression(varNames, manager)); storm::solver::SmtSolver::CheckResult res = solver->check(); solver->pop(); return res; @@ -822,31 +821,65 @@ namespace storm { STORM_LOG_ASSERT(solver, "SMT Solver was not initialized, call toSolver() before checking queries"); if (experimentalMode) STORM_LOG_WARN("DFT-SMT-Checker now runs in experimental mode, bound correction is prone to errors!"); - STORM_LOG_DEBUG("Upper bound correction - try to correct bound " << std::to_string(bound)); - - while (bound > 0) { - STORM_LOG_TRACE("Upper bound correction - check possible bound " << std::to_string(bound)); - setSolverTimeout(timeout * 1000); - storm::solver::SmtSolver::CheckResult tmp_res = - checkFailsAtTimepointWithOnlyMarkovianState(bound); - unsetSolverTimeout(); - switch (tmp_res) { - case storm::solver::SmtSolver::CheckResult::Sat: - STORM_LOG_DEBUG("Upper bound correction - corrected bound to " << std::to_string(bound)); - return bound; - case storm::solver::SmtSolver::CheckResult::Unknown: - STORM_LOG_DEBUG("Upper bound correction - Solver returned 'Unknown', corrected to "); - return bound; - default: - STORM_LOG_TRACE("Upper bound correction - UNSAT"); - --bound; - break; - + uint64_t boundCandidate = bound; + uint64_t nrDepEvents = 0; + uint64_t nrNonMarkovian = 0; + uint64_t currentTimepoint = 0; + // Count dependent events + for (size_t i = 0; i < dft.nrElements(); ++i) { + std::shared_ptr const> element = dft.getElement(i); + if (element->type() == storm::storage::DFTElementType::BE_EXP || + element->type() == storm::storage::DFTElementType::BE_CONST) { + auto be = std::static_pointer_cast const>(element); + if (be->hasIngoingDependencies()) { + ++nrDepEvents; + } } } - STORM_LOG_DEBUG("Upper bound correction - corrected bound to " << std::to_string(bound)); - return bound; + while (boundCandidate >= 0) { + currentTimepoint = bound + 1; + while (currentTimepoint - boundCandidate > 0) { + --currentTimepoint; + nrNonMarkovian = currentTimepoint - boundCandidate; + STORM_LOG_TRACE( + "Upper bound correction - candidate " << std::to_string(boundCandidate) << " check split " + << + std::to_string(currentTimepoint) << "|" + << std::to_string(nrNonMarkovian)); + setSolverTimeout(timeout * 1000); + storm::solver::SmtSolver::CheckResult tmp_res = + checkFailsAtTimepointWithEqNonMarkovianState(currentTimepoint, nrNonMarkovian); + unsetSolverTimeout(); + switch (tmp_res) { + case storm::solver::SmtSolver::CheckResult::Sat: + STORM_LOG_TRACE("Upper bound correction - SAT"); + STORM_LOG_DEBUG("Upper bound correction - corrected to bound " << boundCandidate << + " (TLE can fail at sequence point " + << std::to_string( + currentTimepoint) + << " with " + << std::to_string( + nrNonMarkovian) + << " non-Markovian states)"); + return boundCandidate; + case storm::solver::SmtSolver::CheckResult::Unknown: + // If any query returns unknown, we cannot be sure about the bound and fall back to the naive one + STORM_LOG_DEBUG( + "Upper bound correction - Solver returned 'Unknown', corrected to bound " << bound); + return bound; + default: + // if query is UNSAT, increase number of non-Markovian states and try again + STORM_LOG_TRACE("Lower bound correction - UNSAT"); + break; + } + } + --boundCandidate; + } + + // if for one candidate all queries are UNSAT, it is not valid. Return last valid candidate + STORM_LOG_DEBUG("Upper bound correction - corrected bound to " << std::to_string(boundCandidate)); + return boundCandidate; } uint64_t DFTASFChecker::getLeastFailureBound(uint_fast64_t timeout) { diff --git a/src/storm-dft/modelchecker/dft/DFTASFChecker.h b/src/storm-dft/modelchecker/dft/DFTASFChecker.h index db3a04aa6..73f15a530 100644 --- a/src/storm-dft/modelchecker/dft/DFTASFChecker.h +++ b/src/storm-dft/modelchecker/dft/DFTASFChecker.h @@ -150,13 +150,14 @@ namespace storm { checkFailsLeqWithEqNonMarkovianState(uint64_t checkbound, uint64_t nrNonMarkovian); /** - * Helper function that checks if the DFT can fail at a timepoint while visiting less than a given number of Markovian states + * Helper function that checks if the DFT can fail at a timepoint while visiting a given number of Markovian states * * @param timepoint point in time to check * @return "Sat" if a sequence of BE failures exists such that less than checkNumber Markovian states are visited, * "Unsat" if it does not, otherwise "Unknown" */ - storm::solver::SmtSolver::CheckResult checkFailsAtTimepointWithOnlyMarkovianState(uint64_t timepoint); + storm::solver::SmtSolver::CheckResult + checkFailsAtTimepointWithEqNonMarkovianState(uint64_t timepoint, uint64_t nrNonMarkovian); /** * Helper function for correction of least failure bound when dependencies are present. From 39ec751f8def91744ee8f3c4f92efc0be689fdf0 Mon Sep 17 00:00:00 2001 From: Alexander Bork Date: Fri, 7 Jun 2019 15:28:59 +0200 Subject: [PATCH 19/47] Removed debugging output --- .../transformations/DftTransformator.cpp | 44 ++++++------------- 1 file changed, 13 insertions(+), 31 deletions(-) diff --git a/src/storm-dft/transformations/DftTransformator.cpp b/src/storm-dft/transformations/DftTransformator.cpp index 97428b6b6..473f4249b 100644 --- a/src/storm-dft/transformations/DftTransformator.cpp +++ b/src/storm-dft/transformations/DftTransformator.cpp @@ -11,6 +11,7 @@ namespace storm { template std::shared_ptr> DftTransformator::transformUniqueFailedBe(storm::storage::DFT const &dft) { + STORM_LOG_DEBUG("Start transformation UniqueFailedBe"); storm::builder::DFTBuilder builder = storm::builder::DFTBuilder(true); // NOTE: if probabilities for constant BEs are introduced, change this to vector of tuples (name, prob) std::vector failedBEs; @@ -19,7 +20,6 @@ namespace storm { std::shared_ptr const> element = dft.getElement(i); switch (element->type()) { case storm::storage::DFTElementType::BE_EXP: { - STORM_LOG_DEBUG("Transform " + element->name() + " [BE (exp)]"); auto be_exp = std::static_pointer_cast const>( element); builder.addBasicElementExponential(be_exp->name(), be_exp->activeFailureRate(), @@ -30,62 +30,47 @@ namespace storm { auto be_const = std::static_pointer_cast const>( element); if (be_const->canFail()) { - STORM_LOG_DEBUG("Transform " + element->name() + " [BE (const failed)]"); + STORM_LOG_TRACE("Transform " + element->name() + " [BE (const failed)]"); failedBEs.push_back(be_const->name()); - } else { - STORM_LOG_DEBUG("Transform " + element->name() + " [BE (const failsafe)]"); } // All original constant BEs are set to failsafe, failed BEs are later triggered by a new element builder.addBasicElementConst(be_const->name(), false); break; } case storm::storage::DFTElementType::AND: - STORM_LOG_DEBUG("Transform " + element->name() + " [AND]"); builder.addAndElement(element->name(), getChildrenVector(element)); break; case storm::storage::DFTElementType::OR: - STORM_LOG_DEBUG("Transform " + element->name() + " [OR]"); builder.addOrElement(element->name(), getChildrenVector(element)); break; case storm::storage::DFTElementType::VOT: { - STORM_LOG_DEBUG("Transform " + element->name() + " [VOT]"); auto vot = std::static_pointer_cast const>(element); builder.addVotElement(vot->name(), vot->threshold(), getChildrenVector(vot)); break; } case storm::storage::DFTElementType::PAND: { - STORM_LOG_DEBUG("Transform " + element->name() + " [PAND]"); auto pand = std::static_pointer_cast const>(element); builder.addPandElement(pand->name(), getChildrenVector(pand), pand->isInclusive()); break; } case storm::storage::DFTElementType::POR: { - STORM_LOG_DEBUG("Transform " + element->name() + " [POR]"); auto por = std::static_pointer_cast const>(element); builder.addPandElement(por->name(), getChildrenVector(por), por->isInclusive()); break; } case storm::storage::DFTElementType::SPARE: - STORM_LOG_DEBUG("Transform " + element->name() + " [SPARE]"); builder.addSpareElement(element->name(), getChildrenVector(element)); break; case storm::storage::DFTElementType::PDEP: { auto dep = std::static_pointer_cast const>( element); - if (dep->isFDEP()) { - STORM_LOG_DEBUG("Transform " + element->name() + " [FDEP]"); - } else { - STORM_LOG_DEBUG("Transform " + element->name() + " [PDEP]"); - } builder.addDepElement(dep->name(), getChildrenVector(dep), dep->probability()); break; } case storm::storage::DFTElementType::SEQ: - STORM_LOG_DEBUG("Transform " + element->name() + " [SEQ]"); builder.addSequenceEnforcer(element->name(), getChildrenVector(element)); break; case storm::storage::DFTElementType::MUTEX: - STORM_LOG_DEBUG("Transform " + element->name() + " [MUTEX]"); builder.addMutex(element->name(), getChildrenVector(element)); break; default: @@ -99,27 +84,29 @@ namespace storm { // Introduce new constantly failed BE and FDEPs to trigger all failures if (!failedBEs.empty()) { + STORM_LOG_TRACE("Add Unique_Constant_Failure [BE (const failed)]"); builder.addBasicElementConst("Unique_Constant_Failure", true); failedBEs.insert(std::begin(failedBEs), "Unique_Constant_Failure"); + STORM_LOG_TRACE("Add Failure_Trigger [FDEP]"); builder.addDepElement("Failure_Trigger", failedBEs, storm::utility::one()); } builder.setTopLevel(dft.getTopLevelGate()->name()); - STORM_LOG_DEBUG("Transformation UniqueFailedBe complete!"); + STORM_LOG_DEBUG("Transformation UniqueFailedBe complete"); return std::make_shared>(builder.build()); } template std::shared_ptr> DftTransformator::transformBinaryFDEPs(storm::storage::DFT const &dft) { + STORM_LOG_DEBUG("Start transformation BinaryFDEPs"); storm::builder::DFTBuilder builder = storm::builder::DFTBuilder(true); for (size_t i = 0; i < dft.nrElements(); ++i) { std::shared_ptr const> element = dft.getElement(i); switch (element->type()) { case storm::storage::DFTElementType::BE_EXP: { - STORM_LOG_DEBUG("Transform " + element->name() + " [BE (exp)]"); auto be_exp = std::static_pointer_cast const>( element); builder.addBasicElementExponential(be_exp->name(), be_exp->activeFailureRate(), @@ -129,39 +116,32 @@ namespace storm { case storm::storage::DFTElementType::BE_CONST: { auto be_const = std::static_pointer_cast const>( element); - STORM_LOG_DEBUG("Transform " + element->name() + " [BE (const)]"); // All original constant BEs are set to failsafe, failed BEs are later triggered by a new element builder.addBasicElementConst(be_const->name(), be_const->canFail()); break; } case storm::storage::DFTElementType::AND: - STORM_LOG_DEBUG("Transform " + element->name() + " [AND]"); builder.addAndElement(element->name(), getChildrenVector(element)); break; case storm::storage::DFTElementType::OR: - STORM_LOG_DEBUG("Transform " + element->name() + " [OR]"); builder.addOrElement(element->name(), getChildrenVector(element)); break; case storm::storage::DFTElementType::VOT: { - STORM_LOG_DEBUG("Transform " + element->name() + " [VOT]"); auto vot = std::static_pointer_cast const>(element); builder.addVotElement(vot->name(), vot->threshold(), getChildrenVector(vot)); break; } case storm::storage::DFTElementType::PAND: { - STORM_LOG_DEBUG("Transform " + element->name() + " [PAND]"); auto pand = std::static_pointer_cast const>(element); builder.addPandElement(pand->name(), getChildrenVector(pand), pand->isInclusive()); break; } case storm::storage::DFTElementType::POR: { - STORM_LOG_DEBUG("Transform " + element->name() + " [POR]"); auto por = std::static_pointer_cast const>(element); builder.addPandElement(por->name(), getChildrenVector(por), por->isInclusive()); break; } case storm::storage::DFTElementType::SPARE: - STORM_LOG_DEBUG("Transform " + element->name() + " [SPARE]"); builder.addSpareElement(element->name(), getChildrenVector(element)); break; case storm::storage::DFTElementType::PDEP: { @@ -169,11 +149,13 @@ namespace storm { element); auto children = getChildrenVector(dep); if (!storm::utility::isOne(dep->probability())) { - STORM_LOG_DEBUG("Transform " + element->name() + " [PDEP]"); if (children.size() > 2) { + STORM_LOG_TRACE("Transform " + element->name() + " [PDEP]"); // Introduce additional element for first capturing the probabilistic dependency std::string nameAdditional = dep->name() + "_additional"; + STORM_LOG_TRACE("Add auxilliary BE " << nameAdditional); builder.addBasicElementConst(nameAdditional, false); + STORM_LOG_TRACE("Add " << dep->name() << "_pdep [PDEP]"); // First consider probabilistic dependency builder.addDepElement(dep->name() + "_pdep", {children.front(), nameAdditional}, dep->probability()); @@ -185,6 +167,7 @@ namespace storm { if (builder.nameInUse(nameDep)) { STORM_LOG_ERROR("Element with name '" << nameDep << "' already exists."); } + STORM_LOG_TRACE("Add " << nameDep << " [FDEP]"); builder.addDepElement(nameDep, {dep->name() + "_additional", child}, storm::utility::one()); ++i; @@ -193,7 +176,6 @@ namespace storm { builder.addDepElement(dep->name(), children, dep->probability()); } } else { - STORM_LOG_DEBUG("Transform " + element->name() + " [FDEP]"); // Add dependencies for (size_t i = 1; i < children.size(); ++i) { std::string nameDep; @@ -201,6 +183,8 @@ namespace storm { nameDep = dep->name(); } else { nameDep = dep->name() + "_" + std::to_string(i); + STORM_LOG_TRACE("Transform " + element->name() + " [FDEP]"); + STORM_LOG_TRACE("Add " + nameDep + " [FDEP]"); } if (builder.nameInUse(nameDep)) { STORM_LOG_ERROR("Element with name '" << nameDep << "' already exists."); @@ -214,11 +198,9 @@ namespace storm { break; } case storm::storage::DFTElementType::SEQ: - STORM_LOG_DEBUG("Transform " + element->name() + " [SEQ]"); builder.addSequenceEnforcer(element->name(), getChildrenVector(element)); break; case storm::storage::DFTElementType::MUTEX: - STORM_LOG_DEBUG("Transform " + element->name() + " [MUTEX]"); builder.addMutex(element->name(), getChildrenVector(element)); break; default: @@ -231,7 +213,7 @@ namespace storm { builder.setTopLevel(dft.getTopLevelGate()->name()); - STORM_LOG_DEBUG("Transformation BinaryFDEPs complete!"); + STORM_LOG_DEBUG("Transformation BinaryFDEPs complete"); return std::make_shared>(builder.build()); } From bec75813b1f9b7ecc2f3f75c400e973604f45021 Mon Sep 17 00:00:00 2001 From: Alexander Bork Date: Fri, 7 Jun 2019 15:34:44 +0200 Subject: [PATCH 20/47] Added computation of dynamic behavior vector for DFTs --- src/storm-dft/builder/DFTBuilder.cpp | 86 +++++++++++++++++++++++++++- src/storm-dft/builder/DFTBuilder.h | 2 + src/storm-dft/storage/dft/DFT.cpp | 5 +- src/storm-dft/storage/dft/DFT.h | 8 ++- 4 files changed, 98 insertions(+), 3 deletions(-) diff --git a/src/storm-dft/builder/DFTBuilder.cpp b/src/storm-dft/builder/DFTBuilder.cpp index c02fb3364..af9569eb5 100644 --- a/src/storm-dft/builder/DFTBuilder.cpp +++ b/src/storm-dft/builder/DFTBuilder.cpp @@ -97,7 +97,7 @@ namespace storm { } STORM_LOG_THROW(!mTopLevelIdentifier.empty(), storm::exceptions::WrongFormatException, "No top level element defined."); - storm::storage::DFT dft(elems, mElements[mTopLevelIdentifier]); + storm::storage::DFT dft(elems, mElements[mTopLevelIdentifier], computeHasDynamicBehavior(elems)); // Set layout info for (auto& elem : mElements) { @@ -135,6 +135,90 @@ namespace storm { return elem->rank(); } + template + std::vector DFTBuilder::computeHasDynamicBehavior(DFTElementVector elements) { + std::vector dynamicBehaviorVector(elements.size()); + // Initialize with false + std::fill(dynamicBehaviorVector.begin(), dynamicBehaviorVector.end(), false); + + std::queue elementQueue; + + // deal with all dynamic elements + for (auto const &element : elements) { + switch (element->type()) { + case storage::DFTElementType::PAND: + case storage::DFTElementType::POR: + // TODO check SPAREs, SEQs, MUTEXs + case storage::DFTElementType::SPARE: + case storage::DFTElementType::SEQ: + case storage::DFTElementType::MUTEX: { + auto gate = std::static_pointer_cast>(element); + dynamicBehaviorVector[gate->id()] = true; + for (auto const &child : gate->children()) { + // only enqueue static children + if (!dynamicBehaviorVector.at(child->id())) { + elementQueue.push(child); + } + } + break; + } + default: { + break; + } + + } + } + // propagate dynamic behavior + while (!elementQueue.empty()) { + DFTElementPointer currentElement = elementQueue.front(); + elementQueue.pop(); + switch (currentElement->type()) { + // Static Gates + case storage::DFTElementType::AND: + case storage::DFTElementType::OR: + case storage::DFTElementType::VOT: { + // check all parents and if one has dynamic behavior, propagate it + dynamicBehaviorVector[currentElement->id()] = true; + auto gate = std::static_pointer_cast>(currentElement); + for (auto const &child : gate->children()) { + // only enqueue static children + if (!dynamicBehaviorVector.at(child->id())) { + elementQueue.push(child); + } + } + break; + } + //BEs + case storage::DFTElementType::BE_EXP: + case storage::DFTElementType::BE_CONST: + case storage::DFTElementType::BE: { + auto be = std::static_pointer_cast>(currentElement); + dynamicBehaviorVector[be->id()] = true; + // add all ingoing dependencies to queue + for (auto const &dep : be->ingoingDependencies()) { + if (!dynamicBehaviorVector.at(dep->id())) { + elementQueue.push(dep); + } + } + break; + } + case storage::DFTElementType::PDEP: { + auto dep = std::static_pointer_cast>(currentElement); + dynamicBehaviorVector[dep->id()] = true; + // add all ingoing dependencies to queue + auto trigger = dep->triggerEvent(); + if (!dynamicBehaviorVector.at(trigger->id())) { + elementQueue.push(trigger); + } + break; + } + default: + break; + } + } + return dynamicBehaviorVector; + } + template bool DFTBuilder::addRestriction(std::string const& name, std::vector const& children, storm::storage::DFTElementType tp) { if (children.size() <= 1) { diff --git a/src/storm-dft/builder/DFTBuilder.h b/src/storm-dft/builder/DFTBuilder.h index 31764e2b8..2529ff5c1 100644 --- a/src/storm-dft/builder/DFTBuilder.h +++ b/src/storm-dft/builder/DFTBuilder.h @@ -236,6 +236,8 @@ namespace storm { void topoVisit(DFTElementPointer const& n, std::map>& visited, DFTElementVector& L); DFTElementVector topoSort(); + + std::vector computeHasDynamicBehavior(DFTElementVector elements); // If true, the standard gate adders make a pand inclusive, and exclusive otherwise. bool pandDefaultInclusive; diff --git a/src/storm-dft/storage/dft/DFT.cpp b/src/storm-dft/storage/dft/DFT.cpp index 28b7d29d0..78ecfe8bb 100644 --- a/src/storm-dft/storage/dft/DFT.cpp +++ b/src/storm-dft/storage/dft/DFT.cpp @@ -17,7 +17,10 @@ namespace storm { namespace storage { template - DFT::DFT(DFTElementVector const& elements, DFTElementPointer const& tle) : mElements(elements), mNrOfBEs(0), mNrOfSpares(0), mTopLevelIndex(tle->id()), mMaxSpareChildCount(0) { + DFT::DFT(DFTElementVector const &elements, DFTElementPointer const &tle, + std::vector const &dynamicBehavior) : + mElements(elements), mNrOfBEs(0), mNrOfSpares(0), mTopLevelIndex(tle->id()), mMaxSpareChildCount(0), + mDynamicBehavior(dynamicBehavior) { // Check that ids correspond to indices in the element vector STORM_LOG_ASSERT(elementIndicesCorrect(), "Ids incorrect."); size_t nrRepresentatives = 0; diff --git a/src/storm-dft/storage/dft/DFT.h b/src/storm-dft/storage/dft/DFT.h index 5c885ef59..37ebe58d2 100644 --- a/src/storm-dft/storage/dft/DFT.h +++ b/src/storm-dft/storage/dft/DFT.h @@ -67,9 +67,11 @@ namespace storm { std::map mRepresentants; // id element -> id representative std::vector> mSymmetries; std::map mLayoutInfo; + std::vector mDynamicBehavior; public: - DFT(DFTElementVector const& elements, DFTElementPointer const& tle); + DFT(DFTElementVector const &elements, DFTElementPointer const &tle, + std::vector const &dynamicBehavior); DFTStateGenerationInfo buildStateGenerationInfo(storm::storage::DFTIndependentSymmetries const& symmetries) const; @@ -133,6 +135,10 @@ namespace storm { return mDependencies; } + std::vector const &getDynamicBehavior() const { + return mDynamicBehavior; + } + std::vector nonColdBEs() const { std::vector result; for (DFTElementPointer elem : mElements) { From aa150fc2e36eac98711e260f9fa85c9cab449188 Mon Sep 17 00:00:00 2001 From: Alexander Bork Date: Fri, 7 Jun 2019 15:36:33 +0200 Subject: [PATCH 21/47] Extended FDEP conflict search by not considering pairs of FDEPs with static behavior --- .../modelchecker/dft/DFTASFChecker.cpp | 52 +++++++++++-------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/src/storm-dft/modelchecker/dft/DFTASFChecker.cpp b/src/storm-dft/modelchecker/dft/DFTASFChecker.cpp index 668c75757..133a5ea14 100644 --- a/src/storm-dft/modelchecker/dft/DFTASFChecker.cpp +++ b/src/storm-dft/modelchecker/dft/DFTASFChecker.cpp @@ -948,28 +948,38 @@ namespace storm { dep1Index = dft.getDependencies().at(i); for (size_t j = i + 1; j < dft.getDependencies().size(); ++j) { dep2Index = dft.getDependencies().at(j); - if (dft.getDependency(dep1Index)->triggerEvent() == dft.getDependency(dep2Index)->triggerEvent()) { - STORM_LOG_DEBUG("Conflict between " << dft.getElement(dep1Index)->name() << " and " - << dft.getElement(dep2Index)->name() << ": Same trigger"); - res.emplace_back(std::pair(dep1Index, dep2Index)); - } else { - switch (checkDependencyConflict(dep1Index, dep2Index, timeout)) { - case storm::solver::SmtSolver::CheckResult::Sat: - STORM_LOG_DEBUG("Conflict between " << dft.getElement(dep1Index)->name() << " and " - << dft.getElement(dep2Index)->name()); - res.emplace_back(std::pair(dep1Index, dep2Index)); - break; - case storm::solver::SmtSolver::CheckResult::Unknown: - STORM_LOG_DEBUG( - "Unknown: Conflict between " << dft.getElement(dep1Index)->name() << " and " - << dft.getElement(dep2Index)->name()); - res.emplace_back(std::pair(dep1Index, dep2Index)); - break; - default: - STORM_LOG_DEBUG("No conflict between " << dft.getElement(dep1Index)->name() << " and " - << dft.getElement(dep2Index)->name()); - break; + if (dft.getDynamicBehavior()[dep1Index] || dft.getDynamicBehavior()[dep2Index]) { + if (dft.getDependency(dep1Index)->triggerEvent() == + dft.getDependency(dep2Index)->triggerEvent()) { + STORM_LOG_DEBUG("Conflict between " << dft.getElement(dep1Index)->name() << " and " + << dft.getElement(dep2Index)->name() + << ": Same trigger"); + res.emplace_back(std::pair(dep1Index, dep2Index)); + } else { + switch (checkDependencyConflict(dep1Index, dep2Index, timeout)) { + case storm::solver::SmtSolver::CheckResult::Sat: + STORM_LOG_DEBUG("Conflict between " << dft.getElement(dep1Index)->name() << " and " + << dft.getElement(dep2Index)->name()); + res.emplace_back(std::pair(dep1Index, dep2Index)); + break; + case storm::solver::SmtSolver::CheckResult::Unknown: + STORM_LOG_DEBUG( + "Unknown: Conflict between " << dft.getElement(dep1Index)->name() << " and " + << dft.getElement(dep2Index)->name()); + res.emplace_back(std::pair(dep1Index, dep2Index)); + break; + default: + STORM_LOG_DEBUG( + "No conflict between " << dft.getElement(dep1Index)->name() << " and " + << dft.getElement(dep2Index)->name()); + break; + } } + } else { + STORM_LOG_DEBUG( + "Static behavior: No conflict between " << dft.getElement(dep1Index)->name() << " and " + << dft.getElement(dep2Index)->name()); + break; } } } From 589555c75f485fdd56438ab7fe7e8465110255ef Mon Sep 17 00:00:00 2001 From: Alexander Bork Date: Tue, 18 Jun 2019 16:07:15 +0200 Subject: [PATCH 22/47] Moved dynamic behavior computation from builder to DFT and added SEQ and SPARE cases --- src/storm-dft-cli/storm-dft.cpp | 13 ++ src/storm-dft/builder/DFTBuilder.cpp | 86 +--------- .../modelchecker/dft/DFTASFChecker.cpp | 2 +- src/storm-dft/storage/dft/DFT.cpp | 156 +++++++++++++++++- src/storm-dft/storage/dft/DFT.h | 5 +- 5 files changed, 170 insertions(+), 92 deletions(-) diff --git a/src/storm-dft-cli/storm-dft.cpp b/src/storm-dft-cli/storm-dft.cpp index 87c6aafa9..117ee9c43 100644 --- a/src/storm-dft-cli/storm-dft.cpp +++ b/src/storm-dft-cli/storm-dft.cpp @@ -92,6 +92,17 @@ void processOptions() { } #ifdef STORM_HAVE_Z3 + if(debug.isTestSet()){ + dft->setDynamicBehaviorInfo(); + for(size_t i = 0; i < dft->nrElements(); ++i){ + if(dft->getDynamicBehavior()[i]) { + STORM_LOG_DEBUG("Element " << dft->getElement(i)->name() << " has dynamic behavior"); + } else { + STORM_LOG_DEBUG("Element " << dft->getElement(i)->name() << " has static behavior"); + } + } + } + if (faultTreeSettings.solveWithSMT()) { dft = dftTransformator.transformUniqueFailedBe(*dft); if (dft->getDependencies().size() > 0) { @@ -100,6 +111,8 @@ void processOptions() { } // Solve with SMT STORM_LOG_DEBUG("Running DFT analysis with use of SMT"); + // Set dynamic behavior vector + dft->setDynamicBehaviorInfo(); storm::api::analyzeDFTSMT(*dft, true, debug.isTestSet()); return; } diff --git a/src/storm-dft/builder/DFTBuilder.cpp b/src/storm-dft/builder/DFTBuilder.cpp index af9569eb5..c02fb3364 100644 --- a/src/storm-dft/builder/DFTBuilder.cpp +++ b/src/storm-dft/builder/DFTBuilder.cpp @@ -97,7 +97,7 @@ namespace storm { } STORM_LOG_THROW(!mTopLevelIdentifier.empty(), storm::exceptions::WrongFormatException, "No top level element defined."); - storm::storage::DFT dft(elems, mElements[mTopLevelIdentifier], computeHasDynamicBehavior(elems)); + storm::storage::DFT dft(elems, mElements[mTopLevelIdentifier]); // Set layout info for (auto& elem : mElements) { @@ -135,90 +135,6 @@ namespace storm { return elem->rank(); } - template - std::vector DFTBuilder::computeHasDynamicBehavior(DFTElementVector elements) { - std::vector dynamicBehaviorVector(elements.size()); - // Initialize with false - std::fill(dynamicBehaviorVector.begin(), dynamicBehaviorVector.end(), false); - - std::queue elementQueue; - - // deal with all dynamic elements - for (auto const &element : elements) { - switch (element->type()) { - case storage::DFTElementType::PAND: - case storage::DFTElementType::POR: - // TODO check SPAREs, SEQs, MUTEXs - case storage::DFTElementType::SPARE: - case storage::DFTElementType::SEQ: - case storage::DFTElementType::MUTEX: { - auto gate = std::static_pointer_cast>(element); - dynamicBehaviorVector[gate->id()] = true; - for (auto const &child : gate->children()) { - // only enqueue static children - if (!dynamicBehaviorVector.at(child->id())) { - elementQueue.push(child); - } - } - break; - } - default: { - break; - } - - } - } - // propagate dynamic behavior - while (!elementQueue.empty()) { - DFTElementPointer currentElement = elementQueue.front(); - elementQueue.pop(); - switch (currentElement->type()) { - // Static Gates - case storage::DFTElementType::AND: - case storage::DFTElementType::OR: - case storage::DFTElementType::VOT: { - // check all parents and if one has dynamic behavior, propagate it - dynamicBehaviorVector[currentElement->id()] = true; - auto gate = std::static_pointer_cast>(currentElement); - for (auto const &child : gate->children()) { - // only enqueue static children - if (!dynamicBehaviorVector.at(child->id())) { - elementQueue.push(child); - } - } - break; - } - //BEs - case storage::DFTElementType::BE_EXP: - case storage::DFTElementType::BE_CONST: - case storage::DFTElementType::BE: { - auto be = std::static_pointer_cast>(currentElement); - dynamicBehaviorVector[be->id()] = true; - // add all ingoing dependencies to queue - for (auto const &dep : be->ingoingDependencies()) { - if (!dynamicBehaviorVector.at(dep->id())) { - elementQueue.push(dep); - } - } - break; - } - case storage::DFTElementType::PDEP: { - auto dep = std::static_pointer_cast>(currentElement); - dynamicBehaviorVector[dep->id()] = true; - // add all ingoing dependencies to queue - auto trigger = dep->triggerEvent(); - if (!dynamicBehaviorVector.at(trigger->id())) { - elementQueue.push(trigger); - } - break; - } - default: - break; - } - } - return dynamicBehaviorVector; - } - template bool DFTBuilder::addRestriction(std::string const& name, std::vector const& children, storm::storage::DFTElementType tp) { if (children.size() <= 1) { diff --git a/src/storm-dft/modelchecker/dft/DFTASFChecker.cpp b/src/storm-dft/modelchecker/dft/DFTASFChecker.cpp index 133a5ea14..c30b365a1 100644 --- a/src/storm-dft/modelchecker/dft/DFTASFChecker.cpp +++ b/src/storm-dft/modelchecker/dft/DFTASFChecker.cpp @@ -948,7 +948,7 @@ namespace storm { dep1Index = dft.getDependencies().at(i); for (size_t j = i + 1; j < dft.getDependencies().size(); ++j) { dep2Index = dft.getDependencies().at(j); - if (dft.getDynamicBehavior()[dep1Index] || dft.getDynamicBehavior()[dep2Index]) { + if (dft.getDynamicBehavior()[dep1Index] && dft.getDynamicBehavior()[dep2Index]) { if (dft.getDependency(dep1Index)->triggerEvent() == dft.getDependency(dep2Index)->triggerEvent()) { STORM_LOG_DEBUG("Conflict between " << dft.getElement(dep1Index)->name() << " and " diff --git a/src/storm-dft/storage/dft/DFT.cpp b/src/storm-dft/storage/dft/DFT.cpp index 78ecfe8bb..b65638691 100644 --- a/src/storm-dft/storage/dft/DFT.cpp +++ b/src/storm-dft/storage/dft/DFT.cpp @@ -17,12 +17,15 @@ namespace storm { namespace storage { template - DFT::DFT(DFTElementVector const &elements, DFTElementPointer const &tle, - std::vector const &dynamicBehavior) : - mElements(elements), mNrOfBEs(0), mNrOfSpares(0), mTopLevelIndex(tle->id()), mMaxSpareChildCount(0), - mDynamicBehavior(dynamicBehavior) { + DFT::DFT(DFTElementVector const &elements, DFTElementPointer const &tle) : + mElements(elements), mNrOfBEs(0), mNrOfSpares(0), mTopLevelIndex(tle->id()), mMaxSpareChildCount(0) { // Check that ids correspond to indices in the element vector STORM_LOG_ASSERT(elementIndicesCorrect(), "Ids incorrect."); + + // Initialize dynamic behavior vector with TRUE to preserve correct behavior + // We don't directly call setDynamicBehaviorInfo to not slow down DFT generation if possible + mDynamicBehavior = std::vector(mElements.size()); + std::fill(mDynamicBehavior.begin(), mDynamicBehavior.end(), true); size_t nrRepresentatives = 0; for (auto& elem : mElements) { @@ -88,6 +91,151 @@ namespace storm { mStateVectorSize = nrElements() * 2 + mNrOfSpares * usageInfoBits + nrRepresentatives; } + template + void DFT::setDynamicBehaviorInfo() { + std::vector dynamicBehaviorVector(mElements.size()); + + std::queue elementQueue; + + // deal with all dynamic elements + for (auto const &element : mElements) { + switch (element->type()) { + case storage::DFTElementType::PAND: + case storage::DFTElementType::POR: + case storage::DFTElementType::MUTEX: { + auto gate = std::static_pointer_cast>(element); + dynamicBehaviorVector[gate->id()] = true; + for (auto const &child : gate->children()) { + // only enqueue static children + if (!dynamicBehaviorVector.at(child->id())) { + elementQueue.push(child); + } + } + break; + } + // TODO different cases + case storage::DFTElementType::SPARE: { + auto spare = std::static_pointer_cast>(element); + + // Iterate over all children (representatives of spare modules) + for (auto const &child : spare->children()) { + // Case 1: Shared Module + // If child only has one parent, it is this SPARE -> nothing to check + if (child->nrParents() > 1) { + // TODO make more efficient by directly setting ALL spares which share a module to be dynamic + for (auto const &parent : child->parents()) { + if (parent->isSpareGate() and parent->id() != spare->id()) { + dynamicBehaviorVector[spare->id()] = true; + break; // inner loop + } + } + } + // Case 2: Triggering outside events + // If the SPARE was already detected to have dynamic behavior, do not proceed + if (!dynamicBehaviorVector[spare->id()]) { + for (auto const &memberID : module(child->id())) { + // Iterate over all members of the module child represents + auto member = getElement(memberID); + for (auto const dep : member->outgoingDependencies()) { + // If the member has outgoing dependencies, check if those trigger something outside the module + for (auto const depEvent : dep->dependentEvents()) { + // If a dependent event is not found in the module, SPARE is dynamic + if (std::find(module(child->id()).begin(), module(child->id()).end(), + depEvent->id()) == module(child->id()).end()) { + dynamicBehaviorVector[spare->id()] = true; + break; //depEvent-loop + } + } + if (dynamicBehaviorVector[spare->id()]) { break; } //dependency-loop + } + if (dynamicBehaviorVector[spare->id()]) { break; } //module-loop + } + + } + if (dynamicBehaviorVector[spare->id()]) { break; } //child-loop + } + // if during the computation, dynamic behavior was detected, add children to queue + if (dynamicBehaviorVector[spare->id()]) { + for (auto const &child : spare->children()) { + // only enqueue static children + if (!dynamicBehaviorVector.at(child->id())) { + elementQueue.push(child); + } + } + } + break; + } + case storage::DFTElementType::SEQ: { + auto seq = std::static_pointer_cast>(element); + // A SEQ only has dynamic behavior if not all children are BEs + if (!seq->allChildrenBEs()) { + dynamicBehaviorVector[seq->id()] = true; + for (auto const &child : seq->children()) { + // only enqueue static children + if (!dynamicBehaviorVector.at(child->id())) { + elementQueue.push(child); + } + } + } + break; + } + default: { + break; + } + + } + } + // propagate dynamic behavior + while (!elementQueue.empty()) { + DFTElementPointer currentElement = elementQueue.front(); + elementQueue.pop(); + switch (currentElement->type()) { + // Static Gates + case storage::DFTElementType::AND: + case storage::DFTElementType::OR: + case storage::DFTElementType::VOT: { + // check all parents and if one has dynamic behavior, propagate it + dynamicBehaviorVector[currentElement->id()] = true; + auto gate = std::static_pointer_cast>(currentElement); + for (auto const &child : gate->children()) { + // only enqueue static children + if (!dynamicBehaviorVector.at(child->id())) { + elementQueue.push(child); + } + } + break; + } + //BEs + case storage::DFTElementType::BE_EXP: + case storage::DFTElementType::BE_CONST: + case storage::DFTElementType::BE: { + auto be = std::static_pointer_cast>(currentElement); + dynamicBehaviorVector[be->id()] = true; + // add all ingoing dependencies to queue + for (auto const &dep : be->ingoingDependencies()) { + if (!dynamicBehaviorVector.at(dep->id())) { + elementQueue.push(dep); + } + } + break; + } + case storage::DFTElementType::PDEP: { + auto dep = std::static_pointer_cast>(currentElement); + dynamicBehaviorVector[dep->id()] = true; + // add all ingoing dependencies to queue + auto trigger = dep->triggerEvent(); + if (!dynamicBehaviorVector.at(trigger->id())) { + elementQueue.push(trigger); + } + break; + } + default: + break; + } + } + mDynamicBehavior = dynamicBehaviorVector; + } + template DFTStateGenerationInfo DFT::buildStateGenerationInfo(storm::storage::DFTIndependentSymmetries const& symmetries) const { DFTStateGenerationInfo generationInfo(nrElements(), mMaxSpareChildCount); diff --git a/src/storm-dft/storage/dft/DFT.h b/src/storm-dft/storage/dft/DFT.h index 37ebe58d2..2133a54ab 100644 --- a/src/storm-dft/storage/dft/DFT.h +++ b/src/storm-dft/storage/dft/DFT.h @@ -70,8 +70,7 @@ namespace storm { std::vector mDynamicBehavior; public: - DFT(DFTElementVector const &elements, DFTElementPointer const &tle, - std::vector const &dynamicBehavior); + DFT(DFTElementVector const &elements, DFTElementPointer const &tle); DFTStateGenerationInfo buildStateGenerationInfo(storm::storage::DFTIndependentSymmetries const& symmetries) const; @@ -82,6 +81,8 @@ namespace storm { DFT optimize() const; void copyElements(std::vector elements, storm::builder::DFTBuilder builder) const; + + void setDynamicBehaviorInfo(); size_t stateBitVectorSize() const { // Ensure multiple of 64 From e2ef6bc52ae24eef1777b360934cbf31c9dac854 Mon Sep 17 00:00:00 2001 From: Alexander Bork Date: Wed, 19 Jun 2019 11:18:31 +0200 Subject: [PATCH 23/47] Added missing initialization of result vector --- src/storm-dft/storage/dft/DFT.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/storm-dft/storage/dft/DFT.cpp b/src/storm-dft/storage/dft/DFT.cpp index b65638691..611a9c5d9 100644 --- a/src/storm-dft/storage/dft/DFT.cpp +++ b/src/storm-dft/storage/dft/DFT.cpp @@ -93,7 +93,7 @@ namespace storm { template void DFT::setDynamicBehaviorInfo() { - std::vector dynamicBehaviorVector(mElements.size()); + std::vector dynamicBehaviorVector(mElements.size(), false); std::queue elementQueue; From 3616bdbf13737af01bf6ab6e838dc3588ba195e9 Mon Sep 17 00:00:00 2001 From: Alexander Bork Date: Wed, 19 Jun 2019 11:19:14 +0200 Subject: [PATCH 24/47] Added two test cases for the FDEP conflict search --- .../testfiles/dft/seq_conflict_test.dft | 12 +++++ .../testfiles/dft/spare_conflict_test.dft | 12 +++++ src/test/storm-dft/api/DftSmtTest.cpp | 46 +++++++++++++++++++ 3 files changed, 70 insertions(+) create mode 100644 resources/examples/testfiles/dft/seq_conflict_test.dft create mode 100644 resources/examples/testfiles/dft/spare_conflict_test.dft diff --git a/resources/examples/testfiles/dft/seq_conflict_test.dft b/resources/examples/testfiles/dft/seq_conflict_test.dft new file mode 100644 index 000000000..77b22e0f2 --- /dev/null +++ b/resources/examples/testfiles/dft/seq_conflict_test.dft @@ -0,0 +1,12 @@ +toplevel "A"; +"A" and "I" "B"; +"SEQ_dynamic" seq "I" "B"; +"B" or "J" "K"; +"DEP1_1" fdep "T1" "I"; +"DEP1_2" fdep "T1" "J"; +"DEP1_3" fdep "T1" "K"; + +"I" lambda=0.5 dorm=0; +"J" lambda=0.5 dorm=0; +"K" lambda=0.5 dorm=0; +"T1" lambda=0.5 dorm=0; \ No newline at end of file diff --git a/resources/examples/testfiles/dft/spare_conflict_test.dft b/resources/examples/testfiles/dft/spare_conflict_test.dft new file mode 100644 index 000000000..e7d076fa7 --- /dev/null +++ b/resources/examples/testfiles/dft/spare_conflict_test.dft @@ -0,0 +1,12 @@ +toplevel "A"; +"A" pand "SP1" "SP2"; +"SP1" wsp "I" "J"; +"SP2" wsp "J" "K"; +"DEP1" fdep "T1" "I"; +"DEP2" fdep "T2" "K"; + +"I" lambda=0.5 dorm=0; +"J" lambda=0.5 dorm=0; +"K" lambda=0.5 dorm=0; +"T1" lambda=0.5 dorm=0; +"T2" lambda=0.5 dorm=0; \ No newline at end of file diff --git a/src/test/storm-dft/api/DftSmtTest.cpp b/src/test/storm-dft/api/DftSmtTest.cpp index 95bcad150..a7d5f0993 100644 --- a/src/test/storm-dft/api/DftSmtTest.cpp +++ b/src/test/storm-dft/api/DftSmtTest.cpp @@ -56,4 +56,50 @@ namespace { EXPECT_EQ(smtChecker.getLeastFailureBound(30), uint64_t(1)); EXPECT_EQ(smtChecker.getAlwaysFailedBound(30), uint64_t(5)); } + + TEST(DftSmtTest, FDEPConflictTest) { + std::shared_ptr> dft = + storm::api::loadDFTGalileoFile(STORM_TEST_RESOURCES_DIR "/dft/spare_conflict_test.dft"); + EXPECT_TRUE(storm::api::isWellFormed(*dft)); + std::vector true_vector(10, true); + + dft->setDynamicBehaviorInfo(); + EXPECT_EQ(dft->getDynamicBehavior(), true_vector); + storm::modelchecker::DFTASFChecker smtChecker(*dft); + smtChecker.convert(); + smtChecker.toSolver(); + + EXPECT_TRUE(smtChecker.getDependencyConflicts().empty()); + } + + TEST(DftSmtTest, FDEPConflictSPARETest) { + std::shared_ptr> dft = + storm::api::loadDFTGalileoFile(STORM_TEST_RESOURCES_DIR "/dft/spare_conflict_test.dft"); + EXPECT_TRUE(storm::api::isWellFormed(*dft)); + std::vector true_vector(10, true); + + dft->setDynamicBehaviorInfo(); + EXPECT_EQ(dft->getDynamicBehavior(), true_vector); + storm::modelchecker::DFTASFChecker smtChecker(*dft); + smtChecker.convert(); + smtChecker.toSolver(); + + EXPECT_TRUE(smtChecker.getDependencyConflicts().empty()); + } + + TEST(DftSmtTest, FDEPConflictSEQTest) { + std::shared_ptr> dft = + storm::api::loadDFTGalileoFile(STORM_TEST_RESOURCES_DIR "/dft/seq_conflict_test.dft"); + EXPECT_TRUE(storm::api::isWellFormed(*dft)); + std::vector expected_dynamic_vector(dft->nrElements(), true); + expected_dynamic_vector.at(dft->getTopLevelIndex()) = false; + + dft->setDynamicBehaviorInfo(); + EXPECT_EQ(dft->getDynamicBehavior(), expected_dynamic_vector); + storm::modelchecker::DFTASFChecker smtChecker(*dft); + smtChecker.convert(); + smtChecker.toSolver(); + + EXPECT_EQ(smtChecker.getDependencyConflicts().size(), uint64_t(3)); + } } \ No newline at end of file From add2a40a6294ef3d200eeeea7124a4019a184b42 Mon Sep 17 00:00:00 2001 From: Alexander Bork Date: Fri, 28 Jun 2019 15:26:55 +0200 Subject: [PATCH 25/47] Integrated results of FDEP conflict search in DFT state space generation --- src/storm-dft-cli/storm-dft.cpp | 27 +++++----- .../generator/DftNextStateGenerator.cpp | 8 +-- src/storm-dft/storage/dft/DFT.cpp | 1 + src/storm-dft/storage/dft/DFT.h | 12 +++++ src/storm-dft/storage/dft/DFTState.cpp | 4 +- src/storm-dft/storage/dft/DFTState.h | 53 +++++++++++++++---- 6 files changed, 70 insertions(+), 35 deletions(-) diff --git a/src/storm-dft-cli/storm-dft.cpp b/src/storm-dft-cli/storm-dft.cpp index 117ee9c43..01271e2c5 100644 --- a/src/storm-dft-cli/storm-dft.cpp +++ b/src/storm-dft-cli/storm-dft.cpp @@ -32,7 +32,6 @@ void processOptions() { auto dftTransformator = storm::transformations::dft::DftTransformator(); - if (!dftIOSettings.isDftFileSet() && !dftIOSettings.isDftJsonFileSet()) { STORM_LOG_THROW(false, storm::exceptions::InvalidSettingsException, "No input model given."); } @@ -92,17 +91,6 @@ void processOptions() { } #ifdef STORM_HAVE_Z3 - if(debug.isTestSet()){ - dft->setDynamicBehaviorInfo(); - for(size_t i = 0; i < dft->nrElements(); ++i){ - if(dft->getDynamicBehavior()[i]) { - STORM_LOG_DEBUG("Element " << dft->getElement(i)->name() << " has dynamic behavior"); - } else { - STORM_LOG_DEBUG("Element " << dft->getElement(i)->name() << " has static behavior"); - } - } - } - if (faultTreeSettings.solveWithSMT()) { dft = dftTransformator.transformUniqueFailedBe(*dft); if (dft->getDependencies().size() > 0) { @@ -113,8 +101,18 @@ void processOptions() { STORM_LOG_DEBUG("Running DFT analysis with use of SMT"); // Set dynamic behavior vector dft->setDynamicBehaviorInfo(); - storm::api::analyzeDFTSMT(*dft, true, debug.isTestSet()); - return; + auto smtResults = storm::api::analyzeDFTSMT(*dft, true, debug.isTestSet()); + // Set the conflict map of the dft + std::set conflict_set; + for (auto conflict : smtResults.fdepConflicts) { + conflict_set.insert(size_t(conflict.first)); + conflict_set.insert(size_t(conflict.second)); + } + for (size_t depId : dft->getDependencies()) { + if (!conflict_set.count(depId)) { + dft->setDependencyNotInConflict(depId); + } + } } #endif @@ -215,7 +213,6 @@ void processOptions() { } } - // Analyze DFT // TODO allow building of state space even without properties if (props.empty()) { diff --git a/src/storm-dft/generator/DftNextStateGenerator.cpp b/src/storm-dft/generator/DftNextStateGenerator.cpp index f4af34ed1..bfbc870ba 100644 --- a/src/storm-dft/generator/DftNextStateGenerator.cpp +++ b/src/storm-dft/generator/DftNextStateGenerator.cpp @@ -76,14 +76,8 @@ namespace storm { Choice choice(0, !exploreDependencies); // Let BE fail - bool isFirst = true; while (!state->getFailableElements().isEnd()) { //TODO outside - if (storm::settings::getModule().isTakeFirstDependency() && exploreDependencies && !isFirst) { - // We discard further exploration as we already chose one dependent event - break; - } - isFirst = false; // Construct new state as copy from original one DFTStatePointer newState = state->copy(); @@ -167,7 +161,7 @@ namespace storm { // Set transitions if (exploreDependencies) { - // Failure is due to dependency -> add non-deterministic choice + // Failure is due to dependency -> add non-deterministic choice if necessary ValueType probability = mDft.getDependency(state->getFailableElements().get())->probability(); choice.addProbability(newStateId, probability); STORM_LOG_TRACE("Added transition to " << newStateId << " with probability " << probability); diff --git a/src/storm-dft/storage/dft/DFT.cpp b/src/storm-dft/storage/dft/DFT.cpp index 611a9c5d9..65beab71b 100644 --- a/src/storm-dft/storage/dft/DFT.cpp +++ b/src/storm-dft/storage/dft/DFT.cpp @@ -53,6 +53,7 @@ namespace storm { } } else if (elem->isDependency()) { mDependencies.push_back(elem->id()); + mDependencyInConflict.insert(std::make_pair(elem->id(), true)); } } diff --git a/src/storm-dft/storage/dft/DFT.h b/src/storm-dft/storage/dft/DFT.h index 2133a54ab..c06af7da4 100644 --- a/src/storm-dft/storage/dft/DFT.h +++ b/src/storm-dft/storage/dft/DFT.h @@ -68,6 +68,7 @@ namespace storm { std::vector> mSymmetries; std::map mLayoutInfo; std::vector mDynamicBehavior; + std::map mDependencyInConflict; public: DFT(DFTElementVector const &elements, DFTElementPointer const &tle); @@ -131,6 +132,17 @@ namespace storm { return mSpareModules.find(representativeId)->second; } } + + bool isDependencyInConflict(size_t id) const { + STORM_LOG_ASSERT(isDependency(id), "Not a dependency."); + return mDependencyInConflict.at(id); + } + + + void setDependencyNotInConflict(size_t id) { + STORM_LOG_ASSERT(isDependency(id), "Not a dependency."); + mDependencyInConflict.at(id) = false; + } std::vector const& getDependencies() const { return mDependencies; diff --git a/src/storm-dft/storage/dft/DFTState.cpp b/src/storm-dft/storage/dft/DFTState.cpp index be66bc2a0..887446a1d 100644 --- a/src/storm-dft/storage/dft/DFTState.cpp +++ b/src/storm-dft/storage/dft/DFTState.cpp @@ -82,7 +82,7 @@ namespace storm { STORM_LOG_ASSERT(dependencyId == dependency->id(), "Ids do not match."); assert(dependency->dependentEvents().size() == 1); if (hasFailed(dependency->triggerEvent()->id()) && getElementState(dependency->dependentEvents()[0]->id()) == DFTElementState::Operational) { - failableElements.addDependency(dependencyId); + failableElements.addDependency(dependency->id(), mDft.isDependencyInConflict(dependency->id())); STORM_LOG_TRACE("New dependency failure: " << *dependency); } } @@ -243,7 +243,7 @@ namespace storm { // Check if restriction prevents failure of dependent event if (!isEventDisabledViaRestriction(dependency->dependentEvents()[0]->id())) { // Add dependency as possible failure - failableElements.addDependency(dependency->id()); + failableElements.addDependency(dependency->id(), mDft.isDependencyInConflict(dependency->id())); STORM_LOG_TRACE("New dependency failure: " << *dependency); addedFailableDependency = true; } diff --git a/src/storm-dft/storage/dft/DFTState.h b/src/storm-dft/storage/dft/DFTState.h index fd911b355..2800ed6c8 100644 --- a/src/storm-dft/storage/dft/DFTState.h +++ b/src/storm-dft/storage/dft/DFTState.h @@ -33,9 +33,18 @@ namespace storm { currentlyFailableBE.set(id); } - void addDependency(size_t id) { - if (std::find(mFailableDependencies.begin(), mFailableDependencies.end(), id) == mFailableDependencies.end()) { - mFailableDependencies.push_back(id); + void addDependency(size_t id, bool isConflicting) { + if (isConflicting) { + if (std::find(mFailableConflictingDependencies.begin(), mFailableConflictingDependencies.end(), + id) == mFailableConflictingDependencies.end()) { + mFailableConflictingDependencies.push_back(id); + } + } else { + if (std::find(mFailableNonconflictingDependencies.begin(), + mFailableNonconflictingDependencies.end(), id) == + mFailableNonconflictingDependencies.end()) { + mFailableNonconflictingDependencies.push_back(id); + } } } @@ -44,21 +53,36 @@ namespace storm { } void removeDependency(size_t id) { - auto it = std::find(mFailableDependencies.begin(), mFailableDependencies.end(), id); - if (it != mFailableDependencies.end()) { - mFailableDependencies.erase(it); + auto it1 = std::find(mFailableConflictingDependencies.begin(), + mFailableConflictingDependencies.end(), id); + if (it1 != mFailableConflictingDependencies.end()) { + mFailableConflictingDependencies.erase(it1); + return; + } + auto it2 = std::find(mFailableNonconflictingDependencies.begin(), + mFailableNonconflictingDependencies.end(), id); + if (it2 != mFailableNonconflictingDependencies.end()) { + mFailableNonconflictingDependencies.erase(it2); + return; } } void clear() { currentlyFailableBE.clear(); - mFailableDependencies.clear(); + mFailableConflictingDependencies.clear(); + mFailableNonconflictingDependencies.clear(); } void init(bool dependency) const { this->dependency = dependency; if (this->dependency) { - itDep = mFailableDependencies.begin(); + if (!mFailableNonconflictingDependencies.empty()) { + itDep = mFailableNonconflictingDependencies.begin(); + conflicting = false; + } else { + itDep = mFailableConflictingDependencies.begin(); + conflicting = true; + } } else { it = currentlyFailableBE.begin(); } @@ -77,7 +101,12 @@ namespace storm { bool isEnd() const { if (dependency) { - return itDep == mFailableDependencies.end(); + if (!conflicting) { + // If we are handling the non-conflicting FDEPs, end after the first element + return itDep != mFailableNonconflictingDependencies.begin(); + } else { + return itDep == mFailableConflictingDependencies.end(); + } } else { return it == currentlyFailableBE.end(); } @@ -96,7 +125,7 @@ namespace storm { }; bool hasDependencies() const { - return !mFailableDependencies.empty(); + return !mFailableConflictingDependencies.empty() || !mFailableNonconflictingDependencies.empty(); } bool hasBEs() const { @@ -112,9 +141,11 @@ namespace storm { } mutable bool dependency; + mutable bool conflicting; storm::storage::BitVector currentlyFailableBE; - std::vector mFailableDependencies; + std::vector mFailableConflictingDependencies; + std::vector mFailableNonconflictingDependencies; std::set remainingRelevantEvents; mutable storm::storage::BitVector::const_iterator it; From 27e65d5669c35f76883f06f8a66f040ca650bdbf Mon Sep 17 00:00:00 2001 From: Alexander Bork Date: Wed, 3 Jul 2019 15:44:52 +0200 Subject: [PATCH 26/47] Added construction of the state remapping for elimination of non-Markovian states in MAs --- src/storm/models/sparse/MarkovAutomaton.cpp | 164 ++++++++++++++++---- src/storm/models/sparse/MarkovAutomaton.h | 2 + 2 files changed, 137 insertions(+), 29 deletions(-) diff --git a/src/storm/models/sparse/MarkovAutomaton.cpp b/src/storm/models/sparse/MarkovAutomaton.cpp index 9d6869d84..c6cc5f19f 100644 --- a/src/storm/models/sparse/MarkovAutomaton.cpp +++ b/src/storm/models/sparse/MarkovAutomaton.cpp @@ -1,3 +1,5 @@ +#include + #include "storm/models/sparse/MarkovAutomaton.h" #include "storm/adapters/RationalFunctionAdapter.h" @@ -16,7 +18,7 @@ namespace storm { namespace models { namespace sparse { - + template MarkovAutomaton::MarkovAutomaton(storm::storage::SparseMatrix const& transitionMatrix, storm::models::sparse::StateLabeling const& stateLabeling, @@ -25,7 +27,7 @@ namespace storm { : MarkovAutomaton(storm::storage::sparse::ModelComponents(transitionMatrix, stateLabeling, rewardModels, true, markovianStates)) { // Intentionally left empty } - + template MarkovAutomaton::MarkovAutomaton(storm::storage::SparseMatrix&& transitionMatrix, storm::models::sparse::StateLabeling&& stateLabeling, @@ -34,23 +36,23 @@ namespace storm { : MarkovAutomaton(storm::storage::sparse::ModelComponents(std::move(transitionMatrix), std::move(stateLabeling), std::move(rewardModels), true, std::move(markovianStates))) { // Intentionally left empty } - + template MarkovAutomaton::MarkovAutomaton(storm::storage::sparse::ModelComponents const& components) : NondeterministicModel(ModelType::MarkovAutomaton, components), markovianStates(components.markovianStates.get()) { - + if (components.exitRates) { exitRates = components.exitRates.get(); } - + if (components.rateTransitions) { this->turnRatesToProbabilities(); } closed = this->checkIsClosed(); } - + template MarkovAutomaton::MarkovAutomaton(storm::storage::sparse::ModelComponents&& components) : NondeterministicModel(ModelType::MarkovAutomaton, std::move(components)), markovianStates(std::move(components.markovianStates.get())) { - + if (components.exitRates) { exitRates = std::move(components.exitRates.get()); } @@ -60,52 +62,52 @@ namespace storm { } closed = this->checkIsClosed(); } - + template bool MarkovAutomaton::isClosed() const { return closed; } - + template bool MarkovAutomaton::isHybridState(storm::storage::sparse::state_type state) const { return isMarkovianState(state) && (this->getTransitionMatrix().getRowGroupSize(state) > 1); } - + template bool MarkovAutomaton::isMarkovianState(storm::storage::sparse::state_type state) const { return this->markovianStates.get(state); } - + template bool MarkovAutomaton::isProbabilisticState(storm::storage::sparse::state_type state) const { return !this->markovianStates.get(state); } - + template std::vector const& MarkovAutomaton::getExitRates() const { return this->exitRates; } - + template std::vector& MarkovAutomaton::getExitRates() { return this->exitRates; } - + template ValueType const& MarkovAutomaton::getExitRate(storm::storage::sparse::state_type state) const { return this->exitRates[state]; } - + template ValueType MarkovAutomaton::getMaximalExitRate() const { return storm::utility::vector::max_if(this->exitRates, this->markovianStates); } - + template storm::storage::BitVector const& MarkovAutomaton::getMarkovianStates() const { return this->markovianStates; } - + template void MarkovAutomaton::close() { if (!closed) { @@ -120,16 +122,16 @@ namespace storm { exitRates[state] = storm::utility::zero(); } } - + if (!keptChoices.full()) { *this = std::move(*storm::transformer::buildSubsystem(*this, storm::storage::BitVector(this->getNumberOfStates(), true), keptChoices, false).model->template as>()); } - + // Mark the automaton as closed. closed = true; } } - + template void MarkovAutomaton::turnRatesToProbabilities() { bool assertRates = (this->exitRates.size() == this->getNumberOfStates()); @@ -137,7 +139,7 @@ namespace storm { STORM_LOG_THROW(this->exitRates.empty(), storm::exceptions::InvalidArgumentException, "The specified exit rate vector has an unexpected size."); this->exitRates.reserve(this->getNumberOfStates()); } - + storm::utility::ConstantsComparator comparator; for (uint_fast64_t state = 0; state< this->getNumberOfStates(); ++state) { uint_fast64_t row = this->getTransitionMatrix().getRowGroupIndices()[state]; @@ -163,12 +165,12 @@ namespace storm { } } } - + template bool MarkovAutomaton::isConvertibleToCtmc() const { return isClosed() && markovianStates.full(); } - + template bool MarkovAutomaton::hasOnlyTrivialNondeterminism() const { // Check every state @@ -185,7 +187,7 @@ namespace storm { } return true; } - + template bool MarkovAutomaton::checkIsClosed() const { for (auto state : markovianStates) { @@ -195,7 +197,7 @@ namespace storm { } return true; } - + template std::shared_ptr> MarkovAutomaton::convertToCtmc() const { if (isClosed() && markovianStates.full()) { @@ -265,7 +267,109 @@ namespace storm { return std::make_shared>(std::move(rateMatrix), std::move(stateLabeling)); } - + template + std::shared_ptr> + MarkovAutomaton::eliminateNonmarkovianStates() const { + if (isClosed() && markovianStates.full()) { + storm::storage::sparse::ModelComponents components( + this->getTransitionMatrix(), this->getStateLabeling(), this->getRewardModels(), false); + components.exitRates = this->getExitRates(); + if (this->hasChoiceLabeling()) { + components.choiceLabeling = this->getChoiceLabeling(); + } + if (this->hasStateValuations()) { + components.stateValuations = this->getStateValuations(); + } + if (this->hasChoiceOrigins()) { + components.choiceOrigins = this->getChoiceOrigins(); + } + return std::make_shared>(std::move(components)); + } + + std::map stateRemapping; + std::set statesToKeep; + std::queue changedStates; + std::queue queue; + + storm::storage::SparseMatrix backwards = this->getBackwardTransitions(); + + // Determine the state remapping + // TODO Consider state labels + for (uint_fast64_t base_state = 0; base_state < this->getNumberOfStates(); ++base_state) { + STORM_LOG_ASSERT(!this->isHybridState(base_state), "Base state is hybrid."); + if (this->isMarkovianState(base_state)) { + queue.push(base_state); + + while (!queue.empty()) { + auto currState = queue.front(); + queue.pop(); + // Get predecessors from matrix + typename storm::storage::SparseMatrix::rows entriesInRow = backwards.getRow( + currState); + for (auto entryIt = entriesInRow.begin(), entryIte = entriesInRow.end(); + entryIt != entryIte; ++entryIt) { + uint_fast64_t predecessor = entryIt->getColumn(); + if (!this->isMarkovianState(predecessor) && !statesToKeep.count(predecessor)) { + if (!stateRemapping.count(predecessor)) { + stateRemapping[predecessor] = base_state; + queue.push(predecessor); + } else if (stateRemapping[predecessor] != base_state) { + stateRemapping.erase(predecessor); + statesToKeep.insert(predecessor); + changedStates.push(predecessor); + } + } + } + } + } + } + + // Correct the mapping with the states which have to be kept + while (!changedStates.empty()) { + uint_fast64_t base_state = changedStates.front(); + queue.push(base_state); + + while (!queue.empty()) { + auto currState = queue.front(); + queue.pop(); + // Get predecessors from matrix + typename storm::storage::SparseMatrix::rows entriesInRow = backwards.getRow( + currState); + for (auto entryIt = entriesInRow.begin(), entryIte = entriesInRow.end(); + entryIt != entryIte; ++entryIt) { + uint_fast64_t predecessor = entryIt->getColumn(); + if (!this->isMarkovianState(predecessor) && !statesToKeep.count(predecessor)) { + if (!stateRemapping.count(predecessor)) { + stateRemapping[predecessor] = base_state; + queue.push(predecessor); + } else if (stateRemapping[predecessor] != base_state) { + stateRemapping.erase(predecessor); + statesToKeep.insert(predecessor); + changedStates.push(predecessor); + } + } + } + } + + changedStates.pop(); + } + + // At this point, we hopefully have a valid mapping which eliminates a lot of states + + STORM_PRINT("Remapping \n") + for (auto entry : stateRemapping) { + STORM_PRINT(std::to_string(entry.first) << " -> " << std::to_string(entry.second) << "\n") + } + STORM_PRINT("Remapped States: " << stateRemapping.size() << "\n") + // TODO test some examples, especially ones containing non-determinism + + // Build the new matrix + // TODO + + return nullptr; + } + + template void MarkovAutomaton::printModelInformationToStream(std::ostream& out) const { this->printModelInformationHeaderToStream(out); @@ -274,13 +378,15 @@ namespace storm { out << "Max. Rate.: \t" << this->getMaximalExitRate() << std::endl; this->printModelInformationFooterToStream(out); } - - + + template class MarkovAutomaton; #ifdef STORM_HAVE_CARL + template class MarkovAutomaton; - + template class MarkovAutomaton>; + template class MarkovAutomaton; #endif } // namespace sparse diff --git a/src/storm/models/sparse/MarkovAutomaton.h b/src/storm/models/sparse/MarkovAutomaton.h index b5a7f347f..45ac81a75 100644 --- a/src/storm/models/sparse/MarkovAutomaton.h +++ b/src/storm/models/sparse/MarkovAutomaton.h @@ -147,6 +147,8 @@ namespace storm { * @return The resulting CTMC. */ std::shared_ptr> convertToCtmc() const; + + std::shared_ptr> eliminateNonmarkovianStates() const; virtual void printModelInformationToStream(std::ostream& out) const override; From 549774abc9e12151d641d6071b420a5f6bc3b2a1 Mon Sep 17 00:00:00 2001 From: Alexander Bork Date: Fri, 5 Jul 2019 15:18:15 +0200 Subject: [PATCH 27/47] Added state remapping in state elimination --- src/storm/models/sparse/MarkovAutomaton.cpp | 57 +++++++++++++++------ 1 file changed, 42 insertions(+), 15 deletions(-) diff --git a/src/storm/models/sparse/MarkovAutomaton.cpp b/src/storm/models/sparse/MarkovAutomaton.cpp index c6cc5f19f..9326ca4eb 100644 --- a/src/storm/models/sparse/MarkovAutomaton.cpp +++ b/src/storm/models/sparse/MarkovAutomaton.cpp @@ -286,7 +286,7 @@ namespace storm { return std::make_shared>(std::move(components)); } - std::map stateRemapping; + std::map eliminationMapping; std::set statesToKeep; std::queue changedStates; std::queue queue; @@ -310,11 +310,11 @@ namespace storm { entryIt != entryIte; ++entryIt) { uint_fast64_t predecessor = entryIt->getColumn(); if (!this->isMarkovianState(predecessor) && !statesToKeep.count(predecessor)) { - if (!stateRemapping.count(predecessor)) { - stateRemapping[predecessor] = base_state; + if (!eliminationMapping.count(predecessor)) { + eliminationMapping[predecessor] = base_state; queue.push(predecessor); - } else if (stateRemapping[predecessor] != base_state) { - stateRemapping.erase(predecessor); + } else if (eliminationMapping[predecessor] != base_state) { + eliminationMapping.erase(predecessor); statesToKeep.insert(predecessor); changedStates.push(predecessor); } @@ -339,11 +339,11 @@ namespace storm { entryIt != entryIte; ++entryIt) { uint_fast64_t predecessor = entryIt->getColumn(); if (!this->isMarkovianState(predecessor) && !statesToKeep.count(predecessor)) { - if (!stateRemapping.count(predecessor)) { - stateRemapping[predecessor] = base_state; + if (!eliminationMapping.count(predecessor)) { + eliminationMapping[predecessor] = base_state; queue.push(predecessor); - } else if (stateRemapping[predecessor] != base_state) { - stateRemapping.erase(predecessor); + } else if (eliminationMapping[predecessor] != base_state) { + eliminationMapping.erase(predecessor); statesToKeep.insert(predecessor); changedStates.push(predecessor); } @@ -356,15 +356,42 @@ namespace storm { // At this point, we hopefully have a valid mapping which eliminates a lot of states - STORM_PRINT("Remapping \n") - for (auto entry : stateRemapping) { - STORM_PRINT(std::to_string(entry.first) << " -> " << std::to_string(entry.second) << "\n") + STORM_PRINT("Elimination Mapping" << std::endl) + for (auto entry : eliminationMapping) { + STORM_PRINT(std::to_string(entry.first) << " -> " << std::to_string(entry.second) << std::endl) } - STORM_PRINT("Remapped States: " << stateRemapping.size() << "\n") - // TODO test some examples, especially ones containing non-determinism + STORM_PRINT("Eliminating " << eliminationMapping.size() << " states" << std::endl) + + // TODO explore if one can construct elimination mapping and state remapping in one step + + // Construct a mapping of old state space to new one + std::vector stateRemapping(this->getNumberOfStates(), -1); + uint_fast64_t currentNewState = 0; + for (uint_fast64_t state = 0; state < this->getNumberOfStates(); ++state) { + if (eliminationMapping.count(state) > 0) { + STORM_PRINT("Eliminate state " << state << std::endl) + if (stateRemapping[eliminationMapping[state]] == uint_fast64_t(-1)) { + STORM_PRINT( + "State " << eliminationMapping[state] << " is not mapped yet! Current New State: " + << currentNewState << std::endl) + + stateRemapping[eliminationMapping[state]] = currentNewState; + stateRemapping[state] = currentNewState; + ++currentNewState; + } else { + stateRemapping[state] = stateRemapping[eliminationMapping[state]]; + } + } else if (stateRemapping[state] == uint_fast64_t(-1)) { + stateRemapping[state] = currentNewState; + ++currentNewState; + } + } + + for (uint_fast64_t state = 0; state < stateRemapping.size(); ++state) STORM_PRINT( + state << "->" << stateRemapping[state] << std::endl) // Build the new matrix - // TODO + return nullptr; } From 10bb42e0f6d8806a54d8eebd6f8c0ba4d095f831 Mon Sep 17 00:00:00 2001 From: Alexander Bork Date: Tue, 9 Jul 2019 14:16:25 +0200 Subject: [PATCH 28/47] First version of non-Markovian state elimination for MAs --- src/storm/models/sparse/MarkovAutomaton.cpp | 90 ++++++++++++++++++--- 1 file changed, 79 insertions(+), 11 deletions(-) diff --git a/src/storm/models/sparse/MarkovAutomaton.cpp b/src/storm/models/sparse/MarkovAutomaton.cpp index 9326ca4eb..be443199e 100644 --- a/src/storm/models/sparse/MarkovAutomaton.cpp +++ b/src/storm/models/sparse/MarkovAutomaton.cpp @@ -1,4 +1,5 @@ #include +#include #include "storm/models/sparse/MarkovAutomaton.h" @@ -270,6 +271,9 @@ namespace storm { template std::shared_ptr> MarkovAutomaton::eliminateNonmarkovianStates() const { + // TODO reward models + + STORM_LOG_WARN("State elimination is currently not label preserving!"); if (isClosed() && markovianStates.full()) { storm::storage::sparse::ModelComponents components( this->getTransitionMatrix(), this->getStateLabeling(), this->getRewardModels(), false); @@ -356,10 +360,10 @@ namespace storm { // At this point, we hopefully have a valid mapping which eliminates a lot of states - STORM_PRINT("Elimination Mapping" << std::endl) + /*STORM_PRINT("Elimination Mapping" << std::endl) for (auto entry : eliminationMapping) { STORM_PRINT(std::to_string(entry.first) << " -> " << std::to_string(entry.second) << std::endl) - } + }*/ STORM_PRINT("Eliminating " << eliminationMapping.size() << " states" << std::endl) // TODO explore if one can construct elimination mapping and state remapping in one step @@ -369,31 +373,95 @@ namespace storm { uint_fast64_t currentNewState = 0; for (uint_fast64_t state = 0; state < this->getNumberOfStates(); ++state) { if (eliminationMapping.count(state) > 0) { - STORM_PRINT("Eliminate state " << state << std::endl) if (stateRemapping[eliminationMapping[state]] == uint_fast64_t(-1)) { - STORM_PRINT( - "State " << eliminationMapping[state] << " is not mapped yet! Current New State: " - << currentNewState << std::endl) - stateRemapping[eliminationMapping[state]] = currentNewState; stateRemapping[state] = currentNewState; ++currentNewState; + queue.push(eliminationMapping[state]); } else { stateRemapping[state] = stateRemapping[eliminationMapping[state]]; } } else if (stateRemapping[state] == uint_fast64_t(-1)) { stateRemapping[state] = currentNewState; + queue.push(state); ++currentNewState; } } - for (uint_fast64_t state = 0; state < stateRemapping.size(); ++state) STORM_PRINT( - state << "->" << stateRemapping[state] << std::endl) + // Build the new MA + storm::storage::SparseMatrix newTransitionMatrix; + storm::models::sparse::StateLabeling newStateLabeling( + this->getNumberOfStates() - eliminationMapping.size()); + storm::storage::BitVector newMarkovianStates(this->getNumberOfStates() - eliminationMapping.size(), + false); + std::vector newExitRates; + //TODO choice labeling + boost::optional choiceLabeling; + + // Initialize the matrix builder and helper variables + storm::storage::SparseMatrixBuilder matrixBuilder = storm::storage::SparseMatrixBuilder( + 0, 0, 0, false, true, 0); + uint_fast64_t currentRow = 0; + uint_fast64_t state = 0; + while (!queue.empty()) { + state = queue.front(); + queue.pop(); + + for (auto const &label : this->getLabelsOfState(state)) { + if (!newStateLabeling.containsLabel(label)) { + newStateLabeling.addLabel(label); + } + + newStateLabeling.addLabelToState(label, stateRemapping[state]); + } + + // Use a set to not include redundant rows + std::set> rowSet; + for (uint_fast64_t row = 0; row < this->getTransitionMatrix().getRowGroupSize(state); ++row) { + std::map transitions; + for (typename storm::storage::SparseMatrix::const_iterator itEntry = this->getTransitionMatrix().getRow( + state, row).begin(); + itEntry != this->getTransitionMatrix().getRow(state, row).end(); ++itEntry) { + uint_fast64_t newId = stateRemapping[itEntry->getColumn()]; + if (transitions.count(newId) == 0) { + transitions[newId] = itEntry->getValue(); + } else { + transitions[newId] += itEntry->getValue(); + } + } + rowSet.insert(transitions); + } + + // correctly set rates + auto rate = storm::utility::zero(); + + if (this->isMarkovianState(state)) { + newMarkovianStates.set(stateRemapping[state], true); + rate = this->exitRates.at(state); + } + + newExitRates.push_back(rate); + // Build matrix + matrixBuilder.newRowGroup(currentRow); + for (auto const &row : rowSet) { + for (auto const &transition : row) { + matrixBuilder.addNextValue(currentRow, transition.first, transition.second); + //STORM_PRINT(stateRemapping[state] << "->" << transition.first << " : " << transition.second << std::endl) + } + ++currentRow; + } + } + newTransitionMatrix = matrixBuilder.build(); - // Build the new matrix + storm::storage::sparse::ModelComponents newComponents = storm::storage::sparse::ModelComponents( + std::move(newTransitionMatrix), std::move(newStateLabeling)); + newComponents.rateTransitions = false; + newComponents.markovianStates = std::move(newMarkovianStates); + newComponents.exitRates = std::move(newExitRates); - return nullptr; + return std::make_shared>( + std::move(newComponents)); } From ae5c001d2402617565c8090f3114225860e3eed3 Mon Sep 17 00:00:00 2001 From: Alexander Bork Date: Tue, 9 Jul 2019 18:33:17 +0200 Subject: [PATCH 29/47] Moved non-Markovian state eliminator to its own class --- .../builder/ExplicitDFTModelBuilder.cpp | 6 +- src/storm/models/sparse/MarkovAutomaton.cpp | 197 --------------- src/storm/models/sparse/MarkovAutomaton.h | 1 - .../NonMarkovianChainTransformer.cpp | 233 ++++++++++++++++++ .../NonMarkovianChainTransformer.h | 27 ++ 5 files changed, 264 insertions(+), 200 deletions(-) create mode 100644 src/storm/transformer/NonMarkovianChainTransformer.cpp create mode 100644 src/storm/transformer/NonMarkovianChainTransformer.h diff --git a/src/storm-dft/builder/ExplicitDFTModelBuilder.cpp b/src/storm-dft/builder/ExplicitDFTModelBuilder.cpp index e71f8e4b9..ad1f5d449 100644 --- a/src/storm-dft/builder/ExplicitDFTModelBuilder.cpp +++ b/src/storm-dft/builder/ExplicitDFTModelBuilder.cpp @@ -14,6 +14,7 @@ #include "storm/settings/SettingsManager.h" #include "storm/logic/AtomicLabelFormula.h" #include "storm-dft/settings/modules/FaultTreeSettings.h" +#include "storm/transformer/NonMarkovianChainTransformer.h" namespace storm { @@ -653,8 +654,9 @@ namespace storm { } if (ma->hasOnlyTrivialNondeterminism()) { // Markov automaton can be converted into CTMC - // TODO: change components which were not moved accordingly - model = ma->convertToCtmc(); + // TODO apply transformer to all MAs + model = storm::transformer::NonMarkovianChainTransformer::eliminateNonmarkovianStates( + ma); } else { model = ma; } diff --git a/src/storm/models/sparse/MarkovAutomaton.cpp b/src/storm/models/sparse/MarkovAutomaton.cpp index be443199e..85f6b5d21 100644 --- a/src/storm/models/sparse/MarkovAutomaton.cpp +++ b/src/storm/models/sparse/MarkovAutomaton.cpp @@ -1,5 +1,4 @@ #include -#include #include "storm/models/sparse/MarkovAutomaton.h" @@ -268,202 +267,6 @@ namespace storm { return std::make_shared>(std::move(rateMatrix), std::move(stateLabeling)); } - template - std::shared_ptr> - MarkovAutomaton::eliminateNonmarkovianStates() const { - // TODO reward models - - STORM_LOG_WARN("State elimination is currently not label preserving!"); - if (isClosed() && markovianStates.full()) { - storm::storage::sparse::ModelComponents components( - this->getTransitionMatrix(), this->getStateLabeling(), this->getRewardModels(), false); - components.exitRates = this->getExitRates(); - if (this->hasChoiceLabeling()) { - components.choiceLabeling = this->getChoiceLabeling(); - } - if (this->hasStateValuations()) { - components.stateValuations = this->getStateValuations(); - } - if (this->hasChoiceOrigins()) { - components.choiceOrigins = this->getChoiceOrigins(); - } - return std::make_shared>(std::move(components)); - } - - std::map eliminationMapping; - std::set statesToKeep; - std::queue changedStates; - std::queue queue; - - storm::storage::SparseMatrix backwards = this->getBackwardTransitions(); - - // Determine the state remapping - // TODO Consider state labels - for (uint_fast64_t base_state = 0; base_state < this->getNumberOfStates(); ++base_state) { - STORM_LOG_ASSERT(!this->isHybridState(base_state), "Base state is hybrid."); - if (this->isMarkovianState(base_state)) { - queue.push(base_state); - - while (!queue.empty()) { - auto currState = queue.front(); - queue.pop(); - // Get predecessors from matrix - typename storm::storage::SparseMatrix::rows entriesInRow = backwards.getRow( - currState); - for (auto entryIt = entriesInRow.begin(), entryIte = entriesInRow.end(); - entryIt != entryIte; ++entryIt) { - uint_fast64_t predecessor = entryIt->getColumn(); - if (!this->isMarkovianState(predecessor) && !statesToKeep.count(predecessor)) { - if (!eliminationMapping.count(predecessor)) { - eliminationMapping[predecessor] = base_state; - queue.push(predecessor); - } else if (eliminationMapping[predecessor] != base_state) { - eliminationMapping.erase(predecessor); - statesToKeep.insert(predecessor); - changedStates.push(predecessor); - } - } - } - } - } - } - - // Correct the mapping with the states which have to be kept - while (!changedStates.empty()) { - uint_fast64_t base_state = changedStates.front(); - queue.push(base_state); - - while (!queue.empty()) { - auto currState = queue.front(); - queue.pop(); - // Get predecessors from matrix - typename storm::storage::SparseMatrix::rows entriesInRow = backwards.getRow( - currState); - for (auto entryIt = entriesInRow.begin(), entryIte = entriesInRow.end(); - entryIt != entryIte; ++entryIt) { - uint_fast64_t predecessor = entryIt->getColumn(); - if (!this->isMarkovianState(predecessor) && !statesToKeep.count(predecessor)) { - if (!eliminationMapping.count(predecessor)) { - eliminationMapping[predecessor] = base_state; - queue.push(predecessor); - } else if (eliminationMapping[predecessor] != base_state) { - eliminationMapping.erase(predecessor); - statesToKeep.insert(predecessor); - changedStates.push(predecessor); - } - } - } - } - - changedStates.pop(); - } - - // At this point, we hopefully have a valid mapping which eliminates a lot of states - - /*STORM_PRINT("Elimination Mapping" << std::endl) - for (auto entry : eliminationMapping) { - STORM_PRINT(std::to_string(entry.first) << " -> " << std::to_string(entry.second) << std::endl) - }*/ - STORM_PRINT("Eliminating " << eliminationMapping.size() << " states" << std::endl) - - // TODO explore if one can construct elimination mapping and state remapping in one step - - // Construct a mapping of old state space to new one - std::vector stateRemapping(this->getNumberOfStates(), -1); - uint_fast64_t currentNewState = 0; - for (uint_fast64_t state = 0; state < this->getNumberOfStates(); ++state) { - if (eliminationMapping.count(state) > 0) { - if (stateRemapping[eliminationMapping[state]] == uint_fast64_t(-1)) { - stateRemapping[eliminationMapping[state]] = currentNewState; - stateRemapping[state] = currentNewState; - ++currentNewState; - queue.push(eliminationMapping[state]); - } else { - stateRemapping[state] = stateRemapping[eliminationMapping[state]]; - } - } else if (stateRemapping[state] == uint_fast64_t(-1)) { - stateRemapping[state] = currentNewState; - queue.push(state); - ++currentNewState; - } - } - - // Build the new MA - storm::storage::SparseMatrix newTransitionMatrix; - storm::models::sparse::StateLabeling newStateLabeling( - this->getNumberOfStates() - eliminationMapping.size()); - storm::storage::BitVector newMarkovianStates(this->getNumberOfStates() - eliminationMapping.size(), - false); - std::vector newExitRates; - //TODO choice labeling - boost::optional choiceLabeling; - - // Initialize the matrix builder and helper variables - storm::storage::SparseMatrixBuilder matrixBuilder = storm::storage::SparseMatrixBuilder( - 0, 0, 0, false, true, 0); - uint_fast64_t currentRow = 0; - uint_fast64_t state = 0; - while (!queue.empty()) { - state = queue.front(); - queue.pop(); - - for (auto const &label : this->getLabelsOfState(state)) { - if (!newStateLabeling.containsLabel(label)) { - newStateLabeling.addLabel(label); - } - - newStateLabeling.addLabelToState(label, stateRemapping[state]); - } - - // Use a set to not include redundant rows - std::set> rowSet; - for (uint_fast64_t row = 0; row < this->getTransitionMatrix().getRowGroupSize(state); ++row) { - std::map transitions; - for (typename storm::storage::SparseMatrix::const_iterator itEntry = this->getTransitionMatrix().getRow( - state, row).begin(); - itEntry != this->getTransitionMatrix().getRow(state, row).end(); ++itEntry) { - uint_fast64_t newId = stateRemapping[itEntry->getColumn()]; - if (transitions.count(newId) == 0) { - transitions[newId] = itEntry->getValue(); - } else { - transitions[newId] += itEntry->getValue(); - } - } - rowSet.insert(transitions); - } - - // correctly set rates - auto rate = storm::utility::zero(); - - if (this->isMarkovianState(state)) { - newMarkovianStates.set(stateRemapping[state], true); - rate = this->exitRates.at(state); - } - - newExitRates.push_back(rate); - // Build matrix - matrixBuilder.newRowGroup(currentRow); - for (auto const &row : rowSet) { - for (auto const &transition : row) { - matrixBuilder.addNextValue(currentRow, transition.first, transition.second); - //STORM_PRINT(stateRemapping[state] << "->" << transition.first << " : " << transition.second << std::endl) - } - ++currentRow; - } - } - newTransitionMatrix = matrixBuilder.build(); - - storm::storage::sparse::ModelComponents newComponents = storm::storage::sparse::ModelComponents( - std::move(newTransitionMatrix), std::move(newStateLabeling)); - - newComponents.rateTransitions = false; - newComponents.markovianStates = std::move(newMarkovianStates); - newComponents.exitRates = std::move(newExitRates); - - return std::make_shared>( - std::move(newComponents)); - } - template void MarkovAutomaton::printModelInformationToStream(std::ostream& out) const { diff --git a/src/storm/models/sparse/MarkovAutomaton.h b/src/storm/models/sparse/MarkovAutomaton.h index 45ac81a75..c7ca80f35 100644 --- a/src/storm/models/sparse/MarkovAutomaton.h +++ b/src/storm/models/sparse/MarkovAutomaton.h @@ -148,7 +148,6 @@ namespace storm { */ std::shared_ptr> convertToCtmc() const; - std::shared_ptr> eliminateNonmarkovianStates() const; virtual void printModelInformationToStream(std::ostream& out) const override; diff --git a/src/storm/transformer/NonMarkovianChainTransformer.cpp b/src/storm/transformer/NonMarkovianChainTransformer.cpp new file mode 100644 index 000000000..1bc460ba5 --- /dev/null +++ b/src/storm/transformer/NonMarkovianChainTransformer.cpp @@ -0,0 +1,233 @@ +#include + +#include "NonMarkovianChainTransformer.h" + +#include "storm/storage/sparse/ModelComponents.h" +#include "storm/adapters/RationalFunctionAdapter.h" +#include "storm/models/sparse/StandardRewardModel.h" +#include "storm/utility/constants.h" +#include "storm/utility/ConstantsComparator.h" +#include "storm/utility/vector.h" +#include "storm/utility/macros.h" +#include "storm/utility/graph.h" + +namespace storm { + namespace transformer { + + template + std::shared_ptr> + NonMarkovianChainTransformer::eliminateNonmarkovianStates( + std::shared_ptr> ma, + bool preserveLabels) { + // TODO reward models + + STORM_LOG_WARN("State elimination is currently not label preserving!"); + STORM_LOG_WARN("Reward Models and Choice Labelings are ignored!"); + if (ma->isClosed() && ma->getMarkovianStates().full()) { + storm::storage::sparse::ModelComponents components( + ma->getTransitionMatrix(), ma->getStateLabeling(), ma->getRewardModels(), false); + components.exitRates = ma->getExitRates(); + if (ma->hasChoiceLabeling()) { + components.choiceLabeling = ma->getChoiceLabeling(); + } + if (ma->hasStateValuations()) { + components.stateValuations = ma->getStateValuations(); + } + if (ma->hasChoiceOrigins()) { + components.choiceOrigins = ma->getChoiceOrigins(); + } + return std::make_shared>( + std::move(components)); + } + + std::map eliminationMapping; + std::set statesToKeep; + std::queue changedStates; + std::queue queue; + + storm::storage::SparseMatrix backwards = ma->getBackwardTransitions(); + + // Determine the state remapping + // TODO Consider state labels + for (uint_fast64_t base_state = 0; base_state < ma->getNumberOfStates(); ++base_state) { + STORM_LOG_ASSERT(!ma->isHybridState(base_state), "Base state is hybrid."); + if (ma->isMarkovianState(base_state)) { + queue.push(base_state); + + while (!queue.empty()) { + auto currState = queue.front(); + queue.pop(); + // Get predecessors from matrix + typename storm::storage::SparseMatrix::rows entriesInRow = backwards.getRow( + currState); + for (auto entryIt = entriesInRow.begin(), entryIte = entriesInRow.end(); + entryIt != entryIte; ++entryIt) { + uint_fast64_t predecessor = entryIt->getColumn(); + if (!ma->isMarkovianState(predecessor) && !statesToKeep.count(predecessor)) { + if (!eliminationMapping.count(predecessor)) { + eliminationMapping[predecessor] = base_state; + queue.push(predecessor); + } else if (eliminationMapping[predecessor] != base_state) { + eliminationMapping.erase(predecessor); + statesToKeep.insert(predecessor); + changedStates.push(predecessor); + } + } + } + } + } + } + + // Correct the mapping with the states which have to be kept + while (!changedStates.empty()) { + uint_fast64_t base_state = changedStates.front(); + queue.push(base_state); + + while (!queue.empty()) { + auto currState = queue.front(); + queue.pop(); + // Get predecessors from matrix + typename storm::storage::SparseMatrix::rows entriesInRow = backwards.getRow( + currState); + for (auto entryIt = entriesInRow.begin(), entryIte = entriesInRow.end(); + entryIt != entryIte; ++entryIt) { + uint_fast64_t predecessor = entryIt->getColumn(); + if (!ma->isMarkovianState(predecessor) && !statesToKeep.count(predecessor)) { + if (!eliminationMapping.count(predecessor)) { + eliminationMapping[predecessor] = base_state; + queue.push(predecessor); + } else if (eliminationMapping[predecessor] != base_state) { + eliminationMapping.erase(predecessor); + statesToKeep.insert(predecessor); + changedStates.push(predecessor); + } + } + } + } + + changedStates.pop(); + } + + // At ma point, we hopefully have a valid mapping which eliminates a lot of states + + /*STORM_PRINT("Elimination Mapping" << std::endl) + for (auto entry : eliminationMapping) { + STORM_PRINT(std::to_string(entry.first) << " -> " << std::to_string(entry.second) << std::endl) + }*/ + STORM_PRINT("Eliminating " << eliminationMapping.size() << " states" << std::endl) + + // TODO explore if one can construct elimination mapping and state remapping in one step + + // Construct a mapping of old state space to new one + std::vector stateRemapping(ma->getNumberOfStates(), -1); + uint_fast64_t currentNewState = 0; + for (uint_fast64_t state = 0; state < ma->getNumberOfStates(); ++state) { + if (eliminationMapping.count(state) > 0) { + if (stateRemapping[eliminationMapping[state]] == uint_fast64_t(-1)) { + stateRemapping[eliminationMapping[state]] = currentNewState; + stateRemapping[state] = currentNewState; + ++currentNewState; + queue.push(eliminationMapping[state]); + } else { + stateRemapping[state] = stateRemapping[eliminationMapping[state]]; + } + } else if (stateRemapping[state] == uint_fast64_t(-1)) { + stateRemapping[state] = currentNewState; + queue.push(state); + ++currentNewState; + } + } + + // Build the new MA + storm::storage::SparseMatrix newTransitionMatrix; + storm::models::sparse::StateLabeling newStateLabeling( + ma->getNumberOfStates() - eliminationMapping.size()); + storm::storage::BitVector newMarkovianStates(ma->getNumberOfStates() - eliminationMapping.size(), + false); + std::vector newExitRates; + //TODO choice labeling + boost::optional choiceLabeling; + + // Initialize the matrix builder and helper variables + storm::storage::SparseMatrixBuilder matrixBuilder = storm::storage::SparseMatrixBuilder( + 0, 0, 0, false, true, 0); + uint_fast64_t currentRow = 0; + uint_fast64_t state = 0; + while (!queue.empty()) { + state = queue.front(); + queue.pop(); + + for (auto const &label : ma->getLabelsOfState(state)) { + if (!newStateLabeling.containsLabel(label)) { + newStateLabeling.addLabel(label); + } + + newStateLabeling.addLabelToState(label, stateRemapping[state]); + } + + // Use a set to not include redundant rows + std::set> rowSet; + for (uint_fast64_t row = 0; row < ma->getTransitionMatrix().getRowGroupSize(state); ++row) { + std::map transitions; + for (typename storm::storage::SparseMatrix::const_iterator itEntry = ma->getTransitionMatrix().getRow( + state, row).begin(); + itEntry != ma->getTransitionMatrix().getRow(state, row).end(); ++itEntry) { + uint_fast64_t newId = stateRemapping[itEntry->getColumn()]; + if (transitions.count(newId) == 0) { + transitions[newId] = itEntry->getValue(); + } else { + transitions[newId] += itEntry->getValue(); + } + } + rowSet.insert(transitions); + } + + // correctly set rates + auto rate = storm::utility::zero(); + + if (ma->isMarkovianState(state)) { + newMarkovianStates.set(stateRemapping[state], true); + rate = ma->getExitRates().at(state); + } + + newExitRates.push_back(rate); + // Build matrix + matrixBuilder.newRowGroup(currentRow); + for (auto const &row : rowSet) { + for (auto const &transition : row) { + matrixBuilder.addNextValue(currentRow, transition.first, transition.second); + //STORM_PRINT(stateRemapping[state] << "->" << transition.first << " : " << transition.second << std::endl) + } + ++currentRow; + } + } + newTransitionMatrix = matrixBuilder.build(); + + storm::storage::sparse::ModelComponents newComponents = storm::storage::sparse::ModelComponents( + std::move(newTransitionMatrix), std::move(newStateLabeling)); + + newComponents.rateTransitions = false; + newComponents.markovianStates = std::move(newMarkovianStates); + newComponents.exitRates = std::move(newExitRates); + auto model = std::make_shared>( + std::move(newComponents)); + if (model->isConvertibleToCtmc()) { + return model->convertToCtmc(); + } else { + return model; + } + } + + + template + class NonMarkovianChainTransformer; + +#ifdef STORM_HAVE_CARL + + template + class NonMarkovianChainTransformer; + +#endif + } +} + diff --git a/src/storm/transformer/NonMarkovianChainTransformer.h b/src/storm/transformer/NonMarkovianChainTransformer.h new file mode 100644 index 000000000..ae6759a94 --- /dev/null +++ b/src/storm/transformer/NonMarkovianChainTransformer.h @@ -0,0 +1,27 @@ +#include "storm/models/sparse/MarkovAutomaton.h" + +namespace storm { + namespace transformer { + /** + * Transformer for eliminating chains of non-Markovian states (instantaneous path fragment leading to the same outcome) from Markov Automata + */ + template> + class NonMarkovianChainTransformer { + public: + /** + * Generates a model with the same basic behavior as the input, but eliminates non-Markovian chains. + * If no non-determinism occurs, a CTMC is generated. + * + * @param ma the input Markov Automaton + * @param preserveLabels if set, the procedure considers the labels of non-Markovian states when eliminating states + * @return a reference to the new Mmodel after eliminating non-Markovian states + */ + static std::shared_ptr< + models::sparse::Model < ValueType, RewardModelType>> eliminateNonmarkovianStates(std::shared_ptr< + models::sparse::MarkovAutomaton < ValueType, RewardModelType>> ma, + bool preserveLabels = true + ); + }; + } +} + From 9c74bbed248305c96f795bd7cce488fef070020f Mon Sep 17 00:00:00 2001 From: Alexander Bork Date: Wed, 10 Jul 2019 14:34:53 +0200 Subject: [PATCH 30/47] Decoupled FDEP conflict search and SMT solver --- src/storm-dft/api/storm-dft.cpp | 4 +- src/storm-dft/api/storm-dft.h | 2 +- .../modelchecker/dft/DFTASFChecker.cpp | 46 ------- .../modelchecker/dft/DFTASFChecker.h | 7 -- src/storm-dft/utility/FDEPConflictFinder.cpp | 112 ++++++++++++++++++ src/storm-dft/utility/FDEPConflictFinder.h | 29 +++++ src/test/storm-dft/api/DftSmtTest.cpp | 16 +-- 7 files changed, 149 insertions(+), 67 deletions(-) create mode 100644 src/storm-dft/utility/FDEPConflictFinder.cpp create mode 100644 src/storm-dft/utility/FDEPConflictFinder.h diff --git a/src/storm-dft/api/storm-dft.cpp b/src/storm-dft/api/storm-dft.cpp index 2cb36853f..d74120402 100644 --- a/src/storm-dft/api/storm-dft.cpp +++ b/src/storm-dft/api/storm-dft.cpp @@ -4,6 +4,7 @@ #include "storm-dft/settings/modules/DftGspnSettings.h" #include "storm-conv/settings/modules/JaniExportSettings.h" #include "storm-conv/api/storm-conv.h" +#include "storm-dft/utility/FDEPConflictFinder.h" namespace storm { namespace api { @@ -67,7 +68,8 @@ namespace storm { "Upper bound: " << std::to_string(results.upperBEBound) << std::endl) } - results.fdepConflicts = smtChecker.getDependencyConflicts(solverTimeout); + results.fdepConflicts = storm::dft::utility::FDEPConflictFinder::getDependencyConflicts(dft, true, + solverTimeout); if (printOutput) { STORM_PRINT("========================================" << std::endl << diff --git a/src/storm-dft/api/storm-dft.h b/src/storm-dft/api/storm-dft.h index 2af6a43e1..17bc94a15 100644 --- a/src/storm-dft/api/storm-dft.h +++ b/src/storm-dft/api/storm-dft.h @@ -13,7 +13,7 @@ namespace storm { namespace api { - struct SMTResult { + struct PreprocessingResult { uint64_t lowerBEBound; uint64_t upperBEBound; std::vector> fdepConflicts; diff --git a/src/storm-dft/modelchecker/dft/DFTASFChecker.cpp b/src/storm-dft/modelchecker/dft/DFTASFChecker.cpp index c30b365a1..6fc9ef1ae 100644 --- a/src/storm-dft/modelchecker/dft/DFTASFChecker.cpp +++ b/src/storm-dft/modelchecker/dft/DFTASFChecker.cpp @@ -939,51 +939,5 @@ namespace storm { return bound; } - std::vector> DFTASFChecker::getDependencyConflicts(uint_fast64_t timeout) { - STORM_LOG_ASSERT(solver, "SMT Solver was not initialized, call toSolver() before checking queries"); - std::vector> res; - uint64_t dep1Index; - uint64_t dep2Index; - for (size_t i = 0; i < dft.getDependencies().size(); ++i) { - dep1Index = dft.getDependencies().at(i); - for (size_t j = i + 1; j < dft.getDependencies().size(); ++j) { - dep2Index = dft.getDependencies().at(j); - if (dft.getDynamicBehavior()[dep1Index] && dft.getDynamicBehavior()[dep2Index]) { - if (dft.getDependency(dep1Index)->triggerEvent() == - dft.getDependency(dep2Index)->triggerEvent()) { - STORM_LOG_DEBUG("Conflict between " << dft.getElement(dep1Index)->name() << " and " - << dft.getElement(dep2Index)->name() - << ": Same trigger"); - res.emplace_back(std::pair(dep1Index, dep2Index)); - } else { - switch (checkDependencyConflict(dep1Index, dep2Index, timeout)) { - case storm::solver::SmtSolver::CheckResult::Sat: - STORM_LOG_DEBUG("Conflict between " << dft.getElement(dep1Index)->name() << " and " - << dft.getElement(dep2Index)->name()); - res.emplace_back(std::pair(dep1Index, dep2Index)); - break; - case storm::solver::SmtSolver::CheckResult::Unknown: - STORM_LOG_DEBUG( - "Unknown: Conflict between " << dft.getElement(dep1Index)->name() << " and " - << dft.getElement(dep2Index)->name()); - res.emplace_back(std::pair(dep1Index, dep2Index)); - break; - default: - STORM_LOG_DEBUG( - "No conflict between " << dft.getElement(dep1Index)->name() << " and " - << dft.getElement(dep2Index)->name()); - break; - } - } - } else { - STORM_LOG_DEBUG( - "Static behavior: No conflict between " << dft.getElement(dep1Index)->name() << " and " - << dft.getElement(dep2Index)->name()); - break; - } - } - } - return res; - } } } diff --git a/src/storm-dft/modelchecker/dft/DFTASFChecker.h b/src/storm-dft/modelchecker/dft/DFTASFChecker.h index 73f15a530..3cff2a6a5 100644 --- a/src/storm-dft/modelchecker/dft/DFTASFChecker.h +++ b/src/storm-dft/modelchecker/dft/DFTASFChecker.h @@ -116,13 +116,6 @@ namespace storm { */ uint64_t getAlwaysFailedBound(uint_fast64_t timeout = 10); - /** - * Get a vector of index pairs of FDEPs which are conflicting according to a conservative definition - * - * @param timeout timeout for each query in seconds, defaults to 10 seconds - * @return a vector of pairs of FDEP indices which are conflicting - */ - std::vector> getDependencyConflicts(uint_fast64_t timeout = 10); /** * Set the timeout of the solver diff --git a/src/storm-dft/utility/FDEPConflictFinder.cpp b/src/storm-dft/utility/FDEPConflictFinder.cpp new file mode 100644 index 000000000..fb214bcce --- /dev/null +++ b/src/storm-dft/utility/FDEPConflictFinder.cpp @@ -0,0 +1,112 @@ +#include "FDEPConflictFinder.h" + +namespace storm { + namespace dft { + namespace utility { + + std::vector> + FDEPConflictFinder::getDependencyConflicts(storm::storage::DFT const &dft, + bool useSMT, + uint_fast64_t timeout) { + + std::shared_ptr smtChecker = nullptr; + if (useSMT) { + storm::modelchecker::DFTASFChecker checker(dft); + smtChecker = std::make_shared(checker); + smtChecker->activateExperimentalMode(); + smtChecker->toSolver(); + } + + std::vector> res; + uint64_t dep1Index; + uint64_t dep2Index; + for (size_t i = 0; i < dft.getDependencies().size(); ++i) { + dep1Index = dft.getDependencies().at(i); + for (size_t j = i + 1; j < dft.getDependencies().size(); ++j) { + dep2Index = dft.getDependencies().at(j); + if (dft.getDynamicBehavior()[dep1Index] && dft.getDynamicBehavior()[dep2Index]) { + if (useSMT) { // if an SMT solver is to be used + if (dft.getDependency(dep1Index)->triggerEvent() == + dft.getDependency(dep2Index)->triggerEvent()) { + STORM_LOG_DEBUG("Conflict between " << dft.getElement(dep1Index)->name() << " and " + << dft.getElement(dep2Index)->name() + << ": Same trigger"); + res.emplace_back(std::pair(dep1Index, dep2Index)); + } else { + switch (smtChecker->checkDependencyConflict(dep1Index, dep2Index, timeout)) { + case storm::solver::SmtSolver::CheckResult::Sat: + STORM_LOG_DEBUG( + "Conflict between " << dft.getElement(dep1Index)->name() << " and " + << dft.getElement(dep2Index)->name()); + res.emplace_back(std::pair(dep1Index, dep2Index)); + break; + case storm::solver::SmtSolver::CheckResult::Unknown: + STORM_LOG_DEBUG( + "Unknown: Conflict between " << dft.getElement(dep1Index)->name() + << " and " + << dft.getElement(dep2Index)->name()); + res.emplace_back(std::pair(dep1Index, dep2Index)); + break; + default: + STORM_LOG_DEBUG( + "No conflict between " << dft.getElement(dep1Index)->name() + << " and " + << dft.getElement(dep2Index)->name()); + break; + } + } + } else { + STORM_LOG_DEBUG( + "Conflict between " << dft.getElement(dep1Index)->name() << " and " + << dft.getElement(dep2Index)->name()); + res.emplace_back(std::pair(dep1Index, dep2Index)); + } + } else { + STORM_LOG_DEBUG( + "Static behavior: No conflict between " << dft.getElement(dep1Index)->name() + << " and " + << dft.getElement(dep2Index)->name()); + break; + } + } + } + return res; + } + + std::vector> + FDEPConflictFinder::getDependencyConflicts(storm::storage::DFT const &dft, + bool useSMT, + uint_fast64_t timeout) { + if (useSMT) { + STORM_LOG_WARN("SMT encoding for rational functions is not supported"); + } + + std::vector> res; + uint64_t dep1Index; + uint64_t dep2Index; + for (size_t i = 0; i < dft.getDependencies().size(); ++i) { + dep1Index = dft.getDependencies().at(i); + for (size_t j = i + 1; j < dft.getDependencies().size(); ++j) { + dep2Index = dft.getDependencies().at(j); + if (dft.getDynamicBehavior()[dep1Index] && dft.getDynamicBehavior()[dep2Index]) { + STORM_LOG_DEBUG( + "Conflict between " << dft.getElement(dep1Index)->name() << " and " + << dft.getElement(dep2Index)->name()); + res.emplace_back(std::pair(dep1Index, dep2Index)); + } else { + STORM_LOG_DEBUG( + "Static behavior: No conflict between " << dft.getElement(dep1Index)->name() + << " and " + << dft.getElement(dep2Index)->name()); + break; + } + } + } + return res; + } + + class FDEPConflictFinder; + + } + } +} \ No newline at end of file diff --git a/src/storm-dft/utility/FDEPConflictFinder.h b/src/storm-dft/utility/FDEPConflictFinder.h new file mode 100644 index 000000000..831bfd248 --- /dev/null +++ b/src/storm-dft/utility/FDEPConflictFinder.h @@ -0,0 +1,29 @@ +#include +#include "storm-dft/storage/dft/DFT.h" +#include "storm-dft/modelchecker/dft/DFTASFChecker.h" + +namespace storm { + namespace dft { + namespace utility { + class FDEPConflictFinder { + public: + /** + * Get a vector of index pairs of FDEPs in the DFT which are conflicting. Two FDEPs are conflicting if + * their simultaneous triggering may cause unresolvable non-deterministic behavior. + * + * @param dft the DFT + * @param useSMT if set, an SMT solver is used to refine the conflict set + * @param timeout timeout for each query in seconds, defaults to 10 seconds + * @return a vector of pairs of indices. The indices in a pair refer to FDEPs which are conflicting + */ + static std::vector> + getDependencyConflicts(storm::storage::DFT const &dft, + bool useSMT = false, uint_fast64_t timeout = 10); + + static std::vector> + getDependencyConflicts(storm::storage::DFT const &dft, + bool useSMT = false, uint_fast64_t timeout = 10); + }; + } + } +} diff --git a/src/test/storm-dft/api/DftSmtTest.cpp b/src/test/storm-dft/api/DftSmtTest.cpp index a7d5f0993..5a7b31095 100644 --- a/src/test/storm-dft/api/DftSmtTest.cpp +++ b/src/test/storm-dft/api/DftSmtTest.cpp @@ -1,3 +1,4 @@ +#include #include "gtest/gtest.h" #include "storm-config.h" @@ -65,11 +66,8 @@ namespace { dft->setDynamicBehaviorInfo(); EXPECT_EQ(dft->getDynamicBehavior(), true_vector); - storm::modelchecker::DFTASFChecker smtChecker(*dft); - smtChecker.convert(); - smtChecker.toSolver(); - EXPECT_TRUE(smtChecker.getDependencyConflicts().empty()); + EXPECT_TRUE(storm::dft::utility::FDEPConflictFinder::getDependencyConflicts(*dft, true).empty()); } TEST(DftSmtTest, FDEPConflictSPARETest) { @@ -80,11 +78,8 @@ namespace { dft->setDynamicBehaviorInfo(); EXPECT_EQ(dft->getDynamicBehavior(), true_vector); - storm::modelchecker::DFTASFChecker smtChecker(*dft); - smtChecker.convert(); - smtChecker.toSolver(); - EXPECT_TRUE(smtChecker.getDependencyConflicts().empty()); + EXPECT_TRUE(storm::dft::utility::FDEPConflictFinder::getDependencyConflicts(*dft, true).empty()); } TEST(DftSmtTest, FDEPConflictSEQTest) { @@ -96,10 +91,7 @@ namespace { dft->setDynamicBehaviorInfo(); EXPECT_EQ(dft->getDynamicBehavior(), expected_dynamic_vector); - storm::modelchecker::DFTASFChecker smtChecker(*dft); - smtChecker.convert(); - smtChecker.toSolver(); - EXPECT_EQ(smtChecker.getDependencyConflicts().size(), uint64_t(3)); + EXPECT_EQ(storm::dft::utility::FDEPConflictFinder::getDependencyConflicts(*dft, true).size(), uint64_t(3)); } } \ No newline at end of file From 75d28060cc0c831ebfc02178f909b25c75d51241 Mon Sep 17 00:00:00 2001 From: Alexander Bork Date: Wed, 10 Jul 2019 16:34:18 +0200 Subject: [PATCH 31/47] Moved failure bound computation to decouple it from the SMT checker --- src/storm-dft/api/storm-dft.cpp | 13 +- src/storm-dft/api/storm-dft.h | 4 +- .../modelchecker/dft/DFTASFChecker.cpp | 185 -------------- .../modelchecker/dft/DFTASFChecker.h | 50 +--- src/storm-dft/utility/FailureBoundFinder.cpp | 241 ++++++++++++++++++ src/storm-dft/utility/FailureBoundFinder.h | 73 ++++++ src/test/storm-dft/api/DftSmtTest.cpp | 11 +- 7 files changed, 340 insertions(+), 237 deletions(-) create mode 100644 src/storm-dft/utility/FailureBoundFinder.cpp create mode 100644 src/storm-dft/utility/FailureBoundFinder.h diff --git a/src/storm-dft/api/storm-dft.cpp b/src/storm-dft/api/storm-dft.cpp index d74120402..8d2c3ce2a 100644 --- a/src/storm-dft/api/storm-dft.cpp +++ b/src/storm-dft/api/storm-dft.cpp @@ -4,7 +4,6 @@ #include "storm-dft/settings/modules/DftGspnSettings.h" #include "storm-conv/settings/modules/JaniExportSettings.h" #include "storm-conv/api/storm-conv.h" -#include "storm-dft/utility/FDEPConflictFinder.h" namespace storm { namespace api { @@ -48,7 +47,7 @@ namespace storm { } template<> - storm::api::SMTResult + storm::api::PreprocessingResult analyzeDFTSMT(storm::storage::DFT const &dft, bool printOutput, bool experimentalMode) { uint64_t solverTimeout = 10; @@ -57,10 +56,12 @@ namespace storm { smtChecker.activateExperimentalMode(); } smtChecker.toSolver(); - storm::api::SMTResult results; + storm::api::PreprocessingResult results; - results.lowerBEBound = smtChecker.getLeastFailureBound(solverTimeout); - results.upperBEBound = smtChecker.getAlwaysFailedBound(solverTimeout); + results.lowerBEBound = storm::dft::utility::FailureBoundFinder::getLeastFailureBound(dft, true, + solverTimeout); + results.upperBEBound = storm::dft::utility::FailureBoundFinder::getAlwaysFailedBound(dft, true, + solverTimeout); if (printOutput) { STORM_PRINT("BE FAILURE BOUNDS" << std::endl << "========================================" << std::endl << @@ -85,7 +86,7 @@ namespace storm { } template<> - storm::api::SMTResult + storm::api::PreprocessingResult analyzeDFTSMT(storm::storage::DFT const &dft, bool printOutput, bool experimentalMode) { STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, diff --git a/src/storm-dft/api/storm-dft.h b/src/storm-dft/api/storm-dft.h index 17bc94a15..70d21f854 100644 --- a/src/storm-dft/api/storm-dft.h +++ b/src/storm-dft/api/storm-dft.h @@ -8,6 +8,8 @@ #include "storm-dft/modelchecker/dft/DFTModelChecker.h" #include "storm-dft/modelchecker/dft/DFTASFChecker.h" #include "storm-dft/transformations/DftToGspnTransformator.h" +#include "storm-dft/utility/FDEPConflictFinder.h" +#include "storm-dft/utility/FailureBoundFinder.h" #include "storm-gspn/api/storm-gspn.h" @@ -106,7 +108,7 @@ namespace storm { * @return Result result vector */ template - storm::api::SMTResult + storm::api::PreprocessingResult analyzeDFTSMT(storm::storage::DFT const &dft, bool printOutput, bool experimentalMode); /*! diff --git a/src/storm-dft/modelchecker/dft/DFTASFChecker.cpp b/src/storm-dft/modelchecker/dft/DFTASFChecker.cpp index 6fc9ef1ae..ff9e4f53c 100644 --- a/src/storm-dft/modelchecker/dft/DFTASFChecker.cpp +++ b/src/storm-dft/modelchecker/dft/DFTASFChecker.cpp @@ -754,190 +754,5 @@ namespace storm { return res; } - uint64_t DFTASFChecker::correctLowerBound(uint64_t bound, uint_fast64_t timeout) { - STORM_LOG_ASSERT(solver, "SMT Solver was not initialized, call toSolver() before checking queries"); - if (experimentalMode) - STORM_LOG_WARN("DFT-SMT-Checker now runs in experimental mode, bound correction is prone to errors!"); - STORM_LOG_DEBUG("Lower bound correction - try to correct bound " << std::to_string(bound)); - uint64_t boundCandidate = bound; - uint64_t nrDepEvents = 0; - uint64_t nrNonMarkovian = 0; - // Count dependent events - for (size_t i = 0; i < dft.nrElements(); ++i) { - std::shared_ptr const> element = dft.getElement(i); - if (element->type() == storm::storage::DFTElementType::BE_EXP || - element->type() == storm::storage::DFTElementType::BE_CONST) { - auto be = std::static_pointer_cast const>(element); - if (be->hasIngoingDependencies()) { - ++nrDepEvents; - } - } - } - - // Only need to check as long as bound candidate + nr of non-Markovians to check is smaller than number of dependent events - while (nrNonMarkovian <= nrDepEvents && boundCandidate >= 0) { - if (nrNonMarkovian == 0 and boundCandidate == 0) { - nrNonMarkovian = 1; - } - STORM_LOG_TRACE( - "Lower bound correction - check possible bound " << std::to_string(boundCandidate) << " with " - << std::to_string(nrNonMarkovian) - << " non-Markovian states"); - // The uniqueness transformation for constantly failed BEs guarantees that a DFT never fails - // in step 0 without intermediate non-Markovians, thus forcibly set nrNonMarkovian - setSolverTimeout(timeout * 1000); - storm::solver::SmtSolver::CheckResult tmp_res = - checkFailsLeqWithEqNonMarkovianState(boundCandidate + nrNonMarkovian, nrNonMarkovian); - unsetSolverTimeout(); - switch (tmp_res) { - case storm::solver::SmtSolver::CheckResult::Sat: - /* If SAT, there is a sequence where only boundCandidate-many BEs fail directly and rest is nonMarkovian. - * Bound candidate is vaild, therefore check the next one */ - STORM_LOG_TRACE("Lower bound correction - SAT"); - // Prevent integer underflow - if (boundCandidate == 0) { - STORM_LOG_DEBUG("Lower bound correction - corrected bound to 0"); - return 0; - } - --boundCandidate; - break; - case storm::solver::SmtSolver::CheckResult::Unknown: - // If any query returns unknown, we cannot be sure about the bound and fall back to the naive one - STORM_LOG_DEBUG("Lower bound correction - Solver returned 'Unknown', corrected to 1"); - return 1; - default: - // if query is UNSAT, increase number of non-Markovian states and try again - STORM_LOG_TRACE("Lower bound correction - UNSAT"); - ++nrNonMarkovian; - break; - } - } - // if for one candidate all queries are UNSAT, it is not valid. Return last valid candidate - STORM_LOG_DEBUG("Lower bound correction - corrected bound to " << std::to_string(boundCandidate + 1)); - return boundCandidate + 1; - } - - uint64_t DFTASFChecker::correctUpperBound(uint64_t bound, uint_fast64_t timeout) { - STORM_LOG_ASSERT(solver, "SMT Solver was not initialized, call toSolver() before checking queries"); - if (experimentalMode) - STORM_LOG_WARN("DFT-SMT-Checker now runs in experimental mode, bound correction is prone to errors!"); - STORM_LOG_DEBUG("Upper bound correction - try to correct bound " << std::to_string(bound)); - uint64_t boundCandidate = bound; - uint64_t nrDepEvents = 0; - uint64_t nrNonMarkovian = 0; - uint64_t currentTimepoint = 0; - // Count dependent events - for (size_t i = 0; i < dft.nrElements(); ++i) { - std::shared_ptr const> element = dft.getElement(i); - if (element->type() == storm::storage::DFTElementType::BE_EXP || - element->type() == storm::storage::DFTElementType::BE_CONST) { - auto be = std::static_pointer_cast const>(element); - if (be->hasIngoingDependencies()) { - ++nrDepEvents; - } - } - } - while (boundCandidate >= 0) { - currentTimepoint = bound + 1; - while (currentTimepoint - boundCandidate > 0) { - --currentTimepoint; - nrNonMarkovian = currentTimepoint - boundCandidate; - STORM_LOG_TRACE( - "Upper bound correction - candidate " << std::to_string(boundCandidate) << " check split " - << - std::to_string(currentTimepoint) << "|" - << std::to_string(nrNonMarkovian)); - setSolverTimeout(timeout * 1000); - storm::solver::SmtSolver::CheckResult tmp_res = - checkFailsAtTimepointWithEqNonMarkovianState(currentTimepoint, nrNonMarkovian); - unsetSolverTimeout(); - switch (tmp_res) { - case storm::solver::SmtSolver::CheckResult::Sat: - STORM_LOG_TRACE("Upper bound correction - SAT"); - STORM_LOG_DEBUG("Upper bound correction - corrected to bound " << boundCandidate << - " (TLE can fail at sequence point " - << std::to_string( - currentTimepoint) - << " with " - << std::to_string( - nrNonMarkovian) - << " non-Markovian states)"); - return boundCandidate; - case storm::solver::SmtSolver::CheckResult::Unknown: - // If any query returns unknown, we cannot be sure about the bound and fall back to the naive one - STORM_LOG_DEBUG( - "Upper bound correction - Solver returned 'Unknown', corrected to bound " << bound); - return bound; - default: - // if query is UNSAT, increase number of non-Markovian states and try again - STORM_LOG_TRACE("Lower bound correction - UNSAT"); - break; - } - } - --boundCandidate; - } - - // if for one candidate all queries are UNSAT, it is not valid. Return last valid candidate - STORM_LOG_DEBUG("Upper bound correction - corrected bound to " << std::to_string(boundCandidate)); - return boundCandidate; - } - - uint64_t DFTASFChecker::getLeastFailureBound(uint_fast64_t timeout) { - STORM_LOG_TRACE("Compute lower bound for number of BE failures necessary for the DFT to fail"); - STORM_LOG_ASSERT(solver, "SMT Solver was not initialized, call toSolver() before checking queries"); - uint64_t bound = 0; - while (bound < notFailed) { - setSolverTimeout(timeout * 1000); - storm::solver::SmtSolver::CheckResult tmp_res = checkTleFailsWithLeq(bound); - unsetSolverTimeout(); - switch (tmp_res) { - case storm::solver::SmtSolver::CheckResult::Sat: - if (!dft.getDependencies().empty()) { - return correctLowerBound(bound, timeout); - } else { - return bound; - } - case storm::solver::SmtSolver::CheckResult::Unknown: - STORM_LOG_DEBUG("Lower bound: Solver returned 'Unknown'"); - return bound; - default: - ++bound; - break; - } - - } - - return bound; - } - - uint64_t DFTASFChecker::getAlwaysFailedBound(uint_fast64_t timeout) { - STORM_LOG_TRACE("Compute bound for number of BE failures such that the DFT always fails"); - STORM_LOG_ASSERT(solver, "SMT Solver was not initialized, call toSolver() before checking queries"); - if (checkTleNeverFailed() == storm::solver::SmtSolver::CheckResult::Sat) { - return notFailed; - } - uint64_t bound = notFailed - 1; - while (bound >= 0) { - setSolverTimeout(timeout * 1000); - storm::solver::SmtSolver::CheckResult tmp_res = checkTleFailsWithEq(bound); - unsetSolverTimeout(); - switch (tmp_res) { - case storm::solver::SmtSolver::CheckResult::Sat: - if (!dft.getDependencies().empty()) { - return correctUpperBound(bound, timeout); - } else { - return bound; - } - case storm::solver::SmtSolver::CheckResult::Unknown: - STORM_LOG_DEBUG("Upper bound: Solver returned 'Unknown'"); - return bound; - default: - --bound; - break; - } - } - return bound; - } - } } diff --git a/src/storm-dft/modelchecker/dft/DFTASFChecker.h b/src/storm-dft/modelchecker/dft/DFTASFChecker.h index 3cff2a6a5..61da80af9 100644 --- a/src/storm-dft/modelchecker/dft/DFTASFChecker.h +++ b/src/storm-dft/modelchecker/dft/DFTASFChecker.h @@ -99,23 +99,6 @@ namespace storm { storm::solver::SmtSolver::CheckResult checkDependencyConflict(uint64_t dep1Index, uint64_t dep2Index, uint64_t timeout = 10); - /** - * Get the minimal number of BEs necessary for the TLE to fail (lower bound for number of failures to check) - * - * @param timeout timeout for each query in seconds, defaults to 10 seconds - * @return the minimal number - */ - uint64_t getLeastFailureBound(uint_fast64_t timeout = 10); - - /** - * Get the number of BE failures for which the TLE always fails (upper bound for number of failures to check). - * Note that the returned value may be higher than the real one when dependencies are present. - * - * @param timeout timeout for each query in seconds, defaults to 10 seconds - * @return the number - */ - uint64_t getAlwaysFailedBound(uint_fast64_t timeout = 10); - /** * Set the timeout of the solver @@ -128,8 +111,14 @@ namespace storm { * Unset the timeout for the solver */ void unsetSolverTimeout(); - - private: + + /** + * Get a reference to the DFT + */ + storm::storage::DFT const &getDFT() { + return dft; + } + /** * Helper function to check if the TLE fails before or at a given timepoint while visiting exactly * a given number of non-Markovian states @@ -151,27 +140,8 @@ namespace storm { */ storm::solver::SmtSolver::CheckResult checkFailsAtTimepointWithEqNonMarkovianState(uint64_t timepoint, uint64_t nrNonMarkovian); - - /** - * Helper function for correction of least failure bound when dependencies are present. - * The main idea is to check if a later point of failure for the TLE than the pre-computed bound exists, but - * up until that point the number of non-Markovian states visited is so large, that less than the pre-computed bound BEs fail by themselves. - * The corrected bound is then (newTLEFailureTimepoint)-(nrNonMarkovianStatesVisited). This term is minimized. - * - * @param bound known lower bound to be corrected - * @param timeout timeout timeout for each query in seconds - * @return the corrected bound - */ - uint64_t correctLowerBound(uint64_t bound, uint_fast64_t timeout); - - /** - * Helper function for correction of bound for number of BEs such that the DFT always fails when dependencies are present - * - * @param bound known bound to be corrected - * @param timeout timeout timeout for each query in seconds - * @return the corrected bound - */ - uint64_t correctUpperBound(uint64_t bound, uint_fast64_t timeout); + + private: uint64_t getClaimVariableIndex(uint64_t spareIndex, uint64_t childIndex) const; diff --git a/src/storm-dft/utility/FailureBoundFinder.cpp b/src/storm-dft/utility/FailureBoundFinder.cpp new file mode 100644 index 000000000..59243ad54 --- /dev/null +++ b/src/storm-dft/utility/FailureBoundFinder.cpp @@ -0,0 +1,241 @@ +#include "FailureBoundFinder.h" + +namespace storm { + namespace dft { + namespace utility { + uint64_t + FailureBoundFinder::correctLowerBound(std::shared_ptr smtchecker, + uint64_t bound, uint_fast64_t timeout) { + STORM_LOG_DEBUG("Lower bound correction - try to correct bound " << std::to_string(bound)); + uint64_t boundCandidate = bound; + uint64_t nrDepEvents = 0; + uint64_t nrNonMarkovian = 0; + auto dft = smtchecker->getDFT(); + + // Count dependent events + for (size_t i = 0; i < dft.nrElements(); ++i) { + std::shared_ptr const> element = dft.getElement(i); + if (element->type() == storm::storage::DFTElementType::BE_EXP || + element->type() == storm::storage::DFTElementType::BE_CONST) { + auto be = std::static_pointer_cast const>(element); + if (be->hasIngoingDependencies()) { + ++nrDepEvents; + } + } + } + + // Only need to check as long as bound candidate + nr of non-Markovians to check is smaller than number of dependent events + while (nrNonMarkovian <= nrDepEvents && boundCandidate >= 0) { + if (nrNonMarkovian == 0 and boundCandidate == 0) { + nrNonMarkovian = 1; + } + STORM_LOG_TRACE( + "Lower bound correction - check possible bound " << std::to_string(boundCandidate) + << " with " + << std::to_string(nrNonMarkovian) + << " non-Markovian states"); + // The uniqueness transformation for constantly failed BEs guarantees that a DFT never fails + // in step 0 without intermediate non-Markovians, thus forcibly set nrNonMarkovian + smtchecker->setSolverTimeout(timeout * 1000); + storm::solver::SmtSolver::CheckResult tmp_res = + smtchecker->checkFailsLeqWithEqNonMarkovianState(boundCandidate + nrNonMarkovian, + nrNonMarkovian); + smtchecker->unsetSolverTimeout(); + switch (tmp_res) { + case storm::solver::SmtSolver::CheckResult::Sat: + /* If SAT, there is a sequence where only boundCandidate-many BEs fail directly and rest is nonMarkovian. + * Bound candidate is vaild, therefore check the next one */ + STORM_LOG_TRACE("Lower bound correction - SAT"); + // Prevent integer underflow + if (boundCandidate == 0) { + STORM_LOG_DEBUG("Lower bound correction - corrected bound to 0"); + return 0; + } + --boundCandidate; + break; + case storm::solver::SmtSolver::CheckResult::Unknown: + // If any query returns unknown, we cannot be sure about the bound and fall back to the naive one + STORM_LOG_DEBUG("Lower bound correction - Solver returned 'Unknown', corrected to 1"); + return 1; + default: + // if query is UNSAT, increase number of non-Markovian states and try again + STORM_LOG_TRACE("Lower bound correction - UNSAT"); + ++nrNonMarkovian; + break; + } + } + // if for one candidate all queries are UNSAT, it is not valid. Return last valid candidate + STORM_LOG_DEBUG("Lower bound correction - corrected bound to " << std::to_string(boundCandidate + 1)); + return boundCandidate + 1; + } + + uint64_t + FailureBoundFinder::correctUpperBound(std::shared_ptr smtchecker, + uint64_t bound, uint_fast64_t timeout) { + STORM_LOG_DEBUG("Upper bound correction - try to correct bound " << std::to_string(bound)); + uint64_t boundCandidate = bound; + uint64_t nrDepEvents = 0; + uint64_t nrNonMarkovian = 0; + uint64_t currentTimepoint = 0; + auto dft = smtchecker->getDFT(); + // Count dependent events + for (size_t i = 0; i < dft.nrElements(); ++i) { + std::shared_ptr const> element = dft.getElement(i); + if (element->type() == storm::storage::DFTElementType::BE_EXP || + element->type() == storm::storage::DFTElementType::BE_CONST) { + auto be = std::static_pointer_cast const>(element); + if (be->hasIngoingDependencies()) { + ++nrDepEvents; + } + } + } + while (boundCandidate >= 0) { + currentTimepoint = bound + 1; + while (currentTimepoint - boundCandidate > 0) { + --currentTimepoint; + nrNonMarkovian = currentTimepoint - boundCandidate; + STORM_LOG_TRACE( + "Upper bound correction - candidate " << std::to_string(boundCandidate) + << " check split " + << + std::to_string(currentTimepoint) << "|" + << std::to_string(nrNonMarkovian)); + smtchecker->setSolverTimeout(timeout * 1000); + storm::solver::SmtSolver::CheckResult tmp_res = + smtchecker->checkFailsAtTimepointWithEqNonMarkovianState(currentTimepoint, + nrNonMarkovian); + smtchecker->unsetSolverTimeout(); + switch (tmp_res) { + case storm::solver::SmtSolver::CheckResult::Sat: + STORM_LOG_TRACE("Upper bound correction - SAT"); + STORM_LOG_DEBUG("Upper bound correction - corrected to bound " << boundCandidate << + " (TLE can fail at sequence point " + << std::to_string( + currentTimepoint) + << " with " + << std::to_string( + nrNonMarkovian) + << " non-Markovian states)"); + return boundCandidate; + case storm::solver::SmtSolver::CheckResult::Unknown: + // If any query returns unknown, we cannot be sure about the bound and fall back to the naive one + STORM_LOG_DEBUG( + "Upper bound correction - Solver returned 'Unknown', corrected to bound " + << bound); + return bound; + default: + // if query is UNSAT, increase number of non-Markovian states and try again + STORM_LOG_TRACE("Lower bound correction - UNSAT"); + break; + } + } + --boundCandidate; + } + + // if for one candidate all queries are UNSAT, it is not valid. Return last valid candidate + STORM_LOG_DEBUG("Upper bound correction - corrected bound to " << std::to_string(boundCandidate)); + return boundCandidate; + } + + uint64_t FailureBoundFinder::getLeastFailureBound(storm::storage::DFT const &dft, + bool useSMT, uint_fast64_t timeout) { + if (useSMT) { + STORM_LOG_TRACE("Compute lower bound for number of BE failures necessary for the DFT to fail"); + + storm::modelchecker::DFTASFChecker smtchecker(dft); + smtchecker.activateExperimentalMode(); + smtchecker.toSolver(); + + uint64_t bound = 0; + while (bound < dft.nrBasicElements() + 1) { + smtchecker.setSolverTimeout(timeout * 1000); + storm::solver::SmtSolver::CheckResult tmp_res = smtchecker.checkTleFailsWithLeq(bound); + smtchecker.unsetSolverTimeout(); + switch (tmp_res) { + case storm::solver::SmtSolver::CheckResult::Sat: + if (!dft.getDependencies().empty()) { + return correctLowerBound( + std::make_shared(smtchecker), bound, + timeout); + } else { + return bound; + } + case storm::solver::SmtSolver::CheckResult::Unknown: + STORM_LOG_DEBUG("Lower bound: Solver returned 'Unknown'"); + return bound; + default: + ++bound; + break; + } + + } + + return bound; + } else { + // naive bound + return 1; + } + } + + uint64_t FailureBoundFinder::getLeastFailureBound(storm::storage::DFT const &dft, + bool useSMT, uint_fast64_t timeout) { + if (useSMT) { + STORM_LOG_WARN("SMT encoding does not support rational functions"); + } + return 1; + } + + uint64_t FailureBoundFinder::getAlwaysFailedBound(storm::storage::DFT const &dft, bool useSMT, + uint_fast64_t timeout) { + STORM_LOG_TRACE("Compute bound for number of BE failures such that the DFT always fails"); + if (useSMT) { + + storm::modelchecker::DFTASFChecker smtchecker(dft); + smtchecker.activateExperimentalMode(); + smtchecker.toSolver(); + + if (smtchecker.checkTleNeverFailed() == storm::solver::SmtSolver::CheckResult::Sat) { + return dft.nrBasicElements() + 1; + } + uint64_t bound = dft.nrBasicElements(); + while (bound >= 0) { + smtchecker.setSolverTimeout(timeout * 1000); + storm::solver::SmtSolver::CheckResult tmp_res = smtchecker.checkTleFailsWithEq(bound); + smtchecker.unsetSolverTimeout(); + switch (tmp_res) { + case storm::solver::SmtSolver::CheckResult::Sat: + if (!dft.getDependencies().empty()) { + return correctUpperBound( + std::make_shared(smtchecker), bound, + timeout); + } else { + return bound; + } + case storm::solver::SmtSolver::CheckResult::Unknown: + STORM_LOG_DEBUG("Upper bound: Solver returned 'Unknown'"); + return bound; + default: + --bound; + break; + } + } + return bound; + } else { + // naive bound + return dft.nrBasicElements() + 1; + } + } + + uint64_t + FailureBoundFinder::getAlwaysFailedBound(storm::storage::DFT const &dft, bool useSMT, + uint_fast64_t timeout) { + if (useSMT) { + STORM_LOG_WARN("SMT encoding does not support rational functions"); + } + return dft.nrBasicElements() + 1; + } + + class FailureBoundFinder; + } + } +} \ No newline at end of file diff --git a/src/storm-dft/utility/FailureBoundFinder.h b/src/storm-dft/utility/FailureBoundFinder.h new file mode 100644 index 000000000..f4186da98 --- /dev/null +++ b/src/storm-dft/utility/FailureBoundFinder.h @@ -0,0 +1,73 @@ +#include +#include "storm-dft/storage/dft/DFT.h" +#include "storm-dft/modelchecker/dft/DFTASFChecker.h" + +namespace storm { + namespace dft { + namespace utility { + class FailureBoundFinder { + public: + /** + * Get the minimal number of BEs necessary for the TLE to fail (lower bound for number of failures to check) + * + * @param dft the DFT to check + * @param useSMT if set, an SMT solver is used to improve the bounds + * @param timeout timeout for each query in seconds, defaults to 10 seconds + * @return the minimal number + */ + static uint64_t getLeastFailureBound(storm::storage::DFT const &dft, + bool useSMT = false, + uint_fast64_t timeout = 10); + + static uint64_t getLeastFailureBound(storm::storage::DFT const &dft, + bool useSMT = false, + uint_fast64_t timeout = 10); + + /** + * Get the number of BE failures for which the TLE always fails (upper bound for number of failures to check). + * + * @param dft the DFT to check + * @param useSMT if set, an SMT solver is used to improve the bounds + * @param timeout timeout for each query in seconds, defaults to 10 seconds + * @return the number + */ + static uint64_t getAlwaysFailedBound(storm::storage::DFT const &dft, + bool useSMT = false, + uint_fast64_t timeout = 10); + + static uint64_t getAlwaysFailedBound(storm::storage::DFT const &dft, + bool useSMT = false, + uint_fast64_t timeout = 10); + + private: + /** + * Helper function for correction of least failure bound when dependencies are present. + * The main idea is to check if a later point of failure for the TLE than the pre-computed bound exists, but + * up until that point the number of non-Markovian states visited is so large, that less than the pre-computed bound BEs fail by themselves. + * The corrected bound is then (newTLEFailureTimepoint)-(nrNonMarkovianStatesVisited). This term is minimized. + * + * @param smtchecker the SMT checker to use + * @param bound known lower bound to be corrected + * @param timeout timeout timeout for each query in seconds + * @return the corrected bound + */ + static uint64_t + correctLowerBound(std::shared_ptr smtchecker, uint64_t bound, + uint_fast64_t timeout); + + /** + * Helper function for correction of bound for number of BEs such that the DFT always fails when dependencies are present + * + * @param smtchecker the SMT checker to use + * @param bound known bound to be corrected + * @param timeout timeout timeout for each query in seconds + * @return the corrected bound + */ + static uint64_t + correctUpperBound(std::shared_ptr smtchecker, uint64_t bound, + uint_fast64_t timeout); + }; + } + } +} + diff --git a/src/test/storm-dft/api/DftSmtTest.cpp b/src/test/storm-dft/api/DftSmtTest.cpp index 5a7b31095..a572c2b23 100644 --- a/src/test/storm-dft/api/DftSmtTest.cpp +++ b/src/test/storm-dft/api/DftSmtTest.cpp @@ -1,4 +1,5 @@ -#include +#include "storm-dft/utility/FDEPConflictFinder.h" +#include "storm-dft/utility/FailureBoundFinder.h" #include "gtest/gtest.h" #include "storm-config.h" @@ -43,8 +44,8 @@ namespace { storm::modelchecker::DFTASFChecker smtChecker(*dft); smtChecker.convert(); smtChecker.toSolver(); - EXPECT_EQ(smtChecker.getLeastFailureBound(30), uint64_t(2)); - EXPECT_EQ(smtChecker.getAlwaysFailedBound(30), uint64_t(4)); + EXPECT_EQ(storm::dft::utility::FailureBoundFinder::getLeastFailureBound(*dft, true, 30), uint64_t(2)); + EXPECT_EQ(storm::dft::utility::FailureBoundFinder::getAlwaysFailedBound(*dft, true, 30), uint64_t(4)); } TEST(DftSmtTest, FDEPBoundTest) { @@ -54,8 +55,8 @@ namespace { storm::modelchecker::DFTASFChecker smtChecker(*dft); smtChecker.convert(); smtChecker.toSolver(); - EXPECT_EQ(smtChecker.getLeastFailureBound(30), uint64_t(1)); - EXPECT_EQ(smtChecker.getAlwaysFailedBound(30), uint64_t(5)); + EXPECT_EQ(storm::dft::utility::FailureBoundFinder::getLeastFailureBound(*dft, true, 30), uint64_t(1)); + EXPECT_EQ(storm::dft::utility::FailureBoundFinder::getAlwaysFailedBound(*dft, true, 30), uint64_t(5)); } TEST(DftSmtTest, FDEPConflictTest) { From 449c513db246b22915e0329d5be2cd5fa79c6dff Mon Sep 17 00:00:00 2001 From: Alexander Bork Date: Wed, 10 Jul 2019 17:03:03 +0200 Subject: [PATCH 32/47] Cleanup DFTASFChecker --- src/storm-dft-cli/storm-dft.cpp | 6 ++++-- src/storm-dft/api/storm-dft.cpp | 16 ++++------------ src/storm-dft/api/storm-dft.h | 4 ++-- src/storm-dft/modelchecker/dft/DFTASFChecker.cpp | 9 +-------- src/storm-dft/modelchecker/dft/DFTASFChecker.h | 7 ------- src/storm-dft/utility/FDEPConflictFinder.cpp | 1 - src/storm-dft/utility/FailureBoundFinder.cpp | 2 -- src/test/storm-dft/api/DftSmtTest.cpp | 2 -- 8 files changed, 11 insertions(+), 36 deletions(-) diff --git a/src/storm-dft-cli/storm-dft.cpp b/src/storm-dft-cli/storm-dft.cpp index 01271e2c5..7349e0704 100644 --- a/src/storm-dft-cli/storm-dft.cpp +++ b/src/storm-dft-cli/storm-dft.cpp @@ -86,7 +86,7 @@ void processOptions() { dft = dftTransformator.transformBinaryFDEPs(*dft); } // Export to smtlib2 - storm::api::exportDFTToSMT(*dft, dftIOSettings.getExportSmtFilename(), debug.isTestSet()); + storm::api::exportDFTToSMT(*dft, dftIOSettings.getExportSmtFilename()); return; } @@ -101,7 +101,7 @@ void processOptions() { STORM_LOG_DEBUG("Running DFT analysis with use of SMT"); // Set dynamic behavior vector dft->setDynamicBehaviorInfo(); - auto smtResults = storm::api::analyzeDFTSMT(*dft, true, debug.isTestSet()); + auto smtResults = storm::api::analyzeDFTSMT(*dft, true); // Set the conflict map of the dft std::set conflict_set; for (auto conflict : smtResults.fdepConflicts) { @@ -116,6 +116,8 @@ void processOptions() { } #endif + //Proprocessing + // From now on we analyse DFT via model checking // Set min or max diff --git a/src/storm-dft/api/storm-dft.cpp b/src/storm-dft/api/storm-dft.cpp index 8d2c3ce2a..dd9bd9dcd 100644 --- a/src/storm-dft/api/storm-dft.cpp +++ b/src/storm-dft/api/storm-dft.cpp @@ -31,30 +31,23 @@ namespace storm { } template<> - void exportDFTToSMT(storm::storage::DFT const &dft, std::string const &file, bool experimentalMode) { + void exportDFTToSMT(storm::storage::DFT const &dft, std::string const &file) { storm::modelchecker::DFTASFChecker asfChecker(dft); - if (experimentalMode) { - asfChecker.activateExperimentalMode(); - } asfChecker.convert(); asfChecker.toFile(file); } template<> - void exportDFTToSMT(storm::storage::DFT const &dft, std::string const &file, - bool experimentalMode) { + void exportDFTToSMT(storm::storage::DFT const &dft, std::string const &file) { STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Export to SMT does not support this data type."); } template<> storm::api::PreprocessingResult - analyzeDFTSMT(storm::storage::DFT const &dft, bool printOutput, bool experimentalMode) { + analyzeDFTSMT(storm::storage::DFT const &dft, bool printOutput) { uint64_t solverTimeout = 10; storm::modelchecker::DFTASFChecker smtChecker(dft); - if (experimentalMode) { - smtChecker.activateExperimentalMode(); - } smtChecker.toSolver(); storm::api::PreprocessingResult results; @@ -87,8 +80,7 @@ namespace storm { template<> storm::api::PreprocessingResult - analyzeDFTSMT(storm::storage::DFT const &dft, bool printOutput, - bool experimentalMode) { + analyzeDFTSMT(storm::storage::DFT const &dft, bool printOutput) { STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Analysis by SMT not supported for this data type."); } diff --git a/src/storm-dft/api/storm-dft.h b/src/storm-dft/api/storm-dft.h index 70d21f854..a3cb5a865 100644 --- a/src/storm-dft/api/storm-dft.h +++ b/src/storm-dft/api/storm-dft.h @@ -109,7 +109,7 @@ namespace storm { */ template storm::api::PreprocessingResult - analyzeDFTSMT(storm::storage::DFT const &dft, bool printOutput, bool experimentalMode); + analyzeDFTSMT(storm::storage::DFT const &dft, bool printOutput); /*! * Export DFT to JSON file. @@ -136,7 +136,7 @@ namespace storm { * @param file File. */ template - void exportDFTToSMT(storm::storage::DFT const &dft, std::string const &file, bool experimentalMode); + void exportDFTToSMT(storm::storage::DFT const &dft, std::string const &file); /*! * Transform DFT to GSPN. diff --git a/src/storm-dft/modelchecker/dft/DFTASFChecker.cpp b/src/storm-dft/modelchecker/dft/DFTASFChecker.cpp index ff9e4f53c..558e2a45c 100644 --- a/src/storm-dft/modelchecker/dft/DFTASFChecker.cpp +++ b/src/storm-dft/modelchecker/dft/DFTASFChecker.cpp @@ -18,11 +18,6 @@ namespace storm { // Intentionally left empty. } - void DFTASFChecker::activateExperimentalMode() { - STORM_LOG_WARN("DFT-SMT-Checker now runs in experimental mode, no guarantee for correct results is given!"); - experimentalMode = true; - } - uint64_t DFTASFChecker::getClaimVariableIndex(uint64_t spare, uint64_t child) const { return claimVariables.at(SpareAndChildPair(spare, child)); } @@ -44,9 +39,7 @@ namespace storm { beVariables.push_back(varNames.size() - 1); break; case storm::storage::DFTElementType::BE_CONST: { - STORM_LOG_THROW(experimentalMode, storm::exceptions::NotSupportedException, - "Constant BEs are not supported in SMT translation."); - STORM_LOG_WARN("Constant BEs are only experimentally supported"); + STORM_LOG_WARN("Constant BEs are only experimentally supported in the SMT encoding"); // Constant BEs are initially either failed or failsafe, treat them differently auto be = std::static_pointer_cast const>(element); if (be->failed()) { diff --git a/src/storm-dft/modelchecker/dft/DFTASFChecker.h b/src/storm-dft/modelchecker/dft/DFTASFChecker.h index 61da80af9..8a6e3fda2 100644 --- a/src/storm-dft/modelchecker/dft/DFTASFChecker.h +++ b/src/storm-dft/modelchecker/dft/DFTASFChecker.h @@ -45,12 +45,6 @@ namespace storm { public: DFTASFChecker(storm::storage::DFT const&); - /** - * Activates the experimental support for constant BEs and possibly other not thoroughly tested features - * - */ - void activateExperimentalMode(); - /** * Generate general variables and constraints for the DFT and store them in the corresponding maps and vectors * @@ -235,7 +229,6 @@ namespace storm { std::unordered_map markovianVariables; std::vector tmpTimePointVariables; uint64_t notFailed; - bool experimentalMode = false; }; } } diff --git a/src/storm-dft/utility/FDEPConflictFinder.cpp b/src/storm-dft/utility/FDEPConflictFinder.cpp index fb214bcce..53f63ae4b 100644 --- a/src/storm-dft/utility/FDEPConflictFinder.cpp +++ b/src/storm-dft/utility/FDEPConflictFinder.cpp @@ -13,7 +13,6 @@ namespace storm { if (useSMT) { storm::modelchecker::DFTASFChecker checker(dft); smtChecker = std::make_shared(checker); - smtChecker->activateExperimentalMode(); smtChecker->toSolver(); } diff --git a/src/storm-dft/utility/FailureBoundFinder.cpp b/src/storm-dft/utility/FailureBoundFinder.cpp index 59243ad54..d09caae83 100644 --- a/src/storm-dft/utility/FailureBoundFinder.cpp +++ b/src/storm-dft/utility/FailureBoundFinder.cpp @@ -143,7 +143,6 @@ namespace storm { STORM_LOG_TRACE("Compute lower bound for number of BE failures necessary for the DFT to fail"); storm::modelchecker::DFTASFChecker smtchecker(dft); - smtchecker.activateExperimentalMode(); smtchecker.toSolver(); uint64_t bound = 0; @@ -191,7 +190,6 @@ namespace storm { if (useSMT) { storm::modelchecker::DFTASFChecker smtchecker(dft); - smtchecker.activateExperimentalMode(); smtchecker.toSolver(); if (smtchecker.checkTleNeverFailed() == storm::solver::SmtSolver::CheckResult::Sat) { diff --git a/src/test/storm-dft/api/DftSmtTest.cpp b/src/test/storm-dft/api/DftSmtTest.cpp index a572c2b23..f23a433c9 100644 --- a/src/test/storm-dft/api/DftSmtTest.cpp +++ b/src/test/storm-dft/api/DftSmtTest.cpp @@ -1,5 +1,3 @@ -#include "storm-dft/utility/FDEPConflictFinder.h" -#include "storm-dft/utility/FailureBoundFinder.h" #include "gtest/gtest.h" #include "storm-config.h" From ec67166041d50c3d4127cc5cba1cf76335c77595 Mon Sep 17 00:00:00 2001 From: Alexander Bork Date: Wed, 10 Jul 2019 17:38:57 +0200 Subject: [PATCH 33/47] Decoupled preprocessing and SMT solving in commandline interface --- src/storm-dft-cli/storm-dft.cpp | 68 +++++++++++++++++++++++++-------- src/storm-dft/api/storm-dft.cpp | 35 +++-------------- src/storm-dft/api/storm-dft.h | 2 +- 3 files changed, 60 insertions(+), 45 deletions(-) diff --git a/src/storm-dft-cli/storm-dft.cpp b/src/storm-dft-cli/storm-dft.cpp index 7349e0704..7ce6eadea 100644 --- a/src/storm-dft-cli/storm-dft.cpp +++ b/src/storm-dft-cli/storm-dft.cpp @@ -28,7 +28,6 @@ void processOptions() { storm::settings::modules::FaultTreeSettings const& faultTreeSettings = storm::settings::getModule(); storm::settings::modules::IOSettings const& ioSettings = storm::settings::getModule(); storm::settings::modules::DftGspnSettings const& dftGspnSettings = storm::settings::getModule(); - auto const &debug = storm::settings::getModule(); auto dftTransformator = storm::transformations::dft::DftTransformator(); @@ -90,33 +89,72 @@ void processOptions() { return; } + // TODO introduce some flags + bool useSMT = false; + bool printOutput = false; + uint64_t solverTimeout = 10; #ifdef STORM_HAVE_Z3 if (faultTreeSettings.solveWithSMT()) { + useSMT = true; + STORM_PRINT("Use SMT for preprocessing" << std::endl) dft = dftTransformator.transformUniqueFailedBe(*dft); if (dft->getDependencies().size() > 0) { // Making the constantly failed BE unique may introduce non-binary FDEPs dft = dftTransformator.transformBinaryFDEPs(*dft); } + } +#endif + + dft->setDynamicBehaviorInfo(); + + storm::api::PreprocessingResult preResults; + preResults.lowerBEBound = storm::dft::utility::FailureBoundFinder::getLeastFailureBound(*dft, useSMT, + solverTimeout); + preResults.upperBEBound = storm::dft::utility::FailureBoundFinder::getAlwaysFailedBound(*dft, useSMT, + solverTimeout); + if (printOutput) { + STORM_PRINT("BE FAILURE BOUNDS" << std::endl << + "========================================" << std::endl << + "Lower bound: " << std::to_string(preResults.lowerBEBound) << std::endl << + "Upper bound: " << std::to_string(preResults.upperBEBound) << std::endl) + } + + preResults.fdepConflicts = storm::dft::utility::FDEPConflictFinder::getDependencyConflicts(*dft, true, + solverTimeout); + + if (printOutput) { + STORM_PRINT("========================================" << std::endl << + "FDEP CONFLICTS" << std::endl << + "========================================" + << std::endl) + for (auto pair: preResults.fdepConflicts) { + STORM_PRINT("Conflict between " << dft->getElement(pair.first)->name() << " and " + << dft->getElement(pair.second)->name() << std::endl) + } + } + + // Set the conflict map of the dft + std::set conflict_set; + for (auto conflict : preResults.fdepConflicts) { + conflict_set.insert(size_t(conflict.first)); + conflict_set.insert(size_t(conflict.second)); + } + for (size_t depId : dft->getDependencies()) { + if (!conflict_set.count(depId)) { + dft->setDependencyNotInConflict(depId); + } + } + + +#ifdef STORM_HAVE_Z3 + if (faultTreeSettings.solveWithSMT()) { // Solve with SMT STORM_LOG_DEBUG("Running DFT analysis with use of SMT"); // Set dynamic behavior vector - dft->setDynamicBehaviorInfo(); - auto smtResults = storm::api::analyzeDFTSMT(*dft, true); - // Set the conflict map of the dft - std::set conflict_set; - for (auto conflict : smtResults.fdepConflicts) { - conflict_set.insert(size_t(conflict.first)); - conflict_set.insert(size_t(conflict.second)); - } - for (size_t depId : dft->getDependencies()) { - if (!conflict_set.count(depId)) { - dft->setDependencyNotInConflict(depId); - } - } + storm::api::analyzeDFTSMT(*dft, true); } #endif - //Proprocessing // From now on we analyse DFT via model checking diff --git a/src/storm-dft/api/storm-dft.cpp b/src/storm-dft/api/storm-dft.cpp index dd9bd9dcd..b4deb1cfa 100644 --- a/src/storm-dft/api/storm-dft.cpp +++ b/src/storm-dft/api/storm-dft.cpp @@ -43,43 +43,20 @@ namespace storm { } template<> - storm::api::PreprocessingResult + void analyzeDFTSMT(storm::storage::DFT const &dft, bool printOutput) { uint64_t solverTimeout = 10; storm::modelchecker::DFTASFChecker smtChecker(dft); smtChecker.toSolver(); - storm::api::PreprocessingResult results; - - results.lowerBEBound = storm::dft::utility::FailureBoundFinder::getLeastFailureBound(dft, true, - solverTimeout); - results.upperBEBound = storm::dft::utility::FailureBoundFinder::getAlwaysFailedBound(dft, true, - solverTimeout); - if (printOutput) { - STORM_PRINT("BE FAILURE BOUNDS" << std::endl << - "========================================" << std::endl << - "Lower bound: " << std::to_string(results.lowerBEBound) << std::endl << - "Upper bound: " << std::to_string(results.upperBEBound) << std::endl) - } - - results.fdepConflicts = storm::dft::utility::FDEPConflictFinder::getDependencyConflicts(dft, true, - solverTimeout); - - if (printOutput) { - STORM_PRINT("========================================" << std::endl << - "FDEP CONFLICTS" << std::endl << - "========================================" - << std::endl) - for (auto pair: results.fdepConflicts) { - STORM_PRINT("Conflict between " << dft.getElement(pair.first)->name() << " and " - << dft.getElement(pair.second)->name() << std::endl) - } - } - return results; + // Removed bound computation etc. here + smtChecker.setSolverTimeout(solverTimeout); + smtChecker.checkTleNeverFailed(); + smtChecker.unsetSolverTimeout(); } template<> - storm::api::PreprocessingResult + void analyzeDFTSMT(storm::storage::DFT const &dft, bool printOutput) { STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Analysis by SMT not supported for this data type."); diff --git a/src/storm-dft/api/storm-dft.h b/src/storm-dft/api/storm-dft.h index a3cb5a865..c9aa6b902 100644 --- a/src/storm-dft/api/storm-dft.h +++ b/src/storm-dft/api/storm-dft.h @@ -108,7 +108,7 @@ namespace storm { * @return Result result vector */ template - storm::api::PreprocessingResult + void analyzeDFTSMT(storm::storage::DFT const &dft, bool printOutput); /*! From 88d6300084fae8df424bbf598d122037d36237c4 Mon Sep 17 00:00:00 2001 From: Alexander Bork Date: Mon, 15 Jul 2019 17:26:53 +0200 Subject: [PATCH 34/47] Added option for label preservation to state elimination --- .../NonMarkovianChainTransformer.cpp | 46 +++++++++++++------ 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/src/storm/transformer/NonMarkovianChainTransformer.cpp b/src/storm/transformer/NonMarkovianChainTransformer.cpp index 1bc460ba5..0e5971cae 100644 --- a/src/storm/transformer/NonMarkovianChainTransformer.cpp +++ b/src/storm/transformer/NonMarkovianChainTransformer.cpp @@ -21,7 +21,7 @@ namespace storm { bool preserveLabels) { // TODO reward models - STORM_LOG_WARN("State elimination is currently not label preserving!"); + STORM_LOG_WARN_COND(preserveLabels, "Labels are not preserved! Results may be incorrect."); STORM_LOG_WARN("Reward Models and Choice Labelings are ignored!"); if (ma->isClosed() && ma->getMarkovianStates().full()) { storm::storage::sparse::ModelComponents components( @@ -48,7 +48,6 @@ namespace storm { storm::storage::SparseMatrix backwards = ma->getBackwardTransitions(); // Determine the state remapping - // TODO Consider state labels for (uint_fast64_t base_state = 0; base_state < ma->getNumberOfStates(); ++base_state) { STORM_LOG_ASSERT(!ma->isHybridState(base_state), "Base state is hybrid."); if (ma->isMarkovianState(base_state)) { @@ -57,6 +56,9 @@ namespace storm { while (!queue.empty()) { auto currState = queue.front(); queue.pop(); + + auto currLabels = ma->getLabelsOfState(currState); + // Get predecessors from matrix typename storm::storage::SparseMatrix::rows entriesInRow = backwards.getRow( currState); @@ -64,11 +66,21 @@ namespace storm { entryIt != entryIte; ++entryIt) { uint_fast64_t predecessor = entryIt->getColumn(); if (!ma->isMarkovianState(predecessor) && !statesToKeep.count(predecessor)) { - if (!eliminationMapping.count(predecessor)) { - eliminationMapping[predecessor] = base_state; - queue.push(predecessor); - } else if (eliminationMapping[predecessor] != base_state) { - eliminationMapping.erase(predecessor); + if (!preserveLabels || currLabels == ma->getLabelsOfState(predecessor)) { + // If labels are not to be preserved or states are labeled the same + if (!eliminationMapping.count(predecessor)) { + eliminationMapping[predecessor] = base_state; + queue.push(predecessor); + } else if (eliminationMapping[predecessor] != base_state) { + eliminationMapping.erase(predecessor); + statesToKeep.insert(predecessor); + changedStates.push(predecessor); + } + } else { + // Labels are to be preserved and states have different labels + if (eliminationMapping.count(predecessor)) { + eliminationMapping.erase(predecessor); + } statesToKeep.insert(predecessor); changedStates.push(predecessor); } @@ -108,13 +120,13 @@ namespace storm { changedStates.pop(); } - // At ma point, we hopefully have a valid mapping which eliminates a lot of states + // At this point, we hopefully have a valid mapping which eliminates a lot of states - /*STORM_PRINT("Elimination Mapping" << std::endl) + STORM_LOG_TRACE("Elimination Mapping" << std::endl); for (auto entry : eliminationMapping) { - STORM_PRINT(std::to_string(entry.first) << " -> " << std::to_string(entry.second) << std::endl) - }*/ - STORM_PRINT("Eliminating " << eliminationMapping.size() << " states" << std::endl) + STORM_LOG_TRACE(std::to_string(entry.first) << " -> " << std::to_string(entry.second) << std::endl); + } + STORM_LOG_INFO("Eliminating " << eliminationMapping.size() << " states" << std::endl); // TODO explore if one can construct elimination mapping and state remapping in one step @@ -146,7 +158,7 @@ namespace storm { false); std::vector newExitRates; //TODO choice labeling - boost::optional choiceLabeling; + boost::optional newChoiceLabeling; // Initialize the matrix builder and helper variables storm::storage::SparseMatrixBuilder matrixBuilder = storm::storage::SparseMatrixBuilder( @@ -196,7 +208,8 @@ namespace storm { for (auto const &row : rowSet) { for (auto const &transition : row) { matrixBuilder.addNextValue(currentRow, transition.first, transition.second); - //STORM_PRINT(stateRemapping[state] << "->" << transition.first << " : " << transition.second << std::endl) + STORM_LOG_TRACE(stateRemapping[state] << "->" << transition.first << " : " << transition.second + << std::endl); } ++currentRow; } @@ -222,11 +235,16 @@ namespace storm { template class NonMarkovianChainTransformer; + template + class NonMarkovianChainTransformer>; #ifdef STORM_HAVE_CARL template class NonMarkovianChainTransformer; + template + class NonMarkovianChainTransformer; + #endif } } From 5aa19c9a58e8fd46a9a1da2910151ce2ba51b3dd Mon Sep 17 00:00:00 2001 From: Alexander Bork Date: Mon, 15 Jul 2019 17:38:19 +0200 Subject: [PATCH 35/47] Added settings for non-Markovian state elimination --- src/storm/settings/SettingsManager.cpp | 2 + .../modules/TransformationSettings.cpp | 49 ++++++++++++++++ .../settings/modules/TransformationSettings.h | 56 +++++++++++++++++++ 3 files changed, 107 insertions(+) create mode 100644 src/storm/settings/modules/TransformationSettings.cpp create mode 100644 src/storm/settings/modules/TransformationSettings.h diff --git a/src/storm/settings/SettingsManager.cpp b/src/storm/settings/SettingsManager.cpp index 69ad4a909..7f5ffc604 100644 --- a/src/storm/settings/SettingsManager.cpp +++ b/src/storm/settings/SettingsManager.cpp @@ -37,6 +37,7 @@ #include "storm/settings/modules/JitBuilderSettings.h" #include "storm/settings/modules/MultiObjectiveSettings.h" #include "storm/settings/modules/MultiplierSettings.h" +#include "storm/settings/modules/TransformationSettings.h" #include "storm/utility/macros.h" #include "storm/utility/file.h" #include "storm/utility/string.h" @@ -568,6 +569,7 @@ namespace storm { storm::settings::addModule(); storm::settings::addModule(); storm::settings::addModule(); + storm::settings::addModule(); } } diff --git a/src/storm/settings/modules/TransformationSettings.cpp b/src/storm/settings/modules/TransformationSettings.cpp new file mode 100644 index 000000000..75b1c3daf --- /dev/null +++ b/src/storm/settings/modules/TransformationSettings.cpp @@ -0,0 +1,49 @@ +#include "TransformationSettings.h" + +#include "storm/settings/Option.h" +#include "storm/settings/OptionBuilder.h" +#include "storm/exceptions/InvalidSettingsException.h" + +namespace storm { + namespace settings { + namespace modules { + + const std::string TransformationSettings::moduleName = "transformation"; + + const std::string TransformationSettings::chainEliminationOptionName = "eliminate-chains"; + const std::string TransformationSettings::ignoreLabelingOptionName = "ec-ignore-labeling"; + + + TransformationSettings::TransformationSettings() : ModuleSettings(moduleName) { + this->addOption(storm::settings::OptionBuilder(moduleName, chainEliminationOptionName, false, + "If set, chains of non-Markovian states are eliminated if the resulting model is a Markov Automaton.").build()); + this->addOption(storm::settings::OptionBuilder(moduleName, ignoreLabelingOptionName, false, + "If set, the elimination of chains ignores the labels for all non-Markovian states. This may cause wrong results.").build()); + } + + bool TransformationSettings::isChainEliminationSet() const { + return this->getOption(chainEliminationOptionName).getHasOptionBeenSet(); + } + + bool TransformationSettings::isIgnoreLabelingSet() const { + return this->getOption(ignoreLabelingOptionName).getHasOptionBeenSet(); + } + + + bool TransformationSettings::check() const { + // Ensure that labeling preservation is only set if chain elimination is set + STORM_LOG_THROW(isChainEliminationSet() || !isIgnoreLabelingSet(), + storm::exceptions::InvalidSettingsException, + "Label preservation can only be chosen if chain elimination is applied."); + + return true; + } + + void TransformationSettings::finalize() { + //Intentionally left empty + } + + + } // namespace modules + } // namespace settings +} // namespace storm diff --git a/src/storm/settings/modules/TransformationSettings.h b/src/storm/settings/modules/TransformationSettings.h new file mode 100644 index 000000000..ba646c763 --- /dev/null +++ b/src/storm/settings/modules/TransformationSettings.h @@ -0,0 +1,56 @@ +#ifndef STORM_TRANSFORMATIONSETTINGS_H +#define STORM_TRANSFORMATIONSETTINGS_H + +#include "storm-config.h" +#include "storm/settings/modules/ModuleSettings.h" + +namespace storm { + namespace settings { + namespace modules { + + /*! + * This class represents the model transformer settings + */ + class TransformationSettings : public ModuleSettings { + public: + + /*! + * Creates a new set of transformer settings. + */ + TransformationSettings(); + + /*! + * Retrieves whether the option to eliminate chains of non-Markovian states was set. + * + * @return True if the option to eliminate chains of non-Markovian states was set. + */ + bool isChainEliminationSet() const; + + + /*! + * Retrieves whether the preserve-labeling option for jani was set. + * + * @return True if the preserve-labeling option was set. + */ + bool isIgnoreLabelingSet() const; + + + bool check() const override; + + void finalize() override; + + // The name of the module. + static const std::string moduleName; + + private: + // Define the string names of the options as constants. + static const std::string chainEliminationOptionName; + static const std::string ignoreLabelingOptionName; + + }; + + } // namespace modules + } // namespace settings +} // namespace storm + +#endif //STORM_TRANSFORMATIONSETTINGS_H From a73c2691b68957b1efa2573c97d2b37bd6e76eb4 Mon Sep 17 00:00:00 2001 From: Alexander Bork Date: Mon, 15 Jul 2019 17:41:37 +0200 Subject: [PATCH 36/47] Integration of the new settings in the DFT analysis --- src/storm-dft-cli/storm-dft.cpp | 35 ++-- src/storm-dft/api/storm-dft.h | 9 +- .../builder/ExplicitDFTModelBuilder.cpp | 7 +- .../modelchecker/dft/DFTModelChecker.cpp | 193 +++++++++++++----- .../modelchecker/dft/DFTModelChecker.h | 15 +- src/storm-dft/settings/DftSettings.cpp | 2 + 6 files changed, 177 insertions(+), 84 deletions(-) diff --git a/src/storm-dft-cli/storm-dft.cpp b/src/storm-dft-cli/storm-dft.cpp index 7ce6eadea..1cc9a02e2 100644 --- a/src/storm-dft-cli/storm-dft.cpp +++ b/src/storm-dft-cli/storm-dft.cpp @@ -10,6 +10,7 @@ #include "storm/settings/modules/DebugSettings.h" #include "storm/settings/modules/IOSettings.h" #include "storm/settings/modules/ResourceSettings.h" +#include "storm/settings/modules/TransformationSettings.h" #include "storm/utility/initialize.h" #include "storm-cli-utilities/cli.h" #include "storm-parsers/api/storm-parsers.h" @@ -28,6 +29,7 @@ void processOptions() { storm::settings::modules::FaultTreeSettings const& faultTreeSettings = storm::settings::getModule(); storm::settings::modules::IOSettings const& ioSettings = storm::settings::getModule(); storm::settings::modules::DftGspnSettings const& dftGspnSettings = storm::settings::getModule(); + storm::settings::modules::TransformationSettings const &transformationSettings = storm::settings::getModule(); auto dftTransformator = storm::transformations::dft::DftTransformator(); @@ -55,7 +57,7 @@ void processOptions() { } // Eliminate non-binary dependencies - if (dft->getDependencies().size() > 0) { + if (!dft->getDependencies().empty()) { dft = dftTransformator.transformBinaryFDEPs(*dft); } // Check well-formedness of DFT @@ -81,7 +83,7 @@ void processOptions() { // SMT if (dftIOSettings.isExportToSmt()) { dft = dftTransformator.transformUniqueFailedBe(*dft); - if (dft->getDependencies().size() > 0) { + if (!dft->getDependencies().empty()) { dft = dftTransformator.transformBinaryFDEPs(*dft); } // Export to smtlib2 @@ -91,14 +93,13 @@ void processOptions() { // TODO introduce some flags bool useSMT = false; - bool printOutput = false; uint64_t solverTimeout = 10; #ifdef STORM_HAVE_Z3 if (faultTreeSettings.solveWithSMT()) { useSMT = true; STORM_PRINT("Use SMT for preprocessing" << std::endl) dft = dftTransformator.transformUniqueFailedBe(*dft); - if (dft->getDependencies().size() > 0) { + if (!dft->getDependencies().empty()) { // Making the constantly failed BE unique may introduce non-binary FDEPs dft = dftTransformator.transformBinaryFDEPs(*dft); } @@ -112,25 +113,20 @@ void processOptions() { solverTimeout); preResults.upperBEBound = storm::dft::utility::FailureBoundFinder::getAlwaysFailedBound(*dft, useSMT, solverTimeout); - if (printOutput) { - STORM_PRINT("BE FAILURE BOUNDS" << std::endl << - "========================================" << std::endl << + STORM_LOG_DEBUG("BE FAILURE BOUNDS" << std::endl << "========================================" << std::endl << "Lower bound: " << std::to_string(preResults.lowerBEBound) << std::endl << - "Upper bound: " << std::to_string(preResults.upperBEBound) << std::endl) - } + "Upper bound: " << std::to_string(preResults.upperBEBound) << std::endl); preResults.fdepConflicts = storm::dft::utility::FDEPConflictFinder::getDependencyConflicts(*dft, true, solverTimeout); - if (printOutput) { - STORM_PRINT("========================================" << std::endl << + STORM_LOG_DEBUG("========================================" << std::endl << "FDEP CONFLICTS" << std::endl << "========================================" - << std::endl) - for (auto pair: preResults.fdepConflicts) { - STORM_PRINT("Conflict between " << dft->getElement(pair.first)->name() << " and " - << dft->getElement(pair.second)->name() << std::endl) - } + << std::endl); + for (auto pair: preResults.fdepConflicts) { + STORM_LOG_DEBUG("Conflict between " << dft->getElement(pair.first)->name() << " and " + << dft->getElement(pair.second)->name() << std::endl); } // Set the conflict map of the dft @@ -149,7 +145,7 @@ void processOptions() { #ifdef STORM_HAVE_Z3 if (faultTreeSettings.solveWithSMT()) { // Solve with SMT - STORM_LOG_DEBUG("Running DFT analysis with use of SMT"); + STORM_LOG_DEBUG("Running DFT analysis with use of SMT" << std::endl); // Set dynamic behavior vector storm::api::analyzeDFTSMT(*dft, true); } @@ -263,7 +259,10 @@ void processOptions() { approximationError = faultTreeSettings.getApproximationError(); } storm::api::analyzeDFT(*dft, props, faultTreeSettings.useSymmetryReduction(), faultTreeSettings.useModularisation(), relevantEvents, - faultTreeSettings.isAllowDCForRelevantEvents(), approximationError, faultTreeSettings.getApproximationHeuristic(), true); + faultTreeSettings.isAllowDCForRelevantEvents(), approximationError, + faultTreeSettings.getApproximationHeuristic(), + transformationSettings.isChainEliminationSet(), + transformationSettings.isIgnoreLabelingSet(), true); } } diff --git a/src/storm-dft/api/storm-dft.h b/src/storm-dft/api/storm-dft.h index c9aa6b902..470e3a218 100644 --- a/src/storm-dft/api/storm-dft.h +++ b/src/storm-dft/api/storm-dft.h @@ -81,6 +81,8 @@ namespace storm { * @param allowDCForRelevantEvents If true, Don't Care propagation is allowed even for relevant events. * @param approximationError Allowed approximation error. Value 0 indicates no approximation. * @param approximationHeuristic Heuristic used for state space exploration. + * @param eliminateChains If true, chains of non-Markovian states are elimianted from the resulting MA + * @param ignoreLabeling If true, the labeling of states is ignored during state elimination * @param printOutput If true, model information, timings, results, etc. are printed. * @return Results. */ @@ -88,11 +90,14 @@ namespace storm { typename storm::modelchecker::DFTModelChecker::dft_results analyzeDFT(storm::storage::DFT const& dft, std::vector> const& properties, bool symred = true, bool allowModularisation = true, std::set const& relevantEvents = {}, bool allowDCForRelevantEvents = true, double approximationError = 0.0, - storm::builder::ApproximationHeuristic approximationHeuristic = storm::builder::ApproximationHeuristic::DEPTH, bool printOutput = false) { + storm::builder::ApproximationHeuristic approximationHeuristic = storm::builder::ApproximationHeuristic::DEPTH, + bool eliminateChains = false, bool ignoreLabeling = false, bool printOutput = false) { storm::modelchecker::DFTModelChecker modelChecker(printOutput); typename storm::modelchecker::DFTModelChecker::dft_results results = modelChecker.check(dft, properties, symred, allowModularisation, relevantEvents, allowDCForRelevantEvents, approximationError, - approximationHeuristic); + approximationHeuristic, + eliminateChains, + ignoreLabeling); if (printOutput) { modelChecker.printTimings(); modelChecker.printResults(results); diff --git a/src/storm-dft/builder/ExplicitDFTModelBuilder.cpp b/src/storm-dft/builder/ExplicitDFTModelBuilder.cpp index ad1f5d449..0f3196516 100644 --- a/src/storm-dft/builder/ExplicitDFTModelBuilder.cpp +++ b/src/storm-dft/builder/ExplicitDFTModelBuilder.cpp @@ -652,11 +652,8 @@ namespace storm { maComponents.exitRates = std::move(modelComponents.exitRates); ma = std::make_shared>(std::move(maComponents)); } - if (ma->hasOnlyTrivialNondeterminism()) { - // Markov automaton can be converted into CTMC - // TODO apply transformer to all MAs - model = storm::transformer::NonMarkovianChainTransformer::eliminateNonmarkovianStates( - ma); + if (ma->isConvertibleToCtmc()) { + model = ma->convertToCtmc(); } else { model = ma; } diff --git a/src/storm-dft/modelchecker/dft/DFTModelChecker.cpp b/src/storm-dft/modelchecker/dft/DFTModelChecker.cpp index eed303311..4af4c28c5 100644 --- a/src/storm-dft/modelchecker/dft/DFTModelChecker.cpp +++ b/src/storm-dft/modelchecker/dft/DFTModelChecker.cpp @@ -7,6 +7,8 @@ #include "storm/utility/DirectEncodingExporter.h" #include "storm/modelchecker/results/ExplicitQuantitativeCheckResult.h" #include "storm/modelchecker/results/ExplicitQualitativeCheckResult.h" +#include "storm/models/ModelType.h" +#include "storm/transformer/NonMarkovianChainTransformer.h" #include "storm-dft/builder/ExplicitDFTModelBuilder.h" #include "storm-dft/storage/dft/DFTIsomorphism.h" @@ -17,7 +19,13 @@ namespace storm { namespace modelchecker { template - typename DFTModelChecker::dft_results DFTModelChecker::check(storm::storage::DFT const& origDft, std::vector> const& properties, bool symred, bool allowModularisation, std::set const& relevantEvents, bool allowDCForRelevantEvents, double approximationError, storm::builder::ApproximationHeuristic approximationHeuristic) { + typename DFTModelChecker::dft_results + DFTModelChecker::check(storm::storage::DFT const &origDft, + std::vector> const &properties, + bool symred, bool allowModularisation, std::set const &relevantEvents, + bool allowDCForRelevantEvents, double approximationError, + storm::builder::ApproximationHeuristic approximationHeuristic, + bool eliminateChains, bool ignoreLabeling) { totalTimer.start(); dft_results results; @@ -30,21 +38,32 @@ namespace storm { // TODO: distinguish for all properties, not only for first one if (properties[0]->isTimeOperatorFormula() && allowModularisation) { // Use parallel composition as modularisation approach for expected time - std::shared_ptr> model = buildModelViaComposition(dft, properties, symred, true, relevantEvents); + std::shared_ptr> model = buildModelViaComposition(dft, + properties, + symred, true, + relevantEvents); // Model checking std::vector resultsValue = checkModel(model, properties); for (ValueType result : resultsValue) { results.push_back(result); } } else { - results = checkHelper(dft, properties, symred, allowModularisation, relevantEvents, allowDCForRelevantEvents, approximationError, approximationHeuristic); + results = checkHelper(dft, properties, symred, allowModularisation, relevantEvents, + allowDCForRelevantEvents, approximationError, approximationHeuristic, + eliminateChains, ignoreLabeling); } totalTimer.stop(); return results; } template - typename DFTModelChecker::dft_results DFTModelChecker::checkHelper(storm::storage::DFT const& dft, property_vector const& properties, bool symred, bool allowModularisation, std::set const& relevantEvents, bool allowDCForRelevantEvents, double approximationError, storm::builder::ApproximationHeuristic approximationHeuristic) { + typename DFTModelChecker::dft_results + DFTModelChecker::checkHelper(storm::storage::DFT const &dft, + property_vector const &properties, bool symred, + bool allowModularisation, std::set const &relevantEvents, + bool allowDCForRelevantEvents, double approximationError, + storm::builder::ApproximationHeuristic approximationHeuristic, + bool eliminateChains, bool ignoreLabeling) { STORM_LOG_TRACE("Check helper called"); std::vector> dfts; bool invResults = false; @@ -52,7 +71,7 @@ namespace storm { size_t nrM = 0; // K out of M // Try modularisation - if(allowModularisation) { + if (allowModularisation) { switch (dft.topLevelType()) { case storm::storage::DFTElementType::AND: STORM_LOG_TRACE("top modularisation called AND"); @@ -73,9 +92,10 @@ namespace storm { STORM_LOG_TRACE("top modularisation called VOT"); dfts = dft.topModularisation(); STORM_LOG_TRACE("Modularisation into " << dfts.size() << " submodules."); - nrK = std::static_pointer_cast const>(dft.getTopLevelGate())->threshold(); + nrK = std::static_pointer_cast const>( + dft.getTopLevelGate())->threshold(); nrM = dfts.size(); - if(nrK <= nrM/2) { + if (nrK <= nrM / 2) { nrK -= 1; invResults = true; } @@ -87,7 +107,7 @@ namespace storm { } // Perform modularisation - if(dfts.size() > 1) { + if (dfts.size() > 1) { STORM_LOG_TRACE("Recursive CHECK Call"); // TODO: compute simultaneously dft_results results; @@ -97,39 +117,42 @@ namespace storm { } else { // Recursively call model checking std::vector res; - for(auto const ft : dfts) { + for (auto const ft : dfts) { // TODO: allow approximation in modularisation - dft_results ftResults = checkHelper(ft, {property}, symred, true, relevantEvents, allowDCForRelevantEvents, 0.0); + dft_results ftResults = checkHelper(ft, {property}, symred, true, relevantEvents, + allowDCForRelevantEvents, 0.0); STORM_LOG_ASSERT(ftResults.size() == 1, "Wrong number of results"); res.push_back(boost::get(ftResults[0])); } // Combine modularisation results - STORM_LOG_TRACE("Combining all results... K=" << nrK << "; M=" << nrM << "; invResults=" << (invResults?"On":"Off")); + STORM_LOG_TRACE("Combining all results... K=" << nrK << "; M=" << nrM << "; invResults=" + << (invResults ? "On" : "Off")); ValueType result = storm::utility::zero(); - int limK = invResults ? -1 : nrM+1; + int limK = invResults ? -1 : nrM + 1; int chK = invResults ? -1 : 1; // WARNING: there is a bug for computing permutations with more than 32 elements - STORM_LOG_THROW(res.size() < 32, storm::exceptions::NotSupportedException, "Permutations work only for < 32 elements"); - for(int cK = nrK; cK != limK; cK += chK ) { + STORM_LOG_THROW(res.size() < 32, storm::exceptions::NotSupportedException, + "Permutations work only for < 32 elements"); + for (int cK = nrK; cK != limK; cK += chK) { STORM_LOG_ASSERT(cK >= 0, "ck negative."); size_t permutation = smallestIntWithNBitsSet(static_cast(cK)); do { - STORM_LOG_TRACE("Permutation="<() - result; } results.push_back(result); @@ -138,19 +161,25 @@ namespace storm { return results; } else { // No modularisation was possible - return checkDFT(dft, properties, symred, relevantEvents, allowDCForRelevantEvents, approximationError, approximationHeuristic); + return checkDFT(dft, properties, symred, relevantEvents, allowDCForRelevantEvents, approximationError, + approximationHeuristic, eliminateChains, ignoreLabeling); } } template - std::shared_ptr> DFTModelChecker::buildModelViaComposition(storm::storage::DFT const& dft, property_vector const& properties, bool symred, bool allowModularisation, std::set const& relevantEvents, bool allowDCForRelevantEvents) { + std::shared_ptr> + DFTModelChecker::buildModelViaComposition(storm::storage::DFT const &dft, + property_vector const &properties, bool symred, + bool allowModularisation, + std::set const &relevantEvents, + bool allowDCForRelevantEvents) { // TODO: use approximation? STORM_LOG_TRACE("Build model via composition"); std::vector> dfts; bool isAnd = true; // Try modularisation - if(allowModularisation) { + if (allowModularisation) { switch (dft.topLevelType()) { case storm::storage::DFTElementType::AND: STORM_LOG_TRACE("top modularisation called AND"); @@ -174,7 +203,7 @@ namespace storm { } // Perform modularisation via parallel composition - if(dfts.size() > 1) { + if (dfts.size() > 1) { STORM_LOG_TRACE("Recursive CHECK Call"); bool firstTime = true; std::shared_ptr> composedModel; @@ -185,7 +214,7 @@ namespace storm { // Find symmetries std::map>> emptySymmetry; storm::storage::DFTIndependentSymmetries symmetries(emptySymmetry); - if(symred) { + if (symred) { auto colouring = ft.colourDFT(); symmetries = ft.findSymmetries(colouring); STORM_LOG_DEBUG("Found " << symmetries.groups.size() << " symmetries."); @@ -194,29 +223,37 @@ namespace storm { // Build a single CTMC STORM_LOG_DEBUG("Building Model..."); - storm::builder::ExplicitDFTModelBuilder builder(ft, symmetries, relevantEvents, allowDCForRelevantEvents); + storm::builder::ExplicitDFTModelBuilder builder(ft, symmetries, relevantEvents, + allowDCForRelevantEvents); builder.buildModel(0, 0.0); std::shared_ptr> model = builder.getModel(); explorationTimer.stop(); - STORM_LOG_THROW(model->isOfType(storm::models::ModelType::Ctmc), storm::exceptions::NotSupportedException, "Parallel composition only applicable for CTMCs"); + STORM_LOG_THROW(model->isOfType(storm::models::ModelType::Ctmc), + storm::exceptions::NotSupportedException, + "Parallel composition only applicable for CTMCs"); std::shared_ptr> ctmc = model->template as>(); // Apply bisimulation to new CTMC bisimulationTimer.start(); - ctmc = storm::api::performDeterministicSparseBisimulationMinimization>(ctmc, properties, storm::storage::BisimulationType::Weak)->template as>(); + ctmc = storm::api::performDeterministicSparseBisimulationMinimization>( + ctmc, properties, + storm::storage::BisimulationType::Weak)->template as>(); bisimulationTimer.stop(); if (firstTime) { composedModel = ctmc; firstTime = false; } else { - composedModel = storm::builder::ParallelCompositionBuilder::compose(composedModel, ctmc, isAnd); + composedModel = storm::builder::ParallelCompositionBuilder::compose(composedModel, + ctmc, isAnd); } // Apply bisimulation to parallel composition bisimulationTimer.start(); - composedModel = storm::api::performDeterministicSparseBisimulationMinimization>(composedModel, properties, storm::storage::BisimulationType::Weak)->template as>(); + composedModel = storm::api::performDeterministicSparseBisimulationMinimization>( + composedModel, properties, + storm::storage::BisimulationType::Weak)->template as>(); bisimulationTimer.stop(); STORM_LOG_DEBUG("No. states (Composed): " << composedModel->getNumberOfStates()); @@ -236,7 +273,7 @@ namespace storm { // Find symmetries std::map>> emptySymmetry; storm::storage::DFTIndependentSymmetries symmetries(emptySymmetry); - if(symred) { + if (symred) { auto colouring = dft.colourDFT(); symmetries = dft.findSymmetries(colouring); STORM_LOG_DEBUG("Found " << symmetries.groups.size() << " symmetries."); @@ -245,24 +282,33 @@ namespace storm { // Build a single CTMC STORM_LOG_DEBUG("Building Model..."); - storm::builder::ExplicitDFTModelBuilder builder(dft, symmetries, relevantEvents, allowDCForRelevantEvents); + storm::builder::ExplicitDFTModelBuilder builder(dft, symmetries, relevantEvents, + allowDCForRelevantEvents); builder.buildModel(0, 0.0); std::shared_ptr> model = builder.getModel(); //model->printModelInformationToStream(std::cout); explorationTimer.stop(); - STORM_LOG_THROW(model->isOfType(storm::models::ModelType::Ctmc), storm::exceptions::NotSupportedException, "Parallel composition only applicable for CTMCs"); + STORM_LOG_THROW(model->isOfType(storm::models::ModelType::Ctmc), + storm::exceptions::NotSupportedException, + "Parallel composition only applicable for CTMCs"); return model->template as>(); } } template - typename DFTModelChecker::dft_results DFTModelChecker::checkDFT(storm::storage::DFT const& dft, property_vector const& properties, bool symred, std::set const& relevantEvents, bool allowDCForRelevantEvents, double approximationError, storm::builder::ApproximationHeuristic approximationHeuristic) { + typename DFTModelChecker::dft_results + DFTModelChecker::checkDFT(storm::storage::DFT const &dft, + property_vector const &properties, bool symred, + std::set const &relevantEvents, bool allowDCForRelevantEvents, + double approximationError, + storm::builder::ApproximationHeuristic approximationHeuristic, + bool eliminateChains, bool ignoreLabeling) { explorationTimer.start(); // Find symmetries std::map>> emptySymmetry; storm::storage::DFTIndependentSymmetries symmetries(emptySymmetry); - if(symred) { + if (symred) { auto colouring = dft.colourDFT(); symmetries = dft.findSymmetries(colouring); STORM_LOG_DEBUG("Found " << symmetries.groups.size() << " symmetries."); @@ -273,10 +319,12 @@ namespace storm { // Comparator for checking the error of the approximation storm::utility::ConstantsComparator comparator; // Build approximate Markov Automata for lower and upper bound - approximation_result approxResult = std::make_pair(storm::utility::zero(), storm::utility::zero()); + approximation_result approxResult = std::make_pair(storm::utility::zero(), + storm::utility::zero()); std::shared_ptr> model; std::vector newResult; - storm::builder::ExplicitDFTModelBuilder builder(dft, symmetries, relevantEvents, allowDCForRelevantEvents); + storm::builder::ExplicitDFTModelBuilder builder(dft, symmetries, relevantEvents, + allowDCForRelevantEvents); // TODO: compute approximation for all properties simultaneously? std::shared_ptr property = properties[0]; @@ -285,7 +333,9 @@ namespace storm { } bool probabilityFormula = property->isProbabilityOperatorFormula(); - STORM_LOG_ASSERT((property->isTimeOperatorFormula() && !probabilityFormula) || (!property->isTimeOperatorFormula() && probabilityFormula), "Probability formula not initialized correctly"); + STORM_LOG_ASSERT((property->isTimeOperatorFormula() && !probabilityFormula) || + (!property->isTimeOperatorFormula() && probabilityFormula), + "Probability formula not initialized correctly"); size_t iteration = 0; do { // Iteratively build finer models @@ -310,7 +360,9 @@ namespace storm { // Check lower bounds newResult = checkModel(model, {property}); STORM_LOG_ASSERT(newResult.size() == 1, "Wrong size for result vector."); - STORM_LOG_ASSERT(iteration == 0 || !comparator.isLess(newResult[0], approxResult.first), "New under-approximation " << newResult[0] << " is smaller than old result " << approxResult.first); + STORM_LOG_ASSERT(iteration == 0 || !comparator.isLess(newResult[0], approxResult.first), + "New under-approximation " << newResult[0] << " is smaller than old result " + << approxResult.first); approxResult.first = newResult[0]; // Build model for upper bound @@ -321,17 +373,27 @@ namespace storm { // Check upper bound newResult = checkModel(model, {property}); STORM_LOG_ASSERT(newResult.size() == 1, "Wrong size for result vector."); - STORM_LOG_ASSERT(iteration == 0 || !comparator.isLess(approxResult.second, newResult[0]), "New over-approximation " << newResult[0] << " is greater than old result " << approxResult.second); + STORM_LOG_ASSERT(iteration == 0 || !comparator.isLess(approxResult.second, newResult[0]), + "New over-approximation " << newResult[0] << " is greater than old result " + << approxResult.second); approxResult.second = newResult[0]; ++iteration; - STORM_LOG_ASSERT(comparator.isLess(approxResult.first, approxResult.second) || comparator.isEqual(approxResult.first, approxResult.second), "Under-approximation " << approxResult.first << " is greater than over-approximation " << approxResult.second); + STORM_LOG_ASSERT(comparator.isLess(approxResult.first, approxResult.second) || + comparator.isEqual(approxResult.first, approxResult.second), + "Under-approximation " << approxResult.first + << " is greater than over-approximation " + << approxResult.second); //STORM_LOG_INFO("Result after iteration " << iteration << ": (" << std::setprecision(10) << approxResult.first << ", " << approxResult.second << ")"); totalTimer.stop(); printTimings(); totalTimer.start(); - STORM_LOG_THROW(!storm::utility::isInfinity(approxResult.first) && !storm::utility::isInfinity(approxResult.second), storm::exceptions::NotSupportedException, "Approximation does not work if result might be infinity."); - } while (!isApproximationSufficient(approxResult.first, approxResult.second, approximationError, probabilityFormula)); + STORM_LOG_THROW(!storm::utility::isInfinity(approxResult.first) && + !storm::utility::isInfinity(approxResult.second), + storm::exceptions::NotSupportedException, + "Approximation does not work if result might be infinity."); + } while (!isApproximationSufficient(approxResult.first, approxResult.second, approximationError, + probabilityFormula)); //STORM_LOG_INFO("Finished approximation after " << iteration << " iteration" << (iteration > 1 ? "s." : ".")); dft_results results; @@ -341,9 +403,15 @@ namespace storm { // Build a single Markov Automaton auto ioSettings = storm::settings::getModule(); STORM_LOG_DEBUG("Building Model..."); - storm::builder::ExplicitDFTModelBuilder builder(dft, symmetries, relevantEvents, allowDCForRelevantEvents); + storm::builder::ExplicitDFTModelBuilder builder(dft, symmetries, relevantEvents, + allowDCForRelevantEvents); builder.buildModel(0, 0.0); std::shared_ptr> model = builder.getModel(); + if (eliminateChains && model->isOfType(storm::models::ModelType::MarkovAutomaton)) { + auto ma = std::static_pointer_cast>(model); + model = storm::transformer::NonMarkovianChainTransformer::eliminateNonmarkovianStates(ma, + !ignoreLabeling); + } explorationTimer.stop(); // Print model information @@ -376,12 +444,17 @@ namespace storm { } template - std::vector DFTModelChecker::checkModel(std::shared_ptr>& model, property_vector const& properties) { + std::vector + DFTModelChecker::checkModel(std::shared_ptr> &model, + property_vector const &properties) { // Bisimulation - if (model->isOfType(storm::models::ModelType::Ctmc) && storm::settings::getModule().isBisimulationSet()) { + if (model->isOfType(storm::models::ModelType::Ctmc) && + storm::settings::getModule().isBisimulationSet()) { bisimulationTimer.start(); STORM_LOG_DEBUG("Bisimulation..."); - model = storm::api::performDeterministicSparseBisimulationMinimization>(model->template as>(), properties, storm::storage::BisimulationType::Weak)->template as>(); + model = storm::api::performDeterministicSparseBisimulationMinimization>( + model->template as>(), properties, + storm::storage::BisimulationType::Weak)->template as>(); STORM_LOG_DEBUG("No. states (Bisimulation): " << model->getNumberOfStates()); STORM_LOG_DEBUG("No. transitions (Bisimulation): " << model->getNumberOfTransitions()); bisimulationTimer.stop(); @@ -398,7 +471,9 @@ namespace storm { singleModelCheckingTimer.reset(); singleModelCheckingTimer.start(); //STORM_PRINT_AND_LOG("Model checking property " << *property << " ..." << std::endl); - std::unique_ptr result(storm::api::verifyWithSparseEngine(model, storm::api::createTask(property, true))); + std::unique_ptr result( + storm::api::verifyWithSparseEngine(model, storm::api::createTask(property, + true))); STORM_LOG_ASSERT(result, "Result does not exist."); result->filter(storm::modelchecker::ExplicitQualitativeCheckResult(model->getInitialStates())); ValueType resultValue = result->asExplicitQuantitativeCheckResult().getValueMap().begin()->second; @@ -413,13 +488,15 @@ namespace storm { } template - bool DFTModelChecker::isApproximationSufficient(ValueType , ValueType , double , bool ) { + bool DFTModelChecker::isApproximationSufficient(ValueType, ValueType, double, bool) { STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Approximation works only for double."); } template<> - bool DFTModelChecker::isApproximationSufficient(double lowerBound, double upperBound, double approximationError, bool relative) { - STORM_LOG_THROW(!std::isnan(lowerBound) && !std::isnan(upperBound), storm::exceptions::NotSupportedException, "Approximation does not work if result is NaN."); + bool DFTModelChecker::isApproximationSufficient(double lowerBound, double upperBound, + double approximationError, bool relative) { + STORM_LOG_THROW(!std::isnan(lowerBound) && !std::isnan(upperBound), + storm::exceptions::NotSupportedException, "Approximation does not work if result is NaN."); if (relative) { return upperBound - lowerBound <= approximationError; } else { @@ -428,17 +505,17 @@ namespace storm { } template - void DFTModelChecker::printTimings(std::ostream& os) { + void DFTModelChecker::printTimings(std::ostream &os) { os << "Times:" << std::endl; os << "Exploration:\t" << explorationTimer << std::endl; os << "Building:\t" << buildingTimer << std::endl; - os << "Bisimulation:\t" << bisimulationTimer<< std::endl; + os << "Bisimulation:\t" << bisimulationTimer << std::endl; os << "Modelchecking:\t" << modelCheckingTimer << std::endl; os << "Total:\t\t" << totalTimer << std::endl; } template - void DFTModelChecker::printResults(dft_results const& results, std::ostream& os) { + void DFTModelChecker::printResults(dft_results const &results, std::ostream &os) { bool first = true; os << "Result: ["; for (auto result : results) { @@ -453,10 +530,14 @@ namespace storm { } - template class DFTModelChecker; + template + class DFTModelChecker; #ifdef STORM_HAVE_CARL - template class DFTModelChecker; + + template + class DFTModelChecker; + #endif } } diff --git a/src/storm-dft/modelchecker/dft/DFTModelChecker.h b/src/storm-dft/modelchecker/dft/DFTModelChecker.h index 4b4cd4d3d..9b898725a 100644 --- a/src/storm-dft/modelchecker/dft/DFTModelChecker.h +++ b/src/storm-dft/modelchecker/dft/DFTModelChecker.h @@ -55,11 +55,14 @@ namespace storm { * @param allowDCForRelevantEvents If true, Don't Care propagation is allowed even for relevant events. * @param approximationError Error allowed for approximation. Value 0 indicates no approximation. * @param approximationHeuristic Heuristic used for state space exploration. + * @param eliminateChains If true, chains of non-Markovian states are elimianted from the resulting MA + * @param ignoreLabeling If true, the labeling of states is ignored during state elimination * @return Model checking results for the given properties.. */ dft_results check(storm::storage::DFT const& origDft, property_vector const& properties, bool symred = true, bool allowModularisation = true, std::set const& relevantEvents = {}, bool allowDCForRelevantEvents = true, double approximationError = 0.0, - storm::builder::ApproximationHeuristic approximationHeuristic = storm::builder::ApproximationHeuristic::DEPTH); + storm::builder::ApproximationHeuristic approximationHeuristic = storm::builder::ApproximationHeuristic::DEPTH, + bool eliminateChains = false, bool ignoreLabeling = false); /*! * Print timings of all operations to stream. @@ -98,11 +101,14 @@ namespace storm { * @param allowDCForRelevantEvents If true, Don't Care propagation is allowed even for relevant events. * @param approximationError Error allowed for approximation. Value 0 indicates no approximation. * @param approximationHeuristic Heuristic used for approximation. + * @param eliminateChains If true, chains of non-Markovian states are elimianted from the resulting MA + * @param ignoreLabeling If true, the labeling of states is ignored during state elimination * @return Model checking results (or in case of approximation two results for lower and upper bound) */ dft_results checkHelper(storm::storage::DFT const& dft, property_vector const& properties, bool symred, bool allowModularisation, std::set const& relevantEvents, bool allowDCForRelevantEvents = true, double approximationError = 0.0, - storm::builder::ApproximationHeuristic approximationHeuristic = storm::builder::ApproximationHeuristic::DEPTH); + storm::builder::ApproximationHeuristic approximationHeuristic = storm::builder::ApproximationHeuristic::DEPTH, + bool eliminateChains = false, bool ignoreLabeling = false); /*! * Internal helper for building a CTMC from a DFT via parallel composition. @@ -129,12 +135,15 @@ namespace storm { * @param allowDCForRelevantEvents If true, Don't Care propagation is allowed even for relevant events. * @param approximationError Error allowed for approximation. Value 0 indicates no approximation. * @param approximationHeuristic Heuristic used for approximation. + * @param eliminateChains If true, chains of non-Markovian states are elimianted from the resulting MA + * @param ignoreLabeling If true, the labeling of states is ignored during state elimination * * @return Model checking result */ dft_results checkDFT(storm::storage::DFT const& dft, property_vector const& properties, bool symred, std::set const& relevantEvents = {}, bool allowDCForRelevantEvents = true, double approximationError = 0.0, - storm::builder::ApproximationHeuristic approximationHeuristic = storm::builder::ApproximationHeuristic::DEPTH); + storm::builder::ApproximationHeuristic approximationHeuristic = storm::builder::ApproximationHeuristic::DEPTH, + bool eliminateChains = false, bool ignoreLabeling = false); /*! * Check the given markov model for the given properties. diff --git a/src/storm-dft/settings/DftSettings.cpp b/src/storm-dft/settings/DftSettings.cpp index 43f0b0f51..4ff3e2aaf 100644 --- a/src/storm-dft/settings/DftSettings.cpp +++ b/src/storm-dft/settings/DftSettings.cpp @@ -23,6 +23,7 @@ #include "storm-conv/settings/modules/JaniExportSettings.h" #include "storm-gspn/settings/modules/GSPNSettings.h" #include "storm-gspn/settings/modules/GSPNExportSettings.h" +#include "storm/settings/modules/TransformationSettings.h" namespace storm { @@ -37,6 +38,7 @@ namespace storm { storm::settings::addModule(); storm::settings::addModule(); storm::settings::addModule(); + storm::settings::addModule(); storm::settings::addModule(); storm::settings::addModule(); From 7b038db6d5ac57f13bd68947b916890c02dbab33 Mon Sep 17 00:00:00 2001 From: Alexander Bork Date: Wed, 17 Jul 2019 17:16:45 +0200 Subject: [PATCH 37/47] Fixed missing part for label preservation and added formula preservation check --- .../NonMarkovianChainTransformer.cpp | 55 +++++++++++++++++-- .../NonMarkovianChainTransformer.h | 19 +++++++ 2 files changed, 69 insertions(+), 5 deletions(-) diff --git a/src/storm/transformer/NonMarkovianChainTransformer.cpp b/src/storm/transformer/NonMarkovianChainTransformer.cpp index 0e5971cae..34254af39 100644 --- a/src/storm/transformer/NonMarkovianChainTransformer.cpp +++ b/src/storm/transformer/NonMarkovianChainTransformer.cpp @@ -2,6 +2,9 @@ #include "NonMarkovianChainTransformer.h" +#include "storm/logic/Formulas.h" +#include "storm/logic/FragmentSpecification.h" + #include "storm/storage/sparse/ModelComponents.h" #include "storm/adapters/RationalFunctionAdapter.h" #include "storm/models/sparse/StandardRewardModel.h" @@ -98,6 +101,9 @@ namespace storm { while (!queue.empty()) { auto currState = queue.front(); queue.pop(); + + auto currLabels = ma->getLabelsOfState(currState); + // Get predecessors from matrix typename storm::storage::SparseMatrix::rows entriesInRow = backwards.getRow( currState); @@ -105,11 +111,21 @@ namespace storm { entryIt != entryIte; ++entryIt) { uint_fast64_t predecessor = entryIt->getColumn(); if (!ma->isMarkovianState(predecessor) && !statesToKeep.count(predecessor)) { - if (!eliminationMapping.count(predecessor)) { - eliminationMapping[predecessor] = base_state; - queue.push(predecessor); - } else if (eliminationMapping[predecessor] != base_state) { - eliminationMapping.erase(predecessor); + if (!preserveLabels || currLabels == ma->getLabelsOfState(predecessor)) { + // If labels are not to be preserved or states are labeled the same + if (!eliminationMapping.count(predecessor)) { + eliminationMapping[predecessor] = base_state; + queue.push(predecessor); + } else if (eliminationMapping[predecessor] != base_state) { + eliminationMapping.erase(predecessor); + statesToKeep.insert(predecessor); + changedStates.push(predecessor); + } + } else { + // Labels are to be preserved and states have different labels + if (eliminationMapping.count(predecessor)) { + eliminationMapping.erase(predecessor); + } statesToKeep.insert(predecessor); changedStates.push(predecessor); } @@ -231,6 +247,35 @@ namespace storm { } } + template + bool NonMarkovianChainTransformer::preservesFormula( + storm::logic::Formula const &formula) { + storm::logic::FragmentSpecification fragment = storm::logic::propositional(); + + fragment.setProbabilityOperatorsAllowed(true); + fragment.setGloballyFormulasAllowed(true); + fragment.setReachabilityProbabilityFormulasAllowed(true); + fragment.setUntilFormulasAllowed(true); + + return formula.isInFragment(fragment); + } + + template + std::vector> + NonMarkovianChainTransformer::checkAndTransformFormulas( + std::vector> const &formulas) { + std::vector> result; + + for (auto const &f : formulas) { + if (preservesFormula(*f)) { + result.push_back(f); + } else { + STORM_LOG_INFO("Non-Markovian chain elimination does not preserve formula " << *f); + } + } + return result; + } + template class NonMarkovianChainTransformer; diff --git a/src/storm/transformer/NonMarkovianChainTransformer.h b/src/storm/transformer/NonMarkovianChainTransformer.h index ae6759a94..317be4b2d 100644 --- a/src/storm/transformer/NonMarkovianChainTransformer.h +++ b/src/storm/transformer/NonMarkovianChainTransformer.h @@ -1,4 +1,5 @@ #include "storm/models/sparse/MarkovAutomaton.h" +#include "storm/logic/Formula.h" namespace storm { namespace transformer { @@ -8,6 +9,7 @@ namespace storm { template> class NonMarkovianChainTransformer { public: + /** * Generates a model with the same basic behavior as the input, but eliminates non-Markovian chains. * If no non-determinism occurs, a CTMC is generated. @@ -21,6 +23,23 @@ namespace storm { models::sparse::MarkovAutomaton < ValueType, RewardModelType>> ma, bool preserveLabels = true ); + + /** + * Check if the property specified by the given formula is preserved by the transformation. + * + * @param formula the formula to check + * @return true, if the property is preserved + */ + static bool preservesFormula(storm::logic::Formula const &formula); + + /** + * Checks for the given formulae if the specified properties are preserved and removes formulae of properties which are not preserved. + * + * @param formulas + * @return vector containing all fomulae which are valid for the transformed model + */ + static std::vector> + checkAndTransformFormulas(std::vector> const &formulas); }; } } From 450e074c5b6d81a492cea0f579799f67c07b0580 Mon Sep 17 00:00:00 2001 From: Alexander Bork Date: Wed, 17 Jul 2019 17:25:28 +0200 Subject: [PATCH 38/47] Integrated non-Markovian state elimination into Storm MA modelchecking --- src/storm-cli-utilities/model-handling.h | 26 ++++++++++++++++--- .../modelchecker/dft/DFTModelChecker.cpp | 1 - src/storm-pars-cli/storm-pars.cpp | 14 ++++++++++ src/storm/api/transformation.h | 26 ++++++++++++++++++- 4 files changed, 62 insertions(+), 5 deletions(-) diff --git a/src/storm-cli-utilities/model-handling.h b/src/storm-cli-utilities/model-handling.h index 1d71c3639..61a1eead1 100644 --- a/src/storm-cli-utilities/model-handling.h +++ b/src/storm-cli-utilities/model-handling.h @@ -42,6 +42,7 @@ #include "storm/settings/modules/AbstractionSettings.h" #include "storm/settings/modules/ResourceSettings.h" #include "storm/settings/modules/ModelCheckerSettings.h" +#include "storm/settings/modules/TransformationSettings.h" #include "storm/storage/Qvbs.h" #include "storm/utility/Stopwatch.h" @@ -350,11 +351,18 @@ namespace storm { auto generalSettings = storm::settings::getModule(); auto bisimulationSettings = storm::settings::getModule(); auto ioSettings = storm::settings::getModule(); + auto transformationSettings = storm::settings::getModule(); std::pair>, bool> result = std::make_pair(model, false); if (result.first->isOfType(storm::models::ModelType::MarkovAutomaton)) { result.first = preprocessSparseMarkovAutomaton(result.first->template as>()); + if (transformationSettings.isChainEliminationSet() && + result.first->isOfType(storm::models::ModelType::MarkovAutomaton)) { + result.first = storm::transformer::NonMarkovianChainTransformer::eliminateNonmarkovianStates( + result.first->template as>(), + !transformationSettings.isIgnoreLabelingSet()); + } result.second = true; } @@ -629,19 +637,31 @@ namespace storm { template void verifyProperties(SymbolicInput const& input, std::function(std::shared_ptr const& formula, std::shared_ptr const& states)> const& verificationCallback, std::function const&)> const& postprocessingCallback = PostprocessingIdentity()) { + auto transformationSettings = storm::settings::getModule(); auto const& properties = input.preprocessedProperties ? input.preprocessedProperties.get() : input.properties; for (auto const& property : properties) { printModelCheckingProperty(property); + bool ignored = false; storm::utility::Stopwatch watch(true); std::unique_ptr result; try { - result = verificationCallback(property.getRawFormula(), property.getFilter().getStatesFormula()); + auto rawFormula = property.getRawFormula(); + if (transformationSettings.isChainEliminationSet() && + !storm::transformer::NonMarkovianChainTransformer::preservesFormula(*rawFormula)) { + STORM_LOG_WARN("Property is not preserved by elimination of non-markovian states."); + ignored = true; + } else { + result = verificationCallback(property.getRawFormula(), + property.getFilter().getStatesFormula()); + } } catch (storm::exceptions::BaseException const& ex) { STORM_LOG_WARN("Cannot handle property: " << ex.what()); } watch.stop(); - postprocessingCallback(result); - printResult(result, property, &watch); + if (!ignored) { + postprocessingCallback(result); + printResult(result, property, &watch); + } } } diff --git a/src/storm-dft/modelchecker/dft/DFTModelChecker.cpp b/src/storm-dft/modelchecker/dft/DFTModelChecker.cpp index 4af4c28c5..4a03692ab 100644 --- a/src/storm-dft/modelchecker/dft/DFTModelChecker.cpp +++ b/src/storm-dft/modelchecker/dft/DFTModelChecker.cpp @@ -8,7 +8,6 @@ #include "storm/modelchecker/results/ExplicitQuantitativeCheckResult.h" #include "storm/modelchecker/results/ExplicitQualitativeCheckResult.h" #include "storm/models/ModelType.h" -#include "storm/transformer/NonMarkovianChainTransformer.h" #include "storm-dft/builder/ExplicitDFTModelBuilder.h" #include "storm-dft/storage/dft/DFTIsomorphism.h" diff --git a/src/storm-pars-cli/storm-pars.cpp b/src/storm-pars-cli/storm-pars.cpp index fe5f50993..18b27627d 100644 --- a/src/storm-pars-cli/storm-pars.cpp +++ b/src/storm-pars-cli/storm-pars.cpp @@ -21,6 +21,7 @@ #include "storm/settings/modules/CoreSettings.h" #include "storm/settings/modules/IOSettings.h" #include "storm/settings/modules/BisimulationSettings.h" +#include "storm/settings/modules/TransformationSettings.h" #include "storm/exceptions/BaseException.h" #include "storm/exceptions/InvalidSettingsException.h" @@ -141,6 +142,7 @@ namespace storm { auto generalSettings = storm::settings::getModule(); auto bisimulationSettings = storm::settings::getModule(); auto parametricSettings = storm::settings::getModule(); + auto transformationSettings = storm::settings::getModule(); PreprocessResult result(model, false); @@ -153,6 +155,18 @@ namespace storm { result.model = storm::cli::preprocessSparseModelBisimulation(result.model->template as>(), input, bisimulationSettings); result.changed = true; } + + if (transformationSettings.isChainEliminationSet() && + model->isOfType(storm::models::ModelType::MarkovAutomaton)) { + auto eliminationResult = storm::api::eliminateNonMarkovianChains( + result.model->template as>(), + storm::api::extractFormulasFromProperties(input.properties), + transformationSettings.isIgnoreLabelingSet()); + result.model = eliminationResult.first; + // Set transformed properties as new properties in input + result.formulas = eliminationResult.second; + result.changed = true; + } if (parametricSettings.transformContinuousModel() && (model->isOfType(storm::models::ModelType::Ctmc) || model->isOfType(storm::models::ModelType::MarkovAutomaton))) { auto transformResult = storm::api::transformContinuousToDiscreteTimeSparseModel(std::move(*model->template as>()), storm::api::extractFormulasFromProperties(input.properties)); diff --git a/src/storm/api/transformation.h b/src/storm/api/transformation.h index 0fb25b2f2..4dcaf7f61 100644 --- a/src/storm/api/transformation.h +++ b/src/storm/api/transformation.h @@ -2,6 +2,7 @@ #include "storm/transformer/ContinuousToDiscreteTimeModelTransformer.h" #include "storm/transformer/SymbolicToSparseTransformer.h" +#include "storm/transformer/NonMarkovianChainTransformer.h" #include "storm/utility/macros.h" #include "storm/utility/builder.h" @@ -10,7 +11,30 @@ namespace storm { namespace api { - + + /*! + * Eliminates chains of non-Markovian states from a given Markov Automaton + */ + template + std::pair>, std::vector>> + eliminateNonMarkovianChains(std::shared_ptr> const &ma, + std::vector> const &formulas, + bool ignoreLabeling) { + + auto newFormulas = storm::transformer::NonMarkovianChainTransformer::checkAndTransformFormulas( + formulas); + STORM_LOG_WARN_COND(newFormulas.size() == formulas.size(), + "The state elimination does not preserve all properties."); + STORM_LOG_WARN_COND(!ignoreLabeling, + "Labels are ignored for the state elimination. This may cause incorrect results."); + return std::make_pair( + storm::transformer::NonMarkovianChainTransformer::eliminateNonmarkovianStates(ma, + !ignoreLabeling), + newFormulas); + + } + + /*! * Transforms the given continuous model to a discrete time model. * If such a transformation does not preserve one of the given formulas, a warning is issued. From adf07416dc7bb98394d7cb62ad3985c826c48dc0 Mon Sep 17 00:00:00 2001 From: Alexander Bork Date: Thu, 25 Jul 2019 12:43:04 +0200 Subject: [PATCH 39/47] Added preservation of time bounded until formulae --- src/storm/transformer/NonMarkovianChainTransformer.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/storm/transformer/NonMarkovianChainTransformer.cpp b/src/storm/transformer/NonMarkovianChainTransformer.cpp index 34254af39..3170698c9 100644 --- a/src/storm/transformer/NonMarkovianChainTransformer.cpp +++ b/src/storm/transformer/NonMarkovianChainTransformer.cpp @@ -256,6 +256,7 @@ namespace storm { fragment.setGloballyFormulasAllowed(true); fragment.setReachabilityProbabilityFormulasAllowed(true); fragment.setUntilFormulasAllowed(true); + fragment.setTimeBoundedUntilFormulasAllowed(true); return formula.isInFragment(fragment); } From 541e582934c0d72623f89bbdc5503919b3d8e34f Mon Sep 17 00:00:00 2001 From: Alexander Bork Date: Wed, 7 Aug 2019 12:57:25 +0200 Subject: [PATCH 40/47] Added support for BEs with probabilities in Galileo parser --- src/storm-dft/parser/DFTGalileoParser.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/storm-dft/parser/DFTGalileoParser.cpp b/src/storm-dft/parser/DFTGalileoParser.cpp index 0062abce6..1c97653af 100644 --- a/src/storm-dft/parser/DFTGalileoParser.cpp +++ b/src/storm-dft/parser/DFTGalileoParser.cpp @@ -302,6 +302,24 @@ namespace storm { case Constant: if (storm::utility::isZero(firstValDistribution) || storm::utility::isOne(firstValDistribution)) { return builder.addBasicElementProbability(parseName(name), firstValDistribution, dormancyFactor, false); // TODO set transient BEs + } else { + // Model constant BEs with probability 0 < p < 1 + bool success = true; + if (!builder.nameInUse("constantBeTrigger")) { + // Use a unique constantly failed element that triggers failsafe elements probabilistically + success = success && builder.addBasicElementProbability("constantBeTrigger", + storm::utility::one(), + storm::utility::one(), + false); + } + std::vector childNames; + childNames.push_back("constantBeTrigger"); + success = success && + builder.addBasicElementProbability(parseName(name), storm::utility::zero(), + storm::utility::one(), false); + childNames.push_back(parseName(name)); + return success && + builder.addDepElement(parseName(name) + "_pdep", childNames, firstValDistribution); } STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Constant distribution is not supported for basic element '" << name << "' in line " << lineNo << "."); break; From 4c20495a204880a1f8afe6598e112fa34e4e28fc Mon Sep 17 00:00:00 2001 From: Alexander Bork Date: Wed, 7 Aug 2019 16:28:52 +0200 Subject: [PATCH 41/47] Adjusted tests to removal of mandatory state space reduction --- .../storm-dft/api/DftModelCheckerTest.cpp | 57 +++++++++++++++---- 1 file changed, 46 insertions(+), 11 deletions(-) diff --git a/src/test/storm-dft/api/DftModelCheckerTest.cpp b/src/test/storm-dft/api/DftModelCheckerTest.cpp index bb37c29c7..aad514e79 100644 --- a/src/test/storm-dft/api/DftModelCheckerTest.cpp +++ b/src/test/storm-dft/api/DftModelCheckerTest.cpp @@ -152,17 +152,28 @@ namespace { } TYPED_TEST(DftModelCheckerTest, FdepMTTF) { - double result = this->analyzeMTTF(STORM_TEST_RESOURCES_DIR "/dft/fdep2.dft"); - EXPECT_FLOAT_EQ(result, 2); - result = this->analyzeMTTF(STORM_TEST_RESOURCES_DIR "/dft/fdep3.dft"); - EXPECT_FLOAT_EQ(result, 2.5); if (this->getConfig().useMod) { EXPECT_THROW(this->analyzeMTTF(STORM_TEST_RESOURCES_DIR "/dft/fdep.dft"), storm::exceptions::NotSupportedException); +EXPECT_THROW(this-> +analyzeMTTF(STORM_TEST_RESOURCES_DIR +"/dft/fdep2.dft"), storm::exceptions::NotSupportedException); +EXPECT_THROW(this-> +analyzeMTTF(STORM_TEST_RESOURCES_DIR +"/dft/fdep3.dft"), storm::exceptions::NotSupportedException); EXPECT_THROW(this->analyzeMTTF(STORM_TEST_RESOURCES_DIR "/dft/fdep4.dft"), storm::exceptions::NotSupportedException); EXPECT_THROW(this->analyzeMTTF(STORM_TEST_RESOURCES_DIR "/dft/fdep5.dft"), storm::exceptions::NotSupportedException); } else { - result = this->analyzeMTTF(STORM_TEST_RESOURCES_DIR "/dft/fdep.dft"); +double result = this->analyzeMTTF(STORM_TEST_RESOURCES_DIR +"/dft/fdep.dft"); EXPECT_FLOAT_EQ(result, 2 / 3.0); +result = this->analyzeMTTF(STORM_TEST_RESOURCES_DIR +"/dft/fdep2.dft"); +EXPECT_FLOAT_EQ(result, +2); +result = this->analyzeMTTF(STORM_TEST_RESOURCES_DIR +"/dft/fdep3.dft"); +EXPECT_FLOAT_EQ(result, +2.5); result = this->analyzeMTTF(STORM_TEST_RESOURCES_DIR "/dft/fdep4.dft"); EXPECT_FLOAT_EQ(result, 1); result = this->analyzeMTTF(STORM_TEST_RESOURCES_DIR "/dft/fdep5.dft"); @@ -173,13 +184,24 @@ namespace { TYPED_TEST(DftModelCheckerTest, PdepMTTF) { double result = this->analyzeMTTF(STORM_TEST_RESOURCES_DIR "/dft/pdep.dft"); EXPECT_FLOAT_EQ(result, 8 / 3.0); - result = this->analyzeMTTF(STORM_TEST_RESOURCES_DIR "/dft/pdep3.dft"); - EXPECT_FLOAT_EQ(result, 67 / 24.0); + if (this->getConfig().useMod) { - result = this->analyzeMTTF(STORM_TEST_RESOURCES_DIR "/dft/pdep2.dft"); - EXPECT_FLOAT_EQ(result, 38 / 15.0); +EXPECT_THROW(this-> +analyzeMTTF(STORM_TEST_RESOURCES_DIR +"/dft/pdep2.dft"), storm::exceptions::NotSupportedException); +EXPECT_THROW(this-> +analyzeMTTF(STORM_TEST_RESOURCES_DIR +"/dft/pdep3.dft"), storm::exceptions::NotSupportedException); EXPECT_THROW(this->analyzeMTTF(STORM_TEST_RESOURCES_DIR "/dft/pdep4.dft"), storm::exceptions::NotSupportedException); } else { +result = this->analyzeMTTF(STORM_TEST_RESOURCES_DIR +"/dft/pdep2.dft"); +EXPECT_FLOAT_EQ(result, +38 / 15.0); +result = this->analyzeMTTF(STORM_TEST_RESOURCES_DIR +"/dft/pdep3.dft"); +EXPECT_FLOAT_EQ(result, +67 / 24.0); result = this->analyzeMTTF(STORM_TEST_RESOURCES_DIR "/dft/pdep4.dft"); EXPECT_EQ(result, storm::utility::infinity()); } @@ -215,8 +237,6 @@ namespace { EXPECT_FLOAT_EQ(result, 6); result = this->analyzeMTTF(STORM_TEST_RESOURCES_DIR "/dft/seq5.dft"); EXPECT_EQ(result, storm::utility::infinity()); - result = this->analyzeMTTF(STORM_TEST_RESOURCES_DIR "/dft/seq6.dft"); - EXPECT_FLOAT_EQ(result, 30000); result = this->analyzeMTTF(STORM_TEST_RESOURCES_DIR "/dft/mutex.dft"); EXPECT_FLOAT_EQ(result, 0.5); @@ -224,6 +244,21 @@ namespace { EXPECT_FLOAT_EQ(result, storm::utility::infinity()); result = this->analyzeMTTF(STORM_TEST_RESOURCES_DIR "/dft/mutex3.dft"); EXPECT_FLOAT_EQ(result, storm::utility::infinity()); +if (this-> + +getConfig() + +.useMod){ +EXPECT_THROW(this-> +analyzeMTTF(STORM_TEST_RESOURCES_DIR +"/dft/seq6.dft"), storm::exceptions::NotSupportedException); +} +else { +result = this->analyzeMTTF(STORM_TEST_RESOURCES_DIR +"/dft/seq6.dft"); +EXPECT_FLOAT_EQ(result, +30000); +} } TYPED_TEST(DftModelCheckerTest, HecsReliability) { From 628331fda3ae2ca30ae6413b51c83e4d61abbbca Mon Sep 17 00:00:00 2001 From: Alexander Bork Date: Wed, 7 Aug 2019 16:30:21 +0200 Subject: [PATCH 42/47] Fixed error that SMT solver was always used in the FDEP conflict search --- src/storm-dft-cli/storm-dft.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/storm-dft-cli/storm-dft.cpp b/src/storm-dft-cli/storm-dft.cpp index 1cc9a02e2..34bd53b69 100644 --- a/src/storm-dft-cli/storm-dft.cpp +++ b/src/storm-dft-cli/storm-dft.cpp @@ -117,13 +117,17 @@ void processOptions() { "Lower bound: " << std::to_string(preResults.lowerBEBound) << std::endl << "Upper bound: " << std::to_string(preResults.upperBEBound) << std::endl); - preResults.fdepConflicts = storm::dft::utility::FDEPConflictFinder::getDependencyConflicts(*dft, true, + preResults.fdepConflicts = storm::dft::utility::FDEPConflictFinder::getDependencyConflicts(*dft, useSMT, solverTimeout); - STORM_LOG_DEBUG("========================================" << std::endl << - "FDEP CONFLICTS" << std::endl << - "========================================" - << std::endl); + if (preResults.fdepConflicts.empty()) { + STORM_LOG_DEBUG("No FDEP conflicts found" << std::endl); + } else { + STORM_LOG_DEBUG("========================================" << std::endl << + "FDEP CONFLICTS" << std::endl << + "========================================" + << std::endl); + } for (auto pair: preResults.fdepConflicts) { STORM_LOG_DEBUG("Conflict between " << dft->getElement(pair.first)->name() << " and " << dft->getElement(pair.second)->name() << std::endl); From a25707134665a4147e0e2a1ec8a509e2bf656e9e Mon Sep 17 00:00:00 2001 From: Alexander Bork Date: Wed, 7 Aug 2019 17:10:47 +0200 Subject: [PATCH 43/47] Added option to transform a DFT to only use one unique constantly failed BE --- src/storm-dft-cli/storm-dft.cpp | 14 +++++--------- .../settings/modules/FaultTreeSettings.cpp | 7 +++++++ src/storm-dft/settings/modules/FaultTreeSettings.h | 8 ++++++++ 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/src/storm-dft-cli/storm-dft.cpp b/src/storm-dft-cli/storm-dft.cpp index 34bd53b69..d5f4bba36 100644 --- a/src/storm-dft-cli/storm-dft.cpp +++ b/src/storm-dft-cli/storm-dft.cpp @@ -56,6 +56,11 @@ void processOptions() { storm::api::exportDFTToJsonFile(*dft, dftIOSettings.getExportJsonFilename()); } + // Limit to one constantly failed BE + if (faultTreeSettings.isUniqueFailedBE()) { + dft = dftTransformator.transformUniqueFailedBe(*dft); + } + // Eliminate non-binary dependencies if (!dft->getDependencies().empty()) { dft = dftTransformator.transformBinaryFDEPs(*dft); @@ -82,10 +87,6 @@ void processOptions() { // SMT if (dftIOSettings.isExportToSmt()) { - dft = dftTransformator.transformUniqueFailedBe(*dft); - if (!dft->getDependencies().empty()) { - dft = dftTransformator.transformBinaryFDEPs(*dft); - } // Export to smtlib2 storm::api::exportDFTToSMT(*dft, dftIOSettings.getExportSmtFilename()); return; @@ -98,11 +99,6 @@ void processOptions() { if (faultTreeSettings.solveWithSMT()) { useSMT = true; STORM_PRINT("Use SMT for preprocessing" << std::endl) - dft = dftTransformator.transformUniqueFailedBe(*dft); - if (!dft->getDependencies().empty()) { - // Making the constantly failed BE unique may introduce non-binary FDEPs - dft = dftTransformator.transformBinaryFDEPs(*dft); - } } #endif diff --git a/src/storm-dft/settings/modules/FaultTreeSettings.cpp b/src/storm-dft/settings/modules/FaultTreeSettings.cpp index b45c37498..3fa4d6529 100644 --- a/src/storm-dft/settings/modules/FaultTreeSettings.cpp +++ b/src/storm-dft/settings/modules/FaultTreeSettings.cpp @@ -26,6 +26,7 @@ namespace storm { const std::string FaultTreeSettings::approximationHeuristicOptionName = "approximationheuristic"; const std::string FaultTreeSettings::maxDepthOptionName = "maxdepth"; const std::string FaultTreeSettings::firstDependencyOptionName = "firstdep"; + const std::string FaultTreeSettings::uniqueFailedBEOptionName = "uniquefailedbe"; #ifdef STORM_HAVE_Z3 const std::string FaultTreeSettings::solveWithSmtOptionName = "smt"; #endif @@ -53,6 +54,8 @@ namespace storm { {"depth", "probability", "bounddifference"})).build()).build()); this->addOption(storm::settings::OptionBuilder(moduleName, maxDepthOptionName, false, "Maximal depth for state space exploration.").addArgument( storm::settings::ArgumentBuilder::createUnsignedIntegerArgument("depth", "The maximal depth.").build()).build()); + this->addOption(storm::settings::OptionBuilder(moduleName, uniqueFailedBEOptionName, false, + "Use a unique constantly failed BE.").build()); #ifdef STORM_HAVE_Z3 this->addOption(storm::settings::OptionBuilder(moduleName, solveWithSmtOptionName, true, "Solve the DFT with SMT.").build()); #endif @@ -115,6 +118,10 @@ namespace storm { return this->getOption(firstDependencyOptionName).getHasOptionBeenSet(); } + bool FaultTreeSettings::isUniqueFailedBE() const { + return this->getOption(uniqueFailedBEOptionName).getHasOptionBeenSet(); + } + #ifdef STORM_HAVE_Z3 bool FaultTreeSettings::solveWithSMT() const { diff --git a/src/storm-dft/settings/modules/FaultTreeSettings.h b/src/storm-dft/settings/modules/FaultTreeSettings.h index 5ed230bfb..6c5298f7b 100644 --- a/src/storm-dft/settings/modules/FaultTreeSettings.h +++ b/src/storm-dft/settings/modules/FaultTreeSettings.h @@ -103,6 +103,13 @@ namespace storm { */ bool isTakeFirstDependency() const; + /*! + * Retrieves whether the DFT should be transformed to contain at most one constantly failed BE. + * + * @return True iff the option was set. + */ + bool isUniqueFailedBE() const; + #ifdef STORM_HAVE_Z3 /*! @@ -134,6 +141,7 @@ namespace storm { static const std::string approximationHeuristicOptionName; static const std::string maxDepthOptionName; static const std::string firstDependencyOptionName; + static const std::string uniqueFailedBEOptionName; #ifdef STORM_HAVE_Z3 static const std::string solveWithSmtOptionName; #endif From 2ec921a6835c0a67626eb12b8ad65a73f53391bb Mon Sep 17 00:00:00 2001 From: Alexander Bork Date: Wed, 7 Aug 2019 18:08:27 +0200 Subject: [PATCH 44/47] Added support for constantly failed BEs in the model generation --- .../builder/ExplicitDFTModelBuilder.cpp | 20 +++ .../generator/DftNextStateGenerator.cpp | 135 +++++++++++++----- .../generator/DftNextStateGenerator.h | 20 +++ 3 files changed, 136 insertions(+), 39 deletions(-) diff --git a/src/storm-dft/builder/ExplicitDFTModelBuilder.cpp b/src/storm-dft/builder/ExplicitDFTModelBuilder.cpp index 0f3196516..5275ed7e7 100644 --- a/src/storm-dft/builder/ExplicitDFTModelBuilder.cpp +++ b/src/storm-dft/builder/ExplicitDFTModelBuilder.cpp @@ -167,6 +167,26 @@ namespace storm { STORM_LOG_ASSERT(stateStorage.initialStateIndices.size() == 1, "Only one initial state assumed."); initialStateIndex = stateStorage.initialStateIndices[0]; STORM_LOG_TRACE("Initial state: " << initialStateIndex); + + // DFT may be instantly failed due to a constant failure + // in this case a model only consisting of the uniqueFailedState suffices + if (initialStateIndex == 0 && this->uniqueFailedState) { + modelComponents.markovianStates.resize(1); + modelComponents.deterministicModel = generator.isDeterministicModel(); + + STORM_LOG_TRACE("Markovian states: " << modelComponents.markovianStates); + STORM_LOG_DEBUG("Model has 1 state"); + STORM_LOG_DEBUG( + "Model is " << (generator.isDeterministicModel() ? "deterministic" : "non-deterministic")); + + // Build transition matrix + modelComponents.transitionMatrix = matrixBuilder.builder.build(1, 1); + STORM_LOG_TRACE("Transition matrix: " << std::endl << modelComponents.transitionMatrix); + + buildLabeling(); + return; + } + // Initialize heuristic values for inital state STORM_LOG_ASSERT(!statesNotExplored.at(initialStateIndex).second, "Heuristic for initial state is already initialized"); ExplorationHeuristicPointer heuristic; diff --git a/src/storm-dft/generator/DftNextStateGenerator.cpp b/src/storm-dft/generator/DftNextStateGenerator.cpp index bfbc870ba..d8fcb1072 100644 --- a/src/storm-dft/generator/DftNextStateGenerator.cpp +++ b/src/storm-dft/generator/DftNextStateGenerator.cpp @@ -22,11 +22,53 @@ namespace storm { template std::vector DftNextStateGenerator::getInitialStates(StateToIdCallback const& stateToIdCallback) { DFTStatePointer initialState = std::make_shared>(mDft, mStateGenerationInfo, 0); + size_t constFailedBeCounter = 0; + std::shared_ptr const> constFailedBE = nullptr; + for (auto &be : mDft.getBasicElements()) { + if (be->type() == storm::storage::DFTElementType::BE_CONST) { + auto constBe = std::static_pointer_cast const>(be); + if (constBe->failed()) { + constFailedBeCounter++; + STORM_LOG_THROW(constFailedBeCounter < 2, storm::exceptions::NotSupportedException, + "DFTs with more than one constantly failed BE are not supported"); + constFailedBE = constBe; + } + } + } + StateType id; + if (constFailedBeCounter == 0) { + // Register initial state + id = stateToIdCallback(initialState); + } else { + initialState->letNextBEFail(constFailedBE->id(), false); + // Propagate the constant failure to reach the real initial state + storm::storage::DFTStateSpaceGenerationQueues queues; + propagateFailure(initialState, constFailedBE, queues); + + if (initialState->hasFailed(mDft.getTopLevelIndex()) && uniqueFailedState) { + propagateFailsafe(initialState, constFailedBE, queues); + + // Update failable dependencies + initialState->updateFailableDependencies(constFailedBE->id()); + initialState->updateDontCareDependencies(constFailedBE->id()); + initialState->updateFailableInRestrictions(constFailedBE->id()); + + // Unique failed state + id = 0; + } else { + propagateFailsafe(initialState, constFailedBE, queues); - // Register initial state - StateType id = stateToIdCallback(initialState); + // Update failable dependencies + initialState->updateFailableDependencies(constFailedBE->id()); + initialState->updateDontCareDependencies(constFailedBE->id()); + initialState->updateFailableInRestrictions(constFailedBE->id()); + + id = stateToIdCallback(initialState); + } + } initialState->setId(id); + return {id}; } @@ -90,31 +132,7 @@ namespace storm { // Propagate storm::storage::DFTStateSpaceGenerationQueues queues; - // Propagate failure - for (DFTGatePointer parent : nextBE->parents()) { - if (newState->isOperational(parent->id())) { - queues.propagateFailure(parent); - } - } - // Propagate failures - while (!queues.failurePropagationDone()) { - DFTGatePointer next = queues.nextFailurePropagation(); - next->checkFails(*newState, queues); - newState->updateFailableDependencies(next->id()); - newState->updateFailableInRestrictions(next->id()); - } - - // Check restrictions - for (DFTRestrictionPointer restr : nextBE->restrictions()) { - queues.checkRestrictionLater(restr); - } - // Check restrictions - while(!queues.restrictionChecksDone()) { - DFTRestrictionPointer next = queues.nextRestrictionCheck(); - next->checkFails(*newState, queues); - newState->updateFailableDependencies(next->id()); - newState->updateFailableInRestrictions(next->id()); - } + propagateFailure(newState, nextBE, queues); newState->updateRemainingRelevantEvents(); @@ -137,18 +155,7 @@ namespace storm { // Use unique failed state newStateId = 0; } else { - // Propagate failsafe - while (!queues.failsafePropagationDone()) { - DFTGatePointer next = queues.nextFailsafePropagation(); - next->checkFailsafe(*newState, queues); - } - - // Propagate dont cares - // Relevance is considered for each element independently - while (!queues.dontCarePropagationDone()) { - DFTElementPointer next = queues.nextDontCarePropagation(); - next->checkDontCareAnymore(*newState, queues); - } + propagateFailsafe(newState, nextBE, queues); // Update failable dependencies newState->updateFailableDependencies(nextBE->id()); @@ -222,6 +229,56 @@ namespace storm { return result; } + template + void DftNextStateGenerator::propagateFailure(DFTStatePointer newState, + std::shared_ptr const> &nextBE, + storm::storage::DFTStateSpaceGenerationQueues &queues) { + // Propagate failure + for (DFTGatePointer parent : nextBE->parents()) { + if (newState->isOperational(parent->id())) { + queues.propagateFailure(parent); + } + } + // Propagate failures + while (!queues.failurePropagationDone()) { + DFTGatePointer next = queues.nextFailurePropagation(); + next->checkFails(*newState, queues); + newState->updateFailableDependencies(next->id()); + newState->updateFailableInRestrictions(next->id()); + } + + // Check restrictions + for (DFTRestrictionPointer restr : nextBE->restrictions()) { + queues.checkRestrictionLater(restr); + } + // Check restrictions + while (!queues.restrictionChecksDone()) { + DFTRestrictionPointer next = queues.nextRestrictionCheck(); + next->checkFails(*newState, queues); + newState->updateFailableDependencies(next->id()); + newState->updateFailableInRestrictions(next->id()); + } + + } + + template + void DftNextStateGenerator::propagateFailsafe(DFTStatePointer newState, + std::shared_ptr const> &nextBE, + storm::storage::DFTStateSpaceGenerationQueues &queues) { + // Propagate failsafe + while (!queues.failsafePropagationDone()) { + DFTGatePointer next = queues.nextFailsafePropagation(); + next->checkFailsafe(*newState, queues); + } + + // Propagate dont cares + // Relevance is considered for each element independently + while (!queues.dontCarePropagationDone()) { + DFTElementPointer next = queues.nextDontCarePropagation(); + next->checkDontCareAnymore(*newState, queues); + } + } + template StateBehavior DftNextStateGenerator::createMergeFailedState(StateToIdCallback const& stateToIdCallback) { this->uniqueFailedState = true; diff --git a/src/storm-dft/generator/DftNextStateGenerator.h b/src/storm-dft/generator/DftNextStateGenerator.h index 9fab2ee1f..4a1e45308 100644 --- a/src/storm-dft/generator/DftNextStateGenerator.h +++ b/src/storm-dft/generator/DftNextStateGenerator.h @@ -42,6 +42,26 @@ namespace storm { */ StateBehavior createMergeFailedState(StateToIdCallback const& stateToIdCallback); + /** + * Propagate the failures in a given state if the given BE fails + * + * @param newState starting state of the propagation + * @param nextBE BE whose failure is propagated + */ + void + propagateFailure(DFTStatePointer newState, std::shared_ptr const> &nextBE, + storm::storage::DFTStateSpaceGenerationQueues &queues); + + /** + * Propagate the failsafe state in a given state if the given BE fails + * + * @param newState starting state of the propagation + * @param nextBE BE whose failure is propagated + */ + void + propagateFailsafe(DFTStatePointer newState, std::shared_ptr const> &nextBE, + storm::storage::DFTStateSpaceGenerationQueues &queues); + private: StateBehavior exploreState(StateToIdCallback const& stateToIdCallback, bool exploreDependencies); From 3473a930a2387eea980616f3204c069ff0dbeec6 Mon Sep 17 00:00:00 2001 From: Alexander Bork Date: Fri, 9 Aug 2019 13:09:48 +0200 Subject: [PATCH 45/47] Added hint towards uniquefailedbe flag in error message --- src/storm-dft/generator/DftNextStateGenerator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/storm-dft/generator/DftNextStateGenerator.cpp b/src/storm-dft/generator/DftNextStateGenerator.cpp index d8fcb1072..61596c0f8 100644 --- a/src/storm-dft/generator/DftNextStateGenerator.cpp +++ b/src/storm-dft/generator/DftNextStateGenerator.cpp @@ -30,7 +30,7 @@ namespace storm { if (constBe->failed()) { constFailedBeCounter++; STORM_LOG_THROW(constFailedBeCounter < 2, storm::exceptions::NotSupportedException, - "DFTs with more than one constantly failed BE are not supported"); + "DFTs with more than one constantly failed BE are not supported. Try using the option '--uniquefailedbe'."); constFailedBE = constBe; } } From 584dc6caa7024a05a3a3fb4f1fbf3fac61862fa2 Mon Sep 17 00:00:00 2001 From: Alexander Bork Date: Fri, 9 Aug 2019 13:33:13 +0200 Subject: [PATCH 46/47] Fixed error that matrix dimensions were to small if last columns have only 0 entries --- src/storm/transformer/NonMarkovianChainTransformer.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/storm/transformer/NonMarkovianChainTransformer.cpp b/src/storm/transformer/NonMarkovianChainTransformer.cpp index 3170698c9..be5e57858 100644 --- a/src/storm/transformer/NonMarkovianChainTransformer.cpp +++ b/src/storm/transformer/NonMarkovianChainTransformer.cpp @@ -166,10 +166,11 @@ namespace storm { } } + uint64_t newStateCount = ma->getNumberOfStates() - eliminationMapping.size(); // Build the new MA storm::storage::SparseMatrix newTransitionMatrix; storm::models::sparse::StateLabeling newStateLabeling( - ma->getNumberOfStates() - eliminationMapping.size()); + newStateCount); storm::storage::BitVector newMarkovianStates(ma->getNumberOfStates() - eliminationMapping.size(), false); std::vector newExitRates; @@ -230,7 +231,8 @@ namespace storm { ++currentRow; } } - newTransitionMatrix = matrixBuilder.build(); + // explicitly force dimensions of the matrix in case a column is missing + newTransitionMatrix = matrixBuilder.build(newStateCount, newStateCount, newStateCount); storm::storage::sparse::ModelComponents newComponents = storm::storage::sparse::ModelComponents( std::move(newTransitionMatrix), std::move(newStateLabeling)); From 49ca253cccb225d03030424b295e77d3a5e96975 Mon Sep 17 00:00:00 2001 From: Alexander Bork Date: Fri, 16 Aug 2019 12:21:24 +0200 Subject: [PATCH 47/47] Cleanup --- src/storm-dft-cli/storm-dft.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/storm-dft-cli/storm-dft.cpp b/src/storm-dft-cli/storm-dft.cpp index d5f4bba36..f14d3e6ca 100644 --- a/src/storm-dft-cli/storm-dft.cpp +++ b/src/storm-dft-cli/storm-dft.cpp @@ -92,7 +92,6 @@ void processOptions() { return; } - // TODO introduce some flags bool useSMT = false; uint64_t solverTimeout = 10; #ifdef STORM_HAVE_Z3