Storm 1.11.1.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
RegionRefinementChecker.cpp
Go to the documentation of this file.
2
3#include <functional>
4#include <iterator>
5#include <queue>
6
11
12#include "storm/logic/Bound.h"
15
20
21namespace storm::modelchecker {
22
23template<typename ParametricType>
25 STORM_LOG_ASSERT(regionChecker != nullptr, "The region model checker must not be null.");
26 this->regionChecker = std::move(regionChecker);
27}
28
29template<typename ParametricType>
30bool RegionRefinementChecker<ParametricType>::canHandle(std::shared_ptr<storm::models::ModelBase> parametricModel,
32 return regionChecker->canHandle(parametricModel, checkTask);
33}
34
35template<typename ParametricType>
36void RegionRefinementChecker<ParametricType>::specify(Environment const& env, std::shared_ptr<storm::models::ModelBase> parametricModel,
38 RegionSplittingStrategy splittingStrategy, std::set<VariableType> const& discreteVariables,
39 std::shared_ptr<MonotonicityBackend<ParametricType>> monotonicityBackend, bool allowModelSimplifications,
40 bool graphPreserving) {
41 this->monotonicityBackend = monotonicityBackend ? monotonicityBackend : std::make_shared<MonotonicityBackend<ParametricType>>();
42 this->regionSplittingStrategy = std::move(splittingStrategy);
43 this->discreteVariables = std::move(discreteVariables);
44 this->graphPreserving = graphPreserving;
45 if (this->regionSplittingStrategy.heuristic == RegionSplittingStrategy::Heuristic::Default) {
46 regionSplittingStrategy.heuristic = RegionSplittingStrategy::Heuristic::EstimateBased;
47 }
48 // Potentially determine the kind of region split estimate to generate
49 if (regionSplittingStrategy.heuristic == RegionSplittingStrategy::Heuristic::EstimateBased) {
50 if (regionSplittingStrategy.estimateKind.has_value()) {
51 STORM_LOG_THROW(regionChecker->isRegionSplitEstimateKindSupported(regionSplittingStrategy.estimateKind.value(), checkTask),
52 storm::exceptions::NotSupportedException, "The specified region split estimate kind is not supported by the region model checker.");
53 } else {
54 regionSplittingStrategy.estimateKind = regionChecker->getDefaultRegionSplitEstimateKind(checkTask);
55 STORM_LOG_ASSERT(regionChecker->isRegionSplitEstimateKindSupported(regionSplittingStrategy.estimateKind.value(), checkTask),
56 "The region model checker does not support its default region split estimate kind.");
57 }
58 } else {
59 regionSplittingStrategy.estimateKind = std::nullopt; // do not compute estimates
60 }
61
62 regionChecker->specify(env, parametricModel, checkTask, regionSplittingStrategy.estimateKind, monotonicityBackend, allowModelSimplifications,
63 graphPreserving);
64}
65
66template<typename T>
68 public:
69 PartitioningProgress(T const totalArea, T const coverageThreshold = storm::utility::zero<T>())
70 : totalArea(totalArea),
71 coverageThreshold(coverageThreshold),
72 fractionOfUndiscoveredArea(storm::utility::one<T>()),
73 fractionOfAllSatArea(storm::utility::zero<T>()),
74 fractionOfAllViolatedArea(storm::utility::zero<T>()),
75 progress("% covered area") {
76 progress.setMaxCount(100 - asPercentage(coverageThreshold));
77 progress.startNewMeasurement(0u);
78 }
79
80 uint64_t getUndiscoveredPercentage() const {
81 return asPercentage(fractionOfUndiscoveredArea);
82 }
83
85 return fractionOfUndiscoveredArea <= coverageThreshold;
86 }
87
88 T addDiscoveredArea(T const& area) {
89 auto addedFraction = area / totalArea;
90 fractionOfUndiscoveredArea -= addedFraction;
92 return addedFraction;
93 }
94
95 void addAllSatArea(T const& area) {
96 fractionOfAllSatArea += addDiscoveredArea(area);
97 }
98
99 void addAllViolatedArea(T const& area) {
100 fractionOfAllViolatedArea += addDiscoveredArea(area);
101 }
102
103 void addAllIllDefinedArea(T const& area) {
104 fractionOfAllIllDefinedArea += addDiscoveredArea(area);
105 }
106
107 private:
108 static uint64_t asPercentage(T const& value) {
109 return storm::utility::convertNumber<uint64_t>(storm::utility::round<T>(value * storm::utility::convertNumber<T, uint64_t>(100u)));
110 }
111
112 T const totalArea;
113 T const coverageThreshold;
114 T fractionOfUndiscoveredArea;
115 T fractionOfAllSatArea;
116 T fractionOfAllViolatedArea;
117 T fractionOfAllIllDefinedArea;
119};
120
121template<typename ParametricType>
122std::unique_ptr<storm::modelchecker::RegionRefinementCheckResult<ParametricType>> RegionRefinementChecker<ParametricType>::performRegionPartitioning(
123 Environment const& env, storm::storage::ParameterRegion<ParametricType> const& region, std::optional<ParametricType> coverageThreshold,
124 std::optional<uint64_t> depthThreshold, RegionResultHypothesis const& hypothesis, uint64_t monThresh) {
125 STORM_LOG_INFO("Applying Region Partitioning on region: " << region.toString(true) << " .");
126
128 region.area(), storm::utility::convertNumber<CoefficientType>(coverageThreshold.value_or(storm::utility::zero<ParametricType>())));
129
130 // Holds the initial region as well as all considered (sub)-regions and their annotations as a tree
131 AnnotatedRegion<ParametricType> rootRegion(region);
132
133 // FIFO queue storing the current leafs of the region tree with neither allSat nor allViolated
134 // As we currently split regions in the center, a FIFO queue will ensure that regions with a larger area are processed first.
135 std::queue<std::reference_wrapper<AnnotatedRegion<ParametricType>>> unprocessedRegions;
136 unprocessedRegions.emplace(rootRegion);
137
138 uint64_t numOfAnalyzedRegions{0u};
139 bool monotonicityInitialized{false};
140
141 // Region Refinement Loop
142 while (!progress.isCoverageThresholdReached() && !unprocessedRegions.empty()) {
143 auto& currentRegion = unprocessedRegions.front().get();
144 STORM_LOG_TRACE("Analyzing region #" << numOfAnalyzedRegions << " (Refinement depth " << currentRegion.refinementDepth << "; "
145 << progress.getUndiscoveredPercentage() << "% still unknown; " << unprocessedRegions.size()
146 << " regions unprocessed).");
147 unprocessedRegions.pop(); // can pop already here, since the rootRegion has ownership.
148 ++numOfAnalyzedRegions;
149
150 if (!monotonicityInitialized && currentRegion.refinementDepth >= monThresh) {
151 monotonicityInitialized = true;
152 monotonicityBackend->initializeMonotonicity(env, rootRegion);
153 // Propagate monotonicity (unless the currentRegion is the root)
154 if (currentRegion.refinementDepth > 0) {
155 rootRegion.propagateAnnotationsToSubregions(true);
156 }
157 }
158 if (monotonicityInitialized) {
159 monotonicityBackend->updateMonotonicity(env, currentRegion);
160 }
161
162 currentRegion.result = regionChecker->analyzeRegion(env, currentRegion, hypothesis);
163
164 if (currentRegion.result == RegionResult::AllSat) {
165 progress.addAllSatArea(currentRegion.region.area());
166 } else if (currentRegion.result == RegionResult::AllViolated) {
167 progress.addAllViolatedArea(currentRegion.region.area());
168 } else if (currentRegion.result == RegionResult::AllIllDefined) {
169 // ill defined region => not graph-preserving
170 STORM_LOG_ERROR_COND(!this->graphPreserving,
171 "The region is not graph-preserving, but the selected region verification engine requires this assumption. Please use "
172 "--assume-graph-preserving "
173 "false.");
174 progress.addAllIllDefinedArea(currentRegion.region.area());
175 } else {
176 // Split the region as long as the desired refinement depth is not reached.
177 if (!depthThreshold || currentRegion.refinementDepth < depthThreshold.value()) {
178 monotonicityBackend->updateMonotonicityBeforeSplitting(env, currentRegion);
179 auto splittingVariables = getSplittingVariables(currentRegion, Context::Partitioning);
180 STORM_LOG_INFO("Splitting on variables" << splittingVariables);
181 currentRegion.splitLeafNodeAtCenter(splittingVariables, this->discreteVariables, true);
182 for (auto& child : currentRegion.subRegions) {
183 unprocessedRegions.emplace(child);
184 }
185 }
186 }
187 }
188
189 // Prepare result
190 uint64_t numberOfRegionsKnownThroughMonotonicity{0};
191 std::vector<std::pair<storm::storage::ParameterRegion<ParametricType>, RegionResult>> result;
192 rootRegion.postOrderTraverseSubRegions([&result, &numberOfRegionsKnownThroughMonotonicity](auto& node) {
193 if (node.subRegions.empty()) {
194 if (node.resultKnownThroughMonotonicity) {
195 ++numberOfRegionsKnownThroughMonotonicity;
196 }
197 result.emplace_back(node.region, node.result);
198 }
199 });
200 auto const maxDepth = rootRegion.getMaxDepthOfSubRegions();
201 STORM_LOG_INFO("Region partitioning terminated after analyzing " << numOfAnalyzedRegions << " regions.\n\t" << numberOfRegionsKnownThroughMonotonicity
202 << " regions known through monotonicity.\n\tMaximum refinement depth: " << maxDepth
203 << ".\n\t" << progress.getUndiscoveredPercentage()
204 << "% of the parameter space are not covered.");
205
206 auto regionCopyForResult = region;
207 return std::make_unique<storm::modelchecker::RegionRefinementCheckResult<ParametricType>>(std::move(result), std::move(regionCopyForResult));
208}
209
210template<typename ParametricType>
211std::pair<typename storm::storage::ParameterRegion<ParametricType>::CoefficientType, typename storm::storage::ParameterRegion<ParametricType>::Valuation>
214 std::function<bool(CoefficientType, CoefficientType)> acceptGlobalBound,
215 std::function<bool(CoefficientType)> rejectInstance) {
216 auto progress = PartitioningProgress<CoefficientType>(region.area());
217
218 // Holds the initial region as well as all considered (sub)-regions and their annotations as a tree
219 AnnotatedRegion<ParametricType> rootRegion(region);
220
221 // Priority Queue storing the regions that still need to be processed. Regions with a "good" bound are processed first
222 auto cmp = storm::solver::minimize(dir) ? [](AnnotatedRegion<ParametricType> const& lhs,
223 AnnotatedRegion<ParametricType> const& rhs) { return *lhs.knownLowerValueBound > *rhs.knownLowerValueBound; }
225 return *lhs.knownUpperValueBound < *rhs.knownUpperValueBound;
226 };
227 std::priority_queue<std::reference_wrapper<AnnotatedRegion<ParametricType>>, std::vector<std::reference_wrapper<AnnotatedRegion<ParametricType>>>,
228 decltype(cmp)>
229 unprocessedRegions(cmp);
230 unprocessedRegions.push(rootRegion);
231
232 // Initialize Monotonicity
233 monotonicityBackend->initializeMonotonicity(env, rootRegion);
234
235 // Initialize result
236 auto valueValuation = regionChecker->getAndEvaluateGoodPoint(env, rootRegion, dir);
237 auto& value = valueValuation.first;
238 if (rejectInstance(value)) {
239 return valueValuation;
240 }
241
242 // Helper functions to check if a given result is better than the currently known result
243 auto isBetterThanValue = [&value, &dir](auto const& newValue) { return storm::solver::minimize(dir) ? newValue < value : newValue > value; };
244 // acceptGlobalBound is the opposite of this
245
246 // Region Refinement Loop
247 uint64_t numOfAnalyzedRegions{0u};
248 while (!unprocessedRegions.empty()) {
249 auto& currentRegion = unprocessedRegions.top().get();
250 auto currentBound =
251 storm::solver::minimize(dir) ? currentRegion.knownLowerValueBound.getOptionalValue() : currentRegion.knownUpperValueBound.getOptionalValue();
252 STORM_LOG_TRACE("Analyzing region #" << numOfAnalyzedRegions << " (Refinement depth " << currentRegion.refinementDepth << "; "
253 << progress.getUndiscoveredPercentage() << "% still unknown; " << unprocessedRegions.size()
254 << " regions unprocessed). Best known value: " << value << ".");
255 unprocessedRegions.pop(); // can pop already here, since the rootRegion has ownership.
256 ++numOfAnalyzedRegions;
257
258 monotonicityBackend->updateMonotonicity(env, currentRegion);
259
260 // Compute the bound for this region (unless the known bound is already too weak)
261 if (!currentBound || !acceptGlobalBound(value, currentBound.value())) {
262 // Improve over-approximation of extremal value (within this region)
263 currentBound = regionChecker->getBoundAtInitState(env, currentRegion, dir);
264 if (storm::solver::minimize(dir)) {
265 currentRegion.knownLowerValueBound &= *currentBound;
266 } else {
267 currentRegion.knownUpperValueBound &= *currentBound;
268 }
269 }
270
271 // Process the region if the bound is promising
272 if (!acceptGlobalBound(value, currentBound.value())) {
273 // Improve (global) under-approximation of extremal value
274 // Check whether this region contains a new 'good' value and set this value if that is the case
275 auto [currValue, currValuation] = regionChecker->getAndEvaluateGoodPoint(env, currentRegion, dir);
276 if (isBetterThanValue(currValue)) {
277 valueValuation = {currValue, currValuation};
278 if (rejectInstance(currValue)) {
279 STORM_LOG_INFO("Found rejecting instance " << currValuation << " with value " << currValue);
280 return valueValuation;
281 }
282 }
283 }
284
285 // Trigger region-splitting if over- and under-approximation are still too far apart
286 if (!acceptGlobalBound(value, currentBound.value())) {
287 monotonicityBackend->updateMonotonicityBeforeSplitting(env, currentRegion);
288 auto splittingVariables = getSplittingVariables(currentRegion, Context::ExtremalValue);
289 STORM_LOG_INFO("Splitting on variables " << splittingVariables);
290 currentRegion.splitLeafNodeAtCenter(splittingVariables, this->discreteVariables, true);
291 for (auto& child : currentRegion.subRegions) {
292 unprocessedRegions.emplace(child);
293 }
294 } else {
295 progress.addDiscoveredArea(currentRegion.region.area());
296 }
297 }
298
299 std::cout << "Region partitioning for extremal value terminated after analyzing " << numOfAnalyzedRegions << " regions.\n\t"
300 << progress.getUndiscoveredPercentage() << "% of the parameter space are not covered.\n";
301 return valueValuation;
302}
303
304template<typename ParametricType>
305std::pair<typename storm::storage::ParameterRegion<ParametricType>::CoefficientType, typename storm::storage::ParameterRegion<ParametricType>::Valuation>
307 storm::solver::OptimizationDirection const& dir, ParametricType const& precision,
308 bool absolutePrecision, std::optional<storm::logic::Bound> const& boundInvariant) {
309 // Handle input precision
310 STORM_LOG_THROW(storm::utility::isConstant(precision), storm::exceptions::InvalidArgumentException,
311 "Precision must be a constant value. Got " << precision << " instead.");
312 CoefficientType convertedPrecision = storm::utility::convertNumber<CoefficientType>(precision);
313
314 auto acceptGlobalBound = [&](CoefficientType value, CoefficientType newValue) {
315 CoefficientType const usedPrecision = convertedPrecision * (absolutePrecision ? storm::utility::one<CoefficientType>() : value);
316 return storm::solver::minimize(dir) ? newValue >= value - usedPrecision : newValue <= value + usedPrecision;
317 };
318
319 auto rejectInstance = [&](CoefficientType currentValue) { return boundInvariant && !boundInvariant->isSatisfied(currentValue); };
320
321 return computeExtremalValueHelper(env, region, dir, acceptGlobalBound, rejectInstance);
322}
323
324template<typename ParametricType>
326 const storm::logic::Bound& bound) {
327 // Use the bound from the formula.
328 CoefficientType valueToCheck = storm::utility::convertNumber<CoefficientType>(bound.threshold.evaluateAsRational());
329 // We will try to violate the bound.
331 isLowerBound(bound.comparisonType) ? storm::solver::OptimizationDirection::Minimize : storm::solver::OptimizationDirection::Maximize;
332 // We pass the bound as an invariant; as soon as it is obtained, we can stop the search.
333 auto acceptGlobalBound = [&](CoefficientType value, CoefficientType newValue) { return bound.isSatisfied(newValue); };
334
335 auto rejectInstance = [&](CoefficientType currentValue) { return !bound.isSatisfied(currentValue); };
336
337 auto res = computeExtremalValueHelper(env, region, dir, acceptGlobalBound, rejectInstance).first;
338 std::cout << "Extremal value: " << res << std::endl;
339 return storm::solver::minimize(dir) ? res >= valueToCheck : res <= valueToCheck;
340}
341
342template<typename ParametricType>
343std::set<typename RegionRefinementChecker<ParametricType>::VariableType> RegionRefinementChecker<ParametricType>::getSplittingVariablesEstimateBased(
344 AnnotatedRegion<ParametricType> const& region, Context context) const {
345 // If we can split on all variables, do that instead of requesting region split estimates
346 if (this->regionSplittingStrategy.maxSplitDimensions >= region.region.getVariables().size()) {
347 return region.region.getVariables();
348 }
349
350 auto const& estimates = regionChecker->obtainRegionSplitEstimates(region.region.getVariables());
351 std::vector<std::pair<VariableType, CoefficientType>> estimatesToSort;
352 estimatesToSort.reserve(region.region.getVariables().size());
353 STORM_LOG_ASSERT(estimates.size() == region.region.getVariables().size(), "Unexpected number of estimates");
354 auto estimatesIter = estimates.begin();
355 for (auto const& param : region.region.getVariables()) {
356 estimatesToSort.push_back(std::make_pair(param, *estimatesIter++));
357 }
358
359 // Sort and insert largest n=maxSplitDimensions estimates
360 std::sort(estimatesToSort.begin(), estimatesToSort.end(), [](const auto& a, const auto& b) { return a.second > b.second; });
361
362 std::set<VariableType> splittingVars;
363 for (auto const& estimate : estimatesToSort) {
364 // Do not split on monotone parameters if finding an extremal value is the goal
365 if (context == Context::ExtremalValue && region.monotonicityAnnotation.getGlobalMonotonicityResult() &&
366 region.monotonicityAnnotation.getGlobalMonotonicityResult()->isMonotone(estimate.first)) {
367 continue;
368 }
369 // Do not split on parameters that are fixed
370 if (region.region.getDifference(estimate.first) == storm::utility::zero<CoefficientType>()) {
371 continue;
372 }
373 splittingVars.emplace(estimate.first);
374 if (splittingVars.size() == regionSplittingStrategy.maxSplitDimensions) {
375 break;
376 }
377 }
378 return splittingVars;
379}
380
381template<typename ParametricType>
382std::set<typename RegionRefinementChecker<ParametricType>::VariableType> RegionRefinementChecker<ParametricType>::getSplittingVariablesRoundRobin(
383 AnnotatedRegion<ParametricType> const& region, Context context) const {
384 // Perform round-robin based on the depth of the region, always split on max split dimensions
385 auto const& vars = region.region.getVariables();
386 auto varsIter = vars.begin();
387
388 std::advance(varsIter, (region.refinementDepth * this->regionSplittingStrategy.maxSplitDimensions) % vars.size());
389
390 auto loopPoint = varsIter;
391
392 std::set<VariableType> splittingVars;
393 do {
394 // Do not split on monotone parameters if finding an extremal value is the goal
395 if (context == Context::ExtremalValue && region.monotonicityAnnotation.getGlobalMonotonicityResult() &&
396 region.monotonicityAnnotation.getGlobalMonotonicityResult()->isMonotone(*varsIter)) {
397 continue;
398 }
399 splittingVars.emplace(*varsIter);
400 std::advance(varsIter, 1);
401 if (varsIter == vars.end()) {
402 varsIter = vars.begin();
403 }
404 } while (loopPoint != varsIter && splittingVars.size() < this->regionSplittingStrategy.maxSplitDimensions);
405
406 return splittingVars;
407}
408
409template<typename ParametricType>
410std::set<typename RegionRefinementChecker<ParametricType>::VariableType> RegionRefinementChecker<ParametricType>::getSplittingVariables(
411 AnnotatedRegion<ParametricType> const& region, Context context) const {
412 switch (regionSplittingStrategy.heuristic) {
414 return getSplittingVariablesEstimateBased(region, context);
416 return getSplittingVariablesRoundRobin(region, context);
418 default:
419 STORM_LOG_ERROR("Default strategy should have been populated.");
420 return {};
421 }
422}
423
424template class RegionRefinementChecker<storm::RationalFunction>;
425} // namespace storm::modelchecker
storm::RationalNumber evaluateAsRational() const
Evaluates the expression and returns the resulting rational number.
PartitioningProgress(T const totalArea, T const coverageThreshold=storm::utility::zero< T >())
bool canHandle(std::shared_ptr< storm::models::ModelBase > parametricModel, CheckTask< storm::logic::Formula, ParametricType > const &checkTask) const
typename storm::storage::ParameterRegion< ParametricType >::CoefficientType CoefficientType
bool verifyRegion(Environment const &env, storm::storage::ParameterRegion< ParametricType > const &region, storm::logic::Bound const &bound)
Checks whether the bound is satisfied on the complete region.
RegionRefinementChecker(std::unique_ptr< RegionModelChecker< ParametricType > > &&regionChecker)
std::unique_ptr< storm::modelchecker::RegionRefinementCheckResult< ParametricType > > performRegionPartitioning(Environment const &env, storm::storage::ParameterRegion< ParametricType > const &region, std::optional< ParametricType > coverageThreshold, std::optional< uint64_t > depthThreshold=std::nullopt, RegionResultHypothesis const &hypothesis=RegionResultHypothesis::Unknown, uint64_t monThresh=0)
Iteratively refines the region until the region analysis yields a conclusive result (AllSat or AllVio...
std::pair< CoefficientType, Valuation > computeExtremalValue(Environment const &env, storm::storage::ParameterRegion< ParametricType > const &region, storm::solver::OptimizationDirection const &dir, ParametricType const &precision, bool absolutePrecision, std::optional< storm::logic::Bound > const &boundInvariant)
Finds the extremal value within the given region and with the given precision.
std::pair< CoefficientType, Valuation > computeExtremalValueHelper(Environment const &env, storm::storage::ParameterRegion< ParametricType > const &region, storm::solver::OptimizationDirection const &dir, std::function< bool(CoefficientType, CoefficientType)> acceptGlobalBound, std::function< bool(CoefficientType)> rejectInstance)
Finds the extremal value within the given region and with the given precision.
void specify(Environment const &env, std::shared_ptr< storm::models::ModelBase > parametricModel, CheckTask< storm::logic::Formula, ParametricType > const &checkTask, RegionSplittingStrategy splittingStrategy=RegionSplittingStrategy(), std::set< VariableType > const &discreteVariables={}, std::shared_ptr< MonotonicityBackend< ParametricType > > monotonicityBackend={}, bool allowModelSimplifications=true, bool graphPreserving=true)
std::set< VariableType > const & getVariables() const
CoefficientType area() const
Returns the area of this region.
std::string toString(bool boundariesAsDouble=false) const
storm::utility::parametric::Valuation< ParametricType > Valuation
CoefficientType getDifference(const std::string varName) const
A class that provides convenience operations to display run times.
bool updateProgress(uint64_t count)
Updates the progress to the current count and prints it if the delay passed.
void setMaxCount(uint64_t maxCount)
Sets the maximal possible count.
void startNewMeasurement(uint64_t startCount)
Starts a new measurement, dropping all progress information collected so far.
#define STORM_LOG_INFO(message)
Definition logging.h:24
#define STORM_LOG_TRACE(message)
Definition logging.h:12
#define STORM_LOG_ERROR(message)
Definition logging.h:26
#define STORM_LOG_ASSERT(cond, message)
Definition macros.h:11
#define STORM_LOG_ERROR_COND(cond, message)
Definition macros.h:52
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:30
RegionResult
The results for a single Parameter Region.
@ AllSat
the formula is satisfied for all well-defined parameters in the given region
@ AllViolated
the formula is violated for all well-defined parameters in the given region
@ AllIllDefined
the formula is ill-defined for all parameters in the given region
RegionResultHypothesis
hypothesis for the result for a single Parameter Region
bool constexpr minimize(OptimizationDirection d)
bool isConstant(ValueType const &)
Definition constants.cpp:67
LabParser.cpp.
ComparisonType comparisonType
Definition Bound.h:18
storm::expressions::Expression threshold
Definition Bound.h:19
bool isSatisfied(ValueType const &compareValue) const
Definition Bound.cpp:7
void propagateAnnotationsToSubregions(bool allowDeleteAnnotationsOfThis)
storm::modelchecker::MonotonicityAnnotation< ParametricType > monotonicityAnnotation
Whether the result is known through monotonicity.
void postOrderTraverseSubRegions(std::function< void(AnnotatedRegion< ParametricType > &)> const &visitor)
storm::utility::Maximum< CoefficientType > knownLowerValueBound
storm::utility::Minimum< CoefficientType > knownUpperValueBound
Region const region
The subregions of this region.