#include "storm-pars/modelchecker/instantiation/SparseMdpInstantiationModelChecker.h" #include "storm/logic/FragmentSpecification.h" #include "storm/modelchecker/results/ExplicitQuantitativeCheckResult.h" #include "storm/modelchecker/results/ExplicitQualitativeCheckResult.h" #include "storm/modelchecker/hints/ExplicitModelCheckerHint.h" #include "storm/storage/Scheduler.h" #include "storm/utility/graph.h" #include "storm/utility/vector.h" #include "storm/exceptions/InvalidArgumentException.h" #include "storm/exceptions/InvalidStateException.h" namespace storm { namespace modelchecker { template SparseMdpInstantiationModelChecker::SparseMdpInstantiationModelChecker(SparseModelType const& parametricModel) : SparseInstantiationModelChecker(parametricModel), modelInstantiator(parametricModel) { //Intentionally left empty } template std::unique_ptr SparseMdpInstantiationModelChecker::check(Environment const& env, storm::utility::parametric::Valuation const& valuation) { STORM_LOG_THROW(this->currentCheckTask, storm::exceptions::InvalidStateException, "Checking has been invoked but no property has been specified before."); auto const& instantiatedModel = modelInstantiator.instantiate(valuation); STORM_LOG_THROW(instantiatedModel.getTransitionMatrix().isProbabilistic(), storm::exceptions::InvalidArgumentException, "Instantiation point is invalid as the transition matrix becomes non-stochastic."); storm::modelchecker::SparseMdpPrctlModelChecker> modelChecker(instantiatedModel); // Check if there are some optimizations implemented for the specified property if (this->currentCheckTask->getFormula().isInFragment(storm::logic::reachability())) { return checkReachabilityProbabilityFormula(env, modelChecker, instantiatedModel); } else if (this->currentCheckTask->getFormula().isInFragment(storm::logic::propositional().setRewardOperatorsAllowed(true).setReachabilityRewardFormulasAllowed(true).setOperatorAtTopLevelRequired(true).setNestedOperatorsAllowed(false))) { return checkReachabilityRewardFormula(env, modelChecker, instantiatedModel); } else if (this->currentCheckTask->getFormula().isInFragment(storm::logic::propositional().setProbabilityOperatorsAllowed(true).setBoundedUntilFormulasAllowed(true).setStepBoundedUntilFormulasAllowed(true).setTimeBoundedUntilFormulasAllowed(true).setOperatorAtTopLevelRequired(true).setNestedOperatorsAllowed(false))) { return checkBoundedUntilFormula(env, modelChecker); } else { return modelChecker.check(env, *this->currentCheckTask); } } template std::unique_ptr SparseMdpInstantiationModelChecker::checkReachabilityProbabilityFormula(Environment const& env, storm::modelchecker::SparseMdpPrctlModelChecker>& modelChecker, storm::models::sparse::Mdp const& instantiatedModel) { this->currentCheckTask->setProduceSchedulers(true); if (!this->currentCheckTask->getHint().isExplicitModelCheckerHint()) { this->currentCheckTask->setHint(std::make_shared>()); } ExplicitModelCheckerHint& hint = this->currentCheckTask->getHint().template asExplicitModelCheckerHint(); std::unique_ptr result; // Check the formula and store the result as a hint for the next call. // For qualitative properties, we still want a quantitative result hint. Hence we perform the check on the subformula if (this->currentCheckTask->getFormula().asOperatorFormula().hasQuantitativeResult()) { result = modelChecker.check(env, *this->currentCheckTask); storm::storage::Scheduler const& scheduler = result->template asExplicitQuantitativeCheckResult().getScheduler(); hint.setResultHint(result->template asExplicitQuantitativeCheckResult().getValueVector()); hint.setSchedulerHint(dynamic_cast const&>(scheduler)); } else { auto newCheckTask = this->currentCheckTask->substituteFormula(this->currentCheckTask->getFormula().asOperatorFormula().getSubformula()).setOnlyInitialStatesRelevant(false); std::unique_ptr quantitativeResult = modelChecker.computeProbabilities(env, newCheckTask); result = quantitativeResult->template asExplicitQuantitativeCheckResult().compareAgainstBound(this->currentCheckTask->getFormula().asOperatorFormula().getComparisonType(), this->currentCheckTask->getFormula().asOperatorFormula().template getThresholdAs()); storm::storage::Scheduler& scheduler = quantitativeResult->template asExplicitQuantitativeCheckResult().getScheduler(); hint.setResultHint(std::move(quantitativeResult->template asExplicitQuantitativeCheckResult().getValueVector())); hint.setSchedulerHint(std::move(dynamic_cast&>(scheduler))); } if (this->getInstantiationsAreGraphPreserving() && !hint.hasMaybeStates()) { // Extract the maybe states from the current result. assert(hint.hasResultHint()); storm::storage::BitVector maybeStates = ~storm::utility::vector::filter(hint.getResultHint(), [] (ConstantType const& value) -> bool { return storm::utility::isZero(value) || storm::utility::isOne(value); }); hint.setMaybeStates(std::move(maybeStates)); hint.setComputeOnlyMaybeStates(true); // Check if there can be end components within the maybestates if (storm::solver::minimize(this->currentCheckTask->getOptimizationDirection()) || storm::utility::graph::performProb1A(instantiatedModel.getTransitionMatrix(), instantiatedModel.getTransitionMatrix().getRowGroupIndices(), instantiatedModel.getBackwardTransitions(), hint.getMaybeStates(), ~hint.getMaybeStates()).full()) { hint.setNoEndComponentsInMaybeStates(true); } } return result; } template std::unique_ptr SparseMdpInstantiationModelChecker::checkReachabilityRewardFormula(Environment const& env, storm::modelchecker::SparseMdpPrctlModelChecker>& modelChecker, storm::models::sparse::Mdp const& instantiatedModel) { this->currentCheckTask->setProduceSchedulers(true); if (!this->currentCheckTask->getHint().isExplicitModelCheckerHint()) { this->currentCheckTask->setHint(std::make_shared>()); } ExplicitModelCheckerHint& hint = this->currentCheckTask->getHint().template asExplicitModelCheckerHint(); std::unique_ptr result; // Check the formula and store the result as a hint for the next call. // For qualitative properties, we still want a quantitative result hint. Hence we perform the check on the subformula if(this->currentCheckTask->getFormula().asOperatorFormula().hasQuantitativeResult()) { std::unique_ptr result = modelChecker.check(env, *this->currentCheckTask); storm::storage::Scheduler const& scheduler = result->template asExplicitQuantitativeCheckResult().getScheduler(); hint.setResultHint(result->template asExplicitQuantitativeCheckResult().getValueVector()); hint.setSchedulerHint(dynamic_cast const&>(scheduler)); } else { auto newCheckTask = this->currentCheckTask->substituteFormula(this->currentCheckTask->getFormula().asOperatorFormula().getSubformula()).setOnlyInitialStatesRelevant(false); std::unique_ptr quantitativeResult = modelChecker.computeRewards(env, this->currentCheckTask->getFormula().asRewardOperatorFormula().getMeasureType(), newCheckTask); result = quantitativeResult->template asExplicitQuantitativeCheckResult().compareAgainstBound(this->currentCheckTask->getFormula().asOperatorFormula().getComparisonType(), this->currentCheckTask->getFormula().asOperatorFormula().template getThresholdAs()); storm::storage::Scheduler& scheduler = quantitativeResult->template asExplicitQuantitativeCheckResult().getScheduler(); hint.setResultHint(std::move(quantitativeResult->template asExplicitQuantitativeCheckResult().getValueVector())); hint.setSchedulerHint(std::move(dynamic_cast&>(scheduler))); } if (this->getInstantiationsAreGraphPreserving() && !hint.hasMaybeStates()) { // Extract the maybe states from the current result. assert(hint.hasResultHint()); storm::storage::BitVector maybeStates = ~storm::utility::vector::filterInfinity(hint.getResultHint()); // We need to exclude the target states from the maybe states. // Note that we can not consider the states with reward zero since a valuation might set a reward to zero std::unique_ptr subFormulaResult = modelChecker.check(env, this->currentCheckTask->getFormula().asOperatorFormula().getSubformula().asEventuallyFormula().getSubformula()); maybeStates = maybeStates & ~(subFormulaResult->asExplicitQualitativeCheckResult().getTruthValuesVector()); hint.setMaybeStates(std::move(maybeStates)); hint.setComputeOnlyMaybeStates(true); // Check if there can be end components within the maybestates if (storm::solver::maximize(this->currentCheckTask->getOptimizationDirection()) || storm::utility::graph::performProb1A(instantiatedModel.getTransitionMatrix(), instantiatedModel.getTransitionMatrix().getRowGroupIndices(), instantiatedModel.getBackwardTransitions(), hint.getMaybeStates(), ~hint.getMaybeStates()).full()) { hint.setNoEndComponentsInMaybeStates(true); } } return result; } template std::unique_ptr SparseMdpInstantiationModelChecker::checkBoundedUntilFormula(Environment const& env, storm::modelchecker::SparseMdpPrctlModelChecker>& modelChecker) { if (!this->currentCheckTask->getHint().isExplicitModelCheckerHint()) { this->currentCheckTask->setHint(std::make_shared>()); } std::unique_ptr result; ExplicitModelCheckerHint& hint = this->currentCheckTask->getHint().template asExplicitModelCheckerHint(); if (this->getInstantiationsAreGraphPreserving() && !hint.hasMaybeStates()) { // We extract the maybestates from the quantitative result // For qualitative properties, we still need a quantitative result. Hence we perform the check on the subformula if (this->currentCheckTask->getFormula().asOperatorFormula().hasQuantitativeResult()) { result = modelChecker.check(env, *this->currentCheckTask); hint.setResultHint(result->template asExplicitQuantitativeCheckResult().getValueVector()); } else { auto newCheckTask = this->currentCheckTask->substituteFormula(this->currentCheckTask->getFormula().asOperatorFormula().getSubformula()).setOnlyInitialStatesRelevant(false); std::unique_ptr quantitativeResult = modelChecker.computeProbabilities(env, newCheckTask); result = quantitativeResult->template asExplicitQuantitativeCheckResult().compareAgainstBound(this->currentCheckTask->getFormula().asOperatorFormula().getComparisonType(), this->currentCheckTask->getFormula().asOperatorFormula().template getThresholdAs()); hint.setResultHint(std::move(quantitativeResult->template asExplicitQuantitativeCheckResult().getValueVector())); } storm::storage::BitVector maybeStates = storm::utility::vector::filterGreaterZero(hint.getResultHint()); // We need to exclude the target states from the maybe states. // Note that we can not consider the states with probability one since a state might reach a target state with prob 1 within >0 steps std::unique_ptr subFormulaResult = modelChecker.check(env, this->currentCheckTask->getFormula().asOperatorFormula().getSubformula().asBoundedUntilFormula().getRightSubformula()); maybeStates = maybeStates & ~(subFormulaResult->asExplicitQualitativeCheckResult().getTruthValuesVector()); hint.setMaybeStates(std::move(maybeStates)); hint.setComputeOnlyMaybeStates(true); } else { result = modelChecker.check(env, *this->currentCheckTask); } return result; } template class SparseMdpInstantiationModelChecker, double>; template class SparseMdpInstantiationModelChecker, storm::RationalNumber>; } }