From e49582c7cf723fe4df3c6a18c5dcd3fe589fac66 Mon Sep 17 00:00:00 2001 From: Mavo <matthias.volk@rwth-aachen.de> Date: Wed, 18 May 2016 14:49:04 +0200 Subject: [PATCH 01/65] Replaced assert with STORM_LOG_ASSERT Former-commit-id: 692ae47b1bc78c9534b153ee3f8194db0c221e04 --- src/models/sparse/MarkovAutomaton.cpp | 12 ++--- src/models/sparse/Model.cpp | 4 +- src/models/sparse/StandardRewardModel.cpp | 26 +++++----- .../MILPPermissiveSchedulers.h | 18 +++---- .../PermissiveSchedulerPenalty.h | 2 +- src/permissivesched/PermissiveSchedulers.cpp | 4 +- src/permissivesched/PermissiveSchedulers.h | 2 +- .../SmtBasedPermissiveSchedulers.h | 14 +++--- src/solver/MinMaxLinearEquationSolver.h | 2 +- src/solver/OptimizationDirection.cpp | 4 +- .../stateelimination/StateEliminator.cpp | 2 +- src/storage/FlexibleSparseMatrix.cpp | 12 ++--- src/storage/SparseMatrix.cpp | 14 +++--- src/storage/SparseMatrix.h | 1 + .../bisimulation/BisimulationDecomposition.h | 2 +- src/storage/dft/DFT.cpp | 48 +++++++++---------- src/storage/dft/DFT.h | 14 +++--- src/storage/dft/DFTElementState.h | 18 +++++-- src/storage/dft/DFTIsomorphism.h | 41 ++++++++-------- src/storage/dft/DFTState.cpp | 38 +++++++-------- src/storage/dft/DFTState.h | 3 +- .../dft/DFTStateSpaceGenerationQueues.h | 2 +- src/storage/expressions/Variable.cpp | 5 +- src/storage/expressions/Variable.h | 3 +- src/storage/prism/Assignment.cpp | 3 +- src/storage/prism/Command.cpp | 3 +- src/storage/prism/Program.cpp | 2 +- src/storm-dyftee.cpp | 34 +++++++++++-- 28 files changed, 181 insertions(+), 152 deletions(-) diff --git a/src/models/sparse/MarkovAutomaton.cpp b/src/models/sparse/MarkovAutomaton.cpp index b3b416cfc..7b997293f 100644 --- a/src/models/sparse/MarkovAutomaton.cpp +++ b/src/models/sparse/MarkovAutomaton.cpp @@ -43,8 +43,8 @@ namespace storm { std::unordered_map<std::string, RewardModelType>&& rewardModels, boost::optional<std::vector<LabelSet>>&& optionalChoiceLabeling) : NondeterministicModel<ValueType, RewardModelType>(storm::models::ModelType::MarkovAutomaton, std::move(transitionMatrix), std::move(stateLabeling), std::move(rewardModels), std::move(optionalChoiceLabeling)), markovianStates(markovianStates), exitRates(std::move(exitRates)), closed(this->checkIsClosed()) { - assert(probabilities); - assert(this->getTransitionMatrix().isProbabilistic()); + STORM_LOG_ASSERT(probabilities, "Matrix must be probabilistic."); + STORM_LOG_ASSERT(this->getTransitionMatrix().isProbabilistic(), "Matrix must be probabilistic."); } template <typename ValueType, typename RewardModelType> @@ -244,10 +244,10 @@ namespace storm { // Get number of choices in current state uint_fast64_t numberChoices = this->getTransitionMatrix().getRowGroupIndices()[state + 1] - this->getTransitionMatrix().getRowGroupIndices()[state]; if (isMarkovianState(state)) { - assert(numberChoices == 1); + STORM_LOG_ASSERT(numberChoices == 1, "Wrong number of choices for markovian state."); } if (numberChoices > 1) { - assert(isProbabilisticState(state)); + STORM_LOG_ASSERT(isProbabilisticState(state), "State is not probabilistic."); return false; } } @@ -276,7 +276,7 @@ namespace storm { storm::solver::stateelimination::MAEliminator<storm::models::sparse::Dtmc<ValueType>> stateEliminator(flexibleMatrix, flexibleBackwardTransitions); for (uint_fast64_t state = 0; state < this->getNumberOfStates(); ++state) { - assert(!this->isHybridState(state)); + STORM_LOG_ASSERT(!this->isHybridState(state), "State is hybrid."); if (this->isProbabilisticState(state)) { // Eliminate this probabilistic state stateEliminator.eliminateState(state, true); @@ -293,7 +293,7 @@ namespace storm { // State is eliminated and can be discarded keepStates.set(state, false); } else { - assert(this->isMarkovianState(state)); + STORM_LOG_ASSERT(this->isMarkovianState(state), "State is not markovian."); // Copy transitions for (uint_fast64_t row = flexibleMatrix.getRowGroupIndices()[state]; row < flexibleMatrix.getRowGroupIndices()[state + 1]; ++row) { for (auto const& entry : flexibleMatrix.getRow(row)) { diff --git a/src/models/sparse/Model.cpp b/src/models/sparse/Model.cpp index 265f0d8b0..a47214753 100644 --- a/src/models/sparse/Model.cpp +++ b/src/models/sparse/Model.cpp @@ -91,7 +91,7 @@ namespace storm { template<typename ValueType, typename RewardModelType> RewardModelType& Model<ValueType, RewardModelType>::rewardModel(std::string const& rewardModelName) { - assert(this->hasRewardModel(rewardModelName)); + STORM_LOG_ASSERT(this->hasRewardModel(rewardModelName), "Model has no reward model."); return this->rewardModels.find(rewardModelName)->second; } @@ -117,7 +117,7 @@ namespace storm { if(this->hasRewardModel(rewardModelName)) { STORM_LOG_THROW(!(this->hasRewardModel(rewardModelName)), storm::exceptions::IllegalArgumentException, "A reward model with the given name '" << rewardModelName << "' already exists."); } - assert(newRewardModel.isCompatible(this->getNumberOfStates(), this->getTransitionMatrix().getRowCount())); + STORM_LOG_ASSERT(newRewardModel.isCompatible(this->getNumberOfStates(), this->getTransitionMatrix().getRowCount()), "New reward model is not compatible."); this->rewardModels.emplace(rewardModelName, newRewardModel); } diff --git a/src/models/sparse/StandardRewardModel.cpp b/src/models/sparse/StandardRewardModel.cpp index 4cab9bbb4..0c9b6de89 100644 --- a/src/models/sparse/StandardRewardModel.cpp +++ b/src/models/sparse/StandardRewardModel.cpp @@ -37,13 +37,13 @@ namespace storm { template<typename ValueType> std::vector<ValueType> const& StandardRewardModel<ValueType>::getStateRewardVector() const { - assert(this->hasStateRewards()); + STORM_LOG_ASSERT(this->hasStateRewards(), "No state rewards available."); return this->optionalStateRewardVector.get(); } template<typename ValueType> std::vector<ValueType>& StandardRewardModel<ValueType>::getStateRewardVector() { - assert(this->hasStateRewards()); + STORM_LOG_ASSERT(this->hasStateRewards(), "No state rewards available."); return this->optionalStateRewardVector.get(); } @@ -54,16 +54,16 @@ namespace storm { template<typename ValueType> ValueType const& StandardRewardModel<ValueType>::getStateReward(uint_fast64_t state) const { - assert(this->hasStateRewards()); - assert(state < this->optionalStateRewardVector.get().size()); + STORM_LOG_ASSERT(this->hasStateRewards(), "No state rewards available."); + STORM_LOG_ASSERT(state < this->optionalStateRewardVector.get().size(), "Invalid state."); return this->optionalStateRewardVector.get()[state]; } template<typename ValueType> template<typename T> void StandardRewardModel<ValueType>::setStateReward(uint_fast64_t state, T const & newReward) { - assert(this->hasStateRewards()); - assert(state < this->optionalStateRewardVector.get().size()); + STORM_LOG_ASSERT(this->hasStateRewards(), "No state rewards available."); + STORM_LOG_ASSERT(state < this->optionalStateRewardVector.get().size(), "Invalid state."); this->optionalStateRewardVector.get()[state] = newReward; } @@ -76,28 +76,28 @@ namespace storm { template<typename ValueType> std::vector<ValueType> const& StandardRewardModel<ValueType>::getStateActionRewardVector() const { - assert(this->hasStateActionRewards()); + STORM_LOG_ASSERT(this->hasStateActionRewards(), "No state action rewards available."); return this->optionalStateActionRewardVector.get(); } template<typename ValueType> std::vector<ValueType>& StandardRewardModel<ValueType>::getStateActionRewardVector() { - assert(this->hasStateActionRewards()); + STORM_LOG_ASSERT(this->hasStateActionRewards(), "No state action rewards available."); return this->optionalStateActionRewardVector.get(); } template<typename ValueType> ValueType const& StandardRewardModel<ValueType>::getStateActionReward(uint_fast64_t choiceIndex) const { - assert(this->hasStateActionRewards()); - assert(choiceIndex < this->optionalStateActionRewardVector.get().size()); + STORM_LOG_ASSERT(this->hasStateActionRewards(), "No state action rewards available."); + STORM_LOG_ASSERT(choiceIndex < this->optionalStateActionRewardVector.get().size(), "Invalid choiceIndex."); return this->optionalStateActionRewardVector.get()[choiceIndex]; } template<typename ValueType> template<typename T> void StandardRewardModel<ValueType>::setStateActionReward(uint_fast64_t choiceIndex, T const &newValue) { - assert(this->hasStateActionRewards()); - assert(choiceIndex < this->optionalStateActionRewardVector.get().size()); + STORM_LOG_ASSERT(this->hasStateActionRewards(), "No state action rewards available."); + STORM_LOG_ASSERT(choiceIndex < this->optionalStateActionRewardVector.get().size(), "Invalid choiceIndex."); this->optionalStateActionRewardVector.get()[choiceIndex] = newValue; } @@ -330,4 +330,4 @@ namespace storm { } } -} \ No newline at end of file +} diff --git a/src/permissivesched/MILPPermissiveSchedulers.h b/src/permissivesched/MILPPermissiveSchedulers.h index b0f6b4529..fe5a48e11 100644 --- a/src/permissivesched/MILPPermissiveSchedulers.h +++ b/src/permissivesched/MILPPermissiveSchedulers.h @@ -51,13 +51,13 @@ class MilpPermissiveSchedulerComputation : public PermissiveSchedulerComputation bool foundSolution() const override { - assert(mCalledOptimizer); + STORM_LOG_ASSERT(mCalledOptimizer, "Optimizer not called."); return !solver.isInfeasible(); } SubMDPPermissiveScheduler<RM> getScheduler() const override { - assert(mCalledOptimizer); - assert(foundSolution()); + STORM_LOG_ASSERT(mCalledOptimizer, "Optimizer not called."); + STORM_LOG_ASSERT(foundSolution(), "Solution not found."); SubMDPPermissiveScheduler<RM> result(this->mdp, true); @@ -103,7 +103,7 @@ class MilpPermissiveSchedulerComputation : public PermissiveSchedulerComputation */ void createVariables(PermissiveSchedulerPenalties const& penalties, storm::storage::BitVector const& relevantStates) { // We need the unique initial state later, so we get that one before looping. - assert(this->mdp.getInitialStates().getNumberOfSetBits() == 1); + STORM_LOG_ASSERT(this->mdp.getInitialStates().getNumberOfSetBits() == 1, "No unique initial state."); uint_fast64_t initialStateIndex = this->mdp.getInitialStates().getNextSetIndex(0); storm::expressions::Variable var; @@ -151,9 +151,9 @@ class MilpPermissiveSchedulerComputation : public PermissiveSchedulerComputation // (5) and (7) are omitted on purpose (-- we currenty do not support controllability of actions -- ) // (1) - assert(this->mdp.getInitialStates().getNumberOfSetBits() == 1); + STORM_LOG_ASSERT(this->mdp.getInitialStates().getNumberOfSetBits() == 1, "No unique initial state."); uint_fast64_t initialStateIndex = this->mdp.getInitialStates().getNextSetIndex(0); - assert(relevantStates[initialStateIndex]); + STORM_LOG_ASSERT(relevantStates[initialStateIndex], "Initial state not relevant."); if(lowerBound) { solver.addConstraint("c1", mProbVariables[initialStateIndex] >= solver.getConstant(boundary)); } else { @@ -206,9 +206,9 @@ class MilpPermissiveSchedulerComputation : public PermissiveSchedulerComputation std::string satstring = to_string(sat); // (8) if(relevantStates[entry.getColumn()]) { - assert(mGammaVariables.count(entry.getColumn()) > 0); - assert(mGammaVariables.count(s) > 0); - assert(mBetaVariables.count(sat) > 0); + STORM_LOG_ASSERT(mGammaVariables.count(entry.getColumn()) > 0, "Entry not found."); + STORM_LOG_ASSERT(mGammaVariables.count(s) > 0, "Entry not found."); + STORM_LOG_ASSERT(mBetaVariables.count(sat) > 0, "Entry not found."); solver.addConstraint("c8-" + satstring, mGammaVariables[entry.getColumn()] < mGammaVariables[s] + (solver.getConstant(1) - mBetaVariables[sat])); } } diff --git a/src/permissivesched/PermissiveSchedulerPenalty.h b/src/permissivesched/PermissiveSchedulerPenalty.h index bb680defa..f25f61ac1 100644 --- a/src/permissivesched/PermissiveSchedulerPenalty.h +++ b/src/permissivesched/PermissiveSchedulerPenalty.h @@ -30,7 +30,7 @@ namespace storm { } void set(uint_fast64_t state, uint_fast64_t action, double penalty) { - assert(penalty >= 1.0); + STORM_LOG_ASSERT(penalty >= 1.0, "Penalty too low."); if(penalty == 1.0) { auto it = mPenalties.find(std::make_pair(state, action)); if(it != mPenalties.end()) { diff --git a/src/permissivesched/PermissiveSchedulers.cpp b/src/permissivesched/PermissiveSchedulers.cpp index 21aa9227c..b35f9549c 100644 --- a/src/permissivesched/PermissiveSchedulers.cpp +++ b/src/permissivesched/PermissiveSchedulers.cpp @@ -17,7 +17,7 @@ namespace storm { template<typename RM> boost::optional<SubMDPPermissiveScheduler<RM>> computePermissiveSchedulerViaMILP(storm::models::sparse::Mdp<double, RM> const& mdp, storm::logic::ProbabilityOperatorFormula const& safeProp) { storm::modelchecker::SparsePropositionalModelChecker<storm::models::sparse::Mdp<double, RM>> propMC(mdp); - assert(safeProp.getSubformula().isEventuallyFormula()); + STORM_LOG_ASSERT(safeProp.getSubformula().isEventuallyFormula(), "No eventually formula."); auto backwardTransitions = mdp.getBackwardTransitions(); storm::storage::BitVector goalstates = propMC.check(safeProp.getSubformula().asEventuallyFormula().getSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector(); goalstates = storm::utility::graph::performProb1A(mdp, backwardTransitions, storm::storage::BitVector(goalstates.size(), true), goalstates); @@ -44,7 +44,7 @@ namespace storm { template<typename RM> boost::optional<SubMDPPermissiveScheduler<RM>> computePermissiveSchedulerViaSMT(storm::models::sparse::Mdp<double, RM> const& mdp, storm::logic::ProbabilityOperatorFormula const& safeProp) { storm::modelchecker::SparsePropositionalModelChecker<storm::models::sparse::Mdp<double, RM>> propMC(mdp); - assert(safeProp.getSubformula().isEventuallyFormula()); + STORM_LOG_ASSERT(safeProp.getSubformula().isEventuallyFormula(), "No eventually formula."); auto backwardTransitions = mdp.getBackwardTransitions(); storm::storage::BitVector goalstates = propMC.check(safeProp.getSubformula().asEventuallyFormula().getSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector(); goalstates = storm::utility::graph::performProb1A(mdp, backwardTransitions, storm::storage::BitVector(goalstates.size(), true), goalstates); diff --git a/src/permissivesched/PermissiveSchedulers.h b/src/permissivesched/PermissiveSchedulers.h index 7d0bdb3f7..eb09d4b24 100644 --- a/src/permissivesched/PermissiveSchedulers.h +++ b/src/permissivesched/PermissiveSchedulers.h @@ -32,7 +32,7 @@ namespace storm { } void disable(uint_fast64_t choiceIndex) { - assert(choiceIndex < enabledChoices.size()); + STORM_LOG_ASSERT(choiceIndex < enabledChoices.size(), "Invalid choiceIndex."); enabledChoices.set(choiceIndex, false); } diff --git a/src/permissivesched/SmtBasedPermissiveSchedulers.h b/src/permissivesched/SmtBasedPermissiveSchedulers.h index bfc1530b8..ed183866c 100644 --- a/src/permissivesched/SmtBasedPermissiveSchedulers.h +++ b/src/permissivesched/SmtBasedPermissiveSchedulers.h @@ -34,12 +34,12 @@ namespace storm { } bool foundSolution() const override { - assert(mPerformedSmtLoop); + STORM_LOG_ASSERT(mPerformedSmtLoop, "SMT loop not performed."); return mFoundSolution; } SubMDPPermissiveScheduler<RM> getScheduler() const override { - assert(foundSolution()); + STORM_LOG_ASSERT(foundSolution(), "Solution not found."); SubMDPPermissiveScheduler<RM> result(this->mdp, true); for(auto const& entry : multistrategyVariables) { if(!multistrategyVariablesToTakenMap.at(entry.second)) { @@ -98,9 +98,9 @@ namespace storm { // (4) and (7) are omitted on purpose (-- we currenty do not support controllability of actions -- ) // (1) - assert(this->mdp.getInitialStates().getNumberOfSetBits() == 1); + STORM_LOG_ASSERT(this->mdp.getInitialStates().getNumberOfSetBits() == 1, "No unique initial state."); uint_fast64_t initialStateIndex = this->mdp.getInitialStates().getNextSetIndex(0); - assert(relevantStates[initialStateIndex]); + STORM_LOG_ASSERT(relevantStates[initialStateIndex], "Initial state not relevant."); if(lowerBound) { solver.add(mProbVariables[initialStateIndex] >= manager.rational(boundary)); } else { @@ -162,9 +162,9 @@ namespace storm { // std::string satstring = to_string(sat); // // (8) // if(relevantStates[entry.getColumn()]) { -// assert(mGammaVariables.count(entry.getColumn()) > 0); -// assert(mGammaVariables.count(s) > 0); -// assert(mBetaVariables.count(sat) > 0); +// STORM_LOG_ASSERT(mGammaVariables.count(entry.getColumn()) > 0, "Entry not found."); +// STORM_LOG_ASSERT(mGammaVariables.count(s) > 0, "Entry not found."); +// STORM_LOG_ASSERT(mBetaVariables.count(sat) > 0, "Entry not found."); // solver.addConstraint("c8-" + satstring, mGammaVariables[entry.getColumn()] < mGammaVariables[s] + (solver.getConstant(1) - mBetaVariables[sat]) + mProbVariables[s]); // With rewards, we have to change this. // } // } diff --git a/src/solver/MinMaxLinearEquationSolver.h b/src/solver/MinMaxLinearEquationSolver.h index 71ea10ee2..a9c497d45 100644 --- a/src/solver/MinMaxLinearEquationSolver.h +++ b/src/solver/MinMaxLinearEquationSolver.h @@ -102,7 +102,7 @@ namespace storm { * Can only be called after the direction has been set. */ virtual void solveEquationSystem(std::vector<ValueType>& x, std::vector<ValueType> const& b, std::vector<ValueType>* multiplyResult = nullptr, std::vector<ValueType>* newX = nullptr) const { - assert(isSet(this->direction)); + STORM_LOG_ASSERT(isSet(this->direction), "Direction not set."); solveEquationSystem(convert(this->direction), x, b, multiplyResult, newX); } diff --git a/src/solver/OptimizationDirection.cpp b/src/solver/OptimizationDirection.cpp index cd88657b9..71d0760ed 100644 --- a/src/solver/OptimizationDirection.cpp +++ b/src/solver/OptimizationDirection.cpp @@ -1,6 +1,6 @@ #include "OptimizationDirection.h" #include <iostream> -#include <cassert> +#include "src/utility/macros.h" namespace storm { namespace solver { @@ -18,7 +18,7 @@ namespace storm { } OptimizationDirection convert(OptimizationDirectionSetting s) { - assert(isSet(s)); + STORM_LOG_ASSERT(isSet(s), "Setting is not set."); return static_cast<OptimizationDirection>(s); } diff --git a/src/solver/stateelimination/StateEliminator.cpp b/src/solver/stateelimination/StateEliminator.cpp index 01dc8a5af..6189cb2b0 100644 --- a/src/solver/stateelimination/StateEliminator.cpp +++ b/src/solver/stateelimination/StateEliminator.cpp @@ -70,7 +70,7 @@ namespace storm { // Skip the state itself as one of its predecessors. if (predecessor == state) { - assert(hasSelfLoop); + STORM_LOG_ASSERT(hasSelfLoop, "State has no self loop."); continue; } diff --git a/src/storage/FlexibleSparseMatrix.cpp b/src/storage/FlexibleSparseMatrix.cpp index 2e3a4e876..5ee31060e 100644 --- a/src/storage/FlexibleSparseMatrix.cpp +++ b/src/storage/FlexibleSparseMatrix.cpp @@ -51,15 +51,15 @@ namespace storm { template<typename ValueType> typename FlexibleSparseMatrix<ValueType>::row_type& FlexibleSparseMatrix<ValueType>::getRow(index_type rowGroup, index_type offset) { - assert(rowGroup < this->getRowGroupCount()); - assert(offset < this->getRowGroupSize(rowGroup)); + STORM_LOG_ASSERT(rowGroup < this->getRowGroupCount(), "Invalid rowGroup."); + STORM_LOG_ASSERT(offset < this->getRowGroupSize(rowGroup), "Invalid offset."); return getRow(rowGroupIndices[rowGroup] + offset); } template<typename ValueType> typename FlexibleSparseMatrix<ValueType>::row_type const& FlexibleSparseMatrix<ValueType>::getRow(index_type rowGroup, index_type offset) const { - assert(rowGroup < this->getRowGroupCount()); - assert(offset < this->getRowGroupSize(rowGroup)); + STORM_LOG_ASSERT(rowGroup < this->getRowGroupCount(), "Invalid rowGroup."); + STORM_LOG_ASSERT(offset < this->getRowGroupSize(rowGroup), "Invalid offset."); return getRow(rowGroupIndices[rowGroup] + offset); } @@ -112,7 +112,7 @@ namespace storm { this->columnCount = 0; for (auto const& row : this->data) { for (auto const& element : row) { - assert(!storm::utility::isZero(element.getValue())); + STORM_LOG_ASSERT(!storm::utility::isZero(element.getValue()), "Entry is 0."); ++this->nonzeroEntryCount; this->columnCount = std::max(element.getColumn() + 1, this->columnCount); } @@ -252,4 +252,4 @@ namespace storm { #endif } // namespace storage -} // namespace storm \ No newline at end of file +} // namespace storm diff --git a/src/storage/SparseMatrix.cpp b/src/storage/SparseMatrix.cpp index bfed8f49c..4b763e383 100644 --- a/src/storage/SparseMatrix.cpp +++ b/src/storage/SparseMatrix.cpp @@ -277,9 +277,9 @@ namespace storm { } maxColumn = std::max(maxColumn, elem.getColumn()); } - assert(changed || highestColumn == maxColumn); + STORM_LOG_ASSERT(changed || highestColumn == maxColumn, "Incorrect maximal column."); highestColumn = maxColumn; - assert(changed || lastColumn == columnsAndValues[columnsAndValues.size() - 1].getColumn()); + STORM_LOG_ASSERT(changed || lastColumn == columnsAndValues[columnsAndValues.size() - 1].getColumn(), "Incorrect last column."); lastColumn = columnsAndValues[columnsAndValues.size() - 1].getColumn(); if (changed) { @@ -305,10 +305,10 @@ namespace storm { return a.getColumn() < b.getColumn(); }); // Assert no equal elements - assert(std::is_sorted(columnsAndValues.begin() + rowIndications[i], columnsAndValues.begin() + endRows, + STORM_LOG_ASSERT(std::is_sorted(columnsAndValues.begin() + rowIndications[i], columnsAndValues.begin() + endRows, [](MatrixEntry<index_type, value_type> const& a, MatrixEntry<index_type, value_type> const& b) { return a.getColumn() <= b.getColumn(); - })); + }), "Columns not sorted."); } } } else { @@ -320,10 +320,10 @@ namespace storm { return a.getColumn() < b.getColumn(); }); // Assert no equal elements - assert(std::is_sorted(columnsAndValues.begin() + rowIndications[i], columnsAndValues.begin() + endRows, + STORM_LOG_ASSERT(std::is_sorted(columnsAndValues.begin() + rowIndications[i], columnsAndValues.begin() + endRows, [](MatrixEntry<index_type, value_type> const& a, MatrixEntry<index_type, value_type> const& b) { return a.getColumn() <= b.getColumn(); - })); + }), "Columns not sorted."); } } @@ -1368,7 +1368,7 @@ namespace storm { void SparseMatrix<ValueType>::printAsMatlabMatrix(std::ostream& out) const { // Iterate over all row groups. for (typename SparseMatrix<ValueType>::index_type group = 0; group < this->getRowGroupCount(); ++group) { - assert(this->getRowGroupSize(group) == 1); + STORM_LOG_ASSERT(this->getRowGroupSize(group) == 1, "Incorrect row group size."); for (typename SparseMatrix<ValueType>::index_type i = this->getRowGroupIndices()[group]; i < this->getRowGroupIndices()[group + 1]; ++i) { typename SparseMatrix<ValueType>::index_type nextIndex = this->rowIndications[i]; diff --git a/src/storage/SparseMatrix.h b/src/storage/SparseMatrix.h index 374859af5..c170d43a9 100644 --- a/src/storage/SparseMatrix.h +++ b/src/storage/SparseMatrix.h @@ -11,6 +11,7 @@ #include <boost/optional.hpp> #include "src/utility/OsDetection.h" +#include "src/utility/macros.h" #include "src/adapters/CarlAdapter.h" // Forward declaration for adapter classes. diff --git a/src/storage/bisimulation/BisimulationDecomposition.h b/src/storage/bisimulation/BisimulationDecomposition.h index 6507e3b07..6d86f2df9 100644 --- a/src/storage/bisimulation/BisimulationDecomposition.h +++ b/src/storage/bisimulation/BisimulationDecomposition.h @@ -120,7 +120,7 @@ namespace storm { } OptimizationDirection getOptimizationDirection() const { - assert(optimalityType); + STORM_LOG_ASSERT(optimalityType, "Optimality type not set."); return optimalityType.get(); } // A flag that indicates whether a measure driven initial partition is to be used. If this flag is set diff --git a/src/storage/dft/DFT.cpp b/src/storage/dft/DFT.cpp index 5c70c475d..5fdf44ab3 100644 --- a/src/storage/dft/DFT.cpp +++ b/src/storage/dft/DFT.cpp @@ -14,7 +14,7 @@ namespace storm { template<typename ValueType> DFT<ValueType>::DFT(DFTElementVector const& elements, DFTElementPointer const& tle) : mElements(elements), mNrOfBEs(0), mNrOfSpares(0), mTopLevelIndex(tle->id()), mMaxSpareChildCount(0) { - assert(elementIndicesCorrect()); + STORM_LOG_ASSERT(elementIndicesCorrect(), "Ids incorrect."); size_t nrRepresentatives = 0; for (auto& elem : mElements) { @@ -100,9 +100,9 @@ namespace storm { } else { // Generate information according to symmetries for (size_t symmetryIndex : symmetries.sortedSymmetries) { - assert(!visited[symmetryIndex]); + STORM_LOG_ASSERT(!visited[symmetryIndex], "Element already considered for symmetry."); auto const& symmetryGroup = symmetries.groups.at(symmetryIndex); - assert(!symmetryGroup.empty()); + STORM_LOG_ASSERT(!symmetryGroup.empty(), "No symmetry available."); // Insert all elements of first subtree of each symmetry size_t groupIndex = stateIndex; @@ -117,14 +117,14 @@ namespace storm { // Mirror symmetries size_t noSymmetricElements = symmetryGroup.front().size(); - assert(noSymmetricElements > 1); + STORM_LOG_ASSERT(noSymmetricElements > 1, "No symmetry available."); for (std::vector<size_t> symmetricElements : symmetryGroup) { - assert(symmetricElements.size() == noSymmetricElements); + STORM_LOG_ASSERT(symmetricElements.size() == noSymmetricElements, "No. of symmetric elements do not coincide."); if (visited[symmetricElements[1]]) { // Elements already mirrored for (size_t index : symmetricElements) { - assert(visited[index]); + STORM_LOG_ASSERT(visited[index], "Element not mirrored."); } continue; } @@ -143,13 +143,13 @@ namespace storm { generationInfo.addStateIndex(symmetricElement, index + offset * i); stateIndex += 2; - assert((activationIndex > 0) == isRepresentative(symmetricElement)); + STORM_LOG_ASSERT((activationIndex > 0) == isRepresentative(symmetricElement), "Bits for representative incorrect."); if (activationIndex > 0) { generationInfo.addSpareActivationIndex(symmetricElement, activationIndex + offset * i); ++stateIndex; } - assert((usageIndex > 0) == mElements[symmetricElement]->isSpareGate()); + STORM_LOG_ASSERT((usageIndex > 0) == mElements[symmetricElement]->isSpareGate(), "Bits for usage incorrect."); if (usageIndex > 0) { generationInfo.addSpareUsageIndex(symmetricElement, usageIndex + offset * i); stateIndex += generationInfo.usageInfoBits(); @@ -187,15 +187,15 @@ namespace storm { generationInfo.generateSymmetries(symmetries); STORM_LOG_TRACE(generationInfo); - assert(stateIndex == mStateVectorSize); - assert(visited.full()); + STORM_LOG_ASSERT(stateIndex == mStateVectorSize, "Id incorrect."); + STORM_LOG_ASSERT(visited.full(), "Not all elements considered."); return generationInfo; } template<typename ValueType> size_t DFT<ValueType>::generateStateInfo(DFTStateGenerationInfo& generationInfo, size_t id, storm::storage::BitVector& visited, size_t stateIndex) const { - assert(!visited[id]); + STORM_LOG_ASSERT(!visited[id], "Element already visited."); visited.set(id); // Reserve bits for element @@ -238,7 +238,7 @@ namespace storm { template<typename ValueType> std::vector<DFT<ValueType>> DFT<ValueType>::topModularisation() const { - assert(isGate(mTopLevelIndex)); + STORM_LOG_ASSERT(isGate(mTopLevelIndex), "Top level element is no gate."); auto const& children = getGate(mTopLevelIndex)->children(); std::map<size_t, std::vector<size_t>> subdfts; for(auto const& child : children) { @@ -250,7 +250,7 @@ namespace storm { if (isGate(child->id())) { isubdft = getGate(child->id())->independentSubDft(false); } else { - assert(isBasicElement(child->id())); + STORM_LOG_ASSERT(isBasicElement(child->id()), "Child is no BE."); if(getBasicElement(child->id())->hasIngoingDependencies()) { STORM_LOG_TRACE("child " << child->name() << "does not allow modularisation."); return {*this}; @@ -309,16 +309,16 @@ namespace storm { // Add rewritten elements for (std::vector<size_t> rewrites : rewriteIds) { - assert(rewrites.size() > 1); - assert(mElements[rewrites[1]]->hasParents()); - assert(mElements[rewrites[1]]->parents().front()->isGate()); + STORM_LOG_ASSERT(rewrites.size() > 1, "No rewritten elements."); + STORM_LOG_ASSERT(mElements[rewrites[1]]->hasParents(), "Rewritten elements has no parents."); + STORM_LOG_ASSERT(mElements[rewrites[1]]->parents().front()->isGate(), "Rewritten element has no parent gate."); DFTGatePointer originalParent = std::static_pointer_cast<DFTGate<ValueType>>(mElements[rewrites[1]]->parents().front()); std::string newParentName = builder.getUniqueName(originalParent->name()); // Accumulate children names std::vector<std::string> childrenNames; for (size_t i = 1; i < rewrites.size(); ++i) { - assert(mElements[rewrites[i]]->parents().front()->id() == originalParent->id()); // Children have the same father + STORM_LOG_ASSERT(mElements[rewrites[i]]->parents().front()->id() == originalParent->id(), "Children have the same father"); childrenNames.push_back(mElements[rewrites[i]]->name()); } @@ -390,7 +390,7 @@ namespace storm { stream << "}" << std::endl; return stream.str(); } - assert(it != mTopModule.end()); + STORM_LOG_ASSERT(it != mTopModule.end(), "Element not found."); stream << mElements[(*it)]->name(); ++it; while(it != mTopModule.end()) { @@ -403,7 +403,7 @@ namespace storm { stream << "[" << mElements[spareModule.first]->name() << "] = {"; if (!spareModule.second.empty()) { std::vector<size_t>::const_iterator it = spareModule.second.begin(); - assert(it != spareModule.second.end()); + STORM_LOG_ASSERT(it != spareModule.second.end(), "Element not found."); stream << mElements[(*it)]->name(); ++it; while(it != spareModule.second.end()) { @@ -494,13 +494,13 @@ namespace storm { template<typename ValueType> size_t DFT<ValueType>::getChild(size_t spareId, size_t nrUsedChild) const { - assert(mElements[spareId]->isSpareGate()); + STORM_LOG_ASSERT(mElements[spareId]->isSpareGate(), "Element is no spare."); return getGate(spareId)->children()[nrUsedChild]->id(); } template<typename ValueType> size_t DFT<ValueType>::getNrChild(size_t spareId, size_t childId) const { - assert(mElements[spareId]->isSpareGate()); + STORM_LOG_ASSERT(mElements[spareId]->isSpareGate(), "Element is no spare."); DFTElementVector children = getGate(spareId)->children(); for (size_t nrChild = 0; nrChild < children.size(); ++nrChild) { if (children[nrChild]->id() == childId) { @@ -551,8 +551,8 @@ namespace storm { } } - assert(isGate(index1)); - assert(isGate(index2)); + STORM_LOG_ASSERT(isGate(index1), "Element is no gate."); + STORM_LOG_ASSERT(isGate(index2), "Element is no gate."); std::vector<size_t> isubdft1 = getGate(index1)->independentSubDft(false); std::vector<size_t> isubdft2 = getGate(index2)->independentSubDft(false); if(isubdft1.empty() || isubdft2.empty() || isubdft1.size() != isubdft2.size()) { @@ -591,7 +591,7 @@ namespace storm { size_t childLeftId = spareLeft->children().at(i)->id(); size_t childRightId = spareRight->children().at(i)->id(); - assert(bijection.count(childLeftId) == 0); + STORM_LOG_ASSERT(bijection.count(childLeftId) == 0, "Child already part of bijection."); if (childLeftId == childRightId) { // Ignore shared child continue; diff --git a/src/storage/dft/DFT.h b/src/storage/dft/DFT.h index 6fdfd08ef..7b0177d07 100644 --- a/src/storage/dft/DFT.h +++ b/src/storage/dft/DFT.h @@ -118,7 +118,7 @@ namespace storm { if(representativeId == mTopLevelIndex) { return mTopModule; } else { - assert(mSpareModules.count(representativeId)>0); + STORM_LOG_ASSERT(mSpareModules.count(representativeId) > 0, "Representative not found."); return mSpareModules.find(representativeId)->second; } } @@ -145,7 +145,7 @@ namespace storm { * @param index The id of the element */ DFTElementCPointer getElement(size_t index) const { - assert(index < nrElements()); + STORM_LOG_ASSERT(index < nrElements(), "Index invalid."); return mElements[index]; } @@ -166,7 +166,7 @@ namespace storm { } std::shared_ptr<DFTBE<ValueType> const> getBasicElement(size_t index) const { - assert(isBasicElement(index)); + STORM_LOG_ASSERT(isBasicElement(index), "Element is no BE."); return std::static_pointer_cast<DFTBE<ValueType> const>(mElements[index]); } @@ -175,17 +175,17 @@ namespace storm { } std::shared_ptr<DFTGate<ValueType> const> getGate(size_t index) const { - assert(isGate(index)); + STORM_LOG_ASSERT(isGate(index), "Element is no gate."); return std::static_pointer_cast<DFTGate<ValueType> const>(mElements[index]); } std::shared_ptr<DFTDependency<ValueType> const> getDependency(size_t index) const { - assert(isDependency(index)); + STORM_LOG_ASSERT(isDependency(index), "Element is no dependency."); return std::static_pointer_cast<DFTDependency<ValueType> const>(mElements[index]); } std::shared_ptr<DFTRestriction<ValueType> const> getRestriction(size_t index) const { - assert(isRestriction(index)); + STORM_LOG_ASSERT(isRestriction(index), "Element is no restriction."); return std::static_pointer_cast<DFTRestriction<ValueType> const>(mElements[index]); } @@ -215,7 +215,7 @@ namespace storm { } DFTElementCPointer getRepresentant(size_t id) const { - assert(hasRepresentant(id)); + STORM_LOG_ASSERT(hasRepresentant(id), "Element has no representant."); return getElement(mRepresentants.find(id)->second); } diff --git a/src/storage/dft/DFTElementState.h b/src/storage/dft/DFTElementState.h index d191cc2d9..548eb6c88 100644 --- a/src/storage/dft/DFTElementState.h +++ b/src/storage/dft/DFTElementState.h @@ -2,7 +2,7 @@ #ifndef DFTELEMENTSTATE_H #define DFTELEMENTSTATE_H -#include <cassert> +#include "src/utility/macros.h" namespace storm { namespace storage { @@ -18,8 +18,10 @@ namespace storm { return os << "Failsafe"; case DFTElementState::DontCare: return os << "Don't Care"; + default: + STORM_LOG_ASSERT(false, "Element state not known."); + return os; } - assert(false); } inline char toChar(DFTElementState st) { @@ -32,8 +34,10 @@ namespace storm { return 'S'; case DFTElementState::DontCare: return '-'; + default: + STORM_LOG_ASSERT(false, "Element state not known."); + return ' '; } - assert(false); } enum class DFTDependencyState {Passive = 0, Unsuccessful = 1, Successful = 2, DontCare = 3}; @@ -48,8 +52,10 @@ namespace storm { return os << "Unsuccessful"; case DFTDependencyState::DontCare: return os << "Don't Care"; + default: + STORM_LOG_ASSERT(false, "Element state not known."); + return os; } - assert(false); } inline char toChar(DFTDependencyState st) { @@ -62,8 +68,10 @@ namespace storm { return 'U'; case DFTDependencyState::DontCare: return '-'; + default: + STORM_LOG_ASSERT(false, "Element state not known."); + return ' '; } - assert(false); } } diff --git a/src/storage/dft/DFTIsomorphism.h b/src/storage/dft/DFTIsomorphism.h index d94640d9c..1f66e5449 100644 --- a/src/storage/dft/DFTIsomorphism.h +++ b/src/storage/dft/DFTIsomorphism.h @@ -1,6 +1,5 @@ #pragma once -#include <cassert> #include <vector> #include <unordered_map> #include <utility> @@ -197,7 +196,7 @@ namespace storage { } else if(dft.isDependency(id)) { colourize(dft.getDependency(id)); } else { - assert(dft.isRestriction(id)); + STORM_LOG_ASSERT(dft.isRestriction(id), "Element is no restriction."); colourize(dft.getRestriction(id)); } } @@ -233,7 +232,7 @@ namespace storage { res.pdepCandidates[depColour.at(index)] = std::vector<size_t>({index}); } } else { - assert(dft.isRestriction(index)); + STORM_LOG_ASSERT(dft.isRestriction(index), "Element is no restriction."); auto it = res.restrictionCandidates.find(restrictionColour.at(index)); if(it != res.restrictionCandidates.end()) { it->second.push_back(index); @@ -342,14 +341,14 @@ namespace storage { * Construct the initial bijection. */ void constructInitialBijection() { - assert(candidatesCompatible); + STORM_LOG_ASSERT(candidatesCompatible, "Candidates are not compatible."); // We first construct the currentPermutations, which helps to determine the current state of the check. initializePermutationsAndTreatTrivialGroups(bleft.beCandidates, bright.beCandidates, currentPermutations.beCandidates); initializePermutationsAndTreatTrivialGroups(bleft.gateCandidates, bright.gateCandidates, currentPermutations.gateCandidates); initializePermutationsAndTreatTrivialGroups(bleft.pdepCandidates, bright.pdepCandidates, currentPermutations.pdepCandidates); initializePermutationsAndTreatTrivialGroups(bleft.restrictionCandidates, bright.restrictionCandidates, currentPermutations.restrictionCandidates); STORM_LOG_TRACE(bijection.size() << " vs. " << bleft.size() << " vs. " << bright.size()); - assert(bijection.size() == bleft.size()); + STORM_LOG_ASSERT(bijection.size() == bleft.size(), "No. of bijection elements do not match."); } @@ -358,7 +357,7 @@ namespace storage { * @return true if a next bijection exists. */ bool findNextBijection() { - assert(candidatesCompatible); + STORM_LOG_ASSERT(candidatesCompatible, "Candidates are not compatible."); bool foundNext = false; if(!currentPermutations.beCandidates.empty()) { auto it = currentPermutations.beCandidates.begin(); @@ -395,28 +394,28 @@ namespace storage { if(foundNext) { for(auto const& colour : bleft.beCandidates) { if (colour.second.size() > 1) { - assert(currentPermutations.beCandidates.find(colour.first) != currentPermutations.beCandidates.end()); + STORM_LOG_ASSERT(currentPermutations.beCandidates.find(colour.first) != currentPermutations.beCandidates.end(), "Colour not found."); zipVectorsIntoMap(colour.second, currentPermutations.beCandidates.find(colour.first)->second, bijection); } } for(auto const& colour : bleft.gateCandidates) { if (colour.second.size() > 1) { - assert(currentPermutations.gateCandidates.find(colour.first) != currentPermutations.gateCandidates.end()); + STORM_LOG_ASSERT(currentPermutations.gateCandidates.find(colour.first) != currentPermutations.gateCandidates.end(), "Colour not found."); zipVectorsIntoMap(colour.second, currentPermutations.gateCandidates.find(colour.first)->second, bijection); } } for(auto const& colour : bleft.pdepCandidates) { if (colour.second.size() > 1) { - assert(currentPermutations.pdepCandidates.find(colour.first) != currentPermutations.pdepCandidates.end()); + STORM_LOG_ASSERT(currentPermutations.pdepCandidates.find(colour.first) != currentPermutations.pdepCandidates.end(), "Colour not found."); zipVectorsIntoMap(colour.second, currentPermutations.pdepCandidates.find(colour.first)->second, bijection); } } for(auto const& colour : bleft.restrictionCandidates) { if (colour.second.size() > 1) { - assert(currentPermutations.restrictionCandidates.find(colour.first) != currentPermutations.restrictionCandidates.end()); + STORM_LOG_ASSERT(currentPermutations.restrictionCandidates.find(colour.first) != currentPermutations.restrictionCandidates.end(), "Colour not found."); zipVectorsIntoMap(colour.second, currentPermutations.restrictionCandidates.find(colour.first)->second, bijection); } } @@ -430,13 +429,13 @@ namespace storage { * */ bool check() const { - assert(bijection.size() == bleft.size()); + STORM_LOG_ASSERT(bijection.size() == bleft.size(), "No. of bijection elements do not match."); // We can skip BEs, as they are identified by they're homomorphic if they are in the same class for(auto const& indexpair : bijection) { // Check type first. Colouring takes care of a lot, but not necesarily everything (e.g. voting thresholds) equalType(*dft.getElement(indexpair.first), *dft.getElement(indexpair.second)); if(dft.isGate(indexpair.first)) { - assert(dft.isGate(indexpair.second)); + STORM_LOG_ASSERT(dft.isGate(indexpair.second), "Element is no gate."); auto const& lGate = dft.getGate(indexpair.first); auto const& rGate = dft.getGate(indexpair.second); if(lGate->isDynamicGate()) { @@ -483,7 +482,7 @@ namespace storage { } else if(dft.isDependency(indexpair.first)) { - assert(dft.isDependency(indexpair.second)); + STORM_LOG_ASSERT(dft.isDependency(indexpair.second), "Element is no dependency."); auto const& lDep = dft.getDependency(indexpair.first); auto const& rDep = dft.getDependency(indexpair.second); if(bijection.at(lDep->triggerEvent()->id()) != rDep->triggerEvent()->id()) { @@ -493,7 +492,7 @@ namespace storage { return false; } } else if(dft.isRestriction(indexpair.first)) { - assert(dft.isRestriction(indexpair.second)); + STORM_LOG_ASSERT(dft.isRestriction(indexpair.second), "Element is no restriction."); auto const& lRestr = dft.getRestriction(indexpair.first); std::vector<size_t> childrenLeftMapped; for(auto const& child : lRestr->children() ) { @@ -521,8 +520,8 @@ namespace storage { } } else { - assert(dft.isBasicElement(indexpair.first)); - assert(dft.isBasicElement(indexpair.second)); + STORM_LOG_ASSERT(dft.isBasicElement(indexpair.first), "Element is no BE."); + STORM_LOG_ASSERT(dft.isBasicElement(indexpair.second), "Element is no BE."); // No operations required. } } @@ -590,12 +589,12 @@ namespace storage { for(auto const& colour : right) { if(colour.second.size()>1) { auto it = permutations.insert(colour); - assert(it.second); + STORM_LOG_ASSERT(it.second, "Element already contained."); std::sort(it.first->second.begin(), it.first->second.end()); zipVectorsIntoMap(left.at(colour.first), it.first->second, bijection); } else { - assert(colour.second.size() == 1); - assert(bijection.count(left.at(colour.first).front()) == 0); + STORM_LOG_ASSERT(colour.second.size() == 1, "No elements for colour."); + STORM_LOG_ASSERT(bijection.count(left.at(colour.first).front()) == 0, "Element already contained."); bijection[left.at(colour.first).front()] = colour.second.front(); } } @@ -606,7 +605,7 @@ namespace storage { */ void zipVectorsIntoMap(std::vector<size_t> const& a, std::vector<size_t> const& b, std::map<size_t, size_t>& map) const { // Assert should pass due to compatibility check - assert(a.size() == b.size()); + STORM_LOG_ASSERT(a.size() == b.size(), "Sizes do not match."); auto it = b.cbegin(); for(size_t lIndex : a) { map[lIndex] = *it; @@ -638,4 +637,4 @@ namespace std { return hasher(p.first) ^ hasher(p.second); } }; -} \ No newline at end of file +} diff --git a/src/storage/dft/DFTState.cpp b/src/storage/dft/DFTState.cpp index 25edf1d38..9a0a16dba 100644 --- a/src/storage/dft/DFTState.cpp +++ b/src/storage/dft/DFTState.cpp @@ -11,8 +11,8 @@ namespace storm { // Initialize uses for(size_t id : mDft.getSpareIndices()) { std::shared_ptr<DFTGate<ValueType> const> elem = mDft.getGate(id); - assert(elem->isSpareGate()); - assert(elem->nrChildren() > 0); + STORM_LOG_ASSERT(elem->isSpareGate(), "Element is no spare gate."); + STORM_LOG_ASSERT(elem->nrChildren() > 0, "Element has no child."); this->setUses(id, elem->children()[0]->id()); } @@ -47,7 +47,7 @@ namespace storm { // Initialize failable dependencies for (size_t dependencyId : mDft.getDependencies()) { std::shared_ptr<DFTDependency<ValueType> const> dependency = mDft.getDependency(dependencyId); - assert(dependencyId == dependency->id()); + STORM_LOG_ASSERT(dependencyId == dependency->id(), "Ids do not match."); if (hasFailed(dependency->triggerEvent()->id()) && getElementState(dependency->dependentEvent()->id()) == DFTElementState::Operational) { mFailableDependencies.push_back(dependencyId); STORM_LOG_TRACE("New dependency failure: " << dependency->toString()); @@ -182,9 +182,9 @@ namespace storm { } for (auto dependency : mDft.getElement(id)->outgoingDependencies()) { - assert(dependency->triggerEvent()->id() == id); + STORM_LOG_ASSERT(dependency->triggerEvent()->id() == id, "Ids do not match."); if (getElementState(dependency->dependentEvent()->id()) == DFTElementState::Operational) { - assert(!isFailsafe(dependency->dependentEvent()->id())); + STORM_LOG_ASSERT(!isFailsafe(dependency->dependentEvent()->id()), "Dependent event is failsafe."); mFailableDependencies.push_back(dependency->id()); STORM_LOG_TRACE("New dependency failure: " << dependency->toString()); } @@ -194,11 +194,11 @@ namespace storm { template<typename ValueType> void DFTState<ValueType>::updateDontCareDependencies(size_t id) { - assert(mDft.isBasicElement(id)); - assert(hasFailed(id)); + STORM_LOG_ASSERT(mDft.isBasicElement(id), "Element is no BE."); + STORM_LOG_ASSERT(hasFailed(id), "Element has not failed."); for (auto dependency : mDft.getBasicElement(id)->ingoingDependencies()) { - assert(dependency->dependentEvent()->id() == id); + STORM_LOG_ASSERT(dependency->dependentEvent()->id() == id, "Ids do not match."); setDependencyDontCare(dependency->id()); } } @@ -209,7 +209,7 @@ namespace storm { STORM_LOG_TRACE("currently failable: " << getCurrentlyFailableString()); if (nrFailableDependencies() > 0) { // Consider failure due to dependency - assert(index < nrFailableDependencies()); + STORM_LOG_ASSERT(index < nrFailableDependencies(), "Index invalid."); std::shared_ptr<DFTDependency<ValueType> const> dependency = mDft.getDependency(mFailableDependencies[index]); std::pair<std::shared_ptr<DFTBE<ValueType> const>,bool> res(mDft.getBasicElement(dependency->dependentEvent()->id()), true); mFailableDependencies.erase(mFailableDependencies.begin() + index); @@ -218,9 +218,9 @@ namespace storm { return res; } else { // Consider "normal" failure - assert(index < nrFailableBEs()); + STORM_LOG_ASSERT(index < nrFailableBEs(), "Index invalid."); std::pair<std::shared_ptr<DFTBE<ValueType> const>,bool> res(mDft.getBasicElement(mIsCurrentlyFailableBE[index]), false); - assert(res.first->canFail()); + STORM_LOG_ASSERT(res.first->canFail(), "Element cannot fail."); mIsCurrentlyFailableBE.erase(mIsCurrentlyFailableBE.begin() + index); setFailed(res.first->id()); return res; @@ -229,7 +229,7 @@ namespace storm { template<typename ValueType> void DFTState<ValueType>::letDependencyBeUnsuccessful(size_t index) { - assert(nrFailableDependencies() > 0 && index < nrFailableDependencies()); + STORM_LOG_ASSERT(nrFailableDependencies() > 0 && index < nrFailableDependencies(), "Index invalid."); std::shared_ptr<DFTDependency<ValueType> const> dependency = mDft.getDependency(getDependencyId(index)); mFailableDependencies.erase(mFailableDependencies.begin() + index); setDependencyUnsuccessful(dependency->id()); @@ -243,7 +243,7 @@ namespace storm { template<typename ValueType> bool DFTState<ValueType>::isActive(size_t id) const { - assert(mDft.isRepresentative(id)); + STORM_LOG_ASSERT(mDft.isRepresentative(id), "Element is no representative."); return mStatus[mStateGenerationInfo.getSpareActivationIndex(id)]; } @@ -277,7 +277,7 @@ namespace storm { template<typename ValueType> uint_fast64_t DFTState<ValueType>::extractUses(size_t from) const { - assert(mStateGenerationInfo.usageInfoBits() < 64); + STORM_LOG_ASSERT(mStateGenerationInfo.usageInfoBits() < 64, "UsageInfoBit size too large."); return mStatus.getAsInt(from, mStateGenerationInfo.usageInfoBits()); } @@ -294,14 +294,14 @@ namespace storm { template<typename ValueType> void DFTState<ValueType>::finalizeUses(size_t spareId) { - assert(hasFailed(spareId)); + STORM_LOG_ASSERT(hasFailed(spareId), "Spare has not failed."); mStatus.setFromInt(mStateGenerationInfo.getSpareUsageIndex(spareId), mStateGenerationInfo.usageInfoBits(), mDft.getMaxSpareChildCount()); } template<typename ValueType> bool DFTState<ValueType>::hasOperationalPostSeqElements(size_t id) const { - assert(!mDft.isDependency(id)); - assert(!mDft.isRestriction(id)); + STORM_LOG_ASSERT(!mDft.isDependency(id), "Element is dependency."); + STORM_LOG_ASSERT(!mDft.isRestriction(id), "Element is restriction."); auto const& postIds = mStateGenerationInfo.seqRestrictionPostElements(id); for(size_t id : postIds) { if(isOperational(id)) { @@ -315,7 +315,7 @@ namespace storm { bool DFTState<ValueType>::claimNew(size_t spareId, size_t currentlyUses, std::vector<std::shared_ptr<DFTElement<ValueType>>> const& children) { auto it = children.begin(); while ((*it)->id() != currentlyUses) { - assert(it != children.end()); + STORM_LOG_ASSERT(it != children.end(), "Currently used element not found."); ++it; } ++it; @@ -377,4 +377,4 @@ namespace storm { #endif } -} \ No newline at end of file +} diff --git a/src/storage/dft/DFTState.h b/src/storage/dft/DFTState.h index f647aae17..8562559b3 100644 --- a/src/storage/dft/DFTState.h +++ b/src/storage/dft/DFTState.h @@ -6,7 +6,6 @@ #include <sstream> #include <memory> -#include <cassert> namespace storm { namespace storage { @@ -156,7 +155,7 @@ namespace storm { * @return Id of the dependency */ size_t getDependencyId(size_t index) const { - assert(index < nrFailableDependencies()); + STORM_LOG_ASSERT(index < nrFailableDependencies(), "Index invalid."); return mFailableDependencies[index]; } diff --git a/src/storage/dft/DFTStateSpaceGenerationQueues.h b/src/storage/dft/DFTStateSpaceGenerationQueues.h index 6ded7cb38..bd71b5fbf 100644 --- a/src/storage/dft/DFTStateSpaceGenerationQueues.h +++ b/src/storage/dft/DFTStateSpaceGenerationQueues.h @@ -55,7 +55,7 @@ namespace storm { } DFTRestrictionPointer nextRestrictionCheck() { - assert(!restrictionChecksDone()); + STORM_LOG_ASSERT(!restrictionChecksDone(), "All restriction checks done already."); DFTRestrictionPointer next = restrictionChecks.back(); restrictionChecks.pop_back(); return next; diff --git a/src/storage/expressions/Variable.cpp b/src/storage/expressions/Variable.cpp index aeb432f2a..5c9660b83 100644 --- a/src/storage/expressions/Variable.cpp +++ b/src/storage/expressions/Variable.cpp @@ -1,6 +1,5 @@ #include "src/storage/expressions/Variable.h" #include "src/storage/expressions/ExpressionManager.h" -#include <cassert> namespace storm { namespace expressions { @@ -41,7 +40,7 @@ namespace storm { } ExpressionManager const& Variable::getManager() const { - assert(manager != nullptr); + STORM_LOG_ASSERT(manager != nullptr, "Manager is null."); return *manager; } @@ -65,4 +64,4 @@ namespace storm { return this->getType().isNumericalType(); } } -} \ No newline at end of file +} diff --git a/src/storage/expressions/Variable.h b/src/storage/expressions/Variable.h index 3ade039ed..79b430f65 100644 --- a/src/storage/expressions/Variable.h +++ b/src/storage/expressions/Variable.h @@ -6,6 +6,7 @@ #include <functional> #include "src/utility/OsDetection.h" +#include "src/utility/macros.h" namespace storm { namespace expressions { @@ -155,4 +156,4 @@ namespace std { }; } -#endif /* STORM_STORAGE_EXPRESSIONS_VARIABLE_H_ */ \ No newline at end of file +#endif /* STORM_STORAGE_EXPRESSIONS_VARIABLE_H_ */ diff --git a/src/storage/prism/Assignment.cpp b/src/storage/prism/Assignment.cpp index a6c8b4456..7aa685a6d 100644 --- a/src/storage/prism/Assignment.cpp +++ b/src/storage/prism/Assignment.cpp @@ -1,5 +1,4 @@ #include "Assignment.h" -#include <cassert> namespace storm { namespace prism { @@ -25,7 +24,7 @@ namespace storm { bool Assignment::isIdentity() const { if(this->expression.isVariable()) { - assert(this->expression.getVariables().size() == 1); + STORM_LOG_ASSERT(this->expression.getVariables().size() == 1, "Invalid number of variables."); //if( variable == *(this->expression.getVariables().begin())) { // std::cout << variable.getName() << " == " << (this->expression.getVariables().begin())->getName() << std::endl; //} diff --git a/src/storage/prism/Command.cpp b/src/storage/prism/Command.cpp index 5cad634e7..a5a59522d 100644 --- a/src/storage/prism/Command.cpp +++ b/src/storage/prism/Command.cpp @@ -1,5 +1,4 @@ #include "Command.h" -#include <cassert> namespace storm { namespace prism { @@ -32,7 +31,7 @@ namespace storm { } storm::prism::Update const& Command::getUpdate(uint_fast64_t index) const { - assert(index < getNumberOfUpdates()); + STORM_LOG_ASSERT(index < getNumberOfUpdates(), "Invalid index."); return this->updates[index]; } diff --git a/src/storage/prism/Program.cpp b/src/storage/prism/Program.cpp index 0a7b11bd6..ca55258b3 100644 --- a/src/storage/prism/Program.cpp +++ b/src/storage/prism/Program.cpp @@ -1297,7 +1297,7 @@ namespace storm { } uint_fast64_t Program::largestActionIndex() const { - assert(numberOfActions() != 0); + STORM_LOG_ASSERT(numberOfActions() != 0, "No actions available."); return this->indexToActionMap.rbegin()->first; } diff --git a/src/storm-dyftee.cpp b/src/storm-dyftee.cpp index 3ffd665ad..4cfd1f58f 100644 --- a/src/storm-dyftee.cpp +++ b/src/storm-dyftee.cpp @@ -5,6 +5,7 @@ #include "src/cli/cli.h" #include "src/exceptions/BaseException.h" #include "src/utility/macros.h" +#include "src/builder/DftSmtBuilder.h" #include <boost/lexical_cast.hpp> #include "src/settings/modules/GeneralSettings.h" @@ -44,6 +45,18 @@ void analyzeDFT(std::string filename, std::string property, bool symred = false, analyser.printResult(); } +template<typename ValueType> +void analyzeWithSMT(std::string filename) { + std::cout << "Running DFT analysis on file " << filename << " with use of SMT" << std::endl; + + storm::parser::DFTGalileoParser<ValueType> parser; + storm::storage::DFT<ValueType> dft = parser.parseDFT(filename); + storm::builder::DFTSMTBuilder<ValueType> dftSmtBuilder; + dftSmtBuilder.convertToSMT(dft); + bool sat = dftSmtBuilder.check(); + std::cout << "SMT result: " << sat << std::endl; +} + /*! * Initialize the settings manager. */ @@ -92,6 +105,22 @@ int main(const int argc, const char** argv) { STORM_LOG_THROW(false, storm::exceptions::InvalidSettingsException, "No input model."); } + bool parametric = false; +#ifdef STORM_HAVE_CARL + parametric = generalSettings.isParametricSet(); +#endif + + if (dftSettings.solveWithSMT()) { + // Solve with SMT + if (parametric) { + analyzeWithSMT<storm::RationalFunction>(dftSettings.getDftFilename()); + } else { + analyzeWithSMT<double>(dftSettings.getDftFilename()); + } + storm::utility::cleanUp(); + return 0; + } + // Set min or max bool minimal = true; if (dftSettings.isComputeMaximalValue()) { @@ -132,11 +161,6 @@ int main(const int argc, const char** argv) { STORM_LOG_ASSERT(!pctlFormula.empty(), "Pctl formula empty."); - bool parametric = false; -#ifdef STORM_HAVE_CARL - parametric = generalSettings.isParametricSet(); -#endif - // From this point on we are ready to carry out the actual computations. if (parametric) { analyzeDFT<storm::RationalFunction>(dftSettings.getDftFilename(), pctlFormula, dftSettings.useSymmetryReduction(), allowModular && dftSettings.useModularisation(), !dftSettings.isDisableDC() ); From bc10291680ce76a16785f9b64650954187c2127d Mon Sep 17 00:00:00 2001 From: Mavo <matthias.volk@rwth-aachen.de> Date: Wed, 18 May 2016 14:53:37 +0200 Subject: [PATCH 02/65] STORM_DEVELOPER mode introduced Former-commit-id: 22ff09ad8e779d0ed3f150b1da11978ab9cbb321 --- CMakeLists.txt | 12 +++++------ .../SparseDtmcEliminationModelChecker.cpp | 4 ++++ src/storage/BitVector.cpp | 20 +++++++++---------- 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 13ccb71fc..f2621cf4d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,7 +18,7 @@ include(ExternalProject) ## CMake options of StoRM ## ############################################################# -option(STORM_DEBUG "Sets whether the DEBUG mode is used" ON) +option(STORM_DEVELOPER "Sets whether the development mode is used" OFF) option(STORM_USE_POPCNT "Sets whether the popcnt instruction is going to be used." ON) option(USE_BOOST_STATIC_LIBRARIES "Sets whether the Boost libraries should be linked statically." ON) option(STORM_USE_INTELTBB "Sets whether the Intel TBB libraries should be used." OFF) @@ -40,11 +40,11 @@ set(MSAT_ROOT "" CACHE STRING "The hint to the root directory of MathSAT (option set(ADDITIONAL_INCLUDE_DIRS "" CACHE STRING "Additional directories added to the include directories.") set(ADDITIONAL_LINK_DIRS "" CACHE STRING "Additional directories added to the link directories.") -# If the DEBUG option was turned on, we will target a debug version and a release version otherwise. -if (STORM_DEBUG) - set (CMAKE_BUILD_TYPE "DEBUG") -else() - set (CMAKE_BUILD_TYPE "RELEASE") +# If the STORM_DEVELOPER option was turned on, we will target a debug version. +if (STORM_DEVELOPER) + message(STATUS "StoRM - Using development mode") + set(CMAKE_BUILD_TYPE "DEBUG" CACHE STRING "Set the build type" FORCE) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSTORM_DEV") endif() message(STATUS "StoRM - Building ${CMAKE_BUILD_TYPE} version.") diff --git a/src/modelchecker/reachability/SparseDtmcEliminationModelChecker.cpp b/src/modelchecker/reachability/SparseDtmcEliminationModelChecker.cpp index 0f67cca4a..5a6045f66 100644 --- a/src/modelchecker/reachability/SparseDtmcEliminationModelChecker.cpp +++ b/src/modelchecker/reachability/SparseDtmcEliminationModelChecker.cpp @@ -271,7 +271,9 @@ namespace storm { while (priorityQueue->hasNextState()) { storm::storage::sparse::state_type state = priorityQueue->popNextState(); stateEliminator.eliminateState(state, true); +#ifdef STORM_DEV STORM_LOG_ASSERT(checkConsistent(flexibleMatrix, flexibleBackwardTransitions), "The forward and backward transition matrices became inconsistent."); +#endif } // Now, we set the values of all states in BSCCs to that of the representative value (and clear the @@ -903,7 +905,9 @@ namespace storm { if (removeForwardTransitions) { values[state] = storm::utility::zero<ValueType>(); } +#ifdef STORM_DEV STORM_LOG_ASSERT(checkConsistent(transitionMatrix, backwardTransitions), "The forward and backward transition matrices became inconsistent."); +#endif } } diff --git a/src/storage/BitVector.cpp b/src/storage/BitVector.cpp index fcbceca7b..a60b8c10c 100644 --- a/src/storage/BitVector.cpp +++ b/src/storage/BitVector.cpp @@ -12,8 +12,8 @@ #include <bitset> -#ifndef NDEBUG -//#define ASSERT_BITVECTOR +#ifdef STORM_DEV +#define ASSERT_BITVECTOR #endif namespace storm { @@ -725,11 +725,11 @@ namespace storm { if (result.get(i) != get(start + i)) { STORM_LOG_ERROR("Getting of bits not correct for index " << i); STORM_LOG_ERROR("Getting from " << start << " with length " << length); - stringstream stream; + std::stringstream stream; printBits(stream); stream << std::endl; result.printBits(stream); - STORM_LOG_ERROR(stream); + STORM_LOG_ERROR(stream.str()); STORM_LOG_ASSERT(false, "Getting of bits not correct."); } } @@ -738,11 +738,11 @@ namespace storm { if (original.get(i) != get(i)) { STORM_LOG_ERROR("Getting did change bitvector at index " << i); STORM_LOG_ERROR("Getting from " << start << " with length " << length); - stringstream stream; + std::stringstream stream; printBits(stream); stream << std::endl; original.printBits(stream); - STORM_LOG_ERROR(stream); + STORM_LOG_ERROR(stream.str()); STORM_LOG_ASSERT(false, "Getting of bits not correct."); } } @@ -818,11 +818,11 @@ namespace storm { if (other.get(i) != get(start + i)) { STORM_LOG_ERROR("Setting of bits not correct for index " << i); STORM_LOG_ERROR("Setting from " << start << " with length " << other.bitCount); - stringstream stream; + std::stringstream stream; printBits(stream); stream << std::endl; other.printBits(stream); - STORM_LOG_ERROR(stream); + STORM_LOG_ERROR(stream.str()); STORM_LOG_ASSERT(false, "Setting of bits not correct."); } } @@ -831,11 +831,11 @@ namespace storm { if (original.get(i) != get(i)) { STORM_LOG_ERROR("Setting did change bitvector at index " << i); STORM_LOG_ERROR("Setting from " << start << " with length " << other.bitCount); - stringstream stream; + std::stringstream stream; printBits(stream); stream << std::endl; original.printBits(stream); - STORM_LOG_ERROR(stream); + STORM_LOG_ERROR(stream.str()); STORM_LOG_ASSERT(false, "Setting of bits not correct."); } } From fb5db51595994ff261e0bb36556711690a093c2f Mon Sep 17 00:00:00 2001 From: Mavo <matthias.volk@rwth-aachen.de> Date: Wed, 18 May 2016 14:53:58 +0200 Subject: [PATCH 03/65] Missing include Former-commit-id: 16d4ff67dcbf7cb8c74b873d7b69279b7f03ec69 --- src/solver/MinMaxLinearEquationSolver.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/solver/MinMaxLinearEquationSolver.h b/src/solver/MinMaxLinearEquationSolver.h index a9c497d45..6c547cf0c 100644 --- a/src/solver/MinMaxLinearEquationSolver.h +++ b/src/solver/MinMaxLinearEquationSolver.h @@ -14,6 +14,7 @@ #include "src/solver/OptimizationDirection.h" #include "src/exceptions/InvalidSettingsException.h" +#include "src/utility/macros.h" namespace storm { namespace storage { From 493407f74f82e92d053bbeee5ca144bf0a8dd037 Mon Sep 17 00:00:00 2001 From: Mavo <matthias.volk@rwth-aachen.de> Date: Wed, 18 May 2016 16:27:48 +0200 Subject: [PATCH 04/65] Started with SMT encoding for DFTs Former-commit-id: a2a68cee6ac0385b2653280d295bb482b1b412f6 --- src/builder/DftSmtBuilder.cpp | 120 +++++++++++++++++++++++++++ src/builder/DftSmtBuilder.h | 49 +++++++++++ src/settings/modules/DFTSettings.cpp | 14 +++- src/settings/modules/DFTSettings.h | 12 +++ 4 files changed, 194 insertions(+), 1 deletion(-) create mode 100644 src/builder/DftSmtBuilder.cpp create mode 100644 src/builder/DftSmtBuilder.h diff --git a/src/builder/DftSmtBuilder.cpp b/src/builder/DftSmtBuilder.cpp new file mode 100644 index 000000000..1d60b2c74 --- /dev/null +++ b/src/builder/DftSmtBuilder.cpp @@ -0,0 +1,120 @@ +#include "src/builder/DFTSMTBuilder.h" +#include "src/exceptions/NotImplementedException.h" + +namespace storm { + namespace builder { + + template <typename ValueType> + DFTSMTBuilder<ValueType>::DFTSMTBuilder() : manager(std::make_shared<storm::expressions::ExpressionManager>()) { + solver = storm::utility::solver::SmtSolverFactory().create(*manager); + } + + template <typename ValueType> + void DFTSMTBuilder<ValueType>::convertToSMT(storm::storage::DFT<ValueType> const& dft) { + std::cout << "Convert DFT to SMT" << std::endl; + timeMax = manager->integer(dft.nrBasicElements()); + timeFailSafe = manager->integer(dft.nrBasicElements() + 1); + timeZero = manager->integer(0); + + // Convert all elements + for (size_t i = 0; i < dft.nrElements(); ++i) { + std::shared_ptr<storm::storage::DFTElement<ValueType> const> element = dft.getElement(i); + std::cout << "Consider " << element->toString() << std::endl; + if (element->isBasicElement()) { + storm::expressions::Variable varBE = convert(std::static_pointer_cast<storm::storage::DFTBE<ValueType> const>(element)); + varsBE.push_back(varBE); + } else if (element->isGate()) { + storm::expressions::Variable varGate = convert(std::static_pointer_cast<storm::storage::DFTGate<ValueType> const>(element)); + if (dft.getTopLevelIndex() == i) { + topLevel = varGate; + } + } else if (element->isDependency()) { + convert(std::static_pointer_cast<storm::storage::DFTDependency<ValueType> const>(element)); + } else if (element->isRestriction()) { + convert(std::static_pointer_cast<storm::storage::DFTRestriction<ValueType> const>(element)); + } + } + + // No simultaneous fails can occur + for (size_t i = 0; i < varsBE.size() - 1; ++i) { + storm::expressions::Expression be = varsBE[i]; + for (size_t j = i + 1; j < varsBE.size(); ++j) { + storm::expressions::Expression assertion = be != varsBE[j]; + solver->add(assertion); + } + } + + // For every time-point one BE must fail + for (size_t time = 1; time <= dft.nrBasicElements(); ++time) { + storm::expressions::Expression exprTime = manager->integer(time); + storm::expressions::Expression assertion = varsBE[0] == exprTime; + for (size_t i = 1; i < varsBE.size(); ++i) { + assertion = assertion || varsBE[i] == exprTime; + } + assertion = assertion || topLevel <= exprTime; + solver->add(assertion); + } + + } + + template <typename ValueType> + storm::expressions::Variable DFTSMTBuilder<ValueType>::convert(std::shared_ptr<storm::storage::DFTBE<ValueType> const> const& be) { + storm::expressions::Variable var = manager->declareIntegerVariable(be->name()); + storm::expressions::Expression assertion = timeZero < var && var < timeFailSafe; + solver->add(assertion); + return var; + } + + template <typename ValueType> + storm::expressions::Variable DFTSMTBuilder<ValueType>::convert(std::shared_ptr<storm::storage::DFTGate<ValueType> const> const& gate) { + storm::expressions::Variable var = manager->declareIntegerVariable(gate->name()); + storm::expressions::Expression assertion = timeZero < var && var <= timeFailSafe; + solver->add(assertion); + return var; + } + + template <typename ValueType> + storm::expressions::Variable DFTSMTBuilder<ValueType>::convert(std::shared_ptr<storm::storage::DFTDependency<ValueType> const> const& dependency) { + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "The dependency cannot be converted into SMT."); + } + + template <typename ValueType> + storm::expressions::Variable DFTSMTBuilder<ValueType>::convert(std::shared_ptr<storm::storage::DFTRestriction<ValueType> const> const& restriction) { + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "The restriction cannot be converted into SMT."); + } + + template <typename ValueType> + bool DFTSMTBuilder<ValueType>::check() const { + std::cout << "Check" << std::endl; + storm::solver::SmtSolver::CheckResult result = solver->check(); + switch (result) { + case solver::SmtSolver::CheckResult::Sat: + { + std::cout << "SAT with model:" << std::endl; + std::shared_ptr<storm::solver::SmtSolver::ModelReference> model = solver->getModel(); + for (auto const& pair : *manager) { + std::cout << pair.first.getName() << "->" << model->getIntegerValue(pair.first) << std::endl; + } + return true; + } + case solver::SmtSolver::CheckResult::Unsat: + return false; + case solver::SmtSolver::CheckResult::Unknown: + default: + STORM_LOG_ASSERT(false, "Result is unknown."); + return false; + } + } + + + // Explicitly instantiate the class. + template class DFTSMTBuilder<double>; + +#ifdef STORM_HAVE_CARL + template class DFTSMTBuilder<storm::RationalFunction>; +#endif + + } // namespace builder +} // namespace storm + + diff --git a/src/builder/DftSmtBuilder.h b/src/builder/DftSmtBuilder.h new file mode 100644 index 000000000..596e8106f --- /dev/null +++ b/src/builder/DftSmtBuilder.h @@ -0,0 +1,49 @@ +#ifndef DFTSMTBUILDER_H +#define DFTSMTBUILDER_H + +#include "src/solver/SmtSolver.h" +#include "src/utility/solver.h" +#include "src/storage/dft/DFT.h" + +namespace storm { + namespace builder { + + template<typename ValueType> + class DFTSMTBuilder { + + public: + DFTSMTBuilder(); + + void convertToSMT(storm::storage::DFT<ValueType> const& dft); + + bool check() const; + + private: + + std::shared_ptr<storm::solver::SmtSolver> solver; + + std::shared_ptr<storm::expressions::ExpressionManager> manager; + + storm::expressions::Expression timeMax; + + storm::expressions::Expression timeFailSafe; + + storm::expressions::Expression timeZero; + + storm::expressions::Expression topLevel; + + std::vector<storm::expressions::Variable> varsBE; + + storm::expressions::Variable convert(std::shared_ptr<storm::storage::DFTBE<ValueType> const> const& be); + + storm::expressions::Variable convert(std::shared_ptr<storm::storage::DFTGate<ValueType> const> const& gate); + + storm::expressions::Variable convert(std::shared_ptr<storm::storage::DFTDependency<ValueType> const> const& dependency); + + storm::expressions::Variable convert(std::shared_ptr<storm::storage::DFTRestriction<ValueType> const> const& restriction); + + }; + } +} + +#endif /* DFTSMTBUILDER_H */ diff --git a/src/settings/modules/DFTSettings.cpp b/src/settings/modules/DFTSettings.cpp index 365ba0271..4df95987c 100644 --- a/src/settings/modules/DFTSettings.cpp +++ b/src/settings/modules/DFTSettings.cpp @@ -27,7 +27,10 @@ namespace storm { const std::string DFTSettings::propTimeBoundOptionName = "timebound"; const std::string DFTSettings::minValueOptionName = "min"; const std::string DFTSettings::maxValueOptionName = "max"; - +#ifdef STORM_HAVE_Z3 + const std::string DFTSettings::solveWithSmtOptionName = "smt"; +#endif + DFTSettings::DFTSettings() : ModuleSettings(moduleName) { this->addOption(storm::settings::OptionBuilder(moduleName, dftFileOptionName, false, "Parses the model given in the Galileo format.").setShortName(dftFileOptionShortName) .addArgument(storm::settings::ArgumentBuilder::createStringArgument("filename", "The name of the file from which to read the DFT model.").addValidationFunctionString(storm::settings::ArgumentValidators::existingReadableFileValidator()).build()).build()); @@ -39,6 +42,9 @@ namespace storm { this->addOption(storm::settings::OptionBuilder(moduleName, propTimeBoundOptionName, false, "Compute probability of system failure up to given timebound.").addArgument(storm::settings::ArgumentBuilder::createDoubleArgument("time", "The timebound to use.").addValidationFunctionDouble(storm::settings::ArgumentValidators::doubleGreaterValidatorExcluding(0.0)).build()).build()); this->addOption(storm::settings::OptionBuilder(moduleName, minValueOptionName, false, "Compute minimal value in case of non-determinism.").build()); this->addOption(storm::settings::OptionBuilder(moduleName, maxValueOptionName, false, "Compute maximal value in case of non-determinism.").build()); +#ifdef STORM_HAVE_Z3 + this->addOption(storm::settings::OptionBuilder(moduleName, solveWithSmtOptionName, true, "Solve the DFT with SMT.").build()); +#endif } bool DFTSettings::isDftFileSet() const { @@ -85,6 +91,12 @@ namespace storm { return this->getOption(maxValueOptionName).getHasOptionBeenSet(); } +#ifdef STORM_HAVE_Z3 + bool DFTSettings::solveWithSMT() const { + return this->getOption(solveWithSmtOptionName).getHasOptionBeenSet(); + } +#endif + void DFTSettings::finalize() { } diff --git a/src/settings/modules/DFTSettings.h b/src/settings/modules/DFTSettings.h index 844953c2c..183e00eef 100644 --- a/src/settings/modules/DFTSettings.h +++ b/src/settings/modules/DFTSettings.h @@ -99,6 +99,15 @@ namespace storm { */ double getPropTimebound() const; +#ifdef STORM_HAVE_Z3 + /*! + * Retrieves whether the DFT should be checked via SMT. + * + * @return True iff the option was set. + */ + bool solveWithSMT() const; +#endif + bool check() const override; void finalize() override; @@ -119,6 +128,9 @@ namespace storm { static const std::string propTimeBoundOptionName; static const std::string minValueOptionName; static const std::string maxValueOptionName; +#ifdef STORM_HAVE_Z3 + static const std::string solveWithSmtOptionName; +#endif }; From c4260d3d5af37dba8ec9dc9a9a38e48aa3bb38fc Mon Sep 17 00:00:00 2001 From: Mavo <matthias.volk@rwth-aachen.de> Date: Wed, 18 May 2016 16:29:28 +0200 Subject: [PATCH 05/65] First try on approximation Former-commit-id: 53c6738c729bb143e04d31876c17b9b5873cccd4 --- src/builder/ExplicitDFTModelBuilder.cpp | 42 +++++++++++++++++++++++++ src/builder/ExplicitDFTModelBuilder.h | 3 ++ 2 files changed, 45 insertions(+) diff --git a/src/builder/ExplicitDFTModelBuilder.cpp b/src/builder/ExplicitDFTModelBuilder.cpp index 4fb6131df..7cf1488be 100644 --- a/src/builder/ExplicitDFTModelBuilder.cpp +++ b/src/builder/ExplicitDFTModelBuilder.cpp @@ -4,6 +4,8 @@ #include <src/utility/constants.h> #include <src/utility/vector.h> #include <src/exceptions/UnexpectedException.h> +#include "src/settings/modules/DebugSettings.h" +#include "src/settings/SettingsManager.h" #include <map> namespace storm { @@ -157,7 +159,24 @@ namespace storm { return model; } + + template<> + bool belowThreshold(double const& number) { + return number < 0.1; + } + + template<> + bool belowThreshold(storm::RationalFunction const& number) { + storm::RationalFunction threshold = storm::utility::one<storm::RationalFunction>() / 10; + std::cout << number << " < " << threshold << ": " << (number < threshold) << std::endl; + std::map<storm::Variable, storm::RationalNumber> mapping; + + storm::RationalFunction eval(number.evaluate(mapping)); + std::cout << "Evaluated: " << eval << std::endl; + return eval < threshold; + } + template <typename ValueType> std::pair<uint_fast64_t, bool> ExplicitDFTModelBuilder<ValueType>::exploreStates(DFTStatePointer const& state, size_t& rowOffset, storm::storage::SparseMatrixBuilder<ValueType>& transitionMatrixBuilder, std::vector<uint_fast64_t>& markovianStates, std::vector<ValueType>& exitRates) { STORM_LOG_TRACE("Explore state: " << mDft.getStateString(state)); @@ -206,6 +225,29 @@ namespace storm { STORM_LOG_ASSERT(nextBE, "NextBE is null."); STORM_LOG_ASSERT(nextBEPair.second == hasDependencies, "Failure due to dependencies does not match."); STORM_LOG_TRACE("With the failure of: " << nextBE->name() << " [" << nextBE->id() << "] in " << mDft.getStateString(state)); + + if (storm::settings::getModule<storm::settings::modules::DebugSettings>().isTestSet()) { + if (!storm::utility::isZero(exitRate)) { + ValueType rate = nextBE->activeFailureRate(); + ValueType div = rate / exitRate; + if (!storm::utility::isZero(exitRate) && belowThreshold(div)) { + // Set transition directly to failed state + auto resultFind = outgoingRates.find(failedIndex); + if (resultFind != outgoingRates.end()) { + // Add to existing transition + resultFind->second += rate; + STORM_LOG_TRACE("Updated transition to " << resultFind->first << " with rate " << rate << " to new rate " << resultFind->second); + } else { + // Insert new transition + outgoingRates.insert(std::make_pair(failedIndex, rate)); + STORM_LOG_TRACE("Added transition to " << failedIndex << " with rate " << rate); + } + exitRate += rate; + std::cout << "IGNORE: " << nextBE->name() << " [" << nextBE->id() << "] with rate " << rate << std::endl; + //STORM_LOG_TRACE("Ignore: " << nextBE->name() << " [" << nextBE->id() << "] with rate " << rate); + continue; + }} + } // Propagate failures storm::storage::DFTStateSpaceGenerationQueues<ValueType> queues; diff --git a/src/builder/ExplicitDFTModelBuilder.h b/src/builder/ExplicitDFTModelBuilder.h index 3e78180d1..71bfada61 100644 --- a/src/builder/ExplicitDFTModelBuilder.h +++ b/src/builder/ExplicitDFTModelBuilder.h @@ -91,6 +91,9 @@ namespace storm { std::pair<bool, uint_fast64_t> checkForExploration(DFTStatePointer const& state); }; + + template<typename ValueType> + bool belowThreshold(ValueType const& number); } } From a0cd14905449d03a4fd81650b5450bd6aa94dfc5 Mon Sep 17 00:00:00 2001 From: Mavo <matthias.volk@rwth-aachen.de> Date: Wed, 18 May 2016 17:01:21 +0200 Subject: [PATCH 06/65] Introduced setting for DFT approximation Former-commit-id: 03a2c0ca0cbe406a75d57f8040c6cd64b2151bc7 --- src/builder/ExplicitDFTModelBuilder.cpp | 4 ++-- src/settings/modules/DFTSettings.cpp | 9 ++++++++- src/settings/modules/DFTSettings.h | 9 +++++++++ 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/builder/ExplicitDFTModelBuilder.cpp b/src/builder/ExplicitDFTModelBuilder.cpp index 7cf1488be..4d794cb2f 100644 --- a/src/builder/ExplicitDFTModelBuilder.cpp +++ b/src/builder/ExplicitDFTModelBuilder.cpp @@ -4,7 +4,7 @@ #include <src/utility/constants.h> #include <src/utility/vector.h> #include <src/exceptions/UnexpectedException.h> -#include "src/settings/modules/DebugSettings.h" +#include "src/settings/modules/DFTSettings.h" #include "src/settings/SettingsManager.h" #include <map> @@ -226,7 +226,7 @@ namespace storm { STORM_LOG_ASSERT(nextBEPair.second == hasDependencies, "Failure due to dependencies does not match."); STORM_LOG_TRACE("With the failure of: " << nextBE->name() << " [" << nextBE->id() << "] in " << mDft.getStateString(state)); - if (storm::settings::getModule<storm::settings::modules::DebugSettings>().isTestSet()) { + if (storm::settings::getModule<storm::settings::modules::DFTSettings>().computeApproximation()) { if (!storm::utility::isZero(exitRate)) { ValueType rate = nextBE->activeFailureRate(); ValueType div = rate / exitRate; diff --git a/src/settings/modules/DFTSettings.cpp b/src/settings/modules/DFTSettings.cpp index 4df95987c..416a8a5af 100644 --- a/src/settings/modules/DFTSettings.cpp +++ b/src/settings/modules/DFTSettings.cpp @@ -21,6 +21,8 @@ namespace storm { const std::string DFTSettings::symmetryReductionOptionShortName = "symred"; const std::string DFTSettings::modularisationOptionName = "modularisation"; const std::string DFTSettings::disableDCOptionName = "disabledc"; + const std::string DFTSettings::computeApproximationOptionName = "approximation"; + const std::string DFTSettings::computeApproximationOptionShortName = "approx"; const std::string DFTSettings::propExpectedTimeOptionName = "expectedtime"; const std::string DFTSettings::propExpectedTimeOptionShortName = "mttf"; const std::string DFTSettings::propProbabilityOptionName = "probability"; @@ -37,6 +39,7 @@ namespace storm { this->addOption(storm::settings::OptionBuilder(moduleName, symmetryReductionOptionName, false, "Exploit symmetric structure of model.").setShortName(symmetryReductionOptionShortName).build()); this->addOption(storm::settings::OptionBuilder(moduleName, modularisationOptionName, false, "Use modularisation (not applicable for expected time).").build()); this->addOption(storm::settings::OptionBuilder(moduleName, disableDCOptionName, false, "Disable Dont Care propagation.").build()); + this->addOption(storm::settings::OptionBuilder(moduleName, computeApproximationOptionName, false, "Compute an approximation.").setShortName(computeApproximationOptionShortName).build()); this->addOption(storm::settings::OptionBuilder(moduleName, propExpectedTimeOptionName, false, "Compute expected time of system failure.").setShortName(propExpectedTimeOptionShortName).build()); this->addOption(storm::settings::OptionBuilder(moduleName, propProbabilityOptionName, false, "Compute probability of system failure.").build()); this->addOption(storm::settings::OptionBuilder(moduleName, propTimeBoundOptionName, false, "Compute probability of system failure up to given timebound.").addArgument(storm::settings::ArgumentBuilder::createDoubleArgument("time", "The timebound to use.").addValidationFunctionDouble(storm::settings::ArgumentValidators::doubleGreaterValidatorExcluding(0.0)).build()).build()); @@ -67,6 +70,10 @@ namespace storm { return this->getOption(disableDCOptionName).getHasOptionBeenSet(); } + bool DFTSettings::computeApproximation() const { + return this->getOption(computeApproximationOptionName).getHasOptionBeenSet(); + } + bool DFTSettings::usePropExpectedTime() const { return this->getOption(propExpectedTimeOptionName).getHasOptionBeenSet(); } @@ -116,4 +123,4 @@ namespace storm { } // namespace modules } // namespace settings -} // namespace storm \ No newline at end of file +} // namespace storm diff --git a/src/settings/modules/DFTSettings.h b/src/settings/modules/DFTSettings.h index 183e00eef..f21752f7f 100644 --- a/src/settings/modules/DFTSettings.h +++ b/src/settings/modules/DFTSettings.h @@ -57,6 +57,13 @@ namespace storm { */ bool isDisableDC() const; + /*! + * Retrieves whether the option to compute an approximation is set. + * + * @return True iff the option was set. + */ + bool computeApproximation() const; + /*! * Retrieves whether the property expected time should be used. * @@ -122,6 +129,8 @@ namespace storm { static const std::string symmetryReductionOptionShortName; static const std::string modularisationOptionName; static const std::string disableDCOptionName; + static const std::string computeApproximationOptionName; + static const std::string computeApproximationOptionShortName; static const std::string propExpectedTimeOptionName; static const std::string propExpectedTimeOptionShortName; static const std::string propProbabilityOptionName; From 560b42a94a63b893828192bf90f86e7bf426b629 Mon Sep 17 00:00:00 2001 From: Mavo <matthias.volk@rwth-aachen.de> Date: Mon, 18 Jul 2016 08:55:37 +0200 Subject: [PATCH 07/65] Some interval functions not implemented Former-commit-id: c88dca9d96c280ff8e2b810a46349d92e1c4ae4d --- src/storage/SparseMatrix.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/storage/SparseMatrix.cpp b/src/storage/SparseMatrix.cpp index 4b763e383..c43dc77d9 100644 --- a/src/storage/SparseMatrix.cpp +++ b/src/storage/SparseMatrix.cpp @@ -1014,6 +1014,11 @@ namespace storm { } #ifdef STORM_HAVE_CARL + template<> + typename std::pair<storm::storage::SparseMatrix<Interval>, std::vector<Interval>> SparseMatrix<Interval>::getJacobiDecomposition() const { + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "This operation is not supported."); + } + template<> typename std::pair<storm::storage::SparseMatrix<RationalFunction>, std::vector<RationalFunction>> SparseMatrix<RationalFunction>::getJacobiDecomposition() const { STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "This operation is not supported."); @@ -1144,6 +1149,13 @@ namespace storm { } } +#ifdef STORM_HAVE_CARL + template<> + void SparseMatrix<Interval>::performSuccessiveOverRelaxationStep(Interval omega, std::vector<Interval>& x, std::vector<Interval> const& b) const { + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "This operation is not supported."); + } +#endif + template<typename ValueType> void SparseMatrix<ValueType>::multiplyVectorWithMatrix(std::vector<value_type> const& vector, std::vector<value_type>& result) const { const_iterator it = this->begin(); From 662bbd73d7a434d4c5a846cf35b46560ab86ea9c Mon Sep 17 00:00:00 2001 From: Mavo <matthias.volk@rwth-aachen.de> Date: Mon, 18 Jul 2016 17:17:24 +0200 Subject: [PATCH 08/65] CompressedStateType as template argument for NextStateGenerator Former-commit-id: 145182a9180b4d2f13f67f964086cf40e51528e6 --- src/builder/ExplicitDFTModelBuilder.h | 2 +- src/generator/NextStateGenerator.h | 6 +++--- src/generator/PrismNextStateGenerator.h | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/builder/ExplicitDFTModelBuilder.h b/src/builder/ExplicitDFTModelBuilder.h index 71bfada61..eae19cb61 100644 --- a/src/builder/ExplicitDFTModelBuilder.h +++ b/src/builder/ExplicitDFTModelBuilder.h @@ -60,7 +60,7 @@ namespace storm { size_t initialStateIndex = 0; public: - struct LabelOptions { + struct LabelOptions { bool buildFailLabel = true; bool buildFailSafeLabel = false; std::set<std::string> beLabels = {}; diff --git a/src/generator/NextStateGenerator.h b/src/generator/NextStateGenerator.h index 7dab9f470..67fd5f9f6 100644 --- a/src/generator/NextStateGenerator.h +++ b/src/generator/NextStateGenerator.h @@ -11,15 +11,15 @@ namespace storm { namespace generator { - template<typename ValueType, typename StateType = uint32_t> + template<typename ValueType, typename CompressedStateType, typename StateType = uint32_t> class NextStateGenerator { public: - typedef std::function<StateType (CompressedState const&)> StateToIdCallback; + typedef std::function<StateType (CompressedStateType const&)> StateToIdCallback; virtual bool isDeterministicModel() const = 0; virtual std::vector<StateType> getInitialStates(StateToIdCallback const& stateToIdCallback) = 0; - virtual void load(CompressedState const& state) = 0; + virtual void load(CompressedStateType const& state) = 0; virtual StateBehavior<ValueType, StateType> expand(StateToIdCallback const& stateToIdCallback) = 0; virtual bool satisfies(storm::expressions::Expression const& expression) const = 0; }; diff --git a/src/generator/PrismNextStateGenerator.h b/src/generator/PrismNextStateGenerator.h index fe997d73a..fc1411aa0 100644 --- a/src/generator/PrismNextStateGenerator.h +++ b/src/generator/PrismNextStateGenerator.h @@ -13,9 +13,9 @@ namespace storm { namespace generator { template<typename ValueType, typename StateType = uint32_t> - class PrismNextStateGenerator : public NextStateGenerator<ValueType, StateType> { + class PrismNextStateGenerator : public NextStateGenerator<ValueType, CompressedState, StateType> { public: - typedef typename NextStateGenerator<ValueType, StateType>::StateToIdCallback StateToIdCallback; + typedef typename NextStateGenerator<ValueType, CompressedState, StateType>::StateToIdCallback StateToIdCallback; PrismNextStateGenerator(storm::prism::Program const& program, VariableInformation const& variableInformation, bool buildChoiceLabeling); From 12f7c08bacaac886ef03e131a605208a256adde0 Mon Sep 17 00:00:00 2001 From: Mavo <matthias.volk@rwth-aachen.de> Date: Mon, 18 Jul 2016 17:18:43 +0200 Subject: [PATCH 09/65] New class for DftStateGenerator Former-commit-id: 5d7f798de1071dd908e60d84330ba241104af22d --- examples/dft/and_approx.dft | 5 + examples/dft/and_approx_param.dft | 6 + examples/dft/nonmonoton.dft | 6 + src/generator/DftNextStateGenerator.cpp | 156 ++++++++++++++++++++++++ src/generator/DftNextStateGenerator.h | 40 ++++++ 5 files changed, 213 insertions(+) create mode 100644 examples/dft/and_approx.dft create mode 100644 examples/dft/and_approx_param.dft create mode 100644 examples/dft/nonmonoton.dft create mode 100644 src/generator/DftNextStateGenerator.cpp create mode 100644 src/generator/DftNextStateGenerator.h diff --git a/examples/dft/and_approx.dft b/examples/dft/and_approx.dft new file mode 100644 index 000000000..ff6393dd5 --- /dev/null +++ b/examples/dft/and_approx.dft @@ -0,0 +1,5 @@ +toplevel "A"; +"A" and "B" "C" "D"; +"B" lambda=1 dorm=0; +"C" lambda=100 dorm=0; +"D" lambda=50 dorm=0; diff --git a/examples/dft/and_approx_param.dft b/examples/dft/and_approx_param.dft new file mode 100644 index 000000000..bbd00bd80 --- /dev/null +++ b/examples/dft/and_approx_param.dft @@ -0,0 +1,6 @@ +param x; +toplevel "A"; +"A" and "B" "C" "D"; +"B" lambda=1 dorm=0; +"C" lambda=100 dorm=0; +"D" lambda=100*x dorm=0; diff --git a/examples/dft/nonmonoton.dft b/examples/dft/nonmonoton.dft new file mode 100644 index 000000000..0575028e8 --- /dev/null +++ b/examples/dft/nonmonoton.dft @@ -0,0 +1,6 @@ +toplevel "A"; +"A" or "B" "Z"; +"B" pand "D" "S"; +"Z" lambda=1 dorm=0; +"D" lambda=100 dorm=0; +"S" lambda=50 dorm=0; diff --git a/src/generator/DftNextStateGenerator.cpp b/src/generator/DftNextStateGenerator.cpp new file mode 100644 index 000000000..b56f8a358 --- /dev/null +++ b/src/generator/DftNextStateGenerator.cpp @@ -0,0 +1,156 @@ +#include "src/generator/DftNextStateGenerator.h" + +#include "src/utility/constants.h" +#include "src/utility/macros.h" +#include "src/exceptions/WrongFormatException.h" + +namespace storm { + namespace generator { + + template<typename ValueType, typename StateType> + DftNextStateGenerator<ValueType, StateType>::DftNextStateGenerator(storm::storage::DFT<ValueType> const& dft) : mDft(dft), state(nullptr), comparator() { + // Intentionally left empty. + } + + template<typename ValueType, typename StateType> + bool DftNextStateGenerator<ValueType, StateType>::isDeterministicModel() const { + assert(false); + return true; + } + + template<typename ValueType, typename StateType> + std::vector<StateType> DftNextStateGenerator<ValueType, StateType>::getInitialStates(StateToIdCallback const& stateToIdCallback) { + // FIXME: This only works for models with exactly one initial state. We should make this more general. + /*CompressedState initialState(variableInformation.getTotalBitOffset()); + + // We need to initialize the values of the variables to their initial value. + for (auto const& booleanVariable : variableInformation.booleanVariables) { + initialState.set(booleanVariable.bitOffset, booleanVariable.initialValue); + } + for (auto const& integerVariable : variableInformation.integerVariables) { + initialState.setFromInt(integerVariable.bitOffset, integerVariable.bitWidth, static_cast<uint_fast64_t>(integerVariable.initialValue - integerVariable.lowerBound)); + } + + // Register initial state and return it. + StateType id = stateToIdCallback(initialState); + return {id};*/ + } + + template<typename ValueType, typename StateType> + void DftNextStateGenerator<ValueType, StateType>::load(std::shared_ptr<storm::storage::DFTState<ValueType>> const& state) { + /*// Since almost all subsequent operations are based on the evaluator, we load the state into it now. + unpackStateIntoEvaluator(state, variableInformation, evaluator); + + // Also, we need to store a pointer to the state itself, because we need to be able to access it when expanding it. + this->state = &state;*/ + } + + template<typename ValueType, typename StateType> + bool DftNextStateGenerator<ValueType, StateType>::satisfies(storm::expressions::Expression const& expression) const { + /* if (expression.isTrue()) { + return true; + } + return evaluator.asBool(expression);*/ + } + + template<typename ValueType, typename StateType> + StateBehavior<ValueType, StateType> DftNextStateGenerator<ValueType, StateType>::expand(StateToIdCallback const& stateToIdCallback) { + /*// Prepare the result, in case we return early. + StateBehavior<ValueType, StateType> result; + + // First, construct the state rewards, as we may return early if there are no choices later and we already + // need the state rewards then. + for (auto const& rewardModel : selectedRewardModels) { + ValueType stateRewardValue = storm::utility::zero<ValueType>(); + if (rewardModel.get().hasStateRewards()) { + for (auto const& stateReward : rewardModel.get().getStateRewards()) { + if (evaluator.asBool(stateReward.getStatePredicateExpression())) { + stateRewardValue += ValueType(evaluator.asRational(stateReward.getRewardValueExpression())); + } + } + } + result.addStateReward(stateRewardValue); + } + + // If a terminal expression was set and we must not expand this state, return now. + if (terminalExpression && evaluator.asBool(terminalExpression.get())) { + return result; + } + + // Get all choices for the state. + std::vector<Choice<ValueType>> allChoices = getUnlabeledChoices(*this->state, stateToIdCallback); + std::vector<Choice<ValueType>> allLabeledChoices = getLabeledChoices(*this->state, stateToIdCallback); + for (auto& choice : allLabeledChoices) { + allChoices.push_back(std::move(choice)); + } + + std::size_t totalNumberOfChoices = allChoices.size(); + + // If there is not a single choice, we return immediately, because the state has no behavior (other than + // the state reward). + if (totalNumberOfChoices == 0) { + return result; + } + + // If the model is a deterministic model, we need to fuse the choices into one. + if (program.isDeterministicModel() && totalNumberOfChoices > 1) { + Choice<ValueType> globalChoice; + + // For CTMCs, we need to keep track of the total exit rate to scale the action rewards later. For DTMCs + // this is equal to the number of choices, which is why we initialize it like this here. + ValueType totalExitRate = program.isDiscreteTimeModel() ? static_cast<ValueType>(totalNumberOfChoices) : storm::utility::zero<ValueType>(); + + // Iterate over all choices and combine the probabilities/rates into one choice. + for (auto const& choice : allChoices) { + for (auto const& stateProbabilityPair : choice) { + if (program.isDiscreteTimeModel()) { + globalChoice.addProbability(stateProbabilityPair.first, stateProbabilityPair.second / totalNumberOfChoices); + } else { + globalChoice.addProbability(stateProbabilityPair.first, stateProbabilityPair.second); + } + } + + if (hasStateActionRewards && !program.isDiscreteTimeModel()) { + totalExitRate += choice.getTotalMass(); + } + + if (buildChoiceLabeling) { + globalChoice.addChoiceLabels(choice.getChoiceLabels()); + } + } + + // Now construct the state-action reward for all selected reward models. + for (auto const& rewardModel : selectedRewardModels) { + ValueType stateActionRewardValue = storm::utility::zero<ValueType>(); + if (rewardModel.get().hasStateActionRewards()) { + for (auto const& stateActionReward : rewardModel.get().getStateActionRewards()) { + for (auto const& choice : allChoices) { + if (stateActionReward.getActionIndex() == choice.getActionIndex() && evaluator.asBool(stateActionReward.getStatePredicateExpression())) { + stateActionRewardValue += ValueType(evaluator.asRational(stateActionReward.getRewardValueExpression())) * choice.getTotalMass() / totalExitRate; + } + } + + } + } + globalChoice.addChoiceReward(stateActionRewardValue); + } + + // Move the newly fused choice in place. + allChoices.clear(); + allChoices.push_back(std::move(globalChoice)); + } + + // Move all remaining choices in place. + for (auto& choice : allChoices) { + result.addChoice(std::move(choice)); + } + + result.setExpanded(); + return result;*/ + } + + + template class DftNextStateGenerator<double>; + template class DftNextStateGenerator<storm::RationalFunction>; + } +} \ No newline at end of file diff --git a/src/generator/DftNextStateGenerator.h b/src/generator/DftNextStateGenerator.h new file mode 100644 index 000000000..6c8e5538d --- /dev/null +++ b/src/generator/DftNextStateGenerator.h @@ -0,0 +1,40 @@ +#ifndef STORM_GENERATOR_DFTNEXTSTATEGENERATOR_H_ +#define STORM_GENERATOR_DFTNEXTSTATEGENERATOR_H_ + +#include "src/generator/NextStateGenerator.h" +#include "src/storage/dft/DFT.h" + +#include "src/utility/ConstantsComparator.h" + +namespace storm { + namespace generator { + + template<typename ValueType, typename StateType = uint32_t> + class DftNextStateGenerator : public NextStateGenerator<ValueType, std::shared_ptr<storm::storage::DFTState<ValueType>>, StateType> { + public: + typedef typename NextStateGenerator<ValueType, std::shared_ptr<storm::storage::DFTState<ValueType>>, StateType>::StateToIdCallback StateToIdCallback; + + DftNextStateGenerator(storm::storage::DFT<ValueType> const& dft); + + virtual bool isDeterministicModel() const override; + virtual std::vector<StateType> getInitialStates(StateToIdCallback const& stateToIdCallback) override; + + virtual void load(std::shared_ptr<storm::storage::DFTState<ValueType>> const& state) override; + virtual StateBehavior<ValueType, StateType> expand(StateToIdCallback const& stateToIdCallback) override; + virtual bool satisfies(storm::expressions::Expression const& expression) const override; + + private: + + // The program used for the generation of next states. + storm::storage::DFT<ValueType> const& mDft; + + CompressedState const* state; + + // A comparator used to compare constants. + storm::utility::ConstantsComparator<ValueType> comparator; + }; + + } +} + +#endif /* STORM_GENERATOR_DFTNEXTSTATEGENERATOR_H_ */ \ No newline at end of file From 495b42ff4c5fb9eba078f93ad864c0b51702e426 Mon Sep 17 00:00:00 2001 From: Mavo <matthias.volk@rwth-aachen.de> Date: Thu, 18 Aug 2016 17:09:15 +0200 Subject: [PATCH 10/65] Temporarily split new approximating state generation into own builder Former-commit-id: 70be02f2ae2e07585d9874fdfac80947e4979b8d --- examples/dft/mas.dft | 58 ++ examples/dft/nonmonoton_param.dft | 8 +- src/builder/ExplicitDFTModelBuilder.cpp | 40 -- src/builder/ExplicitDFTModelBuilder.h | 3 - src/builder/ExplicitDFTModelBuilderApprox.cpp | 666 ++++++++++++++++++ src/builder/ExplicitDFTModelBuilderApprox.h | 105 +++ src/generator/DftNextStateGenerator.cpp | 31 +- src/generator/DftNextStateGenerator.h | 15 +- src/modelchecker/DFTAnalyser.h | 30 +- src/storage/sparse/StateStorage.cpp | 1 + 10 files changed, 875 insertions(+), 82 deletions(-) create mode 100644 examples/dft/mas.dft create mode 100644 src/builder/ExplicitDFTModelBuilderApprox.cpp create mode 100644 src/builder/ExplicitDFTModelBuilderApprox.h diff --git a/examples/dft/mas.dft b/examples/dft/mas.dft new file mode 100644 index 000000000..bed57d59c --- /dev/null +++ b/examples/dft/mas.dft @@ -0,0 +1,58 @@ +toplevel "MAS"; +"MAS" or "CPU" "DB" "MB" "VMB" "MEM" "VMS"; +"CPU" or "CW" "SO1" "SO2" "PG" "SM"; +"CW" and "CWA" "CWB"; +"SO1" and "SO1A" "SO1B"; +"SO2" and "SO2A" "SO2B"; +"PG" and "PGA" "PGB"; +"SM" and "SMA" "SMB"; +"CWA" csp "CWAev" "S1" "S2"; +"CWB" csp "CWBev" "S1" "S2"; +"SO1A" csp "SO1Aev" "S1" "S2"; +"SO1B" csp "SO1Bev" "S1" "S2"; +"SO2A" csp "SO2Aev" "S1" "S2"; +"SO2B" csp "SO2Bev" "S1" "S2"; +"PGA" csp "PGAev" "S1" "S2"; +"PGB" csp "PGBev" "S1" "S2"; +"SMA" csp "SMAev" "S1" "S2"; +"SMB" csp "SMBev" "S1" "S2"; +"CWAev" lambda=1.0e-6 dorm=0; +"CWBev" lambda=1.0e-6 dorm=0; +"SO1Aev" lambda=1.0e-6 dorm=0; +"SO1Bev" lambda=1.0e-6 dorm=0; +"SO2Aev" lambda=1.0e-6 dorm=0; +"SO2Bev" lambda=1.0e-6 dorm=0; +"PGAev" lambda=1.0e-6 dorm=0; +"PGBev" lambda=1.0e-6 dorm=0; +"SMAev" lambda=1.0e-6 dorm=0; +"SMBev" lambda=1.0e-6 dorm=0; +"S1" lambda=1.0e-6 dorm=0; +"S2" lambda=1.0e-6 dorm=0; +"DB" and "DB1" "DB2" "DB3"; +"DB1" lambda=5.0e-6 dorm=0; +"DB2" lambda=5.0e-6 dorm=0; +"DB3" lambda=5.0e-6 dorm=0; +"MB" and "MB1" "MB2" "MB3"; +"MB1" lambda=5.0e-6 dorm=0; +"MB2" lambda=5.0e-6 dorm=0; +"MB3" lambda=5.0e-6 dorm=0; +"VMB" and "VMB1" "VMB2"; +"VMB1" lambda=5.0e-6 dorm=0; +"VMB2" lambda=5.0e-6 dorm=0; +"MEM" and "MEM1" "MEM2"; +"MEM1" lambda=1.0e-5 dorm=0; +"MEM2" lambda=1.0e-5 dorm=0; +"VMS" or "VM1" "VM2"; +"VM1" and "VM1A" "VM1B"; +"VM2" and "VM2A" "VM2B"; +"VM1A" csp "VM1Aev" "VMS1" "VMS2"; +"VM1B" csp "VM1Bev" "VMS1" "VMS2"; +"VM2A" csp "VM2Aev" "VMS1" "VMS2"; +"VM2B" csp "VM2Bev" "VMS1" "VMS2"; +"VM1Aev" lambda=1.0e-6 dorm=0; +"VM1Bev" lambda=1.0e-6 dorm=0; +"VM2Aev" lambda=1.0e-6 dorm=0; +"VM2Bev" lambda=1.0e-6 dorm=0; +"VMS1" lambda=1.0e-6 dorm=0; +"VMS2" lambda=1.0e-6 dorm=0; + diff --git a/examples/dft/nonmonoton_param.dft b/examples/dft/nonmonoton_param.dft index e83f3b012..fab011e64 100644 --- a/examples/dft/nonmonoton_param.dft +++ b/examples/dft/nonmonoton_param.dft @@ -2,7 +2,7 @@ param x; param y; toplevel "A"; "A" or "B" "Z"; -"B" pand "D" "S"; -"Z" lambda=y dorm=0; -"D" lambda=100 dorm=0; -"S" lambda=100*x dorm=0; +"Z" pand "C" "D"; +"B" lambda=y dorm=0; +"C" lambda=100 dorm=0; +"D" lambda=100*x dorm=0; diff --git a/src/builder/ExplicitDFTModelBuilder.cpp b/src/builder/ExplicitDFTModelBuilder.cpp index 4d794cb2f..46797aa23 100644 --- a/src/builder/ExplicitDFTModelBuilder.cpp +++ b/src/builder/ExplicitDFTModelBuilder.cpp @@ -160,23 +160,6 @@ namespace storm { return model; } - template<> - bool belowThreshold(double const& number) { - return number < 0.1; - } - - template<> - bool belowThreshold(storm::RationalFunction const& number) { - storm::RationalFunction threshold = storm::utility::one<storm::RationalFunction>() / 10; - std::cout << number << " < " << threshold << ": " << (number < threshold) << std::endl; - std::map<storm::Variable, storm::RationalNumber> mapping; - - storm::RationalFunction eval(number.evaluate(mapping)); - std::cout << "Evaluated: " << eval << std::endl; - return eval < threshold; - } - - template <typename ValueType> std::pair<uint_fast64_t, bool> ExplicitDFTModelBuilder<ValueType>::exploreStates(DFTStatePointer const& state, size_t& rowOffset, storm::storage::SparseMatrixBuilder<ValueType>& transitionMatrixBuilder, std::vector<uint_fast64_t>& markovianStates, std::vector<ValueType>& exitRates) { STORM_LOG_TRACE("Explore state: " << mDft.getStateString(state)); @@ -225,29 +208,6 @@ namespace storm { STORM_LOG_ASSERT(nextBE, "NextBE is null."); STORM_LOG_ASSERT(nextBEPair.second == hasDependencies, "Failure due to dependencies does not match."); STORM_LOG_TRACE("With the failure of: " << nextBE->name() << " [" << nextBE->id() << "] in " << mDft.getStateString(state)); - - if (storm::settings::getModule<storm::settings::modules::DFTSettings>().computeApproximation()) { - if (!storm::utility::isZero(exitRate)) { - ValueType rate = nextBE->activeFailureRate(); - ValueType div = rate / exitRate; - if (!storm::utility::isZero(exitRate) && belowThreshold(div)) { - // Set transition directly to failed state - auto resultFind = outgoingRates.find(failedIndex); - if (resultFind != outgoingRates.end()) { - // Add to existing transition - resultFind->second += rate; - STORM_LOG_TRACE("Updated transition to " << resultFind->first << " with rate " << rate << " to new rate " << resultFind->second); - } else { - // Insert new transition - outgoingRates.insert(std::make_pair(failedIndex, rate)); - STORM_LOG_TRACE("Added transition to " << failedIndex << " with rate " << rate); - } - exitRate += rate; - std::cout << "IGNORE: " << nextBE->name() << " [" << nextBE->id() << "] with rate " << rate << std::endl; - //STORM_LOG_TRACE("Ignore: " << nextBE->name() << " [" << nextBE->id() << "] with rate " << rate); - continue; - }} - } // Propagate failures storm::storage::DFTStateSpaceGenerationQueues<ValueType> queues; diff --git a/src/builder/ExplicitDFTModelBuilder.h b/src/builder/ExplicitDFTModelBuilder.h index eae19cb61..b9c54d2b6 100644 --- a/src/builder/ExplicitDFTModelBuilder.h +++ b/src/builder/ExplicitDFTModelBuilder.h @@ -91,9 +91,6 @@ namespace storm { std::pair<bool, uint_fast64_t> checkForExploration(DFTStatePointer const& state); }; - - template<typename ValueType> - bool belowThreshold(ValueType const& number); } } diff --git a/src/builder/ExplicitDFTModelBuilderApprox.cpp b/src/builder/ExplicitDFTModelBuilderApprox.cpp new file mode 100644 index 000000000..285301807 --- /dev/null +++ b/src/builder/ExplicitDFTModelBuilderApprox.cpp @@ -0,0 +1,666 @@ +#include "src/builder/ExplicitDFTModelBuilderApprox.h" +#include <src/models/sparse/MarkovAutomaton.h> +#include <src/models/sparse/Ctmc.h> +#include <src/utility/constants.h> +#include <src/utility/vector.h> +#include <src/exceptions/UnexpectedException.h> +#include "src/settings/modules/DFTSettings.h" +#include "src/settings/SettingsManager.h" +#include "src/generator/DftNextStateGenerator.h" +#include <map> + +namespace storm { + namespace builder { + + template <typename ValueType> + ExplicitDFTModelBuilderApprox<ValueType>::ModelComponents::ModelComponents() : transitionMatrix(), stateLabeling(), markovianStates(), exitRates(), choiceLabeling() { + // Intentionally left empty. + } + + template <typename ValueType> + ExplicitDFTModelBuilderApprox<ValueType>::ExplicitDFTModelBuilderApprox(storm::storage::DFT<ValueType> const& dft, storm::storage::DFTIndependentSymmetries const& symmetries, bool enableDC) : mDft(dft), enableDC(enableDC), stateStorage(((mDft.stateVectorSize() / 64) + 1) * 64) { + // stateVectorSize is bound for size of bitvector + + mStateGenerationInfo = std::make_shared<storm::storage::DFTStateGenerationInfo>(mDft.buildStateGenerationInfo(symmetries)); + } + + + template <typename ValueType> + std::shared_ptr<storm::models::sparse::Model<ValueType>> ExplicitDFTModelBuilderApprox<ValueType>::buildModel(LabelOptions const& labelOpts) { + // Initialize + bool deterministicModel = false; + size_t rowOffset = 0; + ModelComponents modelComponents; + std::vector<uint_fast64_t> tmpMarkovianStates; + storm::storage::SparseMatrixBuilder<ValueType> transitionMatrixBuilder(0, 0, 0, false, !deterministicModel, 0); + + if(mergeFailedStates) { + // Introduce explicit fail state + failedIndex = newIndex; + newIndex++; + transitionMatrixBuilder.newRowGroup(failedIndex); + transitionMatrixBuilder.addNextValue(failedIndex, failedIndex, storm::utility::one<ValueType>()); + STORM_LOG_TRACE("Introduce fail state with id: " << failedIndex); + modelComponents.exitRates.push_back(storm::utility::one<ValueType>()); + tmpMarkovianStates.push_back(failedIndex); + } + + // Explore state space + DFTStatePointer state = std::make_shared<storm::storage::DFTState<ValueType>>(mDft, *mStateGenerationInfo, newIndex); + auto exploreResult = exploreStates(state, rowOffset, transitionMatrixBuilder, tmpMarkovianStates, modelComponents.exitRates); + initialStateIndex = exploreResult.first; + bool deterministic = exploreResult.second; + + // Before ending the exploration check for pseudo states which are not initialized yet + for (auto & pseudoStatePair : mPseudoStatesMapping) { + if (pseudoStatePair.first == 0) { + // Create state from pseudo state and explore + STORM_LOG_ASSERT(stateStorage.stateToId.contains(pseudoStatePair.second), "Pseudo state not contained."); + STORM_LOG_ASSERT(stateStorage.stateToId.getValue(pseudoStatePair.second) >= OFFSET_PSEUDO_STATE, "State is no pseudo state."); + STORM_LOG_TRACE("Create pseudo state from bit vector " << pseudoStatePair.second); + DFTStatePointer pseudoState = std::make_shared<storm::storage::DFTState<ValueType>>(pseudoStatePair.second, mDft, *mStateGenerationInfo, newIndex); + STORM_LOG_ASSERT(pseudoStatePair.second == pseudoState->status(), "Pseudo states do not coincide."); + STORM_LOG_TRACE("Explore pseudo state " << mDft.getStateString(pseudoState) << " with id " << pseudoState->getId()); + auto exploreResult = exploreStates(pseudoState, rowOffset, transitionMatrixBuilder, tmpMarkovianStates, modelComponents.exitRates); + deterministic &= exploreResult.second; + STORM_LOG_ASSERT(pseudoStatePair.first == pseudoState->getId(), "Pseudo state ids do not coincide"); + STORM_LOG_ASSERT(pseudoState->getId() == exploreResult.first, "Pseudo state ids do not coincide."); + } + } + + // Replace pseudo states in matrix + std::vector<uint_fast64_t> pseudoStatesVector; + for (auto const& pseudoStatePair : mPseudoStatesMapping) { + pseudoStatesVector.push_back(pseudoStatePair.first); + } + STORM_LOG_ASSERT(std::find(pseudoStatesVector.begin(), pseudoStatesVector.end(), 0) == pseudoStatesVector.end(), "Unexplored pseudo state still contained."); + transitionMatrixBuilder.replaceColumns(pseudoStatesVector, OFFSET_PSEUDO_STATE); + + STORM_LOG_DEBUG("Generated " << stateStorage.getNumberOfStates() + (mergeFailedStates ? 1 : 0) << " states"); + STORM_LOG_DEBUG("Model is " << (deterministic ? "deterministic" : "non-deterministic")); + + size_t stateSize = stateStorage.getNumberOfStates() + (mergeFailedStates ? 1 : 0); + // Build Markov Automaton + modelComponents.markovianStates = storm::storage::BitVector(stateSize, tmpMarkovianStates); + // Build transition matrix + modelComponents.transitionMatrix = transitionMatrixBuilder.build(stateSize, stateSize); + if (stateSize <= 15) { + STORM_LOG_TRACE("Transition matrix: " << std::endl << modelComponents.transitionMatrix); + } else { + STORM_LOG_TRACE("Transition matrix: too big to print"); + } + STORM_LOG_TRACE("Exit rates: " << modelComponents.exitRates); + STORM_LOG_TRACE("Markovian states: " << modelComponents.markovianStates); + + // Build state labeling + modelComponents.stateLabeling = storm::models::sparse::StateLabeling(stateStorage.getNumberOfStates() + (mergeFailedStates ? 1 : 0)); + // Initial state is always first state without any failure + modelComponents.stateLabeling.addLabel("init"); + modelComponents.stateLabeling.addLabelToState("init", initialStateIndex); + // Label all states corresponding to their status (failed, failsafe, failed BE) + if(labelOpts.buildFailLabel) { + modelComponents.stateLabeling.addLabel("failed"); + } + if(labelOpts.buildFailSafeLabel) { + modelComponents.stateLabeling.addLabel("failsafe"); + } + + // Collect labels for all BE + std::vector<std::shared_ptr<storage::DFTBE<ValueType>>> basicElements = mDft.getBasicElements(); + for (std::shared_ptr<storage::DFTBE<ValueType>> elem : basicElements) { + if(labelOpts.beLabels.count(elem->name()) > 0) { + modelComponents.stateLabeling.addLabel(elem->name() + "_fail"); + } + } + + if(mergeFailedStates) { + modelComponents.stateLabeling.addLabelToState("failed", failedIndex); + } + for (auto const& stateIdPair : stateStorage.stateToId) { + storm::storage::BitVector state = stateIdPair.first; + size_t stateId = stateIdPair.second; + if (!mergeFailedStates && labelOpts.buildFailLabel && mDft.hasFailed(state, *mStateGenerationInfo)) { + modelComponents.stateLabeling.addLabelToState("failed", stateId); + } + if (labelOpts.buildFailSafeLabel && mDft.isFailsafe(state, *mStateGenerationInfo)) { + modelComponents.stateLabeling.addLabelToState("failsafe", stateId); + }; + // Set fail status for each BE + for (std::shared_ptr<storage::DFTBE<ValueType>> elem : basicElements) { + if (labelOpts.beLabels.count(elem->name()) > 0 && storm::storage::DFTState<ValueType>::hasFailed(state, mStateGenerationInfo->getStateIndex(elem->id())) ) { + modelComponents.stateLabeling.addLabelToState(elem->name() + "_fail", stateId); + } + } + } + + std::shared_ptr<storm::models::sparse::Model<ValueType>> model; + + if (deterministic) { + // Turn the probabilities into rates by multiplying each row with the exit rate of the state. + // TODO Matthias: avoid transforming back and forth + storm::storage::SparseMatrix<ValueType> rateMatrix(modelComponents.transitionMatrix); + for (uint_fast64_t row = 0; row < rateMatrix.getRowCount(); ++row) { + STORM_LOG_ASSERT(row < modelComponents.markovianStates.size(), "Row exceeds no. of markovian states."); + if (modelComponents.markovianStates.get(row)) { + for (auto& entry : rateMatrix.getRow(row)) { + entry.setValue(entry.getValue() * modelComponents.exitRates[row]); + } + } + } + model = std::make_shared<storm::models::sparse::Ctmc<ValueType>>(std::move(rateMatrix), std::move(modelComponents.exitRates), std::move(modelComponents.stateLabeling)); + } else { + std::shared_ptr<storm::models::sparse::MarkovAutomaton<ValueType>> ma = std::make_shared<storm::models::sparse::MarkovAutomaton<ValueType>>(std::move(modelComponents.transitionMatrix), std::move(modelComponents.stateLabeling), std::move(modelComponents.markovianStates), std::move(modelComponents.exitRates), true); + if (ma->hasOnlyTrivialNondeterminism()) { + // Markov automaton can be converted into CTMC + model = ma->convertToCTMC(); + } else { + model = ma; + } + } + + return model; + } + + template <typename ValueType> + std::shared_ptr<storm::models::sparse::Model<ValueType>> ExplicitDFTModelBuilderApprox<ValueType>::buildModelApprox(LabelOptions const& labelOpts) { + // Initialize + bool deterministicModel = false; + size_t rowOffset = 0; + ModelComponents modelComponents; + std::vector<uint_fast64_t> tmpMarkovianStates; + storm::storage::SparseMatrixBuilder<ValueType> transitionMatrixBuilder(0, 0, 0, false, !deterministicModel, 0); + + if(mergeFailedStates) { + // Introduce explicit fail state + failedIndex = newIndex; + newIndex++; + transitionMatrixBuilder.newRowGroup(failedIndex); + transitionMatrixBuilder.addNextValue(failedIndex, failedIndex, storm::utility::one<ValueType>()); + STORM_LOG_TRACE("Introduce fail state with id: " << failedIndex); + modelComponents.exitRates.push_back(storm::utility::one<ValueType>()); + tmpMarkovianStates.push_back(failedIndex); + } + + // Initialize generator + storm::generator::DftNextStateGenerator<ValueType, uint_fast64_t> generator(mDft, *mStateGenerationInfo); + + // Explore state space + typename storm::generator::DftNextStateGenerator<ValueType, uint_fast64_t>::StateToIdCallback stateToIdCallback = [this] (DFTStatePointer const& state) -> uint_fast64_t { + uint_fast64_t id = newIndex++; + std::cout << "Added state " << id << std::endl; + return id; + }; + + uint_fast64_t id = generator.getInitialStates(stateToIdCallback)[0]; + std::cout << "Initial state " << id << std::endl; + DFTStatePointer state = std::make_shared<storm::storage::DFTState<ValueType>>(mDft, *mStateGenerationInfo, newIndex); + auto exploreResult = exploreStates(state, rowOffset, transitionMatrixBuilder, tmpMarkovianStates, modelComponents.exitRates); + initialStateIndex = exploreResult.first; + bool deterministic = exploreResult.second; + + // Before ending the exploration check for pseudo states which are not initialized yet + for (auto & pseudoStatePair : mPseudoStatesMapping) { + if (pseudoStatePair.first == 0) { + // Create state from pseudo state and explore + STORM_LOG_ASSERT(stateStorage.stateToId.contains(pseudoStatePair.second), "Pseudo state not contained."); + STORM_LOG_ASSERT(stateStorage.stateToId.getValue(pseudoStatePair.second) >= OFFSET_PSEUDO_STATE, "State is no pseudo state."); + STORM_LOG_TRACE("Create pseudo state from bit vector " << pseudoStatePair.second); + DFTStatePointer pseudoState = std::make_shared<storm::storage::DFTState<ValueType>>(pseudoStatePair.second, mDft, *mStateGenerationInfo, newIndex); + STORM_LOG_ASSERT(pseudoStatePair.second == pseudoState->status(), "Pseudo states do not coincide."); + STORM_LOG_TRACE("Explore pseudo state " << mDft.getStateString(pseudoState) << " with id " << pseudoState->getId()); + auto exploreResult = exploreStates(pseudoState, rowOffset, transitionMatrixBuilder, tmpMarkovianStates, modelComponents.exitRates); + deterministic &= exploreResult.second; + STORM_LOG_ASSERT(pseudoStatePair.first == pseudoState->getId(), "Pseudo state ids do not coincide"); + STORM_LOG_ASSERT(pseudoState->getId() == exploreResult.first, "Pseudo state ids do not coincide."); + } + } + + // Replace pseudo states in matrix + std::vector<uint_fast64_t> pseudoStatesVector; + for (auto const& pseudoStatePair : mPseudoStatesMapping) { + pseudoStatesVector.push_back(pseudoStatePair.first); + } + STORM_LOG_ASSERT(std::find(pseudoStatesVector.begin(), pseudoStatesVector.end(), 0) == pseudoStatesVector.end(), "Unexplored pseudo state still contained."); + transitionMatrixBuilder.replaceColumns(pseudoStatesVector, OFFSET_PSEUDO_STATE); + + STORM_LOG_DEBUG("Generated " << stateStorage.getNumberOfStates() + (mergeFailedStates ? 1 : 0) << " states"); + STORM_LOG_DEBUG("Model is " << (deterministic ? "deterministic" : "non-deterministic")); + + size_t stateSize = stateStorage.getNumberOfStates() + (mergeFailedStates ? 1 : 0); + // Build Markov Automaton + modelComponents.markovianStates = storm::storage::BitVector(stateSize, tmpMarkovianStates); + // Build transition matrix + modelComponents.transitionMatrix = transitionMatrixBuilder.build(stateSize, stateSize); + if (stateSize <= 15) { + STORM_LOG_TRACE("Transition matrix: " << std::endl << modelComponents.transitionMatrix); + } else { + STORM_LOG_TRACE("Transition matrix: too big to print"); + } + STORM_LOG_TRACE("Exit rates: " << modelComponents.exitRates); + STORM_LOG_TRACE("Markovian states: " << modelComponents.markovianStates); + + // Build state labeling + modelComponents.stateLabeling = storm::models::sparse::StateLabeling(stateStorage.getNumberOfStates() + (mergeFailedStates ? 1 : 0)); + // Initial state is always first state without any failure + modelComponents.stateLabeling.addLabel("init"); + modelComponents.stateLabeling.addLabelToState("init", initialStateIndex); + // Label all states corresponding to their status (failed, failsafe, failed BE) + if(labelOpts.buildFailLabel) { + modelComponents.stateLabeling.addLabel("failed"); + } + if(labelOpts.buildFailSafeLabel) { + modelComponents.stateLabeling.addLabel("failsafe"); + } + + // Collect labels for all BE + std::vector<std::shared_ptr<storage::DFTBE<ValueType>>> basicElements = mDft.getBasicElements(); + for (std::shared_ptr<storage::DFTBE<ValueType>> elem : basicElements) { + if(labelOpts.beLabels.count(elem->name()) > 0) { + modelComponents.stateLabeling.addLabel(elem->name() + "_fail"); + } + } + + if(mergeFailedStates) { + modelComponents.stateLabeling.addLabelToState("failed", failedIndex); + } + for (auto const& stateIdPair : stateStorage.stateToId) { + storm::storage::BitVector state = stateIdPair.first; + size_t stateId = stateIdPair.second; + if (!mergeFailedStates && labelOpts.buildFailLabel && mDft.hasFailed(state, *mStateGenerationInfo)) { + modelComponents.stateLabeling.addLabelToState("failed", stateId); + } + if (labelOpts.buildFailSafeLabel && mDft.isFailsafe(state, *mStateGenerationInfo)) { + modelComponents.stateLabeling.addLabelToState("failsafe", stateId); + }; + // Set fail status for each BE + for (std::shared_ptr<storage::DFTBE<ValueType>> elem : basicElements) { + if (labelOpts.beLabels.count(elem->name()) > 0 && storm::storage::DFTState<ValueType>::hasFailed(state, mStateGenerationInfo->getStateIndex(elem->id())) ) { + modelComponents.stateLabeling.addLabelToState(elem->name() + "_fail", stateId); + } + } + } + + std::shared_ptr<storm::models::sparse::Model<ValueType>> model; + + if (deterministic) { + // Turn the probabilities into rates by multiplying each row with the exit rate of the state. + // TODO Matthias: avoid transforming back and forth + storm::storage::SparseMatrix<ValueType> rateMatrix(modelComponents.transitionMatrix); + for (uint_fast64_t row = 0; row < rateMatrix.getRowCount(); ++row) { + STORM_LOG_ASSERT(row < modelComponents.markovianStates.size(), "Row exceeds no. of markovian states."); + if (modelComponents.markovianStates.get(row)) { + for (auto& entry : rateMatrix.getRow(row)) { + entry.setValue(entry.getValue() * modelComponents.exitRates[row]); + } + } + } + model = std::make_shared<storm::models::sparse::Ctmc<ValueType>>(std::move(rateMatrix), std::move(modelComponents.exitRates), std::move(modelComponents.stateLabeling)); + } else { + std::shared_ptr<storm::models::sparse::MarkovAutomaton<ValueType>> ma = std::make_shared<storm::models::sparse::MarkovAutomaton<ValueType>>(std::move(modelComponents.transitionMatrix), std::move(modelComponents.stateLabeling), std::move(modelComponents.markovianStates), std::move(modelComponents.exitRates), true); + if (ma->hasOnlyTrivialNondeterminism()) { + // Markov automaton can be converted into CTMC + model = ma->convertToCTMC(); + } else { + model = ma; + } + } + + return model; + + } + + template<> + bool belowThreshold(double const& number) { + return number < 0.1; + } + + template<> + bool belowThreshold(storm::RationalFunction const& number) { + storm::RationalFunction threshold = storm::utility::one<storm::RationalFunction>() / 10; + std::cout << number << " < " << threshold << ": " << (number < threshold) << std::endl; + std::map<storm::Variable, storm::RationalNumber> mapping; + + storm::RationalFunction eval(number.evaluate(mapping)); + std::cout << "Evaluated: " << eval << std::endl; + return eval < threshold; + } + + + template <typename ValueType> + std::pair<uint_fast64_t, bool> ExplicitDFTModelBuilderApprox<ValueType>::exploreStates(DFTStatePointer const& state, size_t& rowOffset, storm::storage::SparseMatrixBuilder<ValueType>& transitionMatrixBuilder, std::vector<uint_fast64_t>& markovianStates, std::vector<ValueType>& exitRates) { + STORM_LOG_TRACE("Explore state: " << mDft.getStateString(state)); + + auto explorePair = checkForExploration(state); + if (!explorePair.first) { + // State does not need any exploration + return std::make_pair(explorePair.second, true); + } + + + // Initialization + // TODO Matthias: set Markovian states directly as bitvector? + std::map<uint_fast64_t, ValueType> outgoingRates; + std::vector<std::map<uint_fast64_t, ValueType>> outgoingProbabilities; + bool hasDependencies = state->nrFailableDependencies() > 0; + size_t failableCount = hasDependencies ? state->nrFailableDependencies() : state->nrFailableBEs(); + size_t smallest = 0; + ValueType exitRate = storm::utility::zero<ValueType>(); + bool deterministic = !hasDependencies; + + // Absorbing state + if (mDft.hasFailed(state) || mDft.isFailsafe(state) || state->nrFailableBEs() == 0) { + uint_fast64_t stateId = addState(state); + STORM_LOG_ASSERT(stateId == state->getId(), "Ids do not coincide."); + + // Add self loop + transitionMatrixBuilder.newRowGroup(stateId + rowOffset); + transitionMatrixBuilder.addNextValue(stateId + rowOffset, stateId, storm::utility::one<ValueType>()); + STORM_LOG_TRACE("Added self loop for " << stateId); + exitRates.push_back(storm::utility::one<ValueType>()); + STORM_LOG_ASSERT(exitRates.size()-1 == stateId, "No. of considered states does not match state id."); + markovianStates.push_back(stateId); + // No further exploration required + return std::make_pair(stateId, true); + } + + // Let BE fail + while (smallest < failableCount) { + STORM_LOG_ASSERT(!mDft.hasFailed(state), "Dft has failed."); + + // Construct new state as copy from original one + DFTStatePointer newState = std::make_shared<storm::storage::DFTState<ValueType>>(*state); + std::pair<std::shared_ptr<storm::storage::DFTBE<ValueType> const>, bool> nextBEPair = newState->letNextBEFail(smallest++); + std::shared_ptr<storm::storage::DFTBE<ValueType> const>& nextBE = nextBEPair.first; + STORM_LOG_ASSERT(nextBE, "NextBE is null."); + STORM_LOG_ASSERT(nextBEPair.second == hasDependencies, "Failure due to dependencies does not match."); + STORM_LOG_TRACE("With the failure of: " << nextBE->name() << " [" << nextBE->id() << "] in " << mDft.getStateString(state)); + + if (storm::settings::getModule<storm::settings::modules::DFTSettings>().computeApproximation()) { + if (!storm::utility::isZero(exitRate)) { + ValueType rate = nextBE->activeFailureRate(); + ValueType div = rate / exitRate; + if (!storm::utility::isZero(exitRate) && belowThreshold(div)) { + // Set transition directly to failed state + auto resultFind = outgoingRates.find(failedIndex); + if (resultFind != outgoingRates.end()) { + // Add to existing transition + resultFind->second += rate; + STORM_LOG_TRACE("Updated transition to " << resultFind->first << " with rate " << rate << " to new rate " << resultFind->second); + } else { + // Insert new transition + outgoingRates.insert(std::make_pair(failedIndex, rate)); + STORM_LOG_TRACE("Added transition to " << failedIndex << " with rate " << rate); + } + exitRate += rate; + std::cout << "IGNORE: " << nextBE->name() << " [" << nextBE->id() << "] with rate " << rate << std::endl; + //STORM_LOG_TRACE("Ignore: " << nextBE->name() << " [" << nextBE->id() << "] with rate " << rate); + continue; + } + } + } + + // Propagate failures + storm::storage::DFTStateSpaceGenerationQueues<ValueType> queues; + + for (DFTGatePointer parent : nextBE->parents()) { + if (newState->isOperational(parent->id())) { + queues.propagateFailure(parent); + } + } + for (DFTRestrictionPointer restr : nextBE->restrictions()) { + queues.checkRestrictionLater(restr); + } + + while (!queues.failurePropagationDone()) { + DFTGatePointer next = queues.nextFailurePropagation(); + next->checkFails(*newState, queues); + newState->updateFailableDependencies(next->id()); + } + + while(!queues.restrictionChecksDone()) { + DFTRestrictionPointer next = queues.nextRestrictionCheck(); + next->checkFails(*newState, queues); + newState->updateFailableDependencies(next->id()); + } + + if(newState->isInvalid()) { + continue; + } + bool dftFailed = newState->hasFailed(mDft.getTopLevelIndex()); + + while (!dftFailed && !queues.failsafePropagationDone()) { + DFTGatePointer next = queues.nextFailsafePropagation(); + next->checkFailsafe(*newState, queues); + } + + while (!dftFailed && enableDC && !queues.dontCarePropagationDone()) { + DFTElementPointer next = queues.nextDontCarePropagation(); + next->checkDontCareAnymore(*newState, queues); + } + + // Update failable dependencies + if (!dftFailed) { + newState->updateFailableDependencies(nextBE->id()); + newState->updateDontCareDependencies(nextBE->id()); + } + + uint_fast64_t newStateId; + if(dftFailed && mergeFailedStates) { + newStateId = failedIndex; + } else { + // Explore new state recursively + auto explorePair = exploreStates(newState, rowOffset, transitionMatrixBuilder, markovianStates, exitRates); + newStateId = explorePair.first; + deterministic &= explorePair.second; + } + + // Set transitions + if (hasDependencies) { + // Failure is due to dependency -> add non-deterministic choice + std::map<uint_fast64_t, ValueType> choiceProbabilities; + std::shared_ptr<storm::storage::DFTDependency<ValueType> const> dependency = mDft.getDependency(state->getDependencyId(smallest-1)); + choiceProbabilities.insert(std::make_pair(newStateId, dependency->probability())); + STORM_LOG_TRACE("Added transition to " << newStateId << " with probability " << dependency->probability()); + + if (!storm::utility::isOne(dependency->probability())) { + // Add transition to state where dependency was unsuccessful + DFTStatePointer unsuccessfulState = std::make_shared<storm::storage::DFTState<ValueType>>(*state); + unsuccessfulState->letDependencyBeUnsuccessful(smallest-1); + auto explorePair = exploreStates(unsuccessfulState, rowOffset, transitionMatrixBuilder, markovianStates, exitRates); + uint_fast64_t unsuccessfulStateId = explorePair.first; + deterministic &= explorePair.second; + ValueType remainingProbability = storm::utility::one<ValueType>() - dependency->probability(); + choiceProbabilities.insert(std::make_pair(unsuccessfulStateId, remainingProbability)); + STORM_LOG_TRACE("Added transition to " << unsuccessfulStateId << " with remaining probability " << remainingProbability); + } + outgoingProbabilities.push_back(choiceProbabilities); + } else { + // Set failure rate according to activation + bool isActive = true; + if (mDft.hasRepresentant(nextBE->id())) { + // Active must be checked for the state we are coming from as this state is responsible for the + // rate and not the new state we are going to + isActive = state->isActive(mDft.getRepresentant(nextBE->id())->id()); + } + ValueType rate = isActive ? nextBE->activeFailureRate() : nextBE->passiveFailureRate(); + STORM_LOG_ASSERT(!storm::utility::isZero(rate), "Rate is 0."); + auto resultFind = outgoingRates.find(newStateId); + if (resultFind != outgoingRates.end()) { + // Add to existing transition + resultFind->second += rate; + STORM_LOG_TRACE("Updated transition to " << resultFind->first << " with " << (isActive ? "active" : "passive") << " rate " << rate << " to new rate " << resultFind->second); + } else { + // Insert new transition + outgoingRates.insert(std::make_pair(newStateId, rate)); + STORM_LOG_TRACE("Added transition to " << newStateId << " with " << (isActive ? "active" : "passive") << " rate " << rate); + } + exitRate += rate; + } + + } // end while failing BE + + // Add state + uint_fast64_t stateId = addState(state); + STORM_LOG_ASSERT(stateId == state->getId(), "Ids do not match."); + STORM_LOG_ASSERT(stateId == newIndex-1, "Id does not match no. of states."); + + if (hasDependencies) { + // Add all probability transitions + STORM_LOG_ASSERT(outgoingRates.empty(), "Outgoing transitions not empty."); + transitionMatrixBuilder.newRowGroup(stateId + rowOffset); + for (size_t i = 0; i < outgoingProbabilities.size(); ++i, ++rowOffset) { + STORM_LOG_ASSERT(outgoingProbabilities[i].size() == 1 || outgoingProbabilities[i].size() == 2, "No. of outgoing transitions is not valid."); + for (auto it = outgoingProbabilities[i].begin(); it != outgoingProbabilities[i].end(); ++it) + { + STORM_LOG_TRACE("Set transition from " << stateId << " to " << it->first << " with probability " << it->second); + transitionMatrixBuilder.addNextValue(stateId + rowOffset, it->first, it->second); + } + } + rowOffset--; // One increment too many + } else { + // Try to merge pseudo states with their instantiation + // TODO Matthias: improve? + for (auto it = outgoingRates.begin(); it != outgoingRates.end(); ) { + if (it->first >= OFFSET_PSEUDO_STATE) { + uint_fast64_t newId = it->first - OFFSET_PSEUDO_STATE; + STORM_LOG_ASSERT(newId < mPseudoStatesMapping.size(), "Id is not valid."); + if (mPseudoStatesMapping[newId].first > 0) { + // State exists already + newId = mPseudoStatesMapping[newId].first; + auto itFind = outgoingRates.find(newId); + if (itFind != outgoingRates.end()) { + // Add probability from pseudo state to instantiation + itFind->second += it->second; + STORM_LOG_TRACE("Merged pseudo state " << newId << " adding rate " << it->second << " to total rate of " << itFind->second); + } else { + // Only change id + outgoingRates.emplace(newId, it->second); + STORM_LOG_TRACE("Instantiated pseudo state " << newId << " with rate " << it->second); + } + // Remove pseudo state + it = outgoingRates.erase(it); + } else { + ++it; + } + } else { + ++it; + } + } + + // Add all rate transitions + STORM_LOG_ASSERT(outgoingProbabilities.empty(), "Outgoing probabilities not empty."); + transitionMatrixBuilder.newRowGroup(state->getId() + rowOffset); + STORM_LOG_TRACE("Exit rate for " << state->getId() << ": " << exitRate); + for (auto it = outgoingRates.begin(); it != outgoingRates.end(); ++it) + { + ValueType probability = it->second / exitRate; // Transform rate to probability + STORM_LOG_TRACE("Set transition from " << state->getId() << " to " << it->first << " with rate " << it->second); + transitionMatrixBuilder.addNextValue(state->getId() + rowOffset, it->first, probability); + } + + markovianStates.push_back(state->getId()); + } + + STORM_LOG_TRACE("Finished exploring state: " << mDft.getStateString(state)); + exitRates.push_back(exitRate); + STORM_LOG_ASSERT(exitRates.size()-1 == state->getId(), "Id does not match no. of states."); + return std::make_pair(state->getId(), deterministic); + } + + template <typename ValueType> + std::pair<bool, uint_fast64_t> ExplicitDFTModelBuilderApprox<ValueType>::checkForExploration(DFTStatePointer const& state) { + bool changed = false; + if (mStateGenerationInfo->hasSymmetries()) { + // Order state by symmetry + STORM_LOG_TRACE("Check for symmetry: " << mDft.getStateString(state)); + changed = state->orderBySymmetry(); + STORM_LOG_TRACE("State " << (changed ? "changed to " : "did not change") << (changed ? mDft.getStateString(state) : "")); + } + + if (stateStorage.stateToId.contains(state->status())) { + // State already exists + uint_fast64_t stateId = stateStorage.stateToId.getValue(state->status()); + STORM_LOG_TRACE("State " << mDft.getStateString(state) << " with id " << stateId << " already exists"); + + if (changed || stateId < OFFSET_PSEUDO_STATE) { + // State is changed or an explored "normal" state + return std::make_pair(false, stateId); + } + + stateId -= OFFSET_PSEUDO_STATE; + STORM_LOG_ASSERT(stateId < mPseudoStatesMapping.size(), "Id not valid."); + if (mPseudoStatesMapping[stateId].first > 0) { + // Pseudo state already explored + return std::make_pair(false, mPseudoStatesMapping[stateId].first); + } + + STORM_LOG_ASSERT(mPseudoStatesMapping[stateId].second == state->status(), "States do not coincide."); + STORM_LOG_TRACE("Pseudo state " << mDft.getStateString(state) << " can be explored now"); + return std::make_pair(true, stateId); + } else { + // State does not exists + if (changed) { + // Remember state for later creation + state->setId(mPseudoStatesMapping.size() + OFFSET_PSEUDO_STATE); + mPseudoStatesMapping.push_back(std::make_pair(0, state->status())); + stateStorage.stateToId.findOrAdd(state->status(), state->getId()); + STORM_LOG_TRACE("Remember state for later creation: " << mDft.getStateString(state)); + return std::make_pair(false, state->getId()); + } else { + // State needs exploration + return std::make_pair(true, 0); + } + } + } + + template <typename ValueType> + uint_fast64_t ExplicitDFTModelBuilderApprox<ValueType>::addState(DFTStatePointer const& state) { + uint_fast64_t stateId; + // TODO remove + bool changed = state->orderBySymmetry(); + STORM_LOG_ASSERT(!changed, "State to add has changed by applying symmetry."); + + // Check if state already exists + if (stateStorage.stateToId.contains(state->status())) { + // State already exists + stateId = stateStorage.stateToId.getValue(state->status()); + STORM_LOG_TRACE("State " << mDft.getStateString(state) << " with id " << stateId << " already exists"); + + // Check if possible pseudo state can be created now + STORM_LOG_ASSERT(stateId >= OFFSET_PSEUDO_STATE, "State is no pseudo state."); + stateId -= OFFSET_PSEUDO_STATE; + STORM_LOG_ASSERT(stateId < mPseudoStatesMapping.size(), "Pseudo state not known."); + if (mPseudoStatesMapping[stateId].first == 0) { + // Create pseudo state now + STORM_LOG_ASSERT(mPseudoStatesMapping[stateId].second == state->status(), "Pseudo states do not coincide."); + state->setId(newIndex++); + mPseudoStatesMapping[stateId].first = state->getId(); + stateId = state->getId(); + stateStorage.stateToId.setOrAdd(state->status(), stateId); + STORM_LOG_TRACE("Now create state " << mDft.getStateString(state) << " with id " << stateId); + return stateId; + } else { + STORM_LOG_ASSERT(false, "Pseudo state already created."); + return 0; + } + } else { + // Create new state + state->setId(newIndex++); + stateId = stateStorage.stateToId.findOrAdd(state->status(), state->getId()); + STORM_LOG_TRACE("New state: " << mDft.getStateString(state)); + return stateId; + } + } + + + // Explicitly instantiate the class. + template class ExplicitDFTModelBuilderApprox<double>; + +#ifdef STORM_HAVE_CARL + template class ExplicitDFTModelBuilderApprox<storm::RationalFunction>; +#endif + + } // namespace builder +} // namespace storm + + diff --git a/src/builder/ExplicitDFTModelBuilderApprox.h b/src/builder/ExplicitDFTModelBuilderApprox.h new file mode 100644 index 000000000..d03681cbe --- /dev/null +++ b/src/builder/ExplicitDFTModelBuilderApprox.h @@ -0,0 +1,105 @@ +#ifndef EXPLICITDFTMODELBUILDERAPPROX_H +#define EXPLICITDFTMODELBUILDERAPPROX_H + +#include <src/models/sparse/StateLabeling.h> +#include <src/models/sparse/StandardRewardModel.h> +#include <src/models/sparse/Model.h> +#include <src/storage/SparseMatrix.h> +#include "src/storage/sparse/StateStorage.h" +#include <src/storage/dft/DFT.h> +#include <src/storage/dft/SymmetricUnits.h> +#include <boost/container/flat_set.hpp> +#include <boost/optional/optional.hpp> +#include <stack> +#include <unordered_set> + +namespace storm { + namespace builder { + + template<typename ValueType> + class ExplicitDFTModelBuilderApprox { + + using DFTElementPointer = std::shared_ptr<storm::storage::DFTElement<ValueType>>; + using DFTElementCPointer = std::shared_ptr<storm::storage::DFTElement<ValueType> const>; + using DFTGatePointer = std::shared_ptr<storm::storage::DFTGate<ValueType>>; + using DFTStatePointer = std::shared_ptr<storm::storage::DFTState<ValueType>>; + using DFTRestrictionPointer = std::shared_ptr<storm::storage::DFTRestriction<ValueType>>; + + + // A structure holding the individual components of a model. + struct ModelComponents { + ModelComponents(); + + // The transition matrix. + storm::storage::SparseMatrix<ValueType> transitionMatrix; + + // The state labeling. + storm::models::sparse::StateLabeling stateLabeling; + + // The Markovian states. + storm::storage::BitVector markovianStates; + + // The exit rates. + std::vector<ValueType> exitRates; + + // A vector that stores a labeling for each choice. + boost::optional<std::vector<boost::container::flat_set<uint_fast64_t>>> choiceLabeling; + }; + + const uint_fast64_t OFFSET_PSEUDO_STATE = UINT_FAST64_MAX / 2; + + storm::storage::DFT<ValueType> const& mDft; + std::shared_ptr<storm::storage::DFTStateGenerationInfo> mStateGenerationInfo; + //TODO Matthias: remove when everything works + std::vector<std::pair<uint_fast64_t, storm::storage::BitVector>> mPseudoStatesMapping; // vector of (id to concrete state, bitvector) + size_t newIndex = 0; + bool mergeFailedStates = true; + bool enableDC = true; + size_t failedIndex = 0; + size_t initialStateIndex = 0; + + // Internal information about the states that were explored. + storm::storage::sparse::StateStorage<uint_fast64_t> stateStorage; + + public: + struct LabelOptions { + bool buildFailLabel = true; + bool buildFailSafeLabel = false; + std::set<std::string> beLabels = {}; + }; + + ExplicitDFTModelBuilderApprox(storm::storage::DFT<ValueType> const& dft, storm::storage::DFTIndependentSymmetries const& symmetries, bool enableDC); + + std::shared_ptr<storm::models::sparse::Model<ValueType>> buildModel(LabelOptions const& labelOpts); + + // TODO Matthias: only temporary used for avoiding crashing everything + std::shared_ptr<storm::models::sparse::Model<ValueType>> buildModelApprox(LabelOptions const& labelOpts); + + private: + std::pair<uint_fast64_t, bool> exploreStates(DFTStatePointer const& state, size_t& rowOffset, storm::storage::SparseMatrixBuilder<ValueType>& transitionMatrixBuilder, std::vector<uint_fast64_t>& markovianStates, std::vector<ValueType>& exitRates); + + /*! + * Adds a state to the explored states and handles pseudo states. + * + * @param state The state to add. + * @return Id of added state. + */ + uint_fast64_t addState(DFTStatePointer const& state); + + /*! + * Check if state needs an exploration and remember pseudo states for later creation. + * + * @param state State which might need exploration. + * @return Pair of flag indicating whether the state needs exploration now and the state id if the state already + * exists. + */ + std::pair<bool, uint_fast64_t> checkForExploration(DFTStatePointer const& state); + + }; + + template<typename ValueType> + bool belowThreshold(ValueType const& number); + } +} + +#endif /* EXPLICITDFTMODELBUILDERAPPROX_H */ diff --git a/src/generator/DftNextStateGenerator.cpp b/src/generator/DftNextStateGenerator.cpp index b56f8a358..3138fef6a 100644 --- a/src/generator/DftNextStateGenerator.cpp +++ b/src/generator/DftNextStateGenerator.cpp @@ -2,38 +2,30 @@ #include "src/utility/constants.h" #include "src/utility/macros.h" -#include "src/exceptions/WrongFormatException.h" +#include "src/exceptions/NotImplementedException.h" namespace storm { namespace generator { template<typename ValueType, typename StateType> - DftNextStateGenerator<ValueType, StateType>::DftNextStateGenerator(storm::storage::DFT<ValueType> const& dft) : mDft(dft), state(nullptr), comparator() { + DftNextStateGenerator<ValueType, StateType>::DftNextStateGenerator(storm::storage::DFT<ValueType> const& dft, storm::storage::DFTStateGenerationInfo const& stateGenerationInfo) : mDft(dft), mStateGenerationInfo(stateGenerationInfo), state(nullptr), comparator() { // Intentionally left empty. } template<typename ValueType, typename StateType> bool DftNextStateGenerator<ValueType, StateType>::isDeterministicModel() const { - assert(false); - return true; + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "This functionality is not yet implemented."); } template<typename ValueType, typename StateType> std::vector<StateType> DftNextStateGenerator<ValueType, StateType>::getInitialStates(StateToIdCallback const& stateToIdCallback) { - // FIXME: This only works for models with exactly one initial state. We should make this more general. - /*CompressedState initialState(variableInformation.getTotalBitOffset()); - - // We need to initialize the values of the variables to their initial value. - for (auto const& booleanVariable : variableInformation.booleanVariables) { - initialState.set(booleanVariable.bitOffset, booleanVariable.initialValue); - } - for (auto const& integerVariable : variableInformation.integerVariables) { - initialState.setFromInt(integerVariable.bitOffset, integerVariable.bitWidth, static_cast<uint_fast64_t>(integerVariable.initialValue - integerVariable.lowerBound)); - } + DFTStatePointer initialState = std::make_shared<storm::storage::DFTState<ValueType>>(mDft, mStateGenerationInfo, 0); - // Register initial state and return it. + // Register initial state StateType id = stateToIdCallback(initialState); - return {id};*/ + + initialState->setId(id); + return {id}; } template<typename ValueType, typename StateType> @@ -43,14 +35,12 @@ namespace storm { // Also, we need to store a pointer to the state itself, because we need to be able to access it when expanding it. this->state = &state;*/ + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "This functionality is not yet implemented."); } template<typename ValueType, typename StateType> bool DftNextStateGenerator<ValueType, StateType>::satisfies(storm::expressions::Expression const& expression) const { - /* if (expression.isTrue()) { - return true; - } - return evaluator.asBool(expression);*/ + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "This functionality is not yet implemented."); } template<typename ValueType, typename StateType> @@ -147,6 +137,7 @@ namespace storm { result.setExpanded(); return result;*/ + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "This functionality is not yet implemented."); } diff --git a/src/generator/DftNextStateGenerator.h b/src/generator/DftNextStateGenerator.h index 6c8e5538d..17a7dd25a 100644 --- a/src/generator/DftNextStateGenerator.h +++ b/src/generator/DftNextStateGenerator.h @@ -9,17 +9,20 @@ namespace storm { namespace generator { - template<typename ValueType, typename StateType = uint32_t> + template<typename ValueType, typename StateType = uint_fast64_t> class DftNextStateGenerator : public NextStateGenerator<ValueType, std::shared_ptr<storm::storage::DFTState<ValueType>>, StateType> { + + using DFTStatePointer = std::shared_ptr<storm::storage::DFTState<ValueType>>; + public: - typedef typename NextStateGenerator<ValueType, std::shared_ptr<storm::storage::DFTState<ValueType>>, StateType>::StateToIdCallback StateToIdCallback; + typedef typename NextStateGenerator<ValueType, DFTStatePointer, StateType>::StateToIdCallback StateToIdCallback; - DftNextStateGenerator(storm::storage::DFT<ValueType> const& dft); + DftNextStateGenerator(storm::storage::DFT<ValueType> const& dft, storm::storage::DFTStateGenerationInfo const& stateGenerationInfo); virtual bool isDeterministicModel() const override; virtual std::vector<StateType> getInitialStates(StateToIdCallback const& stateToIdCallback) override; - virtual void load(std::shared_ptr<storm::storage::DFTState<ValueType>> const& state) override; + virtual void load(DFTStatePointer const& state) override; virtual StateBehavior<ValueType, StateType> expand(StateToIdCallback const& stateToIdCallback) override; virtual bool satisfies(storm::expressions::Expression const& expression) const override; @@ -28,7 +31,9 @@ namespace storm { // The program used for the generation of next states. storm::storage::DFT<ValueType> const& mDft; - CompressedState const* state; + storm::storage::DFTStateGenerationInfo const& mStateGenerationInfo; + + DFTStatePointer const state; // A comparator used to compare constants. storm::utility::ConstantsComparator<ValueType> comparator; diff --git a/src/modelchecker/DFTAnalyser.h b/src/modelchecker/DFTAnalyser.h index 24c1068ba..cc1e34696 100644 --- a/src/modelchecker/DFTAnalyser.h +++ b/src/modelchecker/DFTAnalyser.h @@ -1,12 +1,14 @@ #pragma once -#include "logic/Formula.h" -#include "parser/DFTGalileoParser.h" -#include "builder/ExplicitDFTModelBuilder.h" -#include "modelchecker/results/CheckResult.h" -#include "utility/storm.h" -#include "storage/dft/DFTIsomorphism.h" -#include "utility/bitoperations.h" +#include "src/logic/Formula.h" +#include "src/parser/DFTGalileoParser.h" +#include "src/builder/ExplicitDFTModelBuilder.h" +#include "src/builder/ExplicitDFTModelBuilderApprox.h" +#include "src/modelchecker/results/CheckResult.h" +#include "src/utility/storm.h" +#include "src/storage/dft/DFTIsomorphism.h" +#include "src/settings/modules/DFTSettings.h" +#include "src/utility/bitoperations.h" #include <chrono> @@ -149,9 +151,17 @@ private: // Building Markov Automaton STORM_LOG_INFO("Building Model..."); - storm::builder::ExplicitDFTModelBuilder<ValueType> builder(dft, symmetries, enableDC); - typename storm::builder::ExplicitDFTModelBuilder<ValueType>::LabelOptions labeloptions; // TODO initialize this with the formula - std::shared_ptr<storm::models::sparse::Model<ValueType>> model = builder.buildModel(labeloptions); + std::shared_ptr<storm::models::sparse::Model<ValueType>> model; + // TODO Matthias: use only one builder if everything works again + if (storm::settings::getModule<storm::settings::modules::DFTSettings>().computeApproximation()) { + storm::builder::ExplicitDFTModelBuilderApprox<ValueType> builder(dft, symmetries, enableDC); + typename storm::builder::ExplicitDFTModelBuilderApprox<ValueType>::LabelOptions labeloptions; // TODO initialize this with the formula + model = builder.buildModel(labeloptions); + } else { + storm::builder::ExplicitDFTModelBuilder<ValueType> builder(dft, symmetries, enableDC); + typename storm::builder::ExplicitDFTModelBuilder<ValueType>::LabelOptions labeloptions; // TODO initialize this with the formula + model = builder.buildModel(labeloptions); + } //model->printModelInformationToStream(std::cout); STORM_LOG_INFO("No. states (Explored): " << model->getNumberOfStates()); STORM_LOG_INFO("No. transitions (Explored): " << model->getNumberOfTransitions()); diff --git a/src/storage/sparse/StateStorage.cpp b/src/storage/sparse/StateStorage.cpp index 60af49179..15789c355 100644 --- a/src/storage/sparse/StateStorage.cpp +++ b/src/storage/sparse/StateStorage.cpp @@ -15,6 +15,7 @@ namespace storm { } template class StateStorage<uint32_t>; + template class StateStorage<uint_fast64_t>; } } } \ No newline at end of file From fba2071e9fba5b34232470e24c856e4700d466ff Mon Sep 17 00:00:00 2001 From: Mavo <matthias.volk@rwth-aachen.de> Date: Tue, 13 Sep 2016 12:27:20 +0200 Subject: [PATCH 11/65] Dft exploration via NextStateGenerator Former-commit-id: f81ac4e7fc283554cde7d4c70ab99b7b2f7b007f --- examples/dft/fdep4.dft | 7 + src/builder/ExplicitDFTModelBuilderApprox.cpp | 736 +++++------------- src/builder/ExplicitDFTModelBuilderApprox.h | 111 ++- src/generator/DftNextStateGenerator.cpp | 266 ++++--- src/generator/DftNextStateGenerator.h | 43 +- src/generator/StateBehavior.cpp | 3 + src/models/sparse/MarkovAutomaton.cpp | 2 +- src/models/sparse/MarkovAutomaton.h | 7 +- src/models/sparse/StateLabeling.cpp | 2 +- src/models/sparse/StateLabeling.h | 2 +- src/storage/dft/DFT.cpp | 5 + src/storage/dft/DFT.h | 2 + src/storage/dft/DFTState.cpp | 6 +- src/utility/vector.cpp | 32 +- src/utility/vector.h | 1 + 15 files changed, 561 insertions(+), 664 deletions(-) create mode 100644 examples/dft/fdep4.dft diff --git a/examples/dft/fdep4.dft b/examples/dft/fdep4.dft new file mode 100644 index 000000000..7e3c5642a --- /dev/null +++ b/examples/dft/fdep4.dft @@ -0,0 +1,7 @@ +toplevel "A"; +"A" or "F" "B"; +"F" fdep "E" "C" "D"; +"B" wsp "C" "D"; +"C" lambda=1 dorm=0; +"D" lambda=1 dorm=0.5; +"E" lambda=0.5 dorm=0; diff --git a/src/builder/ExplicitDFTModelBuilderApprox.cpp b/src/builder/ExplicitDFTModelBuilderApprox.cpp index 285301807..9ff34b734 100644 --- a/src/builder/ExplicitDFTModelBuilderApprox.cpp +++ b/src/builder/ExplicitDFTModelBuilderApprox.cpp @@ -12,223 +12,167 @@ namespace storm { namespace builder { - template <typename ValueType> - ExplicitDFTModelBuilderApprox<ValueType>::ModelComponents::ModelComponents() : transitionMatrix(), stateLabeling(), markovianStates(), exitRates(), choiceLabeling() { + template <typename ValueType, typename StateType> + ExplicitDFTModelBuilderApprox<ValueType, StateType>::ModelComponents::ModelComponents() : transitionMatrix(), stateLabeling(), markovianStates(), exitRates(), choiceLabeling() { // Intentionally left empty. } - - template <typename ValueType> - ExplicitDFTModelBuilderApprox<ValueType>::ExplicitDFTModelBuilderApprox(storm::storage::DFT<ValueType> const& dft, storm::storage::DFTIndependentSymmetries const& symmetries, bool enableDC) : mDft(dft), enableDC(enableDC), stateStorage(((mDft.stateVectorSize() / 64) + 1) * 64) { + + template <typename ValueType, typename StateType> + ExplicitDFTModelBuilderApprox<ValueType, StateType>::ExplicitDFTModelBuilderApprox(storm::storage::DFT<ValueType> const& dft, storm::storage::DFTIndependentSymmetries const& symmetries, bool enableDC) : dft(dft), enableDC(enableDC), stateStorage(((dft.stateVectorSize() / 64) + 1) * 64) { // stateVectorSize is bound for size of bitvector - mStateGenerationInfo = std::make_shared<storm::storage::DFTStateGenerationInfo>(mDft.buildStateGenerationInfo(symmetries)); + stateGenerationInfo = std::make_shared<storm::storage::DFTStateGenerationInfo>(dft.buildStateGenerationInfo(symmetries)); } + template <typename ValueType, typename StateType> + std::shared_ptr<storm::models::sparse::Model<ValueType>> ExplicitDFTModelBuilderApprox<ValueType, StateType>::buildModel(LabelOptions const& labelOpts) { + STORM_LOG_TRACE("Generating DFT state space"); - template <typename ValueType> - std::shared_ptr<storm::models::sparse::Model<ValueType>> ExplicitDFTModelBuilderApprox<ValueType>::buildModel(LabelOptions const& labelOpts) { // Initialize - bool deterministicModel = false; - size_t rowOffset = 0; - ModelComponents modelComponents; - std::vector<uint_fast64_t> tmpMarkovianStates; - storm::storage::SparseMatrixBuilder<ValueType> transitionMatrixBuilder(0, 0, 0, false, !deterministicModel, 0); + StateType currentRowGroup = 0; + StateType currentRow = 0; + modelComponents.markovianStates = storm::storage::BitVector(INITIAL_BITVECTOR_SIZE); + // Create generator + storm::generator::DftNextStateGenerator<ValueType, StateType> generator(dft, *stateGenerationInfo, enableDC, mergeFailedStates); + // Create sparse matrix builder + storm::storage::SparseMatrixBuilder<ValueType> transitionMatrixBuilder(0, 0, 0, false, !generator.isDeterministicModel(), 0); if(mergeFailedStates) { // Introduce explicit fail state - failedIndex = newIndex; - newIndex++; - transitionMatrixBuilder.newRowGroup(failedIndex); - transitionMatrixBuilder.addNextValue(failedIndex, failedIndex, storm::utility::one<ValueType>()); - STORM_LOG_TRACE("Introduce fail state with id: " << failedIndex); - modelComponents.exitRates.push_back(storm::utility::one<ValueType>()); - tmpMarkovianStates.push_back(failedIndex); - } - - // Explore state space - DFTStatePointer state = std::make_shared<storm::storage::DFTState<ValueType>>(mDft, *mStateGenerationInfo, newIndex); - auto exploreResult = exploreStates(state, rowOffset, transitionMatrixBuilder, tmpMarkovianStates, modelComponents.exitRates); - initialStateIndex = exploreResult.first; - bool deterministic = exploreResult.second; - - // Before ending the exploration check for pseudo states which are not initialized yet - for (auto & pseudoStatePair : mPseudoStatesMapping) { - if (pseudoStatePair.first == 0) { - // Create state from pseudo state and explore - STORM_LOG_ASSERT(stateStorage.stateToId.contains(pseudoStatePair.second), "Pseudo state not contained."); - STORM_LOG_ASSERT(stateStorage.stateToId.getValue(pseudoStatePair.second) >= OFFSET_PSEUDO_STATE, "State is no pseudo state."); - STORM_LOG_TRACE("Create pseudo state from bit vector " << pseudoStatePair.second); - DFTStatePointer pseudoState = std::make_shared<storm::storage::DFTState<ValueType>>(pseudoStatePair.second, mDft, *mStateGenerationInfo, newIndex); - STORM_LOG_ASSERT(pseudoStatePair.second == pseudoState->status(), "Pseudo states do not coincide."); - STORM_LOG_TRACE("Explore pseudo state " << mDft.getStateString(pseudoState) << " with id " << pseudoState->getId()); - auto exploreResult = exploreStates(pseudoState, rowOffset, transitionMatrixBuilder, tmpMarkovianStates, modelComponents.exitRates); - deterministic &= exploreResult.second; - STORM_LOG_ASSERT(pseudoStatePair.first == pseudoState->getId(), "Pseudo state ids do not coincide"); - STORM_LOG_ASSERT(pseudoState->getId() == exploreResult.first, "Pseudo state ids do not coincide."); - } - } - - // Replace pseudo states in matrix - std::vector<uint_fast64_t> pseudoStatesVector; - for (auto const& pseudoStatePair : mPseudoStatesMapping) { - pseudoStatesVector.push_back(pseudoStatePair.first); - } - STORM_LOG_ASSERT(std::find(pseudoStatesVector.begin(), pseudoStatesVector.end(), 0) == pseudoStatesVector.end(), "Unexplored pseudo state still contained."); - transitionMatrixBuilder.replaceColumns(pseudoStatesVector, OFFSET_PSEUDO_STATE); - - STORM_LOG_DEBUG("Generated " << stateStorage.getNumberOfStates() + (mergeFailedStates ? 1 : 0) << " states"); - STORM_LOG_DEBUG("Model is " << (deterministic ? "deterministic" : "non-deterministic")); + storm::generator::StateBehavior<ValueType, StateType> behavior = generator.createMergeFailedState([this] (DFTStatePointer const& state) { + this->failedStateId = newIndex++; + stateRemapping.push_back(0); + return this->failedStateId; + } ); - size_t stateSize = stateStorage.getNumberOfStates() + (mergeFailedStates ? 1 : 0); - // Build Markov Automaton - modelComponents.markovianStates = storm::storage::BitVector(stateSize, tmpMarkovianStates); - // Build transition matrix - modelComponents.transitionMatrix = transitionMatrixBuilder.build(stateSize, stateSize); - if (stateSize <= 15) { - STORM_LOG_TRACE("Transition matrix: " << std::endl << modelComponents.transitionMatrix); - } else { - STORM_LOG_TRACE("Transition matrix: too big to print"); - } - STORM_LOG_TRACE("Exit rates: " << modelComponents.exitRates); - STORM_LOG_TRACE("Markovian states: " << modelComponents.markovianStates); - - // Build state labeling - modelComponents.stateLabeling = storm::models::sparse::StateLabeling(stateStorage.getNumberOfStates() + (mergeFailedStates ? 1 : 0)); - // Initial state is always first state without any failure - modelComponents.stateLabeling.addLabel("init"); - modelComponents.stateLabeling.addLabelToState("init", initialStateIndex); - // Label all states corresponding to their status (failed, failsafe, failed BE) - if(labelOpts.buildFailLabel) { - modelComponents.stateLabeling.addLabel("failed"); - } - if(labelOpts.buildFailSafeLabel) { - modelComponents.stateLabeling.addLabel("failsafe"); - } - - // Collect labels for all BE - std::vector<std::shared_ptr<storage::DFTBE<ValueType>>> basicElements = mDft.getBasicElements(); - for (std::shared_ptr<storage::DFTBE<ValueType>> elem : basicElements) { - if(labelOpts.beLabels.count(elem->name()) > 0) { - modelComponents.stateLabeling.addLabel(elem->name() + "_fail"); + setRemapping(failedStateId, currentRowGroup); + + STORM_LOG_ASSERT(!behavior.empty(), "Behavior is empty."); + setMarkovian(currentRowGroup, behavior.begin()->isMarkovian()); + + // If the model is nondeterministic, we need to open a row group. + if (!generator.isDeterministicModel()) { + transitionMatrixBuilder.newRowGroup(currentRow); } - } - if(mergeFailedStates) { - modelComponents.stateLabeling.addLabelToState("failed", failedIndex); + // Now add self loop. + // TODO Matthias: maybe use general method. + STORM_LOG_ASSERT(behavior.getNumberOfChoices() == 1, "Wrong number of choices for failed state."); + STORM_LOG_ASSERT(behavior.begin()->size() == 1, "Wrong number of transitions for failed state."); + std::pair<StateType, ValueType> stateProbabilityPair = *(behavior.begin()->begin()); + STORM_LOG_ASSERT(stateProbabilityPair.first == failedStateId, "No self loop for failed state."); + STORM_LOG_ASSERT(storm::utility::isOne<ValueType>(stateProbabilityPair.second), "Probability for failed state != 1."); + transitionMatrixBuilder.addNextValue(currentRow, stateProbabilityPair.first, stateProbabilityPair.second); + ++currentRow; + ++currentRowGroup; } - for (auto const& stateIdPair : stateStorage.stateToId) { - storm::storage::BitVector state = stateIdPair.first; - size_t stateId = stateIdPair.second; - if (!mergeFailedStates && labelOpts.buildFailLabel && mDft.hasFailed(state, *mStateGenerationInfo)) { - modelComponents.stateLabeling.addLabelToState("failed", stateId); - } - if (labelOpts.buildFailSafeLabel && mDft.isFailsafe(state, *mStateGenerationInfo)) { - modelComponents.stateLabeling.addLabelToState("failsafe", stateId); - }; - // Set fail status for each BE - for (std::shared_ptr<storage::DFTBE<ValueType>> elem : basicElements) { - if (labelOpts.beLabels.count(elem->name()) > 0 && storm::storage::DFTState<ValueType>::hasFailed(state, mStateGenerationInfo->getStateIndex(elem->id())) ) { - modelComponents.stateLabeling.addLabelToState(elem->name() + "_fail", stateId); - } + + // Create a callback for the next-state generator to enable it to add states + std::function<StateType (DFTStatePointer const&)> stateToIdCallback = std::bind(&ExplicitDFTModelBuilderApprox::getOrAddStateIndex, this, std::placeholders::_1); + + // Build initial states + this->stateStorage.initialStateIndices = generator.getInitialStates(stateToIdCallback); + STORM_LOG_ASSERT(stateStorage.initialStateIndices.size() == 1, "Only one initial state assumed."); + StateType initialStateIndex = stateStorage.initialStateIndices[0]; + STORM_LOG_TRACE("Initial state: " << initialStateIndex); + + // Explore state space + bool explorationFinished = false; + while (!explorationFinished) { + // Get the first state in the queue + DFTStatePointer currentState = statesToExplore.front(); + STORM_LOG_ASSERT(stateStorage.stateToId.getValue(currentState->status()) == currentState->getId(), "Ids of states do not coincide."); + statesToExplore.pop_front(); + + // Remember that this row group was actually filled with the transitions of a different state + setRemapping(currentState->getId(), currentRowGroup); + + // Explore state + generator.load(currentState); + storm::generator::StateBehavior<ValueType, StateType> behavior = generator.expand(stateToIdCallback); + + STORM_LOG_ASSERT(!behavior.empty(), "Behavior is empty."); + setMarkovian(currentRowGroup, behavior.begin()->isMarkovian()); + + // If the model is nondeterministic, we need to open a row group. + if (!generator.isDeterministicModel()) { + transitionMatrixBuilder.newRowGroup(currentRow); } - } - std::shared_ptr<storm::models::sparse::Model<ValueType>> model; - - if (deterministic) { - // Turn the probabilities into rates by multiplying each row with the exit rate of the state. - // TODO Matthias: avoid transforming back and forth - storm::storage::SparseMatrix<ValueType> rateMatrix(modelComponents.transitionMatrix); - for (uint_fast64_t row = 0; row < rateMatrix.getRowCount(); ++row) { - STORM_LOG_ASSERT(row < modelComponents.markovianStates.size(), "Row exceeds no. of markovian states."); - if (modelComponents.markovianStates.get(row)) { - for (auto& entry : rateMatrix.getRow(row)) { - entry.setValue(entry.getValue() * modelComponents.exitRates[row]); + // Now add all choices. + for (auto const& choice : behavior) { + // Add the probabilistic behavior to the matrix. + for (auto const& stateProbabilityPair : choice) { + + // Check that pseudo state and its instantiation do not appear together + // TODO Matthias: prove that this is not possible and remove + if (stateProbabilityPair.first >= OFFSET_PSEUDO_STATE) { + StateType newId = stateProbabilityPair.first - OFFSET_PSEUDO_STATE; + STORM_LOG_ASSERT(newId < mPseudoStatesMapping.size(), "Id is not valid."); + if (mPseudoStatesMapping[newId].first > 0) { + // State exists already + newId = mPseudoStatesMapping[newId].first; + for (auto itFind = choice.begin(); itFind != choice.end(); ++itFind) { + STORM_LOG_ASSERT(itFind->first != newId, "Pseudo state and instantiation occur together in a distribution."); + } + } } + + transitionMatrixBuilder.addNextValue(currentRow, stateProbabilityPair.first, stateProbabilityPair.second); } + ++currentRow; } - model = std::make_shared<storm::models::sparse::Ctmc<ValueType>>(std::move(rateMatrix), std::move(modelComponents.exitRates), std::move(modelComponents.stateLabeling)); - } else { - std::shared_ptr<storm::models::sparse::MarkovAutomaton<ValueType>> ma = std::make_shared<storm::models::sparse::MarkovAutomaton<ValueType>>(std::move(modelComponents.transitionMatrix), std::move(modelComponents.stateLabeling), std::move(modelComponents.markovianStates), std::move(modelComponents.exitRates), true); - if (ma->hasOnlyTrivialNondeterminism()) { - // Markov automaton can be converted into CTMC - model = ma->convertToCTMC(); - } else { - model = ma; - } - } - - return model; - } - - template <typename ValueType> - std::shared_ptr<storm::models::sparse::Model<ValueType>> ExplicitDFTModelBuilderApprox<ValueType>::buildModelApprox(LabelOptions const& labelOpts) { - // Initialize - bool deterministicModel = false; - size_t rowOffset = 0; - ModelComponents modelComponents; - std::vector<uint_fast64_t> tmpMarkovianStates; - storm::storage::SparseMatrixBuilder<ValueType> transitionMatrixBuilder(0, 0, 0, false, !deterministicModel, 0); - - if(mergeFailedStates) { - // Introduce explicit fail state - failedIndex = newIndex; - newIndex++; - transitionMatrixBuilder.newRowGroup(failedIndex); - transitionMatrixBuilder.addNextValue(failedIndex, failedIndex, storm::utility::one<ValueType>()); - STORM_LOG_TRACE("Introduce fail state with id: " << failedIndex); - modelComponents.exitRates.push_back(storm::utility::one<ValueType>()); - tmpMarkovianStates.push_back(failedIndex); - } - - // Initialize generator - storm::generator::DftNextStateGenerator<ValueType, uint_fast64_t> generator(mDft, *mStateGenerationInfo); - - // Explore state space - typename storm::generator::DftNextStateGenerator<ValueType, uint_fast64_t>::StateToIdCallback stateToIdCallback = [this] (DFTStatePointer const& state) -> uint_fast64_t { - uint_fast64_t id = newIndex++; - std::cout << "Added state " << id << std::endl; - return id; - }; - - uint_fast64_t id = generator.getInitialStates(stateToIdCallback)[0]; - std::cout << "Initial state " << id << std::endl; - DFTStatePointer state = std::make_shared<storm::storage::DFTState<ValueType>>(mDft, *mStateGenerationInfo, newIndex); - auto exploreResult = exploreStates(state, rowOffset, transitionMatrixBuilder, tmpMarkovianStates, modelComponents.exitRates); - initialStateIndex = exploreResult.first; - bool deterministic = exploreResult.second; - - // Before ending the exploration check for pseudo states which are not initialized yet - for (auto & pseudoStatePair : mPseudoStatesMapping) { - if (pseudoStatePair.first == 0) { - // Create state from pseudo state and explore - STORM_LOG_ASSERT(stateStorage.stateToId.contains(pseudoStatePair.second), "Pseudo state not contained."); - STORM_LOG_ASSERT(stateStorage.stateToId.getValue(pseudoStatePair.second) >= OFFSET_PSEUDO_STATE, "State is no pseudo state."); - STORM_LOG_TRACE("Create pseudo state from bit vector " << pseudoStatePair.second); - DFTStatePointer pseudoState = std::make_shared<storm::storage::DFTState<ValueType>>(pseudoStatePair.second, mDft, *mStateGenerationInfo, newIndex); - STORM_LOG_ASSERT(pseudoStatePair.second == pseudoState->status(), "Pseudo states do not coincide."); - STORM_LOG_TRACE("Explore pseudo state " << mDft.getStateString(pseudoState) << " with id " << pseudoState->getId()); - auto exploreResult = exploreStates(pseudoState, rowOffset, transitionMatrixBuilder, tmpMarkovianStates, modelComponents.exitRates); - deterministic &= exploreResult.second; - STORM_LOG_ASSERT(pseudoStatePair.first == pseudoState->getId(), "Pseudo state ids do not coincide"); - STORM_LOG_ASSERT(pseudoState->getId() == exploreResult.first, "Pseudo state ids do not coincide."); + ++currentRowGroup; + + if (statesToExplore.empty()) { + explorationFinished = true; + // Before ending the exploration check for pseudo states which are not initialized yet + for ( ; pseudoStatesToCheck < mPseudoStatesMapping.size(); ++pseudoStatesToCheck) { + std::pair<StateType, storm::storage::BitVector> pseudoStatePair = mPseudoStatesMapping[pseudoStatesToCheck]; + if (pseudoStatePair.first == 0) { + // Create state from pseudo state and explore + STORM_LOG_ASSERT(stateStorage.stateToId.contains(pseudoStatePair.second), "Pseudo state not contained."); + STORM_LOG_ASSERT(stateStorage.stateToId.getValue(pseudoStatePair.second) >= OFFSET_PSEUDO_STATE, "State is no pseudo state."); + STORM_LOG_TRACE("Create pseudo state from bit vector " << pseudoStatePair.second); + DFTStatePointer pseudoState = std::make_shared<storm::storage::DFTState<ValueType>>(pseudoStatePair.second, dft, *stateGenerationInfo, newIndex); + STORM_LOG_ASSERT(pseudoStatePair.second == pseudoState->status(), "Pseudo states do not coincide."); + STORM_LOG_TRACE("Explore pseudo state " << dft.getStateString(pseudoState) << " with id " << pseudoState->getId()); + + getOrAddStateIndex(pseudoState); + explorationFinished = false; + break; + } + } } - } - + + } // end exploration + + size_t stateSize = stateStorage.getNumberOfStates() + (mergeFailedStates ? 1 : 0); + modelComponents.markovianStates.resize(stateSize); + // Replace pseudo states in matrix + // TODO Matthias: avoid hack with fixed int type std::vector<uint_fast64_t> pseudoStatesVector; for (auto const& pseudoStatePair : mPseudoStatesMapping) { pseudoStatesVector.push_back(pseudoStatePair.first); } STORM_LOG_ASSERT(std::find(pseudoStatesVector.begin(), pseudoStatesVector.end(), 0) == pseudoStatesVector.end(), "Unexplored pseudo state still contained."); transitionMatrixBuilder.replaceColumns(pseudoStatesVector, OFFSET_PSEUDO_STATE); - - STORM_LOG_DEBUG("Generated " << stateStorage.getNumberOfStates() + (mergeFailedStates ? 1 : 0) << " states"); - STORM_LOG_DEBUG("Model is " << (deterministic ? "deterministic" : "non-deterministic")); - - size_t stateSize = stateStorage.getNumberOfStates() + (mergeFailedStates ? 1 : 0); - // Build Markov Automaton - modelComponents.markovianStates = storm::storage::BitVector(stateSize, tmpMarkovianStates); + + + // Fix the entries in the matrix according to the (reversed) mapping of row groups to indices + STORM_LOG_ASSERT(stateRemapping[initialStateIndex] == initialStateIndex, "Initial state should not be remapped."); + // Fix the transition matrix + transitionMatrixBuilder.replaceColumns(stateRemapping, 0); + // Fix the hash map storing the mapping states -> ids + this->stateStorage.stateToId.remap([this] (StateType const& state) { return this->stateRemapping[state]; } ); + + STORM_LOG_TRACE("State remapping: " << stateRemapping); + STORM_LOG_TRACE("Markovian states: " << modelComponents.markovianStates); + + STORM_LOG_DEBUG("Generated " << stateSize << " states"); + STORM_LOG_DEBUG("Model is " << (generator.isDeterministicModel() ? "deterministic" : "non-deterministic")); + // Build transition matrix modelComponents.transitionMatrix = transitionMatrixBuilder.build(stateSize, stateSize); if (stateSize <= 15) { @@ -236,11 +180,9 @@ namespace storm { } else { STORM_LOG_TRACE("Transition matrix: too big to print"); } - STORM_LOG_TRACE("Exit rates: " << modelComponents.exitRates); - STORM_LOG_TRACE("Markovian states: " << modelComponents.markovianStates); - + // Build state labeling - modelComponents.stateLabeling = storm::models::sparse::StateLabeling(stateStorage.getNumberOfStates() + (mergeFailedStates ? 1 : 0)); + modelComponents.stateLabeling = storm::models::sparse::StateLabeling(stateSize); // Initial state is always first state without any failure modelComponents.stateLabeling.addLabel("init"); modelComponents.stateLabeling.addLabelToState("init", initialStateIndex); @@ -251,52 +193,56 @@ namespace storm { if(labelOpts.buildFailSafeLabel) { modelComponents.stateLabeling.addLabel("failsafe"); } - + // Collect labels for all BE - std::vector<std::shared_ptr<storage::DFTBE<ValueType>>> basicElements = mDft.getBasicElements(); + std::vector<std::shared_ptr<storage::DFTBE<ValueType>>> basicElements = dft.getBasicElements(); for (std::shared_ptr<storage::DFTBE<ValueType>> elem : basicElements) { if(labelOpts.beLabels.count(elem->name()) > 0) { modelComponents.stateLabeling.addLabel(elem->name() + "_fail"); } } - + + // Set labels to states if(mergeFailedStates) { - modelComponents.stateLabeling.addLabelToState("failed", failedIndex); + modelComponents.stateLabeling.addLabelToState("failed", failedStateId); } for (auto const& stateIdPair : stateStorage.stateToId) { storm::storage::BitVector state = stateIdPair.first; size_t stateId = stateIdPair.second; - if (!mergeFailedStates && labelOpts.buildFailLabel && mDft.hasFailed(state, *mStateGenerationInfo)) { + if (!mergeFailedStates && labelOpts.buildFailLabel && dft.hasFailed(state, *stateGenerationInfo)) { modelComponents.stateLabeling.addLabelToState("failed", stateId); } - if (labelOpts.buildFailSafeLabel && mDft.isFailsafe(state, *mStateGenerationInfo)) { + if (labelOpts.buildFailSafeLabel && dft.isFailsafe(state, *stateGenerationInfo)) { modelComponents.stateLabeling.addLabelToState("failsafe", stateId); }; // Set fail status for each BE for (std::shared_ptr<storage::DFTBE<ValueType>> elem : basicElements) { - if (labelOpts.beLabels.count(elem->name()) > 0 && storm::storage::DFTState<ValueType>::hasFailed(state, mStateGenerationInfo->getStateIndex(elem->id())) ) { + if (labelOpts.beLabels.count(elem->name()) > 0 && storm::storage::DFTState<ValueType>::hasFailed(state, stateGenerationInfo->getStateIndex(elem->id())) ) { modelComponents.stateLabeling.addLabelToState(elem->name() + "_fail", stateId); } } } - + std::shared_ptr<storm::models::sparse::Model<ValueType>> model; - - if (deterministic) { - // Turn the probabilities into rates by multiplying each row with the exit rate of the state. - // TODO Matthias: avoid transforming back and forth - storm::storage::SparseMatrix<ValueType> rateMatrix(modelComponents.transitionMatrix); - for (uint_fast64_t row = 0; row < rateMatrix.getRowCount(); ++row) { - STORM_LOG_ASSERT(row < modelComponents.markovianStates.size(), "Row exceeds no. of markovian states."); - if (modelComponents.markovianStates.get(row)) { - for (auto& entry : rateMatrix.getRow(row)) { - entry.setValue(entry.getValue() * modelComponents.exitRates[row]); - } + + if (generator.isDeterministicModel()) { + // Build CTMC + model = std::make_shared<storm::models::sparse::Ctmc<ValueType>>(std::move(modelComponents.transitionMatrix), std::move(modelComponents.stateLabeling)); + } else { + // Build MA + // Compute exit rates + modelComponents.exitRates = std::vector<ValueType>(stateSize); + std::vector<typename storm::storage::SparseMatrix<ValueType>::index_type> indices = modelComponents.transitionMatrix.getRowGroupIndices(); + for (StateType stateIndex = 0; stateIndex < stateSize; ++stateIndex) { + if (modelComponents.markovianStates[stateIndex]) { + modelComponents.exitRates[stateIndex] = modelComponents.transitionMatrix.getRowSum(indices[stateIndex]); + } else { + modelComponents.exitRates[stateIndex] = storm::utility::zero<ValueType>(); } } - model = std::make_shared<storm::models::sparse::Ctmc<ValueType>>(std::move(rateMatrix), std::move(modelComponents.exitRates), std::move(modelComponents.stateLabeling)); - } else { - std::shared_ptr<storm::models::sparse::MarkovAutomaton<ValueType>> ma = std::make_shared<storm::models::sparse::MarkovAutomaton<ValueType>>(std::move(modelComponents.transitionMatrix), std::move(modelComponents.stateLabeling), std::move(modelComponents.markovianStates), std::move(modelComponents.exitRates), true); + STORM_LOG_TRACE("Exit rates: " << modelComponents.exitRates); + + std::shared_ptr<storm::models::sparse::MarkovAutomaton<ValueType>> ma = std::make_shared<storm::models::sparse::MarkovAutomaton<ValueType>>(std::move(modelComponents.transitionMatrix), std::move(modelComponents.stateLabeling), std::move(modelComponents.markovianStates), std::move(modelComponents.exitRates)); if (ma->hasOnlyTrivialNondeterminism()) { // Markov automaton can be converted into CTMC model = ma->convertToCTMC(); @@ -325,331 +271,77 @@ namespace storm { return eval < threshold; } - - template <typename ValueType> - std::pair<uint_fast64_t, bool> ExplicitDFTModelBuilderApprox<ValueType>::exploreStates(DFTStatePointer const& state, size_t& rowOffset, storm::storage::SparseMatrixBuilder<ValueType>& transitionMatrixBuilder, std::vector<uint_fast64_t>& markovianStates, std::vector<ValueType>& exitRates) { - STORM_LOG_TRACE("Explore state: " << mDft.getStateString(state)); - - auto explorePair = checkForExploration(state); - if (!explorePair.first) { - // State does not need any exploration - return std::make_pair(explorePair.second, true); - } - - - // Initialization - // TODO Matthias: set Markovian states directly as bitvector? - std::map<uint_fast64_t, ValueType> outgoingRates; - std::vector<std::map<uint_fast64_t, ValueType>> outgoingProbabilities; - bool hasDependencies = state->nrFailableDependencies() > 0; - size_t failableCount = hasDependencies ? state->nrFailableDependencies() : state->nrFailableBEs(); - size_t smallest = 0; - ValueType exitRate = storm::utility::zero<ValueType>(); - bool deterministic = !hasDependencies; - - // Absorbing state - if (mDft.hasFailed(state) || mDft.isFailsafe(state) || state->nrFailableBEs() == 0) { - uint_fast64_t stateId = addState(state); - STORM_LOG_ASSERT(stateId == state->getId(), "Ids do not coincide."); - - // Add self loop - transitionMatrixBuilder.newRowGroup(stateId + rowOffset); - transitionMatrixBuilder.addNextValue(stateId + rowOffset, stateId, storm::utility::one<ValueType>()); - STORM_LOG_TRACE("Added self loop for " << stateId); - exitRates.push_back(storm::utility::one<ValueType>()); - STORM_LOG_ASSERT(exitRates.size()-1 == stateId, "No. of considered states does not match state id."); - markovianStates.push_back(stateId); - // No further exploration required - return std::make_pair(stateId, true); - } - - // Let BE fail - while (smallest < failableCount) { - STORM_LOG_ASSERT(!mDft.hasFailed(state), "Dft has failed."); - - // Construct new state as copy from original one - DFTStatePointer newState = std::make_shared<storm::storage::DFTState<ValueType>>(*state); - std::pair<std::shared_ptr<storm::storage::DFTBE<ValueType> const>, bool> nextBEPair = newState->letNextBEFail(smallest++); - std::shared_ptr<storm::storage::DFTBE<ValueType> const>& nextBE = nextBEPair.first; - STORM_LOG_ASSERT(nextBE, "NextBE is null."); - STORM_LOG_ASSERT(nextBEPair.second == hasDependencies, "Failure due to dependencies does not match."); - STORM_LOG_TRACE("With the failure of: " << nextBE->name() << " [" << nextBE->id() << "] in " << mDft.getStateString(state)); - - if (storm::settings::getModule<storm::settings::modules::DFTSettings>().computeApproximation()) { - if (!storm::utility::isZero(exitRate)) { - ValueType rate = nextBE->activeFailureRate(); - ValueType div = rate / exitRate; - if (!storm::utility::isZero(exitRate) && belowThreshold(div)) { - // Set transition directly to failed state - auto resultFind = outgoingRates.find(failedIndex); - if (resultFind != outgoingRates.end()) { - // Add to existing transition - resultFind->second += rate; - STORM_LOG_TRACE("Updated transition to " << resultFind->first << " with rate " << rate << " to new rate " << resultFind->second); - } else { - // Insert new transition - outgoingRates.insert(std::make_pair(failedIndex, rate)); - STORM_LOG_TRACE("Added transition to " << failedIndex << " with rate " << rate); - } - exitRate += rate; - std::cout << "IGNORE: " << nextBE->name() << " [" << nextBE->id() << "] with rate " << rate << std::endl; - //STORM_LOG_TRACE("Ignore: " << nextBE->name() << " [" << nextBE->id() << "] with rate " << rate); - continue; - } - } - } - - // Propagate failures - storm::storage::DFTStateSpaceGenerationQueues<ValueType> queues; - - for (DFTGatePointer parent : nextBE->parents()) { - if (newState->isOperational(parent->id())) { - queues.propagateFailure(parent); - } - } - for (DFTRestrictionPointer restr : nextBE->restrictions()) { - queues.checkRestrictionLater(restr); - } - - while (!queues.failurePropagationDone()) { - DFTGatePointer next = queues.nextFailurePropagation(); - next->checkFails(*newState, queues); - newState->updateFailableDependencies(next->id()); - } - - while(!queues.restrictionChecksDone()) { - DFTRestrictionPointer next = queues.nextRestrictionCheck(); - next->checkFails(*newState, queues); - newState->updateFailableDependencies(next->id()); - } - - if(newState->isInvalid()) { - continue; - } - bool dftFailed = newState->hasFailed(mDft.getTopLevelIndex()); - - while (!dftFailed && !queues.failsafePropagationDone()) { - DFTGatePointer next = queues.nextFailsafePropagation(); - next->checkFailsafe(*newState, queues); - } - - while (!dftFailed && enableDC && !queues.dontCarePropagationDone()) { - DFTElementPointer next = queues.nextDontCarePropagation(); - next->checkDontCareAnymore(*newState, queues); - } - - // Update failable dependencies - if (!dftFailed) { - newState->updateFailableDependencies(nextBE->id()); - newState->updateDontCareDependencies(nextBE->id()); - } - - uint_fast64_t newStateId; - if(dftFailed && mergeFailedStates) { - newStateId = failedIndex; - } else { - // Explore new state recursively - auto explorePair = exploreStates(newState, rowOffset, transitionMatrixBuilder, markovianStates, exitRates); - newStateId = explorePair.first; - deterministic &= explorePair.second; - } - - // Set transitions - if (hasDependencies) { - // Failure is due to dependency -> add non-deterministic choice - std::map<uint_fast64_t, ValueType> choiceProbabilities; - std::shared_ptr<storm::storage::DFTDependency<ValueType> const> dependency = mDft.getDependency(state->getDependencyId(smallest-1)); - choiceProbabilities.insert(std::make_pair(newStateId, dependency->probability())); - STORM_LOG_TRACE("Added transition to " << newStateId << " with probability " << dependency->probability()); - - if (!storm::utility::isOne(dependency->probability())) { - // Add transition to state where dependency was unsuccessful - DFTStatePointer unsuccessfulState = std::make_shared<storm::storage::DFTState<ValueType>>(*state); - unsuccessfulState->letDependencyBeUnsuccessful(smallest-1); - auto explorePair = exploreStates(unsuccessfulState, rowOffset, transitionMatrixBuilder, markovianStates, exitRates); - uint_fast64_t unsuccessfulStateId = explorePair.first; - deterministic &= explorePair.second; - ValueType remainingProbability = storm::utility::one<ValueType>() - dependency->probability(); - choiceProbabilities.insert(std::make_pair(unsuccessfulStateId, remainingProbability)); - STORM_LOG_TRACE("Added transition to " << unsuccessfulStateId << " with remaining probability " << remainingProbability); - } - outgoingProbabilities.push_back(choiceProbabilities); - } else { - // Set failure rate according to activation - bool isActive = true; - if (mDft.hasRepresentant(nextBE->id())) { - // Active must be checked for the state we are coming from as this state is responsible for the - // rate and not the new state we are going to - isActive = state->isActive(mDft.getRepresentant(nextBE->id())->id()); - } - ValueType rate = isActive ? nextBE->activeFailureRate() : nextBE->passiveFailureRate(); - STORM_LOG_ASSERT(!storm::utility::isZero(rate), "Rate is 0."); - auto resultFind = outgoingRates.find(newStateId); - if (resultFind != outgoingRates.end()) { - // Add to existing transition - resultFind->second += rate; - STORM_LOG_TRACE("Updated transition to " << resultFind->first << " with " << (isActive ? "active" : "passive") << " rate " << rate << " to new rate " << resultFind->second); - } else { - // Insert new transition - outgoingRates.insert(std::make_pair(newStateId, rate)); - STORM_LOG_TRACE("Added transition to " << newStateId << " with " << (isActive ? "active" : "passive") << " rate " << rate); - } - exitRate += rate; - } - - } // end while failing BE - - // Add state - uint_fast64_t stateId = addState(state); - STORM_LOG_ASSERT(stateId == state->getId(), "Ids do not match."); - STORM_LOG_ASSERT(stateId == newIndex-1, "Id does not match no. of states."); - - if (hasDependencies) { - // Add all probability transitions - STORM_LOG_ASSERT(outgoingRates.empty(), "Outgoing transitions not empty."); - transitionMatrixBuilder.newRowGroup(stateId + rowOffset); - for (size_t i = 0; i < outgoingProbabilities.size(); ++i, ++rowOffset) { - STORM_LOG_ASSERT(outgoingProbabilities[i].size() == 1 || outgoingProbabilities[i].size() == 2, "No. of outgoing transitions is not valid."); - for (auto it = outgoingProbabilities[i].begin(); it != outgoingProbabilities[i].end(); ++it) - { - STORM_LOG_TRACE("Set transition from " << stateId << " to " << it->first << " with probability " << it->second); - transitionMatrixBuilder.addNextValue(stateId + rowOffset, it->first, it->second); - } - } - rowOffset--; // One increment too many - } else { - // Try to merge pseudo states with their instantiation - // TODO Matthias: improve? - for (auto it = outgoingRates.begin(); it != outgoingRates.end(); ) { - if (it->first >= OFFSET_PSEUDO_STATE) { - uint_fast64_t newId = it->first - OFFSET_PSEUDO_STATE; - STORM_LOG_ASSERT(newId < mPseudoStatesMapping.size(), "Id is not valid."); - if (mPseudoStatesMapping[newId].first > 0) { - // State exists already - newId = mPseudoStatesMapping[newId].first; - auto itFind = outgoingRates.find(newId); - if (itFind != outgoingRates.end()) { - // Add probability from pseudo state to instantiation - itFind->second += it->second; - STORM_LOG_TRACE("Merged pseudo state " << newId << " adding rate " << it->second << " to total rate of " << itFind->second); - } else { - // Only change id - outgoingRates.emplace(newId, it->second); - STORM_LOG_TRACE("Instantiated pseudo state " << newId << " with rate " << it->second); - } - // Remove pseudo state - it = outgoingRates.erase(it); - } else { - ++it; - } - } else { - ++it; - } - } - - // Add all rate transitions - STORM_LOG_ASSERT(outgoingProbabilities.empty(), "Outgoing probabilities not empty."); - transitionMatrixBuilder.newRowGroup(state->getId() + rowOffset); - STORM_LOG_TRACE("Exit rate for " << state->getId() << ": " << exitRate); - for (auto it = outgoingRates.begin(); it != outgoingRates.end(); ++it) - { - ValueType probability = it->second / exitRate; // Transform rate to probability - STORM_LOG_TRACE("Set transition from " << state->getId() << " to " << it->first << " with rate " << it->second); - transitionMatrixBuilder.addNextValue(state->getId() + rowOffset, it->first, probability); - } - - markovianStates.push_back(state->getId()); - } - - STORM_LOG_TRACE("Finished exploring state: " << mDft.getStateString(state)); - exitRates.push_back(exitRate); - STORM_LOG_ASSERT(exitRates.size()-1 == state->getId(), "Id does not match no. of states."); - return std::make_pair(state->getId(), deterministic); - } - - template <typename ValueType> - std::pair<bool, uint_fast64_t> ExplicitDFTModelBuilderApprox<ValueType>::checkForExploration(DFTStatePointer const& state) { + template <typename ValueType, typename StateType> + StateType ExplicitDFTModelBuilderApprox<ValueType, StateType>::getOrAddStateIndex(DFTStatePointer const& state) { + StateType stateId; bool changed = false; - if (mStateGenerationInfo->hasSymmetries()) { + + if (stateGenerationInfo->hasSymmetries()) { // Order state by symmetry - STORM_LOG_TRACE("Check for symmetry: " << mDft.getStateString(state)); + STORM_LOG_TRACE("Check for symmetry: " << dft.getStateString(state)); changed = state->orderBySymmetry(); - STORM_LOG_TRACE("State " << (changed ? "changed to " : "did not change") << (changed ? mDft.getStateString(state) : "")); + STORM_LOG_TRACE("State " << (changed ? "changed to " : "did not change") << (changed ? dft.getStateString(state) : "")); } - + if (stateStorage.stateToId.contains(state->status())) { // State already exists - uint_fast64_t stateId = stateStorage.stateToId.getValue(state->status()); - STORM_LOG_TRACE("State " << mDft.getStateString(state) << " with id " << stateId << " already exists"); - - if (changed || stateId < OFFSET_PSEUDO_STATE) { - // State is changed or an explored "normal" state - return std::make_pair(false, stateId); - } - - stateId -= OFFSET_PSEUDO_STATE; - STORM_LOG_ASSERT(stateId < mPseudoStatesMapping.size(), "Id not valid."); - if (mPseudoStatesMapping[stateId].first > 0) { - // Pseudo state already explored - return std::make_pair(false, mPseudoStatesMapping[stateId].first); + stateId = stateStorage.stateToId.getValue(state->status()); + STORM_LOG_TRACE("State " << dft.getStateString(state) << " with id " << stateId << " already exists"); + + if (!changed && stateId >= OFFSET_PSEUDO_STATE) { + // Pseudo state can be created now + STORM_LOG_ASSERT(stateId >= OFFSET_PSEUDO_STATE, "State is no pseudo state."); + stateId -= OFFSET_PSEUDO_STATE; + STORM_LOG_ASSERT(stateId < mPseudoStatesMapping.size(), "Pseudo state not known."); + STORM_LOG_ASSERT(mPseudoStatesMapping[stateId].first == 0, "Pseudo state already created."); + // Create pseudo state now + STORM_LOG_ASSERT(mPseudoStatesMapping[stateId].second == state->status(), "Pseudo states do not coincide."); + state->setId(newIndex++); + mPseudoStatesMapping[stateId].first = state->getId(); + stateId = state->getId(); + stateStorage.stateToId.setOrAdd(state->status(), stateId); + STORM_LOG_TRACE("Now create state " << dft.getStateString(state) << " with id " << stateId); + statesToExplore.push_front(state); } - - STORM_LOG_ASSERT(mPseudoStatesMapping[stateId].second == state->status(), "States do not coincide."); - STORM_LOG_TRACE("Pseudo state " << mDft.getStateString(state) << " can be explored now"); - return std::make_pair(true, stateId); } else { - // State does not exists + // State does not exist yet if (changed) { // Remember state for later creation state->setId(mPseudoStatesMapping.size() + OFFSET_PSEUDO_STATE); mPseudoStatesMapping.push_back(std::make_pair(0, state->status())); - stateStorage.stateToId.findOrAdd(state->status(), state->getId()); - STORM_LOG_TRACE("Remember state for later creation: " << mDft.getStateString(state)); - return std::make_pair(false, state->getId()); + stateId = stateStorage.stateToId.findOrAdd(state->status(), state->getId()); + STORM_LOG_ASSERT(stateId == state->getId(), "Ids do not match."); + STORM_LOG_TRACE("Remember state for later creation: " << dft.getStateString(state)); + // Reserve one slot for the coming state in the remapping + stateRemapping.push_back(0); } else { - // State needs exploration - return std::make_pair(true, 0); + // Create new state + state->setId(newIndex++); + stateId = stateStorage.stateToId.findOrAdd(state->status(), state->getId()); + STORM_LOG_ASSERT(stateId == state->getId(), "Ids do not match."); + STORM_LOG_TRACE("New state: " << dft.getStateString(state)); + statesToExplore.push_front(state); + + // Reserve one slot for the new state in the remapping + stateRemapping.push_back(0); } } + return stateId; } - template <typename ValueType> - uint_fast64_t ExplicitDFTModelBuilderApprox<ValueType>::addState(DFTStatePointer const& state) { - uint_fast64_t stateId; - // TODO remove - bool changed = state->orderBySymmetry(); - STORM_LOG_ASSERT(!changed, "State to add has changed by applying symmetry."); - - // Check if state already exists - if (stateStorage.stateToId.contains(state->status())) { - // State already exists - stateId = stateStorage.stateToId.getValue(state->status()); - STORM_LOG_TRACE("State " << mDft.getStateString(state) << " with id " << stateId << " already exists"); - - // Check if possible pseudo state can be created now - STORM_LOG_ASSERT(stateId >= OFFSET_PSEUDO_STATE, "State is no pseudo state."); - stateId -= OFFSET_PSEUDO_STATE; - STORM_LOG_ASSERT(stateId < mPseudoStatesMapping.size(), "Pseudo state not known."); - if (mPseudoStatesMapping[stateId].first == 0) { - // Create pseudo state now - STORM_LOG_ASSERT(mPseudoStatesMapping[stateId].second == state->status(), "Pseudo states do not coincide."); - state->setId(newIndex++); - mPseudoStatesMapping[stateId].first = state->getId(); - stateId = state->getId(); - stateStorage.stateToId.setOrAdd(state->status(), stateId); - STORM_LOG_TRACE("Now create state " << mDft.getStateString(state) << " with id " << stateId); - return stateId; - } else { - STORM_LOG_ASSERT(false, "Pseudo state already created."); - return 0; - } - } else { - // Create new state - state->setId(newIndex++); - stateId = stateStorage.stateToId.findOrAdd(state->status(), state->getId()); - STORM_LOG_TRACE("New state: " << mDft.getStateString(state)); - return stateId; + template <typename ValueType, typename StateType> + void ExplicitDFTModelBuilderApprox<ValueType, StateType>::setMarkovian(StateType id, bool markovian) { + if (id >= modelComponents.markovianStates.size()) { + // Resize BitVector + modelComponents.markovianStates.resize(modelComponents.markovianStates.size() + INITIAL_BITVECTOR_SIZE); } + modelComponents.markovianStates.set(id, markovian); + } + + template <typename ValueType, typename StateType> + void ExplicitDFTModelBuilderApprox<ValueType, StateType>::setRemapping(StateType id, StateType mappedId) { + STORM_LOG_ASSERT(id < stateRemapping.size(), "Invalid index for remapping."); + stateRemapping[id] = mappedId; } diff --git a/src/builder/ExplicitDFTModelBuilderApprox.h b/src/builder/ExplicitDFTModelBuilderApprox.h index d03681cbe..247b66ca1 100644 --- a/src/builder/ExplicitDFTModelBuilderApprox.h +++ b/src/builder/ExplicitDFTModelBuilderApprox.h @@ -12,11 +12,15 @@ #include <boost/optional/optional.hpp> #include <stack> #include <unordered_set> +#include <limits> namespace storm { namespace builder { - template<typename ValueType> + /*! + * Build a Markov chain from DFT. + */ + template<typename ValueType, typename StateType = uint32_t> class ExplicitDFTModelBuilderApprox { using DFTElementPointer = std::shared_ptr<storm::storage::DFTElement<ValueType>>; @@ -43,57 +47,100 @@ namespace storm { std::vector<ValueType> exitRates; // A vector that stores a labeling for each choice. - boost::optional<std::vector<boost::container::flat_set<uint_fast64_t>>> choiceLabeling; + boost::optional<std::vector<boost::container::flat_set<StateType>>> choiceLabeling; }; - - const uint_fast64_t OFFSET_PSEUDO_STATE = UINT_FAST64_MAX / 2; - - storm::storage::DFT<ValueType> const& mDft; - std::shared_ptr<storm::storage::DFTStateGenerationInfo> mStateGenerationInfo; - //TODO Matthias: remove when everything works - std::vector<std::pair<uint_fast64_t, storm::storage::BitVector>> mPseudoStatesMapping; // vector of (id to concrete state, bitvector) - size_t newIndex = 0; - bool mergeFailedStates = true; - bool enableDC = true; - size_t failedIndex = 0; - size_t initialStateIndex = 0; - - // Internal information about the states that were explored. - storm::storage::sparse::StateStorage<uint_fast64_t> stateStorage; public: + // A structure holding the labeling options. struct LabelOptions { bool buildFailLabel = true; bool buildFailSafeLabel = false; std::set<std::string> beLabels = {}; }; - + + /*! + * Constructor. + * + * @param dft DFT. + * @param symmetries Symmetries in the dft. + * @param enableDC Flag indicating if dont care propagation should be used. + */ ExplicitDFTModelBuilderApprox(storm::storage::DFT<ValueType> const& dft, storm::storage::DFTIndependentSymmetries const& symmetries, bool enableDC); + /*! + * Build model from Dft. + * + * @param labelOpts Options for labeling. + * + * @return Built model (either MA or CTMC). + */ std::shared_ptr<storm::models::sparse::Model<ValueType>> buildModel(LabelOptions const& labelOpts); - // TODO Matthias: only temporary used for avoiding crashing everything - std::shared_ptr<storm::models::sparse::Model<ValueType>> buildModelApprox(LabelOptions const& labelOpts); - private: - std::pair<uint_fast64_t, bool> exploreStates(DFTStatePointer const& state, size_t& rowOffset, storm::storage::SparseMatrixBuilder<ValueType>& transitionMatrixBuilder, std::vector<uint_fast64_t>& markovianStates, std::vector<ValueType>& exitRates); - + /*! - * Adds a state to the explored states and handles pseudo states. + * Add a state to the explored states (if not already there). It also handles pseudo states. * * @param state The state to add. - * @return Id of added state. + * + * @return Id of state. + */ + StateType getOrAddStateIndex(DFTStatePointer const& state); + + /*! + * Set if the given state is markovian. + * + * @param id Id of the state. + * @param markovian Flag indicating if the state is markovian. */ - uint_fast64_t addState(DFTStatePointer const& state); - + void setMarkovian(StateType id, bool markovian); + /*! - * Check if state needs an exploration and remember pseudo states for later creation. + * Set a mapping from a state id to its new id. * - * @param state State which might need exploration. - * @return Pair of flag indicating whether the state needs exploration now and the state id if the state already - * exists. + * @param id Id of the state. + * @param mappedId New id to use. */ - std::pair<bool, uint_fast64_t> checkForExploration(DFTStatePointer const& state); + void setRemapping(StateType id, StateType mappedId); + + // Initial size of the bitvector. + const size_t INITIAL_BITVECTOR_SIZE = 20000; + // Offset used for pseudo states. + const StateType OFFSET_PSEUDO_STATE = std::numeric_limits<StateType>::max() / 2; + + // Dft + storm::storage::DFT<ValueType> const& dft; + + // General information for state generation + // TODO Matthias: use const reference + std::shared_ptr<storm::storage::DFTStateGenerationInfo> stateGenerationInfo; + + // Current id for new state + size_t newIndex = 0; + + //TODO Matthias: remove when everything works + std::vector<std::pair<StateType, storm::storage::BitVector>> mPseudoStatesMapping; // vector of (id to concrete state, bitvector) + //TODO Matthias: make changeable + const bool mergeFailedStates = true; + size_t failedStateId = 0; + size_t initialStateIndex = 0; + size_t pseudoStatesToCheck = 0; + + // Flag indication if dont care propagation should be used. + bool enableDC = true; + + // Structure for the components of the model. + ModelComponents modelComponents; + + // Internal information about the states that were explored. + storm::storage::sparse::StateStorage<StateType> stateStorage; + + // A set of states that still need to be explored. + std::deque<DFTStatePointer> statesToExplore; + + // A mapping from state indices to the row groups in which they actually reside + // TODO Matthias: avoid hack with fixed int type + std::vector<uint_fast64_t> stateRemapping; }; diff --git a/src/generator/DftNextStateGenerator.cpp b/src/generator/DftNextStateGenerator.cpp index 3138fef6a..953a36db4 100644 --- a/src/generator/DftNextStateGenerator.cpp +++ b/src/generator/DftNextStateGenerator.cpp @@ -8,13 +8,13 @@ namespace storm { namespace generator { template<typename ValueType, typename StateType> - DftNextStateGenerator<ValueType, StateType>::DftNextStateGenerator(storm::storage::DFT<ValueType> const& dft, storm::storage::DFTStateGenerationInfo const& stateGenerationInfo) : mDft(dft), mStateGenerationInfo(stateGenerationInfo), state(nullptr), comparator() { - // Intentionally left empty. + DftNextStateGenerator<ValueType, StateType>::DftNextStateGenerator(storm::storage::DFT<ValueType> const& dft, storm::storage::DFTStateGenerationInfo const& stateGenerationInfo, bool enableDC, bool mergeFailedStates) : mDft(dft), mStateGenerationInfo(stateGenerationInfo), state(nullptr), enableDC(enableDC), mergeFailedStates(mergeFailedStates), comparator() { + deterministicModel = !mDft.canHaveNondeterminism(); } template<typename ValueType, typename StateType> bool DftNextStateGenerator<ValueType, StateType>::isDeterministicModel() const { - STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "This functionality is not yet implemented."); + return deterministicModel; } template<typename ValueType, typename StateType> @@ -29,117 +29,201 @@ namespace storm { } template<typename ValueType, typename StateType> - void DftNextStateGenerator<ValueType, StateType>::load(std::shared_ptr<storm::storage::DFTState<ValueType>> const& state) { - /*// Since almost all subsequent operations are based on the evaluator, we load the state into it now. - unpackStateIntoEvaluator(state, variableInformation, evaluator); - - // Also, we need to store a pointer to the state itself, because we need to be able to access it when expanding it. - this->state = &state;*/ - STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "This functionality is not yet implemented."); + void DftNextStateGenerator<ValueType, StateType>::load(DFTStatePointer const& state) { + // TODO Matthias load state from bitvector + // Store a pointer to the state itself, because we need to be able to access it when expanding it. + this->state = &state; } template<typename ValueType, typename StateType> bool DftNextStateGenerator<ValueType, StateType>::satisfies(storm::expressions::Expression const& expression) const { - STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "This functionality is not yet implemented."); + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "The method 'satisfies' is not yet implemented."); } template<typename ValueType, typename StateType> StateBehavior<ValueType, StateType> DftNextStateGenerator<ValueType, StateType>::expand(StateToIdCallback const& stateToIdCallback) { - /*// Prepare the result, in case we return early. + DFTStatePointer currentState = *state; + STORM_LOG_TRACE("Explore state: " << mDft.getStateString(currentState)); + + // Prepare the result, in case we return early. StateBehavior<ValueType, StateType> result; - - // First, construct the state rewards, as we may return early if there are no choices later and we already - // need the state rewards then. - for (auto const& rewardModel : selectedRewardModels) { - ValueType stateRewardValue = storm::utility::zero<ValueType>(); - if (rewardModel.get().hasStateRewards()) { - for (auto const& stateReward : rewardModel.get().getStateRewards()) { - if (evaluator.asBool(stateReward.getStatePredicateExpression())) { - stateRewardValue += ValueType(evaluator.asRational(stateReward.getRewardValueExpression())); - } - } - } - result.addStateReward(stateRewardValue); - } - - // If a terminal expression was set and we must not expand this state, return now. - if (terminalExpression && evaluator.asBool(terminalExpression.get())) { - return result; - } - - // Get all choices for the state. - std::vector<Choice<ValueType>> allChoices = getUnlabeledChoices(*this->state, stateToIdCallback); - std::vector<Choice<ValueType>> allLabeledChoices = getLabeledChoices(*this->state, stateToIdCallback); - for (auto& choice : allLabeledChoices) { - allChoices.push_back(std::move(choice)); - } - - std::size_t totalNumberOfChoices = allChoices.size(); - - // If there is not a single choice, we return immediately, because the state has no behavior (other than - // the state reward). - if (totalNumberOfChoices == 0) { + + // Initialization + bool hasDependencies = currentState->nrFailableDependencies() > 0; + size_t failableCount = hasDependencies ? currentState->nrFailableDependencies() : currentState->nrFailableBEs(); + size_t currentFailable = 0; + Choice<ValueType, StateType> choice(0, !hasDependencies); + + // Check for absorbing state + if (mDft.hasFailed(currentState) || mDft.isFailsafe(currentState) || currentState->nrFailableBEs() == 0) { + // Add self loop + choice.addProbability(currentState->getId(), storm::utility::one<ValueType>()); + STORM_LOG_TRACE("Added self loop for " << currentState->getId()); + // No further exploration required + result.addChoice(std::move(choice)); + result.setExpanded(); return result; } - - // If the model is a deterministic model, we need to fuse the choices into one. - if (program.isDeterministicModel() && totalNumberOfChoices > 1) { - Choice<ValueType> globalChoice; - - // For CTMCs, we need to keep track of the total exit rate to scale the action rewards later. For DTMCs - // this is equal to the number of choices, which is why we initialize it like this here. - ValueType totalExitRate = program.isDiscreteTimeModel() ? static_cast<ValueType>(totalNumberOfChoices) : storm::utility::zero<ValueType>(); - - // Iterate over all choices and combine the probabilities/rates into one choice. - for (auto const& choice : allChoices) { - for (auto const& stateProbabilityPair : choice) { - if (program.isDiscreteTimeModel()) { - globalChoice.addProbability(stateProbabilityPair.first, stateProbabilityPair.second / totalNumberOfChoices); - } else { - globalChoice.addProbability(stateProbabilityPair.first, stateProbabilityPair.second); + + // Let BE fail + while (currentFailable < failableCount) { + STORM_LOG_ASSERT(!mDft.hasFailed(currentState), "Dft has failed."); + + // Construct new state as copy from original one + DFTStatePointer newState = std::make_shared<storm::storage::DFTState<ValueType>>(*currentState); + std::pair<std::shared_ptr<storm::storage::DFTBE<ValueType> const>, bool> nextBEPair = newState->letNextBEFail(currentFailable); + std::shared_ptr<storm::storage::DFTBE<ValueType> const>& nextBE = nextBEPair.first; + STORM_LOG_ASSERT(nextBE, "NextBE is null."); + STORM_LOG_ASSERT(nextBEPair.second == hasDependencies, "Failure due to dependencies does not match."); + STORM_LOG_TRACE("With the failure of: " << nextBE->name() << " [" << nextBE->id() << "] in " << mDft.getStateString(currentState)); + + /*if (storm::settings::getModule<storm::settings::modules::DFTSettings>().computeApproximation()) { + if (!storm::utility::isZero(exitRate)) { + ValueType rate = nextBE->activeFailureRate(); + ValueType div = rate / exitRate; + if (!storm::utility::isZero(exitRate) && belowThreshold(div)) { + // Set transition directly to failed state + auto resultFind = outgoingRates.find(failedIndex); + if (resultFind != outgoingRates.end()) { + // Add to existing transition + resultFind->second += rate; + STORM_LOG_TRACE("Updated transition to " << resultFind->first << " with rate " << rate << " to new rate " << resultFind->second); + } else { + // Insert new transition + outgoingRates.insert(std::make_pair(failedIndex, rate)); + STORM_LOG_TRACE("Added transition to " << failedIndex << " with rate " << rate); + } + exitRate += rate; + std::cout << "IGNORE: " << nextBE->name() << " [" << nextBE->id() << "] with rate " << rate << std::endl; + //STORM_LOG_TRACE("Ignore: " << nextBE->name() << " [" << nextBE->id() << "] with rate " << rate); + continue; } } - - if (hasStateActionRewards && !program.isDiscreteTimeModel()) { - totalExitRate += choice.getTotalMass(); + }*/ + + // Propagate + storm::storage::DFTStateSpaceGenerationQueues<ValueType> 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()); + } + + // 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()); + } + + if(newState->isInvalid()) { + // Continue with next possible state + ++currentFailable; + continue; + } + + StateType newStateId; + + if (newState->hasFailed(mDft.getTopLevelIndex()) && mergeFailedStates) { + // Use unique failed state + newStateId = mergeFailedStateId; + } else { + // Propagate failsafe + while (!queues.failsafePropagationDone()) { + DFTGatePointer next = queues.nextFailsafePropagation(); + next->checkFailsafe(*newState, queues); } - - if (buildChoiceLabeling) { - globalChoice.addChoiceLabels(choice.getChoiceLabels()); + + // Propagate dont cares + while (enableDC && !queues.dontCarePropagationDone()) { + DFTElementPointer next = queues.nextDontCarePropagation(); + next->checkDontCareAnymore(*newState, queues); } + + // Update failable dependencies + newState->updateFailableDependencies(nextBE->id()); + newState->updateDontCareDependencies(nextBE->id()); + + // Add new state + newStateId = stateToIdCallback(newState); } - - // Now construct the state-action reward for all selected reward models. - for (auto const& rewardModel : selectedRewardModels) { - ValueType stateActionRewardValue = storm::utility::zero<ValueType>(); - if (rewardModel.get().hasStateActionRewards()) { - for (auto const& stateActionReward : rewardModel.get().getStateActionRewards()) { - for (auto const& choice : allChoices) { - if (stateActionReward.getActionIndex() == choice.getActionIndex() && evaluator.asBool(stateActionReward.getStatePredicateExpression())) { - stateActionRewardValue += ValueType(evaluator.asRational(stateActionReward.getRewardValueExpression())) * choice.getTotalMass() / totalExitRate; - } - } - - } + + // Set transitions + if (hasDependencies) { + // Failure is due to dependency -> add non-deterministic choice + std::shared_ptr<storm::storage::DFTDependency<ValueType> const> dependency = mDft.getDependency(currentState->getDependencyId(currentFailable)); + choice.addProbability(newStateId, dependency->probability()); + STORM_LOG_TRACE("Added transition to " << newStateId << " with probability " << dependency->probability()); + + if (!storm::utility::isOne(dependency->probability())) { + // Add transition to state where dependency was unsuccessful + DFTStatePointer unsuccessfulState = std::make_shared<storm::storage::DFTState<ValueType>>(*currentState); + unsuccessfulState->letDependencyBeUnsuccessful(currentFailable); + // Add state + StateType unsuccessfulStateId = stateToIdCallback(unsuccessfulState); + ValueType remainingProbability = storm::utility::one<ValueType>() - dependency->probability(); + choice.addProbability(unsuccessfulStateId, remainingProbability); + STORM_LOG_TRACE("Added transition to " << unsuccessfulStateId << " with remaining probability " << remainingProbability); } - globalChoice.addChoiceReward(stateActionRewardValue); + result.addChoice(std::move(choice)); + } else { + // Failure is due to "normal" BE failure + // Set failure rate according to activation + bool isActive = true; + if (mDft.hasRepresentant(nextBE->id())) { + // Active must be checked for the state we are coming from as this state is responsible for the + // rate and not the new state we are going to + isActive = currentState->isActive(mDft.getRepresentant(nextBE->id())->id()); + } + ValueType rate = isActive ? nextBE->activeFailureRate() : nextBE->passiveFailureRate(); + STORM_LOG_ASSERT(!storm::utility::isZero(rate), "Rate is 0."); + choice.addProbability(newStateId, rate); + STORM_LOG_TRACE("Added transition to " << newStateId << " with " << (isActive ? "active" : "passive") << " rate " << rate); } - - // Move the newly fused choice in place. - allChoices.clear(); - allChoices.push_back(std::move(globalChoice)); - } + + ++currentFailable; + } // end while failing BE - // Move all remaining choices in place. - for (auto& choice : allChoices) { + if (!hasDependencies) { + // Add all rates as one choice result.addChoice(std::move(choice)); } - + + STORM_LOG_TRACE("Finished exploring state: " << mDft.getStateString(currentState)); result.setExpanded(); - return result;*/ - STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "This functionality is not yet implemented."); + return result; + } + + template<typename ValueType, typename StateType> + StateBehavior<ValueType, StateType> DftNextStateGenerator<ValueType, StateType>::createMergeFailedState(StateToIdCallback const& stateToIdCallback) { + STORM_LOG_ASSERT(mergeFailedStates, "No unique failed state used."); + // Introduce explicit fail state + DFTStatePointer failedState = std::make_shared<storm::storage::DFTState<ValueType>>(mDft, mStateGenerationInfo, 0); + mergeFailedStateId = stateToIdCallback(failedState); + STORM_LOG_TRACE("Introduce fail state with id: " << mergeFailedStateId); + + // Add self loop + Choice<ValueType, StateType> choice(0, true); + choice.addProbability(mergeFailedStateId, storm::utility::one<ValueType>()); + + // No further exploration required + StateBehavior<ValueType, StateType> result; + result.addChoice(std::move(choice)); + result.setExpanded(); + return result; } - template class DftNextStateGenerator<double>; template class DftNextStateGenerator<storm::RationalFunction>; diff --git a/src/generator/DftNextStateGenerator.h b/src/generator/DftNextStateGenerator.h index 17a7dd25a..f7c4945c7 100644 --- a/src/generator/DftNextStateGenerator.h +++ b/src/generator/DftNextStateGenerator.h @@ -9,15 +9,21 @@ namespace storm { namespace generator { - template<typename ValueType, typename StateType = uint_fast64_t> + /*! + * Next state generator for DFTs. + */ + template<typename ValueType, typename StateType = uint32_t> class DftNextStateGenerator : public NextStateGenerator<ValueType, std::shared_ptr<storm::storage::DFTState<ValueType>>, StateType> { using DFTStatePointer = std::shared_ptr<storm::storage::DFTState<ValueType>>; + using DFTElementPointer = std::shared_ptr<storm::storage::DFTElement<ValueType>>; + using DFTGatePointer = std::shared_ptr<storm::storage::DFTGate<ValueType>>; + using DFTRestrictionPointer = std::shared_ptr<storm::storage::DFTRestriction<ValueType>>; public: typedef typename NextStateGenerator<ValueType, DFTStatePointer, StateType>::StateToIdCallback StateToIdCallback; - DftNextStateGenerator(storm::storage::DFT<ValueType> const& dft, storm::storage::DFTStateGenerationInfo const& stateGenerationInfo); + DftNextStateGenerator(storm::storage::DFT<ValueType> const& dft, storm::storage::DFTStateGenerationInfo const& stateGenerationInfo, bool enableDC, bool mergeFailedStates); virtual bool isDeterministicModel() const override; virtual std::vector<StateType> getInitialStates(StateToIdCallback const& stateToIdCallback) override; @@ -26,15 +32,38 @@ namespace storm { virtual StateBehavior<ValueType, StateType> expand(StateToIdCallback const& stateToIdCallback) override; virtual bool satisfies(storm::expressions::Expression const& expression) const override; + /*! + * Create unique failed state. + * + * @param stateToIdCallback Callback for state. The callback should just return the id and not use the state. + * + * @return Behavior of state. + */ + StateBehavior<ValueType, StateType> createMergeFailedState(StateToIdCallback const& stateToIdCallback); + private: - // The program used for the generation of next states. + // The dft used for the generation of next states. storm::storage::DFT<ValueType> const& mDft; - + + // General information for the state generation. storm::storage::DFTStateGenerationInfo const& mStateGenerationInfo; - - DFTStatePointer const state; - + + // Current state + DFTStatePointer const* state; + + // Flag indicating if dont care propagation is enabled. + bool enableDC; + + // Flag indication if all failed states should be merged into one. + bool mergeFailedStates = true; + + // Id of the merged failed state + StateType mergeFailedStateId = 0; + + // Flag indicating if the model is deterministic. + bool deterministicModel = true; + // A comparator used to compare constants. storm::utility::ConstantsComparator<ValueType> comparator; }; diff --git a/src/generator/StateBehavior.cpp b/src/generator/StateBehavior.cpp index 6ae1f6de0..ccbdd5166 100644 --- a/src/generator/StateBehavior.cpp +++ b/src/generator/StateBehavior.cpp @@ -56,7 +56,10 @@ namespace storm { } template class StateBehavior<double>; + +#ifdef STORM_HAVE_CARL template class StateBehavior<storm::RationalFunction>; +#endif } } \ No newline at end of file diff --git a/src/models/sparse/MarkovAutomaton.cpp b/src/models/sparse/MarkovAutomaton.cpp index 7b997293f..f8c40ad2f 100644 --- a/src/models/sparse/MarkovAutomaton.cpp +++ b/src/models/sparse/MarkovAutomaton.cpp @@ -265,7 +265,7 @@ namespace storm { } template <typename ValueType, typename RewardModelType> - std::shared_ptr<storm::models::sparse::Ctmc<ValueType, RewardModelType>> MarkovAutomaton<ValueType, RewardModelType>::convertToCTMC() { + std::shared_ptr<storm::models::sparse::Ctmc<ValueType, RewardModelType>> MarkovAutomaton<ValueType, RewardModelType>::convertToCTMC() const { STORM_LOG_TRACE("MA matrix:" << std::endl << this->getTransitionMatrix()); STORM_LOG_TRACE("Markovian states: " << getMarkovianStates()); diff --git a/src/models/sparse/MarkovAutomaton.h b/src/models/sparse/MarkovAutomaton.h index 9864f4f85..bb7272f6d 100644 --- a/src/models/sparse/MarkovAutomaton.h +++ b/src/models/sparse/MarkovAutomaton.h @@ -150,7 +150,12 @@ namespace storm { bool hasOnlyTrivialNondeterminism() const; - std::shared_ptr<storm::models::sparse::Ctmc<ValueType, RewardModelType>> convertToCTMC(); + /*! + * Convert the MA into a MA by eliminating all states with probabilistic choices. + * + * @return Ctmc. + */ + std::shared_ptr<storm::models::sparse::Ctmc<ValueType, RewardModelType>> convertToCTMC() const; virtual void writeDotToStream(std::ostream& outStream, bool includeLabeling = true, storm::storage::BitVector const* subsystem = nullptr, std::vector<ValueType> const* firstValue = nullptr, std::vector<ValueType> const* secondValue = nullptr, std::vector<uint_fast64_t> const* stateColoring = nullptr, std::vector<std::string> const* colors = nullptr, std::vector<uint_fast64_t>* scheduler = nullptr, bool finalizeOutput = true) const; diff --git a/src/models/sparse/StateLabeling.cpp b/src/models/sparse/StateLabeling.cpp index 7957e6a99..70561c8cb 100644 --- a/src/models/sparse/StateLabeling.cpp +++ b/src/models/sparse/StateLabeling.cpp @@ -29,7 +29,7 @@ namespace storm { return true; } - StateLabeling StateLabeling::getSubLabeling(storm::storage::BitVector const& states) { + StateLabeling StateLabeling::getSubLabeling(storm::storage::BitVector const& states) const { StateLabeling result(states.getNumberOfSetBits()); for (auto const& labelIndexPair : nameToLabelingIndexMap) { result.addLabel(labelIndexPair.first, labelings[labelIndexPair.second] % states); diff --git a/src/models/sparse/StateLabeling.h b/src/models/sparse/StateLabeling.h index c7b3411a7..ac8ac730a 100644 --- a/src/models/sparse/StateLabeling.h +++ b/src/models/sparse/StateLabeling.h @@ -49,7 +49,7 @@ namespace storm { * * @param states The selected set of states. */ - StateLabeling getSubLabeling(storm::storage::BitVector const& states); + StateLabeling getSubLabeling(storm::storage::BitVector const& states) const; /*! * Adds a new label to the labelings. Initially, no state is labeled with this label. diff --git a/src/storage/dft/DFT.cpp b/src/storage/dft/DFT.cpp index 5fdf44ab3..87ee74cdd 100644 --- a/src/storage/dft/DFT.cpp +++ b/src/storage/dft/DFT.cpp @@ -528,6 +528,11 @@ namespace storm { } } + template<typename ValueType> + bool DFT<ValueType>::canHaveNondeterminism() const { + return !getDependencies().empty(); + } + template<typename ValueType> DFTColouring<ValueType> DFT<ValueType>::colourDFT() const { return DFTColouring<ValueType>(*this); diff --git a/src/storage/dft/DFT.h b/src/storage/dft/DFT.h index 7b0177d07..c12204e06 100644 --- a/src/storage/dft/DFT.h +++ b/src/storage/dft/DFT.h @@ -198,6 +198,8 @@ namespace storm { } return elements; } + + bool canHaveNondeterminism() const; std::vector<DFT<ValueType>> topModularisation() const; diff --git a/src/storage/dft/DFTState.cpp b/src/storage/dft/DFTState.cpp index 9a0a16dba..156f99d14 100644 --- a/src/storage/dft/DFTState.cpp +++ b/src/storage/dft/DFTState.cpp @@ -9,11 +9,11 @@ namespace storm { DFTState<ValueType>::DFTState(DFT<ValueType> const& dft, DFTStateGenerationInfo const& stateGenerationInfo, size_t id) : mStatus(dft.stateVectorSize()), mId(id), mDft(dft), mStateGenerationInfo(stateGenerationInfo) { // Initialize uses - for(size_t id : mDft.getSpareIndices()) { - std::shared_ptr<DFTGate<ValueType> const> elem = mDft.getGate(id); + for(size_t spareId : mDft.getSpareIndices()) { + std::shared_ptr<DFTGate<ValueType> const> elem = mDft.getGate(spareId); STORM_LOG_ASSERT(elem->isSpareGate(), "Element is no spare gate."); STORM_LOG_ASSERT(elem->nrChildren() > 0, "Element has no child."); - this->setUses(id, elem->children()[0]->id()); + this->setUses(spareId, elem->children()[0]->id()); } // Initialize activation diff --git a/src/utility/vector.cpp b/src/utility/vector.cpp index 20af3c628..44e480029 100644 --- a/src/utility/vector.cpp +++ b/src/utility/vector.cpp @@ -1,8 +1,9 @@ #include "src/utility/vector.h" -//template<typename ValueType> -//std::ostream& operator<<(std::ostream& out, std::vector<ValueType> const& vector) { -std::ostream& operator<<(std::ostream& out, std::vector<double> const& vector) { +// Template was causing problems as Carl has the same function +/* +template<typename ValueType> +std::ostream& operator<<(std::ostream& out, std::vector<ValueType> const& vector) { out << "vector (" << vector.size() << ") [ "; for (uint_fast64_t i = 0; i < vector.size() - 1; ++i) { out << vector[i] << ", "; @@ -13,5 +14,26 @@ std::ostream& operator<<(std::ostream& out, std::vector<double> const& vector) { } // Explicitly instantiate functions. -//template std::ostream& operator<<(std::ostream& out, std::vector<double> const& vector); -//template std::ostream& operator<<(std::ostream& out, std::vector<uint_fast64_t> const& vector); \ No newline at end of file +template std::ostream& operator<<(std::ostream& out, std::vector<double> const& vector); +template std::ostream& operator<<(std::ostream& out, std::vector<uint_fast64_t> const& vector); +*/ + +std::ostream& operator<<(std::ostream& out, std::vector<double> const& vector) { + out << "vector (" << vector.size() << ") [ "; + for (uint_fast64_t i = 0; i < vector.size() - 1; ++i) { + out << vector[i] << ", "; + } + out << vector.back(); + out << " ]"; + return out; +} + +std::ostream& operator<<(std::ostream& out, std::vector<uint_fast64_t> const& vector) { + out << "vector (" << vector.size() << ") [ "; + for (uint_fast64_t i = 0; i < vector.size() - 1; ++i) { + out << vector[i] << ", "; + } + out << vector.back(); + out << " ]"; + return out; +} \ No newline at end of file diff --git a/src/utility/vector.h b/src/utility/vector.h index 0a4da1e03..a767f5fcc 100644 --- a/src/utility/vector.h +++ b/src/utility/vector.h @@ -19,6 +19,7 @@ //template<typename ValueType> //std::ostream& operator<<(std::ostream& out, std::vector<ValueType> const& vector); std::ostream& operator<<(std::ostream& out, std::vector<double> const& vector); +std::ostream& operator<<(std::ostream& out, std::vector<uint_fast64_t> const& vector); namespace storm { namespace utility { From cfc082417ba8239a8d19d77b89fa1dab69f18819 Mon Sep 17 00:00:00 2001 From: Mavo <matthias.volk@rwth-aachen.de> Date: Mon, 26 Sep 2016 18:58:47 +0200 Subject: [PATCH 12/65] Refactoring Former-commit-id: b3896c45a40b79181bf8b87dfe93813ca2e99093 --- resources/3rdparty/CMakeLists.txt | 4 +- src/CMakeLists.txt | 2 + src/generator/DftNextStateGenerator.cpp | 41 +++-- src/generator/DftNextStateGenerator.h | 5 +- src/modelchecker/DFTAnalyser.h | 201 --------------------- src/modelchecker/dft/DFTModelChecker.cpp | 214 +++++++++++++++++++++++ src/modelchecker/dft/DFTModelChecker.h | 104 +++++++++++ src/storm-dyftee.cpp | 20 ++- 8 files changed, 359 insertions(+), 232 deletions(-) delete mode 100644 src/modelchecker/DFTAnalyser.h create mode 100644 src/modelchecker/dft/DFTModelChecker.cpp create mode 100644 src/modelchecker/dft/DFTModelChecker.h diff --git a/resources/3rdparty/CMakeLists.txt b/resources/3rdparty/CMakeLists.txt index 9b30445f3..b9a1a1f75 100644 --- a/resources/3rdparty/CMakeLists.txt +++ b/resources/3rdparty/CMakeLists.txt @@ -18,7 +18,7 @@ ExternalProject_Add( DOWNLOAD_COMMAND "" PREFIX ${CMAKE_CURRENT_BINARY_DIR}/glpk-4.57 SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/glpk-4.57 - CONFIGURE_COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/glpk-4.57/configure --prefix=${CMAKE_CURRENT_BINARY_DIR}/glpk-4.57 --libdir=${CMAKE_CURRENT_BINARY_DIR}/glpk-4.57/lib CC=${CMAKE_C_COMPILER} + CONFIGURE_COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/glpk-4.57/configure --prefix=${CMAKE_CURRENT_BINARY_DIR}/glpk-4.57 --libdir=${CMAKE_CURRENT_BINARY_DIR}/glpk-4.57/lib CC=${CMAKE_C_COMPILER} CXX=${CMAKE_CXX_COMPILER} BUILD_COMMAND make "CFLAGS=-O2 -w" INSTALL_COMMAND make install BUILD_IN_SOURCE 0 @@ -33,7 +33,7 @@ ExternalProject_Add( SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/cudd-3.0.0 PREFIX ${CMAKE_CURRENT_BINARY_DIR}/cudd-3.0.0 UPDATE_COMMAND autoreconf - CONFIGURE_COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/cudd-3.0.0/configure --enable-shared --enable-obj --prefix=${CMAKE_CURRENT_BINARY_DIR}/cudd-3.0.0 --libdir=${CMAKE_CURRENT_BINARY_DIR}/cudd-3.0.0/lib CC=${CMAKE_C_COMPILER} CXX=${CMAKE_CXX_COMPILER} + CONFIGURE_COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/cudd-3.0.0/configure --enable-shared --enable-obj --prefix=${CMAKE_CURRENT_BINARY_DIR}/cudd-3.0.0 --libdir=${CMAKE_CURRENT_BINARY_DIR}/cudd-3.0.0/lib CC=${CMAKE_C_COMPILER} CXX=${CMAKE_CXX_COMPILER} BUILD_COMMAND make "CFLAGS=-O2 -w" INSTALL_COMMAND make install BUILD_IN_SOURCE 0 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 34b3cf77b..87b31189e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -22,6 +22,7 @@ file(GLOB_RECURSE STORM_MODELCHECKER_PRCTL_HELPER_FILES ${PROJECT_SOURCE_DIR}/sr file(GLOB STORM_MODELCHECKER_CSL_FILES ${PROJECT_SOURCE_DIR}/src/modelchecker/csl/*.h ${PROJECT_SOURCE_DIR}/src/modelchecker/csl/*.cpp) file(GLOB_RECURSE STORM_MODELCHECKER_CSL_HELPER_FILES ${PROJECT_SOURCE_DIR}/src/modelchecker/csl/helper/*.h ${PROJECT_SOURCE_DIR}/src/modelchecker/csl/helper/*.cpp) file(GLOB_RECURSE STORM_MODELCHECKER_REACHABILITY_FILES ${PROJECT_SOURCE_DIR}/src/modelchecker/reachability/*.h ${PROJECT_SOURCE_DIR}/src/modelchecker/reachability/*.cpp) +file(GLOB_RECURSE STORM_MODELCHECKER_DFT_FILES ${PROJECT_SOURCE_DIR}/src/modelchecker/dft/*.h ${PROJECT_SOURCE_DIR}/src/modelchecker/dft/*.cpp) file(GLOB_RECURSE STORM_MODELCHECKER_EXPLORATION_FILES ${PROJECT_SOURCE_DIR}/src/modelchecker/exploration/*.h ${PROJECT_SOURCE_DIR}/src/modelchecker/exploration/*.cpp) file(GLOB_RECURSE STORM_MODELCHECKER_PROPOSITIONAL_FILES ${PROJECT_SOURCE_DIR}/src/modelchecker/propositional/*.h ${PROJECT_SOURCE_DIR}/src/modelchecker/propositional/*.cpp) file(GLOB_RECURSE STORM_MODELCHECKER_RESULTS_FILES ${PROJECT_SOURCE_DIR}/src/modelchecker/results/*.h ${PROJECT_SOURCE_DIR}/src/modelchecker/results/*.cpp) @@ -75,6 +76,7 @@ source_group(modelchecker\\prctl FILES ${STORM_MODELCHECKER_PRCTL_FILES}) source_group(modelchecker\\prctl\\helper FILES ${STORM_MODELCHECKER_PRCTL_HELPER_FILES}) source_group(modelchecker\\csl FILES ${STORM_MODELCHECKER_CSL_FILES}) source_group(modelchecker\\csl\\helper FILES ${STORM_MODELCHECKER_CSL_HELPER_FILES}) +source_group(modelchecker\\dft FILES ${STORM_MODELCHECKER_DFT_FILES}) source_group(modelchecker\\exploration FILES ${STORM_MODELCHECKER_EXPLORATION_FILES}) source_group(modelchecker\\reachability FILES ${STORM_MODELCHECKER_REACHABILITY_FILES}) source_group(modelchecker\\propositional FILES ${STORM_MODELCHECKER_PROPOSITIONAL_FILES}) diff --git a/src/generator/DftNextStateGenerator.cpp b/src/generator/DftNextStateGenerator.cpp index 953a36db4..1f39fe2cf 100644 --- a/src/generator/DftNextStateGenerator.cpp +++ b/src/generator/DftNextStateGenerator.cpp @@ -27,12 +27,18 @@ namespace storm { initialState->setId(id); return {id}; } - + + template<typename ValueType, typename StateType> + void DftNextStateGenerator<ValueType, StateType>::load(CompressedState const& state) { + // Load the state from bitvector + size_t id = 0; //TODO Matthias: set correct id + this->state = std::make_shared<storm::storage::DFTState<ValueType>>(state, mDft, mStateGenerationInfo, id); + } + template<typename ValueType, typename StateType> void DftNextStateGenerator<ValueType, StateType>::load(DFTStatePointer const& state) { - // TODO Matthias load state from bitvector // Store a pointer to the state itself, because we need to be able to access it when expanding it. - this->state = &state; + this->state = state; } template<typename ValueType, typename StateType> @@ -42,23 +48,22 @@ namespace storm { template<typename ValueType, typename StateType> StateBehavior<ValueType, StateType> DftNextStateGenerator<ValueType, StateType>::expand(StateToIdCallback const& stateToIdCallback) { - DFTStatePointer currentState = *state; - STORM_LOG_TRACE("Explore state: " << mDft.getStateString(currentState)); + STORM_LOG_TRACE("Explore state: " << mDft.getStateString(state)); // Prepare the result, in case we return early. StateBehavior<ValueType, StateType> result; // Initialization - bool hasDependencies = currentState->nrFailableDependencies() > 0; - size_t failableCount = hasDependencies ? currentState->nrFailableDependencies() : currentState->nrFailableBEs(); + bool hasDependencies = state->nrFailableDependencies() > 0; + size_t failableCount = hasDependencies ? state->nrFailableDependencies() : state->nrFailableBEs(); size_t currentFailable = 0; Choice<ValueType, StateType> choice(0, !hasDependencies); // Check for absorbing state - if (mDft.hasFailed(currentState) || mDft.isFailsafe(currentState) || currentState->nrFailableBEs() == 0) { + if (mDft.hasFailed(state) || mDft.isFailsafe(state) || state->nrFailableBEs() == 0) { // Add self loop - choice.addProbability(currentState->getId(), storm::utility::one<ValueType>()); - STORM_LOG_TRACE("Added self loop for " << currentState->getId()); + choice.addProbability(state->getId(), storm::utility::one<ValueType>()); + STORM_LOG_TRACE("Added self loop for " << state->getId()); // No further exploration required result.addChoice(std::move(choice)); result.setExpanded(); @@ -67,15 +72,15 @@ namespace storm { // Let BE fail while (currentFailable < failableCount) { - STORM_LOG_ASSERT(!mDft.hasFailed(currentState), "Dft has failed."); + STORM_LOG_ASSERT(!mDft.hasFailed(state), "Dft has failed."); // Construct new state as copy from original one - DFTStatePointer newState = std::make_shared<storm::storage::DFTState<ValueType>>(*currentState); + DFTStatePointer newState = std::make_shared<storm::storage::DFTState<ValueType>>(*state); std::pair<std::shared_ptr<storm::storage::DFTBE<ValueType> const>, bool> nextBEPair = newState->letNextBEFail(currentFailable); std::shared_ptr<storm::storage::DFTBE<ValueType> const>& nextBE = nextBEPair.first; STORM_LOG_ASSERT(nextBE, "NextBE is null."); STORM_LOG_ASSERT(nextBEPair.second == hasDependencies, "Failure due to dependencies does not match."); - STORM_LOG_TRACE("With the failure of: " << nextBE->name() << " [" << nextBE->id() << "] in " << mDft.getStateString(currentState)); + STORM_LOG_TRACE("With the failure of: " << nextBE->name() << " [" << nextBE->id() << "] in " << mDft.getStateString(state)); /*if (storm::settings::getModule<storm::settings::modules::DFTSettings>().computeApproximation()) { if (!storm::utility::isZero(exitRate)) { @@ -163,13 +168,13 @@ namespace storm { // Set transitions if (hasDependencies) { // Failure is due to dependency -> add non-deterministic choice - std::shared_ptr<storm::storage::DFTDependency<ValueType> const> dependency = mDft.getDependency(currentState->getDependencyId(currentFailable)); + std::shared_ptr<storm::storage::DFTDependency<ValueType> const> dependency = mDft.getDependency(state->getDependencyId(currentFailable)); choice.addProbability(newStateId, dependency->probability()); STORM_LOG_TRACE("Added transition to " << newStateId << " with probability " << dependency->probability()); if (!storm::utility::isOne(dependency->probability())) { // Add transition to state where dependency was unsuccessful - DFTStatePointer unsuccessfulState = std::make_shared<storm::storage::DFTState<ValueType>>(*currentState); + DFTStatePointer unsuccessfulState = std::make_shared<storm::storage::DFTState<ValueType>>(*state); unsuccessfulState->letDependencyBeUnsuccessful(currentFailable); // Add state StateType unsuccessfulStateId = stateToIdCallback(unsuccessfulState); @@ -185,7 +190,7 @@ namespace storm { if (mDft.hasRepresentant(nextBE->id())) { // Active must be checked for the state we are coming from as this state is responsible for the // rate and not the new state we are going to - isActive = currentState->isActive(mDft.getRepresentant(nextBE->id())->id()); + isActive = state->isActive(mDft.getRepresentant(nextBE->id())->id()); } ValueType rate = isActive ? nextBE->activeFailureRate() : nextBE->passiveFailureRate(); STORM_LOG_ASSERT(!storm::utility::isZero(rate), "Rate is 0."); @@ -201,7 +206,7 @@ namespace storm { result.addChoice(std::move(choice)); } - STORM_LOG_TRACE("Finished exploring state: " << mDft.getStateString(currentState)); + STORM_LOG_TRACE("Finished exploring state: " << mDft.getStateString(state)); result.setExpanded(); return result; } @@ -228,4 +233,4 @@ namespace storm { template class DftNextStateGenerator<double>; template class DftNextStateGenerator<storm::RationalFunction>; } -} \ No newline at end of file +} diff --git a/src/generator/DftNextStateGenerator.h b/src/generator/DftNextStateGenerator.h index f7c4945c7..beab8f5f0 100644 --- a/src/generator/DftNextStateGenerator.h +++ b/src/generator/DftNextStateGenerator.h @@ -29,6 +29,7 @@ namespace storm { virtual std::vector<StateType> getInitialStates(StateToIdCallback const& stateToIdCallback) override; virtual void load(DFTStatePointer const& state) override; + void load(CompressedState const& state); virtual StateBehavior<ValueType, StateType> expand(StateToIdCallback const& stateToIdCallback) override; virtual bool satisfies(storm::expressions::Expression const& expression) const override; @@ -50,7 +51,7 @@ namespace storm { storm::storage::DFTStateGenerationInfo const& mStateGenerationInfo; // Current state - DFTStatePointer const* state; + DFTStatePointer state; // Flag indicating if dont care propagation is enabled. bool enableDC; @@ -71,4 +72,4 @@ namespace storm { } } -#endif /* STORM_GENERATOR_DFTNEXTSTATEGENERATOR_H_ */ \ No newline at end of file +#endif /* STORM_GENERATOR_DFTNEXTSTATEGENERATOR_H_ */ diff --git a/src/modelchecker/DFTAnalyser.h b/src/modelchecker/DFTAnalyser.h deleted file mode 100644 index cc1e34696..000000000 --- a/src/modelchecker/DFTAnalyser.h +++ /dev/null @@ -1,201 +0,0 @@ -#pragma once - -#include "src/logic/Formula.h" -#include "src/parser/DFTGalileoParser.h" -#include "src/builder/ExplicitDFTModelBuilder.h" -#include "src/builder/ExplicitDFTModelBuilderApprox.h" -#include "src/modelchecker/results/CheckResult.h" -#include "src/utility/storm.h" -#include "src/storage/dft/DFTIsomorphism.h" -#include "src/settings/modules/DFTSettings.h" -#include "src/utility/bitoperations.h" - - -#include <chrono> - -template<typename ValueType> -class DFTAnalyser { - - std::chrono::duration<double> buildingTime = std::chrono::duration<double>::zero(); - std::chrono::duration<double> explorationTime = std::chrono::duration<double>::zero(); - std::chrono::duration<double> bisimulationTime = std::chrono::duration<double>::zero(); - std::chrono::duration<double> modelCheckingTime = std::chrono::duration<double>::zero(); - std::chrono::duration<double> totalTime = std::chrono::duration<double>::zero(); - ValueType checkResult = storm::utility::zero<ValueType>(); -public: - void check(storm::storage::DFT<ValueType> const& origDft , std::shared_ptr<const storm::logic::Formula> const& formula, bool symred = true, bool allowModularisation = true, bool enableDC = true) { - - // Building DFT - std::chrono::high_resolution_clock::time_point totalStart = std::chrono::high_resolution_clock::now(); - - - // Optimizing DFT - storm::storage::DFT<ValueType> dft = origDft.optimize(); - checkResult = checkHelper(dft, formula, symred, allowModularisation, enableDC); - totalTime = std::chrono::high_resolution_clock::now() - totalStart; - - - - } - -private: - ValueType checkHelper(storm::storage::DFT<ValueType> const& dft , std::shared_ptr<const storm::logic::Formula> const& formula, bool symred = true, bool allowModularisation = true, bool enableDC = true) { - STORM_LOG_TRACE("Check helper called"); - bool invResults = false; - std::vector<storm::storage::DFT<ValueType>> dfts = {dft}; - std::vector<ValueType> res; - size_t nrK = 0; // K out of M - size_t nrM = 0; // K out of M - - if(allowModularisation) { - if(dft.topLevelType() == storm::storage::DFTElementType::AND) { - STORM_LOG_TRACE("top modularisation called AND"); - dfts = dft.topModularisation(); - STORM_LOG_TRACE("Modularsation into " << dfts.size() << " submodules."); - nrK = dfts.size(); - nrM = dfts.size(); - } - if(dft.topLevelType() == storm::storage::DFTElementType::OR) { - STORM_LOG_TRACE("top modularisation called OR"); - dfts = dft.topModularisation(); - STORM_LOG_TRACE("Modularsation into " << dfts.size() << " submodules."); - nrK = 0; - nrM = dfts.size(); - invResults = true; - } - if(dft.topLevelType() == storm::storage::DFTElementType::VOT) { - STORM_LOG_TRACE("top modularisation called VOT"); - dfts = dft.topModularisation(); - STORM_LOG_TRACE("Modularsation into " << dfts.size() << " submodules."); - nrK = std::static_pointer_cast<storm::storage::DFTVot<ValueType> const>(dft.getTopLevelGate())->threshold(); - nrM = dfts.size(); - if(nrK <= nrM/2) { - nrK -= 1; - invResults = true; - } - } - if(dfts.size() > 1) { - STORM_LOG_TRACE("Recursive CHECK Call"); - for(auto const ft : dfts) { - res.push_back(checkHelper(ft, formula, symred, allowModularisation)); - } - } - } - if(res.empty()) { - // Model based modularisation. - auto const& models = buildMarkovModels(dfts, formula, symred, enableDC); - - - for (auto const& model : models) { - // Model checking - STORM_LOG_INFO("Model checking..."); - std::chrono::high_resolution_clock::time_point modelCheckingStart = std::chrono::high_resolution_clock::now(); - std::unique_ptr<storm::modelchecker::CheckResult> result(storm::verifySparseModel(model, formula)); - STORM_LOG_INFO("Model checking done."); - STORM_LOG_ASSERT(result, "Result does not exist."); - result->filter(storm::modelchecker::ExplicitQualitativeCheckResult(model->getInitialStates())); - modelCheckingTime += std::chrono::high_resolution_clock::now() - modelCheckingStart; - res.push_back(result->asExplicitQuantitativeCheckResult<ValueType>().getValueMap().begin()->second); - } - } - if(nrM <= 1) { - // No modularisation done. - STORM_LOG_ASSERT(res.size()==1, "Result size not 1."); - return res[0]; - } - - STORM_LOG_TRACE("Combining all results... K=" << nrK << "; M=" << nrM << "; invResults=" << (invResults?"On":"Off")); - ValueType result = storm::utility::zero<ValueType>(); - int limK = invResults ? -1 : nrM+1; - int chK = invResults ? -1 : 1; - for(int cK = nrK; cK != limK; cK += chK ) { - STORM_LOG_ASSERT(cK >= 0, "ck negative."); - size_t permutation = smallestIntWithNBitsSet(static_cast<size_t>(cK)); - do { - STORM_LOG_TRACE("Permutation="<<permutation); - ValueType permResult = storm::utility::one<ValueType>(); - for(size_t i = 0; i < res.size(); ++i) { - if(permutation & (1 << i)) { - permResult *= res[i]; - } else { - permResult *= storm::utility::one<ValueType>() - res[i]; - } - } - STORM_LOG_TRACE("Result for permutation:"<<permResult); - permutation = nextBitPermutation(permutation); - result += permResult; - } while(permutation < (1 << nrM) && permutation != 0); - } - if(invResults) { - return storm::utility::one<ValueType>() - result; - } - return result; - } - - - std::vector<std::shared_ptr<storm::models::sparse::Model<ValueType>>> buildMarkovModels(std::vector<storm::storage::DFT<ValueType>> const& dfts, std::shared_ptr<const storm::logic::Formula> const& formula, bool symred, bool enableDC) { - std::vector<std::shared_ptr<storm::models::sparse::Model<ValueType>>> models; - for(auto& dft : dfts) { - std::chrono::high_resolution_clock::time_point buildingStart = std::chrono::high_resolution_clock::now(); - - std::map<size_t, std::vector<std::vector<size_t>>> emptySymmetry; - storm::storage::DFTIndependentSymmetries symmetries(emptySymmetry); - if(symred) { - auto colouring = dft.colourDFT(); - symmetries = dft.findSymmetries(colouring); - STORM_LOG_INFO("Found " << symmetries.groups.size() << " symmetries."); - STORM_LOG_TRACE("Symmetries: " << std::endl << symmetries); - } - std::chrono::high_resolution_clock::time_point buildingEnd = std::chrono::high_resolution_clock::now(); - buildingTime += buildingEnd - buildingStart; - - // Building Markov Automaton - STORM_LOG_INFO("Building Model..."); - std::shared_ptr<storm::models::sparse::Model<ValueType>> model; - // TODO Matthias: use only one builder if everything works again - if (storm::settings::getModule<storm::settings::modules::DFTSettings>().computeApproximation()) { - storm::builder::ExplicitDFTModelBuilderApprox<ValueType> builder(dft, symmetries, enableDC); - typename storm::builder::ExplicitDFTModelBuilderApprox<ValueType>::LabelOptions labeloptions; // TODO initialize this with the formula - model = builder.buildModel(labeloptions); - } else { - storm::builder::ExplicitDFTModelBuilder<ValueType> builder(dft, symmetries, enableDC); - typename storm::builder::ExplicitDFTModelBuilder<ValueType>::LabelOptions labeloptions; // TODO initialize this with the formula - model = builder.buildModel(labeloptions); - } - //model->printModelInformationToStream(std::cout); - STORM_LOG_INFO("No. states (Explored): " << model->getNumberOfStates()); - STORM_LOG_INFO("No. transitions (Explored): " << model->getNumberOfTransitions()); - std::chrono::high_resolution_clock::time_point explorationEnd = std::chrono::high_resolution_clock::now(); - explorationTime += explorationEnd -buildingEnd; - - // Bisimulation - if (model->isOfType(storm::models::ModelType::Ctmc) && storm::settings::getModule<storm::settings::modules::GeneralSettings>().isBisimulationSet()) { - STORM_LOG_INFO("Bisimulation..."); - model = storm::performDeterministicSparseBisimulationMinimization<storm::models::sparse::Ctmc<ValueType>>(model->template as<storm::models::sparse::Ctmc<ValueType>>(), {formula}, storm::storage::BisimulationType::Weak)->template as<storm::models::sparse::Ctmc<ValueType>>(); - //model->printModelInformationToStream(std::cout); - } - STORM_LOG_INFO("No. states (Bisimulation): " << model->getNumberOfStates()); - STORM_LOG_INFO("No. transitions (Bisimulation): " << model->getNumberOfTransitions()); - bisimulationTime += std::chrono::high_resolution_clock::now() - explorationEnd; - models.push_back(model); - } - return models; - } - -public: - void printTimings(std::ostream& os = std::cout) { - os << "Times:" << std::endl; - os << "Building:\t" << buildingTime.count() << std::endl; - os << "Exploration:\t" << explorationTime.count() << std::endl; - os << "Bisimulation:\t" << bisimulationTime.count() << std::endl; - os << "Modelchecking:\t" << modelCheckingTime.count() << std::endl; - os << "Total:\t\t" << totalTime.count() << std::endl; - } - - void printResult(std::ostream& os = std::cout) { - - os << "Result: ["; - os << checkResult << "]" << std::endl; - } - -}; diff --git a/src/modelchecker/dft/DFTModelChecker.cpp b/src/modelchecker/dft/DFTModelChecker.cpp new file mode 100644 index 000000000..3aa3ed27b --- /dev/null +++ b/src/modelchecker/dft/DFTModelChecker.cpp @@ -0,0 +1,214 @@ +#include "src/modelchecker/dft/DFTModelChecker.h" + +#include "src/builder/ExplicitDFTModelBuilder.h" +#include "src/builder/ExplicitDFTModelBuilderApprox.h" +#include "src/storage/dft/DFTIsomorphism.h" +#include "src/settings/modules/DFTSettings.h" +#include "src/utility/bitoperations.h" + +namespace storm { + namespace modelchecker { + + template<typename ValueType> + DFTModelChecker<ValueType>::DFTModelChecker() { + // Intentionally left empty. + } + + template<typename ValueType> + void DFTModelChecker<ValueType>::check(storm::storage::DFT<ValueType> const& origDft, std::shared_ptr<const storm::logic::Formula> const& formula, bool symred, bool allowModularisation, bool enableDC) { + std::chrono::high_resolution_clock::time_point totalStart = std::chrono::high_resolution_clock::now(); + // Optimizing DFT + storm::storage::DFT<ValueType> dft = origDft.optimize(); + checkResult = checkHelper(dft, formula, symred, allowModularisation, enableDC); + totalTime = std::chrono::high_resolution_clock::now() - totalStart; + } + + template<typename ValueType> + ValueType DFTModelChecker<ValueType>::checkHelper(storm::storage::DFT<ValueType> const& dft, std::shared_ptr<const storm::logic::Formula> const& formula, bool symred, bool allowModularisation, bool enableDC) { + STORM_LOG_TRACE("Check helper called"); + bool invResults = false; + std::vector<storm::storage::DFT<ValueType>> dfts = {dft}; + std::vector<ValueType> res; + size_t nrK = 0; // K out of M + size_t nrM = 0; // K out of M + + // Try modularisation + if(allowModularisation) { + if(dft.topLevelType() == storm::storage::DFTElementType::AND) { + STORM_LOG_TRACE("top modularisation called AND"); + dfts = dft.topModularisation(); + STORM_LOG_TRACE("Modularsation into " << dfts.size() << " submodules."); + nrK = dfts.size(); + nrM = dfts.size(); + } + if(dft.topLevelType() == storm::storage::DFTElementType::OR) { + STORM_LOG_TRACE("top modularisation called OR"); + dfts = dft.topModularisation(); + STORM_LOG_TRACE("Modularsation into " << dfts.size() << " submodules."); + nrK = 0; + nrM = dfts.size(); + invResults = true; + } + if(dft.topLevelType() == storm::storage::DFTElementType::VOT) { + STORM_LOG_TRACE("top modularisation called VOT"); + dfts = dft.topModularisation(); + STORM_LOG_TRACE("Modularsation into " << dfts.size() << " submodules."); + nrK = std::static_pointer_cast<storm::storage::DFTVot<ValueType> const>(dft.getTopLevelGate())->threshold(); + nrM = dfts.size(); + if(nrK <= nrM/2) { + nrK -= 1; + invResults = true; + } + } + if(dfts.size() > 1) { + STORM_LOG_TRACE("Recursive CHECK Call"); + for(auto const ft : dfts) { + res.push_back(checkHelper(ft, formula, symred, allowModularisation, enableDC)); + } + } + } + + if(res.empty()) { + // Model based modularisation. + auto const& models = buildMarkovModels(dfts, formula, symred, enableDC); + + for (auto const& model : models) { + // Model checking + STORM_LOG_INFO("Model checking..."); + std::chrono::high_resolution_clock::time_point modelCheckingStart = std::chrono::high_resolution_clock::now(); + std::unique_ptr<storm::modelchecker::CheckResult> result(storm::verifySparseModel(model, formula)); + STORM_LOG_INFO("Model checking done."); + STORM_LOG_ASSERT(result, "Result does not exist."); + result->filter(storm::modelchecker::ExplicitQualitativeCheckResult(model->getInitialStates())); + modelCheckingTime += std::chrono::high_resolution_clock::now() - modelCheckingStart; + res.push_back(result->asExplicitQuantitativeCheckResult<ValueType>().getValueMap().begin()->second); + } + } + if(nrM <= 1) { + // No modularisation done. + STORM_LOG_ASSERT(res.size()==1, "Result size not 1."); + return res[0]; + } + + STORM_LOG_TRACE("Combining all results... K=" << nrK << "; M=" << nrM << "; invResults=" << (invResults?"On":"Off")); + ValueType result = storm::utility::zero<ValueType>(); + int limK = invResults ? -1 : nrM+1; + int chK = invResults ? -1 : 1; + for(int cK = nrK; cK != limK; cK += chK ) { + STORM_LOG_ASSERT(cK >= 0, "ck negative."); + size_t permutation = smallestIntWithNBitsSet(static_cast<size_t>(cK)); + do { + STORM_LOG_TRACE("Permutation="<<permutation); + ValueType permResult = storm::utility::one<ValueType>(); + for(size_t i = 0; i < res.size(); ++i) { + if(permutation & (1 << i)) { + permResult *= res[i]; + } else { + permResult *= storm::utility::one<ValueType>() - res[i]; + } + } + STORM_LOG_TRACE("Result for permutation:"<<permResult); + permutation = nextBitPermutation(permutation); + result += permResult; + } while(permutation < (1 << nrM) && permutation != 0); + } + if(invResults) { + return storm::utility::one<ValueType>() - result; + } + return result; + } + + template<typename ValueType> + std::vector<ValueType> DFTModelChecker<ValueType>::checkModel(std::vector<storm::storage::DFT<ValueType>> const& dfts, std::shared_ptr<const storm::logic::Formula> const& formula, bool symred, bool enableDC, double approximationError) { + std::vector<ValueType> results; + + auto const& models = buildMarkovModels(dfts, formula, symred, enableDC); + + for (auto const& model : models) { + // Model checking + STORM_LOG_INFO("Model checking..."); + std::chrono::high_resolution_clock::time_point modelCheckingStart = std::chrono::high_resolution_clock::now(); + std::unique_ptr<storm::modelchecker::CheckResult> result(storm::verifySparseModel(model, formula)); + STORM_LOG_INFO("Model checking done."); + STORM_LOG_ASSERT(result, "Result does not exist."); + result->filter(storm::modelchecker::ExplicitQualitativeCheckResult(model->getInitialStates())); + modelCheckingTime += std::chrono::high_resolution_clock::now() - modelCheckingStart; + results.push_back(result->asExplicitQuantitativeCheckResult<ValueType>().getValueMap().begin()->second); + } + return results; + } + + template<typename ValueType> + std::vector<std::shared_ptr<storm::models::sparse::Model<ValueType>>> DFTModelChecker<ValueType>::buildMarkovModels(std::vector<storm::storage::DFT<ValueType>> const& dfts, std::shared_ptr<const storm::logic::Formula> const& formula, bool symred, bool enableDC, double approximationError) { + std::vector<std::shared_ptr<storm::models::sparse::Model<ValueType>>> models; + for(auto& dft : dfts) { + std::chrono::high_resolution_clock::time_point buildingStart = std::chrono::high_resolution_clock::now(); + + std::map<size_t, std::vector<std::vector<size_t>>> emptySymmetry; + storm::storage::DFTIndependentSymmetries symmetries(emptySymmetry); + if(symred) { + auto colouring = dft.colourDFT(); + symmetries = dft.findSymmetries(colouring); + STORM_LOG_INFO("Found " << symmetries.groups.size() << " symmetries."); + STORM_LOG_TRACE("Symmetries: " << std::endl << symmetries); + } + std::chrono::high_resolution_clock::time_point buildingEnd = std::chrono::high_resolution_clock::now(); + buildingTime += buildingEnd - buildingStart; + + // Building Markov Automaton + STORM_LOG_INFO("Building Model..."); + std::shared_ptr<storm::models::sparse::Model<ValueType>> model; + // TODO Matthias: use only one builder if everything works again + if (storm::settings::getModule<storm::settings::modules::DFTSettings>().computeApproximation()) { + storm::builder::ExplicitDFTModelBuilderApprox<ValueType> builder(dft, symmetries, enableDC); + typename storm::builder::ExplicitDFTModelBuilderApprox<ValueType>::LabelOptions labeloptions; // TODO initialize this with the formula + model = builder.buildModel(labeloptions); + } else { + storm::builder::ExplicitDFTModelBuilder<ValueType> builder(dft, symmetries, enableDC); + typename storm::builder::ExplicitDFTModelBuilder<ValueType>::LabelOptions labeloptions; // TODO initialize this with the formula + model = builder.buildModel(labeloptions); + } + //model->printModelInformationToStream(std::cout); + STORM_LOG_INFO("No. states (Explored): " << model->getNumberOfStates()); + STORM_LOG_INFO("No. transitions (Explored): " << model->getNumberOfTransitions()); + std::chrono::high_resolution_clock::time_point explorationEnd = std::chrono::high_resolution_clock::now(); + explorationTime += explorationEnd -buildingEnd; + + // Bisimulation + if (model->isOfType(storm::models::ModelType::Ctmc) && storm::settings::getModule<storm::settings::modules::GeneralSettings>().isBisimulationSet()) { + STORM_LOG_INFO("Bisimulation..."); + model = storm::performDeterministicSparseBisimulationMinimization<storm::models::sparse::Ctmc<ValueType>>(model->template as<storm::models::sparse::Ctmc<ValueType>>(), {formula}, storm::storage::BisimulationType::Weak)->template as<storm::models::sparse::Ctmc<ValueType>>(); + //model->printModelInformationToStream(std::cout); + } + STORM_LOG_INFO("No. states (Bisimulation): " << model->getNumberOfStates()); + STORM_LOG_INFO("No. transitions (Bisimulation): " << model->getNumberOfTransitions()); + bisimulationTime += std::chrono::high_resolution_clock::now() - explorationEnd; + models.push_back(model); + } + return models; + } + + template<typename ValueType> + void DFTModelChecker<ValueType>::printTimings(std::ostream& os) { + os << "Times:" << std::endl; + os << "Building:\t" << buildingTime.count() << std::endl; + os << "Exploration:\t" << explorationTime.count() << std::endl; + os << "Bisimulation:\t" << bisimulationTime.count() << std::endl; + os << "Modelchecking:\t" << modelCheckingTime.count() << std::endl; + os << "Total:\t\t" << totalTime.count() << std::endl; + } + + template<typename ValueType> + void DFTModelChecker<ValueType>::printResult(std::ostream& os) { + os << "Result: ["; + os << checkResult << "]" << std::endl; + } + + + template class DFTModelChecker<double>; + +#ifdef STORM_HAVE_CARL + template class DFTModelChecker<storm::RationalFunction>; +#endif + } +} diff --git a/src/modelchecker/dft/DFTModelChecker.h b/src/modelchecker/dft/DFTModelChecker.h new file mode 100644 index 000000000..0867ed094 --- /dev/null +++ b/src/modelchecker/dft/DFTModelChecker.h @@ -0,0 +1,104 @@ +#ifndef STORM_MODELCHECKER_DFT_DFTMODELCHECKER_H_ + +#include "src/logic/Formula.h" +#include "src/modelchecker/results/CheckResult.h" +#include "src/storage/dft/DFT.h" +#include "src/utility/storm.h" + +#include <chrono> + + +namespace storm { + namespace modelchecker { + + /** + * Analyser for DFTs. + */ + template<typename ValueType> + class DFTModelChecker { + + public: + + /** + * Constructor. + */ + DFTModelChecker(); + + /** + * Main method for checking DFTs. + * + * @param origDft Original DFT + * @param formula Formula to check for + * @param symred Flag indicating if symmetry reduction should be used + * @param allowModularisation Flag indication if modularisation is allowed + * @param enableDC Flag indicating if dont care propagation should be used + */ + void check(storm::storage::DFT<ValueType> const& origDft, std::shared_ptr<const storm::logic::Formula> const& formula, bool symred = true, bool allowModularisation = true, bool enableDC = true); + + /** + * Print timings of all operations to stream. + * + * @param os Output stream to write to. + */ + void printTimings(std::ostream& os = std::cout); + + /** + * Print result to stream. + * + * @param os Output stream to write to. + */ + void printResult(std::ostream& os = std::cout); + + private: + // Timing values + std::chrono::duration<double> buildingTime = std::chrono::duration<double>::zero(); + std::chrono::duration<double> explorationTime = std::chrono::duration<double>::zero(); + std::chrono::duration<double> bisimulationTime = std::chrono::duration<double>::zero(); + std::chrono::duration<double> modelCheckingTime = std::chrono::duration<double>::zero(); + std::chrono::duration<double> totalTime = std::chrono::duration<double>::zero(); + // Model checking result + ValueType checkResult = storm::utility::zero<ValueType>(); + + /** + * Internal helper for model checking a DFT. + * + * @param dft DFT + * @param formula Formula to check for + * @param symred Flag indicating if symmetry reduction should be used + * @param allowModularisation Flag indication if modularisation is allowed + * @param enableDC Flag indicating if dont care propagation should be used + * + * @return Model checking result + */ + ValueType checkHelper(storm::storage::DFT<ValueType> const& dft, std::shared_ptr<const storm::logic::Formula> const& formula, bool symred, bool allowModularisation, bool enableDC); + + /** + * Check the Dfts via model checking. + * + * @param dfts Vector of Dfts + * @param formula Formula to check for + * @param symred Flag indicating if symmetry reduction should be used + * @param enableDC Flag indicating if dont care propagation should be used + * @param approximationError Error allowed for approximation. Value 0 indicates no approximation + * + * @return Vector of results for each model + */ + std::vector<ValueType> checkModel(std::vector<storm::storage::DFT<ValueType>> const& dfts, std::shared_ptr<const storm::logic::Formula> const& formula, bool symred, bool enableDC, double approximationError = 0.0); + + /** + * Build markov models from DFTs. + * + * @param dfts Vector of Dfts + * @param formula Formula to check for + * @param symred Flag indicating if symmetry reduction should be used + * @param enableDC Flag indicating if dont care propagation should be used + * @param approximationError Error allowed for approximation. Value 0 indicates no approximation + * + * @return Vector of markov models corresponding to DFTs. + */ + std::vector<std::shared_ptr<storm::models::sparse::Model<ValueType>>> buildMarkovModels(std::vector<storm::storage::DFT<ValueType>> const& dfts, std::shared_ptr<const storm::logic::Formula> const& formula, bool symred, bool enableDC, double approximationError = 0.0); + + }; + } +} +#endif /* STORM_MODELCHECKER_DFT_DFTMODELCHECKER_H_ */ diff --git a/src/storm-dyftee.cpp b/src/storm-dyftee.cpp index 4cfd1f58f..89fc1da7b 100644 --- a/src/storm-dyftee.cpp +++ b/src/storm-dyftee.cpp @@ -1,12 +1,12 @@ -#include "logic/Formula.h" -#include "utility/initialize.h" -#include "utility/storm.h" -#include "modelchecker/DFTAnalyser.h" +#include "src/logic/Formula.h" +#include "src/utility/initialize.h" +#include "src/utility/storm.h" +#include "src/parser/DFTGalileoParser.h" +#include "src/modelchecker/dft/DFTModelChecker.h" #include "src/cli/cli.h" #include "src/exceptions/BaseException.h" #include "src/utility/macros.h" #include "src/builder/DftSmtBuilder.h" -#include <boost/lexical_cast.hpp> #include "src/settings/modules/GeneralSettings.h" #include "src/settings/modules/DFTSettings.h" @@ -24,6 +24,8 @@ //#include "src/settings/modules/ParametricSettings.h" #include "src/settings/modules/SparseDtmcEliminationModelCheckerSettings.h" +#include <boost/lexical_cast.hpp> + /*! * Load DFT from filename, build corresponding Model and check against given property. * @@ -39,10 +41,10 @@ void analyzeDFT(std::string filename, std::string property, bool symred = false, std::vector<std::shared_ptr<storm::logic::Formula const>> formulas = storm::parseFormulasForExplicit(property); STORM_LOG_ASSERT(formulas.size() == 1, "Wrong number of formulas."); - DFTAnalyser<ValueType> analyser; - analyser.check(dft, formulas[0], symred, allowModularisation, enableDC); - analyser.printTimings(); - analyser.printResult(); + storm::modelchecker::DFTModelChecker<ValueType> modelChecker; + modelChecker.check(dft, formulas[0], symred, allowModularisation, enableDC); + modelChecker.printTimings(); + modelChecker.printResult(); } template<typename ValueType> From d3d360b50a6c216f6a9a903558e91ea52e86326d Mon Sep 17 00:00:00 2001 From: Mavo <matthias.volk@rwth-aachen.de> Date: Fri, 30 Sep 2016 17:55:16 +0200 Subject: [PATCH 13/65] First version of DFT approximation Former-commit-id: 1d95ad491438283366cbcb19bdb0a2c46f95d791 --- examples/dft/approx.dft | 5 + src/builder/ExplicitDFTModelBuilderApprox.cpp | 172 ++++++--- src/builder/ExplicitDFTModelBuilderApprox.h | 67 +++- src/generator/DftNextStateGenerator.cpp | 83 +++-- src/generator/DftNextStateGenerator.h | 23 +- src/modelchecker/dft/DFTModelChecker.cpp | 347 +++++++++++------- src/modelchecker/dft/DFTModelChecker.h | 66 ++-- src/settings/modules/DFTSettings.cpp | 18 +- src/settings/modules/DFTSettings.h | 20 +- src/storage/dft/DFTState.cpp | 10 +- src/storage/dft/DFTState.h | 17 + src/storm-dyftee.cpp | 20 +- 12 files changed, 566 insertions(+), 282 deletions(-) create mode 100644 examples/dft/approx.dft diff --git a/examples/dft/approx.dft b/examples/dft/approx.dft new file mode 100644 index 000000000..38c7faba0 --- /dev/null +++ b/examples/dft/approx.dft @@ -0,0 +1,5 @@ +toplevel "A"; +"A" and "B" "C" "D"; +"B" lambda=0.1 dorm=0; +"C" lambda=10 dorm=0; +"D" lambda=0.1 dorm=0; diff --git a/src/builder/ExplicitDFTModelBuilderApprox.cpp b/src/builder/ExplicitDFTModelBuilderApprox.cpp index 9ff34b734..74c448c25 100644 --- a/src/builder/ExplicitDFTModelBuilderApprox.cpp +++ b/src/builder/ExplicitDFTModelBuilderApprox.cpp @@ -12,28 +12,29 @@ namespace storm { namespace builder { - template <typename ValueType, typename StateType> + template<typename ValueType, typename StateType> ExplicitDFTModelBuilderApprox<ValueType, StateType>::ModelComponents::ModelComponents() : transitionMatrix(), stateLabeling(), markovianStates(), exitRates(), choiceLabeling() { // Intentionally left empty. } - template <typename ValueType, typename StateType> + template<typename ValueType, typename StateType> ExplicitDFTModelBuilderApprox<ValueType, StateType>::ExplicitDFTModelBuilderApprox(storm::storage::DFT<ValueType> const& dft, storm::storage::DFTIndependentSymmetries const& symmetries, bool enableDC) : dft(dft), enableDC(enableDC), stateStorage(((dft.stateVectorSize() / 64) + 1) * 64) { // stateVectorSize is bound for size of bitvector - stateGenerationInfo = std::make_shared<storm::storage::DFTStateGenerationInfo>(dft.buildStateGenerationInfo(symmetries)); } - template <typename ValueType, typename StateType> - std::shared_ptr<storm::models::sparse::Model<ValueType>> ExplicitDFTModelBuilderApprox<ValueType, StateType>::buildModel(LabelOptions const& labelOpts) { + template<typename ValueType, typename StateType> + void ExplicitDFTModelBuilderApprox<ValueType, StateType>::buildModel(LabelOptions const& labelOpts, double approximationError) { STORM_LOG_TRACE("Generating DFT state space"); // Initialize StateType currentRowGroup = 0; StateType currentRow = 0; + size_t pseudoStatesToCheck = 0; modelComponents.markovianStates = storm::storage::BitVector(INITIAL_BITVECTOR_SIZE); // Create generator storm::generator::DftNextStateGenerator<ValueType, StateType> generator(dft, *stateGenerationInfo, enableDC, mergeFailedStates); + generator.setApproximationError(approximationError); // Create sparse matrix builder storm::storage::SparseMatrixBuilder<ValueType> transitionMatrixBuilder(0, 0, 0, false, !generator.isDeterministicModel(), 0); @@ -87,41 +88,55 @@ namespace storm { // Remember that this row group was actually filled with the transitions of a different state setRemapping(currentState->getId(), currentRowGroup); - // Explore state - generator.load(currentState); - storm::generator::StateBehavior<ValueType, StateType> behavior = generator.expand(stateToIdCallback); - - STORM_LOG_ASSERT(!behavior.empty(), "Behavior is empty."); - setMarkovian(currentRowGroup, behavior.begin()->isMarkovian()); - // If the model is nondeterministic, we need to open a row group. if (!generator.isDeterministicModel()) { transitionMatrixBuilder.newRowGroup(currentRow); } - // Now add all choices. - for (auto const& choice : behavior) { - // Add the probabilistic behavior to the matrix. - for (auto const& stateProbabilityPair : choice) { - - // Check that pseudo state and its instantiation do not appear together - // TODO Matthias: prove that this is not possible and remove - if (stateProbabilityPair.first >= OFFSET_PSEUDO_STATE) { - StateType newId = stateProbabilityPair.first - OFFSET_PSEUDO_STATE; - STORM_LOG_ASSERT(newId < mPseudoStatesMapping.size(), "Id is not valid."); - if (mPseudoStatesMapping[newId].first > 0) { - // State exists already - newId = mPseudoStatesMapping[newId].first; - for (auto itFind = choice.begin(); itFind != choice.end(); ++itFind) { - STORM_LOG_ASSERT(itFind->first != newId, "Pseudo state and instantiation occur together in a distribution."); + // Try to explore the next state + generator.load(currentState); + + if (currentState->isSkip()) { + // Skip the current state + STORM_LOG_TRACE("Skip expansion of state: " << dft.getStateString(currentState)); + setMarkovian(currentRowGroup, true); + // Add transition to target state with temporary value 0 + // TODO Matthias: what to do when there is no unique target state? + transitionMatrixBuilder.addNextValue(currentRow, failedStateId, storm::utility::zero<ValueType>()); + // Remember skipped state + skippedStates[currentRow] = currentState; + ++currentRow; + } else { + // Explore the current state + storm::generator::StateBehavior<ValueType, StateType> behavior = generator.expand(stateToIdCallback); + STORM_LOG_ASSERT(!behavior.empty(), "Behavior is empty."); + setMarkovian(currentRowGroup, behavior.begin()->isMarkovian()); + + // Now add all choices. + for (auto const& choice : behavior) { + // Add the probabilistic behavior to the matrix. + for (auto const& stateProbabilityPair : choice) { + + // Check that pseudo state and its instantiation do not appear together + // TODO Matthias: prove that this is not possible and remove + if (stateProbabilityPair.first >= OFFSET_PSEUDO_STATE) { + StateType newId = stateProbabilityPair.first - OFFSET_PSEUDO_STATE; + STORM_LOG_ASSERT(newId < mPseudoStatesMapping.size(), "Id is not valid."); + if (mPseudoStatesMapping[newId].first > 0) { + // State exists already + newId = mPseudoStatesMapping[newId].first; + for (auto itFind = choice.begin(); itFind != choice.end(); ++itFind) { + STORM_LOG_ASSERT(itFind->first != newId, "Pseudo state and instantiation occur together in a distribution."); + } } } - } - transitionMatrixBuilder.addNextValue(currentRow, stateProbabilityPair.first, stateProbabilityPair.second); + transitionMatrixBuilder.addNextValue(currentRow, stateProbabilityPair.first, stateProbabilityPair.second); + } + ++currentRow; } - ++currentRow; } + ++currentRowGroup; if (statesToExplore.empty()) { @@ -149,6 +164,7 @@ namespace storm { size_t stateSize = stateStorage.getNumberOfStates() + (mergeFailedStates ? 1 : 0); modelComponents.markovianStates.resize(stateSize); + modelComponents.deterministicModel = generator.isDeterministicModel(); // Replace pseudo states in matrix // TODO Matthias: avoid hack with fixed int type @@ -169,8 +185,8 @@ namespace storm { STORM_LOG_TRACE("State remapping: " << stateRemapping); STORM_LOG_TRACE("Markovian states: " << modelComponents.markovianStates); - STORM_LOG_DEBUG("Generated " << stateSize << " states"); + STORM_LOG_DEBUG("Skipped " << skippedStates.size() << " states"); STORM_LOG_DEBUG("Model is " << (generator.isDeterministicModel() ? "deterministic" : "non-deterministic")); // Build transition matrix @@ -222,18 +238,42 @@ namespace storm { } } } + } - std::shared_ptr<storm::models::sparse::Model<ValueType>> model; + template<typename ValueType, typename StateType> + std::shared_ptr<storm::models::sparse::Model<ValueType>> ExplicitDFTModelBuilderApprox<ValueType, StateType>::getModel() { + STORM_LOG_ASSERT(skippedStates.size() == 0, "Concrete model has skipped states"); + return createModel(false); + } + + template<typename ValueType, typename StateType> + std::shared_ptr<storm::models::sparse::Model<ValueType>> ExplicitDFTModelBuilderApprox<ValueType, StateType>::getModelApproximation(bool lowerBound) { + // TODO Matthias: handle case with no skipped states + if (lowerBound) { + changeMatrixLowerBound(modelComponents.transitionMatrix); + } else { + changeMatrixUpperBound(modelComponents.transitionMatrix); + } + return createModel(true); + } - if (generator.isDeterministicModel()) { + template<typename ValueType, typename StateType> + std::shared_ptr<storm::models::sparse::Model<ValueType>> ExplicitDFTModelBuilderApprox<ValueType, StateType>::createModel(bool copy) { + if (modelComponents.deterministicModel) { // Build CTMC - model = std::make_shared<storm::models::sparse::Ctmc<ValueType>>(std::move(modelComponents.transitionMatrix), std::move(modelComponents.stateLabeling)); + if (copy) { + return std::make_shared<storm::models::sparse::Ctmc<ValueType>>(modelComponents.transitionMatrix, modelComponents.stateLabeling); + + } else { + return std::make_shared<storm::models::sparse::Ctmc<ValueType>>(std::move(modelComponents.transitionMatrix), std::move(modelComponents.stateLabeling)); + } } else { // Build MA // Compute exit rates - modelComponents.exitRates = std::vector<ValueType>(stateSize); + // TODO Matthias: avoid computing multiple times + modelComponents.exitRates = std::vector<ValueType>(modelComponents.markovianStates.size()); std::vector<typename storm::storage::SparseMatrix<ValueType>::index_type> indices = modelComponents.transitionMatrix.getRowGroupIndices(); - for (StateType stateIndex = 0; stateIndex < stateSize; ++stateIndex) { + for (StateType stateIndex = 0; stateIndex < modelComponents.markovianStates.size(); ++stateIndex) { if (modelComponents.markovianStates[stateIndex]) { modelComponents.exitRates[stateIndex] = modelComponents.transitionMatrix.getRowSum(indices[stateIndex]); } else { @@ -242,36 +282,52 @@ namespace storm { } STORM_LOG_TRACE("Exit rates: " << modelComponents.exitRates); - std::shared_ptr<storm::models::sparse::MarkovAutomaton<ValueType>> ma = std::make_shared<storm::models::sparse::MarkovAutomaton<ValueType>>(std::move(modelComponents.transitionMatrix), std::move(modelComponents.stateLabeling), std::move(modelComponents.markovianStates), std::move(modelComponents.exitRates)); + std::shared_ptr<storm::models::sparse::MarkovAutomaton<ValueType>> ma; + if (copy) { + ma = std::make_shared<storm::models::sparse::MarkovAutomaton<ValueType>>(modelComponents.transitionMatrix, modelComponents.stateLabeling, modelComponents.markovianStates, modelComponents.exitRates); + } else { + ma = std::make_shared<storm::models::sparse::MarkovAutomaton<ValueType>>(std::move(modelComponents.transitionMatrix), std::move(modelComponents.stateLabeling), std::move(modelComponents.markovianStates), std::move(modelComponents.exitRates)); + } if (ma->hasOnlyTrivialNondeterminism()) { // Markov automaton can be converted into CTMC - model = ma->convertToCTMC(); + return ma->convertToCTMC(); } else { - model = ma; + return ma; } } - - return model; - } - - template<> - bool belowThreshold(double const& number) { - return number < 0.1; + + template<typename ValueType, typename StateType> + void ExplicitDFTModelBuilderApprox<ValueType, StateType>::changeMatrixLowerBound(storm::storage::SparseMatrix<ValueType> & matrix) const { + // Set lower bound for skipped states + for (auto it = skippedStates.begin(); it != skippedStates.end(); ++it) { + auto matrixEntry = matrix.getRow(it->first).begin(); + STORM_LOG_ASSERT(matrixEntry->getColumn() == failedStateId, "Transition has wrong target state."); + // Get the lower bound by considering the failure of the BE with the highest rate + // The list is sorted by rate, therefore we consider the first BE for the highest failure rate + matrixEntry->setValue(it->second->getFailableBERate(0)); + } } - template<> - bool belowThreshold(storm::RationalFunction const& number) { - storm::RationalFunction threshold = storm::utility::one<storm::RationalFunction>() / 10; - std::cout << number << " < " << threshold << ": " << (number < threshold) << std::endl; - std::map<storm::Variable, storm::RationalNumber> mapping; - - storm::RationalFunction eval(number.evaluate(mapping)); - std::cout << "Evaluated: " << eval << std::endl; - return eval < threshold; + template<typename ValueType, typename StateType> + void ExplicitDFTModelBuilderApprox<ValueType, StateType>::changeMatrixUpperBound(storm::storage::SparseMatrix<ValueType> & matrix) const { + // Set uppper bound for skipped states + for (auto it = skippedStates.begin(); it != skippedStates.end(); ++it) { + auto matrixEntry = matrix.getRow(it->first).begin(); + STORM_LOG_ASSERT(matrixEntry->getColumn() == failedStateId, "Transition has wrong target state."); + // Get the upper bound by considering the failure of all BEs + // The used formula for the rate is 1/( 1/a + 1/b + ...) + // TODO Matthias: improve by using closed formula for AND of all BEs + ValueType newRate = storm::utility::zero<ValueType>(); + for (size_t index = 0; index < it->second->nrFailableBEs(); ++index) { + newRate += storm::utility::one<ValueType>() / it->second->getFailableBERate(index); + } + newRate = storm::utility::one<ValueType>() / newRate; + matrixEntry->setValue(newRate); + } } - template <typename ValueType, typename StateType> + template<typename ValueType, typename StateType> StateType ExplicitDFTModelBuilderApprox<ValueType, StateType>::getOrAddStateIndex(DFTStatePointer const& state) { StateType stateId; bool changed = false; @@ -329,7 +385,7 @@ namespace storm { return stateId; } - template <typename ValueType, typename StateType> + template<typename ValueType, typename StateType> void ExplicitDFTModelBuilderApprox<ValueType, StateType>::setMarkovian(StateType id, bool markovian) { if (id >= modelComponents.markovianStates.size()) { // Resize BitVector @@ -338,7 +394,7 @@ namespace storm { modelComponents.markovianStates.set(id, markovian); } - template <typename ValueType, typename StateType> + template<typename ValueType, typename StateType> void ExplicitDFTModelBuilderApprox<ValueType, StateType>::setRemapping(StateType id, StateType mappedId) { STORM_LOG_ASSERT(id < stateRemapping.size(), "Invalid index for remapping."); stateRemapping[id] = mappedId; diff --git a/src/builder/ExplicitDFTModelBuilderApprox.h b/src/builder/ExplicitDFTModelBuilderApprox.h index 247b66ca1..fa529f221 100644 --- a/src/builder/ExplicitDFTModelBuilderApprox.h +++ b/src/builder/ExplicitDFTModelBuilderApprox.h @@ -48,6 +48,9 @@ namespace storm { // A vector that stores a labeling for each choice. boost::optional<std::vector<boost::container::flat_set<StateType>>> choiceLabeling; + + // A flag indicating if the model is deterministic. + bool deterministicModel; }; public: @@ -68,13 +71,28 @@ namespace storm { ExplicitDFTModelBuilderApprox(storm::storage::DFT<ValueType> const& dft, storm::storage::DFTIndependentSymmetries const& symmetries, bool enableDC); /*! - * Build model from Dft. + * Build model from DFT. * - * @param labelOpts Options for labeling. + * @param labelOpts Options for labeling. + * @param approximationError Error allowed for approximation. + */ + void buildModel(LabelOptions const& labelOpts, double approximationError = 0.0); + + /*! + * Get the built model. * - * @return Built model (either MA or CTMC). + * @return The model built from the DFT. */ - std::shared_ptr<storm::models::sparse::Model<ValueType>> buildModel(LabelOptions const& labelOpts); + std::shared_ptr<storm::models::sparse::Model<ValueType>> getModel(); + + /*! + * Get the built approximation model for either the lower or upper bound. + * + * @param lowerBound If true, the lower bound model is returned, else the upper bound model + * + * @return The model built from the DFT. + */ + std::shared_ptr<storm::models::sparse::Model<ValueType>> getModelApproximation(bool lowerBound = true); private: @@ -103,6 +121,31 @@ namespace storm { */ void setRemapping(StateType id, StateType mappedId); + /** + * Change matrix to reflect the lower approximation bound. + * + * @param matrix Matrix to change. The change are reflected here. + */ + void changeMatrixLowerBound(storm::storage::SparseMatrix<ValueType> & matrix) const; + + /*! + * Change matrix to reflect the upper approximation bound. + * + * @param matrix Matrix to change. The change are reflected here. + */ + void changeMatrixUpperBound(storm::storage::SparseMatrix<ValueType> & matrix) const; + + /*! + * Create the model model from the model components. + * + * @param copy If true, all elements of the model component are copied (used for approximation). If false + * they are moved to reduce the memory overhead. + * + * @return The model built from the model components. + */ + std::shared_ptr<storm::models::sparse::Model<ValueType>> createModel(bool copy); + + // Initial size of the bitvector. const size_t INITIAL_BITVECTOR_SIZE = 20000; // Offset used for pseudo states. @@ -118,13 +161,17 @@ namespace storm { // Current id for new state size_t newIndex = 0; - //TODO Matthias: remove when everything works - std::vector<std::pair<StateType, storm::storage::BitVector>> mPseudoStatesMapping; // vector of (id to concrete state, bitvector) + // Mapping from pseudo states to (id of concrete state, bitvector) + std::vector<std::pair<StateType, storm::storage::BitVector>> mPseudoStatesMapping; + //TODO Matthias: make changeable const bool mergeFailedStates = true; + + // Id of failed state size_t failedStateId = 0; + + // Id of initial state size_t initialStateIndex = 0; - size_t pseudoStatesToCheck = 0; // Flag indication if dont care propagation should be used. bool enableDC = true; @@ -142,10 +189,10 @@ namespace storm { // TODO Matthias: avoid hack with fixed int type std::vector<uint_fast64_t> stateRemapping; + // Holds all skipped states which were not yet expanded. More concrete it is a mapping from matrix indices + // to the corresponding skipped state. + std::unordered_map<StateType, DFTStatePointer> skippedStates; }; - - template<typename ValueType> - bool belowThreshold(ValueType const& number); } } diff --git a/src/generator/DftNextStateGenerator.cpp b/src/generator/DftNextStateGenerator.cpp index 1f39fe2cf..f468c673e 100644 --- a/src/generator/DftNextStateGenerator.cpp +++ b/src/generator/DftNextStateGenerator.cpp @@ -8,7 +8,7 @@ namespace storm { namespace generator { template<typename ValueType, typename StateType> - DftNextStateGenerator<ValueType, StateType>::DftNextStateGenerator(storm::storage::DFT<ValueType> const& dft, storm::storage::DFTStateGenerationInfo const& stateGenerationInfo, bool enableDC, bool mergeFailedStates) : mDft(dft), mStateGenerationInfo(stateGenerationInfo), state(nullptr), enableDC(enableDC), mergeFailedStates(mergeFailedStates), comparator() { + DftNextStateGenerator<ValueType, StateType>::DftNextStateGenerator(storm::storage::DFT<ValueType> const& dft, storm::storage::DFTStateGenerationInfo const& stateGenerationInfo, bool enableDC, bool mergeFailedStates) : mDft(dft), mStateGenerationInfo(stateGenerationInfo), state(nullptr), enableDC(enableDC), mergeFailedStates(mergeFailedStates) { deterministicModel = !mDft.canHaveNondeterminism(); } @@ -60,7 +60,7 @@ namespace storm { Choice<ValueType, StateType> choice(0, !hasDependencies); // Check for absorbing state - if (mDft.hasFailed(state) || mDft.isFailsafe(state) || state->nrFailableBEs() == 0) { + if (mDft.hasFailed(state) || mDft.isFailsafe(state) || failableCount == 0) { // Add self loop choice.addProbability(state->getId(), storm::utility::one<ValueType>()); STORM_LOG_TRACE("Added self loop for " << state->getId()); @@ -82,30 +82,6 @@ namespace storm { STORM_LOG_ASSERT(nextBEPair.second == hasDependencies, "Failure due to dependencies does not match."); STORM_LOG_TRACE("With the failure of: " << nextBE->name() << " [" << nextBE->id() << "] in " << mDft.getStateString(state)); - /*if (storm::settings::getModule<storm::settings::modules::DFTSettings>().computeApproximation()) { - if (!storm::utility::isZero(exitRate)) { - ValueType rate = nextBE->activeFailureRate(); - ValueType div = rate / exitRate; - if (!storm::utility::isZero(exitRate) && belowThreshold(div)) { - // Set transition directly to failed state - auto resultFind = outgoingRates.find(failedIndex); - if (resultFind != outgoingRates.end()) { - // Add to existing transition - resultFind->second += rate; - STORM_LOG_TRACE("Updated transition to " << resultFind->first << " with rate " << rate << " to new rate " << resultFind->second); - } else { - // Insert new transition - outgoingRates.insert(std::make_pair(failedIndex, rate)); - STORM_LOG_TRACE("Added transition to " << failedIndex << " with rate " << rate); - } - exitRate += rate; - std::cout << "IGNORE: " << nextBE->name() << " [" << nextBE->id() << "] with rate " << rate << std::endl; - //STORM_LOG_TRACE("Ignore: " << nextBE->name() << " [" << nextBE->id() << "] with rate " << rate); - continue; - } - } - }*/ - // Propagate storm::storage::DFTStateSpaceGenerationQueues<ValueType> queues; @@ -139,8 +115,8 @@ namespace storm { continue; } + // Get the id of the successor state StateType newStateId; - if (newState->hasFailed(mDft.getTopLevelIndex()) && mergeFailedStates) { // Use unique failed state newStateId = mergeFailedStateId; @@ -168,17 +144,17 @@ namespace storm { // Set transitions if (hasDependencies) { // Failure is due to dependency -> add non-deterministic choice - std::shared_ptr<storm::storage::DFTDependency<ValueType> const> dependency = mDft.getDependency(state->getDependencyId(currentFailable)); - choice.addProbability(newStateId, dependency->probability()); - STORM_LOG_TRACE("Added transition to " << newStateId << " with probability " << dependency->probability()); + ValueType probability = mDft.getDependency(state->getDependencyId(currentFailable))->probability(); + choice.addProbability(newStateId, probability); + STORM_LOG_TRACE("Added transition to " << newStateId << " with probability " << probability); - if (!storm::utility::isOne(dependency->probability())) { + if (!storm::utility::isOne(probability)) { // Add transition to state where dependency was unsuccessful DFTStatePointer unsuccessfulState = std::make_shared<storm::storage::DFTState<ValueType>>(*state); unsuccessfulState->letDependencyBeUnsuccessful(currentFailable); // Add state StateType unsuccessfulStateId = stateToIdCallback(unsuccessfulState); - ValueType remainingProbability = storm::utility::one<ValueType>() - dependency->probability(); + ValueType remainingProbability = storm::utility::one<ValueType>() - probability; choice.addProbability(unsuccessfulStateId, remainingProbability); STORM_LOG_TRACE("Added transition to " << unsuccessfulStateId << " with remaining probability " << remainingProbability); } @@ -188,14 +164,21 @@ namespace storm { // Set failure rate according to activation bool isActive = true; if (mDft.hasRepresentant(nextBE->id())) { - // Active must be checked for the state we are coming from as this state is responsible for the - // rate and not the new state we are going to + // Active must be checked for the state we are coming from as this state is responsible for the rate isActive = state->isActive(mDft.getRepresentant(nextBE->id())->id()); } ValueType rate = isActive ? nextBE->activeFailureRate() : nextBE->passiveFailureRate(); STORM_LOG_ASSERT(!storm::utility::isZero(rate), "Rate is 0."); choice.addProbability(newStateId, rate); - STORM_LOG_TRACE("Added transition to " << newStateId << " with " << (isActive ? "active" : "passive") << " rate " << rate); + STORM_LOG_TRACE("Added transition to " << newStateId << " with " << (isActive ? "active" : "passive") << " failure rate " << rate); + + // Check if new state needs expansion for approximation + if (approximationError > 0.0) { + if (checkSkipState(newState, rate, choice.getTotalMass(), approximationError)) { + STORM_LOG_TRACE("Will skip state " << newStateId); + newState->markSkip(); + } + } } ++currentFailable; @@ -229,6 +212,36 @@ namespace storm { result.setExpanded(); return result; } + + template<typename ValueType, typename StateType> + void DftNextStateGenerator<ValueType, StateType>::setApproximationError(double approximationError) { + this->approximationError = approximationError; + } + + template<typename ValueType, typename StateType> + bool DftNextStateGenerator<ValueType, StateType>::checkSkipState(DFTStatePointer const& state, ValueType rate, ValueType exitRate, double approximationError) { + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Approximation works only for double."); + } + + template<> + bool DftNextStateGenerator<double>::checkSkipState(DFTStatePointer const& state, double rate, double exitRate, double approximationError) { + if (mDft.hasFailed(state) || mDft.isFailsafe(state) || (state->nrFailableDependencies() == 0 && state->nrFailableBEs() == 0)) { + // Do not skip absorbing state + return false; + } + // Skip if the rate to reach this state is low compared to all other outgoing rates from the predecessor + return rate/exitRate < approximationError; + } + + template<> + bool DftNextStateGenerator<storm::RationalFunction>::checkSkipState(DFTStatePointer const& state, storm::RationalFunction rate, storm::RationalFunction exitRate, double approximationError) { + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Approximation works only for double."); + /*std::cout << (rate / exitRate) << " < " << threshold << ": " << (number < threshold) << std::endl; + std::map<storm::Variable, storm::RationalNumber> mapping; + storm::RationalFunction eval(number.evaluate(mapping)); + std::cout << "Evaluated: " << eval << std::endl; + return eval < threshold;*/ + } template class DftNextStateGenerator<double>; template class DftNextStateGenerator<storm::RationalFunction>; diff --git a/src/generator/DftNextStateGenerator.h b/src/generator/DftNextStateGenerator.h index beab8f5f0..7c9e1bb48 100644 --- a/src/generator/DftNextStateGenerator.h +++ b/src/generator/DftNextStateGenerator.h @@ -42,6 +42,13 @@ namespace storm { */ StateBehavior<ValueType, StateType> createMergeFailedState(StateToIdCallback const& stateToIdCallback); + /*! + * Set a new value for the allowed approximation error. + * + * @param approximationError Allowed approximation error. + */ + void setApproximationError(double approximationError); + private: // The dft used for the generation of next states. @@ -65,8 +72,20 @@ namespace storm { // Flag indicating if the model is deterministic. bool deterministicModel = true; - // A comparator used to compare constants. - storm::utility::ConstantsComparator<ValueType> comparator; + // Allowed approximation error. + double approximationError = 0.0; + + /*! + * Check if the given state should be skipped for expansion. + * + * @param state State to check for expansion + * @param rate Rate of current state + * @param exitRate Exit rates of all outgoing transitions + * @param approximationError Allowed approximation error + * + * @return True, if the given state should be skipped. + */ + bool checkSkipState(DFTStatePointer const& state, ValueType rate, ValueType exitRate, double approximationError); }; } diff --git a/src/modelchecker/dft/DFTModelChecker.cpp b/src/modelchecker/dft/DFTModelChecker.cpp index 3aa3ed27b..1c0772bf8 100644 --- a/src/modelchecker/dft/DFTModelChecker.cpp +++ b/src/modelchecker/dft/DFTModelChecker.cpp @@ -11,158 +11,213 @@ namespace storm { template<typename ValueType> DFTModelChecker<ValueType>::DFTModelChecker() { - // Intentionally left empty. + checkResult = storm::utility::zero<ValueType>(); } template<typename ValueType> - void DFTModelChecker<ValueType>::check(storm::storage::DFT<ValueType> const& origDft, std::shared_ptr<const storm::logic::Formula> const& formula, bool symred, bool allowModularisation, bool enableDC) { + void DFTModelChecker<ValueType>::check(storm::storage::DFT<ValueType> const& origDft, std::shared_ptr<const storm::logic::Formula> const& formula, bool symred, bool allowModularisation, bool enableDC, double approximationError) { + // Initialize + this->buildingTime = std::chrono::duration<double>::zero(); + this->explorationTime = std::chrono::duration<double>::zero(); + this->bisimulationTime = std::chrono::duration<double>::zero(); + this->modelCheckingTime = std::chrono::duration<double>::zero(); + this->totalTime = std::chrono::duration<double>::zero(); + this->approximationError = approximationError; std::chrono::high_resolution_clock::time_point totalStart = std::chrono::high_resolution_clock::now(); + // Optimizing DFT storm::storage::DFT<ValueType> dft = origDft.optimize(); - checkResult = checkHelper(dft, formula, symred, allowModularisation, enableDC); - totalTime = std::chrono::high_resolution_clock::now() - totalStart; + + // TODO Matthias: check that all paths reach the target state! + + // Checking DFT + checkResult = checkHelper(dft, formula, symred, allowModularisation, enableDC, approximationError); + this->totalTime = std::chrono::high_resolution_clock::now() - totalStart; } template<typename ValueType> - ValueType DFTModelChecker<ValueType>::checkHelper(storm::storage::DFT<ValueType> const& dft, std::shared_ptr<const storm::logic::Formula> const& formula, bool symred, bool allowModularisation, bool enableDC) { + typename DFTModelChecker<ValueType>::dft_result DFTModelChecker<ValueType>::checkHelper(storm::storage::DFT<ValueType> const& dft, std::shared_ptr<const storm::logic::Formula> const& formula, bool symred, bool allowModularisation, bool enableDC, double approximationError) { STORM_LOG_TRACE("Check helper called"); - bool invResults = false; - std::vector<storm::storage::DFT<ValueType>> dfts = {dft}; - std::vector<ValueType> res; - size_t nrK = 0; // K out of M - size_t nrM = 0; // K out of M + bool modularisationPossible = allowModularisation; // Try modularisation - if(allowModularisation) { - if(dft.topLevelType() == storm::storage::DFTElementType::AND) { - STORM_LOG_TRACE("top modularisation called AND"); - dfts = dft.topModularisation(); - STORM_LOG_TRACE("Modularsation into " << dfts.size() << " submodules."); - nrK = dfts.size(); - nrM = dfts.size(); - } - if(dft.topLevelType() == storm::storage::DFTElementType::OR) { - STORM_LOG_TRACE("top modularisation called OR"); - dfts = dft.topModularisation(); - STORM_LOG_TRACE("Modularsation into " << dfts.size() << " submodules."); - nrK = 0; - nrM = dfts.size(); - invResults = true; - } - if(dft.topLevelType() == storm::storage::DFTElementType::VOT) { - STORM_LOG_TRACE("top modularisation called VOT"); - dfts = dft.topModularisation(); - STORM_LOG_TRACE("Modularsation into " << dfts.size() << " submodules."); - nrK = std::static_pointer_cast<storm::storage::DFTVot<ValueType> const>(dft.getTopLevelGate())->threshold(); - nrM = dfts.size(); - if(nrK <= nrM/2) { - nrK -= 1; + if(modularisationPossible) { + bool invResults = false; + std::vector<storm::storage::DFT<ValueType>> dfts; + size_t nrK = 0; // K out of M + size_t nrM = 0; // K out of M + + switch (dft.topLevelType()) { + case storm::storage::DFTElementType::AND: + STORM_LOG_TRACE("top modularisation called AND"); + dfts = dft.topModularisation(); + STORM_LOG_TRACE("Modularsation into " << dfts.size() << " submodules."); + nrK = dfts.size(); + nrM = dfts.size(); + modularisationPossible = dfts.size() > 1; + break; + case storm::storage::DFTElementType::OR: + STORM_LOG_TRACE("top modularisation called OR"); + dfts = dft.topModularisation(); + STORM_LOG_TRACE("Modularsation into " << dfts.size() << " submodules."); + nrK = 0; + nrM = dfts.size(); invResults = true; - } + modularisationPossible = dfts.size() > 1; + break; + case storm::storage::DFTElementType::VOT: + STORM_LOG_TRACE("top modularisation called VOT"); + dfts = dft.topModularisation(); + STORM_LOG_TRACE("Modularsation into " << dfts.size() << " submodules."); + nrK = std::static_pointer_cast<storm::storage::DFTVot<ValueType> const>(dft.getTopLevelGate())->threshold(); + nrM = dfts.size(); + if(nrK <= nrM/2) { + nrK -= 1; + invResults = true; + } + modularisationPossible = dfts.size() > 1; + break; + default: + // No static gate -> no modularisation applicable + modularisationPossible = false; + break; } - if(dfts.size() > 1) { + + if(modularisationPossible) { STORM_LOG_TRACE("Recursive CHECK Call"); + // TODO Matthias: enable modularisation for approximation + STORM_LOG_ASSERT(approximationError == 0.0, "Modularisation not possible for approximation."); + + // Recursively call model checking + std::vector<ValueType> res; for(auto const ft : dfts) { - res.push_back(checkHelper(ft, formula, symred, allowModularisation, enableDC)); + dft_result ftResult = checkHelper(ft, formula, symred, true, enableDC, 0.0); + res.push_back(boost::get<ValueType>(ftResult)); } - } - } - if(res.empty()) { - // Model based modularisation. - auto const& models = buildMarkovModels(dfts, formula, symred, enableDC); - - for (auto const& model : models) { - // Model checking - STORM_LOG_INFO("Model checking..."); - std::chrono::high_resolution_clock::time_point modelCheckingStart = std::chrono::high_resolution_clock::now(); - std::unique_ptr<storm::modelchecker::CheckResult> result(storm::verifySparseModel(model, formula)); - STORM_LOG_INFO("Model checking done."); - STORM_LOG_ASSERT(result, "Result does not exist."); - result->filter(storm::modelchecker::ExplicitQualitativeCheckResult(model->getInitialStates())); - modelCheckingTime += std::chrono::high_resolution_clock::now() - modelCheckingStart; - res.push_back(result->asExplicitQuantitativeCheckResult<ValueType>().getValueMap().begin()->second); + // Combine modularisation results + STORM_LOG_TRACE("Combining all results... K=" << nrK << "; M=" << nrM << "; invResults=" << (invResults?"On":"Off")); + ValueType result = storm::utility::zero<ValueType>(); + int limK = invResults ? -1 : nrM+1; + int chK = invResults ? -1 : 1; + for(int cK = nrK; cK != limK; cK += chK ) { + STORM_LOG_ASSERT(cK >= 0, "ck negative."); + size_t permutation = smallestIntWithNBitsSet(static_cast<size_t>(cK)); + do { + STORM_LOG_TRACE("Permutation="<<permutation); + ValueType permResult = storm::utility::one<ValueType>(); + for(size_t i = 0; i < res.size(); ++i) { + if(permutation & (1 << i)) { + permResult *= res[i]; + } else { + permResult *= storm::utility::one<ValueType>() - res[i]; + } + } + STORM_LOG_TRACE("Result for permutation:"<<permResult); + permutation = nextBitPermutation(permutation); + result += permResult; + } while(permutation < (1 << nrM) && permutation != 0); + } + if(invResults) { + result = storm::utility::one<ValueType>() - result; + } + return result; } } - if(nrM <= 1) { - // No modularisation done. - STORM_LOG_ASSERT(res.size()==1, "Result size not 1."); - return res[0]; - } - STORM_LOG_TRACE("Combining all results... K=" << nrK << "; M=" << nrM << "; invResults=" << (invResults?"On":"Off")); - ValueType result = storm::utility::zero<ValueType>(); - int limK = invResults ? -1 : nrM+1; - int chK = invResults ? -1 : 1; - for(int cK = nrK; cK != limK; cK += chK ) { - STORM_LOG_ASSERT(cK >= 0, "ck negative."); - size_t permutation = smallestIntWithNBitsSet(static_cast<size_t>(cK)); - do { - STORM_LOG_TRACE("Permutation="<<permutation); - ValueType permResult = storm::utility::one<ValueType>(); - for(size_t i = 0; i < res.size(); ++i) { - if(permutation & (1 << i)) { - permResult *= res[i]; - } else { - permResult *= storm::utility::one<ValueType>() - res[i]; - } - } - STORM_LOG_TRACE("Result for permutation:"<<permResult); - permutation = nextBitPermutation(permutation); - result += permResult; - } while(permutation < (1 << nrM) && permutation != 0); - } - if(invResults) { - return storm::utility::one<ValueType>() - result; - } - return result; + // If we are here, no modularisation was possible + STORM_LOG_ASSERT(!modularisationPossible, "Modularisation should not be possible."); + return checkDFT(dft, formula, symred, enableDC, approximationError); } template<typename ValueType> - std::vector<ValueType> DFTModelChecker<ValueType>::checkModel(std::vector<storm::storage::DFT<ValueType>> const& dfts, std::shared_ptr<const storm::logic::Formula> const& formula, bool symred, bool enableDC, double approximationError) { - std::vector<ValueType> results; + typename DFTModelChecker<ValueType>::dft_result DFTModelChecker<ValueType>::checkDFT(storm::storage::DFT<ValueType> const& dft, std::shared_ptr<const storm::logic::Formula> const& formula, bool symred, bool enableDC, double approximationError) { + std::chrono::high_resolution_clock::time_point buildingStart = std::chrono::high_resolution_clock::now(); - auto const& models = buildMarkovModels(dfts, formula, symred, enableDC); - - for (auto const& model : models) { - // Model checking - STORM_LOG_INFO("Model checking..."); - std::chrono::high_resolution_clock::time_point modelCheckingStart = std::chrono::high_resolution_clock::now(); - std::unique_ptr<storm::modelchecker::CheckResult> result(storm::verifySparseModel(model, formula)); - STORM_LOG_INFO("Model checking done."); - STORM_LOG_ASSERT(result, "Result does not exist."); - result->filter(storm::modelchecker::ExplicitQualitativeCheckResult(model->getInitialStates())); - modelCheckingTime += std::chrono::high_resolution_clock::now() - modelCheckingStart; - results.push_back(result->asExplicitQuantitativeCheckResult<ValueType>().getValueMap().begin()->second); + // Find symmetries + std::map<size_t, std::vector<std::vector<size_t>>> emptySymmetry; + storm::storage::DFTIndependentSymmetries symmetries(emptySymmetry); + if(symred) { + auto colouring = dft.colourDFT(); + symmetries = dft.findSymmetries(colouring); + STORM_LOG_INFO("Found " << symmetries.groups.size() << " symmetries."); + STORM_LOG_TRACE("Symmetries: " << std::endl << symmetries); } - return results; - } + std::chrono::high_resolution_clock::time_point buildingEnd = std::chrono::high_resolution_clock::now(); + buildingTime += buildingEnd - buildingStart; - template<typename ValueType> - std::vector<std::shared_ptr<storm::models::sparse::Model<ValueType>>> DFTModelChecker<ValueType>::buildMarkovModels(std::vector<storm::storage::DFT<ValueType>> const& dfts, std::shared_ptr<const storm::logic::Formula> const& formula, bool symred, bool enableDC, double approximationError) { - std::vector<std::shared_ptr<storm::models::sparse::Model<ValueType>>> models; - for(auto& dft : dfts) { - std::chrono::high_resolution_clock::time_point buildingStart = std::chrono::high_resolution_clock::now(); - - std::map<size_t, std::vector<std::vector<size_t>>> emptySymmetry; - storm::storage::DFTIndependentSymmetries symmetries(emptySymmetry); - if(symred) { - auto colouring = dft.colourDFT(); - symmetries = dft.findSymmetries(colouring); - STORM_LOG_INFO("Found " << symmetries.groups.size() << " symmetries."); - STORM_LOG_TRACE("Symmetries: " << std::endl << symmetries); - } - std::chrono::high_resolution_clock::time_point buildingEnd = std::chrono::high_resolution_clock::now(); - buildingTime += buildingEnd - buildingStart; + if (approximationError > 0.0) { + // Build approximate Markov Automata for lower and upper bound + double currentApproximationError = approximationError; + approximation_result approxResult = std::make_pair(storm::utility::zero<ValueType>(), storm::utility::zero<ValueType>()); + std::chrono::high_resolution_clock::time_point explorationStart; + std::shared_ptr<storm::models::sparse::Model<ValueType>> model; + storm::builder::ExplicitDFTModelBuilderApprox<ValueType> builder(dft, symmetries, enableDC); + typename storm::builder::ExplicitDFTModelBuilderApprox<ValueType>::LabelOptions labeloptions; // TODO initialize this with the formula - // Building Markov Automaton + size_t iteration = 0; + do { + // Iteratively build finer models + // TODO Matthias: implement refinement + STORM_LOG_ASSERT(iteration < 1, "Iterative refinement not yet implemented."); + explorationStart = std::chrono::high_resolution_clock::now(); + STORM_LOG_INFO("Building model..."); + // TODO Matthias refine model using existing model and MC results + currentApproximationError = pow(0.1, iteration) * approximationError; + builder.buildModel(labeloptions, currentApproximationError); + // TODO Matthias: possible to do bisimulation on approximated model and not on concrete one? + + // Build model for lower bound + STORM_LOG_INFO("Getting model for lower bound..."); + model = builder.getModelApproximation(true); + //model->printModelInformationToStream(std::cout); + STORM_LOG_INFO("No. states (Explored): " << model->getNumberOfStates()); + STORM_LOG_INFO("No. transitions (Explored): " << model->getNumberOfTransitions()); + if (model->getNumberOfStates() <= 15) { + STORM_LOG_TRACE("Transition matrix: " << std::endl << model->getTransitionMatrix()); + } else { + STORM_LOG_TRACE("Transition matrix: too big to print"); + } + explorationTime += std::chrono::high_resolution_clock::now() - explorationStart; + // Check lower bound + std::unique_ptr<storm::modelchecker::CheckResult> result = checkModel(model, formula); + result->filter(storm::modelchecker::ExplicitQualitativeCheckResult(model->getInitialStates())); + approxResult.first = result->asExplicitQuantitativeCheckResult<ValueType>().getValueMap().begin()->second; + + // Build model for upper bound + STORM_LOG_INFO("Getting model for upper bound..."); + explorationStart = std::chrono::high_resolution_clock::now(); + model = builder.getModelApproximation(false); + //model->printModelInformationToStream(std::cout); + STORM_LOG_INFO("No. states (Explored): " << model->getNumberOfStates()); + STORM_LOG_INFO("No. transitions (Explored): " << model->getNumberOfTransitions()); + if (model->getNumberOfStates() <= 15) { + STORM_LOG_TRACE("Transition matrix: " << std::endl << model->getTransitionMatrix()); + } else { + STORM_LOG_TRACE("Transition matrix: too big to print"); + } + explorationTime += std::chrono::high_resolution_clock::now() - explorationStart; + // Check upper bound + result = checkModel(model, formula); + result->filter(storm::modelchecker::ExplicitQualitativeCheckResult(model->getInitialStates())); + approxResult.second = result->asExplicitQuantitativeCheckResult<ValueType>().getValueMap().begin()->second; + + ++iteration; + STORM_LOG_TRACE("Result after iteration " << iteration << ": (" << approxResult.first << ", " << approxResult.second << ")"); + } while (!isApproximationSufficient(approxResult.first, approxResult.second, approximationError)); + + STORM_LOG_INFO("Finished approximation after " << iteration << " iteration" << (iteration > 1 ? "s." : ".")); + return approxResult; + } else { + // Build a single Markov Automaton STORM_LOG_INFO("Building Model..."); std::shared_ptr<storm::models::sparse::Model<ValueType>> model; // TODO Matthias: use only one builder if everything works again - if (storm::settings::getModule<storm::settings::modules::DFTSettings>().computeApproximation()) { + if (approximationError >= 0.0) { storm::builder::ExplicitDFTModelBuilderApprox<ValueType> builder(dft, symmetries, enableDC); typename storm::builder::ExplicitDFTModelBuilderApprox<ValueType>::LabelOptions labeloptions; // TODO initialize this with the formula - model = builder.buildModel(labeloptions); + builder.buildModel(labeloptions); + model = builder.getModel(); } else { storm::builder::ExplicitDFTModelBuilder<ValueType> builder(dft, symmetries, enableDC); typename storm::builder::ExplicitDFTModelBuilder<ValueType>::LabelOptions labeloptions; // TODO initialize this with the formula @@ -171,21 +226,45 @@ namespace storm { //model->printModelInformationToStream(std::cout); STORM_LOG_INFO("No. states (Explored): " << model->getNumberOfStates()); STORM_LOG_INFO("No. transitions (Explored): " << model->getNumberOfTransitions()); - std::chrono::high_resolution_clock::time_point explorationEnd = std::chrono::high_resolution_clock::now(); - explorationTime += explorationEnd -buildingEnd; + explorationTime += std::chrono::high_resolution_clock::now() - buildingEnd; - // Bisimulation - if (model->isOfType(storm::models::ModelType::Ctmc) && storm::settings::getModule<storm::settings::modules::GeneralSettings>().isBisimulationSet()) { - STORM_LOG_INFO("Bisimulation..."); - model = storm::performDeterministicSparseBisimulationMinimization<storm::models::sparse::Ctmc<ValueType>>(model->template as<storm::models::sparse::Ctmc<ValueType>>(), {formula}, storm::storage::BisimulationType::Weak)->template as<storm::models::sparse::Ctmc<ValueType>>(); - //model->printModelInformationToStream(std::cout); - } + // Model checking + std::unique_ptr<storm::modelchecker::CheckResult> result = checkModel(model, formula); + result->filter(storm::modelchecker::ExplicitQualitativeCheckResult(model->getInitialStates())); + return result->asExplicitQuantitativeCheckResult<ValueType>().getValueMap().begin()->second; + } + } + + template<typename ValueType> + std::unique_ptr<storm::modelchecker::CheckResult> DFTModelChecker<ValueType>::checkModel(std::shared_ptr<storm::models::sparse::Model<ValueType>>& model, std::shared_ptr<const storm::logic::Formula> const& formula) { + // Bisimulation + std::chrono::high_resolution_clock::time_point bisimulationStart = std::chrono::high_resolution_clock::now(); + if (model->isOfType(storm::models::ModelType::Ctmc) && storm::settings::getModule<storm::settings::modules::GeneralSettings>().isBisimulationSet()) { + STORM_LOG_INFO("Bisimulation..."); + model = storm::performDeterministicSparseBisimulationMinimization<storm::models::sparse::Ctmc<ValueType>>(model->template as<storm::models::sparse::Ctmc<ValueType>>(), {formula}, storm::storage::BisimulationType::Weak)->template as<storm::models::sparse::Ctmc<ValueType>>(); STORM_LOG_INFO("No. states (Bisimulation): " << model->getNumberOfStates()); STORM_LOG_INFO("No. transitions (Bisimulation): " << model->getNumberOfTransitions()); - bisimulationTime += std::chrono::high_resolution_clock::now() - explorationEnd; - models.push_back(model); } - return models; + std::chrono::high_resolution_clock::time_point bisimulationEnd = std::chrono::high_resolution_clock::now(); + bisimulationTime += bisimulationEnd - bisimulationStart; + + // Check the model + STORM_LOG_INFO("Model checking..."); + std::unique_ptr<storm::modelchecker::CheckResult> result(storm::verifySparseModel(model, formula)); + STORM_LOG_INFO("Model checking done."); + STORM_LOG_ASSERT(result, "Result does not exist."); + modelCheckingTime += std::chrono::high_resolution_clock::now() - bisimulationEnd; + return result; + } + + template<typename ValueType> + bool DFTModelChecker<ValueType>::isApproximationSufficient(ValueType lowerBound, ValueType upperBound, double approximationError) { + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Approximation works only for double."); + } + + template<> + bool DFTModelChecker<double>::isApproximationSufficient(double lowerBound, double upperBound, double approximationError) { + return upperBound - lowerBound <= approximationError; } template<typename ValueType> @@ -201,7 +280,13 @@ namespace storm { template<typename ValueType> void DFTModelChecker<ValueType>::printResult(std::ostream& os) { os << "Result: ["; - os << checkResult << "]" << std::endl; + if (this->approximationError > 0.0) { + approximation_result result = boost::get<approximation_result>(checkResult); + os << "(" << result.first << ", " << result.second << ")"; + } else { + os << boost::get<ValueType>(checkResult); + } + os << "]" << std::endl; } diff --git a/src/modelchecker/dft/DFTModelChecker.h b/src/modelchecker/dft/DFTModelChecker.h index 0867ed094..62bb5afe4 100644 --- a/src/modelchecker/dft/DFTModelChecker.h +++ b/src/modelchecker/dft/DFTModelChecker.h @@ -11,20 +11,23 @@ namespace storm { namespace modelchecker { - /** + /*! * Analyser for DFTs. */ template<typename ValueType> class DFTModelChecker { + typedef std::pair<ValueType, ValueType> approximation_result; + typedef boost::variant<ValueType, approximation_result> dft_result; + public: - /** + /*! * Constructor. */ DFTModelChecker(); - /** + /*! * Main method for checking DFTs. * * @param origDft Original DFT @@ -32,17 +35,18 @@ namespace storm { * @param symred Flag indicating if symmetry reduction should be used * @param allowModularisation Flag indication if modularisation is allowed * @param enableDC Flag indicating if dont care propagation should be used + * @param approximationError Error allowed for approximation. Value 0 indicates no approximation */ - void check(storm::storage::DFT<ValueType> const& origDft, std::shared_ptr<const storm::logic::Formula> const& formula, bool symred = true, bool allowModularisation = true, bool enableDC = true); + void check(storm::storage::DFT<ValueType> const& origDft, std::shared_ptr<const storm::logic::Formula> const& formula, bool symred = true, bool allowModularisation = true, bool enableDC = true, double approximationError = 0.0); - /** + /*! * Print timings of all operations to stream. * * @param os Output stream to write to. */ void printTimings(std::ostream& os = std::cout); - /** + /*! * Print result to stream. * * @param os Output stream to write to. @@ -50,16 +54,21 @@ namespace storm { void printResult(std::ostream& os = std::cout); private: + // Timing values std::chrono::duration<double> buildingTime = std::chrono::duration<double>::zero(); std::chrono::duration<double> explorationTime = std::chrono::duration<double>::zero(); std::chrono::duration<double> bisimulationTime = std::chrono::duration<double>::zero(); std::chrono::duration<double> modelCheckingTime = std::chrono::duration<double>::zero(); std::chrono::duration<double> totalTime = std::chrono::duration<double>::zero(); + // Model checking result - ValueType checkResult = storm::utility::zero<ValueType>(); + dft_result checkResult; + + // Allowed error bound for approximation + double approximationError; - /** + /*! * Internal helper for model checking a DFT. * * @param dft DFT @@ -67,36 +76,45 @@ namespace storm { * @param symred Flag indicating if symmetry reduction should be used * @param allowModularisation Flag indication if modularisation is allowed * @param enableDC Flag indicating if dont care propagation should be used + * @param approximationError Error allowed for approximation. Value 0 indicates no approximation * - * @return Model checking result + * @return Model checking result (or in case of approximation two results for lower and upper bound) */ - ValueType checkHelper(storm::storage::DFT<ValueType> const& dft, std::shared_ptr<const storm::logic::Formula> const& formula, bool symred, bool allowModularisation, bool enableDC); + dft_result checkHelper(storm::storage::DFT<ValueType> const& dft, std::shared_ptr<const storm::logic::Formula> const& formula, bool symred, bool allowModularisation, bool enableDC, double approximationError); - /** - * Check the Dfts via model checking. + /*! + * Check model generated from DFT. * - * @param dfts Vector of Dfts + * @param dft The DFT * @param formula Formula to check for * @param symred Flag indicating if symmetry reduction should be used * @param enableDC Flag indicating if dont care propagation should be used * @param approximationError Error allowed for approximation. Value 0 indicates no approximation * - * @return Vector of results for each model + * @return Model checking result */ - std::vector<ValueType> checkModel(std::vector<storm::storage::DFT<ValueType>> const& dfts, std::shared_ptr<const storm::logic::Formula> const& formula, bool symred, bool enableDC, double approximationError = 0.0); + dft_result checkDFT(storm::storage::DFT<ValueType> const& dft, std::shared_ptr<const storm::logic::Formula> const& formula, bool symred, bool enableDC, double approximationError = 0.0); - /** - * Build markov models from DFTs. + /*! + * Check the given markov model for the given property. * - * @param dfts Vector of Dfts - * @param formula Formula to check for - * @param symred Flag indicating if symmetry reduction should be used - * @param enableDC Flag indicating if dont care propagation should be used - * @param approximationError Error allowed for approximation. Value 0 indicates no approximation + * @param model Model to check + * @param formula Formula to check for + * + * @return Model checking result + */ + std::unique_ptr<storm::modelchecker::CheckResult> checkModel(std::shared_ptr<storm::models::sparse::Model<ValueType>>& model, std::shared_ptr<const storm::logic::Formula> const& formula); + + /*! + * Checks if the computed approximation is sufficient, i.e. upperBound - lowerBound <= approximationError. + * + * @param lowerBound The lower bound on the result. + * @param upperBound The upper bound on the result. + * @param approximationError The allowed error for approximating. * - * @return Vector of markov models corresponding to DFTs. + * @return True, if the approximation is sufficient. */ - std::vector<std::shared_ptr<storm::models::sparse::Model<ValueType>>> buildMarkovModels(std::vector<storm::storage::DFT<ValueType>> const& dfts, std::shared_ptr<const storm::logic::Formula> const& formula, bool symred, bool enableDC, double approximationError = 0.0); + bool isApproximationSufficient(ValueType lowerBound, ValueType upperBound, double approximationError); }; } diff --git a/src/settings/modules/DFTSettings.cpp b/src/settings/modules/DFTSettings.cpp index 416a8a5af..c27009a85 100644 --- a/src/settings/modules/DFTSettings.cpp +++ b/src/settings/modules/DFTSettings.cpp @@ -21,8 +21,8 @@ namespace storm { const std::string DFTSettings::symmetryReductionOptionShortName = "symred"; const std::string DFTSettings::modularisationOptionName = "modularisation"; const std::string DFTSettings::disableDCOptionName = "disabledc"; - const std::string DFTSettings::computeApproximationOptionName = "approximation"; - const std::string DFTSettings::computeApproximationOptionShortName = "approx"; + const std::string DFTSettings::approximationErrorOptionName = "approximation"; + const std::string DFTSettings::approximationErrorOptionShortName = "approx"; const std::string DFTSettings::propExpectedTimeOptionName = "expectedtime"; const std::string DFTSettings::propExpectedTimeOptionShortName = "mttf"; const std::string DFTSettings::propProbabilityOptionName = "probability"; @@ -39,7 +39,7 @@ namespace storm { this->addOption(storm::settings::OptionBuilder(moduleName, symmetryReductionOptionName, false, "Exploit symmetric structure of model.").setShortName(symmetryReductionOptionShortName).build()); this->addOption(storm::settings::OptionBuilder(moduleName, modularisationOptionName, false, "Use modularisation (not applicable for expected time).").build()); this->addOption(storm::settings::OptionBuilder(moduleName, disableDCOptionName, false, "Disable Dont Care propagation.").build()); - this->addOption(storm::settings::OptionBuilder(moduleName, computeApproximationOptionName, false, "Compute an approximation.").setShortName(computeApproximationOptionShortName).build()); + this->addOption(storm::settings::OptionBuilder(moduleName, approximationErrorOptionName, false, "Approximation error allowed.").setShortName(approximationErrorOptionShortName).addArgument(storm::settings::ArgumentBuilder::createDoubleArgument("error", "The approximation error to use.").addValidationFunctionDouble(storm::settings::ArgumentValidators::doubleGreaterValidatorIncluding(0.0)).build()).build()); this->addOption(storm::settings::OptionBuilder(moduleName, propExpectedTimeOptionName, false, "Compute expected time of system failure.").setShortName(propExpectedTimeOptionShortName).build()); this->addOption(storm::settings::OptionBuilder(moduleName, propProbabilityOptionName, false, "Compute probability of system failure.").build()); this->addOption(storm::settings::OptionBuilder(moduleName, propTimeBoundOptionName, false, "Compute probability of system failure up to given timebound.").addArgument(storm::settings::ArgumentBuilder::createDoubleArgument("time", "The timebound to use.").addValidationFunctionDouble(storm::settings::ArgumentValidators::doubleGreaterValidatorExcluding(0.0)).build()).build()); @@ -69,11 +69,15 @@ namespace storm { bool DFTSettings::isDisableDC() const { return this->getOption(disableDCOptionName).getHasOptionBeenSet(); } - - bool DFTSettings::computeApproximation() const { - return this->getOption(computeApproximationOptionName).getHasOptionBeenSet(); + + bool DFTSettings::isApproximationErrorSet() const { + return this->getOption(approximationErrorOptionName).getHasOptionBeenSet(); } - + + double DFTSettings::getApproximationError() const { + return this->getOption(approximationErrorOptionName).getArgumentByName("error").getValueAsDouble(); + } + bool DFTSettings::usePropExpectedTime() const { return this->getOption(propExpectedTimeOptionName).getHasOptionBeenSet(); } diff --git a/src/settings/modules/DFTSettings.h b/src/settings/modules/DFTSettings.h index f21752f7f..bb83a7475 100644 --- a/src/settings/modules/DFTSettings.h +++ b/src/settings/modules/DFTSettings.h @@ -32,10 +32,7 @@ namespace storm { * @return The name of the file that contains the dft specification. */ std::string getDftFilename() const; - - //expectedtime, probability, timebound, prob - //min, max - + /*! * Retrieves whether the option to use symmetry reduction is set. * @@ -62,8 +59,15 @@ namespace storm { * * @return True iff the option was set. */ - bool computeApproximation() const; - + bool isApproximationErrorSet() const; + + /*! + * Retrieves the error allowed for approximation the model checking result. + * + * @return The allowed errorbound. + */ + double getApproximationError() const; + /*! * Retrieves whether the property expected time should be used. * @@ -129,8 +133,8 @@ namespace storm { static const std::string symmetryReductionOptionShortName; static const std::string modularisationOptionName; static const std::string disableDCOptionName; - static const std::string computeApproximationOptionName; - static const std::string computeApproximationOptionShortName; + static const std::string approximationErrorOptionName; + static const std::string approximationErrorOptionShortName; static const std::string propExpectedTimeOptionName; static const std::string propExpectedTimeOptionShortName; static const std::string propProbabilityOptionName; diff --git a/src/storage/dft/DFTState.cpp b/src/storage/dft/DFTState.cpp index 156f99d14..a38bd21b8 100644 --- a/src/storage/dft/DFTState.cpp +++ b/src/storage/dft/DFTState.cpp @@ -204,8 +204,14 @@ namespace storm { } template<typename ValueType> - std::pair<std::shared_ptr<DFTBE<ValueType> const>, bool> DFTState<ValueType>::letNextBEFail(size_t index) - { + ValueType DFTState<ValueType>::getFailableBERate(size_t index) const { + STORM_LOG_ASSERT(index < nrFailableBEs(), "Index invalid."); + // TODO Matthias: get passive failure rate when applicable + return mDft.getBasicElement(mIsCurrentlyFailableBE[index])->activeFailureRate(); + } + + template<typename ValueType> + std::pair<std::shared_ptr<DFTBE<ValueType> const>, bool> DFTState<ValueType>::letNextBEFail(size_t index) { STORM_LOG_TRACE("currently failable: " << getCurrentlyFailableString()); if (nrFailableDependencies() > 0) { // Consider failure due to dependency diff --git a/src/storage/dft/DFTState.h b/src/storage/dft/DFTState.h index 8562559b3..5558e63ae 100644 --- a/src/storage/dft/DFTState.h +++ b/src/storage/dft/DFTState.h @@ -31,6 +31,7 @@ namespace storm { bool mValid = true; const DFT<ValueType>& mDft; const DFTStateGenerationInfo& mStateGenerationInfo; + bool mSkip = false; public: DFTState(DFT<ValueType> const& dft, DFTStateGenerationInfo const& stateGenerationInfo, size_t id); @@ -95,6 +96,14 @@ namespace storm { bool isInvalid() const { return !mValid; } + + void markSkip() { + mSkip = true; + } + + bool isSkip() const { + return mSkip; + } storm::storage::BitVector const& status() const { return mStatus; @@ -145,6 +154,14 @@ namespace storm { return mIsCurrentlyFailableBE.size(); } + /** Get the failure rate of the currently failable BE on the given index. + * + * @param index Index of BE in list of currently failable BEs. + * + * @return Failure rate of the BE. + */ + ValueType getFailableBERate(size_t index) const; + size_t nrFailableDependencies() const { return mFailableDependencies.size(); } diff --git a/src/storm-dyftee.cpp b/src/storm-dyftee.cpp index 89fc1da7b..51061050e 100644 --- a/src/storm-dyftee.cpp +++ b/src/storm-dyftee.cpp @@ -33,20 +33,25 @@ * @param property PCTC formula capturing the property to check. */ template <typename ValueType> -void analyzeDFT(std::string filename, std::string property, bool symred = false, bool allowModularisation = false, bool enableDC = true) { +void analyzeDFT(std::string filename, std::string property, bool symred, bool allowModularisation, bool enableDC, double approximationError) { std::cout << "Running DFT analysis on file " << filename << " with property " << property << std::endl; storm::parser::DFTGalileoParser<ValueType> parser; storm::storage::DFT<ValueType> dft = parser.parseDFT(filename); std::vector<std::shared_ptr<storm::logic::Formula const>> formulas = storm::parseFormulasForExplicit(property); STORM_LOG_ASSERT(formulas.size() == 1, "Wrong number of formulas."); - + storm::modelchecker::DFTModelChecker<ValueType> modelChecker; - modelChecker.check(dft, formulas[0], symred, allowModularisation, enableDC); + modelChecker.check(dft, formulas[0], symred, allowModularisation, enableDC, approximationError); modelChecker.printTimings(); modelChecker.printResult(); } +/*! + * Analyze the DFT with use of SMT solving. + * + * @param filename Path to DFT file in Galileo format. + */ template<typename ValueType> void analyzeWithSMT(std::string filename) { std::cout << "Running DFT analysis on file " << filename << " with use of SMT" << std::endl; @@ -163,11 +168,16 @@ int main(const int argc, const char** argv) { STORM_LOG_ASSERT(!pctlFormula.empty(), "Pctl formula empty."); + double approximationError = 0.0; + if (dftSettings.isApproximationErrorSet()) { + approximationError = dftSettings.getApproximationError(); + } + // From this point on we are ready to carry out the actual computations. if (parametric) { - analyzeDFT<storm::RationalFunction>(dftSettings.getDftFilename(), pctlFormula, dftSettings.useSymmetryReduction(), allowModular && dftSettings.useModularisation(), !dftSettings.isDisableDC() ); + analyzeDFT<storm::RationalFunction>(dftSettings.getDftFilename(), pctlFormula, dftSettings.useSymmetryReduction(), allowModular && dftSettings.useModularisation(), !dftSettings.isDisableDC(), approximationError); } else { - analyzeDFT<double>(dftSettings.getDftFilename(), pctlFormula, dftSettings.useSymmetryReduction(), allowModular && dftSettings.useModularisation(), !dftSettings.isDisableDC()); + analyzeDFT<double>(dftSettings.getDftFilename(), pctlFormula, dftSettings.useSymmetryReduction(), allowModular && dftSettings.useModularisation(), !dftSettings.isDisableDC(), approximationError); } // All operations have now been performed, so we clean up everything and terminate. From aab45d4eabe6097de9c9f3e409e456cc64f989a5 Mon Sep 17 00:00:00 2001 From: Mavo <matthias.volk@rwth-aachen.de> Date: Mon, 10 Oct 2016 16:40:10 +0200 Subject: [PATCH 14/65] Naive iterative refinement of approximation Former-commit-id: e1620cdefd307b984df1ac164a77797d3c838c99 --- src/builder/ExplicitDFTModelBuilderApprox.cpp | 321 ++++++++++++------ src/builder/ExplicitDFTModelBuilderApprox.h | 147 ++++++-- src/generator/DftNextStateGenerator.cpp | 2 +- src/generator/DftNextStateGenerator.h | 2 +- src/modelchecker/dft/DFTModelChecker.cpp | 25 +- src/storage/dft/DFTState.h | 4 +- 6 files changed, 340 insertions(+), 161 deletions(-) diff --git a/src/builder/ExplicitDFTModelBuilderApprox.cpp b/src/builder/ExplicitDFTModelBuilderApprox.cpp index 74c448c25..b9eda6308 100644 --- a/src/builder/ExplicitDFTModelBuilderApprox.cpp +++ b/src/builder/ExplicitDFTModelBuilderApprox.cpp @@ -6,7 +6,6 @@ #include <src/exceptions/UnexpectedException.h> #include "src/settings/modules/DFTSettings.h" #include "src/settings/SettingsManager.h" -#include "src/generator/DftNextStateGenerator.h" #include <map> namespace storm { @@ -18,80 +17,212 @@ namespace storm { } template<typename ValueType, typename StateType> - ExplicitDFTModelBuilderApprox<ValueType, StateType>::ExplicitDFTModelBuilderApprox(storm::storage::DFT<ValueType> const& dft, storm::storage::DFTIndependentSymmetries const& symmetries, bool enableDC) : dft(dft), enableDC(enableDC), stateStorage(((dft.stateVectorSize() / 64) + 1) * 64) { + ExplicitDFTModelBuilderApprox<ValueType, StateType>::MatrixBuilder::MatrixBuilder(bool canHaveNondeterminism) : mappingOffset(0), stateRemapping(), currentRowGroup(0), currentRow(0) { + // Create matrix builder + builder = storm::storage::SparseMatrixBuilder<ValueType>(0, 0, 0, false, canHaveNondeterminism, 0); + } + + template<typename ValueType, typename StateType> + ExplicitDFTModelBuilderApprox<ValueType, StateType>::ExplicitDFTModelBuilderApprox(storm::storage::DFT<ValueType> const& dft, storm::storage::DFTIndependentSymmetries const& symmetries, bool enableDC) : dft(dft), stateGenerationInfo(std::make_shared<storm::storage::DFTStateGenerationInfo>(dft.buildStateGenerationInfo(symmetries))), enableDC(enableDC), generator(dft, *stateGenerationInfo, enableDC, mergeFailedStates), matrixBuilder(!generator.isDeterministicModel()), stateStorage(((dft.stateVectorSize() / 64) + 1) * 64) { // stateVectorSize is bound for size of bitvector - stateGenerationInfo = std::make_shared<storm::storage::DFTStateGenerationInfo>(dft.buildStateGenerationInfo(symmetries)); } template<typename ValueType, typename StateType> - void ExplicitDFTModelBuilderApprox<ValueType, StateType>::buildModel(LabelOptions const& labelOpts, double approximationError) { + void ExplicitDFTModelBuilderApprox<ValueType, StateType>::buildModel(LabelOptions const& labelOpts, bool firstTime, double approximationError) { STORM_LOG_TRACE("Generating DFT state space"); // Initialize - StateType currentRowGroup = 0; - StateType currentRow = 0; - size_t pseudoStatesToCheck = 0; - modelComponents.markovianStates = storm::storage::BitVector(INITIAL_BITVECTOR_SIZE); - // Create generator - storm::generator::DftNextStateGenerator<ValueType, StateType> generator(dft, *stateGenerationInfo, enableDC, mergeFailedStates); generator.setApproximationError(approximationError); - // Create sparse matrix builder - storm::storage::SparseMatrixBuilder<ValueType> transitionMatrixBuilder(0, 0, 0, false, !generator.isDeterministicModel(), 0); - if(mergeFailedStates) { - // Introduce explicit fail state - storm::generator::StateBehavior<ValueType, StateType> behavior = generator.createMergeFailedState([this] (DFTStatePointer const& state) { + if (firstTime) { + // Initialize + modelComponents.markovianStates = storm::storage::BitVector(INITIAL_BITVECTOR_SIZE); + + if(mergeFailedStates) { + // Introduce explicit fail state + storm::generator::StateBehavior<ValueType, StateType> behavior = generator.createMergeFailedState([this] (DFTStatePointer const& state) { this->failedStateId = newIndex++; - stateRemapping.push_back(0); + matrixBuilder.stateRemapping.push_back(0); return this->failedStateId; } ); - setRemapping(failedStateId, currentRowGroup); + matrixBuilder.setRemapping(failedStateId); + STORM_LOG_ASSERT(!behavior.empty(), "Behavior is empty."); + matrixBuilder.newRowGroup(); + setMarkovian(behavior.begin()->isMarkovian()); + + // Now add self loop. + // TODO Matthias: maybe use general method. + STORM_LOG_ASSERT(behavior.getNumberOfChoices() == 1, "Wrong number of choices for failed state."); + STORM_LOG_ASSERT(behavior.begin()->size() == 1, "Wrong number of transitions for failed state."); + std::pair<StateType, ValueType> stateProbabilityPair = *(behavior.begin()->begin()); + STORM_LOG_ASSERT(stateProbabilityPair.first == failedStateId, "No self loop for failed state."); + STORM_LOG_ASSERT(storm::utility::isOne<ValueType>(stateProbabilityPair.second), "Probability for failed state != 1."); + matrixBuilder.addTransition(stateProbabilityPair.first, stateProbabilityPair.second); + matrixBuilder.finishRow(); + } + + // Build initial state + this->stateStorage.initialStateIndices = generator.getInitialStates(std::bind(&ExplicitDFTModelBuilderApprox::getOrAddStateIndex, this, std::placeholders::_1)); + STORM_LOG_ASSERT(stateStorage.initialStateIndices.size() == 1, "Only one initial state assumed."); + initialStateIndex = stateStorage.initialStateIndices[0]; + STORM_LOG_TRACE("Initial state: " << initialStateIndex); + + } else { + initializeNextIteration(); + } + + exploreStateSpace(); + + size_t stateSize = stateStorage.getNumberOfStates() + (mergeFailedStates ? 1 : 0); + modelComponents.markovianStates.resize(stateSize); + modelComponents.deterministicModel = generator.isDeterministicModel(); + + // Replace pseudo states in matrix + // TODO Matthias: avoid hack with fixed int type + std::vector<uint_fast64_t> pseudoStatesVector; + for (auto const& pseudoStatePair : mPseudoStatesMapping) { + pseudoStatesVector.push_back(pseudoStatePair.first); + } + STORM_LOG_ASSERT(std::find(pseudoStatesVector.begin(), pseudoStatesVector.end(), 0) == pseudoStatesVector.end(), "Unexplored pseudo state still contained."); + matrixBuilder.builder.replaceColumns(pseudoStatesVector, OFFSET_PSEUDO_STATE); + mPseudoStatesMapping.clear(); + + // Fix the entries in the transition matrix according to the mapping of ids to row group indices + STORM_LOG_ASSERT(matrixBuilder.stateRemapping[initialStateIndex] == initialStateIndex, "Initial state should not be remapped."); + // TODO Matthias: do not consider all rows? + matrixBuilder.remap(); + + STORM_LOG_TRACE("State remapping: " << matrixBuilder.stateRemapping); + STORM_LOG_TRACE("Markovian states: " << modelComponents.markovianStates); + STORM_LOG_DEBUG("Generated " << stateSize << " states"); + STORM_LOG_DEBUG("Skipped " << skippedStates.size() << " states"); + STORM_LOG_DEBUG("Model is " << (generator.isDeterministicModel() ? "deterministic" : "non-deterministic")); + + // Build transition matrix + modelComponents.transitionMatrix = matrixBuilder.builder.build(stateSize, stateSize); + if (stateSize <= 15) { + STORM_LOG_TRACE("Transition matrix: " << std::endl << modelComponents.transitionMatrix); + } else { + STORM_LOG_TRACE("Transition matrix: too big to print"); + } - STORM_LOG_ASSERT(!behavior.empty(), "Behavior is empty."); - setMarkovian(currentRowGroup, behavior.begin()->isMarkovian()); + buildLabeling(labelOpts); + } - // If the model is nondeterministic, we need to open a row group. - if (!generator.isDeterministicModel()) { - transitionMatrixBuilder.newRowGroup(currentRow); + template<typename ValueType, typename StateType> + void ExplicitDFTModelBuilderApprox<ValueType, StateType>::initializeNextIteration() { + STORM_LOG_TRACE("Refining DFT state space"); + + // Initialize matrix builder again + // TODO Matthias: avoid copy + std::vector<uint_fast64_t> copyRemapping = matrixBuilder.stateRemapping; + matrixBuilder = MatrixBuilder(!generator.isDeterministicModel()); + matrixBuilder.stateRemapping = copyRemapping; + StateType nrStates = modelComponents.transitionMatrix.getRowGroupCount(); + STORM_LOG_ASSERT(nrStates == matrixBuilder.stateRemapping.size(), "No. of states does not coincide with mapping size."); + + // Start by creating a remapping from the old indices to the new indices + std::vector<StateType> indexRemapping = std::vector<StateType>(nrStates, 0); + auto iterSkipped = skippedStates.begin(); + size_t skippedBefore = 0; + for (size_t i = 0; i < indexRemapping.size(); ++i) { + while (iterSkipped->first <= i) { + ++skippedBefore; + ++iterSkipped; } + indexRemapping[i] = i - skippedBefore; + } - // Now add self loop. - // TODO Matthias: maybe use general method. - STORM_LOG_ASSERT(behavior.getNumberOfChoices() == 1, "Wrong number of choices for failed state."); - STORM_LOG_ASSERT(behavior.begin()->size() == 1, "Wrong number of transitions for failed state."); - std::pair<StateType, ValueType> stateProbabilityPair = *(behavior.begin()->begin()); - STORM_LOG_ASSERT(stateProbabilityPair.first == failedStateId, "No self loop for failed state."); - STORM_LOG_ASSERT(storm::utility::isOne<ValueType>(stateProbabilityPair.second), "Probability for failed state != 1."); - transitionMatrixBuilder.addNextValue(currentRow, stateProbabilityPair.first, stateProbabilityPair.second); - ++currentRow; - ++currentRowGroup; + // Set remapping + size_t nrExpandedStates = nrStates - skippedBefore; + matrixBuilder.mappingOffset = nrStates; + STORM_LOG_TRACE("# expanded states: " << nrExpandedStates); + StateType skippedIndex = nrExpandedStates; + std::map<StateType, DFTStatePointer> skippedStatesNew; + for (size_t id = 0; id < matrixBuilder.stateRemapping.size(); ++id) { + StateType index = matrixBuilder.stateRemapping[id]; + auto itFind = skippedStates.find(index); + if (itFind != skippedStates.end()) { + // Set new mapping for skipped state + matrixBuilder.stateRemapping[id] = skippedIndex; + skippedStatesNew[skippedIndex] = itFind->second; + indexRemapping[index] = skippedIndex; + ++skippedIndex; + } else { + // Set new mapping for expanded state + matrixBuilder.stateRemapping[id] = indexRemapping[index]; + } + } + STORM_LOG_TRACE("New state remapping: " << matrixBuilder.stateRemapping); + std::stringstream ss; + ss << "Index remapping:" << std::endl; + for (auto tmp : indexRemapping) { + ss << tmp << " "; + } + STORM_LOG_TRACE(ss.str()); + + // Remap markovian states + storm::storage::BitVector markovianStatesNew = storm::storage::BitVector(modelComponents.markovianStates.size(), true); + // Iterate over all not set bits + modelComponents.markovianStates.complement(); + size_t index = modelComponents.markovianStates.getNextSetIndex(0); + while (index < modelComponents.markovianStates.size()) { + markovianStatesNew.set(indexRemapping[index], false); + index = modelComponents.markovianStates.getNextSetIndex(index); } + STORM_LOG_ASSERT(modelComponents.markovianStates.size() - modelComponents.markovianStates.getNumberOfSetBits() == markovianStatesNew.getNumberOfSetBits(), "Remapping of markovian states is wrong."); + STORM_LOG_ASSERT(markovianStatesNew.size() == nrStates, "No. of states does not coincide with markovian size."); + modelComponents.markovianStates = markovianStatesNew; + + // Build submatrix for expanded states + // TODO Matthias: only use row groups when necessary + for (StateType oldRowGroup = 0; oldRowGroup < modelComponents.transitionMatrix.getRowGroupCount(); ++oldRowGroup) { + if (indexRemapping[oldRowGroup] < nrExpandedStates) { + // State is expanded -> copy to new matrix + matrixBuilder.newRowGroup(); + for (StateType oldRow = modelComponents.transitionMatrix.getRowGroupIndices()[oldRowGroup]; oldRow < modelComponents.transitionMatrix.getRowGroupIndices()[oldRowGroup+1]; ++oldRow) { + for (typename storm::storage::SparseMatrix<ValueType>::const_iterator itEntry = modelComponents.transitionMatrix.begin(oldRow); itEntry != modelComponents.transitionMatrix.end(oldRow); ++itEntry) { + auto itFind = skippedStates.find(itEntry->getColumn()); + if (itFind != skippedStates.end()) { + // Set id for skipped states as we remap it later + matrixBuilder.addTransition(matrixBuilder.mappingOffset + itFind->second->getId(), itEntry->getValue()); + } else { + // Set newly remapped index for expanded states + matrixBuilder.addTransition(indexRemapping[itEntry->getColumn()], itEntry->getValue()); + } + } + matrixBuilder.finishRow(); + } + } + } + + skippedStates = skippedStatesNew; - // Create a callback for the next-state generator to enable it to add states - std::function<StateType (DFTStatePointer const&)> stateToIdCallback = std::bind(&ExplicitDFTModelBuilderApprox::getOrAddStateIndex, this, std::placeholders::_1); + STORM_LOG_ASSERT(matrixBuilder.getCurrentRowGroup() == nrExpandedStates, "Row group size does not match."); - // Build initial states - this->stateStorage.initialStateIndices = generator.getInitialStates(stateToIdCallback); - STORM_LOG_ASSERT(stateStorage.initialStateIndices.size() == 1, "Only one initial state assumed."); - StateType initialStateIndex = stateStorage.initialStateIndices[0]; - STORM_LOG_TRACE("Initial state: " << initialStateIndex); + // Explore all skipped states now + for (auto const& skippedState : skippedStates) { + skippedState.second->setSkip(false); + statesToExplore.push_front(skippedState.second); + } + skippedStates.clear(); + } - // Explore state space + template<typename ValueType, typename StateType> + void ExplicitDFTModelBuilderApprox<ValueType, StateType>::exploreStateSpace() { bool explorationFinished = false; + size_t pseudoStatesToCheck = 0; while (!explorationFinished) { // Get the first state in the queue DFTStatePointer currentState = statesToExplore.front(); STORM_LOG_ASSERT(stateStorage.stateToId.getValue(currentState->status()) == currentState->getId(), "Ids of states do not coincide."); statesToExplore.pop_front(); - // Remember that this row group was actually filled with the transitions of a different state - setRemapping(currentState->getId(), currentRowGroup); + // Remember that the current row group was actually filled with the transitions of a different state + matrixBuilder.setRemapping(currentState->getId()); - // If the model is nondeterministic, we need to open a row group. - if (!generator.isDeterministicModel()) { - transitionMatrixBuilder.newRowGroup(currentRow); - } + matrixBuilder.newRowGroup(); // Try to explore the next state generator.load(currentState); @@ -99,18 +230,18 @@ namespace storm { if (currentState->isSkip()) { // Skip the current state STORM_LOG_TRACE("Skip expansion of state: " << dft.getStateString(currentState)); - setMarkovian(currentRowGroup, true); + setMarkovian(true); // Add transition to target state with temporary value 0 // TODO Matthias: what to do when there is no unique target state? - transitionMatrixBuilder.addNextValue(currentRow, failedStateId, storm::utility::zero<ValueType>()); + matrixBuilder.addTransition(failedStateId, storm::utility::zero<ValueType>()); // Remember skipped state - skippedStates[currentRow] = currentState; - ++currentRow; + skippedStates[matrixBuilder.getCurrentRowGroup() - 1] = currentState; + matrixBuilder.finishRow(); } else { // Explore the current state - storm::generator::StateBehavior<ValueType, StateType> behavior = generator.expand(stateToIdCallback); + storm::generator::StateBehavior<ValueType, StateType> behavior = generator.expand(std::bind(&ExplicitDFTModelBuilderApprox::getOrAddStateIndex, this, std::placeholders::_1)); STORM_LOG_ASSERT(!behavior.empty(), "Behavior is empty."); - setMarkovian(currentRowGroup, behavior.begin()->isMarkovian()); + setMarkovian(behavior.begin()->isMarkovian()); // Now add all choices. for (auto const& choice : behavior) { @@ -130,15 +261,12 @@ namespace storm { } } } - - transitionMatrixBuilder.addNextValue(currentRow, stateProbabilityPair.first, stateProbabilityPair.second); + matrixBuilder.addTransition(matrixBuilder.mappingOffset + stateProbabilityPair.first, stateProbabilityPair.second); } - ++currentRow; + matrixBuilder.finishRow(); } } - ++currentRowGroup; - if (statesToExplore.empty()) { explorationFinished = true; // Before ending the exploration check for pseudo states which are not initialized yet @@ -161,45 +289,13 @@ namespace storm { } } // end exploration + } - size_t stateSize = stateStorage.getNumberOfStates() + (mergeFailedStates ? 1 : 0); - modelComponents.markovianStates.resize(stateSize); - modelComponents.deterministicModel = generator.isDeterministicModel(); - - // Replace pseudo states in matrix - // TODO Matthias: avoid hack with fixed int type - std::vector<uint_fast64_t> pseudoStatesVector; - for (auto const& pseudoStatePair : mPseudoStatesMapping) { - pseudoStatesVector.push_back(pseudoStatePair.first); - } - STORM_LOG_ASSERT(std::find(pseudoStatesVector.begin(), pseudoStatesVector.end(), 0) == pseudoStatesVector.end(), "Unexplored pseudo state still contained."); - transitionMatrixBuilder.replaceColumns(pseudoStatesVector, OFFSET_PSEUDO_STATE); - - - // Fix the entries in the matrix according to the (reversed) mapping of row groups to indices - STORM_LOG_ASSERT(stateRemapping[initialStateIndex] == initialStateIndex, "Initial state should not be remapped."); - // Fix the transition matrix - transitionMatrixBuilder.replaceColumns(stateRemapping, 0); - // Fix the hash map storing the mapping states -> ids - this->stateStorage.stateToId.remap([this] (StateType const& state) { return this->stateRemapping[state]; } ); - - STORM_LOG_TRACE("State remapping: " << stateRemapping); - STORM_LOG_TRACE("Markovian states: " << modelComponents.markovianStates); - STORM_LOG_DEBUG("Generated " << stateSize << " states"); - STORM_LOG_DEBUG("Skipped " << skippedStates.size() << " states"); - STORM_LOG_DEBUG("Model is " << (generator.isDeterministicModel() ? "deterministic" : "non-deterministic")); - - // Build transition matrix - modelComponents.transitionMatrix = transitionMatrixBuilder.build(stateSize, stateSize); - if (stateSize <= 15) { - STORM_LOG_TRACE("Transition matrix: " << std::endl << modelComponents.transitionMatrix); - } else { - STORM_LOG_TRACE("Transition matrix: too big to print"); - } - + template<typename ValueType, typename StateType> + void ExplicitDFTModelBuilderApprox<ValueType, StateType>::buildLabeling(LabelOptions const& labelOpts) { // Build state labeling - modelComponents.stateLabeling = storm::models::sparse::StateLabeling(stateSize); - // Initial state is always first state without any failure + modelComponents.stateLabeling = storm::models::sparse::StateLabeling(modelComponents.transitionMatrix.getRowGroupCount()); + // Initial state modelComponents.stateLabeling.addLabel("init"); modelComponents.stateLabeling.addLabelToState("init", initialStateIndex); // Label all states corresponding to their status (failed, failsafe, failed BE) @@ -259,13 +355,14 @@ namespace storm { template<typename ValueType, typename StateType> std::shared_ptr<storm::models::sparse::Model<ValueType>> ExplicitDFTModelBuilderApprox<ValueType, StateType>::createModel(bool copy) { + std::shared_ptr<storm::models::sparse::Model<ValueType>> model; + if (modelComponents.deterministicModel) { // Build CTMC if (copy) { - return std::make_shared<storm::models::sparse::Ctmc<ValueType>>(modelComponents.transitionMatrix, modelComponents.stateLabeling); - + model = std::make_shared<storm::models::sparse::Ctmc<ValueType>>(modelComponents.transitionMatrix, modelComponents.stateLabeling); } else { - return std::make_shared<storm::models::sparse::Ctmc<ValueType>>(std::move(modelComponents.transitionMatrix), std::move(modelComponents.stateLabeling)); + model = std::make_shared<storm::models::sparse::Ctmc<ValueType>>(std::move(modelComponents.transitionMatrix), std::move(modelComponents.stateLabeling)); } } else { // Build MA @@ -290,11 +387,21 @@ namespace storm { } if (ma->hasOnlyTrivialNondeterminism()) { // Markov automaton can be converted into CTMC - return ma->convertToCTMC(); + // TODO Matthias: change components which were not moved accordingly + model = ma->convertToCTMC(); } else { - return ma; + model = ma; } } + + STORM_LOG_DEBUG("No. states: " << model->getNumberOfStates()); + STORM_LOG_DEBUG("No. transitions: " << model->getNumberOfTransitions()); + if (model->getNumberOfStates() <= 15) { + STORM_LOG_TRACE("Transition matrix: " << std::endl << model->getTransitionMatrix()); + } else { + STORM_LOG_TRACE("Transition matrix: too big to print"); + } + return model; } template<typename ValueType, typename StateType> @@ -369,7 +476,7 @@ namespace storm { STORM_LOG_ASSERT(stateId == state->getId(), "Ids do not match."); STORM_LOG_TRACE("Remember state for later creation: " << dft.getStateString(state)); // Reserve one slot for the coming state in the remapping - stateRemapping.push_back(0); + matrixBuilder.stateRemapping.push_back(0); } else { // Create new state state->setId(newIndex++); @@ -379,25 +486,19 @@ namespace storm { statesToExplore.push_front(state); // Reserve one slot for the new state in the remapping - stateRemapping.push_back(0); + matrixBuilder.stateRemapping.push_back(0); } } return stateId; } template<typename ValueType, typename StateType> - void ExplicitDFTModelBuilderApprox<ValueType, StateType>::setMarkovian(StateType id, bool markovian) { - if (id >= modelComponents.markovianStates.size()) { + void ExplicitDFTModelBuilderApprox<ValueType, StateType>::setMarkovian(bool markovian) { + if (matrixBuilder.getCurrentRowGroup() > modelComponents.markovianStates.size()) { // Resize BitVector modelComponents.markovianStates.resize(modelComponents.markovianStates.size() + INITIAL_BITVECTOR_SIZE); } - modelComponents.markovianStates.set(id, markovian); - } - - template<typename ValueType, typename StateType> - void ExplicitDFTModelBuilderApprox<ValueType, StateType>::setRemapping(StateType id, StateType mappedId) { - STORM_LOG_ASSERT(id < stateRemapping.size(), "Invalid index for remapping."); - stateRemapping[id] = mappedId; + modelComponents.markovianStates.set(matrixBuilder.getCurrentRowGroup() - 1, markovian); } diff --git a/src/builder/ExplicitDFTModelBuilderApprox.h b/src/builder/ExplicitDFTModelBuilderApprox.h index fa529f221..3f8af519f 100644 --- a/src/builder/ExplicitDFTModelBuilderApprox.h +++ b/src/builder/ExplicitDFTModelBuilderApprox.h @@ -4,6 +4,7 @@ #include <src/models/sparse/StateLabeling.h> #include <src/models/sparse/StandardRewardModel.h> #include <src/models/sparse/Model.h> +#include "src/generator/DftNextStateGenerator.h" #include <src/storage/SparseMatrix.h> #include "src/storage/sparse/StateStorage.h" #include <src/storage/dft/DFT.h> @@ -32,6 +33,7 @@ namespace storm { // A structure holding the individual components of a model. struct ModelComponents { + // Constructor ModelComponents(); // The transition matrix. @@ -53,6 +55,87 @@ namespace storm { bool deterministicModel; }; + // A class holding the information for building the transition matrix. + class MatrixBuilder { + public: + // Constructor + MatrixBuilder(bool canHaveNondeterminism); + + /*! + * Set a mapping from a state id to the index in the matrix. + * + * @param id Id of the state. + */ + void setRemapping(StateType id) { + STORM_LOG_ASSERT(id < stateRemapping.size(), "Invalid index for remapping."); + stateRemapping[id] = currentRowGroup; + } + + /*! + * Create a new row group if the model is nondeterministic. + */ + void newRowGroup() { + if (canHaveNondeterminism) { + builder.newRowGroup(currentRow); + } + ++currentRowGroup; + } + + /*! + * Add a transition from the current row. + * + * @param index Target index + * @param value Value of transition + */ + void addTransition(StateType index, ValueType value) { + builder.addNextValue(currentRow, index, value); + } + + /*! + * Finish the current row. + */ + void finishRow() { + ++currentRow; + } + + /*! + * Remap the columns in the matrix. + */ + void remap() { + builder.replaceColumns(stateRemapping, mappingOffset); + } + + /*! + * Get the current row group. + * + * @return The current row group. + */ + StateType getCurrentRowGroup() { + return currentRowGroup; + } + + // Flag indicating if row groups are needed. + bool canHaveNondeterminism; + + // Matrix builder. + storm::storage::SparseMatrixBuilder<ValueType> builder; + + // Offset to distinguish states which will not be remapped anymore and those which will. + size_t mappingOffset; + + // A mapping from state ids to the row group indices in which they actually reside. + // TODO Matthias: avoid hack with fixed int type + std::vector<uint_fast64_t> stateRemapping; + + private: + + // Index of the current row group. + StateType currentRowGroup; + + // Index of the current row. + StateType currentRow; + }; + public: // A structure holding the labeling options. struct LabelOptions { @@ -74,9 +157,10 @@ namespace storm { * Build model from DFT. * * @param labelOpts Options for labeling. + * @param firstTime Flag indicating if the model is built for the first time or rebuilt. * @param approximationError Error allowed for approximation. */ - void buildModel(LabelOptions const& labelOpts, double approximationError = 0.0); + void buildModel(LabelOptions const& labelOpts, bool firstTime, double approximationError = 0.0); /*! * Get the built model. @@ -96,6 +180,23 @@ namespace storm { private: + /*! + * Explore state space of DFT. + */ + void exploreStateSpace(); + + /*! + * Initialize the matrix for a refinement iteration. + */ + void initializeNextIteration(); + + /*! + * Build the labeling. + * + * @param labelOpts Options for labeling. + */ + void buildLabeling(LabelOptions const& labelOpts); + /*! * Add a state to the explored states (if not already there). It also handles pseudo states. * @@ -106,20 +207,11 @@ namespace storm { StateType getOrAddStateIndex(DFTStatePointer const& state); /*! - * Set if the given state is markovian. + * Set markovian flag for the current state. * - * @param id Id of the state. * @param markovian Flag indicating if the state is markovian. */ - void setMarkovian(StateType id, bool markovian); - - /*! - * Set a mapping from a state id to its new id. - * - * @param id Id of the state. - * @param mappedId New id to use. - */ - void setRemapping(StateType id, StateType mappedId); + void setMarkovian(bool markovian); /** * Change matrix to reflect the lower approximation bound. @@ -158,40 +250,43 @@ namespace storm { // TODO Matthias: use const reference std::shared_ptr<storm::storage::DFTStateGenerationInfo> stateGenerationInfo; - // Current id for new state - size_t newIndex = 0; - - // Mapping from pseudo states to (id of concrete state, bitvector) - std::vector<std::pair<StateType, storm::storage::BitVector>> mPseudoStatesMapping; + // Flag indication if dont care propagation should be used. + bool enableDC = true; //TODO Matthias: make changeable const bool mergeFailedStates = true; + // Current id for new state + size_t newIndex = 0; + // Id of failed state size_t failedStateId = 0; // Id of initial state size_t initialStateIndex = 0; - // Flag indication if dont care propagation should be used. - bool enableDC = true; + // Mapping from pseudo states to (id of concrete state, bitvector representation) + std::vector<std::pair<StateType, storm::storage::BitVector>> mPseudoStatesMapping; + + // Next state generator for exploring the state space + storm::generator::DftNextStateGenerator<ValueType, StateType> generator; // Structure for the components of the model. ModelComponents modelComponents; + // Structure for the transition matrix builder. + MatrixBuilder matrixBuilder; + // Internal information about the states that were explored. storm::storage::sparse::StateStorage<StateType> stateStorage; // A set of states that still need to be explored. std::deque<DFTStatePointer> statesToExplore; - // A mapping from state indices to the row groups in which they actually reside - // TODO Matthias: avoid hack with fixed int type - std::vector<uint_fast64_t> stateRemapping; - - // Holds all skipped states which were not yet expanded. More concrete it is a mapping from matrix indices - // to the corresponding skipped state. - std::unordered_map<StateType, DFTStatePointer> skippedStates; + // Holds all skipped states which were not yet expanded. More concretely it is a mapping from matrix indices + // to the corresponding skipped states. + // Notice that we need an ordered map here to easily iterate in increasing order over state ids. + std::map<StateType, DFTStatePointer> skippedStates; }; } } diff --git a/src/generator/DftNextStateGenerator.cpp b/src/generator/DftNextStateGenerator.cpp index f468c673e..119ff9342 100644 --- a/src/generator/DftNextStateGenerator.cpp +++ b/src/generator/DftNextStateGenerator.cpp @@ -176,7 +176,7 @@ namespace storm { if (approximationError > 0.0) { if (checkSkipState(newState, rate, choice.getTotalMass(), approximationError)) { STORM_LOG_TRACE("Will skip state " << newStateId); - newState->markSkip(); + newState->setSkip(true); } } } diff --git a/src/generator/DftNextStateGenerator.h b/src/generator/DftNextStateGenerator.h index 7c9e1bb48..6fa815e44 100644 --- a/src/generator/DftNextStateGenerator.h +++ b/src/generator/DftNextStateGenerator.h @@ -70,7 +70,7 @@ namespace storm { StateType mergeFailedStateId = 0; // Flag indicating if the model is deterministic. - bool deterministicModel = true; + bool deterministicModel = false; // Allowed approximation error. double approximationError = 0.0; diff --git a/src/modelchecker/dft/DFTModelChecker.cpp b/src/modelchecker/dft/DFTModelChecker.cpp index 1c0772bf8..7ac2f1a8a 100644 --- a/src/modelchecker/dft/DFTModelChecker.cpp +++ b/src/modelchecker/dft/DFTModelChecker.cpp @@ -158,26 +158,17 @@ namespace storm { size_t iteration = 0; do { // Iteratively build finer models - // TODO Matthias: implement refinement - STORM_LOG_ASSERT(iteration < 1, "Iterative refinement not yet implemented."); explorationStart = std::chrono::high_resolution_clock::now(); STORM_LOG_INFO("Building model..."); // TODO Matthias refine model using existing model and MC results currentApproximationError = pow(0.1, iteration) * approximationError; - builder.buildModel(labeloptions, currentApproximationError); + builder.buildModel(labeloptions, iteration < 1, currentApproximationError); + // TODO Matthias: possible to do bisimulation on approximated model and not on concrete one? // Build model for lower bound STORM_LOG_INFO("Getting model for lower bound..."); model = builder.getModelApproximation(true); - //model->printModelInformationToStream(std::cout); - STORM_LOG_INFO("No. states (Explored): " << model->getNumberOfStates()); - STORM_LOG_INFO("No. transitions (Explored): " << model->getNumberOfTransitions()); - if (model->getNumberOfStates() <= 15) { - STORM_LOG_TRACE("Transition matrix: " << std::endl << model->getTransitionMatrix()); - } else { - STORM_LOG_TRACE("Transition matrix: too big to print"); - } explorationTime += std::chrono::high_resolution_clock::now() - explorationStart; // Check lower bound std::unique_ptr<storm::modelchecker::CheckResult> result = checkModel(model, formula); @@ -188,14 +179,6 @@ namespace storm { STORM_LOG_INFO("Getting model for upper bound..."); explorationStart = std::chrono::high_resolution_clock::now(); model = builder.getModelApproximation(false); - //model->printModelInformationToStream(std::cout); - STORM_LOG_INFO("No. states (Explored): " << model->getNumberOfStates()); - STORM_LOG_INFO("No. transitions (Explored): " << model->getNumberOfTransitions()); - if (model->getNumberOfStates() <= 15) { - STORM_LOG_TRACE("Transition matrix: " << std::endl << model->getTransitionMatrix()); - } else { - STORM_LOG_TRACE("Transition matrix: too big to print"); - } explorationTime += std::chrono::high_resolution_clock::now() - explorationStart; // Check upper bound result = checkModel(model, formula); @@ -203,7 +186,7 @@ namespace storm { approxResult.second = result->asExplicitQuantitativeCheckResult<ValueType>().getValueMap().begin()->second; ++iteration; - STORM_LOG_TRACE("Result after iteration " << iteration << ": (" << approxResult.first << ", " << approxResult.second << ")"); + STORM_LOG_INFO("Result after iteration " << iteration << ": (" << std::setprecision(10) << approxResult.first << ", " << approxResult.second << ")"); } while (!isApproximationSufficient(approxResult.first, approxResult.second, approximationError)); STORM_LOG_INFO("Finished approximation after " << iteration << " iteration" << (iteration > 1 ? "s." : ".")); @@ -216,7 +199,7 @@ namespace storm { if (approximationError >= 0.0) { storm::builder::ExplicitDFTModelBuilderApprox<ValueType> builder(dft, symmetries, enableDC); typename storm::builder::ExplicitDFTModelBuilderApprox<ValueType>::LabelOptions labeloptions; // TODO initialize this with the formula - builder.buildModel(labeloptions); + builder.buildModel(labeloptions, true); model = builder.getModel(); } else { storm::builder::ExplicitDFTModelBuilder<ValueType> builder(dft, symmetries, enableDC); diff --git a/src/storage/dft/DFTState.h b/src/storage/dft/DFTState.h index 5558e63ae..c87008c9c 100644 --- a/src/storage/dft/DFTState.h +++ b/src/storage/dft/DFTState.h @@ -97,8 +97,8 @@ namespace storm { return !mValid; } - void markSkip() { - mSkip = true; + void setSkip(bool skip) { + mSkip = skip; } bool isSkip() const { From bf491117c7d35d90c56478d05798c339e8dca0d1 Mon Sep 17 00:00:00 2001 From: Mavo <matthias.volk@rwth-aachen.de> Date: Mon, 10 Oct 2016 17:26:45 +0200 Subject: [PATCH 15/65] Sort row only if replacement took place Former-commit-id: 84f584d6c4dea92d8b199d34af2074e1e2163ed0 --- src/storage/SparseMatrix.cpp | 73 ++++++++++++------------------------ src/storage/SparseMatrix.h | 8 +--- 2 files changed, 24 insertions(+), 57 deletions(-) diff --git a/src/storage/SparseMatrix.cpp b/src/storage/SparseMatrix.cpp index c43dc77d9..d38b0a838 100644 --- a/src/storage/SparseMatrix.cpp +++ b/src/storage/SparseMatrix.cpp @@ -22,6 +22,8 @@ #include "src/utility/macros.h" +#include <iterator> + namespace storm { namespace storage { @@ -267,66 +269,37 @@ namespace storm { } template<typename ValueType> - bool SparseMatrixBuilder<ValueType>::replaceColumns(std::vector<index_type> const& replacements, index_type offset) { - bool changed = false; + void SparseMatrixBuilder<ValueType>::replaceColumns(std::vector<index_type> const& replacements, index_type offset) { index_type maxColumn = 0; - for (auto& elem : columnsAndValues) { - if (elem.getColumn() >= offset) { - elem.setColumn(replacements[elem.getColumn() - offset]); - changed = true; - } - maxColumn = std::max(maxColumn, elem.getColumn()); - } - STORM_LOG_ASSERT(changed || highestColumn == maxColumn, "Incorrect maximal column."); - highestColumn = maxColumn; - STORM_LOG_ASSERT(changed || lastColumn == columnsAndValues[columnsAndValues.size() - 1].getColumn(), "Incorrect last column."); - lastColumn = columnsAndValues[columnsAndValues.size() - 1].getColumn(); - - if (changed) { - fixColumns(); - } - return changed; - } - template<typename ValueType> - void SparseMatrixBuilder<ValueType>::fixColumns() { - // Sort columns per row - typename SparseMatrix<ValueType>::index_type endGroups; - typename SparseMatrix<ValueType>::index_type endRows; - - if (hasCustomRowGrouping) { - for (index_type group = 0; group < rowGroupIndices.get().size(); ++group) { - endGroups = group < rowGroupIndices.get().size()-1 ? rowGroupIndices.get()[group+1] : rowIndications.size(); - for (index_type i = rowGroupIndices.get()[group]; i < endGroups; ++i) { - endRows = i < rowIndications.size()-1 ? rowIndications[i+1] : columnsAndValues.size(); - // Sort the row - std::sort(columnsAndValues.begin() + rowIndications[i], columnsAndValues.begin() + endRows, - [](MatrixEntry<index_type, value_type> const& a, MatrixEntry<index_type, value_type> const& b) { - return a.getColumn() < b.getColumn(); - }); - // Assert no equal elements - STORM_LOG_ASSERT(std::is_sorted(columnsAndValues.begin() + rowIndications[i], columnsAndValues.begin() + endRows, - [](MatrixEntry<index_type, value_type> const& a, MatrixEntry<index_type, value_type> const& b) { - return a.getColumn() <= b.getColumn(); - }), "Columns not sorted."); + for (index_type row = 0; row < rowIndications.size(); ++row) { + bool changed = false; + auto startRow = std::next(columnsAndValues.begin(), rowIndications[row]); + auto endRow = row < rowIndications.size()-1 ? std::next(columnsAndValues.begin(), rowIndications[row+1]) : columnsAndValues.end(); + for (auto entry = startRow; entry != endRow; ++entry) { + if (entry->getColumn() >= offset) { + // Change column + entry->setColumn(replacements[entry->getColumn() - offset]); + changed = true; } + maxColumn = std::max(maxColumn, entry->getColumn()); } - } else { - for (index_type i = 0; i < rowIndications.size(); ++i) { - endRows = i < rowIndications.size()-1 ? rowIndications[i+1] : columnsAndValues.size(); - // Sort the row - std::sort(columnsAndValues.begin() + rowIndications[i], columnsAndValues.begin() + endRows, + if (changed) { + // Sort columns in row + std::sort(startRow, endRow, [](MatrixEntry<index_type, value_type> const& a, MatrixEntry<index_type, value_type> const& b) { return a.getColumn() < b.getColumn(); }); // Assert no equal elements - STORM_LOG_ASSERT(std::is_sorted(columnsAndValues.begin() + rowIndications[i], columnsAndValues.begin() + endRows, - [](MatrixEntry<index_type, value_type> const& a, MatrixEntry<index_type, value_type> const& b) { - return a.getColumn() <= b.getColumn(); - }), "Columns not sorted."); + STORM_LOG_ASSERT(std::is_sorted(startRow, endRow, + [](MatrixEntry<index_type, value_type> const& a, MatrixEntry<index_type, value_type> const& b) { + return a.getColumn() < b.getColumn(); + }), "Columns not sorted."); } - } + + highestColumn = maxColumn; + lastColumn = columnsAndValues[columnsAndValues.size() - 1].getColumn(); } template<typename ValueType> diff --git a/src/storage/SparseMatrix.h b/src/storage/SparseMatrix.h index c170d43a9..5d9f48fe9 100644 --- a/src/storage/SparseMatrix.h +++ b/src/storage/SparseMatrix.h @@ -230,9 +230,8 @@ namespace storm { * * @param replacements Mapping indicating the replacements from offset+i -> value of i. * @param offset Offset to add to each id in vector index. - * @return True if replacement took place, False if nothing changed. */ - bool replaceColumns(std::vector<index_type> const& replacements, index_type offset); + void replaceColumns(std::vector<index_type> const& replacements, index_type offset); private: // A flag indicating whether a row count was set upon construction. @@ -295,11 +294,6 @@ namespace storm { // Stores the currently active row group. This is used for correctly constructing the row grouping of the // matrix. index_type currentRowGroup; - - /*! - * Fixes the matrix by sorting the columns to gain increasing order again. - */ - void fixColumns(); }; /*! From 41a71f86889bcab17236ce170a65f4321acfb634 Mon Sep 17 00:00:00 2001 From: Mavo <matthias.volk@rwth-aachen.de> Date: Mon, 10 Oct 2016 17:44:45 +0200 Subject: [PATCH 16/65] Fixed bug with not setting nondetermism correctly Former-commit-id: cd1e029c296df1d00c965be044f122dd04bf90e0 --- src/builder/ExplicitDFTModelBuilderApprox.cpp | 2 +- src/builder/ExplicitDFTModelBuilderApprox.h | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/builder/ExplicitDFTModelBuilderApprox.cpp b/src/builder/ExplicitDFTModelBuilderApprox.cpp index b9eda6308..0729fec59 100644 --- a/src/builder/ExplicitDFTModelBuilderApprox.cpp +++ b/src/builder/ExplicitDFTModelBuilderApprox.cpp @@ -17,7 +17,7 @@ namespace storm { } template<typename ValueType, typename StateType> - ExplicitDFTModelBuilderApprox<ValueType, StateType>::MatrixBuilder::MatrixBuilder(bool canHaveNondeterminism) : mappingOffset(0), stateRemapping(), currentRowGroup(0), currentRow(0) { + ExplicitDFTModelBuilderApprox<ValueType, StateType>::MatrixBuilder::MatrixBuilder(bool canHaveNondeterminism) : mappingOffset(0), stateRemapping(), currentRowGroup(0), currentRow(0), canHaveNondeterminism((canHaveNondeterminism)) { // Create matrix builder builder = storm::storage::SparseMatrixBuilder<ValueType>(0, 0, 0, false, canHaveNondeterminism, 0); } diff --git a/src/builder/ExplicitDFTModelBuilderApprox.h b/src/builder/ExplicitDFTModelBuilderApprox.h index 3f8af519f..b8658c6ce 100644 --- a/src/builder/ExplicitDFTModelBuilderApprox.h +++ b/src/builder/ExplicitDFTModelBuilderApprox.h @@ -114,9 +114,6 @@ namespace storm { return currentRowGroup; } - // Flag indicating if row groups are needed. - bool canHaveNondeterminism; - // Matrix builder. storm::storage::SparseMatrixBuilder<ValueType> builder; @@ -134,6 +131,9 @@ namespace storm { // Index of the current row. StateType currentRow; + + // Flag indicating if row groups are needed. + bool canHaveNondeterminism; }; public: From 6b7bf3bba7abe115540cd3d20ec56fc5255cfd13 Mon Sep 17 00:00:00 2001 From: Mavo <matthias.volk@rwth-aachen.de> Date: Tue, 11 Oct 2016 22:50:16 +0200 Subject: [PATCH 17/65] Introduced heuristic depth with distance from initial state Former-commit-id: 1b94ebc4f960f126b5338cdb0cee75aea0a42f1a --- src/builder/DftExplorationHeuristic.cpp | 76 +++++++++++++++++++ src/builder/DftExplorationHeuristic.h | 44 +++++++++++ src/builder/ExplicitDFTModelBuilderApprox.cpp | 33 ++++---- src/builder/ExplicitDFTModelBuilderApprox.h | 31 ++++---- src/generator/DftNextStateGenerator.cpp | 49 +++--------- src/generator/DftNextStateGenerator.h | 21 ----- src/modelchecker/dft/DFTModelChecker.cpp | 2 +- src/storage/dft/DFTState.cpp | 9 ++- src/storage/dft/DFTState.h | 27 +++++-- 9 files changed, 192 insertions(+), 100 deletions(-) create mode 100644 src/builder/DftExplorationHeuristic.cpp create mode 100644 src/builder/DftExplorationHeuristic.h diff --git a/src/builder/DftExplorationHeuristic.cpp b/src/builder/DftExplorationHeuristic.cpp new file mode 100644 index 000000000..1364026a1 --- /dev/null +++ b/src/builder/DftExplorationHeuristic.cpp @@ -0,0 +1,76 @@ +#include "src/builder/DftExplorationHeuristic.h" +#include "src/adapters/CarlAdapter.h" +#include "src/utility/macros.h" +#include "src/utility/constants.h" +#include "src/exceptions/NotImplementedException.h" +#include "src/storage/dft/DFTState.h" + +#include <limits> + +namespace storm { + namespace builder { + + template<typename ValueType> + DFTExplorationHeuristic<ValueType>::DFTExplorationHeuristic() : skip(true), depth(std::numeric_limits<std::size_t>::max()), rate(storm::utility::zero<ValueType>()), exitRate(storm::utility::zero<ValueType>()) { + // Intentionally left empty + } + + template<typename ValueType> + bool DFTExplorationHeuristic<ValueType>::isSkip() const { + return skip; + } + + template<typename ValueType> + size_t DFTExplorationHeuristic<ValueType>::getDepth() const { + return depth; + } + + template<typename ValueType> + void DFTExplorationHeuristic<ValueType>::setHeuristicValues(size_t depth, ValueType rate, ValueType exitRate) { + std::cout << "Set priority: " << depth << ", old: " << this->depth << std::endl; + this->depth = depth; + // TODO Matthias: update rates and exitRates as well + this->rate = rate; + this->exitRate = exitRate; + } + + template<typename ValueType> + double DFTExplorationHeuristic<ValueType>::getPriority() const { + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Approximation works only for double."); + } + + template<> + double DFTExplorationHeuristic<double>::getPriority() const { + // TODO Matthias: change according to heuristic + if (!skip) { + // TODO Matthias: change to non-magic number + return 0; + } + //return rate/exitRate; + return depth; + } + + template<> + double DFTExplorationHeuristic<storm::RationalFunction>::getPriority() const { + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Approximation works only for double."); + /*std::cout << (rate / exitRate) << " < " << threshold << ": " << (number < threshold) << std::endl; + std::map<storm::Variable, storm::RationalNumber> mapping; + storm::RationalFunction eval(number.evaluate(mapping)); + std::cout << "Evaluated: " << eval << std::endl; + return eval < threshold;*/ + } + + template<typename ValueType> + bool compareDepth(std::shared_ptr<storm::storage::DFTState<ValueType>> stateA, std::shared_ptr<storm::storage::DFTState<ValueType>> stateB) { + return stateA->getPriority() > stateB->getPriority(); + } + + template class DFTExplorationHeuristic<double>; + template bool compareDepth(std::shared_ptr<storm::storage::DFTState<double>>, std::shared_ptr<storm::storage::DFTState<double>>); + +#ifdef STORM_HAVE_CARL + template class DFTExplorationHeuristic<storm::RationalFunction>; + template bool compareDepth(std::shared_ptr<storm::storage::DFTState<storm::RationalFunction>>, std::shared_ptr<storm::storage::DFTState<storm::RationalFunction>>); +#endif + } +} diff --git a/src/builder/DftExplorationHeuristic.h b/src/builder/DftExplorationHeuristic.h new file mode 100644 index 000000000..59c35f812 --- /dev/null +++ b/src/builder/DftExplorationHeuristic.h @@ -0,0 +1,44 @@ +#ifndef STORM_BUILDER_DFTEXPLORATIONHEURISTIC_H_ +#define STORM_BUILDER_DFTEXPLORATIONHEURISTIC_H_ + +#include <memory> +#include <algorithm> + +namespace storm { + + // Forward declaration + namespace storage { + template<typename ValueType> + class DFTState; + } + + namespace builder { + + template<typename ValueType> + class DFTExplorationHeuristic { + + public: + DFTExplorationHeuristic(); + + void setHeuristicValues(size_t depth, ValueType rate, ValueType exitRate); + + bool isSkip() const; + + size_t getDepth() const; + + double getPriority() const; + + private: + bool skip; + size_t depth; + ValueType rate; + ValueType exitRate; + + }; + + template<typename ValueType> + bool compareDepth(std::shared_ptr<storm::storage::DFTState<ValueType>> stateA, std::shared_ptr<storm::storage::DFTState<ValueType>> stateB); + } +} + +#endif /* STORM_BUILDER_DFTEXPLORATIONHEURISTIC_H_ */ diff --git a/src/builder/ExplicitDFTModelBuilderApprox.cpp b/src/builder/ExplicitDFTModelBuilderApprox.cpp index 0729fec59..3dedfd01d 100644 --- a/src/builder/ExplicitDFTModelBuilderApprox.cpp +++ b/src/builder/ExplicitDFTModelBuilderApprox.cpp @@ -25,15 +25,16 @@ namespace storm { template<typename ValueType, typename StateType> ExplicitDFTModelBuilderApprox<ValueType, StateType>::ExplicitDFTModelBuilderApprox(storm::storage::DFT<ValueType> const& dft, storm::storage::DFTIndependentSymmetries const& symmetries, bool enableDC) : dft(dft), stateGenerationInfo(std::make_shared<storm::storage::DFTStateGenerationInfo>(dft.buildStateGenerationInfo(symmetries))), enableDC(enableDC), generator(dft, *stateGenerationInfo, enableDC, mergeFailedStates), matrixBuilder(!generator.isDeterministicModel()), stateStorage(((dft.stateVectorSize() / 64) + 1) * 64) { // stateVectorSize is bound for size of bitvector + + // Compare states by their distance from the initial state + // TODO Matthias: customize + statesToExplore = std::priority_queue<DFTStatePointer, std::deque<DFTStatePointer>, std::function<bool(DFTStatePointer, DFTStatePointer)>>(&storm::builder::compareDepth<ValueType>); } template<typename ValueType, typename StateType> - void ExplicitDFTModelBuilderApprox<ValueType, StateType>::buildModel(LabelOptions const& labelOpts, bool firstTime, double approximationError) { + void ExplicitDFTModelBuilderApprox<ValueType, StateType>::buildModel(LabelOptions const& labelOpts, bool firstTime, double approximationThreshold) { STORM_LOG_TRACE("Generating DFT state space"); - // Initialize - generator.setApproximationError(approximationError); - if (firstTime) { // Initialize modelComponents.markovianStates = storm::storage::BitVector(INITIAL_BITVECTOR_SIZE); @@ -72,7 +73,7 @@ namespace storm { initializeNextIteration(); } - exploreStateSpace(); + exploreStateSpace(approximationThreshold); size_t stateSize = stateStorage.getNumberOfStates() + (mergeFailedStates ? 1 : 0); modelComponents.markovianStates.resize(stateSize); @@ -169,7 +170,7 @@ namespace storm { size_t index = modelComponents.markovianStates.getNextSetIndex(0); while (index < modelComponents.markovianStates.size()) { markovianStatesNew.set(indexRemapping[index], false); - index = modelComponents.markovianStates.getNextSetIndex(index); + index = modelComponents.markovianStates.getNextSetIndex(index+1); } STORM_LOG_ASSERT(modelComponents.markovianStates.size() - modelComponents.markovianStates.getNumberOfSetBits() == markovianStatesNew.getNumberOfSetBits(), "Remapping of markovian states is wrong."); STORM_LOG_ASSERT(markovianStatesNew.size() == nrStates, "No. of states does not coincide with markovian size."); @@ -201,23 +202,24 @@ namespace storm { STORM_LOG_ASSERT(matrixBuilder.getCurrentRowGroup() == nrExpandedStates, "Row group size does not match."); - // Explore all skipped states now + // Push skipped states to explore queue + // TODO Matthias: remove for (auto const& skippedState : skippedStates) { - skippedState.second->setSkip(false); - statesToExplore.push_front(skippedState.second); + statesToExplore.push(skippedState.second); } skippedStates.clear(); } template<typename ValueType, typename StateType> - void ExplicitDFTModelBuilderApprox<ValueType, StateType>::exploreStateSpace() { + void ExplicitDFTModelBuilderApprox<ValueType, StateType>::exploreStateSpace(double approximationThreshold) { bool explorationFinished = false; size_t pseudoStatesToCheck = 0; while (!explorationFinished) { // Get the first state in the queue - DFTStatePointer currentState = statesToExplore.front(); + DFTStatePointer currentState = statesToExplore.top(); + STORM_LOG_ASSERT(stateStorage.stateToId.contains(currentState->status()), "State is not contained in state storage."); STORM_LOG_ASSERT(stateStorage.stateToId.getValue(currentState->status()) == currentState->getId(), "Ids of states do not coincide."); - statesToExplore.pop_front(); + statesToExplore.pop(); // Remember that the current row group was actually filled with the transitions of a different state matrixBuilder.setRemapping(currentState->getId()); @@ -227,7 +229,8 @@ namespace storm { // Try to explore the next state generator.load(currentState); - if (currentState->isSkip()) { + STORM_LOG_TRACE("Priority of state " <<currentState->getId() << ": " << currentState->getPriority()); + if (currentState->getPriority() > approximationThreshold) { // Skip the current state STORM_LOG_TRACE("Skip expansion of state: " << dft.getStateString(currentState)); setMarkovian(true); @@ -464,7 +467,7 @@ namespace storm { stateId = state->getId(); stateStorage.stateToId.setOrAdd(state->status(), stateId); STORM_LOG_TRACE("Now create state " << dft.getStateString(state) << " with id " << stateId); - statesToExplore.push_front(state); + statesToExplore.push(state); } } else { // State does not exist yet @@ -483,7 +486,7 @@ namespace storm { stateId = stateStorage.stateToId.findOrAdd(state->status(), state->getId()); STORM_LOG_ASSERT(stateId == state->getId(), "Ids do not match."); STORM_LOG_TRACE("New state: " << dft.getStateString(state)); - statesToExplore.push_front(state); + statesToExplore.push(state); // Reserve one slot for the new state in the remapping matrixBuilder.stateRemapping.push_back(0); diff --git a/src/builder/ExplicitDFTModelBuilderApprox.h b/src/builder/ExplicitDFTModelBuilderApprox.h index b8658c6ce..65575735f 100644 --- a/src/builder/ExplicitDFTModelBuilderApprox.h +++ b/src/builder/ExplicitDFTModelBuilderApprox.h @@ -1,14 +1,15 @@ #ifndef EXPLICITDFTMODELBUILDERAPPROX_H #define EXPLICITDFTMODELBUILDERAPPROX_H -#include <src/models/sparse/StateLabeling.h> -#include <src/models/sparse/StandardRewardModel.h> -#include <src/models/sparse/Model.h> +#include "src/builder/DftExplorationHeuristic.h" +#include "src/models/sparse/StateLabeling.h" +#include "src/models/sparse/StandardRewardModel.h" +#include "src/models/sparse/Model.h" #include "src/generator/DftNextStateGenerator.h" -#include <src/storage/SparseMatrix.h> +#include "src/storage/SparseMatrix.h" #include "src/storage/sparse/StateStorage.h" -#include <src/storage/dft/DFT.h> -#include <src/storage/dft/SymmetricUnits.h> +#include "src/storage/dft/DFT.h" +#include "src/storage/dft/SymmetricUnits.h" #include <boost/container/flat_set.hpp> #include <boost/optional/optional.hpp> #include <stack> @@ -156,11 +157,11 @@ namespace storm { /*! * Build model from DFT. * - * @param labelOpts Options for labeling. - * @param firstTime Flag indicating if the model is built for the first time or rebuilt. - * @param approximationError Error allowed for approximation. + * @param labelOpts Options for labeling. + * @param firstTime Flag indicating if the model is built for the first time or rebuilt. + * @param approximationThreshold Threshold determining when to skip exploring states. */ - void buildModel(LabelOptions const& labelOpts, bool firstTime, double approximationError = 0.0); + void buildModel(LabelOptions const& labelOpts, bool firstTime, double approximationThreshold = 0.0); /*! * Get the built model. @@ -182,8 +183,10 @@ namespace storm { /*! * Explore state space of DFT. + * + * @param approximationThreshold Threshold to determine when to skip states. */ - void exploreStateSpace(); + void exploreStateSpace(double approximationThreshold); /*! * Initialize the matrix for a refinement iteration. @@ -237,7 +240,6 @@ namespace storm { */ std::shared_ptr<storm::models::sparse::Model<ValueType>> createModel(bool copy); - // Initial size of the bitvector. const size_t INITIAL_BITVECTOR_SIZE = 20000; // Offset used for pseudo states. @@ -280,14 +282,15 @@ namespace storm { // Internal information about the states that were explored. storm::storage::sparse::StateStorage<StateType> stateStorage; - // A set of states that still need to be explored. - std::deque<DFTStatePointer> statesToExplore; + // A pniority queue of states that still need to be explored. + std::priority_queue<DFTStatePointer, std::deque<DFTStatePointer>, std::function<bool(DFTStatePointer, DFTStatePointer)>> statesToExplore; // Holds all skipped states which were not yet expanded. More concretely it is a mapping from matrix indices // to the corresponding skipped states. // Notice that we need an ordered map here to easily iterate in increasing order over state ids. std::map<StateType, DFTStatePointer> skippedStates; }; + } } diff --git a/src/generator/DftNextStateGenerator.cpp b/src/generator/DftNextStateGenerator.cpp index 119ff9342..31d17fbe0 100644 --- a/src/generator/DftNextStateGenerator.cpp +++ b/src/generator/DftNextStateGenerator.cpp @@ -20,6 +20,7 @@ namespace storm { template<typename ValueType, typename StateType> std::vector<StateType> DftNextStateGenerator<ValueType, StateType>::getInitialStates(StateToIdCallback const& stateToIdCallback) { DFTStatePointer initialState = std::make_shared<storm::storage::DFTState<ValueType>>(mDft, mStateGenerationInfo, 0); + initialState->setHeuristicValues(0, storm::utility::zero<ValueType>(), storm::utility::zero<ValueType>()); // Register initial state StateType id = stateToIdCallback(initialState); @@ -75,7 +76,7 @@ namespace storm { STORM_LOG_ASSERT(!mDft.hasFailed(state), "Dft has failed."); // Construct new state as copy from original one - DFTStatePointer newState = std::make_shared<storm::storage::DFTState<ValueType>>(*state); + DFTStatePointer newState = state->copy(); std::pair<std::shared_ptr<storm::storage::DFTBE<ValueType> const>, bool> nextBEPair = newState->letNextBEFail(currentFailable); std::shared_ptr<storm::storage::DFTBE<ValueType> const>& nextBE = nextBEPair.first; STORM_LOG_ASSERT(nextBE, "NextBE is null."); @@ -150,15 +151,17 @@ namespace storm { if (!storm::utility::isOne(probability)) { // Add transition to state where dependency was unsuccessful - DFTStatePointer unsuccessfulState = std::make_shared<storm::storage::DFTState<ValueType>>(*state); + DFTStatePointer unsuccessfulState = state->copy(); unsuccessfulState->letDependencyBeUnsuccessful(currentFailable); // Add state StateType unsuccessfulStateId = stateToIdCallback(unsuccessfulState); ValueType remainingProbability = storm::utility::one<ValueType>() - probability; choice.addProbability(unsuccessfulStateId, remainingProbability); STORM_LOG_TRACE("Added transition to " << unsuccessfulStateId << " with remaining probability " << remainingProbability); + unsuccessfulState->setHeuristicValues(state, remainingProbability, storm::utility::one<ValueType>()); } result.addChoice(std::move(choice)); + newState->setHeuristicValues(state, probability, storm::utility::one<ValueType>()); } else { // Failure is due to "normal" BE failure // Set failure rate according to activation @@ -171,14 +174,7 @@ namespace storm { STORM_LOG_ASSERT(!storm::utility::isZero(rate), "Rate is 0."); choice.addProbability(newStateId, rate); STORM_LOG_TRACE("Added transition to " << newStateId << " with " << (isActive ? "active" : "passive") << " failure rate " << rate); - - // Check if new state needs expansion for approximation - if (approximationError > 0.0) { - if (checkSkipState(newState, rate, choice.getTotalMass(), approximationError)) { - STORM_LOG_TRACE("Will skip state " << newStateId); - newState->setSkip(true); - } - } + newState->setHeuristicValues(state, rate, choice.getTotalMass()); } ++currentFailable; @@ -213,37 +209,10 @@ namespace storm { return result; } - template<typename ValueType, typename StateType> - void DftNextStateGenerator<ValueType, StateType>::setApproximationError(double approximationError) { - this->approximationError = approximationError; - } - - template<typename ValueType, typename StateType> - bool DftNextStateGenerator<ValueType, StateType>::checkSkipState(DFTStatePointer const& state, ValueType rate, ValueType exitRate, double approximationError) { - STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Approximation works only for double."); - } - - template<> - bool DftNextStateGenerator<double>::checkSkipState(DFTStatePointer const& state, double rate, double exitRate, double approximationError) { - if (mDft.hasFailed(state) || mDft.isFailsafe(state) || (state->nrFailableDependencies() == 0 && state->nrFailableBEs() == 0)) { - // Do not skip absorbing state - return false; - } - // Skip if the rate to reach this state is low compared to all other outgoing rates from the predecessor - return rate/exitRate < approximationError; - } - - template<> - bool DftNextStateGenerator<storm::RationalFunction>::checkSkipState(DFTStatePointer const& state, storm::RationalFunction rate, storm::RationalFunction exitRate, double approximationError) { - STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Approximation works only for double."); - /*std::cout << (rate / exitRate) << " < " << threshold << ": " << (number < threshold) << std::endl; - std::map<storm::Variable, storm::RationalNumber> mapping; - storm::RationalFunction eval(number.evaluate(mapping)); - std::cout << "Evaluated: " << eval << std::endl; - return eval < threshold;*/ - } - template class DftNextStateGenerator<double>; + +#ifdef STORM_HAVE_CARL template class DftNextStateGenerator<storm::RationalFunction>; +#endif } } diff --git a/src/generator/DftNextStateGenerator.h b/src/generator/DftNextStateGenerator.h index 6fa815e44..8fdfbf471 100644 --- a/src/generator/DftNextStateGenerator.h +++ b/src/generator/DftNextStateGenerator.h @@ -42,13 +42,6 @@ namespace storm { */ StateBehavior<ValueType, StateType> createMergeFailedState(StateToIdCallback const& stateToIdCallback); - /*! - * Set a new value for the allowed approximation error. - * - * @param approximationError Allowed approximation error. - */ - void setApproximationError(double approximationError); - private: // The dft used for the generation of next states. @@ -72,20 +65,6 @@ namespace storm { // Flag indicating if the model is deterministic. bool deterministicModel = false; - // Allowed approximation error. - double approximationError = 0.0; - - /*! - * Check if the given state should be skipped for expansion. - * - * @param state State to check for expansion - * @param rate Rate of current state - * @param exitRate Exit rates of all outgoing transitions - * @param approximationError Allowed approximation error - * - * @return True, if the given state should be skipped. - */ - bool checkSkipState(DFTStatePointer const& state, ValueType rate, ValueType exitRate, double approximationError); }; } diff --git a/src/modelchecker/dft/DFTModelChecker.cpp b/src/modelchecker/dft/DFTModelChecker.cpp index 7ac2f1a8a..205e658f3 100644 --- a/src/modelchecker/dft/DFTModelChecker.cpp +++ b/src/modelchecker/dft/DFTModelChecker.cpp @@ -162,7 +162,7 @@ namespace storm { STORM_LOG_INFO("Building model..."); // TODO Matthias refine model using existing model and MC results currentApproximationError = pow(0.1, iteration) * approximationError; - builder.buildModel(labeloptions, iteration < 1, currentApproximationError); + builder.buildModel(labeloptions, iteration < 1, iteration); // TODO Matthias: possible to do bisimulation on approximated model and not on concrete one? diff --git a/src/storage/dft/DFTState.cpp b/src/storage/dft/DFTState.cpp index a38bd21b8..5e84b0c56 100644 --- a/src/storage/dft/DFTState.cpp +++ b/src/storage/dft/DFTState.cpp @@ -6,7 +6,7 @@ namespace storm { namespace storage { template<typename ValueType> - DFTState<ValueType>::DFTState(DFT<ValueType> const& dft, DFTStateGenerationInfo const& stateGenerationInfo, size_t id) : mStatus(dft.stateVectorSize()), mId(id), mDft(dft), mStateGenerationInfo(stateGenerationInfo) { + DFTState<ValueType>::DFTState(DFT<ValueType> const& dft, DFTStateGenerationInfo const& stateGenerationInfo, size_t id) : mStatus(dft.stateVectorSize()), mId(id), mDft(dft), mStateGenerationInfo(stateGenerationInfo), exploreHeuristic() { // Initialize uses for(size_t spareId : mDft.getSpareIndices()) { @@ -55,6 +55,13 @@ namespace storm { } } + template<typename ValueType> + std::shared_ptr<DFTState<ValueType>> DFTState<ValueType>::copy() const { + std::shared_ptr<DFTState<ValueType>> stateCopy = std::make_shared<storm::storage::DFTState<ValueType>>(*this); + stateCopy->exploreHeuristic = storm::builder::DFTExplorationHeuristic<ValueType>(); + return stateCopy; + } + template<typename ValueType> DFTElementState DFTState<ValueType>::getElementState(size_t id) const { return static_cast<DFTElementState>(getElementStateInt(id)); diff --git a/src/storage/dft/DFTState.h b/src/storage/dft/DFTState.h index c87008c9c..cd2b9eb0f 100644 --- a/src/storage/dft/DFTState.h +++ b/src/storage/dft/DFTState.h @@ -1,8 +1,9 @@ #ifndef DFTSTATE_H #define DFTSTATE_H -#include "../BitVector.h" -#include "DFTElementState.h" +#include "src/storage/dft/DFTElementState.h" +#include "src/storage/BitVector.h" +#include "src/builder/DftExplorationHeuristic.h" #include <sstream> #include <memory> @@ -31,7 +32,7 @@ namespace storm { bool mValid = true; const DFT<ValueType>& mDft; const DFTStateGenerationInfo& mStateGenerationInfo; - bool mSkip = false; + storm::builder::DFTExplorationHeuristic<ValueType> exploreHeuristic; public: DFTState(DFT<ValueType> const& dft, DFTStateGenerationInfo const& stateGenerationInfo, size_t id); @@ -40,7 +41,9 @@ namespace storm { * Construct state from underlying bitvector. */ DFTState(storm::storage::BitVector const& status, DFT<ValueType> const& dft, DFTStateGenerationInfo const& stateGenerationInfo, size_t id); - + + std::shared_ptr<DFTState<ValueType>> copy() const; + DFTElementState getElementState(size_t id) const; DFTDependencyState getDependencyState(size_t id) const; @@ -97,12 +100,20 @@ namespace storm { return !mValid; } - void setSkip(bool skip) { - mSkip = skip; + void setHeuristicValues(size_t depth, ValueType rate, ValueType exitRate) { + exploreHeuristic.setHeuristicValues(depth, rate, exitRate); + } + + void setHeuristicValues(std::shared_ptr<storm::storage::DFTState<ValueType>> oldState, ValueType rate, ValueType exitRate) { + if (hasFailed(mDft.getTopLevelIndex()) || isFailsafe(mDft.getTopLevelIndex()) || (nrFailableDependencies() == 0 && nrFailableBEs() == 0)) { + // Do not skip absorbing state + exploreHeuristic.setNotSkip(); + } + exploreHeuristic.setHeuristicValues(oldState->exploreHeuristic.getDepth() + 1, rate, exitRate); } - bool isSkip() const { - return mSkip; + double getPriority() const { + return exploreHeuristic.getPriority(); } storm::storage::BitVector const& status() const { From 53821d3d84a50419f1ed8566fa8bfec64c6bc6ff Mon Sep 17 00:00:00 2001 From: Mavo <matthias.volk@rwth-aachen.de> Date: Tue, 11 Oct 2016 22:58:00 +0200 Subject: [PATCH 18/65] Added settings for approximation heuristic Former-commit-id: 40267add31426b65c1232ad11a82b12d0e1a0e0d --- src/builder/DftExplorationHeuristic.cpp | 25 ++++++++++++++----- src/builder/DftExplorationHeuristic.h | 10 +++++++- src/builder/ExplicitDFTModelBuilderApprox.cpp | 5 ++-- src/builder/ExplicitDFTModelBuilderApprox.h | 3 +++ src/settings/modules/DFTSettings.cpp | 18 +++++++++++-- src/settings/modules/DFTSettings.h | 9 +++++++ src/storage/dft/DFTState.h | 4 +++ 7 files changed, 63 insertions(+), 11 deletions(-) diff --git a/src/builder/DftExplorationHeuristic.cpp b/src/builder/DftExplorationHeuristic.cpp index 1364026a1..74937d4b1 100644 --- a/src/builder/DftExplorationHeuristic.cpp +++ b/src/builder/DftExplorationHeuristic.cpp @@ -16,10 +16,27 @@ namespace storm { } template<typename ValueType> - bool DFTExplorationHeuristic<ValueType>::isSkip() const { - return skip; + bool DFTExplorationHeuristic<ValueType>::isSkip(double approximationThreshold, ApproximationHeuristic heuristic) const { + if (!skip) { + return false; + } + switch (heuristic) { + case ApproximationHeuristic::NONE: + return false; + case ApproximationHeuristic::DEPTH: + return depth > approximationThreshold; + case ApproximationHeuristic::RATERATIO: + // TODO Matthias: implement + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Heuristic rate ration does not work."); + } } + template<typename ValueType> + void DFTExplorationHeuristic<ValueType>::setNotSkip() { + skip = false; + } + + template<typename ValueType> size_t DFTExplorationHeuristic<ValueType>::getDepth() const { return depth; @@ -42,10 +59,6 @@ namespace storm { template<> double DFTExplorationHeuristic<double>::getPriority() const { // TODO Matthias: change according to heuristic - if (!skip) { - // TODO Matthias: change to non-magic number - return 0; - } //return rate/exitRate; return depth; } diff --git a/src/builder/DftExplorationHeuristic.h b/src/builder/DftExplorationHeuristic.h index 59c35f812..a9958443a 100644 --- a/src/builder/DftExplorationHeuristic.h +++ b/src/builder/DftExplorationHeuristic.h @@ -14,6 +14,11 @@ namespace storm { namespace builder { + /*! + * Enum representing the heuristic used for deciding which states to expand. + */ + enum class ApproximationHeuristic { NONE, DEPTH, RATERATIO }; + template<typename ValueType> class DFTExplorationHeuristic { @@ -22,13 +27,16 @@ namespace storm { void setHeuristicValues(size_t depth, ValueType rate, ValueType exitRate); - bool isSkip() const; + bool isSkip(double approximationThreshold, ApproximationHeuristic heuristic) const; + + void setNotSkip(); size_t getDepth() const; double getPriority() const; private: + bool skip; size_t depth; ValueType rate; diff --git a/src/builder/ExplicitDFTModelBuilderApprox.cpp b/src/builder/ExplicitDFTModelBuilderApprox.cpp index 3dedfd01d..9ed09eccb 100644 --- a/src/builder/ExplicitDFTModelBuilderApprox.cpp +++ b/src/builder/ExplicitDFTModelBuilderApprox.cpp @@ -26,6 +26,8 @@ namespace storm { ExplicitDFTModelBuilderApprox<ValueType, StateType>::ExplicitDFTModelBuilderApprox(storm::storage::DFT<ValueType> const& dft, storm::storage::DFTIndependentSymmetries const& symmetries, bool enableDC) : dft(dft), stateGenerationInfo(std::make_shared<storm::storage::DFTStateGenerationInfo>(dft.buildStateGenerationInfo(symmetries))), enableDC(enableDC), generator(dft, *stateGenerationInfo, enableDC, mergeFailedStates), matrixBuilder(!generator.isDeterministicModel()), stateStorage(((dft.stateVectorSize() / 64) + 1) * 64) { // stateVectorSize is bound for size of bitvector + heuristic = storm::settings::getModule<storm::settings::modules::DFTSettings>().getApproximationHeuristic(); + // Compare states by their distance from the initial state // TODO Matthias: customize statesToExplore = std::priority_queue<DFTStatePointer, std::deque<DFTStatePointer>, std::function<bool(DFTStatePointer, DFTStatePointer)>>(&storm::builder::compareDepth<ValueType>); @@ -229,8 +231,7 @@ namespace storm { // Try to explore the next state generator.load(currentState); - STORM_LOG_TRACE("Priority of state " <<currentState->getId() << ": " << currentState->getPriority()); - if (currentState->getPriority() > approximationThreshold) { + if (currentState->isSkip(approximationThreshold, heuristic)) { // Skip the current state STORM_LOG_TRACE("Skip expansion of state: " << dft.getStateString(currentState)); setMarkovian(true); diff --git a/src/builder/ExplicitDFTModelBuilderApprox.h b/src/builder/ExplicitDFTModelBuilderApprox.h index 65575735f..cb9a444af 100644 --- a/src/builder/ExplicitDFTModelBuilderApprox.h +++ b/src/builder/ExplicitDFTModelBuilderApprox.h @@ -258,6 +258,9 @@ namespace storm { //TODO Matthias: make changeable const bool mergeFailedStates = true; + // Heuristic used for approximation + storm::builder::ApproximationHeuristic heuristic; + // Current id for new state size_t newIndex = 0; diff --git a/src/settings/modules/DFTSettings.cpp b/src/settings/modules/DFTSettings.cpp index c27009a85..1b3f0bae2 100644 --- a/src/settings/modules/DFTSettings.cpp +++ b/src/settings/modules/DFTSettings.cpp @@ -9,7 +9,6 @@ #include "src/exceptions/InvalidSettingsException.h" - namespace storm { namespace settings { namespace modules { @@ -23,6 +22,7 @@ namespace storm { const std::string DFTSettings::disableDCOptionName = "disabledc"; const std::string DFTSettings::approximationErrorOptionName = "approximation"; const std::string DFTSettings::approximationErrorOptionShortName = "approx"; + const std::string DFTSettings::approximationHeuristicOptionName = "approximationheuristic"; const std::string DFTSettings::propExpectedTimeOptionName = "expectedtime"; const std::string DFTSettings::propExpectedTimeOptionShortName = "mttf"; const std::string DFTSettings::propProbabilityOptionName = "probability"; @@ -40,6 +40,7 @@ namespace storm { this->addOption(storm::settings::OptionBuilder(moduleName, modularisationOptionName, false, "Use modularisation (not applicable for expected time).").build()); this->addOption(storm::settings::OptionBuilder(moduleName, disableDCOptionName, false, "Disable Dont Care propagation.").build()); this->addOption(storm::settings::OptionBuilder(moduleName, approximationErrorOptionName, false, "Approximation error allowed.").setShortName(approximationErrorOptionShortName).addArgument(storm::settings::ArgumentBuilder::createDoubleArgument("error", "The approximation error to use.").addValidationFunctionDouble(storm::settings::ArgumentValidators::doubleGreaterValidatorIncluding(0.0)).build()).build()); + this->addOption(storm::settings::OptionBuilder(moduleName, approximationHeuristicOptionName, false, "Set the heuristic used for approximation.").addArgument(storm::settings::ArgumentBuilder::createStringArgument("heuristic", "Sets which heuristic is used for approximation. Must be in {depth, rateratio}. Default is").setDefaultValueString("depth").addValidationFunctionString(storm::settings::ArgumentValidators::stringInListValidator({"depth", "rateratio"})).build()).build()); this->addOption(storm::settings::OptionBuilder(moduleName, propExpectedTimeOptionName, false, "Compute expected time of system failure.").setShortName(propExpectedTimeOptionShortName).build()); this->addOption(storm::settings::OptionBuilder(moduleName, propProbabilityOptionName, false, "Compute probability of system failure.").build()); this->addOption(storm::settings::OptionBuilder(moduleName, propTimeBoundOptionName, false, "Compute probability of system failure up to given timebound.").addArgument(storm::settings::ArgumentBuilder::createDoubleArgument("time", "The timebound to use.").addValidationFunctionDouble(storm::settings::ArgumentValidators::doubleGreaterValidatorExcluding(0.0)).build()).build()); @@ -78,6 +79,20 @@ namespace storm { return this->getOption(approximationErrorOptionName).getArgumentByName("error").getValueAsDouble(); } + storm::builder::ApproximationHeuristic DFTSettings::getApproximationHeuristic() const { + if (!isApproximationErrorSet() || getApproximationError() == 0.0) { + // No approximation is done + return storm::builder::ApproximationHeuristic::NONE; + } + std::string heuristicAsString = this->getOption(approximationHeuristicOptionName).getArgumentByName("heuristic").getValueAsString(); + if (heuristicAsString == "depth") { + return storm::builder::ApproximationHeuristic::DEPTH; + } else if (heuristicAsString == "rateratio") { + return storm::builder::ApproximationHeuristic::RATERATIO; + } + STORM_LOG_THROW(false, storm::exceptions::IllegalArgumentValueException, "Illegal value '" << heuristicAsString << "' set as heuristic for approximation."); + } + bool DFTSettings::usePropExpectedTime() const { return this->getOption(propExpectedTimeOptionName).getHasOptionBeenSet(); } @@ -109,7 +124,6 @@ namespace storm { #endif void DFTSettings::finalize() { - } bool DFTSettings::check() const { diff --git a/src/settings/modules/DFTSettings.h b/src/settings/modules/DFTSettings.h index bb83a7475..2aa418538 100644 --- a/src/settings/modules/DFTSettings.h +++ b/src/settings/modules/DFTSettings.h @@ -3,6 +3,7 @@ #include "storm-config.h" #include "src/settings/modules/ModuleSettings.h" +#include "src/builder/DftExplorationHeuristic.h" namespace storm { namespace settings { @@ -68,6 +69,13 @@ namespace storm { */ double getApproximationError() const; + /*! + * Retrieves the heuristic used for approximation. + * + * @return The heuristic to use. + */ + storm::builder::ApproximationHeuristic getApproximationHeuristic() const; + /*! * Retrieves whether the property expected time should be used. * @@ -135,6 +143,7 @@ namespace storm { static const std::string disableDCOptionName; static const std::string approximationErrorOptionName; static const std::string approximationErrorOptionShortName; + static const std::string approximationHeuristicOptionName; static const std::string propExpectedTimeOptionName; static const std::string propExpectedTimeOptionShortName; static const std::string propProbabilityOptionName; diff --git a/src/storage/dft/DFTState.h b/src/storage/dft/DFTState.h index cd2b9eb0f..b49727313 100644 --- a/src/storage/dft/DFTState.h +++ b/src/storage/dft/DFTState.h @@ -112,6 +112,10 @@ namespace storm { exploreHeuristic.setHeuristicValues(oldState->exploreHeuristic.getDepth() + 1, rate, exitRate); } + bool isSkip(double approximationThreshold, storm::builder::ApproximationHeuristic heuristic) { + return exploreHeuristic.isSkip(approximationThreshold, heuristic); + } + double getPriority() const { return exploreHeuristic.getPriority(); } From 2f765a9a955927dac47a71442113cd58982d1d1c Mon Sep 17 00:00:00 2001 From: Mavo <matthias.volk@rwth-aachen.de> Date: Wed, 12 Oct 2016 08:49:33 +0200 Subject: [PATCH 19/65] Expand states reached be dependencies Former-commit-id: ab22162ed579ccdb4678afeee6075a650c0bdd96 --- src/storage/dft/DFTState.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/storage/dft/DFTState.h b/src/storage/dft/DFTState.h index b49727313..617d40a7d 100644 --- a/src/storage/dft/DFTState.h +++ b/src/storage/dft/DFTState.h @@ -105,8 +105,8 @@ namespace storm { } void setHeuristicValues(std::shared_ptr<storm::storage::DFTState<ValueType>> oldState, ValueType rate, ValueType exitRate) { - if (hasFailed(mDft.getTopLevelIndex()) || isFailsafe(mDft.getTopLevelIndex()) || (nrFailableDependencies() == 0 && nrFailableBEs() == 0)) { - // Do not skip absorbing state + if (hasFailed(mDft.getTopLevelIndex()) || isFailsafe(mDft.getTopLevelIndex()) || nrFailableDependencies() > 0 || (nrFailableDependencies() == 0 && nrFailableBEs() == 0)) { + // Do not skip absorbing state or if reached by dependencies exploreHeuristic.setNotSkip(); } exploreHeuristic.setHeuristicValues(oldState->exploreHeuristic.getDepth() + 1, rate, exitRate); From 3d083a171946013598fd757c94ecd07cab8fc699 Mon Sep 17 00:00:00 2001 From: Mavo <matthias.volk@rwth-aachen.de> Date: Wed, 12 Oct 2016 13:06:59 +0200 Subject: [PATCH 20/65] Fixed bug with wrong row in case of nondeterminism Former-commit-id: 24d7bd672c3df018ab35ebc2b70190b226bbb8b9 --- src/builder/DftExplorationHeuristic.cpp | 2 -- src/builder/ExplicitDFTModelBuilderApprox.cpp | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/builder/DftExplorationHeuristic.cpp b/src/builder/DftExplorationHeuristic.cpp index 74937d4b1..727c29c37 100644 --- a/src/builder/DftExplorationHeuristic.cpp +++ b/src/builder/DftExplorationHeuristic.cpp @@ -44,9 +44,7 @@ namespace storm { template<typename ValueType> void DFTExplorationHeuristic<ValueType>::setHeuristicValues(size_t depth, ValueType rate, ValueType exitRate) { - std::cout << "Set priority: " << depth << ", old: " << this->depth << std::endl; this->depth = depth; - // TODO Matthias: update rates and exitRates as well this->rate = rate; this->exitRate = exitRate; } diff --git a/src/builder/ExplicitDFTModelBuilderApprox.cpp b/src/builder/ExplicitDFTModelBuilderApprox.cpp index 9ed09eccb..0bf848838 100644 --- a/src/builder/ExplicitDFTModelBuilderApprox.cpp +++ b/src/builder/ExplicitDFTModelBuilderApprox.cpp @@ -412,7 +412,7 @@ namespace storm { void ExplicitDFTModelBuilderApprox<ValueType, StateType>::changeMatrixLowerBound(storm::storage::SparseMatrix<ValueType> & matrix) const { // Set lower bound for skipped states for (auto it = skippedStates.begin(); it != skippedStates.end(); ++it) { - auto matrixEntry = matrix.getRow(it->first).begin(); + auto matrixEntry = matrix.getRow(it->first, 0).begin(); STORM_LOG_ASSERT(matrixEntry->getColumn() == failedStateId, "Transition has wrong target state."); // Get the lower bound by considering the failure of the BE with the highest rate // The list is sorted by rate, therefore we consider the first BE for the highest failure rate @@ -424,7 +424,7 @@ namespace storm { void ExplicitDFTModelBuilderApprox<ValueType, StateType>::changeMatrixUpperBound(storm::storage::SparseMatrix<ValueType> & matrix) const { // Set uppper bound for skipped states for (auto it = skippedStates.begin(); it != skippedStates.end(); ++it) { - auto matrixEntry = matrix.getRow(it->first).begin(); + auto matrixEntry = matrix.getRow(it->first, 0).begin(); STORM_LOG_ASSERT(matrixEntry->getColumn() == failedStateId, "Transition has wrong target state."); // Get the upper bound by considering the failure of all BEs // The used formula for the rate is 1/( 1/a + 1/b + ...) From 6faa7f0429eb1f29230f93da661bbbb5b93acfe5 Mon Sep 17 00:00:00 2001 From: Mavo <matthias.volk@rwth-aachen.de> Date: Wed, 12 Oct 2016 14:23:07 +0200 Subject: [PATCH 21/65] Abort when getting infinity in approximation Former-commit-id: 8bfda3656ac9b4ca07d2c10e56753e1b8159bcc0 --- src/modelchecker/dft/DFTModelChecker.cpp | 1 + src/utility/constants.cpp | 37 +++++++++++++++++------- src/utility/constants.h | 7 +++-- 3 files changed, 33 insertions(+), 12 deletions(-) diff --git a/src/modelchecker/dft/DFTModelChecker.cpp b/src/modelchecker/dft/DFTModelChecker.cpp index 205e658f3..692f61735 100644 --- a/src/modelchecker/dft/DFTModelChecker.cpp +++ b/src/modelchecker/dft/DFTModelChecker.cpp @@ -187,6 +187,7 @@ namespace storm { ++iteration; STORM_LOG_INFO("Result after iteration " << iteration << ": (" << std::setprecision(10) << approxResult.first << ", " << approxResult.second << ")"); + STORM_LOG_THROW(!storm::utility::isInfinity<ValueType>(approxResult.first) && !storm::utility::isInfinity<ValueType>(approxResult.second), storm::exceptions::NotSupportedException, "Approximation does not work if result might be infinity."); } while (!isApproximationSufficient(approxResult.first, approxResult.second, approximationError)); STORM_LOG_INFO("Finished approximation after " << iteration << " iteration" << (iteration > 1 ? "s." : ".")); diff --git a/src/utility/constants.cpp b/src/utility/constants.cpp index c3184c194..c787b7d91 100644 --- a/src/utility/constants.cpp +++ b/src/utility/constants.cpp @@ -40,7 +40,11 @@ namespace storm { return true; } - + template<typename ValueType> + bool isInfinity(ValueType const& a) { + return a == infinity<ValueType>(); + } + #ifdef STORM_HAVE_CARL template<> bool isOne(storm::RationalFunction const& a) { @@ -56,7 +60,7 @@ namespace storm { bool isConstant(storm::RationalFunction const& a) { return a.isConstant(); } - + template<> bool isOne(storm::Polynomial const& a) { return a.isOne(); @@ -71,13 +75,19 @@ namespace storm { bool isConstant(storm::Polynomial const& a) { return a.isConstant(); } - + template<> storm::RationalFunction infinity() { // FIXME: this should be treated more properly. return storm::RationalFunction(-1.0); } - + + template<> + bool isInfinity(storm::RationalFunction const& a) { + // FIXME: this should be treated more properly. + return a == infinity<storm::RationalFunction>(); + } + template<> bool isOne(storm::RationalNumber const& a) { return carl::isOne(a); @@ -166,7 +176,8 @@ namespace storm { template bool isOne(double const& value); template bool isZero(double const& value); template bool isConstant(double const& value); - + template bool isInfinity(double const& value); + template double one(); template double zero(); template double infinity(); @@ -182,7 +193,8 @@ namespace storm { template bool isOne(float const& value); template bool isZero(float const& value); template bool isConstant(float const& value); - + template bool isInfinity(float const& value); + template float one(); template float zero(); template float infinity(); @@ -198,7 +210,8 @@ namespace storm { template bool isOne(int const& value); template bool isZero(int const& value); template bool isConstant(int const& value); - + template bool isInfinity(int const& value); + template int one(); template int zero(); template int infinity(); @@ -214,6 +227,7 @@ namespace storm { template bool isOne(storm::storage::sparse::state_type const& value); template bool isZero(storm::storage::sparse::state_type const& value); template bool isConstant(storm::storage::sparse::state_type const& value); + template bool isInfinity(storm::storage::sparse::state_type const& value); template uint32_t one(); template uint32_t zero(); @@ -235,7 +249,8 @@ namespace storm { template bool isOne(RationalFunction const& value); template bool isZero(RationalFunction const& value); template bool isConstant(RationalFunction const& value); - + template bool isInfinity(RationalFunction const& value); + template RationalFunction one(); template RationalFunction zero(); template storm::RationalFunction infinity(); @@ -251,7 +266,8 @@ namespace storm { template bool isOne(RationalNumber const& value); template bool isZero(RationalNumber const& value); template bool isConstant(RationalNumber const& value); - + template bool isInfinity(RationalNumber const& value); + template RationalNumber one(); template RationalNumber zero(); @@ -261,7 +277,8 @@ namespace storm { template bool isOne(Interval const& value); template bool isZero(Interval const& value); template bool isConstant(Interval const& value); - + template bool isInfinity(Interval const& value); + template Interval one(); template Interval zero(); diff --git a/src/utility/constants.h b/src/utility/constants.h index 80222b74f..b80b61777 100644 --- a/src/utility/constants.h +++ b/src/utility/constants.h @@ -38,7 +38,10 @@ namespace storm { template<typename ValueType> bool isConstant(ValueType const& a); - + + template<typename ValueType> + bool isInfinity(ValueType const& a); + template<typename ValueType> ValueType pow(ValueType const& value, uint_fast64_t exponent); @@ -56,4 +59,4 @@ namespace storm { } } -#endif /* STORM_UTILITY_CONSTANTS_H_ */ \ No newline at end of file +#endif /* STORM_UTILITY_CONSTANTS_H_ */ From a419cb0d8096ab8d4d0e985ecca570ea3290c06f Mon Sep 17 00:00:00 2001 From: Mavo <matthias.volk@rwth-aachen.de> Date: Wed, 12 Oct 2016 18:18:30 +0200 Subject: [PATCH 22/65] Fixed computing rates for lower and upper bound Former-commit-id: 89846a97885f5119d3082dce17982576755c0f4c --- src/builder/ExplicitDFTModelBuilder.cpp | 2 +- src/builder/ExplicitDFTModelBuilderApprox.cpp | 15 +++- src/generator/DftNextStateGenerator.cpp | 2 +- src/modelchecker/dft/DFTModelChecker.cpp | 10 ++- src/storage/dft/DFT.h | 4 +- src/storage/dft/DFTState.cpp | 72 ++++++++++++++----- src/storage/dft/DFTState.h | 71 ++++++++++++++---- 7 files changed, 136 insertions(+), 40 deletions(-) diff --git a/src/builder/ExplicitDFTModelBuilder.cpp b/src/builder/ExplicitDFTModelBuilder.cpp index 46797aa23..9481e3392 100644 --- a/src/builder/ExplicitDFTModelBuilder.cpp +++ b/src/builder/ExplicitDFTModelBuilder.cpp @@ -290,7 +290,7 @@ namespace storm { if (mDft.hasRepresentant(nextBE->id())) { // Active must be checked for the state we are coming from as this state is responsible for the // rate and not the new state we are going to - isActive = state->isActive(mDft.getRepresentant(nextBE->id())->id()); + isActive = state->isActive(mDft.getRepresentant(nextBE->id())); } ValueType rate = isActive ? nextBE->activeFailureRate() : nextBE->passiveFailureRate(); STORM_LOG_ASSERT(!storm::utility::isZero(rate), "Rate is 0."); diff --git a/src/builder/ExplicitDFTModelBuilderApprox.cpp b/src/builder/ExplicitDFTModelBuilderApprox.cpp index 0bf848838..1f702d270 100644 --- a/src/builder/ExplicitDFTModelBuilderApprox.cpp +++ b/src/builder/ExplicitDFTModelBuilderApprox.cpp @@ -414,9 +414,15 @@ namespace storm { for (auto it = skippedStates.begin(); it != skippedStates.end(); ++it) { auto matrixEntry = matrix.getRow(it->first, 0).begin(); STORM_LOG_ASSERT(matrixEntry->getColumn() == failedStateId, "Transition has wrong target state."); - // Get the lower bound by considering the failure of the BE with the highest rate - // The list is sorted by rate, therefore we consider the first BE for the highest failure rate - matrixEntry->setValue(it->second->getFailableBERate(0)); + // Get the lower bound by considering the failure of all possible BEs + ValueType newRate = storm::utility::zero<ValueType>(); + for (size_t index = 0; index < it->second->nrFailableBEs(); ++index) { + newRate += it->second->getFailableBERate(index); + } + for (size_t index = 0; index < it->second->nrNotFailableBEs(); ++index) { + newRate += it->second->getNotFailableBERate(index); + } + matrixEntry->setValue(newRate); } } @@ -433,6 +439,9 @@ namespace storm { for (size_t index = 0; index < it->second->nrFailableBEs(); ++index) { newRate += storm::utility::one<ValueType>() / it->second->getFailableBERate(index); } + for (size_t index = 0; index < it->second->nrNotFailableBEs(); ++index) { + newRate += storm::utility::one<ValueType>() / it->second->getNotFailableBERate(index); + } newRate = storm::utility::one<ValueType>() / newRate; matrixEntry->setValue(newRate); } diff --git a/src/generator/DftNextStateGenerator.cpp b/src/generator/DftNextStateGenerator.cpp index 31d17fbe0..e61e8e331 100644 --- a/src/generator/DftNextStateGenerator.cpp +++ b/src/generator/DftNextStateGenerator.cpp @@ -168,7 +168,7 @@ namespace storm { bool isActive = true; if (mDft.hasRepresentant(nextBE->id())) { // Active must be checked for the state we are coming from as this state is responsible for the rate - isActive = state->isActive(mDft.getRepresentant(nextBE->id())->id()); + isActive = state->isActive(mDft.getRepresentant(nextBE->id())); } ValueType rate = isActive ? nextBE->activeFailureRate() : nextBE->passiveFailureRate(); STORM_LOG_ASSERT(!storm::utility::isZero(rate), "Rate is 0."); diff --git a/src/modelchecker/dft/DFTModelChecker.cpp b/src/modelchecker/dft/DFTModelChecker.cpp index 692f61735..26f2033d6 100644 --- a/src/modelchecker/dft/DFTModelChecker.cpp +++ b/src/modelchecker/dft/DFTModelChecker.cpp @@ -147,6 +147,8 @@ namespace storm { buildingTime += buildingEnd - buildingStart; if (approximationError > 0.0) { + // Comparator for checking the error of the approximation + storm::utility::ConstantsComparator<ValueType> comparator; // Build approximate Markov Automata for lower and upper bound double currentApproximationError = approximationError; approximation_result approxResult = std::make_pair(storm::utility::zero<ValueType>(), storm::utility::zero<ValueType>()); @@ -173,7 +175,9 @@ namespace storm { // Check lower bound std::unique_ptr<storm::modelchecker::CheckResult> result = checkModel(model, formula); result->filter(storm::modelchecker::ExplicitQualitativeCheckResult(model->getInitialStates())); - approxResult.first = result->asExplicitQuantitativeCheckResult<ValueType>().getValueMap().begin()->second; + ValueType newResult = result->asExplicitQuantitativeCheckResult<ValueType>().getValueMap().begin()->second; + STORM_LOG_ASSERT(iteration == 0 || !comparator.isLess(newResult, approxResult.first), "New under-approximation " << newResult << " is smaller than old result " << approxResult.first); + approxResult.first = newResult; // Build model for upper bound STORM_LOG_INFO("Getting model for upper bound..."); @@ -183,7 +187,9 @@ namespace storm { // Check upper bound result = checkModel(model, formula); result->filter(storm::modelchecker::ExplicitQualitativeCheckResult(model->getInitialStates())); - approxResult.second = result->asExplicitQuantitativeCheckResult<ValueType>().getValueMap().begin()->second; + newResult = result->asExplicitQuantitativeCheckResult<ValueType>().getValueMap().begin()->second; + STORM_LOG_ASSERT(iteration == 0 || !comparator.isLess(approxResult.second, newResult), "New over-approximation " << newResult << " is greater than old result " << approxResult.second); + approxResult.second = newResult; ++iteration; STORM_LOG_INFO("Result after iteration " << iteration << ": (" << std::setprecision(10) << approxResult.first << ", " << approxResult.second << ")"); diff --git a/src/storage/dft/DFT.h b/src/storage/dft/DFT.h index c12204e06..7620a059a 100644 --- a/src/storage/dft/DFT.h +++ b/src/storage/dft/DFT.h @@ -216,9 +216,9 @@ namespace storm { return mRepresentants.find(id) != mRepresentants.end(); } - DFTElementCPointer getRepresentant(size_t id) const { + size_t getRepresentant(size_t id) const { STORM_LOG_ASSERT(hasRepresentant(id), "Element has no representant."); - return getElement(mRepresentants.find(id)->second); + return mRepresentants.find(id)->second; } bool hasFailed(DFTStatePointer const& state) const { diff --git a/src/storage/dft/DFTState.cpp b/src/storage/dft/DFTState.cpp index 5e84b0c56..b292e322d 100644 --- a/src/storage/dft/DFTState.cpp +++ b/src/storage/dft/DFTState.cpp @@ -9,18 +9,29 @@ namespace storm { DFTState<ValueType>::DFTState(DFT<ValueType> const& dft, DFTStateGenerationInfo const& stateGenerationInfo, size_t id) : mStatus(dft.stateVectorSize()), mId(id), mDft(dft), mStateGenerationInfo(stateGenerationInfo), exploreHeuristic() { // Initialize uses - for(size_t spareId : mDft.getSpareIndices()) { + for(size_t spareId : mDft.getSpareIndices()) { std::shared_ptr<DFTGate<ValueType> const> elem = mDft.getGate(spareId); STORM_LOG_ASSERT(elem->isSpareGate(), "Element is no spare gate."); STORM_LOG_ASSERT(elem->nrChildren() > 0, "Element has no child."); this->setUses(spareId, elem->children()[0]->id()); } - + + for (auto elem : mDft.getBasicElements()) { + mCurrentlyNotFailableBE.push_back(elem->id()); + } + // Initialize activation propagateActivation(mDft.getTopLevelIndex()); - std::vector<size_t> alwaysActiveBEs = dft.nonColdBEs(); - mIsCurrentlyFailableBE.insert(mIsCurrentlyFailableBE.end(), alwaysActiveBEs.begin(), alwaysActiveBEs.end()); + std::vector<size_t> alwaysActiveBEs = mDft.nonColdBEs(); + mCurrentlyFailableBE.insert(mCurrentlyFailableBE.end(), alwaysActiveBEs.begin(), alwaysActiveBEs.end()); + // Remove always active BEs from currently not failable BEs + for (size_t id : alwaysActiveBEs) { + auto it = std::find(mCurrentlyNotFailableBE.begin(), mCurrentlyNotFailableBE.end(), id); + STORM_LOG_ASSERT(it != mCurrentlyNotFailableBE.end(), "Id not found."); + mCurrentlyNotFailableBE.erase(it); + } + sortFailableBEs(); } @@ -31,9 +42,13 @@ namespace storm { // Initialize currently failable BE if (mDft.isBasicElement(index) && isOperational(index)) { std::shared_ptr<const DFTBE<ValueType>> be = mDft.getBasicElement(index); - if ((!be->isColdBasicElement() && be->canFail()) || !mDft.hasRepresentant(index) || isActive(mDft.getRepresentant(index)->id())) { - mIsCurrentlyFailableBE.push_back(index); + if ((!be->isColdBasicElement() && be->canFail()) || !mDft.hasRepresentant(index) || isActive(mDft.getRepresentant(index))) { + mCurrentlyFailableBE.push_back(index); STORM_LOG_TRACE("Currently failable: " << mDft.getBasicElement(index)->toString()); + } else { + // BE currently is not failable + mCurrentlyNotFailableBE.push_back(index); + STORM_LOG_TRACE("Currently not failable: " << mDft.getBasicElement(index)->toString()); } } else if (mDft.getElement(index)->isSpareGate()) { // Initialize used representants @@ -176,9 +191,9 @@ namespace storm { template<typename ValueType> void DFTState<ValueType>::beNoLongerFailable(size_t id) { - auto it = std::find(mIsCurrentlyFailableBE.begin(), mIsCurrentlyFailableBE.end(), id); - if(it != mIsCurrentlyFailableBE.end()) { - mIsCurrentlyFailableBE.erase(it); + auto it = std::find(mCurrentlyFailableBE.begin(), mCurrentlyFailableBE.end(), id); + if(it != mCurrentlyFailableBE.end()) { + mCurrentlyFailableBE.erase(it); } } @@ -210,11 +225,29 @@ namespace storm { } } + template<typename ValueType> + ValueType DFTState<ValueType>::getBERate(size_t id, bool considerPassive) const { + STORM_LOG_ASSERT(mDft.isBasicElement(id), "Element is no BE."); + if (considerPassive && mDft.hasRepresentant(id) && !isActive(mDft.getRepresentant(id))) { + return mDft.getBasicElement(id)->passiveFailureRate(); + } else { + return mDft.getBasicElement(id)->activeFailureRate(); + } + } + template<typename ValueType> ValueType DFTState<ValueType>::getFailableBERate(size_t index) const { STORM_LOG_ASSERT(index < nrFailableBEs(), "Index invalid."); - // TODO Matthias: get passive failure rate when applicable - return mDft.getBasicElement(mIsCurrentlyFailableBE[index])->activeFailureRate(); + return getBERate(mCurrentlyFailableBE[index], true); + } + + template<typename ValueType> + ValueType DFTState<ValueType>::getNotFailableBERate(size_t index) const { + STORM_LOG_ASSERT(index < nrNotFailableBEs(), "Index invalid."); + STORM_LOG_ASSERT(storm::utility::isZero<ValueType>(mDft.getBasicElement(mCurrentlyNotFailableBE[index])->activeFailureRate()) || + (mDft.hasRepresentant(mCurrentlyNotFailableBE[index]) && !isActive(mDft.getRepresentant(mCurrentlyNotFailableBE[index]))), "BE " << mCurrentlyNotFailableBE[index] << " can fail"); + // Use active failure rate as passive failure rate is 0. + return getBERate(mCurrentlyNotFailableBE[index], false); } template<typename ValueType> @@ -232,9 +265,9 @@ namespace storm { } else { // Consider "normal" failure STORM_LOG_ASSERT(index < nrFailableBEs(), "Index invalid."); - std::pair<std::shared_ptr<DFTBE<ValueType> const>,bool> res(mDft.getBasicElement(mIsCurrentlyFailableBE[index]), false); + std::pair<std::shared_ptr<DFTBE<ValueType> const>,bool> res(mDft.getBasicElement(mCurrentlyFailableBE[index]), false); STORM_LOG_ASSERT(res.first->canFail(), "Element cannot fail."); - mIsCurrentlyFailableBE.erase(mIsCurrentlyFailableBE.begin() + index); + mCurrentlyFailableBE.erase(mCurrentlyFailableBE.begin() + index); setFailed(res.first->id()); return res; } @@ -269,7 +302,12 @@ namespace storm { if(mDft.isBasicElement(elem) && isOperational(elem)) { std::shared_ptr<const DFTBE<ValueType>> be = mDft.getBasicElement(elem); if (be->isColdBasicElement() && be->canFail()) { - mIsCurrentlyFailableBE.push_back(elem); + // Add to failable BEs + mCurrentlyFailableBE.push_back(elem); + // Remove from not failable BEs + auto it = std::find(mCurrentlyNotFailableBE.begin(), mCurrentlyNotFailableBE.end(), elem); + STORM_LOG_ASSERT(it != mCurrentlyNotFailableBE.end(), "Element not found."); + mCurrentlyNotFailableBE.erase(it); } } else if (mDft.getElement(elem)->isSpareGate() && !isActive(uses(elem))) { propagateActivation(uses(elem)); @@ -373,11 +411,9 @@ namespace storm { template<typename ValueType> void DFTState<ValueType>::sortFailableBEs() { - std::sort( mIsCurrentlyFailableBE.begin( ), mIsCurrentlyFailableBE.end( ), [&](size_t const& lhs, size_t const& rhs) { - ValueType leftRate = mDft.getBasicElement(lhs)->activeFailureRate(); - ValueType rightRate = mDft.getBasicElement(rhs)->activeFailureRate(); + std::sort(mCurrentlyFailableBE.begin( ), mCurrentlyFailableBE.end( ), [&](size_t const& lhs, size_t const& rhs) { // Sort decreasing - return rightRate < leftRate; + return getBERate(rhs, true) < getBERate(lhs, true); }); } diff --git a/src/storage/dft/DFTState.h b/src/storage/dft/DFTState.h index 617d40a7d..a6644f349 100644 --- a/src/storage/dft/DFTState.h +++ b/src/storage/dft/DFTState.h @@ -26,7 +26,8 @@ namespace storm { // Status is bitvector where each element has two bits with the meaning according to DFTElementState storm::storage::BitVector mStatus; size_t mId; - std::vector<size_t> mIsCurrentlyFailableBE; + std::vector<size_t> mCurrentlyFailableBE; + std::vector<size_t> mCurrentlyNotFailableBE; std::vector<size_t> mFailableDependencies; std::vector<size_t> mUsedRepresentants; bool mValid = true; @@ -158,25 +159,58 @@ namespace storm { * @param spareId Id of the spare */ void finalizeUses(size_t spareId); - + + /** + * Claim a new spare child for the given spare gate. + * + * @param spareId Id of the spare gate. + * @param currentlyUses Id of the currently used spare child. + * @param children List of children of this spare. + * + * @return True, if claiming was successful. + */ bool claimNew(size_t spareId, size_t currentlyUses, std::vector<std::shared_ptr<DFTElement<ValueType>>> const& children); - bool hasOutgoingEdges() const { - return !mIsCurrentlyFailableBE.empty(); - } - + /** + * Get number of currently failable BEs. + * + * @return Number of failable BEs. + */ size_t nrFailableBEs() const { - return mIsCurrentlyFailableBE.size(); + return mCurrentlyFailableBE.size(); + } + + /** + * Get number of currently not failable BEs. These are cold BE which can fail in the future. + * + * @return Number of not failable BEs. + */ + size_t nrNotFailableBEs() const { + return mCurrentlyNotFailableBE.size(); } - /** Get the failure rate of the currently failable BE on the given index. + /** + * Get the failure rate of the currently failable BE on the given index. * - * @param index Index of BE in list of currently failable BEs. + * @param index Index of BE in list of currently failable BEs. * * @return Failure rate of the BE. */ ValueType getFailableBERate(size_t index) const; + /** + * Get the failure rate of the currently not failable BE on the given index. + * + * @param index Index of BE in list of currently not failable BEs. + * + * @return Failure rate of the BE. + */ + ValueType getNotFailableBERate(size_t index) const; + + /** Get number of currently failable dependencies. + * + * @return Number of failable dependencies. + */ size_t nrFailableDependencies() const { return mFailableDependencies.size(); } @@ -245,13 +279,13 @@ namespace storm { } stream << "} "; } else { - auto it = mIsCurrentlyFailableBE.begin(); + auto it = mCurrentlyFailableBE.begin(); stream << "{"; - if(it != mIsCurrentlyFailableBE.end()) { + if(it != mCurrentlyFailableBE.end()) { stream << *it; } ++it; - while(it != mIsCurrentlyFailableBE.end()) { + while(it != mCurrentlyFailableBE.end()) { stream << ", " << *it; ++it; } @@ -266,7 +300,18 @@ namespace storm { private: void propagateActivation(size_t representativeId); - + + /** + * Get the failure rate of the given BE. + * + * @param id Id of BE. + * @param considerPassive Flag indicating if the passive failure rate should be returned if + * the BE currently is passive. + * + * @return Failure rate of the BE. + */ + ValueType getBERate(size_t id, bool considerPassive) const; + /*! * Sort failable BEs in decreasing order of their active failure rate. */ From ea00abc35eba8f83b5bc41e6d0ed07592fdf2528 Mon Sep 17 00:00:00 2001 From: Mavo <matthias.volk@rwth-aachen.de> Date: Thu, 13 Oct 2016 13:12:16 +0200 Subject: [PATCH 23/65] Fixed problems with approximation while using symred Former-commit-id: df12c037e731f08e7072e2d74abf64a5ce5c11f7 --- src/builder/ExplicitDFTModelBuilderApprox.cpp | 30 ++++++++++++------- src/storage/dft/DFTState.cpp | 2 +- src/utility/vector.cpp | 14 +++++---- 3 files changed, 29 insertions(+), 17 deletions(-) diff --git a/src/builder/ExplicitDFTModelBuilderApprox.cpp b/src/builder/ExplicitDFTModelBuilderApprox.cpp index 1f702d270..ac26129df 100644 --- a/src/builder/ExplicitDFTModelBuilderApprox.cpp +++ b/src/builder/ExplicitDFTModelBuilderApprox.cpp @@ -82,18 +82,23 @@ namespace storm { modelComponents.deterministicModel = generator.isDeterministicModel(); // Replace pseudo states in matrix - // TODO Matthias: avoid hack with fixed int type - std::vector<uint_fast64_t> pseudoStatesVector; - for (auto const& pseudoStatePair : mPseudoStatesMapping) { - pseudoStatesVector.push_back(pseudoStatePair.first); + if (!mPseudoStatesMapping.empty()) { + // TODO Matthias: avoid hack with fixed int type + std::vector<uint_fast64_t> pseudoStatesVector; + for (auto const& pseudoStatePair : mPseudoStatesMapping) { + pseudoStatesVector.push_back(matrixBuilder.mappingOffset + pseudoStatePair.first); + } + STORM_LOG_ASSERT(std::find(pseudoStatesVector.begin(), pseudoStatesVector.end(), 0) == pseudoStatesVector.end(), "Unexplored pseudo state still contained."); + STORM_LOG_TRACE("Replace pseudo states: " << pseudoStatesVector << ", offset: " << OFFSET_PSEUDO_STATE); + // TODO Matthias: combine replacement with later one + matrixBuilder.builder.replaceColumns(pseudoStatesVector, OFFSET_PSEUDO_STATE); + mPseudoStatesMapping.clear(); } - STORM_LOG_ASSERT(std::find(pseudoStatesVector.begin(), pseudoStatesVector.end(), 0) == pseudoStatesVector.end(), "Unexplored pseudo state still contained."); - matrixBuilder.builder.replaceColumns(pseudoStatesVector, OFFSET_PSEUDO_STATE); - mPseudoStatesMapping.clear(); // Fix the entries in the transition matrix according to the mapping of ids to row group indices STORM_LOG_ASSERT(matrixBuilder.stateRemapping[initialStateIndex] == initialStateIndex, "Initial state should not be remapped."); // TODO Matthias: do not consider all rows? + STORM_LOG_TRACE("Remap matrix: " << matrixBuilder.stateRemapping << ", offset: " << matrixBuilder.mappingOffset); matrixBuilder.remap(); STORM_LOG_TRACE("State remapping: " << matrixBuilder.stateRemapping); @@ -251,10 +256,9 @@ namespace storm { for (auto const& choice : behavior) { // Add the probabilistic behavior to the matrix. for (auto const& stateProbabilityPair : choice) { - - // Check that pseudo state and its instantiation do not appear together - // TODO Matthias: prove that this is not possible and remove if (stateProbabilityPair.first >= OFFSET_PSEUDO_STATE) { + // Check that pseudo state and its instantiation do not appear together + // TODO Matthias: prove that this is not possible and remove StateType newId = stateProbabilityPair.first - OFFSET_PSEUDO_STATE; STORM_LOG_ASSERT(newId < mPseudoStatesMapping.size(), "Id is not valid."); if (mPseudoStatesMapping[newId].first > 0) { @@ -264,8 +268,12 @@ namespace storm { STORM_LOG_ASSERT(itFind->first != newId, "Pseudo state and instantiation occur together in a distribution."); } } + // Set transtion to pseudo state + matrixBuilder.addTransition(stateProbabilityPair.first, stateProbabilityPair.second); + } else { + // Set transition to state id + offset. This helps in only remapping all previously skipped states. + matrixBuilder.addTransition(matrixBuilder.mappingOffset + stateProbabilityPair.first, stateProbabilityPair.second); } - matrixBuilder.addTransition(matrixBuilder.mappingOffset + stateProbabilityPair.first, stateProbabilityPair.second); } matrixBuilder.finishRow(); } diff --git a/src/storage/dft/DFTState.cpp b/src/storage/dft/DFTState.cpp index b292e322d..208dd6bf0 100644 --- a/src/storage/dft/DFTState.cpp +++ b/src/storage/dft/DFTState.cpp @@ -36,7 +36,7 @@ namespace storm { } template<typename ValueType> - DFTState<ValueType>::DFTState(storm::storage::BitVector const& status, DFT<ValueType> const& dft, DFTStateGenerationInfo const& stateGenerationInfo, size_t id) : mStatus(status), mId(id), mDft(dft), mStateGenerationInfo(stateGenerationInfo) { + DFTState<ValueType>::DFTState(storm::storage::BitVector const& status, DFT<ValueType> const& dft, DFTStateGenerationInfo const& stateGenerationInfo, size_t id) : mStatus(status), mId(id), mDft(dft), mStateGenerationInfo(stateGenerationInfo), exploreHeuristic() { for(size_t index = 0; index < mDft.nrElements(); ++index) { // Initialize currently failable BE diff --git a/src/utility/vector.cpp b/src/utility/vector.cpp index 44e480029..595af193e 100644 --- a/src/utility/vector.cpp +++ b/src/utility/vector.cpp @@ -20,20 +20,24 @@ template std::ostream& operator<<(std::ostream& out, std::vector<uint_fast64_t> std::ostream& operator<<(std::ostream& out, std::vector<double> const& vector) { out << "vector (" << vector.size() << ") [ "; - for (uint_fast64_t i = 0; i < vector.size() - 1; ++i) { + for (size_t i = 0; i + 1 < vector.size(); ++i) { out << vector[i] << ", "; } - out << vector.back(); + if (!vector.empty()) { + out << vector.back(); + } out << " ]"; return out; } std::ostream& operator<<(std::ostream& out, std::vector<uint_fast64_t> const& vector) { out << "vector (" << vector.size() << ") [ "; - for (uint_fast64_t i = 0; i < vector.size() - 1; ++i) { + for (size_t i = 0; i + 1 < vector.size(); ++i) { out << vector[i] << ", "; } - out << vector.back(); + if (!vector.empty()) { + out << vector.back(); + } out << " ]"; return out; -} \ No newline at end of file +} From faadf19228bbedc42569b731421d83788cd4b78a Mon Sep 17 00:00:00 2001 From: Mavo <matthias.volk@rwth-aachen.de> Date: Thu, 13 Oct 2016 15:09:09 +0200 Subject: [PATCH 24/65] Approximation error is relative to mean of lower and upper bound Former-commit-id: f01c027a3a2d083dd45dcae15aab256e6d44c97f --- src/modelchecker/dft/DFTModelChecker.cpp | 2 +- src/modelchecker/dft/DFTModelChecker.h | 3 ++- src/settings/modules/DFTSettings.cpp | 2 +- src/settings/modules/DFTSettings.h | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/modelchecker/dft/DFTModelChecker.cpp b/src/modelchecker/dft/DFTModelChecker.cpp index 26f2033d6..717ace4c3 100644 --- a/src/modelchecker/dft/DFTModelChecker.cpp +++ b/src/modelchecker/dft/DFTModelChecker.cpp @@ -254,7 +254,7 @@ namespace storm { template<> bool DFTModelChecker<double>::isApproximationSufficient(double lowerBound, double upperBound, double approximationError) { - return upperBound - lowerBound <= approximationError; + return upperBound - lowerBound <= approximationError * (lowerBound + upperBound) / 2; } template<typename ValueType> diff --git a/src/modelchecker/dft/DFTModelChecker.h b/src/modelchecker/dft/DFTModelChecker.h index 62bb5afe4..83df59bfa 100644 --- a/src/modelchecker/dft/DFTModelChecker.h +++ b/src/modelchecker/dft/DFTModelChecker.h @@ -106,7 +106,8 @@ namespace storm { std::unique_ptr<storm::modelchecker::CheckResult> checkModel(std::shared_ptr<storm::models::sparse::Model<ValueType>>& model, std::shared_ptr<const storm::logic::Formula> const& formula); /*! - * Checks if the computed approximation is sufficient, i.e. upperBound - lowerBound <= approximationError. + * Checks if the computed approximation is sufficient, i.e. + * upperBound - lowerBound <= approximationError * mean(upperBound, lowerBound). * * @param lowerBound The lower bound on the result. * @param upperBound The upper bound on the result. diff --git a/src/settings/modules/DFTSettings.cpp b/src/settings/modules/DFTSettings.cpp index 1b3f0bae2..0dcb49f1d 100644 --- a/src/settings/modules/DFTSettings.cpp +++ b/src/settings/modules/DFTSettings.cpp @@ -39,7 +39,7 @@ namespace storm { this->addOption(storm::settings::OptionBuilder(moduleName, symmetryReductionOptionName, false, "Exploit symmetric structure of model.").setShortName(symmetryReductionOptionShortName).build()); this->addOption(storm::settings::OptionBuilder(moduleName, modularisationOptionName, false, "Use modularisation (not applicable for expected time).").build()); this->addOption(storm::settings::OptionBuilder(moduleName, disableDCOptionName, false, "Disable Dont Care propagation.").build()); - this->addOption(storm::settings::OptionBuilder(moduleName, approximationErrorOptionName, false, "Approximation error allowed.").setShortName(approximationErrorOptionShortName).addArgument(storm::settings::ArgumentBuilder::createDoubleArgument("error", "The approximation error to use.").addValidationFunctionDouble(storm::settings::ArgumentValidators::doubleGreaterValidatorIncluding(0.0)).build()).build()); + this->addOption(storm::settings::OptionBuilder(moduleName, approximationErrorOptionName, false, "Approximation error allowed.").setShortName(approximationErrorOptionShortName).addArgument(storm::settings::ArgumentBuilder::createDoubleArgument("error", "The relative approximation error to use.").addValidationFunctionDouble(storm::settings::ArgumentValidators::doubleGreaterValidatorIncluding(0.0)).build()).build()); this->addOption(storm::settings::OptionBuilder(moduleName, approximationHeuristicOptionName, false, "Set the heuristic used for approximation.").addArgument(storm::settings::ArgumentBuilder::createStringArgument("heuristic", "Sets which heuristic is used for approximation. Must be in {depth, rateratio}. Default is").setDefaultValueString("depth").addValidationFunctionString(storm::settings::ArgumentValidators::stringInListValidator({"depth", "rateratio"})).build()).build()); this->addOption(storm::settings::OptionBuilder(moduleName, propExpectedTimeOptionName, false, "Compute expected time of system failure.").setShortName(propExpectedTimeOptionShortName).build()); this->addOption(storm::settings::OptionBuilder(moduleName, propProbabilityOptionName, false, "Compute probability of system failure.").build()); diff --git a/src/settings/modules/DFTSettings.h b/src/settings/modules/DFTSettings.h index 2aa418538..e580a5d05 100644 --- a/src/settings/modules/DFTSettings.h +++ b/src/settings/modules/DFTSettings.h @@ -63,7 +63,7 @@ namespace storm { bool isApproximationErrorSet() const; /*! - * Retrieves the error allowed for approximation the model checking result. + * Retrieves the relative error allowed for approximating the model checking result. * * @return The allowed errorbound. */ From e05c4dab0dfdb2d6a7f923867052f74e5ff39c75 Mon Sep 17 00:00:00 2001 From: Mavo <matthias.volk@rwth-aachen.de> Date: Thu, 13 Oct 2016 16:00:32 +0200 Subject: [PATCH 25/65] Use custom DynamicPriorityQueue Former-commit-id: b4b552a84d9f43880bc0c37b19b4edd90f3b6ddf --- src/builder/ExplicitDFTModelBuilderApprox.cpp | 18 +++-- src/builder/ExplicitDFTModelBuilderApprox.h | 3 +- src/storage/DynamicPriorityQueue.h | 67 +++++++++++++++++++ 3 files changed, 81 insertions(+), 7 deletions(-) create mode 100644 src/storage/DynamicPriorityQueue.h diff --git a/src/builder/ExplicitDFTModelBuilderApprox.cpp b/src/builder/ExplicitDFTModelBuilderApprox.cpp index ac26129df..88af5276d 100644 --- a/src/builder/ExplicitDFTModelBuilderApprox.cpp +++ b/src/builder/ExplicitDFTModelBuilderApprox.cpp @@ -23,14 +23,17 @@ namespace storm { } template<typename ValueType, typename StateType> - ExplicitDFTModelBuilderApprox<ValueType, StateType>::ExplicitDFTModelBuilderApprox(storm::storage::DFT<ValueType> const& dft, storm::storage::DFTIndependentSymmetries const& symmetries, bool enableDC) : dft(dft), stateGenerationInfo(std::make_shared<storm::storage::DFTStateGenerationInfo>(dft.buildStateGenerationInfo(symmetries))), enableDC(enableDC), generator(dft, *stateGenerationInfo, enableDC, mergeFailedStates), matrixBuilder(!generator.isDeterministicModel()), stateStorage(((dft.stateVectorSize() / 64) + 1) * 64) { + ExplicitDFTModelBuilderApprox<ValueType, StateType>::ExplicitDFTModelBuilderApprox(storm::storage::DFT<ValueType> const& dft, storm::storage::DFTIndependentSymmetries const& symmetries, bool enableDC) : + dft(dft), + stateGenerationInfo(std::make_shared<storm::storage::DFTStateGenerationInfo>(dft.buildStateGenerationInfo(symmetries))), + enableDC(enableDC), + generator(dft, *stateGenerationInfo, enableDC, mergeFailedStates), + matrixBuilder(!generator.isDeterministicModel()), + stateStorage(((dft.stateVectorSize() / 64) + 1) * 64), + statesToExplore(storm::storage::DynamicPriorityQueue<DFTStatePointer, std::vector<DFTStatePointer>, std::function<bool(DFTStatePointer, DFTStatePointer)>>(&storm::builder::compareDepth<ValueType>)) + { // stateVectorSize is bound for size of bitvector - heuristic = storm::settings::getModule<storm::settings::modules::DFTSettings>().getApproximationHeuristic(); - - // Compare states by their distance from the initial state - // TODO Matthias: customize - statesToExplore = std::priority_queue<DFTStatePointer, std::deque<DFTStatePointer>, std::function<bool(DFTStatePointer, DFTStatePointer)>>(&storm::builder::compareDepth<ValueType>); } template<typename ValueType, typename StateType> @@ -279,6 +282,9 @@ namespace storm { } } + // Update priority queue + statesToExplore.fix(); + if (statesToExplore.empty()) { explorationFinished = true; // Before ending the exploration check for pseudo states which are not initialized yet diff --git a/src/builder/ExplicitDFTModelBuilderApprox.h b/src/builder/ExplicitDFTModelBuilderApprox.h index cb9a444af..9dfb3f63d 100644 --- a/src/builder/ExplicitDFTModelBuilderApprox.h +++ b/src/builder/ExplicitDFTModelBuilderApprox.h @@ -10,6 +10,7 @@ #include "src/storage/sparse/StateStorage.h" #include "src/storage/dft/DFT.h" #include "src/storage/dft/SymmetricUnits.h" +#include "src/storage/DynamicPriorityQueue.h" #include <boost/container/flat_set.hpp> #include <boost/optional/optional.hpp> #include <stack> @@ -286,7 +287,7 @@ namespace storm { storm::storage::sparse::StateStorage<StateType> stateStorage; // A pniority queue of states that still need to be explored. - std::priority_queue<DFTStatePointer, std::deque<DFTStatePointer>, std::function<bool(DFTStatePointer, DFTStatePointer)>> statesToExplore; + storm::storage::DynamicPriorityQueue<DFTStatePointer, std::vector<DFTStatePointer>, std::function<bool(DFTStatePointer, DFTStatePointer)>> statesToExplore; // Holds all skipped states which were not yet expanded. More concretely it is a mapping from matrix indices // to the corresponding skipped states. diff --git a/src/storage/DynamicPriorityQueue.h b/src/storage/DynamicPriorityQueue.h new file mode 100644 index 000000000..a416a89b6 --- /dev/null +++ b/src/storage/DynamicPriorityQueue.h @@ -0,0 +1,67 @@ +#ifndef STORM_STORAGE_DYNAMICPRIORITYQUEUE_H_ +#define STORM_STORAGE_DYNAMICPRIORITYQUEUE_H_ + +#include <algorithm> +#include <vector> + +namespace storm { + namespace storage { + + template<typename T, typename Container = std::vector<T>, typename Compare = std::less<T>> + class DynamicPriorityQueue { + + private: + Container container; + Compare compare; + + public: + explicit DynamicPriorityQueue(const Compare& compare) : container(), compare(compare) { + // Intentionally left empty + } + + explicit DynamicPriorityQueue(Container&& container, const Compare& compare) : container(std::move(container)), compare(compare) { + std::make_heap(container.begin(), container.end(), compare); + } + + void fix() { + std::make_heap(container.begin(), container.end(), compare); + } + + bool empty() const { + return container.empty(); + } + + std::size_t size() const { + return container.size(); + } + + const T& top() const { + return container.front(); + } + + void push(const T& item) { + container.push_back(item); + std::push_heap(container.begin(), container.end(), compare); + } + + void push(T&& item) { + container.push_back(std::move(item)); + std::push_heap(container.begin(), container.end(), compare); + } + + void pop() { + std::pop_heap(container.begin(), container.end(), compare); + container.pop_back(); + } + + T popTop() { + T item = top(); + pop(); + return item; + } + + }; + } +} + +#endif // STORM_STORAGE_DYNAMICPRIORITYQUEUE_H_ From 8e1e61c4f249b92fcb33f158fd805de34b58d552 Mon Sep 17 00:00:00 2001 From: Mavo <matthias.volk@rwth-aachen.de> Date: Fri, 14 Oct 2016 18:59:45 +0200 Subject: [PATCH 26/65] Changed datastructures and made handling of pseudo states easier Former-commit-id: a0e0324fe6d25f63312edac06fb4d8871bb26f7d --- src/builder/ExplicitDFTModelBuilderApprox.cpp | 169 +++++++----------- src/builder/ExplicitDFTModelBuilderApprox.h | 21 ++- src/storage/DynamicPriorityQueue.h | 4 +- src/storage/dft/DFTState.cpp | 17 +- src/storage/dft/DFTState.h | 26 ++- 5 files changed, 119 insertions(+), 118 deletions(-) diff --git a/src/builder/ExplicitDFTModelBuilderApprox.cpp b/src/builder/ExplicitDFTModelBuilderApprox.cpp index 88af5276d..76ace8dda 100644 --- a/src/builder/ExplicitDFTModelBuilderApprox.cpp +++ b/src/builder/ExplicitDFTModelBuilderApprox.cpp @@ -27,13 +27,15 @@ namespace storm { dft(dft), stateGenerationInfo(std::make_shared<storm::storage::DFTStateGenerationInfo>(dft.buildStateGenerationInfo(symmetries))), enableDC(enableDC), + heuristic(storm::settings::getModule<storm::settings::modules::DFTSettings>().getApproximationHeuristic()), generator(dft, *stateGenerationInfo, enableDC, mergeFailedStates), matrixBuilder(!generator.isDeterministicModel()), stateStorage(((dft.stateVectorSize() / 64) + 1) * 64), - statesToExplore(storm::storage::DynamicPriorityQueue<DFTStatePointer, std::vector<DFTStatePointer>, std::function<bool(DFTStatePointer, DFTStatePointer)>>(&storm::builder::compareDepth<ValueType>)) + explorationQueue([this](StateType idA, StateType idB) { + return isPriorityGreater(idA, idB); + }) { - // stateVectorSize is bound for size of bitvector - heuristic = storm::settings::getModule<storm::settings::modules::DFTSettings>().getApproximationHeuristic(); + // Intentionally left empty. } template<typename ValueType, typename StateType> @@ -84,20 +86,6 @@ namespace storm { modelComponents.markovianStates.resize(stateSize); modelComponents.deterministicModel = generator.isDeterministicModel(); - // Replace pseudo states in matrix - if (!mPseudoStatesMapping.empty()) { - // TODO Matthias: avoid hack with fixed int type - std::vector<uint_fast64_t> pseudoStatesVector; - for (auto const& pseudoStatePair : mPseudoStatesMapping) { - pseudoStatesVector.push_back(matrixBuilder.mappingOffset + pseudoStatePair.first); - } - STORM_LOG_ASSERT(std::find(pseudoStatesVector.begin(), pseudoStatesVector.end(), 0) == pseudoStatesVector.end(), "Unexplored pseudo state still contained."); - STORM_LOG_TRACE("Replace pseudo states: " << pseudoStatesVector << ", offset: " << OFFSET_PSEUDO_STATE); - // TODO Matthias: combine replacement with later one - matrixBuilder.builder.replaceColumns(pseudoStatesVector, OFFSET_PSEUDO_STATE); - mPseudoStatesMapping.clear(); - } - // Fix the entries in the transition matrix according to the mapping of ids to row group indices STORM_LOG_ASSERT(matrixBuilder.stateRemapping[initialStateIndex] == initialStateIndex, "Initial state should not be remapped."); // TODO Matthias: do not consider all rows? @@ -125,6 +113,14 @@ namespace storm { void ExplicitDFTModelBuilderApprox<ValueType, StateType>::initializeNextIteration() { STORM_LOG_TRACE("Refining DFT state space"); + // TODO Matthias: should be easier now as skipped states all are at the end of the matrix + // Push skipped states to explore queue + // TODO Matthias: remove + for (auto const& skippedState : skippedStates) { + statesNotExplored[skippedState.second->getId()] = skippedState.second; + explorationQueue.push(skippedState.second->getId()); + } + // Initialize matrix builder again // TODO Matthias: avoid copy std::vector<uint_fast64_t> copyRemapping = matrixBuilder.stateRemapping; @@ -211,28 +207,33 @@ namespace storm { skippedStates = skippedStatesNew; STORM_LOG_ASSERT(matrixBuilder.getCurrentRowGroup() == nrExpandedStates, "Row group size does not match."); - - // Push skipped states to explore queue - // TODO Matthias: remove - for (auto const& skippedState : skippedStates) { - statesToExplore.push(skippedState.second); - } skippedStates.clear(); } template<typename ValueType, typename StateType> void ExplicitDFTModelBuilderApprox<ValueType, StateType>::exploreStateSpace(double approximationThreshold) { - bool explorationFinished = false; - size_t pseudoStatesToCheck = 0; - while (!explorationFinished) { + // TODO Matthias: do not empty queue every time but break before + while (!explorationQueue.empty()) { // Get the first state in the queue - DFTStatePointer currentState = statesToExplore.top(); + StateType currentId = explorationQueue.popTop(); + auto itFind = statesNotExplored.find(currentId); + STORM_LOG_ASSERT(itFind != statesNotExplored.end(), "Id " << currentId << " not found"); + DFTStatePointer currentState = itFind->second; + STORM_LOG_ASSERT(currentState->getId() == currentId, "Ids do not match"); + // Remove it from the list of not explored states + statesNotExplored.erase(itFind); STORM_LOG_ASSERT(stateStorage.stateToId.contains(currentState->status()), "State is not contained in state storage."); - STORM_LOG_ASSERT(stateStorage.stateToId.getValue(currentState->status()) == currentState->getId(), "Ids of states do not coincide."); - statesToExplore.pop(); + STORM_LOG_ASSERT(stateStorage.stateToId.getValue(currentState->status()) == currentId, "Ids of states do not coincide."); + + // Get concrete state if necessary + if (currentState->isPseudoState()) { + // Create concrete state from pseudo state + currentState->construct(); + } + STORM_LOG_ASSERT(!currentState->isPseudoState(), "State is pseudo state."); // Remember that the current row group was actually filled with the transitions of a different state - matrixBuilder.setRemapping(currentState->getId()); + matrixBuilder.setRemapping(currentId); matrixBuilder.newRowGroup(); @@ -259,53 +260,17 @@ namespace storm { for (auto const& choice : behavior) { // Add the probabilistic behavior to the matrix. for (auto const& stateProbabilityPair : choice) { - if (stateProbabilityPair.first >= OFFSET_PSEUDO_STATE) { - // Check that pseudo state and its instantiation do not appear together - // TODO Matthias: prove that this is not possible and remove - StateType newId = stateProbabilityPair.first - OFFSET_PSEUDO_STATE; - STORM_LOG_ASSERT(newId < mPseudoStatesMapping.size(), "Id is not valid."); - if (mPseudoStatesMapping[newId].first > 0) { - // State exists already - newId = mPseudoStatesMapping[newId].first; - for (auto itFind = choice.begin(); itFind != choice.end(); ++itFind) { - STORM_LOG_ASSERT(itFind->first != newId, "Pseudo state and instantiation occur together in a distribution."); - } - } - // Set transtion to pseudo state - matrixBuilder.addTransition(stateProbabilityPair.first, stateProbabilityPair.second); - } else { - // Set transition to state id + offset. This helps in only remapping all previously skipped states. - matrixBuilder.addTransition(matrixBuilder.mappingOffset + stateProbabilityPair.first, stateProbabilityPair.second); - } + // Set transition to state id + offset. This helps in only remapping all previously skipped states. + matrixBuilder.addTransition(matrixBuilder.mappingOffset + stateProbabilityPair.first, stateProbabilityPair.second); + // TODO Matthias: set heuristic values here } matrixBuilder.finishRow(); } } // Update priority queue - statesToExplore.fix(); - - if (statesToExplore.empty()) { - explorationFinished = true; - // Before ending the exploration check for pseudo states which are not initialized yet - for ( ; pseudoStatesToCheck < mPseudoStatesMapping.size(); ++pseudoStatesToCheck) { - std::pair<StateType, storm::storage::BitVector> pseudoStatePair = mPseudoStatesMapping[pseudoStatesToCheck]; - if (pseudoStatePair.first == 0) { - // Create state from pseudo state and explore - STORM_LOG_ASSERT(stateStorage.stateToId.contains(pseudoStatePair.second), "Pseudo state not contained."); - STORM_LOG_ASSERT(stateStorage.stateToId.getValue(pseudoStatePair.second) >= OFFSET_PSEUDO_STATE, "State is no pseudo state."); - STORM_LOG_TRACE("Create pseudo state from bit vector " << pseudoStatePair.second); - DFTStatePointer pseudoState = std::make_shared<storm::storage::DFTState<ValueType>>(pseudoStatePair.second, dft, *stateGenerationInfo, newIndex); - STORM_LOG_ASSERT(pseudoStatePair.second == pseudoState->status(), "Pseudo states do not coincide."); - STORM_LOG_TRACE("Explore pseudo state " << dft.getStateString(pseudoState) << " with id " << pseudoState->getId()); - - getOrAddStateIndex(pseudoState); - explorationFinished = false; - break; - } - } - } - + // TODO Matthias: only when necessary + explorationQueue.fix(); } // end exploration } @@ -476,45 +441,31 @@ namespace storm { if (stateStorage.stateToId.contains(state->status())) { // State already exists stateId = stateStorage.stateToId.getValue(state->status()); - STORM_LOG_TRACE("State " << dft.getStateString(state) << " with id " << stateId << " already exists"); - - if (!changed && stateId >= OFFSET_PSEUDO_STATE) { - // Pseudo state can be created now - STORM_LOG_ASSERT(stateId >= OFFSET_PSEUDO_STATE, "State is no pseudo state."); - stateId -= OFFSET_PSEUDO_STATE; - STORM_LOG_ASSERT(stateId < mPseudoStatesMapping.size(), "Pseudo state not known."); - STORM_LOG_ASSERT(mPseudoStatesMapping[stateId].first == 0, "Pseudo state already created."); + STORM_LOG_TRACE("State " << dft.getStateString(state) << " already exists"); + + if (!changed && statesNotExplored.at(stateId)->isPseudoState()) { // Create pseudo state now - STORM_LOG_ASSERT(mPseudoStatesMapping[stateId].second == state->status(), "Pseudo states do not coincide."); - state->setId(newIndex++); - mPseudoStatesMapping[stateId].first = state->getId(); - stateId = state->getId(); - stateStorage.stateToId.setOrAdd(state->status(), stateId); - STORM_LOG_TRACE("Now create state " << dft.getStateString(state) << " with id " << stateId); - statesToExplore.push(state); + STORM_LOG_ASSERT(statesNotExplored.at(stateId)->getId() == stateId, "Ids do not match."); + STORM_LOG_ASSERT(statesNotExplored.at(stateId)->status() == state->status(), "Pseudo states do not coincide."); + state->setId(stateId); + // Update mapping to map to concrete state now + statesNotExplored[stateId] = state; + // We do not push the new state on the exploration queue as the pseudo state was already pushed + STORM_LOG_TRACE("Created pseudo state " << dft.getStateString(state)); } } else { // State does not exist yet - if (changed) { - // Remember state for later creation - state->setId(mPseudoStatesMapping.size() + OFFSET_PSEUDO_STATE); - mPseudoStatesMapping.push_back(std::make_pair(0, state->status())); - stateId = stateStorage.stateToId.findOrAdd(state->status(), state->getId()); - STORM_LOG_ASSERT(stateId == state->getId(), "Ids do not match."); - STORM_LOG_TRACE("Remember state for later creation: " << dft.getStateString(state)); - // Reserve one slot for the coming state in the remapping - matrixBuilder.stateRemapping.push_back(0); - } else { - // Create new state - state->setId(newIndex++); - stateId = stateStorage.stateToId.findOrAdd(state->status(), state->getId()); - STORM_LOG_ASSERT(stateId == state->getId(), "Ids do not match."); - STORM_LOG_TRACE("New state: " << dft.getStateString(state)); - statesToExplore.push(state); - - // Reserve one slot for the new state in the remapping - matrixBuilder.stateRemapping.push_back(0); - } + STORM_LOG_ASSERT(state->isPseudoState() == changed, "State type (pseudo/concrete) wrong."); + // Create new state + state->setId(newIndex++); + stateId = stateStorage.stateToId.findOrAdd(state->status(), state->getId()); + STORM_LOG_ASSERT(stateId == state->getId(), "Ids do not match."); + // Insert state as not yet explored + statesNotExplored[stateId] = state; + explorationQueue.push(stateId); + // Reserve one slot for the new state in the remapping + matrixBuilder.stateRemapping.push_back(0); + STORM_LOG_TRACE("New " << (state->isPseudoState() ? "pseudo" : "concrete") << " state: " << dft.getStateString(state)); } return stateId; } @@ -528,6 +479,12 @@ namespace storm { modelComponents.markovianStates.set(matrixBuilder.getCurrentRowGroup() - 1, markovian); } + template<typename ValueType, typename StateType> + bool ExplicitDFTModelBuilderApprox<ValueType, StateType>::isPriorityGreater(StateType idA, StateType idB) const { + // TODO Matthias: compare directly and according to heuristic + return storm::builder::compareDepth(statesNotExplored.at(idA), statesNotExplored.at(idB)); + } + // Explicitly instantiate the class. template class ExplicitDFTModelBuilderApprox<double>; diff --git a/src/builder/ExplicitDFTModelBuilderApprox.h b/src/builder/ExplicitDFTModelBuilderApprox.h index 9dfb3f63d..5e841fddc 100644 --- a/src/builder/ExplicitDFTModelBuilderApprox.h +++ b/src/builder/ExplicitDFTModelBuilderApprox.h @@ -231,6 +231,16 @@ namespace storm { */ void changeMatrixUpperBound(storm::storage::SparseMatrix<ValueType> & matrix) const; + /*! + * Compares the priority of two states. + * + * @param idA Id of first state + * @param idB Id of second state + * + * @return True if the priority of the first state is greater then the priority of the second one. + */ + bool isPriorityGreater(StateType idA, StateType idB) const; + /*! * Create the model model from the model components. * @@ -271,9 +281,6 @@ namespace storm { // Id of initial state size_t initialStateIndex = 0; - // Mapping from pseudo states to (id of concrete state, bitvector representation) - std::vector<std::pair<StateType, storm::storage::BitVector>> mPseudoStatesMapping; - // Next state generator for exploring the state space storm::generator::DftNextStateGenerator<ValueType, StateType> generator; @@ -286,12 +293,16 @@ namespace storm { // Internal information about the states that were explored. storm::storage::sparse::StateStorage<StateType> stateStorage; - // A pniority queue of states that still need to be explored. - storm::storage::DynamicPriorityQueue<DFTStatePointer, std::vector<DFTStatePointer>, std::function<bool(DFTStatePointer, DFTStatePointer)>> statesToExplore; + // A priority queue of states that still need to be explored. + storm::storage::DynamicPriorityQueue<StateType, std::vector<StateType>, std::function<bool(StateType, StateType)>> explorationQueue; + + // A mapping of not yet explored states from the id to the state object. + std::map<StateType, DFTStatePointer> statesNotExplored; // Holds all skipped states which were not yet expanded. More concretely it is a mapping from matrix indices // to the corresponding skipped states. // Notice that we need an ordered map here to easily iterate in increasing order over state ids. + // TODO remove again std::map<StateType, DFTStatePointer> skippedStates; }; diff --git a/src/storage/DynamicPriorityQueue.h b/src/storage/DynamicPriorityQueue.h index a416a89b6..dc7949b4a 100644 --- a/src/storage/DynamicPriorityQueue.h +++ b/src/storage/DynamicPriorityQueue.h @@ -15,11 +15,11 @@ namespace storm { Compare compare; public: - explicit DynamicPriorityQueue(const Compare& compare) : container(), compare(compare) { + explicit DynamicPriorityQueue(Compare const& compare) : container(), compare(compare) { // Intentionally left empty } - explicit DynamicPriorityQueue(Container&& container, const Compare& compare) : container(std::move(container)), compare(compare) { + explicit DynamicPriorityQueue(Container&& container, Compare const& compare) : container(std::move(container)), compare(compare) { std::make_heap(container.begin(), container.end(), compare); } diff --git a/src/storage/dft/DFTState.cpp b/src/storage/dft/DFTState.cpp index 208dd6bf0..4482a97a0 100644 --- a/src/storage/dft/DFTState.cpp +++ b/src/storage/dft/DFTState.cpp @@ -6,7 +6,8 @@ namespace storm { namespace storage { template<typename ValueType> - DFTState<ValueType>::DFTState(DFT<ValueType> const& dft, DFTStateGenerationInfo const& stateGenerationInfo, size_t id) : mStatus(dft.stateVectorSize()), mId(id), mDft(dft), mStateGenerationInfo(stateGenerationInfo), exploreHeuristic() { + DFTState<ValueType>::DFTState(DFT<ValueType> const& dft, DFTStateGenerationInfo const& stateGenerationInfo, size_t id) : mStatus(dft.stateVectorSize()), mId(id), mPseudoState(false), mDft(dft), mStateGenerationInfo(stateGenerationInfo), exploreHeuristic() { + // TODO Matthias: use construct() // Initialize uses for(size_t spareId : mDft.getSpareIndices()) { @@ -34,10 +35,16 @@ namespace storm { sortFailableBEs(); } + + template<typename ValueType> + DFTState<ValueType>::DFTState(storm::storage::BitVector const& status, DFT<ValueType> const& dft, DFTStateGenerationInfo const& stateGenerationInfo, size_t id) : mStatus(status), mId(id), mPseudoState(true), mDft(dft), mStateGenerationInfo(stateGenerationInfo), exploreHeuristic() { + // Intentionally left empty + } template<typename ValueType> - DFTState<ValueType>::DFTState(storm::storage::BitVector const& status, DFT<ValueType> const& dft, DFTStateGenerationInfo const& stateGenerationInfo, size_t id) : mStatus(status), mId(id), mDft(dft), mStateGenerationInfo(stateGenerationInfo), exploreHeuristic() { - + void DFTState<ValueType>::construct() { + STORM_LOG_TRACE("Construct concrete state from pseudo state " << mDft.getStateString(mStatus, mStateGenerationInfo, mId)); + STORM_LOG_ASSERT(mPseudoState, "Only pseudo states can be constructed."); for(size_t index = 0; index < mDft.nrElements(); ++index) { // Initialize currently failable BE if (mDft.isBasicElement(index) && isOperational(index)) { @@ -68,6 +75,7 @@ namespace storm { STORM_LOG_TRACE("New dependency failure: " << dependency->toString()); } } + mPseudoState = false; } template<typename ValueType> @@ -406,6 +414,9 @@ namespace storm { n = tmp; } while (n > 0); } + if (changed) { + mPseudoState = true; + } return changed; } diff --git a/src/storage/dft/DFTState.h b/src/storage/dft/DFTState.h index a6644f349..b2fb803da 100644 --- a/src/storage/dft/DFTState.h +++ b/src/storage/dft/DFTState.h @@ -30,19 +30,37 @@ namespace storm { std::vector<size_t> mCurrentlyNotFailableBE; std::vector<size_t> mFailableDependencies; std::vector<size_t> mUsedRepresentants; + bool mPseudoState; bool mValid = true; const DFT<ValueType>& mDft; const DFTStateGenerationInfo& mStateGenerationInfo; storm::builder::DFTExplorationHeuristic<ValueType> exploreHeuristic; public: + /** + * Construct the initial state. + * + * @param dft DFT + * @param stateGenerationInfo General information for state generation + * @param id State id + */ DFTState(DFT<ValueType> const& dft, DFTStateGenerationInfo const& stateGenerationInfo, size_t id); - + /** - * Construct state from underlying bitvector. + * Construct temporary pseudo state. The actual state is constructed later. + * + * @param status BitVector representing the status of the state. + * @param dft DFT + * @param stateGenerationInfo General information for state generation + * @param id Pseudo state id */ DFTState(storm::storage::BitVector const& status, DFT<ValueType> const& dft, DFTStateGenerationInfo const& stateGenerationInfo, size_t id); + /** + * Construct concerete state from pseudo state by using the underlying bitvector. + */ + void construct(); + std::shared_ptr<DFTState<ValueType>> copy() const; DFTElementState getElementState(size_t id) const; @@ -101,6 +119,10 @@ namespace storm { return !mValid; } + bool isPseudoState() const { + return mPseudoState; + } + void setHeuristicValues(size_t depth, ValueType rate, ValueType exitRate) { exploreHeuristic.setHeuristicValues(depth, rate, exitRate); } From 26d0a3a7a4442f823c78d4edd9d53c54b67ea584 Mon Sep 17 00:00:00 2001 From: Mavo <matthias.volk@rwth-aachen.de> Date: Mon, 17 Oct 2016 13:20:31 +0200 Subject: [PATCH 27/65] Fixed smaller bugs Former-commit-id: 4c9c22bca138e4cc2de148480964484e253ee2cf --- src/builder/ExplicitDFTModelBuilderApprox.cpp | 27 +++++++++++-------- src/storage/DynamicPriorityQueue.h | 4 +++ src/storage/dft/DFT.cpp | 3 +-- src/storage/dft/DFTState.cpp | 5 ++++ 4 files changed, 26 insertions(+), 13 deletions(-) diff --git a/src/builder/ExplicitDFTModelBuilderApprox.cpp b/src/builder/ExplicitDFTModelBuilderApprox.cpp index 76ace8dda..cb04f3135 100644 --- a/src/builder/ExplicitDFTModelBuilderApprox.cpp +++ b/src/builder/ExplicitDFTModelBuilderApprox.cpp @@ -441,17 +441,22 @@ namespace storm { if (stateStorage.stateToId.contains(state->status())) { // State already exists stateId = stateStorage.stateToId.getValue(state->status()); - STORM_LOG_TRACE("State " << dft.getStateString(state) << " already exists"); - - if (!changed && statesNotExplored.at(stateId)->isPseudoState()) { - // Create pseudo state now - STORM_LOG_ASSERT(statesNotExplored.at(stateId)->getId() == stateId, "Ids do not match."); - STORM_LOG_ASSERT(statesNotExplored.at(stateId)->status() == state->status(), "Pseudo states do not coincide."); - state->setId(stateId); - // Update mapping to map to concrete state now - statesNotExplored[stateId] = state; - // We do not push the new state on the exploration queue as the pseudo state was already pushed - STORM_LOG_TRACE("Created pseudo state " << dft.getStateString(state)); + STORM_LOG_TRACE("State " << dft.getStateString(state) << " with id " << stateId << " already exists"); + if (!changed) { + // Check if state is pseudo state + // If state is explored already the possible pseudo state was already constructed + auto iter = statesNotExplored.find(stateId); + if (iter != statesNotExplored.end() && iter->second->isPseudoState()) { + // Create pseudo state now + STORM_LOG_ASSERT(iter->second->getId() == stateId, "Ids do not match."); + STORM_LOG_ASSERT(iter->second->status() == state->status(), "Pseudo states do not coincide."); + state->setId(stateId); + // Update mapping to map to concrete state now + statesNotExplored[stateId] = state; + // TODO Matthias: copy explorationHeuristic + // We do not push the new state on the exploration queue as the pseudo state was already pushed + STORM_LOG_TRACE("Created pseudo state " << dft.getStateString(state)); + } } } else { // State does not exist yet diff --git a/src/storage/DynamicPriorityQueue.h b/src/storage/DynamicPriorityQueue.h index dc7949b4a..9df114fbf 100644 --- a/src/storage/DynamicPriorityQueue.h +++ b/src/storage/DynamicPriorityQueue.h @@ -60,6 +60,10 @@ namespace storm { return item; } + Container getContainer() const { + return container; + } + }; } } diff --git a/src/storage/dft/DFT.cpp b/src/storage/dft/DFT.cpp index 87ee74cdd..b8024ef56 100644 --- a/src/storage/dft/DFT.cpp +++ b/src/storage/dft/DFT.cpp @@ -481,8 +481,7 @@ namespace storm { } else { useId = getChild(elem->id(), nrUsedChild); } - bool isActive = status[stateGenerationInfo.getSpareActivationIndex(useId)]; - if(useId == elem->id() || isActive) { + if(useId == elem->id() || status[stateGenerationInfo.getSpareActivationIndex(useId)]) { stream << "actively "; } stream << "using " << useId << "]"; diff --git a/src/storage/dft/DFTState.cpp b/src/storage/dft/DFTState.cpp index 4482a97a0..f6bb692a7 100644 --- a/src/storage/dft/DFTState.cpp +++ b/src/storage/dft/DFTState.cpp @@ -44,6 +44,11 @@ namespace storm { template<typename ValueType> void DFTState<ValueType>::construct() { STORM_LOG_TRACE("Construct concrete state from pseudo state " << mDft.getStateString(mStatus, mStateGenerationInfo, mId)); + // Clear information from pseudo state + mCurrentlyFailableBE.clear(); + mCurrentlyNotFailableBE.clear(); + mFailableDependencies.clear(); + mUsedRepresentants.clear(); STORM_LOG_ASSERT(mPseudoState, "Only pseudo states can be constructed."); for(size_t index = 0; index < mDft.nrElements(); ++index) { // Initialize currently failable BE From 4a6f53031e01069f68789c205ff70b77f56c504d Mon Sep 17 00:00:00 2001 From: Mavo <matthias.volk@rwth-aachen.de> Date: Tue, 18 Oct 2016 14:42:52 +0200 Subject: [PATCH 28/65] Choose different approximation heuristics Former-commit-id: e9ddae066b005a67e839a1f31405dd72d7790d0e --- src/builder/DftExplorationHeuristic.cpp | 57 +++++++++---------- src/builder/DftExplorationHeuristic.h | 9 ++- src/builder/ExplicitDFTModelBuilderApprox.cpp | 15 +++-- src/builder/ExplicitDFTModelBuilderApprox.h | 4 +- src/modelchecker/dft/DFTModelChecker.cpp | 4 +- src/storage/dft/DFTState.h | 6 +- 6 files changed, 48 insertions(+), 47 deletions(-) diff --git a/src/builder/DftExplorationHeuristic.cpp b/src/builder/DftExplorationHeuristic.cpp index 727c29c37..e14c34dee 100644 --- a/src/builder/DftExplorationHeuristic.cpp +++ b/src/builder/DftExplorationHeuristic.cpp @@ -15,6 +15,13 @@ namespace storm { // Intentionally left empty } + template<typename ValueType> + void DFTExplorationHeuristic<ValueType>::setHeuristicValues(size_t depth, ValueType rate, ValueType exitRate) { + this->depth = std::min(this->depth, depth); + this->rate = std::max(this->rate, rate); + this->exitRate = std::max(this->exitRate, exitRate); + } + template<typename ValueType> bool DFTExplorationHeuristic<ValueType>::isSkip(double approximationThreshold, ApproximationHeuristic heuristic) const { if (!skip) { @@ -26,8 +33,9 @@ namespace storm { case ApproximationHeuristic::DEPTH: return depth > approximationThreshold; case ApproximationHeuristic::RATERATIO: - // TODO Matthias: implement - STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Heuristic rate ration does not work."); + return getRateRatio() < approximationThreshold; + default: + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Heuristic not known."); } } @@ -36,52 +44,41 @@ namespace storm { skip = false; } - template<typename ValueType> size_t DFTExplorationHeuristic<ValueType>::getDepth() const { return depth; } template<typename ValueType> - void DFTExplorationHeuristic<ValueType>::setHeuristicValues(size_t depth, ValueType rate, ValueType exitRate) { - this->depth = depth; - this->rate = rate; - this->exitRate = exitRate; - } - - template<typename ValueType> - double DFTExplorationHeuristic<ValueType>::getPriority() const { - STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Approximation works only for double."); + bool DFTExplorationHeuristic<ValueType>::compare(DFTExplorationHeuristic<ValueType> other, ApproximationHeuristic heuristic) { + switch (heuristic) { + case ApproximationHeuristic::NONE: + // Just use memory address for comparing + // TODO Matthias: better idea? + return this > &other; + case ApproximationHeuristic::DEPTH: + return this->depth > other.depth; + case ApproximationHeuristic::RATERATIO: + return this->getRateRatio() < other.getRateRatio(); + default: + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Heuristic not known."); + } } template<> - double DFTExplorationHeuristic<double>::getPriority() const { - // TODO Matthias: change according to heuristic - //return rate/exitRate; - return depth; + double DFTExplorationHeuristic<double>::getRateRatio() const { + return rate/exitRate; } template<> - double DFTExplorationHeuristic<storm::RationalFunction>::getPriority() const { - STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Approximation works only for double."); - /*std::cout << (rate / exitRate) << " < " << threshold << ": " << (number < threshold) << std::endl; - std::map<storm::Variable, storm::RationalNumber> mapping; - storm::RationalFunction eval(number.evaluate(mapping)); - std::cout << "Evaluated: " << eval << std::endl; - return eval < threshold;*/ - } - - template<typename ValueType> - bool compareDepth(std::shared_ptr<storm::storage::DFTState<ValueType>> stateA, std::shared_ptr<storm::storage::DFTState<ValueType>> stateB) { - return stateA->getPriority() > stateB->getPriority(); + double DFTExplorationHeuristic<storm::RationalFunction>::getRateRatio() const { + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Heuristic rate ration does not work."); } template class DFTExplorationHeuristic<double>; - template bool compareDepth(std::shared_ptr<storm::storage::DFTState<double>>, std::shared_ptr<storm::storage::DFTState<double>>); #ifdef STORM_HAVE_CARL template class DFTExplorationHeuristic<storm::RationalFunction>; - template bool compareDepth(std::shared_ptr<storm::storage::DFTState<storm::RationalFunction>>, std::shared_ptr<storm::storage::DFTState<storm::RationalFunction>>); #endif } } diff --git a/src/builder/DftExplorationHeuristic.h b/src/builder/DftExplorationHeuristic.h index a9958443a..3fd3d72a8 100644 --- a/src/builder/DftExplorationHeuristic.h +++ b/src/builder/DftExplorationHeuristic.h @@ -33,19 +33,18 @@ namespace storm { size_t getDepth() const; - double getPriority() const; - + bool compare(DFTExplorationHeuristic<ValueType> other, ApproximationHeuristic heuristic); + private: + double getRateRatio() const; + bool skip; size_t depth; ValueType rate; ValueType exitRate; }; - - template<typename ValueType> - bool compareDepth(std::shared_ptr<storm::storage::DFTState<ValueType>> stateA, std::shared_ptr<storm::storage::DFTState<ValueType>> stateB); } } diff --git a/src/builder/ExplicitDFTModelBuilderApprox.cpp b/src/builder/ExplicitDFTModelBuilderApprox.cpp index cb04f3135..5acf60980 100644 --- a/src/builder/ExplicitDFTModelBuilderApprox.cpp +++ b/src/builder/ExplicitDFTModelBuilderApprox.cpp @@ -39,10 +39,10 @@ namespace storm { } template<typename ValueType, typename StateType> - void ExplicitDFTModelBuilderApprox<ValueType, StateType>::buildModel(LabelOptions const& labelOpts, bool firstTime, double approximationThreshold) { + void ExplicitDFTModelBuilderApprox<ValueType, StateType>::buildModel(LabelOptions const& labelOpts, size_t iteration, double approximationThreshold) { STORM_LOG_TRACE("Generating DFT state space"); - if (firstTime) { + if (iteration < 1) { // Initialize modelComponents.markovianStates = storm::storage::BitVector(INITIAL_BITVECTOR_SIZE); @@ -80,6 +80,14 @@ namespace storm { initializeNextIteration(); } + switch (heuristic) { + case storm::builder::ApproximationHeuristic::DEPTH: + approximationThreshold = iteration; + break; + case storm::builder::ApproximationHeuristic::RATERATIO: + approximationThreshold = std::pow(0.1, iteration) * approximationThreshold; + break; + } exploreStateSpace(approximationThreshold); size_t stateSize = stateStorage.getNumberOfStates() + (mergeFailedStates ? 1 : 0); @@ -486,8 +494,7 @@ namespace storm { template<typename ValueType, typename StateType> bool ExplicitDFTModelBuilderApprox<ValueType, StateType>::isPriorityGreater(StateType idA, StateType idB) const { - // TODO Matthias: compare directly and according to heuristic - return storm::builder::compareDepth(statesNotExplored.at(idA), statesNotExplored.at(idB)); + return statesNotExplored.at(idA)->comparePriority(statesNotExplored.at(idB), heuristic); } diff --git a/src/builder/ExplicitDFTModelBuilderApprox.h b/src/builder/ExplicitDFTModelBuilderApprox.h index 5e841fddc..74798e889 100644 --- a/src/builder/ExplicitDFTModelBuilderApprox.h +++ b/src/builder/ExplicitDFTModelBuilderApprox.h @@ -159,10 +159,10 @@ namespace storm { * Build model from DFT. * * @param labelOpts Options for labeling. - * @param firstTime Flag indicating if the model is built for the first time or rebuilt. + * @param iteration Current number of iteration. * @param approximationThreshold Threshold determining when to skip exploring states. */ - void buildModel(LabelOptions const& labelOpts, bool firstTime, double approximationThreshold = 0.0); + void buildModel(LabelOptions const& labelOpts, size_t iteration, double approximationThreshold = 0.0); /*! * Get the built model. diff --git a/src/modelchecker/dft/DFTModelChecker.cpp b/src/modelchecker/dft/DFTModelChecker.cpp index 717ace4c3..1fa1ca245 100644 --- a/src/modelchecker/dft/DFTModelChecker.cpp +++ b/src/modelchecker/dft/DFTModelChecker.cpp @@ -150,7 +150,6 @@ namespace storm { // Comparator for checking the error of the approximation storm::utility::ConstantsComparator<ValueType> comparator; // Build approximate Markov Automata for lower and upper bound - double currentApproximationError = approximationError; approximation_result approxResult = std::make_pair(storm::utility::zero<ValueType>(), storm::utility::zero<ValueType>()); std::chrono::high_resolution_clock::time_point explorationStart; std::shared_ptr<storm::models::sparse::Model<ValueType>> model; @@ -163,8 +162,7 @@ namespace storm { explorationStart = std::chrono::high_resolution_clock::now(); STORM_LOG_INFO("Building model..."); // TODO Matthias refine model using existing model and MC results - currentApproximationError = pow(0.1, iteration) * approximationError; - builder.buildModel(labeloptions, iteration < 1, iteration); + builder.buildModel(labeloptions, iteration, approximationError); // TODO Matthias: possible to do bisimulation on approximated model and not on concrete one? diff --git a/src/storage/dft/DFTState.h b/src/storage/dft/DFTState.h index b2fb803da..eb3164516 100644 --- a/src/storage/dft/DFTState.h +++ b/src/storage/dft/DFTState.h @@ -139,10 +139,10 @@ namespace storm { return exploreHeuristic.isSkip(approximationThreshold, heuristic); } - double getPriority() const { - return exploreHeuristic.getPriority(); + bool comparePriority(std::shared_ptr<DFTState> const& other, storm::builder::ApproximationHeuristic heuristic) { + return this->exploreHeuristic.compare(other->exploreHeuristic, heuristic); } - + storm::storage::BitVector const& status() const { return mStatus; } From 1bfd9747957838406ddb033ac093ad99d0793902 Mon Sep 17 00:00:00 2001 From: Mavo <matthias.volk@rwth-aachen.de> Date: Tue, 18 Oct 2016 15:58:43 +0200 Subject: [PATCH 29/65] Minor fixes Former-commit-id: 9344ebe5aac6a410ae5e84b4230037401c2946f3 --- src/builder/ExplicitDFTModelBuilderApprox.cpp | 19 +++++++++++++++---- src/generator/DftNextStateGenerator.cpp | 4 ---- src/modelchecker/dft/DFTModelChecker.cpp | 2 +- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/builder/ExplicitDFTModelBuilderApprox.cpp b/src/builder/ExplicitDFTModelBuilderApprox.cpp index 5acf60980..fda689be7 100644 --- a/src/builder/ExplicitDFTModelBuilderApprox.cpp +++ b/src/builder/ExplicitDFTModelBuilderApprox.cpp @@ -75,12 +75,16 @@ namespace storm { STORM_LOG_ASSERT(stateStorage.initialStateIndices.size() == 1, "Only one initial state assumed."); initialStateIndex = stateStorage.initialStateIndices[0]; STORM_LOG_TRACE("Initial state: " << initialStateIndex); - + // Initialize heuristic values for inital state + statesNotExplored.at(initialStateIndex)->setHeuristicValues(0, storm::utility::zero<ValueType>(), storm::utility::zero<ValueType>()); } else { initializeNextIteration(); } switch (heuristic) { + case storm::builder::ApproximationHeuristic::NONE: + // Do not change anything + break; case storm::builder::ApproximationHeuristic::DEPTH: approximationThreshold = iteration; break; @@ -246,6 +250,7 @@ namespace storm { matrixBuilder.newRowGroup(); // Try to explore the next state + bool fixQueue = false; generator.load(currentState); if (currentState->isSkip(approximationThreshold, heuristic)) { @@ -270,15 +275,21 @@ namespace storm { for (auto const& stateProbabilityPair : choice) { // Set transition to state id + offset. This helps in only remapping all previously skipped states. matrixBuilder.addTransition(matrixBuilder.mappingOffset + stateProbabilityPair.first, stateProbabilityPair.second); - // TODO Matthias: set heuristic values here + // Set heuristic values for reached states + auto iter = statesNotExplored.find(stateProbabilityPair.first); + if (iter != statesNotExplored.end()) { + iter->second->setHeuristicValues(currentState, stateProbabilityPair.second, choice.getTotalMass()); + fixQueue = true; + } } matrixBuilder.finishRow(); } } // Update priority queue - // TODO Matthias: only when necessary - explorationQueue.fix(); + if (fixQueue) { + explorationQueue.fix(); + } } // end exploration } diff --git a/src/generator/DftNextStateGenerator.cpp b/src/generator/DftNextStateGenerator.cpp index e61e8e331..69fca22b4 100644 --- a/src/generator/DftNextStateGenerator.cpp +++ b/src/generator/DftNextStateGenerator.cpp @@ -20,7 +20,6 @@ namespace storm { template<typename ValueType, typename StateType> std::vector<StateType> DftNextStateGenerator<ValueType, StateType>::getInitialStates(StateToIdCallback const& stateToIdCallback) { DFTStatePointer initialState = std::make_shared<storm::storage::DFTState<ValueType>>(mDft, mStateGenerationInfo, 0); - initialState->setHeuristicValues(0, storm::utility::zero<ValueType>(), storm::utility::zero<ValueType>()); // Register initial state StateType id = stateToIdCallback(initialState); @@ -158,10 +157,8 @@ namespace storm { ValueType remainingProbability = storm::utility::one<ValueType>() - probability; choice.addProbability(unsuccessfulStateId, remainingProbability); STORM_LOG_TRACE("Added transition to " << unsuccessfulStateId << " with remaining probability " << remainingProbability); - unsuccessfulState->setHeuristicValues(state, remainingProbability, storm::utility::one<ValueType>()); } result.addChoice(std::move(choice)); - newState->setHeuristicValues(state, probability, storm::utility::one<ValueType>()); } else { // Failure is due to "normal" BE failure // Set failure rate according to activation @@ -174,7 +171,6 @@ namespace storm { STORM_LOG_ASSERT(!storm::utility::isZero(rate), "Rate is 0."); choice.addProbability(newStateId, rate); STORM_LOG_TRACE("Added transition to " << newStateId << " with " << (isActive ? "active" : "passive") << " failure rate " << rate); - newState->setHeuristicValues(state, rate, choice.getTotalMass()); } ++currentFailable; diff --git a/src/modelchecker/dft/DFTModelChecker.cpp b/src/modelchecker/dft/DFTModelChecker.cpp index 1fa1ca245..cadc37ac7 100644 --- a/src/modelchecker/dft/DFTModelChecker.cpp +++ b/src/modelchecker/dft/DFTModelChecker.cpp @@ -204,7 +204,7 @@ namespace storm { if (approximationError >= 0.0) { storm::builder::ExplicitDFTModelBuilderApprox<ValueType> builder(dft, symmetries, enableDC); typename storm::builder::ExplicitDFTModelBuilderApprox<ValueType>::LabelOptions labeloptions; // TODO initialize this with the formula - builder.buildModel(labeloptions, true); + builder.buildModel(labeloptions, 0); model = builder.getModel(); } else { storm::builder::ExplicitDFTModelBuilder<ValueType> builder(dft, symmetries, enableDC); From ec8b5a23f2e8b7176762a0fa328f8e0db9ddfe39 Mon Sep 17 00:00:00 2001 From: Mavo <matthias.volk@rwth-aachen.de> Date: Thu, 20 Oct 2016 16:57:28 +0200 Subject: [PATCH 30/65] Fixed compile issues with under Linux Former-commit-id: 17f4d895ec52241f653fc9a175d4609c88d71a94 --- src/builder/DftSmtBuilder.cpp | 2 +- src/storm-dyftee.cpp | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/builder/DftSmtBuilder.cpp b/src/builder/DftSmtBuilder.cpp index 1d60b2c74..c1226531c 100644 --- a/src/builder/DftSmtBuilder.cpp +++ b/src/builder/DftSmtBuilder.cpp @@ -1,4 +1,4 @@ -#include "src/builder/DFTSMTBuilder.h" +#include "src/builder/DftSmtBuilder.h" #include "src/exceptions/NotImplementedException.h" namespace storm { diff --git a/src/storm-dyftee.cpp b/src/storm-dyftee.cpp index 51061050e..fb0c0b1e6 100644 --- a/src/storm-dyftee.cpp +++ b/src/storm-dyftee.cpp @@ -117,6 +117,7 @@ int main(const int argc, const char** argv) { parametric = generalSettings.isParametricSet(); #endif +#ifdef STORM_HAVE_Z3 if (dftSettings.solveWithSMT()) { // Solve with SMT if (parametric) { @@ -127,6 +128,7 @@ int main(const int argc, const char** argv) { storm::utility::cleanUp(); return 0; } +#endif // Set min or max bool minimal = true; From a624292ecea60e9983ef677b97c76accabc3a0a9 Mon Sep 17 00:00:00 2001 From: Mavo <matthias.volk@rwth-aachen.de> Date: Fri, 21 Oct 2016 18:28:01 +0200 Subject: [PATCH 31/65] Output no states Former-commit-id: d1548bb3fba7fe2ecbf4f20dc79eb61f4f9369f5 --- src/builder/ExplicitDFTModelBuilder.cpp | 1 + src/builder/ExplicitDFTModelBuilderApprox.cpp | 12 ++++++++++-- src/modelchecker/dft/DFTModelChecker.cpp | 5 ++++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/builder/ExplicitDFTModelBuilder.cpp b/src/builder/ExplicitDFTModelBuilder.cpp index 9481e3392..4a2f56b5f 100644 --- a/src/builder/ExplicitDFTModelBuilder.cpp +++ b/src/builder/ExplicitDFTModelBuilder.cpp @@ -58,6 +58,7 @@ namespace storm { STORM_LOG_ASSERT(mStates.getValue(pseudoStatePair.second) >= OFFSET_PSEUDO_STATE, "State is no pseudo state."); STORM_LOG_TRACE("Create pseudo state from bit vector " << pseudoStatePair.second); DFTStatePointer pseudoState = std::make_shared<storm::storage::DFTState<ValueType>>(pseudoStatePair.second, mDft, *mStateGenerationInfo, newIndex); + pseudoState->construct(); STORM_LOG_ASSERT(pseudoStatePair.second == pseudoState->status(), "Pseudo states do not coincide."); STORM_LOG_TRACE("Explore pseudo state " << mDft.getStateString(pseudoState) << " with id " << pseudoState->getId()); auto exploreResult = exploreStates(pseudoState, rowOffset, transitionMatrixBuilder, tmpMarkovianStates, modelComponents.exitRates); diff --git a/src/builder/ExplicitDFTModelBuilderApprox.cpp b/src/builder/ExplicitDFTModelBuilderApprox.cpp index fda689be7..268ec03dc 100644 --- a/src/builder/ExplicitDFTModelBuilderApprox.cpp +++ b/src/builder/ExplicitDFTModelBuilderApprox.cpp @@ -106,8 +106,7 @@ namespace storm { STORM_LOG_TRACE("State remapping: " << matrixBuilder.stateRemapping); STORM_LOG_TRACE("Markovian states: " << modelComponents.markovianStates); - STORM_LOG_DEBUG("Generated " << stateSize << " states"); - STORM_LOG_DEBUG("Skipped " << skippedStates.size() << " states"); + STORM_LOG_DEBUG("Model has " << stateSize << " states"); STORM_LOG_DEBUG("Model is " << (generator.isDeterministicModel() ? "deterministic" : "non-deterministic")); // Build transition matrix @@ -224,6 +223,8 @@ namespace storm { template<typename ValueType, typename StateType> void ExplicitDFTModelBuilderApprox<ValueType, StateType>::exploreStateSpace(double approximationThreshold) { + size_t nrExpandedStates = 0; + size_t nrSkippedStates = 0; // TODO Matthias: do not empty queue every time but break before while (!explorationQueue.empty()) { // Get the first state in the queue @@ -255,6 +256,7 @@ namespace storm { if (currentState->isSkip(approximationThreshold, heuristic)) { // Skip the current state + ++nrSkippedStates; STORM_LOG_TRACE("Skip expansion of state: " << dft.getStateString(currentState)); setMarkovian(true); // Add transition to target state with temporary value 0 @@ -265,6 +267,7 @@ namespace storm { matrixBuilder.finishRow(); } else { // Explore the current state + ++nrExpandedStates; storm::generator::StateBehavior<ValueType, StateType> behavior = generator.expand(std::bind(&ExplicitDFTModelBuilderApprox::getOrAddStateIndex, this, std::placeholders::_1)); STORM_LOG_ASSERT(!behavior.empty(), "Behavior is empty."); setMarkovian(behavior.begin()->isMarkovian()); @@ -273,6 +276,7 @@ namespace storm { for (auto const& choice : behavior) { // Add the probabilistic behavior to the matrix. for (auto const& stateProbabilityPair : choice) { + STORM_LOG_ASSERT(!storm::utility::isZero(stateProbabilityPair.second), "Probability zero."); // Set transition to state id + offset. This helps in only remapping all previously skipped states. matrixBuilder.addTransition(matrixBuilder.mappingOffset + stateProbabilityPair.first, stateProbabilityPair.second); // Set heuristic values for reached states @@ -291,6 +295,10 @@ namespace storm { explorationQueue.fix(); } } // end exploration + + STORM_LOG_INFO("Expanded " << nrExpandedStates << " states"); + STORM_LOG_INFO("Skipped " << nrSkippedStates << " states"); + STORM_LOG_ASSERT(nrSkippedStates == skippedStates.size(), "Nr skipped states is wrong"); } template<typename ValueType, typename StateType> diff --git a/src/modelchecker/dft/DFTModelChecker.cpp b/src/modelchecker/dft/DFTModelChecker.cpp index cadc37ac7..f19943f2f 100644 --- a/src/modelchecker/dft/DFTModelChecker.cpp +++ b/src/modelchecker/dft/DFTModelChecker.cpp @@ -169,6 +169,9 @@ namespace storm { // Build model for lower bound STORM_LOG_INFO("Getting model for lower bound..."); model = builder.getModelApproximation(true); + // We only output the info from the lower bound as the info for the upper bound is the same + STORM_LOG_INFO("No. states: " << model->getNumberOfStates()); + STORM_LOG_INFO("No. transitions: " << model->getNumberOfTransitions()); explorationTime += std::chrono::high_resolution_clock::now() - explorationStart; // Check lower bound std::unique_ptr<storm::modelchecker::CheckResult> result = checkModel(model, formula); @@ -201,7 +204,7 @@ namespace storm { STORM_LOG_INFO("Building Model..."); std::shared_ptr<storm::models::sparse::Model<ValueType>> model; // TODO Matthias: use only one builder if everything works again - if (approximationError >= 0.0) { + if (storm::settings::getModule<storm::settings::modules::DFTSettings>().isApproximationErrorSet()) { storm::builder::ExplicitDFTModelBuilderApprox<ValueType> builder(dft, symmetries, enableDC); typename storm::builder::ExplicitDFTModelBuilderApprox<ValueType>::LabelOptions labeloptions; // TODO initialize this with the formula builder.buildModel(labeloptions, 0); From a333d29d1663a877fd0980d5ecc562213463a9b9 Mon Sep 17 00:00:00 2001 From: Mavo <matthias.volk@rwth-aachen.de> Date: Sat, 22 Oct 2016 17:31:02 +0200 Subject: [PATCH 32/65] Hard coded heuristic to gain performance Former-commit-id: d0d869bb3e08535bdfa5c56ec2e1e4eb42395e81 --- src/builder/DftExplorationHeuristic.cpp | 84 +++++++++++-------- src/builder/DftExplorationHeuristic.h | 71 ++++++++++++---- src/builder/ExplicitDFTModelBuilderApprox.cpp | 71 ++++++++-------- src/builder/ExplicitDFTModelBuilderApprox.h | 15 ++-- src/storage/dft/DFTState.cpp | 8 +- src/storage/dft/DFTState.h | 23 +---- 6 files changed, 150 insertions(+), 122 deletions(-) diff --git a/src/builder/DftExplorationHeuristic.cpp b/src/builder/DftExplorationHeuristic.cpp index e14c34dee..4bc72ccc1 100644 --- a/src/builder/DftExplorationHeuristic.cpp +++ b/src/builder/DftExplorationHeuristic.cpp @@ -3,7 +3,6 @@ #include "src/utility/macros.h" #include "src/utility/constants.h" #include "src/exceptions/NotImplementedException.h" -#include "src/storage/dft/DFTState.h" #include <limits> @@ -11,74 +10,87 @@ namespace storm { namespace builder { template<typename ValueType> - DFTExplorationHeuristic<ValueType>::DFTExplorationHeuristic() : skip(true), depth(std::numeric_limits<std::size_t>::max()), rate(storm::utility::zero<ValueType>()), exitRate(storm::utility::zero<ValueType>()) { + DFTExplorationHeuristic<ValueType>::DFTExplorationHeuristic(size_t id) : id(id), expand(false), depth(std::numeric_limits<std::size_t>::max()), rate(storm::utility::zero<ValueType>()), exitRate(storm::utility::zero<ValueType>()) { // Intentionally left empty } template<typename ValueType> - void DFTExplorationHeuristic<ValueType>::setHeuristicValues(size_t depth, ValueType rate, ValueType exitRate) { + void DFTExplorationHeuristic<ValueType>::updateHeuristicValues(size_t depth, ValueType rate, ValueType exitRate) { this->depth = std::min(this->depth, depth); this->rate = std::max(this->rate, rate); this->exitRate = std::max(this->exitRate, exitRate); } template<typename ValueType> - bool DFTExplorationHeuristic<ValueType>::isSkip(double approximationThreshold, ApproximationHeuristic heuristic) const { - if (!skip) { - return false; - } - switch (heuristic) { - case ApproximationHeuristic::NONE: - return false; - case ApproximationHeuristic::DEPTH: - return depth > approximationThreshold; - case ApproximationHeuristic::RATERATIO: - return getRateRatio() < approximationThreshold; - default: - STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Heuristic not known."); - } + DFTExplorationHeuristicNone<ValueType>::DFTExplorationHeuristicNone(size_t id) : DFTExplorationHeuristic<ValueType>(id) { + // Intentionally left empty + } + + template<typename ValueType> + bool DFTExplorationHeuristicNone<ValueType>::isSkip(double approximationThreshold) const { + return false; + } + + template<typename ValueType> + bool DFTExplorationHeuristicNone<ValueType>::operator<(DFTExplorationHeuristicNone<ValueType> const& other) const { + // Just use memory address for comparing + // TODO Matthias: better idea? + return this > &other; + } + + template<typename ValueType> + DFTExplorationHeuristicDepth<ValueType>::DFTExplorationHeuristicDepth(size_t id) : DFTExplorationHeuristic<ValueType>(id) { + // Intentionally left empty + } + + template<typename ValueType> + bool DFTExplorationHeuristicDepth<ValueType>::isSkip(double approximationThreshold) const { + return !this->expand && this->depth > approximationThreshold; + } + + template<typename ValueType> + bool DFTExplorationHeuristicDepth<ValueType>::operator<(DFTExplorationHeuristicDepth<ValueType> const& other) const { + return this->depth > other.depth; } template<typename ValueType> - void DFTExplorationHeuristic<ValueType>::setNotSkip() { - skip = false; + DFTExplorationHeuristicRateRatio<ValueType>::DFTExplorationHeuristicRateRatio(size_t id) : DFTExplorationHeuristic<ValueType>(id) { + // Intentionally left empty } template<typename ValueType> - size_t DFTExplorationHeuristic<ValueType>::getDepth() const { - return depth; + bool DFTExplorationHeuristicRateRatio<ValueType>::isSkip(double approximationThreshold) const { + return !this->expand && this->getRateRatio() < approximationThreshold; } template<typename ValueType> - bool DFTExplorationHeuristic<ValueType>::compare(DFTExplorationHeuristic<ValueType> other, ApproximationHeuristic heuristic) { - switch (heuristic) { - case ApproximationHeuristic::NONE: - // Just use memory address for comparing - // TODO Matthias: better idea? - return this > &other; - case ApproximationHeuristic::DEPTH: - return this->depth > other.depth; - case ApproximationHeuristic::RATERATIO: - return this->getRateRatio() < other.getRateRatio(); - default: - STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Heuristic not known."); - } + bool DFTExplorationHeuristicRateRatio<ValueType>::operator<(DFTExplorationHeuristicRateRatio<ValueType> const& other) const { + return this->getRateRatio() < other.getRateRatio(); } + template<> - double DFTExplorationHeuristic<double>::getRateRatio() const { + double DFTExplorationHeuristicRateRatio<double>::getRateRatio() const { return rate/exitRate; } template<> - double DFTExplorationHeuristic<storm::RationalFunction>::getRateRatio() const { + double DFTExplorationHeuristicRateRatio<storm::RationalFunction>::getRateRatio() const { STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Heuristic rate ration does not work."); } + + // Instantiate templates. template class DFTExplorationHeuristic<double>; + template class DFTExplorationHeuristicNone<double>; + template class DFTExplorationHeuristicDepth<double>; + template class DFTExplorationHeuristicRateRatio<double>; #ifdef STORM_HAVE_CARL template class DFTExplorationHeuristic<storm::RationalFunction>; + template class DFTExplorationHeuristicNone<storm::RationalFunction>; + template class DFTExplorationHeuristicDepth<storm::RationalFunction>; + template class DFTExplorationHeuristicRateRatio<storm::RationalFunction>; #endif } } diff --git a/src/builder/DftExplorationHeuristic.h b/src/builder/DftExplorationHeuristic.h index 3fd3d72a8..8f12d2ec1 100644 --- a/src/builder/DftExplorationHeuristic.h +++ b/src/builder/DftExplorationHeuristic.h @@ -2,16 +2,9 @@ #define STORM_BUILDER_DFTEXPLORATIONHEURISTIC_H_ #include <memory> -#include <algorithm> namespace storm { - // Forward declaration - namespace storage { - template<typename ValueType> - class DFTState; - } - namespace builder { /*! @@ -19,32 +12,74 @@ namespace storm { */ enum class ApproximationHeuristic { NONE, DEPTH, RATERATIO }; + + /*! + * General super class for appoximation heuristics. + */ template<typename ValueType> class DFTExplorationHeuristic { public: - DFTExplorationHeuristic(); - - void setHeuristicValues(size_t depth, ValueType rate, ValueType exitRate); - - bool isSkip(double approximationThreshold, ApproximationHeuristic heuristic) const; + DFTExplorationHeuristic(size_t id); - void setNotSkip(); + void updateHeuristicValues(size_t depth, ValueType rate, ValueType exitRate); - size_t getDepth() const; + virtual bool isSkip(double approximationThreshold) const = 0; - bool compare(DFTExplorationHeuristic<ValueType> other, ApproximationHeuristic heuristic); + void markExpand() { + expand = true; + } - private: + size_t getId() const { + return id; + } - double getRateRatio() const; + size_t getDepth() const { + return depth; + } - bool skip; + protected: + size_t id; + bool expand; size_t depth; ValueType rate; ValueType exitRate; }; + + template<typename ValueType> + class DFTExplorationHeuristicNone : public DFTExplorationHeuristic<ValueType> { + public: + DFTExplorationHeuristicNone(size_t id); + + bool isSkip(double approximationThreshold) const override; + + bool operator<(DFTExplorationHeuristicNone<ValueType> const& other) const; + }; + + template<typename ValueType> + class DFTExplorationHeuristicDepth : public DFTExplorationHeuristic<ValueType> { + public: + DFTExplorationHeuristicDepth(size_t id); + + bool isSkip(double approximationThreshold) const override; + + bool operator<(DFTExplorationHeuristicDepth<ValueType> const& other) const; + }; + + template<typename ValueType> + class DFTExplorationHeuristicRateRatio : public DFTExplorationHeuristic<ValueType> { + public: + DFTExplorationHeuristicRateRatio(size_t id); + + bool isSkip(double approximationThreshold) const override; + + bool operator<(DFTExplorationHeuristicRateRatio<ValueType> const& other) const; + + private: + double getRateRatio() const; + }; + } } diff --git a/src/builder/ExplicitDFTModelBuilderApprox.cpp b/src/builder/ExplicitDFTModelBuilderApprox.cpp index 268ec03dc..5595a5f49 100644 --- a/src/builder/ExplicitDFTModelBuilderApprox.cpp +++ b/src/builder/ExplicitDFTModelBuilderApprox.cpp @@ -31,8 +31,8 @@ namespace storm { generator(dft, *stateGenerationInfo, enableDC, mergeFailedStates), matrixBuilder(!generator.isDeterministicModel()), stateStorage(((dft.stateVectorSize() / 64) + 1) * 64), - explorationQueue([this](StateType idA, StateType idB) { - return isPriorityGreater(idA, idB); + explorationQueue([this](ExplorationHeuristicPointer a, ExplorationHeuristicPointer b) { + return *a < *b; }) { // Intentionally left empty. @@ -76,7 +76,7 @@ namespace storm { initialStateIndex = stateStorage.initialStateIndices[0]; STORM_LOG_TRACE("Initial state: " << initialStateIndex); // Initialize heuristic values for inital state - statesNotExplored.at(initialStateIndex)->setHeuristicValues(0, storm::utility::zero<ValueType>(), storm::utility::zero<ValueType>()); + statesNotExplored.at(initialStateIndex).second->updateHeuristicValues(0, storm::utility::zero<ValueType>(), storm::utility::zero<ValueType>()); } else { initializeNextIteration(); } @@ -128,8 +128,8 @@ namespace storm { // Push skipped states to explore queue // TODO Matthias: remove for (auto const& skippedState : skippedStates) { - statesNotExplored[skippedState.second->getId()] = skippedState.second; - explorationQueue.push(skippedState.second->getId()); + statesNotExplored[skippedState.second.first->getId()] = skippedState.second; + explorationQueue.push(skippedState.second.second); } // Initialize matrix builder again @@ -157,7 +157,7 @@ namespace storm { matrixBuilder.mappingOffset = nrStates; STORM_LOG_TRACE("# expanded states: " << nrExpandedStates); StateType skippedIndex = nrExpandedStates; - std::map<StateType, DFTStatePointer> skippedStatesNew; + std::map<StateType, std::pair<DFTStatePointer, ExplorationHeuristicPointer>> skippedStatesNew; for (size_t id = 0; id < matrixBuilder.stateRemapping.size(); ++id) { StateType index = matrixBuilder.stateRemapping[id]; auto itFind = skippedStates.find(index); @@ -204,7 +204,7 @@ namespace storm { auto itFind = skippedStates.find(itEntry->getColumn()); if (itFind != skippedStates.end()) { // Set id for skipped states as we remap it later - matrixBuilder.addTransition(matrixBuilder.mappingOffset + itFind->second->getId(), itEntry->getValue()); + matrixBuilder.addTransition(matrixBuilder.mappingOffset + itFind->second.first->getId(), itEntry->getValue()); } else { // Set newly remapped index for expanded states matrixBuilder.addTransition(indexRemapping[itEntry->getColumn()], itEntry->getValue()); @@ -228,10 +228,12 @@ namespace storm { // TODO Matthias: do not empty queue every time but break before while (!explorationQueue.empty()) { // Get the first state in the queue - StateType currentId = explorationQueue.popTop(); + ExplorationHeuristicPointer currentExplorationHeuristic = explorationQueue.popTop(); + StateType currentId = currentExplorationHeuristic->getId(); auto itFind = statesNotExplored.find(currentId); STORM_LOG_ASSERT(itFind != statesNotExplored.end(), "Id " << currentId << " not found"); - DFTStatePointer currentState = itFind->second; + DFTStatePointer currentState = itFind->second.first; + STORM_LOG_ASSERT(currentExplorationHeuristic == itFind->second.second, "Exploration heuristics do not match"); STORM_LOG_ASSERT(currentState->getId() == currentId, "Ids do not match"); // Remove it from the list of not explored states statesNotExplored.erase(itFind); @@ -254,7 +256,7 @@ namespace storm { bool fixQueue = false; generator.load(currentState); - if (currentState->isSkip(approximationThreshold, heuristic)) { + if (currentExplorationHeuristic->isSkip(approximationThreshold)) { // Skip the current state ++nrSkippedStates; STORM_LOG_TRACE("Skip expansion of state: " << dft.getStateString(currentState)); @@ -263,7 +265,7 @@ namespace storm { // TODO Matthias: what to do when there is no unique target state? matrixBuilder.addTransition(failedStateId, storm::utility::zero<ValueType>()); // Remember skipped state - skippedStates[matrixBuilder.getCurrentRowGroup() - 1] = currentState; + skippedStates[matrixBuilder.getCurrentRowGroup() - 1] = std::make_pair(currentState, currentExplorationHeuristic); matrixBuilder.finishRow(); } else { // Explore the current state @@ -282,7 +284,13 @@ namespace storm { // Set heuristic values for reached states auto iter = statesNotExplored.find(stateProbabilityPair.first); if (iter != statesNotExplored.end()) { - iter->second->setHeuristicValues(currentState, stateProbabilityPair.second, choice.getTotalMass()); + // Update heuristic values + DFTStatePointer state = iter->second.first; + if (state->hasFailed(dft.getTopLevelIndex()) || state->isFailsafe(dft.getTopLevelIndex()) || state->nrFailableDependencies() > 0 || (state->nrFailableDependencies() == 0 && state->nrFailableBEs() == 0)) { + // Do not skip absorbing state or if reached by dependencies + iter->second.second->markExpand(); + } + iter->second.second->updateHeuristicValues(currentExplorationHeuristic->getDepth() + 1, stateProbabilityPair.second, choice.getTotalMass()); fixQueue = true; } } @@ -421,12 +429,13 @@ namespace storm { auto matrixEntry = matrix.getRow(it->first, 0).begin(); STORM_LOG_ASSERT(matrixEntry->getColumn() == failedStateId, "Transition has wrong target state."); // Get the lower bound by considering the failure of all possible BEs + DFTStatePointer state = it->second.first; ValueType newRate = storm::utility::zero<ValueType>(); - for (size_t index = 0; index < it->second->nrFailableBEs(); ++index) { - newRate += it->second->getFailableBERate(index); + for (size_t index = 0; index < state->nrFailableBEs(); ++index) { + newRate += state->getFailableBERate(index); } - for (size_t index = 0; index < it->second->nrNotFailableBEs(); ++index) { - newRate += it->second->getNotFailableBERate(index); + for (size_t index = 0; index < state->nrNotFailableBEs(); ++index) { + newRate += state->getNotFailableBERate(index); } matrixEntry->setValue(newRate); } @@ -441,12 +450,13 @@ namespace storm { // Get the upper bound by considering the failure of all BEs // The used formula for the rate is 1/( 1/a + 1/b + ...) // TODO Matthias: improve by using closed formula for AND of all BEs + DFTStatePointer state = it->second.first; ValueType newRate = storm::utility::zero<ValueType>(); - for (size_t index = 0; index < it->second->nrFailableBEs(); ++index) { - newRate += storm::utility::one<ValueType>() / it->second->getFailableBERate(index); + for (size_t index = 0; index < state->nrFailableBEs(); ++index) { + newRate += storm::utility::one<ValueType>() / state->getFailableBERate(index); } - for (size_t index = 0; index < it->second->nrNotFailableBEs(); ++index) { - newRate += storm::utility::one<ValueType>() / it->second->getNotFailableBERate(index); + for (size_t index = 0; index < state->nrNotFailableBEs(); ++index) { + newRate += storm::utility::one<ValueType>() / state->getNotFailableBERate(index); } newRate = storm::utility::one<ValueType>() / newRate; matrixEntry->setValue(newRate); @@ -473,14 +483,14 @@ namespace storm { // Check if state is pseudo state // If state is explored already the possible pseudo state was already constructed auto iter = statesNotExplored.find(stateId); - if (iter != statesNotExplored.end() && iter->second->isPseudoState()) { + if (iter != statesNotExplored.end() && iter->second.first->isPseudoState()) { // Create pseudo state now - STORM_LOG_ASSERT(iter->second->getId() == stateId, "Ids do not match."); - STORM_LOG_ASSERT(iter->second->status() == state->status(), "Pseudo states do not coincide."); + STORM_LOG_ASSERT(iter->second.first->getId() == stateId, "Ids do not match."); + STORM_LOG_ASSERT(iter->second.first->status() == state->status(), "Pseudo states do not coincide."); state->setId(stateId); // Update mapping to map to concrete state now - statesNotExplored[stateId] = state; - // TODO Matthias: copy explorationHeuristic + // TODO Matthias: just change pointer? + statesNotExplored[stateId] = std::make_pair(state, iter->second.second); // We do not push the new state on the exploration queue as the pseudo state was already pushed STORM_LOG_TRACE("Created pseudo state " << dft.getStateString(state)); } @@ -493,8 +503,9 @@ namespace storm { stateId = stateStorage.stateToId.findOrAdd(state->status(), state->getId()); STORM_LOG_ASSERT(stateId == state->getId(), "Ids do not match."); // Insert state as not yet explored - statesNotExplored[stateId] = state; - explorationQueue.push(stateId); + ExplorationHeuristicPointer heuristic = std::make_shared<ExplorationHeuristic>(stateId); + statesNotExplored[stateId] = std::make_pair(state, heuristic); + explorationQueue.push(heuristic); // Reserve one slot for the new state in the remapping matrixBuilder.stateRemapping.push_back(0); STORM_LOG_TRACE("New " << (state->isPseudoState() ? "pseudo" : "concrete") << " state: " << dft.getStateString(state)); @@ -511,12 +522,6 @@ namespace storm { modelComponents.markovianStates.set(matrixBuilder.getCurrentRowGroup() - 1, markovian); } - template<typename ValueType, typename StateType> - bool ExplicitDFTModelBuilderApprox<ValueType, StateType>::isPriorityGreater(StateType idA, StateType idB) const { - return statesNotExplored.at(idA)->comparePriority(statesNotExplored.at(idB), heuristic); - } - - // Explicitly instantiate the class. template class ExplicitDFTModelBuilderApprox<double>; diff --git a/src/builder/ExplicitDFTModelBuilderApprox.h b/src/builder/ExplicitDFTModelBuilderApprox.h index 74798e889..4238194eb 100644 --- a/src/builder/ExplicitDFTModelBuilderApprox.h +++ b/src/builder/ExplicitDFTModelBuilderApprox.h @@ -26,11 +26,10 @@ namespace storm { template<typename ValueType, typename StateType = uint32_t> class ExplicitDFTModelBuilderApprox { - using DFTElementPointer = std::shared_ptr<storm::storage::DFTElement<ValueType>>; - using DFTElementCPointer = std::shared_ptr<storm::storage::DFTElement<ValueType> const>; - using DFTGatePointer = std::shared_ptr<storm::storage::DFTGate<ValueType>>; using DFTStatePointer = std::shared_ptr<storm::storage::DFTState<ValueType>>; - using DFTRestrictionPointer = std::shared_ptr<storm::storage::DFTRestriction<ValueType>>; + // TODO Matthias: make choosable + using ExplorationHeuristic = DFTExplorationHeuristicNone<ValueType>; + using ExplorationHeuristicPointer = std::shared_ptr<ExplorationHeuristic>; // A structure holding the individual components of a model. @@ -294,16 +293,16 @@ namespace storm { storm::storage::sparse::StateStorage<StateType> stateStorage; // A priority queue of states that still need to be explored. - storm::storage::DynamicPriorityQueue<StateType, std::vector<StateType>, std::function<bool(StateType, StateType)>> explorationQueue; + storm::storage::DynamicPriorityQueue<ExplorationHeuristicPointer, std::vector<ExplorationHeuristicPointer>, std::function<bool(ExplorationHeuristicPointer, ExplorationHeuristicPointer)>> explorationQueue; - // A mapping of not yet explored states from the id to the state object. - std::map<StateType, DFTStatePointer> statesNotExplored; + // A mapping of not yet explored states from the id to the tuple (state object, heuristic values). + std::map<StateType, std::pair<DFTStatePointer, ExplorationHeuristicPointer>> statesNotExplored; // Holds all skipped states which were not yet expanded. More concretely it is a mapping from matrix indices // to the corresponding skipped states. // Notice that we need an ordered map here to easily iterate in increasing order over state ids. // TODO remove again - std::map<StateType, DFTStatePointer> skippedStates; + std::map<StateType, std::pair<DFTStatePointer, ExplorationHeuristicPointer>> skippedStates; }; } diff --git a/src/storage/dft/DFTState.cpp b/src/storage/dft/DFTState.cpp index f6bb692a7..70ff18122 100644 --- a/src/storage/dft/DFTState.cpp +++ b/src/storage/dft/DFTState.cpp @@ -6,7 +6,7 @@ namespace storm { namespace storage { template<typename ValueType> - DFTState<ValueType>::DFTState(DFT<ValueType> const& dft, DFTStateGenerationInfo const& stateGenerationInfo, size_t id) : mStatus(dft.stateVectorSize()), mId(id), mPseudoState(false), mDft(dft), mStateGenerationInfo(stateGenerationInfo), exploreHeuristic() { + DFTState<ValueType>::DFTState(DFT<ValueType> const& dft, DFTStateGenerationInfo const& stateGenerationInfo, size_t id) : mStatus(dft.stateVectorSize()), mId(id), mPseudoState(false), mDft(dft), mStateGenerationInfo(stateGenerationInfo) { // TODO Matthias: use construct() // Initialize uses @@ -37,7 +37,7 @@ namespace storm { } template<typename ValueType> - DFTState<ValueType>::DFTState(storm::storage::BitVector const& status, DFT<ValueType> const& dft, DFTStateGenerationInfo const& stateGenerationInfo, size_t id) : mStatus(status), mId(id), mPseudoState(true), mDft(dft), mStateGenerationInfo(stateGenerationInfo), exploreHeuristic() { + DFTState<ValueType>::DFTState(storm::storage::BitVector const& status, DFT<ValueType> const& dft, DFTStateGenerationInfo const& stateGenerationInfo, size_t id) : mStatus(status), mId(id), mPseudoState(true), mDft(dft), mStateGenerationInfo(stateGenerationInfo) { // Intentionally left empty } @@ -85,9 +85,7 @@ namespace storm { template<typename ValueType> std::shared_ptr<DFTState<ValueType>> DFTState<ValueType>::copy() const { - std::shared_ptr<DFTState<ValueType>> stateCopy = std::make_shared<storm::storage::DFTState<ValueType>>(*this); - stateCopy->exploreHeuristic = storm::builder::DFTExplorationHeuristic<ValueType>(); - return stateCopy; + return std::make_shared<storm::storage::DFTState<ValueType>>(*this); } template<typename ValueType> diff --git a/src/storage/dft/DFTState.h b/src/storage/dft/DFTState.h index eb3164516..207eecac3 100644 --- a/src/storage/dft/DFTState.h +++ b/src/storage/dft/DFTState.h @@ -34,8 +34,7 @@ namespace storm { bool mValid = true; const DFT<ValueType>& mDft; const DFTStateGenerationInfo& mStateGenerationInfo; - storm::builder::DFTExplorationHeuristic<ValueType> exploreHeuristic; - + public: /** * Construct the initial state. @@ -123,26 +122,6 @@ namespace storm { return mPseudoState; } - void setHeuristicValues(size_t depth, ValueType rate, ValueType exitRate) { - exploreHeuristic.setHeuristicValues(depth, rate, exitRate); - } - - void setHeuristicValues(std::shared_ptr<storm::storage::DFTState<ValueType>> oldState, ValueType rate, ValueType exitRate) { - if (hasFailed(mDft.getTopLevelIndex()) || isFailsafe(mDft.getTopLevelIndex()) || nrFailableDependencies() > 0 || (nrFailableDependencies() == 0 && nrFailableBEs() == 0)) { - // Do not skip absorbing state or if reached by dependencies - exploreHeuristic.setNotSkip(); - } - exploreHeuristic.setHeuristicValues(oldState->exploreHeuristic.getDepth() + 1, rate, exitRate); - } - - bool isSkip(double approximationThreshold, storm::builder::ApproximationHeuristic heuristic) { - return exploreHeuristic.isSkip(approximationThreshold, heuristic); - } - - bool comparePriority(std::shared_ptr<DFTState> const& other, storm::builder::ApproximationHeuristic heuristic) { - return this->exploreHeuristic.compare(other->exploreHeuristic, heuristic); - } - storm::storage::BitVector const& status() const { return mStatus; } From 82a3964e5dab0c6d9d0404a0efa08c576bb0a554 Mon Sep 17 00:00:00 2001 From: Mavo <matthias.volk@rwth-aachen.de> Date: Sat, 22 Oct 2016 18:30:26 +0200 Subject: [PATCH 33/65] Only fix queue when needed Former-commit-id: 50231c4554e3bf6cdeeb7b6de37778fa3faedb3a --- src/builder/DftExplorationHeuristic.cpp | 71 +++++-------------- src/builder/DftExplorationHeuristic.h | 66 +++++++++++++---- src/builder/ExplicitDFTModelBuilderApprox.cpp | 3 +- 3 files changed, 73 insertions(+), 67 deletions(-) diff --git a/src/builder/DftExplorationHeuristic.cpp b/src/builder/DftExplorationHeuristic.cpp index 4bc72ccc1..3b9c2e110 100644 --- a/src/builder/DftExplorationHeuristic.cpp +++ b/src/builder/DftExplorationHeuristic.cpp @@ -14,69 +14,34 @@ namespace storm { // Intentionally left empty } - template<typename ValueType> - void DFTExplorationHeuristic<ValueType>::updateHeuristicValues(size_t depth, ValueType rate, ValueType exitRate) { - this->depth = std::min(this->depth, depth); - this->rate = std::max(this->rate, rate); - this->exitRate = std::max(this->exitRate, exitRate); - } - - template<typename ValueType> - DFTExplorationHeuristicNone<ValueType>::DFTExplorationHeuristicNone(size_t id) : DFTExplorationHeuristic<ValueType>(id) { - // Intentionally left empty + template<> + bool DFTExplorationHeuristicRateRatio<double>::updateHeuristicValues(size_t depth, double rate, double exitRate) { + bool update = false; + if (this->rate < rate) { + this->rate = rate; + update = true; + } + if (this->exitRate < exitRate) { + this->exitRate = exitRate; + update = true; + } + return update; } - template<typename ValueType> - bool DFTExplorationHeuristicNone<ValueType>::isSkip(double approximationThreshold) const { + template<> + bool DFTExplorationHeuristicRateRatio<storm::RationalFunction>::updateHeuristicValues(size_t depth, storm::RationalFunction rate, storm::RationalFunction exitRate) { + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Heuristic rate ration does not work for rational functions."); return false; } - template<typename ValueType> - bool DFTExplorationHeuristicNone<ValueType>::operator<(DFTExplorationHeuristicNone<ValueType> const& other) const { - // Just use memory address for comparing - // TODO Matthias: better idea? - return this > &other; - } - - template<typename ValueType> - DFTExplorationHeuristicDepth<ValueType>::DFTExplorationHeuristicDepth(size_t id) : DFTExplorationHeuristic<ValueType>(id) { - // Intentionally left empty - } - - template<typename ValueType> - bool DFTExplorationHeuristicDepth<ValueType>::isSkip(double approximationThreshold) const { - return !this->expand && this->depth > approximationThreshold; - } - - template<typename ValueType> - bool DFTExplorationHeuristicDepth<ValueType>::operator<(DFTExplorationHeuristicDepth<ValueType> const& other) const { - return this->depth > other.depth; - } - - template<typename ValueType> - DFTExplorationHeuristicRateRatio<ValueType>::DFTExplorationHeuristicRateRatio(size_t id) : DFTExplorationHeuristic<ValueType>(id) { - // Intentionally left empty - } - - template<typename ValueType> - bool DFTExplorationHeuristicRateRatio<ValueType>::isSkip(double approximationThreshold) const { - return !this->expand && this->getRateRatio() < approximationThreshold; - } - - template<typename ValueType> - bool DFTExplorationHeuristicRateRatio<ValueType>::operator<(DFTExplorationHeuristicRateRatio<ValueType> const& other) const { - return this->getRateRatio() < other.getRateRatio(); - } - - template<> - double DFTExplorationHeuristicRateRatio<double>::getRateRatio() const { + double DFTExplorationHeuristicRateRatio<double>::getPriority() const { return rate/exitRate; } template<> - double DFTExplorationHeuristicRateRatio<storm::RationalFunction>::getRateRatio() const { - STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Heuristic rate ration does not work."); + double DFTExplorationHeuristicRateRatio<storm::RationalFunction>::getPriority() const { + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Heuristic rate ration does not work for rational functions."); } diff --git a/src/builder/DftExplorationHeuristic.h b/src/builder/DftExplorationHeuristic.h index 8f12d2ec1..2b4fc5047 100644 --- a/src/builder/DftExplorationHeuristic.h +++ b/src/builder/DftExplorationHeuristic.h @@ -22,7 +22,9 @@ namespace storm { public: DFTExplorationHeuristic(size_t id); - void updateHeuristicValues(size_t depth, ValueType rate, ValueType exitRate); + virtual bool updateHeuristicValues(size_t depth, ValueType rate, ValueType exitRate) = 0; + + virtual double getPriority() const = 0; virtual bool isSkip(double approximationThreshold) const = 0; @@ -50,34 +52,74 @@ namespace storm { template<typename ValueType> class DFTExplorationHeuristicNone : public DFTExplorationHeuristic<ValueType> { public: - DFTExplorationHeuristicNone(size_t id); + DFTExplorationHeuristicNone(size_t id) : DFTExplorationHeuristic<ValueType>(id) { + // Intentionally left empty + } + + bool updateHeuristicValues(size_t depth, ValueType rate, ValueType exitRate) override { + return false; + } + + double getPriority() const override { + return this->id; + } - bool isSkip(double approximationThreshold) const override; + bool isSkip(double approximationThreshold) const override { + return false; + } - bool operator<(DFTExplorationHeuristicNone<ValueType> const& other) const; + bool operator<(DFTExplorationHeuristicNone<ValueType> const& other) const { + return this->id > other.id; + } }; template<typename ValueType> class DFTExplorationHeuristicDepth : public DFTExplorationHeuristic<ValueType> { public: - DFTExplorationHeuristicDepth(size_t id); + DFTExplorationHeuristicDepth(size_t id) : DFTExplorationHeuristic<ValueType>(id) { + // Intentionally left empty + } + + bool updateHeuristicValues(size_t depth, ValueType rate, ValueType exitRate) override { + if (depth < this->depth) { + this->depth = depth; + return true; + } + return false; + } + - bool isSkip(double approximationThreshold) const override; + double getPriority() const override { + return this->depth; + } + + bool isSkip(double approximationThreshold) const override { + return !this->expand && this->depth > approximationThreshold; + } - bool operator<(DFTExplorationHeuristicDepth<ValueType> const& other) const; + bool operator<(DFTExplorationHeuristicDepth<ValueType> const& other) const { + return this->depth > other.depth; + } }; template<typename ValueType> class DFTExplorationHeuristicRateRatio : public DFTExplorationHeuristic<ValueType> { public: - DFTExplorationHeuristicRateRatio(size_t id); + DFTExplorationHeuristicRateRatio(size_t id) : DFTExplorationHeuristic<ValueType>(id) { + // Intentionally left empty + } - bool isSkip(double approximationThreshold) const override; + bool updateHeuristicValues(size_t depth, ValueType rate, ValueType exitRate) override; - bool operator<(DFTExplorationHeuristicRateRatio<ValueType> const& other) const; + double getPriority() const override; - private: - double getRateRatio() const; + bool isSkip(double approximationThreshold) const override { + return !this->expand && this->getPriority() < approximationThreshold; + } + + bool operator<(DFTExplorationHeuristicRateRatio<ValueType> const& other) const { + return this->getPriority() < other.getPriority(); + } }; } diff --git a/src/builder/ExplicitDFTModelBuilderApprox.cpp b/src/builder/ExplicitDFTModelBuilderApprox.cpp index 5595a5f49..ed7c1500d 100644 --- a/src/builder/ExplicitDFTModelBuilderApprox.cpp +++ b/src/builder/ExplicitDFTModelBuilderApprox.cpp @@ -290,8 +290,7 @@ namespace storm { // Do not skip absorbing state or if reached by dependencies iter->second.second->markExpand(); } - iter->second.second->updateHeuristicValues(currentExplorationHeuristic->getDepth() + 1, stateProbabilityPair.second, choice.getTotalMass()); - fixQueue = true; + fixQueue = iter->second.second->updateHeuristicValues(currentExplorationHeuristic->getDepth() + 1, stateProbabilityPair.second, choice.getTotalMass()); } } matrixBuilder.finishRow(); From d1d77ff4df3b5d79a3ba956c61f17763012226d3 Mon Sep 17 00:00:00 2001 From: Mavo <matthias.volk@rwth-aachen.de> Date: Mon, 24 Oct 2016 15:20:03 +0200 Subject: [PATCH 34/65] Changed deque to vector in bisimulation to gain performance boost Former-commit-id: 5db8e917b47000312198ad2e94773bec86912ebd --- .../BisimulationDecomposition.cpp | 16 +++++------ .../bisimulation/BisimulationDecomposition.h | 8 +++--- ...ministicModelBisimulationDecomposition.cpp | 28 +++++++++---------- ...erministicModelBisimulationDecomposition.h | 10 +++---- ...ministicModelBisimulationDecomposition.cpp | 14 +++++----- ...erministicModelBisimulationDecomposition.h | 8 +++--- 6 files changed, 42 insertions(+), 42 deletions(-) diff --git a/src/storage/bisimulation/BisimulationDecomposition.cpp b/src/storage/bisimulation/BisimulationDecomposition.cpp index 4b31551f9..33d4cffcc 100644 --- a/src/storage/bisimulation/BisimulationDecomposition.cpp +++ b/src/storage/bisimulation/BisimulationDecomposition.cpp @@ -230,25 +230,25 @@ namespace storm { template<typename ModelType, typename BlockDataType> void BisimulationDecomposition<ModelType, BlockDataType>::performPartitionRefinement() { - // Insert all blocks into the splitter queue as a (potential) splitter. - std::deque<Block<BlockDataType>*> splitterQueue; - std::for_each(partition.getBlocks().begin(), partition.getBlocks().end(), [&] (std::unique_ptr<Block<BlockDataType>> const& block) { block->data().setSplitter(); splitterQueue.push_back(block.get()); } ); + // Insert all blocks into the splitter vector as a (potential) splitter. + std::vector<Block<BlockDataType>*> splitterVector; + std::for_each(partition.getBlocks().begin(), partition.getBlocks().end(), [&] (std::unique_ptr<Block<BlockDataType>> const& block) { block->data().setSplitter(); splitterVector.push_back(block.get()); } ); // Then perform the actual splitting until there are no more splitters. uint_fast64_t iterations = 0; - while (!splitterQueue.empty()) { + while (!splitterVector.empty()) { ++iterations; // Get and prepare the next splitter. // Sort the splitters according to their sizes to prefer small splitters. That is just a heuristic, but // tends to work well. - std::sort(splitterQueue.begin(), splitterQueue.end(), [] (Block<BlockDataType> const* b1, Block<BlockDataType> const* b2) { return b1->getNumberOfStates() < b2->getNumberOfStates(); } ); - Block<BlockDataType>* splitter = splitterQueue.front(); - splitterQueue.pop_front(); + std::sort(splitterVector.begin(), splitterVector.end(), [] (Block<BlockDataType> const* b1, Block<BlockDataType> const* b2) { return b1->getNumberOfStates() > b2->getNumberOfStates(); } ); + Block<BlockDataType>* splitter = splitterVector.back(); + splitterVector.pop_back(); splitter->data().setSplitter(false); // Now refine the partition using the current splitter. - refinePartitionBasedOnSplitter(*splitter, splitterQueue); + refinePartitionBasedOnSplitter(*splitter, splitterVector); } } diff --git a/src/storage/bisimulation/BisimulationDecomposition.h b/src/storage/bisimulation/BisimulationDecomposition.h index 6d86f2df9..df951bce3 100644 --- a/src/storage/bisimulation/BisimulationDecomposition.h +++ b/src/storage/bisimulation/BisimulationDecomposition.h @@ -1,7 +1,7 @@ #ifndef STORM_STORAGE_BISIMULATIONDECOMPOSITION_H_ #define STORM_STORAGE_BISIMULATIONDECOMPOSITION_H_ -#include <deque> +#include <vector> #include "src/settings/SettingsManager.h" #include "src/settings/modules/BisimulationSettings.h" @@ -219,12 +219,12 @@ namespace storm { /*! * Refines the partition by considering the given splitter. All blocks that become potential splitters - * because of this refinement, are marked as splitters and inserted into the splitter queue. + * because of this refinement, are marked as splitters and inserted into the splitter vector. * * @param splitter The splitter to use. - * @param splitterQueue The queue into which to insert the newly discovered potential splitters. + * @param splitterVector The vector into which to insert the newly discovered potential splitters. */ - virtual void refinePartitionBasedOnSplitter(bisimulation::Block<BlockDataType>& splitter, std::deque<bisimulation::Block<BlockDataType>*>& splitterQueue) = 0; + virtual void refinePartitionBasedOnSplitter(bisimulation::Block<BlockDataType>& splitter, std::vector<bisimulation::Block<BlockDataType>*>& splitterVector) = 0; /*! * Builds the quotient model based on the previously computed equivalence classes (stored in the blocks diff --git a/src/storage/bisimulation/DeterministicModelBisimulationDecomposition.cpp b/src/storage/bisimulation/DeterministicModelBisimulationDecomposition.cpp index e968c98cd..dcc12fbfb 100644 --- a/src/storage/bisimulation/DeterministicModelBisimulationDecomposition.cpp +++ b/src/storage/bisimulation/DeterministicModelBisimulationDecomposition.cpp @@ -157,7 +157,7 @@ namespace storm { } template<typename ModelType> - void DeterministicModelBisimulationDecomposition<ModelType>::refinePredecessorBlocksOfSplitterStrong(std::list<Block<BlockDataType>*> const& predecessorBlocks, std::deque<bisimulation::Block<BlockDataType>*>& splitterQueue) { + void DeterministicModelBisimulationDecomposition<ModelType>::refinePredecessorBlocksOfSplitterStrong(std::list<Block<BlockDataType>*> const& predecessorBlocks, std::vector<bisimulation::Block<BlockDataType>*>& splitterVector) { for (auto block : predecessorBlocks) { STORM_LOG_TRACE("Refining predecessor " << block->getId() << " of splitter"); @@ -176,15 +176,15 @@ namespace storm { [this] (storm::storage::sparse::state_type state1, storm::storage::sparse::state_type state2) { return this->comparator.isLess(getProbabilityToSplitter(state1), getProbabilityToSplitter(state2)); }, - [&splitterQueue] (Block<BlockDataType>& block) { - splitterQueue.emplace_back(&block); block.data().setSplitter(); + [&splitterVector] (Block<BlockDataType>& block) { + splitterVector.emplace_back(&block); block.data().setSplitter(); }); - // If the predecessor block was split, we need to insert it into the splitter queue if it is not already + // If the predecessor block was split, we need to insert it into the splitter vector if it is not already // marked as a splitter. if (split && !blockToRefineProbabilistically->data().splitter()) { - splitterQueue.emplace_back(blockToRefineProbabilistically); + splitterVector.emplace_back(blockToRefineProbabilistically); blockToRefineProbabilistically->data().setSplitter(); } @@ -378,7 +378,7 @@ namespace storm { } template<typename ModelType> - void DeterministicModelBisimulationDecomposition<ModelType>::refinePredecessorBlockOfSplitterWeak(bisimulation::Block<BlockDataType>& block, std::deque<bisimulation::Block<BlockDataType>*>& splitterQueue) { + void DeterministicModelBisimulationDecomposition<ModelType>::refinePredecessorBlockOfSplitterWeak(bisimulation::Block<BlockDataType>& block, std::vector<bisimulation::Block<BlockDataType>*>& splitterVector) { // First, we need to turn the one-step probabilities to go to the splitter to the conditional probabilities // for all non-silent states. computeConditionalProbabilitiesForNonSilentStates(block); @@ -395,12 +395,12 @@ namespace storm { [&weakStateLabels,&block,originalBlockIndex,this] (storm::storage::sparse::state_type state1, storm::storage::sparse::state_type state2) { return weakStateLabels[this->partition.getPosition(state1) - originalBlockIndex] < weakStateLabels[this->partition.getPosition(state2) - originalBlockIndex]; }, - [this, &splitterQueue] (bisimulation::Block<BlockDataType>& block) { + [this, &splitterVector] (bisimulation::Block<BlockDataType>& block) { updateSilentProbabilitiesBasedOnTransitions(block); // Insert the new block as a splitter. block.data().setSplitter(); - splitterQueue.emplace_back(&block); + splitterVector.emplace_back(&block); }); // If the block was split, we also update the silent probabilities. @@ -410,16 +410,16 @@ namespace storm { if (!block.data().splitter()) { // Insert the new block as a splitter. block.data().setSplitter(); - splitterQueue.emplace_back(&block); + splitterVector.emplace_back(&block); } } } template<typename ModelType> - void DeterministicModelBisimulationDecomposition<ModelType>::refinePredecessorBlocksOfSplitterWeak(bisimulation::Block<BlockDataType>& splitter, std::list<bisimulation::Block<BlockDataType>*> const& predecessorBlocks, std::deque<bisimulation::Block<BlockDataType>*>& splitterQueue) { + void DeterministicModelBisimulationDecomposition<ModelType>::refinePredecessorBlocksOfSplitterWeak(bisimulation::Block<BlockDataType>& splitter, std::list<bisimulation::Block<BlockDataType>*> const& predecessorBlocks, std::vector<bisimulation::Block<BlockDataType>*>& splitterVector) { for (auto block : predecessorBlocks) { if (*block != splitter) { - refinePredecessorBlockOfSplitterWeak(*block, splitterQueue); + refinePredecessorBlockOfSplitterWeak(*block, splitterVector); } else { // If the block to split is the splitter itself, we must not do any splitting here. } @@ -439,7 +439,7 @@ namespace storm { } template<typename ModelType> - void DeterministicModelBisimulationDecomposition<ModelType>::refinePartitionBasedOnSplitter(bisimulation::Block<BlockDataType>& splitter, std::deque<bisimulation::Block<BlockDataType>*>& splitterQueue) { + void DeterministicModelBisimulationDecomposition<ModelType>::refinePartitionBasedOnSplitter(bisimulation::Block<BlockDataType>& splitter, std::vector<bisimulation::Block<BlockDataType>*>& splitterVector) { STORM_LOG_TRACE("Refining partition based on splitter " << splitter.getId()); // The outline of the refinement is as follows. @@ -513,7 +513,7 @@ namespace storm { if (this->options.getType() == BisimulationType::Strong || this->model.getType() == storm::models::ModelType::Ctmc) { // In the case of CTMCs and weak bisimulation, we still call the "splitStrong" method, but we already have // taken care of not adding the splitter to the predecessor blocks, so this is safe. - refinePredecessorBlocksOfSplitterStrong(predecessorBlocks, splitterQueue); + refinePredecessorBlocksOfSplitterStrong(predecessorBlocks, splitterVector); } else { // If the splitter is a predecessor of we can use the computed probabilities to update the silent // probabilities. @@ -521,7 +521,7 @@ namespace storm { updateSilentProbabilitiesBasedOnProbabilitiesToSplitter(splitter); } - refinePredecessorBlocksOfSplitterWeak(splitter, predecessorBlocks, splitterQueue); + refinePredecessorBlocksOfSplitterWeak(splitter, predecessorBlocks, splitterVector); } } diff --git a/src/storage/bisimulation/DeterministicModelBisimulationDecomposition.h b/src/storage/bisimulation/DeterministicModelBisimulationDecomposition.h index b9f2a2a5a..b460dfa5d 100644 --- a/src/storage/bisimulation/DeterministicModelBisimulationDecomposition.h +++ b/src/storage/bisimulation/DeterministicModelBisimulationDecomposition.h @@ -39,11 +39,11 @@ namespace storm { virtual void buildQuotient() override; - virtual void refinePartitionBasedOnSplitter(bisimulation::Block<BlockDataType>& splitter, std::deque<bisimulation::Block<BlockDataType>*>& splitterQueue) override; + virtual void refinePartitionBasedOnSplitter(bisimulation::Block<BlockDataType>& splitter, std::vector<bisimulation::Block<BlockDataType>*>& splitterVector) override; private: // Refines the predecessor blocks wrt. strong bisimulation. - void refinePredecessorBlocksOfSplitterStrong(std::list<bisimulation::Block<BlockDataType>*> const& predecessorBlocks, std::deque<bisimulation::Block<BlockDataType>*>& splitterQueue); + void refinePredecessorBlocksOfSplitterStrong(std::list<bisimulation::Block<BlockDataType>*> const& predecessorBlocks, std::vector<bisimulation::Block<BlockDataType>*>& splitterVector); /*! * Performs the necessary steps to compute a weak bisimulation on a DTMC. @@ -99,10 +99,10 @@ namespace storm { void updateSilentProbabilitiesBasedOnTransitions(bisimulation::Block<BlockDataType>& block); // Refines the predecessor blocks of the splitter wrt. weak bisimulation in DTMCs. - void refinePredecessorBlocksOfSplitterWeak(bisimulation::Block<BlockDataType>& splitter, std::list<bisimulation::Block<BlockDataType>*> const& predecessorBlocks, std::deque<bisimulation::Block<BlockDataType>*>& splitterQueue); + void refinePredecessorBlocksOfSplitterWeak(bisimulation::Block<BlockDataType>& splitter, std::list<bisimulation::Block<BlockDataType>*> const& predecessorBlocks, std::vector<bisimulation::Block<BlockDataType>*>& splitterVector); // Refines the given block wrt to weak bisimulation in DTMCs. - void refinePredecessorBlockOfSplitterWeak(bisimulation::Block<BlockDataType>& block, std::deque<bisimulation::Block<BlockDataType>*>& splitterQueue); + void refinePredecessorBlockOfSplitterWeak(bisimulation::Block<BlockDataType>& block, std::vector<bisimulation::Block<BlockDataType>*>& splitterVector); // Converts the one-step probabilities of going into the splitter into the conditional probabilities needed // for weak bisimulation (on DTMCs). @@ -127,4 +127,4 @@ namespace storm { } } -#endif /* STORM_STORAGE_BISIMULATION_DETERMINISTICMODELBISIMULATIONDECOMPOSITION_H_ */ \ No newline at end of file +#endif /* STORM_STORAGE_BISIMULATION_DETERMINISTICMODELBISIMULATIONDECOMPOSITION_H_ */ diff --git a/src/storage/bisimulation/NondeterministicModelBisimulationDecomposition.cpp b/src/storage/bisimulation/NondeterministicModelBisimulationDecomposition.cpp index 2e7d54113..fe53c6602 100644 --- a/src/storage/bisimulation/NondeterministicModelBisimulationDecomposition.cpp +++ b/src/storage/bisimulation/NondeterministicModelBisimulationDecomposition.cpp @@ -198,7 +198,7 @@ namespace storm { } template<typename ModelType> - void NondeterministicModelBisimulationDecomposition<ModelType>::updateQuotientDistributionsOfPredecessors(Block<BlockDataType> const& newBlock, Block<BlockDataType> const& oldBlock, std::deque<Block<BlockDataType>*>& splitterQueue) { + void NondeterministicModelBisimulationDecomposition<ModelType>::updateQuotientDistributionsOfPredecessors(Block<BlockDataType> const& newBlock, Block<BlockDataType> const& oldBlock, std::vector<Block<BlockDataType>*>& splitterVector) { uint_fast64_t lastState = 0; bool lastStateInitialized = false; @@ -220,7 +220,7 @@ namespace storm { // If the predecessor block is not marked as to-refined, we do so now. if (!predecessorBlock.data().splitter()) { predecessorBlock.data().setSplitter(); - splitterQueue.push_back(&predecessorBlock); + splitterVector.push_back(&predecessorBlock); } if (lastStateInitialized) { @@ -332,7 +332,7 @@ namespace storm { } template<typename ModelType> - bool NondeterministicModelBisimulationDecomposition<ModelType>::splitBlockAccordingToCurrentQuotientDistributions(Block<BlockDataType>& block, std::deque<Block<BlockDataType>*>& splitterQueue) { + bool NondeterministicModelBisimulationDecomposition<ModelType>::splitBlockAccordingToCurrentQuotientDistributions(Block<BlockDataType>& block, std::vector<Block<BlockDataType>*>& splitterVector) { std::list<Block<BlockDataType>*> newBlocks; bool split = this->partition.splitBlock(block, [this] (storm::storage::sparse::state_type state1, storm::storage::sparse::state_type state2) { @@ -340,7 +340,7 @@ namespace storm { // std::cout << state1 << " is " << (!result ? "not" : "") << " smaller than " << state2 << std::endl; return result; }, - [this, &block, &splitterQueue, &newBlocks] (Block<BlockDataType>& newBlock) { + [this, &block, &splitterVector, &newBlocks] (Block<BlockDataType>& newBlock) { newBlocks.push_back(&newBlock); // this->checkBlockStable(newBlock); @@ -357,7 +357,7 @@ namespace storm { // defer updating the quotient distributions until *after* all splits, because // it otherwise influences the subsequent splits! for (auto el : newBlocks) { - this->updateQuotientDistributionsOfPredecessors(*el, block, splitterQueue); + this->updateQuotientDistributionsOfPredecessors(*el, block, splitterVector); } // this->checkQuotientDistributions(); @@ -405,14 +405,14 @@ namespace storm { } template<typename ModelType> - void NondeterministicModelBisimulationDecomposition<ModelType>::refinePartitionBasedOnSplitter(bisimulation::Block<BlockDataType>& splitter, std::deque<bisimulation::Block<BlockDataType>*>& splitterQueue) { + void NondeterministicModelBisimulationDecomposition<ModelType>::refinePartitionBasedOnSplitter(bisimulation::Block<BlockDataType>& splitter, std::vector<bisimulation::Block<BlockDataType>*>& splitterVector) { if (!possiblyNeedsRefinement(splitter)) { return; } STORM_LOG_TRACE("Refining block " << splitter.getId()); - splitBlockAccordingToCurrentQuotientDistributions(splitter, splitterQueue); + splitBlockAccordingToCurrentQuotientDistributions(splitter, splitterVector); } template class NondeterministicModelBisimulationDecomposition<storm::models::sparse::Mdp<double>>; diff --git a/src/storage/bisimulation/NondeterministicModelBisimulationDecomposition.h b/src/storage/bisimulation/NondeterministicModelBisimulationDecomposition.h index 18a6e8e58..38feaf36a 100644 --- a/src/storage/bisimulation/NondeterministicModelBisimulationDecomposition.h +++ b/src/storage/bisimulation/NondeterministicModelBisimulationDecomposition.h @@ -37,7 +37,7 @@ namespace storm { virtual void buildQuotient() override; - virtual void refinePartitionBasedOnSplitter(bisimulation::Block<BlockDataType>& splitter, std::deque<bisimulation::Block<BlockDataType>*>& splitterQueue) override; + virtual void refinePartitionBasedOnSplitter(bisimulation::Block<BlockDataType>& splitter, std::vector<bisimulation::Block<BlockDataType>*>& splitterVector) override; virtual void initialize() override; @@ -52,7 +52,7 @@ namespace storm { bool possiblyNeedsRefinement(bisimulation::Block<BlockDataType> const& block) const; // Splits the given block according to the current quotient distributions. - bool splitBlockAccordingToCurrentQuotientDistributions(bisimulation::Block<BlockDataType>& block, std::deque<bisimulation::Block<BlockDataType>*>& splitterQueue); + bool splitBlockAccordingToCurrentQuotientDistributions(bisimulation::Block<BlockDataType>& block, std::vector<bisimulation::Block<BlockDataType>*>& splitterVector); // Retrieves whether the quotient distributions of state 1 are considered to be less than the ones of state 2. bool quotientDistributionsLess(storm::storage::sparse::state_type state1, storm::storage::sparse::state_type state2) const; @@ -62,7 +62,7 @@ namespace storm { // Updates the quotient distributions of the predecessors of the new block by taking the probability mass // away from the old block. - void updateQuotientDistributionsOfPredecessors(bisimulation::Block<BlockDataType> const& newBlock, bisimulation::Block<BlockDataType> const& oldBlock, std::deque<bisimulation::Block<BlockDataType>*>& splitterQueue); + void updateQuotientDistributionsOfPredecessors(bisimulation::Block<BlockDataType> const& newBlock, bisimulation::Block<BlockDataType> const& oldBlock, std::vector<bisimulation::Block<BlockDataType>*>& splitterVector); bool checkQuotientDistributions() const; bool checkBlockStable(bisimulation::Block<BlockDataType> const& newBlock) const; @@ -81,4 +81,4 @@ namespace storm { } } -#endif /* STORM_STORAGE_BISIMULATION_NONDETERMINISTICMODELBISIMULATIONDECOMPOSITION_H_ */ \ No newline at end of file +#endif /* STORM_STORAGE_BISIMULATION_NONDETERMINISTICMODELBISIMULATIONDECOMPOSITION_H_ */ From 8d38358c115dda77d55c001c2f8178c2f52aebbb Mon Sep 17 00:00:00 2001 From: Mavo <matthias.volk@rwth-aachen.de> Date: Mon, 24 Oct 2016 17:28:56 +0200 Subject: [PATCH 35/65] Use BucketPriorityQueue instead of DynamicPriorityQueue Former-commit-id: 7a22ef5b1671b4a33319e4cc6c98bff2e473aa77 --- src/builder/DftExplorationHeuristic.cpp | 22 +- src/builder/DftExplorationHeuristic.h | 12 +- src/builder/ExplicitDFTModelBuilderApprox.cpp | 55 +++-- src/builder/ExplicitDFTModelBuilderApprox.h | 9 +- src/storage/BucketPriorityQueue.cpp | 191 ++++++++++++++++++ src/storage/BucketPriorityQueue.h | 70 +++++++ 6 files changed, 320 insertions(+), 39 deletions(-) create mode 100644 src/storage/BucketPriorityQueue.cpp create mode 100644 src/storage/BucketPriorityQueue.h diff --git a/src/builder/DftExplorationHeuristic.cpp b/src/builder/DftExplorationHeuristic.cpp index 3b9c2e110..dbd64088b 100644 --- a/src/builder/DftExplorationHeuristic.cpp +++ b/src/builder/DftExplorationHeuristic.cpp @@ -4,28 +4,20 @@ #include "src/utility/constants.h" #include "src/exceptions/NotImplementedException.h" -#include <limits> - namespace storm { namespace builder { template<typename ValueType> - DFTExplorationHeuristic<ValueType>::DFTExplorationHeuristic(size_t id) : id(id), expand(false), depth(std::numeric_limits<std::size_t>::max()), rate(storm::utility::zero<ValueType>()), exitRate(storm::utility::zero<ValueType>()) { - // Intentionally left empty + DFTExplorationHeuristic<ValueType>::DFTExplorationHeuristic(size_t id, size_t depth, ValueType rate, ValueType exitRate) : id(id), expand(false), depth(depth) { + STORM_LOG_ASSERT(storm::utility::zero<ValueType>() < exitRate, "Exit rate is 0"); + rateRatio = rate/exitRate; } template<> bool DFTExplorationHeuristicRateRatio<double>::updateHeuristicValues(size_t depth, double rate, double exitRate) { - bool update = false; - if (this->rate < rate) { - this->rate = rate; - update = true; - } - if (this->exitRate < exitRate) { - this->exitRate = exitRate; - update = true; - } - return update; + STORM_LOG_ASSERT(exitRate > 0, "Exit rate is 0"); + rateRatio += rate/exitRate; + return true; } template<> @@ -36,7 +28,7 @@ namespace storm { template<> double DFTExplorationHeuristicRateRatio<double>::getPriority() const { - return rate/exitRate; + return rateRatio; } template<> diff --git a/src/builder/DftExplorationHeuristic.h b/src/builder/DftExplorationHeuristic.h index 2b4fc5047..d23c673fc 100644 --- a/src/builder/DftExplorationHeuristic.h +++ b/src/builder/DftExplorationHeuristic.h @@ -20,7 +20,7 @@ namespace storm { class DFTExplorationHeuristic { public: - DFTExplorationHeuristic(size_t id); + DFTExplorationHeuristic(size_t id, size_t depth, ValueType rate, ValueType exitRate); virtual bool updateHeuristicValues(size_t depth, ValueType rate, ValueType exitRate) = 0; @@ -44,15 +44,13 @@ namespace storm { size_t id; bool expand; size_t depth; - ValueType rate; - ValueType exitRate; - + ValueType rateRatio; }; template<typename ValueType> class DFTExplorationHeuristicNone : public DFTExplorationHeuristic<ValueType> { public: - DFTExplorationHeuristicNone(size_t id) : DFTExplorationHeuristic<ValueType>(id) { + DFTExplorationHeuristicNone(size_t id, size_t depth, ValueType rate, ValueType exitRate) : DFTExplorationHeuristic<ValueType>(id, depth, rate, exitRate) { // Intentionally left empty } @@ -76,7 +74,7 @@ namespace storm { template<typename ValueType> class DFTExplorationHeuristicDepth : public DFTExplorationHeuristic<ValueType> { public: - DFTExplorationHeuristicDepth(size_t id) : DFTExplorationHeuristic<ValueType>(id) { + DFTExplorationHeuristicDepth(size_t id, size_t depth, ValueType rate, ValueType exitRate) : DFTExplorationHeuristic<ValueType>(id, depth, rate, exitRate) { // Intentionally left empty } @@ -105,7 +103,7 @@ namespace storm { template<typename ValueType> class DFTExplorationHeuristicRateRatio : public DFTExplorationHeuristic<ValueType> { public: - DFTExplorationHeuristicRateRatio(size_t id) : DFTExplorationHeuristic<ValueType>(id) { + DFTExplorationHeuristicRateRatio(size_t id, size_t depth, ValueType rate, ValueType exitRate) : DFTExplorationHeuristic<ValueType>(id, depth, rate, exitRate) { // Intentionally left empty } diff --git a/src/builder/ExplicitDFTModelBuilderApprox.cpp b/src/builder/ExplicitDFTModelBuilderApprox.cpp index ed7c1500d..07863bb01 100644 --- a/src/builder/ExplicitDFTModelBuilderApprox.cpp +++ b/src/builder/ExplicitDFTModelBuilderApprox.cpp @@ -31,11 +31,13 @@ namespace storm { generator(dft, *stateGenerationInfo, enableDC, mergeFailedStates), matrixBuilder(!generator.isDeterministicModel()), stateStorage(((dft.stateVectorSize() / 64) + 1) * 64), - explorationQueue([this](ExplorationHeuristicPointer a, ExplorationHeuristicPointer b) { - return *a < *b; - }) + // TODO Matthias: make choosable + //explorationQueue(dft.nrElements(), 0, 1) + explorationQueue(41, 0, 1.0/20) { // Intentionally left empty. + // TODO Matthias: remove again + heuristic = storm::builder::ApproximationHeuristic::RATERATIO; } template<typename ValueType, typename StateType> @@ -76,7 +78,11 @@ namespace storm { initialStateIndex = stateStorage.initialStateIndices[0]; STORM_LOG_TRACE("Initial state: " << initialStateIndex); // Initialize heuristic values for inital state - statesNotExplored.at(initialStateIndex).second->updateHeuristicValues(0, storm::utility::zero<ValueType>(), storm::utility::zero<ValueType>()); + STORM_LOG_ASSERT(!statesNotExplored.at(initialStateIndex).second, "Heuristic for initial state is already initialized"); + ExplorationHeuristicPointer heuristic = std::make_shared<ExplorationHeuristic>(initialStateIndex, 0, storm::utility::zero<ValueType>(), storm::utility::one<ValueType>()); + heuristic->markExpand(); + statesNotExplored[initialStateIndex].second = heuristic; + explorationQueue.push(heuristic); } else { initializeNextIteration(); } @@ -84,6 +90,7 @@ namespace storm { switch (heuristic) { case storm::builder::ApproximationHeuristic::NONE: // Do not change anything + approximationThreshold = 0; break; case storm::builder::ApproximationHeuristic::DEPTH: approximationThreshold = iteration; @@ -225,8 +232,12 @@ namespace storm { void ExplicitDFTModelBuilderApprox<ValueType, StateType>::exploreStateSpace(double approximationThreshold) { size_t nrExpandedStates = 0; size_t nrSkippedStates = 0; + size_t fix = 0; // TODO Matthias: do not empty queue every time but break before while (!explorationQueue.empty()) { + explorationQueue.fix(); + //explorationQueue.print(std::cout); + //printNotExplored(); // Get the first state in the queue ExplorationHeuristicPointer currentExplorationHeuristic = explorationQueue.popTop(); StateType currentId = currentExplorationHeuristic->getId(); @@ -253,7 +264,6 @@ namespace storm { matrixBuilder.newRowGroup(); // Try to explore the next state - bool fixQueue = false; generator.load(currentState); if (currentExplorationHeuristic->isSkip(approximationThreshold)) { @@ -286,22 +296,31 @@ namespace storm { if (iter != statesNotExplored.end()) { // Update heuristic values DFTStatePointer state = iter->second.first; + if (!iter->second.second) { + // Initialize heuristic values + ExplorationHeuristicPointer heuristic = std::make_shared<ExplorationHeuristic>(stateProbabilityPair.first, currentExplorationHeuristic->getDepth() + 1, stateProbabilityPair.second, choice.getTotalMass()); + iter->second.second = heuristic; + explorationQueue.push(heuristic); + } else { + double oldPriority = iter->second.second->getPriority(); + if (iter->second.second->updateHeuristicValues(currentExplorationHeuristic->getDepth() + 1, stateProbabilityPair.second, choice.getTotalMass())) { + // Update priority queue + ++fix; + explorationQueue.update(iter->second.second, oldPriority); + } + } if (state->hasFailed(dft.getTopLevelIndex()) || state->isFailsafe(dft.getTopLevelIndex()) || state->nrFailableDependencies() > 0 || (state->nrFailableDependencies() == 0 && state->nrFailableBEs() == 0)) { // Do not skip absorbing state or if reached by dependencies iter->second.second->markExpand(); + // TODO Matthias give highest priority to ensure expanding before all skipped } - fixQueue = iter->second.second->updateHeuristicValues(currentExplorationHeuristic->getDepth() + 1, stateProbabilityPair.second, choice.getTotalMass()); } } matrixBuilder.finishRow(); } } - - // Update priority queue - if (fixQueue) { - explorationQueue.fix(); - } } // end exploration + std::cout << "Fixed queue " << fix << " times" << std::endl; STORM_LOG_INFO("Expanded " << nrExpandedStates << " states"); STORM_LOG_INFO("Skipped " << nrSkippedStates << " states"); @@ -502,9 +521,8 @@ namespace storm { stateId = stateStorage.stateToId.findOrAdd(state->status(), state->getId()); STORM_LOG_ASSERT(stateId == state->getId(), "Ids do not match."); // Insert state as not yet explored - ExplorationHeuristicPointer heuristic = std::make_shared<ExplorationHeuristic>(stateId); - statesNotExplored[stateId] = std::make_pair(state, heuristic); - explorationQueue.push(heuristic); + ExplorationHeuristicPointer nullHeuristic; + statesNotExplored[stateId] = std::make_pair(state, nullHeuristic); // Reserve one slot for the new state in the remapping matrixBuilder.stateRemapping.push_back(0); STORM_LOG_TRACE("New " << (state->isPseudoState() ? "pseudo" : "concrete") << " state: " << dft.getStateString(state)); @@ -521,6 +539,15 @@ namespace storm { modelComponents.markovianStates.set(matrixBuilder.getCurrentRowGroup() - 1, markovian); } + template<typename ValueType, typename StateType> + void ExplicitDFTModelBuilderApprox<ValueType, StateType>::printNotExplored() const { + std::cout << "states not explored:" << std::endl; + for (auto it : statesNotExplored) { + std::cout << it.first << " -> " << dft.getStateString(it.second.first) << std::endl; + } + } + + // Explicitly instantiate the class. template class ExplicitDFTModelBuilderApprox<double>; diff --git a/src/builder/ExplicitDFTModelBuilderApprox.h b/src/builder/ExplicitDFTModelBuilderApprox.h index 4238194eb..e23a8e819 100644 --- a/src/builder/ExplicitDFTModelBuilderApprox.h +++ b/src/builder/ExplicitDFTModelBuilderApprox.h @@ -10,7 +10,7 @@ #include "src/storage/sparse/StateStorage.h" #include "src/storage/dft/DFT.h" #include "src/storage/dft/SymmetricUnits.h" -#include "src/storage/DynamicPriorityQueue.h" +#include "src/storage/BucketPriorityQueue.h" #include <boost/container/flat_set.hpp> #include <boost/optional/optional.hpp> #include <stack> @@ -28,7 +28,7 @@ namespace storm { using DFTStatePointer = std::shared_ptr<storm::storage::DFTState<ValueType>>; // TODO Matthias: make choosable - using ExplorationHeuristic = DFTExplorationHeuristicNone<ValueType>; + using ExplorationHeuristic = DFTExplorationHeuristicRateRatio<ValueType>; using ExplorationHeuristicPointer = std::shared_ptr<ExplorationHeuristic>; @@ -240,6 +240,8 @@ namespace storm { */ bool isPriorityGreater(StateType idA, StateType idB) const; + void printNotExplored() const; + /*! * Create the model model from the model components. * @@ -293,7 +295,8 @@ namespace storm { storm::storage::sparse::StateStorage<StateType> stateStorage; // A priority queue of states that still need to be explored. - storm::storage::DynamicPriorityQueue<ExplorationHeuristicPointer, std::vector<ExplorationHeuristicPointer>, std::function<bool(ExplorationHeuristicPointer, ExplorationHeuristicPointer)>> explorationQueue; + storm::storage::BucketPriorityQueue<ValueType> explorationQueue; + //storm::storage::DynamicPriorityQueue<ExplorationHeuristicPointer, std::vector<ExplorationHeuristicPointer>, std::function<bool(ExplorationHeuristicPointer, ExplorationHeuristicPointer)>> explorationQueue; // A mapping of not yet explored states from the id to the tuple (state object, heuristic values). std::map<StateType, std::pair<DFTStatePointer, ExplorationHeuristicPointer>> statesNotExplored; diff --git a/src/storage/BucketPriorityQueue.cpp b/src/storage/BucketPriorityQueue.cpp new file mode 100644 index 000000000..e87e25506 --- /dev/null +++ b/src/storage/BucketPriorityQueue.cpp @@ -0,0 +1,191 @@ +#include "src/storage/BucketPriorityQueue.h" +#include "src/utility/macros.h" +#include "src/adapters/CarlAdapter.h" + +namespace storm { + namespace storage { + + template<typename ValueType> + BucketPriorityQueue<ValueType>::BucketPriorityQueue(size_t nrBuckets, double lowerValue, double stepPerBucket) : buckets(nrBuckets), currentBucket(nrBuckets), lowerValue(lowerValue), stepPerBucket(stepPerBucket), nrUnsortedItems(0) { + compare = ([this](HeuristicPointer a, HeuristicPointer b) { + return *a < *b; + }); + } + + template<typename ValueType> + void BucketPriorityQueue<ValueType>::fix() { + if (currentBucket < buckets.size() && nrUnsortedItems > 0) { + // Fix current bucket + std::make_heap(buckets[currentBucket].begin(), buckets[currentBucket].end(), compare); + nrUnsortedItems = 0; + } + } + + template<typename ValueType> + bool BucketPriorityQueue<ValueType>::empty() const { + return currentBucket == buckets.size(); + } + + template<typename ValueType> + std::size_t BucketPriorityQueue<ValueType>::size() const { + size_t size = 0; + for (size_t i = currentBucket; currentBucket < buckets.size(); ++i) { + size += buckets[i].size(); + } + STORM_LOG_ASSERT(size == heuristicMapping.size(), "Sizes to not coincide"); + return size; + } + + template<typename ValueType> + typename BucketPriorityQueue<ValueType>::HeuristicPointer const& BucketPriorityQueue<ValueType>::top() const { + STORM_LOG_ASSERT(!empty(), "BucketPriorityQueue is empty"); + STORM_LOG_ASSERT(nrUnsortedItems == 0, "First bucket is not sorted"); + return buckets[currentBucket].front(); + } + + template<typename ValueType> + void BucketPriorityQueue<ValueType>::push(HeuristicPointer const& item) { + size_t bucket = getBucket(item->getPriority()); + if (bucket < currentBucket) { + setMappingBucket(currentBucket); + currentBucket = bucket; + nrUnsortedItems = 0; + } + buckets[bucket].push_back(item); + if (bucket == currentBucket) { + // Insert in first bucket + if (AUTOSORT) { + std::push_heap(buckets[currentBucket].begin(), buckets[currentBucket].end(), compare); + } else { + ++nrUnsortedItems; + } + } + // Set mapping + heuristicMapping[item->getId()] = std::make_pair(bucket, buckets[bucket].size() - 1); + } + + template<typename ValueType> + void BucketPriorityQueue<ValueType>::update(HeuristicPointer const& item, double oldPriority) { + size_t newBucket = getBucket(item->getPriority()); + size_t oldBucket = getBucket(oldPriority); + + if (oldBucket == newBucket) { + if (currentBucket < newBucket) { + // No change as the bucket is not sorted yet + } else { + if (AUTOSORT) { + // Sort first bucket + fix(); + } else { + ++nrUnsortedItems; + } + } + } else { + // Move to new bucket + STORM_LOG_ASSERT(newBucket < oldBucket, "Will update to higher bucket"); + if (newBucket < currentBucket) { + setMappingBucket(currentBucket); + currentBucket = newBucket; + nrUnsortedItems = 0; + } + // Remove old entry by swap-and-pop + if (buckets[oldBucket].size() >= 2) { + size_t oldIndex = heuristicMapping.at(item->getId()).second; + std::iter_swap(buckets[oldBucket].begin() + oldIndex, buckets[oldBucket].end() - 1); + // Set mapping for swapped item + heuristicMapping[buckets[oldBucket][oldIndex]->getId()] = std::make_pair(oldBucket, oldIndex); + } + buckets[oldBucket].pop_back(); + // Insert new element + buckets[newBucket].push_back(item); + if (newBucket == currentBucket) { + if (AUTOSORT) { + // Sort in first bucket + std::push_heap(buckets[currentBucket].begin(), buckets[currentBucket].end(), compare); + } else { + ++nrUnsortedItems; + } + } + // Set mapping + heuristicMapping[item->getId()] = std::make_pair(newBucket, buckets[newBucket].size() - 1); + } + } + + + template<typename ValueType> + void BucketPriorityQueue<ValueType>::pop() { + STORM_LOG_ASSERT(!empty(), "BucketPriorityQueue is empty"); + STORM_LOG_ASSERT(nrUnsortedItems == 0, "First bucket is not sorted"); + std::pop_heap(buckets[currentBucket].begin(), buckets[currentBucket].end(), compare); + // Remove from mapping + heuristicMapping.erase(buckets[currentBucket].back()->getId()); + buckets[currentBucket].pop_back(); + if (buckets[currentBucket].empty()) { + // Find next bucket with elements + for ( ; currentBucket < buckets.size(); ++currentBucket) { + if (!buckets[currentBucket].empty()) { + nrUnsortedItems = buckets[currentBucket].size(); + if (AUTOSORT) { + fix(); + } + break; + } + } + } + } + + template<typename ValueType> + typename BucketPriorityQueue<ValueType>::HeuristicPointer BucketPriorityQueue<ValueType>::popTop() { + HeuristicPointer item = top(); + pop(); + return item; + } + + template<typename ValueType> + size_t BucketPriorityQueue<ValueType>::getBucket(double priority) const { + STORM_LOG_ASSERT(priority >= lowerValue, "Priority " << priority << " is too low"); + size_t newBucket = (priority - lowerValue) / stepPerBucket; + if (HIGHER) { + newBucket = buckets.size()-1 - newBucket; + } + //std::cout << "get Bucket: " << priority << ", " << newBucket << ", " << ((priority - lowerValue) / stepPerBucket) << std::endl; + STORM_LOG_ASSERT(newBucket < buckets.size(), "Priority " << priority << " is too high"); + return newBucket; + } + + template<typename ValueType> + void BucketPriorityQueue<ValueType>::setMappingBucket(size_t bucket) { + if (bucket < buckets.size()) { + for (size_t index = 0; index < buckets[bucket].size(); ++index) { + heuristicMapping[buckets[bucket][index]->getId()] = std::make_pair(bucket, index); + } + } + } + + template<typename ValueType> + void BucketPriorityQueue<ValueType>::print(std::ostream& out) const { + out << "Bucket priority queue with size " << buckets.size() << ", lower value: " << lowerValue << " and step per bucket: " << stepPerBucket << std::endl; + out << "Current bucket (" << currentBucket << ") has " << nrUnsortedItems << " unsorted items" << std::endl; + for (size_t bucket = 0; bucket < buckets.size(); ++bucket) { + if (!buckets[bucket].empty()) { + out << "Bucket " << bucket << " (" << (HIGHER ? buckets.size() -1 - bucket * stepPerBucket : bucket * stepPerBucket) << "):" << std::endl; + for (HeuristicPointer heuristic : buckets[bucket]) { + out << "\t" << heuristic->getId() << ": " << heuristic->getPriority() << std::endl; + } + } + } + out << "Mapping:" << std::endl; + for (auto it : heuristicMapping) { + out << it.first << " -> " << it.second.first << ", " << it.second.second << std::endl; + } + } + + + // Template instantiations + template class BucketPriorityQueue<double>; + +#ifdef STORM_HAVE_CARL + template class BucketPriorityQueue<storm::RationalFunction>; +#endif + } +} diff --git a/src/storage/BucketPriorityQueue.h b/src/storage/BucketPriorityQueue.h new file mode 100644 index 000000000..a08bfbd3b --- /dev/null +++ b/src/storage/BucketPriorityQueue.h @@ -0,0 +1,70 @@ +#ifndef STORM_STORAGE_BUCKETPRIORITYQUEUE_H_ +#define STORM_STORAGE_BUCKETPRIORITYQUEUE_H_ + +#include "src/builder/DftExplorationHeuristic.h" +#include <algorithm> +#include <functional> +#include <unordered_map> +#include <vector> + +namespace storm { + namespace storage { + + template<typename ValueType> + class BucketPriorityQueue { + + using HeuristicPointer = std::shared_ptr<storm::builder::DFTExplorationHeuristicRateRatio<ValueType>>; + + public: + explicit BucketPriorityQueue(size_t nrBuckets, double lowerValue, double stepPerBucket); + + void fix(); + + bool empty() const; + + std::size_t size() const; + + HeuristicPointer const& top() const; + + void push(HeuristicPointer const& item); + + void update(HeuristicPointer const& item, double oldPriority); + + void pop(); + + HeuristicPointer popTop(); + + void print(std::ostream& out) const; + + private: + + size_t getBucket(double priority) const; + + void setMappingBucket(size_t bucket); + + // List of buckets + std::vector<std::vector<HeuristicPointer>> buckets; + + // Index of first bucket which contains items + size_t currentBucket; + + // Mapping from id to (bucket, index in bucket) + std::unordered_map<size_t, std::pair<size_t, size_t>> heuristicMapping; + + std::function<bool(HeuristicPointer, HeuristicPointer)> compare; + + double lowerValue; + + double stepPerBucket; + + size_t nrUnsortedItems; + + const bool HIGHER = true; + + const bool AUTOSORT = false; + }; + + } +} + +#endif // STORM_STORAGE_BUCKETPRIORITYQUEUE_H_ From ef7d4ac87b564cdc62096c96b658b29524924563 Mon Sep 17 00:00:00 2001 From: Mavo <matthias.volk@rwth-aachen.de> Date: Mon, 24 Oct 2016 17:29:14 +0200 Subject: [PATCH 36/65] Do not sort BEs anymore Former-commit-id: 1789ad3644a4dbbb3b58764831e4d5f5b0147aa2 --- src/storage/dft/DFTState.cpp | 14 +------------- src/storage/dft/DFTState.h | 5 ----- 2 files changed, 1 insertion(+), 18 deletions(-) diff --git a/src/storage/dft/DFTState.cpp b/src/storage/dft/DFTState.cpp index 70ff18122..0b2ff7ee8 100644 --- a/src/storage/dft/DFTState.cpp +++ b/src/storage/dft/DFTState.cpp @@ -32,8 +32,6 @@ namespace storm { STORM_LOG_ASSERT(it != mCurrentlyNotFailableBE.end(), "Id not found."); mCurrentlyNotFailableBE.erase(it); } - - sortFailableBEs(); } template<typename ValueType> @@ -69,8 +67,7 @@ namespace storm { STORM_LOG_TRACE("Spare " << index << " uses " << useId); } } - sortFailableBEs(); - + // Initialize failable dependencies for (size_t dependencyId : mDft.getDependencies()) { std::shared_ptr<DFTDependency<ValueType> const> dependency = mDft.getDependency(dependencyId); @@ -324,7 +321,6 @@ namespace storm { propagateActivation(uses(elem)); } } - sortFailableBEs(); } template<typename ValueType> @@ -422,14 +418,6 @@ namespace storm { } return changed; } - - template<typename ValueType> - void DFTState<ValueType>::sortFailableBEs() { - std::sort(mCurrentlyFailableBE.begin( ), mCurrentlyFailableBE.end( ), [&](size_t const& lhs, size_t const& rhs) { - // Sort decreasing - return getBERate(rhs, true) < getBERate(lhs, true); - }); - } // Explicitly instantiate the class. diff --git a/src/storage/dft/DFTState.h b/src/storage/dft/DFTState.h index 207eecac3..4ff36d1ab 100644 --- a/src/storage/dft/DFTState.h +++ b/src/storage/dft/DFTState.h @@ -313,11 +313,6 @@ namespace storm { */ ValueType getBERate(size_t id, bool considerPassive) const; - /*! - * Sort failable BEs in decreasing order of their active failure rate. - */ - void sortFailableBEs(); - }; } From 815bbf10abd7c5cdc2d30a476f0f8d7ab6a6c908 Mon Sep 17 00:00:00 2001 From: Mavo <matthias.volk@rwth-aachen.de> Date: Mon, 24 Oct 2016 18:27:27 +0200 Subject: [PATCH 37/65] Remove map and use linear search in BucketPriorityQueue Former-commit-id: 3fc131facb4ad1a582ba3245292b5a3443343c35 --- src/storage/BucketPriorityQueue.cpp | 34 ++++++++--------------------- src/storage/BucketPriorityQueue.h | 5 ----- 2 files changed, 9 insertions(+), 30 deletions(-) diff --git a/src/storage/BucketPriorityQueue.cpp b/src/storage/BucketPriorityQueue.cpp index e87e25506..bc3ca8780 100644 --- a/src/storage/BucketPriorityQueue.cpp +++ b/src/storage/BucketPriorityQueue.cpp @@ -32,7 +32,6 @@ namespace storm { for (size_t i = currentBucket; currentBucket < buckets.size(); ++i) { size += buckets[i].size(); } - STORM_LOG_ASSERT(size == heuristicMapping.size(), "Sizes to not coincide"); return size; } @@ -47,7 +46,6 @@ namespace storm { void BucketPriorityQueue<ValueType>::push(HeuristicPointer const& item) { size_t bucket = getBucket(item->getPriority()); if (bucket < currentBucket) { - setMappingBucket(currentBucket); currentBucket = bucket; nrUnsortedItems = 0; } @@ -60,8 +58,6 @@ namespace storm { ++nrUnsortedItems; } } - // Set mapping - heuristicMapping[item->getId()] = std::make_pair(bucket, buckets[bucket].size() - 1); } template<typename ValueType> @@ -84,16 +80,21 @@ namespace storm { // Move to new bucket STORM_LOG_ASSERT(newBucket < oldBucket, "Will update to higher bucket"); if (newBucket < currentBucket) { - setMappingBucket(currentBucket); currentBucket = newBucket; nrUnsortedItems = 0; } // Remove old entry by swap-and-pop if (buckets[oldBucket].size() >= 2) { - size_t oldIndex = heuristicMapping.at(item->getId()).second; + // Find old index by linear search + // Notice: using a map to rememeber index was not efficient + size_t oldIndex = 0; + for ( ; oldIndex < buckets[oldBucket].size(); ++oldIndex) { + if (buckets[oldBucket][oldIndex]->getId() == item->getId()) { + break; + } + } + STORM_LOG_ASSERT(oldIndex < buckets[oldBucket].size(), "Id " << item->getId() << " not found"); std::iter_swap(buckets[oldBucket].begin() + oldIndex, buckets[oldBucket].end() - 1); - // Set mapping for swapped item - heuristicMapping[buckets[oldBucket][oldIndex]->getId()] = std::make_pair(oldBucket, oldIndex); } buckets[oldBucket].pop_back(); // Insert new element @@ -106,8 +107,6 @@ namespace storm { ++nrUnsortedItems; } } - // Set mapping - heuristicMapping[item->getId()] = std::make_pair(newBucket, buckets[newBucket].size() - 1); } } @@ -117,8 +116,6 @@ namespace storm { STORM_LOG_ASSERT(!empty(), "BucketPriorityQueue is empty"); STORM_LOG_ASSERT(nrUnsortedItems == 0, "First bucket is not sorted"); std::pop_heap(buckets[currentBucket].begin(), buckets[currentBucket].end(), compare); - // Remove from mapping - heuristicMapping.erase(buckets[currentBucket].back()->getId()); buckets[currentBucket].pop_back(); if (buckets[currentBucket].empty()) { // Find next bucket with elements @@ -153,15 +150,6 @@ namespace storm { return newBucket; } - template<typename ValueType> - void BucketPriorityQueue<ValueType>::setMappingBucket(size_t bucket) { - if (bucket < buckets.size()) { - for (size_t index = 0; index < buckets[bucket].size(); ++index) { - heuristicMapping[buckets[bucket][index]->getId()] = std::make_pair(bucket, index); - } - } - } - template<typename ValueType> void BucketPriorityQueue<ValueType>::print(std::ostream& out) const { out << "Bucket priority queue with size " << buckets.size() << ", lower value: " << lowerValue << " and step per bucket: " << stepPerBucket << std::endl; @@ -174,10 +162,6 @@ namespace storm { } } } - out << "Mapping:" << std::endl; - for (auto it : heuristicMapping) { - out << it.first << " -> " << it.second.first << ", " << it.second.second << std::endl; - } } diff --git a/src/storage/BucketPriorityQueue.h b/src/storage/BucketPriorityQueue.h index a08bfbd3b..2e36e3cb6 100644 --- a/src/storage/BucketPriorityQueue.h +++ b/src/storage/BucketPriorityQueue.h @@ -40,17 +40,12 @@ namespace storm { size_t getBucket(double priority) const; - void setMappingBucket(size_t bucket); - // List of buckets std::vector<std::vector<HeuristicPointer>> buckets; // Index of first bucket which contains items size_t currentBucket; - // Mapping from id to (bucket, index in bucket) - std::unordered_map<size_t, std::pair<size_t, size_t>> heuristicMapping; - std::function<bool(HeuristicPointer, HeuristicPointer)> compare; double lowerValue; From 945447e7e01b176f8eab01e41ae961a1ed8e449e Mon Sep 17 00:00:00 2001 From: Mavo <matthias.volk@rwth-aachen.de> Date: Mon, 24 Oct 2016 18:41:07 +0200 Subject: [PATCH 38/65] Use DFS as default Former-commit-id: 34f9e80d1bd25c18085ca6f89beb5c163644ccdd --- src/builder/ExplicitDFTModelBuilderApprox.cpp | 8 ++++---- src/builder/ExplicitDFTModelBuilderApprox.h | 2 +- src/storage/BucketPriorityQueue.cpp | 2 +- src/storage/BucketPriorityQueue.h | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/builder/ExplicitDFTModelBuilderApprox.cpp b/src/builder/ExplicitDFTModelBuilderApprox.cpp index 07863bb01..78272238c 100644 --- a/src/builder/ExplicitDFTModelBuilderApprox.cpp +++ b/src/builder/ExplicitDFTModelBuilderApprox.cpp @@ -32,12 +32,12 @@ namespace storm { matrixBuilder(!generator.isDeterministicModel()), stateStorage(((dft.stateVectorSize() / 64) + 1) * 64), // TODO Matthias: make choosable - //explorationQueue(dft.nrElements(), 0, 1) - explorationQueue(41, 0, 1.0/20) + explorationQueue(dft.nrElements()+1, 0, 1) + //explorationQueue(141, 0, 0.02) { // Intentionally left empty. // TODO Matthias: remove again - heuristic = storm::builder::ApproximationHeuristic::RATERATIO; + heuristic = storm::builder::ApproximationHeuristic::NONE; } template<typename ValueType, typename StateType> @@ -90,7 +90,7 @@ namespace storm { switch (heuristic) { case storm::builder::ApproximationHeuristic::NONE: // Do not change anything - approximationThreshold = 0; + approximationThreshold = dft.nrElements()+10; break; case storm::builder::ApproximationHeuristic::DEPTH: approximationThreshold = iteration; diff --git a/src/builder/ExplicitDFTModelBuilderApprox.h b/src/builder/ExplicitDFTModelBuilderApprox.h index e23a8e819..1f779e44f 100644 --- a/src/builder/ExplicitDFTModelBuilderApprox.h +++ b/src/builder/ExplicitDFTModelBuilderApprox.h @@ -28,7 +28,7 @@ namespace storm { using DFTStatePointer = std::shared_ptr<storm::storage::DFTState<ValueType>>; // TODO Matthias: make choosable - using ExplorationHeuristic = DFTExplorationHeuristicRateRatio<ValueType>; + using ExplorationHeuristic = DFTExplorationHeuristicDepth<ValueType>; using ExplorationHeuristicPointer = std::shared_ptr<ExplorationHeuristic>; diff --git a/src/storage/BucketPriorityQueue.cpp b/src/storage/BucketPriorityQueue.cpp index bc3ca8780..fe1ca95b5 100644 --- a/src/storage/BucketPriorityQueue.cpp +++ b/src/storage/BucketPriorityQueue.cpp @@ -16,7 +16,7 @@ namespace storm { void BucketPriorityQueue<ValueType>::fix() { if (currentBucket < buckets.size() && nrUnsortedItems > 0) { // Fix current bucket - std::make_heap(buckets[currentBucket].begin(), buckets[currentBucket].end(), compare); + //std::make_heap(buckets[currentBucket].begin(), buckets[currentBucket].end(), compare); nrUnsortedItems = 0; } } diff --git a/src/storage/BucketPriorityQueue.h b/src/storage/BucketPriorityQueue.h index 2e36e3cb6..3d3c2d35d 100644 --- a/src/storage/BucketPriorityQueue.h +++ b/src/storage/BucketPriorityQueue.h @@ -13,7 +13,7 @@ namespace storm { template<typename ValueType> class BucketPriorityQueue { - using HeuristicPointer = std::shared_ptr<storm::builder::DFTExplorationHeuristicRateRatio<ValueType>>; + using HeuristicPointer = std::shared_ptr<storm::builder::DFTExplorationHeuristicDepth<ValueType>>; public: explicit BucketPriorityQueue(size_t nrBuckets, double lowerValue, double stepPerBucket); From 8b78ed234068f118c45eb23fe45b74e614f7e4e9 Mon Sep 17 00:00:00 2001 From: Mavo <matthias.volk@rwth-aachen.de> Date: Tue, 25 Oct 2016 00:24:53 +0200 Subject: [PATCH 39/65] Renamed rateratio to probability Former-commit-id: 6d07985b1d63af7c94245926a8ac5dddb560cade --- src/builder/DftExplorationHeuristic.cpp | 25 ++++++---- src/builder/DftExplorationHeuristic.h | 46 +++++++++++++------ src/builder/ExplicitDFTModelBuilderApprox.cpp | 13 +++--- src/settings/modules/DFTSettings.cpp | 6 +-- 4 files changed, 57 insertions(+), 33 deletions(-) diff --git a/src/builder/DftExplorationHeuristic.cpp b/src/builder/DftExplorationHeuristic.cpp index dbd64088b..75556a06f 100644 --- a/src/builder/DftExplorationHeuristic.cpp +++ b/src/builder/DftExplorationHeuristic.cpp @@ -8,31 +8,36 @@ namespace storm { namespace builder { template<typename ValueType> - DFTExplorationHeuristic<ValueType>::DFTExplorationHeuristic(size_t id, size_t depth, ValueType rate, ValueType exitRate) : id(id), expand(false), depth(depth) { + DFTExplorationHeuristic<ValueType>::DFTExplorationHeuristic(size_t id) : id(id), expand(false), depth(0), probability(storm::utility::zero<ValueType>()) { + // Intentionally left empty + } + + template<typename ValueType> + DFTExplorationHeuristic<ValueType>::DFTExplorationHeuristic(size_t id, DFTExplorationHeuristic const& predecessor, ValueType rate, ValueType exitRate) : id(id), expand(false), depth(predecessor.depth + 1) { STORM_LOG_ASSERT(storm::utility::zero<ValueType>() < exitRate, "Exit rate is 0"); - rateRatio = rate/exitRate; + probability = predecessor.probability * rate/exitRate; } template<> - bool DFTExplorationHeuristicRateRatio<double>::updateHeuristicValues(size_t depth, double rate, double exitRate) { + bool DFTExplorationHeuristicProbability<double>::updateHeuristicValues(DFTExplorationHeuristic<double> const& predecessor, double rate, double exitRate) { STORM_LOG_ASSERT(exitRate > 0, "Exit rate is 0"); - rateRatio += rate/exitRate; + probability += predecessor.getProbability() * rate/exitRate; return true; } template<> - bool DFTExplorationHeuristicRateRatio<storm::RationalFunction>::updateHeuristicValues(size_t depth, storm::RationalFunction rate, storm::RationalFunction exitRate) { + bool DFTExplorationHeuristicProbability<storm::RationalFunction>::updateHeuristicValues(DFTExplorationHeuristic<storm::RationalFunction> const& predecessor, storm::RationalFunction rate, storm::RationalFunction exitRate) { STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Heuristic rate ration does not work for rational functions."); return false; } template<> - double DFTExplorationHeuristicRateRatio<double>::getPriority() const { - return rateRatio; + double DFTExplorationHeuristicProbability<double>::getPriority() const { + return probability; } template<> - double DFTExplorationHeuristicRateRatio<storm::RationalFunction>::getPriority() const { + double DFTExplorationHeuristicProbability<storm::RationalFunction>::getPriority() const { STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Heuristic rate ration does not work for rational functions."); } @@ -41,13 +46,13 @@ namespace storm { template class DFTExplorationHeuristic<double>; template class DFTExplorationHeuristicNone<double>; template class DFTExplorationHeuristicDepth<double>; - template class DFTExplorationHeuristicRateRatio<double>; + template class DFTExplorationHeuristicProbability<double>; #ifdef STORM_HAVE_CARL template class DFTExplorationHeuristic<storm::RationalFunction>; template class DFTExplorationHeuristicNone<storm::RationalFunction>; template class DFTExplorationHeuristicDepth<storm::RationalFunction>; - template class DFTExplorationHeuristicRateRatio<storm::RationalFunction>; + template class DFTExplorationHeuristicProbability<storm::RationalFunction>; #endif } } diff --git a/src/builder/DftExplorationHeuristic.h b/src/builder/DftExplorationHeuristic.h index d23c673fc..f24d7defb 100644 --- a/src/builder/DftExplorationHeuristic.h +++ b/src/builder/DftExplorationHeuristic.h @@ -10,7 +10,7 @@ namespace storm { /*! * Enum representing the heuristic used for deciding which states to expand. */ - enum class ApproximationHeuristic { NONE, DEPTH, RATERATIO }; + enum class ApproximationHeuristic { NONE, DEPTH, PROBABILITY }; /*! @@ -20,9 +20,11 @@ namespace storm { class DFTExplorationHeuristic { public: - DFTExplorationHeuristic(size_t id, size_t depth, ValueType rate, ValueType exitRate); + DFTExplorationHeuristic(size_t id); - virtual bool updateHeuristicValues(size_t depth, ValueType rate, ValueType exitRate) = 0; + DFTExplorationHeuristic(size_t id, DFTExplorationHeuristic const& predecessor, ValueType rate, ValueType exitRate); + + virtual bool updateHeuristicValues(DFTExplorationHeuristic const& predecessor, ValueType rate, ValueType exitRate) = 0; virtual double getPriority() const = 0; @@ -40,21 +42,29 @@ namespace storm { return depth; } + ValueType getProbability() const { + return probability; + } + protected: size_t id; bool expand; size_t depth; - ValueType rateRatio; + ValueType probability; }; template<typename ValueType> class DFTExplorationHeuristicNone : public DFTExplorationHeuristic<ValueType> { public: - DFTExplorationHeuristicNone(size_t id, size_t depth, ValueType rate, ValueType exitRate) : DFTExplorationHeuristic<ValueType>(id, depth, rate, exitRate) { + DFTExplorationHeuristicNone(size_t id) : DFTExplorationHeuristic<ValueType>(id) { + // Intentionally left empty + } + + DFTExplorationHeuristicNone(size_t id, DFTExplorationHeuristicNone<ValueType> const& predecessor, ValueType rate, ValueType exitRate) : DFTExplorationHeuristic<ValueType>(id, predecessor, rate, exitRate) { // Intentionally left empty } - bool updateHeuristicValues(size_t depth, ValueType rate, ValueType exitRate) override { + bool updateHeuristicValues(DFTExplorationHeuristic<ValueType> const& predecessor, ValueType rate, ValueType exitRate) override { return false; } @@ -74,13 +84,17 @@ namespace storm { template<typename ValueType> class DFTExplorationHeuristicDepth : public DFTExplorationHeuristic<ValueType> { public: - DFTExplorationHeuristicDepth(size_t id, size_t depth, ValueType rate, ValueType exitRate) : DFTExplorationHeuristic<ValueType>(id, depth, rate, exitRate) { + DFTExplorationHeuristicDepth(size_t id) : DFTExplorationHeuristic<ValueType>(id) { // Intentionally left empty } - bool updateHeuristicValues(size_t depth, ValueType rate, ValueType exitRate) override { - if (depth < this->depth) { - this->depth = depth; + DFTExplorationHeuristicDepth(size_t id, DFTExplorationHeuristicDepth<ValueType> const& predecessor, ValueType rate, ValueType exitRate) : DFTExplorationHeuristic<ValueType>(id, predecessor, rate, exitRate) { + // Intentionally left empty + } + + bool updateHeuristicValues(DFTExplorationHeuristic<ValueType> const& predecessor, ValueType rate, ValueType exitRate) override { + if (predecessor.getDepth() < this->depth) { + this->depth = predecessor.getDepth(); return true; } return false; @@ -101,13 +115,17 @@ namespace storm { }; template<typename ValueType> - class DFTExplorationHeuristicRateRatio : public DFTExplorationHeuristic<ValueType> { + class DFTExplorationHeuristicProbability : public DFTExplorationHeuristic<ValueType> { public: - DFTExplorationHeuristicRateRatio(size_t id, size_t depth, ValueType rate, ValueType exitRate) : DFTExplorationHeuristic<ValueType>(id, depth, rate, exitRate) { + DFTExplorationHeuristicProbability(size_t id) : DFTExplorationHeuristic<ValueType>(id) { + // Intentionally left empty + } + + DFTExplorationHeuristicProbability(size_t id, DFTExplorationHeuristicProbability<ValueType> const& predecessor, ValueType rate, ValueType exitRate) : DFTExplorationHeuristic<ValueType>(id, predecessor, rate, exitRate) { // Intentionally left empty } - bool updateHeuristicValues(size_t depth, ValueType rate, ValueType exitRate) override; + bool updateHeuristicValues(DFTExplorationHeuristic<ValueType> const& predecessor, ValueType rate, ValueType exitRate) override; double getPriority() const override; @@ -115,7 +133,7 @@ namespace storm { return !this->expand && this->getPriority() < approximationThreshold; } - bool operator<(DFTExplorationHeuristicRateRatio<ValueType> const& other) const { + bool operator<(DFTExplorationHeuristicProbability<ValueType> const& other) const { return this->getPriority() < other.getPriority(); } }; diff --git a/src/builder/ExplicitDFTModelBuilderApprox.cpp b/src/builder/ExplicitDFTModelBuilderApprox.cpp index 78272238c..6b35344cd 100644 --- a/src/builder/ExplicitDFTModelBuilderApprox.cpp +++ b/src/builder/ExplicitDFTModelBuilderApprox.cpp @@ -37,7 +37,7 @@ namespace storm { { // Intentionally left empty. // TODO Matthias: remove again - heuristic = storm::builder::ApproximationHeuristic::NONE; + heuristic = storm::builder::ApproximationHeuristic::PROBABILITY; } template<typename ValueType, typename StateType> @@ -79,7 +79,7 @@ namespace storm { STORM_LOG_TRACE("Initial state: " << initialStateIndex); // Initialize heuristic values for inital state STORM_LOG_ASSERT(!statesNotExplored.at(initialStateIndex).second, "Heuristic for initial state is already initialized"); - ExplorationHeuristicPointer heuristic = std::make_shared<ExplorationHeuristic>(initialStateIndex, 0, storm::utility::zero<ValueType>(), storm::utility::one<ValueType>()); + ExplorationHeuristicPointer heuristic = std::make_shared<ExplorationHeuristic>(initialStateIndex); heuristic->markExpand(); statesNotExplored[initialStateIndex].second = heuristic; explorationQueue.push(heuristic); @@ -95,8 +95,9 @@ namespace storm { case storm::builder::ApproximationHeuristic::DEPTH: approximationThreshold = iteration; break; - case storm::builder::ApproximationHeuristic::RATERATIO: - approximationThreshold = std::pow(0.1, iteration) * approximationThreshold; + case storm::builder::ApproximationHeuristic::PROBABILITY: + //approximationThreshold = std::pow(0.1, iteration) * approximationThreshold; + approximationThreshold = 10 * std::pow(2, iteration); break; } exploreStateSpace(approximationThreshold); @@ -298,12 +299,12 @@ namespace storm { DFTStatePointer state = iter->second.first; if (!iter->second.second) { // Initialize heuristic values - ExplorationHeuristicPointer heuristic = std::make_shared<ExplorationHeuristic>(stateProbabilityPair.first, currentExplorationHeuristic->getDepth() + 1, stateProbabilityPair.second, choice.getTotalMass()); + ExplorationHeuristicPointer heuristic = std::make_shared<ExplorationHeuristic>(stateProbabilityPair.first, *currentExplorationHeuristic, stateProbabilityPair.second, choice.getTotalMass()); iter->second.second = heuristic; explorationQueue.push(heuristic); } else { double oldPriority = iter->second.second->getPriority(); - if (iter->second.second->updateHeuristicValues(currentExplorationHeuristic->getDepth() + 1, stateProbabilityPair.second, choice.getTotalMass())) { + if (iter->second.second->updateHeuristicValues(*currentExplorationHeuristic, stateProbabilityPair.second, choice.getTotalMass())) { // Update priority queue ++fix; explorationQueue.update(iter->second.second, oldPriority); diff --git a/src/settings/modules/DFTSettings.cpp b/src/settings/modules/DFTSettings.cpp index 0dcb49f1d..ab707b5a4 100644 --- a/src/settings/modules/DFTSettings.cpp +++ b/src/settings/modules/DFTSettings.cpp @@ -40,7 +40,7 @@ namespace storm { this->addOption(storm::settings::OptionBuilder(moduleName, modularisationOptionName, false, "Use modularisation (not applicable for expected time).").build()); this->addOption(storm::settings::OptionBuilder(moduleName, disableDCOptionName, false, "Disable Dont Care propagation.").build()); this->addOption(storm::settings::OptionBuilder(moduleName, approximationErrorOptionName, false, "Approximation error allowed.").setShortName(approximationErrorOptionShortName).addArgument(storm::settings::ArgumentBuilder::createDoubleArgument("error", "The relative approximation error to use.").addValidationFunctionDouble(storm::settings::ArgumentValidators::doubleGreaterValidatorIncluding(0.0)).build()).build()); - this->addOption(storm::settings::OptionBuilder(moduleName, approximationHeuristicOptionName, false, "Set the heuristic used for approximation.").addArgument(storm::settings::ArgumentBuilder::createStringArgument("heuristic", "Sets which heuristic is used for approximation. Must be in {depth, rateratio}. Default is").setDefaultValueString("depth").addValidationFunctionString(storm::settings::ArgumentValidators::stringInListValidator({"depth", "rateratio"})).build()).build()); + this->addOption(storm::settings::OptionBuilder(moduleName, approximationHeuristicOptionName, false, "Set the heuristic used for approximation.").addArgument(storm::settings::ArgumentBuilder::createStringArgument("heuristic", "Sets which heuristic is used for approximation. Must be in {depth, probability}. Default is").setDefaultValueString("depth").addValidationFunctionString(storm::settings::ArgumentValidators::stringInListValidator({"depth", "rateratio"})).build()).build()); this->addOption(storm::settings::OptionBuilder(moduleName, propExpectedTimeOptionName, false, "Compute expected time of system failure.").setShortName(propExpectedTimeOptionShortName).build()); this->addOption(storm::settings::OptionBuilder(moduleName, propProbabilityOptionName, false, "Compute probability of system failure.").build()); this->addOption(storm::settings::OptionBuilder(moduleName, propTimeBoundOptionName, false, "Compute probability of system failure up to given timebound.").addArgument(storm::settings::ArgumentBuilder::createDoubleArgument("time", "The timebound to use.").addValidationFunctionDouble(storm::settings::ArgumentValidators::doubleGreaterValidatorExcluding(0.0)).build()).build()); @@ -87,8 +87,8 @@ namespace storm { std::string heuristicAsString = this->getOption(approximationHeuristicOptionName).getArgumentByName("heuristic").getValueAsString(); if (heuristicAsString == "depth") { return storm::builder::ApproximationHeuristic::DEPTH; - } else if (heuristicAsString == "rateratio") { - return storm::builder::ApproximationHeuristic::RATERATIO; + } else if (heuristicAsString == "probability") { + return storm::builder::ApproximationHeuristic::PROBABILITY; } STORM_LOG_THROW(false, storm::exceptions::IllegalArgumentValueException, "Illegal value '" << heuristicAsString << "' set as heuristic for approximation."); } From 20b00e8f1dcff6e72df2c6011b5b4622f7046870 Mon Sep 17 00:00:00 2001 From: Mavo <matthias.volk@rwth-aachen.de> Date: Tue, 25 Oct 2016 00:25:35 +0200 Subject: [PATCH 40/65] Propagate dont care to currently not failable BEs Former-commit-id: 1fcb15f4ec045d244e1397b4fbe2100435ea9557 --- src/storage/dft/DFTState.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/storage/dft/DFTState.cpp b/src/storage/dft/DFTState.cpp index 0b2ff7ee8..369bed3f7 100644 --- a/src/storage/dft/DFTState.cpp +++ b/src/storage/dft/DFTState.cpp @@ -200,8 +200,13 @@ namespace storm { template<typename ValueType> void DFTState<ValueType>::beNoLongerFailable(size_t id) { auto it = std::find(mCurrentlyFailableBE.begin(), mCurrentlyFailableBE.end(), id); - if(it != mCurrentlyFailableBE.end()) { + if (it != mCurrentlyFailableBE.end()) { mCurrentlyFailableBE.erase(it); + } else { + it = std::find(mCurrentlyNotFailableBE.begin(), mCurrentlyNotFailableBE.end(), id); + if (it != mCurrentlyNotFailableBE.end()) { + mCurrentlyNotFailableBE.erase(it); + } } } @@ -253,7 +258,7 @@ namespace storm { ValueType DFTState<ValueType>::getNotFailableBERate(size_t index) const { STORM_LOG_ASSERT(index < nrNotFailableBEs(), "Index invalid."); STORM_LOG_ASSERT(storm::utility::isZero<ValueType>(mDft.getBasicElement(mCurrentlyNotFailableBE[index])->activeFailureRate()) || - (mDft.hasRepresentant(mCurrentlyNotFailableBE[index]) && !isActive(mDft.getRepresentant(mCurrentlyNotFailableBE[index]))), "BE " << mCurrentlyNotFailableBE[index] << " can fail"); + (mDft.hasRepresentant(mCurrentlyNotFailableBE[index]) && !isActive(mDft.getRepresentant(mCurrentlyNotFailableBE[index]))), "BE " << mCurrentlyNotFailableBE[index] << " can fail in state: " << mDft.getStateString(mStatus, mStateGenerationInfo, mId)); // Use active failure rate as passive failure rate is 0. return getBERate(mCurrentlyNotFailableBE[index], false); } From 0d9cdd6ef882fbe21b5db8e9b1ee6d85a02bd767 Mon Sep 17 00:00:00 2001 From: Mavo <matthias.volk@rwth-aachen.de> Date: Tue, 25 Oct 2016 13:47:06 +0200 Subject: [PATCH 41/65] Use Heuristic None Former-commit-id: 63f78f3db06074f322326d9169deed15256b8304 --- src/builder/ExplicitDFTModelBuilderApprox.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/builder/ExplicitDFTModelBuilderApprox.cpp b/src/builder/ExplicitDFTModelBuilderApprox.cpp index 6b35344cd..7de1e0bb4 100644 --- a/src/builder/ExplicitDFTModelBuilderApprox.cpp +++ b/src/builder/ExplicitDFTModelBuilderApprox.cpp @@ -33,11 +33,11 @@ namespace storm { stateStorage(((dft.stateVectorSize() / 64) + 1) * 64), // TODO Matthias: make choosable explorationQueue(dft.nrElements()+1, 0, 1) - //explorationQueue(141, 0, 0.02) + //explorationQueue(1001, 0, 0.001) { // Intentionally left empty. // TODO Matthias: remove again - heuristic = storm::builder::ApproximationHeuristic::PROBABILITY; + heuristic = storm::builder::ApproximationHeuristic::NONE; } template<typename ValueType, typename StateType> @@ -97,7 +97,7 @@ namespace storm { break; case storm::builder::ApproximationHeuristic::PROBABILITY: //approximationThreshold = std::pow(0.1, iteration) * approximationThreshold; - approximationThreshold = 10 * std::pow(2, iteration); + approximationThreshold = iteration;//10 * std::pow(2, iteration); break; } exploreStateSpace(approximationThreshold); @@ -267,6 +267,7 @@ namespace storm { // Try to explore the next state generator.load(currentState); + //if (nrExpandedStates > approximationThreshold) { if (currentExplorationHeuristic->isSkip(approximationThreshold)) { // Skip the current state ++nrSkippedStates; From 58f870729307fb4df9612b292187ea711403a589 Mon Sep 17 00:00:00 2001 From: Mavo <matthias.volk@rwth-aachen.de> Date: Tue, 25 Oct 2016 23:19:41 +0200 Subject: [PATCH 42/65] Tighter over-approximation Former-commit-id: 824e74f88df07a49ab306b2599fd82f9ced47366 --- src/builder/DftExplorationHeuristic.cpp | 21 ++- src/builder/DftExplorationHeuristic.h | 55 +++++++- src/builder/ExplicitDFTModelBuilderApprox.cpp | 124 +++++++++++------- src/builder/ExplicitDFTModelBuilderApprox.h | 22 +++- src/modelchecker/dft/DFTModelChecker.cpp | 1 + src/storage/BucketPriorityQueue.cpp | 23 +++- src/storage/BucketPriorityQueue.h | 5 +- src/storage/dft/DFTState.cpp | 8 +- 8 files changed, 196 insertions(+), 63 deletions(-) diff --git a/src/builder/DftExplorationHeuristic.cpp b/src/builder/DftExplorationHeuristic.cpp index 75556a06f..db26fc60d 100644 --- a/src/builder/DftExplorationHeuristic.cpp +++ b/src/builder/DftExplorationHeuristic.cpp @@ -8,16 +8,22 @@ namespace storm { namespace builder { template<typename ValueType> - DFTExplorationHeuristic<ValueType>::DFTExplorationHeuristic(size_t id) : id(id), expand(false), depth(0), probability(storm::utility::zero<ValueType>()) { + DFTExplorationHeuristic<ValueType>::DFTExplorationHeuristic(size_t id) : id(id), expand(false), lowerBound(storm::utility::zero<ValueType>()), upperBound(storm::utility::infinity<ValueType>()), depth(0), probability(storm::utility::one<ValueType>()) { // Intentionally left empty } template<typename ValueType> - DFTExplorationHeuristic<ValueType>::DFTExplorationHeuristic(size_t id, DFTExplorationHeuristic const& predecessor, ValueType rate, ValueType exitRate) : id(id), expand(false), depth(predecessor.depth + 1) { + DFTExplorationHeuristic<ValueType>::DFTExplorationHeuristic(size_t id, DFTExplorationHeuristic const& predecessor, ValueType rate, ValueType exitRate, ValueType lowerBound, ValueType upperBound) : id(id), expand(false), lowerBound(lowerBound), upperBound(upperBound), depth(predecessor.depth + 1) { STORM_LOG_ASSERT(storm::utility::zero<ValueType>() < exitRate, "Exit rate is 0"); probability = predecessor.probability * rate/exitRate; } + template<typename ValueType> + void DFTExplorationHeuristic<ValueType>::setBounds(ValueType lowerBound, ValueType upperBound) { + this->lowerBound = lowerBound; + this->upperBound = upperBound; + } + template<> bool DFTExplorationHeuristicProbability<double>::updateHeuristicValues(DFTExplorationHeuristic<double> const& predecessor, double rate, double exitRate) { STORM_LOG_ASSERT(exitRate > 0, "Exit rate is 0"); @@ -41,18 +47,29 @@ namespace storm { STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Heuristic rate ration does not work for rational functions."); } + template<> + double DFTExplorationHeuristicBoundDifference<double>::getPriority() const { + return upperBound / lowerBound; + } + + template<> + double DFTExplorationHeuristicBoundDifference<storm::RationalFunction>::getPriority() const { + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Heuristic bound difference does not work for rational functions."); + } // Instantiate templates. template class DFTExplorationHeuristic<double>; template class DFTExplorationHeuristicNone<double>; template class DFTExplorationHeuristicDepth<double>; template class DFTExplorationHeuristicProbability<double>; + template class DFTExplorationHeuristicBoundDifference<double>; #ifdef STORM_HAVE_CARL template class DFTExplorationHeuristic<storm::RationalFunction>; template class DFTExplorationHeuristicNone<storm::RationalFunction>; template class DFTExplorationHeuristicDepth<storm::RationalFunction>; template class DFTExplorationHeuristicProbability<storm::RationalFunction>; + template class DFTExplorationHeuristicBoundDifference<storm::RationalFunction>; #endif } } diff --git a/src/builder/DftExplorationHeuristic.h b/src/builder/DftExplorationHeuristic.h index f24d7defb..12f178f25 100644 --- a/src/builder/DftExplorationHeuristic.h +++ b/src/builder/DftExplorationHeuristic.h @@ -22,7 +22,9 @@ namespace storm { public: DFTExplorationHeuristic(size_t id); - DFTExplorationHeuristic(size_t id, DFTExplorationHeuristic const& predecessor, ValueType rate, ValueType exitRate); + DFTExplorationHeuristic(size_t id, DFTExplorationHeuristic const& predecessor, ValueType rate, ValueType exitRate, ValueType lowerBound, ValueType upperBound); + + void setBounds(ValueType lowerBound, ValueType upperBound); virtual bool updateHeuristicValues(DFTExplorationHeuristic const& predecessor, ValueType rate, ValueType exitRate) = 0; @@ -38,6 +40,10 @@ namespace storm { return id; } + bool isExpand() { + return expand; + } + size_t getDepth() const { return depth; } @@ -46,9 +52,19 @@ namespace storm { return probability; } + ValueType getLowerBound() const { + return lowerBound; + } + + ValueType getUpperBound() const { + return upperBound; + } + protected: size_t id; bool expand; + ValueType lowerBound; + ValueType upperBound; size_t depth; ValueType probability; }; @@ -60,7 +76,7 @@ namespace storm { // Intentionally left empty } - DFTExplorationHeuristicNone(size_t id, DFTExplorationHeuristicNone<ValueType> const& predecessor, ValueType rate, ValueType exitRate) : DFTExplorationHeuristic<ValueType>(id, predecessor, rate, exitRate) { + DFTExplorationHeuristicNone(size_t id, DFTExplorationHeuristicNone<ValueType> const& predecessor, ValueType rate, ValueType exitRate, ValueType lowerBound, ValueType upperBound) : DFTExplorationHeuristic<ValueType>(id, predecessor, rate, exitRate, lowerBound, upperBound) { // Intentionally left empty } @@ -88,7 +104,7 @@ namespace storm { // Intentionally left empty } - DFTExplorationHeuristicDepth(size_t id, DFTExplorationHeuristicDepth<ValueType> const& predecessor, ValueType rate, ValueType exitRate) : DFTExplorationHeuristic<ValueType>(id, predecessor, rate, exitRate) { + DFTExplorationHeuristicDepth(size_t id, DFTExplorationHeuristicDepth<ValueType> const& predecessor, ValueType rate, ValueType exitRate, ValueType lowerBound, ValueType upperBound) : DFTExplorationHeuristic<ValueType>(id, predecessor, rate, exitRate, lowerBound, upperBound) { // Intentionally left empty } @@ -121,7 +137,7 @@ namespace storm { // Intentionally left empty } - DFTExplorationHeuristicProbability(size_t id, DFTExplorationHeuristicProbability<ValueType> const& predecessor, ValueType rate, ValueType exitRate) : DFTExplorationHeuristic<ValueType>(id, predecessor, rate, exitRate) { + DFTExplorationHeuristicProbability(size_t id, DFTExplorationHeuristicProbability<ValueType> const& predecessor, ValueType rate, ValueType exitRate, ValueType lowerBound, ValueType upperBound) : DFTExplorationHeuristic<ValueType>(id, predecessor, rate, exitRate, lowerBound, upperBound) { // Intentionally left empty } @@ -138,6 +154,37 @@ namespace storm { } }; + template<typename ValueType> + class DFTExplorationHeuristicBoundDifference : public DFTExplorationHeuristic<ValueType> { + public: + DFTExplorationHeuristicBoundDifference(size_t id) : DFTExplorationHeuristic<ValueType>(id) { + // Intentionally left empty + } + + DFTExplorationHeuristicBoundDifference(size_t id, DFTExplorationHeuristicBoundDifference<ValueType> const& predecessor, ValueType rate, ValueType exitRate, ValueType lowerBound, ValueType upperBound) : DFTExplorationHeuristic<ValueType>(id, predecessor, rate, exitRate, lowerBound, upperBound) { + // Intentionally left empty + } + + bool updateHeuristicValues(DFTExplorationHeuristic<ValueType> const& predecessor, ValueType rate, ValueType exitRate) override { + return false; + } + + double getPriority() const override; + + bool isSkip(double approximationThreshold) const override { + return !this->expand && this->getPriority() < approximationThreshold; + } + + bool operator<(DFTExplorationHeuristicBoundDifference<ValueType> const& other) const { + return this->getPriority() < other.getPriority(); + } + + private: + ValueType lowerBound; + ValueType upperBound; + }; + + } } diff --git a/src/builder/ExplicitDFTModelBuilderApprox.cpp b/src/builder/ExplicitDFTModelBuilderApprox.cpp index 7de1e0bb4..0328cf055 100644 --- a/src/builder/ExplicitDFTModelBuilderApprox.cpp +++ b/src/builder/ExplicitDFTModelBuilderApprox.cpp @@ -3,6 +3,7 @@ #include <src/models/sparse/Ctmc.h> #include <src/utility/constants.h> #include <src/utility/vector.h> +#include "src/utility/bitoperations.h" #include <src/exceptions/UnexpectedException.h> #include "src/settings/modules/DFTSettings.h" #include "src/settings/SettingsManager.h" @@ -32,12 +33,12 @@ namespace storm { matrixBuilder(!generator.isDeterministicModel()), stateStorage(((dft.stateVectorSize() / 64) + 1) * 64), // TODO Matthias: make choosable - explorationQueue(dft.nrElements()+1, 0, 1) - //explorationQueue(1001, 0, 0.001) + //explorationQueue(dft.nrElements()+1, 0, 1) + explorationQueue(1001, 0, 0.001) { // Intentionally left empty. // TODO Matthias: remove again - heuristic = storm::builder::ApproximationHeuristic::NONE; + heuristic = storm::builder::ApproximationHeuristic::PROBABILITY; } template<typename ValueType, typename StateType> @@ -256,6 +257,9 @@ namespace storm { if (currentState->isPseudoState()) { // Create concrete state from pseudo state currentState->construct(); + ValueType lowerBound = getLowerBound(currentState); + ValueType upperBound = getUpperBound(currentState); + currentExplorationHeuristic->setBounds(lowerBound, upperBound); } STORM_LOG_ASSERT(!currentState->isPseudoState(), "State is pseudo state."); @@ -267,8 +271,8 @@ namespace storm { // Try to explore the next state generator.load(currentState); - //if (nrExpandedStates > approximationThreshold) { - if (currentExplorationHeuristic->isSkip(approximationThreshold)) { + if (nrExpandedStates > approximationThreshold && !currentExplorationHeuristic->isExpand()) { + //if (currentExplorationHeuristic->isSkip(approximationThreshold)) { // Skip the current state ++nrSkippedStates; STORM_LOG_TRACE("Skip expansion of state: " << dft.getStateString(currentState)); @@ -299,11 +303,24 @@ namespace storm { // Update heuristic values DFTStatePointer state = iter->second.first; if (!iter->second.second) { + ValueType lowerBound; + ValueType upperBound; + if (state->isPseudoState()) { + lowerBound = storm::utility::infinity<ValueType>(); + upperBound = storm::utility::infinity<ValueType>(); + } else { + lowerBound = getLowerBound(state); + upperBound = getUpperBound(state); + } // Initialize heuristic values - ExplorationHeuristicPointer heuristic = std::make_shared<ExplorationHeuristic>(stateProbabilityPair.first, *currentExplorationHeuristic, stateProbabilityPair.second, choice.getTotalMass()); + ExplorationHeuristicPointer heuristic = std::make_shared<ExplorationHeuristic>(stateProbabilityPair.first, *currentExplorationHeuristic, stateProbabilityPair.second, choice.getTotalMass(), lowerBound, upperBound); iter->second.second = heuristic; + if (state->hasFailed(dft.getTopLevelIndex()) || state->isFailsafe(dft.getTopLevelIndex()) || state->nrFailableDependencies() > 0 || (state->nrFailableDependencies() == 0 && state->nrFailableBEs() == 0)) { + // Do not skip absorbing state or if reached by dependencies + iter->second.second->markExpand(); + } explorationQueue.push(heuristic); - } else { + } else if (!iter->second.second->isExpand()) { double oldPriority = iter->second.second->getPriority(); if (iter->second.second->updateHeuristicValues(*currentExplorationHeuristic, stateProbabilityPair.second, choice.getTotalMass())) { // Update priority queue @@ -311,11 +328,6 @@ namespace storm { explorationQueue.update(iter->second.second, oldPriority); } } - if (state->hasFailed(dft.getTopLevelIndex()) || state->isFailsafe(dft.getTopLevelIndex()) || state->nrFailableDependencies() > 0 || (state->nrFailableDependencies() == 0 && state->nrFailableBEs() == 0)) { - // Do not skip absorbing state or if reached by dependencies - iter->second.second->markExpand(); - // TODO Matthias give highest priority to ensure expanding before all skipped - } } } matrixBuilder.finishRow(); @@ -383,11 +395,7 @@ namespace storm { template<typename ValueType, typename StateType> std::shared_ptr<storm::models::sparse::Model<ValueType>> ExplicitDFTModelBuilderApprox<ValueType, StateType>::getModelApproximation(bool lowerBound) { // TODO Matthias: handle case with no skipped states - if (lowerBound) { - changeMatrixLowerBound(modelComponents.transitionMatrix); - } else { - changeMatrixUpperBound(modelComponents.transitionMatrix); - } + changeMatrixBound(modelComponents.transitionMatrix, lowerBound); return createModel(true); } @@ -443,44 +451,72 @@ namespace storm { } template<typename ValueType, typename StateType> - void ExplicitDFTModelBuilderApprox<ValueType, StateType>::changeMatrixLowerBound(storm::storage::SparseMatrix<ValueType> & matrix) const { + void ExplicitDFTModelBuilderApprox<ValueType, StateType>::changeMatrixBound(storm::storage::SparseMatrix<ValueType> & matrix, bool lowerBound) const { // Set lower bound for skipped states for (auto it = skippedStates.begin(); it != skippedStates.end(); ++it) { auto matrixEntry = matrix.getRow(it->first, 0).begin(); STORM_LOG_ASSERT(matrixEntry->getColumn() == failedStateId, "Transition has wrong target state."); - // Get the lower bound by considering the failure of all possible BEs - DFTStatePointer state = it->second.first; - ValueType newRate = storm::utility::zero<ValueType>(); - for (size_t index = 0; index < state->nrFailableBEs(); ++index) { - newRate += state->getFailableBERate(index); - } - for (size_t index = 0; index < state->nrNotFailableBEs(); ++index) { - newRate += state->getNotFailableBERate(index); + STORM_LOG_ASSERT(!it->second.first->isPseudoState(), "State is still pseudo state."); + if (lowerBound) { + matrixEntry->setValue(it->second.second->getLowerBound()); + } else { + matrixEntry->setValue(it->second.second->getUpperBound()); } - matrixEntry->setValue(newRate); } } template<typename ValueType, typename StateType> - void ExplicitDFTModelBuilderApprox<ValueType, StateType>::changeMatrixUpperBound(storm::storage::SparseMatrix<ValueType> & matrix) const { - // Set uppper bound for skipped states - for (auto it = skippedStates.begin(); it != skippedStates.end(); ++it) { - auto matrixEntry = matrix.getRow(it->first, 0).begin(); - STORM_LOG_ASSERT(matrixEntry->getColumn() == failedStateId, "Transition has wrong target state."); - // Get the upper bound by considering the failure of all BEs - // The used formula for the rate is 1/( 1/a + 1/b + ...) - // TODO Matthias: improve by using closed formula for AND of all BEs - DFTStatePointer state = it->second.first; - ValueType newRate = storm::utility::zero<ValueType>(); - for (size_t index = 0; index < state->nrFailableBEs(); ++index) { - newRate += storm::utility::one<ValueType>() / state->getFailableBERate(index); - } - for (size_t index = 0; index < state->nrNotFailableBEs(); ++index) { - newRate += storm::utility::one<ValueType>() / state->getNotFailableBERate(index); + ValueType ExplicitDFTModelBuilderApprox<ValueType, StateType>::getLowerBound(DFTStatePointer const& state) const { + // Get the lower bound by considering the failure of all possible BEs + ValueType lowerBound = storm::utility::zero<ValueType>(); + for (size_t index = 0; index < state->nrFailableBEs(); ++index) { + lowerBound += state->getFailableBERate(index); + } + for (size_t index = 0; index < state->nrNotFailableBEs(); ++index) { + lowerBound += state->getNotFailableBERate(index); + } + return lowerBound; + } + + template<typename ValueType, typename StateType> + ValueType ExplicitDFTModelBuilderApprox<ValueType, StateType>::getUpperBound(DFTStatePointer const& state) const { + // Get the upper bound by considering the failure of all BEs + // The used formula for the rate is 1/( 1/a + 1/b + ...) + // TODO Matthias: improve by using closed formula for AND of all BEs + ValueType upperBound = storm::utility::zero<ValueType>(); + + // Get all possible rates + std::vector<ValueType> rates(state->nrFailableBEs() + state->nrNotFailableBEs()); + for (size_t index = 0; index < state->nrFailableBEs(); ++index) { + rates[index] = state->getFailableBERate(index); + } + for (size_t index = 0; index < state->nrNotFailableBEs(); ++index) { + rates[index + state->nrFailableBEs()] = state->getNotFailableBERate(index); + } + + // TODO Matthias: works only for <64 BEs! + for (size_t i = 1; i < 4 && i <= rates.size(); ++i) { + size_t permutation = smallestIntWithNBitsSet(static_cast<size_t>(i)); + ValueType sum = storm::utility::zero<ValueType>(); + do { + ValueType permResult = storm::utility::zero<ValueType>(); + for(size_t j = 0; j < rates.size(); ++j) { + if(permutation & (1 << j)) { + permResult += rates[j]; + } + } + permutation = nextBitPermutation(permutation); + STORM_LOG_ASSERT(!storm::utility::isZero(permResult), "PermResult is 0"); + sum += storm::utility::one<ValueType>() / permResult; + } while(permutation < (1 << rates.size()) && permutation != 0); + if (i % 2 == 0) { + upperBound -= sum; + } else { + upperBound += sum; } - newRate = storm::utility::one<ValueType>() / newRate; - matrixEntry->setValue(newRate); } + STORM_LOG_ASSERT(!storm::utility::isZero(upperBound), "UpperBound is 0"); + return storm::utility::one<ValueType>() / upperBound; } template<typename ValueType, typename StateType> diff --git a/src/builder/ExplicitDFTModelBuilderApprox.h b/src/builder/ExplicitDFTModelBuilderApprox.h index 1f779e44f..446bed6ad 100644 --- a/src/builder/ExplicitDFTModelBuilderApprox.h +++ b/src/builder/ExplicitDFTModelBuilderApprox.h @@ -28,7 +28,7 @@ namespace storm { using DFTStatePointer = std::shared_ptr<storm::storage::DFTState<ValueType>>; // TODO Matthias: make choosable - using ExplorationHeuristic = DFTExplorationHeuristicDepth<ValueType>; + using ExplorationHeuristic = DFTExplorationHeuristicProbability<ValueType>; using ExplorationHeuristicPointer = std::shared_ptr<ExplorationHeuristic>; @@ -217,18 +217,26 @@ namespace storm { void setMarkovian(bool markovian); /** - * Change matrix to reflect the lower approximation bound. + * Change matrix to reflect the lower or upper approximation bound. * - * @param matrix Matrix to change. The change are reflected here. + * @param matrix Matrix to change. The change are reflected here. + * @param lowerBound Flag indicating if the lower bound should be used. Otherwise the upper bound is used. */ - void changeMatrixLowerBound(storm::storage::SparseMatrix<ValueType> & matrix) const; + void changeMatrixBound(storm::storage::SparseMatrix<ValueType> & matrix, bool lowerBound) const; /*! - * Change matrix to reflect the upper approximation bound. + * Get lower bound approximation for state. * - * @param matrix Matrix to change. The change are reflected here. + * @return Lower bound approximation. */ - void changeMatrixUpperBound(storm::storage::SparseMatrix<ValueType> & matrix) const; + ValueType getLowerBound(DFTStatePointer const& state) const; + + /*! + * Get upper bound approximation for state. + * + * @return Upper bound approximation. + */ + ValueType getUpperBound(DFTStatePointer const& state) const; /*! * Compares the priority of two states. diff --git a/src/modelchecker/dft/DFTModelChecker.cpp b/src/modelchecker/dft/DFTModelChecker.cpp index f19943f2f..59d85f5c2 100644 --- a/src/modelchecker/dft/DFTModelChecker.cpp +++ b/src/modelchecker/dft/DFTModelChecker.cpp @@ -255,6 +255,7 @@ namespace storm { template<> bool DFTModelChecker<double>::isApproximationSufficient(double lowerBound, double upperBound, double approximationError) { + STORM_LOG_THROW(!std::isnan(lowerBound) && !std::isnan(upperBound), storm::exceptions::NotSupportedException, "Approximation does not work if result is NaN."); return upperBound - lowerBound <= approximationError * (lowerBound + upperBound) / 2; } diff --git a/src/storage/BucketPriorityQueue.cpp b/src/storage/BucketPriorityQueue.cpp index fe1ca95b5..eb174cce0 100644 --- a/src/storage/BucketPriorityQueue.cpp +++ b/src/storage/BucketPriorityQueue.cpp @@ -16,19 +16,19 @@ namespace storm { void BucketPriorityQueue<ValueType>::fix() { if (currentBucket < buckets.size() && nrUnsortedItems > 0) { // Fix current bucket - //std::make_heap(buckets[currentBucket].begin(), buckets[currentBucket].end(), compare); + std::make_heap(buckets[currentBucket].begin(), buckets[currentBucket].end(), compare); nrUnsortedItems = 0; } } template<typename ValueType> bool BucketPriorityQueue<ValueType>::empty() const { - return currentBucket == buckets.size(); + return currentBucket == buckets.size() && immediateBucket.empty(); } template<typename ValueType> std::size_t BucketPriorityQueue<ValueType>::size() const { - size_t size = 0; + size_t size = immediateBucket.size(); for (size_t i = currentBucket; currentBucket < buckets.size(); ++i) { size += buckets[i].size(); } @@ -37,6 +37,9 @@ namespace storm { template<typename ValueType> typename BucketPriorityQueue<ValueType>::HeuristicPointer const& BucketPriorityQueue<ValueType>::top() const { + if (!immediateBucket.empty()) { + return immediateBucket.back(); + } STORM_LOG_ASSERT(!empty(), "BucketPriorityQueue is empty"); STORM_LOG_ASSERT(nrUnsortedItems == 0, "First bucket is not sorted"); return buckets[currentBucket].front(); @@ -44,6 +47,10 @@ namespace storm { template<typename ValueType> void BucketPriorityQueue<ValueType>::push(HeuristicPointer const& item) { + if (item->isExpand()) { + immediateBucket.push_back(item); + return; + } size_t bucket = getBucket(item->getPriority()); if (bucket < currentBucket) { currentBucket = bucket; @@ -62,6 +69,7 @@ namespace storm { template<typename ValueType> void BucketPriorityQueue<ValueType>::update(HeuristicPointer const& item, double oldPriority) { + STORM_LOG_ASSERT(!item->isExpand(), "Item is marked for expansion"); size_t newBucket = getBucket(item->getPriority()); size_t oldBucket = getBucket(oldPriority); @@ -113,6 +121,10 @@ namespace storm { template<typename ValueType> void BucketPriorityQueue<ValueType>::pop() { + if (!immediateBucket.empty()) { + immediateBucket.pop_back(); + return; + } STORM_LOG_ASSERT(!empty(), "BucketPriorityQueue is empty"); STORM_LOG_ASSERT(nrUnsortedItems == 0, "First bucket is not sorted"); std::pop_heap(buckets[currentBucket].begin(), buckets[currentBucket].end(), compare); @@ -153,6 +165,11 @@ namespace storm { template<typename ValueType> void BucketPriorityQueue<ValueType>::print(std::ostream& out) const { out << "Bucket priority queue with size " << buckets.size() << ", lower value: " << lowerValue << " and step per bucket: " << stepPerBucket << std::endl; + out << "Immediate bucket: "; + for (HeuristicPointer heuristic : immediateBucket) { + out << heuristic->getId() << ", "; + } + out << std::endl; out << "Current bucket (" << currentBucket << ") has " << nrUnsortedItems << " unsorted items" << std::endl; for (size_t bucket = 0; bucket < buckets.size(); ++bucket) { if (!buckets[bucket].empty()) { diff --git a/src/storage/BucketPriorityQueue.h b/src/storage/BucketPriorityQueue.h index 3d3c2d35d..253b34e19 100644 --- a/src/storage/BucketPriorityQueue.h +++ b/src/storage/BucketPriorityQueue.h @@ -13,7 +13,7 @@ namespace storm { template<typename ValueType> class BucketPriorityQueue { - using HeuristicPointer = std::shared_ptr<storm::builder::DFTExplorationHeuristicDepth<ValueType>>; + using HeuristicPointer = std::shared_ptr<storm::builder::DFTExplorationHeuristicProbability<ValueType>>; public: explicit BucketPriorityQueue(size_t nrBuckets, double lowerValue, double stepPerBucket); @@ -43,6 +43,9 @@ namespace storm { // List of buckets std::vector<std::vector<HeuristicPointer>> buckets; + // Bucket containing all items which should be considered immediately + std::vector<HeuristicPointer> immediateBucket; + // Index of first bucket which contains items size_t currentBucket; diff --git a/src/storage/dft/DFTState.cpp b/src/storage/dft/DFTState.cpp index 369bed3f7..b9381598f 100644 --- a/src/storage/dft/DFTState.cpp +++ b/src/storage/dft/DFTState.cpp @@ -18,7 +18,9 @@ namespace storm { } for (auto elem : mDft.getBasicElements()) { - mCurrentlyNotFailableBE.push_back(elem->id()); + if (!storm::utility::isZero(elem->activeFailureRate())) { + mCurrentlyNotFailableBE.push_back(elem->id()); + } } // Initialize activation @@ -242,8 +244,10 @@ namespace storm { ValueType DFTState<ValueType>::getBERate(size_t id, bool considerPassive) const { STORM_LOG_ASSERT(mDft.isBasicElement(id), "Element is no BE."); if (considerPassive && mDft.hasRepresentant(id) && !isActive(mDft.getRepresentant(id))) { + STORM_LOG_ASSERT(!storm::utility::isZero(mDft.getBasicElement(id)->passiveFailureRate()), "Failure rate of BE " << mDft.getBasicElement(id)->id() << " is 0 in state " << mDft.getStateString(mStatus, mStateGenerationInfo, mId)); return mDft.getBasicElement(id)->passiveFailureRate(); } else { + STORM_LOG_ASSERT(!storm::utility::isZero(mDft.getBasicElement(id)->activeFailureRate()), "Failure rate of BE " << mDft.getBasicElement(id)->id() << " is 0 in state " << mDft.getStateString(mStatus, mStateGenerationInfo, mId)); return mDft.getBasicElement(id)->activeFailureRate(); } } @@ -257,7 +261,7 @@ namespace storm { template<typename ValueType> ValueType DFTState<ValueType>::getNotFailableBERate(size_t index) const { STORM_LOG_ASSERT(index < nrNotFailableBEs(), "Index invalid."); - STORM_LOG_ASSERT(storm::utility::isZero<ValueType>(mDft.getBasicElement(mCurrentlyNotFailableBE[index])->activeFailureRate()) || + STORM_LOG_ASSERT(isPseudoState() || storm::utility::isZero<ValueType>(mDft.getBasicElement(mCurrentlyNotFailableBE[index])->activeFailureRate()) || (mDft.hasRepresentant(mCurrentlyNotFailableBE[index]) && !isActive(mDft.getRepresentant(mCurrentlyNotFailableBE[index]))), "BE " << mCurrentlyNotFailableBE[index] << " can fail in state: " << mDft.getStateString(mStatus, mStateGenerationInfo, mId)); // Use active failure rate as passive failure rate is 0. return getBERate(mCurrentlyNotFailableBE[index], false); From a9f97bd210418cf8afb999e362af118d2c92fc30 Mon Sep 17 00:00:00 2001 From: Mavo <matthias.volk@rwth-aachen.de> Date: Tue, 25 Oct 2016 23:28:35 +0200 Subject: [PATCH 43/65] Set heuristic to probability Former-commit-id: a7d8fd77385ce805b2161b8d0b655240b9319f90 --- src/builder/ExplicitDFTModelBuilderApprox.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/builder/ExplicitDFTModelBuilderApprox.cpp b/src/builder/ExplicitDFTModelBuilderApprox.cpp index 0328cf055..6a6474ad8 100644 --- a/src/builder/ExplicitDFTModelBuilderApprox.cpp +++ b/src/builder/ExplicitDFTModelBuilderApprox.cpp @@ -97,8 +97,7 @@ namespace storm { approximationThreshold = iteration; break; case storm::builder::ApproximationHeuristic::PROBABILITY: - //approximationThreshold = std::pow(0.1, iteration) * approximationThreshold; - approximationThreshold = iteration;//10 * std::pow(2, iteration); + approximationThreshold = 10 * std::pow(2, iteration); break; } exploreStateSpace(approximationThreshold); @@ -234,7 +233,6 @@ namespace storm { void ExplicitDFTModelBuilderApprox<ValueType, StateType>::exploreStateSpace(double approximationThreshold) { size_t nrExpandedStates = 0; size_t nrSkippedStates = 0; - size_t fix = 0; // TODO Matthias: do not empty queue every time but break before while (!explorationQueue.empty()) { explorationQueue.fix(); @@ -324,7 +322,6 @@ namespace storm { double oldPriority = iter->second.second->getPriority(); if (iter->second.second->updateHeuristicValues(*currentExplorationHeuristic, stateProbabilityPair.second, choice.getTotalMass())) { // Update priority queue - ++fix; explorationQueue.update(iter->second.second, oldPriority); } } @@ -334,7 +331,6 @@ namespace storm { } } } // end exploration - std::cout << "Fixed queue " << fix << " times" << std::endl; STORM_LOG_INFO("Expanded " << nrExpandedStates << " states"); STORM_LOG_INFO("Skipped " << nrSkippedStates << " states"); From 8e159133da0617fd8c88e20196e83d6f37b29a21 Mon Sep 17 00:00:00 2001 From: Mavo <matthias.volk@rwth-aachen.de> Date: Wed, 26 Oct 2016 23:10:57 +0200 Subject: [PATCH 44/65] Compute lower/upper bounds only when needed Former-commit-id: 8f3af1ab100516000aba505fbe3620c5875d7f8f --- src/builder/DftExplorationHeuristic.cpp | 4 +-- src/builder/DftExplorationHeuristic.h | 12 ++++----- src/builder/ExplicitDFTModelBuilderApprox.cpp | 25 +++++++++---------- 3 files changed, 20 insertions(+), 21 deletions(-) diff --git a/src/builder/DftExplorationHeuristic.cpp b/src/builder/DftExplorationHeuristic.cpp index db26fc60d..40d00115f 100644 --- a/src/builder/DftExplorationHeuristic.cpp +++ b/src/builder/DftExplorationHeuristic.cpp @@ -8,12 +8,12 @@ namespace storm { namespace builder { template<typename ValueType> - DFTExplorationHeuristic<ValueType>::DFTExplorationHeuristic(size_t id) : id(id), expand(false), lowerBound(storm::utility::zero<ValueType>()), upperBound(storm::utility::infinity<ValueType>()), depth(0), probability(storm::utility::one<ValueType>()) { + DFTExplorationHeuristic<ValueType>::DFTExplorationHeuristic(size_t id) : id(id), expand(true), lowerBound(storm::utility::zero<ValueType>()), upperBound(storm::utility::infinity<ValueType>()), depth(0), probability(storm::utility::one<ValueType>()) { // Intentionally left empty } template<typename ValueType> - DFTExplorationHeuristic<ValueType>::DFTExplorationHeuristic(size_t id, DFTExplorationHeuristic const& predecessor, ValueType rate, ValueType exitRate, ValueType lowerBound, ValueType upperBound) : id(id), expand(false), lowerBound(lowerBound), upperBound(upperBound), depth(predecessor.depth + 1) { + DFTExplorationHeuristic<ValueType>::DFTExplorationHeuristic(size_t id, DFTExplorationHeuristic const& predecessor, ValueType rate, ValueType exitRate) : id(id), expand(false), lowerBound(storm::utility::zero<ValueType>()), upperBound(storm::utility::zero<ValueType>()), depth(predecessor.depth + 1) { STORM_LOG_ASSERT(storm::utility::zero<ValueType>() < exitRate, "Exit rate is 0"); probability = predecessor.probability * rate/exitRate; } diff --git a/src/builder/DftExplorationHeuristic.h b/src/builder/DftExplorationHeuristic.h index 12f178f25..8feb0fefa 100644 --- a/src/builder/DftExplorationHeuristic.h +++ b/src/builder/DftExplorationHeuristic.h @@ -22,7 +22,7 @@ namespace storm { public: DFTExplorationHeuristic(size_t id); - DFTExplorationHeuristic(size_t id, DFTExplorationHeuristic const& predecessor, ValueType rate, ValueType exitRate, ValueType lowerBound, ValueType upperBound); + DFTExplorationHeuristic(size_t id, DFTExplorationHeuristic const& predecessor, ValueType rate, ValueType exitRate); void setBounds(ValueType lowerBound, ValueType upperBound); @@ -76,7 +76,7 @@ namespace storm { // Intentionally left empty } - DFTExplorationHeuristicNone(size_t id, DFTExplorationHeuristicNone<ValueType> const& predecessor, ValueType rate, ValueType exitRate, ValueType lowerBound, ValueType upperBound) : DFTExplorationHeuristic<ValueType>(id, predecessor, rate, exitRate, lowerBound, upperBound) { + DFTExplorationHeuristicNone(size_t id, DFTExplorationHeuristicNone<ValueType> const& predecessor, ValueType rate, ValueType exitRate) : DFTExplorationHeuristic<ValueType>(id, predecessor, rate, exitRate) { // Intentionally left empty } @@ -85,7 +85,7 @@ namespace storm { } double getPriority() const override { - return this->id; + return 0; } bool isSkip(double approximationThreshold) const override { @@ -104,7 +104,7 @@ namespace storm { // Intentionally left empty } - DFTExplorationHeuristicDepth(size_t id, DFTExplorationHeuristicDepth<ValueType> const& predecessor, ValueType rate, ValueType exitRate, ValueType lowerBound, ValueType upperBound) : DFTExplorationHeuristic<ValueType>(id, predecessor, rate, exitRate, lowerBound, upperBound) { + DFTExplorationHeuristicDepth(size_t id, DFTExplorationHeuristicDepth<ValueType> const& predecessor, ValueType rate, ValueType exitRate) : DFTExplorationHeuristic<ValueType>(id, predecessor, rate, exitRate) { // Intentionally left empty } @@ -137,7 +137,7 @@ namespace storm { // Intentionally left empty } - DFTExplorationHeuristicProbability(size_t id, DFTExplorationHeuristicProbability<ValueType> const& predecessor, ValueType rate, ValueType exitRate, ValueType lowerBound, ValueType upperBound) : DFTExplorationHeuristic<ValueType>(id, predecessor, rate, exitRate, lowerBound, upperBound) { + DFTExplorationHeuristicProbability(size_t id, DFTExplorationHeuristicProbability<ValueType> const& predecessor, ValueType rate, ValueType exitRate) : DFTExplorationHeuristic<ValueType>(id, predecessor, rate, exitRate) { // Intentionally left empty } @@ -161,7 +161,7 @@ namespace storm { // Intentionally left empty } - DFTExplorationHeuristicBoundDifference(size_t id, DFTExplorationHeuristicBoundDifference<ValueType> const& predecessor, ValueType rate, ValueType exitRate, ValueType lowerBound, ValueType upperBound) : DFTExplorationHeuristic<ValueType>(id, predecessor, rate, exitRate, lowerBound, upperBound) { + DFTExplorationHeuristicBoundDifference(size_t id, DFTExplorationHeuristicBoundDifference<ValueType> const& predecessor, ValueType rate, ValueType exitRate) : DFTExplorationHeuristic<ValueType>(id, predecessor, rate, exitRate) { // Intentionally left empty } diff --git a/src/builder/ExplicitDFTModelBuilderApprox.cpp b/src/builder/ExplicitDFTModelBuilderApprox.cpp index 6a6474ad8..432636161 100644 --- a/src/builder/ExplicitDFTModelBuilderApprox.cpp +++ b/src/builder/ExplicitDFTModelBuilderApprox.cpp @@ -255,9 +255,6 @@ namespace storm { if (currentState->isPseudoState()) { // Create concrete state from pseudo state currentState->construct(); - ValueType lowerBound = getLowerBound(currentState); - ValueType upperBound = getUpperBound(currentState); - currentExplorationHeuristic->setBounds(lowerBound, upperBound); } STORM_LOG_ASSERT(!currentState->isPseudoState(), "State is pseudo state."); @@ -301,17 +298,8 @@ namespace storm { // Update heuristic values DFTStatePointer state = iter->second.first; if (!iter->second.second) { - ValueType lowerBound; - ValueType upperBound; - if (state->isPseudoState()) { - lowerBound = storm::utility::infinity<ValueType>(); - upperBound = storm::utility::infinity<ValueType>(); - } else { - lowerBound = getLowerBound(state); - upperBound = getUpperBound(state); - } // Initialize heuristic values - ExplorationHeuristicPointer heuristic = std::make_shared<ExplorationHeuristic>(stateProbabilityPair.first, *currentExplorationHeuristic, stateProbabilityPair.second, choice.getTotalMass(), lowerBound, upperBound); + ExplorationHeuristicPointer heuristic = std::make_shared<ExplorationHeuristic>(stateProbabilityPair.first, *currentExplorationHeuristic, stateProbabilityPair.second, choice.getTotalMass()); iter->second.second = heuristic; if (state->hasFailed(dft.getTopLevelIndex()) || state->isFailsafe(dft.getTopLevelIndex()) || state->nrFailableDependencies() > 0 || (state->nrFailableDependencies() == 0 && state->nrFailableBEs() == 0)) { // Do not skip absorbing state or if reached by dependencies @@ -453,6 +441,16 @@ namespace storm { auto matrixEntry = matrix.getRow(it->first, 0).begin(); STORM_LOG_ASSERT(matrixEntry->getColumn() == failedStateId, "Transition has wrong target state."); STORM_LOG_ASSERT(!it->second.first->isPseudoState(), "State is still pseudo state."); + + ExplorationHeuristicPointer heuristic = it->second.second; + if (storm::utility::isZero(heuristic->getUpperBound())) { + // Initialize bounds + ValueType lowerBound = getLowerBound(it->second.first); + ValueType upperBound = getUpperBound(it->second.first); + heuristic->setBounds(lowerBound, upperBound); + } + + // Change bound if (lowerBound) { matrixEntry->setValue(it->second.second->getLowerBound()); } else { @@ -489,6 +487,7 @@ namespace storm { for (size_t index = 0; index < state->nrNotFailableBEs(); ++index) { rates[index + state->nrFailableBEs()] = state->getNotFailableBERate(index); } + STORM_LOG_ASSERT(rates.size() > 0, "State is absorbing"); // TODO Matthias: works only for <64 BEs! for (size_t i = 1; i < 4 && i <= rates.size(); ++i) { From 386d4c7f056b3ce475c76f574bf0f56c49d8f75f Mon Sep 17 00:00:00 2001 From: Mavo <matthias.volk@rwth-aachen.de> Date: Wed, 26 Oct 2016 23:14:21 +0200 Subject: [PATCH 45/65] Use heuristic NONE to explore complete state space Former-commit-id: 25990b5ddaa23c00702940d75ac03879df11aeae --- src/builder/ExplicitDFTModelBuilderApprox.cpp | 10 +++++----- src/builder/ExplicitDFTModelBuilderApprox.h | 2 +- src/storage/BucketPriorityQueue.h | 2 +- src/storage/dft/DFTState.cpp | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/builder/ExplicitDFTModelBuilderApprox.cpp b/src/builder/ExplicitDFTModelBuilderApprox.cpp index 432636161..5a3e65585 100644 --- a/src/builder/ExplicitDFTModelBuilderApprox.cpp +++ b/src/builder/ExplicitDFTModelBuilderApprox.cpp @@ -33,12 +33,12 @@ namespace storm { matrixBuilder(!generator.isDeterministicModel()), stateStorage(((dft.stateVectorSize() / 64) + 1) * 64), // TODO Matthias: make choosable - //explorationQueue(dft.nrElements()+1, 0, 1) - explorationQueue(1001, 0, 0.001) + explorationQueue(dft.nrElements()+1, 0, 1) + //explorationQueue(1001, 0, 0.001) { // Intentionally left empty. // TODO Matthias: remove again - heuristic = storm::builder::ApproximationHeuristic::PROBABILITY; + heuristic = storm::builder::ApproximationHeuristic::NONE; } template<typename ValueType, typename StateType> @@ -266,8 +266,8 @@ namespace storm { // Try to explore the next state generator.load(currentState); - if (nrExpandedStates > approximationThreshold && !currentExplorationHeuristic->isExpand()) { - //if (currentExplorationHeuristic->isSkip(approximationThreshold)) { + //if (nrExpandedStates > approximationThreshold && !currentExplorationHeuristic->isExpand()) { + if (currentExplorationHeuristic->isSkip(approximationThreshold)) { // Skip the current state ++nrSkippedStates; STORM_LOG_TRACE("Skip expansion of state: " << dft.getStateString(currentState)); diff --git a/src/builder/ExplicitDFTModelBuilderApprox.h b/src/builder/ExplicitDFTModelBuilderApprox.h index 446bed6ad..c3ffe4467 100644 --- a/src/builder/ExplicitDFTModelBuilderApprox.h +++ b/src/builder/ExplicitDFTModelBuilderApprox.h @@ -28,7 +28,7 @@ namespace storm { using DFTStatePointer = std::shared_ptr<storm::storage::DFTState<ValueType>>; // TODO Matthias: make choosable - using ExplorationHeuristic = DFTExplorationHeuristicProbability<ValueType>; + using ExplorationHeuristic = DFTExplorationHeuristicNone<ValueType>; using ExplorationHeuristicPointer = std::shared_ptr<ExplorationHeuristic>; diff --git a/src/storage/BucketPriorityQueue.h b/src/storage/BucketPriorityQueue.h index 253b34e19..1aa18689d 100644 --- a/src/storage/BucketPriorityQueue.h +++ b/src/storage/BucketPriorityQueue.h @@ -13,7 +13,7 @@ namespace storm { template<typename ValueType> class BucketPriorityQueue { - using HeuristicPointer = std::shared_ptr<storm::builder::DFTExplorationHeuristicProbability<ValueType>>; + using HeuristicPointer = std::shared_ptr<storm::builder::DFTExplorationHeuristicNone<ValueType>>; public: explicit BucketPriorityQueue(size_t nrBuckets, double lowerValue, double stepPerBucket); diff --git a/src/storage/dft/DFTState.cpp b/src/storage/dft/DFTState.cpp index b9381598f..aaad9367f 100644 --- a/src/storage/dft/DFTState.cpp +++ b/src/storage/dft/DFTState.cpp @@ -323,7 +323,7 @@ namespace storm { mCurrentlyFailableBE.push_back(elem); // Remove from not failable BEs auto it = std::find(mCurrentlyNotFailableBE.begin(), mCurrentlyNotFailableBE.end(), elem); - STORM_LOG_ASSERT(it != mCurrentlyNotFailableBE.end(), "Element not found."); + STORM_LOG_ASSERT(it != mCurrentlyNotFailableBE.end(), "Element " << elem << " not found."); mCurrentlyNotFailableBE.erase(it); } } else if (mDft.getElement(elem)->isSpareGate() && !isActive(uses(elem))) { From d9b1285644ea8c5f97b194537ea259533cb9c526 Mon Sep 17 00:00:00 2001 From: Mavo <matthias.volk@rwth-aachen.de> Date: Thu, 27 Oct 2016 16:54:12 +0200 Subject: [PATCH 46/65] Alternative way of computing permutations (at the moment in parallel) Former-commit-id: f5886860bcf2854e1322fc780c113b530cec5094 --- src/builder/ExplicitDFTModelBuilderApprox.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/builder/ExplicitDFTModelBuilderApprox.cpp b/src/builder/ExplicitDFTModelBuilderApprox.cpp index 5a3e65585..3be21bfb4 100644 --- a/src/builder/ExplicitDFTModelBuilderApprox.cpp +++ b/src/builder/ExplicitDFTModelBuilderApprox.cpp @@ -510,6 +510,25 @@ namespace storm { upperBound += sum; } } + + // Compute upper bound with permutations of size <= 3 + ValueType upperBound2 = storm::utility::zero<ValueType>(); + for (size_t i1 = 0; i1 < rates.size(); ++i1) { + // + 1/a + ValueType sum = rates[i1]; + upperBound2 += storm::utility::one<ValueType>() / sum; + for (size_t i2 = 0; i2 < i1; ++i2) { + // - 1/(a+b) + ValueType sum2 = sum + rates[i2]; + upperBound2 -= storm::utility::one<ValueType>() / sum2; + for (size_t i3 = 0; i3 < i2; ++i3) { + // + 1/(a+b+c) + upperBound2 += storm::utility::one<ValueType>() / (sum2 + rates[i3]); + } + } + } + STORM_LOG_ASSERT(upperBound == upperBound2, "Upperbounds are different: " << upperBound << " and " << upperBound2); + STORM_LOG_ASSERT(!storm::utility::isZero(upperBound), "UpperBound is 0"); return storm::utility::one<ValueType>() / upperBound; } From 876b147aa8a5123ffa1d8b4e86b3935d110479aa Mon Sep 17 00:00:00 2001 From: Mavo <matthias.volk@rwth-aachen.de> Date: Thu, 27 Oct 2016 16:55:50 +0200 Subject: [PATCH 47/65] Fixed bug with iterator Former-commit-id: f7248a57c175d5ee47d5929fd739187c59507118 --- src/builder/ExplicitDFTModelBuilderApprox.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/builder/ExplicitDFTModelBuilderApprox.cpp b/src/builder/ExplicitDFTModelBuilderApprox.cpp index 5a3e65585..ac61efc0f 100644 --- a/src/builder/ExplicitDFTModelBuilderApprox.cpp +++ b/src/builder/ExplicitDFTModelBuilderApprox.cpp @@ -153,7 +153,8 @@ namespace storm { auto iterSkipped = skippedStates.begin(); size_t skippedBefore = 0; for (size_t i = 0; i < indexRemapping.size(); ++i) { - while (iterSkipped->first <= i) { + while (iterSkipped != skippedStates.end() && iterSkipped->first <= i) { + STORM_LOG_ASSERT(iterSkipped->first < indexRemapping.size(), "Skipped is too high."); ++skippedBefore; ++iterSkipped; } From c12bbe29045202b3caa2108e6a46d1b3d4aca65b Mon Sep 17 00:00:00 2001 From: Mavo <matthias.volk@rwth-aachen.de> Date: Thu, 27 Oct 2016 18:57:26 +0200 Subject: [PATCH 48/65] Disable old way of computing permutations as there is a bug for >31 elements Former-commit-id: ae7af404f3ab73f03463c6e5d1776bab229379b3 --- src/builder/ExplicitDFTModelBuilderApprox.cpp | 22 +++++++++---------- src/modelchecker/dft/DFTModelChecker.cpp | 2 ++ src/utility/bitoperations.h | 2 +- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/builder/ExplicitDFTModelBuilderApprox.cpp b/src/builder/ExplicitDFTModelBuilderApprox.cpp index 3be21bfb4..1991cec95 100644 --- a/src/builder/ExplicitDFTModelBuilderApprox.cpp +++ b/src/builder/ExplicitDFTModelBuilderApprox.cpp @@ -475,8 +475,7 @@ namespace storm { template<typename ValueType, typename StateType> ValueType ExplicitDFTModelBuilderApprox<ValueType, StateType>::getUpperBound(DFTStatePointer const& state) const { // Get the upper bound by considering the failure of all BEs - // The used formula for the rate is 1/( 1/a + 1/b + ...) - // TODO Matthias: improve by using closed formula for AND of all BEs + // The used formula for the rate is 1/( 1/a + 1/b + 1/c + ... - 1/(a+b) - 1/(a+c) - ... + 1/(a+b+c) + ... - ...) ValueType upperBound = storm::utility::zero<ValueType>(); // Get all possible rates @@ -490,44 +489,45 @@ namespace storm { STORM_LOG_ASSERT(rates.size() > 0, "State is absorbing"); // TODO Matthias: works only for <64 BEs! - for (size_t i = 1; i < 4 && i <= rates.size(); ++i) { + // WARNING: this code produces wrong results for more than 32 BEs + /*for (size_t i = 1; i < 4 && i <= rates.size(); ++i) { size_t permutation = smallestIntWithNBitsSet(static_cast<size_t>(i)); ValueType sum = storm::utility::zero<ValueType>(); do { ValueType permResult = storm::utility::zero<ValueType>(); for(size_t j = 0; j < rates.size(); ++j) { - if(permutation & (1 << j)) { + if(permutation & static_cast<size_t>(1 << static_cast<size_t>(j))) { + // WARNING: if the first bit is set, it also recognizes the 32nd bit as set + // TODO: fix permResult += rates[j]; } } permutation = nextBitPermutation(permutation); STORM_LOG_ASSERT(!storm::utility::isZero(permResult), "PermResult is 0"); sum += storm::utility::one<ValueType>() / permResult; - } while(permutation < (1 << rates.size()) && permutation != 0); + } while(permutation < (static_cast<size_t>(1) << rates.size()) && permutation != 0); if (i % 2 == 0) { upperBound -= sum; } else { upperBound += sum; } - } + }*/ // Compute upper bound with permutations of size <= 3 - ValueType upperBound2 = storm::utility::zero<ValueType>(); for (size_t i1 = 0; i1 < rates.size(); ++i1) { // + 1/a ValueType sum = rates[i1]; - upperBound2 += storm::utility::one<ValueType>() / sum; + upperBound += storm::utility::one<ValueType>() / sum; for (size_t i2 = 0; i2 < i1; ++i2) { // - 1/(a+b) ValueType sum2 = sum + rates[i2]; - upperBound2 -= storm::utility::one<ValueType>() / sum2; + upperBound -= storm::utility::one<ValueType>() / sum2; for (size_t i3 = 0; i3 < i2; ++i3) { // + 1/(a+b+c) - upperBound2 += storm::utility::one<ValueType>() / (sum2 + rates[i3]); + upperBound += storm::utility::one<ValueType>() / (sum2 + rates[i3]); } } } - STORM_LOG_ASSERT(upperBound == upperBound2, "Upperbounds are different: " << upperBound << " and " << upperBound2); STORM_LOG_ASSERT(!storm::utility::isZero(upperBound), "UpperBound is 0"); return storm::utility::one<ValueType>() / upperBound; diff --git a/src/modelchecker/dft/DFTModelChecker.cpp b/src/modelchecker/dft/DFTModelChecker.cpp index 59d85f5c2..ff2148982 100644 --- a/src/modelchecker/dft/DFTModelChecker.cpp +++ b/src/modelchecker/dft/DFTModelChecker.cpp @@ -100,6 +100,8 @@ namespace storm { ValueType result = storm::utility::zero<ValueType>(); 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_ASSERT(res.size() < 32, "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<size_t>(cK)); diff --git a/src/utility/bitoperations.h b/src/utility/bitoperations.h index bec6a6414..18b42a1cd 100644 --- a/src/utility/bitoperations.h +++ b/src/utility/bitoperations.h @@ -10,6 +10,6 @@ inline size_t smallestIntWithNBitsSet(size_t n) { inline size_t nextBitPermutation(size_t v) { if (v==0) return static_cast<size_t>(0); // From https://graphics.stanford.edu/~seander/bithacks.html#NextBitPermutation - unsigned int t = (v | (v - 1)) + 1; + size_t t = (v | (v - 1)) + 1; return t | ((((t & -t) / (v & -v)) >> 1) - 1); } From 6778a018ad9848faabd8702b50f8bd53ecbf143f Mon Sep 17 00:00:00 2001 From: Mavo <matthias.volk@rwth-aachen.de> Date: Thu, 27 Oct 2016 19:05:24 +0200 Subject: [PATCH 49/65] Use heuristic probability Former-commit-id: 747b34cfdc1bcabdb673155939946287923af3da --- src/builder/ExplicitDFTModelBuilderApprox.cpp | 10 +++++----- src/builder/ExplicitDFTModelBuilderApprox.h | 2 +- src/storage/BucketPriorityQueue.h | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/builder/ExplicitDFTModelBuilderApprox.cpp b/src/builder/ExplicitDFTModelBuilderApprox.cpp index 86a45235d..131c0389e 100644 --- a/src/builder/ExplicitDFTModelBuilderApprox.cpp +++ b/src/builder/ExplicitDFTModelBuilderApprox.cpp @@ -33,12 +33,12 @@ namespace storm { matrixBuilder(!generator.isDeterministicModel()), stateStorage(((dft.stateVectorSize() / 64) + 1) * 64), // TODO Matthias: make choosable - explorationQueue(dft.nrElements()+1, 0, 1) - //explorationQueue(1001, 0, 0.001) + //explorationQueue(dft.nrElements()+1, 0, 1) + explorationQueue(1001, 0, 0.001) { // Intentionally left empty. // TODO Matthias: remove again - heuristic = storm::builder::ApproximationHeuristic::NONE; + heuristic = storm::builder::ApproximationHeuristic::PROBABILITY; } template<typename ValueType, typename StateType> @@ -267,8 +267,8 @@ namespace storm { // Try to explore the next state generator.load(currentState); - //if (nrExpandedStates > approximationThreshold && !currentExplorationHeuristic->isExpand()) { - if (currentExplorationHeuristic->isSkip(approximationThreshold)) { + if (nrExpandedStates > approximationThreshold && !currentExplorationHeuristic->isExpand()) { + //if (currentExplorationHeuristic->isSkip(approximationThreshold)) { // Skip the current state ++nrSkippedStates; STORM_LOG_TRACE("Skip expansion of state: " << dft.getStateString(currentState)); diff --git a/src/builder/ExplicitDFTModelBuilderApprox.h b/src/builder/ExplicitDFTModelBuilderApprox.h index c3ffe4467..446bed6ad 100644 --- a/src/builder/ExplicitDFTModelBuilderApprox.h +++ b/src/builder/ExplicitDFTModelBuilderApprox.h @@ -28,7 +28,7 @@ namespace storm { using DFTStatePointer = std::shared_ptr<storm::storage::DFTState<ValueType>>; // TODO Matthias: make choosable - using ExplorationHeuristic = DFTExplorationHeuristicNone<ValueType>; + using ExplorationHeuristic = DFTExplorationHeuristicProbability<ValueType>; using ExplorationHeuristicPointer = std::shared_ptr<ExplorationHeuristic>; diff --git a/src/storage/BucketPriorityQueue.h b/src/storage/BucketPriorityQueue.h index 1aa18689d..253b34e19 100644 --- a/src/storage/BucketPriorityQueue.h +++ b/src/storage/BucketPriorityQueue.h @@ -13,7 +13,7 @@ namespace storm { template<typename ValueType> class BucketPriorityQueue { - using HeuristicPointer = std::shared_ptr<storm::builder::DFTExplorationHeuristicNone<ValueType>>; + using HeuristicPointer = std::shared_ptr<storm::builder::DFTExplorationHeuristicProbability<ValueType>>; public: explicit BucketPriorityQueue(size_t nrBuckets, double lowerValue, double stepPerBucket); From b669a3acef838a2841695eda8add73d85cc421d7 Mon Sep 17 00:00:00 2001 From: Mavo <matthias.volk@rwth-aachen.de> Date: Thu, 27 Oct 2016 21:42:11 +0200 Subject: [PATCH 50/65] Only sort bucket queue if more than 10% is unsorted Former-commit-id: 7ebd1e49c8a74198e4e36e5eeffdfc0ddb158020 --- src/storage/BucketPriorityQueue.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/storage/BucketPriorityQueue.cpp b/src/storage/BucketPriorityQueue.cpp index eb174cce0..c758be7ee 100644 --- a/src/storage/BucketPriorityQueue.cpp +++ b/src/storage/BucketPriorityQueue.cpp @@ -14,7 +14,7 @@ namespace storm { template<typename ValueType> void BucketPriorityQueue<ValueType>::fix() { - if (currentBucket < buckets.size() && nrUnsortedItems > 0) { + if (currentBucket < buckets.size() && nrUnsortedItems > buckets[currentBucket].size() / 10) { // Fix current bucket std::make_heap(buckets[currentBucket].begin(), buckets[currentBucket].end(), compare); nrUnsortedItems = 0; From a2c484bba43de66721bd575ad98002eef7fabf0b Mon Sep 17 00:00:00 2001 From: Mavo <matthias.volk@rwth-aachen.de> Date: Thu, 27 Oct 2016 21:43:02 +0200 Subject: [PATCH 51/65] Support for probability approximation without modularisation Former-commit-id: 921c8d310c9eab5b82300c908d3f039e9451500a --- src/modelchecker/dft/DFTModelChecker.cpp | 18 ++++++++++++------ src/modelchecker/dft/DFTModelChecker.h | 6 ++++-- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/modelchecker/dft/DFTModelChecker.cpp b/src/modelchecker/dft/DFTModelChecker.cpp index ff2148982..e49db7a37 100644 --- a/src/modelchecker/dft/DFTModelChecker.cpp +++ b/src/modelchecker/dft/DFTModelChecker.cpp @@ -158,6 +158,8 @@ namespace storm { storm::builder::ExplicitDFTModelBuilderApprox<ValueType> builder(dft, symmetries, enableDC); typename storm::builder::ExplicitDFTModelBuilderApprox<ValueType>::LabelOptions labeloptions; // TODO initialize this with the formula + bool probabilityFormula = formula->isProbabilityOperatorFormula(); + STORM_LOG_ASSERT((formula->isTimeOperatorFormula() && !probabilityFormula) || (!formula->isTimeOperatorFormula() && probabilityFormula), "Probability formula not initialized correctly"); size_t iteration = 0; do { // Iteratively build finer models @@ -170,7 +172,7 @@ namespace storm { // Build model for lower bound STORM_LOG_INFO("Getting model for lower bound..."); - model = builder.getModelApproximation(true); + model = builder.getModelApproximation(probabilityFormula ? false : true); // We only output the info from the lower bound as the info for the upper bound is the same STORM_LOG_INFO("No. states: " << model->getNumberOfStates()); STORM_LOG_INFO("No. transitions: " << model->getNumberOfTransitions()); @@ -185,7 +187,7 @@ namespace storm { // Build model for upper bound STORM_LOG_INFO("Getting model for upper bound..."); explorationStart = std::chrono::high_resolution_clock::now(); - model = builder.getModelApproximation(false); + model = builder.getModelApproximation(probabilityFormula ? true : false); explorationTime += std::chrono::high_resolution_clock::now() - explorationStart; // Check upper bound result = checkModel(model, formula); @@ -197,7 +199,7 @@ namespace storm { ++iteration; STORM_LOG_INFO("Result after iteration " << iteration << ": (" << std::setprecision(10) << approxResult.first << ", " << approxResult.second << ")"); STORM_LOG_THROW(!storm::utility::isInfinity<ValueType>(approxResult.first) && !storm::utility::isInfinity<ValueType>(approxResult.second), storm::exceptions::NotSupportedException, "Approximation does not work if result might be infinity."); - } while (!isApproximationSufficient(approxResult.first, approxResult.second, approximationError)); + } while (!isApproximationSufficient(approxResult.first, approxResult.second, approximationError, probabilityFormula)); STORM_LOG_INFO("Finished approximation after " << iteration << " iteration" << (iteration > 1 ? "s." : ".")); return approxResult; @@ -251,14 +253,18 @@ namespace storm { } template<typename ValueType> - bool DFTModelChecker<ValueType>::isApproximationSufficient(ValueType lowerBound, ValueType upperBound, double approximationError) { + bool DFTModelChecker<ValueType>::isApproximationSufficient(ValueType lowerBound, ValueType upperBound, double approximationError, bool relative) { STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Approximation works only for double."); } template<> - bool DFTModelChecker<double>::isApproximationSufficient(double lowerBound, double upperBound, double approximationError) { + bool DFTModelChecker<double>::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."); - return upperBound - lowerBound <= approximationError * (lowerBound + upperBound) / 2; + if (relative) { + return upperBound - lowerBound <= approximationError; + } else { + return upperBound - lowerBound <= approximationError * (lowerBound + upperBound) / 2; + } } template<typename ValueType> diff --git a/src/modelchecker/dft/DFTModelChecker.h b/src/modelchecker/dft/DFTModelChecker.h index 83df59bfa..b351ab67b 100644 --- a/src/modelchecker/dft/DFTModelChecker.h +++ b/src/modelchecker/dft/DFTModelChecker.h @@ -107,15 +107,17 @@ namespace storm { /*! * Checks if the computed approximation is sufficient, i.e. - * upperBound - lowerBound <= approximationError * mean(upperBound, lowerBound). + * upperBound - lowerBound <= approximationError * mean(lowerBound, upperBound). * * @param lowerBound The lower bound on the result. * @param upperBound The upper bound on the result. * @param approximationError The allowed error for approximating. + * @param relative Flag indicating if the error should be relative to 1 or + to the mean of lower and upper bound. * * @return True, if the approximation is sufficient. */ - bool isApproximationSufficient(ValueType lowerBound, ValueType upperBound, double approximationError); + bool isApproximationSufficient(ValueType lowerBound, ValueType upperBound, double approximationError, bool relative); }; } From 02c4195f31102da38c3c37615197145f26c669f8 Mon Sep 17 00:00:00 2001 From: Mavo <matthias.volk@rwth-aachen.de> Date: Fri, 28 Oct 2016 17:56:09 +0200 Subject: [PATCH 52/65] Better upper bound for independent subtrees Former-commit-id: 64f5a1ca603833ea1dafedb1078f17be7c049e20 --- src/builder/ExplicitDFTModelBuilderApprox.cpp | 115 +++++++++++++++--- src/builder/ExplicitDFTModelBuilderApprox.h | 13 ++ src/storage/BucketPriorityQueue.cpp | 2 - src/storage/dft/DFTState.cpp | 23 ++-- src/storage/dft/DFTState.h | 22 ++-- 5 files changed, 135 insertions(+), 40 deletions(-) diff --git a/src/builder/ExplicitDFTModelBuilderApprox.cpp b/src/builder/ExplicitDFTModelBuilderApprox.cpp index 131c0389e..7f3e8c09e 100644 --- a/src/builder/ExplicitDFTModelBuilderApprox.cpp +++ b/src/builder/ExplicitDFTModelBuilderApprox.cpp @@ -39,6 +39,61 @@ namespace storm { // Intentionally left empty. // TODO Matthias: remove again heuristic = storm::builder::ApproximationHeuristic::PROBABILITY; + + // Compute independent subtrees + if (dft.topLevelType() == storm::storage::DFTElementType::OR) { + // We only support this for approximation with top level element OR + for (auto const& child : dft.getGate(dft.getTopLevelIndex())->children()) { + // Consider all children of the top level gate + std::vector<size_t> isubdft; + if (child->nrParents() > 1 || child->hasOutgoingDependencies()) { + STORM_LOG_TRACE("child " << child->name() << "does not allow modularisation."); + isubdft.clear(); + } else if (dft.isGate(child->id())) { + isubdft = dft.getGate(child->id())->independentSubDft(false); + } else { + STORM_LOG_ASSERT(dft.isBasicElement(child->id()), "Child is no BE."); + if(dft.getBasicElement(child->id())->hasIngoingDependencies()) { + STORM_LOG_TRACE("child " << child->name() << "does not allow modularisation."); + isubdft.clear(); + } else { + isubdft = {child->id()}; + } + } + if(isubdft.empty()) { + subtreeBEs.clear(); + break; + } else { + // Add new independent subtree + std::vector<size_t> subtree; + for (size_t id : isubdft) { + if (dft.isBasicElement(id)) { + subtree.push_back(id); + } + } + subtreeBEs.push_back(subtree); + } + } + } + if (subtreeBEs.empty()) { + // Consider complete tree + std::vector<size_t> subtree; + for (size_t id = 0; id < dft.nrElements(); ++id) { + if (dft.isBasicElement(id)) { + subtree.push_back(id); + } + } + subtreeBEs.push_back(subtree); + } + + for (auto tree : subtreeBEs) { + std::stringstream stream; + stream << "Subtree: "; + for (size_t id : tree) { + stream << id << ", "; + } + STORM_LOG_TRACE(stream.str()); + } } template<typename ValueType, typename StateType> @@ -476,18 +531,42 @@ namespace storm { template<typename ValueType, typename StateType> ValueType ExplicitDFTModelBuilderApprox<ValueType, StateType>::getUpperBound(DFTStatePointer const& state) const { // Get the upper bound by considering the failure of all BEs - // The used formula for the rate is 1/( 1/a + 1/b + 1/c + ... - 1/(a+b) - 1/(a+c) - ... + 1/(a+b+c) + ... - ...) - ValueType upperBound = storm::utility::zero<ValueType>(); + ValueType upperBound = storm::utility::one<ValueType>(); + ValueType rateSum = storm::utility::zero<ValueType>(); + + // Compute for each independent subtree + for (std::vector<size_t> const& subtree : subtreeBEs) { + // Get all possible rates + std::vector<ValueType> rates; + for (size_t id : subtree) { + if (state->isOperational(id)) { + // Get BE rate + rates.push_back(state->getBERate(id, true)); + rateSum += rates.back(); + } + } - // Get all possible rates - std::vector<ValueType> rates(state->nrFailableBEs() + state->nrNotFailableBEs()); - for (size_t index = 0; index < state->nrFailableBEs(); ++index) { - rates[index] = state->getFailableBERate(index); + // We move backwards and start with swapping the last element to itself + // Then we do not need to swap back + for (auto it = rates.rbegin(); it != rates.rend(); ++it) { + // Compute AND MTTF of subtree without current rate and scale with current rate + std::iter_swap(it, rates.end() - 1); + upperBound += rates.back() * computeMTTFAnd(rates, rates.size() - 1); + } } - for (size_t index = 0; index < state->nrNotFailableBEs(); ++index) { - rates[index + state->nrFailableBEs()] = state->getNotFailableBERate(index); + + STORM_LOG_TRACE("Upper bound is " << (rateSum / upperBound) << " for state " << state->getId()); + STORM_LOG_ASSERT(!storm::utility::isZero(upperBound), "Upper bound is 0"); + STORM_LOG_ASSERT(!storm::utility::isZero(rateSum), "State is absorbing"); + return rateSum / upperBound; + } + + template<typename ValueType, typename StateType> + ValueType ExplicitDFTModelBuilderApprox<ValueType, StateType>::computeMTTFAnd(std::vector<ValueType> rates, size_t size) const { + ValueType result = storm::utility::zero<ValueType>(); + if (size == 0) { + return result; } - STORM_LOG_ASSERT(rates.size() > 0, "State is absorbing"); // TODO Matthias: works only for <64 BEs! // WARNING: this code produces wrong results for more than 32 BEs @@ -508,30 +587,30 @@ namespace storm { sum += storm::utility::one<ValueType>() / permResult; } while(permutation < (static_cast<size_t>(1) << rates.size()) && permutation != 0); if (i % 2 == 0) { - upperBound -= sum; + result -= sum; } else { - upperBound += sum; + result += sum; } }*/ - // Compute upper bound with permutations of size <= 3 - for (size_t i1 = 0; i1 < rates.size(); ++i1) { + // Compute result with permutations of size <= 3 + for (size_t i1 = 0; i1 < size; ++i1) { // + 1/a ValueType sum = rates[i1]; - upperBound += storm::utility::one<ValueType>() / sum; + result += storm::utility::one<ValueType>() / sum; for (size_t i2 = 0; i2 < i1; ++i2) { // - 1/(a+b) ValueType sum2 = sum + rates[i2]; - upperBound -= storm::utility::one<ValueType>() / sum2; + result -= storm::utility::one<ValueType>() / sum2; for (size_t i3 = 0; i3 < i2; ++i3) { // + 1/(a+b+c) - upperBound += storm::utility::one<ValueType>() / (sum2 + rates[i3]); + result += storm::utility::one<ValueType>() / (sum2 + rates[i3]); } } } - STORM_LOG_ASSERT(!storm::utility::isZero(upperBound), "UpperBound is 0"); - return storm::utility::one<ValueType>() / upperBound; + STORM_LOG_ASSERT(!storm::utility::isZero(result), "UpperBound is 0"); + return result; } template<typename ValueType, typename StateType> diff --git a/src/builder/ExplicitDFTModelBuilderApprox.h b/src/builder/ExplicitDFTModelBuilderApprox.h index 446bed6ad..8cc53a176 100644 --- a/src/builder/ExplicitDFTModelBuilderApprox.h +++ b/src/builder/ExplicitDFTModelBuilderApprox.h @@ -238,6 +238,16 @@ namespace storm { */ ValueType getUpperBound(DFTStatePointer const& state) const; + /*! + * Compute the MTTF of an AND gate via a closed formula. + * The used formula is 1/( 1/a + 1/b + 1/c + ... - 1/(a+b) - 1/(a+c) - ... + 1/(a+b+c) + ... - ...) + * + * @param rates List of rates of children of AND. + * @param size Only indices < size are considered in the vector. + * @return MTTF. + */ + ValueType computeMTTFAnd(std::vector<ValueType> rates, size_t size) const; + /*! * Compares the priority of two states. * @@ -314,6 +324,9 @@ namespace storm { // Notice that we need an ordered map here to easily iterate in increasing order over state ids. // TODO remove again std::map<StateType, std::pair<DFTStatePointer, ExplorationHeuristicPointer>> skippedStates; + + // List of independent subtrees and the BEs contained in them. + std::vector<std::vector<size_t>> subtreeBEs; }; } diff --git a/src/storage/BucketPriorityQueue.cpp b/src/storage/BucketPriorityQueue.cpp index c758be7ee..99ed19cd4 100644 --- a/src/storage/BucketPriorityQueue.cpp +++ b/src/storage/BucketPriorityQueue.cpp @@ -41,7 +41,6 @@ namespace storm { return immediateBucket.back(); } STORM_LOG_ASSERT(!empty(), "BucketPriorityQueue is empty"); - STORM_LOG_ASSERT(nrUnsortedItems == 0, "First bucket is not sorted"); return buckets[currentBucket].front(); } @@ -126,7 +125,6 @@ namespace storm { return; } STORM_LOG_ASSERT(!empty(), "BucketPriorityQueue is empty"); - STORM_LOG_ASSERT(nrUnsortedItems == 0, "First bucket is not sorted"); std::pop_heap(buckets[currentBucket].begin(), buckets[currentBucket].end(), compare); buckets[currentBucket].pop_back(); if (buckets[currentBucket].empty()) { diff --git a/src/storage/dft/DFTState.cpp b/src/storage/dft/DFTState.cpp index aaad9367f..64728cbd5 100644 --- a/src/storage/dft/DFTState.cpp +++ b/src/storage/dft/DFTState.cpp @@ -241,21 +241,26 @@ namespace storm { } template<typename ValueType> - ValueType DFTState<ValueType>::getBERate(size_t id, bool considerPassive) const { + ValueType DFTState<ValueType>::getBERate(size_t id, bool avoidCold) const { STORM_LOG_ASSERT(mDft.isBasicElement(id), "Element is no BE."); - if (considerPassive && mDft.hasRepresentant(id) && !isActive(mDft.getRepresentant(id))) { - STORM_LOG_ASSERT(!storm::utility::isZero(mDft.getBasicElement(id)->passiveFailureRate()), "Failure rate of BE " << mDft.getBasicElement(id)->id() << " is 0 in state " << mDft.getStateString(mStatus, mStateGenerationInfo, mId)); - return mDft.getBasicElement(id)->passiveFailureRate(); - } else { - STORM_LOG_ASSERT(!storm::utility::isZero(mDft.getBasicElement(id)->activeFailureRate()), "Failure rate of BE " << mDft.getBasicElement(id)->id() << " is 0 in state " << mDft.getStateString(mStatus, mStateGenerationInfo, mId)); - return mDft.getBasicElement(id)->activeFailureRate(); + + if (mDft.hasRepresentant(id) && !isActive(mDft.getRepresentant(id))) { + if (avoidCold && mDft.getBasicElement(id)->isColdBasicElement()) { + // Return active failure rate instead of 0. + return mDft.getBasicElement(id)->activeFailureRate(); + } else { + // Return passive failure rate + return mDft.getBasicElement(id)->passiveFailureRate(); + } } + // Return active failure rate + return mDft.getBasicElement(id)->activeFailureRate(); } template<typename ValueType> ValueType DFTState<ValueType>::getFailableBERate(size_t index) const { STORM_LOG_ASSERT(index < nrFailableBEs(), "Index invalid."); - return getBERate(mCurrentlyFailableBE[index], true); + return getBERate(mCurrentlyFailableBE[index], false); } template<typename ValueType> @@ -264,7 +269,7 @@ namespace storm { STORM_LOG_ASSERT(isPseudoState() || storm::utility::isZero<ValueType>(mDft.getBasicElement(mCurrentlyNotFailableBE[index])->activeFailureRate()) || (mDft.hasRepresentant(mCurrentlyNotFailableBE[index]) && !isActive(mDft.getRepresentant(mCurrentlyNotFailableBE[index]))), "BE " << mCurrentlyNotFailableBE[index] << " can fail in state: " << mDft.getStateString(mStatus, mStateGenerationInfo, mId)); // Use active failure rate as passive failure rate is 0. - return getBERate(mCurrentlyNotFailableBE[index], false); + return getBERate(mCurrentlyNotFailableBE[index], true); } template<typename ValueType> diff --git a/src/storage/dft/DFTState.h b/src/storage/dft/DFTState.h index 4ff36d1ab..0f8280591 100644 --- a/src/storage/dft/DFTState.h +++ b/src/storage/dft/DFTState.h @@ -208,6 +208,17 @@ namespace storm { */ ValueType getNotFailableBERate(size_t index) const; + /** + * Get the failure rate of the given BE. + * + * @param id Id of BE. + * @param avoidCold Flag indicating if a cold passive failure rate should be avoided by giving + * the active failure rate instead. + * + * @return Failure rate of the BE. + */ + ValueType getBERate(size_t id, bool avoidCold) const; + /** Get number of currently failable dependencies. * * @return Number of failable dependencies. @@ -302,17 +313,6 @@ namespace storm { private: void propagateActivation(size_t representativeId); - /** - * Get the failure rate of the given BE. - * - * @param id Id of BE. - * @param considerPassive Flag indicating if the passive failure rate should be returned if - * the BE currently is passive. - * - * @return Failure rate of the BE. - */ - ValueType getBERate(size_t id, bool considerPassive) const; - }; } From dae1a7eefe69c9ed7bdf7c34d7c43a437f63f105 Mon Sep 17 00:00:00 2001 From: Mavo <matthias.volk@rwth-aachen.de> Date: Fri, 28 Oct 2016 19:31:59 +0200 Subject: [PATCH 53/65] Do not use cold BEs in first step of approximation formula Former-commit-id: d204be9633c605c450933ee4659a9b8a5e1ac4cd --- src/builder/ExplicitDFTModelBuilderApprox.cpp | 32 ++++++----- src/storage/dft/DFTState.cpp | 53 +++---------------- src/storage/dft/DFTState.h | 25 +-------- 3 files changed, 29 insertions(+), 81 deletions(-) diff --git a/src/builder/ExplicitDFTModelBuilderApprox.cpp b/src/builder/ExplicitDFTModelBuilderApprox.cpp index 7f3e8c09e..28b943ba9 100644 --- a/src/builder/ExplicitDFTModelBuilderApprox.cpp +++ b/src/builder/ExplicitDFTModelBuilderApprox.cpp @@ -522,9 +522,6 @@ namespace storm { for (size_t index = 0; index < state->nrFailableBEs(); ++index) { lowerBound += state->getFailableBERate(index); } - for (size_t index = 0; index < state->nrNotFailableBEs(); ++index) { - lowerBound += state->getNotFailableBERate(index); - } return lowerBound; } @@ -538,20 +535,31 @@ namespace storm { for (std::vector<size_t> const& subtree : subtreeBEs) { // Get all possible rates std::vector<ValueType> rates; - for (size_t id : subtree) { + storm::storage::BitVector coldBEs(subtree.size(), false); + for (size_t i = 0; i < subtree.size(); ++i) { + size_t id = subtree[i]; if (state->isOperational(id)) { // Get BE rate - rates.push_back(state->getBERate(id, true)); - rateSum += rates.back(); + ValueType rate = state->getBERate(id); + if (storm::utility::isZero<ValueType>(rate)) { + // Get active failure rate for cold BE + rate = dft.getBasicElement(id)->activeFailureRate(); + // Mark BE as cold + coldBEs.set(i, true); + } + rates.push_back(rate); + rateSum += rate; } } - // We move backwards and start with swapping the last element to itself - // Then we do not need to swap back - for (auto it = rates.rbegin(); it != rates.rend(); ++it) { - // Compute AND MTTF of subtree without current rate and scale with current rate - std::iter_swap(it, rates.end() - 1); - upperBound += rates.back() * computeMTTFAnd(rates, rates.size() - 1); + for (size_t i = 0; i < rates.size(); ++i) { + // Cold BEs cannot fail in the first step + if (!coldBEs.get(i)) { + // Compute AND MTTF of subtree without current rate and scale with current rate + upperBound += rates.back() * computeMTTFAnd(rates, rates.size() - 1); + // Swap here to avoid swapping back + std::iter_swap(rates.begin() + i, rates.end() - 1); + } } } diff --git a/src/storage/dft/DFTState.cpp b/src/storage/dft/DFTState.cpp index 64728cbd5..cc0eaabec 100644 --- a/src/storage/dft/DFTState.cpp +++ b/src/storage/dft/DFTState.cpp @@ -17,23 +17,11 @@ namespace storm { this->setUses(spareId, elem->children()[0]->id()); } - for (auto elem : mDft.getBasicElements()) { - if (!storm::utility::isZero(elem->activeFailureRate())) { - mCurrentlyNotFailableBE.push_back(elem->id()); - } - } - // Initialize activation propagateActivation(mDft.getTopLevelIndex()); std::vector<size_t> alwaysActiveBEs = mDft.nonColdBEs(); mCurrentlyFailableBE.insert(mCurrentlyFailableBE.end(), alwaysActiveBEs.begin(), alwaysActiveBEs.end()); - // Remove always active BEs from currently not failable BEs - for (size_t id : alwaysActiveBEs) { - auto it = std::find(mCurrentlyNotFailableBE.begin(), mCurrentlyNotFailableBE.end(), id); - STORM_LOG_ASSERT(it != mCurrentlyNotFailableBE.end(), "Id not found."); - mCurrentlyNotFailableBE.erase(it); - } } template<typename ValueType> @@ -46,7 +34,6 @@ namespace storm { STORM_LOG_TRACE("Construct concrete state from pseudo state " << mDft.getStateString(mStatus, mStateGenerationInfo, mId)); // Clear information from pseudo state mCurrentlyFailableBE.clear(); - mCurrentlyNotFailableBE.clear(); mFailableDependencies.clear(); mUsedRepresentants.clear(); STORM_LOG_ASSERT(mPseudoState, "Only pseudo states can be constructed."); @@ -57,10 +44,6 @@ namespace storm { if ((!be->isColdBasicElement() && be->canFail()) || !mDft.hasRepresentant(index) || isActive(mDft.getRepresentant(index))) { mCurrentlyFailableBE.push_back(index); STORM_LOG_TRACE("Currently failable: " << mDft.getBasicElement(index)->toString()); - } else { - // BE currently is not failable - mCurrentlyNotFailableBE.push_back(index); - STORM_LOG_TRACE("Currently not failable: " << mDft.getBasicElement(index)->toString()); } } else if (mDft.getElement(index)->isSpareGate()) { // Initialize used representants @@ -204,11 +187,6 @@ namespace storm { auto it = std::find(mCurrentlyFailableBE.begin(), mCurrentlyFailableBE.end(), id); if (it != mCurrentlyFailableBE.end()) { mCurrentlyFailableBE.erase(it); - } else { - it = std::find(mCurrentlyNotFailableBE.begin(), mCurrentlyNotFailableBE.end(), id); - if (it != mCurrentlyNotFailableBE.end()) { - mCurrentlyNotFailableBE.erase(it); - } } } @@ -241,35 +219,22 @@ namespace storm { } template<typename ValueType> - ValueType DFTState<ValueType>::getBERate(size_t id, bool avoidCold) const { + ValueType DFTState<ValueType>::getBERate(size_t id) const { STORM_LOG_ASSERT(mDft.isBasicElement(id), "Element is no BE."); if (mDft.hasRepresentant(id) && !isActive(mDft.getRepresentant(id))) { - if (avoidCold && mDft.getBasicElement(id)->isColdBasicElement()) { - // Return active failure rate instead of 0. - return mDft.getBasicElement(id)->activeFailureRate(); - } else { - // Return passive failure rate - return mDft.getBasicElement(id)->passiveFailureRate(); - } + // Return passive failure rate + return mDft.getBasicElement(id)->passiveFailureRate(); + } else { + // Return active failure rate + return mDft.getBasicElement(id)->activeFailureRate(); } - // Return active failure rate - return mDft.getBasicElement(id)->activeFailureRate(); } template<typename ValueType> ValueType DFTState<ValueType>::getFailableBERate(size_t index) const { STORM_LOG_ASSERT(index < nrFailableBEs(), "Index invalid."); - return getBERate(mCurrentlyFailableBE[index], false); - } - - template<typename ValueType> - ValueType DFTState<ValueType>::getNotFailableBERate(size_t index) const { - STORM_LOG_ASSERT(index < nrNotFailableBEs(), "Index invalid."); - STORM_LOG_ASSERT(isPseudoState() || storm::utility::isZero<ValueType>(mDft.getBasicElement(mCurrentlyNotFailableBE[index])->activeFailureRate()) || - (mDft.hasRepresentant(mCurrentlyNotFailableBE[index]) && !isActive(mDft.getRepresentant(mCurrentlyNotFailableBE[index]))), "BE " << mCurrentlyNotFailableBE[index] << " can fail in state: " << mDft.getStateString(mStatus, mStateGenerationInfo, mId)); - // Use active failure rate as passive failure rate is 0. - return getBERate(mCurrentlyNotFailableBE[index], true); + return getBERate(mCurrentlyFailableBE[index]); } template<typename ValueType> @@ -326,10 +291,6 @@ namespace storm { if (be->isColdBasicElement() && be->canFail()) { // Add to failable BEs mCurrentlyFailableBE.push_back(elem); - // Remove from not failable BEs - auto it = std::find(mCurrentlyNotFailableBE.begin(), mCurrentlyNotFailableBE.end(), elem); - STORM_LOG_ASSERT(it != mCurrentlyNotFailableBE.end(), "Element " << elem << " not found."); - mCurrentlyNotFailableBE.erase(it); } } else if (mDft.getElement(elem)->isSpareGate() && !isActive(uses(elem))) { propagateActivation(uses(elem)); diff --git a/src/storage/dft/DFTState.h b/src/storage/dft/DFTState.h index 0f8280591..b077b58b0 100644 --- a/src/storage/dft/DFTState.h +++ b/src/storage/dft/DFTState.h @@ -27,7 +27,6 @@ namespace storm { storm::storage::BitVector mStatus; size_t mId; std::vector<size_t> mCurrentlyFailableBE; - std::vector<size_t> mCurrentlyNotFailableBE; std::vector<size_t> mFailableDependencies; std::vector<size_t> mUsedRepresentants; bool mPseudoState; @@ -181,15 +180,6 @@ namespace storm { return mCurrentlyFailableBE.size(); } - /** - * Get number of currently not failable BEs. These are cold BE which can fail in the future. - * - * @return Number of not failable BEs. - */ - size_t nrNotFailableBEs() const { - return mCurrentlyNotFailableBE.size(); - } - /** * Get the failure rate of the currently failable BE on the given index. * @@ -200,24 +190,13 @@ namespace storm { ValueType getFailableBERate(size_t index) const; /** - * Get the failure rate of the currently not failable BE on the given index. - * - * @param index Index of BE in list of currently not failable BEs. - * - * @return Failure rate of the BE. - */ - ValueType getNotFailableBERate(size_t index) const; - - /** - * Get the failure rate of the given BE. + * Get the current failure rate of the given BE. * * @param id Id of BE. - * @param avoidCold Flag indicating if a cold passive failure rate should be avoided by giving - * the active failure rate instead. * * @return Failure rate of the BE. */ - ValueType getBERate(size_t id, bool avoidCold) const; + ValueType getBERate(size_t id) const; /** Get number of currently failable dependencies. * From 64699a7badb5a7e56f6e8366bb85758b5175f46d Mon Sep 17 00:00:00 2001 From: Mavo <matthias.volk@rwth-aachen.de> Date: Fri, 28 Oct 2016 21:35:16 +0200 Subject: [PATCH 54/65] Several improvements Former-commit-id: 047ebde33bd77dcde7b0a2df5c5e384828f91684 --- src/builder/ExplicitDFTModelBuilderApprox.cpp | 12 +++--- src/modelchecker/dft/DFTModelChecker.cpp | 26 +++++++------ src/modelchecker/dft/DFTModelChecker.h | 1 + src/storage/BucketPriorityQueue.cpp | 37 +++++++++++++------ src/storage/BucketPriorityQueue.h | 25 ++++++++----- 5 files changed, 60 insertions(+), 41 deletions(-) diff --git a/src/builder/ExplicitDFTModelBuilderApprox.cpp b/src/builder/ExplicitDFTModelBuilderApprox.cpp index 28b943ba9..1d04e5210 100644 --- a/src/builder/ExplicitDFTModelBuilderApprox.cpp +++ b/src/builder/ExplicitDFTModelBuilderApprox.cpp @@ -34,7 +34,7 @@ namespace storm { stateStorage(((dft.stateVectorSize() / 64) + 1) * 64), // TODO Matthias: make choosable //explorationQueue(dft.nrElements()+1, 0, 1) - explorationQueue(1001, 0, 0.001) + explorationQueue(200, 0, 0.9) { // Intentionally left empty. // TODO Matthias: remove again @@ -291,9 +291,6 @@ namespace storm { size_t nrSkippedStates = 0; // TODO Matthias: do not empty queue every time but break before while (!explorationQueue.empty()) { - explorationQueue.fix(); - //explorationQueue.print(std::cout); - //printNotExplored(); // Get the first state in the queue ExplorationHeuristicPointer currentExplorationHeuristic = explorationQueue.popTop(); StateType currentId = currentExplorationHeuristic->getId(); @@ -602,17 +599,18 @@ namespace storm { }*/ // Compute result with permutations of size <= 3 + ValueType one = storm::utility::one<ValueType>(); for (size_t i1 = 0; i1 < size; ++i1) { // + 1/a ValueType sum = rates[i1]; - result += storm::utility::one<ValueType>() / sum; + result += one / sum; for (size_t i2 = 0; i2 < i1; ++i2) { // - 1/(a+b) ValueType sum2 = sum + rates[i2]; - result -= storm::utility::one<ValueType>() / sum2; + result -= one / sum2; for (size_t i3 = 0; i3 < i2; ++i3) { // + 1/(a+b+c) - result += storm::utility::one<ValueType>() / (sum2 + rates[i3]); + result += one / (sum2 + rates[i3]); } } } diff --git a/src/modelchecker/dft/DFTModelChecker.cpp b/src/modelchecker/dft/DFTModelChecker.cpp index e49db7a37..5eee17e3e 100644 --- a/src/modelchecker/dft/DFTModelChecker.cpp +++ b/src/modelchecker/dft/DFTModelChecker.cpp @@ -17,13 +17,13 @@ namespace storm { template<typename ValueType> void DFTModelChecker<ValueType>::check(storm::storage::DFT<ValueType> const& origDft, std::shared_ptr<const storm::logic::Formula> const& formula, bool symred, bool allowModularisation, bool enableDC, double approximationError) { // Initialize - this->buildingTime = std::chrono::duration<double>::zero(); this->explorationTime = std::chrono::duration<double>::zero(); + this->buildingTime = std::chrono::duration<double>::zero(); this->bisimulationTime = std::chrono::duration<double>::zero(); this->modelCheckingTime = std::chrono::duration<double>::zero(); this->totalTime = std::chrono::duration<double>::zero(); this->approximationError = approximationError; - std::chrono::high_resolution_clock::time_point totalStart = std::chrono::high_resolution_clock::now(); + totalStart = std::chrono::high_resolution_clock::now(); // Optimizing DFT storm::storage::DFT<ValueType> dft = origDft.optimize(); @@ -134,7 +134,7 @@ namespace storm { template<typename ValueType> typename DFTModelChecker<ValueType>::dft_result DFTModelChecker<ValueType>::checkDFT(storm::storage::DFT<ValueType> const& dft, std::shared_ptr<const storm::logic::Formula> const& formula, bool symred, bool enableDC, double approximationError) { - std::chrono::high_resolution_clock::time_point buildingStart = std::chrono::high_resolution_clock::now(); + std::chrono::high_resolution_clock::time_point checkingStart = std::chrono::high_resolution_clock::now(); // Find symmetries std::map<size_t, std::vector<std::vector<size_t>>> emptySymmetry; @@ -145,15 +145,12 @@ namespace storm { STORM_LOG_INFO("Found " << symmetries.groups.size() << " symmetries."); STORM_LOG_TRACE("Symmetries: " << std::endl << symmetries); } - std::chrono::high_resolution_clock::time_point buildingEnd = std::chrono::high_resolution_clock::now(); - buildingTime += buildingEnd - buildingStart; if (approximationError > 0.0) { // Comparator for checking the error of the approximation storm::utility::ConstantsComparator<ValueType> comparator; // Build approximate Markov Automata for lower and upper bound approximation_result approxResult = std::make_pair(storm::utility::zero<ValueType>(), storm::utility::zero<ValueType>()); - std::chrono::high_resolution_clock::time_point explorationStart; std::shared_ptr<storm::models::sparse::Model<ValueType>> model; storm::builder::ExplicitDFTModelBuilderApprox<ValueType> builder(dft, symmetries, enableDC); typename storm::builder::ExplicitDFTModelBuilderApprox<ValueType>::LabelOptions labeloptions; // TODO initialize this with the formula @@ -163,10 +160,12 @@ namespace storm { size_t iteration = 0; do { // Iteratively build finer models - explorationStart = std::chrono::high_resolution_clock::now(); + std::chrono::high_resolution_clock::time_point explorationStart = std::chrono::high_resolution_clock::now(); STORM_LOG_INFO("Building model..."); // TODO Matthias refine model using existing model and MC results builder.buildModel(labeloptions, iteration, approximationError); + std::chrono::high_resolution_clock::time_point explorationEnd = std::chrono::high_resolution_clock::now(); + explorationTime = explorationEnd - explorationStart; // TODO Matthias: possible to do bisimulation on approximated model and not on concrete one? @@ -176,7 +175,8 @@ namespace storm { // We only output the info from the lower bound as the info for the upper bound is the same STORM_LOG_INFO("No. states: " << model->getNumberOfStates()); STORM_LOG_INFO("No. transitions: " << model->getNumberOfTransitions()); - explorationTime += std::chrono::high_resolution_clock::now() - explorationStart; + buildingTime += std::chrono::high_resolution_clock::now() - explorationEnd; + // Check lower bound std::unique_ptr<storm::modelchecker::CheckResult> result = checkModel(model, formula); result->filter(storm::modelchecker::ExplicitQualitativeCheckResult(model->getInitialStates())); @@ -186,9 +186,9 @@ namespace storm { // Build model for upper bound STORM_LOG_INFO("Getting model for upper bound..."); - explorationStart = std::chrono::high_resolution_clock::now(); + explorationEnd = std::chrono::high_resolution_clock::now(); model = builder.getModelApproximation(probabilityFormula ? true : false); - explorationTime += std::chrono::high_resolution_clock::now() - explorationStart; + buildingTime += std::chrono::high_resolution_clock::now() - explorationEnd; // Check upper bound result = checkModel(model, formula); result->filter(storm::modelchecker::ExplicitQualitativeCheckResult(model->getInitialStates())); @@ -198,6 +198,8 @@ namespace storm { ++iteration; STORM_LOG_INFO("Result after iteration " << iteration << ": (" << std::setprecision(10) << approxResult.first << ", " << approxResult.second << ")"); + totalTime = std::chrono::high_resolution_clock::now() - totalStart; + printTimings(); STORM_LOG_THROW(!storm::utility::isInfinity<ValueType>(approxResult.first) && !storm::utility::isInfinity<ValueType>(approxResult.second), storm::exceptions::NotSupportedException, "Approximation does not work if result might be infinity."); } while (!isApproximationSufficient(approxResult.first, approxResult.second, approximationError, probabilityFormula)); @@ -221,7 +223,7 @@ namespace storm { //model->printModelInformationToStream(std::cout); STORM_LOG_INFO("No. states (Explored): " << model->getNumberOfStates()); STORM_LOG_INFO("No. transitions (Explored): " << model->getNumberOfTransitions()); - explorationTime += std::chrono::high_resolution_clock::now() - buildingEnd; + explorationTime += std::chrono::high_resolution_clock::now() - checkingStart; // Model checking std::unique_ptr<storm::modelchecker::CheckResult> result = checkModel(model, formula); @@ -270,8 +272,8 @@ namespace storm { template<typename ValueType> void DFTModelChecker<ValueType>::printTimings(std::ostream& os) { os << "Times:" << std::endl; - os << "Building:\t" << buildingTime.count() << std::endl; os << "Exploration:\t" << explorationTime.count() << std::endl; + os << "Building:\t" << buildingTime.count() << std::endl; os << "Bisimulation:\t" << bisimulationTime.count() << std::endl; os << "Modelchecking:\t" << modelCheckingTime.count() << std::endl; os << "Total:\t\t" << totalTime.count() << std::endl; diff --git a/src/modelchecker/dft/DFTModelChecker.h b/src/modelchecker/dft/DFTModelChecker.h index b351ab67b..543bf97fd 100644 --- a/src/modelchecker/dft/DFTModelChecker.h +++ b/src/modelchecker/dft/DFTModelChecker.h @@ -61,6 +61,7 @@ namespace storm { std::chrono::duration<double> bisimulationTime = std::chrono::duration<double>::zero(); std::chrono::duration<double> modelCheckingTime = std::chrono::duration<double>::zero(); std::chrono::duration<double> totalTime = std::chrono::duration<double>::zero(); + std::chrono::high_resolution_clock::time_point totalStart; // Model checking result dft_result checkResult; diff --git a/src/storage/BucketPriorityQueue.cpp b/src/storage/BucketPriorityQueue.cpp index 99ed19cd4..224b14cf6 100644 --- a/src/storage/BucketPriorityQueue.cpp +++ b/src/storage/BucketPriorityQueue.cpp @@ -2,11 +2,13 @@ #include "src/utility/macros.h" #include "src/adapters/CarlAdapter.h" +#include <cmath> + namespace storm { namespace storage { template<typename ValueType> - BucketPriorityQueue<ValueType>::BucketPriorityQueue(size_t nrBuckets, double lowerValue, double stepPerBucket) : buckets(nrBuckets), currentBucket(nrBuckets), lowerValue(lowerValue), stepPerBucket(stepPerBucket), nrUnsortedItems(0) { + BucketPriorityQueue<ValueType>::BucketPriorityQueue(size_t nrBuckets, double lowerValue, double ratio) : lowerValue(lowerValue), logBase(std::log(ratio)), nrBuckets(nrBuckets), nrUnsortedItems(0), buckets(nrBuckets), currentBucket(nrBuckets) { compare = ([this](HeuristicPointer a, HeuristicPointer b) { return *a < *b; }); @@ -14,7 +16,7 @@ namespace storm { template<typename ValueType> void BucketPriorityQueue<ValueType>::fix() { - if (currentBucket < buckets.size() && nrUnsortedItems > buckets[currentBucket].size() / 10) { + if (currentBucket < nrBuckets && nrUnsortedItems > buckets[currentBucket].size() / 10) { // Fix current bucket std::make_heap(buckets[currentBucket].begin(), buckets[currentBucket].end(), compare); nrUnsortedItems = 0; @@ -23,13 +25,13 @@ namespace storm { template<typename ValueType> bool BucketPriorityQueue<ValueType>::empty() const { - return currentBucket == buckets.size() && immediateBucket.empty(); + return currentBucket == nrBuckets && immediateBucket.empty(); } template<typename ValueType> std::size_t BucketPriorityQueue<ValueType>::size() const { size_t size = immediateBucket.size(); - for (size_t i = currentBucket; currentBucket < buckets.size(); ++i) { + for (size_t i = currentBucket; currentBucket < nrBuckets; ++i) { size += buckets[i].size(); } return size; @@ -129,7 +131,7 @@ namespace storm { buckets[currentBucket].pop_back(); if (buckets[currentBucket].empty()) { // Find next bucket with elements - for ( ; currentBucket < buckets.size(); ++currentBucket) { + for ( ; currentBucket < nrBuckets; ++currentBucket) { if (!buckets[currentBucket].empty()) { nrUnsortedItems = buckets[currentBucket].size(); if (AUTOSORT) { @@ -151,18 +153,21 @@ namespace storm { template<typename ValueType> size_t BucketPriorityQueue<ValueType>::getBucket(double priority) const { STORM_LOG_ASSERT(priority >= lowerValue, "Priority " << priority << " is too low"); - size_t newBucket = (priority - lowerValue) / stepPerBucket; - if (HIGHER) { - newBucket = buckets.size()-1 - newBucket; + size_t newBucket = std::log(priority - lowerValue) / logBase; + if (newBucket >= nrBuckets) { + newBucket = nrBuckets - 1; + } + if (!HIGHER) { + newBucket = nrBuckets-1 - newBucket; } - //std::cout << "get Bucket: " << priority << ", " << newBucket << ", " << ((priority - lowerValue) / stepPerBucket) << std::endl; - STORM_LOG_ASSERT(newBucket < buckets.size(), "Priority " << priority << " is too high"); + //std::cout << "get Bucket: " << priority << ", " << newBucket << std::endl; + STORM_LOG_ASSERT(newBucket < nrBuckets, "Priority " << priority << " is too high"); return newBucket; } template<typename ValueType> void BucketPriorityQueue<ValueType>::print(std::ostream& out) const { - out << "Bucket priority queue with size " << buckets.size() << ", lower value: " << lowerValue << " and step per bucket: " << stepPerBucket << std::endl; + out << "Bucket priority queue with size " << buckets.size() << ", lower value: " << lowerValue << " and logBase: " << logBase << std::endl; out << "Immediate bucket: "; for (HeuristicPointer heuristic : immediateBucket) { out << heuristic->getId() << ", "; @@ -171,7 +176,7 @@ namespace storm { out << "Current bucket (" << currentBucket << ") has " << nrUnsortedItems << " unsorted items" << std::endl; for (size_t bucket = 0; bucket < buckets.size(); ++bucket) { if (!buckets[bucket].empty()) { - out << "Bucket " << bucket << " (" << (HIGHER ? buckets.size() -1 - bucket * stepPerBucket : bucket * stepPerBucket) << "):" << std::endl; + out << "Bucket " << bucket << ":" << std::endl; for (HeuristicPointer heuristic : buckets[bucket]) { out << "\t" << heuristic->getId() << ": " << heuristic->getPriority() << std::endl; } @@ -179,6 +184,14 @@ namespace storm { } } + template<typename ValueType> + void BucketPriorityQueue<ValueType>::printSizes(std::ostream& out) const { + out << "Bucket sizes: " << immediateBucket.size() << " | "; + for (size_t bucket = 0; bucket < buckets.size(); ++bucket) { + out << buckets[bucket].size() << " "; + } + std::cout << std::endl; + } // Template instantiations template class BucketPriorityQueue<double>; diff --git a/src/storage/BucketPriorityQueue.h b/src/storage/BucketPriorityQueue.h index 253b34e19..1719c7124 100644 --- a/src/storage/BucketPriorityQueue.h +++ b/src/storage/BucketPriorityQueue.h @@ -16,7 +16,7 @@ namespace storm { using HeuristicPointer = std::shared_ptr<storm::builder::DFTExplorationHeuristicProbability<ValueType>>; public: - explicit BucketPriorityQueue(size_t nrBuckets, double lowerValue, double stepPerBucket); + explicit BucketPriorityQueue(size_t nrBuckets, double lowerValue, double ratio); void fix(); @@ -36,10 +36,24 @@ namespace storm { void print(std::ostream& out) const; + void printSizes(std::ostream& out) const; + private: size_t getBucket(double priority) const; + const double lowerValue; + + const bool HIGHER = true; + + const bool AUTOSORT = false; + + const double logBase; + + const size_t nrBuckets; + + size_t nrUnsortedItems; + // List of buckets std::vector<std::vector<HeuristicPointer>> buckets; @@ -51,15 +65,6 @@ namespace storm { std::function<bool(HeuristicPointer, HeuristicPointer)> compare; - double lowerValue; - - double stepPerBucket; - - size_t nrUnsortedItems; - - const bool HIGHER = true; - - const bool AUTOSORT = false; }; } From d95bb71f75a4febcd93abf7b075261671fe070a5 Mon Sep 17 00:00:00 2001 From: Mavo <matthias.volk@rwth-aachen.de> Date: Sat, 29 Oct 2016 00:02:20 +0200 Subject: [PATCH 55/65] Tried to gain more performance Former-commit-id: 9af2ab7ce0cc6fc311bd5f45c8b2ea79f29c989c --- src/builder/ExplicitDFTModelBuilderApprox.cpp | 19 ++++++++++++++++--- src/builder/ExplicitDFTModelBuilderApprox.h | 2 +- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/builder/ExplicitDFTModelBuilderApprox.cpp b/src/builder/ExplicitDFTModelBuilderApprox.cpp index 1d04e5210..93a7b282e 100644 --- a/src/builder/ExplicitDFTModelBuilderApprox.cpp +++ b/src/builder/ExplicitDFTModelBuilderApprox.cpp @@ -549,11 +549,24 @@ namespace storm { } } + STORM_LOG_ASSERT(rates.size() > 0, "No rates failable"); + + // Sort rates + std::sort(rates.begin(), rates.end()); + std::vector<size_t> rateCount(rates.size(), 0); + size_t lastIndex = 0; + for (size_t i = 0; i < rates.size(); ++i) { + if (rates[lastIndex] != rates[i]) { + lastIndex = i; + } + ++rateCount[lastIndex]; + } + for (size_t i = 0; i < rates.size(); ++i) { // Cold BEs cannot fail in the first step - if (!coldBEs.get(i)) { + if (!coldBEs.get(i) && rateCount[i] > 0) { // Compute AND MTTF of subtree without current rate and scale with current rate - upperBound += rates.back() * computeMTTFAnd(rates, rates.size() - 1); + upperBound += rates.back() * rateCount[i] * computeMTTFAnd(rates, rates.size() - 1); // Swap here to avoid swapping back std::iter_swap(rates.begin() + i, rates.end() - 1); } @@ -567,7 +580,7 @@ namespace storm { } template<typename ValueType, typename StateType> - ValueType ExplicitDFTModelBuilderApprox<ValueType, StateType>::computeMTTFAnd(std::vector<ValueType> rates, size_t size) const { + ValueType ExplicitDFTModelBuilderApprox<ValueType, StateType>::computeMTTFAnd(std::vector<ValueType> const& rates, size_t size) const { ValueType result = storm::utility::zero<ValueType>(); if (size == 0) { return result; diff --git a/src/builder/ExplicitDFTModelBuilderApprox.h b/src/builder/ExplicitDFTModelBuilderApprox.h index 8cc53a176..933cd3277 100644 --- a/src/builder/ExplicitDFTModelBuilderApprox.h +++ b/src/builder/ExplicitDFTModelBuilderApprox.h @@ -246,7 +246,7 @@ namespace storm { * @param size Only indices < size are considered in the vector. * @return MTTF. */ - ValueType computeMTTFAnd(std::vector<ValueType> rates, size_t size) const; + ValueType computeMTTFAnd(std::vector<ValueType> const& rates, size_t size) const; /*! * Compares the priority of two states. From 464a497093f821c37b5bb106afbc45c55cc65ae7 Mon Sep 17 00:00:00 2001 From: sjunges <sebastian.junges@rwth-aachen.de> Date: Sat, 29 Oct 2016 21:47:53 +0200 Subject: [PATCH 56/65] smt checker v1, and better error messages Former-commit-id: ab7391e85db4b7e94d9a6dfb2dfa383d570ff8c0 --- DFTASFChecker.cpp | 9 + DFTASFChecker.hpp | 14 + src/modelchecker/dft/DFTASFChecker.cpp | 413 +++++++++++++++++++++++++ src/modelchecker/dft/DFTASFChecker.h | 61 ++++ src/storage/dft/DFTBuilder.cpp | 2 +- src/storage/dft/DFTState.cpp | 2 +- src/storm-dyftee.cpp | 13 +- 7 files changed, 507 insertions(+), 7 deletions(-) create mode 100644 DFTASFChecker.cpp create mode 100644 DFTASFChecker.hpp create mode 100644 src/modelchecker/dft/DFTASFChecker.cpp create mode 100644 src/modelchecker/dft/DFTASFChecker.h diff --git a/DFTASFChecker.cpp b/DFTASFChecker.cpp new file mode 100644 index 000000000..de7a59428 --- /dev/null +++ b/DFTASFChecker.cpp @@ -0,0 +1,9 @@ +// +// DFTASFChecker.cpp +// storm +// +// Created by Sebastian Junges on 27/10/16. +// +// + +#include "DFTASFChecker.hpp" diff --git a/DFTASFChecker.hpp b/DFTASFChecker.hpp new file mode 100644 index 000000000..7920f026a --- /dev/null +++ b/DFTASFChecker.hpp @@ -0,0 +1,14 @@ +// +// DFTASFChecker.hpp +// storm +// +// Created by Sebastian Junges on 27/10/16. +// +// + +#ifndef DFTASFChecker_hpp +#define DFTASFChecker_hpp + +#include <stdio.h> + +#endif /* DFTASFChecker_hpp */ diff --git a/src/modelchecker/dft/DFTASFChecker.cpp b/src/modelchecker/dft/DFTASFChecker.cpp new file mode 100644 index 000000000..ac28ba5cf --- /dev/null +++ b/src/modelchecker/dft/DFTASFChecker.cpp @@ -0,0 +1,413 @@ +#include "DFTASFChecker.h" +#include <string> + +namespace storm { + + namespace modelchecker { + + /* + * Variable[VarIndex] is the maximum of the others + */ + class IsMaximum : public DFTConstraint { + public: + IsMaximum(uint64_t varIndex, std::vector<uint64_t> const& varIndices) : varIndex(varIndex), varIndices(varIndices) { + + } + + virtual ~IsMaximum() { + + } + + std::string toSmtlib2(std::vector<std::string> const& varNames) const override { + std::stringstream sstr; + sstr << "(and "; + // assert it is largereq than all values. + for (auto const& ovi : varIndices) { + sstr << "(>= " << varNames.at(varIndex) << " " << varNames.at(ovi) << ") "; + } + // assert it is one of the values. + sstr << "(or "; + for (auto const& ovi : varIndices) { + sstr << "(= " << varNames.at(varIndex) << " " << varNames.at(ovi) << ") "; + } + sstr << ")"; // end of the or + sstr << ")"; // end outer and. + return sstr.str(); + + } + + + private: + uint64_t varIndex; + std::vector<uint64_t> varIndices; + }; + /* + * First is the minimum of the others + */ + class IsMinimum : public DFTConstraint { + public: + IsMinimum(uint64_t varIndex, std::vector<uint64_t> const& varIndices) : varIndex(varIndex), varIndices(varIndices) { + + } + + + virtual ~IsMinimum() { + + } + + std::string toSmtlib2(std::vector<std::string> const& varNames) const override { + std::stringstream sstr; + sstr << "(and "; + // assert it is smallereq than all values. + for (auto const& ovi : varIndices) { + sstr << "(<= " << varNames.at(varIndex) << " " << varNames.at(ovi) << ") "; + } + // assert it is one of the values. + sstr << "(or "; + for (auto const& ovi : varIndices) { + sstr << "(= " << varNames.at(varIndex) << " " << varNames.at(ovi) << ") "; + } + sstr << ")"; // end of the or + sstr << ")"; // end outer and. + return sstr.str(); + + } + + + private: + uint64_t varIndex; + std::vector<uint64_t> varIndices; + }; + + class BetweenValues : public DFTConstraint { + public: + BetweenValues(uint64_t varIndex, uint64_t lower, uint64_t upper) : varIndex(varIndex), upperBound(upper) , lowerBound(lower) { + + } + virtual ~BetweenValues() { + + } + + std::string toSmtlib2(std::vector<std::string> const& varNames) const override { + std::stringstream sstr; + sstr << "(and "; + sstr << "(>= " << varNames.at(varIndex) << " " << lowerBound << ")"; + sstr << "(<= " << varNames.at(varIndex) << " " << upperBound << ")"; + sstr << ")"; + return sstr.str(); + } + + private: + uint64_t varIndex; + uint64_t upperBound; + uint64_t lowerBound; + }; + + class And : public DFTConstraint { + public: + And(std::vector<std::shared_ptr<DFTConstraint>> const& constraints) : constraints(constraints) {} + + virtual ~And() { + + } + + std::string toSmtlib2(std::vector<std::string> const& varNames) const override { + std::stringstream sstr; + sstr << "(and"; + for(auto const& c : constraints) { + sstr << " " << c->toSmtlib2(varNames); + } + sstr << ")"; + return sstr.str(); + } + private: + std::vector<std::shared_ptr<DFTConstraint>> constraints; + + }; + + class Iff : public DFTConstraint { + public: + Iff(std::shared_ptr<DFTConstraint> l, std::shared_ptr<DFTConstraint> r) : lhs(l), rhs(r) { + + } + + + std::string toSmtlib2(std::vector<std::string> const& varNames) const override { + std::stringstream sstr; + sstr << "(= " << lhs->toSmtlib2(varNames) << " " << rhs->toSmtlib2(varNames) << ")"; + return sstr.str(); + } + + private: + std::shared_ptr<DFTConstraint> lhs; + std::shared_ptr<DFTConstraint> rhs; + }; + + class IsConstantValue : public DFTConstraint { + public: + IsConstantValue(uint64_t varIndex, uint64_t val) : varIndex(varIndex), value(val) { + + } + + virtual ~IsConstantValue() { + + } + + std::string toSmtlib2(std::vector<std::string> const& varNames) const override { + std::stringstream sstr; + assert(varIndex < varNames.size()); + sstr << "(= " << varNames.at(varIndex) << " " << value << ")"; + return sstr.str(); + } + + private: + uint64_t varIndex; + uint64_t value; + }; + + class IsEqual : public DFTConstraint { + public: + IsEqual(uint64_t varIndex1, uint64_t varIndex2) :var1Index(varIndex1), var2Index(varIndex2) { + + } + + virtual ~IsEqual() { + + } + + std::string toSmtlib2(std::vector<std::string> const& varNames) const override { + return "(= " + varNames.at(var1Index) + " " + varNames.at(var2Index) + " )"; + } + + private: + uint64_t var1Index; + uint64_t var2Index; + }; + + class IsLEqual : public DFTConstraint { + public: + IsLEqual(uint64_t varIndex1, uint64_t varIndex2) :var1Index(varIndex1), var2Index(varIndex2) { + + } + + virtual ~IsLEqual() { + + } + + std::string toSmtlib2(std::vector<std::string> const& varNames) const override { + return "(<= " + varNames.at(var1Index) + " " + varNames.at(var2Index) + " )"; + } + + private: + uint64_t var1Index; + uint64_t var2Index; + }; + + class PairwiseDifferent : public DFTConstraint { + public: + PairwiseDifferent(std::vector<uint64_t> const& indices) : varIndices(indices) { + + } + virtual ~PairwiseDifferent() { + + } + + std::string toSmtlib2(std::vector<std::string> const& varNames) const override { + std::stringstream sstr; + sstr << "(distinct"; + // for(uint64_t i = 0; i < varIndices.size(); ++i) { + // for(uint64_t j = i + 1; j < varIndices.size(); ++j) { + // sstr << "()"; + // } + // } + for (auto const& varIndex : varIndices) { + sstr << " " << varNames.at(varIndex); + } + sstr << ")"; + return sstr.str(); + } + + private: + std::vector<uint64_t> varIndices; + }; + + class Sorted : public DFTConstraint { + public: + Sorted(std::vector<uint64_t> varIndices) : varIndices(varIndices) { + + } + + virtual ~Sorted() { + + } + + std::string toSmtlib2(std::vector<std::string> const& varNames) const override { + std::stringstream sstr; + sstr << "(and "; + for(uint64_t i = 1; i < varIndices.size(); ++i) { + sstr << "(<= " << varNames.at(varIndices.at(i-1)) << " " << varNames.at(varIndices.at(i)) << ")"; + } + sstr << ") "; + return sstr.str(); + } + + + private: + std::vector<uint64_t> varIndices; + }; + + class IfThenElse : public DFTConstraint { + public: + IfThenElse(std::shared_ptr<DFTConstraint> ifC, std::shared_ptr<DFTConstraint> thenC, std::shared_ptr<DFTConstraint> elseC) : ifConstraint(ifC), thenConstraint(thenC), elseConstraint(elseC) { + + } + + std::string toSmtlib2(std::vector<std::string> const& varNames) const override { + std::stringstream sstr; + sstr << "(ite " << ifConstraint->toSmtlib2(varNames) << " " << thenConstraint->toSmtlib2(varNames) << " " << elseConstraint->toSmtlib2(varNames) << ")"; + return sstr.str(); + } + + private: + std::shared_ptr<DFTConstraint> ifConstraint; + std::shared_ptr<DFTConstraint> thenConstraint; + std::shared_ptr<DFTConstraint> elseConstraint; + }; + + + DFTASFChecker::DFTASFChecker(storm::storage::DFT<double> const& dft) : dft(dft) { + // Intentionally left empty. + } + + uint64_t DFTASFChecker::getClaimVariableIndex(uint64_t spare, uint64_t child) const { + return claimVariables.at(SpareAndChildPair(spare, child)); + } + + void DFTASFChecker::convert() { + + std::vector<uint64_t> beVariables; + // Convert all elements + for (size_t i = 0; i < dft.nrElements(); ++i) { + std::shared_ptr<storm::storage::DFTElement<ValueType> const> element = dft.getElement(i); + varNames.push_back("t_" + element->name()); + timePointVariables.emplace(i, varNames.size() - 1); + switch (element->type()) { + case storm::storage::DFTElementType::BE: + beVariables.push_back(varNames.size() - 1); + break; + case storm::storage::DFTElementType::SPARE: + { + auto spare = std::static_pointer_cast<storm::storage::DFTSpare<double> const>(element); + for( auto const& spareChild : spare->children()) { + varNames.push_back("c_" + element->name() + "_" + spareChild->name()); + claimVariables.emplace(SpareAndChildPair(element->id(), spareChild->id()), varNames.size() - 1); + } + break; + } + default: + break; + } + } + + // BE + constraints.push_back(std::make_shared<PairwiseDifferent>(beVariables)); + constraints.back()->setDescription("No two BEs fail at the same time"); + for( auto const& beV : beVariables) { + constraints.push_back(std::make_shared<BetweenValues>(beV, 1, dft.nrBasicElements())); + } + + // Claim variables + for (auto const& csvV : claimVariables) { + constraints.push_back(std::make_shared<BetweenValues>(csvV.second, 0, dft.nrBasicElements() + 1)); + } + + for (size_t i = 0; i < dft.nrElements(); ++i) { + std::shared_ptr<storm::storage::DFTElement<ValueType> const> element = dft.getElement(i); + if(element->isSpareGate()) { + auto spare = std::static_pointer_cast<storm::storage::DFTSpare<double> const>(element); + auto const& spareChild = spare->children().front(); + constraints.push_back(std::make_shared<IsConstantValue>(getClaimVariableIndex(spare->id(), spareChild->id()), 0)); + constraints.back()->setDescription("Spare " + spare->name() + " claims first child"); + + } + } + + + for (size_t i = 0; i < dft.nrElements(); ++i) { + std::vector<uint64_t> childVarIndices; + if (dft.isGate(i)) { + std::shared_ptr<storm::storage::DFTGate<ValueType> const> gate = dft.getGate(i); + for (auto const& child : gate->children()) { + childVarIndices.push_back(timePointVariables.at(child->id())); + } + } + + std::shared_ptr<storm::storage::DFTElement<ValueType> const> element = dft.getElement(i); + switch (element->type()) { + case storm::storage::DFTElementType::AND: + constraints.push_back(std::make_shared<IsMaximum>(timePointVariables.at(i), childVarIndices)); + constraints.back()->setDescription("And gate"); + break; + case storm::storage::DFTElementType::OR: + constraints.push_back(std::make_shared<IsMinimum>(timePointVariables.at(i), childVarIndices)); + constraints.back()->setDescription("Or gate"); + break; + case storm::storage::DFTElementType::PAND: + constraints.push_back(std::make_shared<IfThenElse>(std::make_shared<Sorted>(childVarIndices), std::make_shared<IsEqual>(timePointVariables.at(i), timePointVariables.at(childVarIndices.back())), std::make_shared<IsConstantValue>(timePointVariables.at(i), dft.nrBasicElements()+1))); + constraints.back()->setDescription("Pand gate"); + case storm::storage::DFTElementType::SPARE: + { + auto spare = std::static_pointer_cast<storm::storage::DFTSpare<double> const>(element); + auto const& children = spare->children(); + + constraints.push_back(std::make_shared<Iff>(std::make_shared<IsLEqual>(getClaimVariableIndex(spare->id(), children.back()->id()), childVarIndices.back()), std::make_shared<IsEqual>(timePointVariables.at(i), childVarIndices.back()))); + constraints.back()->setDescription("Last child & claimed -> spare fails"); + std::vector<std::shared_ptr<DFTConstraint>> ifcs; + uint64_t xv = childVarIndices.at(childVarIndices.size()-2); // if v is the child, xv is the time x fails. + uint64_t csn = getClaimVariableIndex(spare->id(), children.back()->id()); // csn is the moment the spare claims the next child + uint64_t xn = childVarIndices.back(); // xn is the moment the next child fails + ifcs.push_back(std::make_shared<IsLEqual>(xv, xn)); + for(auto const& otherSpare : children.back()->parents()) { + if(otherSpare->id() == i) { continue; }// not a OTHER spare. + ifcs.push_back(std::make_shared<IsConstantValue>(getClaimVariableIndex(otherSpare->id(), children.back()->id()), dft.nrBasicElements()+1)); + } + std::shared_ptr<DFTConstraint> ite = std::make_shared<IfThenElse>(std::make_shared<And>(ifcs), std::make_shared<IsEqual>(csn, xv), std::make_shared<IsEqual>(timePointVariables.at(i), xv)); + constraints.push_back(std::make_shared<Iff>(std::make_shared<IsLEqual>(getClaimVariableIndex(spare->id(), children.at(children.size()-2)->id()), xv), ite)); + constraints.back()->setDescription("1 but last child & claimed"); + + for( uint64_t j = 0; j < children.size() - 2; ++j) { + uint64_t currChild = children.size() - 1 - j; + + } + break; + } + default: + break; + } + } + + constraints.push_back(std::make_shared<IsConstantValue>(dft.getTopLevelIndex(), dft.nrBasicElements()+1)); + } + + void DFTASFChecker::toFile(std::string const& filename) { + std::ofstream ofs; + std::cout << "Writing to " << filename << std::endl; + ofs.open(filename); + ofs << "; time point variables" << std::endl; + for (auto const& timeVarEntry : timePointVariables) { + ofs << "(declare-fun " << varNames[timeVarEntry.second] << "() Int)" << std::endl; + } + ofs << "; claim variables" << std::endl; + for (auto const& claimVarEntry : claimVariables) { + ofs << "(declare-fun " << varNames[claimVarEntry.second] << "() Int)" << std::endl; + } + for (auto const& constraint : constraints) { + ofs << "; " << constraint->description() << std::endl; + ofs << "(assert " << constraint->toSmtlib2(varNames) << ")" << std::endl; + } + ofs << "(check-sat)" << std::endl; + ofs.close(); + + } + } +} \ No newline at end of file diff --git a/src/modelchecker/dft/DFTASFChecker.h b/src/modelchecker/dft/DFTASFChecker.h new file mode 100644 index 000000000..73a323b03 --- /dev/null +++ b/src/modelchecker/dft/DFTASFChecker.h @@ -0,0 +1,61 @@ +#pragma once + +#include <string> +#include <vector> +#include <unordered_map> +#include "src/storage/dft/DFT.h" + + +namespace storm { + namespace modelchecker { + class DFTConstraint { + public: + virtual ~DFTConstraint() { + + } + + virtual std::string toSmtlib2(std::vector<std::string> const& varNames) const = 0; + virtual std::string description() const { return descript; } + + void setDescription(std::string const& descr) { + descript = descr; + } + + private: + std::string descript; + }; + + class SpareAndChildPair { + + public: + SpareAndChildPair(uint64_t spareIndex, uint64_t childIndex) : spareIndex(spareIndex), childIndex(childIndex) { + + } + + uint64_t spareIndex; + uint64_t childIndex; + }; + + bool operator<(SpareAndChildPair const& p1, SpareAndChildPair const& p2) { + return p1.spareIndex < p2.spareIndex || (p1.spareIndex == p2.spareIndex && p1.childIndex < p2.childIndex); + } + + class DFTASFChecker { + using ValueType = double; + + public: + DFTASFChecker(storm::storage::DFT<ValueType> const&); + void convert(); + void toFile(std::string const&); + + private: + uint64_t getClaimVariableIndex(uint64_t spareIndex, uint64_t childIndex) const; + + storm::storage::DFT<ValueType> const& dft; + std::vector<std::string> varNames; + std::unordered_map<uint64_t, uint64_t> timePointVariables; + std::vector<std::shared_ptr<DFTConstraint>> constraints; + std::map<SpareAndChildPair, uint64_t> claimVariables; + }; + } +} \ No newline at end of file diff --git a/src/storage/dft/DFTBuilder.cpp b/src/storage/dft/DFTBuilder.cpp index 840f5b77b..55b430863 100644 --- a/src/storage/dft/DFTBuilder.cpp +++ b/src/storage/dft/DFTBuilder.cpp @@ -32,7 +32,7 @@ namespace storm { // Child not found -> find first dependent event to assure that child is dependency // TODO: Not sure whether this is the intended behaviour? auto itFind = mElements.find(child + "_1"); - STORM_LOG_ASSERT(itFind != mElements.end(), "Child not found."); + STORM_LOG_ASSERT(itFind != mElements.end(), "Child '" << child << "' for gate '" << gate->name() << "' not found."); STORM_LOG_ASSERT(itFind->second->isDependency(), "Child is no dependency."); STORM_LOG_TRACE("Ignore functional dependency " << child << " in gate " << gate->name()); } diff --git a/src/storage/dft/DFTState.cpp b/src/storage/dft/DFTState.cpp index f6bb692a7..6e2b80035 100644 --- a/src/storage/dft/DFTState.cpp +++ b/src/storage/dft/DFTState.cpp @@ -319,7 +319,7 @@ namespace storm { mCurrentlyFailableBE.push_back(elem); // Remove from not failable BEs auto it = std::find(mCurrentlyNotFailableBE.begin(), mCurrentlyNotFailableBE.end(), elem); - STORM_LOG_ASSERT(it != mCurrentlyNotFailableBE.end(), "Element not found."); + STORM_LOG_ASSERT(it != mCurrentlyNotFailableBE.end(), "Element " << mDft.getElement(elem)->name() << " not found."); mCurrentlyNotFailableBE.erase(it); } } else if (mDft.getElement(elem)->isSpareGate() && !isActive(uses(elem))) { diff --git a/src/storm-dyftee.cpp b/src/storm-dyftee.cpp index 51061050e..2c7418075 100644 --- a/src/storm-dyftee.cpp +++ b/src/storm-dyftee.cpp @@ -3,6 +3,8 @@ #include "src/utility/storm.h" #include "src/parser/DFTGalileoParser.h" #include "src/modelchecker/dft/DFTModelChecker.h" + +#include "src/modelchecker/dft/DFTASFChecker.h" #include "src/cli/cli.h" #include "src/exceptions/BaseException.h" #include "src/utility/macros.h" @@ -58,10 +60,11 @@ void analyzeWithSMT(std::string filename) { storm::parser::DFTGalileoParser<ValueType> parser; storm::storage::DFT<ValueType> dft = parser.parseDFT(filename); - storm::builder::DFTSMTBuilder<ValueType> dftSmtBuilder; - dftSmtBuilder.convertToSMT(dft); - bool sat = dftSmtBuilder.check(); - std::cout << "SMT result: " << sat << std::endl; + storm::modelchecker::DFTASFChecker asfChecker(dft); + asfChecker.convert(); + asfChecker.toFile("test.smt2"); + //bool sat = dftSmtBuilder.check(); + //std::cout << "SMT result: " << sat << std::endl; } /*! @@ -120,7 +123,7 @@ int main(const int argc, const char** argv) { if (dftSettings.solveWithSMT()) { // Solve with SMT if (parametric) { - analyzeWithSMT<storm::RationalFunction>(dftSettings.getDftFilename()); + // analyzeWithSMT<storm::RationalFunction>(dftSettings.getDftFilename()); } else { analyzeWithSMT<double>(dftSettings.getDftFilename()); } From 9947d9b64cc90ffa1368080b2ad98a31572d771c Mon Sep 17 00:00:00 2001 From: Mavo <matthias.volk@rwth-aachen.de> Date: Sat, 29 Oct 2016 22:29:26 +0200 Subject: [PATCH 57/65] Add up exploration time Former-commit-id: f417ecc0214d492e376aab50833dbdb715ad3740 --- src/modelchecker/dft/DFTModelChecker.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modelchecker/dft/DFTModelChecker.cpp b/src/modelchecker/dft/DFTModelChecker.cpp index 5eee17e3e..be5ad6913 100644 --- a/src/modelchecker/dft/DFTModelChecker.cpp +++ b/src/modelchecker/dft/DFTModelChecker.cpp @@ -165,7 +165,7 @@ namespace storm { // TODO Matthias refine model using existing model and MC results builder.buildModel(labeloptions, iteration, approximationError); std::chrono::high_resolution_clock::time_point explorationEnd = std::chrono::high_resolution_clock::now(); - explorationTime = explorationEnd - explorationStart; + explorationTime += explorationEnd - explorationStart; // TODO Matthias: possible to do bisimulation on approximated model and not on concrete one? From 1c95722711e82a46c78a96996eb92f2584dfc8c3 Mon Sep 17 00:00:00 2001 From: Mavo <matthias.volk@rwth-aachen.de> Date: Sat, 29 Oct 2016 22:46:20 +0200 Subject: [PATCH 58/65] Do not skip states if approx = 0.0 Former-commit-id: 37dbb9739c95648dcfc91e5e0f85792f28d14afa --- src/builder/ExplicitDFTModelBuilderApprox.cpp | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/builder/ExplicitDFTModelBuilderApprox.cpp b/src/builder/ExplicitDFTModelBuilderApprox.cpp index 93a7b282e..c6c584e86 100644 --- a/src/builder/ExplicitDFTModelBuilderApprox.cpp +++ b/src/builder/ExplicitDFTModelBuilderApprox.cpp @@ -143,17 +143,19 @@ namespace storm { initializeNextIteration(); } - switch (heuristic) { - case storm::builder::ApproximationHeuristic::NONE: - // Do not change anything - approximationThreshold = dft.nrElements()+10; - break; - case storm::builder::ApproximationHeuristic::DEPTH: - approximationThreshold = iteration; - break; - case storm::builder::ApproximationHeuristic::PROBABILITY: - approximationThreshold = 10 * std::pow(2, iteration); - break; + if (approximationThreshold > 0) { + switch (heuristic) { + case storm::builder::ApproximationHeuristic::NONE: + // Do not change anything + approximationThreshold = dft.nrElements()+10; + break; + case storm::builder::ApproximationHeuristic::DEPTH: + approximationThreshold = iteration; + break; + case storm::builder::ApproximationHeuristic::PROBABILITY: + approximationThreshold = 10 * std::pow(2, iteration); + break; + } } exploreStateSpace(approximationThreshold); @@ -319,7 +321,7 @@ namespace storm { // Try to explore the next state generator.load(currentState); - if (nrExpandedStates > approximationThreshold && !currentExplorationHeuristic->isExpand()) { + if (approximationThreshold > 0.0 && nrExpandedStates > approximationThreshold && !currentExplorationHeuristic->isExpand()) { //if (currentExplorationHeuristic->isSkip(approximationThreshold)) { // Skip the current state ++nrSkippedStates; From 875feebdb8b33df83639001d351b5235cf68af05 Mon Sep 17 00:00:00 2001 From: sjunges <sebastian.junges@rwth-aachen.de> Date: Sun, 30 Oct 2016 01:55:30 +0200 Subject: [PATCH 59/65] modularisation checks for restrictors Former-commit-id: 6c4eb363cd11f9aa442f823f6cf03c969f0bc72a --- src/storage/dft/DFT.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/storage/dft/DFT.cpp b/src/storage/dft/DFT.cpp index b8024ef56..101f3f962 100644 --- a/src/storage/dft/DFT.cpp +++ b/src/storage/dft/DFT.cpp @@ -243,7 +243,7 @@ namespace storm { std::map<size_t, std::vector<size_t>> subdfts; for(auto const& child : children) { std::vector<size_t> isubdft; - if(child->nrParents() > 1 || child->hasOutgoingDependencies()) { + if(child->nrParents() > 1 || child->hasOutgoingDependencies() || child->hasRestrictions()) { STORM_LOG_TRACE("child " << child->name() << "does not allow modularisation."); return {*this}; } @@ -696,6 +696,8 @@ namespace storm { // suitable parent gate! - Lets check the independent submodules of the children auto const& children = std::static_pointer_cast<DFTGate<ValueType>>(e)->children(); for(auto const& child : children) { + + auto ISD = std::static_pointer_cast<DFTGate<ValueType>>(child)->independentSubDft(true); // In the ISD, check for other children: From 6fcc97a7b4a7eee22cd94514fd9d4aeb4a9cf046 Mon Sep 17 00:00:00 2001 From: Mavo <matthias.volk@rwth-aachen.de> Date: Sun, 30 Oct 2016 02:30:39 +0200 Subject: [PATCH 60/65] Implemented modularisation for MTTF via parallel composition of CTMCs Former-commit-id: 552949346b81a4c4def16dc68dd2206e3a993807 --- src/builder/ParallelCompositionBuilder.cpp | 172 ++++++++++++++++ src/builder/ParallelCompositionBuilder.h | 24 +++ src/modelchecker/dft/DFTModelChecker.cpp | 218 +++++++++++++++++---- src/modelchecker/dft/DFTModelChecker.h | 14 ++ src/storage/dft/DFT.cpp | 2 +- src/storm-dyftee.cpp | 6 +- 6 files changed, 392 insertions(+), 44 deletions(-) create mode 100644 src/builder/ParallelCompositionBuilder.cpp create mode 100644 src/builder/ParallelCompositionBuilder.h diff --git a/src/builder/ParallelCompositionBuilder.cpp b/src/builder/ParallelCompositionBuilder.cpp new file mode 100644 index 000000000..5d78d74fa --- /dev/null +++ b/src/builder/ParallelCompositionBuilder.cpp @@ -0,0 +1,172 @@ +#include "src/builder/ParallelCompositionBuilder.h" +#include "src/models/sparse/StandardRewardModel.h" +#include <src/utility/constants.h> + +namespace storm { + namespace builder { + + template<typename ValueType> + std::shared_ptr<storm::models::sparse::Ctmc<ValueType>> ParallelCompositionBuilder<ValueType>::compose(std::shared_ptr<storm::models::sparse::Ctmc<ValueType>> const& ctmcA, std::shared_ptr<storm::models::sparse::Ctmc<ValueType>> const& ctmcB, bool labelAnd) { + STORM_LOG_TRACE("Parallel composition"); + + storm::storage::SparseMatrix<ValueType> matrixA = ctmcA->getTransitionMatrix(); + storm::storage::SparseMatrix<ValueType> matrixB = ctmcB->getTransitionMatrix(); + storm::models::sparse::StateLabeling labelingA = ctmcA->getStateLabeling(); + storm::models::sparse::StateLabeling labelingB = ctmcB->getStateLabeling(); + size_t sizeA = ctmcA->getNumberOfStates(); + size_t sizeB = ctmcB->getNumberOfStates(); + size_t size = sizeA * sizeB; + size_t rowIndex = 0; + + // Build matrix + storm::storage::SparseMatrixBuilder<ValueType> builder(size, size, 0, true, false, 0); + + for (size_t stateA = 0; stateA < sizeA; ++stateA) { + for (size_t stateB = 0; stateB < sizeB; ++stateB) { + STORM_LOG_ASSERT(rowIndex == stateA * sizeB + stateB, "Row " << rowIndex << " is not correct"); + + auto rowA = matrixA.getRow(stateA); + auto itA = rowA.begin(); + auto rowB = matrixB.getRow(stateB); + auto itB = rowB.begin(); + + // First consider all target states of A < the current stateA + while (itA != rowA.end() && itA->getColumn() < stateA) { + builder.addNextValue(rowIndex, itA->getColumn() * sizeB + stateB, itA->getValue()); + ++itA; + } + + // Then consider all target states of B + while (itB != rowB.end()) { + builder.addNextValue(rowIndex, stateA * sizeB + itB->getColumn(), itB->getValue()); + ++itB; + } + + // Last consider all remaining target states of A > the current stateA + while (itA != rowA.end()) { + builder.addNextValue(rowIndex, itA->getColumn() * sizeB + stateB, itA->getValue()); + ++itA; + } + + ++rowIndex; + } + } + + storm::storage::SparseMatrix<ValueType> matrixComposed = builder.build(); + STORM_LOG_ASSERT(matrixComposed.getRowCount() == size, "Row count is not correct"); + STORM_LOG_ASSERT(matrixComposed.getColumnCount() == size, "Column count is not correct"); + + // Build labeling + storm::models::sparse::StateLabeling labeling = storm::models::sparse::StateLabeling(sizeA * sizeB); + + if (labelAnd) { + for (std::string const& label : labelingA.getLabels()) { + if (labelingB.containsLabel(label)) { + // Only consider labels contained in both CTMCs + storm::storage::BitVector labelStates(size, false); + for (auto entryA : labelingA.getStates(label)) { + for (auto entryB : labelingB.getStates(label)) { + labelStates.set(entryA * sizeB + entryB); + } + } + labeling.addLabel(label, labelStates); + } + } + } else { + // Set labels from A + for (std::string const& label : labelingA.getLabels()) { + if (label == "init") { + // Initial states must be initial in both CTMCs + STORM_LOG_ASSERT(labelingB.containsLabel(label), "B does not have init."); + storm::storage::BitVector labelStates(size, false); + for (auto entryA : labelingA.getStates(label)) { + for (auto entryB : labelingB.getStates(label)) { + labelStates.set(entryA * sizeB + entryB); + } + } + labeling.addLabel(label, labelStates); + } else { + storm::storage::BitVector labelStates(size, false); + for (auto entry : labelingA.getStates(label)) { + for (size_t index = entry * sizeB; index < entry * sizeB + sizeB; ++index) { + labelStates.set(index, true); + } + } + labeling.addLabel(label, labelStates); + } + } + // Set labels from B + for (std::string const& label : labelingB.getLabels()) { + if (label == "init") { + continue; + } + if (labeling.containsLabel(label)) { + // Label is already there from A + for (auto entry : labelingB.getStates(label)) { + for (size_t index = 0; index < sizeA; ++index) { + labeling.addLabelToState(label, index * sizeB + entry); + } + } + } else { + storm::storage::BitVector labelStates(size, false); + for (auto entry : labelingB.getStates(label)) { + for (size_t index = 0; index < sizeA; ++index) { + labelStates.set(index * sizeB + entry, true); + } + } + labeling.addLabel(label, labelStates); + } + } + } + + + // Build CTMC + std::shared_ptr<storm::models::sparse::Ctmc<ValueType>> composedCtmc = std::make_shared<storm::models::sparse::Ctmc<ValueType>>(matrixComposed, labeling); + + // Print for debugging + /*std::cout << "Matrix A:" << std::endl; + std::cout << matrixA << std::endl; + std::cout << "Matrix B:" << std::endl; + std::cout << matrixB << std::endl; + std::cout << "Composed matrix: " << std::endl << matrixComposed; + std::cout << "Labeling A" << std::endl; + labelingA.printLabelingInformationToStream(std::cout); + for (size_t stateA = 0; stateA < sizeA; ++stateA) { + std::cout << "State " << stateA << ": "; + for (auto label : labelingA.getLabelsOfState(stateA)) { + std::cout << label << ", "; + } + std::cout << std::endl; + } + std::cout << "Labeling B" << std::endl; + labelingB.printLabelingInformationToStream(std::cout); + for (size_t stateB = 0; stateB < sizeB; ++stateB) { + std::cout << "State " << stateB << ": "; + for (auto label : labelingB.getLabelsOfState(stateB)) { + std::cout << label << ", "; + } + std::cout << std::endl; + } + std::cout << "Labeling Composed" << std::endl; + labeling.printLabelingInformationToStream(std::cout); + for (size_t state = 0; state < size; ++state) { + std::cout << "State " << state << ": "; + for (auto label : labeling.getLabelsOfState(state)) { + std::cout << label << ", "; + } + std::cout << std::endl; + }*/ + + return composedCtmc; + } + + + // Explicitly instantiate the class. + template class ParallelCompositionBuilder<double>; + +#ifdef STORM_HAVE_CARL + template class ParallelCompositionBuilder<storm::RationalFunction>; +#endif + + } // namespace builder +} // namespace storm diff --git a/src/builder/ParallelCompositionBuilder.h b/src/builder/ParallelCompositionBuilder.h new file mode 100644 index 000000000..3b0fab790 --- /dev/null +++ b/src/builder/ParallelCompositionBuilder.h @@ -0,0 +1,24 @@ +#ifndef PARALLELCOMPOSITIONBUILDER_H +#define PARALLELCOMPOSITIONBUILDER_H + +#include <src/models/sparse/Ctmc.h> + +namespace storm { + namespace builder { + + /*! + * Build a parallel composition of Markov chains. + */ + template<typename ValueType> + class ParallelCompositionBuilder { + + public: + + static std::shared_ptr<storm::models::sparse::Ctmc<ValueType>> compose(std::shared_ptr<storm::models::sparse::Ctmc<ValueType>> const& ctmcA, std::shared_ptr<storm::models::sparse::Ctmc<ValueType>> const& ctmcB, bool labelAnd); + + }; + + } +} + +#endif /* EXPLICITDFTMODELBUPARALLELCOMPOSITIONBUILDER_HILDERAPPROX_H */ diff --git a/src/modelchecker/dft/DFTModelChecker.cpp b/src/modelchecker/dft/DFTModelChecker.cpp index be5ad6913..748abe188 100644 --- a/src/modelchecker/dft/DFTModelChecker.cpp +++ b/src/modelchecker/dft/DFTModelChecker.cpp @@ -2,6 +2,7 @@ #include "src/builder/ExplicitDFTModelBuilder.h" #include "src/builder/ExplicitDFTModelBuilderApprox.h" +#include "src/builder/ParallelCompositionBuilder.h" #include "src/storage/dft/DFTIsomorphism.h" #include "src/settings/modules/DFTSettings.h" #include "src/utility/bitoperations.h" @@ -28,10 +29,19 @@ namespace storm { // Optimizing DFT storm::storage::DFT<ValueType> dft = origDft.optimize(); - // TODO Matthias: check that all paths reach the target state! + // TODO Matthias: check that all paths reach the target state for approximation // Checking DFT - checkResult = checkHelper(dft, formula, symred, allowModularisation, enableDC, approximationError); + if (formula->isProbabilityOperatorFormula() || !allowModularisation) { + checkResult = checkHelper(dft, formula, symred, allowModularisation, enableDC, approximationError); + } else { + std::shared_ptr<storm::models::sparse::Model<ValueType>> model = buildModelComposition(dft, formula, symred, allowModularisation, enableDC); + // Model checking + std::unique_ptr<storm::modelchecker::CheckResult> result = checkModel(model, formula); + result->filter(storm::modelchecker::ExplicitQualitativeCheckResult(model->getInitialStates())); + checkResult = result->asExplicitQuantitativeCheckResult<ValueType>().getValueMap().begin()->second; + + } this->totalTime = std::chrono::high_resolution_clock::now() - totalStart; } @@ -85,45 +95,44 @@ namespace storm { if(modularisationPossible) { STORM_LOG_TRACE("Recursive CHECK Call"); - // TODO Matthias: enable modularisation for approximation - STORM_LOG_ASSERT(approximationError == 0.0, "Modularisation not possible for approximation."); - - // Recursively call model checking - std::vector<ValueType> res; - for(auto const ft : dfts) { - dft_result ftResult = checkHelper(ft, formula, symred, true, enableDC, 0.0); - res.push_back(boost::get<ValueType>(ftResult)); - } + if (formula->isProbabilityOperatorFormula()) { + // Recursively call model checking + std::vector<ValueType> res; + for(auto const ft : dfts) { + dft_result ftResult = checkHelper(ft, formula, symred, true, enableDC, 0.0); + res.push_back(boost::get<ValueType>(ftResult)); + } - // Combine modularisation results - STORM_LOG_TRACE("Combining all results... K=" << nrK << "; M=" << nrM << "; invResults=" << (invResults?"On":"Off")); - ValueType result = storm::utility::zero<ValueType>(); - 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_ASSERT(res.size() < 32, "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<size_t>(cK)); - do { - STORM_LOG_TRACE("Permutation="<<permutation); - ValueType permResult = storm::utility::one<ValueType>(); - for(size_t i = 0; i < res.size(); ++i) { - if(permutation & (1 << i)) { - permResult *= res[i]; - } else { - permResult *= storm::utility::one<ValueType>() - res[i]; + // Combine modularisation results + STORM_LOG_TRACE("Combining all results... K=" << nrK << "; M=" << nrM << "; invResults=" << (invResults?"On":"Off")); + ValueType result = storm::utility::zero<ValueType>(); + 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_ASSERT(res.size() < 32, "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<size_t>(cK)); + do { + STORM_LOG_TRACE("Permutation="<<permutation); + ValueType permResult = storm::utility::one<ValueType>(); + for(size_t i = 0; i < res.size(); ++i) { + if(permutation & (1 << i)) { + permResult *= res[i]; + } else { + permResult *= storm::utility::one<ValueType>() - res[i]; + } } - } - STORM_LOG_TRACE("Result for permutation:"<<permResult); - permutation = nextBitPermutation(permutation); - result += permResult; - } while(permutation < (1 << nrM) && permutation != 0); - } - if(invResults) { - result = storm::utility::one<ValueType>() - result; + STORM_LOG_TRACE("Result for permutation:"<<permResult); + permutation = nextBitPermutation(permutation); + result += permResult; + } while(permutation < (1 << nrM) && permutation != 0); + } + if(invResults) { + result = storm::utility::one<ValueType>() - result; + } + return result; } - return result; } } @@ -132,6 +141,137 @@ namespace storm { return checkDFT(dft, formula, symred, enableDC, approximationError); } + template<typename ValueType> + std::shared_ptr<storm::models::sparse::Ctmc<ValueType>> DFTModelChecker<ValueType>::buildModelComposition(storm::storage::DFT<ValueType> const& dft, std::shared_ptr<const storm::logic::Formula> const& formula, bool symred, bool allowModularisation, bool enableDC) { + STORM_LOG_TRACE("Build model via composition"); + // Use parallel composition for CTMCs for expected time + STORM_LOG_ASSERT(formula->isTimeOperatorFormula(), "Formula is not a time operator formula"); + bool modularisationPossible = allowModularisation; + + // Try modularisation + if(modularisationPossible) { + std::vector<storm::storage::DFT<ValueType>> dfts; + bool isAnd = true; + + switch (dft.topLevelType()) { + case storm::storage::DFTElementType::AND: + STORM_LOG_TRACE("top modularisation called AND"); + dfts = dft.topModularisation(); + STORM_LOG_TRACE("Modularisation into " << dfts.size() << " submodules."); + modularisationPossible = dfts.size() > 1; + isAnd = true; + break; + case storm::storage::DFTElementType::OR: + STORM_LOG_TRACE("top modularisation called OR"); + dfts = dft.topModularisation(); + STORM_LOG_TRACE("Modularsation into " << dfts.size() << " submodules."); + modularisationPossible = dfts.size() > 1; + isAnd = false; + break; + case storm::storage::DFTElementType::VOT: + /*STORM_LOG_TRACE("top modularisation called VOT"); + dfts = dft.topModularisation(); + STORM_LOG_TRACE("Modularsation into " << dfts.size() << " submodules."); + std::static_pointer_cast<storm::storage::DFTVot<ValueType> const>(dft.getTopLevelGate())->threshold(); + */ + // TODO enable modularisation for voting gate + modularisationPossible = false; + break; + default: + // No static gate -> no modularisation applicable + modularisationPossible = false; + break; + } + + if(modularisationPossible) { + STORM_LOG_TRACE("Recursive CHECK Call"); + bool firstTime = true; + std::shared_ptr<storm::models::sparse::Ctmc<ValueType>> composedModel; + for (auto const ft : dfts) { + STORM_LOG_INFO("Building Model via parallel composition..."); + std::chrono::high_resolution_clock::time_point checkingStart = std::chrono::high_resolution_clock::now(); + + // Find symmetries + std::map<size_t, std::vector<std::vector<size_t>>> emptySymmetry; + storm::storage::DFTIndependentSymmetries symmetries(emptySymmetry); + if(symred) { + auto colouring = ft.colourDFT(); + symmetries = ft.findSymmetries(colouring); + STORM_LOG_INFO("Found " << symmetries.groups.size() << " symmetries."); + STORM_LOG_TRACE("Symmetries: " << std::endl << symmetries); + } + + // Build a single CTMC + STORM_LOG_INFO("Building Model..."); + storm::builder::ExplicitDFTModelBuilderApprox<ValueType> builder(ft, symmetries, enableDC); + typename storm::builder::ExplicitDFTModelBuilderApprox<ValueType>::LabelOptions labeloptions; // TODO initialize this with the formula + builder.buildModel(labeloptions, 0, 0.0); + std::shared_ptr<storm::models::sparse::Model<ValueType>> model = builder.getModel(); + //model->printModelInformationToStream(std::cout); + STORM_LOG_INFO("No. states (Explored): " << model->getNumberOfStates()); + STORM_LOG_INFO("No. transitions (Explored): " << model->getNumberOfTransitions()); + explorationTime += std::chrono::high_resolution_clock::now() - checkingStart; + + STORM_LOG_THROW(model->isOfType(storm::models::ModelType::Ctmc), storm::exceptions::NotSupportedException, "Parallel composition only applicable for CTMCs"); + std::shared_ptr<storm::models::sparse::Ctmc<ValueType>> ctmc = model->template as<storm::models::sparse::Ctmc<ValueType>>(); + + ctmc = storm::performDeterministicSparseBisimulationMinimization<storm::models::sparse::Ctmc<ValueType>>(ctmc, {formula}, storm::storage::BisimulationType::Weak)->template as<storm::models::sparse::Ctmc<ValueType>>(); + + if (firstTime) { + composedModel = ctmc; + firstTime = false; + } else { + composedModel = storm::builder::ParallelCompositionBuilder<ValueType>::compose(composedModel, ctmc, isAnd); + } + + // Apply bisimulation + std::chrono::high_resolution_clock::time_point bisimulationStart = std::chrono::high_resolution_clock::now(); + composedModel = storm::performDeterministicSparseBisimulationMinimization<storm::models::sparse::Ctmc<ValueType>>(composedModel, {formula}, storm::storage::BisimulationType::Weak)->template as<storm::models::sparse::Ctmc<ValueType>>(); + std::chrono::high_resolution_clock::time_point bisimulationEnd = std::chrono::high_resolution_clock::now(); + bisimulationTime += bisimulationEnd - bisimulationStart; + + STORM_LOG_INFO("No. states (Composed): " << composedModel->getNumberOfStates()); + STORM_LOG_INFO("No. transitions (Composed): " << composedModel->getNumberOfTransitions()); + if (composedModel->getNumberOfStates() <= 15) { + STORM_LOG_TRACE("Transition matrix: " << std::endl << composedModel->getTransitionMatrix()); + } else { + STORM_LOG_TRACE("Transition matrix: too big to print"); + } + + } + return composedModel; + } + } + + // If we are here, no composition was possible + STORM_LOG_ASSERT(!modularisationPossible, "Modularisation should not be possible."); + std::chrono::high_resolution_clock::time_point checkingStart = std::chrono::high_resolution_clock::now(); + // Find symmetries + std::map<size_t, std::vector<std::vector<size_t>>> emptySymmetry; + storm::storage::DFTIndependentSymmetries symmetries(emptySymmetry); + if(symred) { + auto colouring = dft.colourDFT(); + symmetries = dft.findSymmetries(colouring); + STORM_LOG_INFO("Found " << symmetries.groups.size() << " symmetries."); + STORM_LOG_TRACE("Symmetries: " << std::endl << symmetries); + } + // Build a single CTMC + STORM_LOG_INFO("Building Model..."); + + + storm::builder::ExplicitDFTModelBuilderApprox<ValueType> builder(dft, symmetries, enableDC); + typename storm::builder::ExplicitDFTModelBuilderApprox<ValueType>::LabelOptions labeloptions; // TODO initialize this with the formula + builder.buildModel(labeloptions, 0, 0.0); + std::shared_ptr<storm::models::sparse::Model<ValueType>> model = builder.getModel(); + //model->printModelInformationToStream(std::cout); + STORM_LOG_INFO("No. states (Explored): " << model->getNumberOfStates()); + STORM_LOG_INFO("No. transitions (Explored): " << model->getNumberOfTransitions()); + explorationTime += std::chrono::high_resolution_clock::now() - checkingStart; + STORM_LOG_THROW(model->isOfType(storm::models::ModelType::Ctmc), storm::exceptions::NotSupportedException, "Parallel composition only applicable for CTMCs"); + + return model->template as<storm::models::sparse::Ctmc<ValueType>>(); + } + template<typename ValueType> typename DFTModelChecker<ValueType>::dft_result DFTModelChecker<ValueType>::checkDFT(storm::storage::DFT<ValueType> const& dft, std::shared_ptr<const storm::logic::Formula> const& formula, bool symred, bool enableDC, double approximationError) { std::chrono::high_resolution_clock::time_point checkingStart = std::chrono::high_resolution_clock::now(); @@ -213,7 +353,7 @@ namespace storm { if (storm::settings::getModule<storm::settings::modules::DFTSettings>().isApproximationErrorSet()) { storm::builder::ExplicitDFTModelBuilderApprox<ValueType> builder(dft, symmetries, enableDC); typename storm::builder::ExplicitDFTModelBuilderApprox<ValueType>::LabelOptions labeloptions; // TODO initialize this with the formula - builder.buildModel(labeloptions, 0); + builder.buildModel(labeloptions, 0, 0.0); model = builder.getModel(); } else { storm::builder::ExplicitDFTModelBuilder<ValueType> builder(dft, symmetries, enableDC); diff --git a/src/modelchecker/dft/DFTModelChecker.h b/src/modelchecker/dft/DFTModelChecker.h index 543bf97fd..67deb2af0 100644 --- a/src/modelchecker/dft/DFTModelChecker.h +++ b/src/modelchecker/dft/DFTModelChecker.h @@ -83,6 +83,20 @@ namespace storm { */ dft_result checkHelper(storm::storage::DFT<ValueType> const& dft, std::shared_ptr<const storm::logic::Formula> const& formula, bool symred, bool allowModularisation, bool enableDC, double approximationError); + /*! + * Internal helper for building a CTMC from a DFT via parallel composition. + * + * @param dft DFT + * @param formula Formula to check for + * @param symred Flag indicating if symmetry reduction should be used + * @param allowModularisation Flag indication if modularisation is allowed + * @param enableDC Flag indicating if dont care propagation should be used + * @param approximationError Error allowed for approximation. Value 0 indicates no approximation + * + * @return CTMC representing the DFT + */ + std::shared_ptr<storm::models::sparse::Ctmc<ValueType>> buildModelComposition(storm::storage::DFT<ValueType> const& dft, std::shared_ptr<const storm::logic::Formula> const& formula, bool symred, bool allowModularisation, bool enableDC); + /*! * Check model generated from DFT. * diff --git a/src/storage/dft/DFT.cpp b/src/storage/dft/DFT.cpp index 101f3f962..88be1df73 100644 --- a/src/storage/dft/DFT.cpp +++ b/src/storage/dft/DFT.cpp @@ -244,7 +244,7 @@ namespace storm { for(auto const& child : children) { std::vector<size_t> isubdft; if(child->nrParents() > 1 || child->hasOutgoingDependencies() || child->hasRestrictions()) { - STORM_LOG_TRACE("child " << child->name() << "does not allow modularisation."); + STORM_LOG_TRACE("child " << child->name() << " does not allow modularisation."); return {*this}; } if (isGate(child->id())) { diff --git a/src/storm-dyftee.cpp b/src/storm-dyftee.cpp index 5b040a3b0..05e01c6d1 100644 --- a/src/storm-dyftee.cpp +++ b/src/storm-dyftee.cpp @@ -142,7 +142,6 @@ int main(const int argc, const char** argv) { // Construct pctlFormula std::string pctlFormula = ""; - bool allowModular = true; std::string operatorType = ""; std::string targetFormula = ""; @@ -153,7 +152,6 @@ int main(const int argc, const char** argv) { STORM_LOG_THROW(!dftSettings.usePropProbability() && !dftSettings.usePropTimebound(), storm::exceptions::InvalidSettingsException, "More than one property given."); operatorType = "T"; targetFormula = "F \"failed\""; - allowModular = false; } else if (dftSettings.usePropProbability()) { STORM_LOG_THROW(!dftSettings.usePropTimebound(), storm::exceptions::InvalidSettingsException, "More than one property given."); operatorType = "P";; @@ -180,9 +178,9 @@ int main(const int argc, const char** argv) { // From this point on we are ready to carry out the actual computations. if (parametric) { - analyzeDFT<storm::RationalFunction>(dftSettings.getDftFilename(), pctlFormula, dftSettings.useSymmetryReduction(), allowModular && dftSettings.useModularisation(), !dftSettings.isDisableDC(), approximationError); + analyzeDFT<storm::RationalFunction>(dftSettings.getDftFilename(), pctlFormula, dftSettings.useSymmetryReduction(), dftSettings.useModularisation(), !dftSettings.isDisableDC(), approximationError); } else { - analyzeDFT<double>(dftSettings.getDftFilename(), pctlFormula, dftSettings.useSymmetryReduction(), allowModular && dftSettings.useModularisation(), !dftSettings.isDisableDC(), approximationError); + analyzeDFT<double>(dftSettings.getDftFilename(), pctlFormula, dftSettings.useSymmetryReduction(), dftSettings.useModularisation(), !dftSettings.isDisableDC(), approximationError); } // All operations have now been performed, so we clean up everything and terminate. From cd1aec77503944f42c00558763903479a5f352a1 Mon Sep 17 00:00:00 2001 From: Mavo <matthias.volk@rwth-aachen.de> Date: Sun, 30 Oct 2016 16:10:00 +0100 Subject: [PATCH 61/65] Heuristic using the difference between upper and lower bound Former-commit-id: a514e33d6947fd25431ddf3b04a6a55984c189fe --- src/builder/DftExplorationHeuristic.cpp | 22 +++++++++++++++- src/builder/DftExplorationHeuristic.h | 11 ++++---- src/builder/ExplicitDFTModelBuilderApprox.cpp | 25 ++++++++++++++++--- src/builder/ExplicitDFTModelBuilderApprox.h | 4 +-- src/storage/BucketPriorityQueue.cpp | 11 +++++++- src/storage/BucketPriorityQueue.h | 2 +- 6 files changed, 60 insertions(+), 15 deletions(-) diff --git a/src/builder/DftExplorationHeuristic.cpp b/src/builder/DftExplorationHeuristic.cpp index 40d00115f..527513134 100644 --- a/src/builder/DftExplorationHeuristic.cpp +++ b/src/builder/DftExplorationHeuristic.cpp @@ -47,9 +47,29 @@ namespace storm { STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Heuristic rate ration does not work for rational functions."); } + template<> + bool DFTExplorationHeuristicBoundDifference<double>::updateHeuristicValues(DFTExplorationHeuristic<double> const& predecessor, double rate, double exitRate) { + STORM_LOG_ASSERT(exitRate > 0, "Exit rate is 0"); + probability += predecessor.getProbability() * rate/exitRate; + return true; + } + + template<> + bool DFTExplorationHeuristicBoundDifference<storm::RationalFunction>::updateHeuristicValues(DFTExplorationHeuristic<storm::RationalFunction> const& predecessor, storm::RationalFunction rate, storm::RationalFunction exitRate) { + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Heuristic rate ration does not work for rational functions."); + return false; + } + + template<typename ValueType> + void DFTExplorationHeuristicBoundDifference<ValueType>::setBounds(ValueType lowerBound, ValueType upperBound) { + this->lowerBound = lowerBound; + this->upperBound = upperBound; + difference = (storm::utility::one<ValueType>() / upperBound) - (storm::utility::one<ValueType>() / lowerBound); + } + template<> double DFTExplorationHeuristicBoundDifference<double>::getPriority() const { - return upperBound / lowerBound; + return probability * difference; } template<> diff --git a/src/builder/DftExplorationHeuristic.h b/src/builder/DftExplorationHeuristic.h index 8feb0fefa..44931dfbf 100644 --- a/src/builder/DftExplorationHeuristic.h +++ b/src/builder/DftExplorationHeuristic.h @@ -10,7 +10,7 @@ namespace storm { /*! * Enum representing the heuristic used for deciding which states to expand. */ - enum class ApproximationHeuristic { NONE, DEPTH, PROBABILITY }; + enum class ApproximationHeuristic { NONE, DEPTH, PROBABILITY, BOUNDDIFFERENCE }; /*! @@ -165,9 +165,9 @@ namespace storm { // Intentionally left empty } - bool updateHeuristicValues(DFTExplorationHeuristic<ValueType> const& predecessor, ValueType rate, ValueType exitRate) override { - return false; - } + void setBounds(ValueType lowerBound, ValueType upperBound); + + bool updateHeuristicValues(DFTExplorationHeuristic<ValueType> const& predecessor, ValueType rate, ValueType exitRate) override; double getPriority() const override; @@ -180,8 +180,7 @@ namespace storm { } private: - ValueType lowerBound; - ValueType upperBound; + ValueType difference; }; diff --git a/src/builder/ExplicitDFTModelBuilderApprox.cpp b/src/builder/ExplicitDFTModelBuilderApprox.cpp index c6c584e86..2f302b0db 100644 --- a/src/builder/ExplicitDFTModelBuilderApprox.cpp +++ b/src/builder/ExplicitDFTModelBuilderApprox.cpp @@ -28,7 +28,7 @@ namespace storm { dft(dft), stateGenerationInfo(std::make_shared<storm::storage::DFTStateGenerationInfo>(dft.buildStateGenerationInfo(symmetries))), enableDC(enableDC), - heuristic(storm::settings::getModule<storm::settings::modules::DFTSettings>().getApproximationHeuristic()), + usedHeuristic(storm::settings::getModule<storm::settings::modules::DFTSettings>().getApproximationHeuristic()), generator(dft, *stateGenerationInfo, enableDC, mergeFailedStates), matrixBuilder(!generator.isDeterministicModel()), stateStorage(((dft.stateVectorSize() / 64) + 1) * 64), @@ -38,7 +38,7 @@ namespace storm { { // Intentionally left empty. // TODO Matthias: remove again - heuristic = storm::builder::ApproximationHeuristic::PROBABILITY; + usedHeuristic = storm::builder::ApproximationHeuristic::BOUNDDIFFERENCE; // Compute independent subtrees if (dft.topLevelType() == storm::storage::DFTElementType::OR) { @@ -144,7 +144,7 @@ namespace storm { } if (approximationThreshold > 0) { - switch (heuristic) { + switch (usedHeuristic) { case storm::builder::ApproximationHeuristic::NONE: // Do not change anything approximationThreshold = dft.nrElements()+10; @@ -153,6 +153,7 @@ namespace storm { approximationThreshold = iteration; break; case storm::builder::ApproximationHeuristic::PROBABILITY: + case storm::builder::ApproximationHeuristic::BOUNDDIFFERENCE: approximationThreshold = 10 * std::pow(2, iteration); break; } @@ -360,6 +361,20 @@ namespace storm { // Do not skip absorbing state or if reached by dependencies iter->second.second->markExpand(); } + if (usedHeuristic == storm::builder::ApproximationHeuristic::BOUNDDIFFERENCE) { + // Compute bounds for heuristic now + if (state->isPseudoState()) { + // Create concrete state from pseudo state + state->construct(); + } + STORM_LOG_ASSERT(!currentState->isPseudoState(), "State is pseudo state."); + + // Initialize bounds + ValueType lowerBound = getLowerBound(state); + ValueType upperBound = getUpperBound(state); + heuristic->setBounds(lowerBound, upperBound); + } + explorationQueue.push(heuristic); } else if (!iter->second.second->isExpand()) { double oldPriority = iter->second.second->getPriority(); @@ -567,9 +582,11 @@ namespace storm { for (size_t i = 0; i < rates.size(); ++i) { // Cold BEs cannot fail in the first step if (!coldBEs.get(i) && rateCount[i] > 0) { + std::iter_swap(rates.begin() + i, rates.end() - 1); // Compute AND MTTF of subtree without current rate and scale with current rate upperBound += rates.back() * rateCount[i] * computeMTTFAnd(rates, rates.size() - 1); - // Swap here to avoid swapping back + // Swap back + // TODO try to avoid swapping back std::iter_swap(rates.begin() + i, rates.end() - 1); } } diff --git a/src/builder/ExplicitDFTModelBuilderApprox.h b/src/builder/ExplicitDFTModelBuilderApprox.h index 933cd3277..0e9b9dc57 100644 --- a/src/builder/ExplicitDFTModelBuilderApprox.h +++ b/src/builder/ExplicitDFTModelBuilderApprox.h @@ -28,7 +28,7 @@ namespace storm { using DFTStatePointer = std::shared_ptr<storm::storage::DFTState<ValueType>>; // TODO Matthias: make choosable - using ExplorationHeuristic = DFTExplorationHeuristicProbability<ValueType>; + using ExplorationHeuristic = DFTExplorationHeuristicBoundDifference<ValueType>; using ExplorationHeuristicPointer = std::shared_ptr<ExplorationHeuristic>; @@ -289,7 +289,7 @@ namespace storm { const bool mergeFailedStates = true; // Heuristic used for approximation - storm::builder::ApproximationHeuristic heuristic; + storm::builder::ApproximationHeuristic usedHeuristic; // Current id for new state size_t newIndex = 0; diff --git a/src/storage/BucketPriorityQueue.cpp b/src/storage/BucketPriorityQueue.cpp index 224b14cf6..7870888f0 100644 --- a/src/storage/BucketPriorityQueue.cpp +++ b/src/storage/BucketPriorityQueue.cpp @@ -153,7 +153,16 @@ namespace storm { template<typename ValueType> size_t BucketPriorityQueue<ValueType>::getBucket(double priority) const { STORM_LOG_ASSERT(priority >= lowerValue, "Priority " << priority << " is too low"); - size_t newBucket = std::log(priority - lowerValue) / logBase; + + // For possible values greater 1 + double tmpBucket = std::log(priority - lowerValue) / logBase; + tmpBucket += buckets.size() / 2; + if (tmpBucket < 0) { + tmpBucket = 0; + } + size_t newBucket = tmpBucket; + // For values ensured to be lower 1 + //size_t newBucket = std::log(priority - lowerValue) / logBase; if (newBucket >= nrBuckets) { newBucket = nrBuckets - 1; } diff --git a/src/storage/BucketPriorityQueue.h b/src/storage/BucketPriorityQueue.h index 1719c7124..75f065bdc 100644 --- a/src/storage/BucketPriorityQueue.h +++ b/src/storage/BucketPriorityQueue.h @@ -13,7 +13,7 @@ namespace storm { template<typename ValueType> class BucketPriorityQueue { - using HeuristicPointer = std::shared_ptr<storm::builder::DFTExplorationHeuristicProbability<ValueType>>; + using HeuristicPointer = std::shared_ptr<storm::builder::DFTExplorationHeuristicBoundDifference<ValueType>>; public: explicit BucketPriorityQueue(size_t nrBuckets, double lowerValue, double ratio); From b044b2aca48946fefca0dd769b48d292a001a5e5 Mon Sep 17 00:00:00 2001 From: sjunges <sebastian.junges@rwth-aachen.de> Date: Sun, 30 Oct 2016 23:02:55 +0100 Subject: [PATCH 62/65] .. Former-commit-id: c06171dddc48e66053252bc826d468751d3ad2b2 --- DFTASFChecker.cpp | 9 --------- DFTASFChecker.hpp | 14 -------------- 2 files changed, 23 deletions(-) delete mode 100644 DFTASFChecker.cpp delete mode 100644 DFTASFChecker.hpp diff --git a/DFTASFChecker.cpp b/DFTASFChecker.cpp deleted file mode 100644 index de7a59428..000000000 --- a/DFTASFChecker.cpp +++ /dev/null @@ -1,9 +0,0 @@ -// -// DFTASFChecker.cpp -// storm -// -// Created by Sebastian Junges on 27/10/16. -// -// - -#include "DFTASFChecker.hpp" diff --git a/DFTASFChecker.hpp b/DFTASFChecker.hpp deleted file mode 100644 index 7920f026a..000000000 --- a/DFTASFChecker.hpp +++ /dev/null @@ -1,14 +0,0 @@ -// -// DFTASFChecker.hpp -// storm -// -// Created by Sebastian Junges on 27/10/16. -// -// - -#ifndef DFTASFChecker_hpp -#define DFTASFChecker_hpp - -#include <stdio.h> - -#endif /* DFTASFChecker_hpp */ From ee4b6c96a893d608b1cb317ac7e64fbe0df56963 Mon Sep 17 00:00:00 2001 From: Mavo <matthias.volk@rwth-aachen.de> Date: Thu, 3 Nov 2016 17:58:23 +0100 Subject: [PATCH 63/65] Use operator<< from carl Former-commit-id: 733c029ebe03f2bbd56323deb62913f1ff2da6d7 --- src/builder/ExplicitDFTModelBuilderApprox.cpp | 8 ++-- src/utility/logging.h | 10 +++++ src/utility/vector.cpp | 43 ------------------- src/utility/vector.h | 6 --- 4 files changed, 14 insertions(+), 53 deletions(-) delete mode 100644 src/utility/vector.cpp diff --git a/src/builder/ExplicitDFTModelBuilderApprox.cpp b/src/builder/ExplicitDFTModelBuilderApprox.cpp index 9d802829b..2f302b0db 100644 --- a/src/builder/ExplicitDFTModelBuilderApprox.cpp +++ b/src/builder/ExplicitDFTModelBuilderApprox.cpp @@ -167,10 +167,10 @@ namespace storm { // Fix the entries in the transition matrix according to the mapping of ids to row group indices STORM_LOG_ASSERT(matrixBuilder.stateRemapping[initialStateIndex] == initialStateIndex, "Initial state should not be remapped."); // TODO Matthias: do not consider all rows? - //STORM_LOG_TRACE("Remap matrix: " << matrixBuilder.stateRemapping << ", offset: " << matrixBuilder.mappingOffset); + STORM_LOG_TRACE("Remap matrix: " << matrixBuilder.stateRemapping << ", offset: " << matrixBuilder.mappingOffset); matrixBuilder.remap(); - //STORM_LOG_TRACE("State remapping: " << matrixBuilder.stateRemapping); + STORM_LOG_TRACE("State remapping: " << matrixBuilder.stateRemapping); STORM_LOG_TRACE("Markovian states: " << modelComponents.markovianStates); STORM_LOG_DEBUG("Model has " << stateSize << " states"); STORM_LOG_DEBUG("Model is " << (generator.isDeterministicModel() ? "deterministic" : "non-deterministic")); @@ -239,7 +239,7 @@ namespace storm { matrixBuilder.stateRemapping[id] = indexRemapping[index]; } } - //STORM_LOG_TRACE("New state remapping: " << matrixBuilder.stateRemapping); + STORM_LOG_TRACE("New state remapping: " << matrixBuilder.stateRemapping); std::stringstream ss; ss << "Index remapping:" << std::endl; for (auto tmp : indexRemapping) { @@ -477,7 +477,7 @@ namespace storm { modelComponents.exitRates[stateIndex] = storm::utility::zero<ValueType>(); } } - //STORM_LOG_TRACE("Exit rates: " << modelComponents.exitRates); + STORM_LOG_TRACE("Exit rates: " << modelComponents.exitRates); std::shared_ptr<storm::models::sparse::MarkovAutomaton<ValueType>> ma; if (copy) { diff --git a/src/utility/logging.h b/src/utility/logging.h index 2fc58a008..951935e46 100644 --- a/src/utility/logging.h +++ b/src/utility/logging.h @@ -1,6 +1,16 @@ #ifndef STORM_UTILITY_LOGGING_H_ #define STORM_UTILITY_LOGGING_H_ +// Include config to know whether CARL is available or not. +#include "storm-config.h" +#ifdef STORM_HAVE_CARL +// Load streaming operator from CARL +#include <carl/io/streamingOperators.h> +namespace l3pp { + using carl::operator<<; +} +#endif + #include <l3pp.h> #if !defined(STORM_LOG_DISABLE_DEBUG) && !defined(STORM_LOG_DISABLE_TRACE) diff --git a/src/utility/vector.cpp b/src/utility/vector.cpp deleted file mode 100644 index 595af193e..000000000 --- a/src/utility/vector.cpp +++ /dev/null @@ -1,43 +0,0 @@ -#include "src/utility/vector.h" - -// Template was causing problems as Carl has the same function -/* -template<typename ValueType> -std::ostream& operator<<(std::ostream& out, std::vector<ValueType> const& vector) { - out << "vector (" << vector.size() << ") [ "; - for (uint_fast64_t i = 0; i < vector.size() - 1; ++i) { - out << vector[i] << ", "; - } - out << vector.back(); - out << " ]"; - return out; -} - -// Explicitly instantiate functions. -template std::ostream& operator<<(std::ostream& out, std::vector<double> const& vector); -template std::ostream& operator<<(std::ostream& out, std::vector<uint_fast64_t> const& vector); -*/ - -std::ostream& operator<<(std::ostream& out, std::vector<double> const& vector) { - out << "vector (" << vector.size() << ") [ "; - for (size_t i = 0; i + 1 < vector.size(); ++i) { - out << vector[i] << ", "; - } - if (!vector.empty()) { - out << vector.back(); - } - out << " ]"; - return out; -} - -std::ostream& operator<<(std::ostream& out, std::vector<uint_fast64_t> const& vector) { - out << "vector (" << vector.size() << ") [ "; - for (size_t i = 0; i + 1 < vector.size(); ++i) { - out << vector[i] << ", "; - } - if (!vector.empty()) { - out << vector.back(); - } - out << " ]"; - return out; -} diff --git a/src/utility/vector.h b/src/utility/vector.h index 059bf46ac..3257c3516 100644 --- a/src/utility/vector.h +++ b/src/utility/vector.h @@ -16,12 +16,6 @@ #include "src/utility/macros.h" #include "src/solver/OptimizationDirection.h" -// Template was causing problems as Carl has the same function -//template<typename ValueType> -//std::ostream& operator<<(std::ostream& out, std::vector<ValueType> const& vector); -std::ostream& operator<<(std::ostream& out, std::vector<double> const& vector); -std::ostream& operator<<(std::ostream& out, std::vector<uint_fast64_t> const& vector); - namespace storm { namespace utility { namespace vector { From 35a76010b7517480361349afbbfadb2ed4490216 Mon Sep 17 00:00:00 2001 From: Mavo <matthias.volk@rwth-aachen.de> Date: Wed, 9 Nov 2016 10:55:39 +0100 Subject: [PATCH 64/65] Redundant include Former-commit-id: a6aa6c8dcf2a2e8d2b613618ab40f44c58384b68 --- src/solver/AbstractEquationSolver.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/solver/AbstractEquationSolver.h b/src/solver/AbstractEquationSolver.h index 6cc03d56d..491570637 100644 --- a/src/solver/AbstractEquationSolver.h +++ b/src/solver/AbstractEquationSolver.h @@ -1,7 +1,6 @@ #ifndef STORM_SOLVER_ABSTRACTEQUATIONSOLVER_H_ #define STORM_SOLVER_ABSTRACTEQUATIONSOLVER_H_ -#include <memory> #include "src/solver/TerminationCondition.h" #include <memory> From 4d53dc49301608f68e6e6dc42273943dda9ee299 Mon Sep 17 00:00:00 2001 From: TimQu <tim.quatmann@rwth-aachen.de> Date: Thu, 3 Nov 2016 14:11:48 +0100 Subject: [PATCH 65/65] Fix in SparseMatrix::swapRows Former-commit-id: 40e2d890d4517fe8187200a8b71b6a9b462faa1e [formerly f46095e877109a553a1588c8916716c61622e976] Former-commit-id: 61fcf545cb82952cbfd9b9e1eb04c829fa12e864 --- src/storage/SparseMatrix.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/storage/SparseMatrix.cpp b/src/storage/SparseMatrix.cpp index 401fc4082..1792ae5e9 100644 --- a/src/storage/SparseMatrix.cpp +++ b/src/storage/SparseMatrix.cpp @@ -688,6 +688,9 @@ namespace storm { *writeIt = std::move(intermediateRowEntry); ++writeIt; } + } else { + // skip the intermediate rows + writeIt = getRow(smallerRow).begin(); } // Write the larger row to its new position. @@ -720,6 +723,9 @@ namespace storm { *writeIt = std::move(*intermediateRowEntryIt); --writeIt; } + } else { + // skip the intermediate rows + writeIt = getRow(smallerRow).end() - 1; } // Write the larger row to its new position.