Storm 1.14.0.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
OrderExtender.cpp
Go to the documentation of this file.
2#include <algorithm>
3#include <vector>
4
10#include "storm/utility/graph.h"
12
18
20
21namespace storm {
22namespace analysis {
23
24template<typename ValueType, typename ConstantType>
25OrderExtender<ValueType, ConstantType>::OrderExtender(std::shared_ptr<models::sparse::Model<ValueType>> model, std::shared_ptr<logic::Formula const> formula)
26 : monotonicityChecker(MonotonicityChecker<ValueType>(model->getTransitionMatrix())) {
27 this->model = model;
28 this->matrix = model->getTransitionMatrix();
29 this->numberOfStates = this->model->getNumberOfStates();
30 this->formula = formula;
31 this->assumptionMaker = std::make_unique<analysis::AssumptionMaker<ValueType, ConstantType>>(matrix);
32}
33
34template<typename ValueType, typename ConstantType>
37 : monotonicityChecker(MonotonicityChecker<ValueType>(matrix)) {
38 this->matrix = matrix;
39 this->model = nullptr;
40 this->monotonicityChecker = MonotonicityChecker<ValueType>(matrix);
41 this->numberOfStates = matrix.getColumnCount();
42
43 this->bottomTopOrder = computeInitialOrder(topStates, bottomStates, matrix, /*addStatesWithDirectBoundaryTransition=*/true);
44
45 this->assumptionMaker = std::make_unique<analysis::AssumptionMaker<ValueType, ConstantType>>(matrix);
46}
47
48template<typename ValueType, typename ConstantType>
49std::shared_ptr<Order> OrderExtender<ValueType, ConstantType>::computeInitialOrder(storm::storage::BitVector const& topStates,
50 storm::storage::BitVector const& bottomStates,
52 bool addStatesWithDirectBoundaryTransition) {
54 options.forceTopologicalSort();
55
56 std::vector<uint64_t> firstStates;
57 storm::storage::BitVector subStates(topStates.size(), true);
58 for (auto state : topStates) {
59 firstStates.push_back(state);
60 subStates.set(state, false);
61 }
62 for (auto state : bottomStates) {
63 firstStates.push_back(state);
64 subStates.set(state, false);
65 }
66 cyclic = storm::utility::graph::hasCycle(matrix, subStates);
68 if (cyclic) {
70 }
71
72 auto statesSorted = storm::utility::graph::getTopologicalSort(matrix.transpose(), firstStates);
73 auto order = std::make_shared<Order>(topStates, bottomStates, numberOfStates, std::move(decomposition), std::move(statesSorted));
74
75 // Build stateMap
76 for (uint_fast64_t state = 0; state < numberOfStates; ++state) {
77 auto const& row = matrix.getRow(state);
78 stateMap[state] = std::vector<uint_fast64_t>();
79 std::set<VariableType> occurringVariables;
80
81 for (auto& entry : matrix.getRow(state)) {
82 // ignore self-loops when there are more transitions
83 if (state != entry.getColumn() || row.getNumberOfEntries() == 1) {
84 if (addStatesWithDirectBoundaryTransition && !subStates[entry.getColumn()] && !order->contains(state)) {
85 order->add(state);
86 }
87 stateMap[state].push_back(entry.getColumn());
88 }
89 storm::utility::parametric::gatherOccurringVariables(entry.getValue(), occurringVariables);
90 }
91 if (occurringVariables.empty()) {
92 nonParametricStates.insert(state);
93 }
94
95 for (auto& var : occurringVariables) {
96 occuringStatesAtVariable[var].push_back(state);
97 }
98 occuringVariablesAtState.push_back(std::move(occurringVariables));
99 }
100
101 return order;
102}
103
104template<typename ValueType, typename ConstantType>
105std::shared_ptr<Order> OrderExtender<ValueType, ConstantType>::getBottomTopOrder() {
106 if (bottomTopOrder == nullptr) {
107 STORM_LOG_ASSERT(model != nullptr, "Cannot lazily build the bottom-top order without a model.");
108 STORM_LOG_THROW(matrix.getRowCount() == matrix.getColumnCount(), exceptions::NotSupportedException,
109 "Creating order not supported for non-square matrix");
110 modelchecker::SparsePropositionalModelChecker<models::sparse::Model<ValueType>> propositionalChecker(*model);
111 storage::BitVector phiStates;
112 storage::BitVector psiStates;
113 STORM_LOG_ASSERT(formula->isProbabilityOperatorFormula(), "Expected a probability operator formula.");
114 if (formula->asProbabilityOperatorFormula().getSubformula().isUntilFormula()) {
115 phiStates = propositionalChecker.check(formula->asProbabilityOperatorFormula().getSubformula().asUntilFormula().getLeftSubformula())
116 ->template asExplicitQualitativeCheckResult<ValueType>()
117 .getTruthValuesVector();
118 psiStates = propositionalChecker.check(formula->asProbabilityOperatorFormula().getSubformula().asUntilFormula().getRightSubformula())
119 ->template asExplicitQualitativeCheckResult<ValueType>()
120 .getTruthValuesVector();
121 } else {
122 STORM_LOG_ASSERT(formula->asProbabilityOperatorFormula().getSubformula().isEventuallyFormula(), "Expected an eventually formula.");
123 phiStates = storage::BitVector(numberOfStates, true);
124 psiStates = propositionalChecker.check(formula->asProbabilityOperatorFormula().getSubformula().asEventuallyFormula().getSubformula())
125 ->template asExplicitQualitativeCheckResult<ValueType>()
126 .getTruthValuesVector();
127 }
128 // Get the maybeStates
129 std::pair<storage::BitVector, storage::BitVector> statesWithProbability01 =
130 utility::graph::performProb01(this->model->getBackwardTransitions(), phiStates, psiStates);
131 storage::BitVector topStates = statesWithProbability01.second;
132 storage::BitVector bottomStates = statesWithProbability01.first;
133
134 STORM_LOG_THROW(topStates.begin() != topStates.end(), exceptions::NotSupportedException, "Formula yields to no 1 states");
135 STORM_LOG_THROW(bottomStates.begin() != bottomStates.end(), exceptions::NotSupportedException, "Formula yields to no zero states");
136
137 // Unlike the BitVector-matrix constructor, this path does not pre-add states with a direct
138 // transition to a top/bottom state to the order ahead of the main construction loop.
139 bottomTopOrder = computeInitialOrder(topStates, bottomStates, this->model->getTransitionMatrix(), /*addStatesWithDirectBoundaryTransition=*/false);
140 }
141
142 if (minValuesInit && maxValuesInit) {
143 auto& ctx = context(bottomTopOrder);
144 ctx.continueExtending = true;
145 ctx.usePLA = true;
146 ctx.minValues = std::move(minValuesInit.get());
147 ctx.maxValues = std::move(maxValuesInit.get());
148 // The bounds are moved out, so they have to be set again before the next installation.
149 minValuesInit = boost::none;
150 maxValuesInit = boost::none;
151 }
152 return bottomTopOrder;
153}
154
155template<typename ValueType, typename ConstantType>
156typename OrderExtender<ValueType, ConstantType>::Context& OrderExtender<ValueType, ConstantType>::context(std::shared_ptr<Order> const& order) {
157 auto result = contexts.try_emplace(order);
158 if (result.second) {
159 result.first->second.unknownStates = {numberOfStates, numberOfStates};
160 // The map is keyed by weak_ptr, so entries whose orders have been destroyed are not removed
161 // automatically. Reclaim them once the map has grown enough for the scan to be worthwhile.
162 if (contexts.size() >= nextContextPruneSize) {
163 for (auto it = contexts.begin(); it != contexts.end();) {
164 if (it->first.expired()) {
165 it = contexts.erase(it);
166 } else {
167 ++it;
168 }
169 }
170 nextContextPruneSize = std::max<std::size_t>(64, contexts.size() * 2);
171 }
172 }
173 return result.first->second;
174}
175
176template<typename ValueType, typename ConstantType>
177typename OrderExtender<ValueType, ConstantType>::Context const& OrderExtender<ValueType, ConstantType>::contextAt(std::shared_ptr<Order> const& order) const {
178 auto it = contexts.find(order);
179 STORM_LOG_ASSERT(it != contexts.end(), "No context set for this order.");
180 return it->second;
181}
182
183template<typename ValueType, typename ConstantType>
184std::tuple<std::shared_ptr<Order>, uint_fast64_t, uint_fast64_t> OrderExtender<ValueType, ConstantType>::toOrder(
186 return this->extendOrder(nullptr, region, monRes, std::nullopt);
187}
188
189template<typename ValueType, typename ConstantType>
190void OrderExtender<ValueType, ConstantType>::handleAssumption(std::shared_ptr<Order> order, Assumption const& assumption) const {
191 uint_fast64_t val1 = assumption.state1;
192 uint_fast64_t val2 = assumption.state2;
193
194 STORM_LOG_ASSERT(order->compare(val1, val2) == Order::UNKNOWN, "The assumption's states are already ordered; handling it again is redundant.");
195
196 Order::Node* n1 = order->getNode(val1);
197 Order::Node* n2 = order->getNode(val2);
198
199 if (assumption.relation == AssumptionRelation::Equal) {
200 if (n1 != nullptr && n2 != nullptr) {
201 order->mergeNodes(n1, n2);
202 } else if (n1 != nullptr) {
203 order->addToNode(val2, n1);
204 } else if (n2 != nullptr) {
205 order->addToNode(val1, n2);
206 } else {
207 order->add(val1);
208 order->addToNode(val2, order->getNode(val1));
209 }
210 } else {
211 if (n1 != nullptr && n2 != nullptr) {
212 order->addRelationNodes(n1, n2);
213 } else if (n1 != nullptr) {
214 order->addBetween(val2, n1, order->getBottom());
215 } else if (n2 != nullptr) {
216 order->addBetween(val1, order->getTop(), n2);
217 } else {
218 order->add(val1);
219 order->addBetween(val2, order->getNode(val1), order->getBottom());
220 }
221 }
222}
223
224template<typename ValueType, typename ConstantType>
225std::tuple<std::shared_ptr<Order>, uint_fast64_t, uint_fast64_t> OrderExtender<ValueType, ConstantType>::extendOrder(
226 std::shared_ptr<Order> order, storm::storage::ParameterRegion<ValueType> region, std::shared_ptr<MonotonicityResult<VariableType>> monRes,
227 std::optional<Assumption> assumption) {
228 this->region = region;
229 if (order == nullptr) {
230 order = getBottomTopOrder();
231 auto& initialContext = context(order);
232 if (initialContext.usePLA) {
233 auto& min = initialContext.minValues;
234 auto& max = initialContext.maxValues;
235 // Try to make the order as complete as possible based on pla results
236 auto& statesSorted = order->getStatesSorted();
237 auto itr = statesSorted.begin();
238 while (itr != statesSorted.end()) {
239 auto state = *itr;
240 auto& successors = stateMap[state];
241 bool all = true;
242 for (uint_fast64_t i = 0; i < successors.size(); ++i) {
243 auto state1 = successors[i];
244 for (uint_fast64_t j = i + 1; j < successors.size(); ++j) {
245 auto state2 = successors[j];
246 if (min[state1] > max[state2]) {
247 if (!order->contains(state1)) {
248 order->add(state1);
249 }
250 if (!order->contains(state2)) {
251 order->add(state2);
252 }
253 order->addRelation(state1, state2, false);
254 } else if (min[state2] > max[state1]) {
255 if (!order->contains(state1)) {
256 order->add(state1);
257 }
258 if (!order->contains(state2)) {
259 order->add(state2);
260 }
261 order->addRelation(state2, state1, false);
262 } else if (min[state1] == max[state2] && max[state1] == min[state2]) {
263 if (!order->contains(state1) && !order->contains(state2)) {
264 order->add(state1);
265 order->addToNode(state2, order->getNode(state1));
266 } else if (!order->contains(state1)) {
267 order->addToNode(state1, order->getNode(state2));
268 } else if (!order->contains(state2)) {
269 order->addToNode(state2, order->getNode(state1));
270 } else if (!order->merge(state1, state2)) {
271 // The PLA-bound-based conclusion that state1 and state2 are equal
272 // contradicts what the order already knows about them. Bail out here
273 // rather than continuing to build on top of an inconsistent order.
274 return std::make_tuple(order, numberOfStates, numberOfStates);
275 }
276 } else {
277 all = false;
278 }
279 }
280 }
281 if (all) {
282 STORM_LOG_INFO("All successors of state " << state << " sorted based on min max values");
283 }
284 ++itr;
285 }
286 }
287 initialContext.continueExtending = true;
288 }
289 auto& ctx = context(order);
290 if (ctx.continueExtending || assumption.has_value()) {
291 return extendOrder(order, monRes, assumption);
292 } else {
293 auto res = ctx.unknownStates;
294 ctx.continueExtending = false;
295 return {order, res.first, res.second};
296 }
297}
298
299template<typename ValueType, typename ConstantType>
300std::tuple<std::shared_ptr<Order>, uint_fast64_t, uint_fast64_t> OrderExtender<ValueType, ConstantType>::extendOrder(
301 std::shared_ptr<Order> order, std::shared_ptr<MonotonicityResult<VariableType>> monRes, std::optional<Assumption> assumption) {
302 if (assumption.has_value()) {
303 STORM_LOG_INFO("Handling assumption " << *assumption << '\n');
304 handleAssumption(order, *assumption);
305 if (order->isInvalid()) {
306 // The assumption led to a mathematically inconsistent order (a merge of two states that
307 // should stay distinct). Bail out here rather than continuing to build on top of it.
308 return std::make_tuple(order, numberOfStates, numberOfStates);
309 }
310 }
311
312 auto currentStateMode = getNextState(order, numberOfStates, false);
313 while (currentStateMode.first != numberOfStates) {
314 STORM_LOG_ASSERT(currentStateMode.first < numberOfStates, "Expected a valid state, not the sentinel value.");
315 auto& currentState = currentStateMode.first;
316 auto& successors = stateMap[currentState];
317 std::pair<uint_fast64_t, uint_fast64_t> result = {numberOfStates, numberOfStates};
318
319 if (successors.size() == 1) {
320 STORM_LOG_ASSERT(order->contains(successors[0]), "The single successor of a state must already be in the order.");
321 handleOneSuccessor(order, currentState, successors[0]);
322 } else if (!successors.empty()) {
323 if (order->isOnlyBottomTopOrder()) {
324 order->add(currentState);
325 if (!order->isTrivial(currentState)) {
326 // This state is part of an scc, therefore, we could do forward reasoning here
327 result = extendByForwardReasoning(order, currentState, successors, assumption.has_value());
328 } else {
329 result = {numberOfStates, numberOfStates};
330 }
331 } else {
332 result = extendNormal(order, currentState, successors, assumption.has_value());
333 }
334 }
335
336 if (order->isInvalid()) {
337 // A merge performed while extending this state (directly, or via forward/backward
338 // reasoning) produced a mathematically inconsistent order. Bail out here rather than
339 // continuing to build on top of it.
340 return std::make_tuple(order, numberOfStates, numberOfStates);
341 }
342
343 if (result.first == numberOfStates) {
344 // We did extend the order
345 STORM_LOG_ASSERT(result.second == numberOfStates, "Expected both entries of result to be the sentinel value.");
346 STORM_LOG_ASSERT(order->sortStates(&successors).size() == successors.size(), "Expected all successors to be sortable at this point.");
347 STORM_LOG_ASSERT(order->contains(currentState) && order->getNode(currentState) != nullptr,
348 "The current state should have been placed in the order.");
349
350 if (monRes != nullptr) {
351 for (auto& param : occuringVariablesAtState[currentState]) {
352 checkParOnStateMonRes(currentState, order, param, monRes);
353 }
354 }
355 // Get the next state
356 currentStateMode = getNextState(order, currentState, true);
357 } else {
358 STORM_LOG_ASSERT(result.first < numberOfStates, "Expected a valid state, not the sentinel value.");
359 STORM_LOG_ASSERT(result.second < numberOfStates, "Expected a valid state, not the sentinel value.");
360 STORM_LOG_ASSERT(order->compare(result.first, result.second) == Order::UNKNOWN, "Expected the unresolved pair to indeed be unordered.");
361 STORM_LOG_ASSERT(order->compare(result.second, result.first) == Order::UNKNOWN, "Expected the unresolved pair to indeed be unordered.");
362 // Try to add states based on min/max and assumptions, only if we are not in statesToHandle mode
363 if (currentStateMode.second && extendByAssumption(order, result.first, result.second)) {
364 if (order->isInvalid()) {
365 return std::make_tuple(order, numberOfStates, numberOfStates);
366 }
367 continue;
368 }
369 // We couldn't extend the order
370 if (nonParametricStates.find(currentState) != nonParametricStates.end()) {
371 if (!order->contains(currentState)) {
372 // State is not parametric, so we hope that just adding it between =) and =( will help us
373 order->add(currentState);
374 }
375 currentStateMode = getNextState(order, currentState, true);
376 continue;
377 } else {
378 if (!currentStateMode.second) {
379 // The state was based on statesToHandle, so it is not bad if we cannot continue with this.
380 currentStateMode = getNextState(order, currentState, false);
381 continue;
382 } else {
383 // The state was based on the topological sorting, so we need to return, but first add this state to the states Sorted as we are not done
384 // with it
385 order->addStateSorted(currentState);
386 context(order).continueExtending = false;
387 return {order, result.first, result.second};
388 }
389 }
390 }
391 }
392
393 STORM_LOG_ASSERT(order->getDoneBuilding(), "Expected the order to be fully built at this point.");
394 if (monRes != nullptr) {
395 // monotonicity result for the in-build checking of monotonicity
396 monRes->setDone();
397 }
398 return std::make_tuple(order, numberOfStates, numberOfStates);
399}
400
401template<typename ValueType, typename ConstantType>
402std::pair<uint_fast64_t, uint_fast64_t> OrderExtender<ValueType, ConstantType>::extendNormal(std::shared_ptr<Order> order, uint_fast64_t currentState,
403 const std::vector<uint_fast64_t>& successors, bool allowMerge) {
404 // when it is cyclic and the current state is part of an SCC we do forwardreasoning
405 if (cyclic && !order->isTrivial(currentState) && order->contains(currentState)) {
406 // Try to extend the order for this scc
407 return extendByForwardReasoning(order, currentState, successors, allowMerge);
408 } else {
409 STORM_LOG_ASSERT(order->isTrivial(currentState) || !order->contains(currentState),
410 "A non-trivial (SCC) state that is already in the order should use forward reasoning, not backward reasoning.");
411 // Do backward reasoning, all successor states must be in the order
412 return extendByBackwardReasoning(order, currentState, successors, allowMerge);
413 }
414}
415
416template<typename ValueType, typename ConstantType>
417void OrderExtender<ValueType, ConstantType>::handleOneSuccessor(std::shared_ptr<Order> order, uint_fast64_t currentState, uint_fast64_t successor) {
418 STORM_LOG_ASSERT(order->contains(successor), "The successor must already be in the order.");
419 if (currentState != successor) {
420 if (order->contains(currentState)) {
421 order->merge(currentState, successor);
422 } else {
423 order->addToNode(currentState, order->getNode(successor));
424 }
425 }
426}
427
428template<typename ValueType, typename ConstantType>
429std::pair<uint_fast64_t, uint_fast64_t> OrderExtender<ValueType, ConstantType>::extendByBackwardReasoning(std::shared_ptr<Order> order,
430 uint_fast64_t currentState,
431 std::vector<uint_fast64_t> const& successors,
432 bool allowMerge) {
433 STORM_LOG_ASSERT(!order->isOnlyBottomTopOrder(), "Backward reasoning requires the order to have grown beyond just top and bottom.");
434 STORM_LOG_ASSERT(successors.size() > 1, "Backward reasoning is only needed for states with more than one successor.");
435
436 auto& ctx = context(order);
437 bool pla = ctx.usePLA;
438 std::vector<uint_fast64_t> sortedSuccs;
439
440 if (pla && ctx.continueExtending) {
441 for (auto& state1 : successors) {
442 if (sortedSuccs.size() == 0) {
443 sortedSuccs.push_back(state1);
444 } else {
445 bool added = false;
446 for (auto itr = sortedSuccs.begin(); itr != sortedSuccs.end(); ++itr) {
447 auto& state2 = *itr;
448 auto compareRes = order->compareFast(state1, state2);
449 if (compareRes == Order::NodeComparison::UNKNOWN) {
450 compareRes = addStatesBasedOnMinMax(order, state1, state2);
451 }
452 if (compareRes == Order::NodeComparison::UNKNOWN) {
453 // If fast comparison did not help, we continue by checking "slow" comparison
454 compareRes = order->compare(state1, state2);
455 }
456 if (compareRes == Order::NodeComparison::ABOVE || compareRes == Order::NodeComparison::SAME) {
457 // insert at current pointer (while keeping other values)
458 sortedSuccs.insert(itr, state1);
459 added = true;
460 break;
461 } else if (compareRes == Order::NodeComparison::UNKNOWN) {
462 ctx.continueExtending = false;
463 return {state1, state2};
464 }
465 }
466 if (!added) {
467 sortedSuccs.push_back(state1);
468 }
469 }
470 }
471 } else {
472 auto temp = order->sortStatesUnorderedPair(&successors);
473 if (temp.first.first != numberOfStates) {
474 return temp.first;
475 } else {
476 sortedSuccs = std::move(temp.second);
477 }
478 }
479
480 if (order->compare(sortedSuccs[0], sortedSuccs[sortedSuccs.size() - 1]) == Order::SAME) {
481 if (!order->contains(currentState)) {
482 order->addToNode(currentState, order->getNode(sortedSuccs[0]));
483 } else {
484 order->merge(currentState, sortedSuccs[0]);
485 }
486 } else {
487 if (!order->contains(sortedSuccs[0])) {
488 STORM_LOG_ASSERT(order->isBottomState(sortedSuccs[sortedSuccs.size() - 1]), "Expected the other successor to be a bottom state.");
489 STORM_LOG_ASSERT(sortedSuccs.size() == 2, "Expected exactly two successors in this case.");
490 order->addAbove(sortedSuccs[0], order->getBottom());
491 }
492 if (!order->contains(sortedSuccs[sortedSuccs.size() - 1])) {
493 STORM_LOG_ASSERT(order->isTopState(sortedSuccs[0]), "Expected the other successor to be a top state.");
494 STORM_LOG_ASSERT(sortedSuccs.size() == 2, "Expected exactly two successors in this case.");
495 order->addBelow(sortedSuccs[sortedSuccs.size() - 1], order->getTop());
496 }
497 // sortedSuccs[0] is highest
498 if (!order->contains(currentState)) {
499 order->addBetween(currentState, sortedSuccs[0], sortedSuccs[sortedSuccs.size() - 1]);
500 } else {
501 order->addRelation(sortedSuccs[0], currentState, allowMerge);
502 order->addRelation(currentState, sortedSuccs[sortedSuccs.size() - 1], allowMerge);
503 }
504 }
505 STORM_LOG_ASSERT(order->contains(currentState) && order->compare(order->getNode(currentState), order->getBottom()) == Order::ABOVE &&
506 order->compare(order->getNode(currentState), order->getTop()) == Order::BELOW,
507 "The current state should have ended up strictly between top and bottom in the order.");
508 return {numberOfStates, numberOfStates};
509}
510
511template<typename ValueType, typename ConstantType>
512std::pair<uint_fast64_t, uint_fast64_t> OrderExtender<ValueType, ConstantType>::extendByForwardReasoning(std::shared_ptr<Order> order,
513 uint_fast64_t currentState,
514 std::vector<uint_fast64_t> const& successors,
515 bool allowMerge) {
516 STORM_LOG_ASSERT(successors.size() > 1, "Forward reasoning is only needed for states with more than one successor.");
517 STORM_LOG_ASSERT(order->contains(currentState), "The current state must already be in the order before doing forward reasoning on it.");
518 STORM_LOG_ASSERT(cyclic, "Forward reasoning is only applicable to cyclic pMCs.");
519
520 std::vector<uint_fast64_t> statesSorted;
521 statesSorted.push_back(currentState);
522 bool pla = context(order).usePLA;
523 // Go over all states
524 bool oneUnknown = false;
525 bool unknown = false;
526 uint_fast64_t s1 = numberOfStates;
527 uint_fast64_t s2 = numberOfStates;
528 for (auto& state : successors) {
529 unknown = false;
530 bool added = false;
531 for (auto itr = statesSorted.begin(); itr != statesSorted.end(); ++itr) {
532 auto compareRes = order->compareFast(state, (*itr));
533 if (pla && compareRes == Order::NodeComparison::UNKNOWN) {
534 compareRes = addStatesBasedOnMinMax(order, state, (*itr));
535 }
536 if (compareRes == Order::NodeComparison::UNKNOWN) {
537 compareRes = order->compare(state, *itr);
538 }
539 if (compareRes == Order::NodeComparison::ABOVE || compareRes == Order::NodeComparison::SAME) {
540 if (!order->contains(state) && compareRes == Order::NodeComparison::ABOVE) {
541 order->add(state);
542 order->addStateToHandle(state);
543 }
544 added = true;
545 // insert at current pointer (while keeping other values)
546 statesSorted.insert(itr, state);
547 break;
548 } else if (compareRes == Order::NodeComparison::UNKNOWN && !oneUnknown) {
549 // We miss state in the result.
550 s1 = state;
551 s2 = *itr;
552 oneUnknown = true;
553 added = true;
554 break;
555 } else if (compareRes == Order::NodeComparison::UNKNOWN && oneUnknown) {
556 unknown = true;
557 added = true;
558 break;
559 }
560 }
561 if (!(unknown && oneUnknown) && !added) {
562 // State will be last in the list
563 statesSorted.push_back(state);
564 }
565 if (unknown && oneUnknown) {
566 break;
567 }
568 }
569 if (!unknown && oneUnknown) {
570 STORM_LOG_ASSERT(statesSorted.size() == successors.size(), "Expected all but the single unresolved successor to have been sorted.");
571 s2 = numberOfStates;
572 }
573
574 if (s1 == numberOfStates) {
575 STORM_LOG_ASSERT(statesSorted.size() == successors.size() + 1, "Expected all successors (plus the current state) to have been sorted.");
576 // all could be sorted, no need to do anything
577 } else if (s2 == numberOfStates) {
578 if (!order->contains(s1)) {
579 order->add(s1);
580 }
581
582 if (statesSorted[0] == currentState) {
583 order->addRelation(s1, statesSorted[0], allowMerge);
584 // Fallback checks statesSorted[0]: that's the element the addRelation call just above
585 // related (and, if allowMerge applies, may have merged) s1 with.
586 STORM_LOG_ASSERT((order->compare(s1, statesSorted[0]) == Order::ABOVE) || (allowMerge && (order->compare(s1, statesSorted[0]) == Order::SAME)),
587 "Expected s1 to end up above (or, if merged, equal to) statesSorted[0].");
588 order->addRelation(s1, statesSorted[statesSorted.size() - 1], allowMerge);
589 STORM_LOG_ASSERT((order->compare(s1, statesSorted[statesSorted.size() - 1]) == Order::ABOVE) ||
590 (allowMerge && (order->compare(s1, statesSorted[statesSorted.size() - 1]) == Order::SAME)),
591 "Expected s1 to end up above (or, if merged, equal to) the lowest sorted successor.");
592 order->addStateToHandle(s1);
593 } else if (statesSorted[statesSorted.size() - 1] == currentState) {
594 order->addRelation(statesSorted[0], s1, allowMerge);
595 // Fallback checks statesSorted[0]: that's the element the addRelation call just above
596 // related (and, if allowMerge applies, may have merged) s1 with.
597 STORM_LOG_ASSERT((order->compare(s1, statesSorted[0]) == Order::BELOW) || (allowMerge && (order->compare(s1, statesSorted[0]) == Order::SAME)),
598 "Expected s1 to end up below (or, if merged, equal to) statesSorted[0].");
599 order->addRelation(statesSorted[statesSorted.size() - 1], s1, allowMerge);
600 STORM_LOG_ASSERT((order->compare(s1, statesSorted[statesSorted.size() - 1]) == Order::BELOW) ||
601 (allowMerge && (order->compare(s1, statesSorted[statesSorted.size() - 1]) == Order::SAME)),
602 "Expected s1 to end up below (or, if merged, equal to) the highest sorted successor.");
603 order->addStateToHandle(s1);
604 } else {
605 bool continueSearch = true;
606 for (auto& entry : matrix.getRow(currentState)) {
607 if (entry.getColumn() == s1) {
608 if (entry.getValue().isConstant()) {
609 continueSearch = false;
610 }
611 }
612 }
613 if (continueSearch) {
614 for (auto& i : statesSorted) {
615 if (order->compare(i, s1) == Order::UNKNOWN) {
616 return {i, s1};
617 }
618 }
619 }
620 }
621 } else {
622 return {s1, s2};
623 }
624 STORM_LOG_ASSERT(order->contains(currentState) && order->compare(order->getNode(currentState), order->getBottom()) == Order::ABOVE &&
625 order->compare(order->getNode(currentState), order->getTop()) == Order::BELOW,
626 "The current state should have ended up strictly between top and bottom in the order.");
627 return {numberOfStates, numberOfStates};
628}
629
630template<typename ValueType, typename ConstantType>
631bool OrderExtender<ValueType, ConstantType>::extendByAssumption(std::shared_ptr<Order> order, uint_fast64_t state1, uint_fast64_t state2) {
632 auto& ctx = context(order);
633 STORM_LOG_ASSERT(order->compare(state1, state2) == Order::UNKNOWN, "Expected the given pair to indeed be unordered.");
634 STORM_LOG_ASSERT(!ctx.usePLA || (ctx.minValues.size() == numberOfStates && ctx.maxValues.size() == numberOfStates),
635 "The PLA path requires both bound vectors to be available.");
636 auto assumptions = ctx.usePLA ? assumptionMaker->createAndCheckAssumptions(state1, state2, order, region, ctx.minValues, ctx.maxValues)
637 : assumptionMaker->createAndCheckAssumptions(state1, state2, order, region);
638 if (assumptions.size() == 1 && assumptions.begin()->second == AssumptionStatus::VALID) {
639 handleAssumption(order, assumptions.begin()->first);
640 // Assumptions worked, we continue
641 return true;
642 }
643 return false;
644}
645
646template<typename ValueType, typename ConstantType>
647Order::NodeComparison OrderExtender<ValueType, ConstantType>::addStatesBasedOnMinMax(std::shared_ptr<Order> order, uint_fast64_t state1,
648 uint_fast64_t state2) const {
649 STORM_LOG_ASSERT(order->compareFast(state1, state2) == Order::UNKNOWN, "Expected the given pair to indeed be unordered.");
650 auto const& ctx = contextAt(order);
651 std::vector<ConstantType> const& mins = ctx.minValues;
652 std::vector<ConstantType> const& maxs = ctx.maxValues;
653 if (mins[state1] == maxs[state1] && mins[state2] == maxs[state2] && mins[state1] == mins[state2]) {
654 if (order->contains(state1)) {
655 if (order->contains(state2)) {
656 if (!order->merge(state1, state2)) {
657 // The min/max-based conclusion that state1 and state2 are equal contradicts
658 // what the order already knows about them; treat it as unresolved rather than
659 // silently building on top of an inconsistent order.
660 return Order::UNKNOWN;
661 }
662 } else {
663 order->addToNode(state2, order->getNode(state1));
664 }
665 }
666 return Order::SAME;
667 } else if (mins[state1] > maxs[state2]) {
668 // state 1 will always be larger than state2
669 if (!order->contains(state1)) {
670 order->add(state1);
671 }
672 if (!order->contains(state2)) {
673 order->add(state2);
674 }
675 STORM_LOG_ASSERT(order->compare(state1, state2) != Order::BELOW, "min/max values say state1 is above state2, contradicting the order.");
676 STORM_LOG_ASSERT(order->compare(state1, state2) != Order::SAME, "min/max values say state1 is strictly above state2, contradicting the order.");
677 order->addRelation(state1, state2);
678
679 return Order::ABOVE;
680 } else if (mins[state2] > maxs[state1]) {
681 // state2 will always be larger than state1
682 if (!order->contains(state1)) {
683 order->add(state1);
684 }
685 if (!order->contains(state2)) {
686 order->add(state2);
687 }
688 STORM_LOG_ASSERT(order->compare(state2, state1) != Order::BELOW, "min/max values say state2 is above state1, contradicting the order.");
689 STORM_LOG_ASSERT(order->compare(state2, state1) != Order::SAME, "min/max values say state2 is strictly above state1, contradicting the order.");
690 order->addRelation(state2, state1);
691 return Order::BELOW;
692 } else {
693 // Couldn't add relation between state1 and state 2 based on min/max values;
694 return Order::UNKNOWN;
695 }
696}
697
698template<typename ValueType, typename ConstantType>
700 if (model != nullptr) {
701 // Use parameter lifting modelchecker to get initial min/max values for order creation
703 std::unique_ptr<modelchecker::CheckResult> checkResult;
704 boost::optional<modelchecker::CheckTask<logic::Formula, ValueType>> checkTask;
705 if (this->formula->hasQuantitativeResult()) {
707 } else {
708 storm::logic::OperatorInformation opInfo(boost::none, boost::none);
709 auto newFormula =
710 std::make_shared<storm::logic::ProbabilityOperatorFormula>(formula->asProbabilityOperatorFormula().getSubformula().asSharedPointer(), opInfo);
712 }
713 STORM_LOG_THROW(plaModelChecker.canHandle(model, checkTask.get()), exceptions::NotSupportedException, "Cannot handle this formula");
714 bool const allowModelSimplification = false; // make sure that the results align with the input model
715 plaModelChecker.specify(env, model, checkTask.get(), std::nullopt, nullptr, allowModelSimplification);
716
719 plaModelChecker.check(env, annotatedRegion, solver::OptimizationDirection::Minimize)->template asExplicitQuantitativeCheckResult<ConstantType>();
721 plaModelChecker.check(env, annotatedRegion, solver::OptimizationDirection::Maximize)->template asExplicitQuantitativeCheckResult<ConstantType>();
722 minValuesInit = minCheck.getFiniteValueVector();
723 maxValuesInit = maxCheck.getFiniteValueVector();
724 STORM_LOG_ASSERT(minValuesInit->size() == numberOfStates, "Expected one lower bound per state.");
725 STORM_LOG_ASSERT(maxValuesInit->size() == numberOfStates, "Expected one upper bound per state.");
726 }
727}
728
729template<typename ValueType, typename ConstantType>
730void OrderExtender<ValueType, ConstantType>::setMinMaxValues(std::shared_ptr<Order> order, std::vector<ConstantType>&& minValues,
731 std::vector<ConstantType>&& maxValues) {
732 STORM_LOG_ASSERT(minValues.size() == numberOfStates, "Expected one lower bound per state.");
733 STORM_LOG_ASSERT(maxValues.size() == numberOfStates, "Expected one upper bound per state.");
734 auto& ctx = context(order);
735 ctx.usePLA = true;
736 if (ctx.unknownStates.first != numberOfStates) {
737 ctx.continueExtending = minValues[ctx.unknownStates.first] >= maxValues[ctx.unknownStates.second] ||
738 minValues[ctx.unknownStates.second] >= maxValues[ctx.unknownStates.first];
739 } else {
740 ctx.continueExtending = true;
741 }
742 ctx.minValues = std::move(minValues);
743 ctx.maxValues = std::move(maxValues);
744}
745
746template<typename ValueType, typename ConstantType>
747void OrderExtender<ValueType, ConstantType>::setMinValues(std::shared_ptr<Order> order, std::vector<ConstantType>&& minValues) {
748 STORM_LOG_ASSERT(minValues.size() == numberOfStates, "Expected one lower bound per state.");
749 auto& ctx = context(order);
750 // Only take the PLA path once both bound vectors are available, as assumption checks index into
751 // ctx.maxValues (setMaxValues below mirrors this).
752 ctx.usePLA = !ctx.maxValues.empty();
753 if (ctx.maxValues.size() == 0) {
754 ctx.continueExtending = false;
755 } else if (ctx.unknownStates.first != numberOfStates) {
756 ctx.continueExtending = minValues[ctx.unknownStates.first] >= ctx.maxValues[ctx.unknownStates.second] ||
757 minValues[ctx.unknownStates.second] >= ctx.maxValues[ctx.unknownStates.first];
758 } else {
759 ctx.continueExtending = true;
760 }
761 ctx.minValues = std::move(minValues);
762}
763
764template<typename ValueType, typename ConstantType>
765void OrderExtender<ValueType, ConstantType>::setMaxValues(std::shared_ptr<Order> order, std::vector<ConstantType>&& maxValues) {
766 STORM_LOG_ASSERT(maxValues.size() == numberOfStates, "Expected one upper bound per state.");
767 auto& ctx = context(order);
768 ctx.usePLA = !ctx.minValues.empty();
769 if (ctx.minValues.size() == 0) {
770 ctx.continueExtending = false;
771 } else if (ctx.unknownStates.first != numberOfStates) {
772 ctx.continueExtending = ctx.minValues[ctx.unknownStates.first] >= maxValues[ctx.unknownStates.second] ||
773 ctx.minValues[ctx.unknownStates.second] >= maxValues[ctx.unknownStates.first];
774 } else {
775 ctx.continueExtending = true;
776 }
777 ctx.maxValues = std::move(maxValues);
778}
779template<typename ValueType, typename ConstantType>
780void OrderExtender<ValueType, ConstantType>::setMinValuesInit(std::vector<ConstantType>&& minValues) {
781 STORM_LOG_ASSERT(minValues.size() == numberOfStates, "Expected one lower bound per state.");
782 this->minValuesInit = std::move(minValues);
783}
784
785template<typename ValueType, typename ConstantType>
786void OrderExtender<ValueType, ConstantType>::setMaxValuesInit(std::vector<ConstantType>&& maxValues) {
787 STORM_LOG_ASSERT(maxValues.size() == numberOfStates, "Expected one upper bound per state.");
788 this->maxValuesInit = std::move(maxValues); // maxCheck->asExplicitQuantitativeCheckResult<ConstantType>().getValueVector();
789}
790
791template<typename ValueType, typename ConstantType>
792void OrderExtender<ValueType, ConstantType>::checkParOnStateMonRes(uint_fast64_t s, std::shared_ptr<Order> order,
794 std::shared_ptr<MonotonicityResult<VariableType>> monResult) {
795 auto mon = monotonicityChecker.checkLocalMonotonicity(order, s, param, region);
796 monResult->updateMonotonicityResult(param, mon);
797}
798
799template<typename ValueType, typename ConstantType>
800void OrderExtender<ValueType, ConstantType>::setUnknownStates(std::shared_ptr<Order> order, uint_fast64_t state1, uint_fast64_t state2) {
801 STORM_LOG_ASSERT(state1 != numberOfStates && state2 != numberOfStates, "Expected two actual states, not the sentinel value.");
802 context(order).unknownStates = {state1, state2};
803}
804
805template<typename ValueType, typename ConstantType>
806std::pair<uint_fast64_t, uint_fast64_t> OrderExtender<ValueType, ConstantType>::getUnknownStates(std::shared_ptr<Order> order) const {
807 auto it = contexts.find(order);
808 if (it != contexts.end()) {
809 return it->second.unknownStates;
810 }
811 return {numberOfStates, numberOfStates};
812}
813
814template<typename ValueType, typename ConstantType>
815void OrderExtender<ValueType, ConstantType>::copyContext(std::shared_ptr<Order> orderOriginal, std::shared_ptr<Order> orderCopy) {
816 STORM_LOG_ASSERT(contexts.find(orderCopy) == contexts.end(), "The copy must not already have a context set.");
817 contexts[orderCopy] = contextAt(orderOriginal);
818}
819
820template<typename ValueType, typename ConstantType>
821std::pair<uint_fast64_t, bool> OrderExtender<ValueType, ConstantType>::getNextState(std::shared_ptr<Order> order, uint_fast64_t currentState, bool done) {
822 if (done && currentState != numberOfStates) {
823 order->setDoneState(currentState);
824 }
825 if (cyclic && order->existsStateToHandle()) {
826 return order->getStateToHandle();
827 }
828 if (currentState == numberOfStates) {
829 return order->getNextStateNumber();
830 }
831 if (currentState != numberOfStates) {
832 return order->getNextStateNumber();
833 }
834 return {numberOfStates, true};
835}
836
837template<typename ValueType, typename ConstantType>
838bool OrderExtender<ValueType, ConstantType>::isHope(std::shared_ptr<Order> order) {
839 STORM_LOG_ASSERT(contexts.find(order) != contexts.end(), "Expected a context to be set for this order.");
840 STORM_LOG_ASSERT(!order->getDoneBuilding(), "Asking for hope on an order that is already fully built is meaningless.");
841 // First check if bounds helped us
842 return context(order).continueExtending;
843}
844template<typename ValueType, typename ConstantType>
848template<typename ValueType, typename ConstantType>
849const std::vector<std::set<typename OrderExtender<ValueType, ConstantType>::VariableType>>&
853
856} // namespace analysis
857} // namespace storm
void initializeMinMaxValues(Environment const &env, storage::ParameterRegion< ValueType > region)
void setMaxValuesInit(std::vector< ConstantType > &&minValues)
std::vector< std::set< VariableType > > const & getVariablesOccuringAtState()
std::pair< uint_fast64_t, uint_fast64_t > getUnknownStates(std::shared_ptr< Order > order) const
std::tuple< std::shared_ptr< Order >, uint_fast64_t, uint_fast64_t > extendOrder(std::shared_ptr< Order > order, storm::storage::ParameterRegion< ValueType > region, std::shared_ptr< MonotonicityResult< VariableType > > monRes=nullptr, std::optional< Assumption > assumption=std::nullopt)
Extends the order for the given region.
void setMaxValues(std::shared_ptr< Order > order, std::vector< ConstantType > &&maxValues)
MonotonicityChecker< ValueType > & getMonotoncityChecker()
void setMinValues(std::shared_ptr< Order > order, std::vector< ConstantType > &&minValues)
void setMinValuesInit(std::vector< ConstantType > &&minValues)
void setUnknownStates(std::shared_ptr< Order > order, uint_fast64_t state1, uint_fast64_t state2)
OrderExtender(std::shared_ptr< models::sparse::Model< ValueType > > model, std::shared_ptr< logic::Formula const > formula)
Constructs a new OrderExtender.
void copyContext(std::shared_ptr< Order > orderOriginal, std::shared_ptr< Order > orderCopy)
utility::parametric::VariableType< ValueType >::type VariableType
std::tuple< std::shared_ptr< Order >, uint_fast64_t, uint_fast64_t > toOrder(storage::ParameterRegion< ValueType > region, std::shared_ptr< MonotonicityResult< VariableType > > monRes=nullptr)
Creates an order based on the given formula.
void setMinMaxValues(std::shared_ptr< Order > order, std::vector< ConstantType > &&minValues, std::vector< ConstantType > &&maxValues)
void checkParOnStateMonRes(uint_fast64_t s, std::shared_ptr< Order > order, typename OrderExtender< ValueType, ConstantType >::VariableType param, std::shared_ptr< MonotonicityResult< VariableType > > monResult)
bool isHope(std::shared_ptr< Order > order)
NodeComparison
Constants for comparison of nodes/states.
Definition Order.h:18
A quantitative check result over the states of a sparse model.
virtual bool canHandle(std::shared_ptr< storm::models::ModelBase > parametricModel, CheckTask< storm::logic::Formula, ParametricType > const &checkTask) const override
virtual void specify(Environment const &env, std::shared_ptr< storm::models::ModelBase > parametricModel, CheckTask< storm::logic::Formula, ParametricType > const &checkTask, std::optional< RegionSplitEstimateKind > generateRegionSplitEstimates=std::nullopt, std::shared_ptr< MonotonicityBackend< ParametricType > > monotonicityBackend={}, bool allowModelSimplifications=true, storm::pars::analysis::GraphPreservationStatus graphPreservationStatus=storm::pars::analysis::GraphPreservationStatus::GraphPreserving) override
std::unique_ptr< CheckResult > check(Environment const &env, AnnotatedRegion< ParametricType > &region, storm::solver::OptimizationDirection const &dirForParameters)
Checks the specified formula on the given region by applying parameter lifting (Parameter choices are...
Base class for all sparse models.
Definition Model.h:34
A bit vector that is internally represented as a vector of 64-bit values.
Definition BitVector.h:16
size_t size() const
Retrieves the number of bits this bit vector can store.
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.
index_type getColumnCount() const
Returns the number of columns of the matrix.
storm::storage::SparseMatrix< value_type > transpose(bool joinGroups=false, bool keepZeros=false) const
Transposes the matrix.
index_type getRowCount() const
Returns the number of rows of the matrix.
This class represents the decomposition of a graph-like structure into its strongly connected compone...
#define STORM_LOG_INFO(message)
Definition logging.h:34
#define STORM_LOG_ASSERT(cond, message)
Definition macros.h:9
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:39
std::pair< storm::storage::BitVector, storm::storage::BitVector > performProb01(storm::models::sparse::DeterministicModel< T > const &model, storm::storage::BitVector const &phiStates, storm::storage::BitVector const &psiStates)
Computes the sets of states that have probability 0 or 1, respectively, of satisfying phi until psi i...
Definition graph.cpp:394
std::vector< uint_fast64_t > getTopologicalSort(storm::storage::SparseMatrix< T > const &matrix, std::vector< uint64_t > const &firstStates)
Performs a topological sort of the states of the system according to the given transitions.
Definition graph.cpp:1883
bool hasCycle(storm::storage::SparseMatrix< T > const &transitionMatrix, boost::optional< storm::storage::BitVector > const &subsystem)
Returns true if the graph represented by the given matrix has a cycle.
Definition graph.cpp:137
void gatherOccurringVariables(FunctionType const &function, std::set< typename VariableType< FunctionType >::type > &variableSet)
Add all variables that occur in the given function to the the given set.
A candidate relation between two states of a reachability order: either state1 > state2 (Greater) or ...
Definition Assumption.h:20
AssumptionRelation relation
Definition Assumption.h:23
Nodes of the Reachability Order.
Definition Order.h:28
StronglyConnectedComponentDecompositionOptions & forceTopologicalSort(bool value=true)
Enforces that the returned SCCs are sorted in a topological order.