597 lines
44 KiB

8 years ago
  1. #include "storm-pars/modelchecker/region/SparseParameterLiftingModelChecker.h"
  2. #include <queue>
  3. #include <boost/container/flat_set.hpp>
  4. #include <storm-pars/analysis/MonotonicityChecker.h>
  5. #include "storm/adapters/RationalFunctionAdapter.h"
  6. #include "storm/logic/FragmentSpecification.h"
  7. #include "storm/modelchecker/results/ExplicitQuantitativeCheckResult.h"
  8. #include "storm/modelchecker/results/ExplicitQualitativeCheckResult.h"
  9. #include "storm/utility/vector.h"
  10. #include "storm/models/sparse/Dtmc.h"
  11. #include "storm/models/sparse/Mdp.h"
  12. #include "storm/models/sparse/StandardRewardModel.h"
  13. #include "storm/exceptions/InvalidArgumentException.h"
  14. #include "storm/exceptions/NotSupportedException.h"
  15. namespace storm {
  16. namespace modelchecker {
  17. template <typename SparseModelType, typename ConstantType>
  18. SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::SparseParameterLiftingModelChecker() {
  19. //Intentionally left empty
  20. }
  21. template <typename SparseModelType, typename ConstantType>
  22. void SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::specifyFormula(Environment const& env, storm::modelchecker::CheckTask<storm::logic::Formula, typename SparseModelType::ValueType> const& checkTask) {
  23. currentFormula = checkTask.getFormula().asSharedPointer();
  24. currentCheckTask = std::make_unique<storm::modelchecker::CheckTask<storm::logic::Formula, ConstantType>>(checkTask.substituteFormula(*currentFormula).template convertValueType<ConstantType>());
  25. if (currentCheckTask->getFormula().isProbabilityOperatorFormula()) {
  26. auto const& probOpFormula = currentCheckTask->getFormula().asProbabilityOperatorFormula();
  27. if(probOpFormula.getSubformula().isBoundedUntilFormula()) {
  28. specifyBoundedUntilFormula(env, currentCheckTask->substituteFormula(probOpFormula.getSubformula().asBoundedUntilFormula()));
  29. } else if(probOpFormula.getSubformula().isUntilFormula()) {
  30. specifyUntilFormula(env, currentCheckTask->substituteFormula(probOpFormula.getSubformula().asUntilFormula()));
  31. } else if (probOpFormula.getSubformula().isEventuallyFormula()) {
  32. specifyReachabilityProbabilityFormula(env, currentCheckTask->substituteFormula(probOpFormula.getSubformula().asEventuallyFormula()));
  33. } else {
  34. STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Parameter lifting is not supported for the given property.");
  35. }
  36. } else if (currentCheckTask->getFormula().isRewardOperatorFormula()) {
  37. auto const& rewOpFormula = currentCheckTask->getFormula().asRewardOperatorFormula();
  38. if(rewOpFormula.getSubformula().isEventuallyFormula()) {
  39. specifyReachabilityRewardFormula(env, currentCheckTask->substituteFormula(rewOpFormula.getSubformula().asEventuallyFormula()));
  40. } else if (rewOpFormula.getSubformula().isCumulativeRewardFormula()) {
  41. specifyCumulativeRewardFormula(env, currentCheckTask->substituteFormula(rewOpFormula.getSubformula().asCumulativeRewardFormula()));
  42. }
  43. }
  44. }
  45. template <typename SparseModelType, typename ConstantType>
  46. RegionResult SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::analyzeRegion(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, RegionResultHypothesis const& hypothesis, RegionResult const& initialResult, bool sampleVerticesOfRegion, std::shared_ptr<storm::analysis::Order> reachabilityOrder, std::shared_ptr<storm::analysis::LocalMonotonicityResult<typename RegionModelChecker<typename SparseModelType::ValueType>::VariableType>> localMonotonicityResult) {
  47. typedef typename RegionModelChecker<typename SparseModelType::ValueType>::VariableType VariableType;
  48. typedef typename storm::analysis::MonotonicityResult<VariableType>::Monotonicity Monotonicity;
  49. typedef typename storm::utility::parametric::Valuation<typename SparseModelType::ValueType> Valuation;
  50. typedef typename storm::storage::ParameterRegion<typename SparseModelType::ValueType>::CoefficientType CoefficientType;
  51. STORM_LOG_THROW(this->currentCheckTask->isOnlyInitialStatesRelevantSet(), storm::exceptions::NotSupportedException, "Analyzing regions with parameter lifting requires a property where only the value in the initial states is relevant.");
  52. STORM_LOG_THROW(this->currentCheckTask->isBoundSet(), storm::exceptions::NotSupportedException, "Analyzing regions with parameter lifting requires a bounded property.");
  53. STORM_LOG_THROW(this->parametricModel->getInitialStates().getNumberOfSetBits() == 1, storm::exceptions::NotSupportedException, "Analyzing regions with parameter lifting requires a model with a single initial state.");
  54. RegionResult result = initialResult;
  55. // Check if we need to check the formula on one point to decide whether to show AllSat or AllViolated
  56. if (hypothesis == RegionResultHypothesis::Unknown && result == RegionResult::Unknown) {
  57. result = getInstantiationChecker().check(env, region.getCenterPoint())->asExplicitQualitativeCheckResult()[*this->parametricModel->getInitialStates().begin()] ? RegionResult::CenterSat : RegionResult::CenterViolated;
  58. }
  59. bool existsSat = (hypothesis == RegionResultHypothesis::AllSat || result == RegionResult::ExistsSat || result == RegionResult::CenterSat);
  60. bool existsViolated = (hypothesis == RegionResultHypothesis::AllViolated || result == RegionResult::ExistsViolated || result == RegionResult::CenterViolated);
  61. // Here we check on global monotonicity
  62. if (localMonotonicityResult != nullptr && localMonotonicityResult->isDone()) {
  63. // Try to check it with a global monotonicity result
  64. auto monRes = localMonotonicityResult->getGlobalMonotonicityResult();
  65. bool lowerBound = isLowerBound(this->currentCheckTask->getBound().comparisonType);
  66. if (monRes->isDone() && monRes->isAllMonotonicity()) {
  67. // Build valuations
  68. auto monMap = monRes->getMonotonicityResult();
  69. Valuation valuationToCheckSat;
  70. Valuation valuationToCheckViolated;
  71. for (auto var : region.getVariables()) {
  72. auto monVar = monMap[var];
  73. if (monVar == Monotonicity::Constant) {
  74. valuationToCheckSat.insert(std::pair<VariableType, CoefficientType>(var, region.getLowerBoundary(var)));
  75. valuationToCheckViolated.insert(std::pair<VariableType, CoefficientType>(var, region.getLowerBoundary(var)));
  76. } else if (monVar == Monotonicity::Decr) {
  77. if (lowerBound) {
  78. valuationToCheckSat.insert(std::pair<VariableType, CoefficientType>(var, region.getUpperBoundary(var)));
  79. valuationToCheckViolated.insert(std::pair<VariableType, CoefficientType>(var, region.getLowerBoundary(var)));
  80. } else {
  81. valuationToCheckSat.insert(std::pair<VariableType, CoefficientType>(var, region.getLowerBoundary(var)));
  82. valuationToCheckViolated.insert(std::pair<VariableType, CoefficientType>(var, region.getUpperBoundary(var)));
  83. }
  84. } else if (monVar == Monotonicity::Incr) {
  85. if (lowerBound) {
  86. valuationToCheckSat.insert(std::pair<VariableType, CoefficientType>(var, region.getLowerBoundary(var)));
  87. valuationToCheckViolated.insert(std::pair<VariableType, CoefficientType>(var, region.getUpperBoundary(var)));
  88. } else {
  89. valuationToCheckSat.insert(std::pair<VariableType, CoefficientType>(var, region.getUpperBoundary(var)));
  90. valuationToCheckViolated.insert(std::pair<VariableType, CoefficientType>(var, region.getLowerBoundary(var)));
  91. }
  92. }
  93. }
  94. // Check for result
  95. if (existsSat && getInstantiationCheckerSAT().check(env, valuationToCheckSat)->asExplicitQualitativeCheckResult()[*this->parametricModel->getInitialStates().begin()]) {
  96. STORM_LOG_INFO("Region " << region << " is AllSat, discovered with instantiation checker on " << valuationToCheckSat << " and help of monotonicity" << std::endl);
  97. RegionModelChecker<typename SparseModelType::ValueType>::numberOfRegionsKnownThroughMonotonicity++;
  98. return RegionResult::AllSat;
  99. }
  100. if (existsViolated && !getInstantiationCheckerVIO().check(env, valuationToCheckViolated)->asExplicitQualitativeCheckResult()[*this->parametricModel->getInitialStates().begin()]) {
  101. STORM_LOG_INFO("Region " << region << " is AllViolated, discovered with instantiation checker on " << valuationToCheckViolated << " and help of monotonicity" << std::endl);
  102. RegionModelChecker<typename SparseModelType::ValueType>::numberOfRegionsKnownThroughMonotonicity++;
  103. return RegionResult::AllViolated;
  104. }
  105. return RegionResult::ExistsBoth;
  106. }
  107. }
  108. // Try to prove AllSat or AllViolated, depending on the hypothesis or the current result
  109. if (existsSat) {
  110. // show AllSat:
  111. storm::solver::OptimizationDirection parameterOptimizationDirection = isLowerBound(this->currentCheckTask->getBound().comparisonType) ? storm::solver::OptimizationDirection::Minimize : storm::solver::OptimizationDirection::Maximize;
  112. auto checkResult = this->check(env, region, parameterOptimizationDirection, reachabilityOrder, localMonotonicityResult);
  113. if (checkResult->asExplicitQualitativeCheckResult()[*this->parametricModel->getInitialStates().begin()]) {
  114. result = RegionResult::AllSat;
  115. } else if (sampleVerticesOfRegion) {
  116. result = sampleVertices(env, region, result);
  117. }
  118. } else if (existsViolated) {
  119. // show AllViolated:
  120. storm::solver::OptimizationDirection parameterOptimizationDirection = isLowerBound(this->currentCheckTask->getBound().comparisonType) ? storm::solver::OptimizationDirection::Maximize : storm::solver::OptimizationDirection::Minimize;
  121. auto checkResult = this->check(env, region, parameterOptimizationDirection, reachabilityOrder, localMonotonicityResult);
  122. if (!checkResult->asExplicitQualitativeCheckResult()[*this->parametricModel->getInitialStates().begin()]) {
  123. result = RegionResult::AllViolated;
  124. } else if (sampleVerticesOfRegion) {
  125. result = sampleVertices(env, region, result);
  126. }
  127. } else {
  128. STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "When analyzing a region, an invalid initial result was given: " << initialResult);
  129. }
  130. return result;
  131. }
  132. template <typename SparseModelType, typename ConstantType>
  133. RegionResult SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::sampleVertices(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, RegionResult const& initialResult) {
  134. RegionResult result = initialResult;
  135. if (result == RegionResult::AllSat || result == RegionResult::AllViolated) {
  136. return result;
  137. }
  138. bool hasSatPoint = result == RegionResult::ExistsSat || result == RegionResult::CenterSat;
  139. bool hasViolatedPoint = result == RegionResult::ExistsViolated || result == RegionResult::CenterViolated;
  140. // Check if there is a point in the region for which the property is satisfied
  141. auto vertices = region.getVerticesOfRegion(region.getVariables());
  142. auto vertexIt = vertices.begin();
  143. while (vertexIt != vertices.end() && !(hasSatPoint && hasViolatedPoint)) {
  144. if (getInstantiationChecker().check(env, *vertexIt)->asExplicitQualitativeCheckResult()[*this->parametricModel->getInitialStates().begin()]) {
  145. hasSatPoint = true;
  146. } else {
  147. hasViolatedPoint = true;
  148. }
  149. ++vertexIt;
  150. }
  151. if (hasSatPoint) {
  152. if (hasViolatedPoint) {
  153. result = RegionResult::ExistsBoth;
  154. } else if (result != RegionResult::CenterSat) {
  155. result = RegionResult::ExistsSat;
  156. }
  157. } else if (hasViolatedPoint && result != RegionResult::CenterViolated) {
  158. result = RegionResult::ExistsViolated;
  159. }
  160. return result;
  161. }
  162. template <typename SparseModelType, typename ConstantType>
  163. std::unique_ptr<CheckResult> SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::check(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dirForParameters, std::shared_ptr<storm::analysis::Order> reachabilityOrder, std::shared_ptr<storm::analysis::LocalMonotonicityResult<typename RegionModelChecker<typename SparseModelType::ValueType>::VariableType>> localMonotonicityResult) {
  164. auto quantitativeResult = computeQuantitativeValues(env, region, dirForParameters, localMonotonicityResult);
  165. lastValue = quantitativeResult->template asExplicitQuantitativeCheckResult<ConstantType>()[*this->parametricModel->getInitialStates().begin()];
  166. if(currentCheckTask->getFormula().hasQuantitativeResult()) {
  167. return quantitativeResult;
  168. } else {
  169. return quantitativeResult->template asExplicitQuantitativeCheckResult<ConstantType>().compareAgainstBound(this->currentCheckTask->getFormula().asOperatorFormula().getComparisonType(), this->currentCheckTask->getFormula().asOperatorFormula().template getThresholdAs<ConstantType>());
  170. }
  171. }
  172. template <typename SparseModelType, typename ConstantType>
  173. std::unique_ptr<QuantitativeCheckResult<ConstantType>> SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::getBound(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dirForParameters, std::shared_ptr<storm::analysis::LocalMonotonicityResult<typename RegionModelChecker<typename SparseModelType::ValueType>::VariableType>> localMonotonicityResult) {
  174. STORM_LOG_WARN_COND(this->currentCheckTask->getFormula().hasQuantitativeResult(), "Computing quantitative bounds for a qualitative formula...");
  175. return std::make_unique<ExplicitQuantitativeCheckResult<ConstantType>>(std::move(computeQuantitativeValues(env, region, dirForParameters, localMonotonicityResult)->template asExplicitQuantitativeCheckResult<ConstantType>()));
  176. }
  177. template <typename SparseModelType, typename ConstantType>
  178. typename SparseModelType::ValueType SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::getBoundAtInitState(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dirForParameters) {
  179. STORM_LOG_THROW(this->parametricModel->getInitialStates().getNumberOfSetBits() == 1, storm::exceptions::NotSupportedException, "Getting a bound at the initial state requires a model with a single initial state.");
  180. return storm::utility::convertNumber<typename SparseModelType::ValueType>(getBound(env, region, dirForParameters)->template asExplicitQuantitativeCheckResult<ConstantType>()[*this->parametricModel->getInitialStates().begin()]);
  181. }
  182. template <typename SparseModelType, typename ConstantType>
  183. storm::modelchecker::SparseInstantiationModelChecker<SparseModelType, ConstantType>& SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::getInstantiationCheckerSAT() {
  184. return getInstantiationChecker();
  185. }
  186. template <typename SparseModelType, typename ConstantType>
  187. storm::modelchecker::SparseInstantiationModelChecker<SparseModelType, ConstantType>& SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::getInstantiationCheckerVIO() {
  188. return getInstantiationChecker();
  189. }
  190. template <typename SparseModelType, typename ConstantType>
  191. struct RegionBound {
  192. typedef typename storm::storage::ParameterRegion<typename SparseModelType::ValueType>::VariableType VariableType;
  193. RegionBound(RegionBound<SparseModelType, ConstantType> const& other) = default;
  194. RegionBound(storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& r, std::shared_ptr<storm::analysis::Order> o, std::shared_ptr<storm::analysis::LocalMonotonicityResult<VariableType>> l, ConstantType const& b) : region(r), order(o), localMonRes(l), bound(b) {}
  195. storm::storage::ParameterRegion<typename SparseModelType::ValueType> region;
  196. std::shared_ptr<storm::analysis::Order> order;
  197. std::shared_ptr<storm::analysis::LocalMonotonicityResult<VariableType>> localMonRes;
  198. ConstantType bound;
  199. };
  200. template<typename SparseModelType, typename ConstantType>
  201. std::pair<typename SparseModelType::ValueType, typename storm::storage::ParameterRegion<typename SparseModelType::ValueType>::Valuation> SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::computeExtremalValue(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dir, typename SparseModelType::ValueType const& precision, boost::optional<ConstantType> const& initialValue) {
  202. typedef typename storm::storage::ParameterRegion<typename SparseModelType::ValueType>::CoefficientType CoefficientType;
  203. typedef typename storm::storage::ParameterRegion<typename SparseModelType::ValueType>::Valuation Valuation;
  204. STORM_LOG_THROW(this->parametricModel->getInitialStates().getNumberOfSetBits() == 1, storm::exceptions::NotSupportedException, "Getting extremal values at the initial state requires a model with a single initial state.");
  205. bool const useMonotonicity = this->isUseMonotonicitySet();
  206. bool const minimize = storm::solver::minimize(dir);
  207. // Comparator for the region queue
  208. auto cmp = storm::solver::minimize(dir) ?
  209. [](RegionBound<SparseModelType, ConstantType> const& lhs, RegionBound<SparseModelType, ConstantType> const& rhs) { return lhs.bound > rhs.bound; } :
  210. [](RegionBound<SparseModelType, ConstantType> const& lhs, RegionBound<SparseModelType, ConstantType> const& rhs) { return lhs.bound < rhs.bound; };
  211. std::priority_queue<RegionBound<SparseModelType, ConstantType>, std::vector<RegionBound<SparseModelType, ConstantType>>, decltype(cmp)> regionQueue(cmp);
  212. storm::utility::Stopwatch initialWatch(true);
  213. storm::utility::Stopwatch boundsWatch(false);
  214. auto numberOfPLACallsBounds = 0;
  215. ConstantType initBound;
  216. initBound = storm::utility::zero<ConstantType>();
  217. bool first = true;
  218. if (useMonotonicity) {
  219. if (this->isUseBoundsSet()) {
  220. numberOfPLACallsBounds++;
  221. numberOfPLACallsBounds++;
  222. auto minBound = getBound(env, region, storm::solver::OptimizationDirection::Minimize, nullptr)->template asExplicitQuantitativeCheckResult<ConstantType>().getValueVector();
  223. auto maxBound = getBound(env, region, storm::solver::OptimizationDirection::Maximize, nullptr)->template asExplicitQuantitativeCheckResult<ConstantType>().getValueVector();
  224. if (minimize) {
  225. initBound = minBound[*this->parametricModel->getInitialStates().begin()];
  226. } else {
  227. initBound = maxBound[*this->parametricModel->getInitialStates().begin()];
  228. }
  229. orderExtender->setMinValuesInit(minBound);
  230. orderExtender->setMaxValuesInit(maxBound);
  231. }
  232. auto order = this->extendOrder(env, nullptr, region);
  233. auto monRes = std::shared_ptr<storm::analysis::LocalMonotonicityResult<VariableType>>(new storm::analysis::LocalMonotonicityResult<VariableType>(order->getNumberOfStates()));
  234. storm::utility::Stopwatch monotonicityWatch(true);
  235. this->extendLocalMonotonicityResult(region, order, monRes);
  236. monotonicityWatch.stop();
  237. STORM_LOG_INFO(std::endl << "Total time for monotonicity checking: " << monotonicityWatch << "." << std::endl << std::endl);
  238. regionQueue.emplace(region, order, monRes, initBound);
  239. first = false;
  240. } else {
  241. regionQueue.emplace(region, nullptr, nullptr, initBound);
  242. }
  243. // The results
  244. boost::optional<ConstantType> value;
  245. Valuation valuation;
  246. if (!initialValue) {
  247. auto init = getGoodInitialPoint(env, region, dir, regionQueue.top().localMonRes);
  248. value = storm::utility::convertNumber<ConstantType>(init.first);
  249. valuation = std::move(init.second);
  250. } else {
  251. value = initialValue;
  252. }
  253. initialWatch.stop();
  254. STORM_LOG_INFO(std::endl << "Total time for initial points: " << initialWatch << "." << std::endl << std::endl);
  255. if (!initialValue) {
  256. STORM_LOG_INFO("Initial value: " << value.get() << " at " << valuation);
  257. } else {
  258. STORM_LOG_INFO("Initial value: " << value.get() << " as provided by the user");
  259. }
  260. auto numberOfSplits = 0;
  261. auto numberOfPLACalls = 0;
  262. auto numberOfOrderCopies = 0;
  263. auto numberOfMonResCopies = 0;
  264. storm::utility::Stopwatch loopWatch(true);
  265. if (!(useMonotonicity && regionQueue.top().localMonRes->getGlobalMonotonicityResult()->isDone() && regionQueue.top().localMonRes->getGlobalMonotonicityResult()->isAllMonotonicity())) {
  266. // Doing the extremal computation, only when we don't use monotonicity or there are possibly not monotone variables.
  267. auto totalArea = storm::utility::convertNumber<ConstantType>(region.area());
  268. auto coveredArea = storm::utility::zero<ConstantType>();
  269. while (!regionQueue.empty()) {
  270. assert (value);
  271. auto currRegion = regionQueue.top().region;
  272. auto order = regionQueue.top().order;
  273. auto localMonotonicityResult = regionQueue.top().localMonRes;
  274. auto currBound = regionQueue.top().bound;
  275. STORM_LOG_INFO("Currently looking at region: " << currRegion);
  276. std::vector<storm::storage::ParameterRegion<typename SparseModelType::ValueType>> newRegions;
  277. // Check whether this region needs further investigation based on the bound of the parent region
  278. bool investigateBounds = first || (minimize && currBound < value.get() - storm::utility::convertNumber<ConstantType>(precision))
  279. || (!minimize && currBound > value.get() + storm::utility::convertNumber<ConstantType>(precision));
  280. first = false;
  281. if (investigateBounds) {
  282. numberOfPLACalls++;
  283. auto bounds = getBound(env, currRegion, dir, localMonotonicityResult)->template asExplicitQuantitativeCheckResult<ConstantType>().getValueVector();
  284. currBound = bounds[*this->parametricModel->getInitialStates().begin()];
  285. // Check whether this region needs further investigation based on the bound of this region
  286. bool lookAtRegion = (minimize && currBound < value.get() - storm::utility::convertNumber<ConstantType>(precision))
  287. || (!minimize && currBound > value.get() + storm::utility::convertNumber<ConstantType>(precision));
  288. if (lookAtRegion) {
  289. if (useMonotonicity) {
  290. // Continue extending order/monotonicity result
  291. bool changedOrder = false;
  292. if (!order->getDoneBuilding() && orderExtender->isHope(order, currRegion)) {
  293. if (numberOfCopiesOrder[order] != 1) {
  294. numberOfCopiesOrder[order]--;
  295. order = copyOrder(order);
  296. numberOfOrderCopies++;
  297. } else {
  298. assert (numberOfCopiesOrder[order] == 1);
  299. }
  300. this->extendOrder(env, order, currRegion);
  301. changedOrder = true;
  302. }
  303. if (changedOrder) {
  304. assert(!localMonotonicityResult->isDone());
  305. if (numberOfCopiesMonRes[localMonotonicityResult] != 1) {
  306. numberOfCopiesMonRes[localMonotonicityResult]--;
  307. localMonotonicityResult = localMonotonicityResult->copy();
  308. numberOfMonResCopies++;
  309. } else {
  310. assert (numberOfCopiesMonRes[localMonotonicityResult] == 1);
  311. }
  312. this->extendLocalMonotonicityResult(currRegion, order, localMonotonicityResult);
  313. STORM_LOG_INFO("Order and monotonicity result got extended");
  314. }
  315. }
  316. // Check whether this region contains a new 'good' value and set this value
  317. auto point = useMonotonicity ? currRegion.getPoint(dir, *(localMonotonicityResult->getGlobalMonotonicityResult())) : currRegion.getCenterPoint();
  318. auto currValue = getInstantiationChecker().check(env, point)->template asExplicitQuantitativeCheckResult<ConstantType>()[*this->parametricModel->getInitialStates().begin()];
  319. if (!value || (minimize ? currValue <= value.get() : currValue >= value.get())) {
  320. value = currValue;
  321. valuation = point;
  322. }
  323. if ((minimize && currBound < value.get() - storm::utility::convertNumber<ConstantType>(precision))
  324. || (!minimize && currBound > value.get() + storm::utility::convertNumber<ConstantType>(precision))) {
  325. // We will split the region in this case, but first we set the bounds to extend the order for the new regions.
  326. if (useMonotonicity && this->isUseBoundsSet() && !order->getDoneBuilding()) {
  327. boundsWatch.start();
  328. numberOfPLACallsBounds++;
  329. if (minimize) {
  330. orderExtender->setMinMaxValues(order, bounds, getBound(env, currRegion, storm::solver::OptimizationDirection::Maximize, localMonotonicityResult)->template asExplicitQuantitativeCheckResult<ConstantType>().getValueVector());
  331. } else {
  332. orderExtender->setMinMaxValues(order, getBound(env, currRegion, storm::solver::OptimizationDirection::Maximize, localMonotonicityResult)->template asExplicitQuantitativeCheckResult<ConstantType>().getValueVector(), bounds);
  333. }
  334. boundsWatch.stop();
  335. }
  336. // Now split the region
  337. if (useMonotonicity) {
  338. this->splitSmart(currRegion, newRegions, order, *(localMonotonicityResult->getGlobalMonotonicityResult()), true);
  339. } else if (this->isRegionSplitEstimateSupported()) {
  340. auto empty = storm::analysis::MonotonicityResult<VariableType>();
  341. this->splitSmart(currRegion, newRegions, order, empty, true);
  342. } else {
  343. currRegion.split(currRegion.getCenterPoint(), newRegions);
  344. }
  345. }
  346. }
  347. }
  348. if (newRegions.empty()) {
  349. // When the newRegions is empty we are done with the current region
  350. coveredArea += storm::utility::convertNumber<ConstantType>(currRegion.area());
  351. if (order != nullptr) {
  352. numberOfCopiesOrder[order]--;
  353. numberOfCopiesMonRes[localMonotonicityResult]--;
  354. }
  355. regionQueue.pop();
  356. } else {
  357. regionQueue.pop();
  358. STORM_LOG_INFO("Splitting region " << currRegion << " into " << newRegions.size());
  359. numberOfSplits++;
  360. // Add the new regions to the queue
  361. if (useMonotonicity) {
  362. for (auto &r : newRegions) {
  363. r.setBoundParent(storm::utility::convertNumber<CoefficientType>(currBound));
  364. regionQueue.emplace(r, order, localMonotonicityResult, currBound);
  365. }
  366. if (numberOfCopiesOrder.find(order) != numberOfCopiesOrder.end()) {
  367. numberOfCopiesOrder[order] += newRegions.size();
  368. numberOfCopiesMonRes[localMonotonicityResult] += newRegions.size();
  369. } else {
  370. numberOfCopiesOrder[order] = newRegions.size();
  371. numberOfCopiesMonRes[localMonotonicityResult] = newRegions.size();
  372. }
  373. } else {
  374. for (auto &r : newRegions) {
  375. r.setBoundParent(storm::utility::convertNumber<CoefficientType>(currBound));
  376. regionQueue.emplace(r, nullptr, nullptr, currBound);
  377. }
  378. }
  379. }
  380. STORM_LOG_INFO("Current value : " << value.get() << ", current bound: " << currBound << ".");
  381. STORM_LOG_INFO("Covered " << (coveredArea * storm::utility::convertNumber<ConstantType>(100.0) / totalArea) << "% of the region." << std::endl);
  382. }
  383. loopWatch.stop();
  384. }
  385. STORM_LOG_INFO("Total number of splits: " << numberOfSplits << std::endl);
  386. STORM_PRINT("Total number of plaCalls: " << numberOfPLACalls << std::endl);
  387. if (useMonotonicity) {
  388. STORM_PRINT("Total number of plaCalls for bounds for monotonicity checking: " << numberOfPLACallsBounds << std::endl);
  389. STORM_PRINT("Total number of copies of the order: " << numberOfOrderCopies << std::endl);
  390. STORM_PRINT("Total number of copies of the local monotonicity result: " << numberOfMonResCopies
  391. << std::endl);
  392. }
  393. STORM_LOG_INFO(std::endl << "Total time for region refinement: " << loopWatch << "." << std::endl << std::endl);
  394. STORM_LOG_INFO(std::endl << "Total time for additional bounds: " << boundsWatch << "." << std::endl << std::endl);
  395. return std::make_pair(storm::utility::convertNumber<typename SparseModelType::ValueType>(value.get()), valuation);
  396. }
  397. template <typename SparseModelType, typename ConstantType>
  398. std::pair<typename SparseModelType::ValueType, typename storm::storage::ParameterRegion<typename SparseModelType::ValueType>::Valuation> SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::computeExtremalValue(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dir, typename SparseModelType::ValueType const& precision) {
  399. return computeExtremalValue(env, region, dir, precision, boost::none);
  400. }
  401. template <typename SparseModelType, typename ConstantType>
  402. bool SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::checkExtremalValue(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dir, typename SparseModelType::ValueType const& precision, typename SparseModelType::ValueType const& valueToCheck) {
  403. auto res = computeExtremalValue(env, region, dir, precision, storm::utility::convertNumber<ConstantType>(valueToCheck)).first;
  404. return storm::solver::minimize(dir) ? storm::utility::convertNumber<ConstantType>(res) >= storm::utility::convertNumber<ConstantType>(valueToCheck) : storm::utility::convertNumber<ConstantType>(res) <= storm::utility::convertNumber<ConstantType>(valueToCheck);
  405. }
  406. template <typename SparseModelType, typename ConstantType>
  407. SparseModelType const& SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::getConsideredParametricModel() const {
  408. return *parametricModel;
  409. }
  410. template <typename SparseModelType, typename ConstantType>
  411. CheckTask<storm::logic::Formula, ConstantType> const& SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::getCurrentCheckTask() const {
  412. return *currentCheckTask;
  413. }
  414. template <typename SparseModelType, typename ConstantType>
  415. void SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::specifyBoundedUntilFormula(Environment const& env, CheckTask<logic::BoundedUntilFormula, ConstantType> const& checkTask) {
  416. STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Parameter lifting is not supported for the given property.");
  417. }
  418. template <typename SparseModelType, typename ConstantType>
  419. void SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::specifyUntilFormula(Environment const& env, CheckTask<logic::UntilFormula, ConstantType> const& checkTask) {
  420. STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Parameter lifting is not supported for the given property.");
  421. }
  422. template <typename SparseModelType, typename ConstantType>
  423. void SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::specifyReachabilityProbabilityFormula(Environment const& env, CheckTask<logic::EventuallyFormula, ConstantType> const& checkTask) {
  424. // transform to until formula
  425. auto untilFormula = std::make_shared<storm::logic::UntilFormula const>(storm::logic::Formula::getTrueFormula(), checkTask.getFormula().getSubformula().asSharedPointer());
  426. specifyUntilFormula(env, currentCheckTask->substituteFormula(*untilFormula));
  427. }
  428. template <typename SparseModelType, typename ConstantType>
  429. void SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::specifyReachabilityRewardFormula(Environment const& env, CheckTask<logic::EventuallyFormula, ConstantType> const& checkTask) {
  430. STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Parameter lifting is not supported for the given property.");
  431. }
  432. template <typename SparseModelType, typename ConstantType>
  433. void SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::specifyCumulativeRewardFormula(Environment const& env, CheckTask<logic::CumulativeRewardFormula, ConstantType> const& checkTask) {
  434. STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Parameter lifting is not supported for the given property.");
  435. }
  436. template<typename SparseModelType, typename ConstantType>
  437. std::shared_ptr<storm::analysis::Order> SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::copyOrder(std::shared_ptr<storm::analysis::Order> order) {
  438. auto res = order->copy();
  439. if (orderExtender) {
  440. orderExtender->setUnknownStates(order, res);
  441. orderExtender->copyMinMax(order, res);
  442. }
  443. return res;
  444. }
  445. template<typename SparseModelType, typename ConstantType>
  446. std::pair<typename SparseModelType::ValueType, typename storm::storage::ParameterRegion<typename SparseModelType::ValueType>::Valuation> SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::checkForPossibleMonotonicity(Environment const& env,
  447. const storage::ParameterRegion<typename SparseModelType::ValueType> &region,
  448. std::set<VariableType>& possibleMonotoneIncrParameters,
  449. std::set<VariableType>& possibleMonotoneDecrParameters,
  450. std::set<VariableType>& possibleNotMonotoneParameters,
  451. std::set<VariableType>const& consideredVariables,
  452. storm::solver::OptimizationDirection const& dir) {
  453. bool minimize = storm::solver::minimize(dir);
  454. typedef typename storm::storage::ParameterRegion<typename SparseModelType::ValueType>::Valuation Valuation;
  455. typedef typename storm::storage::ParameterRegion<typename SparseModelType::ValueType>::CoefficientType CoefficientType;
  456. ConstantType value = storm::solver::minimize(dir) ? 1 : 0;
  457. Valuation valuation;
  458. for (auto& var : consideredVariables) {
  459. ConstantType previousCenter = -1;
  460. bool monDecr = true;
  461. bool monIncr = true;
  462. auto valuationCenter = region.getCenterPoint();
  463. valuationCenter[var] = region.getLowerBoundary(var);
  464. // TODO: make cmdline argument or 1/precision
  465. int numberOfSamples = 50;
  466. auto stepSize = (region.getUpperBoundary(var) - region.getLowerBoundary(var)) / (numberOfSamples - 1);
  467. while (valuationCenter[var] <= region.getUpperBoundary(var)) {
  468. // Create valuation
  469. ConstantType valueCenter = getInstantiationChecker().check(env, valuationCenter)->template asExplicitQuantitativeCheckResult<ConstantType>()[*this->parametricModel->getInitialStates().begin()];
  470. if (storm::solver::minimize(dir) ? valueCenter <= value : valueCenter >= value) {
  471. value = valueCenter;
  472. valuation = valuationCenter;
  473. }
  474. // Calculate difference with result for previous valuation
  475. ConstantType diffCenter = previousCenter - valueCenter;
  476. assert (previousCenter == -1 || (diffCenter >= -1 && diffCenter <= 1));
  477. if (previousCenter != -1) {
  478. assert (previousCenter != -1 && previousCenter != -1);
  479. monDecr &= diffCenter > 0 && diffCenter > 0 && diffCenter > 0; // then previous value is larger than the current value from the initial states
  480. monIncr &= diffCenter < 0 && diffCenter < 0 && diffCenter < 0;
  481. }
  482. previousCenter = valueCenter;
  483. if (!monDecr && ! monIncr) {
  484. break;
  485. }
  486. valuationCenter[var] += stepSize;
  487. }
  488. if (monIncr) {
  489. possibleMonotoneParameters.insert(var);
  490. possibleMonotoneIncrParameters.insert(var);
  491. } else if (monDecr) {
  492. possibleMonotoneParameters.insert(var);
  493. possibleMonotoneDecrParameters.insert(var);
  494. } else {
  495. possibleNotMonotoneParameters.insert(var);
  496. }
  497. }
  498. return std::make_pair(storm::utility::convertNumber<typename SparseModelType::ValueType>(value), std::move(valuation));
  499. }
  500. template<typename SparseModelType, typename ConstantType>
  501. std::pair<typename SparseModelType::ValueType, typename storm::storage::ParameterRegion<typename SparseModelType::ValueType>::Valuation> SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::getGoodInitialPoint(const Environment &env, const storage::ParameterRegion<typename SparseModelType::ValueType> &region, const OptimizationDirection &dir, std::shared_ptr<storm::analysis::LocalMonotonicityResult<VariableType>> localMonRes) {
  502. typedef typename storm::storage::ParameterRegion<typename SparseModelType::ValueType>::Valuation Valuation;
  503. typedef typename storm::storage::ParameterRegion<typename SparseModelType::ValueType>::CoefficientType CoefficientType;
  504. ConstantType value = storm::solver::minimize(dir) ? 1 : 0;
  505. Valuation valuation;
  506. std::set<VariableType> monIncr, monDecr, notMon, notMonFirst;
  507. STORM_LOG_INFO("Number of parameters: " << region.getVariables().size() << std::endl;);
  508. if (localMonRes != nullptr) {
  509. localMonRes->getGlobalMonotonicityResult()->splitBasedOnMonotonicity(region.getVariables(), monIncr, monDecr, notMonFirst);
  510. auto numMon = monIncr.size() + monDecr.size();
  511. STORM_LOG_INFO("Number of monotone parameters: " << numMon << std::endl;);
  512. if (numMon < region.getVariables().size()) {
  513. checkForPossibleMonotonicity(env, region, monIncr, monDecr, notMon, notMonFirst, dir);
  514. STORM_LOG_INFO("Number of possible monotone parameters: " << (monIncr.size() + monDecr.size() - numMon) << std::endl;);
  515. STORM_LOG_INFO("Number of definitely not monotone parameters: " << notMon.size() << std::endl;);
  516. }
  517. valuation = region.getPoint(dir, monIncr, monDecr);
  518. } else {
  519. valuation = region.getCenterPoint();
  520. }
  521. value = getInstantiationChecker().check(env, valuation)->template asExplicitQuantitativeCheckResult<ConstantType>()[*this->parametricModel->getInitialStates().begin()];
  522. return std::make_pair(storm::utility::convertNumber<typename SparseModelType::ValueType>(value), std::move(valuation));
  523. }
  524. template class SparseParameterLiftingModelChecker<storm::models::sparse::Dtmc<storm::RationalFunction>, double>;
  525. template class SparseParameterLiftingModelChecker<storm::models::sparse::Mdp<storm::RationalFunction>, double>;
  526. template class SparseParameterLiftingModelChecker<storm::models::sparse::Dtmc<storm::RationalFunction>, storm::RationalNumber>;
  527. template class SparseParameterLiftingModelChecker<storm::models::sparse::Mdp<storm::RationalFunction>, storm::RationalNumber>;
  528. }
  529. }