25template<
typename ParametricType>
27 STORM_LOG_ASSERT(regionChecker !=
nullptr,
"The region model checker must not be null.");
28 this->regionChecker = std::move(regionChecker);
31template<
typename ParametricType>
34 return regionChecker->canHandle(parametricModel, checkTask);
37template<
typename ParametricType>
43 this->monotonicityBackend = monotonicityBackend ? monotonicityBackend : std::make_shared<MonotonicityBackend<ParametricType>>();
44 this->regionSplittingStrategy = std::move(splittingStrategy);
45 this->discreteVariables = std::move(discreteVariables);
46 this->graphPreservationStatus = graphPreservationStatus;
52 if (regionSplittingStrategy.estimateKind.has_value()) {
53 STORM_LOG_THROW(regionChecker->isRegionSplitEstimateKindSupported(regionSplittingStrategy.estimateKind.value(), checkTask),
54 storm::exceptions::NotSupportedException,
"The specified region split estimate kind is not supported by the region model checker.");
56 regionSplittingStrategy.estimateKind = regionChecker->getDefaultRegionSplitEstimateKind(checkTask);
57 STORM_LOG_ASSERT(regionChecker->isRegionSplitEstimateKindSupported(regionSplittingStrategy.estimateKind.value(), checkTask),
58 "The region model checker does not support its default region split estimate kind.");
61 regionSplittingStrategy.estimateKind = std::nullopt;
64 regionChecker->specify(env, parametricModel, checkTask, regionSplittingStrategy.estimateKind, monotonicityBackend, allowModelSimplifications,
65 graphPreservationStatus);
72 : totalArea(totalArea),
73 coverageThreshold(coverageThreshold),
77 progress(
"% covered area", delay) {
78 progress.setMaxCount(100 - asPercentage(coverageThreshold));
79 progress.startNewMeasurement(0u);
83 return asPercentage(fractionOfUndiscoveredArea);
87 return fractionOfUndiscoveredArea <= coverageThreshold;
91 auto addedFraction = area / totalArea;
92 fractionOfUndiscoveredArea -= addedFraction;
110 static uint64_t asPercentage(T
const& value) {
115 T
const coverageThreshold;
116 T fractionOfUndiscoveredArea;
117 T fractionOfAllSatArea;
118 T fractionOfAllViolatedArea;
119 T fractionOfAllIllDefinedArea;
123template<
typename ParametricType>
138 std::queue<std::reference_wrapper<AnnotatedRegion<ParametricType>>> unprocessedRegions;
139 unprocessedRegions.emplace(rootRegion);
141 uint64_t numOfAnalyzedRegions{0u};
142 bool monotonicityInitialized{
false};
145 while (!progress.isCoverageThresholdReached() && !unprocessedRegions.empty()) {
146 auto& currentRegion = unprocessedRegions.front().get();
147 STORM_LOG_TRACE(
"Analyzing region #" << numOfAnalyzedRegions <<
" (Refinement depth " << currentRegion.refinementDepth <<
"; "
148 << progress.getUndiscoveredPercentage() <<
"% still unknown; " << unprocessedRegions.size()
149 <<
" regions unprocessed).");
150 unprocessedRegions.pop();
151 ++numOfAnalyzedRegions;
153 if (!monotonicityInitialized && currentRegion.refinementDepth >= monThresh) {
154 monotonicityInitialized =
true;
155 monotonicityBackend->initializeMonotonicity(env, rootRegion);
157 if (currentRegion.refinementDepth > 0) {
161 if (monotonicityInitialized) {
162 monotonicityBackend->updateMonotonicity(env, currentRegion);
165 currentRegion.result = regionChecker->analyzeRegion(env, currentRegion, hypothesis);
168 progress.addAllSatArea(currentRegion.region.area());
170 progress.addAllViolatedArea(currentRegion.region.area());
174 "The region is not graph-preserving, but the selected region verification engine requires this assumption. Please use "
175 "--assume-graph-preserving false.");
176 progress.addAllIllDefinedArea(currentRegion.region.area());
179 if (!depthThreshold || currentRegion.refinementDepth < depthThreshold.value()) {
180 monotonicityBackend->updateMonotonicityBeforeSplitting(env, currentRegion);
181 auto splittingVariables = getSplittingVariables(currentRegion, Context::Partitioning);
183 currentRegion.splitLeafNodeAtCenter(splittingVariables, this->discreteVariables,
true);
184 for (
auto& child : currentRegion.subRegions) {
185 unprocessedRegions.emplace(child);
192 uint64_t numberOfRegionsKnownThroughMonotonicity{0};
193 std::vector<std::pair<storm::storage::ParameterRegion<ParametricType>,
RegionResult>> result;
195 if (node.subRegions.empty()) {
196 if (node.resultKnownThroughMonotonicity) {
197 ++numberOfRegionsKnownThroughMonotonicity;
199 result.emplace_back(node.region, node.result);
203 STORM_LOG_INFO(
"Region partitioning terminated after analyzing " << numOfAnalyzedRegions <<
" regions.\n\t" << numberOfRegionsKnownThroughMonotonicity
204 <<
" regions known through monotonicity.\n\tMaximum refinement depth: " << maxDepth
205 <<
".\n\t" << progress.getUndiscoveredPercentage()
206 <<
"% of the parameter space are not covered.");
208 auto regionCopyForResult =
region;
209 return std::make_unique<storm::modelchecker::RegionRefinementCheckResult<ParametricType>>(std::move(result), std::move(regionCopyForResult));
212template<
typename ParametricType>
226 rhs) {
return lhs.
knownLowerValueBound.getExtendedValue() > rhs.knownLowerValueBound.getExtendedValue(); }
228 return lhs.
knownUpperValueBound.getExtendedValue() < rhs.knownUpperValueBound.getExtendedValue();
230 std::priority_queue<std::reference_wrapper<AnnotatedRegion<ParametricType>>, std::vector<std::reference_wrapper<AnnotatedRegion<ParametricType>>>,
232 unprocessedRegions(cmp);
233 unprocessedRegions.push(rootRegion);
236 monotonicityBackend->initializeMonotonicity(env, rootRegion);
239 auto valueValuation = regionChecker->getAndEvaluateGoodPoint(env, rootRegion, dir);
240 auto& value = valueValuation.first;
241 if (rejectInstance(value)) {
242 return valueValuation;
246 auto isBetterThanValue = [&value, &dir](
auto const& newValue) {
return storm::solver::minimize(dir) ? newValue < value : newValue > value; };
250 uint64_t numOfAnalyzedRegions{0u};
251 while (!unprocessedRegions.empty()) {
252 auto& currentRegion = unprocessedRegions.top().get();
254 storm::solver::minimize(dir) ? currentRegion.knownLowerValueBound.getExtendedValue() : currentRegion.knownUpperValueBound.getExtendedValue();
255 STORM_LOG_TRACE(
"Analyzing region #" << numOfAnalyzedRegions <<
" (Refinement depth " << currentRegion.refinementDepth <<
"; "
256 << progress.getUndiscoveredPercentage() <<
"% still unknown; " << unprocessedRegions.size()
257 <<
" regions unprocessed). Best known value: " << value <<
".");
258 unprocessedRegions.pop();
259 ++numOfAnalyzedRegions;
261 monotonicityBackend->updateMonotonicity(env, currentRegion);
264 if (!acceptGlobalBound(value, currentBound)) {
266 currentBound = regionChecker->getBoundAtInitState(env, currentRegion, dir);
268 currentRegion.knownLowerValueBound &= currentBound;
270 currentRegion.knownUpperValueBound &= currentBound;
275 if (!acceptGlobalBound(value, currentBound)) {
278 auto [currValue, currValuation] = regionChecker->getAndEvaluateGoodPoint(env, currentRegion, dir);
279 if (isBetterThanValue(currValue)) {
280 valueValuation = {currValue, currValuation};
281 if (rejectInstance(currValue)) {
282 STORM_LOG_INFO(
"Found rejecting instance " << currValuation <<
" with value " << currValue);
283 return valueValuation;
289 if (!acceptGlobalBound(value, currentBound)) {
290 monotonicityBackend->updateMonotonicityBeforeSplitting(env, currentRegion);
291 auto splittingVariables = getSplittingVariables(currentRegion, Context::ExtremalValue);
293 currentRegion.splitLeafNodeAtCenter(splittingVariables, this->discreteVariables,
true);
294 for (
auto& child : currentRegion.subRegions) {
295 unprocessedRegions.emplace(child);
298 progress.addDiscoveredArea(currentRegion.region.area());
302 STORM_LOG_INFO(
"Region partitioning for extremal value terminated after analyzing "
303 << numOfAnalyzedRegions <<
" regions.\n\t" << progress.getUndiscoveredPercentage() <<
"% of the parameter space are not covered.");
304 return valueValuation;
307template<
typename ParametricType>
311 bool absolutePrecision, std::optional<storm::logic::Bound>
const& boundInvariant) {
314 "Precision must be a constant value. Got " << precision <<
" instead.");
319 return newValue == value;
325 auto rejectInstance = [&](
ExtendedCoefficientType const& currentValue) {
return boundInvariant && !boundInvariant->isSatisfied(currentValue); };
330template<
typename ParametricType>
337 isLowerBound(bound.
comparisonType) ? storm::solver::OptimizationDirection::Minimize : storm::solver::OptimizationDirection::Maximize;
348template<
typename ParametricType>
349std::set<typename RegionRefinementChecker<ParametricType>::VariableType> RegionRefinementChecker<ParametricType>::getSplittingVariablesEstimateBased(
352 if (this->regionSplittingStrategy.maxSplitDimensions >=
region.region.getVariables().size()) {
353 return region.region.getVariables();
356 auto const& estimates = regionChecker->obtainRegionSplitEstimates(region.
region.
getVariables());
357 std::vector<std::pair<VariableType, CoefficientType>> estimatesToSort;
360 auto estimatesIter = estimates.begin();
362 estimatesToSort.push_back(std::make_pair(param, *estimatesIter++));
366 std::sort(estimatesToSort.begin(), estimatesToSort.end(), [](
const auto& a,
const auto& b) { return a.second > b.second; });
368 std::set<VariableType> splittingVars;
369 for (
auto const& estimate : estimatesToSort) {
379 splittingVars.emplace(estimate.first);
380 if (splittingVars.size() == regionSplittingStrategy.maxSplitDimensions) {
384 return splittingVars;
387template<
typename ParametricType>
388std::set<typename RegionRefinementChecker<ParametricType>::VariableType> RegionRefinementChecker<ParametricType>::getSplittingVariablesRoundRobin(
391 auto const& vars =
region.region.getVariables();
392 auto varsIter = vars.begin();
394 std::advance(varsIter, (
region.refinementDepth * this->regionSplittingStrategy.maxSplitDimensions) % vars.size());
396 auto loopPoint = varsIter;
398 std::set<VariableType> splittingVars;
401 if (context == Context::ExtremalValue &&
region.monotonicityAnnotation.getGlobalMonotonicityResult() &&
402 region.monotonicityAnnotation.getGlobalMonotonicityResult()->isMonotone(*varsIter)) {
405 splittingVars.emplace(*varsIter);
406 std::advance(varsIter, 1);
407 if (varsIter == vars.end()) {
408 varsIter = vars.begin();
410 }
while (loopPoint != varsIter && splittingVars.size() < this->regionSplittingStrategy.maxSplitDimensions);
412 return splittingVars;
415template<
typename ParametricType>
416std::set<typename RegionRefinementChecker<ParametricType>::VariableType> RegionRefinementChecker<ParametricType>::getSplittingVariables(
418 switch (regionSplittingStrategy.heuristic) {
420 return getSplittingVariablesEstimateBased(
region, context);
422 return getSplittingVariablesRoundRobin(
region, context);
SolverEnvironment & solver()
uint64_t getShowProgressDelay() const
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, uint64_t delay, 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.
storm::utility::ExtendedValueType< CoefficientType > ExtendedCoefficientType
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< ExtendedCoefficientType, 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.
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, storm::pars::analysis::GraphPreservationStatus graphPreservationStatus=storm::pars::analysis::GraphPreservationStatus::GraphPreserving)
std::pair< ExtendedCoefficientType, Valuation > computeExtremalValueHelper(Environment const &env, storm::storage::ParameterRegion< ParametricType > const ®ion, storm::solver::OptimizationDirection const &dir, std::function< bool(ExtendedCoefficientType const &, ExtendedCoefficientType const &)> acceptGlobalBound, std::function< bool(ExtendedCoefficientType const &)> rejectInstance)
Finds the extremal value within the given region and with the given precision.
std::set< VariableType > const & getVariables() const
storm::utility::parametric::Valuation< ParametricType > Valuation
A class that provides convenience operations to display run times.
#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
GraphPreservationStatus
Describes to what extent a set of regions is known to be well-defined and to preserve the graph struc...
bool isGraphPreserving(GraphPreservationStatus status)
Returns whether the given status guarantees that the graph structure of the model is preserved.
bool constexpr minimize(OptimizationDirection d)
bool isFinite(ExtendedNumber< ValueType > const &value)
bool isConstant(ValueType const &)
ValueType round(ValueType const &number)
TargetType convertNumber(SourceType const &number)
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.