Browse Source

A lot of work on model checker interfaces. In particular, the SCC elimination model checker is almost integrated.

Former-commit-id: bbf988c943
main
dehnert 10 years ago
parent
commit
8a4706d9c9
  1. 2
      CMakeLists.txt
  2. 6
      src/counterexamples/MILPMinimalLabelSetGenerator.h
  3. 6
      src/counterexamples/SMTMinimalCommandSetGenerator.h
  4. 47
      src/logic/ExpectedTimeOperatorFormula.cpp
  5. 31
      src/logic/ExpectedTimeOperatorFormula.h
  6. 22
      src/logic/Formula.cpp
  7. 13
      src/logic/Formula.h
  8. 3
      src/logic/Formulas.h
  9. 47
      src/logic/LongRunAverageOperatorFormula.cpp
  10. 31
      src/logic/LongRunAverageOperatorFormula.h
  11. 47
      src/logic/SteadyStateOperatorFormula.cpp
  12. 31
      src/logic/SteadyStateOperatorFormula.h
  13. 72
      src/modelchecker/AbstractModelChecker.cpp
  14. 9
      src/modelchecker/AbstractModelChecker.h
  15. 2
      src/modelchecker/CheckResult.cpp
  16. 1
      src/modelchecker/CheckResult.h
  17. 130
      src/modelchecker/ExplicitQualitativeCheckResult.cpp
  18. 41
      src/modelchecker/ExplicitQualitativeCheckResult.h
  19. 199
      src/modelchecker/ExplicitQuantitativeCheckResult.cpp
  20. 39
      src/modelchecker/ExplicitQuantitativeCheckResult.h
  21. 95
      src/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.cpp
  22. 12
      src/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.h
  23. 20
      src/modelchecker/prctl/SparseDtmcPrctlModelChecker.cpp
  24. 20
      src/modelchecker/prctl/SparseMdpPrctlModelChecker.cpp
  25. 864
      src/modelchecker/reachability/SparseDtmcEliminationModelChecker.cpp
  26. 75
      src/modelchecker/reachability/SparseDtmcEliminationModelChecker.h
  27. 435
      src/modelchecker/reachability/SparseSccModelChecker.cpp
  28. 43
      src/modelchecker/reachability/SparseSccModelChecker.h
  29. 15
      src/parser/FormulaParser.cpp
  30. 4
      src/parser/FormulaParser.h
  31. 5
      src/settings/SettingsManager.cpp
  32. 8
      src/settings/SettingsManager.h
  33. 64
      src/settings/modules/SparseDtmcEliminationModelCheckerSettings.cpp
  34. 73
      src/settings/modules/SparseDtmcEliminationModelCheckerSettings.h
  35. 123
      src/utility/ConstantsComparator.cpp
  36. 58
      src/utility/ConstantsComparator.h

2
CMakeLists.txt

