23template<
typename ParametricType>
25 STORM_LOG_ASSERT(regionChecker !=
nullptr,
"The region model checker must not be null.");
26 this->regionChecker = std::move(regionChecker);
29template<
typename ParametricType>
32 return regionChecker->canHandle(parametricModel, checkTask);
35template<
typename ParametricType>
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;
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.");
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.");
59 regionSplittingStrategy.estimateKind = std::nullopt;
62 regionChecker->specify(env, parametricModel, checkTask, regionSplittingStrategy.estimateKind, monotonicityBackend, allowModelSimplifications,
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));
81 return asPercentage(fractionOfUndiscoveredArea);
85 return fractionOfUndiscoveredArea <= coverageThreshold;
89 auto addedFraction = area / totalArea;
90 fractionOfUndiscoveredArea -= addedFraction;
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)));
113 T
const coverageThreshold;
114 T fractionOfUndiscoveredArea;
115 T fractionOfAllSatArea;
116 T fractionOfAllViolatedArea;
117 T fractionOfAllIllDefinedArea;
121template<
typename ParametricType>
128 region.
area(), storm::utility::convertNumber<CoefficientType>(coverageThreshold.value_or(storm::utility::zero<ParametricType>())));
135 std::queue<std::reference_wrapper<AnnotatedRegion<ParametricType>>> unprocessedRegions;
136 unprocessedRegions.emplace(rootRegion);
138 uint64_t numOfAnalyzedRegions{0u};
139 bool monotonicityInitialized{
false};
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();
148 ++numOfAnalyzedRegions;
150 if (!monotonicityInitialized && currentRegion.refinementDepth >= monThresh) {
151 monotonicityInitialized =
true;
152 monotonicityBackend->initializeMonotonicity(env, rootRegion);
154 if (currentRegion.refinementDepth > 0) {
158 if (monotonicityInitialized) {
159 monotonicityBackend->updateMonotonicity(env, currentRegion);
162 currentRegion.result = regionChecker->analyzeRegion(env, currentRegion, hypothesis);
165 progress.addAllSatArea(currentRegion.region.area());
167 progress.addAllViolatedArea(currentRegion.region.area());
171 "The region is not graph-preserving, but the selected region verification engine requires this assumption. Please use "
172 "--assume-graph-preserving "
174 progress.addAllIllDefinedArea(currentRegion.region.area());
177 if (!depthThreshold || currentRegion.refinementDepth < depthThreshold.value()) {
178 monotonicityBackend->updateMonotonicityBeforeSplitting(env, currentRegion);
179 auto splittingVariables = getSplittingVariables(currentRegion, Context::Partitioning);
181 currentRegion.splitLeafNodeAtCenter(splittingVariables, this->discreteVariables,
true);
182 for (
auto& child : currentRegion.subRegions) {
183 unprocessedRegions.emplace(child);
190 uint64_t numberOfRegionsKnownThroughMonotonicity{0};
191 std::vector<std::pair<storm::storage::ParameterRegion<ParametricType>,
RegionResult>> result;
193 if (node.subRegions.empty()) {
194 if (node.resultKnownThroughMonotonicity) {
195 ++numberOfRegionsKnownThroughMonotonicity;
197 result.emplace_back(node.region, node.result);
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.");
206 auto regionCopyForResult = region;
207 return std::make_unique<storm::modelchecker::RegionRefinementCheckResult<ParametricType>>(std::move(result), std::move(regionCopyForResult));
210template<
typename ParametricType>
227 std::priority_queue<std::reference_wrapper<AnnotatedRegion<ParametricType>>, std::vector<std::reference_wrapper<AnnotatedRegion<ParametricType>>>,
229 unprocessedRegions(cmp);
230 unprocessedRegions.push(rootRegion);
233 monotonicityBackend->initializeMonotonicity(env, rootRegion);
236 auto valueValuation = regionChecker->getAndEvaluateGoodPoint(env, rootRegion, dir);
237 auto& value = valueValuation.first;
238 if (rejectInstance(value)) {
239 return valueValuation;
243 auto isBetterThanValue = [&value, &dir](
auto const& newValue) {
return storm::solver::minimize(dir) ? newValue < value : newValue > value; };
247 uint64_t numOfAnalyzedRegions{0u};
248 while (!unprocessedRegions.empty()) {
249 auto& currentRegion = unprocessedRegions.top().get();
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();
256 ++numOfAnalyzedRegions;
258 monotonicityBackend->updateMonotonicity(env, currentRegion);
261 if (!currentBound || !acceptGlobalBound(value, currentBound.value())) {
263 currentBound = regionChecker->getBoundAtInitState(env, currentRegion, dir);
265 currentRegion.knownLowerValueBound &= *currentBound;
267 currentRegion.knownUpperValueBound &= *currentBound;
272 if (!acceptGlobalBound(value, currentBound.value())) {
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;
286 if (!acceptGlobalBound(value, currentBound.value())) {
287 monotonicityBackend->updateMonotonicityBeforeSplitting(env, currentRegion);
288 auto splittingVariables = getSplittingVariables(currentRegion, Context::ExtremalValue);
290 currentRegion.splitLeafNodeAtCenter(splittingVariables, this->discreteVariables,
true);
291 for (
auto& child : currentRegion.subRegions) {
292 unprocessedRegions.emplace(child);
295 progress.addDiscoveredArea(currentRegion.region.area());
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;
304template<
typename ParametricType>
308 bool absolutePrecision, std::optional<storm::logic::Bound>
const& boundInvariant) {
311 "Precision must be a constant value. Got " << precision <<
" instead.");
312 CoefficientType convertedPrecision = storm::utility::convertNumber<CoefficientType>(precision);
315 CoefficientType const usedPrecision = convertedPrecision * (absolutePrecision ? storm::utility::one<CoefficientType>() : value);
319 auto rejectInstance = [&](
CoefficientType currentValue) {
return boundInvariant && !boundInvariant->isSatisfied(currentValue); };
321 return computeExtremalValueHelper(env, region, dir, acceptGlobalBound, rejectInstance);
324template<
typename ParametricType>
331 isLowerBound(bound.
comparisonType) ? storm::solver::OptimizationDirection::Minimize : storm::solver::OptimizationDirection::Maximize;
337 auto res = computeExtremalValueHelper(env, region, dir, acceptGlobalBound, rejectInstance).first;
338 std::cout <<
"Extremal value: " << res << std::endl;
342template<
typename ParametricType>
346 if (this->regionSplittingStrategy.maxSplitDimensions >= region.
region.
getVariables().size()) {
350 auto const& estimates = regionChecker->obtainRegionSplitEstimates(region.
region.
getVariables());
351 std::vector<std::pair<VariableType, CoefficientType>> estimatesToSort;
354 auto estimatesIter = estimates.begin();
355 for (
auto const& param : region.region.getVariables()) {
356 estimatesToSort.push_back(std::make_pair(param, *estimatesIter++));
360 std::sort(estimatesToSort.begin(), estimatesToSort.end(), [](
const auto& a,
const auto& b) { return a.second > b.second; });
362 std::set<VariableType> splittingVars;
363 for (
auto const& estimate : estimatesToSort) {
370 if (region.
region.
getDifference(estimate.first) == storm::utility::zero<CoefficientType>()) {
373 splittingVars.emplace(estimate.first);
374 if (splittingVars.size() == regionSplittingStrategy.maxSplitDimensions) {
378 return splittingVars;
381template<
typename ParametricType>
382std::set<typename RegionRefinementChecker<ParametricType>::VariableType> RegionRefinementChecker<ParametricType>::getSplittingVariablesRoundRobin(
383 AnnotatedRegion<ParametricType>
const& region, Context context)
const {
385 auto const& vars = region.region.getVariables();
386 auto varsIter = vars.begin();
388 std::advance(varsIter, (region.refinementDepth * this->regionSplittingStrategy.maxSplitDimensions) % vars.size());
390 auto loopPoint = varsIter;
392 std::set<VariableType> splittingVars;
395 if (context == Context::ExtremalValue && region.monotonicityAnnotation.getGlobalMonotonicityResult() &&
396 region.monotonicityAnnotation.getGlobalMonotonicityResult()->isMonotone(*varsIter)) {
399 splittingVars.emplace(*varsIter);
400 std::advance(varsIter, 1);
401 if (varsIter == vars.end()) {
402 varsIter = vars.begin();
404 }
while (loopPoint != varsIter && splittingVars.size() < this->regionSplittingStrategy.maxSplitDimensions);
406 return splittingVars;
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);
424template class RegionRefinementChecker<storm::RationalFunction>;
storm::RationalNumber evaluateAsRational() const
Evaluates the expression and returns the resulting rational number.
void addAllIllDefinedArea(T const &area)
void addAllViolatedArea(T const &area)
bool isCoverageThresholdReached() const
T addDiscoveredArea(T const &area)
void addAllSatArea(T const &area)
uint64_t getUndiscoveredPercentage() const
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 ®ion, storm::logic::Bound const &bound)
Checks whether the bound is satisfied on the complete region.
RegionRefinementChecker(std::unique_ptr< RegionModelChecker< ParametricType > > &®ionChecker)
std::unique_ptr< storm::modelchecker::RegionRefinementCheckResult< ParametricType > > performRegionPartitioning(Environment const &env, storm::storage::ParameterRegion< ParametricType > const ®ion, 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 ®ion, 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 ®ion, 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)
#define STORM_LOG_TRACE(message)
#define STORM_LOG_ERROR(message)
#define STORM_LOG_ASSERT(cond, message)
#define STORM_LOG_ERROR_COND(cond, message)
#define STORM_LOG_THROW(cond, exception, message)
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 &)
ComparisonType comparisonType
storm::expressions::Expression threshold
bool isSatisfied(ValueType const &compareValue) const
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
uint64_t getMaxDepthOfSubRegions() const
Region const region
The subregions of this region.