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