#include "test/storm_gtest.h" #include "storm-config.h" #include "storm/api/builder.h" #include "storm-conv/api/storm-conv.h" #include "storm-parsers/api/model_descriptions.h" #include "storm/api/properties.h" #include "storm-parsers/api/properties.h" #include "storm/models/sparse/Mdp.h" #include "storm/models/symbolic/Mdp.h" #include "storm/models/sparse/StandardRewardModel.h" #include "storm/models/symbolic/StandardRewardModel.h" #include "storm/modelchecker/prctl/SparseMdpPrctlModelChecker.h" #include "storm/modelchecker/prctl/HybridMdpPrctlModelChecker.h" #include "storm/modelchecker/prctl/SymbolicMdpPrctlModelChecker.h" #include "storm/modelchecker/results/QuantitativeCheckResult.h" #include "storm/modelchecker/results/ExplicitQualitativeCheckResult.h" #include "storm/modelchecker/results/SymbolicQualitativeCheckResult.h" #include "storm/modelchecker/results/QualitativeCheckResult.h" #include "storm/environment/solver/MinMaxSolverEnvironment.h" #include "storm/environment/solver/TopologicalSolverEnvironment.h" #include "storm/environment/solver/MultiplierEnvironment.h" #include "storm/settings/modules/CoreSettings.h" #include "storm/logic/Formulas.h" #include "storm/storage/jani/Property.h" #include "storm/exceptions/UncheckedRequirementException.h" namespace { enum class MdpEngine {PrismSparse, JaniSparse, JitSparse, Hybrid, PrismDd, JaniDd}; class SparseDoubleValueIterationGmmxxGaussSeidelMultEnvironment { public: static const storm::dd::DdType ddType = storm::dd::DdType::Sylvan; // Unused for sparse models static const MdpEngine engine = MdpEngine::PrismSparse; static const bool isExact = false; typedef double ValueType; typedef storm::models::sparse::Mdp ModelType; static storm::Environment createEnvironment() { storm::Environment env; env.solver().minMax().setMethod(storm::solver::MinMaxMethod::ValueIteration); env.solver().minMax().setPrecision(storm::utility::convertNumber(1e-10)); env.solver().minMax().setMultiplicationStyle(storm::solver::MultiplicationStyle::GaussSeidel); env.solver().multiplier().setType(storm::solver::MultiplierType::Gmmxx); return env; } }; class SparseDoubleValueIterationGmmxxRegularMultEnvironment { public: static const storm::dd::DdType ddType = storm::dd::DdType::Sylvan; // Unused for sparse models static const MdpEngine engine = MdpEngine::PrismSparse; static const bool isExact = false; typedef double ValueType; typedef storm::models::sparse::Mdp ModelType; static storm::Environment createEnvironment() { storm::Environment env; env.solver().minMax().setMethod(storm::solver::MinMaxMethod::ValueIteration); env.solver().minMax().setPrecision(storm::utility::convertNumber(1e-10)); env.solver().minMax().setMultiplicationStyle(storm::solver::MultiplicationStyle::Regular); env.solver().multiplier().setType(storm::solver::MultiplierType::Gmmxx); return env; } }; class SparseDoubleValueIterationNativeGaussSeidelMultEnvironment { public: static const storm::dd::DdType ddType = storm::dd::DdType::Sylvan; // Unused for sparse models static const MdpEngine engine = MdpEngine::PrismSparse; static const bool isExact = false; typedef double ValueType; typedef storm::models::sparse::Mdp ModelType; static storm::Environment createEnvironment() { storm::Environment env; env.solver().minMax().setMethod(storm::solver::MinMaxMethod::ValueIteration); env.solver().minMax().setPrecision(storm::utility::convertNumber(1e-10)); env.solver().minMax().setMultiplicationStyle(storm::solver::MultiplicationStyle::GaussSeidel); env.solver().multiplier().setType(storm::solver::MultiplierType::Native); return env; } }; class SparseDoubleValueIterationNativeRegularMultEnvironment { public: static const storm::dd::DdType ddType = storm::dd::DdType::Sylvan; // Unused for sparse models static const MdpEngine engine = MdpEngine::PrismSparse; static const bool isExact = false; typedef double ValueType; typedef storm::models::sparse::Mdp ModelType; static storm::Environment createEnvironment() { storm::Environment env; env.solver().minMax().setMethod(storm::solver::MinMaxMethod::ValueIteration); env.solver().minMax().setPrecision(storm::utility::convertNumber(1e-10)); env.solver().minMax().setMultiplicationStyle(storm::solver::MultiplicationStyle::Regular); env.solver().multiplier().setType(storm::solver::MultiplierType::Native); return env; } }; class JaniSparseDoubleValueIterationEnvironment { public: static const storm::dd::DdType ddType = storm::dd::DdType::Sylvan; // Unused for sparse models static const MdpEngine engine = MdpEngine::JaniSparse; static const bool isExact = false; typedef double ValueType; typedef storm::models::sparse::Mdp ModelType; static storm::Environment createEnvironment() { storm::Environment env; env.solver().minMax().setMethod(storm::solver::MinMaxMethod::ValueIteration); env.solver().minMax().setPrecision(storm::utility::convertNumber(1e-10)); return env; } }; class JitSparseDoubleValueIterationEnvironment { public: static const storm::dd::DdType ddType = storm::dd::DdType::Sylvan; // Unused for sparse models static const MdpEngine engine = MdpEngine::JitSparse; static const bool isExact = false; typedef double ValueType; typedef storm::models::sparse::Mdp ModelType; static storm::Environment createEnvironment() { storm::Environment env; env.solver().minMax().setMethod(storm::solver::MinMaxMethod::ValueIteration); env.solver().minMax().setPrecision(storm::utility::convertNumber(1e-10)); return env; } }; class SparseDoubleIntervalIterationEnvironment { public: static const storm::dd::DdType ddType = storm::dd::DdType::Sylvan; // Unused for sparse models static const MdpEngine engine = MdpEngine::PrismSparse; static const bool isExact = false; typedef double ValueType; typedef storm::models::sparse::Mdp ModelType; static storm::Environment createEnvironment() { storm::Environment env; env.solver().setForceSoundness(true); env.solver().minMax().setMethod(storm::solver::MinMaxMethod::IntervalIteration); env.solver().minMax().setPrecision(storm::utility::convertNumber(1e-6)); env.solver().minMax().setRelativeTerminationCriterion(false); return env; } }; class SparseDoubleSoundValueIterationEnvironment { public: static const storm::dd::DdType ddType = storm::dd::DdType::Sylvan; // Unused for sparse models static const MdpEngine engine = MdpEngine::PrismSparse; static const bool isExact = false; typedef double ValueType; typedef storm::models::sparse::Mdp ModelType; static storm::Environment createEnvironment() { storm::Environment env; env.solver().setForceSoundness(true); env.solver().minMax().setMethod(storm::solver::MinMaxMethod::SoundValueIteration); env.solver().minMax().setPrecision(storm::utility::convertNumber(1e-6)); env.solver().minMax().setRelativeTerminationCriterion(false); return env; } }; class SparseDoubleOptimisticValueIterationEnvironment { public: static const storm::dd::DdType ddType = storm::dd::DdType::Sylvan; // Unused for sparse models static const MdpEngine engine = MdpEngine::PrismSparse; static const bool isExact = false; typedef double ValueType; typedef storm::models::sparse::Mdp ModelType; static storm::Environment createEnvironment() { storm::Environment env; env.solver().setForceSoundness(true); env.solver().minMax().setMethod(storm::solver::MinMaxMethod::OptimisticValueIteration); env.solver().minMax().setPrecision(storm::utility::convertNumber(1e-6)); env.solver().minMax().setRelativeTerminationCriterion(false); return env; } }; class SparseDoubleTopologicalValueIterationEnvironment { public: static const storm::dd::DdType ddType = storm::dd::DdType::Sylvan; // Unused for sparse models static const MdpEngine engine = MdpEngine::PrismSparse; static const bool isExact = false; typedef double ValueType; typedef storm::models::sparse::Mdp ModelType; static storm::Environment createEnvironment() { storm::Environment env; env.solver().minMax().setMethod(storm::solver::MinMaxMethod::Topological); env.solver().topological().setUnderlyingMinMaxMethod(storm::solver::MinMaxMethod::ValueIteration); env.solver().minMax().setPrecision(storm::utility::convertNumber(1e-8)); env.solver().minMax().setRelativeTerminationCriterion(false); return env; } }; class SparseDoubleTopologicalSoundValueIterationEnvironment { public: static const storm::dd::DdType ddType = storm::dd::DdType::Sylvan; // Unused for sparse models static const MdpEngine engine = MdpEngine::PrismSparse; static const bool isExact = false; typedef double ValueType; typedef storm::models::sparse::Mdp ModelType; static storm::Environment createEnvironment() { storm::Environment env; env.solver().setForceSoundness(true); env.solver().minMax().setMethod(storm::solver::MinMaxMethod::Topological); env.solver().topological().setUnderlyingMinMaxMethod(storm::solver::MinMaxMethod::SoundValueIteration); env.solver().minMax().setPrecision(storm::utility::convertNumber(1e-6)); env.solver().minMax().setRelativeTerminationCriterion(false); return env; } }; class SparseRationalPolicyIterationEnvironment { public: static const storm::dd::DdType ddType = storm::dd::DdType::Sylvan; // Unused for sparse models static const MdpEngine engine = MdpEngine::PrismSparse; static const bool isExact = true; typedef storm::RationalNumber ValueType; typedef storm::models::sparse::Mdp ModelType; static storm::Environment createEnvironment() { storm::Environment env; env.solver().minMax().setMethod(storm::solver::MinMaxMethod::PolicyIteration); return env; } }; class SparseRationalViToPiEnvironment { public: static const storm::dd::DdType ddType = storm::dd::DdType::Sylvan; // Unused for sparse models static const MdpEngine engine = MdpEngine::PrismSparse; static const bool isExact = true; typedef storm::RationalNumber ValueType; typedef storm::models::sparse::Mdp ModelType; static storm::Environment createEnvironment() { storm::Environment env; env.solver().minMax().setMethod(storm::solver::MinMaxMethod::ViToPi); return env; } }; class SparseRationalRationalSearchEnvironment { public: static const storm::dd::DdType ddType = storm::dd::DdType::Sylvan; // Unused for sparse models static const MdpEngine engine = MdpEngine::PrismSparse; static const bool isExact = true; typedef storm::RationalNumber ValueType; typedef storm::models::sparse::Mdp ModelType; static storm::Environment createEnvironment() { storm::Environment env; env.solver().minMax().setMethod(storm::solver::MinMaxMethod::RationalSearch); return env; } }; class HybridCuddDoubleValueIterationEnvironment { public: static const storm::dd::DdType ddType = storm::dd::DdType::CUDD; static const MdpEngine engine = MdpEngine::Hybrid; static const bool isExact = false; typedef double ValueType; typedef storm::models::symbolic::Mdp ModelType; static storm::Environment createEnvironment() { storm::Environment env; env.solver().minMax().setMethod(storm::solver::MinMaxMethod::ValueIteration); env.solver().minMax().setPrecision(storm::utility::convertNumber(1e-10)); return env; } }; class HybridSylvanDoubleValueIterationEnvironment { public: static const storm::dd::DdType ddType = storm::dd::DdType::Sylvan; static const MdpEngine engine = MdpEngine::Hybrid; static const bool isExact = false; typedef double ValueType; typedef storm::models::symbolic::Mdp ModelType; static storm::Environment createEnvironment() { storm::Environment env; env.solver().minMax().setMethod(storm::solver::MinMaxMethod::ValueIteration); env.solver().minMax().setPrecision(storm::utility::convertNumber(1e-10)); return env; } }; class HybridCuddDoubleSoundValueIterationEnvironment { public: static const storm::dd::DdType ddType = storm::dd::DdType::CUDD; static const MdpEngine engine = MdpEngine::Hybrid; static const bool isExact = false; typedef double ValueType; typedef storm::models::symbolic::Mdp ModelType; static storm::Environment createEnvironment() { storm::Environment env; env.solver().setForceSoundness(true); env.solver().minMax().setMethod(storm::solver::MinMaxMethod::SoundValueIteration); env.solver().minMax().setPrecision(storm::utility::convertNumber(1e-6)); env.solver().minMax().setRelativeTerminationCriterion(false); return env; } }; class HybridCuddDoubleOptimisticValueIterationEnvironment { public: static const storm::dd::DdType ddType = storm::dd::DdType::CUDD; static const MdpEngine engine = MdpEngine::Hybrid; static const bool isExact = false; typedef double ValueType; typedef storm::models::symbolic::Mdp ModelType; static storm::Environment createEnvironment() { storm::Environment env; env.solver().setForceSoundness(true); env.solver().minMax().setMethod(storm::solver::MinMaxMethod::OptimisticValueIteration); env.solver().minMax().setPrecision(storm::utility::convertNumber(1e-6)); env.solver().minMax().setRelativeTerminationCriterion(false); return env; } }; class HybridSylvanRationalPolicyIterationEnvironment { public: static const storm::dd::DdType ddType = storm::dd::DdType::Sylvan; static const MdpEngine engine = MdpEngine::Hybrid; static const bool isExact = true; typedef storm::RationalNumber ValueType; typedef storm::models::symbolic::Mdp ModelType; static storm::Environment createEnvironment() { storm::Environment env; env.solver().minMax().setMethod(storm::solver::MinMaxMethod::PolicyIteration); return env; } }; class DdCuddDoubleValueIterationEnvironment { public: static const storm::dd::DdType ddType = storm::dd::DdType::CUDD; static const MdpEngine engine = MdpEngine::PrismDd; static const bool isExact = false; typedef double ValueType; typedef storm::models::symbolic::Mdp ModelType; static storm::Environment createEnvironment() { storm::Environment env; env.solver().minMax().setMethod(storm::solver::MinMaxMethod::ValueIteration); env.solver().minMax().setPrecision(storm::utility::convertNumber(1e-10)); return env; } }; class JaniDdCuddDoubleValueIterationEnvironment { public: static const storm::dd::DdType ddType = storm::dd::DdType::CUDD; static const MdpEngine engine = MdpEngine::JaniDd; static const bool isExact = false; typedef double ValueType; typedef storm::models::symbolic::Mdp ModelType; static storm::Environment createEnvironment() { storm::Environment env; env.solver().minMax().setMethod(storm::solver::MinMaxMethod::ValueIteration); env.solver().minMax().setPrecision(storm::utility::convertNumber(1e-10)); return env; } }; class DdSylvanDoubleValueIterationEnvironment { public: static const storm::dd::DdType ddType = storm::dd::DdType::Sylvan; static const MdpEngine engine = MdpEngine::PrismDd; static const bool isExact = false; typedef double ValueType; typedef storm::models::symbolic::Mdp ModelType; static storm::Environment createEnvironment() { storm::Environment env; env.solver().minMax().setMethod(storm::solver::MinMaxMethod::ValueIteration); env.solver().minMax().setPrecision(storm::utility::convertNumber(1e-10)); return env; } }; class DdCuddDoublePolicyIterationEnvironment { public: static const storm::dd::DdType ddType = storm::dd::DdType::CUDD; static const MdpEngine engine = MdpEngine::PrismDd; static const bool isExact = false; typedef double ValueType; typedef storm::models::symbolic::Mdp ModelType; static storm::Environment createEnvironment() { storm::Environment env; env.solver().minMax().setMethod(storm::solver::MinMaxMethod::PolicyIteration); env.solver().minMax().setPrecision(storm::utility::convertNumber(1e-10)); return env; } }; class DdSylvanRationalRationalSearchEnvironment { public: static const storm::dd::DdType ddType = storm::dd::DdType::Sylvan; static const MdpEngine engine = MdpEngine::PrismDd; static const bool isExact = true; typedef storm::RationalNumber ValueType; typedef storm::models::symbolic::Mdp ModelType; static storm::Environment createEnvironment() { storm::Environment env; env.solver().minMax().setMethod(storm::solver::MinMaxMethod::RationalSearch); return env; } }; template class MdpPrctlModelCheckerTest : public ::testing::Test { public: typedef typename TestType::ValueType ValueType; typedef typename storm::models::sparse::Mdp SparseModelType; typedef typename storm::models::symbolic::Mdp SymbolicModelType; MdpPrctlModelCheckerTest() : _environment(TestType::createEnvironment()) {} storm::Environment const& env() const { return _environment; } ValueType parseNumber(std::string const& input) const { return storm::utility::convertNumber(input);} ValueType precision() const { return TestType::isExact ? parseNumber("0") : parseNumber("1e-6");} bool isSparseModel() const { return std::is_same::value; } bool isSymbolicModel() const { return std::is_same::value; } template typename std::enable_if::value, std::pair, std::vector>>>::type buildModelFormulas(std::string const& pathToPrismFile, std::string const& formulasAsString, std::string const& constantDefinitionString = "") const { std::pair, std::vector>> result; storm::prism::Program program = storm::api::parseProgram(pathToPrismFile); program = storm::utility::prism::preprocess(program, constantDefinitionString); if (TestType::engine == MdpEngine::PrismSparse) { result.second = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulasAsString, program)); result.first = storm::api::buildSparseModel(program, result.second)->template as(); } else if (TestType::engine == MdpEngine::JaniSparse || TestType::engine == MdpEngine::JitSparse) { auto janiData = storm::api::convertPrismToJani(program, storm::api::parsePropertiesForPrismProgram(formulasAsString, program)); janiData.first.substituteFunctions(); result.second = storm::api::extractFormulasFromProperties(janiData.second); result.first = storm::api::buildSparseModel(janiData.first, result.second, TestType::engine == MdpEngine::JitSparse)->template as(); } return result; } template typename std::enable_if::value, std::pair, std::vector>>>::type buildModelFormulas(std::string const& pathToPrismFile, std::string const& formulasAsString, std::string const& constantDefinitionString = "") const { std::pair, std::vector>> result; storm::prism::Program program = storm::api::parseProgram(pathToPrismFile); program = storm::utility::prism::preprocess(program, constantDefinitionString); if (TestType::engine == MdpEngine::Hybrid || TestType::engine == MdpEngine::PrismDd) { result.second = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulasAsString, program)); result.first = storm::api::buildSymbolicModel(program, result.second)->template as(); } else if (TestType::engine == MdpEngine::JaniDd) { auto janiData = storm::api::convertPrismToJani(program, storm::api::parsePropertiesForPrismProgram(formulasAsString, program)); janiData.first.substituteFunctions(); result.second = storm::api::extractFormulasFromProperties(janiData.second); result.first = storm::api::buildSymbolicModel(janiData.first, result.second)->template as(); } return result; } std::vector> getTasks(std::vector> const& formulas) const { std::vector> result; for (auto const& f : formulas) { result.emplace_back(*f); } return result; } template typename std::enable_if::value, std::shared_ptr>>::type createModelChecker(std::shared_ptr const& model) const { if (TestType::engine == MdpEngine::PrismSparse || TestType::engine == MdpEngine::JaniSparse || TestType::engine == MdpEngine::JitSparse) { return std::make_shared>(*model); } } template typename std::enable_if::value, std::shared_ptr>>::type createModelChecker(std::shared_ptr const& model) const { if (TestType::engine == MdpEngine::Hybrid) { return std::make_shared>(*model); } else if (TestType::engine == MdpEngine::PrismDd || TestType::engine == MdpEngine::JaniDd) { return std::make_shared>(*model); } } bool getQualitativeResultAtInitialState(std::shared_ptr> const& model, std::unique_ptr& result) { auto filter = getInitialStateFilter(model); result->filter(*filter); return result->asQualitativeCheckResult().forallTrue(); } ValueType getQuantitativeResultAtInitialState(std::shared_ptr> const& model, std::unique_ptr& result) { auto filter = getInitialStateFilter(model); result->filter(*filter); return result->asQuantitativeCheckResult().getMin(); } private: storm::Environment _environment; std::unique_ptr getInitialStateFilter(std::shared_ptr> const& model) const { if (isSparseModel()) { return std::make_unique(model->template as()->getInitialStates()); } else { return std::make_unique>(model->template as()->getReachableStates(), model->template as()->getInitialStates()); } } }; typedef ::testing::Types< SparseDoubleValueIterationGmmxxGaussSeidelMultEnvironment, SparseDoubleValueIterationGmmxxRegularMultEnvironment, SparseDoubleValueIterationNativeGaussSeidelMultEnvironment, SparseDoubleValueIterationNativeRegularMultEnvironment, JaniSparseDoubleValueIterationEnvironment, JitSparseDoubleValueIterationEnvironment, SparseDoubleIntervalIterationEnvironment, SparseDoubleSoundValueIterationEnvironment, SparseDoubleOptimisticValueIterationEnvironment, SparseDoubleTopologicalValueIterationEnvironment, SparseDoubleTopologicalSoundValueIterationEnvironment, SparseRationalPolicyIterationEnvironment, SparseRationalViToPiEnvironment, SparseRationalRationalSearchEnvironment, HybridCuddDoubleValueIterationEnvironment, HybridSylvanDoubleValueIterationEnvironment, HybridCuddDoubleSoundValueIterationEnvironment, HybridCuddDoubleOptimisticValueIterationEnvironment, HybridSylvanRationalPolicyIterationEnvironment, DdCuddDoubleValueIterationEnvironment, JaniDdCuddDoubleValueIterationEnvironment, DdSylvanDoubleValueIterationEnvironment, DdCuddDoublePolicyIterationEnvironment, DdSylvanRationalRationalSearchEnvironment > TestingTypes; TYPED_TEST_SUITE(MdpPrctlModelCheckerTest, TestingTypes,); TYPED_TEST(MdpPrctlModelCheckerTest, Dice) { std::string formulasString = "Pmin=? [F \"two\"]"; formulasString += "; Pmax=? [F \"two\"]"; formulasString += "; Pmin=? [F \"three\"]"; formulasString += "; Pmax=? [F \"three\"]"; formulasString += "; Pmin=? [F \"four\"]"; formulasString += "; Pmax=? [F \"four\"]"; formulasString += "; Rmin=? [F \"done\"]"; formulasString += "; Rmax=? [F \"done\"]"; auto modelFormulas = this->buildModelFormulas(STORM_TEST_RESOURCES_DIR "/mdp/two_dice.nm", formulasString); auto model = std::move(modelFormulas.first); auto tasks = this->getTasks(modelFormulas.second); EXPECT_EQ(169ul, model->getNumberOfStates()); EXPECT_EQ(436ul, model->getNumberOfTransitions()); ASSERT_EQ(model->getType(), storm::models::ModelType::Mdp); auto checker = this->createModelChecker(model); std::unique_ptr result; result = checker->check(this->env(), tasks[0]); EXPECT_NEAR(this->parseNumber("1/36"), this->getQuantitativeResultAtInitialState(model, result), this->precision()); result = checker->check(this->env(), tasks[1]); EXPECT_NEAR(this->parseNumber("1/36"), this->getQuantitativeResultAtInitialState(model, result), this->precision()); result = checker->check(this->env(), tasks[2]); EXPECT_NEAR(this->parseNumber("2/36"), this->getQuantitativeResultAtInitialState(model, result), this->precision()); result = checker->check(this->env(), tasks[3]); EXPECT_NEAR(this->parseNumber("2/36"), this->getQuantitativeResultAtInitialState(model, result), this->precision()); result = checker->check(this->env(), tasks[4]); EXPECT_NEAR(this->parseNumber("3/36"), this->getQuantitativeResultAtInitialState(model, result), this->precision()); result = checker->check(this->env(), tasks[5]); EXPECT_NEAR(this->parseNumber("3/36"), this->getQuantitativeResultAtInitialState(model, result), this->precision()); result = checker->check(this->env(), tasks[6]); EXPECT_NEAR(this->parseNumber("22/3"), this->getQuantitativeResultAtInitialState(model, result), this->precision()); result = checker->check(this->env(), tasks[7]); EXPECT_NEAR(this->parseNumber("22/3"), this->getQuantitativeResultAtInitialState(model, result), this->precision()); } TYPED_TEST(MdpPrctlModelCheckerTest, AsynchronousLeader) { std::string formulasString = "Pmin=? [F \"elected\"]"; formulasString += "; Pmax=? [F \"elected\"]"; formulasString += "; Pmin=? [F<=25 \"elected\"]"; formulasString += "; Pmax=? [F<=25 \"elected\"]"; formulasString += "; Rmin=? [F \"elected\"]"; formulasString += "; Rmax=? [F \"elected\"]"; auto modelFormulas = this->buildModelFormulas(STORM_TEST_RESOURCES_DIR "/mdp/leader4.nm", formulasString); auto model = std::move(modelFormulas.first); auto tasks = this->getTasks(modelFormulas.second); EXPECT_EQ(3172ul, model->getNumberOfStates()); EXPECT_EQ(7144ul, model->getNumberOfTransitions()); ASSERT_EQ(model->getType(), storm::models::ModelType::Mdp); auto checker = this->createModelChecker(model); std::unique_ptr result; result = checker->check(this->env(), tasks[0]); EXPECT_NEAR(this->parseNumber("1"), this->getQuantitativeResultAtInitialState(model, result), this->precision()); result = checker->check(this->env(), tasks[1]); EXPECT_NEAR(this->parseNumber("1"), this->getQuantitativeResultAtInitialState(model, result), this->precision()); result = checker->check(this->env(), tasks[2]); EXPECT_NEAR(this->parseNumber("1/16"), this->getQuantitativeResultAtInitialState(model, result), this->precision()); result = checker->check(this->env(), tasks[3]); EXPECT_NEAR(this->parseNumber("1/16"), this->getQuantitativeResultAtInitialState(model, result), this->precision()); result = checker->check(this->env(), tasks[4]); EXPECT_NEAR(this->parseNumber("30/7"), this->getQuantitativeResultAtInitialState(model, result), this->precision()); result = checker->check(this->env(), tasks[5]); EXPECT_NEAR(this->parseNumber("30/7"), this->getQuantitativeResultAtInitialState(model, result), this->precision()); } TYPED_TEST(MdpPrctlModelCheckerTest, consensus) { std::string formulasString = "Pmax=? [F \"finished\"]"; formulasString += "; Pmax=? [F \"all_coins_equal_1\"]"; formulasString += "; P<0.8 [F \"all_coins_equal_1\"]"; formulasString += "; P<0.9 [F \"all_coins_equal_1\"]"; formulasString += "; Rmax=? [F \"all_coins_equal_1\"]"; formulasString += "; Rmin=? [F \"all_coins_equal_1\"]"; formulasString += "; Rmax=? [F \"finished\"]"; formulasString += "; Rmin=? [F \"finished\"]"; auto modelFormulas = this->buildModelFormulas(STORM_TEST_RESOURCES_DIR "/mdp/coin2-2.nm", formulasString); auto model = std::move(modelFormulas.first); auto tasks = this->getTasks(modelFormulas.second); EXPECT_EQ(272ul, model->getNumberOfStates()); EXPECT_EQ(492ul, model->getNumberOfTransitions()); ASSERT_EQ(model->getType(), storm::models::ModelType::Mdp); auto checker = this->createModelChecker(model); std::unique_ptr result; result = checker->check(this->env(), tasks[0]); EXPECT_NEAR(this->parseNumber("1"), this->getQuantitativeResultAtInitialState(model, result), this->precision()); result = checker->check(this->env(), tasks[1]); EXPECT_NEAR(this->parseNumber("57/64"), this->getQuantitativeResultAtInitialState(model, result), this->precision()); result = checker->check(this->env(), tasks[2]); EXPECT_FALSE(this->getQualitativeResultAtInitialState(model, result)); result = checker->check(this->env(), tasks[3]); EXPECT_TRUE(this->getQualitativeResultAtInitialState(model, result)); result = checker->check(this->env(), tasks[4]); EXPECT_TRUE(storm::utility::isInfinity(this->getQuantitativeResultAtInitialState(model, result))); result = checker->check(this->env(), tasks[5]); EXPECT_TRUE(storm::utility::isInfinity(this->getQuantitativeResultAtInitialState(model, result))); result = checker->check(this->env(), tasks[6]); EXPECT_NEAR(this->parseNumber("75"), this->getQuantitativeResultAtInitialState(model, result), this->precision()); result = checker->check(this->env(), tasks[7]); EXPECT_NEAR(this->parseNumber("48"), this->getQuantitativeResultAtInitialState(model, result), this->precision()); } TYPED_TEST(MdpPrctlModelCheckerTest, TinyRewards) { std::string formulasString = "Rmin=? [F \"target\"]"; auto modelFormulas = this->buildModelFormulas(STORM_TEST_RESOURCES_DIR "/mdp/tiny_rewards.nm", formulasString); auto model = std::move(modelFormulas.first); auto tasks = this->getTasks(modelFormulas.second); EXPECT_EQ(3ul, model->getNumberOfStates()); EXPECT_EQ(4ul, model->getNumberOfTransitions()); ASSERT_EQ(model->getType(), storm::models::ModelType::Mdp); auto checker = this->createModelChecker(model); std::unique_ptr result; // This example considers a zero-reward end component that does not reach the target // For some methods this requires end-component elimination which is (currently) not supported in the Dd engine if (TypeParam::engine == MdpEngine::PrismDd && this->env().solver().minMax().getMethod() == storm::solver::MinMaxMethod::RationalSearch) { STORM_SILENT_EXPECT_THROW(checker->check(this->env(), tasks[0]), storm::exceptions::UncheckedRequirementException); } else { result = checker->check(this->env(), tasks[0]); EXPECT_NEAR(this->parseNumber("1"), this->getQuantitativeResultAtInitialState(model, result), this->precision()); } } TYPED_TEST(MdpPrctlModelCheckerTest, Team) { std::string formulasString = "R{\"w_1_total\"}max=? [ C ]"; auto modelFormulas = this->buildModelFormulas(STORM_TEST_RESOURCES_DIR "/mdp/multiobj_team3.nm", formulasString); auto model = std::move(modelFormulas.first); auto tasks = this->getTasks(modelFormulas.second); EXPECT_EQ(12475ul, model->getNumberOfStates()); EXPECT_EQ(15228ul, model->getNumberOfTransitions()); ASSERT_EQ(model->getType(), storm::models::ModelType::Mdp); auto checker = this->createModelChecker(model); std::unique_ptr result; // This example considers an expected total reward formula, which is not supported in all engines if (TypeParam::engine == MdpEngine::PrismSparse || TypeParam::engine == MdpEngine::JaniSparse || TypeParam::engine == MdpEngine::JitSparse) { result = checker->check(this->env(), tasks[0]); EXPECT_NEAR(this->parseNumber("114/49"), this->getQuantitativeResultAtInitialState(model, result), this->precision()); } else { EXPECT_FALSE(checker->canHandle(tasks[0])); } } }