From 83d4b5000cd96ea378dedfd60a28047f03948a4a Mon Sep 17 00:00:00 2001 From: Luko van der Maas Date: Wed, 26 Nov 2025 15:33:30 +0100 Subject: [PATCH 01/51] Add ValueType to ExplicitQualitativeCheckResult --- src/storm-cli-utilities/model-handling.h | 20 +-- .../api/counterexamples.cpp | 2 +- .../MILPMinimalLabelSetGenerator.h | 6 +- .../SMTMinimalLabelSetGenerator.h | 6 +- .../modelchecker/DFTModelChecker.cpp | 2 +- src/storm-pars-cli/sampling.h | 2 +- src/storm-pars-cli/solutionFunctions.h | 2 +- ...rseDerivativeInstantiationModelChecker.cpp | 8 +- .../SparseDtmcInstantiationModelChecker.cpp | 2 +- .../SparseMdpInstantiationModelChecker.cpp | 2 +- ...SparseDtmcParameterLiftingModelChecker.cpp | 10 +- .../SparseMdpParameterLiftingModelChecker.cpp | 10 +- .../SparseParameterLiftingModelChecker.cpp | 15 +-- ...tingSparseParameterLiftingModelChecker.cpp | 10 +- .../region/monotonicity/OrderExtender.cpp | 6 +- .../SparseParametricDtmcSimplifier.cpp | 10 +- .../SparseParametricMdpSimplifier.cpp | 10 +- .../analysis/PermissiveSchedulers.cpp | 10 +- src/storm-pomdp-cli/storm-pomdp.cpp | 2 +- .../analysis/FormulaInformation.cpp | 2 +- .../analysis/QualitativeAnalysisOnGraphs.cpp | 2 +- .../GlobalPomdpMecChoiceEliminator.cpp | 2 +- src/storm/api/export.h | 3 +- .../modelchecker/AbstractModelChecker.cpp | 2 +- .../csl/SparseCtmcCslModelChecker.cpp | 26 ++-- .../SparseMarkovAutomatonCslModelChecker.cpp | 22 ++-- .../SparseCbAchievabilityQuery.cpp | 2 +- ...eterministicSchedsAchievabilityChecker.cpp | 4 +- .../DeterministicSchedsObjectiveHelper.cpp | 2 +- .../pcaa/SparsePcaaAchievabilityQuery.cpp | 3 +- .../pcaa/SparsePcaaQuantitativeQuery.cpp | 3 +- .../SparseMultiObjectivePreprocessor.cpp | 30 ++--- .../prctl/HybridMdpPrctlModelChecker.cpp | 2 +- .../prctl/SparseDtmcPrctlModelChecker.cpp | 30 ++--- .../prctl/SparseMdpPrctlModelChecker.cpp | 28 ++--- .../helper/rewardbounded/ProductModel.cpp | 10 +- .../SparsePropositionalModelChecker.cpp | 6 +- .../SparseDtmcEliminationModelChecker.cpp | 22 ++-- .../modelchecker/results/CheckResult.cpp | 22 +++- src/storm/modelchecker/results/CheckResult.h | 8 +- .../ExplicitParetoCurveCheckResult.cpp | 4 +- .../ExplicitQualitativeCheckResult.cpp | 117 +++++++++++++----- .../results/ExplicitQualitativeCheckResult.h | 6 + .../ExplicitQuantitativeCheckResult.cpp | 12 +- .../results/ExplicitQuantitativeCheckResult.h | 3 +- .../results/LexicographicCheckResult.cpp | 4 +- .../BisimulationDecomposition.cpp | 4 +- src/storm/transformer/MemoryIncorporation.cpp | 3 +- .../monotonicity/MonotonicityCheckerTest.cpp | 10 +- .../region/monotonicity/OrderExtenderTest.cpp | 10 +- .../analysis/MilpPermissiveSchedulerTest.cpp | 4 +- .../analysis/SmtPermissiveSchedulerTest.cpp | 4 +- .../csl/CtmcCslModelCheckerTest.cpp | 2 +- .../csl/LraCtmcCslModelCheckerTest.cpp | 2 +- .../MarkovAutomatonCslModelCheckerTest.cpp | 2 +- ...ultiObjectiveSchedRestModelCheckerTest.cpp | 10 +- ...arseMaCbMultiObjectiveModelCheckerTest.cpp | 4 +- ...seMaPcaaMultiObjectiveModelCheckerTest.cpp | 6 +- ...rseMdpCbMultiObjectiveModelCheckerTest.cpp | 8 +- ...eMdpPcaaMultiObjectiveModelCheckerTest.cpp | 8 +- .../prctl/dtmc/DtmcPrctlModelCheckerTest.cpp | 2 +- .../prctl/mdp/MdpPrctlModelCheckerTest.cpp | 2 +- .../prctl/mdp/QuantileQueryTest.cpp | 2 +- .../mdp/RobustMdpPrctlModelCheckerTest.cpp | 4 +- 64 files changed, 343 insertions(+), 256 deletions(-) diff --git a/src/storm-cli-utilities/model-handling.h b/src/storm-cli-utilities/model-handling.h index 8dcd741e1b..6f7eaee4b2 100644 --- a/src/storm-cli-utilities/model-handling.h +++ b/src/storm-cli-utilities/model-handling.h @@ -903,14 +903,14 @@ inline void printCounterexample(std::shared_ptr - requires(!std::derived_from>) -inline void generateCounterexamples(std::shared_ptr const&, SymbolicInput const&) { +requires(!std::derived_from>) inline void generateCounterexamples(std::shared_ptr const&, + SymbolicInput const&) { STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Counterexample generation is not supported for this data-type."); } template - requires(std::derived_from>) -inline void generateCounterexamples(std::shared_ptr const& sparseModel, SymbolicInput const& input) { +requires(std::derived_from>) inline void generateCounterexamples(std::shared_ptr const& sparseModel, + SymbolicInput const& input) { using ValueType = typename ModelType::ValueType; for (auto& rewModel : sparseModel->getRewardModels()) { @@ -966,8 +966,8 @@ inline void generateCounterexamples(std::shared_ptr const& sparseMode } template - requires(!storm::IsIntervalType) -void printFilteredResult(std::unique_ptr const& result, storm::modelchecker::FilterType ft) { +requires(!storm::IsIntervalType) void printFilteredResult(std::unique_ptr const& result, + storm::modelchecker::FilterType ft) { if (result->isQuantitative()) { if (ft == storm::modelchecker::FilterType::VALUES) { STORM_PRINT(*result); @@ -1034,9 +1034,9 @@ inline void printModelCheckingProperty(storm::jani::Property const& property) { } template - requires(!storm::IsIntervalType) -void printResult(std::unique_ptr const& result, storm::logic::Formula const& filterStatesFormula, - storm::modelchecker::FilterType const& filterType, storm::utility::Stopwatch* watch = nullptr) { +requires(!storm::IsIntervalType) void printResult(std::unique_ptr const& result, + storm::logic::Formula const& filterStatesFormula, + storm::modelchecker::FilterType const& filterType, storm::utility::Stopwatch* watch = nullptr) { if (result) { std::stringstream ss; ss << "'" << filterStatesFormula << "'"; @@ -1302,7 +1302,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, storm::api::createTask(states, false)); } 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 cdd057d069..e14a490411 100644 --- a/src/storm-counterexamples/counterexamples/MILPMinimalLabelSetGenerator.h +++ b/src/storm-counterexamples/counterexamples/MILPMinimalLabelSetGenerator.h @@ -1023,8 +1023,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(); @@ -1033,7 +1033,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 6dbbc1f31a..e56ed46673 100644 --- a/src/storm-dft/modelchecker/DFTModelChecker.cpp +++ b/src/storm-dft/modelchecker/DFTModelChecker.cpp @@ -461,7 +461,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 622f806415..b163dabe3f 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..ce1ae41f60 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 6b7387b67c..6551db7209 100644 --- a/src/storm-pars/modelchecker/instantiation/SparseDtmcInstantiationModelChecker.cpp +++ b/src/storm-pars/modelchecker/instantiation/SparseDtmcInstantiationModelChecker.cpp @@ -181,7 +181,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 dc75828481..d866cc858b 100644 --- a/src/storm-pars/modelchecker/instantiation/SparseMdpInstantiationModelChecker.cpp +++ b/src/storm-pars/modelchecker/instantiation/SparseMdpInstantiationModelChecker.cpp @@ -220,7 +220,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 11ada9bdd9..150f271501 100644 --- a/src/storm-pars/modelchecker/region/SparseDtmcParameterLiftingModelChecker.cpp +++ b/src/storm-pars/modelchecker/region/SparseDtmcParameterLiftingModelChecker.cpp @@ -151,9 +151,9 @@ void SparseDtmcParameterLiftingModelCheckerasExplicitQualitativeCheckResult().getTruthValuesVector()); + std::move(propositionalChecker.check(checkTask.getFormula().getLeftSubformula())->template asExplicitQualitativeCheckResult().getTruthValuesVector()); storm::storage::BitVector psiStates = - std::move(propositionalChecker.check(checkTask.getFormula().getRightSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector()); + 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); @@ -193,9 +193,9 @@ void SparseDtmcParameterLiftingModelCheckerasExplicitQualitativeCheckResult().getTruthValuesVector()); + std::move(propositionalChecker.check(checkTask.getFormula().getLeftSubformula())->template asExplicitQualitativeCheckResult().getTruthValuesVector()); storm::storage::BitVector psiStates = - std::move(propositionalChecker.check(checkTask.getFormula().getRightSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector()); + std::move(propositionalChecker.check(checkTask.getFormula().getRightSubformula())->template asExplicitQualitativeCheckResult().getTruthValuesVector()); // get the maybeStates std::pair statesWithProbability01 = @@ -265,7 +265,7 @@ void SparseDtmcParameterLiftingModelCheckerasExplicitQualitativeCheckResult().getTruthValuesVector()); + 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 9e9c2f366f..ff81f266b4 100644 --- a/src/storm-pars/modelchecker/region/SparseMdpParameterLiftingModelChecker.cpp +++ b/src/storm-pars/modelchecker/region/SparseMdpParameterLiftingModelChecker.cpp @@ -110,9 +110,9 @@ void SparseMdpParameterLiftingModelChecker::speci 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()); + std::move(propositionalChecker.check(checkTask.getFormula().getLeftSubformula())->template asExplicitQualitativeCheckResult().getTruthValuesVector()); storm::storage::BitVector psiStates = - std::move(propositionalChecker.check(checkTask.getFormula().getRightSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector()); + std::move(propositionalChecker.check(checkTask.getFormula().getRightSubformula())->template asExplicitQualitativeCheckResult().getTruthValuesVector()); // get the maybeStates maybeStates = storm::solver::minimize(checkTask.getOptimizationDirection()) @@ -153,9 +153,9 @@ void SparseMdpParameterLiftingModelChecker::speci 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()); + std::move(propositionalChecker.check(checkTask.getFormula().getLeftSubformula())->template asExplicitQualitativeCheckResult().getTruthValuesVector()); storm::storage::BitVector psiStates = - std::move(propositionalChecker.check(checkTask.getFormula().getRightSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector()); + std::move(propositionalChecker.check(checkTask.getFormula().getRightSubformula())->template asExplicitQualitativeCheckResult().getTruthValuesVector()); // get the maybeStates std::pair statesWithProbability01 = @@ -204,7 +204,7 @@ void SparseMdpParameterLiftingModelChecker::speci 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()); + 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 7258cbb5b2..ea66d7728e 100644 --- a/src/storm-pars/transformer/SparseParametricDtmcSimplifier.cpp +++ b/src/storm-pars/transformer/SparseParametricDtmcSimplifier.cpp @@ -30,9 +30,9 @@ bool SparseParametricDtmcSimplifier::simplifyForUntilProbabilit return false; } storm::storage::BitVector phiStates = std::move( - propositionalChecker.check(formula.getSubformula().asUntilFormula().getLeftSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector()); + propositionalChecker.check(formula.getSubformula().asUntilFormula().getLeftSubformula())->template asExplicitQualitativeCheckResult().getTruthValuesVector()); storm::storage::BitVector psiStates = std::move( - propositionalChecker.check(formula.getSubformula().asUntilFormula().getRightSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector()); + 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 @@ -97,10 +97,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); @@ -152,7 +152,7 @@ bool SparseParametricDtmcSimplifier::simplifyForReachabilityRew return false; } storm::storage::BitVector targetStates = std::move( - propositionalChecker.check(formula.getSubformula().asEventuallyFormula().getSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector()); + 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 375690682f..4026d9dcfb 100644 --- a/src/storm-pars/transformer/SparseParametricMdpSimplifier.cpp +++ b/src/storm-pars/transformer/SparseParametricMdpSimplifier.cpp @@ -35,9 +35,9 @@ bool SparseParametricMdpSimplifier::simplifyForUntilProbabiliti return false; } storm::storage::BitVector phiStates = std::move( - propositionalChecker.check(formula.getSubformula().asUntilFormula().getLeftSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector()); + propositionalChecker.check(formula.getSubformula().asUntilFormula().getLeftSubformula())->template asExplicitQualitativeCheckResult().getTruthValuesVector()); storm::storage::BitVector psiStates = std::move( - propositionalChecker.check(formula.getSubformula().asUntilFormula().getRightSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector()); + 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); @@ -121,10 +121,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(), @@ -182,7 +182,7 @@ bool SparseParametricMdpSimplifier::simplifyForReachabilityRewa return false; } storm::storage::BitVector targetStates = std::move( - propositionalChecker.check(formula.getSubformula().asEventuallyFormula().getSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector()); + 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-permissive/analysis/PermissiveSchedulers.cpp b/src/storm-permissive/analysis/PermissiveSchedulers.cpp index 5ce205bc4d..a73a11c45f 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); @@ -49,8 +50,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/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 3b7ad31bbf..62aaee7078 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 3f0f71845c..5a9a6fcea5 100644 --- a/src/storm-pomdp/analysis/QualitativeAnalysisOnGraphs.cpp +++ b/src/storm-pomdp/analysis/QualitativeAnalysisOnGraphs.cpp @@ -202,7 +202,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/transformer/GlobalPomdpMecChoiceEliminator.cpp b/src/storm-pomdp/transformer/GlobalPomdpMecChoiceEliminator.cpp index a357cc5917..a8f1f388a6 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/api/export.h b/src/storm/api/export.h index c24ea4ce33..baff690a6b 100644 --- a/src/storm/api/export.h +++ b/src/storm/api/export.h @@ -115,7 +115,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/modelchecker/AbstractModelChecker.cpp b/src/storm/modelchecker/AbstractModelChecker.cpp index f81331c3d2..6b42c5be6c 100644 --- a/src/storm/modelchecker/AbstractModelChecker.cpp +++ b/src/storm/modelchecker/AbstractModelChecker.cpp @@ -150,7 +150,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."); diff --git a/src/storm/modelchecker/csl/SparseCtmcCslModelChecker.cpp b/src/storm/modelchecker/csl/SparseCtmcCslModelChecker.cpp index 8a5e847156..b36579b09c 100644 --- a/src/storm/modelchecker/csl/SparseCtmcCslModelChecker.cpp +++ b/src/storm/modelchecker/csl/SparseCtmcCslModelChecker.cpp @@ -57,8 +57,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."); @@ -86,7 +86,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))); @@ -97,7 +97,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(), @@ -111,8 +111,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(), @@ -130,7 +130,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); @@ -148,7 +148,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); @@ -196,7 +196,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(), @@ -220,7 +220,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()); @@ -246,7 +246,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(), @@ -269,8 +269,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 203cbce8f1..f5aa67bc85 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/multiobjective/constraintbased/SparseCbAchievabilityQuery.cpp b/src/storm/modelchecker/multiobjective/constraintbased/SparseCbAchievabilityQuery.cpp index 225b7b12f5..4bfd4de17f 100644 --- a/src/storm/modelchecker/multiobjective/constraintbased/SparseCbAchievabilityQuery.cpp +++ b/src/storm/modelchecker/multiobjective/constraintbased/SparseCbAchievabilityQuery.cpp @@ -34,7 +34,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 cedca9edfe..75bdfe5c8b 100644 --- a/src/storm/modelchecker/multiobjective/deterministicScheds/DeterministicSchedsAchievabilityChecker.cpp +++ b/src/storm/modelchecker/multiobjective/deterministicScheds/DeterministicSchedsAchievabilityChecker.cpp @@ -63,13 +63,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); @@ -80,7 +80,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 0346b41e4f..c1d03129b9 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/SparsePcaaAchievabilityQuery.cpp b/src/storm/modelchecker/multiobjective/pcaa/SparsePcaaAchievabilityQuery.cpp index 9271fe33e6..562f4dbfc5 100644 --- a/src/storm/modelchecker/multiobjective/pcaa/SparsePcaaAchievabilityQuery.cpp +++ b/src/storm/modelchecker/multiobjective/pcaa/SparsePcaaAchievabilityQuery.cpp @@ -51,7 +51,8 @@ std::unique_ptr SparsePcaaAchievabilityQuerycheckAchievability(env); - 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/pcaa/SparsePcaaQuantitativeQuery.cpp b/src/storm/modelchecker/multiobjective/pcaa/SparsePcaaQuantitativeQuery.cpp index c13762c22e..e4e613f69a 100644 --- a/src/storm/modelchecker/multiobjective/pcaa/SparsePcaaQuantitativeQuery.cpp +++ b/src/storm/modelchecker/multiobjective/pcaa/SparsePcaaQuantitativeQuery.cpp @@ -91,7 +91,8 @@ std::unique_ptr SparsePcaaQuantitativeQuery(new ExplicitQuantitativeCheckResult( this->originalModel.getInitialStates().getNextSetIndex(0), resultForOriginalModel)); } else { - return std::unique_ptr(new ExplicitQualitativeCheckResult(this->originalModel.getInitialStates().getNextSetIndex(0), false)); + return std::unique_ptr( + new ExplicitQualitativeCheckResult(this->originalModel.getInitialStates().getNextSetIndex(0), false)); } } diff --git a/src/storm/modelchecker/multiobjective/preprocessing/SparseMultiObjectivePreprocessor.cpp b/src/storm/modelchecker/multiobjective/preprocessing/SparseMultiObjectivePreprocessor.cpp index 6eabbd6bda..1a51f2cafc 100644 --- a/src/storm/modelchecker/multiobjective/preprocessing/SparseMultiObjectivePreprocessor.cpp +++ b/src/storm/modelchecker/multiobjective/preprocessing/SparseMultiObjectivePreprocessor.cpp @@ -128,8 +128,8 @@ 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()) { @@ -139,9 +139,9 @@ void SparseMultiObjectivePreprocessor::removeIrrelevantStates(s 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(); + mc.check(pathFormula.asBoundedUntilFormula().getLeftSubformula(i))->template asExplicitQualitativeCheckResult().getTruthValuesVector(); auto rhs = - mc.check(pathFormula.asBoundedUntilFormula().getRightSubformula(i))->asExplicitQualitativeCheckResult().getTruthValuesVector(); + 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 +151,8 @@ 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 +161,12 @@ 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 +181,7 @@ 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 +210,13 @@ 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 +432,7 @@ 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 +445,7 @@ 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 +455,7 @@ 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 +542,7 @@ 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 344766e323..81815a9d85 100644 --- a/src/storm/modelchecker/prctl/HybridMdpPrctlModelChecker.cpp +++ b/src/storm/modelchecker/prctl/HybridMdpPrctlModelChecker.cpp @@ -176,7 +176,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 1686bbe010..431039c622 100644 --- a/src/storm/modelchecker/prctl/SparseDtmcPrctlModelChecker.cpp +++ b/src/storm/modelchecker/prctl/SparseDtmcPrctlModelChecker.cpp @@ -92,8 +92,8 @@ std::unique_ptr SparseDtmcPrctlModelChecker::c STORM_LOG_THROW(pathFormula.hasIntegerUpperBound(), storm::exceptions::InvalidPropertyException, "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(), @@ -109,7 +109,7 @@ std::unique_ptr SparseDtmcPrctlModelChecker::c 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::SparseDtmcPrctlHelper::computeNextProbabilities( env, this->getModel().getTransitionMatrix(), subResult.getTruthValuesVector()); return std::unique_ptr(new ExplicitQuantitativeCheckResult(std::move(numericResult))); @@ -121,8 +121,8 @@ std::unique_ptr SparseDtmcPrctlModelChecker::c 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::SparseDtmcPrctlHelper::computeUntilProbabilities( env, storm::solver::SolveGoal(this->getModel(), checkTask), this->getModel().getTransitionMatrix(), this->getModel().getBackwardTransitions(), leftResult.getTruthValuesVector(), rightResult.getTruthValuesVector(), checkTask.isQualitativeSet(), @@ -135,7 +135,7 @@ std::unique_ptr SparseDtmcPrctlModelChecker::c 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(); std::vector numericResult = storm::modelchecker::helper::SparseDtmcPrctlHelper::computeGloballyProbabilities( env, storm::solver::SolveGoal(this->getModel(), checkTask), this->getModel().getTransitionMatrix(), this->getModel().getBackwardTransitions(), subResult.getTruthValuesVector(), checkTask.isQualitativeSet()); @@ -151,7 +151,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); @@ -168,7 +168,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); @@ -236,7 +236,7 @@ std::unique_ptr SparseDtmcPrctlModelChecker::c 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::SparseDtmcPrctlHelper::computeReachabilityRewards( env, storm::solver::SolveGoal(this->getModel(), checkTask), this->getModel().getTransitionMatrix(), @@ -249,7 +249,7 @@ std::unique_ptr SparseDtmcPrctlModelChecker::c 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(); std::vector numericResult = storm::modelchecker::helper::SparseDtmcPrctlHelper::computeReachabilityTimes( env, storm::solver::SolveGoal(this->getModel(), checkTask), this->getModel().getTransitionMatrix(), this->getModel().getBackwardTransitions(), subResult.getTruthValuesVector(), checkTask.isQualitativeSet(), checkTask.getHint()); @@ -290,7 +290,7 @@ std::unique_ptr SparseDtmcPrctlModelChecker::c 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(); storm::modelchecker::helper::SparseDeterministicInfiniteHorizonHelper helper(this->getModel().getTransitionMatrix()); storm::modelchecker::helper::setInformationFromCheckTaskDeterministic(helper, checkTask, this->getModel()); @@ -320,8 +320,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( env, storm::solver::SolveGoal(this->getModel(), checkTask), this->getModel().getTransitionMatrix(), @@ -340,8 +340,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( env, storm::solver::SolveGoal(this->getModel(), checkTask), this->getModel().getTransitionMatrix(), diff --git a/src/storm/modelchecker/prctl/SparseMdpPrctlModelChecker.cpp b/src/storm/modelchecker/prctl/SparseMdpPrctlModelChecker.cpp index e532228189..8d19c3dea6 100644 --- a/src/storm/modelchecker/prctl/SparseMdpPrctlModelChecker.cpp +++ b/src/storm/modelchecker/prctl/SparseMdpPrctlModelChecker.cpp @@ -139,8 +139,8 @@ std::unique_ptr SparseMdpPrctlModelChecker::com "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(); + 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(), @@ -158,7 +158,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 +172,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 +192,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()); @@ -215,7 +215,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); @@ -245,7 +245,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,8 +276,8 @@ 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(); + ExplicitQualitativeCheckResult const& leftResult = leftResultPointer->template asExplicitQualitativeCheckResult(); + ExplicitQualitativeCheckResult const& rightResult = rightResultPointer->template asExplicitQualitativeCheckResult(); if constexpr (std::is_same_v) { throw exceptions::NotImplementedException() << "Conditional Probabilities are not supported with interval models"; } else { @@ -363,7 +363,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 +383,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(), @@ -446,7 +446,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, 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()); @@ -499,7 +499,7 @@ std::unique_ptr SparseMdpPrctlModelChecker::che 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())); diff --git a/src/storm/modelchecker/prctl/helper/rewardbounded/ProductModel.cpp b/src/storm/modelchecker/prctl/helper/rewardbounded/ProductModel.cpp index 7bb836cc88..02b9e2f177 100644 --- a/src/storm/modelchecker/prctl/helper/rewardbounded/ProductModel.cpp +++ b/src/storm/modelchecker/prctl/helper/rewardbounded/ProductModel.cpp @@ -133,8 +133,8 @@ storm::storage::MemoryStructure ProductModel::computeMemoryStructure( 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()); + (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 +155,7 @@ 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 +426,9 @@ 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 094a2332cf..8fa08b56c7 100644 --- a/src/storm/modelchecker/propositional/SparsePropositionalModelChecker.cpp +++ b/src/storm/modelchecker/propositional/SparsePropositionalModelChecker.cpp @@ -35,9 +35,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()))); } } @@ -47,7 +47,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 diff --git a/src/storm/modelchecker/reachability/SparseDtmcEliminationModelChecker.cpp b/src/storm/modelchecker/reachability/SparseDtmcEliminationModelChecker.cpp index 43296a4d13..506f03b3ff 100644 --- a/src/storm/modelchecker/reachability/SparseDtmcEliminationModelChecker.cpp +++ b/src/storm/modelchecker/reachability/SparseDtmcEliminationModelChecker.cpp @@ -55,7 +55,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(); @@ -112,7 +112,7 @@ std::unique_ptr SparseDtmcEliminationModelCheckerfilter(ExplicitQualitativeCheckResult(initialStates)); + checkResult->filter(ExplicitQualitativeCheckResult(initialStates)); } return checkResult; } @@ -177,7 +177,7 @@ std::unique_ptr SparseDtmcEliminationModelCheckerfilter(ExplicitQualitativeCheckResult(initialStates)); + checkResult->filter(ExplicitQualitativeCheckResult(initialStates)); } return checkResult; } @@ -350,8 +350,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. @@ -444,7 +444,7 @@ std::unique_ptr SparseDtmcEliminationModelCheckerfilter(ExplicitQualitativeCheckResult(this->getModel().getInitialStates() | psiStates)); + checkResult->filter(ExplicitQualitativeCheckResult(this->getModel().getInitialStates() | psiStates)); } return checkResult; } @@ -457,8 +457,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()); @@ -538,7 +538,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() : ""); @@ -650,8 +650,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 8549ce26d0..c43c2cf441 100644 --- a/src/storm/modelchecker/results/CheckResult.cpp +++ b/src/storm/modelchecker/results/CheckResult.cpp @@ -82,12 +82,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 @@ -188,6 +190,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(); @@ -221,6 +225,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; @@ -228,6 +234,9 @@ 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; @@ -235,5 +244,10 @@ template LexicographicCheckResult& CheckResult::asLexicog template LexicographicCheckResult const& CheckResult::asLexicographicCheckResult() const; #endif + +// 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 a9d59197a5..ac58ec1fc2 100644 --- a/src/storm/modelchecker/results/ExplicitParetoCurveCheckResult.cpp +++ b/src/storm/modelchecker/results/ExplicitParetoCurveCheckResult.cpp @@ -56,8 +56,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 f608a3065b..9404a7c355 100644 --- a/src/storm/modelchecker/results/ExplicitQualitativeCheckResult.cpp +++ b/src/storm/modelchecker/results/ExplicitQualitativeCheckResult.cpp @@ -3,53 +3,67 @@ #include "storm/modelchecker/results/ExplicitQualitativeCheckResult.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) : truthValues(truthValues) { // Intentionally left empty. } -ExplicitQualitativeCheckResult::ExplicitQualitativeCheckResult(boost::variant&& truthValues) : truthValues(std::move(truthValues)) { +template +ExplicitQualitativeCheckResult::ExplicitQualitativeCheckResult(boost::variant&& truthValues) + : truthValues(std::move(truthValues)) { // 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 +92,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 +117,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 +132,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 +147,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 +159,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 +180,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 +239,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()) { @@ -259,9 +288,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,10 +307,33 @@ 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; + +#ifdef STORM_HAVE_CARL +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; +#endif } // namespace modelchecker } // namespace storm diff --git a/src/storm/modelchecker/results/ExplicitQualitativeCheckResult.h b/src/storm/modelchecker/results/ExplicitQualitativeCheckResult.h index 8e13acbef7..9e67d1b277 100644 --- a/src/storm/modelchecker/results/ExplicitQualitativeCheckResult.h +++ b/src/storm/modelchecker/results/ExplicitQualitativeCheckResult.h @@ -9,12 +9,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; @@ -69,6 +72,9 @@ class ExplicitQualitativeCheckResult : public QualitativeCheckResult { // The values of the quantitative check result. boost::variant truthValues; + + // An optional scheduler that accompanies the values. + boost::optional>> scheduler; }; } // namespace modelchecker } // namespace storm diff --git a/src/storm/modelchecker/results/ExplicitQuantitativeCheckResult.cpp b/src/storm/modelchecker/results/ExplicitQuantitativeCheckResult.cpp index 3464ee9381..278ef000b4 100644 --- a/src/storm/modelchecker/results/ExplicitQuantitativeCheckResult.cpp +++ b/src/storm/modelchecker/results/ExplicitQuantitativeCheckResult.cpp @@ -61,7 +61,7 @@ ExplicitQuantitativeCheckResult::ExplicitQuantitativeCheckResult(boos } 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; @@ -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))); } 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))); } } diff --git a/src/storm/modelchecker/results/ExplicitQuantitativeCheckResult.h b/src/storm/modelchecker/results/ExplicitQuantitativeCheckResult.h index e9c9ac487d..125752ca14 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 @@ -39,7 +40,7 @@ class ExplicitQuantitativeCheckResult : public QuantitativeCheckResult const& other); virtual ~ExplicitQuantitativeCheckResult() = default; 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/storage/bisimulation/BisimulationDecomposition.cpp b/src/storm/storage/bisimulation/BisimulationDecomposition.cpp index 4828c13ef7..2f3206ac29 100644 --- a/src/storm/storage/bisimulation/BisimulationDecomposition.cpp +++ b/src/storm/storage/bisimulation/BisimulationDecomposition.cpp @@ -160,8 +160,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 = boost::none; } diff --git a/src/storm/transformer/MemoryIncorporation.cpp b/src/storm/transformer/MemoryIncorporation.cpp index a0d894e6a3..14554ec711 100644 --- a/src/storm/transformer/MemoryIncorporation.cpp +++ b/src/storm/transformer/MemoryIncorporation.cpp @@ -27,7 +27,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/test/storm-pars/modelchecker/region/monotonicity/MonotonicityCheckerTest.cpp b/src/test/storm-pars/modelchecker/region/monotonicity/MonotonicityCheckerTest.cpp index ceab40c2fe..4abd1731d5 100644 --- a/src/test/storm-pars/modelchecker/region/monotonicity/MonotonicityCheckerTest.cpp +++ b/src/test/storm-pars/modelchecker/region/monotonicity/MonotonicityCheckerTest.cpp @@ -52,7 +52,7 @@ 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); @@ -100,7 +100,7 @@ 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); @@ -149,7 +149,7 @@ 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); @@ -202,7 +202,7 @@ 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); @@ -256,7 +256,7 @@ 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..cf556ea3a0 100644 --- a/src/test/storm-pars/modelchecker/region/monotonicity/OrderExtenderTest.cpp +++ b/src/test/storm-pars/modelchecker/region/monotonicity/OrderExtenderTest.cpp @@ -144,7 +144,7 @@ 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 +191,7 @@ 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 +268,7 @@ 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 +359,7 @@ 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 +412,7 @@ 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-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 755a60f403..42fd7aa01e 100644 --- a/src/test/storm/modelchecker/multiobjective/MultiObjectiveSchedRestModelCheckerTest.cpp +++ b/src/test/storm/modelchecker/multiobjective/MultiObjectiveSchedRestModelCheckerTest.cpp @@ -274,13 +274,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; { @@ -293,7 +293,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()]); } } @@ -359,7 +359,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; @@ -371,7 +371,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 94df68f83b..074797ec94 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 74a47f3f6a..f4a19f9597 100644 --- a/src/test/storm/modelchecker/multiobjective/SparseMaPcaaMultiObjectiveModelCheckerTest.cpp +++ b/src/test/storm/modelchecker/multiobjective/SparseMaPcaaMultiObjectiveModelCheckerTest.cpp @@ -172,12 +172,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) { @@ -210,7 +210,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 baa4726654..16250a581f 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 d08c62033d..8a40aa4161 100644 --- a/src/test/storm/modelchecker/multiobjective/SparseMdpPcaaMultiObjectiveModelCheckerTest.cpp +++ b/src/test/storm/modelchecker/multiobjective/SparseMdpPcaaMultiObjectiveModelCheckerTest.cpp @@ -228,11 +228,11 @@ TEST(SparseMdpPcaaMultiObjectiveModelCheckerTest, 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(SparseMdpPcaaMultiObjectiveModelCheckerTest, zeroconf) { @@ -304,7 +304,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) { @@ -328,7 +328,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/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 635b352b69..c2204c444d 100644 --- a/src/test/storm/modelchecker/prctl/mdp/QuantileQueryTest.cpp +++ b/src/test/storm/modelchecker/prctl/mdp/QuantileQueryTest.cpp @@ -164,7 +164,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 27a9254a16..3d509f2fbc 100644 --- a/src/test/storm/modelchecker/prctl/mdp/RobustMdpPrctlModelCheckerTest.cpp +++ b/src/test/storm/modelchecker/prctl/mdp/RobustMdpPrctlModelCheckerTest.cpp @@ -17,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, From ee9f051ecc3d6094efe8280296ed56756c45c1e5 Mon Sep 17 00:00:00 2001 From: Luko van der Maas Date: Wed, 26 Nov 2025 15:44:13 +0100 Subject: [PATCH 02/51] Remember scheduler from quantitative scheduler when applying bounds --- resources/3rdparty/l3pp | 1 + .../ExplicitQualitativeCheckResult.cpp | 31 +++++++++++++++++-- .../results/ExplicitQualitativeCheckResult.h | 11 +++++-- .../ExplicitQuantitativeCheckResult.cpp | 4 +-- 4 files changed, 40 insertions(+), 7 deletions(-) create mode 160000 resources/3rdparty/l3pp 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/modelchecker/results/ExplicitQualitativeCheckResult.cpp b/src/storm/modelchecker/results/ExplicitQualitativeCheckResult.cpp index 9404a7c355..f39ac58a74 100644 --- a/src/storm/modelchecker/results/ExplicitQualitativeCheckResult.cpp +++ b/src/storm/modelchecker/results/ExplicitQualitativeCheckResult.cpp @@ -41,13 +41,16 @@ ExplicitQualitativeCheckResult::ExplicitQualitativeCheckResult(storm: } template -ExplicitQualitativeCheckResult::ExplicitQualitativeCheckResult(boost::variant const& truthValues) : truthValues(truthValues) { +ExplicitQualitativeCheckResult::ExplicitQualitativeCheckResult(boost::variant const& truthValues, + boost::optional>> scheduler) + : truthValues(truthValues), scheduler(scheduler) { // Intentionally left empty. } template -ExplicitQualitativeCheckResult::ExplicitQualitativeCheckResult(boost::variant&& truthValues) - : truthValues(std::move(truthValues)) { +ExplicitQualitativeCheckResult::ExplicitQualitativeCheckResult(boost::variant&& truthValues, + boost::optional>> scheduler) + : truthValues(std::move(truthValues)), scheduler(scheduler) { // Intentionally left empty. } @@ -270,6 +273,28 @@ void ExplicitQualitativeCheckResult::filter(QualitativeCheckResult co } } +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.get(); +} + +template +storm::storage::Scheduler& ExplicitQualitativeCheckResult::getScheduler() { + STORM_LOG_THROW(this->hasScheduler(), storm::exceptions::InvalidOperationException, "Unable to retrieve non-existing scheduler."); + return *scheduler.get(); +} + template void insertJsonEntry(storm::json& json, uint64_t const& id, bool value, std::optional const& stateValuations = std::nullopt, diff --git a/src/storm/modelchecker/results/ExplicitQualitativeCheckResult.h b/src/storm/modelchecker/results/ExplicitQualitativeCheckResult.h index 9e67d1b277..14d781ef86 100644 --- a/src/storm/modelchecker/results/ExplicitQualitativeCheckResult.h +++ b/src/storm/modelchecker/results/ExplicitQualitativeCheckResult.h @@ -30,8 +30,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, + boost::optional>> scheduler = boost::none); + ExplicitQualitativeCheckResult(boost::variant&& truthValues, + boost::optional>> scheduler = boost::none); ExplicitQualitativeCheckResult(ExplicitQualitativeCheckResult const& other) = default; ExplicitQualitativeCheckResult& operator=(ExplicitQualitativeCheckResult const& other) = default; @@ -63,6 +65,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; diff --git a/src/storm/modelchecker/results/ExplicitQuantitativeCheckResult.cpp b/src/storm/modelchecker/results/ExplicitQuantitativeCheckResult.cpp index 278ef000b4..f9dc8f2bef 100644 --- a/src/storm/modelchecker/results/ExplicitQuantitativeCheckResult.cpp +++ b/src/storm/modelchecker/results/ExplicitQuantitativeCheckResult.cpp @@ -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))); } } From 1868e4421eb6c70c37c5f8a7b030ffa4271c91f9 Mon Sep 17 00:00:00 2001 From: Luko van der Maas Date: Wed, 26 Nov 2025 15:49:56 +0100 Subject: [PATCH 03/51] Export schedulers for qualitative models in the cli --- src/storm-cli-utilities/model-handling.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/storm-cli-utilities/model-handling.h b/src/storm-cli-utilities/model-handling.h index 6f7eaee4b2..62dc3a4279 100644 --- a/src/storm-cli-utilities/model-handling.h +++ b/src/storm-cli-utilities/model-handling.h @@ -1348,6 +1348,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."); } From 0da7fd862ebf3fc3938d951481f0b5f35e022d1a Mon Sep 17 00:00:00 2001 From: Luko van der Maas Date: Wed, 3 Dec 2025 15:21:40 +0100 Subject: [PATCH 04/51] Fix test --- .../transformer/IntervalEndComponentPreserverCheckTest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/storm-pars/transformer/IntervalEndComponentPreserverCheckTest.cpp b/src/test/storm-pars/transformer/IntervalEndComponentPreserverCheckTest.cpp index bdc748e6e0..6fd2a97347 100644 --- a/src/test/storm-pars/transformer/IntervalEndComponentPreserverCheckTest.cpp +++ b/src/test/storm-pars/transformer/IntervalEndComponentPreserverCheckTest.cpp @@ -53,7 +53,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()); From 798d3e4f0e49c64cc3560e2429e38ff00ddfac4f Mon Sep 17 00:00:00 2001 From: Luko van der Maas Date: Wed, 3 Dec 2025 15:25:11 +0100 Subject: [PATCH 05/51] Format --- src/storm-cli-utilities/model-handling.h | 18 +++---- ...SparseDtmcParameterLiftingModelChecker.cpp | 25 ++++++---- .../SparseMdpParameterLiftingModelChecker.cpp | 25 ++++++---- .../SparseParametricDtmcSimplifier.cpp | 15 +++--- .../SparseParametricMdpSimplifier.cpp | 15 +++--- .../SparseMultiObjectivePreprocessor.cpp | 48 ++++++++++++------- .../helper/rewardbounded/ProductModel.cpp | 18 ++++--- .../monotonicity/MonotonicityCheckerTest.cpp | 15 ++++-- .../region/monotonicity/OrderExtenderTest.cpp | 15 ++++-- 9 files changed, 121 insertions(+), 73 deletions(-) diff --git a/src/storm-cli-utilities/model-handling.h b/src/storm-cli-utilities/model-handling.h index 62dc3a4279..1e81947d93 100644 --- a/src/storm-cli-utilities/model-handling.h +++ b/src/storm-cli-utilities/model-handling.h @@ -903,14 +903,14 @@ inline void printCounterexample(std::shared_ptr -requires(!std::derived_from>) inline void generateCounterexamples(std::shared_ptr const&, - SymbolicInput const&) { + requires(!std::derived_from>) +inline void generateCounterexamples(std::shared_ptr const&, SymbolicInput const&) { STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Counterexample generation is not supported for this data-type."); } template -requires(std::derived_from>) inline void generateCounterexamples(std::shared_ptr const& sparseModel, - SymbolicInput const& input) { + requires(std::derived_from>) +inline void generateCounterexamples(std::shared_ptr const& sparseModel, SymbolicInput const& input) { using ValueType = typename ModelType::ValueType; for (auto& rewModel : sparseModel->getRewardModels()) { @@ -966,8 +966,8 @@ requires(std::derived_from>) inl } template -requires(!storm::IsIntervalType) void printFilteredResult(std::unique_ptr const& result, - storm::modelchecker::FilterType ft) { + requires(!storm::IsIntervalType) +void printFilteredResult(std::unique_ptr const& result, storm::modelchecker::FilterType ft) { if (result->isQuantitative()) { if (ft == storm::modelchecker::FilterType::VALUES) { STORM_PRINT(*result); @@ -1034,9 +1034,9 @@ inline void printModelCheckingProperty(storm::jani::Property const& property) { } template -requires(!storm::IsIntervalType) void printResult(std::unique_ptr const& result, - storm::logic::Formula const& filterStatesFormula, - storm::modelchecker::FilterType const& filterType, storm::utility::Stopwatch* watch = nullptr) { + requires(!storm::IsIntervalType) +void printResult(std::unique_ptr const& result, storm::logic::Formula const& filterStatesFormula, + storm::modelchecker::FilterType const& filterType, storm::utility::Stopwatch* watch = nullptr) { if (result) { std::stringstream ss; ss << "'" << filterStatesFormula << "'"; diff --git a/src/storm-pars/modelchecker/region/SparseDtmcParameterLiftingModelChecker.cpp b/src/storm-pars/modelchecker/region/SparseDtmcParameterLiftingModelChecker.cpp index 150f271501..002151ac7c 100644 --- a/src/storm-pars/modelchecker/region/SparseDtmcParameterLiftingModelChecker.cpp +++ b/src/storm-pars/modelchecker/region/SparseDtmcParameterLiftingModelChecker.cpp @@ -150,10 +150,12 @@ void SparseDtmcParameterLiftingModelCheckertemplate asExplicitQualitativeCheckResult().getTruthValuesVector()); - storm::storage::BitVector psiStates = - std::move(propositionalChecker.check(checkTask.getFormula().getRightSubformula())->template 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); @@ -192,10 +194,12 @@ void SparseDtmcParameterLiftingModelCheckertemplate asExplicitQualitativeCheckResult().getTruthValuesVector()); - storm::storage::BitVector psiStates = - std::move(propositionalChecker.check(checkTask.getFormula().getRightSubformula())->template 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 = @@ -264,8 +268,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())->template 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 ff81f266b4..4294ad72b5 100644 --- a/src/storm-pars/modelchecker/region/SparseMdpParameterLiftingModelChecker.cpp +++ b/src/storm-pars/modelchecker/region/SparseMdpParameterLiftingModelChecker.cpp @@ -109,10 +109,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())->template asExplicitQualitativeCheckResult().getTruthValuesVector()); - storm::storage::BitVector psiStates = - std::move(propositionalChecker.check(checkTask.getFormula().getRightSubformula())->template 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()) @@ -152,10 +154,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())->template asExplicitQualitativeCheckResult().getTruthValuesVector()); - storm::storage::BitVector psiStates = - std::move(propositionalChecker.check(checkTask.getFormula().getRightSubformula())->template 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 = @@ -203,8 +207,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())->template 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/transformer/SparseParametricDtmcSimplifier.cpp b/src/storm-pars/transformer/SparseParametricDtmcSimplifier.cpp index ea66d7728e..2910fa4881 100644 --- a/src/storm-pars/transformer/SparseParametricDtmcSimplifier.cpp +++ b/src/storm-pars/transformer/SparseParametricDtmcSimplifier.cpp @@ -29,10 +29,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())->template asExplicitQualitativeCheckResult().getTruthValuesVector()); - storm::storage::BitVector psiStates = std::move( - propositionalChecker.check(formula.getSubformula().asUntilFormula().getRightSubformula())->template 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 @@ -151,8 +153,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())->template 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 4026d9dcfb..ed9ac1efbe 100644 --- a/src/storm-pars/transformer/SparseParametricMdpSimplifier.cpp +++ b/src/storm-pars/transformer/SparseParametricMdpSimplifier.cpp @@ -34,10 +34,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())->template asExplicitQualitativeCheckResult().getTruthValuesVector()); - storm::storage::BitVector psiStates = std::move( - propositionalChecker.check(formula.getSubformula().asUntilFormula().getRightSubformula())->template 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); @@ -181,8 +183,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())->template 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/modelchecker/multiobjective/preprocessing/SparseMultiObjectivePreprocessor.cpp b/src/storm/modelchecker/multiobjective/preprocessing/SparseMultiObjectivePreprocessor.cpp index 1a51f2cafc..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())->template asExplicitQualitativeCheckResult().getTruthValuesVector(); - auto rhs = mc.check(pathFormula.asUntilFormula().getRightSubformula())->template 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))->template asExplicitQualitativeCheckResult().getTruthValuesVector(); - auto rhs = - mc.check(pathFormula.asBoundedUntilFormula().getRightSubformula(i))->template 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())->template asExplicitQualitativeCheckResult().getTruthValuesVector(); - auto rhs = mc.check(pathFormula.asBoundedUntilFormula().getRightSubformula())->template 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())->template 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())->template 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())->template 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,7 +221,8 @@ void SparseMultiObjectivePreprocessor::removeIrrelevantStates(s } } else if (opFormula->isTimeOperatorFormula()) { if (pathFormula.isEventuallyFormula()) { - auto phi = mc.check(pathFormula.asEventuallyFormula().getSubformula())->template 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."); @@ -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())->template 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())->template 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())->template 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())->template 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/helper/rewardbounded/ProductModel.cpp b/src/storm/modelchecker/prctl/helper/rewardbounded/ProductModel.cpp index 02b9e2f177..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())->template asExplicitQualitativeCheckResult().getTruthValuesVector() | - mc.check(dimension.formula->asBoundedUntilFormula().getRightSubformula())->template 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)->template 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)->template 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)->template 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/test/storm-pars/modelchecker/region/monotonicity/MonotonicityCheckerTest.cpp b/src/test/storm-pars/modelchecker/region/monotonicity/MonotonicityCheckerTest.cpp index 4abd1731d5..9a78b6f47c 100644 --- a/src/test/storm-pars/modelchecker/region/monotonicity/MonotonicityCheckerTest.cpp +++ b/src/test/storm-pars/modelchecker/region/monotonicity/MonotonicityCheckerTest.cpp @@ -52,7 +52,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())->template asExplicitQualitativeCheckResult().getTruthValuesVector(); + psiStates = + propositionalChecker.check(formula.getSubformula())->template asExplicitQualitativeCheckResult().getTruthValuesVector(); // Get the maybeStates std::pair statesWithProbability01 = storm::utility::graph::performProb01(model->getBackwardTransitions(), phiStates, psiStates); @@ -100,7 +101,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())->template asExplicitQualitativeCheckResult().getTruthValuesVector(); + psiStates = + propositionalChecker.check(formula.getSubformula())->template asExplicitQualitativeCheckResult().getTruthValuesVector(); // Get the maybeStates std::pair statesWithProbability01 = storm::utility::graph::performProb01(model->getBackwardTransitions(), phiStates, psiStates); @@ -149,7 +151,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())->template asExplicitQualitativeCheckResult().getTruthValuesVector(); + psiStates = + propositionalChecker.check(formula.getSubformula())->template asExplicitQualitativeCheckResult().getTruthValuesVector(); // Get the maybeStates std::pair statesWithProbability01 = storm::utility::graph::performProb01(model->getBackwardTransitions(), phiStates, psiStates); @@ -202,7 +205,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())->template asExplicitQualitativeCheckResult().getTruthValuesVector(); + psiStates = + propositionalChecker.check(formula.getSubformula())->template asExplicitQualitativeCheckResult().getTruthValuesVector(); // Get the maybeStates std::pair statesWithProbability01 = storm::utility::graph::performProb01(model->getBackwardTransitions(), phiStates, psiStates); @@ -256,7 +260,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())->template 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 cf556ea3a0..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())->template 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())->template 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())->template 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())->template 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())->template asExplicitQualitativeCheckResult().getTruthValuesVector(); + psiStates = + propositionalChecker.check(formula.getSubformula())->template asExplicitQualitativeCheckResult().getTruthValuesVector(); // Get the maybeStates std::pair statesWithProbability01 = storm::utility::graph::performProb01(model->getBackwardTransitions(), phiStates, psiStates); From 73e73d8d5552d293002109fe25e24c214aaabb3d Mon Sep 17 00:00:00 2001 From: Luko van der Maas Date: Wed, 3 Dec 2025 15:34:29 +0100 Subject: [PATCH 06/51] remove weird git thing --- resources/3rdparty/l3pp | 1 - 1 file changed, 1 deletion(-) delete mode 160000 resources/3rdparty/l3pp diff --git a/resources/3rdparty/l3pp b/resources/3rdparty/l3pp deleted file mode 160000 index e4f8d7fe6c..0000000000 --- a/resources/3rdparty/l3pp +++ /dev/null @@ -1 +0,0 @@ -Subproject commit e4f8d7fe6c328849aff34d2dfd6fd592c14070d5 From f3f81e1aff674933e888b528026e6e0a5ae84426 Mon Sep 17 00:00:00 2001 From: Luko van der Maas Date: Wed, 3 Dec 2025 17:00:00 +0100 Subject: [PATCH 07/51] fix one ValueType --- .../SparseDerivativeInstantiationModelChecker.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/storm-pars/derivative/SparseDerivativeInstantiationModelChecker.cpp b/src/storm-pars/derivative/SparseDerivativeInstantiationModelChecker.cpp index ce1ae41f60..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)->template 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)->template asExplicitQualitativeCheckResult().getTruthValuesVector(); - avoid = propositionalChecker.check(leftSubformula)->template 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)->template asExplicitQualitativeCheckResult().getTruthValuesVector(); + target = propositionalChecker.check(subformula)->template asExplicitQualitativeCheckResult().getTruthValuesVector(); } } initialStateModel = model.getStates("init").getNextSetIndex(0); From 02350c239dc7e068819fa726c2e7279901011996 Mon Sep 17 00:00:00 2001 From: Luko van der Maas Date: Wed, 4 Mar 2026 16:59:58 +0100 Subject: [PATCH 08/51] Refactor ExplicitQualitativeCheckResult constructor to use std::optional for scheduler parameter --- .../modelchecker/results/ExplicitQualitativeCheckResult.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/storm/modelchecker/results/ExplicitQualitativeCheckResult.cpp b/src/storm/modelchecker/results/ExplicitQualitativeCheckResult.cpp index 1ca56bc279..8707f463ed 100644 --- a/src/storm/modelchecker/results/ExplicitQualitativeCheckResult.cpp +++ b/src/storm/modelchecker/results/ExplicitQualitativeCheckResult.cpp @@ -42,14 +42,14 @@ ExplicitQualitativeCheckResult::ExplicitQualitativeCheckResult(storm: template ExplicitQualitativeCheckResult::ExplicitQualitativeCheckResult(boost::variant const& truthValues, - boost::optional>> scheduler) + std::optional>> scheduler) : truthValues(truthValues), scheduler(scheduler) { // Intentionally left empty. } template ExplicitQualitativeCheckResult::ExplicitQualitativeCheckResult(boost::variant&& truthValues, - boost::optional>> scheduler) + std::optional>> scheduler) : truthValues(std::move(truthValues)), scheduler(scheduler) { // Intentionally left empty. } From a82759ae27667098653ddf58cb62d542d8016552 Mon Sep 17 00:00:00 2001 From: Luko van der Maas Date: Thu, 5 Mar 2026 15:25:30 +0100 Subject: [PATCH 09/51] fix some new location and swich to std::optional --- .../modelchecker/multiobjective/pcaa/SparsePcaaQuery.cpp | 6 +++--- .../modelchecker/prctl/SparseDtmcPrctlModelChecker.cpp | 2 +- src/storm/modelchecker/results/CheckResult.cpp | 1 + .../results/ExplicitQualitativeCheckResult.cpp | 7 +++---- .../modelchecker/results/ExplicitQualitativeCheckResult.h | 6 +++--- .../results/ExplicitQuantitativeCheckResult.cpp | 8 ++++---- .../results/ExplicitQuantitativeCheckResult.h | 6 +++--- .../prctl/dtmc/RobustDtmcPrctlModelCheckerTest.cpp | 8 ++++---- 8 files changed, 22 insertions(+), 22 deletions(-) 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/prctl/SparseDtmcPrctlModelChecker.cpp b/src/storm/modelchecker/prctl/SparseDtmcPrctlModelChecker.cpp index 8f8fd1f980..394fe5702c 100644 --- a/src/storm/modelchecker/prctl/SparseDtmcPrctlModelChecker.cpp +++ b/src/storm/modelchecker/prctl/SparseDtmcPrctlModelChecker.cpp @@ -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(), diff --git a/src/storm/modelchecker/results/CheckResult.cpp b/src/storm/modelchecker/results/CheckResult.cpp index 0bdd406f21..94c0ed9d26 100644 --- a/src/storm/modelchecker/results/CheckResult.cpp +++ b/src/storm/modelchecker/results/CheckResult.cpp @@ -1,6 +1,7 @@ #include "storm/modelchecker/results/CheckResult.h" #include "storm/adapters/RationalFunctionAdapter.h" +#include "storm/adapters/IntervalAdapter.h" #include "storm/modelchecker/results/ExplicitParetoCurveCheckResult.h" #include "storm/modelchecker/results/ExplicitQualitativeCheckResult.h" #include "storm/modelchecker/results/ExplicitQuantitativeCheckResult.h" diff --git a/src/storm/modelchecker/results/ExplicitQualitativeCheckResult.cpp b/src/storm/modelchecker/results/ExplicitQualitativeCheckResult.cpp index 8707f463ed..50b9a9e16c 100644 --- a/src/storm/modelchecker/results/ExplicitQualitativeCheckResult.cpp +++ b/src/storm/modelchecker/results/ExplicitQualitativeCheckResult.cpp @@ -1,3 +1,4 @@ +#include "storm/adapters/IntervalAdapter.h" #include "storm/adapters/RationalNumberAdapter.h" // Must come first. TODO: fix #include "storm/modelchecker/results/ExplicitQualitativeCheckResult.h" @@ -286,13 +287,13 @@ void ExplicitQualitativeCheckResult::setScheduler(std::unique_ptr storm::storage::Scheduler const& ExplicitQualitativeCheckResult::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& ExplicitQualitativeCheckResult::getScheduler() { STORM_LOG_THROW(this->hasScheduler(), storm::exceptions::InvalidOperationException, "Unable to retrieve non-existing scheduler."); - return *scheduler.get(); + return *scheduler.value(); } template @@ -337,7 +338,6 @@ template class ExplicitQualitativeCheckResult; template storm::json ExplicitQualitativeCheckResult::toJson(std::optional const&, std::optional const&) const; -#ifdef STORM_HAVE_CARL template storm::json ExplicitQualitativeCheckResult::toJson( std::optional const&, std::optional const&) const; @@ -358,7 +358,6 @@ template storm::json ExplicitQualitativeCheckResult::to std::optional const&) const; template storm::json ExplicitQualitativeCheckResult::toJson( std::optional const&, std::optional const&) const; -#endif } // namespace modelchecker } // namespace storm diff --git a/src/storm/modelchecker/results/ExplicitQualitativeCheckResult.h b/src/storm/modelchecker/results/ExplicitQualitativeCheckResult.h index 6825f4bce5..95db1271bb 100644 --- a/src/storm/modelchecker/results/ExplicitQualitativeCheckResult.h +++ b/src/storm/modelchecker/results/ExplicitQualitativeCheckResult.h @@ -30,9 +30,9 @@ class ExplicitQualitativeCheckResult : public QualitativeCheckResult { ExplicitQualitativeCheckResult(vector_type const& truthValues); ExplicitQualitativeCheckResult(vector_type&& truthValues); ExplicitQualitativeCheckResult(boost::variant const& truthValues, - boost::optional>> scheduler = boost::none); + std::optional>> scheduler = {}); ExplicitQualitativeCheckResult(boost::variant&& truthValues, - boost::optional>> scheduler = boost::none); + std::optional>> scheduler = {}); ExplicitQualitativeCheckResult(ExplicitQualitativeCheckResult const& other) = default; ExplicitQualitativeCheckResult& operator=(ExplicitQualitativeCheckResult const& other) = default; @@ -79,7 +79,7 @@ class ExplicitQualitativeCheckResult : public QualitativeCheckResult { boost::variant truthValues; // An optional scheduler that accompanies the values. - boost::optional>> scheduler; + std::optional>> scheduler; }; } // namespace modelchecker } // namespace storm diff --git a/src/storm/modelchecker/results/ExplicitQuantitativeCheckResult.cpp b/src/storm/modelchecker/results/ExplicitQuantitativeCheckResult.cpp index ec8222fe09..d775b924e4 100644 --- a/src/storm/modelchecker/results/ExplicitQuantitativeCheckResult.cpp +++ b/src/storm/modelchecker/results/ExplicitQuantitativeCheckResult.cpp @@ -48,14 +48,14 @@ 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. } @@ -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 diff --git a/src/storm/modelchecker/results/ExplicitQuantitativeCheckResult.h b/src/storm/modelchecker/results/ExplicitQuantitativeCheckResult.h index 125752ca14..bf543cd180 100644 --- a/src/storm/modelchecker/results/ExplicitQuantitativeCheckResult.h +++ b/src/storm/modelchecker/results/ExplicitQuantitativeCheckResult.h @@ -32,9 +32,9 @@ 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; @@ -85,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/test/storm/modelchecker/prctl/dtmc/RobustDtmcPrctlModelCheckerTest.cpp b/src/test/storm/modelchecker/prctl/dtmc/RobustDtmcPrctlModelCheckerTest.cpp index 1ee43344b7..07369bfbbc 100644 --- a/src/test/storm/modelchecker/prctl/dtmc/RobustDtmcPrctlModelCheckerTest.cpp +++ b/src/test/storm/modelchecker/prctl/dtmc/RobustDtmcPrctlModelCheckerTest.cpp @@ -16,11 +16,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 +138,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 +148,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]); } } From f07309c5c00dc83790bb0d84d57f3262ef7bae68 Mon Sep 17 00:00:00 2001 From: Luko van der Maas Date: Thu, 5 Mar 2026 15:29:43 +0100 Subject: [PATCH 10/51] format --- src/storm/modelchecker/results/CheckResult.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/storm/modelchecker/results/CheckResult.cpp b/src/storm/modelchecker/results/CheckResult.cpp index 94c0ed9d26..f6c6827c0b 100644 --- a/src/storm/modelchecker/results/CheckResult.cpp +++ b/src/storm/modelchecker/results/CheckResult.cpp @@ -1,7 +1,7 @@ #include "storm/modelchecker/results/CheckResult.h" -#include "storm/adapters/RationalFunctionAdapter.h" #include "storm/adapters/IntervalAdapter.h" +#include "storm/adapters/RationalFunctionAdapter.h" #include "storm/modelchecker/results/ExplicitParetoCurveCheckResult.h" #include "storm/modelchecker/results/ExplicitQualitativeCheckResult.h" #include "storm/modelchecker/results/ExplicitQuantitativeCheckResult.h" From 4be794c5a5964b10c2271b67c16a845cabd251a7 Mon Sep 17 00:00:00 2001 From: Luko van der Maas Date: Fri, 6 Mar 2026 10:46:55 +0100 Subject: [PATCH 11/51] Update ExplicitQualitativeCheckResult usage to use SolutionType and include IntervalForward in tests --- src/storm/modelchecker/prctl/SparseDtmcPrctlModelChecker.cpp | 4 ++-- .../prctl/dtmc/RobustDtmcPrctlModelCheckerTest.cpp | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/storm/modelchecker/prctl/SparseDtmcPrctlModelChecker.cpp b/src/storm/modelchecker/prctl/SparseDtmcPrctlModelChecker.cpp index 394fe5702c..39c60fea41 100644 --- a/src/storm/modelchecker/prctl/SparseDtmcPrctlModelChecker.cpp +++ b/src/storm/modelchecker/prctl/SparseDtmcPrctlModelChecker.cpp @@ -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->template asExplicitQualitativeCheckResult(); - ExplicitQualitativeCheckResult const& rightResult = rightResultPointer->template 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(), diff --git a/src/test/storm/modelchecker/prctl/dtmc/RobustDtmcPrctlModelCheckerTest.cpp b/src/test/storm/modelchecker/prctl/dtmc/RobustDtmcPrctlModelCheckerTest.cpp index 07369bfbbc..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" From 7e57f56b2dee2a778574db5b28ed31543d77c486 Mon Sep 17 00:00:00 2001 From: Luko van der Maas Date: Fri, 6 Mar 2026 11:20:47 +0100 Subject: [PATCH 12/51] Fix other qualitative check result value types --- .../prctl/SparseDtmcPrctlModelChecker.cpp | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/storm/modelchecker/prctl/SparseDtmcPrctlModelChecker.cpp b/src/storm/modelchecker/prctl/SparseDtmcPrctlModelChecker.cpp index 39c60fea41..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->template asExplicitQualitativeCheckResult(); - ExplicitQualitativeCheckResult const& rightResult = rightResultPointer->template 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->template asExplicitQualitativeCheckResult(); + ExplicitQualitativeCheckResult const& subResult = subResultPointer->template asExplicitQualitativeCheckResult(); std::vector numericResult = storm::modelchecker::helper::SparseDtmcPrctlHelper::computeNextProbabilities( env, this->getModel().getTransitionMatrix(), subResult.getTruthValuesVector()); @@ -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->template 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)->template 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)->template 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->template asExplicitQualitativeCheckResult(); + ExplicitQualitativeCheckResult const& subResult = subResultPointer->template asExplicitQualitativeCheckResult(); auto rewardModel = storm::utility::createFilteredRewardModel(this->getModel(), checkTask); std::vector numericResult = storm::modelchecker::helper::SparseDtmcPrctlHelper::computeReachabilityRewards( @@ -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->template 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->template asExplicitQualitativeCheckResult(); - ExplicitQualitativeCheckResult const& rightResult = rightResultPointer->template 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->template asExplicitQualitativeCheckResult(); - ExplicitQualitativeCheckResult const& rightResult = rightResultPointer->template asExplicitQualitativeCheckResult(); + ExplicitQualitativeCheckResult const& leftResult = leftResultPointer->template asExplicitQualitativeCheckResult(); + ExplicitQualitativeCheckResult const& rightResult = rightResultPointer->template asExplicitQualitativeCheckResult(); std::vector numericResult = storm::modelchecker::helper::SparseDtmcPrctlHelper::computeConditionalRewards( From c7268369c32a88e55a4e5fe81742e044c64e23a8 Mon Sep 17 00:00:00 2001 From: Luko van der Maas Date: Tue, 10 Mar 2026 13:16:33 +0100 Subject: [PATCH 13/51] Add exact Intervals and bounded model checking of intervals --- src/storm-cli-utilities/model-handling.h | 80 +++++++--- src/storm-parsers/parser/ValueParser.cpp | 48 ++++++ src/storm/adapters/IntervalForward.h | 8 + src/storm/io/DirectEncodingExporter.cpp | 5 + .../modelchecker/AbstractModelChecker.cpp | 5 + src/storm/modelchecker/AbstractModelChecker.h | 20 ++- ...ndeterministicStepBoundedHorizonHelper.cpp | 65 +++++--- ...NondeterministicStepBoundedHorizonHelper.h | 12 +- .../hints/ExplicitModelCheckerHint.cpp | 1 + .../modelchecker/hints/ModelCheckerHint.cpp | 2 + .../prctl/SparseMdpPrctlModelChecker.cpp | 104 ++++++------- .../prctl/SparseMdpPrctlModelChecker.h | 2 +- .../SparseMdpEndComponentInformation.cpp | 10 +- .../prctl/helper/SparseMdpPrctlHelper.cpp | 103 +++++++++++-- .../prctl/helper/SparseMdpPrctlHelper.h | 6 + .../MultiDimensionalRewardUnfolding.cpp | 2 + .../SparsePropositionalModelChecker.cpp | 8 + .../SparsePropositionalModelChecker.h | 20 ++- .../ExplicitQualitativeCheckResult.cpp | 1 + src/storm/models/sparse/Ctmc.cpp | 2 + .../models/sparse/DeterministicModel.cpp | 2 + src/storm/models/sparse/Dtmc.cpp | 3 + src/storm/models/sparse/MarkovAutomaton.cpp | 2 + src/storm/models/sparse/Mdp.cpp | 2 + src/storm/models/sparse/Model.cpp | 4 +- .../models/sparse/NondeterministicModel.cpp | 4 +- src/storm/models/sparse/Pomdp.cpp | 2 + src/storm/models/sparse/Smg.cpp | 2 + .../models/sparse/StandardRewardModel.cpp | 42 +++++ .../models/sparse/StochasticTwoPlayerGame.cpp | 4 +- .../settings/modules/GeneralSettings.cpp | 6 + src/storm/settings/modules/GeneralSettings.h | 8 + .../IterativeMinMaxLinearEquationSolver.cpp | 34 ++-- src/storm/solver/LinearEquationSolver.cpp | 2 +- .../solver/MinMaxLinearEquationSolver.cpp | 10 +- src/storm/solver/SolveGoal.cpp | 1 + .../StandardMinMaxLinearEquationSolver.cpp | 1 + .../solver/helper/SchedulerTrackingHelper.cpp | 2 + .../solver/helper/ValueIterationHelper.cpp | 2 + .../solver/helper/ValueIterationOperator.cpp | 4 +- .../solver/helper/ValueIterationOperator.h | 32 +++- src/storm/solver/multiplier/Multiplier.cpp | 82 ++++++---- src/storm/solver/multiplier/Multiplier.h | 36 +++-- .../multiplier/ViOperatorMultiplier.cpp | 105 +++++++------ .../solver/multiplier/ViOperatorMultiplier.h | 17 +- src/storm/storage/Distribution.cpp | 5 + .../MaximalEndComponentDecomposition.cpp | 4 + src/storm/storage/Scheduler.cpp | 1 + src/storm/storage/SchedulerChoice.cpp | 2 + src/storm/storage/SparseMatrix.cpp | 25 ++- src/storm/storage/SparseMatrix.h | 7 + ...tronglyConnectedComponentDecomposition.cpp | 1 + .../MemoryStructureBuilder.cpp | 2 + .../SparseModelMemoryProduct.cpp | 2 + src/storm/transformer/AddUncertainty.cpp | 14 +- src/storm/transformer/AddUncertainty.h | 3 +- src/storm/transformer/SubsystemBuilder.cpp | 9 ++ src/storm/utility/ConstantsComparator.cpp | 1 + src/storm/utility/NumberTraits.h | 13 ++ src/storm/utility/builder.cpp | 6 + src/storm/utility/constants.cpp | 52 +++++++ src/storm/utility/graph.cpp | 145 ++++++++++++++++++ .../mdp/RobustMdpPrctlModelCheckerTest.cpp | 50 +++++- 63 files changed, 992 insertions(+), 263 deletions(-) diff --git a/src/storm-cli-utilities/model-handling.h b/src/storm-cli-utilities/model-handling.h index f28dc7faf2..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 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/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/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 92122e5605..56664fcf2a 100644 --- a/src/storm/modelchecker/AbstractModelChecker.cpp +++ b/src/storm/modelchecker/AbstractModelChecker.cpp @@ -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/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/prctl/SparseMdpPrctlModelChecker.cpp b/src/storm/modelchecker/prctl/SparseMdpPrctlModelChecker.cpp index 0d21f5c533..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->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))); } + } 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))); } } @@ -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(); @@ -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(); @@ -278,12 +279,12 @@ std::unique_ptr SparseMdpPrctlModelChecker::com std::unique_ptr rightResultPointer = this->check(env, conditionalFormula.getConditionFormula().asEventuallyFormula().getSubformula()); ExplicitQualitativeCheckResult const& leftResult = leftResultPointer->template asExplicitQualitativeCheckResult(); ExplicitQualitativeCheckResult const& rightResult = rightResultPointer->template asExplicitQualitativeCheckResult(); - if constexpr (std::is_same_v) { + 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); @@ -439,7 +436,7 @@ 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(); @@ -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,7 +492,7 @@ 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) { @@ -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..1afc0a3a39 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,7 +777,7 @@ MDPSparseModelCheckingHelperReturnType SparseMdpPrctlHelper>(*scheduler, resultForMaybeStates.getScheduler(), + 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,6 +1061,9 @@ 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."); + 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) { @@ -1083,6 +1086,45 @@ std::vector SparseMdpPrctlHelper::compute }) .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<> std::vector SparseMdpPrctlHelper::computeReachabilityRewards( @@ -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,7 +1523,7 @@ 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)); @@ -1546,6 +1597,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..cbcc545b4d 100644 --- a/src/storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.h +++ b/src/storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.h @@ -98,6 +98,12 @@ 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); + 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/propositional/SparsePropositionalModelChecker.cpp b/src/storm/modelchecker/propositional/SparsePropositionalModelChecker.cpp index d0f2648c07..120fac31db 100644 --- a/src/storm/modelchecker/propositional/SparsePropositionalModelChecker.cpp +++ b/src/storm/modelchecker/propositional/SparsePropositionalModelChecker.cpp @@ -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/results/ExplicitQualitativeCheckResult.cpp b/src/storm/modelchecker/results/ExplicitQualitativeCheckResult.cpp index 50b9a9e16c..76d86175b0 100644 --- a/src/storm/modelchecker/results/ExplicitQualitativeCheckResult.cpp +++ b/src/storm/modelchecker/results/ExplicitQualitativeCheckResult.cpp @@ -3,6 +3,7 @@ #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" 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/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..90d00ceff8 100644 --- a/src/storm/solver/SolveGoal.cpp +++ b/src/storm/solver/SolveGoal.cpp @@ -147,6 +147,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/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/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..6e7763f048 100644 --- a/src/storm/storage/MaximalEndComponentDecomposition.cpp +++ b/src/storm/storage/MaximalEndComponentDecomposition.cpp @@ -252,6 +252,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..1dacf8d86d 100644 --- a/src/storm/storage/Scheduler.cpp +++ b/src/storm/storage/Scheduler.cpp @@ -391,6 +391,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/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/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/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..7a80f4b17a 100644 --- a/src/storm/utility/ConstantsComparator.cpp +++ b/src/storm/utility/ConstantsComparator.cpp @@ -65,5 +65,6 @@ 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/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..0e32f2187e 100644 --- a/src/storm/utility/constants.cpp +++ b/src/storm/utility/constants.cpp @@ -809,6 +809,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 +1014,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 +1030,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 +1054,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 +1073,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 +1091,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 +1262,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/modelchecker/prctl/mdp/RobustMdpPrctlModelCheckerTest.cpp b/src/test/storm/modelchecker/prctl/mdp/RobustMdpPrctlModelCheckerTest.cpp index d49c63f129..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" @@ -25,6 +27,11 @@ std::unique_ptr getInitialStateFilt return std::make_unique>(model->getInitialStates()); } +std::unique_ptr getInitialStateFilter( + std::shared_ptr> const& model) { + return std::make_unique(model->getInitialStates()); +} + double getQuantitativeResultAtInitialState(std::shared_ptr> const& model, std::unique_ptr& result) { auto filter = getInitialStateFilter(model); @@ -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) { From 73413d788612d59a9282f1be934c1478c9c626ce Mon Sep 17 00:00:00 2001 From: Luko van der Maas Date: Tue, 10 Mar 2026 13:18:35 +0100 Subject: [PATCH 14/51] Add GenerateMonitorVerifier module and update ObservationTraceUnfolder --- .../settings/PomdpSettings.cpp | 2 + .../GenerateMonitorVerifierSettings.cpp | 13 + .../modules/GenerateMonitorVerifierSettings.h | 21 + .../generator/GenerateMonitorVerifier.cpp | 437 ++++++++++++++++++ .../generator/GenerateMonitorVerifier.h | 53 +++ .../NondeterministicBeliefTracker.cpp | 5 +- .../transformer/ObservationTraceUnfolder.cpp | 107 +++-- .../transformer/ObservationTraceUnfolder.h | 20 +- 8 files changed, 620 insertions(+), 38 deletions(-) create mode 100644 src/storm-pomdp-cli/settings/modules/GenerateMonitorVerifierSettings.cpp create mode 100644 src/storm-pomdp-cli/settings/modules/GenerateMonitorVerifierSettings.h create mode 100644 src/storm-pomdp/generator/GenerateMonitorVerifier.cpp create mode 100644 src/storm-pomdp/generator/GenerateMonitorVerifier.h 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/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/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 From 1ba0f32967428348144d00b816b7f20acd21945c Mon Sep 17 00:00:00 2001 From: Luko van der Maas Date: Tue, 10 Mar 2026 13:19:33 +0100 Subject: [PATCH 15/51] Add conditional model checking algs --- .../modelchecker/ModelCheckerEnvironment.cpp | 18 + .../modelchecker/ModelCheckerEnvironment.h | 10 + .../ConditionalAlgorithmSetting.cpp | 8 + .../conditional/ConditionalAlgorithmSetting.h | 10 +- .../helper/conditional/ConditionalHelper.cpp | 1030 ++++++++++++++--- .../helper/conditional/ConditionalHelper.h | 3 + .../prctl/helper/SparseMdpPrctlHelper.cpp | 219 +++- .../prctl/helper/SparseMdpPrctlHelper.h | 6 + .../modules/MinMaxEquationSolverSettings.cpp | 2 +- .../settings/modules/ModelCheckerSettings.cpp | 18 +- .../settings/modules/ModelCheckerSettings.h | 7 + src/storm/solver/SolveGoal.cpp | 5 + src/storm/solver/SolveGoal.h | 2 + src/storm/storage/BitVector.cpp | 4 + .../MaximalEndComponentDecomposition.cpp | 2 + src/storm/storage/Scheduler.cpp | 20 + src/storm/storage/Scheduler.h | 12 + src/storm/utility/RationalApproximation.cpp | 156 +++ src/storm/utility/RationalApproximation.h | 18 + src/storm/utility/constants.cpp | 5 + .../ConditionalMdpPrctlModelCheckerTest.cpp | 70 +- .../utility/RationalApproximationTest.cpp | 70 ++ 22 files changed, 1482 insertions(+), 213 deletions(-) create mode 100644 src/storm/utility/RationalApproximation.cpp create mode 100644 src/storm/utility/RationalApproximation.h create mode 100644 src/test/storm/utility/RationalApproximationTest.cpp 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/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/prctl/helper/SparseMdpPrctlHelper.cpp b/src/storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.cpp index 1afc0a3a39..60a4d0a3b4 100644 --- a/src/storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.cpp +++ b/src/storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.cpp @@ -778,7 +778,7 @@ MDPSparseModelCheckingHelperReturnType SparseMdpPrctlHelper>(*scheduler, resultForMaybeStates.getScheduler(), - qualitativeStateSets.maybeStates); + qualitativeStateSets.maybeStates); } } } @@ -1064,28 +1064,28 @@ std::vector SparseMdpPrctlHelper::compute 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; -} + 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 @@ -1530,6 +1530,179 @@ MDPSparseModelCheckingHelperReturnType SparseMdpPrctlHelper +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, diff --git a/src/storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.h b/src/storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.h index cbcc545b4d..73422caca1 100644 --- a/src/storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.h +++ b/src/storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.h @@ -104,6 +104,12 @@ class SparseMdpPrctlHelper { 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/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/SolveGoal.cpp b/src/storm/solver/SolveGoal.cpp index 90d00ceff8..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(); 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/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/MaximalEndComponentDecomposition.cpp b/src/storm/storage/MaximalEndComponentDecomposition.cpp index 6e7763f048..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(); diff --git a/src/storm/storage/Scheduler.cpp b/src/storm/storage/Scheduler.cpp index 1dacf8d86d..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 { 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/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/constants.cpp b/src/storm/utility/constants.cpp index 0e32f2187e..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(); } 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/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 From 1e81ea3d63877b1411d9ae2f87234bdc3e7d8447 Mon Sep 17 00:00:00 2001 From: Luko van der Maas Date: Wed, 11 Mar 2026 13:42:49 +0100 Subject: [PATCH 16/51] Move conditional code for later commit. --- src/storm/modelchecker/prctl/SparseMdpPrctlModelChecker.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/storm/modelchecker/prctl/SparseMdpPrctlModelChecker.cpp b/src/storm/modelchecker/prctl/SparseMdpPrctlModelChecker.cpp index 55c4102e46..a562790211 100644 --- a/src/storm/modelchecker/prctl/SparseMdpPrctlModelChecker.cpp +++ b/src/storm/modelchecker/prctl/SparseMdpPrctlModelChecker.cpp @@ -282,9 +282,9 @@ std::unique_ptr SparseMdpPrctlModelChecker::com 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), checkTask, this->getModel().getTransitionMatrix(), - this->getModel().getBackwardTransitions(), leftResult.getTruthValuesVector(), rightResult.getTruthValuesVector()); + return storm::modelchecker::computeConditionalProbabilities(env, storm::solver::SolveGoal(this->getModel(), checkTask), + this->getModel().getTransitionMatrix(), this->getModel().getBackwardTransitions(), + leftResult.getTruthValuesVector(), rightResult.getTruthValuesVector()); } } From 68a67f0cbdb718775f820053c61b2eb138ed5d3f Mon Sep 17 00:00:00 2001 From: Luko van der Maas Date: Wed, 11 Mar 2026 13:47:42 +0100 Subject: [PATCH 17/51] format --- .../prctl/helper/SparseMdpPrctlHelper.cpp | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/src/storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.cpp b/src/storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.cpp index 1afc0a3a39..cb205d7cae 100644 --- a/src/storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.cpp +++ b/src/storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.cpp @@ -778,7 +778,7 @@ MDPSparseModelCheckingHelperReturnType SparseMdpPrctlHelper>(*scheduler, resultForMaybeStates.getScheduler(), - qualitativeStateSets.maybeStates); + qualitativeStateSets.maybeStates); } } } @@ -1064,28 +1064,28 @@ std::vector SparseMdpPrctlHelper::compute 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; -} + 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 From 981c96587b70510b0c294b869b76875c84e2a52a Mon Sep 17 00:00:00 2001 From: Luko van der Maas Date: Wed, 11 Mar 2026 16:55:09 +0100 Subject: [PATCH 18/51] Enhance support for RationalNumber in AddUncertainty transformer and related components and added tests - Updated AddUncertainty transformer to handle RationalNumber types, allowing for exact arithmetic with RationalInterval. - Modified various helper functions and model checkers to accommodate RationalNumber and RationalInterval. - Introduced new tests for RationalNumber scenarios in model checking and uncertainty transformations. - Ensured compatibility with existing models while expanding functionality for uncertain models using RationalNumber. --- src/storm-cli-utilities/model-handling.h | 80 ++----- .../parser/DirectEncodingParser.cpp | 5 + src/storm/builder/ExplicitModelBuilder.cpp | 1 + src/storm/builder/RewardModelBuilder.cpp | 1 + src/storm/generator/Choice.cpp | 1 + src/storm/generator/Distribution.cpp | 2 + src/storm/generator/DistributionEntry.cpp | 3 + src/storm/generator/NextStateGenerator.cpp | 4 + .../generator/PrismNextStateGenerator.cpp | 1 + src/storm/generator/StateBehavior.cpp | 1 + .../modelchecker/AbstractModelChecker.cpp | 1 + .../prctl/SparseDtmcPrctlModelChecker.cpp | 5 +- .../prctl/SparseDtmcPrctlModelChecker.h | 3 +- .../prctl/SparseMdpPrctlModelChecker.cpp | 16 ++ .../prctl/helper/SparseDtmcPrctlHelper.cpp | 1 + .../MultiDimensionalRewardUnfolding.cpp | 2 - .../solver/helper/SchedulerTrackingHelper.cpp | 2 +- .../solver/helper/ValueIterationHelper.cpp | 4 +- .../solver/helper/ValueIterationOperator.cpp | 3 +- src/storm/transformer/AddUncertainty.cpp | 46 ++-- src/storm/transformer/AddUncertainty.h | 16 +- src/storm/utility/constants.cpp | 15 ++ .../dtmc/RobustDtmcPrctlModelCheckerTest.cpp | 209 ++++++++++++++++++ .../mdp/RobustMdpPrctlModelCheckerTest.cpp | 102 ++++++++- .../storm/transformer/AddUncertaintyTest.cpp | 33 +++ 25 files changed, 458 insertions(+), 99 deletions(-) diff --git a/src/storm-cli-utilities/model-handling.h b/src/storm-cli-utilities/model-handling.h index 2394c58bcc..f28dc7faf2 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, FinitePrecisionInterval, ExactInterval }; + enum class ValueType { FinitePrecision, Exact, Parametric }; ValueType buildValueType; // ... during model building ValueType verificationValueType; // ... during model verification @@ -222,17 +222,9 @@ inline ModelProcessingInformation getModelProcessingInformation(SymbolicInput co if (generalSettings.isParametricSet()) { mpi.verificationValueType = ModelProcessingInformation::ValueType::Parametric; } else if (generalSettings.isExactSet()) { - if (generalSettings.isIntervalSet()) { - mpi.verificationValueType = ModelProcessingInformation::ValueType::ExactInterval; - } else { - mpi.verificationValueType = ModelProcessingInformation::ValueType::Exact; - } + mpi.verificationValueType = ModelProcessingInformation::ValueType::Exact; } else { - if (generalSettings.isIntervalSet()) { - mpi.verificationValueType = ModelProcessingInformation::ValueType::FinitePrecisionInterval; - } else { - mpi.verificationValueType = ModelProcessingInformation::ValueType::FinitePrecision; - } + mpi.verificationValueType = ModelProcessingInformation::ValueType::FinitePrecision; } auto originalVerificationValueType = mpi.verificationValueType; @@ -278,12 +270,6 @@ 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; }; @@ -395,10 +381,6 @@ 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."); } @@ -418,10 +400,6 @@ 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."); @@ -613,12 +591,8 @@ 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; @@ -1311,38 +1285,30 @@ inline std::vector> parseInjectedRef template void verifyWithAbstractionRefinementEngine(SymbolicInput const& input, ModelProcessingInformation const& mpi) { - 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); - }); - } + 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) { - 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)); - }); - } + 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 diff --git a/src/storm-parsers/parser/DirectEncodingParser.cpp b/src/storm-parsers/parser/DirectEncodingParser.cpp index 8cdc5d0ef6..f8e1badbd0 100644 --- a/src/storm-parsers/parser/DirectEncodingParser.cpp +++ b/src/storm-parsers/parser/DirectEncodingParser.cpp @@ -9,6 +9,7 @@ #include "storm-parsers/parser/ValueParser.h" #include "storm/adapters/IntervalAdapter.h" +#include "storm/adapters/IntervalForward.h" #include "storm/adapters/RationalFunctionAdapter.h" #include "storm/adapters/RationalNumberAdapter.h" #include "storm/exceptions/AbortException.h" @@ -87,6 +88,8 @@ bool isCompatibleValueType(DirectEncodingValueType fileValueType) { return fileValueType == Double || fileValueType == Rational; } else if constexpr (std::is_same_v) { return fileValueType == Double || fileValueType == Rational || fileValueType == DoubleInterval || fileValueType == RationalInterval; + } else if constexpr (std::is_same_v) { + return fileValueType == Double || fileValueType == Rational || fileValueType == DoubleInterval || fileValueType == RationalInterval; } else if constexpr (std::is_same_v) { return fileValueType == Double || fileValueType == Rational || fileValueType == Parametric; } else { @@ -581,6 +584,8 @@ template std::shared_ptr> pa std::filesystem::path const& file, DirectEncodingParserOptions const& options); template std::shared_ptr> parseDirectEncodingModel(std::filesystem::path const& file, DirectEncodingParserOptions const& options); +template std::shared_ptr> parseDirectEncodingModel( + std::filesystem::path const& file, DirectEncodingParserOptions const& options); template std::shared_ptr> parseDirectEncodingModel( std::filesystem::path const& file, DirectEncodingParserOptions const& options); diff --git a/src/storm/builder/ExplicitModelBuilder.cpp b/src/storm/builder/ExplicitModelBuilder.cpp index 4f7a195177..55dbcfdc8a 100644 --- a/src/storm/builder/ExplicitModelBuilder.cpp +++ b/src/storm/builder/ExplicitModelBuilder.cpp @@ -431,6 +431,7 @@ template class ExplicitModelBuilder, uint32_t>; template class ExplicitModelBuilder, uint32_t>; // TODO: where is this used? template class ExplicitModelBuilder, uint32_t>; +template class ExplicitModelBuilder, uint32_t>; } // namespace builder } // namespace storm diff --git a/src/storm/builder/RewardModelBuilder.cpp b/src/storm/builder/RewardModelBuilder.cpp index 6b01224047..d6681c99ad 100644 --- a/src/storm/builder/RewardModelBuilder.cpp +++ b/src/storm/builder/RewardModelBuilder.cpp @@ -66,6 +66,7 @@ template class RewardModelBuilder; template class RewardModelBuilder; template class RewardModelBuilder; template class RewardModelBuilder; +template class RewardModelBuilder; } // namespace builder } // namespace storm diff --git a/src/storm/generator/Choice.cpp b/src/storm/generator/Choice.cpp index 6350cb0d6d..a082330868 100644 --- a/src/storm/generator/Choice.cpp +++ b/src/storm/generator/Choice.cpp @@ -205,5 +205,6 @@ template struct Choice; template struct Choice; template struct Choice; template struct Choice; +template struct Choice; } // namespace generator } // namespace storm diff --git a/src/storm/generator/Distribution.cpp b/src/storm/generator/Distribution.cpp index 32969b24a3..8ef23fc3fb 100644 --- a/src/storm/generator/Distribution.cpp +++ b/src/storm/generator/Distribution.cpp @@ -129,10 +129,12 @@ template class Distribution; template class Distribution; template class Distribution; template class Distribution; +template class Distribution; template class Distribution; template class Distribution; template class Distribution; template class Distribution; +template class Distribution; } // namespace storm::generator diff --git a/src/storm/generator/DistributionEntry.cpp b/src/storm/generator/DistributionEntry.cpp index 1545afdc81..9c48bc39d5 100644 --- a/src/storm/generator/DistributionEntry.cpp +++ b/src/storm/generator/DistributionEntry.cpp @@ -1,4 +1,5 @@ #include "storm/generator/DistributionEntry.h" +#include #include "storm/adapters/IntervalAdapter.h" #include "storm/adapters/RationalFunctionAdapter.h" @@ -41,10 +42,12 @@ template class DistributionEntry; template class DistributionEntry; template class DistributionEntry; template class DistributionEntry; +template class DistributionEntry; template class DistributionEntry; template class DistributionEntry; template class DistributionEntry; template class DistributionEntry; +template class DistributionEntry; } // namespace storm::generator diff --git a/src/storm/generator/NextStateGenerator.cpp b/src/storm/generator/NextStateGenerator.cpp index 4bb73c6394..aa3d1cf334 100644 --- a/src/storm/generator/NextStateGenerator.cpp +++ b/src/storm/generator/NextStateGenerator.cpp @@ -363,5 +363,9 @@ template class NextStateGenerator; template class ActionMask; template class StateValuationFunctionMask; template class NextStateGenerator; + +template class ActionMask; +template class StateValuationFunctionMask; +template class NextStateGenerator; } // namespace generator } // namespace storm diff --git a/src/storm/generator/PrismNextStateGenerator.cpp b/src/storm/generator/PrismNextStateGenerator.cpp index f9a7cd251a..e020c7d6f7 100644 --- a/src/storm/generator/PrismNextStateGenerator.cpp +++ b/src/storm/generator/PrismNextStateGenerator.cpp @@ -1100,5 +1100,6 @@ template class PrismNextStateGenerator; template class PrismNextStateGenerator; template class PrismNextStateGenerator; template class PrismNextStateGenerator; +template class PrismNextStateGenerator; } // namespace generator } // namespace storm diff --git a/src/storm/generator/StateBehavior.cpp b/src/storm/generator/StateBehavior.cpp index f09edce0e6..533a1d4a85 100644 --- a/src/storm/generator/StateBehavior.cpp +++ b/src/storm/generator/StateBehavior.cpp @@ -76,5 +76,6 @@ template class StateBehavior; template class StateBehavior; template class StateBehavior; template class StateBehavior; +template class StateBehavior; } // namespace generator } // namespace storm diff --git a/src/storm/modelchecker/AbstractModelChecker.cpp b/src/storm/modelchecker/AbstractModelChecker.cpp index 56664fcf2a..1a02acc28e 100644 --- a/src/storm/modelchecker/AbstractModelChecker.cpp +++ b/src/storm/modelchecker/AbstractModelChecker.cpp @@ -495,6 +495,7 @@ template class AbstractModelChecker> template class AbstractModelChecker>; template class AbstractModelChecker>; +template class AbstractModelChecker>; // DD template class AbstractModelChecker>; diff --git a/src/storm/modelchecker/prctl/SparseDtmcPrctlModelChecker.cpp b/src/storm/modelchecker/prctl/SparseDtmcPrctlModelChecker.cpp index e54a69b4ef..e5ee8b3c78 100644 --- a/src/storm/modelchecker/prctl/SparseDtmcPrctlModelChecker.cpp +++ b/src/storm/modelchecker/prctl/SparseDtmcPrctlModelChecker.cpp @@ -141,7 +141,7 @@ template std::unique_ptr SparseDtmcPrctlModelChecker::computeUntilProbabilities( Environment const& env, CheckTask const& checkTask) { storm::logic::UntilFormula const& pathFormula = checkTask.getFormula(); - if (storm::IsIntervalType) { + if constexpr (storm::IsIntervalType) { STORM_LOG_THROW(checkTask.isUncertaintyResolutionModeSet(), storm::exceptions::InvalidSettingsException, "Uncertainty resolution mode must be set for uncertain (interval) models."); STORM_LOG_THROW(checkTask.getUncertaintyResolutionMode() != UncertaintyResolutionMode::Robust && @@ -297,7 +297,7 @@ template std::unique_ptr SparseDtmcPrctlModelChecker::computeReachabilityRewards( Environment const& env, CheckTask const& checkTask) { storm::logic::EventuallyFormula const& eventuallyFormula = checkTask.getFormula(); - if (storm::IsIntervalType) { + if constexpr (storm::IsIntervalType) { STORM_LOG_THROW(checkTask.isUncertaintyResolutionModeSet(), storm::exceptions::InvalidSettingsException, "Uncertainty resolution mode must be set for uncertain (interval) models."); STORM_LOG_THROW(checkTask.getUncertaintyResolutionMode() != UncertaintyResolutionMode::Robust && @@ -537,5 +537,6 @@ template class SparseDtmcPrctlModelChecker>; template class SparseDtmcPrctlModelChecker>; template class SparseDtmcPrctlModelChecker>; template class SparseDtmcPrctlModelChecker>; +template class SparseDtmcPrctlModelChecker>; } // namespace modelchecker } // namespace storm diff --git a/src/storm/modelchecker/prctl/SparseDtmcPrctlModelChecker.h b/src/storm/modelchecker/prctl/SparseDtmcPrctlModelChecker.h index 6372205cdc..551b6a48cc 100644 --- a/src/storm/modelchecker/prctl/SparseDtmcPrctlModelChecker.h +++ b/src/storm/modelchecker/prctl/SparseDtmcPrctlModelChecker.h @@ -1,5 +1,6 @@ #pragma once +#include "storm/adapters/IntervalForward.h" #include "storm/modelchecker/propositional/SparsePropositionalModelChecker.h" #include "storm/models/sparse/Dtmc.h" @@ -11,7 +12,7 @@ class SparseDtmcPrctlModelChecker : public SparsePropositionalModelChecker, double, ValueType>::type; + using SolutionType = storm::IntervalBaseType; explicit SparseDtmcPrctlModelChecker(SparseDtmcModelType const& model); diff --git a/src/storm/modelchecker/prctl/SparseMdpPrctlModelChecker.cpp b/src/storm/modelchecker/prctl/SparseMdpPrctlModelChecker.cpp index a562790211..a17ca029dc 100644 --- a/src/storm/modelchecker/prctl/SparseMdpPrctlModelChecker.cpp +++ b/src/storm/modelchecker/prctl/SparseMdpPrctlModelChecker.cpp @@ -1,6 +1,7 @@ #include "storm/modelchecker/prctl/SparseMdpPrctlModelChecker.h" #include "storm/adapters/IntervalAdapter.h" +#include "storm/adapters/IntervalForward.h" #include "storm/adapters/RationalFunctionAdapter.h" #include "storm/adapters/RationalNumberAdapter.h" #include "storm/exceptions/InvalidPropertyException.h" @@ -291,6 +292,9 @@ std::unique_ptr SparseMdpPrctlModelChecker::com template std::unique_ptr SparseMdpPrctlModelChecker::computeCumulativeRewards( Environment const& env, CheckTask const& checkTask) { + if constexpr (storm::IsIntervalType) { + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Cumulative reward properties are not implemented for interval models."); + } storm::logic::CumulativeRewardFormula const& rewardPathFormula = 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."); @@ -325,6 +329,12 @@ std::unique_ptr SparseMdpPrctlModelChecker +std::unique_ptr SparseMdpPrctlModelChecker>::computeDiscountedCumulativeRewards( + Environment const& env, CheckTask const& checkTask) { + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Discounted properties are not implemented for interval models."); +} + template std::unique_ptr SparseMdpPrctlModelChecker::computeDiscountedCumulativeRewards( Environment const& env, CheckTask const& checkTask) { @@ -414,6 +424,12 @@ std::unique_ptr SparseMdpPrctlModelChecker +std::unique_ptr SparseMdpPrctlModelChecker>::computeDiscountedTotalRewards( + Environment const& env, CheckTask const& checkTask) { + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Discounted properties are not implemented for interval models."); +} + template std::unique_ptr SparseMdpPrctlModelChecker::computeDiscountedTotalRewards( Environment const& env, CheckTask const& checkTask) { diff --git a/src/storm/modelchecker/prctl/helper/SparseDtmcPrctlHelper.cpp b/src/storm/modelchecker/prctl/helper/SparseDtmcPrctlHelper.cpp index a8c6153e06..ba0bd28c3a 100644 --- a/src/storm/modelchecker/prctl/helper/SparseDtmcPrctlHelper.cpp +++ b/src/storm/modelchecker/prctl/helper/SparseDtmcPrctlHelper.cpp @@ -939,6 +939,7 @@ template class SparseDtmcPrctlHelper; template class SparseDtmcPrctlHelper; template class SparseDtmcPrctlHelper; template class SparseDtmcPrctlHelper, double>; +template class SparseDtmcPrctlHelper, storm::RationalNumber>; } // namespace helper } // namespace modelchecker } // namespace storm diff --git a/src/storm/modelchecker/prctl/helper/rewardbounded/MultiDimensionalRewardUnfolding.cpp b/src/storm/modelchecker/prctl/helper/rewardbounded/MultiDimensionalRewardUnfolding.cpp index 552122bbc2..6f3533e6a8 100644 --- a/src/storm/modelchecker/prctl/helper/rewardbounded/MultiDimensionalRewardUnfolding.cpp +++ b/src/storm/modelchecker/prctl/helper/rewardbounded/MultiDimensionalRewardUnfolding.cpp @@ -964,8 +964,6 @@ 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/solver/helper/SchedulerTrackingHelper.cpp b/src/storm/solver/helper/SchedulerTrackingHelper.cpp index 0a296626c1..478866794e 100644 --- a/src/storm/solver/helper/SchedulerTrackingHelper.cpp +++ b/src/storm/solver/helper/SchedulerTrackingHelper.cpp @@ -108,7 +108,7 @@ bool SchedulerTrackingHelper::compu std::vector& schedulerStorage, UncertaintyResolutionMode uncertaintyResolutionMode, std::vector* operandOut, boost::optional> const& robustIndices) const { bool robustUncertainty = false; - if (storm::IsIntervalType) { + if constexpr (storm::IsIntervalType) { robustUncertainty = isUncertaintyResolvedRobust(uncertaintyResolutionMode, dir); } diff --git a/src/storm/solver/helper/ValueIterationHelper.cpp b/src/storm/solver/helper/ValueIterationHelper.cpp index 72e6918851..512a25b3db 100644 --- a/src/storm/solver/helper/ValueIterationHelper.cpp +++ b/src/storm/solver/helper/ValueIterationHelper.cpp @@ -108,7 +108,7 @@ SolverStatus ValueIterationHelper:: MultiplicationStyle mult, UncertaintyResolutionMode const& uncertaintyResolutionMode) const { bool robustUncertainty = false; - if (storm::IsIntervalType) { + if constexpr (storm::IsIntervalType) { robustUncertainty = isUncertaintyResolvedRobust(uncertaintyResolutionMode, Dir); } @@ -126,7 +126,7 @@ SolverStatus ValueIterationHelper:: std::function const& iterationCallback, MultiplicationStyle mult, UncertaintyResolutionMode const& uncertaintyResolutionMode) const { - if (storm::IsIntervalType) { + if constexpr (storm::IsIntervalType) { STORM_LOG_THROW(uncertaintyResolutionMode != UncertaintyResolutionMode::Unset, storm::exceptions::IllegalFunctionCallException, "Uncertainty resolution mode must be set for uncertain (interval) models."); STORM_LOG_THROW(dir.has_value() || (uncertaintyResolutionMode != UncertaintyResolutionMode::Robust && diff --git a/src/storm/solver/helper/ValueIterationOperator.cpp b/src/storm/solver/helper/ValueIterationOperator.cpp index 44dbc87aa0..4f352705e3 100644 --- a/src/storm/solver/helper/ValueIterationOperator.cpp +++ b/src/storm/solver/helper/ValueIterationOperator.cpp @@ -3,6 +3,7 @@ #include #include "storm/adapters/IntervalAdapter.h" +#include "storm/adapters/IntervalForward.h" #include "storm/adapters/RationalNumberAdapter.h" #include "storm/storage/BitVector.h" #include "storm/storage/SparseMatrix.h" @@ -33,7 +34,7 @@ void ValueIterationOperator::setMat matrixColumns.reserve(matrix.getNonzeroEntryCount() + numRows + 1); // matrixColumns also contain indications for when a row(group) starts // hasOnlyConstants is only used for Interval matrices, currently only populated for iMCs - if constexpr (std::is_same::value) { + if constexpr (storm::IsIntervalType) { applyCache.hasOnlyConstants.clear(); applyCache.hasOnlyConstants.grow(matrix.getRowCount()); } diff --git a/src/storm/transformer/AddUncertainty.cpp b/src/storm/transformer/AddUncertainty.cpp index a571208c97..f8102f9bc5 100644 --- a/src/storm/transformer/AddUncertainty.cpp +++ b/src/storm/transformer/AddUncertainty.cpp @@ -8,6 +8,7 @@ #include "storm/models/sparse/Mdp.h" #include "storm/models/sparse/StandardRewardModel.h" #include "storm/storage/SparseMatrix.h" +#include "storm/utility/constants.h" #include "storm/utility/macros.h" #include "storm/utility/vector.h" @@ -17,11 +18,11 @@ template AddUncertainty::AddUncertainty(std::shared_ptr> const& originalModel) : origModel(originalModel) {} template -std::shared_ptr> AddUncertainty::transform(double additiveUncertainty, double minimalTransitionProbability, - uint64_t maxSuccessors) { +std::shared_ptr::IntervalType>> 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(), + 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) { @@ -35,24 +36,24 @@ std::shared_ptr> AddUncertainty modelComponents(newMatrixBuilder.build(), origModel->getStateLabeling()); + storm::storage::sparse::ModelComponents modelComponents(newMatrixBuilder.build(), origModel->getStateLabeling()); if (!origModel->getTransitionMatrix().hasTrivialRowGrouping()) { modelComponents.transitionMatrix.setRowGroupIndices(origModel->getTransitionMatrix().getRowGroupIndices()); } // Change value type of standard reward model. - std::unordered_map> newRewardModels; + std::unordered_map> newRewardModels; for (auto const& rewModel : origModel->getRewardModels()) { auto const& oldRewModel = rewModel.second; - std::optional> stateRewards; - std::optional> stateActionRewards; + std::optional> stateRewards; + std::optional> stateActionRewards; if (oldRewModel.hasStateRewards()) { - stateRewards = utility::vector::convertNumericVector(oldRewModel.getStateRewardVector()); + stateRewards = utility::vector::convertNumericVector(oldRewModel.getStateRewardVector()); } if (oldRewModel.hasStateActionRewards()) { - stateActionRewards = utility::vector::convertNumericVector(oldRewModel.getStateActionRewardVector()); + stateActionRewards = utility::vector::convertNumericVector(oldRewModel.getStateActionRewardVector()); } STORM_LOG_THROW(!oldRewModel.hasTransitionRewards(), exceptions::NotImplementedException, "Transition rewards are not supported."); - models::sparse::StandardRewardModel newRewModel(std::move(stateRewards), std::move(stateActionRewards)); + models::sparse::StandardRewardModel newRewModel(std::move(stateRewards), std::move(stateActionRewards)); newRewardModels.emplace(rewModel.first, std::move(newRewModel)); } @@ -64,26 +65,29 @@ std::shared_ptr> AddUncertaintygetType()) { case storm::models::ModelType::Dtmc: - return std::make_shared>(std::move(modelComponents)); + return std::make_shared>(std::move(modelComponents)); case storm::models::ModelType::Mdp: - return std::make_shared>(std::move(modelComponents)); + return std::make_shared>(std::move(modelComponents)); default: STORM_LOG_THROW(false, exceptions::NotImplementedException, "Only DTMC and MDP model types are currently supported."); } } template -storm::Interval AddUncertainty::addUncertainty(ValueType const& vt, double additiveUncertainty, double minimalValue) { +typename AddUncertainty::IntervalType AddUncertainty::addUncertainty(ValueType const& vt, double additiveUncertainty, + double minimalValue) { if (utility::isOne(vt)) { - return storm::Interval(1.0, 1.0); + return IntervalType(storm::utility::one(), storm::utility::one()); } - double center = storm::utility::convertNumber(vt); - STORM_LOG_THROW(center >= minimalValue, storm::exceptions::InvalidArgumentException, "Transition probability is smaller than minimal value"); - double lowerBound = std::max(center - additiveUncertainty, minimalValue); - double upperBound = std::min(center + additiveUncertainty, 1.0 - minimalValue); - STORM_LOG_ASSERT(lowerBound > 0, "Lower bound must be strictly above zero."); - STORM_LOG_ASSERT(upperBound < 1, "Upper bound must be strictly below one."); - return storm::Interval(lowerBound, upperBound); + ValueType const center = vt; + ValueType const uncertainty = storm::utility::convertNumber(additiveUncertainty); + ValueType const minVal = storm::utility::convertNumber(minimalValue); + STORM_LOG_THROW(center >= minVal, storm::exceptions::InvalidArgumentException, "Transition probability is smaller than minimal value"); + ValueType const lowerBound = storm::utility::max(center - uncertainty, minVal); + ValueType const upperBound = storm::utility::min(center + uncertainty, storm::utility::one() - minVal); + STORM_LOG_ASSERT(storm::utility::isPositive(lowerBound), "Lower bound must be strictly above zero."); + STORM_LOG_ASSERT(upperBound < storm::utility::one(), "Upper bound must be strictly below one."); + return IntervalType(lowerBound, upperBound); } template class AddUncertainty; diff --git a/src/storm/transformer/AddUncertainty.h b/src/storm/transformer/AddUncertainty.h index 7e0ae97986..d3421dc7a2 100644 --- a/src/storm/transformer/AddUncertainty.h +++ b/src/storm/transformer/AddUncertainty.h @@ -1,6 +1,9 @@ #pragma once +#include + #include "storm/adapters/IntervalForward.h" +#include "storm/adapters/RationalNumberForward.h" #include "storm/models/sparse/Model.h" namespace storm::transformer { @@ -10,17 +13,22 @@ namespace storm::transformer { * We currently support only one type of self-defined uncertainty, although additional types of uncertainty are imaginable. * The transformer does maintain reward models, state labels, state valuations, choice labels and choice origins. * - * @tparam ValueType + * When ValueType is storm::RationalNumber the output model uses storm::RationalInterval (exact arithmetic). + * For all other ValueTypes (e.g. double) the output uses storm::Interval (double-precision). + * + * @tparam ValueType The value type of the input model. */ template class AddUncertainty { public: + using IntervalType = std::conditional_t, storm::RationalInterval, storm::Interval>; + AddUncertainty(std::shared_ptr> const& originalModel); - std::shared_ptr> transform(double additiveUncertainty, double minimalValue = 0.0001, - uint64_t maxSuccessors = 10000000); + 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); + IntervalType addUncertainty(ValueType const& vt, double additiveUncertainty, double minimalValue); std::shared_ptr> origModel; }; diff --git a/src/storm/utility/constants.cpp b/src/storm/utility/constants.cpp index 0e32f2187e..ee928fb062 100644 --- a/src/storm/utility/constants.cpp +++ b/src/storm/utility/constants.cpp @@ -1019,6 +1019,11 @@ storm::RationalInterval convertNumber(double const& number) { return storm::RationalInterval(convertNumber(number)); } +template<> +storm::RationalInterval convertNumber(uint64_t const& number) { + return storm::RationalInterval(convertNumber(number)); +} + #if defined(STORM_HAVE_GMP) template<> storm::Interval convertNumber(storm::GmpRationalNumber const& n) { @@ -1091,6 +1096,13 @@ bool isApproxEqual(storm::Interval const& a, storm::Interval const& b, storm::In isApproxEqual(a.upper(), b.upper(), precision.center(), relative); } +template<> +bool isApproxEqual(storm::RationalInterval const& a, storm::RationalInterval const& b, storm::RationalInterval const& precision, bool relative) { + STORM_LOG_ASSERT(precision.isPointInterval(), "Precision must be a point interval"); + return isApproxEqual(a.lower(), b.lower(), precision.center(), relative) && + isApproxEqual(a.upper(), b.upper(), precision.center(), relative); +} + template<> storm::RationalInterval abs(storm::RationalInterval const& interval) { return interval.abs(); @@ -1269,6 +1281,9 @@ 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 bool isNonNegative(RationalInterval const& value); +template bool isPositive(RationalInterval const& value); +template bool isBetween(RationalInterval const&, RationalInterval const&, RationalInterval const& value, bool); template std::string to_string(storm::RationalInterval const& value); } // namespace utility diff --git a/src/test/storm/modelchecker/prctl/dtmc/RobustDtmcPrctlModelCheckerTest.cpp b/src/test/storm/modelchecker/prctl/dtmc/RobustDtmcPrctlModelCheckerTest.cpp index 2cb9f531a3..f08c7c3fb8 100644 --- a/src/test/storm/modelchecker/prctl/dtmc/RobustDtmcPrctlModelCheckerTest.cpp +++ b/src/test/storm/modelchecker/prctl/dtmc/RobustDtmcPrctlModelCheckerTest.cpp @@ -1,5 +1,6 @@ #include "storm-config.h" #include "storm/adapters/IntervalForward.h" +#include "storm/adapters/RationalNumberForward.h" #include "test/storm_gtest.h" #include "storm-parsers/api/model_descriptions.h" @@ -14,6 +15,7 @@ #include "storm/modelchecker/results/ExplicitQualitativeCheckResult.h" #include "storm/modelchecker/results/ExplicitQuantitativeCheckResult.h" #include "storm/transformer/AddUncertainty.h" +#include "storm/utility/constants.h" std::unique_ptr getInitialStateFilter( std::shared_ptr> const& model) { @@ -153,6 +155,133 @@ void checkModelForQualitativeResult(std::string const& path, std::string const& } } +std::unique_ptr getInitialStateFilter( + std::shared_ptr> const& model) { + return std::make_unique>(model->getInitialStates()); +} + +storm::RationalNumber getQuantitativeResultAtInitialState(std::shared_ptr> const& model, + std::unique_ptr& result) { + auto filter = getInitialStateFilter(model); + result->filter(*filter); + return result->asQuantitativeCheckResult().getMin(); +} + +std::unique_ptr getInitialStateFilter( + std::shared_ptr> const& model) { + return std::make_unique>(model->getInitialStates()); +} + +storm::RationalNumber getQuantitativeResultAtInitialState(std::shared_ptr> const& model, + std::unique_ptr& result) { + auto filter = getInitialStateFilter(model); + result->filter(*filter); + return result->asQuantitativeCheckResult().getMin(); +} + +void expectThrowRational(std::string const& path, std::string const& formulaString, + std::optional uncertaintyResolutionMode = std::nullopt) { + std::shared_ptr> modelPtr = storm::parser::parseDirectEncodingModel(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> dtmc = modelPtr->as>(); + ASSERT_EQ(storm::models::ModelType::Dtmc, modelPtr->getType()); + auto task = storm::modelchecker::CheckTask(*formulas[0]); + if (uncertaintyResolutionMode.has_value()) { + task.setUncertaintyResolutionMode(uncertaintyResolutionMode.value()); + } + + auto checker = storm::modelchecker::SparseDtmcPrctlModelChecker>(*dtmc); + STORM_SILENT_EXPECT_THROW(checker.check(env, task), storm::exceptions::BaseException); +} + +void checkExplicitModelForQuantitativeResultRational(std::string const& path, std::string const& formulaString, storm::RationalNumber min, + storm::RationalNumber max) { + std::shared_ptr> modelPtr = storm::parser::parseDirectEncodingModel(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> dtmc = modelPtr->as>(); + ASSERT_EQ(storm::models::ModelType::Dtmc, modelPtr->getType()); + auto taskMax = storm::modelchecker::CheckTask(*formulas[0]); + taskMax.setUncertaintyResolutionMode(storm::UncertaintyResolutionMode::Maximize); + + auto checker = storm::modelchecker::SparseDtmcPrctlModelChecker>(*dtmc); + auto resultMax = checker.check(env, taskMax); + EXPECT_EQ(max, getQuantitativeResultAtInitialState(dtmc, resultMax)); + + auto taskMin = storm::modelchecker::CheckTask(*formulas[1]); + taskMin.setUncertaintyResolutionMode(storm::UncertaintyResolutionMode::Minimize); + + auto resultMin = checker.check(env, taskMin); + EXPECT_EQ(min, getQuantitativeResultAtInitialState(dtmc, resultMin)); +} + +void checkPrismModelForQuantitativeResultRational(std::string const& path, std::string const& formulaString, storm::RationalNumber min, + storm::RationalNumber max) { + storm::prism::Program program = storm::api::parseProgram(path); + program = storm::utility::prism::preprocess(program, ""); + + std::vector> formulas = storm::api::extractFormulasFromProperties(storm::api::parseProperties(formulaString)); + std::shared_ptr> modelPtr = storm::api::buildSparseModel(program, formulas); + + storm::Environment env; + env.solver().minMax().setMethod(storm::solver::MinMaxMethod::ValueIteration); + + std::shared_ptr> dtmc = modelPtr->as>(); + ASSERT_EQ(storm::models::ModelType::Dtmc, modelPtr->getType()); + auto taskMax = storm::modelchecker::CheckTask(*formulas[0]); + taskMax.setUncertaintyResolutionMode(storm::UncertaintyResolutionMode::Maximize); + + auto checker = storm::modelchecker::SparseDtmcPrctlModelChecker>(*dtmc); + auto resultMax = checker.check(env, taskMax); + EXPECT_EQ(max, getQuantitativeResultAtInitialState(dtmc, resultMax)); + + auto taskMin = storm::modelchecker::CheckTask(*formulas[1]); + taskMin.setUncertaintyResolutionMode(storm::UncertaintyResolutionMode::Minimize); + + auto resultMin = checker.check(env, taskMin); + EXPECT_EQ(min, getQuantitativeResultAtInitialState(dtmc, resultMin)); +} + +void makeUncertainAndCheckRational(std::string const& path, std::string const& formulaString, double amountOfUncertainty) { + storm::prism::Program program = storm::api::parseProgram(path); + program = storm::utility::prism::preprocess(program, ""); + std::vector> formulas = + storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaString, program)); + std::shared_ptr> modelPtr = storm::api::buildSparseModel(program, formulas); + auto dtmc = modelPtr->as>(); + + storm::Environment env; + auto taskCertain = storm::modelchecker::CheckTask(*formulas[0]); + auto checker = storm::modelchecker::SparseDtmcPrctlModelChecker>(*dtmc); + auto exresult = checker.check(env, taskCertain); + storm::RationalNumber certainValue = getQuantitativeResultAtInitialState(modelPtr, exresult); + + storm::Environment envIntervals; + envIntervals.solver().minMax().setMethod(storm::solver::MinMaxMethod::ValueIteration); + auto transformer = storm::transformer::AddUncertainty(modelPtr); + auto idtmc = transformer.transform(amountOfUncertainty)->as>(); + auto ichecker = storm::modelchecker::SparseDtmcPrctlModelChecker>(*idtmc); + + auto taskMin = storm::modelchecker::CheckTask(*formulas[0]); + taskMin.setUncertaintyResolutionMode(storm::UncertaintyResolutionMode::Minimize); + auto iresultMin = ichecker.check(envIntervals, taskMin); + storm::RationalNumber minValue = getQuantitativeResultAtInitialState(idtmc, iresultMin); + EXPECT_LE(minValue, certainValue); + + auto taskMax = storm::modelchecker::CheckTask(*formulas[0]); + taskMax.setUncertaintyResolutionMode(storm::UncertaintyResolutionMode::Maximize); + auto iresultMax = ichecker.check(envIntervals, taskMax); + storm::RationalNumber maxValue = getQuantitativeResultAtInitialState(idtmc, iresultMax); + EXPECT_LE(certainValue, maxValue); +} + TEST(RobustDtmcModelCheckerTest, Tiny01ReachMaxMinProbs) { // Maximal Reachability probabilities using explicit format. checkExplicitModelForQuantitativeResult(STORM_TEST_RESOURCES_DIR "/idtmc/tiny-01.drn", "P=? [ F \"target\"];P=? [ F \"target\"]", 0.3, 0.5); @@ -228,3 +357,83 @@ TEST(RobustDtmcModelCheckerTest, TinyO2Propositional) { checkModelForQualitativeResult(STORM_TEST_RESOURCES_DIR "/idtmc/tiny-02.drn", "\"target\";!\"target\"", expectedResults); } + +// ---- RationalInterval tests (exact arithmetic) ---- + +TEST(RobustRationalDtmcModelCheckerTest, Tiny01ReachMaxMinProbs) { + checkExplicitModelForQuantitativeResultRational(STORM_TEST_RESOURCES_DIR "/idtmc/tiny-01.drn", "P=? [ F \"target\"];P=? [ F \"target\"]", + storm::RationalNumber(3, 10), storm::RationalNumber(1, 2)); +} + +TEST(RobustRationalDtmcModelCheckerTest, Tiny01MaxReachProbNoUncertaintyResolutionMode) { + expectThrowRational(STORM_TEST_RESOURCES_DIR "/idtmc/tiny-01.drn", "P=? [ F \"target\"];", + std::make_optional(storm::UncertaintyResolutionMode::Unset)); +} + +TEST(RobustRationalDtmcModelCheckerTest, Tiny01MaxReachProbNoOptimizationDirectionButRobust) { + expectThrowRational(STORM_TEST_RESOURCES_DIR "/idtmc/tiny-01.drn", "P=? [ F \"target\"];", + std::make_optional(storm::UncertaintyResolutionMode::Robust)); +} + +TEST(RobustRationalDtmcModelCheckerTest, Tiny02GloballyMaxMinProbs) { + expectThrowRational(STORM_TEST_RESOURCES_DIR "/idtmc/tiny-02.drn", "P=? [ G \"target\"];P=? [ G \"target\"]"); +} + +TEST(RobustRationalDtmcModelCheckerTest, DieIntervalsMaxMin) { +#ifndef STORM_HAVE_Z3 + GTEST_SKIP() << "Z3 not available."; +#endif + checkPrismModelForQuantitativeResultRational(STORM_TEST_RESOURCES_DIR "/idtmc/die-intervals.pm", "P=? [ F \"one\"];P=? [ F \"one\"]", + storm::RationalNumber(4483008223, 94143178827), storm::RationalNumber(35864065784, 94143178827)); +} + +TEST(RobustRationalDtmcModelCheckerTest, BrpIntervalsMaxMin) { +#ifndef STORM_HAVE_Z3 + GTEST_SKIP() << "Z3 not available."; +#endif + checkPrismModelForQuantitativeResultRational( + STORM_TEST_RESOURCES_DIR "/idtmc/brp-32-2-intervals.pm", "P=? [ F \"error\" ];P=? [ F \"error\" ]", + // The number is to large to be represented as a literal, so we construct it from strings. + storm::RationalNumber("10238464074071514998168131748974289312176706851339248682605279417683104172439385084862067764341447739354976890683145110072150529" + "29484271217452553595194641992099205093706027267658909966207049695357884534755950707204946503657943965652247014748702342577938440" + "579441829932769922685028147340905022682454110960938051" + "/" + "40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000"), + storm::RationalNumber("73421101667166357614487085157865792442422423664718103404519649530149236548299533815461648662459394997073289369183682645650367121" + "4626220291672876565353557862092334897" + "/" + "86736173798840354720596224069595336914062500000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000")); +} + +TEST(RobustRationalDtmcModelCheckerTest, DieIntervalsMaxMinRewards) { +#ifndef STORM_HAVE_Z3 + GTEST_SKIP() << "Z3 not available."; +#endif + checkPrismModelForQuantitativeResultRational(STORM_TEST_RESOURCES_DIR "/idtmc/die-intervals.pm", "R=? [ F \"done\"];R=? [ F \"done\"]", + storm::RationalNumber(15544649, 4782969), storm::RationalNumber(76715008330675523, 16677181699666569)); +} + +TEST(RobustRationalDtmcModelCheckerTest, Tiny03MaxMinRewards) { + checkExplicitModelForQuantitativeResultRational(STORM_TEST_RESOURCES_DIR "/idtmc/tiny-03.drn", "R=? [ F \"target\"];R=? [ F \"target\"]", + storm::RationalNumber(13, 2), storm::RationalNumber(17, 2)); +} + +TEST(RobustRationalDtmcModelCheckerTest, Tiny03RewardsNoUncertaintyResolutionMode) { + expectThrowRational(STORM_TEST_RESOURCES_DIR "/idtmc/tiny-03.drn", "R=? [ F \"target\"]", storm::UncertaintyResolutionMode::Unset); +} + +TEST(RobustRationalDtmcModelCheckerTest, Tiny04MaxMinRewards) { + checkExplicitModelForQuantitativeResultRational(STORM_TEST_RESOURCES_DIR "/idtmc/tiny-04.drn", "R=? [ F \"target\"];R=? [ F \"target\"]", + storm::utility::infinity(), storm::utility::infinity()); +} + +TEST(RobustRationalDtmcModelCheckerTest, AddUncertaintyBrpMax) { +#ifndef STORM_HAVE_Z3 + GTEST_SKIP() << "Z3 not available."; +#endif + makeUncertainAndCheckRational(STORM_TEST_RESOURCES_DIR "/dtmc/brp-16-2.pm", "P=? [ F \"target\"]", 0.01); + makeUncertainAndCheckRational(STORM_TEST_RESOURCES_DIR "/dtmc/brp-16-2.pm", "P=? [ F \"target\"]", 0.05); +} diff --git a/src/test/storm/modelchecker/prctl/mdp/RobustMdpPrctlModelCheckerTest.cpp b/src/test/storm/modelchecker/prctl/mdp/RobustMdpPrctlModelCheckerTest.cpp index 46e9b8e9b6..c1e3a71cdd 100644 --- a/src/test/storm/modelchecker/prctl/mdp/RobustMdpPrctlModelCheckerTest.cpp +++ b/src/test/storm/modelchecker/prctl/mdp/RobustMdpPrctlModelCheckerTest.cpp @@ -27,9 +27,21 @@ std::unique_ptr getInitialStateFilt return std::make_unique>(model->getInitialStates()); } +std::unique_ptr getInitialStateFilter( + std::shared_ptr> const& model) { + return std::make_unique>(model->getInitialStates()); +} + +storm::RationalNumber getQuantitativeResultAtInitialState(std::shared_ptr> const& model, + std::unique_ptr& result) { + auto filter = getInitialStateFilter(model); + result->filter(*filter); + return result->asQuantitativeCheckResult().getMin(); +} + 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, @@ -140,8 +152,8 @@ void checkPrismModelForQuantitativeResult(std::string const& path, std::string c 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::shared_ptr> modelPtr = storm::parser::parseDirectEncodingModel(path); + std::vector> formulas = storm::api::extractFormulasFromProperties(storm::api::parseProperties(formulaString)); storm::Environment env; env.solver().minMax().setMethod(storm::solver::MinMaxMethod::ValueIteration); @@ -152,18 +164,20 @@ void checkModelRational(std::string const& path, std::string const& formulaStrin taskMax.setProduceSchedulers(produceScheduler); auto checker = storm::modelchecker::SparseMdpPrctlModelChecker>(*mdp); + taskMax.setUncertaintyResolutionMode(storm::UncertaintyResolutionMode::Robust); auto resultMax = checker.check(env, taskMax); EXPECT_EQ(maxmin, getQuantitativeResultAtInitialState(mdp, resultMax)); - taskMax.setRobustUncertainty(false); + taskMax.setUncertaintyResolutionMode(storm::UncertaintyResolutionMode::Cooperative); auto resultMaxNonRobust = checker.check(env, taskMax); EXPECT_EQ(maxmax, getQuantitativeResultAtInitialState(mdp, resultMaxNonRobust)); auto taskMin = storm::modelchecker::CheckTask(*formulas[1]); taskMin.setProduceSchedulers(produceScheduler); + taskMin.setUncertaintyResolutionMode(storm::UncertaintyResolutionMode::Robust); auto resultMin = checker.check(env, taskMin); EXPECT_EQ(minmax, getQuantitativeResultAtInitialState(mdp, resultMin)); - taskMin.setRobustUncertainty(false); + taskMin.setUncertaintyResolutionMode(storm::UncertaintyResolutionMode::Cooperative); auto resultMinNonRobust = checker.check(env, taskMin); EXPECT_EQ(minmin, getQuantitativeResultAtInitialState(mdp, resultMinNonRobust)); } @@ -212,6 +226,42 @@ void makeUncertainAndCheck(std::string const& path, std::string const& formulaSt EXPECT_LE(certainValue, maxValue); } +void makeUncertainAndCheckRational(std::string const& path, std::string const& formulaString, double amountOfUncertainty) { + storm::prism::Program program = storm::api::parseProgram(path); + program = storm::utility::prism::preprocess(program, ""); + std::vector> formulas = + storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaString, program)); + std::shared_ptr> modelPtr = storm::api::buildSparseModel(program, formulas); + auto mdp = modelPtr->as>(); + + ASSERT_TRUE(formulas[0]->isProbabilityOperatorFormula()); + ASSERT_TRUE(formulas[0]->asProbabilityOperatorFormula().getOptimalityType() == storm::solver::OptimizationDirection::Maximize); + + storm::Environment env; + auto taskCertain = storm::modelchecker::CheckTask(*formulas[0]); + auto checker = storm::modelchecker::SparseMdpPrctlModelChecker>(*mdp); + auto exresult = checker.check(env, taskCertain); + storm::RationalNumber certainValue = getQuantitativeResultAtInitialState(modelPtr, exresult); + + storm::Environment envIntervals; + envIntervals.solver().minMax().setMethod(storm::solver::MinMaxMethod::ValueIteration); + auto transformer = storm::transformer::AddUncertainty(modelPtr); + auto imdp = transformer.transform(amountOfUncertainty)->as>(); + auto ichecker = storm::modelchecker::SparseMdpPrctlModelChecker>(*imdp); + + auto taskMin = storm::modelchecker::CheckTask(*formulas[0]); + taskMin.setUncertaintyResolutionMode(storm::UncertaintyResolutionMode::Minimize); + auto iresultMin = ichecker.check(envIntervals, taskMin); + storm::RationalNumber minValue = getQuantitativeResultAtInitialState(imdp, iresultMin); + EXPECT_LE(minValue, certainValue); + + auto taskMax = storm::modelchecker::CheckTask(*formulas[0]); + taskMax.setUncertaintyResolutionMode(storm::UncertaintyResolutionMode::Maximize); + auto iresultMax = ichecker.check(envIntervals, taskMax); + storm::RationalNumber maxValue = getQuantitativeResultAtInitialState(imdp, iresultMax); + EXPECT_LE(certainValue, maxValue); +} + TEST(RobustMDPModelCheckingTest, Tiny01maxmin) { checkModel(STORM_TEST_RESOURCES_DIR "/imdp/tiny-01.drn", "Pmax=? [ F \"target\"];Pmin=? [ F \"target\"]", 0.4, 0.5, 0.5, 0.4, false); } @@ -221,9 +271,9 @@ TEST(RobustMDPModelCheckingTest, Tiny03maxmin) { } TEST(RobustMDPModelCheckingTest, BoundedTiny03maxmin) { - 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); + checkModel(STORM_TEST_RESOURCES_DIR "/imdp/tiny-03.drn", "Pmax=? [ F<=3 \"target\"];Pmin=? [ F<=3 \"target\"]", 0.4, 0.4, 0.5, 0.5, true); + checkModelRational(STORM_TEST_RESOURCES_DIR "/imdp/tiny-03.drn", "Pmax=? [ F<=3 \"target\"];Pmin=? [ F<=3 \"target\"]", storm::RationalNumber(2, 5), + storm::RationalNumber(2, 5), storm::RationalNumber(1, 2), storm::RationalNumber(1, 2), true); } TEST(RobustMDPModelCheckingTest, Tiny04maxmin) { @@ -238,6 +288,34 @@ TEST(RobustMDPModelCheckingTest, Tiny04maxmin_rewards) { expectThrow(STORM_TEST_RESOURCES_DIR "/imdp/tiny-04.drn", "Rmin=? [ F \"target\"]"); } +// ---- RationalInterval tests (exact arithmetic) ---- + +TEST(RobustRationalMDPModelCheckingTest, Tiny01maxmin) { + checkModelRational(STORM_TEST_RESOURCES_DIR "/imdp/tiny-01.drn", "Pmax=? [ F \"target\"];Pmin=? [ F \"target\"]", storm::RationalNumber(2, 5), + storm::RationalNumber(1, 2), storm::RationalNumber(1, 2), storm::RationalNumber(2, 5), false); +} + +TEST(RobustRationalMDPModelCheckingTest, Tiny03maxmin) { + checkModelRational(STORM_TEST_RESOURCES_DIR "/imdp/tiny-03.drn", "Pmax=? [ F \"target\"];Pmin=? [ F \"target\"]", storm::RationalNumber(2, 5), + storm::RationalNumber(1, 2), storm::RationalNumber(1, 2), storm::RationalNumber(2, 5), true); +} + +TEST(RobustRationalMDPModelCheckingTest, BoundedTiny03maxmin) { + checkModelRational(STORM_TEST_RESOURCES_DIR "/imdp/tiny-03.drn", "Pmax=? [ F<=3 \"target\"];Pmin=? [ F<=3 \"target\"]", storm::RationalNumber(2, 5), + storm::RationalNumber(2, 5), storm::RationalNumber(1, 2), storm::RationalNumber(1, 2), true); +} + +TEST(RobustRationalMDPModelCheckingTest, Tiny04maxmin) { + // Fill in exact rational values once test output is known. + checkModelRational(STORM_TEST_RESOURCES_DIR "/imdp/tiny-04.drn", "Pmax=? [ F \"target\"];Pmin=? [ F \"target\"]", storm::RationalNumber(1), + storm::RationalNumber(1), storm::RationalNumber(42857140807299, 100000000000000), storm::RationalNumber(21, 50), false); +} + +TEST(RobustRationalMDPModelCheckingTest, Tiny05maxmin) { + checkModelRational(STORM_TEST_RESOURCES_DIR "/imdp/tiny-05.drn", "Pmax=? [ F \"target\"];Pmin=? [ F \"target\"]", storm::RationalNumber(3, 10), + storm::RationalNumber(2, 5), storm::RationalNumber(2, 5), storm::RationalNumber(3, 10), false); +} + TEST(RobustMDPModelCheckingTest, AddUncertaintyCoin22max) { #ifndef STORM_HAVE_Z3 GTEST_SKIP() << "Z3 not available."; @@ -245,3 +323,11 @@ TEST(RobustMDPModelCheckingTest, AddUncertaintyCoin22max) { makeUncertainAndCheck(STORM_TEST_RESOURCES_DIR "/mdp/coin2-2.nm", "Pmax=? [F \"all_coins_equal_1\"]", 0.1); makeUncertainAndCheck(STORM_TEST_RESOURCES_DIR "/mdp/coin2-2.nm", "Pmax=? [F \"all_coins_equal_1\"]", 0.2); } + +TEST(RobustRationalMDPModelCheckingTest, AddUncertaintyCoin22max) { +#ifndef STORM_HAVE_Z3 + GTEST_SKIP() << "Z3 not available."; +#endif + makeUncertainAndCheckRational(STORM_TEST_RESOURCES_DIR "/mdp/coin2-2.nm", "Pmax=? [F \"all_coins_equal_1\"]", 0.1); + makeUncertainAndCheckRational(STORM_TEST_RESOURCES_DIR "/mdp/coin2-2.nm", "Pmax=? [F \"all_coins_equal_1\"]", 0.2); +} diff --git a/src/test/storm/transformer/AddUncertaintyTest.cpp b/src/test/storm/transformer/AddUncertaintyTest.cpp index 1911aba39d..300b788d9b 100644 --- a/src/test/storm/transformer/AddUncertaintyTest.cpp +++ b/src/test/storm/transformer/AddUncertaintyTest.cpp @@ -3,6 +3,7 @@ #include "storm-parsers/api/storm-parsers.h" #include "storm-parsers/parser/PrismParser.h" +#include "storm/adapters/RationalNumberForward.h" #include "storm/api/storm.h" #include "storm/transformer/AddUncertainty.h" @@ -37,3 +38,35 @@ TEST(AddUncertaintyTransformerTest, Coin22Test) { EXPECT_EQ(uncertainModel->getNumberOfTransitions(), model->getNumberOfTransitions()); EXPECT_TRUE(uncertainModel->hasUncertainty()); } + +TEST(AddUncertaintyTransformerTest, BrpTestRational) { +#ifndef STORM_HAVE_Z3 + GTEST_SKIP() << "Z3 not available."; +#endif + storm::prism::Program program = storm::parser::PrismParser::parse(STORM_TEST_RESOURCES_DIR "/dtmc/brp-16-2.pm"); + std::string formulasString = "P=? [ F \"target\"]"; + auto formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulasString, program)); + auto model = storm::api::buildSparseModel(program, formulas); + + auto transformer = storm::transformer::AddUncertainty(model); + auto uncertainModel = transformer.transform(0.01); + EXPECT_EQ(uncertainModel->getNumberOfStates(), model->getNumberOfStates()); + EXPECT_EQ(uncertainModel->getNumberOfTransitions(), model->getNumberOfTransitions()); + EXPECT_TRUE(uncertainModel->hasUncertainty()); +} + +TEST(AddUncertaintyTransformerTest, Coin22TestRational) { +#ifndef STORM_HAVE_Z3 + GTEST_SKIP() << "Z3 not available."; +#endif + storm::prism::Program program = storm::parser::PrismParser::parse(STORM_TEST_RESOURCES_DIR "/mdp/coin2-2.nm"); + std::string formulasString = "Pmax=? [ F \"all_coins_equal_1\"]"; + auto formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulasString, program)); + auto model = storm::api::buildSparseModel(program, formulas); + + auto transformer = storm::transformer::AddUncertainty(model); + auto uncertainModel = transformer.transform(0.01); + EXPECT_EQ(uncertainModel->getNumberOfStates(), model->getNumberOfStates()); + EXPECT_EQ(uncertainModel->getNumberOfTransitions(), model->getNumberOfTransitions()); + EXPECT_TRUE(uncertainModel->hasUncertainty()); +} From 3b7de9611b8bdd8b6d470e200a16ef29ceea059d Mon Sep 17 00:00:00 2001 From: Luko van der Maas Date: Wed, 11 Mar 2026 16:56:01 +0100 Subject: [PATCH 19/51] format --- src/storm/transformer/AddUncertainty.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/storm/transformer/AddUncertainty.cpp b/src/storm/transformer/AddUncertainty.cpp index f8102f9bc5..98d746d7cc 100644 --- a/src/storm/transformer/AddUncertainty.cpp +++ b/src/storm/transformer/AddUncertainty.cpp @@ -23,7 +23,7 @@ std::shared_ptr: // 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); + origModel->getTransitionMatrix().getNonzeroEntryCount(), true, false); // Build transition matrix (without row grouping) for (uint64_t rowIndex = 0; rowIndex < origModel->getTransitionMatrix().getRowCount(); ++rowIndex) { if (origModel->getTransitionMatrix().getRowEntryCount(rowIndex) <= maxSuccessors) { From eb3fa033187d55b848a1e56b022a1bc20dff5888 Mon Sep 17 00:00:00 2001 From: Luko van der Maas Date: Wed, 11 Mar 2026 17:17:07 +0100 Subject: [PATCH 20/51] add back condition code removed in exact inteval --- src/storm/modelchecker/prctl/SparseMdpPrctlModelChecker.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/storm/modelchecker/prctl/SparseMdpPrctlModelChecker.cpp b/src/storm/modelchecker/prctl/SparseMdpPrctlModelChecker.cpp index a17ca029dc..acf311cec9 100644 --- a/src/storm/modelchecker/prctl/SparseMdpPrctlModelChecker.cpp +++ b/src/storm/modelchecker/prctl/SparseMdpPrctlModelChecker.cpp @@ -283,9 +283,9 @@ std::unique_ptr SparseMdpPrctlModelChecker::com 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()); } } From 9e7d03eef14fc30916a145ed345048709314ac34 Mon Sep 17 00:00:00 2001 From: Luko van der Maas Date: Wed, 11 Mar 2026 17:22:18 +0100 Subject: [PATCH 21/51] write ration numbers using string for cln --- .../dtmc/RobustDtmcPrctlModelCheckerTest.cpp | 8 +++---- .../mdp/RobustMdpPrctlModelCheckerTest.cpp | 22 +++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/test/storm/modelchecker/prctl/dtmc/RobustDtmcPrctlModelCheckerTest.cpp b/src/test/storm/modelchecker/prctl/dtmc/RobustDtmcPrctlModelCheckerTest.cpp index f08c7c3fb8..156c529f56 100644 --- a/src/test/storm/modelchecker/prctl/dtmc/RobustDtmcPrctlModelCheckerTest.cpp +++ b/src/test/storm/modelchecker/prctl/dtmc/RobustDtmcPrctlModelCheckerTest.cpp @@ -362,7 +362,7 @@ TEST(RobustDtmcModelCheckerTest, TinyO2Propositional) { TEST(RobustRationalDtmcModelCheckerTest, Tiny01ReachMaxMinProbs) { checkExplicitModelForQuantitativeResultRational(STORM_TEST_RESOURCES_DIR "/idtmc/tiny-01.drn", "P=? [ F \"target\"];P=? [ F \"target\"]", - storm::RationalNumber(3, 10), storm::RationalNumber(1, 2)); + storm::RationalNumber("3/10"), storm::RationalNumber("1/2")); } TEST(RobustRationalDtmcModelCheckerTest, Tiny01MaxReachProbNoUncertaintyResolutionMode) { @@ -384,7 +384,7 @@ TEST(RobustRationalDtmcModelCheckerTest, DieIntervalsMaxMin) { GTEST_SKIP() << "Z3 not available."; #endif checkPrismModelForQuantitativeResultRational(STORM_TEST_RESOURCES_DIR "/idtmc/die-intervals.pm", "P=? [ F \"one\"];P=? [ F \"one\"]", - storm::RationalNumber(4483008223, 94143178827), storm::RationalNumber(35864065784, 94143178827)); + storm::RationalNumber("4483008223/94143178827"), storm::RationalNumber("35864065784/94143178827")); } TEST(RobustRationalDtmcModelCheckerTest, BrpIntervalsMaxMin) { @@ -413,12 +413,12 @@ TEST(RobustRationalDtmcModelCheckerTest, DieIntervalsMaxMinRewards) { GTEST_SKIP() << "Z3 not available."; #endif checkPrismModelForQuantitativeResultRational(STORM_TEST_RESOURCES_DIR "/idtmc/die-intervals.pm", "R=? [ F \"done\"];R=? [ F \"done\"]", - storm::RationalNumber(15544649, 4782969), storm::RationalNumber(76715008330675523, 16677181699666569)); + storm::RationalNumber("15544649/4782969"), storm::RationalNumber("76715008330675523/16677181699666569")); } TEST(RobustRationalDtmcModelCheckerTest, Tiny03MaxMinRewards) { checkExplicitModelForQuantitativeResultRational(STORM_TEST_RESOURCES_DIR "/idtmc/tiny-03.drn", "R=? [ F \"target\"];R=? [ F \"target\"]", - storm::RationalNumber(13, 2), storm::RationalNumber(17, 2)); + storm::RationalNumber("13/2"), storm::RationalNumber("17/2")); } TEST(RobustRationalDtmcModelCheckerTest, Tiny03RewardsNoUncertaintyResolutionMode) { diff --git a/src/test/storm/modelchecker/prctl/mdp/RobustMdpPrctlModelCheckerTest.cpp b/src/test/storm/modelchecker/prctl/mdp/RobustMdpPrctlModelCheckerTest.cpp index c1e3a71cdd..84216e7928 100644 --- a/src/test/storm/modelchecker/prctl/mdp/RobustMdpPrctlModelCheckerTest.cpp +++ b/src/test/storm/modelchecker/prctl/mdp/RobustMdpPrctlModelCheckerTest.cpp @@ -272,8 +272,8 @@ TEST(RobustMDPModelCheckingTest, Tiny03maxmin) { TEST(RobustMDPModelCheckingTest, BoundedTiny03maxmin) { checkModel(STORM_TEST_RESOURCES_DIR "/imdp/tiny-03.drn", "Pmax=? [ F<=3 \"target\"];Pmin=? [ F<=3 \"target\"]", 0.4, 0.4, 0.5, 0.5, true); - checkModelRational(STORM_TEST_RESOURCES_DIR "/imdp/tiny-03.drn", "Pmax=? [ F<=3 \"target\"];Pmin=? [ F<=3 \"target\"]", storm::RationalNumber(2, 5), - storm::RationalNumber(2, 5), storm::RationalNumber(1, 2), storm::RationalNumber(1, 2), true); + checkModelRational(STORM_TEST_RESOURCES_DIR "/imdp/tiny-03.drn", "Pmax=? [ F<=3 \"target\"];Pmin=? [ F<=3 \"target\"]", storm::RationalNumber("2/5"), + storm::RationalNumber("2/5"), storm::RationalNumber("1/2"), storm::RationalNumber("1/2"), true); } TEST(RobustMDPModelCheckingTest, Tiny04maxmin) { @@ -291,29 +291,29 @@ TEST(RobustMDPModelCheckingTest, Tiny04maxmin_rewards) { // ---- RationalInterval tests (exact arithmetic) ---- TEST(RobustRationalMDPModelCheckingTest, Tiny01maxmin) { - checkModelRational(STORM_TEST_RESOURCES_DIR "/imdp/tiny-01.drn", "Pmax=? [ F \"target\"];Pmin=? [ F \"target\"]", storm::RationalNumber(2, 5), - storm::RationalNumber(1, 2), storm::RationalNumber(1, 2), storm::RationalNumber(2, 5), false); + checkModelRational(STORM_TEST_RESOURCES_DIR "/imdp/tiny-01.drn", "Pmax=? [ F \"target\"];Pmin=? [ F \"target\"]", storm::RationalNumber("2/5"), + storm::RationalNumber("1/2"), storm::RationalNumber("1/2"), storm::RationalNumber("2/5"), false); } TEST(RobustRationalMDPModelCheckingTest, Tiny03maxmin) { - checkModelRational(STORM_TEST_RESOURCES_DIR "/imdp/tiny-03.drn", "Pmax=? [ F \"target\"];Pmin=? [ F \"target\"]", storm::RationalNumber(2, 5), - storm::RationalNumber(1, 2), storm::RationalNumber(1, 2), storm::RationalNumber(2, 5), true); + checkModelRational(STORM_TEST_RESOURCES_DIR "/imdp/tiny-03.drn", "Pmax=? [ F \"target\"];Pmin=? [ F \"target\"]", storm::RationalNumber("2/5"), + storm::RationalNumber("1/2"), storm::RationalNumber("1/2"), storm::RationalNumber("2/5"), true); } TEST(RobustRationalMDPModelCheckingTest, BoundedTiny03maxmin) { - checkModelRational(STORM_TEST_RESOURCES_DIR "/imdp/tiny-03.drn", "Pmax=? [ F<=3 \"target\"];Pmin=? [ F<=3 \"target\"]", storm::RationalNumber(2, 5), - storm::RationalNumber(2, 5), storm::RationalNumber(1, 2), storm::RationalNumber(1, 2), true); + checkModelRational(STORM_TEST_RESOURCES_DIR "/imdp/tiny-03.drn", "Pmax=? [ F<=3 \"target\"];Pmin=? [ F<=3 \"target\"]", storm::RationalNumber("2/5"), + storm::RationalNumber("2/5"), storm::RationalNumber("1/2"), storm::RationalNumber("1/2"), true); } TEST(RobustRationalMDPModelCheckingTest, Tiny04maxmin) { // Fill in exact rational values once test output is known. checkModelRational(STORM_TEST_RESOURCES_DIR "/imdp/tiny-04.drn", "Pmax=? [ F \"target\"];Pmin=? [ F \"target\"]", storm::RationalNumber(1), - storm::RationalNumber(1), storm::RationalNumber(42857140807299, 100000000000000), storm::RationalNumber(21, 50), false); + storm::RationalNumber(1), storm::RationalNumber("42857140807299/100000000000000"), storm::RationalNumber("21/50"), false); } TEST(RobustRationalMDPModelCheckingTest, Tiny05maxmin) { - checkModelRational(STORM_TEST_RESOURCES_DIR "/imdp/tiny-05.drn", "Pmax=? [ F \"target\"];Pmin=? [ F \"target\"]", storm::RationalNumber(3, 10), - storm::RationalNumber(2, 5), storm::RationalNumber(2, 5), storm::RationalNumber(3, 10), false); + checkModelRational(STORM_TEST_RESOURCES_DIR "/imdp/tiny-05.drn", "Pmax=? [ F \"target\"];Pmin=? [ F \"target\"]", storm::RationalNumber("3/10"), + storm::RationalNumber("2/5"), storm::RationalNumber("2/5"), storm::RationalNumber("3/10"), false); } TEST(RobustMDPModelCheckingTest, AddUncertaintyCoin22max) { From 21411c71c0ef7da5cfcab15402849094f240bc37 Mon Sep 17 00:00:00 2001 From: Luko van der Maas Date: Thu, 19 Mar 2026 11:46:35 +0100 Subject: [PATCH 22/51] Refactor AddUncertainty transformer to use ValueType for uncertainty parameters and update tests accordingly --- src/storm/transformer/AddUncertainty.cpp | 19 ++++++++----------- src/storm/transformer/AddUncertainty.h | 8 ++++---- src/storm/utility/NumberTraits.h | 4 ++-- .../storm/transformer/AddUncertaintyTest.cpp | 4 ++-- 4 files changed, 16 insertions(+), 19 deletions(-) diff --git a/src/storm/transformer/AddUncertainty.cpp b/src/storm/transformer/AddUncertainty.cpp index 98d746d7cc..a9600f0f7b 100644 --- a/src/storm/transformer/AddUncertainty.cpp +++ b/src/storm/transformer/AddUncertainty.cpp @@ -19,20 +19,20 @@ AddUncertainty::AddUncertainty(std::shared_ptr std::shared_ptr::IntervalType>> AddUncertainty::transform( - double additiveUncertainty, double minimalTransitionProbability, uint64_t maxSuccessors) { + ValueType additiveUncertainty, ValueType minimalTransitionProbability, std::optional 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) { - if (origModel->getTransitionMatrix().getRowEntryCount(rowIndex) <= maxSuccessors) { + if (maxSuccessors.has_value() && origModel->getTransitionMatrix().getRowEntryCount(rowIndex) <= maxSuccessors.value()) { 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)); + newMatrixBuilder.addNextValue(rowIndex, entry.getColumn(), storm::utility::convertNumber(entry.getValue())); } } } @@ -74,17 +74,14 @@ std::shared_ptr: } template -typename AddUncertainty::IntervalType AddUncertainty::addUncertainty(ValueType const& vt, double additiveUncertainty, - double minimalValue) { +typename AddUncertainty::IntervalType AddUncertainty::addUncertainty(ValueType const& vt, ValueType additiveUncertainty, + ValueType minimalValue) { if (utility::isOne(vt)) { return IntervalType(storm::utility::one(), storm::utility::one()); } - ValueType const center = vt; - ValueType const uncertainty = storm::utility::convertNumber(additiveUncertainty); - ValueType const minVal = storm::utility::convertNumber(minimalValue); - STORM_LOG_THROW(center >= minVal, storm::exceptions::InvalidArgumentException, "Transition probability is smaller than minimal value"); - ValueType const lowerBound = storm::utility::max(center - uncertainty, minVal); - ValueType const upperBound = storm::utility::min(center + uncertainty, storm::utility::one() - minVal); + STORM_LOG_THROW(vt >= minimalValue, storm::exceptions::InvalidArgumentException, "Transition probability is smaller than minimal value"); + ValueType const lowerBound = storm::utility::max(vt - additiveUncertainty, minimalValue); + ValueType const upperBound = storm::utility::min(vt + additiveUncertainty, storm::utility::one() - minimalValue); STORM_LOG_ASSERT(storm::utility::isPositive(lowerBound), "Lower bound must be strictly above zero."); STORM_LOG_ASSERT(upperBound < storm::utility::one(), "Upper bound must be strictly below one."); return IntervalType(lowerBound, upperBound); diff --git a/src/storm/transformer/AddUncertainty.h b/src/storm/transformer/AddUncertainty.h index d3421dc7a2..6a7170fa60 100644 --- a/src/storm/transformer/AddUncertainty.h +++ b/src/storm/transformer/AddUncertainty.h @@ -22,13 +22,13 @@ template class AddUncertainty { public: using IntervalType = std::conditional_t, storm::RationalInterval, storm::Interval>; - + static_assert(std::is_same_v>, "Expected ValueType to match the interval base type."); AddUncertainty(std::shared_ptr> const& originalModel); - std::shared_ptr> transform(double additiveUncertainty, double minimalValue = 0.0001, - uint64_t maxSuccessors = 10000000); + std::shared_ptr> transform(ValueType additiveUncertainty, ValueType minimalValue = 0.0001, + std::optional maxSuccessors = {}); private: - IntervalType addUncertainty(ValueType const& vt, double additiveUncertainty, double minimalValue); + IntervalType addUncertainty(ValueType const& vt, ValueType additiveUncertainty, ValueType minimalValue); std::shared_ptr> origModel; }; diff --git a/src/storm/utility/NumberTraits.h b/src/storm/utility/NumberTraits.h index 408471dfa6..23cb9a6036 100644 --- a/src/storm/utility/NumberTraits.h +++ b/src/storm/utility/NumberTraits.h @@ -43,13 +43,13 @@ struct NumberTraits { template<> struct NumberTraits { - static const bool SupportsExponential = false; + static const bool SupportsExponential = true; static const bool IsExact = false; }; template<> struct NumberTraits { - static const bool SupportsExponential = false; + static const bool SupportsExponential = true; static const bool IsExact = true; }; diff --git a/src/test/storm/transformer/AddUncertaintyTest.cpp b/src/test/storm/transformer/AddUncertaintyTest.cpp index 300b788d9b..d35b4d38ee 100644 --- a/src/test/storm/transformer/AddUncertaintyTest.cpp +++ b/src/test/storm/transformer/AddUncertaintyTest.cpp @@ -49,7 +49,7 @@ TEST(AddUncertaintyTransformerTest, BrpTestRational) { auto model = storm::api::buildSparseModel(program, formulas); auto transformer = storm::transformer::AddUncertainty(model); - auto uncertainModel = transformer.transform(0.01); + auto uncertainModel = transformer.transform(storm::RationalNumber("1/100")); EXPECT_EQ(uncertainModel->getNumberOfStates(), model->getNumberOfStates()); EXPECT_EQ(uncertainModel->getNumberOfTransitions(), model->getNumberOfTransitions()); EXPECT_TRUE(uncertainModel->hasUncertainty()); @@ -65,7 +65,7 @@ TEST(AddUncertaintyTransformerTest, Coin22TestRational) { auto model = storm::api::buildSparseModel(program, formulas); auto transformer = storm::transformer::AddUncertainty(model); - auto uncertainModel = transformer.transform(0.01); + auto uncertainModel = transformer.transform(storm::RationalNumber("1/100")); EXPECT_EQ(uncertainModel->getNumberOfStates(), model->getNumberOfStates()); EXPECT_EQ(uncertainModel->getNumberOfTransitions(), model->getNumberOfTransitions()); EXPECT_TRUE(uncertainModel->hasUncertainty()); From 1b80a97cd95ef5b2c2ba531555c149f78321ab66 Mon Sep 17 00:00:00 2001 From: Luko van der Maas Date: Thu, 19 Mar 2026 14:12:51 +0100 Subject: [PATCH 23/51] Fix logic in AddUncertainty transformer to handle maxSuccessors condition correctly --- src/storm/transformer/AddUncertainty.cpp | 2 +- src/storm/transformer/AddUncertainty.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/storm/transformer/AddUncertainty.cpp b/src/storm/transformer/AddUncertainty.cpp index a9600f0f7b..e750f34089 100644 --- a/src/storm/transformer/AddUncertainty.cpp +++ b/src/storm/transformer/AddUncertainty.cpp @@ -26,7 +26,7 @@ std::shared_ptr: origModel->getTransitionMatrix().getNonzeroEntryCount(), true, false); // Build transition matrix (without row grouping) for (uint64_t rowIndex = 0; rowIndex < origModel->getTransitionMatrix().getRowCount(); ++rowIndex) { - if (maxSuccessors.has_value() && origModel->getTransitionMatrix().getRowEntryCount(rowIndex) <= maxSuccessors.value()) { + if (!maxSuccessors.has_value() || origModel->getTransitionMatrix().getRowEntryCount(rowIndex) <= maxSuccessors.value()) { for (auto const& entry : origModel->getTransitionMatrix().getRow(rowIndex)) { newMatrixBuilder.addNextValue(rowIndex, entry.getColumn(), addUncertainty(entry.getValue(), additiveUncertainty, minimalTransitionProbability)); } diff --git a/src/storm/transformer/AddUncertainty.h b/src/storm/transformer/AddUncertainty.h index 6a7170fa60..ad1ff5abc2 100644 --- a/src/storm/transformer/AddUncertainty.h +++ b/src/storm/transformer/AddUncertainty.h @@ -23,6 +23,7 @@ class AddUncertainty { public: using IntervalType = std::conditional_t, storm::RationalInterval, storm::Interval>; static_assert(std::is_same_v>, "Expected ValueType to match the interval base type."); + AddUncertainty(std::shared_ptr> const& originalModel); std::shared_ptr> transform(ValueType additiveUncertainty, ValueType minimalValue = 0.0001, std::optional maxSuccessors = {}); From 82a0bfb6d3c7ebbb68d7e889cdb82c4ffeb98b57 Mon Sep 17 00:00:00 2001 From: Luko van der Maas Date: Thu, 19 Mar 2026 15:17:28 +0100 Subject: [PATCH 24/51] Fix default value of argument not convertable to rational numbers for cln --- src/storm/transformer/AddUncertainty.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/storm/transformer/AddUncertainty.h b/src/storm/transformer/AddUncertainty.h index ad1ff5abc2..1ff2d35995 100644 --- a/src/storm/transformer/AddUncertainty.h +++ b/src/storm/transformer/AddUncertainty.h @@ -5,6 +5,7 @@ #include "storm/adapters/IntervalForward.h" #include "storm/adapters/RationalNumberForward.h" #include "storm/models/sparse/Model.h" +#include "storm/utility/constants.h" namespace storm::transformer { @@ -25,7 +26,8 @@ class AddUncertainty { static_assert(std::is_same_v>, "Expected ValueType to match the interval base type."); AddUncertainty(std::shared_ptr> const& originalModel); - std::shared_ptr> transform(ValueType additiveUncertainty, ValueType minimalValue = 0.0001, + std::shared_ptr> transform(ValueType additiveUncertainty, + ValueType minimalValue = storm::utility::convertNumber(0.0001), std::optional maxSuccessors = {}); private: From 4580bd1f79bc605e58af166c2550ba9a88bd6126 Mon Sep 17 00:00:00 2001 From: Luko van der Maas Date: Thu, 19 Mar 2026 15:45:00 +0100 Subject: [PATCH 25/51] Fix conversion error in robust model checking test --- .../prctl/mdp/RobustMdpPrctlModelCheckerTest.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/storm/modelchecker/prctl/mdp/RobustMdpPrctlModelCheckerTest.cpp b/src/test/storm/modelchecker/prctl/mdp/RobustMdpPrctlModelCheckerTest.cpp index 84216e7928..6c6eb17ff1 100644 --- a/src/test/storm/modelchecker/prctl/mdp/RobustMdpPrctlModelCheckerTest.cpp +++ b/src/test/storm/modelchecker/prctl/mdp/RobustMdpPrctlModelCheckerTest.cpp @@ -226,7 +226,7 @@ void makeUncertainAndCheck(std::string const& path, std::string const& formulaSt EXPECT_LE(certainValue, maxValue); } -void makeUncertainAndCheckRational(std::string const& path, std::string const& formulaString, double amountOfUncertainty) { +void makeUncertainAndCheckRational(std::string const& path, std::string const& formulaString, storm::RationalNumber amountOfUncertainty) { storm::prism::Program program = storm::api::parseProgram(path); program = storm::utility::prism::preprocess(program, ""); std::vector> formulas = @@ -328,6 +328,6 @@ TEST(RobustRationalMDPModelCheckingTest, AddUncertaintyCoin22max) { #ifndef STORM_HAVE_Z3 GTEST_SKIP() << "Z3 not available."; #endif - makeUncertainAndCheckRational(STORM_TEST_RESOURCES_DIR "/mdp/coin2-2.nm", "Pmax=? [F \"all_coins_equal_1\"]", 0.1); - makeUncertainAndCheckRational(STORM_TEST_RESOURCES_DIR "/mdp/coin2-2.nm", "Pmax=? [F \"all_coins_equal_1\"]", 0.2); + makeUncertainAndCheckRational(STORM_TEST_RESOURCES_DIR "/mdp/coin2-2.nm", "Pmax=? [F \"all_coins_equal_1\"]", storm::RationalNumber("1/10")); + makeUncertainAndCheckRational(STORM_TEST_RESOURCES_DIR "/mdp/coin2-2.nm", "Pmax=? [F \"all_coins_equal_1\"]", storm::RationalNumber("1/5")); } From c8ae01235e0b42aba7d2b371c6a7dca6846e8f56 Mon Sep 17 00:00:00 2001 From: Luko van der Maas Date: Thu, 19 Mar 2026 16:40:06 +0100 Subject: [PATCH 26/51] Update makeUncertainAndCheckRational to use storm::RationalNumber for uncertainty parameter --- .../prctl/dtmc/RobustDtmcPrctlModelCheckerTest.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/storm/modelchecker/prctl/dtmc/RobustDtmcPrctlModelCheckerTest.cpp b/src/test/storm/modelchecker/prctl/dtmc/RobustDtmcPrctlModelCheckerTest.cpp index 156c529f56..d132bdca1d 100644 --- a/src/test/storm/modelchecker/prctl/dtmc/RobustDtmcPrctlModelCheckerTest.cpp +++ b/src/test/storm/modelchecker/prctl/dtmc/RobustDtmcPrctlModelCheckerTest.cpp @@ -249,7 +249,7 @@ void checkPrismModelForQuantitativeResultRational(std::string const& path, std:: EXPECT_EQ(min, getQuantitativeResultAtInitialState(dtmc, resultMin)); } -void makeUncertainAndCheckRational(std::string const& path, std::string const& formulaString, double amountOfUncertainty) { +void makeUncertainAndCheckRational(std::string const& path, std::string const& formulaString, storm::RationalNumber amountOfUncertainty) { storm::prism::Program program = storm::api::parseProgram(path); program = storm::utility::prism::preprocess(program, ""); std::vector> formulas = @@ -434,6 +434,6 @@ TEST(RobustRationalDtmcModelCheckerTest, AddUncertaintyBrpMax) { #ifndef STORM_HAVE_Z3 GTEST_SKIP() << "Z3 not available."; #endif - makeUncertainAndCheckRational(STORM_TEST_RESOURCES_DIR "/dtmc/brp-16-2.pm", "P=? [ F \"target\"]", 0.01); - makeUncertainAndCheckRational(STORM_TEST_RESOURCES_DIR "/dtmc/brp-16-2.pm", "P=? [ F \"target\"]", 0.05); + makeUncertainAndCheckRational(STORM_TEST_RESOURCES_DIR "/dtmc/brp-16-2.pm", "P=? [ F \"target\"]", storm::RationalNumber("0.01")); + makeUncertainAndCheckRational(STORM_TEST_RESOURCES_DIR "/dtmc/brp-16-2.pm", "P=? [ F \"target\"]", storm::RationalNumber("0.05")); } From db5ae16a9d30630644a949fd54e7800505a1b482 Mon Sep 17 00:00:00 2001 From: Luko van der Maas Date: Thu, 19 Mar 2026 18:00:38 +0100 Subject: [PATCH 27/51] Fix test --- .../prctl/dtmc/RobustDtmcPrctlModelCheckerTest.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/storm/modelchecker/prctl/dtmc/RobustDtmcPrctlModelCheckerTest.cpp b/src/test/storm/modelchecker/prctl/dtmc/RobustDtmcPrctlModelCheckerTest.cpp index d132bdca1d..9c2b0f4f7b 100644 --- a/src/test/storm/modelchecker/prctl/dtmc/RobustDtmcPrctlModelCheckerTest.cpp +++ b/src/test/storm/modelchecker/prctl/dtmc/RobustDtmcPrctlModelCheckerTest.cpp @@ -434,6 +434,6 @@ TEST(RobustRationalDtmcModelCheckerTest, AddUncertaintyBrpMax) { #ifndef STORM_HAVE_Z3 GTEST_SKIP() << "Z3 not available."; #endif - makeUncertainAndCheckRational(STORM_TEST_RESOURCES_DIR "/dtmc/brp-16-2.pm", "P=? [ F \"target\"]", storm::RationalNumber("0.01")); - makeUncertainAndCheckRational(STORM_TEST_RESOURCES_DIR "/dtmc/brp-16-2.pm", "P=? [ F \"target\"]", storm::RationalNumber("0.05")); + makeUncertainAndCheckRational(STORM_TEST_RESOURCES_DIR "/dtmc/brp-16-2.pm", "P=? [ F \"target\"]", storm::RationalNumber("1/10")); + makeUncertainAndCheckRational(STORM_TEST_RESOURCES_DIR "/dtmc/brp-16-2.pm", "P=? [ F \"target\"]", storm::RationalNumber("1/20")); } From d2618bd7ed2e113fe77d63bfc539bea0ab33867d Mon Sep 17 00:00:00 2001 From: Luko van der Maas Date: Tue, 24 Mar 2026 14:32:29 +0100 Subject: [PATCH 28/51] Use IntervalBaseType in SparseDtmcEliminationModelChecker and correct error messages about intervals in SparseMdpPrctlHelper. --- src/storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.cpp | 4 ++-- .../reachability/SparseDtmcEliminationModelChecker.h | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.cpp b/src/storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.cpp index cb205d7cae..c610ffe7ff 100644 --- a/src/storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.cpp +++ b/src/storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.cpp @@ -1062,7 +1062,7 @@ std::vector SparseMdpPrctlHelper::compute // 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 rational interval rewards with double interval models."); + STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "We do not support double interval rewards with rational interval models."); } else { return computeReachabilityRewardsHelper( env, std::move(goal), transitionMatrix, backwardTransitions, @@ -1097,7 +1097,7 @@ std::vector SparseMdpPrctlHelper::compute // 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."); + 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, diff --git a/src/storm/modelchecker/reachability/SparseDtmcEliminationModelChecker.h b/src/storm/modelchecker/reachability/SparseDtmcEliminationModelChecker.h index f02feee29b..8b70af8ea4 100644 --- a/src/storm/modelchecker/reachability/SparseDtmcEliminationModelChecker.h +++ b/src/storm/modelchecker/reachability/SparseDtmcEliminationModelChecker.h @@ -1,5 +1,6 @@ #pragma once +#include "storm/adapters/IntervalForward.h" #include "storm/modelchecker/propositional/SparsePropositionalModelChecker.h" #include "storm/models/sparse/Dtmc.h" #include "storm/solver/stateelimination/StatePriorityQueue.h" @@ -27,7 +28,7 @@ class SparseDtmcEliminationModelChecker : public SparsePropositionalModelChecker typedef typename SparseDtmcModelType::RewardModelType RewardModelType; typedef typename storm::storage::FlexibleSparseMatrix::row_type FlexibleRowType; typedef typename FlexibleRowType::iterator FlexibleRowIterator; - using SolutionType = typename std::conditional, double, ValueType>::type; + using SolutionType = storm::IntervalBaseType; /*! * Creates an elimination-based model checker for the given model. From 8222dcfd1bbba58b3601946fd23eaea0b2840ab6 Mon Sep 17 00:00:00 2001 From: Luko van der Maas Date: Tue, 24 Mar 2026 16:00:03 +0100 Subject: [PATCH 29/51] Remove unused IntervalForward includes, interval setting, and adjust optimization direction in ViOperatorMultiplier --- .../results/ExplicitQualitativeCheckResult.cpp | 1 - .../modelchecker/results/ExplicitQualitativeCheckResult.h | 1 + src/storm/settings/modules/GeneralSettings.cpp | 6 ------ src/storm/settings/modules/GeneralSettings.h | 8 -------- src/storm/solver/IterativeMinMaxLinearEquationSolver.cpp | 1 - src/storm/solver/helper/ValueIterationOperator.cpp | 1 - src/storm/solver/helper/ValueIterationOperator.h | 1 - src/storm/solver/multiplier/ViOperatorMultiplier.cpp | 8 ++++---- 8 files changed, 5 insertions(+), 22 deletions(-) diff --git a/src/storm/modelchecker/results/ExplicitQualitativeCheckResult.cpp b/src/storm/modelchecker/results/ExplicitQualitativeCheckResult.cpp index 76d86175b0..50b9a9e16c 100644 --- a/src/storm/modelchecker/results/ExplicitQualitativeCheckResult.cpp +++ b/src/storm/modelchecker/results/ExplicitQualitativeCheckResult.cpp @@ -3,7 +3,6 @@ #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" diff --git a/src/storm/modelchecker/results/ExplicitQualitativeCheckResult.h b/src/storm/modelchecker/results/ExplicitQualitativeCheckResult.h index 95db1271bb..9fb3228f0d 100644 --- a/src/storm/modelchecker/results/ExplicitQualitativeCheckResult.h +++ b/src/storm/modelchecker/results/ExplicitQualitativeCheckResult.h @@ -4,6 +4,7 @@ #include #include +#include "storm/adapters/IntervalForward.h" #include "storm/adapters/JsonForward.h" #include "storm/modelchecker/results/QualitativeCheckResult.h" #include "storm/models/sparse/StateLabeling.h" diff --git a/src/storm/settings/modules/GeneralSettings.cpp b/src/storm/settings/modules/GeneralSettings.cpp index 8b440ee61f..d2efd632e8 100644 --- a/src/storm/settings/modules/GeneralSettings.cpp +++ b/src/storm/settings/modules/GeneralSettings.cpp @@ -32,7 +32,6 @@ 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( @@ -86,7 +85,6 @@ 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 { @@ -154,10 +152,6 @@ 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 2e2e12620f..1723583b14 100644 --- a/src/storm/settings/modules/GeneralSettings.h +++ b/src/storm/settings/modules/GeneralSettings.h @@ -123,13 +123,6 @@ 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; @@ -156,7 +149,6 @@ 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/solver/IterativeMinMaxLinearEquationSolver.cpp b/src/storm/solver/IterativeMinMaxLinearEquationSolver.cpp index 32040e68ec..0ce268ac03 100644 --- a/src/storm/solver/IterativeMinMaxLinearEquationSolver.cpp +++ b/src/storm/solver/IterativeMinMaxLinearEquationSolver.cpp @@ -4,7 +4,6 @@ #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" diff --git a/src/storm/solver/helper/ValueIterationOperator.cpp b/src/storm/solver/helper/ValueIterationOperator.cpp index 4f352705e3..bd8a856f53 100644 --- a/src/storm/solver/helper/ValueIterationOperator.cpp +++ b/src/storm/solver/helper/ValueIterationOperator.cpp @@ -3,7 +3,6 @@ #include #include "storm/adapters/IntervalAdapter.h" -#include "storm/adapters/IntervalForward.h" #include "storm/adapters/RationalNumberAdapter.h" #include "storm/storage/BitVector.h" #include "storm/storage/SparseMatrix.h" diff --git a/src/storm/solver/helper/ValueIterationOperator.h b/src/storm/solver/helper/ValueIterationOperator.h index a3172ad40e..257f6c95fe 100644 --- a/src/storm/solver/helper/ValueIterationOperator.h +++ b/src/storm/solver/helper/ValueIterationOperator.h @@ -4,7 +4,6 @@ #include #include #include -#include #include #include #include diff --git a/src/storm/solver/multiplier/ViOperatorMultiplier.cpp b/src/storm/solver/multiplier/ViOperatorMultiplier.cpp index fc85ce4cd1..39b5ca17da 100644 --- a/src/storm/solver/multiplier/ViOperatorMultiplier.cpp +++ b/src/storm/solver/multiplier/ViOperatorMultiplier.cpp @@ -241,18 +241,18 @@ void ViOperatorMultiplier::multiply if (storm::solver::minimize(dir)) { if (choices) { detail::MultiplierBackend backend(*choices, this->matrix.getRowGroupIndices()); - apply(backend, OptimizationDirection::Maximize); + apply(backend, OptimizationDirection::Minimize); } else { detail::MultiplierBackend backend; - apply(backend, OptimizationDirection::Maximize); + apply(backend, OptimizationDirection::Minimize); } } else { if (choices) { detail::MultiplierBackend backend(*choices, this->matrix.getRowGroupIndices()); - apply(backend, OptimizationDirection::Minimize); + apply(backend, OptimizationDirection::Maximize); } else { detail::MultiplierBackend backend; - apply(backend, OptimizationDirection::Minimize); + apply(backend, OptimizationDirection::Maximize); } } } From e30a4eadc65b7336cfd7165f77c433b21920091c Mon Sep 17 00:00:00 2001 From: Luko van der Maas Date: Wed, 1 Apr 2026 13:35:17 +0200 Subject: [PATCH 30/51] add uncertainty resolution mode to multipliers and enable next properties for interval models --- resources/examples/testfiles/imdp/tiny-03.drn | 4 +- .../SparseMarkovAutomatonCslModelChecker.cpp | 3 +- ...ndeterministicStepBoundedHorizonHelper.cpp | 6 +- .../infinitehorizon/internal/LraViHelper.cpp | 2 +- .../prctl/SparseMdpPrctlModelChecker.cpp | 3 +- .../prctl/helper/SparseMdpPrctlHelper.cpp | 27 ++++----- .../prctl/helper/SparseMdpPrctlHelper.h | 2 + .../IterativeMinMaxLinearEquationSolver.cpp | 2 +- src/storm/solver/StandardGameSolver.cpp | 2 +- src/storm/solver/multiplier/Multiplier.cpp | 13 ++-- src/storm/solver/multiplier/Multiplier.h | 13 +++- .../solver/multiplier/NativeMultiplier.cpp | 7 ++- .../solver/multiplier/NativeMultiplier.h | 1 + .../multiplier/ViOperatorMultiplier.cpp | 59 +++++++++++-------- .../solver/multiplier/ViOperatorMultiplier.h | 1 + .../mdp/RobustMdpPrctlModelCheckerTest.cpp | 12 ++-- src/test/storm/solver/MultiplierTest.cpp | 2 + 17 files changed, 95 insertions(+), 64 deletions(-) diff --git a/resources/examples/testfiles/imdp/tiny-03.drn b/resources/examples/testfiles/imdp/tiny-03.drn index 46767f506c..01c8fd5858 100644 --- a/resources/examples/testfiles/imdp/tiny-03.drn +++ b/resources/examples/testfiles/imdp/tiny-03.drn @@ -14,8 +14,8 @@ state 0 init 1 : [0.4, 0.9] 2 : [0.5, 0.8] action 1 - 1 : [0.4, 0.9] - 2 : [0.5, 0.8] + 1 : [0.3, 0.7] + 2 : [0.4, 0.8] state 1 target action 0 1 : [1, 1] diff --git a/src/storm/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.cpp b/src/storm/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.cpp index e4e5f820b1..6e15e9e9be 100644 --- a/src/storm/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.cpp +++ b/src/storm/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.cpp @@ -105,7 +105,8 @@ std::unique_ptr SparseMarkovAutomatonCslModelChecker subResultPointer = this->check(env, pathFormula.getSubformula()); ExplicitQualitativeCheckResult const& subResult = subResultPointer->template asExplicitQualitativeCheckResult(); std::vector numericResult = storm::modelchecker::helper::SparseMdpPrctlHelper::computeNextProbabilities( - env, checkTask.getOptimizationDirection(), this->getModel().getTransitionMatrix(), subResult.getTruthValuesVector()); + env, checkTask.getOptimizationDirection(), checkTask.getUncertaintyResolutionMode(), this->getModel().getTransitionMatrix(), + subResult.getTruthValuesVector()); return std::unique_ptr(new ExplicitQuantitativeCheckResult(std::move(numericResult))); } diff --git a/src/storm/modelchecker/helper/finitehorizon/SparseNondeterministicStepBoundedHorizonHelper.cpp b/src/storm/modelchecker/helper/finitehorizon/SparseNondeterministicStepBoundedHorizonHelper.cpp index 9eedacca02..b4a53af606 100644 --- a/src/storm/modelchecker/helper/finitehorizon/SparseNondeterministicStepBoundedHorizonHelper.cpp +++ b/src/storm/modelchecker/helper/finitehorizon/SparseNondeterministicStepBoundedHorizonHelper.cpp @@ -85,9 +85,9 @@ std::vector SparseNondeterministicStepBoundedHorizonHelper().create(env, submatrix); if (lowerBound == 0) { - multiplier->repeatedMultiplyAndReduce(env, goal.direction(), subresult, &b, upperBound); + multiplier->repeatedMultiplyAndReduce(env, goal.direction(), subresult, &b, upperBound, goal.getUncertaintyResolutionMode()); } else { - multiplier->repeatedMultiplyAndReduce(env, goal.direction(), subresult, &b, upperBound - lowerBound + 1); + multiplier->repeatedMultiplyAndReduce(env, goal.direction(), subresult, &b, upperBound - lowerBound + 1, goal.getUncertaintyResolutionMode()); storm::storage::SparseMatrix submatrix; if constexpr (storm::IsIntervalType) { @@ -98,7 +98,7 @@ std::vector SparseNondeterministicStepBoundedHorizonHelper().create(env, submatrix); b = std::vector(b.size(), storm::utility::zero()); - multiplier->repeatedMultiplyAndReduce(env, goal.direction(), subresult, &b, lowerBound - 1); + multiplier->repeatedMultiplyAndReduce(env, goal.direction(), subresult, &b, lowerBound - 1, goal.getUncertaintyResolutionMode()); } // Set the values of the resulting vector accordingly. storm::utility::vector::setVectorValues(result, maybeStates, subresult); diff --git a/src/storm/modelchecker/helper/infinitehorizon/internal/LraViHelper.cpp b/src/storm/modelchecker/helper/infinitehorizon/internal/LraViHelper.cpp index 80d87e91bd..3c241c4688 100644 --- a/src/storm/modelchecker/helper/infinitehorizon/internal/LraViHelper.cpp +++ b/src/storm/modelchecker/helper/infinitehorizon/internal/LraViHelper.cpp @@ -373,7 +373,7 @@ void LraViHelper::performIterationSte } else { // Also keep track of the choices made. std::vector tsChoices(_TsTransitions.getRowGroupCount()); - _TsMultiplier->multiplyAndReduce(env, *dir, xOld(), &_TsChoiceValues, xNew(), &tsChoices); + _TsMultiplier->multiplyAndReduce(env, *dir, xOld(), &_TsChoiceValues, xNew(), UncertaintyResolutionMode::Unset, &tsChoices); // Note that nondeterminism within the timed states means that there can not be instant states (We either have MDPs or MAs) // Hence, in this branch we don't have to care for choices at instant states. STORM_LOG_ASSERT(!_hasInstantStates, "Nondeterministic timed states are only supported if there are no instant states."); diff --git a/src/storm/modelchecker/prctl/SparseMdpPrctlModelChecker.cpp b/src/storm/modelchecker/prctl/SparseMdpPrctlModelChecker.cpp index a17ca029dc..54f0fb436c 100644 --- a/src/storm/modelchecker/prctl/SparseMdpPrctlModelChecker.cpp +++ b/src/storm/modelchecker/prctl/SparseMdpPrctlModelChecker.cpp @@ -162,7 +162,8 @@ std::unique_ptr SparseMdpPrctlModelChecker::com std::unique_ptr subResultPointer = this->check(env, pathFormula.getSubformula()); ExplicitQualitativeCheckResult const& subResult = subResultPointer->template asExplicitQualitativeCheckResult(); std::vector numericResult = storm::modelchecker::helper::SparseMdpPrctlHelper::computeNextProbabilities( - env, checkTask.getOptimizationDirection(), this->getModel().getTransitionMatrix(), subResult.getTruthValuesVector()); + env, checkTask.getOptimizationDirection(), checkTask.getUncertaintyResolutionMode(), this->getModel().getTransitionMatrix(), + subResult.getTruthValuesVector()); return std::unique_ptr(new ExplicitQuantitativeCheckResult(std::move(numericResult))); } diff --git a/src/storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.cpp b/src/storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.cpp index c610ffe7ff..7e10f08ed4 100644 --- a/src/storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.cpp +++ b/src/storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.cpp @@ -131,20 +131,16 @@ std::map SparseMdpPrctlHelper< template std::vector SparseMdpPrctlHelper::computeNextProbabilities( - Environment const& env, OptimizationDirection dir, storm::storage::SparseMatrix const& transitionMatrix, - storm::storage::BitVector const& nextStates) { - 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()); - storm::utility::vector::setVectorValues(result, nextStates, storm::utility::one()); + Environment const& env, OptimizationDirection dir, UncertaintyResolutionMode uncertaintyResolutionMode, + storm::storage::SparseMatrix const& transitionMatrix, storm::storage::BitVector const& nextStates) { + // Create the vector with which to multiply and initialize it correctly. + std::vector result(transitionMatrix.getRowGroupCount()); + storm::utility::vector::setVectorValues(result, nextStates, storm::utility::one()); - auto multiplier = storm::solver::MultiplierFactory().create(env, transitionMatrix); - multiplier->multiplyAndReduce(env, dir, result, nullptr, result); + auto multiplier = storm::solver::MultiplierFactory().create(env, transitionMatrix); + multiplier->multiplyAndReduce(env, dir, result, nullptr, result, uncertaintyResolutionMode); - return result; - } + return result; } template @@ -843,7 +839,7 @@ std::vector SparseMdpPrctlHelper::compute std::vector result(rewardModel.getStateRewardVector()); auto multiplier = storm::solver::MultiplierFactory().create(env, transitionMatrix); - multiplier->repeatedMultiplyAndReduce(env, goal.direction(), result, nullptr, stepCount); + multiplier->repeatedMultiplyAndReduce(env, goal.direction(), result, nullptr, stepCount, goal.getUncertaintyResolutionMode()); return result; } @@ -867,7 +863,7 @@ std::vector SparseMdpPrctlHelper::compute std::vector result(transitionMatrix.getRowGroupCount(), storm::utility::zero()); auto multiplier = storm::solver::MultiplierFactory().create(env, transitionMatrix); - multiplier->repeatedMultiplyAndReduce(env, goal.direction(), result, &totalRewardVector, stepBound); + multiplier->repeatedMultiplyAndReduce(env, goal.direction(), result, &totalRewardVector, stepBound, goal.getUncertaintyResolutionMode()); return result; } @@ -989,7 +985,8 @@ std::vector SparseMdpPrctlHelper::compute std::vector result(transitionMatrix.getRowGroupCount(), storm::utility::zero()); auto multiplier = storm::solver::MultiplierFactory().create(env, transitionMatrix); - multiplier->repeatedMultiplyAndReduceWithFactor(env, goal.direction(), result, &totalRewardVector, stepBound, discountFactor); + multiplier->repeatedMultiplyAndReduceWithFactor(env, goal.direction(), result, &totalRewardVector, stepBound, discountFactor, + goal.getUncertaintyResolutionMode()); return result; } diff --git a/src/storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.h b/src/storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.h index cbcc545b4d..12f2ebbc18 100644 --- a/src/storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.h +++ b/src/storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.h @@ -7,6 +7,7 @@ #include "storm/modelchecker/prctl/helper/MDPModelCheckingHelperReturnType.h" #include "storm/modelchecker/prctl/helper/rewardbounded/MultiDimensionalRewardUnfolding.h" #include "storm/solver/SolveGoal.h" +#include "storm/solver/UncertaintyResolutionMode.h" #include "storm/storage/MaximalEndComponent.h" #include "storm/storage/SparseMatrix.h" @@ -38,6 +39,7 @@ class SparseMdpPrctlHelper { storm::storage::BitVector const& initialStates); static std::vector computeNextProbabilities(Environment const& env, OptimizationDirection dir, + UncertaintyResolutionMode uncertaintyResolutionMode, storm::storage::SparseMatrix const& transitionMatrix, storm::storage::BitVector const& nextStates); diff --git a/src/storm/solver/IterativeMinMaxLinearEquationSolver.cpp b/src/storm/solver/IterativeMinMaxLinearEquationSolver.cpp index 0ce268ac03..3819591a75 100644 --- a/src/storm/solver/IterativeMinMaxLinearEquationSolver.cpp +++ b/src/storm/solver/IterativeMinMaxLinearEquationSolver.cpp @@ -63,7 +63,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 && diff --git a/src/storm/solver/StandardGameSolver.cpp b/src/storm/solver/StandardGameSolver.cpp index 0d9fc4337d..079bf2f0c4 100644 --- a/src/storm/solver/StandardGameSolver.cpp +++ b/src/storm/solver/StandardGameSolver.cpp @@ -427,7 +427,7 @@ void StandardGameSolver::multiplyAndReduce(Environment const& env, Op storm::solver::Multiplier const& multiplier, std::vector& player2ReducedResult, std::vector& player1ReducedResult, std::vector* player1SchedulerChoices, std::vector* player2SchedulerChoices) const { - multiplier.multiplyAndReduce(env, player2Dir, x, b, player2ReducedResult, player2SchedulerChoices); + multiplier.multiplyAndReduce(env, player2Dir, x, b, player2ReducedResult, UncertaintyResolutionMode::Unset, player2SchedulerChoices); if (this->player1RepresentedByMatrix()) { // Player 1 represented by matrix. diff --git a/src/storm/solver/multiplier/Multiplier.cpp b/src/storm/solver/multiplier/Multiplier.cpp index 7b30a6afca..45acc9d578 100644 --- a/src/storm/solver/multiplier/Multiplier.cpp +++ b/src/storm/solver/multiplier/Multiplier.cpp @@ -31,8 +31,9 @@ void Multiplier::clearCache() const { template void Multiplier::multiplyAndReduce(Environment const& env, OptimizationDirection const& dir, std::vector const& x, std::vector const* b, std::vector& result, + UncertaintyResolutionMode const& uncertaintyResolutionMode, std::vector* choices) const { - multiplyAndReduce(env, dir, this->matrix.getRowGroupIndices(), x, b, result, choices); + multiplyAndReduce(env, dir, this->matrix.getRowGroupIndices(), x, b, result, uncertaintyResolutionMode, choices); } template @@ -60,13 +61,14 @@ void Multiplier::repeatedMultiply(Environment const& en template void Multiplier::repeatedMultiplyAndReduce(Environment const& env, OptimizationDirection const& dir, std::vector& x, - std::vector const* b, uint64_t n) const { + std::vector const* b, uint64_t n, + UncertaintyResolutionMode const& uncertaintyResolutionMode) const { storm::utility::ProgressMeasurement progress("multiplications"); progress.setMaxCount(n); progress.startNewMeasurement(0); for (uint64_t i = 0; i < n; ++i) { progress.updateProgress(i); - multiplyAndReduce(env, dir, x, b, x); + multiplyAndReduce(env, dir, x, b, x, uncertaintyResolutionMode); if (storm::utility::resources::isTerminate()) { STORM_LOG_WARN("Aborting after " << i << " of " << n << " multiplications"); break; @@ -77,14 +79,15 @@ void Multiplier::repeatedMultiplyAndReduce(Environment template void Multiplier::repeatedMultiplyAndReduceWithFactor(Environment const& env, OptimizationDirection const& dir, std::vector& x, std::vector const* b, uint64_t n, - SolutionType factor) const { + SolutionType factor, + UncertaintyResolutionMode const& uncertaintyResolutionMode) 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](SolutionType& c) { return c * factor; }); - multiplyAndReduce(env, dir, x, b, x); + multiplyAndReduce(env, dir, x, b, x, uncertaintyResolutionMode); if (storm::utility::resources::isTerminate()) { STORM_LOG_WARN("Aborting after " << i << " of " << n << " multiplications"); break; diff --git a/src/storm/solver/multiplier/Multiplier.h b/src/storm/solver/multiplier/Multiplier.h index 3a8bccd009..b9c8e6a244 100644 --- a/src/storm/solver/multiplier/Multiplier.h +++ b/src/storm/solver/multiplier/Multiplier.h @@ -5,6 +5,7 @@ #include "storm/solver/MultiplicationStyle.h" #include "storm/solver/OptimizationDirection.h" +#include "storm/solver/UncertaintyResolutionMode.h" namespace storm { @@ -65,12 +66,15 @@ class Multiplier { * to the number of rows of A. * @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. + * @param uncertaintyResolutionMode The mode according to which to resolve uncertainty in the reduction step. * @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; + std::vector& result, UncertaintyResolutionMode const& uncertaintyResolutionMode = UncertaintyResolutionMode::Unset, + 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, + UncertaintyResolutionMode const& uncertaintyResolutionMode = UncertaintyResolutionMode::Unset, std::vector* choices = nullptr) const = 0; /*! @@ -119,9 +123,10 @@ 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. * @param n The number of times to perform the multiplication. + * @param uncertaintyResolutionMode The mode according to which to resolve uncertainty in the reduction step. */ void repeatedMultiplyAndReduce(Environment const& env, OptimizationDirection const& dir, std::vector& x, std::vector const* b, - uint64_t n) const; + uint64_t n, UncertaintyResolutionMode const& uncertaintyResolutionMode = UncertaintyResolutionMode::Unset) const; /*! * Performs repeated matrix-vector multiplication x' = A*(factor * x) + b. Vector x is scaled by factor in each iteration. * @@ -149,10 +154,12 @@ 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. * @param n The number of times to perform the multiplication. + * @param uncertaintyResolutionMode The mode according to which to resolve uncertainty in the reduction step. * @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, SolutionType factor) const; + std::vector const* b, uint64_t n, SolutionType factor, + UncertaintyResolutionMode const& uncertaintyResolutionMode = UncertaintyResolutionMode::Unset) const; protected: std::vector& provideCachedVector(uint64_t size) const; diff --git a/src/storm/solver/multiplier/NativeMultiplier.cpp b/src/storm/solver/multiplier/NativeMultiplier.cpp index 876f7eea6e..50b41b559b 100644 --- a/src/storm/solver/multiplier/NativeMultiplier.cpp +++ b/src/storm/solver/multiplier/NativeMultiplier.cpp @@ -4,6 +4,8 @@ #include "storm/adapters/RationalFunctionAdapter.h" #include "storm/adapters/RationalNumberAdapter.h" #include "storm/environment/solver/MultiplierEnvironment.h" +#include "storm/exceptions/NotSupportedException.h" +#include "storm/solver/UncertaintyResolutionMode.h" #include "storm/storage/SparseMatrix.h" #include "storm/utility/macros.h" @@ -41,7 +43,10 @@ void NativeMultiplier::multiplyGaussSeidel(Environment const& env, st template void NativeMultiplier::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 { + UncertaintyResolutionMode const& uncertaintyResolutionMode, std::vector* choices) const { + STORM_LOG_THROW(uncertaintyResolutionMode == UncertaintyResolutionMode::Unset, storm::exceptions::NotSupportedException, + "Uncertainty resolution modes other than 'Unset' are not supported by the native multiplier."); + std::vector* target = &result; if (&x == &result) { target = &this->provideCachedVector(x.size()); diff --git a/src/storm/solver/multiplier/NativeMultiplier.h b/src/storm/solver/multiplier/NativeMultiplier.h index ad722fe89c..fbecd2e861 100644 --- a/src/storm/solver/multiplier/NativeMultiplier.h +++ b/src/storm/solver/multiplier/NativeMultiplier.h @@ -23,6 +23,7 @@ class NativeMultiplier : public Multiplier { 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, + UncertaintyResolutionMode const& uncertaintyResolutionMode = UncertaintyResolutionMode::Unset, 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, diff --git a/src/storm/solver/multiplier/ViOperatorMultiplier.cpp b/src/storm/solver/multiplier/ViOperatorMultiplier.cpp index 39b5ca17da..59af83eb62 100644 --- a/src/storm/solver/multiplier/ViOperatorMultiplier.cpp +++ b/src/storm/solver/multiplier/ViOperatorMultiplier.cpp @@ -3,6 +3,7 @@ #include "storm/adapters/IntervalAdapter.h" #include "storm/adapters/RationalNumberAdapter.h" #include "storm/exceptions/NotSupportedException.h" +#include "storm/solver/OptimizationDirection.h" #include "storm/solver/helper/ValueIterationOperator.h" #include "storm/storage/SparseMatrix.h" #include "storm/utility/Extremum.h" @@ -213,47 +214,57 @@ void ViOperatorMultiplier::multiply std::vector const& rowGroupIndices, std::vector const& x, std::vector const* b, std::vector& result, + UncertaintyResolutionMode const& uncertaintyResolutionMode, std::vector* choices) const { if (&result == &x) { auto& tmpResult = this->provideCachedVector(x.size()); - multiplyAndReduce(env, dir, rowGroupIndices, x, b, tmpResult, choices); + multiplyAndReduce(env, dir, rowGroupIndices, x, b, tmpResult, uncertaintyResolutionMode, choices); std::swap(result, tmpResult); return; } 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, OptimizationDirection od) { - if (od == OptimizationDirection::Minimize) { - if (b) { - viOp.template applyRobust(x, result, *b, backend); + + auto applyRobustDirection = [&](BT& backend, OffsetType const& offset) { + bool robustUncertainty = false; + if constexpr (storm::IsIntervalType) { + robustUncertainty = isUncertaintyResolvedRobust(uncertaintyResolutionMode, dir); + } + + if (robustUncertainty) { + viOp.template applyRobust(x, result, offset, backend); + } else { + viOp.template applyRobust(x, result, offset, backend); + } + }; + + auto applyBackend = [&](OffsetType const& offset) { + if (storm::solver::minimize(dir)) { + if (choices) { + detail::MultiplierBackend backend(*choices, + this->matrix.getRowGroupIndices()); + applyRobustDirection.template operator()(backend, offset); } else { - viOp.template applyRobust(x, result, storm::utility::zero(), backend); + detail::MultiplierBackend backend; + applyRobustDirection.template operator()(backend, offset); } } else { - if (b) { - viOp.template applyRobust(x, result, *b, backend); + if (choices) { + detail::MultiplierBackend backend(*choices, + this->matrix.getRowGroupIndices()); + applyRobustDirection.template operator()(backend, offset); } else { - viOp.template applyRobust(x, result, storm::utility::zero(), backend); + detail::MultiplierBackend backend; + applyRobustDirection.template operator()(backend, offset); } } }; - if (storm::solver::minimize(dir)) { - if (choices) { - detail::MultiplierBackend backend(*choices, this->matrix.getRowGroupIndices()); - apply(backend, OptimizationDirection::Minimize); - } else { - detail::MultiplierBackend backend; - apply(backend, OptimizationDirection::Minimize); - } + + if (b) { + applyBackend(*b); } else { - if (choices) { - detail::MultiplierBackend backend(*choices, this->matrix.getRowGroupIndices()); - apply(backend, OptimizationDirection::Maximize); - } else { - detail::MultiplierBackend backend; - apply(backend, OptimizationDirection::Maximize); - } + applyBackend(storm::utility::zero()); } } diff --git a/src/storm/solver/multiplier/ViOperatorMultiplier.h b/src/storm/solver/multiplier/ViOperatorMultiplier.h index daeae609d3..983e02e573 100644 --- a/src/storm/solver/multiplier/ViOperatorMultiplier.h +++ b/src/storm/solver/multiplier/ViOperatorMultiplier.h @@ -26,6 +26,7 @@ class ViOperatorMultiplier : public Multiplier { 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, + UncertaintyResolutionMode const& uncertaintyResolutionMode = UncertaintyResolutionMode::Unset, 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, diff --git a/src/test/storm/modelchecker/prctl/mdp/RobustMdpPrctlModelCheckerTest.cpp b/src/test/storm/modelchecker/prctl/mdp/RobustMdpPrctlModelCheckerTest.cpp index 6c6eb17ff1..8756b7f1d2 100644 --- a/src/test/storm/modelchecker/prctl/mdp/RobustMdpPrctlModelCheckerTest.cpp +++ b/src/test/storm/modelchecker/prctl/mdp/RobustMdpPrctlModelCheckerTest.cpp @@ -262,18 +262,18 @@ void makeUncertainAndCheckRational(std::string const& path, std::string const& f EXPECT_LE(certainValue, maxValue); } +// TODO: Add next properties + TEST(RobustMDPModelCheckingTest, Tiny01maxmin) { checkModel(STORM_TEST_RESOURCES_DIR "/imdp/tiny-01.drn", "Pmax=? [ F \"target\"];Pmin=? [ F \"target\"]", 0.4, 0.5, 0.5, 0.4, false); } TEST(RobustMDPModelCheckingTest, Tiny03maxmin) { - checkModel(STORM_TEST_RESOURCES_DIR "/imdp/tiny-03.drn", "Pmax=? [ F \"target\"];Pmin=? [ F \"target\"]", 0.4, 0.5, 0.5, 0.4, true); + checkModel(STORM_TEST_RESOURCES_DIR "/imdp/tiny-03.drn", "Pmax=? [ F \"target\"];Pmin=? [ F \"target\"]", 0.4, 0.6, 0.5, 0.3, true); } TEST(RobustMDPModelCheckingTest, BoundedTiny03maxmin) { - checkModel(STORM_TEST_RESOURCES_DIR "/imdp/tiny-03.drn", "Pmax=? [ F<=3 \"target\"];Pmin=? [ F<=3 \"target\"]", 0.4, 0.4, 0.5, 0.5, true); - checkModelRational(STORM_TEST_RESOURCES_DIR "/imdp/tiny-03.drn", "Pmax=? [ F<=3 \"target\"];Pmin=? [ F<=3 \"target\"]", storm::RationalNumber("2/5"), - storm::RationalNumber("2/5"), storm::RationalNumber("1/2"), storm::RationalNumber("1/2"), true); + checkModel(STORM_TEST_RESOURCES_DIR "/imdp/tiny-03.drn", "Pmax=? [ F<=3 \"target\"];Pmin=? [ F<=3 \"target\"]", 0.4, 0.6, 0.5, 0.3, true); } TEST(RobustMDPModelCheckingTest, Tiny04maxmin) { @@ -297,12 +297,12 @@ TEST(RobustRationalMDPModelCheckingTest, Tiny01maxmin) { TEST(RobustRationalMDPModelCheckingTest, Tiny03maxmin) { checkModelRational(STORM_TEST_RESOURCES_DIR "/imdp/tiny-03.drn", "Pmax=? [ F \"target\"];Pmin=? [ F \"target\"]", storm::RationalNumber("2/5"), - storm::RationalNumber("1/2"), storm::RationalNumber("1/2"), storm::RationalNumber("2/5"), true); + storm::RationalNumber("3/5"), storm::RationalNumber("1/2"), storm::RationalNumber("3/10"), true); } TEST(RobustRationalMDPModelCheckingTest, BoundedTiny03maxmin) { checkModelRational(STORM_TEST_RESOURCES_DIR "/imdp/tiny-03.drn", "Pmax=? [ F<=3 \"target\"];Pmin=? [ F<=3 \"target\"]", storm::RationalNumber("2/5"), - storm::RationalNumber("2/5"), storm::RationalNumber("1/2"), storm::RationalNumber("1/2"), true); + storm::RationalNumber("3/5"), storm::RationalNumber("1/2"), storm::RationalNumber("3/10"), true); } TEST(RobustRationalMDPModelCheckingTest, Tiny04maxmin) { diff --git a/src/test/storm/solver/MultiplierTest.cpp b/src/test/storm/solver/MultiplierTest.cpp index 693ad44f05..1933065ce5 100644 --- a/src/test/storm/solver/MultiplierTest.cpp +++ b/src/test/storm/solver/MultiplierTest.cpp @@ -123,4 +123,6 @@ TYPED_TEST(MultiplierTest, repeatedMultiplyAndReduceTest) { EXPECT_NEAR(x[0], this->parseNumber("0.923808265834023387639"), this->precision()); } +// TODO: Write test using the uncertainty resolution mode when more modes than 'Unset' are supported by the native multiplier. + } // namespace \ No newline at end of file From 1496050acf4b9a1f325e37d1a055454812b10b7b Mon Sep 17 00:00:00 2001 From: Luko van der Maas Date: Thu, 9 Apr 2026 09:31:28 +0200 Subject: [PATCH 31/51] Add support for RationalInterval in DirectEncodingExporter --- src/storm/io/DirectEncodingExporter.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/storm/io/DirectEncodingExporter.cpp b/src/storm/io/DirectEncodingExporter.cpp index 26679ca7f0..2f21aede6f 100644 --- a/src/storm/io/DirectEncodingExporter.cpp +++ b/src/storm/io/DirectEncodingExporter.cpp @@ -329,6 +329,9 @@ template void explicitExportSparseModel(std::filesystem template void explicitExportSparseModel(std::filesystem::path const& os, std::shared_ptr> sparseModel, std::vector const& parameters, DirectEncodingExporterOptions const& options); +template void explicitExportSparseModel(std::filesystem::path const& 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); From 9b6f4b334260cca54beca2e934f0a4342d9978d4 Mon Sep 17 00:00:00 2001 From: Luko van der Maas Date: Fri, 17 Apr 2026 10:30:13 +0200 Subject: [PATCH 32/51] Fix merge --- .../modelchecker/prctl/helper/SparseMdpPrctlHelper.cpp | 10 ---------- .../modelchecker/prctl/helper/SparseMdpPrctlHelper.h | 6 ------ 2 files changed, 16 deletions(-) diff --git a/src/storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.cpp b/src/storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.cpp index 8d1edcbc8b..82b0edd205 100644 --- a/src/storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.cpp +++ b/src/storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.cpp @@ -3,7 +3,6 @@ #include "storm/adapters/IntervalAdapter.h" #include "storm/environment/solver/MinMaxSolverEnvironment.h" #include "storm/exceptions/IllegalArgumentException.h" -#include "storm/exceptions/IllegalFunctionCallException.h" #include "storm/exceptions/InvalidPropertyException.h" #include "storm/exceptions/InvalidSettingsException.h" #include "storm/exceptions/NotSupportedException.h" @@ -1089,15 +1088,6 @@ std::vector SparseMdpPrctlHelper::compute } } -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; diff --git a/src/storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.h b/src/storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.h index 4bac15e928..eeb696c6b4 100644 --- a/src/storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.h +++ b/src/storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.h @@ -101,12 +101,6 @@ 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, From 924c4bb9257cff025d76a96928d4ce125da63b82 Mon Sep 17 00:00:00 2001 From: Luko van der Maas Date: Fri, 17 Apr 2026 10:42:37 +0200 Subject: [PATCH 33/51] fix merge 2 --- src/storm-parsers/parser/ValueParser.cpp | 45 ------------------- .../settings/PomdpSettings.cpp | 2 - .../GenerateMonitorVerifierSettings.cpp | 13 ------ .../modules/GenerateMonitorVerifierSettings.h | 21 --------- .../NondeterministicBeliefTracker.cpp | 1 - .../transformer/ObservationTraceUnfolder.h | 2 +- .../prctl/SparseMdpPrctlModelChecker.cpp | 1 - .../results/ExplicitQualitativeCheckResult.h | 1 - .../mdp/RobustMdpPrctlModelCheckerTest.cpp | 7 --- src/test/storm/solver/MultiplierTest.cpp | 2 - 10 files changed, 1 insertion(+), 94 deletions(-) delete mode 100644 src/storm-pomdp-cli/settings/modules/GenerateMonitorVerifierSettings.cpp delete mode 100644 src/storm-pomdp-cli/settings/modules/GenerateMonitorVerifierSettings.h diff --git a/src/storm-parsers/parser/ValueParser.cpp b/src/storm-parsers/parser/ValueParser.cpp index 7a482119d3..bd0e7641c4 100644 --- a/src/storm-parsers/parser/ValueParser.cpp +++ b/src/storm-parsers/parser/ValueParser.cpp @@ -121,51 +121,6 @@ bool parseInterval(std::string const& value, IntervalType& 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) { diff --git a/src/storm-pomdp-cli/settings/PomdpSettings.cpp b/src/storm-pomdp-cli/settings/PomdpSettings.cpp index be6c2f48cd..7c7cdeb4fa 100644 --- a/src/storm-pomdp-cli/settings/PomdpSettings.cpp +++ b/src/storm-pomdp-cli/settings/PomdpSettings.cpp @@ -30,7 +30,6 @@ #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" @@ -69,7 +68,6 @@ 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 deleted file mode 100644 index e2baab8be1..0000000000 --- a/src/storm-pomdp-cli/settings/modules/GenerateMonitorVerifierSettings.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#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 deleted file mode 100644 index 08ad112aba..0000000000 --- a/src/storm-pomdp-cli/settings/modules/GenerateMonitorVerifierSettings.h +++ /dev/null @@ -1,21 +0,0 @@ -#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/generator/NondeterministicBeliefTracker.cpp b/src/storm-pomdp/generator/NondeterministicBeliefTracker.cpp index a2a2d45df2..46eb624827 100644 --- a/src/storm-pomdp/generator/NondeterministicBeliefTracker.cpp +++ b/src/storm-pomdp/generator/NondeterministicBeliefTracker.cpp @@ -237,7 +237,6 @@ 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; diff --git a/src/storm-pomdp/transformer/ObservationTraceUnfolder.h b/src/storm-pomdp/transformer/ObservationTraceUnfolder.h index a03e8b1452..3934106434 100644 --- a/src/storm-pomdp/transformer/ObservationTraceUnfolder.h +++ b/src/storm-pomdp/transformer/ObservationTraceUnfolder.h @@ -47,7 +47,7 @@ class ObservationTraceUnfolder { bool isRestartSemanticsSet() const; private: - storm::models::sparse::Pomdp model; + storm::models::sparse::Pomdp const& model; std::vector risk; // TODO reconsider holding this as a reference, but there were some strange bugs std::shared_ptr& exprManager; std::vector traceSoFar; diff --git a/src/storm/modelchecker/prctl/SparseMdpPrctlModelChecker.cpp b/src/storm/modelchecker/prctl/SparseMdpPrctlModelChecker.cpp index ad99b1dd84..a5fcc41408 100644 --- a/src/storm/modelchecker/prctl/SparseMdpPrctlModelChecker.cpp +++ b/src/storm/modelchecker/prctl/SparseMdpPrctlModelChecker.cpp @@ -1,7 +1,6 @@ #include "storm/modelchecker/prctl/SparseMdpPrctlModelChecker.h" #include "storm/adapters/IntervalAdapter.h" -#include "storm/adapters/IntervalForward.h" #include "storm/adapters/RationalFunctionAdapter.h" #include "storm/adapters/RationalNumberAdapter.h" #include "storm/exceptions/InvalidPropertyException.h" diff --git a/src/storm/modelchecker/results/ExplicitQualitativeCheckResult.h b/src/storm/modelchecker/results/ExplicitQualitativeCheckResult.h index c8a9e7e4d1..70a1053755 100644 --- a/src/storm/modelchecker/results/ExplicitQualitativeCheckResult.h +++ b/src/storm/modelchecker/results/ExplicitQualitativeCheckResult.h @@ -4,7 +4,6 @@ #include #include -#include "storm/adapters/IntervalForward.h" #include "storm/adapters/JsonForward.h" #include "storm/modelchecker/results/QualitativeCheckResult.h" #include "storm/models/sparse/StateLabeling.h" diff --git a/src/test/storm/modelchecker/prctl/mdp/RobustMdpPrctlModelCheckerTest.cpp b/src/test/storm/modelchecker/prctl/mdp/RobustMdpPrctlModelCheckerTest.cpp index fbcd5b3450..7822f7caf3 100644 --- a/src/test/storm/modelchecker/prctl/mdp/RobustMdpPrctlModelCheckerTest.cpp +++ b/src/test/storm/modelchecker/prctl/mdp/RobustMdpPrctlModelCheckerTest.cpp @@ -322,13 +322,6 @@ TEST(RobustRationalMDPModelCheckingTest, Tiny05maxmin) { storm::RationalNumber("2/5"), storm::RationalNumber("2/5"), storm::RationalNumber("3/10"), false); } -TEST(RobustRationalMDPModelCheckingTest, CrowdsQuotientIMDP) { - // Ensuring equivalent behavior when checking identical model as IDTMC and IMDP (cf. CrowdsQuotientIDTMC) - checkModelRational(STORM_TEST_RESOURCES_DIR "/imdp/crowds-quotient-3-5.drn", "Pmax=? [ F \"observe0Greater1\"]; Pmin=? [ F \"observe0Greater1\"]", - storm::RationalNumber("1383409/10000000"), storm::RationalNumber("1383409/10000000"), storm::RationalNumber("1383409/10000000"), - storm::RationalNumber("1383409/10000000"), false); -} - TEST(RobustMDPModelCheckingTest, AddUncertaintyCoin22max) { #ifndef STORM_HAVE_Z3 GTEST_SKIP() << "Z3 not available."; diff --git a/src/test/storm/solver/MultiplierTest.cpp b/src/test/storm/solver/MultiplierTest.cpp index 1933065ce5..693ad44f05 100644 --- a/src/test/storm/solver/MultiplierTest.cpp +++ b/src/test/storm/solver/MultiplierTest.cpp @@ -123,6 +123,4 @@ TYPED_TEST(MultiplierTest, repeatedMultiplyAndReduceTest) { EXPECT_NEAR(x[0], this->parseNumber("0.923808265834023387639"), this->precision()); } -// TODO: Write test using the uncertainty resolution mode when more modes than 'Unset' are supported by the native multiplier. - } // namespace \ No newline at end of file From a19eb36d0a07a85870b95e012d8709e1421029aa Mon Sep 17 00:00:00 2001 From: Luko van der Maas Date: Fri, 17 Apr 2026 10:43:42 +0200 Subject: [PATCH 34/51] One morge change reverted --- src/storm/models/sparse/Dtmc.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/storm/models/sparse/Dtmc.cpp b/src/storm/models/sparse/Dtmc.cpp index 887dceff3d..8fb6e5dd92 100644 --- a/src/storm/models/sparse/Dtmc.cpp +++ b/src/storm/models/sparse/Dtmc.cpp @@ -3,7 +3,6 @@ #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 { From 9be608b20f815576306e82aeb7ad2e65e3c4e660 Mon Sep 17 00:00:00 2001 From: Filip Macak Date: Fri, 5 Dec 2025 16:10:05 +0100 Subject: [PATCH 35/51] fixed scheduler extraction for trivial case where no target state is reachable from any condition states --- .../helper/conditional/ConditionalHelper.cpp | 28 +++++++++++++++++-- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/src/storm/modelchecker/helper/conditional/ConditionalHelper.cpp b/src/storm/modelchecker/helper/conditional/ConditionalHelper.cpp index 50476e61e8..26910f800f 100644 --- a/src/storm/modelchecker/helper/conditional/ConditionalHelper.cpp +++ b/src/storm/modelchecker/helper/conditional/ConditionalHelper.cpp @@ -1296,7 +1296,21 @@ std::unique_ptr computeConditionalProbabilities(Environment const& 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())); + if (initialStateValue == storm::utility::zero() && !normalFormData.terminalStates.get(initialState) && checkTask.isProduceSchedulersSet()) { + // we need to compute a scheduler that at least reaches the condition with non-zero probability + auto initialStateBitVector = storm::storage::BitVector(transitionMatrix.getRowGroupCount(), false); + initialStateBitVector.set(initialState, true); + auto const conditionReachResult = helper::SparseMdpPrctlHelper::computeUntilProbabilities( + env, storm::solver::SolveGoal(storm::solver::OptimizationDirection::Maximize, initialStateBitVector), transitionMatrix, transitionMatrix.transpose(true), storm::storage::BitVector(conditionStates.size(), true), conditionStates, false, true); + scheduler = std::unique_ptr>(new storm::storage::Scheduler(transitionMatrix.getRowGroupCount())); + auto stateId = 0; + for (uint64_t state = 0; state < transitionMatrix.getRowGroupCount(); ++state) { + scheduler->setChoice(conditionReachResult.scheduler->getChoice(stateId), state); + ++stateId; + } + } else { + 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"); @@ -1404,9 +1418,17 @@ std::unique_ptr computeConditionalProbabilities(Environment const& 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); + if (normalFormData.schedulerChoicesForReachingTargetStates->isChoiceSelected(state)) { + finalScheduler->setChoice(normalFormData.schedulerChoicesForReachingTargetStates->getChoice(state), state, 0); + } else { + finalScheduler->setChoice(0, state, 0); // arbitrary choice if no choice was recorded, TODO: this could be problematic for paynt? + } } else if (targetStates.get(state)) { - finalScheduler->setChoice(normalFormData.schedulerChoicesForReachingConditionStates->getChoice(state), state, 0); + if (normalFormData.schedulerChoicesForReachingConditionStates->isChoiceSelected(state)) { + finalScheduler->setChoice(normalFormData.schedulerChoicesForReachingConditionStates->getChoice(state), state, 0); + } else { + finalScheduler->setChoice(0, state, 0); // arbitrary choice if no choice was recorded, TODO: this could be problematic for paynt? + } } else { finalScheduler->setChoice(scheduler->getChoice(state), state, 0); } From 259790733fa9a62b5a41553d3a4fdc66554b1c3e Mon Sep 17 00:00:00 2001 From: Luko van der Maas Date: Fri, 17 Apr 2026 11:19:24 +0200 Subject: [PATCH 36/51] formatting --- .../helper/conditional/ConditionalHelper.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/storm/modelchecker/helper/conditional/ConditionalHelper.cpp b/src/storm/modelchecker/helper/conditional/ConditionalHelper.cpp index 26910f800f..907c7f9fb9 100644 --- a/src/storm/modelchecker/helper/conditional/ConditionalHelper.cpp +++ b/src/storm/modelchecker/helper/conditional/ConditionalHelper.cpp @@ -1301,15 +1301,18 @@ std::unique_ptr computeConditionalProbabilities(Environment const& auto initialStateBitVector = storm::storage::BitVector(transitionMatrix.getRowGroupCount(), false); initialStateBitVector.set(initialState, true); auto const conditionReachResult = helper::SparseMdpPrctlHelper::computeUntilProbabilities( - env, storm::solver::SolveGoal(storm::solver::OptimizationDirection::Maximize, initialStateBitVector), transitionMatrix, transitionMatrix.transpose(true), storm::storage::BitVector(conditionStates.size(), true), conditionStates, false, true); - scheduler = std::unique_ptr>(new storm::storage::Scheduler(transitionMatrix.getRowGroupCount())); + env, storm::solver::SolveGoal(storm::solver::OptimizationDirection::Maximize, initialStateBitVector), transitionMatrix, + transitionMatrix.transpose(true), storm::storage::BitVector(conditionStates.size(), true), conditionStates, false, true); + scheduler = + std::unique_ptr>(new storm::storage::Scheduler(transitionMatrix.getRowGroupCount())); auto stateId = 0; for (uint64_t state = 0; state < transitionMatrix.getRowGroupCount(); ++state) { scheduler->setChoice(conditionReachResult.scheduler->getChoice(stateId), state); ++stateId; } } else { - scheduler = std::unique_ptr>(new storm::storage::Scheduler(transitionMatrix.getRowGroupCount())); + scheduler = + std::unique_ptr>(new storm::storage::Scheduler(transitionMatrix.getRowGroupCount())); } STORM_LOG_DEBUG("Initial state has trivial value " << initialStateValue); } else { From 920a704e69f07abfe6ff05ab9297710ed3c1ecfc Mon Sep 17 00:00:00 2001 From: Luko van der Maas Date: Mon, 20 Apr 2026 14:08:07 +0200 Subject: [PATCH 37/51] Fix bug in scheduler generation for models with initial states not as the first state. --- .../helper/conditional/ConditionalHelper.cpp | 5 +++++ src/storm/utility/constants.cpp | 5 ----- .../mdp/ConditionalMdpPrctlModelCheckerTest.cpp | 15 +++++++++++---- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/storm/modelchecker/helper/conditional/ConditionalHelper.cpp b/src/storm/modelchecker/helper/conditional/ConditionalHelper.cpp index 907c7f9fb9..980cf9c252 100644 --- a/src/storm/modelchecker/helper/conditional/ConditionalHelper.cpp +++ b/src/storm/modelchecker/helper/conditional/ConditionalHelper.cpp @@ -680,6 +680,11 @@ class WeightedReachabilityHelper { initialStateInSubmatrix = originalToReducedStateIndexMap[initialState]; auto const eliminatedInitialComponentStates = normalForm.maybeStates & ~subMatrixRowGroups; + // Inital component states do not have the correct mapping yet. + for (auto state : eliminatedInitialComponentStates) { + originalToReducedStateIndexMap[state] = initialStateInSubmatrix; + } + // build matrix, rows that sum up to 1, target values, condition values storm::storage::SparseMatrixBuilder matrixBuilder(numSubmatrixRows, numSubmatrixRowGroups, 0, true, true, numSubmatrixRowGroups); rowsWithSum1 = storm::storage::BitVector(numSubmatrixRows, true); diff --git a/src/storm/utility/constants.cpp b/src/storm/utility/constants.cpp index 48bfa1d536..aafd3d489f 100644 --- a/src/storm/utility/constants.cpp +++ b/src/storm/utility/constants.cpp @@ -37,11 +37,6 @@ 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(); } diff --git a/src/test/storm/modelchecker/prctl/mdp/ConditionalMdpPrctlModelCheckerTest.cpp b/src/test/storm/modelchecker/prctl/mdp/ConditionalMdpPrctlModelCheckerTest.cpp index 572be8909f..f8faef6e26 100644 --- a/src/test/storm/modelchecker/prctl/mdp/ConditionalMdpPrctlModelCheckerTest.cpp +++ b/src/test/storm/modelchecker/prctl/mdp/ConditionalMdpPrctlModelCheckerTest.cpp @@ -119,7 +119,7 @@ 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 + env.solver().minMax().setPrecision(storm::utility::zero()); // TODO: this should not be necessary return env; } }; @@ -144,7 +144,7 @@ class SparseRationalNumberBisectionAdvancedPtEnvironment { 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 + env.solver().minMax().setPrecision(storm::utility::zero()); // TODO: this should not be necessary return env; } }; @@ -253,8 +253,15 @@ TYPED_TEST(ConditionalMdpPrctlModelCheckerTest, two_dice) { EXPECT_NEAR(this->parseNumber("0"), result[*mdp->getInitialStates().begin()], this->precision()); result = checker.check(this->env(), tasks[5])->template asExplicitQuantitativeCheckResult(); EXPECT_NEAR(this->parseNumber("0"), result[*mdp->getInitialStates().begin()], this->precision()); - result = checker.check(this->env(), tasks[6])->template asExplicitQuantitativeCheckResult(); - EXPECT_NEAR(this->parseNumber("1"), result[*mdp->getInitialStates().begin()], this->precision()); + + if constexpr (std::is_same_v) { + // Non sound model checking this property with advanced bisection leads to incorrect pMax bound, resulting in inconsistent bisection bounds. + EXPECT_DEATH_IF_SUPPORTED(checker.check(this->env(), tasks[6]), "Bisection method bounds are inconsistent"); + } else { + result = checker.check(this->env(), tasks[6])->template asExplicitQuantitativeCheckResult(); + EXPECT_NEAR(this->parseNumber("1"), result[*mdp->getInitialStates().begin()], this->precision()); + } + result = checker.check(this->env(), tasks[7])->template asExplicitQuantitativeCheckResult(); EXPECT_NEAR(this->parseNumber("1"), result[*mdp->getInitialStates().begin()], this->precision()); } From 9f193a5ac6fc228dc5fc69dd477ec315849092fd Mon Sep 17 00:00:00 2001 From: Luko van der Maas Date: Mon, 20 Apr 2026 16:30:00 +0200 Subject: [PATCH 38/51] Fix tests and add conditional sub env --- src/storm/environment/SubEnvironment.cpp | 1 + .../AllModelCheckerEnvironments.h | 1 + .../modelchecker/ModelCheckerEnvironment.cpp | 37 ++++------------ .../modelchecker/ModelCheckerEnvironment.h | 21 +++------ .../helper/conditional/ConditionalHelper.cpp | 44 +++++++++---------- .../ConditionalMdpPrctlModelCheckerTest.cpp | 29 +++++++----- 6 files changed, 55 insertions(+), 78 deletions(-) diff --git a/src/storm/environment/SubEnvironment.cpp b/src/storm/environment/SubEnvironment.cpp index ab2a7afbd6..d187483e1b 100644 --- a/src/storm/environment/SubEnvironment.cpp +++ b/src/storm/environment/SubEnvironment.cpp @@ -48,6 +48,7 @@ void SubEnvironment::assertInitialized() const { template class SubEnvironment; +template class SubEnvironment; template class SubEnvironment; template class SubEnvironment; diff --git a/src/storm/environment/modelchecker/AllModelCheckerEnvironments.h b/src/storm/environment/modelchecker/AllModelCheckerEnvironments.h index f37921b606..30a6735b27 100644 --- a/src/storm/environment/modelchecker/AllModelCheckerEnvironments.h +++ b/src/storm/environment/modelchecker/AllModelCheckerEnvironments.h @@ -1,4 +1,5 @@ #pragma once +#include "storm/environment/modelchecker/ConditionalModelCheckerEnvironment.h" #include "storm/environment/modelchecker/ModelCheckerEnvironment.h" #include "storm/environment/modelchecker/MultiObjectiveModelCheckerEnvironment.h" \ No newline at end of file diff --git a/src/storm/environment/modelchecker/ModelCheckerEnvironment.cpp b/src/storm/environment/modelchecker/ModelCheckerEnvironment.cpp index d0ecb5c347..bed60a93bc 100644 --- a/src/storm/environment/modelchecker/ModelCheckerEnvironment.cpp +++ b/src/storm/environment/modelchecker/ModelCheckerEnvironment.cpp @@ -1,5 +1,6 @@ #include "storm/environment/modelchecker/ModelCheckerEnvironment.h" +#include "storm/environment/modelchecker/ConditionalModelCheckerEnvironment.h" #include "storm/environment/modelchecker/MultiObjectiveModelCheckerEnvironment.h" #include "storm/settings/SettingsManager.h" @@ -19,46 +20,26 @@ ModelCheckerEnvironment::ModelCheckerEnvironment() { } auto const& ioSettings = storm::settings::getModule(); steadyStateDistributionAlgorithm = ioSettings.getSteadyStateDistributionAlgorithm(); - - conditionalAlgorithmSetting = mcSettings.getConditionalAlgorithmSetting(); - conditionalToleranceSetting = mcSettings.getConditionalTolerance(); - allowOptimizationForBoundedProperties = true; } ModelCheckerEnvironment::~ModelCheckerEnvironment() { // Intentionally left empty } -SteadyStateDistributionAlgorithm ModelCheckerEnvironment::getSteadyStateDistributionAlgorithm() const { - return steadyStateDistributionAlgorithm; -} - -void ModelCheckerEnvironment::setSteadyStateDistributionAlgorithm(SteadyStateDistributionAlgorithm value) { - steadyStateDistributionAlgorithm = value; -} - -ConditionalAlgorithmSetting ModelCheckerEnvironment::getConditionalAlgorithmSetting() const { - return conditionalAlgorithmSetting; +ConditionalModelCheckerEnvironment& ModelCheckerEnvironment::conditional() { + return conditionalModelCheckerEnvironment.get(); } -void ModelCheckerEnvironment::setConditionalAlgorithmSetting(ConditionalAlgorithmSetting value) { - conditionalAlgorithmSetting = value; +ConditionalModelCheckerEnvironment const& ModelCheckerEnvironment::conditional() const { + return conditionalModelCheckerEnvironment.get(); } -void ModelCheckerEnvironment::setConditionalTolerance(storm::RationalNumber const& value) { - conditionalToleranceSetting = value; -} - -storm::RationalNumber ModelCheckerEnvironment::getConditionalTolerance() const { - return conditionalToleranceSetting; -} - -bool ModelCheckerEnvironment::isAllowOptimizationForBoundedPropertiesSet() const { - return allowOptimizationForBoundedProperties; +SteadyStateDistributionAlgorithm ModelCheckerEnvironment::getSteadyStateDistributionAlgorithm() const { + return steadyStateDistributionAlgorithm; } -void ModelCheckerEnvironment::setAllowOptimizationForBoundedProperties(bool value) { - allowOptimizationForBoundedProperties = value; +void ModelCheckerEnvironment::setSteadyStateDistributionAlgorithm(SteadyStateDistributionAlgorithm value) { + steadyStateDistributionAlgorithm = value; } MultiObjectiveModelCheckerEnvironment& ModelCheckerEnvironment::multi() { diff --git a/src/storm/environment/modelchecker/ModelCheckerEnvironment.h b/src/storm/environment/modelchecker/ModelCheckerEnvironment.h index b011b37a0b..983d3d37a8 100644 --- a/src/storm/environment/modelchecker/ModelCheckerEnvironment.h +++ b/src/storm/environment/modelchecker/ModelCheckerEnvironment.h @@ -1,18 +1,16 @@ #pragma once #include -#include #include -#include "storm/adapters/RationalNumberAdapter.h" #include "storm/environment/Environment.h" #include "storm/environment/SubEnvironment.h" -#include "storm/modelchecker/helper/conditional/ConditionalAlgorithmSetting.h" #include "storm/modelchecker/helper/infinitehorizon/SteadyStateDistributionAlgorithm.h" namespace storm { // Forward declare subenvironments +class ConditionalModelCheckerEnvironment; class MultiObjectiveModelCheckerEnvironment; class ModelCheckerEnvironment { @@ -20,33 +18,24 @@ class ModelCheckerEnvironment { ModelCheckerEnvironment(); ~ModelCheckerEnvironment(); + ConditionalModelCheckerEnvironment& conditional(); + ConditionalModelCheckerEnvironment const& conditional() const; + MultiObjectiveModelCheckerEnvironment& multi(); MultiObjectiveModelCheckerEnvironment const& multi() const; SteadyStateDistributionAlgorithm getSteadyStateDistributionAlgorithm() const; void setSteadyStateDistributionAlgorithm(SteadyStateDistributionAlgorithm value); - 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); void unsetLtl2daTool(); private: + SubEnvironment conditionalModelCheckerEnvironment; SubEnvironment multiObjectiveModelCheckerEnvironment; boost::optional ltl2daTool; SteadyStateDistributionAlgorithm steadyStateDistributionAlgorithm; - ConditionalAlgorithmSetting conditionalAlgorithmSetting; - bool allowOptimizationForBoundedProperties; - storm::RationalNumber conditionalToleranceSetting; }; } // namespace storm diff --git a/src/storm/modelchecker/helper/conditional/ConditionalHelper.cpp b/src/storm/modelchecker/helper/conditional/ConditionalHelper.cpp index 980cf9c252..737d421fe5 100644 --- a/src/storm/modelchecker/helper/conditional/ConditionalHelper.cpp +++ b/src/storm/modelchecker/helper/conditional/ConditionalHelper.cpp @@ -4,6 +4,7 @@ #include #include "storm/adapters/RationalNumberAdapter.h" +#include "storm/environment/modelchecker/ConditionalModelCheckerEnvironment.h" #include "storm/environment/modelchecker/ModelCheckerEnvironment.h" #include "storm/environment/solver/MinMaxSolverEnvironment.h" #include "storm/exceptions/NotImplementedException.h" @@ -99,7 +100,7 @@ SolutionType solveMinMaxEquationSystem(storm::Environment const& env, storm::sto 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()) { + if (goal.isBounded() && env.modelchecker().conditional().isAllowOptimizationForBoundedPropertiesSet()) { return {goal.direction(), goal.boundComparisonType(), goal.thresholdValue(), relevantValues}; } else { return {goal.direction(), relevantValues}; @@ -518,8 +519,8 @@ typename internal::ResultReturnType computeViaRestartMethod(Environme initStateInReduced = ecElimResult2->oldToNewStateMapping[initStateInReduced]; } - STORM_PRINT_AND_LOG("Processed model has " << matrix.getRowGroupCount() << " states and " << matrix.getRowGroupCount() << " choices and " - << matrix.getEntryCount() << " transitions."); + STORM_LOG_INFO("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; @@ -749,8 +750,8 @@ class WeightedReachabilityHelper { 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.")); + STORM_LOG_INFO("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 @@ -974,7 +975,7 @@ typename internal::ResultReturnType computeViaBisection(Environment c // We currently handle sound model checking incorrectly: we would need the actual lower/upper bounds of the weightedReachabilityHelper 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()); + return storm::utility::convertNumber(env.modelchecker().conditional().getTolerance()); }(); 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."); @@ -1064,12 +1065,12 @@ typename internal::ResultReturnType computeViaBisection(Environment c << "."); } if (boundDiff <= (relative ? (precision * *lowerBound) : precision)) { - STORM_PRINT_AND_LOG("Bisection method converged after " << iterationCount << " iterations. Difference is " - << std::setprecision(std::numeric_limits::digits10) - << storm::utility::convertNumber(boundDiff) << "."); + STORM_LOG_INFO("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."); + STORM_LOG_INFO("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; @@ -1078,16 +1079,16 @@ typename internal::ResultReturnType computeViaBisection(Environment c } // 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()) << "."); + STORM_LOG_INFO("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_PRINT_AND_LOG("Bisection solver aborted after " << iterationCount << "iterations. Bound difference is " - << storm::utility::convertNumber(boundDiff) << "."); + STORM_LOG_INFO("Bisection solver aborted after " << iterationCount << "iterations. Bound difference is " + << storm::utility::convertNumber(boundDiff) << "."); break; } // process the middle value for the next iteration @@ -1294,7 +1295,7 @@ std::unique_ptr computeConditionalProbabilities(Environment const& 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"); + // STORM_LOG_INFO("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(); @@ -1322,12 +1323,11 @@ std::unique_ptr computeConditionalProbabilities(Environment const& 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"); - auto alg = analysisEnv.modelchecker().getConditionalAlgorithmSetting(); + auto alg = analysisEnv.modelchecker().conditional().getAlgorithm(); if (alg == ConditionalAlgorithmSetting::Default) { alg = ConditionalAlgorithmSetting::Restart; } - STORM_PRINT_AND_LOG("Analyzing normal form with " << normalFormData.maybeStates.getNumberOfSetBits() << " maybe states using algorithm '" << alg - << "."); + STORM_LOG_INFO("Analyzing normal form with " << normalFormData.maybeStates.getNumberOfSetBits() << " maybe states using algorithm '" << alg << "."); // sw.restart(); internal::ResultReturnType result{storm::utility::zero()}; switch (alg) { @@ -1340,7 +1340,7 @@ std::unique_ptr computeConditionalProbabilities(Environment const& case ConditionalAlgorithmSetting::BisectionAdvanced: case ConditionalAlgorithmSetting::BisectionPolicyTracking: case ConditionalAlgorithmSetting::BisectionAdvancedPolicyTracking: { - if (goal.isBounded() && env.modelchecker().isAllowOptimizationForBoundedPropertiesSet()) { + if (goal.isBounded() && env.modelchecker().conditional().isAllowOptimizationForBoundedPropertiesSet()) { result = internal::decideThreshold(analysisEnv, initialState, goal.direction(), goal.thresholdValue(), checkTask.isProduceSchedulersSet(), transitionMatrix, backwardTransitions, normalFormData); } else { @@ -1361,7 +1361,7 @@ std::unique_ptr computeConditionalProbabilities(Environment const& scheduler = std::move(result.scheduler); // sw.stop(); - // STORM_PRINT_AND_LOG("Time for analyzing the normal form: " << sw << ".\n"); + // STORM_LOG_INFO("Time for analyzing the normal form: " << sw << ".\n"); } std::unique_ptr result(new ExplicitQuantitativeCheckResult(initialState, initialStateValue)); diff --git a/src/test/storm/modelchecker/prctl/mdp/ConditionalMdpPrctlModelCheckerTest.cpp b/src/test/storm/modelchecker/prctl/mdp/ConditionalMdpPrctlModelCheckerTest.cpp index f8faef6e26..47f1190f41 100644 --- a/src/test/storm/modelchecker/prctl/mdp/ConditionalMdpPrctlModelCheckerTest.cpp +++ b/src/test/storm/modelchecker/prctl/mdp/ConditionalMdpPrctlModelCheckerTest.cpp @@ -5,6 +5,7 @@ #include "storm-parsers/parser/PrismParser.h" #include "storm/api/builder.h" #include "storm/api/properties.h" +#include "storm/environment/modelchecker/ConditionalModelCheckerEnvironment.h" #include "storm/environment/modelchecker/ModelCheckerEnvironment.h" #include "storm/environment/solver/MinMaxSolverEnvironment.h" #include "storm/modelchecker/prctl/SparseMdpPrctlModelChecker.h" @@ -20,7 +21,7 @@ class SparseDoubleRestartEnvironment { typedef storm::models::sparse::Mdp ModelType; static storm::Environment createEnvironment() { storm::Environment env; - env.modelchecker().setConditionalAlgorithmSetting(storm::ConditionalAlgorithmSetting::Restart); + env.modelchecker().conditional().setAlgorithm(storm::ConditionalAlgorithmSetting::Restart); env.solver().minMax().setPrecision(storm::utility::convertNumber(1e-10)); // restart algorithm requires a higher precision return env; } @@ -33,7 +34,7 @@ class SparseDoubleBisectionEnvironment { typedef storm::models::sparse::Mdp ModelType; static storm::Environment createEnvironment() { storm::Environment env; - env.modelchecker().setConditionalAlgorithmSetting(storm::ConditionalAlgorithmSetting::Bisection); + env.modelchecker().conditional().setAlgorithm(storm::ConditionalAlgorithmSetting::Bisection); return env; } }; @@ -45,7 +46,7 @@ class SparseDoubleBisectionAdvancedEnvironment { typedef storm::models::sparse::Mdp ModelType; static storm::Environment createEnvironment() { storm::Environment env; - env.modelchecker().setConditionalAlgorithmSetting(storm::ConditionalAlgorithmSetting::BisectionAdvanced); + env.modelchecker().conditional().setAlgorithm(storm::ConditionalAlgorithmSetting::BisectionAdvanced); return env; } }; @@ -57,7 +58,7 @@ class SparseDoubleBisectionPtEnvironment { typedef storm::models::sparse::Mdp ModelType; static storm::Environment createEnvironment() { storm::Environment env; - env.modelchecker().setConditionalAlgorithmSetting(storm::ConditionalAlgorithmSetting::BisectionPolicyTracking); + env.modelchecker().conditional().setAlgorithm(storm::ConditionalAlgorithmSetting::BisectionPolicyTracking); return env; } }; @@ -69,7 +70,7 @@ class SparseDoubleBisectionAdvancedPtEnvironment { typedef storm::models::sparse::Mdp ModelType; static storm::Environment createEnvironment() { storm::Environment env; - env.modelchecker().setConditionalAlgorithmSetting(storm::ConditionalAlgorithmSetting::BisectionAdvancedPolicyTracking); + env.modelchecker().conditional().setAlgorithm(storm::ConditionalAlgorithmSetting::BisectionAdvancedPolicyTracking); return env; } }; @@ -81,7 +82,7 @@ class SparseDoublePiEnvironment { typedef storm::models::sparse::Mdp ModelType; static storm::Environment createEnvironment() { storm::Environment env; - env.modelchecker().setConditionalAlgorithmSetting(storm::ConditionalAlgorithmSetting::PolicyIteration); + env.modelchecker().conditional().setAlgorithm(storm::ConditionalAlgorithmSetting::PolicyIteration); return env; } }; @@ -93,7 +94,7 @@ class SparseRationalNumberRestartEnvironment { typedef storm::models::sparse::Mdp ModelType; static storm::Environment createEnvironment() { storm::Environment env; - env.modelchecker().setConditionalAlgorithmSetting(storm::ConditionalAlgorithmSetting::Restart); + env.modelchecker().conditional().setAlgorithm(storm::ConditionalAlgorithmSetting::Restart); env.solver().minMax().setPrecision(storm::utility::convertNumber(1e-10)); // restart algorithm requires a higher precision return env; } @@ -106,7 +107,8 @@ class SparseRationalNumberBisectionEnvironment { typedef storm::models::sparse::Mdp ModelType; static storm::Environment createEnvironment() { storm::Environment env; - env.modelchecker().setConditionalAlgorithmSetting(storm::ConditionalAlgorithmSetting::Bisection); + env.modelchecker().conditional().setAlgorithm(storm::ConditionalAlgorithmSetting::Bisection); + env.modelchecker().conditional().setTolerance(storm::utility::zero()); return env; } }; @@ -118,7 +120,8 @@ class SparseRationalNumberBisectionAdvancedEnvironment { typedef storm::models::sparse::Mdp ModelType; static storm::Environment createEnvironment() { storm::Environment env; - env.modelchecker().setConditionalAlgorithmSetting(storm::ConditionalAlgorithmSetting::BisectionAdvanced); + env.modelchecker().conditional().setAlgorithm(storm::ConditionalAlgorithmSetting::BisectionAdvanced); + env.modelchecker().conditional().setTolerance(storm::utility::zero()); env.solver().minMax().setPrecision(storm::utility::zero()); // TODO: this should not be necessary return env; } @@ -131,7 +134,8 @@ class SparseRationalNumberBisectionPtEnvironment { typedef storm::models::sparse::Mdp ModelType; static storm::Environment createEnvironment() { storm::Environment env; - env.modelchecker().setConditionalAlgorithmSetting(storm::ConditionalAlgorithmSetting::BisectionPolicyTracking); + env.modelchecker().conditional().setAlgorithm(storm::ConditionalAlgorithmSetting::BisectionPolicyTracking); + env.modelchecker().conditional().setTolerance(storm::utility::zero()); return env; } }; @@ -143,7 +147,8 @@ class SparseRationalNumberBisectionAdvancedPtEnvironment { typedef storm::models::sparse::Mdp ModelType; static storm::Environment createEnvironment() { storm::Environment env; - env.modelchecker().setConditionalAlgorithmSetting(storm::ConditionalAlgorithmSetting::BisectionAdvancedPolicyTracking); + env.modelchecker().conditional().setAlgorithm(storm::ConditionalAlgorithmSetting::BisectionAdvancedPolicyTracking); + env.modelchecker().conditional().setTolerance(storm::utility::zero()); env.solver().minMax().setPrecision(storm::utility::zero()); // TODO: this should not be necessary return env; } @@ -156,7 +161,7 @@ class SparseRationalNumberPiEnvironment { typedef storm::models::sparse::Mdp ModelType; static storm::Environment createEnvironment() { storm::Environment env; - env.modelchecker().setConditionalAlgorithmSetting(storm::ConditionalAlgorithmSetting::PolicyIteration); + env.modelchecker().conditional().setAlgorithm(storm::ConditionalAlgorithmSetting::PolicyIteration); return env; } }; From 1d9839edca4ae48a85da355d3cb6eb25626d2cb4 Mon Sep 17 00:00:00 2001 From: Luko van der Maas Date: Mon, 20 Apr 2026 16:30:09 +0200 Subject: [PATCH 39/51] missed changes --- .../ConditionalModelCheckerEnvironment.cpp | 43 +++++++++++++++++++ .../ConditionalModelCheckerEnvironment.h | 28 ++++++++++++ 2 files changed, 71 insertions(+) create mode 100644 src/storm/environment/modelchecker/ConditionalModelCheckerEnvironment.cpp create mode 100644 src/storm/environment/modelchecker/ConditionalModelCheckerEnvironment.h diff --git a/src/storm/environment/modelchecker/ConditionalModelCheckerEnvironment.cpp b/src/storm/environment/modelchecker/ConditionalModelCheckerEnvironment.cpp new file mode 100644 index 0000000000..6e6e9474e7 --- /dev/null +++ b/src/storm/environment/modelchecker/ConditionalModelCheckerEnvironment.cpp @@ -0,0 +1,43 @@ +#include "storm/environment/modelchecker/ConditionalModelCheckerEnvironment.h" + +#include "storm/settings/SettingsManager.h" +#include "storm/settings/modules/ModelCheckerSettings.h" + +namespace storm { + +ConditionalModelCheckerEnvironment::ConditionalModelCheckerEnvironment() { + auto const& mcSettings = storm::settings::getModule(); + algorithm = mcSettings.getConditionalAlgorithmSetting(); + tolerance = mcSettings.getConditionalTolerance(); + allowOptimizationForBoundedProperties = true; +} + +ConditionalModelCheckerEnvironment::~ConditionalModelCheckerEnvironment() { + // Intentionally left empty +} + +ConditionalAlgorithmSetting ConditionalModelCheckerEnvironment::getAlgorithm() const { + return algorithm; +} + +void ConditionalModelCheckerEnvironment::setAlgorithm(ConditionalAlgorithmSetting value) { + algorithm = value; +} + +storm::RationalNumber ConditionalModelCheckerEnvironment::getTolerance() const { + return tolerance; +} + +void ConditionalModelCheckerEnvironment::setTolerance(storm::RationalNumber const& value) { + tolerance = value; +} + +bool ConditionalModelCheckerEnvironment::isAllowOptimizationForBoundedPropertiesSet() const { + return allowOptimizationForBoundedProperties; +} + +void ConditionalModelCheckerEnvironment::setAllowOptimizationForBoundedProperties(bool value) { + allowOptimizationForBoundedProperties = value; +} + +} // namespace storm diff --git a/src/storm/environment/modelchecker/ConditionalModelCheckerEnvironment.h b/src/storm/environment/modelchecker/ConditionalModelCheckerEnvironment.h new file mode 100644 index 0000000000..17708753bc --- /dev/null +++ b/src/storm/environment/modelchecker/ConditionalModelCheckerEnvironment.h @@ -0,0 +1,28 @@ +#pragma once + +#include "storm/adapters/RationalNumberAdapter.h" +#include "storm/modelchecker/helper/conditional/ConditionalAlgorithmSetting.h" + +namespace storm { + +class ConditionalModelCheckerEnvironment { + public: + ConditionalModelCheckerEnvironment(); + ~ConditionalModelCheckerEnvironment(); + + ConditionalAlgorithmSetting getAlgorithm() const; + void setAlgorithm(ConditionalAlgorithmSetting value); + + storm::RationalNumber getTolerance() const; + void setTolerance(storm::RationalNumber const& value); + + bool isAllowOptimizationForBoundedPropertiesSet() const; + void setAllowOptimizationForBoundedProperties(bool value); + + private: + ConditionalAlgorithmSetting algorithm; + storm::RationalNumber tolerance; + bool allowOptimizationForBoundedProperties; +}; + +} // namespace storm From 63f656d2642e4cac44c4daf1ee4b323446a8b0ef Mon Sep 17 00:00:00 2001 From: Luko van der Maas Date: Tue, 21 Apr 2026 10:34:47 +0200 Subject: [PATCH 40/51] Move conditional tolerance as general field to checktask --- .../ConditionalModelCheckerEnvironment.cpp | 18 ---------- .../ConditionalModelCheckerEnvironment.h | 8 ----- src/storm/modelchecker/CheckTask.h | 34 +++++++++++++++---- .../helper/conditional/ConditionalHelper.cpp | 27 +++++++-------- .../settings/modules/ModelCheckerSettings.cpp | 14 -------- .../settings/modules/ModelCheckerSettings.h | 6 ---- src/storm/utility/constants.cpp | 15 ++++++++ src/storm/utility/constants.h | 3 ++ .../ConditionalMdpPrctlModelCheckerTest.cpp | 15 ++++---- 9 files changed, 64 insertions(+), 76 deletions(-) diff --git a/src/storm/environment/modelchecker/ConditionalModelCheckerEnvironment.cpp b/src/storm/environment/modelchecker/ConditionalModelCheckerEnvironment.cpp index 6e6e9474e7..e09abd191c 100644 --- a/src/storm/environment/modelchecker/ConditionalModelCheckerEnvironment.cpp +++ b/src/storm/environment/modelchecker/ConditionalModelCheckerEnvironment.cpp @@ -8,8 +8,6 @@ namespace storm { ConditionalModelCheckerEnvironment::ConditionalModelCheckerEnvironment() { auto const& mcSettings = storm::settings::getModule(); algorithm = mcSettings.getConditionalAlgorithmSetting(); - tolerance = mcSettings.getConditionalTolerance(); - allowOptimizationForBoundedProperties = true; } ConditionalModelCheckerEnvironment::~ConditionalModelCheckerEnvironment() { @@ -24,20 +22,4 @@ void ConditionalModelCheckerEnvironment::setAlgorithm(ConditionalAlgorithmSettin algorithm = value; } -storm::RationalNumber ConditionalModelCheckerEnvironment::getTolerance() const { - return tolerance; -} - -void ConditionalModelCheckerEnvironment::setTolerance(storm::RationalNumber const& value) { - tolerance = value; -} - -bool ConditionalModelCheckerEnvironment::isAllowOptimizationForBoundedPropertiesSet() const { - return allowOptimizationForBoundedProperties; -} - -void ConditionalModelCheckerEnvironment::setAllowOptimizationForBoundedProperties(bool value) { - allowOptimizationForBoundedProperties = value; -} - } // namespace storm diff --git a/src/storm/environment/modelchecker/ConditionalModelCheckerEnvironment.h b/src/storm/environment/modelchecker/ConditionalModelCheckerEnvironment.h index 17708753bc..93e20e0111 100644 --- a/src/storm/environment/modelchecker/ConditionalModelCheckerEnvironment.h +++ b/src/storm/environment/modelchecker/ConditionalModelCheckerEnvironment.h @@ -13,16 +13,8 @@ class ConditionalModelCheckerEnvironment { ConditionalAlgorithmSetting getAlgorithm() const; void setAlgorithm(ConditionalAlgorithmSetting value); - storm::RationalNumber getTolerance() const; - void setTolerance(storm::RationalNumber const& value); - - bool isAllowOptimizationForBoundedPropertiesSet() const; - void setAllowOptimizationForBoundedProperties(bool value); - private: ConditionalAlgorithmSetting algorithm; - storm::RationalNumber tolerance; - bool allowOptimizationForBoundedProperties; }; } // namespace storm diff --git a/src/storm/modelchecker/CheckTask.h b/src/storm/modelchecker/CheckTask.h index 16cd6ca63a..94463b916c 100644 --- a/src/storm/modelchecker/CheckTask.h +++ b/src/storm/modelchecker/CheckTask.h @@ -36,8 +36,9 @@ class CheckTask { /*! * Creates a task object with the default options for the given formula. */ - CheckTask(FormulaType const& formula, bool onlyInitialStatesRelevant = false, UncertaintyResolutionMode = UncertaintyResolutionMode::Unset) - : formula(formula), hint(new ModelCheckerHint()) { + CheckTask(FormulaType const& formula, bool onlyInitialStatesRelevant = false, UncertaintyResolutionMode = UncertaintyResolutionMode::Unset, + ValueType topLevelTolerance = storm::utility::defaultTolerance()) + : formula(formula), hint(new ModelCheckerHint()), TopLevelTolerance(std::make_shared(std::move(topLevelTolerance))) { this->onlyInitialStatesRelevant = onlyInitialStatesRelevant; this->produceSchedulers = false; this->qualitative = false; @@ -54,7 +55,7 @@ class CheckTask { CheckTask substituteFormula(NewFormulaType const& newFormula) const { CheckTask result(newFormula, this->optimizationDirection, this->playerCoalition, this->rewardModel, this->onlyInitialStatesRelevant, this->bound, this->qualitative, this->produceSchedulers, this->hint, - this->uncertaintyResolutionMode); + this->uncertaintyResolutionMode, *this->TopLevelTolerance); result.updateOperatorInformation(); return result; } @@ -133,7 +134,7 @@ class CheckTask { CheckTask convertValueType() const { return CheckTask(this->formula, this->optimizationDirection, this->playerCoalition, this->rewardModel, this->onlyInitialStatesRelevant, this->bound, this->qualitative, this->produceSchedulers, this->hint, - this->uncertaintyResolutionMode); + this->uncertaintyResolutionMode, storm::utility::convertNumber(*this->TopLevelTolerance)); } /*! @@ -321,6 +322,20 @@ class CheckTask { return isSet(this->uncertaintyResolutionMode); } + /*! + * Retrieves the tolerance for the top-level model checking. + */ + ValueType const& getTopLevelTolerance() const { + return *TopLevelTolerance; + } + + /*! + * Sets the tolerance for the top-level model checking. + */ + void setTopLevelTolerance(ValueType topLevelTolerance) { + this->TopLevelTolerance = std::make_shared(std::move(topLevelTolerance)); + } + /*! * Conversion operator that strips the type of the formula. */ @@ -343,11 +358,14 @@ class CheckTask { * with bounds 0/1. * @param produceSchedulers If supported by the model checker and the model formalism, schedulers to achieve * a value will be produced if this flag is set. + * @param hint A hint that might contain information that speeds up the modelchecking process (if supported by the model checker) + * @param uncertaintyResolutionMode Whether uncertainty should be resolved be minimizing, maximizing, acting robust or cooperative. + * @param topLevelTolerance The tolerance for the top-level model checking. */ CheckTask(std::reference_wrapper const& formula, boost::optional const& optimizationDirection, boost::optional playerCoalition, boost::optional const& rewardModel, bool onlyInitialStatesRelevant, boost::optional const& bound, bool qualitative, bool produceSchedulers, std::shared_ptr const& hint, - UncertaintyResolutionMode uncertaintyResolutionMode) + UncertaintyResolutionMode uncertaintyResolutionMode, ValueType topLevelTolerance) : formula(formula), optimizationDirection(optimizationDirection), playerCoalition(playerCoalition), @@ -357,7 +375,8 @@ class CheckTask { qualitative(qualitative), produceSchedulers(produceSchedulers), hint(hint), - uncertaintyResolutionMode(uncertaintyResolutionMode) { + uncertaintyResolutionMode(uncertaintyResolutionMode), + TopLevelTolerance(std::make_shared(std::move(topLevelTolerance))) { // Intentionally left empty. } @@ -391,6 +410,9 @@ class CheckTask { // Whether uncertainty should be resolved be minimizing, maximizing, acting robust or cooperative. UncertaintyResolutionMode uncertaintyResolutionMode; + + // The tolerance for the top-level model checking. + std::shared_ptr TopLevelTolerance; }; } // namespace modelchecker diff --git a/src/storm/modelchecker/helper/conditional/ConditionalHelper.cpp b/src/storm/modelchecker/helper/conditional/ConditionalHelper.cpp index 737d421fe5..8ab015fa27 100644 --- a/src/storm/modelchecker/helper/conditional/ConditionalHelper.cpp +++ b/src/storm/modelchecker/helper/conditional/ConditionalHelper.cpp @@ -100,7 +100,7 @@ SolutionType solveMinMaxEquationSystem(storm::Environment const& env, storm::sto storm::storage::BitVector relevantValues(matrix.getRowGroupCount(), false); relevantValues.set(initialState, true); auto getGoal = [&env, &goal, &relevantValues]() -> storm::solver::SolveGoal { - if (goal.isBounded() && env.modelchecker().conditional().isAllowOptimizationForBoundedPropertiesSet()) { + if (goal.isBounded()) { return {goal.direction(), goal.boundComparisonType(), goal.thresholdValue(), relevantValues}; } else { return {goal.direction(), relevantValues}; @@ -968,15 +968,12 @@ class WeightedReachabilityHelper { template 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, + SolutionType const precision, 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 - 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().conditional().getTolerance()); - }(); 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."); @@ -1150,9 +1147,9 @@ typename internal::ResultReturnType computeViaBisection(Environment c } 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, +typename internal::ResultReturnType computeViaBisection(Environment const& env, SolutionType const precision, 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; @@ -1160,8 +1157,8 @@ typename internal::ResultReturnType computeViaBisection(Environment c "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); + return computeViaBisection(env, useAdvancedBounds, usePolicyTracking, precision, initialState, goal, computeScheduler, transitionMatrix, + backwardTransitions, normalForm); } template @@ -1340,12 +1337,12 @@ std::unique_ptr computeConditionalProbabilities(Environment const& case ConditionalAlgorithmSetting::BisectionAdvanced: case ConditionalAlgorithmSetting::BisectionPolicyTracking: case ConditionalAlgorithmSetting::BisectionAdvancedPolicyTracking: { - if (goal.isBounded() && env.modelchecker().conditional().isAllowOptimizationForBoundedPropertiesSet()) { + if (goal.isBounded()) { 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); + result = internal::computeViaBisection(analysisEnv, checkTask.getTopLevelTolerance(), alg, initialState, goal, + checkTask.isProduceSchedulersSet(), transitionMatrix, backwardTransitions, normalFormData); } break; } diff --git a/src/storm/settings/modules/ModelCheckerSettings.cpp b/src/storm/settings/modules/ModelCheckerSettings.cpp index 8a79ee6907..4b81dff52a 100644 --- a/src/storm/settings/modules/ModelCheckerSettings.cpp +++ b/src/storm/settings/modules/ModelCheckerSettings.cpp @@ -13,7 +13,6 @@ 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, @@ -35,15 +34,6 @@ ModelCheckerSettings::ModelCheckerSettings() : ModuleSettings(moduleName) { .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 { @@ -62,10 +52,6 @@ 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 ef31a110b8..fbf5e09ac8 100644 --- a/src/storm/settings/modules/ModelCheckerSettings.h +++ b/src/storm/settings/modules/ModelCheckerSettings.h @@ -45,12 +45,6 @@ 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/utility/constants.cpp b/src/storm/utility/constants.cpp index aafd3d489f..bf8f4ccaea 100644 --- a/src/storm/utility/constants.cpp +++ b/src/storm/utility/constants.cpp @@ -25,6 +25,15 @@ ValueType zero() { return ValueType(0); } +template +ValueType defaultTolerance() { + if constexpr (storm::NumberTraits::IsExact) { + return zero(); + } else { + return convertNumber(1e-6); + } +} + template ValueType infinity() { return std::numeric_limits::infinity(); @@ -1140,6 +1149,7 @@ storm::RationalInterval abs(storm::RationalInterval const& interval) { // double template double one(); template double zero(); +template double defaultTolerance(); template double infinity(); template bool isOne(double const& value); template bool isZero(double const& value); @@ -1225,6 +1235,7 @@ template double convertNumber(long const&); template storm::ClnRationalNumber one(); template NumberTraits::IntegerType one(); template storm::ClnRationalNumber zero(); +template storm::ClnRationalNumber defaultTolerance(); template bool isZero(NumberTraits::IntegerType const& value); template bool isConstant(storm::ClnRationalNumber const& value); template bool isPositive(storm::ClnRationalNumber const& value); @@ -1256,6 +1267,7 @@ template uint64_t bitsize(storm::ClnIntegerNumber const& number); template storm::GmpRationalNumber one(); template NumberTraits::IntegerType one(); template storm::GmpRationalNumber zero(); +template storm::GmpRationalNumber defaultTolerance(); template bool isZero(NumberTraits::IntegerType const& value); template bool isConstant(storm::GmpRationalNumber const& value); template bool isPositive(storm::GmpRationalNumber const& value); @@ -1284,6 +1296,7 @@ template uint64_t bitsize(storm::GmpIntegerNumber const& number); // Instantiations for rational function. template RationalFunction one(); template RationalFunction zero(); +template RationalFunction defaultTolerance(); template bool isNan(RationalFunction const&); @@ -1294,6 +1307,7 @@ template Polynomial zero(); // Instantiations for intervals. template Interval one(); template Interval zero(); +template Interval defaultTolerance(); template bool isOne(Interval const& value); template bool isZero(Interval const& value); template bool isInfinity(Interval const& value); @@ -1308,6 +1322,7 @@ template std::string to_string(storm::Interval const& value); // Instantiations for intervals. template RationalInterval one(); template RationalInterval zero(); +template RationalInterval defaultTolerance(); template bool isOne(RationalInterval const& value); template bool isZero(RationalInterval const& value); template bool isInfinity(RationalInterval const& value); diff --git a/src/storm/utility/constants.h b/src/storm/utility/constants.h index 977df3601c..5bafae7a2a 100644 --- a/src/storm/utility/constants.h +++ b/src/storm/utility/constants.h @@ -76,6 +76,9 @@ ValueType one(); template ValueType zero(); +template +ValueType defaultTolerance(); + template ValueType infinity(); diff --git a/src/test/storm/modelchecker/prctl/mdp/ConditionalMdpPrctlModelCheckerTest.cpp b/src/test/storm/modelchecker/prctl/mdp/ConditionalMdpPrctlModelCheckerTest.cpp index 47f1190f41..9e3a459976 100644 --- a/src/test/storm/modelchecker/prctl/mdp/ConditionalMdpPrctlModelCheckerTest.cpp +++ b/src/test/storm/modelchecker/prctl/mdp/ConditionalMdpPrctlModelCheckerTest.cpp @@ -108,7 +108,6 @@ class SparseRationalNumberBisectionEnvironment { static storm::Environment createEnvironment() { storm::Environment env; env.modelchecker().conditional().setAlgorithm(storm::ConditionalAlgorithmSetting::Bisection); - env.modelchecker().conditional().setTolerance(storm::utility::zero()); return env; } }; @@ -121,7 +120,6 @@ class SparseRationalNumberBisectionAdvancedEnvironment { static storm::Environment createEnvironment() { storm::Environment env; env.modelchecker().conditional().setAlgorithm(storm::ConditionalAlgorithmSetting::BisectionAdvanced); - env.modelchecker().conditional().setTolerance(storm::utility::zero()); env.solver().minMax().setPrecision(storm::utility::zero()); // TODO: this should not be necessary return env; } @@ -135,7 +133,6 @@ class SparseRationalNumberBisectionPtEnvironment { static storm::Environment createEnvironment() { storm::Environment env; env.modelchecker().conditional().setAlgorithm(storm::ConditionalAlgorithmSetting::BisectionPolicyTracking); - env.modelchecker().conditional().setTolerance(storm::utility::zero()); return env; } }; @@ -148,7 +145,6 @@ class SparseRationalNumberBisectionAdvancedPtEnvironment { static storm::Environment createEnvironment() { storm::Environment env; env.modelchecker().conditional().setAlgorithm(storm::ConditionalAlgorithmSetting::BisectionAdvancedPolicyTracking); - env.modelchecker().conditional().setTolerance(storm::utility::zero()); env.solver().minMax().setPrecision(storm::utility::zero()); // TODO: this should not be necessary return env; } @@ -203,10 +199,11 @@ class ConditionalMdpPrctlModelCheckerTest : public ::testing::Test { } std::vector> getTasks( - std::vector> const& formulas) const { + std::vector> const& formulas, ValueType tolerance) const { std::vector> result; for (auto const& f : formulas) { result.emplace_back(*f, true); // Set onlyInitialStatesRelevant to true for conditional tasks + result.back().setTopLevelTolerance(tolerance); } return result; } @@ -239,7 +236,7 @@ TYPED_TEST(ConditionalMdpPrctlModelCheckerTest, two_dice) { auto program = storm::parser::PrismParser::parse(STORM_TEST_RESOURCES_DIR "/mdp/two_dice.nm"); auto modelFormulas = this->buildModelFormulas(program, formulasString); auto model = std::move(modelFormulas.first); - auto tasks = this->getTasks(modelFormulas.second); + auto tasks = this->getTasks(modelFormulas.second, this->precision()); EXPECT_EQ(169ul, model->getNumberOfStates()); EXPECT_EQ(436ul, model->getNumberOfTransitions()); ASSERT_EQ(model->getType(), storm::models::ModelType::Mdp); @@ -261,7 +258,7 @@ TYPED_TEST(ConditionalMdpPrctlModelCheckerTest, two_dice) { if constexpr (std::is_same_v) { // Non sound model checking this property with advanced bisection leads to incorrect pMax bound, resulting in inconsistent bisection bounds. - EXPECT_DEATH_IF_SUPPORTED(checker.check(this->env(), tasks[6]), "Bisection method bounds are inconsistent"); + EXPECT_DEATH_IF_SUPPORTED(checker.check(this->env(), tasks[6]), ""); } else { result = checker.check(this->env(), tasks[6])->template asExplicitQuantitativeCheckResult(); EXPECT_NEAR(this->parseNumber("1"), result[*mdp->getInitialStates().begin()], this->precision()); @@ -287,7 +284,7 @@ TYPED_TEST(ConditionalMdpPrctlModelCheckerTest, consensus) { auto program = storm::parser::PrismParser::parse(STORM_TEST_RESOURCES_DIR "/mdp/coin2-2.nm"); auto modelFormulas = this->buildModelFormulas(program, formulasString); auto model = std::move(modelFormulas.first); - auto tasks = this->getTasks(modelFormulas.second); + auto tasks = this->getTasks(modelFormulas.second, this->precision()); EXPECT_EQ(272ul, model->getNumberOfStates()); EXPECT_EQ(492ul, model->getNumberOfTransitions()); ASSERT_EQ(model->getType(), storm::models::ModelType::Mdp); @@ -331,7 +328,7 @@ endmodule auto program = storm::parser::PrismParser::parseFromString(programAsString, ""); auto modelFormulas = this->buildModelFormulas(program, formulasString); auto model = std::move(modelFormulas.first); - auto tasks = this->getTasks(modelFormulas.second); + auto tasks = this->getTasks(modelFormulas.second, this->precision()); EXPECT_EQ(3ul, model->getNumberOfStates()); EXPECT_EQ(4ul, model->getNumberOfTransitions()); ASSERT_EQ(model->getType(), storm::models::ModelType::Mdp); From c43c368dbb001a6b9bcb39769eff99e79f6ffb4d Mon Sep 17 00:00:00 2001 From: Luko van der Maas Date: Tue, 21 Apr 2026 10:37:58 +0200 Subject: [PATCH 41/51] Remove commented-out code and clean up arbitrary choice comments in computeConditionalProbabilities function --- .../helper/conditional/ConditionalHelper.cpp | 26 +++---------------- 1 file changed, 4 insertions(+), 22 deletions(-) diff --git a/src/storm/modelchecker/helper/conditional/ConditionalHelper.cpp b/src/storm/modelchecker/helper/conditional/ConditionalHelper.cpp index 8ab015fa27..f4a87ef3b1 100644 --- a/src/storm/modelchecker/helper/conditional/ConditionalHelper.cpp +++ b/src/storm/modelchecker/helper/conditional/ConditionalHelper.cpp @@ -1270,16 +1270,6 @@ std::unique_ptr computeConditionalProbabilities(Environment const& 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)); - // } - // We first translate the problem into a normal form. // @see doi.org/10.1007/978-3-642-54862-8_43 STORM_LOG_THROW(goal.hasRelevantValues(), storm::exceptions::NotSupportedException, @@ -1288,11 +1278,8 @@ std::unique_ptr computeConditionalProbabilities(Environment const& "Only one initial state is supported for conditional probabilities"); 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(), checkTask.isProduceSchedulersSet(), transitionMatrix, backwardTransitions, goal.relevantValues(), targetStates, conditionStates); - // sw.stop(); - // STORM_LOG_INFO("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(); @@ -1325,7 +1312,6 @@ std::unique_ptr computeConditionalProbabilities(Environment const& alg = ConditionalAlgorithmSetting::Restart; } STORM_LOG_INFO("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: { @@ -1356,15 +1342,11 @@ std::unique_ptr computeConditionalProbabilities(Environment const& } initialStateValue = result.initialStateValue; scheduler = std::move(result.scheduler); - - // sw.stop(); - // STORM_LOG_INFO("Time for analyzing the normal form: " << sw << ".\n"); } 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, @@ -1426,13 +1408,13 @@ std::unique_ptr computeConditionalProbabilities(Environment const& if (normalFormData.schedulerChoicesForReachingTargetStates->isChoiceSelected(state)) { finalScheduler->setChoice(normalFormData.schedulerChoicesForReachingTargetStates->getChoice(state), state, 0); } else { - finalScheduler->setChoice(0, state, 0); // arbitrary choice if no choice was recorded, TODO: this could be problematic for paynt? + finalScheduler->setChoice(0, state, 0); // arbitrary choice if no choice was recorded. } } else if (targetStates.get(state)) { if (normalFormData.schedulerChoicesForReachingConditionStates->isChoiceSelected(state)) { finalScheduler->setChoice(normalFormData.schedulerChoicesForReachingConditionStates->getChoice(state), state, 0); } else { - finalScheduler->setChoice(0, state, 0); // arbitrary choice if no choice was recorded, TODO: this could be problematic for paynt? + finalScheduler->setChoice(0, state, 0); // arbitrary choice if no choice was recorded. } } else { finalScheduler->setChoice(scheduler->getChoice(state), state, 0); @@ -1442,13 +1424,13 @@ std::unique_ptr computeConditionalProbabilities(Environment const& 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? + finalScheduler->setChoice(0, state, 1); // arbitrary choice if no choice was recorded. } // 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? + finalScheduler->setChoice(0, state, 2); // arbitrary choice if no choice was recorded. } } From 683f9f1bd09a88c69382a4e96d977bd636388bd9 Mon Sep 17 00:00:00 2001 From: Luko van der Maas Date: Tue, 21 Apr 2026 14:44:00 +0200 Subject: [PATCH 42/51] Mostly revert "Move conditional tolerance as general field to checktask" --- .../ConditionalModelCheckerEnvironment.cpp | 9 +++++ .../ConditionalModelCheckerEnvironment.h | 4 +++ src/storm/modelchecker/CheckTask.h | 34 ++++--------------- .../helper/conditional/ConditionalHelper.cpp | 23 +++++++------ .../settings/modules/ModelCheckerSettings.cpp | 14 ++++++++ .../settings/modules/ModelCheckerSettings.h | 6 ++++ src/storm/utility/constants.cpp | 15 -------- src/storm/utility/constants.h | 3 -- .../ConditionalMdpPrctlModelCheckerTest.cpp | 15 ++++---- 9 files changed, 60 insertions(+), 63 deletions(-) diff --git a/src/storm/environment/modelchecker/ConditionalModelCheckerEnvironment.cpp b/src/storm/environment/modelchecker/ConditionalModelCheckerEnvironment.cpp index e09abd191c..4ad6d451fe 100644 --- a/src/storm/environment/modelchecker/ConditionalModelCheckerEnvironment.cpp +++ b/src/storm/environment/modelchecker/ConditionalModelCheckerEnvironment.cpp @@ -8,6 +8,7 @@ namespace storm { ConditionalModelCheckerEnvironment::ConditionalModelCheckerEnvironment() { auto const& mcSettings = storm::settings::getModule(); algorithm = mcSettings.getConditionalAlgorithmSetting(); + tolerance = mcSettings.getConditionalTolerance(); } ConditionalModelCheckerEnvironment::~ConditionalModelCheckerEnvironment() { @@ -22,4 +23,12 @@ void ConditionalModelCheckerEnvironment::setAlgorithm(ConditionalAlgorithmSettin algorithm = value; } +storm::RationalNumber ConditionalModelCheckerEnvironment::getTolerance() const { + return tolerance; +} + +void ConditionalModelCheckerEnvironment::setTolerance(storm::RationalNumber const& value) { + tolerance = value; +} + } // namespace storm diff --git a/src/storm/environment/modelchecker/ConditionalModelCheckerEnvironment.h b/src/storm/environment/modelchecker/ConditionalModelCheckerEnvironment.h index 93e20e0111..f198701440 100644 --- a/src/storm/environment/modelchecker/ConditionalModelCheckerEnvironment.h +++ b/src/storm/environment/modelchecker/ConditionalModelCheckerEnvironment.h @@ -13,8 +13,12 @@ class ConditionalModelCheckerEnvironment { ConditionalAlgorithmSetting getAlgorithm() const; void setAlgorithm(ConditionalAlgorithmSetting value); + storm::RationalNumber getTolerance() const; + void setTolerance(storm::RationalNumber const& value); + private: ConditionalAlgorithmSetting algorithm; + storm::RationalNumber tolerance; }; } // namespace storm diff --git a/src/storm/modelchecker/CheckTask.h b/src/storm/modelchecker/CheckTask.h index 94463b916c..16cd6ca63a 100644 --- a/src/storm/modelchecker/CheckTask.h +++ b/src/storm/modelchecker/CheckTask.h @@ -36,9 +36,8 @@ class CheckTask { /*! * Creates a task object with the default options for the given formula. */ - CheckTask(FormulaType const& formula, bool onlyInitialStatesRelevant = false, UncertaintyResolutionMode = UncertaintyResolutionMode::Unset, - ValueType topLevelTolerance = storm::utility::defaultTolerance()) - : formula(formula), hint(new ModelCheckerHint()), TopLevelTolerance(std::make_shared(std::move(topLevelTolerance))) { + CheckTask(FormulaType const& formula, bool onlyInitialStatesRelevant = false, UncertaintyResolutionMode = UncertaintyResolutionMode::Unset) + : formula(formula), hint(new ModelCheckerHint()) { this->onlyInitialStatesRelevant = onlyInitialStatesRelevant; this->produceSchedulers = false; this->qualitative = false; @@ -55,7 +54,7 @@ class CheckTask { CheckTask substituteFormula(NewFormulaType const& newFormula) const { CheckTask result(newFormula, this->optimizationDirection, this->playerCoalition, this->rewardModel, this->onlyInitialStatesRelevant, this->bound, this->qualitative, this->produceSchedulers, this->hint, - this->uncertaintyResolutionMode, *this->TopLevelTolerance); + this->uncertaintyResolutionMode); result.updateOperatorInformation(); return result; } @@ -134,7 +133,7 @@ class CheckTask { CheckTask convertValueType() const { return CheckTask(this->formula, this->optimizationDirection, this->playerCoalition, this->rewardModel, this->onlyInitialStatesRelevant, this->bound, this->qualitative, this->produceSchedulers, this->hint, - this->uncertaintyResolutionMode, storm::utility::convertNumber(*this->TopLevelTolerance)); + this->uncertaintyResolutionMode); } /*! @@ -322,20 +321,6 @@ class CheckTask { return isSet(this->uncertaintyResolutionMode); } - /*! - * Retrieves the tolerance for the top-level model checking. - */ - ValueType const& getTopLevelTolerance() const { - return *TopLevelTolerance; - } - - /*! - * Sets the tolerance for the top-level model checking. - */ - void setTopLevelTolerance(ValueType topLevelTolerance) { - this->TopLevelTolerance = std::make_shared(std::move(topLevelTolerance)); - } - /*! * Conversion operator that strips the type of the formula. */ @@ -358,14 +343,11 @@ class CheckTask { * with bounds 0/1. * @param produceSchedulers If supported by the model checker and the model formalism, schedulers to achieve * a value will be produced if this flag is set. - * @param hint A hint that might contain information that speeds up the modelchecking process (if supported by the model checker) - * @param uncertaintyResolutionMode Whether uncertainty should be resolved be minimizing, maximizing, acting robust or cooperative. - * @param topLevelTolerance The tolerance for the top-level model checking. */ CheckTask(std::reference_wrapper const& formula, boost::optional const& optimizationDirection, boost::optional playerCoalition, boost::optional const& rewardModel, bool onlyInitialStatesRelevant, boost::optional const& bound, bool qualitative, bool produceSchedulers, std::shared_ptr const& hint, - UncertaintyResolutionMode uncertaintyResolutionMode, ValueType topLevelTolerance) + UncertaintyResolutionMode uncertaintyResolutionMode) : formula(formula), optimizationDirection(optimizationDirection), playerCoalition(playerCoalition), @@ -375,8 +357,7 @@ class CheckTask { qualitative(qualitative), produceSchedulers(produceSchedulers), hint(hint), - uncertaintyResolutionMode(uncertaintyResolutionMode), - TopLevelTolerance(std::make_shared(std::move(topLevelTolerance))) { + uncertaintyResolutionMode(uncertaintyResolutionMode) { // Intentionally left empty. } @@ -410,9 +391,6 @@ class CheckTask { // Whether uncertainty should be resolved be minimizing, maximizing, acting robust or cooperative. UncertaintyResolutionMode uncertaintyResolutionMode; - - // The tolerance for the top-level model checking. - std::shared_ptr TopLevelTolerance; }; } // namespace modelchecker diff --git a/src/storm/modelchecker/helper/conditional/ConditionalHelper.cpp b/src/storm/modelchecker/helper/conditional/ConditionalHelper.cpp index f4a87ef3b1..4920be220e 100644 --- a/src/storm/modelchecker/helper/conditional/ConditionalHelper.cpp +++ b/src/storm/modelchecker/helper/conditional/ConditionalHelper.cpp @@ -968,12 +968,15 @@ class WeightedReachabilityHelper { template typename internal::ResultReturnType computeViaBisection(Environment const& env, bool const useAdvancedBounds, bool const usePolicyTracking, - SolutionType const precision, uint64_t const initialState, - storm::solver::SolveGoal const& goal, bool computeScheduler, - storm::storage::SparseMatrix const& transitionMatrix, + 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 + 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().conditional().getTolerance()); + }(); 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."); @@ -1147,9 +1150,9 @@ typename internal::ResultReturnType computeViaBisection(Environment c } template -typename internal::ResultReturnType computeViaBisection(Environment const& env, SolutionType const precision, ConditionalAlgorithmSetting const alg, - uint64_t const initialState, storm::solver::SolveGoal const& goal, - bool computeScheduler, storm::storage::SparseMatrix const& transitionMatrix, +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; @@ -1157,8 +1160,8 @@ typename internal::ResultReturnType computeViaBisection(Environment c "Unhandled Bisection algorithm " << alg << "."); bool const useAdvancedBounds = (alg == BisectionAdvanced || alg == BisectionAdvancedPolicyTracking); bool const usePolicyTracking = (alg == BisectionPolicyTracking || alg == BisectionAdvancedPolicyTracking); - return computeViaBisection(env, useAdvancedBounds, usePolicyTracking, precision, initialState, goal, computeScheduler, transitionMatrix, - backwardTransitions, normalForm); + return computeViaBisection(env, useAdvancedBounds, usePolicyTracking, initialState, goal, computeScheduler, transitionMatrix, backwardTransitions, + normalForm); } template @@ -1327,8 +1330,8 @@ std::unique_ptr computeConditionalProbabilities(Environment const& result = internal::decideThreshold(analysisEnv, initialState, goal.direction(), goal.thresholdValue(), checkTask.isProduceSchedulersSet(), transitionMatrix, backwardTransitions, normalFormData); } else { - result = internal::computeViaBisection(analysisEnv, checkTask.getTopLevelTolerance(), alg, initialState, goal, - checkTask.isProduceSchedulersSet(), transitionMatrix, backwardTransitions, normalFormData); + result = internal::computeViaBisection(analysisEnv, alg, initialState, goal, checkTask.isProduceSchedulersSet(), transitionMatrix, + backwardTransitions, normalFormData); } break; } diff --git a/src/storm/settings/modules/ModelCheckerSettings.cpp b/src/storm/settings/modules/ModelCheckerSettings.cpp index 4b81dff52a..8a79ee6907 100644 --- a/src/storm/settings/modules/ModelCheckerSettings.cpp +++ b/src/storm/settings/modules/ModelCheckerSettings.cpp @@ -13,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, @@ -34,6 +35,15 @@ ModelCheckerSettings::ModelCheckerSettings() : ModuleSettings(moduleName) { .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 fbf5e09ac8..ef31a110b8 100644 --- a/src/storm/settings/modules/ModelCheckerSettings.h +++ b/src/storm/settings/modules/ModelCheckerSettings.h @@ -45,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/utility/constants.cpp b/src/storm/utility/constants.cpp index bf8f4ccaea..aafd3d489f 100644 --- a/src/storm/utility/constants.cpp +++ b/src/storm/utility/constants.cpp @@ -25,15 +25,6 @@ ValueType zero() { return ValueType(0); } -template -ValueType defaultTolerance() { - if constexpr (storm::NumberTraits::IsExact) { - return zero(); - } else { - return convertNumber(1e-6); - } -} - template ValueType infinity() { return std::numeric_limits::infinity(); @@ -1149,7 +1140,6 @@ storm::RationalInterval abs(storm::RationalInterval const& interval) { // double template double one(); template double zero(); -template double defaultTolerance(); template double infinity(); template bool isOne(double const& value); template bool isZero(double const& value); @@ -1235,7 +1225,6 @@ template double convertNumber(long const&); template storm::ClnRationalNumber one(); template NumberTraits::IntegerType one(); template storm::ClnRationalNumber zero(); -template storm::ClnRationalNumber defaultTolerance(); template bool isZero(NumberTraits::IntegerType const& value); template bool isConstant(storm::ClnRationalNumber const& value); template bool isPositive(storm::ClnRationalNumber const& value); @@ -1267,7 +1256,6 @@ template uint64_t bitsize(storm::ClnIntegerNumber const& number); template storm::GmpRationalNumber one(); template NumberTraits::IntegerType one(); template storm::GmpRationalNumber zero(); -template storm::GmpRationalNumber defaultTolerance(); template bool isZero(NumberTraits::IntegerType const& value); template bool isConstant(storm::GmpRationalNumber const& value); template bool isPositive(storm::GmpRationalNumber const& value); @@ -1296,7 +1284,6 @@ template uint64_t bitsize(storm::GmpIntegerNumber const& number); // Instantiations for rational function. template RationalFunction one(); template RationalFunction zero(); -template RationalFunction defaultTolerance(); template bool isNan(RationalFunction const&); @@ -1307,7 +1294,6 @@ template Polynomial zero(); // Instantiations for intervals. template Interval one(); template Interval zero(); -template Interval defaultTolerance(); template bool isOne(Interval const& value); template bool isZero(Interval const& value); template bool isInfinity(Interval const& value); @@ -1322,7 +1308,6 @@ template std::string to_string(storm::Interval const& value); // Instantiations for intervals. template RationalInterval one(); template RationalInterval zero(); -template RationalInterval defaultTolerance(); template bool isOne(RationalInterval const& value); template bool isZero(RationalInterval const& value); template bool isInfinity(RationalInterval const& value); diff --git a/src/storm/utility/constants.h b/src/storm/utility/constants.h index 5bafae7a2a..977df3601c 100644 --- a/src/storm/utility/constants.h +++ b/src/storm/utility/constants.h @@ -76,9 +76,6 @@ ValueType one(); template ValueType zero(); -template -ValueType defaultTolerance(); - template ValueType infinity(); diff --git a/src/test/storm/modelchecker/prctl/mdp/ConditionalMdpPrctlModelCheckerTest.cpp b/src/test/storm/modelchecker/prctl/mdp/ConditionalMdpPrctlModelCheckerTest.cpp index 9e3a459976..54704ab467 100644 --- a/src/test/storm/modelchecker/prctl/mdp/ConditionalMdpPrctlModelCheckerTest.cpp +++ b/src/test/storm/modelchecker/prctl/mdp/ConditionalMdpPrctlModelCheckerTest.cpp @@ -108,6 +108,7 @@ class SparseRationalNumberBisectionEnvironment { static storm::Environment createEnvironment() { storm::Environment env; env.modelchecker().conditional().setAlgorithm(storm::ConditionalAlgorithmSetting::Bisection); + env.modelchecker().conditional().setTolerance(storm::utility::zero()); return env; } }; @@ -120,7 +121,7 @@ class SparseRationalNumberBisectionAdvancedEnvironment { static storm::Environment createEnvironment() { storm::Environment env; env.modelchecker().conditional().setAlgorithm(storm::ConditionalAlgorithmSetting::BisectionAdvanced); - env.solver().minMax().setPrecision(storm::utility::zero()); // TODO: this should not be necessary + env.modelchecker().conditional().setTolerance(storm::utility::zero()); return env; } }; @@ -133,6 +134,7 @@ class SparseRationalNumberBisectionPtEnvironment { static storm::Environment createEnvironment() { storm::Environment env; env.modelchecker().conditional().setAlgorithm(storm::ConditionalAlgorithmSetting::BisectionPolicyTracking); + env.modelchecker().conditional().setTolerance(storm::utility::zero()); return env; } }; @@ -145,7 +147,7 @@ class SparseRationalNumberBisectionAdvancedPtEnvironment { static storm::Environment createEnvironment() { storm::Environment env; env.modelchecker().conditional().setAlgorithm(storm::ConditionalAlgorithmSetting::BisectionAdvancedPolicyTracking); - env.solver().minMax().setPrecision(storm::utility::zero()); // TODO: this should not be necessary + env.modelchecker().conditional().setTolerance(storm::utility::zero()); return env; } }; @@ -199,11 +201,10 @@ class ConditionalMdpPrctlModelCheckerTest : public ::testing::Test { } std::vector> getTasks( - std::vector> const& formulas, ValueType tolerance) const { + std::vector> const& formulas) const { std::vector> result; for (auto const& f : formulas) { result.emplace_back(*f, true); // Set onlyInitialStatesRelevant to true for conditional tasks - result.back().setTopLevelTolerance(tolerance); } return result; } @@ -236,7 +237,7 @@ TYPED_TEST(ConditionalMdpPrctlModelCheckerTest, two_dice) { auto program = storm::parser::PrismParser::parse(STORM_TEST_RESOURCES_DIR "/mdp/two_dice.nm"); auto modelFormulas = this->buildModelFormulas(program, formulasString); auto model = std::move(modelFormulas.first); - auto tasks = this->getTasks(modelFormulas.second, this->precision()); + auto tasks = this->getTasks(modelFormulas.second); EXPECT_EQ(169ul, model->getNumberOfStates()); EXPECT_EQ(436ul, model->getNumberOfTransitions()); ASSERT_EQ(model->getType(), storm::models::ModelType::Mdp); @@ -284,7 +285,7 @@ TYPED_TEST(ConditionalMdpPrctlModelCheckerTest, consensus) { auto program = storm::parser::PrismParser::parse(STORM_TEST_RESOURCES_DIR "/mdp/coin2-2.nm"); auto modelFormulas = this->buildModelFormulas(program, formulasString); auto model = std::move(modelFormulas.first); - auto tasks = this->getTasks(modelFormulas.second, this->precision()); + auto tasks = this->getTasks(modelFormulas.second); EXPECT_EQ(272ul, model->getNumberOfStates()); EXPECT_EQ(492ul, model->getNumberOfTransitions()); ASSERT_EQ(model->getType(), storm::models::ModelType::Mdp); @@ -328,7 +329,7 @@ endmodule auto program = storm::parser::PrismParser::parseFromString(programAsString, ""); auto modelFormulas = this->buildModelFormulas(program, formulasString); auto model = std::move(modelFormulas.first); - auto tasks = this->getTasks(modelFormulas.second, this->precision()); + auto tasks = this->getTasks(modelFormulas.second); EXPECT_EQ(3ul, model->getNumberOfStates()); EXPECT_EQ(4ul, model->getNumberOfTransitions()); ASSERT_EQ(model->getType(), storm::models::ModelType::Mdp); From 1aec1d3aa208808062f5c31b2274d980404debff Mon Sep 17 00:00:00 2001 From: Luko van der Maas Date: Tue, 21 Apr 2026 16:21:36 +0200 Subject: [PATCH 43/51] Fix comments --- .../helper/conditional/ConditionalHelper.cpp | 44 ++--- .../helper/conditional/ConditionalHelper.h | 3 +- .../prctl/SparseMdpPrctlModelChecker.cpp | 7 +- .../prctl/helper/SparseMdpPrctlHelper.cpp | 173 ------------------ .../modules/MinMaxEquationSolverSettings.cpp | 2 +- 5 files changed, 26 insertions(+), 203 deletions(-) diff --git a/src/storm/modelchecker/helper/conditional/ConditionalHelper.cpp b/src/storm/modelchecker/helper/conditional/ConditionalHelper.cpp index 4920be220e..bef114bc46 100644 --- a/src/storm/modelchecker/helper/conditional/ConditionalHelper.cpp +++ b/src/storm/modelchecker/helper/conditional/ConditionalHelper.cpp @@ -973,11 +973,8 @@ typename internal::ResultReturnType computeViaBisection(Environment c 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 - 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().conditional().getTolerance()); - }(); - STORM_LOG_WARN_COND(!(env.solver().isForceSoundness() && storm::utility::isZero(precision)), + SolutionType const precision = [&env]() { return storm::utility::convertNumber(env.modelchecker().conditional().getTolerance()); }(); + STORM_LOG_WARN_COND(!env.solver().isForceSoundness(), "Bisection method does not adequately handle propagation of errors. Result is not necessarily sound."); bool const relative = env.solver().minMax().getRelativeTerminationCriterion(); @@ -1265,8 +1262,7 @@ 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, + bool produceSchedulers, 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). @@ -1281,15 +1277,15 @@ std::unique_ptr computeConditionalProbabilities(Environment const& "Only one initial state is supported for conditional probabilities"); STORM_LOG_TRACE("Computing conditional probabilities for a model with " << transitionMatrix.getRowGroupCount() << " states and " << transitionMatrix.getEntryCount() << " transitions."); - auto normalFormData = internal::obtainNormalForm(normalFormConstructionEnv, goal.direction(), checkTask.isProduceSchedulersSet(), transitionMatrix, - backwardTransitions, goal.relevantValues(), targetStates, conditionStates); + auto normalFormData = internal::obtainNormalForm(normalFormConstructionEnv, goal.direction(), produceSchedulers, transitionMatrix, backwardTransitions, + goal.relevantValues(), targetStates, conditionStates); // 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; - if (initialStateValue == storm::utility::zero() && !normalFormData.terminalStates.get(initialState) && checkTask.isProduceSchedulersSet()) { + if (initialStateValue == storm::utility::zero() && !normalFormData.terminalStates.get(initialState) && produceSchedulers) { // we need to compute a scheduler that at least reaches the condition with non-zero probability auto initialStateBitVector = storm::storage::BitVector(transitionMatrix.getRowGroupCount(), false); initialStateBitVector.set(initialState, true); @@ -1318,8 +1314,8 @@ std::unique_ptr computeConditionalProbabilities(Environment const& internal::ResultReturnType result{storm::utility::zero()}; switch (alg) { case ConditionalAlgorithmSetting::Restart: { - result = internal::computeViaRestartMethod(analysisEnv, initialState, goal, checkTask.isProduceSchedulersSet(), transitionMatrix, - backwardTransitions, normalFormData); + result = internal::computeViaRestartMethod(analysisEnv, initialState, goal, produceSchedulers, transitionMatrix, backwardTransitions, + normalFormData); break; } case ConditionalAlgorithmSetting::Bisection: @@ -1327,11 +1323,11 @@ std::unique_ptr computeConditionalProbabilities(Environment const& case ConditionalAlgorithmSetting::BisectionPolicyTracking: case ConditionalAlgorithmSetting::BisectionAdvancedPolicyTracking: { if (goal.isBounded()) { - result = internal::decideThreshold(analysisEnv, initialState, goal.direction(), goal.thresholdValue(), checkTask.isProduceSchedulersSet(), - transitionMatrix, backwardTransitions, normalFormData); + result = internal::decideThreshold(analysisEnv, initialState, goal.direction(), goal.thresholdValue(), produceSchedulers, transitionMatrix, + backwardTransitions, normalFormData); } else { - result = internal::computeViaBisection(analysisEnv, alg, initialState, goal, checkTask.isProduceSchedulersSet(), transitionMatrix, - backwardTransitions, normalFormData); + result = internal::computeViaBisection(analysisEnv, alg, initialState, goal, produceSchedulers, transitionMatrix, backwardTransitions, + normalFormData); } break; } @@ -1349,7 +1345,7 @@ std::unique_ptr computeConditionalProbabilities(Environment const& 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) { + if (produceSchedulers && scheduler) { storm::utility::graph::computeSchedulerProb1E(normalFormData.targetStates, transitionMatrix, backwardTransitions, normalFormData.targetStates, targetStates, *scheduler); storm::utility::graph::computeSchedulerProb1E(normalFormData.conditionStates, transitionMatrix, backwardTransitions, normalFormData.conditionStates, @@ -1442,17 +1438,17 @@ std::unique_ptr computeConditionalProbabilities(Environment const& return result; } -template std::unique_ptr computeConditionalProbabilities(Environment const& env, storm::solver::SolveGoal&& goal, - storm::modelchecker::CheckTask const& checkTask, +template std::unique_ptr computeConditionalProbabilities(Environment const& env, storm::solver::SolveGoal&& goal, bool produceSchedulers, 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); +template std::unique_ptr computeConditionalProbabilities(Environment const& env, storm::solver::SolveGoal&& goal, + bool produceSchedulers, + 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 5596ebf115..2f87e395b1 100644 --- a/src/storm/modelchecker/helper/conditional/ConditionalHelper.h +++ b/src/storm/modelchecker/helper/conditional/ConditionalHelper.h @@ -24,8 +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, + bool produceSchedulers, 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/prctl/SparseMdpPrctlModelChecker.cpp b/src/storm/modelchecker/prctl/SparseMdpPrctlModelChecker.cpp index a5fcc41408..ea116d0da0 100644 --- a/src/storm/modelchecker/prctl/SparseMdpPrctlModelChecker.cpp +++ b/src/storm/modelchecker/prctl/SparseMdpPrctlModelChecker.cpp @@ -283,9 +283,10 @@ std::unique_ptr SparseMdpPrctlModelChecker::com 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), checkTask, this->getModel().getTransitionMatrix(), - this->getModel().getBackwardTransitions(), leftResult.getTruthValuesVector(), rightResult.getTruthValuesVector()); + return storm::modelchecker::computeConditionalProbabilities(env, storm::solver::SolveGoal(this->getModel(), checkTask), + checkTask.isProduceSchedulersSet(), this->getModel().getTransitionMatrix(), + this->getModel().getBackwardTransitions(), leftResult.getTruthValuesVector(), + rightResult.getTruthValuesVector()); } } diff --git a/src/storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.cpp b/src/storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.cpp index 82b0edd205..8e04642eea 100644 --- a/src/storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.cpp +++ b/src/storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.cpp @@ -1475,179 +1475,6 @@ MDPSparseModelCheckingHelperReturnType SparseMdpPrctlHelper -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, diff --git a/src/storm/settings/modules/MinMaxEquationSolverSettings.cpp b/src/storm/settings/modules/MinMaxEquationSolverSettings.cpp index 7697aebd14..ef674be8ce 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::createDoubleRangeValidatorIncluding(0.0, 1.0)) + .addValidatorDouble(ArgumentValidatorFactory::createDoubleRangeValidatorExcluding(0.0, 1.0)) .build()) .build()); From bb081d94645a5ace35204b2ec48007e92650f044 Mon Sep 17 00:00:00 2001 From: Luko van der Maas Date: Wed, 22 Apr 2026 14:10:15 +0200 Subject: [PATCH 44/51] Change tolerance to precision, add relative flag, add default handling of precision when exact. --- .../ConditionalModelCheckerEnvironment.cpp | 31 ++++++++++--- .../ConditionalModelCheckerEnvironment.h | 12 +++-- .../helper/conditional/ConditionalHelper.cpp | 45 +++++++++++++++---- src/storm/settings/SettingsManager.cpp | 2 + .../settings/modules/ModelCheckerSettings.cpp | 33 -------------- .../settings/modules/ModelCheckerSettings.h | 19 -------- .../ConditionalMdpPrctlModelCheckerTest.cpp | 10 ++--- 7 files changed, 75 insertions(+), 77 deletions(-) diff --git a/src/storm/environment/modelchecker/ConditionalModelCheckerEnvironment.cpp b/src/storm/environment/modelchecker/ConditionalModelCheckerEnvironment.cpp index 4ad6d451fe..99b3b6b8ac 100644 --- a/src/storm/environment/modelchecker/ConditionalModelCheckerEnvironment.cpp +++ b/src/storm/environment/modelchecker/ConditionalModelCheckerEnvironment.cpp @@ -1,14 +1,18 @@ #include "storm/environment/modelchecker/ConditionalModelCheckerEnvironment.h" +#include "storm/adapters/RationalNumberForward.h" #include "storm/settings/SettingsManager.h" -#include "storm/settings/modules/ModelCheckerSettings.h" +#include "storm/settings/modules/ConditionalSettings.h" +#include "storm/utility/constants.h" namespace storm { ConditionalModelCheckerEnvironment::ConditionalModelCheckerEnvironment() { - auto const& mcSettings = storm::settings::getModule(); + auto const& mcSettings = storm::settings::getModule(); algorithm = mcSettings.getConditionalAlgorithmSetting(); - tolerance = mcSettings.getConditionalTolerance(); + precision = storm::utility::convertNumber(mcSettings.getConditionalPrecision()); + relative = mcSettings.isConditionalPrecisionRelative(); + precisionSetFromDefault = mcSettings.isConditionalPrecisionSetFromDefaultValue(); } ConditionalModelCheckerEnvironment::~ConditionalModelCheckerEnvironment() { @@ -23,12 +27,25 @@ void ConditionalModelCheckerEnvironment::setAlgorithm(ConditionalAlgorithmSettin algorithm = value; } -storm::RationalNumber ConditionalModelCheckerEnvironment::getTolerance() const { - return tolerance; +storm::RationalNumber ConditionalModelCheckerEnvironment::getPrecision() const { + return precision; } -void ConditionalModelCheckerEnvironment::setTolerance(storm::RationalNumber const& value) { - tolerance = value; +void ConditionalModelCheckerEnvironment::setPrecision(storm::RationalNumber const& value, bool setFromDefault) { + precision = value; + precisionSetFromDefault = setFromDefault; +} + +bool ConditionalModelCheckerEnvironment::isPrecisionSetFromDefault() const { + return precisionSetFromDefault; +} + +bool ConditionalModelCheckerEnvironment::isRelativePrecision() const { + return relative; +} + +void ConditionalModelCheckerEnvironment::setRelativePrecision(bool value) { + relative = value; } } // namespace storm diff --git a/src/storm/environment/modelchecker/ConditionalModelCheckerEnvironment.h b/src/storm/environment/modelchecker/ConditionalModelCheckerEnvironment.h index f198701440..296cf85a5f 100644 --- a/src/storm/environment/modelchecker/ConditionalModelCheckerEnvironment.h +++ b/src/storm/environment/modelchecker/ConditionalModelCheckerEnvironment.h @@ -13,12 +13,18 @@ class ConditionalModelCheckerEnvironment { ConditionalAlgorithmSetting getAlgorithm() const; void setAlgorithm(ConditionalAlgorithmSetting value); - storm::RationalNumber getTolerance() const; - void setTolerance(storm::RationalNumber const& value); + storm::RationalNumber getPrecision() const; + bool isPrecisionSetFromDefault() const; + void setPrecision(storm::RationalNumber const& value, bool setFromDefault); + + bool isRelativePrecision() const; + void setRelativePrecision(bool value); private: ConditionalAlgorithmSetting algorithm; - storm::RationalNumber tolerance; + storm::RationalNumber precision; + bool precisionSetFromDefault; + bool relative; }; } // namespace storm diff --git a/src/storm/modelchecker/helper/conditional/ConditionalHelper.cpp b/src/storm/modelchecker/helper/conditional/ConditionalHelper.cpp index bef114bc46..3b7aba797b 100644 --- a/src/storm/modelchecker/helper/conditional/ConditionalHelper.cpp +++ b/src/storm/modelchecker/helper/conditional/ConditionalHelper.cpp @@ -4,6 +4,7 @@ #include #include "storm/adapters/RationalNumberAdapter.h" +#include "storm/adapters/RationalNumberForward.h" #include "storm/environment/modelchecker/ConditionalModelCheckerEnvironment.h" #include "storm/environment/modelchecker/ModelCheckerEnvironment.h" #include "storm/environment/solver/MinMaxSolverEnvironment.h" @@ -18,6 +19,7 @@ #include "storm/storage/StronglyConnectedComponentDecomposition.h" #include "storm/transformer/EndComponentEliminator.h" #include "storm/utility/Extremum.h" +#include "storm/utility/NumberTraits.h" #include "storm/utility/OptionalRef.h" #include "storm/utility/RationalApproximation.h" #include "storm/utility/SignalHandler.h" @@ -99,7 +101,7 @@ SolutionType solveMinMaxEquationSystem(storm::Environment const& env, storm::sto storm::solver::GeneralMinMaxLinearEquationSolverFactory factory; storm::storage::BitVector relevantValues(matrix.getRowGroupCount(), false); relevantValues.set(initialState, true); - auto getGoal = [&env, &goal, &relevantValues]() -> storm::solver::SolveGoal { + auto getGoal = [&goal, &relevantValues]() -> storm::solver::SolveGoal { if (goal.isBounded()) { return {goal.direction(), goal.boundComparisonType(), goal.thresholdValue(), relevantValues}; } else { @@ -141,6 +143,10 @@ std::unique_ptr> computeReachabilityProbabi scheduler = std::make_unique>(transitionMatrix.getRowGroupCount()); } + auto reachabilityEnv = env; + reachabilityEnv.solver().minMax().setPrecision(env.modelchecker().conditional().getPrecision()); + reachabilityEnv.solver().minMax().setRelativeTerminationCriterion(env.modelchecker().conditional().isRelativePrecision()); + if (initialStates.empty()) { // nothing to do return scheduler; } @@ -153,8 +159,8 @@ std::unique_ptr> computeReachabilityProbabi 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, computeScheduler); + reachabilityEnv, storm::solver::SolveGoal(dir, subInits), submatrix, submatrix.transpose(true), + storm::storage::BitVector(subTargets.size(), true), subTargets, false, computeScheduler); auto origInitIt = initialStates.begin(); for (auto subInit : subInits) { @@ -973,11 +979,11 @@ typename internal::ResultReturnType computeViaBisection(Environment c 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 - SolutionType const precision = [&env]() { return storm::utility::convertNumber(env.modelchecker().conditional().getTolerance()); }(); STORM_LOG_WARN_COND(!env.solver().isForceSoundness(), "Bisection method does not adequately handle propagation of errors. Result is not necessarily sound."); - bool const relative = env.solver().minMax().getRelativeTerminationCriterion(); + bool const relative = env.modelchecker().conditional().isRelativePrecision(); + auto const precision = env.modelchecker().conditional().getPrecision(); WeightedReachabilityHelper wrh(initialState, transitionMatrix, normalForm, computeScheduler); SolutionType pMin{storm::utility::zero()}; @@ -1084,7 +1090,7 @@ typename internal::ResultReturnType computeViaBisection(Environment c } // check for early termination if (storm::utility::resources::isTerminate()) { - STORM_LOG_INFO("Bisection solver aborted after " << iterationCount << "iterations. Bound difference is " + STORM_LOG_WARN("Bisection solver aborted after " << iterationCount << "iterations. Bound difference is " << storm::utility::convertNumber(boundDiff) << "."); break; } @@ -1265,9 +1271,25 @@ std::unique_ptr computeConditionalProbabilities(Environment const& bool produceSchedulers, storm::storage::SparseMatrix const& transitionMatrix, storm::storage::SparseMatrix const& backwardTransitions, storm::storage::BitVector const& targetStates, storm::storage::BitVector const& conditionStates) { + auto precision = storm::utility::convertNumber(env.modelchecker().conditional().getPrecision()); + if (storm::NumberTraits::IsExact && env.modelchecker().conditional().isPrecisionSetFromDefault()) { + STORM_LOG_INFO("Setting the conditional precision to 0 since the value type is exact and the precision was not explicitly set by the user."); + precision = storm::utility::zero(); + } + // 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.modelchecker().conditional().setPrecision(precision * normalFormPrecisionFactor, false); + analysisEnv.modelchecker().conditional().setPrecision(precision * (storm::utility::one() - normalFormPrecisionFactor), false); + } else { + normalFormConstructionEnv.modelchecker().conditional().setPrecision(precision, false); + analysisEnv.modelchecker().conditional().setPrecision(precision, false); + } // We first translate the problem into a normal form. // @see doi.org/10.1007/978-3-642-54862-8_43 @@ -1308,14 +1330,19 @@ std::unique_ptr computeConditionalProbabilities(Environment const& STORM_LOG_ASSERT(normalFormData.maybeStates.get(initialState), "Initial state must be a maybe state if it is not a terminal state"); auto alg = analysisEnv.modelchecker().conditional().getAlgorithm(); if (alg == ConditionalAlgorithmSetting::Default) { - alg = ConditionalAlgorithmSetting::Restart; + alg = ConditionalAlgorithmSetting::BisectionPolicyTracking; } + STORM_LOG_INFO("Analyzing normal form with " << normalFormData.maybeStates.getNumberOfSetBits() << " maybe states using algorithm '" << alg << "."); internal::ResultReturnType result{storm::utility::zero()}; switch (alg) { case ConditionalAlgorithmSetting::Restart: { - result = internal::computeViaRestartMethod(analysisEnv, initialState, goal, produceSchedulers, transitionMatrix, backwardTransitions, - normalFormData); + auto restartEnv = analysisEnv; + restartEnv.solver().minMax().setPrecision(analysisEnv.modelchecker().conditional().getPrecision()); + restartEnv.solver().minMax().setRelativeTerminationCriterion(analysisEnv.modelchecker().conditional().isRelativePrecision()); + + result = + internal::computeViaRestartMethod(restartEnv, initialState, goal, produceSchedulers, transitionMatrix, backwardTransitions, normalFormData); break; } case ConditionalAlgorithmSetting::Bisection: diff --git a/src/storm/settings/SettingsManager.cpp b/src/storm/settings/SettingsManager.cpp index 9602f0a67d..7d34356b61 100644 --- a/src/storm/settings/SettingsManager.cpp +++ b/src/storm/settings/SettingsManager.cpp @@ -16,6 +16,7 @@ #include "storm/settings/modules/AbstractionSettings.h" #include "storm/settings/modules/BisimulationSettings.h" #include "storm/settings/modules/BuildSettings.h" +#include "storm/settings/modules/ConditionalSettings.h" #include "storm/settings/modules/CoreSettings.h" #include "storm/settings/modules/CuddSettings.h" #include "storm/settings/modules/DebugSettings.h" @@ -708,6 +709,7 @@ void initializeAll(std::string const& name, std::string const& executableName) { storm::settings::addModule(); storm::settings::addModule(); storm::settings::addModule(); + storm::settings::addModule(); } } // namespace settings diff --git a/src/storm/settings/modules/ModelCheckerSettings.cpp b/src/storm/settings/modules/ModelCheckerSettings.cpp index 8a79ee6907..a5e67bf339 100644 --- a/src/storm/settings/modules/ModelCheckerSettings.cpp +++ b/src/storm/settings/modules/ModelCheckerSettings.cpp @@ -5,15 +5,11 @@ #include "storm/settings/Option.h" #include "storm/settings/OptionBuilder.h" #include "storm/settings/SettingsManager.h" -#include "storm/utility/constants.h" - namespace storm::settings::modules { 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, @@ -27,23 +23,6 @@ ModelCheckerSettings::ModelCheckerSettings() : ModuleSettings(moduleName) { "filename", "A script that can be called with a prefix formula and a name for the output automaton.") .build()) .build()); - - 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.") - .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 { @@ -58,16 +37,4 @@ std::string ModelCheckerSettings::getLtl2daTool() const { return this->getOption(ltl2daToolOptionName).getArgumentByName("filename").getValueAsString(); } -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()); -} - } // namespace storm::settings::modules diff --git a/src/storm/settings/modules/ModelCheckerSettings.h b/src/storm/settings/modules/ModelCheckerSettings.h index ef31a110b8..4d4df33b95 100644 --- a/src/storm/settings/modules/ModelCheckerSettings.h +++ b/src/storm/settings/modules/ModelCheckerSettings.h @@ -1,8 +1,6 @@ #pragma once #include "storm-config.h" -#include "storm/adapters/RationalNumberAdapter.h" -#include "storm/modelchecker/helper/conditional/ConditionalAlgorithmSetting.h" #include "storm/settings/modules/ModuleSettings.h" namespace storm { @@ -35,22 +33,6 @@ class ModelCheckerSettings : public ModuleSettings { */ std::string getLtl2daTool() const; - /*! - * Retrieves whether an algorithm for conditional properties has been set. - */ - bool isConditionalAlgorithmSet() const; - - /*! - * Retrieves the specified algorithm for conditional probabilities. - */ - ConditionalAlgorithmSetting getConditionalAlgorithmSetting() const; - - /*! - * TODO probably generalize - * Retrieves - */ - storm::RationalNumber getConditionalTolerance() const; - // The name of the module. static const std::string moduleName; @@ -58,7 +40,6 @@ class ModelCheckerSettings : public ModuleSettings { // Define the string names of the options as constants. static const std::string filterRewZeroOptionName; static const std::string ltl2daToolOptionName; - static const std::string conditionalAlgorithmOptionName; }; } // namespace modules diff --git a/src/test/storm/modelchecker/prctl/mdp/ConditionalMdpPrctlModelCheckerTest.cpp b/src/test/storm/modelchecker/prctl/mdp/ConditionalMdpPrctlModelCheckerTest.cpp index 54704ab467..771c1f548b 100644 --- a/src/test/storm/modelchecker/prctl/mdp/ConditionalMdpPrctlModelCheckerTest.cpp +++ b/src/test/storm/modelchecker/prctl/mdp/ConditionalMdpPrctlModelCheckerTest.cpp @@ -22,7 +22,8 @@ class SparseDoubleRestartEnvironment { static storm::Environment createEnvironment() { storm::Environment env; env.modelchecker().conditional().setAlgorithm(storm::ConditionalAlgorithmSetting::Restart); - env.solver().minMax().setPrecision(storm::utility::convertNumber(1e-10)); // restart algorithm requires a higher precision + env.modelchecker().conditional().setPrecision(storm::utility::convertNumber(1e-10), + false); // restart algorithm requires a higher precision return env; } }; @@ -95,7 +96,8 @@ class SparseRationalNumberRestartEnvironment { static storm::Environment createEnvironment() { storm::Environment env; env.modelchecker().conditional().setAlgorithm(storm::ConditionalAlgorithmSetting::Restart); - env.solver().minMax().setPrecision(storm::utility::convertNumber(1e-10)); // restart algorithm requires a higher precision + env.modelchecker().conditional().setPrecision(storm::utility::convertNumber(1e-10), + false); // restart algorithm requires a higher precision return env; } }; @@ -108,7 +110,6 @@ class SparseRationalNumberBisectionEnvironment { static storm::Environment createEnvironment() { storm::Environment env; env.modelchecker().conditional().setAlgorithm(storm::ConditionalAlgorithmSetting::Bisection); - env.modelchecker().conditional().setTolerance(storm::utility::zero()); return env; } }; @@ -121,7 +122,6 @@ class SparseRationalNumberBisectionAdvancedEnvironment { static storm::Environment createEnvironment() { storm::Environment env; env.modelchecker().conditional().setAlgorithm(storm::ConditionalAlgorithmSetting::BisectionAdvanced); - env.modelchecker().conditional().setTolerance(storm::utility::zero()); return env; } }; @@ -134,7 +134,6 @@ class SparseRationalNumberBisectionPtEnvironment { static storm::Environment createEnvironment() { storm::Environment env; env.modelchecker().conditional().setAlgorithm(storm::ConditionalAlgorithmSetting::BisectionPolicyTracking); - env.modelchecker().conditional().setTolerance(storm::utility::zero()); return env; } }; @@ -147,7 +146,6 @@ class SparseRationalNumberBisectionAdvancedPtEnvironment { static storm::Environment createEnvironment() { storm::Environment env; env.modelchecker().conditional().setAlgorithm(storm::ConditionalAlgorithmSetting::BisectionAdvancedPolicyTracking); - env.modelchecker().conditional().setTolerance(storm::utility::zero()); return env; } }; From f7c08818a9b9362bf0ffdd7bd7f3eb18da0a3178 Mon Sep 17 00:00:00 2001 From: Luko van der Maas Date: Wed, 22 Apr 2026 14:14:20 +0200 Subject: [PATCH 45/51] Forgot to commit these --- .../settings/modules/ConditionalSettings.cpp | 61 +++++++++++++++++++ .../settings/modules/ConditionalSettings.h | 54 ++++++++++++++++ 2 files changed, 115 insertions(+) create mode 100644 src/storm/settings/modules/ConditionalSettings.cpp create mode 100644 src/storm/settings/modules/ConditionalSettings.h diff --git a/src/storm/settings/modules/ConditionalSettings.cpp b/src/storm/settings/modules/ConditionalSettings.cpp new file mode 100644 index 0000000000..1eee2d1473 --- /dev/null +++ b/src/storm/settings/modules/ConditionalSettings.cpp @@ -0,0 +1,61 @@ +#include "storm/settings/modules/ConditionalSettings.h" + +#include "storm/settings/ArgumentBuilder.h" +#include "storm/settings/Option.h" +#include "storm/settings/OptionBuilder.h" +#include "storm/settings/SettingsManager.h" + +namespace storm::settings::modules { + +const std::string ConditionalSettings::moduleName = "conditional"; +const std::string ConditionalSettings::conditionalAlgorithmOptionName = "conditional-algorithm"; +const std::string ConditionalSettings::conditionalPrecisionOptionName = "precision"; +const std::string ConditionalSettings::conditionalPrecisionRelativeOptionName = "relative"; + +ConditionalSettings::ConditionalSettings() : ModuleSettings(moduleName) { + 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()); + + this->addOption(storm::settings::OptionBuilder(moduleName, conditionalPrecisionOptionName, false, + "The internally used precision for computing conditional probabilities..") + .setIsAdvanced() + .addArgument(storm::settings::ArgumentBuilder::createDoubleArgument("value", "The precision to use.") + .setDefaultValueDouble(1e-06) + .addValidatorDouble(ArgumentValidatorFactory::createDoubleRangeValidatorIncluding(0.0, 1.0)) + .build()) + .build()); + + this->addOption(storm::settings::OptionBuilder(moduleName, conditionalPrecisionRelativeOptionName, false, + "Whether the precision for computing conditional probabilities is considered relative.") + .setIsAdvanced() + .build()); +} + +bool ConditionalSettings::isConditionalAlgorithmSet() const { + return this->getOption(conditionalAlgorithmOptionName).getHasOptionBeenSet(); +} + +ConditionalAlgorithmSetting ConditionalSettings::getConditionalAlgorithmSetting() const { + return conditionalAlgorithmSettingFromString(this->getOption(conditionalAlgorithmOptionName).getArgumentByName("name").getValueAsString()); +} + +double ConditionalSettings::getConditionalPrecision() const { + return this->getOption(conditionalPrecisionOptionName).getArgumentByName("value").getValueAsDouble(); +} + +bool ConditionalSettings::isConditionalPrecisionSetFromDefaultValue() const { + return !this->getOption(conditionalPrecisionOptionName).getArgumentByName("value").getHasBeenSet() || + this->getOption(conditionalPrecisionOptionName).getArgumentByName("value").wasSetFromDefaultValue(); +} + +bool ConditionalSettings::isConditionalPrecisionRelative() const { + return this->getOption(conditionalPrecisionRelativeOptionName).getHasOptionBeenSet(); +} + +} // namespace storm::settings::modules \ No newline at end of file diff --git a/src/storm/settings/modules/ConditionalSettings.h b/src/storm/settings/modules/ConditionalSettings.h new file mode 100644 index 0000000000..a2f9fd47c4 --- /dev/null +++ b/src/storm/settings/modules/ConditionalSettings.h @@ -0,0 +1,54 @@ +#pragma once + +#include "storm-config.h" +#include "storm/modelchecker/helper/conditional/ConditionalAlgorithmSetting.h" +#include "storm/settings/modules/ModuleSettings.h" + +namespace storm { +namespace settings { +namespace modules { + +/*! + * This class represents the LRA solver settings. + */ +class ConditionalSettings : public ModuleSettings { + public: + ConditionalSettings(); + + /*! + * Retrieves whether an algorithm for conditional properties has been set. + */ + bool isConditionalAlgorithmSet() const; + + /*! + * Retrieves the specified algorithm for conditional probabilities. + */ + ConditionalAlgorithmSetting getConditionalAlgorithmSetting() const; + + /*! + * Retrieves the specified precision for computing conditional probabilities. + */ + double getConditionalPrecision() const; + + /*! + * Retrieves whether the precision for computing conditional probabilities was set from a default value. + */ + bool isConditionalPrecisionSetFromDefaultValue() const; + + /*! + * Retrieves whether the precision for computing conditional probabilities is considered relative. + */ + bool isConditionalPrecisionRelative() const; + + // The name of the module. + static const std::string moduleName; + + private: + static const std::string conditionalAlgorithmOptionName; + static const std::string conditionalPrecisionOptionName; + static const std::string conditionalPrecisionRelativeOptionName; +}; + +} // namespace modules +} // namespace settings +} // namespace storm From 0f3dd99dd2790630af10d01c00cd815c60fb21e2 Mon Sep 17 00:00:00 2001 From: Luko van der Maas Date: Wed, 22 Apr 2026 14:35:34 +0200 Subject: [PATCH 46/51] Fix precision type bug for CLN --- .../modelchecker/helper/conditional/ConditionalHelper.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/storm/modelchecker/helper/conditional/ConditionalHelper.cpp b/src/storm/modelchecker/helper/conditional/ConditionalHelper.cpp index 3b7aba797b..6661018b39 100644 --- a/src/storm/modelchecker/helper/conditional/ConditionalHelper.cpp +++ b/src/storm/modelchecker/helper/conditional/ConditionalHelper.cpp @@ -1271,10 +1271,10 @@ std::unique_ptr computeConditionalProbabilities(Environment const& bool produceSchedulers, storm::storage::SparseMatrix const& transitionMatrix, storm::storage::SparseMatrix const& backwardTransitions, storm::storage::BitVector const& targetStates, storm::storage::BitVector const& conditionStates) { - auto precision = storm::utility::convertNumber(env.modelchecker().conditional().getPrecision()); + auto precision = env.modelchecker().conditional().getPrecision(); if (storm::NumberTraits::IsExact && env.modelchecker().conditional().isPrecisionSetFromDefault()) { STORM_LOG_INFO("Setting the conditional precision to 0 since the value type is exact and the precision was not explicitly set by the user."); - precision = storm::utility::zero(); + precision = storm::utility::zero(); } // We might require adapting the precision of the solver to counter error propagation (e.g. when computing the normal form). From 513d095e50c711b90eed1902d5075f5266479450 Mon Sep 17 00:00:00 2001 From: Luko van der Maas Date: Wed, 22 Apr 2026 14:45:33 +0200 Subject: [PATCH 47/51] Change type of precision in bisection --- src/storm/modelchecker/helper/conditional/ConditionalHelper.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/storm/modelchecker/helper/conditional/ConditionalHelper.cpp b/src/storm/modelchecker/helper/conditional/ConditionalHelper.cpp index 6661018b39..7aa76acb90 100644 --- a/src/storm/modelchecker/helper/conditional/ConditionalHelper.cpp +++ b/src/storm/modelchecker/helper/conditional/ConditionalHelper.cpp @@ -983,7 +983,7 @@ typename internal::ResultReturnType computeViaBisection(Environment c "Bisection method does not adequately handle propagation of errors. Result is not necessarily sound."); bool const relative = env.modelchecker().conditional().isRelativePrecision(); - auto const precision = env.modelchecker().conditional().getPrecision(); + auto const precision = storm::utility::convertNumber(env.modelchecker().conditional().getPrecision()); WeightedReachabilityHelper wrh(initialState, transitionMatrix, normalForm, computeScheduler); SolutionType pMin{storm::utility::zero()}; From acc57d596adf39f4f8efa1227d6069059f38967e Mon Sep 17 00:00:00 2001 From: Luko van der Maas Date: Wed, 22 Apr 2026 15:39:01 +0200 Subject: [PATCH 48/51] Skip assertion for SparseDoubleBisectionAdvancedPtEnvironment in two_dice test due to platform-dependent behavior --- .../prctl/mdp/ConditionalMdpPrctlModelCheckerTest.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/test/storm/modelchecker/prctl/mdp/ConditionalMdpPrctlModelCheckerTest.cpp b/src/test/storm/modelchecker/prctl/mdp/ConditionalMdpPrctlModelCheckerTest.cpp index 771c1f548b..39652c298d 100644 --- a/src/test/storm/modelchecker/prctl/mdp/ConditionalMdpPrctlModelCheckerTest.cpp +++ b/src/test/storm/modelchecker/prctl/mdp/ConditionalMdpPrctlModelCheckerTest.cpp @@ -255,10 +255,8 @@ TYPED_TEST(ConditionalMdpPrctlModelCheckerTest, two_dice) { result = checker.check(this->env(), tasks[5])->template asExplicitQuantitativeCheckResult(); EXPECT_NEAR(this->parseNumber("0"), result[*mdp->getInitialStates().begin()], this->precision()); - if constexpr (std::is_same_v) { - // Non sound model checking this property with advanced bisection leads to incorrect pMax bound, resulting in inconsistent bisection bounds. - EXPECT_DEATH_IF_SUPPORTED(checker.check(this->env(), tasks[6]), ""); - } else { + // This Environment depending on the platform fails or does not fail an assertion. Thus this env is skipped. + if constexpr (!std::is_same_v) { result = checker.check(this->env(), tasks[6])->template asExplicitQuantitativeCheckResult(); EXPECT_NEAR(this->parseNumber("1"), result[*mdp->getInitialStates().begin()], this->precision()); } From 79864998d54f7cb2fb0cae1167b1d0162dc8783f Mon Sep 17 00:00:00 2001 From: Luko van der Maas Date: Wed, 22 Apr 2026 16:29:37 +0200 Subject: [PATCH 49/51] Change conditional algorithm setting name --- src/storm/settings/modules/ConditionalSettings.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/storm/settings/modules/ConditionalSettings.cpp b/src/storm/settings/modules/ConditionalSettings.cpp index 1eee2d1473..36035acf08 100644 --- a/src/storm/settings/modules/ConditionalSettings.cpp +++ b/src/storm/settings/modules/ConditionalSettings.cpp @@ -8,7 +8,7 @@ namespace storm::settings::modules { const std::string ConditionalSettings::moduleName = "conditional"; -const std::string ConditionalSettings::conditionalAlgorithmOptionName = "conditional-algorithm"; +const std::string ConditionalSettings::conditionalAlgorithmOptionName = "algorithm"; const std::string ConditionalSettings::conditionalPrecisionOptionName = "precision"; const std::string ConditionalSettings::conditionalPrecisionRelativeOptionName = "relative"; From 5097e114cb49305a3b3092009b23995f47fc5c47 Mon Sep 17 00:00:00 2001 From: Luko van der Maas Date: Thu, 23 Apr 2026 10:35:25 +0200 Subject: [PATCH 50/51] Use relative as default in conditional settings. --- .../ConditionalModelCheckerEnvironment.cpp | 2 +- src/storm/settings/modules/ConditionalSettings.cpp | 10 +++++----- src/storm/settings/modules/ConditionalSettings.h | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/storm/environment/modelchecker/ConditionalModelCheckerEnvironment.cpp b/src/storm/environment/modelchecker/ConditionalModelCheckerEnvironment.cpp index 99b3b6b8ac..9859b41bf8 100644 --- a/src/storm/environment/modelchecker/ConditionalModelCheckerEnvironment.cpp +++ b/src/storm/environment/modelchecker/ConditionalModelCheckerEnvironment.cpp @@ -11,7 +11,7 @@ ConditionalModelCheckerEnvironment::ConditionalModelCheckerEnvironment() { auto const& mcSettings = storm::settings::getModule(); algorithm = mcSettings.getConditionalAlgorithmSetting(); precision = storm::utility::convertNumber(mcSettings.getConditionalPrecision()); - relative = mcSettings.isConditionalPrecisionRelative(); + relative = !mcSettings.isConditionalPrecisionAbsolute(); precisionSetFromDefault = mcSettings.isConditionalPrecisionSetFromDefaultValue(); } diff --git a/src/storm/settings/modules/ConditionalSettings.cpp b/src/storm/settings/modules/ConditionalSettings.cpp index 36035acf08..e13b4e1e06 100644 --- a/src/storm/settings/modules/ConditionalSettings.cpp +++ b/src/storm/settings/modules/ConditionalSettings.cpp @@ -10,7 +10,7 @@ namespace storm::settings::modules { const std::string ConditionalSettings::moduleName = "conditional"; const std::string ConditionalSettings::conditionalAlgorithmOptionName = "algorithm"; const std::string ConditionalSettings::conditionalPrecisionOptionName = "precision"; -const std::string ConditionalSettings::conditionalPrecisionRelativeOptionName = "relative"; +const std::string ConditionalSettings::conditionalPrecisionAbsoluteOptionName = "absolute"; ConditionalSettings::ConditionalSettings() : ModuleSettings(moduleName) { std::vector const conditionalAlgs = {"default", "restart", "bisection", "bisection-advanced", "bisection-pt", "bisection-advanced-pt", "pi"}; @@ -31,8 +31,8 @@ ConditionalSettings::ConditionalSettings() : ModuleSettings(moduleName) { .build()) .build()); - this->addOption(storm::settings::OptionBuilder(moduleName, conditionalPrecisionRelativeOptionName, false, - "Whether the precision for computing conditional probabilities is considered relative.") + this->addOption(storm::settings::OptionBuilder(moduleName, conditionalPrecisionAbsoluteOptionName, false, + "Whether the precision for computing conditional probabilities is considered absolute.") .setIsAdvanced() .build()); } @@ -54,8 +54,8 @@ bool ConditionalSettings::isConditionalPrecisionSetFromDefaultValue() const { this->getOption(conditionalPrecisionOptionName).getArgumentByName("value").wasSetFromDefaultValue(); } -bool ConditionalSettings::isConditionalPrecisionRelative() const { - return this->getOption(conditionalPrecisionRelativeOptionName).getHasOptionBeenSet(); +bool ConditionalSettings::isConditionalPrecisionAbsolute() const { + return this->getOption(conditionalPrecisionAbsoluteOptionName).getHasOptionBeenSet(); } } // namespace storm::settings::modules \ No newline at end of file diff --git a/src/storm/settings/modules/ConditionalSettings.h b/src/storm/settings/modules/ConditionalSettings.h index a2f9fd47c4..44936c159c 100644 --- a/src/storm/settings/modules/ConditionalSettings.h +++ b/src/storm/settings/modules/ConditionalSettings.h @@ -36,9 +36,9 @@ class ConditionalSettings : public ModuleSettings { bool isConditionalPrecisionSetFromDefaultValue() const; /*! - * Retrieves whether the precision for computing conditional probabilities is considered relative. + * Retrieves whether the precision for computing conditional probabilities is considered absolute. */ - bool isConditionalPrecisionRelative() const; + bool isConditionalPrecisionAbsolute() const; // The name of the module. static const std::string moduleName; @@ -46,7 +46,7 @@ class ConditionalSettings : public ModuleSettings { private: static const std::string conditionalAlgorithmOptionName; static const std::string conditionalPrecisionOptionName; - static const std::string conditionalPrecisionRelativeOptionName; + static const std::string conditionalPrecisionAbsoluteOptionName; }; } // namespace modules From 586de6900f8870db87068ea8a1fa8ad7ec1d6ea0 Mon Sep 17 00:00:00 2001 From: Luko van der Maas Date: Thu, 23 Apr 2026 11:31:12 +0200 Subject: [PATCH 51/51] Copy model in observation trace unfolder as I got garbage models. --- src/storm-pomdp/transformer/ObservationTraceUnfolder.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/storm-pomdp/transformer/ObservationTraceUnfolder.h b/src/storm-pomdp/transformer/ObservationTraceUnfolder.h index 3934106434..a03e8b1452 100644 --- a/src/storm-pomdp/transformer/ObservationTraceUnfolder.h +++ b/src/storm-pomdp/transformer/ObservationTraceUnfolder.h @@ -47,7 +47,7 @@ class ObservationTraceUnfolder { bool isRestartSemanticsSet() 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 traceSoFar;