Browse Source

Merge branch 'master' into rubicon

main
Sebastian Junges 5 years ago
parent
commit
11a893f3d2
  1. 5
      CHANGELOG.md
  2. 5
      CMakeLists.txt
  3. 9
      doc/checklist_new_release.md
  4. 22
      resources/3rdparty/CMakeLists.txt
  5. 7
      resources/3rdparty/sylvan/src/storm_wrapper.cpp
  6. 1
      resources/3rdparty/sylvan/src/sylvan_obj_mtbdd_storm.hpp
  7. 6
      resources/3rdparty/sylvan/src/sylvan_obj_storm.cpp
  8. 7
      src/CMakeLists.txt
  9. 40
      src/storm-cli-utilities/model-handling.h
  10. 66
      src/storm-pars/modelchecker/instantiation/SparseDtmcInstantiationModelChecker.cpp
  11. 82
      src/storm-pars/modelchecker/instantiation/SparseMdpInstantiationModelChecker.cpp
  12. 4
      src/storm-pars/storage/ParameterRegion.cpp
  13. 27
      src/storm/builder/DdPrismModelBuilder.cpp
  14. 5
      src/storm/environment/solver/OviSolverEnvironment.cpp
  15. 2
      src/storm/environment/solver/OviSolverEnvironment.h
  16. 8
      src/storm/modelchecker/CheckTask.h
  17. 2
      src/storm/modelchecker/helper/infinitehorizon/SparseDeterministicInfiniteHorizonHelper.cpp
  18. 10
      src/storm/modelchecker/helper/infinitehorizon/SparseInfiniteHorizonHelper.cpp
  19. 2
      src/storm/settings/modules/CoreSettings.cpp
  20. 9
      src/storm/settings/modules/OviSolverSettings.cpp
  21. 3
      src/storm/settings/modules/OviSolverSettings.h
  22. 5
      src/storm/solver/AbstractEquationSolver.cpp
  23. 1
      src/storm/solver/AbstractEquationSolver.h
  24. 63
      src/storm/solver/IterativeMinMaxLinearEquationSolver.cpp
  25. 2
      src/storm/solver/IterativeMinMaxLinearEquationSolver.h
  26. 57
      src/storm/solver/NativeLinearEquationSolver.cpp
  27. 2
      src/storm/solver/NativeLinearEquationSolver.h
  28. 13
      src/storm/solver/TopologicalLinearEquationSolver.cpp
  29. 11
      src/storm/solver/TopologicalMinMaxLinearEquationSolver.cpp
  30. 374
      src/storm/solver/helper/OptimisticValueIterationHelper.cpp
  31. 347
      src/storm/solver/helper/OptimisticValueIterationHelper.h
  32. 4
      src/storm/storage/dd/sylvan/InternalSylvanAdd.cpp
  33. 28
      src/storm/utility/AutomaticSettings.cpp
  34. 4
      src/storm/utility/AutomaticSettings.h
  35. 8
      src/storm/utility/Engine.cpp
  36. 2
      src/storm/utility/Engine.h
  37. 17
      src/test/storm/modelchecker/csl/LraCtmcCslModelCheckerTest.cpp
  38. 15
      src/test/storm/modelchecker/prctl/mdp/LraMdpPrctlModelCheckerTest.cpp
  39. 2
      version.cmake

5
CHANGELOG.md

@ -8,10 +8,11 @@ The releases of major and minor versions contain an overview of changes since th
Version 1.6.x
-------------
## Version 1.6.1 (??)
## Version 1.6.2 (2020/09)
- Prism program simplification improved.
- Revamped implementation of long-run-average algorithms, including scheduler export for LRA properties on Markov automata.
- Support for step-bounded properties of the form ... [F[x,y] ... ] for DTMCs and MDPs (sparse engine).
- Support for step-bounded properties of the form ... [F[x,y] ... ] for DTMCs and MDPs (sparse engine).
- Renamed portfolio engine to automatic
- `storm-dft`: Fix for relevant events when using symmetry reduction.
- `storm-pomdp`: Fix for --transformsimple and --transformbinary when used with until formulae.
- `storm-pomdp`: POMDPs can be parametric as well.

5
CMakeLists.txt

@ -61,6 +61,9 @@ export_option(STORM_USE_CLN_RF)
option(BUILD_SHARED_LIBS "Build the Storm library dynamically" OFF)
option(STORM_DEBUG_CUDD "Build CUDD in debug mode." OFF)
MARK_AS_ADVANCED(STORM_DEBUG_CUDD)
option(STORM_EXCLUDE_TESTS_FROM_ALL "If set, tests will not be compiled by default" OFF )
export_option(STORM_EXCLUDE_TESTS_FROM_ALL)
MARK_AS_ADVANCED(STORM_EXCLUDE_TESTS_FROM_ALL)
set(BOOST_ROOT "" CACHE STRING "A hint to the root directory of Boost (optional).")
set(GUROBI_ROOT "" CACHE STRING "A hint to the root directory of Gurobi (optional).")
set(Z3_ROOT "" CACHE STRING "A hint to the root directory of Z3 (optional).")
@ -445,7 +448,7 @@ set(STORM_VERSION_APPENDIX "${CMAKE_MATCH_5}") # might be empty
if (STORM_GIT_VERSION_STRING MATCHES "NOTFOUND")
set(STORM_VERSION_SOURCE "VersionSource::Static")
set(STORM_VERSION_COMMITS_AHEAD 0)
set(STORM_VERSION_DIRTY DirtyState:Unknown)
set(STORM_VERSION_DIRTY DirtyState::Unknown)
include(version.cmake)
message(WARNING "Storm - Git version information not available, statically assuming version ${STORM_VERSION_MAJOR}.${STORM_VERSION_MINOR}.${STORM_VERSION_PATCH}.")
else()

9
doc/checklist_new_release.md

