// // Created by Jip Spel on 20.09.18. // #include "gtest/gtest.h" #include "storm-config.h" #include "test/storm_gtest.h" #include "storm-pars/analysis/MonotonicityChecker.h" #include "storm/storage/expressions/BinaryRelationExpression.h" #include "storm/storage/SparseMatrix.h" #include "storm/adapters/RationalFunctionAdapter.h" #include "storm-parsers/parser/FormulaParser.h" #include "storm/logic/Formulas.h" #include "storm/models/sparse/StandardRewardModel.h" #include "storm/modelchecker/prctl/SparseDtmcPrctlModelChecker.h" #include "storm/modelchecker/results/ExplicitQuantitativeCheckResult.h" #include "storm-parsers/parser/AutoParser.h" #include "storm-parsers/parser/PrismParser.h" #include "storm/storage/expressions/ExpressionManager.h" #include "storm/api/builder.h" #include "storm-pars/transformer/SparseParametricDtmcSimplifier.h" #include "storm-pars/api/storm-pars.h" #include "storm/api/storm.h" #include "storm-parsers/api/storm-parsers.h" TEST(MonotonicityCheckerTest, Derivative_checker) { // Create the region typename storm::storage::ParameterRegion::Valuation lowerBoundaries; typename storm::storage::ParameterRegion::Valuation upperBoundaries; auto region = storm::storage::ParameterRegion(std::move(lowerBoundaries), std::move(upperBoundaries)); // Derivative 0 auto constFunction = storm::RationalFunction(0); auto constFunctionRes = storm::analysis::MonotonicityChecker::checkDerivative(constFunction, region); EXPECT_TRUE(constFunctionRes.first); EXPECT_TRUE(constFunctionRes.second); // Derivative 5 constFunction = storm::RationalFunction(5); constFunctionRes = storm::analysis::MonotonicityChecker::checkDerivative(constFunction, region); EXPECT_TRUE(constFunctionRes.first); EXPECT_FALSE(constFunctionRes.second); // Derivative -4 constFunction = storm::RationalFunction(storm::RationalFunction(1)-constFunction); constFunctionRes = storm::analysis::MonotonicityChecker::checkDerivative(constFunction, region); EXPECT_FALSE(constFunctionRes.first); EXPECT_TRUE(constFunctionRes.second); std::shared_ptr cache = std::make_shared(); carl::StringParser parser; parser.setVariables({"p", "q"}); // Create the region auto functionP = storm::RationalFunction(storm::Polynomial(parser.template parseMultivariatePolynomial("p"), cache)); auto functionQ = storm::RationalFunction(storm::Polynomial(parser.template parseMultivariatePolynomial("q"), cache)); auto varsP = functionP.gatherVariables(); auto varsQ = functionQ.gatherVariables(); storm::utility::parametric::Valuation lowerBoundaries2; storm::utility::parametric::Valuation upperBoundaries2; for (auto var : varsP) { typename storm::storage::ParameterRegion::CoefficientType lb = storm::utility::convertNumber::CoefficientType>(0 + 0.000001); typename storm::storage::ParameterRegion::CoefficientType ub = storm::utility::convertNumber::CoefficientType>(1 - 0.000001); lowerBoundaries2.emplace(std::make_pair(var, lb)); upperBoundaries2.emplace(std::make_pair(var, ub)); } for (auto var : varsQ) { typename storm::storage::ParameterRegion::CoefficientType lb = storm::utility::convertNumber::CoefficientType>(0 + 0.000001); typename storm::storage::ParameterRegion::CoefficientType ub = storm::utility::convertNumber::CoefficientType>(1 - 0.000001); lowerBoundaries2.emplace(std::make_pair(var, lb)); upperBoundaries2.emplace(std::make_pair(var, ub)); } region = storm::storage::ParameterRegion(std::move(lowerBoundaries2), std::move(upperBoundaries2)); // Derivative p auto function = functionP; auto functionRes = storm::analysis::MonotonicityChecker::checkDerivative(function, region); EXPECT_TRUE(functionRes.first); EXPECT_FALSE(functionRes.second); // Derivative 1-p auto functionDecr = storm::RationalFunction(storm::RationalFunction(1)-function); auto functionDecrRes = storm::analysis::MonotonicityChecker::checkDerivative(functionDecr, region); EXPECT_TRUE(functionDecrRes.first); EXPECT_FALSE(functionDecrRes.second); // Derivative 1-2p auto functionNonMonotonic = storm::RationalFunction(storm::RationalFunction(1)-storm::RationalFunction(2)*function); auto functionNonMonotonicRes = storm::analysis::MonotonicityChecker::checkDerivative(functionNonMonotonic, region); EXPECT_FALSE(functionNonMonotonicRes.first); EXPECT_FALSE(functionNonMonotonicRes.second); // Derivative -p functionDecr = storm::RationalFunction(storm::RationalFunction(0)-function); functionDecrRes = storm::analysis::MonotonicityChecker::checkDerivative(functionDecr, region); EXPECT_FALSE(functionDecrRes.first); EXPECT_TRUE(functionDecrRes.second); // Derivative p*q function = functionP * functionQ ; functionRes = storm::analysis::MonotonicityChecker::checkDerivative(function, region); EXPECT_TRUE(functionRes.first); EXPECT_FALSE(functionRes.second); } TEST(MonotonicityCheckerTest, Brp_with_bisimulation_no_samples) { std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/brp16_2.pm"; std::string formulaAsString = "P=? [true U s=4 & i=N ]"; std::string constantsAsString = ""; //e.g. pL=0.9,TOACK=0.5 // Program and formula storm::prism::Program program = storm::api::parseProgram(programFile); program = storm::utility::prism::preprocess(program, constantsAsString); std::vector> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program)); std::shared_ptr> model = storm::api::buildSparseModel(program, formulas)->as>(); std::shared_ptr> dtmc = model->as>(); auto simplifier = storm::transformer::SparseParametricDtmcSimplifier>(*dtmc); ASSERT_TRUE(simplifier.simplify(*(formulas[0]))); model = simplifier.getSimplifiedModel(); // Apply bisimulation storm::storage::BisimulationType bisimType = storm::storage::BisimulationType::Strong; if (storm::settings::getModule().isWeakBisimulationSet()) { bisimType = storm::storage::BisimulationType::Weak; } dtmc = storm::api::performBisimulationMinimization(model, formulas, bisimType)->as>(); // Create the region typename storm::storage::ParameterRegion::Valuation lowerBoundaries; typename storm::storage::ParameterRegion::Valuation upperBoundaries; std::set::VariableType> vars = storm::models::sparse::getProbabilityParameters(*dtmc); for (auto var : vars) { typename storm::storage::ParameterRegion::CoefficientType lb = storm::utility::convertNumber::CoefficientType>(0 + 0.000001); typename storm::storage::ParameterRegion::CoefficientType ub = storm::utility::convertNumber::CoefficientType>(1 - 0.000001); lowerBoundaries.emplace(std::make_pair(var, lb)); upperBoundaries.emplace(std::make_pair(var, ub)); } auto region = storm::storage::ParameterRegion(std::move(lowerBoundaries), std::move(upperBoundaries)); std::vector> regions = {region}; ASSERT_EQ(dtmc->getNumberOfStates(), 99ull); ASSERT_EQ(dtmc->getNumberOfTransitions(), 195ull); storm::analysis::MonotonicityChecker monotonicityChecker = storm::analysis::MonotonicityChecker(dtmc, formulas, regions, true); auto result = monotonicityChecker.checkMonotonicity(); EXPECT_EQ(1ul, result.size()); EXPECT_EQ(2ul, result.begin()->second.size()); auto monotone = result.begin()->second.begin(); EXPECT_EQ(true, monotone->second.first); EXPECT_EQ(false, monotone->second.second); } TEST(MonotonicityCheckerTest, Brp_with_bisimulation_samples) { std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/brp16_2.pm"; std::string formulaAsString = "P=? [true U s=4 & i=N ]"; std::string constantsAsString = ""; //e.g. pL=0.9,TOACK=0.5 // Program and formula storm::prism::Program program = storm::api::parseProgram(programFile); program = storm::utility::prism::preprocess(program, constantsAsString); std::vector> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program)); std::shared_ptr> model = storm::api::buildSparseModel(program, formulas)->as>(); std::shared_ptr> dtmc = model->as>(); auto simplifier = storm::transformer::SparseParametricDtmcSimplifier>(*dtmc); ASSERT_TRUE(simplifier.simplify(*(formulas[0]))); model = simplifier.getSimplifiedModel(); // Apply bisimulation storm::storage::BisimulationType bisimType = storm::storage::BisimulationType::Strong; if (storm::settings::getModule().isWeakBisimulationSet()) { bisimType = storm::storage::BisimulationType::Weak; } dtmc = storm::api::performBisimulationMinimization(model, formulas, bisimType)->as>(); // Create the region typename storm::storage::ParameterRegion::Valuation lowerBoundaries; typename storm::storage::ParameterRegion::Valuation upperBoundaries; std::set::VariableType> vars = storm::models::sparse::getProbabilityParameters(*dtmc); for (auto var : vars) { typename storm::storage::ParameterRegion::CoefficientType lb = storm::utility::convertNumber::CoefficientType>(0 + 0.000001); typename storm::storage::ParameterRegion::CoefficientType ub = storm::utility::convertNumber::CoefficientType>(1 - 0.000001); lowerBoundaries.emplace(std::make_pair(var, lb)); upperBoundaries.emplace(std::make_pair(var, ub)); } auto region = storm::storage::ParameterRegion(std::move(lowerBoundaries), std::move(upperBoundaries)); std::vector> regions = {region}; ASSERT_EQ(dtmc->getNumberOfStates(), 99ull); ASSERT_EQ(dtmc->getNumberOfTransitions(), 195ull); auto monotonicityChecker = storm::analysis::MonotonicityChecker(dtmc, formulas, regions, true, 50); auto result = monotonicityChecker.checkMonotonicity(); EXPECT_EQ(1ul, result.size()); EXPECT_EQ(2ul, result.begin()->second.size()); auto monotone = result.begin()->second.begin(); EXPECT_EQ(true, monotone->second.first); EXPECT_EQ(false, monotone->second.second); }