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.

599 lines
46 KiB

  1. /*
  2. * SparseMdpPrctlModelChecker.h
  3. *
  4. * Created on: 15.02.2013
  5. * Author: Christian Dehnert
  6. */
  7. #ifndef STORM_MODELCHECKER_PRCTL_SPARSEMDPPRCTLMODELCHECKER_H_
  8. #define STORM_MODELCHECKER_PRCTL_SPARSEMDPPRCTLMODELCHECKER_H_
  9. #include <vector>
  10. #include <stack>
  11. #include <fstream>
  12. #include "src/modelchecker/prctl/AbstractModelChecker.h"
  13. #include "src/solver/AbstractNondeterministicLinearEquationSolver.h"
  14. #include "src/solver/GmmxxLinearEquationSolver.h"
  15. #include "src/models/Mdp.h"
  16. #include "src/utility/vector.h"
  17. #include "src/utility/graph.h"
  18. #include "src/utility/solver.h"
  19. #include "src/settings/Settings.h"
  20. #include "src/storage/TotalScheduler.h"
  21. namespace storm {
  22. namespace modelchecker {
  23. namespace prctl {
  24. /*!
  25. * This class represents the base class for all PRCTL model checkers for MDPs.
  26. */
  27. template<class Type>
  28. class SparseMdpPrctlModelChecker : public AbstractModelChecker<Type> {
  29. public:
  30. /*!
  31. * Constructs a SparseMdpPrctlModelChecker with the given model.
  32. *
  33. * @param model The MDP to be checked.
  34. */
  35. explicit SparseMdpPrctlModelChecker(storm::models::Mdp<Type> const& model) : AbstractModelChecker<Type>(model), minimumOperatorStack(), nondeterministicLinearEquationSolver(storm::utility::solver::getNondeterministicLinearEquationSolver<Type>()) {
  36. // Intentionally left empty.
  37. }
  38. explicit SparseMdpPrctlModelChecker(storm::models::Mdp<Type> const& model, std::shared_ptr<storm::solver::AbstractNondeterministicLinearEquationSolver<Type>> nondeterministicLinearEquationSolver) : AbstractModelChecker<Type>(model), minimumOperatorStack(), nondeterministicLinearEquationSolver(nondeterministicLinearEquationSolver) {
  39. // Intentionally left empty.
  40. }
  41. /*!
  42. * Copy constructs a SparseMdpPrctlModelChecker from the given model checker. In particular, this means that the newly
  43. * constructed model checker will have the model of the given model checker as its associated model.
  44. */
  45. explicit SparseMdpPrctlModelChecker(storm::modelchecker::prctl::SparseMdpPrctlModelChecker<Type> const& modelchecker)
  46. : AbstractModelChecker<Type>(modelchecker), minimumOperatorStack(), nondeterministicLinearEquationSolver(storm::utility::solver::getNondeterministicLinearEquationSolver<Type>()) {
  47. // Intentionally left empty.
  48. }
  49. /*!
  50. * Returns a constant reference to the MDP associated with this model checker.
  51. * @returns A constant reference to the MDP associated with this model checker.
  52. */
  53. storm::models::Mdp<Type> const& getModel() const {
  54. return AbstractModelChecker<Type>::template getModel<storm::models::Mdp<Type>>();
  55. }
  56. /*!
  57. * Checks the given formula that is a P/R operator without a bound.
  58. *
  59. * @param formula The formula to check.
  60. * @returns The set of states satisfying the formula represented by a bit vector.
  61. */
  62. virtual std::vector<Type> checkNoBoundOperator(const storm::property::prctl::AbstractNoBoundOperator<Type>& formula) const {
  63. // Check if the operator was an non-optimality operator and report an error in that case.
  64. if (!formula.isOptimalityOperator()) {
  65. LOG4CPLUS_ERROR(logger, "Formula does not specify neither min nor max optimality, which is not meaningful over nondeterministic models.");
  66. throw storm::exceptions::InvalidArgumentException() << "Formula does not specify neither min nor max optimality, which is not meaningful over nondeterministic models.";
  67. }
  68. minimumOperatorStack.push(formula.isMinimumOperator());
  69. std::vector<Type> result = formula.check(*this, false);
  70. minimumOperatorStack.pop();
  71. return result;
  72. }
  73. /*!
  74. * Computes the probability to satisfy phi until psi within a limited number of steps for each state.
  75. *
  76. * @param phiStates A bit vector indicating which states satisfy phi.
  77. * @param psiStates A bit vector indicating which states satisfy psi.
  78. * @param stepBound The upper bound for the number of steps.
  79. * @param qualitative A flag indicating whether the check only needs to be done qualitatively, i.e. if the
  80. * results are only compared against the bounds 0 and 1. If set to true, this will most likely results that are only
  81. * qualitatively correct, i.e. do not represent the correct value, but only the correct relation with respect to the
  82. * bounds 0 and 1.
  83. * @return The probabilities for satisfying phi until psi within a limited number of steps for each state.
  84. * If the qualitative flag is set, exact probabilities might not be computed.
  85. */
  86. std::vector<Type> checkBoundedUntil(storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates, uint_fast64_t stepBound, bool qualitative) const {
  87. std::vector<Type> result(this->getModel().getNumberOfStates());
  88. // Determine the states that have 0 probability of reaching the target states.
  89. storm::storage::BitVector statesWithProbabilityGreater0;
  90. if (this->minimumOperatorStack.top()) {
  91. statesWithProbabilityGreater0 = storm::utility::graph::performProbGreater0A(this->getModel().getTransitionMatrix(), this->getModel().getNondeterministicChoiceIndices(), this->getModel().getBackwardTransitions(), phiStates, psiStates, true, stepBound);
  92. } else {
  93. statesWithProbabilityGreater0 = storm::utility::graph::performProbGreater0E(this->getModel().getTransitionMatrix(), this->getModel().getNondeterministicChoiceIndices(), this->getModel().getBackwardTransitions(), phiStates, psiStates, true, stepBound);
  94. }
  95. // Check if we already know the result (i.e. probability 0) for all initial states and
  96. // don't compute anything in this case.
  97. if (this->getModel().getInitialStates().isDisjointFrom(statesWithProbabilityGreater0)) {
  98. LOG4CPLUS_INFO(logger, "The probabilities for the initial states were determined in a preprocessing step."
  99. << " No exact probabilities were computed.");
  100. // Set the values for all maybe-states to 0.5 to indicate that their probability values are not 0 (and
  101. // not necessarily 1).
  102. storm::utility::vector::setVectorValues(result, statesWithProbabilityGreater0, Type(0.5));
  103. } else {
  104. // In this case we have have to compute the probabilities.
  105. // We can eliminate the rows and columns from the original transition probability matrix that have probability 0.
  106. storm::storage::SparseMatrix<Type> submatrix = this->getModel().getTransitionMatrix().getSubmatrix(statesWithProbabilityGreater0, this->getModel().getNondeterministicChoiceIndices());
  107. // Get the "new" nondeterministic choice indices for the submatrix.
  108. std::vector<uint_fast64_t> subNondeterministicChoiceIndices = storm::utility::vector::getConstrainedOffsetVector(this->getModel().getNondeterministicChoiceIndices(), statesWithProbabilityGreater0);
  109. // Compute the new set of target states in the reduced system.
  110. storm::storage::BitVector rightStatesInReducedSystem = statesWithProbabilityGreater0 % psiStates;
  111. // Make all rows absorbing that satisfy the second sub-formula.
  112. submatrix.makeRowsAbsorbing(rightStatesInReducedSystem, subNondeterministicChoiceIndices);
  113. // Create the vector with which to multiply.
  114. std::vector<Type> subresult(statesWithProbabilityGreater0.getNumberOfSetBits());
  115. storm::utility::vector::setVectorValues(subresult, rightStatesInReducedSystem, storm::utility::constGetOne<Type>());
  116. this->nondeterministicLinearEquationSolver->performMatrixVectorMultiplication(this->minimumOperatorStack.top(), submatrix, subresult, subNondeterministicChoiceIndices, nullptr, stepBound);
  117. // Set the values of the resulting vector accordingly.
  118. storm::utility::vector::setVectorValues(result, statesWithProbabilityGreater0, subresult);
  119. storm::utility::vector::setVectorValues(result, ~statesWithProbabilityGreater0, storm::utility::constGetZero<Type>());
  120. }
  121. return result;
  122. }
  123. /*!
  124. * Checks the given formula that is a bounded-until formula.
  125. *
  126. * @param formula The formula to check.
  127. * @param qualitative A flag indicating whether the formula only needs to be evaluated qualitatively, i.e. if the
  128. * results are only compared against the bounds 0 and 1. If set to true, this will most likely results that are only
  129. * qualitatively correct, i.e. do not represent the correct value, but only the correct relation with respect to the
  130. * bounds 0 and 1.
  131. * @return The probabilities for the given formula to hold on every state of the model associated with this model
  132. * checker. If the qualitative flag is set, exact probabilities might not be computed.
  133. */
  134. virtual std::vector<Type> checkBoundedUntil(storm::property::prctl::BoundedUntil<Type> const& formula, bool qualitative) const {
  135. return checkBoundedUntil(formula.getLeft().check(*this), formula.getRight().check(*this), formula.getBound(), qualitative);
  136. }
  137. /*!
  138. * Computes the probability to reach the given set of states in the next step for each state.
  139. *
  140. * @param nextStates A bit vector defining the states to reach in the next state.
  141. * @param qualitative A flag indicating whether the formula only needs to be evaluated qualitatively, i.e. if the
  142. * results are only compared against the bounds 0 and 1. If set to true, this will most likely results that are only
  143. * qualitatively correct, i.e. do not represent the correct value, but only the correct relation with respect to the
  144. * bounds 0 and 1.
  145. * @return The probabilities to reach the gien set of states in the next step for each state. If the
  146. * qualitative flag is set, exact probabilities might not be computed.
  147. */
  148. virtual std::vector<Type> checkNext(storm::storage::BitVector const& nextStates, bool qualitative) const {
  149. // Create the vector with which to multiply and initialize it correctly.
  150. std::vector<Type> result(this->getModel().getNumberOfStates());
  151. storm::utility::vector::setVectorValues(result, nextStates, storm::utility::constGetOne<Type>());
  152. this->nondeterministicLinearEquationSolver->performMatrixVectorMultiplication(this->minimumOperatorStack.top(), this->getModel().getTransitionMatrix(), result, this->getModel().getNondeterministicChoiceIndices());
  153. return result;
  154. }
  155. /*!
  156. * Checks the given formula that is a next formula.
  157. *
  158. * @param formula The formula to check.
  159. * @param qualitative A flag indicating whether the formula only needs to be evaluated qualitatively, i.e. if the
  160. * results are only compared against the bounds 0 and 1. If set to true, this will most likely results that are only
  161. * qualitatively correct, i.e. do not represent the correct value, but only the correct relation with respect to the
  162. * bounds 0 and 1.
  163. * @return The probabilities for the given formula to hold on every state of the model associated with this model
  164. * checker. If the qualitative flag is set, exact probabilities might not be computed.
  165. */
  166. virtual std::vector<Type> checkNext(const storm::property::prctl::Next<Type>& formula, bool qualitative) const {
  167. return checkNext(formula.getChild().check(*this), qualitative);
  168. }
  169. /*!
  170. * Checks the given formula that is a bounded-eventually formula.
  171. *
  172. * @param formula The formula to check.
  173. * @param qualitative A flag indicating whether the formula only needs to be evaluated qualitatively, i.e. if the
  174. * results are only compared against the bounds 0 and 1. If set to true, this will most likely results that are only
  175. * qualitatively correct, i.e. do not represent the correct value, but only the correct relation with respect to the
  176. * bounds 0 and 1.
  177. * @returns The probabilities for the given formula to hold on every state of the model associated with this model
  178. * checker. If the qualitative flag is set, exact probabilities might not be computed.
  179. */
  180. virtual std::vector<Type> checkBoundedEventually(const storm::property::prctl::BoundedEventually<Type>& formula, bool qualitative) const {
  181. // Create equivalent temporary bounded until formula and check it.
  182. storm::property::prctl::BoundedUntil<Type> temporaryBoundedUntilFormula(new storm::property::prctl::Ap<Type>("true"), formula.getChild().clone(), formula.getBound());
  183. return this->checkBoundedUntil(temporaryBoundedUntilFormula, qualitative);
  184. }
  185. /*!
  186. * Checks the given formula that is an eventually formula.
  187. *
  188. * @param formula The formula to check.
  189. * @param qualitative A flag indicating whether the formula only needs to be evaluated qualitatively, i.e. if the
  190. * results are only compared against the bounds 0 and 1. If set to true, this will most likely results that are only
  191. * qualitatively correct, i.e. do not represent the correct value, but only the correct relation with respect to the
  192. * bounds 0 and 1.
  193. * @returns The probabilities for the given formula to hold on every state of the model associated with this model
  194. * checker. If the qualitative flag is set, exact probabilities might not be computed.
  195. */
  196. virtual std::vector<Type> checkEventually(const storm::property::prctl::Eventually<Type>& formula, bool qualitative) const {
  197. // Create equivalent temporary until formula and check it.
  198. storm::property::prctl::Until<Type> temporaryUntilFormula(new storm::property::prctl::Ap<Type>("true"), formula.getChild().clone());
  199. return this->checkUntil(temporaryUntilFormula, qualitative);
  200. }
  201. /*!
  202. * Checks the given formula that is a globally formula.
  203. *
  204. * @param formula The formula to check.
  205. * @param qualitative A flag indicating whether the formula only needs to be evaluated qualitatively, i.e. if the
  206. * results are only compared against the bounds 0 and 1. If set to true, this will most likely results that are only
  207. * qualitatively correct, i.e. do not represent the correct value, but only the correct relation with respect to the
  208. * bounds 0 and 1.
  209. * @returns The probabilities for the given formula to hold on every state of the model associated with this model
  210. * checker. If the qualitative flag is set, exact probabilities might not be computed.
  211. */
  212. virtual std::vector<Type> checkGlobally(const storm::property::prctl::Globally<Type>& formula, bool qualitative) const {
  213. // Create "equivalent" temporary eventually formula and check it.
  214. storm::property::prctl::Eventually<Type> temporaryEventuallyFormula(new storm::property::prctl::Not<Type>(formula.getChild().clone()));
  215. std::vector<Type> result = this->checkEventually(temporaryEventuallyFormula, qualitative);
  216. // Now subtract the resulting vector from the constant one vector to obtain final result.
  217. storm::utility::vector::subtractFromConstantOneVector(result);
  218. return result;
  219. }
  220. /*!
  221. * Check the given formula that is an until formula.
  222. *
  223. * @param formula The formula to check.
  224. * @param qualitative A flag indicating whether the formula only needs to be evaluated qualitatively, i.e. if the
  225. * results are only compared against the bounds 0 and 1. If set to true, this will most likely results that are only
  226. * qualitatively correct, i.e. do not represent the correct value, but only the correct relation with respect to the
  227. * bounds 0 and 1.
  228. * @param scheduler If <code>qualitative</code> is false and this vector is non-null and has as many elements as
  229. * there are states in the MDP, this vector will represent a scheduler for the model that achieves the probability
  230. * returned by model checking. To this end, the vector will hold the nondeterministic choice made for each state.
  231. * @return The probabilities for the given formula to hold on every state of the model associated with this model
  232. * checker. If the qualitative flag is set, exact probabilities might not be computed.
  233. */
  234. virtual std::vector<Type> checkUntil(const storm::property::prctl::Until<Type>& formula, bool qualitative) const {
  235. return this->checkUntil(this->minimumOperatorStack.top(), formula.getLeft().check(*this), formula.getRight().check(*this), qualitative).first;
  236. }
  237. /*!
  238. * Computes the extremal probability to satisfy phi until psi for each state in the model.
  239. *
  240. * @param minimize If set, the probability is minimized and maximized otherwise.
  241. * @param phiStates A bit vector indicating which states satisfy phi.
  242. * @param psiStates A bit vector indicating which states satisfy psi.
  243. * @param qualitative A flag indicating whether the formula only needs to be evaluated qualitatively, i.e. if the
  244. * results are only compared against the bounds 0 and 1. If set to true, this will most likely results that are only
  245. * qualitatively correct, i.e. do not represent the correct value, but only the correct relation with respect to the
  246. * bounds 0 and 1.
  247. * @param scheduler If <code>qualitative</code> is false and this vector is non-null and has as many elements as
  248. * there are states in the MDP, this vector will represent a scheduler for the model that achieves the probability
  249. * returned by model checking. To this end, the vector will hold the nondeterministic choice made for each state.
  250. * @return The probabilities for the satisfying phi until psi for each state of the model. If the
  251. * qualitative flag is set, exact probabilities might not be computed.
  252. */
  253. static std::pair<std::vector<Type>, storm::storage::TotalScheduler> computeUnboundedUntilProbabilities(bool minimize, storm::storage::SparseMatrix<Type> const& transitionMatrix, std::vector<uint_fast64_t> nondeterministicChoiceIndices, storm::storage::SparseMatrix<Type> const& backwardTransitions, storm::storage::BitVector const& initialStates, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates, std::shared_ptr<storm::solver::AbstractNondeterministicLinearEquationSolver<Type>> nondeterministicLinearEquationSolver, bool qualitative) {
  254. size_t numberOfStates = phiStates.size();
  255. // We need to identify the states which have to be taken out of the matrix, i.e.
  256. // all states that have probability 0 and 1 of satisfying the until-formula.
  257. std::pair<storm::storage::BitVector, storm::storage::BitVector> statesWithProbability01;
  258. if (minimize) {
  259. statesWithProbability01 = storm::utility::graph::performProb01Min(transitionMatrix, nondeterministicChoiceIndices, backwardTransitions, phiStates, psiStates);
  260. } else {
  261. statesWithProbability01 = storm::utility::graph::performProb01Max(transitionMatrix, nondeterministicChoiceIndices, backwardTransitions, phiStates, psiStates);
  262. }
  263. storm::storage::BitVector statesWithProbability0 = std::move(statesWithProbability01.first);
  264. storm::storage::BitVector statesWithProbability1 = std::move(statesWithProbability01.second);
  265. storm::storage::BitVector maybeStates = ~(statesWithProbability0 | statesWithProbability1);
  266. LOG4CPLUS_INFO(logger, "Found " << statesWithProbability0.getNumberOfSetBits() << " 'no' states.");
  267. LOG4CPLUS_INFO(logger, "Found " << statesWithProbability1.getNumberOfSetBits() << " 'yes' states.");
  268. LOG4CPLUS_INFO(logger, "Found " << maybeStates.getNumberOfSetBits() << " 'maybe' states.");
  269. // Create resulting vector.
  270. std::vector<Type> result(numberOfStates);
  271. // Check whether we need to compute exact probabilities for some states.
  272. if (initialStates.isDisjointFrom(maybeStates) || qualitative) {
  273. if (qualitative) {
  274. LOG4CPLUS_INFO(logger, "The formula was checked qualitatively. No exact probabilities were computed.");
  275. } else {
  276. LOG4CPLUS_INFO(logger, "The probabilities for the initial states were determined in a preprocessing step."
  277. << " No exact probabilities were computed.");
  278. }
  279. // Set the values for all maybe-states to 0.5 to indicate that their probability values
  280. // are neither 0 nor 1.
  281. storm::utility::vector::setVectorValues<Type>(result, maybeStates, Type(0.5));
  282. } else {
  283. // In this case we have have to compute the probabilities.
  284. // First, we can eliminate the rows and columns from the original transition probability matrix for states
  285. // whose probabilities are already known.
  286. storm::storage::SparseMatrix<Type> submatrix = transitionMatrix.getSubmatrix(maybeStates, nondeterministicChoiceIndices);
  287. // Get the "new" nondeterministic choice indices for the submatrix.
  288. std::vector<uint_fast64_t> subNondeterministicChoiceIndices = storm::utility::vector::getConstrainedOffsetVector(nondeterministicChoiceIndices, maybeStates);
  289. // Prepare the right-hand side of the equation system. For entry i this corresponds to
  290. // the accumulated probability of going from state i to some 'yes' state.
  291. std::vector<Type> b = transitionMatrix.getConstrainedRowSumVector(maybeStates, nondeterministicChoiceIndices, statesWithProbability1, submatrix.getRowCount());
  292. // Create vector for results for maybe states.
  293. std::vector<Type> x(maybeStates.getNumberOfSetBits());
  294. // Solve the corresponding system of equations.
  295. nondeterministicLinearEquationSolver->solveEquationSystem(minimize, submatrix, x, b, subNondeterministicChoiceIndices);
  296. // Set values of resulting vector according to result.
  297. storm::utility::vector::setVectorValues<Type>(result, maybeStates, x);
  298. }
  299. // Set values of resulting vector that are known exactly.
  300. storm::utility::vector::setVectorValues<Type>(result, statesWithProbability0, storm::utility::constGetZero<Type>());
  301. storm::utility::vector::setVectorValues<Type>(result, statesWithProbability1, storm::utility::constGetOne<Type>());
  302. // Finally, compute a scheduler that achieves the extramal value.
  303. storm::storage::TotalScheduler scheduler = computeExtremalScheduler(minimize, transitionMatrix, nondeterministicChoiceIndices, result);
  304. return std::make_pair(result, scheduler);
  305. }
  306. std::pair<std::vector<Type>, storm::storage::TotalScheduler> checkUntil(bool minimize, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates, bool qualitative) const {
  307. return computeUnboundedUntilProbabilities(minimize, this->getModel().getTransitionMatrix(), this->getModel().getNondeterministicChoiceIndices(), this->getModel().getBackwardTransitions(), this->getModel().getInitialStates(), phiStates, psiStates, this->nondeterministicLinearEquationSolver, qualitative);
  308. }
  309. /*!
  310. * Checks the given formula that is an instantaneous reward formula.
  311. *
  312. * @param formula The formula to check.
  313. * @param qualitative A flag indicating whether the formula only needs to be evaluated qualitatively, i.e. if the
  314. * results are only compared against the bound 0. If set to true, this will most likely results that are only
  315. * qualitatively correct, i.e. do not represent the correct value, but only the correct relation with respect to the
  316. * bound 0.
  317. * @return The reward values for the given formula for every state of the model associated with this model
  318. * checker. If the qualitative flag is set, exact values might not be computed.
  319. */
  320. virtual std::vector<Type> checkInstantaneousReward(const storm::property::prctl::InstantaneousReward<Type>& formula, bool qualitative) const {
  321. // Only compute the result if the model has a state-based reward model.
  322. if (!this->getModel().hasStateRewards()) {
  323. LOG4CPLUS_ERROR(logger, "Missing (state-based) reward model for formula.");
  324. throw storm::exceptions::InvalidPropertyException() << "Missing (state-based) reward model for formula.";
  325. }
  326. // Initialize result to state rewards of the model.
  327. std::vector<Type> result(this->getModel().getStateRewardVector());
  328. this->nondeterministicLinearEquationSolver->performMatrixVectorMultiplication(this->minimumOperatorStack.top(), this->getModel().getTransitionMatrix(), result, this->getModel().getNondeterministicChoiceIndices(), nullptr, formula.getBound());
  329. return result;
  330. }
  331. /*!
  332. * Check the given formula that is a cumulative reward formula.
  333. *
  334. * @param formula The formula to check.
  335. * @param qualitative A flag indicating whether the formula only needs to be evaluated qualitatively, i.e. if the
  336. * results are only compared against the bound 0. If set to true, this will most likely results that are only
  337. * qualitatively correct, i.e. do not represent the correct value, but only the correct relation with respect to the
  338. * bound 0.
  339. * @return The reward values for the given formula for every state of the model associated with this model
  340. * checker. If the qualitative flag is set, exact values might not be computed.
  341. */
  342. virtual std::vector<Type> checkCumulativeReward(const storm::property::prctl::CumulativeReward<Type>& formula, bool qualitative) const {
  343. // Only compute the result if the model has at least one reward model.
  344. if (!this->getModel().hasStateRewards() && !this->getModel().hasTransitionRewards()) {
  345. LOG4CPLUS_ERROR(logger, "Missing reward model for formula.");
  346. throw storm::exceptions::InvalidPropertyException() << "Missing reward model for formula.";
  347. }
  348. // Compute the reward vector to add in each step based on the available reward models.
  349. std::vector<Type> totalRewardVector;
  350. if (this->getModel().hasTransitionRewards()) {
  351. totalRewardVector = this->getModel().getTransitionMatrix().getPointwiseProductRowSumVector(this->getModel().getTransitionRewardMatrix());
  352. if (this->getModel().hasStateRewards()) {
  353. storm::utility::vector::addVectorsInPlace(totalRewardVector, this->getModel().getStateRewardVector());
  354. }
  355. } else {
  356. totalRewardVector = std::vector<Type>(this->getModel().getStateRewardVector());
  357. }
  358. // Initialize result to either the state rewards of the model or the null vector.
  359. std::vector<Type> result;
  360. if (this->getModel().hasStateRewards()) {
  361. result = std::vector<Type>(this->getModel().getStateRewardVector());
  362. } else {
  363. result.resize(this->getModel().getNumberOfStates());
  364. }
  365. this->nondeterministicLinearEquationSolver->performMatrixVectorMultiplication(this->minimumOperatorStack.top(), this->getModel().getTransitionMatrix(), result, this->getModel().getNondeterministicChoiceIndices(), &totalRewardVector, formula.getBound());
  366. return result;
  367. }
  368. /*!
  369. * Checks the given formula that is a reachability reward formula.
  370. *
  371. * @param formula The formula to check.
  372. * @param qualitative A flag indicating whether the formula only needs to be evaluated qualitatively, i.e. if the
  373. * results are only compared against the bound 0. If set to true, this will most likely results that are only
  374. * qualitatively correct, i.e. do not represent the correct value, but only the correct relation with respect to the
  375. * bound 0.
  376. * @return The reward values for the given formula for every state of the model associated with this model
  377. * checker. If the qualitative flag is set, exact values might not be computed.
  378. */
  379. virtual std::vector<Type> checkReachabilityReward(const storm::property::prctl::ReachabilityReward<Type>& formula, bool qualitative) const {
  380. return this->checkReachabilityReward(this->minimumOperatorStack.top(), formula.getChild().check(*this), qualitative).first;
  381. }
  382. /*!
  383. * Computes the expected reachability reward that is gained before a target state is reached for each state.
  384. *
  385. * @param minimize If set, the reward is to be minimized and maximized otherwise.
  386. * @param targetStates The target states before which rewards can be gained.
  387. * @param qualitative A flag indicating whether the formula only needs to be evaluated qualitatively, i.e. if the
  388. * results are only compared against the bound 0. If set to true, this will most likely results that are only
  389. * qualitatively correct, i.e. do not represent the correct value, but only the correct relation with respect to the
  390. * bound 0.
  391. * @param scheduler If <code>qualitative</code> is false and this vector is non-null and has as many elements as
  392. * there are states in the MDP, this vector will represent a scheduler for the model that achieves the probability
  393. * returned by model checking. To this end, the vector will hold the nondeterministic choice made for each state.
  394. * @return The expected reward values gained before a target state is reached for each state. If the
  395. * qualitative flag is set, exact values might not be computed.
  396. */
  397. virtual std::pair<std::vector<Type>, storm::storage::TotalScheduler> checkReachabilityReward(bool minimize, storm::storage::BitVector const& targetStates, bool qualitative) const {
  398. // Only compute the result if the model has at least one reward model.
  399. if (!(this->getModel().hasStateRewards() || this->getModel().hasTransitionRewards())) {
  400. LOG4CPLUS_ERROR(logger, "Missing reward model for formula.");
  401. throw storm::exceptions::InvalidPropertyException() << "Missing reward model for formula.";
  402. }
  403. // Determine which states have a reward of infinity by definition.
  404. storm::storage::BitVector infinityStates;
  405. storm::storage::BitVector trueStates(this->getModel().getNumberOfStates(), true);
  406. if (minimize) {
  407. infinityStates = std::move(storm::utility::graph::performProb1A(this->getModel().getTransitionMatrix(), this->getModel().getNondeterministicChoiceIndices(), this->getModel().getBackwardTransitions(), trueStates, targetStates));
  408. } else {
  409. infinityStates = std::move(storm::utility::graph::performProb1E(this->getModel().getTransitionMatrix(), this->getModel().getNondeterministicChoiceIndices(), this->getModel().getBackwardTransitions(), trueStates, targetStates));
  410. }
  411. infinityStates.complement();
  412. storm::storage::BitVector maybeStates = ~targetStates & ~infinityStates;
  413. LOG4CPLUS_INFO(logger, "Found " << infinityStates.getNumberOfSetBits() << " 'infinity' states.");
  414. LOG4CPLUS_INFO(logger, "Found " << targetStates.getNumberOfSetBits() << " 'target' states.");
  415. LOG4CPLUS_INFO(logger, "Found " << maybeStates.getNumberOfSetBits() << " 'maybe' states.");
  416. // Create resulting vector.
  417. std::vector<Type> result(this->getModel().getNumberOfStates());
  418. // Check whether we need to compute exact rewards for some states.
  419. if (this->getModel().getInitialStates().isDisjointFrom(maybeStates)) {
  420. LOG4CPLUS_INFO(logger, "The rewards for the initial states were determined in a preprocessing step."
  421. << " No exact rewards were computed.");
  422. // Set the values for all maybe-states to 1 to indicate that their reward values
  423. // are neither 0 nor infinity.
  424. storm::utility::vector::setVectorValues<Type>(result, maybeStates, storm::utility::constGetOne<Type>());
  425. } else {
  426. // In this case we have to compute the reward values for the remaining states.
  427. // We can eliminate the rows and columns from the original transition probability matrix for states
  428. // whose reward values are already known.
  429. storm::storage::SparseMatrix<Type> submatrix = this->getModel().getTransitionMatrix().getSubmatrix(maybeStates, this->getModel().getNondeterministicChoiceIndices());
  430. // Get the "new" nondeterministic choice indices for the submatrix.
  431. std::vector<uint_fast64_t> subNondeterministicChoiceIndices = storm::utility::vector::getConstrainedOffsetVector(this->getModel().getNondeterministicChoiceIndices(), maybeStates);
  432. // Prepare the right-hand side of the equation system. For entry i this corresponds to
  433. // the accumulated probability of going from state i to some 'yes' state.
  434. std::vector<Type> b(submatrix.getRowCount());
  435. if (this->getModel().hasTransitionRewards()) {
  436. // If a transition-based reward model is available, we initialize the right-hand
  437. // side to the vector resulting from summing the rows of the pointwise product
  438. // of the transition probability matrix and the transition reward matrix.
  439. std::vector<Type> pointwiseProductRowSumVector = this->getModel().getTransitionMatrix().getPointwiseProductRowSumVector(this->getModel().getTransitionRewardMatrix());
  440. storm::utility::vector::selectVectorValues(b, maybeStates, this->getModel().getNondeterministicChoiceIndices(), pointwiseProductRowSumVector);
  441. if (this->getModel().hasStateRewards()) {
  442. // If a state-based reward model is also available, we need to add this vector
  443. // as well. As the state reward vector contains entries not just for the states
  444. // that we still consider (i.e. maybeStates), we need to extract these values
  445. // first.
  446. std::vector<Type> subStateRewards(b.size());
  447. storm::utility::vector::selectVectorValuesRepeatedly(subStateRewards, maybeStates, this->getModel().getNondeterministicChoiceIndices(), this->getModel().getStateRewardVector());
  448. storm::utility::vector::addVectorsInPlace(b, subStateRewards);
  449. }
  450. } else {
  451. // If only a state-based reward model is available, we take this vector as the
  452. // right-hand side. As the state reward vector contains entries not just for the
  453. // states that we still consider (i.e. maybeStates), we need to extract these values
  454. // first.
  455. storm::utility::vector::selectVectorValuesRepeatedly(b, maybeStates, this->getModel().getNondeterministicChoiceIndices(), this->getModel().getStateRewardVector());
  456. }
  457. // Create vector for results for maybe states.
  458. std::vector<Type> x(maybeStates.getNumberOfSetBits());
  459. // Solve the corresponding system of equations.
  460. this->nondeterministicLinearEquationSolver->solveEquationSystem(minimize, submatrix, x, b, subNondeterministicChoiceIndices);
  461. // Set values of resulting vector according to result.
  462. storm::utility::vector::setVectorValues<Type>(result, maybeStates, x);
  463. }
  464. // Set values of resulting vector that are known exactly.
  465. storm::utility::vector::setVectorValues(result, targetStates, storm::utility::constGetZero<Type>());
  466. storm::utility::vector::setVectorValues(result, infinityStates, storm::utility::constGetInfinity<Type>());
  467. // Finally, compute a scheduler that achieves the extramal value.
  468. storm::storage::TotalScheduler scheduler = computeExtremalScheduler(this->minimumOperatorStack.top(), this->getModel().getTransitionMatrix(), this->getModel().getNondeterministicChoiceIndices(), result, this->getModel().hasStateRewards() ? &this->getModel().getStateRewardVector() : nullptr, this->getModel().hasTransitionRewards() ? &this->getModel().getTransitionRewardMatrix() : nullptr);
  469. return std::make_pair(result, scheduler);
  470. }
  471. protected:
  472. /*!
  473. * Computes the vector of choices that need to be made to minimize/maximize the model checking result for each state.
  474. *
  475. * @param minimize If set, all choices are resolved such that the solution value becomes minimal and maximal otherwise.
  476. * @param nondeterministicResult The model checking result for nondeterministic choices of all states.
  477. * @param takenChoices The output vector that is to store the taken choices.
  478. * @param nondeterministicChoiceIndices The assignment of states to their nondeterministic choices in the matrix.
  479. */
  480. static storm::storage::TotalScheduler computeExtremalScheduler(bool minimize, storm::storage::SparseMatrix<Type> const& transitionMatrix, std::vector<uint_fast64_t> const& nondeterministicChoiceIndices, std::vector<Type> const& result, std::vector<Type> const* stateRewardVector = nullptr, storm::storage::SparseMatrix<Type> const* transitionRewardMatrix = nullptr) {
  481. std::vector<Type> temporaryResult(nondeterministicChoiceIndices.size() - 1);
  482. std::vector<Type> nondeterministicResult(result);
  483. storm::solver::GmmxxLinearEquationSolver<Type> solver;
  484. solver.performMatrixVectorMultiplication(transitionMatrix, nondeterministicResult, nullptr, 1);
  485. if (stateRewardVector != nullptr || transitionRewardMatrix != nullptr) {
  486. std::vector<Type> totalRewardVector;
  487. if (transitionRewardMatrix != nullptr) {
  488. totalRewardVector = transitionMatrix.getPointwiseProductRowSumVector(*transitionRewardMatrix);
  489. if (stateRewardVector != nullptr) {
  490. std::vector<Type> stateRewards(totalRewardVector.size());
  491. storm::utility::vector::selectVectorValuesRepeatedly(stateRewards, storm::storage::BitVector(stateRewardVector->size(), true), nondeterministicChoiceIndices, *stateRewardVector);
  492. storm::utility::vector::addVectorsInPlace(totalRewardVector, stateRewards);
  493. }
  494. } else {
  495. totalRewardVector.resize(nondeterministicResult.size());
  496. storm::utility::vector::selectVectorValuesRepeatedly(totalRewardVector, storm::storage::BitVector(stateRewardVector->size(), true), nondeterministicChoiceIndices, *stateRewardVector);
  497. }
  498. storm::utility::vector::addVectorsInPlace(nondeterministicResult, totalRewardVector);
  499. }
  500. std::vector<uint_fast64_t> choices(result.size());
  501. if (minimize) {
  502. storm::utility::vector::reduceVectorMin(nondeterministicResult, temporaryResult, nondeterministicChoiceIndices, &choices);
  503. } else {
  504. storm::utility::vector::reduceVectorMax(nondeterministicResult, temporaryResult, nondeterministicChoiceIndices, &choices);
  505. }
  506. return storm::storage::TotalScheduler(choices);
  507. }
  508. /*!
  509. * A stack used for storing whether we are currently computing min or max probabilities or rewards, respectively.
  510. * The topmost element is true if and only if we are currently computing minimum probabilities or rewards.
  511. */
  512. mutable std::stack<bool> minimumOperatorStack;
  513. /*!
  514. * A solver that is used for solving systems of linear equations that are the result of nondeterministic choices.
  515. */
  516. std::shared_ptr<storm::solver::AbstractNondeterministicLinearEquationSolver<Type>> nondeterministicLinearEquationSolver;
  517. };
  518. } // namespace prctl
  519. } // namespace modelchecker
  520. } // namespace storm
  521. #endif /* STORM_MODELCHECKER_PRCTL_SPARSEMDPPRCTLMODELCHECKER_H_ */