Storm 1.11.1.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
ExplicitDFTModelBuilder.cpp
Go to the documentation of this file.
2
3#include <map>
4
18
19namespace storm::dft {
20namespace builder {
21
22template<typename ValueType, typename StateType>
23ExplicitDFTModelBuilder<ValueType, StateType>::ModelComponents::ModelComponents()
24 : transitionMatrix(), stateLabeling(), markovianStates(), exitRates(), choiceLabeling() {
25 // Intentionally left empty.
26}
27
28template<typename ValueType, typename StateType>
29ExplicitDFTModelBuilder<ValueType, StateType>::MatrixBuilder::MatrixBuilder(bool canHaveNondeterminism)
30 : mappingOffset(0), stateRemapping(), currentRowGroup(0), currentRow(0), canHaveNondeterminism((canHaveNondeterminism)) {
31 // Create matrix builder
32 builder = storm::storage::SparseMatrixBuilder<ValueType>(0, 0, 0, false, canHaveNondeterminism, 0);
33}
34
35template<typename ValueType, typename StateType>
37 storm::dft::storage::DftSymmetries const& symmetries)
38 : dft(dft),
39 stateGenerationInfo(std::make_shared<storm::dft::storage::DFTStateGenerationInfo>(dft.buildStateGenerationInfo(symmetries))),
40 generator(dft, *stateGenerationInfo),
41 matrixBuilder(!generator.isDeterministicModel()),
42 stateStorage(dft.stateBitVectorSize()),
43 explorationQueue(1, 0, 0.9, false) {
44 // Set relevant events
45 STORM_LOG_DEBUG("Relevant events: " << this->dft.getRelevantEventsString());
46 if (dft.getRelevantEvents().size() <= 1) {
47 STORM_LOG_ASSERT(dft.getRelevantEvents()[0] == dft.getTopLevelIndex(), "TLE is not relevant");
48 // Only interested in top level event -> introduce unique failed state
49 this->uniqueFailedState = true;
50 STORM_LOG_DEBUG("Using unique failed state with id 0.");
51 }
52
53 // Compute independent subtrees
55 // We only support this for approximation with top level element OR
56 for (auto const& child : dft.getGate(dft.getTopLevelIndex())->children()) {
57 // Consider all children of the top level gate
58 std::vector<size_t> isubdft;
59 if (child->nrParents() > 1 || child->hasOutgoingDependencies()) {
60 STORM_LOG_TRACE("child " << child->name() << " does not allow modularisation.");
61 isubdft.clear();
62 } else if (dft.isGate(child->id())) {
63 isubdft = dft.getGate(child->id())->independentSubDft(false);
64 } else {
65 STORM_LOG_ASSERT(dft.isBasicElement(child->id()), "Child is no BE.");
66 if (dft.getBasicElement(child->id())->hasIngoingDependencies()) {
67 STORM_LOG_TRACE("child " << child->name() << " does not allow modularisation.");
68 isubdft.clear();
69 } else {
70 isubdft = {child->id()};
71 }
72 }
73 if (isubdft.empty()) {
74 subtreeBEs.clear();
75 break;
76 } else {
77 // Add new independent subtree
78 std::vector<size_t> subtree;
79 for (size_t id : isubdft) {
80 if (dft.isBasicElement(id)) {
81 subtree.push_back(id);
82 }
83 }
84 subtreeBEs.push_back(subtree);
85 }
86 }
87 }
88 if (subtreeBEs.empty()) {
89 // Consider complete tree
90 std::vector<size_t> subtree;
91 for (size_t id = 0; id < dft.nrElements(); ++id) {
92 if (dft.isBasicElement(id)) {
93 subtree.push_back(id);
94 }
95 }
96 subtreeBEs.push_back(subtree);
97 }
98
99 for (auto tree : subtreeBEs) {
100 std::stringstream stream;
101 stream << "Subtree: ";
102 for (size_t id : tree) {
103 stream << id << ", ";
104 }
105 STORM_LOG_TRACE(stream.str());
106 }
107}
108
109template<typename ValueType, typename StateType>
110void ExplicitDFTModelBuilder<ValueType, StateType>::buildModel(size_t iteration, double approximationThreshold,
111 storm::dft::builder::ApproximationHeuristic approximationHeuristic) {
112 STORM_LOG_TRACE("Generating DFT state space");
113 usedHeuristic = approximationHeuristic;
114
115 if (approximationThreshold > 0 && !this->uniqueFailedState) {
116 // Approximation requires unique failed states
117 // TODO lift this restriction
118 STORM_LOG_WARN("Approximation requires unique failed state. Forcing use of unique failed state.");
119 this->uniqueFailedState = true;
120 }
121
122 if (iteration < 1) {
123 // Initialize
124 switch (usedHeuristic) {
126 explorationQueue = storm::dft::storage::BucketPriorityQueue<ExplorationHeuristic>(dft.nrElements() + 1, 0, 0.9, false);
127 break;
129 explorationQueue = storm::dft::storage::BucketPriorityQueue<ExplorationHeuristic>(200, 0, 0.9, true);
130 break;
132 explorationQueue = storm::dft::storage::BucketPriorityQueue<ExplorationHeuristic>(200, 0, 0.9, true);
133 break;
134 default:
135 STORM_LOG_THROW(false, storm::exceptions::IllegalArgumentException, "Heuristic not known.");
136 }
137 modelComponents.markovianStates = storm::storage::BitVector(INITIAL_BITVECTOR_SIZE);
138
139 if (this->uniqueFailedState) {
140 // Introduce explicit fail state
141 storm::generator::StateBehavior<ValueType, StateType> behavior = generator.createMergeFailedState([this](DFTStatePointer const& state) {
142 size_t failedStateId = newIndex++;
143 STORM_LOG_ASSERT(failedStateId == 0, "Unique failed id is not 0.");
144 matrixBuilder.stateRemapping.push_back(0);
145 return failedStateId;
146 });
147
148 matrixBuilder.setRemapping(0);
149 STORM_LOG_ASSERT(!behavior.empty(), "Behavior is empty.");
150 matrixBuilder.newRowGroup();
151 setMarkovian(behavior.begin()->isMarkovian());
152
153 // Now add self loop.
154 // TODO: maybe use general method.
155 STORM_LOG_ASSERT(behavior.getNumberOfChoices() == 1, "Wrong number of choices for failed state.");
156 STORM_LOG_ASSERT(behavior.begin()->size() == 1, "Wrong number of transitions for failed state.");
157 std::pair<StateType, ValueType> stateProbabilityPair = *(behavior.begin()->begin());
158 STORM_LOG_ASSERT(stateProbabilityPair.first == 0, "No self loop for failed state.");
159 STORM_LOG_ASSERT(storm::utility::isOne<ValueType>(stateProbabilityPair.second), "Probability for failed state != 1.");
160 matrixBuilder.addTransition(stateProbabilityPair.first, stateProbabilityPair.second);
161 matrixBuilder.finishRow();
162 }
163
164 // Build initial state
165 this->stateStorage.initialStateIndices =
166 generator.getInitialStates(std::bind(&ExplicitDFTModelBuilder::getOrAddStateIndex, this, std::placeholders::_1));
167 STORM_LOG_ASSERT(stateStorage.initialStateIndices.size() == 1, "Only one initial state assumed.");
168 initialStateIndex = stateStorage.initialStateIndices[0];
169 STORM_LOG_TRACE("Initial state: " << initialStateIndex);
170
171 // DFT may be instantly failed due to a constant failure
172 // in this case a model only consisting of the uniqueFailedState suffices
173 if (initialStateIndex == 0 && this->uniqueFailedState) {
174 modelComponents.markovianStates.resize(1);
175 modelComponents.deterministicModel = generator.isDeterministicModel();
176
177 STORM_LOG_TRACE("Markovian states: " << modelComponents.markovianStates);
178 STORM_LOG_DEBUG("Model has 1 state");
179 STORM_LOG_DEBUG("Model is " << (generator.isDeterministicModel() ? "deterministic" : "non-deterministic"));
180
181 // Build transition matrix
182 modelComponents.transitionMatrix = matrixBuilder.builder.build(1, 1);
183 STORM_LOG_TRACE("Transition matrix: \n" << modelComponents.transitionMatrix);
184
185 buildLabeling();
186 return;
187 }
188
189 // Initialize heuristic values for inital state
190 STORM_LOG_ASSERT(!statesNotExplored.at(initialStateIndex).second, "Heuristic for initial state is already initialized");
191 ExplorationHeuristicPointer heuristic;
192 switch (usedHeuristic) {
194 heuristic = std::make_shared<DFTExplorationHeuristicDepth<ValueType>>(initialStateIndex);
195 break;
197 heuristic = std::make_shared<DFTExplorationHeuristicProbability<ValueType>>(initialStateIndex);
198 break;
200 heuristic = std::make_shared<DFTExplorationHeuristicBoundDifference<ValueType>>(initialStateIndex);
201 break;
202 default:
203 STORM_LOG_THROW(false, storm::exceptions::IllegalArgumentException, "Heuristic not known.");
204 }
205 heuristic->markExpand();
206 statesNotExplored[initialStateIndex].second = heuristic;
207 explorationQueue.push(heuristic);
208 } else {
209 initializeNextIteration();
210 }
211
212 if (approximationThreshold > 0.0) {
213 switch (usedHeuristic) {
215 approximationThreshold = iteration + 1;
216 break;
219 approximationThreshold = std::pow(2, -(double)iteration); // Need conversion to avoid overflow when negating
220 break;
221 default:
222 STORM_LOG_THROW(false, storm::exceptions::IllegalArgumentException, "Heuristic not known.");
223 }
224 }
225
226 auto ftSettings = storm::settings::getModule<storm::dft::settings::modules::FaultTreeSettings>();
227 if (ftSettings.isMaxDepthSet()) {
228 STORM_LOG_ASSERT(usedHeuristic == storm::dft::builder::ApproximationHeuristic::DEPTH, "MaxDepth requires 'depth' exploration heuristic.");
229 approximationThreshold = ftSettings.getMaxDepth();
230 }
231
232 exploreStateSpace(approximationThreshold);
233
234 size_t stateSize = stateStorage.getNumberOfStates() + (this->uniqueFailedState ? 1 : 0);
235 modelComponents.markovianStates.resize(stateSize);
236 modelComponents.deterministicModel = generator.isDeterministicModel();
237
238 // Fix the entries in the transition matrix according to the mapping of ids to row group indices
239 STORM_LOG_ASSERT(matrixBuilder.getRemapping(initialStateIndex) == initialStateIndex, "Initial state should not be remapped.");
240 // TODO: do not consider all rows?
241 STORM_LOG_TRACE("Remap matrix: " << matrixBuilder.stateRemapping << ", offset: " << matrixBuilder.mappingOffset);
242 matrixBuilder.remap();
243
244 STORM_LOG_TRACE("State remapping: " << matrixBuilder.stateRemapping);
245 STORM_LOG_TRACE("Markovian states: " << modelComponents.markovianStates);
246 STORM_LOG_DEBUG("Model has " << stateSize << " states");
247 STORM_LOG_DEBUG("Model is " << (generator.isDeterministicModel() ? "deterministic" : "non-deterministic"));
248
249 // Build transition matrix
250 modelComponents.transitionMatrix = matrixBuilder.builder.build(stateSize, stateSize);
251 if (stateSize <= 15) {
252 STORM_LOG_TRACE("Transition matrix: \n" << modelComponents.transitionMatrix);
253 } else {
254 STORM_LOG_TRACE("Transition matrix: too big to print");
255 }
256
257 buildLabeling();
258}
259
260template<typename ValueType, typename StateType>
262 STORM_LOG_TRACE("Refining DFT state space");
263
264 // TODO: should be easier now as skipped states all are at the end of the matrix
265 // Push skipped states to explore queue
266 // TODO: remove
267 for (auto const& skippedState : skippedStates) {
268 statesNotExplored[skippedState.second.first->getId()] = skippedState.second;
269 explorationQueue.push(skippedState.second.second);
270 }
271
272 // Initialize matrix builder again
273 // TODO: avoid copy
274 std::vector<uint_fast64_t> copyRemapping = matrixBuilder.stateRemapping;
275 matrixBuilder = MatrixBuilder(!generator.isDeterministicModel());
276 matrixBuilder.stateRemapping = copyRemapping;
277 StateType nrStates = modelComponents.transitionMatrix.getRowGroupCount();
278 STORM_LOG_ASSERT(nrStates == matrixBuilder.stateRemapping.size(), "No. of states does not coincide with mapping size.");
279
280 // Start by creating a remapping from the old indices to the new indices
281 std::vector<StateType> indexRemapping = std::vector<StateType>(nrStates, 0);
282 auto iterSkipped = skippedStates.begin();
283 size_t skippedBefore = 0;
284 for (size_t i = 0; i < indexRemapping.size(); ++i) {
285 while (iterSkipped != skippedStates.end() && iterSkipped->first <= i) {
286 STORM_LOG_ASSERT(iterSkipped->first < indexRemapping.size(), "Skipped is too high.");
287 ++skippedBefore;
288 ++iterSkipped;
289 }
290 indexRemapping[i] = i - skippedBefore;
291 }
292
293 // Set remapping
294 size_t nrExpandedStates = nrStates - skippedBefore;
295 matrixBuilder.mappingOffset = nrStates;
296 STORM_LOG_TRACE("# expanded states: " << nrExpandedStates);
297 StateType skippedIndex = nrExpandedStates;
298 std::map<StateType, std::pair<DFTStatePointer, ExplorationHeuristicPointer>> skippedStatesNew;
299 for (size_t id = 0; id < matrixBuilder.stateRemapping.size(); ++id) {
300 StateType index = matrixBuilder.getRemapping(id);
301 auto itFind = skippedStates.find(index);
302 if (itFind != skippedStates.end()) {
303 // Set new mapping for skipped state
304 matrixBuilder.stateRemapping[id] = skippedIndex;
305 skippedStatesNew[skippedIndex] = itFind->second;
306 indexRemapping[index] = skippedIndex;
307 ++skippedIndex;
308 } else {
309 // Set new mapping for expanded state
310 matrixBuilder.stateRemapping[id] = indexRemapping[index];
311 }
312 }
313 STORM_LOG_TRACE("New state remapping: " << matrixBuilder.stateRemapping);
314 std::stringstream ss;
315 ss << "Index remapping:\n";
316 for (auto tmp : indexRemapping) {
317 ss << tmp << " ";
318 }
319 STORM_LOG_TRACE(ss.str());
320
321 // Remap markovian states
322 storm::storage::BitVector markovianStatesNew = storm::storage::BitVector(modelComponents.markovianStates.size(), true);
323 // Iterate over all not set bits
324 modelComponents.markovianStates.complement();
325 size_t index = modelComponents.markovianStates.getNextSetIndex(0);
326 while (index < modelComponents.markovianStates.size()) {
327 markovianStatesNew.set(indexRemapping[index], false);
328 index = modelComponents.markovianStates.getNextSetIndex(index + 1);
329 }
330 STORM_LOG_ASSERT(modelComponents.markovianStates.size() - modelComponents.markovianStates.getNumberOfSetBits() == markovianStatesNew.getNumberOfSetBits(),
331 "Remapping of markovian states is wrong.");
332 STORM_LOG_ASSERT(markovianStatesNew.size() == nrStates, "No. of states does not coincide with markovian size.");
333 modelComponents.markovianStates = markovianStatesNew;
334
335 // Build submatrix for expanded states
336 // TODO: only use row groups when necessary
337 for (StateType oldRowGroup = 0; oldRowGroup < modelComponents.transitionMatrix.getRowGroupCount(); ++oldRowGroup) {
338 if (indexRemapping[oldRowGroup] < nrExpandedStates) {
339 // State is expanded -> copy to new matrix
340 matrixBuilder.newRowGroup();
341 for (StateType oldRow = modelComponents.transitionMatrix.getRowGroupIndices()[oldRowGroup];
342 oldRow < modelComponents.transitionMatrix.getRowGroupIndices()[oldRowGroup + 1]; ++oldRow) {
343 for (typename storm::storage::SparseMatrix<ValueType>::const_iterator itEntry = modelComponents.transitionMatrix.begin(oldRow);
344 itEntry != modelComponents.transitionMatrix.end(oldRow); ++itEntry) {
345 auto itFind = skippedStates.find(itEntry->getColumn());
346 if (itFind != skippedStates.end()) {
347 // Set id for skipped states as we remap it later
348 matrixBuilder.addTransition(matrixBuilder.mappingOffset + itFind->second.first->getId(), itEntry->getValue());
349 } else {
350 // Set newly remapped index for expanded states
351 matrixBuilder.addTransition(indexRemapping[itEntry->getColumn()], itEntry->getValue());
352 }
353 }
354 matrixBuilder.finishRow();
355 }
356 }
357 }
358
359 skippedStates = skippedStatesNew;
360
361 STORM_LOG_ASSERT(matrixBuilder.getCurrentRowGroup() == nrExpandedStates, "Row group size does not match.");
362 skippedStates.clear();
363}
364
365template<typename ValueType, typename StateType>
366void ExplicitDFTModelBuilder<ValueType, StateType>::exploreStateSpace(double approximationThreshold) {
367 size_t nrExpandedStates = 0;
368 size_t nrSkippedStates = 0;
369 storm::utility::ProgressMeasurement progress("explored states");
370 progress.startNewMeasurement(0);
371 // TODO: do not empty queue every time but break before
372 while (!explorationQueue.empty()) {
373 // Get the first state in the queue
374 ExplorationHeuristicPointer currentExplorationHeuristic = explorationQueue.pop();
375 StateType currentId = currentExplorationHeuristic->getId();
376 auto itFind = statesNotExplored.find(currentId);
377 STORM_LOG_ASSERT(itFind != statesNotExplored.end(), "Id " << currentId << " not found");
378 DFTStatePointer currentState = itFind->second.first;
379 STORM_LOG_ASSERT(currentExplorationHeuristic == itFind->second.second, "Exploration heuristics do not match");
380 STORM_LOG_ASSERT(currentState->getId() == currentId, "Ids do not match");
381 // Remove it from the list of not explored states
382 statesNotExplored.erase(itFind);
383 STORM_LOG_ASSERT(stateStorage.stateToId.contains(currentState->status()), "State is not contained in state storage.");
384 STORM_LOG_ASSERT(stateStorage.stateToId.getValue(currentState->status()) == currentId, "Ids of states do not coincide.");
385
386 // Get concrete state if necessary
387 if (currentState->isPseudoState()) {
388 // Create concrete state from pseudo state
389 currentState->construct();
390 }
391 STORM_LOG_ASSERT(!currentState->isPseudoState(), "State is pseudo state.");
392
393 // Remember that the current row group was actually filled with the transitions of a different state
394 matrixBuilder.setRemapping(currentId);
395
396 matrixBuilder.newRowGroup();
397
398 // Try to explore the next state
399 generator.load(currentState);
400
401 // if (approximationThreshold > 0.0 && nrExpandedStates > approximationThreshold && !currentExplorationHeuristic->isExpand()) {
402 if (approximationThreshold > 0.0 && currentExplorationHeuristic->isSkip(approximationThreshold)) {
403 // Skip the current state
404 ++nrSkippedStates;
405 STORM_LOG_TRACE("Skip expansion of state: " << dft.getStateString(currentState));
406 setMarkovian(true);
407 // Add transition to target state with temporary value 0
408 // TODO: what to do when there is no unique target state?
409 // STORM_LOG_ASSERT(this->uniqueFailedState, "Approximation only works with unique failed state");
410 matrixBuilder.addTransition(0, storm::utility::zero<ValueType>());
411 // Remember skipped state
412 skippedStates[matrixBuilder.getCurrentRowGroup() - 1] = std::make_pair(currentState, currentExplorationHeuristic);
413 matrixBuilder.finishRow();
414 } else {
415 // Explore the current state
416 ++nrExpandedStates;
418 generator.expand(std::bind(&ExplicitDFTModelBuilder::getOrAddStateIndex, this, std::placeholders::_1));
419 STORM_LOG_ASSERT(!behavior.empty(), "Behavior is empty.");
420 setMarkovian(behavior.begin()->isMarkovian());
421
422 // Now add all choices.
423 for (auto const& choice : behavior) {
424 // Add the probabilistic behavior to the matrix.
425 for (auto const& stateProbabilityPair : choice) {
426 STORM_LOG_ASSERT(!storm::utility::isZero(stateProbabilityPair.second), "Probability zero.");
427 // Set transition to state id + offset. This helps in only remapping all previously skipped states.
428 matrixBuilder.addTransition(matrixBuilder.mappingOffset + stateProbabilityPair.first, stateProbabilityPair.second);
429 // Set heuristic values for reached states
430 auto iter = statesNotExplored.find(stateProbabilityPair.first);
431 if (iter != statesNotExplored.end()) {
432 // Update heuristic values
433 DFTStatePointer state = iter->second.first;
434 if (!iter->second.second) {
435 // Initialize heuristic values
436 ExplorationHeuristicPointer heuristic;
437 switch (usedHeuristic) {
439 heuristic =
440 std::make_shared<DFTExplorationHeuristicDepth<ValueType>>(stateProbabilityPair.first, *currentExplorationHeuristic);
441 break;
443 heuristic = std::make_shared<DFTExplorationHeuristicProbability<ValueType>>(
444 stateProbabilityPair.first, *currentExplorationHeuristic, stateProbabilityPair.second, choice.getTotalMass());
445 break;
447 heuristic = std::make_shared<DFTExplorationHeuristicBoundDifference<ValueType>>(
448 stateProbabilityPair.first, *currentExplorationHeuristic, stateProbabilityPair.second, choice.getTotalMass());
449 break;
450 default:
451 STORM_LOG_THROW(false, storm::exceptions::IllegalArgumentException, "Heuristic not known.");
452 }
453
454 iter->second.second = heuristic;
455 // if (state->hasFailed(dft.getTopLevelIndex()) || state->isFailsafe(dft.getTopLevelIndex()) ||
456 // state->getFailableElements().hasDependencies() || (!state->getFailableElements().hasDependencies() &&
457 // !state->getFailableElements().hasBEs())) {
458 if (state->getFailableElements().hasDependencies() ||
459 (!state->getFailableElements().hasDependencies() && !state->getFailableElements().hasBEs())) {
460 // Do not skip absorbing state or if reached by dependencies
461 iter->second.second->markExpand();
462 }
464 // Compute bounds for heuristic now
465 if (state->isPseudoState()) {
466 // Create concrete state from pseudo state
467 state->construct();
468 }
469 STORM_LOG_ASSERT(!currentState->isPseudoState(), "State is pseudo state.");
470
471 // Initialize bounds
472 // TODO: avoid hack
473 ValueType lowerBound = getLowerBound(state);
474 ValueType upperBound = getUpperBound(state);
475 heuristic->setBounds(lowerBound, upperBound);
476 }
477
478 explorationQueue.push(heuristic);
479 } else if (!iter->second.second->isExpand()) {
480 bool changedPriority = false;
481 double oldPriority = iter->second.second->getPriority();
482 switch (usedHeuristic) {
484 changedPriority = iter->second.second->updateHeuristicValues(*currentExplorationHeuristic,
485 /* next values are irrelevant */ stateProbabilityPair.second,
486 stateProbabilityPair.second);
487 break;
489 changedPriority = iter->second.second->updateHeuristicValues(*currentExplorationHeuristic, stateProbabilityPair.second,
490 choice.getTotalMass());
491 break;
493 changedPriority = iter->second.second->updateHeuristicValues(*currentExplorationHeuristic, stateProbabilityPair.second,
494 choice.getTotalMass());
495 break;
496 default:
497 STORM_LOG_THROW(false, storm::exceptions::IllegalArgumentException, "Heuristic not known.");
498 }
499 if (changedPriority) {
500 // Update priority queue
501 explorationQueue.update(iter->second.second, oldPriority);
502 }
503 }
504 }
505 }
506 matrixBuilder.finishRow();
507 }
508 }
510 break;
511 }
512 // Output number of currently explored states
513 if (nrExpandedStates % 100 == 0) {
514 progress.updateProgress(nrExpandedStates);
515 }
516 } // end exploration
517
518 STORM_LOG_INFO("Expanded " << nrExpandedStates << " states");
519 STORM_LOG_INFO("Skipped " << nrSkippedStates << " states");
520 STORM_LOG_ASSERT(nrSkippedStates == skippedStates.size(), "Nr skipped states is wrong");
521}
522
523template<typename ValueType, typename StateType>
524void ExplicitDFTModelBuilder<ValueType, StateType>::buildLabeling() {
525 bool isAddLabelsClaiming = storm::settings::getModule<storm::dft::settings::modules::FaultTreeSettings>().isAddLabelsClaiming();
526
527 // Build state labeling
528 modelComponents.stateLabeling = storm::models::sparse::StateLabeling(modelComponents.transitionMatrix.getRowGroupCount());
529 // Initial state
530 modelComponents.stateLabeling.addLabel("init");
531 STORM_LOG_ASSERT(matrixBuilder.getRemapping(initialStateIndex) == initialStateIndex, "Initial state should not be remapped.");
532 modelComponents.stateLabeling.addLabelToState("init", initialStateIndex);
533 // System failure
534 modelComponents.stateLabeling.addLabel("failed");
535
536 // Label all states corresponding to their status (failed, don't care BE)
537 // Collect labels for all necessary elements
538 for (size_t id = 0; id < dft.nrElements(); ++id) {
539 std::shared_ptr<storm::dft::storage::elements::DFTElement<ValueType> const> element = dft.getElement(id);
540 if (element->isRelevant()) {
541 modelComponents.stateLabeling.addLabel(element->name() + "_failed");
542 modelComponents.stateLabeling.addLabel(element->name() + "_dc");
543 }
544 }
545 std::vector<std::shared_ptr<storm::dft::storage::elements::DFTGate<ValueType> const>> spares; // Only filled if needed
546 if (isAddLabelsClaiming) {
547 // Collect labels for claiming
548 for (size_t spareId : dft.getSpareIndices()) {
549 auto const& spare = dft.getGate(spareId);
550 spares.push_back(spare);
551 for (auto const& child : spare->children()) {
552 modelComponents.stateLabeling.addLabel(spare->name() + "_claimed_" + child->name());
553 }
554 }
555 }
556
557 // Set labels to states
558 if (this->uniqueFailedState) {
559 // Unique failed state has label 0
560 modelComponents.stateLabeling.addLabelToState("failed", 0);
561 std::shared_ptr<storm::dft::storage::elements::DFTElement<ValueType> const> element = dft.getElement(dft.getTopLevelIndex());
562 STORM_LOG_ASSERT(element->isRelevant(), "TLE should be relevant if unique failed state is used.");
563 modelComponents.stateLabeling.addLabelToState(element->name() + "_failed", 0);
564 }
565 for (auto const& stateIdPair : stateStorage.stateToId) {
566 storm::storage::BitVector state = stateIdPair.first;
567 size_t stateId = matrixBuilder.getRemapping(stateIdPair.second);
568 if (dft.hasFailed(state, *stateGenerationInfo)) {
569 modelComponents.stateLabeling.addLabelToState("failed", stateId);
570 }
571 // Set failed/don't care status for each necessary element
572 for (size_t id = 0; id < dft.nrElements(); ++id) {
573 std::shared_ptr<storm::dft::storage::elements::DFTElement<ValueType> const> element = dft.getElement(id);
574 if (element->isRelevant()) {
576 storm::dft::storage::DFTState<ValueType>::getElementState(state, *stateGenerationInfo, element->id());
577 switch (elementState) {
579 modelComponents.stateLabeling.addLabelToState(element->name() + "_failed", stateId);
580 break;
582 modelComponents.stateLabeling.addLabelToState(element->name() + "_dc", stateId);
583 break;
586 // do nothing
587 break;
588 default:
589 STORM_LOG_ASSERT(false, "Unknown element state " << elementState);
590 }
591 }
592 }
593 if (isAddLabelsClaiming) {
594 for (auto const& spare : spares) {
595 size_t claimedChildId = dft.uses(state, *stateGenerationInfo, spare->id());
596 if (claimedChildId != spare->id()) {
597 modelComponents.stateLabeling.addLabelToState(spare->name() + "_claimed_" + dft.getElement(claimedChildId)->name(), stateId);
598 }
599 }
600 }
601 }
602
603 STORM_LOG_TRACE(modelComponents.stateLabeling);
604}
605
606template<typename ValueType, typename StateType>
607std::shared_ptr<storm::models::sparse::Model<ValueType>> ExplicitDFTModelBuilder<ValueType, StateType>::getModel() {
608 if (storm::settings::getModule<storm::dft::settings::modules::FaultTreeSettings>().isMaxDepthSet() && skippedStates.size() > 0) {
609 // Give skipped states separate label "skipped"
610 modelComponents.stateLabeling.addLabel("skipped");
611 for (auto it = skippedStates.begin(); it != skippedStates.end(); ++it) {
612 modelComponents.stateLabeling.addLabelToState("skipped", it->first);
613 }
614 } else {
615 STORM_LOG_ASSERT(skippedStates.size() == 0, "Concrete model has skipped states");
616 }
617
618 return createModel(false);
619}
620
621template<typename ValueType, typename StateType>
622std::shared_ptr<storm::models::sparse::Model<ValueType>> ExplicitDFTModelBuilder<ValueType, StateType>::getModelApproximation(bool lowerBound,
623 bool expectedTime) {
624 if (expectedTime) {
625 // TODO: handle case with no skipped states
626 changeMatrixBound(modelComponents.transitionMatrix, lowerBound);
627 return createModel(true);
628 } else {
629 // Change model for probabilities
630 // TODO: make nicer
631 storm::storage::SparseMatrix<ValueType> matrix = modelComponents.transitionMatrix;
632 storm::models::sparse::StateLabeling labeling = modelComponents.stateLabeling;
633 if (lowerBound) {
634 // Set self loop for lower bound
635 for (auto it = skippedStates.begin(); it != skippedStates.end(); ++it) {
636 auto matrixEntry = matrix.getRow(it->first, 0).begin();
637 STORM_LOG_ASSERT(matrixEntry->getColumn() == 0, "Transition has wrong target state.");
638 STORM_LOG_ASSERT(!it->second.first->isPseudoState(), "State is still pseudo state.");
639 matrixEntry->setValue(storm::utility::one<ValueType>());
640 matrixEntry->setColumn(it->first);
641 }
642 } else {
643 // Make skipped states failed states for upper bound
644 storm::storage::BitVector failedStates = modelComponents.stateLabeling.getStates("failed");
645 for (auto it = skippedStates.begin(); it != skippedStates.end(); ++it) {
646 failedStates.set(it->first);
647 }
648 labeling.setStates("failed", failedStates);
649 }
650
651 std::shared_ptr<storm::models::sparse::Model<ValueType>> model;
652 if (modelComponents.deterministicModel) {
653 model = std::make_shared<storm::models::sparse::Ctmc<ValueType>>(std::move(matrix), std::move(labeling));
654 } else {
655 // Build MA
656 // Compute exit rates
657 // TODO: avoid computing multiple times
658 modelComponents.exitRates = std::vector<ValueType>(modelComponents.markovianStates.size());
659 std::vector<typename storm::storage::SparseMatrix<ValueType>::index_type> indices = matrix.getRowGroupIndices();
660 for (StateType stateIndex = 0; stateIndex < modelComponents.markovianStates.size(); ++stateIndex) {
661 if (modelComponents.markovianStates[stateIndex]) {
662 modelComponents.exitRates[stateIndex] = matrix.getRowSum(indices[stateIndex]);
663 } else {
664 modelComponents.exitRates[stateIndex] = storm::utility::zero<ValueType>();
665 }
666 }
667 STORM_LOG_TRACE("Exit rates: " << modelComponents.exitRates);
668
669 storm::storage::sparse::ModelComponents<ValueType> maComponents(std::move(matrix), std::move(labeling));
670 maComponents.rateTransitions = true;
671 maComponents.markovianStates = modelComponents.markovianStates;
672 maComponents.exitRates = modelComponents.exitRates;
673 std::shared_ptr<storm::models::sparse::MarkovAutomaton<ValueType>> ma =
674 std::make_shared<storm::models::sparse::MarkovAutomaton<ValueType>>(std::move(maComponents));
675 if (ma->hasOnlyTrivialNondeterminism()) {
676 // Markov automaton can be converted into CTMC
679 } else {
680 model = ma;
681 }
682 }
683
684 if (model->getNumberOfStates() <= 15) {
685 STORM_LOG_TRACE("Transition matrix: \n" << model->getTransitionMatrix());
686 } else {
687 STORM_LOG_TRACE("Transition matrix: too big to print");
688 }
689 return model;
690 }
691}
692
693template<typename ValueType, typename StateType>
694std::shared_ptr<storm::models::sparse::Model<ValueType>> ExplicitDFTModelBuilder<ValueType, StateType>::createModel(bool copy) {
695 std::shared_ptr<storm::models::sparse::Model<ValueType>> model;
696
697 if (modelComponents.deterministicModel) {
698 // Build CTMC
699 if (copy) {
700 model = std::make_shared<storm::models::sparse::Ctmc<ValueType>>(modelComponents.transitionMatrix, modelComponents.stateLabeling);
701 } else {
702 model =
703 std::make_shared<storm::models::sparse::Ctmc<ValueType>>(std::move(modelComponents.transitionMatrix), std::move(modelComponents.stateLabeling));
704 }
705 } else {
706 // Build MA
707 // Compute exit rates
708 // TODO: avoid computing multiple times
709 modelComponents.exitRates = std::vector<ValueType>(modelComponents.markovianStates.size());
710 std::vector<typename storm::storage::SparseMatrix<ValueType>::index_type> indices = modelComponents.transitionMatrix.getRowGroupIndices();
711 for (StateType stateIndex = 0; stateIndex < modelComponents.markovianStates.size(); ++stateIndex) {
712 if (modelComponents.markovianStates[stateIndex]) {
713 modelComponents.exitRates[stateIndex] = modelComponents.transitionMatrix.getRowSum(indices[stateIndex]);
714 } else {
715 modelComponents.exitRates[stateIndex] = storm::utility::zero<ValueType>();
716 }
717 }
718 STORM_LOG_TRACE("Exit rates: " << modelComponents.exitRates);
719
720 std::shared_ptr<storm::models::sparse::MarkovAutomaton<ValueType>> ma;
721 if (copy) {
722 storm::storage::sparse::ModelComponents<ValueType> maComponents(modelComponents.transitionMatrix, modelComponents.stateLabeling);
723 maComponents.rateTransitions = true;
724 maComponents.markovianStates = modelComponents.markovianStates;
725 maComponents.exitRates = modelComponents.exitRates;
726 ma = std::make_shared<storm::models::sparse::MarkovAutomaton<ValueType>>(std::move(maComponents));
727 } else {
728 storm::storage::sparse::ModelComponents<ValueType> maComponents(std::move(modelComponents.transitionMatrix),
729 std::move(modelComponents.stateLabeling));
730 maComponents.rateTransitions = true;
731 maComponents.markovianStates = std::move(modelComponents.markovianStates);
732 maComponents.exitRates = std::move(modelComponents.exitRates);
733 ma = std::make_shared<storm::models::sparse::MarkovAutomaton<ValueType>>(std::move(maComponents));
734 }
735 if (ma->hasOnlyTrivialNondeterminism()) {
736 // Markov automaton can be converted into CTMC
739 } else {
740 model = ma;
741 }
742 }
743
744 if (model->getNumberOfStates() <= 15) {
745 STORM_LOG_TRACE("Transition matrix: \n" << model->getTransitionMatrix());
746 } else {
747 STORM_LOG_TRACE("Transition matrix: too big to print");
748 }
749 return model;
750}
751
752template<typename ValueType, typename StateType>
753void ExplicitDFTModelBuilder<ValueType, StateType>::changeMatrixBound(storm::storage::SparseMatrix<ValueType>& matrix, bool lowerBound) const {
754 // Set lower bound for skipped states
755 for (auto it = skippedStates.begin(); it != skippedStates.end(); ++it) {
756 auto matrixEntry = matrix.getRow(it->first, 0).begin();
757 STORM_LOG_ASSERT(matrixEntry->getColumn() == 0, "Transition has wrong target state.");
758 STORM_LOG_ASSERT(!it->second.first->isPseudoState(), "State is still pseudo state.");
759
760 // Change bound
761 // TODO: cache values inbetween iterations
762 if (lowerBound) {
763 matrixEntry->setValue(getLowerBound(it->second.first));
764 } else {
765 matrixEntry->setValue(getUpperBound(it->second.first));
766 }
767 }
768}
769
770template<typename ValueType, typename StateType>
771ValueType ExplicitDFTModelBuilder<ValueType, StateType>::getLowerBound(DFTStatePointer const& state) const {
772 // Get the lower bound by considering the failure of all possible BEs
773 ValueType lowerBound = storm::utility::zero<ValueType>();
774 STORM_LOG_ASSERT(!state->getFailableElements().hasDependencies(), "Lower bound should only be computed if dependencies were already handled.");
775 for (auto it = state->getFailableElements().begin(); it != state->getFailableElements().end(); ++it) {
776 lowerBound += state->getBERate(*it);
777 }
778 STORM_LOG_TRACE("Lower bound is " << lowerBound << " for state " << state->getId());
779 return lowerBound;
780}
781
782template<typename ValueType, typename StateType>
783ValueType ExplicitDFTModelBuilder<ValueType, StateType>::getUpperBound(DFTStatePointer const& state) const {
784 if (state->hasFailed(dft.getTopLevelIndex())) {
785 return storm::utility::zero<ValueType>();
786 }
787
788 // Get the upper bound by considering the failure of all BEs
789 ValueType upperBound = storm::utility::one<ValueType>();
790 ValueType rateSum = storm::utility::zero<ValueType>();
791
792 // Compute for each independent subtree
793 for (std::vector<size_t> const& subtree : subtreeBEs) {
794 // Get all possible rates
795 std::vector<ValueType> rates;
796 storm::storage::BitVector coldBEs(subtree.size(), false);
797 for (size_t i = 0; i < subtree.size(); ++i) {
798 size_t id = subtree[i];
799 // Consider only still operational BEs
800 if (state->isOperational(id)) {
801 auto be = dft.getBasicElement(id);
802 switch (be->beType()) {
804 // Ignore BE which cannot fail
805 continue;
807 // Get BE rate
808 ValueType rate = state->getBERate(id);
809 if (storm::utility::isZero<ValueType>(rate)) {
810 // Get active failure rate for cold BE
811 auto beExp = std::static_pointer_cast<storm::dft::storage::elements::BEExponential<ValueType> const>(be);
812 rate = beExp->activeFailureRate();
813 STORM_LOG_ASSERT(!storm::utility::isZero<ValueType>(rate), "Failure rate should not be zero.");
814 // Mark BE as cold
815 coldBEs.set(i, true);
816 }
817 rates.push_back(rate);
818 rateSum += rate;
819 break;
820 }
821 default:
822 STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "BE of type '" << be->beType() << "' is not known.");
823 break;
824 }
825 }
826 }
827 STORM_LOG_ASSERT(rates.size() > 0, "No rates failable");
828
829 // Sort rates
830 std::sort(rates.begin(), rates.end());
831 std::vector<size_t> rateCount(rates.size(), 0);
832 size_t lastIndex = 0;
833 for (size_t i = 0; i < rates.size(); ++i) {
834 if (rates[lastIndex] != rates[i]) {
835 lastIndex = i;
836 }
837 ++rateCount[lastIndex];
838 }
839
840 for (size_t i = 0; i < rates.size(); ++i) {
841 // Cold BEs cannot fail in the first step
842 if (!coldBEs.get(i) && rateCount[i] > 0) {
843 std::iter_swap(rates.begin() + i, rates.end() - 1);
844 // Compute AND MTTF of subtree without current rate and scale with current rate
845 upperBound += rates.back() * rateCount[i] * computeMTTFAnd(rates, rates.size() - 1);
846 // Swap back
847 // TODO try to avoid swapping back
848 std::iter_swap(rates.begin() + i, rates.end() - 1);
849 }
850 }
851 }
852
853 STORM_LOG_TRACE("Upper bound is " << (rateSum / upperBound) << " for state " << state->getId());
854 STORM_LOG_ASSERT(!storm::utility::isZero(upperBound), "Upper bound is 0");
855 STORM_LOG_ASSERT(!storm::utility::isZero(rateSum), "State is absorbing");
856 return rateSum / upperBound;
857}
858
859template<typename ValueType, typename StateType>
860ValueType ExplicitDFTModelBuilder<ValueType, StateType>::computeMTTFAnd(std::vector<ValueType> const& rates, size_t size) const {
861 ValueType result = storm::utility::zero<ValueType>();
862 if (size == 0) {
863 return result;
864 }
865
866 // TODO: works only for <64 BEs!
867 // WARNING: this code produces wrong results for more than 32 BEs
868 /*for (size_t i = 1; i < 4 && i <= rates.size(); ++i) {
869 size_t permutation = smallestIntWithNBitsSet(static_cast<size_t>(i));
870 ValueType sum = storm::utility::zero<ValueType>();
871 do {
872 ValueType permResult = storm::utility::zero<ValueType>();
873 for(size_t j = 0; j < rates.size(); ++j) {
874 if(permutation & static_cast<size_t>(1 << static_cast<size_t>(j))) {
875 // WARNING: if the first bit is set, it also recognizes the 32nd bit as set
876 // TODO: fix
877 permResult += rates[j];
878 }
879 }
880 permutation = nextBitPermutation(permutation);
881 STORM_LOG_ASSERT(!storm::utility::isZero(permResult), "PermResult is 0");
882 sum += storm::utility::one<ValueType>() / permResult;
883 } while(permutation < (static_cast<size_t>(1) << rates.size()) && permutation != 0);
884 if (i % 2 == 0) {
885 result -= sum;
886 } else {
887 result += sum;
888 }
889 }*/
890
891 // Compute result with permutations of size <= 3
892 ValueType one = storm::utility::one<ValueType>();
893 for (size_t i1 = 0; i1 < size; ++i1) {
894 // + 1/a
895 ValueType sum = rates[i1];
896 result += one / sum;
897 for (size_t i2 = 0; i2 < i1; ++i2) {
898 // - 1/(a+b)
899 ValueType sum2 = sum + rates[i2];
900 result -= one / sum2;
901 for (size_t i3 = 0; i3 < i2; ++i3) {
902 // + 1/(a+b+c)
903 result += one / (sum2 + rates[i3]);
904 }
905 }
906 }
907
908 STORM_LOG_ASSERT(!storm::utility::isZero(result), "UpperBound is 0");
909 return result;
910}
911
912template<typename ValueType, typename StateType>
913StateType ExplicitDFTModelBuilder<ValueType, StateType>::getOrAddStateIndex(DFTStatePointer const& state) {
914 StateType stateId;
915 bool changed = false;
916
917 if (stateGenerationInfo->hasSymmetries()) {
918 // Order state by symmetry
919 STORM_LOG_TRACE("Check for symmetry: " << dft.getStateString(state));
920 changed = state->orderBySymmetry();
921 STORM_LOG_TRACE("State " << (changed ? "changed to " : "did not change") << (changed ? dft.getStateString(state) : ""));
922 }
923
924 if (stateStorage.stateToId.contains(state->status())) {
925 // State already exists
926 stateId = stateStorage.stateToId.getValue(state->status());
927 STORM_LOG_TRACE("State " << dft.getStateString(state) << " with id " << stateId << " already exists");
928 if (!changed) {
929 // Check if state is pseudo state
930 // If state is explored already the possible pseudo state was already constructed
931 auto iter = statesNotExplored.find(stateId);
932 if (iter != statesNotExplored.end() && iter->second.first->isPseudoState()) {
933 // Create pseudo state now
934 STORM_LOG_ASSERT(iter->second.first->getId() == stateId, "Ids do not match.");
935 STORM_LOG_ASSERT(iter->second.first->status() == state->status(), "Pseudo states do not coincide.");
936 state->setId(stateId);
937 // Update mapping to map to concrete state now
938 // TODO: just change pointer?
939 statesNotExplored[stateId] = std::make_pair(state, iter->second.second);
940 // We do not push the new state on the exploration queue as the pseudo state was already pushed
941 STORM_LOG_TRACE("Created pseudo state " << dft.getStateString(state));
942 }
943 }
944 } else {
945 // State does not exist yet
946 STORM_LOG_ASSERT(state->isPseudoState() == changed, "State type (pseudo/concrete) wrong.");
947 // Create new state
948 state->setId(newIndex++);
949 stateId = stateStorage.stateToId.findOrAdd(state->status(), state->getId());
950 STORM_LOG_ASSERT(stateId == state->getId(), "Ids do not match.");
951 // Insert state as not yet explored
952 ExplorationHeuristicPointer nullHeuristic;
953 statesNotExplored[stateId] = std::make_pair(state, nullHeuristic);
954 // Reserve one slot for the new state in the remapping
955 matrixBuilder.stateRemapping.push_back(0);
956 STORM_LOG_TRACE("New " << (state->isPseudoState() ? "pseudo" : "concrete") << " state: " << dft.getStateString(state));
957 }
958 return stateId;
959}
960
961template<typename ValueType, typename StateType>
962void ExplicitDFTModelBuilder<ValueType, StateType>::setMarkovian(bool markovian) {
963 if (matrixBuilder.getCurrentRowGroup() > modelComponents.markovianStates.size()) {
964 // Resize BitVector
965 modelComponents.markovianStates.resize(modelComponents.markovianStates.size() + INITIAL_BITVECTOR_SIZE);
966 }
967 modelComponents.markovianStates.set(matrixBuilder.getCurrentRowGroup() - 1, markovian);
968}
969
970template<typename ValueType, typename StateType>
971void ExplicitDFTModelBuilder<ValueType, StateType>::printNotExplored() const {
972 std::cout << "states not explored:\n";
973 for (auto it : statesNotExplored) {
974 std::cout << it.first << " -> " << dft.getStateString(it.second.first) << '\n';
975 }
976}
977
978// Explicitly instantiate the class.
979template class ExplicitDFTModelBuilder<double>;
980template class ExplicitDFTModelBuilder<storm::RationalFunction>;
981
982} // namespace builder
983} // namespace storm::dft
std::shared_ptr< storm::models::sparse::Model< ValueType > > getModelApproximation(bool lowerBound, bool expectedTime)
Get the built approximation model for either the lower or upper bound.
std::shared_ptr< storm::models::sparse::Model< ValueType > > getModel()
Get the built model.
void buildModel(size_t iteration, double approximationThreshold=0.0, storm::dft::builder::ApproximationHeuristic approximationHeuristic=storm::dft::builder::ApproximationHeuristic::DEPTH)
Build model from DFT.
Priority queue based on buckets.
Represents a Dynamic Fault Tree.
Definition DFT.h:52
size_t getTopLevelIndex() const
Definition DFT.h:105
std::string getRelevantEventsString() const
Get a string containing the list of all relevant events.
Definition DFT.cpp:716
std::shared_ptr< storm::dft::storage::elements::DFTBE< ValueType > const > getBasicElement(size_t index) const
Definition DFT.h:208
storm::dft::storage::elements::DFTElementType getTopLevelType() const
Definition DFT.h:109
size_t nrElements() const
Definition DFT.h:93
std::shared_ptr< storm::dft::storage::elements::DFTGate< ValueType > const > getGate(size_t index) const
Definition DFT.h:217
bool isGate(size_t index) const
Definition DFT.h:196
std::vector< size_t > const & getRelevantEvents() const
Get all relevant events.
Definition DFT.cpp:711
bool isBasicElement(size_t index) const
Definition DFT.h:192
DFTElementState getElementState(size_t id) const
Definition DFTState.cpp:111
bool empty() const
Retrieves whether the behavior is empty in the sense that there are no available choices.
std::vector< Choice< ValueType, StateType > >::const_iterator begin() const
Retrieves an iterator to the choices available in the behavior.
std::size_t getNumberOfChoices() const
Retrieves the number of choices in the behavior.
This class manages the labeling of the state space with a number of (atomic) labels.
void setStates(std::string const &label, storage::BitVector const &labeling)
Sets the labeling of states associated with the given label.
A bit vector that is internally represented as a vector of 64-bit values.
Definition BitVector.h:16
uint64_t getNumberOfSetBits() const
Returns the number of bits that are set to true in this bit vector.
void set(uint64_t index, bool value=true)
Sets the given truth value at the given index.
size_t size() const
Retrieves the number of bits this bit vector can store.
const_iterator begin() const
Retrieves an iterator that points to the beginning of the rows.
A class that can be used to build a sparse matrix by adding value by value.
A class that holds a possibly non-square matrix in the compressed row storage format.
const_rows getRow(index_type row) const
Returns an object representing the given row.
value_type getRowSum(index_type row) const
Computes the sum of the entries in a given row.
std::vector< MatrixEntry< index_type, value_type > >::const_iterator const_iterator
std::vector< index_type > const & getRowGroupIndices() const
Returns the grouping of rows of this matrix.
static std::shared_ptr< models::sparse::Model< ValueType, RewardModelType > > eliminateNonmarkovianStates(std::shared_ptr< models::sparse::MarkovAutomaton< ValueType, RewardModelType > > ma, EliminationLabelBehavior labelBehavior=EliminationLabelBehavior::KeepLabels)
Generates a model with the same basic behavior as the input, but eliminates non-Markovian chains.
A class that provides convenience operations to display run times.
#define STORM_LOG_INFO(message)
Definition logging.h:24
#define STORM_LOG_WARN(message)
Definition logging.h:25
#define STORM_LOG_DEBUG(message)
Definition logging.h:18
#define STORM_LOG_TRACE(message)
Definition logging.h:12
#define STORM_LOG_ASSERT(cond, message)
Definition macros.h:11
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:30
ApproximationHeuristic
Enum representing the heuristic used for deciding which states to expand.
SFTBDDChecker::ValueType ValueType
Expression sum(std::vector< storm::expressions::Expression > const &expressions)
bool isTerminate()
Check whether the program should terminate (due to some abort signal).
bool isZero(ValueType const &a)
Definition constants.cpp:39
ValueType one()
Definition constants.cpp:19
boost::optional< storm::storage::BitVector > markovianStates
boost::optional< std::vector< ValueType > > exitRates