#ifndef STORM_H #define STORM_H #include #include #include #include #include #include #include #include "initialize.h" #include "storm-config.h" // Headers that provide auxiliary functionality. #include "src/settings/SettingsManager.h" #include "src/settings/modules/BisimulationSettings.h" #include "src/settings/modules/ParametricSettings.h" // Formula headers. #include "src/logic/Formulas.h" // Model headers. #include "src/models/ModelBase.h" #include "src/models/sparse/Model.h" #include "src/models/sparse/StandardRewardModel.h" #include "src/models/sparse/MarkovAutomaton.h" #include "src/models/symbolic/Model.h" #include "src/models/symbolic/StandardRewardModel.h" #include "src/storage/dd/Add.h" #include "src/storage/dd/Bdd.h" #include "src/parser/AutoParser.h" // Headers of builders. #include "src/builder/ExplicitPrismModelBuilder.h" #include "src/builder/DdPrismModelBuilder.h" // Headers for model processing. #include "src/storage/bisimulation/DeterministicModelBisimulationDecomposition.h" #include "src/storage/bisimulation/NondeterministicModelBisimulationDecomposition.h" #include "src/storage/ModelFormulasPair.h" // Headers for model checking. #include "src/modelchecker/prctl/SparseDtmcPrctlModelChecker.h" #include "src/modelchecker/prctl/SparseMdpPrctlModelChecker.h" #include "src/modelchecker/prctl/HybridDtmcPrctlModelChecker.h" #include "src/modelchecker/prctl/HybridMdpPrctlModelChecker.h" #include "src/modelchecker/prctl/SymbolicDtmcPrctlModelChecker.h" #include "src/modelchecker/prctl/SymbolicMdpPrctlModelChecker.h" #include "src/modelchecker/reachability/SparseDtmcEliminationModelChecker.h" #include "src/modelchecker/csl/SparseCtmcCslModelChecker.h" #include "src/modelchecker/csl/helper/SparseCtmcCslHelper.h" #include "src/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.h" #include "src/modelchecker/csl/HybridCtmcCslModelChecker.h" #include "src/modelchecker/results/ExplicitQualitativeCheckResult.h" #include "src/modelchecker/results/SymbolicQualitativeCheckResult.h" // Headers for counterexample generation. #include "src/counterexamples/MILPMinimalLabelSetGenerator.h" #include "src/counterexamples/SMTMinimalCommandSetGenerator.h" // Headers related to exception handling. #include "src/exceptions/InvalidStateException.h" #include "src/exceptions/InvalidArgumentException.h" #include "src/exceptions/InvalidSettingsException.h" #include "src/exceptions/InvalidTypeException.h" #include "src/exceptions/NotImplementedException.h" #include "src/exceptions/NotSupportedException.h" namespace storm { template std::shared_ptr> buildExplicitModel(std::string const& transitionsFile, std::string const& labelingFile, boost::optional const& stateRewardsFile = boost::none, boost::optional const& transitionRewardsFile = boost::none, boost::optional const& choiceLabelingFile = boost::none) { return storm::parser::AutoParser<>::parseModel(transitionsFile, labelingFile, stateRewardsFile ? stateRewardsFile.get() : "", transitionRewardsFile ? transitionRewardsFile.get() : "", choiceLabelingFile ? choiceLabelingFile.get() : "" ); } storm::prism::Program parseProgram(std::string const& path); std::vector> parseFormulasForExplicit(std::string const& inputString); std::vector> parseFormulasForProgram(std::string const& inputString, storm::prism::Program const& program); template storm::storage::ModelFormulasPair buildSymbolicModel(storm::prism::Program const& program, std::vector> const& formulas) { storm::storage::ModelFormulasPair result; storm::prism::Program translatedProgram; storm::settings::modules::GeneralSettings settings = storm::settings::generalSettings(); // Get the string that assigns values to the unknown currently undefined constants in the model. std::string constants = settings.getConstantDefinitionString(); // Customize and perform model-building. if (settings.getEngine() == storm::settings::modules::GeneralSettings::Engine::Sparse) { typename storm::builder::ExplicitPrismModelBuilder>::Options options; options = typename storm::builder::ExplicitPrismModelBuilder>::Options(formulas); options.addConstantDefinitionsFromString(program, constants); // Generate command labels if we are going to build a counterexample later. if (storm::settings::counterexampleGeneratorSettings().isMinimalCommandSetGenerationSet()) { options.buildCommandLabels = true; } storm::builder::ExplicitPrismModelBuilder builder; result.model = builder.translateProgram(program, options); translatedProgram = builder.getTranslatedProgram(); } else if (settings.getEngine() == storm::settings::modules::GeneralSettings::Engine::Dd || settings.getEngine() == storm::settings::modules::GeneralSettings::Engine::Hybrid) { typename storm::builder::DdPrismModelBuilder::Options options; options = typename storm::builder::DdPrismModelBuilder::Options(formulas); options.addConstantDefinitionsFromString(program, constants); storm::builder::DdPrismModelBuilder builder; result.model = builder.translateProgram(program, options); translatedProgram = builder.getTranslatedProgram(); } // There may be constants of the model appearing in the formulas, so we replace all their occurrences // by their definitions in the translated program. // Start by building a mapping from constants of the (translated) model to their defining expressions. std::map constantSubstitution; for (auto const& constant : translatedProgram.getConstants()) { if (constant.isDefined()) { constantSubstitution.emplace(constant.getExpressionVariable(), constant.getExpression()); } } for (auto const& formula : formulas) { result.formulas.emplace_back(formula->substitute(constantSubstitution)); } return result; } template std::shared_ptr performDeterministicSparseBisimulationMinimization(std::shared_ptr model, std::vector> const& formulas, storm::storage::BisimulationType type) { std::cout << "Performing bisimulation minimization... "; typename storm::storage::DeterministicModelBisimulationDecomposition::Options options; if (!formulas.empty()) { options = typename storm::storage::DeterministicModelBisimulationDecomposition::Options(*model, formulas); } options.setType(type); storm::storage::DeterministicModelBisimulationDecomposition bisimulationDecomposition(*model, options); bisimulationDecomposition.computeBisimulationDecomposition(); model = bisimulationDecomposition.getQuotient(); std::cout << "done." << std::endl << std::endl; return model; } template std::shared_ptr performNondeterministicSparseBisimulationMinimization(std::shared_ptr model, std::vector> const& formulas, storm::storage::BisimulationType type) { std::cout << "Performing bisimulation minimization... "; typename storm::storage::DeterministicModelBisimulationDecomposition::Options options; if (!formulas.empty()) { options = typename storm::storage::NondeterministicModelBisimulationDecomposition::Options(*model, formulas); } options.setType(type); storm::storage::NondeterministicModelBisimulationDecomposition bisimulationDecomposition(*model, options); bisimulationDecomposition.computeBisimulationDecomposition(); model = bisimulationDecomposition.getQuotient(); std::cout << "done." << std::endl << std::endl; return model; } template std::shared_ptr> performBisimulationMinimization(std::shared_ptr> const& model, std::vector> const& formulas, storm::storage::BisimulationType type) { using ValueType = typename ModelType::ValueType; STORM_LOG_THROW(model->isOfType(storm::models::ModelType::Dtmc) || model->isOfType(storm::models::ModelType::Ctmc) || model->isOfType(storm::models::ModelType::Mdp), storm::exceptions::InvalidSettingsException, "Bisimulation minimization is currently only available for DTMCs, CTMCs and MDPs."); model->reduceToStateBasedRewards(); if (model->isOfType(storm::models::ModelType::Dtmc)) { return performDeterministicSparseBisimulationMinimization>(model->template as>(), formulas, type); } else if (model->isOfType(storm::models::ModelType::Ctmc)) { return performDeterministicSparseBisimulationMinimization>(model->template as>(), formulas, type); } else { return performNondeterministicSparseBisimulationMinimization>(model->template as>(), formulas, type); } } template std::shared_ptr> performBisimulationMinimization(std::shared_ptr> const& model, std::shared_ptr const& formula, storm::storage::BisimulationType type) { std::vector> formulas = { formula }; return performBisimulationMinimization(model, formulas , type); } template std::shared_ptr preprocessModel(std::shared_ptr model, std::vector> const& formulas) { if(model->getType() == storm::models::ModelType::MarkovAutomaton && model->isSparseModel()) { std::shared_ptr> ma = model->template as>(); if (ma->hasOnlyTrivialNondeterminism()) { // Markov automaton can be converted into CTMC model = ma->convertToCTMC(); } } if (model->isSparseModel() && storm::settings::generalSettings().isBisimulationSet()) { storm::storage::BisimulationType bisimType = storm::storage::BisimulationType::Strong; if (storm::settings::bisimulationSettings().isWeakBisimulationSet()) { bisimType = storm::storage::BisimulationType::Weak; } STORM_LOG_THROW(model->isSparseModel(), storm::exceptions::InvalidSettingsException, "Bisimulation minimization is currently only available for sparse models."); return performBisimulationMinimization(model->template as>(), formulas, bisimType); } return model; } template void generateCounterexample(storm::prism::Program const& program, std::shared_ptr> model, std::shared_ptr const& formula) { if (storm::settings::counterexampleGeneratorSettings().isMinimalCommandSetGenerationSet()) { STORM_LOG_THROW(model->getType() == storm::models::ModelType::Mdp, storm::exceptions::InvalidTypeException, "Minimal command set generation is only available for MDPs."); STORM_LOG_THROW(storm::settings::generalSettings().isSymbolicSet(), storm::exceptions::InvalidSettingsException, "Minimal command set generation is only available for symbolic models."); std::shared_ptr> mdp = model->template as>(); // Determine whether we are required to use the MILP-version or the SAT-version. bool useMILP = storm::settings::counterexampleGeneratorSettings().isUseMilpBasedMinimalCommandSetGenerationSet(); if (useMILP) { storm::counterexamples::MILPMinimalLabelSetGenerator::computeCounterexample(program, *mdp, formula); } else { storm::counterexamples::SMTMinimalCommandSetGenerator::computeCounterexample(program, storm::settings::generalSettings().getConstantDefinitionString(), *mdp, formula); } } else { STORM_LOG_THROW(false, storm::exceptions::InvalidSettingsException, "No suitable counterexample representation selected."); } } #ifdef STORM_HAVE_CARL template<> inline void generateCounterexample(storm::prism::Program const& program, std::shared_ptr> model, std::shared_ptr const& formula) { STORM_LOG_THROW(false, storm::exceptions::InvalidSettingsException, "Unable to generate counterexample for parametric model."); } #endif template std::unique_ptr verifyModel(std::shared_ptr model, std::shared_ptr const& formula, bool onlyInitialStatesRelevant) { storm::settings::modules::GeneralSettings const& settings = storm::settings::generalSettings(); switch(settings.getEngine()) { case storm::settings::modules::GeneralSettings::Engine::Sparse: { std::shared_ptr> sparseModel = model->template as>(); STORM_LOG_THROW(sparseModel != nullptr, storm::exceptions::InvalidArgumentException, "Sparse engine requires a sparse input model"); return verifySparseModel(sparseModel, formula, onlyInitialStatesRelevant); } case storm::settings::modules::GeneralSettings::Engine::Hybrid: { std::shared_ptr> ddModel = model->template as>(); STORM_LOG_THROW(ddModel != nullptr, storm::exceptions::InvalidArgumentException, "Hybrid engine requires a dd input model"); return verifySymbolicModelWithHybridEngine(ddModel, formula, onlyInitialStatesRelevant); } case storm::settings::modules::GeneralSettings::Engine::Dd: { std::shared_ptr> ddModel = model->template as>(); STORM_LOG_THROW(ddModel != nullptr, storm::exceptions::InvalidArgumentException, "Dd engine requires a dd input model"); return verifySymbolicModelWithDdEngine(ddModel, formula, onlyInitialStatesRelevant); } case storm::settings::modules::GeneralSettings::Engine::AbstractionRefinement: { STORM_LOG_ASSERT(false, "This position should not be reached, as at this point no model has been built."); } } } template std::unique_ptr verifySparseModel(std::shared_ptr> model, std::shared_ptr const& formula, bool onlyInitialStatesRelevant = false) { std::unique_ptr result; storm::modelchecker::CheckTask task(*formula, onlyInitialStatesRelevant); if (model->getType() == storm::models::ModelType::Dtmc) { std::shared_ptr> dtmc = model->template as>(); storm::modelchecker::SparseDtmcPrctlModelChecker> modelchecker(*dtmc); if (modelchecker.canHandle(task)) { result = modelchecker.check(task); } else { storm::modelchecker::SparseDtmcEliminationModelChecker> modelchecker2(*dtmc); if (modelchecker2.canHandle(task)) { result = modelchecker2.check(task); } else { STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "The property " << *formula << " is not supported on DTMCs."); } } } else if (model->getType() == storm::models::ModelType::Mdp) { std::shared_ptr> mdp = model->template as>(); #ifdef STORM_HAVE_CUDA if (settings.isCudaSet()) { storm::modelchecker::TopologicalValueIterationMdpPrctlModelChecker modelchecker(*mdp); result = modelchecker.check(task); } else { storm::modelchecker::SparseMdpPrctlModelChecker> modelchecker(*mdp); result = modelchecker.check(task); } #else storm::modelchecker::SparseMdpPrctlModelChecker> modelchecker(*mdp); result = modelchecker.check(task); #endif } else if (model->getType() == storm::models::ModelType::Ctmc) { std::shared_ptr> ctmc = model->template as>(); storm::modelchecker::SparseCtmcCslModelChecker> modelchecker(*ctmc); result = modelchecker.check(task); } else if (model->getType() == storm::models::ModelType::MarkovAutomaton) { std::shared_ptr> ma = model->template as>(); storm::modelchecker::SparseMarkovAutomatonCslModelChecker> modelchecker(*ma); result = modelchecker.check(task); } else { STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "The model type " << model->getType() << " is not supported."); } return result; } #ifdef STORM_HAVE_CARL inline void exportParametricResultToFile(storm::RationalFunction const& result, storm::models::sparse::Dtmc::ConstraintCollector const& constraintCollector, std::string const& path) { std::ofstream filestream; filestream.open(path); // TODO: add checks. filestream << "!Parameters: "; std::set vars = result.gatherVariables(); std::copy(vars.begin(), vars.end(), std::ostream_iterator(filestream, ", ")); filestream << std::endl; filestream << "!Result: " << result << std::endl; filestream << "!Well-formed Constraints: " << std::endl; std::copy(constraintCollector.getWellformedConstraints().begin(), constraintCollector.getWellformedConstraints().end(), std::ostream_iterator>(filestream, "\n")); filestream << "!Graph-preserving Constraints: " << std::endl; std::copy(constraintCollector.getGraphPreservingConstraints().begin(), constraintCollector.getGraphPreservingConstraints().end(), std::ostream_iterator>(filestream, "\n")); filestream.close(); } template<> inline std::unique_ptr verifySparseModel(std::shared_ptr> model, std::shared_ptr const& formula, bool onlyInitialStatesRelevant) { std::unique_ptr result; if (model->getType() == storm::models::ModelType::Dtmc) { std::shared_ptr> dtmc = model->template as>(); storm::modelchecker::SparseDtmcEliminationModelChecker> modelchecker(*dtmc); storm::modelchecker::CheckTask task(*formula, onlyInitialStatesRelevant); if (modelchecker.canHandle(task)) { result = modelchecker.check(task); } else { STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "The parametric engine currently does not support this property on DTMCs."); } } else if (model->getType() == storm::models::ModelType::Mdp) { std::shared_ptr> mdp = model->template as>(); STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "The parametric engine currently does not support MDPs."); } else if (model->getType() == storm::models::ModelType::Ctmc) { // Hack to avoid instantiating the CTMC Model Checker which currently does not work for rational functions if (formula->isExpectedTimeOperatorFormula()) { // Compute expected time for pCTMCs STORM_LOG_THROW(formula->asExpectedTimeOperatorFormula().getSubformula().isEventuallyFormula(), storm::exceptions::NotSupportedException, "The parametric engine only supports Eventually formulas for this property"); storm::logic::EventuallyFormula eventuallyFormula = formula->asExpectedTimeOperatorFormula().getSubformula().asEventuallyFormula(); STORM_LOG_THROW(eventuallyFormula.getSubformula().isPropositionalFormula(), storm::exceptions::NotSupportedException, "The parametric engine does not support nested formulas on CTMCs"); // Compute goal states std::shared_ptr> ctmc = model->template as>(); storm::modelchecker::SparsePropositionalModelChecker> propositionalModelchecker(*ctmc); std::unique_ptr subResultPointer = propositionalModelchecker.check(eventuallyFormula.getSubformula()); storm::storage::BitVector const& targetStates = subResultPointer->asExplicitQualitativeCheckResult().getTruthValuesVector(); std::vector numericResult = storm::modelchecker::helper::SparseCtmcCslHelper::computeExpectedTimesElimination(ctmc->getTransitionMatrix(), ctmc->getBackwardTransitions(), ctmc->getExitRateVector(), ctmc->getInitialStates(), targetStates, false); result = std::unique_ptr(new storm::modelchecker::ExplicitQuantitativeCheckResult(std::move(numericResult))); } else if (formula->isProbabilityOperatorFormula()) { // Compute reachability probability for pCTMCs storm::logic::ProbabilityOperatorFormula probOpFormula = formula->asProbabilityOperatorFormula(); STORM_LOG_THROW(probOpFormula.getSubformula().isUntilFormula() || probOpFormula.getSubformula().isEventuallyFormula(), storm::exceptions::NotSupportedException, "The parametric engine only supports Until formulas for this property"); // Compute phi and psi states std::shared_ptr> ctmc = model->template as>(); storm::modelchecker::SparsePropositionalModelChecker> propositionalModelchecker(*ctmc); storm::storage::BitVector phiStates(model->getNumberOfStates(), true); storm::storage::BitVector psiStates; if (probOpFormula.getSubformula().isUntilFormula()) { // Until formula storm::logic::UntilFormula untilFormula = formula->asProbabilityOperatorFormula().getSubformula().asUntilFormula(); STORM_LOG_THROW(untilFormula.getLeftSubformula().isPropositionalFormula(), storm::exceptions::NotSupportedException, "The parametric engine does not support nested formulas on CTMCs"); STORM_LOG_THROW(untilFormula.getRightSubformula().isPropositionalFormula(), storm::exceptions::NotSupportedException, "The parametric engine does not support nested formulas on CTMCs"); std::unique_ptr leftResultPointer = propositionalModelchecker.check(untilFormula.getLeftSubformula()); std::unique_ptr rightResultPointer = propositionalModelchecker.check(untilFormula.getRightSubformula()); phiStates = leftResultPointer->asExplicitQualitativeCheckResult().getTruthValuesVector(); psiStates = rightResultPointer->asExplicitQualitativeCheckResult().getTruthValuesVector(); } else { // Eventually formula assert(probOpFormula.getSubformula().isEventuallyFormula()); storm::logic::EventuallyFormula eventuallyFormula = probOpFormula.getSubformula().asEventuallyFormula(); STORM_LOG_THROW(eventuallyFormula.getSubformula().isPropositionalFormula(), storm::exceptions::NotSupportedException, "The parametric engine does not support nested formulas on CTMCs"); std::unique_ptr resultPointer = propositionalModelchecker.check(eventuallyFormula.getSubformula()); psiStates = resultPointer->asExplicitQualitativeCheckResult().getTruthValuesVector(); } std::vector numericResult = storm::modelchecker::helper::SparseCtmcCslHelper::computeUntilProbabilitiesElimination(ctmc->getTransitionMatrix(), ctmc->getBackwardTransitions(), ctmc->getExitRateVector(), ctmc->getInitialStates(), phiStates, psiStates, false); result = std::unique_ptr(new storm::modelchecker::ExplicitQuantitativeCheckResult(std::move(numericResult))); } else { STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "The parametric engine currently does not support this property on CTMCs."); } } else { STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "The parametric engine currently does not support " << model->getType()); } return result; } #endif template std::unique_ptr verifySymbolicModelWithHybridEngine(std::shared_ptr> model, std::shared_ptr const& formula, bool onlyInitialStatesRelevant = false) { std::unique_ptr result; storm::modelchecker::CheckTask task(*formula, onlyInitialStatesRelevant); if (model->getType() == storm::models::ModelType::Dtmc) { std::shared_ptr> dtmc = model->template as>(); storm::modelchecker::HybridDtmcPrctlModelChecker modelchecker(*dtmc); if (modelchecker.canHandle(task)) { result = modelchecker.check(task); } } else if (model->getType() == storm::models::ModelType::Ctmc) { std::shared_ptr> ctmc = model->template as>(); storm::modelchecker::HybridCtmcCslModelChecker modelchecker(*ctmc); if (modelchecker.canHandle(task)) { result = modelchecker.check(task); } } else if (model->getType() == storm::models::ModelType::Mdp) { std::shared_ptr> mdp = model->template as>(); storm::modelchecker::HybridMdpPrctlModelChecker modelchecker(*mdp); if (modelchecker.canHandle(task)) { result = modelchecker.check(task); } } else { STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "This functionality is not yet implemented."); } return result; } template std::unique_ptr verifySymbolicModelWithDdEngine(std::shared_ptr> model, std::shared_ptr const& formula, bool onlyInitialStatesRelevant) { std::unique_ptr result; storm::modelchecker::CheckTask task(*formula, onlyInitialStatesRelevant); if (model->getType() == storm::models::ModelType::Dtmc) { std::shared_ptr> dtmc = model->template as>(); storm::modelchecker::SymbolicDtmcPrctlModelChecker modelchecker(*dtmc); if (modelchecker.canHandle(task)) { result = modelchecker.check(task); } } else if (model->getType() == storm::models::ModelType::Mdp) { std::shared_ptr> mdp = model->template as>(); storm::modelchecker::SymbolicMdpPrctlModelChecker modelchecker(*mdp); if (modelchecker.canHandle(task)) { result = modelchecker.check(task); } } else { STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "This functionality is not yet implemented."); } return result; } template void exportMatrixToFile(std::shared_ptr> model, std::string const& filepath) { STORM_LOG_THROW(model->getType() != storm::models::ModelType::Ctmc, storm::exceptions::NotImplementedException, "This functionality is not yet implemented." ); std::ofstream ofs; ofs.open (filepath, std::ofstream::out); model->getTransitionMatrix().printAsMatlabMatrix(ofs); ofs.close(); } } #endif /* STORM_H */