Storm 1.14.0.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
GraphTest.cpp
Go to the documentation of this file.
1#include "storm-config.h"
3#include "test/storm_gtest.h"
4
18#include "storm/utility/graph.h"
19
20class Cudd {
21 public:
22 static void checkLibraryAvailable() {
23#ifndef STORM_HAVE_CUDD
24 GTEST_SKIP() << "Library CUDD not available.";
25#endif
26 }
27
29};
30
31class Sylvan {
32 public:
33 static void checkLibraryAvailable() {
34#ifndef STORM_HAVE_SYLVAN
35 GTEST_SKIP() << "Library Sylvan not available.";
36#endif
37 }
38
40};
41
42template<typename TestType>
43class GraphTestSymbolic : public ::testing::Test {
44 public:
46
47 static const storm::dd::DdType DdType = TestType::DdType;
48
49 protected:
50 void SetUp() override {
51#ifndef STORM_HAVE_Z3
52 GTEST_SKIP() << "Library Z3 not available.";
53#endif
54 TestType::checkLibraryAvailable();
55 }
56};
57
58class GraphTestExplicit : public ::testing::Test {
59 protected:
60 void SetUp() override {
61#ifndef STORM_HAVE_Z3
62 GTEST_SKIP() << "Library Z3 not available.";
63#endif
64 }
65};
66
67typedef ::testing::Types<Cudd, Sylvan> TestingTypes;
69
70TYPED_TEST(GraphTestSymbolic, SymbolicProb01) {
71 const storm::dd::DdType DdType = TestFixture::DdType;
72 storm::storage::SymbolicModelDescription modelDescription = storm::parser::PrismParser::parse(STORM_TEST_RESOURCES_DIR "/dtmc/crowds-5-5.pm");
73 storm::prism::Program program = modelDescription.preprocess().asPrismProgram();
74 std::shared_ptr<storm::models::symbolic::Model<DdType>> model = storm::builder::DdPrismModelBuilder<DdType>().build(this->env, program);
75
76 ASSERT_TRUE(model->getType() == storm::models::ModelType::Dtmc);
77
78 {
79 // This block is necessary, so the BDDs get disposed before the manager (contained in the model).
80 std::pair<storm::dd::Bdd<DdType>, storm::dd::Bdd<DdType>> statesWithProbability01;
81
82 ASSERT_NO_THROW(statesWithProbability01 = storm::utility::graph::performProb01(*model->template as<storm::models::symbolic::Dtmc<DdType>>(),
83 model->getReachableStates(), model->getStates("observe0Greater1")));
84 EXPECT_EQ(4409ull, statesWithProbability01.first.getNonZeroCount());
85 EXPECT_EQ(1316ull, statesWithProbability01.second.getNonZeroCount());
86
87 ASSERT_NO_THROW(statesWithProbability01 = storm::utility::graph::performProb01(*model->template as<storm::models::symbolic::Dtmc<DdType>>(),
88 model->getReachableStates(), model->getStates("observeIGreater1")));
89 EXPECT_EQ(1091ull, statesWithProbability01.first.getNonZeroCount());
90 EXPECT_EQ(4802ull, statesWithProbability01.second.getNonZeroCount());
91
92 ASSERT_NO_THROW(statesWithProbability01 = storm::utility::graph::performProb01(*model->template as<storm::models::symbolic::Dtmc<DdType>>(),
93 model->getReachableStates(), model->getStates("observeOnlyTrueSender")));
94 EXPECT_EQ(5829ull, statesWithProbability01.first.getNonZeroCount());
95 EXPECT_EQ(1032ull, statesWithProbability01.second.getNonZeroCount());
96 }
97}
98
99TYPED_TEST(GraphTestSymbolic, SymbolicProb01MinMax) {
100 const storm::dd::DdType DdType = TestFixture::DdType;
101 storm::storage::SymbolicModelDescription modelDescription = storm::parser::PrismParser::parse(STORM_TEST_RESOURCES_DIR "/mdp/leader3.nm");
102 storm::prism::Program program = modelDescription.preprocess().asPrismProgram();
103 std::shared_ptr<storm::models::symbolic::Model<DdType>> model = storm::builder::DdPrismModelBuilder<DdType>().build(this->env, program);
104
105 ASSERT_TRUE(model->getType() == storm::models::ModelType::Mdp);
106
107 {
108 // This block is necessary, so the BDDs get disposed before the manager (contained in the model).
109 std::pair<storm::dd::Bdd<DdType>, storm::dd::Bdd<DdType>> statesWithProbability01;
110
111 ASSERT_NO_THROW(statesWithProbability01 = storm::utility::graph::performProb01Min(*model->template as<storm::models::symbolic::Mdp<DdType>>(),
112 model->getReachableStates(), model->getStates("elected")));
113 EXPECT_EQ(0ull, statesWithProbability01.first.getNonZeroCount());
114 EXPECT_EQ(364ull, statesWithProbability01.second.getNonZeroCount());
115
116 ASSERT_NO_THROW(statesWithProbability01 = storm::utility::graph::performProb01Max(*model->template as<storm::models::symbolic::Mdp<DdType>>(),
117 model->getReachableStates(), model->getStates("elected")));
118 EXPECT_EQ(0ull, statesWithProbability01.first.getNonZeroCount());
119 EXPECT_EQ(364ull, statesWithProbability01.second.getNonZeroCount());
120 }
121
122 modelDescription = storm::parser::PrismParser::parse(STORM_TEST_RESOURCES_DIR "/mdp/coin2-2.nm");
123 program = modelDescription.preprocess().asPrismProgram();
124 model = storm::builder::DdPrismModelBuilder<DdType>().build(this->env, program);
125
126 ASSERT_TRUE(model->getType() == storm::models::ModelType::Mdp);
127
128 {
129 // This block is necessary, so the BDDs get disposed before the manager (contained in the model).
130 std::pair<storm::dd::Bdd<DdType>, storm::dd::Bdd<DdType>> statesWithProbability01;
131
132 ASSERT_NO_THROW(statesWithProbability01 = storm::utility::graph::performProb01Min(*model->template as<storm::models::symbolic::Mdp<DdType>>(),
133 model->getReachableStates(), model->getStates("all_coins_equal_0")));
134 EXPECT_EQ(77ull, statesWithProbability01.first.getNonZeroCount());
135 EXPECT_EQ(149ull, statesWithProbability01.second.getNonZeroCount());
136
137 ASSERT_NO_THROW(statesWithProbability01 = storm::utility::graph::performProb01Max(*model->template as<storm::models::symbolic::Mdp<DdType>>(),
138 model->getReachableStates(), model->getStates("all_coins_equal_0")));
139 EXPECT_EQ(74ull, statesWithProbability01.first.getNonZeroCount());
140 EXPECT_EQ(198ull, statesWithProbability01.second.getNonZeroCount());
141
142 ASSERT_NO_THROW(statesWithProbability01 = storm::utility::graph::performProb01Min(*model->template as<storm::models::symbolic::Mdp<DdType>>(),
143 model->getReachableStates(), model->getStates("all_coins_equal_1")));
144 EXPECT_EQ(94ull, statesWithProbability01.first.getNonZeroCount());
145 EXPECT_EQ(33ull, statesWithProbability01.second.getNonZeroCount());
146
147 ASSERT_NO_THROW(statesWithProbability01 = storm::utility::graph::performProb01Max(*model->template as<storm::models::symbolic::Mdp<DdType>>(),
148 model->getReachableStates(), model->getStates("all_coins_equal_1")));
149 EXPECT_EQ(83ull, statesWithProbability01.first.getNonZeroCount());
150 EXPECT_EQ(35ull, statesWithProbability01.second.getNonZeroCount());
151 }
152
153 modelDescription = storm::parser::PrismParser::parse(STORM_TEST_RESOURCES_DIR "/mdp/csma2-2.nm");
154 program = modelDescription.preprocess().asPrismProgram();
155 model = storm::builder::DdPrismModelBuilder<DdType>().build(this->env, program);
156
157 ASSERT_TRUE(model->getType() == storm::models::ModelType::Mdp);
158
159 {
160 // This block is necessary, so the BDDs get disposed before the manager (contained in the model).
161 std::pair<storm::dd::Bdd<DdType>, storm::dd::Bdd<DdType>> statesWithProbability01;
162
163 ASSERT_NO_THROW(statesWithProbability01 =
165 model->getStates("collision_max_backoff")));
166 EXPECT_EQ(993ull, statesWithProbability01.first.getNonZeroCount());
167 EXPECT_EQ(16ull, statesWithProbability01.second.getNonZeroCount());
168
169 ASSERT_NO_THROW(statesWithProbability01 =
171 model->getStates("collision_max_backoff")));
172 EXPECT_EQ(993ull, statesWithProbability01.first.getNonZeroCount());
173 EXPECT_EQ(16ull, statesWithProbability01.second.getNonZeroCount());
174 }
175}
176
177TEST_F(GraphTestExplicit, ExplicitProb01) {
178 storm::storage::SymbolicModelDescription modelDescription = storm::parser::PrismParser::parse(STORM_TEST_RESOURCES_DIR "/dtmc/crowds-5-5.pm");
179 storm::prism::Program program = modelDescription.preprocess().asPrismProgram();
180 std::shared_ptr<storm::models::sparse::Model<double>> model =
182
183 ASSERT_TRUE(model->getType() == storm::models::ModelType::Dtmc);
184
185 std::pair<storm::storage::BitVector, storm::storage::BitVector> statesWithProbability01;
186
187 ASSERT_NO_THROW(statesWithProbability01 = storm::utility::graph::performProb01(*model->as<storm::models::sparse::Dtmc<double>>(),
188 storm::storage::BitVector(model->getNumberOfStates(), true),
189 model->getStates("observe0Greater1")));
190 EXPECT_EQ(4409ull, statesWithProbability01.first.getNumberOfSetBits());
191 EXPECT_EQ(1316ull, statesWithProbability01.second.getNumberOfSetBits());
192
193 ASSERT_NO_THROW(statesWithProbability01 = storm::utility::graph::performProb01(*model->as<storm::models::sparse::Dtmc<double>>(),
194 storm::storage::BitVector(model->getNumberOfStates(), true),
195 model->getStates("observeIGreater1")));
196 EXPECT_EQ(1091ull, statesWithProbability01.first.getNumberOfSetBits());
197 EXPECT_EQ(4802ull, statesWithProbability01.second.getNumberOfSetBits());
198
199 ASSERT_NO_THROW(statesWithProbability01 = storm::utility::graph::performProb01(*model->as<storm::models::sparse::Dtmc<double>>(),
200 storm::storage::BitVector(model->getNumberOfStates(), true),
201 model->getStates("observeOnlyTrueSender")));
202 EXPECT_EQ(5829ull, statesWithProbability01.first.getNumberOfSetBits());
203 EXPECT_EQ(1032ull, statesWithProbability01.second.getNumberOfSetBits());
204}
205
206TEST_F(GraphTestExplicit, ExplicitProb01MinMax) {
207 storm::storage::SymbolicModelDescription modelDescription = storm::parser::PrismParser::parse(STORM_TEST_RESOURCES_DIR "/mdp/leader3.nm");
208 storm::prism::Program program = modelDescription.preprocess().asPrismProgram();
209 std::shared_ptr<storm::models::sparse::Model<double>> model =
211
212 ASSERT_TRUE(model->getType() == storm::models::ModelType::Mdp);
213
214 std::pair<storm::storage::BitVector, storm::storage::BitVector> statesWithProbability01;
215
216 ASSERT_NO_THROW(statesWithProbability01 =
218 storm::storage::BitVector(model->getNumberOfStates(), true), model->getStates("elected")));
219 EXPECT_EQ(0ull, statesWithProbability01.first.getNumberOfSetBits());
220 EXPECT_EQ(364ull, statesWithProbability01.second.getNumberOfSetBits());
221
222 ASSERT_NO_THROW(statesWithProbability01 =
224 storm::storage::BitVector(model->getNumberOfStates(), true), model->getStates("elected")));
225 EXPECT_EQ(0ull, statesWithProbability01.first.getNumberOfSetBits());
226 EXPECT_EQ(364ull, statesWithProbability01.second.getNumberOfSetBits());
227
228 modelDescription = storm::parser::PrismParser::parse(STORM_TEST_RESOURCES_DIR "/mdp/coin2-2.nm");
229 program = modelDescription.preprocess().asPrismProgram();
231
232 ASSERT_TRUE(model->getType() == storm::models::ModelType::Mdp);
233
234 ASSERT_NO_THROW(statesWithProbability01 = storm::utility::graph::performProb01Min(*model->as<storm::models::sparse::Mdp<double>>(),
235 storm::storage::BitVector(model->getNumberOfStates(), true),
236 model->getStates("all_coins_equal_0")));
237 EXPECT_EQ(77ull, statesWithProbability01.first.getNumberOfSetBits());
238 EXPECT_EQ(149ull, statesWithProbability01.second.getNumberOfSetBits());
239
240 ASSERT_NO_THROW(statesWithProbability01 = storm::utility::graph::performProb01Max(*model->as<storm::models::sparse::Mdp<double>>(),
241 storm::storage::BitVector(model->getNumberOfStates(), true),
242 model->getStates("all_coins_equal_0")));
243 EXPECT_EQ(74ull, statesWithProbability01.first.getNumberOfSetBits());
244 EXPECT_EQ(198ull, statesWithProbability01.second.getNumberOfSetBits());
245
246 ASSERT_NO_THROW(statesWithProbability01 = storm::utility::graph::performProb01Min(*model->as<storm::models::sparse::Mdp<double>>(),
247 storm::storage::BitVector(model->getNumberOfStates(), true),
248 model->getStates("all_coins_equal_1")));
249 EXPECT_EQ(94ull, statesWithProbability01.first.getNumberOfSetBits());
250 EXPECT_EQ(33ull, statesWithProbability01.second.getNumberOfSetBits());
251
252 ASSERT_NO_THROW(statesWithProbability01 = storm::utility::graph::performProb01Max(*model->as<storm::models::sparse::Mdp<double>>(),
253 storm::storage::BitVector(model->getNumberOfStates(), true),
254 model->getStates("all_coins_equal_1")));
255 EXPECT_EQ(83ull, statesWithProbability01.first.getNumberOfSetBits());
256 EXPECT_EQ(35ull, statesWithProbability01.second.getNumberOfSetBits());
257
258 modelDescription = storm::parser::PrismParser::parse(STORM_TEST_RESOURCES_DIR "/mdp/csma2-2.nm");
259 program = modelDescription.preprocess().asPrismProgram();
261
262 ASSERT_TRUE(model->getType() == storm::models::ModelType::Mdp);
263
264 ASSERT_NO_THROW(statesWithProbability01 = storm::utility::graph::performProb01Min(*model->as<storm::models::sparse::Mdp<double>>(),
265 storm::storage::BitVector(model->getNumberOfStates(), true),
266 model->getStates("collision_max_backoff")));
267 EXPECT_EQ(993ull, statesWithProbability01.first.getNumberOfSetBits());
268 EXPECT_EQ(16ull, statesWithProbability01.second.getNumberOfSetBits());
269
270 ASSERT_NO_THROW(statesWithProbability01 = storm::utility::graph::performProb01Max(*model->as<storm::models::sparse::Mdp<double>>(),
271 storm::storage::BitVector(model->getNumberOfStates(), true),
272 model->getStates("collision_max_backoff")));
273 EXPECT_EQ(993ull, statesWithProbability01.first.getNumberOfSetBits());
274 EXPECT_EQ(16ull, statesWithProbability01.second.getNumberOfSetBits());
275}
276namespace {
280storm::storage::SparseMatrix<double> buildProb1ETestModel(std::vector<uint_fast64_t>& rowGroupIndices) {
281 uint64_t const numberOfStates = 10;
282 storm::storage::SparseMatrixBuilder<double> builder(0, numberOfStates, 0, false, true, numberOfStates);
283 auto newState = [&builder, &rowGroupIndices](uint64_t row) {
284 rowGroupIndices.push_back(row);
285 builder.newRowGroup(row);
286 };
287 newState(0); // s0
288 builder.addNextValue(0, 1, 1.0);
289 newState(1); // s1
290 builder.addNextValue(1, 2, 1.0);
291 newState(2); // s2: leaving the safe states with positive probability or staying put forever
292 builder.addNextValue(2, 3, 0.5);
293 builder.addNextValue(2, 6, 0.5);
294 builder.addNextValue(3, 2, 1.0);
295 newState(4); // s3: one choice towards the goal and one towards s6
296 builder.addNextValue(4, 4, 1.0);
297 builder.addNextValue(5, 6, 1.0);
298 newState(6); // s4
299 builder.addNextValue(6, 5, 1.0);
300 newState(7); // s5
301 builder.addNextValue(7, 5, 1.0);
302 newState(8); // s6
303 builder.addNextValue(8, 6, 1.0);
304 newState(9); // s7: start of a chain that can only end up in s6
305 builder.addNextValue(9, 8, 1.0);
306 newState(10); // s8
307 builder.addNextValue(10, 9, 1.0);
308 newState(11); // s9
309 builder.addNextValue(11, 6, 1.0);
310 rowGroupIndices.push_back(12);
311 return builder.build(12, numberOfStates, numberOfStates);
312}
313
314storm::storage::BitVector asBitVector(uint64_t size, std::vector<uint64_t> const& setIndices) {
315 return storm::storage::BitVector(size, setIndices);
316}
317} // namespace
318
319TEST(GraphTestExplicitProb1E, SafetyAndReachabilityAlternation) {
320 std::vector<uint_fast64_t> rowGroupIndices;
321 auto matrix = buildProb1ETestModel(rowGroupIndices);
322 auto backwardTransitions = matrix.transpose(true);
323 // s6 is the only state that does not satisfy phi, s5 is the goal.
324 auto phiStates = ~asBitVector(10, {6});
325 auto psiStates = asBitVector(10, {5});
326
327 // s7, s8 and s9 are removed one after the other since they can only reach s6.
328 // Afterwards, s2 still has a choice that stays in the candidates (the self loop), but it can not reach s5 anymore.
329 // Removing s2 for that reason in turn makes s1 and then s0 unsafe.
330 auto result = storm::utility::graph::performProb1E(matrix, rowGroupIndices, backwardTransitions, phiStates, psiStates);
331 EXPECT_EQ(asBitVector(10, {3, 4, 5}), result);
332
333 // performProb01Max has to agree, in particular since it narrows down the phi states before calling performProb1E.
334 auto prob01 = storm::utility::graph::performProb01Max(matrix, rowGroupIndices, backwardTransitions, phiStates, psiStates);
335 EXPECT_EQ(asBitVector(10, {6, 7, 8, 9}), prob01.first);
336 EXPECT_EQ(result, prob01.second);
337}
338
339TEST(GraphTestExplicitProb1E, ChoiceConstraint) {
340 std::vector<uint_fast64_t> rowGroupIndices;
341 auto matrix = buildProb1ETestModel(rowGroupIndices);
342 auto backwardTransitions = matrix.transpose(true);
343 auto phiStates = ~asBitVector(10, {6});
344 auto psiStates = asBitVector(10, {5});
345
346 // Disabling row 4 takes away the only choice of s3 that reaches the goal, so s3 is removed as well.
347 auto choiceConstraint = ~asBitVector(matrix.getRowCount(), {4});
348 auto result = storm::utility::graph::performProb1E(matrix, rowGroupIndices, backwardTransitions, phiStates, psiStates, choiceConstraint);
349 EXPECT_EQ(asBitVector(10, {4, 5}), result);
350
351 // Disabling the self loop of s2 (row 3) does not change the result, it only removes s2 for a different reason.
352 choiceConstraint = ~asBitVector(matrix.getRowCount(), {3});
353 result = storm::utility::graph::performProb1E(matrix, rowGroupIndices, backwardTransitions, phiStates, psiStates, choiceConstraint);
354 EXPECT_EQ(asBitVector(10, {3, 4, 5}), result);
355}
356
357TEST(GraphTestExplicitProb1E, PsiStatesOutsidePhiStates) {
358 std::vector<uint_fast64_t> rowGroupIndices;
359 auto matrix = buildProb1ETestModel(rowGroupIndices);
360 auto backwardTransitions = matrix.transpose(true);
361 // Now s6 is the goal, but it is still not a phi state.
362 auto phiStates = ~asBitVector(10, {6});
363 auto psiStates = asBitVector(10, {6});
364
365 // s3 reaches s6 via row 5, so s2 reaches it under every outcome of row 2, and so do s1 and s0.
366 // s4 and s5 can never reach s6, which also takes away the choice of s3 that leads to s4.
367 auto result = storm::utility::graph::performProb1E(matrix, rowGroupIndices, backwardTransitions, phiStates, psiStates);
368 EXPECT_EQ(asBitVector(10, {0, 1, 2, 3, 6, 7, 8, 9}), result);
369}
static void checkLibraryAvailable()
Definition GraphTest.cpp:22
static const storm::dd::DdType DdType
Definition GraphTest.cpp:33
void SetUp() override
Definition GraphTest.cpp:60
void SetUp() override
Definition GraphTest.cpp:50
static const storm::dd::DdType DdType
Definition GraphTest.cpp:47
storm::Environment env
Definition GraphTest.cpp:45
static const storm::dd::DdType DdType
Definition GraphTest.cpp:44
static void checkLibraryAvailable()
Definition GraphTest.cpp:33
std::shared_ptr< storm::models::symbolic::Model< Type, ValueType > > build(storm::Environment const &env, storm::prism::Program const &program, Options const &options=Options())
Translates the given program into a symbolic model (i.e.
std::shared_ptr< storm::models::sparse::Model< ValueType, RewardModelType > > build()
Convert the program given at construction time to an abstract model.
This class represents a discrete-time Markov chain.
Definition Dtmc.h:16
This class represents a (discrete-time) Markov decision process.
Definition Mdp.h:16
This class represents a discrete-time Markov chain.
Definition Dtmc.h:13
This class represents a discrete-time Markov decision process.
Definition Mdp.h:13
storm::dd::Bdd< Type > const & getReachableStates() const
Retrieves the reachable states of the model.
Definition Model.cpp:99
static storm::prism::Program parse(std::string const &filename, bool prismCompatability=false)
Parses the given file into the PRISM storage classes assuming it complies with the PRISM syntax.
A bit vector that is internally represented as a vector of 64-bit values.
Definition BitVector.h:16
A class that can be used to build a sparse matrix by adding value by value.
A class that holds a possibly non-square matrix in the compressed row storage format.
storm::prism::Program const & asPrismProgram() const
SymbolicModelDescription preprocess(std::string const &constantDefinitionString="") const
storm::builder::BuilderOptions NextStateGeneratorOptions
std::pair< storm::storage::BitVector, storm::storage::BitVector > performProb01(storm::models::sparse::DeterministicModel< T > const &model, storm::storage::BitVector const &phiStates, storm::storage::BitVector const &psiStates)
Computes the sets of states that have probability 0 or 1, respectively, of satisfying phi until psi i...
Definition graph.cpp:394
std::pair< storm::storage::BitVector, storm::storage::BitVector > performProb01Max(storm::storage::SparseMatrix< T > const &transitionMatrix, std::vector< uint_fast64_t > const &nondeterministicChoiceIndices, storm::storage::SparseMatrix< T > const &backwardTransitions, storm::storage::BitVector const &phiStates, storm::storage::BitVector const &psiStates)
Definition graph.cpp:855
std::pair< storm::storage::BitVector, storm::storage::BitVector > performProb01Min(storm::storage::SparseMatrix< T > const &transitionMatrix, std::vector< uint_fast64_t > const &nondeterministicChoiceIndices, storm::storage::SparseMatrix< T > const &backwardTransitions, storm::storage::BitVector const &phiStates, storm::storage::BitVector const &psiStates)
Definition graph.cpp:1101
storm::storage::BitVector performProb1E(storm::storage::SparseMatrix< T > const &transitionMatrix, std::vector< uint_fast64_t > const &nondeterministicChoiceIndices, storm::storage::SparseMatrix< T > const &backwardTransitions, storm::storage::BitVector const &phiStates, storm::storage::BitVector const &psiStates, boost::optional< storm::storage::BitVector > const &choiceConstraint)
Computes the sets of states that have probability 1 of satisfying phi until psi under at least one po...
Definition graph.cpp:740
TYPED_TEST(GraphTestAR, SymbolicProb01StochasticGameDieSmall)
Definition GraphTest.cpp:64
TYPED_TEST_SUITE(GraphTestAR, TestingTypes,)
::testing::Types< Cudd, Sylvan > TestingTypes
Definition GraphTest.cpp:61
TEST(GraphTestExplicitProb1E, SafetyAndReachabilityAlternation)
TEST_F(GraphTestExplicit, ExplicitProb01)