diff --git a/CHANGELOG.md b/CHANGELOG.md index dc6cf6827..6b4cb0017 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,11 @@ Version 1.4.x - Added option `--build:buildchoiceorig` to build a model (PRISM or JANI) with choice origins (which are exported with, e.g. `--exportscheduler`). - Apply the maximum progress assumption while building a Markov automata with the Dd engine. - Added option `--build:nomaxprog` to disable applying the maximum progress assumption during model building (for Markov Automata) +- Added hybrid engine for Markov Automata +- Implemented optimistic value iteration for sound computations and used it as a default +- Unif+ for time bounded properties on Markov automata now computes results with relative precision. Use `--absolute` for the previous behavior. +- Various performance improvements for model building with the sparse engine +- `storm-dft`: Symmetry reduction is now enabled by default and can be disabled via `--nosymmetryreduction` - `storm-pomdp`: Only accept POMDPs that are canonical - `storm-pomdp`: Prism language extended with observable expressions - `storm-pomdp`: Various fixes that prevented usage. diff --git a/src/storm-cli-utilities/cli.cpp b/src/storm-cli-utilities/cli.cpp index c94c38853..64a2fcd72 100644 --- a/src/storm-cli-utilities/cli.cpp +++ b/src/storm-cli-utilities/cli.cpp @@ -249,10 +249,10 @@ namespace storm { SymbolicInput symbolicInput = parseSymbolicInput(); // Obtain settings for model processing - ModelProcessingInformation mpi = getModelProcessingInformation(symbolicInput); + ModelProcessingInformation mpi; // Preprocess the symbolic input - symbolicInput = preprocessSymbolicInput(symbolicInput, mpi.engine); + std::tie(symbolicInput, mpi) = preprocessSymbolicInput(symbolicInput); // Export symbolic input (if requested) exportSymbolicInput(symbolicInput); diff --git a/src/storm-cli-utilities/model-handling.h b/src/storm-cli-utilities/model-handling.h index 5b8f2031b..68a5f214a 100644 --- a/src/storm-cli-utilities/model-handling.h +++ b/src/storm-cli-utilities/model-handling.h @@ -156,6 +156,9 @@ namespace storm { // 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, @@ -175,16 +178,17 @@ namespace storm { void getModelProcessingInformationPortfolio(SymbolicInput const& input, ModelProcessingInformation& mpi) { auto hints = storm::settings::getModule(); - STORM_LOG_THROW(input.model.is_initialized(), storm::exceptions::InvalidArgumentException, "Portfolio engine requires a symbolic model (PRISM or JANI."); + STORM_LOG_THROW(input.model.is_initialized(), storm::exceptions::InvalidArgumentException, "Portfolio engine requires a JANI input model."); + STORM_LOG_THROW(input.model->isJaniModel(), storm::exceptions::InvalidArgumentException, "Portfolio 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, "Portfolio engine requires a property."); STORM_LOG_WARN_COND(properties.size() == 1, "Portfolio engine does not support decisions based on multiple properties. Only the first property will be considered."); storm::utility::Portfolio pf; if (hints.isNumberStatesSet()) { - pf.predict(input.model.get(), properties.front(), hints.getNumberStates()); + pf.predict(input.model->asJaniModel(), properties.front(), hints.getNumberStates()); } else { - pf.predict(input.model.get(), properties.front()); + pf.predict(input.model->asJaniModel(), properties.front()); } mpi.engine = pf.getEngine(); @@ -194,11 +198,22 @@ namespace storm { if (pf.enableExact() && mpi.verificationValueType == ModelProcessingInformation::ValueType::FinitePrecision) { mpi.verificationValueType = ModelProcessingInformation::ValueType::Exact; } - STORM_PRINT_AND_LOG( "Portfolio engine picked the following settings: " << std::endl << "\tengine=" << mpi.engine << "\t bisimulation=" << mpi.applyBisimulation << "\t exact=" << (mpi.verificationValueType != ModelProcessingInformation::ValueType::FinitePrecision) << std::endl) + STORM_PRINT_AND_LOG( "Portfolio 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); } - ModelProcessingInformation getModelProcessingInformation(SymbolicInput const& input) { + /*! + * 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(); @@ -219,10 +234,29 @@ namespace storm { } // Since the remaining settings could depend on the ones above, we need apply the portfolio engine now. - bool usePortfolio = mpi.engine == storm::utility::Engine::Portfolio; + bool usePortfolio = input.model.is_initialized() && mpi.engine == storm::utility::Engine::Portfolio; if (usePortfolio) { - // 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) - getModelProcessingInformationPortfolio(input, mpi); + 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) + getModelProcessingInformationPortfolio(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) + getModelProcessingInformationPortfolio(janiInput, mpi); + if (transformedJaniInput) { + // We cache the transformation result. + *transformedJaniInput = std::move(janiInput); + } + } } // Check whether these settings are compatible with the provided input. @@ -250,8 +284,16 @@ namespace storm { STORM_LOG_WARN("The model checking query does not seem to be supported for the selected engine. Storm will try to solve the query, but you will most likely get an error for at least one of the provided properties."); } } - + // Set whether a transformation to jani is required or necessary + mpi.transformToJani = ioSettings.isPrismToJaniSet(); + 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); + STORM_LOG_WARN_COND(mpi.transformToJani || !transformToJaniForDdMA, "Dd-based model builder for Markov Automata is only available for JANI models, automatically converting the PRISM input model."); + mpi.transformToJani |= (transformToJaniForJit || transformToJaniForDdMA); + // Set the Valuetype used during model building mpi.buildValueType = mpi.verificationValueType; if (bisimulationSettings.useExactArithmeticInDdBisimulation()) { @@ -289,17 +331,11 @@ namespace storm { } } - SymbolicInput preprocessSymbolicInput(SymbolicInput const& input, storm::utility::Engine const& engine) { + std::pair preprocessSymbolicInput(SymbolicInput const& input) { auto ioSettings = storm::settings::getModule(); - auto builderType = storm::utility::getBuilderType(engine); SymbolicInput output = input; - if (output.model && output.model.get().isJaniModel()) { - storm::jani::ModelFeatures supportedFeatures = storm::api::getSupportedJaniFeatures(builderType); - storm::api::simplifyJaniModel(output.model.get().asJaniModel(), output.properties, supportedFeatures); - } - // Substitute constant definitions in symbolic input. std::string constantDefinitionString = ioSettings.getConstantDefinitionString(); std::map constantDefinitions; @@ -310,34 +346,37 @@ namespace storm { 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); + + auto builderType = storm::utility::getBuilderType(mpi.engine); // Check whether conversion for PRISM to JANI is requested or necessary. - if (input.model && input.model.get().isPrismProgram()) { - bool transformToJani = ioSettings.isPrismToJaniSet(); - bool transformToJaniForJit = builderType == storm::builder::BuilderType::Jit; - STORM_LOG_WARN_COND(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); - STORM_LOG_WARN_COND(transformToJani || !transformToJaniForDdMA, "Dd-based model builder for Markov Automata is only available for JANI models, automatically converting the PRISM input model."); - transformToJani |= (transformToJaniForJit || transformToJaniForDdMA); - - if (transformToJani) { - storm::prism::Program const& model = output.model.get().asPrismProgram(); - auto modelAndProperties = model.toJani(output.properties); - - // Remove functions here - modelAndProperties.first.substituteFunctions(); - - output.model = modelAndProperties.first; - - if (!modelAndProperties.second.empty()) { - output.preprocessedProperties = std::move(modelAndProperties.second); + 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); + } } } } - return output; + if (output.model && output.model.get().isJaniModel()) { + storm::jani::ModelFeatures supportedFeatures = storm::api::getSupportedJaniFeatures(builderType); + storm::api::simplifyJaniModel(output.model.get().asJaniModel(), output.properties, supportedFeatures); + } + + return {output, mpi}; } void exportSymbolicInput(SymbolicInput const& input) { @@ -350,16 +389,6 @@ namespace storm { } } - SymbolicInput parseAndPreprocessSymbolicInput(storm::utility::Engine const& engine) { - // Get the used builder type to handle cases where preprocessing depends on it - auto buildSettings = storm::settings::getModule(); - - SymbolicInput input = parseSymbolicInput(); - input = preprocessSymbolicInput(input, engine); - exportSymbolicInput(input); - return input; - } - std::vector> createFormulasToRespect(std::vector const& properties) { std::vector> result = storm::api::extractFormulasFromProperties(properties); diff --git a/src/storm-dft-cli/storm-dft.cpp b/src/storm-dft-cli/storm-dft.cpp index 130e32765..5d835b4c7 100644 --- a/src/storm-dft-cli/storm-dft.cpp +++ b/src/storm-dft-cli/storm-dft.cpp @@ -21,8 +21,6 @@ */ template void processOptions() { - // Start by setting some urgent options (log levels, resources, etc.) - storm::cli::setUrgentOptions(); auto const& dftIOSettings = storm::settings::getModule(); auto const& faultTreeSettings = storm::settings::getModule(); @@ -265,10 +263,16 @@ int main(const int argc, const char** argv) { if (!storm::cli::parseOptions(argc, argv)) { return -1; } + + // Start by setting some urgent options (log levels, resources, etc.) + storm::cli::setUrgentOptions(); storm::settings::modules::GeneralSettings const& generalSettings = storm::settings::getModule(); if (generalSettings.isParametricSet()) { processOptions(); + } else if (generalSettings.isExactSet()) { + STORM_LOG_WARN("Exact solving over rational numbers is not implemented. Performing exact solving using rational functions instead."); + processOptions(); } else { processOptions(); } diff --git a/src/storm-dft/settings/DftSettings.cpp b/src/storm-dft/settings/DftSettings.cpp index a3d0bf4ed..c120bab3a 100644 --- a/src/storm-dft/settings/DftSettings.cpp +++ b/src/storm-dft/settings/DftSettings.cpp @@ -25,6 +25,8 @@ #include "storm-gspn/settings/modules/GSPNExportSettings.h" #include "storm/settings/modules/TransformationSettings.h" #include "storm/settings/modules/HintSettings.h" +#include "storm/settings/modules/OviSolverSettings.h" +#include "storm/settings/modules/TimeBoundedSolverSettings.h" namespace storm { @@ -51,6 +53,8 @@ namespace storm { storm::settings::addModule(); storm::settings::addModule(); storm::settings::addModule(); + storm::settings::addModule(); + storm::settings::addModule(); storm::settings::addModule(false); // storm::settings::addModule(); storm::settings::addModule(); diff --git a/src/storm-dft/settings/modules/FaultTreeSettings.cpp b/src/storm-dft/settings/modules/FaultTreeSettings.cpp index 3fa4d6529..5a6cc7243 100644 --- a/src/storm-dft/settings/modules/FaultTreeSettings.cpp +++ b/src/storm-dft/settings/modules/FaultTreeSettings.cpp @@ -15,8 +15,8 @@ namespace storm { namespace modules { const std::string FaultTreeSettings::moduleName = "dft"; - const std::string FaultTreeSettings::symmetryReductionOptionName = "symmetryreduction"; - const std::string FaultTreeSettings::symmetryReductionOptionShortName = "symred"; + const std::string FaultTreeSettings::noSymmetryReductionOptionName = "nosymmetryreduction"; + const std::string FaultTreeSettings::noSymmetryReductionOptionShortName = "nosymred"; const std::string FaultTreeSettings::modularisationOptionName = "modularisation"; const std::string FaultTreeSettings::disableDCOptionName = "disabledc"; const std::string FaultTreeSettings::allowDCRelevantOptionName = "allowdcrelevant"; @@ -32,8 +32,8 @@ namespace storm { #endif FaultTreeSettings::FaultTreeSettings() : ModuleSettings(moduleName) { - this->addOption(storm::settings::OptionBuilder(moduleName, symmetryReductionOptionName, false, "Exploit symmetric structure of model.").setShortName( - symmetryReductionOptionShortName).build()); + this->addOption(storm::settings::OptionBuilder(moduleName, noSymmetryReductionOptionName, false, "Do not exploit symmetric structure of model.").setShortName( + noSymmetryReductionOptionShortName).build()); this->addOption(storm::settings::OptionBuilder(moduleName, modularisationOptionName, false, "Use modularisation (not applicable for expected time).").build()); this->addOption(storm::settings::OptionBuilder(moduleName, disableDCOptionName, false, "Disable Don't Care propagation.").build()); this->addOption(storm::settings::OptionBuilder(moduleName, firstDependencyOptionName, false, @@ -62,7 +62,7 @@ namespace storm { } bool FaultTreeSettings::useSymmetryReduction() const { - return this->getOption(symmetryReductionOptionName).getHasOptionBeenSet(); + return !this->getOption(noSymmetryReductionOptionName).getHasOptionBeenSet(); } bool FaultTreeSettings::useModularisation() const { diff --git a/src/storm-dft/settings/modules/FaultTreeSettings.h b/src/storm-dft/settings/modules/FaultTreeSettings.h index 6c5298f7b..3099f4b8b 100644 --- a/src/storm-dft/settings/modules/FaultTreeSettings.h +++ b/src/storm-dft/settings/modules/FaultTreeSettings.h @@ -130,8 +130,8 @@ namespace storm { private: // Define the string names of the options as constants. - static const std::string symmetryReductionOptionName; - static const std::string symmetryReductionOptionShortName; + static const std::string noSymmetryReductionOptionName; + static const std::string noSymmetryReductionOptionShortName; static const std::string modularisationOptionName; static const std::string disableDCOptionName; static const std::string allowDCRelevantOptionName; diff --git a/src/storm-pars-cli/storm-pars.cpp b/src/storm-pars-cli/storm-pars.cpp index 0b7ff3bbd..4ae3fde89 100644 --- a/src/storm-pars-cli/storm-pars.cpp +++ b/src/storm-pars-cli/storm-pars.cpp @@ -783,8 +783,8 @@ namespace storm { // Parse and preprocess symbolic input (PRISM, JANI, properties, etc.) auto symbolicInput = storm::cli::parseSymbolicInput(); - auto mpi = storm::cli::getModelProcessingInformation(symbolicInput); - symbolicInput = storm::cli::preprocessSymbolicInput(symbolicInput, mpi.engine); + storm::cli::ModelProcessingInformation mpi; + std::tie(symbolicInput, mpi) = storm::cli::preprocessSymbolicInput(symbolicInput); processInputWithValueTypeAndDdlib(symbolicInput, mpi); } diff --git a/src/storm-pars/settings/ParsSettings.cpp b/src/storm-pars/settings/ParsSettings.cpp index 36a16e33a..5e46c4b59 100644 --- a/src/storm-pars/settings/ParsSettings.cpp +++ b/src/storm-pars/settings/ParsSettings.cpp @@ -27,6 +27,8 @@ #include "storm/settings/modules/MultiplierSettings.h" #include "storm/settings/modules/TransformationSettings.h" #include "storm/settings/modules/HintSettings.h" +#include "storm/settings/modules/OviSolverSettings.h" +#include "storm/settings/modules/TimeBoundedSolverSettings.h" @@ -59,6 +61,8 @@ namespace storm { storm::settings::addModule(); storm::settings::addModule(); storm::settings::addModule(); + storm::settings::addModule(); + storm::settings::addModule(); storm::settings::addModule(); } diff --git a/src/storm-pomdp-cli/storm-pomdp.cpp b/src/storm-pomdp-cli/storm-pomdp.cpp index 5f2eb128f..ef0b4c38f 100644 --- a/src/storm-pomdp-cli/storm-pomdp.cpp +++ b/src/storm-pomdp-cli/storm-pomdp.cpp @@ -98,8 +98,8 @@ int main(const int argc, const char** argv) { auto const& pomdpSettings = storm::settings::getModule(); auto symbolicInput = storm::cli::parseSymbolicInput(); - auto mpi = storm::cli::getModelProcessingInformation(symbolicInput); - symbolicInput = storm::cli::preprocessSymbolicInput(symbolicInput, mpi.engine); + storm::cli::ModelProcessingInformation mpi; + std::tie(symbolicInput, mpi) = storm::cli::preprocessSymbolicInput(symbolicInput); // We should not export here if we are going to do some processing first. auto model = storm::cli::buildPreprocessExportModelWithValueTypeAndDdlib(symbolicInput, mpi); diff --git a/src/storm/generator/PrismNextStateGenerator.cpp b/src/storm/generator/PrismNextStateGenerator.cpp index 30e55f5bd..a0fb216e5 100644 --- a/src/storm/generator/PrismNextStateGenerator.cpp +++ b/src/storm/generator/PrismNextStateGenerator.cpp @@ -288,22 +288,18 @@ namespace storm { result.setExpanded(); std::vector> allChoices; - std::vector> allLabeledChoices; if (this->getOptions().isApplyMaximalProgressAssumptionSet()) { // First explore only edges without a rate allChoices = getUnlabeledChoices(*this->state, stateToIdCallback, CommandFilter::Probabilistic); - allLabeledChoices = getLabeledChoices(*this->state, stateToIdCallback, CommandFilter::Probabilistic); - if (allChoices.empty() && allLabeledChoices.empty()) { + addLabeledChoices(allChoices, *this->state, stateToIdCallback, CommandFilter::Probabilistic); + if (allChoices.empty()) { // Expand the Markovian edges if there are no probabilistic ones. allChoices = getUnlabeledChoices(*this->state, stateToIdCallback, CommandFilter::Markovian); - allLabeledChoices = getLabeledChoices(*this->state, stateToIdCallback, CommandFilter::Markovian); + addLabeledChoices(allChoices, *this->state, stateToIdCallback, CommandFilter::Markovian); } } else { allChoices = getUnlabeledChoices(*this->state, stateToIdCallback); - allLabeledChoices = getLabeledChoices(*this->state, stateToIdCallback); - } - for (auto& choice : allLabeledChoices) { - allChoices.push_back(std::move(choice)); + addLabeledChoices(allChoices, *this->state, stateToIdCallback); } std::size_t totalNumberOfChoices = allChoices.size(); @@ -428,11 +424,25 @@ namespace storm { return newState; } + struct ActiveCommandData { + ActiveCommandData(storm::prism::Module const* modulePtr, std::set const* commandIndicesPtr, typename std::set::const_iterator currentCommandIndexIt) : modulePtr(modulePtr), commandIndicesPtr(commandIndicesPtr), currentCommandIndexIt(currentCommandIndexIt) { + // Intentionally left empty + } + storm::prism::Module const* modulePtr; + std::set const* commandIndicesPtr; + typename std::set::const_iterator currentCommandIndexIt; + }; + template boost::optional>>> PrismNextStateGenerator::getActiveCommandsByActionIndex(uint_fast64_t const& actionIndex, CommandFilter const& commandFilter) { - boost::optional>>> result((std::vector>>())); - + + // First check whether there is at least one enabled command at each module + // This avoids evaluating unnecessarily many guards. + // If we find one module without an enabled command, we return boost::none. + // At the same time, we store pointers to the relevant modules, the relevant command sets and the first enabled command within each set. + // Iterate over all modules. + std::vector activeCommands; for (uint_fast64_t i = 0; i < program.getNumberOfModules(); ++i) { storm::prism::Module const& module = program.getModule(i); @@ -446,14 +456,47 @@ namespace storm { // If the module contains the action, but there is no command in the module that is labeled with // this action, we don't have any feasible command combinations. if (commandIndices.empty()) { - return boost::optional>>>(); + return boost::none; } + // Look up commands by their indices and check if the guard evaluates to true in the given state. + bool hasOneEnabledCommand = false; + for (auto commandIndexIt = commandIndices.begin(), commandIndexIte = commandIndices.end(); commandIndexIt != commandIndexIte; ++commandIndexIt) { + storm::prism::Command const& command = module.getCommand(*commandIndexIt); + if (commandFilter != CommandFilter::All) { + STORM_LOG_ASSERT(commandFilter == CommandFilter::Markovian || commandFilter == CommandFilter::Probabilistic, "Unexpected command filter."); + if ((commandFilter == CommandFilter::Markovian) != command.isMarkovian()) { + continue; + } + } + if (this->evaluator->asBool(command.getGuardExpression())) { + // Found the first enabled command for this module. + hasOneEnabledCommand = true; + activeCommands.emplace_back(&module, &commandIndices, commandIndexIt); + break; + } + } + + if (!hasOneEnabledCommand) { + return boost::none; + } + } + + // If we reach this point, there has to be at least one active command for each relevant module. + std::vector>> result; + + // Iterate over all command sets. + for (auto const& activeCommand : activeCommands) { std::vector> commands; + auto commandIndexIt = activeCommand.currentCommandIndexIt; + // The command at the current position is already known to be enabled + commands.push_back(activeCommand.modulePtr->getCommand(*commandIndexIt)); + // Look up commands by their indices and add them if the guard evaluates to true in the given state. - for (uint_fast64_t commandIndex : commandIndices) { - storm::prism::Command const& command = module.getCommand(commandIndex); + auto commandIndexIte = activeCommand.commandIndicesPtr->end(); + for (++commandIndexIt; commandIndexIt != commandIndexIte; ++commandIndexIt) { + storm::prism::Command const& command = activeCommand.modulePtr->getCommand(*commandIndexIt); if (commandFilter != CommandFilter::All) { STORM_LOG_ASSERT(commandFilter == CommandFilter::Markovian || commandFilter == CommandFilter::Probabilistic, "Unexpected command filter."); if ((commandFilter == CommandFilter::Markovian) != command.isMarkovian()) { @@ -465,16 +508,10 @@ namespace storm { } } - // If there was no enabled command although the module has some command with the required action label, - // we must not return anything. - if (commands.size() == 0) { - return boost::none; - } - - result.get().push_back(std::move(commands)); + result.push_back(std::move(commands)); } - STORM_LOG_ASSERT(!result->empty(), "Expected non-empty list."); + STORM_LOG_ASSERT(!result.empty(), "Expected non-empty list."); return result; } @@ -576,8 +613,7 @@ namespace storm { } template - std::vector> PrismNextStateGenerator::getLabeledChoices(CompressedState const& state, StateToIdCallback stateToIdCallback, CommandFilter const& commandFilter) { - std::vector> result; + void PrismNextStateGenerator::addLabeledChoices(std::vector>& choices, CompressedState const& state, StateToIdCallback stateToIdCallback, CommandFilter const& commandFilter) { for (uint_fast64_t actionIndex : program.getSynchronizingActionIndices()) { boost::optional>>> optionalActiveCommandLists = getActiveCommandsByActionIndex(actionIndex, commandFilter); @@ -604,10 +640,10 @@ namespace storm { // At this point, we applied all commands of the current command combination and newTargetStates // contains all target states and their respective probabilities. That means we are now ready to // add the choice to the list of transitions. - result.push_back(Choice(actionIndex)); + choices.push_back(Choice(actionIndex)); // Now create the actual distribution. - Choice& choice = result.back(); + Choice& choice = choices.back(); // Remember the choice label and origins only if we were asked to. if (this->options.isBuildChoiceLabelsSet()) { @@ -623,6 +659,7 @@ namespace storm { // Add the probabilities/rates to the newly created choice. ValueType probabilitySum = storm::utility::zero(); + choice.reserve(std::distance(distribution.begin(), distribution.end())); for (auto const& stateProbability : distribution) { choice.addProbability(stateProbability.getState(), stateProbability.getValue()); if (this->options.isExplorationChecksSet()) { @@ -664,8 +701,6 @@ namespace storm { } } } - - return result; } template diff --git a/src/storm/generator/PrismNextStateGenerator.h b/src/storm/generator/PrismNextStateGenerator.h index b2eb1b555..e302fd02e 100644 --- a/src/storm/generator/PrismNextStateGenerator.h +++ b/src/storm/generator/PrismNextStateGenerator.h @@ -95,10 +95,11 @@ namespace storm { /*! * Retrieves all labeled choices possible from the given state. * + * @param choices The new choices are inserted in this vector * @param state The state for which to retrieve the unlabeled choices. * @return The labeled choices of the state. */ - std::vector> getLabeledChoices(CompressedState const& state, StateToIdCallback stateToIdCallback, CommandFilter const& commandFilter = CommandFilter::All); + void addLabeledChoices(std::vector>& choices, CompressedState const& state, StateToIdCallback stateToIdCallback, CommandFilter const& commandFilter = CommandFilter::All); /*! diff --git a/src/storm/modelchecker/csl/helper/SparseCtmcCslHelper.cpp b/src/storm/modelchecker/csl/helper/SparseCtmcCslHelper.cpp index 5911b4044..64f734e67 100644 --- a/src/storm/modelchecker/csl/helper/SparseCtmcCslHelper.cpp +++ b/src/storm/modelchecker/csl/helper/SparseCtmcCslHelper.cpp @@ -16,6 +16,7 @@ #include "storm/adapters/RationalFunctionAdapter.h" #include "storm/environment/solver/LongRunAverageSolverEnvironment.h" #include "storm/environment/solver/TopologicalSolverEnvironment.h" +#include "storm/environment/solver/TimeBoundedSolverEnvironment.h" #include "storm/utility/macros.h" #include "storm/utility/vector.h" @@ -1028,7 +1029,7 @@ namespace storm { // Use Fox-Glynn to get the truncation points and the weights. // std::tuple> foxGlynnResult = storm::utility::numerical::getFoxGlynnCutoff(lambda, 1e+300, storm::settings::getModule().getPrecision() / 8.0); - storm::utility::numerical::FoxGlynnResult foxGlynnResult = storm::utility::numerical::foxGlynn(lambda, storm::settings::getModule().getPrecision() / 8.0); + storm::utility::numerical::FoxGlynnResult foxGlynnResult = storm::utility::numerical::foxGlynn(lambda, storm::utility::convertNumber(env.solver().timeBounded().getPrecision()) / 8.0); STORM_LOG_DEBUG("Fox-Glynn cutoff points: left=" << foxGlynnResult.left << ", right=" << foxGlynnResult.right); // Scale the weights so they add up to one. diff --git a/src/storm/solver/IterativeMinMaxLinearEquationSolver.cpp b/src/storm/solver/IterativeMinMaxLinearEquationSolver.cpp index f5b7d4c8f..02141283e 100644 --- a/src/storm/solver/IterativeMinMaxLinearEquationSolver.cpp +++ b/src/storm/solver/IterativeMinMaxLinearEquationSolver.cpp @@ -50,8 +50,8 @@ namespace storm { } } else if (env.solver().isForceSoundness() && method != MinMaxMethod::SoundValueIteration && method != MinMaxMethod::IntervalIteration && method != MinMaxMethod::PolicyIteration && method != MinMaxMethod::RationalSearch && method != MinMaxMethod::OptimisticValueIteration) { if (env.solver().minMax().isMethodSetFromDefault()) { - STORM_LOG_INFO("Selecting 'sound value iteration' as the solution technique to guarantee sound results. If you want to override this, please explicitly specify a different method."); - method = MinMaxMethod::SoundValueIteration; + method = MinMaxMethod::OptimisticValueIteration; + STORM_LOG_INFO("Selecting '" << toString(method) << "' as the solution technique to guarantee sound results. If you want to override this, please explicitly specify a different method."); } else { STORM_LOG_WARN("The selected solution method does not guarantee sound results."); } diff --git a/src/storm/solver/NativeLinearEquationSolver.cpp b/src/storm/solver/NativeLinearEquationSolver.cpp index 97c988f71..a018d5f02 100644 --- a/src/storm/solver/NativeLinearEquationSolver.cpp +++ b/src/storm/solver/NativeLinearEquationSolver.cpp @@ -987,7 +987,7 @@ namespace storm { } } else if (env.solver().isForceSoundness() && method != NativeLinearEquationSolverMethod::SoundValueIteration && method != NativeLinearEquationSolverMethod::OptimisticValueIteration && method != NativeLinearEquationSolverMethod::IntervalIteration && method != NativeLinearEquationSolverMethod::RationalSearch) { if (env.solver().native().isMethodSetFromDefault()) { - method = NativeLinearEquationSolverMethod::SoundValueIteration; + method = NativeLinearEquationSolverMethod::OptimisticValueIteration; STORM_LOG_INFO("Selecting '" + toString(method) + "' as the solution technique to guarantee sound results. If you want to override this, please explicitly specify a different method."); } else { STORM_LOG_WARN("The selected solution method does not guarantee sound results."); diff --git a/src/storm/utility/Portfolio.cpp b/src/storm/utility/Portfolio.cpp index c45f51d2b..2045ed153 100644 --- a/src/storm/utility/Portfolio.cpp +++ b/src/storm/utility/Portfolio.cpp @@ -34,15 +34,7 @@ namespace storm { } struct Features { - Features(storm::storage::SymbolicModelDescription const& modelDescription, storm::jani::Property const& property) { - if (!modelDescription.isJaniModel()) { - initialize(modelDescription.toJani().asJaniModel(), property); - } else { - initialize(modelDescription.asJaniModel(), property); - } - } - - void initialize(storm::jani::Model const& model, storm::jani::Property const& property) { + Features(storm::jani::Model const& model, storm::jani::Property const& property) { continuousTime = !model.isDiscreteTimeModel(); nondeterminism = !model.isDeterministicModel(); propertyType = getPropertyType(property); @@ -65,9 +57,9 @@ namespace storm { // Intentionally left empty } - void Portfolio::predict(storm::storage::SymbolicModelDescription const& modelDescription, storm::jani::Property const& property) { + void Portfolio::predict(storm::jani::Model const& model, storm::jani::Property const& property) { typedef pfinternal::PropertyType PropertyType; - auto f = pfinternal::Features(modelDescription, property); + auto f = pfinternal::Features(model, property); { // Decision tree start if (f.numEdges <= 618) { @@ -171,12 +163,12 @@ namespace storm { } - void Portfolio::predict(storm::storage::SymbolicModelDescription const& modelDescription, storm::jani::Property const& property, uint64_t stateEstimate) { + void Portfolio::predict(storm::jani::Model const& model, storm::jani::Property const& property, uint64_t stateEstimate) { typedef pfinternal::PropertyType PropertyType; - auto f = pfinternal::Features(modelDescription, property); + auto f = pfinternal::Features(model, property); f.stateEstimate = stateEstimate; // TODO: Actually make use of the estimate - predict(modelDescription, property); + predict(model, property); } storm::utility::Engine Portfolio::getEngine() const { diff --git a/src/storm/utility/Portfolio.h b/src/storm/utility/Portfolio.h index 8fceda6b8..472369bc7 100644 --- a/src/storm/utility/Portfolio.h +++ b/src/storm/utility/Portfolio.h @@ -5,13 +5,10 @@ namespace storm { namespace jani { + class Model; class Property; } - namespace storage { - class SymbolicModelDescription; - } - namespace utility { class Portfolio { public: @@ -20,13 +17,13 @@ namespace storm { /*! * Predicts "good" settings for the provided model checking query */ - void predict(storm::storage::SymbolicModelDescription const& modelDescription, storm::jani::Property const& property); + void predict(storm::jani::Model const& model, storm::jani::Property const& property); /*! * Predicts "good" settings for the provided model checking query * @param stateEstimate A hint that gives a (rough) estimate for the number of states. */ - void predict(storm::storage::SymbolicModelDescription const& modelDescription, storm::jani::Property const& property, uint64_t stateEstimate); + void predict(storm::jani::Model const& model, storm::jani::Property const& property, uint64_t stateEstimate); /// Retrieve "good" settings after calling predict. storm::utility::Engine getEngine() const;