Browse Source

Merge branch 'master' into monitoring

tempestpy_adaptions
Sebastian Junges 4 years ago
parent
commit
3c682057ea
  1. 3
      CHANGELOG.md
  2. 2
      CMakeLists.txt
  3. 7
      resources/3rdparty/sylvan/src/storm_wrapper.cpp
  4. 1
      resources/3rdparty/sylvan/src/sylvan_obj_mtbdd_storm.hpp
  5. 6
      resources/3rdparty/sylvan/src/sylvan_obj_storm.cpp
  6. 40
      src/storm-cli-utilities/model-handling.h
  7. 5
      src/storm-pomdp/transformer/BinaryPomdpTransformer.cpp
  8. 4
      src/storm-pomdp/transformer/BinaryPomdpTransformer.h
  9. 19
      src/storm-pomdp/transformer/PomdpMemoryUnfolder.cpp
  10. 5
      src/storm-pomdp/transformer/PomdpMemoryUnfolder.h
  11. 27
      src/storm/builder/DdPrismModelBuilder.cpp
  12. 2
      src/storm/modelchecker/helper/infinitehorizon/SparseDeterministicInfiniteHorizonHelper.cpp
  13. 10
      src/storm/modelchecker/helper/infinitehorizon/SparseInfiniteHorizonHelper.cpp
  14. 2
      src/storm/settings/modules/CoreSettings.cpp
  15. 17
      src/storm/solver/IterativeMinMaxLinearEquationSolver.cpp
  16. 14
      src/storm/solver/NativeLinearEquationSolver.cpp
  17. 13
      src/storm/solver/TopologicalLinearEquationSolver.cpp
  18. 11
      src/storm/solver/TopologicalMinMaxLinearEquationSolver.cpp
  19. 362
      src/storm/solver/helper/OptimisticValueIterationHelper.cpp
  20. 359
      src/storm/solver/helper/OptimisticValueIterationHelper.h
  21. 4
      src/storm/storage/dd/sylvan/InternalSylvanAdd.cpp
  22. 10
      src/storm/storage/prism/Module.cpp
  23. 5
      src/storm/storage/prism/Module.h
  24. 10
      src/storm/storage/prism/Program.cpp
  25. 7
      src/storm/storage/prism/Program.h
  26. 8
      src/storm/storage/sparse/StateValuations.cpp
  27. 2
      src/storm/storage/sparse/StateValuations.h
  28. 28
      src/storm/utility/AutomaticSettings.cpp
  29. 4
      src/storm/utility/AutomaticSettings.h
  30. 8
      src/storm/utility/Engine.cpp
  31. 2
      src/storm/utility/Engine.h
  32. 17
      src/test/storm/modelchecker/csl/LraCtmcCslModelCheckerTest.cpp
  33. 15
      src/test/storm/modelchecker/prctl/mdp/LraMdpPrctlModelCheckerTest.cpp

3
CHANGELOG.md

@ -11,7 +11,8 @@ Version 1.6.x
## Version 1.6.1 (??)
- 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.

2
CMakeLists.txt

@ -445,7 +445,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()

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;

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;

5
src/storm-pomdp/transformer/BinaryPomdpTransformer.cpp