@ -34,11 +34,18 @@ Note that in most cases a simultaneous release of [carl](https://github.com/smtr
6. [Add new release](https://github.com/moves-rwth/storm/releases/new) in GitHub.
7. Update `stable` branch:
```console
git checkout stable
git merge master
git rebase master
git push origin stable
```
Note: Rebasing might fail if `stable` is ahead of `master` (e.g. because of merge commits). In this case we can do:
```console
git checkout stable
git reset --hard master
git push --force origin stable
```
8. Update [Homebrew formula](https://github.com/moves-rwth/homebrew-storm).

22
resources/3rdparty/CMakeLists.txt

@ -260,14 +260,26 @@ if (NOT STORM_FORCE_SHIPPED_CARL)
find_package(carl QUIET)
endif()
endif()
set(STORM_SHIPPED_CARL OFF)
if(carl_FOUND AND NOT STORM_FORCE_SHIPPED_CARL)
get_target_property(carlLOCATION lib_carl LOCATION)
if("${carlLOCATION}" STREQUAL "carlLOCATION-NOTFOUND")
message(FATAL_ERROR "Library location for carl is not found, did you build carl?")
if(EXISTS ${STORM_3RDPARTY_BINARY_DIR}/carl)
message(WARNING "Storm - Library for carl location is not found but the directory ${STORM_3RDPARTY_BINARY_DIR}/carl exists. Will (re-)try to build a shipped version of carl.")
set(STORM_SHIPPED_CARL ON)
else()
message(FATAL_ERROR "Library location for carl is not found, did you build carl?")
endif()
elseif(EXISTS ${carlLOCATION})
#empty on purpose
else()
message(FATAL_ERROR "File ${carlLOCATION} does not exist, did you build carl?")
if(EXISTS ${STORM_3RDPARTY_BINARY_DIR}/carl)
message(WARNING "Storm - File ${carlLOCATION} does not exist but the directory ${STORM_3RDPARTY_BINARY_DIR}/carl exists. Will (re-)try to build a shipped version of carl.")
set(STORM_SHIPPED_CARL ON)
else()
message(FATAL_ERROR "File ${carlLOCATION} does not exist, did you build carl?")
endif()
endif()
if("${carl_VERSION_MAJOR}" STREQUAL "${CARL_C14VERSION}")
message(STATUS "Storm - Found carl using master14 branch.")
@ -276,14 +288,16 @@ if(carl_FOUND AND NOT STORM_FORCE_SHIPPED_CARL)
message(FATAL_ERROR "Carl outdated, require ${CARL_MINVERSION}, have ${carl_VERSION}")
endif()
set(STORM_SHIPPED_CARL OFF)
set(STORM_HAVE_CARL ON)
message(STATUS "Storm - Use system version of carl.")
message(STATUS "Storm - Linking with preinstalled carl ${carl_VERSION} (include: ${carl_INCLUDE_DIR}, library ${carl_LIBRARIES}, CARL_USE_CLN_NUMBERS: ${CARL_USE_CLN_NUMBERS}, CARL_USE_GINAC: ${CARL_USE_GINAC}).")
set(STORM_HAVE_CLN ${CARL_USE_CLN_NUMBERS})
set(STORM_HAVE_GINAC ${CARL_USE_GINAC})
else()
set(STORM_SHIPPED_CARL ON)
set(STORM_SHIPPED_CARL ON)
endif()
if (STORM_SHIPPED_CARL)
# The first external project will be built at *configure stage*
message(STATUS "Carl - Start of config process")
file(MAKE_DIRECTORY ${STORM_3RDPARTY_BINARY_DIR}/carl_download)

7
resources/3rdparty/sylvan/src/storm_wrapper.cpp

@ -227,8 +227,11 @@ storm_rational_number_ptr storm_rational_number_mod(storm_rational_number_ptr a,
storm::RationalNumber const& srn_a = *(storm::RationalNumber const*)a;
storm::RationalNumber const& srn_b = *(storm::RationalNumber const*)b;
throw storm::exceptions::InvalidOperationException() << "Modulo not supported for rational numbers.";
if (carl::isInteger(srn_a) && carl::isInteger(srn_b)) {
storm::RationalNumber* result_srn = new storm::RationalNumber(carl::mod(carl::getNum(srn_a), carl::getNum(srn_b)));
return (storm_rational_number_ptr)result_srn;
}
throw storm::exceptions::InvalidOperationException() << "Modulo not supported for rational, non-integer numbers.";
}
storm_rational_number_ptr storm_rational_number_min(storm_rational_number_ptr a, storm_rational_number_ptr b) {

1
resources/3rdparty/sylvan/src/sylvan_obj_mtbdd_storm.hpp

@ -53,6 +53,7 @@ Mtbdd DivideRN(const Mtbdd &other) const;
Mtbdd FloorRN() const;
Mtbdd CeilRN() const;
Mtbdd PowRN(const Mtbdd& other) const;
Mtbdd ModRN(const Mtbdd& other) const;
Mtbdd MinimumRN() const;
Mtbdd MaximumRN() const;

6
resources/3rdparty/sylvan/src/sylvan_obj_storm.cpp

@ -315,6 +315,12 @@ Mtbdd::PowRN(const Mtbdd& other) const {
return sylvan_storm_rational_number_pow(mtbdd, other.mtbdd);
}
Mtbdd
Mtbdd::ModRN(const Mtbdd& other) const {
LACE_ME;
return sylvan_storm_rational_number_mod(mtbdd, other.mtbdd);
}
Mtbdd
Mtbdd::MinimumRN() const {
LACE_ME;

7
src/CMakeLists.txt

@ -23,7 +23,10 @@ add_subdirectory(storm-pomdp-cli)
add_subdirectory(storm-conv)
add_subdirectory(storm-conv-cli)
add_subdirectory(test)
if (STORM_EXCLUDE_TESTS_FROM_ALL)
add_subdirectory(test EXCLUDE_FROM_ALL)
else()
add_subdirectory(test)
endif()
set(STORM_TARGETS ${STORM_TARGETS} PARENT_SCOPE)

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

@ -10,7 +10,7 @@
#include "storm/utility/macros.h"
#include "storm/utility/NumberTraits.h"
#include "storm/utility/Engine.h"
#include "storm/utility/Portfolio.h"
#include "storm/utility/AutomaticSettings.h"
#include "storm/utility/initialize.h"
#include "storm/utility/Stopwatch.h"
@ -178,30 +178,30 @@ namespace storm {
bool isCompatible;
};
void getModelProcessingInformationPortfolio(SymbolicInput const& input, ModelProcessingInformation& mpi) {
void getModelProcessingInformationAutomatic(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 JANI input model.");
STORM_LOG_THROW(input.model->isJaniModel(), storm::exceptions::InvalidArgumentException, "Portfolio engine requires a JANI input model.");
STORM_LOG_THROW(input.model.is_initialized(), storm::exceptions::InvalidArgumentException, "Automatic engine requires a JANI input model.");
STORM_LOG_THROW(input.model->isJaniModel(), storm::exceptions::InvalidArgumentException, "Automatic engine requires a JANI input model.");
std::vector<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_LOG_THROW(!properties.empty(), storm::exceptions::InvalidArgumentException, "Automatic engine requires a property.");
STORM_LOG_WARN_COND(properties.size() == 1, "Automatic engine does not support decisions based on multiple properties. Only the first property will be considered.");
storm::utility::Portfolio pf;
storm::utility::AutomaticSettings as;
if (hints.isNumberStatesSet()) {
pf.predict(input.model->asJaniModel(), properties.front(), hints.getNumberStates());
as.predict(input.model->asJaniModel(), properties.front(), hints.getNumberStates());
} else {
pf.predict(input.model->asJaniModel(), properties.front());
as.predict(input.model->asJaniModel(), properties.front());
}
mpi.engine = pf.getEngine();
if (pf.enableBisimulation()) {
mpi.engine = as.getEngine();
if (as.enableBisimulation()) {
mpi.applyBisimulation = true;
}
if (pf.enableExact() && mpi.verificationValueType == ModelProcessingInformation::ValueType::FinitePrecision) {
if (as.enableExact() && mpi.verificationValueType == ModelProcessingInformation::ValueType::FinitePrecision) {
mpi.verificationValueType = ModelProcessingInformation::ValueType::Exact;
}
STORM_PRINT_AND_LOG( "Portfolio engine picked the following settings: " << std::endl
STORM_PRINT_AND_LOG( "Automatic engine picked the following settings: " << std::endl
<< "\tengine=" << mpi.engine
<< std::boolalpha
<< "\t bisimulation=" << mpi.applyBisimulation
@ -237,12 +237,12 @@ namespace storm {
}
auto originalVerificationValueType = mpi.verificationValueType;
// Since the remaining settings could depend on the ones above, we need apply the portfolio engine now.
bool usePortfolio = input.model.is_initialized() && mpi.engine == storm::utility::Engine::Portfolio;
if (usePortfolio) {
// Since the remaining settings could depend on the ones above, we need apply the automatic engine now.
bool useAutomatic = input.model.is_initialized() && mpi.engine == storm::utility::Engine::Automatic;
if (useAutomatic) {
if (input.model->isJaniModel()) {
// This can potentially overwrite the settings above, but will not overwrite settings that were explicitly set by the user (e.g. we will not disable bisimulation or disable exact arithmetic)
getModelProcessingInformationPortfolio(input, mpi);
getModelProcessingInformationAutomatic(input, mpi);
} else {
// Transform Prism to jani first
STORM_LOG_ASSERT(input.model->isPrismProgram(), "Unexpected type of input.");
@ -255,7 +255,7 @@ namespace storm {
janiInput.preprocessedProperties = std::move(modelAndProperties.second);
}
// This can potentially overwrite the settings above, but will not overwrite settings that were explicitly set by the user (e.g. we will not disable bisimulation or disable exact arithmetic)
getModelProcessingInformationPortfolio(janiInput, mpi);
getModelProcessingInformationAutomatic(janiInput, mpi);
if (transformedJaniInput) {
// We cache the transformation result.
*transformedJaniInput = std::move(janiInput);
@ -279,9 +279,9 @@ namespace storm {
};
mpi.isCompatible = checkCompatibleSettings();
if (!mpi.isCompatible) {
if (usePortfolio) {
if (useAutomatic) {
bool useExact = mpi.verificationValueType != ModelProcessingInformation::ValueType::FinitePrecision;
STORM_LOG_WARN("The settings picked by the portfolio engine (engine=" << mpi.engine << ", bisim=" << mpi.applyBisimulation << ", exact=" << useExact << ") are incompatible with this model. Falling back to default settings.");
STORM_LOG_WARN("The settings picked by the automatic engine (engine=" << mpi.engine << ", bisim=" << mpi.applyBisimulation << ", exact=" << useExact << ") are incompatible with this model. Falling back to default settings.");
mpi.engine = storm::utility::Engine::Sparse;
mpi.applyBisimulation = false;
mpi.verificationValueType = originalVerificationValueType;

66
src/storm-pars/modelchecker/instantiation/SparseDtmcInstantiationModelChecker.cpp

@ -42,8 +42,29 @@ namespace storm {
this->currentCheckTask->setHint(std::make_shared<ExplicitModelCheckerHint<ConstantType>>());
}
ExplicitModelCheckerHint<ConstantType>& hint = this->currentCheckTask->getHint().template asExplicitModelCheckerHint<ConstantType>();
std::unique_ptr<CheckResult> result;
if (this->getInstantiationsAreGraphPreserving() && !hint.hasMaybeStates()) {
// Perform purely qualitative analysis once
std::vector<ConstantType> qualitativeResult;
if (this->currentCheckTask->getFormula().asOperatorFormula().hasQuantitativeResult()) {
auto newCheckTask = *this->currentCheckTask;
newCheckTask.setQualitative(true);
newCheckTask.setOnlyInitialStatesRelevant(false);
qualitativeResult = modelChecker.check(env, newCheckTask)->template asExplicitQuantitativeCheckResult<ConstantType>().getValueVector();
} else {
auto newCheckTask = this->currentCheckTask->substituteFormula(this->currentCheckTask->getFormula().asOperatorFormula().getSubformula());
newCheckTask.setQualitative(true);
newCheckTask.setOnlyInitialStatesRelevant(false);
qualitativeResult = modelChecker.computeProbabilities(env, newCheckTask)->template asExplicitQuantitativeCheckResult<ConstantType>().getValueVector();
}
storm::storage::BitVector maybeStates = storm::utility::vector::filter<ConstantType>(qualitativeResult,
[] (ConstantType const& value) -> bool { return !(storm::utility::isZero<ConstantType>(value) || storm::utility::isOne<ConstantType>(value)); });
hint.setMaybeStates(std::move(maybeStates));
hint.setResultHint(std::move(qualitativeResult));
hint.setComputeOnlyMaybeStates(true);
}
std::unique_ptr<CheckResult> result;
// Check the formula and store the result as a hint for the next call.
// For qualitative properties, we still want a quantitative result hint. Hence we perform the check on the subformula
if (this->currentCheckTask->getFormula().asOperatorFormula().hasQuantitativeResult()) {
@ -56,15 +77,6 @@ namespace storm {
hint.setResultHint(std::move(quantitativeResult->template asExplicitQuantitativeCheckResult<ConstantType>().getValueVector()));
}
if (this->getInstantiationsAreGraphPreserving() && !hint.hasMaybeStates()) {
// Extract the maybe states from the current result.
assert(hint.hasResultHint());
storm::storage::BitVector maybeStates = ~storm::utility::vector::filter<ConstantType>(hint.getResultHint(),
[] (ConstantType const& value) -> bool { return storm::utility::isZero<ConstantType>(value) || storm::utility::isOne<ConstantType>(value); });
hint.setMaybeStates(std::move(maybeStates));
hint.setComputeOnlyMaybeStates(true);
}
return result;
}
@ -75,6 +87,28 @@ namespace storm {
this->currentCheckTask->setHint(std::make_shared<ExplicitModelCheckerHint<ConstantType>>());
}
ExplicitModelCheckerHint<ConstantType>& hint = this->currentCheckTask->getHint().template asExplicitModelCheckerHint<ConstantType>();
if (this->getInstantiationsAreGraphPreserving() && !hint.hasMaybeStates()) {
// Perform purely qualitative analysis once
std::vector<ConstantType> qualitativeResult;
if (this->currentCheckTask->getFormula().asOperatorFormula().hasQuantitativeResult()) {
auto newCheckTask = *this->currentCheckTask;
newCheckTask.setQualitative(true);
newCheckTask.setOnlyInitialStatesRelevant(false);
qualitativeResult = modelChecker.check(env, newCheckTask)->template asExplicitQuantitativeCheckResult<ConstantType>().getValueVector();
} else {
auto newCheckTask = this->currentCheckTask->substituteFormula(this->currentCheckTask->getFormula().asOperatorFormula().getSubformula());
newCheckTask.setQualitative(true);
newCheckTask.setOnlyInitialStatesRelevant(false);
qualitativeResult = modelChecker.computeRewards(env, this->currentCheckTask->getFormula().asRewardOperatorFormula().getMeasureType(), newCheckTask)->template asExplicitQuantitativeCheckResult<ConstantType>().getValueVector();
}
storm::storage::BitVector maybeStates = storm::utility::vector::filter<ConstantType>(qualitativeResult,
[] (ConstantType const& value) -> bool { return !(storm::utility::isZero<ConstantType>(value) || storm::utility::isInfinity<ConstantType>(value)); });
hint.setMaybeStates(std::move(maybeStates));
hint.setResultHint(std::move(qualitativeResult));
hint.setComputeOnlyMaybeStates(true);
}
std::unique_ptr<CheckResult> result;
// Check the formula and store the result as a hint for the next call.
@ -89,18 +123,6 @@ namespace storm {
this->currentCheckTask->getHint().template asExplicitModelCheckerHint<ConstantType>().setResultHint(std::move(quantitativeResult->template asExplicitQuantitativeCheckResult<ConstantType>().getValueVector()));
}
if (this->getInstantiationsAreGraphPreserving() && !hint.hasMaybeStates()) {
// Extract the maybe states from the current result.
assert(hint.hasResultHint());
storm::storage::BitVector maybeStates = ~storm::utility::vector::filterInfinity(hint.getResultHint());
// We need to exclude the target states from the maybe states.
// Note that we can not consider the states with reward zero since a valuation might set a reward to zero
std::unique_ptr<CheckResult> subFormulaResult = modelChecker.check(env, this->currentCheckTask->getFormula().asOperatorFormula().getSubformula().asEventuallyFormula().getSubformula());
maybeStates = maybeStates & ~(subFormulaResult->asExplicitQualitativeCheckResult().getTruthValuesVector());
hint.setMaybeStates(std::move(maybeStates));
hint.setComputeOnlyMaybeStates(true);
}
return result;
}

82
src/storm-pars/modelchecker/instantiation/SparseMdpInstantiationModelChecker.cpp

@ -47,8 +47,31 @@ namespace storm {
this->currentCheckTask->setHint(std::make_shared<ExplicitModelCheckerHint<ConstantType>>());
}
ExplicitModelCheckerHint<ConstantType>& hint = this->currentCheckTask->getHint().template asExplicitModelCheckerHint<ConstantType>();
std::unique_ptr<CheckResult> result;
if (this->getInstantiationsAreGraphPreserving() && !hint.hasMaybeStates()) {
// Perform purely qualitative analysis once
std::vector<ConstantType> qualitativeResult;
if (this->currentCheckTask->getFormula().asOperatorFormula().hasQuantitativeResult()) {
auto newCheckTask = *this->currentCheckTask;
newCheckTask.setQualitative(true);
newCheckTask.setOnlyInitialStatesRelevant(false);
newCheckTask.setProduceSchedulers(false);
qualitativeResult = modelChecker.check(env, newCheckTask)->template asExplicitQuantitativeCheckResult<ConstantType>().getValueVector();
} else {
auto newCheckTask = this->currentCheckTask->substituteFormula(this->currentCheckTask->getFormula().asOperatorFormula().getSubformula());
newCheckTask.setQualitative(true);
newCheckTask.setOnlyInitialStatesRelevant(false);
newCheckTask.setProduceSchedulers(false);
qualitativeResult = modelChecker.computeProbabilities(env, newCheckTask)->template asExplicitQuantitativeCheckResult<ConstantType>().getValueVector();
}
storm::storage::BitVector maybeStates = storm::utility::vector::filter<ConstantType>(qualitativeResult,
[] (ConstantType const& value) -> bool { return !(storm::utility::isZero<ConstantType>(value) || storm::utility::isOne<ConstantType>(value)); });
hint.setMaybeStates(std::move(maybeStates));
hint.setResultHint(std::move(qualitativeResult));
hint.setComputeOnlyMaybeStates(true);
}
std::unique_ptr<CheckResult> result;
// Check the formula and store the result as a hint for the next call.
// For qualitative properties, we still want a quantitative result hint. Hence we perform the check on the subformula
if (this->currentCheckTask->getFormula().asOperatorFormula().hasQuantitativeResult()) {
@ -65,21 +88,6 @@ namespace storm {
hint.setSchedulerHint(std::move(dynamic_cast<storm::storage::Scheduler<ConstantType>&>(scheduler)));
}
if (this->getInstantiationsAreGraphPreserving() && !hint.hasMaybeStates()) {
// Extract the maybe states from the current result.
assert(hint.hasResultHint());
storm::storage::BitVector maybeStates = ~storm::utility::vector::filter<ConstantType>(hint.getResultHint(),
[] (ConstantType const& value) -> bool { return storm::utility::isZero<ConstantType>(value) || storm::utility::isOne<ConstantType>(value); });
hint.setMaybeStates(std::move(maybeStates));
hint.setComputeOnlyMaybeStates(true);
// Check if there can be end components within the maybestates
if (storm::solver::minimize(this->currentCheckTask->getOptimizationDirection()) ||
storm::utility::graph::performProb1A(instantiatedModel.getTransitionMatrix(), instantiatedModel.getTransitionMatrix().getRowGroupIndices(), instantiatedModel.getBackwardTransitions(), hint.getMaybeStates(), ~hint.getMaybeStates()).full()) {
hint.setNoEndComponentsInMaybeStates(true);
}
}
return result;
}
@ -92,8 +100,31 @@ namespace storm {
this->currentCheckTask->setHint(std::make_shared<ExplicitModelCheckerHint<ConstantType>>());
}
ExplicitModelCheckerHint<ConstantType>& hint = this->currentCheckTask->getHint().template asExplicitModelCheckerHint<ConstantType>();
std::unique_ptr<CheckResult> result;
if (this->getInstantiationsAreGraphPreserving() && !hint.hasMaybeStates()) {
// Perform purely qualitative analysis once
std::vector<ConstantType> qualitativeResult;
if (this->currentCheckTask->getFormula().asOperatorFormula().hasQuantitativeResult()) {
auto newCheckTask = *this->currentCheckTask;
newCheckTask.setQualitative(true);
newCheckTask.setOnlyInitialStatesRelevant(false);
newCheckTask.setProduceSchedulers(false);
qualitativeResult = modelChecker.check(env, newCheckTask)->template asExplicitQuantitativeCheckResult<ConstantType>().getValueVector();
} else {
auto newCheckTask = this->currentCheckTask->substituteFormula(this->currentCheckTask->getFormula().asOperatorFormula().getSubformula());
newCheckTask.setQualitative(true);
newCheckTask.setOnlyInitialStatesRelevant(false);
newCheckTask.setProduceSchedulers(false);
qualitativeResult = modelChecker.computeRewards(env, this->currentCheckTask->getFormula().asRewardOperatorFormula().getMeasureType(), newCheckTask)->template asExplicitQuantitativeCheckResult<ConstantType>().getValueVector();
}
storm::storage::BitVector maybeStates = storm::utility::vector::filter<ConstantType>(qualitativeResult,
[] (ConstantType const& value) -> bool { return !(storm::utility::isZero<ConstantType>(value) || storm::utility::isInfinity<ConstantType>(value)); });
hint.setMaybeStates(std::move(maybeStates));
hint.setResultHint(std::move(qualitativeResult));
hint.setComputeOnlyMaybeStates(true);
}
std::unique_ptr<CheckResult> result;
// Check the formula and store the result as a hint for the next call.
// For qualitative properties, we still want a quantitative result hint. Hence we perform the check on the subformula
if(this->currentCheckTask->getFormula().asOperatorFormula().hasQuantitativeResult()) {
@ -110,23 +141,6 @@ namespace storm {
hint.setSchedulerHint(std::move(dynamic_cast<storm::storage::Scheduler<ConstantType>&>(scheduler)));
}
if (this->getInstantiationsAreGraphPreserving() && !hint.hasMaybeStates()) {
// Extract the maybe states from the current result.
assert(hint.hasResultHint());
storm::storage::BitVector maybeStates = ~storm::utility::vector::filterInfinity(hint.getResultHint());
// We need to exclude the target states from the maybe states.
// Note that we can not consider the states with reward zero since a valuation might set a reward to zero
std::unique_ptr<CheckResult> subFormulaResult = modelChecker.check(env, this->currentCheckTask->getFormula().asOperatorFormula().getSubformula().asEventuallyFormula().getSubformula());
maybeStates = maybeStates & ~(subFormulaResult->asExplicitQualitativeCheckResult().getTruthValuesVector());
hint.setMaybeStates(std::move(maybeStates));
hint.setComputeOnlyMaybeStates(true);
// Check if there can be end components within the maybestates
if (storm::solver::maximize(this->currentCheckTask->getOptimizationDirection()) ||
storm::utility::graph::performProb1A(instantiatedModel.getTransitionMatrix(), instantiatedModel.getTransitionMatrix().getRowGroupIndices(), instantiatedModel.getBackwardTransitions(), hint.getMaybeStates(), ~hint.getMaybeStates()).full()) {
hint.setNoEndComponentsInMaybeStates(true);
}
}
return result;
}

4
src/storm-pars/storage/ParameterRegion.cpp

@ -1,7 +1,10 @@
#include "storm-pars/storage/ParameterRegion.h"
#include <limits>
#include "storm/utility/macros.h"
#include "storm/exceptions/InvalidArgumentException.h"
#include "storm/exceptions/OutOfRangeException.h"
#include "storm/utility/constants.h"
namespace storm {
@ -88,6 +91,7 @@ namespace storm {
template<typename ParametricType>
std::vector<typename ParameterRegion<ParametricType>::Valuation> ParameterRegion<ParametricType>::getVerticesOfRegion(std::set<VariableType> const& consideredVariables) const {
std::size_t const numOfVariables = consideredVariables.size();
STORM_LOG_THROW(numOfVariables <= std::numeric_limits<std::size_t>::digits, storm::exceptions::OutOfRangeException, "Number of variables " << numOfVariables << " is too high.");
std::size_t const numOfVertices = std::pow(2, numOfVariables);
std::vector<Valuation> resultingVector(numOfVertices);

27
src/storm/builder/DdPrismModelBuilder.cpp

@ -1166,14 +1166,14 @@ namespace storm {
}
template <storm::dd::DdType Type, typename ValueType>
void checkRewards(storm::dd::Add<Type, ValueType> const& rewards) {
STORM_LOG_WARN_COND(rewards.getMin() >= 0, "The reward model assigns negative rewards to some states.");
STORM_LOG_WARN_COND(!rewards.isZero(), "The reward model does not assign any non-zero rewards.");
void checkRewards(storm::dd::Add<Type, ValueType> const& rewards, std::string const& rewardType) {
STORM_LOG_WARN_COND(rewards.getMin() >= 0, "The reward model assigns negative " << rewardType << " to some states.");
STORM_LOG_WARN_COND(!rewards.isZero(), "The reward model declares " << rewardType << " but does not assign any non-zero values.");
}
template <storm::dd::DdType Type>
void checkRewards(storm::dd::Add<Type, storm::RationalFunction> const& rewards) {
STORM_LOG_WARN_COND(!rewards.isZero(), "The reward model does not assign any non-zero rewards.");
void checkRewards(storm::dd::Add<Type, storm::RationalFunction> const& rewards, std::string const& rewardType) {
STORM_LOG_WARN_COND(!rewards.isZero(), "The reward model declares " << rewardType << " but does not assign any non-zero values.");
}
template <storm::dd::DdType Type, typename ValueType>
@ -1191,12 +1191,11 @@ namespace storm {
// Restrict the rewards to those states that satisfy the condition.
rewards = reachableStatesAdd * states * rewards;
// Perform some sanity checks.
checkRewards(rewards);
// Add the rewards to the global state reward vector.
stateRewards.get() += rewards;
}
// Perform some sanity checks.
checkRewards(stateRewards.get(), "state rewards");
}
// Next, build the state-action reward vector.
@ -1228,9 +1227,6 @@ namespace storm {
stateActionRewardDd *= actionDd.transitionsDd.sumAbstract(generationInfo.columnMetaVariables);
}
// Perform some sanity checks.
checkRewards(stateActionRewardDd);
// Add the rewards to the global transition reward matrix.
stateActionRewards.get() += stateActionRewardDd;
}
@ -1243,6 +1239,9 @@ namespace storm {
stateActionRewards.get() /= stateActionDd.get();
}
// Perform some sanity checks.
checkRewards(stateActionRewards.get(), "action rewards");
}
// Then build the transition reward matrix.
@ -1279,12 +1278,12 @@ namespace storm {
transitionRewardDd = transitions.notZero().template toAdd<ValueType>() * transitionRewardDd;
}
// Perform some sanity checks.
checkRewards(transitionRewardDd);
// Add the rewards to the global transition reward matrix.
transitionRewards.get() += transitionRewardDd;
}
// Perform some sanity checks.
checkRewards(transitionRewards.get(), "transition rewards");
// Scale transition rewards for DTMCs.
if (generationInfo.program.getModelType() == storm::prism::Program::ModelType::DTMC) {

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

@ -11,7 +11,6 @@ namespace storm {
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();
noTerminationGuaranteeMinimumMethod = oviSettings.useNoTerminationGuaranteeMinimumMethod();
@ -29,10 +28,6 @@ namespace storm {
return maxVerificationIterationFactor;
}
bool OviSolverEnvironment::useRelevantValuesForPrecisionUpdate() const {
return relevantValuesForPrecisionUpdate;
}
storm::RationalNumber OviSolverEnvironment::getUpperBoundGuessingFactor() const {
return upperBoundGuessingFactor;
}

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

@ -14,7 +14,6 @@ namespace storm {
storm::RationalNumber getPrecisionUpdateFactor() const;
storm::RationalNumber getMaxVerificationIterationFactor() const;
bool useRelevantValuesForPrecisionUpdate() const;
storm::RationalNumber getUpperBoundGuessingFactor() const;
uint64_t getUpperBoundOnlyIterations() const;
bool useNoTerminationGuaranteeMinimumMethod() const;
@ -22,7 +21,6 @@ namespace storm {
private:
storm::RationalNumber precisionUpdateFactor;
storm::RationalNumber maxVerificationIterationFactor;
bool relevantValuesForPrecisionUpdate;
storm::RationalNumber upperBoundGuessingFactor;
uint64_t upperBoundOnlyIterations;
bool noTerminationGuaranteeMinimumMethod;

8
src/storm/modelchecker/CheckTask.h

@ -207,6 +207,14 @@ namespace storm {
bool isQualitativeSet() const {
return qualitative;
}
/*!
* sets whether the computation only needs to be performed qualitatively, because the values will only
* be compared to 0/1.
*/
void setQualitative(bool value) {
qualitative = value;
}
/*!
* Sets whether to produce schedulers (if supported).

2
src/storm/modelchecker/helper/infinitehorizon/SparseDeterministicInfiniteHorizonHelper.cpp

@ -405,6 +405,8 @@ namespace storm {
solver->setBounds(*lowerUpperBounds.first, *lowerUpperBounds.second);
// Check solver requirements
auto requirements = solver->getRequirements(env);
requirements.clearUpperBounds();
requirements.clearLowerBounds();
STORM_LOG_THROW(!requirements.hasEnabledCriticalRequirement(), storm::exceptions::UnmetRequirementException, "Solver requirements " + requirements.getEnabledRequirementsAsString() + " not checked.");
sspValues.assign(sspMatrixVector.first.getRowCount(), (*lowerUpperBounds.first + *lowerUpperBounds.second) / storm::utility::convertNumber<ValueType,uint64_t>(2));
solver->solveEquations(env, sspValues, sspMatrixVector.second);

10
src/storm/modelchecker/helper/infinitehorizon/SparseInfiniteHorizonHelper.cpp

@ -17,6 +17,8 @@
#include "storm/utility/SignalHandler.h"
#include "storm/utility/solver.h"
#include "storm/utility/vector.h"
#include "storm/utility/Stopwatch.h"
#include "storm/utility/ProgressMeasurement.h"
#include "storm/environment/solver/LongRunAverageSolverEnvironment.h"
#include "storm/environment/solver/MinMaxSolverEnvironment.h"
@ -133,13 +135,21 @@ namespace storm {
createDecomposition();
// Compute the long-run average for all components in isolation.
// Set up some logging
std::string const componentString = (Nondeterministic ? std::string("Maximal end") : std::string("Bottom strongly connected")) + (_longRunComponentDecomposition->size() == 1 ? std::string(" component") : std::string(" components"));
storm::utility::ProgressMeasurement progress(componentString);
progress.setMaxCount( _longRunComponentDecomposition->size());
progress.startNewMeasurement(0);
STORM_LOG_INFO("Computing long run average values for " << _longRunComponentDecomposition->size() << " " << componentString << " individually...");
std::vector<ValueType> componentLraValues;
componentLraValues.reserve(_longRunComponentDecomposition->size());
for (auto const& c : *_longRunComponentDecomposition) {
componentLraValues.push_back(computeLraForComponent(underlyingSolverEnvironment, stateRewardsGetter, actionRewardsGetter, c));
progress.updateProgress(componentLraValues.size());
}
// Solve the resulting SSP where end components are collapsed into single auxiliary states
STORM_LOG_INFO("Solving stochastic shortest path problem.");
return buildAndSolveSsp(underlyingSolverEnvironment, componentLraValues);
}

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

@ -37,6 +37,8 @@ namespace storm {
for (auto e : storm::utility::getEngines()) {
engines.push_back(storm::utility::toString(e));
}
engines.push_back("portfolio"); // for backwards compatibility
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());

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

@ -14,7 +14,6 @@ namespace storm {
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";
const std::string OviSolverSettings::useNoTerminationGuaranteeMinimumMethodOptionName = "no-termination-guarantee";
@ -23,9 +22,7 @@ namespace storm {
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, 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(1.0).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());
@ -42,10 +39,6 @@ namespace storm {
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();
}

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

@ -19,8 +19,6 @@ namespace storm {
double getMaxVerificationIterationFactor() const;
bool useRelevantValuesForPrecisionUpdate() const;
double getUpperBoundGuessingFactor() const;
uint64_t getUpperBoundOnlyIterations() const;
@ -33,7 +31,6 @@ namespace storm {
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;
static const std::string useNoTerminationGuaranteeMinimumMethodOptionName;

5
src/storm/solver/AbstractEquationSolver.cpp

@ -66,6 +66,11 @@ namespace storm {
return relevantValues.get();
}
template<typename ValueType>
boost::optional<storm::storage::BitVector> const& AbstractEquationSolver<ValueType>::getOptionalRelevantValues() const {
return relevantValues;
}
template<typename ValueType>
void AbstractEquationSolver<ValueType>::setRelevantValues(storm::storage::BitVector&& relevantValues) {
this->relevantValues = std::move(relevantValues);

1
src/storm/solver/AbstractEquationSolver.h

@ -52,6 +52,7 @@ namespace storm {
* Retrieves the relevant values (if there are any).
*/
storm::storage::BitVector const& getRelevantValues() const;
boost::optional<storm::storage::BitVector> const& getOptionalRelevantValues() const;
/*!
* Sets the relevant values.

63
src/storm/solver/IterativeMinMaxLinearEquationSolver.cpp

@ -2,7 +2,6 @@
#include <limits>
#include "storm/solver/IterativeMinMaxLinearEquationSolver.h"
#include "storm/solver/helper/OptimisticValueIterationHelper.h"
#include "storm/environment/solver/MinMaxSolverEnvironment.h"
#include "storm/environment/solver/OviSolverEnvironment.h"
@ -360,61 +359,37 @@ namespace storm {
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 (!storm::utility::vector::hasNonZeroEntry(b)) {
// If all entries are zero, OVI might run in an endless loop. However, the result is easy in this case.
x.assign(x.size(), storm::utility::zero<ValueType>());
if (this->isTrackSchedulerSet()) {
this->schedulerChoices = std::vector<uint_fast64_t>(x.size(), 0);
}
return true;
}
if (!auxiliaryRowGroupVector) {
auxiliaryRowGroupVector = std::make_unique<std::vector<ValueType>>(this->A->getRowGroupCount());
}
if (!auxiliaryRowGroupVector2) {
auxiliaryRowGroupVector2 = std::make_unique<std::vector<ValueType>>(this->A->getRowGroupCount());
if (!optimisticValueIterationHelper) {
optimisticValueIterationHelper = std::make_unique<storm::solver::helper::OptimisticValueIterationHelper<ValueType>>(*this->A);
}
// 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();
}
storm::solver::helper::OptimisticValueIterationHelper<ValueType> helper(*this->A);
// 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);
}
},
env.solver().minMax().getRelativeTerminationCriterion(),
storm::utility::convertNumber<ValueType>(env.solver().minMax().getPrecision()),
env.solver().minMax().getMaximalNumberOfIterations(),
relevantValues);
auto statusIters = helper.solveEquations(env, lowerX, upperX, b,
env.solver().minMax().getRelativeTerminationCriterion(),
storm::utility::convertNumber<ValueType>(env.solver().minMax().getPrecision()),
env.solver().minMax().getMaximalNumberOfIterations(),
dir,
this->getOptionalRelevantValues());
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; });
@ -423,8 +398,7 @@ namespace storm {
// 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());
this->A->multiplyAndReduce(dir, this->A->getRowGroupIndices(), x, &b, *auxiliaryRowGroupVector.get(), &this->schedulerChoices.get());
}
if (!this->isCachingEnabled()) {
@ -1098,6 +1072,7 @@ namespace storm {
auxiliaryRowGroupVector.reset();
auxiliaryRowGroupVector2.reset();
soundValueIterationHelper.reset();
optimisticValueIterationHelper.reset();
StandardMinMaxLinearEquationSolver<ValueType>::clearCache();
}

2
src/storm/solver/IterativeMinMaxLinearEquationSolver.h

@ -8,6 +8,7 @@
#include "storm/solver/Multiplier.h"
#include "storm/solver/StandardMinMaxLinearEquationSolver.h"
#include "storm/solver/helper/SoundValueIterationHelper.h"
#include "storm/solver/helper/OptimisticValueIterationHelper.h"
#include "storm/solver/SolverStatus.h"
@ -85,6 +86,7 @@ namespace storm {
mutable std::unique_ptr<std::vector<ValueType>> auxiliaryRowGroupVector; // A.rowGroupCount() entries
mutable std::unique_ptr<std::vector<ValueType>> auxiliaryRowGroupVector2; // A.rowGroupCount() entries
mutable std::unique_ptr<storm::solver::helper::SoundValueIterationHelper<ValueType>> soundValueIterationHelper;
mutable std::unique_ptr<storm::solver::helper::OptimisticValueIterationHelper<ValueType>> optimisticValueIterationHelper;
};

57
src/storm/solver/NativeLinearEquationSolver.cpp

@ -637,60 +637,32 @@ namespace storm {
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 (!storm::utility::vector::hasNonZeroEntry(b)) {
// If all entries are zero, OVI might run in an endless loop. However, the result is easy in this case.
x.assign(x.size(), storm::utility::zero<ValueType>());
return true;
}
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());
if (!optimisticValueIterationHelper) {
optimisticValueIterationHelper = std::make_unique<storm::solver::helper::OptimisticValueIterationHelper<ValueType>>(*this->A);
}
// 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);
}
},
env.solver().native().getRelativeTerminationCriterion(),
storm::utility::convertNumber<ValueType>(env.solver().native().getPrecision()),
env.solver().native().getMaximalNumberOfIterations(),
relevantValues);
storm::solver::helper::OptimisticValueIterationHelper<ValueType> helper(*this->A);
auto statusIters = helper.solveEquations(env, lowerX, upperX, b,
env.solver().native().getRelativeTerminationCriterion(),
storm::utility::convertNumber<ValueType>(env.solver().native().getPrecision()),
env.solver().native().getMaximalNumberOfIterations(),
boost::none, // No optimization dir
this->getOptionalRelevantValues());
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);
@ -1058,6 +1030,7 @@ namespace storm {
walkerChaeData.reset();
multiplier.reset();
soundValueIterationHelper.reset();
optimisticValueIterationHelper.reset();
LinearEquationSolver<ValueType>::clearCache();
}

2
src/storm/solver/NativeLinearEquationSolver.h

@ -9,6 +9,7 @@
#include "storm/solver/NativeMultiplier.h"
#include "storm/solver/SolverStatus.h"
#include "storm/solver/helper/SoundValueIterationHelper.h"
#include "storm/solver/helper/OptimisticValueIterationHelper.h"
#include "storm/utility/NumberTraits.h"
@ -96,6 +97,7 @@ namespace storm {
// cached auxiliary data
mutable std::unique_ptr<std::vector<ValueType>> cachedRowVector2; // A.getRowCount() rows
mutable std::unique_ptr<storm::solver::helper::SoundValueIterationHelper<ValueType>> soundValueIterationHelper;
mutable std::unique_ptr<storm::solver::helper::OptimisticValueIterationHelper<ValueType>> optimisticValueIterationHelper;
struct JacobiDecomposition {
JacobiDecomposition(Environment const& env, storm::storage::SparseMatrix<ValueType> const& A);

13
src/storm/solver/TopologicalLinearEquationSolver.cpp

@ -4,6 +4,8 @@
#include "storm/utility/constants.h"
#include "storm/utility/vector.h"
#include "storm/utility/Stopwatch.h"
#include "storm/utility/ProgressMeasurement.h"
#include "storm/exceptions/InvalidStateException.h"
#include "storm/exceptions/InvalidEnvironmentException.h"
#include "storm/exceptions/UnexpectedException.h"
@ -61,7 +63,10 @@ namespace storm {
if (!this->sortedSccDecomposition || (needAdaptPrecision && !this->longestSccChainSize)) {
STORM_LOG_TRACE("Creating SCC decomposition.");
storm::utility::Stopwatch sccSw(true);
createSortedSccDecomposition(needAdaptPrecision);
sccSw.stop();
STORM_LOG_INFO("SCC decomposition computed in " << sccSw << ". Found " << this->sortedSccDecomposition->size() << " SCC(s) containing a total of " << x.size() << " states. Average SCC size is " << static_cast<double>(this->getMatrixRowCount()) / static_cast<double>(this->sortedSccDecomposition->size()) << ".");
}
// We do not need to adapt the precision if all SCCs are trivial (i.e., the system is acyclic)
@ -69,7 +74,6 @@ namespace storm {
storm::Environment sccSolverEnvironment = getEnvironmentForUnderlyingSolver(env, needAdaptPrecision);
STORM_LOG_INFO("Found " << this->sortedSccDecomposition->size() << " SCC(s). Average size is " << static_cast<double>(this->getMatrixRowCount()) / static_cast<double>(this->sortedSccDecomposition->size()) << ".");
if (this->longestSccChainSize) {
STORM_LOG_INFO("Longest SCC chain size is " << this->longestSccChainSize.get() << ".");
}
@ -79,8 +83,12 @@ namespace storm {
if (this->sortedSccDecomposition->size() == 1) {
returnValue = solveFullyConnectedEquationSystem(sccSolverEnvironment, x, b);
} else {
// Solve each SCC individually
storm::storage::BitVector sccAsBitVector(x.size(), false);
uint64_t sccIndex = 0;
storm::utility::ProgressMeasurement progress("states");
progress.setMaxCount(x.size());
progress.startNewMeasurement(0);
for (auto const& scc : *this->sortedSccDecomposition) {
if (scc.size() == 1) {
returnValue = solveTrivialScc(*scc.begin(), x, b) && returnValue;
@ -92,6 +100,7 @@ namespace storm {
returnValue = solveScc(sccSolverEnvironment, sccAsBitVector, x, b) && returnValue;
}
++sccIndex;
progress.updateProgress(sccIndex);
if (storm::utility::resources::isTerminate()) {
STORM_LOG_WARN("Topological solver aborted after analyzing " << sccIndex << "/" << this->sortedSccDecomposition->size() << " SCCs.");
break;
@ -177,8 +186,6 @@ namespace storm {
if (asEquationSystem) {
sccA.convertToEquationSystem();
}
// std::cout << "Solving SCC " << scc << std::endl;
// std::cout << "Matrix is " << sccA << std::endl;
this->sccSolver->setMatrix(std::move(sccA));
// x Vector

11
src/storm/solver/TopologicalMinMaxLinearEquationSolver.cpp

@ -5,6 +5,8 @@
#include "storm/utility/constants.h"
#include "storm/utility/vector.h"
#include "storm/utility/Stopwatch.h"
#include "storm/utility/ProgressMeasurement.h"
#include "storm/exceptions/InvalidStateException.h"
#include "storm/exceptions/InvalidEnvironmentException.h"
#include "storm/exceptions/UnexpectedException.h"
@ -51,8 +53,10 @@ namespace storm {
if (!this->sortedSccDecomposition || (needAdaptPrecision && !this->longestSccChainSize)) {
STORM_LOG_TRACE("Creating SCC decomposition.");
storm::utility::Stopwatch sccSw(true);
createSortedSccDecomposition(needAdaptPrecision);
STORM_LOG_INFO("Found " << this->sortedSccDecomposition->size() << " SCC(s). Average size is " << static_cast<double>(this->A->getRowGroupCount()) / static_cast<double>(this->sortedSccDecomposition->size()) << ".");
sccSw.stop();
STORM_LOG_INFO("SCC decomposition computed in " << sccSw << ". Found " << this->sortedSccDecomposition->size() << " SCC(s) containing a total of " << x.size() << " states. Average SCC size is " << static_cast<double>(this->A->getRowGroupCount()) / static_cast<double>(this->sortedSccDecomposition->size()) << ".");
}
// We do not need to adapt the precision if all SCCs are trivial (i.e., the system is acyclic)
@ -69,6 +73,7 @@ namespace storm {
// Handle the case where there is just one large SCC
returnValue = solveFullyConnectedEquationSystem(sccSolverEnvironment, dir, x, b);
} else {
// Solve each SCC individually
if (this->isTrackSchedulerSet()) {
if (this->schedulerChoices) {
this->schedulerChoices.get().resize(x.size());
@ -79,6 +84,9 @@ namespace storm {
storm::storage::BitVector sccRowGroupsAsBitVector(x.size(), false);
storm::storage::BitVector sccRowsAsBitVector(b.size(), false);
uint64_t sccIndex = 0;
storm::utility::ProgressMeasurement progress("states");
progress.setMaxCount(x.size());
progress.startNewMeasurement(0);
for (auto const& scc : *this->sortedSccDecomposition) {
if (scc.size() == 1) {
returnValue = solveTrivialScc(*scc.begin(), dir, x, b) && returnValue;
@ -95,6 +103,7 @@ namespace storm {
returnValue = solveScc(sccSolverEnvironment, dir, sccRowGroupsAsBitVector, sccRowsAsBitVector, x, b) && returnValue;
}
++sccIndex;
progress.updateProgress(sccIndex);
if (storm::utility::resources::isTerminate()) {
STORM_LOG_WARN("Topological solver aborted after analyzing " << sccIndex << "/" << this->sortedSccDecomposition->size() << " SCCs.");
break;

374
src/storm/solver/helper/OptimisticValueIterationHelper.cpp

@ -0,0 +1,374 @@
#include "OptimisticValueIterationHelper.h"
#include "storm/utility/vector.h"
#include "storm/utility/SignalHandler.h"
#include "storm/environment/solver/OviSolverEnvironment.h"
#include "storm/exceptions/NotSupportedException.h"
#include "storm/utility/macros.h"
namespace storm {
namespace solver {
namespace helper {
namespace oviinternal {
template<typename ValueType>
ValueType updateIterationPrecision(storm::Environment const& env, ValueType const& diff) {
auto factor = storm::utility::convertNumber<ValueType>(env.solver().ovi().getPrecisionUpdateFactor());
return factor * diff;
}
template<typename ValueType>
void guessUpperBoundRelative(std::vector<ValueType> const& x, std::vector<ValueType> &target, ValueType const& relativeBoundGuessingScaler) {
storm::utility::vector::applyPointwise<ValueType, ValueType>(x, target, [&relativeBoundGuessingScaler] (ValueType const& argument) -> ValueType { return argument * relativeBoundGuessingScaler; });
}
template<typename ValueType>
void guessUpperBoundAbsolute(std::vector<ValueType> const& x, std::vector<ValueType> &target, ValueType const& precision) {
storm::utility::vector::applyPointwise<ValueType, ValueType>(x, target, [&precision] (ValueType const& argument) -> ValueType { return argument + precision; });
}
template <typename ValueType>
IterationHelper<ValueType>::IterationHelper(storm::storage::SparseMatrix<ValueType> const& matrix) {
STORM_LOG_THROW(static_cast<uint64_t>(std::numeric_limits<uint32_t>::max()) > matrix.getRowCount() + 1, storm::exceptions::NotSupportedException, "Matrix dimensions too large.");
STORM_LOG_THROW(static_cast<uint64_t>(std::numeric_limits<uint32_t>::max()) > matrix.getEntryCount(), storm::exceptions::NotSupportedException, "Matrix dimensions too large.");
matrixValues.reserve(matrix.getNonzeroEntryCount());
matrixColumns.reserve(matrix.getColumnCount());
rowIndications.reserve(matrix.getRowCount() + 1);
rowIndications.push_back(0);
for (IndexType r = 0; r < static_cast<IndexType>(matrix.getRowCount()); ++r) {
for (auto const& entry : matrix.getRow(r)) {
matrixValues.push_back(entry.getValue());
matrixColumns.push_back(entry.getColumn());
}
rowIndications.push_back(matrixValues.size());
}
if (!matrix.hasTrivialRowGrouping()) {
rowGroupIndices = &matrix.getRowGroupIndices();
}
}
template <typename ValueType>
ValueType IterationHelper<ValueType>::singleIterationWithDiff(std::vector<ValueType>& x, std::vector<ValueType> const& b, bool computeRelativeDiff) {
return singleIterationWithDiffInternal<false, storm::solver::OptimizationDirection::Minimize>(x, b, computeRelativeDiff);
}
template <typename ValueType>
ValueType IterationHelper<ValueType>::singleIterationWithDiff(storm::solver::OptimizationDirection const& dir, std::vector<ValueType>& x, std::vector<ValueType> const& b, bool computeRelativeDiff) {
if (minimize(dir)) {
return singleIterationWithDiffInternal<true, storm::solver::OptimizationDirection::Minimize>(x, b, computeRelativeDiff);
} else {
return singleIterationWithDiffInternal<true, storm::solver::OptimizationDirection::Maximize>(x, b, computeRelativeDiff);
}
}
template <typename ValueType>
template<bool HasRowGroups, storm::solver::OptimizationDirection Dir>
ValueType IterationHelper<ValueType>::singleIterationWithDiffInternal(std::vector<ValueType>& x, std::vector<ValueType> const& b, bool computeRelativeDiff) {
STORM_LOG_ASSERT(x.size() > 0, "Empty equation system not expected.");
ValueType diff = storm::utility::zero<ValueType>();
IndexType i = x.size();
while (i > 0) {
--i;
ValueType newXi = HasRowGroups ? multiplyRowGroup<Dir>(i, b, x) : multiplyRow(i, b[i], x);
ValueType& oldXi = x[i];
if (computeRelativeDiff) {
if (storm::utility::isZero(newXi)) {
if (storm::utility::isZero(oldXi)) {
// this operation has no effect:
// diff = std::max(diff, storm::utility::zero<ValueType>());
} else {
diff = std::max(diff, storm::utility::one<ValueType>());
}
} else {
diff = std::max(diff, storm::utility::abs<ValueType>((newXi - oldXi) / newXi));
}
} else {
diff = std::max(diff, storm::utility::abs<ValueType>(newXi - oldXi));
}
oldXi = std::move(newXi);
}
return diff;
}
template <typename ValueType>
uint64_t IterationHelper<ValueType>::repeatedIterate(storm::solver::OptimizationDirection const& dir, std::vector<ValueType>& x, std::vector<ValueType> const& b, ValueType precision, bool relative) {
if (minimize(dir)) {
return repeatedIterateInternal<true, storm::solver::OptimizationDirection::Minimize>(x, b, precision, relative);
} else {
return repeatedIterateInternal<true, storm::solver::OptimizationDirection::Maximize>(x, b, precision, relative);
}
}
template <typename ValueType>
uint64_t IterationHelper<ValueType>::repeatedIterate(std::vector<ValueType>& x, const std::vector<ValueType>& b, ValueType precision, bool relative) {
return repeatedIterateInternal<false, storm::solver::OptimizationDirection::Minimize>(x, b, precision, relative);
}
template <typename ValueType>
template<bool HasRowGroups, storm::solver::OptimizationDirection Dir>
uint64_t IterationHelper<ValueType>::repeatedIterateInternal(std::vector<ValueType>& x, std::vector<ValueType> const& b, ValueType precision, bool relative) {
// Do a backwards gauss-seidel style iteration
bool convergence = true;
IndexType i = x.size();
while (i > 0) {
--i;
ValueType newXi = HasRowGroups ? multiplyRowGroup<Dir>(i, b, x) : multiplyRow(i, b[i], x);
ValueType& oldXi = x[i];
// Check if we converged
if (relative) {
if (storm::utility::isZero(oldXi)) {
if (!storm::utility::isZero(newXi)) {
convergence = false;
break;
}
} else if (storm::utility::abs<ValueType>((newXi - oldXi) / oldXi) > precision) {
convergence = false;
break;
}
} else {
if (storm::utility::abs<ValueType>((newXi - oldXi)) > precision) {
convergence = false;
break;
}
}
}
if (!convergence) {
// we now know that we did not converge. We still need to set the remaining values
while (i > 0) {
--i;
x[i] = HasRowGroups ? multiplyRowGroup<Dir>(i, b, x) : multiplyRow(i, b[i], x);
}
}
return convergence;
}
template <typename ValueType>
typename IterationHelper<ValueType>::IterateResult IterationHelper<ValueType>::iterateUpper(storm::solver::OptimizationDirection const& dir, std::vector<ValueType>& x, std::vector<ValueType> const& b, bool takeMinOfOldAndNew) {
if (minimize(dir)) {
return iterateUpperInternal<true, storm::solver::OptimizationDirection::Minimize>(x, b, takeMinOfOldAndNew);
} else {
return iterateUpperInternal<true, storm::solver::OptimizationDirection::Maximize>(x, b, takeMinOfOldAndNew);
}
}
template <typename ValueType>
typename IterationHelper<ValueType>::IterateResult IterationHelper<ValueType>::iterateUpper(std::vector<ValueType>& x, std::vector<ValueType> const& b, bool takeMinOfOldAndNew) {
return iterateUpperInternal<false, storm::solver::OptimizationDirection::Minimize>(x, b, takeMinOfOldAndNew);
}
template <typename ValueType>
template<bool HasRowGroups, storm::solver::OptimizationDirection Dir>
typename IterationHelper<ValueType>::IterateResult IterationHelper<ValueType>::iterateUpperInternal(std::vector<ValueType>& x, std::vector<ValueType> const& b, bool takeMinOfOldAndNew) {
// For each row compare the new upper bound candidate with the old one
bool newUpperBoundAlwaysHigherEqual = true;
bool newUpperBoundAlwaysLowerEqual = true;
// Do a backwards gauss-seidel style iteration
for (IndexType i = x.size(); i > 0;) {
--i;
ValueType newXi = HasRowGroups ? multiplyRowGroup<Dir>(i, b, x) : multiplyRow(i, b[i], x);
ValueType& oldXi = x[i];
if (newXi > oldXi) {
newUpperBoundAlwaysLowerEqual = false;
if (!takeMinOfOldAndNew) {
oldXi = newXi;
}
} else if (newXi != oldXi) {
assert(newXi < oldXi);
newUpperBoundAlwaysHigherEqual = false;
oldXi = newXi;
}
}
// Return appropriate result
if (newUpperBoundAlwaysLowerEqual) {
if (newUpperBoundAlwaysHigherEqual) {
return IterateResult::Equal;
} else {
return IterateResult::AlwaysLowerOrEqual;
}
} else {
if (newUpperBoundAlwaysHigherEqual) {
return IterateResult::AlwaysHigherOrEqual;
} else {
return IterateResult::Incomparable;
}
}
}
template <typename ValueType>
ValueType IterationHelper<ValueType>::multiplyRow(IndexType const& rowIndex, ValueType const& bi, std::vector<ValueType> const& x) {
assert(rowIndex < rowIndications.size());
ValueType xRes = bi;
auto entryIt = matrixValues.begin() + rowIndications[rowIndex];
auto entryItE = matrixValues.begin() + rowIndications[rowIndex + 1];
auto colIt = matrixColumns.begin() + rowIndications[rowIndex];
for (; entryIt != entryItE; ++entryIt, ++colIt) {
xRes += *entryIt * x[*colIt];
}
return xRes;
}
template <typename ValueType>
template<storm::solver::OptimizationDirection Dir>
ValueType IterationHelper<ValueType>::multiplyRowGroup(IndexType const& rowGroupIndex, std::vector<ValueType> const& b, std::vector<ValueType> const& x) {
STORM_LOG_ASSERT(rowGroupIndices != nullptr, "No row group indices available.");
auto row = (*rowGroupIndices)[rowGroupIndex];
auto const& groupEnd = (*rowGroupIndices)[rowGroupIndex + 1];
STORM_LOG_ASSERT(row < groupEnd, "Empty row group not expected.");
ValueType xRes = multiplyRow(row, b[row], x);
for (++row; row < groupEnd; ++row) {
ValueType xCur = multiplyRow(row, b[row], x);
xRes = minimize(Dir) ? std::min(xRes, xCur) : std::max(xRes, xCur);
}
return xRes;
}
}
template<typename ValueType>
OptimisticValueIterationHelper<ValueType>::OptimisticValueIterationHelper(storm::storage::SparseMatrix<ValueType> const& matrix) : iterationHelper(matrix) {
// Intentionally left empty.
}
template<typename ValueType>
std::pair<SolverStatus, uint64_t> OptimisticValueIterationHelper<ValueType>::solveEquations(Environment const& env, std::vector<ValueType>* lowerX, std::vector<ValueType>* upperX, std::vector<ValueType> const& b, bool relative, ValueType precision, uint64_t maxOverallIterations, boost::optional<storm::solver::OptimizationDirection> dir, boost::optional<storm::storage::BitVector> const& relevantValues) {
STORM_LOG_ASSERT(lowerX->size() == upperX->size(), "Dimension missmatch.");
// As we will shuffle pointers around, let's store the original positions here.
std::vector<ValueType>* initLowerX = lowerX;
std::vector<ValueType>* initUpperX = upperX;
uint64_t overallIterations = 0;
uint64_t lastValueIterationIterations = 0;
uint64_t currentVerificationIterations = 0;
// Get some parameters for the algorithm
// 2
ValueType two = storm::utility::convertNumber<ValueType>(2.0);
// Use no termination guaranteed upper bound iteration method
bool noTerminationGuarantee = env.solver().ovi().useNoTerminationGuaranteeMinimumMethod();
// Desired max difference between upperX and lowerX
ValueType doublePrecision = precision * two;
// Upper bound only iterations
uint64_t upperBoundOnlyIterations = env.solver().ovi().getUpperBoundOnlyIterations();
ValueType relativeBoundGuessingScaler = (storm::utility::one<ValueType>() + storm::utility::convertNumber<ValueType>(env.solver().ovi().getUpperBoundGuessingFactor()) * precision);
// Initial precision for the value iteration calls
ValueType iterationPrecision = precision;
SolverStatus status = SolverStatus::InProgress;
while (status == SolverStatus::InProgress && overallIterations < maxOverallIterations) {
// Perform value iteration until convergence
lastValueIterationIterations = dir ? iterationHelper.repeatedIterate(dir.get(), *lowerX, b, iterationPrecision, relative) : iterationHelper.repeatedIterate(*lowerX, b, iterationPrecision, relative);
overallIterations += lastValueIterationIterations;
bool intervalIterationNeeded = false;
currentVerificationIterations = 0;
if (relative) {
oviinternal::guessUpperBoundRelative(*lowerX, *upperX, relativeBoundGuessingScaler);
} else {
oviinternal::guessUpperBoundAbsolute(*lowerX, *upperX, precision);
}
bool cancelGuess = false;
while (status == SolverStatus::InProgress && overallIterations < maxOverallIterations) {
++overallIterations;
++currentVerificationIterations;
// Perform value iteration stepwise for lower bound and guessed upper bound
// Upper bound iteration
auto upperBoundIterResult = dir ? iterationHelper.iterateUpper(dir.get(), *upperX, b, !noTerminationGuarantee) : iterationHelper.iterateUpper(*upperX, b, !noTerminationGuarantee);
if (upperBoundIterResult == oviinternal::IterationHelper<ValueType>::IterateResult::AlwaysHigherOrEqual) {
// All values moved up (and did not stay the same)
// That means the guess for an upper bound is actually a lower bound
auto diff = dir ? iterationHelper.singleIterationWithDiff(dir.get(), *upperX, b, relative) : iterationHelper.singleIterationWithDiff(*upperX, b, relative);
iterationPrecision = oviinternal::updateIterationPrecision(env, diff);
// We assume to have a single fixed point. We can thus safely set the new lower bound, to the wrongly guessed upper bound
// Set lowerX to the upper bound candidate
std::swap(lowerX, upperX);
break;
} else if (upperBoundIterResult == oviinternal::IterationHelper<ValueType>::IterateResult::AlwaysLowerOrEqual) {
// All values moved down (and stayed not the same)
// This is a valid upper bound. We still need to check the precision.
// We can safely use twice the requested precision, as we calculate the center of both vectors
bool reachedPrecision;
if (relevantValues) {
reachedPrecision = storm::utility::vector::equalModuloPrecision(*lowerX, *upperX, relevantValues.get(), doublePrecision, relative);
} else {
reachedPrecision = storm::utility::vector::equalModuloPrecision(*lowerX, *upperX, doublePrecision, relative);
}
if (reachedPrecision) {
status = SolverStatus::Converged;
break;
} else {
// From now on, we keep updating both bounds
intervalIterationNeeded = true;
}
// The following case below covers that both vectors (old and new) are equal.
// Theoretically, this means that the precise fixpoint has been reached. However, numerical instabilities can be tricky and this detection might be incorrect (see the haddad-monmege model).
// We therefore disable it. It is very unlikely that we guessed the right fixpoint anyway.
//} else if (upperBoundIterResult == oviinternal::IterationHelper<ValueType>::IterateResult::Equal) {
// In this case, the guessed upper bound is the precise fixpoint
// status = SolverStatus::Converged;
// break;
}
// Check whether we tried this guess for too long
ValueType scaledIterationCount = storm::utility::convertNumber<ValueType>(currentVerificationIterations) * storm::utility::convertNumber<ValueType>(env.solver().ovi().getMaxVerificationIterationFactor());
if (!intervalIterationNeeded && scaledIterationCount * iterationPrecision >= storm::utility::one<ValueType>()) {
cancelGuess = true;
// In this case we will make one more iteration on the lower bound (mainly to obtain a new iterationPrecision)
}
// Lower bound iteration (only if needed)
if (cancelGuess || intervalIterationNeeded || currentVerificationIterations > upperBoundOnlyIterations) {
auto diff = dir ? iterationHelper.singleIterationWithDiff(dir.get(), *lowerX, b, relative) : iterationHelper.singleIterationWithDiff(*lowerX, b, relative);
// Check whether the upper and lower bounds have crossed, i.e., the upper bound is smaller than the lower bound.
bool valuesCrossed = false;
for (uint64_t i = 0; i < lowerX->size(); ++i) {
if ((*upperX)[i] < (*lowerX)[i]) {
valuesCrossed = true;
break;
}
}
if (cancelGuess || valuesCrossed) {
// A new guess is needed.
iterationPrecision = oviinternal::updateIterationPrecision(env, diff);
break;
}
}
}
if (storm::utility::resources::isTerminate()) {
status = SolverStatus::Aborted;
}
} // end while
// Swap the results into the output vectors (if necessary).
assert(initLowerX != lowerX || (initLowerX == lowerX && initUpperX == upperX));
if (initLowerX != lowerX) {
assert(initUpperX == lowerX);
assert(initLowerX == upperX);
lowerX->swap(*upperX);
}
if (overallIterations > maxOverallIterations) {
status = SolverStatus::MaximalIterationsExceeded;
}
return {status, overallIterations};
}
template class OptimisticValueIterationHelper<double>;
template class OptimisticValueIterationHelper<storm::RationalNumber>;
}
}
}

347
src/storm/solver/helper/OptimisticValueIterationHelper.h

@ -3,309 +3,88 @@
#include <vector>
#include <boost/optional.hpp>
#include "storm/storage/SparseMatrix.h"
#include "storm/solver/OptimizationDirection.h"
#include "storm/solver/SolverStatus.h"
#include "storm/utility/vector.h"
#include "storm/utility/ProgressMeasurement.h"
#include "storm/utility/SignalHandler.h"
#include "storm/storage/BitVector.h"
#include "storm/environment/solver/MinMaxSolverEnvironment.h"
#include "storm/environment/solver/OviSolverEnvironment.h"
#include "storm/utility/macros.h"
namespace storm {
class Environment;
namespace solver {
namespace helper {
namespace oviinternal {
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>
ValueType computeMaxAbsDiff(std::vector<ValueType> const& allOldValues, std::vector<ValueType> const& allNewValues) {
ValueType result = storm::utility::zero<ValueType>();
for (uint64_t i = 0; i < allOldValues.size(); ++i) {
result = storm::utility::max<ValueType>(result, storm::utility::abs<ValueType>(allNewValues[i] - allOldValues[i]));
}
return result;
}
template<typename ValueType>
ValueType computeMaxRelDiff(std::vector<ValueType> const& allOldValues, std::vector<ValueType> const& allNewValues, storm::storage::BitVector const& relevantValues) {
ValueType result = storm::utility::zero<ValueType>();
for (auto const& i : relevantValues) {
STORM_LOG_ASSERT(!storm::utility::isZero(allNewValues[i]) || storm::utility::isZero(allOldValues[i]), "Unexpected entry in iteration vector.");
if (!storm::utility::isZero(allNewValues[i])) {
result = storm::utility::max<ValueType>(result, storm::utility::abs<ValueType>(allNewValues[i] - allOldValues[i]) / allNewValues[i]);
}
}
return result;
}
template <typename ValueType>
class IterationHelper {
public:
typedef uint32_t IndexType;
IterationHelper(storm::storage::SparseMatrix<ValueType> const& matrix);
enum class IterateResult {
AlwaysHigherOrEqual,
AlwaysLowerOrEqual,
Equal,
Incomparable
};
/// performs a single iteration and returns the maximal difference between the iterations as well as the index where this difference happened
ValueType singleIterationWithDiff(std::vector<ValueType>& x, std::vector<ValueType> const& b, bool computeRelativeDiff);
ValueType singleIterationWithDiff(storm::solver::OptimizationDirection const& dir, std::vector<ValueType>& x, std::vector<ValueType> const& b, bool computeRelativeDiff);
/// returns the number of performed iterations
uint64_t repeatedIterate(std::vector<ValueType>& x, std::vector<ValueType> const& b, ValueType precision, bool relative);
uint64_t repeatedIterate(storm::solver::OptimizationDirection const& dir, std::vector<ValueType>& x, std::vector<ValueType> const& b, ValueType precision, bool relative);
/// Performs a single iteration for the upper bound
IterateResult iterateUpper(std::vector<ValueType>& x, std::vector<ValueType> const& b, bool takeMinOfOldAndNew);
IterateResult iterateUpper(storm::solver::OptimizationDirection const& dir, std::vector<ValueType>& x, std::vector<ValueType> const& b, bool takeMinOfOldAndNew);
template<typename ValueType>
ValueType computeMaxRelDiff(std::vector<ValueType> const& allOldValues, std::vector<ValueType> const& allNewValues) {
ValueType result = storm::utility::zero<ValueType>();
for (uint64_t i = 0; i < allOldValues.size(); ++i) {
STORM_LOG_ASSERT(!storm::utility::isZero(allNewValues[i]) || storm::utility::isZero(allOldValues[i]), "Unexpected entry in iteration vector.");
if (!storm::utility::isZero(allNewValues[i])) {
result = storm::utility::max<ValueType>(result, storm::utility::abs<ValueType>(allNewValues[i] - allOldValues[i]) / allNewValues[i]);
}
}
return result;
}
template<typename ValueType>
ValueType updateIterationPrecision(storm::Environment const& env, std::vector<ValueType> const& currentX, std::vector<ValueType> const& newX, bool const& relative, boost::optional<storm::storage::BitVector> const& relevantValues) {
auto factor = storm::utility::convertNumber<ValueType>(env.solver().ovi().getPrecisionUpdateFactor());
bool useRelevant = relevantValues.is_initialized() && env.solver().ovi().useRelevantValuesForPrecisionUpdate();
if (relative) {
return (useRelevant ? computeMaxRelDiff(newX, currentX, relevantValues.get()) : computeMaxRelDiff(newX, currentX)) * factor;
} else {
return (useRelevant ? computeMaxAbsDiff(newX, currentX, relevantValues.get()) : computeMaxAbsDiff(newX, currentX)) * factor;
}
}
template<typename ValueType>
void guessUpperBoundRelative(std::vector<ValueType> const& x, std::vector<ValueType> &target, ValueType const& relativeBoundGuessingScaler) {
storm::utility::vector::applyPointwise<ValueType, ValueType>(x, target, [&relativeBoundGuessingScaler] (ValueType const& argument) -> ValueType { return argument * relativeBoundGuessingScaler; });
}
template<typename ValueType>
void guessUpperBoundAbsolute(std::vector<ValueType> const& x, std::vector<ValueType> &target, ValueType const& precision) {
storm::utility::vector::applyPointwise<ValueType, ValueType>(x, target, [&precision] (ValueType const& argument) -> ValueType { return argument + precision; });
}
private:
template<bool HasRowGroups, storm::solver::OptimizationDirection Dir>
ValueType singleIterationWithDiffInternal(std::vector<ValueType>& x, std::vector<ValueType> const& b, bool computeRelativeDiff);
template<bool HasRowGroups, storm::solver::OptimizationDirection Dir>
uint64_t repeatedIterateInternal(std::vector<ValueType>& x, std::vector<ValueType> const& b, ValueType precision, bool relative);
template<bool HasRowGroups, storm::solver::OptimizationDirection Dir>
IterateResult iterateUpperInternal(std::vector<ValueType>& x, std::vector<ValueType> const& b, bool takeMinOfOldAndNew);
ValueType multiplyRow(IndexType const& rowIndex, ValueType const& bi, std::vector<ValueType> const& x);
template<storm::solver::OptimizationDirection Dir>
ValueType multiplyRowGroup(IndexType const& rowGroupIndex, std::vector<ValueType> const& b, std::vector<ValueType> const& x);
std::vector<ValueType> matrixValues;
std::vector<IndexType> matrixColumns;
std::vector<IndexType> rowIndications;
std::vector<uint64_t> const* rowGroupIndices;
};
}
/*!
* Performs Optimistic value iteration.
* See https://arxiv.org/abs/1910.01100 for more information on this algorithm
*
* @tparam ValueType
* @tparam ValueType
* @param env
* @param lowerX Needs to be some arbitrary lower bound on the actual values initially
* @param upperX Does not need to be an upper bound initially
* @param auxVector auxiliary storage
* @param valueIterationCallback Function that should perform standard value iteration on the input vector
* @param singleIterationCallback Function that should perform a single value iteration step on the input vector e.g. ( x' = min/max(A*x + b))
* @param relevantValues If given, we only check the precision at the states with the given indices.
* @return The status upon termination as well as the number of iterations Also, the maximum (relative/absolute) difference between lowerX and upperX will be 2*epsilon
* with precision parameters as given by the environment env.
* @see Hartmanns, Kaminski: Optimistic Value Iteration. https://doi.org/10.1007/978-3-030-53291-8\_26
*/
template<typename ValueType, typename ValueIterationCallback, typename SingleIterationCallback>
std::pair<SolverStatus, uint64_t> solveEquationsOptimisticValueIteration(Environment const& env, std::vector<ValueType>* lowerX, std::vector<ValueType>* upperX, std::vector<ValueType>* auxVector, ValueIterationCallback const& valueIterationCallback, SingleIterationCallback const& singleIterationCallback, bool relative, ValueType precision, uint64_t maxOverallIterations, boost::optional<storm::storage::BitVector> relevantValues = boost::none) {
STORM_LOG_ASSERT(lowerX->size() == upperX->size(), "Dimension missmatch.");
STORM_LOG_ASSERT(lowerX->size() == auxVector->size(), "Dimension missmatch.");
// As we will shuffle pointers around, let's store the original positions here.
std::vector<ValueType>* initLowerX = lowerX;
std::vector<ValueType>* initUpperX = upperX;
std::vector<ValueType>* initAux = auxVector;
uint64_t overallIterations = 0;
uint64_t lastValueIterationIterations = 0;
uint64_t currentVerificationIterations = 0;
uint64_t valueIterationInvocations = 0;
// Get some parameters for the algorithm
// 2
ValueType two = storm::utility::convertNumber<ValueType>(2.0);
// Use no termination guaranteed upper bound iteration method
bool noTerminationGuarantee = env.solver().ovi().useNoTerminationGuaranteeMinimumMethod();
// Desired max difference between upperX and lowerX
ValueType doublePrecision = precision * two;
// Upper bound only iterations
uint64_t upperBoundOnlyIterations = env.solver().ovi().getUpperBoundOnlyIterations();
ValueType relativeBoundGuessingScaler = (storm::utility::one<ValueType>() + storm::utility::convertNumber<ValueType>(env.solver().ovi().getUpperBoundGuessingFactor()) * precision);
// Initial precision for the value iteration calls
ValueType iterationPrecision = precision;
template<typename ValueType>
class OptimisticValueIterationHelper {
public:
OptimisticValueIterationHelper(storm::storage::SparseMatrix<ValueType> const& matrix);
SolverStatus status = SolverStatus::InProgress;
while (status == SolverStatus::InProgress && overallIterations < maxOverallIterations) {
// Perform value iteration until convergence
++valueIterationInvocations;
auto result = valueIterationCallback(lowerX, auxVector, iterationPrecision, relative, overallIterations, maxOverallIterations);
lastValueIterationIterations = result.iterations;
overallIterations += result.iterations;
if (result.status != SolverStatus::Converged) {
status = result.status;
} else {
bool intervalIterationNeeded = false;
currentVerificationIterations = 0;
if (relative) {
oviinternal::guessUpperBoundRelative(*lowerX, *upperX, relativeBoundGuessingScaler);
} else {
oviinternal::guessUpperBoundAbsolute(*lowerX, *upperX, precision);
}
bool cancelGuess = false;
while (status == SolverStatus::InProgress && overallIterations < maxOverallIterations) {
++overallIterations;
++currentVerificationIterations;
// Perform value iteration stepwise for lower bound and guessed upper bound
// Upper bound iteration
singleIterationCallback(upperX, auxVector, overallIterations);
// At this point, auxVector contains the old values for the upper bound whereas upperX contains the new ones.
// Compare the new upper bound candidate with the old one
bool newUpperBoundAlwaysHigherEqual = true;
bool newUpperBoundAlwaysLowerEqual = true;
if (noTerminationGuarantee) {
bool cancelOuterScan = false;
for (uint64_t i = 0; i < upperX->size() & !cancelOuterScan; ++i) {
if ((*upperX)[i] < (*auxVector)[i]) {
newUpperBoundAlwaysHigherEqual = false;
for (++i; i < upperX->size(); ++i) {
if ((*upperX)[i] > (*auxVector)[i]) {
newUpperBoundAlwaysLowerEqual = false;
cancelOuterScan = true;
break;
}
}
} else if ((*upperX)[i] != (*auxVector)[i]) {
newUpperBoundAlwaysLowerEqual = false;
for (++i; i < upperX->size(); ++i) {
if ((*upperX)[i] < (*auxVector)[i]) {
newUpperBoundAlwaysHigherEqual = false;
cancelOuterScan = true;
break;
}
}
}
}
} else {
for (uint64_t i = 0; i < upperX->size(); ++i) {
if ((*upperX)[i] < (*auxVector)[i]) {
newUpperBoundAlwaysHigherEqual = false;
} else if ((*upperX)[i] != (*auxVector)[i]) {
newUpperBoundAlwaysLowerEqual = false;
std::swap((*upperX)[i], (*auxVector)[i]);
}
}
}
if (newUpperBoundAlwaysHigherEqual && !newUpperBoundAlwaysLowerEqual) {
// All values moved up or stayed the same
// That means the guess for an upper bound is actually a lower bound
iterationPrecision = oviinternal::updateIterationPrecision(env, *auxVector, *upperX, relative, relevantValues);
// We assume to have a single fixed point. We can thus safely set the new lower bound, to the wrongly guessed upper bound
// Set lowerX to the upper bound candidate
std::swap(lowerX, upperX);
break;
} else if (newUpperBoundAlwaysLowerEqual && !newUpperBoundAlwaysHigherEqual) {
// All values moved down or stayed the same and we have a maximum difference of twice the requested precision
// We can safely use twice the requested precision, as we calculate the center of both vectors
bool reachedPrecision;
if (relevantValues) {
reachedPrecision = storm::utility::vector::equalModuloPrecision(*lowerX, *upperX, relevantValues.get(), doublePrecision, relative);
} else {
reachedPrecision = storm::utility::vector::equalModuloPrecision(*lowerX, *upperX, doublePrecision, relative);
}
if (reachedPrecision) {
status = SolverStatus::Converged;
break;
} else {
// From now on, we keep updating both bounds
intervalIterationNeeded = true;
}
} else if (newUpperBoundAlwaysHigherEqual && newUpperBoundAlwaysLowerEqual) {
// In this case, the guessed upper bound is the precise fixpoint
status = SolverStatus::Converged;
std::swap(lowerX, auxVector);
break;
}
// At this point, the old upper bounds (auxVector) are not needed anymore.
// Check whether we tried this guess for too long
ValueType scaledIterationCount = storm::utility::convertNumber<ValueType>(currentVerificationIterations) * storm::utility::convertNumber<ValueType>(env.solver().ovi().getMaxVerificationIterationFactor());
if (!intervalIterationNeeded && scaledIterationCount >= storm::utility::convertNumber<ValueType>(lastValueIterationIterations)) {
cancelGuess = true;
// In this case we will make one more iteration on the lower bound (mainly to obtain a new iterationPrecision)
}
// Lower bound iteration (only if needed)
if (cancelGuess || intervalIterationNeeded || currentVerificationIterations > upperBoundOnlyIterations) {
singleIterationCallback(lowerX, auxVector, overallIterations);
// At this point, auxVector contains the old values for the lower bound whereas lowerX contains the new ones.
// Check whether the upper and lower bounds have crossed, i.e., the upper bound is smaller than the lower bound.
bool valuesCrossed = false;
for (uint64_t i = 0; i < lowerX->size(); ++i) {
if ((*upperX)[i] < (*lowerX)[i]) {
valuesCrossed = true;
break;
}
}
if (cancelGuess || valuesCrossed) {
// A new guess is needed.
iterationPrecision = oviinternal::updateIterationPrecision(env, *auxVector, *lowerX, relative, relevantValues);
break;
}
}
}
if (storm::utility::resources::isTerminate()) {
status = SolverStatus::Aborted;
}
}
} // end while
// Swap the results into the output vectors.
if (initLowerX == lowerX) {
// lowerX is already at the correct position. We still have to care for upperX
if (initUpperX != upperX) {
// UpperX is not at the correct position. It has to be at the auxVector
assert(initAux == upperX);
std::swap(*initUpperX, *initAux);
}
} else if (initUpperX == upperX) {
// UpperX is already at the correct position.
// We already know that lowerX is at the wrong position. It has to be at the auxVector
assert(initAux == lowerX);
std::swap(*initLowerX, *initAux);
} else if (initAux == auxVector) {
// We know that upperX and lowerX are swapped.
assert(initLowerX == upperX);
assert(initUpperX == lowerX);
std::swap(*initUpperX, *initLowerX);
} else {
// Now we know that all vectors are at the wrong position. There are only two possibilities left
if (initLowerX == upperX) {
assert(initUpperX == auxVector);
assert(initAux == lowerX);
std::swap(*initLowerX, *initAux);
std::swap(*initUpperX, *initAux);
} else {
assert(initLowerX == auxVector);
assert(initUpperX == lowerX);
assert (initAux == upperX);
std::swap(*initUpperX, *initAux);
std::swap(*initLowerX, *initAux);
}
}
/*!
* @param env
* @param lowerX Needs to be some arbitrary lower bound on the actual values initially
* @param upperX Does not need to be an upper bound initially
* @param b the values added to each matrix row (the b in A*x+b)
* @param dir The optimization direction
* @param relevantValues If given, we only check the precision at the states with the given indices.
* @return The status upon termination as well as the number of iterations Also, the maximum (relative/absolute) difference between lowerX and upperX will be 2*epsilon
* with the provided precision parameters.
*/
std::pair<SolverStatus, uint64_t> solveEquations(Environment const& env, std::vector<ValueType>* lowerX, std::vector<ValueType>* upperX, std::vector<ValueType> const& b, bool relative, ValueType precision, uint64_t maxOverallIterations, boost::optional<storm::solver::OptimizationDirection> dir, boost::optional<storm::storage::BitVector> const& relevantValues);
private:
oviinternal::IterationHelper<ValueType> iterationHelper;
if (overallIterations > maxOverallIterations) {
status = SolverStatus::MaximalIterationsExceeded;
}
return {status, overallIterations};
}
};
}
}
}

4
src/storm/storage/dd/sylvan/InternalSylvanAdd.cpp

@ -328,8 +328,8 @@ namespace storm {
}
template<>
InternalAdd<DdType::Sylvan, storm::RationalNumber> InternalAdd<DdType::Sylvan, storm::RationalNumber>::mod(InternalAdd<DdType::Sylvan, storm::RationalNumber> const&) const {
STORM_LOG_THROW(false, storm::exceptions::InvalidOperationException, "Operation (mod) not supported by rational numbers.");
InternalAdd<DdType::Sylvan, storm::RationalNumber> InternalAdd<DdType::Sylvan, storm::RationalNumber>::mod(InternalAdd<DdType::Sylvan, storm::RationalNumber> const& other) const {
return InternalAdd<DdType::Sylvan, storm::RationalNumber>(ddManager, this->sylvanMtbdd.ModRN(other.sylvanMtbdd));
}
#ifdef STORM_HAVE_CARL

28
src/storm/utility/Portfolio.cpp → src/storm/utility/AutomaticSettings.cpp

@ -1,4 +1,4 @@
#include "storm/utility/Portfolio.h"
#include "storm/utility/AutomaticSettings.h"
#include <sstream>
@ -90,14 +90,14 @@ namespace storm {
};
}
Portfolio::Portfolio() : engine(storm::utility::Engine::Unknown), useBisimulation(false), useExact(false) {
AutomaticSettings::AutomaticSettings() : engine(storm::utility::Engine::Unknown), useBisimulation(false), useExact(false) {
// Intentionally left empty
}
void Portfolio::predict(storm::jani::Model const& model, storm::jani::Property const& property) {
void AutomaticSettings::predict(storm::jani::Model const& model, storm::jani::Property const& property) {
typedef pfinternal::PropertyType PropertyType;
auto f = pfinternal::Features(model, property);
STORM_LOG_INFO("Portfolio engine using features " << f.toString() << ".");
STORM_LOG_INFO("Automatic engine using features " << f.toString() << ".");
if (f.numVariables <= 12) {
if (f.avgDomainSize <= 323.25) {
@ -218,54 +218,54 @@ namespace storm {
}
}
void Portfolio::predict(storm::jani::Model const& model, storm::jani::Property const& property, uint64_t) {
void AutomaticSettings::predict(storm::jani::Model const& model, storm::jani::Property const& property, uint64_t) {
//typedef pfinternal::PropertyType PropertyType;
//auto f = pfinternal::Features(model, property);
//f.stateEstimate = stateEstimate;
//STORM_LOG_INFO("Portfolio engine using features " << f.toString() << ".");
//STORM_LOG_INFO("Automatic engine using features " << f.toString() << ".");
// Right now, we do not make use of the state estimate, so we just ask the 'default' decision tree.
predict(model, property);
}
storm::utility::Engine Portfolio::getEngine() const {
storm::utility::Engine AutomaticSettings::getEngine() const {
STORM_LOG_THROW(engine != storm::utility::Engine::Unknown, storm::exceptions::InvalidOperationException, "Tried to get the engine but apparently no prediction was done before.");
return engine;
}
bool Portfolio::enableBisimulation() const {
bool AutomaticSettings::enableBisimulation() const {
return useBisimulation;
}
bool Portfolio::enableExact() const {
bool AutomaticSettings::enableExact() const {
return useExact;
}
void Portfolio::sparse() {
void AutomaticSettings::sparse() {
engine = storm::utility::Engine::Sparse;
useBisimulation = false;
useExact = false;
}
void Portfolio::hybrid() {
void AutomaticSettings::hybrid() {
engine = storm::utility::Engine::Hybrid;
useBisimulation = false;
useExact = false;
}
void Portfolio::dd() {
void AutomaticSettings::dd() {
engine = storm::utility::Engine::Dd;
useBisimulation = false;
useExact = false;
}
void Portfolio::exact() {
void AutomaticSettings::exact() {
engine = storm::utility::Engine::Sparse;
useBisimulation = false;
useExact = true;
}
void Portfolio::ddbisim() {
void AutomaticSettings::ddbisim() {
engine = storm::utility::Engine::DdSparse;
useBisimulation = true;
useExact = false;

4
src/storm/utility/Portfolio.h → src/storm/utility/AutomaticSettings.h

@ -10,9 +10,9 @@ namespace storm {
}
namespace utility {
class Portfolio {
class AutomaticSettings {
public:
Portfolio();
AutomaticSettings();
/*!
* Predicts "good" settings for the provided model checking query

8
src/storm/utility/Engine.cpp

@ -51,8 +51,8 @@ namespace storm {
return "expl";
case Engine::AbstractionRefinement:
return "abs";
case Engine::Portfolio:
return "portfolio";
case Engine::Automatic:
return "automatic";
case Engine::Unknown:
return "UNKNOWN";
default:
@ -72,6 +72,10 @@ namespace storm {
return e;
}
}
if (engineStr == "portfolio") {
STORM_LOG_WARN("The engine name \"portfolio\" is deprecated. The name of this engine has been changed to \"" << toString(Engine::Automatic) << "\".");
return Engine::Automatic;
}
STORM_LOG_ERROR("The engine '" << engineStr << "' was not found.");
return Engine::Unknown;
}

2
src/storm/utility/Engine.h

@ -30,7 +30,7 @@ namespace storm {
/// An enumeration of all engines.
enum class Engine {
// The last one should always be 'Unknown' to make sure that the getEngines() method below works.
Sparse, Hybrid, Dd, DdSparse, Jit, Exploration, AbstractionRefinement, Portfolio, Unknown
Sparse, Hybrid, Dd, DdSparse, Jit, Exploration, AbstractionRefinement, Automatic, Unknown
};
/*!

17
src/test/storm/modelchecker/csl/LraCtmcCslModelCheckerTest.cpp

@ -209,6 +209,20 @@ namespace {
}
};
class SoundEnvironment {
public:
static const storm::dd::DdType ddType = storm::dd::DdType::Sylvan; // unused for sparse models
static const CtmcEngine engine = CtmcEngine::PrismSparse;
static const bool isExact = false;
typedef double ValueType;
typedef storm::models::sparse::Ctmc<ValueType> ModelType;
static storm::Environment createEnvironment() {
storm::Environment env;
env.solver().setForceSoundness(true);
return env;
}
};
template<typename TestType>
class LraCtmcCslModelCheckerTest : public ::testing::Test {
public:
@ -317,7 +331,8 @@ namespace {
GBSparseNativeSorEnvironment,
DistrSparseGmmxxGmresIluEnvironment,
DistrSparseEigenDoubleLUEnvironment,
ValueIterationSparseEnvironment
ValueIterationSparseEnvironment,
SoundEnvironment
> TestingTypes;
TYPED_TEST_SUITE(LraCtmcCslModelCheckerTest, TestingTypes,);

15
src/test/storm/modelchecker/prctl/mdp/LraMdpPrctlModelCheckerTest.cpp

@ -53,6 +53,18 @@ namespace {
}
};
class SparseSoundEnvironment {
public:
static const bool isExact = false;
typedef double ValueType;
typedef storm::models::sparse::Mdp<ValueType> ModelType;
static storm::Environment createEnvironment() {
storm::Environment env;
env.solver().setForceSoundness(true);
return env;
}
};
class SparseRationalLinearProgrammingEnvironment {
public:
static const bool isExact = true;
@ -100,7 +112,8 @@ namespace {
typedef ::testing::Types<
SparseValueTypeValueIterationEnvironment,
SparseValueTypeLinearProgrammingEnvironment
SparseValueTypeLinearProgrammingEnvironment,
SparseSoundEnvironment
#ifdef STORM_HAVE_Z3_OPTIMIZE
, SparseRationalLinearProgrammingEnvironment
#endif

2
version.cmake

@ -1,4 +1,4 @@
set(STORM_VERSION_MAJOR 1)
set(STORM_VERSION_MINOR 6)
set(STORM_VERSION_PATCH 0)
set(STORM_VERSION_PATCH 2)
Loading…
Cancel
Save