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