diff --git a/src/storm-pars-cli/storm-pars.cpp b/src/storm-pars-cli/storm-pars.cpp index 8ffaa852e..6fb83ad26 100644 --- a/src/storm-pars-cli/storm-pars.cpp +++ b/src/storm-pars-cli/storm-pars.cpp @@ -179,25 +179,11 @@ namespace storm { } } - template - std::pair>, std::shared_ptr> performSimplification(std::shared_ptr> const& model, std::shared_ptr const& formula) { - auto parametricSettings = storm::settings::getModule(); - - std::pair>, std::shared_ptr> result; - - if (!(parametricSettings.isSimplifySet() && storm::api::simplifyParametricModel(model, formula, result.first, result.second))) { - result = std::make_pair(model, formula); - } - - return result; - } - template void verifyPropertiesWithSparseEngine(std::shared_ptr> const& model, SymbolicInput const& input) { verifyProperties(input.properties, [&model] (std::shared_ptr const& formula) { - auto simplificationResult = performSimplification(model, formula); - std::unique_ptr result = storm::api::verifyWithSparseEngine(simplificationResult.first, storm::api::createTask(simplificationResult.second, true)); + std::unique_ptr result = storm::api::verifyWithSparseEngine(model, storm::api::createTask(formula, true)); result->filter(storm::modelchecker::ExplicitQualitativeCheckResult(simplificationResult.first->getInitialStates())); return result; }, @@ -235,15 +221,13 @@ namespace storm { STORM_PRINT_AND_LOG(" with iterative refinement until " << (1.0 - regionSettings.getRefinementThreshold()) * 100.0 << "% is covered." << std::endl); verificationCallback = [&] (std::shared_ptr const& formula) { ValueType refinementThreshold = storm::utility::convertNumber(regionSettings.getRefinementThreshold()); - auto simplificationResult = performSimplification(model, formula); - std::unique_ptr> result = storm::api::checkAndRefineRegionWithSparseEngine(simplificationResult.first, storm::api::createTask(simplificationResult.second, true), regions.front(), refinementThreshold, engine); + std::unique_ptr> result = storm::api::checkAndRefineRegionWithSparseEngine(model, storm::api::createTask(formula, true), regions.front(), refinementThreshold, engine); return result; }; } else { STORM_PRINT_AND_LOG("." << std::endl); verificationCallback = [&] (std::shared_ptr const& formula) { - auto simplificationResult = performSimplification(model, formula); - std::unique_ptr result = storm::api::checkRegionsWithSparseEngine(simplificationResult.first, storm::api::createTask(simplificationResult.second, true), regions, engine); + std::unique_ptr result = storm::api::checkRegionsWithSparseEngine(model, storm::api::createTask(formula, true), regions, engine); return result; }; } diff --git a/src/storm-pars/api/parametric.h b/src/storm-pars/api/parametric.h deleted file mode 100644 index aec977003..000000000 --- a/src/storm-pars/api/parametric.h +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once - -#include - -#include "storm-pars/transformer/SparseParametricDtmcSimplifier.h" -#include "storm-pars/transformer/SparseParametricMdpSimplifier.h" - -#include "storm/models/sparse/Model.h" -#include "storm/logic/Formula.h" -#include "storm/utility/macros.h" - -namespace storm { - namespace api { - - template - bool simplifyParametricModel(std::shared_ptr> const& inputModel, std::shared_ptr const& inputFormula, std::shared_ptr>& outputModel, std::shared_ptr& outputFormula) { - - if (inputModel->isOfType(storm::models::ModelType::Dtmc)) { - auto const& dtmc = *inputModel->template as>(); - auto simplifier = storm::transformer::SparseParametricDtmcSimplifier>(dtmc); - if (simplifier.simplify(*inputFormula)) { - outputModel = simplifier.getSimplifiedModel(); - outputFormula = simplifier.getSimplifiedFormula(); - return true; - } - } else if (inputModel->isOfType(storm::models::ModelType::Mdp)) { - auto const& mdp = *inputModel->template as>(); - auto simplifier = storm::transformer::SparseParametricMdpSimplifier>(mdp); - if (simplifier.simplify(*inputFormula)) { - outputModel = simplifier.getSimplifiedModel(); - outputFormula = simplifier.getSimplifiedFormula(); - return true; - } - } else { - STORM_LOG_ERROR("Unable to simplify model with the given type."); - } - - // Reaching this point means that simplification was not successful. - outputModel = nullptr; - outputFormula = nullptr; - return false; - - } - } -} diff --git a/src/storm-pars/api/region.h b/src/storm-pars/api/region.h index 2a5ff9fcd..043f461b0 100644 --- a/src/storm-pars/api/region.h +++ b/src/storm-pars/api/region.h @@ -6,14 +6,16 @@ #include #include -#include "storm-pars/storage/ParameterRegion.h" - #include "storm-pars/modelchecker/results/RegionCheckResult.h" #include "storm-pars/modelchecker/results/RegionRefinementCheckResult.h" -#include "storm-pars/modelchecker/region/SparseDtmcParameterLiftingModelChecker.h" #include "storm-pars/modelchecker/region/RegionCheckEngine.h" +#include "storm-pars/modelchecker/region/SparseDtmcParameterLiftingModelChecker.h" #include "storm-pars/modelchecker/region/SparseMdpParameterLiftingModelChecker.h" +#include "storm-pars/modelchecker/region/ValidatingSparseMdpParameterLiftingModelChecker.h" +#include "storm-pars/modelchecker/region/ValidatingSparseDtmcParameterLiftingModelChecker.h" #include "storm-pars/parser/ParameterRegionParser.h" +#include "storm-pars/storage/ParameterRegion.h" +#include "storm-pars/utility/parameterlifting.h" #include "storm/api/transformation.h" #include "storm/utility/file.h" @@ -53,51 +55,86 @@ namespace storm { template storm::storage::ParameterRegion parseRegion(std::string const& inputString, std::set::VariableType> const& consideredVariables) { - auto res = parseRegions(inputString, consideredVariables); + // Handle the "empty region" case + if (inputString == "" && consideredVariables.empty()) { + return storm::storage::ParameterRegion(); + } + + auto res = parseRegions(inputString, consideredVariables); STORM_LOG_THROW(res.size() == 1, storm::exceptions::InvalidOperationException, "Parsed " << res.size() << " regions but exactly one was expected."); return res.front(); } template storm::storage::ParameterRegion parseRegion(std::string const& inputString, storm::models::ModelBase const& model) { - auto res = parseRegions(inputString, model); + // Handle the "empty region" case + if (inputString == "" && !model.hasParameters()) { + return storm::storage::ParameterRegion(); + } + + auto res = parseRegions(inputString, model); STORM_LOG_THROW(res.size() == 1, storm::exceptions::InvalidOperationException, "Parsed " << res.size() << " regions but exactly one was expected."); return res.front(); } - template std::unique_ptr> initializeParameterLiftingRegionModelChecker(std::shared_ptr> const& model, storm::modelchecker::CheckTask const& task) { + + STORM_LOG_WARN_COND(storm::utility::parameterlifting::validateParameterLiftingSound(*model, task.getFormula()), "Could not validate whether parameter lifting is applicable. Please validate manually..."); + + std::shared_ptr> consideredModel = model; + // Treat continuous time models - if (model->isOfType(storm::models::ModelType::Ctmc) || model->isOfType(storm::models::ModelType::MarkovAutomaton)) { + if (consideredModel->isOfType(storm::models::ModelType::Ctmc) || consideredModel->isOfType(storm::models::ModelType::MarkovAutomaton)) { STORM_LOG_WARN("Parameter lifting not supported for continuous time models. Transforming continuous model to discrete model..."); std::vector> taskFormulaAsVector { task.getFormula().asSharedPointer() }; - auto discreteTimeModel = storm::api::transformContinuousToDiscreteTimeSparseModel(model, taskFormulaAsVector); - STORM_LOG_THROW(discreteTimeModel->isOfType(storm::models::ModelType::Dtmc) || discreteTimeModel->isOfType(storm::models::ModelType::Mdp), storm::exceptions::UnexpectedException, "Transformation to discrete time model has failed."); - return initializeParameterLiftingRegionModelChecker(discreteTimeModel, task); + consideredModel = storm::api::transformContinuousToDiscreteTimeSparseModel(consideredModel, taskFormulaAsVector); + STORM_LOG_THROW(consideredModel->isOfType(storm::models::ModelType::Dtmc) || consideredModel->isOfType(storm::models::ModelType::Mdp), storm::exceptions::UnexpectedException, "Transformation to discrete time model has failed."); } // Obtain the region model checker std::unique_ptr> result; - if (model->isOfType(storm::models::ModelType::Dtmc)) { - auto const& dtmc = *model->template as>(); - result = std::make_unique, ConstantType>>(dtmc); - } else if (model->isOfType(storm::models::ModelType::Mdp)) { - auto const& mdp = *model->template as>(); - result = std::make_unique, ConstantType>>(mdp); + if (consideredModel->isOfType(storm::models::ModelType::Dtmc)) { + result = std::make_unique, ConstantType>>(); + } else if (consideredModel->isOfType(storm::models::ModelType::Mdp)) { + result = std::make_unique, ConstantType>>(); } else { STORM_LOG_THROW(false, storm::exceptions::InvalidOperationException, "Unable to perform parameterLifting on the provided model type."); } - - result->specifyFormula(task); - + + result->specify(consideredModel, task); + return result; } template std::unique_ptr> initializeValidatingRegionModelChecker(std::shared_ptr> const& model, storm::modelchecker::CheckTask const& task) { - // todo - return nullptr; + + STORM_LOG_WARN_COND(storm::utility::parameterlifting::validateParameterLiftingSound(*model, task.getFormula()), "Could not validate whether parameter lifting is applicable. Please validate manually..."); + + std::shared_ptr> consideredModel = model; + + // Treat continuous time models + if (consideredModel->isOfType(storm::models::ModelType::Ctmc) || consideredModel->isOfType(storm::models::ModelType::MarkovAutomaton)) { + STORM_LOG_WARN("Parameter lifting not supported for continuous time models. Transforming continuous model to discrete model..."); + std::vector> taskFormulaAsVector { task.getFormula().asSharedPointer() }; + consideredModel = storm::api::transformContinuousToDiscreteTimeSparseModel(consideredModel, taskFormulaAsVector); + STORM_LOG_THROW(consideredModel->isOfType(storm::models::ModelType::Dtmc) || consideredModel->isOfType(storm::models::ModelType::Mdp), storm::exceptions::UnexpectedException, "Transformation to discrete time model has failed."); + } + + // Obtain the region model checker + std::unique_ptr> result; + if (consideredModel->isOfType(storm::models::ModelType::Dtmc)) { + result = std::make_unique, ImpreciseType, PreciseType>>(); + } else if (consideredModel->isOfType(storm::models::ModelType::Mdp)) { + result = std::make_unique, ImpreciseType, PreciseType>>(); + } else { + STORM_LOG_THROW(false, storm::exceptions::InvalidOperationException, "Unable to perform parameterLifting on the provided model type."); + } + + result->specify(consideredModel, task); + + return result; } template diff --git a/src/storm-pars/api/storm-pars.h b/src/storm-pars/api/storm-pars.h index f7b15dda0..42ac7dc79 100644 --- a/src/storm-pars/api/storm-pars.h +++ b/src/storm-pars/api/storm-pars.h @@ -1,4 +1,3 @@ #pragma once #include "storm-pars/api/region.h" -#include "storm-pars/api/parametric.h" diff --git a/src/storm-pars/modelchecker/region/RegionModelChecker.cpp b/src/storm-pars/modelchecker/region/RegionModelChecker.cpp index d6a457899..a2a19451d 100644 --- a/src/storm-pars/modelchecker/region/RegionModelChecker.cpp +++ b/src/storm-pars/modelchecker/region/RegionModelChecker.cpp @@ -40,47 +40,6 @@ namespace storm { return std::make_unique>(std::move(result)); } - /* -#include "storm/modelchecker/results/ExplicitQualitativeCheckResult.h" - template - RegionResult RegionModelChecker::analyzeRegionExactValidation(storm::storage::ParameterRegion const& region, RegionResult const& initialResult) { - RegionResult numericResult = analyzeRegion(region, initialResult, false); - parameterLiftingCheckerStopwatch.start(); - if (numericResult == RegionResult::AllSat || numericResult == RegionResult::AllViolated) { - applyHintsToExactChecker(); - } - if (numericResult == RegionResult::AllSat) { - if(!exactParameterLiftingChecker->check(region, this->currentCheckTask->getOptimizationDirection())->asExplicitQualitativeCheckResult()[*getConsideredParametricModel().getInitialStates().begin()]) { - // Numerical result is wrong; Check whether the region is AllViolated! - STORM_LOG_INFO("Numerical result was wrong for one region... Applying exact methods to obtain the actual result..."); - ++numOfCorrectedRegions; - if(!exactParameterLiftingChecker->check(region, storm::solver::invert(this->currentCheckTask->getOptimizationDirection()))->asExplicitQualitativeCheckResult()[*getConsideredParametricModel().getInitialStates().begin()]) { - parameterLiftingCheckerStopwatch.stop(); - return RegionResult::AllViolated; - } else { - parameterLiftingCheckerStopwatch.stop(); - return RegionResult::Unknown; - } - } - } else if (numericResult == RegionResult::AllViolated) { - if(exactParameterLiftingChecker->check(region, storm::solver::invert(this->currentCheckTask->getOptimizationDirection()))->asExplicitQualitativeCheckResult()[*getConsideredParametricModel().getInitialStates().begin()]) { - // Numerical result is wrong; Check whether the region is AllSat! - STORM_LOG_INFO("Numerical result was wrong for one region... Applying exact methods to obtain the actual result..."); - ++numOfCorrectedRegions; - if(exactParameterLiftingChecker->check(region, this->currentCheckTask->getOptimizationDirection())->asExplicitQualitativeCheckResult()[*getConsideredParametricModel().getInitialStates().begin()]) { - parameterLiftingCheckerStopwatch.stop(); - return RegionResult::AllSat; - } else { - parameterLiftingCheckerStopwatch.stop(); - return RegionResult::Unknown; - } - } - } - parameterLiftingCheckerStopwatch.stop(); - return numericResult; - } -*/ - template std::unique_ptr> RegionModelChecker::performRegionRefinement(storm::storage::ParameterRegion const& region, ParametricType const& threshold) { STORM_LOG_INFO("Applying refinement on region: " << region.toString(true) << " ."); diff --git a/src/storm-pars/modelchecker/region/RegionModelChecker.h b/src/storm-pars/modelchecker/region/RegionModelChecker.h index 746c9b2d3..a8f1fdf01 100644 --- a/src/storm-pars/modelchecker/region/RegionModelChecker.h +++ b/src/storm-pars/modelchecker/region/RegionModelChecker.h @@ -7,6 +7,7 @@ #include "storm-pars/modelchecker/region/RegionResult.h" #include "storm-pars/storage/ParameterRegion.h" +#include "storm/models/ModelBase.h" #include "storm/modelchecker/CheckTask.h" namespace storm { @@ -21,9 +22,8 @@ namespace storm { RegionModelChecker(); virtual ~RegionModelChecker() = default; - virtual bool canHandle(CheckTask const& checkTask) const = 0; - - virtual void specifyFormula(CheckTask const& checkTask) = 0; + virtual bool canHandle(std::shared_ptr parametricModel, CheckTask const& checkTask) const = 0; + virtual void specify(std::shared_ptr parametricModel, CheckTask const& checkTask) = 0; /*! * Analyzes the given region. diff --git a/src/storm-pars/modelchecker/region/SparseDtmcParameterLiftingModelChecker.cpp b/src/storm-pars/modelchecker/region/SparseDtmcParameterLiftingModelChecker.cpp index bdfd17f84..ff8f29577 100644 --- a/src/storm-pars/modelchecker/region/SparseDtmcParameterLiftingModelChecker.cpp +++ b/src/storm-pars/modelchecker/region/SparseDtmcParameterLiftingModelChecker.cpp @@ -1,5 +1,6 @@ #include "storm-pars/modelchecker/region/SparseDtmcParameterLiftingModelChecker.h" -#include "storm-pars/utility/parameterlifting.h" + +#include "storm-pars/transformer/SparseParametricDtmcSimplifier.h" #include "storm/adapters/RationalFunctionAdapter.h" #include "storm/modelchecker/propositional/SparsePropositionalModelChecker.h" @@ -15,28 +16,59 @@ #include "storm/exceptions/InvalidArgumentException.h" #include "storm/exceptions/InvalidPropertyException.h" #include "storm/exceptions/NotSupportedException.h" +#include "storm/exceptions/UnexpectedException.h" namespace storm { namespace modelchecker { template - SparseDtmcParameterLiftingModelChecker::SparseDtmcParameterLiftingModelChecker(SparseModelType const& parametricModel) : SparseParameterLiftingModelChecker(parametricModel), solverFactory(std::make_unique>()) { + SparseDtmcParameterLiftingModelChecker::SparseDtmcParameterLiftingModelChecker() : SparseDtmcParameterLiftingModelChecker(std::make_unique>()) { + // Intentionally left empty } template - SparseDtmcParameterLiftingModelChecker::SparseDtmcParameterLiftingModelChecker(SparseModelType const& parametricModel, std::unique_ptr>&& solverFactory) : SparseParameterLiftingModelChecker(parametricModel), solverFactory(std::move(solverFactory)) { + SparseDtmcParameterLiftingModelChecker::SparseDtmcParameterLiftingModelChecker(std::unique_ptr>&& solverFactory) : solverFactory(std::move(solverFactory)) { + // Intentionally left empty } template - bool SparseDtmcParameterLiftingModelChecker::canHandle(CheckTask const& checkTask) const { - bool result = checkTask.getFormula().isInFragment(storm::logic::reachability().setRewardOperatorsAllowed(true).setReachabilityRewardFormulasAllowed(true).setBoundedUntilFormulasAllowed(true).setCumulativeRewardFormulasAllowed(true).setStepBoundedUntilFormulasAllowed(true)); - if (result) { - storm::utility::parameterlifting::validateParameterLiftingSound(this->parametricModel, checkTask.getFormula()); - STORM_LOG_WARN_COND(storm::utility::parameterlifting::validateParameterLiftingSound(this->parametricModel, checkTask.getFormula()), "Could not validate whether parameter lifting is applicable. Please validate manually..."); - } + bool SparseDtmcParameterLiftingModelChecker::canHandle(std::shared_ptr parametricModel, CheckTask const& checkTask) const { + bool result = parametricModel->isOfType(storm::models::ModelType::Dtmc); + result &= parametricModel->isSparseModel(); + result &= parametricModel->supportsParameters(); + auto dtmc = parametricModel->template as(); + result &= static_cast(dtmc); + result &= checkTask.getFormula().isInFragment(storm::logic::reachability().setRewardOperatorsAllowed(true).setReachabilityRewardFormulasAllowed(true).setBoundedUntilFormulasAllowed(true).setCumulativeRewardFormulasAllowed(true).setStepBoundedUntilFormulasAllowed(true).setTimeBoundedUntilFormulasAllowed(true)); return result; } + template + void SparseDtmcParameterLiftingModelChecker::specify(std::shared_ptr parametricModel, CheckTask const& checkTask) { + auto dtmc = parametricModel->template as(); + specify(dtmc, checkTask, false); + } + + template + void SparseDtmcParameterLiftingModelChecker::specify(std::shared_ptr parametricModel, CheckTask const& checkTask, bool skipModelSimplification) { + + STORM_LOG_ASSERT(this->canHandle(parametricModel, checkTask), "specified model and formula can not be handled by this."); + + reset(); + + if (skipModelSimplification) { + this->parametricModel = parametricModel; + this->specifyFormula(checkTask); + } else { + auto simplifier = storm::transformer::SparseParametricDtmcSimplifier(*parametricModel); + if (!simplifier.simplify(checkTask.getFormula())) { + STORM_LOG_THROW(false, storm::exceptions::UnexpectedException, "Simplifying the model was not successfull."); + } + this->parametricModel = simplifier.getSimplifiedModel(); + this->specifyFormula(checkTask.substituteFormula(*simplifier.getSimplifiedFormula())); + } + } + + template void SparseDtmcParameterLiftingModelChecker::specifyBoundedUntilFormula(CheckTask const& checkTask) { @@ -53,25 +85,25 @@ namespace storm { STORM_LOG_THROW(*stepBound > 0, storm::exceptions::NotSupportedException, "Can not apply parameter lifting on step bounded formula: The step bound has to be positive."); // get the results for the subformulas - storm::modelchecker::SparsePropositionalModelChecker propositionalChecker(this->parametricModel); + storm::modelchecker::SparsePropositionalModelChecker propositionalChecker(*this->parametricModel); STORM_LOG_THROW(propositionalChecker.canHandle(checkTask.getFormula().getLeftSubformula()) && propositionalChecker.canHandle(checkTask.getFormula().getRightSubformula()), storm::exceptions::NotSupportedException, "Parameter lifting with non-propositional subformulas is not supported"); storm::storage::BitVector phiStates = std::move(propositionalChecker.check(checkTask.getFormula().getLeftSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector()); storm::storage::BitVector psiStates = std::move(propositionalChecker.check(checkTask.getFormula().getRightSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector()); // get the maybeStates - maybeStates = storm::utility::graph::performProbGreater0(this->parametricModel.getBackwardTransitions(), phiStates, psiStates, true, *stepBound); + maybeStates = storm::utility::graph::performProbGreater0(this->parametricModel->getBackwardTransitions(), phiStates, psiStates, true, *stepBound); maybeStates &= ~psiStates; // set the result for all non-maybe states - resultsForNonMaybeStates = std::vector(this->parametricModel.getNumberOfStates(), storm::utility::zero()); + resultsForNonMaybeStates = std::vector(this->parametricModel->getNumberOfStates(), storm::utility::zero()); storm::utility::vector::setVectorValues(resultsForNonMaybeStates, psiStates, storm::utility::one()); // if there are maybestates, create the parameterLifter if (!maybeStates.empty()) { // Create the vector of one-step probabilities to go to target states. - std::vector b = this->parametricModel.getTransitionMatrix().getConstrainedRowSumVector(storm::storage::BitVector(this->parametricModel.getTransitionMatrix().getRowCount(), true), psiStates); + std::vector b = this->parametricModel->getTransitionMatrix().getConstrainedRowSumVector(storm::storage::BitVector(this->parametricModel->getTransitionMatrix().getRowCount(), true), psiStates); - parameterLifter = std::make_unique>(this->parametricModel.getTransitionMatrix(), b, maybeStates, maybeStates); + parameterLifter = std::make_unique>(this->parametricModel->getTransitionMatrix(), b, maybeStates, maybeStates); } // We know some bounds for the results so set them @@ -83,25 +115,25 @@ namespace storm { void SparseDtmcParameterLiftingModelChecker::specifyUntilFormula(CheckTask const& checkTask) { // get the results for the subformulas - storm::modelchecker::SparsePropositionalModelChecker propositionalChecker(this->parametricModel); + storm::modelchecker::SparsePropositionalModelChecker propositionalChecker(*this->parametricModel); STORM_LOG_THROW(propositionalChecker.canHandle(checkTask.getFormula().getLeftSubformula()) && propositionalChecker.canHandle(checkTask.getFormula().getRightSubformula()), storm::exceptions::NotSupportedException, "Parameter lifting with non-propositional subformulas is not supported"); storm::storage::BitVector phiStates = std::move(propositionalChecker.check(checkTask.getFormula().getLeftSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector()); storm::storage::BitVector psiStates = std::move(propositionalChecker.check(checkTask.getFormula().getRightSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector()); // get the maybeStates - std::pair statesWithProbability01 = storm::utility::graph::performProb01(this->parametricModel.getBackwardTransitions(), phiStates, psiStates); + std::pair statesWithProbability01 = storm::utility::graph::performProb01(this->parametricModel->getBackwardTransitions(), phiStates, psiStates); maybeStates = ~(statesWithProbability01.first | statesWithProbability01.second); // set the result for all non-maybe states - resultsForNonMaybeStates = std::vector(this->parametricModel.getNumberOfStates(), storm::utility::zero()); + resultsForNonMaybeStates = std::vector(this->parametricModel->getNumberOfStates(), storm::utility::zero()); storm::utility::vector::setVectorValues(resultsForNonMaybeStates, statesWithProbability01.second, storm::utility::one()); // if there are maybestates, create the parameterLifter if (!maybeStates.empty()) { // Create the vector of one-step probabilities to go to target states. - std::vector b = this->parametricModel.getTransitionMatrix().getConstrainedRowSumVector(storm::storage::BitVector(this->parametricModel.getTransitionMatrix().getRowCount(), true), psiStates); + std::vector b = this->parametricModel->getTransitionMatrix().getConstrainedRowSumVector(storm::storage::BitVector(this->parametricModel->getTransitionMatrix().getRowCount(), true), psiStates); - parameterLifter = std::make_unique>(this->parametricModel.getTransitionMatrix(), b, maybeStates, maybeStates); + parameterLifter = std::make_unique>(this->parametricModel->getTransitionMatrix(), b, maybeStates, maybeStates); } // We know some bounds for the results so set them @@ -113,29 +145,29 @@ namespace storm { void SparseDtmcParameterLiftingModelChecker::specifyReachabilityRewardFormula(CheckTask const& checkTask) { // get the results for the subformula - storm::modelchecker::SparsePropositionalModelChecker propositionalChecker(this->parametricModel); + storm::modelchecker::SparsePropositionalModelChecker propositionalChecker(*this->parametricModel); STORM_LOG_THROW(propositionalChecker.canHandle(checkTask.getFormula().getSubformula()), storm::exceptions::NotSupportedException, "Parameter lifting with non-propositional subformulas is not supported"); storm::storage::BitVector targetStates = std::move(propositionalChecker.check(checkTask.getFormula().getSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector()); // get the maybeStates - storm::storage::BitVector infinityStates = storm::utility::graph::performProb1(this->parametricModel.getBackwardTransitions(), storm::storage::BitVector(this->parametricModel.getNumberOfStates(), true), targetStates); + storm::storage::BitVector infinityStates = storm::utility::graph::performProb1(this->parametricModel->getBackwardTransitions(), storm::storage::BitVector(this->parametricModel->getNumberOfStates(), true), targetStates); infinityStates.complement(); maybeStates = ~(targetStates | infinityStates); // set the result for all the non-maybe states - resultsForNonMaybeStates = std::vector(this->parametricModel.getNumberOfStates(), storm::utility::zero()); + resultsForNonMaybeStates = std::vector(this->parametricModel->getNumberOfStates(), storm::utility::zero()); storm::utility::vector::setVectorValues(resultsForNonMaybeStates, infinityStates, storm::utility::infinity()); // if there are maybestates, create the parameterLifter if (!maybeStates.empty()) { // Create the reward vector - STORM_LOG_THROW((checkTask.isRewardModelSet() && this->parametricModel.hasRewardModel(checkTask.getRewardModel())) || (!checkTask.isRewardModelSet() && this->parametricModel.hasUniqueRewardModel()), storm::exceptions::InvalidPropertyException, "The reward model specified by the CheckTask is not available in the given model."); + STORM_LOG_THROW((checkTask.isRewardModelSet() && this->parametricModel->hasRewardModel(checkTask.getRewardModel())) || (!checkTask.isRewardModelSet() && this->parametricModel->hasUniqueRewardModel()), storm::exceptions::InvalidPropertyException, "The reward model specified by the CheckTask is not available in the given model."); - typename SparseModelType::RewardModelType const& rewardModel = checkTask.isRewardModelSet() ? this->parametricModel.getRewardModel(checkTask.getRewardModel()) : this->parametricModel.getUniqueRewardModel(); + typename SparseModelType::RewardModelType const& rewardModel = checkTask.isRewardModelSet() ? this->parametricModel->getRewardModel(checkTask.getRewardModel()) : this->parametricModel->getUniqueRewardModel(); - std::vector b = rewardModel.getTotalRewardVector(this->parametricModel.getTransitionMatrix()); + std::vector b = rewardModel.getTotalRewardVector(this->parametricModel->getTransitionMatrix()); - parameterLifter = std::make_unique>(this->parametricModel.getTransitionMatrix(), b, maybeStates, maybeStates); + parameterLifter = std::make_unique>(this->parametricModel->getTransitionMatrix(), b, maybeStates, maybeStates); } // We only know a lower bound for the result @@ -155,15 +187,15 @@ namespace storm { STORM_LOG_THROW(*stepBound > 0, storm::exceptions::NotSupportedException, "Can not apply parameter lifting on step bounded formula: The step bound has to be positive."); //Every state is a maybeState - maybeStates = storm::storage::BitVector(this->parametricModel.getTransitionMatrix().getColumnCount(), true); - resultsForNonMaybeStates = std::vector(this->parametricModel.getNumberOfStates()); + maybeStates = storm::storage::BitVector(this->parametricModel->getTransitionMatrix().getColumnCount(), true); + resultsForNonMaybeStates = std::vector(this->parametricModel->getNumberOfStates()); // Create the reward vector - STORM_LOG_THROW((checkTask.isRewardModelSet() && this->parametricModel.hasRewardModel(checkTask.getRewardModel())) || (!checkTask.isRewardModelSet() && this->parametricModel.hasUniqueRewardModel()), storm::exceptions::InvalidPropertyException, "The reward model specified by the CheckTask is not available in the given model."); - typename SparseModelType::RewardModelType const& rewardModel = checkTask.isRewardModelSet() ? this->parametricModel.getRewardModel(checkTask.getRewardModel()) : this->parametricModel.getUniqueRewardModel(); - std::vector b = rewardModel.getTotalRewardVector(this->parametricModel.getTransitionMatrix()); + STORM_LOG_THROW((checkTask.isRewardModelSet() && this->parametricModel->hasRewardModel(checkTask.getRewardModel())) || (!checkTask.isRewardModelSet() && this->parametricModel->hasUniqueRewardModel()), storm::exceptions::InvalidPropertyException, "The reward model specified by the CheckTask is not available in the given model."); + typename SparseModelType::RewardModelType const& rewardModel = checkTask.isRewardModelSet() ? this->parametricModel->getRewardModel(checkTask.getRewardModel()) : this->parametricModel->getUniqueRewardModel(); + std::vector b = rewardModel.getTotalRewardVector(this->parametricModel->getTransitionMatrix()); - parameterLifter = std::make_unique>(this->parametricModel.getTransitionMatrix(), b, maybeStates, maybeStates); + parameterLifter = std::make_unique>(this->parametricModel->getTransitionMatrix(), b, maybeStates, maybeStates); // We only know a lower bound for the result @@ -173,7 +205,7 @@ namespace storm { template storm::modelchecker::SparseInstantiationModelChecker& SparseDtmcParameterLiftingModelChecker::getInstantiationChecker() { if (!instantiationChecker) { - instantiationChecker = std::make_unique>(this->parametricModel); + instantiationChecker = std::make_unique>(*this->parametricModel); instantiationChecker->specifyFormula(this->currentCheckTask->template convertValueType()); } return *instantiationChecker; @@ -205,7 +237,7 @@ namespace storm { if (this->currentCheckTask->isBoundSet() && solver->hasSchedulerHint()) { // If we reach this point, we know that after applying the hint, the x-values can only become larger (if we maximize) or smaller (if we minimize). std::unique_ptr> termCond; - storm::storage::BitVector relevantStatesInSubsystem = this->currentCheckTask->isOnlyInitialStatesRelevantSet() ? this->parametricModel.getInitialStates() % maybeStates : storm::storage::BitVector(maybeStates.getNumberOfSetBits(), true); + storm::storage::BitVector relevantStatesInSubsystem = this->currentCheckTask->isOnlyInitialStatesRelevantSet() ? this->parametricModel->getInitialStates() % maybeStates : storm::storage::BitVector(maybeStates.getNumberOfSetBits(), true); if (storm::solver::minimize(dirForParameters)) { // Terminate if the value for ALL relevant states is already below the threshold termCond = std::make_unique> (relevantStatesInSubsystem, this->currentCheckTask->getBoundThreshold(), true, false); diff --git a/src/storm-pars/modelchecker/region/SparseDtmcParameterLiftingModelChecker.h b/src/storm-pars/modelchecker/region/SparseDtmcParameterLiftingModelChecker.h index 8831bbe6b..d6f51032b 100644 --- a/src/storm-pars/modelchecker/region/SparseDtmcParameterLiftingModelChecker.h +++ b/src/storm-pars/modelchecker/region/SparseDtmcParameterLiftingModelChecker.h @@ -19,11 +19,14 @@ namespace storm { template class SparseDtmcParameterLiftingModelChecker : public SparseParameterLiftingModelChecker { public: - SparseDtmcParameterLiftingModelChecker(SparseModelType const& parametricModel); - SparseDtmcParameterLiftingModelChecker(SparseModelType const& parametricModel, std::unique_ptr>&& solverFactory); - - virtual bool canHandle(CheckTask const& checkTask) const override; - + SparseDtmcParameterLiftingModelChecker(); + SparseDtmcParameterLiftingModelChecker(std::unique_ptr>&& solverFactory); + virtual ~SparseDtmcParameterLiftingModelChecker() = default; + + virtual bool canHandle(std::shared_ptr parametricModel, CheckTask const& checkTask) const override; + virtual void specify(std::shared_ptr parametricModel, CheckTask const& checkTask) override; + void specify(std::shared_ptr parametricModel, CheckTask const& checkTask, bool skipModelSimplification); + boost::optional> getCurrentMinScheduler(); boost::optional> getCurrentMaxScheduler(); diff --git a/src/storm-pars/modelchecker/region/SparseMdpParameterLiftingModelChecker.cpp b/src/storm-pars/modelchecker/region/SparseMdpParameterLiftingModelChecker.cpp index f2c6bb21b..32f8ca809 100644 --- a/src/storm-pars/modelchecker/region/SparseMdpParameterLiftingModelChecker.cpp +++ b/src/storm-pars/modelchecker/region/SparseMdpParameterLiftingModelChecker.cpp @@ -1,5 +1,6 @@ #include "storm-pars/modelchecker/region/SparseMdpParameterLiftingModelChecker.h" #include "storm-pars/utility/parameterlifting.h" +#include "storm-pars/transformer/SparseParametricMdpSimplifier.h" #include "storm/adapters/RationalFunctionAdapter.h" #include "storm/modelchecker/propositional/SparsePropositionalModelChecker.h" @@ -16,25 +17,57 @@ #include "storm/exceptions/InvalidArgumentException.h" #include "storm/exceptions/InvalidPropertyException.h" #include "storm/exceptions/NotSupportedException.h" +#include "storm/exceptions/UnexpectedException.h" namespace storm { namespace modelchecker { + + template - SparseMdpParameterLiftingModelChecker::SparseMdpParameterLiftingModelChecker(SparseModelType const& parametricModel) : SparseParameterLiftingModelChecker(parametricModel), solverFactory(std::make_unique>()) { + SparseMdpParameterLiftingModelChecker::SparseMdpParameterLiftingModelChecker() : SparseMdpParameterLiftingModelChecker(std::make_unique>()) { + // Intentionally left empty } template - SparseMdpParameterLiftingModelChecker::SparseMdpParameterLiftingModelChecker(SparseModelType const& parametricModel, std::unique_ptr>&& solverFactory) : SparseParameterLiftingModelChecker(parametricModel), solverFactory(std::move(solverFactory)) { + SparseMdpParameterLiftingModelChecker::SparseMdpParameterLiftingModelChecker(std::unique_ptr>&& solverFactory) : solverFactory(std::move(solverFactory)) { + // Intentionally left empty } - + template - bool SparseMdpParameterLiftingModelChecker::canHandle(CheckTask const& checkTask) const { - bool result = checkTask.getFormula().isInFragment(storm::logic::reachability().setRewardOperatorsAllowed(true).setReachabilityRewardFormulasAllowed(true).setBoundedUntilFormulasAllowed(true).setStepBoundedUntilFormulasAllowed(true).setCumulativeRewardFormulasAllowed(true)); - if (result) { - STORM_LOG_WARN_COND(storm::utility::parameterlifting::validateParameterLiftingSound(this->parametricModel, checkTask.getFormula()), "Could not validate whether parameter lifting is applicable. Please validate manually..."); - } + bool SparseMdpParameterLiftingModelChecker::canHandle(std::shared_ptr parametricModel, CheckTask const& checkTask) const { + bool result = parametricModel->isOfType(storm::models::ModelType::Mdp); + result &= parametricModel->isSparseModel(); + result &= parametricModel->supportsParameters(); + auto mdp = parametricModel->template as(); + result &= static_cast(mdp); + result &= checkTask.getFormula().isInFragment(storm::logic::reachability().setRewardOperatorsAllowed(true).setReachabilityRewardFormulasAllowed(true).setBoundedUntilFormulasAllowed(true).setCumulativeRewardFormulasAllowed(true).setStepBoundedUntilFormulasAllowed(true).setTimeBoundedUntilFormulasAllowed(true)); return result; + } + + template + void SparseMdpParameterLiftingModelChecker::specify(std::shared_ptr parametricModel, CheckTask const& checkTask) { + auto mdp = parametricModel->template as(); + specify(mdp, checkTask, false); + } + + template + void SparseMdpParameterLiftingModelChecker::specify(std::shared_ptr parametricModel, CheckTask const& checkTask, bool skipModelSimplification) { + STORM_LOG_ASSERT(this->canHandle(parametricModel, checkTask), "specified model and formula can not be handled by this."); + + reset(); + + if (skipModelSimplification) { + this->parametricModel = parametricModel; + this->specifyFormula(checkTask); + } else { + auto simplifier = storm::transformer::SparseParametricMdpSimplifier(*parametricModel); + if (!simplifier.simplify(checkTask.getFormula())) { + STORM_LOG_THROW(false, storm::exceptions::UnexpectedException, "Simplifying the model was not successfull."); + } + this->parametricModel = simplifier.getSimplifiedModel(); + this->specifyFormula(checkTask.substituteFormula(*simplifier.getSimplifiedFormula())); + } } template @@ -53,27 +86,27 @@ namespace storm { STORM_LOG_THROW(*stepBound > 0, storm::exceptions::NotSupportedException, "Can not apply parameter lifting on step bounded formula: The step bound has to be positive."); // get the results for the subformulas - storm::modelchecker::SparsePropositionalModelChecker propositionalChecker(this->parametricModel); + storm::modelchecker::SparsePropositionalModelChecker propositionalChecker(*this->parametricModel); STORM_LOG_THROW(propositionalChecker.canHandle(checkTask.getFormula().getLeftSubformula()) && propositionalChecker.canHandle(checkTask.getFormula().getRightSubformula()), storm::exceptions::NotSupportedException, "Parameter lifting with non-propositional subformulas is not supported"); storm::storage::BitVector phiStates = std::move(propositionalChecker.check(checkTask.getFormula().getLeftSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector()); storm::storage::BitVector psiStates = std::move(propositionalChecker.check(checkTask.getFormula().getRightSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector()); // get the maybeStates maybeStates = storm::solver::minimize(checkTask.getOptimizationDirection()) ? - storm::utility::graph::performProbGreater0A(this->parametricModel.getTransitionMatrix(), this->parametricModel.getTransitionMatrix().getRowGroupIndices(), this->parametricModel.getBackwardTransitions(), phiStates, psiStates, true, *stepBound) : - storm::utility::graph::performProbGreater0E(this->parametricModel.getBackwardTransitions(), phiStates, psiStates, true, *stepBound); + storm::utility::graph::performProbGreater0A(this->parametricModel->getTransitionMatrix(), this->parametricModel->getTransitionMatrix().getRowGroupIndices(), this->parametricModel->getBackwardTransitions(), phiStates, psiStates, true, *stepBound) : + storm::utility::graph::performProbGreater0E(this->parametricModel->getBackwardTransitions(), phiStates, psiStates, true, *stepBound); maybeStates &= ~psiStates; // set the result for all non-maybe states - resultsForNonMaybeStates = std::vector(this->parametricModel.getNumberOfStates(), storm::utility::zero()); + resultsForNonMaybeStates = std::vector(this->parametricModel->getNumberOfStates(), storm::utility::zero()); storm::utility::vector::setVectorValues(resultsForNonMaybeStates, psiStates, storm::utility::one()); // if there are maybestates, create the parameterLifter if (!maybeStates.empty()) { // Create the vector of one-step probabilities to go to target states. - std::vector b = this->parametricModel.getTransitionMatrix().getConstrainedRowSumVector(storm::storage::BitVector(this->parametricModel.getTransitionMatrix().getRowCount(), true), psiStates); + std::vector b = this->parametricModel->getTransitionMatrix().getConstrainedRowSumVector(storm::storage::BitVector(this->parametricModel->getTransitionMatrix().getRowCount(), true), psiStates); - parameterLifter = std::make_unique>(this->parametricModel.getTransitionMatrix(), b, this->parametricModel.getTransitionMatrix().getRowFilter(maybeStates), maybeStates); + parameterLifter = std::make_unique>(this->parametricModel->getTransitionMatrix(), b, this->parametricModel->getTransitionMatrix().getRowFilter(maybeStates), maybeStates); computePlayer1Matrix(); applyPreviousResultAsHint = false; @@ -88,32 +121,32 @@ namespace storm { void SparseMdpParameterLiftingModelChecker::specifyUntilFormula(CheckTask const& checkTask) { // get the results for the subformulas - storm::modelchecker::SparsePropositionalModelChecker propositionalChecker(this->parametricModel); + storm::modelchecker::SparsePropositionalModelChecker propositionalChecker(*this->parametricModel); STORM_LOG_THROW(propositionalChecker.canHandle(checkTask.getFormula().getLeftSubformula()) && propositionalChecker.canHandle(checkTask.getFormula().getRightSubformula()), storm::exceptions::NotSupportedException, "Parameter lifting with non-propositional subformulas is not supported"); storm::storage::BitVector phiStates = std::move(propositionalChecker.check(checkTask.getFormula().getLeftSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector()); storm::storage::BitVector psiStates = std::move(propositionalChecker.check(checkTask.getFormula().getRightSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector()); // get the maybeStates std::pair statesWithProbability01 = storm::solver::minimize(checkTask.getOptimizationDirection()) ? - storm::utility::graph::performProb01Min(this->parametricModel.getTransitionMatrix(), this->parametricModel.getTransitionMatrix().getRowGroupIndices(), this->parametricModel.getBackwardTransitions(), phiStates, psiStates) : - storm::utility::graph::performProb01Max(this->parametricModel.getTransitionMatrix(), this->parametricModel.getTransitionMatrix().getRowGroupIndices(), this->parametricModel.getBackwardTransitions(), phiStates, psiStates); + storm::utility::graph::performProb01Min(this->parametricModel->getTransitionMatrix(), this->parametricModel->getTransitionMatrix().getRowGroupIndices(), this->parametricModel->getBackwardTransitions(), phiStates, psiStates) : + storm::utility::graph::performProb01Max(this->parametricModel->getTransitionMatrix(), this->parametricModel->getTransitionMatrix().getRowGroupIndices(), this->parametricModel->getBackwardTransitions(), phiStates, psiStates); maybeStates = ~(statesWithProbability01.first | statesWithProbability01.second); // set the result for all non-maybe states - resultsForNonMaybeStates = std::vector(this->parametricModel.getNumberOfStates(), storm::utility::zero()); + resultsForNonMaybeStates = std::vector(this->parametricModel->getNumberOfStates(), storm::utility::zero()); storm::utility::vector::setVectorValues(resultsForNonMaybeStates, statesWithProbability01.second, storm::utility::one()); // if there are maybestates, create the parameterLifter if (!maybeStates.empty()) { // Create the vector of one-step probabilities to go to target states. - std::vector b = this->parametricModel.getTransitionMatrix().getConstrainedRowSumVector(storm::storage::BitVector(this->parametricModel.getTransitionMatrix().getRowCount(), true), statesWithProbability01.second); + std::vector b = this->parametricModel->getTransitionMatrix().getConstrainedRowSumVector(storm::storage::BitVector(this->parametricModel->getTransitionMatrix().getRowCount(), true), statesWithProbability01.second); - parameterLifter = std::make_unique>(this->parametricModel.getTransitionMatrix(), b, this->parametricModel.getTransitionMatrix().getRowFilter(maybeStates), maybeStates); + parameterLifter = std::make_unique>(this->parametricModel->getTransitionMatrix(), b, this->parametricModel->getTransitionMatrix().getRowFilter(maybeStates), maybeStates); computePlayer1Matrix(); // Check whether there is an EC consisting of maybestates applyPreviousResultAsHint = storm::solver::minimize(checkTask.getOptimizationDirection()) || // when minimizing, there can not be an EC within the maybestates - storm::utility::graph::performProb1A(this->parametricModel.getTransitionMatrix(), this->parametricModel.getTransitionMatrix().getRowGroupIndices(), this->parametricModel.getBackwardTransitions(), maybeStates, ~maybeStates).full(); + storm::utility::graph::performProb1A(this->parametricModel->getTransitionMatrix(), this->parametricModel->getTransitionMatrix().getRowGroupIndices(), this->parametricModel->getBackwardTransitions(), maybeStates, ~maybeStates).full(); } // We know some bounds for the results @@ -125,40 +158,40 @@ namespace storm { void SparseMdpParameterLiftingModelChecker::specifyReachabilityRewardFormula(CheckTask const& checkTask) { // get the results for the subformula - storm::modelchecker::SparsePropositionalModelChecker propositionalChecker(this->parametricModel); + storm::modelchecker::SparsePropositionalModelChecker propositionalChecker(*this->parametricModel); STORM_LOG_THROW(propositionalChecker.canHandle(checkTask.getFormula().getSubformula()), storm::exceptions::NotSupportedException, "Parameter lifting with non-propositional subformulas is not supported"); storm::storage::BitVector targetStates = std::move(propositionalChecker.check(checkTask.getFormula().getSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector()); // get the maybeStates storm::storage::BitVector infinityStates = storm::solver::minimize(checkTask.getOptimizationDirection()) ? - storm::utility::graph::performProb1E(this->parametricModel.getTransitionMatrix(), this->parametricModel.getTransitionMatrix().getRowGroupIndices(), this->parametricModel.getBackwardTransitions(), storm::storage::BitVector(this->parametricModel.getNumberOfStates(), true), targetStates) : - storm::utility::graph::performProb1A(this->parametricModel.getTransitionMatrix(), this->parametricModel.getTransitionMatrix().getRowGroupIndices(), this->parametricModel.getBackwardTransitions(), storm::storage::BitVector(this->parametricModel.getNumberOfStates(), true), targetStates); + storm::utility::graph::performProb1E(this->parametricModel->getTransitionMatrix(), this->parametricModel->getTransitionMatrix().getRowGroupIndices(), this->parametricModel->getBackwardTransitions(), storm::storage::BitVector(this->parametricModel->getNumberOfStates(), true), targetStates) : + storm::utility::graph::performProb1A(this->parametricModel->getTransitionMatrix(), this->parametricModel->getTransitionMatrix().getRowGroupIndices(), this->parametricModel->getBackwardTransitions(), storm::storage::BitVector(this->parametricModel->getNumberOfStates(), true), targetStates); infinityStates.complement(); maybeStates = ~(targetStates | infinityStates); // set the result for all the non-maybe states - resultsForNonMaybeStates = std::vector(this->parametricModel.getNumberOfStates(), storm::utility::zero()); + resultsForNonMaybeStates = std::vector(this->parametricModel->getNumberOfStates(), storm::utility::zero()); storm::utility::vector::setVectorValues(resultsForNonMaybeStates, infinityStates, storm::utility::infinity()); // if there are maybestates, create the parameterLifter if (!maybeStates.empty()) { // Create the reward vector - STORM_LOG_THROW((checkTask.isRewardModelSet() && this->parametricModel.hasRewardModel(checkTask.getRewardModel())) || (!checkTask.isRewardModelSet() && this->parametricModel.hasUniqueRewardModel()), storm::exceptions::InvalidPropertyException, "The reward model specified by the CheckTask is not available in the given model."); + STORM_LOG_THROW((checkTask.isRewardModelSet() && this->parametricModel->hasRewardModel(checkTask.getRewardModel())) || (!checkTask.isRewardModelSet() && this->parametricModel->hasUniqueRewardModel()), storm::exceptions::InvalidPropertyException, "The reward model specified by the CheckTask is not available in the given model."); - typename SparseModelType::RewardModelType const& rewardModel = checkTask.isRewardModelSet() ? this->parametricModel.getRewardModel(checkTask.getRewardModel()) : this->parametricModel.getUniqueRewardModel(); + typename SparseModelType::RewardModelType const& rewardModel = checkTask.isRewardModelSet() ? this->parametricModel->getRewardModel(checkTask.getRewardModel()) : this->parametricModel->getUniqueRewardModel(); - std::vector b = rewardModel.getTotalRewardVector(this->parametricModel.getTransitionMatrix()); + std::vector b = rewardModel.getTotalRewardVector(this->parametricModel->getTransitionMatrix()); // We need to handle choices that lead to an infinity state. // As a maybeState does not have reward infinity, a choice leading to an infinity state will never be picked. Hence, we can unselect the corresponding rows - storm::storage::BitVector selectedRows = this->parametricModel.getTransitionMatrix().getRowFilter(maybeStates, ~infinityStates); + storm::storage::BitVector selectedRows = this->parametricModel->getTransitionMatrix().getRowFilter(maybeStates, ~infinityStates); - parameterLifter = std::make_unique>(this->parametricModel.getTransitionMatrix(), b, selectedRows, maybeStates); + parameterLifter = std::make_unique>(this->parametricModel->getTransitionMatrix(), b, selectedRows, maybeStates); computePlayer1Matrix(); // Check whether there is an EC consisting of maybestates applyPreviousResultAsHint = !storm::solver::minimize(checkTask.getOptimizationDirection()) || // when maximizing, there can not be an EC within the maybestates - storm::utility::graph::performProb1A(this->parametricModel.getTransitionMatrix(), this->parametricModel.getTransitionMatrix().getRowGroupIndices(), this->parametricModel.getBackwardTransitions(), maybeStates, ~maybeStates).full(); + storm::utility::graph::performProb1A(this->parametricModel->getTransitionMatrix(), this->parametricModel->getTransitionMatrix().getRowGroupIndices(), this->parametricModel->getBackwardTransitions(), maybeStates, ~maybeStates).full(); } // We only know a lower bound for the result @@ -178,15 +211,15 @@ namespace storm { STORM_LOG_THROW(*stepBound > 0, storm::exceptions::NotSupportedException, "Can not apply parameter lifting on step bounded formula: The step bound has to be positive."); //Every state is a maybeState - maybeStates = storm::storage::BitVector(this->parametricModel.getTransitionMatrix().getColumnCount(), true); - resultsForNonMaybeStates = std::vector(this->parametricModel.getNumberOfStates()); + maybeStates = storm::storage::BitVector(this->parametricModel->getTransitionMatrix().getColumnCount(), true); + resultsForNonMaybeStates = std::vector(this->parametricModel->getNumberOfStates()); // Create the reward vector - STORM_LOG_THROW((checkTask.isRewardModelSet() && this->parametricModel.hasRewardModel(checkTask.getRewardModel())) || (!checkTask.isRewardModelSet() && this->parametricModel.hasUniqueRewardModel()), storm::exceptions::InvalidPropertyException, "The reward model specified by the CheckTask is not available in the given model."); - typename SparseModelType::RewardModelType const& rewardModel = checkTask.isRewardModelSet() ? this->parametricModel.getRewardModel(checkTask.getRewardModel()) : this->parametricModel.getUniqueRewardModel(); - std::vector b = rewardModel.getTotalRewardVector(this->parametricModel.getTransitionMatrix()); + STORM_LOG_THROW((checkTask.isRewardModelSet() && this->parametricModel->hasRewardModel(checkTask.getRewardModel())) || (!checkTask.isRewardModelSet() && this->parametricModel->hasUniqueRewardModel()), storm::exceptions::InvalidPropertyException, "The reward model specified by the CheckTask is not available in the given model."); + typename SparseModelType::RewardModelType const& rewardModel = checkTask.isRewardModelSet() ? this->parametricModel->getRewardModel(checkTask.getRewardModel()) : this->parametricModel->getUniqueRewardModel(); + std::vector b = rewardModel.getTotalRewardVector(this->parametricModel->getTransitionMatrix()); - parameterLifter = std::make_unique>(this->parametricModel.getTransitionMatrix(), b, storm::storage::BitVector(this->parametricModel.getTransitionMatrix().getRowCount(), true), maybeStates); + parameterLifter = std::make_unique>(this->parametricModel->getTransitionMatrix(), b, storm::storage::BitVector(this->parametricModel->getTransitionMatrix().getRowCount(), true), maybeStates); computePlayer1Matrix(); applyPreviousResultAsHint = false; @@ -198,7 +231,7 @@ namespace storm { template storm::modelchecker::SparseInstantiationModelChecker& SparseMdpParameterLiftingModelChecker::getInstantiationChecker() { if (!instantiationChecker) { - instantiationChecker = std::make_unique>(this->parametricModel); + instantiationChecker = std::make_unique>(*this->parametricModel); instantiationChecker->specifyFormula(this->currentCheckTask->template convertValueType()); } return *instantiationChecker; @@ -235,7 +268,7 @@ namespace storm { if (this->currentCheckTask->isBoundSet() && this->currentCheckTask->getOptimizationDirection() == dirForParameters && solver->hasSchedulerHints()) { // If we reach this point, we know that after applying the hints, the x-values can only become larger (if we maximize) or smaller (if we minimize). std::unique_ptr> termCond; - storm::storage::BitVector relevantStatesInSubsystem = this->currentCheckTask->isOnlyInitialStatesRelevantSet() ? this->parametricModel.getInitialStates() % maybeStates : storm::storage::BitVector(maybeStates.getNumberOfSetBits(), true); + storm::storage::BitVector relevantStatesInSubsystem = this->currentCheckTask->isOnlyInitialStatesRelevantSet() ? this->parametricModel->getInitialStates() % maybeStates : storm::storage::BitVector(maybeStates.getNumberOfSetBits(), true); if (storm::solver::minimize(dirForParameters)) { // Terminate if the value for ALL relevant states is already below the threshold termCond = std::make_unique> (relevantStatesInSubsystem, this->currentCheckTask->getBoundThreshold(), true, false); @@ -276,14 +309,14 @@ namespace storm { void SparseMdpParameterLiftingModelChecker::computePlayer1Matrix() { uint_fast64_t n = 0; for(auto const& maybeState : maybeStates) { - n += this->parametricModel.getTransitionMatrix().getRowGroupSize(maybeState); + n += this->parametricModel->getTransitionMatrix().getRowGroupSize(maybeState); } // The player 1 matrix is the identity matrix of size n with the row groups as given by the original matrix storm::storage::SparseMatrixBuilder matrixBuilder(n, n, n, true, true, maybeStates.getNumberOfSetBits()); uint_fast64_t p1MatrixRow = 0; for (auto maybeState : maybeStates){ matrixBuilder.newRowGroup(p1MatrixRow); - for (uint_fast64_t row = this->parametricModel.getTransitionMatrix().getRowGroupIndices()[maybeState]; row < this->parametricModel.getTransitionMatrix().getRowGroupIndices()[maybeState + 1]; ++row){ + for (uint_fast64_t row = this->parametricModel->getTransitionMatrix().getRowGroupIndices()[maybeState]; row < this->parametricModel->getTransitionMatrix().getRowGroupIndices()[maybeState + 1]; ++row){ matrixBuilder.addNextValue(p1MatrixRow, p1MatrixRow, storm::utility::one()); ++p1MatrixRow; } diff --git a/src/storm-pars/modelchecker/region/SparseMdpParameterLiftingModelChecker.h b/src/storm-pars/modelchecker/region/SparseMdpParameterLiftingModelChecker.h index 55504bd9e..b08d71da2 100644 --- a/src/storm-pars/modelchecker/region/SparseMdpParameterLiftingModelChecker.h +++ b/src/storm-pars/modelchecker/region/SparseMdpParameterLiftingModelChecker.h @@ -20,11 +20,14 @@ namespace storm { template class SparseMdpParameterLiftingModelChecker : public SparseParameterLiftingModelChecker { public: - SparseMdpParameterLiftingModelChecker(SparseModelType const& parametricModel); - SparseMdpParameterLiftingModelChecker(SparseModelType const& parametricModel, std::unique_ptr>&& solverFactory); - - virtual bool canHandle(CheckTask const& checkTask) const override; - + SparseMdpParameterLiftingModelChecker(); + SparseMdpParameterLiftingModelChecker(std::unique_ptr>&& solverFactory); + virtual ~SparseMdpParameterLiftingModelChecker() = default; + + virtual bool canHandle(std::shared_ptr parametricModel, CheckTask const& checkTask) const override; + virtual void specify(std::shared_ptr parametricModel, CheckTask const& checkTask) override; + void specify(std::shared_ptr parametricModel, CheckTask const& checkTask, bool skipModelSimplification); + boost::optional> getCurrentMinScheduler(); boost::optional> getCurrentMaxScheduler(); boost::optional> getCurrentPlayer1Scheduler(); diff --git a/src/storm-pars/modelchecker/region/SparseParameterLiftingModelChecker.cpp b/src/storm-pars/modelchecker/region/SparseParameterLiftingModelChecker.cpp index 544dd734d..95886ffbd 100644 --- a/src/storm-pars/modelchecker/region/SparseParameterLiftingModelChecker.cpp +++ b/src/storm-pars/modelchecker/region/SparseParameterLiftingModelChecker.cpp @@ -1,4 +1,4 @@ -#include "SparseParameterLiftingModelChecker.h" +#include "storm-pars/modelchecker/region/SparseParameterLiftingModelChecker.h" #include "storm/adapters/RationalFunctionAdapter.h" #include "storm/logic/FragmentSpecification.h" @@ -15,14 +15,13 @@ namespace storm { namespace modelchecker { template - SparseParameterLiftingModelChecker::SparseParameterLiftingModelChecker(SparseModelType const& parametricModel) : parametricModel(parametricModel) { + SparseParameterLiftingModelChecker::SparseParameterLiftingModelChecker() { //Intentionally left empty } template void SparseParameterLiftingModelChecker::specifyFormula(storm::modelchecker::CheckTask const& checkTask) { - STORM_LOG_ASSERT(this->canHandle(checkTask), "specified formula can not be handled by this."); - reset(); + currentFormula = checkTask.getFormula().asSharedPointer(); currentCheckTask = std::make_unique>(checkTask.substituteFormula(*currentFormula).template convertValueType()); @@ -52,49 +51,74 @@ namespace storm { 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."); + 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 (result == RegionResult::Unknown) { - result = getInstantiationChecker().check(region.getCenterPoint())->asExplicitQualitativeCheckResult()[*this->parametricModel.getInitialStates().begin()] ? RegionResult::CenterSat : RegionResult::CenterViolated; + result = getInstantiationChecker().check(region.getCenterPoint())->asExplicitQualitativeCheckResult()[*this->parametricModel->getInitialStates().begin()] ? RegionResult::CenterSat : RegionResult::CenterViolated; } // try to prove AllSat or AllViolated, depending on the obtained result if (result == RegionResult::ExistsSat || result == RegionResult::CenterSat) { // show AllSat: storm::solver::OptimizationDirection parameterOptimizationDirection = isLowerBound(this->currentCheckTask->getBound().comparisonType) ? storm::solver::OptimizationDirection::Minimize : storm::solver::OptimizationDirection::Maximize; - if (this->check(region, parameterOptimizationDirection)->asExplicitQualitativeCheckResult()[*this->parametricModel.getInitialStates().begin()]) { + if (this->check(region, parameterOptimizationDirection)->asExplicitQualitativeCheckResult()[*this->parametricModel->getInitialStates().begin()]) { result = RegionResult::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 (!getInstantiationChecker().check(v)->asExplicitQualitativeCheckResult()[*this->parametricModel.getInitialStates().begin()]) { - result = RegionResult::ExistsBoth; - } - } + result = sampleVertices(region, result); } } else if (result == RegionResult::ExistsViolated || result == RegionResult::CenterViolated) { // show AllViolated: storm::solver::OptimizationDirection parameterOptimizationDirection = isLowerBound(this->currentCheckTask->getBound().comparisonType) ? storm::solver::OptimizationDirection::Maximize : storm::solver::OptimizationDirection::Minimize; - if (!this->check(region, parameterOptimizationDirection)->asExplicitQualitativeCheckResult()[*this->parametricModel.getInitialStates().begin()]) { + if (!this->check(region, parameterOptimizationDirection)->asExplicitQualitativeCheckResult()[*this->parametricModel->getInitialStates().begin()]) { result = RegionResult::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 (getInstantiationChecker().check(v)->asExplicitQualitativeCheckResult()[*this->parametricModel.getInitialStates().begin()]) { - result = RegionResult::ExistsBoth; - } - } + result = sampleVertices(region, result); } } else { STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "When analyzing a region, an invalid initial result was given: " << initialResult); } return result; } + + template + RegionResult SparseParameterLiftingModelChecker::sampleVertices(storm::storage::ParameterRegion const& region, RegionResult const& initialResult) { + RegionResult result = initialResult; + + if (result == RegionResult::AllSat || result == RegionResult::AllViolated) { + return result; + } + + bool hasSatPoint = result == RegionResult::ExistsViolated || result == RegionResult::CenterViolated; + bool hasViolatedPoint = result == RegionResult::ExistsSat || result == RegionResult::CenterSat; + + // 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(*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 std::unique_ptr SparseParameterLiftingModelChecker::check(storm::storage::ParameterRegion const& region, storm::solver::OptimizationDirection const& dirForParameters) { @@ -105,6 +129,16 @@ namespace storm { return quantitativeResult->template asExplicitQuantitativeCheckResult().compareAgainstBound(this->currentCheckTask->getFormula().asOperatorFormula().getComparisonType(), this->currentCheckTask->getFormula().asOperatorFormula().template getThresholdAs()); } } + + template + SparseModelType const& SparseParameterLiftingModelChecker::getConsideredParametricModel() const { + return *parametricModel; + } + + template + CheckTask const& SparseParameterLiftingModelChecker::getCurrentCheckTask() const { + return *currentCheckTask; + } template void SparseParameterLiftingModelChecker::specifyBoundedUntilFormula(CheckTask const& checkTask) { diff --git a/src/storm-pars/modelchecker/region/SparseParameterLiftingModelChecker.h b/src/storm-pars/modelchecker/region/SparseParameterLiftingModelChecker.h index ea53dd803..b8abd518c 100644 --- a/src/storm-pars/modelchecker/region/SparseParameterLiftingModelChecker.h +++ b/src/storm-pars/modelchecker/region/SparseParameterLiftingModelChecker.h @@ -22,10 +22,9 @@ namespace storm { template class SparseParameterLiftingModelChecker : public RegionModelChecker { public: - SparseParameterLiftingModelChecker(SparseModelType const& parametricModel); + SparseParameterLiftingModelChecker(); virtual ~SparseParameterLiftingModelChecker() = default; - virtual void specifyFormula(CheckTask const& checkTask) override; /*! * Analyzes the given region by means of parameter lifting. @@ -36,6 +35,11 @@ namespace storm { */ virtual RegionResult analyzeRegion(storm::storage::ParameterRegion const& region, RegionResult const& initialResult = RegionResult::Unknown, bool sampleVerticesOfRegion = false) override; + /*! + * Analyzes the 2^#parameters corner points of the given region. + */ + RegionResult sampleVertices(storm::storage::ParameterRegion const& region, RegionResult const& initialResult = RegionResult::Unknown); + /*! * Checks the specified formula on the given region by applying parameter lifting (Parameter choices are lifted to nondeterministic choices) * This yields a (sound) approximative model checking result. @@ -45,7 +49,12 @@ namespace storm { */ std::unique_ptr check(storm::storage::ParameterRegion const& region, storm::solver::OptimizationDirection const& dirForParameters); + + SparseModelType const& getConsideredParametricModel() const; + CheckTask const& getCurrentCheckTask() const; + protected: + void specifyFormula(CheckTask const& checkTask); // Resets all data that correspond to the currently defined property. virtual void reset() = 0; @@ -62,7 +71,7 @@ namespace storm { virtual std::unique_ptr computeQuantitativeValues(storm::storage::ParameterRegion const& region, storm::solver::OptimizationDirection const& dirForParameters) = 0; - SparseModelType const& parametricModel; + std::shared_ptr parametricModel; std::unique_ptr> currentCheckTask; private: diff --git a/src/storm-pars/settings/modules/ParametricSettings.cpp b/src/storm-pars/settings/modules/ParametricSettings.cpp index 5b7024af8..a50ef941e 100644 --- a/src/storm-pars/settings/modules/ParametricSettings.cpp +++ b/src/storm-pars/settings/modules/ParametricSettings.cpp @@ -14,7 +14,6 @@ namespace storm { const std::string ParametricSettings::moduleName = "parametric"; const std::string ParametricSettings::exportResultOptionName = "resultfile"; - const std::string ParametricSettings::simplifyOptionName = "simplify"; const std::string ParametricSettings::derivativesOptionName = "derivatives"; const std::string ParametricSettings::transformContinuousOptionName = "transformcontinuous"; const std::string ParametricSettings::transformContinuousShortOptionName = "tc"; @@ -22,7 +21,6 @@ namespace storm { ParametricSettings::ParametricSettings() : ModuleSettings(moduleName) { this->addOption(storm::settings::OptionBuilder(moduleName, exportResultOptionName, false, "A path to a file where the parametric result should be saved.") .addArgument(storm::settings::ArgumentBuilder::createStringArgument("path", "the location.").addValidatorString(ArgumentValidatorFactory::createWritableFileValidator()).build()).build()); - this->addOption(storm::settings::OptionBuilder(moduleName, simplifyOptionName, false, "Sets whether to perform simplification steps before model analysis.").build()); this->addOption(storm::settings::OptionBuilder(moduleName, derivativesOptionName, false, "Sets whether to generate the derivatives of the resulting rational function.").build()); this->addOption(storm::settings::OptionBuilder(moduleName, transformContinuousOptionName, false, "Sets whether to transform a continuous time input model to a discrete time model.").setShortName(transformContinuousShortOptionName).build()); } @@ -35,10 +33,6 @@ namespace storm { return this->getOption(exportResultOptionName).getArgumentByName("path").getValueAsString(); } - bool ParametricSettings::isSimplifySet() const { - return this->getOption(simplifyOptionName).getHasOptionBeenSet(); - } - bool ParametricSettings::isDerivativesSet() const { return this->getOption(derivativesOptionName).getHasOptionBeenSet(); } diff --git a/src/storm-pars/settings/modules/ParametricSettings.h b/src/storm-pars/settings/modules/ParametricSettings.h index 9bcfa9ea1..436faa92d 100644 --- a/src/storm-pars/settings/modules/ParametricSettings.h +++ b/src/storm-pars/settings/modules/ParametricSettings.h @@ -30,11 +30,6 @@ namespace storm { */ std::string exportResultPath() const; - /*! - * Retrieves whether or not the input model should be simplified before its analysis. - */ - bool isSimplifySet() const; - /*! * Retrieves whether or not derivatives of the resulting rational function are to be generated. * @@ -51,7 +46,6 @@ namespace storm { private: const static std::string exportResultOptionName; - const static std::string simplifyOptionName; const static std::string derivativesOptionName; const static std::string transformContinuousOptionName; const static std::string transformContinuousShortOptionName;