You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

673 lines
46 KiB

4 years ago
8 years ago
8 years ago
8 years ago
  1. #include "storm-pars/modelchecker/region/SparseDtmcParameterLiftingModelChecker.h"
  2. #include "storm-pars/transformer/SparseParametricDtmcSimplifier.h"
  3. #include "storm/adapters/RationalFunctionAdapter.h"
  4. #include "storm/modelchecker/propositional/SparsePropositionalModelChecker.h"
  5. #include "storm/modelchecker/results/ExplicitQualitativeCheckResult.h"
  6. #include "storm/modelchecker/results/ExplicitQuantitativeCheckResult.h"
  7. #include "storm/modelchecker/prctl/helper/DsMpiUpperRewardBoundsComputer.h"
  8. #include "storm/modelchecker/prctl/helper/BaierUpperRewardBoundsComputer.h"
  9. #include "storm/models/sparse/Dtmc.h"
  10. #include "storm/models/sparse/StandardRewardModel.h"
  11. #include "storm/solver/MinMaxLinearEquationSolver.h"
  12. #include "storm/solver/Multiplier.h"
  13. #include "storm/utility/vector.h"
  14. #include "storm/utility/graph.h"
  15. #include "storm/utility/NumberTraits.h"
  16. #include "storm/exceptions/InvalidArgumentException.h"
  17. #include "storm/exceptions/InvalidPropertyException.h"
  18. #include "storm/exceptions/NotSupportedException.h"
  19. #include "storm/exceptions/UnexpectedException.h"
  20. #include "storm/exceptions/InvalidOperationException.h"
  21. #include "storm/exceptions/UncheckedRequirementException.h"
  22. namespace storm {
  23. namespace modelchecker {
  24. template <typename SparseModelType, typename ConstantType>
  25. SparseDtmcParameterLiftingModelChecker<SparseModelType, ConstantType>::SparseDtmcParameterLiftingModelChecker() : SparseDtmcParameterLiftingModelChecker<SparseModelType, ConstantType>(std::make_unique<storm::solver::GeneralMinMaxLinearEquationSolverFactory<ConstantType>>()) {
  26. // Intentionally left empty
  27. }
  28. template <typename SparseModelType, typename ConstantType>
  29. SparseDtmcParameterLiftingModelChecker<SparseModelType, ConstantType>::SparseDtmcParameterLiftingModelChecker(std::unique_ptr<storm::solver::MinMaxLinearEquationSolverFactory<ConstantType>>&& solverFactory) : solverFactory(std::move(solverFactory)), solvingRequiresUpperRewardBounds(false), regionSplitEstimationsEnabled(false) {
  30. // Intentionally left empty
  31. }
  32. template <typename SparseModelType, typename ConstantType>
  33. bool SparseDtmcParameterLiftingModelChecker<SparseModelType, ConstantType>::canHandle(std::shared_ptr<storm::models::ModelBase> parametricModel, CheckTask<storm::logic::Formula, ValueType> const& checkTask) const {
  34. bool result = parametricModel->isOfType(storm::models::ModelType::Dtmc);
  35. result &= parametricModel->isSparseModel();
  36. result &= parametricModel->supportsParameters();
  37. auto dtmc = parametricModel->template as<SparseModelType>();
  38. result &= static_cast<bool>(dtmc);
  39. result &= checkTask.getFormula().isInFragment(storm::logic::reachability().setRewardOperatorsAllowed(true).setReachabilityRewardFormulasAllowed(true).setBoundedUntilFormulasAllowed(true).setCumulativeRewardFormulasAllowed(true).setStepBoundedCumulativeRewardFormulasAllowed(true).setTimeBoundedCumulativeRewardFormulasAllowed(true).setTimeBoundedUntilFormulasAllowed(true).setStepBoundedUntilFormulasAllowed(true).setTimeBoundedUntilFormulasAllowed(true));
  40. return result;
  41. }
  42. template <typename SparseModelType, typename ConstantType>
  43. void SparseDtmcParameterLiftingModelChecker<SparseModelType, ConstantType>::specify(Environment const& env, std::shared_ptr<storm::models::ModelBase> parametricModel, CheckTask<storm::logic::Formula, ValueType> const& checkTask, bool generateRegionSplitEstimates, bool allowModelSimplification) {
  44. auto dtmc = parametricModel->template as<SparseModelType>();
  45. monotonicityChecker = std::make_unique<storm::analysis::MonotonicityChecker<ValueType>>(dtmc->getTransitionMatrix());
  46. specify_internal(env, dtmc, checkTask, generateRegionSplitEstimates, !allowModelSimplification);
  47. if (checkTask.isBoundSet()) {
  48. thresholdTask = storm::utility::convertNumber<ConstantType>(checkTask.getBoundThreshold());
  49. }
  50. }
  51. template <typename SparseModelType, typename ConstantType>
  52. void SparseDtmcParameterLiftingModelChecker<SparseModelType, ConstantType>::specify_internal(Environment const& env, std::shared_ptr<SparseModelType> parametricModel, CheckTask<storm::logic::Formula, ValueType> const& checkTask, bool generateRegionSplitEstimates, bool skipModelSimplification) {
  53. STORM_LOG_ASSERT(this->canHandle(parametricModel, checkTask), "specified model and formula can not be handled by this.");
  54. reset();
  55. regionSplitEstimationsEnabled = generateRegionSplitEstimates;
  56. if (skipModelSimplification) {
  57. this->parametricModel = parametricModel;
  58. this->specifyFormula(env, checkTask);
  59. } else {
  60. auto simplifier = storm::transformer::SparseParametricDtmcSimplifier<SparseModelType>(*parametricModel);
  61. if (!simplifier.simplify(checkTask.getFormula())) {
  62. STORM_LOG_THROW(false, storm::exceptions::UnexpectedException, "Simplifying the model was not successfull.");
  63. }
  64. this->parametricModel = simplifier.getSimplifiedModel();
  65. this->specifyFormula(env, checkTask.substituteFormula(*simplifier.getSimplifiedFormula()));
  66. }
  67. }
  68. template <typename SparseModelType, typename ConstantType>
  69. void SparseDtmcParameterLiftingModelChecker<SparseModelType, ConstantType>::specifyBoundedUntilFormula(Environment const& env, CheckTask<storm::logic::BoundedUntilFormula, ConstantType> const& checkTask) {
  70. // get the step bound
  71. STORM_LOG_THROW(!checkTask.getFormula().hasLowerBound(), storm::exceptions::NotSupportedException, "Lower step bounds are not supported.");
  72. STORM_LOG_THROW(checkTask.getFormula().hasUpperBound(), storm::exceptions::NotSupportedException, "Expected a bounded until formula with an upper bound.");
  73. STORM_LOG_THROW(checkTask.getFormula().getTimeBoundReference().isStepBound(), storm::exceptions::NotSupportedException, "Expected a bounded until formula with step bounds.");
  74. stepBound = checkTask.getFormula().getUpperBound().evaluateAsInt();
  75. STORM_LOG_THROW(*stepBound > 0, storm::exceptions::NotSupportedException, "Can not apply parameter lifting on step bounded formula: The step bound has to be positive.");
  76. if (checkTask.getFormula().isUpperBoundStrict()) {
  77. STORM_LOG_THROW(*stepBound > 0, storm::exceptions::NotSupportedException, "Expected a strict upper step bound that is greater than zero.");
  78. --(*stepBound);
  79. }
  80. STORM_LOG_THROW(*stepBound > 0, storm::exceptions::NotSupportedException, "Can not apply parameter lifting on step bounded formula: The step bound has to be positive.");
  81. // get the results for the subformulas
  82. storm::modelchecker::SparsePropositionalModelChecker<SparseModelType> propositionalChecker(*this->parametricModel);
  83. STORM_LOG_THROW(propositionalChecker.canHandle(checkTask.getFormula().getLeftSubformula()) && propositionalChecker.canHandle(checkTask.getFormula().getRightSubformula()), storm::exceptions::NotSupportedException, "Parameter lifting with non-propositional subformulas is not supported");
  84. storm::storage::BitVector phiStates = std::move(propositionalChecker.check(checkTask.getFormula().getLeftSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector());
  85. storm::storage::BitVector psiStates = std::move(propositionalChecker.check(checkTask.getFormula().getRightSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector());
  86. // get the maybeStates
  87. maybeStates = storm::utility::graph::performProbGreater0(this->parametricModel->getBackwardTransitions(), phiStates, psiStates, true, *stepBound);
  88. maybeStates &= ~psiStates;
  89. // set the result for all non-maybe states
  90. resultsForNonMaybeStates = std::vector<ConstantType>(this->parametricModel->getNumberOfStates(), storm::utility::zero<ConstantType>());
  91. storm::utility::vector::setVectorValues(resultsForNonMaybeStates, psiStates, storm::utility::one<ConstantType>());
  92. // if there are maybestates, create the parameterLifter
  93. if (!maybeStates.empty()) {
  94. // Create the vector of one-step probabilities to go to target states.
  95. std::vector<ValueType> b = this->parametricModel->getTransitionMatrix().getConstrainedRowSumVector(storm::storage::BitVector(this->parametricModel->getTransitionMatrix().getRowCount(), true), psiStates);
  96. parameterLifter = std::make_unique<storm::transformer::ParameterLifter<ValueType, ConstantType>>(this->parametricModel->getTransitionMatrix(), b, maybeStates, maybeStates, false, RegionModelChecker<ValueType>::isUseMonotonicitySet());
  97. }
  98. // We know some bounds for the results so set them
  99. lowerResultBound = storm::utility::zero<ConstantType>();
  100. upperResultBound = storm::utility::one<ConstantType>();
  101. // No requirements for bounded formulas
  102. solverFactory->setRequirementsChecked(true);
  103. // For monotonicity checking
  104. std::pair<storm::storage::BitVector, storm::storage::BitVector> statesWithProbability01 = storm::utility::graph::performProb01(this->parametricModel->getBackwardTransitions(), phiStates, psiStates);
  105. this->orderExtender = storm::analysis::OrderExtender<ValueType,ConstantType>(&statesWithProbability01.second, &statesWithProbability01.first, this->parametricModel->getTransitionMatrix());
  106. }
  107. template <typename SparseModelType, typename ConstantType>
  108. void SparseDtmcParameterLiftingModelChecker<SparseModelType, ConstantType>::specifyUntilFormula(Environment const& env, CheckTask<storm::logic::UntilFormula, ConstantType> const& checkTask) {
  109. // get the results for the subformulas
  110. storm::modelchecker::SparsePropositionalModelChecker<SparseModelType> propositionalChecker(*this->parametricModel);
  111. STORM_LOG_THROW(propositionalChecker.canHandle(checkTask.getFormula().getLeftSubformula()) && propositionalChecker.canHandle(checkTask.getFormula().getRightSubformula()), storm::exceptions::NotSupportedException, "Parameter lifting with non-propositional subformulas is not supported");
  112. storm::storage::BitVector phiStates = std::move(propositionalChecker.check(checkTask.getFormula().getLeftSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector());
  113. storm::storage::BitVector psiStates = std::move(propositionalChecker.check(checkTask.getFormula().getRightSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector());
  114. // get the maybeStates
  115. std::pair<storm::storage::BitVector, storm::storage::BitVector> statesWithProbability01 = storm::utility::graph::performProb01(this->parametricModel->getBackwardTransitions(), phiStates, psiStates);
  116. maybeStates = ~(statesWithProbability01.first | statesWithProbability01.second);
  117. // set the result for all non-maybe states
  118. resultsForNonMaybeStates = std::vector<ConstantType>(this->parametricModel->getNumberOfStates(), storm::utility::zero<ConstantType>());
  119. storm::utility::vector::setVectorValues(resultsForNonMaybeStates, statesWithProbability01.second, storm::utility::one<ConstantType>());
  120. // if there are maybestates, create the parameterLifter
  121. if (!maybeStates.empty()) {
  122. // Create the vector of one-step probabilities to go to target states.
  123. std::vector<ValueType> b = this->parametricModel->getTransitionMatrix().getConstrainedRowSumVector(storm::storage::BitVector(this->parametricModel->getTransitionMatrix().getRowCount(), true), statesWithProbability01.second);
  124. parameterLifter = std::make_unique<storm::transformer::ParameterLifter<ValueType, ConstantType>>(this->parametricModel->getTransitionMatrix(), b, maybeStates, maybeStates, regionSplitEstimationsEnabled, RegionModelChecker<ValueType>::isUseMonotonicitySet());
  125. }
  126. // We know some bounds for the results so set them
  127. lowerResultBound = storm::utility::zero<ConstantType>();
  128. upperResultBound = storm::utility::one<ConstantType>();
  129. // The solution of the min-max equation system will always be unique (assuming graph-preserving instantiations, every induced DTMC has the same graph structure).
  130. auto req = solverFactory->getRequirements(env, true, true, boost::none, true);
  131. req.clearBounds();
  132. STORM_LOG_THROW(!req.hasEnabledCriticalRequirement(), storm::exceptions::UncheckedRequirementException, "Solver requirements " + req.getEnabledRequirementsAsString() + " not checked.");
  133. solverFactory->setRequirementsChecked(true);
  134. this->orderExtender = storm::analysis::OrderExtender<ValueType,ConstantType>(&statesWithProbability01.second, &statesWithProbability01.first, this->parametricModel->getTransitionMatrix());
  135. }
  136. template <typename SparseModelType, typename ConstantType>
  137. void SparseDtmcParameterLiftingModelChecker<SparseModelType, ConstantType>::specifyReachabilityRewardFormula(Environment const& env, CheckTask<storm::logic::EventuallyFormula, ConstantType> const& checkTask) {
  138. // get the results for the subformula
  139. storm::modelchecker::SparsePropositionalModelChecker<SparseModelType> propositionalChecker(*this->parametricModel);
  140. STORM_LOG_THROW(propositionalChecker.canHandle(checkTask.getFormula().getSubformula()), storm::exceptions::NotSupportedException, "Parameter lifting with non-propositional subformulas is not supported");
  141. storm::storage::BitVector targetStates = std::move(propositionalChecker.check(checkTask.getFormula().getSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector());
  142. // get the maybeStates
  143. storm::storage::BitVector infinityStates = storm::utility::graph::performProb1(this->parametricModel->getBackwardTransitions(), storm::storage::BitVector(this->parametricModel->getNumberOfStates(), true), targetStates);
  144. infinityStates.complement();
  145. maybeStates = ~(targetStates | infinityStates);
  146. // set the result for all the non-maybe states
  147. resultsForNonMaybeStates = std::vector<ConstantType>(this->parametricModel->getNumberOfStates(), storm::utility::zero<ConstantType>());
  148. storm::utility::vector::setVectorValues(resultsForNonMaybeStates, infinityStates, storm::utility::infinity<ConstantType>());
  149. // if there are maybestates, create the parameterLifter
  150. if (!maybeStates.empty()) {
  151. // Create the reward vector
  152. STORM_LOG_THROW((checkTask.isRewardModelSet() && this->parametricModel->hasRewardModel(checkTask.getRewardModel())) || (!checkTask.isRewardModelSet() && this->parametricModel->hasUniqueRewardModel()), storm::exceptions::InvalidPropertyException, "The reward model specified by the CheckTask is not available in the given model.");
  153. typename SparseModelType::RewardModelType const& rewardModel = checkTask.isRewardModelSet() ? this->parametricModel->getRewardModel(checkTask.getRewardModel()) : this->parametricModel->getUniqueRewardModel();
  154. std::vector<ValueType> b = rewardModel.getTotalRewardVector(this->parametricModel->getTransitionMatrix());
  155. parameterLifter = std::make_unique<storm::transformer::ParameterLifter<ValueType, ConstantType>>(this->parametricModel->getTransitionMatrix(), b, maybeStates, maybeStates, regionSplitEstimationsEnabled);
  156. }
  157. // We only know a lower bound for the result
  158. lowerResultBound = storm::utility::zero<ConstantType>();
  159. // The solution of the min-max equation system will always be unique (assuming graph-preserving instantiations, every induced DTMC has the same graph structure).
  160. auto req = solverFactory->getRequirements(env, true, true, boost::none, true);
  161. req.clearLowerBounds();
  162. if (req.upperBounds()) {
  163. solvingRequiresUpperRewardBounds = true;
  164. req.clearUpperBounds();
  165. }
  166. STORM_LOG_THROW(!req.hasEnabledCriticalRequirement(), storm::exceptions::UncheckedRequirementException, "Solver requirements " + req.getEnabledRequirementsAsString() + " not checked.");
  167. solverFactory->setRequirementsChecked(true);
  168. }
  169. template <typename SparseModelType, typename ConstantType>
  170. void SparseDtmcParameterLiftingModelChecker<SparseModelType, ConstantType>::specifyCumulativeRewardFormula(Environment const& env, CheckTask<storm::logic::CumulativeRewardFormula, ConstantType> const& checkTask) {
  171. // Obtain the stepBound
  172. stepBound = checkTask.getFormula().getBound().evaluateAsInt();
  173. if (checkTask.getFormula().isBoundStrict()) {
  174. STORM_LOG_THROW(*stepBound > 0, storm::exceptions::NotSupportedException, "Expected a strict upper step bound that is greater than zero.");
  175. --(*stepBound);
  176. }
  177. STORM_LOG_THROW(*stepBound > 0, storm::exceptions::NotSupportedException, "Can not apply parameter lifting on step bounded formula: The step bound has to be positive.");
  178. //Every state is a maybeState
  179. maybeStates = storm::storage::BitVector(this->parametricModel->getTransitionMatrix().getColumnCount(), true);
  180. resultsForNonMaybeStates = std::vector<ConstantType>(this->parametricModel->getNumberOfStates());
  181. // Create the reward vector
  182. STORM_LOG_THROW((checkTask.isRewardModelSet() && this->parametricModel->hasRewardModel(checkTask.getRewardModel())) || (!checkTask.isRewardModelSet() && this->parametricModel->hasUniqueRewardModel()), storm::exceptions::InvalidPropertyException, "The reward model specified by the CheckTask is not available in the given model.");
  183. typename SparseModelType::RewardModelType const& rewardModel = checkTask.isRewardModelSet() ? this->parametricModel->getRewardModel(checkTask.getRewardModel()) : this->parametricModel->getUniqueRewardModel();
  184. std::vector<ValueType> b = rewardModel.getTotalRewardVector(this->parametricModel->getTransitionMatrix());
  185. parameterLifter = std::make_unique<storm::transformer::ParameterLifter<ValueType, ConstantType>>(this->parametricModel->getTransitionMatrix(), b, maybeStates, maybeStates);
  186. // We only know a lower bound for the result
  187. lowerResultBound = storm::utility::zero<ConstantType>();
  188. // No requirements for bounded reward formula
  189. solverFactory->setRequirementsChecked(true);
  190. }
  191. template <typename SparseModelType, typename ConstantType>
  192. storm::modelchecker::SparseInstantiationModelChecker<SparseModelType, ConstantType>& SparseDtmcParameterLiftingModelChecker<SparseModelType, ConstantType>::getInstantiationCheckerSAT() {
  193. if (!instantiationCheckerSAT) {
  194. instantiationCheckerSAT = std::make_unique<storm::modelchecker::SparseDtmcInstantiationModelChecker<SparseModelType, ConstantType>>(*this->parametricModel);
  195. instantiationCheckerSAT->specifyFormula(this->currentCheckTask->template convertValueType<ValueType>());
  196. instantiationCheckerSAT->setInstantiationsAreGraphPreserving(true);
  197. }
  198. return *instantiationCheckerSAT;
  199. }
  200. template <typename SparseModelType, typename ConstantType>
  201. storm::modelchecker::SparseInstantiationModelChecker<SparseModelType, ConstantType>& SparseDtmcParameterLiftingModelChecker<SparseModelType, ConstantType>::getInstantiationCheckerVIO() {
  202. if (!instantiationCheckerVIO) {
  203. instantiationCheckerVIO = std::make_unique<storm::modelchecker::SparseDtmcInstantiationModelChecker<SparseModelType, ConstantType>>(*this->parametricModel);
  204. instantiationCheckerVIO->specifyFormula(this->currentCheckTask->template convertValueType<ValueType>());
  205. instantiationCheckerVIO->setInstantiationsAreGraphPreserving(true);
  206. }
  207. return *instantiationCheckerVIO;
  208. }
  209. template <typename SparseModelType, typename ConstantType>
  210. storm::modelchecker::SparseInstantiationModelChecker<SparseModelType, ConstantType>& SparseDtmcParameterLiftingModelChecker<SparseModelType, ConstantType>::getInstantiationChecker() {
  211. if (!instantiationChecker) {
  212. instantiationChecker = std::make_unique<storm::modelchecker::SparseDtmcInstantiationModelChecker<SparseModelType, ConstantType>>(*this->parametricModel);
  213. instantiationChecker->specifyFormula(this->currentCheckTask->template convertValueType<ValueType>());
  214. instantiationChecker->setInstantiationsAreGraphPreserving(true);
  215. }
  216. return *instantiationChecker;
  217. }
  218. template <typename SparseModelType, typename ConstantType>
  219. std::unique_ptr<CheckResult> SparseDtmcParameterLiftingModelChecker<SparseModelType, ConstantType>::computeQuantitativeValues(Environment const& env, storm::storage::ParameterRegion<ValueType> const& region, storm::solver::OptimizationDirection const& dirForParameters, std::shared_ptr<storm::analysis::LocalMonotonicityResult<VariableType>> localMonotonicityResult) {
  220. if (maybeStates.empty()) {
  221. return std::make_unique<storm::modelchecker::ExplicitQuantitativeCheckResult<ConstantType>>(resultsForNonMaybeStates);
  222. }
  223. parameterLifter->specifyRegion(region, dirForParameters);
  224. if (stepBound) {
  225. assert(*stepBound > 0);
  226. x = std::vector<ConstantType>(maybeStates.getNumberOfSetBits(), storm::utility::zero<ConstantType>());
  227. auto multiplier = storm::solver::MultiplierFactory<ConstantType>().create(env, parameterLifter->getMatrix());
  228. multiplier->repeatedMultiplyAndReduce(env, dirForParameters, x, &parameterLifter->getVector(), *stepBound);
  229. } else {
  230. auto solver = solverFactory->create(env, parameterLifter->getMatrix());
  231. solver->setHasUniqueSolution();
  232. solver->setHasNoEndComponents();
  233. if (lowerResultBound) solver->setLowerBound(lowerResultBound.get());
  234. if (upperResultBound) {
  235. solver->setUpperBound(upperResultBound.get());
  236. } else if (solvingRequiresUpperRewardBounds) {
  237. // For the min-case, we use DS-MPI, for the max-case variant 2 of the Baier et al. paper (CAV'17).
  238. std::vector<ConstantType> oneStepProbs;
  239. oneStepProbs.reserve(parameterLifter->getMatrix().getRowCount());
  240. for (uint64_t row = 0; row < parameterLifter->getMatrix().getRowCount(); ++row) {
  241. oneStepProbs.push_back(
  242. storm::utility::one<ConstantType>() - parameterLifter->getMatrix().getRowSum(row));
  243. }
  244. if (dirForParameters == storm::OptimizationDirection::Minimize) {
  245. storm::modelchecker::helper::DsMpiMdpUpperRewardBoundsComputer<ConstantType> dsmpi(
  246. parameterLifter->getMatrix(), parameterLifter->getVector(), oneStepProbs);
  247. solver->setUpperBounds(dsmpi.computeUpperBounds());
  248. } else {
  249. storm::modelchecker::helper::BaierUpperRewardBoundsComputer<ConstantType> baier(
  250. parameterLifter->getMatrix(), parameterLifter->getVector(), oneStepProbs);
  251. solver->setUpperBound(baier.computeUpperBound());
  252. }
  253. }
  254. solver->setTrackScheduler(true);
  255. if (localMonotonicityResult != nullptr && !this->isOnlyGlobalSet()) {
  256. storm::storage::BitVector choiceFixedForStates(parameterLifter->getRowGroupCount(), false);
  257. bool useMinimize = storm::solver::minimize(dirForParameters);
  258. if (useMinimize && !minSchedChoices) {
  259. minSchedChoices = std::vector<uint_fast64_t>(parameterLifter->getRowGroupCount(), 0);
  260. }
  261. if (!useMinimize && !maxSchedChoices) {
  262. maxSchedChoices = std::vector<uint_fast64_t>(parameterLifter->getRowGroupCount(), 0);
  263. }
  264. // TODO: this only works since we decided to keep all columns
  265. auto const & occuringVariables = parameterLifter->getOccurringVariablesAtState();
  266. for (uint_fast64_t state = 0; state < parameterLifter->getRowGroupCount(); ++state) {
  267. auto oldStateNumber = parameterLifter->getOriginalStateNumber(state);
  268. auto& variables = occuringVariables.at(oldStateNumber);
  269. // point at which we start with rows for this state
  270. STORM_LOG_THROW(variables.size() <= 1, storm::exceptions::NotImplementedException, "Using localMonRes not yet implemented for states with 2 or more variables, please run without --use-monotonicity");
  271. bool allMonotone = true;
  272. for (auto var : variables) {
  273. auto monotonicity = localMonotonicityResult->getMonotonicity(oldStateNumber, var);
  274. bool ignoreUpperBound = monotonicity == Monotonicity::Constant || (useMinimize && monotonicity == Monotonicity::Incr) || (!useMinimize && monotonicity == Monotonicity::Decr);
  275. bool ignoreLowerBound = !ignoreUpperBound && ((useMinimize && monotonicity == Monotonicity::Decr) || (!useMinimize && monotonicity == Monotonicity::Incr));
  276. allMonotone &= (ignoreUpperBound || ignoreLowerBound);
  277. if (ignoreLowerBound) {
  278. if (useMinimize) {
  279. minSchedChoices.get()[state] = 1;
  280. } else {
  281. maxSchedChoices.get()[state] = 1;
  282. }
  283. } else if (ignoreUpperBound) {
  284. if (useMinimize) {
  285. minSchedChoices.get()[state] = 0;
  286. } else {
  287. maxSchedChoices.get()[state] = 0;
  288. }
  289. }
  290. }
  291. if (allMonotone) {
  292. choiceFixedForStates.set(state);
  293. }
  294. }
  295. solver->setChoiceFixedForStates(std::move(choiceFixedForStates));
  296. }
  297. if (storm::solver::minimize(dirForParameters) && minSchedChoices)
  298. solver->setInitialScheduler(std::move(minSchedChoices.get()));
  299. if (storm::solver::maximize(dirForParameters) && maxSchedChoices)
  300. solver->setInitialScheduler(std::move(maxSchedChoices.get()));
  301. if (this->currentCheckTask->isBoundSet() && solver->hasInitialScheduler()) {
  302. // If we reach this point, we know that after applying the hint, the x-values can only become larger (if we maximize) or smaller (if we minimize).
  303. std::unique_ptr<storm::solver::TerminationCondition<ConstantType>> termCond;
  304. storm::storage::BitVector relevantStatesInSubsystem = this->currentCheckTask->isOnlyInitialStatesRelevantSet()
  305. ? this->parametricModel->getInitialStates() %
  306. maybeStates : storm::storage::BitVector(
  307. maybeStates.getNumberOfSetBits(), true);
  308. if (storm::solver::minimize(dirForParameters)) {
  309. // Terminate if the value for ALL relevant states is already below the threshold
  310. termCond = std::make_unique<storm::solver::TerminateIfFilteredExtremumBelowThreshold<ConstantType>>(
  311. relevantStatesInSubsystem, true, this->currentCheckTask->getBoundThreshold(), false);
  312. } else {
  313. // Terminate if the value for ALL relevant states is already above the threshold
  314. termCond = std::make_unique<storm::solver::TerminateIfFilteredExtremumExceedsThreshold<ConstantType>>(
  315. relevantStatesInSubsystem, true, this->currentCheckTask->getBoundThreshold(), true);
  316. }
  317. solver->setTerminationCondition(std::move(termCond));
  318. }
  319. // Invoke the solver
  320. x.resize(maybeStates.getNumberOfSetBits(), storm::utility::zero<ConstantType>());
  321. solver->solveEquations(env, dirForParameters, x, parameterLifter->getVector());
  322. if (storm::solver::minimize(dirForParameters)) {
  323. minSchedChoices = solver->getSchedulerChoices();
  324. } else {
  325. maxSchedChoices = solver->getSchedulerChoices();
  326. }
  327. if (isRegionSplitEstimateSupported()) {
  328. computeRegionSplitEstimates(x, solver->getSchedulerChoices(), region, dirForParameters);
  329. }
  330. }
  331. // Get the result for the complete model (including maybestates)
  332. std::vector<ConstantType> result = resultsForNonMaybeStates;
  333. auto maybeStateResIt = x.begin();
  334. for(auto const& maybeState : maybeStates) {
  335. result[maybeState] = *maybeStateResIt;
  336. ++maybeStateResIt;
  337. }
  338. return std::make_unique<storm::modelchecker::ExplicitQuantitativeCheckResult<ConstantType>>(std::move(result));
  339. }
  340. template <typename SparseModelType, typename ConstantType>
  341. void SparseDtmcParameterLiftingModelChecker<SparseModelType, ConstantType>::computeRegionSplitEstimates(std::vector<ConstantType> const& quantitativeResult, std::vector<uint_fast64_t> const& schedulerChoices, storm::storage::ParameterRegion<ValueType> const& region, storm::solver::OptimizationDirection const& dirForParameters) {
  342. std::map<VariableType, double> deltaLower, deltaUpper;
  343. for (auto const& p : region.getVariables()) {
  344. deltaLower.insert(std::make_pair(p, 0.0));
  345. deltaUpper.insert(std::make_pair(p, 0.0));
  346. }
  347. auto const& choiceValuations = parameterLifter->getRowLabels();
  348. auto const& matrix = parameterLifter->getMatrix();
  349. auto const& vector = parameterLifter->getVector();
  350. std::vector<ConstantType> stateResults;
  351. for (uint64_t state = 0; state < schedulerChoices.size(); ++state) {
  352. uint64_t rowOffset = matrix.getRowGroupIndices()[state];
  353. uint64_t optimalChoice = schedulerChoices[state];
  354. auto const& optimalChoiceVal = choiceValuations[rowOffset + optimalChoice];
  355. assert(optimalChoiceVal.getUnspecifiedParameters().empty());
  356. stateResults.clear();
  357. for (uint64_t row = rowOffset; row < matrix.getRowGroupIndices()[state + 1]; ++row) {
  358. stateResults.push_back(matrix.multiplyRowWithVector(row, quantitativeResult) + vector[row]);
  359. }
  360. // Do this twice, once for upperbound once for lowerbound
  361. bool checkUpperParameters = false;
  362. do {
  363. auto const& consideredParameters = checkUpperParameters ? optimalChoiceVal.getUpperParameters() : optimalChoiceVal.getLowerParameters();
  364. for (auto const& p : consideredParameters) {
  365. // Find the 'best' choice that assigns the parameter to the other bound
  366. ConstantType bestValue = 0;
  367. bool foundBestValue = false;
  368. for (uint64_t choice = 0; choice < stateResults.size(); ++choice) {
  369. if (choice != optimalChoice) {
  370. auto const& otherBoundParsOfChoice = checkUpperParameters ? choiceValuations[rowOffset + choice].getLowerParameters() : choiceValuations[rowOffset + choice].getUpperParameters();
  371. if (otherBoundParsOfChoice.find(p) != otherBoundParsOfChoice.end()) {
  372. ConstantType const& choiceValue = stateResults[choice];
  373. if (!foundBestValue || (storm::solver::minimize(dirForParameters) ? choiceValue < bestValue : choiceValue > bestValue)) {
  374. foundBestValue = true;
  375. bestValue = choiceValue;
  376. }
  377. }
  378. }
  379. }
  380. auto optimal = storm::utility::convertNumber<double>(stateResults[optimalChoice]);
  381. auto diff = optimal - storm::utility::convertNumber<double>(bestValue);
  382. if (foundBestValue) {
  383. if (checkUpperParameters) {
  384. deltaLower[p] += std::abs(diff);
  385. } else {
  386. deltaUpper[p] += std::abs(diff);
  387. }
  388. }
  389. }
  390. checkUpperParameters = !checkUpperParameters;
  391. } while (checkUpperParameters);
  392. }
  393. regionSplitEstimates.clear();
  394. useRegionSplitEstimates = false;
  395. for (auto const& p : region.getVariables()) {
  396. if (this->possibleMonotoneParameters.find(p) != this->possibleMonotoneParameters.end()) {
  397. if (deltaLower[p] > deltaUpper[p] && deltaUpper[p] >= 0.0001) {
  398. regionSplitEstimates.insert(std::make_pair(p, deltaUpper[p]));
  399. useRegionSplitEstimates = true;
  400. } else if (deltaLower[p] <= deltaUpper[p] && deltaLower[p] >= 0.0001) {
  401. {
  402. regionSplitEstimates.insert(std::make_pair(p, deltaLower[p]));
  403. useRegionSplitEstimates = true;
  404. }
  405. }
  406. }
  407. }
  408. // large regionsplitestimate implies that parameter p occurs as p and 1-p at least once
  409. }
  410. template <typename SparseModelType, typename ConstantType>
  411. void SparseDtmcParameterLiftingModelChecker<SparseModelType, ConstantType>::reset() {
  412. maybeStates.resize(0);
  413. resultsForNonMaybeStates.clear();
  414. stepBound = boost::none;
  415. instantiationChecker = nullptr;
  416. parameterLifter = nullptr;
  417. minSchedChoices = boost::none;
  418. maxSchedChoices = boost::none;
  419. x.clear();
  420. lowerResultBound = boost::none;
  421. upperResultBound = boost::none;
  422. regionSplitEstimationsEnabled = false;
  423. }
  424. template <typename SparseModelType, typename ConstantType>
  425. boost::optional<storm::storage::Scheduler<ConstantType>> SparseDtmcParameterLiftingModelChecker<SparseModelType, ConstantType>::getCurrentMinScheduler() {
  426. if (!minSchedChoices) {
  427. return boost::none;
  428. }
  429. storm::storage::Scheduler<ConstantType> result(minSchedChoices->size());
  430. uint_fast64_t state = 0;
  431. for (auto const& schedulerChoice : minSchedChoices.get()) {
  432. result.setChoice(schedulerChoice, state);
  433. ++state;
  434. }
  435. return result;
  436. }
  437. template <typename SparseModelType, typename ConstantType>
  438. boost::optional<storm::storage::Scheduler<ConstantType>> SparseDtmcParameterLiftingModelChecker<SparseModelType, ConstantType>::getCurrentMaxScheduler() {
  439. if (!maxSchedChoices) {
  440. return boost::none;
  441. }
  442. storm::storage::Scheduler<ConstantType> result(maxSchedChoices->size());
  443. uint_fast64_t state = 0;
  444. for (auto const& schedulerChoice : maxSchedChoices.get()) {
  445. result.setChoice(schedulerChoice, state);
  446. ++state;
  447. }
  448. return result;
  449. }
  450. template <typename SparseModelType, typename ConstantType>
  451. bool SparseDtmcParameterLiftingModelChecker<SparseModelType, ConstantType>::isRegionSplitEstimateSupported() const {
  452. return regionSplitEstimationsEnabled && !stepBound;
  453. }
  454. template <typename SparseModelType, typename ConstantType>
  455. std::map<typename RegionModelChecker<typename SparseModelType::ValueType>::VariableType, double> SparseDtmcParameterLiftingModelChecker<SparseModelType, ConstantType>::getRegionSplitEstimate() const {
  456. STORM_LOG_THROW(isRegionSplitEstimateSupported(), storm::exceptions::InvalidOperationException, "Region split estimation requested but are not enabled (or supported).");
  457. return regionSplitEstimates;
  458. }
  459. template<typename SparseModelType, typename ConstantType>
  460. std::shared_ptr<storm::analysis::Order> SparseDtmcParameterLiftingModelChecker<SparseModelType, ConstantType>::extendOrder(Environment const& env, std::shared_ptr<storm::analysis::Order> order, storm::storage::ParameterRegion<ValueType> region) {
  461. if (this->orderExtender) {
  462. auto res = this->orderExtender->extendOrder(order, region);
  463. order = std::get<0>(res);
  464. if (std::get<1>(res) != order->getNumberOfStates()) {
  465. this->orderExtender.get().setUnknownStates(order, std::get<1>(res), std::get<2>(res));
  466. }
  467. } else {
  468. STORM_LOG_WARN("Extending order for RegionModelChecker not implemented");
  469. }
  470. return order;
  471. }
  472. template <typename SparseModelType, typename ConstantType>
  473. void SparseDtmcParameterLiftingModelChecker<SparseModelType, ConstantType>::extendLocalMonotonicityResult(storm::storage::ParameterRegion<ValueType> const& region, std::shared_ptr<storm::analysis::Order> order, std::shared_ptr<storm::analysis::LocalMonotonicityResult<VariableType>> localMonotonicityResult) {
  474. if (this->monotoneIncrParameters && !localMonotonicityResult->isFixedParametersSet()) {
  475. for (auto & var : this->monotoneIncrParameters.get()) {
  476. localMonotonicityResult->setMonotoneIncreasing(var);
  477. }
  478. for (auto & var : this->monotoneDecrParameters.get()) {
  479. localMonotonicityResult->setMonotoneDecreasing(var);
  480. }
  481. }
  482. auto state = order->getNextDoneState(-1);
  483. auto const variablesAtState = parameterLifter->getOccurringVariablesAtState();
  484. while (state != order->getNumberOfStates()) {
  485. if (localMonotonicityResult->getMonotonicity(state) == nullptr) {
  486. auto variables = variablesAtState[state];
  487. if (variables.size() == 0 || order->isBottomState(state) || order->isTopState(state)) {
  488. localMonotonicityResult->setConstant(state);
  489. } else {
  490. for (auto const &var : variables) {
  491. auto monotonicity = localMonotonicityResult->getMonotonicity(state, var);
  492. if (monotonicity == Monotonicity::Unknown || monotonicity == Monotonicity::Not) {
  493. monotonicity = monotonicityChecker->checkLocalMonotonicity(order, state, var, region);
  494. if (monotonicity == Monotonicity::Unknown || monotonicity == Monotonicity::Not) {
  495. // TODO: Skip for now?
  496. } else {
  497. localMonotonicityResult->setMonotonicity(state, var, monotonicity);
  498. }
  499. }
  500. }
  501. }
  502. } else {
  503. // Do nothing, we already checked this one
  504. }
  505. state = order->getNextDoneState(state);
  506. }
  507. auto const statesAtVariable = parameterLifter->getOccuringStatesAtVariable();
  508. bool allDone = true;
  509. for (auto const & entry : statesAtVariable) {
  510. auto states = entry.second;
  511. auto var = entry.first;
  512. bool done = true;
  513. for (auto const& state : states) {
  514. done &= order->contains(state) && localMonotonicityResult->getMonotonicity(state, var) != Monotonicity::Unknown;
  515. if (!done) {
  516. break;
  517. }
  518. }
  519. allDone &= done;
  520. if (done) {
  521. localMonotonicityResult->getGlobalMonotonicityResult()->setDoneForVar(var);
  522. }
  523. }
  524. if (allDone) {
  525. localMonotonicityResult->setDone();
  526. while (order->existsNextState()) {
  527. // Simply add the states we couldn't add sofar between =) and =( as we could find local monotonicity for all parametric states
  528. order->add(order->getNextStateNumber().second);
  529. }
  530. assert (order->getDoneBuilding());
  531. }
  532. }
  533. template <typename SparseModelType, typename ConstantType>
  534. void SparseDtmcParameterLiftingModelChecker<SparseModelType, ConstantType>::splitSmart (
  535. storm::storage::ParameterRegion<ValueType> &region, std::vector<storm::storage::ParameterRegion<ValueType>> &regionVector,
  536. std::shared_ptr<storm::analysis::Order> order, storm::analysis::MonotonicityResult<VariableType> & monRes, bool splitForExtremum) const {
  537. assert (regionVector.size() == 0);
  538. std::multimap<double, VariableType> sortedOnValues;
  539. std::set<VariableType> consideredVariables;
  540. if (splitForExtremum) {
  541. if (regionSplitEstimationsEnabled && useRegionSplitEstimates) {
  542. STORM_LOG_INFO("Splitting based on region split estimates");
  543. for (auto &entry : regionSplitEstimates) {
  544. assert (!this->isUseMonotonicitySet() || (!monRes.isMonotone(entry.first) && this->possibleMonotoneParameters.find(entry.first) != this->possibleMonotoneParameters.end()));
  545. // sortedOnValues.insert({-(entry.second * storm::utility::convertNumber<double>(region.getDifference(entry.first))* storm::utility::convertNumber<double>(region.getDifference(entry.first))), entry.first});
  546. sortedOnValues.insert({-(entry.second ), entry.first});
  547. }
  548. for (auto itr = sortedOnValues.begin(); itr != sortedOnValues.end() && consideredVariables.size() < region.getSplitThreshold(); ++itr) {
  549. consideredVariables.insert(itr->second);
  550. }
  551. assert (consideredVariables.size() > 0);
  552. region.split(region.getCenterPoint(), regionVector, std::move(consideredVariables));
  553. } else {
  554. STORM_LOG_INFO("Splitting based on sorting");
  555. auto &sortedOnDifference = region.getVariablesSorted();
  556. for (auto itr = sortedOnDifference.begin(); itr != sortedOnDifference.end() && consideredVariables.size() < region.getSplitThreshold(); ++itr) {
  557. if (!this->isUseMonotonicitySet() || !monRes.isMonotone(itr->second)) {
  558. consideredVariables.insert(itr->second);
  559. }
  560. }
  561. assert (consideredVariables.size() > 0 || (monRes.isDone() && monRes.isAllMonotonicity()));
  562. region.split(region.getCenterPoint(), regionVector, std::move(consideredVariables));
  563. }
  564. } else {
  565. // split for pla
  566. if (regionSplitEstimationsEnabled && useRegionSplitEstimates) {
  567. STORM_LOG_INFO("Splitting based on region split estimates");
  568. ConstantType diff = this->lastValue - (this->currentCheckTask->getFormula().asOperatorFormula().template getThresholdAs<ConstantType>());
  569. for (auto &entry : regionSplitEstimates) {
  570. if ((!this->isUseMonotonicitySet() || !monRes.isMonotone(entry.first)) && storm::utility::convertNumber<ConstantType>(entry.second) > diff) {
  571. sortedOnValues.insert({-(entry.second * storm::utility::convertNumber<double>(region.getDifference(entry.first)) * storm::utility::convertNumber<double>(region.getDifference(entry.first))), entry.first});
  572. }
  573. }
  574. for (auto itr = sortedOnValues.begin(); itr != sortedOnValues.end() && consideredVariables.size() < region.getSplitThreshold(); ++itr) {
  575. consideredVariables.insert(itr->second);
  576. }
  577. }
  578. if (consideredVariables.size() == 0) {
  579. auto &sortedOnDifference = region.getVariablesSorted();
  580. for (auto itr = sortedOnDifference.begin(); itr != sortedOnDifference.end() && consideredVariables.size() < region.getSplitThreshold(); ++itr) {
  581. consideredVariables.insert(itr->second);
  582. }
  583. }
  584. assert (consideredVariables.size() > 0);
  585. region.split(region.getCenterPoint(), regionVector, std::move(consideredVariables));
  586. }
  587. }
  588. template class SparseDtmcParameterLiftingModelChecker<storm::models::sparse::Dtmc<storm::RationalFunction>, double>;
  589. template class SparseDtmcParameterLiftingModelChecker<storm::models::sparse::Dtmc<storm::RationalFunction>, storm::RationalNumber>;
  590. }
  591. }