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());
170 STORM_LOG_THROW(!this->graphPreserving, storm::exceptions::NotImplementedException,
171 "The region is not graph-preserving, but the selected region verification engine requires this assumption. Please use "
172 "--assume-graph-preserving false.");
173 progress.addAllIllDefinedArea(currentRegion.region.area());
176 if (!depthThreshold || currentRegion.refinementDepth < depthThreshold.value()) {
177 monotonicityBackend->updateMonotonicityBeforeSplitting(env, currentRegion);
178 auto splittingVariables = getSplittingVariables(currentRegion, Context::Partitioning);
180 currentRegion.splitLeafNodeAtCenter(splittingVariables, this->discreteVariables,
true);
181 for (
auto& child : currentRegion.subRegions) {
182 unprocessedRegions.emplace(child);
189 uint64_t numberOfRegionsKnownThroughMonotonicity{0};
190 std::vector<std::pair<storm::storage::ParameterRegion<ParametricType>,
RegionResult>> result;
192 if (node.subRegions.empty()) {
193 if (node.resultKnownThroughMonotonicity) {
194 ++numberOfRegionsKnownThroughMonotonicity;
196 result.emplace_back(node.region, node.result);
200 STORM_LOG_INFO(
"Region partitioning terminated after analyzing " << numOfAnalyzedRegions <<
" regions.\n\t" << numberOfRegionsKnownThroughMonotonicity
201 <<
" regions known through monotonicity.\n\tMaximum refinement depth: " << maxDepth
202 <<
".\n\t" << progress.getUndiscoveredPercentage()
203 <<
"% of the parameter space are not covered.");
205 auto regionCopyForResult = region;
206 return std::make_unique<storm::modelchecker::RegionRefinementCheckResult<ParametricType>>(std::move(result), std::move(regionCopyForResult));
209template<
typename ParametricType>
226 std::priority_queue<std::reference_wrapper<AnnotatedRegion<ParametricType>>, std::vector<std::reference_wrapper<AnnotatedRegion<ParametricType>>>,
228 unprocessedRegions(cmp);
229 unprocessedRegions.push(rootRegion);
232 monotonicityBackend->initializeMonotonicity(env, rootRegion);
235 auto valueValuation = regionChecker->getAndEvaluateGoodPoint(env, rootRegion, dir);
236 auto& value = valueValuation.first;
237 if (rejectInstance(value)) {
238 return valueValuation;
242 auto isBetterThanValue = [&value, &dir](
auto const& newValue) {
return storm::solver::minimize(dir) ? newValue < value : newValue > value; };
246 uint64_t numOfAnalyzedRegions{0u};
247 while (!unprocessedRegions.empty()) {
248 auto& currentRegion = unprocessedRegions.top().get();
250 storm::solver::minimize(dir) ? currentRegion.knownLowerValueBound.getOptionalValue() : currentRegion.knownUpperValueBound.getOptionalValue();
251 STORM_LOG_TRACE(
"Analyzing region #" << numOfAnalyzedRegions <<
" (Refinement depth " << currentRegion.refinementDepth <<
"; "
252 << progress.getUndiscoveredPercentage() <<
"% still unknown; " << unprocessedRegions.size()
253 <<
" regions unprocessed). Best known value: " << value <<
".");
254 unprocessedRegions.pop();
255 ++numOfAnalyzedRegions;
257 monotonicityBackend->updateMonotonicity(env, currentRegion);
260 if (!currentBound || !acceptGlobalBound(value, currentBound.value())) {
262 currentBound = regionChecker->getBoundAtInitState(env, currentRegion, dir);
264 currentRegion.knownLowerValueBound &= *currentBound;
266 currentRegion.knownUpperValueBound &= *currentBound;
271 if (!acceptGlobalBound(value, currentBound.value())) {
274 auto [currValue, currValuation] = regionChecker->getAndEvaluateGoodPoint(env, currentRegion, dir);
275 if (isBetterThanValue(currValue)) {
276 valueValuation = {currValue, currValuation};
277 if (rejectInstance(currValue)) {
278 STORM_LOG_INFO(
"Found rejecting instance " << currValuation <<
" with value " << currValue);
279 return valueValuation;
285 if (!acceptGlobalBound(value, currentBound.value())) {
286 monotonicityBackend->updateMonotonicityBeforeSplitting(env, currentRegion);
287 auto splittingVariables = getSplittingVariables(currentRegion, Context::ExtremalValue);
289 currentRegion.splitLeafNodeAtCenter(splittingVariables, this->discreteVariables,
true);
290 for (
auto& child : currentRegion.subRegions) {
291 unprocessedRegions.emplace(child);
294 progress.addDiscoveredArea(currentRegion.region.area());
298 std::cout <<
"Region partitioning for extremal value terminated after analyzing " << numOfAnalyzedRegions <<
" regions.\n\t"
299 << progress.getUndiscoveredPercentage() <<
"% of the parameter space are not covered.\n";
300 return valueValuation;
303template<
typename ParametricType>
307 bool absolutePrecision, std::optional<storm::logic::Bound>
const& boundInvariant) {
310 "Precision must be a constant value. Got " << precision <<
" instead.");
311 CoefficientType convertedPrecision = storm::utility::convertNumber<CoefficientType>(precision);
314 CoefficientType const usedPrecision = convertedPrecision * (absolutePrecision ? storm::utility::one<CoefficientType>() : value);
318 auto rejectInstance = [&](
CoefficientType currentValue) {
return boundInvariant && !boundInvariant->isSatisfied(currentValue); };
320 return computeExtremalValueHelper(env, region, dir, acceptGlobalBound, rejectInstance);
323template<
typename ParametricType>
330 isLowerBound(bound.
comparisonType) ? storm::solver::OptimizationDirection::Minimize : storm::solver::OptimizationDirection::Maximize;
336 auto res = computeExtremalValueHelper(env, region, dir, acceptGlobalBound, rejectInstance).first;
337 std::cout <<
"Extremal value: " << res << std::endl;
341template<
typename ParametricType>
345 if (this->regionSplittingStrategy.maxSplitDimensions >= region.
region.
getVariables().size()) {
349 auto const& estimates = regionChecker->obtainRegionSplitEstimates(region.
region.
getVariables());
350 std::vector<std::pair<VariableType, CoefficientType>> estimatesToSort;
353 auto estimatesIter = estimates.begin();
354 for (
auto const& param : region.region.getVariables()) {
355 estimatesToSort.push_back(std::make_pair(param, *estimatesIter++));
359 std::sort(estimatesToSort.begin(), estimatesToSort.end(), [](
const auto& a,
const auto& b) { return a.second > b.second; });
361 std::set<VariableType> splittingVars;
362 for (
auto const& estimate : estimatesToSort) {
369 if (region.
region.
getDifference(estimate.first) == storm::utility::zero<CoefficientType>()) {
372 splittingVars.emplace(estimate.first);
373 if (splittingVars.size() == regionSplittingStrategy.maxSplitDimensions) {
377 return splittingVars;
380template<
typename ParametricType>
381std::set<typename RegionRefinementChecker<ParametricType>::VariableType> RegionRefinementChecker<ParametricType>::getSplittingVariablesRoundRobin(
382 AnnotatedRegion<ParametricType>
const& region, Context context)
const {
384 auto const& vars = region.region.getVariables();
385 auto varsIter = vars.begin();
387 std::advance(varsIter, (region.refinementDepth * this->regionSplittingStrategy.maxSplitDimensions) % vars.size());
389 auto loopPoint = varsIter;
391 std::set<VariableType> splittingVars;
394 if (context == Context::ExtremalValue && region.monotonicityAnnotation.getGlobalMonotonicityResult() &&
395 region.monotonicityAnnotation.getGlobalMonotonicityResult()->isMonotone(*varsIter)) {
398 splittingVars.emplace(*varsIter);
399 std::advance(varsIter, 1);
400 if (varsIter == vars.end()) {
401 varsIter = vars.begin();
403 }
while (loopPoint != varsIter && splittingVars.size() < this->regionSplittingStrategy.maxSplitDimensions);
405 return splittingVars;
408template<
typename ParametricType>
409std::set<typename RegionRefinementChecker<ParametricType>::VariableType> RegionRefinementChecker<ParametricType>::getSplittingVariables(
410 AnnotatedRegion<ParametricType>
const& region, Context context)
const {
411 switch (regionSplittingStrategy.heuristic) {
413 return getSplittingVariablesEstimateBased(region, context);
415 return getSplittingVariablesRoundRobin(region, context);
423template 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_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.