@ -15,7 +15,7 @@ namespace storm {
}
template<typename ValueType>
std::shared_ptr<storm::models::sparse::Pomdp<ValueType>> BinaryPomdpTransformer<ValueType>::transform(storm::models::sparse::Pomdp<ValueType> const& pomdp, bool transformSimple) const {
std::shared_ptr<storm::models::sparse::Pomdp<ValueType>> BinaryPomdpTransformer<ValueType>::transform(storm::models::sparse::Pomdp<ValueType> const& pomdp, bool transformSimple, bool keepStateValuations) const {
auto data = transformTransitions(pomdp, transformSimple);
storm::storage::sparse::ModelComponents<ValueType> components;
components.stateLabeling = transformStateLabeling(pomdp, data);
@ -24,6 +24,9 @@ namespace storm {
}
components.transitionMatrix = std::move(data.simpleMatrix);
components.observabilityClasses = std::move(data.simpleObservations);
if (keepStateValuations && pomdp.hasStateValuations()) {
components.stateValuations = pomdp.getStateValuations().blowup(data.simpleStateToOriginalState);
}
return std::make_shared<storm::models::sparse::Pomdp<ValueType>>(std::move(components), true);
}

4
src/storm-pomdp/transformer/BinaryPomdpTransformer.h

@ -13,7 +13,7 @@ namespace storm {
BinaryPomdpTransformer();
std::shared_ptr<storm::models::sparse::Pomdp<ValueType>> transform(storm::models::sparse::Pomdp<ValueType> const& pomdp, bool transformSimple) const;
std::shared_ptr<storm::models::sparse::Pomdp<ValueType>> transform(storm::models::sparse::Pomdp<ValueType> const& pomdp, bool transformSimple, bool keepStateValuations = false) const;
private:
@ -27,7 +27,7 @@ namespace storm {
TransformationData transformTransitions(storm::models::sparse::Pomdp<ValueType> const& pomdp, bool transformSimple) const;
storm::models::sparse::StateLabeling transformStateLabeling(storm::models::sparse::Pomdp<ValueType> const& pomdp, TransformationData const& data) const;
storm::models::sparse::StandardRewardModel<ValueType> transformRewardModel(storm::models::sparse::Pomdp<ValueType> const& pomdp, storm::models::sparse::StandardRewardModel<ValueType> const& rewardModel, TransformationData const& data) const;
storm::models::sparse::ChoiceLabeling transformChoiceLabeling(storm::models::sparse::Pomdp<ValueType> const& pomdp, TransformationData const& data) const;
};
}
}

19
src/storm-pomdp/transformer/PomdpMemoryUnfolder.cpp

@ -11,7 +11,8 @@ namespace storm {
template<typename ValueType>
PomdpMemoryUnfolder<ValueType>::PomdpMemoryUnfolder(storm::models::sparse::Pomdp<ValueType> const& pomdp, storm::storage::PomdpMemory const& memory) : pomdp(pomdp), memory(memory) {
PomdpMemoryUnfolder<ValueType>::PomdpMemoryUnfolder(storm::models::sparse::Pomdp<ValueType> const& pomdp, storm::storage::PomdpMemory const& memory, bool addMemoryLabels, bool keepStateValuations)
: pomdp(pomdp), memory(memory), addMemoryLabels(addMemoryLabels), keepStateValuations(keepStateValuations) {
// intentionally left empty
}
@ -29,6 +30,13 @@ namespace storm {
auto reachableStates = storm::utility::graph::getReachableStates(components.transitionMatrix, components.stateLabeling.getStates("init"), allStates, ~allStates);
components.transitionMatrix = components.transitionMatrix.getSubmatrix(true, reachableStates, reachableStates);
components.stateLabeling = components.stateLabeling.getSubLabeling(reachableStates);
if (keepStateValuations && pomdp.hasStateValuations()) {
std::vector<uint64_t> newToOldStates(pomdp.getNumberOfStates() * memory.getNumberOfStates(), 0);
for (uint64_t newState = 0; newState < newToOldStates.size(); newState++) {
newToOldStates[newState] = getModelState(newState);
}
components.stateValuations = pomdp.getStateValuations().blowup(newToOldStates).selectStates(reachableStates);
}
// build the remaining components
components.observabilityClasses = transformObservabilityClasses(reachableStates);
@ -94,6 +102,15 @@ namespace storm {
}
labeling.addLabel(labelName, std::move(newStates));
}
if (addMemoryLabels) {
for (uint64_t memState = 0; memState < memory.getNumberOfStates(); ++memState) {
storm::storage::BitVector newStates(pomdp.getNumberOfStates() * memory.getNumberOfStates(), false);
for (uint64_t modelState = 0; modelState < pomdp.getNumberOfStates(); ++modelState) {
newStates.set(getUnfoldingState(modelState, memState));
}
labeling.addLabel("memstate_"+std::to_string(memState), newStates);
}
}
return labeling;
}

5
src/storm-pomdp/transformer/PomdpMemoryUnfolder.h

@ -12,7 +12,7 @@ namespace storm {
public:
PomdpMemoryUnfolder(storm::models::sparse::Pomdp<ValueType> const& pomdp, storm::storage::PomdpMemory const& memory);
PomdpMemoryUnfolder(storm::models::sparse::Pomdp<ValueType> const& pomdp, storm::storage::PomdpMemory const& memory, bool addMemoryLabels = false, bool keepStateValuations = false);
std::shared_ptr<storm::models::sparse::Pomdp<ValueType>> transform() const;
@ -33,6 +33,9 @@ namespace storm {
storm::models::sparse::Pomdp<ValueType> const& pomdp;
storm::storage::PomdpMemory const& memory;
bool addMemoryLabels;
bool keepStateValuations;
};
}
}

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) {

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());

