597 lines
44 KiB
597 lines
44 KiB
#include "storm-pars/modelchecker/region/SparseParameterLiftingModelChecker.h"
|
|
|
|
#include <queue>
|
|
#include <boost/container/flat_set.hpp>
|
|
#include <storm-pars/analysis/MonotonicityChecker.h>
|
|
|
|
#include "storm/adapters/RationalFunctionAdapter.h"
|
|
#include "storm/logic/FragmentSpecification.h"
|
|
#include "storm/modelchecker/results/ExplicitQuantitativeCheckResult.h"
|
|
#include "storm/modelchecker/results/ExplicitQualitativeCheckResult.h"
|
|
#include "storm/utility/vector.h"
|
|
#include "storm/models/sparse/Dtmc.h"
|
|
#include "storm/models/sparse/Mdp.h"
|
|
#include "storm/models/sparse/StandardRewardModel.h"
|
|
|
|
#include "storm/exceptions/InvalidArgumentException.h"
|
|
#include "storm/exceptions/NotSupportedException.h"
|
|
|
|
namespace storm {
|
|
namespace modelchecker {
|
|
|
|
template <typename SparseModelType, typename ConstantType>
|
|
SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::SparseParameterLiftingModelChecker() {
|
|
//Intentionally left empty
|
|
}
|
|
|
|
template <typename SparseModelType, typename ConstantType>
|
|
void SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::specifyFormula(Environment const& env, storm::modelchecker::CheckTask<storm::logic::Formula, typename SparseModelType::ValueType> const& checkTask) {
|
|
|
|
currentFormula = checkTask.getFormula().asSharedPointer();
|
|
currentCheckTask = std::make_unique<storm::modelchecker::CheckTask<storm::logic::Formula, ConstantType>>(checkTask.substituteFormula(*currentFormula).template convertValueType<ConstantType>());
|
|
|
|
if (currentCheckTask->getFormula().isProbabilityOperatorFormula()) {
|
|
auto const& probOpFormula = currentCheckTask->getFormula().asProbabilityOperatorFormula();
|
|
if(probOpFormula.getSubformula().isBoundedUntilFormula()) {
|
|
specifyBoundedUntilFormula(env, currentCheckTask->substituteFormula(probOpFormula.getSubformula().asBoundedUntilFormula()));
|
|
} else if(probOpFormula.getSubformula().isUntilFormula()) {
|
|
specifyUntilFormula(env, currentCheckTask->substituteFormula(probOpFormula.getSubformula().asUntilFormula()));
|
|
} else if (probOpFormula.getSubformula().isEventuallyFormula()) {
|
|
specifyReachabilityProbabilityFormula(env, currentCheckTask->substituteFormula(probOpFormula.getSubformula().asEventuallyFormula()));
|
|
} else {
|
|
STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Parameter lifting is not supported for the given property.");
|
|
}
|
|
} else if (currentCheckTask->getFormula().isRewardOperatorFormula()) {
|
|
auto const& rewOpFormula = currentCheckTask->getFormula().asRewardOperatorFormula();
|
|
if(rewOpFormula.getSubformula().isEventuallyFormula()) {
|
|
specifyReachabilityRewardFormula(env, currentCheckTask->substituteFormula(rewOpFormula.getSubformula().asEventuallyFormula()));
|
|
} else if (rewOpFormula.getSubformula().isCumulativeRewardFormula()) {
|
|
specifyCumulativeRewardFormula(env, currentCheckTask->substituteFormula(rewOpFormula.getSubformula().asCumulativeRewardFormula()));
|
|
}
|
|
}
|
|
}
|
|
|
|
template <typename SparseModelType, typename ConstantType>
|
|
RegionResult SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::analyzeRegion(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, RegionResultHypothesis const& hypothesis, RegionResult const& initialResult, bool sampleVerticesOfRegion, std::shared_ptr<storm::analysis::Order> reachabilityOrder, std::shared_ptr<storm::analysis::LocalMonotonicityResult<typename RegionModelChecker<typename SparseModelType::ValueType>::VariableType>> localMonotonicityResult) {
|
|
typedef typename RegionModelChecker<typename SparseModelType::ValueType>::VariableType VariableType;
|
|
typedef typename storm::analysis::MonotonicityResult<VariableType>::Monotonicity Monotonicity;
|
|
typedef typename storm::utility::parametric::Valuation<typename SparseModelType::ValueType> Valuation;
|
|
typedef typename storm::storage::ParameterRegion<typename SparseModelType::ValueType>::CoefficientType CoefficientType;
|
|
STORM_LOG_THROW(this->currentCheckTask->isOnlyInitialStatesRelevantSet(), storm::exceptions::NotSupportedException, "Analyzing regions with parameter lifting requires a property where only the value in the initial states is relevant.");
|
|
STORM_LOG_THROW(this->currentCheckTask->isBoundSet(), storm::exceptions::NotSupportedException, "Analyzing regions with parameter lifting requires a bounded property.");
|
|
STORM_LOG_THROW(this->parametricModel->getInitialStates().getNumberOfSetBits() == 1, storm::exceptions::NotSupportedException, "Analyzing regions with parameter lifting requires a model with a single initial state.");
|
|
|
|
RegionResult result = initialResult;
|
|
|
|
// Check if we need to check the formula on one point to decide whether to show AllSat or AllViolated
|
|
if (hypothesis == RegionResultHypothesis::Unknown && result == RegionResult::Unknown) {
|
|
result = getInstantiationChecker().check(env, region.getCenterPoint())->asExplicitQualitativeCheckResult()[*this->parametricModel->getInitialStates().begin()] ? RegionResult::CenterSat : RegionResult::CenterViolated;
|
|
}
|
|
|
|
bool existsSat = (hypothesis == RegionResultHypothesis::AllSat || result == RegionResult::ExistsSat || result == RegionResult::CenterSat);
|
|
bool existsViolated = (hypothesis == RegionResultHypothesis::AllViolated || result == RegionResult::ExistsViolated || result == RegionResult::CenterViolated);
|
|
|
|
// Here we check on global monotonicity
|
|
if (localMonotonicityResult != nullptr && localMonotonicityResult->isDone()) {
|
|
// Try to check it with a global monotonicity result
|
|
auto monRes = localMonotonicityResult->getGlobalMonotonicityResult();
|
|
bool lowerBound = isLowerBound(this->currentCheckTask->getBound().comparisonType);
|
|
|
|
if (monRes->isDone() && monRes->isAllMonotonicity()) {
|
|
// Build valuations
|
|
auto monMap = monRes->getMonotonicityResult();
|
|
Valuation valuationToCheckSat;
|
|
Valuation valuationToCheckViolated;
|
|
for (auto var : region.getVariables()) {
|
|
auto monVar = monMap[var];
|
|
if (monVar == Monotonicity::Constant) {
|
|
valuationToCheckSat.insert(std::pair<VariableType, CoefficientType>(var, region.getLowerBoundary(var)));
|
|
valuationToCheckViolated.insert(std::pair<VariableType, CoefficientType>(var, region.getLowerBoundary(var)));
|
|
} else if (monVar == Monotonicity::Decr) {
|
|
if (lowerBound) {
|
|
valuationToCheckSat.insert(std::pair<VariableType, CoefficientType>(var, region.getUpperBoundary(var)));
|
|
valuationToCheckViolated.insert(std::pair<VariableType, CoefficientType>(var, region.getLowerBoundary(var)));
|
|
} else {
|
|
valuationToCheckSat.insert(std::pair<VariableType, CoefficientType>(var, region.getLowerBoundary(var)));
|
|
valuationToCheckViolated.insert(std::pair<VariableType, CoefficientType>(var, region.getUpperBoundary(var)));
|
|
}
|
|
} else if (monVar == Monotonicity::Incr) {
|
|
if (lowerBound) {
|
|
valuationToCheckSat.insert(std::pair<VariableType, CoefficientType>(var, region.getLowerBoundary(var)));
|
|
valuationToCheckViolated.insert(std::pair<VariableType, CoefficientType>(var, region.getUpperBoundary(var)));
|
|
} else {
|
|
valuationToCheckSat.insert(std::pair<VariableType, CoefficientType>(var, region.getUpperBoundary(var)));
|
|
valuationToCheckViolated.insert(std::pair<VariableType, CoefficientType>(var, region.getLowerBoundary(var)));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check for result
|
|
if (existsSat && getInstantiationCheckerSAT().check(env, valuationToCheckSat)->asExplicitQualitativeCheckResult()[*this->parametricModel->getInitialStates().begin()]) {
|
|
STORM_LOG_INFO("Region " << region << " is AllSat, discovered with instantiation checker on " << valuationToCheckSat << " and help of monotonicity" << std::endl);
|
|
RegionModelChecker<typename SparseModelType::ValueType>::numberOfRegionsKnownThroughMonotonicity++;
|
|
return RegionResult::AllSat;
|
|
}
|
|
|
|
if (existsViolated && !getInstantiationCheckerVIO().check(env, valuationToCheckViolated)->asExplicitQualitativeCheckResult()[*this->parametricModel->getInitialStates().begin()]) {
|
|
STORM_LOG_INFO("Region " << region << " is AllViolated, discovered with instantiation checker on " << valuationToCheckViolated << " and help of monotonicity" << std::endl);
|
|
RegionModelChecker<typename SparseModelType::ValueType>::numberOfRegionsKnownThroughMonotonicity++;
|
|
return RegionResult::AllViolated;
|
|
}
|
|
|
|
return RegionResult::ExistsBoth;
|
|
}
|
|
}
|
|
|
|
// Try to prove AllSat or AllViolated, depending on the hypothesis or the current result
|
|
if (existsSat) {
|
|
// show AllSat:
|
|
storm::solver::OptimizationDirection parameterOptimizationDirection = isLowerBound(this->currentCheckTask->getBound().comparisonType) ? storm::solver::OptimizationDirection::Minimize : storm::solver::OptimizationDirection::Maximize;
|
|
auto checkResult = this->check(env, region, parameterOptimizationDirection, reachabilityOrder, localMonotonicityResult);
|
|
if (checkResult->asExplicitQualitativeCheckResult()[*this->parametricModel->getInitialStates().begin()]) {
|
|
result = RegionResult::AllSat;
|
|
} else if (sampleVerticesOfRegion) {
|
|
result = sampleVertices(env, region, result);
|
|
}
|
|
} else if (existsViolated) {
|
|
// show AllViolated:
|
|
storm::solver::OptimizationDirection parameterOptimizationDirection = isLowerBound(this->currentCheckTask->getBound().comparisonType) ? storm::solver::OptimizationDirection::Maximize : storm::solver::OptimizationDirection::Minimize;
|
|
auto checkResult = this->check(env, region, parameterOptimizationDirection, reachabilityOrder, localMonotonicityResult);
|
|
if (!checkResult->asExplicitQualitativeCheckResult()[*this->parametricModel->getInitialStates().begin()]) {
|
|
result = RegionResult::AllViolated;
|
|
} else if (sampleVerticesOfRegion) {
|
|
result = sampleVertices(env, region, result);
|
|
}
|
|
} else {
|
|
STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "When analyzing a region, an invalid initial result was given: " << initialResult);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
template <typename SparseModelType, typename ConstantType>
|
|
RegionResult SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::sampleVertices(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, RegionResult const& initialResult) {
|
|
RegionResult result = initialResult;
|
|
|
|
if (result == RegionResult::AllSat || result == RegionResult::AllViolated) {
|
|
return result;
|
|
}
|
|
|
|
bool hasSatPoint = result == RegionResult::ExistsSat || result == RegionResult::CenterSat;
|
|
bool hasViolatedPoint = result == RegionResult::ExistsViolated || result == RegionResult::CenterViolated;
|
|
|
|
// Check if there is a point in the region for which the property is satisfied
|
|
auto vertices = region.getVerticesOfRegion(region.getVariables());
|
|
auto vertexIt = vertices.begin();
|
|
while (vertexIt != vertices.end() && !(hasSatPoint && hasViolatedPoint)) {
|
|
if (getInstantiationChecker().check(env, *vertexIt)->asExplicitQualitativeCheckResult()[*this->parametricModel->getInitialStates().begin()]) {
|
|
hasSatPoint = true;
|
|
} else {
|
|
hasViolatedPoint = true;
|
|
}
|
|
++vertexIt;
|
|
}
|
|
|
|
if (hasSatPoint) {
|
|
if (hasViolatedPoint) {
|
|
result = RegionResult::ExistsBoth;
|
|
} else if (result != RegionResult::CenterSat) {
|
|
result = RegionResult::ExistsSat;
|
|
}
|
|
} else if (hasViolatedPoint && result != RegionResult::CenterViolated) {
|
|
result = RegionResult::ExistsViolated;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
template <typename SparseModelType, typename ConstantType>
|
|
std::unique_ptr<CheckResult> SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::check(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dirForParameters, std::shared_ptr<storm::analysis::Order> reachabilityOrder, std::shared_ptr<storm::analysis::LocalMonotonicityResult<typename RegionModelChecker<typename SparseModelType::ValueType>::VariableType>> localMonotonicityResult) {
|
|
auto quantitativeResult = computeQuantitativeValues(env, region, dirForParameters, localMonotonicityResult);
|
|
lastValue = quantitativeResult->template asExplicitQuantitativeCheckResult<ConstantType>()[*this->parametricModel->getInitialStates().begin()];
|
|
if(currentCheckTask->getFormula().hasQuantitativeResult()) {
|
|
return quantitativeResult;
|
|
} else {
|
|
return quantitativeResult->template asExplicitQuantitativeCheckResult<ConstantType>().compareAgainstBound(this->currentCheckTask->getFormula().asOperatorFormula().getComparisonType(), this->currentCheckTask->getFormula().asOperatorFormula().template getThresholdAs<ConstantType>());
|
|
}
|
|
}
|
|
|
|
template <typename SparseModelType, typename ConstantType>
|
|
std::unique_ptr<QuantitativeCheckResult<ConstantType>> SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::getBound(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dirForParameters, std::shared_ptr<storm::analysis::LocalMonotonicityResult<typename RegionModelChecker<typename SparseModelType::ValueType>::VariableType>> localMonotonicityResult) {
|
|
STORM_LOG_WARN_COND(this->currentCheckTask->getFormula().hasQuantitativeResult(), "Computing quantitative bounds for a qualitative formula...");
|
|
return std::make_unique<ExplicitQuantitativeCheckResult<ConstantType>>(std::move(computeQuantitativeValues(env, region, dirForParameters, localMonotonicityResult)->template asExplicitQuantitativeCheckResult<ConstantType>()));
|
|
}
|
|
|
|
template <typename SparseModelType, typename ConstantType>
|
|
typename SparseModelType::ValueType SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::getBoundAtInitState(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dirForParameters) {
|
|
STORM_LOG_THROW(this->parametricModel->getInitialStates().getNumberOfSetBits() == 1, storm::exceptions::NotSupportedException, "Getting a bound at the initial state requires a model with a single initial state.");
|
|
return storm::utility::convertNumber<typename SparseModelType::ValueType>(getBound(env, region, dirForParameters)->template asExplicitQuantitativeCheckResult<ConstantType>()[*this->parametricModel->getInitialStates().begin()]);
|
|
}
|
|
|
|
template <typename SparseModelType, typename ConstantType>
|
|
storm::modelchecker::SparseInstantiationModelChecker<SparseModelType, ConstantType>& SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::getInstantiationCheckerSAT() {
|
|
return getInstantiationChecker();
|
|
}
|
|
|
|
template <typename SparseModelType, typename ConstantType>
|
|
storm::modelchecker::SparseInstantiationModelChecker<SparseModelType, ConstantType>& SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::getInstantiationCheckerVIO() {
|
|
return getInstantiationChecker();
|
|
}
|
|
|
|
template <typename SparseModelType, typename ConstantType>
|
|
struct RegionBound {
|
|
typedef typename storm::storage::ParameterRegion<typename SparseModelType::ValueType>::VariableType VariableType;
|
|
|
|
RegionBound(RegionBound<SparseModelType, ConstantType> const& other) = default;
|
|
RegionBound(storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& r, std::shared_ptr<storm::analysis::Order> o, std::shared_ptr<storm::analysis::LocalMonotonicityResult<VariableType>> l, ConstantType const& b) : region(r), order(o), localMonRes(l), bound(b) {}
|
|
storm::storage::ParameterRegion<typename SparseModelType::ValueType> region;
|
|
std::shared_ptr<storm::analysis::Order> order;
|
|
std::shared_ptr<storm::analysis::LocalMonotonicityResult<VariableType>> localMonRes;
|
|
ConstantType bound;
|
|
};
|
|
|
|
template<typename SparseModelType, typename ConstantType>
|
|
std::pair<typename SparseModelType::ValueType, typename storm::storage::ParameterRegion<typename SparseModelType::ValueType>::Valuation> SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::computeExtremalValue(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dir, typename SparseModelType::ValueType const& precision, boost::optional<ConstantType> const& initialValue) {
|
|
typedef typename storm::storage::ParameterRegion<typename SparseModelType::ValueType>::CoefficientType CoefficientType;
|
|
typedef typename storm::storage::ParameterRegion<typename SparseModelType::ValueType>::Valuation Valuation;
|
|
STORM_LOG_THROW(this->parametricModel->getInitialStates().getNumberOfSetBits() == 1, storm::exceptions::NotSupportedException, "Getting extremal values at the initial state requires a model with a single initial state.");
|
|
bool const useMonotonicity = this->isUseMonotonicitySet();
|
|
bool const minimize = storm::solver::minimize(dir);
|
|
|
|
// Comparator for the region queue
|
|
auto cmp = storm::solver::minimize(dir) ?
|
|
[](RegionBound<SparseModelType, ConstantType> const& lhs, RegionBound<SparseModelType, ConstantType> const& rhs) { return lhs.bound > rhs.bound; } :
|
|
[](RegionBound<SparseModelType, ConstantType> const& lhs, RegionBound<SparseModelType, ConstantType> const& rhs) { return lhs.bound < rhs.bound; };
|
|
std::priority_queue<RegionBound<SparseModelType, ConstantType>, std::vector<RegionBound<SparseModelType, ConstantType>>, decltype(cmp)> regionQueue(cmp);
|
|
storm::utility::Stopwatch initialWatch(true);
|
|
|
|
storm::utility::Stopwatch boundsWatch(false);
|
|
auto numberOfPLACallsBounds = 0;
|
|
ConstantType initBound;
|
|
initBound = storm::utility::zero<ConstantType>();
|
|
bool first = true;
|
|
if (useMonotonicity) {
|
|
if (this->isUseBoundsSet()) {
|
|
numberOfPLACallsBounds++;
|
|
numberOfPLACallsBounds++;
|
|
auto minBound = getBound(env, region, storm::solver::OptimizationDirection::Minimize, nullptr)->template asExplicitQuantitativeCheckResult<ConstantType>().getValueVector();
|
|
auto maxBound = getBound(env, region, storm::solver::OptimizationDirection::Maximize, nullptr)->template asExplicitQuantitativeCheckResult<ConstantType>().getValueVector();
|
|
if (minimize) {
|
|
initBound = minBound[*this->parametricModel->getInitialStates().begin()];
|
|
} else {
|
|
initBound = maxBound[*this->parametricModel->getInitialStates().begin()];
|
|
}
|
|
orderExtender->setMinValuesInit(minBound);
|
|
orderExtender->setMaxValuesInit(maxBound);
|
|
}
|
|
auto order = this->extendOrder(env, nullptr, region);
|
|
auto monRes = std::shared_ptr<storm::analysis::LocalMonotonicityResult<VariableType>>(new storm::analysis::LocalMonotonicityResult<VariableType>(order->getNumberOfStates()));
|
|
storm::utility::Stopwatch monotonicityWatch(true);
|
|
this->extendLocalMonotonicityResult(region, order, monRes);
|
|
monotonicityWatch.stop();
|
|
STORM_LOG_INFO(std::endl << "Total time for monotonicity checking: " << monotonicityWatch << "." << std::endl << std::endl);
|
|
|
|
regionQueue.emplace(region, order, monRes, initBound);
|
|
first = false;
|
|
} else {
|
|
regionQueue.emplace(region, nullptr, nullptr, initBound);
|
|
}
|
|
|
|
// The results
|
|
boost::optional<ConstantType> value;
|
|
Valuation valuation;
|
|
if (!initialValue) {
|
|
auto init = getGoodInitialPoint(env, region, dir, regionQueue.top().localMonRes);
|
|
value = storm::utility::convertNumber<ConstantType>(init.first);
|
|
valuation = std::move(init.second);
|
|
} else {
|
|
value = initialValue;
|
|
}
|
|
|
|
initialWatch.stop();
|
|
STORM_LOG_INFO(std::endl << "Total time for initial points: " << initialWatch << "." << std::endl << std::endl);
|
|
if (!initialValue) {
|
|
STORM_LOG_INFO("Initial value: " << value.get() << " at " << valuation);
|
|
} else {
|
|
STORM_LOG_INFO("Initial value: " << value.get() << " as provided by the user");
|
|
}
|
|
|
|
auto numberOfSplits = 0;
|
|
auto numberOfPLACalls = 0;
|
|
auto numberOfOrderCopies = 0;
|
|
auto numberOfMonResCopies = 0;
|
|
storm::utility::Stopwatch loopWatch(true);
|
|
if (!(useMonotonicity && regionQueue.top().localMonRes->getGlobalMonotonicityResult()->isDone() && regionQueue.top().localMonRes->getGlobalMonotonicityResult()->isAllMonotonicity())) {
|
|
// Doing the extremal computation, only when we don't use monotonicity or there are possibly not monotone variables.
|
|
auto totalArea = storm::utility::convertNumber<ConstantType>(region.area());
|
|
auto coveredArea = storm::utility::zero<ConstantType>();
|
|
while (!regionQueue.empty()) {
|
|
assert (value);
|
|
auto currRegion = regionQueue.top().region;
|
|
auto order = regionQueue.top().order;
|
|
auto localMonotonicityResult = regionQueue.top().localMonRes;
|
|
auto currBound = regionQueue.top().bound;
|
|
STORM_LOG_INFO("Currently looking at region: " << currRegion);
|
|
std::vector<storm::storage::ParameterRegion<typename SparseModelType::ValueType>> newRegions;
|
|
|
|
// Check whether this region needs further investigation based on the bound of the parent region
|
|
bool investigateBounds = first || (minimize && currBound < value.get() - storm::utility::convertNumber<ConstantType>(precision))
|
|
|| (!minimize && currBound > value.get() + storm::utility::convertNumber<ConstantType>(precision));
|
|
first = false;
|
|
if (investigateBounds) {
|
|
numberOfPLACalls++;
|
|
auto bounds = getBound(env, currRegion, dir, localMonotonicityResult)->template asExplicitQuantitativeCheckResult<ConstantType>().getValueVector();
|
|
currBound = bounds[*this->parametricModel->getInitialStates().begin()];
|
|
// Check whether this region needs further investigation based on the bound of this region
|
|
bool lookAtRegion = (minimize && currBound < value.get() - storm::utility::convertNumber<ConstantType>(precision))
|
|
|| (!minimize && currBound > value.get() + storm::utility::convertNumber<ConstantType>(precision));
|
|
if (lookAtRegion) {
|
|
if (useMonotonicity) {
|
|
// Continue extending order/monotonicity result
|
|
bool changedOrder = false;
|
|
if (!order->getDoneBuilding() && orderExtender->isHope(order, currRegion)) {
|
|
if (numberOfCopiesOrder[order] != 1) {
|
|
numberOfCopiesOrder[order]--;
|
|
order = copyOrder(order);
|
|
numberOfOrderCopies++;
|
|
} else {
|
|
assert (numberOfCopiesOrder[order] == 1);
|
|
}
|
|
this->extendOrder(env, order, currRegion);
|
|
changedOrder = true;
|
|
}
|
|
if (changedOrder) {
|
|
assert(!localMonotonicityResult->isDone());
|
|
|
|
if (numberOfCopiesMonRes[localMonotonicityResult] != 1) {
|
|
numberOfCopiesMonRes[localMonotonicityResult]--;
|
|
localMonotonicityResult = localMonotonicityResult->copy();
|
|
numberOfMonResCopies++;
|
|
} else {
|
|
assert (numberOfCopiesMonRes[localMonotonicityResult] == 1);
|
|
}
|
|
this->extendLocalMonotonicityResult(currRegion, order, localMonotonicityResult);
|
|
STORM_LOG_INFO("Order and monotonicity result got extended");
|
|
}
|
|
}
|
|
|
|
// Check whether this region contains a new 'good' value and set this value
|
|
auto point = useMonotonicity ? currRegion.getPoint(dir, *(localMonotonicityResult->getGlobalMonotonicityResult())) : currRegion.getCenterPoint();
|
|
auto currValue = getInstantiationChecker().check(env, point)->template asExplicitQuantitativeCheckResult<ConstantType>()[*this->parametricModel->getInitialStates().begin()];
|
|
if (!value || (minimize ? currValue <= value.get() : currValue >= value.get())) {
|
|
value = currValue;
|
|
valuation = point;
|
|
}
|
|
|
|
if ((minimize && currBound < value.get() - storm::utility::convertNumber<ConstantType>(precision))
|
|
|| (!minimize && currBound > value.get() + storm::utility::convertNumber<ConstantType>(precision))) {
|
|
|
|
// We will split the region in this case, but first we set the bounds to extend the order for the new regions.
|
|
if (useMonotonicity && this->isUseBoundsSet() && !order->getDoneBuilding()) {
|
|
boundsWatch.start();
|
|
numberOfPLACallsBounds++;
|
|
if (minimize) {
|
|
orderExtender->setMinMaxValues(order, bounds, getBound(env, currRegion, storm::solver::OptimizationDirection::Maximize, localMonotonicityResult)->template asExplicitQuantitativeCheckResult<ConstantType>().getValueVector());
|
|
} else {
|
|
orderExtender->setMinMaxValues(order, getBound(env, currRegion, storm::solver::OptimizationDirection::Maximize, localMonotonicityResult)->template asExplicitQuantitativeCheckResult<ConstantType>().getValueVector(), bounds);
|
|
}
|
|
boundsWatch.stop();
|
|
}
|
|
// Now split the region
|
|
if (useMonotonicity) {
|
|
this->splitSmart(currRegion, newRegions, order, *(localMonotonicityResult->getGlobalMonotonicityResult()), true);
|
|
} else if (this->isRegionSplitEstimateSupported()) {
|
|
auto empty = storm::analysis::MonotonicityResult<VariableType>();
|
|
this->splitSmart(currRegion, newRegions, order, empty, true);
|
|
} else {
|
|
currRegion.split(currRegion.getCenterPoint(), newRegions);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (newRegions.empty()) {
|
|
// When the newRegions is empty we are done with the current region
|
|
coveredArea += storm::utility::convertNumber<ConstantType>(currRegion.area());
|
|
if (order != nullptr) {
|
|
numberOfCopiesOrder[order]--;
|
|
numberOfCopiesMonRes[localMonotonicityResult]--;
|
|
}
|
|
regionQueue.pop();
|
|
} else {
|
|
regionQueue.pop();
|
|
STORM_LOG_INFO("Splitting region " << currRegion << " into " << newRegions.size());
|
|
numberOfSplits++;
|
|
// Add the new regions to the queue
|
|
if (useMonotonicity) {
|
|
for (auto &r : newRegions) {
|
|
r.setBoundParent(storm::utility::convertNumber<CoefficientType>(currBound));
|
|
regionQueue.emplace(r, order, localMonotonicityResult, currBound);
|
|
}
|
|
if (numberOfCopiesOrder.find(order) != numberOfCopiesOrder.end()) {
|
|
numberOfCopiesOrder[order] += newRegions.size();
|
|
numberOfCopiesMonRes[localMonotonicityResult] += newRegions.size();
|
|
} else {
|
|
numberOfCopiesOrder[order] = newRegions.size();
|
|
numberOfCopiesMonRes[localMonotonicityResult] = newRegions.size();
|
|
}
|
|
} else {
|
|
for (auto &r : newRegions) {
|
|
r.setBoundParent(storm::utility::convertNumber<CoefficientType>(currBound));
|
|
regionQueue.emplace(r, nullptr, nullptr, currBound);
|
|
}
|
|
}
|
|
}
|
|
|
|
STORM_LOG_INFO("Current value : " << value.get() << ", current bound: " << currBound << ".");
|
|
STORM_LOG_INFO("Covered " << (coveredArea * storm::utility::convertNumber<ConstantType>(100.0) / totalArea) << "% of the region." << std::endl);
|
|
}
|
|
loopWatch.stop();
|
|
}
|
|
|
|
STORM_LOG_INFO("Total number of splits: " << numberOfSplits << std::endl);
|
|
STORM_PRINT("Total number of plaCalls: " << numberOfPLACalls << std::endl);
|
|
if (useMonotonicity) {
|
|
STORM_PRINT("Total number of plaCalls for bounds for monotonicity checking: " << numberOfPLACallsBounds << std::endl);
|
|
STORM_PRINT("Total number of copies of the order: " << numberOfOrderCopies << std::endl);
|
|
STORM_PRINT("Total number of copies of the local monotonicity result: " << numberOfMonResCopies
|
|
<< std::endl);
|
|
}
|
|
STORM_LOG_INFO(std::endl << "Total time for region refinement: " << loopWatch << "." << std::endl << std::endl);
|
|
STORM_LOG_INFO(std::endl << "Total time for additional bounds: " << boundsWatch << "." << std::endl << std::endl);
|
|
|
|
return std::make_pair(storm::utility::convertNumber<typename SparseModelType::ValueType>(value.get()), valuation);
|
|
}
|
|
|
|
template <typename SparseModelType, typename ConstantType>
|
|
std::pair<typename SparseModelType::ValueType, typename storm::storage::ParameterRegion<typename SparseModelType::ValueType>::Valuation> SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::computeExtremalValue(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dir, typename SparseModelType::ValueType const& precision) {
|
|
return computeExtremalValue(env, region, dir, precision, boost::none);
|
|
}
|
|
|
|
template <typename SparseModelType, typename ConstantType>
|
|
bool SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::checkExtremalValue(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dir, typename SparseModelType::ValueType const& precision, typename SparseModelType::ValueType const& valueToCheck) {
|
|
auto res = computeExtremalValue(env, region, dir, precision, storm::utility::convertNumber<ConstantType>(valueToCheck)).first;
|
|
return storm::solver::minimize(dir) ? storm::utility::convertNumber<ConstantType>(res) >= storm::utility::convertNumber<ConstantType>(valueToCheck) : storm::utility::convertNumber<ConstantType>(res) <= storm::utility::convertNumber<ConstantType>(valueToCheck);
|
|
}
|
|
|
|
template <typename SparseModelType, typename ConstantType>
|
|
SparseModelType const& SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::getConsideredParametricModel() const {
|
|
return *parametricModel;
|
|
}
|
|
|
|
template <typename SparseModelType, typename ConstantType>
|
|
CheckTask<storm::logic::Formula, ConstantType> const& SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::getCurrentCheckTask() const {
|
|
return *currentCheckTask;
|
|
}
|
|
|
|
template <typename SparseModelType, typename ConstantType>
|
|
void SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::specifyBoundedUntilFormula(Environment const& env, CheckTask<logic::BoundedUntilFormula, ConstantType> const& checkTask) {
|
|
STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Parameter lifting is not supported for the given property.");
|
|
}
|
|
|
|
template <typename SparseModelType, typename ConstantType>
|
|
void SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::specifyUntilFormula(Environment const& env, CheckTask<logic::UntilFormula, ConstantType> const& checkTask) {
|
|
STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Parameter lifting is not supported for the given property.");
|
|
}
|
|
|
|
template <typename SparseModelType, typename ConstantType>
|
|
void SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::specifyReachabilityProbabilityFormula(Environment const& env, CheckTask<logic::EventuallyFormula, ConstantType> const& checkTask) {
|
|
// transform to until formula
|
|
auto untilFormula = std::make_shared<storm::logic::UntilFormula const>(storm::logic::Formula::getTrueFormula(), checkTask.getFormula().getSubformula().asSharedPointer());
|
|
specifyUntilFormula(env, currentCheckTask->substituteFormula(*untilFormula));
|
|
}
|
|
|
|
template <typename SparseModelType, typename ConstantType>
|
|
void SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::specifyReachabilityRewardFormula(Environment const& env, CheckTask<logic::EventuallyFormula, ConstantType> const& checkTask) {
|
|
STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Parameter lifting is not supported for the given property.");
|
|
}
|
|
|
|
template <typename SparseModelType, typename ConstantType>
|
|
void SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::specifyCumulativeRewardFormula(Environment const& env, CheckTask<logic::CumulativeRewardFormula, ConstantType> const& checkTask) {
|
|
STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Parameter lifting is not supported for the given property.");
|
|
}
|
|
|
|
template<typename SparseModelType, typename ConstantType>
|
|
std::shared_ptr<storm::analysis::Order> SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::copyOrder(std::shared_ptr<storm::analysis::Order> order) {
|
|
auto res = order->copy();
|
|
if (orderExtender) {
|
|
orderExtender->setUnknownStates(order, res);
|
|
orderExtender->copyMinMax(order, res);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
template<typename SparseModelType, typename ConstantType>
|
|
std::pair<typename SparseModelType::ValueType, typename storm::storage::ParameterRegion<typename SparseModelType::ValueType>::Valuation> SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::checkForPossibleMonotonicity(Environment const& env,
|
|
const storage::ParameterRegion<typename SparseModelType::ValueType> ®ion,
|
|
std::set<VariableType>& possibleMonotoneIncrParameters,
|
|
std::set<VariableType>& possibleMonotoneDecrParameters,
|
|
std::set<VariableType>& possibleNotMonotoneParameters,
|
|
std::set<VariableType>const& consideredVariables,
|
|
storm::solver::OptimizationDirection const& dir) {
|
|
bool minimize = storm::solver::minimize(dir);
|
|
typedef typename storm::storage::ParameterRegion<typename SparseModelType::ValueType>::Valuation Valuation;
|
|
typedef typename storm::storage::ParameterRegion<typename SparseModelType::ValueType>::CoefficientType CoefficientType;
|
|
ConstantType value = storm::solver::minimize(dir) ? 1 : 0;
|
|
Valuation valuation;
|
|
for (auto& var : consideredVariables) {
|
|
ConstantType previousCenter = -1;
|
|
bool monDecr = true;
|
|
bool monIncr = true;
|
|
auto valuationCenter = region.getCenterPoint();
|
|
|
|
valuationCenter[var] = region.getLowerBoundary(var);
|
|
// TODO: make cmdline argument or 1/precision
|
|
int numberOfSamples = 50;
|
|
auto stepSize = (region.getUpperBoundary(var) - region.getLowerBoundary(var)) / (numberOfSamples - 1);
|
|
|
|
while (valuationCenter[var] <= region.getUpperBoundary(var)) {
|
|
// Create valuation
|
|
ConstantType valueCenter = getInstantiationChecker().check(env, valuationCenter)->template asExplicitQuantitativeCheckResult<ConstantType>()[*this->parametricModel->getInitialStates().begin()];
|
|
if (storm::solver::minimize(dir) ? valueCenter <= value : valueCenter >= value) {
|
|
value = valueCenter;
|
|
valuation = valuationCenter;
|
|
}
|
|
// Calculate difference with result for previous valuation
|
|
ConstantType diffCenter = previousCenter - valueCenter;
|
|
assert (previousCenter == -1 || (diffCenter >= -1 && diffCenter <= 1));
|
|
if (previousCenter != -1) {
|
|
assert (previousCenter != -1 && previousCenter != -1);
|
|
monDecr &= diffCenter > 0 && diffCenter > 0 && diffCenter > 0; // then previous value is larger than the current value from the initial states
|
|
monIncr &= diffCenter < 0 && diffCenter < 0 && diffCenter < 0;
|
|
}
|
|
previousCenter = valueCenter;
|
|
if (!monDecr && ! monIncr) {
|
|
break;
|
|
}
|
|
valuationCenter[var] += stepSize;
|
|
}
|
|
if (monIncr) {
|
|
possibleMonotoneParameters.insert(var);
|
|
possibleMonotoneIncrParameters.insert(var);
|
|
} else if (monDecr) {
|
|
possibleMonotoneParameters.insert(var);
|
|
possibleMonotoneDecrParameters.insert(var);
|
|
} else {
|
|
possibleNotMonotoneParameters.insert(var);
|
|
}
|
|
}
|
|
return std::make_pair(storm::utility::convertNumber<typename SparseModelType::ValueType>(value), std::move(valuation));
|
|
|
|
}
|
|
|
|
template<typename SparseModelType, typename ConstantType>
|
|
std::pair<typename SparseModelType::ValueType, typename storm::storage::ParameterRegion<typename SparseModelType::ValueType>::Valuation> SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::getGoodInitialPoint(const Environment &env, const storage::ParameterRegion<typename SparseModelType::ValueType> ®ion, const OptimizationDirection &dir, std::shared_ptr<storm::analysis::LocalMonotonicityResult<VariableType>> localMonRes) {
|
|
typedef typename storm::storage::ParameterRegion<typename SparseModelType::ValueType>::Valuation Valuation;
|
|
typedef typename storm::storage::ParameterRegion<typename SparseModelType::ValueType>::CoefficientType CoefficientType;
|
|
ConstantType value = storm::solver::minimize(dir) ? 1 : 0;
|
|
Valuation valuation;
|
|
std::set<VariableType> monIncr, monDecr, notMon, notMonFirst;
|
|
STORM_LOG_INFO("Number of parameters: " << region.getVariables().size() << std::endl;);
|
|
|
|
if (localMonRes != nullptr) {
|
|
localMonRes->getGlobalMonotonicityResult()->splitBasedOnMonotonicity(region.getVariables(), monIncr, monDecr, notMonFirst);
|
|
|
|
auto numMon = monIncr.size() + monDecr.size();
|
|
STORM_LOG_INFO("Number of monotone parameters: " << numMon << std::endl;);
|
|
|
|
if (numMon < region.getVariables().size()) {
|
|
checkForPossibleMonotonicity(env, region, monIncr, monDecr, notMon, notMonFirst, dir);
|
|
STORM_LOG_INFO("Number of possible monotone parameters: " << (monIncr.size() + monDecr.size() - numMon) << std::endl;);
|
|
STORM_LOG_INFO("Number of definitely not monotone parameters: " << notMon.size() << std::endl;);
|
|
}
|
|
|
|
valuation = region.getPoint(dir, monIncr, monDecr);
|
|
} else {
|
|
valuation = region.getCenterPoint();
|
|
}
|
|
value = getInstantiationChecker().check(env, valuation)->template asExplicitQuantitativeCheckResult<ConstantType>()[*this->parametricModel->getInitialStates().begin()];
|
|
|
|
return std::make_pair(storm::utility::convertNumber<typename SparseModelType::ValueType>(value), std::move(valuation));
|
|
}
|
|
|
|
template class SparseParameterLiftingModelChecker<storm::models::sparse::Dtmc<storm::RationalFunction>, double>;
|
|
template class SparseParameterLiftingModelChecker<storm::models::sparse::Mdp<storm::RationalFunction>, double>;
|
|
template class SparseParameterLiftingModelChecker<storm::models::sparse::Dtmc<storm::RationalFunction>, storm::RationalNumber>;
|
|
template class SparseParameterLiftingModelChecker<storm::models::sparse::Mdp<storm::RationalFunction>, storm::RationalNumber>;
|
|
}
|
|
}
|