You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
244 lines
17 KiB
244 lines
17 KiB
#include "storm/modelchecker/parametric/ParameterLifting.h"
|
|
|
|
#include "storm/adapters/CarlAdapter.h"
|
|
|
|
#include "storm/modelchecker/parametric/SparseDtmcParameterLiftingModelChecker.h"
|
|
#include "storm/modelchecker/parametric/SparseDtmcInstantiationModelChecker.h"
|
|
#include "storm/modelchecker/parametric/SparseMdpParameterLiftingModelChecker.h"
|
|
#include "storm/modelchecker/parametric/SparseMdpInstantiationModelChecker.h"
|
|
#include "storm/transformer/SparseParametricDtmcSimplifier.h"
|
|
#include "storm/transformer/SparseParametricMdpSimplifier.h"
|
|
#include "storm/modelchecker/results/ExplicitQualitativeCheckResult.h"
|
|
|
|
#include "storm/utility/vector.h"
|
|
#include "storm/models/sparse/StandardRewardModel.h"
|
|
#include "storm/models/sparse/Dtmc.h"
|
|
#include "storm/models/sparse/Mdp.h"
|
|
|
|
#include "storm/exceptions/NotSupportedException.h"
|
|
#include "storm/exceptions/InvalidStateException.h"
|
|
#include "storm/exceptions/InvalidArgumentException.h"
|
|
|
|
#include "storm/utility/Stopwatch.h"
|
|
#include "storm/settings/SettingsManager.h"
|
|
#include "storm/settings/modules/IOSettings.h"
|
|
#include "storm/settings/modules/DebugSettings.h"
|
|
|
|
|
|
namespace storm {
|
|
namespace modelchecker {
|
|
namespace parametric {
|
|
|
|
|
|
template <typename SparseModelType, typename ConstantType>
|
|
ParameterLifting<SparseModelType, ConstantType>::ParameterLifting(SparseModelType const& parametricModel) : parametricModel(parametricModel){
|
|
STORM_LOG_THROW(parametricModel.getInitialStates().getNumberOfSetBits() == 1, storm::exceptions::NotSupportedException, "Parameter lifting requires models with only one initial state");
|
|
}
|
|
|
|
template <typename SparseModelType, typename ConstantType>
|
|
void ParameterLifting<SparseModelType, ConstantType>::specifyFormula(CheckTask<storm::logic::Formula, typename SparseModelType::ValueType> const& checkTask) {
|
|
STORM_LOG_THROW(checkTask.isOnlyInitialStatesRelevantSet(), storm::exceptions::NotSupportedException, "Parameter lifting requires a property where only the value in the initial states is relevant.");
|
|
STORM_LOG_THROW(checkTask.isBoundSet(), storm::exceptions::NotSupportedException, "Parameter lifting requires a bounded property.");
|
|
|
|
storm::utility::Stopwatch sw(true);
|
|
simplifyParametricModel(checkTask);
|
|
sw.stop();
|
|
std::cout << "SIStats: " << sw << std::endl;
|
|
initializeUnderlyingCheckers();
|
|
currentCheckTask = std::make_unique<storm::modelchecker::CheckTask<storm::logic::Formula, typename SparseModelType::ValueType>>(checkTask.substituteFormula(*currentFormula));
|
|
|
|
STORM_LOG_THROW(parameterLiftingChecker->canHandle(*currentCheckTask), storm::exceptions::NotSupportedException, "Parameter lifting is not supported for this property.");
|
|
instantiationChecker->specifyFormula(*currentCheckTask);
|
|
parameterLiftingChecker->specifyFormula(*currentCheckTask);
|
|
}
|
|
|
|
template <typename SparseModelType, typename ConstantType>
|
|
RegionCheckResult ParameterLifting<SparseModelType, ConstantType>::analyzeRegion(storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, RegionCheckResult const& initialResult, bool sampleVerticesOfRegion) const {
|
|
RegionCheckResult result = initialResult;
|
|
|
|
// Check if we need to check the formula on one point to decide whether to show AllSat or AllViolated
|
|
if (result == RegionCheckResult::Unknown) {
|
|
result = instantiationChecker->check(region.getCenterPoint())->asExplicitQualitativeCheckResult()[*getConsideredParametricModel().getInitialStates().begin()] ? RegionCheckResult::CenterSat : RegionCheckResult::CenterViolated;
|
|
}
|
|
|
|
// try to prove AllSat or AllViolated, depending on the obtained result
|
|
if(result == RegionCheckResult::ExistsSat || result == RegionCheckResult::CenterSat) {
|
|
// show AllSat:
|
|
if(parameterLiftingChecker->check(region, this->currentCheckTask->getOptimizationDirection())->asExplicitQualitativeCheckResult()[*getConsideredParametricModel().getInitialStates().begin()]) {
|
|
result = RegionCheckResult::AllSat;
|
|
} else if (sampleVerticesOfRegion) {
|
|
// Check if there is a point in the region for which the property is violated
|
|
auto vertices = region.getVerticesOfRegion(region.getVariables());
|
|
for (auto const& v : vertices) {
|
|
if (!instantiationChecker->check(v)->asExplicitQualitativeCheckResult()[*getConsideredParametricModel().getInitialStates().begin()]) {
|
|
result = RegionCheckResult::ExistsBoth;
|
|
}
|
|
}
|
|
}
|
|
} else if (result == RegionCheckResult::ExistsViolated || result == RegionCheckResult::CenterViolated) {
|
|
// show AllViolated:
|
|
if(!parameterLiftingChecker->check(region, storm::solver::invert(this->currentCheckTask->getOptimizationDirection()))->asExplicitQualitativeCheckResult()[*getConsideredParametricModel().getInitialStates().begin()]) {
|
|
result = RegionCheckResult::AllViolated;
|
|
} else if (sampleVerticesOfRegion) {
|
|
// Check if there is a point in the region for which the property is satisfied
|
|
auto vertices = region.getVerticesOfRegion(region.getVariables());
|
|
for (auto const& v : vertices) {
|
|
if (instantiationChecker->check(v)->asExplicitQualitativeCheckResult()[*getConsideredParametricModel().getInitialStates().begin()]) {
|
|
result = RegionCheckResult::ExistsBoth;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "When analyzing a region, an invalid initial result was given: " << initialResult);
|
|
}
|
|
|
|
// TODO remove this:
|
|
if(storm::settings::getModule<storm::settings::modules::DebugSettings>().isDebugSet() && storm::settings::getModule<storm::settings::modules::IOSettings>().isPrismInputSet() && storm::settings::getModule<storm::settings::modules::IOSettings>().isPropertySet()) {
|
|
std::vector<storm::utility::parametric::Valuation<typename SparseModelType::ValueType>> points;
|
|
if(result == RegionCheckResult::AllSat || result == RegionCheckResult::AllViolated) {
|
|
points = region.getVerticesOfRegion(region.getVariables());
|
|
points.push_back(region.getCenterPoint());
|
|
} else if (result == RegionCheckResult::CenterSat || result == RegionCheckResult::CenterViolated) {
|
|
points.push_back(region.getCenterPoint());
|
|
}
|
|
if(!points.empty()) std::cout << "VALIDATE_REGION:" << region.toString(true) << std::endl;
|
|
for (auto const& p : points) {
|
|
if((result == RegionCheckResult::AllSat) || (result == RegionCheckResult::CenterSat)) {
|
|
std::cout << "EXPECTTRUE:";
|
|
} else {
|
|
std::cout << "EXPECTFALSE:";
|
|
}
|
|
std::cout << "STORMEXECUTABLE --prism " << storm::settings::getModule<storm::settings::modules::IOSettings>().getPrismInputFilename()
|
|
<< " --prop " << storm::settings::getModule<storm::settings::modules::IOSettings>().getProperty();
|
|
if( storm::settings::getModule<storm::settings::modules::IOSettings>().isConstantsSet()) {
|
|
std::cout << " -const " << storm::settings::getModule<storm::settings::modules::IOSettings>().getConstantDefinitionString() << ",";
|
|
}
|
|
for(auto varDef = p.begin(); varDef != p.end(); ++varDef) {
|
|
if(varDef!=p.begin()) std::cout << ",";
|
|
std::cout << varDef->first << "=" << storm::utility::convertNumber<double>(varDef->second);
|
|
}
|
|
std::cout << std::endl;
|
|
}
|
|
}
|
|
|
|
|
|
return result;
|
|
}
|
|
|
|
template <typename SparseModelType, typename ConstantType>
|
|
std::vector<std::pair<storm::storage::ParameterRegion<typename SparseModelType::ValueType>, RegionCheckResult>> ParameterLifting<SparseModelType, ConstantType>::performRegionRefinement(storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, typename storm::storage::ParameterRegion<typename SparseModelType::ValueType>::CoefficientType const& threshold) const {
|
|
STORM_LOG_INFO("Applying refinement on region: " << region.toString(true) << " .");
|
|
|
|
storm::utility::Stopwatch sw(true);
|
|
|
|
auto areaOfParameterSpace = region.area();
|
|
auto fractionOfUndiscoveredArea = storm::utility::one<typename storm::storage::ParameterRegion<typename SparseModelType::ValueType>::CoefficientType>();
|
|
auto fractionOfAllSatArea = storm::utility::zero<typename storm::storage::ParameterRegion<typename SparseModelType::ValueType>::CoefficientType>();
|
|
auto fractionOfAllViolatedArea = storm::utility::zero<typename storm::storage::ParameterRegion<typename SparseModelType::ValueType>::CoefficientType>();
|
|
|
|
std::vector<std::pair<storm::storage::ParameterRegion<typename SparseModelType::ValueType>, RegionCheckResult>> regions;
|
|
regions.emplace_back(region, RegionCheckResult::Unknown);
|
|
storm::storage::BitVector resultRegions(1, true);
|
|
uint_fast64_t indexOfCurrentRegion = 0;
|
|
|
|
while (fractionOfUndiscoveredArea > threshold) {
|
|
STORM_LOG_THROW(indexOfCurrentRegion < regions.size(), storm::exceptions::InvalidStateException, "Threshold for undiscovered area not reached but no unprocessed regions left.");
|
|
STORM_LOG_INFO("Analyzing region #" << regions.size() -1 << " (" << storm::utility::convertNumber<double>(fractionOfUndiscoveredArea) * 100 << "% still unknown)");
|
|
auto const& currentRegion = regions[indexOfCurrentRegion].first;
|
|
auto& res = regions[indexOfCurrentRegion].second;
|
|
res = analyzeRegion(currentRegion, res, false);
|
|
switch (res) {
|
|
case RegionCheckResult::AllSat:
|
|
fractionOfUndiscoveredArea -= currentRegion.area() / areaOfParameterSpace;
|
|
fractionOfAllSatArea += currentRegion.area() / areaOfParameterSpace;
|
|
break;
|
|
case RegionCheckResult::AllViolated:
|
|
fractionOfUndiscoveredArea -= currentRegion.area() / areaOfParameterSpace;
|
|
fractionOfAllViolatedArea += currentRegion.area() / areaOfParameterSpace;
|
|
break;
|
|
default:
|
|
std::vector<storm::storage::ParameterRegion<typename SparseModelType::ValueType>> newRegions;
|
|
currentRegion.split(currentRegion.getCenterPoint(), newRegions);
|
|
resultRegions.grow(regions.size() + newRegions.size(), true);
|
|
RegionCheckResult initResForNewRegions = (res == RegionCheckResult::CenterSat) ? RegionCheckResult::ExistsSat :
|
|
((res == RegionCheckResult::CenterViolated) ? RegionCheckResult::ExistsViolated :
|
|
RegionCheckResult::Unknown);
|
|
for(auto& newRegion : newRegions) {
|
|
regions.emplace_back(std::move(newRegion), initResForNewRegions);
|
|
}
|
|
resultRegions.set(indexOfCurrentRegion, false);
|
|
break;
|
|
}
|
|
++indexOfCurrentRegion;
|
|
}
|
|
resultRegions.resize(regions.size());
|
|
|
|
sw.stop();
|
|
std::cout << "REStats: " << sw << std::endl;
|
|
return storm::utility::vector::filterVector(regions, resultRegions);
|
|
}
|
|
|
|
template <typename SparseModelType, typename ConstantType>
|
|
SparseParameterLiftingModelChecker<SparseModelType, ConstantType> const& ParameterLifting<SparseModelType, ConstantType>::getParameterLiftingChecker() const {
|
|
return *parameterLiftingChecker;
|
|
}
|
|
|
|
template <typename SparseModelType, typename ConstantType>
|
|
SparseInstantiationModelChecker<SparseModelType, ConstantType> const& ParameterLifting<SparseModelType, ConstantType>::getInstantiationChecker() const {
|
|
return *instantiationChecker;
|
|
}
|
|
|
|
template <typename SparseModelType, typename ConstantType>
|
|
SparseModelType const& ParameterLifting<SparseModelType, ConstantType>::getConsideredParametricModel() const {
|
|
if (simplifiedModel) {
|
|
return *simplifiedModel;
|
|
} else {
|
|
return parametricModel;
|
|
}
|
|
}
|
|
|
|
template <>
|
|
void ParameterLifting<storm::models::sparse::Dtmc<storm::RationalFunction>, double>::initializeUnderlyingCheckers() {
|
|
parameterLiftingChecker = std::make_unique<SparseDtmcParameterLiftingModelChecker<storm::models::sparse::Dtmc<storm::RationalFunction>, double>>(getConsideredParametricModel());
|
|
instantiationChecker = std::make_unique<SparseDtmcInstantiationModelChecker<storm::models::sparse::Dtmc<storm::RationalFunction>, double>>(getConsideredParametricModel());
|
|
}
|
|
|
|
template <>
|
|
void ParameterLifting<storm::models::sparse::Mdp<storm::RationalFunction>, double>::initializeUnderlyingCheckers() {
|
|
parameterLiftingChecker = std::make_unique<SparseMdpParameterLiftingModelChecker<storm::models::sparse::Mdp<storm::RationalFunction>, double>>(getConsideredParametricModel());
|
|
instantiationChecker = std::make_unique<SparseMdpInstantiationModelChecker<storm::models::sparse::Mdp<storm::RationalFunction>, double>>(getConsideredParametricModel());
|
|
}
|
|
|
|
template <>
|
|
void ParameterLifting<storm::models::sparse::Dtmc<storm::RationalFunction>, double>::simplifyParametricModel(CheckTask<logic::Formula, storm::RationalFunction> const& checkTask) {
|
|
storm::transformer::SparseParametricDtmcSimplifier<storm::models::sparse::Dtmc<storm::RationalFunction>> simplifier(parametricModel);
|
|
if(simplifier.simplify(checkTask.getFormula())) {
|
|
simplifiedModel = simplifier.getSimplifiedModel();
|
|
currentFormula = simplifier.getSimplifiedFormula();
|
|
} else {
|
|
simplifiedModel = nullptr;
|
|
currentFormula = checkTask.getFormula().asSharedPointer();
|
|
}
|
|
}
|
|
|
|
template <>
|
|
void ParameterLifting<storm::models::sparse::Mdp<storm::RationalFunction>, double>::simplifyParametricModel(CheckTask<logic::Formula, storm::RationalFunction> const& checkTask) {
|
|
storm::transformer::SparseParametricMdpSimplifier<storm::models::sparse::Mdp<storm::RationalFunction>> simplifier(parametricModel);
|
|
if(simplifier.simplify(checkTask.getFormula())) {
|
|
simplifiedModel = simplifier.getSimplifiedModel();
|
|
currentFormula = simplifier.getSimplifiedFormula();
|
|
} else {
|
|
simplifiedModel = nullptr;
|
|
currentFormula = checkTask.getFormula().asSharedPointer();
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef STORM_HAVE_CARL
|
|
template class ParameterLifting<storm::models::sparse::Dtmc<storm::RationalFunction>, double>;
|
|
template class ParameterLifting<storm::models::sparse::Mdp<storm::RationalFunction>, double>;
|
|
#endif
|
|
} // namespace parametric
|
|
} //namespace modelchecker
|
|
} //namespace storm
|
|
|