Storm 1.14.0.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
RobustMdpPrctlModelCheckerTest.cpp
Go to the documentation of this file.
1#include "storm-config.h"
4#include "test/storm_gtest.h"
5
10#include "storm/api/builder.h"
20
21std::unique_ptr<storm::modelchecker::QualitativeCheckResult> getInitialStateFilter(
22 std::shared_ptr<storm::models::sparse::Model<storm::Interval>> const& model) {
23 return std::make_unique<storm::modelchecker::ExplicitQualitativeCheckResult<double>>(model->getInitialStates());
24}
25
26std::unique_ptr<storm::modelchecker::QualitativeCheckResult> getInitialStateFilter(std::shared_ptr<storm::models::sparse::Model<double>> const& model) {
27 return std::make_unique<storm::modelchecker::ExplicitQualitativeCheckResult<double>>(model->getInitialStates());
28}
29
30std::unique_ptr<storm::modelchecker::QualitativeCheckResult> getInitialStateFilter(
31 std::shared_ptr<storm::models::sparse::Model<storm::RationalNumber>> const& model) {
32 return std::make_unique<storm::modelchecker::ExplicitQualitativeCheckResult<storm::RationalNumber>>(model->getInitialStates());
33}
34
36 std::unique_ptr<storm::modelchecker::CheckResult>& result) {
37 auto filter = getInitialStateFilter(model);
38 result->filter(*filter);
39 return result->asQuantitativeCheckResult<storm::RationalNumber>().getMin();
40}
41
42std::unique_ptr<storm::modelchecker::QualitativeCheckResult> getInitialStateFilter(
43 std::shared_ptr<storm::models::sparse::Model<storm::RationalInterval>> const& model) {
44 return std::make_unique<storm::modelchecker::ExplicitQualitativeCheckResult<storm::RationalNumber>>(model->getInitialStates());
45}
46
48 std::unique_ptr<storm::modelchecker::CheckResult>& result) {
49 auto filter = getInitialStateFilter(model);
50 result->filter(*filter);
51 return result->asQuantitativeCheckResult<double>().getMin();
52}
53
55 std::unique_ptr<storm::modelchecker::CheckResult>& result) {
56 auto filter = getInitialStateFilter(model);
57 result->filter(*filter);
58 return result->asQuantitativeCheckResult<double>().getMin();
59}
60
62 std::unique_ptr<storm::modelchecker::CheckResult>& result) {
63 auto filter = getInitialStateFilter(model);
64 result->filter(*filter);
65 return result->asQuantitativeCheckResult<storm::RationalNumber>().getMin();
66}
67
68void expectSolutionBoundsSound(std::unique_ptr<storm::modelchecker::CheckResult> const& result, double expected) {
69 auto const& explicitResult = result->asExplicitQuantitativeCheckResult<double>();
70 ASSERT_TRUE(explicitResult.hasLowerBounds() || explicitResult.hasUpperBounds()) << "No bound on the solution was reported.";
71 if (explicitResult.hasLowerBounds()) {
72 EXPECT_LE(explicitResult.getLowerBoundVector().front(), expected + 0.0001) << "The lower bound exceeds the expected value.";
73 }
74 if (explicitResult.hasUpperBounds()) {
75 EXPECT_LE(expected, explicitResult.getUpperBoundVector().front() + 0.0001) << "The upper bound falls below the expected value.";
76 }
77}
78
79void expectSolutionBoundsSound(std::unique_ptr<storm::modelchecker::CheckResult> const& result, storm::RationalNumber const& expected) {
80 auto const& explicitResult = result->asExplicitQuantitativeCheckResult<storm::RationalNumber>();
81 ASSERT_TRUE(explicitResult.hasLowerBounds() || explicitResult.hasUpperBounds()) << "No bound on the solution was reported.";
82 if (explicitResult.hasLowerBounds()) {
83 EXPECT_LE(explicitResult.getLowerBoundVector().front(), expected) << "The lower bound exceeds the expected value.";
84 }
85 if (explicitResult.hasUpperBounds()) {
86 EXPECT_LE(expected, explicitResult.getUpperBoundVector().front()) << "The upper bound falls below the expected value.";
87 }
88}
89
90void expectThrow(std::string const& path, std::string const& formulaString) {
91 std::shared_ptr<storm::models::sparse::Model<storm::Interval>> modelPtr = storm::parser::parseDirectEncodingModel<storm::Interval>(path);
92 std::vector<std::shared_ptr<storm::logic::Formula const>> formulas = storm::api::extractFormulasFromProperties(storm::api::parseProperties(formulaString));
93
95 env.solver().minMax().setMethod(storm::solver::MinMaxMethod::ValueIteration);
96
97 std::shared_ptr<storm::models::sparse::Mdp<storm::Interval>> mdp = modelPtr->as<storm::models::sparse::Mdp<storm::Interval>>();
98 ASSERT_EQ(storm::models::ModelType::Mdp, modelPtr->getType());
100
102 STORM_SILENT_EXPECT_THROW(checker.check(env, task), storm::exceptions::NotImplementedException);
103}
104
105void checkModel(std::string const& path, std::string const& formulaString, double maxmin, double maxmax, double minmax, double minmin, bool produceScheduler) {
106 std::shared_ptr<storm::models::sparse::Model<storm::Interval>> modelPtr = storm::parser::parseDirectEncodingModel<storm::Interval>(path);
107 std::vector<std::shared_ptr<storm::logic::Formula const>> formulas = storm::api::extractFormulasFromProperties(storm::api::parseProperties(formulaString));
109 env.solver().minMax().setMethod(storm::solver::MinMaxMethod::ValueIteration);
110 env.solver().setForceSoundness(true);
111
112 std::shared_ptr<storm::models::sparse::Mdp<storm::Interval>> mdp = modelPtr->as<storm::models::sparse::Mdp<storm::Interval>>();
113 ASSERT_EQ(storm::models::ModelType::Mdp, modelPtr->getType());
115 taskMax.setUncertaintyResolutionMode(storm::UncertaintyResolutionMode::Robust);
116 taskMax.setProduceSchedulers(produceScheduler);
117
119 auto resultMax = checker.check(env, taskMax);
120 EXPECT_NEAR(maxmin, getQuantitativeResultAtInitialState(mdp, resultMax), 0.0001);
121 expectSolutionBoundsSound(resultMax, maxmin);
122 taskMax.setUncertaintyResolutionMode(storm::UncertaintyResolutionMode::Cooperative);
123 auto resultMaxNonRobust = checker.check(env, taskMax);
124 EXPECT_NEAR(maxmax, getQuantitativeResultAtInitialState(mdp, resultMaxNonRobust), 0.0001);
125 expectSolutionBoundsSound(resultMaxNonRobust, maxmax);
126
128 taskMin.setUncertaintyResolutionMode(storm::UncertaintyResolutionMode::Robust);
129 taskMin.setProduceSchedulers(produceScheduler);
130
131 auto resultMin = checker.check(env, taskMin);
132 EXPECT_NEAR(minmax, getQuantitativeResultAtInitialState(mdp, resultMin), 0.0001);
133 expectSolutionBoundsSound(resultMin, minmax);
134 taskMin.setUncertaintyResolutionMode(storm::UncertaintyResolutionMode::Cooperative);
135 auto resultMinNonRobust = checker.check(env, taskMin);
136 EXPECT_NEAR(minmin, getQuantitativeResultAtInitialState(mdp, resultMinNonRobust), 0.0001);
137 expectSolutionBoundsSound(resultMinNonRobust, minmin);
138}
139
140void checkPrismModelForQuantitativeResult(std::string const& path, std::string const& formulaString, double minmin, double minmax, double maxmin, double maxmax,
141 std::string constantsString) {
143 program = program.preprocess(constantsString);
144
145 std::vector<std::shared_ptr<storm::logic::Formula const>> formulas = storm::api::extractFormulasFromProperties(storm::api::parseProperties(formulaString));
146 std::shared_ptr<storm::models::sparse::Model<storm::Interval>> modelPtr = storm::api::buildSparseModel<storm::Interval>(program, formulas);
147
149 env.solver().minMax().setMethod(storm::solver::MinMaxMethod::ValueIteration);
150
151 std::shared_ptr<storm::models::sparse::Mdp<storm::Interval>> mdp = modelPtr->as<storm::models::sparse::Mdp<storm::Interval>>();
152 ASSERT_EQ(storm::models::ModelType::Mdp, modelPtr->getType());
153
155 taskMinMin.setUncertaintyResolutionMode(storm::UncertaintyResolutionMode::Minimize);
156
158 auto resultMinMin = checker.check(env, taskMinMin);
159 EXPECT_NEAR(minmin, getQuantitativeResultAtInitialState(mdp, resultMinMin), 0.0001);
160
162 taskMinMax.setUncertaintyResolutionMode(storm::UncertaintyResolutionMode::Maximize);
163
164 auto resultMinMax = checker.check(env, taskMinMax);
165 EXPECT_NEAR(minmax, getQuantitativeResultAtInitialState(mdp, resultMinMax), 0.0001);
166
168 taskMaxMin.setUncertaintyResolutionMode(storm::UncertaintyResolutionMode::Minimize);
169
170 auto resultMaxMin = checker.check(env, taskMaxMin);
171 EXPECT_NEAR(maxmin, getQuantitativeResultAtInitialState(mdp, resultMaxMin), 0.0001);
172
174 taskMaxMin.setUncertaintyResolutionMode(storm::UncertaintyResolutionMode::Maximize);
175
176 auto resultMaxMax = checker.check(env, taskMaxMax);
177 EXPECT_NEAR(maxmax, getQuantitativeResultAtInitialState(mdp, resultMaxMax), 0.0001);
178}
179
180void checkModelRational(std::string const& path, std::string const& formulaString, storm::RationalNumber maxmin, storm::RationalNumber maxmax,
181 storm::RationalNumber minmax, storm::RationalNumber minmin, bool produceScheduler) {
182 std::shared_ptr<storm::models::sparse::Model<storm::RationalInterval>> modelPtr = storm::parser::parseDirectEncodingModel<storm::RationalInterval>(path);
183
184 std::vector<std::shared_ptr<storm::logic::Formula const>> formulas = storm::api::extractFormulasFromProperties(storm::api::parseProperties(formulaString));
186 env.solver().minMax().setMethod(storm::solver::MinMaxMethod::ValueIteration);
187 env.solver().setForceSoundness(true);
188
189 std::shared_ptr<storm::models::sparse::Mdp<storm::RationalInterval>> mdp = modelPtr->as<storm::models::sparse::Mdp<storm::RationalInterval>>();
190 ASSERT_EQ(storm::models::ModelType::Mdp, modelPtr->getType());
192 taskMax.setProduceSchedulers(produceScheduler);
193
195 taskMax.setUncertaintyResolutionMode(storm::UncertaintyResolutionMode::Robust);
196 auto resultMax = checker.check(env, taskMax);
197 EXPECT_EQ(maxmin, getQuantitativeResultAtInitialState(mdp, resultMax));
198 expectSolutionBoundsSound(resultMax, maxmin);
199 taskMax.setUncertaintyResolutionMode(storm::UncertaintyResolutionMode::Cooperative);
200 auto resultMaxNonRobust = checker.check(env, taskMax);
201 EXPECT_EQ(maxmax, getQuantitativeResultAtInitialState(mdp, resultMaxNonRobust));
202 expectSolutionBoundsSound(resultMaxNonRobust, maxmax);
203
205 taskMin.setProduceSchedulers(produceScheduler);
206
207 taskMin.setUncertaintyResolutionMode(storm::UncertaintyResolutionMode::Robust);
208 auto resultMin = checker.check(env, taskMin);
209 EXPECT_EQ(minmax, getQuantitativeResultAtInitialState(mdp, resultMin));
210 expectSolutionBoundsSound(resultMin, minmax);
211 taskMin.setUncertaintyResolutionMode(storm::UncertaintyResolutionMode::Cooperative);
212 auto resultMinNonRobust = checker.check(env, taskMin);
213 EXPECT_EQ(minmin, getQuantitativeResultAtInitialState(mdp, resultMinNonRobust));
214}
215
216TEST(RobustMdpModelCheckerTest, RobotMinMaxTest) {
217#ifndef STORM_HAVE_Z3
218 GTEST_SKIP() << "Z3 not available.";
219#endif
220 // Maxima reachability rewards using PRISM format.
221 checkPrismModelForQuantitativeResult(STORM_TEST_RESOURCES_DIR "/imdp/robot.prism",
222 "Pmin=? [ F \"goal2\"];Pmin=? [ F \"goal2\"];Pmax=? [ F \"goal2\"];Pmax=? [ F \"goal2\"]", 0.4, 0.6, 1.0, 1.0,
223 "delta=0.1");
224}
225
226void makeUncertainAndCheck(std::string const& path, std::string const& formulaString, double amountOfUncertainty) {
228 program = program.preprocess("");
229 std::vector<std::shared_ptr<storm::logic::Formula const>> formulas =
231 std::shared_ptr<storm::models::sparse::Model<double>> modelPtr = storm::api::buildSparseModel<double>(program, formulas);
232
233 auto mdp = modelPtr->as<storm::models::sparse::Mdp<double>>();
234
235 ASSERT_TRUE(formulas[0]->isProbabilityOperatorFormula());
236 // These tests cases where written with max in mind.
237 ASSERT_TRUE(formulas[0]->asProbabilityOperatorFormula().getOptimalityType() == storm::solver::OptimizationDirection::Maximize);
240
241 // First compute certain value
243 auto exresult = checker.check(env, task);
244 double certainValue = getQuantitativeResultAtInitialState(mdp, exresult);
245
246 storm::Environment envIntervals;
247 envIntervals.solver().minMax().setMethod(storm::solver::MinMaxMethod::ValueIteration);
248 auto transformer = storm::transformer::AddUncertainty<double>(modelPtr);
249 auto imdp = transformer.transform(amountOfUncertainty)->as<storm::models::sparse::Mdp<storm::Interval>>();
251 auto iresultMin = checker.check(env, task);
252 double minValue = getQuantitativeResultAtInitialState(mdp, iresultMin);
253 EXPECT_LE(minValue, certainValue);
254 task.setUncertaintyResolutionMode(storm::UncertaintyResolutionMode::Cooperative);
255 auto iresultMax = checker.check(env, task);
256 double maxValue = getQuantitativeResultAtInitialState(mdp, iresultMax);
257 EXPECT_LE(certainValue, maxValue);
258}
259
260void makeUncertainAndCheckRational(std::string const& path, std::string const& formulaString, storm::RationalNumber amountOfUncertainty) {
262 program = program.preprocess("");
263 std::vector<std::shared_ptr<storm::logic::Formula const>> formulas =
265 std::shared_ptr<storm::models::sparse::Model<storm::RationalNumber>> modelPtr = storm::api::buildSparseModel<storm::RationalNumber>(program, formulas);
266 auto mdp = modelPtr->as<storm::models::sparse::Mdp<storm::RationalNumber>>();
267
268 ASSERT_TRUE(formulas[0]->isProbabilityOperatorFormula());
269 ASSERT_TRUE(formulas[0]->asProbabilityOperatorFormula().getOptimalityType() == storm::solver::OptimizationDirection::Maximize);
270
274 auto exresult = checker.check(env, taskCertain);
275 auto const certainValue = getQuantitativeResultAtInitialState(modelPtr, exresult);
276
277 storm::Environment envIntervals;
278 envIntervals.solver().minMax().setMethod(storm::solver::MinMaxMethod::ValueIteration);
280 auto imdp = transformer.transform(amountOfUncertainty)->as<storm::models::sparse::Mdp<storm::RationalInterval>>();
282
284 taskMin.setUncertaintyResolutionMode(storm::UncertaintyResolutionMode::Minimize);
285 auto iresultMin = ichecker.check(envIntervals, taskMin);
286 auto const minValue = getQuantitativeResultAtInitialState(imdp, iresultMin);
287 EXPECT_LE(minValue, certainValue);
288
290 taskMax.setUncertaintyResolutionMode(storm::UncertaintyResolutionMode::Maximize);
291 auto iresultMax = ichecker.check(envIntervals, taskMax);
292 auto const maxValue = getQuantitativeResultAtInitialState(imdp, iresultMax);
293 EXPECT_LE(certainValue, maxValue);
294}
295
296// TODO: Add next properties
297
298TEST(RobustMDPModelCheckingTest, Tiny01maxmin) {
299 checkModel(STORM_TEST_RESOURCES_DIR "/imdp/tiny-01.drn", "Pmax=? [ F \"target\"];Pmin=? [ F \"target\"]", 0.4, 0.5, 0.5, 0.4, false);
300}
301
302TEST(RobustMDPModelCheckingTest, Tiny03maxmin) {
303 checkModel(STORM_TEST_RESOURCES_DIR "/imdp/tiny-03.drn", "Pmax=? [ F \"target\"];Pmin=? [ F \"target\"]", 0.4, 0.6, 0.5, 0.3, true);
304}
305
306TEST(RobustMDPModelCheckingTest, BoundedTiny03maxmin) {
307 checkModel(STORM_TEST_RESOURCES_DIR "/imdp/tiny-03.drn", "Pmax=? [ F<=1 \"target\"];Pmin=? [ F<=1 \"target\"]", 0.4, 0.6, 0.5, 0.3, true);
308 checkModel(STORM_TEST_RESOURCES_DIR "/imdp/tiny-03.drn", "Pmax=? [ F[1,1] \"target\"];Pmin=? [ F[1,1] \"target\"]", 0.4, 0.6, 0.5, 0.3, true);
309}
310
311TEST(RobustMDPModelCheckingTest, BoundedMaxmin) {
312 checkModel(STORM_TEST_RESOURCES_DIR "/imdp/tiny-bounded.drn", "Pmax=? [ F<=4 \"target\"];Pmin=? [ F<=4 \"target\"]", 0.6528, 0.81, 0.81, 0.6528, true);
313 checkModel(STORM_TEST_RESOURCES_DIR "/imdp/tiny-bounded.drn", "Pmax=? [ F[0,4] \"target\"];Pmin=? [ F[0,4] \"target\"]", 0.6528, 0.81, 0.81, 0.6528, true);
314 checkModel(STORM_TEST_RESOURCES_DIR "/imdp/tiny-bounded.drn", "Pmax=? [ F[4,4] \"target\"];Pmin=? [ F[4,4] \"target\"]", 0.1952, 0.324, 0.324, 0.1952,
315 true);
316 checkModel(STORM_TEST_RESOURCES_DIR "/imdp/tiny-bounded.drn", "Pmax=? [ F[4,5] \"target\"];Pmin=? [ F[4,5] \"target\"]", 0.3236, 0.50292, 0.50292, 0.3236,
317 true);
318 checkModel(STORM_TEST_RESOURCES_DIR "/imdp/tiny-bounded.drn", "Pmax=? [ F[4,6] \"target\"];Pmin=? [ F[4,6] \"target\"]", 0.37928, 0.57888, 0.57888, 0.37928,
319 true);
320 checkModel(STORM_TEST_RESOURCES_DIR "/imdp/tiny-bounded.drn", "Pmax=? [ F[2,2] \"target\"];Pmin=? [ F[2,2] \"target\"]", 0.32, 0.45, 0.45, 0.32, true);
321 checkModel(STORM_TEST_RESOURCES_DIR "/imdp/tiny-bounded.drn", "Pmax=? [ F<=2 \"target\"];Pmin=? [ F<=2 \"target\"]", 0.32, 0.45, 0.45, 0.32, true);
322}
323
324TEST(RobustMDPModelCheckingTest, Tiny04maxmin) {
325 checkModel(STORM_TEST_RESOURCES_DIR "/imdp/tiny-04.drn", "Pmax=? [ F \"target\"];Pmin=? [ F \"target\"]", 1, 1, 0.42857, 0.42, false);
326}
327
328TEST(RobustMDPModelCheckingTest, Tiny05maxmin) {
329 checkModel(STORM_TEST_RESOURCES_DIR "/imdp/tiny-05.drn", "Pmax=? [ F \"target\"];Pmin=? [ F \"target\"]", 0.3, 0.4, 0.4, 0.3, false);
330}
331
332TEST(RobustMDPModelCheckingTest, Tiny04MinRewards) {
333 // The reward of a state is collected every time the state is visited. Only the cycle 0 -> 2 -> 0 can be repeated without reaching infinite reward, and
334 // state 0 reaches the target with (fixed) probability 0.3. From state 1 (reached with probability 0.4) one has to move on to state 2 (reward 1).
335 // This yields V0 = 1 + 0.4 * (1 + V2) + 0.3 * V2 with V2 = 1 + V0, i.e., V0 = 7. Rmax is infinite as the sink state 4 is reachable.
336 checkModel(STORM_TEST_RESOURCES_DIR "/imdp/tiny-04.drn", "Rmin=? [ F \"target\" ];Rmin=? [ F \"target\" ]", 7, 7, 7, 7, false);
337}
338
339TEST(RobustMDPModelCheckingTest, RewardsTargetTransitionInterval) {
340 // The self-loop probability p is in [0.2, 0.5], the probability of moving to the target is in [0.5, 0.8]. The expected reward 1 / (1 - p) is thus in
341 // [1.25, 2]. This requires that the interval of the transition to the target state is respected when resolving the uncertainty.
342 // Arguments: Rmax robust, Rmax cooperative, Rmin robust, Rmin cooperative.
343 checkModel(STORM_TEST_RESOURCES_DIR "/imdp/tiny-09.drn", "Rmax=? [ F \"target\" ];Rmin=? [ F \"target\" ]", 1.25, 2, 2, 1.25, false);
344}
345
346TEST(RobustMDPModelCheckingTest, RewardsTargetStateBeforeMaybeStates) {
347 // Same as above, but the target state has a smaller index than the other state. The values of target states must not be mixed up with the values of
348 // the remaining states.
349 checkModel(STORM_TEST_RESOURCES_DIR "/imdp/tiny-11.drn", "Rmax=? [ F \"target\" ];Rmin=? [ F \"target\" ]", 1.25, 2, 2, 1.25, false);
350}
351
352TEST(RobustMDPModelCheckingTest, ReachabilityTimes) {
353 // Action 0 has self-loop probability p in [0.2, 0.5], i.e., expected time 1 / (1 - p) in [1.25, 2]. Action 1 has expected time 1 + 0.5 * 1 = 1.5.
354 // Arguments: Tmax robust, Tmax cooperative, Tmin robust, Tmin cooperative.
355 checkModel(STORM_TEST_RESOURCES_DIR "/imdp/tiny-10.drn", "Tmax=? [ F \"target\" ];Tmin=? [ F \"target\" ]", 1.5, 2, 1.5, 1.25, false);
356}
357
358TEST(RobustMDPModelCheckingTest, ReachabilityTimesSingleAction) {
359 checkModel(STORM_TEST_RESOURCES_DIR "/imdp/tiny-09.drn", "Tmax=? [ F \"target\" ];Tmin=? [ F \"target\" ]", 1.25, 2, 2, 1.25, false);
360 checkModel(STORM_TEST_RESOURCES_DIR "/imdp/tiny-11.drn", "Tmax=? [ F \"target\" ];Tmin=? [ F \"target\" ]", 1.25, 2, 2, 1.25, false);
361}
362
363TEST(RobustMDPModelCheckingTest, GraphPreservationRequired) {
364 // The self-loop interval [0, 1] allows to never reach the target, i.e., the graph of the model is not preserved.
365 std::shared_ptr<storm::models::sparse::Model<storm::Interval>> modelPtr =
366 storm::parser::parseDirectEncodingModel<storm::Interval>(STORM_TEST_RESOURCES_DIR "/imdp/tiny-12.drn");
367 auto mdp = modelPtr->as<storm::models::sparse::Mdp<storm::Interval>>();
368 EXPECT_FALSE(mdp->getTransitionMatrix().isProbabilisticGraphPreserving());
370 env.solver().minMax().setMethod(storm::solver::MinMaxMethod::ValueIteration);
372 for (std::string const formulaString : {"Pmax=? [ F \"target\" ]", "Rmin=? [ F \"target\" ]", "Tmin=? [ F \"target\" ]"}) {
373 std::vector<std::shared_ptr<storm::logic::Formula const>> formulas =
376 task.setUncertaintyResolutionMode(storm::UncertaintyResolutionMode::Cooperative);
377 STORM_SILENT_EXPECT_THROW(checker.check(env, task), storm::exceptions::InvalidSettingsException);
378 }
379}
380
381TEST(RobustMDPModelCheckingTest, GraphPreservationAfterTightening) {
382 // The self-loop interval [0, 0.5] is tightened to [0.5, 0.5] as the other transition has probability at most 0.5. The model is graph preserving.
383 std::shared_ptr<storm::models::sparse::Model<storm::Interval>> modelPtr =
384 storm::parser::parseDirectEncodingModel<storm::Interval>(STORM_TEST_RESOURCES_DIR "/imdp/tiny-13.drn");
385 EXPECT_FALSE(modelPtr->getTransitionMatrix().hasOnlyPositiveEntries());
386 EXPECT_TRUE(modelPtr->getTransitionMatrix().isProbabilisticGraphPreserving());
387 checkModel(STORM_TEST_RESOURCES_DIR "/imdp/tiny-13.drn", "Tmax=? [ F \"target\" ];Tmin=? [ F \"target\" ]", 2, 2, 2, 2, false);
388}
389
390TEST(RobustMDPModelCheckingTest, MinRewardsUniqueSolution) {
391 // There is no end component with zero reward among the states with finite reward, i.e., the solution of the equation system is unique and no upper
392 // reward bounds are required. Arguments: Rmin robust, Rmin cooperative, Rmin robust, Rmin cooperative.
393 // Choosing action 0 in state 0 yields 2 + 1 / (1 - p) with self-loop probability p in [0.2, 0.5], which is cheaper than action 1 (cost 6).
394 checkModel(STORM_TEST_RESOURCES_DIR "/imdp/tiny-08.drn", "Rmin=? [ F \"target\" ];Rmin=? [ F \"target\" ]", 4, 3.25, 4, 3.25, false);
395}
396
397TEST(RobustMDPModelCheckingTest, MinRewardsNonUniqueSolution) {
398 // State 0 has a zero-reward self loop, i.e., there is an end component with zero reward. The solution is not unique and we cannot compute the required
399 // upper reward bounds for interval models.
400 expectThrow(STORM_TEST_RESOURCES_DIR "/imdp/tiny-07.drn", "Rmin=? [ F \"target\" ]");
401}
402
403TEST(RobustMDPModelCheckingTest, CumulativeRewards) {
404 // Arguments: Rmax robust, Rmax cooperative, Rmin robust, Rmin cooperative
405 checkModel(STORM_TEST_RESOURCES_DIR "/imdp/tiny-06.drn", "Rmax=? [ C<=0 ];Rmin=? [ C<=0 ]", 0, 0, 0, 0, false);
406 checkModel(STORM_TEST_RESOURCES_DIR "/imdp/tiny-06.drn", "Rmax=? [ C<=1 ];Rmin=? [ C<=1 ]", 7, 7, 1, 1, false);
407 checkModel(STORM_TEST_RESOURCES_DIR "/imdp/tiny-06.drn", "Rmax=? [ C<=2 ];Rmin=? [ C<=2 ]", 9, 9.5, 9, 7.5, false);
408 checkModel(STORM_TEST_RESOURCES_DIR "/imdp/tiny-06.drn", "Rmax=? [ C<=3 ];Rmin=? [ C<=3 ]", 11, 11.5, 11, 9.5, false);
409}
410
411TEST(RobustMDPModelCheckingTest, CumulativeRewardsNoUncertaintyResolutionMode) {
412 std::shared_ptr<storm::models::sparse::Model<storm::Interval>> modelPtr =
413 storm::parser::parseDirectEncodingModel<storm::Interval>(STORM_TEST_RESOURCES_DIR "/imdp/tiny-06.drn");
414 std::vector<std::shared_ptr<storm::logic::Formula const>> formulas =
417 env.solver().minMax().setMethod(storm::solver::MinMaxMethod::ValueIteration);
418 auto mdp = modelPtr->as<storm::models::sparse::Mdp<storm::Interval>>();
422}
423
424TEST(RobustMDPModelCheckingTest, CrowdsQuotientIMDP) {
425 // Ensuring equivalent behavior when checking identical model as IDTMC and IMDP (cf. CrowdsQuotientIDTMC)
426 checkModel(STORM_TEST_RESOURCES_DIR "/imdp/crowds-quotient-3-5.drn", "Pmax=? [ F \"observe0Greater1\"]; Pmin=? [ F \"observe0Greater1\"]", 0.1383409,
427 0.1383409, 0.1383409, 0.1383409, false);
428}
429
430// ---- RationalInterval tests (exact arithmetic) ----
431
432TEST(RobustRationalMDPModelCheckingTest, CumulativeRewards) {
433 checkModelRational(STORM_TEST_RESOURCES_DIR "/imdp/tiny-06.drn", "Rmax=? [ C<=2 ];Rmin=? [ C<=2 ]", storm::RationalNumber(9), storm::RationalNumber("19/2"),
434 storm::RationalNumber(9), storm::RationalNumber("15/2"), false);
435 checkModelRational(STORM_TEST_RESOURCES_DIR "/imdp/tiny-06.drn", "Rmax=? [ C<=3 ];Rmin=? [ C<=3 ]", storm::RationalNumber(11),
436 storm::RationalNumber("23/2"), storm::RationalNumber(11), storm::RationalNumber("19/2"), false);
437}
438
439TEST(RobustRationalMDPModelCheckingTest, Tiny01maxmin) {
440 checkModelRational(STORM_TEST_RESOURCES_DIR "/imdp/tiny-01.drn", "Pmax=? [ F \"target\"];Pmin=? [ F \"target\"]", storm::RationalNumber("2/5"),
441 storm::RationalNumber("1/2"), storm::RationalNumber("1/2"), storm::RationalNumber("2/5"), false);
442}
443
444TEST(RobustRationalMDPModelCheckingTest, Tiny03maxmin) {
445 checkModelRational(STORM_TEST_RESOURCES_DIR "/imdp/tiny-03.drn", "Pmax=? [ F \"target\"];Pmin=? [ F \"target\"]", storm::RationalNumber("2/5"),
446 storm::RationalNumber("3/5"), storm::RationalNumber("1/2"), storm::RationalNumber("3/10"), true);
447}
448
449TEST(RobustRationalMDPModelCheckingTest, BoundedTiny03maxmin) {
450 checkModelRational(STORM_TEST_RESOURCES_DIR "/imdp/tiny-03.drn", "Pmax=? [ F<=1 \"target\"];Pmin=? [ F<=1 \"target\"]", storm::RationalNumber("2/5"),
451 storm::RationalNumber("3/5"), storm::RationalNumber("1/2"), storm::RationalNumber("3/10"), true);
452 checkModelRational(STORM_TEST_RESOURCES_DIR "/imdp/tiny-03.drn", "Pmax=? [ F[1,1] \"target\"];Pmin=? [ F[1,1] \"target\"]", storm::RationalNumber("2/5"),
453 storm::RationalNumber("3/5"), storm::RationalNumber("1/2"), storm::RationalNumber("3/10"), true);
454}
455
456TEST(RobustRationalMDPModelCheckingTest, BoundedMaxmin) {
457 checkModelRational(STORM_TEST_RESOURCES_DIR "/imdp/tiny-bounded.drn", "Pmax=? [ F<=4 \"target\"];Pmin=? [ F<=4 \"target\"]",
458 storm::RationalNumber("408/625"), storm::RationalNumber("81/100"), storm::RationalNumber("81/100"), storm::RationalNumber("408/625"),
459 true);
460 checkModelRational(STORM_TEST_RESOURCES_DIR "/imdp/tiny-bounded.drn", "Pmax=? [ F[0,4] \"target\"];Pmin=? [ F[0,4] \"target\"]",
461 storm::RationalNumber("408/625"), storm::RationalNumber("81/100"), storm::RationalNumber("81/100"), storm::RationalNumber("408/625"),
462 true);
463 checkModelRational(STORM_TEST_RESOURCES_DIR "/imdp/tiny-bounded.drn", "Pmax=? [ F[4,4] \"target\"];Pmin=? [ F[4,4] \"target\"]",
464 storm::RationalNumber("122/625"), storm::RationalNumber("81/250"), storm::RationalNumber("81/250"), storm::RationalNumber("122/625"),
465 true);
466 checkModelRational(STORM_TEST_RESOURCES_DIR "/imdp/tiny-bounded.drn", "Pmax=? [ F[4,5] \"target\"];Pmin=? [ F[4,5] \"target\"]",
467 storm::RationalNumber("809/2500"), storm::RationalNumber("12573/25000"), storm::RationalNumber("12573/25000"),
468 storm::RationalNumber("809/2500"), true);
469 checkModelRational(STORM_TEST_RESOURCES_DIR "/imdp/tiny-bounded.drn", "Pmax=? [ F[4,6] \"target\"];Pmin=? [ F[4,6] \"target\"]",
470 storm::RationalNumber("4741/12500"), storm::RationalNumber("1809/3125"), storm::RationalNumber("1809/3125"),
471 storm::RationalNumber("4741/12500"), true);
472 checkModelRational(STORM_TEST_RESOURCES_DIR "/imdp/tiny-bounded.drn", "Pmax=? [ F[2,2] \"target\"];Pmin=? [ F[2,2] \"target\"]",
473 storm::RationalNumber("8/25"), storm::RationalNumber("9/20"), storm::RationalNumber("9/20"), storm::RationalNumber("8/25"), true);
474 checkModelRational(STORM_TEST_RESOURCES_DIR "/imdp/tiny-bounded.drn", "Pmax=? [ F<=2 \"target\"];Pmin=? [ F<=2 \"target\"]", storm::RationalNumber("8/25"),
475 storm::RationalNumber("9/20"), storm::RationalNumber("9/20"), storm::RationalNumber("8/25"), true);
476}
477
478TEST(RobustRationalMDPModelCheckingTest, Tiny04maxmin) {
479 // Fill in exact rational values once test output is known.
480 checkModelRational(STORM_TEST_RESOURCES_DIR "/imdp/tiny-04.drn", "Pmax=? [ F \"target\"];Pmin=? [ F \"target\"]", storm::RationalNumber(1),
481 storm::RationalNumber(1), storm::RationalNumber("42857140807299/100000000000000"), storm::RationalNumber("21/50"), false);
482}
483
484TEST(RobustRationalMDPModelCheckingTest, Tiny05maxmin) {
485 checkModelRational(STORM_TEST_RESOURCES_DIR "/imdp/tiny-05.drn", "Pmax=? [ F \"target\"];Pmin=? [ F \"target\"]", storm::RationalNumber("3/10"),
486 storm::RationalNumber("2/5"), storm::RationalNumber("2/5"), storm::RationalNumber("3/10"), false);
487}
488
489TEST(RobustMDPModelCheckingTest, AddUncertaintyCoin22max) {
490#ifndef STORM_HAVE_Z3
491 GTEST_SKIP() << "Z3 not available.";
492#endif
493 makeUncertainAndCheck(STORM_TEST_RESOURCES_DIR "/mdp/coin2-2.nm", "Pmax=? [F \"all_coins_equal_1\"]", 0.1);
494 makeUncertainAndCheck(STORM_TEST_RESOURCES_DIR "/mdp/coin2-2.nm", "Pmax=? [F \"all_coins_equal_1\"]", 0.2);
495}
496
497TEST(RobustRationalMDPModelCheckingTest, AddUncertaintyCoin22max) {
498#ifndef STORM_HAVE_Z3
499 GTEST_SKIP() << "Z3 not available.";
500#endif
501 makeUncertainAndCheckRational(STORM_TEST_RESOURCES_DIR "/mdp/coin2-2.nm", "Pmax=? [F \"all_coins_equal_1\"]", storm::RationalNumber("1/10"));
502 makeUncertainAndCheckRational(STORM_TEST_RESOURCES_DIR "/mdp/coin2-2.nm", "Pmax=? [F \"all_coins_equal_1\"]", storm::RationalNumber("1/5"));
503}
void expectSolutionBoundsSound(std::unique_ptr< storm::modelchecker::CheckResult > const &result, double expected)
TEST(RobustMdpModelCheckerTest, RobotMinMaxTest)
std::unique_ptr< storm::modelchecker::QualitativeCheckResult > getInitialStateFilter(std::shared_ptr< storm::models::sparse::Model< storm::Interval > > const &model)
storm::ExtendedRationalNumber getQuantitativeResultAtInitialState(std::shared_ptr< storm::models::sparse::Model< storm::RationalNumber > > const &model, std::unique_ptr< storm::modelchecker::CheckResult > &result)
void checkPrismModelForQuantitativeResult(std::string const &path, std::string const &formulaString, double minmin, double minmax, double maxmin, double maxmax, std::string constantsString)
void expectThrow(std::string const &path, std::string const &formulaString)
void checkModel(std::string const &path, std::string const &formulaString, double maxmin, double maxmax, double minmax, double minmin, bool produceScheduler)
void checkModelRational(std::string const &path, std::string const &formulaString, storm::RationalNumber maxmin, storm::RationalNumber maxmax, storm::RationalNumber minmax, storm::RationalNumber minmin, bool produceScheduler)
void makeUncertainAndCheckRational(std::string const &path, std::string const &formulaString, storm::RationalNumber amountOfUncertainty)
void makeUncertainAndCheck(std::string const &path, std::string const &formulaString, double amountOfUncertainty)
SolverEnvironment & solver()
void setMethod(storm::solver::MinMaxMethod value, bool isSetFromDefault=false)
MinMaxSolverEnvironment & minMax()
This class represents the base class of all exception classes.
virtual std::unique_ptr< CheckResult > check(Environment const &env, CheckTask< storm::logic::Formula, SolutionType > const &checkTask)
Checks the provided formula.
This class represents a (discrete-time) Markov decision process.
Definition Mdp.h:16
Base class for all sparse models.
Definition Model.h:34
Program preprocess(std::map< storm::expressions::Variable, storm::expressions::Expression > const &constantDefinitions) const
Preprocesses the program by defining the given constant definitions, substituting constants and formu...
Definition Program.cpp:1170
This class is a convenience transformer to add uncertainty.
std::vector< storm::jani::Property > parsePropertiesForPrismProgram(std::string const &inputString, storm::prism::Program const &program, boost::optional< std::set< std::string > > const &propertyFilter)
storm::prism::Program parseProgram(std::string const &filename, bool prismCompatibility, bool simplify)
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::vector< storm::jani::Property > parseProperties(storm::parser::FormulaParser &formulaParser, std::string const &inputString, boost::optional< std::set< std::string > > const &propertyFilter)
template STORM_PARSERS_API std::shared_ptr< storm::models::sparse::Model< storm::RationalInterval > > parseDirectEncodingModel< storm::RationalInterval >(std::filesystem::path const &file, DirectEncodingParserOptions const &options)
template STORM_PARSERS_API std::shared_ptr< storm::models::sparse::Model< storm::Interval > > parseDirectEncodingModel< storm::Interval >(std::filesystem::path const &file, DirectEncodingParserOptions const &options)
storm::utility::ExtendedValueType< RationalNumber > ExtendedRationalNumber
The rational number type extended with the two infinities.
#define STORM_SILENT_EXPECT_THROW(statement, expected_exception)
Definition storm_gtest.h:21