@ -258,6 +258,7 @@ file(GLOB_RECURSE STORM_LOGIC_FILES ${PROJECT_SOURCE_DIR}/src/logic/*.h ${PROJEC
file(GLOB STORM_MODELCHECKER_FILES ${PROJECT_SOURCE_DIR}/src/modelchecker/*.h ${PROJECT_SOURCE_DIR}/src/modelchecker/*.cpp)
file(GLOB_RECURSE STORM_MODELCHECKER_PRCTL_FILES ${PROJECT_SOURCE_DIR}/src/modelchecker/prctl/*.h ${PROJECT_SOURCE_DIR}/src/modelchecker/prctl/*.cpp)
file(GLOB_RECURSE STORM_MODELCHECKER_CSL_FILES ${PROJECT_SOURCE_DIR}/src/modelchecker/csl/*.h ${PROJECT_SOURCE_DIR}/src/modelchecker/csl/*.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_COUNTEREXAMPLES_FILES ${PROJECT_SOURCE_DIR}/src/counterexamples/*.h ${PROJECT_SOURCE_DIR}/src/counterexamples/*.cpp)
file(GLOB_RECURSE STORM_MODELS_FILES ${PROJECT_SOURCE_DIR}/src/models/*.h ${PROJECT_SOURCE_DIR}/src/models/*.cpp)
file(GLOB STORM_PARSER_FILES ${PROJECT_SOURCE_DIR}/src/parser/*.h ${PROJECT_SOURCE_DIR}/src/parser/*.cpp)
@ -292,6 +293,7 @@ source_group(generated FILES ${STORM_BUILD_HEADERS} ${STORM_BUILD_SOURCES})
source_group(modelchecker FILES ${STORM_MODELCHECKER_FILES})
source_group(modelchecker\\prctl FILES ${STORM_MODELCHECKER_PRCTL_FILES})
source_group(modelchecker\\csl FILES ${STORM_MODELCHECKER_CSL_FILES})
source_group(modelchecker\\reachability FILES ${STORM_MODELCHECKER_REACHABILITY_FILES})
source_group(counterexamples FILES ${STORM_COUNTEREXAMPLES_FILES})
source_group(models FILES ${STORM_MODELS_FILES})
source_group(parser FILES ${STORM_PARSER_FILES})

6
src/counterexamples/MILPMinimalLabelSetGenerator.h

@ -1000,8 +1000,8 @@ namespace storm {
storm::modelchecker::ExplicitQualitativeCheckResult const& leftQualitativeResult = leftResult->asExplicitQualitativeCheckResult();
storm::modelchecker::ExplicitQualitativeCheckResult const& rightQualitativeResult = rightResult->asExplicitQualitativeCheckResult();
phiStates = leftQualitativeResult.getTruthValues();
psiStates = rightQualitativeResult.getTruthValues();
phiStates = leftQualitativeResult.getTruthValuesVector();
psiStates = rightQualitativeResult.getTruthValuesVector();
} else if (probabilityOperator.getSubformula().isEventuallyFormula()) {
storm::logic::EventuallyFormula const& eventuallyFormula = probabilityOperator.getSubformula().asEventuallyFormula();
@ -1010,7 +1010,7 @@ namespace storm {
storm::modelchecker::ExplicitQualitativeCheckResult const& subQualitativeResult = subResult->asExplicitQualitativeCheckResult();
phiStates = storm::storage::BitVector(labeledMdp.getNumberOfStates(), true);
psiStates = subQualitativeResult.getTruthValues();
psiStates = subQualitativeResult.getTruthValuesVector();
}
// Delegate the actual computation work to the function of equal name.

6
src/counterexamples/SMTMinimalCommandSetGenerator.h

@ -1779,8 +1779,8 @@ namespace storm {
storm::modelchecker::ExplicitQualitativeCheckResult const& leftQualitativeResult = leftResult.asExplicitQualitativeCheckResult();
storm::modelchecker::ExplicitQualitativeCheckResult const& rightQualitativeResult = rightResult.asExplicitQualitativeCheckResult();
phiStates = leftQualitativeResult.getTruthValues();
psiStates = rightQualitativeResult.getTruthValues();
phiStates = leftQualitativeResult.getTruthValuesVector();
psiStates = rightQualitativeResult.getTruthValuesVector();
} else if (probabilityOperator.getSubformula().isEventuallyFormula()) {
storm::logic::EventuallyFormula const& eventuallyFormula = probabilityOperator.getSubformula().asEventuallyFormula();
@ -1789,7 +1789,7 @@ namespace storm {
storm::modelchecker::ExplicitQualitativeCheckResult const& subQualitativeResult = subResult.asExplicitQualitativeCheckResult();
phiStates = storm::storage::BitVector(labeledMdp.getNumberOfStates(), true);
psiStates = subResult.getTruthValues();
psiStates = subResult.getTruthValuesVector();
}
// Delegate the actual computation work to the function of equal name.

47
src/logic/ExpectedTimeOperatorFormula.cpp

@ -0,0 +1,47 @@
#include "src/logic/ExpectedTimeOperatorFormula.h"
namespace storm {
namespace logic {
ExpectedTimeOperatorFormula::ExpectedTimeOperatorFormula(std::shared_ptr<Formula const> const& subformula) : ExpectedTimeOperatorFormula(boost::optional<OptimalityType>(), boost::optional<ComparisonType>(), boost::optional<double>(), subformula) {
// Intentionally left empty.
}
ExpectedTimeOperatorFormula::ExpectedTimeOperatorFormula(ComparisonType comparisonType, double bound, std::shared_ptr<Formula const> const& subformula) : ExpectedTimeOperatorFormula(boost::optional<OptimalityType>(), boost::optional<ComparisonType>(comparisonType), boost::optional<double>(bound), subformula) {
// Intentionally left empty.
}
ExpectedTimeOperatorFormula::ExpectedTimeOperatorFormula(OptimalityType optimalityType, ComparisonType comparisonType, double bound, std::shared_ptr<Formula const> const& subformula) : ExpectedTimeOperatorFormula(boost::optional<OptimalityType>(optimalityType), boost::optional<ComparisonType>(comparisonType), boost::optional<double>(bound), subformula) {
// Intentionally left empty.
}
ExpectedTimeOperatorFormula::ExpectedTimeOperatorFormula(OptimalityType optimalityType, std::shared_ptr<Formula const> const& subformula) : ExpectedTimeOperatorFormula(boost::optional<OptimalityType>(optimalityType), boost::optional<ComparisonType>(), boost::optional<double>(), subformula) {
// Intentionally left empty.
}
bool ExpectedTimeOperatorFormula::isExpectedTimeOperatorFormula() const {
return true;
}
bool ExpectedTimeOperatorFormula::isPctlStateFormula() const {
return this->getSubformula().isPctlStateFormula();
}
bool ExpectedTimeOperatorFormula::hasProbabilityOperator() const {
return this->getSubformula().hasProbabilityOperator();
}
bool ExpectedTimeOperatorFormula::hasNestedProbabilityOperators() const {
return this->getSubformula().hasNestedProbabilityOperators();
}
ExpectedTimeOperatorFormula::ExpectedTimeOperatorFormula(boost::optional<OptimalityType> optimalityType, boost::optional<ComparisonType> comparisonType, boost::optional<double> bound, std::shared_ptr<Formula const> const& subformula) : OperatorFormula(optimalityType, comparisonType, bound, subformula) {
// Intentionally left empty.
}
std::ostream& ExpectedTimeOperatorFormula::writeToStream(std::ostream& out) const {
out << "ET";
OperatorFormula::writeToStream(out);
return out;
}
}
}

31
src/logic/ExpectedTimeOperatorFormula.h

@ -0,0 +1,31 @@
#ifndef STORM_LOGIC_EXPECTEDTIMEOPERATORFORMULA_H_
#define STORM_LOGIC_EXPECTEDTIMEOPERATORFORMULA_H_
#include "src/logic/OperatorFormula.h"
namespace storm {
namespace logic {
class ExpectedTimeOperatorFormula : public OperatorFormula {
public:
ExpectedTimeOperatorFormula(std::shared_ptr<Formula const> const& subformula);
ExpectedTimeOperatorFormula(ComparisonType comparisonType, double bound, std::shared_ptr<Formula const> const& subformula);
ExpectedTimeOperatorFormula(OptimalityType optimalityType, ComparisonType comparisonType, double bound, std::shared_ptr<Formula const> const& subformula);
ExpectedTimeOperatorFormula(OptimalityType optimalityType, std::shared_ptr<Formula const> const& subformula);
ExpectedTimeOperatorFormula(boost::optional<OptimalityType> optimalityType, boost::optional<ComparisonType> comparisonType, boost::optional<double> bound, std::shared_ptr<Formula const> const& subformula);
virtual ~ExpectedTimeOperatorFormula() {
// Intentionally left empty.
}
virtual bool isExpectedTimeOperatorFormula() const override;
virtual bool isPctlStateFormula() const override;
virtual bool hasProbabilityOperator() const override;
virtual bool hasNestedProbabilityOperators() const override;
virtual std::ostream& writeToStream(std::ostream& out) const override;
};
}
}
#endif /* STORM_LOGIC_EXPECTEDTIMEOPERATORFORMULA_H_ */

22
src/logic/Formula.cpp

@ -78,7 +78,11 @@ namespace storm {
return false;
}
bool Formula::isSteadyStateOperatorFormula() const {
bool Formula::isLongRunAverageOperatorFormula() const {
return false;
}
bool Formula::isExpectedTimeOperatorFormula() const {
return false;
}
@ -282,12 +286,20 @@ namespace storm {
return dynamic_cast<NextFormula const&>(*this);
}
SteadyStateOperatorFormula& Formula::asSteadyStateOperatorFormula() {
return dynamic_cast<SteadyStateOperatorFormula&>(*this);
LongRunAverageOperatorFormula& Formula::asLongRunAverageOperatorFormula() {
return dynamic_cast<LongRunAverageOperatorFormula&>(*this);
}
LongRunAverageOperatorFormula const& Formula::asLongRunAverageOperatorFormula() const {
return dynamic_cast<LongRunAverageOperatorFormula const&>(*this);
}
ExpectedTimeOperatorFormula& Formula::asExpectedTimeOperatorFormula() {
return dynamic_cast<ExpectedTimeOperatorFormula&>(*this);
}
SteadyStateOperatorFormula const& Formula::asSteadyStateOperatorFormula() const {
return dynamic_cast<SteadyStateOperatorFormula const&>(*this);
ExpectedTimeOperatorFormula const& Formula::asExpectedTimeOperatorFormula() const {
return dynamic_cast<ExpectedTimeOperatorFormula const&>(*this);
}
RewardPathFormula& Formula::asRewardPathFormula() {

13
src/logic/Formula.h

@ -26,7 +26,8 @@ namespace storm {
class UnaryPathFormula;
class ConditionalPathFormula;
class NextFormula;
class SteadyStateOperatorFormula;
class LongRunAverageOperatorFormula;
class ExpectedTimeOperatorFormula;
class RewardPathFormula;
class CumulativeRewardFormula;
class InstantaneousRewardFormula;
@ -66,7 +67,8 @@ namespace storm {
virtual bool isUnaryPathFormula() const;
virtual bool isConditionalPathFormula() const;
virtual bool isNextFormula() const;
virtual bool isSteadyStateOperatorFormula() const;
virtual bool isLongRunAverageOperatorFormula() const;
virtual bool isExpectedTimeOperatorFormula() const;
virtual bool isRewardPathFormula() const;
virtual bool isCumulativeRewardFormula() const;
virtual bool isInstantaneousRewardFormula() const;
@ -137,8 +139,11 @@ namespace storm {
NextFormula& asNextFormula();
NextFormula const& asNextFormula() const;
SteadyStateOperatorFormula& asSteadyStateOperatorFormula();
SteadyStateOperatorFormula const& asSteadyStateOperatorFormula() const;
LongRunAverageOperatorFormula& asLongRunAverageOperatorFormula();
LongRunAverageOperatorFormula const& asLongRunAverageOperatorFormula() const;
ExpectedTimeOperatorFormula& asExpectedTimeOperatorFormula();
ExpectedTimeOperatorFormula const& asExpectedTimeOperatorFormula() const;
RewardPathFormula& asRewardPathFormula();
RewardPathFormula const& asRewardPathFormula() const;

3
src/logic/Formulas.h

@ -17,7 +17,8 @@
#include "src/logic/ReachabilityRewardFormula.h"
#include "src/logic/RewardOperatorFormula.h"
#include "src/logic/StateFormula.h"
#include "src/logic/SteadyStateOperatorFormula.h"
#include "src/logic/LongRunAverageOperatorFormula.h"
#include "src/logic/ExpectedTimeOperatorFormula.h"
#include "src/logic/UnaryBooleanStateFormula.h"
#include "src/logic/UnaryPathFormula.h"
#include "src/logic/UnaryStateFormula.h"

47
src/logic/LongRunAverageOperatorFormula.cpp

@ -0,0 +1,47 @@
#include "src/logic/LongRunAverageOperatorFormula.h"
namespace storm {
namespace logic {
LongRunAverageOperatorFormula::LongRunAverageOperatorFormula(std::shared_ptr<Formula const> const& subformula) : LongRunAverageOperatorFormula(boost::optional<OptimalityType>(), boost::optional<ComparisonType>(), boost::optional<double>(), subformula) {
// Intentionally left empty.
}
LongRunAverageOperatorFormula::LongRunAverageOperatorFormula(ComparisonType comparisonType, double bound, std::shared_ptr<Formula const> const& subformula) : LongRunAverageOperatorFormula(boost::optional<OptimalityType>(), boost::optional<ComparisonType>(comparisonType), boost::optional<double>(bound), subformula) {
// Intentionally left empty.
}
LongRunAverageOperatorFormula::LongRunAverageOperatorFormula(OptimalityType optimalityType, ComparisonType comparisonType, double bound, std::shared_ptr<Formula const> const& subformula) : LongRunAverageOperatorFormula(boost::optional<OptimalityType>(optimalityType), boost::optional<ComparisonType>(comparisonType), boost::optional<double>(bound), subformula) {
// Intentionally left empty.
}
LongRunAverageOperatorFormula::LongRunAverageOperatorFormula(OptimalityType optimalityType, std::shared_ptr<Formula const> const& subformula) : LongRunAverageOperatorFormula(boost::optional<OptimalityType>(optimalityType), boost::optional<ComparisonType>(), boost::optional<double>(), subformula) {
// Intentionally left empty.
}
bool LongRunAverageOperatorFormula::isLongRunAverageOperatorFormula() const {
return true;
}
bool LongRunAverageOperatorFormula::isPctlStateFormula() const {
return this->getSubformula().isPctlStateFormula();
}
bool LongRunAverageOperatorFormula::hasProbabilityOperator() const {
return this->getSubformula().hasProbabilityOperator();
}
bool LongRunAverageOperatorFormula::hasNestedProbabilityOperators() const {
return this->getSubformula().hasNestedProbabilityOperators();
}
LongRunAverageOperatorFormula::LongRunAverageOperatorFormula(boost::optional<OptimalityType> optimalityType, boost::optional<ComparisonType> comparisonType, boost::optional<double> bound, std::shared_ptr<Formula const> const& subformula) : OperatorFormula(optimalityType, comparisonType, bound, subformula) {
// Intentionally left empty.
}
std::ostream& LongRunAverageOperatorFormula::writeToStream(std::ostream& out) const {
out << "LRA";
OperatorFormula::writeToStream(out);
return out;
}
}
}

31
src/logic/LongRunAverageOperatorFormula.h

@ -0,0 +1,31 @@
#ifndef STORM_LOGIC_LONGRUNAVERAGEOPERATORFORMULA_H_
#define STORM_LOGIC_LONGRUNAVERAGEOPERATORFORMULA_H_
#include "src/logic/OperatorFormula.h"
namespace storm {
namespace logic {
class LongRunAverageOperatorFormula : public OperatorFormula {
public:
LongRunAverageOperatorFormula(std::shared_ptr<Formula const> const& subformula);
LongRunAverageOperatorFormula(ComparisonType comparisonType, double bound, std::shared_ptr<Formula const> const& subformula);
LongRunAverageOperatorFormula(OptimalityType optimalityType, ComparisonType comparisonType, double bound, std::shared_ptr<Formula const> const& subformula);
LongRunAverageOperatorFormula(OptimalityType optimalityType, std::shared_ptr<Formula const> const& subformula);
LongRunAverageOperatorFormula(boost::optional<OptimalityType> optimalityType, boost::optional<ComparisonType> comparisonType, boost::optional<double> bound, std::shared_ptr<Formula const> const& subformula);
virtual ~LongRunAverageOperatorFormula() {
// Intentionally left empty.
}
virtual bool isLongRunAverageOperatorFormula() const override;
virtual bool isPctlStateFormula() const override;
virtual bool hasProbabilityOperator() const override;
virtual bool hasNestedProbabilityOperators() const override;
virtual std::ostream& writeToStream(std::ostream& out) const override;
};
}
}
#endif /* STORM_LOGIC_LONGRUNAVERAGEOPERATORFORMULA_H_ */

47
src/logic/SteadyStateOperatorFormula.cpp

@ -1,47 +0,0 @@
#include "src/logic/SteadyStateOperatorFormula.h"
namespace storm {
namespace logic {
SteadyStateOperatorFormula::SteadyStateOperatorFormula(std::shared_ptr<Formula const> const& subformula) : SteadyStateOperatorFormula(boost::optional<OptimalityType>(), boost::optional<ComparisonType>(), boost::optional<double>(), subformula) {
// Intentionally left empty.
}
SteadyStateOperatorFormula::SteadyStateOperatorFormula(ComparisonType comparisonType, double bound, std::shared_ptr<Formula const> const& subformula) : SteadyStateOperatorFormula(boost::optional<OptimalityType>(), boost::optional<ComparisonType>(comparisonType), boost::optional<double>(bound), subformula) {
// Intentionally left empty.
}
SteadyStateOperatorFormula::SteadyStateOperatorFormula(OptimalityType optimalityType, ComparisonType comparisonType, double bound, std::shared_ptr<Formula const> const& subformula) : SteadyStateOperatorFormula(boost::optional<OptimalityType>(optimalityType), boost::optional<ComparisonType>(comparisonType), boost::optional<double>(bound), subformula) {
// Intentionally left empty.
}
SteadyStateOperatorFormula::SteadyStateOperatorFormula(OptimalityType optimalityType, std::shared_ptr<Formula const> const& subformula) : SteadyStateOperatorFormula(boost::optional<OptimalityType>(optimalityType), boost::optional<ComparisonType>(), boost::optional<double>(), subformula) {
// Intentionally left empty.
}
bool SteadyStateOperatorFormula::isSteadyStateOperatorFormula() const {
return true;
}
bool SteadyStateOperatorFormula::isPctlStateFormula() const {
return this->getSubformula().isPctlStateFormula();
}
bool SteadyStateOperatorFormula::hasProbabilityOperator() const {
return this->getSubformula().hasProbabilityOperator();
}
bool SteadyStateOperatorFormula::hasNestedProbabilityOperators() const {
return this->getSubformula().hasNestedProbabilityOperators();
}
SteadyStateOperatorFormula::SteadyStateOperatorFormula(boost::optional<OptimalityType> optimalityType, boost::optional<ComparisonType> comparisonType, boost::optional<double> bound, std::shared_ptr<Formula const> const& subformula) : OperatorFormula(optimalityType, comparisonType, bound, subformula) {
// Intentionally left empty.
}
std::ostream& SteadyStateOperatorFormula::writeToStream(std::ostream& out) const {
out << "S";
OperatorFormula::writeToStream(out);
return out;
}
}
}

31
src/logic/SteadyStateOperatorFormula.h

@ -1,31 +0,0 @@
#ifndef STORM_LOGIC_STEADYSTATEFORMULA_H_
#define STORM_LOGIC_STEADYSTATEFORMULA_H_
#include "src/logic/OperatorFormula.h"
namespace storm {
namespace logic {
class SteadyStateOperatorFormula : public OperatorFormula {
public:
SteadyStateOperatorFormula(std::shared_ptr<Formula const> const& subformula);
SteadyStateOperatorFormula(ComparisonType comparisonType, double bound, std::shared_ptr<Formula const> const& subformula);
SteadyStateOperatorFormula(OptimalityType optimalityType, ComparisonType comparisonType, double bound, std::shared_ptr<Formula const> const& subformula);
SteadyStateOperatorFormula(OptimalityType optimalityType, std::shared_ptr<Formula const> const& subformula);
SteadyStateOperatorFormula(boost::optional<OptimalityType> optimalityType, boost::optional<ComparisonType> comparisonType, boost::optional<double> bound, std::shared_ptr<Formula const> const& subformula);
virtual ~SteadyStateOperatorFormula() {
// Intentionally left empty.
}
virtual bool isSteadyStateOperatorFormula() const override;
virtual bool isPctlStateFormula() const override;
virtual bool hasProbabilityOperator() const override;
virtual bool hasNestedProbabilityOperators() const override;
virtual std::ostream& writeToStream(std::ostream& out) const override;
};
}
}
#endif /* STORM_LOGIC_STEADYSTATEFORMULA_H_ */

72
src/modelchecker/AbstractModelChecker.cpp

@ -81,6 +81,14 @@ namespace storm {
STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "This model checker does not support the formula: " << rewardPathFormula << ".");
}
std::unique_ptr<CheckResult> AbstractModelChecker::computeLongRunAverage(storm::logic::StateFormula const& eventuallyFormula, bool qualitative, boost::optional<storm::logic::OptimalityType> const& optimalityType) {
STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "This model checker does not support the computation of long-run averages.");
}
std::unique_ptr<CheckResult> AbstractModelChecker::computeExpectedTimes(storm::logic::EventuallyFormula const& eventuallyFormula, bool qualitative, boost::optional<storm::logic::OptimalityType> const& optimalityType) {
STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "This model checker does not support the computation of expected times.");
}
std::unique_ptr<CheckResult> AbstractModelChecker::checkStateFormula(storm::logic::StateFormula const& stateFormula) {
if (stateFormula.isBinaryBooleanStateFormula()) {
return this->checkBinaryBooleanStateFormula(stateFormula.asBinaryBooleanStateFormula());
@ -92,8 +100,10 @@ namespace storm {
return this->checkProbabilityOperatorFormula(stateFormula.asProbabilityOperatorFormula());
} else if (stateFormula.isRewardOperatorFormula()) {
return this->checkRewardOperatorFormula(stateFormula.asRewardOperatorFormula());
} else if (stateFormula.isSteadyStateOperatorFormula()) {
return this->checkSteadyStateOperatorFormula(stateFormula.asSteadyStateOperatorFormula());
} else if (stateFormula.isExpectedTimeOperatorFormula()) {
return this->checkExpectedTimeOperatorFormula(stateFormula.asExpectedTimeOperatorFormula());
} else if (stateFormula.isLongRunAverageOperatorFormula()) {
return this->checkLongRunAverageOperatorFormula(stateFormula.asLongRunAverageOperatorFormula());
} else if (stateFormula.isAtomicExpressionFormula()) {
return this->checkAtomicExpressionFormula(stateFormula.asAtomicExpressionFormula());
} else if (stateFormula.isAtomicLabelFormula()) {
@ -161,22 +171,68 @@ namespace storm {
std::unique_ptr<CheckResult> AbstractModelChecker::checkRewardOperatorFormula(storm::logic::RewardOperatorFormula const& stateFormula) {
STORM_LOG_THROW(stateFormula.getSubformula().isRewardPathFormula(), storm::exceptions::InvalidArgumentException, "The given formula is invalid.");
// If the probability bound is 0, is suffices to do qualitative model checking.
// If the reward bound is 0, is suffices to do qualitative model checking.
bool qualitative = false;
if (stateFormula.hasBound()) {
if (stateFormula.getBound() == storm::utility::zero<double>() || stateFormula.getBound() == storm::utility::one<double>()) {
if (stateFormula.getBound() == storm::utility::zero<double>()) {
qualitative = true;
}
}
std::unique_ptr<CheckResult> result;
if (stateFormula.hasOptimalityType()) {
return this->computeRewards(stateFormula.getSubformula().asRewardPathFormula(), qualitative, stateFormula.getOptimalityType());
result = this->computeRewards(stateFormula.getSubformula().asRewardPathFormula(), qualitative, stateFormula.getOptimalityType());
} else {
result = this->computeRewards(stateFormula.getSubformula().asRewardPathFormula(), qualitative);
}
if (stateFormula.hasBound()) {
return result->compareAgainstBound(stateFormula.getComparisonType(), stateFormula.getBound());
} else {
return this->computeRewards(stateFormula.getSubformula().asRewardPathFormula(), qualitative);
return result;
}
}
std::unique_ptr<CheckResult> AbstractModelChecker::checkSteadyStateOperatorFormula(storm::logic::SteadyStateOperatorFormula const& stateFormula) {
STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "This model checker does not support the formula: " << stateFormula << ".");
std::unique_ptr<CheckResult> AbstractModelChecker::checkExpectedTimeOperatorFormula(storm::logic::ExpectedTimeOperatorFormula const& stateFormula) {
STORM_LOG_THROW(stateFormula.getSubformula().isStateFormula(), storm::exceptions::InvalidArgumentException, "The given formula is invalid.");
// If the reward bound is 0, is suffices to do qualitative model checking.
bool qualitative = false;
if (stateFormula.hasBound()) {
if (stateFormula.getBound() == storm::utility::zero<double>()) {
qualitative = true;
}
}
std::unique_ptr<CheckResult> result;
if (stateFormula.hasOptimalityType()) {
result = this->computeExpectedTimes(stateFormula.getSubformula().asEventuallyFormula(), qualitative, stateFormula.getOptimalityType());
} else {
result = this->computeExpectedTimes(stateFormula.getSubformula().asEventuallyFormula(), qualitative);
}
if (stateFormula.hasBound()) {
return result->compareAgainstBound(stateFormula.getComparisonType(), stateFormula.getBound());
} else {
return result;
}
}
std::unique_ptr<CheckResult> AbstractModelChecker::checkLongRunAverageOperatorFormula(storm::logic::LongRunAverageOperatorFormula const& stateFormula) {
STORM_LOG_THROW(stateFormula.getSubformula().isEventuallyFormula(), storm::exceptions::InvalidArgumentException, "The given formula is invalid.");
if (stateFormula.hasOptimalityType()) {
return this->computeLongRunAverage(stateFormula.getSubformula().asStateFormula(), false, stateFormula.getOptimalityType());
} else {
return this->computeLongRunAverage(stateFormula.getSubformula().asStateFormula(), false);
}
std::unique_ptr<CheckResult> result;
if (stateFormula.hasBound()) {
return result->compareAgainstBound(stateFormula.getComparisonType(), stateFormula.getBound());
} else {
return result;
}
}
std::unique_ptr<CheckResult> AbstractModelChecker::checkUnaryBooleanStateFormula(storm::logic::UnaryBooleanStateFormula const& stateFormula) {

9
src/modelchecker/AbstractModelChecker.h

@ -38,13 +38,17 @@ namespace storm {
virtual std::unique_ptr<CheckResult> computeGloballyProbabilities(storm::logic::GloballyFormula const& pathFormula, bool qualitative = false, boost::optional<storm::logic::OptimalityType> const& optimalityType = boost::optional<storm::logic::OptimalityType>());
virtual std::unique_ptr<CheckResult> computeNextProbabilities(storm::logic::NextFormula const& pathFormula, bool qualitative = false, boost::optional<storm::logic::OptimalityType> const& optimalityType = boost::optional<storm::logic::OptimalityType>());
virtual std::unique_ptr<CheckResult> computeUntilProbabilities(storm::logic::UntilFormula const& pathFormula, bool qualitative = false, boost::optional<storm::logic::OptimalityType> const& optimalityType = boost::optional<storm::logic::OptimalityType>());
// The methods to compute the rewards for path formulas.
virtual std::unique_ptr<CheckResult> computeRewards(storm::logic::RewardPathFormula const& rewardPathFormula, bool qualitative = false, boost::optional<storm::logic::OptimalityType> const& optimalityType = boost::optional<storm::logic::OptimalityType>());
virtual std::unique_ptr<CheckResult> computeCumulativeRewards(storm::logic::CumulativeRewardFormula const& rewardPathFormula, bool qualitative = false, boost::optional<storm::logic::OptimalityType> const& optimalityType = boost::optional<storm::logic::OptimalityType>());
virtual std::unique_ptr<CheckResult> computeInstantaneousRewards(storm::logic::InstantaneousRewardFormula const& rewardPathFormula, bool qualitative = false, boost::optional<storm::logic::OptimalityType> const& optimalityType = boost::optional<storm::logic::OptimalityType>());
virtual std::unique_ptr<CheckResult> computeReachabilityRewards(storm::logic::ReachabilityRewardFormula const& rewardPathFormula, bool qualitative = false, boost::optional<storm::logic::OptimalityType> const& optimalityType = boost::optional<storm::logic::OptimalityType>());
// The methods to compute the long-run average and expected time.
virtual std::unique_ptr<CheckResult> computeLongRunAverage(storm::logic::StateFormula const& stateFormula, bool qualitative = false, boost::optional<storm::logic::OptimalityType> const& optimalityType = boost::optional<storm::logic::OptimalityType>());
virtual std::unique_ptr<CheckResult> computeExpectedTimes(storm::logic::EventuallyFormula const& eventuallyFormula, bool qualitative = false, boost::optional<storm::logic::OptimalityType> const& optimalityType = boost::optional<storm::logic::OptimalityType>());
// The methods to check state formulas.
virtual std::unique_ptr<CheckResult> checkStateFormula(storm::logic::StateFormula const& stateFormula);
virtual std::unique_ptr<CheckResult> checkAtomicExpressionFormula(storm::logic::AtomicExpressionFormula const& stateFormula);
@ -53,7 +57,8 @@ namespace storm {
virtual std::unique_ptr<CheckResult> checkBooleanLiteralFormula(storm::logic::BooleanLiteralFormula const& stateFormula);
virtual std::unique_ptr<CheckResult> checkProbabilityOperatorFormula(storm::logic::ProbabilityOperatorFormula const& stateFormula);
virtual std::unique_ptr<CheckResult> checkRewardOperatorFormula(storm::logic::RewardOperatorFormula const& stateFormula);
virtual std::unique_ptr<CheckResult> checkSteadyStateOperatorFormula(storm::logic::SteadyStateOperatorFormula const& stateFormula);
virtual std::unique_ptr<CheckResult> checkExpectedTimeOperatorFormula(storm::logic::ExpectedTimeOperatorFormula const& stateFormula);
virtual std::unique_ptr<CheckResult> checkLongRunAverageOperatorFormula(storm::logic::LongRunAverageOperatorFormula const& stateFormula);
virtual std::unique_ptr<CheckResult> checkUnaryBooleanStateFormula(storm::logic::UnaryBooleanStateFormula const& stateFormula);
};
}

2
src/modelchecker/CheckResult.cpp

@ -52,7 +52,7 @@ namespace storm {
bool CheckResult::isExplicitQuantitativeCheckResult() const {
return false;
}
ExplicitQualitativeCheckResult& CheckResult::asExplicitQualitativeCheckResult() {
return dynamic_cast<ExplicitQualitativeCheckResult&>(*this);
}

1
src/modelchecker/CheckResult.h

@ -31,6 +31,7 @@ namespace storm {
virtual bool isExplicitQualitativeCheckResult() const;
virtual bool isExplicitQuantitativeCheckResult() const;
virtual bool isExplicitSingleStateQuantitativeCheckResult() const;
ExplicitQualitativeCheckResult& asExplicitQualitativeCheckResult();
ExplicitQualitativeCheckResult const& asExplicitQualitativeCheckResult() const;

130
src/modelchecker/ExplicitQualitativeCheckResult.cpp

@ -5,30 +5,90 @@
namespace storm {
namespace modelchecker {
ExplicitQualitativeCheckResult::ExplicitQualitativeCheckResult() : truthValues(map_type()) {
// Intentionally left empty.
}
ExplicitQualitativeCheckResult::ExplicitQualitativeCheckResult(map_type const& map) : truthValues(map) {
// Intentionally left empty.
}
ExplicitQualitativeCheckResult::ExplicitQualitativeCheckResult(map_type&& map) : truthValues(map) {
// Intentionally left empty.
}
ExplicitQualitativeCheckResult::ExplicitQualitativeCheckResult(storm::storage::sparse::state_type state, bool value) : truthValues(map_type()) {
boost::get<map_type>(truthValues)[state] = value;
}
ExplicitQualitativeCheckResult::ExplicitQualitativeCheckResult(storm::storage::BitVector const& truthValues) : truthValues(truthValues) {
// Intentionally left empty.
}
ExplicitQualitativeCheckResult::ExplicitQualitativeCheckResult(storm::storage::BitVector&& truthValues) : truthValues(std::move(truthValues)) {
// Intentionally left empty.
}
void ExplicitQualitativeCheckResult::performLogicalOperation(ExplicitQualitativeCheckResult& first, CheckResult const& second, std::function<bool (bool, bool)> const& function) {
STORM_LOG_THROW(typeid(second) == typeid(ExplicitQualitativeCheckResult), storm::exceptions::InvalidOperationException, "Cannot perform logical 'and' on check results of incompatible type.");
STORM_LOG_THROW(first.isResultForAllStates() == second.isResultForAllStates(), storm::exceptions::InvalidOperationException, "Cannot perform logical 'and' on check results of incompatible type.");
ExplicitQualitativeCheckResult const& secondCheckResult = static_cast<ExplicitQualitativeCheckResult const&>(second);
if (first.isResultForAllStates()) {
boost::get<vector_type>(first.truthValues) &= boost::get<vector_type>(secondCheckResult.truthValues);
} else {
map_type& map1 = boost::get<map_type>(first.truthValues);
map_type const& map2 = boost::get<map_type>(secondCheckResult.truthValues);
for (auto& element1 : map1) {
auto const& keyValuePair = map2.find(element1.first);
STORM_LOG_THROW(keyValuePair != map2.end(), storm::exceptions::InvalidOperationException, "Cannot perform logical 'and' on check results of incompatible type.");
element1.second = function(element1.second, keyValuePair->second);
}
// Double-check that there are no entries in map2 that the current result does not have.
for (auto const& element2 : map2) {
auto const& keyValuePair = map1.find(element2.first);
STORM_LOG_THROW(keyValuePair != map1.end(), storm::exceptions::InvalidOperationException, "Cannot perform logical 'and' on check results of incompatible type.");
}
}
}
CheckResult& ExplicitQualitativeCheckResult::operator&=(CheckResult const& other) {
STORM_LOG_THROW(typeid(other) == typeid(ExplicitQualitativeCheckResult), storm::exceptions::InvalidOperationException, "Cannot perform logical 'and' on check results of incompatible type.");
ExplicitQualitativeCheckResult const& otherCheckResult = static_cast<ExplicitQualitativeCheckResult const&>(other);
this->truthValues &= otherCheckResult.truthValues;
performLogicalOperation(*this, other, [] (bool a, bool b) { return a && b; });
return *this;
}
CheckResult& ExplicitQualitativeCheckResult::operator|=(CheckResult const& other) {
STORM_LOG_THROW(typeid(other) == typeid(ExplicitQualitativeCheckResult), storm::exceptions::InvalidOperationException, "Cannot perform logical 'or' on check results of incompatible type.");
ExplicitQualitativeCheckResult const& otherCheckResult = static_cast<ExplicitQualitativeCheckResult const&>(other);
this->truthValues |= otherCheckResult.truthValues;
performLogicalOperation(*this, other, [] (bool a, bool b) { return a || b; });
return *this;
}
bool ExplicitQualitativeCheckResult::operator[](uint_fast64_t index) const {
return truthValues.get(index);
bool ExplicitQualitativeCheckResult::operator[](storm::storage::sparse::state_type state) const {
if (this->isResultForAllStates()) {
return boost::get<vector_type>(truthValues).get(state);
} else {
map_type const& map = boost::get<map_type>(truthValues);
auto const& keyValuePair = map.find(state);
STORM_LOG_THROW(keyValuePair != map.end(), storm::exceptions::InvalidOperationException, "Unknown key '" << state << "'.");
return keyValuePair->second;
}
}
storm::storage::BitVector const& ExplicitQualitativeCheckResult::getTruthValues() const {
return truthValues;
ExplicitQualitativeCheckResult::vector_type const& ExplicitQualitativeCheckResult::getTruthValuesVector() const {
return boost::get<vector_type>(truthValues);
}
ExplicitQualitativeCheckResult::map_type const& ExplicitQualitativeCheckResult::getTruthValuesVectorMap() const {
return boost::get<map_type>(truthValues);
}
void ExplicitQualitativeCheckResult::complement() {
truthValues.complement();
if (this->isResultForAllStates()) {
boost::get<vector_type>(truthValues).complement();
} else {
for (auto& element : boost::get<map_type>(truthValues)) {
element.second = !element.second;
}
}
}
bool ExplicitQualitativeCheckResult::isExplicit() const {
@ -36,7 +96,7 @@ namespace storm {
}
bool ExplicitQualitativeCheckResult::isResultForAllStates() const {
return true;
return truthValues.which() == 0;
}
bool ExplicitQualitativeCheckResult::isExplicitQualitativeCheckResult() const {
@ -44,7 +104,26 @@ namespace storm {
}
std::ostream& ExplicitQualitativeCheckResult::writeToStream(std::ostream& out) const {
out << truthValues;
if (this->isResultForAllStates()) {
out << boost::get<vector_type>(truthValues);
} else {
std::ios::fmtflags oldflags(std::cout.flags());
out << std::boolalpha;
map_type const& map = boost::get<map_type>(truthValues);
typename map_type::const_iterator it = map.begin();
typename map_type::const_iterator itPlusOne = map.begin();
++itPlusOne;
typename map_type::const_iterator ite = map.end();
for (; it != ite; ++itPlusOne, ++it) {
out << it->second;
if (itPlusOne != ite) {
out << ", ";
}
}
std::cout.flags(oldflags);
}
return out;
}
@ -58,10 +137,27 @@ namespace storm {
storm::storage::BitVector::const_iterator ite = filter.end();
out << std::boolalpha;
for (; it != ite; ++itPlusOne, ++it) {
out << truthValues[*it];
if (itPlusOne != ite) {
out << ", ";
if (this->isResultForAllStates()) {
vector_type const& vector = boost::get<vector_type>(truthValues);
for (; it != ite; ++itPlusOne, ++it) {
out << vector[*it];
if (itPlusOne != ite) {
out << ", ";
}
}
} else {
map_type const& map = boost::get<map_type>(truthValues);
bool allResultsAvailable = true;
for (; it != ite; ++itPlusOne, ++it) {
auto const& keyValuePair = map.find(*it);
if (keyValuePair != map.end()) {
out << keyValuePair->second;
if (itPlusOne != ite) {
out << ", ";
}
} else {
allResultsAvailable = false;
}
}
}
out << "]";

41
src/modelchecker/ExplicitQualitativeCheckResult.h

@ -1,31 +1,28 @@
#ifndef STORM_MODELCHECKER_EXPLICITQUALITATIVECHECKRESULT_H_
#define STORM_MODELCHECKER_EXPLICITQUALITATIVECHECKRESULT_H_
#include <map>
#include <functional>
#include <boost/variant.hpp>
#include "src/modelchecker/QualitativeCheckResult.h"
#include "src/storage/sparse/StateType.h"
#include "src/storage/BitVector.h"
#include "src/utility/OsDetection.h"
namespace storm {
namespace modelchecker {
class ExplicitQualitativeCheckResult : public QualitativeCheckResult {
public:
/*!
* Constructs a check result with the provided truth values.
*
* @param truthValues The truth values of the result.
*/
ExplicitQualitativeCheckResult(storm::storage::BitVector const& truthValues) : truthValues(truthValues) {
// Intentionally left empty.
}
typedef storm::storage::BitVector vector_type;
typedef std::map<storm::storage::sparse::state_type, bool> map_type;
/*!
* Constructs a check result with the provided truth values.
*
* @param truthValues The truth values of the result.
*/
ExplicitQualitativeCheckResult(storm::storage::BitVector&& truthValues) : truthValues(std::move(truthValues)) {
// Intentionally left empty.
}
ExplicitQualitativeCheckResult();
ExplicitQualitativeCheckResult(map_type const& map);
ExplicitQualitativeCheckResult(map_type&& map);
ExplicitQualitativeCheckResult(storm::storage::sparse::state_type state, bool value);
ExplicitQualitativeCheckResult(vector_type const& truthValues);
ExplicitQualitativeCheckResult(vector_type&& truthValues);
ExplicitQualitativeCheckResult(ExplicitQualitativeCheckResult const& other) = default;
ExplicitQualitativeCheckResult& operator=(ExplicitQualitativeCheckResult const& other) = default;
@ -34,7 +31,8 @@ namespace storm {
ExplicitQualitativeCheckResult& operator=(ExplicitQualitativeCheckResult&& other) = default;
#endif
bool operator[](uint_fast64_t index) const;
bool operator[](storm::storage::sparse::state_type index) const;
void setValue(storm::storage::sparse::state_type, bool value);
virtual bool isExplicit() const override;
virtual bool isResultForAllStates() const override;
@ -45,14 +43,17 @@ namespace storm {
virtual CheckResult& operator|=(CheckResult const& other) override;
virtual void complement() override;
storm::storage::BitVector const& getTruthValues() const;
vector_type const& getTruthValuesVector() const;
map_type const& getTruthValuesVectorMap() const;
virtual std::ostream& writeToStream(std::ostream& out) const override;
virtual std::ostream& writeToStream(std::ostream& out, storm::storage::BitVector const& filter) const override;
private:
static void performLogicalOperation(ExplicitQualitativeCheckResult& first, CheckResult const& second, std::function<bool (bool, bool)> const& function);
// The values of the quantitative check result.
storm::storage::BitVector truthValues;
boost::variant<vector_type, map_type> truthValues;
};
}
}

199
src/modelchecker/ExplicitQuantitativeCheckResult.cpp

@ -8,8 +8,43 @@
namespace storm {
namespace modelchecker {
template<typename ValueType>
std::vector<ValueType> const& ExplicitQuantitativeCheckResult<ValueType>::getValues() const {
return values;
ExplicitQuantitativeCheckResult<ValueType>::ExplicitQuantitativeCheckResult() : values(map_type()) {
// Intentionally left empty.
}
template<typename ValueType>
ExplicitQuantitativeCheckResult<ValueType>::ExplicitQuantitativeCheckResult(map_type const& values) : values(values) {
// Intentionally left empty.
}
template<typename ValueType>
ExplicitQuantitativeCheckResult<ValueType>::ExplicitQuantitativeCheckResult(map_type&& values) : values(std::move(values)) {
// Intentionally left empty.
}
template<typename ValueType>
ExplicitQuantitativeCheckResult<ValueType>::ExplicitQuantitativeCheckResult(storm::storage::sparse::state_type const& state, ValueType const& value) : values(map_type()) {
boost::get<map_type>(values).emplace(state, value);
}
template<typename ValueType>
ExplicitQuantitativeCheckResult<ValueType>::ExplicitQuantitativeCheckResult(vector_type const& values) : values(values) {
// Intentionally left empty.
}
template<typename ValueType>
ExplicitQuantitativeCheckResult<ValueType>::ExplicitQuantitativeCheckResult(vector_type&& values) : values(std::move(values)) {
// Intentionally left empty.
}
template<typename ValueType>
typename ExplicitQuantitativeCheckResult<ValueType>::vector_type const& ExplicitQuantitativeCheckResult<ValueType>::getValueVector() const {
return boost::get<vector_type>(values);
}
template<typename ValueType>
typename ExplicitQuantitativeCheckResult<ValueType>::map_type const& ExplicitQuantitativeCheckResult<ValueType>::getValueMap() const {
return boost::get<map_type>(values);
}
template<typename ValueType>
@ -20,12 +55,31 @@ namespace storm {
++itPlusOne;
storm::storage::BitVector::const_iterator ite = filter.end();
for (; it != ite; ++itPlusOne, ++it) {
out << values[*it];
if (itPlusOne != ite) {
out << ", ";
if (this->isResultForAllStates()) {
vector_type const& valuesAsVector = boost::get<vector_type>(values);
for (; it != ite; ++itPlusOne, ++it) {
out << valuesAsVector[*it];
if (itPlusOne != ite) {
out << ", ";
}
}
} else {
map_type const& valuesAsMap = boost::get<map_type>(values);
bool allResultsAvailable = true;
for (; it != ite; ++itPlusOne, ++it) {
auto const& keyValuePair = valuesAsMap.find(*it);
if (keyValuePair != valuesAsMap.end()) {
out << keyValuePair->second;
if (itPlusOne != ite) {
out << ", ";
}
} else {
allResultsAvailable = false;
}
}
STORM_LOG_THROW(allResultsAvailable, storm::exceptions::InvalidOperationException, "Unable to print result for some states, because the result is not available.");
}
out << "]";
return out;
}
@ -33,9 +87,31 @@ namespace storm {
template<typename ValueType>
std::ostream& ExplicitQuantitativeCheckResult<ValueType>::writeToStream(std::ostream& out) const {
out << "[";
if (!values.empty()) {
for (auto element: values) {
out << element << " ";
if (this->isResultForAllStates()) {
vector_type const& valuesAsVector = boost::get<vector_type>(values);
typename vector_type::const_iterator it = valuesAsVector.begin();
typename vector_type::const_iterator itPlusOne = valuesAsVector.begin();
++itPlusOne;
typename vector_type::const_iterator ite = valuesAsVector.end();
for (; it != ite; ++itPlusOne, ++it) {
out << *it;
if (itPlusOne != ite) {
out << ", ";
}
}
} else {
map_type const& valuesAsMap = boost::get<map_type>(values);
typename map_type::const_iterator it = valuesAsMap.begin();
typename map_type::const_iterator itPlusOne = valuesAsMap.begin();
++itPlusOne;
typename map_type::const_iterator ite = valuesAsMap.end();
for (; it != ite; ++itPlusOne, ++it) {
out << it->second;
if (itPlusOne != ite) {
out << ", ";
}
}
}
out << "]";
@ -44,43 +120,88 @@ namespace storm {
template<typename ValueType>
std::unique_ptr<CheckResult> ExplicitQuantitativeCheckResult<ValueType>::compareAgainstBound(storm::logic::ComparisonType comparisonType, double bound) const {
storm::storage::BitVector result(values.size());
switch (comparisonType) {
case logic::Less:
for (uint_fast64_t index = 0; index < values.size(); ++index) {
if (result[index] < bound) {
result.set(index);
if (this->isResultForAllStates()) {
vector_type const& valuesAsVector = boost::get<vector_type>(values);
storm::storage::BitVector result(valuesAsVector.size());
switch (comparisonType) {
case logic::Less:
for (uint_fast64_t index = 0; index < valuesAsVector.size(); ++index) {
if (result[index] < bound) {
result.set(index);
}
}
}
break;
case logic::LessEqual:
for (uint_fast64_t index = 0; index < values.size(); ++index) {
if (result[index] <= bound) {
result.set(index);
break;
case logic::LessEqual:
for (uint_fast64_t index = 0; index < valuesAsVector.size(); ++index) {
if (result[index] <= bound) {
result.set(index);
}
}
}
break;
case logic::Greater:
for (uint_fast64_t index = 0; index < values.size(); ++index) {
if (result[index] > bound) {
result.set(index);
break;
case logic::Greater:
for (uint_fast64_t index = 0; index < valuesAsVector.size(); ++index) {
if (result[index] > bound) {
result.set(index);
}
}
}
break;
case logic::GreaterEqual:
for (uint_fast64_t index = 0; index < values.size(); ++index) {
if (result[index] >= bound) {
result.set(index);
break;
case logic::GreaterEqual:
for (uint_fast64_t index = 0; index < valuesAsVector.size(); ++index) {
if (result[index] >= bound) {
result.set(index);
}
}
}
break;
break;
}
return std::unique_ptr<CheckResult>(new ExplicitQualitativeCheckResult(std::move(result)));
} else {
map_type const& valuesAsMap = boost::get<map_type>(values);
std::map<storm::storage::sparse::state_type, bool> result;
switch (comparisonType) {
case logic::Less:
for (auto const& element : valuesAsMap) {
result[element.first] = element.second < bound;
}
break;
case logic::LessEqual:
for (auto const& element : valuesAsMap) {
result[element.first] = element.second <= bound;
}
break;
case logic::Greater:
for (auto const& element : valuesAsMap) {
result[element.first] = element.second > bound;
}
break;
case logic::GreaterEqual:
for (auto const& element : valuesAsMap) {
result[element.first] = element.second >= bound;
}
break;
}
return std::unique_ptr<CheckResult>(new ExplicitQualitativeCheckResult(std::move(result)));
}
return std::unique_ptr<CheckResult>(new ExplicitQualitativeCheckResult(std::move(result)));
}
template<typename ValueType>
ValueType ExplicitQuantitativeCheckResult<ValueType>::operator[](uint_fast64_t index) const {
return values[index];
ValueType& ExplicitQuantitativeCheckResult<ValueType>::operator[](storm::storage::sparse::state_type state) {
if (this->isResultForAllStates()) {
return boost::get<vector_type>(values)[state];
} else {
return boost::get<map_type>(values)[state];
}
}
template<typename ValueType>
ValueType const& ExplicitQuantitativeCheckResult<ValueType>::operator[](storm::storage::sparse::state_type state) const {
if (this->isResultForAllStates()) {
return boost::get<vector_type>(values)[state];
} else {
map_type const& valuesAsMap = boost::get<map_type>(values);
auto const& keyValuePair = valuesAsMap.find(state);
STORM_LOG_THROW(keyValuePair != valuesAsMap.end(), storm::exceptions::InvalidOperationException, "Unknown key '" << state << "'.");
return keyValuePair->second;
}
}
template<typename ValueType>
@ -90,7 +211,7 @@ namespace storm {
template<typename ValueType>
bool ExplicitQuantitativeCheckResult<ValueType>::isResultForAllStates() const {
return true;
return values.which() == 0;
}
template<typename ValueType>

39
src/modelchecker/ExplicitQuantitativeCheckResult.h

@ -2,8 +2,11 @@
#define STORM_MODELCHECKER_EXPLICITQUANTITATIVECHECKRESULT_H_
#include <vector>
#include <map>
#include <boost/variant.hpp>
#include "src/modelchecker/QuantitativeCheckResult.h"
#include "src/storage/sparse/StateType.h"
#include "src/utility/OsDetection.h"
namespace storm {
@ -11,23 +14,15 @@ namespace storm {
template<typename ValueType>
class ExplicitQuantitativeCheckResult : public QuantitativeCheckResult<ValueType> {
public:
/*!
* Constructs a check result with the provided values.
*
* @param values The values of the result.
*/
ExplicitQuantitativeCheckResult(std::vector<ValueType> const& values) : values(values) {
// Intentionally left empty.
}
/*!
* Constructs a check result with the provided values.
*
* @param values The values of the result.
*/
ExplicitQuantitativeCheckResult(std::vector<ValueType>&& values) : values(std::move(values)) {
// Intentionally left empty.
}
typedef std::vector<ValueType> vector_type;
typedef std::map<storm::storage::sparse::state_type, ValueType> map_type;
ExplicitQuantitativeCheckResult();
ExplicitQuantitativeCheckResult(map_type const& values);
ExplicitQuantitativeCheckResult(map_type&& values);
ExplicitQuantitativeCheckResult(storm::storage::sparse::state_type const& state, ValueType const& value);
ExplicitQuantitativeCheckResult(vector_type const& values);
ExplicitQuantitativeCheckResult(vector_type&& values);
ExplicitQuantitativeCheckResult(ExplicitQuantitativeCheckResult const& other) = default;
ExplicitQuantitativeCheckResult& operator=(ExplicitQuantitativeCheckResult const& other) = default;
@ -36,23 +31,25 @@ namespace storm {
ExplicitQuantitativeCheckResult& operator=(ExplicitQuantitativeCheckResult&& other) = default;
#endif
ValueType& operator[](storm::storage::sparse::state_type state);
ValueType const& operator[](storm::storage::sparse::state_type state) const;
virtual std::unique_ptr<CheckResult> compareAgainstBound(storm::logic::ComparisonType comparisonType, double bound) const override;
ValueType operator[](uint_fast64_t index) const;
virtual bool isExplicit() const override;
virtual bool isResultForAllStates() const override;
virtual bool isExplicitQuantitativeCheckResult() const override;
std::vector<ValueType> const& getValues() const;
vector_type const& getValueVector() const;
map_type const& getValueMap() const;
virtual std::ostream& writeToStream(std::ostream& out) const override;
virtual std::ostream& writeToStream(std::ostream& out, storm::storage::BitVector const& filter) const override;
private:
// The values of the quantitative check result.
std::vector<ValueType> values;
boost::variant<vector_type, map_type> values;
};
}
}

95
src/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.cpp

@ -187,10 +187,10 @@ namespace storm {
std::unique_ptr<CheckResult> SparseMarkovAutomatonCslModelChecker<ValueType>::computeBoundedUntilProbabilities(storm::logic::BoundedUntilFormula const& pathFormula, bool qualitative, boost::optional<storm::logic::OptimalityType> const& optimalityType) {
STORM_LOG_THROW(optimalityType, storm::exceptions::InvalidArgumentException, "Formula needs to specify whether minimal or maximal values are to be computed on nondeterministic model.");
STORM_LOG_THROW(pathFormula.getLeftSubformula().isTrueFormula(), storm::exceptions::NotImplementedException, "Only bounded properties of the form 'true U[t1, t2] phi' are currently supported.");
STORM_LOG_THROW(model.isClosed(), storm::exceptions::InvalidArgumentException, "Unable to compute time-bounded reachability probilities in non-closed Markov automaton.");
STORM_LOG_THROW(model.isClosed(), storm::exceptions::InvalidArgumentException, "Unable to compute time-bounded reachability probabilities in non-closed Markov automaton.");
std::unique_ptr<CheckResult> rightResultPointer = this->check(pathFormula.getRightSubformula());
ExplicitQualitativeCheckResult& rightResult = dynamic_cast<ExplicitQualitativeCheckResult&>(*rightResultPointer);
std::unique_ptr<CheckResult> result = std::unique_ptr<CheckResult>(new ExplicitQuantitativeCheckResult<ValueType>(this->computeBoundedUntilProbabilitiesHelper(optimalityType.get() == storm::logic::OptimalityType::Minimize, rightResult.getTruthValues(), pathFormula.getIntervalBounds())));
ExplicitQualitativeCheckResult const& rightResult = rightResultPointer->asExplicitQualitativeCheckResult();
std::unique_ptr<CheckResult> result = std::unique_ptr<CheckResult>(new ExplicitQuantitativeCheckResult<ValueType>(this->computeBoundedUntilProbabilitiesHelper(optimalityType.get() == storm::logic::OptimalityType::Minimize, rightResult.getTruthValuesVector(), pathFormula.getIntervalBounds())));
return result;
}
@ -204,22 +204,63 @@ namespace storm {
STORM_LOG_THROW(optimalityType, storm::exceptions::InvalidArgumentException, "Formula needs to specify whether minimal or maximal values are to be computed on nondeterministic model.");
std::unique_ptr<CheckResult> leftResultPointer = this->check(pathFormula.getLeftSubformula());
std::unique_ptr<CheckResult> rightResultPointer = this->check(pathFormula.getRightSubformula());
ExplicitQualitativeCheckResult& leftResult = dynamic_cast<ExplicitQualitativeCheckResult&>(*leftResultPointer);
ExplicitQualitativeCheckResult& rightResult = dynamic_cast<ExplicitQualitativeCheckResult&>(*rightResultPointer);
return std::unique_ptr<CheckResult>(new ExplicitQuantitativeCheckResult<ValueType>(this->computeUntilProbabilitiesHelper(optimalityType.get() == storm::logic::OptimalityType::Minimize, leftResult.getTruthValues(), rightResult.getTruthValues(), qualitative)));
ExplicitQualitativeCheckResult& leftResult = leftResultPointer->asExplicitQualitativeCheckResult();
ExplicitQualitativeCheckResult& rightResult = rightResultPointer->asExplicitQualitativeCheckResult();
return std::unique_ptr<CheckResult>(new ExplicitQuantitativeCheckResult<ValueType>(this->computeUntilProbabilitiesHelper(optimalityType.get() == storm::logic::OptimalityType::Minimize, leftResult.getTruthValuesVector(), rightResult.getTruthValuesVector(), qualitative)));
}
template<typename ValueType>
std::vector<ValueType> SparseMarkovAutomatonCslModelChecker<ValueType>::computeReachabilityRewardsHelper(bool minimize, storm::storage::BitVector const& targetStates, bool qualitative) const {
// FIXME
std::vector<ValueType> SparseMarkovAutomatonCslModelChecker<ValueType>::computeReachabilityRewardsHelper(bool minimize, storm::storage::BitVector const& psiStates, bool qualitative) const {
std::vector<ValueType> totalRewardVector;
if (model.hasTransitionRewards()) {
totalRewardVector = model.getTransitionMatrix().getPointwiseProductRowSumVector(model.getTransitionRewardMatrix());
if (model.hasStateRewards()) {
storm::utility::vector::addVectorsInPlace(totalRewardVector, model.getStateRewardVector());
}
} else {
totalRewardVector = std::vector<ValueType>(model.getStateRewardVector());
}
return this->computeExpectedRewards(minimize, psiStates, totalRewardVector);
}
template<typename ValueType>
std::unique_ptr<CheckResult> SparseMarkovAutomatonCslModelChecker<ValueType>::computeReachabilityRewards(storm::logic::ReachabilityRewardFormula const& rewardPathFormula, bool qualitative, boost::optional<storm::logic::OptimalityType> const& optimalityType) {
STORM_LOG_THROW(optimalityType, storm::exceptions::InvalidArgumentException, "Formula needs to specify whether minimal or maximal values are to be computed on nondeterministic model.");
STORM_LOG_THROW(model.isClosed(), storm::exceptions::InvalidArgumentException, "Unable to compute reachability rewards in non-closed Markov automaton.");
std::unique_ptr<CheckResult> subResultPointer = this->check(rewardPathFormula.getSubformula());
ExplicitQualitativeCheckResult& subResult = dynamic_cast<ExplicitQualitativeCheckResult&>(*subResultPointer);
return std::unique_ptr<CheckResult>(new ExplicitQuantitativeCheckResult<ValueType>(this->computeReachabilityRewardsHelper(optimalityType.get() == storm::logic::OptimalityType::Minimize, subResult.getTruthValues(), qualitative)));
ExplicitQualitativeCheckResult const& subResult = subResultPointer->asExplicitQualitativeCheckResult();
return std::unique_ptr<CheckResult>(new ExplicitQuantitativeCheckResult<ValueType>(this->computeReachabilityRewardsHelper(optimalityType.get() == storm::logic::OptimalityType::Minimize, subResult.getTruthValuesVector(), qualitative)));
}
template<typename ValueType>
std::vector<ValueType> SparseMarkovAutomatonCslModelChecker<ValueType>::computeLongRunAverageHelper(bool minimize, storm::storage::BitVector const& psiStates, bool qualitative) const {
// FIXME
}
template<typename ValueType>
std::unique_ptr<CheckResult> SparseMarkovAutomatonCslModelChecker<ValueType>::computeLongRunAverage(storm::logic::StateFormula const& stateFormula, bool qualitative, boost::optional<storm::logic::OptimalityType> const& optimalityType) {
STORM_LOG_THROW(optimalityType, storm::exceptions::InvalidArgumentException, "Formula needs to specify whether minimal or maximal values are to be computed on nondeterministic model.");
STORM_LOG_THROW(model.isClosed(), storm::exceptions::InvalidArgumentException, "Unable to compute long-run average in non-closed Markov automaton.");
std::unique_ptr<CheckResult> subResultPointer = this->check(stateFormula);
ExplicitQualitativeCheckResult const& subResult = subResultPointer->asExplicitQualitativeCheckResult();
return std::unique_ptr<CheckResult>(new ExplicitQuantitativeCheckResult<ValueType>(this->computeLongRunAverageHelper(optimalityType.get() == storm::logic::OptimalityType::Minimize, subResult.getTruthValuesVector(), qualitative)));
}
template<typename ValueType>
std::vector<ValueType> SparseMarkovAutomatonCslModelChecker<ValueType>::computeExpectedTimesHelper(bool minimize, storm::storage::BitVector const& psiStates, bool qualitative) const {
std::vector<ValueType> rewardValues(model.getNumberOfStates(), storm::utility::zero<ValueType>());
storm::utility::vector::setVectorValues(rewardValues, model.getMarkovianStates(), storm::utility::one<ValueType>());
return this->computeExpectedRewards(minimize, psiStates, rewardValues);
}
template<typename ValueType>
std::unique_ptr<CheckResult> SparseMarkovAutomatonCslModelChecker<ValueType>::computeExpectedTimes(storm::logic::EventuallyFormula const& eventuallyFormula, bool qualitative, boost::optional<storm::logic::OptimalityType> const& optimalityType) {
STORM_LOG_THROW(optimalityType, storm::exceptions::InvalidArgumentException, "Formula needs to specify whether minimal or maximal values are to be computed on nondeterministic model.");
STORM_LOG_THROW(model.isClosed(), storm::exceptions::InvalidArgumentException, "Unable to compute expected times in non-closed Markov automaton.");
std::unique_ptr<CheckResult> subResultPointer = this->check(eventuallyFormula.getSubformula());
ExplicitQualitativeCheckResult& subResult = subResultPointer->asExplicitQualitativeCheckResult();
return std::unique_ptr<CheckResult>(new ExplicitQuantitativeCheckResult<ValueType>(this->computeExpectedTimesHelper(optimalityType.get() == storm::logic::OptimalityType::Minimize, subResult.getTruthValuesVector(), qualitative)));
}
template<typename ValueType>
@ -238,19 +279,19 @@ namespace storm {
}
template<typename ValueType>
std::vector<ValueType> SparseMarkovAutomatonCslModelChecker<ValueType>::checkLongRunAverage(bool min, storm::storage::BitVector const& goalStates) const {
std::vector<ValueType> SparseMarkovAutomatonCslModelChecker<ValueType>::computeLongRunAverage(bool minimize, storm::storage::BitVector const& psiStates) const {
// Check whether the automaton is closed.
if (!model.isClosed()) {
throw storm::exceptions::InvalidArgumentException() << "Unable to compute long-run average on non-closed Markov automaton.";
}
// If there are no goal states, we avoid the computation and directly return zero.
if (goalStates.empty()) {
if (psiStates.empty()) {
return std::vector<ValueType>(model.getNumberOfStates(), storm::utility::zero<ValueType>());
}
// Likewise, if all bits are set, we can avoid the computation and set.
if ((~goalStates).empty()) {
if ((~psiStates).empty()) {
return std::vector<ValueType>(model.getNumberOfStates(), storm::utility::one<ValueType>());
}
@ -280,7 +321,7 @@ namespace storm {
}
// Compute the LRA value for the current MEC.
lraValuesForEndComponents.push_back(this->computeLraForMaximalEndComponent(min, transitionMatrix, nondeterministicChoiceIndices, model.getMarkovianStates(), model.getExitRates(), goalStates, mec, currentMecIndex));
lraValuesForEndComponents.push_back(this->computeLraForMaximalEndComponent(minimize, transitionMatrix, nondeterministicChoiceIndices, model.getMarkovianStates(), model.getExitRates(), psiStates, mec, currentMecIndex));
}
// For fast transition rewriting, we build some auxiliary data structures.
@ -386,7 +427,7 @@ namespace storm {
storm::storage::SparseMatrix<ValueType> sspMatrix = sspMatrixBuilder.build(currentChoice);
std::vector<ValueType> x(numberOfStatesNotInMecs + mecDecomposition.size());
nondeterministicLinearEquationSolver->solveEquationSystem(min, sspMatrix, x, b);
nondeterministicLinearEquationSolver->solveEquationSystem(minimize, sspMatrix, x, b);
// Prepare result vector.
std::vector<ValueType> result(model.getNumberOfStates());
@ -403,9 +444,9 @@ namespace storm {
}
template<typename ValueType>
ValueType SparseMarkovAutomatonCslModelChecker<ValueType>::computeLraForMaximalEndComponent(bool min, storm::storage::SparseMatrix<ValueType> const& transitionMatrix, std::vector<uint_fast64_t> const& nondeterministicChoiceIndices, storm::storage::BitVector const& markovianStates, std::vector<ValueType> const& exitRates, storm::storage::BitVector const& goalStates, storm::storage::MaximalEndComponent const& mec, uint_fast64_t mecIndex) {
ValueType SparseMarkovAutomatonCslModelChecker<ValueType>::computeLraForMaximalEndComponent(bool minimize, storm::storage::SparseMatrix<ValueType> const& transitionMatrix, std::vector<uint_fast64_t> const& nondeterministicChoiceIndices, storm::storage::BitVector const& markovianStates, std::vector<ValueType> const& exitRates, storm::storage::BitVector const& psiStates, storm::storage::MaximalEndComponent const& mec, uint_fast64_t mecIndex) {
std::shared_ptr<storm::solver::LpSolver> solver = storm::utility::solver::getLpSolver("LRA for MEC");
solver->setModelSense(min ? storm::solver::LpSolver::ModelSense::Maximize : storm::solver::LpSolver::ModelSense::Minimize);
solver->setModelSense(minimize ? storm::solver::LpSolver::ModelSense::Maximize : storm::solver::LpSolver::ModelSense::Minimize);
// First, we need to create the variables for the problem.
std::map<uint_fast64_t, storm::expressions::Variable> stateToVariableMap;
@ -429,8 +470,8 @@ namespace storm {
}
constraint = constraint + solver->getConstant(storm::utility::one<ValueType>() / exitRates[state]) * k;
storm::expressions::Expression rightHandSide = goalStates.get(state) ? solver->getConstant(storm::utility::one<ValueType>() / exitRates[state]) : solver->getConstant(0);
if (min) {
storm::expressions::Expression rightHandSide = psiStates.get(state) ? solver->getConstant(storm::utility::one<ValueType>() / exitRates[state]) : solver->getConstant(0);
if (minimize) {
constraint = constraint <= rightHandSide;
} else {
constraint = constraint >= rightHandSide;
@ -447,7 +488,7 @@ namespace storm {
}
storm::expressions::Expression rightHandSide = solver->getConstant(storm::utility::zero<ValueType>());
if (min) {
if (minimize) {
constraint = constraint <= rightHandSide;
} else {
constraint = constraint >= rightHandSide;
@ -461,22 +502,8 @@ namespace storm {
return solver->getContinuousValue(k);
}
template<typename ValueType>
std::vector<ValueType> SparseMarkovAutomatonCslModelChecker<ValueType>::checkExpectedTime(bool minimize, storm::storage::BitVector const& goalStates) const {
// Reduce the problem of computing the expected time to computing expected rewards where the rewards
// for all probabilistic states are zero and the reward values of Markovian states is 1.
std::vector<ValueType> rewardValues(model.getNumberOfStates(), storm::utility::zero<ValueType>());
storm::utility::vector::setVectorValues(rewardValues, model.getMarkovianStates(), storm::utility::one<ValueType>());
return this->computeExpectedRewards(minimize, goalStates, rewardValues);
}
template<typename ValueType>
std::vector<ValueType> SparseMarkovAutomatonCslModelChecker<ValueType>::computeExpectedRewards(bool minimize, storm::storage::BitVector const& goalStates, std::vector<ValueType> const& stateRewards) const {
// Check whether the automaton is closed.
if (!model.isClosed()) {
throw storm::exceptions::InvalidArgumentException() << "Unable to compute expected time on non-closed Markov automaton.";
}
// First, we need to check which states have infinite expected time (by definition).
storm::storage::BitVector infinityStates;
if (minimize) {

12
src/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.h

@ -22,19 +22,21 @@ namespace storm {
virtual std::unique_ptr<CheckResult> computeBoundedUntilProbabilities(storm::logic::BoundedUntilFormula const& pathFormula, bool qualitative = false, boost::optional<storm::logic::OptimalityType> const& optimalityType = boost::optional<storm::logic::OptimalityType>()) override;
virtual std::unique_ptr<CheckResult> computeUntilProbabilities(storm::logic::UntilFormula const& pathFormula, bool qualitative = false, boost::optional<storm::logic::OptimalityType> const& optimalityType = boost::optional<storm::logic::OptimalityType>()) override;
virtual std::unique_ptr<CheckResult> computeReachabilityRewards(storm::logic::ReachabilityRewardFormula const& rewardPathFormula, bool qualitative = false, boost::optional<storm::logic::OptimalityType> const& optimalityType = boost::optional<storm::logic::OptimalityType>()) override;
virtual std::unique_ptr<CheckResult> computeLongRunAverage(storm::logic::StateFormula const& stateFormula, bool qualitative = false, boost::optional<storm::logic::OptimalityType> const& optimalityType = boost::optional<storm::logic::OptimalityType>()) override;
virtual std::unique_ptr<CheckResult> computeExpectedTimes(storm::logic::EventuallyFormula const& eventuallyFormula, bool qualitative = false, boost::optional<storm::logic::OptimalityType> const& optimalityType = boost::optional<storm::logic::OptimalityType>());
virtual std::unique_ptr<CheckResult> checkBooleanLiteralFormula(storm::logic::BooleanLiteralFormula const& stateFormula) override;
virtual std::unique_ptr<CheckResult> checkAtomicLabelFormula(storm::logic::AtomicLabelFormula const& stateFormula) override;
private:
// The methods that perform the actual checking.
std::vector<ValueType> computeBoundedUntilProbabilitiesHelper(bool minimize, storm::storage::BitVector const& psiStates, std::pair<double, double> const& boundsPair) const;
static void computeBoundedReachabilityProbabilities(bool minimize, storm::storage::SparseMatrix<ValueType> const& transitionMatrix, std::vector<ValueType> const& exitRates, storm::storage::BitVector const& markovianStates, storm::storage::BitVector const& goalStates, storm::storage::BitVector const& markovianNonGoalStates, storm::storage::BitVector const& probabilisticNonGoalStates, std::vector<ValueType>& markovianNonGoalValues, std::vector<ValueType>& probabilisticNonGoalValues, ValueType delta, uint_fast64_t numberOfSteps);
std::vector<ValueType> computeUntilProbabilitiesHelper(bool minimize, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates, bool qualitative) const;
std::vector<ValueType> computeReachabilityRewardsHelper(bool minimize, storm::storage::BitVector const& targetStates, bool qualitative) const;
// FIXME: Methods that are not yet accessible from the outside and need to be included in the checking framework.
std::vector<ValueType> checkLongRunAverage(bool minimize, storm::storage::BitVector const& goalStates) const;
std::vector<ValueType> checkExpectedTime(bool minimize, storm::storage::BitVector const& goalStates) const;
std::vector<ValueType> computeReachabilityRewardsHelper(bool minimize, storm::storage::BitVector const& psiStates, bool qualitative) const;
std::vector<ValueType> computeLongRunAverageHelper(bool minimize, storm::storage::BitVector const& psiStates, bool qualitative) const;
std::vector<ValueType> computeExpectedTimesHelper(bool minimize, storm::storage::BitVector const& psiStates, bool qualitative) const;
std::vector<ValueType> computeLongRunAverage(bool minimize, storm::storage::BitVector const& psiStates) const;
/*!
* Computes the long-run average value for the given maximal end component of a Markov automaton.

20
src/modelchecker/prctl/SparseDtmcPrctlModelChecker.cpp

@ -66,9 +66,9 @@ namespace storm {
std::unique_ptr<CheckResult> SparseDtmcPrctlModelChecker<ValueType>::computeBoundedUntilProbabilities(storm::logic::BoundedUntilFormula const& pathFormula, bool qualitative, boost::optional<storm::logic::OptimalityType> const& optimalityType) {
std::unique_ptr<CheckResult> leftResultPointer = this->check(pathFormula.getLeftSubformula());
std::unique_ptr<CheckResult> rightResultPointer = this->check(pathFormula.getRightSubformula());
ExplicitQualitativeCheckResult& leftResult = dynamic_cast<ExplicitQualitativeCheckResult&>(*leftResultPointer);
ExplicitQualitativeCheckResult& rightResult = dynamic_cast<ExplicitQualitativeCheckResult&>(*rightResultPointer);
std::unique_ptr<CheckResult> result = std::unique_ptr<CheckResult>(new ExplicitQuantitativeCheckResult<ValueType>(this->computeBoundedUntilProbabilitiesHelper(leftResult.getTruthValues(), rightResult.getTruthValues(), pathFormula.getUpperBound())));
ExplicitQualitativeCheckResult const& leftResult = leftResultPointer->asExplicitQualitativeCheckResult();;
ExplicitQualitativeCheckResult const& rightResult = rightResultPointer->asExplicitQualitativeCheckResult();
std::unique_ptr<CheckResult> result = std::unique_ptr<CheckResult>(new ExplicitQuantitativeCheckResult<ValueType>(this->computeBoundedUntilProbabilitiesHelper(leftResult.getTruthValuesVector(), rightResult.getTruthValuesVector(), pathFormula.getUpperBound())));
return result;
}
@ -88,8 +88,8 @@ namespace storm {
template<typename ValueType>
std::unique_ptr<CheckResult> SparseDtmcPrctlModelChecker<ValueType>::computeNextProbabilities(storm::logic::NextFormula const& pathFormula, bool qualitative, boost::optional<storm::logic::OptimalityType> const& optimalityType) {
std::unique_ptr<CheckResult> subResultPointer = this->check(pathFormula.getSubformula());
ExplicitQualitativeCheckResult& subResult = dynamic_cast<ExplicitQualitativeCheckResult&>(*subResultPointer);
return std::unique_ptr<CheckResult>(new ExplicitQuantitativeCheckResult<ValueType>(this->computeNextProbabilitiesHelper(subResult.getTruthValues())));
ExplicitQualitativeCheckResult const& subResult = subResultPointer->asExplicitQualitativeCheckResult();
return std::unique_ptr<CheckResult>(new ExplicitQuantitativeCheckResult<ValueType>(this->computeNextProbabilitiesHelper(subResult.getTruthValuesVector())));
}
template<typename ValueType>
@ -153,9 +153,9 @@ namespace storm {
std::unique_ptr<CheckResult> SparseDtmcPrctlModelChecker<ValueType>::computeUntilProbabilities(storm::logic::UntilFormula const& pathFormula, bool qualitative, boost::optional<storm::logic::OptimalityType> const& optimalityType) {
std::unique_ptr<CheckResult> leftResultPointer = this->check(pathFormula.getLeftSubformula());
std::unique_ptr<CheckResult> rightResultPointer = this->check(pathFormula.getRightSubformula());
ExplicitQualitativeCheckResult& leftResult = dynamic_cast<ExplicitQualitativeCheckResult&>(*leftResultPointer);
ExplicitQualitativeCheckResult& rightResult = dynamic_cast<ExplicitQualitativeCheckResult&>(*rightResultPointer);
return std::unique_ptr<CheckResult>(new ExplicitQuantitativeCheckResult<ValueType>(this->computeUntilProbabilitiesHelper(leftResult.getTruthValues(), rightResult.getTruthValues(), qualitative)));
ExplicitQualitativeCheckResult const& leftResult = leftResultPointer->asExplicitQualitativeCheckResult();;
ExplicitQualitativeCheckResult const& rightResult = rightResultPointer->asExplicitQualitativeCheckResult();;
return std::unique_ptr<CheckResult>(new ExplicitQuantitativeCheckResult<ValueType>(this->computeUntilProbabilitiesHelper(leftResult.getTruthValuesVector(), rightResult.getTruthValuesVector(), qualitative)));
}
template<typename ValueType>
@ -293,8 +293,8 @@ namespace storm {
template<typename ValueType>
std::unique_ptr<CheckResult> SparseDtmcPrctlModelChecker<ValueType>::computeReachabilityRewards(storm::logic::ReachabilityRewardFormula const& rewardPathFormula, bool qualitative, boost::optional<storm::logic::OptimalityType> const& optimalityType) {
std::unique_ptr<CheckResult> subResultPointer = this->check(rewardPathFormula.getSubformula());
ExplicitQualitativeCheckResult& subResult = dynamic_cast<ExplicitQualitativeCheckResult&>(*subResultPointer);
return std::unique_ptr<CheckResult>(new ExplicitQuantitativeCheckResult<ValueType>(this->computeReachabilityRewardsHelper(subResult.getTruthValues(), qualitative)));
ExplicitQualitativeCheckResult const& subResult = subResultPointer->asExplicitQualitativeCheckResult();
return std::unique_ptr<CheckResult>(new ExplicitQuantitativeCheckResult<ValueType>(this->computeReachabilityRewardsHelper(subResult.getTruthValuesVector(), qualitative)));
}
template<typename ValueType>

20
src/modelchecker/prctl/SparseMdpPrctlModelChecker.cpp

@ -72,9 +72,9 @@ namespace storm {
STORM_LOG_THROW(optimalityType, storm::exceptions::InvalidArgumentException, "Formula needs to specify whether minimal or maximal values are to be computed on nondeterministic model.");
std::unique_ptr<CheckResult> leftResultPointer = this->check(pathFormula.getLeftSubformula());
std::unique_ptr<CheckResult> rightResultPointer = this->check(pathFormula.getRightSubformula());
ExplicitQualitativeCheckResult& leftResult = dynamic_cast<ExplicitQualitativeCheckResult&>(*leftResultPointer);
ExplicitQualitativeCheckResult& rightResult = dynamic_cast<ExplicitQualitativeCheckResult&>(*rightResultPointer);
std::unique_ptr<CheckResult> result = std::unique_ptr<CheckResult>(new ExplicitQuantitativeCheckResult<ValueType>(this->computeBoundedUntilProbabilitiesHelper(optimalityType.get() == storm::logic::OptimalityType::Minimize, leftResult.getTruthValues(), rightResult.getTruthValues(), pathFormula.getUpperBound())));
ExplicitQualitativeCheckResult const& leftResult = leftResultPointer->asExplicitQualitativeCheckResult();
ExplicitQualitativeCheckResult const& rightResult = rightResultPointer->asExplicitQualitativeCheckResult();
std::unique_ptr<CheckResult> result = std::unique_ptr<CheckResult>(new ExplicitQuantitativeCheckResult<ValueType>(this->computeBoundedUntilProbabilitiesHelper(optimalityType.get() == storm::logic::OptimalityType::Minimize, leftResult.getTruthValuesVector(), rightResult.getTruthValuesVector(), pathFormula.getUpperBound())));
return result;
}
@ -94,8 +94,8 @@ namespace storm {
std::unique_ptr<CheckResult> SparseMdpPrctlModelChecker<ValueType>::computeNextProbabilities(storm::logic::NextFormula const& pathFormula, bool qualitative, boost::optional<storm::logic::OptimalityType> const& optimalityType) {
STORM_LOG_THROW(optimalityType, storm::exceptions::InvalidArgumentException, "Formula needs to specify whether minimal or maximal values are to be computed on nondeterministic model.");
std::unique_ptr<CheckResult> subResultPointer = this->check(pathFormula.getSubformula());
ExplicitQualitativeCheckResult& subResult = dynamic_cast<ExplicitQualitativeCheckResult&>(*subResultPointer);
return std::unique_ptr<CheckResult>(new ExplicitQuantitativeCheckResult<ValueType>(this->computeNextProbabilitiesHelper(optimalityType.get() == storm::logic::OptimalityType::Minimize, subResult.getTruthValues())));
ExplicitQualitativeCheckResult const& subResult = subResultPointer->asExplicitQualitativeCheckResult();
return std::unique_ptr<CheckResult>(new ExplicitQuantitativeCheckResult<ValueType>(this->computeNextProbabilitiesHelper(optimalityType.get() == storm::logic::OptimalityType::Minimize, subResult.getTruthValuesVector())));
}
template<typename ValueType>
@ -159,9 +159,9 @@ namespace storm {
STORM_LOG_THROW(optimalityType, storm::exceptions::InvalidArgumentException, "Formula needs to specify whether minimal or maximal values are to be computed on nondeterministic model.");
std::unique_ptr<CheckResult> leftResultPointer = this->check(pathFormula.getLeftSubformula());
std::unique_ptr<CheckResult> rightResultPointer = this->check(pathFormula.getRightSubformula());
ExplicitQualitativeCheckResult& leftResult = dynamic_cast<ExplicitQualitativeCheckResult&>(*leftResultPointer);
ExplicitQualitativeCheckResult& rightResult = dynamic_cast<ExplicitQualitativeCheckResult&>(*rightResultPointer);
return std::unique_ptr<CheckResult>(new ExplicitQuantitativeCheckResult<ValueType>(SparseMdpPrctlModelChecker<ValueType>::computeUntilProbabilitiesHelper(optimalityType.get() == storm::logic::OptimalityType::Minimize, model.getTransitionMatrix(), model.getBackwardTransitions(), leftResult.getTruthValues(), rightResult.getTruthValues(), nondeterministicLinearEquationSolver, qualitative)));
ExplicitQualitativeCheckResult const& leftResult = leftResultPointer->asExplicitQualitativeCheckResult();
ExplicitQualitativeCheckResult const& rightResult = rightResultPointer->asExplicitQualitativeCheckResult();
return std::unique_ptr<CheckResult>(new ExplicitQuantitativeCheckResult<ValueType>(SparseMdpPrctlModelChecker<ValueType>::computeUntilProbabilitiesHelper(optimalityType.get() == storm::logic::OptimalityType::Minimize, model.getTransitionMatrix(), model.getBackwardTransitions(), leftResult.getTruthValuesVector(), rightResult.getTruthValuesVector(), nondeterministicLinearEquationSolver, qualitative)));
}
template<typename ValueType>
@ -304,8 +304,8 @@ namespace storm {
std::unique_ptr<CheckResult> SparseMdpPrctlModelChecker<ValueType>::computeReachabilityRewards(storm::logic::ReachabilityRewardFormula const& rewardPathFormula, bool qualitative, boost::optional<storm::logic::OptimalityType> const& optimalityType) {
STORM_LOG_THROW(optimalityType, storm::exceptions::InvalidArgumentException, "Formula needs to specify whether minimal or maximal values are to be computed on nondeterministic model.");
std::unique_ptr<CheckResult> subResultPointer = this->check(rewardPathFormula.getSubformula());
ExplicitQualitativeCheckResult& subResult = dynamic_cast<ExplicitQualitativeCheckResult&>(*subResultPointer);
return std::unique_ptr<CheckResult>(new ExplicitQuantitativeCheckResult<ValueType>(this->computeReachabilityRewardsHelper(optimalityType.get() == storm::logic::OptimalityType::Minimize, subResult.getTruthValues(), qualitative)));
ExplicitQualitativeCheckResult const& subResult = subResultPointer->asExplicitQualitativeCheckResult();
return std::unique_ptr<CheckResult>(new ExplicitQuantitativeCheckResult<ValueType>(this->computeReachabilityRewardsHelper(optimalityType.get() == storm::logic::OptimalityType::Minimize, subResult.getTruthValuesVector(), qualitative)));
}
template<typename ValueType>

864
src/modelchecker/reachability/SparseDtmcEliminationModelChecker.cpp

@ -0,0 +1,864 @@
#include "src/modelchecker/reachability/SparseDtmcEliminationModelChecker.h"
#include <algorithm>
#ifdef PARAMETRIC_SYSTEMS
#include "src/storage/parameters.h"
#endif
#include "src/storage/StronglyConnectedComponentDecomposition.h"
#include "src/modelchecker/ExplicitQualitativeCheckResult.h"
#include "src/modelchecker/ExplicitQuantitativeCheckResult.h"
#include "src/utility/graph.h"
#include "src/utility/vector.h"
#include "src/utility/macros.h"
#include "src/utility/ConstantsComparator.h"
#include "src/exceptions/InvalidPropertyException.h"
#include "src/exceptions/InvalidStateException.h"
namespace storm {
namespace modelchecker {
template<typename ValueType>
SparseDtmcEliminationModelChecker<ValueType>::SparseDtmcEliminationModelChecker(storm::models::Dtmc<ValueType> const& model) : model(model) {
// Intentionally left empty.
}
template<typename ValueType>
bool SparseDtmcEliminationModelChecker<ValueType>::canHandle(storm::logic::Formula const& formula) const {
if (formula.isProbabilityOperatorFormula()) {
storm::logic::ProbabilityOperatorFormula const& probabilityOperatorFormula = formula.asProbabilityOperatorFormula();
// The probability operator must not have a bound.
if (!probabilityOperatorFormula.hasBound()) {
return this->canHandle(probabilityOperatorFormula.getSubformula());
}
} else if (formula.isRewardOperatorFormula()) {
storm::logic::RewardOperatorFormula const& rewardOperatorFormula = formula.asRewardOperatorFormula();
// The reward operator must not have a bound.
if (!rewardOperatorFormula.hasBound()) {
return this->canHandle(rewardOperatorFormula.getSubformula());
}
} else if (formula.isUntilFormula() || formula.isEventuallyFormula()) {
if (formula.isUntilFormula()) {
storm::logic::UntilFormula const& untilFormula = formula.asUntilFormula();
if (untilFormula.getLeftSubformula().isPropositionalFormula() && untilFormula.getRightSubformula().isPropositionalFormula()) {
return true;
}
} else if (formula.isEventuallyFormula()) {
storm::logic::EventuallyFormula const& eventuallyFormula = formula.asEventuallyFormula();
if (eventuallyFormula.getSubformula().isPropositionalFormula()) {
return true;
}
}
} else if (formula.isReachabilityRewardFormula()) {
storm::logic::ReachabilityRewardFormula reachabilityRewardFormula = formula.asReachabilityRewardFormula();
if (reachabilityRewardFormula.getSubformula().isPropositionalFormula()) {
return true;
}
} else if (formula.isConditionalPathFormula()) {
storm::logic::ConditionalPathFormula conditionalPathFormula = formula.asConditionalPathFormula();
if (conditionalPathFormula.getLeftSubformula().isEventuallyFormula() && conditionalPathFormula.getRightSubformula().isEventuallyFormula()) {
return this->canHandle(conditionalPathFormula.getLeftSubformula()) && this->canHandle(conditionalPathFormula.getRightSubformula());
}
}
return false;
}
template<typename ValueType>
std::unique_ptr<CheckResult> SparseDtmcEliminationModelChecker<ValueType>::computeUntilProbabilities(storm::logic::UntilFormula const& pathFormula, bool qualitative, boost::optional<storm::logic::OptimalityType> const& optimalityType) {
// Retrieve the appropriate bitvectors by model checking the subformulas.
std::unique_ptr<CheckResult> leftResultPointer = this->check(pathFormula.getLeftSubformula());
std::unique_ptr<CheckResult> rightResultPointer = this->check(pathFormula.getRightSubformula());
storm::storage::BitVector const& phiStates = leftResultPointer->asExplicitQualitativeCheckResult().getTruthValuesVector();
storm::storage::BitVector const& psiStates = rightResultPointer->asExplicitQualitativeCheckResult().getTruthValuesVector();
// Do some sanity checks to establish some required properties.
STORM_LOG_THROW(model.getInitialStates().getNumberOfSetBits() == 1, storm::exceptions::IllegalArgumentException, "Input model is required to have exactly one initial state.");
// Then, compute the subset of states that has a probability of 0 or 1, respectively.
std::pair<storm::storage::BitVector, storm::storage::BitVector> statesWithProbability01 = storm::utility::graph::performProb01(model, phiStates, psiStates);
storm::storage::BitVector statesWithProbability0 = statesWithProbability01.first;
storm::storage::BitVector statesWithProbability1 = statesWithProbability01.second;
storm::storage::BitVector maybeStates = ~(statesWithProbability0 | statesWithProbability1);
// If the initial state is known to have either probability 0 or 1, we can directly return the result.
if (model.getInitialStates().isDisjointFrom(maybeStates)) {
STORM_LOG_DEBUG("The probability of all initial states was found in a preprocessing step.");
return statesWithProbability0.get(*model.getInitialStates().begin()) ? storm::utility::zero<ValueType>() : storm::utility::one<ValueType>();
}
// Determine the set of states that is reachable from the initial state without jumping over a target state.
storm::storage::BitVector reachableStates = storm::utility::graph::getReachableStates(model.getTransitionMatrix(), model.getInitialStates(), maybeStates, statesWithProbability1);
// Subtract from the maybe states the set of states that is not reachable (on a path from the initial to a target state).
maybeStates &= reachableStates;
// Create a vector for the probabilities to go to a state with probability 1 in one step.
std::vector<ValueType> oneStepProbabilities = model.getTransitionMatrix().getConstrainedRowSumVector(maybeStates, statesWithProbability1);
// Determine the set of initial states of the sub-model.
storm::storage::BitVector newInitialStates = model.getInitialStates() % maybeStates;
// We then build the submatrix that only has the transitions of the maybe states.
storm::storage::SparseMatrix<ValueType> submatrix = model.getTransitionMatrix().getSubmatrix(false, maybeStates, maybeStates);
storm::storage::SparseMatrix<ValueType> submatrixTransposed = submatrix.transpose();
// Before starting the model checking process, we assign priorities to states so we can use them to
// impose ordering constraints later.
std::vector<std::size_t> statePriorities = getStatePriorities(submatrix, submatrixTransposed, newInitialStates, oneStepProbabilities);
boost::optional<std::vector<ValueType>> missingStateRewards;
return computeReachabilityValue(submatrix, oneStepProbabilities, submatrixTransposed, newInitialStates, phiStates, psiStates, missingStateRewards, statePriorities);
}
template<typename ValueType>
std::unique_ptr<CheckResult> SparseDtmcEliminationModelChecker<ValueType>::computeReachabilityRewards(storm::logic::ReachabilityRewardFormula const& rewardPathFormula, bool qualitative, boost::optional<storm::logic::OptimalityType> const& optimalityType) {
// Retrieve the appropriate bitvectors by model checking the subformulas.
std::unique_ptr<CheckResult> subResultPointer = this->check(rewardPathFormula.getSubformula());
storm::storage::BitVector phiStates(model.getNumberOfStates(), true);
storm::storage::BitVector const& psiStates = subResultPointer->asExplicitQualitativeCheckResult().getTruthValuesVector();
// Do some sanity checks to establish some required properties.
STORM_LOG_THROW(!model.hasTransitionRewards(), storm::exceptions::IllegalArgumentException, "Input model does have transition-based rewards, which are currently unsupported.");
STORM_LOG_THROW(model.hasStateRewards(), storm::exceptions::IllegalArgumentException, "Input model does not have a state-based reward model.");
STORM_LOG_THROW(model.getInitialStates().getNumberOfSetBits() == 1, storm::exceptions::IllegalArgumentException, "Input model is required to have exactly one initial state.");
// Then, compute the subset of states that has a reachability reward less than infinity.
storm::storage::BitVector trueStates(model.getNumberOfStates(), true);
storm::storage::BitVector infinityStates = storm::utility::graph::performProb1(model.getBackwardTransitions(), trueStates, psiStates);
infinityStates.complement();
storm::storage::BitVector maybeStates = ~psiStates & ~infinityStates;
// If the initial state is known to have 0 reward or an infinite reward value, we can directly return the result.
STORM_LOG_THROW(model.getInitialStates().isDisjointFrom(infinityStates), storm::exceptions::IllegalArgumentException, "Initial state has infinite reward.");
if (!model.getInitialStates().isDisjointFrom(psiStates)) {
STORM_LOG_DEBUG("The reward of all initial states was found in a preprocessing step.");
return storm::utility::zero<ValueType>();
}
// Determine the set of states that is reachable from the initial state without jumping over a target state.
storm::storage::BitVector reachableStates = storm::utility::graph::getReachableStates(model.getTransitionMatrix(), model.getInitialStates(), maybeStates, psiStates);
// Subtract from the maybe states the set of states that is not reachable (on a path from the initial to a target state).
maybeStates &= reachableStates;
// Create a vector for the probabilities to go to a state with probability 1 in one step.
std::vector<ValueType> oneStepProbabilities = model.getTransitionMatrix().getConstrainedRowSumVector(maybeStates, psiStates);
// Project the state reward vector to all maybe-states.
boost::optional<std::vector<ValueType>> stateRewards(maybeStates.getNumberOfSetBits());
storm::utility::vector::selectVectorValues(stateRewards.get(), maybeStates, model.getStateRewardVector());
// Determine the set of initial states of the sub-model.
storm::storage::BitVector newInitialStates = model.getInitialStates() % maybeStates;
// We then build the submatrix that only has the transitions of the maybe states.
storm::storage::SparseMatrix<ValueType> submatrix = model.getTransitionMatrix().getSubmatrix(false, maybeStates, maybeStates);
storm::storage::SparseMatrix<ValueType> submatrixTransposed = submatrix.transpose();
// Before starting the model checking process, we assign priorities to states so we can use them to
// impose ordering constraints later.
std::vector<std::size_t> statePriorities = getStatePriorities(submatrix, submatrixTransposed, newInitialStates, oneStepProbabilities);
return computeReachabilityValue(submatrix, oneStepProbabilities, submatrixTransposed, newInitialStates, phiStates, psiStates, stateRewards, statePriorities);
}
template<typename ValueType>
std::unique_ptr<CheckResult> SparseDtmcEliminationModelChecker<ValueType>::computeConditionalProbabilities(storm::logic::ConditionalPathFormula const& pathFormula, bool qualitative, boost::optional<storm::logic::OptimalityType> const& optimalityType) {
// Retrieve the appropriate bitvectors by model checking the subformulas.
STORM_LOG_THROW(pathFormula.getLeftSubformula().isEventuallyFormula(), storm::exceptions::InvalidPropertyException, "Expected 'eventually' formula.");
STORM_LOG_THROW(pathFormula.getRightSubformula().isEventuallyFormula(), storm::exceptions::InvalidPropertyException, "Expected 'eventually' formula.");
std::unique_ptr<CheckResult> leftResultPointer = this->check(pathFormula.getLeftSubformula().asEventuallyFormula().getSubformula());
std::unique_ptr<CheckResult> rightResultPointer = this->check(pathFormula.getRightSubformula().asEventuallyFormula().getSubformula());
storm::storage::BitVector phiStates = leftResultPointer->asExplicitQualitativeCheckResult().getTruthValuesVector();
storm::storage::BitVector psiStates = rightResultPointer->asExplicitQualitativeCheckResult().getTruthValuesVector();
storm::storage::BitVector trueStates(model.getNumberOfStates(), true);
// Do some sanity checks to establish some required properties.
STORM_LOG_THROW(model.getInitialStates().getNumberOfSetBits() == 1, storm::exceptions::IllegalArgumentException, "Input model is required to have exactly one initial state.");
STORM_LOG_THROW(storm::settings::sparseDtmcEliminationModelCheckerSettings().getEliminationMethod() != storm::settings::modules::SparseDtmcEliminationModelCheckerSettings::EliminationMethod::State, storm::exceptions::InvalidArgumentException, "Unsupported elimination method for conditional probabilities.");
storm::storage::SparseMatrix<ValueType> backwardTransitions = model.getBackwardTransitions();
std::pair<storm::storage::BitVector, storm::storage::BitVector> statesWithProbability01 = storm::utility::graph::performProb01(backwardTransitions, trueStates, psiStates);
storm::storage::BitVector statesWithProbabilityGreater0 = ~statesWithProbability01.first;
storm::storage::BitVector statesWithProbability1 = std::move(statesWithProbability01.second);
STORM_LOG_THROW(model.getInitialStates().isSubsetOf(statesWithProbabilityGreater0), storm::exceptions::InvalidPropertyException, "The condition of the conditional probability has zero probability.");
// If the initial state is known to have probability 1 of satisfying the condition, we can apply regular model checking.
if (model.getInitialStates().isSubsetOf(statesWithProbability1)) {
std::shared_ptr<storm::logic::BooleanLiteralFormula> trueFormula = std::make_shared<storm::logic::BooleanLiteralFormula>(true);
std::shared_ptr<storm::logic::UntilFormula> untilFormula = std::make_shared<storm::logic::UntilFormula>(trueFormula, pathFormula.getLeftSubformula().asSharedPointer());
return this->computeUntilProbabilities(untilFormula);
}
// From now on, we know the condition does not have a trivial probability in the initial state.
// Compute the states that can be reached on a path that has a psi state in it.
storm::storage::BitVector statesWithPsiPredecessor = storm::utility::graph::performProbGreater0(model.getTransitionMatrix(), trueStates, psiStates);
storm::storage::BitVector statesReachingPhi = storm::utility::graph::performProbGreater0(backwardTransitions, trueStates, phiStates);
// The set of states we need to consider are those that have a non-zero probability to satisfy the condition or are on some path that has a psi state in it.
STORM_LOG_DEBUG("Initial state: " << model.getInitialStates());
STORM_LOG_DEBUG("Phi states: " << phiStates);
STORM_LOG_DEBUG("Psi state: " << psiStates);
STORM_LOG_DEBUG("States with probability greater 0 of satisfying the condition: " << statesWithProbabilityGreater0);
STORM_LOG_DEBUG("States with psi predecessor: " << statesWithPsiPredecessor);
STORM_LOG_DEBUG("States reaching phi: " << statesReachingPhi);
storm::storage::BitVector maybeStates = statesWithProbabilityGreater0 | (statesWithPsiPredecessor & statesReachingPhi);
STORM_LOG_DEBUG("Found " << maybeStates.getNumberOfSetBits() << " relevant states: " << maybeStates);
// Determine the set of initial states of the sub-DTMC.
storm::storage::BitVector newInitialStates = model.getInitialStates() % maybeStates;
// Create a dummy vector for the one-step probabilities.
std::vector<ValueType> oneStepProbabilities(maybeStates.getNumberOfSetBits(), storm::utility::zero<ValueType>());
// We then build the submatrix that only has the transitions of the maybe states.
storm::storage::SparseMatrix<ValueType> submatrix = model.getTransitionMatrix().getSubmatrix(false, maybeStates, maybeStates);
storm::storage::SparseMatrix<ValueType> submatrixTransposed = submatrix.transpose();
// The states we want to eliminate are those that are tagged with "maybe" but are not a phi or psi state.
phiStates = phiStates % maybeStates;
psiStates = psiStates % maybeStates;
// Keep only the states that we do not eliminate in the maybe states.
maybeStates = phiStates | psiStates;
STORM_LOG_DEBUG("Phi states in reduced model " << phiStates);
STORM_LOG_DEBUG("Psi states in reduced model " << psiStates);
storm::storage::BitVector statesToEliminate = ~maybeStates & ~newInitialStates;
STORM_LOG_DEBUG("Eliminating the states " << statesToEliminate);
// Before starting the model checking process, we assign priorities to states so we can use them to
// impose ordering constraints later.
std::vector<std::size_t> statePriorities = getStatePriorities(submatrix, submatrixTransposed, newInitialStates, oneStepProbabilities);
std::vector<storm::storage::sparse::state_type> states(statesToEliminate.begin(), statesToEliminate.end());
// Sort the states according to the priorities.
std::sort(states.begin(), states.end(), [&statePriorities] (storm::storage::sparse::state_type const& a, storm::storage::sparse::state_type const& b) { return statePriorities[a] < statePriorities[b]; });
STORM_PRINT_AND_LOG("Computing conditional probilities." << std::endl);
STORM_PRINT_AND_LOG("Eliminating " << states.size() << " states using the state elimination technique." << std::endl);
boost::optional<std::vector<ValueType>> missingStateRewards;
FlexibleSparseMatrix flexibleMatrix = getFlexibleSparseMatrix(submatrix);
FlexibleSparseMatrix flexibleBackwardTransitions = getFlexibleSparseMatrix(submatrixTransposed, true);
for (auto const& state : states) {
eliminateState(flexibleMatrix, oneStepProbabilities, state, flexibleBackwardTransitions, missingStateRewards);
}
STORM_PRINT_AND_LOG("Eliminated " << states.size() << " states." << std::endl);
eliminateState(flexibleMatrix, oneStepProbabilities, *newInitialStates.begin(), flexibleBackwardTransitions, missingStateRewards, false);
// Now eliminate all chains of phi or psi states.
for (auto phiState : phiStates) {
// Only eliminate the state if it has a successor that is not itself.
auto const& currentRow = flexibleMatrix.getRow(phiState);
if (currentRow.size() == 1) {
auto const& firstEntry = currentRow.front();
if (firstEntry.getColumn() == phiState) {
break;
}
}
eliminateState(flexibleMatrix, oneStepProbabilities, phiState, flexibleBackwardTransitions, missingStateRewards, false, true, phiStates);
}
for (auto psiState : psiStates) {
// Only eliminate the state if it has a successor that is not itself.
auto const& currentRow = flexibleMatrix.getRow(psiState);
if (currentRow.size() == 1) {
auto const& firstEntry = currentRow.front();
if (firstEntry.getColumn() == psiState) {
break;
}
}
eliminateState(flexibleMatrix, oneStepProbabilities, psiState, flexibleBackwardTransitions, missingStateRewards, false, true, psiStates);
}
ValueType numerator = storm::utility::zero<ValueType>();
ValueType denominator = storm::utility::zero<ValueType>();
for (auto const& trans1 : flexibleMatrix.getRow(*newInitialStates.begin())) {
auto initialStateSuccessor = trans1.getColumn();
if (phiStates.get(initialStateSuccessor)) {
ValueType additiveTerm = storm::utility::zero<ValueType>();
for (auto const& trans2 : flexibleMatrix.getRow(initialStateSuccessor)) {
STORM_LOG_ASSERT(psiStates.get(trans2.getColumn()), "Expected psi state.");
additiveTerm += trans2.getValue();
}
additiveTerm *= trans1.getValue();
numerator += additiveTerm;
denominator += additiveTerm;
} else {
STORM_LOG_ASSERT(psiStates.get(initialStateSuccessor), "Expected psi state.");
denominator += trans1.getValue();
ValueType additiveTerm = storm::utility::zero<ValueType>();
for (auto const& trans2 : flexibleMatrix.getRow(initialStateSuccessor)) {
STORM_LOG_ASSERT(phiStates.get(trans2.getColumn()), "Expected phi state.");
additiveTerm += trans2.getValue();
}
numerator += trans1.getValue() * additiveTerm;
}
}
return numerator / denominator;
}
template<typename ValueType>
std::unique_ptr<CheckResult> SparseDtmcEliminationModelChecker<ValueType>::checkBooleanLiteralFormula(storm::logic::BooleanLiteralFormula const& stateFormula) {
if (stateFormula.isTrueFormula()) {
return std::unique_ptr<CheckResult>(new ExplicitQualitativeCheckResult(storm::storage::BitVector(model.getNumberOfStates(), true)));
} else {
return std::unique_ptr<CheckResult>(new ExplicitQualitativeCheckResult(storm::storage::BitVector(model.getNumberOfStates())));
}
}
template<typename ValueType>
std::unique_ptr<CheckResult> SparseDtmcEliminationModelChecker<ValueType>::checkAtomicLabelFormula(storm::logic::AtomicLabelFormula const& stateFormula) {
STORM_LOG_THROW(model.hasAtomicProposition(stateFormula.getLabel()), storm::exceptions::InvalidPropertyException, "The property refers to unknown label '" << stateFormula.getLabel() << "'.");
return std::unique_ptr<CheckResult>(new ExplicitQualitativeCheckResult(model.getLabeledStates(stateFormula.getLabel())));
}
template<typename ValueType>
ValueType SparseDtmcEliminationModelChecker<ValueType>::computeReachabilityValue(storm::storage::SparseMatrix<ValueType> const& transitionMatrix, std::vector<ValueType>& oneStepProbabilities, storm::storage::SparseMatrix<ValueType> const& backwardTransitions, storm::storage::BitVector const& initialStates, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates, boost::optional<std::vector<ValueType>>& stateRewards, boost::optional<std::vector<std::size_t>> const& statePriorities) {
std::chrono::high_resolution_clock::time_point totalTimeStart = std::chrono::high_resolution_clock::now();
std::chrono::high_resolution_clock::time_point conversionStart = std::chrono::high_resolution_clock::now();
// Create a bit vector that represents the subsystem of states we still have to eliminate.
storm::storage::BitVector subsystem = storm::storage::BitVector(transitionMatrix.getRowCount(), true);
// Then, we convert the reduced matrix to a more flexible format to be able to perform state elimination more easily.
FlexibleSparseMatrix flexibleMatrix = getFlexibleSparseMatrix(transitionMatrix);
FlexibleSparseMatrix flexibleBackwardTransitions = getFlexibleSparseMatrix(backwardTransitions, true);
auto conversionEnd = std::chrono::high_resolution_clock::now();
std::chrono::high_resolution_clock::time_point modelCheckingStart = std::chrono::high_resolution_clock::now();
uint_fast64_t maximalDepth = 0;
if (storm::settings::sparseDtmcEliminationModelCheckerSettings().getEliminationMethod() == storm::settings::modules::SparseDtmcEliminationModelCheckerSettings::EliminationMethod::State) {
// If we are required to do pure state elimination, we simply create a vector of all states to
// eliminate and sort it according to the given priorities.
// Remove the initial state from the states which we need to eliminate.
subsystem &= ~initialStates;
std::vector<storm::storage::sparse::state_type> states(subsystem.begin(), subsystem.end());
if (statePriorities) {
std::sort(states.begin(), states.end(), [&statePriorities] (storm::storage::sparse::state_type const& a, storm::storage::sparse::state_type const& b) { return statePriorities.get()[a] < statePriorities.get()[b]; });
}
STORM_PRINT_AND_LOG("Eliminating " << states.size() << " states using the state elimination technique." << std::endl);
for (auto const& state : states) {
eliminateState(flexibleMatrix, oneStepProbabilities, state, flexibleBackwardTransitions, stateRewards);
}
STORM_PRINT_AND_LOG("Eliminated " << states.size() << " states." << std::endl);
} else if (storm::settings::sparseDtmcEliminationModelCheckerSettings().getEliminationMethod() == storm::settings::modules::SparseDtmcEliminationModelCheckerSettings::EliminationMethod::Hybrid) {
// When using the hybrid technique, we recursively treat the SCCs up to some size.
storm::utility::ConstantsComparator<ValueType> comparator;
std::vector<storm::storage::sparse::state_type> entryStateQueue;
STORM_PRINT_AND_LOG("Eliminating " << subsystem.size() << " states using the hybrid elimination technique." << std::endl);
maximalDepth = treatScc(flexibleMatrix, oneStepProbabilities, initialStates, subsystem, transitionMatrix, flexibleBackwardTransitions, false, 0, storm::settings::sparseDtmcEliminationModelCheckerSettings().getMaximalSccSize(), entryStateQueue, comparator, stateRewards, statePriorities);
// If the entry states were to be eliminated last, we need to do so now.
STORM_LOG_DEBUG("Eliminating " << entryStateQueue.size() << " entry states as a last step.");
if (storm::settings::sparseDtmcEliminationModelCheckerSettings().isEliminateEntryStatesLastSet()) {
for (auto const& state : entryStateQueue) {
eliminateState(flexibleMatrix, oneStepProbabilities, state, flexibleBackwardTransitions, stateRewards);
}
}
STORM_PRINT_AND_LOG("Eliminated " << subsystem.size() << " states." << std::endl);
}
// Finally eliminate initial state.
if (!stateRewards) {
// If we are computing probabilities, then we can simply call the state elimination procedure. It
// will scale the transition row of the initial state with 1/(1-loopProbability).
STORM_PRINT_AND_LOG("Eliminating initial state " << *initialStates.begin() << "." << std::endl);
eliminateState(flexibleMatrix, oneStepProbabilities, *initialStates.begin(), flexibleBackwardTransitions, stateRewards);
} else {
// If we are computing rewards, we cannot call the state elimination procedure for technical reasons.
// Instead, we need to get rid of a potential loop in this state explicitly.
// Start by finding the self-loop element. Since it can only be the only remaining outgoing transition
// of the initial state, this amounts to checking whether the outgoing transitions of the initial
// state are non-empty.
if (!flexibleMatrix.getRow(*initialStates.begin()).empty()) {
STORM_LOG_ASSERT(flexibleMatrix.getRow(*initialStates.begin()).size() == 1, "At most one outgoing transition expected at this point, but found more.");
STORM_LOG_ASSERT(flexibleMatrix.getRow(*initialStates.begin()).front().getColumn() == *initialStates.begin(), "Remaining entry should be a self-loop, but it is not.");
ValueType loopProbability = flexibleMatrix.getRow(*initialStates.begin()).front().getValue();
loopProbability = storm::utility::one<ValueType>() / (storm::utility::one<ValueType>() - loopProbability);
loopProbability = storm::utility::pow(loopProbability, 2);
STORM_PRINT_AND_LOG("Scaling the transition reward of the initial state.");
stateRewards.get()[(*initialStates.begin())] *= loopProbability;
}
}
// Make sure that we have eliminated all transitions from the initial state.
STORM_LOG_ASSERT(flexibleMatrix.getRow(*initialStates.begin()).empty(), "The transitions of the initial states are non-empty.");
std::chrono::high_resolution_clock::time_point modelCheckingEnd = std::chrono::high_resolution_clock::now();
std::chrono::high_resolution_clock::time_point totalTimeEnd = std::chrono::high_resolution_clock::now();
if (storm::settings::generalSettings().isShowStatisticsSet()) {
std::chrono::high_resolution_clock::duration conversionTime = conversionEnd - conversionStart;
std::chrono::milliseconds conversionTimeInMilliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(conversionTime);
std::chrono::high_resolution_clock::duration modelCheckingTime = modelCheckingEnd - modelCheckingStart;
std::chrono::milliseconds modelCheckingTimeInMilliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(modelCheckingTime);
std::chrono::high_resolution_clock::duration totalTime = totalTimeEnd - totalTimeStart;
std::chrono::milliseconds totalTimeInMilliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(totalTime);
STORM_PRINT_AND_LOG(std::endl);
STORM_PRINT_AND_LOG("Time breakdown:" << std::endl);
STORM_PRINT_AND_LOG(" * time for conversion: " << conversionTimeInMilliseconds.count() << "ms" << std::endl);
STORM_PRINT_AND_LOG(" * time for checking: " << modelCheckingTimeInMilliseconds.count() << "ms" << std::endl);
STORM_PRINT_AND_LOG("------------------------------------------" << std::endl);
STORM_PRINT_AND_LOG(" * total time: " << totalTimeInMilliseconds.count() << "ms" << std::endl);
STORM_PRINT_AND_LOG(std::endl);
STORM_PRINT_AND_LOG("Other:" << std::endl);
STORM_PRINT_AND_LOG(" * number of states eliminated: " << transitionMatrix.getRowCount() << std::endl);
if (storm::settings::sparseDtmcEliminationModelCheckerSettings().getEliminationMethod() == storm::settings::modules::SparseDtmcEliminationModelCheckerSettings::EliminationMethod::Hybrid) {
STORM_PRINT_AND_LOG(" * maximal depth of SCC decomposition: " << maximalDepth << std::endl);
}
}
// Now, we return the value for the only initial state.
if (stateRewards) {
return storm::utility::simplify(stateRewards.get()[*initialStates.begin()]);
} else {
return storm::utility::simplify(oneStepProbabilities[*initialStates.begin()]);
}
}
template<typename ValueType>
std::vector<std::size_t> SparseDtmcEliminationModelChecker<ValueType>::getStatePriorities(storm::storage::SparseMatrix<ValueType> const& transitionMatrix, storm::storage::SparseMatrix<ValueType> const& transitionMatrixTransposed, storm::storage::BitVector const& initialStates, std::vector<ValueType> const& oneStepProbabilities) {
std::vector<std::size_t> statePriorities(transitionMatrix.getRowCount());
std::vector<std::size_t> states(transitionMatrix.getRowCount());
for (std::size_t index = 0; index < states.size(); ++index) {
states[index] = index;
}
if (storm::settings::sparseDtmcEliminationModelCheckerSettings().getEliminationOrder() == storm::settings::modules::SparseDtmcEliminationModelCheckerSettings::EliminationOrder::Random) {
std::random_shuffle(states.begin(), states.end());
} else {
std::vector<std::size_t> distances;
if (storm::settings::sparseDtmcEliminationModelCheckerSettings().getEliminationOrder() == storm::settings::modules::SparseDtmcEliminationModelCheckerSettings::EliminationOrder::Forward || storm::settings::sparseDtmcEliminationModelCheckerSettings().getEliminationOrder() == storm::settings::modules::SparseDtmcEliminationModelCheckerSettings::EliminationOrder::ForwardReversed) {
distances = storm::utility::graph::getDistances(transitionMatrix, initialStates);
} else if (storm::settings::sparseDtmcEliminationModelCheckerSettings().getEliminationOrder() == storm::settings::modules::SparseDtmcEliminationModelCheckerSettings::EliminationOrder::Backward || storm::settings::sparseDtmcEliminationModelCheckerSettings().getEliminationOrder() == storm::settings::modules::SparseDtmcEliminationModelCheckerSettings::EliminationOrder::BackwardReversed) {
// Since the target states were eliminated from the matrix already, we construct a replacement by
// treating all states that have some non-zero probability to go to a target state in one step.
storm::utility::ConstantsComparator<ValueType> comparator;
storm::storage::BitVector pseudoTargetStates(transitionMatrix.getRowCount());
for (std::size_t index = 0; index < oneStepProbabilities.size(); ++index) {
if (!comparator.isZero(oneStepProbabilities[index])) {
pseudoTargetStates.set(index);
}
}
distances = storm::utility::graph::getDistances(transitionMatrixTransposed, pseudoTargetStates);
} else {
STORM_LOG_ASSERT(false, "Illegal sorting order selected.");
}
// In case of the forward or backward ordering, we can sort the states according to the distances.
if (storm::settings::sparseDtmcEliminationModelCheckerSettings().getEliminationOrder() == storm::settings::modules::SparseDtmcEliminationModelCheckerSettings::EliminationOrder::Forward || storm::settings::sparseDtmcEliminationModelCheckerSettings().getEliminationOrder() == storm::settings::modules::SparseDtmcEliminationModelCheckerSettings::EliminationOrder::Backward) {
std::sort(states.begin(), states.end(), [&distances] (storm::storage::sparse::state_type const& state1, storm::storage::sparse::state_type const& state2) { return distances[state1] < distances[state2]; } );
} else {
// Otherwise, we sort them according to descending distances.
std::sort(states.begin(), states.end(), [&distances] (storm::storage::sparse::state_type const& state1, storm::storage::sparse::state_type const& state2) { return distances[state1] > distances[state2]; } );
}
}
// Now convert the ordering of the states to priorities.
for (std::size_t index = 0; index < states.size(); ++index) {
statePriorities[states[index]] = index;
}
return statePriorities;
}
template<typename ValueType>
uint_fast64_t SparseDtmcEliminationModelChecker<ValueType>::treatScc(FlexibleSparseMatrix& matrix, std::vector<ValueType>& oneStepProbabilities, storm::storage::BitVector const& entryStates, storm::storage::BitVector const& scc, storm::storage::SparseMatrix<ValueType> const& forwardTransitions, FlexibleSparseMatrix& backwardTransitions, bool eliminateEntryStates, uint_fast64_t level, uint_fast64_t maximalSccSize, std::vector<storm::storage::sparse::state_type>& entryStateQueue, storm::utility::ConstantsComparator<ValueType> const& comparator, boost::optional<std::vector<ValueType>>& stateRewards, boost::optional<std::vector<std::size_t>> const& statePriorities) {
uint_fast64_t maximalDepth = level;
// If the SCCs are large enough, we try to split them further.
if (scc.getNumberOfSetBits() > maximalSccSize) {
STORM_LOG_DEBUG("SCC is large enough (" << scc.getNumberOfSetBits() << " states) to be decomposed further.");
// Here, we further decompose the SCC into sub-SCCs.
storm::storage::StronglyConnectedComponentDecomposition<ValueType> decomposition(forwardTransitions, scc & ~entryStates, false, false);
STORM_LOG_DEBUG("Decomposed SCC into " << decomposition.size() << " sub-SCCs.");
// Store a bit vector of remaining SCCs so we can be flexible when it comes to the order in which
// we eliminate the SCCs.
storm::storage::BitVector remainingSccs(decomposition.size(), true);
// First, get rid of the trivial SCCs.
std::vector<std::pair<storm::storage::sparse::state_type, uint_fast64_t>> trivialSccs;
for (uint_fast64_t sccIndex = 0; sccIndex < decomposition.size(); ++sccIndex) {
storm::storage::StronglyConnectedComponent const& scc = decomposition.getBlock(sccIndex);
if (scc.isTrivial()) {
storm::storage::sparse::state_type onlyState = *scc.begin();
trivialSccs.emplace_back(onlyState, sccIndex);
}
}
// If we are given priorities, sort the trivial SCCs accordingly.
if (statePriorities) {
std::sort(trivialSccs.begin(), trivialSccs.end(), [&statePriorities] (std::pair<storm::storage::sparse::state_type, uint_fast64_t> const& a, std::pair<storm::storage::sparse::state_type, uint_fast64_t> const& b) { return statePriorities.get()[a.first] < statePriorities.get()[b.first]; });
}
STORM_LOG_DEBUG("Eliminating " << trivialSccs.size() << " trivial SCCs.");
for (auto const& stateIndexPair : trivialSccs) {
eliminateState(matrix, oneStepProbabilities, stateIndexPair.first, backwardTransitions, stateRewards);
remainingSccs.set(stateIndexPair.second, false);
}
STORM_LOG_DEBUG("Eliminated all trivial SCCs.");
// And then recursively treat the remaining sub-SCCs.
STORM_LOG_DEBUG("Eliminating " << remainingSccs.getNumberOfSetBits() << " remaining SCCs on level " << level << ".");
for (auto sccIndex : remainingSccs) {
storm::storage::StronglyConnectedComponent const& newScc = decomposition.getBlock(sccIndex);
// Rewrite SCC into bit vector and subtract it from the remaining states.
storm::storage::BitVector newSccAsBitVector(forwardTransitions.getRowCount(), newScc.begin(), newScc.end());
// Determine the set of entry states of the SCC.
storm::storage::BitVector entryStates(forwardTransitions.getRowCount());
for (auto const& state : newScc) {
for (auto const& predecessor : backwardTransitions.getRow(state)) {
if (predecessor.getValue() != storm::utility::zero<ValueType>() && !newSccAsBitVector.get(predecessor.getColumn())) {
entryStates.set(state);
}
}
}
// Recursively descend in SCC-hierarchy.
uint_fast64_t depth = treatScc(matrix, oneStepProbabilities, entryStates, newSccAsBitVector, forwardTransitions, backwardTransitions, !storm::settings::sparseDtmcEliminationModelCheckerSettings().isEliminateEntryStatesLastSet(), level + 1, maximalSccSize, entryStateQueue, comparator, stateRewards, statePriorities);
maximalDepth = std::max(maximalDepth, depth);
}
} else {
// In this case, we perform simple state elimination in the current SCC.
STORM_LOG_DEBUG("SCC of size " << scc.getNumberOfSetBits() << " is small enough to be eliminated directly.");
storm::storage::BitVector remainingStates = scc & ~entryStates;
std::vector<uint_fast64_t> states(remainingStates.begin(), remainingStates.end());
// If we are given priorities, sort the trivial SCCs accordingly.
if (statePriorities) {
std::sort(states.begin(), states.end(), [&statePriorities] (storm::storage::sparse::state_type const& a, storm::storage::sparse::state_type const& b) { return statePriorities.get()[a] < statePriorities.get()[b]; });
}
// Eliminate the remaining states that do not have a self-loop (in the current, i.e. modified)
// transition probability matrix.
for (auto const& state : states) {
eliminateState(matrix, oneStepProbabilities, state, backwardTransitions, stateRewards);
}
STORM_LOG_DEBUG("Eliminated all states of SCC.");
}
// Finally, eliminate the entry states (if we are required to do so).
if (eliminateEntryStates) {
STORM_LOG_DEBUG("Finally, eliminating/adding entry states.");
for (auto state : entryStates) {
eliminateState(matrix, oneStepProbabilities, state, backwardTransitions, stateRewards);
}
STORM_LOG_DEBUG("Eliminated/added entry states.");
} else {
for (auto state : entryStates) {
entryStateQueue.push_back(state);
}
}
return maximalDepth;
}
namespace {
static int chunkCounter = 0;
static int counter = 0;
}
template<typename ValueType>
void SparseDtmcEliminationModelChecker<ValueType>::eliminateState(FlexibleSparseMatrix& matrix, std::vector<ValueType>& oneStepProbabilities, uint_fast64_t state, FlexibleSparseMatrix& backwardTransitions, boost::optional<std::vector<ValueType>>& stateRewards, bool removeForwardTransitions, bool constrained, storm::storage::BitVector const& predecessorConstraint) {
auto eliminationStart = std::chrono::high_resolution_clock::now();
++counter;
STORM_LOG_DEBUG("Eliminating state " << state << ".");
if (counter > matrix.getNumberOfRows() / 10) {
++chunkCounter;
STORM_PRINT_AND_LOG("Eliminated " << (chunkCounter * 10) << "% of the states." << std::endl);
counter = 0;
}
bool hasSelfLoop = false;
ValueType loopProbability = storm::utility:: <ValueType>();
// Start by finding loop probability.
typename FlexibleSparseMatrix::row_type& currentStateSuccessors = matrix.getRow(state);
for (auto const& entry : currentStateSuccessors) {
if (entry.getColumn() >= state) {
if (entry.getColumn() == state) {
loopProbability = entry.getValue();
hasSelfLoop = true;
}
break;
}
}
// Scale all entries in this row with (1 / (1 - loopProbability)) only in case there was a self-loop.
std::size_t scaledSuccessors = 0;
if (hasSelfLoop) {
loopProbability = storm::utility::one<ValueType>() / (storm::utility::one<ValueType>() - loopProbability);
storm::utility::simplify(loopProbability);
for (auto& entry : matrix.getRow(state)) {
// Only scale the non-diagonal entries.
if (entry.getColumn() != state) {
++scaledSuccessors;
entry.setValue(storm::utility::simplify(entry.getValue() * loopProbability));
}
}
if (!stateRewards) {
oneStepProbabilities[state] = oneStepProbabilities[state] * loopProbability;
}
}
STORM_LOG_DEBUG((hasSelfLoop ? "State has self-loop." : "State does not have a self-loop."));
// Now connect the predecessors of the state being eliminated with its successors.
typename FlexibleSparseMatrix::row_type& currentStatePredecessors = backwardTransitions.getRow(state);
std::size_t numberOfPredecessors = currentStatePredecessors.size();
std::size_t predecessorForwardTransitionCount = 0;
for (auto const& predecessorEntry : currentStatePredecessors) {
uint_fast64_t predecessor = predecessorEntry.getColumn();
// Skip the state itself as one of its predecessors.
if (predecessor == state) {
assert(hasSelfLoop);
continue;
}
// Skip the state if the elimination is constrained, but the predecessor is not in the constraint.
if (constrained && !predecessorConstraint.get(predecessor)) {
continue;
}
// First, find the probability with which the predecessor can move to the current state, because
// the other probabilities need to be scaled with this factor.
typename FlexibleSparseMatrix::row_type& predecessorForwardTransitions = matrix.getRow(predecessor);
predecessorForwardTransitionCount += predecessorForwardTransitions.size();
typename FlexibleSparseMatrix::row_type::iterator multiplyElement = std::find_if(predecessorForwardTransitions.begin(), predecessorForwardTransitions.end(), [&](storm::storage::MatrixEntry<typename FlexibleSparseMatrix::index_type, typename FlexibleSparseMatrix::value_type> const& a) { return a.getColumn() == state; });
// Make sure we have found the probability and set it to zero.
STORM_LOG_THROW(multiplyElement != predecessorForwardTransitions.end(), storm::exceptions::InvalidStateException, "No probability for successor found.");
ValueType multiplyFactor = multiplyElement->getValue();
multiplyElement->setValue(storm::utility::zero<ValueType>());
// At this point, we need to update the (forward) transitions of the predecessor.
typename FlexibleSparseMatrix::row_type::iterator first1 = predecessorForwardTransitions.begin();
typename FlexibleSparseMatrix::row_type::iterator last1 = predecessorForwardTransitions.end();
typename FlexibleSparseMatrix::row_type::iterator first2 = currentStateSuccessors.begin();
typename FlexibleSparseMatrix::row_type::iterator last2 = currentStateSuccessors.end();
typename FlexibleSparseMatrix::row_type newSuccessors;
newSuccessors.reserve((last1 - first1) + (last2 - first2));
std::insert_iterator<typename FlexibleSparseMatrix::row_type> result(newSuccessors, newSuccessors.end());
// Now we merge the two successor lists. (Code taken from std::set_union and modified to suit our needs).
for (; first1 != last1; ++result) {
// Skip the transitions to the state that is currently being eliminated.
if (first1->getColumn() == state || (first2 != last2 && first2->getColumn() == state)) {
if (first1->getColumn() == state) {
++first1;
}
if (first2 != last2 && first2->getColumn() == state) {
++first2;
}
continue;
}
if (first2 == last2) {
std::copy_if(first1, last1, result, [&] (storm::storage::MatrixEntry<typename FlexibleSparseMatrix::index_type, typename FlexibleSparseMatrix::value_type> const& a) { return a.getColumn() != state; } );
break;
}
if (first2->getColumn() < first1->getColumn()) {
*result = storm::utility::simplify(std::move(*first2 * multiplyFactor));
++first2;
} else if (first1->getColumn() < first2->getColumn()) {
*result = *first1;
++first1;
} else {
*result = storm::storage::MatrixEntry<typename FlexibleSparseMatrix::index_type, typename FlexibleSparseMatrix::value_type>(first1->getColumn(), storm::utility::simplify(first1->getValue() + storm::utility::simplify(multiplyFactor * first2->getValue())));
++first1;
++first2;
}
}
for (; first2 != last2; ++first2) {
if (first2->getColumn() != state) {
*result = storm::utility::simplify(std::move(*first2 * multiplyFactor));
}
}
// Now move the new transitions in place.
predecessorForwardTransitions = std::move(newSuccessors);
if (!stateRewards) {
// Add the probabilities to go to a target state in just one step if we have to compute probabilities.
oneStepProbabilities[predecessor] += storm::utility::simplify(multiplyFactor * oneStepProbabilities[state]);
STORM_LOG_DEBUG("Fixed new next-state probabilities of predecessor states.");
} else {
// If we are computing rewards, we basically scale the state reward of the state to eliminate and
// add the result to the state reward of the predecessor.
if (hasSelfLoop) {
stateRewards.get()[predecessor] += storm::utility::simplify(multiplyFactor * storm::utility::pow(loopProbability, 2) * stateRewards.get()[state]);
} else {
stateRewards.get()[predecessor] += storm::utility::simplify(multiplyFactor * stateRewards.get()[state]);
}
}
}
// Finally, we need to add the predecessor to the set of predecessors of every successor.
for (auto const& successorEntry : currentStateSuccessors) {
typename FlexibleSparseMatrix::row_type& successorBackwardTransitions = backwardTransitions.getRow(successorEntry.getColumn());
// Delete the current state as a predecessor of the successor state.
typename FlexibleSparseMatrix::row_type::iterator elimIt = std::find_if(successorBackwardTransitions.begin(), successorBackwardTransitions.end(), [&](storm::storage::MatrixEntry<typename FlexibleSparseMatrix::index_type, typename FlexibleSparseMatrix::value_type> const& a) { return a.getColumn() == state; });
if (elimIt != successorBackwardTransitions.end()) {
successorBackwardTransitions.erase(elimIt);
}
typename FlexibleSparseMatrix::row_type::iterator first1 = successorBackwardTransitions.begin();
typename FlexibleSparseMatrix::row_type::iterator last1 = successorBackwardTransitions.end();
typename FlexibleSparseMatrix::row_type::iterator first2 = currentStatePredecessors.begin();
typename FlexibleSparseMatrix::row_type::iterator last2 = currentStatePredecessors.end();
typename FlexibleSparseMatrix::row_type newPredecessors;
newPredecessors.reserve((last1 - first1) + (last2 - first2));
std::insert_iterator<typename FlexibleSparseMatrix::row_type> result(newPredecessors, newPredecessors.end());
for (; first1 != last1; ++result) {
if (first2 == last2) {
std::copy_if(first1, last1, result, [&] (storm::storage::MatrixEntry<typename FlexibleSparseMatrix::index_type, typename FlexibleSparseMatrix::value_type> const& a) { return a.getColumn() != state; });
break;
}
if (first2->getColumn() < first1->getColumn()) {
if (first2->getColumn() != state) {
*result = *first2;
}
++first2;
} else {
if (first1->getColumn() != state) {
*result = *first1;
}
if (first1->getColumn() == first2->getColumn()) {
++first2;
}
++first1;
}
}
std::copy_if(first2, last2, result, [&] (storm::storage::MatrixEntry<typename FlexibleSparseMatrix::index_type, typename FlexibleSparseMatrix::value_type> const& a) { return a.getColumn() != state; });
// Now move the new predecessors in place.
successorBackwardTransitions = std::move(newPredecessors);
}
STORM_LOG_DEBUG("Fixed predecessor lists of successor states.");
if (removeForwardTransitions) {
// Clear the eliminated row to reduce memory consumption.
currentStateSuccessors.clear();
currentStateSuccessors.shrink_to_fit();
}
if (!constrained) {
// FIXME: is this safe? If the elimination is constrained, we might have to repair the predecessor
// relation.
currentStatePredecessors.clear();
currentStatePredecessors.shrink_to_fit();
}
auto eliminationEnd = std::chrono::high_resolution_clock::now();
auto eliminationTime = eliminationEnd - eliminationStart;
}
template<typename ValueType>
SparseDtmcEliminationModelChecker<ValueType>::FlexibleSparseMatrix::FlexibleSparseMatrix(index_type rows) : data(rows) {
// Intentionally left empty.
}
template<typename ValueType>
void SparseDtmcEliminationModelChecker<ValueType>::FlexibleSparseMatrix::reserveInRow(index_type row, index_type numberOfElements) {
this->data[row].reserve(numberOfElements);
}
template<typename ValueType>
typename SparseDtmcEliminationModelChecker<ValueType>::FlexibleSparseMatrix::row_type& SparseDtmcEliminationModelChecker<ValueType>::FlexibleSparseMatrix::getRow(index_type index) {
return this->data[index];
}
template<typename ValueType>
typename SparseDtmcEliminationModelChecker<ValueType>::FlexibleSparseMatrix::row_type const& SparseDtmcEliminationModelChecker<ValueType>::FlexibleSparseMatrix::getRow(index_type index) const {
return this->data[index];
}
template<typename ValueType>
typename SparseDtmcEliminationModelChecker<ValueType>::FlexibleSparseMatrix::index_type SparseDtmcEliminationModelChecker<ValueType>::FlexibleSparseMatrix::getNumberOfRows() const {
return this->data.size();
}
template<typename ValueType>
bool SparseDtmcEliminationModelChecker<ValueType>::FlexibleSparseMatrix::hasSelfLoop(storm::storage::sparse::state_type state) {
for (auto const& entry : this->getRow(state)) {
if (entry.getColumn() < state) {
continue;
} else if (entry.getColumn() > state) {
return false;
} else if (entry.getColumn() == state) {
return true;
}
}
return false;
}
template<typename ValueType>
void SparseDtmcEliminationModelChecker<ValueType>::FlexibleSparseMatrix::print() const {
for (uint_fast64_t index = 0; index < this->data.size(); ++index) {
std::cout << index << " - ";
for (auto const& element : this->getRow(index)) {
std::cout << "(" << element.getColumn() << ", " << element.getValue() << ") ";
}
std::cout << std::endl;
}
}
template<typename ValueType>
typename SparseDtmcEliminationModelChecker<ValueType>::FlexibleSparseMatrix SparseDtmcEliminationModelChecker<ValueType>::getFlexibleSparseMatrix(storm::storage::SparseMatrix<ValueType> const& matrix, bool setAllValuesToOne) {
FlexibleSparseMatrix flexibleMatrix(matrix.getRowCount());
for (typename FlexibleSparseMatrix::index_type rowIndex = 0; rowIndex < matrix.getRowCount(); ++rowIndex) {
typename storm::storage::SparseMatrix<ValueType>::const_rows row = matrix.getRow(rowIndex);
flexibleMatrix.reserveInRow(rowIndex, row.getNumberOfEntries());
for (auto const& element : row) {
if (setAllValuesToOne) {
flexibleMatrix.getRow(rowIndex).emplace_back(element.getColumn(), storm::utility::one<ValueType>());
} else {
flexibleMatrix.getRow(rowIndex).emplace_back(element);
}
}
}
return flexibleMatrix;
}
template class SparseDtmcEliminationModelChecker<double>;
#ifdef PARAMETRIC_SYSTEMS
template class FlexibleSparseMatrix<RationalFunction>;
template class SparseDtmcEliminationModelChecker<RationalFunction>;
#endif
} // namespace modelchecker
} // namespace storm

75
src/modelchecker/reachability/SparseDtmcEliminationModelChecker.h

@ -0,0 +1,75 @@
#ifndef STORM_MODELCHECKER_REACHABILITY_SPARSEDTMCELIMINATIONMODELCHECKER_H_
#define STORM_MODELCHECKER_REACHABILITY_SPARSEDTMCELIMINATIONMODELCHECKER_H_
#include "src/storage/sparse/StateType.h"
#include "src/models/Dtmc.h"
#include "src/modelchecker/AbstractModelChecker.h"
namespace storm {
namespace modelchecker {
template<typename ValueType>
class SparseDtmcEliminationModelChecker : public AbstractModelChecker {
public:
explicit SparseDtmcEliminationModelChecker(storm::models::Dtmc<ValueType> const& model);
// The implemented methods of the AbstractModelChecker interface.
virtual bool canHandle(storm::logic::Formula const& formula) const override;
virtual std::unique_ptr<CheckResult> computeUntilProbabilities(storm::logic::UntilFormula const& pathFormula, bool qualitative = false, boost::optional<storm::logic::OptimalityType> const& optimalityType = boost::optional<storm::logic::OptimalityType>()) override;
virtual std::unique_ptr<CheckResult> computeReachabilityRewards(storm::logic::ReachabilityRewardFormula const& rewardPathFormula, bool qualitative = false, boost::optional<storm::logic::OptimalityType> const& optimalityType = boost::optional<storm::logic::OptimalityType>()) override;
virtual std::unique_ptr<CheckResult> computeConditionalProbabilities(storm::logic::ConditionalPathFormula const& pathFormula, bool qualitative = false, boost::optional<storm::logic::OptimalityType> const& optimalityType = boost::optional<storm::logic::OptimalityType>()) override;
virtual std::unique_ptr<CheckResult> checkBooleanLiteralFormula(storm::logic::BooleanLiteralFormula const& stateFormula) override;
virtual std::unique_ptr<CheckResult> checkAtomicLabelFormula(storm::logic::AtomicLabelFormula const& stateFormula) override;
private:
class FlexibleSparseMatrix {
public:
typedef uint_fast64_t index_type;
typedef ValueType value_type;
typedef std::vector<storm::storage::MatrixEntry<index_type, value_type>> row_type;
typedef typename row_type::iterator iterator;
typedef typename row_type::const_iterator const_iterator;
FlexibleSparseMatrix() = default;
FlexibleSparseMatrix(index_type rows);
void reserveInRow(index_type row, index_type numberOfElements);
row_type& getRow(index_type);
row_type const& getRow(index_type) const;
index_type getNumberOfRows() const;
void print() const;
/*!
* Checks whether the given state has a self-loop with an arbitrary probability in the given probability matrix.
*
* @param state The state for which to check whether it possesses a self-loop.
* @param matrix The matrix in which to look for the loop.
* @return True iff the given state has a self-loop with an arbitrary probability in the given probability matrix.
*/
bool hasSelfLoop(storm::storage::sparse::state_type state);
private:
std::vector<row_type> data;
};
static ValueType computeReachabilityValue(storm::storage::SparseMatrix<ValueType> const& transitionMatrix, std::vector<ValueType>& oneStepProbabilities, storm::storage::SparseMatrix<ValueType> const& backwardTransitions, storm::storage::BitVector const& initialStates, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates, boost::optional<std::vector<ValueType>>& stateRewards, boost::optional<std::vector<std::size_t>> const& statePriorities = {});
static uint_fast64_t treatScc(FlexibleSparseMatrix& matrix, std::vector<ValueType>& oneStepProbabilities, storm::storage::BitVector const& entryStates, storm::storage::BitVector const& scc, storm::storage::SparseMatrix<ValueType> const& forwardTransitions, FlexibleSparseMatrix& backwardTransitions, bool eliminateEntryStates, uint_fast64_t level, uint_fast64_t maximalSccSize, std::vector<storm::storage::sparse::state_type>& entryStateQueue, storm::utility::ConstantsComparator<ValueType> const& comparator, boost::optional<std::vector<ValueType>>& stateRewards, boost::optional<std::vector<std::size_t>> const& statePriorities = {});
static FlexibleSparseMatrix getFlexibleSparseMatrix(storm::storage::SparseMatrix<ValueType> const& matrix, bool setAllValuesToOne = false);
static void eliminateState(FlexibleSparseMatrix& matrix, std::vector<ValueType>& oneStepProbabilities, uint_fast64_t state, FlexibleSparseMatrix& backwardTransitions, boost::optional<std::vector<ValueType>>& stateRewards, bool removeForwardTransitions = true, bool constrained = false, storm::storage::BitVector const& predecessorConstraint = storm::storage::BitVector());
static std::vector<std::size_t> getStatePriorities(storm::storage::SparseMatrix<ValueType> const& transitionMatrix, storm::storage::SparseMatrix<ValueType> const& transitionMatrixTransposed, storm::storage::BitVector const& initialStates, std::vector<ValueType> const& oneStepProbabilities);
// The model this model checker is supposed to analyze.
storm::models::Dtmc<ValueType> const& model;
};
} // namespace modelchecker
} // namespace storm
#endif /* STORM_MODELCHECKER_REACHABILITY_SPARSEDTMCELIMINATIONMODELCHECKER_H_ */

435
src/modelchecker/reachability/SparseSccModelChecker.cpp

@ -1,435 +0,0 @@
#include "src/modelchecker/reachability/SparseSccModelChecker.h"
#include <algorithm>
#include "src/storage/StronglyConnectedComponentDecomposition.h"
#include "src/utility/graph.h"
#include "src/utility/vector.h"
#include "src/exceptions/InvalidStateException.h"
#include "src/utility/macros.h"
namespace storm {
namespace modelchecker {
namespace reachability {
template<typename ValueType>
static ValueType&& simplify(ValueType&& value) {
// In the general case, we don't to anything here, but merely return the value. If something else is
// supposed to happen here, the templated function can be specialized for this particular type.
return std::forward<ValueType>(value);
}
template<typename IndexType, typename ValueType>
static storm::storage::MatrixEntry<IndexType, ValueType>&& simplify(storm::storage::MatrixEntry<IndexType, ValueType>&& matrixEntry) {
simplify(matrixEntry.getValue());
return std::move(matrixEntry);
}
template<typename IndexType, typename ValueType>
static storm::storage::MatrixEntry<IndexType, ValueType>& simplify(storm::storage::MatrixEntry<IndexType, ValueType>& matrixEntry) {
matrixEntry.setValue(simplify(matrixEntry.getValue()));
return matrixEntry;
}
template<typename ValueType>
ValueType SparseSccModelChecker<ValueType>::computeReachabilityProbability(storm::models::Dtmc<ValueType> const& dtmc, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates) {
// First, do some sanity checks to establish some required properties.
STORM_LOG_THROW(dtmc.getInitialStates().getNumberOfSetBits() == 1, storm::exceptions::IllegalArgumentException, "Input model is required to have exactly one initial state.");
typename FlexibleSparseMatrix<ValueType>::index_type initialStateIndex = *dtmc.getInitialStates().begin();
// Then, compute the subset of states that has a probability of 0 or 1, respectively.
std::pair<storm::storage::BitVector, storm::storage::BitVector> statesWithProbability01 = storm::utility::graph::performProb01(dtmc, phiStates, psiStates);
storm::storage::BitVector statesWithProbability0 = statesWithProbability01.first;
storm::storage::BitVector statesWithProbability1 = statesWithProbability01.second;
storm::storage::BitVector maybeStates = ~(statesWithProbability0 | statesWithProbability1);
// If the initial state is known to have either probability 0 or 1, we can directly return the result.
if (!maybeStates.get(initialStateIndex)) {
return statesWithProbability0.get(initialStateIndex) ? 0 : 1;
}
// Determine the set of states that is reachable from the initial state without jumping over a target state.
storm::storage::BitVector reachableStates = storm::utility::graph::getReachableStates(dtmc.getTransitionMatrix(), dtmc.getInitialStates(), maybeStates, statesWithProbability1);
// Subtract from the maybe states the set of states that is not reachable (on a path from the initial to a target state).
maybeStates &= reachableStates;
// Create a vector for the probabilities to go to a state with probability 1 in one step.
std::vector<ValueType> oneStepProbabilities = dtmc.getTransitionMatrix().getConstrainedRowSumVector(maybeStates, statesWithProbability1);
// Determine the set of initial states of the sub-DTMC.
storm::storage::BitVector newInitialStates = dtmc.getInitialStates() % maybeStates;
// We then build the submatrix that only has the transitions of the maybe states.
storm::storage::SparseMatrix<ValueType> submatrix = dtmc.getTransitionMatrix().getSubmatrix(false, maybeStates, maybeStates);
// Create a bit vector that represents the subsystem of states we still have to eliminate.
storm::storage::BitVector subsystem = storm::storage::BitVector(maybeStates.getNumberOfSetBits(), true);
// Then, we convert the reduced matrix to a more flexible format to be able to perform state elimination more easily.
FlexibleSparseMatrix<ValueType> flexibleMatrix = getFlexibleSparseMatrix(submatrix);
FlexibleSparseMatrix<ValueType> flexibleBackwardTransitions = getFlexibleSparseMatrix(submatrix.transpose(), true);
// Then, we recursively treat all SCCs.
treatScc(dtmc, flexibleMatrix, oneStepProbabilities, newInitialStates, subsystem, submatrix, flexibleBackwardTransitions, false, 0);
// Now, we return the value for the only initial state.
return oneStepProbabilities[initialStateIndex];
}
template<typename ValueType>
void SparseSccModelChecker<ValueType>::treatScc(storm::models::Dtmc<ValueType> const& dtmc, FlexibleSparseMatrix<ValueType>& matrix, std::vector<ValueType>& oneStepProbabilities, storm::storage::BitVector const& entryStates, storm::storage::BitVector const& scc, storm::storage::SparseMatrix<ValueType> const& forwardTransitions, FlexibleSparseMatrix<ValueType>& backwardTransitions, bool eliminateEntryStates, uint_fast64_t level) {
// If the SCCs are large enough, we try to split them further.
if (scc.getNumberOfSetBits() > SparseSccModelChecker<ValueType>::maximalSccSize) {
// Here, we further decompose the SCC into sub-SCCs.
storm::storage::StronglyConnectedComponentDecomposition<ValueType> decomposition(forwardTransitions, scc & ~entryStates, false, false);
// To eliminate the remaining one-state SCCs, we need to keep track of them.
// storm::storage::BitVector remainingStates(scc);
// Store a bit vector of remaining SCCs so we can be flexible when it comes to the order in which
// we eliminate the SCCs.
storm::storage::BitVector remainingSccs(decomposition.size(), true);
// First, get rid of the trivial SCCs.
for (uint_fast64_t sccIndex = 0; sccIndex < decomposition.size(); ++sccIndex) {
storm::storage::StronglyConnectedComponent const& scc = decomposition.getBlock(sccIndex);
if (scc.isTrivial()) {
storm::storage::sparse::state_type onlyState = *scc.begin();
eliminateState(matrix, oneStepProbabilities, onlyState, backwardTransitions);
remainingSccs.set(sccIndex, false);
}
}
// And then recursively treat the remaining sub-SCCs.
for (auto sccIndex : remainingSccs) {
storm::storage::StronglyConnectedComponent const& newScc = decomposition.getBlock(sccIndex);
// If the SCC consists of just one state, we do not explore it recursively, but rather eliminate
// it directly.
if (newScc.size() == 1) {
continue;
}
// Rewrite SCC into bit vector and subtract it from the remaining states.
storm::storage::BitVector newSccAsBitVector(forwardTransitions.getRowCount(), newScc.begin(), newScc.end());
// remainingStates &= ~newSccAsBitVector;
// Determine the set of entry states of the SCC.
storm::storage::BitVector entryStates(dtmc.getNumberOfStates());
for (auto const& state : newScc) {
for (auto const& predecessor : backwardTransitions.getRow(state)) {
if (predecessor.getValue() > storm::utility::constantZero<ValueType>() && !newSccAsBitVector.get(predecessor.getColumn())) {
entryStates.set(state);
}
}
}
// Recursively descend in SCC-hierarchy.
treatScc(dtmc, matrix, oneStepProbabilities, entryStates, newSccAsBitVector, forwardTransitions, backwardTransitions, true, level + 1);
}
// If we are not supposed to eliminate the entry states, we need to take them out of the set of
// remaining states.
// if (!eliminateEntryStates) {
// remainingStates &= ~entryStates;
// }
//
// Now that we eliminated all non-trivial sub-SCCs, we need to take care of trivial sub-SCCs.
// Therefore, we need to eliminate all states.
// for (auto const& state : remainingStates) {
// eliminateState(matrix, oneStepProbabilities, state, backwardTransitions);
// }
} else {
// In this case, we perform simple state elimination in the current SCC.
storm::storage::BitVector remainingStates = scc;
// if (eliminateEntryStates) {
remainingStates &= ~entryStates;
// }
// Eliminate the remaining states.
for (auto const& state : remainingStates) {
eliminateState(matrix, oneStepProbabilities, state, backwardTransitions);
}
// Finally, eliminate the entry states (if we are allowed to do so).
if (eliminateEntryStates) {
for (auto state : entryStates) {
eliminateState(matrix, oneStepProbabilities, state, backwardTransitions);
}
}
}
}
template<typename ValueType>
void SparseSccModelChecker<ValueType>::eliminateState(FlexibleSparseMatrix<ValueType>& matrix, std::vector<ValueType>& oneStepProbabilities, uint_fast64_t state, FlexibleSparseMatrix<ValueType>& backwardTransitions) {
bool hasSelfLoop = false;
ValueType loopProbability = storm::utility::constantZero<ValueType>();
// Start by finding loop probability.
typename FlexibleSparseMatrix<ValueType>::row_type& currentStateSuccessors = matrix.getRow(state);
for (auto const& entry : currentStateSuccessors) {
if (entry.getColumn() >= state) {
if (entry.getColumn() == state) {
loopProbability = entry.getValue();
hasSelfLoop = true;
}
break;
}
}
// Scale all entries in this row with (1 / (1 - loopProbability)) only in case there was a self-loop.
if (hasSelfLoop) {
loopProbability = 1 / (1 - loopProbability);
simplify(loopProbability);
for (auto& entry : matrix.getRow(state)) {
entry.setValue(simplify(entry.getValue() * loopProbability));
}
oneStepProbabilities[state] = simplify(oneStepProbabilities[state] * loopProbability);
}
// Now connect the predecessors of the state being eliminated with its successors.
typename FlexibleSparseMatrix<ValueType>::row_type& currentStatePredecessors = backwardTransitions.getRow(state);
for (auto const& predecessorEntry : currentStatePredecessors) {
uint_fast64_t predecessor = predecessorEntry.getColumn();
// Skip the state itself as one of its predecessors.
if (predecessor == state) {
continue;
}
// First, find the probability with which the predecessor can move to the current state, because
// the other probabilities need to be scaled with this factor.
typename FlexibleSparseMatrix<ValueType>::row_type& predecessorForwardTransitions = matrix.getRow(predecessor);
typename FlexibleSparseMatrix<ValueType>::row_type::iterator multiplyElement = std::find_if(predecessorForwardTransitions.begin(), predecessorForwardTransitions.end(), [&](storm::storage::MatrixEntry<typename FlexibleSparseMatrix<ValueType>::index_type, typename FlexibleSparseMatrix<ValueType>::value_type> const& a) { return a.getColumn() == state; });
// Make sure we have found the probability and set it to zero.
STORM_LOG_THROW(multiplyElement != predecessorForwardTransitions.end(), storm::exceptions::InvalidStateException, "No probability for successor found.");
ValueType multiplyFactor = multiplyElement->getValue();
multiplyElement->setValue(0);
// At this point, we need to update the (forward) transitions of the predecessor.
typename FlexibleSparseMatrix<ValueType>::row_type::iterator first1 = predecessorForwardTransitions.begin();
typename FlexibleSparseMatrix<ValueType>::row_type::iterator last1 = predecessorForwardTransitions.end();
typename FlexibleSparseMatrix<ValueType>::row_type::iterator first2 = currentStateSuccessors.begin();
typename FlexibleSparseMatrix<ValueType>::row_type::iterator last2 = currentStateSuccessors.end();
typename FlexibleSparseMatrix<ValueType>::row_type newSuccessors;
newSuccessors.reserve((last1 - first1) + (last2 - first2));
std::insert_iterator<typename FlexibleSparseMatrix<ValueType>::row_type> result(newSuccessors, newSuccessors.end());
// Now we merge the two successor lists. (Code taken from std::set_union and modified to suit our needs).
for (; first1 != last1; ++result) {
// Skip the transitions to the state that is currently being eliminated.
if (first1->getColumn() == state || (first2 != last2 && first2->getColumn() == state)) {
if (first1->getColumn() == state) {
++first1;
}
if (first2 != last2 && first2->getColumn() == state) {
++first2;
}
continue;
}
if (first2 == last2) {
std::copy_if(first1, last1, result, [&] (storm::storage::MatrixEntry<typename FlexibleSparseMatrix<ValueType>::index_type, typename FlexibleSparseMatrix<ValueType>::value_type> const& a) { return a.getColumn() != state; } );
break;
}
if (first2->getColumn() < first1->getColumn()) {
*result = simplify(*first2 * multiplyFactor);
++first2;
} else if (first1->getColumn() < first2->getColumn()) {
*result = *first1;
++first1;
} else {
*result = storm::storage::MatrixEntry<typename FlexibleSparseMatrix<ValueType>::index_type, typename FlexibleSparseMatrix<ValueType>::value_type>(first1->getColumn(), simplify(first1->getValue() + simplify(multiplyFactor * first2->getValue())));
++first1;
++first2;
}
}
for (; first2 != last2; ++first2) {
if (first2->getColumn() != state) {
*result = simplify(*first2 * multiplyFactor);
}
}
// Now move the new transitions in place.
predecessorForwardTransitions = std::move(newSuccessors);
// Add the probabilities to go to a target state in just one step.
oneStepProbabilities[predecessor] = simplify(oneStepProbabilities[predecessor] + simplify(multiplyFactor * oneStepProbabilities[state]));
}
// Finally, we need to add the predecessor to the set of predecessors of every successor.
for (auto const& successorEntry : currentStateSuccessors) {
typename FlexibleSparseMatrix<ValueType>::row_type& successorBackwardTransitions = backwardTransitions.getRow(successorEntry.getColumn());
// Delete the current state as a predecessor of the successor state.
typename FlexibleSparseMatrix<ValueType>::row_type::iterator elimIt = std::find_if(successorBackwardTransitions.begin(), successorBackwardTransitions.end(), [&](storm::storage::MatrixEntry<typename FlexibleSparseMatrix<ValueType>::index_type, typename FlexibleSparseMatrix<ValueType>::value_type> const& a) { return a.getColumn() == state; });
if (elimIt != successorBackwardTransitions.end()) {
successorBackwardTransitions.erase(elimIt);
}
typename FlexibleSparseMatrix<ValueType>::row_type::iterator first1 = successorBackwardTransitions.begin();
typename FlexibleSparseMatrix<ValueType>::row_type::iterator last1 = successorBackwardTransitions.end();
typename FlexibleSparseMatrix<ValueType>::row_type::iterator first2 = currentStatePredecessors.begin();
typename FlexibleSparseMatrix<ValueType>::row_type::iterator last2 = currentStatePredecessors.end();
typename FlexibleSparseMatrix<ValueType>::row_type newPredecessors;
newPredecessors.reserve((last1 - first1) + (last2 - first2));
std::insert_iterator<typename FlexibleSparseMatrix<ValueType>::row_type> result(newPredecessors, newPredecessors.end());
for (; first1 != last1; ++result) {
if (first2 == last2) {
std::copy_if(first1, last1, result, [&] (storm::storage::MatrixEntry<typename FlexibleSparseMatrix<ValueType>::index_type, typename FlexibleSparseMatrix<ValueType>::value_type> const& a) { return a.getColumn() != state; });
break;
}
if (first2->getColumn() < first1->getColumn()) {
if (first2->getColumn() != state) {
*result = *first2;
}
++first2;
} else {
if (first1->getColumn() != state) {
*result = *first1;
}
if (first1->getColumn() == first2->getColumn()) {
++first2;
}
++first1;
}
}
std::copy_if(first2, last2, result, [&] (storm::storage::MatrixEntry<typename FlexibleSparseMatrix<ValueType>::index_type, typename FlexibleSparseMatrix<ValueType>::value_type> const& a) { return a.getColumn() != state; });
// Now move the new predecessors in place.
successorBackwardTransitions = std::move(newPredecessors);
}
// Clear the eliminated row to reduce memory consumption.
currentStateSuccessors.clear();
currentStateSuccessors.shrink_to_fit();
}
template <typename ValueType>
bool SparseSccModelChecker<ValueType>::eliminateStateInPlace(storm::storage::SparseMatrix<ValueType>& matrix, std::vector<ValueType>& oneStepProbabilities, uint_fast64_t state, storm::storage::SparseMatrix<ValueType>& backwardTransitions) {
typename storm::storage::SparseMatrix<ValueType>::iterator forwardElement = matrix.getRow(state).begin();
typename storm::storage::SparseMatrix<ValueType>::iterator backwardElement = backwardTransitions.getRow(state).begin();
if (forwardElement->getValue() != storm::utility::constantOne<ValueType>() || backwardElement->getValue() != storm::utility::constantOne<ValueType>()) {
return false;
}
std::cout << "eliminating " << state << std::endl;
std::cout << "fwd element: " << *forwardElement << " and bwd element: " << *backwardElement << std::endl;
// Find the element of the predecessor that moves to the state that we want to eliminate.
typename storm::storage::SparseMatrix<ValueType>::rows forwardRow = matrix.getRow(backwardElement->getColumn());
typename storm::storage::SparseMatrix<ValueType>::iterator multiplyElement = std::find_if(forwardRow.begin(), forwardRow.end(), [&](storm::storage::MatrixEntry<typename storm::storage::SparseMatrix<ValueType>::index_type, typename storm::storage::SparseMatrix<ValueType>::value_type> const& a) { return a.getColumn() == state; });
std::cout << "before fwd: " << std::endl;
for (auto element : matrix.getRow(backwardElement->getColumn())) {
std::cout << element << ", " << std::endl;
}
// Modify the forward probability entry of the predecessor.
multiplyElement->setValue(multiplyElement->getValue() * forwardElement->getValue());
multiplyElement->setColumn(forwardElement->getColumn());
// Modify the one-step probability for the predecessor if necessary.
if (oneStepProbabilities[state] != storm::utility::constantZero<ValueType>()) {
oneStepProbabilities[backwardElement->getColumn()] += multiplyElement->getValue() * oneStepProbabilities[state];
}
// If the forward entry is not at the right position, we need to move it there.
if (multiplyElement != forwardRow.begin() && multiplyElement->getColumn() < (multiplyElement - 1)->getColumn()) {
while (multiplyElement != forwardRow.begin() && multiplyElement->getColumn() < (multiplyElement - 1)->getColumn()) {
std::swap(*multiplyElement, *(multiplyElement - 1));
--multiplyElement;
}
} else if ((multiplyElement + 1) != forwardRow.end() && multiplyElement->getColumn() > (multiplyElement + 1)->getColumn()) {
while ((multiplyElement + 1) != forwardRow.end() && multiplyElement->getColumn() > (multiplyElement + 1)->getColumn()) {
std::swap(*multiplyElement, *(multiplyElement + 1));
++multiplyElement;
}
}
std::cout << "after fwd: " << std::endl;
for (auto element : matrix.getRow(backwardElement->getColumn())) {
std::cout << element << ", " << std::endl;
}
// Find the backward element of the successor that moves to the state that we want to eliminate.
typename storm::storage::SparseMatrix<ValueType>::rows backwardRow = backwardTransitions.getRow(forwardElement->getColumn());
typename storm::storage::SparseMatrix<ValueType>::iterator backwardEntry = std::find_if(backwardRow.begin(), backwardRow.end(), [&](storm::storage::MatrixEntry<typename storm::storage::SparseMatrix<ValueType>::index_type, typename storm::storage::SparseMatrix<ValueType>::value_type> const& a) { return a.getColumn() == state; });
std::cout << "before bwd" << std::endl;
for (auto element : backwardTransitions.getRow(forwardElement->getColumn())) {
std::cout << element << ", " << std::endl;
}
// Modify the predecessor list of the successor and add the predecessor of the state we eliminate.
backwardEntry->setColumn(backwardElement->getColumn());
// If the backward entry is not at the right position, we need to move it there.
if (backwardEntry != backwardRow.begin() && backwardEntry->getColumn() < (backwardEntry - 1)->getColumn()) {
while (backwardEntry != backwardRow.begin() && backwardEntry->getColumn() < (backwardEntry - 1)->getColumn()) {
std::swap(*backwardEntry, *(backwardEntry - 1));
--backwardEntry;
}
} else if ((backwardEntry + 1) != backwardRow.end() && backwardEntry->getColumn() > (backwardEntry + 1)->getColumn()) {
while ((backwardEntry + 1) != backwardRow.end() && backwardEntry->getColumn() > (backwardEntry + 1)->getColumn()) {
std::swap(*backwardEntry, *(backwardEntry + 1));
++backwardEntry;
}
}
std::cout << "after bwd" << std::endl;
for (auto element : backwardTransitions.getRow(forwardElement->getColumn())) {
std::cout << element << ", " << std::endl;
}
return true;
}
template<typename ValueType>
FlexibleSparseMatrix<ValueType>::FlexibleSparseMatrix(index_type rows) : data(rows) {
// Intentionally left empty.
}
template<typename ValueType>
void FlexibleSparseMatrix<ValueType>::reserveInRow(index_type row, index_type numberOfElements) {
this->data[row].reserve(numberOfElements);
}
template<typename ValueType>
typename FlexibleSparseMatrix<ValueType>::row_type& FlexibleSparseMatrix<ValueType>::getRow(index_type index) {
return this->data[index];
}
template<typename ValueType>
FlexibleSparseMatrix<ValueType> SparseSccModelChecker<ValueType>::getFlexibleSparseMatrix(storm::storage::SparseMatrix<ValueType> const& matrix, bool setAllValuesToOne) {
FlexibleSparseMatrix<ValueType> flexibleMatrix(matrix.getRowCount());
for (typename FlexibleSparseMatrix<ValueType>::index_type rowIndex = 0; rowIndex < matrix.getRowCount(); ++rowIndex) {
typename storm::storage::SparseMatrix<ValueType>::const_rows row = matrix.getRow(rowIndex);
flexibleMatrix.reserveInRow(rowIndex, row.getNumberOfEntries());
for (auto const& element : row) {
if (setAllValuesToOne) {
flexibleMatrix.getRow(rowIndex).emplace_back(element.getColumn(), storm::utility::constantOne<ValueType>());
} else {
flexibleMatrix.getRow(rowIndex).emplace_back(element);
}
}
}
return flexibleMatrix;
}
template class FlexibleSparseMatrix<double>;
template class SparseSccModelChecker<double>;
} // namespace reachability
} // namespace modelchecker
} // namespace storm

43
src/modelchecker/reachability/SparseSccModelChecker.h

@ -1,43 +0,0 @@
#include "src/models/Dtmc.h"
namespace storm {
namespace modelchecker {
namespace reachability {
template<typename ValueType>
class FlexibleSparseMatrix {
public:
typedef uint_fast64_t index_type;
typedef ValueType value_type;
typedef std::vector<storm::storage::MatrixEntry<index_type, value_type>> row_type;
typedef typename row_type::iterator iterator;
typedef typename row_type::const_iterator const_iterator;
FlexibleSparseMatrix() = default;
FlexibleSparseMatrix(index_type rows);
void reserveInRow(index_type row, index_type numberOfElements);
row_type& getRow(index_type);
private:
std::vector<row_type> data;
};
template<typename ValueType>
class SparseSccModelChecker {
public:
static ValueType computeReachabilityProbability(storm::models::Dtmc<ValueType> const& dtmc, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates);
private:
static void treatScc(storm::models::Dtmc<ValueType> const& dtmc, FlexibleSparseMatrix<ValueType>& matrix, std::vector<ValueType>& oneStepProbabilities, storm::storage::BitVector const& entryStates, storm::storage::BitVector const& scc, storm::storage::SparseMatrix<ValueType> const& forwardTransitions, FlexibleSparseMatrix<ValueType>& backwardTransitions, bool eliminateEntryStates, uint_fast64_t level);
static FlexibleSparseMatrix<ValueType> getFlexibleSparseMatrix(storm::storage::SparseMatrix<ValueType> const& matrix, bool setAllValuesToOne = false);
static void eliminateState(FlexibleSparseMatrix<ValueType>& matrix, std::vector<ValueType>& oneStepProbabilities, uint_fast64_t state, FlexibleSparseMatrix<ValueType>& backwardTransitions);
static bool eliminateStateInPlace(storm::storage::SparseMatrix<ValueType>& matrix, std::vector<ValueType>& oneStepProbabilities, uint_fast64_t state, storm::storage::SparseMatrix<ValueType>& backwardTransitions);
static const uint_fast64_t maximalSccSize = 1000;
};
}
}
}

15
src/parser/FormulaParser.cpp

@ -77,12 +77,15 @@ namespace storm {
operatorInformation = (-optimalityOperator_[qi::_a = qi::_1] >> ((relationalOperator_[qi::_b = qi::_1] > qi::double_[qi::_c = qi::_1]) | (qi::lit("=") > qi::lit("?"))))[qi::_val = phoenix::construct<std::tuple<boost::optional<storm::logic::OptimalityType>, boost::optional<storm::logic::ComparisonType>, boost::optional<double>>>(qi::_a, qi::_b, qi::_c)];
operatorInformation.name("operator information");
steadyStateOperator = (qi::lit("S") > operatorInformation > qi::lit("[") > stateFormula > qi::lit("]"))[qi::_val = phoenix::bind(&FormulaParser::createSteadyStateOperatorFormula, phoenix::ref(*this), qi::_1, qi::_2)];
steadyStateOperator.name("steady state operator");
steadyStateOperator = (qi::lit("LRA") > operatorInformation > qi::lit("[") > stateFormula > qi::lit("]"))[qi::_val = phoenix::bind(&FormulaParser::createLongRunAverageOperatorFormula, phoenix::ref(*this), qi::_1, qi::_2)];
steadyStateOperator.name("long-run average operator");
rewardOperator = (qi::lit("R") > operatorInformation > qi::lit("[") > rewardPathFormula > qi::lit("]"))[qi::_val = phoenix::bind(&FormulaParser::createRewardOperatorFormula, phoenix::ref(*this), qi::_1, qi::_2)];
rewardOperator.name("reward operator");
expectedTimeOperator = (qi::lit("ET") > operatorInformation > qi::lit("[") > eventuallyFormula > qi::lit("]"))[qi::_val = phoenix::bind(&FormulaParser::createExpectedTimeOperatorFormula, phoenix::ref(*this), qi::_1, qi::_2)];
expectedTimeOperator.name("expected time operator");
probabilityOperator = (qi::lit("P") > operatorInformation > qi::lit("[") > pathFormula > qi::lit("]"))[qi::_val = phoenix::bind(&FormulaParser::createProbabilityOperatorFormula, phoenix::ref(*this), qi::_1, qi::_2)];
probabilityOperator.name("probability operator");
@ -224,14 +227,18 @@ namespace storm {
return std::shared_ptr<storm::logic::Formula>(new storm::logic::ConditionalPathFormula(leftSubformula, rightSubformula));
}
std::shared_ptr<storm::logic::Formula> FormulaParser::createSteadyStateOperatorFormula(std::tuple<boost::optional<storm::logic::OptimalityType>, boost::optional<storm::logic::ComparisonType>, boost::optional<double>> const& operatorInformation, std::shared_ptr<storm::logic::Formula> const& subformula) const {
return std::shared_ptr<storm::logic::Formula>(new storm::logic::SteadyStateOperatorFormula(std::get<0>(operatorInformation), std::get<1>(operatorInformation), std::get<2>(operatorInformation), subformula));
std::shared_ptr<storm::logic::Formula> FormulaParser::createLongRunAverageOperatorFormula(std::tuple<boost::optional<storm::logic::OptimalityType>, boost::optional<storm::logic::ComparisonType>, boost::optional<double>> const& operatorInformation, std::shared_ptr<storm::logic::Formula> const& subformula) const {
return std::shared_ptr<storm::logic::Formula>(new storm::logic::LongRunAverageOperatorFormula(std::get<0>(operatorInformation), std::get<1>(operatorInformation), std::get<2>(operatorInformation), subformula));
}
std::shared_ptr<storm::logic::Formula> FormulaParser::createRewardOperatorFormula(std::tuple<boost::optional<storm::logic::OptimalityType>, boost::optional<storm::logic::ComparisonType>, boost::optional<double>> const& operatorInformation, std::shared_ptr<storm::logic::Formula> const& subformula) const {
return std::shared_ptr<storm::logic::Formula>(new storm::logic::RewardOperatorFormula(std::get<0>(operatorInformation), std::get<1>(operatorInformation), std::get<2>(operatorInformation), subformula));
}
std::shared_ptr<storm::logic::Formula> FormulaParser::createExpectedTimeOperatorFormula(std::tuple<boost::optional<storm::logic::OptimalityType>, boost::optional<storm::logic::ComparisonType>, boost::optional<double>> const& operatorInformation, std::shared_ptr<storm::logic::Formula> const& subformula) const {
return std::shared_ptr<storm::logic::Formula>(new storm::logic::ExpectedTimeOperatorFormula(std::get<0>(operatorInformation), std::get<1>(operatorInformation), std::get<2>(operatorInformation), subformula));
}
std::shared_ptr<storm::logic::Formula> FormulaParser::createProbabilityOperatorFormula(std::tuple<boost::optional<storm::logic::OptimalityType>, boost::optional<storm::logic::ComparisonType>, boost::optional<double>> const& operatorInformation, std::shared_ptr<storm::logic::Formula> const& subformula) {
return std::shared_ptr<storm::logic::Formula>(new storm::logic::ProbabilityOperatorFormula(std::get<0>(operatorInformation), std::get<1>(operatorInformation), std::get<2>(operatorInformation), subformula));
}

4
src/parser/FormulaParser.h

@ -123,6 +123,7 @@ namespace storm {
qi::rule<Iterator, std::tuple<boost::optional<storm::logic::OptimalityType>, boost::optional<storm::logic::ComparisonType>, boost::optional<double>>(), qi::locals<boost::optional<storm::logic::OptimalityType>, boost::optional<storm::logic::ComparisonType>, boost::optional<double>>, Skipper> operatorInformation;
qi::rule<Iterator, std::shared_ptr<storm::logic::Formula>(), Skipper> probabilityOperator;
qi::rule<Iterator, std::shared_ptr<storm::logic::Formula>(), Skipper> rewardOperator;
qi::rule<Iterator, std::shared_ptr<storm::logic::Formula>(), Skipper> expectedTimeOperator;
qi::rule<Iterator, std::shared_ptr<storm::logic::Formula>(), Skipper> steadyStateOperator;
qi::rule<Iterator, std::shared_ptr<storm::logic::Formula>(), Skipper> formula;
@ -165,8 +166,9 @@ namespace storm {
std::shared_ptr<storm::logic::Formula> createUntilFormula(std::shared_ptr<storm::logic::Formula> const& leftSubformula, std::shared_ptr<storm::logic::Formula> const& rightSubformula);
std::shared_ptr<storm::logic::Formula> createBoundedUntilFormula(std::shared_ptr<storm::logic::Formula> const& leftSubformula, unsigned stepBound, std::shared_ptr<storm::logic::Formula> const& rightSubformula) const;
std::shared_ptr<storm::logic::Formula> createConditionalFormula(std::shared_ptr<storm::logic::Formula> const& leftSubformula, std::shared_ptr<storm::logic::Formula> const& rightSubformula) const;
std::shared_ptr<storm::logic::Formula> createSteadyStateOperatorFormula(std::tuple<boost::optional<storm::logic::OptimalityType>, boost::optional<storm::logic::ComparisonType>, boost::optional<double>> const& operatorInformation, std::shared_ptr<storm::logic::Formula> const& subformula) const;
std::shared_ptr<storm::logic::Formula> createLongRunAverageOperatorFormula(std::tuple<boost::optional<storm::logic::OptimalityType>, boost::optional<storm::logic::ComparisonType>, boost::optional<double>> const& operatorInformation, std::shared_ptr<storm::logic::Formula> const& subformula) const;
std::shared_ptr<storm::logic::Formula> createRewardOperatorFormula(std::tuple<boost::optional<storm::logic::OptimalityType>, boost::optional<storm::logic::ComparisonType>, boost::optional<double>> const& operatorInformation, std::shared_ptr<storm::logic::Formula> const& subformula) const;
std::shared_ptr<storm::logic::Formula> createExpectedTimeOperatorFormula(std::tuple<boost::optional<storm::logic::OptimalityType>, boost::optional<storm::logic::ComparisonType>, boost::optional<double>> const& operatorInformation, std::shared_ptr<storm::logic::Formula> const& subformula) const;
std::shared_ptr<storm::logic::Formula> createProbabilityOperatorFormula(std::tuple<boost::optional<storm::logic::OptimalityType>, boost::optional<storm::logic::ComparisonType>, boost::optional<double>> const& operatorInformation, std::shared_ptr<storm::logic::Formula> const& subformula);
std::shared_ptr<storm::logic::Formula> createBinaryBooleanStateFormula(std::shared_ptr<storm::logic::Formula> const& leftSubformula, std::shared_ptr<storm::logic::Formula> const& rightSubformula, storm::logic::BinaryBooleanStateFormula::OperatorType operatorType);
std::shared_ptr<storm::logic::Formula> createUnaryBooleanStateFormula(std::shared_ptr<storm::logic::Formula> const& subformula, boost::optional<storm::logic::UnaryBooleanStateFormula::OperatorType> const& operatorType);

5
src/settings/SettingsManager.cpp

@ -27,6 +27,7 @@ namespace storm {
this->addModule(std::unique_ptr<modules::ModuleSettings>(new modules::BisimulationSettings(*this)));
this->addModule(std::unique_ptr<modules::ModuleSettings>(new modules::GlpkSettings(*this)));
this->addModule(std::unique_ptr<modules::ModuleSettings>(new modules::GurobiSettings(*this)));
this->addModule(std::unique_ptr<modules::ModuleSettings>(new modules::SparseDtmcEliminationModelCheckerSettings(*this)));
}
SettingsManager::~SettingsManager() {
@ -507,5 +508,9 @@ namespace storm {
storm::settings::modules::GurobiSettings const& gurobiSettings() {
return dynamic_cast<storm::settings::modules::GurobiSettings const&>(manager().getModule(storm::settings::modules::GurobiSettings::moduleName));
}
storm::settings::modules::SparseDtmcEliminationModelCheckerSettings const& sparseDtmcEliminationModelCheckerSettings() {
return dynamic_cast<storm::settings::modules::SparseDtmcEliminationModelCheckerSettings const&>(manager().getModule(storm::settings::modules::SparseDtmcEliminationModelCheckerSettings::moduleName));
}
}
}

8
src/settings/SettingsManager.h

@ -28,6 +28,7 @@
#include "src/settings/modules/BisimulationSettings.h"
#include "src/settings/modules/GlpkSettings.h"
#include "src/settings/modules/GurobiSettings.h"
#include "src/settings/modules/SparseDtmcEliminationModelCheckerSettings.h"
#include "src/utility/macros.h"
#include "src/exceptions/OptionParserException.h"
@ -309,6 +310,13 @@ namespace storm {
*/
storm::settings::modules::GurobiSettings const& gurobiSettings();
/*!
* Retrieves the settings of the elimination-based DTMC model checker.
*
* @return An object that allows accessing the settings of the elimination-based DTMC model checker.
*/
storm::settings::modules::SparseDtmcEliminationModelCheckerSettings const& sparseDtmcEliminationModelCheckerSettings();
} // namespace settings
} // namespace storm

64
src/settings/modules/SparseDtmcEliminationModelCheckerSettings.cpp

@ -0,0 +1,64 @@
#include "src/settings/modules/SparseDtmcEliminationModelCheckerSettings.h"
#include "src/settings/SettingsManager.h"
namespace storm {
namespace settings {
namespace modules {
const std::string SparseDtmcEliminationModelCheckerSettings::moduleName = "sparseelim";
const std::string SparseDtmcEliminationModelCheckerSettings::eliminationMethodOptionName = "method";
const std::string SparseDtmcEliminationModelCheckerSettings::eliminationOrderOptionName = "order";
const std::string SparseDtmcEliminationModelCheckerSettings::entryStatesLastOptionName = "entrylast";
const std::string SparseDtmcEliminationModelCheckerSettings::maximalSccSizeOptionName = "sccsize";
SparseDtmcEliminationModelCheckerSettings::SparseDtmcEliminationModelCheckerSettings(storm::settings::SettingsManager& settingsManager) : ModuleSettings(settingsManager, moduleName) {
std::vector<std::string> orders = {"fw", "fwrev", "bw", "bwrev", "rand"};
this->addOption(storm::settings::OptionBuilder(moduleName, eliminationOrderOptionName, true, "The order that is to be used for the elimination techniques. Available are {fw, fwrev, bw, bwrev, rand}.").addArgument(storm::settings::ArgumentBuilder::createStringArgument("name", "The name of the order in which states are chosen for elimination.").addValidationFunctionString(storm::settings::ArgumentValidators::stringInListValidator(orders)).setDefaultValueString("bw").build()).build());
std::vector<std::string> methods = {"state", "hybrid"};
this->addOption(storm::settings::OptionBuilder(moduleName, eliminationMethodOptionName, true, "The elimination technique to use. Available are {state, hybrid}.").addArgument(storm::settings::ArgumentBuilder::createStringArgument("name", "The name of the elimination technique to use.").addValidationFunctionString(storm::settings::ArgumentValidators::stringInListValidator(methods)).setDefaultValueString("hybrid").build()).build());
this->addOption(storm::settings::OptionBuilder(moduleName, entryStatesLastOptionName, true, "Sets whether the entry states are eliminated last.").build());
this->addOption(storm::settings::OptionBuilder(moduleName, maximalSccSizeOptionName, true, "Sets the maximal size of the SCCs for which state elimination is applied.")
.addArgument(storm::settings::ArgumentBuilder::createUnsignedIntegerArgument("maxsize", "The maximal size of an SCC on which state elimination is applied.").setDefaultValueUnsignedInteger(20).setIsOptional(true).build()).build());
}
SparseDtmcEliminationModelCheckerSettings::EliminationMethod SparseDtmcEliminationModelCheckerSettings::getEliminationMethod() const {
std::string eliminationMethodAsString = this->getOption(eliminationMethodOptionName).getArgumentByName("name").getValueAsString();
if (eliminationMethodAsString == "state") {
return EliminationMethod::State;
} else if (eliminationMethodAsString == "hybrid") {
return EliminationMethod::Hybrid;
} else {
STORM_LOG_THROW(false, storm::exceptions::IllegalArgumentValueException, "Illegal elimination method selected.");
}
}
SparseDtmcEliminationModelCheckerSettings::EliminationOrder SparseDtmcEliminationModelCheckerSettings::getEliminationOrder() const {
std::string eliminationOrderAsString = this->getOption(eliminationOrderOptionName).getArgumentByName("name").getValueAsString();
if (eliminationOrderAsString == "fw") {
return EliminationOrder::Forward;
} else if (eliminationOrderAsString == "fwrev") {
return EliminationOrder::ForwardReversed;
} else if (eliminationOrderAsString == "bw") {
return EliminationOrder::Backward;
} else if (eliminationOrderAsString == "bwrev") {
return EliminationOrder::BackwardReversed;
} else if (eliminationOrderAsString == "rand") {
return EliminationOrder::Random;
} else {
STORM_LOG_THROW(false, storm::exceptions::IllegalArgumentValueException, "Illegal elimination order selected.");
}
}
bool SparseDtmcEliminationModelCheckerSettings::isEliminateEntryStatesLastSet() const {
return this->getOption(entryStatesLastOptionName).getHasOptionBeenSet();
}
uint_fast64_t SparseDtmcEliminationModelCheckerSettings::getMaximalSccSize() const {
return this->getOption(maximalSccSizeOptionName).getArgumentByName("maxsize").getValueAsUnsignedInteger();
}
} // namespace modules
} // namespace settings
} // namespace storm

73
src/settings/modules/SparseDtmcEliminationModelCheckerSettings.h

@ -0,0 +1,73 @@
#ifndef STORM_SETTINGS_MODULES_SPARSEDTMCELIMINATIONMODELCHECKERSETTINGS_H_
#define STORM_SETTINGS_MODULES_SPARSEDTMCELIMINATIONMODELCHECKERSETTINGS_H_
#include "src/settings/modules/ModuleSettings.h"
namespace storm {
namespace settings {
namespace modules {
/*!
* This class represents the settings for the elimination-based DTMC model checker.
*/
class SparseDtmcEliminationModelCheckerSettings : public ModuleSettings {
public:
/*!
* An enum that contains all available state elimination orders.
*/
enum class EliminationOrder { Forward, ForwardReversed, Backward, BackwardReversed, Random };
/*!
* An enum that contains all available techniques to solve parametric systems.
*/
enum class EliminationMethod { State, Scc, Hybrid};
/*!
* Creates a new set of parametric model checking settings that is managed by the given manager.
*
* @param settingsManager The responsible manager.
*/
SparseDtmcEliminationModelCheckerSettings(storm::settings::SettingsManager& settingsManager);
/*!
* Retrieves the selected elimination method.
*
* @return The selected elimination method.
*/
EliminationMethod getEliminationMethod() const;
/*!
* Retrieves the selected elimination order.
*
* @return The selected elimination order.
*/
EliminationOrder getEliminationOrder() const;
/*!
* Retrieves whether the option to eliminate entry states in the very end is set.
*
* @return True iff the option is set.
*/
bool isEliminateEntryStatesLastSet() const;
/*!
* Retrieves the maximal size of an SCC on which state elimination is to be directly applied.
*
* @return The maximal size of an SCC on which state elimination is to be directly applied.
*/
uint_fast64_t getMaximalSccSize() const;
const static std::string moduleName;
private:
const static std::string eliminationMethodOptionName;
const static std::string eliminationOrderOptionName;
const static std::string entryStatesLastOptionName;
const static std::string maximalSccSizeOptionName;
};
} // namespace modules
} // namespace settings
} // namespace storm
#endif /* STORM_SETTINGS_MODULES_SPARSEDTMCELIMINATIONMODELCHECKERSETTINGS_H_ */

123
src/utility/ConstantsComparator.cpp

@ -1,5 +1,6 @@
#include "src/utility/ConstantsComparator.h"
#include "src/storage/SparseMatrix.h"
#include "src/storage/sparse/StateType.h"
namespace storm {
@ -20,6 +21,11 @@ namespace storm {
return std::numeric_limits<ValueType>::infinity();
}
template<typename ValueType>
ValueType pow(ValueType const& value, uint_fast64_t exponent) {
return std::pow(value, exponent);
}
template<>
double simplify(double value) {
// In the general case, we don't to anything here, but merely return the value. If something else is
@ -27,6 +33,13 @@ namespace storm {
return value;
}
template<>
int simplify(int value) {
// In the general case, we don't to anything here, but merely return the value. If something else is
// supposed to happen here, the templated function can be specialized for this particular type.
return value;
}
template<typename ValueType>
bool ConstantsComparator<ValueType>::isOne(ValueType const& value) const {
return value == one<ValueType>();
@ -66,20 +79,128 @@ namespace storm {
return true;
}
#ifdef PARAMETRIC_SYSTEMS
template<>
RationalFunction pow(RationalFunction const& value, uint_fast64_t exponent) {
return carl::pow(value, exponent);
}
template<>
RationalFunction simplify(RationalFunction value) {
value.simplify();
return value;
}
template<>
RationalFunction& simplify(RationalFunction& value) {
value.simplify();
return value;
}
template<>
RationalFunction&& simplify(RationalFunction&& value) {
value.simplify();
return std::move(value);
}
bool ConstantsComparator<storm::RationalFunction>::isOne(storm::RationalFunction const& value) const {
return value.isOne();
}
bool ConstantsComparator<storm::RationalFunction>::isZero(storm::RationalFunction const& value) const {
return value.isZero();
}
bool ConstantsComparator<storm::RationalFunction>::isEqual(storm::RationalFunction const& value1, storm::RationalFunction const& value2) const {
return value1 == value2;
}
bool ConstantsComparator<storm::RationalFunction>::isConstant(storm::RationalFunction const& value) const {
return value.isConstant();
}
bool ConstantsComparator<storm::Polynomial>::isOne(storm::Polynomial const& value) const {
return value.isOne();
}
bool ConstantsComparator<storm::Polynomial>::isZero(storm::Polynomial const& value) const {
return value.isZero();
}
bool ConstantsComparator<storm::Polynomial>::isEqual(storm::Polynomial const& value1, storm::Polynomial const& value2) const {
return value1 == value2;
}
bool ConstantsComparator<storm::Polynomial>::isConstant(storm::Polynomial const& value) const {
return value.isConstant();
}
#endif
template<typename IndexType, typename ValueType>
storm::storage::MatrixEntry<IndexType, ValueType> simplify(storm::storage::MatrixEntry<IndexType, ValueType> matrixEntry) {
simplify(matrixEntry.getValue());
return matrixEntry;
}
template<typename IndexType, typename ValueType>
storm::storage::MatrixEntry<IndexType, ValueType>& simplify(storm::storage::MatrixEntry<IndexType, ValueType>& matrixEntry) {
simplify(matrixEntry.getValue());
return matrixEntry;
}
template<typename IndexType, typename ValueType>
storm::storage::MatrixEntry<IndexType, ValueType>&& simplify(storm::storage::MatrixEntry<IndexType, ValueType>&& matrixEntry) {
simplify(matrixEntry.getValue());
return std::move(matrixEntry);
}
template class ConstantsComparator<double>;
template double one();
template double zero();
template double infinity();
template double pow(double const& value, uint_fast64_t exponent);
template double simplify(double value);
template storm::storage::MatrixEntry<storm::storage::sparse::state_type, double> simplify(storm::storage::MatrixEntry<storm::storage::sparse::state_type, double> matrixEntry);
template storm::storage::MatrixEntry<storm::storage::sparse::state_type, double>& simplify(storm::storage::MatrixEntry<storm::storage::sparse::state_type, double>& matrixEntry);
template storm::storage::MatrixEntry<storm::storage::sparse::state_type, double>&& simplify(storm::storage::MatrixEntry<storm::storage::sparse::state_type, double>&& matrixEntry);
template class ConstantsComparator<int>;
template int one();
template int zero();
template int infinity();
template int pow(int const& value, uint_fast64_t exponent);
template int simplify(int value);
template storm::storage::MatrixEntry<storm::storage::sparse::state_type, int> simplify(storm::storage::MatrixEntry<storm::storage::sparse::state_type, int> matrixEntry);
template storm::storage::MatrixEntry<storm::storage::sparse::state_type, int>& simplify(storm::storage::MatrixEntry<storm::storage::sparse::state_type, int>& matrixEntry);
template storm::storage::MatrixEntry<storm::storage::sparse::state_type, int>&& simplify(storm::storage::MatrixEntry<storm::storage::sparse::state_type, int>&& matrixEntry);
#ifdef PARAMETRIC_SYSTEMS
template class ConstantsComparator<RationalFunction>;
template class ConstantsComparator<Polynomial>;
template RationalFunction one();
template RationalFunction zero();
template RationalFunction pow(RationalFunction const& value, uint_fast64_t exponent);
template Polynomial one();
template Polynomial zero();
template RationalFunction simplify(RationalFunction value);
template RationalFunction& simplify(RationalFunction& value);
template RationalFunction&& simplify(RationalFunction&& value);
template storm::storage::MatrixEntry<storm::storage::sparse::state_type, RationalFunction> simplify(storm::storage::MatrixEntry<storm::storage::sparse::state_type, RationalFunction> matrixEntry);
template storm::storage::MatrixEntry<storm::storage::sparse::state_type, RationalFunction>& simplify(storm::storage::MatrixEntry<storm::storage::sparse::state_type, RationalFunction>& matrixEntry);
template storm::storage::MatrixEntry<storm::storage::sparse::state_type, RationalFunction>&& simplify(storm::storage::MatrixEntry<storm::storage::sparse::state_type, RationalFunction>&& matrixEntry);
#endif
}
}

58
src/utility/ConstantsComparator.h

@ -13,9 +13,18 @@
#include <cstdint>
#include "src/settings/SettingsManager.h"
#include "src/storage/SparseMatrix.h"
#ifdef PARAMETRIC_SYSTEMS
#include "src/storage/parameters.h"
#endif
namespace storm {
// Forward-declare MatrixEntry class.
namespace storage {
template<typename IndexType, typename ValueType> class MatrixEntry;
}
namespace utility {
template<typename ValueType>
@ -27,9 +36,17 @@ namespace storm {
template<typename ValueType>
ValueType infinity();
template<typename ValueType>
ValueType pow(ValueType const& value, uint_fast64_t exponent);
#ifdef PARAMETRIC_SYSTEMS
template<>
RationalFunction pow(RationalFunction const& value, uint_fast64_t exponent);
#endif
template<typename ValueType>
ValueType simplify(ValueType value);
// A class that can be used for comparing constants.
template<typename ValueType>
class ConstantsComparator {
@ -46,7 +63,7 @@ namespace storm {
class ConstantsComparator<double> {
public:
ConstantsComparator();
ConstantsComparator(double precision);
bool isOne(double const& value) const;
@ -62,8 +79,43 @@ namespace storm {
double precision;
};
#ifdef PARAMETRIC_SYSTEMS
template<>
RationalFunction& simplify(RationalFunction& value);
template<>
RationalFunction&& simplify(RationalFunction&& value);
template<>
class ConstantsComparator<storm::RationalFunction> {
public:
bool isOne(storm::RationalFunction const& value) const;
bool isZero(storm::RationalFunction const& value) const;
bool isEqual(storm::RationalFunction const& value1, storm::RationalFunction const& value2) const;
bool isConstant(storm::RationalFunction const& value) const;
};
template<>
class ConstantsComparator<storm::Polynomial> {
public:
bool isOne(storm::Polynomial const& value) const;
bool isZero(storm::Polynomial const& value) const;
bool isEqual(storm::Polynomial const& value1, storm::Polynomial const& value2) const;
bool isConstant(storm::Polynomial const& value) const;
};
#endif
template<typename IndexType, typename ValueType>
storm::storage::MatrixEntry<IndexType, ValueType>& simplify(storm::storage::MatrixEntry<IndexType, ValueType>& matrixEntry);
template<typename IndexType, typename ValueType>
storm::storage::MatrixEntry<IndexType, ValueType>&& simplify(storm::storage::MatrixEntry<IndexType, ValueType>&& matrixEntry);
}
}
|||||||
100:0
Loading…
Cancel
Save