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.

735 lines
41 KiB

  1. #include "ExplicitDFTModelBuilderApprox.h"
  2. #include <map>
  3. #include "storm/models/sparse/MarkovAutomaton.h"
  4. #include "storm/models/sparse/Ctmc.h"
  5. #include "storm/utility/constants.h"
  6. #include "storm/utility/vector.h"
  7. #include "storm/utility/bitoperations.h"
  8. #include "storm/exceptions/UnexpectedException.h"
  9. #include "storm/settings/SettingsManager.h"
  10. #include "storm-dft/settings/modules/DFTSettings.h"
  11. namespace storm {
  12. namespace builder {
  13. template<typename ValueType, typename StateType>
  14. ExplicitDFTModelBuilderApprox<ValueType, StateType>::ModelComponents::ModelComponents() : transitionMatrix(), stateLabeling(), markovianStates(), exitRates(), choiceLabeling() {
  15. // Intentionally left empty.
  16. }
  17. template<typename ValueType, typename StateType>
  18. ExplicitDFTModelBuilderApprox<ValueType, StateType>::MatrixBuilder::MatrixBuilder(bool canHaveNondeterminism) : mappingOffset(0), stateRemapping(), currentRowGroup(0), currentRow(0), canHaveNondeterminism((canHaveNondeterminism)) {
  19. // Create matrix builder
  20. builder = storm::storage::SparseMatrixBuilder<ValueType>(0, 0, 0, false, canHaveNondeterminism, 0);
  21. }
  22. template<typename ValueType, typename StateType>
  23. ExplicitDFTModelBuilderApprox<ValueType, StateType>::ExplicitDFTModelBuilderApprox(storm::storage::DFT<ValueType> const& dft, storm::storage::DFTIndependentSymmetries const& symmetries, bool enableDC) :
  24. dft(dft),
  25. stateGenerationInfo(std::make_shared<storm::storage::DFTStateGenerationInfo>(dft.buildStateGenerationInfo(symmetries))),
  26. enableDC(enableDC),
  27. usedHeuristic(storm::settings::getModule<storm::settings::modules::DFTSettings>().getApproximationHeuristic()),
  28. generator(dft, *stateGenerationInfo, enableDC, mergeFailedStates),
  29. matrixBuilder(!generator.isDeterministicModel()),
  30. stateStorage(((dft.stateVectorSize() / 64) + 1) * 64),
  31. // TODO Matthias: make choosable
  32. //explorationQueue(dft.nrElements()+1, 0, 1)
  33. explorationQueue(200, 0, 0.9)
  34. {
  35. // Intentionally left empty.
  36. // TODO Matthias: remove again
  37. usedHeuristic = storm::builder::ApproximationHeuristic::BOUNDDIFFERENCE;
  38. // Compute independent subtrees
  39. if (dft.topLevelType() == storm::storage::DFTElementType::OR) {
  40. // We only support this for approximation with top level element OR
  41. for (auto const& child : dft.getGate(dft.getTopLevelIndex())->children()) {
  42. // Consider all children of the top level gate
  43. std::vector<size_t> isubdft;
  44. if (child->nrParents() > 1 || child->hasOutgoingDependencies()) {
  45. STORM_LOG_TRACE("child " << child->name() << "does not allow modularisation.");
  46. isubdft.clear();
  47. } else if (dft.isGate(child->id())) {
  48. isubdft = dft.getGate(child->id())->independentSubDft(false);
  49. } else {
  50. STORM_LOG_ASSERT(dft.isBasicElement(child->id()), "Child is no BE.");
  51. if(dft.getBasicElement(child->id())->hasIngoingDependencies()) {
  52. STORM_LOG_TRACE("child " << child->name() << "does not allow modularisation.");
  53. isubdft.clear();
  54. } else {
  55. isubdft = {child->id()};
  56. }
  57. }
  58. if(isubdft.empty()) {
  59. subtreeBEs.clear();
  60. break;
  61. } else {
  62. // Add new independent subtree
  63. std::vector<size_t> subtree;
  64. for (size_t id : isubdft) {
  65. if (dft.isBasicElement(id)) {
  66. subtree.push_back(id);
  67. }
  68. }
  69. subtreeBEs.push_back(subtree);
  70. }
  71. }
  72. }
  73. if (subtreeBEs.empty()) {
  74. // Consider complete tree
  75. std::vector<size_t> subtree;
  76. for (size_t id = 0; id < dft.nrElements(); ++id) {
  77. if (dft.isBasicElement(id)) {
  78. subtree.push_back(id);
  79. }
  80. }
  81. subtreeBEs.push_back(subtree);
  82. }
  83. for (auto tree : subtreeBEs) {
  84. std::stringstream stream;
  85. stream << "Subtree: ";
  86. for (size_t id : tree) {
  87. stream << id << ", ";
  88. }
  89. STORM_LOG_TRACE(stream.str());
  90. }
  91. }
  92. template<typename ValueType, typename StateType>
  93. void ExplicitDFTModelBuilderApprox<ValueType, StateType>::buildModel(LabelOptions const& labelOpts, size_t iteration, double approximationThreshold) {
  94. STORM_LOG_TRACE("Generating DFT state space");
  95. if (iteration < 1) {
  96. // Initialize
  97. modelComponents.markovianStates = storm::storage::BitVector(INITIAL_BITVECTOR_SIZE);
  98. if(mergeFailedStates) {
  99. // Introduce explicit fail state
  100. storm::generator::StateBehavior<ValueType, StateType> behavior = generator.createMergeFailedState([this] (DFTStatePointer const& state) {
  101. this->failedStateId = newIndex++;
  102. matrixBuilder.stateRemapping.push_back(0);
  103. return this->failedStateId;
  104. } );
  105. matrixBuilder.setRemapping(failedStateId);
  106. STORM_LOG_ASSERT(!behavior.empty(), "Behavior is empty.");
  107. matrixBuilder.newRowGroup();
  108. setMarkovian(behavior.begin()->isMarkovian());
  109. // Now add self loop.
  110. // TODO Matthias: maybe use general method.
  111. STORM_LOG_ASSERT(behavior.getNumberOfChoices() == 1, "Wrong number of choices for failed state.");
  112. STORM_LOG_ASSERT(behavior.begin()->size() == 1, "Wrong number of transitions for failed state.");
  113. std::pair<StateType, ValueType> stateProbabilityPair = *(behavior.begin()->begin());
  114. STORM_LOG_ASSERT(stateProbabilityPair.first == failedStateId, "No self loop for failed state.");
  115. STORM_LOG_ASSERT(storm::utility::isOne<ValueType>(stateProbabilityPair.second), "Probability for failed state != 1.");
  116. matrixBuilder.addTransition(stateProbabilityPair.first, stateProbabilityPair.second);
  117. matrixBuilder.finishRow();
  118. }
  119. // Build initial state
  120. this->stateStorage.initialStateIndices = generator.getInitialStates(std::bind(&ExplicitDFTModelBuilderApprox::getOrAddStateIndex, this, std::placeholders::_1));
  121. STORM_LOG_ASSERT(stateStorage.initialStateIndices.size() == 1, "Only one initial state assumed.");
  122. initialStateIndex = stateStorage.initialStateIndices[0];
  123. STORM_LOG_TRACE("Initial state: " << initialStateIndex);
  124. // Initialize heuristic values for inital state
  125. STORM_LOG_ASSERT(!statesNotExplored.at(initialStateIndex).second, "Heuristic for initial state is already initialized");
  126. ExplorationHeuristicPointer heuristic = std::make_shared<ExplorationHeuristic>(initialStateIndex);
  127. heuristic->markExpand();
  128. statesNotExplored[initialStateIndex].second = heuristic;
  129. explorationQueue.push(heuristic);
  130. } else {
  131. initializeNextIteration();
  132. }
  133. if (approximationThreshold > 0) {
  134. switch (usedHeuristic) {
  135. case storm::builder::ApproximationHeuristic::NONE:
  136. // Do not change anything
  137. approximationThreshold = dft.nrElements()+10;
  138. break;
  139. case storm::builder::ApproximationHeuristic::DEPTH:
  140. approximationThreshold = iteration;
  141. break;
  142. case storm::builder::ApproximationHeuristic::PROBABILITY:
  143. case storm::builder::ApproximationHeuristic::BOUNDDIFFERENCE:
  144. approximationThreshold = 10 * std::pow(2, iteration);
  145. break;
  146. }
  147. }
  148. exploreStateSpace(approximationThreshold);
  149. size_t stateSize = stateStorage.getNumberOfStates() + (mergeFailedStates ? 1 : 0);
  150. modelComponents.markovianStates.resize(stateSize);
  151. modelComponents.deterministicModel = generator.isDeterministicModel();
  152. // Fix the entries in the transition matrix according to the mapping of ids to row group indices
  153. STORM_LOG_ASSERT(matrixBuilder.stateRemapping[initialStateIndex] == initialStateIndex, "Initial state should not be remapped.");
  154. // TODO Matthias: do not consider all rows?
  155. STORM_LOG_TRACE("Remap matrix: " << matrixBuilder.stateRemapping << ", offset: " << matrixBuilder.mappingOffset);
  156. matrixBuilder.remap();
  157. STORM_LOG_TRACE("State remapping: " << matrixBuilder.stateRemapping);
  158. STORM_LOG_TRACE("Markovian states: " << modelComponents.markovianStates);
  159. STORM_LOG_DEBUG("Model has " << stateSize << " states");
  160. STORM_LOG_DEBUG("Model is " << (generator.isDeterministicModel() ? "deterministic" : "non-deterministic"));
  161. // Build transition matrix
  162. modelComponents.transitionMatrix = matrixBuilder.builder.build(stateSize, stateSize);
  163. if (stateSize <= 15) {
  164. STORM_LOG_TRACE("Transition matrix: " << std::endl << modelComponents.transitionMatrix);
  165. } else {
  166. STORM_LOG_TRACE("Transition matrix: too big to print");
  167. }
  168. buildLabeling(labelOpts);
  169. }
  170. template<typename ValueType, typename StateType>
  171. void ExplicitDFTModelBuilderApprox<ValueType, StateType>::initializeNextIteration() {
  172. STORM_LOG_TRACE("Refining DFT state space");
  173. // TODO Matthias: should be easier now as skipped states all are at the end of the matrix
  174. // Push skipped states to explore queue
  175. // TODO Matthias: remove
  176. for (auto const& skippedState : skippedStates) {
  177. statesNotExplored[skippedState.second.first->getId()] = skippedState.second;
  178. explorationQueue.push(skippedState.second.second);
  179. }
  180. // Initialize matrix builder again
  181. // TODO Matthias: avoid copy
  182. std::vector<uint_fast64_t> copyRemapping = matrixBuilder.stateRemapping;
  183. matrixBuilder = MatrixBuilder(!generator.isDeterministicModel());
  184. matrixBuilder.stateRemapping = copyRemapping;
  185. StateType nrStates = modelComponents.transitionMatrix.getRowGroupCount();
  186. STORM_LOG_ASSERT(nrStates == matrixBuilder.stateRemapping.size(), "No. of states does not coincide with mapping size.");
  187. // Start by creating a remapping from the old indices to the new indices
  188. std::vector<StateType> indexRemapping = std::vector<StateType>(nrStates, 0);
  189. auto iterSkipped = skippedStates.begin();
  190. size_t skippedBefore = 0;
  191. for (size_t i = 0; i < indexRemapping.size(); ++i) {
  192. while (iterSkipped != skippedStates.end() && iterSkipped->first <= i) {
  193. STORM_LOG_ASSERT(iterSkipped->first < indexRemapping.size(), "Skipped is too high.");
  194. ++skippedBefore;
  195. ++iterSkipped;
  196. }
  197. indexRemapping[i] = i - skippedBefore;
  198. }
  199. // Set remapping
  200. size_t nrExpandedStates = nrStates - skippedBefore;
  201. matrixBuilder.mappingOffset = nrStates;
  202. STORM_LOG_TRACE("# expanded states: " << nrExpandedStates);
  203. StateType skippedIndex = nrExpandedStates;
  204. std::map<StateType, std::pair<DFTStatePointer, ExplorationHeuristicPointer>> skippedStatesNew;
  205. for (size_t id = 0; id < matrixBuilder.stateRemapping.size(); ++id) {
  206. StateType index = matrixBuilder.stateRemapping[id];
  207. auto itFind = skippedStates.find(index);
  208. if (itFind != skippedStates.end()) {
  209. // Set new mapping for skipped state
  210. matrixBuilder.stateRemapping[id] = skippedIndex;
  211. skippedStatesNew[skippedIndex] = itFind->second;
  212. indexRemapping[index] = skippedIndex;
  213. ++skippedIndex;
  214. } else {
  215. // Set new mapping for expanded state
  216. matrixBuilder.stateRemapping[id] = indexRemapping[index];
  217. }
  218. }
  219. STORM_LOG_TRACE("New state remapping: " << matrixBuilder.stateRemapping);
  220. std::stringstream ss;
  221. ss << "Index remapping:" << std::endl;
  222. for (auto tmp : indexRemapping) {
  223. ss << tmp << " ";
  224. }
  225. STORM_LOG_TRACE(ss.str());
  226. // Remap markovian states
  227. storm::storage::BitVector markovianStatesNew = storm::storage::BitVector(modelComponents.markovianStates.size(), true);
  228. // Iterate over all not set bits
  229. modelComponents.markovianStates.complement();
  230. size_t index = modelComponents.markovianStates.getNextSetIndex(0);
  231. while (index < modelComponents.markovianStates.size()) {
  232. markovianStatesNew.set(indexRemapping[index], false);
  233. index = modelComponents.markovianStates.getNextSetIndex(index+1);
  234. }
  235. STORM_LOG_ASSERT(modelComponents.markovianStates.size() - modelComponents.markovianStates.getNumberOfSetBits() == markovianStatesNew.getNumberOfSetBits(), "Remapping of markovian states is wrong.");
  236. STORM_LOG_ASSERT(markovianStatesNew.size() == nrStates, "No. of states does not coincide with markovian size.");
  237. modelComponents.markovianStates = markovianStatesNew;
  238. // Build submatrix for expanded states
  239. // TODO Matthias: only use row groups when necessary
  240. for (StateType oldRowGroup = 0; oldRowGroup < modelComponents.transitionMatrix.getRowGroupCount(); ++oldRowGroup) {
  241. if (indexRemapping[oldRowGroup] < nrExpandedStates) {
  242. // State is expanded -> copy to new matrix
  243. matrixBuilder.newRowGroup();
  244. for (StateType oldRow = modelComponents.transitionMatrix.getRowGroupIndices()[oldRowGroup]; oldRow < modelComponents.transitionMatrix.getRowGroupIndices()[oldRowGroup+1]; ++oldRow) {
  245. for (typename storm::storage::SparseMatrix<ValueType>::const_iterator itEntry = modelComponents.transitionMatrix.begin(oldRow); itEntry != modelComponents.transitionMatrix.end(oldRow); ++itEntry) {
  246. auto itFind = skippedStates.find(itEntry->getColumn());
  247. if (itFind != skippedStates.end()) {
  248. // Set id for skipped states as we remap it later
  249. matrixBuilder.addTransition(matrixBuilder.mappingOffset + itFind->second.first->getId(), itEntry->getValue());
  250. } else {
  251. // Set newly remapped index for expanded states
  252. matrixBuilder.addTransition(indexRemapping[itEntry->getColumn()], itEntry->getValue());
  253. }
  254. }
  255. matrixBuilder.finishRow();
  256. }
  257. }
  258. }
  259. skippedStates = skippedStatesNew;
  260. STORM_LOG_ASSERT(matrixBuilder.getCurrentRowGroup() == nrExpandedStates, "Row group size does not match.");
  261. skippedStates.clear();
  262. }
  263. template<typename ValueType, typename StateType>
  264. void ExplicitDFTModelBuilderApprox<ValueType, StateType>::exploreStateSpace(double approximationThreshold) {
  265. size_t nrExpandedStates = 0;
  266. size_t nrSkippedStates = 0;
  267. // TODO Matthias: do not empty queue every time but break before
  268. while (!explorationQueue.empty()) {
  269. // Get the first state in the queue
  270. ExplorationHeuristicPointer currentExplorationHeuristic = explorationQueue.popTop();
  271. StateType currentId = currentExplorationHeuristic->getId();
  272. auto itFind = statesNotExplored.find(currentId);
  273. STORM_LOG_ASSERT(itFind != statesNotExplored.end(), "Id " << currentId << " not found");
  274. DFTStatePointer currentState = itFind->second.first;
  275. STORM_LOG_ASSERT(currentExplorationHeuristic == itFind->second.second, "Exploration heuristics do not match");
  276. STORM_LOG_ASSERT(currentState->getId() == currentId, "Ids do not match");
  277. // Remove it from the list of not explored states
  278. statesNotExplored.erase(itFind);
  279. STORM_LOG_ASSERT(stateStorage.stateToId.contains(currentState->status()), "State is not contained in state storage.");
  280. STORM_LOG_ASSERT(stateStorage.stateToId.getValue(currentState->status()) == currentId, "Ids of states do not coincide.");
  281. // Get concrete state if necessary
  282. if (currentState->isPseudoState()) {
  283. // Create concrete state from pseudo state
  284. currentState->construct();
  285. }
  286. STORM_LOG_ASSERT(!currentState->isPseudoState(), "State is pseudo state.");
  287. // Remember that the current row group was actually filled with the transitions of a different state
  288. matrixBuilder.setRemapping(currentId);
  289. matrixBuilder.newRowGroup();
  290. // Try to explore the next state
  291. generator.load(currentState);
  292. if (approximationThreshold > 0.0 && nrExpandedStates > approximationThreshold && !currentExplorationHeuristic->isExpand()) {
  293. //if (currentExplorationHeuristic->isSkip(approximationThreshold)) {
  294. // Skip the current state
  295. ++nrSkippedStates;
  296. STORM_LOG_TRACE("Skip expansion of state: " << dft.getStateString(currentState));
  297. setMarkovian(true);
  298. // Add transition to target state with temporary value 0
  299. // TODO Matthias: what to do when there is no unique target state?
  300. matrixBuilder.addTransition(failedStateId, storm::utility::zero<ValueType>());
  301. // Remember skipped state
  302. skippedStates[matrixBuilder.getCurrentRowGroup() - 1] = std::make_pair(currentState, currentExplorationHeuristic);
  303. matrixBuilder.finishRow();
  304. } else {
  305. // Explore the current state
  306. ++nrExpandedStates;
  307. storm::generator::StateBehavior<ValueType, StateType> behavior = generator.expand(std::bind(&ExplicitDFTModelBuilderApprox::getOrAddStateIndex, this, std::placeholders::_1));
  308. STORM_LOG_ASSERT(!behavior.empty(), "Behavior is empty.");
  309. setMarkovian(behavior.begin()->isMarkovian());
  310. // Now add all choices.
  311. for (auto const& choice : behavior) {
  312. // Add the probabilistic behavior to the matrix.
  313. for (auto const& stateProbabilityPair : choice) {
  314. STORM_LOG_ASSERT(!storm::utility::isZero(stateProbabilityPair.second), "Probability zero.");
  315. // Set transition to state id + offset. This helps in only remapping all previously skipped states.
  316. matrixBuilder.addTransition(matrixBuilder.mappingOffset + stateProbabilityPair.first, stateProbabilityPair.second);
  317. // Set heuristic values for reached states
  318. auto iter = statesNotExplored.find(stateProbabilityPair.first);
  319. if (iter != statesNotExplored.end()) {
  320. // Update heuristic values
  321. DFTStatePointer state = iter->second.first;
  322. if (!iter->second.second) {
  323. // Initialize heuristic values
  324. ExplorationHeuristicPointer heuristic = std::make_shared<ExplorationHeuristic>(stateProbabilityPair.first, *currentExplorationHeuristic, stateProbabilityPair.second, choice.getTotalMass());
  325. iter->second.second = heuristic;
  326. if (state->hasFailed(dft.getTopLevelIndex()) || state->isFailsafe(dft.getTopLevelIndex()) || state->nrFailableDependencies() > 0 || (state->nrFailableDependencies() == 0 && state->nrFailableBEs() == 0)) {
  327. // Do not skip absorbing state or if reached by dependencies
  328. iter->second.second->markExpand();
  329. }
  330. if (usedHeuristic == storm::builder::ApproximationHeuristic::BOUNDDIFFERENCE) {
  331. // Compute bounds for heuristic now
  332. if (state->isPseudoState()) {
  333. // Create concrete state from pseudo state
  334. state->construct();
  335. }
  336. STORM_LOG_ASSERT(!currentState->isPseudoState(), "State is pseudo state.");
  337. // Initialize bounds
  338. ValueType lowerBound = getLowerBound(state);
  339. ValueType upperBound = getUpperBound(state);
  340. heuristic->setBounds(lowerBound, upperBound);
  341. }
  342. explorationQueue.push(heuristic);
  343. } else if (!iter->second.second->isExpand()) {
  344. double oldPriority = iter->second.second->getPriority();
  345. if (iter->second.second->updateHeuristicValues(*currentExplorationHeuristic, stateProbabilityPair.second, choice.getTotalMass())) {
  346. // Update priority queue
  347. explorationQueue.update(iter->second.second, oldPriority);
  348. }
  349. }
  350. }
  351. }
  352. matrixBuilder.finishRow();
  353. }
  354. }
  355. } // end exploration
  356. STORM_LOG_INFO("Expanded " << nrExpandedStates << " states");
  357. STORM_LOG_INFO("Skipped " << nrSkippedStates << " states");
  358. STORM_LOG_ASSERT(nrSkippedStates == skippedStates.size(), "Nr skipped states is wrong");
  359. }
  360. template<typename ValueType, typename StateType>
  361. void ExplicitDFTModelBuilderApprox<ValueType, StateType>::buildLabeling(LabelOptions const& labelOpts) {
  362. // Build state labeling
  363. modelComponents.stateLabeling = storm::models::sparse::StateLabeling(modelComponents.transitionMatrix.getRowGroupCount());
  364. // Initial state
  365. modelComponents.stateLabeling.addLabel("init");
  366. modelComponents.stateLabeling.addLabelToState("init", initialStateIndex);
  367. // Label all states corresponding to their status (failed, failsafe, failed BE)
  368. if(labelOpts.buildFailLabel) {
  369. modelComponents.stateLabeling.addLabel("failed");
  370. }
  371. if(labelOpts.buildFailSafeLabel) {
  372. modelComponents.stateLabeling.addLabel("failsafe");
  373. }
  374. // Collect labels for all BE
  375. std::vector<std::shared_ptr<storage::DFTBE<ValueType>>> basicElements = dft.getBasicElements();
  376. for (std::shared_ptr<storage::DFTBE<ValueType>> elem : basicElements) {
  377. if(labelOpts.beLabels.count(elem->name()) > 0) {
  378. modelComponents.stateLabeling.addLabel(elem->name() + "_fail");
  379. }
  380. }
  381. // Set labels to states
  382. if(mergeFailedStates) {
  383. modelComponents.stateLabeling.addLabelToState("failed", failedStateId);
  384. }
  385. for (auto const& stateIdPair : stateStorage.stateToId) {
  386. storm::storage::BitVector state = stateIdPair.first;
  387. size_t stateId = stateIdPair.second;
  388. if (!mergeFailedStates && labelOpts.buildFailLabel && dft.hasFailed(state, *stateGenerationInfo)) {
  389. modelComponents.stateLabeling.addLabelToState("failed", stateId);
  390. }
  391. if (labelOpts.buildFailSafeLabel && dft.isFailsafe(state, *stateGenerationInfo)) {
  392. modelComponents.stateLabeling.addLabelToState("failsafe", stateId);
  393. };
  394. // Set fail status for each BE
  395. for (std::shared_ptr<storage::DFTBE<ValueType>> elem : basicElements) {
  396. if (labelOpts.beLabels.count(elem->name()) > 0 && storm::storage::DFTState<ValueType>::hasFailed(state, stateGenerationInfo->getStateIndex(elem->id())) ) {
  397. modelComponents.stateLabeling.addLabelToState(elem->name() + "_fail", stateId);
  398. }
  399. }
  400. }
  401. }
  402. template<typename ValueType, typename StateType>
  403. std::shared_ptr<storm::models::sparse::Model<ValueType>> ExplicitDFTModelBuilderApprox<ValueType, StateType>::getModel() {
  404. STORM_LOG_ASSERT(skippedStates.size() == 0, "Concrete model has skipped states");
  405. return createModel(false);
  406. }
  407. template<typename ValueType, typename StateType>
  408. std::shared_ptr<storm::models::sparse::Model<ValueType>> ExplicitDFTModelBuilderApprox<ValueType, StateType>::getModelApproximation(bool lowerBound) {
  409. // TODO Matthias: handle case with no skipped states
  410. changeMatrixBound(modelComponents.transitionMatrix, lowerBound);
  411. return createModel(true);
  412. }
  413. template<typename ValueType, typename StateType>
  414. std::shared_ptr<storm::models::sparse::Model<ValueType>> ExplicitDFTModelBuilderApprox<ValueType, StateType>::createModel(bool copy) {
  415. std::shared_ptr<storm::models::sparse::Model<ValueType>> model;
  416. if (modelComponents.deterministicModel) {
  417. // Build CTMC
  418. if (copy) {
  419. model = std::make_shared<storm::models::sparse::Ctmc<ValueType>>(modelComponents.transitionMatrix, modelComponents.stateLabeling);
  420. } else {
  421. model = std::make_shared<storm::models::sparse::Ctmc<ValueType>>(std::move(modelComponents.transitionMatrix), std::move(modelComponents.stateLabeling));
  422. }
  423. } else {
  424. // Build MA
  425. // Compute exit rates
  426. // TODO Matthias: avoid computing multiple times
  427. modelComponents.exitRates = std::vector<ValueType>(modelComponents.markovianStates.size());
  428. std::vector<typename storm::storage::SparseMatrix<ValueType>::index_type> indices = modelComponents.transitionMatrix.getRowGroupIndices();
  429. for (StateType stateIndex = 0; stateIndex < modelComponents.markovianStates.size(); ++stateIndex) {
  430. if (modelComponents.markovianStates[stateIndex]) {
  431. modelComponents.exitRates[stateIndex] = modelComponents.transitionMatrix.getRowSum(indices[stateIndex]);
  432. } else {
  433. modelComponents.exitRates[stateIndex] = storm::utility::zero<ValueType>();
  434. }
  435. }
  436. STORM_LOG_TRACE("Exit rates: " << modelComponents.exitRates);
  437. std::shared_ptr<storm::models::sparse::MarkovAutomaton<ValueType>> ma;
  438. if (copy) {
  439. ma = std::make_shared<storm::models::sparse::MarkovAutomaton<ValueType>>(modelComponents.transitionMatrix, modelComponents.stateLabeling, modelComponents.markovianStates, modelComponents.exitRates);
  440. } else {
  441. ma = std::make_shared<storm::models::sparse::MarkovAutomaton<ValueType>>(std::move(modelComponents.transitionMatrix), std::move(modelComponents.stateLabeling), std::move(modelComponents.markovianStates), std::move(modelComponents.exitRates));
  442. }
  443. if (ma->hasOnlyTrivialNondeterminism()) {
  444. // Markov automaton can be converted into CTMC
  445. // TODO Matthias: change components which were not moved accordingly
  446. model = ma->convertToCTMC();
  447. } else {
  448. model = ma;
  449. }
  450. }
  451. STORM_LOG_DEBUG("No. states: " << model->getNumberOfStates());
  452. STORM_LOG_DEBUG("No. transitions: " << model->getNumberOfTransitions());
  453. if (model->getNumberOfStates() <= 15) {
  454. STORM_LOG_TRACE("Transition matrix: " << std::endl << model->getTransitionMatrix());
  455. } else {
  456. STORM_LOG_TRACE("Transition matrix: too big to print");
  457. }
  458. return model;
  459. }
  460. template<typename ValueType, typename StateType>
  461. void ExplicitDFTModelBuilderApprox<ValueType, StateType>::changeMatrixBound(storm::storage::SparseMatrix<ValueType> & matrix, bool lowerBound) const {
  462. // Set lower bound for skipped states
  463. for (auto it = skippedStates.begin(); it != skippedStates.end(); ++it) {
  464. auto matrixEntry = matrix.getRow(it->first, 0).begin();
  465. STORM_LOG_ASSERT(matrixEntry->getColumn() == failedStateId, "Transition has wrong target state.");
  466. STORM_LOG_ASSERT(!it->second.first->isPseudoState(), "State is still pseudo state.");
  467. ExplorationHeuristicPointer heuristic = it->second.second;
  468. if (storm::utility::isZero(heuristic->getUpperBound())) {
  469. // Initialize bounds
  470. ValueType lowerBound = getLowerBound(it->second.first);
  471. ValueType upperBound = getUpperBound(it->second.first);
  472. heuristic->setBounds(lowerBound, upperBound);
  473. }
  474. // Change bound
  475. if (lowerBound) {
  476. matrixEntry->setValue(it->second.second->getLowerBound());
  477. } else {
  478. matrixEntry->setValue(it->second.second->getUpperBound());
  479. }
  480. }
  481. }
  482. template<typename ValueType, typename StateType>
  483. ValueType ExplicitDFTModelBuilderApprox<ValueType, StateType>::getLowerBound(DFTStatePointer const& state) const {
  484. // Get the lower bound by considering the failure of all possible BEs
  485. ValueType lowerBound = storm::utility::zero<ValueType>();
  486. for (size_t index = 0; index < state->nrFailableBEs(); ++index) {
  487. lowerBound += state->getFailableBERate(index);
  488. }
  489. return lowerBound;
  490. }
  491. template<typename ValueType, typename StateType>
  492. ValueType ExplicitDFTModelBuilderApprox<ValueType, StateType>::getUpperBound(DFTStatePointer const& state) const {
  493. // Get the upper bound by considering the failure of all BEs
  494. ValueType upperBound = storm::utility::one<ValueType>();
  495. ValueType rateSum = storm::utility::zero<ValueType>();
  496. // Compute for each independent subtree
  497. for (std::vector<size_t> const& subtree : subtreeBEs) {
  498. // Get all possible rates
  499. std::vector<ValueType> rates;
  500. storm::storage::BitVector coldBEs(subtree.size(), false);
  501. for (size_t i = 0; i < subtree.size(); ++i) {
  502. size_t id = subtree[i];
  503. if (state->isOperational(id)) {
  504. // Get BE rate
  505. ValueType rate = state->getBERate(id);
  506. if (storm::utility::isZero<ValueType>(rate)) {
  507. // Get active failure rate for cold BE
  508. rate = dft.getBasicElement(id)->activeFailureRate();
  509. // Mark BE as cold
  510. coldBEs.set(i, true);
  511. }
  512. rates.push_back(rate);
  513. rateSum += rate;
  514. }
  515. }
  516. STORM_LOG_ASSERT(rates.size() > 0, "No rates failable");
  517. // Sort rates
  518. std::sort(rates.begin(), rates.end());
  519. std::vector<size_t> rateCount(rates.size(), 0);
  520. size_t lastIndex = 0;
  521. for (size_t i = 0; i < rates.size(); ++i) {
  522. if (rates[lastIndex] != rates[i]) {
  523. lastIndex = i;
  524. }
  525. ++rateCount[lastIndex];
  526. }
  527. for (size_t i = 0; i < rates.size(); ++i) {
  528. // Cold BEs cannot fail in the first step
  529. if (!coldBEs.get(i) && rateCount[i] > 0) {
  530. std::iter_swap(rates.begin() + i, rates.end() - 1);
  531. // Compute AND MTTF of subtree without current rate and scale with current rate
  532. upperBound += rates.back() * rateCount[i] * computeMTTFAnd(rates, rates.size() - 1);
  533. // Swap back
  534. // TODO try to avoid swapping back
  535. std::iter_swap(rates.begin() + i, rates.end() - 1);
  536. }
  537. }
  538. }
  539. STORM_LOG_TRACE("Upper bound is " << (rateSum / upperBound) << " for state " << state->getId());
  540. STORM_LOG_ASSERT(!storm::utility::isZero(upperBound), "Upper bound is 0");
  541. STORM_LOG_ASSERT(!storm::utility::isZero(rateSum), "State is absorbing");
  542. return rateSum / upperBound;
  543. }
  544. template<typename ValueType, typename StateType>
  545. ValueType ExplicitDFTModelBuilderApprox<ValueType, StateType>::computeMTTFAnd(std::vector<ValueType> const& rates, size_t size) const {
  546. ValueType result = storm::utility::zero<ValueType>();
  547. if (size == 0) {
  548. return result;
  549. }
  550. // TODO Matthias: works only for <64 BEs!
  551. // WARNING: this code produces wrong results for more than 32 BEs
  552. /*for (size_t i = 1; i < 4 && i <= rates.size(); ++i) {
  553. size_t permutation = smallestIntWithNBitsSet(static_cast<size_t>(i));
  554. ValueType sum = storm::utility::zero<ValueType>();
  555. do {
  556. ValueType permResult = storm::utility::zero<ValueType>();
  557. for(size_t j = 0; j < rates.size(); ++j) {
  558. if(permutation & static_cast<size_t>(1 << static_cast<size_t>(j))) {
  559. // WARNING: if the first bit is set, it also recognizes the 32nd bit as set
  560. // TODO: fix
  561. permResult += rates[j];
  562. }
  563. }
  564. permutation = nextBitPermutation(permutation);
  565. STORM_LOG_ASSERT(!storm::utility::isZero(permResult), "PermResult is 0");
  566. sum += storm::utility::one<ValueType>() / permResult;
  567. } while(permutation < (static_cast<size_t>(1) << rates.size()) && permutation != 0);
  568. if (i % 2 == 0) {
  569. result -= sum;
  570. } else {
  571. result += sum;
  572. }
  573. }*/
  574. // Compute result with permutations of size <= 3
  575. ValueType one = storm::utility::one<ValueType>();
  576. for (size_t i1 = 0; i1 < size; ++i1) {
  577. // + 1/a
  578. ValueType sum = rates[i1];
  579. result += one / sum;
  580. for (size_t i2 = 0; i2 < i1; ++i2) {
  581. // - 1/(a+b)
  582. ValueType sum2 = sum + rates[i2];
  583. result -= one / sum2;
  584. for (size_t i3 = 0; i3 < i2; ++i3) {
  585. // + 1/(a+b+c)
  586. result += one / (sum2 + rates[i3]);
  587. }
  588. }
  589. }
  590. STORM_LOG_ASSERT(!storm::utility::isZero(result), "UpperBound is 0");
  591. return result;
  592. }
  593. template<typename ValueType, typename StateType>
  594. StateType ExplicitDFTModelBuilderApprox<ValueType, StateType>::getOrAddStateIndex(DFTStatePointer const& state) {
  595. StateType stateId;
  596. bool changed = false;
  597. if (stateGenerationInfo->hasSymmetries()) {
  598. // Order state by symmetry
  599. STORM_LOG_TRACE("Check for symmetry: " << dft.getStateString(state));
  600. changed = state->orderBySymmetry();
  601. STORM_LOG_TRACE("State " << (changed ? "changed to " : "did not change") << (changed ? dft.getStateString(state) : ""));
  602. }
  603. if (stateStorage.stateToId.contains(state->status())) {
  604. // State already exists
  605. stateId = stateStorage.stateToId.getValue(state->status());
  606. STORM_LOG_TRACE("State " << dft.getStateString(state) << " with id " << stateId << " already exists");
  607. if (!changed) {
  608. // Check if state is pseudo state
  609. // If state is explored already the possible pseudo state was already constructed
  610. auto iter = statesNotExplored.find(stateId);
  611. if (iter != statesNotExplored.end() && iter->second.first->isPseudoState()) {
  612. // Create pseudo state now
  613. STORM_LOG_ASSERT(iter->second.first->getId() == stateId, "Ids do not match.");
  614. STORM_LOG_ASSERT(iter->second.first->status() == state->status(), "Pseudo states do not coincide.");
  615. state->setId(stateId);
  616. // Update mapping to map to concrete state now
  617. // TODO Matthias: just change pointer?
  618. statesNotExplored[stateId] = std::make_pair(state, iter->second.second);
  619. // We do not push the new state on the exploration queue as the pseudo state was already pushed
  620. STORM_LOG_TRACE("Created pseudo state " << dft.getStateString(state));
  621. }
  622. }
  623. } else {
  624. // State does not exist yet
  625. STORM_LOG_ASSERT(state->isPseudoState() == changed, "State type (pseudo/concrete) wrong.");
  626. // Create new state
  627. state->setId(newIndex++);
  628. stateId = stateStorage.stateToId.findOrAdd(state->status(), state->getId());
  629. STORM_LOG_ASSERT(stateId == state->getId(), "Ids do not match.");
  630. // Insert state as not yet explored
  631. ExplorationHeuristicPointer nullHeuristic;
  632. statesNotExplored[stateId] = std::make_pair(state, nullHeuristic);
  633. // Reserve one slot for the new state in the remapping
  634. matrixBuilder.stateRemapping.push_back(0);
  635. STORM_LOG_TRACE("New " << (state->isPseudoState() ? "pseudo" : "concrete") << " state: " << dft.getStateString(state));
  636. }
  637. return stateId;
  638. }
  639. template<typename ValueType, typename StateType>
  640. void ExplicitDFTModelBuilderApprox<ValueType, StateType>::setMarkovian(bool markovian) {
  641. if (matrixBuilder.getCurrentRowGroup() > modelComponents.markovianStates.size()) {
  642. // Resize BitVector
  643. modelComponents.markovianStates.resize(modelComponents.markovianStates.size() + INITIAL_BITVECTOR_SIZE);
  644. }
  645. modelComponents.markovianStates.set(matrixBuilder.getCurrentRowGroup() - 1, markovian);
  646. }
  647. template<typename ValueType, typename StateType>
  648. void ExplicitDFTModelBuilderApprox<ValueType, StateType>::printNotExplored() const {
  649. std::cout << "states not explored:" << std::endl;
  650. for (auto it : statesNotExplored) {
  651. std::cout << it.first << " -> " << dft.getStateString(it.second.first) << std::endl;
  652. }
  653. }
  654. // Explicitly instantiate the class.
  655. template class ExplicitDFTModelBuilderApprox<double>;
  656. #ifdef STORM_HAVE_CARL
  657. template class ExplicitDFTModelBuilderApprox<storm::RationalFunction>;
  658. #endif
  659. } // namespace builder
  660. } // namespace storm