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.

557 lines
25 KiB

  1. #ifndef STORM_MODELS_ABSTRACTMODEL_H_
  2. #define STORM_MODELS_ABSTRACTMODEL_H_
  3. #include "src/models/AtomicPropositionsLabeling.h"
  4. #include "src/storage/BitVector.h"
  5. #include "src/storage/SparseMatrix.h"
  6. #include "src/utility/Hash.h"
  7. #include <memory>
  8. #include <vector>
  9. #include <boost/optional.hpp>
  10. namespace storm {
  11. namespace models {
  12. /*!
  13. * @brief Enumeration of all supported types of models.
  14. */
  15. enum ModelType {
  16. Unknown, DTMC, CTMC, MDP, CTMDP
  17. };
  18. /*!
  19. * @brief Stream output operator for ModelType.
  20. */
  21. std::ostream& operator<<(std::ostream& os, ModelType const type);
  22. /*!
  23. * @brief Base class for all model classes.
  24. *
  25. * This is base class defines a common interface for all models to identify
  26. * their type and obtain the special model.
  27. */
  28. template<class T>
  29. class AbstractModel: public std::enable_shared_from_this<AbstractModel<T>> {
  30. public:
  31. /*! Copy Constructor for an abstract model from the given transition matrix and
  32. * the given labeling of the states. Creates copies of all given references.
  33. * @param other The Source Abstract Model
  34. */
  35. AbstractModel(AbstractModel<T> const& other)
  36. : transitionMatrix(other.transitionMatrix),
  37. stateLabeling(other.stateLabeling),
  38. stateRewardVector(other.stateRewardVector),
  39. transitionRewardMatrix(other.transitionRewardMatrix),
  40. choiceLabeling(other.choiceLabeling) {
  41. // Intentionally left empty.
  42. }
  43. /*! Move Constructor for an abstract model from the given transition matrix and
  44. * the given labeling of the states. Creates copies of all given references.
  45. * @param other The Source Abstract Model
  46. */
  47. AbstractModel(AbstractModel<T>&& other)
  48. : transitionMatrix(std::move(other.transitionMatrix)), choiceLabeling(std::move(other.choiceLabeling)),
  49. stateLabeling(std::move(other.stateLabeling)), stateRewardVector(std::move(other.stateRewardVector)),
  50. transitionRewardMatrix(std::move(other.transitionRewardMatrix)) {
  51. // Intentionally left empty.
  52. }
  53. /*! Constructs an abstract model from the given transition matrix and
  54. * the given labeling of the states. Creates copies of all given references.
  55. * @param transitionMatrix The matrix representing the transitions in the model.
  56. * @param stateLabeling The labeling that assigns a set of atomic
  57. * propositions to each state.
  58. * @param optionalStateRewardVector The reward values associated with the states.
  59. * @param optionalTransitionRewardMatrix The reward values associated with the transitions of the model.
  60. * @param optionalChoiceLabeling A vector that represents the labels associated with the choices of each state.
  61. */
  62. AbstractModel(storm::storage::SparseMatrix<T> const& transitionMatrix, storm::models::AtomicPropositionsLabeling const& stateLabeling,
  63. boost::optional<std::vector<T>> const& optionalStateRewardVector, boost::optional<storm::storage::SparseMatrix<T>> const& optionalTransitionRewardMatrix,
  64. boost::optional<std::vector<std::set<uint_fast64_t>>> const& optionalChoiceLabeling)
  65. : transitionMatrix(transitionMatrix), stateLabeling(stateLabeling) {
  66. if (optionalStateRewardVector) {
  67. this->stateRewardVector.reset(optionalStateRewardVector.get());
  68. }
  69. if (optionalTransitionRewardMatrix) {
  70. this->transitionRewardMatrix.reset(optionalTransitionRewardMatrix.get());
  71. }
  72. if (optionalChoiceLabeling) {
  73. this->choiceLabeling.reset(optionalChoiceLabeling.get());
  74. }
  75. }
  76. /*! Constructs an abstract model from the given transition matrix and
  77. * the given labeling of the states. Moves all given references.
  78. * @param transitionMatrix The matrix representing the transitions in the model.
  79. * @param stateLabeling The labeling that assigns a set of atomic
  80. * propositions to each state.
  81. * @param optionalStateRewardVector The reward values associated with the states.
  82. * @param optionalTransitionRewardMatrix The reward values associated with the transitions of the model.
  83. * @param optionalChoiceLabeling A vector that represents the labels associated with the choices of each state.
  84. */
  85. AbstractModel(storm::storage::SparseMatrix<T>&& transitionMatrix, storm::models::AtomicPropositionsLabeling&& stateLabeling,
  86. boost::optional<std::vector<T>>&& optionalStateRewardVector, boost::optional<storm::storage::SparseMatrix<T>>&& optionalTransitionRewardMatrix,
  87. boost::optional<std::vector<std::set<uint_fast64_t>>>&& optionalChoiceLabeling) :
  88. transitionMatrix(std::move(transitionMatrix)), choiceLabeling(std::move(optionalChoiceLabeling)),
  89. stateLabeling(std::move(stateLabeling)), stateRewardVector(std::move(optionalStateRewardVector)),
  90. transitionRewardMatrix(std::move(optionalTransitionRewardMatrix)) {
  91. // Intentionally left empty.
  92. }
  93. /*!
  94. * Destructor.
  95. */
  96. virtual ~AbstractModel() {
  97. // Intentionally left empty.
  98. }
  99. /*!
  100. * @brief Casts the model to the model type that was actually
  101. * created.
  102. *
  103. * As all methods that work on generic models will use this
  104. * AbstractModel class, this method provides a convenient way to
  105. * cast an AbstractModel object to an object of a concrete model
  106. * type, which can be obtained via getType(). The mapping from an
  107. * element of the ModelType enum to the actual class must be done
  108. * by the caller.
  109. *
  110. * This methods uses std::dynamic_pointer_cast internally.
  111. *
  112. * @return Shared pointer of new type to this object.
  113. */
  114. template <typename Model>
  115. std::shared_ptr<Model> as() {
  116. return std::dynamic_pointer_cast<Model>(this->shared_from_this());
  117. }
  118. /*!
  119. * @brief Return the actual type of the model.
  120. *
  121. * Each model must implement this method.
  122. *
  123. * @return Type of the model.
  124. */
  125. virtual ModelType getType() const = 0;
  126. /*!
  127. * Extracts the dependency graph from the model according to the given partition.
  128. *
  129. * @param partition A vector containing the blocks of the partition of the system.
  130. * @return A sparse matrix with bool entries that represents the dependency graph of the blocks of the partition.
  131. */
  132. storm::storage::SparseMatrix<bool> extractPartitionDependencyGraph(std::vector<std::vector<uint_fast64_t>> const& partition) const {
  133. uint_fast64_t numberOfStates = partition.size();
  134. // First, we need to create a mapping of states to their SCC index, to ease the computation
  135. // of dependency transitions later.
  136. std::vector<uint_fast64_t> stateToBlockMap(this->getNumberOfStates());
  137. for (uint_fast64_t i = 0; i < numberOfStates; ++i) {
  138. for (uint_fast64_t j = 0; j < partition[i].size(); ++j) {
  139. stateToBlockMap[partition[i][j]] = i;
  140. }
  141. }
  142. // The resulting sparse matrix will have as many rows/columns as there are blocks in the partition.
  143. storm::storage::SparseMatrix<bool> dependencyGraph(numberOfStates);
  144. dependencyGraph.initialize();
  145. for (uint_fast64_t currentBlockIndex = 0; currentBlockIndex < partition.size(); ++currentBlockIndex) {
  146. // Get the next block.
  147. std::vector<uint_fast64_t> const& block = partition[currentBlockIndex];
  148. // Now, we determine the blocks which are reachable (in one step) from the current block.
  149. std::set<uint_fast64_t> allTargetBlocks;
  150. for (auto state : block) {
  151. typename storm::storage::SparseMatrix<T>::Rows rows = this->getRows(state);
  152. for (auto& transition : rows) {
  153. uint_fast64_t targetBlock = stateToBlockMap[transition.column()];
  154. // We only need to consider transitions that are actually leaving the SCC.
  155. if (targetBlock != currentBlockIndex) {
  156. allTargetBlocks.insert(targetBlock);
  157. }
  158. }
  159. }
  160. // Now we can just enumerate all the target SCCs and insert the corresponding transitions.
  161. for (auto targetBlock : allTargetBlocks) {
  162. dependencyGraph.insertNextValue(currentBlockIndex, targetBlock, true);
  163. }
  164. }
  165. // Finalize the matrix and return result.
  166. dependencyGraph.finalize(true);
  167. return dependencyGraph;
  168. }
  169. /*!
  170. * Retrieves the backward transition relation of the model, i.e. a set of transitions
  171. * between states that correspond to the reversed transition relation of this model.
  172. *
  173. * @return A sparse matrix that represents the backward transitions of this model.
  174. */
  175. storm::storage::SparseMatrix<bool> getBackwardTransitions() const {
  176. return getBackwardTransitions<bool>([](T const& value) -> bool { return value != 0; });
  177. }
  178. /*!
  179. * Retrieves the backward transition relation of the model, i.e. a set of transitions
  180. * between states that correspond to the reversed transition relation of this model.
  181. *
  182. * @return A sparse matrix that represents the backward transitions of this model.
  183. */
  184. template <typename TransitionType>
  185. storm::storage::SparseMatrix<TransitionType> getBackwardTransitions(std::function<TransitionType(T const&)> const& selectionFunction) const {
  186. uint_fast64_t numberOfStates = this->getNumberOfStates();
  187. uint_fast64_t numberOfTransitions = this->getNumberOfTransitions();
  188. std::vector<uint_fast64_t> rowIndications(numberOfStates + 1);
  189. std::vector<uint_fast64_t> columnIndications(numberOfTransitions);
  190. std::vector<TransitionType> values(numberOfTransitions, TransitionType());
  191. // First, we need to count how many backward transitions each state has.
  192. for (uint_fast64_t i = 0; i < numberOfStates; ++i) {
  193. typename storm::storage::SparseMatrix<T>::Rows rows = this->getRows(i);
  194. for (auto const& transition : rows) {
  195. if (transition.value() > 0) {
  196. ++rowIndications[transition.column() + 1];
  197. }
  198. }
  199. }
  200. // Now compute the accumulated offsets.
  201. for (uint_fast64_t i = 1; i < numberOfStates; ++i) {
  202. rowIndications[i] = rowIndications[i - 1] + rowIndications[i];
  203. }
  204. // Put a sentinel element at the end of the indices list. This way,
  205. // for each state i the range of indices can be read off between
  206. // state_indices_list[i] and state_indices_list[i + 1].
  207. // FIXME: This should not be necessary and already be implied by the first steps.
  208. rowIndications[numberOfStates] = numberOfTransitions;
  209. // Create an array that stores the next index for each state. Initially
  210. // this corresponds to the previously computed accumulated offsets.
  211. std::vector<uint_fast64_t> nextIndices = rowIndications;
  212. // Now we are ready to actually fill in the list of predecessors for
  213. // every state. Again, we start by considering all but the last row.
  214. for (uint_fast64_t i = 0; i < numberOfStates; ++i) {
  215. typename storm::storage::SparseMatrix<T>::Rows rows = this->getRows(i);
  216. for (auto& transition : rows) {
  217. if (transition.value() > 0) {
  218. values[nextIndices[transition.column()]] = selectionFunction(transition.value());
  219. columnIndications[nextIndices[transition.column()]++] = i;
  220. }
  221. }
  222. }
  223. storm::storage::SparseMatrix<TransitionType> backwardTransitionMatrix(numberOfStates, numberOfStates,
  224. numberOfTransitions,
  225. std::move(rowIndications),
  226. std::move(columnIndications),
  227. std::move(values));
  228. return backwardTransitionMatrix;
  229. }
  230. /*!
  231. * Returns an object representing the matrix rows associated with the given state.
  232. *
  233. * @param state The state for which to retrieve the rows.
  234. * @return An object representing the matrix rows associated with the given state.
  235. */
  236. virtual typename storm::storage::SparseMatrix<T>::Rows getRows(uint_fast64_t state) const = 0;
  237. /*!
  238. * Returns an iterator to the successors of the given state.
  239. *
  240. * @param state The state for which to return the iterator.
  241. * @return An iterator to the successors of the given state.
  242. */
  243. virtual typename storm::storage::SparseMatrix<T>::ConstRowIterator rowIteratorBegin(uint_fast64_t state) const = 0;
  244. /*!
  245. * Returns an iterator pointing to the element past the successors of the given state.
  246. *
  247. * @param state The state for which to return the iterator.
  248. * @return An iterator pointing to the element past the successors of the given state.
  249. */
  250. virtual typename storm::storage::SparseMatrix<T>::ConstRowIterator rowIteratorEnd(uint_fast64_t state) const = 0;
  251. /*!
  252. * Returns the state space size of the model.
  253. * @return The size of the state space of the model.
  254. */
  255. virtual uint_fast64_t getNumberOfStates() const {
  256. return this->getTransitionMatrix().getColumnCount();
  257. }
  258. /*!
  259. * Returns the number of (non-zero) transitions of the model.
  260. * @return The number of (non-zero) transitions of the model.
  261. */
  262. virtual uint_fast64_t getNumberOfTransitions() const {
  263. return this->getTransitionMatrix().getNonZeroEntryCount();
  264. }
  265. /*!
  266. * Retrieves the initial states of the model.
  267. *
  268. * @return The initial states of the model represented by a bit vector.
  269. */
  270. storm::storage::BitVector const& getInitialStates() const {
  271. return this->getLabeledStates("init");
  272. }
  273. /*!
  274. * Returns a bit vector in which exactly those bits are set to true that
  275. * correspond to a state labeled with the given atomic proposition.
  276. * @param ap The atomic proposition for which to get the bit vector.
  277. * @return A bit vector in which exactly those bits are set to true that
  278. * correspond to a state labeled with the given atomic proposition.
  279. */
  280. storm::storage::BitVector const& getLabeledStates(std::string const& ap) const {
  281. return stateLabeling.getLabeledStates(ap);
  282. }
  283. /*!
  284. * Retrieves whether the given atomic proposition is a valid atomic proposition in this model.
  285. * @param atomicProposition The atomic proposition to be checked for validity.
  286. * @return True if the given atomic proposition is valid in this model.
  287. */
  288. bool hasAtomicProposition(std::string const& atomicProposition) const {
  289. return stateLabeling.containsAtomicProposition(atomicProposition);
  290. }
  291. /*!
  292. * Returns a pointer to the matrix representing the transition probability
  293. * function.
  294. * @return A pointer to the matrix representing the transition probability
  295. * function.
  296. */
  297. storm::storage::SparseMatrix<T> const& getTransitionMatrix() const {
  298. return transitionMatrix;
  299. }
  300. /*!
  301. * Returns a pointer to the matrix representing the transition rewards.
  302. * @return A pointer to the matrix representing the transition rewards.
  303. */
  304. storm::storage::SparseMatrix<T> const& getTransitionRewardMatrix() const {
  305. return transitionRewardMatrix.get();
  306. }
  307. /*!
  308. * Returns a pointer to the vector representing the state rewards.
  309. * @return A pointer to the vector representing the state rewards.
  310. */
  311. std::vector<T> const& getStateRewardVector() const {
  312. return stateRewardVector.get();
  313. }
  314. /*!
  315. * Returns the labels for the choices of the model, if there are any.
  316. * @return The labels for the choices of the model.
  317. */
  318. std::vector<std::set<uint_fast64_t>> const& getChoiceLabeling() const {
  319. return choiceLabeling.get();
  320. }
  321. /*!
  322. * Returns the set of labels with which the given state is labeled.
  323. *
  324. * @param state The state for which to return the set of labels.
  325. * @return The set of labels with which the given state is labeled.
  326. */
  327. std::set<std::string> getLabelsForState(uint_fast64_t state) const {
  328. return stateLabeling.getPropositionsForState(state);
  329. }
  330. /*!
  331. * Returns the state labeling associated with this model.
  332. * @return The state labeling associated with this model.
  333. */
  334. storm::models::AtomicPropositionsLabeling const& getStateLabeling() const {
  335. return stateLabeling;
  336. }
  337. /*!
  338. * Retrieves whether this model has a state reward model.
  339. * @return True if this model has a state reward model.
  340. */
  341. bool hasStateRewards() const {
  342. return stateRewardVector;
  343. }
  344. /*!
  345. * Retrieves whether this model has a transition reward model.
  346. * @return True if this model has a transition reward model.
  347. */
  348. bool hasTransitionRewards() const {
  349. return transitionRewardMatrix;
  350. }
  351. /*!
  352. * Retrieves whether this model has a labeling for the choices.
  353. * @return True if this model has a labeling.
  354. */
  355. bool hasChoiceLabels() const {
  356. return choiceLabeling;
  357. }
  358. /*!
  359. * Retrieves the size of the internal representation of the model in memory.
  360. * @return the size of the internal representation of the model in memory
  361. * measured in bytes.
  362. */
  363. virtual uint_fast64_t getSizeInMemory() const {
  364. uint_fast64_t result = transitionMatrix.getSizeInMemory() + stateLabeling.getSizeInMemory();
  365. if (stateRewardVector) {
  366. result += getStateRewardVector().size() * sizeof(T);
  367. }
  368. if (hasTransitionRewards()) {
  369. result += getTransitionRewardMatrix().getSizeInMemory();
  370. }
  371. return result;
  372. }
  373. /*!
  374. * Prints information about the model to the specified stream.
  375. * @param out The stream the information is to be printed to.
  376. */
  377. virtual void printModelInformationToStream(std::ostream& out) const {
  378. out << "-------------------------------------------------------------- " << std::endl;
  379. out << "Model type: \t\t" << this->getType() << std::endl;
  380. out << "States: \t\t" << this->getNumberOfStates() << std::endl;
  381. out << "Transitions: \t\t" << this->getNumberOfTransitions() << std::endl;
  382. this->getStateLabeling().printAtomicPropositionsInformationToStream(out);
  383. out << "Size in memory: \t" << (this->getSizeInMemory())/1024 << " kbytes" << std::endl;
  384. out << "-------------------------------------------------------------- " << std::endl;
  385. }
  386. /*!
  387. * Calculates a hash over all values contained in this Model.
  388. * @return size_t A Hash Value
  389. */
  390. virtual size_t getHash() const {
  391. std::size_t result = 0;
  392. boost::hash_combine(result, transitionMatrix.getHash());
  393. boost::hash_combine(result, stateLabeling.getHash());
  394. if (stateRewardVector) {
  395. boost::hash_combine(result, storm::utility::Hash<T>::getHash(stateRewardVector.get()));
  396. }
  397. if (transitionRewardMatrix) {
  398. boost::hash_combine(result, transitionRewardMatrix.get().getHash());
  399. }
  400. return result;
  401. }
  402. /*!
  403. * Assigns this model a new set of choiceLabels, giving each choice the stateId
  404. * @return void
  405. */
  406. virtual void setStateIdBasedChoiceLabeling() = 0;
  407. protected:
  408. /*!
  409. * Exports the model to the dot-format and prints the result to the given stream.
  410. *
  411. * @param outStream The stream to which the model is to be written.
  412. * @param includeLabling If set to true, the states will be exported with their labels.
  413. * @param subsystem If not null, this represents the subsystem that is to be exported.
  414. * @param firstValue If not null, the values in this vector are attached to the states.
  415. * @param secondValue If not null, the values in this vector are attached to the states.
  416. * @param stateColoring If not null, this is a mapping from states to color codes.
  417. * @param colors A mapping of color codes to color names.
  418. * @param finalizeOutput A flag that sets whether or not the dot stream is closed with a curly brace.
  419. * @return A string containing the exported model in dot-format.
  420. */
  421. virtual void writeDotToStream(std::ostream& outStream, bool includeLabeling = true, storm::storage::BitVector const* subsystem = nullptr, std::vector<T> const* firstValue = nullptr, std::vector<T> const* secondValue = nullptr, std::vector<uint_fast64_t> const* stateColoring = nullptr, std::vector<std::string> const* colors = nullptr, std::vector<uint_fast64_t>* scheduler = nullptr, bool finalizeOutput = true) const {
  422. outStream << "digraph model {" << std::endl;
  423. // Write all states to the stream.
  424. for (uint_fast64_t state = 0, highestStateIndex = this->getNumberOfStates() - 1; state <= highestStateIndex; ++state) {
  425. if (subsystem == nullptr || subsystem->get(state)) {
  426. outStream << "\t" << state;
  427. if (includeLabeling || firstValue != nullptr || secondValue != nullptr || stateColoring != nullptr) {
  428. outStream << " [ ";
  429. // If we need to print some extra information, do so now.
  430. if (includeLabeling || firstValue != nullptr || secondValue != nullptr) {
  431. outStream << "label = \"" << state << ": ";
  432. // Now print the state labeling to the stream if requested.
  433. if (includeLabeling) {
  434. outStream << "{";
  435. bool includeComma = false;
  436. for (std::string const& label : this->getLabelsForState(state)) {
  437. if (includeComma) {
  438. outStream << ", ";
  439. } else {
  440. includeComma = true;
  441. }
  442. outStream << label;
  443. }
  444. outStream << "}";
  445. }
  446. // If we are to include some values for the state as well, we do so now.
  447. if (firstValue != nullptr || secondValue != nullptr) {
  448. outStream << " [";
  449. if (firstValue != nullptr) {
  450. outStream << (*firstValue)[state];
  451. if (secondValue != nullptr) {
  452. outStream << ", ";
  453. }
  454. }
  455. if (secondValue != nullptr) {
  456. outStream << (*secondValue)[state];
  457. }
  458. outStream << "]";
  459. }
  460. outStream << "\"";
  461. // Now, we color the states if there were colors given.
  462. if (stateColoring != nullptr && colors != nullptr) {
  463. outStream << ", ";
  464. outStream << " style = filled, fillcolor = " << (*colors)[(*stateColoring)[state]];
  465. }
  466. }
  467. outStream << " ]";
  468. }
  469. outStream << ";" << std::endl;
  470. }
  471. }
  472. // If this methods has not been called from a derived class, we want to close the digraph here.
  473. if (finalizeOutput) {
  474. outStream << "}" << std::endl;
  475. }
  476. }
  477. /*! A matrix representing the likelihoods of moving between states. */
  478. storm::storage::SparseMatrix<T> transitionMatrix;
  479. /*! The labeling that is associated with the choices for each state. */
  480. boost::optional<std::vector<std::set<uint_fast64_t>>> choiceLabeling;
  481. private:
  482. /*! The labeling of the states of the model. */
  483. storm::models::AtomicPropositionsLabeling stateLabeling;
  484. /*! The state-based rewards of the model. */
  485. boost::optional<std::vector<T>> stateRewardVector;
  486. /*! The transition-based rewards of the model. */
  487. boost::optional<storm::storage::SparseMatrix<T>> transitionRewardMatrix;
  488. };
  489. } // namespace models
  490. } // namespace storm
  491. #endif /* STORM_MODELS_ABSTRACTMODEL_H_ */