Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions resources/examples/testfiles/dtmc/test_conditional.pm
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,6 @@ rewards
s=2 : 1;
endrewards

label "unreachable" = false;
label "condition" = s=2;
label "target" = s=3;
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
dtmc

module test
s : [0 .. 3];

[] s=0 -> 0.5 : (s'=1) + 0.5 : (s'=2);
[] s=1 -> 1 : true;
[] s=2 -> 1 : (s'=3);
[] s=3 -> 1 : true;

endmodule

// The condition is reachable from state 0, but not from state 1.
init
s=0 | s=1
endinit

rewards
s=2 : 1;
endrewards

label "start" = s=0;
label "condition" = s=2;
label "target" = s=3;
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "storm/environment/modelchecker/ConditionalModelCheckerEnvironment.h"
#include "storm/environment/modelchecker/ModelCheckerEnvironment.h"
#include "storm/environment/solver/MinMaxSolverEnvironment.h"
#include "storm/exceptions/InvalidPropertyException.h"
#include "storm/exceptions/NotImplementedException.h"
#include "storm/exceptions/NotSupportedException.h"
#include "storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.h"
Expand Down Expand Up @@ -1252,7 +1253,7 @@ std::optional<SolutionType> handleTrivialCases(uint64_t const initialState, Norm
if (normalForm.conditionStates.get(initialState)) {
return normalForm.getTargetValue(initialState); // The value is already known, nothing to do.
} else {
STORM_LOG_THROW(!normalForm.universalObservationFailureStates.get(initialState), storm::exceptions::NotSupportedException,
STORM_LOG_THROW(!normalForm.universalObservationFailureStates.get(initialState), storm::exceptions::InvalidPropertyException,
"Trying to compute undefined conditional probability: the condition has probability 0 under all policies.");
// The last case for a terminal initial state is that it is already target and the condition is reachable with non-zero probability.
// In this case, all schedulers induce a conditional probability of 1 (or do not reach the condition, i.e., have undefined value)
Expand Down
102 changes: 55 additions & 47 deletions src/storm/modelchecker/prctl/helper/SparseDtmcPrctlHelper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -858,35 +858,39 @@ std::vector<SolutionType> SparseDtmcPrctlHelper<ValueType, RewardModelType, Solu
if constexpr (storm::IsIntervalType<ValueType>) {
STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "We do not support computing conditional probabilities with interval models.");
} else {
// The conditional value is only defined for states that reach the condition with positive probability.
storm::storage::BitVector const conditionProbGreater0States =
storm::utility::graph::performProbGreater0(backwardTransitions, storm::storage::BitVector(conditionStates.size(), true), conditionStates);
STORM_LOG_THROW(goal.hasRelevantValues() ? goal.relevantValues().isSubsetOf(conditionProbGreater0States) : !conditionProbGreater0States.empty(),
storm::exceptions::InvalidPropertyException, "Trying to compute an undefined conditional value: the condition is not reachable.");

// Prepare result vector.
std::vector<ValueType> result(transitionMatrix.getRowCount(), storm::utility::infinity<ValueType>());

if (!conditionStates.empty()) {
BaierTransformedModel transformedModel =
computeBaierTransformation(env, transitionMatrix, backwardTransitions, targetStates, conditionStates, boost::none);

if (transformedModel.noTargetStates) {
storm::utility::vector::setVectorValues(result, transformedModel.beforeStates, storm::utility::zero<ValueType>());
BaierTransformedModel transformedModel =
computeBaierTransformation(env, transitionMatrix, backwardTransitions, targetStates, conditionStates, boost::none);

if (transformedModel.noTargetStates) {
storm::utility::vector::setVectorValues(result, transformedModel.beforeStates, storm::utility::zero<ValueType>());
} else {
// At this point, we do not need to check whether there are 'before' states, since the condition
// states were non-empty so there is at least one state with a positive probability of satisfying
// the condition.

// Now compute reachability probabilities in the transformed model.
storm::storage::SparseMatrix<ValueType> const& newTransitionMatrix = transformedModel.transitionMatrix.get();
storm::storage::BitVector newRelevantValues;
if (goal.hasRelevantValues()) {
newRelevantValues = transformedModel.getNewRelevantStates(goal.relevantValues());
} else {
// At this point, we do not need to check whether there are 'before' states, since the condition
// states were non-empty so there is at least one state with a positive probability of satisfying
// the condition.

// Now compute reachability probabilities in the transformed model.
storm::storage::SparseMatrix<ValueType> const& newTransitionMatrix = transformedModel.transitionMatrix.get();
storm::storage::BitVector newRelevantValues;
if (goal.hasRelevantValues()) {
newRelevantValues = transformedModel.getNewRelevantStates(goal.relevantValues());
} else {
newRelevantValues = transformedModel.getNewRelevantStates();
}
goal.setRelevantValues(std::move(newRelevantValues));
std::vector<ValueType> conditionalProbabilities = computeUntilProbabilities(
env, std::move(goal), newTransitionMatrix, newTransitionMatrix.transpose(),
storm::storage::BitVector(newTransitionMatrix.getRowCount(), true), transformedModel.targetStates.get(), qualitative);

storm::utility::vector::setVectorValues(result, transformedModel.beforeStates, conditionalProbabilities);
newRelevantValues = transformedModel.getNewRelevantStates();
}
goal.setRelevantValues(std::move(newRelevantValues));
std::vector<ValueType> conditionalProbabilities =
computeUntilProbabilities(env, std::move(goal), newTransitionMatrix, newTransitionMatrix.transpose(),
storm::storage::BitVector(newTransitionMatrix.getRowCount(), true), transformedModel.targetStates.get(), qualitative);

storm::utility::vector::setVectorValues(result, transformedModel.beforeStates, conditionalProbabilities);
}

return result;
Expand All @@ -901,34 +905,38 @@ std::vector<SolutionType> SparseDtmcPrctlHelper<ValueType, RewardModelType, Solu
if constexpr (storm::IsIntervalType<ValueType>) {
STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "We do not support computing conditional rewards with interval models.");
} else {
// The conditional value is only defined for states that reach the condition with positive probability.
storm::storage::BitVector const conditionProbGreater0States =
storm::utility::graph::performProbGreater0(backwardTransitions, storm::storage::BitVector(conditionStates.size(), true), conditionStates);
STORM_LOG_THROW(goal.hasRelevantValues() ? goal.relevantValues().isSubsetOf(conditionProbGreater0States) : !conditionProbGreater0States.empty(),
storm::exceptions::InvalidPropertyException, "Trying to compute an undefined conditional value: the condition is not reachable.");

// Prepare result vector.
std::vector<ValueType> result(transitionMatrix.getRowCount(), storm::utility::infinity<ValueType>());

if (!conditionStates.empty()) {
BaierTransformedModel transformedModel = computeBaierTransformation(env, transitionMatrix, backwardTransitions, targetStates, conditionStates,
rewardModel.getTotalRewardVector(transitionMatrix));

if (transformedModel.noTargetStates) {
storm::utility::vector::setVectorValues(result, transformedModel.beforeStates, storm::utility::zero<ValueType>());
BaierTransformedModel transformedModel = computeBaierTransformation(env, transitionMatrix, backwardTransitions, targetStates, conditionStates,
rewardModel.getTotalRewardVector(transitionMatrix));

if (transformedModel.noTargetStates) {
storm::utility::vector::setVectorValues(result, transformedModel.beforeStates, storm::utility::zero<ValueType>());
} else {
// At this point, we do not need to check whether there are 'before' states, since the condition
// states were non-empty so there is at least one state with a positive probability of satisfying
// the condition.

// Now compute reachability probabilities in the transformed model.
storm::storage::SparseMatrix<ValueType> const& newTransitionMatrix = transformedModel.transitionMatrix.get();
storm::storage::BitVector newRelevantValues;
if (goal.hasRelevantValues()) {
newRelevantValues = transformedModel.getNewRelevantStates(goal.relevantValues());
} else {
// At this point, we do not need to check whether there are 'before' states, since the condition
// states were non-empty so there is at least one state with a positive probability of satisfying
// the condition.

// Now compute reachability probabilities in the transformed model.
storm::storage::SparseMatrix<ValueType> const& newTransitionMatrix = transformedModel.transitionMatrix.get();
storm::storage::BitVector newRelevantValues;
if (goal.hasRelevantValues()) {
newRelevantValues = transformedModel.getNewRelevantStates(goal.relevantValues());
} else {
newRelevantValues = transformedModel.getNewRelevantStates();
}
goal.setRelevantValues(std::move(newRelevantValues));
std::vector<ValueType> conditionalRewards =
computeReachabilityRewards(env, std::move(goal), newTransitionMatrix, newTransitionMatrix.transpose(), transformedModel.stateRewards.get(),
transformedModel.targetStates.get(), qualitative);
storm::utility::vector::setVectorValues(result, transformedModel.beforeStates, conditionalRewards);
newRelevantValues = transformedModel.getNewRelevantStates();
}
goal.setRelevantValues(std::move(newRelevantValues));
std::vector<ValueType> conditionalRewards =
computeReachabilityRewards(env, std::move(goal), newTransitionMatrix, newTransitionMatrix.transpose(), transformedModel.stateRewards.get(),
transformedModel.targetStates.get(), qualitative);
storm::utility::vector::setVectorValues(result, transformedModel.beforeStates, conditionalRewards);
}

return result;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "storm/environment/solver/EigenSolverEnvironment.h"
#include "storm/environment/solver/GmmxxSolverEnvironment.h"
#include "storm/environment/solver/NativeSolverEnvironment.h"
#include "storm/exceptions/InvalidPropertyException.h"
#include "storm/logic/Formulas.h"
#include "storm/modelchecker/prctl/SparseDtmcPrctlModelChecker.h"
#include "storm/modelchecker/results/ExplicitQuantitativeCheckResult.h"
Expand Down Expand Up @@ -165,6 +166,10 @@ TYPED_TEST(ConditionalDtmcPrctlModelCheckerTest, Conditional) {
storm::modelchecker::ExplicitQuantitativeCheckResult<ValueType>& quantitativeResult2 = result->asExplicitQuantitativeCheckResult<ValueType>();
EXPECT_NEAR(storm::utility::one<ValueType>(), quantitativeResult2[0], this->precision());

formula = formulaParser.parseSingleFormulaFromString("P=? [F \"target\" || F \"unreachable\"]");

STORM_SILENT_EXPECT_THROW(checker.check(this->env(), *formula), storm::exceptions::InvalidPropertyException);

formula = formulaParser.parseSingleFormulaFromString("R=? [F \"target\"]");

result = checker.check(this->env(), *formula);
Expand All @@ -176,5 +181,54 @@ TYPED_TEST(ConditionalDtmcPrctlModelCheckerTest, Conditional) {
result = checker.check(this->env(), *formula);
storm::modelchecker::ExplicitQuantitativeCheckResult<ValueType>& quantitativeResult4 = result->asExplicitQuantitativeCheckResult<ValueType>();
EXPECT_NEAR(storm::utility::one<ValueType>(), quantitativeResult4[0], this->precision());

formula = formulaParser.parseSingleFormulaFromString("R=? [F \"target\" || F \"unreachable\"]");

STORM_SILENT_EXPECT_THROW(checker.check(this->env(), *formula), storm::exceptions::InvalidPropertyException);
}

TYPED_TEST(ConditionalDtmcPrctlModelCheckerTest, ConditionalUnreachableFromInitialState) {
typedef typename TestFixture::ValueType ValueType;

// In this model, the condition is reachable from one of the two initial states only.
storm::prism::Program program = storm::parser::PrismParser::parse(STORM_TEST_RESOURCES_DIR "/dtmc/test_conditional_multiple_initial.pm");

storm::generator::NextStateGeneratorOptions options;
options.setBuildAllLabels().setBuildAllRewardModels();
std::shared_ptr<storm::models::sparse::Model<ValueType>> model = storm::builder::ExplicitModelBuilder<ValueType>(program, options).build();
ASSERT_TRUE(model->getType() == storm::models::ModelType::Dtmc);
ASSERT_EQ(4ul, model->getNumberOfStates());

std::shared_ptr<storm::models::sparse::Dtmc<ValueType>> dtmc = model->template as<storm::models::sparse::Dtmc<ValueType>>();
ASSERT_EQ(2ul, dtmc->getInitialStates().getNumberOfSetBits());
storm::storage::BitVector initialStatesWithoutCondition = dtmc->getInitialStates() & ~dtmc->getStates("start");
ASSERT_EQ(1ul, initialStatesWithoutCondition.getNumberOfSetBits());

storm::modelchecker::SparseDtmcPrctlModelChecker<storm::models::sparse::Dtmc<ValueType>> checker(*dtmc);

auto expManager = std::make_shared<storm::expressions::ExpressionManager>();
storm::parser::FormulaParser formulaParser(expManager);

std::shared_ptr<storm::logic::Formula const> formula = formulaParser.parseSingleFormulaFromString("P=? [F \"target\" || F \"condition\"]");

// If we are only interested in the initial states, the value is undefined for the initial state that cannot reach the condition.
STORM_SILENT_EXPECT_THROW(checker.check(this->env(), storm::modelchecker::CheckTask<storm::logic::Formula, ValueType>(*formula, true)),
storm::exceptions::InvalidPropertyException);

// If all states are relevant, the undefined values are reported as infinity.
std::unique_ptr<storm::modelchecker::CheckResult> result = checker.check(this->env(), *formula);
storm::modelchecker::ExplicitQuantitativeCheckResult<ValueType>& quantitativeResult1 = result->asExplicitQuantitativeCheckResult<ValueType>();
EXPECT_NEAR(storm::utility::one<ValueType>(), quantitativeResult1[*dtmc->getStates("start").begin()], this->precision());
EXPECT_EQ(storm::utility::infinity<ValueType>(), quantitativeResult1[*initialStatesWithoutCondition.begin()]);

formula = formulaParser.parseSingleFormulaFromString("R=? [F \"target\" || F \"condition\"]");

STORM_SILENT_EXPECT_THROW(checker.check(this->env(), storm::modelchecker::CheckTask<storm::logic::Formula, ValueType>(*formula, true)),
storm::exceptions::InvalidPropertyException);

result = checker.check(this->env(), *formula);
storm::modelchecker::ExplicitQuantitativeCheckResult<ValueType>& quantitativeResult2 = result->asExplicitQuantitativeCheckResult<ValueType>();
EXPECT_NEAR(storm::utility::one<ValueType>(), quantitativeResult2[*dtmc->getStates("start").begin()], this->precision());
EXPECT_EQ(storm::utility::infinity<ValueType>(), quantitativeResult2[*initialStatesWithoutCondition.begin()]);
}
} // namespace
Loading