Storm 1.11.1.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
JaniLocalEliminatorTests.cpp
Go to the documentation of this file.
1#include "storm-config.h"
2#include "test/storm_gtest.h"
3
7#include "storm/api/storm.h"
21
26
29
30// The tests are grouped into these groups
31// * Input behaviour
32// * Common functionality
33// * Unfolding
34// * Elimination
35// * Rebuilding without unreachable locations
36// Input behaviour tests whether the basic public interface of the eliminator works correctly, whereas
37// common functionality mainly refers to internal functions that are used in multiple places.
38
39// **************
40// Helper functions
41// **************
42
43class JaniLocalElimination : public ::testing::Test {
44 protected:
45 void SetUp() override {
46#ifndef STORM_HAVE_Z3
47 GTEST_SKIP() << "Z3 not available.";
48#endif
49 }
50};
51
52// As many tests rely on building and checking a model (which requires quite a few lines of code), we use this common
53// helper function to do the model checking. Apart from this, the tests don't share any helper functions.
54void checkModel(storm::jani::Model model, std::vector<storm::jani::Property> properties,
55 std::map<storm::expressions::Variable, storm::expressions::Expression> consts, double expectedValue) {
56 model = model.defineUndefinedConstants(consts);
57 properties[0] = properties[0].substitute(consts);
58
59 auto formulae = storm::api::extractFormulasFromProperties(properties);
60 storm::builder::BuilderOptions options(formulae, model);
61 options.setBuildAllLabels(true);
62 auto explicitModel = storm::api::buildSparseModel<double>(model, options)->template as<Dtmc>();
63
64 auto task = storm::modelchecker::CheckTask<>(*(formulae[0]), true);
66 auto checkResult = storm::api::verifyWithSparseEngine<double>(env, explicitModel, task);
67 auto quantResult = checkResult->asExplicitQuantitativeCheckResult<double>();
68
69 auto initialStates = explicitModel->getInitialStates();
70 EXPECT_EQ(1u, initialStates.getNumberOfSetBits());
71 for (auto state = initialStates.begin(); state != initialStates.end(); ++state) {
72 EXPECT_NEAR(expectedValue, quantResult[*state], storm::settings::getModule<storm::settings::modules::GeneralSettings>().getPrecision());
73 }
74}
75
76// *******************************
77// Tests for input behaviour:
78// *******************************
79
80// This test verifies that an error is produced if the type of property is not supported and that no error is thrown
81// when a supported property type is provided.
82TEST_F(JaniLocalElimination, PropertyTypeTest) {
83 // Load a model (the model contains two variables x and y, but doesn't do anything with them).
84 auto model =
85 storm::api::parseJaniModel(STORM_TEST_RESOURCES_DIR "/localeliminator/do_nothing.jani", storm::jani::getAllKnownModelFeatures(), boost::none).first;
86 storm::parser::FormulaParser formulaParser(model.getExpressionManager().shared_from_this());
87
88 {
89 // This should fail because we only support F and R as top-level operators
90 std::shared_ptr<storm::logic::Formula const> formula = formulaParser.parseSingleFormulaFromString("S=? [ true ]");
91 auto property = storm::jani::Property("steady_state", formula, std::set<storm::expressions::Variable>());
92 STORM_SILENT_EXPECT_THROW(JaniLocalEliminator(model, property), storm::exceptions::NotSupportedException);
93 }
94 {
95 // This should fail because we only support reachability formulas
96 std::shared_ptr<storm::logic::Formula const> formula = formulaParser.parseSingleFormulaFromString("P=? [G x=1]");
97 auto property = storm::jani::Property("generally", formula, std::set<storm::expressions::Variable>());
98 STORM_SILENT_EXPECT_THROW(JaniLocalEliminator(model, property), storm::exceptions::NotSupportedException);
99 }
100 {
101 // This should fail because we only support reachability formulas (this test is mainly there because of the
102 // similarity between eventually and until formulas)
103 std::shared_ptr<storm::logic::Formula const> formula = formulaParser.parseSingleFormulaFromString("P=? [x=1 U y=1]");
104 auto property = storm::jani::Property("until", formula, std::set<storm::expressions::Variable>());
105 STORM_SILENT_EXPECT_THROW(JaniLocalEliminator(model, property), storm::exceptions::NotSupportedException);
106 }
107 {
108 // This should succeed because reachability probabilities are supported
109 std::shared_ptr<storm::logic::Formula const> formula = formulaParser.parseSingleFormulaFromString("P=? [ F x=1 ]");
110 auto property = storm::jani::Property("reachability", formula, std::set<storm::expressions::Variable>());
111 EXPECT_NO_THROW(JaniLocalEliminator(model, property));
112 }
113 {
114 // This should succeed because reachability probabilities are supported
115 std::shared_ptr<storm::logic::Formula const> formula = formulaParser.parseSingleFormulaFromString("R=? [ F x=1 ]");
116 auto property = storm::jani::Property("reward_reachability", formula, std::set<storm::expressions::Variable>());
117 EXPECT_NO_THROW(JaniLocalEliminator(model, property));
118 }
119}
120
121// This test verifies that an error is given if no properties are provided.
122TEST_F(JaniLocalElimination, NoPropertiesTest) {
123 auto model =
124 storm::api::parseJaniModel(STORM_TEST_RESOURCES_DIR "/localeliminator/do_nothing.jani", storm::jani::getAllKnownModelFeatures(), boost::none).first;
125 std::vector<storm::jani::Property> empty_properties;
126 STORM_SILENT_ASSERT_THROW(JaniLocalEliminator(model, empty_properties), storm::exceptions::InvalidArgumentException);
127}
128
129// This test verifies that the model is flattened if it has more than one automaton and that the user is informed
130// of this.
131TEST_F(JaniLocalElimination, FlatteningTest) {
132 auto modelAndProps =
133 storm::api::parseJaniModel(STORM_TEST_RESOURCES_DIR "/localeliminator/two_modules.jani", storm::jani::getAllKnownModelFeatures(), boost::none);
134 auto eliminator = JaniLocalEliminator(modelAndProps.first, modelAndProps.second);
135 eliminator.eliminate();
136 auto result = eliminator.getResult();
137 EXPECT_EQ(1u, result.getNumberOfAutomata());
138}
139
140// *******************************
141// Tests for common functionality:
142// *******************************
143
144// This test verifies that missing guard completion works correctly. It should not alter the behaviour of the model
145// and ensure that every location has an outgoing edge for every possible case.
146TEST_F(JaniLocalElimination, MissingGuardCompletion) {
147 // In this model, s_1 has a missing guard (the case x=0 is not covered). Thus, if we eliminate s_1 without
148 // taking additional precautions, this missing guard will propagate to s_0, which causes an incorrect result.
149 // If we activate the addMissingGuards option of the eliminator, this will be avoided.
150
151 auto modelAndProps =
152 storm::api::parseJaniModel(STORM_TEST_RESOURCES_DIR "/localeliminator/missing_guard.jani", storm::jani::getAllKnownModelFeatures(), boost::none);
153 auto eliminator = JaniLocalEliminator(modelAndProps.first, modelAndProps.second, true);
154 eliminator.scheduler.addAction(std::make_unique<UnfoldAction>("main", "s"));
155 eliminator.scheduler.addAction(std::make_unique<EliminateAction>("main", "l_s_1"));
156 eliminator.eliminate();
157 auto result = eliminator.getResult();
158 EXPECT_EQ(1u, result.getNumberOfAutomata());
159 checkModel(result, modelAndProps.second, std::map<storm::expressions::Variable, storm::expressions::Expression>(), 0.5);
160}
161
162// This test verifies that locations with loops are correctly identified.
164 auto model = storm::api::parseJaniModel(STORM_TEST_RESOURCES_DIR "/localeliminator/loops.jani", storm::jani::getAllKnownModelFeatures(), boost::none).first;
165
166 storm::parser::FormulaParser formulaParser(model.getExpressionManager().shared_from_this());
167 std::shared_ptr<storm::logic::Formula const> formula = formulaParser.parseSingleFormulaFromString("P=? [F x=1]");
168 auto property = storm::jani::Property("prop", formula, std::set<storm::expressions::Variable>());
169
170 // TODO: To access the internals, I replicate some of the internal behaviour of the JaniLocalEliminator. This should probably be done in a neater way by
171 // exposing the relevant bits publicly.
172
173 auto session = JaniLocalEliminator::Session(model, property);
174 UnfoldAction unfoldAction("main", "s");
175 unfoldAction.doAction(session);
176
177 EXPECT_FALSE(session.hasLoops("main", "l_s_0"));
178 EXPECT_TRUE(session.hasLoops("main", "l_s_1"));
179 EXPECT_TRUE(session.hasLoops("main", "l_s_2"));
180 EXPECT_FALSE(session.hasLoops("main", "l_s_3"));
181 EXPECT_TRUE(session.hasLoops("main", "l_s_4"));
182 EXPECT_TRUE(session.hasLoops("main", "l_s_5"));
183 EXPECT_FALSE(session.hasLoops("main", "l_s_6"));
184}
185
186// This test verifies that locations that can potentially satisfy the property are correctly identified.
187TEST_F(JaniLocalElimination, IsPartOfPropComputation) {
188 auto model =
189 storm::api::parseJaniModel(STORM_TEST_RESOURCES_DIR "/localeliminator/three_variables.jani", storm::jani::getAllKnownModelFeatures(), boost::none)
190 .first;
191
192 storm::parser::FormulaParser formulaParser(model.getExpressionManager().shared_from_this());
193 std::shared_ptr<storm::logic::Formula const> formula = formulaParser.parseSingleFormulaFromString("P=? [F (x=1 & (y>3 | z<2))]");
194 auto property = storm::jani::Property("prop", formula, std::set<storm::expressions::Variable>());
195
196 // TODO: To access the internals, I replicate some of the internal behaviour of the JaniLocalEliminator. This should probably be done in a neater way by
197 // exposing the relevant bits publicly.
198
199 auto session = JaniLocalEliminator::Session(model, property);
200 EXPECT_TRUE(session.computeIsPartOfProp("main", "l"));
201
202 UnfoldAction unfoldActionX("main", "x");
203 unfoldActionX.doAction(session);
204
205 EXPECT_FALSE(session.computeIsPartOfProp("main", "l_x_0"));
206 EXPECT_TRUE(session.computeIsPartOfProp("main", "l_x_1"));
207 EXPECT_FALSE(session.computeIsPartOfProp("main", "l_x_2"));
208
209 UnfoldAction unfoldActionY("main", "y");
210 unfoldActionY.doAction(session);
211
212 EXPECT_TRUE(session.computeIsPartOfProp("main", "l_x_1_y_4"));
213 EXPECT_TRUE(session.computeIsPartOfProp("main", "l_x_1_y_3"));
214 EXPECT_FALSE(session.computeIsPartOfProp("main", "l_x_2_y_4"));
215
216 UnfoldAction unfoldActionZ("main", "z");
217 unfoldActionZ.doAction(session);
218
219 EXPECT_FALSE(session.computeIsPartOfProp("main", "l_x_0_y_2_z_3"));
220 EXPECT_FALSE(session.computeIsPartOfProp("main", "l_x_0_y_2_z_1"));
221 EXPECT_FALSE(session.computeIsPartOfProp("main", "l_x_0_y_5_z_3"));
222 EXPECT_FALSE(session.computeIsPartOfProp("main", "l_x_0_y_5_z_1"));
223 EXPECT_FALSE(session.computeIsPartOfProp("main", "l_x_1_y_2_z_3"));
224 EXPECT_TRUE(session.computeIsPartOfProp("main", "l_x_1_y_2_z_1"));
225 EXPECT_TRUE(session.computeIsPartOfProp("main", "l_x_1_y_5_z_3"));
226 EXPECT_TRUE(session.computeIsPartOfProp("main", "l_x_1_y_5_z_1"));
227 EXPECT_TRUE(session.computeIsPartOfProp("main", "l_x_1_y_4_z_1"));
228 EXPECT_TRUE(session.computeIsPartOfProp("main", "l_x_1_y_5_z_0"));
229}
230
231// This test verifies that the initial location is correctly identified.
232TEST_F(JaniLocalElimination, IsInitialDetection) {
233 auto model =
234 storm::api::parseJaniModel(STORM_TEST_RESOURCES_DIR "/localeliminator/initial_locations.jani", storm::jani::getAllKnownModelFeatures(), boost::none)
235 .first;
236
237 storm::parser::FormulaParser formulaParser(model.getExpressionManager().shared_from_this());
238 std::shared_ptr<storm::logic::Formula const> formula = formulaParser.parseSingleFormulaFromString("P=? [F x=1]");
239 auto property = storm::jani::Property("prop", formula, std::set<storm::expressions::Variable>());
240
241 auto session = JaniLocalEliminator::Session(model, property);
242
243 EXPECT_TRUE(session.isPossiblyInitial("main", "l"));
244
245 UnfoldAction unfoldActionX("main", "x");
246 unfoldActionX.doAction(session);
247
248 EXPECT_FALSE(session.isPossiblyInitial("main", "l_x_0"));
249 EXPECT_FALSE(session.isPossiblyInitial("main", "l_x_1"));
250 EXPECT_TRUE(session.isPossiblyInitial("main", "l_x_2"));
251
252 UnfoldAction unfoldActionY("main", "y");
253 unfoldActionY.doAction(session);
254
255 EXPECT_TRUE(session.isPossiblyInitial("main", "l_x_2_y_-1"));
256 EXPECT_FALSE(session.isPossiblyInitial("main", "l_x_2_y_0"));
257 EXPECT_FALSE(session.isPossiblyInitial("main", "l_x_0_y_0"));
258 EXPECT_FALSE(session.isPossiblyInitial("main", "l_x_0_y_-1"));
259
260 UnfoldAction unfoldActionZ("main", "z");
261 unfoldActionZ.doAction(session);
262
263 EXPECT_TRUE(session.isPossiblyInitial("main", "l_x_2_y_-1_z_0"));
264 EXPECT_FALSE(session.isPossiblyInitial("main", "l_x_2_y_-1_z_1"));
265 EXPECT_FALSE(session.isPossiblyInitial("main", "l_x_2_y_0_z_0"));
266 EXPECT_FALSE(session.isPossiblyInitial("main", "l_x_1_y_-1_z_0"));
267
268 UnfoldAction unfoldActionA("main", "a");
269 unfoldActionA.doAction(session);
270
271 EXPECT_TRUE(session.isPossiblyInitial("main", "l_x_2_y_-1_z_0_a_true"));
272 EXPECT_FALSE(session.isPossiblyInitial("main", "l_x_2_y_-1_z_0_a_false"));
273 EXPECT_FALSE(session.isPossiblyInitial("main", "l_x_0_y_-1_z_0_a_true"));
274
275 UnfoldAction unfoldActionB("main", "b");
276 unfoldActionB.doAction(session);
277
278 EXPECT_TRUE(session.isPossiblyInitial("main", "l_x_2_y_-1_z_0_a_true_b_false"));
279 EXPECT_FALSE(session.isPossiblyInitial("main", "l_x_2_y_-1_z_0_a_true_b_true"));
280 EXPECT_FALSE(session.isPossiblyInitial("main", "l_x_2_y_0_z_0_a_true_b_true"));
281
282 UnfoldAction unfoldActionC("main", "c");
283 unfoldActionC.doAction(session);
284
285 EXPECT_TRUE(session.isPossiblyInitial("main", "l_x_2_y_-1_z_0_a_true_b_false_c_false"));
286 EXPECT_FALSE(session.isPossiblyInitial("main", "l_x_2_y_-1_z_0_a_true_b_false_c_true"));
287}
288
289// This test verifies that the eliminable locations are correctly identified.
290TEST_F(JaniLocalElimination, IsEliminableDetection) {
291 auto model = storm::api::parseJaniModel(STORM_TEST_RESOURCES_DIR "/localeliminator/uneliminable_locations.jani", storm::jani::getAllKnownModelFeatures(),
292 boost::none)
293 .first;
294
295 storm::parser::FormulaParser formulaParser(model.getExpressionManager().shared_from_this());
296 std::shared_ptr<storm::logic::Formula const> formula = formulaParser.parseSingleFormulaFromString("P=? [F s=4]");
297 auto property = storm::jani::Property("prop", formula, std::set<storm::expressions::Variable>());
298
299 auto session = JaniLocalEliminator::Session(model, property);
300
301 EXPECT_FALSE(session.isEliminable("main", "l"));
302
303 UnfoldAction unfoldActionX("main", "s");
304 unfoldActionX.doAction(session);
305
306 EXPECT_FALSE(session.isEliminable("main", "l_s_0"));
307 EXPECT_FALSE(session.isEliminable("main", "l_s_1"));
308 EXPECT_TRUE(session.isEliminable("main", "l_s_2"));
309 EXPECT_TRUE(session.isEliminable("main", "l_s_3"));
310 EXPECT_FALSE(session.isEliminable("main", "l_s_4"));
311 EXPECT_FALSE(session.isEliminable("main", "l_s_5"));
312}
313
314// This test verifies that the set of variables that make up the property is correctly identified.
315TEST_F(JaniLocalElimination, IsVariablePartOfProperty) {
316 auto model =
317 storm::api::parseJaniModel(STORM_TEST_RESOURCES_DIR "/localeliminator/do_nothing.jani", storm::jani::getAllKnownModelFeatures(), boost::none).first;
318 {
319 storm::parser::FormulaParser formulaParser(model.getExpressionManager().shared_from_this());
320 std::shared_ptr<storm::logic::Formula const> formula = formulaParser.parseSingleFormulaFromString("P=? [F x=1]");
321 auto property = storm::jani::Property("prop", formula, std::set<storm::expressions::Variable>());
322 auto session = JaniLocalEliminator::Session(model, property);
323 EXPECT_TRUE(session.isVariablePartOfProperty("x"));
324 EXPECT_FALSE(session.isVariablePartOfProperty("y"));
325 }
326 {
327 storm::parser::FormulaParser formulaParser(model.getExpressionManager().shared_from_this());
328 std::shared_ptr<storm::logic::Formula const> formula = formulaParser.parseSingleFormulaFromString("P=? [F x=1 & y=1]");
329 auto property = storm::jani::Property("prop", formula, std::set<storm::expressions::Variable>());
330 auto session = JaniLocalEliminator::Session(model, property);
331 EXPECT_TRUE(session.isVariablePartOfProperty("x"));
332 EXPECT_TRUE(session.isVariablePartOfProperty("y"));
333 }
334}
335
336// *******************
337// Tests for unfolding
338// *******************
339
340// This test verifies that unfolding a bounded integer works correctly
341TEST_F(JaniLocalElimination, UnfoldingBoundedInteger) {
342 auto model = storm::api::parseJaniModel(STORM_TEST_RESOURCES_DIR "/localeliminator/simple_bounded_integer_unfolding.jani",
344 .first;
345 storm::parser::FormulaParser formulaParser(model.getExpressionManager().shared_from_this());
346 std::shared_ptr<storm::logic::Formula const> formula = formulaParser.parseSingleFormulaFromString("P=? [F x=1]");
347 auto property = storm::jani::Property("prop", formula, std::set<storm::expressions::Variable>());
348
349 auto eliminator = JaniLocalEliminator(model, property, false);
350 eliminator.scheduler.addAction(std::make_unique<UnfoldAction>("main", "x"));
351 eliminator.eliminate();
352 auto result = eliminator.getResult();
353 EXPECT_EQ(5u, result.getAutomaton(0).getNumberOfLocations());
354 checkModel(result, {property}, std::map<storm::expressions::Variable, storm::expressions::Expression>(), 0.25);
355}
356
357// This test verifies that unfolding a boolean works correctly
358TEST_F(JaniLocalElimination, UnfoldingBoolean) {
359 auto model =
360 storm::api::parseJaniModel(STORM_TEST_RESOURCES_DIR "/localeliminator/simple_bool_unfolding.jani", storm::jani::getAllKnownModelFeatures(), boost::none)
361 .first;
362 storm::parser::FormulaParser formulaParser(model.getExpressionManager().shared_from_this());
363 std::shared_ptr<storm::logic::Formula const> formula = formulaParser.parseSingleFormulaFromString("P=? [F (!x)]");
364 auto property = storm::jani::Property("prop", formula, std::set<storm::expressions::Variable>());
365
366 auto eliminator = JaniLocalEliminator(model, property, false);
367 eliminator.scheduler.addAction(std::make_unique<UnfoldAction>("main", "x"));
368 eliminator.eliminate();
369 auto result = eliminator.getResult();
370 EXPECT_EQ(2u, result.getAutomaton(0).getNumberOfLocations());
371 checkModel(result, {property}, std::map<storm::expressions::Variable, storm::expressions::Expression>(), 0.875);
372}
373
374// This test verifies that trying to unfold a variable that has already been unfolded produces an error
375TEST_F(JaniLocalElimination, UnfoldingTwice) {
376 auto model = storm::api::parseJaniModel(STORM_TEST_RESOURCES_DIR "/localeliminator/simple_bounded_integer_unfolding.jani",
378 .first;
379 storm::parser::FormulaParser formulaParser(model.getExpressionManager().shared_from_this());
380 std::shared_ptr<storm::logic::Formula const> formula = formulaParser.parseSingleFormulaFromString("P=? [F x=1]");
381 auto property = storm::jani::Property("prop", formula, std::set<storm::expressions::Variable>());
382
383 auto eliminator = JaniLocalEliminator(model, property, false);
384 eliminator.scheduler.addAction(std::make_unique<UnfoldAction>("main", "x"));
385 eliminator.scheduler.addAction(std::make_unique<UnfoldAction>("main", "x"));
386 STORM_SILENT_EXPECT_THROW(eliminator.eliminate(), storm::exceptions::InvalidOperationException);
387}
388
389// This test verifies that the sink isn't duplicated during unfolding and that the correct location is marked as sink
390TEST_F(JaniLocalElimination, UnfoldingWithSink) {
391 auto modelAndProps =
392 storm::api::parseJaniModel(STORM_TEST_RESOURCES_DIR "/localeliminator/missing_guard.jani", storm::jani::getAllKnownModelFeatures(), boost::none);
393 auto eliminator = JaniLocalEliminator(modelAndProps.first, modelAndProps.second, true);
394 eliminator.scheduler.addAction(std::make_unique<UnfoldAction>("main", "s"));
395 eliminator.eliminate();
396 auto result = eliminator.getResult();
397 EXPECT_EQ(5u, result.getAutomaton(0).getNumberOfLocations());
398}
399
400// This test verifies that whether a location can potentially satisfy the property is correctly propagated to the new
401// locations during unfolding.
402TEST_F(JaniLocalElimination, IsPartOfPropPropagationUnfolding) {
403 // TODO
404}
405
406// *********************
407// Tests for elimination
408// *********************
409
410// This test verifies that combining the two guards during elimination works correctly.
411TEST_F(JaniLocalElimination, EliminationNewGuardTest) {
412 auto model =
413 storm::api::parseJaniModel(STORM_TEST_RESOURCES_DIR "/localeliminator/simple_guards.jani", storm::jani::getAllKnownModelFeatures(), boost::none).first;
414 storm::parser::FormulaParser formulaParser(model.getExpressionManager().shared_from_this());
415 std::shared_ptr<storm::logic::Formula const> formula = formulaParser.parseSingleFormulaFromString("P=? [F c=2]");
416 auto property = storm::jani::Property("prop", formula, std::set<storm::expressions::Variable>());
417
418 auto eliminator = JaniLocalEliminator(model, property, false);
419 eliminator.scheduler.addAction(std::make_unique<UnfoldAction>("main", "c"));
420 eliminator.scheduler.addAction(std::make_unique<EliminateAction>("main", "l_c_1"));
421 eliminator.scheduler.addAction(std::make_unique<RebuildWithoutUnreachableAction>());
422 eliminator.eliminate();
423 auto result = eliminator.getResult();
424
425 EXPECT_EQ(2u, result.getAutomaton(0).getNumberOfLocations());
426 checkModel(result, {property}, std::map<storm::expressions::Variable, storm::expressions::Expression>(), 1.0);
427}
428
429// This test verifies that combining the two probabilities during elimination works correctly.
430TEST_F(JaniLocalElimination, EliminationNewProbabilityTest) {
431 auto model =
432 storm::api::parseJaniModel(STORM_TEST_RESOURCES_DIR "/localeliminator/simple_probabilities.jani", storm::jani::getAllKnownModelFeatures(), boost::none)
433 .first;
434 storm::parser::FormulaParser formulaParser(model.getExpressionManager().shared_from_this());
435 std::shared_ptr<storm::logic::Formula const> formula = formulaParser.parseSingleFormulaFromString("P=? [F c=2]");
436 auto property = storm::jani::Property("prop", formula, std::set<storm::expressions::Variable>());
437
438 auto eliminator = JaniLocalEliminator(model, property, false);
439 eliminator.scheduler.addAction(std::make_unique<UnfoldAction>("main", "c"));
440 eliminator.scheduler.addAction(std::make_unique<EliminateAction>("main", "l_c_1"));
441 eliminator.scheduler.addAction(std::make_unique<RebuildWithoutUnreachableAction>());
442 eliminator.eliminate();
443 auto result = eliminator.getResult();
444
445 EXPECT_EQ(3u, result.getAutomaton(0).getNumberOfLocations());
446 checkModel(result, {property}, std::map<storm::expressions::Variable, storm::expressions::Expression>(), 1.0 / 12.0);
447}
448
449// This test verifies that generating a new assignment block from two existing ones works correctly.
450TEST_F(JaniLocalElimination, EliminationNewUpdatesTest) {
451 auto model =
452 storm::api::parseJaniModel(STORM_TEST_RESOURCES_DIR "/localeliminator/simple_assignments.jani", storm::jani::getAllKnownModelFeatures(), boost::none)
453 .first;
454 storm::parser::FormulaParser formulaParser(model.getExpressionManager().shared_from_this());
455 std::shared_ptr<storm::logic::Formula const> formula = formulaParser.parseSingleFormulaFromString("P=? [F c=2&x=3&y=5&z=4&w=2]");
456 auto property = storm::jani::Property("prop", formula, std::set<storm::expressions::Variable>());
457
458 auto eliminator = JaniLocalEliminator(model, property, false);
459 eliminator.scheduler.addAction(std::make_unique<UnfoldAction>("main", "c"));
460 eliminator.scheduler.addAction(std::make_unique<EliminateAction>("main", "l_c_1"));
461 eliminator.scheduler.addAction(std::make_unique<RebuildWithoutUnreachableAction>());
462 eliminator.eliminate();
463 auto result = eliminator.getResult();
464
465 EXPECT_EQ(2u, result.getAutomaton(0).getNumberOfLocations());
466 EXPECT_EQ(4u, result.getAutomaton(0).getEdgesFromLocation(0).begin()->getDestination(0).getOrderedAssignments().getNumberOfAssignments());
467 checkModel(result, {property}, std::map<storm::expressions::Variable, storm::expressions::Expression>(), 1.0);
468}
469
470// This test tests the elimination process if multiple edges are incoming and outgoing from the location to be eliminated
471TEST_F(JaniLocalElimination, EliminationMultipleEdges) {
472 auto model =
473 storm::api::parseJaniModel(STORM_TEST_RESOURCES_DIR "/localeliminator/incoming_and_outgoing.jani", storm::jani::getAllKnownModelFeatures(), boost::none)
474 .first;
475 storm::parser::FormulaParser formulaParser(model.getExpressionManager().shared_from_this());
476 std::shared_ptr<storm::logic::Formula const> formula = formulaParser.parseSingleFormulaFromString("P=? [F c=4]");
477 auto property = storm::jani::Property("prop", formula, std::set<storm::expressions::Variable>());
478
479 auto eliminator = JaniLocalEliminator(model, property, false);
480 eliminator.scheduler.addAction(std::make_unique<UnfoldAction>("main", "c"));
481 eliminator.scheduler.addAction(std::make_unique<EliminateAction>("main", "l_c_3"));
482 eliminator.scheduler.addAction(std::make_unique<RebuildWithoutUnreachableAction>());
483 eliminator.eliminate();
484 auto result = eliminator.getResult();
485
486 EXPECT_EQ(6u, result.getAutomaton(0).getNumberOfLocations());
487 checkModel(result, {property}, std::map<storm::expressions::Variable, storm::expressions::Expression>(), 0.01845238095);
488}
489
490// This test verifies the behaviour if multiple destinations of a single edge point to the location to be eliminated.
491TEST_F(JaniLocalElimination, EliminationMultiplicityTest) {
492 auto model =
493 storm::api::parseJaniModel(STORM_TEST_RESOURCES_DIR "/localeliminator/multiplicity.jani", storm::jani::getAllKnownModelFeatures(), boost::none).first;
494 storm::parser::FormulaParser formulaParser(model.getExpressionManager().shared_from_this());
495 std::shared_ptr<storm::logic::Formula const> formula = formulaParser.parseSingleFormulaFromString("P=? [F c=2&x=3]");
496 auto property = storm::jani::Property("prop", formula, std::set<storm::expressions::Variable>());
497
498 auto eliminator = JaniLocalEliminator(model, property, false);
499 eliminator.scheduler.addAction(std::make_unique<UnfoldAction>("main", "c"));
500 eliminator.scheduler.addAction(std::make_unique<EliminateAction>("main", "l_c_1"));
501 eliminator.scheduler.addAction(std::make_unique<RebuildWithoutUnreachableAction>());
502 eliminator.eliminate();
503 auto result = eliminator.getResult();
504
505 EXPECT_EQ(2u, result.getAutomaton(0).getNumberOfLocations());
506 checkModel(result, {property}, std::map<storm::expressions::Variable, storm::expressions::Expression>(), 2.0 / 15.0);
507}
storm::models::sparse::Dtmc< double > Dtmc
TEST_F(JaniLocalElimination, PropertyTypeTest)
storm::models::sparse::Mdp< double > Mdp
storm::modelchecker::SparseMdpPrctlModelChecker< Dtmc > MdpModelChecker
storm::modelchecker::SparseDtmcPrctlModelChecker< Dtmc > DtmcModelChecker
void checkModel(storm::jani::Model model, std::vector< storm::jani::Property > properties, std::map< storm::expressions::Variable, storm::expressions::Expression > consts, double expectedValue)
BuilderOptions & setBuildAllLabels(bool newValue=true)
Should all reward models be built? If not set, only required reward models are build.
void substitute(std::map< storm::expressions::Variable, storm::expressions::Expression > const &substitution, bool const substituteTranscendentalNumbers)
Substitutes all expression variables in all expressions of the model.
Definition Model.cpp:1171
Model defineUndefinedConstants(std::map< storm::expressions::Variable, storm::expressions::Expression > const &constantDefinitions) const
Defines the undefined constants of the model by the given expressions.
Definition Model.cpp:1046
void doAction(JaniLocalEliminator::Session &session) override
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
std::shared_ptr< storm::logic::Formula const > parseSingleFormulaFromString(std::string const &formulaString) const
Parses the formula given by the provided string.
std::pair< storm::jani::Model, std::vector< storm::jani::Property > > parseJaniModel(std::string const &filename, boost::optional< std::vector< std::string > > const &propertyFilter)
std::vector< std::shared_ptr< storm::logic::Formula const > > extractFormulasFromProperties(std::vector< storm::jani::Property > const &properties)
ModelFeatures getAllKnownModelFeatures()
#define STORM_SILENT_EXPECT_THROW(statement, expected_exception)
Definition storm_gtest.h:31
#define STORM_SILENT_ASSERT_THROW(statement, expected_exception)
Definition storm_gtest.h:26