17
src/storm/solver/IterativeMinMaxLinearEquationSolver.cpp

@ -360,6 +360,15 @@ 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 (!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 (!this->multiplierA) {
this->multiplierA = storm::solver::MultiplierFactory<ValueType>().create(env, *this->A);
}
@ -393,11 +402,12 @@ namespace storm {
this->startMeasureProgress();
auto statusIters = storm::solver::helper::solveEquationsOptimisticValueIteration(env, lowerX, upperX, auxVector,
storm::solver::helper::OptimisticValueIterationHelper<ValueType> helper(*this->A);
auto statusIters = helper.solveEquationsOptimisticValueIteration(env, lowerX, upperX, auxVector, b,
[&] (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);
auto result = performValueIteration(env, dir, y, yPrime, b, precision, relative, guarantee, i, maxI, multiplicationStyle);
return std::make_pair(result.iterations, result.status);
},
[&] (std::vector<ValueType>* y, std::vector<ValueType>* yPrime, uint64_t const& i) {
this->showProgressIterative(i);
@ -414,6 +424,7 @@ namespace storm {
env.solver().minMax().getRelativeTerminationCriterion(),
storm::utility::convertNumber<ValueType>(env.solver().minMax().getPrecision()),
env.solver().minMax().getMaximalNumberOfIterations(),
dir,
relevantValues);
auto two = storm::utility::convertNumber<ValueType>(2.0);
storm::utility::vector::applyPointwise<ValueType, ValueType, ValueType>(*lowerX, *upperX, x, [&two] (ValueType const& a, ValueType const& b) -> ValueType { return (a + b) / two; });

14
src/storm/solver/NativeLinearEquationSolver.cpp

@ -637,6 +637,12 @@ namespace storm {
template<typename ValueType>
bool NativeLinearEquationSolver<ValueType>::solveEquationsOptimisticValueIteration(Environment const& env, std::vector<ValueType>& x, std::vector<ValueType> const& b) const {
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->multiplier) {
this->multiplier = storm::solver::MultiplierFactory<ValueType>().create(env, *this->A);
}
@ -669,11 +675,12 @@ namespace storm {
std::vector<ValueType>* auxVector = this->cachedRowVector2.get();
this->startMeasureProgress();
auto statusIters = storm::solver::helper::solveEquationsOptimisticValueIteration(env, lowerX, upperX, auxVector,
storm::solver::helper::OptimisticValueIterationHelper<ValueType> helper(*this->A);
auto statusIters = helper.solveEquationsOptimisticValueIteration(env, lowerX, upperX, auxVector, b,
[&] (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);
auto result = performPowerIteration(env, y, yPrime, b, precision, relative, guarantee, i, maxI, multiplicationStyle);
return std::make_pair(result.iterations, result.status);
},
[&] (std::vector<ValueType>* y, std::vector<ValueType>* yPrime, uint64_t const& i) {
this->showProgressIterative(i);
@ -690,6 +697,7 @@ namespace storm {
env.solver().native().getRelativeTerminationCriterion(),
storm::utility::convertNumber<ValueType>(env.solver().native().getPrecision()),
env.solver().native().getMaximalNumberOfIterations(),
boost::none, // No optimization dir
relevantValues);
auto two = storm::utility::convertNumber<ValueType>(2.0);
storm::utility::vector::applyPointwise<ValueType, ValueType, ValueType>(*lowerX, *upperX, x, [&two] (ValueType const& a, ValueType const& b) -> ValueType { return (a + b) / two; });

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;

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

@ -0,0 +1,362 @@
#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 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>
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; });
}
template <typename ValueType>
UpperBoundIterator<ValueType>::UpperBoundIterator(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>
typename UpperBoundIterator<ValueType>::IterateResult UpperBoundIterator<ValueType>::iterate(std::vector<ValueType>& x, std::vector<ValueType> const& b, bool takeMinOfOldAndNew) {
return iterateInternal<false, storm::solver::OptimizationDirection::Minimize>(x, b, takeMinOfOldAndNew);
}
template <typename ValueType>
typename UpperBoundIterator<ValueType>::IterateResult UpperBoundIterator<ValueType>::iterate(storm::solver::OptimizationDirection const& dir, std::vector<ValueType>& x, std::vector<ValueType> const& b, bool takeMinOfOldAndNew) {
if (minimize(dir)) {
return iterateInternal<true, storm::solver::OptimizationDirection::Minimize>(x, b, takeMinOfOldAndNew);
} else {
return iterateInternal<true, storm::solver::OptimizationDirection::Maximize>(x, b, takeMinOfOldAndNew);
}
}
template <typename ValueType>
template<bool HasRowGroups, storm::solver::OptimizationDirection Dir>
typename UpperBoundIterator<ValueType>::IterateResult UpperBoundIterator<ValueType>::iterateInternal(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 UpperBoundIterator<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 UpperBoundIterator<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) : upperBoundIterator(matrix) {
// Intentionally left empty.
}
template<typename ValueType>
std::pair<SolverStatus, uint64_t> OptimisticValueIterationHelper<ValueType>::solveEquationsOptimisticValueIteration(Environment const& env, std::vector<ValueType>* lowerX, std::vector<ValueType>* upperX, std::vector<ValueType>* auxVector, std::vector<ValueType> const& b, ValueIterationCallBackType const& valueIterationCallback, SingleIterationCallBackType const& singleIterationCallback, bool relative, ValueType precision, uint64_t maxOverallIterations, boost::optional<storm::solver::OptimizationDirection> dir, boost::optional<storm::storage::BitVector> relevantValues) {
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;
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.first;
overallIterations += result.first;
if (result.second != SolverStatus::Converged) {
status = result.second;
} 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
auto upperBoundIterResult = dir ? upperBoundIterator.iterate(dir.get(), *upperX, b, !noTerminationGuarantee) : upperBoundIterator.iterate(*upperX, b, !noTerminationGuarantee);
if (upperBoundIterResult == oviinternal::UpperBoundIterator<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
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 (upperBoundIterResult == oviinternal::UpperBoundIterator<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::UpperBoundIterator<ValueType>::IterateResult::Equal) {
// In this case, the guessed upper bound is the precise fixpoint
// status = SolverStatus::Converged;
// std::swap(lowerX, auxVector);
// 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 >= 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);
}
}
if (overallIterations > maxOverallIterations) {
status = SolverStatus::MaximalIterationsExceeded;
}
return {status, overallIterations};
}
template class OptimisticValueIterationHelper<double>;
template class OptimisticValueIterationHelper<storm::RationalNumber>;
}
}
}

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

@ -3,309 +3,104 @@
#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 UpperBoundIterator {
public:
typedef uint32_t IndexType;
UpperBoundIterator(storm::storage::SparseMatrix<ValueType> const& matrix);
enum class IterateResult {
AlwaysHigherOrEqual,
AlwaysLowerOrEqual,
Equal,
Incomparable
};
IterateResult iterate(std::vector<ValueType>& x, std::vector<ValueType> const& b, bool takeMinOfOldAndNew);
IterateResult iterate(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>
IterateResult iterateInternal(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
/*!
* Return type of value iteration callback.
* The first component shall be the number of performed iterations, the second component is the final convergence status.
*/
typedef std::pair<uint64_t, storm::solver::SolverStatus> ValueIterationReturnType;
// 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);
}
}
/*!
* Value iteration callback. Performs conventional value iteration for the given input.
* @pre y points to a vector that contains starting values
* @post y points to a vector that contains resulting values
* @param yPrime points to auxiliary storage
* @param precision is the target precision
* @param relative sets whether relative precision is considered
* @param maxI the maximal number of iterations to perform
*/
typedef std::function<ValueIterationReturnType(std::vector<ValueType>*& y, std::vector<ValueType>*& yPrime, ValueType const& precision, bool const& relative, uint64_t const& i, uint64_t const& maxI)> ValueIterationCallBackType;
if (overallIterations > maxOverallIterations) {
status = SolverStatus::MaximalIterationsExceeded;
}
/*!
* Should perform a single value iteration step (using conventional value iteration).
* @pre y points to a vector that contains starting values
* @post y points to a vector that contains resulting values
* @param yPrime points to auxiliary storage
* @param currI the current iteration (can be used to display progress within the callback)
*/
typedef std::function<void(std::vector<ValueType>* y, std::vector<ValueType>* yPrime, uint64_t const& currI)> SingleIterationCallBackType;
return {status, overallIterations};
}
/*!
* @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 (same size as lowerX and upperX)
* @param b the values added to each matrix row (the b in A*x+b)
* @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 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> solveEquationsOptimisticValueIteration(Environment const& env, std::vector<ValueType>* lowerX, std::vector<ValueType>* upperX, std::vector<ValueType>* auxVector, std::vector<ValueType> const& b, ValueIterationCallBackType const& valueIterationCallback, SingleIterationCallBackType const& singleIterationCallback, bool relative, ValueType precision, uint64_t maxOverallIterations, boost::optional<storm::solver::OptimizationDirection> dir, boost::optional<storm::storage::BitVector> relevantValues = boost::none);
private:
oviinternal::UpperBoundIterator<ValueType> upperBoundIterator;
};
}
}
}

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

10
src/storm/storage/prism/Module.cpp

@ -114,6 +114,16 @@ namespace storm {
bool Module::hasActionIndex(uint_fast64_t actionIndex) const {
return this->actionIndicesToCommandIndexMap.find(actionIndex) != this->actionIndicesToCommandIndexMap.end();
}
uint64_t Module::getNumberOfUnlabeledCommands() const {
uint64_t result = 0;
for (auto const& cmd : commands) {
if(!cmd.isLabeled()) {
result++;
}
}
return result;
}
bool Module::isRenamedFromModule() const {
return this->renamedFromModule != "";

5
src/storm/storage/prism/Module.h

@ -263,6 +263,11 @@ namespace storm {
* Equips all of the modules' variables without initial values with initial values based on their type.
*/
void createMissingInitialValues();
/*
* Gets the number of commands without a label
*/
uint64_t getNumberOfUnlabeledCommands() const;
/*!
* Returns true, if an invariant was specified (only relevant for PTA models)

10
src/storm/storage/prism/Program.cpp

@ -508,7 +508,15 @@ namespace storm {
std::map<std::string, uint_fast64_t> const& Program::getActionNameToIndexMapping() const {
return actionToIndexMap;
}
uint64_t Program::getNumberOfUnlabeledCommands() const {
uint64_t result = 0;
for (auto const& m : modules) {
result += m.getNumberOfUnlabeledCommands();
}
return result;
}
bool Program::hasInitialConstruct() const {
return static_cast<bool>(initialConstruct);
}

7
src/storm/storage/prism/Program.h

@ -462,7 +462,12 @@ namespace storm {
* @return the index of the module and the (local) index of the found command
*/
std::pair<uint_fast64_t, uint_fast64_t> getModuleCommandIndexByGlobalCommandIndex(uint_fast64_t globalCommandIndex) const;
/*
* Get total number of unlabeled commands
*/
uint64_t getNumberOfUnlabeledCommands() const;
/*!
* Retrieves whether the program has reward models.
*

8
src/storm/storage/sparse/StateValuations.cpp

@ -296,6 +296,14 @@ namespace storm {
}
return StateValuations(variableToIndexMap, std::move(selectedValuations));
}
StateValuations StateValuations::blowup(const std::vector<uint64_t> &mapNewToOld) const {
std::vector<StateValuation> newValuations;
for( auto const& oldState : mapNewToOld) {
newValuations.push_back(valuations[oldState]);
}
return StateValuations(variableToIndexMap, std::move(newValuations));
}
StateValuationsBuilder::StateValuationsBuilder() : booleanVarCount(0), integerVarCount(0), rationalVarCount(0), labelCount(0) {
// Intentionally left empty.

2
src/storm/storage/sparse/StateValuations.h

@ -129,6 +129,8 @@ namespace storm {
*/
StateValuations selectStates(std::vector<storm::storage::sparse::state_type> const& selectedStates) const;
StateValuations blowup(std::vector<uint64_t> const& mapNewToOld) const;
virtual std::size_t hash() const;
private:

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

Loading…
Cancel
Save