Browse Source

Merge from qcomp2020

main
Matthias Volk 5 years ago
parent
commit
5192bd1623
  1. 3
      CHANGELOG.md
  2. 7567
      resources/3rdparty/exprtk/exprtk.hpp
  3. 39
      src/storm-cli-utilities/cli.cpp
  4. 411
      src/storm-cli-utilities/model-handling.h
  5. 4
      src/storm-conv/api/storm-conv.cpp
  6. 4
      src/storm-conv/converter/options/JaniConversionOptions.cpp
  7. 4
      src/storm-conv/converter/options/JaniConversionOptions.h
  8. 6
      src/storm-conv/settings/modules/JaniExportSettings.cpp
  9. 3
      src/storm-conv/settings/modules/JaniExportSettings.h
  10. 58
      src/storm-pars-cli/storm-pars.cpp
  11. 29
      src/storm-parsers/api/model_descriptions.cpp
  12. 4
      src/storm-parsers/api/model_descriptions.h
  13. 10
      src/storm-pomdp-cli/storm-pomdp.cpp
  14. 3
      src/storm/adapters/ExprttkAdapter.h
  15. 6
      src/storm/api/bisimulation.h
  16. 8
      src/storm/api/builder.h
  17. 2
      src/storm/api/transformation.h
  18. 25
      src/storm/api/verification.h
  19. 69
      src/storm/builder/BuilderType.cpp
  20. 15
      src/storm/builder/BuilderType.h
  21. 81
      src/storm/builder/DdJaniModelBuilder.cpp
  22. 17
      src/storm/builder/DdJaniModelBuilder.h
  23. 7
      src/storm/builder/DdPrismModelBuilder.cpp
  24. 7
      src/storm/builder/DdPrismModelBuilder.h
  25. 27
      src/storm/builder/jit/ExplicitJitJaniModelBuilder.cpp
  26. 12
      src/storm/builder/jit/ExplicitJitJaniModelBuilder.h
  27. 6
      src/storm/environment/SubEnvironment.cpp
  28. 43
      src/storm/environment/solver/OviSolverEnvironment.cpp
  29. 29
      src/storm/environment/solver/OviSolverEnvironment.h
  30. 27
      src/storm/environment/solver/SolverEnvironment.cpp
  31. 11
      src/storm/environment/solver/SolverEnvironment.h
  32. 60
      src/storm/environment/solver/TimeBoundedSolverEnvironment.cpp
  33. 36
      src/storm/environment/solver/TimeBoundedSolverEnvironment.h
  34. 5
      src/storm/generator/Choice.cpp
  35. 5
      src/storm/generator/Choice.h
  36. 141
      src/storm/generator/JaniNextStateGenerator.cpp
  37. 14
      src/storm/generator/JaniNextStateGenerator.h
  38. 6
      src/storm/generator/PrismNextStateGenerator.cpp
  39. 9
      src/storm/generator/PrismNextStateGenerator.h
  40. 10
      src/storm/logic/FormulaInformation.cpp
  41. 3
      src/storm/logic/FormulaInformation.h
  42. 9
      src/storm/logic/FormulaInformationVisitor.cpp
  43. 6
      src/storm/modelchecker/AbstractModelChecker.cpp
  44. 2
      src/storm/modelchecker/abstraction/BisimulationAbstractionRefinementModelChecker.cpp
  45. 21
      src/storm/modelchecker/csl/HybridCtmcCslModelChecker.cpp
  46. 12
      src/storm/modelchecker/csl/HybridCtmcCslModelChecker.h
  47. 126
      src/storm/modelchecker/csl/HybridMarkovAutomatonCslModelChecker.cpp
  48. 36
      src/storm/modelchecker/csl/HybridMarkovAutomatonCslModelChecker.h
  49. 23
      src/storm/modelchecker/csl/SparseCtmcCslModelChecker.cpp
  50. 10
      src/storm/modelchecker/csl/SparseCtmcCslModelChecker.h
  51. 54
      src/storm/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.cpp
  52. 13
      src/storm/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.h
  53. 150
      src/storm/modelchecker/csl/helper/HybridMarkovAutomatonCslHelper.cpp
  54. 42
      src/storm/modelchecker/csl/helper/HybridMarkovAutomatonCslHelper.h
  55. 2
      src/storm/modelchecker/csl/helper/SparseCtmcCslHelper.cpp
  56. 719
      src/storm/modelchecker/csl/helper/SparseMarkovAutomatonCslHelper.cpp
  57. 5
      src/storm/modelchecker/csl/helper/SparseMarkovAutomatonCslHelper.h
  58. 9
      src/storm/modelchecker/prctl/HybridDtmcPrctlModelChecker.cpp
  59. 5
      src/storm/modelchecker/prctl/HybridDtmcPrctlModelChecker.h
  60. 24
      src/storm/modelchecker/prctl/HybridMdpPrctlModelChecker.cpp
  61. 6
      src/storm/modelchecker/prctl/HybridMdpPrctlModelChecker.h
  62. 18
      src/storm/modelchecker/prctl/SparseDtmcPrctlModelChecker.cpp
  63. 6
      src/storm/modelchecker/prctl/SparseDtmcPrctlModelChecker.h
  64. 29
      src/storm/modelchecker/prctl/SparseMdpPrctlModelChecker.cpp
  65. 6
      src/storm/modelchecker/prctl/SparseMdpPrctlModelChecker.h
  66. 7
      src/storm/modelchecker/prctl/SymbolicDtmcPrctlModelChecker.cpp
  67. 3
      src/storm/modelchecker/prctl/SymbolicDtmcPrctlModelChecker.h
  68. 7
      src/storm/modelchecker/prctl/SymbolicMdpPrctlModelChecker.cpp
  69. 3
      src/storm/modelchecker/prctl/SymbolicMdpPrctlModelChecker.h
  70. 2
      src/storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.cpp
  71. 6
      src/storm/modelchecker/propositional/SymbolicPropositionalModelChecker.cpp
  72. 5
      src/storm/models/sparse/MarkovAutomaton.cpp
  73. 4
      src/storm/settings/SettingsManager.cpp
  74. 11
      src/storm/settings/modules/BisimulationSettings.cpp
  75. 12
      src/storm/settings/modules/BisimulationSettings.h
  76. 6
      src/storm/settings/modules/BuildSettings.cpp
  77. 6
      src/storm/settings/modules/BuildSettings.h
  78. 28
      src/storm/settings/modules/CoreSettings.cpp
  79. 11
      src/storm/settings/modules/CoreSettings.h
  80. 6
      src/storm/settings/modules/DebugSettings.cpp
  81. 8
      src/storm/settings/modules/DebugSettings.h
  82. 3
      src/storm/settings/modules/ExplorationSettings.cpp
  83. 16
      src/storm/settings/modules/MinMaxEquationSolverSettings.cpp
  84. 11
      src/storm/settings/modules/MinMaxEquationSolverSettings.h
  85. 4
      src/storm/settings/modules/NativeEquationSolverSettings.cpp
  86. 56
      src/storm/settings/modules/OviSolverSettings.cpp
  87. 42
      src/storm/settings/modules/OviSolverSettings.h
  88. 62
      src/storm/settings/modules/TimeBoundedSolverSettings.cpp
  89. 66
      src/storm/settings/modules/TimeBoundedSolverSettings.h
  90. 4
      src/storm/settings/modules/TopologicalEquationSolverSettings.cpp
  91. 2
      src/storm/solver/EigenLinearEquationSolver.cpp
  92. 1
      src/storm/solver/GmmxxLinearEquationSolver.cpp
  93. 135
      src/storm/solver/IterativeMinMaxLinearEquationSolver.cpp
  94. 1
      src/storm/solver/IterativeMinMaxLinearEquationSolver.h
  95. 11
      src/storm/solver/LinearEquationSolver.cpp
  96. 4
      src/storm/solver/MinMaxLinearEquationSolver.cpp
  97. 78
      src/storm/solver/NativeLinearEquationSolver.cpp
  98. 1
      src/storm/solver/NativeLinearEquationSolver.h
  99. 14
      src/storm/solver/SolverSelectionOptions.cpp
  100. 5
      src/storm/solver/SolverSelectionOptions.h

3
CHANGELOG.md

@ -8,6 +8,9 @@ Version 1.4.x
-------------
## Version 1.4.2 (under development)
- Added portfolio engine which picks a good engine (among other settings) based on features of the symbolic input
- Setting `--engine dd-to-sparse --bisimulation` now triggers extracting the sparse bisimulation quotiont
- JIT model building is now invoked via `--engine jit` (instead of `--jit`)
- DRN: support import of choice labelling
- 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.

7567
resources/3rdparty/exprtk/exprtk.hpp
File diff suppressed because it is too large
View File

39
src/storm-cli-utilities/cli.cpp

@ -245,25 +245,34 @@ namespace storm {
// Start by setting some urgent options (log levels, resources, etc.)
setUrgentOptions();
// Parse and preprocess symbolic input (PRISM, JANI, properties, etc.)
SymbolicInput symbolicInput = parseAndPreprocessSymbolicInput();
// Parse symbolic input (PRISM, JANI, properties, etc.)
SymbolicInput symbolicInput = parseSymbolicInput();
auto generalSettings = storm::settings::getModule<storm::settings::modules::GeneralSettings>();
if (generalSettings.isParametricSet()) {
#ifdef STORM_HAVE_CARL
processInputWithValueType<storm::RationalFunction>(symbolicInput);
#else
STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "No parameters are supported in this build.");
#endif
} else if (generalSettings.isExactSet()) {
// Obtain settings for model processing
ModelProcessingInformation mpi = getModelProcessingInformation(symbolicInput);
// Preprocess the symbolic input
symbolicInput = preprocessSymbolicInput(symbolicInput, mpi.engine);
// Export symbolic input (if requested)
exportSymbolicInput(symbolicInput);
#ifdef STORM_HAVE_CARL
processInputWithValueType<storm::RationalNumber>(symbolicInput);
switch (mpi.verificationValueType) {
case ModelProcessingInformation::ValueType::Parametric:
processInputWithValueType<storm::RationalFunction>(symbolicInput, mpi);
break;
case ModelProcessingInformation::ValueType::Exact:
processInputWithValueType<storm::RationalNumber>(symbolicInput, mpi);
break;
case ModelProcessingInformation::ValueType::FinitePrecision:
processInputWithValueType<double>(symbolicInput, mpi);
break;
}
#else
STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "No exact numbers are supported in this build.");
STORM_LOG_THROW(mpi.verificationValueType == ModelProcessingInformation::ValueType::FinitePrecision, storm::exceptions::NotSupportedException, "No exact numbers or parameters are supported in this build.");
processInputWithValueType<double>(symbolicInput, mpi);
#endif
} else {
processInputWithValueType<double>(symbolicInput);
}
}
void printTimeAndMemoryStatistics(uint64_t wallclockMilliseconds) {

411
src/storm-cli-utilities/model-handling.h

@ -10,6 +10,8 @@
#include "storm/utility/storm-version.h"
#include "storm/utility/macros.h"
#include "storm/utility/NumberTraits.h"
#include "storm/utility/Engine.h"
#include "storm/utility/Portfolio.h"
#include "storm/utility/initialize.h"
#include "storm/utility/Stopwatch.h"
@ -24,6 +26,8 @@
#include "storm/models/ModelBase.h"
#include "storm/environment/Environment.h"
#include "storm/exceptions/OptionParserException.h"
#include "storm/modelchecker/results/SymbolicQualitativeCheckResult.h"
@ -43,6 +47,7 @@
#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"
@ -62,13 +67,12 @@ namespace storm {
boost::optional<std::vector<storm::jani::Property>> preprocessedProperties;
};
void parseSymbolicModelDescription(storm::settings::modules::IOSettings const& ioSettings, SymbolicInput& input, storm::builder::BuilderType const& builderType) {
void parseSymbolicModelDescription(storm::settings::modules::IOSettings const& ioSettings, SymbolicInput& input) {
if (ioSettings.isPrismOrJaniInputSet()) {
storm::utility::Stopwatch modelParsingWatch(true);
if (ioSettings.isPrismInputSet()) {
input.model = storm::api::parseProgram(ioSettings.getPrismInputFilename(), storm::settings::getModule<storm::settings::modules::BuildSettings>().isPrismCompatibilityEnabled());
} else {
storm::jani::ModelFeatures supportedFeatures = storm::api::getSupportedJaniFeatures(builderType);
boost::optional<std::vector<std::string>> propertyFilter;
if (ioSettings.isJaniPropertiesSet()) {
if (ioSettings.areJaniPropertiesSelected()) {
@ -79,7 +83,7 @@ namespace storm {
} else {
propertyFilter = std::vector<std::string>();
}
auto janiInput = storm::api::parseJaniModel(ioSettings.getJaniInputFilename(), supportedFeatures, propertyFilter);
auto janiInput = storm::api::parseJaniModel(ioSettings.getJaniInputFilename(), propertyFilter);
input.model = std::move(janiInput.first);
if (ioSettings.isJaniPropertiesSet()) {
input.properties = std::move(janiInput.second);
@ -102,20 +106,175 @@ namespace storm {
input.properties.insert(input.properties.end(), newProperties.begin(), newProperties.end());
}
}
SymbolicInput parseSymbolicInput(storm::builder::BuilderType const& builderType) {
auto ioSettings = storm::settings::getModule<storm::settings::modules::IOSettings>();
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 the property filter, if any is given.
// Parse additional properties
boost::optional<std::set<std::string>> propertyFilter = storm::api::parsePropertyFilter(ioSettings.getPropertyFilter());
SymbolicInput input;
parseSymbolicModelDescription(ioSettings, input, builderType);
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<storm::settings::modules::IOSettings>();
if (ioSettings.isQvbsInputSet()) {
return parseSymbolicInputQvbs(ioSettings);
} else {
// Parse the property filter, if any is given.
boost::optional<std::set<std::string>> 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;
// 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;
};
void getModelProcessingInformationPortfolio(SymbolicInput const& input, ModelProcessingInformation& mpi) {
auto hints = storm::settings::getModule<storm::settings::modules::HintSettings>();
STORM_LOG_THROW(input.model.is_initialized(), storm::exceptions::InvalidArgumentException, "Portfolio engine requires a symbolic model (PRISM or JANI.");
std::vector<storm::jani::Property> 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());
} else {
pf.predict(input.model.get(), properties.front());
}
mpi.engine = pf.getEngine();
if (pf.enableBisimulation()) {
mpi.applyBisimulation = true;
}
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)
}
ModelProcessingInformation getModelProcessingInformation(SymbolicInput const& input) {
ModelProcessingInformation mpi;
auto coreSettings = storm::settings::getModule<storm::settings::modules::CoreSettings>();
auto generalSettings = storm::settings::getModule<storm::settings::modules::GeneralSettings>();
auto bisimulationSettings = storm::settings::getModule<storm::settings::modules::BisimulationSettings>();
// 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;
}
// 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;
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);
}
// Check whether these settings are compatible with the provided input.
bool incompatibleSettings = false;
if (input.model) {
switch (mpi.verificationValueType) {
case ModelProcessingInformation::ValueType::Parametric:
incompatibleSettings = !storm::utility::canHandle<storm::RationalFunction>(mpi.engine, input.preprocessedProperties.is_initialized() ? input.preprocessedProperties.get() : input.properties, input.model.get());
break;
case ModelProcessingInformation::ValueType::Exact:
incompatibleSettings = !storm::utility::canHandle<storm::RationalNumber>(mpi.engine, input.preprocessedProperties.is_initialized() ? input.preprocessedProperties.get() : input.properties, input.model.get());
break;
case ModelProcessingInformation::ValueType::FinitePrecision:
incompatibleSettings = !storm::utility::canHandle<double>(mpi.engine, input.preprocessedProperties.is_initialized() ? input.preprocessedProperties.get() : input.properties, input.model.get());
break;
}
}
if (incompatibleSettings) {
if (usePortfolio) {
STORM_LOG_WARN("The settings picked by the portfolio engine (engine=" << mpi.engine << ", bisim=" << mpi.applyBisimulation << ", exact=" << (mpi.verificationValueType != ModelProcessingInformation::ValueType::FinitePrecision) << ") are incompatible with this model. Falling back to sparse engine without bisimulation and floating point arithmetic.");
mpi.engine = storm::utility::Engine::Sparse;
mpi.applyBisimulation = false;
mpi.verificationValueType = ModelProcessingInformation::ValueType::FinitePrecision;
} else {
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 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<storm::jani::Property> const& properties) {
// Make sure there are no undefined constants remaining in any property.
for (auto const& property : properties) {
@ -130,11 +289,17 @@ namespace storm {
}
}
SymbolicInput preprocessSymbolicInput(SymbolicInput const& input, storm::builder::BuilderType const& builderType) {
SymbolicInput preprocessSymbolicInput(SymbolicInput const& input, storm::utility::Engine const& engine) {
auto ioSettings = storm::settings::getModule<storm::settings::modules::IOSettings>();
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<storm::expressions::Variable, storm::expressions::Expression> constantDefinitions;
@ -185,63 +350,12 @@ namespace storm {
}
}
storm::builder::BuilderType getBuilderType(storm::settings::modules::CoreSettings::Engine const& engine, bool useJit) {
if (engine == storm::settings::modules::CoreSettings::Engine::Dd || engine == storm::settings::modules::CoreSettings::Engine::Hybrid || engine == storm::settings::modules::CoreSettings::Engine::DdSparse || engine == storm::settings::modules::CoreSettings::Engine::AbstractionRefinement) {
return storm::builder::BuilderType::Dd;
} else if (engine == storm::settings::modules::CoreSettings::Engine::Sparse) {
if (useJit) {
return storm::builder::BuilderType::Jit;
} else {
return storm::builder::BuilderType::Explicit;
}
} else if (engine == storm::settings::modules::CoreSettings::Engine::Exploration) {
return storm::builder::BuilderType::Explicit;
}
STORM_LOG_THROW(false, storm::exceptions::InvalidSettingsException, "Unable to determine the model builder type.");
}
SymbolicInput parseAndPreprocessSymbolicInputQvbs(storm::settings::modules::IOSettings const& ioSettings, storm::builder::BuilderType const& builderType) {
// 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);
storm::jani::ModelFeatures supportedFeatures = storm::api::getSupportedJaniFeatures(builderType);
auto janiInput = storm::api::parseJaniModel(benchmark.getJaniFile(ioSettings.getQvbsInstanceIndex()), supportedFeatures, 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<std::set<std::string>> 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);
}
ensureNoUndefinedPropertyConstants(input.properties);
return input;
}
SymbolicInput parseAndPreprocessSymbolicInput() {
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<storm::settings::modules::BuildSettings>();
auto coreSettings = storm::settings::getModule<storm::settings::modules::CoreSettings>();
auto ioSettings = storm::settings::getModule<storm::settings::modules::IOSettings>();
auto builderType = getBuilderType(coreSettings.getEngine(), buildSettings.isJitSet());
SymbolicInput input;
if (ioSettings.isQvbsInputSet()) {
input = parseAndPreprocessSymbolicInputQvbs(ioSettings, builderType);
} else {
input = parseSymbolicInput(builderType);
input = preprocessSymbolicInput(input, builderType);
}
SymbolicInput input = parseSymbolicInput();
input = preprocessSymbolicInput(input, engine);
exportSymbolicInput(input);
return input;
}
@ -265,7 +379,7 @@ namespace storm {
}
template <typename ValueType>
std::shared_ptr<storm::models::ModelBase> buildModelSparse(SymbolicInput const& input, storm::settings::modules::BuildSettings const& buildSettings) {
std::shared_ptr<storm::models::ModelBase> buildModelSparse(SymbolicInput const& input, storm::settings::modules::BuildSettings const& buildSettings, bool useJit) {
storm::builder::BuilderOptions options(createFormulasToRespect(input.properties), input.model.get());
options.setBuildChoiceLabels(buildSettings.isBuildChoiceLabelsSet());
options.setBuildStateValuations(buildSettings.isBuildStateValuationsSet());
@ -293,7 +407,7 @@ namespace storm {
options.setBuildAllLabels(true);
options.setBuildAllRewardModels(true);
}
return storm::api::buildSparseModel<ValueType>(input.model.get(), options, buildSettings.isJitSet(), storm::settings::getModule<storm::settings::modules::JitBuilderSettings>().isDoctorSet());
return storm::api::buildSparseModel<ValueType>(input.model.get(), options, useJit, storm::settings::getModule<storm::settings::modules::JitBuilderSettings>().isDoctorSet());
}
template <typename ValueType>
@ -313,20 +427,20 @@ namespace storm {
}
template <storm::dd::DdType DdType, typename ValueType>
std::shared_ptr<storm::models::ModelBase> buildModel(storm::settings::modules::CoreSettings::Engine const& engine, SymbolicInput const& input, storm::settings::modules::IOSettings const& ioSettings) {
std::shared_ptr<storm::models::ModelBase> buildModel(SymbolicInput const& input, storm::settings::modules::IOSettings const& ioSettings, ModelProcessingInformation const& mpi) {
storm::utility::Stopwatch modelBuildingWatch(true);
auto buildSettings = storm::settings::getModule<storm::settings::modules::BuildSettings>();
std::shared_ptr<storm::models::ModelBase> result;
if (input.model) {
auto builderType = getBuilderType(engine, buildSettings.isJitSet());
auto builderType = storm::utility::getBuilderType(mpi.engine);
if (builderType == storm::builder::BuilderType::Dd) {
result = buildModelDd<DdType, ValueType>(input);
} else if (builderType == storm::builder::BuilderType::Explicit || builderType == storm::builder::BuilderType::Jit) {
result = buildModelSparse<ValueType>(input, buildSettings);
result = buildModelSparse<ValueType>(input, buildSettings, builderType == storm::builder::BuilderType::Jit);
}
} else if (ioSettings.isExplicitSet() || ioSettings.isExplicitDRNSet() || ioSettings.isExplicitIMCASet()) {
STORM_LOG_THROW(engine == storm::settings::modules::CoreSettings::Engine::Sparse, storm::exceptions::InvalidSettingsException, "Can only use sparse engine with explicit input.");
STORM_LOG_THROW(mpi.engine == storm::utility::Engine::Sparse, storm::exceptions::InvalidSettingsException, "Can only use sparse engine with explicit input.");
result = buildModelExplicit<ValueType>(ioSettings, buildSettings);
}
@ -341,11 +455,12 @@ namespace storm {
template <typename ValueType>
std::shared_ptr<storm::models::sparse::Model<ValueType>> preprocessSparseMarkovAutomaton(std::shared_ptr<storm::models::sparse::MarkovAutomaton<ValueType>> const& model) {
auto transformationSettings = storm::settings::getModule<storm::settings::modules::TransformationSettings>();
auto debugSettings = storm::settings::getModule<storm::settings::modules::DebugSettings>();
std::shared_ptr<storm::models::sparse::Model<ValueType>> result = model;
model->close();
STORM_LOG_WARN_COND(!model->containsZenoCycle(), "MA contains a Zeno cycle. Model checking results cannot be trusted.");
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();
@ -371,8 +486,7 @@ namespace storm {
}
template <typename ValueType>
std::pair<std::shared_ptr<storm::models::sparse::Model<ValueType>>, bool> preprocessSparseModel(std::shared_ptr<storm::models::sparse::Model<ValueType>> const& model, SymbolicInput const& input) {
auto generalSettings = storm::settings::getModule<storm::settings::modules::GeneralSettings>();
std::pair<std::shared_ptr<storm::models::sparse::Model<ValueType>>, bool> preprocessSparseModel(std::shared_ptr<storm::models::sparse::Model<ValueType>> const& model, SymbolicInput const& input, ModelProcessingInformation const& mpi) {
auto bisimulationSettings = storm::settings::getModule<storm::settings::modules::BisimulationSettings>();
auto ioSettings = storm::settings::getModule<storm::settings::modules::IOSettings>();
auto transformationSettings = storm::settings::getModule<storm::settings::modules::TransformationSettings>();
@ -384,7 +498,7 @@ namespace storm {
result.second = true;
}
if (generalSettings.isBisimulationSet()) {
if (mpi.applyBisimulation) {
result.first = preprocessSparseModelBisimulation(result.first, input, bisimulationSettings);
result.second = true;
}
@ -457,17 +571,21 @@ namespace storm {
}
template <storm::dd::DdType DdType, typename ValueType, typename ExportValueType = ValueType>
std::shared_ptr<storm::models::Model<ExportValueType>> preprocessDdModelBisimulation(std::shared_ptr<storm::models::symbolic::Model<DdType, ValueType>> const& model, SymbolicInput const& input, storm::settings::modules::BisimulationSettings const& bisimulationSettings) {
std::shared_ptr<storm::models::Model<ExportValueType>> preprocessDdModelBisimulation(std::shared_ptr<storm::models::symbolic::Model<DdType, ValueType>> 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<DdType, ValueType, ExportValueType>(model, createFormulasToRespect(input.properties), storm::storage::BisimulationType::Strong, bisimulationSettings.getSignatureMode());
return storm::api::performBisimulationMinimization<DdType, ValueType, ExportValueType>(model, createFormulasToRespect(input.properties), storm::storage::BisimulationType::Strong, bisimulationSettings.getSignatureMode(), quotientFormat);
}
template <storm::dd::DdType DdType, typename ValueType, typename ExportValueType = ValueType>
std::pair<std::shared_ptr<storm::models::ModelBase>, bool> preprocessDdModel(std::shared_ptr<storm::models::symbolic::Model<DdType, ValueType>> const& model, SymbolicInput const& input) {
std::pair<std::shared_ptr<storm::models::ModelBase>, bool> preprocessDdModel(std::shared_ptr<storm::models::symbolic::Model<DdType, ValueType>> const& model, SymbolicInput const& input, ModelProcessingInformation const& mpi) {
auto bisimulationSettings = storm::settings::getModule<storm::settings::modules::BisimulationSettings>();
auto generalSettings = storm::settings::getModule<storm::settings::modules::GeneralSettings>();
std::pair<std::shared_ptr<storm::models::Model<ValueType>>, bool> intermediateResult = std::make_pair(model, false);
if (model->isOfType(storm::models::ModelType::MarkovAutomaton)) {
@ -477,14 +595,14 @@ namespace storm {
std::unique_ptr<std::pair<std::shared_ptr<storm::models::Model<ExportValueType>>, bool>> result;
auto symbolicModel = intermediateResult.first->template as<storm::models::symbolic::Model<DdType, ValueType>>();
if (generalSettings.isBisimulationSet()) {
std::shared_ptr<storm::models::Model<ExportValueType>> newModel = preprocessDdModelBisimulation<DdType, ValueType, ExportValueType>(symbolicModel, input, bisimulationSettings);
if (mpi.applyBisimulation) {
std::shared_ptr<storm::models::Model<ExportValueType>> newModel = preprocessDdModelBisimulation<DdType, ValueType, ExportValueType>(symbolicModel, input, bisimulationSettings, mpi);
result = std::make_unique<std::pair<std::shared_ptr<storm::models::Model<ExportValueType>>, bool>>(newModel, true);
} else {
result = std::make_unique<std::pair<std::shared_ptr<storm::models::Model<ExportValueType>>, bool>>(symbolicModel->template toValueType<ExportValueType>(), !std::is_same<ValueType, ExportValueType>::value);
}
if (result && result->first->isSymbolicModel() && storm::settings::getModule<storm::settings::modules::CoreSettings>().getEngine() == storm::settings::modules::CoreSettings::Engine::DdSparse) {
if (result && result->first->isSymbolicModel() && mpi.engine == storm::utility::Engine::DdSparse) {
// Mark as changed.
result->second = true;
@ -501,15 +619,15 @@ namespace storm {
}
template <storm::dd::DdType DdType, typename BuildValueType, typename ExportValueType = BuildValueType>
std::pair<std::shared_ptr<storm::models::ModelBase>, bool> preprocessModel(std::shared_ptr<storm::models::ModelBase> const& model, SymbolicInput const& input) {
std::pair<std::shared_ptr<storm::models::ModelBase>, bool> preprocessModel(std::shared_ptr<storm::models::ModelBase> const& model, SymbolicInput const& input, ModelProcessingInformation const& mpi) {
storm::utility::Stopwatch preprocessingWatch(true);
std::pair<std::shared_ptr<storm::models::ModelBase>, bool> result = std::make_pair(model, false);
if (model->isSparseModel()) {
result = preprocessSparseModel<BuildValueType>(result.first->as<storm::models::sparse::Model<BuildValueType>>(), input);
result = preprocessSparseModel<BuildValueType>(result.first->as<storm::models::sparse::Model<BuildValueType>>(), input, mpi);
} else {
STORM_LOG_ASSERT(model->isSymbolicModel(), "Unexpected model type.");
result = preprocessDdModel<DdType, BuildValueType, ExportValueType>(result.first->as<storm::models::symbolic::Model<DdType, BuildValueType>>(), input);
result = preprocessDdModel<DdType, BuildValueType, ExportValueType>(result.first->as<storm::models::symbolic::Model<DdType, BuildValueType>>(), input, mpi);
}
preprocessingWatch.stop();
@ -791,45 +909,45 @@ namespace storm {
}
template <storm::dd::DdType DdType, typename ValueType>
void verifyWithAbstractionRefinementEngine(SymbolicInput const& input) {
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::settings::modules::AbstractionSettings>();
storm::api::AbstractionRefinementOptions options(parseConstraints(input.model->getManager(), abstractionSettings.getConstraintString()), parseInjectedRefinementPredicates(input.model->getManager(), abstractionSettings.getInjectedRefinementPredicates()));
verifyProperties<ValueType>(input, [&input,&options] (std::shared_ptr<storm::logic::Formula const> const& formula, std::shared_ptr<storm::logic::Formula const> const& states) {
verifyProperties<ValueType>(input, [&input,&options,&mpi] (std::shared_ptr<storm::logic::Formula const> const& formula, std::shared_ptr<storm::logic::Formula const> const& states) {
STORM_LOG_THROW(states->isInitialFormula(), storm::exceptions::NotSupportedException, "Abstraction-refinement can only filter initial states.");
return storm::api::verifyWithAbstractionRefinementEngine<DdType, ValueType>(input.model.get(), storm::api::createTask<ValueType>(formula, true), options);
return storm::api::verifyWithAbstractionRefinementEngine<DdType, ValueType>(mpi.env, input.model.get(), storm::api::createTask<ValueType>(formula, true), options);
});
}
template <typename ValueType>
void verifyWithExplorationEngine(SymbolicInput const& input) {
void verifyWithExplorationEngine(SymbolicInput const& input, ModelProcessingInformation const& mpi) {
STORM_LOG_ASSERT(input.model, "Expected symbolic model description.");
STORM_LOG_THROW((std::is_same<ValueType, double>::value), storm::exceptions::NotSupportedException, "Exploration does not support other data-types than floating points.");
verifyProperties<ValueType>(input, [&input] (std::shared_ptr<storm::logic::Formula const> const& formula, std::shared_ptr<storm::logic::Formula const> const& states) {
verifyProperties<ValueType>(input, [&input,&mpi] (std::shared_ptr<storm::logic::Formula const> const& formula, std::shared_ptr<storm::logic::Formula const> const& states) {
STORM_LOG_THROW(states->isInitialFormula(), storm::exceptions::NotSupportedException, "Exploration can only filter initial states.");
return storm::api::verifyWithExplorationEngine<ValueType>(input.model.get(), storm::api::createTask<ValueType>(formula, true));
return storm::api::verifyWithExplorationEngine<ValueType>(mpi.env, input.model.get(), storm::api::createTask<ValueType>(formula, true));
});
}
template <typename ValueType>
void verifyWithSparseEngine(std::shared_ptr<storm::models::ModelBase> const& model, SymbolicInput const& input) {
void verifyWithSparseEngine(std::shared_ptr<storm::models::ModelBase> const& model, SymbolicInput const& input, ModelProcessingInformation const& mpi) {
auto sparseModel = model->as<storm::models::sparse::Model<ValueType>>();
auto const& ioSettings = storm::settings::getModule<storm::settings::modules::IOSettings>();
verifyProperties<ValueType>(input,
[&sparseModel,&ioSettings] (std::shared_ptr<storm::logic::Formula const> const& formula, std::shared_ptr<storm::logic::Formula const> const& states) {
[&sparseModel,&ioSettings,&mpi] (std::shared_ptr<storm::logic::Formula const> const& formula, std::shared_ptr<storm::logic::Formula const> const& states) {
bool filterForInitialStates = states->isInitialFormula();
auto task = storm::api::createTask<ValueType>(formula, filterForInitialStates);
if (ioSettings.isExportSchedulerSet()) {
task.setProduceSchedulers(true);
}
std::unique_ptr<storm::modelchecker::CheckResult> result = storm::api::verifyWithSparseEngine<ValueType>(sparseModel, task);
std::unique_ptr<storm::modelchecker::CheckResult> result = storm::api::verifyWithSparseEngine<ValueType>(mpi.env, sparseModel, task);
std::unique_ptr<storm::modelchecker::CheckResult> filter;
if (filterForInitialStates) {
filter = std::make_unique<storm::modelchecker::ExplicitQualitativeCheckResult>(sparseModel->getInitialStates());
} else {
filter = storm::api::verifyWithSparseEngine<ValueType>(sparseModel, storm::api::createTask<ValueType>(states, false));
filter = storm::api::verifyWithSparseEngine<ValueType>(mpi.env, sparseModel, storm::api::createTask<ValueType>(states, false));
}
if (result && filter) {
result->filter(filter->asQualitativeCheckResult());
@ -854,19 +972,19 @@ namespace storm {
}
template <storm::dd::DdType DdType, typename ValueType>
void verifyWithHybridEngine(std::shared_ptr<storm::models::ModelBase> const& model, SymbolicInput const& input) {
verifyProperties<ValueType>(input, [&model] (std::shared_ptr<storm::logic::Formula const> const& formula, std::shared_ptr<storm::logic::Formula const> const& states) {
void verifyWithHybridEngine(std::shared_ptr<storm::models::ModelBase> const& model, SymbolicInput const& input, ModelProcessingInformation const& mpi) {
verifyProperties<ValueType>(input, [&model,&mpi] (std::shared_ptr<storm::logic::Formula const> const& formula, std::shared_ptr<storm::logic::Formula const> const& states) {
bool filterForInitialStates = states->isInitialFormula();
auto task = storm::api::createTask<ValueType>(formula, filterForInitialStates);
auto symbolicModel = model->as<storm::models::symbolic::Model<DdType, ValueType>>();
std::unique_ptr<storm::modelchecker::CheckResult> result = storm::api::verifyWithHybridEngine<DdType, ValueType>(symbolicModel, task);
std::unique_ptr<storm::modelchecker::CheckResult> result = storm::api::verifyWithHybridEngine<DdType, ValueType>(mpi.env, symbolicModel, task);
std::unique_ptr<storm::modelchecker::CheckResult> filter;
if (filterForInitialStates) {
filter = std::make_unique<storm::modelchecker::SymbolicQualitativeCheckResult<DdType>>(symbolicModel->getReachableStates(), symbolicModel->getInitialStates());
} else {
filter = storm::api::verifyWithHybridEngine<DdType, ValueType>(symbolicModel, storm::api::createTask<ValueType>(states, false));
filter = storm::api::verifyWithHybridEngine<DdType, ValueType>(mpi.env, symbolicModel, storm::api::createTask<ValueType>(states, false));
}
if (result && filter) {
result->filter(filter->asQualitativeCheckResult());
@ -876,19 +994,19 @@ namespace storm {
}
template <storm::dd::DdType DdType, typename ValueType>
void verifyWithDdEngine(std::shared_ptr<storm::models::ModelBase> const& model, SymbolicInput const& input) {
verifyProperties<ValueType>(input, [&model] (std::shared_ptr<storm::logic::Formula const> const& formula, std::shared_ptr<storm::logic::Formula const> const& states) {
void verifyWithDdEngine(std::shared_ptr<storm::models::ModelBase> const& model, SymbolicInput const& input, ModelProcessingInformation const& mpi) {
verifyProperties<ValueType>(input, [&model,&mpi] (std::shared_ptr<storm::logic::Formula const> const& formula, std::shared_ptr<storm::logic::Formula const> const& states) {
bool filterForInitialStates = states->isInitialFormula();
auto task = storm::api::createTask<ValueType>(formula, filterForInitialStates);
auto symbolicModel = model->as<storm::models::symbolic::Model<DdType, ValueType>>();
std::unique_ptr<storm::modelchecker::CheckResult> result = storm::api::verifyWithDdEngine<DdType, ValueType>(symbolicModel, storm::api::createTask<ValueType>(formula, true));
std::unique_ptr<storm::modelchecker::CheckResult> result = storm::api::verifyWithDdEngine<DdType, ValueType>(mpi.env, symbolicModel, storm::api::createTask<ValueType>(formula, true));
std::unique_ptr<storm::modelchecker::CheckResult> filter;
if (filterForInitialStates) {
filter = std::make_unique<storm::modelchecker::SymbolicQualitativeCheckResult<DdType>>(symbolicModel->getReachableStates(), symbolicModel->getInitialStates());
} else {
filter = storm::api::verifyWithDdEngine<DdType, ValueType>(symbolicModel, storm::api::createTask<ValueType>(states, false));
filter = storm::api::verifyWithDdEngine<DdType, ValueType>(mpi.env, symbolicModel, storm::api::createTask<ValueType>(states, false));
}
if (result && filter) {
result->filter(filter->asQualitativeCheckResult());
@ -898,48 +1016,47 @@ namespace storm {
}
template <storm::dd::DdType DdType, typename ValueType>
void verifyWithAbstractionRefinementEngine(std::shared_ptr<storm::models::ModelBase> const& model, SymbolicInput const& input) {
verifyProperties<ValueType>(input, [&model] (std::shared_ptr<storm::logic::Formula const> const& formula, std::shared_ptr<storm::logic::Formula const> const& states) {
void verifyWithAbstractionRefinementEngine(std::shared_ptr<storm::models::ModelBase> const& model, SymbolicInput const& input, ModelProcessingInformation const& mpi) {
verifyProperties<ValueType>(input, [&model,&mpi] (std::shared_ptr<storm::logic::Formula const> const& formula, std::shared_ptr<storm::logic::Formula const> const& states) {
STORM_LOG_THROW(states->isInitialFormula(), storm::exceptions::NotSupportedException, "Abstraction-refinement can only filter initial states.");
auto symbolicModel = model->as<storm::models::symbolic::Model<DdType, ValueType>>();
return storm::api::verifyWithAbstractionRefinementEngine<DdType, ValueType>(symbolicModel, storm::api::createTask<ValueType>(formula, true));
return storm::api::verifyWithAbstractionRefinementEngine<DdType, ValueType>(mpi.env, symbolicModel, storm::api::createTask<ValueType>(formula, true));
});
}
template <storm::dd::DdType DdType, typename ValueType>
typename std::enable_if<DdType != storm::dd::DdType::CUDD || std::is_same<ValueType, double>::value, void>::type verifySymbolicModel(std::shared_ptr<storm::models::ModelBase> const& model, SymbolicInput const& input, storm::settings::modules::CoreSettings const& coreSettings) {
storm::settings::modules::CoreSettings::Engine engine = coreSettings.getEngine();;
if (engine == storm::settings::modules::CoreSettings::Engine::Hybrid) {
verifyWithHybridEngine<DdType, ValueType>(model, input);
} else if (engine == storm::settings::modules::CoreSettings::Engine::Dd) {
verifyWithDdEngine<DdType, ValueType>(model, input);
typename std::enable_if<DdType != storm::dd::DdType::CUDD || std::is_same<ValueType, double>::value, void>::type verifySymbolicModel(std::shared_ptr<storm::models::ModelBase> const& model, SymbolicInput const& input, ModelProcessingInformation const& mpi) {
if (mpi.engine == storm::utility::Engine::Hybrid) {
verifyWithHybridEngine<DdType, ValueType>(model, input, mpi);
} else if (mpi.engine == storm::utility::Engine::Dd) {
verifyWithDdEngine<DdType, ValueType>(model, input, mpi);
} else {
verifyWithAbstractionRefinementEngine<DdType, ValueType>(model, input);
verifyWithAbstractionRefinementEngine<DdType, ValueType>(model, input, mpi);
}
}
template <storm::dd::DdType DdType, typename ValueType>
typename std::enable_if<DdType == storm::dd::DdType::CUDD && !std::is_same<ValueType, double>::value, void>::type verifySymbolicModel(std::shared_ptr<storm::models::ModelBase> const& model, SymbolicInput const& input, storm::settings::modules::CoreSettings const& coreSettings) {
typename std::enable_if<DdType == storm::dd::DdType::CUDD && !std::is_same<ValueType, double>::value, void>::type verifySymbolicModel(std::shared_ptr<storm::models::ModelBase> const&, SymbolicInput const&, ModelProcessingInformation const&) {
STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "CUDD does not support the selected data-type.");
}
template <storm::dd::DdType DdType, typename ValueType>
void verifyModel(std::shared_ptr<storm::models::ModelBase> const& model, SymbolicInput const& input, storm::settings::modules::CoreSettings const& coreSettings) {
void verifyModel(std::shared_ptr<storm::models::ModelBase> const& model, SymbolicInput const& input, ModelProcessingInformation const& mpi) {
if (model->isSparseModel()) {
verifyWithSparseEngine<ValueType>(model, input);
verifyWithSparseEngine<ValueType>(model, input, mpi);
} else {
STORM_LOG_ASSERT(model->isSymbolicModel(), "Unexpected model type.");
verifySymbolicModel<DdType, ValueType>(model, input, coreSettings);
verifySymbolicModel<DdType, ValueType>(model, input, mpi);
}
}
template <storm::dd::DdType DdType, typename BuildValueType, typename VerificationValueType = BuildValueType>
std::shared_ptr<storm::models::ModelBase> buildPreprocessExportModelWithValueTypeAndDdlib(SymbolicInput const& input, storm::settings::modules::CoreSettings::Engine engine) {
std::shared_ptr<storm::models::ModelBase> buildPreprocessExportModelWithValueTypeAndDdlib(SymbolicInput const& input, ModelProcessingInformation const& mpi) {
auto ioSettings = storm::settings::getModule<storm::settings::modules::IOSettings>();
auto buildSettings = storm::settings::getModule<storm::settings::modules::BuildSettings>();
std::shared_ptr<storm::models::ModelBase> model;
if (!buildSettings.isNoBuildModelSet()) {
model = buildModel<DdType, BuildValueType>(engine, input, ioSettings);
model = buildModel<DdType, BuildValueType>(input, ioSettings, mpi);
}
if (model) {
@ -949,7 +1066,7 @@ namespace storm {
STORM_LOG_THROW(model || input.properties.empty(), storm::exceptions::InvalidSettingsException, "No input model.");
if (model) {
auto preprocessingResult = preprocessModel<DdType, BuildValueType, VerificationValueType>(model, input);
auto preprocessingResult = preprocessModel<DdType, BuildValueType, VerificationValueType>(model, input, mpi);
if (preprocessingResult.second) {
model = preprocessingResult.first;
model->printModelInformationToStream(std::cout);
@ -960,50 +1077,48 @@ namespace storm {
}
template <storm::dd::DdType DdType, typename BuildValueType, typename VerificationValueType = BuildValueType>
void processInputWithValueTypeAndDdlib(SymbolicInput const& input) {
auto coreSettings = storm::settings::getModule<storm::settings::modules::CoreSettings>();
void processInputWithValueTypeAndDdlib(SymbolicInput const& input, ModelProcessingInformation const& mpi) {
auto abstractionSettings = storm::settings::getModule<storm::settings::modules::AbstractionSettings>();
auto counterexampleSettings = storm::settings::getModule<storm::settings::modules::CounterexampleGeneratorSettings>();
// For several engines, no model building step is performed, but the verification is started right away.
storm::settings::modules::CoreSettings::Engine engine = coreSettings.getEngine();
if (engine == storm::settings::modules::CoreSettings::Engine::AbstractionRefinement && abstractionSettings.getAbstractionRefinementMethod() == storm::settings::modules::AbstractionSettings::Method::Games) {
verifyWithAbstractionRefinementEngine<DdType, VerificationValueType>(input);
} else if (engine == storm::settings::modules::CoreSettings::Engine::Exploration) {
verifyWithExplorationEngine<VerificationValueType>(input);
if (mpi.engine == storm::utility::Engine::AbstractionRefinement && abstractionSettings.getAbstractionRefinementMethod() == storm::settings::modules::AbstractionSettings::Method::Games) {
verifyWithAbstractionRefinementEngine<DdType, VerificationValueType>(input, mpi);
} else if (mpi.engine == storm::utility::Engine::Exploration) {
verifyWithExplorationEngine<VerificationValueType>(input, mpi);
} else {
std::shared_ptr<storm::models::ModelBase> model = buildPreprocessExportModelWithValueTypeAndDdlib<DdType, BuildValueType, VerificationValueType>(input, engine);
std::shared_ptr<storm::models::ModelBase> model = buildPreprocessExportModelWithValueTypeAndDdlib<DdType, BuildValueType, VerificationValueType>(input, mpi);
if (model) {
if (counterexampleSettings.isCounterexampleSet()) {
generateCounterexamples<VerificationValueType>(model, input);
} else {
verifyModel<DdType, VerificationValueType>(model, input, coreSettings);
verifyModel<DdType, VerificationValueType>(model, input, mpi);
}
}
}
}
template <typename ValueType>
void processInputWithValueType(SymbolicInput const& input) {
auto coreSettings = storm::settings::getModule<storm::settings::modules::CoreSettings>();
auto generalSettings = storm::settings::getModule<storm::settings::modules::GeneralSettings>();
auto bisimulationSettings = storm::settings::getModule<storm::settings::modules::BisimulationSettings>();
void processInputWithValueType(SymbolicInput const& input, ModelProcessingInformation const& mpi) {
if (coreSettings.getDdLibraryType() == storm::dd::DdType::CUDD && coreSettings.isDdLibraryTypeSetFromDefaultValue() && generalSettings.isExactSet()) {
STORM_LOG_INFO("Switching to DD library sylvan to allow for rational arithmetic.");
processInputWithValueTypeAndDdlib<storm::dd::DdType::Sylvan, storm::RationalNumber>(input);
} else if (coreSettings.getDdLibraryType() == storm::dd::DdType::CUDD && coreSettings.isDdLibraryTypeSetFromDefaultValue() && std::is_same<ValueType, double>::value && generalSettings.isBisimulationSet() && bisimulationSettings.useExactArithmeticInDdBisimulation()) {
STORM_LOG_INFO("Switching to DD library sylvan to allow for rational arithmetic.");
processInputWithValueTypeAndDdlib<storm::dd::DdType::Sylvan, storm::RationalNumber, double>(input);
} else if (coreSettings.getDdLibraryType() == storm::dd::DdType::CUDD) {
processInputWithValueTypeAndDdlib<storm::dd::DdType::CUDD, double>(input);
if (mpi.ddType == storm::dd::DdType::CUDD) {
STORM_LOG_ASSERT(mpi.verificationValueType == ModelProcessingInformation::ValueType::FinitePrecision && mpi.buildValueType == ModelProcessingInformation::ValueType::FinitePrecision && (std::is_same<ValueType, double>::value), "Unexpected value type for Dd library cudd.");
processInputWithValueTypeAndDdlib<storm::dd::DdType::CUDD, double>(input, mpi);
} else {
STORM_LOG_ASSERT(coreSettings.getDdLibraryType() == storm::dd::DdType::Sylvan, "Unknown DD library.");
processInputWithValueTypeAndDdlib<storm::dd::DdType::Sylvan, ValueType>(input);
STORM_LOG_ASSERT(mpi.ddType == storm::dd::DdType::Sylvan, "Unknown DD library.");
if (mpi.buildValueType == mpi.verificationValueType) {
processInputWithValueTypeAndDdlib<storm::dd::DdType::Sylvan, ValueType>(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<ValueType, double>::value) && mpi.buildValueType == ModelProcessingInformation::ValueType::Exact, storm::exceptions::InvalidArgumentException, "Unexpected combination of buildValueType and verificationValueType");
#ifdef STORM_HAVE_CARL
processInputWithValueTypeAndDdlib<storm::dd::DdType::Sylvan, storm::RationalNumber, double>(input, mpi);
#else
STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Unexpected buildValueType.");
#endif
}
}
}
}
}

4
src/storm-conv/api/storm-conv.cpp

@ -53,6 +53,10 @@ namespace storm {
}
}
if (options.simplifyComposition) {
janiModel.simplifyComposition();
}
if (options.flatten) {
std::shared_ptr<storm::utility::solver::SmtSolverFactory> smtSolverFactory;
if (storm::settings::hasModule<storm::settings::modules::CoreSettings>()) {

4
src/storm-conv/converter/options/JaniConversionOptions.cpp

@ -3,11 +3,11 @@
namespace storm {
namespace converter {
JaniConversionOptions::JaniConversionOptions() : edgeAssignments(false), flatten(false), substituteConstants(true), localVars(false), globalVars(false), allowedModelFeatures(storm::jani::getAllKnownModelFeatures()), addPropertyConstants(true), replaceUnassignedVariablesWithConstants(false) {
JaniConversionOptions::JaniConversionOptions() : edgeAssignments(false), flatten(false), substituteConstants(true), localVars(false), globalVars(false), allowedModelFeatures(storm::jani::getAllKnownModelFeatures()), addPropertyConstants(true), replaceUnassignedVariablesWithConstants(false), simplifyComposition(false) {
// Intentionally left empty
}
JaniConversionOptions::JaniConversionOptions(storm::settings::modules::JaniExportSettings const& settings) : locationVariables(settings.getLocationVariables()), edgeAssignments(settings.isAllowEdgeAssignmentsSet()), flatten(settings.isExportFlattenedSet()), substituteConstants(true), localVars(settings.isLocalVarsSet()), globalVars(settings.isGlobalVarsSet()), allowedModelFeatures(storm::jani::getAllKnownModelFeatures()), addPropertyConstants(true), replaceUnassignedVariablesWithConstants(settings.isReplaceUnassignedVariablesWithConstantsSet()) {
JaniConversionOptions::JaniConversionOptions(storm::settings::modules::JaniExportSettings const& settings) : locationVariables(settings.getLocationVariables()), edgeAssignments(settings.isAllowEdgeAssignmentsSet()), flatten(settings.isExportFlattenedSet()), substituteConstants(true), localVars(settings.isLocalVarsSet()), globalVars(settings.isGlobalVarsSet()), allowedModelFeatures(storm::jani::getAllKnownModelFeatures()), addPropertyConstants(true), replaceUnassignedVariablesWithConstants(settings.isReplaceUnassignedVariablesWithConstantsSet()), simplifyComposition(settings.isSimplifyCompositionSet()) {
if (settings.isEliminateFunctionsSet()) {
allowedModelFeatures.remove(storm::jani::ModelFeature::Functions);
}

4
src/storm-conv/converter/options/JaniConversionOptions.h

@ -44,6 +44,10 @@ namespace storm {
/// If set, local and global variables that are (a) not assigned to some value and (b) have a known initial value are replaced by constants.
bool replaceUnassignedVariablesWithConstants;
/// If set, attempts to simplify the system composition
bool simplifyComposition;
};
}
}

6
src/storm-conv/settings/modules/JaniExportSettings.cpp

@ -23,6 +23,7 @@ namespace storm {
const std::string JaniExportSettings::eliminateArraysOptionName = "remove-arrays";
const std::string JaniExportSettings::eliminateFunctionsOptionName = "remove-functions";
const std::string JaniExportSettings::replaceUnassignedVariablesWithConstantsOptionName = "replace-unassigned-vars";
const std::string JaniExportSettings::simplifyCompositionOptionName = "simplify-composition";
JaniExportSettings::JaniExportSettings() : ModuleSettings(moduleName) {
this->addOption(storm::settings::OptionBuilder(moduleName, locationVariablesOptionName, true, "Variables to export in the location").addArgument(storm::settings::ArgumentBuilder::createStringArgument("variables", "A comma separated list of automaton and local variable names seperated by a dot, e.g. A.x,B.y.").setDefaultValueString("").build()).build());
@ -34,6 +35,7 @@ namespace storm {
this->addOption(storm::settings::OptionBuilder(moduleName, eliminateArraysOptionName, false, "If set, transforms the model such that array variables/expressions are eliminated.").build());
this->addOption(storm::settings::OptionBuilder(moduleName, eliminateFunctionsOptionName, false, "If set, transforms the model such that functions are eliminated.").build());
this->addOption(storm::settings::OptionBuilder(moduleName, replaceUnassignedVariablesWithConstantsOptionName, false, "If set, local and global variables that are (a) not assigned to some value and (b) have a known initial value are replaced by constants.").build());
this->addOption(storm::settings::OptionBuilder(moduleName, simplifyCompositionOptionName, false, "If set, attempts to simplify the system composition.").build());
}
bool JaniExportSettings::isAllowEdgeAssignmentsSet() const {
@ -88,6 +90,10 @@ namespace storm {
return this->getOption(replaceUnassignedVariablesWithConstantsOptionName).getHasOptionBeenSet();
}
bool JaniExportSettings::isSimplifyCompositionSet() const {
return this->getOption(simplifyCompositionOptionName).getHasOptionBeenSet();
}
void JaniExportSettings::finalize() {
}

3
src/storm-conv/settings/modules/JaniExportSettings.h

@ -31,6 +31,8 @@ namespace storm {
bool isEliminateFunctionsSet() const;
bool isReplaceUnassignedVariablesWithConstantsSet() const;
bool isSimplifyCompositionSet() const;
std::vector<std::pair<std::string, std::string>> getLocationVariables() const;
@ -49,6 +51,7 @@ namespace storm {
static const std::string eliminateArraysOptionName;
static const std::string eliminateFunctionsOptionName;
static const std::string replaceUnassignedVariablesWithConstantsOptionName;
static const std::string simplifyCompositionOptionName;
};
}

58
src/storm-pars-cli/storm-pars.cpp

@ -23,7 +23,6 @@
#include "storm/exceptions/BaseException.h"
#include "storm/exceptions/InvalidSettingsException.h"
#include "storm/exceptions/InvalidSettingsException.h"
#include "storm/exceptions/NotSupportedException.h"
#include "storm/models/ModelBase.h"
@ -39,6 +38,7 @@
#include "storm/utility/initialize.h"
#include "storm/utility/Stopwatch.h"
#include "storm/utility/macros.h"
#include "storm/utility/Engine.h"
#include "storm/settings/modules/GeneralSettings.h"
#include "storm/settings/modules/CoreSettings.h"
@ -158,8 +158,7 @@ namespace storm {
}
template <typename ValueType>
PreprocessResult preprocessSparseModel(std::shared_ptr<storm::models::sparse::Model<ValueType>> const& model, SymbolicInput const& input) {
auto generalSettings = storm::settings::getModule<storm::settings::modules::GeneralSettings>();
PreprocessResult preprocessSparseModel(std::shared_ptr<storm::models::sparse::Model<ValueType>> const& model, SymbolicInput const& input, storm::cli::ModelProcessingInformation const& mpi) {
auto bisimulationSettings = storm::settings::getModule<storm::settings::modules::BisimulationSettings>();
auto parametricSettings = storm::settings::getModule<storm::settings::modules::ParametricSettings>();
auto transformationSettings = storm::settings::getModule<storm::settings::modules::TransformationSettings>();
@ -171,7 +170,7 @@ namespace storm {
result.changed = true;
}
if (generalSettings.isBisimulationSet()) {
if (mpi.applyBisimulation) {
result.model = storm::cli::preprocessSparseModelBisimulation(result.model->template as<storm::models::sparse::Model<ValueType>>(), input, bisimulationSettings);
result.changed = true;
}
@ -200,28 +199,26 @@ namespace storm {
}
template <storm::dd::DdType DdType, typename ValueType>
PreprocessResult preprocessDdModel(std::shared_ptr<storm::models::symbolic::Model<DdType, ValueType>> const& model, SymbolicInput const& input) {
auto coreSettings = storm::settings::getModule<storm::settings::modules::CoreSettings>();
auto generalSettings = storm::settings::getModule<storm::settings::modules::GeneralSettings>();
PreprocessResult preprocessDdModel(std::shared_ptr<storm::models::symbolic::Model<DdType, ValueType>> const& model, SymbolicInput const& input, storm::cli::ModelProcessingInformation const& mpi) {
auto bisimulationSettings = storm::settings::getModule<storm::settings::modules::BisimulationSettings>();
PreprocessResult result(model, false);
if (coreSettings.getEngine() == storm::settings::modules::CoreSettings::Engine::Hybrid) {
if (mpi.engine == storm::utility::Engine::Hybrid) {
// Currently, hybrid engine for parametric models just refers to building the model symbolically.
STORM_LOG_INFO("Translating symbolic model to sparse model...");
result.model = storm::api::transformSymbolicToSparseModel(model);
result.changed = true;
// Invoke preprocessing on the sparse model
PreprocessResult sparsePreprocessingResult = storm::pars::preprocessSparseModel<ValueType>(result.model->as<storm::models::sparse::Model<ValueType>>(), input);
PreprocessResult sparsePreprocessingResult = storm::pars::preprocessSparseModel<ValueType>(result.model->as<storm::models::sparse::Model<ValueType>>(), input, mpi);
if (sparsePreprocessingResult.changed) {
result.model = sparsePreprocessingResult.model;
result.formulas = sparsePreprocessingResult.formulas;
}
} else {
STORM_LOG_ASSERT(coreSettings.getEngine() == storm::settings::modules::CoreSettings::Engine::Dd, "Expected Dd engine.");
if (generalSettings.isBisimulationSet()) {
result.model = storm::cli::preprocessDdModelBisimulation(result.model->template as<storm::models::symbolic::Model<DdType, ValueType>>(), input, bisimulationSettings);
STORM_LOG_ASSERT(mpi.engine == storm::utility::Engine::Dd, "Expected Dd engine.");
if (mpi.applyBisimulation) {
result.model = storm::cli::preprocessDdModelBisimulation(result.model->template as<storm::models::symbolic::Model<DdType, ValueType>>(), input, bisimulationSettings, mpi);
result.changed = true;
}
}
@ -229,15 +226,15 @@ namespace storm {
}
template <storm::dd::DdType DdType, typename ValueType>
PreprocessResult preprocessModel(std::shared_ptr<storm::models::ModelBase> const& model, SymbolicInput const& input) {
PreprocessResult preprocessModel(std::shared_ptr<storm::models::ModelBase> const& model, SymbolicInput const& input, storm::cli::ModelProcessingInformation const& mpi) {
storm::utility::Stopwatch preprocessingWatch(true);
PreprocessResult result(model, false);
if (model->isSparseModel()) {
result = storm::pars::preprocessSparseModel<ValueType>(result.model->as<storm::models::sparse::Model<ValueType>>(), input);
result = storm::pars::preprocessSparseModel<ValueType>(result.model->as<storm::models::sparse::Model<ValueType>>(), input, mpi);
} else {
STORM_LOG_ASSERT(model->isSymbolicModel(), "Unexpected model type.");
result = storm::pars::preprocessDdModel<DdType, ValueType>(result.model->as<storm::models::symbolic::Model<DdType, ValueType>>(), input);
result = storm::pars::preprocessDdModel<DdType, ValueType>(result.model->as<storm::models::symbolic::Model<DdType, ValueType>>(), input, mpi);
}
if (result.changed) {
@ -565,22 +562,18 @@ namespace storm {
}
}
template <storm::dd::DdType DdType, typename ValueType>
void processInputWithValueTypeAndDdlib(SymbolicInput& input) {
auto coreSettings = storm::settings::getModule<storm::settings::modules::CoreSettings>();
void processInputWithValueTypeAndDdlib(SymbolicInput& input, storm::cli::ModelProcessingInformation const& mpi) {
auto ioSettings = storm::settings::getModule<storm::settings::modules::IOSettings>();
auto buildSettings = storm::settings::getModule<storm::settings::modules::BuildSettings>();
auto parSettings = storm::settings::getModule<storm::settings::modules::ParametricSettings>();
auto monSettings = storm::settings::getModule<storm::settings::modules::MonotonicitySettings>();
auto engine = coreSettings.getEngine();
STORM_LOG_THROW(engine == storm::settings::modules::CoreSettings::Engine::Sparse || engine == storm::settings::modules::CoreSettings::Engine::Hybrid || engine == storm::settings::modules::CoreSettings::Engine::Dd, storm::exceptions::InvalidSettingsException, "The selected engine is not supported for parametric models.");
STORM_LOG_THROW(mpi.engine == storm::utility::Engine::Sparse || mpi.engine == storm::utility::Engine::Hybrid || mpi.engine == storm::utility::Engine::Dd, storm::exceptions::InvalidSettingsException, "The selected engine is not supported for parametric models.");
std::shared_ptr<storm::models::ModelBase> model;
if (!buildSettings.isNoBuildModelSet()) {
model = storm::cli::buildModel<DdType, ValueType>(engine, input, ioSettings);
model = storm::cli::buildModel<DdType, ValueType>(input, ioSettings, mpi);
}
if (model) {
@ -592,7 +585,7 @@ namespace storm {
if (model) {
auto preprocessingResult = storm::pars::preprocessModel<DdType, ValueType>(model, input);
auto preprocessingResult = storm::pars::preprocessModel<DdType, ValueType>(model, input, mpi);
if (preprocessingResult.changed) {
model = preprocessingResult.model;
@ -646,7 +639,7 @@ namespace storm {
}
if (model) {
auto preprocessingResult = storm::pars::preprocessModel<DdType, ValueType>(model, input);
auto preprocessingResult = storm::pars::preprocessModel<DdType, ValueType>(model, input, mpi);
if (preprocessingResult.changed) {
model = preprocessingResult.model;
@ -783,15 +776,16 @@ namespace storm {
void processOptions() {
// Start by setting some urgent options (log levels, resources, etc.)
storm::cli::setUrgentOptions();
// Parse and preprocess symbolic input (PRISM, JANI, properties, etc.)
SymbolicInput symbolicInput = storm::cli::parseAndPreprocessSymbolicInput();
auto coreSettings = storm::settings::getModule<storm::settings::modules::CoreSettings>();
auto engine = coreSettings.getEngine();
STORM_LOG_WARN_COND(engine != storm::settings::modules::CoreSettings::Engine::Dd || engine != storm::settings::modules::CoreSettings::Engine::Hybrid || coreSettings.getDdLibraryType() == storm::dd::DdType::Sylvan, "The selected DD library does not support parametric models. Switching to Sylvan...");
processInputWithValueTypeAndDdlib<storm::dd::DdType::Sylvan, storm::RationalFunction>(symbolicInput);
STORM_LOG_WARN_COND(engine != storm::utility::Engine::Dd || engine != storm::utility::Engine::Hybrid || coreSettings.getDdLibraryType() == storm::dd::DdType::Sylvan, "The selected DD library does not support parametric models. Switching to Sylvan...");
// 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);
processInputWithValueTypeAndDdlib<storm::dd::DdType::Sylvan, storm::RationalFunction>(symbolicInput, mpi);
}
}

29
src/storm-parsers/api/model_descriptions.cpp

@ -22,21 +22,7 @@ namespace storm {
return program;
}
std::pair<storm::jani::Model, std::map<std::string, storm::jani::Property>> parseJaniModel(std::string const& filename) {
storm::jani::ModelFeatures features;
// Add derived-operators and state-exit-rewards as these can be handled by all model builders
features.add(storm::jani::ModelFeature::DerivedOperators);
features.add(storm::jani::ModelFeature::StateExitRewards);
auto parsedResult = parseJaniModel(filename, features);
std::map<std::string, storm::jani::Property> propertyMap;
for (auto const& property : parsedResult.second) {
propertyMap.emplace(property.getName(), property);
}
return std::make_pair(std::move(parsedResult.first), std::move(propertyMap));
}
std::pair<storm::jani::Model, std::vector<storm::jani::Property>> parseJaniModel(std::string const& filename, storm::jani::ModelFeatures const& allowedFeatures, boost::optional<std::vector<std::string>> const& propertyFilter) {
std::pair<storm::jani::Model, std::vector<storm::jani::Property>> parseJaniModel(std::string const& filename, boost::optional<std::vector<std::string>> const& propertyFilter) {
bool parseProperties = !propertyFilter.is_initialized() || !propertyFilter.get().empty();
std::pair<storm::jani::Model, std::vector<storm::jani::Property>> modelAndFormulae = storm::parser::JaniParser::parse(filename, parseProperties);
@ -57,13 +43,20 @@ namespace storm {
}
modelAndFormulae.second = std::move(newProperties);
}
modelAndFormulae.first.checkValid();
auto nonEliminatedFeatures = modelAndFormulae.first.restrictToFeatures(allowedFeatures, modelAndFormulae.second);
STORM_LOG_THROW(nonEliminatedFeatures.empty(), storm::exceptions::NotSupportedException, "The used model feature(s) " << nonEliminatedFeatures.toString() << " is/are not in the list of allowed features.");
return modelAndFormulae;
}
std::pair<storm::jani::Model, std::vector<storm::jani::Property>> parseJaniModel(std::string const& filename, storm::jani::ModelFeatures const& supportedFeatures, boost::optional<std::vector<std::string>> const& propertyFilter) {
auto modelAndFormulae = parseJaniModel(filename, propertyFilter);
simplifyJaniModel(modelAndFormulae.first, modelAndFormulae.second, supportedFeatures);
return modelAndFormulae;
}
void simplifyJaniModel(storm::jani::Model& model, std::vector<storm::jani::Property>& properties , storm::jani::ModelFeatures const& supportedFeatures) {
auto nonEliminatedFeatures = model.restrictToFeatures(supportedFeatures, properties);
STORM_LOG_THROW(nonEliminatedFeatures.empty(), storm::exceptions::NotSupportedException, "The used model feature(s) " << nonEliminatedFeatures.toString() << " is/are not in the list of supported features.");
}
}
}

4
src/storm-parsers/api/model_descriptions.h

@ -19,7 +19,9 @@ namespace storm {
storm::prism::Program parseProgram(std::string const& filename, bool prismCompatibility = false, bool simplify = true);
std::pair<storm::jani::Model, std::map<std::string, storm::jani::Property>> parseJaniModel(std::string const& filename);
std::pair<storm::jani::Model, std::vector<storm::jani::Property>> parseJaniModel(std::string const& filename, boost::optional<std::vector<std::string>> const& propertyFilter = boost::none);
std::pair<storm::jani::Model, std::vector<storm::jani::Property>> parseJaniModel(std::string const& filename, storm::jani::ModelFeatures const& allowedFeatures, boost::optional<std::vector<std::string>> const& propertyFilter = boost::none);
void simplifyJaniModel(storm::jani::Model& model, std::vector<storm::jani::Property>& properties , storm::jani::ModelFeatures const& supportedFeatures);
}
}

10
src/storm-pomdp-cli/storm-pomdp.cpp

@ -98,12 +98,12 @@ int main(const int argc, const char** argv) {
auto const& coreSettings = storm::settings::getModule<storm::settings::modules::CoreSettings>();
auto const& pomdpSettings = storm::settings::getModule<storm::settings::modules::POMDPSettings>();
// For several engines, no model building step is performed, but the verification is started right away.
storm::settings::modules::CoreSettings::Engine engine = coreSettings.getEngine();
storm::cli::SymbolicInput symbolicInput = storm::cli::parseAndPreprocessSymbolicInput();
auto symbolicInput = storm::cli::parseSymbolicInput();
auto mpi = storm::cli::getModelProcessingInformation(symbolicInput);
symbolicInput = storm::cli::preprocessSymbolicInput(symbolicInput, mpi.engine);
// We should not export here if we are going to do some processing first.
auto model = storm::cli::buildPreprocessExportModelWithValueTypeAndDdlib<storm::dd::DdType::Sylvan, storm::RationalNumber>(symbolicInput, engine);
auto model = storm::cli::buildPreprocessExportModelWithValueTypeAndDdlib<storm::dd::DdType::Sylvan, storm::RationalNumber>(symbolicInput, mpi);
STORM_LOG_THROW(model && model->getType() == storm::models::ModelType::Pomdp, storm::exceptions::WrongFormatException, "Expected a POMDP.");
std::shared_ptr<storm::models::sparse::Pomdp<storm::RationalNumber>> pomdp = model->template as<storm::models::sparse::Pomdp<storm::RationalNumber>>();
storm::transformer::MakePOMDPCanonic<storm::RationalNumber> makeCanonic(*pomdp);

3
src/storm/utility/exprtk.h → src/storm/adapters/ExprttkAdapter.h

@ -5,6 +5,9 @@
#pragma GCC diagnostic push
// exprtk should be case sensitive in our case.
#define exprtk_disable_caseinsensitivity
#include "exprtk.hpp"
#pragma GCC diagnostic pop

6
src/storm/api/bisimulation.h

@ -56,7 +56,7 @@ namespace storm {
}
template <storm::dd::DdType DdType, typename ValueType, typename ExportValueType = ValueType>
typename std::enable_if<DdType == storm::dd::DdType::Sylvan || std::is_same<ValueType, double>::value, std::shared_ptr<storm::models::Model<ExportValueType>>>::type performBisimulationMinimization(std::shared_ptr<storm::models::symbolic::Model<DdType, ValueType>> const& model, std::vector<std::shared_ptr<storm::logic::Formula const>> const& formulas, storm::storage::BisimulationType const& bisimulationType = storm::storage::BisimulationType::Strong, storm::dd::bisimulation::SignatureMode const& mode = storm::dd::bisimulation::SignatureMode::Eager) {
typename std::enable_if<DdType == storm::dd::DdType::Sylvan || std::is_same<ValueType, double>::value, std::shared_ptr<storm::models::Model<ExportValueType>>>::type performBisimulationMinimization(std::shared_ptr<storm::models::symbolic::Model<DdType, ValueType>> const& model, std::vector<std::shared_ptr<storm::logic::Formula const>> const& formulas, storm::storage::BisimulationType const& bisimulationType = storm::storage::BisimulationType::Strong, storm::dd::bisimulation::SignatureMode const& mode = storm::dd::bisimulation::SignatureMode::Eager, storm::dd::bisimulation::QuotientFormat const& quotientFormat = storm::dd::bisimulation::QuotientFormat::Dd) {
STORM_LOG_THROW(model->isOfType(storm::models::ModelType::Dtmc) || model->isOfType(storm::models::ModelType::Ctmc) || model->isOfType(storm::models::ModelType::Mdp) || model->isOfType(storm::models::ModelType::MarkovAutomaton), storm::exceptions::NotSupportedException, "Symbolic bisimulation minimization is currently only available for DTMCs, CTMCs, MDPs and MAs.");
STORM_LOG_THROW(bisimulationType == storm::storage::BisimulationType::Strong, storm::exceptions::NotSupportedException, "Currently only strong bisimulation is supported.");
@ -66,11 +66,11 @@ namespace storm {
storm::dd::BisimulationDecomposition<DdType, ValueType, ExportValueType> decomposition(*model, formulas, bisimulationType);
decomposition.compute(mode);
return decomposition.getQuotient();
return decomposition.getQuotient(quotientFormat);
}
template <storm::dd::DdType DdType, typename ValueType, typename ExportValueType = ValueType>
typename std::enable_if<DdType != storm::dd::DdType::Sylvan && !std::is_same<ValueType, double>::value, std::shared_ptr<storm::models::Model<ExportValueType>>>::type performBisimulationMinimization(std::shared_ptr<storm::models::symbolic::Model<DdType, ValueType>> const& model, std::vector<std::shared_ptr<storm::logic::Formula const>> const& formulas, storm::storage::BisimulationType const& bisimulationType = storm::storage::BisimulationType::Strong, storm::dd::bisimulation::SignatureMode const& mode = storm::dd::bisimulation::SignatureMode::Eager) {
typename std::enable_if<DdType != storm::dd::DdType::Sylvan && !std::is_same<ValueType, double>::value, std::shared_ptr<storm::models::Model<ExportValueType>>>::type performBisimulationMinimization(std::shared_ptr<storm::models::symbolic::Model<DdType, ValueType>> const& model, std::vector<std::shared_ptr<storm::logic::Formula const>> const& formulas, storm::storage::BisimulationType const& bisimulationType = storm::storage::BisimulationType::Strong, storm::dd::bisimulation::SignatureMode const& mode = storm::dd::bisimulation::SignatureMode::Eager, storm::dd::bisimulation::QuotientFormat const& quotientFormat = storm::dd::bisimulation::QuotientFormat::Dd) {
STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Symbolic bisimulation minimization is not supported for this combination of DD library and value type.");
return nullptr;
}

8
src/storm/api/builder.h

@ -33,13 +33,7 @@ namespace storm {
namespace api {
inline storm::jani::ModelFeatures getSupportedJaniFeatures(storm::builder::BuilderType const& builderType) {
storm::jani::ModelFeatures features;
features.add(storm::jani::ModelFeature::DerivedOperators);
features.add(storm::jani::ModelFeature::StateExitRewards);
if (builderType == storm::builder::BuilderType::Explicit) {
features.add(storm::jani::ModelFeature::Arrays);
}
return features;
return storm::builder::getSupportedJaniFeatures(builderType);
}
template<storm::dd::DdType LibraryType, typename ValueType>

2
src/storm/api/transformation.h

@ -105,6 +105,8 @@ namespace storm {
return storm::transformer::SymbolicMdpToSparseMdpTransformer<Type, ValueType>::translate(*symbolicModel->template as<storm::models::symbolic::Mdp<Type, ValueType>>(), formulas);
case storm::models::ModelType::Ctmc:
return storm::transformer::SymbolicCtmcToSparseCtmcTransformer<Type, ValueType>::translate(*symbolicModel->template as<storm::models::symbolic::Ctmc<Type, ValueType>>(), formulas);
case storm::models::ModelType::MarkovAutomaton:
return storm::transformer::SymbolicMaToSparseMaTransformer<Type, ValueType>::translate(*symbolicModel->template as<storm::models::symbolic::MarkovAutomaton<Type, ValueType>>(), formulas);
default:
STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Transformation of symbolic " << symbolicModel->getType() << " to sparse model is not supported.");
}

25
src/storm/api/verification.h

@ -13,6 +13,7 @@
#include "storm/modelchecker/prctl/HybridMdpPrctlModelChecker.h"
#include "storm/modelchecker/prctl/SymbolicMdpPrctlModelChecker.h"
#include "storm/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.h"
#include "storm/modelchecker/csl/HybridMarkovAutomatonCslModelChecker.h"
#include "storm/modelchecker/abstraction/GameBasedMdpModelChecker.h"
#include "storm/modelchecker/abstraction/BisimulationAbstractionRefinementModelChecker.h"
#include "storm/modelchecker/exploration/SparseExplorationModelChecker.h"
@ -20,6 +21,7 @@
#include "storm/models/symbolic/Dtmc.h"
#include "storm/models/symbolic/Mdp.h"
#include "storm/models/symbolic/MarkovAutomaton.h"
#include "storm/models/sparse/Dtmc.h"
#include "storm/models/sparse/Mdp.h"
@ -327,6 +329,27 @@ namespace storm {
return verifyWithHybridEngine(env, mdp, task);
}
template<storm::dd::DdType DdType, typename ValueType>
typename std::enable_if<!std::is_same<ValueType, storm::RationalFunction>::value, std::unique_ptr<storm::modelchecker::CheckResult>>::type verifyWithHybridEngine(storm::Environment const& env, std::shared_ptr<storm::models::symbolic::MarkovAutomaton<DdType, ValueType>> const& ma, storm::modelchecker::CheckTask<storm::logic::Formula, ValueType> const& task) {
std::unique_ptr<storm::modelchecker::CheckResult> result;
storm::modelchecker::HybridMarkovAutomatonCslModelChecker<storm::models::symbolic::MarkovAutomaton<DdType, ValueType>> modelchecker(*ma);
if (modelchecker.canHandle(task)) {
result = modelchecker.check(env, task);
}
return result;
}
template<storm::dd::DdType DdType, typename ValueType>
typename std::enable_if<std::is_same<ValueType, storm::RationalFunction>::value, std::unique_ptr<storm::modelchecker::CheckResult>>::type verifyWithHybridEngine(storm::Environment const& , std::shared_ptr<storm::models::symbolic::MarkovAutomaton<DdType, ValueType>> const&, storm::modelchecker::CheckTask<storm::logic::Formula, ValueType> const&) {
STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Hybrid engine cannot verify MDPs with this data type.");
}
template<storm::dd::DdType DdType, typename ValueType>
std::unique_ptr<storm::modelchecker::CheckResult> verifyWithHybridEngine(std::shared_ptr<storm::models::symbolic::MarkovAutomaton<DdType, ValueType>> const& ma, storm::modelchecker::CheckTask<storm::logic::Formula, ValueType> const& task) {
Environment env;
return verifyWithHybridEngine(env, ma, task);
}
template<storm::dd::DdType DdType, typename ValueType>
std::unique_ptr<storm::modelchecker::CheckResult> verifyWithHybridEngine(storm::Environment const& env, std::shared_ptr<storm::models::symbolic::Model<DdType, ValueType>> const& model, storm::modelchecker::CheckTask<storm::logic::Formula, ValueType> const& task) {
std::unique_ptr<storm::modelchecker::CheckResult> result;
@ -336,6 +359,8 @@ namespace storm {
result = verifyWithHybridEngine(env, model->template as<storm::models::symbolic::Ctmc<DdType, ValueType>>(), task);
} else if (model->getType() == storm::models::ModelType::Mdp) {
result = verifyWithHybridEngine(env, model->template as<storm::models::symbolic::Mdp<DdType, ValueType>>(), task);
} else if (model->getType() == storm::models::ModelType::MarkovAutomaton) {
result = verifyWithHybridEngine(env, model->template as<storm::models::symbolic::MarkovAutomaton<DdType, ValueType>>(), task);
} else {
STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "The model type " << model->getType() << " is not supported by the hybrid engine.");
}

69
src/storm/builder/BuilderType.cpp

@ -0,0 +1,69 @@
#include "storm/builder/BuilderType.h"
#include "storm/storage/dd/DdType.h"
#include "storm/storage/jani/ModelFeatures.h"
#include "storm/generator/JaniNextStateGenerator.h"
#include "storm/generator/PrismNextStateGenerator.h"
#include "storm/builder/DdJaniModelBuilder.h"
#include "storm/builder/DdPrismModelBuilder.h"
#include "storm/builder/jit/ExplicitJitJaniModelBuilder.h"
#include "storm/utility/macros.h"
#include "storm/exceptions/UnexpectedException.h"
namespace storm {
namespace builder {
storm::jani::ModelFeatures getSupportedJaniFeatures(BuilderType const& builderType) {
// The supported jani features should be independent of the ValueType and Dd type. We just take sylvan and double for all.
storm::dd::DdType const ddType = storm::dd::DdType::Sylvan;
typedef double ValueType;
switch (builderType) {
case BuilderType::Explicit:
return storm::generator::JaniNextStateGenerator<ValueType>::getSupportedJaniFeatures();
case BuilderType::Dd:
return storm::builder::DdJaniModelBuilder<ddType, ValueType>::getSupportedJaniFeatures();
case BuilderType::Jit:
return storm::builder::jit::ExplicitJitJaniModelBuilder<ValueType>::getSupportedJaniFeatures();
}
STORM_LOG_THROW(false, storm::exceptions::UnexpectedException, "Unexpected builder type.");
}
template <typename ValueType>
bool canHandle(BuilderType const& builderType, storm::storage::SymbolicModelDescription const& modelDescription, boost::optional<std::vector<storm::jani::Property>> const& properties) {
storm::dd::DdType const ddType = storm::dd::DdType::Sylvan;
if (!modelDescription.hasModel()) {
// If there is no model to be build, we assume that the task of obtaining a model is either not required or can be accomplished somehow.
return true;
}
STORM_LOG_THROW(modelDescription.isPrismProgram() || modelDescription.isJaniModel(), storm::exceptions::UnexpectedException, "The model is neither PRISM nor Jani which is not expected.");
switch (builderType) {
case BuilderType::Explicit:
if (modelDescription.isPrismProgram()) {
return storm::generator::PrismNextStateGenerator<ValueType>::canHandle(modelDescription.asPrismProgram());
} else {
return storm::generator::JaniNextStateGenerator<ValueType>::canHandle(modelDescription.asJaniModel());
}
case BuilderType::Dd:
if (modelDescription.isPrismProgram()) {
return storm::builder::DdPrismModelBuilder<ddType, ValueType>::canHandle(modelDescription.asPrismProgram());
} else {
return storm::builder::DdJaniModelBuilder<ddType, ValueType>::canHandle(modelDescription.asJaniModel(), properties);
}
case BuilderType::Jit:
return storm::builder::jit::ExplicitJitJaniModelBuilder<ValueType>::canHandle(modelDescription.asJaniModel());
}
STORM_LOG_THROW(false, storm::exceptions::UnexpectedException, "Unhandled builderType.");
return false;
}
template bool canHandle<double>(BuilderType const& builderType, storm::storage::SymbolicModelDescription const& modelDescription, boost::optional<std::vector<storm::jani::Property>> const& properties);
template bool canHandle<storm::RationalNumber>(BuilderType const& builderType, storm::storage::SymbolicModelDescription const& modelDescription, boost::optional<std::vector<storm::jani::Property>> const& properties);
template bool canHandle<storm::RationalFunction>(BuilderType const& builderType, storm::storage::SymbolicModelDescription const& modelDescription, boost::optional<std::vector<storm::jani::Property>> const& properties);
}
}

15
src/storm/builder/BuilderType.h

@ -1,11 +1,26 @@
#pragma once
#include <vector>
#include <boost/optional.hpp>
#include "storm/storage/jani/Property.h"
#include "storm/storage/SymbolicModelDescription.h"
namespace storm {
namespace jani {
class ModelFeatures;
}
namespace builder {
enum class BuilderType {
Explicit,
Dd,
Jit
};
storm::jani::ModelFeatures getSupportedJaniFeatures(BuilderType const& builderType);
template <typename ValueType>
bool canHandle(BuilderType const& builderType, storm::storage::SymbolicModelDescription const& modelDescription, boost::optional<std::vector<storm::jani::Property>> const& properties = boost::none);
}
}

81
src/storm/builder/DdJaniModelBuilder.cpp

@ -14,6 +14,7 @@
#include "storm/storage/jani/AutomatonComposition.h"
#include "storm/storage/jani/ParallelComposition.h"
#include "storm/storage/jani/CompositionInformationVisitor.h"
#include "storm/storage/jani/ArrayEliminator.h"
#include "storm/storage/dd/Add.h"
#include "storm/storage/dd/Bdd.h"
@ -45,6 +46,57 @@
namespace storm {
namespace builder {
template <storm::dd::DdType Type, typename ValueType>
storm::jani::ModelFeatures DdJaniModelBuilder<Type, ValueType>::getSupportedJaniFeatures() {
storm::jani::ModelFeatures features;
features.add(storm::jani::ModelFeature::DerivedOperators);
features.add(storm::jani::ModelFeature::StateExitRewards);
// We do not add Functions and arrays as these should ideally be substituted before creating this generator.
// This is because functions or arrays may also occur in properties and the user of this builder should take care of that.
return features;
}
template <storm::dd::DdType Type, typename ValueType>
bool DdJaniModelBuilder<Type, ValueType>::canHandle(storm::jani::Model const& model, boost::optional<std::vector<storm::jani::Property>> const& properties) {
// Check jani features
auto features = model.getModelFeatures();
features.remove(storm::jani::ModelFeature::Arrays); // can be substituted
features.remove(storm::jani::ModelFeature::DerivedOperators);
features.remove(storm::jani::ModelFeature::Functions); // can be substituted
features.remove(storm::jani::ModelFeature::StateExitRewards);
if (!features.empty()) {
STORM_LOG_INFO("Symbolic engine can not build Jani model due to unsupported jani features.");
return false;
}
// Check assignment levels
if (model.usesAssignmentLevels()) {
STORM_LOG_INFO("Symbolic engine can not build Jani model due to assignment levels.");
return false;
}
// Check nonTrivial reward expressions
if (properties) {
std::set<std::string> rewardModels;
for (auto const& p : properties.get()) {
p.gatherReferencedRewardModels(rewardModels);
}
for (auto const& r : rewardModels) {
if (model.isNonTrivialRewardModelExpression(r)) {
STORM_LOG_INFO("Symbolic engine can not build Jani model due to non-trivial reward expressions.");
return false;
}
}
} else {
if (model.hasNonTrivialRewardExpression()) {
STORM_LOG_INFO("Symbolic engine can not build Jani model due to non-trivial reward expressions.");
return false;
}
}
// There probably are more cases where the model is unsupported. However, checking these is often more involved.
// As this method is supposed to be a quick check, we just return true at this point.
return true;
}
template <storm::dd::DdType Type, typename ValueType>
DdJaniModelBuilder<Type, ValueType>::Options::Options(bool buildAllLabels, bool buildAllRewardModels, bool applyMaximumProgressAssumption) : buildAllLabels(buildAllLabels), buildAllRewardModels(buildAllRewardModels), applyMaximumProgressAssumption(applyMaximumProgressAssumption), rewardModelsToBuild(), constantDefinitions() {
// Intentionally left empty.
@ -887,7 +939,7 @@ namespace storm {
inputEnabledActionIndices.insert(actionInformation.getActionIndex(actionName));
}
return buildAutomatonDd(composition.getAutomatonName(), data.empty() ? actionInstantiations : boost::any_cast<ActionInstantiations const&>(data), inputEnabledActionIndices);
return buildAutomatonDd(composition.getAutomatonName(), data.empty() ? actionInstantiations : boost::any_cast<ActionInstantiations const&>(data), inputEnabledActionIndices, data.empty());
}
boost::any visit(storm::jani::ParallelComposition const& composition, boost::any const& data) override {
@ -1707,10 +1759,13 @@ namespace storm {
}
}
AutomatonDd buildAutomatonDd(std::string const& automatonName, ActionInstantiations const& actionInstantiations, std::set<uint64_t> const& inputEnabledActionIndices) {
AutomatonDd buildAutomatonDd(std::string const& automatonName, ActionInstantiations const& actionInstantiations, std::set<uint64_t> const& inputEnabledActionIndices, bool isTopLevelAutomaton) {
STORM_LOG_TRACE("Building DD for automaton '" << automatonName << "'.");
AutomatonDd result(this->variables.automatonToIdentityMap.at(automatonName));
// Disjunction of all guards of non-markovian actions (only required for maximum progress assumption).
storm::dd::Bdd<Type> nonMarkovianActionGuards = this->variables.manager->getBddZero();
storm::jani::Automaton const& automaton = this->model.getAutomaton(automatonName);
for (auto const& actionInstantiation : actionInstantiations) {
uint64_t actionIndex = actionInstantiation.first;
@ -1727,12 +1782,20 @@ namespace storm {
if (inputEnabled) {
actionDd.setIsInputEnabled();
}
if (applyMaximumProgress && isTopLevelAutomaton && !instantiation.isMarkovian()) {
nonMarkovianActionGuards |= actionDd.guard;
}
STORM_LOG_TRACE("Used local nondeterminism variables are " << actionDd.getLowestLocalNondeterminismVariable() << " to " << actionDd.getHighestLocalNondeterminismVariable() << ".");
result.actions[ActionIdentification(actionIndex, instantiation.synchronizationVectorIndex, instantiation.isMarkovian())] = actionDd;
result.extendLocalNondeterminismVariables(actionDd.getLocalNondeterminismVariables());
}
}
if (applyMaximumProgress && isTopLevelAutomaton) {
ActionIdentification silentMarkovianActionIdentification(storm::jani::Model::SILENT_ACTION_INDEX, true);
result.actions[silentMarkovianActionIdentification].conjunctGuardWith(!nonMarkovianActionGuards);
}
for (uint64_t locationIndex = 0; locationIndex < automaton.getNumberOfLocations(); ++locationIndex) {
auto const& location = automaton.getLocation(locationIndex);
performTransientAssignments(location.getAssignments().getTransientAssignments(), [this,&automatonName,locationIndex,&result] (storm::jani::Assignment const& assignment) {
@ -2076,10 +2139,20 @@ namespace storm {
auto features = model.getModelFeatures();
features.remove(storm::jani::ModelFeature::DerivedOperators);
features.remove(storm::jani::ModelFeature::StateExitRewards);
STORM_LOG_THROW(features.empty(), storm::exceptions::InvalidSettingsException, "The dd jani model builder does not support the following model feature(s): " << features.toString() << ".");
storm::jani::Model preparedModel = model;
preparedModel.substituteFunctions();
preparedModel.simplifyComposition();
if (features.hasArrays()) {
STORM_LOG_ERROR("The jani model still considers arrays. These should have been eliminated before calling the dd builder. The arrays are eliminated now, but occurrences in properties will not be handled properly.");
preparedModel.eliminateArrays();
features.remove(storm::jani::ModelFeature::Arrays);
}
if (features.hasFunctions()) {
STORM_LOG_ERROR("The jani model still considers functions. These should have been substituted before calling the dd builder. The functions are substituted now, but occurrences in properties will not be handled properly.");
preparedModel.substituteFunctions();
features.remove(storm::jani::ModelFeature::Functions);
}
STORM_LOG_THROW(features.empty(), storm::exceptions::InvalidSettingsException, "The dd jani model builder does not support the following model feature(s): " << features.toString() << ".");
// Lift the transient edge destinations. We can do so, as we know that there are no assignment levels (because that's not supported anyway).
if (preparedModel.hasTransientEdgeDestinationAssignments()) {

17
src/storm/builder/DdJaniModelBuilder.h

@ -3,6 +3,7 @@
#include <boost/optional.hpp>
#include "storm/storage/dd/DdType.h"
#include "storm/storage/jani/Property.h"
#include "storm/logic/Formula.h"
#include "storm/builder/TerminalStatesGetter.h"
@ -17,13 +18,27 @@ namespace storm {
}
namespace jani {
class Model;
class ModelFeatures;
}
namespace builder {
template <storm::dd::DdType Type, typename ValueType = double>
class DdJaniModelBuilder {
public:
public:
/*!
* Returns the jani features with which this builder can deal natively.
*/
static storm::jani::ModelFeatures getSupportedJaniFeatures();
/*!
* A quick check to detect whether the given model is not supported.
* This method only over-approximates the set of models that can be handled, i.e., if this
* returns true, the model might still be unsupported.
*/
static bool canHandle(storm::jani::Model const& model, boost::optional<std::vector<storm::jani::Property>> const& properties = boost::none);
struct Options {
/*!
* Creates an object representing the default building options.

7
src/storm/builder/DdPrismModelBuilder.cpp

@ -540,7 +540,12 @@ namespace storm {
};
template <storm::dd::DdType Type, typename ValueType>
DdPrismModelBuilder<Type, ValueType>::Options::Options() : buildAllRewardModels(false), rewardModelsToBuild(), buildAllLabels(false), labelsToBuild() {
bool DdPrismModelBuilder<Type, ValueType>::canHandle(storm::prism::Program const& program) {
return program.getModelType() != storm::prism::Program::ModelType::PTA;
}
template <storm::dd::DdType Type, typename ValueType>
DdPrismModelBuilder<Type, ValueType>::Options::Options() : buildAllRewardModels(false), rewardModelsToBuild(), buildAllLabels(false), labelsToBuild(), terminalStates() {
// Intentionally left empty.
}

7
src/storm/builder/DdPrismModelBuilder.h

@ -33,6 +33,13 @@ namespace storm {
template <storm::dd::DdType Type, typename ValueType = double>
class DdPrismModelBuilder {
public:
/*!
* A quick check to detect whether the given model is not supported.
* This method only over-approximates the set of models that can be handled, i.e., if this
* returns true, the model might still be unsupported.
*/
static bool canHandle(storm::prism::Program const& program);
struct Options {
/*!
* Creates an object representing the default building options.

27
src/storm/builder/jit/ExplicitJitJaniModelBuilder.cpp

@ -53,6 +53,33 @@ namespace storm {
#ifdef WINDOWS
static const std::string DYLIB_EXTENSION = ".dll";
#endif
template <typename ValueType, typename RewardModelType>
storm::jani::ModelFeatures ExplicitJitJaniModelBuilder<ValueType, RewardModelType>::getSupportedJaniFeatures() {
storm::jani::ModelFeatures features;
features.add(storm::jani::ModelFeature::DerivedOperators);
features.add(storm::jani::ModelFeature::StateExitRewards);
// We do not add Functions and arrays as these should ideally be substituted before creating this generator.
// This is because functions or arrays may also occur in properties and the user of this builder should take care of that.
return features;
}
template <typename ValueType, typename RewardModelType>
bool ExplicitJitJaniModelBuilder<ValueType, RewardModelType>::canHandle(storm::jani::Model const& model) {
// Check jani features
auto features = model.getModelFeatures();
features.remove(storm::jani::ModelFeature::Arrays); // can be substituted
features.remove(storm::jani::ModelFeature::DerivedOperators);
features.remove(storm::jani::ModelFeature::Functions); // can be substituted
features.remove(storm::jani::ModelFeature::StateExitRewards);
if (!features.empty()) {
STORM_LOG_INFO("Jit engine can not build Jani model due to unsupported jani features.");
return false;
}
// There probably are more cases where the model is unsupported. However, checking these is often more involved.
// As this method is supposed to be a quick check, we just return true at this point.
return true;
}
template <typename ValueType, typename RewardModelType>
ExplicitJitJaniModelBuilder<ValueType, RewardModelType>::ExplicitJitJaniModelBuilder(storm::jani::Model const& model, storm::builder::BuilderOptions const& options) : options(options), model(model.substituteConstantsFunctions()), modelComponentsBuilder(model.getModelType()) {

12
src/storm/builder/jit/ExplicitJitJaniModelBuilder.h

@ -48,6 +48,18 @@ namespace storm {
typedef JitModelBuilderInterface<IndexType, ValueType>* (CreateFunctionType)(ModelComponentsBuilder<IndexType, ValueType>& modelComponentsBuilder);
typedef boost::function<CreateFunctionType> ImportCreateFunctionType;
/*!
* Returns the jani features with which this builder can deal natively.
*/
static storm::jani::ModelFeatures getSupportedJaniFeatures();
/*!
* A quick check to detect whether the given model is not supported.
* This method only over-approximates the set of models that can be handled, i.e., if this
* returns true, the model might still be unsupported.
*/
static bool canHandle(storm::jani::Model const& model);
/*!
* Creates a model builder for the given model. The provided options are used to determine which part of
* the model is built.

6
src/storm/environment/SubEnvironment.cpp

@ -6,12 +6,14 @@
#include "storm/environment/solver/SolverEnvironment.h"
#include "storm/environment/solver/EigenSolverEnvironment.h"
#include "storm/environment/solver/GameSolverEnvironment.h"
#include "storm/environment/solver/GmmxxSolverEnvironment.h"
#include "storm/environment/solver/NativeSolverEnvironment.h"
#include "storm/environment/solver/LongRunAverageSolverEnvironment.h"
#include "storm/environment/solver/TimeBoundedSolverEnvironment.h"
#include "storm/environment/solver/MinMaxSolverEnvironment.h"
#include "storm/environment/solver/MultiplierEnvironment.h"
#include "storm/environment/solver/GameSolverEnvironment.h"
#include "storm/environment/solver/OviSolverEnvironment.h"
#include "storm/environment/solver/TopologicalSolverEnvironment.h"
namespace storm {
@ -65,8 +67,10 @@ namespace storm {
template class SubEnvironment<GmmxxSolverEnvironment>;
template class SubEnvironment<NativeSolverEnvironment>;
template class SubEnvironment<LongRunAverageSolverEnvironment>;
template class SubEnvironment<TimeBoundedSolverEnvironment>;
template class SubEnvironment<MinMaxSolverEnvironment>;
template class SubEnvironment<MultiplierEnvironment>;
template class SubEnvironment<OviSolverEnvironment>;
template class SubEnvironment<GameSolverEnvironment>;
template class SubEnvironment<TopologicalSolverEnvironment>;

43
src/storm/environment/solver/OviSolverEnvironment.cpp

@ -0,0 +1,43 @@
#include "storm/environment/solver/OviSolverEnvironment.h"
#include "storm/settings/SettingsManager.h"
#include "storm/settings/modules/OviSolverSettings.h"
#include "storm/utility/constants.h"
#include "storm/utility/macros.h"
namespace storm {
OviSolverEnvironment::OviSolverEnvironment() {
auto const& oviSettings = storm::settings::getModule<storm::settings::modules::OviSolverSettings>();
precisionUpdateFactor = storm::utility::convertNumber<storm::RationalNumber>(oviSettings.getPrecisionUpdateFactor());
maxVerificationIterationFactor = storm::utility::convertNumber<storm::RationalNumber>(oviSettings.getMaxVerificationIterationFactor());
relevantValuesForPrecisionUpdate = oviSettings.useRelevantValuesForPrecisionUpdate();
upperBoundGuessingFactor = storm::utility::convertNumber<storm::RationalNumber>(oviSettings.getUpperBoundGuessingFactor());
upperBoundOnlyIterations = oviSettings.getUpperBoundOnlyIterations();
}
OviSolverEnvironment::~OviSolverEnvironment() {
// Intentionally left empty
}
storm::RationalNumber OviSolverEnvironment::getPrecisionUpdateFactor() const {
return precisionUpdateFactor;
}
storm::RationalNumber OviSolverEnvironment::getMaxVerificationIterationFactor() const {
return maxVerificationIterationFactor;
}
bool OviSolverEnvironment::useRelevantValuesForPrecisionUpdate() const {
return relevantValuesForPrecisionUpdate;
}
storm::RationalNumber OviSolverEnvironment::getUpperBoundGuessingFactor() const {
return maxVerificationIterationFactor;
}
uint64_t OviSolverEnvironment::getUpperBoundOnlyIterations() const {
return upperBoundOnlyIterations;
}
}

29
src/storm/environment/solver/OviSolverEnvironment.h

@ -0,0 +1,29 @@
#pragma once
#include "storm/environment/solver/SolverEnvironment.h"
#include "storm/adapters/RationalNumberAdapter.h"
namespace storm {
class OviSolverEnvironment {
public:
OviSolverEnvironment();
~OviSolverEnvironment();
storm::RationalNumber getPrecisionUpdateFactor() const;
storm::RationalNumber getMaxVerificationIterationFactor() const;
bool useRelevantValuesForPrecisionUpdate() const;
storm::RationalNumber getUpperBoundGuessingFactor() const;
uint64_t getUpperBoundOnlyIterations() const;
private:
storm::RationalNumber precisionUpdateFactor;
storm::RationalNumber maxVerificationIterationFactor;
bool relevantValuesForPrecisionUpdate;
storm::RationalNumber upperBoundGuessingFactor;
uint64_t upperBoundOnlyIterations;
};
}

27
src/storm/environment/solver/SolverEnvironment.cpp

@ -1,6 +1,7 @@
#include "storm/environment/solver/SolverEnvironment.h"
#include "storm/environment/solver/LongRunAverageSolverEnvironment.h"
#include "storm/environment/solver/TimeBoundedSolverEnvironment.h"
#include "storm/environment/solver/MinMaxSolverEnvironment.h"
#include "storm/environment/solver/MultiplierEnvironment.h"
#include "storm/environment/solver/EigenSolverEnvironment.h"
@ -8,6 +9,7 @@
#include "storm/environment/solver/NativeSolverEnvironment.h"
#include "storm/environment/solver/GameSolverEnvironment.h"
#include "storm/environment/solver/TopologicalSolverEnvironment.h"
#include "storm/environment/solver/OviSolverEnvironment.h"
#include "storm/settings/SettingsManager.h"
#include "storm/settings/modules/GeneralSettings.h"
@ -22,6 +24,7 @@ namespace storm {
SolverEnvironment::SolverEnvironment() {
forceSoundness = storm::settings::getModule<storm::settings::modules::GeneralSettings>().isSoundSet();
forceExact = storm::settings::getModule<storm::settings::modules::GeneralSettings>().isExactSet();
linearEquationSolverType = storm::settings::getModule<storm::settings::modules::CoreSettings>().getEquationSolver();
linearEquationSolverTypeSetFromDefault = storm::settings::getModule<storm::settings::modules::CoreSettings>().isEquationSolverSetFromDefaultValue();
}
@ -38,6 +41,14 @@ namespace storm {
return longRunAverageSolverEnvironment.get();
}
TimeBoundedSolverEnvironment& SolverEnvironment::timeBounded() {
return timeBoundedSolverEnvironment.get();
}
TimeBoundedSolverEnvironment const& SolverEnvironment::timeBounded() const {
return timeBoundedSolverEnvironment.get();
}
MinMaxSolverEnvironment& SolverEnvironment::minMax() {
return minMaxSolverEnvironment.get();
}
@ -94,6 +105,14 @@ namespace storm {
return topologicalSolverEnvironment.get();
}
OviSolverEnvironment& SolverEnvironment::ovi() {
return oviSolverEnvironment.get();
}
OviSolverEnvironment const& SolverEnvironment::ovi() const {
return oviSolverEnvironment.get();
}
bool SolverEnvironment::isForceSoundness() const {
return forceSoundness;
}
@ -102,6 +121,14 @@ namespace storm {
SolverEnvironment::forceSoundness = value;
}
bool SolverEnvironment::isForceExact() const {
return forceExact;
}
void SolverEnvironment::setForceExact(bool value) {
SolverEnvironment::forceExact = value;
}
storm::solver::EquationSolverType const& SolverEnvironment::getLinearEquationSolverType() const {
return linearEquationSolverType;
}

11
src/storm/environment/solver/SolverEnvironment.h

@ -15,10 +15,12 @@ namespace storm {
class GmmxxSolverEnvironment;
class NativeSolverEnvironment;
class LongRunAverageSolverEnvironment;
class TimeBoundedSolverEnvironment;
class MinMaxSolverEnvironment;
class MultiplierEnvironment;
class GameSolverEnvironment;
class TopologicalSolverEnvironment;
class OviSolverEnvironment;
class SolverEnvironment {
public:
@ -34,10 +36,14 @@ namespace storm {
NativeSolverEnvironment const& native() const;
LongRunAverageSolverEnvironment& lra();
LongRunAverageSolverEnvironment const& lra() const;
TimeBoundedSolverEnvironment& timeBounded();
TimeBoundedSolverEnvironment const& timeBounded() const;
MinMaxSolverEnvironment& minMax();
MinMaxSolverEnvironment const& minMax() const;
MultiplierEnvironment& multiplier();
MultiplierEnvironment const& multiplier() const;
OviSolverEnvironment const& ovi() const;
OviSolverEnvironment& ovi();
GameSolverEnvironment& game();
GameSolverEnvironment const& game() const;
TopologicalSolverEnvironment& topological();
@ -45,6 +51,8 @@ namespace storm {
bool isForceSoundness() const;
void setForceSoundness(bool value);
bool isForceExact() const;
void setForceExact(bool value);
storm::solver::EquationSolverType const& getLinearEquationSolverType() const;
void setLinearEquationSolverType(storm::solver::EquationSolverType const& value, bool isSetFromDefault = false);
@ -60,12 +68,15 @@ namespace storm {
SubEnvironment<GameSolverEnvironment> gameSolverEnvironment;
SubEnvironment<TopologicalSolverEnvironment> topologicalSolverEnvironment;
SubEnvironment<LongRunAverageSolverEnvironment> longRunAverageSolverEnvironment;
SubEnvironment<TimeBoundedSolverEnvironment> timeBoundedSolverEnvironment;
SubEnvironment<MinMaxSolverEnvironment> minMaxSolverEnvironment;
SubEnvironment<MultiplierEnvironment> multiplierEnvironment;
SubEnvironment<OviSolverEnvironment> oviSolverEnvironment;
storm::solver::EquationSolverType linearEquationSolverType;
bool linearEquationSolverTypeSetFromDefault;
bool forceSoundness;
bool forceExact;
};
}

60
src/storm/environment/solver/TimeBoundedSolverEnvironment.cpp

@ -0,0 +1,60 @@
#include "storm/environment/solver/TimeBoundedSolverEnvironment.h"
#include "storm/settings/SettingsManager.h"
#include "storm/settings/modules/TimeBoundedSolverSettings.h"
#include "storm/utility/constants.h"
#include "storm/utility/macros.h"
namespace storm {
TimeBoundedSolverEnvironment::TimeBoundedSolverEnvironment() {
auto const& tbSettings = storm::settings::getModule<storm::settings::modules::TimeBoundedSolverSettings>();
maMethod = tbSettings.getMaMethod();
maMethodSetFromDefault = tbSettings.isMaMethodSetFromDefaultValue();
precision = storm::utility::convertNumber<storm::RationalNumber>(tbSettings.getPrecision());
relative = tbSettings.isRelativePrecision();
unifPlusKappa = tbSettings.getUnifPlusKappa();
}
TimeBoundedSolverEnvironment::~TimeBoundedSolverEnvironment() {
// Intentionally left empty
}
storm::solver::MaBoundedReachabilityMethod const& TimeBoundedSolverEnvironment::getMaMethod() const {
return maMethod;
}
bool const& TimeBoundedSolverEnvironment::isMaMethodSetFromDefault() const {
return maMethodSetFromDefault;
}
void TimeBoundedSolverEnvironment::setMaMethod(storm::solver::MaBoundedReachabilityMethod value, bool isSetFromDefault) {
maMethod = value;
maMethodSetFromDefault = isSetFromDefault;
}
storm::RationalNumber const& TimeBoundedSolverEnvironment::getPrecision() const {
return precision;
}
void TimeBoundedSolverEnvironment::setPrecision(storm::RationalNumber value) {
precision = value;
}
bool const& TimeBoundedSolverEnvironment::getRelativeTerminationCriterion() const {
return relative;
}
void TimeBoundedSolverEnvironment::setRelativeTerminationCriterion(bool value) {
relative = value;
}
storm::RationalNumber const& TimeBoundedSolverEnvironment::getUnifPlusKappa() const {
return unifPlusKappa;
}
void TimeBoundedSolverEnvironment::setUnifPlusKappa(storm::RationalNumber value) {
unifPlusKappa = value;
}
}

36
src/storm/environment/solver/TimeBoundedSolverEnvironment.h

@ -0,0 +1,36 @@
#pragma once
#include "storm/environment/solver/SolverEnvironment.h"
#include "storm/solver/SolverSelectionOptions.h"
namespace storm {
class TimeBoundedSolverEnvironment {
public:
TimeBoundedSolverEnvironment();
~TimeBoundedSolverEnvironment();
storm::solver::MaBoundedReachabilityMethod const& getMaMethod() const;
bool const& isMaMethodSetFromDefault() const;
void setMaMethod(storm::solver::MaBoundedReachabilityMethod value, bool isSetFromDefault = false);
storm::RationalNumber const& getPrecision() const;
void setPrecision(storm::RationalNumber value);
bool const& getRelativeTerminationCriterion() const;
void setRelativeTerminationCriterion(bool value);
storm::RationalNumber const& getUnifPlusKappa() const;
void setUnifPlusKappa(storm::RationalNumber value);
private:
storm::solver::MaBoundedReachabilityMethod maMethod;
bool maMethodSetFromDefault;
storm::RationalNumber precision;
bool relative;
storm::RationalNumber unifPlusKappa;
};
}

5
src/storm/generator/Choice.cpp

@ -163,6 +163,11 @@ namespace storm {
return distribution.size();
}
template<typename ValueType, typename StateType>
void Choice<ValueType, StateType>::reserve(std::size_t const& size) {
distribution.reserve(size);
}
template<typename ValueType, typename StateType>
std::ostream& operator<<(std::ostream& out, Choice<ValueType, StateType> const& choice) {
out << "<";

5
src/storm/generator/Choice.h

@ -150,6 +150,11 @@ namespace storm {
*/
std::size_t size() const;
/*!
* If the size is already known, reserves space in the underlying distribution.
*/
void reserve(std::size_t const& size);
private:
// A flag indicating whether this choice is Markovian or not.
bool markovian;

141
src/storm/generator/JaniNextStateGenerator.cpp

@ -115,6 +115,34 @@ namespace storm {
}
}
}
template<typename ValueType, typename StateType>
storm::jani::ModelFeatures JaniNextStateGenerator<ValueType, StateType>::getSupportedJaniFeatures() {
storm::jani::ModelFeatures features;
features.add(storm::jani::ModelFeature::DerivedOperators);
features.add(storm::jani::ModelFeature::StateExitRewards);
features.add(storm::jani::ModelFeature::Arrays);
// We do not add Functions as these should ideally be substituted before creating this generator.
// This is because functions may also occur in properties and the user of this class should take care of that.
return features;
}
template<typename ValueType, typename StateType>
bool JaniNextStateGenerator<ValueType, StateType>::canHandle(storm::jani::Model const& model) {
auto features = model.getModelFeatures();
features.remove(storm::jani::ModelFeature::Arrays);
features.remove(storm::jani::ModelFeature::DerivedOperators);
features.remove(storm::jani::ModelFeature::Functions); // can be substituted
features.remove(storm::jani::ModelFeature::StateExitRewards);
if (!features.empty()) {
STORM_LOG_INFO("The model can not be build as it contains these unsupported features: " << features.toString());
return false;
}
// There probably are more cases where the model is unsupported. However, checking these is more involved.
// As this method is supposed to be a quick check, we just return true at this point.
return true;
}
template<typename ValueType, typename StateType>
ModelType JaniNextStateGenerator<ValueType, StateType>::getModelType() const {
@ -747,8 +775,7 @@ namespace storm {
}
template<typename ValueType, typename StateType>
std::vector<Choice<ValueType>> JaniNextStateGenerator<ValueType, StateType>::expandSynchronizingEdgeCombination(AutomataEdgeSets const& edgeCombination, uint64_t outputActionIndex, CompressedState const& state, StateToIdCallback stateToIdCallback) {
std::vector<Choice<ValueType>> result;
void JaniNextStateGenerator<ValueType, StateType>::expandSynchronizingEdgeCombination(AutomataEdgeSets const& edgeCombination, uint64_t outputActionIndex, CompressedState const& state, StateToIdCallback stateToIdCallback, std::vector<Choice<ValueType>>& newChoices) {
if (this->options.isExplorationChecksSet()) {
// Check whether a global variable is written multiple times in any combination.
@ -778,10 +805,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.emplace_back(outputActionIndex);
newChoices.emplace_back(outputActionIndex);
// Now create the actual distribution.
Choice<ValueType>& choice = result.back();
Choice<ValueType>& choice = newChoices.back();
// Add the edge indices if requested.
if (this->getOptions().isBuildChoiceOriginsSet()) {
@ -793,6 +820,7 @@ namespace storm {
// Add the probabilities/rates to the newly created choice.
ValueType probabilitySum = storm::utility::zero<ValueType>();
choice.reserve(std::distance(distribution.begin(), distribution.end()));
for (auto const& stateProbability : distribution) {
choice.addProbability(stateProbability.getState(), stateProbability.getValue());
@ -820,15 +848,19 @@ namespace storm {
done = !movedIterator;
}
return result;
}
template<typename ValueType, typename StateType>
std::vector<Choice<ValueType>> JaniNextStateGenerator<ValueType, StateType>::getActionChoices(std::vector<uint64_t> const& locations, CompressedState const& state, StateToIdCallback stateToIdCallback, EdgeFilter const& edgeFilter) {
std::vector<Choice<ValueType>> result;
for (auto const& outputAndEdges : edges) {
// To avoid reallocations, we declare some memory here here.
// This vector will store for each automaton the set of edges with the current output and the current source location
std::vector<EdgeSetWithIndices const*> edgeSetsMemory;
// This vector will store the 'first' combination of edges that is productive.
std::vector<typename EdgeSetWithIndices::const_iterator> edgeIteratorMemory;
for (OutputAndEdges const& outputAndEdges : edges) {
auto const& edges = outputAndEdges.second;
if (edges.size() == 1) {
// If the synch consists of just one element, it's non-synchronizing.
@ -848,61 +880,106 @@ namespace storm {
continue;
}
Choice<ValueType> choice = expandNonSynchronizingEdge(*indexAndEdge.second, outputAndEdges.first ? outputAndEdges.first.get() : indexAndEdge.second->getActionIndex(), automatonIndex, state, stateToIdCallback);
result.push_back(expandNonSynchronizingEdge(*indexAndEdge.second, outputAndEdges.first ? outputAndEdges.first.get() : indexAndEdge.second->getActionIndex(), automatonIndex, state, stateToIdCallback));
if (this->getOptions().isBuildChoiceOriginsSet()) {
EdgeIndexSet edgeIndex { model.encodeAutomatonAndEdgeIndices(automatonIndex, indexAndEdge.first) };
choice.addOriginData(boost::any(std::move(edgeIndex)));
result.back().addOriginData(boost::any(std::move(edgeIndex)));
}
result.emplace_back(std::move(choice));
}
}
} else {
// If the element has more than one set of edges, we need to perform a synchronization.
STORM_LOG_ASSERT(outputAndEdges.first, "Need output action index for synchronization.");
AutomataEdgeSets automataEdgeSets;
uint64_t outputActionIndex = outputAndEdges.first.get();
// Find out whether this combination is productive
bool productiveCombination = true;
// First check, whether each automaton has at least one edge with the current output and the current source location
// We will also store the edges of each automaton with the current outputAction
edgeSetsMemory.clear();
for (auto const& automatonAndEdges : outputAndEdges.second) {
uint64_t automatonIndex = automatonAndEdges.first;
EdgeSetWithIndices enabledEdgesOfAutomaton;
bool atLeastOneEdge = false;
auto edgesIt = automatonAndEdges.second.find(locations[automatonIndex]);
if (edgesIt != automatonAndEdges.second.end()) {
for (auto const& indexAndEdge : edgesIt->second) {
LocationsAndEdges const& locationsAndEdges = automatonAndEdges.second;
auto edgesIt = locationsAndEdges.find(locations[automatonIndex]);
if (edgesIt == locationsAndEdges.end()) {
productiveCombination = false;
break;
}
edgeSetsMemory.push_back(&edgesIt->second);
}
if (productiveCombination) {
// second, check whether each automaton has at least one enabled action
edgeIteratorMemory.clear(); // Store the first enabled edge in each automaton.
for (auto const& edgesIt : edgeSetsMemory) {
bool atLeastOneEdge = false;
EdgeSetWithIndices const& edgeSetWithIndices = *edgesIt;
for (auto indexAndEdgeIt = edgeSetWithIndices.begin(), indexAndEdgeIte = edgeSetWithIndices.end(); indexAndEdgeIt != indexAndEdgeIte; ++indexAndEdgeIt) {
// check whether we do not consider this edge
if (edgeFilter != EdgeFilter::All) {
STORM_LOG_ASSERT(edgeFilter == EdgeFilter::WithRate || edgeFilter == EdgeFilter::WithoutRate, "Unexpected edge filter.");
if ((edgeFilter == EdgeFilter::WithRate) != indexAndEdge.second->hasRate()) {
if ((edgeFilter == EdgeFilter::WithRate) != indexAndEdgeIt->second->hasRate()) {
continue;
}
}
if (!this->evaluator->asBool(indexAndEdge.second->getGuard())) {
if (!this->evaluator->asBool(indexAndEdgeIt->second->getGuard())) {
continue;
}
// If we reach this point, the edge is considered enabled.
atLeastOneEdge = true;
enabledEdgesOfAutomaton.emplace_back(indexAndEdge);
edgeIteratorMemory.push_back(indexAndEdgeIt);
break;
}
// If there is no enabled edge of this automaton, the whole combination is not productive.
if (!atLeastOneEdge) {
productiveCombination = false;
break;
}
}
// If there is no enabled edge of this automaton, the whole combination is not productive.
if (!atLeastOneEdge) {
productiveCombination = false;
break;
}
automataEdgeSets.emplace_back(std::make_pair(automatonIndex, std::move(enabledEdgesOfAutomaton)));
}
// produce the combination
if (productiveCombination) {
std::vector<Choice<ValueType>> choices = expandSynchronizingEdgeCombination(automataEdgeSets, outputActionIndex, state, stateToIdCallback);
for (auto const& choice : choices) {
result.emplace_back(std::move(choice));
AutomataEdgeSets automataEdgeSets;
automataEdgeSets.reserve(outputAndEdges.second.size());
STORM_LOG_ASSERT(edgeSetsMemory.size() == outputAndEdges.second.size(), "Unexpected number of edge sets stored.");
STORM_LOG_ASSERT(edgeIteratorMemory.size() == outputAndEdges.second.size(), "Unexpected number of edge iterators stored.");
auto edgeSetIt = edgeSetsMemory.begin();
auto edgeIteratorIt = edgeIteratorMemory.begin();
for (auto const& automatonAndEdges : outputAndEdges.second) {
EdgeSetWithIndices enabledEdgesOfAutomaton;
uint64_t automatonIndex = automatonAndEdges.first;
EdgeSetWithIndices const& edgeSetWithIndices = **edgeSetIt;
auto indexAndEdgeIt = *edgeIteratorIt;
// The first edge where the edgeIterator points to is always enabled.
enabledEdgesOfAutomaton.emplace_back(*indexAndEdgeIt);
auto indexAndEdgeIte = edgeSetWithIndices.end();
for (++indexAndEdgeIt; indexAndEdgeIt != indexAndEdgeIte; ++indexAndEdgeIt) {
// check whether we do not consider this edge
if (edgeFilter != EdgeFilter::All) {
STORM_LOG_ASSERT(edgeFilter == EdgeFilter::WithRate || edgeFilter == EdgeFilter::WithoutRate, "Unexpected edge filter.");
if ((edgeFilter == EdgeFilter::WithRate) != indexAndEdgeIt->second->hasRate()) {
continue;
}
}
if (!this->evaluator->asBool(indexAndEdgeIt->second->getGuard())) {
continue;
}
// If we reach this point, the edge is considered enabled.
enabledEdgesOfAutomaton.emplace_back(*indexAndEdgeIt);
}
automataEdgeSets.emplace_back(std::move(automatonIndex), std::move(enabledEdgesOfAutomaton));
++edgeSetIt;
++edgeIteratorIt;
}
// insert choices in the result vector.
expandSynchronizingEdgeCombination(automataEdgeSets, outputActionIndex, state, stateToIdCallback, result);
}
}
}

14
src/storm/generator/JaniNextStateGenerator.h

@ -32,6 +32,18 @@ namespace storm {
JaniNextStateGenerator(storm::jani::Model const& model, NextStateGeneratorOptions const& options = NextStateGeneratorOptions());
/*!
* Returns the jani features with which this builder can deal natively.
*/
static storm::jani::ModelFeatures getSupportedJaniFeatures();
/*!
* A quick check to detect whether the given model is not supported.
* This method only over-approximates the set of models that can be handled, i.e., if this
* returns true, the model might still be unsupported.
*/
static bool canHandle(storm::jani::Model const& model);
virtual ModelType getModelType() const override;
virtual bool isDeterministicModel() const override;
virtual bool isDiscreteTimeModel() const override;
@ -124,7 +136,7 @@ namespace storm {
typedef std::pair<uint64_t, EdgeSetWithIndices> AutomatonAndEdgeSet;
typedef std::vector<AutomatonAndEdgeSet> AutomataEdgeSets;
std::vector<Choice<ValueType>> expandSynchronizingEdgeCombination(AutomataEdgeSets const& edgeCombination, uint64_t outputActionIndex, CompressedState const& state, StateToIdCallback stateToIdCallback);
void expandSynchronizingEdgeCombination(AutomataEdgeSets const& edgeCombination, uint64_t outputActionIndex, CompressedState const& state, StateToIdCallback stateToIdCallback, std::vector<Choice<ValueType>>& newChoices);
void generateSynchronizedDistribution(storm::storage::BitVector const& state, AutomataEdgeSets const& edgeCombination, std::vector<EdgeSetWithIndices::const_iterator> const& iteratorList, storm::builder::jit::Distribution<StateType, ValueType>& distribution, std::vector<ValueType>& stateActionRewards, EdgeIndexSet& edgeIndices, StateToIdCallback stateToIdCallback);
/*!

6
src/storm/generator/PrismNextStateGenerator.cpp

@ -82,6 +82,12 @@ namespace storm {
}
}
template<typename ValueType, typename StateType>
bool PrismNextStateGenerator<ValueType, StateType>::canHandle(storm::prism::Program const& program) {
// We can handle all valid prism programs (except for PTAs)
return program.getModelType() != storm::prism::Program::ModelType::PTA;
}
template<typename ValueType, typename StateType>
void PrismNextStateGenerator<ValueType, StateType>::checkValid() const {
// If the program still contains undefined constants and we are not in a parametric setting, assemble an appropriate error message.

9
src/storm/generator/PrismNextStateGenerator.h

@ -24,7 +24,14 @@ namespace storm {
enum class CommandFilter {All, Markovian, Probabilistic};
PrismNextStateGenerator(storm::prism::Program const& program, NextStateGeneratorOptions const& options = NextStateGeneratorOptions());
/*!
* A quick check to detect whether the given model is not supported.
* This method only over-approximates the set of models that can be handled, i.e., if this
* returns true, the model might still be unsupported.
*/
static bool canHandle(storm::prism::Program const& program);
virtual ModelType getModelType() const override;
virtual bool isDeterministicModel() const override;
virtual bool isDiscreteTimeModel() const override;

10
src/storm/logic/FormulaInformation.cpp

@ -26,6 +26,10 @@ namespace storm {
return this->mContainsRewardBoundedFormula;
}
bool FormulaInformation::containsLongRunFormula() const {
return this->mContainsLongRunFormula;
}
FormulaInformation FormulaInformation::join(FormulaInformation const& other) {
FormulaInformation result;
result.mContainsRewardOperator = this->containsRewardOperator() || other.containsRewardOperator();
@ -33,6 +37,7 @@ namespace storm {
result.mContainsBoundedUntilFormula = this->containsBoundedUntilFormula() || other.containsBoundedUntilFormula();
result.mContainsCumulativeRewardFormula = this->containsCumulativeRewardFormula() || other.containsCumulativeRewardFormula();
result.mContainsRewardBoundedFormula = this->containsRewardBoundedFormula() || other.containsRewardBoundedFormula();
result.mContainsLongRunFormula = this->containsLongRunFormula() || other.containsLongRunFormula();
return result;
}
@ -60,5 +65,10 @@ namespace storm {
this->mContainsRewardBoundedFormula = newValue;
return *this;
}
FormulaInformation& FormulaInformation::setContainsLongRunFormula(bool newValue) {
this->mContainsLongRunFormula = newValue;
return *this;
}
}
}

3
src/storm/logic/FormulaInformation.h

@ -17,6 +17,7 @@ namespace storm {
bool containsBoundedUntilFormula() const;
bool containsCumulativeRewardFormula() const;
bool containsRewardBoundedFormula() const;
bool containsLongRunFormula() const;
FormulaInformation join(FormulaInformation const& other);
@ -25,6 +26,7 @@ namespace storm {
FormulaInformation& setContainsBoundedUntilFormula(bool newValue = true);
FormulaInformation& setContainsCumulativeRewardFormula(bool newValue = true);
FormulaInformation& setContainsRewardBoundedFormula(bool newValue = true);
FormulaInformation& setContainsLongRunFormula(bool newValue = true);
private:
bool mContainsRewardOperator;
@ -32,6 +34,7 @@ namespace storm {
bool mContainsBoundedUntilFormula;
bool mContainsCumulativeRewardFormula;
bool mContainsRewardBoundedFormula;
bool mContainsLongRunFormula;
};
}

9
src/storm/logic/FormulaInformationVisitor.cpp

@ -78,11 +78,16 @@ namespace storm {
}
boost::any FormulaInformationVisitor::visit(LongRunAverageOperatorFormula const& f, boost::any const& data) const {
return f.getSubformula().accept(*this, data);
FormulaInformation result;
result.setContainsLongRunFormula(true);
result.join(boost::any_cast<FormulaInformation>(f.getSubformula().accept(*this, data)));
return result;
}
boost::any FormulaInformationVisitor::visit(LongRunAverageRewardFormula const&, boost::any const&) const {
return FormulaInformation();
FormulaInformation result;
result.setContainsLongRunFormula(true);
return result;
}
boost::any FormulaInformationVisitor::visit(MultiObjectiveFormula const& f, boost::any const& data) const {

6
src/storm/modelchecker/AbstractModelChecker.cpp

@ -17,6 +17,7 @@
#include "storm/models/symbolic/Dtmc.h"
#include "storm/models/symbolic/Ctmc.h"
#include "storm/models/symbolic/Mdp.h"
#include "storm/models/symbolic/MarkovAutomaton.h"
#include "storm/models/symbolic/StochasticTwoPlayerGame.h"
#include "storm/models/sparse/MarkovAutomaton.h"
#include "storm/models/sparse/StandardRewardModel.h"
@ -25,6 +26,7 @@
#include "storm/storage/dd/Bdd.h"
#include <boost/core/typeinfo.hpp>
#include <storm/models/symbolic/MarkovAutomaton.h>
namespace storm {
namespace modelchecker {
@ -360,6 +362,10 @@ namespace storm {
template class AbstractModelChecker<storm::models::symbolic::Ctmc<storm::dd::DdType::Sylvan, double>>;
template class AbstractModelChecker<storm::models::symbolic::Ctmc<storm::dd::DdType::Sylvan, storm::RationalNumber>>;
template class AbstractModelChecker<storm::models::symbolic::Ctmc<storm::dd::DdType::Sylvan, storm::RationalFunction>>;
template class AbstractModelChecker<storm::models::symbolic::MarkovAutomaton<storm::dd::DdType::CUDD, double>>;
template class AbstractModelChecker<storm::models::symbolic::MarkovAutomaton<storm::dd::DdType::Sylvan, double>>;
template class AbstractModelChecker<storm::models::symbolic::MarkovAutomaton<storm::dd::DdType::Sylvan, storm::RationalNumber>>;
template class AbstractModelChecker<storm::models::symbolic::MarkovAutomaton<storm::dd::DdType::Sylvan, storm::RationalFunction>>;
template class AbstractModelChecker<storm::models::symbolic::StochasticTwoPlayerGame<storm::dd::DdType::CUDD, double>>;
template class AbstractModelChecker<storm::models::symbolic::StochasticTwoPlayerGame<storm::dd::DdType::Sylvan, double>>;
template class AbstractModelChecker<storm::models::symbolic::StochasticTwoPlayerGame<storm::dd::DdType::Sylvan, storm::RationalNumber>>;

2
src/storm/modelchecker/abstraction/BisimulationAbstractionRefinementModelChecker.cpp

@ -60,7 +60,7 @@ namespace storm {
template<typename ModelType>
std::shared_ptr<storm::models::Model<typename BisimulationAbstractionRefinementModelChecker<ModelType>::ValueType>> BisimulationAbstractionRefinementModelChecker<ModelType>::getAbstractModel() {
lastAbstractModel = this->bisimulation->getQuotient();
lastAbstractModel = this->bisimulation->getQuotient(storm::dd::bisimulation::QuotientFormat::Dd);
return lastAbstractModel;
}

21
src/storm/modelchecker/csl/HybridCtmcCslModelChecker.cpp

@ -24,22 +24,17 @@ namespace storm {
}
template <typename ModelType>
bool HybridCtmcCslModelChecker<ModelType>::canHandle(CheckTask<storm::logic::Formula, ValueType> const& checkTask) const {
return HybridCtmcCslModelChecker<ModelType>::canHandleImplementation<ValueType>(checkTask);
}
template <typename ModelType>
template<typename CValueType, typename std::enable_if<storm::NumberTraits<CValueType>::SupportsExponential, int>::type>
bool HybridCtmcCslModelChecker<ModelType>::canHandleImplementation(CheckTask<storm::logic::Formula, CValueType> const& checkTask) const {
storm::logic::Formula const& formula = checkTask.getFormula();
return formula.isInFragment(storm::logic::csrl().setGloballyFormulasAllowed(false).setLongRunAverageRewardFormulasAllowed(true).setLongRunAverageProbabilitiesAllowed(true).setTimeAllowed(true).setTimeOperatorsAllowed(true).setRewardAccumulationAllowed(true));
bool HybridCtmcCslModelChecker<ModelType>::canHandleStatic(CheckTask<storm::logic::Formula, ValueType> const& checkTask) {
auto fragment = storm::logic::csrl().setGloballyFormulasAllowed(false).setLongRunAverageRewardFormulasAllowed(true).setLongRunAverageProbabilitiesAllowed(true).setTimeAllowed(true).setTimeOperatorsAllowed(true).setRewardAccumulationAllowed(true);
if (!storm::NumberTraits<ValueType>::SupportsExponential) {
fragment.setBoundedUntilFormulasAllowed(false).setCumulativeRewardFormulasAllowed(false).setInstantaneousFormulasAllowed(false);
}
return checkTask.getFormula().isInFragment(fragment);
}
template <typename ModelType>
template<typename CValueType, typename std::enable_if<!storm::NumberTraits<CValueType>::SupportsExponential, int>::type>
bool HybridCtmcCslModelChecker<ModelType>::canHandleImplementation(CheckTask<storm::logic::Formula, CValueType> const& checkTask) const {
storm::logic::Formula const& formula = checkTask.getFormula();
return formula.isInFragment(storm::logic::csrl().setGloballyFormulasAllowed(false).setLongRunAverageRewardFormulasAllowed(true).setLongRunAverageProbabilitiesAllowed(true).setTimeAllowed(true).setTimeOperatorsAllowed(true).setBoundedUntilFormulasAllowed(false).setCumulativeRewardFormulasAllowed(false).setInstantaneousFormulasAllowed(false));
bool HybridCtmcCslModelChecker<ModelType>::canHandle(CheckTask<storm::logic::Formula, ValueType> const& checkTask) const {
return canHandleStatic(checkTask);
}
template<typename ModelType>

12
src/storm/modelchecker/csl/HybridCtmcCslModelChecker.h

@ -20,6 +20,9 @@ namespace storm {
static const storm::dd::DdType DdType = ModelType::DdType;
explicit HybridCtmcCslModelChecker(ModelType const& model);
// Returns false, if this task can certainly not be handled by this model checker (independent of the concrete model).
static bool canHandleStatic(CheckTask<storm::logic::Formula, ValueType> const& checkTask);
// The implemented methods of the AbstractModelChecker interface.
virtual bool canHandle(CheckTask<storm::logic::Formula, ValueType> const& checkTask) const override;
@ -33,14 +36,7 @@ namespace storm {
virtual std::unique_ptr<CheckResult> computeCumulativeRewards(Environment const& env, storm::logic::RewardMeasureType rewardMeasureType, CheckTask<storm::logic::CumulativeRewardFormula, ValueType> const& checkTask) override;
virtual std::unique_ptr<CheckResult> computeReachabilityRewards(Environment const& env, storm::logic::RewardMeasureType rewardMeasureType, CheckTask<storm::logic::EventuallyFormula, ValueType> const& checkTask) override;
virtual std::unique_ptr<CheckResult> computeReachabilityTimes(Environment const& env, storm::logic::RewardMeasureType rewardMeasureType, CheckTask<storm::logic::EventuallyFormula, ValueType> const& checkTask) override;
private:
template<typename CValueType = ValueType, typename std::enable_if<storm::NumberTraits<CValueType>::SupportsExponential, int>::type = 0>
bool canHandleImplementation(CheckTask<storm::logic::Formula, CValueType> const& checkTask) const;
template<typename CValueType = ValueType, typename std::enable_if<!storm::NumberTraits<CValueType>::SupportsExponential, int>::type = 0>
bool canHandleImplementation(CheckTask<storm::logic::Formula, CValueType> const& checkTask) const;
};
} // namespace modelchecker

126
src/storm/modelchecker/csl/HybridMarkovAutomatonCslModelChecker.cpp

@ -0,0 +1,126 @@
#include "storm/modelchecker/csl/HybridMarkovAutomatonCslModelChecker.h"
#include "storm/models/symbolic/StandardRewardModel.h"
#include "storm/modelchecker/csl/helper/SparseMarkovAutomatonCslHelper.h"
#include "storm/modelchecker/csl/helper/HybridMarkovAutomatonCslHelper.h"
#include "storm/modelchecker/prctl/helper/HybridMdpPrctlHelper.h"
#include "storm/modelchecker/results/SymbolicQualitativeCheckResult.h"
#include "storm/storage/dd/DdManager.h"
#include "storm/storage/dd/Add.h"
#include "storm/storage/dd/Bdd.h"
#include "storm/utility/FilteredRewardModel.h"
#include "storm/logic/FragmentSpecification.h"
#include "storm/exceptions/NotImplementedException.h"
#include "storm/exceptions/InvalidPropertyException.h"
namespace storm {
namespace modelchecker {
template<typename ModelType>
HybridMarkovAutomatonCslModelChecker<ModelType>::HybridMarkovAutomatonCslModelChecker(ModelType const& model) : SymbolicPropositionalModelChecker<ModelType>(model) {
// Intentionally left empty.
}
template <typename ModelType>
bool HybridMarkovAutomatonCslModelChecker<ModelType>::canHandleStatic(CheckTask<storm::logic::Formula, ValueType> const& checkTask) {
auto singleObjectiveFragment = storm::logic::csl().setGloballyFormulasAllowed(false).setNextFormulasAllowed(false).setRewardOperatorsAllowed(true).setReachabilityRewardFormulasAllowed(true).setTotalRewardFormulasAllowed(false).setTimeAllowed(true).setLongRunAverageProbabilitiesAllowed(true).setLongRunAverageRewardFormulasAllowed(true).setRewardAccumulationAllowed(true).setInstantaneousFormulasAllowed(false).setCumulativeRewardFormulasAllowed(false);
if (!storm::NumberTraits<ValueType>::SupportsExponential) {
singleObjectiveFragment.setBoundedUntilFormulasAllowed(false);
}
return checkTask.getFormula().isInFragment(singleObjectiveFragment);
}
template <typename ModelType>
bool HybridMarkovAutomatonCslModelChecker<ModelType>::canHandle(CheckTask<storm::logic::Formula, ValueType> const& checkTask) const {
return canHandleStatic(checkTask);
}
template<typename ModelType>
std::unique_ptr<CheckResult> HybridMarkovAutomatonCslModelChecker<ModelType>::computeUntilProbabilities(Environment const& env, CheckTask<storm::logic::UntilFormula, ValueType> const& checkTask) {
STORM_LOG_THROW(checkTask.isOptimizationDirectionSet(), storm::exceptions::InvalidPropertyException, "Formula needs to specify whether minimal or maximal values are to be computed on nondeterministic model.");
storm::logic::UntilFormula const& pathFormula = checkTask.getFormula();
std::unique_ptr<CheckResult> leftResultPointer = this->check(env, pathFormula.getLeftSubformula());
std::unique_ptr<CheckResult> rightResultPointer = this->check(env, pathFormula.getRightSubformula());
SymbolicQualitativeCheckResult<DdType> const& leftResult = leftResultPointer->asSymbolicQualitativeCheckResult<DdType>();
SymbolicQualitativeCheckResult<DdType> const& rightResult = rightResultPointer->asSymbolicQualitativeCheckResult<DdType>();
return storm::modelchecker::helper::HybridMdpPrctlHelper<DdType, ValueType>::computeUntilProbabilities(env, checkTask.getOptimizationDirection(), this->getModel(), this->getModel().getTransitionMatrix(), leftResult.getTruthValuesVector(), rightResult.getTruthValuesVector(), checkTask.isQualitativeSet());
}
template<typename ModelType>
std::unique_ptr<CheckResult> HybridMarkovAutomatonCslModelChecker<ModelType>::computeReachabilityRewards(Environment const& env, storm::logic::RewardMeasureType, CheckTask<storm::logic::EventuallyFormula, ValueType> const& checkTask) {
STORM_LOG_THROW(checkTask.isOptimizationDirectionSet(), storm::exceptions::InvalidPropertyException, "Formula needs to specify whether minimal or maximal values are to be computed on nondeterministic model.");
storm::logic::EventuallyFormula const& eventuallyFormula = checkTask.getFormula();
std::unique_ptr<CheckResult> subResultPointer = this->check(env, eventuallyFormula.getSubformula());
SymbolicQualitativeCheckResult<DdType> const& subResult = subResultPointer->asSymbolicQualitativeCheckResult<DdType>();
auto rewardModel = storm::utility::createFilteredRewardModel(this->getModel(), checkTask);
return storm::modelchecker::helper::HybridMarkovAutomatonCslHelper::computeReachabilityRewards<DdType, ValueType>(env, checkTask.getOptimizationDirection(), this->getModel(), this->getModel().getTransitionMatrix(), this->getModel().getMarkovianStates(), this->getModel().getExitRateVector(), rewardModel.get(), subResult.getTruthValuesVector(), checkTask.isQualitativeSet());
}
template<typename ModelType>
std::unique_ptr<CheckResult> HybridMarkovAutomatonCslModelChecker<ModelType>::computeReachabilityTimes(Environment const& env, storm::logic::RewardMeasureType, CheckTask<storm::logic::EventuallyFormula, ValueType> const& checkTask) {
STORM_LOG_THROW(checkTask.isOptimizationDirectionSet(), storm::exceptions::InvalidPropertyException, "Formula needs to specify whether minimal or maximal values are to be computed on nondeterministic model.");
storm::logic::EventuallyFormula const& eventuallyFormula = checkTask.getFormula();
std::unique_ptr<CheckResult> subResultPointer = this->check(env, eventuallyFormula.getSubformula());
SymbolicQualitativeCheckResult<DdType> const& subResult = subResultPointer->asSymbolicQualitativeCheckResult<DdType>();
storm::models::symbolic::StandardRewardModel<DdType, ValueType> timeRewardModel(this->getModel().getManager().getConstant(storm::utility::one<ValueType>()), boost::none, boost::none);
return storm::modelchecker::helper::HybridMarkovAutomatonCslHelper::computeReachabilityRewards<DdType, ValueType>(env, checkTask.getOptimizationDirection(), this->getModel(), this->getModel().getTransitionMatrix(), this->getModel().getMarkovianStates(), this->getModel().getExitRateVector(), timeRewardModel, subResult.getTruthValuesVector(), checkTask.isQualitativeSet());
}
template<typename ModelType>
std::unique_ptr<CheckResult> HybridMarkovAutomatonCslModelChecker<ModelType>::computeBoundedUntilProbabilities(Environment const& env, CheckTask<storm::logic::BoundedUntilFormula, ValueType> const& checkTask) {
storm::logic::BoundedUntilFormula const& pathFormula = checkTask.getFormula();
std::unique_ptr<CheckResult> leftResultPointer = this->check(env, pathFormula.getLeftSubformula());
SymbolicQualitativeCheckResult<DdType> const& leftResult = leftResultPointer->asSymbolicQualitativeCheckResult<DdType>();
std::unique_ptr<CheckResult> rightResultPointer = this->check(env, pathFormula.getRightSubformula());
SymbolicQualitativeCheckResult<DdType> const& rightResult = rightResultPointer->asSymbolicQualitativeCheckResult<DdType>();
STORM_LOG_THROW(checkTask.isOptimizationDirectionSet(), storm::exceptions::InvalidPropertyException, "Formula needs to specify whether minimal or maximal values are to be computed on nondeterministic model.");
STORM_LOG_THROW(pathFormula.getTimeBoundReference().isTimeBound(), storm::exceptions::NotImplementedException, "Currently step-bounded and reward-bounded properties on MarkovAutomatons are not supported.");
double lowerBound = 0;
double upperBound = 0;
if (pathFormula.hasLowerBound()) {
lowerBound = pathFormula.getLowerBound<double>();
}
if (pathFormula.hasUpperBound()) {
upperBound = pathFormula.getNonStrictUpperBound<double>();
} else {
upperBound = storm::utility::infinity<double>();
}
return storm::modelchecker::helper::HybridMarkovAutomatonCslHelper::computeBoundedUntilProbabilities<DdType, ValueType>(env, checkTask.getOptimizationDirection(), this->getModel(), this->getModel().getTransitionMatrix(), this->getModel().getMarkovianStates(), this->getModel().getExitRateVector(), leftResult.getTruthValuesVector(), rightResult.getTruthValuesVector(), checkTask.isQualitativeSet(), lowerBound, upperBound);
}
template<typename ModelType>
std::unique_ptr<CheckResult> HybridMarkovAutomatonCslModelChecker<ModelType>::computeLongRunAverageProbabilities(Environment const& env, CheckTask<storm::logic::StateFormula, ValueType> const& checkTask) {
storm::logic::StateFormula const& stateFormula = checkTask.getFormula();
std::unique_ptr<CheckResult> subResultPointer = this->check(env, stateFormula);
SymbolicQualitativeCheckResult<DdType> const& subResult = subResultPointer->asSymbolicQualitativeCheckResult<DdType>();
STORM_LOG_THROW(checkTask.isOptimizationDirectionSet(), storm::exceptions::InvalidPropertyException, "Formula needs to specify whether minimal or maximal values are to be computed on nondeterministic model.");
return storm::modelchecker::helper::HybridMarkovAutomatonCslHelper::computeLongRunAverageProbabilities(env, checkTask.getOptimizationDirection(), this->getModel(), this->getModel().getTransitionMatrix(), this->getModel().getMarkovianStates(), this->getModel().getExitRateVector(), subResult.getTruthValuesVector());
}
template<typename ModelType>
std::unique_ptr<CheckResult> HybridMarkovAutomatonCslModelChecker<ModelType>::computeLongRunAverageRewards(Environment const& env, storm::logic::RewardMeasureType rewardMeasureType, CheckTask<storm::logic::LongRunAverageRewardFormula, ValueType> const& checkTask) {
STORM_LOG_THROW(checkTask.isOptimizationDirectionSet(), storm::exceptions::InvalidPropertyException, "Formula needs to specify whether minimal or maximal values are to be computed on nondeterministic model.");
auto rewardModel = storm::utility::createFilteredRewardModel(this->getModel(), checkTask);
return storm::modelchecker::helper::HybridMarkovAutomatonCslHelper::computeLongRunAverageRewards(env, checkTask.getOptimizationDirection(), this->getModel(), this->getModel().getTransitionMatrix(), this->getModel().getMarkovianStates(), this->getModel().getExitRateVector(), rewardModel.get());
}
// Explicitly instantiate the model checker.
template class HybridMarkovAutomatonCslModelChecker<storm::models::symbolic::MarkovAutomaton<storm::dd::DdType::CUDD, double>>;
template class HybridMarkovAutomatonCslModelChecker<storm::models::symbolic::MarkovAutomaton<storm::dd::DdType::Sylvan, double>>;
template class HybridMarkovAutomatonCslModelChecker<storm::models::symbolic::MarkovAutomaton<storm::dd::DdType::Sylvan, storm::RationalNumber>>;
} // namespace modelchecker
} // namespace storm

36
src/storm/modelchecker/csl/HybridMarkovAutomatonCslModelChecker.h

@ -0,0 +1,36 @@
#pragma once
#include "storm/modelchecker/propositional/SymbolicPropositionalModelChecker.h"
#include "storm/models/symbolic/MarkovAutomaton.h"
#include "storm/solver/LinearEquationSolver.h"
#include "storm/utility/NumberTraits.h"
namespace storm {
namespace modelchecker {
template<typename ModelType>
class HybridMarkovAutomatonCslModelChecker : public SymbolicPropositionalModelChecker<ModelType> {
public:
typedef typename ModelType::ValueType ValueType;
static const storm::dd::DdType DdType = ModelType::DdType;
explicit HybridMarkovAutomatonCslModelChecker(ModelType const& model);
// The implemented methods of the AbstractModelChecker interface.
static bool canHandleStatic(CheckTask<storm::logic::Formula, ValueType> const& checkTask);
virtual bool canHandle(CheckTask<storm::logic::Formula, ValueType> const& checkTask) const override;
virtual std::unique_ptr<CheckResult> computeBoundedUntilProbabilities(Environment const& env, CheckTask<storm::logic::BoundedUntilFormula, ValueType> const& checkTask) override;
virtual std::unique_ptr<CheckResult> computeUntilProbabilities(Environment const& env, CheckTask<storm::logic::UntilFormula, ValueType> const& checkTask) override;
virtual std::unique_ptr<CheckResult> computeReachabilityRewards(Environment const& env, storm::logic::RewardMeasureType rewardMeasureType, CheckTask<storm::logic::EventuallyFormula, ValueType> const& checkTask) override;
virtual std::unique_ptr<CheckResult> computeReachabilityTimes(Environment const& env, storm::logic::RewardMeasureType rewardMeasureType, CheckTask<storm::logic::EventuallyFormula, ValueType> const& checkTask) override;
virtual std::unique_ptr<CheckResult> computeLongRunAverageProbabilities(Environment const& env, CheckTask<storm::logic::StateFormula, ValueType> const& checkTask) override;
virtual std::unique_ptr<CheckResult> computeLongRunAverageRewards(Environment const& env, storm::logic::RewardMeasureType rewardMeasureType, CheckTask<storm::logic::LongRunAverageRewardFormula, ValueType> const& checkTask) override;
};
} // namespace modelchecker
} // namespace storm

23
src/storm/modelchecker/csl/SparseCtmcCslModelChecker.cpp

@ -29,23 +29,18 @@ namespace storm {
// Intentionally left empty.
}
template <typename SparseCtmcModelType>
bool SparseCtmcCslModelChecker<SparseCtmcModelType>::canHandle(CheckTask<storm::logic::Formula, ValueType> const& checkTask) const {
return SparseCtmcCslModelChecker<SparseCtmcModelType>::canHandleImplementation<ValueType>(checkTask);
}
template <typename SparseCtmcModelType>
template<typename CValueType, typename std::enable_if<storm::NumberTraits<CValueType>::SupportsExponential, int>::type>
bool SparseCtmcCslModelChecker<SparseCtmcModelType>::canHandleImplementation(CheckTask<storm::logic::Formula, CValueType> const& checkTask) const {
storm::logic::Formula const& formula = checkTask.getFormula();
return formula.isInFragment(storm::logic::csrl().setGloballyFormulasAllowed(false).setLongRunAverageRewardFormulasAllowed(true).setLongRunAverageProbabilitiesAllowed(true).setTimeAllowed(true).setTotalRewardFormulasAllowed(true).setRewardAccumulationAllowed(true));
template <typename ModelType>
bool SparseCtmcCslModelChecker<ModelType>::canHandleStatic(CheckTask<storm::logic::Formula, ValueType> const& checkTask) {
auto fragment = storm::logic::csrl().setGloballyFormulasAllowed(false).setLongRunAverageRewardFormulasAllowed(true).setLongRunAverageProbabilitiesAllowed(true).setTimeAllowed(true).setTimeOperatorsAllowed(true).setTotalRewardFormulasAllowed(true).setRewardAccumulationAllowed(true);
if (!storm::NumberTraits<ValueType>::SupportsExponential) {
fragment.setBoundedUntilFormulasAllowed(false).setCumulativeRewardFormulasAllowed(false).setInstantaneousFormulasAllowed(false);
}
return checkTask.getFormula().isInFragment(fragment);
}
template <typename SparseCtmcModelType>
template<typename CValueType, typename std::enable_if<!storm::NumberTraits<CValueType>::SupportsExponential, int>::type>
bool SparseCtmcCslModelChecker<SparseCtmcModelType>::canHandleImplementation(CheckTask<storm::logic::Formula, CValueType> const& checkTask) const {
storm::logic::Formula const& formula = checkTask.getFormula();
return formula.isInFragment(storm::logic::csrl().setGloballyFormulasAllowed(false).setLongRunAverageRewardFormulasAllowed(true).setLongRunAverageProbabilitiesAllowed(true).setTimeAllowed(true).setTotalRewardFormulasAllowed(true).setBoundedUntilFormulasAllowed(false).setCumulativeRewardFormulasAllowed(false).setInstantaneousFormulasAllowed(false));
bool SparseCtmcCslModelChecker<SparseCtmcModelType>::canHandle(CheckTask<storm::logic::Formula, ValueType> const& checkTask) const {
return canHandleStatic(checkTask);
}
template <typename SparseCtmcModelType>

10
src/storm/modelchecker/csl/SparseCtmcCslModelChecker.h

@ -21,6 +21,9 @@ namespace storm {
explicit SparseCtmcCslModelChecker(SparseCtmcModelType const& model);
// Returns false, if this task can certainly not be handled by this model checker (independent of the concrete model).
static bool canHandleStatic(CheckTask<storm::logic::Formula, ValueType> const& checkTask);
// The implemented methods of the AbstractModelChecker interface.
virtual bool canHandle(CheckTask<storm::logic::Formula, ValueType> const& checkTask) const override;
virtual std::unique_ptr<CheckResult> computeBoundedUntilProbabilities(Environment const& env, CheckTask<storm::logic::BoundedUntilFormula, ValueType> const& checkTask) override;
@ -40,13 +43,6 @@ namespace storm {
*/
std::vector<ValueType> computeAllTransientProbabilities(Environment const& env, CheckTask<storm::logic::BoundedUntilFormula, ValueType> const& checkTask);
private:
template<typename CValueType = ValueType, typename std::enable_if<storm::NumberTraits<CValueType>::SupportsExponential, int>::type = 0>
bool canHandleImplementation(CheckTask<storm::logic::Formula, CValueType> const& checkTask) const;
template<typename CValueType = ValueType, typename std::enable_if<!storm::NumberTraits<CValueType>::SupportsExponential, int>::type = 0>
bool canHandleImplementation(CheckTask<storm::logic::Formula, CValueType> const& checkTask) const;
};
} // namespace modelchecker

54
src/storm/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.cpp

@ -11,6 +11,7 @@
#include "storm/settings/SettingsManager.h"
#include "storm/settings/modules/GeneralSettings.h"
#include "storm/solver/SolveGoal.h"
#include "storm/modelchecker/results/ExplicitQualitativeCheckResult.h"
#include "storm/modelchecker/results/ExplicitQuantitativeCheckResult.h"
@ -27,38 +28,31 @@ namespace storm {
// Intentionally left empty.
}
template<typename SparseMarkovAutomatonModelType>
bool SparseMarkovAutomatonCslModelChecker<SparseMarkovAutomatonModelType>::canHandle(CheckTask<storm::logic::Formula, ValueType> const& checkTask) const {
return SparseMarkovAutomatonCslModelChecker<SparseMarkovAutomatonModelType>::canHandleImplementation<ValueType>(checkTask);
}
template <typename SparseMarkovAutomatonModelType>
template<typename CValueType, typename std::enable_if<storm::NumberTraits<CValueType>::SupportsExponential, int>::type>
bool SparseMarkovAutomatonCslModelChecker<SparseMarkovAutomatonModelType>::canHandleImplementation(CheckTask<storm::logic::Formula, CValueType> const& checkTask) const {
storm::logic::Formula const& formula = checkTask.getFormula();
if(formula.isInFragment(storm::logic::csl().setGloballyFormulasAllowed(false).setNextFormulasAllowed(false).setRewardOperatorsAllowed(true).setReachabilityRewardFormulasAllowed(true).setTotalRewardFormulasAllowed(true).setTimeAllowed(true).setLongRunAverageProbabilitiesAllowed(true).setLongRunAverageRewardFormulasAllowed(true).setRewardAccumulationAllowed(true))) {
template <typename ModelType>
bool SparseMarkovAutomatonCslModelChecker<ModelType>::canHandleStatic(CheckTask<storm::logic::Formula, ValueType> const& checkTask, bool* requiresSingleInitialState) {
auto singleObjectiveFragment = storm::logic::csl().setGloballyFormulasAllowed(false).setNextFormulasAllowed(false).setRewardOperatorsAllowed(true).setReachabilityRewardFormulasAllowed(true).setTotalRewardFormulasAllowed(true).setTimeAllowed(true).setLongRunAverageProbabilitiesAllowed(true).setLongRunAverageRewardFormulasAllowed(true).setRewardAccumulationAllowed(true).setInstantaneousFormulasAllowed(false);
auto multiObjectiveFragment = storm::logic::multiObjective().setTimeAllowed(true).setTimeBoundedUntilFormulasAllowed(true);
if (!storm::NumberTraits<ValueType>::SupportsExponential) {
singleObjectiveFragment.setBoundedUntilFormulasAllowed(false).setCumulativeRewardFormulasAllowed(false);
multiObjectiveFragment.setTimeBoundedUntilFormulasAllowed(false).setCumulativeRewardFormulasAllowed(false);
}
if (checkTask.getFormula().isInFragment(singleObjectiveFragment)) {
return true;
} else {
// Check whether we consider a multi-objective formula
// For multi-objective model checking, each initial state requires an individual scheduler (in contrast to single objective model checking). Let's exclude multiple initial states.
if (this->getModel().getInitialStates().getNumberOfSetBits() > 1) return false;
if (!checkTask.isOnlyInitialStatesRelevantSet()) return false;
return formula.isInFragment(storm::logic::multiObjective().setTimeAllowed(true).setTimeBoundedUntilFormulasAllowed(true));
} else if (checkTask.isOnlyInitialStatesRelevantSet() && checkTask.getFormula().isInFragment(multiObjectiveFragment)) {
if (requiresSingleInitialState) {
*requiresSingleInitialState = true;
}
}
return false;
}
template <typename SparseMarkovAutomatonModelType>
template<typename CValueType, typename std::enable_if<!storm::NumberTraits<CValueType>::SupportsExponential, int>::type>
bool SparseMarkovAutomatonCslModelChecker<SparseMarkovAutomatonModelType>::canHandleImplementation(CheckTask<storm::logic::Formula, CValueType> const& checkTask) const {
storm::logic::Formula const& formula = checkTask.getFormula();
if(formula.isInFragment(storm::logic::prctl().setGloballyFormulasAllowed(false).setNextFormulasAllowed(false).setRewardOperatorsAllowed(true).setReachabilityRewardFormulasAllowed(true).setTotalRewardFormulasAllowed(true).setTimeAllowed(true).setLongRunAverageProbabilitiesAllowed(true).setLongRunAverageRewardFormulasAllowed(true).setTimeBoundedUntilFormulasAllowed(false).setCumulativeRewardFormulasAllowed(false).setInstantaneousFormulasAllowed(false))) {
return true;
template<typename SparseMarkovAutomatonModelType>
bool SparseMarkovAutomatonCslModelChecker<SparseMarkovAutomatonModelType>::canHandle(CheckTask<storm::logic::Formula, ValueType> const& checkTask) const {
bool requiresSingleInitialState = false;
if (canHandleStatic(checkTask, &requiresSingleInitialState)) {
return !requiresSingleInitialState || this->getModel().getInitialStates().getNumberOfSetBits() == 1;
} else {
// Check whether we consider a multi-objective formula
// For multi-objective model checking, each initial state requires an individual scheduler (in contrast to single objective model checking). Let's exclude multiple initial states.
if (this->getModel().getInitialStates().getNumberOfSetBits() > 1) return false;
if (!checkTask.isOnlyInitialStatesRelevantSet()) return false;
return formula.isInFragment(storm::logic::multiObjective().setTimeAllowed(true).setTimeBoundedUntilFormulasAllowed(false).setCumulativeRewardFormulasAllowed(false).setInstantaneousFormulasAllowed(false));
return false;
}
}
@ -66,11 +60,13 @@ namespace storm {
std::unique_ptr<CheckResult> SparseMarkovAutomatonCslModelChecker<SparseMarkovAutomatonModelType>::computeBoundedUntilProbabilities(Environment const& env, CheckTask<storm::logic::BoundedUntilFormula, ValueType> const& checkTask) {
storm::logic::BoundedUntilFormula const& pathFormula = checkTask.getFormula();
STORM_LOG_THROW(checkTask.isOptimizationDirectionSet(), storm::exceptions::InvalidPropertyException, "Formula needs to specify whether minimal or maximal values are to be computed on nondeterministic model.");
STORM_LOG_THROW(pathFormula.getLeftSubformula().isTrueFormula(), storm::exceptions::NotImplementedException, "Only bounded properties of the form 'true U[t1, t2] phi' are currently supported.");
STORM_LOG_THROW(this->getModel().isClosed(), storm::exceptions::InvalidPropertyException, "Unable to compute time-bounded reachability probabilities in non-closed Markov automaton.");
std::unique_ptr<CheckResult> rightResultPointer = this->check(env, pathFormula.getRightSubformula());
ExplicitQualitativeCheckResult const& rightResult = rightResultPointer->asExplicitQualitativeCheckResult();
std::unique_ptr<CheckResult> leftResultPointer = this->check(env, pathFormula.getLeftSubformula());
ExplicitQualitativeCheckResult const& leftResult = leftResultPointer->asExplicitQualitativeCheckResult();
STORM_LOG_THROW(pathFormula.getTimeBoundReference().isTimeBound(), storm::exceptions::NotImplementedException, "Currently step-bounded and reward-bounded properties on MAs are not supported.");
double lowerBound = 0;
double upperBound = 0;
@ -83,7 +79,7 @@ namespace storm {
upperBound = storm::utility::infinity<double>();
}
std::vector<ValueType> result = storm::modelchecker::helper::SparseMarkovAutomatonCslHelper::computeBoundedUntilProbabilities(env, checkTask.getOptimizationDirection(), this->getModel().getTransitionMatrix(), this->getModel().getExitRates(), this->getModel().getMarkovianStates(), rightResult.getTruthValuesVector(), std::make_pair(lowerBound, upperBound));
std::vector<ValueType> result = storm::modelchecker::helper::SparseMarkovAutomatonCslHelper::computeBoundedUntilProbabilities(env, storm::solver::SolveGoal<ValueType>(this->getModel(), checkTask), this->getModel().getTransitionMatrix(), this->getModel().getExitRates(), this->getModel().getMarkovianStates(), leftResult.getTruthValuesVector(), rightResult.getTruthValuesVector(), std::make_pair(lowerBound, upperBound));
return std::unique_ptr<CheckResult>(new ExplicitQuantitativeCheckResult<ValueType>(std::move(result)));
}

13
src/storm/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.h

@ -19,6 +19,12 @@ namespace storm {
explicit SparseMarkovAutomatonCslModelChecker(SparseMarkovAutomatonModelType const& model);
/*!
* Returns false, if this task can certainly not be handled by this model checker (independent of the concrete model).
* @param requiresSingleInitialState if not nullptr, this flag is set to true iff checking this formula requires a model with a single initial state
*/
static bool canHandleStatic(CheckTask<storm::logic::Formula, ValueType> const& checkTask, bool* requiresSingleInitialState = nullptr);
// The implemented methods of the AbstractModelChecker interface.
virtual bool canHandle(CheckTask<storm::logic::Formula, ValueType> const& checkTask) const override;
virtual std::unique_ptr<CheckResult> computeBoundedUntilProbabilities(Environment const& env, CheckTask<storm::logic::BoundedUntilFormula, ValueType> const& checkTask) override;
@ -29,13 +35,6 @@ namespace storm {
virtual std::unique_ptr<CheckResult> computeLongRunAverageRewards(Environment const& env, storm::logic::RewardMeasureType rewardMeasureType, CheckTask<storm::logic::LongRunAverageRewardFormula, ValueType> const& checkTask) override;
virtual std::unique_ptr<CheckResult> computeReachabilityTimes(Environment const& env, storm::logic::RewardMeasureType rewardMeasureType, CheckTask<storm::logic::EventuallyFormula, ValueType> const& checkTask) override;
virtual std::unique_ptr<CheckResult> checkMultiObjectiveFormula(Environment const& env, CheckTask<storm::logic::MultiObjectiveFormula, ValueType> const& checkTask) override;
private:
template<typename CValueType = ValueType, typename std::enable_if<storm::NumberTraits<CValueType>::SupportsExponential, int>::type = 0>
bool canHandleImplementation(CheckTask<storm::logic::Formula, CValueType> const& checkTask) const;
template<typename CValueType = ValueType, typename std::enable_if<!storm::NumberTraits<CValueType>::SupportsExponential, int>::type = 0>
bool canHandleImplementation(CheckTask<storm::logic::Formula, CValueType> const& checkTask) const;
};
}
}

150
src/storm/modelchecker/csl/helper/HybridMarkovAutomatonCslHelper.cpp

@ -0,0 +1,150 @@
#include "storm/modelchecker/csl/helper/HybridMarkovAutomatonCslHelper.h"
#include "storm/modelchecker/csl/helper/SparseMarkovAutomatonCslHelper.h"
#include "storm/modelchecker/prctl/helper/HybridMdpPrctlHelper.h"
#include "storm/storage/dd/DdManager.h"
#include "storm/storage/dd/Add.h"
#include "storm/storage/dd/Bdd.h"
#include "storm/utility/macros.h"
#include "storm/utility/graph.h"
#include "storm/utility/constants.h"
#include "storm/solver/SolveGoal.h"
#include "storm/models/symbolic/StandardRewardModel.h"
#include "storm/modelchecker/results/SymbolicQualitativeCheckResult.h"
#include "storm/modelchecker/results/SymbolicQuantitativeCheckResult.h"
#include "storm/modelchecker/results/HybridQuantitativeCheckResult.h"
#include "storm/modelchecker/results/ExplicitQuantitativeCheckResult.h"
#include "storm/utility/Stopwatch.h"
#include "storm/exceptions/InvalidOperationException.h"
#include "storm/exceptions/NotSupportedException.h"
namespace storm {
namespace modelchecker {
namespace helper {
template<storm::dd::DdType DdType, class ValueType>
void discretizeRewardModel(typename storm::models::symbolic::Model<DdType, ValueType>::RewardModelType& rewardModel, storm::dd::Add<DdType, ValueType> const& exitRateVector, storm::dd::Bdd<DdType> const& markovianStates) {
if (rewardModel.hasStateRewards()) {
rewardModel.getStateRewardVector() *= markovianStates.ite(exitRateVector.getDdManager().template getAddOne<ValueType>() / exitRateVector, exitRateVector.getDdManager().template getAddZero<ValueType>());
}
}
template<storm::dd::DdType DdType, class ValueType>
std::unique_ptr<CheckResult> HybridMarkovAutomatonCslHelper::computeReachabilityRewards(Environment const& env, OptimizationDirection dir, storm::models::symbolic::MarkovAutomaton<DdType, ValueType> const& model, storm::dd::Add<DdType, ValueType> const& transitionMatrix, storm::dd::Bdd<DdType> const& markovianStates, storm::dd::Add<DdType, ValueType> const& exitRateVector, typename storm::models::symbolic::Model<DdType, ValueType>::RewardModelType const& rewardModel, storm::dd::Bdd<DdType> const& targetStates, bool qualitative) {
auto discretizedRewardModel = rewardModel;
discretizeRewardModel(discretizedRewardModel, exitRateVector, markovianStates);
return HybridMdpPrctlHelper<DdType, ValueType>::computeReachabilityRewards(env, dir, model, transitionMatrix, discretizedRewardModel, targetStates, qualitative);
}
template<storm::dd::DdType DdType, typename ValueType, typename std::enable_if<storm::NumberTraits<ValueType>::SupportsExponential, int>::type>
std::unique_ptr<CheckResult> HybridMarkovAutomatonCslHelper::computeBoundedUntilProbabilities(Environment const& env, OptimizationDirection dir, storm::models::symbolic::MarkovAutomaton<DdType, ValueType> const& model, storm::dd::Add<DdType, ValueType> const& transitionMatrix, storm::dd::Bdd<DdType> const& markovianStates, storm::dd::Add<DdType, ValueType> const& exitRateVector, storm::dd::Bdd<DdType> const& phiStates, storm::dd::Bdd<DdType> const& psiStates, bool qualitative, double lowerBound, double upperBound) {
// If the time bounds are [0, inf], we rather call untimed reachability.
if (storm::utility::isZero(lowerBound) && upperBound == storm::utility::infinity<ValueType>()) {
return storm::modelchecker::helper::HybridMdpPrctlHelper<DdType, ValueType>::computeUntilProbabilities(env, dir, model, transitionMatrix, phiStates, psiStates, qualitative);
}
// If the interval is of the form [0,0], we can return the result directly
if (storm::utility::isZero(upperBound)) {
// In this case, the interval is of the form [0, 0].
return std::unique_ptr<CheckResult>(new SymbolicQuantitativeCheckResult<DdType, ValueType>(model.getReachableStates(), psiStates.template toAdd<ValueType>()));
}
// If we reach this point, we convert this query to an instance for the sparse engine.
storm::utility::Stopwatch conversionWatch(true);
// Create ODD for the translation.
storm::dd::Odd odd = model.getReachableStates().createOdd();
storm::storage::SparseMatrix<ValueType> explicitTransitionMatrix = transitionMatrix.toMatrix(model.getNondeterminismVariables(), odd, odd);
std::vector<ValueType> explicitExitRateVector = exitRateVector.toVector(odd);
conversionWatch.stop();
STORM_LOG_INFO("Converting symbolic matrix to explicit representation done in " << conversionWatch.getTimeInMilliseconds() << "ms.");
auto explicitResult = storm::modelchecker::helper::SparseMarkovAutomatonCslHelper::computeBoundedUntilProbabilities(env, storm::solver::SolveGoal<ValueType>(dir), explicitTransitionMatrix, explicitExitRateVector, markovianStates.toVector(odd), phiStates.toVector(odd), psiStates.toVector(odd), {lowerBound, upperBound});
return std::unique_ptr<CheckResult>(new HybridQuantitativeCheckResult<DdType, ValueType>(model.getReachableStates(), model.getManager().getBddZero(), model.getManager().template getAddZero<ValueType>(), model.getReachableStates(), std::move(odd), std::move(explicitResult)));
}
template<storm::dd::DdType DdType, typename ValueType, typename std::enable_if<!storm::NumberTraits<ValueType>::SupportsExponential, int>::type>
std::unique_ptr<CheckResult> HybridMarkovAutomatonCslHelper::computeBoundedUntilProbabilities(Environment const&, OptimizationDirection, storm::models::symbolic::MarkovAutomaton<DdType, ValueType> const&, storm::dd::Add<DdType, ValueType> const&, storm::dd::Bdd<DdType> const&, storm::dd::Add<DdType, ValueType> const&, storm::dd::Bdd<DdType> const&, storm::dd::Bdd<DdType> const&, bool, double, double) {
STORM_LOG_THROW(false, storm::exceptions::InvalidOperationException, "Computing bounded until probabilities is unsupported for this value type.");
}
template<storm::dd::DdType DdType, class ValueType>
std::unique_ptr<CheckResult> HybridMarkovAutomatonCslHelper::computeLongRunAverageProbabilities(Environment const& env, OptimizationDirection dir, storm::models::symbolic::MarkovAutomaton<DdType, ValueType> const& model, storm::dd::Add<DdType, ValueType> const& transitionMatrix, storm::dd::Bdd<DdType> const& markovianStates, storm::dd::Add<DdType, ValueType> const& exitRateVector, storm::dd::Bdd<DdType> const& psiStates) {
// Convert this query to an instance for the sparse engine.
storm::utility::Stopwatch conversionWatch(true);
// Create ODD for the translation.
storm::dd::Odd odd = model.getReachableStates().createOdd();
storm::storage::SparseMatrix<ValueType> explicitTransitionMatrix = transitionMatrix.toMatrix(model.getNondeterminismVariables(), odd, odd);
std::vector<ValueType> explicitExitRateVector = exitRateVector.toVector(odd);
conversionWatch.stop();
STORM_LOG_INFO("Converting symbolic matrix to explicit representation done in " << conversionWatch.getTimeInMilliseconds() << "ms.");
auto explicitResult = storm::modelchecker::helper::SparseMarkovAutomatonCslHelper::computeLongRunAverageProbabilities(env, dir, explicitTransitionMatrix, explicitTransitionMatrix.transpose(true), explicitExitRateVector, markovianStates.toVector(odd), psiStates.toVector(odd));
return std::unique_ptr<CheckResult>(new HybridQuantitativeCheckResult<DdType, ValueType>(model.getReachableStates(), model.getManager().getBddZero(), model.getManager().template getAddZero<ValueType>(), model.getReachableStates(), std::move(odd), std::move(explicitResult)));
}
template<storm::dd::DdType DdType, class ValueType>
std::unique_ptr<CheckResult> HybridMarkovAutomatonCslHelper::computeLongRunAverageRewards(Environment const& env, OptimizationDirection dir, storm::models::symbolic::MarkovAutomaton<DdType, ValueType> const& model, storm::dd::Add<DdType, ValueType> const& transitionMatrix, storm::dd::Bdd<DdType> const& markovianStates, storm::dd::Add<DdType, ValueType> const& exitRateVector, typename storm::models::symbolic::Model<DdType, ValueType>::RewardModelType const& rewardModel) {
// Convert this query to an instance for the sparse engine.
storm::utility::Stopwatch conversionWatch(true);
// Create ODD for the translation.
storm::dd::Odd odd = model.getReachableStates().createOdd();
std::vector<ValueType> explicitExitRateVector = exitRateVector.toVector(odd);
storm::storage::SparseMatrix<ValueType> explicitTransitionMatrix;
boost::optional<std::vector<ValueType>> optionalStateRewards, optionalStateActionRewards;
if (rewardModel.hasStateRewards()) {
optionalStateRewards = rewardModel.getStateRewardVector().toVector(odd);
}
if (rewardModel.hasStateActionRewards()) {
auto matrixRewards = transitionMatrix.toMatrixVector(rewardModel.getStateActionRewardVector(), model.getNondeterminismVariables(), odd, odd);
explicitTransitionMatrix = std::move(matrixRewards.first);
optionalStateActionRewards = std::move(matrixRewards.second);
} else {
explicitTransitionMatrix = transitionMatrix.toMatrix(model.getNondeterminismVariables(), odd, odd);
}
STORM_LOG_THROW(!rewardModel.hasTransitionRewards(), storm::exceptions::NotSupportedException, "Transition rewards are not supported in this engine.");
storm::models::sparse::StandardRewardModel<ValueType> explicitRewardModel(optionalStateRewards, optionalStateActionRewards);
conversionWatch.stop();
STORM_LOG_INFO("Converting symbolic matrix to explicit representation done in " << conversionWatch.getTimeInMilliseconds() << "ms.");
auto explicitResult = storm::modelchecker::helper::SparseMarkovAutomatonCslHelper::computeLongRunAverageRewards(env, dir, explicitTransitionMatrix, explicitTransitionMatrix.transpose(true), explicitExitRateVector, markovianStates.toVector(odd), explicitRewardModel);
return std::unique_ptr<CheckResult>(new HybridQuantitativeCheckResult<DdType, ValueType>(model.getReachableStates(), model.getManager().getBddZero(), model.getManager().template getAddZero<ValueType>(), model.getReachableStates(), std::move(odd), std::move(explicitResult)));
}
// Explicit instantiations.
// Cudd, double.
template std::unique_ptr<CheckResult> HybridMarkovAutomatonCslHelper::computeReachabilityRewards(Environment const& env, OptimizationDirection dir, storm::models::symbolic::MarkovAutomaton<storm::dd::DdType::CUDD, double> const& model, storm::dd::Add<storm::dd::DdType::CUDD, double> const& transitionMatrix, storm::dd::Bdd<storm::dd::DdType::CUDD> const& markovianStates, storm::dd::Add<storm::dd::DdType::CUDD, double> const& exitRateVector, typename storm::models::symbolic::Model<storm::dd::DdType::CUDD, double>::RewardModelType const& rewardModel, storm::dd::Bdd<storm::dd::DdType::CUDD> const& targetStates, bool qualitative);
template std::unique_ptr<CheckResult> HybridMarkovAutomatonCslHelper::computeBoundedUntilProbabilities(Environment const& env, OptimizationDirection dir, storm::models::symbolic::MarkovAutomaton<storm::dd::DdType::CUDD, double> const& model, storm::dd::Add<storm::dd::DdType::CUDD, double> const& transitionMatrix, storm::dd::Bdd<storm::dd::DdType::CUDD> const& markovianStates, storm::dd::Add<storm::dd::DdType::CUDD, double> const& exitRateVector, storm::dd::Bdd<storm::dd::DdType::CUDD> const& phiStates, storm::dd::Bdd<storm::dd::DdType::CUDD> const& psiStates, bool qualitative, double lowerBound, double upperBound);
template std::unique_ptr<CheckResult> HybridMarkovAutomatonCslHelper::computeLongRunAverageProbabilities(Environment const& env, OptimizationDirection dir, storm::models::symbolic::MarkovAutomaton<storm::dd::DdType::CUDD, double> const& model, storm::dd::Add<storm::dd::DdType::CUDD, double> const& transitionMatrix, storm::dd::Bdd<storm::dd::DdType::CUDD> const& markovianStates, storm::dd::Add<storm::dd::DdType::CUDD, double> const& exitRateVector, storm::dd::Bdd<storm::dd::DdType::CUDD> const& psiStates);
template std::unique_ptr<CheckResult> HybridMarkovAutomatonCslHelper::computeLongRunAverageRewards(Environment const& env, OptimizationDirection dir, storm::models::symbolic::MarkovAutomaton<storm::dd::DdType::CUDD, double> const& model, storm::dd::Add<storm::dd::DdType::CUDD, double> const& transitionMatrix, storm::dd::Bdd<storm::dd::DdType::CUDD> const& markovianStates, storm::dd::Add<storm::dd::DdType::CUDD, double> const& exitRateVector, typename storm::models::symbolic::Model<storm::dd::DdType::CUDD, double>::RewardModelType const& rewardModel);
// Sylvan, double.
template std::unique_ptr<CheckResult> HybridMarkovAutomatonCslHelper::computeReachabilityRewards(Environment const& env, OptimizationDirection dir, storm::models::symbolic::MarkovAutomaton<storm::dd::DdType::Sylvan, double> const& model, storm::dd::Add<storm::dd::DdType::Sylvan, double> const& transitionMatrix, storm::dd::Bdd<storm::dd::DdType::Sylvan> const& markovianStates, storm::dd::Add<storm::dd::DdType::Sylvan, double> const& exitRateVector, typename storm::models::symbolic::Model<storm::dd::DdType::Sylvan, double>::RewardModelType const& rewardModel, storm::dd::Bdd<storm::dd::DdType::Sylvan> const& targetStates, bool qualitative);
template std::unique_ptr<CheckResult> HybridMarkovAutomatonCslHelper::computeBoundedUntilProbabilities(Environment const& env, OptimizationDirection dir, storm::models::symbolic::MarkovAutomaton<storm::dd::DdType::Sylvan, double> const& model, storm::dd::Add<storm::dd::DdType::Sylvan, double> const& transitionMatrix, storm::dd::Bdd<storm::dd::DdType::Sylvan> const& markovianStates, storm::dd::Add<storm::dd::DdType::Sylvan, double> const& exitRateVector, storm::dd::Bdd<storm::dd::DdType::Sylvan> const& phiStates, storm::dd::Bdd<storm::dd::DdType::Sylvan> const& psiStates, bool qualitative, double lowerBound, double upperBound);
template std::unique_ptr<CheckResult> HybridMarkovAutomatonCslHelper::computeLongRunAverageProbabilities(Environment const& env, OptimizationDirection dir, storm::models::symbolic::MarkovAutomaton<storm::dd::DdType::Sylvan, double> const& model, storm::dd::Add<storm::dd::DdType::Sylvan, double> const& transitionMatrix, storm::dd::Bdd<storm::dd::DdType::Sylvan> const& markovianStates, storm::dd::Add<storm::dd::DdType::Sylvan, double> const& exitRateVector, storm::dd::Bdd<storm::dd::DdType::Sylvan> const& psiStates);
template std::unique_ptr<CheckResult> HybridMarkovAutomatonCslHelper::computeLongRunAverageRewards(Environment const& env, OptimizationDirection dir, storm::models::symbolic::MarkovAutomaton<storm::dd::DdType::Sylvan, double> const& model, storm::dd::Add<storm::dd::DdType::Sylvan, double> const& transitionMatrix, storm::dd::Bdd<storm::dd::DdType::Sylvan> const& markovianStates, storm::dd::Add<storm::dd::DdType::Sylvan, double> const& exitRateVector, typename storm::models::symbolic::Model<storm::dd::DdType::Sylvan, double>::RewardModelType const& rewardModel);
// Sylvan, rational number.
template std::unique_ptr<CheckResult> HybridMarkovAutomatonCslHelper::computeReachabilityRewards(Environment const& env, OptimizationDirection dir, storm::models::symbolic::MarkovAutomaton<storm::dd::DdType::Sylvan, storm::RationalNumber> const& model, storm::dd::Add<storm::dd::DdType::Sylvan, storm::RationalNumber> const& transitionMatrix, storm::dd::Bdd<storm::dd::DdType::Sylvan> const& markovianStates, storm::dd::Add<storm::dd::DdType::Sylvan, storm::RationalNumber> const& exitRateVector, typename storm::models::symbolic::Model<storm::dd::DdType::Sylvan, storm::RationalNumber>::RewardModelType const& rewardModel, storm::dd::Bdd<storm::dd::DdType::Sylvan> const& targetStates, bool qualitative);
template std::unique_ptr<CheckResult> HybridMarkovAutomatonCslHelper::computeBoundedUntilProbabilities(Environment const& env, OptimizationDirection dir, storm::models::symbolic::MarkovAutomaton<storm::dd::DdType::Sylvan, storm::RationalNumber> const& model, storm::dd::Add<storm::dd::DdType::Sylvan, storm::RationalNumber> const& transitionMatrix, storm::dd::Bdd<storm::dd::DdType::Sylvan> const& markovianStates, storm::dd::Add<storm::dd::DdType::Sylvan, storm::RationalNumber> const& exitRateVector, storm::dd::Bdd<storm::dd::DdType::Sylvan> const& phiStates, storm::dd::Bdd<storm::dd::DdType::Sylvan> const& psiStates, bool qualitative, double lowerBound, double upperBound);
template std::unique_ptr<CheckResult> HybridMarkovAutomatonCslHelper::computeLongRunAverageProbabilities(Environment const& env, OptimizationDirection dir, storm::models::symbolic::MarkovAutomaton<storm::dd::DdType::Sylvan, storm::RationalNumber> const& model, storm::dd::Add<storm::dd::DdType::Sylvan, storm::RationalNumber> const& transitionMatrix, storm::dd::Bdd<storm::dd::DdType::Sylvan> const& markovianStates, storm::dd::Add<storm::dd::DdType::Sylvan, storm::RationalNumber> const& exitRateVector, storm::dd::Bdd<storm::dd::DdType::Sylvan> const& psiStates);
template std::unique_ptr<CheckResult> HybridMarkovAutomatonCslHelper::computeLongRunAverageRewards(Environment const& env, OptimizationDirection dir, storm::models::symbolic::MarkovAutomaton<storm::dd::DdType::Sylvan, storm::RationalNumber> const& model, storm::dd::Add<storm::dd::DdType::Sylvan, storm::RationalNumber> const& transitionMatrix, storm::dd::Bdd<storm::dd::DdType::Sylvan> const& markovianStates, storm::dd::Add<storm::dd::DdType::Sylvan, storm::RationalNumber> const& exitRateVector, typename storm::models::symbolic::Model<storm::dd::DdType::Sylvan, storm::RationalNumber>::RewardModelType const& rewardModel);
}
}
}

42
src/storm/modelchecker/csl/helper/HybridMarkovAutomatonCslHelper.h

@ -0,0 +1,42 @@
#pragma once
#include <memory>
#include "storm/models/symbolic/MarkovAutomaton.h"
#include "storm/modelchecker/results/CheckResult.h"
#include "storm/utility/NumberTraits.h"
#include "storm/solver/OptimizationDirection.h"
namespace storm {
class Environment;
namespace modelchecker {
namespace helper {
class HybridMarkovAutomatonCslHelper {
public:
template<storm::dd::DdType DdType, typename ValueType>
static std::unique_ptr<CheckResult> computeReachabilityRewards(Environment const& env, OptimizationDirection dir, storm::models::symbolic::MarkovAutomaton<DdType, ValueType> const& model, storm::dd::Add<DdType, ValueType> const& transitionMatrix, storm::dd::Bdd<DdType> const& markovianStates, storm::dd::Add<DdType, ValueType> const& exitRateVector, typename storm::models::symbolic::Model<DdType, ValueType>::RewardModelType const& rewardModel, storm::dd::Bdd<DdType> const& targetStates, bool qualitative);
template<storm::dd::DdType DdType, typename ValueType, typename std::enable_if<storm::NumberTraits<ValueType>::SupportsExponential, int>::type = 0>
static std::unique_ptr<CheckResult> computeBoundedUntilProbabilities(Environment const& env, OptimizationDirection dir, storm::models::symbolic::MarkovAutomaton<DdType, ValueType> const& model, storm::dd::Add<DdType, ValueType> const& transitionMatrix, storm::dd::Bdd<DdType> const& markovianStates, storm::dd::Add<DdType, ValueType> const& exitRateVector, storm::dd::Bdd<DdType> const& phiStates, storm::dd::Bdd<DdType> const& psiStates, bool qualitative, double lowerBound, double upperBound);
template<storm::dd::DdType DdType, typename ValueType, typename std::enable_if<!storm::NumberTraits<ValueType>::SupportsExponential, int>::type = 0>
static std::unique_ptr<CheckResult> computeBoundedUntilProbabilities(Environment const& env, OptimizationDirection dir, storm::models::symbolic::MarkovAutomaton<DdType, ValueType> const& model, storm::dd::Add<DdType, ValueType> const& transitionMatrix, storm::dd::Bdd<DdType> const& markovianStates, storm::dd::Add<DdType, ValueType> const& exitRateVector, storm::dd::Bdd<DdType> const& phiStates, storm::dd::Bdd<DdType> const& psiStates, bool qualitative, double lowerBound, double upperBound);
template<storm::dd::DdType DdType, typename ValueType>
static std::unique_ptr<CheckResult> computeLongRunAverageProbabilities(Environment const& env, OptimizationDirection dir, storm::models::symbolic::MarkovAutomaton<DdType, ValueType> const& model, storm::dd::Add<DdType, ValueType> const& transitionMatrix, storm::dd::Bdd<DdType> const& markovianStates, storm::dd::Add<DdType, ValueType> const& exitRateVector, storm::dd::Bdd<DdType> const& psiStates);
template<storm::dd::DdType DdType, typename ValueType>
static std::unique_ptr<CheckResult> computeLongRunAverageRewards(Environment const& env, OptimizationDirection dir, storm::models::symbolic::MarkovAutomaton<DdType, ValueType> const& model, storm::dd::Add<DdType, ValueType> const& transitionMatrix, storm::dd::Bdd<DdType> const& markovianStates, storm::dd::Add<DdType, ValueType> const& exitRateVector, typename storm::models::symbolic::Model<DdType, ValueType>::RewardModelType const& rewardModel);
};
}
}
}

2
src/storm/modelchecker/csl/helper/SparseCtmcCslHelper.cpp

@ -576,7 +576,7 @@ namespace storm {
}
storm::solver::LraMethod method = env.solver().lra().getDetLraMethod();
if (storm::NumberTraits<ValueType>::IsExact && env.solver().lra().isDetLraMethodSetFromDefault() && method == storm::solver::LraMethod::ValueIteration) {
if ((storm::NumberTraits<ValueType>::IsExact || env.solver().isForceExact()) && env.solver().lra().isDetLraMethodSetFromDefault() && method == storm::solver::LraMethod::ValueIteration) {
method = storm::solver::LraMethod::GainBiasEquations;
STORM_LOG_INFO("Selecting " << storm::solver::toString(method) << " as the solution technique for long-run properties to guarantee exact results. If you want to override this, please explicitly specify a different LRA method.");
} else if (env.solver().isForceSoundness() && env.solver().lra().isDetLraMethodSetFromDefault() && method != storm::solver::LraMethod::ValueIteration) {

719
src/storm/modelchecker/csl/helper/SparseMarkovAutomatonCslHelper.cpp

@ -16,6 +16,7 @@
#include "storm/environment/solver/TopologicalSolverEnvironment.h"
#include "storm/environment/solver/LongRunAverageSolverEnvironment.h"
#include "storm/environment/solver/EigenSolverEnvironment.h"
#include "storm/environment/solver/TimeBoundedSolverEnvironment.h"
#include "storm/utility/macros.h"
#include "storm/utility/vector.h"
@ -27,6 +28,7 @@
#include "storm/storage/expressions/Expression.h"
#include "storm/storage/expressions/ExpressionManager.h"
#include "storm/solver/Multiplier.h"
#include "storm/solver/MinMaxLinearEquationSolver.h"
#include "storm/solver/LpSolver.h"
@ -38,398 +40,386 @@
namespace storm {
namespace modelchecker {
namespace helper {
/**
* Data structure holding result vectors (vLower, vUpper, wUpper) for Unif+.
*/
template<typename ValueType>
struct UnifPlusVectors {
UnifPlusVectors() {
// Intentionally empty
}
/**
* Initialize results vectors. vLowerOld, vUpperOld and wUpper[k=N] are initialized with zeros.
*/
UnifPlusVectors(uint64_t steps, uint64_t noStates) : numberOfStates(noStates), steps(steps), resLowerOld(numberOfStates, storm::utility::zero<ValueType>()), resLowerNew(numberOfStates, -1), resUpper(numberOfStates, storm::utility::zero<ValueType>()), wUpperOld(numberOfStates, storm::utility::zero<ValueType>()), wUpperNew(numberOfStates, -1) {
// Intentionally left empty
}
/**
* Prepare new iteration by setting the new result vectors as old result vectors, and initializing the new result vectors with -1 again.
*/
void prepareNewIteration() {
resLowerOld.swap(resLowerNew);
std::fill(resLowerNew.begin(), resLowerNew.end(), -1);
wUpperOld.swap(wUpperNew);
std::fill(wUpperNew.begin(), wUpperNew.end(), -1);
}
uint64_t numberOfStates;
uint64_t steps;
std::vector<ValueType> resLowerOld;
std::vector<ValueType> resLowerNew;
std::vector<ValueType> resUpper;
std::vector<ValueType> wUpperOld;
std::vector<ValueType> wUpperNew;
};
template<typename ValueType>
void calculateUnifPlusVector(Environment const& env, uint64_t k, uint64_t state, bool calcLower, ValueType lambda, uint64_t numberOfProbabilisticChoices, std::vector<std::vector<ValueType>> const & relativeReachability, OptimizationDirection dir, UnifPlusVectors<ValueType>& unifVectors, storm::storage::SparseMatrix<ValueType> const& fullTransitionMatrix, storm::storage::BitVector const& markovianStates, storm::storage::BitVector const& psiStates, std::unique_ptr<storm::solver::MinMaxLinearEquationSolver<ValueType>> const& solver, storm::utility::numerical::FoxGlynnResult<ValueType> const& poisson, bool cycleFree) {
// Set reference to acutal vector
std::vector<ValueType>& resVectorOld = calcLower ? unifVectors.resLowerOld : unifVectors.wUpperOld;
std::vector<ValueType>& resVectorNew = calcLower ? unifVectors.resLowerNew : unifVectors.wUpperNew;
if (resVectorNew[state] != -1) {
// Result already calculated.
return;
}
auto numberOfStates = fullTransitionMatrix.getRowGroupCount();
uint64_t N = unifVectors.steps;
auto const& rowGroupIndices = fullTransitionMatrix.getRowGroupIndices();
ValueType res;
// First case, k==N, independent from kind of state.
if (k == N) {
STORM_LOG_ASSERT(false, "Result for k=N was already calculated.");
resVectorNew[state] = storm::utility::zero<ValueType>();
return;
class UnifPlusHelper {
public:
UnifPlusHelper(storm::storage::SparseMatrix<ValueType> const& transitionMatrix, std::vector<ValueType> const& exitRateVector, storm::storage::BitVector const& markovianStates) : transitionMatrix(transitionMatrix), exitRateVector(exitRateVector), markovianStates(markovianStates) {
// Intentionally left empty
}
// Goal state, independent from kind of state.
if (psiStates[state]) {
if (calcLower) {
// v lower
res = storm::utility::zero<ValueType>();
for (uint64_t i = k; i < N; ++i){
if (i >= poisson.left && i <= poisson.right) {
res += poisson.weights[i - poisson.left];
}
}
resVectorNew[state] = res;
} else {
// w upper
resVectorNew[state] = storm::utility::one<ValueType>();
std::vector<ValueType> computeBoundedUntilProbabilities(storm::Environment const& env, OptimizationDirection dir, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates, ValueType const& upperTimeBound, boost::optional<storm::storage::BitVector> const& relevantStates = boost::none) {
// Since there is no lower time bound, we can treat the psiStates as if they are absorbing.
// Compute some important subsets of states
storm::storage::BitVector maybeStates = ~(getProb0States(dir, phiStates, psiStates) | psiStates);
storm::storage::BitVector markovianMaybeStates = markovianStates & maybeStates;
storm::storage::BitVector probabilisticMaybeStates = ~markovianStates & maybeStates;
storm::storage::BitVector markovianStatesModMaybeStates = markovianMaybeStates % maybeStates;
storm::storage::BitVector probabilisticStatesModMaybeStates = probabilisticMaybeStates % maybeStates;
boost::optional<storm::storage::BitVector> relevantMaybeStates;
if (relevantStates) {
relevantMaybeStates = relevantStates.get() % maybeStates;
}
return;
}
// Catch the case where this is query can be solved by solving the untimed variant instead.
// This is the case if there is no Markovian maybe state (e.g. if the initial state is already a psi state) of if the time bound is infinity.
if (markovianMaybeStates.empty() || storm::utility::isInfinity(upperTimeBound)) {
return SparseMarkovAutomatonCslHelper::computeUntilProbabilities<ValueType>(env, dir, transitionMatrix, transitionMatrix.transpose(true), phiStates, psiStates, false, false).values;
}
// Get the exit rates restricted to only markovian maybe states.
std::vector<ValueType> markovianExitRates = storm::utility::vector::filterVector(exitRateVector, markovianMaybeStates);
// Obtain parameters of the algorithm
// Truncation error
ValueType kappa = storm::utility::convertNumber<ValueType>(env.solver().timeBounded().getUnifPlusKappa());
// Precision to be achieved
ValueType epsilon = storm::utility::convertNumber<ValueType>(2.0) * storm::utility::convertNumber<ValueType>(env.solver().timeBounded().getPrecision());
bool relativePrecision = env.solver().timeBounded().getRelativeTerminationCriterion();
// Uniformization rate
ValueType lambda = *std::max_element(markovianExitRates.begin(), markovianExitRates.end());
STORM_LOG_DEBUG("Initial lambda is " << lambda << ".");
// Markovian non-goal state.
if (markovianStates[state]) {
res = storm::utility::zero<ValueType>();
for (auto const& element : fullTransitionMatrix.getRow(rowGroupIndices[state])) {
uint64_t successor = element.getColumn();
if (resVectorOld[successor] == -1) {
STORM_LOG_ASSERT(false, "Need to calculate previous result.");
calculateUnifPlusVector(env, k+1, successor, calcLower, lambda, numberOfProbabilisticChoices, relativeReachability, dir, unifVectors, fullTransitionMatrix, markovianStates, psiStates, solver, poisson, cycleFree);
}
res += element.getValue() * resVectorOld[successor];
// Split the transitions into various part
// The (uniformized) probabilities to go from a Markovian state to a psi state in one step
std::vector<std::pair<uint64_t, ValueType>> markovianToPsiProbabilities = getSparseOneStepProbabilities(markovianMaybeStates, psiStates);
for (auto& entry : markovianToPsiProbabilities) {
entry.second *= markovianExitRates[entry.first] / lambda;
}
resVectorNew[state]=res;
return;
}
// Probabilistic non-goal state.
if (cycleFree) {
// If the model is cycle free, do "slight value iteration". (What is that?)
res = -1;
for (uint64_t i = rowGroupIndices[state]; i < rowGroupIndices[state + 1]; ++i) {
auto row = fullTransitionMatrix.getRow(i);
ValueType between = storm::utility::zero<ValueType>();
for (auto const& element : row) {
uint64_t successor = element.getColumn();
// This should never happen, right? The model has no cycles, and therefore also no self-loops.
if (successor == state) {
continue;
}
if (resVectorNew[successor] == -1) {
calculateUnifPlusVector(env, k, successor, calcLower, lambda, numberOfProbabilisticChoices, relativeReachability, dir, unifVectors, fullTransitionMatrix, markovianStates, psiStates, solver, poisson, cycleFree);
// Uniformized transitions from Markovian maybe states to all other maybe states. Inserts selfloop entries.
storm::storage::SparseMatrix<ValueType> markovianToMaybeTransitions = getUniformizedMarkovianTransitions(markovianExitRates, lambda, maybeStates, markovianMaybeStates);
// Transitions from probabilistic maybe states to probabilistic maybe states.
storm::storage::SparseMatrix<ValueType> probabilisticToProbabilisticTransitions = transitionMatrix.getSubmatrix(true, probabilisticMaybeStates, probabilisticMaybeStates, false);
// Transitions from probabilistic maybe states to Markovian maybe states.
storm::storage::SparseMatrix<ValueType> probabilisticToMarkovianTransitions = transitionMatrix.getSubmatrix(true, probabilisticMaybeStates, markovianMaybeStates, false);
// The probabilities to go from a probabilistic state to a psi state in one step
std::vector<std::pair<uint64_t, ValueType>> probabilisticToPsiProbabilities = getSparseOneStepProbabilities(probabilisticMaybeStates, psiStates);
// Set up a solver for the transitions between probabilistic states (if there are some)
Environment solverEnv = env;
solverEnv.solver().setForceExact(true); // Errors within the inner iterations can propagate significantly
auto solver = setUpProbabilisticStatesSolver(solverEnv, dir, probabilisticToProbabilisticTransitions);
// Allocate auxiliary memory that can be used during the iterations
std::vector<ValueType> maybeStatesValuesLower(maybeStates.getNumberOfSetBits(), storm::utility::zero<ValueType>()); // should be zero initially
std::vector<ValueType> maybeStatesValuesWeightedUpper(maybeStates.getNumberOfSetBits(), storm::utility::zero<ValueType>()); // should be zero initially
std::vector<ValueType> maybeStatesValuesUpper(maybeStates.getNumberOfSetBits(), storm::utility::zero<ValueType>()); // should be zero initially
std::vector<ValueType> nextMarkovianStateValues = std::move(markovianExitRates); // At this point, the markovianExitRates are no longer needed, so we 'move' them away instead of allocating new memory
std::vector<ValueType> nextProbabilisticStateValues(probabilisticToProbabilisticTransitions.getRowGroupCount());
std::vector<ValueType> eqSysRhs(probabilisticToProbabilisticTransitions.getRowCount());
// Start the outer iterations which increase the uniformization rate until lower and upper bound on the result vector is sufficiently small
storm::utility::ProgressMeasurement progressIterations("iterations");
uint64_t iteration = 0;
progressIterations.startNewMeasurement(iteration);
bool converged = false;
while (!converged) {
// Maximal step size
uint64_t N = storm::utility::ceil(lambda * upperTimeBound * std::exp(2) - storm::utility::log(kappa * epsilon));
// Compute poisson distribution.
// The division by 8 is similar to what is done for CTMCs (probably to reduce numerical impacts?)
auto foxGlynnResult = storm::utility::numerical::foxGlynn(lambda * upperTimeBound, epsilon * kappa / storm::utility::convertNumber<ValueType>(8.0));
// Scale the weights so they sum to one.
//storm::utility::vector::scaleVectorInPlace(foxGlynnResult.weights, storm::utility::one<ValueType>() / foxGlynnResult.totalWeight);
// Set up multiplier
auto markovianToMaybeMultiplier = storm::solver::MultiplierFactory<ValueType>().create(env, markovianToMaybeTransitions);
auto probabilisticToMarkovianMultiplier = storm::solver::MultiplierFactory<ValueType>().create(env, probabilisticToMarkovianTransitions);
//Perform inner iterations first for upper, then for lower bound
STORM_LOG_ASSERT(!storm::utility::vector::hasNonZeroEntry(maybeStatesValuesUpper), "Current values need to be initialized with zero.");
for (bool computeLowerBound : {false, true}) {
auto& maybeStatesValues = computeLowerBound ? maybeStatesValuesLower : maybeStatesValuesWeightedUpper;
ValueType targetValue = computeLowerBound ? storm::utility::zero<ValueType>() : storm::utility::one<ValueType>();
storm::utility::ProgressMeasurement progressSteps("steps in iteration " + std::to_string(iteration) + " for " + std::string(computeLowerBound ? "lower" : "upper") + " bounds.");
progressSteps.setMaxCount(N);
progressSteps.startNewMeasurement(0);
bool firstIteration = true; // The first iterations can be irrelevant, because they will only produce zeroes anyway.
// Iteration k = N is always non-relevant
for (int64_t k = N - 1; k >= 0; --k) {
// Check whether the iteration is relevant, that is, whether it will contribute non-zero values to the overall result
if (computeLowerBound) {
// Check whether the value for visiting a target state will be zero.
if (static_cast<uint64_t>(k) > foxGlynnResult.right) {
// Reaching this point means that we are in one of the earlier iterations where fox glynn told us to cut off
continue;
}
} else {
uint64_t i = N-1-k;
if (i > foxGlynnResult.right) {
// Reaching this point means that we are in a later iteration which will not contribute to the upper bound
// Since i will only get larger in subsequent iterations, we can directly break here.
break;
}
}
// Compute the values at Markovian maybe states.
if (firstIteration) {
firstIteration = false;
// Reaching this point means that this is the very first relevant iteration.
// If we are in the very first relevant iteration, we know that all states from the previous iteration have value zero.
// It is therefore valid (and necessary) to just set the values of Markovian states to zero.
std::fill(nextMarkovianStateValues.begin(), nextMarkovianStateValues.end(), storm::utility::zero<ValueType>());
} else {
// Compute the values at Markovian maybe states.
markovianToMaybeMultiplier->multiply(env, maybeStatesValues, nullptr, nextMarkovianStateValues);
for (auto const& oneStepProb : markovianToPsiProbabilities) {
nextMarkovianStateValues[oneStepProb.first] += oneStepProb.second * targetValue;
}
}
// Update the value when reaching a psi state.
// This has to be done after updating the Markovian state values since we needed the 'old' target value above.
if (computeLowerBound && static_cast<uint64_t>(k) >= foxGlynnResult.left) {
assert(static_cast<uint64_t>(k) <= foxGlynnResult.right); // has to hold since this iteration is relevant
targetValue += foxGlynnResult.weights[k - foxGlynnResult.left];
}
// Compute the values at probabilistic states.
probabilisticToMarkovianMultiplier->multiply(env, nextMarkovianStateValues, nullptr, eqSysRhs);
for (auto const& oneStepProb : probabilisticToPsiProbabilities) {
eqSysRhs[oneStepProb.first] += oneStepProb.second * targetValue;
}
if (solver) {
solver->solveEquations(solverEnv, dir, nextProbabilisticStateValues, eqSysRhs);
} else {
storm::utility::vector::reduceVectorMinOrMax(dir, eqSysRhs, nextProbabilisticStateValues, probabilisticToProbabilisticTransitions.getRowGroupIndices());
}
// Create the new values for the maybestates
// Fuse the results together
storm::utility::vector::setVectorValues(maybeStatesValues, markovianStatesModMaybeStates, nextMarkovianStateValues);
storm::utility::vector::setVectorValues(maybeStatesValues, probabilisticStatesModMaybeStates, nextProbabilisticStateValues);
if (!computeLowerBound) {
// Add the scaled values to the actual result vector
uint64_t i = N-1-k;
if (i >= foxGlynnResult.left) {
assert(i <= foxGlynnResult.right); // has to hold since this iteration is considered relevant.
ValueType const& weight = foxGlynnResult.weights[i - foxGlynnResult.left];
storm::utility::vector::addScaledVector(maybeStatesValuesUpper, maybeStatesValuesWeightedUpper, weight);
}
}
progressSteps.updateProgress(N-k);
}
between += element.getValue() * resVectorNew[successor];
}
if (maximize(dir)) {
res = storm::utility::max(res, between);
} else {
if (res != -1) {
res = storm::utility::min(res, between);
if (computeLowerBound) {
storm::utility::vector::scaleVectorInPlace(maybeStatesValuesLower, storm::utility::one<ValueType>() / foxGlynnResult.totalWeight);
} else {
res = between;
storm::utility::vector::scaleVectorInPlace(maybeStatesValuesUpper, storm::utility::one<ValueType>() / foxGlynnResult.totalWeight);
}
}
}
resVectorNew[state] = res;
return;
}
// If we arrived at this point, the model is not cycle free. Use the solver to solve the underlying equation system.
uint64_t numberOfProbabilisticStates = numberOfStates - markovianStates.getNumberOfSetBits();
std::vector<ValueType> b(numberOfProbabilisticChoices, storm::utility::zero<ValueType>());
std::vector<ValueType> x(numberOfProbabilisticStates, storm::utility::zero<ValueType>());
// Compute right-hand side vector b.
uint64_t row = 0;
for (uint64_t i = 0; i < numberOfStates; ++i) {
if (markovianStates[i]) {
continue;
}
for (auto j = rowGroupIndices[i]; j < rowGroupIndices[i + 1]; j++) {
uint64_t stateCount = 0;
res = storm::utility::zero<ValueType>();
for (auto const& element : fullTransitionMatrix.getRow(j)) {
auto successor = element.getColumn();
if (!markovianStates[successor]) {
continue;
// Check if the lower and upper bound are sufficiently close to each other
converged = checkConvergence(maybeStatesValuesLower, maybeStatesValuesUpper, relevantMaybeStates, epsilon, relativePrecision, kappa);
if (converged) {
break;
}
}
if (!converged) {
// Increase the uniformization rate and prepare the next run
// Double lambda.
ValueType oldLambda = lambda;
lambda *= storm::utility::convertNumber<ValueType>(2.0);
STORM_LOG_DEBUG("Increased lambda to " << lambda << ".");
if (resVectorNew[successor] == -1) {
calculateUnifPlusVector(env, k, successor, calcLower, lambda, numberOfProbabilisticStates, relativeReachability, dir, unifVectors, fullTransitionMatrix, markovianStates, psiStates, solver, poisson, cycleFree);
if (relativePrecision) {
// Reduce kappa a bit
ValueType minValue = *std::min_element(maybeStatesValuesUpper.begin(), maybeStatesValuesUpper.end());
minValue *= storm::utility::convertNumber<ValueType>(env.solver().timeBounded().getUnifPlusKappa());
kappa = std::min(kappa, minValue);
STORM_LOG_DEBUG("Decreased kappa to " << kappa << ".");
}
res += relativeReachability[j][stateCount] * resVectorNew[successor];
++stateCount;
// Apply uniformization with new rate
uniformize(markovianToMaybeTransitions, markovianToPsiProbabilities, oldLambda, lambda, markovianStatesModMaybeStates);
// Reset the values of the maybe states to zero.
std::fill(maybeStatesValuesUpper.begin(), maybeStatesValuesUpper.end(), storm::utility::zero<ValueType>());
}
b[row] = res;
++row;
progressIterations.updateProgress(++iteration);
}
// We take the average of the lower and upper bounds
auto two = storm::utility::convertNumber<ValueType>(2.0);
storm::utility::vector::applyPointwise<ValueType, ValueType, ValueType>(maybeStatesValuesLower, maybeStatesValuesUpper, maybeStatesValuesLower, [&two] (ValueType const& a, ValueType const& b) -> ValueType { return (a + b) / two; });
std::vector<ValueType> result(transitionMatrix.getRowGroupCount(), storm::utility::zero<ValueType>());
storm::utility::vector::setVectorValues(result, psiStates, storm::utility::one<ValueType>());
storm::utility::vector::setVectorValues(result, maybeStates, maybeStatesValuesLower);
return result;
}
// Solve the equation system.
solver->solveEquations(env, dir, x, b);
// Expand the solution for the probabilistic states to all states.
storm::utility::vector::setVectorValues(resVectorNew, ~markovianStates, x);
}
template <typename ValueType>
void eliminateProbabilisticSelfLoops(storm::storage::SparseMatrix<ValueType>& transitionMatrix, storm::storage::BitVector const& markovianStates) {
auto const& rowGroupIndices = transitionMatrix.getRowGroupIndices();
for (uint64_t i = 0; i < transitionMatrix.getRowGroupCount(); ++i) {
if (markovianStates[i]) {
continue;
private:
bool checkConvergence(std::vector<ValueType> const& lower, std::vector<ValueType> const& upper, boost::optional<storm::storage::BitVector> const& relevantValues, ValueType const& epsilon, bool relative, ValueType& kappa) {
STORM_LOG_ASSERT(!relevantValues.is_initialized() || relevantValues->size() == lower.size(), "Relevant values size mismatch.");
if (!relative) {
if (relevantValues) {
return storm::utility::vector::equalModuloPrecision(lower, upper, relevantValues.get(), epsilon * (storm::utility::one<ValueType>() - kappa), false);
} else {
return storm::utility::vector::equalModuloPrecision(lower, upper, epsilon * (storm::utility::one<ValueType>() - kappa), false);
}
}
for (uint64_t j = rowGroupIndices[i]; j < rowGroupIndices[i + 1]; j++) {
ValueType selfLoop = storm::utility::zero<ValueType>();
for (auto const& element: transitionMatrix.getRow(j)){
if (element.getColumn() == i) {
selfLoop += element.getValue();
ValueType truncationError = epsilon * kappa;
for (uint64_t i = 0; i < lower.size(); ++i) {
if (relevantValues) {
i = relevantValues->getNextSetIndex(i);
if (i == lower.size()) {
break;
}
}
if (storm::utility::isZero(selfLoop)) {
if (lower[i] == upper[i]) {
continue;
}
for (auto& element : transitionMatrix.getRow(j)) {
if (element.getColumn() != i) {
if (!storm::utility::isOne(selfLoop)) {
element.setValue(element.getValue() / (storm::utility::one<ValueType>() - selfLoop));
}
} else {
element.setValue(storm::utility::zero<ValueType>());
}
if (lower[i] <= truncationError) {
return false;
}
ValueType absDiff = upper[i] - lower[i] + truncationError;
ValueType relDiff = absDiff / lower[i];
if (relDiff > epsilon) {
return false;
}
STORM_LOG_ASSERT(absDiff > storm::utility::zero<ValueType>(), "Upper bound " << upper[i] << " is smaller than lower bound " << lower[i] << ".");
}
return true;
}
}
template<typename ValueType>
std::vector<ValueType> computeBoundedUntilProbabilitiesUnifPlus(Environment const& env, OptimizationDirection dir, storm::storage::SparseMatrix<ValueType> const& transitionMatrix, std::vector<ValueType> const& exitRateVector, storm::storage::BitVector const& markovianStates, storm::storage::BitVector const& psiStates, std::pair<double, double> const& boundsPair) {
STORM_LOG_TRACE("Using UnifPlus to compute bounded until probabilities.");
// Obtain bit vectors to identify different kind of states.
storm::storage::BitVector allStates(markovianStates.size(), true);
storm::storage::BitVector probabilisticStates = ~markovianStates;
// Searching for SCCs in probabilistic fragment to decide which algorithm is applied.
bool cycleFree = !storm::utility::graph::hasCycle(transitionMatrix, probabilisticStates);
// Vectors to store computed vectors.
UnifPlusVectors<ValueType> unifVectors;
// Transitions from goal states will be ignored. However, we mark them as non-probabilistic to make sure
// we do not apply the MDP algorithm to them.
storm::storage::BitVector markovianAndGoalStates = markovianStates | psiStates;
probabilisticStates &= ~psiStates;
std::vector<ValueType> mutableExitRates = exitRateVector;
// Extend the transition matrix with diagonal entries so we can change them easily during the uniformization step.
typename storm::storage::SparseMatrix<ValueType> fullTransitionMatrix = transitionMatrix.getSubmatrix(true, allStates, allStates, true);
// Eliminate self-loops of probabilistic states. Is this really needed for the "slight value iteration" process?
eliminateProbabilisticSelfLoops(fullTransitionMatrix, markovianAndGoalStates);
typename storm::storage::SparseMatrix<ValueType> probMatrix;
uint64_t numberOfProbabilisticChoices = 0;
if (!probabilisticStates.empty()) {
probMatrix = fullTransitionMatrix.getSubmatrix(true, probabilisticStates, probabilisticStates, true);
numberOfProbabilisticChoices = probMatrix.getRowCount();
}
// Get row grouping of transition matrix.
auto const& rowGroupIndices = fullTransitionMatrix.getRowGroupIndices();
// (1) define/declare horizon, epsilon, kappa, N, lambda, maxNorm
uint64_t numberOfStates = fullTransitionMatrix.getRowGroupCount();
// 'Unpack' the bounds to make them more easily accessible.
double lowerBound = boundsPair.first;
double upperBound = boundsPair.second;
// Lower bound > 0 is not implemented!
STORM_LOG_THROW(lowerBound == 0, storm::exceptions::NotImplementedException, "Support for lower bound > 0 not implemented in Unif+.");
// Truncation error
// TODO: make kappa a parameter.
ValueType kappa = storm::utility::one<ValueType>() / 10;
// Approximation error
ValueType epsilon = storm::settings::getModule<storm::settings::modules::GeneralSettings>().getPrecision();
// Lambda is largest exit rate
ValueType lambda = exitRateVector[0];
for (ValueType const& rate : exitRateVector) {
lambda = std::max(rate, lambda);
}
STORM_LOG_DEBUG("Initial lambda is " << lambda << ".");
// Compute the relative reachability vectors and create solver for models with SCCs.
std::vector<std::vector<ValueType>> relativeReachabilities(transitionMatrix.getRowCount());
std::unique_ptr<storm::solver::MinMaxLinearEquationSolver<ValueType>> solver;
if (!cycleFree) {
for (uint64_t i = 0; i < numberOfStates; i++) {
if (markovianAndGoalStates[i]) {
continue;
}
storm::storage::SparseMatrix<ValueType> getUniformizedMarkovianTransitions(std::vector<ValueType> const& oldRates, ValueType uniformizationRate, storm::storage::BitVector const& maybeStates, storm::storage::BitVector const& markovianMaybeStates) {
// We need a submatrix whose rows correspond to the markovian states and columns correpsond to the maybestates.
// In addition, we need 'selfloop' entries for the markovian maybe states.
// First build a submatrix without selfloop entries
auto submatrix = transitionMatrix.getSubmatrix(true, markovianMaybeStates, maybeStates);
assert(submatrix.getRowCount() == submatrix.getRowGroupCount());
for (auto j = rowGroupIndices[i]; j < rowGroupIndices[i + 1]; ++j) {
for (auto const& element : fullTransitionMatrix.getRow(j)) {
if (markovianAndGoalStates[element.getColumn()]) {
relativeReachabilities[j].push_back(element.getValue());
}
// Now add selfloop entries at the correct positions and apply uniformization
storm::storage::SparseMatrixBuilder<ValueType> builder(submatrix.getRowCount(), submatrix.getColumnCount());
auto markovianStateColumns = markovianMaybeStates % maybeStates;
uint64_t row = 0;
for (auto const& selfloopColumn : markovianStateColumns) {
ValueType const& oldExitRate = oldRates[row];
bool foundSelfoop = false;
for (auto const& entry : submatrix.getRow(row)) {
if (entry.getColumn() == selfloopColumn) {
foundSelfoop = true;
ValueType newSelfLoop = uniformizationRate - oldExitRate + entry.getValue() * oldExitRate;
builder.addNextValue(row, entry.getColumn(), newSelfLoop / uniformizationRate);
} else {
builder.addNextValue(row, entry.getColumn(), entry.getValue() * oldExitRate / uniformizationRate);
}
}
if (!foundSelfoop) {
ValueType newSelfLoop = uniformizationRate - oldExitRate;
builder.addNextValue(row, selfloopColumn, newSelfLoop / uniformizationRate);
}
++row;
}
// Create solver.
storm::solver::GeneralMinMaxLinearEquationSolverFactory<ValueType> minMaxLinearEquationSolverFactory;
storm::solver::MinMaxLinearEquationSolverRequirements requirements = minMaxLinearEquationSolverFactory.getRequirements(env, true, true, dir);
requirements.clearBounds();
STORM_LOG_THROW(!requirements.hasEnabledCriticalRequirement(), storm::exceptions::UncheckedRequirementException, "Solver requirements " + requirements.getEnabledRequirementsAsString() + " not checked.");
if (numberOfProbabilisticChoices > 0) {
solver = minMaxLinearEquationSolverFactory.create(env, probMatrix);
solver->setHasUniqueSolution();
solver->setHasNoEndComponents();
solver->setBounds(storm::utility::zero<ValueType>(), storm::utility::one<ValueType>());
solver->setRequirementsChecked();
solver->setCachingEnabled(true);
}
assert(row == submatrix.getRowCount());
return builder.build();
}
ValueType maxNorm = storm::utility::zero<ValueType>();
// Maximal step size
uint64_t N;
storm::utility::ProgressMeasurement progressIterations("iterations");
size_t iteration = 0;
progressIterations.startNewMeasurement(iteration);
// Loop until result is within precision bound.
do {
// (2) update parameter
N = storm::utility::ceil(lambda * upperBound * std::exp(2) - storm::utility::log(kappa * epsilon));
// (3) uniform - just applied to Markovian states.
for (uint64_t i = 0; i < numberOfStates; i++) {
if (!markovianAndGoalStates[i] || psiStates[i]) {
continue;
}
// As the current state is Markovian, its branching probabilities are stored within one row.
uint64_t markovianRowIndex = rowGroupIndices[i];
if (mutableExitRates[i] == lambda) {
void uniformize(storm::storage::SparseMatrix<ValueType>& matrix, std::vector<std::pair<uint64_t, ValueType>>& oneSteps, std::vector<ValueType> const& oldRates, ValueType uniformizationRate, storm::storage::BitVector const& selfloopColumns) {
uint64_t row = 0;
for (auto const& selfloopColumn : selfloopColumns) {
ValueType const& oldExitRate = oldRates[row];
if (oldExitRate == uniformizationRate) {
// Already uniformized.
++row;
continue;
}
auto markovianRow = fullTransitionMatrix.getRow(markovianRowIndex);
ValueType oldExitRate = mutableExitRates[i];
ValueType newExitRate = lambda;
for (auto& v : markovianRow) {
if (v.getColumn() == i) {
ValueType newSelfLoop = newExitRate - oldExitRate + v.getValue() * oldExitRate;
ValueType newRate = newSelfLoop / newExitRate;
v.setValue(newRate);
for (auto& v : matrix.getRow(row)) {
if (v.getColumn() == selfloopColumn) {
ValueType newSelfLoop = uniformizationRate - oldExitRate + v.getValue() * oldExitRate;
v.setValue(newSelfLoop / uniformizationRate);
} else {
ValueType oldProbability = v.getValue();
ValueType newProbability = oldProbability * oldExitRate / newExitRate;
v.setValue(newProbability);
v.setValue(v.getValue() * oldExitRate / uniformizationRate);
}
}
mutableExitRates[i] = newExitRate;
++row;
}
// Compute poisson distribution.
storm::utility::numerical::FoxGlynnResult<ValueType> foxGlynnResult = storm::utility::numerical::foxGlynn(lambda * upperBound, epsilon * kappa / 100);
// Scale the weights so they sum to one.
for (auto& element : foxGlynnResult.weights) {
element /= foxGlynnResult.totalWeight;
assert(row == matrix.getRowCount());
for (auto& oneStep : oneSteps) {
oneStep.second *= oldRates[oneStep.first] / uniformizationRate;
}
// (4) Define vectors/matrices.
// Initialize result vectors and already insert zeros for iteration N
unifVectors = UnifPlusVectors<ValueType>(N, numberOfStates);
// (5) Compute vectors and maxNorm.
// Iteration k = N was already performed by initializing with zeros.
// Iterations k < N
storm::utility::ProgressMeasurement progressSteps("steps in iteration " + std::to_string(iteration));
progressSteps.setMaxCount(N);
progressSteps.startNewMeasurement(0);
for (int64_t k = N-1; k >= 0; --k) {
if (k < (int64_t)(N-1)) {
unifVectors.prepareNewIteration();
}
for (uint64_t state = 0; state < numberOfStates; ++state) {
// Calculate results for lower bound and wUpper
calculateUnifPlusVector(env, k, state, true, lambda, numberOfProbabilisticChoices, relativeReachabilities, dir, unifVectors, fullTransitionMatrix, markovianAndGoalStates, psiStates, solver, foxGlynnResult, cycleFree);
calculateUnifPlusVector(env, k, state, false, lambda, numberOfProbabilisticChoices, relativeReachabilities, dir, unifVectors, fullTransitionMatrix, markovianAndGoalStates, psiStates, solver, foxGlynnResult, cycleFree);
// Calculate result for upper bound
uint64_t index = N-1-k;
if (index >= foxGlynnResult.left && index <= foxGlynnResult.right) {
STORM_LOG_ASSERT(unifVectors.wUpperNew[state] != -1, "wUpper was not computed before.");
unifVectors.resUpper[state] += foxGlynnResult.weights[index - foxGlynnResult.left] * unifVectors.wUpperNew[state];
}
/// Uniformizes the given matrix assuming that it is already uniform. The selfloopColumns indicate for each row, the column indices that correspond to the 'selfloops' for that row
void uniformize(storm::storage::SparseMatrix<ValueType>& matrix, std::vector<std::pair<uint64_t, ValueType>>& oneSteps, ValueType oldUniformizationRate, ValueType newUniformizationRate, storm::storage::BitVector const& selfloopColumns) {
if (oldUniformizationRate != newUniformizationRate) {
assert(oldUniformizationRate < newUniformizationRate);
ValueType rateDiff = newUniformizationRate - oldUniformizationRate;
ValueType rateFraction = oldUniformizationRate / newUniformizationRate;
uint64_t row = 0;
for (auto const& selfloopColumn : selfloopColumns) {
for (auto& v : matrix.getRow(row)) {
if (v.getColumn() == selfloopColumn) {
ValueType newSelfLoop = rateDiff + v.getValue() * oldUniformizationRate;
v.setValue(newSelfLoop / newUniformizationRate);
} else {
v.setValue(v.getValue() * rateFraction);
}
}
++row;
}
if (storm::utility::resources::isTerminate()) {
break;
assert(row == matrix.getRowCount());
for (auto& oneStep : oneSteps) {
oneStep.second *= rateFraction;
}
progressSteps.updateProgress(N-k);
}
// Only iterate over result vector, as the results can only get more precise.
maxNorm = storm::utility::zero<ValueType>();
for (uint64_t i = 0; i < numberOfStates; i++){
ValueType diff = storm::utility::abs(unifVectors.resUpper[i] - unifVectors.resLowerNew[i]);
maxNorm = std::max(maxNorm, diff);
}
std::unique_ptr<storm::solver::MinMaxLinearEquationSolver<ValueType>> setUpProbabilisticStatesSolver(storm::Environment const& env, OptimizationDirection dir, storm::storage::SparseMatrix<ValueType> const& transitions) const {
std::unique_ptr<storm::solver::MinMaxLinearEquationSolver<ValueType>> solver;
if (transitions.getNonzeroEntryCount() > 0) {
storm::solver::GeneralMinMaxLinearEquationSolverFactory<ValueType> factory;
solver = factory.create(env, transitions);
solver->setHasUniqueSolution(true); // Assume non-zeno MA
solver->setHasNoEndComponents(true); // assume non-zeno MA
solver->setLowerBound(storm::utility::zero<ValueType>());
solver->setUpperBound(storm::utility::one<ValueType>());
solver->setCachingEnabled(true);
solver->setRequirementsChecked(true);
auto req = solver->getRequirements(env, dir);
req.clearBounds();
req.clearUniqueSolution();
STORM_LOG_THROW(!req.hasEnabledCriticalRequirement(), storm::exceptions::UncheckedRequirementException, "The solver requirement " << req.getEnabledRequirementsAsString() << " has not been checked.");
}
// (6) Double lambda.
lambda *= 2;
STORM_LOG_DEBUG("Increased lambda to " << lambda << ", max diff is " << maxNorm << ".");
if (storm::utility::resources::isTerminate()) {
break;
return solver;
}
storm::storage::BitVector getProb0States(OptimizationDirection dir, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates) const {
if (dir == storm::solver::OptimizationDirection::Maximize) {
return storm::utility::graph::performProb0A(transitionMatrix.transpose(true), phiStates, psiStates);
} else {
return storm::utility::graph::performProb0E(transitionMatrix, transitionMatrix.getRowGroupIndices(), transitionMatrix.transpose(true), phiStates, psiStates);
}
progressIterations.updateProgress(++iteration);
} while (maxNorm > epsilon * (1 - kappa));
return unifVectors.resLowerNew;
}
}
/*!
* Returns a vector with pairs of state indices and non-zero probabilities to move from the corresponding state to a target state.
* The state indices are with respect to the number of states satisfying the sourceStateConstraint, i.e. the indices are in the range [0, sourceStateConstraint.getNumberOfSetBits())
*/
std::vector<std::pair<uint64_t, ValueType>> getSparseOneStepProbabilities(storm::storage::BitVector const& sourceStateConstraint, storm::storage::BitVector const& targetStateConstraint) const {
auto denseResult = transitionMatrix.getConstrainedRowGroupSumVector(sourceStateConstraint, targetStateConstraint);
std::vector<std::pair<uint64_t, ValueType>> sparseResult;
for (uint64 i = 0; i < denseResult.size(); ++i) {
auto const& val = denseResult[i];
if (!storm::utility::isZero(val)) {
sparseResult.emplace_back(i, val);
}
}
return sparseResult;
}
storm::storage::SparseMatrix<ValueType> const& transitionMatrix;
std::vector<ValueType> const& exitRateVector;
storm::storage::BitVector const& markovianStates;
};
template <typename ValueType>
void computeBoundedReachabilityProbabilitiesImca(Environment const& env, OptimizationDirection dir, storm::storage::SparseMatrix<ValueType> const& transitionMatrix, std::vector<ValueType> const& exitRates, storm::storage::BitVector const& goalStates, storm::storage::BitVector const& markovianNonGoalStates, storm::storage::BitVector const& probabilisticNonGoalStates, std::vector<ValueType>& markovianNonGoalValues, std::vector<ValueType>& probabilisticNonGoalValues, ValueType delta, uint64_t numberOfSteps) {
@ -564,7 +554,7 @@ namespace storm {
for (auto value : exitRateVector) {
maxExitRate = std::max(maxExitRate, value);
}
ValueType delta = (2 * storm::settings::getModule<storm::settings::modules::GeneralSettings>().getPrecision()) / (upperBound * maxExitRate * maxExitRate);
ValueType delta = (2.0 * storm::utility::convertNumber<ValueType>(env.solver().timeBounded().getPrecision())) / (upperBound * maxExitRate * maxExitRate);
// (2) Compute the number of steps we need to make for the interval.
uint64_t numberOfSteps = static_cast<uint64_t>(std::ceil((upperBound - lowerBound) / delta));
@ -615,23 +605,36 @@ namespace storm {
}
template <typename ValueType, typename std::enable_if<storm::NumberTraits<ValueType>::SupportsExponential, int>::type>
std::vector<ValueType> SparseMarkovAutomatonCslHelper::computeBoundedUntilProbabilities(Environment const& env, OptimizationDirection dir, storm::storage::SparseMatrix<ValueType> const& transitionMatrix, std::vector<ValueType> const& exitRateVector, storm::storage::BitVector const& markovianStates, storm::storage::BitVector const& psiStates, std::pair<double, double> const& boundsPair) {
auto const& settings = storm::settings::getModule<storm::settings::modules::MinMaxEquationSolverSettings>();
if (settings.getMarkovAutomatonBoundedReachabilityMethod() == storm::settings::modules::MinMaxEquationSolverSettings::MarkovAutomatonBoundedReachabilityMethod::Imca) {
return computeBoundedUntilProbabilitiesImca(env, dir, transitionMatrix, exitRateVector, markovianStates, psiStates, boundsPair);
std::vector<ValueType> SparseMarkovAutomatonCslHelper::computeBoundedUntilProbabilities(Environment const& env, storm::solver::SolveGoal<ValueType>&& goal, storm::storage::SparseMatrix<ValueType> const& transitionMatrix, std::vector<ValueType> const& exitRateVector, storm::storage::BitVector const& markovianStates, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates, std::pair<double, double> const& boundsPair) {
// Choose the applicable method
auto method = env.solver().timeBounded().getMaMethod();
if (method == storm::solver::MaBoundedReachabilityMethod::Imca) {
if (!phiStates.full()) {
STORM_LOG_WARN("Using Unif+ method because IMCA method does not support (phi Until psi) for non-trivial phi");
method = storm::solver::MaBoundedReachabilityMethod::UnifPlus;
}
} else {
STORM_LOG_ASSERT(settings.getMarkovAutomatonBoundedReachabilityMethod() == storm::settings::modules::MinMaxEquationSolverSettings::MarkovAutomatonBoundedReachabilityMethod::UnifPlus, "Unknown solution method.");
STORM_LOG_ASSERT(method == storm::solver::MaBoundedReachabilityMethod::UnifPlus, "Unknown solution method.");
if (!storm::utility::isZero(boundsPair.first)) {
STORM_LOG_WARN("Using IMCA method because Unif+ does not support a lower bound > 0.");
return computeBoundedUntilProbabilitiesImca(env, dir, transitionMatrix, exitRateVector, markovianStates, psiStates, boundsPair);
} else {
return computeBoundedUntilProbabilitiesUnifPlus(env, dir, transitionMatrix, exitRateVector, markovianStates, psiStates, boundsPair);
method = storm::solver::MaBoundedReachabilityMethod::Imca;
}
}
if (method == storm::solver::MaBoundedReachabilityMethod::Imca) {
return computeBoundedUntilProbabilitiesImca(env, goal.direction(), transitionMatrix, exitRateVector, markovianStates, psiStates, boundsPair);
} else {
UnifPlusHelper<ValueType> helper(transitionMatrix, exitRateVector, markovianStates);
boost::optional<storm::storage::BitVector> relevantValues;
if (goal.hasRelevantValues()) {
relevantValues = std::move(goal.relevantValues());
}
return helper.computeBoundedUntilProbabilities(env, goal.direction(), phiStates, psiStates, boundsPair.second, relevantValues);
}
}
template <typename ValueType, typename std::enable_if<!storm::NumberTraits<ValueType>::SupportsExponential, int>::type>
std::vector<ValueType> SparseMarkovAutomatonCslHelper::computeBoundedUntilProbabilities(Environment const& env, OptimizationDirection dir, storm::storage::SparseMatrix<ValueType> const& transitionMatrix, std::vector<ValueType> const& exitRateVector, storm::storage::BitVector const& markovianStates, storm::storage::BitVector const& psiStates, std::pair<double, double> const& boundsPair) {
std::vector<ValueType> SparseMarkovAutomatonCslHelper::computeBoundedUntilProbabilities(Environment const& env, storm::solver::SolveGoal<ValueType>&& goal, storm::storage::SparseMatrix<ValueType> const& transitionMatrix, std::vector<ValueType> const& exitRateVector, storm::storage::BitVector const& markovianStates, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates, std::pair<double, double> const& boundsPair) {
STORM_LOG_THROW(false, storm::exceptions::InvalidOperationException, "Computing bounded until probabilities is unsupported for this value type.");
}
@ -891,7 +894,7 @@ namespace storm {
// Solve MEC with the method specified in the settings
storm::solver::LraMethod method = env.solver().lra().getNondetLraMethod();
if (storm::NumberTraits<ValueType>::IsExact && env.solver().lra().isNondetLraMethodSetFromDefault() && method != storm::solver::LraMethod::LinearProgramming) {
if ((storm::NumberTraits<ValueType>::IsExact || env.solver().isForceExact()) && env.solver().lra().isNondetLraMethodSetFromDefault() && method != storm::solver::LraMethod::LinearProgramming) {
STORM_LOG_INFO("Selecting 'LP' as the solution technique for long-run properties to guarantee exact results. If you want to override this, please explicitly specify a different LRA method.");
method = storm::solver::LraMethod::LinearProgramming;
} else if (env.solver().isForceSoundness() && env.solver().lra().isNondetLraMethodSetFromDefault() && method != storm::solver::LraMethod::ValueIteration) {
@ -1144,7 +1147,7 @@ namespace storm {
return v.front() * uniformizationRate;
}
template std::vector<double> SparseMarkovAutomatonCslHelper::computeBoundedUntilProbabilities(Environment const& env, OptimizationDirection dir, storm::storage::SparseMatrix<double> const& transitionMatrix, std::vector<double> const& exitRateVector, storm::storage::BitVector const& markovianStates, storm::storage::BitVector const& psiStates, std::pair<double, double> const& boundsPair);
template std::vector<double> SparseMarkovAutomatonCslHelper::computeBoundedUntilProbabilities(Environment const& env, storm::solver::SolveGoal<double>&& goal, storm::storage::SparseMatrix<double> const& transitionMatrix, std::vector<double> const& exitRateVector, storm::storage::BitVector const& markovianStates, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates, std::pair<double, double> const& boundsPair);
template MDPSparseModelCheckingHelperReturnType<double> SparseMarkovAutomatonCslHelper::computeUntilProbabilities(Environment const& env, OptimizationDirection dir, storm::storage::SparseMatrix<double> const& transitionMatrix, storm::storage::SparseMatrix<double> const& backwardTransitions, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates, bool qualitative, bool produceScheduler);
@ -1164,7 +1167,7 @@ namespace storm {
template double SparseMarkovAutomatonCslHelper::computeLraForMaximalEndComponentVI(Environment const& env, OptimizationDirection dir, storm::storage::SparseMatrix<double> const& transitionMatrix, std::vector<double> const& exitRateVector, storm::storage::BitVector const& markovianStates, storm::models::sparse::StandardRewardModel<double> const& rewardModel, storm::storage::MaximalEndComponent const& mec);
template std::vector<storm::RationalNumber> SparseMarkovAutomatonCslHelper::computeBoundedUntilProbabilities(Environment const& env, OptimizationDirection dir, storm::storage::SparseMatrix<storm::RationalNumber> const& transitionMatrix, std::vector<storm::RationalNumber> const& exitRateVector, storm::storage::BitVector const& markovianStates, storm::storage::BitVector const& psiStates, std::pair<double, double> const& boundsPair);
template std::vector<storm::RationalNumber> SparseMarkovAutomatonCslHelper::computeBoundedUntilProbabilities(Environment const& env, storm::solver::SolveGoal<storm::RationalNumber>&& goal, storm::storage::SparseMatrix<storm::RationalNumber> const& transitionMatrix, std::vector<storm::RationalNumber> const& exitRateVector, storm::storage::BitVector const& markovianStates, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates, std::pair<double, double> const& boundsPair);
template MDPSparseModelCheckingHelperReturnType<storm::RationalNumber> SparseMarkovAutomatonCslHelper::computeUntilProbabilities(Environment const& env, OptimizationDirection dir, storm::storage::SparseMatrix<storm::RationalNumber> const& transitionMatrix, storm::storage::SparseMatrix<storm::RationalNumber> const& backwardTransitions, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates, bool qualitative, bool produceScheduler);

5
src/storm/modelchecker/csl/helper/SparseMarkovAutomatonCslHelper.h

@ -6,6 +6,7 @@
#include "storm/storage/MaximalEndComponent.h"
#include "storm/solver/OptimizationDirection.h"
#include "storm/solver/MinMaxLinearEquationSolver.h"
#include "storm/solver/SolveGoal.h"
#include "storm/utility/NumberTraits.h"
namespace storm {
@ -19,10 +20,10 @@ namespace storm {
public:
template <typename ValueType, typename std::enable_if<storm::NumberTraits<ValueType>::SupportsExponential, int>::type = 0>
static std::vector<ValueType> computeBoundedUntilProbabilities(Environment const& env, OptimizationDirection dir, storm::storage::SparseMatrix<ValueType> const& transitionMatrix, std::vector<ValueType> const& exitRateVector, storm::storage::BitVector const& markovianStates, storm::storage::BitVector const& psiStates, std::pair<double, double> const& boundsPair);
static std::vector<ValueType> computeBoundedUntilProbabilities(Environment const& env, storm::solver::SolveGoal<ValueType>&& goal, storm::storage::SparseMatrix<ValueType> const& transitionMatrix, std::vector<ValueType> const& exitRateVector, storm::storage::BitVector const& markovianStates, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates, std::pair<double, double> const& boundsPair);
template <typename ValueType, typename std::enable_if<!storm::NumberTraits<ValueType>::SupportsExponential, int>::type = 0>
static std::vector<ValueType> computeBoundedUntilProbabilities(Environment const& env, OptimizationDirection dir, storm::storage::SparseMatrix<ValueType> const& transitionMatrix, std::vector<ValueType> const& exitRateVector, storm::storage::BitVector const& markovianStates, storm::storage::BitVector const& psiStates, std::pair<double, double> const& boundsPair);
static std::vector<ValueType> computeBoundedUntilProbabilities(Environment const& env, storm::solver::SolveGoal<ValueType>&& goal, storm::storage::SparseMatrix<ValueType> const& transitionMatrix, std::vector<ValueType> const& exitRateVector, storm::storage::BitVector const& markovianStates, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates, std::pair<double, double> const& boundsPair);
template <typename ValueType>
static MDPSparseModelCheckingHelperReturnType<ValueType> computeUntilProbabilities(Environment const& env, OptimizationDirection dir, storm::storage::SparseMatrix<ValueType> const& transitionMatrix, storm::storage::SparseMatrix<ValueType> const& backwardTransitions, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates, bool qualitative, bool produceScheduler);

9
src/storm/modelchecker/prctl/HybridDtmcPrctlModelChecker.cpp

@ -32,11 +32,16 @@ namespace storm {
}
template<typename ModelType>
bool HybridDtmcPrctlModelChecker<ModelType>::canHandle(CheckTask<storm::logic::Formula, ValueType> const& checkTask) const {
bool HybridDtmcPrctlModelChecker<ModelType>::canHandleStatic(CheckTask<storm::logic::Formula, ValueType> const& checkTask) {
storm::logic::Formula const& formula = checkTask.getFormula();
return formula.isInFragment(storm::logic::prctl().setLongRunAverageRewardFormulasAllowed(true).setLongRunAverageProbabilitiesAllowed(true).setTimeOperatorsAllowed(true).setReachbilityTimeFormulasAllowed(true).setRewardAccumulationAllowed(true));
}
template<typename ModelType>
bool HybridDtmcPrctlModelChecker<ModelType>::canHandle(CheckTask<storm::logic::Formula, ValueType> const& checkTask) const {
return canHandleStatic(checkTask);
}
template<typename ModelType>
std::unique_ptr<CheckResult> HybridDtmcPrctlModelChecker<ModelType>::computeUntilProbabilities(Environment const& env, CheckTask<storm::logic::UntilFormula, ValueType> const& checkTask) {
storm::logic::UntilFormula const& pathFormula = checkTask.getFormula();

5
src/storm/modelchecker/prctl/HybridDtmcPrctlModelChecker.h

@ -19,6 +19,11 @@ namespace storm {
explicit HybridDtmcPrctlModelChecker(ModelType const& model);
/*!
* Returns false, if this task can certainly not be handled by this model checker (independent of the concrete model).
*/
static bool canHandleStatic(CheckTask<storm::logic::Formula, ValueType> const& checkTask);
// The implemented methods of the AbstractModelChecker interface.
virtual bool canHandle(CheckTask<storm::logic::Formula, ValueType> const& checkTask) const override;
virtual std::unique_ptr<CheckResult> computeBoundedUntilProbabilities(Environment const& env, CheckTask<storm::logic::BoundedUntilFormula, ValueType> const& checkTask) override;

24
src/storm/modelchecker/prctl/HybridMdpPrctlModelChecker.cpp

@ -38,20 +38,28 @@ namespace storm {
}
template<typename ModelType>
bool HybridMdpPrctlModelChecker<ModelType>::canHandle(CheckTask<storm::logic::Formula, ValueType> const& checkTask) const {
bool HybridMdpPrctlModelChecker<ModelType>::canHandleStatic(CheckTask<storm::logic::Formula, ValueType> const& checkTask, bool* requiresSingleInitialState) {
storm::logic::Formula const& formula = checkTask.getFormula();
if (formula.isInFragment(storm::logic::prctl().setLongRunAverageRewardFormulasAllowed(false).setTimeOperatorsAllowed(true).setReachbilityTimeFormulasAllowed(true).setRewardAccumulationAllowed(true))) {
return true;
} else if (checkTask.isOnlyInitialStatesRelevantSet() && formula.isInFragment(storm::logic::multiObjective().setCumulativeRewardFormulasAllowed(true))) {
if (requiresSingleInitialState) {
*requiresSingleInitialState = true;
}
}
return false;
}
template<typename ModelType>
bool HybridMdpPrctlModelChecker<ModelType>::canHandle(CheckTask<storm::logic::Formula, ValueType> const& checkTask) const {
bool requiresSingleInitialState = false;
if (canHandleStatic(checkTask, &requiresSingleInitialState)) {
return !requiresSingleInitialState || this->getModel().getInitialStates().getNonZeroCount() == 1;
} else {
// Check whether we consider a multi-objective formula
// For multi-objective model checking, each state requires an individual scheduler (in contrast to single-objective model checking). Let's exclude that multiple states are relevant
if(this->getModel().getInitialStates().getNonZeroCount() > 1) return false;
if(!checkTask.isOnlyInitialStatesRelevantSet()) return false;
return formula.isInFragment(storm::logic::multiObjective().setCumulativeRewardFormulasAllowed(true));
return false;
}
}
template<typename ModelType>
std::unique_ptr<CheckResult> HybridMdpPrctlModelChecker<ModelType>::computeUntilProbabilities(Environment const& env, CheckTask<storm::logic::UntilFormula, ValueType> const& checkTask) {
storm::logic::UntilFormula const& pathFormula = checkTask.getFormula();

6
src/storm/modelchecker/prctl/HybridMdpPrctlModelChecker.h

@ -26,6 +26,12 @@ namespace storm {
explicit HybridMdpPrctlModelChecker(ModelType const& model);
/*!
* Returns false, if this task can certainly not be handled by this model checker (independent of the concrete model).
* @param requiresSingleInitialState if not nullptr, this flag is set to true iff checking this formula requires a model with a single initial state
*/
static bool canHandleStatic(CheckTask<storm::logic::Formula, ValueType> const& checkTask, bool* requiresSingleInitialState = nullptr);
// The implemented methods of the AbstractModelChecker interface.
virtual bool canHandle(CheckTask<storm::logic::Formula, ValueType> const& checkTask) const override;
virtual std::unique_ptr<CheckResult> computeBoundedUntilProbabilities(Environment const& env, CheckTask<storm::logic::BoundedUntilFormula, ValueType> const& checkTask) override;

18
src/storm/modelchecker/prctl/SparseDtmcPrctlModelChecker.cpp

@ -32,17 +32,29 @@ namespace storm {
}
template<typename SparseDtmcModelType>
bool SparseDtmcPrctlModelChecker<SparseDtmcModelType>::canHandle(CheckTask<storm::logic::Formula, ValueType> const& checkTask) const {
bool SparseDtmcPrctlModelChecker<SparseDtmcModelType>::canHandleStatic(CheckTask<storm::logic::Formula, ValueType> const& checkTask, bool* requiresSingleInitialState) {
storm::logic::Formula const& formula = checkTask.getFormula();
if (formula.isInFragment(storm::logic::prctl().setLongRunAverageRewardFormulasAllowed(true).setLongRunAverageProbabilitiesAllowed(true).setConditionalProbabilityFormulasAllowed(true).setConditionalRewardFormulasAllowed(true).setTotalRewardFormulasAllowed(true).setOnlyEventuallyFormuluasInConditionalFormulasAllowed(true).setRewardBoundedUntilFormulasAllowed(true).setRewardBoundedCumulativeRewardFormulasAllowed(true).setMultiDimensionalBoundedUntilFormulasAllowed(true).setMultiDimensionalCumulativeRewardFormulasAllowed(true).setTimeOperatorsAllowed(true).setReachbilityTimeFormulasAllowed(true).setRewardAccumulationAllowed(true))) {
return true;
} else if (formula.isInFragment(storm::logic::quantiles())) {
if (this->getModel().getInitialStates().getNumberOfSetBits() > 1) return false;
} else if (checkTask.isOnlyInitialStatesRelevantSet() && formula.isInFragment(storm::logic::quantiles())) {
if (requiresSingleInitialState) {
*requiresSingleInitialState = true;
}
return true;
}
return false;
}
template<typename SparseDtmcModelType>
bool SparseDtmcPrctlModelChecker<SparseDtmcModelType>::canHandle(CheckTask<storm::logic::Formula, ValueType> const& checkTask) const {
bool requiresSingleInitialState = false;
if (canHandleStatic(checkTask, &requiresSingleInitialState)) {
return !requiresSingleInitialState || this->getModel().getInitialStates().getNumberOfSetBits() == 1;
} else {
return false;
}
}
template<typename SparseDtmcModelType>
std::unique_ptr<CheckResult> SparseDtmcPrctlModelChecker<SparseDtmcModelType>::computeBoundedUntilProbabilities(Environment const& env, CheckTask<storm::logic::BoundedUntilFormula, ValueType> const& checkTask) {
storm::logic::BoundedUntilFormula const& pathFormula = checkTask.getFormula();

6
src/storm/modelchecker/prctl/SparseDtmcPrctlModelChecker.h

@ -18,6 +18,12 @@ namespace storm {
explicit SparseDtmcPrctlModelChecker(SparseDtmcModelType const& model);
/*!
* Returns false, if this task can certainly not be handled by this model checker (independent of the concrete model).
* @param requiresSingleInitialState if not nullptr, this flag is set to true iff checking this formula requires a model with a single initial state
*/
static bool canHandleStatic(CheckTask<storm::logic::Formula, ValueType> const& checkTask, bool* requiresSingleInitialState = nullptr);
// The implemented methods of the AbstractModelChecker interface.
virtual bool canHandle(CheckTask<storm::logic::Formula, ValueType> const& checkTask) const override;
virtual std::unique_ptr<CheckResult> computeBoundedUntilProbabilities(Environment const& env, CheckTask<storm::logic::BoundedUntilFormula, ValueType> const& checkTask) override;

29
src/storm/modelchecker/prctl/SparseMdpPrctlModelChecker.cpp

@ -39,23 +39,32 @@ namespace storm {
}
template<typename SparseMdpModelType>
bool SparseMdpPrctlModelChecker<SparseMdpModelType>::canHandle(CheckTask<storm::logic::Formula, ValueType> const& checkTask) const {
bool SparseMdpPrctlModelChecker<SparseMdpModelType>::canHandleStatic(CheckTask<storm::logic::Formula, ValueType> const& checkTask, bool* requiresSingleInitialState) {
storm::logic::Formula const& formula = checkTask.getFormula();
if (formula.isInFragment(storm::logic::prctl().setLongRunAverageRewardFormulasAllowed(true).setLongRunAverageProbabilitiesAllowed(true).setConditionalProbabilityFormulasAllowed(true).setOnlyEventuallyFormuluasInConditionalFormulasAllowed(true).setTotalRewardFormulasAllowed(true).setRewardBoundedUntilFormulasAllowed(true).setRewardBoundedCumulativeRewardFormulasAllowed(true).setMultiDimensionalBoundedUntilFormulasAllowed(true).setMultiDimensionalCumulativeRewardFormulasAllowed(true).setTimeOperatorsAllowed(true).setReachbilityTimeFormulasAllowed(true).setRewardAccumulationAllowed(true))) {
return true;
} else if (formula.isInFragment(storm::logic::multiObjective().setCumulativeRewardFormulasAllowed(true).setTimeBoundedCumulativeRewardFormulasAllowed(true).setStepBoundedCumulativeRewardFormulasAllowed(true).setRewardBoundedCumulativeRewardFormulasAllowed(true).setTimeBoundedUntilFormulasAllowed(true).setStepBoundedUntilFormulasAllowed(true).setRewardBoundedUntilFormulasAllowed(true).setMultiDimensionalBoundedUntilFormulasAllowed(true).setMultiDimensionalCumulativeRewardFormulasAllowed(true))) {
// Check whether we consider a multi-objective formula
// For multi-objective model checking, each initial state requires an individual scheduler (in contrast to single-objective model checking). Let's exclude multiple initial states.
if (this->getModel().getInitialStates().getNumberOfSetBits() > 1) return false;
if (!checkTask.isOnlyInitialStatesRelevantSet()) return false;
return true;
} else if (formula.isInFragment(storm::logic::quantiles())) {
if (this->getModel().getInitialStates().getNumberOfSetBits() > 1) return false;
return true;
} else if (checkTask.isOnlyInitialStatesRelevantSet()) {
auto multiObjectiveFragment = storm::logic::multiObjective().setCumulativeRewardFormulasAllowed(true).setTimeBoundedCumulativeRewardFormulasAllowed(true).setStepBoundedCumulativeRewardFormulasAllowed(true).setRewardBoundedCumulativeRewardFormulasAllowed(true).setTimeBoundedUntilFormulasAllowed(true).setStepBoundedUntilFormulasAllowed(true).setRewardBoundedUntilFormulasAllowed(true).setMultiDimensionalBoundedUntilFormulasAllowed(true).setMultiDimensionalCumulativeRewardFormulasAllowed(true);
if (formula.isInFragment(multiObjectiveFragment) || formula.isInFragment(storm::logic::quantiles())) {
if (requiresSingleInitialState) {
*requiresSingleInitialState = true;
}
return true;
}
}
return false;
}
template<typename SparseMdpModelType>
bool SparseMdpPrctlModelChecker<SparseMdpModelType>::canHandle(CheckTask<storm::logic::Formula, ValueType> const& checkTask) const {
bool requiresSingleInitialState = false;
if (canHandleStatic(checkTask, &requiresSingleInitialState)) {
return !requiresSingleInitialState || this->getModel().getInitialStates().getNumberOfSetBits() == 1;
} else {
return false;
}
}
template<typename SparseMdpModelType>
std::unique_ptr<CheckResult> SparseMdpPrctlModelChecker<SparseMdpModelType>::computeBoundedUntilProbabilities(Environment const& env, CheckTask<storm::logic::BoundedUntilFormula, ValueType> const& checkTask) {
storm::logic::BoundedUntilFormula const& pathFormula = checkTask.getFormula();

6
src/storm/modelchecker/prctl/SparseMdpPrctlModelChecker.h

@ -18,6 +18,12 @@ namespace storm {
explicit SparseMdpPrctlModelChecker(SparseMdpModelType const& model);
/*!
* Returns false, if this task can certainly not be handled by this model checker (independent of the concrete model).
* @param requiresSingleInitialState if not nullptr, this flag is set to true iff checking this formula requires a model with a single initial state
*/
static bool canHandleStatic(CheckTask<storm::logic::Formula, ValueType> const& checkTask, bool* requiresSingleInitialState = nullptr);
// The implemented methods of the AbstractModelChecker interface.
virtual bool canHandle(CheckTask<storm::logic::Formula, ValueType> const& checkTask) const override;
virtual std::unique_ptr<CheckResult> computeBoundedUntilProbabilities(Environment const& env, CheckTask<storm::logic::BoundedUntilFormula, ValueType> const& checkTask) override;

7
src/storm/modelchecker/prctl/SymbolicDtmcPrctlModelChecker.cpp

@ -29,11 +29,16 @@ namespace storm {
}
template<typename ModelType>
bool SymbolicDtmcPrctlModelChecker<ModelType>::canHandle(CheckTask<storm::logic::Formula, ValueType> const& checkTask) const {
bool SymbolicDtmcPrctlModelChecker<ModelType>::canHandleStatic(CheckTask<storm::logic::Formula, ValueType> const& checkTask) {
storm::logic::Formula const& formula = checkTask.getFormula();
return formula.isInFragment(storm::logic::prctl().setLongRunAverageRewardFormulasAllowed(false).setTimeOperatorsAllowed(true).setReachbilityTimeFormulasAllowed(true).setRewardAccumulationAllowed(true));
}
template<typename ModelType>
bool SymbolicDtmcPrctlModelChecker<ModelType>::canHandle(CheckTask<storm::logic::Formula, ValueType> const& checkTask) const {
return canHandleStatic(checkTask);
}
template<typename ModelType>
std::unique_ptr<CheckResult> SymbolicDtmcPrctlModelChecker<ModelType>::computeUntilProbabilities(Environment const& env, CheckTask<storm::logic::UntilFormula, ValueType> const& checkTask) {
storm::logic::UntilFormula const& pathFormula = checkTask.getFormula();

3
src/storm/modelchecker/prctl/SymbolicDtmcPrctlModelChecker.h

@ -17,6 +17,9 @@ namespace storm {
explicit SymbolicDtmcPrctlModelChecker(ModelType const& model);
// Returns false, if this task can certainly not be handled by this model checker (independent of the concrete model).
static bool canHandleStatic(CheckTask<storm::logic::Formula, ValueType> const& checkTask);
// The implemented methods of the AbstractModelChecker interface.
virtual bool canHandle(CheckTask<storm::logic::Formula, ValueType> const& checkTask) const override;
virtual std::unique_ptr<CheckResult> computeBoundedUntilProbabilities(Environment const& env, CheckTask<storm::logic::BoundedUntilFormula, ValueType> const& checkTask) override;

7
src/storm/modelchecker/prctl/SymbolicMdpPrctlModelChecker.cpp

@ -29,11 +29,16 @@ namespace storm {
}
template<typename ModelType>
bool SymbolicMdpPrctlModelChecker<ModelType>::canHandle(CheckTask<storm::logic::Formula, ValueType> const& checkTask) const {
bool SymbolicMdpPrctlModelChecker<ModelType>::canHandleStatic(CheckTask<storm::logic::Formula, ValueType> const& checkTask) {
storm::logic::Formula const& formula = checkTask.getFormula();
return formula.isInFragment(storm::logic::prctl().setLongRunAverageRewardFormulasAllowed(false).setTimeOperatorsAllowed(true).setReachbilityTimeFormulasAllowed(true).setRewardAccumulationAllowed(true));
}
template<typename ModelType>
bool SymbolicMdpPrctlModelChecker<ModelType>::canHandle(CheckTask<storm::logic::Formula, ValueType> const& checkTask) const {
return canHandleStatic(checkTask);
}
template<typename ModelType>
std::unique_ptr<CheckResult> SymbolicMdpPrctlModelChecker<ModelType>::computeUntilProbabilities(Environment const& env, CheckTask<storm::logic::UntilFormula, ValueType> const& checkTask) {
storm::logic::UntilFormula const& pathFormula = checkTask.getFormula();

3
src/storm/modelchecker/prctl/SymbolicMdpPrctlModelChecker.h

@ -18,6 +18,9 @@ namespace storm {
explicit SymbolicMdpPrctlModelChecker(ModelType const& model);
// Returns false, if this task can certainly not be handled by this model checker (independent of the concrete model).
static bool canHandleStatic(CheckTask<storm::logic::Formula, ValueType> const& checkTask);
// The implemented methods of the AbstractModelChecker interface.
virtual bool canHandle(CheckTask<storm::logic::Formula, ValueType> const& checkTask) const override;
virtual std::unique_ptr<CheckResult> computeBoundedUntilProbabilities(Environment const& env, CheckTask<storm::logic::BoundedUntilFormula, ValueType> const& checkTask) override;

2
src/storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.cpp

@ -1477,7 +1477,7 @@ namespace storm {
// Solve MEC with the method specified in the settings
storm::solver::LraMethod method = env.solver().lra().getNondetLraMethod();
if (storm::NumberTraits<ValueType>::IsExact && env.solver().lra().isNondetLraMethodSetFromDefault() && method != storm::solver::LraMethod::LinearProgramming) {
if ((storm::NumberTraits<ValueType>::IsExact || env.solver().isForceExact()) && env.solver().lra().isNondetLraMethodSetFromDefault() && method != storm::solver::LraMethod::LinearProgramming) {
STORM_LOG_INFO("Selecting 'LP' as the solution technique for long-run properties to guarantee exact results. If you want to override this, please explicitly specify a different LRA method.");
method = storm::solver::LraMethod::LinearProgramming;
} else if (env.solver().isForceSoundness() && env.solver().lra().isNondetLraMethodSetFromDefault() && method != storm::solver::LraMethod::ValueIteration) {

6
src/storm/modelchecker/propositional/SymbolicPropositionalModelChecker.cpp

@ -1,3 +1,4 @@
#include <storm/models/symbolic/MarkovAutomaton.h>
#include "storm/modelchecker/propositional/SymbolicPropositionalModelChecker.h"
#include "storm/storage/dd/Add.h"
@ -6,6 +7,7 @@
#include "storm/models/symbolic/Dtmc.h"
#include "storm/models/symbolic/Ctmc.h"
#include "storm/models/symbolic/Mdp.h"
#include "storm/models/symbolic/MarkovAutomaton.h"
#include "storm/models/symbolic/StochasticTwoPlayerGame.h"
#include "storm/models/symbolic/StandardRewardModel.h"
@ -62,21 +64,25 @@ namespace storm {
template class SymbolicPropositionalModelChecker<storm::models::symbolic::Dtmc<storm::dd::DdType::CUDD, double>>;
template class SymbolicPropositionalModelChecker<storm::models::symbolic::Ctmc<storm::dd::DdType::CUDD, double>>;
template class SymbolicPropositionalModelChecker<storm::models::symbolic::Mdp<storm::dd::DdType::CUDD, double>>;
template class SymbolicPropositionalModelChecker<storm::models::symbolic::MarkovAutomaton<storm::dd::DdType::CUDD, double>>;
template class SymbolicPropositionalModelChecker<storm::models::symbolic::StochasticTwoPlayerGame<storm::dd::DdType::CUDD, double>>;
template class SymbolicPropositionalModelChecker<storm::models::symbolic::Model<storm::dd::DdType::Sylvan, double>>;
template class SymbolicPropositionalModelChecker<storm::models::symbolic::Dtmc<storm::dd::DdType::Sylvan, double>>;
template class SymbolicPropositionalModelChecker<storm::models::symbolic::Ctmc<storm::dd::DdType::Sylvan, double>>;
template class SymbolicPropositionalModelChecker<storm::models::symbolic::Mdp<storm::dd::DdType::Sylvan, double>>;
template class SymbolicPropositionalModelChecker<storm::models::symbolic::MarkovAutomaton<storm::dd::DdType::Sylvan, double>>;
template class SymbolicPropositionalModelChecker<storm::models::symbolic::StochasticTwoPlayerGame<storm::dd::DdType::Sylvan, double>>;
template class SymbolicPropositionalModelChecker<storm::models::symbolic::Model<storm::dd::DdType::Sylvan, storm::RationalNumber>>;
template class SymbolicPropositionalModelChecker<storm::models::symbolic::Dtmc<storm::dd::DdType::Sylvan, storm::RationalNumber>>;
template class SymbolicPropositionalModelChecker<storm::models::symbolic::Ctmc<storm::dd::DdType::Sylvan, storm::RationalNumber>>;
template class SymbolicPropositionalModelChecker<storm::models::symbolic::Mdp<storm::dd::DdType::Sylvan, storm::RationalNumber>>;
template class SymbolicPropositionalModelChecker<storm::models::symbolic::MarkovAutomaton<storm::dd::DdType::Sylvan, storm::RationalNumber>>;
template class SymbolicPropositionalModelChecker<storm::models::symbolic::StochasticTwoPlayerGame<storm::dd::DdType::Sylvan, storm::RationalNumber>>;
template class SymbolicPropositionalModelChecker<storm::models::symbolic::Model<storm::dd::DdType::Sylvan, storm::RationalFunction>>;
template class SymbolicPropositionalModelChecker<storm::models::symbolic::Dtmc<storm::dd::DdType::Sylvan, storm::RationalFunction>>;
template class SymbolicPropositionalModelChecker<storm::models::symbolic::Ctmc<storm::dd::DdType::Sylvan, storm::RationalFunction>>;
template class SymbolicPropositionalModelChecker<storm::models::symbolic::Mdp<storm::dd::DdType::Sylvan, storm::RationalFunction>>;
template class SymbolicPropositionalModelChecker<storm::models::symbolic::MarkovAutomaton<storm::dd::DdType::Sylvan, storm::RationalFunction>>;
template class SymbolicPropositionalModelChecker<storm::models::symbolic::StochasticTwoPlayerGame<storm::dd::DdType::Sylvan, storm::RationalFunction>>;
}

5
src/storm/models/sparse/MarkovAutomaton.cpp

@ -282,9 +282,8 @@ namespace storm {
if (isClosed() && markovianStates.empty()) {
return true;
}
storm::storage::MaximalEndComponentDecomposition<ValueType> maxEnd(this->getTransitionMatrix(), this->getBackwardTransitions(),~markovianStates);
return !maxEnd.empty();
storm::storage::BitVector statesWithZenoCycle = storm::utility::graph::performProb0E(*this, this->getBackwardTransitions(), ~markovianStates, markovianStates);
return !statesWithZenoCycle.empty();
}

4
src/storm/settings/SettingsManager.cpp

@ -32,6 +32,7 @@
#include "storm/settings/modules/GurobiSettings.h"
#include "storm/settings/modules/Smt2SmtSolverSettings.h"
#include "storm/settings/modules/TopologicalEquationSolverSettings.h"
#include "storm/settings/modules/TimeBoundedSolverSettings.h"
#include "storm/settings/modules/ExplorationSettings.h"
#include "storm/settings/modules/ResourceSettings.h"
#include "storm/settings/modules/AbstractionSettings.h"
@ -40,6 +41,7 @@
#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/utility/macros.h"
#include "storm/utility/file.h"
#include "storm/utility/string.h"
@ -657,6 +659,7 @@ namespace storm {
storm::settings::addModule<storm::settings::modules::NativeEquationSolverSettings>();
storm::settings::addModule<storm::settings::modules::EliminationSettings>();
storm::settings::addModule<storm::settings::modules::LongRunAverageSolverSettings>();
storm::settings::addModule<storm::settings::modules::TimeBoundedSolverSettings>();
storm::settings::addModule<storm::settings::modules::MinMaxEquationSolverSettings>();
storm::settings::addModule<storm::settings::modules::GameSolverSettings>();
storm::settings::addModule<storm::settings::modules::BisimulationSettings>();
@ -672,6 +675,7 @@ namespace storm {
storm::settings::addModule<storm::settings::modules::MultiplierSettings>();
storm::settings::addModule<storm::settings::modules::TransformationSettings>();
storm::settings::addModule<storm::settings::modules::HintSettings>();
storm::settings::addModule<storm::settings::modules::OviSolverSettings>();
}
}

11
src/storm/settings/modules/BisimulationSettings.cpp

@ -70,12 +70,17 @@ namespace storm {
return false;
}
BisimulationSettings::QuotientFormat BisimulationSettings::getQuotientFormat() const {
bool BisimulationSettings::isQuotientFormatSetFromDefaultValue() const {
return !this->getOption(quotientFormatOptionName).getHasOptionBeenSet() || this->getOption(quotientFormatOptionName).getArgumentByName("format").wasSetFromDefaultValue();
}
storm::dd::bisimulation::QuotientFormat BisimulationSettings::getQuotientFormat() const {
std::string quotientFormatAsString = this->getOption(quotientFormatOptionName).getArgumentByName("format").getValueAsString();
if (quotientFormatAsString == "sparse") {
return BisimulationSettings::QuotientFormat::Sparse;
return storm::dd::bisimulation::QuotientFormat::Sparse;
}
return BisimulationSettings::QuotientFormat::Dd;
STORM_LOG_ASSERT(quotientFormatAsString == "dd", "Invalid bisimulation quotient format: " << quotientFormatAsString << ".");
return storm::dd::bisimulation::QuotientFormat::Dd;
}
bool BisimulationSettings::isUseRepresentativesSet() const {

12
src/storm/settings/modules/BisimulationSettings.h

@ -4,6 +4,7 @@
#include "storm/settings/modules/ModuleSettings.h"
#include "storm/storage/dd/bisimulation/SignatureMode.h"
#include "storm/storage/dd/bisimulation/QuotientFormat.h"
namespace storm {
namespace settings {
@ -17,8 +18,6 @@ namespace storm {
// An enumeration of all available bisimulation types.
enum class BisimulationType { Strong, Weak };
enum class QuotientFormat { Sparse, Dd };
enum class ReuseMode { None, BlockNumbers };
enum class InitialPartitionMode { Regular, Finer };
@ -44,11 +43,18 @@ namespace storm {
*/
bool isWeakBisimulationSet() const;
/*!
* Retrieves whether the format in which the quotient is to be extracted has been set from its default value.
*
* @return True iff it has been set from its default value.
*/
bool isQuotientFormatSetFromDefaultValue() const;
/*!
* Retrieves the format in which the quotient is to be extracted.
* NOTE: only applies to DD-based bisimulation.
*/
QuotientFormat getQuotientFormat() const;
storm::dd::bisimulation::QuotientFormat getQuotientFormat() const;
/*!
* Retrieves whether representatives for blocks are to be used instead of the block numbers.

6
src/storm/settings/modules/BuildSettings.cpp

@ -18,7 +18,6 @@ namespace storm {
const std::string BuildSettings::moduleName = "build";
const std::string jitOptionName = "jit";
const std::string explorationOrderOptionName = "explorder";
const std::string explorationOrderOptionShortName = "eo";
const std::string explorationChecksOptionName = "explchecks";
@ -40,7 +39,6 @@ namespace storm {
this->addOption(storm::settings::OptionBuilder(moduleName, prismCompatibilityOptionName, false, "Enables PRISM compatibility. This may be necessary to process some PRISM models.").setShortName(prismCompatibilityOptionShortName).build());
this->addOption(storm::settings::OptionBuilder(moduleName, dontFixDeadlockOptionName, false, "If the model contains deadlock states, they need to be fixed by setting this option.").setShortName(dontFixDeadlockOptionShortName).setIsAdvanced().build());
this->addOption(storm::settings::OptionBuilder(moduleName, jitOptionName, false, "If set, the model is built using the JIT model builder.").build());
this->addOption(storm::settings::OptionBuilder(moduleName, fullModelBuildOptionName, false, "If set, include all rewards and labels.").build());
this->addOption(storm::settings::OptionBuilder(moduleName, applyNoMaxProgAssumptionOptionName, false, "If set, the maximum progress assumption is not applied while building the model (relevant for MAs)").setIsAdvanced().build());
this->addOption(storm::settings::OptionBuilder(moduleName, buildChoiceLabelOptionName, false, "If set, also build the choice labels").setIsAdvanced().build());
@ -57,10 +55,6 @@ namespace storm {
.addArgument(storm::settings::ArgumentBuilder::createUnsignedIntegerArgument("number", "The number of bits.").addValidatorUnsignedInteger(ArgumentValidatorFactory::createUnsignedRangeValidatorExcluding(0,63)).setDefaultValueUnsignedInteger(32).build()).build());
}
bool BuildSettings::isJitSet() const {
return this->getOption(jitOptionName).getHasOptionBeenSet();
}
bool BuildSettings::isExplorationOrderSet() const {
return this->getOption(explorationOrderOptionName).getHasOptionBeenSet();
}

6
src/storm/settings/modules/BuildSettings.h

@ -16,12 +16,6 @@ namespace storm {
* Creates a new set of core settings.
*/
BuildSettings();
/*!
* Retrieves whether the option to use the JIT builder is set.
*
* @return True iff the JIT builder is to be used.
*/
bool isJitSet() const;
/*!
* Retrieves whether the model exploration order was set.

28
src/storm/settings/modules/CoreSettings.cpp

@ -32,8 +32,11 @@ namespace storm {
const std::string CoreSettings::intelTbbOptionName = "enable-tbb";
const std::string CoreSettings::intelTbbOptionShortName = "tbb";
CoreSettings::CoreSettings() : ModuleSettings(moduleName), engine(CoreSettings::Engine::Sparse) {
std::vector<std::string> engines = {"sparse", "hybrid", "dd", "dd-to-sparse", "expl", "abs"};
CoreSettings::CoreSettings() : ModuleSettings(moduleName), engine(storm::utility::Engine::Sparse) {
std::vector<std::string> engines;
for (auto e : storm::utility::getEngines()) {
engines.push_back(storm::utility::toString(e));
}
this->addOption(storm::settings::OptionBuilder(moduleName, engineOptionName, false, "Sets which engine is used for model building and model checking.").setShortName(engineOptionShortName)
.addArgument(storm::settings::ArgumentBuilder::createStringArgument("name", "The name of the engine to use.").addValidatorString(ArgumentValidatorFactory::createMultipleChoiceValidator(engines)).setDefaultValueString("sparse").build()).build());
@ -133,32 +136,19 @@ namespace storm {
return this->getOption(cudaOptionName).getHasOptionBeenSet();
}
CoreSettings::Engine CoreSettings::getEngine() const {
storm::utility::Engine CoreSettings::getEngine() const {
return engine;
}
void CoreSettings::setEngine(Engine newEngine) {
void CoreSettings::setEngine(storm::utility::Engine const& newEngine) {
this->engine = newEngine;
}
void CoreSettings::finalize() {
// Finalize engine.
std::string engineStr = this->getOption(engineOptionName).getArgumentByName("name").getValueAsString();
if (engineStr == "sparse") {
engine = CoreSettings::Engine::Sparse;
} else if (engineStr == "hybrid") {
engine = CoreSettings::Engine::Hybrid;
} else if (engineStr == "dd") {
engine = CoreSettings::Engine::Dd;
} else if (engineStr == "dd-to-sparse") {
engine = CoreSettings::Engine::DdSparse;
} else if (engineStr == "expl") {
engine = CoreSettings::Engine::Exploration;
} else if (engineStr == "abs") {
engine = CoreSettings::Engine::AbstractionRefinement;
} else {
STORM_LOG_THROW(false, storm::exceptions::IllegalArgumentValueException, "Unknown engine '" << engineStr << "'.");
}
engine = storm::utility::engineFromString(engineStr);
STORM_LOG_THROW(engine != storm::utility::Engine::Unknown, storm::exceptions::IllegalArgumentValueException, "Unknown engine '" << engineStr << "'.");
}
bool CoreSettings::check() const {

11
src/storm/settings/modules/CoreSettings.h

@ -5,6 +5,7 @@
#include "storm/settings/modules/ModuleSettings.h"
#include "storm/builder/ExplorationOrder.h"
#include "storm/utility/Engine.h"
namespace storm {
namespace solver {
@ -26,10 +27,6 @@ namespace storm {
*/
class CoreSettings : public ModuleSettings {
public:
// An enumeration of all engines.
enum class Engine {
Sparse, Hybrid, Dd, DdSparse, Exploration, AbstractionRefinement
};
/*!
* Creates a new set of core settings.
@ -118,12 +115,12 @@ namespace storm {
*
* @return The selected engine.
*/
Engine getEngine() const;
storm::utility::Engine getEngine() const;
/*!
* Sets the engine for further usage.
*/
void setEngine(Engine);
void setEngine(storm::utility::Engine const& engine);
bool check() const override;
void finalize() override;
@ -132,7 +129,7 @@ namespace storm {
static const std::string moduleName;
private:
Engine engine;
storm::utility::Engine engine;
// Define the string names of the options as constants.
static const std::string eqSolverOptionName;

6
src/storm/settings/modules/DebugSettings.cpp

@ -13,6 +13,7 @@ namespace storm {
const std::string DebugSettings::moduleName = "debug";
const std::string DebugSettings::debugOptionName = "debug";
const std::string DebugSettings::traceOptionName = "trace";
const std::string DebugSettings::additionalChecksOptionName = "additional-checks";
const std::string DebugSettings::logfileOptionName = "logfile";
const std::string DebugSettings::logfileOptionShortName = "l";
const std::string DebugSettings::testOptionName = "test";
@ -20,6 +21,7 @@ namespace storm {
DebugSettings::DebugSettings() : ModuleSettings(moduleName) {
this->addOption(storm::settings::OptionBuilder(moduleName, debugOptionName, false, "Print debug output.").build());
this->addOption(storm::settings::OptionBuilder(moduleName, traceOptionName, false, "Print even more debug output.").build());
this->addOption(storm::settings::OptionBuilder(moduleName, additionalChecksOptionName, false, "If set, additional sanity checks are performed during execution.").build());
this->addOption(storm::settings::OptionBuilder(moduleName, logfileOptionName, false, "If specified, the log output will also be written to this file.").setShortName(logfileOptionShortName)
.addArgument(storm::settings::ArgumentBuilder::createStringArgument("filename", "The name of the file to write the log.").build()).build());
this->addOption(storm::settings::OptionBuilder(moduleName, testOptionName, false, "Activate a test setting.").setIsAdvanced().build());
@ -33,6 +35,10 @@ namespace storm {
return this->getOption(traceOptionName).getHasOptionBeenSet();
}
bool DebugSettings::isAdditionalChecksSet() const {
return this->getOption(additionalChecksOptionName).getHasOptionBeenSet();
}
bool DebugSettings::isLogfileSet() const {
return this->getOption(logfileOptionName).getHasOptionBeenSet();
}

8
src/storm/settings/modules/DebugSettings.h

@ -31,6 +31,13 @@ namespace storm {
*/
bool isTraceSet() const;
/*!
* Retrieves whether additional checks on the input should be performed.
*
* @return True iff additoinal checks on the input should be performed.
*/
bool isAdditionalChecksSet() const;
/*!
* Retrieves whether the logfile option was set.
*
@ -60,6 +67,7 @@ namespace storm {
// Define the string names of the options as constants.
static const std::string debugOptionName;
static const std::string traceOptionName;
static const std::string additionalChecksOptionName;
static const std::string logfileOptionName;
static const std::string logfileOptionShortName;
static const std::string testOptionName;

3
src/storm/settings/modules/ExplorationSettings.cpp

@ -7,6 +7,7 @@
#include "storm/settings/SettingsManager.h"
#include "storm/utility/macros.h"
#include "storm/utility/Engine.h"
#include "storm/exceptions/IllegalArgumentValueException.h"
namespace storm {
@ -91,7 +92,7 @@ namespace storm {
this->getOption(numberOfExplorationStepsUntilPrecomputationOptionName).getHasOptionBeenSet() ||
this->getOption(numberOfSampledPathsUntilPrecomputationOptionName).getHasOptionBeenSet() ||
this->getOption(nextStateHeuristicOptionName).getHasOptionBeenSet();
STORM_LOG_WARN_COND(storm::settings::getModule<storm::settings::modules::CoreSettings>().getEngine() == storm::settings::modules::CoreSettings::Engine::Exploration || !optionsSet, "Exploration engine is not selected, so setting options for it has no effect.");
STORM_LOG_WARN_COND(storm::settings::getModule<storm::settings::modules::CoreSettings>().getEngine() == storm::utility::Engine::Exploration || !optionsSet, "Exploration engine is not selected, so setting options for it has no effect.");
return true;
}
} // namespace modules

16
src/storm/settings/modules/MinMaxEquationSolverSettings.cpp

@ -17,12 +17,11 @@ namespace storm {
const std::string MinMaxEquationSolverSettings::maximalIterationsOptionShortName = "i";
const std::string MinMaxEquationSolverSettings::precisionOptionName = "precision";
const std::string MinMaxEquationSolverSettings::absoluteOptionName = "absolute";
const std::string MinMaxEquationSolverSettings::markovAutomatonBoundedReachabilityMethodOptionName = "mamethod";
const std::string MinMaxEquationSolverSettings::valueIterationMultiplicationStyleOptionName = "vimult";
const std::string MinMaxEquationSolverSettings::intervalIterationSymmetricUpdatesOptionName = "symmetricupdates";
MinMaxEquationSolverSettings::MinMaxEquationSolverSettings() : ModuleSettings(moduleName) {
std::vector<std::string> minMaxSolvingTechniques = {"vi", "value-iteration", "pi", "policy-iteration", "lp", "linear-programming", "rs", "ratsearch", "ii", "interval-iteration", "svi", "sound-value-iteration", "topological", "vi-to-pi"};
std::vector<std::string> minMaxSolvingTechniques = {"vi", "value-iteration", "pi", "policy-iteration", "lp", "linear-programming", "rs", "ratsearch", "ii", "interval-iteration", "svi", "sound-value-iteration", "ovi", "optimistic-value-iteration", "topological", "vi-to-pi"};
this->addOption(storm::settings::OptionBuilder(moduleName, solvingMethodOptionName, false, "Sets which min/max linear equation solving technique is preferred.").setIsAdvanced()
.addArgument(storm::settings::ArgumentBuilder::createStringArgument("name", "The name of a min/max linear equation solving technique.").addValidatorString(ArgumentValidatorFactory::createMultipleChoiceValidator(minMaxSolvingTechniques)).setDefaultValueString("topological").build()).build());
@ -32,9 +31,6 @@ namespace storm {
this->addOption(storm::settings::OptionBuilder(moduleName, absoluteOptionName, false, "Sets whether the relative or the absolute error is considered for detecting convergence.").setIsAdvanced().build());
std::vector<std::string> maMethods = {"imca", "unifplus"};
this->addOption(storm::settings::OptionBuilder(moduleName, markovAutomatonBoundedReachabilityMethodOptionName, false, "The method to use to solve bounded reachability queries on MAs.").setIsAdvanced().addArgument(storm::settings::ArgumentBuilder::createStringArgument("name", "The name of the method to use.").addValidatorString(ArgumentValidatorFactory::createMultipleChoiceValidator(maMethods)).setDefaultValueString("unifplus").build()).build());
std::vector<std::string> multiplicationStyles = {"gaussseidel", "regular", "gs", "r"};
this->addOption(storm::settings::OptionBuilder(moduleName, valueIterationMultiplicationStyleOptionName, false, "Sets which method multiplication style to prefer for value iteration.").setIsAdvanced()
.addArgument(storm::settings::ArgumentBuilder::createStringArgument("name", "The name of a multiplication style.").addValidatorString(ArgumentValidatorFactory::createMultipleChoiceValidator(multiplicationStyles)).setDefaultValueString("gaussseidel").build()).build());
@ -57,6 +53,8 @@ namespace storm {
return storm::solver::MinMaxMethod::IntervalIteration;
} else if (minMaxEquationSolvingTechnique == "sound-value-iteration" || minMaxEquationSolvingTechnique == "svi") {
return storm::solver::MinMaxMethod::SoundValueIteration;
} else if (minMaxEquationSolvingTechnique == "optimistic-value-iteration" || minMaxEquationSolvingTechnique == "ovi") {
return storm::solver::MinMaxMethod::OptimisticValueIteration;
} else if (minMaxEquationSolvingTechnique == "topological") {
return storm::solver::MinMaxMethod::Topological;
} else if (minMaxEquationSolvingTechnique == "vi-to-pi") {
@ -98,14 +96,6 @@ namespace storm {
return this->getOption(absoluteOptionName).getHasOptionBeenSet() ? MinMaxEquationSolverSettings::ConvergenceCriterion::Absolute : MinMaxEquationSolverSettings::ConvergenceCriterion::Relative;
}
MinMaxEquationSolverSettings::MarkovAutomatonBoundedReachabilityMethod MinMaxEquationSolverSettings::getMarkovAutomatonBoundedReachabilityMethod() const {
std::string techniqueAsString = this->getOption(markovAutomatonBoundedReachabilityMethodOptionName).getArgumentByName("name").getValueAsString();
if (techniqueAsString == "imca") {
return MinMaxEquationSolverSettings::MarkovAutomatonBoundedReachabilityMethod::Imca;
}
return MinMaxEquationSolverSettings::MarkovAutomatonBoundedReachabilityMethod::UnifPlus;
}
storm::solver::MultiplicationStyle MinMaxEquationSolverSettings::getValueIterationMultiplicationStyle() const {
std::string multiplicationStyleString = this->getOption(valueIterationMultiplicationStyleOptionName).getArgumentByName("name").getValueAsString();
if (multiplicationStyleString == "gaussseidel" || multiplicationStyleString == "gs") {

11
src/storm/settings/modules/MinMaxEquationSolverSettings.h

@ -18,9 +18,6 @@ namespace storm {
// An enumeration of all available convergence criteria.
enum class ConvergenceCriterion { Absolute, Relative };
// An enumeration of all available bounded reachability methods for MAs.
enum class MarkovAutomatonBoundedReachabilityMethod { Imca, UnifPlus };
MinMaxEquationSolverSettings();
/*!
@ -86,13 +83,6 @@ namespace storm {
*/
ConvergenceCriterion getConvergenceCriterion() const;
/*!
* Retrieves the method to be used for bounded reachability on MAs.
*
* @return The selected method.
*/
MarkovAutomatonBoundedReachabilityMethod getMarkovAutomatonBoundedReachabilityMethod() const;
/*!
* Retrieves the multiplication style to use in the min-max methods.
*
@ -114,7 +104,6 @@ namespace storm {
static const std::string maximalIterationsOptionShortName;
static const std::string precisionOptionName;
static const std::string absoluteOptionName;
static const std::string markovAutomatonBoundedReachabilityMethodOptionName;
static const std::string valueIterationMultiplicationStyleOptionName;
static const std::string intervalIterationSymmetricUpdatesOptionName;
static const std::string forceBoundsOptionName;

4
src/storm/settings/modules/NativeEquationSolverSettings.cpp

@ -26,7 +26,7 @@ namespace storm {
const std::string NativeEquationSolverSettings::intervalIterationSymmetricUpdatesOptionName = "symmetricupdates";
NativeEquationSolverSettings::NativeEquationSolverSettings() : ModuleSettings(moduleName) {
std::vector<std::string> methods = { "jacobi", "gaussseidel", "sor", "walkerchae", "power", "sound-value-iteration", "svi", "interval-iteration", "ii", "ratsearch" };
std::vector<std::string> methods = { "jacobi", "gaussseidel", "sor", "walkerchae", "power", "sound-value-iteration", "svi", "optimistic-value-itearation", "ovi", "interval-iteration", "ii", "ratsearch" };
this->addOption(storm::settings::OptionBuilder(moduleName, techniqueOptionName, true, "The method to be used for solving linear equation systems with the native engine.").setIsAdvanced().addArgument(storm::settings::ArgumentBuilder::createStringArgument("name", "The name of the method to use.").addValidatorString(ArgumentValidatorFactory::createMultipleChoiceValidator(methods)).setDefaultValueString("jacobi").build()).build());
this->addOption(storm::settings::OptionBuilder(moduleName, maximalIterationsOptionName, false, "The maximal number of iterations to perform before iterative solving is aborted.").setIsAdvanced().setShortName(maximalIterationsOptionShortName).addArgument(storm::settings::ArgumentBuilder::createUnsignedIntegerArgument("count", "The maximal iteration count.").build()).build());
@ -66,6 +66,8 @@ namespace storm {
return storm::solver::NativeLinearEquationSolverMethod::Power;
} else if (linearEquationSystemTechniqueAsString == "sound-value-iteration" || linearEquationSystemTechniqueAsString == "svi") {
return storm::solver::NativeLinearEquationSolverMethod::SoundValueIteration;
} else if (linearEquationSystemTechniqueAsString == "optimistic-value-iteration" || linearEquationSystemTechniqueAsString == "ovi") {
return storm::solver::NativeLinearEquationSolverMethod::OptimisticValueIteration;
} else if (linearEquationSystemTechniqueAsString == "interval-iteration" || linearEquationSystemTechniqueAsString == "ii") {
return storm::solver::NativeLinearEquationSolverMethod::IntervalIteration;
} else if (linearEquationSystemTechniqueAsString == "ratsearch") {

56
src/storm/settings/modules/OviSolverSettings.cpp

@ -0,0 +1,56 @@
#include "storm/settings/modules/OviSolverSettings.h"
#include "storm/settings/Option.h"
#include "storm/settings/ArgumentBuilder.h"
#include "storm/settings/OptionBuilder.h"
#include "storm/utility/macros.h"
#include "storm/exceptions/IllegalArgumentValueException.h"
namespace storm {
namespace settings {
namespace modules {
const std::string OviSolverSettings::moduleName = "ovi";
const std::string OviSolverSettings::precisionUpdateFactorOptionName = "precision-update-factor";
const std::string OviSolverSettings::maxVerificationIterationFactorOptionName = "max-verification-iter-factor";
const std::string OviSolverSettings::useRelevantValuesForPrecisionUpdateOptionName = "use-relevant-values";
const std::string OviSolverSettings::upperBoundGuessingFactorOptionName = "upper-bound-factor";
const std::string OviSolverSettings::upperBoundOnlyIterationsOptionName = "check-upper-only-iter";
OviSolverSettings::OviSolverSettings() : ModuleSettings(moduleName) {
this->addOption(storm::settings::OptionBuilder(moduleName, precisionUpdateFactorOptionName, false, "Sets with which factor the precision of the inner value iteration is updated.").setIsAdvanced().addArgument(storm::settings::ArgumentBuilder::createDoubleArgument("factor", "The factor.").setDefaultValueDouble(0.4).addValidatorDouble(ArgumentValidatorFactory::createDoubleRangeValidatorExcluding(0.0, 1.0)).build()).build());
this->addOption(storm::settings::OptionBuilder(moduleName, useRelevantValuesForPrecisionUpdateOptionName, false, "Sets whether the precision of the inner value iteration is only based on the relevant values (i.e. initial states).").setIsAdvanced().build());
this->addOption(storm::settings::OptionBuilder(moduleName, maxVerificationIterationFactorOptionName, false, "Controls how many verification iterations are performed before guessing a new upper bound.").setIsAdvanced().addArgument(storm::settings::ArgumentBuilder::createDoubleArgument("factor", "The factor.").setDefaultValueDouble(0.1).addValidatorDouble(ArgumentValidatorFactory::createDoubleGreaterValidator(0.0)).build()).build());
this->addOption(storm::settings::OptionBuilder(moduleName, upperBoundGuessingFactorOptionName, false, "Sets with which factor the precision is multiplied to guess the upper bound.").setIsAdvanced().addArgument(storm::settings::ArgumentBuilder::createDoubleArgument("factor", "The factor.").setDefaultValueDouble(1.0).addValidatorDouble(ArgumentValidatorFactory::createDoubleGreaterValidator(0.0)).build()).build());
this->addOption(storm::settings::OptionBuilder(moduleName, upperBoundOnlyIterationsOptionName, false, "Sets the max. iterations OVI will only iterate over the upper bound.").setIsAdvanced().addArgument(storm::settings::ArgumentBuilder::createIntegerArgument("iter", "The iterations.").setDefaultValueInteger(20000).addValidatorInteger(ArgumentValidatorFactory::createIntegerGreaterValidator(0)).build()).build());
}
double OviSolverSettings::getPrecisionUpdateFactor() const {
return this->getOption(precisionUpdateFactorOptionName).getArgumentByName("factor").getValueAsDouble();
}
double OviSolverSettings::getMaxVerificationIterationFactor() const {
return this->getOption(maxVerificationIterationFactorOptionName).getArgumentByName("factor").getValueAsDouble();
}
bool OviSolverSettings::useRelevantValuesForPrecisionUpdate() const {
return this->getOption(useRelevantValuesForPrecisionUpdateOptionName).getHasOptionBeenSet();
}
double OviSolverSettings::getUpperBoundGuessingFactor() const {
return this->getOption(upperBoundGuessingFactorOptionName).getArgumentByName("factor").getValueAsDouble();
}
uint64_t OviSolverSettings::getUpperBoundOnlyIterations() const {
return this->getOption(upperBoundOnlyIterationsOptionName).getArgumentByName("iter").getValueAsInteger();
}
}
}
}

42
src/storm/settings/modules/OviSolverSettings.h

@ -0,0 +1,42 @@
#pragma once
#include "storm-config.h"
#include "storm/settings/modules/ModuleSettings.h"
namespace storm {
namespace settings {
namespace modules {
/*!
* This class represents the settings for the optimistic value iteration solver.
*/
class OviSolverSettings : public ModuleSettings {
public:
OviSolverSettings();
double getPrecisionUpdateFactor() const;
double getMaxVerificationIterationFactor() const;
bool useRelevantValuesForPrecisionUpdate() const;
double getUpperBoundGuessingFactor() const;
uint64_t getUpperBoundOnlyIterations() const;
// The name of the module.
static const std::string moduleName;
private:
static const std::string precisionUpdateFactorOptionName;
static const std::string maxVerificationIterationFactorOptionName;
static const std::string useRelevantValuesForPrecisionUpdateOptionName;
static const std::string upperBoundGuessingFactorOptionName;
static const std::string upperBoundOnlyIterationsOptionName;
};
}
}
}

62
src/storm/settings/modules/TimeBoundedSolverSettings.cpp

@ -0,0 +1,62 @@
#include "storm/settings/modules/TimeBoundedSolverSettings.h"
#include "storm/settings/Option.h"
#include "storm/settings/ArgumentBuilder.h"
#include "storm/settings/OptionBuilder.h"
#include "storm/utility/macros.h"
namespace storm {
namespace settings {
namespace modules {
const std::string TimeBoundedSolverSettings::moduleName = "timebounded";
const std::string TimeBoundedSolverSettings::maMethodOptionName = "mamethod";
const std::string TimeBoundedSolverSettings::precisionOptionName = "precision";
const std::string TimeBoundedSolverSettings::absoluteOptionName = "absolute";
const std::string TimeBoundedSolverSettings::unifPlusKappaOptionName = "kappa";
TimeBoundedSolverSettings::TimeBoundedSolverSettings() : ModuleSettings(moduleName) {
std::vector<std::string> maMethods = {"imca", "unifplus"};
this->addOption(storm::settings::OptionBuilder(moduleName, maMethodOptionName, false, "The method to use to solve bounded reachability queries on MAs.").setIsAdvanced().addArgument(storm::settings::ArgumentBuilder::createStringArgument("name", "The name of the method to use.").addValidatorString(ArgumentValidatorFactory::createMultipleChoiceValidator(maMethods)).setDefaultValueString("unifplus").build()).build());
this->addOption(storm::settings::OptionBuilder(moduleName, precisionOptionName, false, "The precision used for detecting convergence of iterative methods.").setIsAdvanced().addArgument(storm::settings::ArgumentBuilder::createDoubleArgument("value", "The precision to achieve.").setDefaultValueDouble(1e-06).addValidatorDouble(ArgumentValidatorFactory::createDoubleRangeValidatorExcluding(0.0, 1.0)).build()).build());
this->addOption(storm::settings::OptionBuilder(moduleName, absoluteOptionName, false, "Sets whether the relative or the absolute error is considered for detecting convergence.").setIsAdvanced().build());
this->addOption(storm::settings::OptionBuilder(moduleName, unifPlusKappaOptionName, false, "The truncation factor used in unifPlus.").setIsAdvanced().addArgument(storm::settings::ArgumentBuilder::createDoubleArgument("kappa", "The factor").setDefaultValueDouble(0.1).addValidatorDouble(ArgumentValidatorFactory::createDoubleRangeValidatorExcluding(0.0, 1.0)).build()).build());
}
bool TimeBoundedSolverSettings::isPrecisionSet() const {
return this->getOption(precisionOptionName).getHasOptionBeenSet();
}
double TimeBoundedSolverSettings::getPrecision() const {
return this->getOption(precisionOptionName).getArgumentByName("value").getValueAsDouble();
}
bool TimeBoundedSolverSettings::isRelativePrecision() const {
return !this->getOption(absoluteOptionName).getHasOptionBeenSet();
}
storm::solver::MaBoundedReachabilityMethod TimeBoundedSolverSettings::getMaMethod() const {
std::string techniqueAsString = this->getOption(maMethodOptionName).getArgumentByName("name").getValueAsString();
if (techniqueAsString == "imca") {
return storm::solver::MaBoundedReachabilityMethod::Imca;
}
return storm::solver::MaBoundedReachabilityMethod::UnifPlus;
}
bool TimeBoundedSolverSettings::isMaMethodSetFromDefaultValue() const {
return !this->getOption(maMethodOptionName).getArgumentByName("name").getHasBeenSet() || this->getOption(maMethodOptionName).getArgumentByName("name").wasSetFromDefaultValue();
}
double TimeBoundedSolverSettings::getUnifPlusKappa() const {
return this->getOption(unifPlusKappaOptionName).getArgumentByName("kappa").getValueAsDouble();
}
}
}
}

66
src/storm/settings/modules/TimeBoundedSolverSettings.h

@ -0,0 +1,66 @@
#pragma once
#include "storm-config.h"
#include "storm/settings/modules/ModuleSettings.h"
#include "storm/solver/SolverSelectionOptions.h"
namespace storm {
namespace settings {
namespace modules {
/*!
* This class represents the min/max solver settings.
*/
class TimeBoundedSolverSettings : public ModuleSettings {
public:
TimeBoundedSolverSettings();
/*!
* Retrieves whether solving technique for time bounded reachability on Markov Automata has been set from its default value.
*/
bool isMaMethodSetFromDefaultValue() const;
/*!
* Retrieves the selected solving technique for time bounded reachability on Markov Automata.
*/
storm::solver::MaBoundedReachabilityMethod getMaMethod() const;
/*!
* Retrieves whether the precision has been set.
*
* @return True iff the precision has been set.
*/
bool isPrecisionSet() const;
/*!
* Retrieves the precision that is used for detecting convergence.
*
* @return The precision to use for detecting convergence.
*/
double getPrecision() const;
/*!
* Retrieves whether the convergence criterion has been set to relative or absolute.
*/
bool isRelativePrecision() const;
/*!
* Retrieves the truncation factor used for unifPlus
*/
double getUnifPlusKappa() const;
// The name of the module.
static const std::string moduleName;
private:
static const std::string maMethodOptionName;
static const std::string precisionOptionName;
static const std::string absoluteOptionName;
static const std::string unifPlusKappaOptionName;
};
}
}
}

4
src/storm/settings/modules/TopologicalEquationSolverSettings.cpp

@ -29,7 +29,7 @@ namespace storm {
std::vector<std::string> linearEquationSolver = {"gmm++", "native", "eigen", "elimination"};
this->addOption(storm::settings::OptionBuilder(moduleName, underlyingEquationSolverOptionName, true, "Sets which solver is considered for solving the underlying equation systems.").setIsAdvanced()
.addArgument(storm::settings::ArgumentBuilder::createStringArgument("name", "The name of the used solver.").addValidatorString(ArgumentValidatorFactory::createMultipleChoiceValidator(linearEquationSolver)).setDefaultValueString("gmm++").build()).build());
std::vector<std::string> minMaxSolvingTechniques = {"vi", "value-iteration", "pi", "policy-iteration", "lp", "linear-programming", "rs", "ratsearch", "ii", "interval-iteration", "svi", "sound-value-iteration", "vi-to-pi"};
std::vector<std::string> minMaxSolvingTechniques = {"vi", "value-iteration", "pi", "policy-iteration", "lp", "linear-programming", "rs", "ratsearch", "ii", "interval-iteration", "svi", "sound-value-iteration", "ovi", "optimistic-value-iteration", "vi-to-pi"};
this->addOption(storm::settings::OptionBuilder(moduleName, underlyingMinMaxMethodOptionName, true, "Sets which minmax method is considered for solving the underlying minmax equation systems.").setIsAdvanced()
.addArgument(storm::settings::ArgumentBuilder::createStringArgument("name", "The name of the used min max method.").addValidatorString(ArgumentValidatorFactory::createMultipleChoiceValidator(minMaxSolvingTechniques)).setDefaultValueString("value-iteration").build()).build());
}
@ -78,6 +78,8 @@ namespace storm {
return storm::solver::MinMaxMethod::IntervalIteration;
} else if (minMaxEquationSolvingTechnique == "sound-value-iteration" || minMaxEquationSolvingTechnique == "svi") {
return storm::solver::MinMaxMethod::SoundValueIteration;
} else if (minMaxEquationSolvingTechnique == "optimistic-value-iteration" || minMaxEquationSolvingTechnique == "ovi") {
return storm::solver::MinMaxMethod::OptimisticValueIteration;
} else if (minMaxEquationSolvingTechnique == "vi-to-pi") {
return storm::solver::MinMaxMethod::ViToPi;
}

2
src/storm/solver/EigenLinearEquationSolver.cpp

@ -107,7 +107,7 @@ namespace storm {
auto eigenX = StormEigen::Matrix<ValueType, StormEigen::Dynamic, 1>::Map(x.data(), x.size());
auto eigenB = StormEigen::Matrix<ValueType, StormEigen::Dynamic, 1>::Map(b.data(), b.size());
auto solutionMethod = getMethod(env, false);
auto solutionMethod = getMethod(env, env.solver().isForceExact());
if (solutionMethod == EigenLinearEquationSolverMethod::SparseLU) {
STORM_LOG_INFO("Solving linear equation system (" << x.size() << " rows) with sparse LU factorization (Eigen library).");
StormEigen::SparseLU<StormEigen::SparseMatrix<ValueType>, StormEigen::COLAMDOrdering<int>> solver;

1
src/storm/solver/GmmxxLinearEquationSolver.cpp

@ -46,6 +46,7 @@ namespace storm {
template<typename ValueType>
GmmxxLinearEquationSolverMethod GmmxxLinearEquationSolver<ValueType>::getMethod(Environment const& env) const {
STORM_LOG_ERROR_COND(!env.solver().isForceSoundness(), "This linear equation solver does not support sound computations. Using unsound methods now...");
STORM_LOG_ERROR_COND(!env.solver().isForceExact(), "This linear equation solver does not support exact computations. Using unsound methods now...");
return env.solver().gmmxx().getMethod();
}

135
src/storm/solver/IterativeMinMaxLinearEquationSolver.cpp

@ -2,10 +2,12 @@
#include <limits>
#include "storm/solver/IterativeMinMaxLinearEquationSolver.h"
#include "storm/solver/helper/OptimisticValueIterationHelper.h"
#include "storm/utility/ConstantsComparator.h"
#include "storm/environment/solver/MinMaxSolverEnvironment.h"
#include "storm/environment/solver/OviSolverEnvironment.h"
#include "storm/utility/KwekMehlhorn.h"
#include "storm/utility/NumberTraits.h"
@ -49,7 +51,7 @@ namespace storm {
} else {
STORM_LOG_WARN("The selected solution method " << toString(method) << " does not guarantee exact results.");
}
} else if (env.solver().isForceSoundness() && method != MinMaxMethod::SoundValueIteration && method != MinMaxMethod::IntervalIteration && method != MinMaxMethod::PolicyIteration && method != MinMaxMethod::RationalSearch) {
} 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;
@ -57,17 +59,20 @@ namespace storm {
STORM_LOG_WARN("The selected solution method does not guarantee sound results.");
}
}
STORM_LOG_THROW(method == MinMaxMethod::ValueIteration || method == MinMaxMethod::PolicyIteration || method == MinMaxMethod::RationalSearch || method == MinMaxMethod::SoundValueIteration || method == MinMaxMethod::IntervalIteration || method == MinMaxMethod::ViToPi, storm::exceptions::InvalidEnvironmentException, "This solver does not support the selected method.");
STORM_LOG_THROW(method == MinMaxMethod::ValueIteration || method == MinMaxMethod::PolicyIteration || method == MinMaxMethod::RationalSearch || method == MinMaxMethod::SoundValueIteration || method == MinMaxMethod::IntervalIteration || method == MinMaxMethod::OptimisticValueIteration || method == MinMaxMethod::ViToPi, storm::exceptions::InvalidEnvironmentException, "This solver does not support the selected method.");
return method;
}
template<typename ValueType>
bool IterativeMinMaxLinearEquationSolver<ValueType>::internalSolveEquations(Environment const& env, OptimizationDirection dir, std::vector<ValueType>& x, std::vector<ValueType> const& b) const {
bool result = false;
switch (getMethod(env, storm::NumberTraits<ValueType>::IsExact)) {
switch (getMethod(env, storm::NumberTraits<ValueType>::IsExact || env.solver().isForceExact())) {
case MinMaxMethod::ValueIteration:
result = solveEquationsValueIteration(env, dir, x, b);
break;
case MinMaxMethod::OptimisticValueIteration:
result = solveEquationsOptimisticValueIteration(env, dir, x, b);
break;
case MinMaxMethod::PolicyIteration:
result = solveEquationsPolicyIteration(env, dir, x, b);
break;
@ -228,7 +233,7 @@ namespace storm {
template<typename ValueType>
MinMaxLinearEquationSolverRequirements IterativeMinMaxLinearEquationSolver<ValueType>::getRequirements(Environment const& env, boost::optional<storm::solver::OptimizationDirection> const& direction, bool const& hasInitialScheduler) const {
auto method = getMethod(env, storm::NumberTraits<ValueType>::IsExact);
auto method = getMethod(env, storm::NumberTraits<ValueType>::IsExact || env.solver().isForceExact());
// Check whether a linear equation solver is needed and potentially start with its requirements
bool needsLinEqSolver = false;
@ -252,6 +257,13 @@ namespace storm {
}
}
}
} else if (method == MinMaxMethod::OptimisticValueIteration) {
// OptimisticValueIteration always requires lower bounds and a unique solution.
if (!this->hasUniqueSolution()) {
requirements.requireUniqueSolution();
}
requirements.requireLowerBounds();
} else if (method == MinMaxMethod::IntervalIteration) {
// Interval iteration requires a unique solution and lower+upper bounds
if (!this->hasUniqueSolution()) {
@ -327,7 +339,100 @@ namespace storm {
return ValueIterationResult(iterations - currentIterations, status);
}
template<typename ValueType>
ValueType computeMaxAbsDiff(std::vector<ValueType> const& allValues, storm::storage::BitVector const& relevantValues, std::vector<ValueType> const& oldValues) {
ValueType result = storm::utility::zero<ValueType>();
auto oldValueIt = oldValues.begin();
for (auto value : relevantValues) {
result = storm::utility::max<ValueType>(result, storm::utility::abs<ValueType>(allValues[value] - *oldValueIt));
++oldValueIt;
}
return result;
}
template<typename ValueType>
ValueType computeMaxAbsDiff(std::vector<ValueType> const& allOldValues, std::vector<ValueType> const& allNewValues, storm::storage::BitVector const& relevantValues) {
ValueType result = storm::utility::zero<ValueType>();
for (auto value : relevantValues) {
result = storm::utility::max<ValueType>(result, storm::utility::abs<ValueType>(allNewValues[value] - allOldValues[value]));
}
return result;
}
template<typename ValueType>
bool IterativeMinMaxLinearEquationSolver<ValueType>::solveEquationsOptimisticValueIteration(Environment const& env, OptimizationDirection dir, std::vector<ValueType>& x, std::vector<ValueType> const& b) const {
if (!this->multiplierA) {
this->multiplierA = storm::solver::MultiplierFactory<ValueType>().create(env, *this->A);
}
if (!auxiliaryRowGroupVector) {
auxiliaryRowGroupVector = std::make_unique<std::vector<ValueType>>(this->A->getRowGroupCount());
}
if (!auxiliaryRowGroupVector2) {
auxiliaryRowGroupVector2 = std::make_unique<std::vector<ValueType>>(this->A->getRowGroupCount());
}
// By default, we can not provide any guarantee
SolverGuarantee guarantee = SolverGuarantee::None;
// Get handle to multiplier.
storm::solver::Multiplier<ValueType> const &multiplier = *this->multiplierA;
// Allow aliased multiplications.
storm::solver::MultiplicationStyle multiplicationStyle = env.solver().minMax().getMultiplicationStyle();
bool useGaussSeidelMultiplication = multiplicationStyle == storm::solver::MultiplicationStyle::GaussSeidel;
boost::optional<storm::storage::BitVector> relevantValues;
if (this->hasRelevantValues()) {
relevantValues = this->getRelevantValues();
}
// x has to start with a lower bound.
this->createLowerBoundsVector(x);
std::vector<ValueType>* lowerX = &x;
std::vector<ValueType>* upperX = auxiliaryRowGroupVector.get();
std::vector<ValueType>* auxVector = auxiliaryRowGroupVector2.get();
this->startMeasureProgress();
auto statusIters = storm::solver::helper::solveEquationsOptimisticValueIteration(env, lowerX, upperX, auxVector,
[&] (std::vector<ValueType>*& y, std::vector<ValueType>*& yPrime, ValueType const& precision, bool const& relative, uint64_t const& i, uint64_t const& maxI) {
this->showProgressIterative(i);
return performValueIteration(env, dir, y, yPrime, b, precision, relative, guarantee, i, maxI, multiplicationStyle);
},
[&] (std::vector<ValueType>* y, std::vector<ValueType>* yPrime, uint64_t const& i) {
this->showProgressIterative(i);
if (useGaussSeidelMultiplication) {
// Copy over the current vectors so we can modify them in-place.
// This is necessary as we want to compare the new values with the current ones.
*yPrime = *y;
multiplier.multiplyAndReduceGaussSeidel(env, dir, *y, &b);
} else {
multiplier.multiplyAndReduce(env, dir, *y, &b, *yPrime);
std::swap(y, yPrime);
}
}, relevantValues);
auto two = storm::utility::convertNumber<ValueType>(2.0);
storm::utility::vector::applyPointwise<ValueType, ValueType, ValueType>(*lowerX, *upperX, x, [&two] (ValueType const& a, ValueType const& b) -> ValueType { return (a + b) / two; });
reportStatus(statusIters.first, statusIters.second);
// If requested, we store the scheduler for retrieval.
if (this->isTrackSchedulerSet()) {
this->schedulerChoices = std::vector<uint_fast64_t>(this->A->getRowGroupCount());
this->multiplierA->multiplyAndReduce(env, dir, x, &b, *auxiliaryRowGroupVector.get(), &this->schedulerChoices.get());
this->multiplierA->multiplyAndReduce(env, dir, x, &b, *auxiliaryRowGroupVector.get(), &this->schedulerChoices.get());
}
if (!this->isCachingEnabled()) {
clearCache();
}
return statusIters.first == SolverStatus::Converged || statusIters.first == SolverStatus::TerminatedEarly;
}
template<typename ValueType>
bool IterativeMinMaxLinearEquationSolver<ValueType>::solveEquationsValueIteration(Environment const& env, OptimizationDirection dir, std::vector<ValueType>& x, std::vector<ValueType> const& b) const {
if (!this->multiplierA) {
@ -418,26 +523,6 @@ namespace storm {
storm::utility::vector::selectVectorValues(oldValues, relevantValues, allValues);
}
template<typename ValueType>
ValueType computeMaxAbsDiff(std::vector<ValueType> const& allValues, storm::storage::BitVector const& relevantValues, std::vector<ValueType> const& oldValues) {
ValueType result = storm::utility::zero<ValueType>();
auto oldValueIt = oldValues.begin();
for (auto value : relevantValues) {
result = storm::utility::max<ValueType>(result, storm::utility::abs<ValueType>(allValues[value] - *oldValueIt));
++oldValueIt;
}
return result;
}
template<typename ValueType>
ValueType computeMaxAbsDiff(std::vector<ValueType> const& allOldValues, std::vector<ValueType> const& allNewValues, storm::storage::BitVector const& relevantValues) {
ValueType result = storm::utility::zero<ValueType>();
for (auto value : relevantValues) {
result = storm::utility::max<ValueType>(result, storm::utility::abs<ValueType>(allNewValues[value] - allOldValues[value]));
}
return result;
}
/*!
* This version of value iteration is sound, because it approaches the solution from below and above. This
* technique is due to Haddad and Monmege (Interval iteration algorithm for MDPs and IMDPs, TCS 2017) and was

1
src/storm/solver/IterativeMinMaxLinearEquationSolver.h

@ -40,6 +40,7 @@ namespace storm {
bool valueImproved(OptimizationDirection dir, ValueType const& value1, ValueType const& value2) const;
bool solveEquationsValueIteration(Environment const& env, OptimizationDirection dir, std::vector<ValueType>& x, std::vector<ValueType> const& b) const;
bool solveEquationsOptimisticValueIteration(Environment const& env, OptimizationDirection dir, std::vector<ValueType>& x, std::vector<ValueType> const& b) const;
bool solveEquationsIntervalIteration(Environment const& env, OptimizationDirection dir, std::vector<ValueType>& x, std::vector<ValueType> const& b) const;
bool solveEquationsSoundValueIteration(Environment const& env, OptimizationDirection dir, std::vector<ValueType>& x, std::vector<ValueType> const& b) const;
bool solveEquationsViToPi(Environment const& env, OptimizationDirection dir, std::vector<ValueType>& x, std::vector<ValueType> const& b) const;

11
src/storm/solver/LinearEquationSolver.cpp

@ -128,8 +128,15 @@ namespace storm {
std::unique_ptr<LinearEquationSolver<ValueType>> GeneralLinearEquationSolverFactory<ValueType>::create(Environment const& env) const {
EquationSolverType type = env.solver().getLinearEquationSolverType();
// Adjust the solver type if none was specified and we want sound computations
if (env.solver().isForceSoundness() && type != EquationSolverType::Native && type != EquationSolverType::Eigen && type != EquationSolverType::Elimination && type != EquationSolverType::Topological) {
// Adjust the solver type if none was specified and we want sound/exact computations
if (env.solver().isForceExact() && type != EquationSolverType::Native && type != EquationSolverType::Eigen && type != EquationSolverType::Elimination && type != EquationSolverType::Topological) {
if (env.solver().isLinearEquationSolverTypeSetFromDefaultValue()) {
type = EquationSolverType::Eigen;
STORM_LOG_INFO("Selecting '" + toString(type) + "' as the linear equation solver to guarantee exact results. If you want to override this, please explicitly specify a different solver.");
} else {
STORM_LOG_WARN("The selected solver does not yield exact results.");
}
} else if (env.solver().isForceSoundness() && type != EquationSolverType::Native && type != EquationSolverType::Eigen && type != EquationSolverType::Elimination && type != EquationSolverType::Topological) {
if (env.solver().isLinearEquationSolverTypeSetFromDefaultValue()) {
type = EquationSolverType::Native;
STORM_LOG_INFO("Selecting '" + toString(type) + "' as the linear equation solver to guarantee sound results. If you want to override this, please explicitly specify a different solver.");

4
src/storm/solver/MinMaxLinearEquationSolver.cpp

@ -203,7 +203,7 @@ namespace storm {
std::unique_ptr<MinMaxLinearEquationSolver<ValueType>> GeneralMinMaxLinearEquationSolverFactory<ValueType>::create(Environment const& env) const {
std::unique_ptr<MinMaxLinearEquationSolver<ValueType>> result;
auto method = env.solver().minMax().getMethod();
if (method == MinMaxMethod::ValueIteration || method == MinMaxMethod::PolicyIteration || method == MinMaxMethod::RationalSearch || method == MinMaxMethod::IntervalIteration || method == MinMaxMethod::SoundValueIteration || method == MinMaxMethod::ViToPi) {
if (method == MinMaxMethod::ValueIteration || method == MinMaxMethod::PolicyIteration || method == MinMaxMethod::RationalSearch || method == MinMaxMethod::IntervalIteration || method == MinMaxMethod::SoundValueIteration || method == MinMaxMethod::OptimisticValueIteration || method == MinMaxMethod::ViToPi) {
result = std::make_unique<IterativeMinMaxLinearEquationSolver<ValueType>>(std::make_unique<GeneralLinearEquationSolverFactory<ValueType>>());
} else if (method == MinMaxMethod::Topological) {
result = std::make_unique<TopologicalMinMaxLinearEquationSolver<ValueType>>();
@ -222,7 +222,7 @@ namespace storm {
std::unique_ptr<MinMaxLinearEquationSolver<storm::RationalNumber>> GeneralMinMaxLinearEquationSolverFactory<storm::RationalNumber>::create(Environment const& env) const {
std::unique_ptr<MinMaxLinearEquationSolver<storm::RationalNumber>> result;
auto method = env.solver().minMax().getMethod();
if (method == MinMaxMethod::ValueIteration || method == MinMaxMethod::PolicyIteration || method == MinMaxMethod::RationalSearch || method == MinMaxMethod::IntervalIteration || method == MinMaxMethod::SoundValueIteration || method == MinMaxMethod::ViToPi) {
if (method == MinMaxMethod::ValueIteration || method == MinMaxMethod::PolicyIteration || method == MinMaxMethod::RationalSearch || method == MinMaxMethod::IntervalIteration || method == MinMaxMethod::SoundValueIteration || method == MinMaxMethod::OptimisticValueIteration || method == MinMaxMethod::ViToPi) {
result = std::make_unique<IterativeMinMaxLinearEquationSolver<storm::RationalNumber>>(std::make_unique<GeneralLinearEquationSolverFactory<storm::RationalNumber>>());
} else if (method == MinMaxMethod::LinearProgramming) {
result = std::make_unique<LpMinMaxLinearEquationSolver<storm::RationalNumber>>(std::make_unique<storm::utility::solver::LpSolverFactory<storm::RationalNumber>>());

78
src/storm/solver/NativeLinearEquationSolver.cpp

@ -10,6 +10,7 @@
#include "storm/utility/constants.h"
#include "storm/utility/vector.h"
#include "storm/solver/helper/SoundValueIterationHelper.h"
#include "storm/solver/helper/OptimisticValueIterationHelper.h"
#include "storm/solver/Multiplier.h"
#include "storm/exceptions/InvalidStateException.h"
#include "storm/exceptions/InvalidEnvironmentException.h"
@ -623,6 +624,69 @@ namespace storm {
return converged;
}
template<typename ValueType>
bool NativeLinearEquationSolver<ValueType>::solveEquationsOptimisticValueIteration(Environment const& env, std::vector<ValueType>& x, std::vector<ValueType> const& b) const {
if (!this->multiplier) {
this->multiplier = storm::solver::MultiplierFactory<ValueType>().create(env, *this->A);
}
if (!this->cachedRowVector) {
this->cachedRowVector = std::make_unique<std::vector<ValueType>>(this->A->getRowCount());
}
if (!this->cachedRowVector2) {
this->cachedRowVector2 = std::make_unique<std::vector<ValueType>>(this->A->getRowCount());
}
// By default, we can not provide any guarantee
SolverGuarantee guarantee = SolverGuarantee::None;
// Get handle to multiplier.
storm::solver::Multiplier<ValueType> const &multiplier = *this->multiplier;
// Allow aliased multiplications.
storm::solver::MultiplicationStyle multiplicationStyle = env.solver().native().getPowerMethodMultiplicationStyle();
bool useGaussSeidelMultiplication = multiplicationStyle == storm::solver::MultiplicationStyle::GaussSeidel;
boost::optional<storm::storage::BitVector> relevantValues;
if (this->hasRelevantValues()) {
relevantValues = this->getRelevantValues();
}
// x has to start with a lower bound.
this->createLowerBoundsVector(x);
std::vector<ValueType>* lowerX = &x;
std::vector<ValueType>* upperX = this->cachedRowVector.get();
std::vector<ValueType>* auxVector = this->cachedRowVector2.get();
this->startMeasureProgress();
auto statusIters = storm::solver::helper::solveEquationsOptimisticValueIteration(env, lowerX, upperX, auxVector,
[&] (std::vector<ValueType>*& y, std::vector<ValueType>*& yPrime, ValueType const& precision, bool const& relative, uint64_t const& i, uint64_t const& maxI) {
this->showProgressIterative(i);
return performPowerIteration(env, y, yPrime, b, precision, relative, guarantee, i, maxI, multiplicationStyle);
},
[&] (std::vector<ValueType>* y, std::vector<ValueType>* yPrime, uint64_t const& i) {
this->showProgressIterative(i);
if (useGaussSeidelMultiplication) {
// Copy over the current vectors so we can modify them in-place.
// This is necessary as we want to compare the new values with the current ones.
*yPrime = *y;
multiplier.multiplyGaussSeidel(env, *y, &b);
} else {
multiplier.multiply(env, *y, &b, *yPrime);
std::swap(y, yPrime);
}
}, relevantValues);
auto two = storm::utility::convertNumber<ValueType>(2.0);
storm::utility::vector::applyPointwise<ValueType, ValueType, ValueType>(*lowerX, *upperX, x, [&two] (ValueType const& a, ValueType const& b) -> ValueType { return (a + b) / two; });
this->logIterations(statusIters.first == SolverStatus::Converged, statusIters.first == SolverStatus::TerminatedEarly, statusIters.second);
if (!this->isCachingEnabled()) {
clearCache();
}
return statusIters.first == SolverStatus::Converged || statusIters.first == SolverStatus::TerminatedEarly;
}
template<typename ValueType>
bool NativeLinearEquationSolver<ValueType>::solveEquationsRationalSearch(Environment const& env, std::vector<ValueType>& x, std::vector<ValueType> const& b) const {
return solveEquationsRationalSearchHelper<double>(env, x, b);
@ -908,7 +972,7 @@ namespace storm {
} else {
STORM_LOG_WARN("The selected solution method does not guarantee exact results.");
}
} else if (env.solver().isForceSoundness() && method != NativeLinearEquationSolverMethod::SoundValueIteration && method != NativeLinearEquationSolverMethod::IntervalIteration && method != NativeLinearEquationSolverMethod::RationalSearch) {
} 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;
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.");
@ -922,7 +986,7 @@ namespace storm {
template<typename ValueType>
bool NativeLinearEquationSolver<ValueType>::internalSolveEquations(Environment const& env, std::vector<ValueType>& x, std::vector<ValueType> const& b) const {
switch(getMethod(env, storm::NumberTraits<ValueType>::IsExact)) {
switch(getMethod(env, storm::NumberTraits<ValueType>::IsExact || env.solver().isForceExact())) {
case NativeLinearEquationSolverMethod::SOR:
return this->solveEquationsSOR(env, x, b, storm::utility::convertNumber<ValueType>(env.solver().native().getSorOmega()));
case NativeLinearEquationSolverMethod::GaussSeidel:
@ -935,6 +999,8 @@ namespace storm {
return this->solveEquationsPower(env, x, b);
case NativeLinearEquationSolverMethod::SoundValueIteration:
return this->solveEquationsSoundValueIteration(env, x, b);
case NativeLinearEquationSolverMethod::OptimisticValueIteration:
return this->solveEquationsOptimisticValueIteration(env, x, b);
case NativeLinearEquationSolverMethod::IntervalIteration:
return this->solveEquationsIntervalIteration(env, x, b);
case NativeLinearEquationSolverMethod::RationalSearch:
@ -946,8 +1012,8 @@ namespace storm {
template<typename ValueType>
LinearEquationSolverProblemFormat NativeLinearEquationSolver<ValueType>::getEquationProblemFormat(Environment const& env) const {
auto method = getMethod(env, storm::NumberTraits<ValueType>::IsExact);
if (method == NativeLinearEquationSolverMethod::Power || method == NativeLinearEquationSolverMethod::SoundValueIteration || method == NativeLinearEquationSolverMethod::RationalSearch || method == NativeLinearEquationSolverMethod::IntervalIteration) {
auto method = getMethod(env, storm::NumberTraits<ValueType>::IsExact || env.solver().isForceExact());
if (method == NativeLinearEquationSolverMethod::Power || method == NativeLinearEquationSolverMethod::SoundValueIteration || method == NativeLinearEquationSolverMethod::OptimisticValueIteration || method == NativeLinearEquationSolverMethod::RationalSearch || method == NativeLinearEquationSolverMethod::IntervalIteration) {
return LinearEquationSolverProblemFormat::FixedPointSystem;
} else {
return LinearEquationSolverProblemFormat::EquationSystem;
@ -957,10 +1023,10 @@ namespace storm {
template<typename ValueType>
LinearEquationSolverRequirements NativeLinearEquationSolver<ValueType>::getRequirements(Environment const& env) const {
LinearEquationSolverRequirements requirements;
auto method = getMethod(env, storm::NumberTraits<ValueType>::IsExact);
auto method = getMethod(env, storm::NumberTraits<ValueType>::IsExact || env.solver().isForceExact());
if (method == NativeLinearEquationSolverMethod::IntervalIteration) {
requirements.requireBounds();
} else if (method == NativeLinearEquationSolverMethod::RationalSearch) {
} else if (method == NativeLinearEquationSolverMethod::RationalSearch || method == NativeLinearEquationSolverMethod::OptimisticValueIteration) {
requirements.requireLowerBounds();
} else if (method == NativeLinearEquationSolverMethod::SoundValueIteration) {
requirements.requireBounds(false);

1
src/storm/solver/NativeLinearEquationSolver.h

@ -66,6 +66,7 @@ namespace storm {
virtual bool solveEquationsWalkerChae(storm::Environment const& env, std::vector<ValueType>& x, std::vector<ValueType> const& b) const;
virtual bool solveEquationsPower(storm::Environment const& env, std::vector<ValueType>& x, std::vector<ValueType> const& b) const;
virtual bool solveEquationsSoundValueIteration(storm::Environment const& env, std::vector<ValueType>& x, std::vector<ValueType> const& b) const;
virtual bool solveEquationsOptimisticValueIteration(storm::Environment const& env, std::vector<ValueType>& x, std::vector<ValueType> const& b) const;
virtual bool solveEquationsIntervalIteration(storm::Environment const& env, std::vector<ValueType>& x, std::vector<ValueType> const& b) const;
virtual bool solveEquationsRationalSearch(storm::Environment const& env, std::vector<ValueType>& x, std::vector<ValueType> const& b) const;

14
src/storm/solver/SolverSelectionOptions.cpp

@ -18,6 +18,8 @@ namespace storm {
return "intervaliteration";
case MinMaxMethod::SoundValueIteration:
return "soundvalueiteration";
case MinMaxMethod::OptimisticValueIteration:
return "optimisticvalueiteration";
case MinMaxMethod::TopologicalCuda:
return "topologicalcuda";
case MinMaxMethod::ViToPi:
@ -60,6 +62,16 @@ namespace storm {
return "invalid";
}
std::string toString(MaBoundedReachabilityMethod m) {
switch(m) {
case MaBoundedReachabilityMethod::Imca:
return "imca";
case MaBoundedReachabilityMethod::UnifPlus:
return "unifplus";
}
return "invalid";
}
std::string toString(LpSolverType t) {
switch(t) {
case LpSolverType::Gurobi:
@ -112,6 +124,8 @@ namespace storm {
return "Power";
case NativeLinearEquationSolverMethod::SoundValueIteration:
return "SoundValueIteration";
case NativeLinearEquationSolverMethod::OptimisticValueIteration:
return "optimisticvalueiteration";
case NativeLinearEquationSolverMethod::IntervalIteration:
return "IntervalIteration";
case NativeLinearEquationSolverMethod::RationalSearch:

5
src/storm/solver/SolverSelectionOptions.h

@ -6,16 +6,17 @@
namespace storm {
namespace solver {
ExtendEnumsWithSelectionField(MinMaxMethod, PolicyIteration, ValueIteration, LinearProgramming, Topological, RationalSearch, IntervalIteration, SoundValueIteration, TopologicalCuda, ViToPi)
ExtendEnumsWithSelectionField(MinMaxMethod, ValueIteration, PolicyIteration, LinearProgramming, Topological, RationalSearch, IntervalIteration, SoundValueIteration, OptimisticValueIteration, TopologicalCuda, ViToPi)
ExtendEnumsWithSelectionField(MultiplierType, Native, Gmmxx)
ExtendEnumsWithSelectionField(GameMethod, PolicyIteration, ValueIteration)
ExtendEnumsWithSelectionField(LraMethod, LinearProgramming, ValueIteration, GainBiasEquations, LraDistributionEquations)
ExtendEnumsWithSelectionField(MaBoundedReachabilityMethod, Imca, UnifPlus)
ExtendEnumsWithSelectionField(LpSolverType, Gurobi, Glpk, Z3)
ExtendEnumsWithSelectionField(EquationSolverType, Native, Gmmxx, Eigen, Elimination, Topological)
ExtendEnumsWithSelectionField(SmtSolverType, Z3, Mathsat)
ExtendEnumsWithSelectionField(NativeLinearEquationSolverMethod, Jacobi, GaussSeidel, SOR, WalkerChae, Power, SoundValueIteration, IntervalIteration, RationalSearch)
ExtendEnumsWithSelectionField(NativeLinearEquationSolverMethod, Jacobi, GaussSeidel, SOR, WalkerChae, Power, SoundValueIteration, OptimisticValueIteration, IntervalIteration, RationalSearch)
ExtendEnumsWithSelectionField(GmmxxLinearEquationSolverMethod, Bicgstab, Qmr, Gmres)
ExtendEnumsWithSelectionField(GmmxxLinearEquationSolverPreconditioner, Ilu, Diagonal, None)
ExtendEnumsWithSelectionField(EigenLinearEquationSolverMethod, SparseLU, Bicgstab, DGmres, Gmres)

Some files were not shown because too many files changed in this diff

|||||||
100:0
Loading…
Cancel
Save