#pragma once #include "storm/api/storm.h" #include "storm-counterexamples/api/counterexamples.h" #include "storm-parsers/api/storm-parsers.h" #include "storm/utility/SignalHandler.h" #include "storm/io/file.h" #include "storm/utility/macros.h" #include "storm/utility/NumberTraits.h" #include "storm/utility/Engine.h" #include "storm/utility/AutomaticSettings.h" #include "storm/utility/initialize.h" #include "storm/utility/Stopwatch.h" #include #include "storm/storage/SymbolicModelDescription.h" #include "storm/storage/jani/Property.h" #include "storm/builder/BuilderType.h" #include "storm/models/ModelBase.h" #include "storm/environment/Environment.h" #include "storm/exceptions/OptionParserException.h" #include "storm/modelchecker/results/SymbolicQualitativeCheckResult.h" #include "storm/models/sparse/StandardRewardModel.h" #include "storm/models/symbolic/StandardRewardModel.h" #include "storm/models/symbolic/MarkovAutomaton.h" #include "storm/settings/SettingsManager.h" #include "storm/settings/modules/ResourceSettings.h" #include "storm/settings/modules/JitBuilderSettings.h" #include "storm/settings/modules/BuildSettings.h" #include "storm/settings/modules/DebugSettings.h" #include "storm/settings/modules/IOSettings.h" #include "storm/settings/modules/CoreSettings.h" #include "storm/settings/modules/AbstractionSettings.h" #include "storm/settings/modules/ResourceSettings.h" #include "storm/settings/modules/ModelCheckerSettings.h" #include "storm/settings/modules/TransformationSettings.h" #include "storm/settings/modules/HintSettings.h" #include "storm/storage/Qvbs.h" #include "storm/utility/Stopwatch.h" namespace storm { namespace cli { struct SymbolicInput { // The symbolic model description. boost::optional model; // The original properties to check. std::vector properties; // The preprocessed properties to check (in case they needed amendment). boost::optional> preprocessedProperties; }; void parseSymbolicModelDescription(storm::settings::modules::IOSettings const& ioSettings, SymbolicInput& input) { auto buildSettings = storm::settings::getModule(); if (ioSettings.isPrismOrJaniInputSet()) { storm::utility::Stopwatch modelParsingWatch(true); if (ioSettings.isPrismInputSet()) { input.model = storm::api::parseProgram(ioSettings.getPrismInputFilename(), buildSettings.isPrismCompatibilityEnabled(), !buildSettings.isNoSimplifySet()); } else { boost::optional> propertyFilter; if (ioSettings.isJaniPropertiesSet()) { if (ioSettings.areJaniPropertiesSelected()) { propertyFilter = ioSettings.getSelectedJaniProperties(); } else { propertyFilter = boost::none; } } else { propertyFilter = std::vector(); } auto janiInput = storm::api::parseJaniModel(ioSettings.getJaniInputFilename(), propertyFilter); input.model = std::move(janiInput.first); if (ioSettings.isJaniPropertiesSet()) { input.properties = std::move(janiInput.second); } } modelParsingWatch.stop(); STORM_PRINT("Time for model input parsing: " << modelParsingWatch << "." << std::endl << std::endl); } } void parseProperties(storm::settings::modules::IOSettings const& ioSettings, SymbolicInput& input, boost::optional> const& propertyFilter) { if (ioSettings.isPropertySet()) { std::vector newProperties; if (input.model) { newProperties = storm::api::parsePropertiesForSymbolicModelDescription(ioSettings.getProperty(), input.model.get(), propertyFilter); } else { newProperties = storm::api::parseProperties(ioSettings.getProperty(), propertyFilter); } input.properties.insert(input.properties.end(), newProperties.begin(), newProperties.end()); } } SymbolicInput parseSymbolicInputQvbs(storm::settings::modules::IOSettings const& ioSettings) { // Parse the model input SymbolicInput input; storm::storage::QvbsBenchmark benchmark(ioSettings.getQvbsModelName()); STORM_PRINT_AND_LOG(benchmark.getInfo(ioSettings.getQvbsInstanceIndex(), ioSettings.getQvbsPropertyFilter())); storm::utility::Stopwatch modelParsingWatch(true); auto janiInput = storm::api::parseJaniModel(benchmark.getJaniFile(ioSettings.getQvbsInstanceIndex()), ioSettings.getQvbsPropertyFilter()); input.model = std::move(janiInput.first); input.properties = std::move(janiInput.second); modelParsingWatch.stop(); STORM_PRINT("Time for model input parsing: " << modelParsingWatch << "." << std::endl << std::endl); // Parse additional properties boost::optional> propertyFilter = storm::api::parsePropertyFilter(ioSettings.getPropertyFilter()); parseProperties(ioSettings, input, propertyFilter); // Substitute constant definitions auto constantDefinitions = input.model.get().parseConstantDefinitions(benchmark.getConstantDefinition(ioSettings.getQvbsInstanceIndex())); input.model = input.model.get().preprocess(constantDefinitions); if (!input.properties.empty()) { input.properties = storm::api::substituteConstantsInProperties(input.properties, constantDefinitions); } return input; } SymbolicInput parseSymbolicInput() { auto ioSettings = storm::settings::getModule(); if (ioSettings.isQvbsInputSet()) { return parseSymbolicInputQvbs(ioSettings); } else { // Parse the property filter, if any is given. boost::optional> propertyFilter = storm::api::parsePropertyFilter(ioSettings.getPropertyFilter()); SymbolicInput input; parseSymbolicModelDescription(ioSettings, input); parseProperties(ioSettings, input, propertyFilter); return input; } } struct ModelProcessingInformation { // The engine to use storm::utility::Engine engine; // If set, bisimulation will be applied. bool applyBisimulation; // If set, a transformation to Jani will be enforced bool transformToJani; // Which data type is to be used for numbers ... enum class ValueType { FinitePrecision, Exact, Parametric }; ValueType buildValueType; // ... during model building ValueType verificationValueType; // ... during model verification // The Dd library to be used storm::dd::DdType ddType; // The environment used during model checking storm::Environment env; // A flag which is set to true, if the settings were detected to be compatible. // If this is false, it could be that the query can not be handled. bool isCompatible; }; void getModelProcessingInformationAutomatic(SymbolicInput const& input, ModelProcessingInformation& mpi) { auto hints = storm::settings::getModule(); STORM_LOG_THROW(input.model.is_initialized(), storm::exceptions::InvalidArgumentException, "Automatic engine requires a JANI input model."); STORM_LOG_THROW(input.model->isJaniModel(), storm::exceptions::InvalidArgumentException, "Automatic engine requires a JANI input model."); std::vector const& properties = input.preprocessedProperties.is_initialized() ? input.preprocessedProperties.get() : input.properties; STORM_LOG_THROW(!properties.empty(), storm::exceptions::InvalidArgumentException, "Automatic engine requires a property."); STORM_LOG_WARN_COND(properties.size() == 1, "Automatic engine does not support decisions based on multiple properties. Only the first property will be considered."); storm::utility::AutomaticSettings as; if (hints.isNumberStatesSet()) { as.predict(input.model->asJaniModel(), properties.front(), hints.getNumberStates()); } else { as.predict(input.model->asJaniModel(), properties.front()); } mpi.engine = as.getEngine(); if (as.enableBisimulation()) { mpi.applyBisimulation = true; } if (as.enableExact() && mpi.verificationValueType == ModelProcessingInformation::ValueType::FinitePrecision) { mpi.verificationValueType = ModelProcessingInformation::ValueType::Exact; } STORM_PRINT_AND_LOG( "Automatic engine picked the following settings: " << std::endl << "\tengine=" << mpi.engine << std::boolalpha << "\t bisimulation=" << mpi.applyBisimulation << "\t exact=" << (mpi.verificationValueType != ModelProcessingInformation::ValueType::FinitePrecision) << std::noboolalpha << std::endl); } /*! * Sets the model processing information based on the given input. * Finding the right model processing information might require a conversion to jani. * In this case, the jani conversion is stored in the transformedJaniInput pointer (unless it is null) */ ModelProcessingInformation getModelProcessingInformation(SymbolicInput const& input, std::shared_ptr const& transformedJaniInput = nullptr) { ModelProcessingInformation mpi; auto ioSettings = storm::settings::getModule(); auto coreSettings = storm::settings::getModule(); auto generalSettings = storm::settings::getModule(); auto bisimulationSettings = storm::settings::getModule(); // Set the engine. mpi.engine = coreSettings.getEngine(); // Set whether bisimulation is to be used. mpi.applyBisimulation = generalSettings.isBisimulationSet(); // Set the value type used for numeric values if (generalSettings.isParametricSet()) { mpi.verificationValueType = ModelProcessingInformation::ValueType::Parametric; } else if (generalSettings.isExactSet()) { mpi.verificationValueType = ModelProcessingInformation::ValueType::Exact; } else { mpi.verificationValueType = ModelProcessingInformation::ValueType::FinitePrecision; } auto originalVerificationValueType = mpi.verificationValueType; // Since the remaining settings could depend on the ones above, we need apply the automatic engine now. bool useAutomatic = input.model.is_initialized() && mpi.engine == storm::utility::Engine::Automatic; if (useAutomatic) { if (input.model->isJaniModel()) { // This can potentially overwrite the settings above, but will not overwrite settings that were explicitly set by the user (e.g. we will not disable bisimulation or disable exact arithmetic) getModelProcessingInformationAutomatic(input, mpi); } else { // Transform Prism to jani first STORM_LOG_ASSERT(input.model->isPrismProgram(), "Unexpected type of input."); SymbolicInput janiInput; janiInput.properties = input.properties; storm::prism::Program const& prog = input.model.get().asPrismProgram(); auto modelAndProperties = prog.toJani(input.preprocessedProperties.is_initialized() ? input.preprocessedProperties.get() : input.properties); janiInput.model = modelAndProperties.first; if (!modelAndProperties.second.empty()) { janiInput.preprocessedProperties = std::move(modelAndProperties.second); } // This can potentially overwrite the settings above, but will not overwrite settings that were explicitly set by the user (e.g. we will not disable bisimulation or disable exact arithmetic) getModelProcessingInformationAutomatic(janiInput, mpi); if (transformedJaniInput) { // We cache the transformation result. *transformedJaniInput = std::move(janiInput); } } } // Check whether these settings are compatible with the provided input. if (input.model) { auto checkCompatibleSettings = [&mpi, &input] { switch (mpi.verificationValueType) { case ModelProcessingInformation::ValueType::Parametric: return storm::utility::canHandle(mpi.engine, input.preprocessedProperties.is_initialized() ? input.preprocessedProperties.get() : input.properties, input.model.get()); case ModelProcessingInformation::ValueType::Exact: return storm::utility::canHandle(mpi.engine, input.preprocessedProperties.is_initialized() ? input.preprocessedProperties.get() : input.properties, input.model.get()); break; case ModelProcessingInformation::ValueType::FinitePrecision: return storm::utility::canHandle(mpi.engine, input.preprocessedProperties.is_initialized() ? input.preprocessedProperties.get() : input.properties, input.model.get()); } return false; }; mpi.isCompatible = checkCompatibleSettings(); if (!mpi.isCompatible) { if (useAutomatic) { bool useExact = mpi.verificationValueType != ModelProcessingInformation::ValueType::FinitePrecision; STORM_LOG_WARN("The settings picked by the automatic engine (engine=" << mpi.engine << ", bisim=" << mpi.applyBisimulation << ", exact=" << useExact << ") are incompatible with this model. Falling back to default settings."); mpi.engine = storm::utility::Engine::Sparse; mpi.applyBisimulation = false; mpi.verificationValueType = originalVerificationValueType; // Retry check with new settings mpi.isCompatible = checkCompatibleSettings(); } } } else { // If there is no input model, nothing has to be done, actually mpi.isCompatible = true; } // Set whether a transformation to jani is required or necessary mpi.transformToJani = ioSettings.isPrismToJaniSet(); if (input.model) { auto builderType = storm::utility::getBuilderType(mpi.engine); bool transformToJaniForJit = builderType == storm::builder::BuilderType::Jit; STORM_LOG_WARN_COND(mpi.transformToJani || !transformToJaniForJit, "The JIT-based model builder is only available for JANI models, automatically converting the PRISM input model."); bool transformToJaniForDdMA = (builderType == storm::builder::BuilderType::Dd) && (input.model->getModelType() == storm::storage::SymbolicModelDescription::ModelType::MA) && (!input.model->isJaniModel()); STORM_LOG_WARN_COND(mpi.transformToJani || !transformToJaniForDdMA, "Dd-based model builder for Markov Automata is only available for JANI models, automatically converting the input model."); mpi.transformToJani |= (transformToJaniForJit || transformToJaniForDdMA); } // Set the Valuetype used during model building mpi.buildValueType = mpi.verificationValueType; if (bisimulationSettings.useExactArithmeticInDdBisimulation()) { if (storm::utility::getBuilderType(mpi.engine) == storm::builder::BuilderType::Dd && mpi.applyBisimulation) { if (mpi.buildValueType == ModelProcessingInformation::ValueType::FinitePrecision) { mpi.buildValueType = ModelProcessingInformation::ValueType::Exact; } } else { STORM_LOG_WARN("Requested using exact arithmetic in Dd bisimulation but no dd bisimulation is applied."); } } // Set the Dd library mpi.ddType = coreSettings.getDdLibraryType(); if (mpi.ddType == storm::dd::DdType::CUDD && coreSettings.isDdLibraryTypeSetFromDefaultValue()) { if (!(mpi.buildValueType == ModelProcessingInformation::ValueType::FinitePrecision && mpi.verificationValueType == ModelProcessingInformation::ValueType::FinitePrecision)) { STORM_LOG_INFO("Switching to DD library sylvan to allow for rational arithmetic."); mpi.ddType = storm::dd::DdType::Sylvan; } } return mpi; } void ensureNoUndefinedPropertyConstants(std::vector const& properties) { // Make sure there are no undefined constants remaining in any property. for (auto const& property : properties) { std::set usedUndefinedConstants = property.getUndefinedConstants(); if (!usedUndefinedConstants.empty()) { std::vector undefinedConstantsNames; for (auto const& constant : usedUndefinedConstants) { undefinedConstantsNames.emplace_back(constant.getName()); } STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "The property '" << property << " still refers to the undefined constants " << boost::algorithm::join(undefinedConstantsNames, ",") << "."); } } } std::pair preprocessSymbolicInput(SymbolicInput const& input) { auto ioSettings = storm::settings::getModule(); SymbolicInput output = input; // Preprocess properties (if requested) if (ioSettings.isPropertiesAsMultiSet()) { STORM_LOG_THROW(!input.properties.empty(), storm::exceptions::InvalidArgumentException, "Can not translate properties to multi-objective formula because no properties were specified."); output.properties = {storm::api::createMultiObjectiveProperty(output.properties)}; } // Substitute constant definitions in symbolic input. std::string constantDefinitionString = ioSettings.getConstantDefinitionString(); std::map constantDefinitions; if (output.model) { constantDefinitions = output.model.get().parseConstantDefinitions(constantDefinitionString); output.model = output.model.get().preprocess(constantDefinitions); } if (!output.properties.empty()) { output.properties = storm::api::substituteConstantsInProperties(output.properties, constantDefinitions); } ensureNoUndefinedPropertyConstants(output.properties); auto transformedJani = std::make_shared(); ModelProcessingInformation mpi = getModelProcessingInformation(output, transformedJani); // Check whether conversion for PRISM to JANI is requested or necessary. if (output.model && output.model.get().isPrismProgram()) { if (mpi.transformToJani) { if (transformedJani->model) { // Use the cached transformation if possible output = std::move(*transformedJani); } else { storm::prism::Program const& model = output.model.get().asPrismProgram(); auto modelAndProperties = model.toJani(output.properties); output.model = modelAndProperties.first; if (!modelAndProperties.second.empty()) { output.preprocessedProperties = std::move(modelAndProperties.second); } } } } if (output.model && output.model.get().isJaniModel()) { storm::jani::ModelFeatures supportedFeatures = storm::api::getSupportedJaniFeatures(storm::utility::getBuilderType(mpi.engine)); storm::api::simplifyJaniModel(output.model.get().asJaniModel(), output.properties, supportedFeatures); } return {output, mpi}; } void exportSymbolicInput(SymbolicInput const& input) { auto ioSettings = storm::settings::getModule(); if (input.model && input.model.get().isJaniModel()) { storm::storage::SymbolicModelDescription const& model = input.model.get(); if (ioSettings.isExportJaniDotSet()) { storm::api::exportJaniModelAsDot(model.asJaniModel(), ioSettings.getExportJaniDotFilename()); } } } std::vector> createFormulasToRespect(std::vector const& properties) { std::vector> result = storm::api::extractFormulasFromProperties(properties); for (auto const& property : properties) { if (!property.getFilter().getStatesFormula()->isInitialFormula()) { result.push_back(property.getFilter().getStatesFormula()); } } return result; } template std::shared_ptr buildModelDd(SymbolicInput const& input) { auto buildSettings = storm::settings::getModule(); return storm::api::buildSymbolicModel(input.model.get(), createFormulasToRespect(input.properties), buildSettings.isBuildFullModelSet(), !buildSettings.isApplyNoMaximumProgressAssumptionSet()); } template std::shared_ptr buildModelSparse(SymbolicInput const& input, storm::settings::modules::BuildSettings const& buildSettings, bool useJit) { storm::builder::BuilderOptions options(createFormulasToRespect(input.properties), input.model.get()); options.setBuildChoiceLabels(options.isBuildChoiceLabelsSet() || buildSettings.isBuildChoiceLabelsSet()); options.setBuildStateValuations(options.isBuildStateValuationsSet() || buildSettings.isBuildStateValuationsSet()); options.setBuildAllLabels(options.isBuildAllLabelsSet() || buildSettings.isBuildAllLabelsSet()); bool buildChoiceOrigins = options.isBuildChoiceOriginsSet() || buildSettings.isBuildChoiceOriginsSet(); if (storm::settings::manager().hasModule(storm::settings::modules::CounterexampleGeneratorSettings::moduleName)) { auto counterexampleGeneratorSettings = storm::settings::getModule(); if (counterexampleGeneratorSettings.isCounterexampleSet()) { buildChoiceOrigins |= counterexampleGeneratorSettings.isMinimalCommandSetGenerationSet(); } } options.setBuildChoiceOrigins(buildChoiceOrigins); if (buildSettings.isApplyNoMaximumProgressAssumptionSet()) { options.setApplyMaximalProgressAssumption(false); } if (buildSettings.isExplorationChecksSet()) { options.setExplorationChecks(); } options.setReservedBitsForUnboundedVariables(buildSettings.getBitsForUnboundedVariables()); options.setAddOutOfBoundsState(buildSettings.isBuildOutOfBoundsStateSet()); if (buildSettings.isBuildFullModelSet()) { options.clearTerminalStates(); options.setApplyMaximalProgressAssumption(false); options.setBuildAllLabels(true); options.setBuildAllRewardModels(true); } if (buildSettings.isAddOverlappingGuardsLabelSet()) { options.setAddOverlappingGuardsLabel(true); } return storm::api::buildSparseModel(input.model.get(), options, useJit, storm::settings::getModule().isDoctorSet()); } template std::shared_ptr buildModelExplicit(storm::settings::modules::IOSettings const& ioSettings, storm::settings::modules::BuildSettings const& buildSettings) { std::shared_ptr result; if (ioSettings.isExplicitSet()) { result = storm::api::buildExplicitModel(ioSettings.getTransitionFilename(), ioSettings.getLabelingFilename(), ioSettings.isStateRewardsSet() ? boost::optional(ioSettings.getStateRewardsFilename()) : boost::none, ioSettings.isTransitionRewardsSet() ? boost::optional(ioSettings.getTransitionRewardsFilename()) : boost::none, ioSettings.isChoiceLabelingSet() ? boost::optional(ioSettings.getChoiceLabelingFilename()) : boost::none); } else if (ioSettings.isExplicitDRNSet()) { storm::parser::DirectEncodingParserOptions options; options.buildChoiceLabeling = buildSettings.isBuildChoiceLabelsSet(); result = storm::api::buildExplicitDRNModel(ioSettings.getExplicitDRNFilename(), options); } else { STORM_LOG_THROW(ioSettings.isExplicitIMCASet(), storm::exceptions::InvalidSettingsException, "Unexpected explicit model input type."); result = storm::api::buildExplicitIMCAModel(ioSettings.getExplicitIMCAFilename()); } return result; } template std::shared_ptr buildModel(SymbolicInput const& input, storm::settings::modules::IOSettings const& ioSettings, ModelProcessingInformation const& mpi) { storm::utility::Stopwatch modelBuildingWatch(true); auto buildSettings = storm::settings::getModule(); std::shared_ptr result; if (input.model) { auto builderType = storm::utility::getBuilderType(mpi.engine); if (builderType == storm::builder::BuilderType::Dd) { result = buildModelDd(input); } else if (builderType == storm::builder::BuilderType::Explicit || builderType == storm::builder::BuilderType::Jit) { result = buildModelSparse(input, buildSettings, builderType == storm::builder::BuilderType::Jit); } } else if (ioSettings.isExplicitSet() || ioSettings.isExplicitDRNSet() || ioSettings.isExplicitIMCASet()) { STORM_LOG_THROW(mpi.engine == storm::utility::Engine::Sparse, storm::exceptions::InvalidSettingsException, "Can only use sparse engine with explicit input."); result = buildModelExplicit(ioSettings, buildSettings); } modelBuildingWatch.stop(); if (result) { STORM_PRINT("Time for model construction: " << modelBuildingWatch << "." << std::endl << std::endl); } return result; } template std::shared_ptr> preprocessSparseMarkovAutomaton(std::shared_ptr> const& model) { auto transformationSettings = storm::settings::getModule(); auto debugSettings = storm::settings::getModule(); std::shared_ptr> result = model; model->close(); STORM_LOG_WARN_COND(!debugSettings.isAdditionalChecksSet() || !model->containsZenoCycle(), "MA contains a Zeno cycle. Model checking results cannot be trusted."); if (model->isConvertibleToCtmc()) { STORM_LOG_WARN_COND(false, "MA is convertible to a CTMC, consider using a CTMC instead."); result = model->convertToCtmc(); } if (transformationSettings.isChainEliminationSet() && result->isOfType(storm::models::ModelType::MarkovAutomaton)) { result = storm::transformer::NonMarkovianChainTransformer::eliminateNonmarkovianStates( result->template as>(), transformationSettings.getLabelBehavior()); } return result; } template std::shared_ptr> preprocessSparseModelBisimulation(std::shared_ptr> const& model, SymbolicInput const& input, storm::settings::modules::BisimulationSettings const& bisimulationSettings) { storm::storage::BisimulationType bisimType = storm::storage::BisimulationType::Strong; if (bisimulationSettings.isWeakBisimulationSet()) { bisimType = storm::storage::BisimulationType::Weak; } STORM_LOG_INFO("Performing bisimulation minimization..."); return storm::api::performBisimulationMinimization(model, createFormulasToRespect(input.properties), bisimType); } template std::pair>, bool> preprocessSparseModel(std::shared_ptr> const& model, SymbolicInput const& input, ModelProcessingInformation const& mpi) { auto bisimulationSettings = storm::settings::getModule(); auto ioSettings = storm::settings::getModule(); auto transformationSettings = storm::settings::getModule(); std::pair>, bool> result = std::make_pair(model, false); if (result.first->isOfType(storm::models::ModelType::MarkovAutomaton)) { result.first = preprocessSparseMarkovAutomaton(result.first->template as>()); result.second = true; } if (mpi.applyBisimulation) { result.first = preprocessSparseModelBisimulation(result.first, input, bisimulationSettings); result.second = true; } if (transformationSettings.isToDiscreteTimeModelSet()) { // TODO: we should also transform the properties at this point. STORM_LOG_WARN_COND(!model->hasRewardModel("_time"), "Scheduled transformation to discrete time model, but a reward model named '_time' is already present in this model. We might take the wrong reward model later."); result.first = storm::api::transformContinuousToDiscreteTimeSparseModel(std::move(*result.first), storm::api::extractFormulasFromProperties(input.properties)).first; result.second = true; } if (transformationSettings.isToNondeterministicModelSet()) { result.first = storm::api::transformToNondeterministicModel(std::move(*result.first)); result.second = true; } return result; } template void exportSparseModel(std::shared_ptr> const& model, SymbolicInput const& input) { auto ioSettings = storm::settings::getModule(); if (ioSettings.isExportExplicitSet()) { storm::api::exportSparseModelAsDrn(model, ioSettings.getExportExplicitFilename(), input.model ? input.model.get().getParameterNames() : std::vector(), !ioSettings.isExplicitExportPlaceholdersDisabled()); } if (ioSettings.isExportDdSet()) { STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Exporting in drdd format is only supported for DDs."); } if (ioSettings.isExportDotSet()) { storm::api::exportSparseModelAsDot(model, ioSettings.getExportDotFilename(), ioSettings.getExportDotMaxWidth()); } } template void exportDdModel(std::shared_ptr> const& model, SymbolicInput const& input) { auto ioSettings = storm::settings::getModule(); if (ioSettings.isExportExplicitSet()) { STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Exporting in drn format is only supported for sparse models."); } if (ioSettings.isExportDdSet()) { storm::api::exportSparseModelAsDrdd(model, ioSettings.getExportDdFilename()); } if (ioSettings.isExportDotSet()) { storm::api::exportSymbolicModelAsDot(model, ioSettings.getExportDotFilename()); } } template void exportModel(std::shared_ptr const& model, SymbolicInput const& input) { if (model->isSparseModel()) { exportSparseModel(model->as>(), input); } else { exportDdModel(model->as>(), input); } } template typename std::enable_if::value, std::shared_ptr>>::type preprocessDdMarkovAutomaton(std::shared_ptr> const& model) { return model; } template typename std::enable_if::value, std::shared_ptr>>::type preprocessDdMarkovAutomaton(std::shared_ptr> const& model) { auto ma = model->template as>(); if (!ma->isClosed()) { return std::make_shared>(ma->close()); } else { return model; } } template std::shared_ptr> preprocessDdModelBisimulation(std::shared_ptr> const& model, SymbolicInput const& input, storm::settings::modules::BisimulationSettings const& bisimulationSettings, ModelProcessingInformation const& mpi) { STORM_LOG_WARN_COND(!bisimulationSettings.isWeakBisimulationSet(), "Weak bisimulation is currently not supported on DDs. Falling back to strong bisimulation."); auto quotientFormat = bisimulationSettings.getQuotientFormat(); if (mpi.engine == storm::utility::Engine::DdSparse && quotientFormat != storm::dd::bisimulation::QuotientFormat::Sparse && bisimulationSettings.isQuotientFormatSetFromDefaultValue()) { STORM_LOG_INFO("Setting bisimulation quotient format to 'sparse'."); quotientFormat = storm::dd::bisimulation::QuotientFormat::Sparse; } STORM_LOG_INFO("Performing bisimulation minimization..."); return storm::api::performBisimulationMinimization(model, createFormulasToRespect(input.properties), storm::storage::BisimulationType::Strong, bisimulationSettings.getSignatureMode(), quotientFormat); } template std::pair, bool> preprocessDdModel(std::shared_ptr> const& model, SymbolicInput const& input, ModelProcessingInformation const& mpi) { auto bisimulationSettings = storm::settings::getModule(); std::pair>, bool> intermediateResult = std::make_pair(model, false); if (model->isOfType(storm::models::ModelType::MarkovAutomaton)) { intermediateResult.first = preprocessDdMarkovAutomaton(intermediateResult.first->template as>()); intermediateResult.second = true; } std::unique_ptr>, bool>> result; auto symbolicModel = intermediateResult.first->template as>(); if (mpi.applyBisimulation) { std::shared_ptr> newModel = preprocessDdModelBisimulation(symbolicModel, input, bisimulationSettings, mpi); result = std::make_unique>, bool>>(newModel, true); } else { result = std::make_unique>, bool>>(symbolicModel->template toValueType(), !std::is_same::value); } if (result && result->first->isSymbolicModel() && mpi.engine == storm::utility::Engine::DdSparse) { // Mark as changed. result->second = true; std::shared_ptr> symbolicModel = result->first->template as>(); std::vector> formulas; for (auto const& property : input.properties) { formulas.emplace_back(property.getRawFormula()); } result->first = storm::api::transformSymbolicToSparseModel(symbolicModel, formulas); STORM_LOG_THROW(result, storm::exceptions::NotSupportedException, "The translation to a sparse model is not supported for the given model type."); } return *result; } template std::pair, bool> preprocessModel(std::shared_ptr const& model, SymbolicInput const& input, ModelProcessingInformation const& mpi) { storm::utility::Stopwatch preprocessingWatch(true); std::pair, bool> result = std::make_pair(model, false); if (model->isSparseModel()) { result = preprocessSparseModel(result.first->as>(), input, mpi); } else { STORM_LOG_ASSERT(model->isSymbolicModel(), "Unexpected model type."); result = preprocessDdModel(result.first->as>(), input, mpi); } preprocessingWatch.stop(); if (result.second) { STORM_PRINT(std::endl << "Time for model preprocessing: " << preprocessingWatch << "." << std::endl << std::endl); } return result; } void printComputingCounterexample(storm::jani::Property const& property) { STORM_PRINT("Computing counterexample for property " << *property.getRawFormula() << " ..." << std::endl); } void printCounterexample(std::shared_ptr const& counterexample, storm::utility::Stopwatch* watch = nullptr) { if (counterexample) { STORM_PRINT(*counterexample << std::endl); if (watch) { STORM_PRINT("Time for computation: " << *watch << "." << std::endl); } } else { STORM_PRINT(" failed." << std::endl); } } template void generateCounterexamples(std::shared_ptr const& model, SymbolicInput const& input) { STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Counterexample generation is not supported for this data-type."); } template <> void generateCounterexamples(std::shared_ptr const& model, SymbolicInput const& input) { typedef double ValueType; STORM_LOG_THROW(model->isSparseModel(), storm::exceptions::NotSupportedException, "Counterexample generation is currently only supported for sparse models."); auto sparseModel = model->as>(); for (auto& rewModel : sparseModel->getRewardModels()) { rewModel.second.reduceToStateBasedRewards(sparseModel->getTransitionMatrix(), true); } STORM_LOG_THROW(sparseModel->isOfType(storm::models::ModelType::Dtmc) || sparseModel->isOfType(storm::models::ModelType::Mdp), storm::exceptions::NotSupportedException, "Counterexample is currently only supported for discrete-time models."); auto counterexampleSettings = storm::settings::getModule(); if (counterexampleSettings.isMinimalCommandSetGenerationSet()) { bool useMilp = counterexampleSettings.isUseMilpBasedMinimalCommandSetGenerationSet(); for (auto const& property : input.properties) { std::shared_ptr counterexample; printComputingCounterexample(property); storm::utility::Stopwatch watch(true); if (useMilp) { STORM_LOG_THROW(sparseModel->isOfType(storm::models::ModelType::Mdp), storm::exceptions::NotSupportedException, "Counterexample generation using MILP is currently only supported for MDPs."); counterexample = storm::api::computeHighLevelCounterexampleMilp(input.model.get(), sparseModel->template as>(), property.getRawFormula()); } else { STORM_LOG_THROW(sparseModel->isOfType(storm::models::ModelType::Dtmc) || sparseModel->isOfType(storm::models::ModelType::Mdp), storm::exceptions::NotSupportedException, "Counterexample generation using MaxSAT is currently only supported for discrete-time models."); if (sparseModel->isOfType(storm::models::ModelType::Dtmc)) { counterexample = storm::api::computeHighLevelCounterexampleMaxSmt(input.model.get(), sparseModel->template as>(), property.getRawFormula()); } else { counterexample = storm::api::computeHighLevelCounterexampleMaxSmt(input.model.get(), sparseModel->template as>(), property.getRawFormula()); } } watch.stop(); printCounterexample(counterexample, &watch); } } else if (counterexampleSettings.isShortestPathGenerationSet()) { for (auto const& property : input.properties) { std::shared_ptr counterexample; printComputingCounterexample(property); storm::utility::Stopwatch watch(true); STORM_LOG_THROW(sparseModel->isOfType(storm::models::ModelType::Dtmc), storm::exceptions::NotSupportedException, "Counterexample generation using shortest paths is currently only supported for DTMCs."); counterexample = storm::api::computeKShortestPathCounterexample(sparseModel->template as>(), property.getRawFormula(), counterexampleSettings.getShortestPathMaxK()); watch.stop(); printCounterexample(counterexample, &watch); } } else { STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "The selected counterexample formalism is unsupported."); } } template void printFilteredResult(std::unique_ptr const& result, storm::modelchecker::FilterType ft) { if (result->isQuantitative()) { if (ft == storm::modelchecker::FilterType::VALUES) { STORM_PRINT(*result); } else { ValueType resultValue; switch (ft) { case storm::modelchecker::FilterType::SUM: resultValue = result->asQuantitativeCheckResult().sum(); break; case storm::modelchecker::FilterType::AVG: resultValue = result->asQuantitativeCheckResult().average(); break; case storm::modelchecker::FilterType::MIN: resultValue = result->asQuantitativeCheckResult().getMin(); break; case storm::modelchecker::FilterType::MAX: resultValue = result->asQuantitativeCheckResult().getMax(); break; case storm::modelchecker::FilterType::ARGMIN: case storm::modelchecker::FilterType::ARGMAX: STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Outputting states is not supported."); case storm::modelchecker::FilterType::EXISTS: case storm::modelchecker::FilterType::FORALL: case storm::modelchecker::FilterType::COUNT: STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Filter type only defined for qualitative results."); default: STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Unhandled filter type."); } if (storm::NumberTraits::IsExact && storm::utility::isConstant(resultValue)) { STORM_PRINT(resultValue << " (approx. " << storm::utility::convertNumber(resultValue) << ")"); } else { STORM_PRINT(resultValue); } } } else { switch (ft) { case storm::modelchecker::FilterType::VALUES: STORM_PRINT(*result << std::endl); break; case storm::modelchecker::FilterType::EXISTS: STORM_PRINT(result->asQualitativeCheckResult().existsTrue()); break; case storm::modelchecker::FilterType::FORALL: STORM_PRINT(result->asQualitativeCheckResult().forallTrue()); break; case storm::modelchecker::FilterType::COUNT: STORM_PRINT(result->asQualitativeCheckResult().count()); break; case storm::modelchecker::FilterType::ARGMIN: case storm::modelchecker::FilterType::ARGMAX: STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Outputting states is not supported."); case storm::modelchecker::FilterType::SUM: case storm::modelchecker::FilterType::AVG: case storm::modelchecker::FilterType::MIN: case storm::modelchecker::FilterType::MAX: STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Filter type only defined for quantitative results."); } } STORM_PRINT(std::endl); } void printModelCheckingProperty(storm::jani::Property const& property) { STORM_PRINT(std::endl << "Model checking property \"" << property.getName() << "\": " << *property.getRawFormula() << " ..." << std::endl); } template void printResult(std::unique_ptr const& result, storm::jani::Property const& property, storm::utility::Stopwatch* watch = nullptr) { if (result) { std::stringstream ss; ss << "'" << *property.getFilter().getStatesFormula() << "'"; STORM_PRINT((storm::utility::resources::isTerminate() ? "Result till abort" : "Result") << " (for " << (property.getFilter().getStatesFormula()->isInitialFormula() ? "initial" : ss.str()) << " states): "); printFilteredResult(result, property.getFilter().getFilterType()); if (watch) { STORM_PRINT("Time for model checking: " << *watch << "." << std::endl); } } else { STORM_LOG_ERROR("Property is unsupported by selected engine/settings." << std::endl); } } struct PostprocessingIdentity { void operator()(std::unique_ptr const&) { // Intentionally left empty. } }; template void verifyProperties(SymbolicInput const& input, std::function(std::shared_ptr const& formula, std::shared_ptr const& states)> const& verificationCallback, std::function const&)> const& postprocessingCallback = PostprocessingIdentity()) { auto transformationSettings = storm::settings::getModule(); auto const& properties = input.preprocessedProperties ? input.preprocessedProperties.get() : input.properties; for (auto const& property : properties) { printModelCheckingProperty(property); bool ignored = false; storm::utility::Stopwatch watch(true); std::unique_ptr result; try { auto rawFormula = property.getRawFormula(); if (transformationSettings.isChainEliminationSet() && !storm::transformer::NonMarkovianChainTransformer::preservesFormula(*rawFormula)) { STORM_LOG_WARN("Property is not preserved by elimination of non-markovian states."); ignored = true; } else if (transformationSettings.isToDiscreteTimeModelSet()) { auto propertyFormula = storm::api::checkAndTransformContinuousToDiscreteTimeFormula(*property.getRawFormula()); auto filterFormula = storm::api::checkAndTransformContinuousToDiscreteTimeFormula(*property.getFilter().getStatesFormula()); if (propertyFormula && filterFormula) { result = verificationCallback(propertyFormula, filterFormula); } else { ignored = true; } } else { result = verificationCallback(property.getRawFormula(), property.getFilter().getStatesFormula()); } } catch (storm::exceptions::BaseException const& ex) { STORM_LOG_WARN("Cannot handle property: " << ex.what()); } watch.stop(); if (!ignored) { postprocessingCallback(result); printResult(result, property, &watch); } } } std::vector parseConstraints(storm::expressions::ExpressionManager const& expressionManager, std::string const& constraintsString) { std::vector constraints; std::vector constraintsAsStrings; boost::split(constraintsAsStrings, constraintsString, boost::is_any_of(",")); storm::parser::ExpressionParser expressionParser(expressionManager); std::unordered_map variableMapping; for (auto const& variableTypePair : expressionManager) { variableMapping[variableTypePair.first.getName()] = variableTypePair.first; } expressionParser.setIdentifierMapping(variableMapping); for (auto const& constraintString : constraintsAsStrings) { if (constraintString.empty()) { continue; } storm::expressions::Expression constraint = expressionParser.parseFromString(constraintString); STORM_LOG_TRACE("Adding special (user-provided) constraint " << constraint << "."); constraints.emplace_back(constraint); } return constraints; } std::vector> parseInjectedRefinementPredicates(storm::expressions::ExpressionManager const& expressionManager, std::string const& refinementPredicatesString) { std::vector> injectedRefinementPredicates; storm::parser::ExpressionParser expressionParser(expressionManager); std::unordered_map variableMapping; for (auto const& variableTypePair : expressionManager) { variableMapping[variableTypePair.first.getName()] = variableTypePair.first; } expressionParser.setIdentifierMapping(variableMapping); std::vector predicateGroupsAsStrings; boost::split(predicateGroupsAsStrings, refinementPredicatesString, boost::is_any_of(";")); if (!predicateGroupsAsStrings.empty()) { for (auto const& predicateGroupString : predicateGroupsAsStrings) { if (predicateGroupString.empty()) { continue; } std::vector predicatesAsStrings; boost::split(predicatesAsStrings, predicateGroupString, boost::is_any_of(":")); if (!predicatesAsStrings.empty()) { injectedRefinementPredicates.emplace_back(); for (auto const& predicateString : predicatesAsStrings) { storm::expressions::Expression predicate = expressionParser.parseFromString(predicateString); STORM_LOG_TRACE("Adding special (user-provided) refinement predicate " << predicateString << "."); injectedRefinementPredicates.back().emplace_back(predicate); } STORM_LOG_THROW(!injectedRefinementPredicates.back().empty(), storm::exceptions::InvalidArgumentException, "Expecting non-empty list of predicates to inject for each (mentioned) refinement step."); // Finally reverse the list, because we take the predicates from the back. std::reverse(injectedRefinementPredicates.back().begin(), injectedRefinementPredicates.back().end()); } } // Finally reverse the list, because we take the predicates from the back. std::reverse(injectedRefinementPredicates.begin(), injectedRefinementPredicates.end()); } return injectedRefinementPredicates; } template void verifyWithAbstractionRefinementEngine(SymbolicInput const& input, ModelProcessingInformation const& mpi) { STORM_LOG_ASSERT(input.model, "Expected symbolic model description."); storm::settings::modules::AbstractionSettings const& abstractionSettings = storm::settings::getModule(); storm::api::AbstractionRefinementOptions options(parseConstraints(input.model->getManager(), abstractionSettings.getConstraintString()), parseInjectedRefinementPredicates(input.model->getManager(), abstractionSettings.getInjectedRefinementPredicates())); verifyProperties(input, [&input,&options,&mpi] (std::shared_ptr const& formula, std::shared_ptr const& states) { STORM_LOG_THROW(states->isInitialFormula(), storm::exceptions::NotSupportedException, "Abstraction-refinement can only filter initial states."); return storm::api::verifyWithAbstractionRefinementEngine(mpi.env, input.model.get(), storm::api::createTask(formula, true), options); }); } template void verifyWithExplorationEngine(SymbolicInput const& input, ModelProcessingInformation const& mpi) { STORM_LOG_ASSERT(input.model, "Expected symbolic model description."); STORM_LOG_THROW((std::is_same::value), storm::exceptions::NotSupportedException, "Exploration does not support other data-types than floating points."); verifyProperties(input, [&input,&mpi] (std::shared_ptr const& formula, std::shared_ptr const& states) { STORM_LOG_THROW(states->isInitialFormula(), storm::exceptions::NotSupportedException, "Exploration can only filter initial states."); return storm::api::verifyWithExplorationEngine(mpi.env, input.model.get(), storm::api::createTask(formula, true)); }); } template void verifyWithSparseEngine(std::shared_ptr const& model, SymbolicInput const& input, ModelProcessingInformation const& mpi) { auto sparseModel = model->as>(); auto const& ioSettings = storm::settings::getModule(); auto verificationCallback = [&sparseModel,&ioSettings,&mpi] (std::shared_ptr const& formula, std::shared_ptr const& states) { bool filterForInitialStates = states->isInitialFormula(); auto task = storm::api::createTask(formula, filterForInitialStates); if (ioSettings.isExportSchedulerSet()) { task.setProduceSchedulers(true); } std::unique_ptr result = storm::api::verifyWithSparseEngine(mpi.env, sparseModel, task); std::unique_ptr filter; if (filterForInitialStates) { filter = std::make_unique(sparseModel->getInitialStates()); } else { filter = storm::api::verifyWithSparseEngine(mpi.env, sparseModel, storm::api::createTask(states, false)); } if (result && filter) { result->filter(filter->asQualitativeCheckResult()); } return result; }; uint64_t exportCount = 0; // this number will be prepended to the export file name of schedulers and/or check results in case of multiple properties. auto postprocessingCallback = [&sparseModel,&ioSettings,&input,&exportCount] (std::unique_ptr const& result) { if (ioSettings.isExportSchedulerSet()) { if (result->isExplicitQuantitativeCheckResult()) { if (result->template asExplicitQuantitativeCheckResult().hasScheduler()) { auto const& scheduler = result->template asExplicitQuantitativeCheckResult().getScheduler(); STORM_PRINT_AND_LOG("Exporting scheduler ... ") if (input.model) { STORM_LOG_WARN_COND(sparseModel->hasStateValuations(), "No information of state valuations available. The scheduler output will use internal state ids. You might be interested in building the model with state valuations using --buildstateval."); STORM_LOG_WARN_COND(sparseModel->hasChoiceLabeling() || sparseModel->hasChoiceOrigins(), "No symbolic choice information is available. The scheduler output will use internal choice ids. You might be interested in building the model with choice labels or choice origins using --buildchoicelab or --buildchoiceorig."); STORM_LOG_WARN_COND(sparseModel->hasChoiceLabeling() && !sparseModel->hasChoiceOrigins(), "Only partial choice information is available. You might want to build the model with choice origins using --buildchoicelab or --buildchoiceorig."); } STORM_LOG_WARN_COND(exportCount == 0, "Prepending " << exportCount << " to file name for this property because there are multiple properties."); storm::api::exportScheduler(sparseModel, scheduler, (exportCount == 0 ? std::string("") : std::to_string(exportCount)) + ioSettings.getExportSchedulerFilename()); } else { STORM_LOG_ERROR("Scheduler requested but could not be generated."); } } else { STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Scheduler export not supported for this property."); } } if (ioSettings.isExportCheckResultSet()) { STORM_LOG_WARN_COND(sparseModel->hasStateValuations(), "No information of state valuations available. The result output will use internal state ids. You might be interested in building the model with state valuations using --buildstateval."); STORM_LOG_WARN_COND(exportCount == 0, "Prepending " << exportCount << " to file name for this property because there are multiple properties."); storm::api::exportCheckResultToJson(sparseModel, result, (exportCount == 0 ? std::string("") : std::to_string(exportCount)) + ioSettings.getExportCheckResultFilename()); } ++exportCount; }; verifyProperties(input,verificationCallback, postprocessingCallback); if (ioSettings.isComputeSteadyStateDistributionSet()) { storm::utility::Stopwatch watch(true); std::unique_ptr result; try { result = storm::api::computeSteadyStateDistributionWithSparseEngine(mpi.env, sparseModel); } catch (storm::exceptions::BaseException const& ex) { STORM_LOG_WARN("Cannot compute steady-state probabilities: " << ex.what()); } watch.stop(); postprocessingCallback(result); STORM_PRINT((storm::utility::resources::isTerminate() ? "Result till abort: " : "Result: ") << *result << std::endl); STORM_PRINT("Time for model checking: " << watch << "." << std::endl); } } template void verifyWithHybridEngine(std::shared_ptr const& model, SymbolicInput const& input, ModelProcessingInformation const& mpi) { verifyProperties(input, [&model,&mpi] (std::shared_ptr const& formula, std::shared_ptr const& states) { bool filterForInitialStates = states->isInitialFormula(); auto task = storm::api::createTask(formula, filterForInitialStates); auto symbolicModel = model->as>(); std::unique_ptr result = storm::api::verifyWithHybridEngine(mpi.env, symbolicModel, task); std::unique_ptr filter; if (filterForInitialStates) { filter = std::make_unique>(symbolicModel->getReachableStates(), symbolicModel->getInitialStates()); } else { filter = storm::api::verifyWithHybridEngine(mpi.env, symbolicModel, storm::api::createTask(states, false)); } if (result && filter) { result->filter(filter->asQualitativeCheckResult()); } return result; }); } template void verifyWithDdEngine(std::shared_ptr const& model, SymbolicInput const& input, ModelProcessingInformation const& mpi) { verifyProperties(input, [&model,&mpi] (std::shared_ptr const& formula, std::shared_ptr const& states) { bool filterForInitialStates = states->isInitialFormula(); auto task = storm::api::createTask(formula, filterForInitialStates); auto symbolicModel = model->as>(); std::unique_ptr result = storm::api::verifyWithDdEngine(mpi.env, symbolicModel, storm::api::createTask(formula, true)); std::unique_ptr filter; if (filterForInitialStates) { filter = std::make_unique>(symbolicModel->getReachableStates(), symbolicModel->getInitialStates()); } else { filter = storm::api::verifyWithDdEngine(mpi.env, symbolicModel, storm::api::createTask(states, false)); } if (result && filter) { result->filter(filter->asQualitativeCheckResult()); } return result; }); } template void verifyWithAbstractionRefinementEngine(std::shared_ptr const& model, SymbolicInput const& input, ModelProcessingInformation const& mpi) { verifyProperties(input, [&model,&mpi] (std::shared_ptr const& formula, std::shared_ptr const& states) { STORM_LOG_THROW(states->isInitialFormula(), storm::exceptions::NotSupportedException, "Abstraction-refinement can only filter initial states."); auto symbolicModel = model->as>(); return storm::api::verifyWithAbstractionRefinementEngine(mpi.env, symbolicModel, storm::api::createTask(formula, true)); }); } template typename std::enable_if::value, void>::type verifySymbolicModel(std::shared_ptr const& model, SymbolicInput const& input, ModelProcessingInformation const& mpi) { if (mpi.engine == storm::utility::Engine::Hybrid) { verifyWithHybridEngine(model, input, mpi); } else if (mpi.engine == storm::utility::Engine::Dd) { verifyWithDdEngine(model, input, mpi); } else { verifyWithAbstractionRefinementEngine(model, input, mpi); } } template typename std::enable_if::value, void>::type verifySymbolicModel(std::shared_ptr const&, SymbolicInput const&, ModelProcessingInformation const&) { STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "CUDD does not support the selected data-type."); } template void verifyModel(std::shared_ptr const& model, SymbolicInput const& input, ModelProcessingInformation const& mpi) { if (model->isSparseModel()) { verifyWithSparseEngine(model, input, mpi); } else { STORM_LOG_ASSERT(model->isSymbolicModel(), "Unexpected model type."); verifySymbolicModel(model, input, mpi); } } template std::shared_ptr buildPreprocessModelWithValueTypeAndDdlib(SymbolicInput const& input, ModelProcessingInformation const& mpi) { auto ioSettings = storm::settings::getModule(); auto buildSettings = storm::settings::getModule(); std::shared_ptr model; if (!buildSettings.isNoBuildModelSet()) { model = buildModel(input, ioSettings, mpi); } if (model) { model->printModelInformationToStream(std::cout); } STORM_LOG_THROW(model || input.properties.empty(), storm::exceptions::InvalidSettingsException, "No input model."); if (model) { auto preprocessingResult = preprocessModel(model, input, mpi); if (preprocessingResult.second) { model = preprocessingResult.first; model->printModelInformationToStream(std::cout); } } return model; } template std::shared_ptr buildPreprocessExportModelWithValueTypeAndDdlib(SymbolicInput const& input, ModelProcessingInformation const& mpi) { auto model = buildPreprocessModelWithValueTypeAndDdlib(input, mpi); if (model) { exportModel(model, input); } return model; } template void processInputWithValueTypeAndDdlib(SymbolicInput const& input, ModelProcessingInformation const& mpi) { auto abstractionSettings = storm::settings::getModule(); auto counterexampleSettings = storm::settings::getModule(); // For several engines, no model building step is performed, but the verification is started right away. if (mpi.engine == storm::utility::Engine::AbstractionRefinement && abstractionSettings.getAbstractionRefinementMethod() == storm::settings::modules::AbstractionSettings::Method::Games) { verifyWithAbstractionRefinementEngine(input, mpi); } else if (mpi.engine == storm::utility::Engine::Exploration) { verifyWithExplorationEngine(input, mpi); } else { std::shared_ptr model = buildPreprocessExportModelWithValueTypeAndDdlib(input, mpi); if (model) { if (counterexampleSettings.isCounterexampleSet()) { generateCounterexamples(model, input); } else { verifyModel(model, input, mpi); } } } } template void processInputWithValueType(SymbolicInput const& input, ModelProcessingInformation const& mpi) { if (mpi.ddType == storm::dd::DdType::CUDD) { STORM_LOG_ASSERT(mpi.verificationValueType == ModelProcessingInformation::ValueType::FinitePrecision && mpi.buildValueType == ModelProcessingInformation::ValueType::FinitePrecision && (std::is_same::value), "Unexpected value type for Dd library cudd."); processInputWithValueTypeAndDdlib(input, mpi); } else { STORM_LOG_ASSERT(mpi.ddType == storm::dd::DdType::Sylvan, "Unknown DD library."); if (mpi.buildValueType == mpi.verificationValueType) { processInputWithValueTypeAndDdlib(input, mpi); } else { // Right now, we only require (buildType == Exact and verificationType == FinitePrecision). // We exclude all other combinations to safe a few template instantiations. STORM_LOG_THROW((std::is_same::value) && mpi.buildValueType == ModelProcessingInformation::ValueType::Exact, storm::exceptions::InvalidArgumentException, "Unexpected combination of buildValueType and verificationValueType"); #ifdef STORM_HAVE_CARL processInputWithValueTypeAndDdlib(input, mpi); #else STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Unexpected buildValueType."); #endif } } } } }