diff --git a/resources/3rdparty/l3pp b/resources/3rdparty/l3pp new file mode 160000 index 0000000000..e4f8d7fe6c --- /dev/null +++ b/resources/3rdparty/l3pp @@ -0,0 +1 @@ +Subproject commit e4f8d7fe6c328849aff34d2dfd6fd592c14070d5 diff --git a/src/storm-cli-utilities/model-handling.h b/src/storm-cli-utilities/model-handling.h index 5d80120439..2394c58bcc 100644 --- a/src/storm-cli-utilities/model-handling.h +++ b/src/storm-cli-utilities/model-handling.h @@ -154,7 +154,7 @@ struct ModelProcessingInformation { bool transformToJani; // Which data type is to be used for numbers ... - enum class ValueType { FinitePrecision, Exact, Parametric }; + enum class ValueType { FinitePrecision, Exact, Parametric, FinitePrecisionInterval, ExactInterval }; ValueType buildValueType; // ... during model building ValueType verificationValueType; // ... during model verification @@ -222,9 +222,17 @@ inline ModelProcessingInformation getModelProcessingInformation(SymbolicInput co if (generalSettings.isParametricSet()) { mpi.verificationValueType = ModelProcessingInformation::ValueType::Parametric; } else if (generalSettings.isExactSet()) { - mpi.verificationValueType = ModelProcessingInformation::ValueType::Exact; + if (generalSettings.isIntervalSet()) { + mpi.verificationValueType = ModelProcessingInformation::ValueType::ExactInterval; + } else { + mpi.verificationValueType = ModelProcessingInformation::ValueType::Exact; + } } else { - mpi.verificationValueType = ModelProcessingInformation::ValueType::FinitePrecision; + if (generalSettings.isIntervalSet()) { + mpi.verificationValueType = ModelProcessingInformation::ValueType::FinitePrecisionInterval; + } else { + mpi.verificationValueType = ModelProcessingInformation::ValueType::FinitePrecision; + } } auto originalVerificationValueType = mpi.verificationValueType; @@ -270,6 +278,12 @@ inline ModelProcessingInformation getModelProcessingInformation(SymbolicInput co case ModelProcessingInformation::ValueType::FinitePrecision: return storm::utility::canHandle( mpi.engine, input.preprocessedProperties.is_initialized() ? input.preprocessedProperties.get() : input.properties, input.model.get()); + case ModelProcessingInformation::ValueType::FinitePrecisionInterval: + return storm::utility::canHandle( + mpi.engine, input.preprocessedProperties.is_initialized() ? input.preprocessedProperties.get() : input.properties, input.model.get()); + case ModelProcessingInformation::ValueType::ExactInterval: + return storm::utility::canHandle( + mpi.engine, input.preprocessedProperties.is_initialized() ? input.preprocessedProperties.get() : input.properties, input.model.get()); } return false; }; @@ -381,6 +395,10 @@ auto applyValueType(ModelProcessingInformation::ValueType vt, auto const& callba return callback.template operator()(); case Parametric: return callback.template operator()(); + case FinitePrecisionInterval: + return callback.template operator()(); + case ExactInterval: + return callback.template operator()(); } STORM_LOG_THROW(false, storm::exceptions::UnexpectedException, "Unexpected value type."); } @@ -400,6 +418,10 @@ auto applyDdLibValueType(storm::dd::DdType dd, ModelProcessingInformation::Value return callback.template operator()(); case Parametric: return callback.template operator()(); + case FinitePrecisionInterval: + return callback.template operator()(); + case ExactInterval: + return callback.template operator()(); } } STORM_LOG_THROW(false, storm::exceptions::UnexpectedException, "Unexpected DDType or value type."); @@ -591,8 +613,12 @@ std::shared_ptr buildModelExplicit(storm::settings::mo storm::parser::DirectEncodingValueType valueType{Default}; if constexpr (std::is_same_v) { valueType = Double; + } else if constexpr (std::is_same_v) { + valueType = DoubleInterval; } else if constexpr (std::is_same_v) { valueType = Rational; + } else if constexpr (std::is_same_v) { + valueType = RationalInterval; } else { static_assert(std::is_same_v, "Unexpected value type."); valueType = Parametric; @@ -1285,30 +1311,38 @@ inline std::vector> parseInjectedRef template void verifyWithAbstractionRefinementEngine(SymbolicInput const& input, ModelProcessingInformation const& mpi) { - STORM_LOG_ASSERT(input.model, "Expected symbolic model description."); - storm::settings::modules::AbstractionSettings const& abstractionSettings = storm::settings::getModule(); - storm::gbar::api::AbstractionRefinementOptions options( - parseConstraints(input.model->getManager(), abstractionSettings.getConstraintString()), - parseInjectedRefinementPredicates(input.model->getManager(), abstractionSettings.getInjectedRefinementPredicates())); - - verifyProperties(input, [&input, &options, &mpi](std::shared_ptr const& formula, - std::shared_ptr const& states) { - STORM_LOG_THROW(states->isInitialFormula(), storm::exceptions::NotSupportedException, "Abstraction-refinement can only filter initial states."); - return storm::gbar::api::verifyWithAbstractionRefinementEngine(mpi.env, input.model.get(), - storm::api::createTask(formula, true), options); - }); + if constexpr (storm::IsIntervalType) { + STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Abstraction-refinement engine does not support interval value types."); + } else { + STORM_LOG_ASSERT(input.model, "Expected symbolic model description."); + storm::settings::modules::AbstractionSettings const& abstractionSettings = storm::settings::getModule(); + storm::gbar::api::AbstractionRefinementOptions options( + parseConstraints(input.model->getManager(), abstractionSettings.getConstraintString()), + parseInjectedRefinementPredicates(input.model->getManager(), abstractionSettings.getInjectedRefinementPredicates())); + + verifyProperties(input, [&input, &options, &mpi](std::shared_ptr const& formula, + std::shared_ptr const& states) { + STORM_LOG_THROW(states->isInitialFormula(), storm::exceptions::NotSupportedException, "Abstraction-refinement can only filter initial states."); + return storm::gbar::api::verifyWithAbstractionRefinementEngine(mpi.env, input.model.get(), + storm::api::createTask(formula, true), options); + }); + } } template void verifyWithExplorationEngine(SymbolicInput const& input, ModelProcessingInformation const& mpi) { - STORM_LOG_ASSERT(input.model, "Expected symbolic model description."); - STORM_LOG_THROW((std::is_same::value), storm::exceptions::NotSupportedException, - "Exploration does not support other data-types than floating points."); - verifyProperties( - input, [&input, &mpi](std::shared_ptr const& formula, std::shared_ptr const& states) { - STORM_LOG_THROW(states->isInitialFormula(), storm::exceptions::NotSupportedException, "Exploration can only filter initial states."); - return storm::api::verifyWithExplorationEngine(mpi.env, input.model.get(), storm::api::createTask(formula, true)); - }); + if constexpr (storm::IsIntervalType) { + STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Exploration engine does not support interval value types."); + } else { + STORM_LOG_ASSERT(input.model, "Expected symbolic model description."); + STORM_LOG_THROW((std::is_same::value), storm::exceptions::NotSupportedException, + "Exploration does not support other data-types than floating points."); + verifyProperties( + input, [&input, &mpi](std::shared_ptr const& formula, std::shared_ptr const& states) { + STORM_LOG_THROW(states->isInitialFormula(), storm::exceptions::NotSupportedException, "Exploration can only filter initial states."); + return storm::api::verifyWithExplorationEngine(mpi.env, input.model.get(), storm::api::createTask(formula, true)); + }); + } } template @@ -1336,7 +1370,7 @@ void verifyModel(std::shared_ptr> const& std::unique_ptr filter; if (filterForInitialStates) { - filter = std::make_unique(sparseModel->getInitialStates()); + filter = std::make_unique>(sparseModel->getInitialStates()); } else if (!states->isTrueFormula()) { // No need to apply filter if it is the formula 'true' filter = storm::api::verifyWithSparseEngine(mpi.env, sparseModel, createTask(states, false)); } @@ -1382,6 +1416,13 @@ void verifyModel(std::shared_ptr> const& auto const& paretoRes = result->template asExplicitParetoCurveCheckResult(); storm::api::exportParetoScheduler(sparseModel, paretoRes.getPoints(), paretoRes.getSchedulers(), schedulerExportPath.string()); } + } else if (result->isExplicitQualitativeCheckResult()) { + if constexpr (storm::IsIntervalType) { + STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Scheduler export for interval models is not supported."); + } else { + storm::api::exportScheduler(sparseModel, result->template asExplicitQualitativeCheckResult().getScheduler(), + schedulerExportPath.string()); + } } else { STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Scheduler export not supported for this value type."); } diff --git a/src/storm-counterexamples/api/counterexamples.cpp b/src/storm-counterexamples/api/counterexamples.cpp index 6738bf2239..af0104a901 100644 --- a/src/storm-counterexamples/api/counterexamples.cpp +++ b/src/storm-counterexamples/api/counterexamples.cpp @@ -45,7 +45,7 @@ std::shared_ptr computeKShortestPathCoun storm::logic::EventuallyFormula const& eventuallyFormula = subformula.asEventuallyFormula(); std::unique_ptr subResult = modelchecker.check(env, eventuallyFormula.getSubformula()); - storm::modelchecker::ExplicitQualitativeCheckResult const& subQualitativeResult = subResult->asExplicitQualitativeCheckResult(); + storm::modelchecker::ExplicitQualitativeCheckResult const& subQualitativeResult = subResult->template asExplicitQualitativeCheckResult(); // Check if counterexample is even possible storm::storage::BitVector phiStates(model->getNumberOfStates(), true); diff --git a/src/storm-counterexamples/counterexamples/MILPMinimalLabelSetGenerator.h b/src/storm-counterexamples/counterexamples/MILPMinimalLabelSetGenerator.h index 2abc0ca9f1..8bc1e7c82b 100644 --- a/src/storm-counterexamples/counterexamples/MILPMinimalLabelSetGenerator.h +++ b/src/storm-counterexamples/counterexamples/MILPMinimalLabelSetGenerator.h @@ -1022,8 +1022,8 @@ class MILPMinimalLabelSetGenerator { std::unique_ptr leftResult = modelchecker.check(env, untilFormula.getLeftSubformula()); std::unique_ptr rightResult = modelchecker.check(env, untilFormula.getRightSubformula()); - storm::modelchecker::ExplicitQualitativeCheckResult const& leftQualitativeResult = leftResult->asExplicitQualitativeCheckResult(); - storm::modelchecker::ExplicitQualitativeCheckResult const& rightQualitativeResult = rightResult->asExplicitQualitativeCheckResult(); + storm::modelchecker::ExplicitQualitativeCheckResult const& leftQualitativeResult = leftResult->template asExplicitQualitativeCheckResult(); + storm::modelchecker::ExplicitQualitativeCheckResult const& rightQualitativeResult = rightResult->template asExplicitQualitativeCheckResult(); phiStates = leftQualitativeResult.getTruthValuesVector(); psiStates = rightQualitativeResult.getTruthValuesVector(); @@ -1032,7 +1032,7 @@ class MILPMinimalLabelSetGenerator { std::unique_ptr subResult = modelchecker.check(env, eventuallyFormula.getSubformula()); - storm::modelchecker::ExplicitQualitativeCheckResult const& subQualitativeResult = subResult->asExplicitQualitativeCheckResult(); + storm::modelchecker::ExplicitQualitativeCheckResult const& subQualitativeResult = subResult->template asExplicitQualitativeCheckResult(); phiStates = storm::storage::BitVector(mdp.getNumberOfStates(), true); psiStates = subQualitativeResult.getTruthValuesVector(); diff --git a/src/storm-counterexamples/counterexamples/SMTMinimalLabelSetGenerator.h b/src/storm-counterexamples/counterexamples/SMTMinimalLabelSetGenerator.h index 4cf453c738..5a837289ae 100644 --- a/src/storm-counterexamples/counterexamples/SMTMinimalLabelSetGenerator.h +++ b/src/storm-counterexamples/counterexamples/SMTMinimalLabelSetGenerator.h @@ -2157,8 +2157,8 @@ class SMTMinimalLabelSetGenerator { std::unique_ptr leftResult = modelchecker.check(env, untilFormula.getLeftSubformula()); std::unique_ptr rightResult = modelchecker.check(env, untilFormula.getRightSubformula()); - storm::modelchecker::ExplicitQualitativeCheckResult const& leftQualitativeResult = leftResult->asExplicitQualitativeCheckResult(); - storm::modelchecker::ExplicitQualitativeCheckResult const& rightQualitativeResult = rightResult->asExplicitQualitativeCheckResult(); + storm::modelchecker::ExplicitQualitativeCheckResult const& leftQualitativeResult = leftResult->template asExplicitQualitativeCheckResult(); + storm::modelchecker::ExplicitQualitativeCheckResult const& rightQualitativeResult = rightResult->template asExplicitQualitativeCheckResult(); result.phiStates = leftQualitativeResult.getTruthValuesVector(); result.psiStates = rightQualitativeResult.getTruthValuesVector(); @@ -2167,7 +2167,7 @@ class SMTMinimalLabelSetGenerator { std::unique_ptr subResult = modelchecker.check(env, eventuallyFormula.getSubformula()); - storm::modelchecker::ExplicitQualitativeCheckResult const& subQualitativeResult = subResult->asExplicitQualitativeCheckResult(); + storm::modelchecker::ExplicitQualitativeCheckResult const& subQualitativeResult = subResult->template asExplicitQualitativeCheckResult(); result.phiStates = storm::storage::BitVector(model.getNumberOfStates(), true); result.psiStates = subQualitativeResult.getTruthValuesVector(); diff --git a/src/storm-dft/modelchecker/DFTModelChecker.cpp b/src/storm-dft/modelchecker/DFTModelChecker.cpp index 1581d7eabf..d22ed14067 100644 --- a/src/storm-dft/modelchecker/DFTModelChecker.cpp +++ b/src/storm-dft/modelchecker/DFTModelChecker.cpp @@ -463,7 +463,7 @@ std::vector DFTModelChecker::checkModel(std::shared_ptr(model, storm::api::createTask(property, true))); if (result) { - result->filter(storm::modelchecker::ExplicitQualitativeCheckResult(model->getInitialStates())); + result->filter(storm::modelchecker::ExplicitQualitativeCheckResult(model->getInitialStates())); ValueType resultValue = result->asExplicitQuantitativeCheckResult().getValueMap().begin()->second; results.push_back(resultValue); } else { diff --git a/src/storm-pars-cli/sampling.h b/src/storm-pars-cli/sampling.h index 351ec5c661..968c44d121 100644 --- a/src/storm-pars-cli/sampling.h +++ b/src/storm-pars-cli/sampling.h @@ -115,7 +115,7 @@ void verifyPropertiesAtSamplePoints(ModelType const& model, cli::SymbolicInput c valuationWatch.stop(); if (result) { - result->filter(storm::modelchecker::ExplicitQualitativeCheckResult(model.getInitialStates())); + result->filter(storm::modelchecker::ExplicitQualitativeCheckResult(model.getInitialStates())); } printInitialStatesResult(result, &valuationWatch, &valuation); diff --git a/src/storm-pars-cli/solutionFunctions.h b/src/storm-pars-cli/solutionFunctions.h index 294c50c56b..8c74e278ff 100644 --- a/src/storm-pars-cli/solutionFunctions.h +++ b/src/storm-pars-cli/solutionFunctions.h @@ -31,7 +31,7 @@ void computeSolutionFunctionsWithSparseEngine(std::shared_ptr result = storm::api::verifyWithSparseEngine(model, storm::api::createTask(formula, true)); if (result) { - result->filter(storm::modelchecker::ExplicitQualitativeCheckResult(model->getInitialStates())); + result->filter(storm::modelchecker::ExplicitQualitativeCheckResult(model->getInitialStates())); } return result; }, diff --git a/src/storm-pars/derivative/SparseDerivativeInstantiationModelChecker.cpp b/src/storm-pars/derivative/SparseDerivativeInstantiationModelChecker.cpp index 5bf5ec4f12..8f57bb92f7 100644 --- a/src/storm-pars/derivative/SparseDerivativeInstantiationModelChecker.cpp +++ b/src/storm-pars/derivative/SparseDerivativeInstantiationModelChecker.cpp @@ -143,20 +143,20 @@ void SparseDerivativeInstantiationModelChecker::spec if (this->currentFormula->isRewardOperatorFormula()) { auto subformula = modelchecker::CheckTask( this->currentFormula->asRewardOperatorFormula().getSubformula().asEventuallyFormula().getSubformula()); - target = propositionalChecker.check(subformula)->asExplicitQualitativeCheckResult().getTruthValuesVector(); + target = propositionalChecker.check(subformula)->template asExplicitQualitativeCheckResult().getTruthValuesVector(); } else { if (this->currentFormula->asProbabilityOperatorFormula().getSubformula().isUntilFormula()) { auto rightSubformula = modelchecker::CheckTask( this->currentFormula->asProbabilityOperatorFormula().getSubformula().asUntilFormula().getRightSubformula()); auto leftSubformula = modelchecker::CheckTask( this->currentFormula->asProbabilityOperatorFormula().getSubformula().asUntilFormula().getLeftSubformula()); - target = propositionalChecker.check(rightSubformula)->asExplicitQualitativeCheckResult().getTruthValuesVector(); - avoid = propositionalChecker.check(leftSubformula)->asExplicitQualitativeCheckResult().getTruthValuesVector(); + target = propositionalChecker.check(rightSubformula)->template asExplicitQualitativeCheckResult().getTruthValuesVector(); + avoid = propositionalChecker.check(leftSubformula)->template asExplicitQualitativeCheckResult().getTruthValuesVector(); avoid.complement(); } else { auto subformula = modelchecker::CheckTask( this->currentFormula->asProbabilityOperatorFormula().getSubformula().asEventuallyFormula().getSubformula()); - target = propositionalChecker.check(subformula)->asExplicitQualitativeCheckResult().getTruthValuesVector(); + target = propositionalChecker.check(subformula)->template asExplicitQualitativeCheckResult().getTruthValuesVector(); } } initialStateModel = model.getStates("init").getNextSetIndex(0); diff --git a/src/storm-pars/modelchecker/instantiation/SparseDtmcInstantiationModelChecker.cpp b/src/storm-pars/modelchecker/instantiation/SparseDtmcInstantiationModelChecker.cpp index 6ec8bae93e..1c9b77c72c 100644 --- a/src/storm-pars/modelchecker/instantiation/SparseDtmcInstantiationModelChecker.cpp +++ b/src/storm-pars/modelchecker/instantiation/SparseDtmcInstantiationModelChecker.cpp @@ -190,7 +190,7 @@ std::unique_ptr SparseDtmcInstantiationModelChecker0 steps std::unique_ptr subFormulaResult = modelChecker.check(env, this->currentCheckTask->getFormula().asOperatorFormula().getSubformula().asBoundedUntilFormula().getRightSubformula()); - maybeStates = maybeStates & ~(subFormulaResult->asExplicitQualitativeCheckResult().getTruthValuesVector()); + maybeStates = maybeStates & ~(subFormulaResult->template asExplicitQualitativeCheckResult().getTruthValuesVector()); hint.setMaybeStates(std::move(maybeStates)); hint.setComputeOnlyMaybeStates(true); } else { diff --git a/src/storm-pars/modelchecker/instantiation/SparseMdpInstantiationModelChecker.cpp b/src/storm-pars/modelchecker/instantiation/SparseMdpInstantiationModelChecker.cpp index e43100655c..40f8b2ad91 100644 --- a/src/storm-pars/modelchecker/instantiation/SparseMdpInstantiationModelChecker.cpp +++ b/src/storm-pars/modelchecker/instantiation/SparseMdpInstantiationModelChecker.cpp @@ -230,7 +230,7 @@ std::unique_ptr SparseMdpInstantiationModelChecker0 steps std::unique_ptr subFormulaResult = modelChecker.check(env, this->currentCheckTask->getFormula().asOperatorFormula().getSubformula().asBoundedUntilFormula().getRightSubformula()); - maybeStates = maybeStates & ~(subFormulaResult->asExplicitQualitativeCheckResult().getTruthValuesVector()); + maybeStates = maybeStates & ~(subFormulaResult->template asExplicitQualitativeCheckResult().getTruthValuesVector()); hint.setMaybeStates(std::move(maybeStates)); hint.setComputeOnlyMaybeStates(true); } else { diff --git a/src/storm-pars/modelchecker/region/SparseDtmcParameterLiftingModelChecker.cpp b/src/storm-pars/modelchecker/region/SparseDtmcParameterLiftingModelChecker.cpp index 9d4b9f4e82..8f13cd6e25 100644 --- a/src/storm-pars/modelchecker/region/SparseDtmcParameterLiftingModelChecker.cpp +++ b/src/storm-pars/modelchecker/region/SparseDtmcParameterLiftingModelChecker.cpp @@ -148,10 +148,12 @@ void SparseDtmcParameterLiftingModelCheckerasExplicitQualitativeCheckResult().getTruthValuesVector()); - storm::storage::BitVector psiStates = - std::move(propositionalChecker.check(checkTask.getFormula().getRightSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector()); + storm::storage::BitVector phiStates = std::move(propositionalChecker.check(checkTask.getFormula().getLeftSubformula()) + ->template asExplicitQualitativeCheckResult() + .getTruthValuesVector()); + storm::storage::BitVector psiStates = std::move(propositionalChecker.check(checkTask.getFormula().getRightSubformula()) + ->template asExplicitQualitativeCheckResult() + .getTruthValuesVector()); // get the maybeStates maybeStates = storm::utility::graph::performProbGreater0(this->parametricModel->getBackwardTransitions(), phiStates, psiStates, true, *stepBound); @@ -190,10 +192,12 @@ void SparseDtmcParameterLiftingModelCheckerasExplicitQualitativeCheckResult().getTruthValuesVector()); - storm::storage::BitVector psiStates = - std::move(propositionalChecker.check(checkTask.getFormula().getRightSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector()); + storm::storage::BitVector phiStates = std::move(propositionalChecker.check(checkTask.getFormula().getLeftSubformula()) + ->template asExplicitQualitativeCheckResult() + .getTruthValuesVector()); + storm::storage::BitVector psiStates = std::move(propositionalChecker.check(checkTask.getFormula().getRightSubformula()) + ->template asExplicitQualitativeCheckResult() + .getTruthValuesVector()); // get the maybeStates std::pair statesWithProbability01 = @@ -262,8 +266,9 @@ void SparseDtmcParameterLiftingModelChecker propositionalChecker(*this->parametricModel); STORM_LOG_THROW(propositionalChecker.canHandle(checkTask.getFormula().getSubformula()), storm::exceptions::NotSupportedException, "Parameter lifting with non-propositional subformulas is not supported"); - storm::storage::BitVector targetStates = - std::move(propositionalChecker.check(checkTask.getFormula().getSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector()); + storm::storage::BitVector targetStates = std::move(propositionalChecker.check(checkTask.getFormula().getSubformula()) + ->template asExplicitQualitativeCheckResult() + .getTruthValuesVector()); // get the maybeStates storm::storage::BitVector infinityStates = storm::utility::graph::performProb1( this->parametricModel->getBackwardTransitions(), storm::storage::BitVector(this->parametricModel->getNumberOfStates(), true), targetStates); diff --git a/src/storm-pars/modelchecker/region/SparseMdpParameterLiftingModelChecker.cpp b/src/storm-pars/modelchecker/region/SparseMdpParameterLiftingModelChecker.cpp index 22d948e1e2..e2789d6d84 100644 --- a/src/storm-pars/modelchecker/region/SparseMdpParameterLiftingModelChecker.cpp +++ b/src/storm-pars/modelchecker/region/SparseMdpParameterLiftingModelChecker.cpp @@ -108,10 +108,12 @@ void SparseMdpParameterLiftingModelChecker::speci STORM_LOG_THROW(propositionalChecker.canHandle(checkTask.getFormula().getLeftSubformula()) && propositionalChecker.canHandle(checkTask.getFormula().getRightSubformula()), storm::exceptions::NotSupportedException, "Parameter lifting with non-propositional subformulas is not supported"); - storm::storage::BitVector phiStates = - std::move(propositionalChecker.check(checkTask.getFormula().getLeftSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector()); - storm::storage::BitVector psiStates = - std::move(propositionalChecker.check(checkTask.getFormula().getRightSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector()); + storm::storage::BitVector phiStates = std::move(propositionalChecker.check(checkTask.getFormula().getLeftSubformula()) + ->template asExplicitQualitativeCheckResult() + .getTruthValuesVector()); + storm::storage::BitVector psiStates = std::move(propositionalChecker.check(checkTask.getFormula().getRightSubformula()) + ->template asExplicitQualitativeCheckResult() + .getTruthValuesVector()); // get the maybeStates maybeStates = storm::solver::minimize(checkTask.getOptimizationDirection()) @@ -151,10 +153,12 @@ void SparseMdpParameterLiftingModelChecker::speci STORM_LOG_THROW(propositionalChecker.canHandle(checkTask.getFormula().getLeftSubformula()) && propositionalChecker.canHandle(checkTask.getFormula().getRightSubformula()), storm::exceptions::NotSupportedException, "Parameter lifting with non-propositional subformulas is not supported"); - storm::storage::BitVector phiStates = - std::move(propositionalChecker.check(checkTask.getFormula().getLeftSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector()); - storm::storage::BitVector psiStates = - std::move(propositionalChecker.check(checkTask.getFormula().getRightSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector()); + storm::storage::BitVector phiStates = std::move(propositionalChecker.check(checkTask.getFormula().getLeftSubformula()) + ->template asExplicitQualitativeCheckResult() + .getTruthValuesVector()); + storm::storage::BitVector psiStates = std::move(propositionalChecker.check(checkTask.getFormula().getRightSubformula()) + ->template asExplicitQualitativeCheckResult() + .getTruthValuesVector()); // get the maybeStates std::pair statesWithProbability01 = @@ -202,8 +206,9 @@ void SparseMdpParameterLiftingModelChecker::speci storm::modelchecker::SparsePropositionalModelChecker propositionalChecker(*this->parametricModel); STORM_LOG_THROW(propositionalChecker.canHandle(checkTask.getFormula().getSubformula()), storm::exceptions::NotSupportedException, "Parameter lifting with non-propositional subformulas is not supported"); - storm::storage::BitVector targetStates = - std::move(propositionalChecker.check(checkTask.getFormula().getSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector()); + storm::storage::BitVector targetStates = std::move(propositionalChecker.check(checkTask.getFormula().getSubformula()) + ->template asExplicitQualitativeCheckResult() + .getTruthValuesVector()); // get the maybeStates storm::storage::BitVector infinityStates = diff --git a/src/storm-pars/modelchecker/region/SparseParameterLiftingModelChecker.cpp b/src/storm-pars/modelchecker/region/SparseParameterLiftingModelChecker.cpp index 69964a1cff..0cb3c6a252 100644 --- a/src/storm-pars/modelchecker/region/SparseParameterLiftingModelChecker.cpp +++ b/src/storm-pars/modelchecker/region/SparseParameterLiftingModelChecker.cpp @@ -101,15 +101,16 @@ RegionResult SparseParameterLiftingModelChecker:: (result == RegionResult::Unknown || result == RegionResult::ExistsIllDefined || result == RegionResult::CenterIllDefined)) { auto const center = region.region.getCenterPoint(); if (getInstantiationChecker(false).isWellDefined(center)) { - result = getInstantiationChecker(false).check(env, center)->asExplicitQualitativeCheckResult()[getUniqueInitialState()] + result = getInstantiationChecker(false).check(env, center)->template asExplicitQualitativeCheckResult()[getUniqueInitialState()] ? RegionResult::CenterSat : RegionResult::CenterViolated; } else { auto const lowerCorner = region.region.getLowerBoundaries(); if (getInstantiationChecker(false).isWellDefined(lowerCorner)) { - result = getInstantiationChecker(false).check(env, lowerCorner)->asExplicitQualitativeCheckResult()[getUniqueInitialState()] - ? RegionResult::ExistsSat - : RegionResult::ExistsViolated; + result = + getInstantiationChecker(false).check(env, lowerCorner)->template asExplicitQualitativeCheckResult()[getUniqueInitialState()] + ? RegionResult::ExistsSat + : RegionResult::ExistsViolated; } else { result = RegionResult::CenterIllDefined; } @@ -143,7 +144,7 @@ RegionResult SparseParameterLiftingModelChecker:: auto const valuation = getOptimalValuationForMonotonicity(region.region, globalMonotonicity->getMonotonicityResult(), dirToCheck); STORM_LOG_ASSERT(valuation.size() == region.region.getVariables().size(), "Not all parameters seem to be monotonic."); auto& checker = existsSat ? getInstantiationCheckerSAT(false) : getInstantiationCheckerVIO(false); - bool const monCheckResult = checker.check(env, valuation)->asExplicitQualitativeCheckResult()[getUniqueInitialState()]; + bool const monCheckResult = checker.check(env, valuation)->template asExplicitQualitativeCheckResult()[getUniqueInitialState()]; if (existsSat == monCheckResult) { result = existsSat ? RegionResult::AllSat : RegionResult::AllViolated; STORM_LOG_INFO("Region " << region.region << " is " << result << ", discovered with instantiation checker on " << valuation @@ -165,7 +166,7 @@ RegionResult SparseParameterLiftingModelChecker:: // Try to prove AllSat or AllViolated through parameterLifting auto const checkResult = this->check(env, region, dirToCheck); if (checkResult) { - bool const value = checkResult->asExplicitQualitativeCheckResult()[getUniqueInitialState()]; + bool const value = checkResult->template asExplicitQualitativeCheckResult()[getUniqueInitialState()]; if ((dirToCheck == dirForSat) == value) { result = (dirToCheck == dirForSat) ? RegionResult::AllSat : RegionResult::AllViolated; } else if (sampleVerticesOfRegion) { @@ -196,7 +197,7 @@ RegionResult SparseParameterLiftingModelChecker:: auto vertices = region.getVerticesOfRegion(region.getVariables()); auto vertexIt = vertices.begin(); while (vertexIt != vertices.end() && !(hasSatPoint && hasViolatedPoint)) { - if (getInstantiationChecker(false).check(env, *vertexIt)->asExplicitQualitativeCheckResult()[getUniqueInitialState()]) { + if (getInstantiationChecker(false).check(env, *vertexIt)->template asExplicitQualitativeCheckResult()[getUniqueInitialState()]) { hasSatPoint = true; } else { hasViolatedPoint = true; diff --git a/src/storm-pars/modelchecker/region/ValidatingSparseParameterLiftingModelChecker.cpp b/src/storm-pars/modelchecker/region/ValidatingSparseParameterLiftingModelChecker.cpp index 05fa65ef00..184b549475 100644 --- a/src/storm-pars/modelchecker/region/ValidatingSparseParameterLiftingModelChecker.cpp +++ b/src/storm-pars/modelchecker/region/ValidatingSparseParameterLiftingModelChecker.cpp @@ -91,8 +91,9 @@ RegionResult ValidatingSparseParameterLiftingModelCheckerasExplicitQualitativeCheckResult()[*preciseChecker.getConsideredParametricModel().getInitialStates().begin()]; + bool preciseResult = + preciseChecker.check(env, region, parameterOptDir) + ->template asExplicitQualitativeCheckResult()[*preciseChecker.getConsideredParametricModel().getInitialStates().begin()]; bool preciseResultAgrees = preciseResult == (currentResult == RegionResult::AllSat); if (!preciseResultAgrees) { @@ -103,8 +104,9 @@ RegionResult ValidatingSparseParameterLiftingModelCheckerasExplicitQualitativeCheckResult()[*preciseChecker.getConsideredParametricModel().getInitialStates().begin()]; + preciseResult = + preciseChecker.check(env, region, parameterOptDir) + ->template asExplicitQualitativeCheckResult()[*preciseChecker.getConsideredParametricModel().getInitialStates().begin()]; if (preciseResult && parameterOptDir == preciseChecker.getCurrentCheckTask().getOptimizationDirection()) { currentResult = RegionResult::AllSat; } else if (!preciseResult && parameterOptDir == storm::solver::invert(preciseChecker.getCurrentCheckTask().getOptimizationDirection())) { diff --git a/src/storm-pars/modelchecker/region/monotonicity/OrderExtender.cpp b/src/storm-pars/modelchecker/region/monotonicity/OrderExtender.cpp index 90cabf4968..a57d2d9ead 100644 --- a/src/storm-pars/modelchecker/region/monotonicity/OrderExtender.cpp +++ b/src/storm-pars/modelchecker/region/monotonicity/OrderExtender.cpp @@ -102,16 +102,16 @@ std::shared_ptr OrderExtender::getBottomTopOrder assert(formula->isProbabilityOperatorFormula()); if (formula->asProbabilityOperatorFormula().getSubformula().isUntilFormula()) { phiStates = propositionalChecker.check(formula->asProbabilityOperatorFormula().getSubformula().asUntilFormula().getLeftSubformula()) - ->asExplicitQualitativeCheckResult() + ->template asExplicitQualitativeCheckResult() .getTruthValuesVector(); psiStates = propositionalChecker.check(formula->asProbabilityOperatorFormula().getSubformula().asUntilFormula().getRightSubformula()) - ->asExplicitQualitativeCheckResult() + ->template asExplicitQualitativeCheckResult() .getTruthValuesVector(); } else { assert(formula->asProbabilityOperatorFormula().getSubformula().isEventuallyFormula()); phiStates = storage::BitVector(numberOfStates, true); psiStates = propositionalChecker.check(formula->asProbabilityOperatorFormula().getSubformula().asEventuallyFormula().getSubformula()) - ->asExplicitQualitativeCheckResult() + ->template asExplicitQualitativeCheckResult() .getTruthValuesVector(); } // Get the maybeStates diff --git a/src/storm-pars/transformer/SparseParametricDtmcSimplifier.cpp b/src/storm-pars/transformer/SparseParametricDtmcSimplifier.cpp index ea7ab04cad..8ddebc5dca 100644 --- a/src/storm-pars/transformer/SparseParametricDtmcSimplifier.cpp +++ b/src/storm-pars/transformer/SparseParametricDtmcSimplifier.cpp @@ -28,10 +28,12 @@ bool SparseParametricDtmcSimplifier::simplifyForUntilProbabilit STORM_LOG_DEBUG("Can not simplify when Until-formula has non-propositional subformula(s). Formula: " << formula); return false; } - storm::storage::BitVector phiStates = std::move( - propositionalChecker.check(formula.getSubformula().asUntilFormula().getLeftSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector()); - storm::storage::BitVector psiStates = std::move( - propositionalChecker.check(formula.getSubformula().asUntilFormula().getRightSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector()); + storm::storage::BitVector phiStates = std::move(propositionalChecker.check(formula.getSubformula().asUntilFormula().getLeftSubformula()) + ->template asExplicitQualitativeCheckResult() + .getTruthValuesVector()); + storm::storage::BitVector psiStates = std::move(propositionalChecker.check(formula.getSubformula().asUntilFormula().getRightSubformula()) + ->template asExplicitQualitativeCheckResult() + .getTruthValuesVector()); std::pair statesWithProbability01 = storm::utility::graph::performProb01(this->originalModel, phiStates, psiStates); // Only consider the maybestates that are reachable from one initial state without hopping over a target (i.e., prob1) state @@ -96,10 +98,10 @@ bool SparseParametricDtmcSimplifier::simplifyForBoundedUntilPro return false; } storm::storage::BitVector phiStates = std::move(propositionalChecker.check(formula.getSubformula().asBoundedUntilFormula().getLeftSubformula()) - ->asExplicitQualitativeCheckResult() + ->template asExplicitQualitativeCheckResult() .getTruthValuesVector()); storm::storage::BitVector psiStates = std::move(propositionalChecker.check(formula.getSubformula().asBoundedUntilFormula().getRightSubformula()) - ->asExplicitQualitativeCheckResult() + ->template asExplicitQualitativeCheckResult() .getTruthValuesVector()); storm::storage::BitVector probGreater0States = storm::utility::graph::performProbGreater0(this->originalModel.getBackwardTransitions(), phiStates, psiStates, true, upperStepBound); @@ -150,8 +152,9 @@ bool SparseParametricDtmcSimplifier::simplifyForReachabilityRew STORM_LOG_DEBUG("Can not simplify when reachability reward formula has non-propositional subformula(s). Formula: " << formula); return false; } - storm::storage::BitVector targetStates = std::move( - propositionalChecker.check(formula.getSubformula().asEventuallyFormula().getSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector()); + storm::storage::BitVector targetStates = std::move(propositionalChecker.check(formula.getSubformula().asEventuallyFormula().getSubformula()) + ->template asExplicitQualitativeCheckResult() + .getTruthValuesVector()); // The set of target states can be extended by the states that reach target with probability 1 without collecting any reward targetStates = storm::utility::graph::performProb1(this->originalModel.getBackwardTransitions(), originalRewardModel.getStatesWithZeroReward(this->originalModel.getTransitionMatrix()), targetStates); diff --git a/src/storm-pars/transformer/SparseParametricMdpSimplifier.cpp b/src/storm-pars/transformer/SparseParametricMdpSimplifier.cpp index 30fc623608..10f41f3fca 100644 --- a/src/storm-pars/transformer/SparseParametricMdpSimplifier.cpp +++ b/src/storm-pars/transformer/SparseParametricMdpSimplifier.cpp @@ -33,10 +33,12 @@ bool SparseParametricMdpSimplifier::simplifyForUntilProbabiliti STORM_LOG_DEBUG("Can not simplify when Until-formula has non-propositional subformula(s). Formula: " << formula); return false; } - storm::storage::BitVector phiStates = std::move( - propositionalChecker.check(formula.getSubformula().asUntilFormula().getLeftSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector()); - storm::storage::BitVector psiStates = std::move( - propositionalChecker.check(formula.getSubformula().asUntilFormula().getRightSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector()); + storm::storage::BitVector phiStates = std::move(propositionalChecker.check(formula.getSubformula().asUntilFormula().getLeftSubformula()) + ->template asExplicitQualitativeCheckResult() + .getTruthValuesVector()); + storm::storage::BitVector psiStates = std::move(propositionalChecker.check(formula.getSubformula().asUntilFormula().getRightSubformula()) + ->template asExplicitQualitativeCheckResult() + .getTruthValuesVector()); std::pair statesWithProbability01 = minimizing ? storm::utility::graph::performProb01Min(this->originalModel, phiStates, psiStates) : storm::utility::graph::performProb01Max(this->originalModel, phiStates, psiStates); @@ -120,10 +122,10 @@ bool SparseParametricMdpSimplifier::simplifyForBoundedUntilProb return false; } storm::storage::BitVector phiStates = std::move(propositionalChecker.check(formula.getSubformula().asBoundedUntilFormula().getLeftSubformula()) - ->asExplicitQualitativeCheckResult() + ->template asExplicitQualitativeCheckResult() .getTruthValuesVector()); storm::storage::BitVector psiStates = std::move(propositionalChecker.check(formula.getSubformula().asBoundedUntilFormula().getRightSubformula()) - ->asExplicitQualitativeCheckResult() + ->template asExplicitQualitativeCheckResult() .getTruthValuesVector()); storm::storage::BitVector probGreater0States = minimizing ? storm::utility::graph::performProbGreater0A(this->originalModel.getTransitionMatrix(), @@ -180,8 +182,9 @@ bool SparseParametricMdpSimplifier::simplifyForReachabilityRewa STORM_LOG_DEBUG("Can not simplify when reachability reward formula has non-propositional subformula(s). Formula: " << formula); return false; } - storm::storage::BitVector targetStates = std::move( - propositionalChecker.check(formula.getSubformula().asEventuallyFormula().getSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector()); + storm::storage::BitVector targetStates = std::move(propositionalChecker.check(formula.getSubformula().asEventuallyFormula().getSubformula()) + ->template asExplicitQualitativeCheckResult() + .getTruthValuesVector()); // The set of target states can be extended by the states that reach target with probability 1 without collecting any reward // TODO for the call of Prob1E we could restrict the analysis to actions with zero reward instead of states with zero reward targetStates = diff --git a/src/storm-parsers/parser/ValueParser.cpp b/src/storm-parsers/parser/ValueParser.cpp index 00df851f84..4bb0e35e88 100644 --- a/src/storm-parsers/parser/ValueParser.cpp +++ b/src/storm-parsers/parser/ValueParser.cpp @@ -119,6 +119,51 @@ bool parseInterval(std::string const& value, storm::Interval& result) { return true; } +bool parseRationalInterval(std::string const& value, storm::RationalInterval& result) { + // Try whether it is a constant. + if (storm::RationalNumber pointResult; parseNumber(value, pointResult)) { + result = storm::RationalInterval(pointResult); + return true; + } + + std::string intermediate = value; + boost::trim(intermediate); + carl::BoundType leftBound; + carl::BoundType rightBound; + if (intermediate.front() == '(') { + leftBound = carl::BoundType::STRICT; + } else if (intermediate.front() == '[') { + leftBound = carl::BoundType::WEAK; + } else { + return false; // Expect start with '(' or '['. + } + if (intermediate.back() == ')') { + rightBound = carl::BoundType::STRICT; + } else if (intermediate.back() == ']') { + rightBound = carl::BoundType::WEAK; + } else { + return false; // Expected end with ')' or ']'. + } + intermediate = intermediate.substr(1, intermediate.size() - 2); + + std::vector words; + boost::split(words, intermediate, boost::is_any_of(",")); + if (words.size() != 2) { + return false; // Did not find exactly one comma. + } + storm::RationalNumber leftVal, rightVal; + boost::trim(words[0]); + if (!parseNumber(words[0], leftVal)) { + return false; // lower value of interval invalid. + } + boost::trim(words[1]); + if (!parseNumber(words[1], rightVal)) { + return false; // upper value of interval invalid. + } + result = storm::RationalInterval(leftVal, leftBound, rightVal, rightBound); + return true; +} + template bool parseNumber(std::string const& value, NumberType& result) { if constexpr (std::is_same_v) { @@ -127,6 +172,8 @@ bool parseNumber(std::string const& value, NumberType& result) { return carl::try_parse(value, result); } else if constexpr (std::is_same_v) { return parseInterval(value, result); + } else if constexpr (std::is_same_v) { + return parseRationalInterval(value, result); } else { return boost::conversion::try_lexical_convert(value, result); } @@ -137,6 +184,7 @@ template class ValueParser; template class ValueParser; template class ValueParser; template class ValueParser; +template class ValueParser; template std::size_t parseNumber(std::string const&); diff --git a/src/storm-permissive/analysis/PermissiveSchedulers.cpp b/src/storm-permissive/analysis/PermissiveSchedulers.cpp index 1ed34897c8..4e7908b7b3 100644 --- a/src/storm-permissive/analysis/PermissiveSchedulers.cpp +++ b/src/storm-permissive/analysis/PermissiveSchedulers.cpp @@ -20,8 +20,9 @@ boost::optional> computePermissiveSchedulerViaMILP storm::modelchecker::SparsePropositionalModelChecker> propMC(mdp); STORM_LOG_ASSERT(safeProp.getSubformula().isEventuallyFormula(), "No eventually formula."); auto backwardTransitions = mdp.getBackwardTransitions(); - storm::storage::BitVector goalstates = - propMC.check(safeProp.getSubformula().asEventuallyFormula().getSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector(); + storm::storage::BitVector goalstates = propMC.check(safeProp.getSubformula().asEventuallyFormula().getSubformula()) + ->template asExplicitQualitativeCheckResult() + .getTruthValuesVector(); goalstates = storm::utility::graph::performProb1A(mdp, backwardTransitions, storm::storage::BitVector(goalstates.size(), true), goalstates); storm::storage::BitVector sinkstates = storm::utility::graph::performProb0A(backwardTransitions, storm::storage::BitVector(goalstates.size(), true), goalstates); @@ -45,8 +46,9 @@ boost::optional> computePermissiveSchedulerViaSMT( storm::modelchecker::SparsePropositionalModelChecker> propMC(mdp); STORM_LOG_ASSERT(safeProp.getSubformula().isEventuallyFormula(), "No eventually formula."); auto backwardTransitions = mdp.getBackwardTransitions(); - storm::storage::BitVector goalstates = - propMC.check(safeProp.getSubformula().asEventuallyFormula().getSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector(); + storm::storage::BitVector goalstates = propMC.check(safeProp.getSubformula().asEventuallyFormula().getSubformula()) + ->template asExplicitQualitativeCheckResult() + .getTruthValuesVector(); goalstates = storm::utility::graph::performProb1A(mdp, backwardTransitions, storm::storage::BitVector(goalstates.size(), true), goalstates); storm::storage::BitVector sinkstates = storm::utility::graph::performProb0A(backwardTransitions, storm::storage::BitVector(goalstates.size(), true), goalstates); diff --git a/src/storm-pomdp-cli/settings/PomdpSettings.cpp b/src/storm-pomdp-cli/settings/PomdpSettings.cpp index 7c7cdeb4fa..be6c2f48cd 100644 --- a/src/storm-pomdp-cli/settings/PomdpSettings.cpp +++ b/src/storm-pomdp-cli/settings/PomdpSettings.cpp @@ -30,6 +30,7 @@ #include "storm/settings/modules/TransformationSettings.h" #include "storm-pomdp-cli/settings/modules/BeliefExplorationSettings.h" +#include "storm-pomdp-cli/settings/modules/GenerateMonitorVerifierSettings.h" #include "storm-pomdp-cli/settings/modules/POMDPSettings.h" #include "storm-pomdp-cli/settings/modules/QualitativePOMDPAnalysisSettings.h" #include "storm-pomdp-cli/settings/modules/ToParametricSettings.h" @@ -68,6 +69,7 @@ void initializePomdpSettings(std::string const& name, std::string const& executa storm::settings::addModule(); storm::settings::addModule(); storm::settings::addModule(); + storm::settings::addModule(); } } // namespace settings } // namespace storm diff --git a/src/storm-pomdp-cli/settings/modules/GenerateMonitorVerifierSettings.cpp b/src/storm-pomdp-cli/settings/modules/GenerateMonitorVerifierSettings.cpp new file mode 100644 index 0000000000..e2baab8be1 --- /dev/null +++ b/src/storm-pomdp-cli/settings/modules/GenerateMonitorVerifierSettings.cpp @@ -0,0 +1,13 @@ +#include "storm-pomdp-cli/settings/modules/GenerateMonitorVerifierSettings.h" + +namespace storm { +namespace settings { +namespace modules { + +const std::string GenerateMonitorVerifierSettings::moduleName = "generateMonitorVerifier"; + +GenerateMonitorVerifierSettings::GenerateMonitorVerifierSettings() : ModuleSettings(moduleName) {} + +} // namespace modules +} // namespace settings +} // namespace storm diff --git a/src/storm-pomdp-cli/settings/modules/GenerateMonitorVerifierSettings.h b/src/storm-pomdp-cli/settings/modules/GenerateMonitorVerifierSettings.h new file mode 100644 index 0000000000..08ad112aba --- /dev/null +++ b/src/storm-pomdp-cli/settings/modules/GenerateMonitorVerifierSettings.h @@ -0,0 +1,21 @@ +#pragma once + +#include "storm/settings/modules/ModuleSettings.h" + +namespace storm { +namespace settings { +namespace modules { + +class GenerateMonitorVerifierSettings : public ModuleSettings { + public: + GenerateMonitorVerifierSettings(); + + virtual ~GenerateMonitorVerifierSettings() = default; + + // The name of the module. + static const std::string moduleName; +}; + +} // namespace modules +} // namespace settings +} // namespace storm diff --git a/src/storm-pomdp-cli/storm-pomdp.cpp b/src/storm-pomdp-cli/storm-pomdp.cpp index 45805c4cc7..45f5595dcb 100644 --- a/src/storm-pomdp-cli/storm-pomdp.cpp +++ b/src/storm-pomdp-cli/storm-pomdp.cpp @@ -273,7 +273,7 @@ bool performAnalysis(std::shared_ptr> co storm::api::createTask(formula.asSharedPointer(), true)); if (resultPtr) { auto result = resultPtr->template asExplicitQuantitativeCheckResult(); - result.filter(storm::modelchecker::ExplicitQualitativeCheckResult(pomdp->getInitialStates())); + result.filter(storm::modelchecker::ExplicitQualitativeCheckResult(pomdp->getInitialStates())); if (storm::utility::resources::isTerminate()) { STORM_PRINT_AND_LOG("\nResult till abort: "); } else { diff --git a/src/storm-pomdp/analysis/FormulaInformation.cpp b/src/storm-pomdp/analysis/FormulaInformation.cpp index e51e7672e6..18517e8119 100644 --- a/src/storm-pomdp/analysis/FormulaInformation.cpp +++ b/src/storm-pomdp/analysis/FormulaInformation.cpp @@ -114,7 +114,7 @@ template storm::storage::BitVector getStates(storm::logic::Formula const& propositionalFormula, bool formulaInverted, PomdpType const& pomdp) { storm::modelchecker::SparsePropositionalModelChecker mc(pomdp); auto checkResult = mc.check(propositionalFormula); - storm::storage::BitVector resultBitVector(checkResult->asExplicitQualitativeCheckResult().getTruthValuesVector()); + storm::storage::BitVector resultBitVector(checkResult->template asExplicitQualitativeCheckResult().getTruthValuesVector()); if (formulaInverted) { resultBitVector.complement(); } diff --git a/src/storm-pomdp/analysis/QualitativeAnalysisOnGraphs.cpp b/src/storm-pomdp/analysis/QualitativeAnalysisOnGraphs.cpp index 8ddf6bdc04..792c3b65ed 100644 --- a/src/storm-pomdp/analysis/QualitativeAnalysisOnGraphs.cpp +++ b/src/storm-pomdp/analysis/QualitativeAnalysisOnGraphs.cpp @@ -201,7 +201,7 @@ storm::storage::BitVector QualitativeAnalysisOnGraphs::checkPropositi storm::modelchecker::SparsePropositionalModelChecker> mc(pomdp); STORM_LOG_THROW(mc.canHandle(propositionalFormula), storm::exceptions::InvalidPropertyException, "Propositional model checker can not handle formula " << propositionalFormula); - return mc.check(propositionalFormula)->asExplicitQualitativeCheckResult().getTruthValuesVector(); + return mc.check(propositionalFormula)->template asExplicitQualitativeCheckResult().getTruthValuesVector(); } template class QualitativeAnalysisOnGraphs; diff --git a/src/storm-pomdp/generator/GenerateMonitorVerifier.cpp b/src/storm-pomdp/generator/GenerateMonitorVerifier.cpp new file mode 100644 index 0000000000..1cd6584c44 --- /dev/null +++ b/src/storm-pomdp/generator/GenerateMonitorVerifier.cpp @@ -0,0 +1,437 @@ +#include "storm-pomdp/generator/GenerateMonitorVerifier.h" +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include "storm/adapters/RationalNumberAdapter.h" +#include "storm/exceptions/IllegalArgumentException.h" +#include "storm/exceptions/InvalidArgumentException.h" +#include "storm/storage/BitVector.h" +#include "storm/storage/SparseMatrix.h" +#include "storm/storage/expressions/ExpressionManager.h" +#include "storm/utility/constants.h" +#include "storm/utility/macros.h" + +namespace storm { +namespace generator { + +template +MonitorVerifier::MonitorVerifier(const models::sparse::Pomdp& product, + const std::map, uint32_t>& observationMap, + std::map observationDefaultAction) + : product(storm::models::sparse::Pomdp(product)), observationMap(observationMap), observationDefaultAction(observationDefaultAction) {} + +template +const std::map, uint32_t>& MonitorVerifier::getObservationMap() { + return observationMap; +} + +template +const models::sparse::Pomdp& MonitorVerifier::getProduct() { + return product; +} + +template +const std::map& MonitorVerifier::getObservationDefaultAction() { + return observationDefaultAction; +} + +template +GenerateMonitorVerifier::GenerateMonitorVerifier(models::sparse::Dtmc const& mc, models::sparse::Mdp const& monitor, + std::shared_ptr& exprManager, Options const& options) + : mc(mc), monitor(monitor), risk(), exprManager(exprManager), options(options) { + monvar = exprManager->declareFreshIntegerVariable(false, "_mon"); + mcvar = exprManager->declareFreshIntegerVariable(false, "_mc"); +} + +template +std::shared_ptr> GenerateMonitorVerifier::createProduct() { + typedef storm::storage::sparse::state_type state_type; + typedef std::pair product_state_type; + + STORM_LOG_THROW(monitor.hasChoiceLabeling(), storm::exceptions::InvalidArgumentException, "The monitor should contain choice labeling"); + + const std::set& actions = monitor.getChoiceLabeling().getLabels(); + + // Build choice label map of monitor choices + std::vector monitorChoiceLabels; + for (typename storm::storage::SparseMatrix::index_type i = 0; i < monitor.getTransitionMatrix().getRowCount(); i++) { + auto const& monitorLabels = monitor.getChoiceLabeling().getLabelsOfChoice(i); + STORM_LOG_THROW(monitorLabels.size() == 1, storm::exceptions::InvalidArgumentException, "Monitor choice has not exactly one choice label"); + monitorChoiceLabels.push_back(*monitorLabels.begin()); + } + + uint32_t nextObservation = 0; + std::map, uint32_t> observationMap; + std::vector observations; + + std::map, storage::BitVector> rowActionObservationMap; + std::vector> observationUsedActions; + + storm::storage::SparseMatrixBuilder builder(0, 0, 0, false, true); + std::size_t currentRow = 0; + state_type nextStateId = 0; + + state_type goalIndex = nextStateId++; + builder.newRowGroup(currentRow); + rowActionObservationMap[std::make_pair("end", nextObservation)].grow(currentRow + 1); + rowActionObservationMap[std::make_pair("end", nextObservation)].set(currentRow); + observationUsedActions.push_back({"end"}); + builder.addDiagonalEntry(currentRow++, utility::one()); + observations.push_back(nextObservation++); + + state_type stopIndex = nextStateId++; + builder.newRowGroup(currentRow); + rowActionObservationMap[std::make_pair("end", nextObservation)].grow(currentRow + 1); + rowActionObservationMap[std::make_pair("end", nextObservation)].set(currentRow); + observationUsedActions.push_back({"end"}); + builder.addDiagonalEntry(currentRow++, utility::one()); + observations.push_back(nextObservation++); + + std::map prodToIndexMap; + std::vector rejectToStates; + + state_type rejectionIndex; + if (!options.useRejectionSampling) { + // Add sink state where all invalid transitions go + rejectionIndex = nextStateId++; + builder.newRowGroup(currentRow); + rowActionObservationMap[std::make_pair("end", nextObservation)].grow(currentRow + 1); + rowActionObservationMap[std::make_pair("end", nextObservation)].set(currentRow); + observationUsedActions.push_back({"end"}); + builder.addDiagonalEntry(currentRow++, utility::one()); + observations.push_back(nextObservation++); + rejectToStates.push_back(rejectionIndex); + } + + std::vector initialStates; + + std::deque todo; + for (state_type mc_s_0 : mc.getInitialStates()) { + for (state_type mon_s_0 : monitor.getInitialStates()) { + product_state_type prod_s(mc_s_0, mon_s_0); + state_type index = nextStateId++; + prodToIndexMap[prod_s] = index; + initialStates.push_back(index); + if (options.useRejectionSampling) + rejectToStates.push_back(index); + todo.push_back(prod_s); + } + } + + while (!todo.empty()) { + auto const [mc_from, mon_from] = std::move(todo.front()); + todo.pop_front(); + + // Set observations for from + bool accepting = monitor.getStateLabeling().getStateHasLabel(options.acceptingLabel, mon_from); + uint32_t step; + for (auto& label : monitor.getStateLabeling().getLabelsOfState(mon_from)) { + if (label.starts_with(options.stepPrefix)) { + step = std::stoi(label.substr(options.stepPrefix.length())); + } + } + std::pair obsPair(step, accepting); + if (!observationMap.contains(obsPair)) { + observationMap[obsPair] = nextObservation++; + observationUsedActions.push_back(std::set()); + } + u_int32_t currentObservation = observationMap.at(obsPair); + observations.push_back(currentObservation); + + // Set transitions for from and add new states to todo + builder.newRowGroup(currentRow); + if (monitor.getStateLabeling().getLabelsOfState(mon_from).contains(options.horizonLabel)) { + const auto& action = *actions.begin(); + for (state_type initState : rejectToStates) { + builder.addNextValue(currentRow, initState, storm::utility::one() / rejectToStates.size()); + } + rowActionObservationMap[std::make_pair(action, currentObservation)].grow(currentRow + 1); + rowActionObservationMap[std::make_pair(action, currentObservation)].set(currentRow); + observationUsedActions[currentObservation].emplace(action); + currentRow++; + } else { + std::size_t numMonRows = monitor.getTransitionMatrix().getRowGroupSize(mon_from); + std::size_t monGroupStart = monitor.getTransitionMatrix().getRowGroupIndices()[mon_from]; + std::set actionsNotTaken(actions); + for (std::size_t i = 0; i < numMonRows; i++) { + // Remove labels of monitor choice from the labels we still have to take + + const auto action = monitorChoiceLabels[monGroupStart + i]; + actionsNotTaken.erase(action); + + const auto& monitorRow = monitor.getTransitionMatrix().getRow(mon_from, i); + STORM_LOG_ASSERT(monitorRow.getNumberOfEntries() == 1, "Monitor is not fully deterministic"); + const auto& monitorEntry = monitorRow.begin(); + + const auto& mcRow = mc.getTransitionMatrix().getRow(mc_from); + + // Find total probability of the transitions to a state with label action + auto totalProbability = utility::zero(); + for (const auto& mcEntry : mcRow) { + if (mc.getStateLabeling().getStateHasLabel(action, mcEntry.getColumn())) { + totalProbability += mcEntry.getValue(); + } + } + + // Add new entries to an unsorted vector containing possible duplicate indexes + std::map newRow; + + // Direct probability not used towards the initial states + if (totalProbability < storm::utility::one()) { + for (state_type initState : rejectToStates) { + if (newRow.contains(initState)) + newRow[initState] = newRow[initState] + (1 - totalProbability) / rejectToStates.size(); + else + newRow[initState] = (1 - totalProbability) / rejectToStates.size(); + } + } + + // Add transitions to the successors, if the successor has not yet been added, add it to the todo list + if (totalProbability > storm::utility::zero()) { + for (const auto& mcEntry : mcRow) { + if (mc.getStateLabeling().getStateHasLabel(action, mcEntry.getColumn())) { + const product_state_type to_pair(mcEntry.getColumn(), monitorEntry->getColumn()); + state_type indexTo; + if (auto it = prodToIndexMap.find(to_pair); it != prodToIndexMap.end()) { + indexTo = it->second; + } else { + indexTo = nextStateId++; + todo.push_back(to_pair); + prodToIndexMap[to_pair] = indexTo; + } + if (newRow.contains(indexTo)) + newRow[indexTo] = newRow[indexTo] + mcEntry.getValue(); + else + newRow[indexTo] = mcEntry.getValue(); + } + } + + // Set action to used for this observation + observationUsedActions[currentObservation].emplace(action); + } + + // Insert new entries + for (const auto& entry : newRow) { + builder.addNextValue(currentRow, entry.first, entry.second); + } + auto& rowBitVec = rowActionObservationMap[std::make_pair(action, currentObservation)]; + rowBitVec.grow(currentRow + 1); + rowBitVec.set(currentRow); + currentRow++; + } + + for (const auto& action : actionsNotTaken) { + for (state_type initState : rejectToStates) { + builder.addNextValue(currentRow, initState, storm::utility::one() / rejectToStates.size()); + } + auto& rowBitVec = rowActionObservationMap[std::make_pair(action, currentObservation)]; + rowBitVec.grow(currentRow + 1); + rowBitVec.set(currentRow); + currentRow++; + } + } + + if (monitor.getStateLabeling().getStateHasLabel(options.acceptingLabel, mon_from)) { + if (options.useRisk) { + STORM_LOG_THROW(risk[mc_from] >= -utility::convertNumber(1e-12) && risk[mc_from] <= utility::convertNumber(1.0 + 1e-12), + exceptions::IllegalArgumentException, "Risk for state " + std::to_string(mc_from) + " is not in [0, 1]"); + if (utility::isAlmostZero(risk[mc_from])) { + builder.addNextValue(currentRow, stopIndex, utility::one()); + } else if (utility::isAlmostOne(risk[mc_from])) { + builder.addNextValue(currentRow, goalIndex, utility::one()); + } else { + builder.addNextValue(currentRow, goalIndex, risk[mc_from]); + builder.addNextValue(currentRow, stopIndex, utility::one() - risk[mc_from]); + } + } else { + if (mc.getStateLabeling().getStateHasLabel(options.goodLabel, mc_from)) { + builder.addNextValue(currentRow, goalIndex, utility::one()); + } else { + builder.addNextValue(currentRow, stopIndex, utility::one()); + } + } + observationUsedActions[currentObservation].emplace("end"); + auto& rowBitVec = rowActionObservationMap[std::make_pair("end", currentObservation)]; + rowBitVec.grow(currentRow + 1); + rowBitVec.set(currentRow); + currentRow++; + } + } + + size_t numberOfRows = currentRow; + + // Make all observation action bitvectors of size numberOfRows + for (auto& [labelObsPair, vec] : rowActionObservationMap) { + vec.resize(numberOfRows); + } + + // Calculate which rows belong to action which don't all return for an observation and only keep these + storm::storage::SparseMatrix transMatrix = builder.build(); + storm::storage::BitVector rowsToKeep(transMatrix.getRowCount()); + std::map observationDefaultAction; + u_int32_t currentObservation = 0; + for (auto const& actionsInObs : observationUsedActions) { + if (actionsInObs.size() == 1) { + observationDefaultAction[currentObservation] = *actionsInObs.begin(); + } + + for (auto const& action : actionsInObs) { + // std::cout << "Keeping action obs (" << action << ", " << currentObservation << ")" << std::endl; + rowsToKeep |= rowActionObservationMap[std::make_pair(action, currentObservation)]; + } + currentObservation++; + } + // std::cout << "Kept " << rowsToKeep.getNumberOfSetBits() << " out of " << numberOfRows << " rows." << std::endl; + // rowsToKeep.setMultiple(0, numberOfRows); + numberOfRows = rowsToKeep.getNumberOfSetBits(); + storm::storage::SparseMatrix reducedTransitionMatrix = transMatrix.restrictRows(rowsToKeep); + + // Create state labeling + const state_type numberOfStates = nextStateId; + storm::models::sparse::StateLabeling stateLabeling(numberOfStates); + stateLabeling.addLabel("init", storm::storage::BitVector(numberOfStates, initialStates.begin(), initialStates.end())); + + stateLabeling.addLabel("goal", storm::storage::BitVector(numberOfStates)); + stateLabeling.addLabelToState("goal", goalIndex); + + stateLabeling.addLabel("stop", storm::storage::BitVector(numberOfStates)); + stateLabeling.addLabelToState("stop", stopIndex); + + stateLabeling.addLabel("condition", storm::storage::BitVector(numberOfStates)); + stateLabeling.addLabelToState("condition", goalIndex); + stateLabeling.addLabelToState("condition", stopIndex); + + if (!options.useRejectionSampling) { + stateLabeling.addLabel("sink", storm::storage::BitVector(numberOfStates)); + stateLabeling.addLabelToState("sink", rejectionIndex); + } + + storm::storage::sparse::ModelComponents components(reducedTransitionMatrix, std::move(stateLabeling)); + components.observabilityClasses = std::move(observations); + + // Add choice labeling + const std::vector rowMapping = rowsToKeep.getNumberOfSetBitsBeforeIndices(); // Vector which maps old row id to new row id + storm::models::sparse::ChoiceLabeling choiceLabeling(numberOfRows); + for (const auto& [labelObsPair, bitvec] : rowActionObservationMap) { + // Rebuild bitvec with restricted rows + storm::storage::BitVector newBitVec(numberOfRows); + for (const auto& setbit : bitvec) { + if (rowsToKeep[setbit]) + newBitVec.set(rowMapping[setbit]); + } + // auto newBitVec = bitvec; + + if (choiceLabeling.containsLabel(labelObsPair.first)) { + choiceLabeling.setChoices(labelObsPair.first, newBitVec | choiceLabeling.getChoices(labelObsPair.first)); + } else { + choiceLabeling.addLabel(labelObsPair.first, newBitVec); + } + } + + components.choiceLabeling = std::move(choiceLabeling); + + if (mc.hasStateValuations()) { + // Add state valuations + storm::storage::sparse::StateValuationsBuilder svBuilder; + svBuilder.addVariable(monvar); + svBuilder.addVariable(mcvar); + std::set variables; + for (uint_fast64_t i = 0; i < mc.getNumberOfStates(); i++) { + const auto& valAssignment = mc.getStateValuations().at(i); + for (auto val = valAssignment.begin(); val != valAssignment.end(); ++val) { + if (val.isVariableAssignment() && !variables.contains(val.getVariable())) { + variables.emplace(val.getVariable()); + svBuilder.addVariable(val.getVariable()); + } + } + } + + for (uint_fast64_t i = 0; i < mc.getNumberOfStates(); i++) { + for (uint_fast64_t j = 0; j < monitor.getNumberOfStates(); j++) { + product_state_type s(i, j); + if (!prodToIndexMap.contains(s)) + continue; + + std::vector booleanValues; + std::vector integerValues; + std::vector rationalValues; + + integerValues.push_back(j); // Set monvar + integerValues.push_back(i); // Set mcvar + + const auto& valAssignment = mc.getStateValuations().at(i); + + for (auto& var : variables) { + for (auto val = valAssignment.begin(); val != valAssignment.end(); ++val) { + if (var == val.getVariable()) { + if (val.isBoolean()) { + booleanValues.push_back(val.getBooleanValue()); + } else if (val.isInteger()) { + integerValues.push_back(val.getIntegerValue()); + } else if (val.isRational()) { + rationalValues.push_back(val.getRationalValue()); + } + break; + } + } + } + svBuilder.addState(prodToIndexMap[std::make_pair(i, j)], std::move(booleanValues), std::move(integerValues), std::move(rationalValues)); + } + } + + std::vector goalBooleanValues; + std::vector goalIntegerValues(2, -1); + std::vector goalRationalValues; + for (auto& var : variables) { + if (var.hasBooleanType()) { + goalBooleanValues.push_back(false); + } else if (var.hasIntegerType()) { + goalIntegerValues.push_back(-1); + } else if (var.hasRationalType()) { + goalRationalValues.emplace_back(-1); + } + } + svBuilder.addState(goalIndex, std::move(goalBooleanValues), std::move(goalIntegerValues), std::move(goalRationalValues)); + + std::vector stopBooleanValues; + std::vector stopIntegerValues(2, -1); + std::vector stopRationalValues; + for (auto& var : variables) { + if (var.hasBooleanType()) { + stopBooleanValues.push_back(false); + } else if (var.hasIntegerType()) { + stopIntegerValues.push_back(-1); + } else if (var.hasRationalType()) { + stopRationalValues.emplace_back(-1); + } + } + svBuilder.addState(stopIndex, std::move(stopBooleanValues), std::move(stopIntegerValues), std::move(stopRationalValues)); + + components.stateValuations = svBuilder.build(); + } + + // Store model + storm::models::sparse::Pomdp product(std::move(components)); + auto mv = std::make_shared>(std::move(product), std::move(observationMap), std::move(observationDefaultAction)); + return mv; +} + +template +void GenerateMonitorVerifier::setRisk(std::vector const& risk) { + this->risk = risk; +} + +template class MonitorVerifier; +template class MonitorVerifier; +template class GenerateMonitorVerifier; +template class GenerateMonitorVerifier; + +} // namespace generator +} // namespace storm diff --git a/src/storm-pomdp/generator/GenerateMonitorVerifier.h b/src/storm-pomdp/generator/GenerateMonitorVerifier.h new file mode 100644 index 0000000000..7fcd1b868f --- /dev/null +++ b/src/storm-pomdp/generator/GenerateMonitorVerifier.h @@ -0,0 +1,53 @@ +#pragma once +#include +#include +#include +#include "storm/models/sparse/Dtmc.h" +#include "storm/models/sparse/Mdp.h" +#include "storm/models/sparse/Pomdp.h" + +namespace storm::generator { + +template +class MonitorVerifier { + public: + MonitorVerifier(const storm::models::sparse::Pomdp& product, const std::map, uint32_t>& observationMap, + std::map observationDefaultAction); + + const std::map, uint32_t>& getObservationMap(); + const std::map& getObservationDefaultAction(); + const storm::models::sparse::Pomdp& getProduct(); + + private: + storm::models::sparse::Pomdp product; + std::map, uint32_t> observationMap; + std::map observationDefaultAction; +}; + +template +class GenerateMonitorVerifier { + public: + struct Options { + std::string goodLabel = "good"; + std::string acceptingLabel = "accepting"; + std::string stepPrefix = "step"; + std::string horizonLabel = "horizon"; + bool useRejectionSampling = true; + bool useRisk = true; + }; + GenerateMonitorVerifier(storm::models::sparse::Dtmc const& mc, storm::models::sparse::Mdp const& monitor, + std::shared_ptr& exprManager, Options const& options); + std::shared_ptr> createProduct(); + void setRisk(std::vector const& risk); + + private: + const storm::models::sparse::Dtmc& mc; + const storm::models::sparse::Mdp& monitor; + std::vector risk; + std::shared_ptr& exprManager; + storm::expressions::Variable monvar; + storm::expressions::Variable mcvar; + Options options; +}; + +} // namespace storm::generator \ No newline at end of file diff --git a/src/storm-pomdp/generator/NondeterministicBeliefTracker.cpp b/src/storm-pomdp/generator/NondeterministicBeliefTracker.cpp index d6fed4ac45..75d0e95d1a 100644 --- a/src/storm-pomdp/generator/NondeterministicBeliefTracker.cpp +++ b/src/storm-pomdp/generator/NondeterministicBeliefTracker.cpp @@ -382,7 +382,9 @@ NondeterministicBeliefTracker::NondeterministicBeliefTra template bool NondeterministicBeliefTracker::reset(uint32_t observation) { + beliefs = std::unordered_set(); bool hit = false; + beliefs.clear(); for (auto state : pomdp.getInitialStates()) { if (observation == pomdp.getObservation(state)) { hit = true; @@ -492,9 +494,6 @@ bool NondeterministicBeliefTracker::hasTimedOut() const template class SparseBeliefState; template bool operator==(SparseBeliefState const&, SparseBeliefState const&); template class NondeterministicBeliefTracker>; -// template class ObservationDenseBeliefState; -// template bool operator==(ObservationDenseBeliefState const&, ObservationDenseBeliefState const&); -// template class NondeterministicBeliefTracker>; template class SparseBeliefState; template bool operator==(SparseBeliefState const&, SparseBeliefState const&); diff --git a/src/storm-pomdp/transformer/GlobalPomdpMecChoiceEliminator.cpp b/src/storm-pomdp/transformer/GlobalPomdpMecChoiceEliminator.cpp index 410517cc9f..a4a5650ca2 100644 --- a/src/storm-pomdp/transformer/GlobalPomdpMecChoiceEliminator.cpp +++ b/src/storm-pomdp/transformer/GlobalPomdpMecChoiceEliminator.cpp @@ -234,7 +234,7 @@ storm::storage::BitVector GlobalPomdpMecChoiceEliminator::checkPropos storm::modelchecker::SparsePropositionalModelChecker> mc(pomdp); STORM_LOG_THROW(mc.canHandle(propositionalFormula), storm::exceptions::InvalidPropertyException, "Propositional model checker can not handle formula " << propositionalFormula); - return mc.check(propositionalFormula)->asExplicitQualitativeCheckResult().getTruthValuesVector(); + return mc.check(propositionalFormula)->template asExplicitQualitativeCheckResult().getTruthValuesVector(); } template class GlobalPomdpMecChoiceEliminator; diff --git a/src/storm-pomdp/transformer/ObservationTraceUnfolder.cpp b/src/storm-pomdp/transformer/ObservationTraceUnfolder.cpp index 7227dd077f..832db84daf 100644 --- a/src/storm-pomdp/transformer/ObservationTraceUnfolder.cpp +++ b/src/storm-pomdp/transformer/ObservationTraceUnfolder.cpp @@ -1,8 +1,12 @@ #include "storm-pomdp/transformer/ObservationTraceUnfolder.h" +#include +#include "storm/adapters/IntervalForward.h" #include "storm/adapters/RationalFunctionAdapter.h" +#include "storm/adapters/RationalNumberForward.h" #include "storm/exceptions/InvalidArgumentException.h" #include "storm/storage/expressions/ExpressionManager.h" +#include "storm/utility/ConstantsComparator.h" #undef _VERBOSE_OBSERVATION_UNFOLDING @@ -10,13 +14,16 @@ namespace storm { namespace pomdp { template ObservationTraceUnfolder::ObservationTraceUnfolder(storm::models::sparse::Pomdp const& model, std::vector const& risk, - std::shared_ptr& exprManager) - : model(model), risk(risk), exprManager(exprManager) { - statesPerObservation = std::vector(model.getNrObservations() + 1, storm::storage::BitVector(model.getNumberOfStates())); - for (uint64_t state = 0; state < model.getNumberOfStates(); ++state) { - statesPerObservation[model.getObservation(state)].set(state, true); - } + std::shared_ptr& exprManager, + ObservationTraceUnfolderOptions const& options) + : model(model), risk(risk), exprManager(exprManager), options(options) { + // std::cout << "Starting observation trace unfolder with n obervations " << model.getNrObservations() << "\n"; + // statesPerObservation = std::vector(model.getNrObservations() + 1, storm::storage::BitVector(model.getNumberOfStates())); + // for (uint64_t state = 0; state < model.getNumberOfStates(); ++state) { + // statesPerObservation[model.getObservation(state)].set(state, true); + // } svvar = exprManager->declareFreshIntegerVariable(false, "_s"); + tsvar = exprManager->declareFreshIntegerVariable(false, "_t"); } template @@ -24,6 +31,7 @@ std::shared_ptr> ObservationTraceUnfolder< std::vector modifiedObservations = observations; // First observation should be special. // This just makes the algorithm simpler because we do not treat the first step as a special case later. + // We overwrite the observation with a non-existing obs z* modifiedObservations[0] = model.getNrObservations(); storm::storage::BitVector initialStates = model.getInitialStates(); @@ -35,32 +43,52 @@ std::shared_ptr> ObservationTraceUnfolder< } STORM_LOG_THROW(actualInitialStates.getNumberOfSetBits() == 1, storm::exceptions::InvalidArgumentException, "Must have unique initial state matching the observation"); - // - statesPerObservation[model.getNrObservations()] = actualInitialStates; + // For this z* that only exists in the initial state, we now also define the states for this observation. + // statesPerObservation[model.getNrObservations()] = actualInitialStates; #ifdef _VERBOSE_OBSERVATION_UNFOLDING std::cout << "build valution builder..\n"; #endif storm::storage::sparse::StateValuationsBuilder svbuilder; svbuilder.addVariable(svvar); + svbuilder.addVariable(tsvar); - std::map unfoldedToOld; - std::map unfoldedToOldNextStep; - std::map oldToUnfolded; + std::unordered_map unfoldedToOld; + std::unordered_map unfoldedToOldNextStep; + std::unordered_map oldToUnfolded; #ifdef _VERBOSE_OBSERVATION_UNFOLDING std::cout << "start buildiing matrix...\n"; #endif - // Add this initial state state: - unfoldedToOldNextStep[0] = actualInitialStates.getNextSetIndex(0); + uint64_t newStateIndex = 0; + uint64_t const violatedState = newStateIndex; + if (!options.rejectionSampling) { + // The violated state is only used if we do no use the rejection semantics. + ++newStateIndex; + } + // Add this initial state: + uint64_t const initialState = newStateIndex; + ++newStateIndex; + + unfoldedToOldNextStep[initialState] = actualInitialStates.getNextSetIndex(0); + uint64_t const resetDestination = options.rejectionSampling ? initialState : violatedState; // Should be initial state for the standard semantics. storm::storage::SparseMatrixBuilder transitionMatrixBuilder(0, 0, 0, true, true); - uint64_t newStateIndex = 1; - uint64_t newRowGroupStart = 0; - uint64_t newRowCount = 0; - // Notice that we are going to use a special last step + // TODO only add this state if it is actually reachable / rejection sampling + if (!options.rejectionSampling) { + // the violated state (only used when no rejection sampling) is a sink state + transitionMatrixBuilder.newRowGroup(violatedState); + transitionMatrixBuilder.addNextValue(violatedState, violatedState, storm::utility::one()); + svbuilder.addState(violatedState, {}, {-1, -1}); + } + + // Now we are starting to build the MDP from the initial state onwards. + uint64_t newRowGroupStart = initialState; + uint64_t newRowCount = initialState; + + // Notice that we are going to use a special last step for (uint64_t step = 0; step < observations.size() - 1; ++step) { oldToUnfolded.clear(); unfoldedToOld = unfoldedToOldNextStep; @@ -71,8 +99,9 @@ std::shared_ptr> ObservationTraceUnfolder< #ifdef _VERBOSE_OBSERVATION_UNFOLDING std::cout << "\tconsider new state " << unfoldedToOldEntry.first << '\n'; #endif - assert(step == 0 || newRowCount == transitionMatrixBuilder.getLastRow() + 1); - svbuilder.addState(unfoldedToOldEntry.first, {}, {static_cast(unfoldedToOldEntry.second)}); + STORM_LOG_ASSERT(step == 0 || newRowCount == transitionMatrixBuilder.getLastRow() + 1, + "step " << step << " newRowCount " << newRowCount << " lastRow " << transitionMatrixBuilder.getLastRow()); + svbuilder.addState(unfoldedToOldEntry.first, {}, {static_cast(unfoldedToOldEntry.second), static_cast(step)}); uint64_t oldRowIndexStart = model.getNondeterministicChoiceIndices()[unfoldedToOldEntry.second]; uint64_t oldRowIndexEnd = model.getNondeterministicChoiceIndices()[unfoldedToOldEntry.second + 1]; @@ -87,6 +116,13 @@ std::shared_ptr> ObservationTraceUnfolder< for (auto const& oldRowEntry : model.getTransitionMatrix().getRow(oldRowIndex)) { if (model.getObservation(oldRowEntry.getColumn()) != observations[step + 1]) { resetProb += oldRowEntry.getValue(); + if constexpr (std::is_same_v) { + resetProb.setUpper(std::min(resetProb.upper(), 1.0)); + resetProb.setLower(std::max(resetProb.lower(), 0.0)); + } else if constexpr (std::is_same_v) { + resetProb.setUpper(std::min(resetProb.upper(), utility::one())); + resetProb.setLower(std::max(resetProb.lower(), utility::zero())); + } } } #ifdef _VERBOSE_OBSERVATION_UNFOLDING @@ -95,7 +131,7 @@ std::shared_ptr> ObservationTraceUnfolder< // Add the resets if (resetProb != storm::utility::zero()) { - transitionMatrixBuilder.addNextValue(newRowCount, 0, resetProb); + transitionMatrixBuilder.addNextValue(newRowCount, resetDestination, resetProb); } #ifdef _VERBOSE_OBSERVATION_UNFOLDING std::cout << "\t\t\t add other transitions...\n"; @@ -124,7 +160,6 @@ std::shared_ptr> ObservationTraceUnfolder< } newRowCount++; } - newRowGroupStart = transitionMatrixBuilder.getLastRow() + 1; } } @@ -132,7 +167,7 @@ std::shared_ptr> ObservationTraceUnfolder< uint64_t sinkState = newStateIndex; uint64_t targetState = newStateIndex + 1; for (auto const& unfoldedToOldEntry : unfoldedToOldNextStep) { - svbuilder.addState(unfoldedToOldEntry.first, {}, {static_cast(unfoldedToOldEntry.second)}); + svbuilder.addState(unfoldedToOldEntry.first, {}, {static_cast(unfoldedToOldEntry.second), static_cast(observations.size() - 1)}); transitionMatrixBuilder.newRowGroup(newRowGroupStart); STORM_LOG_ASSERT(risk.size() > unfoldedToOldEntry.second, "Must be a state"); @@ -150,13 +185,13 @@ std::shared_ptr> ObservationTraceUnfolder< // sink state transitionMatrixBuilder.newRowGroup(newRowGroupStart); transitionMatrixBuilder.addNextValue(newRowGroupStart, sinkState, storm::utility::one()); - svbuilder.addState(sinkState, {}, {-1}); + svbuilder.addState(sinkState, {}, {-1, -1}); newRowGroupStart++; transitionMatrixBuilder.newRowGroup(newRowGroupStart); // target state transitionMatrixBuilder.addNextValue(newRowGroupStart, targetState, storm::utility::one()); - svbuilder.addState(targetState, {}, {-1}); + svbuilder.addState(targetState, {}, {-1, -1}); #ifdef _VERBOSE_OBSERVATION_UNFOLDING std::cout << "build matrix...\n"; #endif @@ -164,24 +199,29 @@ std::shared_ptr> ObservationTraceUnfolder< storm::storage::sparse::ModelComponents components; components.transitionMatrix = transitionMatrixBuilder.build(); #ifdef _VERBOSE_OBSERVATION_UNFOLDING - std::cout << components.transitionMatrix << '\n'; + // std::cout << components.transitionMatrix << '\n'; #endif - STORM_LOG_ASSERT(components.transitionMatrix.getRowGroupCount() == targetState + 1, - "Expect row group count (" << components.transitionMatrix.getRowGroupCount() << ") one more as target state index " << targetState << ")"); storm::models::sparse::StateLabeling labeling(components.transitionMatrix.getRowGroupCount()); labeling.addLabel("_goal"); labeling.addLabelToState("_goal", targetState); + if (!options.rejectionSampling) { + labeling.addLabel("_violated"); + labeling.addLabelToState("_violated", violatedState); + } + labeling.addLabel("_end"); + labeling.addLabelToState("_end", sinkState); + labeling.addLabelToState("_end", targetState); labeling.addLabel("init"); - labeling.addLabelToState("init", 0); + labeling.addLabelToState("init", initialState); components.stateLabeling = labeling; components.stateValuations = svbuilder.build(); return std::make_shared>(std::move(components)); } template -std::shared_ptr> ObservationTraceUnfolder::extend(uint32_t observation) { - traceSoFar.push_back(observation); +std::shared_ptr> ObservationTraceUnfolder::extend(std::vector const& observations) { + traceSoFar.insert(traceSoFar.end(), observations.begin(), observations.end()); return transform(traceSoFar); } @@ -190,7 +230,14 @@ void ObservationTraceUnfolder::reset(uint32_t observation) { traceSoFar = {observation}; } +template +bool ObservationTraceUnfolder::isRejectionSamplingSet() const { + return options.rejectionSampling; +} + template class ObservationTraceUnfolder; +template class ObservationTraceUnfolder; +template class ObservationTraceUnfolder; template class ObservationTraceUnfolder; template class ObservationTraceUnfolder; } // namespace pomdp diff --git a/src/storm-pomdp/transformer/ObservationTraceUnfolder.h b/src/storm-pomdp/transformer/ObservationTraceUnfolder.h index 6109896177..9782d187f3 100644 --- a/src/storm-pomdp/transformer/ObservationTraceUnfolder.h +++ b/src/storm-pomdp/transformer/ObservationTraceUnfolder.h @@ -4,6 +4,12 @@ namespace storm { namespace pomdp { + +class ObservationTraceUnfolderOptions { + public: + bool rejectionSampling = true; +}; + /** * Observation-trace unrolling to allow model checking for monitoring. * This approach is outlined in Junges, Hazem, Seshia -- Runtime Monitoring for Markov Decision Processes @@ -19,7 +25,7 @@ class ObservationTraceUnfolder { * @param exprManager an Expression Manager */ ObservationTraceUnfolder(storm::models::sparse::Pomdp const& model, std::vector const& risk, - std::shared_ptr& exprManager); + std::shared_ptr& exprManager, ObservationTraceUnfolderOptions const& options); /** * Transform in one shot * @param observations @@ -31,20 +37,24 @@ class ObservationTraceUnfolder { * @param observation * @return */ - std::shared_ptr> extend(uint32_t observation); + std::shared_ptr> extend(std::vector const& observations); /** * When using the incremental approach, reset the observations made so far. * @param observation The new initial observation */ void reset(uint32_t observation); + bool isRejectionSamplingSet() const; + private: - storm::models::sparse::Pomdp const& model; + storm::models::sparse::Pomdp model; std::vector risk; // TODO reconsider holding this as a reference, but there were some strange bugs std::shared_ptr& exprManager; - std::vector statesPerObservation; + // std::vector statesPerObservation; std::vector traceSoFar; - storm::expressions::Variable svvar; + storm::expressions::Variable svvar; // Maps to the old state (explicit encoding) + storm::expressions::Variable tsvar; // Maps to the time step + ObservationTraceUnfolderOptions options; }; } // namespace pomdp diff --git a/src/storm/adapters/IntervalForward.h b/src/storm/adapters/IntervalForward.h index ddc9a5d284..f0fc334b2e 100644 --- a/src/storm/adapters/IntervalForward.h +++ b/src/storm/adapters/IntervalForward.h @@ -1,5 +1,7 @@ #pragma once +#include "storm/adapters/RationalNumberForward.h" + namespace carl { template class Interval; @@ -11,6 +13,7 @@ namespace storm { * Interval type */ typedef carl::Interval Interval; +typedef carl::Interval RationalInterval; namespace detail { template @@ -23,6 +26,11 @@ struct IntervalMetaProgrammingHelper { using BaseType = double; static const bool isInterval = true; }; +template<> +struct IntervalMetaProgrammingHelper { + using BaseType = storm::RationalNumber; + static const bool isInterval = true; +}; } // namespace detail /*! diff --git a/src/storm/api/export.h b/src/storm/api/export.h index 60ea2d5d7b..2f4f11e085 100644 --- a/src/storm/api/export.h +++ b/src/storm/api/export.h @@ -118,7 +118,8 @@ inline void exportCheckResultToJson(std::shared_ptrisExplicitQualitativeCheckResult()) { - auto j = checkResult->asExplicitQualitativeCheckResult().toJson(model->getOptionalStateValuations(), model->getStateLabeling()); + auto j = checkResult->template asExplicitQualitativeCheckResult().template toJson(model->getOptionalStateValuations(), + model->getStateLabeling()); stream << storm::dumpJson(j); } else { STORM_LOG_THROW(checkResult->isExplicitQuantitativeCheckResult(), storm::exceptions::NotSupportedException, diff --git a/src/storm/environment/modelchecker/ModelCheckerEnvironment.cpp b/src/storm/environment/modelchecker/ModelCheckerEnvironment.cpp index 5ff79fd9a3..d0ecb5c347 100644 --- a/src/storm/environment/modelchecker/ModelCheckerEnvironment.cpp +++ b/src/storm/environment/modelchecker/ModelCheckerEnvironment.cpp @@ -21,6 +21,8 @@ ModelCheckerEnvironment::ModelCheckerEnvironment() { steadyStateDistributionAlgorithm = ioSettings.getSteadyStateDistributionAlgorithm(); conditionalAlgorithmSetting = mcSettings.getConditionalAlgorithmSetting(); + conditionalToleranceSetting = mcSettings.getConditionalTolerance(); + allowOptimizationForBoundedProperties = true; } ModelCheckerEnvironment::~ModelCheckerEnvironment() { @@ -43,6 +45,22 @@ void ModelCheckerEnvironment::setConditionalAlgorithmSetting(ConditionalAlgorith conditionalAlgorithmSetting = value; } +void ModelCheckerEnvironment::setConditionalTolerance(storm::RationalNumber const& value) { + conditionalToleranceSetting = value; +} + +storm::RationalNumber ModelCheckerEnvironment::getConditionalTolerance() const { + return conditionalToleranceSetting; +} + +bool ModelCheckerEnvironment::isAllowOptimizationForBoundedPropertiesSet() const { + return allowOptimizationForBoundedProperties; +} + +void ModelCheckerEnvironment::setAllowOptimizationForBoundedProperties(bool value) { + allowOptimizationForBoundedProperties = value; +} + MultiObjectiveModelCheckerEnvironment& ModelCheckerEnvironment::multi() { return multiObjectiveModelCheckerEnvironment.get(); } diff --git a/src/storm/environment/modelchecker/ModelCheckerEnvironment.h b/src/storm/environment/modelchecker/ModelCheckerEnvironment.h index fce3e9e337..b011b37a0b 100644 --- a/src/storm/environment/modelchecker/ModelCheckerEnvironment.h +++ b/src/storm/environment/modelchecker/ModelCheckerEnvironment.h @@ -4,6 +4,7 @@ #include #include +#include "storm/adapters/RationalNumberAdapter.h" #include "storm/environment/Environment.h" #include "storm/environment/SubEnvironment.h" #include "storm/modelchecker/helper/conditional/ConditionalAlgorithmSetting.h" @@ -28,6 +29,13 @@ class ModelCheckerEnvironment { ConditionalAlgorithmSetting getConditionalAlgorithmSetting() const; void setConditionalAlgorithmSetting(ConditionalAlgorithmSetting value); + // TODO: This should probably be moved. + storm::RationalNumber getConditionalTolerance() const; + void setConditionalTolerance(storm::RationalNumber const& value); + + bool isAllowOptimizationForBoundedPropertiesSet() const; + void setAllowOptimizationForBoundedProperties(bool value); + bool isLtl2daToolSet() const; std::string const& getLtl2daTool() const; void setLtl2daTool(std::string const& value); @@ -38,5 +46,7 @@ class ModelCheckerEnvironment { boost::optional ltl2daTool; SteadyStateDistributionAlgorithm steadyStateDistributionAlgorithm; ConditionalAlgorithmSetting conditionalAlgorithmSetting; + bool allowOptimizationForBoundedProperties; + storm::RationalNumber conditionalToleranceSetting; }; } // namespace storm diff --git a/src/storm/io/DirectEncodingExporter.cpp b/src/storm/io/DirectEncodingExporter.cpp index 39366362e3..26679ca7f0 100644 --- a/src/storm/io/DirectEncodingExporter.cpp +++ b/src/storm/io/DirectEncodingExporter.cpp @@ -72,6 +72,8 @@ void explicitExportSparseModel(std::ostream& os, std::shared_ptr) { os << "double-interval"; + } else if constexpr (std::is_same_v) { + os << "rational-interval"; } else if constexpr (std::is_same_v) { os << "parametric"; } else { @@ -338,5 +340,8 @@ template void explicitExportSparseModel(std::ostream& o std::vector const& parameters, DirectEncodingExporterOptions const& options); template void explicitExportSparseModel(std::ostream& os, std::shared_ptr> sparseModel, std::vector const& parameters, DirectEncodingExporterOptions const& options); +template void explicitExportSparseModel(std::ostream& os, + std::shared_ptr> sparseModel, + std::vector const& parameters, DirectEncodingExporterOptions const& options); } // namespace io } // namespace storm diff --git a/src/storm/modelchecker/AbstractModelChecker.cpp b/src/storm/modelchecker/AbstractModelChecker.cpp index 04cc3cce72..56664fcf2a 100644 --- a/src/storm/modelchecker/AbstractModelChecker.cpp +++ b/src/storm/modelchecker/AbstractModelChecker.cpp @@ -148,7 +148,7 @@ std::unique_ptr AbstractModelChecker::computeStateFormul std::unique_ptr resultPointer = this->check(env, checkTask.getFormula()); if (resultPointer->isExplicitQualitativeCheckResult()) { STORM_LOG_ASSERT(ModelType::Representation == storm::models::ModelRepresentation::Sparse, "Unexpected model type."); - return std::make_unique>(resultPointer->asExplicitQualitativeCheckResult()); + return std::make_unique>(resultPointer->template asExplicitQualitativeCheckResult()); } else { STORM_LOG_ASSERT(resultPointer->isSymbolicQualitativeCheckResult(), "Unexpected result type."); STORM_LOG_ASSERT(ModelType::Representation != storm::models::ModelRepresentation::Sparse, "Unexpected model type."); @@ -473,6 +473,9 @@ template class AbstractModelChecker>; template class AbstractModelChecker>>; template class AbstractModelChecker>>; +template class AbstractModelChecker>>; +template class AbstractModelChecker>>; + template class AbstractModelChecker>; template class AbstractModelChecker>; template class AbstractModelChecker>; @@ -491,6 +494,8 @@ template class AbstractModelChecker>; template class AbstractModelChecker>; +template class AbstractModelChecker>; + // DD template class AbstractModelChecker>; template class AbstractModelChecker>; diff --git a/src/storm/modelchecker/AbstractModelChecker.h b/src/storm/modelchecker/AbstractModelChecker.h index d1ec2e86ff..51261bddce 100644 --- a/src/storm/modelchecker/AbstractModelChecker.h +++ b/src/storm/modelchecker/AbstractModelChecker.h @@ -15,13 +15,31 @@ class CheckResult; template class AbstractModelChecker { + private: + // Due to a GCC bug we have to add this dummy template type here + // https://stackoverflow.com/questions/49707184/explicit-specialization-in-non-namespace-scope-does-not-compile-in-gcc + template + struct GetSolutionType { + using type = T; + }; + + template + struct GetSolutionType { + using type = double; + }; + + template + struct GetSolutionType { + using type = storm::RationalNumber; + }; + public: virtual ~AbstractModelChecker() { // Intentionally left empty. } typedef typename ModelType::ValueType ValueType; - using SolutionType = typename std::conditional, double, ValueType>::type; + using SolutionType = typename GetSolutionType::type; /*! * Returns the name of the model checker class (e.g., for display in error messages). diff --git a/src/storm/modelchecker/csl/SparseCtmcCslModelChecker.cpp b/src/storm/modelchecker/csl/SparseCtmcCslModelChecker.cpp index 9762a2c732..6eb001bc85 100644 --- a/src/storm/modelchecker/csl/SparseCtmcCslModelChecker.cpp +++ b/src/storm/modelchecker/csl/SparseCtmcCslModelChecker.cpp @@ -54,8 +54,8 @@ std::unique_ptr SparseCtmcCslModelChecker::com storm::logic::BoundedUntilFormula const& pathFormula = checkTask.getFormula(); std::unique_ptr leftResultPointer = this->check(env, pathFormula.getLeftSubformula()); std::unique_ptr rightResultPointer = this->check(env, pathFormula.getRightSubformula()); - ExplicitQualitativeCheckResult const& leftResult = leftResultPointer->asExplicitQualitativeCheckResult(); - ExplicitQualitativeCheckResult const& rightResult = rightResultPointer->asExplicitQualitativeCheckResult(); + ExplicitQualitativeCheckResult const& leftResult = leftResultPointer->template asExplicitQualitativeCheckResult(); + ExplicitQualitativeCheckResult const& rightResult = rightResultPointer->template asExplicitQualitativeCheckResult(); STORM_LOG_THROW(pathFormula.getTimeBoundReference().isTimeBound(), storm::exceptions::NotImplementedException, "Currently step-bounded or reward-bounded properties on CTMCs are not supported."); @@ -83,7 +83,7 @@ std::unique_ptr SparseCtmcCslModelChecker::com Environment const& env, CheckTask const& checkTask) { storm::logic::NextFormula const& pathFormula = checkTask.getFormula(); std::unique_ptr subResultPointer = this->check(env, pathFormula.getSubformula()); - ExplicitQualitativeCheckResult const& subResult = subResultPointer->asExplicitQualitativeCheckResult(); + ExplicitQualitativeCheckResult const& subResult = subResultPointer->template asExplicitQualitativeCheckResult(); std::vector numericResult = storm::modelchecker::helper::SparseCtmcCslHelper::computeNextProbabilities( env, this->getModel().getTransitionMatrix(), this->getModel().getExitRateVector(), subResult.getTruthValuesVector()); return std::unique_ptr(new ExplicitQuantitativeCheckResult(std::move(numericResult))); @@ -94,7 +94,7 @@ std::unique_ptr SparseCtmcCslModelChecker::com Environment const& env, CheckTask const& checkTask) { storm::logic::GloballyFormula const& pathFormula = checkTask.getFormula(); std::unique_ptr subResultPointer = this->check(env, pathFormula.getSubformula()); - ExplicitQualitativeCheckResult const& subResult = subResultPointer->asExplicitQualitativeCheckResult(); + ExplicitQualitativeCheckResult const& subResult = subResultPointer->template asExplicitQualitativeCheckResult(); auto probabilisticTransitions = this->getModel().computeProbabilityMatrix(); std::vector numericResult = storm::modelchecker::helper::SparseDtmcPrctlHelper::computeGloballyProbabilities( env, storm::solver::SolveGoal(this->getModel(), checkTask), probabilisticTransitions, probabilisticTransitions.transpose(), @@ -108,8 +108,8 @@ std::unique_ptr SparseCtmcCslModelChecker::com storm::logic::UntilFormula const& pathFormula = checkTask.getFormula(); std::unique_ptr leftResultPointer = this->check(env, pathFormula.getLeftSubformula()); std::unique_ptr rightResultPointer = this->check(env, pathFormula.getRightSubformula()); - ExplicitQualitativeCheckResult const& leftResult = leftResultPointer->asExplicitQualitativeCheckResult(); - ExplicitQualitativeCheckResult const& rightResult = rightResultPointer->asExplicitQualitativeCheckResult(); + ExplicitQualitativeCheckResult const& leftResult = leftResultPointer->template asExplicitQualitativeCheckResult(); + ExplicitQualitativeCheckResult const& rightResult = rightResultPointer->template asExplicitQualitativeCheckResult(); std::vector numericResult = storm::modelchecker::helper::SparseCtmcCslHelper::computeUntilProbabilities( env, storm::solver::SolveGoal(this->getModel(), checkTask), this->getModel().getTransitionMatrix(), this->getModel().getBackwardTransitions(), this->getModel().getExitRateVector(), leftResult.getTruthValuesVector(), rightResult.getTruthValuesVector(), @@ -127,7 +127,7 @@ std::unique_ptr SparseCtmcCslModelChecker::com storm::modelchecker::helper::setInformationFromCheckTaskDeterministic(helper, checkTask, this->getModel()); auto formulaChecker = [&](storm::logic::Formula const& formula) { - return this->check(env, formula)->asExplicitQualitativeCheckResult().getTruthValuesVector(); + return this->check(env, formula)->template asExplicitQualitativeCheckResult().getTruthValuesVector(); }; auto apSets = helper.computeApSets(pathFormula.getAPMapping(), formulaChecker); std::vector numericResult = helper.computeDAProductProbabilities(env, *pathFormula.readAutomaton(), apSets); @@ -145,7 +145,7 @@ std::unique_ptr SparseCtmcCslModelChecker::com storm::modelchecker::helper::setInformationFromCheckTaskDeterministic(helper, checkTask, this->getModel()); auto formulaChecker = [&](storm::logic::Formula const& formula) { - return this->check(env, formula)->asExplicitQualitativeCheckResult().getTruthValuesVector(); + return this->check(env, formula)->template asExplicitQualitativeCheckResult().getTruthValuesVector(); }; std::vector numericResult = helper.computeLTLProbabilities(env, pathFormula, formulaChecker); @@ -193,7 +193,7 @@ std::unique_ptr SparseCtmcCslModelChecker::com Environment const& env, CheckTask const& checkTask) { storm::logic::EventuallyFormula const& eventuallyFormula = checkTask.getFormula(); std::unique_ptr subResultPointer = this->check(env, eventuallyFormula.getSubformula()); - ExplicitQualitativeCheckResult const& subResult = subResultPointer->asExplicitQualitativeCheckResult(); + ExplicitQualitativeCheckResult const& subResult = subResultPointer->template asExplicitQualitativeCheckResult(); auto rewardModel = storm::utility::createFilteredRewardModel(this->getModel(), checkTask); std::vector numericResult = storm::modelchecker::helper::SparseCtmcCslHelper::computeReachabilityRewards( env, storm::solver::SolveGoal(this->getModel(), checkTask), this->getModel().getTransitionMatrix(), @@ -217,7 +217,7 @@ std::unique_ptr SparseCtmcCslModelChecker::com Environment const& env, CheckTask const& checkTask) { storm::logic::StateFormula const& stateFormula = checkTask.getFormula(); std::unique_ptr subResultPointer = this->check(env, stateFormula); - ExplicitQualitativeCheckResult const& subResult = subResultPointer->asExplicitQualitativeCheckResult(); + ExplicitQualitativeCheckResult const& subResult = subResultPointer->template asExplicitQualitativeCheckResult(); auto probabilisticTransitions = this->getModel().computeProbabilityMatrix(); storm::modelchecker::helper::SparseDeterministicInfiniteHorizonHelper helper(probabilisticTransitions, this->getModel().getExitRateVector()); @@ -243,7 +243,7 @@ std::unique_ptr SparseCtmcCslModelChecker::com Environment const& env, CheckTask const& checkTask) { storm::logic::EventuallyFormula const& eventuallyFormula = checkTask.getFormula(); std::unique_ptr subResultPointer = this->check(env, eventuallyFormula.getSubformula()); - ExplicitQualitativeCheckResult& subResult = subResultPointer->asExplicitQualitativeCheckResult(); + ExplicitQualitativeCheckResult& subResult = subResultPointer->template asExplicitQualitativeCheckResult(); std::vector numericResult = storm::modelchecker::helper::SparseCtmcCslHelper::computeReachabilityTimes( env, storm::solver::SolveGoal(this->getModel(), checkTask), this->getModel().getTransitionMatrix(), @@ -266,8 +266,8 @@ std::vector SparseCtmcCslModelChecker leftResultPointer = this->check(env, pathFormula.getLeftSubformula()); std::unique_ptr rightResultPointer = this->check(env, pathFormula.getRightSubformula()); - ExplicitQualitativeCheckResult const& leftResult = leftResultPointer->asExplicitQualitativeCheckResult(); - ExplicitQualitativeCheckResult const& rightResult = rightResultPointer->asExplicitQualitativeCheckResult(); + ExplicitQualitativeCheckResult const& leftResult = leftResultPointer->template asExplicitQualitativeCheckResult(); + ExplicitQualitativeCheckResult const& rightResult = rightResultPointer->template asExplicitQualitativeCheckResult(); std::vector result = storm::modelchecker::helper::SparseCtmcCslHelper::computeAllTransientProbabilities( env, this->getModel().getTransitionMatrix(), this->getModel().getInitialStates(), leftResult.getTruthValuesVector(), diff --git a/src/storm/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.cpp b/src/storm/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.cpp index 4bc4b65094..e4e5f820b1 100644 --- a/src/storm/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.cpp +++ b/src/storm/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.cpp @@ -72,10 +72,10 @@ std::unique_ptr SparseMarkovAutomatonCslModelCheckergetModel().isClosed(), storm::exceptions::InvalidPropertyException, "Unable to compute time-bounded reachability probabilities in non-closed Markov automaton."); std::unique_ptr rightResultPointer = this->check(env, pathFormula.getRightSubformula()); - ExplicitQualitativeCheckResult const& rightResult = rightResultPointer->asExplicitQualitativeCheckResult(); + ExplicitQualitativeCheckResult const& rightResult = rightResultPointer->template asExplicitQualitativeCheckResult(); std::unique_ptr leftResultPointer = this->check(env, pathFormula.getLeftSubformula()); - ExplicitQualitativeCheckResult const& leftResult = leftResultPointer->asExplicitQualitativeCheckResult(); + ExplicitQualitativeCheckResult const& leftResult = leftResultPointer->template asExplicitQualitativeCheckResult(); STORM_LOG_THROW(pathFormula.getTimeBoundReference().isTimeBound(), storm::exceptions::NotImplementedException, "Currently step-bounded and reward-bounded properties on MAs are not supported."); @@ -103,7 +103,7 @@ std::unique_ptr SparseMarkovAutomatonCslModelChecker subResultPointer = this->check(env, pathFormula.getSubformula()); - ExplicitQualitativeCheckResult const& subResult = subResultPointer->asExplicitQualitativeCheckResult(); + ExplicitQualitativeCheckResult const& subResult = subResultPointer->template asExplicitQualitativeCheckResult(); std::vector numericResult = storm::modelchecker::helper::SparseMdpPrctlHelper::computeNextProbabilities( env, checkTask.getOptimizationDirection(), this->getModel().getTransitionMatrix(), subResult.getTruthValuesVector()); return std::unique_ptr(new ExplicitQuantitativeCheckResult(std::move(numericResult))); @@ -116,7 +116,7 @@ std::unique_ptr SparseMarkovAutomatonCslModelChecker subResultPointer = this->check(env, pathFormula.getSubformula()); - ExplicitQualitativeCheckResult const& subResult = subResultPointer->asExplicitQualitativeCheckResult(); + ExplicitQualitativeCheckResult const& subResult = subResultPointer->template asExplicitQualitativeCheckResult(); auto ret = storm::modelchecker::helper::SparseMdpPrctlHelper::computeGloballyProbabilities( env, storm::solver::SolveGoal(this->getModel(), checkTask), this->getModel().getTransitionMatrix(), this->getModel().getBackwardTransitions(), subResult.getTruthValuesVector(), checkTask.isQualitativeSet(), checkTask.isProduceSchedulersSet()); @@ -135,8 +135,8 @@ std::unique_ptr SparseMarkovAutomatonCslModelChecker leftResultPointer = this->check(env, pathFormula.getLeftSubformula()); std::unique_ptr rightResultPointer = this->check(env, pathFormula.getRightSubformula()); - ExplicitQualitativeCheckResult& leftResult = leftResultPointer->asExplicitQualitativeCheckResult(); - ExplicitQualitativeCheckResult& rightResult = rightResultPointer->asExplicitQualitativeCheckResult(); + ExplicitQualitativeCheckResult& leftResult = leftResultPointer->template asExplicitQualitativeCheckResult(); + ExplicitQualitativeCheckResult& rightResult = rightResultPointer->template asExplicitQualitativeCheckResult(); auto ret = storm::modelchecker::helper::SparseMarkovAutomatonCslHelper::computeUntilProbabilities( env, checkTask.getOptimizationDirection(), this->getModel().getTransitionMatrix(), this->getModel().getBackwardTransitions(), @@ -157,7 +157,7 @@ std::unique_ptr SparseMarkovAutomatonCslModelCheckergetModel()); auto formulaChecker = [&](storm::logic::Formula const& formula) { - return this->check(env, formula)->asExplicitQualitativeCheckResult().getTruthValuesVector(); + return this->check(env, formula)->template asExplicitQualitativeCheckResult().getTruthValuesVector(); }; auto apSets = helper.computeApSets(pathFormula.getAPMapping(), formulaChecker); std::vector numericResult = helper.computeDAProductProbabilities(env, *pathFormula.readAutomaton(), apSets); @@ -183,7 +183,7 @@ std::unique_ptr SparseMarkovAutomatonCslModelCheckergetModel()); auto formulaChecker = [&](storm::logic::Formula const& formula) { - return this->check(env, formula)->asExplicitQualitativeCheckResult().getTruthValuesVector(); + return this->check(env, formula)->template asExplicitQualitativeCheckResult().getTruthValuesVector(); }; std::vector numericResult = helper.computeLTLProbabilities(env, pathFormula, formulaChecker); @@ -205,7 +205,7 @@ std::unique_ptr SparseMarkovAutomatonCslModelCheckergetModel().isClosed(), storm::exceptions::InvalidPropertyException, "Unable to compute reachability rewards in non-closed Markov automaton."); std::unique_ptr subResultPointer = this->check(env, eventuallyFormula.getSubformula()); - ExplicitQualitativeCheckResult const& subResult = subResultPointer->asExplicitQualitativeCheckResult(); + ExplicitQualitativeCheckResult const& subResult = subResultPointer->template asExplicitQualitativeCheckResult(); auto rewardModel = storm::utility::createFilteredRewardModel(this->getModel(), checkTask); auto ret = storm::modelchecker::helper::SparseMarkovAutomatonCslHelper::computeReachabilityRewards( @@ -247,7 +247,7 @@ std::unique_ptr SparseMarkovAutomatonCslModelCheckergetModel().isClosed(), storm::exceptions::InvalidPropertyException, "Unable to compute long-run average in non-closed Markov automaton."); std::unique_ptr subResultPointer = this->check(env, stateFormula); - ExplicitQualitativeCheckResult const& subResult = subResultPointer->asExplicitQualitativeCheckResult(); + ExplicitQualitativeCheckResult const& subResult = subResultPointer->template asExplicitQualitativeCheckResult(); storm::modelchecker::helper::SparseNondeterministicInfiniteHorizonHelper helper( this->getModel().getTransitionMatrix(), this->getModel().getMarkovianStates(), this->getModel().getExitRates()); @@ -291,7 +291,7 @@ std::unique_ptr SparseMarkovAutomatonCslModelCheckergetModel().isClosed(), storm::exceptions::InvalidPropertyException, "Unable to compute expected times in non-closed Markov automaton."); std::unique_ptr subResultPointer = this->check(env, eventuallyFormula.getSubformula()); - ExplicitQualitativeCheckResult& subResult = subResultPointer->asExplicitQualitativeCheckResult(); + ExplicitQualitativeCheckResult& subResult = subResultPointer->template asExplicitQualitativeCheckResult(); auto ret = storm::modelchecker::helper::SparseMarkovAutomatonCslHelper::computeReachabilityTimes( env, checkTask.getOptimizationDirection(), this->getModel().getTransitionMatrix(), this->getModel().getBackwardTransitions(), diff --git a/src/storm/modelchecker/helper/conditional/ConditionalAlgorithmSetting.cpp b/src/storm/modelchecker/helper/conditional/ConditionalAlgorithmSetting.cpp index e909df6278..5837a7a727 100644 --- a/src/storm/modelchecker/helper/conditional/ConditionalAlgorithmSetting.cpp +++ b/src/storm/modelchecker/helper/conditional/ConditionalAlgorithmSetting.cpp @@ -11,6 +11,10 @@ std::ostream& operator<<(std::ostream& stream, ConditionalAlgorithmSetting const return stream << "bisection"; case ConditionalAlgorithmSetting::BisectionAdvanced: return stream << "bisection-advanced"; + case ConditionalAlgorithmSetting::BisectionPolicyTracking: + return stream << "bisection-pt"; + case ConditionalAlgorithmSetting::BisectionAdvancedPolicyTracking: + return stream << "bisection-advanced-pt"; case ConditionalAlgorithmSetting::PolicyIteration: return stream << "pi"; } @@ -27,6 +31,10 @@ ConditionalAlgorithmSetting conditionalAlgorithmSettingFromString(std::string co return ConditionalAlgorithmSetting::Bisection; } else if (algorithm == "bisection-advanced") { return ConditionalAlgorithmSetting::BisectionAdvanced; + } else if (algorithm == "bisection-pt") { + return ConditionalAlgorithmSetting::BisectionPolicyTracking; + } else if (algorithm == "bisection-advanced-pt") { + return ConditionalAlgorithmSetting::BisectionAdvancedPolicyTracking; } else if (algorithm == "pi") { return ConditionalAlgorithmSetting::PolicyIteration; } diff --git a/src/storm/modelchecker/helper/conditional/ConditionalAlgorithmSetting.h b/src/storm/modelchecker/helper/conditional/ConditionalAlgorithmSetting.h index 4a9440abb4..6c77495af9 100644 --- a/src/storm/modelchecker/helper/conditional/ConditionalAlgorithmSetting.h +++ b/src/storm/modelchecker/helper/conditional/ConditionalAlgorithmSetting.h @@ -5,7 +5,15 @@ #include "storm/utility/macros.h" namespace storm { -enum class ConditionalAlgorithmSetting { Default, Restart, Bisection, BisectionAdvanced, PolicyIteration }; +enum class ConditionalAlgorithmSetting { + Default, + Restart, + Bisection, + BisectionAdvanced, + BisectionPolicyTracking, + BisectionAdvancedPolicyTracking, + PolicyIteration +}; std::ostream& operator<<(std::ostream& stream, ConditionalAlgorithmSetting const& algorithm); ConditionalAlgorithmSetting conditionalAlgorithmSettingFromString(std::string const& algorithm); diff --git a/src/storm/modelchecker/helper/conditional/ConditionalHelper.cpp b/src/storm/modelchecker/helper/conditional/ConditionalHelper.cpp index 6d8d3bcc3c..50476e61e8 100644 --- a/src/storm/modelchecker/helper/conditional/ConditionalHelper.cpp +++ b/src/storm/modelchecker/helper/conditional/ConditionalHelper.cpp @@ -1,6 +1,7 @@ #include "storm/modelchecker/helper/conditional/ConditionalHelper.h" #include +#include #include "storm/adapters/RationalNumberAdapter.h" #include "storm/environment/modelchecker/ModelCheckerEnvironment.h" @@ -13,11 +14,15 @@ #include "storm/storage/BitVector.h" #include "storm/storage/MaximalEndComponentDecomposition.h" #include "storm/storage/SparseMatrix.h" +#include "storm/storage/StronglyConnectedComponentDecomposition.h" #include "storm/transformer/EndComponentEliminator.h" #include "storm/utility/Extremum.h" -#include "storm/utility/KwekMehlhorn.h" +#include "storm/utility/OptionalRef.h" +#include "storm/utility/RationalApproximation.h" #include "storm/utility/SignalHandler.h" +#include "storm/utility/constants.h" #include "storm/utility/graph.h" +#include "storm/utility/logging.h" #include "storm/utility/macros.h" namespace storm::modelchecker { @@ -25,12 +30,13 @@ namespace storm::modelchecker { namespace internal { template -void eliminateEndComponents(storm::storage::BitVector possibleEcStates, bool addRowAtRepresentativeState, std::optional representativeRowEntry, - storm::storage::SparseMatrix& matrix, uint64_t& initialState, storm::storage::BitVector& rowsWithSum1, - std::vector& rowValues1, storm::OptionalRef> rowValues2 = {}) { +std::optional::EndComponentEliminatorReturnType> eliminateEndComponents( + storm::storage::BitVector const& possibleEcStates, bool addRowAtRepresentativeState, std::optional const representativeRowEntry, + storm::storage::SparseMatrix& matrix, storm::storage::BitVector& rowsWithSum1, std::vector& rowValues1, + storm::OptionalRef> rowValues2 = {}) { storm::storage::MaximalEndComponentDecomposition ecs(matrix, matrix.transpose(true), possibleEcStates, rowsWithSum1); if (ecs.empty()) { - return; // nothing to do + return {}; // nothing to do } storm::storage::BitVector allRowGroups(matrix.getRowGroupCount(), true); @@ -66,9 +72,6 @@ void eliminateEndComponents(storm::storage::BitVector possibleEcStates, bool add updateRowValue(*rowValues2); } - // update initial state - initialState = ecElimResult.oldToNewStateMapping[initialState]; - // update bitvector storm::storage::BitVector newRowsWithSum1(ecElimResult.newToOldRowMapping.size(), true); uint64_t newRowIndex = 0; @@ -79,26 +82,47 @@ void eliminateEndComponents(storm::storage::BitVector possibleEcStates, bool add ++newRowIndex; } rowsWithSum1 = std::move(newRowsWithSum1); + + return ecElimResult; } template SolutionType solveMinMaxEquationSystem(storm::Environment const& env, storm::storage::SparseMatrix const& matrix, std::vector const& rowValues, storm::storage::BitVector const& rowsWithSum1, - storm::solver::OptimizationDirection const dir, uint64_t const initialState) { + storm::solver::SolveGoal const& goal, uint64_t const initialState, + std::optional>& schedulerOutput) { // Initialize the solution vector. std::vector x(matrix.getRowGroupCount(), storm::utility::zero()); // Set up the solver. - auto solver = storm::solver::GeneralMinMaxLinearEquationSolverFactory().create(env, matrix); - solver->setOptimizationDirection(dir); + storm::solver::GeneralMinMaxLinearEquationSolverFactory factory; + storm::storage::BitVector relevantValues(matrix.getRowGroupCount(), false); + relevantValues.set(initialState, true); + auto getGoal = [&env, &goal, &relevantValues]() -> storm::solver::SolveGoal { + if (goal.isBounded() && env.modelchecker().isAllowOptimizationForBoundedPropertiesSet()) { + return {goal.direction(), goal.boundComparisonType(), goal.thresholdValue(), relevantValues}; + } else { + return {goal.direction(), relevantValues}; + } + }; + auto solver = storm::solver::configureMinMaxLinearEquationSolver(env, getGoal(), factory, matrix); + + storm::solver::GeneralMinMaxLinearEquationSolverFactory().create(env, matrix); + solver->setOptimizationDirection(goal.direction()); solver->setRequirementsChecked(); solver->setHasUniqueSolution(true); solver->setHasNoEndComponents(true); solver->setLowerBound(storm::utility::zero()); solver->setUpperBound(storm::utility::one()); + solver->setTrackScheduler(schedulerOutput.has_value()); // Solve the corresponding system of equations. solver->solveEquations(env, x, rowValues); + + if (schedulerOutput) { + *schedulerOutput = std::move(solver->getSchedulerChoices()); + } + return x[initialState]; } @@ -107,23 +131,30 @@ SolutionType solveMinMaxEquationSystem(storm::Environment const& env, storm::sto * @note This code is optimized for cases where not all states are reachable from the initial states. */ template -void computeReachabilityProbabilities(Environment const& env, std::map& nonZeroResults, storm::solver::OptimizationDirection const dir, - storm::storage::SparseMatrix const& transitionMatrix, storm::storage::BitVector const& initialStates, - storm::storage::BitVector const& allowedStates, storm::storage::BitVector const& targetStates) { +std::unique_ptr> computeReachabilityProbabilities( + Environment const& env, std::map& nonZeroResults, storm::solver::OptimizationDirection const dir, + storm::storage::SparseMatrix const& transitionMatrix, storm::storage::BitVector const& initialStates, + storm::storage::BitVector const& allowedStates, storm::storage::BitVector const& targetStates, bool computeScheduler = true) { + std::unique_ptr> scheduler; + if (computeScheduler) { + scheduler = std::make_unique>(transitionMatrix.getRowGroupCount()); + } + if (initialStates.empty()) { // nothing to do - return; + return scheduler; } auto const reachableStates = storm::utility::graph::getReachableStates(transitionMatrix, initialStates, allowedStates, targetStates); auto const subTargets = targetStates % reachableStates; // Catch the case where no target is reachable from an initial state. In this case, there is nothing to do since all probabilities are zero. if (subTargets.empty()) { - return; + return scheduler; } auto const subInits = initialStates % reachableStates; auto const submatrix = transitionMatrix.getSubmatrix(true, reachableStates, reachableStates); auto const subResult = helper::SparseMdpPrctlHelper::computeUntilProbabilities( env, storm::solver::SolveGoal(dir, subInits), submatrix, submatrix.transpose(true), storm::storage::BitVector(subTargets.size(), true), - subTargets, false, false); + subTargets, false, computeScheduler); + auto origInitIt = initialStates.begin(); for (auto subInit : subInits) { auto const& val = subResult.values[subInit]; @@ -132,6 +163,16 @@ void computeReachabilityProbabilities(Environment const& env, std::mapsetChoice(subResult.scheduler->getChoice(submatrixIdx), state); + ++submatrixIdx; + } + } + + return scheduler; } template @@ -139,6 +180,7 @@ struct NormalFormData { storm::storage::BitVector const maybeStates; // Those states that can be reached from initial without reaching a terminal state storm::storage::BitVector const terminalStates; // Those states where we already know the probability to reach the condition and the target value storm::storage::BitVector const conditionStates; // Those states where the condition holds almost surely (under all schedulers) + storm::storage::BitVector const targetStates; // Those states where the target holds almost surely (under all schedulers) storm::storage::BitVector const universalObservationFailureStates; // Those states where the condition is not reachable (under all schedulers) storm::storage::BitVector const existentialObservationFailureStates; // Those states s where a scheduler exists that (i) does not reach the condition from // s and (ii) acts optimal in all terminal states @@ -152,6 +194,11 @@ struct NormalFormData { // TerminalStates is a superset of conditionStates and dom(nonZeroTargetStateValues). // For a terminalState that is not a conditionState, it is impossible to (reach the condition and not reach the target). + std::unique_ptr> + schedulerChoicesForReachingTargetStates; // Scheduler choices for reaching target states, used for constructing the resulting scheduler + std::unique_ptr> + schedulerChoicesForReachingConditionStates; // Scheduler choices for reaching condition states, used for constructing the resulting scheduler + ValueType getTargetValue(uint64_t state) const { STORM_LOG_ASSERT(terminalStates.get(state), "Tried to get target value for non-terminal state"); auto const it = nonZeroTargetStateValues.find(state); @@ -167,7 +214,7 @@ struct NormalFormData { }; template -NormalFormData obtainNormalForm(Environment const& env, storm::solver::OptimizationDirection const dir, +NormalFormData obtainNormalForm(Environment const& env, storm::solver::OptimizationDirection const dir, bool computeScheduler, storm::storage::SparseMatrix const& transitionMatrix, storm::storage::SparseMatrix const& backwardTransitions, storm::storage::BitVector const& relevantStates, storm::storage::BitVector const& targetStates, storm::storage::BitVector const& conditionStates) { @@ -178,9 +225,13 @@ NormalFormData obtainNormalForm(Environment const& env, storm::solver std::map nonZeroTargetStateValues; auto const extendedTargetStates = storm::utility::graph::performProb1A(transitionMatrix, transitionMatrix.getRowGroupIndices(), backwardTransitions, allStates, targetStates); - computeReachabilityProbabilities(env, nonZeroTargetStateValues, dir, transitionMatrix, extendedConditionStates, allStates, extendedTargetStates); auto const targetAndNotCondFailStates = extendedTargetStates & ~(extendedConditionStates | universalObservationFailureStates); - computeReachabilityProbabilities(env, nonZeroTargetStateValues, dir, transitionMatrix, targetAndNotCondFailStates, allStates, extendedConditionStates); + + // compute schedulers for reaching target and condition states from target and condition states + std::unique_ptr> schedulerChoicesForReachingTargetStates = computeReachabilityProbabilities( + env, nonZeroTargetStateValues, dir, transitionMatrix, extendedConditionStates, allStates, extendedTargetStates, computeScheduler); + std::unique_ptr> schedulerChoicesForReachingConditionStates = computeReachabilityProbabilities( + env, nonZeroTargetStateValues, dir, transitionMatrix, targetAndNotCondFailStates, allStates, extendedConditionStates, computeScheduler); // get states where the optimal policy reaches the condition with positive probability auto terminalStatesThatReachCondition = extendedConditionStates; @@ -211,20 +262,184 @@ NormalFormData obtainNormalForm(Environment const& env, storm::solver return NormalFormData{.maybeStates = std::move(nonTerminalStates), .terminalStates = std::move(terminalStates), .conditionStates = std::move(extendedConditionStates), + .targetStates = std::move(extendedTargetStates), .universalObservationFailureStates = std::move(universalObservationFailureStates), .existentialObservationFailureStates = std::move(existentialObservationFailureStates), - .nonZeroTargetStateValues = std::move(nonZeroTargetStateValues)}; + .nonZeroTargetStateValues = std::move(nonZeroTargetStateValues), + .schedulerChoicesForReachingTargetStates = std::move(schedulerChoicesForReachingTargetStates), + .schedulerChoicesForReachingConditionStates = std::move(schedulerChoicesForReachingConditionStates)}; +} + +// computes the scheduler that reaches the EC exits from the maybe states that were removed by EC elimination +template +void finalizeSchedulerForMaybeStates(storm::storage::Scheduler& scheduler, storm::storage::SparseMatrix const& transitionMatrix, + storm::storage::SparseMatrix const& backwardTransitions, storm::storage::BitVector const& maybeStates, + storm::storage::BitVector const& maybeStatesWithoutChoice, storm::storage::BitVector const& maybeStatesWithChoice, + std::vector const& stateToFinalEc, NormalFormData const& normalForm, uint64_t initialComponentIndex, + storm::storage::BitVector const& initialComponentExitStates, storm::storage::BitVector const& initialComponentExitRows, + uint64_t chosenInitialComponentExitState, uint64_t chosenInitialComponentExit) { + // Compute the EC stay choices for the states in maybeStatesWithChoice + storm::storage::BitVector ecStayChoices(transitionMatrix.getRowCount(), false); + storm::storage::BitVector initialComponentStates(transitionMatrix.getRowGroupCount(), false); + + // compute initial component states and all choices that stay within a given EC + for (auto state : maybeStates) { + auto ecIndex = stateToFinalEc[state]; + if (ecIndex == initialComponentIndex) { + initialComponentStates.set(state, true); + continue; // state part of the initial component + } else if (ecIndex == std::numeric_limits::max()) { + continue; + } + for (auto choiceIndex : transitionMatrix.getRowGroupIndices(state)) { + bool isEcStayChoice = true; + for (auto const& entry : transitionMatrix.getRow(choiceIndex)) { + auto targetState = entry.getColumn(); + if (stateToFinalEc[targetState] != ecIndex) { + isEcStayChoice = false; + break; + } + } + if (isEcStayChoice) { + ecStayChoices.set(choiceIndex, true); + } + } + } + + // fill choices for ECs that reach the chosen EC exit + auto const maybeNonICStatesWithoutChoice = maybeStatesWithoutChoice & ~initialComponentStates; + storm::utility::graph::computeSchedulerProb1E(maybeNonICStatesWithoutChoice, transitionMatrix, backwardTransitions, maybeStates, maybeStatesWithChoice, + scheduler, ecStayChoices); + + // collect all choices from the initial component states and the choices that were selected by the scheduler so far + auto const condOrTargetStates = normalForm.conditionStates | normalForm.targetStates; + auto const& rowGroups = transitionMatrix.getRowGroupIndices(); + storm::storage::BitVector allowedChoices(transitionMatrix.getRowCount(), false); + auto const rowGroupCount = transitionMatrix.getRowGroupCount(); + for (uint64_t state = 0; state < rowGroupCount; ++state) { + if (scheduler.isChoiceSelected(state)) { + auto choiceIndex = scheduler.getChoice(state).getDeterministicChoice(); + allowedChoices.set(rowGroups[state] + choiceIndex, true); + } else if (initialComponentStates.get(state) || condOrTargetStates.get(state)) { + for (auto choiceIndex : transitionMatrix.getRowGroupIndices(state)) { + allowedChoices.set(choiceIndex, true); + } + } + } + + // dfs to find which choices in initial component states lead to condOrTargetStates + storm::storage::BitVector choicesThatCanVisitCondOrTargetStates(transitionMatrix.getRowCount(), false); + std::stack toProcess; + for (auto state : condOrTargetStates) { + toProcess.push(state); + } + auto visitedStates = condOrTargetStates; + while (!toProcess.empty()) { + auto currentState = toProcess.top(); + toProcess.pop(); + for (auto const& entry : backwardTransitions.getRow(currentState)) { + uint64_t const predecessorState = entry.getColumn(); + for (uint64_t const predecessorChoice : transitionMatrix.getRowGroupIndices(predecessorState)) { + if (!allowedChoices.get(predecessorChoice) || choicesThatCanVisitCondOrTargetStates.get(predecessorChoice)) { + continue; // The choice is either not allowed or has been considered already + } + if (auto const r = transitionMatrix.getRow(predecessorChoice); + std::none_of(r.begin(), r.end(), [¤tState](auto const& e) { return e.getColumn() == currentState; })) { + continue; // not an actual predecessor choice + } + choicesThatCanVisitCondOrTargetStates.set(predecessorChoice, true); + if (!visitedStates.get(predecessorState)) { + visitedStates.set(predecessorState, true); + toProcess.push(predecessorState); + } + } + } + } + + // we want to disallow taking initial component exits that can lead to a condition or target state, beside the one exit that was chosen + storm::storage::BitVector disallowedInitialComponentExits = initialComponentExitRows & choicesThatCanVisitCondOrTargetStates; + disallowedInitialComponentExits.set(chosenInitialComponentExit, false); + storm::storage::BitVector choicesAllowedForInitialComponent = allowedChoices & ~disallowedInitialComponentExits; + + storm::storage::BitVector goodInitialComponentStates = initialComponentStates; + bool progress = false; + for (auto state : initialComponentExitStates) { + auto const groupStart = transitionMatrix.getRowGroupIndices()[state]; + auto const groupEnd = transitionMatrix.getRowGroupIndices()[state + 1]; + bool const allChoicesAreDisallowed = disallowedInitialComponentExits.getNextUnsetIndex(groupStart) >= groupEnd; + if (allChoicesAreDisallowed) { + goodInitialComponentStates.set(state, false); + progress = true; + } + } + while (progress) { + progress = false; + for (auto state : goodInitialComponentStates) { + bool allChoicesAreDisallowed = true; + for (auto choiceIndex : transitionMatrix.getRowGroupIndices(state)) { + auto row = transitionMatrix.getRow(choiceIndex); + bool const hasBadSuccessor = std::any_of( + row.begin(), row.end(), [&goodInitialComponentStates](auto const& entry) { return !goodInitialComponentStates.get(entry.getColumn()); }); + if (hasBadSuccessor) { + choicesAllowedForInitialComponent.set(choiceIndex, false); + } else { + allChoicesAreDisallowed = false; + } + } + if (allChoicesAreDisallowed) { + goodInitialComponentStates.set(state, false); + progress = true; + } + } + } + + storm::storage::BitVector exitStateBitvector(transitionMatrix.getRowGroupCount(), false); + exitStateBitvector.set(chosenInitialComponentExitState, true); + + storm::utility::graph::computeSchedulerProbGreater0E(transitionMatrix, backwardTransitions, initialComponentStates, exitStateBitvector, scheduler, + choicesAllowedForInitialComponent); + + // fill the choices of initial component states that do not have a choice yet + // these states should not reach the condition or target states under the constructed scheduler + for (auto state : initialComponentStates) { + if (!scheduler.isChoiceSelected(state)) { + for (auto choiceIndex : transitionMatrix.getRowGroupIndices(state)) { + if (choicesAllowedForInitialComponent.get(choiceIndex)) { + scheduler.setChoice(choiceIndex - rowGroups[state], state); + break; + } + } + } + } } +template +struct ResultReturnType { + ResultReturnType(ValueType initialStateValue, std::unique_ptr>&& scheduler = nullptr) + : initialStateValue(initialStateValue), scheduler(std::move(scheduler)) { + // Intentionally left empty. + } + + bool hasScheduler() const { + return static_cast(scheduler); + } + + ValueType initialStateValue; + std::unique_ptr> scheduler; +}; + /*! * Uses the restart method by Baier et al. // @see doi.org/10.1007/978-3-642-54862-8_43 */ template -SolutionType computeViaRestartMethod(Environment const& env, uint64_t const initialState, storm::solver::OptimizationDirection const dir, - storm::storage::SparseMatrix const& transitionMatrix, NormalFormData const& normalForm) { +typename internal::ResultReturnType computeViaRestartMethod(Environment const& env, uint64_t const initialState, + storm::solver::SolveGoal const& goal, bool computeScheduler, + storm::storage::SparseMatrix const& transitionMatrix, + storm::storage::SparseMatrix const& backwardTransitions, + NormalFormData const& normalForm) { auto const& maybeStates = normalForm.maybeStates; - auto const stateToMatrixIndexMap = maybeStates.getNumberOfSetBitsBeforeIndices(); + auto originalToReducedStateIndexMap = maybeStates.getNumberOfSetBitsBeforeIndices(); auto const numMaybeStates = maybeStates.getNumberOfSetBits(); auto const numMaybeChoices = transitionMatrix.getNumRowsInRowGroups(maybeStates); @@ -266,39 +481,146 @@ SolutionType computeViaRestartMethod(Environment const& env, uint64_t const init // Insert backloop probability if we haven't done so yet and are past the initial state index // This is to avoid a costly out-of-order insertion into the matrix if (addRestartTransition && entry.getColumn() > initialState) { - matrixBuilder.addNextValue(currentRow, stateToMatrixIndexMap[initialState], restartProbability); + matrixBuilder.addNextValue(currentRow, originalToReducedStateIndexMap[initialState], restartProbability); addRestartTransition = false; } if (maybeStates.get(entry.getColumn())) { - matrixBuilder.addNextValue(currentRow, stateToMatrixIndexMap[entry.getColumn()], entry.getValue()); + matrixBuilder.addNextValue(currentRow, originalToReducedStateIndexMap[entry.getColumn()], entry.getValue()); } } // Add the backloop if we haven't done this already if (addRestartTransition) { - matrixBuilder.addNextValue(currentRow, stateToMatrixIndexMap[initialState], restartProbability); + matrixBuilder.addNextValue(currentRow, originalToReducedStateIndexMap[initialState], restartProbability); } ++currentRow; } } + STORM_LOG_ASSERT(currentRow == numMaybeChoices, "Unexpected number of constructed rows."); auto matrix = matrixBuilder.build(); - auto initStateInMatrix = stateToMatrixIndexMap[initialState]; + auto initStateInReduced = originalToReducedStateIndexMap[initialState]; // Eliminate end components in two phases // First, we catch all end components that do not contain the initial state. It is possible to stay in those ECs forever // without reaching the condition. This is reflected by a backloop to the initial state. - storm::storage::BitVector selectedStatesInMatrix(numMaybeStates, true); - selectedStatesInMatrix.set(initStateInMatrix, false); - eliminateEndComponents(selectedStatesInMatrix, true, initStateInMatrix, matrix, initStateInMatrix, rowsWithSum1, rowValues); + storm::storage::BitVector selectedStatesInReduced(numMaybeStates, true); + selectedStatesInReduced.set(initStateInReduced, false); + auto ecElimResult1 = eliminateEndComponents(selectedStatesInReduced, true, initStateInReduced, matrix, rowsWithSum1, rowValues); + selectedStatesInReduced.set(initStateInReduced, true); + if (ecElimResult1) { + selectedStatesInReduced.resize(matrix.getRowGroupCount(), true); + initStateInReduced = ecElimResult1->oldToNewStateMapping[initStateInReduced]; + } // Second, eliminate the remaining ECs. These must involve the initial state and might have been introduced in the previous step. // A policy selecting such an EC must reach the condition with probability zero and is thus invalid. - selectedStatesInMatrix.set(initStateInMatrix, true); - eliminateEndComponents(selectedStatesInMatrix, false, std::nullopt, matrix, initStateInMatrix, rowsWithSum1, rowValues); + auto ecElimResult2 = eliminateEndComponents(selectedStatesInReduced, false, std::nullopt, matrix, rowsWithSum1, rowValues); + if (ecElimResult2) { + initStateInReduced = ecElimResult2->oldToNewStateMapping[initStateInReduced]; + } + + STORM_PRINT_AND_LOG("Processed model has " << matrix.getRowGroupCount() << " states and " << matrix.getRowGroupCount() << " choices and " + << matrix.getEntryCount() << " transitions."); + + // Finally, solve the equation system, potentially computing a scheduler + std::optional> reducedSchedulerChoices; + if (computeScheduler) { + reducedSchedulerChoices.emplace(); + } + auto resultValue = solveMinMaxEquationSystem(env, matrix, rowValues, rowsWithSum1, goal, initStateInReduced, reducedSchedulerChoices); + + // Create result (scheduler potentially added below) + auto finalResult = ResultReturnType(resultValue); + + if (!computeScheduler) { + return finalResult; + } + // At this point we have to reconstruct the scheduler for the original model + STORM_LOG_ASSERT(reducedSchedulerChoices.has_value() && reducedSchedulerChoices->size() == matrix.getRowGroupCount(), + "Requested scheduler, but it was not computed or has invalid size."); + // For easier access, we create and update some index mappings + std::vector originalRowToStateIndexMap; // maps original row indices to original state indices. transitionMatrix.getRowGroupIndices() are the + // inverse of that mapping + originalRowToStateIndexMap.reserve(transitionMatrix.getRowCount()); + for (uint64_t originalStateIndex = 0; originalStateIndex < transitionMatrix.getRowGroupCount(); ++originalStateIndex) { + originalRowToStateIndexMap.insert(originalRowToStateIndexMap.end(), transitionMatrix.getRowGroupSize(originalStateIndex), originalStateIndex); + } + std::vector reducedToOriginalRowIndexMap; // maps row indices of the reduced model to the original ones + reducedToOriginalRowIndexMap.reserve(numMaybeChoices); + for (uint64_t const originalMaybeState : maybeStates) { + for (auto const originalRowIndex : transitionMatrix.getRowGroupIndices(originalMaybeState)) { + reducedToOriginalRowIndexMap.push_back(originalRowIndex); + } + } + if (ecElimResult1.has_value() || ecElimResult2.has_value()) { + // reducedToOriginalRowIndexMap needs to be updated so it maps from rows of the ec-eliminated system + std::vector tmpReducedToOriginalRowIndexMap; + tmpReducedToOriginalRowIndexMap.reserve(matrix.getRowCount()); + for (uint64_t reducedRow = 0; reducedRow < matrix.getRowCount(); ++reducedRow) { + uint64_t intermediateRow = reducedRow; + if (ecElimResult2.has_value()) { + intermediateRow = ecElimResult2->newToOldRowMapping.at(intermediateRow); + } + if (ecElimResult1.has_value()) { + intermediateRow = ecElimResult1->newToOldRowMapping.at(intermediateRow); + } + tmpReducedToOriginalRowIndexMap.push_back(reducedToOriginalRowIndexMap[intermediateRow]); + } + reducedToOriginalRowIndexMap = std::move(tmpReducedToOriginalRowIndexMap); + // originalToReducedStateIndexMap needs to be updated so it maps into the ec-eliminated system + for (uint64_t originalStateIndex = 0; originalStateIndex < transitionMatrix.getRowGroupCount(); ++originalStateIndex) { + auto& reducedIndex = originalToReducedStateIndexMap[originalStateIndex]; + if (maybeStates.get(originalStateIndex)) { + if (ecElimResult1.has_value()) { + reducedIndex = ecElimResult1->oldToNewStateMapping.at(reducedIndex); + } + if (ecElimResult2.has_value()) { + reducedIndex = ecElimResult2->oldToNewStateMapping.at(reducedIndex); + } + } else { + reducedIndex = std::numeric_limits::max(); // The original state does not exist in the reduced model. + } + } + } + + storm::storage::BitVector initialComponentExitRows(transitionMatrix.getRowCount(), false); + storm::storage::BitVector initialComponentExitStates(transitionMatrix.getRowGroupCount(), false); + for (auto const reducedRowIndex : matrix.getRowGroupIndices(initStateInReduced)) { + uint64_t const originalRowIndex = reducedToOriginalRowIndexMap[reducedRowIndex]; + uint64_t const originalState = originalRowToStateIndexMap[originalRowIndex]; + + initialComponentExitRows.set(originalRowIndex, true); + initialComponentExitStates.set(originalState, true); + } + + // If requested, construct the scheduler for the original model + storm::storage::BitVector maybeStatesWithChoice(maybeStates.size(), false); + uint64_t chosenInitialComponentExitState = std::numeric_limits::max(); + uint64_t chosenInitialComponentExit = std::numeric_limits::max(); + auto scheduler = std::make_unique>(transitionMatrix.getRowGroupCount()); + + uint64_t reducedState = 0; + for (auto const& choice : reducedSchedulerChoices.value()) { + uint64_t const reducedRowIndex = matrix.getRowGroupIndices()[reducedState] + choice; + uint64_t const originalRowIndex = reducedToOriginalRowIndexMap[reducedRowIndex]; + uint64_t const originalState = originalRowToStateIndexMap[originalRowIndex]; + uint64_t const originalChoice = originalRowIndex - transitionMatrix.getRowGroupIndices()[originalState]; + scheduler->setChoice(originalChoice, originalState); + maybeStatesWithChoice.set(originalState, true); + if (reducedState == initStateInReduced) { + chosenInitialComponentExitState = originalState; + chosenInitialComponentExit = originalRowIndex; + } + ++reducedState; + } + + auto const maybeStatesWithoutChoice = maybeStates & ~maybeStatesWithChoice; + finalizeSchedulerForMaybeStates(*scheduler, transitionMatrix, backwardTransitions, maybeStates, maybeStatesWithoutChoice, maybeStatesWithChoice, + originalToReducedStateIndexMap, normalForm, initStateInReduced, initialComponentExitStates, initialComponentExitRows, + chosenInitialComponentExitState, chosenInitialComponentExit); - STORM_LOG_INFO("Processed model has " << matrix.getRowGroupCount() << " states and " << matrix.getRowGroupCount() << " choices and " - << matrix.getEntryCount() << " transitions."); - // Finally, solve the equation system - return solveMinMaxEquationSystem(env, matrix, rowValues, rowsWithSum1, dir, initStateInMatrix); + finalResult.scheduler = std::move(scheduler); + + return finalResult; } /*! @@ -310,7 +632,7 @@ template class WeightedReachabilityHelper { public: WeightedReachabilityHelper(uint64_t const initialState, storm::storage::SparseMatrix const& transitionMatrix, - NormalFormData const& normalForm) { + NormalFormData const& normalForm, bool computeScheduler) { // Determine rowgroups (states) and rows (choices) of the submatrix auto subMatrixRowGroups = normalForm.maybeStates; // Identify and eliminate the initial component to enforce that it is eventually exited @@ -321,7 +643,8 @@ class WeightedReachabilityHelper { // An optimal scheduler can intuitively pick the best exiting action of C and enforce that all paths that satisfy the condition exit C through that // action. By eliminating the initial component, we ensure that only policies that actually exit C are considered. The remaining policies have // probability zero of satisfying the condition. - storm::storage::BitVector initialComponentExitRows(transitionMatrix.getRowCount(), false); + initialComponentExitRows = storm::storage::BitVector(transitionMatrix.getRowCount(), false); + initialComponentExitStates = storm::storage::BitVector(transitionMatrix.getRowGroupCount(), false); subMatrixRowGroups.set(initialState, false); // temporarily unset initial state std::vector dfsStack = {initialState}; while (!dfsStack.empty()) { @@ -340,6 +663,7 @@ class WeightedReachabilityHelper { } } else { initialComponentExitRows.set(rowIndex, true); + initialComponentExitStates.set(state, true); } } } @@ -347,13 +671,14 @@ class WeightedReachabilityHelper { subMatrixRowGroups.set(initialState, true); // set initial state again, as single representative state for the initial component auto const numSubmatrixRowGroups = subMatrixRowGroups.getNumberOfSetBits(); + if (computeScheduler) { + reducedToOriginalRowIndexMap.reserve(numSubmatrixRows); + } + // state index mapping and initial state - auto stateToMatrixIndexMap = subMatrixRowGroups.getNumberOfSetBitsBeforeIndices(); - initialStateInSubmatrix = stateToMatrixIndexMap[initialState]; + originalToReducedStateIndexMap = subMatrixRowGroups.getNumberOfSetBitsBeforeIndices(); + initialStateInSubmatrix = originalToReducedStateIndexMap[initialState]; auto const eliminatedInitialComponentStates = normalForm.maybeStates & ~subMatrixRowGroups; - for (auto state : eliminatedInitialComponentStates) { - stateToMatrixIndexMap[state] = initialStateInSubmatrix; // map all eliminated states to the initial state - } // build matrix, rows that sum up to 1, target values, condition values storm::storage::SparseMatrixBuilder matrixBuilder(numSubmatrixRows, numSubmatrixRowGroups, 0, true, true, numSubmatrixRowGroups); @@ -366,13 +691,10 @@ class WeightedReachabilityHelper { // Put the row processing into a lambda for avoiding code duplications auto processRow = [&](uint64_t origRowIndex) { - // We make two passes. First, we find out the probability to reach an eliminated initial component state - ValueType const eliminatedInitialComponentProbability = transitionMatrix.getConstrainedRowSum(origRowIndex, eliminatedInitialComponentStates); - // Second, we insert the submatrix entries and find out the target and condition probabilities for this row + // insert the submatrix entries and find out the target and condition probabilities for this row ValueType targetProbability = storm::utility::zero(); ValueType conditionProbability = storm::utility::zero(); bool rowSumIsLess1 = false; - bool initialStateEntryInserted = false; for (auto const& entry : transitionMatrix.getRow(origRowIndex)) { if (normalForm.terminalStates.get(entry.getColumn())) { STORM_LOG_ASSERT(!storm::utility::isZero(entry.getValue()), "Transition probability must be non-zero"); @@ -384,19 +706,11 @@ class WeightedReachabilityHelper { } else { conditionProbability += scaledTargetValue; // for terminal, non-condition states, the condition value equals the target value } - } else if (!eliminatedInitialComponentStates.get(entry.getColumn())) { - auto const columnIndex = stateToMatrixIndexMap[entry.getColumn()]; - if (!initialStateEntryInserted && columnIndex >= initialStateInSubmatrix) { - if (columnIndex == initialStateInSubmatrix) { - matrixBuilder.addNextValue(currentRow, initialStateInSubmatrix, eliminatedInitialComponentProbability + entry.getValue()); - } else { - matrixBuilder.addNextValue(currentRow, initialStateInSubmatrix, eliminatedInitialComponentProbability); - matrixBuilder.addNextValue(currentRow, columnIndex, entry.getValue()); - } - initialStateEntryInserted = true; - } else { - matrixBuilder.addNextValue(currentRow, columnIndex, entry.getValue()); - } + } else if (eliminatedInitialComponentStates.get(entry.getColumn())) { + rowSumIsLess1 = true; + } else { + auto const columnIndex = originalToReducedStateIndexMap[entry.getColumn()]; + matrixBuilder.addNextValue(currentRow, columnIndex, entry.getValue()); } } if (rowSumIsLess1) { @@ -410,10 +724,12 @@ class WeightedReachabilityHelper { if (state == initialState) { for (auto origRowIndex : initialComponentExitRows) { processRow(origRowIndex); + reducedToOriginalRowIndexMap.push_back(origRowIndex); } } else { for (auto origRowIndex : transitionMatrix.getRowGroupIndices(state)) { processRow(origRowIndex); + reducedToOriginalRowIndexMap.push_back(origRowIndex); } } } @@ -423,30 +739,77 @@ class WeightedReachabilityHelper { // For all remaining ECs, staying in an EC forever is reflected by collecting a value of zero for both, target and condition storm::storage::BitVector allExceptInit(numSubmatrixRowGroups, true); allExceptInit.set(initialStateInSubmatrix, false); - eliminateEndComponents(allExceptInit, true, std::nullopt, submatrix, initialStateInSubmatrix, rowsWithSum1, targetRowValues, - conditionRowValues); - STORM_LOG_INFO("Processed model has " << submatrix.getRowGroupCount() << " states and " << submatrix.getRowGroupCount() << " choices and " - << submatrix.getEntryCount() << " transitions."); + ecResult = eliminateEndComponents(allExceptInit, true, std::nullopt, submatrix, rowsWithSum1, targetRowValues, conditionRowValues); + if (ecResult) { + initialStateInSubmatrix = ecResult->oldToNewStateMapping[initialStateInSubmatrix]; + } + isAcyclic = !storm::utility::graph::hasCycle(submatrix); + STORM_PRINT_AND_LOG("Processed model has " << submatrix.getRowGroupCount() << " states and " << submatrix.getRowGroupCount() << " choices and " + << submatrix.getEntryCount() << " transitions. Matrix is " << (isAcyclic ? "acyclic." : "cyclic.")); + + if (computeScheduler) { + // For easier conversion of schedulers to the original model, we create and update some index mappings + STORM_LOG_ASSERT(reducedToOriginalRowIndexMap.size() == numSubmatrixRows, "Unexpected size of reducedToOriginalRowIndexMap."); + if (ecResult.has_value()) { + // reducedToOriginalRowIndexMap needs to be updated so it maps from rows of the ec-eliminated system + std::vector tmpReducedToOriginalRowIndexMap; + tmpReducedToOriginalRowIndexMap.reserve(submatrix.getRowCount()); + for (uint64_t reducedRow = 0; reducedRow < submatrix.getRowCount(); ++reducedRow) { + uint64_t intermediateRow = ecResult->newToOldRowMapping.at(reducedRow); + tmpReducedToOriginalRowIndexMap.push_back(reducedToOriginalRowIndexMap[intermediateRow]); + } + reducedToOriginalRowIndexMap = std::move(tmpReducedToOriginalRowIndexMap); + // originalToReducedStateIndexMap needs to be updated so it maps into the ec-eliminated system + for (uint64_t originalStateIndex = 0; originalStateIndex < transitionMatrix.getRowGroupCount(); ++originalStateIndex) { + auto& reducedIndex = originalToReducedStateIndexMap[originalStateIndex]; + if (subMatrixRowGroups.get(originalStateIndex)) { + reducedIndex = ecResult->oldToNewStateMapping.at(reducedIndex); + } else { + reducedIndex = std::numeric_limits::max(); // The original state does not exist in the reduced model. + } + } + } + } else { + // Clear data that is only needed if we compute schedulers + originalToReducedStateIndexMap.clear(); + reducedToOriginalRowIndexMap.clear(); + ecResult.emplace(); + initialComponentExitRows.clear(); + initialComponentExitStates.clear(); + } } SolutionType computeWeightedDiff(storm::Environment const& env, storm::OptimizationDirection const dir, ValueType const& targetWeight, - ValueType const& conditionWeight) const { - auto rowValues = createScaledVector(targetWeight, targetRowValues, conditionWeight, conditionRowValues); + ValueType const& conditionWeight, storm::OptionalRef> schedulerOutput = {}) { + // Set up the solver. + if (!cachedSolver) { + auto solverEnv = env; + if (isAcyclic) { + STORM_LOG_INFO("Using acyclic min-max solver for weighted reachability computation."); + solverEnv.solver().minMax().setMethod(storm::solver::MinMaxMethod::Acyclic); + } + cachedSolver = storm::solver::GeneralMinMaxLinearEquationSolverFactory().create(solverEnv, submatrix); + cachedSolver->setCachingEnabled(true); + cachedSolver->setRequirementsChecked(); + cachedSolver->setHasUniqueSolution(true); + cachedSolver->setHasNoEndComponents(true); + cachedSolver->setLowerBound(-storm::utility::one()); + cachedSolver->setUpperBound(storm::utility::one()); + } + cachedSolver->setTrackScheduler(schedulerOutput.has_value()); + cachedSolver->setOptimizationDirection(dir); - // Initialize the solution vector. - std::vector x(submatrix.getRowGroupCount(), storm::utility::zero()); + // Initialize the right-hand side vector. + createScaledVector(cachedB, targetWeight, targetRowValues, conditionWeight, conditionRowValues); - // Set up the solver. - auto solver = storm::solver::GeneralMinMaxLinearEquationSolverFactory().create(env, submatrix); - solver->setOptimizationDirection(dir); - solver->setRequirementsChecked(); - solver->setHasUniqueSolution(true); - solver->setHasNoEndComponents(true); - solver->setLowerBound(-storm::utility::one()); - solver->setUpperBound(storm::utility::one()); + // Initialize the solution vector. + cachedX.assign(submatrix.getRowGroupCount(), storm::utility::zero()); - solver->solveEquations(env, x, rowValues); - return x[initialStateInSubmatrix]; + cachedSolver->solveEquations(env, cachedX, cachedB); + if (schedulerOutput) { + *schedulerOutput = cachedSolver->getSchedulerChoices(); + } + return cachedX[initialStateInSubmatrix]; } auto getInternalInitialState() const { @@ -454,7 +817,7 @@ class WeightedReachabilityHelper { } void evaluateScheduler(storm::Environment const& env, std::vector& scheduler, std::vector& targetResults, - std::vector& conditionResults) const { + std::vector& conditionResults) { if (scheduler.empty()) { scheduler.resize(submatrix.getRowGroupCount(), 0); } @@ -464,23 +827,31 @@ class WeightedReachabilityHelper { if (conditionResults.empty()) { conditionResults.resize(submatrix.getRowGroupCount(), storm::utility::zero()); } - // apply the scheduler - storm::solver::GeneralLinearEquationSolverFactory factory; - bool const convertToEquationSystem = factory.getEquationProblemFormat(env) == storm::solver::LinearEquationSolverProblemFormat::EquationSystem; - auto scheduledMatrix = submatrix.selectRowsFromRowGroups(scheduler, convertToEquationSystem); - if (convertToEquationSystem) { - scheduledMatrix.convertToEquationSystem(); - } - auto solver = factory.create(env, std::move(scheduledMatrix)); - solver->setBounds(storm::utility::zero(), storm::utility::one()); - solver->setCachingEnabled(true); + auto solver = getScheduledSolver(env, scheduler); + + cachedB.resize(submatrix.getRowGroupCount()); + storm::utility::vector::selectVectorValues(cachedB, scheduler, submatrix.getRowGroupIndices(), targetRowValues); + solver->solveEquations(env, targetResults, cachedB); + + storm::utility::vector::selectVectorValues(cachedB, scheduler, submatrix.getRowGroupIndices(), conditionRowValues); + solver->solveEquations(env, conditionResults, cachedB); + } + + SolutionType evaluateScheduler(storm::Environment const& env, std::vector const& scheduler) { + STORM_LOG_ASSERT(scheduler.size() == submatrix.getRowGroupCount(), "Scheduler size does not match number of row groups"); + auto solver = getScheduledSolver(env, scheduler); + cachedB.resize(submatrix.getRowGroupCount()); + cachedX.resize(submatrix.getRowGroupCount()); - std::vector subB(submatrix.getRowGroupCount()); - storm::utility::vector::selectVectorValues(subB, scheduler, submatrix.getRowGroupIndices(), targetRowValues); - solver->solveEquations(env, targetResults, subB); + storm::utility::vector::selectVectorValues(cachedB, scheduler, submatrix.getRowGroupIndices(), targetRowValues); + solver->solveEquations(env, cachedX, cachedB); + SolutionType targetValue = cachedX[initialStateInSubmatrix]; - storm::utility::vector::selectVectorValues(subB, scheduler, submatrix.getRowGroupIndices(), conditionRowValues); - solver->solveEquations(env, conditionResults, subB); + storm::utility::vector::selectVectorValues(cachedB, scheduler, submatrix.getRowGroupIndices(), conditionRowValues); + solver->solveEquations(env, cachedX, cachedB); + SolutionType conditionValue = cachedX[initialStateInSubmatrix]; + + return targetValue / conditionValue; } template @@ -511,16 +882,64 @@ class WeightedReachabilityHelper { return improved; } + std::unique_ptr> constructSchedulerForInputModel( + std::vector const& schedulerForReducedModel, storm::storage::SparseMatrix const& originalTransitionMatrix, + storm::storage::SparseMatrix const& originalBackwardTransitions, NormalFormData const& normalForm) const { + std::vector originalRowToStateIndexMap; // maps original row indices to original state indices. transitionMatrix.getRowGroupIndices() are + // the inverse of that mapping + originalRowToStateIndexMap.reserve(originalTransitionMatrix.getRowCount()); + for (uint64_t originalStateIndex = 0; originalStateIndex < originalTransitionMatrix.getRowGroupCount(); ++originalStateIndex) { + originalRowToStateIndexMap.insert(originalRowToStateIndexMap.end(), originalTransitionMatrix.getRowGroupSize(originalStateIndex), + originalStateIndex); + } + + storm::storage::BitVector maybeStatesWithChoice(normalForm.maybeStates.size(), false); + uint64_t chosenInitialComponentExitState = std::numeric_limits::max(); + uint64_t chosenInitialComponentExit = std::numeric_limits::max(); + auto scheduler = std::make_unique>(originalTransitionMatrix.getRowGroupCount()); + + uint64_t reducedState = 0; + for (auto const& choice : schedulerForReducedModel) { + uint64_t const reducedRowIndex = submatrix.getRowGroupIndices()[reducedState] + choice; + uint64_t const originalRowIndex = reducedToOriginalRowIndexMap[reducedRowIndex]; + uint64_t const originalState = originalRowToStateIndexMap[originalRowIndex]; + uint64_t const originalChoice = originalRowIndex - originalTransitionMatrix.getRowGroupIndices()[originalState]; + scheduler->setChoice(originalChoice, originalState); + maybeStatesWithChoice.set(originalState, true); + if (reducedState == initialStateInSubmatrix) { + chosenInitialComponentExitState = originalState; + chosenInitialComponentExit = originalRowIndex; + } + ++reducedState; + } + + auto const maybeStatesWithoutChoice = normalForm.maybeStates & ~maybeStatesWithChoice; + finalizeSchedulerForMaybeStates(*scheduler, originalTransitionMatrix, originalBackwardTransitions, normalForm.maybeStates, maybeStatesWithoutChoice, + maybeStatesWithChoice, originalToReducedStateIndexMap, normalForm, initialStateInSubmatrix, initialComponentExitStates, + initialComponentExitRows, chosenInitialComponentExitState, chosenInitialComponentExit); + return scheduler; + } + private: - std::vector createScaledVector(ValueType const& w1, std::vector const& v1, ValueType const& w2, - std::vector const& v2) const { + void createScaledVector(std::vector& out, ValueType const& w1, std::vector const& v1, ValueType const& w2, + std::vector const& v2) const { STORM_LOG_ASSERT(v1.size() == v2.size(), "Vector sizes must match"); - std::vector result; - result.reserve(v1.size()); - for (size_t i = 0; i < v1.size(); ++i) { - result.push_back(w1 * v1[i] + w2 * v2[i]); + out.resize(v1.size()); + storm::utility::vector::applyPointwise(v1, v2, out, [&w1, &w2](ValueType const& a, ValueType const& b) -> ValueType { return w1 * a + w2 * b; }); + } + + auto getScheduledSolver(storm::Environment const& env, std::vector const& scheduler) const { + // apply the scheduler + storm::solver::GeneralLinearEquationSolverFactory factory; + bool const convertToEquationSystem = factory.getEquationProblemFormat(env) == storm::solver::LinearEquationSolverProblemFormat::EquationSystem; + auto scheduledMatrix = submatrix.selectRowsFromRowGroups(scheduler, convertToEquationSystem); + if (convertToEquationSystem) { + scheduledMatrix.convertToEquationSystem(); } - return result; + auto solver = factory.create(env, std::move(scheduledMatrix)); + solver->setBounds(storm::utility::zero(), storm::utility::one()); + solver->setCachingEnabled(true); + return solver; } storm::storage::SparseMatrix submatrix; @@ -528,63 +947,96 @@ class WeightedReachabilityHelper { std::vector targetRowValues; std::vector conditionRowValues; uint64_t initialStateInSubmatrix; + bool isAcyclic; + std::unique_ptr> cachedSolver; + std::vector cachedX; + std::vector cachedB; + + // Data used to translate schedulers: + std::vector originalToReducedStateIndexMap; + std::vector reducedToOriginalRowIndexMap; + std::optional::EndComponentEliminatorReturnType> ecResult; + storm::storage::BitVector initialComponentExitRows; + storm::storage::BitVector initialComponentExitStates; }; -enum class BisectionMethodBounds { Simple, Advanced }; template -SolutionType computeViaBisection(Environment const& env, BisectionMethodBounds boundOption, uint64_t const initialState, - storm::solver::OptimizationDirection const dir, storm::storage::SparseMatrix const& transitionMatrix, - NormalFormData const& normalForm) { +typename internal::ResultReturnType computeViaBisection(Environment const& env, bool const useAdvancedBounds, bool const usePolicyTracking, + uint64_t const initialState, storm::solver::SolveGoal const& goal, + bool computeScheduler, storm::storage::SparseMatrix const& transitionMatrix, + storm::storage::SparseMatrix const& backwardTransitions, + NormalFormData const& normalForm) { // We currently handle sound model checking incorrectly: we would need the actual lower/upper bounds of the weightedReachabilityHelper - STORM_LOG_WARN_COND(!env.solver().isForceSoundness(), - "Bisection method does not adequately handle propagation of errors. Result is not necessarily sound."); - SolutionType const precision = [&env, boundOption]() { - if (storm::NumberTraits::IsExact || env.solver().isForceExact()) { - STORM_LOG_WARN_COND(storm::NumberTraits::IsExact && boundOption == BisectionMethodBounds::Advanced, - "Selected bisection method with exact precision in a setting that might not terminate."); - return storm::utility::zero(); - } else { - return storm::utility::convertNumber(env.solver().minMax().getPrecision()); - } + SolutionType const precision = [&env]() { + // TODO: Discussed that this may be better in the solveGoal or checkTask, but lets be pragmatic today. + return storm::utility::convertNumber(env.modelchecker().getConditionalTolerance()); }(); + STORM_LOG_WARN_COND(!(env.solver().isForceSoundness() && storm::utility::isZero(precision)), + "Bisection method does not adequately handle propagation of errors. Result is not necessarily sound."); + bool const relative = env.solver().minMax().getRelativeTerminationCriterion(); - WeightedReachabilityHelper wrh(initialState, transitionMatrix, normalForm); + WeightedReachabilityHelper wrh(initialState, transitionMatrix, normalForm, computeScheduler); SolutionType pMin{storm::utility::zero()}; SolutionType pMax{storm::utility::one()}; - if (boundOption == BisectionMethodBounds::Advanced) { + if (useAdvancedBounds) { pMin = wrh.computeWeightedDiff(env, storm::OptimizationDirection::Minimize, storm::utility::zero(), storm::utility::one()); pMax = wrh.computeWeightedDiff(env, storm::OptimizationDirection::Maximize, storm::utility::zero(), storm::utility::one()); STORM_LOG_TRACE("Conditioning event bounds:\n\t Lower bound: " << storm::utility::convertNumber(pMin) << ",\n\t Upper bound: " << storm::utility::convertNumber(pMax)); } - storm::utility::Extremum lowerBound = storm::utility::zero(); - storm::utility::Extremum upperBound = storm::utility::one(); - SolutionType middle = (*lowerBound + *upperBound) / 2; + storm::utility::Maximum lowerBound = storm::utility::zero(); + storm::utility::Minimum upperBound = storm::utility::one(); + + std::optional> lowerScheduler, upperScheduler, middleScheduler; + storm::OptionalRef> middleSchedulerRef; + if (usePolicyTracking) { + lowerScheduler.emplace(); + upperScheduler.emplace(); + middleScheduler.emplace(); + middleSchedulerRef.reset(*middleScheduler); + } + + SolutionType middle = goal.isBounded() ? goal.thresholdValue() : (*lowerBound + *upperBound) / 2; + [[maybe_unused]] SolutionType rationalCandiate = middle; // relevant for exact computations + [[maybe_unused]] uint64_t rationalCandidateCount = 0; + std::set checkedMiddleValues; // Middle values that have been checked already + bool terminatedThroughPolicyTracking = false; for (uint64_t iterationCount = 1; true; ++iterationCount) { // evaluate the current middle - SolutionType const middleValue = wrh.computeWeightedDiff(env, dir, storm::utility::one(), -middle); + SolutionType const middleValue = wrh.computeWeightedDiff(env, goal.direction(), storm::utility::one(), -middle, middleSchedulerRef); + checkedMiddleValues.insert(middle); // update the bounds and new middle value according to the bisection method - if (boundOption == BisectionMethodBounds::Simple) { + if (!useAdvancedBounds) { if (middleValue >= storm::utility::zero()) { - lowerBound &= middle; + if (lowerBound &= middle) { + lowerScheduler.swap(middleScheduler); + } } if (middleValue <= storm::utility::zero()) { - upperBound &= middle; + if (upperBound &= middle) { + upperScheduler.swap(middleScheduler); + } } middle = (*lowerBound + *upperBound) / 2; // update middle to the average of the bounds } else { - STORM_LOG_ASSERT(boundOption == BisectionMethodBounds::Advanced, "Unknown bisection method bounds"); if (middleValue >= storm::utility::zero()) { - lowerBound &= middle + (middleValue / pMax); + if (lowerBound &= middle + (middleValue / pMax)) { + lowerScheduler.swap(middleScheduler); + } upperBound &= middle + (middleValue / pMin); } if (middleValue <= storm::utility::zero()) { lowerBound &= middle + (middleValue / pMin); - upperBound &= middle + (middleValue / pMax); + if (upperBound &= middle + (middleValue / pMax)) { + upperScheduler.swap(middleScheduler); + } } - // update middle to the average of the bounds, but scale it according to the middle value (which is in [-1,1]) + // update middle to the average of the bounds, but use the middleValue as a hint: + // If middleValue is close to -1, we use a value close to lowerBound + // If middleValue is close to 0, we use a value close to the avg(lowerBound, upperBound) + // If middleValue is close to +1, we use a value close to upperBound middle = *lowerBound + (storm::utility::one() + middleValue) * (*upperBound - *lowerBound) / 2; if (!storm::NumberTraits::IsExact && storm::utility::isAlmostZero(*upperBound - *lowerBound)) { @@ -598,52 +1050,158 @@ SolutionType computeViaBisection(Environment const& env, BisectionMethodBounds b } // check for convergence SolutionType const boundDiff = *upperBound - *lowerBound; - STORM_LOG_TRACE("Iteration #" << iterationCount << ":\n\t Lower bound: " << storm::utility::convertNumber(*lowerBound) - << ",\n\t Upper bound: " << storm::utility::convertNumber(*upperBound) - << ",\n\t Difference: " << storm::utility::convertNumber(boundDiff) - << ",\n\t Middle val: " << storm::utility::convertNumber(middleValue) << ",\n\t Difference bound: " - << storm::utility::convertNumber((relative ? (precision * *lowerBound) : precision)) << "."); + STORM_LOG_TRACE("Iteration #" << iterationCount << ":\n\t Lower bound: " << *lowerBound << ",\n\t Upper bound: " << *upperBound + << ",\n\t Difference: " << boundDiff << ",\n\t Middle val: " << middleValue + << ",\n\t Difference bound: " << (relative ? (precision * *lowerBound) : precision) << "."); + if (goal.isBounded()) { + STORM_LOG_TRACE("Using threshold " << storm::utility::convertNumber(goal.thresholdValue()) << " with comparison " + << (goal.boundIsALowerBound() ? (goal.boundIsStrict() ? ">" : ">=") : (goal.boundIsStrict() ? "<" : "<=")) + << "."); + } if (boundDiff <= (relative ? (precision * *lowerBound) : precision)) { - STORM_LOG_INFO("Bisection method converged after " << iterationCount << " iterations. Difference is " - << std::setprecision(std::numeric_limits::digits10) - << storm::utility::convertNumber(boundDiff) << "."); + STORM_PRINT_AND_LOG("Bisection method converged after " << iterationCount << " iterations. Difference is " + << std::setprecision(std::numeric_limits::digits10) + << storm::utility::convertNumber(boundDiff) << "."); + break; + } else if (usePolicyTracking && lowerScheduler && upperScheduler && (*lowerScheduler == *upperScheduler)) { + STORM_PRINT_AND_LOG("Bisection method converged after " << iterationCount << " iterations due to identical schedulers for lower and upper bound."); + auto result = wrh.evaluateScheduler(env, *lowerScheduler); + lowerBound &= result; + upperBound &= result; + terminatedThroughPolicyTracking = true; + break; + } + // Check if bounds are fully below or above threshold + if (goal.isBounded() && (*upperBound <= goal.thresholdValue() || (*lowerBound >= goal.thresholdValue()))) { + STORM_PRINT_AND_LOG("Bisection method determined result after " << iterationCount << " iterations. Found bounds are [" + << storm::utility::convertNumber(*lowerBound) << ", " + << storm::utility::convertNumber(*upperBound) << "], threshold is " + << storm::utility::convertNumber(goal.thresholdValue()) << "."); break; } // check for early termination if (storm::utility::resources::isTerminate()) { - STORM_LOG_WARN("Bisection solver aborted after " << iterationCount << "iterations. Bound difference is " - << storm::utility::convertNumber(boundDiff) << "."); + STORM_PRINT_AND_LOG("Bisection solver aborted after " << iterationCount << "iterations. Bound difference is " + << storm::utility::convertNumber(boundDiff) << "."); break; } // process the middle value for the next iteration + // This sets the middle value to a rational number with the smallest enumerator/denominator that is still within the bounds + // With close bounds this can lead to the middle being set to exactly the lower or upper bound, thus allowing for an exact answer. if constexpr (storm::NumberTraits::IsExact) { - // find a rational number with a concise representation close to middle and within the bounds - auto const exactMiddle = middle; - - // Find number of digits - 1. Method using log10 does not work since that uses doubles internally. - auto numDigits = storm::utility::numDigits(*upperBound - *lowerBound) - 1; - - do { - ++numDigits; - middle = storm::utility::kwek_mehlhorn::sharpen(numDigits, exactMiddle); - } while (middle <= *lowerBound || middle >= *upperBound); + // Check if the rationalCandidate has been within the bounds for four iterations. + // If yes, we take that as our next "middle". + // Otherwise, we set a new rationalCandidate. + // This heuristic ensures that we eventually check every rational number without affecting the binary search too much + if (rationalCandidateCount >= 4 && rationalCandiate >= *lowerBound && rationalCandiate <= *upperBound && + !checkedMiddleValues.contains(rationalCandiate)) { + middle = rationalCandiate; + rationalCandidateCount = 0; + } else { + // find a rational number with a concise representation within our current bounds + bool const includeLower = !checkedMiddleValues.contains(*lowerBound); + bool const includeUpper = !checkedMiddleValues.contains(*upperBound); + auto newRationalCandiate = storm::utility::findRational(*lowerBound, includeLower, *upperBound, includeUpper); + if (rationalCandiate == newRationalCandiate) { + ++rationalCandidateCount; + } else { + rationalCandiate = newRationalCandiate; + rationalCandidateCount = 0; + } + // Also simplify the middle value + SolutionType delta = + std::min(*upperBound - middle, middle - *lowerBound) / storm::utility::convertNumber(16); + middle = storm::utility::findRational(middle - delta, true, middle + delta, true); + } } - // Since above code never sets 'middle' to exactly zero or one, we check if that could be necessary after a couple of iterations + // Since above code might never set 'middle' to exactly zero or one, we check if that could be necessary after a couple of iterations if (iterationCount == 8) { // 8 is just a heuristic value, it could be any number - if (storm::utility::isZero(*lowerBound)) { + if (storm::utility::isZero(*lowerBound) && !checkedMiddleValues.contains(storm::utility::zero())) { middle = storm::utility::zero(); - } else if (storm::utility::isOne(*upperBound)) { + } else if (storm::utility::isOne(*upperBound) && !checkedMiddleValues.contains(storm::utility::one())) { middle = storm::utility::one(); } } } - return (*lowerBound + *upperBound) / 2; + + // Create result without scheduler + auto finalResult = ResultReturnType((*lowerBound + *upperBound) / 2); + + if (!computeScheduler) { + return finalResult; // nothing else to do + } + // If requested, construct the scheduler for the original model + std::vector reducedSchedulerChoices; + if (terminatedThroughPolicyTracking) { + // We already have computed a scheduler + reducedSchedulerChoices = std::move(*lowerScheduler); + } else { + // Compute a scheduler on the middle result by performing one more iteration + wrh.computeWeightedDiff(env, goal.direction(), storm::utility::one(), -finalResult.initialStateValue, reducedSchedulerChoices); + } + finalResult.scheduler = wrh.constructSchedulerForInputModel(reducedSchedulerChoices, transitionMatrix, backwardTransitions, normalForm); + return finalResult; +} + +template +typename internal::ResultReturnType computeViaBisection(Environment const& env, ConditionalAlgorithmSetting const alg, uint64_t const initialState, + storm::solver::SolveGoal const& goal, bool computeScheduler, + storm::storage::SparseMatrix const& transitionMatrix, + storm::storage::SparseMatrix const& backwardTransitions, + NormalFormData const& normalForm) { + using enum ConditionalAlgorithmSetting; + STORM_LOG_ASSERT(alg == Bisection || alg == BisectionAdvanced || alg == BisectionPolicyTracking || alg == BisectionAdvancedPolicyTracking, + "Unhandled Bisection algorithm " << alg << "."); + bool const useAdvancedBounds = (alg == BisectionAdvanced || alg == BisectionAdvancedPolicyTracking); + bool const usePolicyTracking = (alg == BisectionPolicyTracking || alg == BisectionAdvancedPolicyTracking); + return computeViaBisection(env, useAdvancedBounds, usePolicyTracking, initialState, goal, computeScheduler, transitionMatrix, backwardTransitions, + normalForm); } template -SolutionType computeViaPolicyIteration(Environment const& env, uint64_t const initialState, storm::solver::OptimizationDirection const dir, - storm::storage::SparseMatrix const& transitionMatrix, NormalFormData const& normalForm) { - WeightedReachabilityHelper wrh(initialState, transitionMatrix, normalForm); +typename internal::ResultReturnType decideThreshold(Environment const& env, uint64_t const initialState, + storm::OptimizationDirection const& direction, SolutionType const& threshold, + bool computeScheduler, storm::storage::SparseMatrix const& transitionMatrix, + storm::storage::SparseMatrix const& backwardTransitions, + NormalFormData const& normalForm) { + // We currently handle sound model checking incorrectly: we would need the actual lower/upper bounds of the weightedReachabilityHelper + + WeightedReachabilityHelper wrh(initialState, transitionMatrix, normalForm, computeScheduler); + + std::optional> scheduler; + storm::OptionalRef> schedulerRef; + if (computeScheduler) { + scheduler.emplace(); + schedulerRef.reset(*scheduler); + } + + SolutionType val = wrh.computeWeightedDiff(env, direction, storm::utility::one(), -threshold, schedulerRef); + SolutionType outputProbability; + if (val > storm::utility::zero()) { + // if val is positive, the conditional probability is (strictly) greater than threshold + outputProbability = storm::utility::one(); + } else if (val < storm::utility::zero()) { + // if val is negative, the conditional probability is (strictly) smaller than threshold + outputProbability = storm::utility::zero(); + } else { + // if val is zero, the conditional probability equals the threshold + outputProbability = threshold; + } + auto finalResult = ResultReturnType(outputProbability); + + if (computeScheduler) { + // If requested, construct the scheduler for the original model + finalResult.scheduler = wrh.constructSchedulerForInputModel(scheduler.value(), transitionMatrix, backwardTransitions, normalForm); + } + return finalResult; +} + +template +internal::ResultReturnType computeViaPolicyIteration(Environment const& env, uint64_t const initialState, + storm::solver::OptimizationDirection const dir, + storm::storage::SparseMatrix const& transitionMatrix, + NormalFormData const& normalForm) { + WeightedReachabilityHelper wrh(initialState, transitionMatrix, normalForm, false); // scheduler computation not yet implemented. std::vector scheduler; std::vector targetResults, conditionResults; @@ -701,20 +1259,23 @@ std::optional handleTrivialCases(uint64_t const initialState, Norm template std::unique_ptr computeConditionalProbabilities(Environment const& env, storm::solver::SolveGoal&& goal, + storm::modelchecker::CheckTask const& checkTask, storm::storage::SparseMatrix const& transitionMatrix, storm::storage::SparseMatrix const& backwardTransitions, storm::storage::BitVector const& targetStates, storm::storage::BitVector const& conditionStates) { // We might require adapting the precision of the solver to counter error propagation (e.g. when computing the normal form). auto normalFormConstructionEnv = env; auto analysisEnv = env; - if (env.solver().isForceSoundness()) { - // We intuitively have to divide the precision into two parts, one for computations when constructing the normal form and one for the actual analysis. - // As the former is usually less numerically challenging, we use a factor of 1/10 for the normal form construction and 9/10 for the analysis. - auto const normalFormPrecisionFactor = storm::utility::convertNumber("1/10"); - normalFormConstructionEnv.solver().minMax().setPrecision(env.solver().minMax().getPrecision() * normalFormPrecisionFactor); - analysisEnv.solver().minMax().setPrecision(env.solver().minMax().getPrecision() * - (storm::utility::one() - normalFormPrecisionFactor)); - } + + // if (env.solver().isForceSoundness()) { + // // We intuitively have to divide the precision into two parts, one for computations when constructing the normal form and one for the actual + // analysis. + // // As the former is usually less numerically challenging, we use a factor of 1/10 for the normal form construction and 9/10 for the analysis. + // auto const normalFormPrecisionFactor = storm::utility::convertNumber("1/10"); + // normalFormConstructionEnv.solver().minMax().setPrecision(env.solver().minMax().getPrecision() * normalFormPrecisionFactor); + // analysisEnv.solver().minMax().setPrecision(env.solver().minMax().getPrecision() * + // (storm::utility::one() - normalFormPrecisionFactor)); + // } // We first translate the problem into a normal form. // @see doi.org/10.1007/978-3-642-54862-8_43 @@ -725,15 +1286,17 @@ std::unique_ptr computeConditionalProbabilities(Environment const& STORM_LOG_TRACE("Computing conditional probabilities for a model with " << transitionMatrix.getRowGroupCount() << " states and " << transitionMatrix.getEntryCount() << " transitions."); // storm::utility::Stopwatch sw(true); - auto normalFormData = internal::obtainNormalForm(normalFormConstructionEnv, goal.direction(), transitionMatrix, backwardTransitions, goal.relevantValues(), - targetStates, conditionStates); + auto normalFormData = internal::obtainNormalForm(normalFormConstructionEnv, goal.direction(), checkTask.isProduceSchedulersSet(), transitionMatrix, + backwardTransitions, goal.relevantValues(), targetStates, conditionStates); // sw.stop(); // STORM_PRINT_AND_LOG("Time for obtaining the normal form: " << sw << ".\n"); // Then, we solve the induced problem using the selected algorithm auto const initialState = *goal.relevantValues().begin(); ValueType initialStateValue = -storm::utility::one(); + std::unique_ptr> scheduler = nullptr; if (auto trivialValue = internal::handleTrivialCases(initialState, normalFormData); trivialValue.has_value()) { initialStateValue = *trivialValue; + scheduler = std::unique_ptr>(new storm::storage::Scheduler(transitionMatrix.getRowGroupCount())); STORM_LOG_DEBUG("Initial state has trivial value " << initialStateValue); } else { STORM_LOG_ASSERT(normalFormData.maybeStates.get(initialState), "Initial state must be a maybe state if it is not a terminal state"); @@ -741,42 +1304,143 @@ std::unique_ptr computeConditionalProbabilities(Environment const& if (alg == ConditionalAlgorithmSetting::Default) { alg = ConditionalAlgorithmSetting::Restart; } - STORM_LOG_INFO("Analyzing normal form with " << normalFormData.maybeStates.getNumberOfSetBits() << " maybe states using algorithm '" << alg << "."); + STORM_PRINT_AND_LOG("Analyzing normal form with " << normalFormData.maybeStates.getNumberOfSetBits() << " maybe states using algorithm '" << alg + << "."); // sw.restart(); + internal::ResultReturnType result{storm::utility::zero()}; switch (alg) { - case ConditionalAlgorithmSetting::Restart: - initialStateValue = internal::computeViaRestartMethod(analysisEnv, initialState, goal.direction(), transitionMatrix, normalFormData); + case ConditionalAlgorithmSetting::Restart: { + result = internal::computeViaRestartMethod(analysisEnv, initialState, goal, checkTask.isProduceSchedulersSet(), transitionMatrix, + backwardTransitions, normalFormData); break; + } case ConditionalAlgorithmSetting::Bisection: - initialStateValue = internal::computeViaBisection(analysisEnv, internal::BisectionMethodBounds::Simple, initialState, goal.direction(), - transitionMatrix, normalFormData); - break; case ConditionalAlgorithmSetting::BisectionAdvanced: - initialStateValue = internal::computeViaBisection(analysisEnv, internal::BisectionMethodBounds::Advanced, initialState, goal.direction(), - transitionMatrix, normalFormData); + case ConditionalAlgorithmSetting::BisectionPolicyTracking: + case ConditionalAlgorithmSetting::BisectionAdvancedPolicyTracking: { + if (goal.isBounded() && env.modelchecker().isAllowOptimizationForBoundedPropertiesSet()) { + result = internal::decideThreshold(analysisEnv, initialState, goal.direction(), goal.thresholdValue(), checkTask.isProduceSchedulersSet(), + transitionMatrix, backwardTransitions, normalFormData); + } else { + result = internal::computeViaBisection(analysisEnv, alg, initialState, goal, checkTask.isProduceSchedulersSet(), transitionMatrix, + backwardTransitions, normalFormData); + } break; - case ConditionalAlgorithmSetting::PolicyIteration: - initialStateValue = internal::computeViaPolicyIteration(analysisEnv, initialState, goal.direction(), transitionMatrix, normalFormData); + } + case ConditionalAlgorithmSetting::PolicyIteration: { + result = internal::computeViaPolicyIteration(analysisEnv, initialState, goal.direction(), transitionMatrix, normalFormData); break; - default: + } + default: { STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Unknown conditional probability algorithm: " << alg); + } } + initialStateValue = result.initialStateValue; + scheduler = std::move(result.scheduler); + // sw.stop(); // STORM_PRINT_AND_LOG("Time for analyzing the normal form: " << sw << ".\n"); } - return std::unique_ptr(new ExplicitQuantitativeCheckResult(initialState, initialStateValue)); + std::unique_ptr result(new ExplicitQuantitativeCheckResult(initialState, initialStateValue)); + + // if produce schedulers was set, we have to construct a scheduler with memory + if (checkTask.isProduceSchedulersSet() && scheduler) { + // not sure about this + storm::utility::graph::computeSchedulerProb1E(normalFormData.targetStates, transitionMatrix, backwardTransitions, normalFormData.targetStates, + targetStates, *scheduler); + storm::utility::graph::computeSchedulerProb1E(normalFormData.conditionStates, transitionMatrix, backwardTransitions, normalFormData.conditionStates, + conditionStates, *scheduler); + // fill in the scheduler with default choices for states that are missing a choice, these states should be just the ones from which the condition is + // unreachable this is also used to fill choices for the trivial cases + for (uint64_t state = 0; state < transitionMatrix.getRowGroupCount(); ++state) { + if (!scheduler->isChoiceSelected(state)) { + // select an arbitrary choice + scheduler->setChoice(0, state); + } + } + + // create scheduler with memory structure + storm::storage::MemoryStructure::TransitionMatrix memoryTransitions(3, std::vector>(3, boost::none)); + storm::models::sparse::StateLabeling memoryStateLabeling(3); + memoryStateLabeling.addLabel("init_memory"); + memoryStateLabeling.addLabel("condition_reached"); + memoryStateLabeling.addLabel("target_reached"); + memoryStateLabeling.addLabelToState("init_memory", 0); + memoryStateLabeling.addLabelToState("condition_reached", 1); + memoryStateLabeling.addLabelToState("target_reached", 2); + + storm::storage::BitVector allTransitions(transitionMatrix.getEntryCount(), true); + storm::storage::BitVector conditionExitTransitions(transitionMatrix.getEntryCount(), false); + storm::storage::BitVector targetExitTransitions(transitionMatrix.getEntryCount(), false); + + for (auto state : conditionStates) { + for (auto choice : transitionMatrix.getRowGroupIndices(state)) { + for (auto entryIt = transitionMatrix.getRow(choice).begin(); entryIt < transitionMatrix.getRow(choice).end(); ++entryIt) { + conditionExitTransitions.set(entryIt - transitionMatrix.begin(), true); + } + } + } + for (auto state : targetStates) { + for (auto choice : transitionMatrix.getRowGroupIndices(state)) { + for (auto entryIt = transitionMatrix.getRow(choice).begin(); entryIt < transitionMatrix.getRow(choice).end(); ++entryIt) { + targetExitTransitions.set(entryIt - transitionMatrix.begin(), true); + } + } + } + + memoryTransitions[0][0] = + allTransitions & ~conditionExitTransitions & ~targetExitTransitions; // if neither condition nor target reached, stay in init_memory + memoryTransitions[0][1] = conditionExitTransitions; + memoryTransitions[0][2] = targetExitTransitions & ~conditionExitTransitions; + memoryTransitions[1][1] = allTransitions; // once condition reached, stay in that memory state + memoryTransitions[2][2] = allTransitions; // once target reached, stay in that memory state + + // this assumes there is a single initial state + auto memoryStructure = storm::storage::MemoryStructure(memoryTransitions, memoryStateLabeling, std::vector(1, 0), true); + + auto finalScheduler = std::unique_ptr>( + new storm::storage::Scheduler(transitionMatrix.getRowGroupCount(), std::move(memoryStructure))); + + for (uint64_t state = 0; state < transitionMatrix.getRowGroupCount(); ++state) { + // set choices for memory 0 + if (conditionStates.get(state)) { + finalScheduler->setChoice(normalFormData.schedulerChoicesForReachingTargetStates->getChoice(state), state, 0); + } else if (targetStates.get(state)) { + finalScheduler->setChoice(normalFormData.schedulerChoicesForReachingConditionStates->getChoice(state), state, 0); + } else { + finalScheduler->setChoice(scheduler->getChoice(state), state, 0); + } + + // set choices for memory 1, these are the choices after condition was reached + if (normalFormData.schedulerChoicesForReachingTargetStates->isChoiceSelected(state)) { + finalScheduler->setChoice(normalFormData.schedulerChoicesForReachingTargetStates->getChoice(state), state, 1); + } else { + finalScheduler->setChoice(0, state, 1); // arbitrary choice if no choice was recorded, TODO: this could be problematic for paynt? + } + // set choices for memory 2, these are the choices after target was reached + if (normalFormData.schedulerChoicesForReachingConditionStates->isChoiceSelected(state)) { + finalScheduler->setChoice(normalFormData.schedulerChoicesForReachingConditionStates->getChoice(state), state, 2); + } else { + finalScheduler->setChoice(0, state, 2); // arbitrary choice if no choice was recorded, TODO: this could be problematic for paynt? + } + } + + result->asExplicitQuantitativeCheckResult().setScheduler(std::move(finalScheduler)); + } + return result; } template std::unique_ptr computeConditionalProbabilities(Environment const& env, storm::solver::SolveGoal&& goal, + storm::modelchecker::CheckTask const& checkTask, storm::storage::SparseMatrix const& transitionMatrix, storm::storage::SparseMatrix const& backwardTransitions, storm::storage::BitVector const& targetStates, storm::storage::BitVector const& conditionStates); -template std::unique_ptr computeConditionalProbabilities(Environment const& env, storm::solver::SolveGoal&& goal, - storm::storage::SparseMatrix const& transitionMatrix, - storm::storage::SparseMatrix const& backwardTransitions, - storm::storage::BitVector const& targetStates, - storm::storage::BitVector const& conditionStates); +template std::unique_ptr computeConditionalProbabilities( + Environment const& env, storm::solver::SolveGoal&& goal, + storm::modelchecker::CheckTask const& checkTask, + storm::storage::SparseMatrix const& transitionMatrix, storm::storage::SparseMatrix const& backwardTransitions, + storm::storage::BitVector const& targetStates, storm::storage::BitVector const& conditionStates); } // namespace storm::modelchecker diff --git a/src/storm/modelchecker/helper/conditional/ConditionalHelper.h b/src/storm/modelchecker/helper/conditional/ConditionalHelper.h index 12e8c0791e..5596ebf115 100644 --- a/src/storm/modelchecker/helper/conditional/ConditionalHelper.h +++ b/src/storm/modelchecker/helper/conditional/ConditionalHelper.h @@ -1,6 +1,8 @@ #pragma once #include +#include "storm/logic/ConditionalFormula.h" +#include "storm/modelchecker/CheckTask.h" #include "storm/solver/SolveGoal.h" namespace storm { @@ -22,6 +24,7 @@ class BackwardTransitionCache; template std::unique_ptr computeConditionalProbabilities(Environment const& env, storm::solver::SolveGoal&& goal, + storm::modelchecker::CheckTask const& checkTask, storm::storage::SparseMatrix const& transitionMatrix, storm::storage::SparseMatrix const& backwardTransitions, storm::storage::BitVector const& targetStates, storm::storage::BitVector const& conditionStates); diff --git a/src/storm/modelchecker/helper/finitehorizon/SparseNondeterministicStepBoundedHorizonHelper.cpp b/src/storm/modelchecker/helper/finitehorizon/SparseNondeterministicStepBoundedHorizonHelper.cpp index 02cb13fbca..9eedacca02 100644 --- a/src/storm/modelchecker/helper/finitehorizon/SparseNondeterministicStepBoundedHorizonHelper.cpp +++ b/src/storm/modelchecker/helper/finitehorizon/SparseNondeterministicStepBoundedHorizonHelper.cpp @@ -1,9 +1,14 @@ #include "storm/modelchecker/helper/finitehorizon/SparseNondeterministicStepBoundedHorizonHelper.h" +#include "storm/adapters/IntervalAdapter.h" +#include "storm/adapters/IntervalForward.h" +#include "storm/adapters/RationalNumberAdapter.h" +#include "storm/adapters/RationalNumberForward.h" #include "storm/modelchecker/hints/ExplicitModelCheckerHint.h" #include "storm/modelchecker/prctl/helper/SparseMdpEndComponentInformation.h" #include "storm/models/sparse/StandardRewardModel.h" +#include "storm/storage/BitVector.h" #include "storm/utility/graph.h" #include "storm/utility/macros.h" #include "storm/utility/vector.h" @@ -17,22 +22,20 @@ namespace storm { namespace modelchecker { namespace helper { -template -SparseNondeterministicStepBoundedHorizonHelper::SparseNondeterministicStepBoundedHorizonHelper( +template +SparseNondeterministicStepBoundedHorizonHelper::SparseNondeterministicStepBoundedHorizonHelper( /*storm::storage::SparseMatrix const& transitionMatrix, storm::storage::SparseMatrix const& backwardTransitions*/) // transitionMatrix(transitionMatrix), backwardTransitions(backwardTransitions) { // Intentionally left empty. } -template -std::vector SparseNondeterministicStepBoundedHorizonHelper::compute(Environment const& env, storm::solver::SolveGoal&& goal, - storm::storage::SparseMatrix const& transitionMatrix, - storm::storage::SparseMatrix const& backwardTransitions, - storm::storage::BitVector const& phiStates, - storm::storage::BitVector const& psiStates, uint64_t lowerBound, - uint64_t upperBound, ModelCheckerHint const& hint) { - std::vector result(transitionMatrix.getRowGroupCount(), storm::utility::zero()); +template +std::vector SparseNondeterministicStepBoundedHorizonHelper::compute( + Environment const& env, storm::solver::SolveGoal&& goal, storm::storage::SparseMatrix const& transitionMatrix, + storm::storage::SparseMatrix const& backwardTransitions, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates, + uint64_t lowerBound, uint64_t upperBound, ModelCheckerHint const& hint) { + std::vector result(transitionMatrix.getRowGroupCount(), storm::utility::zero()); storm::storage::BitVector makeZeroColumns; // Determine the states that have 0 probability of reaching the target states. @@ -56,20 +59,44 @@ std::vector SparseNondeterministicStepBoundedHorizonHelper STORM_LOG_INFO("Preprocessing: " << maybeStates.getNumberOfSetBits() << " non-target states with probability greater 0."); if (!maybeStates.empty()) { - // We can eliminate the rows and columns from the original transition probability matrix that have probability 0. - storm::storage::SparseMatrix submatrix = transitionMatrix.getSubmatrix(true, maybeStates, maybeStates, false, makeZeroColumns); - std::vector b = transitionMatrix.getConstrainedRowGroupSumVector(maybeStates, psiStates); + storm::storage::SparseMatrix submatrix; + std::vector b; + uint64_t subresultSize; + + if constexpr (storm::IsIntervalType) { + // For intervals, we cannot remove all non maybe states as that would lead to the upper probability of rows summing to below 1. + // Instead we only drop all outgoing transitions of non maybe states. + // See src/storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.cpp:624 for more details. + submatrix = transitionMatrix.filterEntries(transitionMatrix.getRowFilter(maybeStates)); + + storm::utility::vector::setAllValues(b, transitionMatrix.getRowFilter(psiStates)); + + subresultSize = transitionMatrix.getRowCount(); + } else { + // We can eliminate the rows and columns from the original transition probability matrix that have probability 0. + submatrix = transitionMatrix.getSubmatrix(true, maybeStates, maybeStates, false, makeZeroColumns); + + b = transitionMatrix.getConstrainedRowGroupSumVector(maybeStates, psiStates); + subresultSize = maybeStates.getNumberOfSetBits(); + } // Create the vector with which to multiply. - std::vector subresult(maybeStates.getNumberOfSetBits()); + std::vector subresult(subresultSize); - auto multiplier = storm::solver::MultiplierFactory().create(env, submatrix); + auto multiplier = storm::solver::MultiplierFactory().create(env, submatrix); if (lowerBound == 0) { multiplier->repeatedMultiplyAndReduce(env, goal.direction(), subresult, &b, upperBound); } else { multiplier->repeatedMultiplyAndReduce(env, goal.direction(), subresult, &b, upperBound - lowerBound + 1); - storm::storage::SparseMatrix submatrix = transitionMatrix.getSubmatrix(true, maybeStates, maybeStates, false); - auto multiplier = storm::solver::MultiplierFactory().create(env, submatrix); + + storm::storage::SparseMatrix submatrix; + if constexpr (storm::IsIntervalType) { + submatrix = transitionMatrix.filterEntries(transitionMatrix.getRowFilter(maybeStates)); + } else { + submatrix = transitionMatrix.getSubmatrix(true, maybeStates, maybeStates, false); + } + + auto multiplier = storm::solver::MultiplierFactory().create(env, submatrix); b = std::vector(b.size(), storm::utility::zero()); multiplier->repeatedMultiplyAndReduce(env, goal.direction(), subresult, &b, lowerBound - 1); } @@ -77,13 +104,15 @@ std::vector SparseNondeterministicStepBoundedHorizonHelper storm::utility::vector::setVectorValues(result, maybeStates, subresult); } if (lowerBound == 0) { - storm::utility::vector::setVectorValues(result, psiStates, storm::utility::one()); + storm::utility::vector::setVectorValues(result, psiStates, storm::utility::one()); } return result; } template class SparseNondeterministicStepBoundedHorizonHelper; template class SparseNondeterministicStepBoundedHorizonHelper; +template class SparseNondeterministicStepBoundedHorizonHelper; +template class SparseNondeterministicStepBoundedHorizonHelper; } // namespace helper } // namespace modelchecker } // namespace storm \ No newline at end of file diff --git a/src/storm/modelchecker/helper/finitehorizon/SparseNondeterministicStepBoundedHorizonHelper.h b/src/storm/modelchecker/helper/finitehorizon/SparseNondeterministicStepBoundedHorizonHelper.h index fae2a12eae..304cee3ed9 100644 --- a/src/storm/modelchecker/helper/finitehorizon/SparseNondeterministicStepBoundedHorizonHelper.h +++ b/src/storm/modelchecker/helper/finitehorizon/SparseNondeterministicStepBoundedHorizonHelper.h @@ -10,15 +10,15 @@ namespace storm { namespace modelchecker { namespace helper { -template +template class SparseNondeterministicStepBoundedHorizonHelper { public: SparseNondeterministicStepBoundedHorizonHelper(); - std::vector compute(Environment const& env, storm::solver::SolveGoal&& goal, - storm::storage::SparseMatrix const& transitionMatrix, - storm::storage::SparseMatrix const& backwardTransitions, storm::storage::BitVector const& phiStates, - storm::storage::BitVector const& psiStates, uint64_t lowerBound, uint64_t upperBound, - ModelCheckerHint const& hint = ModelCheckerHint()); + std::vector compute(Environment const& env, storm::solver::SolveGoal&& goal, + storm::storage::SparseMatrix const& transitionMatrix, + storm::storage::SparseMatrix const& backwardTransitions, storm::storage::BitVector const& phiStates, + storm::storage::BitVector const& psiStates, uint64_t lowerBound, uint64_t upperBound, + ModelCheckerHint const& hint = ModelCheckerHint()); private: }; diff --git a/src/storm/modelchecker/hints/ExplicitModelCheckerHint.cpp b/src/storm/modelchecker/hints/ExplicitModelCheckerHint.cpp index e35a064071..915155cf78 100644 --- a/src/storm/modelchecker/hints/ExplicitModelCheckerHint.cpp +++ b/src/storm/modelchecker/hints/ExplicitModelCheckerHint.cpp @@ -122,6 +122,7 @@ template class ExplicitModelCheckerHint; template class ExplicitModelCheckerHint; template class ExplicitModelCheckerHint; template class ExplicitModelCheckerHint; +template class ExplicitModelCheckerHint; } // namespace modelchecker } // namespace storm diff --git a/src/storm/modelchecker/hints/ModelCheckerHint.cpp b/src/storm/modelchecker/hints/ModelCheckerHint.cpp index af5991fb16..073e6b884f 100644 --- a/src/storm/modelchecker/hints/ModelCheckerHint.cpp +++ b/src/storm/modelchecker/hints/ModelCheckerHint.cpp @@ -34,6 +34,8 @@ template ExplicitModelCheckerHint const& ModelCheckerHi template ExplicitModelCheckerHint& ModelCheckerHint::asExplicitModelCheckerHint(); template ExplicitModelCheckerHint const& ModelCheckerHint::asExplicitModelCheckerHint() const; template ExplicitModelCheckerHint& ModelCheckerHint::asExplicitModelCheckerHint(); +template ExplicitModelCheckerHint const& ModelCheckerHint::asExplicitModelCheckerHint() const; +template ExplicitModelCheckerHint& ModelCheckerHint::asExplicitModelCheckerHint(); } // namespace modelchecker } // namespace storm diff --git a/src/storm/modelchecker/multiobjective/constraintbased/SparseCbAchievabilityQuery.cpp b/src/storm/modelchecker/multiobjective/constraintbased/SparseCbAchievabilityQuery.cpp index a2365add7e..19b5482681 100644 --- a/src/storm/modelchecker/multiobjective/constraintbased/SparseCbAchievabilityQuery.cpp +++ b/src/storm/modelchecker/multiobjective/constraintbased/SparseCbAchievabilityQuery.cpp @@ -33,7 +33,7 @@ template std::unique_ptr SparseCbAchievabilityQuery::check(Environment const& env) { bool result = this->checkAchievability(); - return std::unique_ptr(new ExplicitQualitativeCheckResult(this->originalModel.getInitialStates().getNextSetIndex(0), result)); + return std::unique_ptr(new ExplicitQualitativeCheckResult(this->originalModel.getInitialStates().getNextSetIndex(0), result)); } template diff --git a/src/storm/modelchecker/multiobjective/deterministicScheds/DeterministicSchedsAchievabilityChecker.cpp b/src/storm/modelchecker/multiobjective/deterministicScheds/DeterministicSchedsAchievabilityChecker.cpp index ed9c198728..4c974ad591 100644 --- a/src/storm/modelchecker/multiobjective/deterministicScheds/DeterministicSchedsAchievabilityChecker.cpp +++ b/src/storm/modelchecker/multiobjective/deterministicScheds/DeterministicSchedsAchievabilityChecker.cpp @@ -62,13 +62,13 @@ std::unique_ptr DeterministicSchedsAchievabilityCheckercheck(env, thresholdPolytope, eps); bool const isAchievable = achievingPoint.has_value(); + using ValueType = typename SparseModelType::ValueType; if (isAchievable) { STORM_LOG_INFO( "Found achievable point: " << storm::utility::vector::toString(achievingPoint->first) << " ( approx. " << storm::utility::vector::toString(storm::utility::vector::convertNumericVector(achievingPoint->first)) << " )."); if (optimizingObjectiveIndex.has_value()) { - using ValueType = typename SparseModelType::ValueType; // Average between obtained lower- and upper bounds auto result = storm::utility::convertNumber(achievingPoint->first[*optimizingObjectiveIndex] + achievingPoint->second); @@ -79,7 +79,7 @@ std::unique_ptr DeterministicSchedsAchievabilityChecker>(originalModelInitialState, result); } } - return std::make_unique(originalModelInitialState, isAchievable); + return std::make_unique>(originalModelInitialState, isAchievable); } template class DeterministicSchedsAchievabilityChecker, storm::RationalNumber>; diff --git a/src/storm/modelchecker/multiobjective/deterministicScheds/DeterministicSchedsObjectiveHelper.cpp b/src/storm/modelchecker/multiobjective/deterministicScheds/DeterministicSchedsObjectiveHelper.cpp index eca4efc7fc..d7ce5a4dc1 100644 --- a/src/storm/modelchecker/multiobjective/deterministicScheds/DeterministicSchedsObjectiveHelper.cpp +++ b/src/storm/modelchecker/multiobjective/deterministicScheds/DeterministicSchedsObjectiveHelper.cpp @@ -39,7 +39,7 @@ storm::storage::BitVector evaluatePropositionalFormula(ModelType const& model, s auto checkResult = mc.check(formula); STORM_LOG_THROW(checkResult && checkResult->isExplicitQualitativeCheckResult(), storm::exceptions::UnexpectedException, "Unexpected type of check result for subformula " << formula << "."); - return checkResult->asExplicitQualitativeCheckResult().getTruthValuesVector(); + return checkResult->template asExplicitQualitativeCheckResult().getTruthValuesVector(); } template diff --git a/src/storm/modelchecker/multiobjective/pcaa/SparsePcaaQuery.cpp b/src/storm/modelchecker/multiobjective/pcaa/SparsePcaaQuery.cpp index 3164eeee04..7324d97da1 100644 --- a/src/storm/modelchecker/multiobjective/pcaa/SparsePcaaQuery.cpp +++ b/src/storm/modelchecker/multiobjective/pcaa/SparsePcaaQuery.cpp @@ -227,7 +227,7 @@ SparsePcaaQuery::tryAnswerOrNextWeightsAchie if (!optObjIndex.has_value()) { if (!overApproximation->contains(thresholds)) { // The thresholds are not achievable - return std::unique_ptr(new ExplicitQualitativeCheckResult(initialStateOfOriginalModel, false)); + return std::unique_ptr(new ExplicitQualitativeCheckResult(initialStateOfOriginalModel, false)); } referencePoint = thresholds; } else { @@ -247,7 +247,7 @@ SparsePcaaQuery::tryAnswerOrNextWeightsAchie auto optRes = overApproximation->intersection(thresholdPolytope)->optimize(optDirVector); if (!optRes.second) { // The thresholds are not achievable - return std::unique_ptr(new ExplicitQualitativeCheckResult(initialStateOfOriginalModel, false)); + return std::unique_ptr(new ExplicitQualitativeCheckResult(initialStateOfOriginalModel, false)); } referencePoint = thresholds; referencePoint[optObjIndex.value()] = @@ -275,7 +275,7 @@ SparsePcaaQuery::tryAnswerOrNextWeightsAchie return std::unique_ptr(new ExplicitQuantitativeCheckResult(initialStateOfOriginalModel, resultForOriginalModel)); } else { - return std::unique_ptr(new ExplicitQualitativeCheckResult(initialStateOfOriginalModel, true)); + return std::unique_ptr(new ExplicitQualitativeCheckResult(initialStateOfOriginalModel, true)); } } diff --git a/src/storm/modelchecker/multiobjective/preprocessing/SparseMultiObjectivePreprocessor.cpp b/src/storm/modelchecker/multiobjective/preprocessing/SparseMultiObjectivePreprocessor.cpp index 6eabbd6bda..91dc7be1b2 100644 --- a/src/storm/modelchecker/multiobjective/preprocessing/SparseMultiObjectivePreprocessor.cpp +++ b/src/storm/modelchecker/multiobjective/preprocessing/SparseMultiObjectivePreprocessor.cpp @@ -128,8 +128,10 @@ void SparseMultiObjectivePreprocessor::removeIrrelevantStates(s auto const& pathFormula = opFormula->asOperatorFormula().getSubformula(); if (opFormula->isProbabilityOperatorFormula()) { if (pathFormula.isUntilFormula()) { - auto lhs = mc.check(pathFormula.asUntilFormula().getLeftSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector(); - auto rhs = mc.check(pathFormula.asUntilFormula().getRightSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector(); + auto lhs = + mc.check(pathFormula.asUntilFormula().getLeftSubformula())->template asExplicitQualitativeCheckResult().getTruthValuesVector(); + auto rhs = + mc.check(pathFormula.asUntilFormula().getRightSubformula())->template asExplicitQualitativeCheckResult().getTruthValuesVector(); absorbingStatesForSubformula = storm::utility::graph::performProb0A(backwardTransitions, lhs, rhs); absorbingStatesForSubformula |= getOnlyReachableViaPhi(*model, ~lhs | rhs); } else if (pathFormula.isBoundedUntilFormula()) { @@ -138,10 +140,12 @@ void SparseMultiObjectivePreprocessor::removeIrrelevantStates(s storm::storage::BitVector absorbingStatesForSubSubformula; for (uint64_t i = 0; i < pathFormula.asBoundedUntilFormula().getDimension(); ++i) { auto subPathFormula = pathFormula.asBoundedUntilFormula().restrictToDimension(i); - auto lhs = - mc.check(pathFormula.asBoundedUntilFormula().getLeftSubformula(i))->asExplicitQualitativeCheckResult().getTruthValuesVector(); - auto rhs = - mc.check(pathFormula.asBoundedUntilFormula().getRightSubformula(i))->asExplicitQualitativeCheckResult().getTruthValuesVector(); + auto lhs = mc.check(pathFormula.asBoundedUntilFormula().getLeftSubformula(i)) + ->template asExplicitQualitativeCheckResult() + .getTruthValuesVector(); + auto rhs = mc.check(pathFormula.asBoundedUntilFormula().getRightSubformula(i)) + ->template asExplicitQualitativeCheckResult() + .getTruthValuesVector(); absorbingStatesForSubSubformula = storm::utility::graph::performProb0A(backwardTransitions, lhs, rhs); if (pathFormula.asBoundedUntilFormula().hasLowerBound(i)) { absorbingStatesForSubSubformula |= getOnlyReachableViaPhi(*model, ~lhs); @@ -151,8 +155,12 @@ void SparseMultiObjectivePreprocessor::removeIrrelevantStates(s absorbingStatesForSubformula &= absorbingStatesForSubSubformula; } } else { - auto lhs = mc.check(pathFormula.asBoundedUntilFormula().getLeftSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector(); - auto rhs = mc.check(pathFormula.asBoundedUntilFormula().getRightSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector(); + auto lhs = mc.check(pathFormula.asBoundedUntilFormula().getLeftSubformula()) + ->template asExplicitQualitativeCheckResult() + .getTruthValuesVector(); + auto rhs = mc.check(pathFormula.asBoundedUntilFormula().getRightSubformula()) + ->template asExplicitQualitativeCheckResult() + .getTruthValuesVector(); absorbingStatesForSubformula = storm::utility::graph::performProb0A(backwardTransitions, lhs, rhs); if (pathFormula.asBoundedUntilFormula().hasLowerBound()) { absorbingStatesForSubformula |= getOnlyReachableViaPhi(*model, ~lhs); @@ -161,12 +169,14 @@ void SparseMultiObjectivePreprocessor::removeIrrelevantStates(s } } } else if (pathFormula.isGloballyFormula()) { - auto phi = mc.check(pathFormula.asGloballyFormula().getSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector(); + auto phi = + mc.check(pathFormula.asGloballyFormula().getSubformula())->template asExplicitQualitativeCheckResult().getTruthValuesVector(); auto notPhi = ~phi; absorbingStatesForSubformula = storm::utility::graph::performProb0A(backwardTransitions, phi, notPhi); absorbingStatesForSubformula |= getOnlyReachableViaPhi(*model, notPhi); } else if (pathFormula.isEventuallyFormula()) { - auto phi = mc.check(pathFormula.asEventuallyFormula().getSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector(); + auto phi = + mc.check(pathFormula.asEventuallyFormula().getSubformula())->template asExplicitQualitativeCheckResult().getTruthValuesVector(); absorbingStatesForSubformula = storm::utility::graph::performProb0A(backwardTransitions, ~phi, phi); absorbingStatesForSubformula |= getOnlyReachableViaPhi(*model, phi); } else { @@ -181,7 +191,8 @@ void SparseMultiObjectivePreprocessor::removeIrrelevantStates(s storm::storage::BitVector statesWithoutReward = rewardModel.get().getStatesWithZeroReward(model->getTransitionMatrix()); // Make states that can not reach a state with non-zero reward absorbing absorbingStatesForSubformula = storm::utility::graph::performProb0A(backwardTransitions, statesWithoutReward, ~statesWithoutReward); - auto phi = mc.check(pathFormula.asEventuallyFormula().getSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector(); + auto phi = + mc.check(pathFormula.asEventuallyFormula().getSubformula())->template asExplicitQualitativeCheckResult().getTruthValuesVector(); // Make states that reach phi with prob 1 while only visiting states with reward 0 absorbing absorbingStatesForSubformula |= storm::utility::graph::performProb1A( model->getTransitionMatrix(), model->getTransitionMatrix().getRowGroupIndices(), backwardTransitions, statesWithoutReward, phi); @@ -210,13 +221,14 @@ void SparseMultiObjectivePreprocessor::removeIrrelevantStates(s } } else if (opFormula->isTimeOperatorFormula()) { if (pathFormula.isEventuallyFormula()) { - auto phi = mc.check(pathFormula.asEventuallyFormula().getSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector(); + auto phi = + mc.check(pathFormula.asEventuallyFormula().getSubformula())->template asExplicitQualitativeCheckResult().getTruthValuesVector(); absorbingStatesForSubformula = getOnlyReachableViaPhi(*model, phi); } else { STORM_LOG_THROW(false, storm::exceptions::InvalidPropertyException, "The subformula of " << pathFormula << " is not supported."); } } else if (opFormula->isLongRunAverageOperatorFormula()) { - auto lraStates = mc.check(pathFormula)->asExplicitQualitativeCheckResult().getTruthValuesVector(); + auto lraStates = mc.check(pathFormula)->template asExplicitQualitativeCheckResult().getTruthValuesVector(); // Compute Sat(Forall F (Forall G not "lraStates")) auto forallGloballyNotLraStates = storm::utility::graph::performProb0A(backwardTransitions, ~lraStates, lraStates); absorbingStatesForSubformula = storm::utility::graph::performProb1A(model->getTransitionMatrix(), model->getNondeterministicChoiceIndices(), @@ -432,7 +444,8 @@ void SparseMultiObjectivePreprocessor::preprocessLongRunAverage // Create and add the new reward model that only gives one reward for goal states storm::modelchecker::SparsePropositionalModelChecker mc(*data.model); - storm::storage::BitVector subFormulaResult = mc.check(formula.getSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector(); + storm::storage::BitVector subFormulaResult = + mc.check(formula.getSubformula())->template asExplicitQualitativeCheckResult().getTruthValuesVector(); std::vector lraRewards(data.model->getNumberOfStates(), storm::utility::zero()); storm::utility::vector::setVectorValues(lraRewards, subFormulaResult, storm::utility::one()); data.model->addRewardModel(rewardModelName, typename SparseModelType::RewardModelType(std::move(lraRewards))); @@ -445,7 +458,8 @@ void SparseMultiObjectivePreprocessor::preprocessUntilFormula(s // Try to transform the formula to expected total (or cumulative) rewards storm::modelchecker::SparsePropositionalModelChecker mc(*data.model); - storm::storage::BitVector rightSubformulaResult = mc.check(formula.getRightSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector(); + storm::storage::BitVector rightSubformulaResult = + mc.check(formula.getRightSubformula())->template asExplicitQualitativeCheckResult().getTruthValuesVector(); // Check if the formula is already satisfied in the initial state because then the transformation to expected rewards will fail. // TODO: Handle this case more properly STORM_LOG_THROW((data.model->getInitialStates() & rightSubformulaResult).empty(), storm::exceptions::NotImplementedException, @@ -455,7 +469,8 @@ void SparseMultiObjectivePreprocessor::preprocessUntilFormula(s // Whenever a state that violates the left subformula or satisfies the right subformula is reached, the objective is 'decided', i.e., no more reward should // be collected from there - storm::storage::BitVector notLeftOrRight = mc.check(formula.getLeftSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector(); + storm::storage::BitVector notLeftOrRight = + mc.check(formula.getLeftSubformula())->template asExplicitQualitativeCheckResult().getTruthValuesVector(); notLeftOrRight.complement(); notLeftOrRight |= rightSubformulaResult; @@ -542,7 +557,8 @@ void SparseMultiObjectivePreprocessor::preprocessEventuallyForm // Analyze the subformula storm::modelchecker::SparsePropositionalModelChecker mc(*data.model); - storm::storage::BitVector subFormulaResult = mc.check(formula.getSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector(); + storm::storage::BitVector subFormulaResult = + mc.check(formula.getSubformula())->template asExplicitQualitativeCheckResult().getTruthValuesVector(); // Get the states that are reachable from a goal state storm::storage::BitVector allStates(data.model->getNumberOfStates(), true), noStates(data.model->getNumberOfStates(), false); diff --git a/src/storm/modelchecker/prctl/HybridMdpPrctlModelChecker.cpp b/src/storm/modelchecker/prctl/HybridMdpPrctlModelChecker.cpp index 6bfdb2555f..aab273d075 100644 --- a/src/storm/modelchecker/prctl/HybridMdpPrctlModelChecker.cpp +++ b/src/storm/modelchecker/prctl/HybridMdpPrctlModelChecker.cpp @@ -173,7 +173,7 @@ std::unique_ptr HybridMdpPrctlModelChecker::checkMultiOb // Convert the explicit result if (explicitResult->isExplicitQualitativeCheckResult()) { - if (explicitResult->asExplicitQualitativeCheckResult()[*sparseModel->getInitialStates().begin()]) { + if (explicitResult->template asExplicitQualitativeCheckResult()[*sparseModel->getInitialStates().begin()]) { return std::unique_ptr(new storm::modelchecker::SymbolicQualitativeCheckResult( this->getModel().getReachableStates(), this->getModel().getInitialStates(), this->getModel().getManager().getBddOne())); } else { diff --git a/src/storm/modelchecker/prctl/SparseDtmcPrctlModelChecker.cpp b/src/storm/modelchecker/prctl/SparseDtmcPrctlModelChecker.cpp index 7ee772bf0c..e54a69b4ef 100644 --- a/src/storm/modelchecker/prctl/SparseDtmcPrctlModelChecker.cpp +++ b/src/storm/modelchecker/prctl/SparseDtmcPrctlModelChecker.cpp @@ -108,8 +108,8 @@ std::unique_ptr SparseDtmcPrctlModelChecker::c "Formula needs to have discrete upper step bound."); std::unique_ptr leftResultPointer = this->check(env, pathFormula.getLeftSubformula()); std::unique_ptr rightResultPointer = this->check(env, pathFormula.getRightSubformula()); - ExplicitQualitativeCheckResult const& leftResult = leftResultPointer->asExplicitQualitativeCheckResult(); - ExplicitQualitativeCheckResult const& rightResult = rightResultPointer->asExplicitQualitativeCheckResult(); + ExplicitQualitativeCheckResult const& leftResult = leftResultPointer->template asExplicitQualitativeCheckResult(); + ExplicitQualitativeCheckResult const& rightResult = rightResultPointer->template asExplicitQualitativeCheckResult(); storm::modelchecker::helper::SparseDeterministicStepBoundedHorizonHelper helper; std::vector numericResult = helper.compute(env, storm::solver::SolveGoal(this->getModel(), checkTask), this->getModel().getTransitionMatrix(), @@ -129,7 +129,7 @@ std::unique_ptr SparseDtmcPrctlModelChecker::c } else { storm::logic::NextFormula const& pathFormula = checkTask.getFormula(); std::unique_ptr subResultPointer = this->check(env, pathFormula.getSubformula()); - ExplicitQualitativeCheckResult const& subResult = subResultPointer->asExplicitQualitativeCheckResult(); + ExplicitQualitativeCheckResult const& subResult = subResultPointer->template asExplicitQualitativeCheckResult(); std::vector numericResult = storm::modelchecker::helper::SparseDtmcPrctlHelper::computeNextProbabilities( env, this->getModel().getTransitionMatrix(), subResult.getTruthValuesVector()); @@ -153,8 +153,8 @@ std::unique_ptr SparseDtmcPrctlModelChecker::c } std::unique_ptr leftResultPointer = this->check(env, pathFormula.getLeftSubformula()); std::unique_ptr rightResultPointer = this->check(env, pathFormula.getRightSubformula()); - ExplicitQualitativeCheckResult const& leftResult = leftResultPointer->asExplicitQualitativeCheckResult(); - ExplicitQualitativeCheckResult const& rightResult = rightResultPointer->asExplicitQualitativeCheckResult(); + ExplicitQualitativeCheckResult const& leftResult = leftResultPointer->template asExplicitQualitativeCheckResult(); + ExplicitQualitativeCheckResult const& rightResult = rightResultPointer->template asExplicitQualitativeCheckResult(); std::vector numericResult = storm::modelchecker::helper::SparseDtmcPrctlHelper::computeUntilProbabilities( env, storm::solver::SolveGoal(this->getModel(), checkTask), this->getModel().getTransitionMatrix(), @@ -171,7 +171,7 @@ std::unique_ptr SparseDtmcPrctlModelChecker::c } else { storm::logic::GloballyFormula const& pathFormula = checkTask.getFormula(); std::unique_ptr subResultPointer = this->check(env, pathFormula.getSubformula()); - ExplicitQualitativeCheckResult const& subResult = subResultPointer->asExplicitQualitativeCheckResult(); + ExplicitQualitativeCheckResult const& subResult = subResultPointer->template asExplicitQualitativeCheckResult(); std::vector numericResult = storm::modelchecker::helper::SparseDtmcPrctlHelper::computeGloballyProbabilities( env, storm::solver::SolveGoal(this->getModel(), checkTask), this->getModel().getTransitionMatrix(), @@ -192,7 +192,7 @@ std::unique_ptr SparseDtmcPrctlModelChecker::c storm::modelchecker::helper::setInformationFromCheckTaskDeterministic(helper, checkTask, this->getModel()); auto formulaChecker = [&](storm::logic::Formula const& formula) { - return this->check(env, formula)->asExplicitQualitativeCheckResult().getTruthValuesVector(); + return this->check(env, formula)->template asExplicitQualitativeCheckResult().getTruthValuesVector(); }; auto apSets = helper.computeApSets(pathFormula.getAPMapping(), formulaChecker); std::vector numericResult = helper.computeDAProductProbabilities(env, *pathFormula.readAutomaton(), apSets); @@ -213,7 +213,7 @@ std::unique_ptr SparseDtmcPrctlModelChecker::c storm::modelchecker::helper::setInformationFromCheckTaskDeterministic(helper, checkTask, this->getModel()); auto formulaChecker = [&](storm::logic::Formula const& formula) { - return this->check(env, formula)->asExplicitQualitativeCheckResult().getTruthValuesVector(); + return this->check(env, formula)->template asExplicitQualitativeCheckResult().getTruthValuesVector(); }; std::vector numericResult = helper.computeLTLProbabilities(env, pathFormula, formulaChecker); @@ -308,7 +308,7 @@ std::unique_ptr SparseDtmcPrctlModelChecker::c "Computing rewards on uncertain model requires graph-preservation."); } std::unique_ptr subResultPointer = this->check(env, eventuallyFormula.getSubformula()); - ExplicitQualitativeCheckResult const& subResult = subResultPointer->asExplicitQualitativeCheckResult(); + ExplicitQualitativeCheckResult const& subResult = subResultPointer->template asExplicitQualitativeCheckResult(); auto rewardModel = storm::utility::createFilteredRewardModel(this->getModel(), checkTask); std::vector numericResult = storm::modelchecker::helper::SparseDtmcPrctlHelper::computeReachabilityRewards( @@ -325,7 +325,7 @@ std::unique_ptr SparseDtmcPrctlModelChecker::c } else { storm::logic::EventuallyFormula const& eventuallyFormula = checkTask.getFormula(); std::unique_ptr subResultPointer = this->check(env, eventuallyFormula.getSubformula()); - ExplicitQualitativeCheckResult const& subResult = subResultPointer->asExplicitQualitativeCheckResult(); + ExplicitQualitativeCheckResult const& subResult = subResultPointer->template asExplicitQualitativeCheckResult(); std::vector numericResult = storm::modelchecker::helper::SparseDtmcPrctlHelper::computeReachabilityTimes( env, storm::solver::SolveGoal(this->getModel(), checkTask), this->getModel().getTransitionMatrix(), @@ -380,13 +380,13 @@ std::unique_ptr SparseDtmcPrctlModelChecker::c } else { storm::logic::StateFormula const& stateFormula = checkTask.getFormula(); std::unique_ptr subResultPointer = this->check(env, stateFormula); - ExplicitQualitativeCheckResult const& subResult = subResultPointer->asExplicitQualitativeCheckResult(); + ExplicitQualitativeCheckResult const& subResult = subResultPointer->template asExplicitQualitativeCheckResult(); storm::modelchecker::helper::SparseDeterministicInfiniteHorizonHelper helper(this->getModel().getTransitionMatrix()); storm::modelchecker::helper::setInformationFromCheckTaskDeterministic(helper, checkTask, this->getModel()); auto values = helper.computeLongRunAverageProbabilities(env, subResult.getTruthValuesVector()); - return std::unique_ptr(new ExplicitQuantitativeCheckResult(std::move(values))); + return std::unique_ptr(new ExplicitQuantitativeCheckResult(std::move(values))); } } @@ -418,8 +418,8 @@ std::unique_ptr SparseDtmcPrctlModelChecker::c std::unique_ptr leftResultPointer = this->check(env, conditionalFormula.getSubformula().asEventuallyFormula().getSubformula()); std::unique_ptr rightResultPointer = this->check(env, conditionalFormula.getConditionFormula().asEventuallyFormula().getSubformula()); - ExplicitQualitativeCheckResult const& leftResult = leftResultPointer->asExplicitQualitativeCheckResult(); - ExplicitQualitativeCheckResult const& rightResult = rightResultPointer->asExplicitQualitativeCheckResult(); + ExplicitQualitativeCheckResult const& leftResult = leftResultPointer->template asExplicitQualitativeCheckResult(); + ExplicitQualitativeCheckResult const& rightResult = rightResultPointer->template asExplicitQualitativeCheckResult(); std::vector numericResult = storm::modelchecker::helper::SparseDtmcPrctlHelper::computeConditionalProbabilities( @@ -443,8 +443,8 @@ std::unique_ptr SparseDtmcPrctlModelChecker::c std::unique_ptr leftResultPointer = this->check(env, conditionalFormula.getSubformula().asReachabilityRewardFormula().getSubformula()); std::unique_ptr rightResultPointer = this->check(env, conditionalFormula.getConditionFormula().asEventuallyFormula().getSubformula()); - ExplicitQualitativeCheckResult const& leftResult = leftResultPointer->asExplicitQualitativeCheckResult(); - ExplicitQualitativeCheckResult const& rightResult = rightResultPointer->asExplicitQualitativeCheckResult(); + ExplicitQualitativeCheckResult const& leftResult = leftResultPointer->template asExplicitQualitativeCheckResult(); + ExplicitQualitativeCheckResult const& rightResult = rightResultPointer->template asExplicitQualitativeCheckResult(); std::vector numericResult = storm::modelchecker::helper::SparseDtmcPrctlHelper::computeConditionalRewards( diff --git a/src/storm/modelchecker/prctl/SparseMdpPrctlModelChecker.cpp b/src/storm/modelchecker/prctl/SparseMdpPrctlModelChecker.cpp index 16ce6c7bb5..55c4102e46 100644 --- a/src/storm/modelchecker/prctl/SparseMdpPrctlModelChecker.cpp +++ b/src/storm/modelchecker/prctl/SparseMdpPrctlModelChecker.cpp @@ -37,13 +37,16 @@ template bool SparseMdpPrctlModelChecker::canHandleStatic(CheckTask const& checkTask, bool* requiresSingleInitialState) { storm::logic::Formula const& formula = checkTask.getFormula(); - if constexpr (std::is_same_v) { + if constexpr (storm::IsIntervalType) { if (formula.isInFragment(storm::logic::propositional())) { return true; } if (formula.isInFragment(storm::logic::reachability())) { return true; } + if (formula.isInFragment(storm::logic::prctlstar().setBoundedUntilFormulasAllowed(true))) { + return true; + } } else { if (formula.isInFragment(storm::logic::prctlstar() .setLongRunAverageRewardFormulasAllowed(true) @@ -111,14 +114,14 @@ bool SparseMdpPrctlModelChecker::canHandle(CheckTask std::unique_ptr SparseMdpPrctlModelChecker::computeBoundedUntilProbabilities( Environment const& env, CheckTask const& checkTask) { - if constexpr (std::is_same_v) { - STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "We have not yet implemented bounded until with intervals"); - return nullptr; - } else { - storm::logic::BoundedUntilFormula const& pathFormula = checkTask.getFormula(); - STORM_LOG_THROW(checkTask.isOptimizationDirectionSet(), storm::exceptions::InvalidPropertyException, - "Formula needs to specify whether minimal or maximal values are to be computed on nondeterministic model."); - if (pathFormula.isMultiDimensional() || pathFormula.getTimeBoundReference().isRewardBound()) { + storm::logic::BoundedUntilFormula const& pathFormula = checkTask.getFormula(); + STORM_LOG_THROW(checkTask.isOptimizationDirectionSet(), storm::exceptions::InvalidPropertyException, + "Formula needs to specify whether minimal or maximal values are to be computed on nondeterministic model."); + if (pathFormula.isMultiDimensional() || pathFormula.getTimeBoundReference().isRewardBound()) { + if constexpr (storm::IsIntervalType) { + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "We have not yet implemented multi dimensional bounded until with intervals"); + return nullptr; + } else { STORM_LOG_THROW(checkTask.isOnlyInitialStatesRelevantSet(), storm::exceptions::InvalidOperationException, "Checking non-trivial bounded until probabilities can only be computed for the initial states of the model."); STORM_LOG_WARN_COND(!checkTask.isQualitativeSet(), "Checking non-trivial bounded until formulas is not optimized w.r.t. qualitative queries"); @@ -131,23 +134,21 @@ std::unique_ptr SparseMdpPrctlModelChecker::com auto numericResult = storm::modelchecker::helper::SparseMdpPrctlHelper::computeRewardBoundedValues( env, checkTask.getOptimizationDirection(), rewardUnfolding, this->getModel().getInitialStates()); return std::unique_ptr(new ExplicitQuantitativeCheckResult(std::move(numericResult))); - } else { - STORM_LOG_THROW(pathFormula.hasUpperBound(), storm::exceptions::InvalidPropertyException, "Formula needs to have (a single) upper step bound."); - STORM_LOG_THROW(pathFormula.hasIntegerLowerBound(), storm::exceptions::InvalidPropertyException, - "Formula lower step bound must be discrete/integral."); - STORM_LOG_THROW(pathFormula.hasIntegerUpperBound(), storm::exceptions::InvalidPropertyException, - "Formula needs to have discrete upper time bound."); - std::unique_ptr leftResultPointer = this->check(env, pathFormula.getLeftSubformula()); - std::unique_ptr rightResultPointer = this->check(env, pathFormula.getRightSubformula()); - ExplicitQualitativeCheckResult const& leftResult = leftResultPointer->asExplicitQualitativeCheckResult(); - ExplicitQualitativeCheckResult const& rightResult = rightResultPointer->asExplicitQualitativeCheckResult(); - storm::modelchecker::helper::SparseNondeterministicStepBoundedHorizonHelper helper; - std::vector numericResult = - helper.compute(env, storm::solver::SolveGoal(this->getModel(), checkTask), this->getModel().getTransitionMatrix(), - this->getModel().getBackwardTransitions(), leftResult.getTruthValuesVector(), rightResult.getTruthValuesVector(), - pathFormula.getNonStrictLowerBound(), pathFormula.getNonStrictUpperBound(), checkTask.getHint()); - return std::unique_ptr(new ExplicitQuantitativeCheckResult(std::move(numericResult))); } + } else { + STORM_LOG_THROW(pathFormula.hasUpperBound(), storm::exceptions::InvalidPropertyException, "Formula needs to have (a single) upper step bound."); + STORM_LOG_THROW(pathFormula.hasIntegerLowerBound(), storm::exceptions::InvalidPropertyException, "Formula lower step bound must be discrete/integral."); + STORM_LOG_THROW(pathFormula.hasIntegerUpperBound(), storm::exceptions::InvalidPropertyException, "Formula needs to have discrete upper time bound."); + std::unique_ptr leftResultPointer = this->check(env, pathFormula.getLeftSubformula()); + std::unique_ptr rightResultPointer = this->check(env, pathFormula.getRightSubformula()); + ExplicitQualitativeCheckResult const& leftResult = leftResultPointer->template asExplicitQualitativeCheckResult(); + ExplicitQualitativeCheckResult const& rightResult = rightResultPointer->template asExplicitQualitativeCheckResult(); + storm::modelchecker::helper::SparseNondeterministicStepBoundedHorizonHelper helper; + std::vector numericResult = + helper.compute(env, storm::solver::SolveGoal(this->getModel(), checkTask), this->getModel().getTransitionMatrix(), + this->getModel().getBackwardTransitions(), leftResult.getTruthValuesVector(), rightResult.getTruthValuesVector(), + pathFormula.getNonStrictLowerBound(), pathFormula.getNonStrictUpperBound(), checkTask.getHint()); + return std::unique_ptr(new ExplicitQuantitativeCheckResult(std::move(numericResult))); } } @@ -158,7 +159,7 @@ std::unique_ptr SparseMdpPrctlModelChecker::com STORM_LOG_THROW(checkTask.isOptimizationDirectionSet(), storm::exceptions::InvalidPropertyException, "Formula needs to specify whether minimal or maximal values are to be computed on nondeterministic model."); std::unique_ptr subResultPointer = this->check(env, pathFormula.getSubformula()); - ExplicitQualitativeCheckResult const& subResult = subResultPointer->asExplicitQualitativeCheckResult(); + ExplicitQualitativeCheckResult const& subResult = subResultPointer->template asExplicitQualitativeCheckResult(); std::vector numericResult = storm::modelchecker::helper::SparseMdpPrctlHelper::computeNextProbabilities( env, checkTask.getOptimizationDirection(), this->getModel().getTransitionMatrix(), subResult.getTruthValuesVector()); return std::unique_ptr(new ExplicitQuantitativeCheckResult(std::move(numericResult))); @@ -172,8 +173,8 @@ std::unique_ptr SparseMdpPrctlModelChecker::com "Formula needs to specify whether minimal or maximal values are to be computed on nondeterministic model."); std::unique_ptr leftResultPointer = this->check(env, pathFormula.getLeftSubformula()); std::unique_ptr rightResultPointer = this->check(env, pathFormula.getRightSubformula()); - ExplicitQualitativeCheckResult const& leftResult = leftResultPointer->asExplicitQualitativeCheckResult(); - ExplicitQualitativeCheckResult const& rightResult = rightResultPointer->asExplicitQualitativeCheckResult(); + ExplicitQualitativeCheckResult const& leftResult = leftResultPointer->template asExplicitQualitativeCheckResult(); + ExplicitQualitativeCheckResult const& rightResult = rightResultPointer->template asExplicitQualitativeCheckResult(); auto ret = storm::modelchecker::helper::SparseMdpPrctlHelper::computeUntilProbabilities( env, storm::solver::SolveGoal(this->getModel(), checkTask), this->getModel().getTransitionMatrix(), this->getModel().getBackwardTransitions(), leftResult.getTruthValuesVector(), rightResult.getTruthValuesVector(), checkTask.isQualitativeSet(), @@ -192,7 +193,7 @@ std::unique_ptr SparseMdpPrctlModelChecker::com STORM_LOG_THROW(checkTask.isOptimizationDirectionSet(), storm::exceptions::InvalidPropertyException, "Formula needs to specify whether minimal or maximal values are to be computed on nondeterministic model."); std::unique_ptr subResultPointer = this->check(env, pathFormula.getSubformula()); - ExplicitQualitativeCheckResult const& subResult = subResultPointer->asExplicitQualitativeCheckResult(); + ExplicitQualitativeCheckResult const& subResult = subResultPointer->template asExplicitQualitativeCheckResult(); auto ret = storm::modelchecker::helper::SparseMdpPrctlHelper::computeGloballyProbabilities( env, storm::solver::SolveGoal(this->getModel(), checkTask), this->getModel().getTransitionMatrix(), this->getModel().getBackwardTransitions(), subResult.getTruthValuesVector(), checkTask.isQualitativeSet(), checkTask.isProduceSchedulersSet()); @@ -206,7 +207,7 @@ std::unique_ptr SparseMdpPrctlModelChecker::com template std::unique_ptr SparseMdpPrctlModelChecker::computeHOAPathProbabilities( Environment const& env, CheckTask const& checkTask) { - if constexpr (std::is_same_v) { + if constexpr (storm::IsIntervalType) { STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "We have not yet implemented automata-props with intervals"); } else { storm::logic::HOAPathFormula const& pathFormula = checkTask.getFormula(); @@ -215,7 +216,7 @@ std::unique_ptr SparseMdpPrctlModelChecker::com storm::modelchecker::helper::setInformationFromCheckTaskNondeterministic(helper, checkTask, this->getModel()); auto formulaChecker = [&](storm::logic::Formula const& formula) { - return this->check(env, formula)->asExplicitQualitativeCheckResult().getTruthValuesVector(); + return this->check(env, formula)->template asExplicitQualitativeCheckResult().getTruthValuesVector(); }; auto apSets = helper.computeApSets(pathFormula.getAPMapping(), formulaChecker); std::vector numericResult = helper.computeDAProductProbabilities(env, *pathFormula.readAutomaton(), apSets); @@ -233,7 +234,7 @@ std::unique_ptr SparseMdpPrctlModelChecker::com template std::unique_ptr SparseMdpPrctlModelChecker::computeLTLProbabilities( Environment const& env, CheckTask const& checkTask) { - if constexpr (std::is_same_v) { + if constexpr (storm::IsIntervalType) { STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "We have not yet implemented LTL with intervals"); } else { storm::logic::PathFormula const& pathFormula = checkTask.getFormula(); @@ -245,7 +246,7 @@ std::unique_ptr SparseMdpPrctlModelChecker::com storm::modelchecker::helper::setInformationFromCheckTaskNondeterministic(helper, checkTask, this->getModel()); auto formulaChecker = [&](storm::logic::Formula const& formula) { - return this->check(env, formula)->asExplicitQualitativeCheckResult().getTruthValuesVector(); + return this->check(env, formula)->template asExplicitQualitativeCheckResult().getTruthValuesVector(); }; std::vector numericResult = helper.computeLTLProbabilities(env, pathFormula, formulaChecker); @@ -276,14 +277,14 @@ std::unique_ptr SparseMdpPrctlModelChecker::com std::unique_ptr leftResultPointer = this->check(env, conditionalFormula.getSubformula().asEventuallyFormula().getSubformula()); std::unique_ptr rightResultPointer = this->check(env, conditionalFormula.getConditionFormula().asEventuallyFormula().getSubformula()); - ExplicitQualitativeCheckResult const& leftResult = leftResultPointer->asExplicitQualitativeCheckResult(); - ExplicitQualitativeCheckResult const& rightResult = rightResultPointer->asExplicitQualitativeCheckResult(); - if constexpr (std::is_same_v) { + ExplicitQualitativeCheckResult const& leftResult = leftResultPointer->template asExplicitQualitativeCheckResult(); + ExplicitQualitativeCheckResult const& rightResult = rightResultPointer->template asExplicitQualitativeCheckResult(); + if constexpr (storm::IsIntervalType) { throw exceptions::NotImplementedException() << "Conditional Probabilities are not supported with interval models"; } else { - return storm::modelchecker::computeConditionalProbabilities(env, storm::solver::SolveGoal(this->getModel(), checkTask), - this->getModel().getTransitionMatrix(), this->getModel().getBackwardTransitions(), - leftResult.getTruthValuesVector(), rightResult.getTruthValuesVector()); + return storm::modelchecker::computeConditionalProbabilities( + env, storm::solver::SolveGoal(this->getModel(), checkTask), checkTask, this->getModel().getTransitionMatrix(), + this->getModel().getBackwardTransitions(), leftResult.getTruthValuesVector(), rightResult.getTruthValuesVector()); } } @@ -294,24 +295,20 @@ std::unique_ptr SparseMdpPrctlModelChecker::com STORM_LOG_THROW(checkTask.isOptimizationDirectionSet(), storm::exceptions::InvalidPropertyException, "Formula needs to specify whether minimal or maximal values are to be computed on nondeterministic model."); if (rewardPathFormula.isMultiDimensional() || rewardPathFormula.getTimeBoundReference().isRewardBound()) { - if constexpr (std::is_same_v) { - throw exceptions::NotImplementedException() << "Multi-reward bounded is not supported with interval models"; - } else { - STORM_LOG_THROW(checkTask.isOnlyInitialStatesRelevantSet(), storm::exceptions::InvalidOperationException, - "Checking reward bounded cumulative reward formulas can only be done for the initial states of the model."); - STORM_LOG_THROW(!checkTask.getFormula().hasRewardAccumulation(), storm::exceptions::InvalidOperationException, - "Checking reward bounded cumulative reward formulas is not supported if reward accumulations are given."); - STORM_LOG_WARN_COND(!checkTask.isQualitativeSet(), "Checking reward bounded until formulas is not optimized w.r.t. qualitative queries"); - storm::logic::OperatorInformation opInfo(checkTask.getOptimizationDirection()); - if (checkTask.isBoundSet()) { - opInfo.bound = checkTask.getBound(); - } - auto formula = std::make_shared(checkTask.getFormula().asSharedPointer(), checkTask.getRewardModel(), opInfo); - helper::rewardbounded::MultiDimensionalRewardUnfolding rewardUnfolding(this->getModel(), formula); - auto numericResult = storm::modelchecker::helper::SparseMdpPrctlHelper::computeRewardBoundedValues( - env, checkTask.getOptimizationDirection(), rewardUnfolding, this->getModel().getInitialStates()); - return std::unique_ptr(new ExplicitQuantitativeCheckResult(std::move(numericResult))); + STORM_LOG_THROW(checkTask.isOnlyInitialStatesRelevantSet(), storm::exceptions::InvalidOperationException, + "Checking reward bounded cumulative reward formulas can only be done for the initial states of the model."); + STORM_LOG_THROW(!checkTask.getFormula().hasRewardAccumulation(), storm::exceptions::InvalidOperationException, + "Checking reward bounded cumulative reward formulas is not supported if reward accumulations are given."); + STORM_LOG_WARN_COND(!checkTask.isQualitativeSet(), "Checking reward bounded until formulas is not optimized w.r.t. qualitative queries"); + storm::logic::OperatorInformation opInfo(checkTask.getOptimizationDirection()); + if (checkTask.isBoundSet()) { + opInfo.bound = checkTask.getBound(); } + auto formula = std::make_shared(checkTask.getFormula().asSharedPointer(), checkTask.getRewardModel(), opInfo); + helper::rewardbounded::MultiDimensionalRewardUnfolding rewardUnfolding(this->getModel(), formula); + auto numericResult = storm::modelchecker::helper::SparseMdpPrctlHelper::computeRewardBoundedValues( + env, checkTask.getOptimizationDirection(), rewardUnfolding, this->getModel().getInitialStates()); + return std::unique_ptr(new ExplicitQuantitativeCheckResult(std::move(numericResult))); } else { STORM_LOG_THROW(rewardPathFormula.hasIntegerBound(), storm::exceptions::InvalidPropertyException, "Formula needs to have a discrete time bound."); auto rewardModel = storm::utility::createFilteredRewardModel(this->getModel(), checkTask); @@ -363,7 +360,7 @@ std::unique_ptr SparseMdpPrctlModelChecker::com STORM_LOG_THROW(checkTask.isOptimizationDirectionSet(), storm::exceptions::InvalidPropertyException, "Formula needs to specify whether minimal or maximal values are to be computed on nondeterministic model."); std::unique_ptr subResultPointer = this->check(env, eventuallyFormula.getSubformula()); - ExplicitQualitativeCheckResult const& subResult = subResultPointer->asExplicitQualitativeCheckResult(); + ExplicitQualitativeCheckResult const& subResult = subResultPointer->template asExplicitQualitativeCheckResult(); auto rewardModel = storm::utility::createFilteredRewardModel(this->getModel(), checkTask); auto ret = storm::modelchecker::helper::SparseMdpPrctlHelper::computeReachabilityRewards( env, storm::solver::SolveGoal(this->getModel(), checkTask), this->getModel().getTransitionMatrix(), @@ -383,7 +380,7 @@ std::unique_ptr SparseMdpPrctlModelChecker::com STORM_LOG_THROW(checkTask.isOptimizationDirectionSet(), storm::exceptions::InvalidPropertyException, "Formula needs to specify whether minimal or maximal values are to be computed on nondeterministic model."); std::unique_ptr subResultPointer = this->check(env, eventuallyFormula.getSubformula()); - ExplicitQualitativeCheckResult const& subResult = subResultPointer->asExplicitQualitativeCheckResult(); + ExplicitQualitativeCheckResult const& subResult = subResultPointer->template asExplicitQualitativeCheckResult(); auto ret = storm::modelchecker::helper::SparseMdpPrctlHelper::computeReachabilityTimes( env, storm::solver::SolveGoal(this->getModel(), checkTask), this->getModel().getTransitionMatrix(), this->getModel().getBackwardTransitions(), subResult.getTruthValuesVector(), checkTask.isQualitativeSet(), checkTask.isProduceSchedulersSet(), @@ -439,14 +436,14 @@ std::unique_ptr SparseMdpPrctlModelChecker::com template std::unique_ptr SparseMdpPrctlModelChecker::computeLongRunAverageProbabilities( Environment const& env, CheckTask const& checkTask) { - if constexpr (std::is_same_v) { + if constexpr (storm::IsIntervalType) { STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "We have not yet implemented LRA probabilities with intervals"); } else { storm::logic::StateFormula const& stateFormula = checkTask.getFormula(); STORM_LOG_THROW(checkTask.isOptimizationDirectionSet(), storm::exceptions::InvalidPropertyException, "Formula needs to specify whether minimal or maximal values are to be computed on nondeterministic model."); std::unique_ptr subResultPointer = this->check(env, stateFormula); - ExplicitQualitativeCheckResult const& subResult = subResultPointer->asExplicitQualitativeCheckResult(); + ExplicitQualitativeCheckResult const& subResult = subResultPointer->template asExplicitQualitativeCheckResult(); storm::modelchecker::helper::SparseNondeterministicInfiniteHorizonHelper helper(this->getModel().getTransitionMatrix()); storm::modelchecker::helper::setInformationFromCheckTaskNondeterministic(helper, checkTask, this->getModel()); @@ -464,7 +461,7 @@ std::unique_ptr SparseMdpPrctlModelChecker::com template std::unique_ptr SparseMdpPrctlModelChecker::computeLongRunAverageRewards( Environment const& env, CheckTask const& checkTask) { - if constexpr (std::is_same_v) { + if constexpr (storm::IsIntervalType) { STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "We have not yet implemented lra with intervals"); } else { STORM_LOG_THROW(checkTask.isOptimizationDirectionSet(), storm::exceptions::InvalidPropertyException, @@ -485,7 +482,7 @@ std::unique_ptr SparseMdpPrctlModelChecker::com template std::unique_ptr SparseMdpPrctlModelChecker::checkMultiObjectiveFormula( Environment const& env, CheckTask const& checkTask) { - if constexpr (std::is_same_v) { + if constexpr (storm::IsIntervalType) { STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "We have not yet implemented multi-objective with intervals"); } else { return multiobjective::performMultiObjectiveModelChecking(env, this->getModel(), checkTask.getFormula(), checkTask.isProduceSchedulersSet()); @@ -495,11 +492,11 @@ std::unique_ptr SparseMdpPrctlModelChecker::che template std::unique_ptr SparseMdpPrctlModelChecker::checkLexObjectiveFormula( const Environment& env, const CheckTask& checkTask) { - if constexpr (std::is_same_v) { + if constexpr (storm::IsIntervalType) { STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "We have not yet implemented lexicographic model checking with intervals"); } else { auto formulaChecker = [&](storm::logic::Formula const& formula) { - return this->check(env, formula)->asExplicitQualitativeCheckResult().getTruthValuesVector(); + return this->check(env, formula)->template asExplicitQualitativeCheckResult().getTruthValuesVector(); }; auto ret = lexicographic::check(env, this->getModel(), checkTask, formulaChecker); std::unique_ptr result(new LexicographicCheckResult(ret.values, *this->getModel().getInitialStates().begin())); @@ -510,7 +507,7 @@ std::unique_ptr SparseMdpPrctlModelChecker::che template std::unique_ptr SparseMdpPrctlModelChecker::checkQuantileFormula( Environment const& env, CheckTask const& checkTask) { - if constexpr (std::is_same_v) { + if constexpr (storm::IsIntervalType) { STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "We have not yet implemented quantile formulas with intervals"); } else { STORM_LOG_THROW(checkTask.isOnlyInitialStatesRelevantSet(), storm::exceptions::InvalidOperationException, @@ -533,5 +530,6 @@ std::unique_ptr SparseMdpPrctlModelChecker::che template class SparseMdpPrctlModelChecker>; template class SparseMdpPrctlModelChecker>; template class SparseMdpPrctlModelChecker>; +template class SparseMdpPrctlModelChecker>; } // namespace modelchecker } // namespace storm diff --git a/src/storm/modelchecker/prctl/SparseMdpPrctlModelChecker.h b/src/storm/modelchecker/prctl/SparseMdpPrctlModelChecker.h index aa39728344..5a7f10a1fe 100644 --- a/src/storm/modelchecker/prctl/SparseMdpPrctlModelChecker.h +++ b/src/storm/modelchecker/prctl/SparseMdpPrctlModelChecker.h @@ -14,7 +14,7 @@ class SparseMdpPrctlModelChecker : public SparsePropositionalModelChecker, double, ValueType>::type; + using SolutionType = storm::IntervalBaseType; explicit SparseMdpPrctlModelChecker(SparseMdpModelType const& model); diff --git a/src/storm/modelchecker/prctl/helper/SparseMdpEndComponentInformation.cpp b/src/storm/modelchecker/prctl/helper/SparseMdpEndComponentInformation.cpp index 275318d813..9ef02c1c25 100644 --- a/src/storm/modelchecker/prctl/helper/SparseMdpEndComponentInformation.cpp +++ b/src/storm/modelchecker/prctl/helper/SparseMdpEndComponentInformation.cpp @@ -105,7 +105,7 @@ SparseMdpEndComponentInformation SparseMdpEndComponentInformation* columnSumVector, std::vector* summandResultVector, bool gatherExitChoices) { SparseMdpEndComponentInformation result(endComponentDecomposition, maybeStates); // TODO: Just like SparseMdpPrctlHelper::computeFixedPointSystemUntilProbabilities, this method must be adapted for intervals. - if constexpr (std::is_same_v) { + if constexpr (storm::IsIntervalType) { STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "We do not support the elimination of end components and the creation of an adequate equation system with interval models."); } @@ -247,7 +247,7 @@ SparseMdpEndComponentInformation SparseMdpEndComponentInformation& submatrix, std::vector& subvector, bool gatherExitChoices) { SparseMdpEndComponentInformation result(endComponentDecomposition, maybeStates); // TODO: Just like SparseMdpPrctlHelper::computeFixedPointSystemUntilProbabilities, this method must be adapted for intervals. - if constexpr (std::is_same_v) { + if constexpr (storm::IsIntervalType) { STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "We do not support the elimination of end components and the creation of an adequate equation system with interval models."); } @@ -421,6 +421,7 @@ void SparseMdpEndComponentInformation::setScheduler(storm::storage::S template class SparseMdpEndComponentInformation; template class SparseMdpEndComponentInformation; template class SparseMdpEndComponentInformation; +template class SparseMdpEndComponentInformation; template void SparseMdpEndComponentInformation::setScheduler(storm::storage::Scheduler& scheduler, storm::storage::BitVector const& maybeStates, storm::storage::SparseMatrix const& transitionMatrix, @@ -438,6 +439,11 @@ template void SparseMdpEndComponentInformation::setScheduler(st storm::storage::SparseMatrix const& backwardTransitions, std::vector const& fromResult); +template void SparseMdpEndComponentInformation::setScheduler( + storm::storage::Scheduler& scheduler, storm::storage::BitVector const& maybeStates, + storm::storage::SparseMatrix const& transitionMatrix, + storm::storage::SparseMatrix const& backwardTransitions, std::vector const& fromResult); + // template class SparseMdpEndComponentInformation; } // namespace helper diff --git a/src/storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.cpp b/src/storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.cpp index ce93657d0d..60a4d0a3b4 100644 --- a/src/storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.cpp +++ b/src/storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.cpp @@ -43,7 +43,7 @@ template std::map SparseMdpPrctlHelper::computeRewardBoundedValues( Environment const& env, OptimizationDirection dir, rewardbounded::MultiDimensionalRewardUnfolding& rewardUnfolding, storm::storage::BitVector const& initialStates) { - if constexpr (std::is_same_v) { + if constexpr (storm::IsIntervalType) { STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "We do not support computing reward bounded values with interval models."); } else { storm::utility::Stopwatch swAll(true), swBuild, swCheck; @@ -133,8 +133,8 @@ template std::vector SparseMdpPrctlHelper::computeNextProbabilities( Environment const& env, OptimizationDirection dir, storm::storage::SparseMatrix const& transitionMatrix, storm::storage::BitVector const& nextStates) { - if constexpr (std::is_same_v) { - STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "We do not support next probabilities with reward models."); + if constexpr (storm::IsIntervalType) { + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "We do not support next probabilities with interval models."); } else { // Create the vector with which to multiply and initialize it correctly. std::vector result(transitionMatrix.getRowGroupCount()); @@ -612,7 +612,7 @@ void computeFixedPointSystemUntilProbabilities(storm::solver::SolveGoal const& transitionMatrix, QualitativeStateSetsUntilProbabilities const& qualitativeStateSets, storm::storage::SparseMatrix& submatrix, std::vector& b) { - if constexpr (std::is_same_v) { + if constexpr (storm::IsIntervalType) { // For non-interval based models, we can eliminate the rows and columns from the original transition probability matrix for states // whose probabilities are already known... However, there is information in the transition to those states. // Thus, we cannot eliminate them all. @@ -757,7 +757,7 @@ MDPSparseModelCheckingHelperReturnType SparseMdpPrctlHelper) { + if constexpr (storm::IsIntervalType) { STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "We do not support this end component with interval models."); } else { ecInformation.get().setValues(result, qualitativeStateSets.maybeStates, resultForMaybeStates.getValues()); @@ -768,7 +768,7 @@ MDPSparseModelCheckingHelperReturnType SparseMdpPrctlHelper) { + if constexpr (!storm::IsIntervalType) { // For non-interval models, we only operated on the maybe states, and we must recover the qualitative values for the other state. storm::utility::vector::setVectorValues(result, qualitativeStateSets.maybeStates, resultForMaybeStates.getValues()); } else { @@ -777,8 +777,8 @@ MDPSparseModelCheckingHelperReturnType SparseMdpPrctlHelper>(*scheduler, resultForMaybeStates.getScheduler(), - qualitativeStateSets.maybeStates); + extractSchedulerChoices>(*scheduler, resultForMaybeStates.getScheduler(), + qualitativeStateSets.maybeStates); } } } @@ -833,7 +833,7 @@ template std::vector SparseMdpPrctlHelper::computeInstantaneousRewards( Environment const& env, storm::solver::SolveGoal&& goal, storm::storage::SparseMatrix const& transitionMatrix, RewardModelType const& rewardModel, uint_fast64_t stepCount) { - if constexpr (std::is_same_v) { + if constexpr (storm::IsIntervalType) { STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "We do not support instantenous rewards with interval models."); } else { // Only compute the result if the model has a state-based reward this->getModel(). @@ -854,7 +854,7 @@ template std::vector SparseMdpPrctlHelper::computeCumulativeRewards( Environment const& env, storm::solver::SolveGoal&& goal, storm::storage::SparseMatrix const& transitionMatrix, RewardModelType const& rewardModel, uint_fast64_t stepBound) { - if constexpr (std::is_same_v) { + if constexpr (storm::IsIntervalType) { STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "We do not support cumulative rewards with interval models."); } else { // Only compute the result if the model has at least one reward this->getModel(). @@ -1061,27 +1061,69 @@ std::vector SparseMdpPrctlHelper::compute bool lowerBoundOfIntervals, storm::storage::BitVector const& targetStates, bool qualitative) { // Only compute the result if the reward model is not empty. STORM_LOG_THROW(!intervalRewardModel.empty(), storm::exceptions::InvalidPropertyException, "Missing reward model for formula. Skipping formula."); - return computeReachabilityRewardsHelper( - env, std::move(goal), transitionMatrix, backwardTransitions, - [&](uint_fast64_t rowCount, storm::storage::SparseMatrix const& transitionMatrix, storm::storage::BitVector const& maybeStates) { - std::vector result; - result.reserve(rowCount); - std::vector subIntervalVector = intervalRewardModel.getTotalRewardVector(rowCount, transitionMatrix, maybeStates); - for (auto const& interval : subIntervalVector) { - result.push_back(lowerBoundOfIntervals ? interval.lower() : interval.upper()); - } - return result; - }, - targetStates, qualitative, false, - [&]() { - return intervalRewardModel.getStatesWithFilter( - transitionMatrix, [&](storm::Interval const& i) { return storm::utility::isZero(lowerBoundOfIntervals ? i.lower() : i.upper()); }); - }, - [&]() { - return intervalRewardModel.getChoicesWithFilter( - transitionMatrix, [&](storm::Interval const& i) { return storm::utility::isZero(lowerBoundOfIntervals ? i.lower() : i.upper()); }); - }) - .values; + if constexpr (std::is_same_v) { + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "We do not support rational interval rewards with double interval models."); + } else { + return computeReachabilityRewardsHelper( + env, std::move(goal), transitionMatrix, backwardTransitions, + [&](uint_fast64_t rowCount, storm::storage::SparseMatrix const& transitionMatrix, storm::storage::BitVector const& maybeStates) { + std::vector result; + result.reserve(rowCount); + std::vector subIntervalVector = intervalRewardModel.getTotalRewardVector(rowCount, transitionMatrix, maybeStates); + for (auto const& interval : subIntervalVector) { + result.push_back(lowerBoundOfIntervals ? interval.lower() : interval.upper()); + } + return result; + }, + targetStates, qualitative, false, + [&]() { + return intervalRewardModel.getStatesWithFilter( + transitionMatrix, [&](storm::Interval const& i) { return storm::utility::isZero(lowerBoundOfIntervals ? i.lower() : i.upper()); }); + }, + [&]() { + return intervalRewardModel.getChoicesWithFilter( + transitionMatrix, [&](storm::Interval const& i) { return storm::utility::isZero(lowerBoundOfIntervals ? i.lower() : i.upper()); }); + }) + .values; + } +} + +template +std::vector SparseMdpPrctlHelper::computeReachabilityRewards( + Environment const& env, storm::solver::SolveGoal&& goal, storm::storage::SparseMatrix const& transitionMatrix, + storm::storage::SparseMatrix const& backwardTransitions, + storm::models::sparse::StandardRewardModel const& intervalRewardModel, bool lowerBoundOfIntervals, + storm::storage::BitVector const& targetStates, bool qualitative) { + // Only compute the result if the reward model is not empty. + STORM_LOG_THROW(!intervalRewardModel.empty(), storm::exceptions::InvalidPropertyException, "Missing reward model for formula. Skipping formula."); + if constexpr (std::is_same_v) { + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "We do not support interval rewards with rational interval models."); + } else { + return computeReachabilityRewardsHelper( + env, std::move(goal), transitionMatrix, backwardTransitions, + [&](uint_fast64_t rowCount, storm::storage::SparseMatrix const& transitionMatrix, storm::storage::BitVector const& maybeStates) { + std::vector result; + result.reserve(rowCount); + std::vector subIntervalVector = + intervalRewardModel.getTotalRewardVector(rowCount, transitionMatrix, maybeStates); + for (auto const& interval : subIntervalVector) { + result.push_back(lowerBoundOfIntervals ? interval.lower() : interval.upper()); + } + return result; + }, + targetStates, qualitative, false, + [&]() { + return intervalRewardModel.getStatesWithFilter(transitionMatrix, [&](storm::RationalInterval const& i) { + return storm::utility::isZero(lowerBoundOfIntervals ? i.lower() : i.upper()); + }); + }, + [&]() { + return intervalRewardModel.getChoicesWithFilter(transitionMatrix, [&](storm::RationalInterval const& i) { + return storm::utility::isZero(lowerBoundOfIntervals ? i.lower() : i.upper()); + }); + }) + .values; + } } template<> @@ -1092,6 +1134,15 @@ std::vector SparseMdpPrctlHelper:: STORM_LOG_THROW(false, storm::exceptions::IllegalFunctionCallException, "Computing reachability rewards is unsupported for this data type."); } +template<> +std::vector SparseMdpPrctlHelper::computeReachabilityRewards(Environment const& env, storm::solver::SolveGoal&&, + storm::storage::SparseMatrix const&, + storm::storage::SparseMatrix const&, + storm::models::sparse::StandardRewardModel const&, bool, + storm::storage::BitVector const&, bool) { + STORM_LOG_THROW(false, storm::exceptions::IllegalFunctionCallException, "Computing reachability rewards is unsupported for this data type."); +} + struct QualitativeStateSetsReachabilityRewards { storm::storage::BitVector maybeStates; storm::storage::BitVector infinityStates; @@ -1332,7 +1383,7 @@ template void computeUpperRewardBounds(SparseMdpHintType& hintInformation, storm::OptimizationDirection const& direction, storm::storage::SparseMatrix const& submatrix, std::vector const& choiceRewards, std::vector const& oneStepTargetProbabilities) { - if constexpr (std::is_same_v) { + if constexpr (storm::IsIntervalType) { STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "We do not support computing upper reward bounds with interval models."); } else { // For the min-case, we use DS-MPI, for the max-case variant 2 of the Baier et al. paper (CAV'17). @@ -1371,7 +1422,7 @@ MDPSparseModelCheckingHelperReturnType SparseMdpPrctlHelper> scheduler; if (produceScheduler) { - if constexpr (std::is_same_v) { + if constexpr (storm::IsIntervalType) { STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "We do not support producing schedulers in this function with interval models."); } else { scheduler = std::make_unique>(transitionMatrix.getRowGroupCount()); @@ -1416,7 +1467,7 @@ MDPSparseModelCheckingHelperReturnType SparseMdpPrctlHelper> ecInformation; if (hintInformation.getEliminateEndComponents()) { - if constexpr (std::is_same_v) { + if constexpr (storm::IsIntervalType) { STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "We do not support eliminating end components with interval models."); } else { ecInformation = computeFixedPointSystemReachabilityRewardsEliminateEndComponents( @@ -1441,7 +1492,7 @@ MDPSparseModelCheckingHelperReturnType SparseMdpPrctlHelper) { + if constexpr (storm::IsIntervalType) { STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "We do not support eliminating end components with interval models."); } else { ecInformation.get().setValues(result, qualitativeStateSets.maybeStates, resultForMaybeStates.getValues()); @@ -1472,13 +1523,186 @@ MDPSparseModelCheckingHelperReturnType SparseMdpPrctlHelperisDeterministicScheduler(), "Expected a deterministic scheduler"); STORM_LOG_ASSERT((!produceScheduler && !scheduler) || scheduler->isMemorylessScheduler(), "Expected a memoryless scheduler"); - if constexpr (std::is_same_v) { + if constexpr (storm::IsIntervalType) { return MDPSparseModelCheckingHelperReturnType(std::move(result)); } else { return MDPSparseModelCheckingHelperReturnType(std::move(result), std::move(scheduler)); } } +template +std::unique_ptr SparseMdpPrctlHelper::computeConditionalProbabilities( + Environment const& env, storm::solver::SolveGoal&& goal, storm::storage::SparseMatrix const& transitionMatrix, + storm::storage::SparseMatrix const& backwardTransitions, storm::storage::BitVector const& targetStates, + storm::storage::BitVector const& conditionStates) { + if constexpr (storm::IsIntervalType) { + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "We do not support computing conditional probabilities with interval models."); + } else { + std::chrono::high_resolution_clock::time_point start = std::chrono::high_resolution_clock::now(); + + // For the max-case, we can simply take the given target states. For the min-case, however, we need to + // find the MECs of non-target states and make them the new target states. + storm::storage::BitVector fixedTargetStates; + if (!goal.minimize()) { + fixedTargetStates = targetStates; + } else { + fixedTargetStates = storm::storage::BitVector(targetStates.size()); + storm::storage::MaximalEndComponentDecomposition mecDecomposition(transitionMatrix, backwardTransitions, ~targetStates); + for (auto const& mec : mecDecomposition) { + for (auto const& stateActionsPair : mec) { + fixedTargetStates.set(stateActionsPair.first); + } + } + } + + storm::storage::BitVector allStates(fixedTargetStates.size(), true); + + // Extend the target states by computing all states that have probability 1 to go to a target state + // under *all* schedulers. + fixedTargetStates = + storm::utility::graph::performProb1A(transitionMatrix, transitionMatrix.getRowGroupIndices(), backwardTransitions, allStates, fixedTargetStates); + + // We solve the max-case and later adjust the result if the optimization direction was to minimize. + storm::storage::BitVector initialStatesBitVector = goal.relevantValues(); + STORM_LOG_THROW(initialStatesBitVector.getNumberOfSetBits() == 1, storm::exceptions::NotSupportedException, + "Computing conditional probabilities in MDPs is only supported for models with exactly one initial state."); + storm::storage::sparse::state_type initialState = *initialStatesBitVector.begin(); + + // Extend the condition states by computing all states that have probability 1 to go to a condition state + // under *all* schedulers. + storm::storage::BitVector extendedConditionStates = + storm::utility::graph::performProb1A(transitionMatrix, transitionMatrix.getRowGroupIndices(), backwardTransitions, allStates, conditionStates); + + STORM_LOG_DEBUG("Computing probabilities to satisfy condition."); + std::chrono::high_resolution_clock::time_point conditionStart = std::chrono::high_resolution_clock::now(); + std::vector conditionProbabilities = + std::move(computeUntilProbabilities(env, OptimizationDirection::Maximize, transitionMatrix, backwardTransitions, allStates, extendedConditionStates, + false, false) + .values); + std::chrono::high_resolution_clock::time_point conditionEnd = std::chrono::high_resolution_clock::now(); + STORM_LOG_DEBUG("Computed probabilities to satisfy for condition in " + << std::chrono::duration_cast(conditionEnd - conditionStart).count() << "ms."); + + // If the conditional probability is undefined for the initial state, we return directly. + if (storm::utility::isZero(conditionProbabilities[initialState])) { + return std::unique_ptr(new ExplicitQuantitativeCheckResult(initialState, storm::utility::infinity())); + } + + STORM_LOG_DEBUG("Computing probabilities to reach target."); + std::chrono::high_resolution_clock::time_point targetStart = std::chrono::high_resolution_clock::now(); + std::vector targetProbabilities = std::move( + computeUntilProbabilities(env, OptimizationDirection::Maximize, transitionMatrix, backwardTransitions, allStates, fixedTargetStates, false, false) + .values); + std::chrono::high_resolution_clock::time_point targetEnd = std::chrono::high_resolution_clock::now(); + STORM_LOG_DEBUG("Computed probabilities to reach target in " << std::chrono::duration_cast(targetEnd - targetStart).count() + << "ms."); + + storm::storage::BitVector statesWithProbabilityGreater0E(transitionMatrix.getRowGroupCount(), true); + storm::storage::sparse::state_type state = 0; + for (auto const& element : conditionProbabilities) { + if (storm::utility::isZero(element)) { + statesWithProbabilityGreater0E.set(state, false); + } + ++state; + } + + // Determine those states that need to be equipped with a restart mechanism. + STORM_LOG_DEBUG("Computing problematic states."); + storm::storage::BitVector pureResetStates = storm::utility::graph::performProb0A(backwardTransitions, allStates, extendedConditionStates); + storm::storage::BitVector problematicStates = storm::utility::graph::performProb0E( + transitionMatrix, transitionMatrix.getRowGroupIndices(), backwardTransitions, allStates, extendedConditionStates | fixedTargetStates); + + // Otherwise, we build the transformed MDP. + storm::storage::BitVector relevantStates = storm::utility::graph::getReachableStates(transitionMatrix, initialStatesBitVector, allStates, + extendedConditionStates | fixedTargetStates | pureResetStates); + STORM_LOG_TRACE("Found " << relevantStates.getNumberOfSetBits() << " relevant states for conditional probability computation."); + std::vector numberOfStatesBeforeRelevantStates = relevantStates.getNumberOfSetBitsBeforeIndices(); + storm::storage::sparse::state_type newGoalState = relevantStates.getNumberOfSetBits(); + storm::storage::sparse::state_type newStopState = newGoalState + 1; + storm::storage::sparse::state_type newFailState = newStopState + 1; + + // Build the transitions of the (relevant) states of the original model. + storm::storage::SparseMatrixBuilder builder(0, newFailState + 1, 0, true, true); + uint_fast64_t currentRow = 0; + for (auto state : relevantStates) { + builder.newRowGroup(currentRow); + if (fixedTargetStates.get(state)) { + if (!storm::utility::isZero(conditionProbabilities[state])) { + builder.addNextValue(currentRow, newGoalState, conditionProbabilities[state]); + } + if (!storm::utility::isOne(conditionProbabilities[state])) { + builder.addNextValue(currentRow, newFailState, storm::utility::one() - conditionProbabilities[state]); + } + ++currentRow; + } else if (extendedConditionStates.get(state)) { + if (!storm::utility::isZero(targetProbabilities[state])) { + builder.addNextValue(currentRow, newGoalState, targetProbabilities[state]); + } + if (!storm::utility::isOne(targetProbabilities[state])) { + builder.addNextValue(currentRow, newStopState, storm::utility::one() - targetProbabilities[state]); + } + ++currentRow; + } else if (pureResetStates.get(state)) { + builder.addNextValue(currentRow, numberOfStatesBeforeRelevantStates[initialState], storm::utility::one()); + ++currentRow; + } else { + for (uint_fast64_t row = transitionMatrix.getRowGroupIndices()[state]; row < transitionMatrix.getRowGroupIndices()[state + 1]; ++row) { + for (auto const& successorEntry : transitionMatrix.getRow(row)) { + builder.addNextValue(currentRow, numberOfStatesBeforeRelevantStates[successorEntry.getColumn()], successorEntry.getValue()); + } + ++currentRow; + } + if (problematicStates.get(state)) { + builder.addNextValue(currentRow, numberOfStatesBeforeRelevantStates[initialState], storm::utility::one()); + ++currentRow; + } + } + } + + // Now build the transitions of the newly introduced states. + builder.newRowGroup(currentRow); + builder.addNextValue(currentRow, newGoalState, storm::utility::one()); + ++currentRow; + builder.newRowGroup(currentRow); + builder.addNextValue(currentRow, newStopState, storm::utility::one()); + ++currentRow; + builder.newRowGroup(currentRow); + builder.addNextValue(currentRow, numberOfStatesBeforeRelevantStates[initialState], storm::utility::one()); + ++currentRow; + + std::chrono::high_resolution_clock::time_point end = std::chrono::high_resolution_clock::now(); + STORM_LOG_DEBUG("Computed transformed model in " << std::chrono::duration_cast(end - start).count() << "ms."); + + // Finally, build the matrix and dispatch the query as a reachability query. + STORM_LOG_DEBUG("Computing conditional probabilties."); + storm::storage::BitVector newGoalStates(newFailState + 1); + newGoalStates.set(newGoalState); + storm::storage::SparseMatrix newTransitionMatrix = builder.build(); + STORM_LOG_DEBUG("Transformed model has " << newTransitionMatrix.getRowGroupCount() << " states and " << newTransitionMatrix.getNonzeroEntryCount() + << " transitions."); + storm::storage::SparseMatrix newBackwardTransitions = newTransitionMatrix.transpose(true); + + storm::solver::OptimizationDirection dir = goal.direction(); + if (goal.minimize()) { + goal.oneMinus(); + } + + std::chrono::high_resolution_clock::time_point conditionalStart = std::chrono::high_resolution_clock::now(); + std::vector goalProbabilities = + std::move(computeUntilProbabilities(env, std::move(goal), newTransitionMatrix, newBackwardTransitions, + storm::storage::BitVector(newFailState + 1, true), newGoalStates, false, false) + .values); + std::chrono::high_resolution_clock::time_point conditionalEnd = std::chrono::high_resolution_clock::now(); + STORM_LOG_DEBUG("Computed conditional probabilities in transformed model in " + << std::chrono::duration_cast(conditionalEnd - conditionalStart).count() << "ms."); + + return std::unique_ptr(new ExplicitQuantitativeCheckResult( + initialState, dir == OptimizationDirection::Maximize + ? goalProbabilities[numberOfStatesBeforeRelevantStates[initialState]] + : storm::utility::one() - goalProbabilities[numberOfStatesBeforeRelevantStates[initialState]])); + } +} + template class SparseMdpPrctlHelper; template std::vector SparseMdpPrctlHelper::computeInstantaneousRewards(Environment const& env, storm::solver::SolveGoal&& goal, storm::storage::SparseMatrix const& transitionMatrix, @@ -1546,6 +1770,30 @@ template MDPSparseModelCheckingHelperReturnType SparseMdpPrctlHelper const& backwardTransitions, storm::models::sparse::StandardRewardModel const& rewardModel, bool qualitative, bool produceScheduler, ModelCheckerHint const& hint); +template class SparseMdpPrctlHelper; +template std::vector SparseMdpPrctlHelper::computeInstantaneousRewards( + Environment const& env, storm::solver::SolveGoal&& goal, + storm::storage::SparseMatrix const& transitionMatrix, + storm::models::sparse::StandardRewardModel const& rewardModel, uint_fast64_t stepCount); +template std::vector SparseMdpPrctlHelper::computeCumulativeRewards( + Environment const& env, storm::solver::SolveGoal&& goal, + storm::storage::SparseMatrix const& transitionMatrix, + storm::models::sparse::StandardRewardModel const& rewardModel, uint_fast64_t stepBound); +template MDPSparseModelCheckingHelperReturnType +SparseMdpPrctlHelper::computeReachabilityRewards( + Environment const& env, storm::solver::SolveGoal&& goal, + storm::storage::SparseMatrix const& transitionMatrix, + storm::storage::SparseMatrix const& backwardTransitions, + storm::models::sparse::StandardRewardModel const& rewardModel, storm::storage::BitVector const& targetStates, bool qualitative, + bool produceScheduler, ModelCheckerHint const& hint); +template MDPSparseModelCheckingHelperReturnType +SparseMdpPrctlHelper::computeTotalRewards( + Environment const& env, storm::solver::SolveGoal&& goal, + storm::storage::SparseMatrix const& transitionMatrix, + storm::storage::SparseMatrix const& backwardTransitions, + storm::models::sparse::StandardRewardModel const& rewardModel, bool qualitative, bool produceScheduler, + ModelCheckerHint const& hint); + } // namespace helper } // namespace modelchecker } // namespace storm diff --git a/src/storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.h b/src/storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.h index eb9576b0da..73422caca1 100644 --- a/src/storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.h +++ b/src/storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.h @@ -98,6 +98,18 @@ class SparseMdpPrctlHelper { storm::models::sparse::StandardRewardModel const& intervalRewardModel, bool lowerBoundOfIntervals, storm::storage::BitVector const& targetStates, bool qualitative); + static std::vector computeReachabilityRewards(Environment const& env, storm::solver::SolveGoal&& goal, + storm::storage::SparseMatrix const& transitionMatrix, + storm::storage::SparseMatrix const& backwardTransitions, + storm::models::sparse::StandardRewardModel const& intervalRewardModel, + bool lowerBoundOfIntervals, storm::storage::BitVector const& targetStates, bool qualitative); + + static std::unique_ptr computeConditionalProbabilities(Environment const& env, storm::solver::SolveGoal&& goal, + storm::storage::SparseMatrix const& transitionMatrix, + storm::storage::SparseMatrix const& backwardTransitions, + storm::storage::BitVector const& targetStates, + storm::storage::BitVector const& conditionStates); + private: static MDPSparseModelCheckingHelperReturnType computeReachabilityRewardsHelper( Environment const& env, storm::solver::SolveGoal&& goal, storm::storage::SparseMatrix const& transitionMatrix, diff --git a/src/storm/modelchecker/prctl/helper/rewardbounded/MultiDimensionalRewardUnfolding.cpp b/src/storm/modelchecker/prctl/helper/rewardbounded/MultiDimensionalRewardUnfolding.cpp index 6f3533e6a8..552122bbc2 100644 --- a/src/storm/modelchecker/prctl/helper/rewardbounded/MultiDimensionalRewardUnfolding.cpp +++ b/src/storm/modelchecker/prctl/helper/rewardbounded/MultiDimensionalRewardUnfolding.cpp @@ -964,6 +964,8 @@ template class MultiDimensionalRewardUnfolding; template class MultiDimensionalRewardUnfolding; template class MultiDimensionalRewardUnfolding; template class MultiDimensionalRewardUnfolding; +// template class MultiDimensionalRewardUnfolding; +// template class MultiDimensionalRewardUnfolding; } // namespace rewardbounded } // namespace helper } // namespace modelchecker diff --git a/src/storm/modelchecker/prctl/helper/rewardbounded/ProductModel.cpp b/src/storm/modelchecker/prctl/helper/rewardbounded/ProductModel.cpp index 7bb836cc88..ccc196db4e 100644 --- a/src/storm/modelchecker/prctl/helper/rewardbounded/ProductModel.cpp +++ b/src/storm/modelchecker/prctl/helper/rewardbounded/ProductModel.cpp @@ -132,9 +132,12 @@ storm::storage::MemoryStructure ProductModel::computeMemoryStructure( for (auto dim : objectiveDimensions[objIndex]) { auto const& dimension = dimensions[dim]; STORM_LOG_ASSERT(dimension.formula->isBoundedUntilFormula(), "Unexpected Formula type"); - constraintStates &= - (mc.check(dimension.formula->asBoundedUntilFormula().getLeftSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector() | - mc.check(dimension.formula->asBoundedUntilFormula().getRightSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector()); + constraintStates &= (mc.check(dimension.formula->asBoundedUntilFormula().getLeftSubformula()) + ->template asExplicitQualitativeCheckResult() + .getTruthValuesVector() | + mc.check(dimension.formula->asBoundedUntilFormula().getRightSubformula()) + ->template asExplicitQualitativeCheckResult() + .getTruthValuesVector()); } // Build the transitions between the memory states @@ -155,7 +158,8 @@ storm::storage::MemoryStructure ProductModel::computeMemoryStructure( storm::logic::BinaryBooleanStateFormula::OperatorType::And, transitionFormula, subObjFormula); } - storm::storage::BitVector transitionStates = mc.check(*transitionFormula)->asExplicitQualitativeCheckResult().getTruthValuesVector(); + storm::storage::BitVector transitionStates = + mc.check(*transitionFormula)->template asExplicitQualitativeCheckResult().getTruthValuesVector(); if (memStatePrimeBV.empty()) { transitionStates |= ~constraintStates; } else { @@ -426,9 +430,11 @@ std::vector> ProductModel::computeObjectiveRew } } - storm::storage::BitVector relevantStates = mc.check(*relevantStatesFormula)->asExplicitQualitativeCheckResult().getTruthValuesVector(); + storm::storage::BitVector relevantStates = + mc.check(*relevantStatesFormula)->template asExplicitQualitativeCheckResult().getTruthValuesVector(); storm::storage::BitVector relevantChoices = getProduct().getTransitionMatrix().getRowFilter(relevantStates); - storm::storage::BitVector goalStates = mc.check(*goalStatesFormula)->asExplicitQualitativeCheckResult().getTruthValuesVector(); + storm::storage::BitVector goalStates = + mc.check(*goalStatesFormula)->template asExplicitQualitativeCheckResult().getTruthValuesVector(); for (auto choice : relevantChoices) { objRew[choice] += getProduct().getTransitionMatrix().getConstrainedRowSum(choice, goalStates); } diff --git a/src/storm/modelchecker/propositional/SparsePropositionalModelChecker.cpp b/src/storm/modelchecker/propositional/SparsePropositionalModelChecker.cpp index 10428e033e..120fac31db 100644 --- a/src/storm/modelchecker/propositional/SparsePropositionalModelChecker.cpp +++ b/src/storm/modelchecker/propositional/SparsePropositionalModelChecker.cpp @@ -33,9 +33,9 @@ std::unique_ptr SparsePropositionalModelChecker::c Environment const& env, CheckTask const& checkTask) { storm::logic::BooleanLiteralFormula const& stateFormula = checkTask.getFormula(); if (stateFormula.isTrueFormula()) { - return std::unique_ptr(new ExplicitQualitativeCheckResult(storm::storage::BitVector(model.getNumberOfStates(), true))); + return std::unique_ptr(new ExplicitQualitativeCheckResult(storm::storage::BitVector(model.getNumberOfStates(), true))); } else { - return std::unique_ptr(new ExplicitQualitativeCheckResult(storm::storage::BitVector(model.getNumberOfStates()))); + return std::unique_ptr(new ExplicitQualitativeCheckResult(storm::storage::BitVector(model.getNumberOfStates()))); } } @@ -45,7 +45,7 @@ std::unique_ptr SparsePropositionalModelChecker::c storm::logic::AtomicLabelFormula const& stateFormula = checkTask.getFormula(); STORM_LOG_THROW(model.hasLabel(stateFormula.getLabel()), storm::exceptions::InvalidPropertyException, "The property refers to unknown label '" << stateFormula.getLabel() << "'."); - return std::unique_ptr(new ExplicitQualitativeCheckResult(model.getStates(stateFormula.getLabel()))); + return std::unique_ptr(new ExplicitQualitativeCheckResult(model.getStates(stateFormula.getLabel()))); } template @@ -65,6 +65,11 @@ template class SparsePropositionalModelChecker>>; template class SparsePropositionalModelChecker>>; +template class SparsePropositionalModelChecker< + storm::models::sparse::Mdp>>; +template class SparsePropositionalModelChecker< + storm::models::sparse::Smg>>; + template class SparsePropositionalModelChecker>; template class SparsePropositionalModelChecker>; template class SparsePropositionalModelChecker>; @@ -82,5 +87,8 @@ template class SparsePropositionalModelChecker>; template class SparsePropositionalModelChecker>; + +template class SparsePropositionalModelChecker>; +template class SparsePropositionalModelChecker>; } // namespace modelchecker } // namespace storm diff --git a/src/storm/modelchecker/propositional/SparsePropositionalModelChecker.h b/src/storm/modelchecker/propositional/SparsePropositionalModelChecker.h index 52a6c3c137..7de1792319 100644 --- a/src/storm/modelchecker/propositional/SparsePropositionalModelChecker.h +++ b/src/storm/modelchecker/propositional/SparsePropositionalModelChecker.h @@ -8,10 +8,28 @@ namespace modelchecker { template class SparsePropositionalModelChecker : public AbstractModelChecker { + private: + // Due to a GCC bug we have to add this dummy template type here + // https://stackoverflow.com/questions/49707184/explicit-specialization-in-non-namespace-scope-does-not-compile-in-gcc + template + struct GetSolutionType { + using type = T; + }; + + template + struct GetSolutionType { + using type = double; + }; + + template + struct GetSolutionType { + using type = storm::RationalNumber; + }; + public: typedef typename SparseModelType::ValueType ValueType; typedef typename SparseModelType::RewardModelType RewardModelType; - using SolutionType = typename std::conditional, double, ValueType>::type; + using SolutionType = typename GetSolutionType::type; explicit SparsePropositionalModelChecker(SparseModelType const& model); diff --git a/src/storm/modelchecker/reachability/SparseDtmcEliminationModelChecker.cpp b/src/storm/modelchecker/reachability/SparseDtmcEliminationModelChecker.cpp index b0512229c6..2c9c2aeb2b 100644 --- a/src/storm/modelchecker/reachability/SparseDtmcEliminationModelChecker.cpp +++ b/src/storm/modelchecker/reachability/SparseDtmcEliminationModelChecker.cpp @@ -52,7 +52,7 @@ std::unique_ptr SparseDtmcEliminationModelChecker const& checkTask) { storm::logic::StateFormula const& stateFormula = checkTask.getFormula(); std::unique_ptr subResultPointer = this->check(stateFormula); - storm::storage::BitVector const& psiStates = subResultPointer->asExplicitQualitativeCheckResult().getTruthValuesVector(); + storm::storage::BitVector const& psiStates = subResultPointer->template asExplicitQualitativeCheckResult().getTruthValuesVector(); storm::storage::SparseMatrix const& transitionMatrix = this->getModel().getTransitionMatrix(); uint_fast64_t numberOfStates = transitionMatrix.getRowCount(); @@ -109,7 +109,7 @@ std::unique_ptr SparseDtmcEliminationModelCheckerfilter(ExplicitQualitativeCheckResult(initialStates)); + checkResult->filter(ExplicitQualitativeCheckResult(initialStates)); } return checkResult; } @@ -174,7 +174,7 @@ std::unique_ptr SparseDtmcEliminationModelCheckerfilter(ExplicitQualitativeCheckResult(initialStates)); + checkResult->filter(ExplicitQualitativeCheckResult(initialStates)); } return checkResult; } @@ -347,8 +347,8 @@ std::unique_ptr SparseDtmcEliminationModelChecker leftResultPointer = this->check(pathFormula.getLeftSubformula()); std::unique_ptr rightResultPointer = this->check(pathFormula.getRightSubformula()); - storm::storage::BitVector const& phiStates = leftResultPointer->asExplicitQualitativeCheckResult().getTruthValuesVector(); - storm::storage::BitVector const& psiStates = rightResultPointer->asExplicitQualitativeCheckResult().getTruthValuesVector(); + storm::storage::BitVector const& phiStates = leftResultPointer->template asExplicitQualitativeCheckResult().getTruthValuesVector(); + storm::storage::BitVector const& psiStates = rightResultPointer->template asExplicitQualitativeCheckResult().getTruthValuesVector(); // Start by determining the states that have a non-zero probability of reaching the target states within the // time bound. @@ -441,7 +441,7 @@ std::unique_ptr SparseDtmcEliminationModelCheckerfilter(ExplicitQualitativeCheckResult(this->getModel().getInitialStates() | psiStates)); + checkResult->filter(ExplicitQualitativeCheckResult(this->getModel().getInitialStates() | psiStates)); } return checkResult; } @@ -454,8 +454,8 @@ std::unique_ptr SparseDtmcEliminationModelChecker leftResultPointer = this->check(pathFormula.getLeftSubformula()); std::unique_ptr rightResultPointer = this->check(pathFormula.getRightSubformula()); - storm::storage::BitVector const& phiStates = leftResultPointer->asExplicitQualitativeCheckResult().getTruthValuesVector(); - storm::storage::BitVector const& psiStates = rightResultPointer->asExplicitQualitativeCheckResult().getTruthValuesVector(); + storm::storage::BitVector const& phiStates = leftResultPointer->template asExplicitQualitativeCheckResult().getTruthValuesVector(); + storm::storage::BitVector const& psiStates = rightResultPointer->template asExplicitQualitativeCheckResult().getTruthValuesVector(); return computeUntilProbabilities(this->getModel().getTransitionMatrix(), this->getModel().getBackwardTransitions(), this->getModel().getInitialStates(), phiStates, psiStates, checkTask.isOnlyInitialStatesRelevantSet()); @@ -535,7 +535,7 @@ std::unique_ptr SparseDtmcEliminationModelChecker subResultPointer = this->check(eventuallyFormula.getSubformula()); storm::storage::BitVector trueStates(this->getModel().getNumberOfStates(), true); - storm::storage::BitVector const& targetStates = subResultPointer->asExplicitQualitativeCheckResult().getTruthValuesVector(); + storm::storage::BitVector const& targetStates = subResultPointer->template asExplicitQualitativeCheckResult().getTruthValuesVector(); // Do some sanity checks to establish some required properties. RewardModelType const& rewardModel = this->getModel().getRewardModel(checkTask.isRewardModelSet() ? checkTask.getRewardModel() : ""); @@ -647,8 +647,8 @@ std::unique_ptr SparseDtmcEliminationModelChecker leftResultPointer = this->check(conditionalFormula.getSubformula().asEventuallyFormula().getSubformula()); std::unique_ptr rightResultPointer = this->check(conditionalFormula.getConditionFormula().asEventuallyFormula().getSubformula()); - storm::storage::BitVector phiStates = leftResultPointer->asExplicitQualitativeCheckResult().getTruthValuesVector(); - storm::storage::BitVector psiStates = rightResultPointer->asExplicitQualitativeCheckResult().getTruthValuesVector(); + storm::storage::BitVector phiStates = leftResultPointer->template asExplicitQualitativeCheckResult().getTruthValuesVector(); + storm::storage::BitVector psiStates = rightResultPointer->template asExplicitQualitativeCheckResult().getTruthValuesVector(); storm::storage::BitVector trueStates(this->getModel().getNumberOfStates(), true); // Do some sanity checks to establish some required properties. diff --git a/src/storm/modelchecker/results/CheckResult.cpp b/src/storm/modelchecker/results/CheckResult.cpp index 5ff97293d3..f6c6827c0b 100644 --- a/src/storm/modelchecker/results/CheckResult.cpp +++ b/src/storm/modelchecker/results/CheckResult.cpp @@ -1,5 +1,6 @@ #include "storm/modelchecker/results/CheckResult.h" +#include "storm/adapters/IntervalAdapter.h" #include "storm/adapters/RationalFunctionAdapter.h" #include "storm/modelchecker/results/ExplicitParetoCurveCheckResult.h" #include "storm/modelchecker/results/ExplicitQualitativeCheckResult.h" @@ -77,12 +78,14 @@ bool CheckResult::isHybridQuantitativeCheckResult() const { return false; } -ExplicitQualitativeCheckResult& CheckResult::asExplicitQualitativeCheckResult() { - return dynamic_cast(*this); +template +ExplicitQualitativeCheckResult& CheckResult::asExplicitQualitativeCheckResult() { + return dynamic_cast&>(*this); } -ExplicitQualitativeCheckResult const& CheckResult::asExplicitQualitativeCheckResult() const { - return dynamic_cast(*this); +template +ExplicitQualitativeCheckResult const& CheckResult::asExplicitQualitativeCheckResult() const { + return dynamic_cast const&>(*this); } template @@ -183,6 +186,8 @@ template QuantitativeCheckResult const& CheckResult::asQuantitativeCheck template ExplicitQuantitativeCheckResult& CheckResult::asExplicitQuantitativeCheckResult(); template ExplicitQuantitativeCheckResult const& CheckResult::asExplicitQuantitativeCheckResult() const; +template ExplicitQualitativeCheckResult& CheckResult::asExplicitQualitativeCheckResult(); +template ExplicitQualitativeCheckResult const& CheckResult::asExplicitQualitativeCheckResult() const; template ExplicitParetoCurveCheckResult& CheckResult::asExplicitParetoCurveCheckResult(); template ExplicitParetoCurveCheckResult const& CheckResult::asExplicitParetoCurveCheckResult() const; template LexicographicCheckResult& CheckResult::asLexicographicCheckResult(); @@ -215,6 +220,8 @@ template QuantitativeCheckResult const& CheckResult::asQu template ExplicitQuantitativeCheckResult& CheckResult::asExplicitQuantitativeCheckResult(); template ExplicitQuantitativeCheckResult const& CheckResult::asExplicitQuantitativeCheckResult() const; +template ExplicitQualitativeCheckResult& CheckResult::asExplicitQualitativeCheckResult(); +template ExplicitQualitativeCheckResult const& CheckResult::asExplicitQualitativeCheckResult() const; template QuantitativeCheckResult& CheckResult::asQuantitativeCheckResult(); template QuantitativeCheckResult const& CheckResult::asQuantitativeCheckResult() const; @@ -222,11 +229,18 @@ template QuantitativeCheckResult const& CheckResult::as template ExplicitQuantitativeCheckResult& CheckResult::asExplicitQuantitativeCheckResult(); template ExplicitQuantitativeCheckResult const& CheckResult::asExplicitQuantitativeCheckResult() const; +template ExplicitQualitativeCheckResult& CheckResult::asExplicitQualitativeCheckResult(); +template ExplicitQualitativeCheckResult const& CheckResult::asExplicitQualitativeCheckResult() const; + template ExplicitParetoCurveCheckResult& CheckResult::asExplicitParetoCurveCheckResult(); template ExplicitParetoCurveCheckResult const& CheckResult::asExplicitParetoCurveCheckResult() const; template LexicographicCheckResult& CheckResult::asLexicographicCheckResult(); template LexicographicCheckResult const& CheckResult::asLexicographicCheckResult() const; +// Instantiation for storm::Interval (carl::Interval) +template ExplicitQualitativeCheckResult& CheckResult::asExplicitQualitativeCheckResult(); +template ExplicitQualitativeCheckResult const& CheckResult::asExplicitQualitativeCheckResult() const; + } // namespace modelchecker } // namespace storm diff --git a/src/storm/modelchecker/results/CheckResult.h b/src/storm/modelchecker/results/CheckResult.h index f46e1abcf9..c8ba918329 100644 --- a/src/storm/modelchecker/results/CheckResult.h +++ b/src/storm/modelchecker/results/CheckResult.h @@ -13,6 +13,7 @@ namespace modelchecker { class QualitativeCheckResult; template class QuantitativeCheckResult; +template class ExplicitQualitativeCheckResult; template @@ -77,8 +78,11 @@ class CheckResult { template QuantitativeCheckResult const& asQuantitativeCheckResult() const; - ExplicitQualitativeCheckResult& asExplicitQualitativeCheckResult(); - ExplicitQualitativeCheckResult const& asExplicitQualitativeCheckResult() const; + template + ExplicitQualitativeCheckResult& asExplicitQualitativeCheckResult(); + + template + ExplicitQualitativeCheckResult const& asExplicitQualitativeCheckResult() const; template ExplicitQuantitativeCheckResult& asExplicitQuantitativeCheckResult(); diff --git a/src/storm/modelchecker/results/ExplicitParetoCurveCheckResult.cpp b/src/storm/modelchecker/results/ExplicitParetoCurveCheckResult.cpp index c5be5d612b..5578618d5f 100644 --- a/src/storm/modelchecker/results/ExplicitParetoCurveCheckResult.cpp +++ b/src/storm/modelchecker/results/ExplicitParetoCurveCheckResult.cpp @@ -53,8 +53,8 @@ void ExplicitParetoCurveCheckResult::filter(QualitativeCheckResult co STORM_LOG_THROW(filter.isExplicitQualitativeCheckResult(), storm::exceptions::InvalidOperationException, "Cannot filter explicit check result with non-explicit filter."); STORM_LOG_THROW(filter.isResultForAllStates(), storm::exceptions::InvalidOperationException, "Cannot filter check result with non-complete filter."); - ExplicitQualitativeCheckResult const& explicitFilter = filter.asExplicitQualitativeCheckResult(); - ExplicitQualitativeCheckResult::vector_type const& filterTruthValues = explicitFilter.getTruthValuesVector(); + ExplicitQualitativeCheckResult const& explicitFilter = filter.template asExplicitQualitativeCheckResult(); + typename ExplicitQualitativeCheckResult::vector_type const& filterTruthValues = explicitFilter.getTruthValuesVector(); STORM_LOG_THROW(filterTruthValues.getNumberOfSetBits() == 1 && filterTruthValues.get(state), storm::exceptions::InvalidOperationException, "The check result fails to contain some results referred to by the filter."); diff --git a/src/storm/modelchecker/results/ExplicitQualitativeCheckResult.cpp b/src/storm/modelchecker/results/ExplicitQualitativeCheckResult.cpp index 6c50453991..76d86175b0 100644 --- a/src/storm/modelchecker/results/ExplicitQualitativeCheckResult.cpp +++ b/src/storm/modelchecker/results/ExplicitQualitativeCheckResult.cpp @@ -1,55 +1,74 @@ +#include "storm/adapters/IntervalAdapter.h" #include "storm/adapters/RationalNumberAdapter.h" // Must come first. TODO: fix #include "storm/modelchecker/results/ExplicitQualitativeCheckResult.h" +#include "storm/adapters/IntervalForward.h" #include "storm/adapters/JsonAdapter.h" +#include "storm/adapters/RationalFunctionAdapter.h" #include "storm/exceptions/InvalidOperationException.h" #include "storm/utility/macros.h" namespace storm { namespace modelchecker { -ExplicitQualitativeCheckResult::ExplicitQualitativeCheckResult() : truthValues(map_type()) { + +template +ExplicitQualitativeCheckResult::ExplicitQualitativeCheckResult() : truthValues(map_type()) { // Intentionally left empty. } -ExplicitQualitativeCheckResult::ExplicitQualitativeCheckResult(map_type const& map) : truthValues(map) { +template +ExplicitQualitativeCheckResult::ExplicitQualitativeCheckResult(map_type const& map) : truthValues(map) { // Intentionally left empty. } -ExplicitQualitativeCheckResult::ExplicitQualitativeCheckResult(map_type&& map) : truthValues(map) { +template +ExplicitQualitativeCheckResult::ExplicitQualitativeCheckResult(map_type&& map) : truthValues(map) { // Intentionally left empty. } -ExplicitQualitativeCheckResult::ExplicitQualitativeCheckResult(storm::storage::sparse::state_type state, bool value) : truthValues(map_type()) { +template +ExplicitQualitativeCheckResult::ExplicitQualitativeCheckResult(storm::storage::sparse::state_type state, bool value) : truthValues(map_type()) { boost::get(truthValues)[state] = value; } -ExplicitQualitativeCheckResult::ExplicitQualitativeCheckResult(storm::storage::BitVector const& truthValues) : truthValues(truthValues) { +template +ExplicitQualitativeCheckResult::ExplicitQualitativeCheckResult(storm::storage::BitVector const& truthValues) : truthValues(truthValues) { // Intentionally left empty. } -ExplicitQualitativeCheckResult::ExplicitQualitativeCheckResult(storm::storage::BitVector&& truthValues) : truthValues(std::move(truthValues)) { +template +ExplicitQualitativeCheckResult::ExplicitQualitativeCheckResult(storm::storage::BitVector&& truthValues) : truthValues(std::move(truthValues)) { // Intentionally left empty. } -ExplicitQualitativeCheckResult::ExplicitQualitativeCheckResult(boost::variant const& truthValues) : truthValues(truthValues) { +template +ExplicitQualitativeCheckResult::ExplicitQualitativeCheckResult(boost::variant const& truthValues, + std::optional>> scheduler) + : truthValues(truthValues), scheduler(scheduler) { // Intentionally left empty. } -ExplicitQualitativeCheckResult::ExplicitQualitativeCheckResult(boost::variant&& truthValues) : truthValues(std::move(truthValues)) { +template +ExplicitQualitativeCheckResult::ExplicitQualitativeCheckResult(boost::variant&& truthValues, + std::optional>> scheduler) + : truthValues(std::move(truthValues)), scheduler(scheduler) { // Intentionally left empty. } -std::unique_ptr ExplicitQualitativeCheckResult::clone() const { - return std::make_unique(this->truthValues); +template +std::unique_ptr ExplicitQualitativeCheckResult::clone() const { + return std::make_unique>(this->truthValues); } -void ExplicitQualitativeCheckResult::performLogicalOperation(ExplicitQualitativeCheckResult& first, QualitativeCheckResult const& second, bool logicalAnd) { +template +void ExplicitQualitativeCheckResult::performLogicalOperation(ExplicitQualitativeCheckResult& first, QualitativeCheckResult const& second, + bool logicalAnd) { STORM_LOG_THROW(second.isExplicitQualitativeCheckResult(), storm::exceptions::InvalidOperationException, "Cannot perform logical 'and' on check results of incompatible type."); STORM_LOG_THROW(first.isResultForAllStates() == second.isResultForAllStates(), storm::exceptions::InvalidOperationException, "Cannot perform logical 'and' on check results of incompatible type."); - ExplicitQualitativeCheckResult const& secondCheckResult = static_cast(second); + ExplicitQualitativeCheckResult const& secondCheckResult = static_cast const&>(second); if (first.isResultForAllStates()) { if (logicalAnd) { boost::get(first.truthValues) &= boost::get(secondCheckResult.truthValues); @@ -78,17 +97,20 @@ void ExplicitQualitativeCheckResult::performLogicalOperation(ExplicitQualitative } } -QualitativeCheckResult& ExplicitQualitativeCheckResult::operator&=(QualitativeCheckResult const& other) { +template +QualitativeCheckResult& ExplicitQualitativeCheckResult::operator&=(QualitativeCheckResult const& other) { performLogicalOperation(*this, other, true); return *this; } -QualitativeCheckResult& ExplicitQualitativeCheckResult::operator|=(QualitativeCheckResult const& other) { +template +QualitativeCheckResult& ExplicitQualitativeCheckResult::operator|=(QualitativeCheckResult const& other) { performLogicalOperation(*this, other, false); return *this; } -bool ExplicitQualitativeCheckResult::existsTrue() const { +template +bool ExplicitQualitativeCheckResult::existsTrue() const { if (this->isResultForAllStates()) { return !boost::get(truthValues).empty(); } else { @@ -100,7 +122,9 @@ bool ExplicitQualitativeCheckResult::existsTrue() const { return false; } } -bool ExplicitQualitativeCheckResult::forallTrue() const { + +template +bool ExplicitQualitativeCheckResult::forallTrue() const { if (this->isResultForAllStates()) { return boost::get(truthValues).full(); } else { @@ -113,7 +137,8 @@ bool ExplicitQualitativeCheckResult::forallTrue() const { } } -uint64_t ExplicitQualitativeCheckResult::count() const { +template +uint64_t ExplicitQualitativeCheckResult::count() const { if (this->isResultForAllStates()) { return boost::get(truthValues).getNumberOfSetBits(); } else { @@ -127,7 +152,8 @@ uint64_t ExplicitQualitativeCheckResult::count() const { } } -bool ExplicitQualitativeCheckResult::operator[](storm::storage::sparse::state_type state) const { +template +bool ExplicitQualitativeCheckResult::operator[](storm::storage::sparse::state_type state) const { if (this->isResultForAllStates()) { return boost::get(truthValues).get(state); } else { @@ -138,15 +164,18 @@ bool ExplicitQualitativeCheckResult::operator[](storm::storage::sparse::state_ty } } -ExplicitQualitativeCheckResult::vector_type const& ExplicitQualitativeCheckResult::getTruthValuesVector() const { +template +typename ExplicitQualitativeCheckResult::vector_type const& ExplicitQualitativeCheckResult::getTruthValuesVector() const { return boost::get(truthValues); } -ExplicitQualitativeCheckResult::map_type const& ExplicitQualitativeCheckResult::getTruthValuesMap() const { +template +typename ExplicitQualitativeCheckResult::map_type const& ExplicitQualitativeCheckResult::getTruthValuesMap() const { return boost::get(truthValues); } -void ExplicitQualitativeCheckResult::complement() { +template +void ExplicitQualitativeCheckResult::complement() { if (this->isResultForAllStates()) { boost::get(truthValues).complement(); } else { @@ -156,19 +185,23 @@ void ExplicitQualitativeCheckResult::complement() { } } -bool ExplicitQualitativeCheckResult::isExplicit() const { +template +bool ExplicitQualitativeCheckResult::isExplicit() const { return true; } -bool ExplicitQualitativeCheckResult::isResultForAllStates() const { +template +bool ExplicitQualitativeCheckResult::isResultForAllStates() const { return truthValues.which() == 0; } -bool ExplicitQualitativeCheckResult::isExplicitQualitativeCheckResult() const { +template +bool ExplicitQualitativeCheckResult::isExplicitQualitativeCheckResult() const { return true; } -std::ostream& ExplicitQualitativeCheckResult::writeToStream(std::ostream& out) const { +template +std::ostream& ExplicitQualitativeCheckResult::writeToStream(std::ostream& out) const { if (this->isResultForAllStates()) { vector_type const& vector = boost::get(truthValues); bool allTrue = vector.full(); @@ -211,11 +244,12 @@ std::ostream& ExplicitQualitativeCheckResult::writeToStream(std::ostream& out) c return out; } -void ExplicitQualitativeCheckResult::filter(QualitativeCheckResult const& filter) { +template +void ExplicitQualitativeCheckResult::filter(QualitativeCheckResult const& filter) { STORM_LOG_THROW(filter.isExplicitQualitativeCheckResult(), storm::exceptions::InvalidOperationException, "Cannot filter explicit check result with non-explicit filter."); STORM_LOG_THROW(filter.isResultForAllStates(), storm::exceptions::InvalidOperationException, "Cannot filter check result with non-complete filter."); - ExplicitQualitativeCheckResult const& explicitFilter = filter.asExplicitQualitativeCheckResult(); + ExplicitQualitativeCheckResult const& explicitFilter = filter.template asExplicitQualitativeCheckResult(); vector_type const& filterTruthValues = explicitFilter.getTruthValuesVector(); if (this->isResultForAllStates()) { @@ -241,6 +275,28 @@ void ExplicitQualitativeCheckResult::filter(QualitativeCheckResult const& filter } } +template +bool ExplicitQualitativeCheckResult::hasScheduler() const { + return static_cast(scheduler); +} + +template +void ExplicitQualitativeCheckResult::setScheduler(std::unique_ptr>&& scheduler) { + this->scheduler = std::move(scheduler); +} + +template +storm::storage::Scheduler const& ExplicitQualitativeCheckResult::getScheduler() const { + STORM_LOG_THROW(this->hasScheduler(), storm::exceptions::InvalidOperationException, "Unable to retrieve non-existing scheduler."); + return *scheduler.value(); +} + +template +storm::storage::Scheduler& ExplicitQualitativeCheckResult::getScheduler() { + STORM_LOG_THROW(this->hasScheduler(), storm::exceptions::InvalidOperationException, "Unable to retrieve non-existing scheduler."); + return *scheduler.value(); +} + template void insertJsonEntry(storm::json& json, uint64_t const& id, bool value, std::optional const& stateValuations = std::nullopt, @@ -259,9 +315,10 @@ void insertJsonEntry(storm::json& json, uint64_t const& id, bo json.push_back(std::move(entry)); } +template template -storm::json ExplicitQualitativeCheckResult::toJson(std::optional const& stateValuations, - std::optional const& stateLabels) const { +storm::json ExplicitQualitativeCheckResult::toJson(std::optional const& stateValuations, + std::optional const& stateLabels) const { storm::json result; if (this->isResultForAllStates()) { vector_type const& valuesAsVector = boost::get(truthValues); @@ -277,9 +334,30 @@ storm::json ExplicitQualitativeCheckResult::toJson(std::option return result; } -template storm::json ExplicitQualitativeCheckResult::toJson(std::optional const&, - std::optional const&) const; -template storm::json ExplicitQualitativeCheckResult::toJson( +// Explicit template instantiations +template class ExplicitQualitativeCheckResult; +template storm::json ExplicitQualitativeCheckResult::toJson(std::optional const&, + std::optional const&) const; + +template storm::json ExplicitQualitativeCheckResult::toJson( + std::optional const&, std::optional const&) const; + +template class ExplicitQualitativeCheckResult; +template storm::json ExplicitQualitativeCheckResult::toJson( + std::optional const&, std::optional const&) const; +template storm::json ExplicitQualitativeCheckResult::toJson( + std::optional const&, std::optional const&) const; + +template class ExplicitQualitativeCheckResult; +template storm::json ExplicitQualitativeCheckResult::toJson( + std::optional const&, std::optional const&) const; +template storm::json ExplicitQualitativeCheckResult::toJson( + std::optional const&, std::optional const&) const; + +template class ExplicitQualitativeCheckResult; +template storm::json ExplicitQualitativeCheckResult::toJson(std::optional const&, + std::optional const&) const; +template storm::json ExplicitQualitativeCheckResult::toJson( std::optional const&, std::optional const&) const; } // namespace modelchecker diff --git a/src/storm/modelchecker/results/ExplicitQualitativeCheckResult.h b/src/storm/modelchecker/results/ExplicitQualitativeCheckResult.h index 5505f6b3b8..95db1271bb 100644 --- a/src/storm/modelchecker/results/ExplicitQualitativeCheckResult.h +++ b/src/storm/modelchecker/results/ExplicitQualitativeCheckResult.h @@ -8,12 +8,15 @@ #include "storm/modelchecker/results/QualitativeCheckResult.h" #include "storm/models/sparse/StateLabeling.h" #include "storm/storage/BitVector.h" +#include "storm/storage/Scheduler.h" #include "storm/storage/sparse/StateType.h" #include "storm/storage/sparse/StateValuations.h" namespace storm { namespace modelchecker { + +template class ExplicitQualitativeCheckResult : public QualitativeCheckResult { public: typedef storm::storage::BitVector vector_type; @@ -26,8 +29,10 @@ class ExplicitQualitativeCheckResult : public QualitativeCheckResult { ExplicitQualitativeCheckResult(storm::storage::sparse::state_type state, bool value); ExplicitQualitativeCheckResult(vector_type const& truthValues); ExplicitQualitativeCheckResult(vector_type&& truthValues); - ExplicitQualitativeCheckResult(boost::variant const& truthValues); - ExplicitQualitativeCheckResult(boost::variant&& truthValues); + ExplicitQualitativeCheckResult(boost::variant const& truthValues, + std::optional>> scheduler = {}); + ExplicitQualitativeCheckResult(boost::variant&& truthValues, + std::optional>> scheduler = {}); ExplicitQualitativeCheckResult(ExplicitQualitativeCheckResult const& other) = default; ExplicitQualitativeCheckResult& operator=(ExplicitQualitativeCheckResult const& other) = default; @@ -58,6 +63,11 @@ class ExplicitQualitativeCheckResult : public QualitativeCheckResult { virtual void filter(QualitativeCheckResult const& filter) override; + virtual bool hasScheduler() const override; + void setScheduler(std::unique_ptr>&& scheduler); + storm::storage::Scheduler const& getScheduler() const; + storm::storage::Scheduler& getScheduler(); + template storm::json toJson(std::optional const& stateValuations = std::nullopt, std::optional const& stateLabels = std::nullopt) const; @@ -67,6 +77,9 @@ class ExplicitQualitativeCheckResult : public QualitativeCheckResult { // The values of the quantitative check result. boost::variant truthValues; + + // An optional scheduler that accompanies the values. + std::optional>> scheduler; }; } // namespace modelchecker } // namespace storm diff --git a/src/storm/modelchecker/results/ExplicitQuantitativeCheckResult.cpp b/src/storm/modelchecker/results/ExplicitQuantitativeCheckResult.cpp index 3f86073134..d775b924e4 100644 --- a/src/storm/modelchecker/results/ExplicitQuantitativeCheckResult.cpp +++ b/src/storm/modelchecker/results/ExplicitQuantitativeCheckResult.cpp @@ -48,20 +48,20 @@ ExplicitQuantitativeCheckResult::ExplicitQuantitativeCheckResult(vect template ExplicitQuantitativeCheckResult::ExplicitQuantitativeCheckResult(boost::variant const& values, - boost::optional>> scheduler) + std::optional>> scheduler) : values(values), scheduler(scheduler) { // Intentionally left empty. } template ExplicitQuantitativeCheckResult::ExplicitQuantitativeCheckResult(boost::variant&& values, - boost::optional>> scheduler) + std::optional>> scheduler) : values(std::move(values)), scheduler(scheduler) { // Intentionally left empty. } template -ExplicitQuantitativeCheckResult::ExplicitQuantitativeCheckResult(ExplicitQualitativeCheckResult const& other) { +ExplicitQuantitativeCheckResult::ExplicitQuantitativeCheckResult(ExplicitQualitativeCheckResult const& other) { if (other.isResultForAllStates()) { storm::storage::BitVector const& bvValues = other.getTruthValuesVector(); @@ -73,7 +73,7 @@ ExplicitQuantitativeCheckResult::ExplicitQuantitativeCheckResult(Expl values = newVector; } else { - ExplicitQualitativeCheckResult::map_type const& bitMap = other.getTruthValuesMap(); + typename ExplicitQualitativeCheckResult::map_type const& bitMap = other.getTruthValuesMap(); map_type newMap; for (auto const& e : bitMap) { @@ -109,8 +109,8 @@ void ExplicitQuantitativeCheckResult::filter(QualitativeCheckResult c STORM_LOG_THROW(filter.isExplicitQualitativeCheckResult(), storm::exceptions::InvalidOperationException, "Cannot filter explicit check result with non-explicit filter."); STORM_LOG_THROW(filter.isResultForAllStates(), storm::exceptions::InvalidOperationException, "Cannot filter check result with non-complete filter."); - ExplicitQualitativeCheckResult const& explicitFilter = filter.asExplicitQualitativeCheckResult(); - ExplicitQualitativeCheckResult::vector_type const& filterTruthValues = explicitFilter.getTruthValuesVector(); + ExplicitQualitativeCheckResult const& explicitFilter = filter.template asExplicitQualitativeCheckResult(); + typename ExplicitQualitativeCheckResult::vector_type const& filterTruthValues = explicitFilter.getTruthValuesVector(); if (this->isResultForAllStates()) { map_type newMap; @@ -226,13 +226,13 @@ void ExplicitQuantitativeCheckResult::setScheduler(std::unique_ptr storm::storage::Scheduler const& ExplicitQuantitativeCheckResult::getScheduler() const { STORM_LOG_THROW(this->hasScheduler(), storm::exceptions::InvalidOperationException, "Unable to retrieve non-existing scheduler."); - return *scheduler.get(); + return *scheduler.value(); } template storm::storage::Scheduler& ExplicitQuantitativeCheckResult::getScheduler() { STORM_LOG_THROW(this->hasScheduler(), storm::exceptions::InvalidOperationException, "Unable to retrieve non-existing scheduler."); - return *scheduler.get(); + return *scheduler.value(); } template @@ -369,7 +369,7 @@ std::unique_ptr ExplicitQuantitativeCheckResult::compare } break; } - return std::unique_ptr(new ExplicitQualitativeCheckResult(std::move(result))); + return std::unique_ptr(new ExplicitQualitativeCheckResult(std::move(result), std::move(scheduler))); } else { map_type const& valuesAsMap = boost::get(values); std::map result; @@ -395,7 +395,7 @@ std::unique_ptr ExplicitQuantitativeCheckResult::compare } break; } - return std::unique_ptr(new ExplicitQualitativeCheckResult(std::move(result))); + return std::unique_ptr(new ExplicitQualitativeCheckResult(std::move(result), std::move(scheduler))); } } diff --git a/src/storm/modelchecker/results/ExplicitQuantitativeCheckResult.h b/src/storm/modelchecker/results/ExplicitQuantitativeCheckResult.h index e9c9ac487d..bf543cd180 100644 --- a/src/storm/modelchecker/results/ExplicitQuantitativeCheckResult.h +++ b/src/storm/modelchecker/results/ExplicitQuantitativeCheckResult.h @@ -16,6 +16,7 @@ namespace storm { namespace modelchecker { // Forward declaration +template class ExplicitQualitativeCheckResult; template @@ -31,15 +32,15 @@ class ExplicitQuantitativeCheckResult : public QuantitativeCheckResult const& values, - boost::optional>> scheduler = boost::none); + std::optional>> scheduler = {}); ExplicitQuantitativeCheckResult(boost::variant&& values, - boost::optional>> scheduler = boost::none); + std::optional>> scheduler = {}); ExplicitQuantitativeCheckResult(ExplicitQuantitativeCheckResult const& other) = default; ExplicitQuantitativeCheckResult& operator=(ExplicitQuantitativeCheckResult const& other) = default; ExplicitQuantitativeCheckResult(ExplicitQuantitativeCheckResult&& other) = default; ExplicitQuantitativeCheckResult& operator=(ExplicitQuantitativeCheckResult&& other) = default; - explicit ExplicitQuantitativeCheckResult(ExplicitQualitativeCheckResult const& other); + explicit ExplicitQuantitativeCheckResult(ExplicitQualitativeCheckResult const& other); virtual ~ExplicitQuantitativeCheckResult() = default; @@ -84,7 +85,7 @@ class ExplicitQuantitativeCheckResult : public QuantitativeCheckResult values; // An optional scheduler that accompanies the values. - boost::optional>> scheduler; + std::optional>> scheduler; }; } // namespace modelchecker } // namespace storm diff --git a/src/storm/modelchecker/results/LexicographicCheckResult.cpp b/src/storm/modelchecker/results/LexicographicCheckResult.cpp index 027f21f3ef..0206554088 100644 --- a/src/storm/modelchecker/results/LexicographicCheckResult.cpp +++ b/src/storm/modelchecker/results/LexicographicCheckResult.cpp @@ -43,8 +43,8 @@ void LexicographicCheckResult::filter(QualitativeCheckResult const& f STORM_LOG_THROW(filter.isExplicitQualitativeCheckResult(), storm::exceptions::InvalidOperationException, "Cannot filter explicit check result with non-explicit filter."); STORM_LOG_THROW(filter.isResultForAllStates(), storm::exceptions::InvalidOperationException, "Cannot filter check result with non-complete filter."); - ExplicitQualitativeCheckResult const& explicitFilter = filter.asExplicitQualitativeCheckResult(); - ExplicitQualitativeCheckResult::vector_type const& filterTruthValues = explicitFilter.getTruthValuesVector(); + ExplicitQualitativeCheckResult const& explicitFilter = filter.template asExplicitQualitativeCheckResult(); + typename ExplicitQualitativeCheckResult::vector_type const& filterTruthValues = explicitFilter.getTruthValuesVector(); STORM_LOG_THROW(filterTruthValues.getNumberOfSetBits() == 1 && filterTruthValues.get(state), storm::exceptions::InvalidOperationException, "The check result fails to contain some results referred to by the filter."); diff --git a/src/storm/models/sparse/Ctmc.cpp b/src/storm/models/sparse/Ctmc.cpp index ccb39cad09..88ebbbd7e6 100644 --- a/src/storm/models/sparse/Ctmc.cpp +++ b/src/storm/models/sparse/Ctmc.cpp @@ -96,8 +96,10 @@ storm::storage::SparseMatrix Ctmc::comput template class Ctmc; template class Ctmc; template class Ctmc>; +template class Ctmc>; template class Ctmc; template class Ctmc; +template class Ctmc; } // namespace sparse } // namespace models } // namespace storm diff --git a/src/storm/models/sparse/DeterministicModel.cpp b/src/storm/models/sparse/DeterministicModel.cpp index 956c4e2e0c..c4d326380f 100644 --- a/src/storm/models/sparse/DeterministicModel.cpp +++ b/src/storm/models/sparse/DeterministicModel.cpp @@ -65,7 +65,9 @@ void DeterministicModel::writeDotToStream(std::ostre template class DeterministicModel; template class DeterministicModel>; template class DeterministicModel; +template class DeterministicModel>; template class DeterministicModel; +template class DeterministicModel; template class DeterministicModel; } // namespace sparse } // namespace models diff --git a/src/storm/models/sparse/Dtmc.cpp b/src/storm/models/sparse/Dtmc.cpp index 29ff1bd0a3..887dceff3d 100644 --- a/src/storm/models/sparse/Dtmc.cpp +++ b/src/storm/models/sparse/Dtmc.cpp @@ -3,6 +3,7 @@ #include "storm/adapters/IntervalAdapter.h" #include "storm/adapters/RationalFunctionAdapter.h" #include "storm/adapters/RationalNumberAdapter.h" +#include "storm/adapters/RationalNumberForward.h" #include "storm/models/sparse/StandardRewardModel.h" namespace storm { @@ -47,7 +48,9 @@ void Dtmc::reduceToStateBasedRewards() { template class Dtmc; template class Dtmc>; template class Dtmc; +template class Dtmc>; template class Dtmc; +template class Dtmc; template class Dtmc; } // namespace sparse } // namespace models diff --git a/src/storm/models/sparse/MarkovAutomaton.cpp b/src/storm/models/sparse/MarkovAutomaton.cpp index 73d18d6b2f..c5fd21397e 100644 --- a/src/storm/models/sparse/MarkovAutomaton.cpp +++ b/src/storm/models/sparse/MarkovAutomaton.cpp @@ -250,7 +250,9 @@ void MarkovAutomaton::printModelInformationToStream( template class MarkovAutomaton; template class MarkovAutomaton>; template class MarkovAutomaton; +template class MarkovAutomaton>; template class MarkovAutomaton; +template class MarkovAutomaton; template class MarkovAutomaton; } // namespace sparse } // namespace models diff --git a/src/storm/models/sparse/Mdp.cpp b/src/storm/models/sparse/Mdp.cpp index d07bbf49da..60d4f636d3 100644 --- a/src/storm/models/sparse/Mdp.cpp +++ b/src/storm/models/sparse/Mdp.cpp @@ -44,7 +44,9 @@ Mdp::Mdp(storm::storage::sparse::ModelComponents; template class Mdp>; template class Mdp; +template class Mdp>; template class Mdp; +template class Mdp; template class Mdp; } // namespace sparse } // namespace models diff --git a/src/storm/models/sparse/Model.cpp b/src/storm/models/sparse/Model.cpp index dada7de970..f283bfaa90 100644 --- a/src/storm/models/sparse/Model.cpp +++ b/src/storm/models/sparse/Model.cpp @@ -644,7 +644,7 @@ bool Model::supportsParameters() const { template bool Model::supportsUncertainty() const { - return std::is_same::value; + return storm::IsIntervalType; } template @@ -729,7 +729,9 @@ std::set getAllParameters(Model; template class Model>; template class Model; +template class Model>; template class Model; +template class Model; template class Model; } // namespace sparse } // namespace models diff --git a/src/storm/models/sparse/NondeterministicModel.cpp b/src/storm/models/sparse/NondeterministicModel.cpp index 0a256f75a4..5ee47ec3ed 100644 --- a/src/storm/models/sparse/NondeterministicModel.cpp +++ b/src/storm/models/sparse/NondeterministicModel.cpp @@ -190,8 +190,10 @@ uint_least64_t NondeterministicModel::getChoiceIndex template class NondeterministicModel; template class NondeterministicModel>; -template class NondeterministicModel; template class NondeterministicModel; +template class NondeterministicModel>; +template class NondeterministicModel; +template class NondeterministicModel; template class NondeterministicModel; } // namespace sparse } // namespace models diff --git a/src/storm/models/sparse/Pomdp.cpp b/src/storm/models/sparse/Pomdp.cpp index 69ecda855d..8f52964b25 100644 --- a/src/storm/models/sparse/Pomdp.cpp +++ b/src/storm/models/sparse/Pomdp.cpp @@ -156,7 +156,9 @@ std::size_t Pomdp::hash() const { template class Pomdp; template class Pomdp>; template class Pomdp; +template class Pomdp>; template class Pomdp; +template class Pomdp; template class Pomdp; } // namespace sparse } // namespace models diff --git a/src/storm/models/sparse/Smg.cpp b/src/storm/models/sparse/Smg.cpp index 9f7d708a1f..2a67a930bc 100644 --- a/src/storm/models/sparse/Smg.cpp +++ b/src/storm/models/sparse/Smg.cpp @@ -80,7 +80,9 @@ storm::storage::BitVector Smg::computeStatesOfCoalit template class Smg; template class Smg>; template class Smg; +template class Smg>; template class Smg; +template class Smg; template class Smg; } // namespace sparse diff --git a/src/storm/models/sparse/StandardRewardModel.cpp b/src/storm/models/sparse/StandardRewardModel.cpp index bdcdf92ecd..6c8c8f03c6 100644 --- a/src/storm/models/sparse/StandardRewardModel.cpp +++ b/src/storm/models/sparse/StandardRewardModel.cpp @@ -662,6 +662,48 @@ template storm::storage::BitVector StandardRewardModel::getChoi storm::storage::SparseMatrix const& transitionMatrix) const; template class StandardRewardModel; template std::ostream& operator<< (std::ostream& out, StandardRewardModel const& rewardModel); + +template std::vector StandardRewardModel::getTotalRewardVector( + uint_fast64_t numberOfRows, storm::storage::SparseMatrix const& transitionMatrix, storm::storage::BitVector const& filter) const; +template std::vector StandardRewardModel::getTotalRewardVector( + storm::storage::SparseMatrix const& transitionMatrix) const; +template std::vector StandardRewardModel::getTotalRewardVector( + storm::storage::SparseMatrix const& transitionMatrix, std::vector const& weights) const; +template std::vector StandardRewardModel::getTotalRewardVector( + uint_fast64_t numberOfRows, storm::storage::SparseMatrix const& transitionMatrix, storm::storage::BitVector const& filter) const; +template std::vector StandardRewardModel::getTotalRewardVector( + storm::storage::SparseMatrix const& transitionMatrix) const; +template std::vector StandardRewardModel::getTotalRewardVector( + storm::storage::SparseMatrix const& transitionMatrix, std::vector const& weights) const; +template std::vector StandardRewardModel::getTotalActionRewardVector( + storm::storage::SparseMatrix const& transitionMatrix, std::vector const& stateRewardWeights) const; +template storm::storage::BitVector StandardRewardModel::getStatesWithFilter( + storm::storage::SparseMatrix const& transitionMatrix, std::function const& filter) const; +template storm::storage::BitVector StandardRewardModel::getStatesWithFilter( + storm::storage::SparseMatrix const& transitionMatrix, std::function const& filter) const; +template storm::storage::BitVector StandardRewardModel::getChoicesWithFilter( + storm::storage::SparseMatrix const& transitionMatrix, std::function const& filter) const; +template storm::storage::BitVector StandardRewardModel::getChoicesWithFilter( + storm::storage::SparseMatrix const& transitionMatrix, std::function const& filter) const; +template void StandardRewardModel::setStateActionReward(uint_fast64_t choiceIndex, storm::RationalNumber const& newValue); +template void StandardRewardModel::setStateActionReward(uint_fast64_t choiceIndex, storm::RationalInterval const& newValue); +template void StandardRewardModel::setStateReward(uint_fast64_t state, storm::RationalNumber const& newValue); +template void StandardRewardModel::setStateReward(uint_fast64_t state, storm::RationalInterval const& newValue); +template void StandardRewardModel::clearRewardAtState(uint_fast64_t state, + storm::storage::SparseMatrix const& transitionMatrix); +template void StandardRewardModel::clearRewardAtState(uint_fast64_t state, + storm::storage::SparseMatrix const& transitionMatrix); +template void StandardRewardModel::reduceToStateBasedRewards( + storm::storage::SparseMatrix const& transitionMatrix, bool reduceToStateRewards, std::vector const* weights); +template void StandardRewardModel::reduceToStateBasedRewards( + storm::storage::SparseMatrix const& transitionMatrix, bool reduceToStateRewards, + std::vector const* weights); +template storm::storage::BitVector StandardRewardModel::getStatesWithZeroReward( + storm::storage::SparseMatrix const& transitionMatrix) const; +template storm::storage::BitVector StandardRewardModel::getChoicesWithZeroReward( + storm::storage::SparseMatrix const& transitionMatrix) const; +template class StandardRewardModel; +template std::ostream& operator<< (std::ostream& out, StandardRewardModel const& rewardModel); } // namespace sparse } // namespace models diff --git a/src/storm/models/sparse/StochasticTwoPlayerGame.cpp b/src/storm/models/sparse/StochasticTwoPlayerGame.cpp index 47cc89ea67..c85bc0b163 100644 --- a/src/storm/models/sparse/StochasticTwoPlayerGame.cpp +++ b/src/storm/models/sparse/StochasticTwoPlayerGame.cpp @@ -63,8 +63,10 @@ storm::models::sparse::ChoiceLabeling const& StochasticTwoPlayerGame; template class StochasticTwoPlayerGame>; -template class StochasticTwoPlayerGame; template class StochasticTwoPlayerGame; +template class StochasticTwoPlayerGame>; +template class StochasticTwoPlayerGame; +template class StochasticTwoPlayerGame; template class StochasticTwoPlayerGame; } // namespace sparse diff --git a/src/storm/settings/modules/GeneralSettings.cpp b/src/storm/settings/modules/GeneralSettings.cpp index d2efd632e8..8b440ee61f 100644 --- a/src/storm/settings/modules/GeneralSettings.cpp +++ b/src/storm/settings/modules/GeneralSettings.cpp @@ -32,6 +32,7 @@ const std::string GeneralSettings::bisimulationOptionShortName = "bisim"; const std::string GeneralSettings::parametricOptionName = "parametric"; const std::string GeneralSettings::exactOptionName = "exact"; const std::string GeneralSettings::soundOptionName = "sound"; +const std::string GeneralSettings::intervalOptionName = "interval"; GeneralSettings::GeneralSettings() : ModuleSettings(moduleName) { this->addOption( @@ -85,6 +86,7 @@ GeneralSettings::GeneralSettings() : ModuleSettings(moduleName) { .build()) .build()); this->addOption(storm::settings::OptionBuilder(moduleName, soundOptionName, false, "Sets whether to force sound model checking.").build()); + this->addOption(storm::settings::OptionBuilder(moduleName, intervalOptionName, false, "Sets whether to enable interval model checking.").build()); } bool GeneralSettings::isHelpSet() const { @@ -152,6 +154,10 @@ bool GeneralSettings::isSoundSet() const { return this->getOption(soundOptionName).getHasOptionBeenSet(); } +bool GeneralSettings::isIntervalSet() const { + return this->getOption(intervalOptionName).getHasOptionBeenSet(); +} + void GeneralSettings::finalize() { // Intentionally left empty. } diff --git a/src/storm/settings/modules/GeneralSettings.h b/src/storm/settings/modules/GeneralSettings.h index 1723583b14..2e2e12620f 100644 --- a/src/storm/settings/modules/GeneralSettings.h +++ b/src/storm/settings/modules/GeneralSettings.h @@ -123,6 +123,13 @@ class GeneralSettings : public ModuleSettings { */ bool isSoundSet() const; + /*! + * Retrieves whether the option enabling interval models is set. + * + * @return True if the option was set + */ + bool isIntervalSet() const; + bool check() const override; void finalize() override; @@ -149,6 +156,7 @@ class GeneralSettings : public ModuleSettings { static const std::string parametricOptionName; static const std::string exactOptionName; static const std::string soundOptionName; + static const std::string intervalOptionName; }; } // namespace modules diff --git a/src/storm/settings/modules/MinMaxEquationSolverSettings.cpp b/src/storm/settings/modules/MinMaxEquationSolverSettings.cpp index ef674be8ce..7697aebd14 100644 --- a/src/storm/settings/modules/MinMaxEquationSolverSettings.cpp +++ b/src/storm/settings/modules/MinMaxEquationSolverSettings.cpp @@ -54,7 +54,7 @@ MinMaxEquationSolverSettings::MinMaxEquationSolverSettings() : ModuleSettings(mo .setIsAdvanced() .addArgument(storm::settings::ArgumentBuilder::createDoubleArgument("value", "The precision to achieve.") .setDefaultValueDouble(1e-06) - .addValidatorDouble(ArgumentValidatorFactory::createDoubleRangeValidatorExcluding(0.0, 1.0)) + .addValidatorDouble(ArgumentValidatorFactory::createDoubleRangeValidatorIncluding(0.0, 1.0)) .build()) .build()); diff --git a/src/storm/settings/modules/ModelCheckerSettings.cpp b/src/storm/settings/modules/ModelCheckerSettings.cpp index 7806c9abc7..8a79ee6907 100644 --- a/src/storm/settings/modules/ModelCheckerSettings.cpp +++ b/src/storm/settings/modules/ModelCheckerSettings.cpp @@ -5,6 +5,7 @@ #include "storm/settings/Option.h" #include "storm/settings/OptionBuilder.h" #include "storm/settings/SettingsManager.h" +#include "storm/utility/constants.h" namespace storm::settings::modules { @@ -12,6 +13,7 @@ const std::string ModelCheckerSettings::moduleName = "modelchecker"; const std::string ModelCheckerSettings::filterRewZeroOptionName = "filterrewzero"; const std::string ModelCheckerSettings::ltl2daToolOptionName = "ltl2datool"; const std::string ModelCheckerSettings::conditionalAlgorithmOptionName = "conditional"; +static const std::string conditionalToleranceName = "conditional-tolerance"; ModelCheckerSettings::ModelCheckerSettings() : ModuleSettings(moduleName) { this->addOption(storm::settings::OptionBuilder(moduleName, filterRewZeroOptionName, false, @@ -26,14 +28,22 @@ ModelCheckerSettings::ModelCheckerSettings() : ModuleSettings(moduleName) { .build()) .build()); - std::vector const conditionalAlgs = {"default", "restart", "bisection", "bisection-advanced", "pi"}; + std::vector const conditionalAlgs = {"default", "restart", "bisection", "bisection-advanced", "bisection-pt", "bisection-advanced-pt", "pi"}; this->addOption(storm::settings::OptionBuilder(moduleName, conditionalAlgorithmOptionName, false, "The used algorithm for conditional probabilities.") - .setIsAdvanced() .addArgument(storm::settings::ArgumentBuilder::createStringArgument("name", "The name of the method to use.") .addValidatorString(ArgumentValidatorFactory::createMultipleChoiceValidator(conditionalAlgs)) .setDefaultValueString("default") .build()) .build()); + // Would be better if there was a createRationalArgument . + this->addOption( + storm::settings::OptionBuilder(moduleName, conditionalToleranceName, false, "The internally used tolerance for computing conditional probabilities..") + .setShortName("condtol") + .addArgument(storm::settings::ArgumentBuilder::createDoubleArgument("value", "The precision to use.") + .setDefaultValueDouble(1e-06) + .addValidatorDouble(ArgumentValidatorFactory::createDoubleRangeValidatorIncluding(0.0, 1.0)) + .build()) + .build()); } bool ModelCheckerSettings::isFilterRewZeroSet() const { @@ -52,6 +62,10 @@ bool ModelCheckerSettings::isConditionalAlgorithmSet() const { return this->getOption(conditionalAlgorithmOptionName).getHasOptionBeenSet(); } +storm::RationalNumber ModelCheckerSettings::getConditionalTolerance() const { + return storm::utility::convertNumber(this->getOption(conditionalToleranceName).getArgumentByName("value").getValueAsDouble()); +} + ConditionalAlgorithmSetting ModelCheckerSettings::getConditionalAlgorithmSetting() const { return conditionalAlgorithmSettingFromString(this->getOption(conditionalAlgorithmOptionName).getArgumentByName("name").getValueAsString()); } diff --git a/src/storm/settings/modules/ModelCheckerSettings.h b/src/storm/settings/modules/ModelCheckerSettings.h index ce7e5bd234..ef31a110b8 100644 --- a/src/storm/settings/modules/ModelCheckerSettings.h +++ b/src/storm/settings/modules/ModelCheckerSettings.h @@ -1,6 +1,7 @@ #pragma once #include "storm-config.h" +#include "storm/adapters/RationalNumberAdapter.h" #include "storm/modelchecker/helper/conditional/ConditionalAlgorithmSetting.h" #include "storm/settings/modules/ModuleSettings.h" @@ -44,6 +45,12 @@ class ModelCheckerSettings : public ModuleSettings { */ ConditionalAlgorithmSetting getConditionalAlgorithmSetting() const; + /*! + * TODO probably generalize + * Retrieves + */ + storm::RationalNumber getConditionalTolerance() const; + // The name of the module. static const std::string moduleName; diff --git a/src/storm/solver/IterativeMinMaxLinearEquationSolver.cpp b/src/storm/solver/IterativeMinMaxLinearEquationSolver.cpp index 3fe6aa749d..32040e68ec 100644 --- a/src/storm/solver/IterativeMinMaxLinearEquationSolver.cpp +++ b/src/storm/solver/IterativeMinMaxLinearEquationSolver.cpp @@ -4,6 +4,7 @@ #include #include "storm/adapters/IntervalAdapter.h" +#include "storm/adapters/IntervalForward.h" #include "storm/adapters/RationalNumberAdapter.h" #include "storm/environment/solver/MinMaxSolverEnvironment.h" #include "storm/environment/solver/OviSolverEnvironment.h" @@ -63,7 +64,7 @@ MinMaxMethod IterativeMinMaxLinearEquationSolver::getMe "different method."); method = MinMaxMethod::PolicyIteration; } else { - STORM_LOG_WARN("The selected solution method " << toString(method) << " does not guarantee exact results."); + // STORM_LOG_WARN("The selected solution method " << toString(method) << " does not guarantee exact results."); } } else if (env.solver().isForceSoundness() && method != MinMaxMethod::SoundValueIteration && method != MinMaxMethod::IntervalIteration && method != MinMaxMethod::PolicyIteration && method != MinMaxMethod::RationalSearch && method != MinMaxMethod::OptimisticValueIteration && @@ -135,7 +136,7 @@ void IterativeMinMaxLinearEquationSolver::setUpViOperat // The trivial row grouping minmax operator makes sense over intervals. viOperatorTriv = std::make_shared>(); viOperatorTriv->setMatrixBackwards(*this->A); - if constexpr (!std::is_same_v) { + if constexpr (!storm::IsIntervalType) { // It might be that someone is using a minmaxlinearequationsolver with an advanced VI algorithm // but is just passing a DTMC over doubles. In this case we need to populate this VI operator. // It behaves exactly the same as the trivial row grouping operator, but it is currently hardcoded @@ -168,7 +169,7 @@ template void IterativeMinMaxLinearEquationSolver::extractScheduler(std::vector& x, std::vector const& b, OptimizationDirection const& dir, UncertaintyResolutionMode uncertaintyResolutionMode, bool updateX) const { - if (std::is_same_v && this->A->hasTrivialRowGrouping()) { + if (storm::IsIntervalType && this->A->hasTrivialRowGrouping()) { // Create robust scheduler index if it doesn't exist yet if (!this->robustSchedulerIndex) { this->robustSchedulerIndex = std::vector(x.size(), 0); @@ -201,7 +202,7 @@ void IterativeMinMaxLinearEquationSolver::extractSchedu setUpViOperator(); } if (viOperatorTriv) { - if constexpr (std::is_same() && std::is_same()) { + if constexpr (storm::IsIntervalType && storm::IsIntervalType) { storm::solver::helper::SchedulerTrackingHelper schedHelper(viOperatorTriv); schedHelper.computeScheduler(x, b, dir, *this->schedulerChoices, uncertaintyResolutionMode, updateX ? &x : nullptr, this->robustSchedulerIndex); } else { @@ -218,8 +219,8 @@ template bool IterativeMinMaxLinearEquationSolver::solveInducedEquationSystem( Environment const& env, std::unique_ptr>& linearEquationSolver, std::vector const& scheduler, std::vector& x, std::vector& subB, std::vector const& originalB, OptimizationDirection dir) const { - if constexpr (std::is_same_v) { - if constexpr (std::is_same_v) { + if constexpr (storm::IsIntervalType) { + if constexpr (storm::IsIntervalType) { STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "We did not implement solving induced equation systems for interval-based models outside of robust VI."); // Implementing this requires linear equation systems with different value types and solution types (or some appropriate casting) @@ -251,7 +252,7 @@ bool IterativeMinMaxLinearEquationSolver::solveInducedE for (uint64_t i = 0; i < row.getNumberOfEntries(); i++, schedulerIterator++) { if (!utility::isZero(probLeft)) { auto const& entry = rowIter[*schedulerIterator]; - auto const diameter = entry.getValue().upper() - entry.getValue().lower(); + SolutionType const diameter = entry.getValue().upper() - entry.getValue().lower(); auto const value = utility::min(probLeft, diameter); tmp[*schedulerIterator] += value; probLeft -= value; @@ -334,7 +335,7 @@ template bool IterativeMinMaxLinearEquationSolver::performPolicyIteration( Environment const& env, OptimizationDirection dir, std::vector& x, std::vector const& b, std::vector&& initialPolicy) const { - if constexpr (std::is_same_v) { + if constexpr (storm::IsIntervalType) { STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "We did not implement policy iteration for interval-based models."); return false; } else { @@ -460,7 +461,7 @@ MinMaxLinearEquationSolverRequirements IterativeMinMaxLinearEquationSolver) { + if constexpr (storm::IsIntervalType) { STORM_LOG_ASSERT(!needsLinEqSolver, "Intervals should not require a linear equation solver."); // nothing to be done; } else if (needsLinEqSolver) { @@ -561,7 +562,7 @@ template bool IterativeMinMaxLinearEquationSolver::solveEquationsOptimisticValueIteration(Environment const& env, OptimizationDirection dir, std::vector& x, std::vector const& b) const { - if constexpr (std::is_same_v) { + if constexpr (storm::IsIntervalType) { STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "We did not implement optimistic value iteration for interval-based models."); return false; } else { @@ -617,7 +618,7 @@ template bool IterativeMinMaxLinearEquationSolver::solveEquationsGuessingValueIteration(Environment const& env, OptimizationDirection dir, std::vector& x, std::vector const& b) const { - if constexpr (std::is_same_v) { + if constexpr (storm::IsIntervalType) { STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "We did not implement guessing value iteration for interval-based models."); return false; } else { @@ -784,8 +785,8 @@ template bool IterativeMinMaxLinearEquationSolver::solveEquationsIntervalIteration(Environment const& env, OptimizationDirection dir, std::vector& x, std::vector const& b) const { - if constexpr (std::is_same_v) { - STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "We did not implement intervaliteration for interval-based models"); + if constexpr (storm::IsIntervalType) { + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "We did not implement interval iteration for interval-based models"); return false; } else { setUpViOperator(); @@ -827,7 +828,7 @@ template bool IterativeMinMaxLinearEquationSolver::solveEquationsSoundValueIteration(Environment const& env, OptimizationDirection dir, std::vector& x, std::vector const& b) const { - if constexpr (std::is_same_v) { + if constexpr (storm::IsIntervalType) { STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "SoundVI does not handle interval-based models"); return false; } else { @@ -879,7 +880,7 @@ bool IterativeMinMaxLinearEquationSolver::solveEquation template bool IterativeMinMaxLinearEquationSolver::solveEquationsViToPi(Environment const& env, OptimizationDirection dir, std::vector& x, std::vector const& b) const { - if constexpr (std::is_same_v) { + if constexpr (storm::IsIntervalType) { STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "ViToPi does not handle interval-based models"); return false; } @@ -914,7 +915,7 @@ template bool IterativeMinMaxLinearEquationSolver::solveEquationsRationalSearch(Environment const& env, OptimizationDirection dir, std::vector& x, std::vector const& b) const { - if constexpr (std::is_same_v) { + if constexpr (storm::IsIntervalType) { STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Rational search does not handle interval-based models"); return false; } else { @@ -986,6 +987,7 @@ void IterativeMinMaxLinearEquationSolver::clearCache() template class IterativeMinMaxLinearEquationSolver; template class IterativeMinMaxLinearEquationSolver; template class IterativeMinMaxLinearEquationSolver; +template class IterativeMinMaxLinearEquationSolver; } // namespace solver } // namespace storm diff --git a/src/storm/solver/LinearEquationSolver.cpp b/src/storm/solver/LinearEquationSolver.cpp index 51d43b1bcd..3ad3d168d9 100644 --- a/src/storm/solver/LinearEquationSolver.cpp +++ b/src/storm/solver/LinearEquationSolver.cpp @@ -148,7 +148,7 @@ template std::unique_ptr> GeneralLinearEquationSolverFactory::create(Environment const& env) const { EquationSolverType type = env.solver().getLinearEquationSolverType(); - if constexpr (std::is_same_v) { + if constexpr (storm::IsIntervalType) { STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "We have not implemented interval-based linear equation solvers"); } diff --git a/src/storm/solver/MinMaxLinearEquationSolver.cpp b/src/storm/solver/MinMaxLinearEquationSolver.cpp index 81242b0296..5ee11d5e9a 100644 --- a/src/storm/solver/MinMaxLinearEquationSolver.cpp +++ b/src/storm/solver/MinMaxLinearEquationSolver.cpp @@ -252,19 +252,19 @@ std::unique_ptr> GeneralMinM result = std::make_unique>( std::make_unique>()); } else if (method == MinMaxMethod::Topological) { - if constexpr (std::is_same_v) { + if constexpr (storm::IsIntervalType) { STORM_LOG_ERROR("Topological method not implemented for ValueType==Interval."); } else { result = std::make_unique>(); } } else if (method == MinMaxMethod::LinearProgramming || method == MinMaxMethod::ViToLp) { - if constexpr (std::is_same_v) { + if constexpr (storm::IsIntervalType) { STORM_LOG_ERROR("LP method not implemented for ValueType==Interval."); } else { result = std::make_unique>(storm::utility::solver::getLpSolverFactory()); } } else if (method == MinMaxMethod::Acyclic) { - if constexpr (std::is_same_v) { + if constexpr (storm::IsIntervalType) { STORM_LOG_ERROR("Acyclic method not implemented for ValueType==Interval"); } else { result = std::make_unique>(); @@ -322,4 +322,8 @@ template class GeneralMinMaxLinearEquationSolverFactory; template class MinMaxLinearEquationSolver; template class MinMaxLinearEquationSolverFactory; template class GeneralMinMaxLinearEquationSolverFactory; + +template class MinMaxLinearEquationSolver; +template class MinMaxLinearEquationSolverFactory; +template class GeneralMinMaxLinearEquationSolverFactory; } // namespace storm::solver diff --git a/src/storm/solver/SolveGoal.cpp b/src/storm/solver/SolveGoal.cpp index c42b8a9d8a..7cb7686edb 100644 --- a/src/storm/solver/SolveGoal.cpp +++ b/src/storm/solver/SolveGoal.cpp @@ -111,6 +111,11 @@ UncertaintyResolutionMode SolveGoal::getUncertaintyReso return uncertaintyResolutionMode; } +template +storm::logic::ComparisonType SolveGoal::boundComparisonType() const { + return comparisonType.get(); +} + template SolutionType const& SolveGoal::thresholdValue() const { return threshold.get(); @@ -147,6 +152,7 @@ template class SolveGoal; template class SolveGoal; template class SolveGoal; template class SolveGoal; +template class SolveGoal; } // namespace solver } // namespace storm diff --git a/src/storm/solver/SolveGoal.h b/src/storm/solver/SolveGoal.h index 79072a38ea..f085b9351d 100644 --- a/src/storm/solver/SolveGoal.h +++ b/src/storm/solver/SolveGoal.h @@ -85,6 +85,8 @@ class SolveGoal { bool boundIsStrict() const; + storm::logic::ComparisonType boundComparisonType() const; + SolutionType const& thresholdValue() const; bool hasRelevantValues() const; diff --git a/src/storm/solver/StandardMinMaxLinearEquationSolver.cpp b/src/storm/solver/StandardMinMaxLinearEquationSolver.cpp index 7f35cbb08f..1f9736902e 100644 --- a/src/storm/solver/StandardMinMaxLinearEquationSolver.cpp +++ b/src/storm/solver/StandardMinMaxLinearEquationSolver.cpp @@ -43,5 +43,6 @@ void StandardMinMaxLinearEquationSolver::setMatrix(stor template class StandardMinMaxLinearEquationSolver; template class StandardMinMaxLinearEquationSolver; template class StandardMinMaxLinearEquationSolver; +template class StandardMinMaxLinearEquationSolver; } // namespace storm::solver diff --git a/src/storm/solver/helper/SchedulerTrackingHelper.cpp b/src/storm/solver/helper/SchedulerTrackingHelper.cpp index 13e0ba5eaa..0a296626c1 100644 --- a/src/storm/solver/helper/SchedulerTrackingHelper.cpp +++ b/src/storm/solver/helper/SchedulerTrackingHelper.cpp @@ -135,5 +135,7 @@ template class SchedulerTrackingHelper; template class SchedulerTrackingHelper; template class SchedulerTrackingHelper; template class SchedulerTrackingHelper; +template class SchedulerTrackingHelper; +template class SchedulerTrackingHelper; } // namespace storm::solver::helper diff --git a/src/storm/solver/helper/ValueIterationHelper.cpp b/src/storm/solver/helper/ValueIterationHelper.cpp index 586b6ae9ae..72e6918851 100644 --- a/src/storm/solver/helper/ValueIterationHelper.cpp +++ b/src/storm/solver/helper/ValueIterationHelper.cpp @@ -172,5 +172,7 @@ template class ValueIterationHelper; template class ValueIterationHelper; template class ValueIterationHelper; template class ValueIterationHelper; +template class ValueIterationHelper; +template class ValueIterationHelper; } // namespace storm::solver::helper diff --git a/src/storm/solver/helper/ValueIterationOperator.cpp b/src/storm/solver/helper/ValueIterationOperator.cpp index 4ab2674bf5..44dbc87aa0 100644 --- a/src/storm/solver/helper/ValueIterationOperator.cpp +++ b/src/storm/solver/helper/ValueIterationOperator.cpp @@ -53,7 +53,7 @@ void ValueIterationOperator::setMat matrixColumns.back() = StartOfRowGroupIndicator; // This is the start of the next row group } } else { - if constexpr (std::is_same::value) { + if constexpr (storm::IsIntervalType) { matrixColumns.push_back(StartOfRowIndicator); // Indicate start of first row for (auto rowIndex : indexRange(0, numRows)) { bool hasOnlyConstants = true; @@ -204,5 +204,7 @@ template class ValueIterationOperator; template class ValueIterationOperator; template class ValueIterationOperator; template class ValueIterationOperator; +template class ValueIterationOperator; +template class ValueIterationOperator; } // namespace storm::solver::helper diff --git a/src/storm/solver/helper/ValueIterationOperator.h b/src/storm/solver/helper/ValueIterationOperator.h index f14d976f6f..a3172ad40e 100644 --- a/src/storm/solver/helper/ValueIterationOperator.h +++ b/src/storm/solver/helper/ValueIterationOperator.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -241,6 +242,15 @@ class ValueIterationOperator { return {(*offsets.first)[offsetIndex], offsets.second}; } + template + OpT robustInitializeRowRes(std::vector const&, OffT const& offsets, uint64_t offsetIndex) const { + if constexpr (RobustDirection == OptimizationDirection::Maximize) { + return offsets.upper(); + } else { + return offsets.lower(); + } + } + template OpT robustInitializeRowRes(std::vector const&, std::vector const& offsets, uint64_t offsetIndex) const { if constexpr (RobustDirection == OptimizationDirection::Maximize) { @@ -272,7 +282,7 @@ class ValueIterationOperator { template auto applyRow(std::vector::const_iterator& matrixColumnIt, typename std::vector::const_iterator& matrixValueIt, OperandType const& operand, OffsetType const& offsets, uint64_t offsetIndex) const { - if constexpr (std::is_same_v) { + if constexpr (storm::IsIntervalType) { return applyRowRobust(matrixColumnIt, matrixValueIt, operand, offsets, offsetIndex); } else { return applyRowStandard(matrixColumnIt, matrixValueIt, operand, offsets, offsetIndex); @@ -312,13 +322,13 @@ class ValueIterationOperator { auto applyRowRobust(std::vector::const_iterator& matrixColumnIt, typename std::vector::const_iterator& matrixValueIt, OperandType const& operand, OffsetType const& offsets, uint64_t offsetIndex) const { STORM_LOG_ASSERT(*matrixColumnIt >= StartOfRowIndicator, "VI Operator in invalid state."); - auto result{robustInitializeRowRes(operand, offsets, offsetIndex)}; + SolutionType result{robustInitializeRowRes(operand, offsets, offsetIndex)}; applyCache.robustOrder.clear(); if (applyCache.hasOnlyConstants.size() > 0 && applyCache.hasOnlyConstants.get(offsetIndex)) { for (++matrixColumnIt; *matrixColumnIt < StartOfRowIndicator; ++matrixColumnIt, ++matrixValueIt) { - auto const lower = matrixValueIt->lower(); + SolutionType const lower = matrixValueIt->lower(); if constexpr (isPair::value) { STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Value Iteration is not implemented with pairs and interval-models."); // Notice the unclear semantics here in terms of how to order things. @@ -332,7 +342,7 @@ class ValueIterationOperator { SolutionType remainingValue{storm::utility::one()}; uint64_t orderCounter = 0; for (++matrixColumnIt; *matrixColumnIt < StartOfRowIndicator; ++matrixColumnIt, ++matrixValueIt, ++orderCounter) { - auto const lower = matrixValueIt->lower(); + SolutionType const lower = matrixValueIt->lower(); if constexpr (isPair::value) { STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Value Iteration is not implemented with pairs and interval-models."); // Notice the unclear semantics here in terms of how to order things. @@ -340,8 +350,8 @@ class ValueIterationOperator { result += operand[*matrixColumnIt] * lower; } remainingValue -= lower; - auto const diameter = matrixValueIt->upper() - lower; - if (!storm::utility::isZero(diameter)) { + SolutionType const diameter = static_cast(-lower + matrixValueIt->upper()); + if (!storm::utility::isZero(diameter)) { applyCache.robustOrder.emplace_back(operand[*matrixColumnIt], std::make_pair(diameter, orderCounter)); } } @@ -353,7 +363,7 @@ class ValueIterationOperator { std::sort(applyCache.robustOrder.begin(), applyCache.robustOrder.end(), cmp); for (auto const& pair : applyCache.robustOrder) { - auto availableMass = std::min(pair.second.first, remainingValue); + SolutionType availableMass = std::min(pair.second.first, remainingValue); result += availableMass * pair.first; remainingValue -= availableMass; if (storm::utility::isZero(remainingValue)) { @@ -462,8 +472,14 @@ class ValueIterationOperator { storage::BitVector hasOnlyConstants; }; + template + struct ApplyCache { + mutable std::vector>> robustOrder; + storage::BitVector hasOnlyConstants; + }; + /*! - * Cache for robust value iteration, empty struct for other ValueTypes than storm::Interval. + * Cache for robust value iteration, empty struct for other ValueTypes than storm::Interval and storm::RationalInterval. */ ApplyCache applyCache; diff --git a/src/storm/solver/multiplier/Multiplier.cpp b/src/storm/solver/multiplier/Multiplier.cpp index e12fb09b4a..7b30a6afca 100644 --- a/src/storm/solver/multiplier/Multiplier.cpp +++ b/src/storm/solver/multiplier/Multiplier.cpp @@ -1,4 +1,5 @@ #include "storm/solver/multiplier/Multiplier.h" +#include #include "storm/adapters/IntervalAdapter.h" #include "storm/adapters/RationalFunctionAdapter.h" @@ -17,30 +18,33 @@ namespace storm { namespace solver { -template -Multiplier::Multiplier(storm::storage::SparseMatrix const& matrix) : matrix(matrix) { +template +Multiplier::Multiplier(storm::storage::SparseMatrix const& matrix) : matrix(matrix) { // Intentionally left empty. } -template -void Multiplier::clearCache() const { +template +void Multiplier::clearCache() const { cachedVector.reset(); } -template -void Multiplier::multiplyAndReduce(Environment const& env, OptimizationDirection const& dir, std::vector const& x, - std::vector const* b, std::vector& result, std::vector* choices) const { +template +void Multiplier::multiplyAndReduce(Environment const& env, OptimizationDirection const& dir, std::vector const& x, + std::vector const* b, std::vector& result, + std::vector* choices) const { multiplyAndReduce(env, dir, this->matrix.getRowGroupIndices(), x, b, result, choices); } -template -void Multiplier::multiplyAndReduceGaussSeidel(Environment const& env, OptimizationDirection const& dir, std::vector& x, - std::vector const* b, std::vector* choices, bool backwards) const { +template +void Multiplier::multiplyAndReduceGaussSeidel(Environment const& env, OptimizationDirection const& dir, std::vector& x, + std::vector const* b, std::vector* choices, + bool backwards) const { multiplyAndReduceGaussSeidel(env, dir, this->matrix.getRowGroupIndices(), x, b, choices, backwards); } -template -void Multiplier::repeatedMultiply(Environment const& env, std::vector& x, std::vector const* b, uint64_t n) const { +template +void Multiplier::repeatedMultiply(Environment const& env, std::vector& x, std::vector const* b, + uint64_t n) const { storm::utility::ProgressMeasurement progress("multiplications"); progress.setMaxCount(n); progress.startNewMeasurement(0); @@ -54,9 +58,9 @@ void Multiplier::repeatedMultiply(Environment const& env, std::vector } } -template -void Multiplier::repeatedMultiplyAndReduce(Environment const& env, OptimizationDirection const& dir, std::vector& x, - std::vector const* b, uint64_t n) const { +template +void Multiplier::repeatedMultiplyAndReduce(Environment const& env, OptimizationDirection const& dir, std::vector& x, + std::vector const* b, uint64_t n) const { storm::utility::ProgressMeasurement progress("multiplications"); progress.setMaxCount(n); progress.startNewMeasurement(0); @@ -70,15 +74,16 @@ void Multiplier::repeatedMultiplyAndReduce(Environment const& env, Op } } -template -void Multiplier::repeatedMultiplyAndReduceWithFactor(Environment const& env, OptimizationDirection const& dir, std::vector& x, - std::vector const* b, uint64_t n, ValueType factor) const { +template +void Multiplier::repeatedMultiplyAndReduceWithFactor(Environment const& env, OptimizationDirection const& dir, + std::vector& x, std::vector const* b, uint64_t n, + SolutionType factor) const { storm::utility::ProgressMeasurement progress("multiplications"); progress.setMaxCount(n); progress.startNewMeasurement(0); for (uint64_t i = 0; i < n; ++i) { progress.updateProgress(i); - std::transform(x.begin(), x.end(), x.begin(), [factor](ValueType& c) { return c * factor; }); + std::transform(x.begin(), x.end(), x.begin(), [factor](SolutionType& c) { return c * factor; }); multiplyAndReduce(env, dir, x, b, x); if (storm::utility::resources::isTerminate()) { STORM_LOG_WARN("Aborting after " << i << " of " << n << " multiplications"); @@ -87,15 +92,15 @@ void Multiplier::repeatedMultiplyAndReduceWithFactor(Environment cons } } -template -void Multiplier::repeatedMultiplyWithFactor(Environment const& env, std::vector& x, std::vector const* b, uint64_t n, - ValueType factor) const { +template +void Multiplier::repeatedMultiplyWithFactor(Environment const& env, std::vector& x, std::vector const* b, + uint64_t n, SolutionType factor) const { storm::utility::ProgressMeasurement progress("multiplications"); progress.setMaxCount(n); progress.startNewMeasurement(0); for (uint64_t i = 0; i < n; ++i) { progress.updateProgress(i); - std::transform(x.begin(), x.end(), x.begin(), [factor](ValueType& c) { return c * factor; }); + std::transform(x.begin(), x.end(), x.begin(), [factor](SolutionType& c) { return c * factor; }); multiply(env, x, b, x); if (storm::utility::resources::isTerminate()) { STORM_LOG_WARN("Aborting after " << i << " of " << n << " multiplications"); @@ -104,39 +109,44 @@ void Multiplier::repeatedMultiplyWithFactor(Environment const& env, s } } -template - -std::vector& Multiplier::provideCachedVector(uint64_t size) const { +template +std::vector& Multiplier::provideCachedVector(uint64_t size) const { if (this->cachedVector) { this->cachedVector->resize(size); } else { - this->cachedVector = std::make_unique>(size); + this->cachedVector = std::make_unique>(size); } return *this->cachedVector; } -template -std::unique_ptr> MultiplierFactory::create(Environment const& env, storm::storage::SparseMatrix const& matrix) { +template +std::unique_ptr> MultiplierFactory::create(Environment const& env, + storm::storage::SparseMatrix const& matrix) { auto type = env.solver().multiplier().getType(); // Adjust the type if the ValueType is not supported - if (type == MultiplierType::ViOperator && (std::is_same_v || std::is_same_v)) { + if (type == MultiplierType::ViOperator && + (std::is_same_v || (storm::IsIntervalType && storm::IsIntervalType))) { STORM_LOG_INFO("Switching multiplier type from 'vioperator' to 'native' because the given ValueType is not supported by the VI Operator multiplier."); type = MultiplierType::Native; } switch (type) { case MultiplierType::ViOperator: - if constexpr (std::is_same_v || std::is_same_v) { + if constexpr (std::is_same_v || (storm::IsIntervalType && storm::IsIntervalType)) { throw storm::exceptions::NotImplementedException() << "VI Operator multiplier not supported with given value type."; } if (matrix.hasTrivialRowGrouping()) { - return std::make_unique>(matrix); + return std::make_unique>(matrix); } else { - return std::make_unique>(matrix); + return std::make_unique>(matrix); } case MultiplierType::Native: - return std::make_unique>(matrix); + if constexpr (std::is_same_v) { + return std::make_unique>(matrix); + } else { + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Native multiplier not implemented for unequal ValueType and SolutionType."); + } } STORM_LOG_THROW(false, storm::exceptions::IllegalArgumentException, "Unknown MultiplierType"); } @@ -149,6 +159,10 @@ template class Multiplier; template class MultiplierFactory; template class Multiplier; template class MultiplierFactory; +template class Multiplier; +template class MultiplierFactory; +template class Multiplier; +template class MultiplierFactory; } // namespace solver } // namespace storm diff --git a/src/storm/solver/multiplier/Multiplier.h b/src/storm/solver/multiplier/Multiplier.h index fe8e0f7d4a..3a8bccd009 100644 --- a/src/storm/solver/multiplier/Multiplier.h +++ b/src/storm/solver/multiplier/Multiplier.h @@ -17,7 +17,7 @@ class SparseMatrix; namespace solver { -template +template class Multiplier { public: Multiplier(storm::storage::SparseMatrix const& matrix); @@ -39,7 +39,8 @@ class Multiplier { * @param result The target vector into which to write the multiplication result. Its length must be equal * to the number of rows of A. Can be the same as the x vector. */ - virtual void multiply(Environment const& env, std::vector const& x, std::vector const* b, std::vector& result) const = 0; + virtual void multiply(Environment const& env, std::vector const& x, std::vector const* b, + std::vector& result) const = 0; /*! * Performs a matrix-vector multiplication in gauss-seidel style. @@ -50,7 +51,7 @@ class Multiplier { * to the number of rows of A. * @param backwards if true, the iterations will be performed beginning from the last row and ending at the first row. */ - virtual void multiplyGaussSeidel(Environment const& env, std::vector& x, std::vector const* b, bool backwards = true) const = 0; + virtual void multiplyGaussSeidel(Environment const& env, std::vector& x, std::vector const* b, bool backwards = true) const = 0; /*! * Performs a matrix-vector multiplication x' = A*x + b and then minimizes/maximizes over the row groups @@ -66,10 +67,10 @@ class Multiplier { * to the number of rows of A. Can be the same as the x vector. * @param choices If given, the choices made in the reduction process are written to this vector. */ - void multiplyAndReduce(Environment const& env, OptimizationDirection const& dir, std::vector const& x, std::vector const* b, - std::vector& result, std::vector* choices = nullptr) const; + void multiplyAndReduce(Environment const& env, OptimizationDirection const& dir, std::vector const& x, std::vector const* b, + std::vector& result, std::vector* choices = nullptr) const; virtual void multiplyAndReduce(Environment const& env, OptimizationDirection const& dir, std::vector const& rowGroupIndices, - std::vector const& x, std::vector const* b, std::vector& result, + std::vector const& x, std::vector const* b, std::vector& result, std::vector* choices = nullptr) const = 0; /*! @@ -87,10 +88,10 @@ class Multiplier { * @param choices If given, the choices made in the reduction process are written to this vector. * @param backwards if true, the iterations will be performed beginning from the last rowgroup and ending at the first rowgroup. */ - void multiplyAndReduceGaussSeidel(Environment const& env, OptimizationDirection const& dir, std::vector& x, std::vector const* b, + void multiplyAndReduceGaussSeidel(Environment const& env, OptimizationDirection const& dir, std::vector& x, std::vector const* b, std::vector* choices = nullptr, bool backwards = true) const; virtual void multiplyAndReduceGaussSeidel(Environment const& env, OptimizationDirection const& dir, std::vector const& rowGroupIndices, - std::vector& x, std::vector const* b, std::vector* choices = nullptr, + std::vector& x, std::vector const* b, std::vector* choices = nullptr, bool backwards = true) const = 0; /*! @@ -104,7 +105,7 @@ class Multiplier { * to the number of rows of A. * @param n The number of times to perform the multiplication. */ - void repeatedMultiply(Environment const& env, std::vector& x, std::vector const* b, uint64_t n) const; + void repeatedMultiply(Environment const& env, std::vector& x, std::vector const* b, uint64_t n) const; /*! * Performs repeated matrix-vector multiplication x' = A*x + b and then minimizes/maximizes over the row groups @@ -119,7 +120,7 @@ class Multiplier { * to the number of rows of A. * @param n The number of times to perform the multiplication. */ - void repeatedMultiplyAndReduce(Environment const& env, OptimizationDirection const& dir, std::vector& x, std::vector const* b, + void repeatedMultiplyAndReduce(Environment const& env, OptimizationDirection const& dir, std::vector& x, std::vector const* b, uint64_t n) const; /*! * Performs repeated matrix-vector multiplication x' = A*(factor * x) + b. Vector x is scaled by factor in each iteration. @@ -133,7 +134,8 @@ class Multiplier { * @param n The number of times to perform the multiplication. * @param factor The scalar to multiply with in each iteration. */ - void repeatedMultiplyWithFactor(Environment const& env, std::vector& x, std::vector const* b, uint64_t n, ValueType factor) const; + void repeatedMultiplyWithFactor(Environment const& env, std::vector& x, std::vector const* b, uint64_t n, + SolutionType factor) const; /*! * Performs repeated matrix-vector multiplication x' = A*(factor * x) + b, minimizes/maximizes over the row groups @@ -149,23 +151,23 @@ class Multiplier { * @param n The number of times to perform the multiplication. * @param factor The scalar to multiply with in each iteration. */ - void repeatedMultiplyAndReduceWithFactor(Environment const& env, OptimizationDirection const& dir, std::vector& x, - std::vector const* b, uint64_t n, ValueType factor) const; + void repeatedMultiplyAndReduceWithFactor(Environment const& env, OptimizationDirection const& dir, std::vector& x, + std::vector const* b, uint64_t n, SolutionType factor) const; protected: - std::vector& provideCachedVector(uint64_t size) const; + std::vector& provideCachedVector(uint64_t size) const; - mutable std::unique_ptr> cachedVector; + mutable std::unique_ptr> cachedVector; storm::storage::SparseMatrix const& matrix; }; -template +template class MultiplierFactory { public: MultiplierFactory() = default; ~MultiplierFactory() = default; - std::unique_ptr> create(Environment const& env, storm::storage::SparseMatrix const& matrix); + std::unique_ptr> create(Environment const& env, storm::storage::SparseMatrix const& matrix); }; } // namespace solver diff --git a/src/storm/solver/multiplier/ViOperatorMultiplier.cpp b/src/storm/solver/multiplier/ViOperatorMultiplier.cpp index fce6106637..fc85ce4cd1 100644 --- a/src/storm/solver/multiplier/ViOperatorMultiplier.cpp +++ b/src/storm/solver/multiplier/ViOperatorMultiplier.cpp @@ -1,5 +1,6 @@ #include "ViOperatorMultiplier.h" +#include "storm/adapters/IntervalAdapter.h" #include "storm/adapters/RationalNumberAdapter.h" #include "storm/exceptions/NotSupportedException.h" #include "storm/solver/helper/ValueIterationOperator.h" @@ -142,14 +143,15 @@ class PlainMultiplicationBackend { } // namespace detail -template -ViOperatorMultiplier::ViOperatorMultiplier(storm::storage::SparseMatrix const& matrix) - : Multiplier(matrix) { +template +ViOperatorMultiplier::ViOperatorMultiplier(storm::storage::SparseMatrix const& matrix) + : Multiplier(matrix) { // Intentionally left empty. } -template -typename ViOperatorMultiplier::ViOpT& ViOperatorMultiplier::initialize() const { +template +typename ViOperatorMultiplier::ViOpT& +ViOperatorMultiplier::initialize() const { if (!viOperatorFwd) { return initialize(false); // default to backward operator } else { @@ -157,8 +159,9 @@ typename ViOperatorMultiplier::ViOpT& ViOperatorM } } -template -typename ViOperatorMultiplier::ViOpT& ViOperatorMultiplier::initialize(bool backwards) const { +template +typename ViOperatorMultiplier::ViOpT& +ViOperatorMultiplier::initialize(bool backwards) const { auto& viOp = backwards ? viOperatorBwd : viOperatorFwd; if (!viOp) { viOp = std::make_unique(); @@ -171,9 +174,9 @@ typename ViOperatorMultiplier::ViOpT& ViOperatorM return *viOp; } -template -void ViOperatorMultiplier::multiply(Environment const& env, std::vector const& x, std::vector const* b, - std::vector& result) const { +template +void ViOperatorMultiplier::multiply(Environment const& env, std::vector const& x, + std::vector const* b, std::vector& result) const { if (&result == &x) { auto& tmpResult = this->provideCachedVector(x.size()); multiply(env, x, b, tmpResult); @@ -181,7 +184,7 @@ void ViOperatorMultiplier::multiply(Environment c return; } auto const& viOp = initialize(); - detail::PlainMultiplicationBackend backend(result); + detail::PlainMultiplicationBackend backend(result); // Below, we just add 'result' as a dummy argument to the apply method. // The backend already takes care of filling the result vector while processing the rows. if (b) { @@ -191,12 +194,12 @@ void ViOperatorMultiplier::multiply(Environment c } } -template -void ViOperatorMultiplier::multiplyGaussSeidel(Environment const& /*env*/, std::vector& x, - std::vector const* b, bool backwards) const { +template +void ViOperatorMultiplier::multiplyGaussSeidel(Environment const& /*env*/, std::vector& x, + std::vector const* b, bool backwards) const { STORM_LOG_THROW(TrivialRowGrouping, storm::exceptions::NotSupportedException, "This multiplier does not support multiplications without reduction when invoked with non-trivial row groups"); - detail::MultiplierBackend backend; + detail::MultiplierBackend backend; auto const& viOp = initialize(backwards); if (b) { viOp.applyInPlace(x, *b, backend); @@ -205,11 +208,12 @@ void ViOperatorMultiplier::multiplyGaussSeidel(En } } -template -void ViOperatorMultiplier::multiplyAndReduce(Environment const& env, OptimizationDirection const& dir, - std::vector const& rowGroupIndices, std::vector const& x, - std::vector const* b, std::vector& result, - std::vector* choices) const { +template +void ViOperatorMultiplier::multiplyAndReduce(Environment const& env, OptimizationDirection const& dir, + std::vector const& rowGroupIndices, + std::vector const& x, std::vector const* b, + std::vector& result, + std::vector* choices) const { if (&result == &x) { auto& tmpResult = this->provideCachedVector(x.size()); multiplyAndReduce(env, dir, rowGroupIndices, x, b, tmpResult, choices); @@ -219,37 +223,44 @@ void ViOperatorMultiplier::multiplyAndReduce(Envi STORM_LOG_THROW(&rowGroupIndices == &this->matrix.getRowGroupIndices(), storm::exceptions::NotSupportedException, "The row group indices must be the same as the ones stored in the matrix of this multiplier"); auto const& viOp = initialize(); - auto apply = [&](BT& backend) { - if (b) { - viOp.apply(x, result, *b, backend); + auto apply = [&](BT& backend, OptimizationDirection od) { + if (od == OptimizationDirection::Minimize) { + if (b) { + viOp.template applyRobust(x, result, *b, backend); + } else { + viOp.template applyRobust(x, result, storm::utility::zero(), backend); + } } else { - viOp.apply(x, result, storm::utility::zero(), backend); + if (b) { + viOp.template applyRobust(x, result, *b, backend); + } else { + viOp.template applyRobust(x, result, storm::utility::zero(), backend); + } } }; if (storm::solver::minimize(dir)) { if (choices) { - detail::MultiplierBackend backend(*choices, this->matrix.getRowGroupIndices()); - apply(backend); + detail::MultiplierBackend backend(*choices, this->matrix.getRowGroupIndices()); + apply(backend, OptimizationDirection::Maximize); } else { - detail::MultiplierBackend backend; - apply(backend); + detail::MultiplierBackend backend; + apply(backend, OptimizationDirection::Maximize); } } else { if (choices) { - detail::MultiplierBackend backend(*choices, this->matrix.getRowGroupIndices()); - apply(backend); + detail::MultiplierBackend backend(*choices, this->matrix.getRowGroupIndices()); + apply(backend, OptimizationDirection::Minimize); } else { - detail::MultiplierBackend backend; - apply(backend); + detail::MultiplierBackend backend; + apply(backend, OptimizationDirection::Minimize); } } } -template -void ViOperatorMultiplier::multiplyAndReduceGaussSeidel(Environment const& env, OptimizationDirection const& dir, - std::vector const& rowGroupIndices, std::vector& x, - std::vector const* b, std::vector* choices, - bool backwards) const { +template +void ViOperatorMultiplier::multiplyAndReduceGaussSeidel( + Environment const& env, OptimizationDirection const& dir, std::vector const& rowGroupIndices, std::vector& x, + std::vector const* b, std::vector* choices, bool backwards) const { STORM_LOG_THROW(&rowGroupIndices == &this->matrix.getRowGroupIndices(), storm::exceptions::NotSupportedException, "The row group indices must be the same as the ones stored in the matrix of this multiplier"); auto const& viOp = initialize(backwards); @@ -262,28 +273,28 @@ void ViOperatorMultiplier::multiplyAndReduceGauss }; if (storm::solver::minimize(dir)) { if (choices) { - detail::MultiplierBackend backend(*choices, this->matrix.getRowGroupIndices()); + detail::MultiplierBackend backend(*choices, this->matrix.getRowGroupIndices()); apply(backend); } else { - detail::MultiplierBackend backend; + detail::MultiplierBackend backend; apply(backend); } } else { if (choices) { - detail::MultiplierBackend backend(*choices, this->matrix.getRowGroupIndices()); + detail::MultiplierBackend backend(*choices, this->matrix.getRowGroupIndices()); apply(backend); } else { - detail::MultiplierBackend backend; + detail::MultiplierBackend backend; apply(backend); } } } -template -void ViOperatorMultiplier::clearCache() const { +template +void ViOperatorMultiplier::clearCache() const { viOperatorBwd.reset(); viOperatorFwd.reset(); - Multiplier::clearCache(); + Multiplier::clearCache(); }; template class ViOperatorMultiplier; @@ -292,4 +303,10 @@ template class ViOperatorMultiplier; template class ViOperatorMultiplier; template class ViOperatorMultiplier; +template class ViOperatorMultiplier; +template class ViOperatorMultiplier; + +template class ViOperatorMultiplier; +template class ViOperatorMultiplier; + } // namespace storm::solver diff --git a/src/storm/solver/multiplier/ViOperatorMultiplier.h b/src/storm/solver/multiplier/ViOperatorMultiplier.h index 209ff9ee01..daeae609d3 100644 --- a/src/storm/solver/multiplier/ViOperatorMultiplier.h +++ b/src/storm/solver/multiplier/ViOperatorMultiplier.h @@ -14,25 +14,26 @@ class SparseMatrix; namespace solver { -template -class ViOperatorMultiplier : public Multiplier { +template +class ViOperatorMultiplier : public Multiplier { public: ViOperatorMultiplier(storm::storage::SparseMatrix const& matrix); virtual ~ViOperatorMultiplier() = default; - virtual void multiply(Environment const& env, std::vector const& x, std::vector const* b, - std::vector& result) const override; - virtual void multiplyGaussSeidel(Environment const& env, std::vector& x, std::vector const* b, bool backwards = true) const override; + virtual void multiply(Environment const& env, std::vector const& x, std::vector const* b, + std::vector& result) const override; + virtual void multiplyGaussSeidel(Environment const& env, std::vector& x, std::vector const* b, + bool backwards = true) const override; virtual void multiplyAndReduce(Environment const& env, OptimizationDirection const& dir, std::vector const& rowGroupIndices, - std::vector const& x, std::vector const* b, std::vector& result, + std::vector const& x, std::vector const* b, std::vector& result, std::vector* choices = nullptr) const override; virtual void multiplyAndReduceGaussSeidel(Environment const& env, OptimizationDirection const& dir, std::vector const& rowGroupIndices, - std::vector& x, std::vector const* b, std::vector* choices = nullptr, + std::vector& x, std::vector const* b, std::vector* choices = nullptr, bool backwards = true) const override; virtual void clearCache() const override; private: - using ViOpT = storm::solver::helper::ValueIterationOperator; + using ViOpT = storm::solver::helper::ValueIterationOperator; ViOpT& initialize() const; ViOpT& initialize(bool backwards) const; diff --git a/src/storm/storage/BitVector.cpp b/src/storm/storage/BitVector.cpp index e7f84b710e..6711fb05aa 100644 --- a/src/storm/storage/BitVector.cpp +++ b/src/storm/storage/BitVector.cpp @@ -721,6 +721,10 @@ std::vector BitVector::getNumberOfSetBitsBeforeIndices() const { } ++currentNumberOfSetBits; } + while (lastIndex < this->size()) { + bitsSetBeforeIndices.push_back(currentNumberOfSetBits); + ++lastIndex; + } return bitsSetBeforeIndices; } diff --git a/src/storm/storage/Distribution.cpp b/src/storm/storage/Distribution.cpp index 8ed053f11e..77b771e9f3 100644 --- a/src/storm/storage/Distribution.cpp +++ b/src/storm/storage/Distribution.cpp @@ -230,5 +230,10 @@ template std::ostream& operator<<(std::ostream& out, Distribution; template std::ostream& operator<<(std::ostream& out, Distribution const& distribution); +template class Distribution; +template std::ostream& operator<<(std::ostream& out, Distribution const& distribution); +template class Distribution; +template std::ostream& operator<<(std::ostream& out, Distribution const& distribution); + } // namespace storage } // namespace storm diff --git a/src/storm/storage/MaximalEndComponentDecomposition.cpp b/src/storm/storage/MaximalEndComponentDecomposition.cpp index 1df4402f1d..f1d42eb903 100644 --- a/src/storm/storage/MaximalEndComponentDecomposition.cpp +++ b/src/storm/storage/MaximalEndComponentDecomposition.cpp @@ -150,6 +150,8 @@ void MaximalEndComponentDecomposition::performMaximalEndComponentDeco storm::storage::SparseMatrix const& backwardTransitions, storm::OptionalRef states, storm::OptionalRef choices) { + STORM_LOG_ASSERT(!states.has_value() || transitionMatrix.getRowGroupCount() == states->size(), "Unexpected size of states bitvector."); + STORM_LOG_ASSERT(!choices.has_value() || transitionMatrix.getRowCount() == choices->size(), "Unexpected size of choices bitvector."); // Get some data for convenient access. auto const& nondeterministicChoiceIndices = transitionMatrix.getRowGroupIndices(); @@ -252,6 +254,10 @@ template class MaximalEndComponentDecomposition; template MaximalEndComponentDecomposition::MaximalEndComponentDecomposition( storm::models::sparse::NondeterministicModel const& model); +template class MaximalEndComponentDecomposition; +template MaximalEndComponentDecomposition::MaximalEndComponentDecomposition( + storm::models::sparse::NondeterministicModel const& model); + template class MaximalEndComponentDecomposition; template MaximalEndComponentDecomposition::MaximalEndComponentDecomposition( storm::models::sparse::NondeterministicModel const& model); diff --git a/src/storm/storage/Scheduler.cpp b/src/storm/storage/Scheduler.cpp index a5db7a903c..7b772ecb9c 100644 --- a/src/storm/storage/Scheduler.cpp +++ b/src/storm/storage/Scheduler.cpp @@ -76,6 +76,13 @@ bool Scheduler::isChoiceSelected(BitVector const& selectedStates, uin return true; } +template +bool Scheduler::isChoiceSelected(uint64_t modelState, uint64_t memoryState) const { + STORM_LOG_ASSERT(memoryState < getNumberOfMemoryStates(), "Illegal memory state index"); + STORM_LOG_ASSERT(modelState < schedulerChoices[memoryState].size(), "Illegal model state index"); + return schedulerChoices[memoryState][modelState].isDefined(); +} + template void Scheduler::clearChoice(uint_fast64_t modelState, uint_fast64_t memoryState) { STORM_LOG_ASSERT(memoryState < getNumberOfMemoryStates(), "Illegal memory state index"); @@ -172,6 +179,19 @@ boost::optional const& Scheduler::ge return memoryStructure; } +template +Scheduler Scheduler::getMemorylessSchedulerForMemoryState(uint64_t memoryState) const { + STORM_LOG_ASSERT(memoryState < getNumberOfMemoryStates(), "Illegal memory state index"); + + Scheduler memorylessScheduler(getNumberOfModelStates()); + for (uint64_t modelState = 0; modelState < getNumberOfModelStates(); ++modelState) { + if (schedulerChoices[memoryState][modelState].isDefined()) { + memorylessScheduler.setChoice(schedulerChoices[memoryState][modelState], modelState); + } + } + return memorylessScheduler; +} + template void Scheduler::printToStream(std::ostream& out, std::shared_ptr> model, bool skipUniqueChoices, bool skipDontCareStates) const { @@ -391,6 +411,7 @@ template class Scheduler; template class Scheduler; template class Scheduler; template class Scheduler; +template class Scheduler; } // namespace storage } // namespace storm diff --git a/src/storm/storage/Scheduler.h b/src/storm/storage/Scheduler.h index 4bf67f486a..8eb200b123 100644 --- a/src/storm/storage/Scheduler.h +++ b/src/storm/storage/Scheduler.h @@ -40,6 +40,11 @@ class Scheduler { */ bool isChoiceSelected(BitVector const& selectedStates, uint64_t memoryState = 0) const; + /*! + * Is the scheduler defined on the given state + */ + bool isChoiceSelected(uint64_t modelState, uint64_t memoryState = 0) const; + /*! * Clears the choice defined by the scheduler for the given state. * @@ -114,6 +119,13 @@ class Scheduler { */ boost::optional const& getMemoryStructure() const; + /*! + * Retrieves a memoryless scheduler that corresponds to the given memory state. + * + * @param memoryState the memory state to fix + */ + Scheduler getMemorylessSchedulerForMemoryState(uint64_t memoryState = 0) const; + /*! * Returns a copy of this scheduler with the new value type */ diff --git a/src/storm/storage/SchedulerChoice.cpp b/src/storm/storage/SchedulerChoice.cpp index 3c89c96efc..668052c88b 100644 --- a/src/storm/storage/SchedulerChoice.cpp +++ b/src/storm/storage/SchedulerChoice.cpp @@ -75,6 +75,8 @@ template class SchedulerChoice; template std::ostream& operator<<(std::ostream& out, SchedulerChoice const& schedulerChoice); template class SchedulerChoice; template std::ostream& operator<<(std::ostream& out, SchedulerChoice const& schedulerChoice); +template class SchedulerChoice; +template std::ostream& operator<<(std::ostream& out, SchedulerChoice const& schedulerChoice); } // namespace storage } // namespace storm diff --git a/src/storm/storage/SparseMatrix.cpp b/src/storm/storage/SparseMatrix.cpp index d64bfc0f94..c10db5a6fd 100644 --- a/src/storm/storage/SparseMatrix.cpp +++ b/src/storm/storage/SparseMatrix.cpp @@ -672,15 +672,20 @@ typename SparseMatrix::index_type SparseMatrix::getEntryCo template typename SparseMatrix::index_type SparseMatrix::getRowGroupEntryCount(index_type const group) const { - index_type result = 0; if (!this->hasTrivialRowGrouping()) { + index_type result = 0; for (auto row : this->getRowGroupIndices(group)) { result += (this->rowIndications[row + 1] - this->rowIndications[row]); } + return result; } else { - result += (this->rowIndications[group + 1] - this->rowIndications[group]); + return (this->rowIndications[group + 1] - this->rowIndications[group]); } - return result; +} + +template +typename SparseMatrix::index_type SparseMatrix::getRowEntryCount(index_type const row) const { + return (this->rowIndications[row + 1] - this->rowIndications[row]); } template @@ -2532,5 +2537,19 @@ template bool SparseMatrix::isSubmatrixOf(SparseMatrix::isSubmatrixOf(SparseMatrix const& matrix) const; +// Rational Intervals +template std::vector SparseMatrix::getPointwiseProductRowSumVector( + storm::storage::SparseMatrix const& otherMatrix) const; +template class MatrixEntry::index_type, RationalInterval>; +template std::ostream& operator<<(std::ostream& out, MatrixEntry::index_type, RationalInterval> const& entry); +template class SparseMatrixBuilder; +template class SparseMatrix; +template std::ostream& operator<<(std::ostream& out, SparseMatrix const& matrix); +template std::vector SparseMatrix::getPointwiseProductRowSumVector( + storm::storage::SparseMatrix const& otherMatrix) const; +template bool SparseMatrix::isSubmatrixOf(SparseMatrix const& matrix) const; + +template bool SparseMatrix::isSubmatrixOf(SparseMatrix const& matrix) const; + } // namespace storage } // namespace storm diff --git a/src/storm/storage/SparseMatrix.h b/src/storm/storage/SparseMatrix.h index 5ccccceb85..f7eea9c4ce 100644 --- a/src/storm/storage/SparseMatrix.h +++ b/src/storm/storage/SparseMatrix.h @@ -531,6 +531,13 @@ class SparseMatrix { */ index_type getRowGroupEntryCount(index_type const group) const; + /*! + * Returns the number of entries in the given row of the matrix. + * @param row Which row + * @return Number of entries + */ + index_type getRowEntryCount(index_type const row) const; + /*! * Returns the cached number of nonzero entries in the matrix. * diff --git a/src/storm/storage/StronglyConnectedComponentDecomposition.cpp b/src/storm/storage/StronglyConnectedComponentDecomposition.cpp index 48808a2448..507339d3e7 100644 --- a/src/storm/storage/StronglyConnectedComponentDecomposition.cpp +++ b/src/storm/storage/StronglyConnectedComponentDecomposition.cpp @@ -343,5 +343,6 @@ template class StronglyConnectedComponentDecomposition; template class StronglyConnectedComponentDecomposition; template class StronglyConnectedComponentDecomposition; template class StronglyConnectedComponentDecomposition; +template class StronglyConnectedComponentDecomposition; } // namespace storm::storage diff --git a/src/storm/storage/bisimulation/BisimulationDecomposition.cpp b/src/storm/storage/bisimulation/BisimulationDecomposition.cpp index e8312956c9..faa5ba93ee 100644 --- a/src/storm/storage/bisimulation/BisimulationDecomposition.cpp +++ b/src/storm/storage/bisimulation/BisimulationDecomposition.cpp @@ -153,8 +153,8 @@ void BisimulationDecomposition::Options::checkAndSetMe storm::modelchecker::SparsePropositionalModelChecker checker(model); std::unique_ptr phiStatesCheckResult = checker.check(*leftSubformula); std::unique_ptr psiStatesCheckResult = checker.check(*rightSubformula); - phiStates = phiStatesCheckResult->asExplicitQualitativeCheckResult().getTruthValuesVector(); - psiStates = psiStatesCheckResult->asExplicitQualitativeCheckResult().getTruthValuesVector(); + phiStates = phiStatesCheckResult->template asExplicitQualitativeCheckResult().getTruthValuesVector(); + psiStates = psiStatesCheckResult->template asExplicitQualitativeCheckResult().getTruthValuesVector(); } else { optimalityType.reset(); } diff --git a/src/storm/storage/memorystructure/MemoryStructureBuilder.cpp b/src/storm/storage/memorystructure/MemoryStructureBuilder.cpp index 7f2d19b3c0..3de2e01489 100644 --- a/src/storm/storage/memorystructure/MemoryStructureBuilder.cpp +++ b/src/storm/storage/memorystructure/MemoryStructureBuilder.cpp @@ -135,9 +135,11 @@ MemoryStructure MemoryStructureBuilder::buildTrivial template class MemoryStructureBuilder; template class MemoryStructureBuilder>; +template class MemoryStructureBuilder>; template class MemoryStructureBuilder; template class MemoryStructureBuilder; template class MemoryStructureBuilder; +template class MemoryStructureBuilder; } // namespace storage } // namespace storm diff --git a/src/storm/storage/memorystructure/SparseModelMemoryProduct.cpp b/src/storm/storage/memorystructure/SparseModelMemoryProduct.cpp index fbb85aa97d..6bcea8b21a 100644 --- a/src/storm/storage/memorystructure/SparseModelMemoryProduct.cpp +++ b/src/storm/storage/memorystructure/SparseModelMemoryProduct.cpp @@ -571,9 +571,11 @@ SparseModelMemoryProductReverseData SparseModelMemoryProduct; template class SparseModelMemoryProduct>; +template class SparseModelMemoryProduct>; template class SparseModelMemoryProduct; template class SparseModelMemoryProduct; template class SparseModelMemoryProduct; +template class SparseModelMemoryProduct; } // namespace storage } // namespace storm diff --git a/src/storm/transformer/AddUncertainty.cpp b/src/storm/transformer/AddUncertainty.cpp index 64713ccfa1..a571208c97 100644 --- a/src/storm/transformer/AddUncertainty.cpp +++ b/src/storm/transformer/AddUncertainty.cpp @@ -17,15 +17,22 @@ template AddUncertainty::AddUncertainty(std::shared_ptr> const& originalModel) : origModel(originalModel) {} template -std::shared_ptr> AddUncertainty::transform(double additiveUncertainty, double minimalTransitionProbability) { +std::shared_ptr> AddUncertainty::transform(double additiveUncertainty, double minimalTransitionProbability, + uint64_t maxSuccessors) { // we first build the matrix and later copy the row grouping. auto newMatrixBuilder = storage::SparseMatrixBuilder(origModel->getTransitionMatrix().getRowCount(), origModel->getTransitionMatrix().getColumnCount(), origModel->getTransitionMatrix().getNonzeroEntryCount(), true, false); // Build transition matrix (without row grouping) for (uint64_t rowIndex = 0; rowIndex < origModel->getTransitionMatrix().getRowCount(); ++rowIndex) { - for (auto const& entry : origModel->getTransitionMatrix().getRow(rowIndex)) { - newMatrixBuilder.addNextValue(rowIndex, entry.getColumn(), addUncertainty(entry.getValue(), additiveUncertainty, minimalTransitionProbability)); + if (origModel->getTransitionMatrix().getRowEntryCount(rowIndex) <= maxSuccessors) { + for (auto const& entry : origModel->getTransitionMatrix().getRow(rowIndex)) { + newMatrixBuilder.addNextValue(rowIndex, entry.getColumn(), addUncertainty(entry.getValue(), additiveUncertainty, minimalTransitionProbability)); + } + } else { + for (auto const& entry : origModel->getTransitionMatrix().getRow(rowIndex)) { + newMatrixBuilder.addNextValue(rowIndex, entry.getColumn(), addUncertainty(entry.getValue(), 0, 0)); + } } } storm::storage::sparse::ModelComponents modelComponents(newMatrixBuilder.build(), origModel->getStateLabeling()); @@ -80,4 +87,5 @@ storm::Interval AddUncertainty::addUncertainty(ValueType const& vt, d } template class AddUncertainty; +template class AddUncertainty; } // namespace storm::transformer \ No newline at end of file diff --git a/src/storm/transformer/AddUncertainty.h b/src/storm/transformer/AddUncertainty.h index f31088c1ca..7e0ae97986 100644 --- a/src/storm/transformer/AddUncertainty.h +++ b/src/storm/transformer/AddUncertainty.h @@ -16,7 +16,8 @@ template class AddUncertainty { public: AddUncertainty(std::shared_ptr> const& originalModel); - std::shared_ptr> transform(double additiveUncertainty, double minimalValue = 0.0001); + std::shared_ptr> transform(double additiveUncertainty, double minimalValue = 0.0001, + uint64_t maxSuccessors = 10000000); private: storm::Interval addUncertainty(ValueType const& vt, double additiveUncertainty, double minimalValue); diff --git a/src/storm/transformer/MemoryIncorporation.cpp b/src/storm/transformer/MemoryIncorporation.cpp index 4f9526dc8f..d82f03914b 100644 --- a/src/storm/transformer/MemoryIncorporation.cpp +++ b/src/storm/transformer/MemoryIncorporation.cpp @@ -24,7 +24,8 @@ storm::storage::MemoryStructure getGoalMemory(SparseModelType const& model, stor "The subformula " << propositionalGoalStateFormula << " should be propositional."); storm::modelchecker::SparsePropositionalModelChecker mc(model); - storm::storage::BitVector goalStates = mc.check(propositionalGoalStateFormula)->asExplicitQualitativeCheckResult().getTruthValuesVector(); + storm::storage::BitVector goalStates = + mc.check(propositionalGoalStateFormula)->template asExplicitQualitativeCheckResult().getTruthValuesVector(); // Check if the formula is already satisfied for all initial states. In such a case the trivial memory structure suffices. if (model.getInitialStates().isSubsetOf(goalStates)) { diff --git a/src/storm/transformer/SubsystemBuilder.cpp b/src/storm/transformer/SubsystemBuilder.cpp index cbc6f8539a..c19983994e 100644 --- a/src/storm/transformer/SubsystemBuilder.cpp +++ b/src/storm/transformer/SubsystemBuilder.cpp @@ -225,6 +225,10 @@ template SubsystemBuilderReturnType> const& originalModel, storm::storage::BitVector const& subsystemStates, storm::storage::BitVector const& subsystemActions, bool keepUnreachableStates = true, SubsystemBuilderOptions options = SubsystemBuilderOptions()); +template SubsystemBuilderReturnType> buildSubsystem( + storm::models::sparse::Model> const& originalModel, + storm::storage::BitVector const& subsystemStates, storm::storage::BitVector const& subsystemActions, bool keepUnreachableStates = true, + SubsystemBuilderOptions options = SubsystemBuilderOptions()); template SubsystemBuilderReturnType buildSubsystem(storm::models::sparse::Model const& originalModel, storm::storage::BitVector const& subsystemStates, storm::storage::BitVector const& subsystemActions, bool keepUnreachableStates = true, @@ -238,6 +242,11 @@ template SubsystemBuilderReturnType buildSubsystem(storm::model storm::storage::BitVector const& subsystemStates, storm::storage::BitVector const& subsystemActions, bool keepUnreachableStates = true, SubsystemBuilderOptions options = SubsystemBuilderOptions()); +template SubsystemBuilderReturnType buildSubsystem(storm::models::sparse::Model const& originalModel, + storm::storage::BitVector const& subsystemStates, + storm::storage::BitVector const& subsystemActions, + bool keepUnreachableStates = true, + SubsystemBuilderOptions options = SubsystemBuilderOptions()); } // namespace transformer } // namespace storm diff --git a/src/storm/utility/ConstantsComparator.cpp b/src/storm/utility/ConstantsComparator.cpp index 5513626567..25a196ac66 100644 --- a/src/storm/utility/ConstantsComparator.cpp +++ b/src/storm/utility/ConstantsComparator.cpp @@ -64,6 +64,8 @@ template class ConstantsComparator; #endif template class ConstantsComparator; +template class ConstantsComparator; template class ConstantsComparator; +template class ConstantsComparator; } // namespace utility } // namespace storm diff --git a/src/storm/utility/NumberTraits.h b/src/storm/utility/NumberTraits.h index 0ef102199b..408471dfa6 100644 --- a/src/storm/utility/NumberTraits.h +++ b/src/storm/utility/NumberTraits.h @@ -1,5 +1,6 @@ #pragma once +#include "storm/adapters/IntervalForward.h" #include "storm/adapters/RationalFunctionForward.h" #include "storm/adapters/RationalNumberForward.h" @@ -40,6 +41,18 @@ struct NumberTraits { }; #endif +template<> +struct NumberTraits { + static const bool SupportsExponential = false; + static const bool IsExact = false; +}; + +template<> +struct NumberTraits { + static const bool SupportsExponential = false; + static const bool IsExact = true; +}; + template<> struct NumberTraits { static const bool SupportsExponential = false; diff --git a/src/storm/utility/RationalApproximation.cpp b/src/storm/utility/RationalApproximation.cpp new file mode 100644 index 0000000000..84700e5f76 --- /dev/null +++ b/src/storm/utility/RationalApproximation.cpp @@ -0,0 +1,156 @@ + +#include "storm/utility/RationalApproximation.h" + +#include "storm/adapters/RationalNumberAdapter.h" +#include "storm/utility/constants.h" +#include "storm/utility/macros.h" + +namespace storm::utility { + +storm::RationalNumber findRational(storm::RationalNumber const& lowerBound, bool lowerInclusive, storm::RationalNumber const& upperBound, bool upperInclusive) { + using Integer = typename storm::NumberTraits::IntegerType; + STORM_LOG_ASSERT(lowerBound < upperBound || (lowerBound == upperBound && lowerInclusive && upperInclusive), "Invalid interval for rational approximation."); + + // Handle negative numbers + if (auto const zero = storm::utility::zero(); lowerBound < zero) { + // check if zero is in the interval + if (upperBound > zero || (upperBound == zero && upperInclusive)) { + return storm::utility::zero(); + } else { + // all numbers in the interval are negative. We translate that to a positive problem and negate the result + return -findRational(-upperBound, upperInclusive, -lowerBound, lowerInclusive); + } + } + // At this point, the solution is known to be non-negative + + // We compute a path in the Stern-Brocot tree from the root to the node representing the simplest rational in the closed interval [lowerBound, upperBound] + // If the input interval is open on one or both sides, we traverse the tree further down until a suitable rational number is found + // @see https://en.wikipedia.org/wiki/Stern–Brocot_tree#A_tree_of_continued_fractions + // @see https://mathoverflow.net/a/424509 + // The path is encoded using a simple continued fraction representation. + // We take path[0] times the right child, path[1] times the left child, path[2] times the right child, etc, using path.back()-1 steps in the last direction. + std::vector path; // in simple continued fraction representation + auto l = lowerBound; + auto u = upperBound; + while (true) { + auto l_den = storm::utility::denominator(l); + auto u_den = storm::utility::denominator(u); + auto const [l_i, l_rem] = storm::utility::divide(storm::utility::numerator(l), l_den); + auto const [u_i, u_rem] = storm::utility::divide(storm::utility::numerator(u), u_den); + + path.push_back(std::min(l_i, u_i)); // insert tree traversal information + if (l_i == u_i && !storm::utility::isZero(l_rem) && !storm::utility::isZero(u_rem)) { + // continue traversing the tree + l = storm::utility::convertNumber(l_den) / l_rem; + u = storm::utility::convertNumber(u_den) / u_rem; + continue; + } + // Reaching this point means that we have found a node in the Stern-Brocot tree where the paths for lower and upper bound diverge. + // If there still is a remainder, we need to add one to the last entry of the path so that it correctly encodes the node we are referring to. + if (l_i != u_i && !storm::utility::isZero(l_i < u_i ? l_rem : u_rem)) { + path.back() += Integer(1); + } + + // Find out if we hit an interval boundary and whether we need to adapt this due to open intervals + bool const needAdjustLower = !lowerInclusive && path.back() == l_i && storm::utility::isZero(l_rem); + bool const needAdjustUpper = !upperInclusive && path.back() == u_i && storm::utility::isZero(u_rem); + if (needAdjustLower || needAdjustUpper) { + // handle for values of the "other" bound that does not need adjustment + auto const& o_i = needAdjustLower ? u_i : l_i; + auto const& o_rem = needAdjustLower ? u_rem : l_rem; + auto const& o_den = needAdjustLower ? u_den : l_den; + auto const& o_inclusive = needAdjustLower ? upperInclusive : lowerInclusive; + + // When adjusting lower bounds, we need to explore the right subtree to obtain a larger value than the current lower bound + // When adjusting upper bounds, we need to explore the left subtree to obtain a smaller value than the current upper bound + // Whether we currently look at left or right subtrees is determined by the parity of the index in the path: + // Path entries at even indices correspond to right moves (increasing value) and entries at odd indices correspond to left moves (decreasing value) + bool const currentDirectionIsIncreasing = (path.size() - 1) % 2 == 0; + bool const adjustInCurrentDirection = (needAdjustLower && currentDirectionIsIncreasing) || (needAdjustUpper && !currentDirectionIsIncreasing); + // Below, we navigate through the Stern-Brocot tree by adapting the path + // path.back() += 1; extends the path to a child in the "current direction" + // path.back() -= 1; path.emplace_back(2); extends the path to a child in the "counter direction" + if (adjustInCurrentDirection) { + STORM_LOG_ASSERT(path.back() <= o_i, "Unexpected case when navigating the Stern-Brocot tree."); + if (path.back() + Integer(1) < o_i || (path.back() + Integer(1) == o_i && !storm::utility::isZero(o_rem))) { + // In this case, the next child (in the current direction) is inside the interval, so we can just take that + path.back() += Integer(1); + } else if (path.back() + Integer(1) == o_i && storm::utility::isZero(o_rem)) { + // In this case, the next child coincides with the other boundary + if (o_inclusive) { + path.back() += Integer(1); // add next child + } else { + // We first take one child in the current direction and then one child in the counter direction. + // path.back() += 1; path.back() -= 1; // cancels out + path.emplace_back(2); + } + } else { + // The following assertion holds because path.back() > o_i is not possible due to the way we constructed the path above + // and if there would be no remainder, the other boundary would be hit as well (i.e. we would have an empty interval (a,a). + STORM_LOG_ASSERT(path.back() == o_i && !storm::utility::isZero(o_rem), "Unexpected case when navigating the Stern-Brocot tree."); + // In this case, we need to append one child in the current direction and multiple children in the counter direction based on the continued + // fraction representation of the other boundary + auto const [o_i2, o_rem2] = storm::utility::divide(o_den, o_rem); + // path.back() += 1; path.back() -= 1; // cancels out + path.push_back(o_i2); + if (!storm::utility::isZero(o_rem2)) { + // If there still is a remainder, we add one to the last entry of the path so that it correctly encodes the node we are referring to. + path.back() += Integer(1); + } else if (!o_inclusive) { + // If there is no remainder, we are exactly on the other boundary. If that boundary is also excluded, we need to add one more step. + path.back() += Integer(1); + } + } + } else { + // Adjusting a bound in the counter direction can only happen if the other bound still has a remainder + // Otherwise, we would have also hit that bound + STORM_LOG_ASSERT(o_i == path.back() - Integer(1), "Unexpected case when navigating the Stern-Brocot tree."); + STORM_LOG_ASSERT(!storm::utility::isZero(o_rem), "Unexpected case when navigating the Stern-Brocot tree."); + auto const [o_i2, o_rem2] = storm::utility::divide(o_den, o_rem); + path.back() -= Integer(1); // necessary in all cases + if (o_i2 > Integer(2) || (o_i2 == Integer(2) && !storm::utility::isZero(o_rem2))) { + // In this case, the next child (in the counter direction) is inside the interval, so we can just take that + path.emplace_back(2); + } else if (o_i2 == Integer(2) && storm::utility::isZero(o_rem2)) { + // In this case, the next child in counter direction coincides with the other boundary + if (o_inclusive) { + path.emplace_back(2); + } else { + // We first take one child in the counter direction and then one child in the current direction. + path.emplace_back(1); + path.emplace_back(2); + } + } else { + STORM_LOG_ASSERT(o_i2 == Integer(1) && !storm::utility::isZero(o_rem2), "Unexpected case when navigating the Stern-Brocot tree."); + // In this case, we need to append one child in the counter direction and multiple children in the current direction based on the continued + // fraction representation of the other boundary + auto const [o_i3, o_rem3] = storm::utility::divide(o_rem, o_rem2); + path.emplace_back(1); + path.push_back(o_i3); + if (!storm::utility::isZero(o_rem3)) { + // If there still is a remainder, we add one to the last entry of the path so that it correctly encodes the node we are referring to. + path.back() += Integer(1); + } else if (!o_inclusive) { + // If there is no remainder, we are exactly on the other boundary. If that boundary is also excluded, we need to add one more step. + path.back() += Integer(1); + } + } + } + } + break; + } + + // Now, construct the rational number from the path + auto it = path.rbegin(); + auto result = storm::utility::convertNumber(*it); + for (++it; it != path.rend(); ++it) { + result = storm::utility::convertNumber(*it) + storm::utility::one() / result; + } + return result; + + STORM_LOG_ASSERT(result > lowerBound || (lowerInclusive && result == lowerBound), "Result is below lower bound."); + STORM_LOG_ASSERT(result < upperBound || (upperInclusive && result == upperBound), "Result is above upper bound."); + return result; +} + +} // namespace storm::utility \ No newline at end of file diff --git a/src/storm/utility/RationalApproximation.h b/src/storm/utility/RationalApproximation.h new file mode 100644 index 0000000000..17bae03928 --- /dev/null +++ b/src/storm/utility/RationalApproximation.h @@ -0,0 +1,18 @@ +#pragma once + +#include "storm/adapters/RationalNumberForward.h" + +namespace storm::utility { + +/*! + * Finds the "simplest" rational number in the given interval, where "simplest" means having the smallest denominator + * @pre lowerBound < upperBound or (lowerBound == upperBound and lowerInclusive and upperInclusive) + * @param lowerBound The lower bound of the interval + * @param lowerInclusive Whether the lower bound itself is included + * @param upperBound the upper bound of the interval + * @param upperInclusive Whether the upper bound itself is included + * @return the rational number in the given interval with the smallest denominator + */ +storm::RationalNumber findRational(storm::RationalNumber const& lowerBound, bool lowerInclusive, storm::RationalNumber const& upperBound, bool upperInclusive); + +} // namespace storm::utility \ No newline at end of file diff --git a/src/storm/utility/builder.cpp b/src/storm/utility/builder.cpp index 0e67de3fb8..1a19e27316 100644 --- a/src/storm/utility/builder.cpp +++ b/src/storm/utility/builder.cpp @@ -43,12 +43,18 @@ template std::shared_ptr> buildModelFromCom template std::shared_ptr>> buildModelFromComponents( storm::models::ModelType modelType, storm::storage::sparse::ModelComponents>&& components); +template std::shared_ptr>> +buildModelFromComponents( + storm::models::ModelType modelType, + storm::storage::sparse::ModelComponents>&& components); template std::shared_ptr> buildModelFromComponents( storm::models::ModelType modelType, storm::storage::sparse::ModelComponents&& components); template std::shared_ptr> buildModelFromComponents( storm::models::ModelType modelType, storm::storage::sparse::ModelComponents&& components); template std::shared_ptr> buildModelFromComponents( storm::models::ModelType modelType, storm::storage::sparse::ModelComponents&& components); +template std::shared_ptr> buildModelFromComponents( + storm::models::ModelType modelType, storm::storage::sparse::ModelComponents&& components); } // namespace builder } // namespace utility diff --git a/src/storm/utility/constants.cpp b/src/storm/utility/constants.cpp index c2d63d1cf5..c0096dac71 100644 --- a/src/storm/utility/constants.cpp +++ b/src/storm/utility/constants.cpp @@ -37,6 +37,11 @@ bool isOne(ValueType const& a) { template bool isZero(ValueType const& a) { + static_assert( + not std::is_same_v< + ValueType, + __gmp_expr<__mpq_struct, __gmp_binary_expr<__gmp_expr<__mpq_struct, __mpq_struct>, __gmp_expr<__mpq_struct, __mpq_struct>, __gmp_binary_minus>>>, + "Missing template instantiation!"); return a == zero(); } @@ -809,6 +814,11 @@ bool isConstant(storm::Interval const& a) { return a.isPointInterval(); } +template<> +bool isConstant(storm::RationalInterval const& a) { + return a.isPointInterval(); +} + template<> bool isInfinity(storm::RationalFunction const& a) { // FIXME: this should be treated more properly. @@ -1009,6 +1019,11 @@ storm::Interval convertNumber(uint64_t const& number) { return storm::Interval(convertNumber(number)); } +template<> +storm::RationalInterval convertNumber(double const& number) { + return storm::RationalInterval(convertNumber(number)); +} + #if defined(STORM_HAVE_GMP) template<> storm::Interval convertNumber(storm::GmpRationalNumber const& n) { @@ -1020,6 +1035,17 @@ storm::GmpRationalNumber convertNumber(storm::Interval const& number) { STORM_LOG_ASSERT(number.isPointInterval(), "Interval must be a point interval to convert"); return convertNumber(number.lower()); } + +template<> +storm::RationalInterval convertNumber(storm::GmpRationalNumber const& n) { + return storm::RationalInterval(convertNumber(n)); +} + +template<> +storm::GmpRationalNumber convertNumber(storm::RationalInterval const& number) { + STORM_LOG_ASSERT(number.isPointInterval(), "Interval must be a point interval to convert"); + return convertNumber(number.lower()); +} #endif #if defined(STORM_HAVE_CLN) @@ -1033,6 +1059,17 @@ storm::ClnRationalNumber convertNumber(storm::Interval const& number) { STORM_LOG_ASSERT(number.isPointInterval(), "Interval must be a point interval to convert"); return convertNumber(number.lower()); } + +template<> +storm::RationalInterval convertNumber(storm::ClnRationalNumber const& n) { + return storm::RationalInterval(convertNumber(n)); +} + +template<> +storm::ClnRationalNumber convertNumber(storm::RationalInterval const& number) { + STORM_LOG_ASSERT(number.isPointInterval(), "Interval must be a point interval to convert"); + return convertNumber(number.lower()); +} #endif template<> @@ -1041,6 +1078,12 @@ double convertNumber(storm::Interval const& number) { return number.lower(); } +template<> +double convertNumber(storm::RationalInterval const& number) { + STORM_LOG_ASSERT(number.isPointInterval(), "Rational interval must be a point interval to convert"); + return convertNumber(number.lower()); +} + template<> storm::Interval abs(storm::Interval const& interval) { return interval.abs(); @@ -1053,6 +1096,11 @@ bool isApproxEqual(storm::Interval const& a, storm::Interval const& b, storm::In isApproxEqual(a.upper(), b.upper(), precision.center(), relative); } +template<> +storm::RationalInterval abs(storm::RationalInterval const& interval) { + return interval.abs(); +} + // Explicit instantiations. // double @@ -1219,5 +1267,14 @@ template bool isBetween(Interval const&, Interval const&, Interval const& value, template std::string to_string(storm::Interval const& value); +// Instantiations for intervals. +template RationalInterval one(); +template RationalInterval zero(); +template bool isOne(RationalInterval const& value); +template bool isZero(RationalInterval const& value); +template bool isInfinity(RationalInterval const& value); +template bool isAlmostZero(RationalInterval const& value); + +template std::string to_string(storm::RationalInterval const& value); } // namespace utility } // namespace storm diff --git a/src/storm/utility/graph.cpp b/src/storm/utility/graph.cpp index e407834ecf..8964fde8ab 100644 --- a/src/storm/utility/graph.cpp +++ b/src/storm/utility/graph.cpp @@ -1962,6 +1962,10 @@ template storm::storage::BitVector performProb0E( template storm::storage::BitVector performProb0E( storm::models::sparse::NondeterministicModel> const& model, storm::storage::SparseMatrix const& backwardTransitions, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates); +template storm::storage::BitVector performProb0E( + storm::models::sparse::NondeterministicModel> const& model, + storm::storage::SparseMatrix const& backwardTransitions, storm::storage::BitVector const& phiStates, + storm::storage::BitVector const& psiStates); template storm::storage::BitVector performProb0E(storm::storage::SparseMatrix const& transitionMatrix, std::vector const& nondeterministicChoiceIndices, storm::storage::SparseMatrix const& backwardTransitions, storm::storage::BitVector const& phiStates, @@ -1973,6 +1977,10 @@ template storm::storage::BitVector performProb1A( template storm::storage::BitVector performProb1A( storm::models::sparse::NondeterministicModel> const& model, storm::storage::SparseMatrix const& backwardTransitions, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates); +template storm::storage::BitVector performProb1A( + storm::models::sparse::NondeterministicModel> const& model, + storm::storage::SparseMatrix const& backwardTransitions, storm::storage::BitVector const& phiStates, + storm::storage::BitVector const& psiStates); template storm::storage::BitVector performProb1A(storm::storage::SparseMatrix const& transitionMatrix, std::vector const& nondeterministicChoiceIndices, storm::storage::SparseMatrix const& backwardTransitions, storm::storage::BitVector const& phiStates, @@ -2264,6 +2272,143 @@ template ExplicitGameProb01Result performProb1(storm::storage::SparseMatrix getTopologicalSort(storm::storage::SparseMatrix const& matrix, std::vector const& firstStates); // End storm::interval +// Instantiations for storm::Rationalinterval + +template storm::storage::BitVector getReachableOneStep(storm::storage::SparseMatrix const& transitionMatrix, + storm::storage::BitVector const& initialStates); + +template storm::storage::BitVector getReachableStates(storm::storage::SparseMatrix const& transitionMatrix, + storm::storage::BitVector const& initialStates, storm::storage::BitVector const& constraintStates, + storm::storage::BitVector const& targetStates, bool useStepBound, uint_fast64_t maximalSteps, + boost::optional const& choiceFilter); + +template storm::storage::BitVector getBsccCover(storm::storage::SparseMatrix const& transitionMatrix); + +template bool hasCycle(storm::storage::SparseMatrix const& transitionMatrix, + boost::optional const& subsystem); + +template bool checkIfECWithChoiceExists(storm::storage::SparseMatrix const& transitionMatrix, + storm::storage::SparseMatrix const& backwardTransitions, + storm::storage::BitVector const& subsystem, storm::storage::BitVector const& choices); + +template std::vector getDistances(storm::storage::SparseMatrix const& transitionMatrix, + storm::storage::BitVector const& initialStates, boost::optional const& subsystem); + +template storm::storage::BitVector performProbGreater0(storm::storage::SparseMatrix const& backwardTransitions, + storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates, + bool useStepBound = false, uint_fast64_t maximalSteps = 0); + +template storm::storage::BitVector performProb1(storm::storage::SparseMatrix const& backwardTransitions, + storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates, + storm::storage::BitVector const& statesWithProbabilityGreater0); + +template storm::storage::BitVector performProb1(storm::storage::SparseMatrix const& backwardTransitions, + storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates); + +template std::pair performProb01( + storm::models::sparse::DeterministicModel const& model, storm::storage::BitVector const& phiStates, + storm::storage::BitVector const& psiStates); + +template std::pair performProb01( + storm::storage::SparseMatrix const& backwardTransitions, storm::storage::BitVector const& phiStates, + storm::storage::BitVector const& psiStates); + +template void computeSchedulerProbGreater0E(storm::storage::SparseMatrix const& transitionMatrix, + storm::storage::SparseMatrix const& backwardTransitions, + storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates, + storm::storage::Scheduler& scheduler, + boost::optional const& rowFilter); + +template void computeSchedulerProb0E(storm::storage::BitVector const& prob0EStates, + storm::storage::SparseMatrix const& transitionMatrix, + storm::storage::Scheduler& scheduler); + +template void computeSchedulerRewInf(storm::storage::BitVector const& rewInfStates, + storm::storage::SparseMatrix const& transitionMatrix, + storm::storage::SparseMatrix const& backwardTransitions, + storm::storage::Scheduler& scheduler); + +template void computeSchedulerProb1E(storm::storage::BitVector const& prob1EStates, + storm::storage::SparseMatrix const& transitionMatrix, + storm::storage::SparseMatrix const& backwardTransitions, + storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates, + storm::storage::Scheduler& scheduler, + boost::optional const& rowFilter = boost::none); + +template storm::storage::BitVector performProbGreater0E(storm::storage::SparseMatrix const& backwardTransitions, + storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates, + bool useStepBound = false, uint_fast64_t maximalSteps = 0); + +template storm::storage::BitVector performProb0A(storm::storage::SparseMatrix const& backwardTransitions, + storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates); + +template storm::storage::BitVector performProb1E(storm::storage::SparseMatrix const& transitionMatrix, + std::vector const& nondeterministicChoiceIndices, + storm::storage::SparseMatrix const& backwardTransitions, + storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates, + boost::optional const& choiceConstraint = boost::none); + +template storm::storage::BitVector performProb1E(storm::models::sparse::NondeterministicModel const& model, + storm::storage::SparseMatrix const& backwardTransitions, + storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates); + +template std::pair performProb01Max( + storm::storage::SparseMatrix const& transitionMatrix, std::vector const& nondeterministicChoiceIndices, + storm::storage::SparseMatrix const& backwardTransitions, storm::storage::BitVector const& phiStates, + storm::storage::BitVector const& psiStates); + +template std::pair performProb01Max( + storm::models::sparse::NondeterministicModel const& model, storm::storage::BitVector const& phiStates, + storm::storage::BitVector const& psiStates); + +template storm::storage::BitVector performProbGreater0A(storm::storage::SparseMatrix const& transitionMatrix, + std::vector const& nondeterministicChoiceIndices, + storm::storage::SparseMatrix const& backwardTransitions, + storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates, + bool useStepBound = false, uint_fast64_t maximalSteps = 0, + boost::optional const& choiceConstraint = boost::none); + +template storm::storage::BitVector performProb0E(storm::models::sparse::NondeterministicModel const& model, + storm::storage::SparseMatrix const& backwardTransitions, + storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates); + +template storm::storage::BitVector performProb0E(storm::storage::SparseMatrix const& transitionMatrix, + std::vector const& nondeterministicChoiceIndices, + storm::storage::SparseMatrix const& backwardTransitions, + storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates); + +template storm::storage::BitVector performProb1A(storm::storage::SparseMatrix const& transitionMatrix, + std::vector const& nondeterministicChoiceIndices, + storm::storage::SparseMatrix const& backwardTransitions, + storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates); + +template std::pair performProb01Min( + storm::storage::SparseMatrix const& transitionMatrix, std::vector const& nondeterministicChoiceIndices, + storm::storage::SparseMatrix const& backwardTransitions, storm::storage::BitVector const& phiStates, + storm::storage::BitVector const& psiStates); + +template std::pair performProb01Min( + storm::models::sparse::NondeterministicModel const& model, storm::storage::BitVector const& phiStates, + storm::storage::BitVector const& psiStates); + +template ExplicitGameProb01Result performProb0(storm::storage::SparseMatrix const& transitionMatrix, + std::vector const& player1RowGrouping, + storm::storage::SparseMatrix const& player1BackwardTransitions, + std::vector const& player2BackwardTransitions, storm::storage::BitVector const& phiStates, + storm::storage::BitVector const& psiStates, storm::OptimizationDirection const& player1Direction, + storm::OptimizationDirection const& player2Direction, storm::storage::ExplicitGameStrategyPair* strategyPair); + +template ExplicitGameProb01Result performProb1(storm::storage::SparseMatrix const& transitionMatrix, + std::vector const& player1RowGrouping, + storm::storage::SparseMatrix const& player1BackwardTransitions, + std::vector const& player2BackwardTransitions, storm::storage::BitVector const& phiStates, + storm::storage::BitVector const& psiStates, storm::OptimizationDirection const& player1Direction, + storm::OptimizationDirection const& player2Direction, storm::storage::ExplicitGameStrategyPair* strategyPair, + boost::optional const& player1Candidates); + +template std::vector getTopologicalSort(storm::storage::SparseMatrix const& matrix, + std::vector const& firstStates); +// End storm::Rationalinterval template storm::storage::BitVector getReachableStates(storm::storage::SparseMatrix const& transitionMatrix, storm::storage::BitVector const& initialStates, storm::storage::BitVector const& constraintStates, diff --git a/src/test/storm-pars/modelchecker/region/monotonicity/MonotonicityCheckerTest.cpp b/src/test/storm-pars/modelchecker/region/monotonicity/MonotonicityCheckerTest.cpp index 02989923e7..61369fd18a 100644 --- a/src/test/storm-pars/modelchecker/region/monotonicity/MonotonicityCheckerTest.cpp +++ b/src/test/storm-pars/modelchecker/region/monotonicity/MonotonicityCheckerTest.cpp @@ -50,7 +50,8 @@ TEST_F(MonotonicityCheckerTest, Simple1_larger_region) { storm::storage::BitVector psiStates; phiStates = storm::storage::BitVector(model->getTransitionMatrix().getRowCount(), true); storm::logic::EventuallyFormula formula = formulas[0]->asProbabilityOperatorFormula().getSubformula().asEventuallyFormula(); - psiStates = propositionalChecker.check(formula.getSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector(); + psiStates = + propositionalChecker.check(formula.getSubformula())->template asExplicitQualitativeCheckResult().getTruthValuesVector(); // Get the maybeStates std::pair statesWithProbability01 = storm::utility::graph::performProb01(model->getBackwardTransitions(), phiStates, psiStates); @@ -98,7 +99,8 @@ TEST_F(MonotonicityCheckerTest, Simple1_small_region) { storm::storage::BitVector psiStates; phiStates = storm::storage::BitVector(model->getTransitionMatrix().getRowCount(), true); storm::logic::EventuallyFormula formula = formulas[0]->asProbabilityOperatorFormula().getSubformula().asEventuallyFormula(); - psiStates = propositionalChecker.check(formula.getSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector(); + psiStates = + propositionalChecker.check(formula.getSubformula())->template asExplicitQualitativeCheckResult().getTruthValuesVector(); // Get the maybeStates std::pair statesWithProbability01 = storm::utility::graph::performProb01(model->getBackwardTransitions(), phiStates, psiStates); @@ -147,7 +149,8 @@ TEST_F(MonotonicityCheckerTest, Casestudy1) { storm::storage::BitVector psiStates; phiStates = storm::storage::BitVector(model->getTransitionMatrix().getRowCount(), true); storm::logic::EventuallyFormula formula = formulas[0]->asProbabilityOperatorFormula().getSubformula().asEventuallyFormula(); - psiStates = propositionalChecker.check(formula.getSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector(); + psiStates = + propositionalChecker.check(formula.getSubformula())->template asExplicitQualitativeCheckResult().getTruthValuesVector(); // Get the maybeStates std::pair statesWithProbability01 = storm::utility::graph::performProb01(model->getBackwardTransitions(), phiStates, psiStates); @@ -200,7 +203,8 @@ TEST_F(MonotonicityCheckerTest, Casestudy2) { storm::storage::BitVector psiStates; phiStates = storm::storage::BitVector(model->getTransitionMatrix().getRowCount(), true); storm::logic::EventuallyFormula formula = formulas[0]->asProbabilityOperatorFormula().getSubformula().asEventuallyFormula(); - psiStates = propositionalChecker.check(formula.getSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector(); + psiStates = + propositionalChecker.check(formula.getSubformula())->template asExplicitQualitativeCheckResult().getTruthValuesVector(); // Get the maybeStates std::pair statesWithProbability01 = storm::utility::graph::performProb01(model->getBackwardTransitions(), phiStates, psiStates); @@ -254,7 +258,8 @@ TEST_F(MonotonicityCheckerTest, Casestudy3) { storm::storage::BitVector psiStates; phiStates = storm::storage::BitVector(model->getTransitionMatrix().getRowCount(), true); storm::logic::EventuallyFormula formula = formulas[0]->asProbabilityOperatorFormula().getSubformula().asEventuallyFormula(); - psiStates = propositionalChecker.check(formula.getSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector(); + psiStates = + propositionalChecker.check(formula.getSubformula())->template asExplicitQualitativeCheckResult().getTruthValuesVector(); // Get the maybeStates std::pair statesWithProbability01 = storm::utility::graph::performProb01(model->getBackwardTransitions(), phiStates, psiStates); diff --git a/src/test/storm-pars/modelchecker/region/monotonicity/OrderExtenderTest.cpp b/src/test/storm-pars/modelchecker/region/monotonicity/OrderExtenderTest.cpp index 0c865a12ea..a50109532e 100644 --- a/src/test/storm-pars/modelchecker/region/monotonicity/OrderExtenderTest.cpp +++ b/src/test/storm-pars/modelchecker/region/monotonicity/OrderExtenderTest.cpp @@ -144,7 +144,8 @@ TEST_F(OrderExtenderTest, Brp_with_bisimulation_on_matrix) { storm::storage::BitVector psiStates; phiStates = storm::storage::BitVector(model->getTransitionMatrix().getRowCount(), true); storm::logic::EventuallyFormula formula = formulas[0]->asProbabilityOperatorFormula().getSubformula().asEventuallyFormula(); - psiStates = propositionalChecker.check(formula.getSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector(); + psiStates = + propositionalChecker.check(formula.getSubformula())->template asExplicitQualitativeCheckResult().getTruthValuesVector(); // Get the maybeStates std::pair statesWithProbability01 = storm::utility::graph::performProb01(model->getBackwardTransitions(), phiStates, psiStates); @@ -191,7 +192,8 @@ TEST_F(OrderExtenderTest, Brp_without_bisimulation_on_matrix) { storm::storage::BitVector psiStates; phiStates = storm::storage::BitVector(model->getTransitionMatrix().getRowCount(), true); storm::logic::EventuallyFormula formula = formulas[0]->asProbabilityOperatorFormula().getSubformula().asEventuallyFormula(); - psiStates = propositionalChecker.check(formula.getSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector(); + psiStates = + propositionalChecker.check(formula.getSubformula())->template asExplicitQualitativeCheckResult().getTruthValuesVector(); // Get the maybeStates std::pair statesWithProbability01 = storm::utility::graph::performProb01(model->getBackwardTransitions(), phiStates, psiStates); @@ -268,7 +270,8 @@ TEST_F(OrderExtenderTest, simple1_on_matrix) { storm::storage::BitVector psiStates; phiStates = storm::storage::BitVector(model->getTransitionMatrix().getRowCount(), true); storm::logic::EventuallyFormula formula = formulas[0]->asProbabilityOperatorFormula().getSubformula().asEventuallyFormula(); - psiStates = propositionalChecker.check(formula.getSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector(); + psiStates = + propositionalChecker.check(formula.getSubformula())->template asExplicitQualitativeCheckResult().getTruthValuesVector(); // Get the maybeStates std::pair statesWithProbability01 = storm::utility::graph::performProb01(model->getBackwardTransitions(), phiStates, psiStates); @@ -359,7 +362,8 @@ TEST_F(OrderExtenderTest, casestudy1_on_matrix) { storm::storage::BitVector psiStates; phiStates = storm::storage::BitVector(model->getTransitionMatrix().getRowCount(), true); storm::logic::EventuallyFormula formula = formulas[0]->asProbabilityOperatorFormula().getSubformula().asEventuallyFormula(); - psiStates = propositionalChecker.check(formula.getSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector(); + psiStates = + propositionalChecker.check(formula.getSubformula())->template asExplicitQualitativeCheckResult().getTruthValuesVector(); // Get the maybeStates std::pair statesWithProbability01 = storm::utility::graph::performProb01(model->getBackwardTransitions(), phiStates, psiStates); @@ -412,7 +416,8 @@ TEST_F(OrderExtenderTest, casestudy2_on_matrix) { storm::storage::BitVector psiStates; phiStates = storm::storage::BitVector(model->getTransitionMatrix().getRowCount(), true); storm::logic::EventuallyFormula formula = formulas[0]->asProbabilityOperatorFormula().getSubformula().asEventuallyFormula(); - psiStates = propositionalChecker.check(formula.getSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector(); + psiStates = + propositionalChecker.check(formula.getSubformula())->template asExplicitQualitativeCheckResult().getTruthValuesVector(); // Get the maybeStates std::pair statesWithProbability01 = storm::utility::graph::performProb01(model->getBackwardTransitions(), phiStates, psiStates); diff --git a/src/test/storm-pars/transformer/IntervalEndComponentPreserverCheckTest.cpp b/src/test/storm-pars/transformer/IntervalEndComponentPreserverCheckTest.cpp index b0e51a0fe8..5c0e9e5ba4 100644 --- a/src/test/storm-pars/transformer/IntervalEndComponentPreserverCheckTest.cpp +++ b/src/test/storm-pars/transformer/IntervalEndComponentPreserverCheckTest.cpp @@ -41,7 +41,7 @@ void testModelInterval(std::string programFile, std::string formulaAsString, std storm::modelchecker::SparsePropositionalModelChecker> propositionalChecker(*dtmc); storm::storage::BitVector psiStates = propositionalChecker.check(checkTask.getFormula().asProbabilityOperatorFormula().getSubformula().asEventuallyFormula().getSubformula()) - ->asExplicitQualitativeCheckResult() + ->template asExplicitQualitativeCheckResult() .getTruthValuesVector(); std::vector target(model->getNumberOfStates(), storm::utility::zero()); diff --git a/src/test/storm-permissive/analysis/MilpPermissiveSchedulerTest.cpp b/src/test/storm-permissive/analysis/MilpPermissiveSchedulerTest.cpp index b3c587afd2..46ab3d299b 100644 --- a/src/test/storm-permissive/analysis/MilpPermissiveSchedulerTest.cpp +++ b/src/test/storm-permissive/analysis/MilpPermissiveSchedulerTest.cpp @@ -55,7 +55,7 @@ TEST(MilpPermissiveSchedulerTest, DieSelection) { storm::modelchecker::SparseMdpPrctlModelChecker> checker0(*mdp); std::unique_ptr result0 = checker0.check(env, formula02); - storm::modelchecker::ExplicitQualitativeCheckResult& qualitativeResult0 = result0->asExplicitQualitativeCheckResult(); + storm::modelchecker::ExplicitQualitativeCheckResult& qualitativeResult0 = result0->template asExplicitQualitativeCheckResult(); ASSERT_FALSE(qualitativeResult0[0]); @@ -63,7 +63,7 @@ TEST(MilpPermissiveSchedulerTest, DieSelection) { storm::modelchecker::SparseMdpPrctlModelChecker> checker1(submdp); std::unique_ptr result1 = checker1.check(env, formula02); - storm::modelchecker::ExplicitQualitativeCheckResult& qualitativeResult1 = result1->asExplicitQualitativeCheckResult(); + storm::modelchecker::ExplicitQualitativeCheckResult& qualitativeResult1 = result1->template asExplicitQualitativeCheckResult(); EXPECT_TRUE(qualitativeResult1[0]); } diff --git a/src/test/storm-permissive/analysis/SmtPermissiveSchedulerTest.cpp b/src/test/storm-permissive/analysis/SmtPermissiveSchedulerTest.cpp index 84594a1389..99aa4fa1b8 100644 --- a/src/test/storm-permissive/analysis/SmtPermissiveSchedulerTest.cpp +++ b/src/test/storm-permissive/analysis/SmtPermissiveSchedulerTest.cpp @@ -49,7 +49,7 @@ TEST(SmtPermissiveSchedulerTest, DieSelection) { storm::modelchecker::SparseMdpPrctlModelChecker> checker0(*mdp); std::unique_ptr result0 = checker0.check(env, formula02b); - storm::modelchecker::ExplicitQualitativeCheckResult& qualitativeResult0 = result0->asExplicitQualitativeCheckResult(); + storm::modelchecker::ExplicitQualitativeCheckResult& qualitativeResult0 = result0->template asExplicitQualitativeCheckResult(); ASSERT_FALSE(qualitativeResult0[0]); @@ -57,7 +57,7 @@ TEST(SmtPermissiveSchedulerTest, DieSelection) { storm::modelchecker::SparseMdpPrctlModelChecker> checker1(submdp); std::unique_ptr result1 = checker1.check(env, formula02b); - storm::modelchecker::ExplicitQualitativeCheckResult& qualitativeResult1 = result1->asExplicitQualitativeCheckResult(); + storm::modelchecker::ExplicitQualitativeCheckResult& qualitativeResult1 = result1->template asExplicitQualitativeCheckResult(); EXPECT_TRUE(qualitativeResult1[0]); diff --git a/src/test/storm/modelchecker/csl/CtmcCslModelCheckerTest.cpp b/src/test/storm/modelchecker/csl/CtmcCslModelCheckerTest.cpp index e546e33be6..2692e8c546 100644 --- a/src/test/storm/modelchecker/csl/CtmcCslModelCheckerTest.cpp +++ b/src/test/storm/modelchecker/csl/CtmcCslModelCheckerTest.cpp @@ -318,7 +318,7 @@ class CtmcCslModelCheckerTest : public ::testing::Test { std::unique_ptr getInitialStateFilter(std::shared_ptr> const& model) const { if (isSparseModel()) { - return std::make_unique(model->template as()->getInitialStates()); + return std::make_unique>(model->template as()->getInitialStates()); } else { return std::make_unique>( model->template as()->getReachableStates(), model->template as()->getInitialStates()); diff --git a/src/test/storm/modelchecker/csl/LraCtmcCslModelCheckerTest.cpp b/src/test/storm/modelchecker/csl/LraCtmcCslModelCheckerTest.cpp index c7273dd2a4..36e162ab5b 100644 --- a/src/test/storm/modelchecker/csl/LraCtmcCslModelCheckerTest.cpp +++ b/src/test/storm/modelchecker/csl/LraCtmcCslModelCheckerTest.cpp @@ -381,7 +381,7 @@ class LraCtmcCslModelCheckerTest : public ::testing::Test { std::unique_ptr getInitialStateFilter(std::shared_ptr> const& model) const { if (isSparseModel()) { - return std::make_unique(model->template as()->getInitialStates()); + return std::make_unique>(model->template as()->getInitialStates()); } else { return std::make_unique>( model->template as()->getReachableStates(), model->template as()->getInitialStates()); diff --git a/src/test/storm/modelchecker/csl/MarkovAutomatonCslModelCheckerTest.cpp b/src/test/storm/modelchecker/csl/MarkovAutomatonCslModelCheckerTest.cpp index 4010abee25..3a398d1b1e 100644 --- a/src/test/storm/modelchecker/csl/MarkovAutomatonCslModelCheckerTest.cpp +++ b/src/test/storm/modelchecker/csl/MarkovAutomatonCslModelCheckerTest.cpp @@ -245,7 +245,7 @@ class MarkovAutomatonCslModelCheckerTest : public ::testing::Test { std::unique_ptr getInitialStateFilter(std::shared_ptr> const& model) const { if (isSparseModel()) { - return std::make_unique(model->template as()->getInitialStates()); + return std::make_unique>(model->template as()->getInitialStates()); } else { return std::make_unique>( model->template as()->getReachableStates(), model->template as()->getInitialStates()); diff --git a/src/test/storm/modelchecker/multiobjective/MultiObjectiveSchedRestModelCheckerTest.cpp b/src/test/storm/modelchecker/multiobjective/MultiObjectiveSchedRestModelCheckerTest.cpp index ba637e5cef..8d37ec2a95 100644 --- a/src/test/storm/modelchecker/multiobjective/MultiObjectiveSchedRestModelCheckerTest.cpp +++ b/src/test/storm/modelchecker/multiobjective/MultiObjectiveSchedRestModelCheckerTest.cpp @@ -276,13 +276,13 @@ TYPED_TEST(MultiObjectiveSchedRestModelCheckerTest, steps) { { auto result = storm::modelchecker::multiobjective::performMultiObjectiveModelChecking(env, *mdp, formulas[formulaIndex]->asMultiObjectiveFormula()); ASSERT_TRUE(result->isExplicitQualitativeCheckResult()); - EXPECT_TRUE(result->asExplicitQualitativeCheckResult()[*mdp->getInitialStates().begin()]); + EXPECT_TRUE(result->template asExplicitQualitativeCheckResult()[*mdp->getInitialStates().begin()]); } ++formulaIndex; { auto result = storm::modelchecker::multiobjective::performMultiObjectiveModelChecking(env, *mdp, formulas[formulaIndex]->asMultiObjectiveFormula()); ASSERT_TRUE(result->isExplicitQualitativeCheckResult()); - EXPECT_FALSE(result->asExplicitQualitativeCheckResult()[*mdp->getInitialStates().begin()]); + EXPECT_FALSE(result->template asExplicitQualitativeCheckResult()[*mdp->getInitialStates().begin()]); } ++formulaIndex; { @@ -295,7 +295,7 @@ TYPED_TEST(MultiObjectiveSchedRestModelCheckerTest, steps) { { auto result = storm::modelchecker::multiobjective::performMultiObjectiveModelChecking(env, *mdp, formulas[formulaIndex]->asMultiObjectiveFormula()); ASSERT_TRUE(result->isExplicitQualitativeCheckResult()); - EXPECT_FALSE(result->asExplicitQualitativeCheckResult()[*mdp->getInitialStates().begin()]); + EXPECT_FALSE(result->template asExplicitQualitativeCheckResult()[*mdp->getInitialStates().begin()]); } } @@ -361,7 +361,7 @@ TYPED_TEST(MultiObjectiveSchedRestModelCheckerTest, mecs) { } else { auto result = storm::modelchecker::multiobjective::performMultiObjectiveModelChecking(env, *mdp, formulas[formulaIndex]->asMultiObjectiveFormula()); ASSERT_TRUE(result->isExplicitQualitativeCheckResult()); - EXPECT_TRUE(result->asExplicitQualitativeCheckResult()[*mdp->getInitialStates().begin()]); + EXPECT_TRUE(result->template asExplicitQualitativeCheckResult()[*mdp->getInitialStates().begin()]); } } ++formulaIndex; @@ -373,7 +373,7 @@ TYPED_TEST(MultiObjectiveSchedRestModelCheckerTest, mecs) { } else { auto result = storm::modelchecker::multiobjective::performMultiObjectiveModelChecking(env, *mdp, formulas[formulaIndex]->asMultiObjectiveFormula()); ASSERT_TRUE(result->isExplicitQualitativeCheckResult()); - EXPECT_FALSE(result->asExplicitQualitativeCheckResult()[*mdp->getInitialStates().begin()]); + EXPECT_FALSE(result->template asExplicitQualitativeCheckResult()[*mdp->getInitialStates().begin()]); } } } diff --git a/src/test/storm/modelchecker/multiobjective/SparseMaCbMultiObjectiveModelCheckerTest.cpp b/src/test/storm/modelchecker/multiobjective/SparseMaCbMultiObjectiveModelCheckerTest.cpp index 3326258099..847e881e4e 100644 --- a/src/test/storm/modelchecker/multiobjective/SparseMaCbMultiObjectiveModelCheckerTest.cpp +++ b/src/test/storm/modelchecker/multiobjective/SparseMaCbMultiObjectiveModelCheckerTest.cpp @@ -43,9 +43,9 @@ TEST_F(SparseMaCbMultiObjectiveModelCheckerTest, server) { result = storm::modelchecker::multiobjective::performMultiObjectiveModelChecking(env, *ma, formulas[0]->asMultiObjectiveFormula()); ASSERT_TRUE(result->isExplicitQualitativeCheckResult()); - EXPECT_TRUE(result->asExplicitQualitativeCheckResult()[initState]); + EXPECT_TRUE(result->template asExplicitQualitativeCheckResult()[initState]); result = storm::modelchecker::multiobjective::performMultiObjectiveModelChecking(env, *ma, formulas[1]->asMultiObjectiveFormula()); ASSERT_TRUE(result->isExplicitQualitativeCheckResult()); - EXPECT_FALSE(result->asExplicitQualitativeCheckResult()[initState]); + EXPECT_FALSE(result->template asExplicitQualitativeCheckResult()[initState]); } diff --git a/src/test/storm/modelchecker/multiobjective/SparseMaPcaaMultiObjectiveModelCheckerTest.cpp b/src/test/storm/modelchecker/multiobjective/SparseMaPcaaMultiObjectiveModelCheckerTest.cpp index 9480934206..29f8ded251 100644 --- a/src/test/storm/modelchecker/multiobjective/SparseMaPcaaMultiObjectiveModelCheckerTest.cpp +++ b/src/test/storm/modelchecker/multiobjective/SparseMaPcaaMultiObjectiveModelCheckerTest.cpp @@ -173,12 +173,12 @@ TEST(SparseMaPcaaMultiObjectiveModelCheckerTest, jobscheduler_achievability_3Obj std::unique_ptr result = storm::modelchecker::multiobjective::performMultiObjectiveModelChecking(env, *ma, formulas[0]->asMultiObjectiveFormula()); ASSERT_TRUE(result->isExplicitQualitativeCheckResult()); - EXPECT_TRUE(result->asExplicitQualitativeCheckResult()[initState]); + EXPECT_TRUE(result->template asExplicitQualitativeCheckResult()[initState]); std::unique_ptr result2 = storm::modelchecker::multiobjective::performMultiObjectiveModelChecking(env, *ma, formulas[1]->asMultiObjectiveFormula()); ASSERT_TRUE(result2->isExplicitQualitativeCheckResult()); - EXPECT_FALSE(result2->asExplicitQualitativeCheckResult()[initState]); + EXPECT_FALSE(result2->template asExplicitQualitativeCheckResult()[initState]); } TEST(SparseMaPcaaMultiObjectiveModelCheckerTest, jobscheduler_quantitative_3Obj) { @@ -211,7 +211,7 @@ TEST(SparseMaPcaaMultiObjectiveModelCheckerTest, jobscheduler_quantitative_3Obj) std::unique_ptr result2 = storm::modelchecker::multiobjective::performMultiObjectiveModelChecking(env, *ma, formulas[1]->asMultiObjectiveFormula()); ASSERT_TRUE(result2->isExplicitQualitativeCheckResult()); - EXPECT_FALSE(result2->asExplicitQualitativeCheckResult()[initState]); + EXPECT_FALSE(result2->template asExplicitQualitativeCheckResult()[initState]); } TEST(SparseMaPcaaMultiObjectiveModelCheckerTest, jobscheduler_pareto_2Obj) { diff --git a/src/test/storm/modelchecker/multiobjective/SparseMdpCbMultiObjectiveModelCheckerTest.cpp b/src/test/storm/modelchecker/multiobjective/SparseMdpCbMultiObjectiveModelCheckerTest.cpp index ebf0a6cc25..99eb25c299 100644 --- a/src/test/storm/modelchecker/multiobjective/SparseMdpCbMultiObjectiveModelCheckerTest.cpp +++ b/src/test/storm/modelchecker/multiobjective/SparseMdpCbMultiObjectiveModelCheckerTest.cpp @@ -43,11 +43,11 @@ TEST_F(SparseMdpCbMultiObjectiveModelCheckerTest, consensus) { result = storm::modelchecker::multiobjective::performMultiObjectiveModelChecking(env, *mdp, formulas[1]->asMultiObjectiveFormula()); ASSERT_TRUE(result->isExplicitQualitativeCheckResult()); - EXPECT_TRUE(result->asExplicitQualitativeCheckResult()[initState]); + EXPECT_TRUE(result->template asExplicitQualitativeCheckResult()[initState]); result = storm::modelchecker::multiobjective::performMultiObjectiveModelChecking(env, *mdp, formulas[2]->asMultiObjectiveFormula()); ASSERT_TRUE(result->isExplicitQualitativeCheckResult()); - EXPECT_FALSE(result->asExplicitQualitativeCheckResult()[initState]); + EXPECT_FALSE(result->template asExplicitQualitativeCheckResult()[initState]); } TEST_F(SparseMdpCbMultiObjectiveModelCheckerTest, zeroconf) { @@ -69,7 +69,7 @@ TEST_F(SparseMdpCbMultiObjectiveModelCheckerTest, zeroconf) { std::unique_ptr result = storm::modelchecker::multiobjective::performMultiObjectiveModelChecking(env, *mdp, formulas[0]->asMultiObjectiveFormula()); ASSERT_TRUE(result->isExplicitQualitativeCheckResult()); - EXPECT_TRUE(result->asExplicitQualitativeCheckResult()[initState]); + EXPECT_TRUE(result->template asExplicitQualitativeCheckResult()[initState]); } /* This test takes a little bit too long ... @@ -88,6 +88,6 @@ formulas)->as>(); uint_fast64_ std::unique_ptr result = storm::modelchecker::multiobjective::performMultiObjectiveModelChecking(*mdp, formulas[0]->asMultiObjectiveFormula()) ASSERT_TRUE(result->isExplicitQualitativeCheckResult()); - EXPECT_FALSE(result->asExplicitQualitativeCheckResult()[initState]); + EXPECT_FALSE(result->template asExplicitQualitativeCheckResult()[initState]); } */ diff --git a/src/test/storm/modelchecker/multiobjective/SparseMdpPcaaMultiObjectiveModelCheckerTest.cpp b/src/test/storm/modelchecker/multiobjective/SparseMdpPcaaMultiObjectiveModelCheckerTest.cpp index c6433b705a..0a8cdf79eb 100644 --- a/src/test/storm/modelchecker/multiobjective/SparseMdpPcaaMultiObjectiveModelCheckerTest.cpp +++ b/src/test/storm/modelchecker/multiobjective/SparseMdpPcaaMultiObjectiveModelCheckerTest.cpp @@ -228,11 +228,11 @@ TEST(SparseMdpPcaaMultiObjectiveModelCheckerTest, consensus) { EXPECT_NEAR(0.1083326097, result->asExplicitQuantitativeCheckResult()[initState], prec); result = storm::modelchecker::multiobjective::performMultiObjectiveModelChecking(env, *mdp, formulas[1]->asMultiObjectiveFormula()); ASSERT_TRUE(result->isExplicitQualitativeCheckResult()); - EXPECT_TRUE(result->asExplicitQualitativeCheckResult()[initState]); + EXPECT_TRUE(result->template asExplicitQualitativeCheckResult()[initState]); result = storm::modelchecker::multiobjective::performMultiObjectiveModelChecking(env, *mdp, formulas[2]->asMultiObjectiveFormula()); ASSERT_TRUE(result->isExplicitQualitativeCheckResult()); - EXPECT_FALSE(result->asExplicitQualitativeCheckResult()[initState]); + EXPECT_FALSE(result->template asExplicitQualitativeCheckResult()[initState]); } TEST(SparseMdpPcaaMultiObjectiveModelCheckerTest, zeroconf) { @@ -306,7 +306,7 @@ TEST(SparseMdpPcaaMultiObjectiveModelCheckerTest, tiny_rewards_negative) { std::unique_ptr result = storm::modelchecker::multiobjective::performMultiObjectiveModelChecking(env, *mdp, formulas[0]->asMultiObjectiveFormula()); - EXPECT_TRUE(result->asExplicitQualitativeCheckResult()[initState]); + EXPECT_TRUE(result->template asExplicitQualitativeCheckResult()[initState]); } TEST(SparseMdpPcaaMultiObjectiveModelCheckerTest, scheduler) { @@ -330,7 +330,7 @@ TEST(SparseMdpPcaaMultiObjectiveModelCheckerTest, scheduler) { std::unique_ptr result = storm::modelchecker::multiobjective::performMultiObjectiveModelChecking(env, *mdp, formulas[0]->asMultiObjectiveFormula()); - EXPECT_TRUE(result->asExplicitQualitativeCheckResult()[initState]); + EXPECT_TRUE(result->template asExplicitQualitativeCheckResult()[initState]); } TEST(SparseMdpPcaaMultiObjectiveModelCheckerTest, dpm) { diff --git a/src/test/storm/modelchecker/prctl/dtmc/DtmcPrctlModelCheckerTest.cpp b/src/test/storm/modelchecker/prctl/dtmc/DtmcPrctlModelCheckerTest.cpp index 5083138f3b..a5a8fe0e26 100644 --- a/src/test/storm/modelchecker/prctl/dtmc/DtmcPrctlModelCheckerTest.cpp +++ b/src/test/storm/modelchecker/prctl/dtmc/DtmcPrctlModelCheckerTest.cpp @@ -644,7 +644,7 @@ class DtmcPrctlModelCheckerTest : public ::testing::Test { std::unique_ptr getInitialStateFilter(std::shared_ptr> const& model) const { if (isSparseModel()) { - return std::make_unique(model->template as()->getInitialStates()); + return std::make_unique>(model->template as()->getInitialStates()); } else { return std::make_unique>( model->template as()->getReachableStates(), model->template as()->getInitialStates()); diff --git a/src/test/storm/modelchecker/prctl/dtmc/RobustDtmcPrctlModelCheckerTest.cpp b/src/test/storm/modelchecker/prctl/dtmc/RobustDtmcPrctlModelCheckerTest.cpp index 1ee43344b7..2cb9f531a3 100644 --- a/src/test/storm/modelchecker/prctl/dtmc/RobustDtmcPrctlModelCheckerTest.cpp +++ b/src/test/storm/modelchecker/prctl/dtmc/RobustDtmcPrctlModelCheckerTest.cpp @@ -1,4 +1,5 @@ #include "storm-config.h" +#include "storm/adapters/IntervalForward.h" #include "test/storm_gtest.h" #include "storm-parsers/api/model_descriptions.h" @@ -16,11 +17,11 @@ std::unique_ptr getInitialStateFilter( std::shared_ptr> const& model) { - return std::make_unique(model->getInitialStates()); + return std::make_unique>(model->getInitialStates()); } std::unique_ptr getInitialStateFilter(std::shared_ptr> const& model) { - return std::make_unique(model->getInitialStates()); + return std::make_unique>(model->getInitialStates()); } double getQuantitativeResultAtInitialState(std::shared_ptr> const& model, @@ -138,7 +139,7 @@ void checkModelForQualitativeResult(std::string const& path, std::string const& ASSERT_TRUE(result->isExplicitQualitativeCheckResult()); for (size_t i = 0; i < expectedResultVector[0].size(); i++) { - EXPECT_EQ(expectedResultVector[0].get(i), result->asExplicitQualitativeCheckResult()[i]); + EXPECT_EQ(expectedResultVector[0].get(i), result->template asExplicitQualitativeCheckResult()[i]); } auto task2 = storm::modelchecker::CheckTask(*formulas[1]); @@ -148,7 +149,7 @@ void checkModelForQualitativeResult(std::string const& path, std::string const& ASSERT_TRUE(result->isExplicitQualitativeCheckResult()); for (size_t i = 0; i < expectedResultVector[1].size(); i++) { - EXPECT_EQ(expectedResultVector[1].get(i), result->asExplicitQualitativeCheckResult()[i]); + EXPECT_EQ(expectedResultVector[1].get(i), result->template asExplicitQualitativeCheckResult()[i]); } } diff --git a/src/test/storm/modelchecker/prctl/mdp/ConditionalMdpPrctlModelCheckerTest.cpp b/src/test/storm/modelchecker/prctl/mdp/ConditionalMdpPrctlModelCheckerTest.cpp index 1f9f41cbe8..572be8909f 100644 --- a/src/test/storm/modelchecker/prctl/mdp/ConditionalMdpPrctlModelCheckerTest.cpp +++ b/src/test/storm/modelchecker/prctl/mdp/ConditionalMdpPrctlModelCheckerTest.cpp @@ -8,6 +8,7 @@ #include "storm/environment/modelchecker/ModelCheckerEnvironment.h" #include "storm/environment/solver/MinMaxSolverEnvironment.h" #include "storm/modelchecker/prctl/SparseMdpPrctlModelChecker.h" +#include "storm/modelchecker/results/ExplicitQualitativeCheckResult.h" #include "storm/modelchecker/results/ExplicitQuantitativeCheckResult.h" namespace { @@ -49,6 +50,30 @@ class SparseDoubleBisectionAdvancedEnvironment { } }; +class SparseDoubleBisectionPtEnvironment { + public: + static const bool isExact = false; + typedef double ValueType; + typedef storm::models::sparse::Mdp ModelType; + static storm::Environment createEnvironment() { + storm::Environment env; + env.modelchecker().setConditionalAlgorithmSetting(storm::ConditionalAlgorithmSetting::BisectionPolicyTracking); + return env; + } +}; + +class SparseDoubleBisectionAdvancedPtEnvironment { + public: + static const bool isExact = false; + typedef double ValueType; + typedef storm::models::sparse::Mdp ModelType; + static storm::Environment createEnvironment() { + storm::Environment env; + env.modelchecker().setConditionalAlgorithmSetting(storm::ConditionalAlgorithmSetting::BisectionAdvancedPolicyTracking); + return env; + } +}; + class SparseDoublePiEnvironment { public: static const bool isExact = false; @@ -94,6 +119,32 @@ class SparseRationalNumberBisectionAdvancedEnvironment { static storm::Environment createEnvironment() { storm::Environment env; env.modelchecker().setConditionalAlgorithmSetting(storm::ConditionalAlgorithmSetting::BisectionAdvanced); + env.solver().minMax().setPrecision(storm::utility::convertNumber(0)); // TODO: this should not be necessary + return env; + } +}; + +class SparseRationalNumberBisectionPtEnvironment { + public: + static const bool isExact = true; + typedef storm::RationalNumber ValueType; + typedef storm::models::sparse::Mdp ModelType; + static storm::Environment createEnvironment() { + storm::Environment env; + env.modelchecker().setConditionalAlgorithmSetting(storm::ConditionalAlgorithmSetting::BisectionPolicyTracking); + return env; + } +}; + +class SparseRationalNumberBisectionAdvancedPtEnvironment { + public: + static const bool isExact = true; + typedef storm::RationalNumber ValueType; + typedef storm::models::sparse::Mdp ModelType; + static storm::Environment createEnvironment() { + storm::Environment env; + env.modelchecker().setConditionalAlgorithmSetting(storm::ConditionalAlgorithmSetting::BisectionAdvancedPolicyTracking); + env.solver().minMax().setPrecision(storm::utility::convertNumber(0)); // TODO: this should not be necessary return env; } }; @@ -159,9 +210,10 @@ class ConditionalMdpPrctlModelCheckerTest : public ::testing::Test { storm::Environment _environment; }; -typedef ::testing::Types + SparseRationalNumberBisectionPtEnvironment, SparseRationalNumberBisectionAdvancedPtEnvironment, SparseRationalNumberPiEnvironment> TestingTypes; TYPED_TEST_SUITE(ConditionalMdpPrctlModelCheckerTest, TestingTypes, ); @@ -214,7 +266,11 @@ TYPED_TEST(ConditionalMdpPrctlModelCheckerTest, consensus) { "Pmax=? [F \"all_coins_equal_0\" & \"finished\" || F \"agree\" & \"finished\"];" "Pmin=? [F \"all_coins_equal_0\" & \"finished\" || F \"agree\" & \"finished\"];" "Pmax=? [F \"all_coins_equal_1\" & \"finished\" || F \"agree\" & \"finished\"];" - "Pmin=? [F \"all_coins_equal_1\" & \"finished\" || F \"agree\" & \"finished\"];"; + "Pmin=? [F \"all_coins_equal_1\" & \"finished\" || F \"agree\" & \"finished\"];" + "P<=560/953 [F \"all_coins_equal_1\" & \"finished\" || F \"agree\" & \"finished\"];" + "P<562/953 [F \"all_coins_equal_1\" & \"finished\" || F \"agree\" & \"finished\"];" + "P>393/953 [F \"all_coins_equal_1\" & \"finished\" || F \"agree\" & \"finished\"];" + "P>=391/953 [F \"all_coins_equal_1\" & \"finished\" || F \"agree\" & \"finished\"];"; auto program = storm::parser::PrismParser::parse(STORM_TEST_RESOURCES_DIR "/mdp/coin2-2.nm"); auto modelFormulas = this->buildModelFormulas(program, formulasString); @@ -234,6 +290,14 @@ TYPED_TEST(ConditionalMdpPrctlModelCheckerTest, consensus) { EXPECT_NEAR(this->parseNumber("561/953"), result[*mdp->getInitialStates().begin()], this->precision()); result = checker.check(this->env(), tasks[3])->template asExplicitQuantitativeCheckResult(); EXPECT_NEAR(this->parseNumber("392/953"), result[*mdp->getInitialStates().begin()], this->precision()); + auto qualResult = checker.check(this->env(), tasks[4])->template asExplicitQualitativeCheckResult(); + EXPECT_FALSE(qualResult[*mdp->getInitialStates().begin()]); + qualResult = checker.check(this->env(), tasks[5])->template asExplicitQualitativeCheckResult(); + EXPECT_TRUE(qualResult[*mdp->getInitialStates().begin()]); + qualResult = checker.check(this->env(), tasks[6])->template asExplicitQualitativeCheckResult(); + EXPECT_FALSE(qualResult[*mdp->getInitialStates().begin()]); + qualResult = checker.check(this->env(), tasks[7])->template asExplicitQualitativeCheckResult(); + EXPECT_TRUE(qualResult[*mdp->getInitialStates().begin()]); } TYPED_TEST(ConditionalMdpPrctlModelCheckerTest, simple) { diff --git a/src/test/storm/modelchecker/prctl/mdp/MdpPrctlModelCheckerTest.cpp b/src/test/storm/modelchecker/prctl/mdp/MdpPrctlModelCheckerTest.cpp index 8c01df8684..26d4b4622e 100644 --- a/src/test/storm/modelchecker/prctl/mdp/MdpPrctlModelCheckerTest.cpp +++ b/src/test/storm/modelchecker/prctl/mdp/MdpPrctlModelCheckerTest.cpp @@ -670,7 +670,7 @@ class MdpPrctlModelCheckerTest : public ::testing::Test { std::unique_ptr getInitialStateFilter(std::shared_ptr> const& model) const { if (isSparseModel()) { - return std::make_unique(model->template as()->getInitialStates()); + return std::make_unique>(model->template as()->getInitialStates()); } else { return std::make_unique>( model->template as()->getReachableStates(), model->template as()->getInitialStates()); diff --git a/src/test/storm/modelchecker/prctl/mdp/QuantileQueryTest.cpp b/src/test/storm/modelchecker/prctl/mdp/QuantileQueryTest.cpp index 677ceeecf4..3d5d25c397 100644 --- a/src/test/storm/modelchecker/prctl/mdp/QuantileQueryTest.cpp +++ b/src/test/storm/modelchecker/prctl/mdp/QuantileQueryTest.cpp @@ -162,7 +162,7 @@ class QuantileQueryTest : public ::testing::Test { std::unique_ptr getInitialStateFilter( std::shared_ptr> const& model) const { - return std::make_unique(model->getInitialStates()); + return std::make_unique>(model->getInitialStates()); } }; diff --git a/src/test/storm/modelchecker/prctl/mdp/RobustMdpPrctlModelCheckerTest.cpp b/src/test/storm/modelchecker/prctl/mdp/RobustMdpPrctlModelCheckerTest.cpp index 7185b446c8..46e9b8e9b6 100644 --- a/src/test/storm/modelchecker/prctl/mdp/RobustMdpPrctlModelCheckerTest.cpp +++ b/src/test/storm/modelchecker/prctl/mdp/RobustMdpPrctlModelCheckerTest.cpp @@ -1,4 +1,6 @@ #include "storm-config.h" +#include "storm/adapters/RationalNumberForward.h" +#include "storm/exceptions/NotImplementedException.h" #include "test/storm_gtest.h" #include "storm-parsers/api/model_descriptions.h" @@ -18,10 +20,15 @@ std::unique_ptr getInitialStateFilter( std::shared_ptr> const& model) { - return std::make_unique(model->getInitialStates()); + return std::make_unique>(model->getInitialStates()); } std::unique_ptr getInitialStateFilter(std::shared_ptr> const& model) { + return std::make_unique>(model->getInitialStates()); +} + +std::unique_ptr getInitialStateFilter( + std::shared_ptr> const& model) { return std::make_unique(model->getInitialStates()); } @@ -39,6 +46,13 @@ double getQuantitativeResultAtInitialState(std::shared_ptrasQuantitativeCheckResult().getMin(); } +storm::RationalNumber getQuantitativeResultAtInitialState(std::shared_ptr> const& model, + std::unique_ptr& result) { + auto filter = getInitialStateFilter(model); + result->filter(*filter); + return result->asQuantitativeCheckResult().getMin(); +} + void expectThrow(std::string const& path, std::string const& formulaString) { std::shared_ptr> modelPtr = storm::parser::parseDirectEncodingModel(path); std::vector> formulas = storm::api::extractFormulasFromProperties(storm::api::parseProperties(formulaString)); @@ -51,7 +65,7 @@ void expectThrow(std::string const& path, std::string const& formulaString) { auto task = storm::modelchecker::CheckTask(*formulas[0]); auto checker = storm::modelchecker::SparseMdpPrctlModelChecker>(*mdp); - STORM_SILENT_EXPECT_THROW(checker.check(env, task), storm::exceptions::InvalidArgumentException); + STORM_SILENT_EXPECT_THROW(checker.check(env, task), storm::exceptions::NotImplementedException); } void checkModel(std::string const& path, std::string const& formulaString, double maxmin, double maxmax, double minmax, double minmin, bool produceScheduler) { @@ -124,6 +138,36 @@ void checkPrismModelForQuantitativeResult(std::string const& path, std::string c EXPECT_NEAR(maxmax, getQuantitativeResultAtInitialState(mdp, resultMaxMax), 0.0001); } +void checkModelRational(std::string const& path, std::string const& formulaString, storm::RationalNumber maxmin, storm::RationalNumber maxmax, + storm::RationalNumber minmax, storm::RationalNumber minmin, bool produceScheduler) { + std::shared_ptr> modelPtr = + storm::parser::DirectEncodingParser::parseModel(path); + std::vector> formulas = storm::api::extractFormulasFromProperties(storm::api::parseProperties(formulaString)); + storm::Environment env; + env.solver().minMax().setMethod(storm::solver::MinMaxMethod::ValueIteration); + + std::shared_ptr> mdp = modelPtr->as>(); + ASSERT_EQ(storm::models::ModelType::Mdp, modelPtr->getType()); + auto taskMax = storm::modelchecker::CheckTask(*formulas[0]); + taskMax.setProduceSchedulers(produceScheduler); + + auto checker = storm::modelchecker::SparseMdpPrctlModelChecker>(*mdp); + auto resultMax = checker.check(env, taskMax); + EXPECT_EQ(maxmin, getQuantitativeResultAtInitialState(mdp, resultMax)); + taskMax.setRobustUncertainty(false); + auto resultMaxNonRobust = checker.check(env, taskMax); + EXPECT_EQ(maxmax, getQuantitativeResultAtInitialState(mdp, resultMaxNonRobust)); + + auto taskMin = storm::modelchecker::CheckTask(*formulas[1]); + taskMin.setProduceSchedulers(produceScheduler); + + auto resultMin = checker.check(env, taskMin); + EXPECT_EQ(minmax, getQuantitativeResultAtInitialState(mdp, resultMin)); + taskMin.setRobustUncertainty(false); + auto resultMinNonRobust = checker.check(env, taskMin); + EXPECT_EQ(minmin, getQuantitativeResultAtInitialState(mdp, resultMinNonRobust)); +} + TEST(RobustMdpModelCheckerTest, RobotMinMaxTest) { #ifndef STORM_HAVE_Z3 GTEST_SKIP() << "Z3 not available."; @@ -177,7 +221,9 @@ TEST(RobustMDPModelCheckingTest, Tiny03maxmin) { } TEST(RobustMDPModelCheckingTest, BoundedTiny03maxmin) { - expectThrow(STORM_TEST_RESOURCES_DIR "/imdp/tiny-03.drn", "Pmax=? [ F<=3 \"target\"]"); + checkModel(STORM_TEST_RESOURCES_DIR "/imdp/tiny-03.drn", "Pmax=? [ F<=3 \"target\"];Pmin=? [ F<=3 \"target\"]", 0.5, 0.5, 0.5, 0.5, true); + checkModelRational(STORM_TEST_RESOURCES_DIR "/imdp/tiny-03.drn", "Pmax=? [ F<=3 \"target\"];Pmin=? [ F<=3 \"target\"]", storm::RationalNumber(1, 2), + storm::RationalNumber(1, 2), storm::RationalNumber(1, 2), storm::RationalNumber(1, 2), true); } TEST(RobustMDPModelCheckingTest, Tiny04maxmin) { diff --git a/src/test/storm/parser/DirectEncodingParserTest.cpp b/src/test/storm/parser/DirectEncodingParserTest.cpp index 01ee8d7f59..7ba12aa684 100644 --- a/src/test/storm/parser/DirectEncodingParserTest.cpp +++ b/src/test/storm/parser/DirectEncodingParserTest.cpp @@ -135,6 +135,15 @@ TEST(DirectEncodingParserTest, IntervalDtmcTest) { EXPECT_TRUE(modelPtr->hasUncertainty()); } +TEST(DirectEncodingParserTest, RationalIntervalDtmcTest) { + std::shared_ptr> modelPtr = + storm::parser::DirectEncodingParser::parseModel(STORM_TEST_RESOURCES_DIR "/idtmc/brp-16-2.drn"); + std::shared_ptr> dtmc = modelPtr->as>(); + ASSERT_EQ(storm::models::ModelType::Dtmc, modelPtr->getType()); + ASSERT_EQ(613ul, dtmc->getNumberOfStates()); + EXPECT_TRUE(modelPtr->hasUncertainty()); +} + TEST(DirectEncodingParserTest, PomdpParsing) { std::shared_ptr> modelPtr = storm::parser::parseDirectEncodingModel(STORM_TEST_RESOURCES_DIR "/pomdp/maze2_sl0.drn"); @@ -191,4 +200,4 @@ TEST(DirectEncodingParserTest, CompressedParsing) { auto dtmc = modelPtr->as>(); ASSERT_EQ(677ul, dtmc->getNumberOfStates()); } -} \ No newline at end of file +} diff --git a/src/test/storm/utility/RationalApproximationTest.cpp b/src/test/storm/utility/RationalApproximationTest.cpp new file mode 100644 index 0000000000..9e7f770ee5 --- /dev/null +++ b/src/test/storm/utility/RationalApproximationTest.cpp @@ -0,0 +1,70 @@ +#include "storm-config.h" +#include "test/storm_gtest.h" + +#include "storm/adapters/RationalNumberAdapter.h" +#include "storm/utility/RationalApproximation.h" +#include "storm/utility/constants.h" + +namespace { + +storm::RationalNumber rn(double doubleValue) { + return storm::utility::convertNumber(doubleValue); +} + +storm::RationalNumber rn(std::string const& str) { + return storm::utility::convertNumber(str); +} +TEST(RationalApproximationTest, inclusive_bounds) { + EXPECT_EQ(rn("0"), storm::utility::findRational(rn("0"), true, rn("0"), true)); + EXPECT_EQ(rn("1"), storm::utility::findRational(rn("1"), true, rn("1"), true)); + EXPECT_EQ(rn("0"), storm::utility::findRational(rn("0"), true, rn("1"), true)); + EXPECT_EQ(rn("1/2"), storm::utility::findRational(rn("1/3"), true, rn("2/3"), true)); + EXPECT_EQ(rn("1/2"), storm::utility::findRational(rn("1/10"), true, rn("9/10"), true)); + EXPECT_EQ(rn("1"), storm::utility::findRational(rn("1/2"), true, rn("1"), true)); + EXPECT_EQ(rn("1"), storm::utility::findRational(rn("2/3"), true, rn("1"), true)); + EXPECT_EQ(rn("2/3"), storm::utility::findRational(rn("2/3"), true, rn("3/4"), true)); + EXPECT_EQ(rn("2/3"), storm::utility::findRational(rn("3/5"), true, rn("3/4"), true)); + EXPECT_EQ(rn("1"), storm::utility::findRational(rn("2/3"), true, rn("123456"), true)); + EXPECT_EQ(rn("23/3"), storm::utility::findRational(rn("23/3"), true, rn("31/4"), true)); + EXPECT_EQ(rn("23/3"), storm::utility::findRational(rn("38/5"), true, rn("31/4"), true)); + EXPECT_EQ(rn("75/7"), storm::utility::findRational(rn(10.71), true, rn(10.72), true)); + EXPECT_EQ(rn(0.123456), storm::utility::findRational(rn(0.123456), true, rn(0.123456), true)); + EXPECT_EQ(rn(987.123456), storm::utility::findRational(rn(987.123456), true, rn(987.123456), true)); +} + +TEST(RationalApproximationTest, exclusive_bounds) { + EXPECT_EQ(rn("1/2"), storm::utility::findRational(rn("0"), false, rn("1"), false)); + EXPECT_EQ(rn("0"), storm::utility::findRational(rn("0"), true, rn("1"), false)); + EXPECT_EQ(rn("1"), storm::utility::findRational(rn("0"), false, rn("1"), true)); + EXPECT_EQ(rn("1/3"), storm::utility::findRational(rn("0"), false, rn("1/2"), false)); + EXPECT_EQ(rn("2/3"), storm::utility::findRational(rn("1/2"), false, rn("1"), false)); + EXPECT_EQ(rn("3/4"), storm::utility::findRational(rn("2/3"), false, rn("1"), false)); + EXPECT_EQ(rn("5/7"), storm::utility::findRational(rn("2/3"), false, rn("3/4"), false)); + EXPECT_EQ(rn("3/2"), storm::utility::findRational(rn("1"), false, rn("2"), false)); + EXPECT_EQ(rn("30/19"), storm::utility::findRational(rn("11/7"), false, rn("19/12"), false)); + EXPECT_EQ(rn("11/7"), storm::utility::findRational(rn("11/7"), true, rn("19/12"), false)); + EXPECT_EQ(rn("19/12"), storm::utility::findRational(rn("11/7"), false, rn("19/12"), true)); + EXPECT_EQ(rn("1000/1001"), storm::utility::findRational(rn("999/1000"), false, rn("1"), false)); + EXPECT_EQ(rn("999/1000"), storm::utility::findRational(rn("999/1000"), true, rn("1"), false)); + EXPECT_EQ(rn("333/334"), storm::utility::findRational(rn("997/1000"), true, rn("1"), false)); + EXPECT_EQ(rn("1001/1000"), storm::utility::findRational(rn("1"), false, rn("1001/1000"), true)); + EXPECT_EQ(rn("1002/1001"), storm::utility::findRational(rn("1"), false, rn("1001/1000"), false)); + EXPECT_EQ(rn("335/334"), storm::utility::findRational(rn("1"), false, rn("1003/1000"), true)); + EXPECT_EQ(rn("500/1001"), storm::utility::findRational(rn("999/2000"), false, rn("1/2"), false)); + EXPECT_EQ(rn("167/335"), storm::utility::findRational(rn("997/2000"), true, rn("1/2"), false)); + EXPECT_EQ(rn("500/1001"), storm::utility::findRational(rn("500/1001"), true, rn("1/2"), false)); + EXPECT_EQ(rn("501/1001"), storm::utility::findRational(rn("1/2"), false, rn("501/1001"), true)); + EXPECT_EQ(rn("502/1003"), storm::utility::findRational(rn("1/2"), false, rn("501/1001"), false)); + EXPECT_EQ(rn("168/335"), storm::utility::findRational(rn("1/2"), false, rn("502/1001"), true)); +} + +TEST(RationalApproximationTest, negative) { + EXPECT_EQ(rn("0"), storm::utility::findRational(rn("-1"), false, rn("1"), false)); + EXPECT_EQ(rn("0"), storm::utility::findRational(rn("-1"), true, rn("0"), true)); + EXPECT_EQ(rn("-1"), storm::utility::findRational(rn("-1"), true, rn("0"), false)); + EXPECT_EQ(rn("-30/19"), storm::utility::findRational(rn("-19/12"), false, rn("-11/7"), false)); + EXPECT_EQ(rn("-11/7"), storm::utility::findRational(rn("-19/12"), false, rn("-11/7"), true)); + EXPECT_EQ(rn("-19/12"), storm::utility::findRational(rn("-19/12"), true, rn("-11/7"), false)); +} + +} // namespace