Storm 1.14.0.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
BeliefBasedModelCheckerTest.cpp
Go to the documentation of this file.
1#include "storm-config.h"
2#include "test/storm_gtest.h"
3
15#include "storm/api/storm.h"
20#include "storm/utility/graph.h"
21
22namespace {
23enum class PreprocessingType { None, SelfloopReduction, QualitativeReduction, All };
24
25class DefaultDoubleVIEnvironment {
26 public:
27 typedef double POMDPValueType;
28 typedef double BeliefValueType;
29 typedef double BeliefMDPValueType;
30
31 static storm::Environment createEnvironment() {
32 storm::Environment env;
33 env.solver().minMax().setMethod(storm::solver::MinMaxMethod::ValueIteration);
35 return env;
36 }
37 static bool const isExactModelChecking = false;
38 static POMDPValueType precision() {
40 } // there actually aren't any precision guarantees, but we still want to detect if results are weird.
41 static PreprocessingType const preprocessingType = PreprocessingType::None;
42 static uint64_t overApproxResolution() {
43 return 2;
44 }
45};
46
47class SelfloopReductionDefaultDoubleVIEnvironment {
48 public:
49 typedef double POMDPValueType;
50 typedef double BeliefValueType;
51 typedef double BeliefMDPValueType;
52 static storm::Environment createEnvironment() {
53 storm::Environment env;
54 env.solver().minMax().setMethod(storm::solver::MinMaxMethod::ValueIteration);
56 return env;
57 }
58 static bool const isExactModelChecking = false;
59 static POMDPValueType precision() {
61 } // there actually aren't any precision guarantees, but we still want to detect if results are weird.
62 static PreprocessingType const preprocessingType = PreprocessingType::SelfloopReduction;
63 static uint64_t overApproxResolution() {
64 return 2;
65 }
66};
67
68class QualitativeReductionDefaultDoubleVIEnvironment {
69 public:
70 typedef double POMDPValueType;
71 typedef double BeliefValueType;
72 typedef double BeliefMDPValueType;
73 static storm::Environment createEnvironment() {
74 storm::Environment env;
75 env.solver().minMax().setMethod(storm::solver::MinMaxMethod::ValueIteration);
77 return env;
78 }
79 static bool const isExactModelChecking = false;
80 static POMDPValueType precision() {
82 } // there actually aren't any precision guarantees, but we still want to detect if results are weird.
83 static PreprocessingType const preprocessingType = PreprocessingType::QualitativeReduction;
84 static uint64_t overApproxResolution() {
85 return 2;
86 }
87};
88
89class PreprocessedDefaultDoubleVIEnvironment {
90 public:
91 typedef double POMDPValueType;
92 typedef double BeliefValueType;
93 typedef double BeliefMDPValueType;
94 static storm::Environment createEnvironment() {
95 storm::Environment env;
96 env.solver().minMax().setMethod(storm::solver::MinMaxMethod::ValueIteration);
98 return env;
99 }
100 static bool const isExactModelChecking = false;
101 static POMDPValueType precision() {
103 } // there actually aren't any precision guarantees, but we still want to detect if results are weird.
104 static PreprocessingType const preprocessingType = PreprocessingType::All;
105 static uint64_t overApproxResolution() {
106 return 2;
107 }
108};
109
110class FineDoubleVIEnvironment {
111 public:
112 typedef double POMDPValueType;
113 typedef double BeliefValueType;
114 typedef double BeliefMDPValueType;
115 static storm::Environment createEnvironment() {
116 storm::Environment env;
117 env.solver().minMax().setMethod(storm::solver::MinMaxMethod::ValueIteration);
119 return env;
120 }
121 static bool const isExactModelChecking = false;
122 static POMDPValueType precision() {
124 } // there actually aren't any precision guarantees, but we still want to detect if results are weird.
125 static PreprocessingType const preprocessingType = PreprocessingType::None;
126 static uint64_t overApproxResolution() {
127 return 24;
128 }
129};
130
131class DefaultDoubleOVIEnvironment {
132 public:
133 typedef double POMDPValueType;
134 typedef double BeliefValueType;
135 typedef double BeliefMDPValueType;
136 static storm::Environment createEnvironment() {
137 storm::Environment env;
138 env.solver().minMax().setMethod(storm::solver::MinMaxMethod::OptimisticValueIteration);
140 env.solver().setForceSoundness(true);
141 return env;
142 }
143 static bool const isExactModelChecking = false;
144 static POMDPValueType precision() {
146 } // there actually aren't any precision guarantees, but we still want to detect if results are weird.
147 static PreprocessingType const preprocessingType = PreprocessingType::None;
148 static uint64_t overApproxResolution() {
149 return 2;
150 }
151};
152
153class DefaultDoubleSVIEnvironment {
154 public:
155 typedef double POMDPValueType;
156 typedef double BeliefValueType;
157 typedef double BeliefMDPValueType;
158 static storm::Environment createEnvironment() {
159 storm::Environment env;
160 env.solver().minMax().setMethod(storm::solver::MinMaxMethod::SoundValueIteration);
162 env.solver().setForceSoundness(true);
163 return env;
164 }
165 static bool const isExactModelChecking = false;
166 static POMDPValueType precision() {
168 } // there actually aren't any precision guarantees, but we still want to detect if results are weird.
169 static PreprocessingType const preprocessingType = PreprocessingType::None;
170 static uint64_t overApproxResolution() {
171 return 2;
172 }
173};
174
175class DefaultRationalPIEnvironment {
176 public:
177 typedef storm::RationalNumber POMDPValueType;
178 typedef storm::RationalNumber BeliefValueType;
179 typedef storm::RationalNumber BeliefMDPValueType;
180
181 static storm::Environment createEnvironment() {
182 storm::Environment env;
183 env.solver().minMax().setMethod(storm::solver::MinMaxMethod::PolicyIteration);
184 env.solver().setForceExact(true);
185 return env;
186 }
187 static bool const isExactModelChecking = true;
188 static POMDPValueType precision() {
190 } // there actually aren't any precision guarantees, but we still want to detect if results are weird.
191 static PreprocessingType const preprocessingType = PreprocessingType::None;
192 static uint64_t overApproxResolution() {
193 return 2;
194 }
195};
196
197class PreprocessedDefaultRationalPIEnvironment {
198 public:
199 typedef storm::RationalNumber POMDPValueType;
200 typedef storm::RationalNumber BeliefValueType;
201 typedef storm::RationalNumber BeliefMDPValueType;
202
203 static storm::Environment createEnvironment() {
204 storm::Environment env;
205 env.solver().minMax().setMethod(storm::solver::MinMaxMethod::PolicyIteration);
206 env.solver().setForceExact(true);
207 return env;
208 }
209 static bool const isExactModelChecking = true;
210 static POMDPValueType precision() {
212 } // there actually aren't any precision guarantees, but we still want to detect if results are weird.
213 static PreprocessingType const preprocessingType = PreprocessingType::All;
214 static uint64_t overApproxResolution() {
215 return 2;
216 }
217};
218
219template<typename TestType>
220class BeliefBasedModelCheckerTest : public ::testing::Test {
221 public:
222 typedef typename TestType::POMDPValueType POMDPValueType;
223 typedef typename TestType::BeliefValueType BeliefValueType;
224 typedef typename TestType::BeliefMDPValueType BeliefMDPValueType;
225 typedef storm::utility::ExtendedValueType<BeliefMDPValueType> ExtendedBeliefMDPValueType;
226
227 BeliefBasedModelCheckerTest() : _environment(TestType::createEnvironment()) {}
228
229 void SetUp() override {
230#ifndef STORM_HAVE_Z3
231 GTEST_SKIP() << "Z3 not available.";
232#endif
233 }
234
235 storm::Environment const& env() const {
236 return _environment;
237 }
238
239 template<typename ValueType>
240 ValueType parseNumber(std::string const& str) {
242 }
243 struct Input {
244 std::shared_ptr<storm::models::sparse::Pomdp<POMDPValueType>> model;
245 std::shared_ptr<storm::logic::Formula const> formula;
246 std::shared_ptr<storm::pomdp::beliefs::PropertyInformation> propertyInfo = std::make_shared<storm::pomdp::beliefs::PropertyInformation>();
247 };
248 Input buildPrism(std::string const& programFile, std::string const& formulaAsString, std::string const& constantsAsString = "") const {
249 // Parse and build input
250 storm::prism::Program program = storm::api::parseProgram(programFile);
251 program = program.preprocess(constantsAsString);
252 Input input;
253 input.formula = storm::api::parsePropertiesForPrismProgram(formulaAsString, program).front().getRawFormula();
254 input.model = storm::api::buildSparseModel<POMDPValueType>(program, {input.formula})->template as<storm::models::sparse::Pomdp<POMDPValueType>>();
255 bool const isBoundedProbability =
256 input.formula->isProbabilityOperatorFormula() && input.formula->asProbabilityOperatorFormula().getSubformula().isBoundedUntilFormula();
257
258 // Preprocess
259 storm::transformer::MakePOMDPCanonic<POMDPValueType> makeCanonic(*input.model);
260 input.model = makeCanonic.transform();
261 EXPECT_TRUE(input.model->isCanonic());
262 if (!isBoundedProbability &&
263 (TestType::preprocessingType == PreprocessingType::SelfloopReduction || TestType::preprocessingType == PreprocessingType::All)) {
264 storm::transformer::GlobalPOMDPSelfLoopEliminator<POMDPValueType> selfLoopEliminator(*input.model);
265 if (selfLoopEliminator.preservesFormula(*input.formula)) {
266 input.model = selfLoopEliminator.transform();
267 } else {
268 EXPECT_TRUE(input.formula->isOperatorFormula());
269 EXPECT_TRUE(input.formula->asOperatorFormula().hasOptimalityType());
270 bool maximizing = storm::solver::maximize(input.formula->asOperatorFormula().getOptimalityType());
271 // Valid reasons for unpreserved formulas:
272 EXPECT_TRUE(maximizing || input.formula->isProbabilityOperatorFormula());
273 EXPECT_TRUE(!maximizing || input.formula->isRewardOperatorFormula());
274 }
275 }
276 if (!isBoundedProbability &&
277 (TestType::preprocessingType == PreprocessingType::QualitativeReduction || TestType::preprocessingType == PreprocessingType::All)) {
278 EXPECT_TRUE(input.formula->isOperatorFormula());
279 EXPECT_TRUE(input.formula->asOperatorFormula().hasOptimalityType());
280 if (input.formula->isProbabilityOperatorFormula() && storm::solver::maximize(input.formula->asOperatorFormula().getOptimalityType())) {
281 storm::analysis::QualitativeAnalysisOnGraphs<POMDPValueType> qualitativeAnalysis(*input.model);
282 storm::storage::BitVector prob0States = qualitativeAnalysis.analyseProb0(input.formula->asProbabilityOperatorFormula());
283 storm::storage::BitVector prob1States = qualitativeAnalysis.analyseProb1(input.formula->asProbabilityOperatorFormula());
284 storm::pomdp::transformer::KnownProbabilityTransformer<POMDPValueType> kpt;
285 input.model = kpt.transform(*input.model, prob0States, prob1States);
286 }
287 }
288 EXPECT_TRUE(input.model->isCanonic());
289 auto formulaInfo = storm::pomdp::analysis::getFormulaInformation(*input.model, *input.formula);
290 std::optional<std::string> rewardModelName;
291 std::set<uint32_t> targetObservations;
292 EXPECT_TRUE(formulaInfo.isNonNestedReachabilityProbability() || formulaInfo.isNonNestedExpectedRewardFormula());
293 if (formulaInfo.getTargetStates().observationClosed) {
294 targetObservations = formulaInfo.getTargetStates().observations;
295 } else {
296 storm::transformer::MakeStateSetObservationClosed<POMDPValueType> obsCloser(input.model);
297 std::tie(input.model, targetObservations) = obsCloser.transform(formulaInfo.getTargetStates().states);
298 }
299 if (formulaInfo.isNonNestedReachabilityProbability()) {
300 if (!formulaInfo.getSinkStates().empty()) {
301 storm::storage::sparse::ModelComponents<POMDPValueType> components;
302 components.stateLabeling = input.model->getStateLabeling();
303 components.rewardModels = input.model->getRewardModels();
304 auto matrix = input.model->getTransitionMatrix();
305 matrix.makeRowGroupsAbsorbing(formulaInfo.getSinkStates().states);
306 components.transitionMatrix = matrix;
307 components.observabilityClasses = input.model->getObservations();
308 if (input.model->hasChoiceLabeling()) {
309 components.choiceLabeling = input.model->getChoiceLabeling();
310 }
311 if (input.model->hasObservationValuations()) {
312 components.observationValuations = input.model->getObservationValuations();
313 }
314 input.model = std::make_shared<storm::models::sparse::Pomdp<POMDPValueType>>(std::move(components), true);
315 auto reachableFromSinkStates =
316 storm::utility::graph::getReachableStates(input.model->getTransitionMatrix(), formulaInfo.getSinkStates().states,
317 formulaInfo.getSinkStates().states, ~formulaInfo.getSinkStates().states);
318 reachableFromSinkStates &= ~formulaInfo.getSinkStates().states;
319 STORM_LOG_THROW(reachableFromSinkStates.empty(), storm::exceptions::NotSupportedException,
320 "There are sink states that can reach non-sink states. This is currently not supported");
321 }
322 } else {
323 // Expected reward formula!
324 rewardModelName = formulaInfo.getRewardModelName();
325 }
326
327 if (rewardModelName) {
329 input.propertyInfo->rewardModelName = rewardModelName;
330 } else if (formulaInfo.isBounded()) {
332 auto const& boundedFormula = input.formula->asProbabilityOperatorFormula().getSubformula().asBoundedUntilFormula();
333 for (uint64_t i = 0; i < boundedFormula.getDimension(); ++i) {
334 auto const& reference = boundedFormula.getTimeBoundReference(i);
335 input.propertyInfo->rewardBounds.push_back({.rewardModelName = reference.getOptionalRewardModelName().get_value_or(""),
336 .lowerBound = boundedFormula.getLowerBoundAsOptionalTimeBound(i),
337 .upperBound = boundedFormula.getUpperBoundAsOptionalTimeBound(i)});
338 }
339 } else {
341 }
342 input.propertyInfo->dir = formulaInfo.getOptimizationDirection();
343 input.propertyInfo->targetObservations = targetObservations;
344
345 return input;
346 }
347 POMDPValueType precision() const {
348 return TestType::precision();
349 }
350 uint64_t overApproxResolution() const {
351 return TestType::overApproxResolution();
352 }
353 template<typename ValueType>
354 ValueType modelcheckingPrecision() const {
355 if (TestType::isExactModelChecking) {
357 } else {
359 }
360 }
361 bool isExact() const {
362 return TestType::isExactModelChecking;
363 }
364
365 private:
366 storm::Environment _environment;
367};
368
369typedef ::testing::Types<DefaultDoubleVIEnvironment, SelfloopReductionDefaultDoubleVIEnvironment, QualitativeReductionDefaultDoubleVIEnvironment,
370 PreprocessedDefaultDoubleVIEnvironment, FineDoubleVIEnvironment, DefaultDoubleOVIEnvironment, DefaultDoubleSVIEnvironment,
371 DefaultRationalPIEnvironment, PreprocessedDefaultRationalPIEnvironment>
373
374TYPED_TEST_SUITE(BeliefBasedModelCheckerTest, TestingTypes, );
375
376TYPED_TEST(BeliefBasedModelCheckerTest, cut_zero_gap) {
377 using POMDPValueType = typename TestFixture::POMDPValueType;
378 using BeliefValueType = typename TestFixture::BeliefValueType;
379 using BeliefMDPValueType = typename TestFixture::BeliefMDPValueType;
381
382 auto check = [&](std::string const& formula, std::string const& expectedValue) {
383 SCOPED_TRACE(formula);
384 auto data = this->buildPrism(STORM_TEST_RESOURCES_DIR "/pomdp/simple.prism", formula, "slippery=0");
385 ASSERT_EQ(data.model->getInitialStates().getNumberOfSetBits(), 1ul);
386 auto const initialState = data.model->getInitialStates().getNextSetIndex(0);
387 auto const expected = this->template parseNumber<BeliefMDPValueType>(expectedValue);
388 auto const initialValue = this->template parseNumber<POMDPValueType>(expectedValue);
389 auto const delta = this->template parseNumber<POMDPValueType>("1/100000000");
393 bounds.preprocessingBounds = preprocessChecker.getValueBounds(this->env(), *data.formula);
394 auto setInitialBounds = [&](POMDPValueType const& lower, POMDPValueType const& upper) {
395 for (auto& values : bounds.preprocessingBounds->lower) {
396 values[initialState] = lower;
397 }
398 for (auto& values : bounds.preprocessingBounds->upper) {
399 values[initialState] = upper;
400 }
401 };
402
404 EXPECT_FALSE(options.cutZeroGap);
405 options.buildChoiceLabeling = false;
407 auto propertyInfo = *data.propertyInfo;
408 for (int const mode : {0, 1, 2}) {
409 SCOPED_TRACE(mode); // Unfolding, static discretization, dynamic discretization.
410 auto run = [&]() {
411 return mode == 0 ? checker.checkUnfold(this->env(), propertyInfo, options, bounds)
412 : checker.checkDiscretize(this->env(), propertyInfo, options, this->overApproxResolution(), mode == 2, bounds);
413 };
414
415 setInitialBounds(initialValue, initialValue);
416 options.cutZeroGap = true;
417 auto const cut = run();
418 EXPECT_TRUE(cut.completedExploration);
419 EXPECT_EQ(cut.statistics.discoveredBeliefs, 1ul);
420 EXPECT_EQ(cut.statistics.exploredBeliefs, 0ul);
421 EXPECT_LE(storm::utility::abs(cut.value - expected), this->template modelcheckingPrecision<BeliefMDPValueType>());
422
423 options.cutZeroGap = false;
424 auto const uncut = run();
425 EXPECT_TRUE(uncut.completedExploration);
426 EXPECT_GT(uncut.statistics.discoveredBeliefs, 1ul);
427 EXPECT_GT(uncut.statistics.exploredBeliefs, 0ul);
428
429 // A positive gap below the floating-point solver precision must still be explored.
430 setInitialBounds(initialValue - delta, initialValue + delta);
431 options.cutZeroGap = true;
432 auto const positiveGap = run();
433 EXPECT_TRUE(positiveGap.completedExploration);
434 EXPECT_GT(positiveGap.statistics.discoveredBeliefs, 1ul);
435 EXPECT_GT(positiveGap.statistics.exploredBeliefs, 0ul);
436 }
437 };
438
439 check("Pmax=? [F \"goal\" ]", "7/10");
440 check("Pmin=? [F \"goal\" ]", "3/10");
441 check("Rmax=? [F s>4 ]", "29/50");
442 check("Rmin=? [F s>4 ]", "19/50");
443}
444
445TYPED_TEST(BeliefBasedModelCheckerTest, simple_Pmax) {
447 typedef typename TestFixture::POMDPValueType POMDPValueType;
448 typedef typename TestFixture::BeliefValueType BeliefValueType;
449 typedef typename TestFixture::BeliefMDPValueType BeliefMDPValueType;
450
451 auto data = this->buildPrism(STORM_TEST_RESOURCES_DIR "/pomdp/simple.prism", "Pmax=? [F \"goal\" ]", "slippery=0");
454
456 precomputedBeliefBounds.preprocessingBounds = preprocessChecker.getValueBounds(this->env(), *data.formula);
457
459 options.buildChoiceLabeling = false;
461
462 typename TestFixture::ExtendedBeliefMDPValueType overResultValue;
463 typename TestFixture::ExtendedBeliefMDPValueType underResultValue;
464
465 auto expected = this->template parseNumber<BeliefMDPValueType>("7/10");
466 auto overCheckResult = checker.checkDiscretize(this->env(), *data.propertyInfo, options, this->overApproxResolution(), true, precomputedBeliefBounds);
467 overResultValue = overCheckResult.value;
468 EXPECT_GT(overCheckResult.statistics.discoveredBeliefs, 0ul);
469 EXPECT_GT(overCheckResult.statistics.exploredBeliefs, 0ul);
470 EXPECT_GT(overCheckResult.statistics.beliefMdpStates, 0ul);
471 EXPECT_GT(overCheckResult.statistics.beliefMdpChoices, 0ul);
472 EXPECT_GT(overCheckResult.statistics.beliefMdpTransitions, 0ul);
473 EXPECT_GE(overResultValue, expected - this->template modelcheckingPrecision<BeliefMDPValueType>());
474
475 auto underCheckResult = checker.checkUnfold(this->env(), *data.propertyInfo, options, precomputedBeliefBounds);
476 underResultValue = underCheckResult.value;
477 EXPECT_LE(underResultValue, expected + this->template modelcheckingPrecision<BeliefMDPValueType>());
478 EXPECT_LE(storm::utility::abs(overResultValue - underResultValue), this->precision())
479 << "Result [" << underResultValue << ", " << overResultValue
480 << "] is not precise enough. If (only) this fails, the result bounds are still correct, but they might be unexpectedly imprecise.\n";
481}
482
483TYPED_TEST(BeliefBasedModelCheckerTest, simple_Pmin) {
485 typedef typename TestFixture::POMDPValueType POMDPValueType;
486 typedef typename TestFixture::BeliefValueType BeliefValueType;
487 typedef typename TestFixture::BeliefMDPValueType BeliefMDPValueType;
488
489 auto data = this->buildPrism(STORM_TEST_RESOURCES_DIR "/pomdp/simple.prism", "Pmin=? [F \"goal\" ]", "slippery=0");
490 storm::pomdp::beliefs::BeliefBasedModelChecker<storm::models::sparse::Pomdp<POMDPValueType>, BeliefValueType, BeliefMDPValueType> checker(*data.model);
492
494 precomputedBeliefBounds.preprocessingBounds = preprocessChecker.getValueBounds(this->env(), *data.formula);
495
497 options.buildChoiceLabeling = false;
499
500 typename TestFixture::ExtendedBeliefMDPValueType overResultValue;
501 typename TestFixture::ExtendedBeliefMDPValueType underResultValue;
502
503 BeliefMDPValueType expected = this->template parseNumber<BeliefMDPValueType>("3/10");
504 auto overCheckResult = checker.checkDiscretize(this->env(), *data.propertyInfo, options, this->overApproxResolution(), true, precomputedBeliefBounds);
505 overResultValue = overCheckResult.value;
506 EXPECT_LE(overResultValue, expected + this->template modelcheckingPrecision<BeliefMDPValueType>());
507
508 auto underCheckResult = checker.checkUnfold(this->env(), *data.propertyInfo, options, precomputedBeliefBounds);
509 underResultValue = underCheckResult.value;
510 EXPECT_GE(underResultValue, expected - this->template modelcheckingPrecision<BeliefMDPValueType>());
511 EXPECT_LE(storm::utility::abs(overResultValue - underResultValue), this->precision())
512 << "Result [" << overResultValue << ", " << underResultValue
513 << "] is not precise enough. If (only) this fails, the result bounds are still correct, but they might be unexpectedly imprecise.\n";
514}
515
516TYPED_TEST(BeliefBasedModelCheckerTest, discretization_rejects_exploration_limits) {
517 using POMDPValueType = typename TestFixture::POMDPValueType;
518 using BeliefValueType = typename TestFixture::BeliefValueType;
519 using BeliefMDPValueType = typename TestFixture::BeliefMDPValueType;
521
522 auto data = this->buildPrism(STORM_TEST_RESOURCES_DIR "/pomdp/simple.prism", "Pmin=? [F \"goal\" ]", "slippery=0");
525 bounds.preprocessingBounds.emplace();
526 bounds.preprocessingBounds->lower.emplace_back(data.model->getNumberOfStates(), storm::utility::zero<POMDPValueType>());
527 bounds.preprocessingBounds->upper.emplace_back(data.model->getNumberOfStates(), storm::utility::one<POMDPValueType>());
528
530 options.maxExplorationSize = 1;
532
533 STORM_SILENT_EXPECT_THROW(checker.checkDiscretize(this->env(), *data.propertyInfo, options, 10, false, bounds), storm::exceptions::NotSupportedException);
534
535 auto const unfolded = checker.checkUnfold(this->env(), *data.propertyInfo, options, bounds);
536 EXPECT_FALSE(unfolded.completedExploration);
537
538 options.maxExplorationSize.reset();
539 auto const discretized = checker.checkDiscretize(this->env(), *data.propertyInfo, options, 10, false, bounds);
540 EXPECT_TRUE(discretized.completedExploration);
541 EXPECT_GT(discretized.statistics.exploredBeliefs, 1);
542}
543
544TYPED_TEST(BeliefBasedModelCheckerTest, simple_slippery_Pmax) {
546 typedef typename TestFixture::POMDPValueType POMDPValueType;
547 typedef typename TestFixture::BeliefValueType BeliefValueType;
548 typedef typename TestFixture::BeliefMDPValueType BeliefMDPValueType;
549
550 auto data = this->buildPrism(STORM_TEST_RESOURCES_DIR "/pomdp/simple.prism", "Pmax=? [F \"goal\" ]", "slippery=0.4");
551 storm::pomdp::beliefs::BeliefBasedModelChecker<storm::models::sparse::Pomdp<POMDPValueType>, BeliefValueType, BeliefMDPValueType> checker(*data.model);
553
555 precomputedBeliefBounds.preprocessingBounds = preprocessChecker.getValueBounds(this->env(), *data.formula);
556
558 options.buildChoiceLabeling = false;
560 typename TestFixture::ExtendedBeliefMDPValueType overResultValue;
561 typename TestFixture::ExtendedBeliefMDPValueType underResultValue;
562
563 BeliefMDPValueType expected = this->template parseNumber<BeliefMDPValueType>("7/10");
564 auto overCheckResult = checker.checkDiscretize(this->env(), *data.propertyInfo, options, this->overApproxResolution(), true, precomputedBeliefBounds);
565 overResultValue = overCheckResult.value;
566 EXPECT_GE(overResultValue, expected - this->template modelcheckingPrecision<BeliefMDPValueType>());
567 options.maxExplorationSize = data.model->getNumberOfStates() * data.model->getMaxNrStatesWithSameObservation();
568
569 auto underCheckResult = checker.checkUnfold(this->env(), *data.propertyInfo, options, precomputedBeliefBounds);
570 underResultValue = underCheckResult.value;
571 EXPECT_LE(underResultValue, expected + this->template modelcheckingPrecision<BeliefMDPValueType>());
572 EXPECT_LE(storm::utility::abs(overResultValue - underResultValue), this->precision())
573 << "Result [" << underResultValue << ", " << overResultValue
574 << "] is not precise enough. If (only) this fails, the result bounds are still correct, but they might be unexpectedly imprecise.\n";
575}
576
577TYPED_TEST(BeliefBasedModelCheckerTest, simple_slippery_Pmin) {
579 typedef typename TestFixture::POMDPValueType POMDPValueType;
580 typedef typename TestFixture::BeliefValueType BeliefValueType;
581 typedef typename TestFixture::BeliefMDPValueType BeliefMDPValueType;
582
583 auto data = this->buildPrism(STORM_TEST_RESOURCES_DIR "/pomdp/simple.prism", "Pmin=? [F \"goal\" ]", "slippery=0.4");
584 storm::pomdp::beliefs::BeliefBasedModelChecker<storm::models::sparse::Pomdp<POMDPValueType>, BeliefValueType, BeliefMDPValueType> checker(*data.model);
586
588 precomputedBeliefBounds.preprocessingBounds = preprocessChecker.getValueBounds(this->env(), *data.formula);
590 options.buildChoiceLabeling = false;
592 typename TestFixture::ExtendedBeliefMDPValueType overResultValue;
593 typename TestFixture::ExtendedBeliefMDPValueType underResultValue;
594
595 POMDPValueType expected = this->template parseNumber<BeliefMDPValueType>("3/10");
596 auto overCheckResult = checker.checkDiscretize(this->env(), *data.propertyInfo, options, this->overApproxResolution(), true, precomputedBeliefBounds);
597 overResultValue = overCheckResult.value;
598
599 options.maxExplorationSize = data.model->getNumberOfStates() * data.model->getMaxNrStatesWithSameObservation();
600 auto underCheckResult = checker.checkUnfold(this->env(), *data.propertyInfo, options, precomputedBeliefBounds);
601 underResultValue = underCheckResult.value;
602 if (this->isExact()) {
603 // This model's value can only be approximated arbitrarily close but never reached
604 // Exact arithmetics will thus not reach the value with absoulute precision either.
605 POMDPValueType approxPrecision = storm::utility::convertNumber<POMDPValueType>(1e-5);
606 EXPECT_GE(underResultValue, expected - approxPrecision);
607 EXPECT_LE(overResultValue, expected + approxPrecision);
608 } else {
609 EXPECT_GE(underResultValue, expected - this->template modelcheckingPrecision<BeliefMDPValueType>());
610 EXPECT_LE(overResultValue, expected + this->template modelcheckingPrecision<BeliefMDPValueType>());
611 }
612 EXPECT_LE(storm::utility::abs(overResultValue - underResultValue), this->precision())
613 << "Result [" << underResultValue << ", " << overResultValue
614 << "] is not precise enough. If (only) this fails, the result bounds are still correct, but they might be unexpectedly imprecise.\n";
615}
616
617TYPED_TEST(BeliefBasedModelCheckerTest, simple_Rmax) {
619 typedef typename TestFixture::POMDPValueType POMDPValueType;
620 typedef typename TestFixture::BeliefValueType BeliefValueType;
621 typedef typename TestFixture::BeliefMDPValueType BeliefMDPValueType;
622
623 auto data = this->buildPrism(STORM_TEST_RESOURCES_DIR "/pomdp/simple.prism", "Rmax=? [F s>4 ]", "slippery=0");
624 storm::pomdp::beliefs::BeliefBasedModelChecker<storm::models::sparse::Pomdp<POMDPValueType>, BeliefValueType, BeliefMDPValueType> checker(*data.model);
626
628 precomputedBeliefBounds.preprocessingBounds = preprocessChecker.getValueBounds(this->env(), *data.formula);
630 options.buildChoiceLabeling = false;
632
633 typename TestFixture::ExtendedBeliefMDPValueType overResultValue;
634 typename TestFixture::ExtendedBeliefMDPValueType underResultValue;
635
636 BeliefMDPValueType expected = this->template parseNumber<BeliefMDPValueType>("29/50");
637 auto overCheckResult = checker.checkDiscretize(this->env(), *data.propertyInfo, options, this->overApproxResolution(), true, precomputedBeliefBounds);
638 overResultValue = overCheckResult.value;
639 EXPECT_GE(overResultValue, expected - this->template modelcheckingPrecision<BeliefMDPValueType>());
640
641 options.maxExplorationSize = data.model->getNumberOfStates() * data.model->getMaxNrStatesWithSameObservation();
642 auto underCheckResult = checker.checkUnfold(this->env(), *data.propertyInfo, options, precomputedBeliefBounds);
643 underResultValue = underCheckResult.value;
644 EXPECT_LE(underResultValue, expected + this->template modelcheckingPrecision<BeliefMDPValueType>());
645 EXPECT_LE(storm::utility::abs(overResultValue - underResultValue), this->precision())
646 << "Result [" << underResultValue << ", " << overResultValue
647 << "] is not precise enough. If (only) this fails, the result bounds are still correct, but they might be unexpectedly imprecise.\n";
648}
649
650TYPED_TEST(BeliefBasedModelCheckerTest, simple_Rmin) {
652 typedef typename TestFixture::POMDPValueType POMDPValueType;
653 typedef typename TestFixture::BeliefValueType BeliefValueType;
654 typedef typename TestFixture::BeliefMDPValueType BeliefMDPValueType;
655
656 auto data = this->buildPrism(STORM_TEST_RESOURCES_DIR "/pomdp/simple.prism", "Rmin=? [F s>4 ]", "slippery=0");
657 storm::pomdp::beliefs::BeliefBasedModelChecker<storm::models::sparse::Pomdp<POMDPValueType>, BeliefValueType, BeliefMDPValueType> checker(*data.model);
659
661 precomputedBeliefBounds.preprocessingBounds = preprocessChecker.getValueBounds(this->env(), *data.formula);
663 options.buildChoiceLabeling = false;
665
666 typename TestFixture::ExtendedBeliefMDPValueType overResultValue;
667 typename TestFixture::ExtendedBeliefMDPValueType underResultValue;
668
669 BeliefMDPValueType expected = this->template parseNumber<BeliefMDPValueType>("19/50");
670 auto overCheckResult = checker.checkDiscretize(this->env(), *data.propertyInfo, options, this->overApproxResolution(), true, precomputedBeliefBounds);
671 overResultValue = overCheckResult.value;
672 EXPECT_LE(overResultValue, expected + this->template modelcheckingPrecision<BeliefMDPValueType>());
673
674 options.maxExplorationSize = data.model->getNumberOfStates() * data.model->getMaxNrStatesWithSameObservation();
675 auto underCheckResult = checker.checkUnfold(this->env(), *data.propertyInfo, options, precomputedBeliefBounds);
676 underResultValue = underCheckResult.value;
677 EXPECT_GE(underResultValue, expected - this->template modelcheckingPrecision<BeliefMDPValueType>());
678 EXPECT_LE(storm::utility::abs(overResultValue - underResultValue), this->precision())
679 << "Result [" << overResultValue << ", " << underResultValue
680 << "] is not precise enough. If (only) this fails, the result bounds are still correct, but they might be unexpectedly imprecise.\n";
681}
682
683TYPED_TEST(BeliefBasedModelCheckerTest, simple_slippery_Rmax) {
685 typedef typename TestFixture::POMDPValueType POMDPValueType;
686 typedef typename TestFixture::BeliefValueType BeliefValueType;
687 typedef typename TestFixture::BeliefMDPValueType BeliefMDPValueType;
688
689 auto data = this->buildPrism(STORM_TEST_RESOURCES_DIR "/pomdp/simple.prism", "Rmax=? [F s>4 ]", "slippery=0.4");
690 storm::pomdp::beliefs::BeliefBasedModelChecker<storm::models::sparse::Pomdp<POMDPValueType>, BeliefValueType, BeliefMDPValueType> checker(*data.model);
693 precomputedBeliefBounds.preprocessingBounds = preprocessChecker.getValueBounds(this->env(), *data.formula);
695 options.buildChoiceLabeling = false;
697
698 typename TestFixture::ExtendedBeliefMDPValueType overResultValue;
699 typename TestFixture::ExtendedBeliefMDPValueType underResultValue;
700
701 BeliefMDPValueType expected = this->template parseNumber<BeliefMDPValueType>("29/30");
702 auto overCheckResult = checker.checkDiscretize(this->env(), *data.propertyInfo, options, this->overApproxResolution(), true, precomputedBeliefBounds);
703 overResultValue = overCheckResult.value;
704 EXPECT_GE(overResultValue, expected - this->template modelcheckingPrecision<BeliefMDPValueType>());
705
706 options.maxExplorationSize = data.model->getNumberOfStates() * data.model->getMaxNrStatesWithSameObservation();
707 auto underCheckResult = checker.checkUnfold(this->env(), *data.propertyInfo, options, precomputedBeliefBounds);
708 underResultValue = underCheckResult.value;
709 EXPECT_LE(underResultValue, expected + this->template modelcheckingPrecision<BeliefMDPValueType>());
710 EXPECT_LE(storm::utility::abs(overResultValue - underResultValue), this->precision())
711 << "Result [" << underResultValue << ", " << overResultValue
712 << "] is not precise enough. If (only) this fails, the result bounds are still correct, but they might be unexpectedly imprecise.\n";
713}
714
715TYPED_TEST(BeliefBasedModelCheckerTest, simple_slippery_Rmin) {
717 typedef typename TestFixture::POMDPValueType POMDPValueType;
718 typedef typename TestFixture::BeliefValueType BeliefValueType;
719 typedef typename TestFixture::BeliefMDPValueType BeliefMDPValueType;
720
721 auto data = this->buildPrism(STORM_TEST_RESOURCES_DIR "/pomdp/simple.prism", "Rmin=? [F s>4 ]", "slippery=0.4");
722 storm::pomdp::beliefs::BeliefBasedModelChecker<storm::models::sparse::Pomdp<POMDPValueType>, BeliefValueType, BeliefMDPValueType> checker(*data.model);
724
726 precomputedBeliefBounds.preprocessingBounds = preprocessChecker.getValueBounds(this->env(), *data.formula);
728 options.buildChoiceLabeling = false;
730
731 typename TestFixture::ExtendedBeliefMDPValueType overResultValue;
732 typename TestFixture::ExtendedBeliefMDPValueType underResultValue;
733
734 BeliefMDPValueType expected = this->template parseNumber<BeliefMDPValueType>("19/30");
735 auto overCheckResult = checker.checkDiscretize(this->env(), *data.propertyInfo, options, this->overApproxResolution(), true, precomputedBeliefBounds);
736 overResultValue = overCheckResult.value;
737 EXPECT_LE(overResultValue, expected + this->template modelcheckingPrecision<BeliefMDPValueType>());
738
739 options.maxExplorationSize = data.model->getNumberOfStates() * data.model->getMaxNrStatesWithSameObservation();
740 auto underCheckResult = checker.checkUnfold(this->env(), *data.propertyInfo, options, precomputedBeliefBounds);
741 underResultValue = underCheckResult.value;
742 EXPECT_GE(underResultValue, expected - this->template modelcheckingPrecision<BeliefMDPValueType>());
743 EXPECT_LE(storm::utility::abs(overResultValue - underResultValue), this->precision())
744 << "Result [" << overResultValue << ", " << underResultValue
745 << "] is not precise enough. If (only) this fails, the result bounds are still correct, but they might be unexpectedly imprecise.\n";
746}
747
748TYPED_TEST(BeliefBasedModelCheckerTest, maze2_Rmin) {
750 typedef typename TestFixture::POMDPValueType POMDPValueType;
751 typedef typename TestFixture::BeliefValueType BeliefValueType;
752 typedef typename TestFixture::BeliefMDPValueType BeliefMDPValueType;
753
754 auto data = this->buildPrism(STORM_TEST_RESOURCES_DIR "/pomdp/maze2.prism", "Rmin=? [F \"goal\"]", "sl=0");
755 storm::pomdp::beliefs::BeliefBasedModelChecker<storm::models::sparse::Pomdp<POMDPValueType>, BeliefValueType, BeliefMDPValueType> checker(*data.model);
757
759 precomputedBeliefBounds.preprocessingBounds = preprocessChecker.getValueBounds(this->env(), *data.formula);
761 options.buildChoiceLabeling = false;
763
764 typename TestFixture::ExtendedBeliefMDPValueType overResultValue;
765 typename TestFixture::ExtendedBeliefMDPValueType underResultValue;
766
767 BeliefMDPValueType expected = this->template parseNumber<BeliefMDPValueType>("74/91");
768 auto overCheckResult = checker.checkDiscretize(this->env(), *data.propertyInfo, options, this->overApproxResolution(), true, precomputedBeliefBounds);
769 overResultValue = overCheckResult.value;
770 EXPECT_LE(overResultValue, expected + this->template modelcheckingPrecision<BeliefMDPValueType>());
771
772 options.maxExplorationSize = data.model->getNumberOfStates() * data.model->getMaxNrStatesWithSameObservation();
773 auto underCheckResult = checker.checkUnfold(this->env(), *data.propertyInfo, options, precomputedBeliefBounds);
774 underResultValue = underCheckResult.value;
775 EXPECT_GE(underResultValue, expected - this->template modelcheckingPrecision<BeliefMDPValueType>());
776 EXPECT_LE(storm::utility::abs(overResultValue - underResultValue), this->precision())
777 << "Result [" << overResultValue << ", " << underResultValue
778 << "] is not precise enough. If (only) this fails, the result bounds are still correct, but they might be unexpectedly imprecise.\n";
779}
780
781TYPED_TEST(BeliefBasedModelCheckerTest, maze2_Rmax) {
783 typedef typename TestFixture::POMDPValueType POMDPValueType;
784 typedef typename TestFixture::BeliefValueType BeliefValueType;
785 typedef typename TestFixture::BeliefMDPValueType BeliefMDPValueType;
786
787 auto data = this->buildPrism(STORM_TEST_RESOURCES_DIR "/pomdp/maze2.prism", "Rmax=? [F \"goal\"]", "sl=0");
788 storm::pomdp::beliefs::BeliefBasedModelChecker<storm::models::sparse::Pomdp<POMDPValueType>, BeliefValueType, BeliefMDPValueType> checker(*data.model);
790
792 precomputedBeliefBounds.preprocessingBounds = preprocessChecker.getValueBounds(this->env(), *data.formula);
794 options.buildChoiceLabeling = false;
796
797 typename TestFixture::ExtendedBeliefMDPValueType overResultValue;
798 typename TestFixture::ExtendedBeliefMDPValueType underResultValue;
799
800 auto overCheckResult = checker.checkDiscretize(this->env(), *data.propertyInfo, options, this->overApproxResolution(), true, precomputedBeliefBounds);
801 overResultValue = overCheckResult.value;
802 EXPECT_EQ(storm::utility::positiveInfinity<BeliefMDPValueType>(), overResultValue);
803
804 options.maxExplorationSize = data.model->getNumberOfStates() * data.model->getMaxNrStatesWithSameObservation();
805 auto underCheckResult = checker.checkUnfold(this->env(), *data.propertyInfo, options, precomputedBeliefBounds);
806 underResultValue = underCheckResult.value;
807 EXPECT_EQ(storm::utility::positiveInfinity<BeliefMDPValueType>(), underResultValue);
808}
809
810TYPED_TEST(BeliefBasedModelCheckerTest, maze2_slippery_Rmin) {
812 typedef typename TestFixture::POMDPValueType POMDPValueType;
813 typedef typename TestFixture::BeliefValueType BeliefValueType;
814 typedef typename TestFixture::BeliefMDPValueType BeliefMDPValueType;
815
816 auto data = this->buildPrism(STORM_TEST_RESOURCES_DIR "/pomdp/maze2.prism", "Rmin=? [F \"goal\"]", "sl=0.075");
817 storm::pomdp::beliefs::BeliefBasedModelChecker<storm::models::sparse::Pomdp<POMDPValueType>, BeliefValueType, BeliefMDPValueType> checker(*data.model);
819
821 precomputedBeliefBounds.preprocessingBounds = preprocessChecker.getValueBounds(this->env(), *data.formula);
823 options.buildChoiceLabeling = false;
825
826 typename TestFixture::ExtendedBeliefMDPValueType overResultValue;
827 typename TestFixture::ExtendedBeliefMDPValueType underResultValue;
828
829 BeliefMDPValueType expected = this->template parseNumber<BeliefMDPValueType>("80/91");
830 auto overCheckResult = checker.checkDiscretize(this->env(), *data.propertyInfo, options, this->overApproxResolution(), true, precomputedBeliefBounds);
831 overResultValue = overCheckResult.value;
832 EXPECT_LE(overResultValue, expected + this->template modelcheckingPrecision<BeliefMDPValueType>());
833
834 options.maxExplorationSize = data.model->getNumberOfStates() * data.model->getMaxNrStatesWithSameObservation();
835 auto underCheckResult = checker.checkUnfold(this->env(), *data.propertyInfo, options, precomputedBeliefBounds);
836 underResultValue = underCheckResult.value;
837 EXPECT_GE(underResultValue, expected - this->template modelcheckingPrecision<BeliefMDPValueType>());
838 EXPECT_LE(storm::utility::abs(overResultValue - underResultValue), this->precision())
839 << "Result [" << overResultValue << ", " << underResultValue
840 << "] is not precise enough. If (only) this fails, the result bounds are still correct, but they might be unexpectedly imprecise.\n";
841}
842
843TYPED_TEST(BeliefBasedModelCheckerTest, maze2_slippery_Rmax) {
845 typedef typename TestFixture::POMDPValueType POMDPValueType;
846 typedef typename TestFixture::BeliefValueType BeliefValueType;
847 typedef typename TestFixture::BeliefMDPValueType BeliefMDPValueType;
848
849 auto data = this->buildPrism(STORM_TEST_RESOURCES_DIR "/pomdp/maze2.prism", "Rmax=? [F \"goal\"]", "sl=0.075");
850 storm::pomdp::beliefs::BeliefBasedModelChecker<storm::models::sparse::Pomdp<POMDPValueType>, BeliefValueType, BeliefMDPValueType> checker(*data.model);
852
854 precomputedBeliefBounds.preprocessingBounds = preprocessChecker.getValueBounds(this->env(), *data.formula);
856 options.buildChoiceLabeling = false;
858
859 typename TestFixture::ExtendedBeliefMDPValueType overResultValue;
860 typename TestFixture::ExtendedBeliefMDPValueType underResultValue;
861
862 auto overCheckResult = checker.checkDiscretize(this->env(), *data.propertyInfo, options, this->overApproxResolution(), true, precomputedBeliefBounds);
863 overResultValue = overCheckResult.value;
864 EXPECT_EQ(storm::utility::positiveInfinity<BeliefMDPValueType>(), overResultValue);
865
866 options.maxExplorationSize = data.model->getNumberOfStates() * data.model->getMaxNrStatesWithSameObservation();
867 auto underCheckResult = checker.checkUnfold(this->env(), *data.propertyInfo, options, precomputedBeliefBounds);
868 underResultValue = underCheckResult.value;
869 EXPECT_EQ(storm::utility::positiveInfinity<BeliefMDPValueType>(), underResultValue);
870}
871
872TYPED_TEST(BeliefBasedModelCheckerTest, refuel_Pmax) {
874 typedef typename TestFixture::POMDPValueType POMDPValueType;
875 typedef typename TestFixture::BeliefValueType BeliefValueType;
876 typedef typename TestFixture::BeliefMDPValueType BeliefMDPValueType;
877
878 auto data = this->buildPrism(STORM_TEST_RESOURCES_DIR "/pomdp/refuel.prism", "Pmax=?[\"notbad\" U \"goal\"]", "N=4");
879 storm::pomdp::beliefs::BeliefBasedModelChecker<storm::models::sparse::Pomdp<POMDPValueType>, BeliefValueType, BeliefMDPValueType> checker(*data.model);
881
883 precomputedBeliefBounds.preprocessingBounds = preprocessChecker.getValueBounds(this->env(), *data.formula);
885 options.buildChoiceLabeling = false;
887
888 typename TestFixture::ExtendedBeliefMDPValueType overResultValue;
889 typename TestFixture::ExtendedBeliefMDPValueType underResultValue;
890
891 BeliefMDPValueType expected = this->template parseNumber<BeliefMDPValueType>("38/155");
892 auto overCheckResult = checker.checkDiscretize(this->env(), *data.propertyInfo, options, this->overApproxResolution(), true, precomputedBeliefBounds);
893 overResultValue = overCheckResult.value;
894 EXPECT_GE(overResultValue, expected - this->template modelcheckingPrecision<BeliefMDPValueType>());
895
896 options.maxExplorationSize = data.model->getNumberOfStates() * data.model->getMaxNrStatesWithSameObservation();
897 auto underCheckResult = checker.checkUnfold(this->env(), *data.propertyInfo, options, precomputedBeliefBounds);
898 underResultValue = underCheckResult.value;
899 EXPECT_LE(underResultValue, expected + this->template modelcheckingPrecision<BeliefMDPValueType>());
900 EXPECT_LE(storm::utility::abs(overResultValue - underResultValue), this->precision())
901 << "Result [" << underResultValue << ", " << overResultValue
902 << "] is not precise enough. If (only) this fails, the result bounds are still correct, but they might be unexpectedly imprecise.\n";
903}
904
905TYPED_TEST(BeliefBasedModelCheckerTest, refuel_Pmin) {
907 typedef typename TestFixture::POMDPValueType POMDPValueType;
908 typedef typename TestFixture::BeliefValueType BeliefValueType;
909 typedef typename TestFixture::BeliefMDPValueType BeliefMDPValueType;
910
911 auto data = this->buildPrism(STORM_TEST_RESOURCES_DIR "/pomdp/refuel.prism", "Pmin=?[\"notbad\" U \"goal\"]", "N=4");
912 storm::pomdp::beliefs::BeliefBasedModelChecker<storm::models::sparse::Pomdp<POMDPValueType>, BeliefValueType, BeliefMDPValueType> checker(*data.model);
914
916 precomputedBeliefBounds.preprocessingBounds = preprocessChecker.getValueBounds(this->env(), *data.formula);
918 options.buildChoiceLabeling = false;
920
921 typename TestFixture::ExtendedBeliefMDPValueType overResultValue;
922 typename TestFixture::ExtendedBeliefMDPValueType underResultValue;
923
924 BeliefMDPValueType expected = this->template parseNumber<BeliefMDPValueType>("0");
925 auto overCheckResult = checker.checkDiscretize(this->env(), *data.propertyInfo, options, this->overApproxResolution(), true, precomputedBeliefBounds);
926 overResultValue = overCheckResult.value;
927 EXPECT_LE(overResultValue, expected + this->template modelcheckingPrecision<BeliefMDPValueType>());
928
929 options.maxExplorationSize = data.model->getNumberOfStates() * data.model->getMaxNrStatesWithSameObservation();
930 auto underCheckResult = checker.checkUnfold(this->env(), *data.propertyInfo, options, precomputedBeliefBounds);
931 underResultValue = underCheckResult.value;
932 EXPECT_GE(underResultValue, expected - this->template modelcheckingPrecision<BeliefMDPValueType>());
933 EXPECT_LE(storm::utility::abs(overResultValue - underResultValue), this->precision())
934 << "Result [" << overResultValue << ", " << underResultValue
935 << "] is not precise enough. If (only) this fails, the result bounds are still correct, but they might be unexpectedly imprecise.\n";
936}
937
938TYPED_TEST(BeliefBasedModelCheckerTest, reward_bounded_simple_min_max) {
940 typedef typename TestFixture::POMDPValueType POMDPValueType;
941 typedef typename TestFixture::BeliefValueType BeliefValueType;
942 typedef typename TestFixture::BeliefMDPValueType BeliefMDPValueType;
943
944 auto check = [this](std::string const& formula, std::string const& expectedValue) {
945 SCOPED_TRACE(formula);
946 auto data = this->buildPrism(STORM_TEST_RESOURCES_DIR "/pomdp/simple_unit_rewards.prism", formula, "slippery=0");
949 precomputedBeliefBounds.preprocessingBounds.emplace();
950 precomputedBeliefBounds.preprocessingBounds->lower.emplace_back(data.model->getNumberOfStates(), storm::utility::zero<POMDPValueType>());
951 precomputedBeliefBounds.preprocessingBounds->upper.emplace_back(data.model->getNumberOfStates(), storm::utility::one<POMDPValueType>());
952
954 options.buildChoiceLabeling = false;
956 std::vector<std::string> rewardModelNames;
957 for (auto const& rewardBound : data.propertyInfo->rewardBounds) {
958 rewardModelNames.push_back(rewardBound.rewardModelName);
959 }
960 auto const expected = this->template parseNumber<BeliefMDPValueType>(expectedValue);
961 for (bool const discretize : {false, true}) {
962 SCOPED_TRACE(discretize ? "discretize" : "unfold");
963 // Resolution 10 represents the model's (0.7, 0.3) beliefs exactly.
964 auto const result =
965 discretize ? checker.checkRewardAwareDiscretize(this->env(), *data.propertyInfo, options, 10, false, precomputedBeliefBounds, rewardModelNames)
966 : checker.checkRewardAwareUnfold(this->env(), *data.propertyInfo, options, precomputedBeliefBounds, rewardModelNames);
967 EXPECT_TRUE(result.completedExploration);
968 EXPECT_LE(storm::utility::abs(result.value - expected), this->template modelcheckingPrecision<BeliefMDPValueType>());
969 }
970 };
971
972 // The goal is first reachable after three unit rewards; the scheduler chooses between success probabilities 0.3 and 0.7.
973 check("Pmax=? [ true Urew{\"rew\"}<=3 \"goal\" ]", "7/10");
974 check("Pmin=? [ true Urew{\"rew\"}<=3 \"goal\" ]", "3/10");
975 check("Pmax=? [ true Urew{\"rew\"}<=2 \"goal\" ]", "0");
976 check("Pmin=? [ true Urew{\"rew\"}<=2 \"goal\" ]", "0");
977 // Target beliefs must remain explorable: their rewarded self-loop can satisfy a lower bound after the first visit.
978 check("Pmax=? [ true Urew{\"rew\"}>=4 \"goal\" ]", "7/10");
979 check("Pmin=? [ true Urew{\"rew\"}>=4 \"goal\" ]", "3/10");
980}
981
982TYPED_TEST(BeliefBasedModelCheckerTest, reused_explorer_clears_reward_model) {
985 using BeliefMDPValueType = typename TestFixture::BeliefMDPValueType;
991
992 auto data = this->buildPrism(STORM_TEST_RESOURCES_DIR "/pomdp/simple.prism", "Rmax=? [F s>4 ]", "slippery=0");
993 Explorer explorer(*data.model);
994 std::string const rewardModelName;
995 auto const selectRewardModel = [&]() {
996 auto info = explorer.template initializeExploration<StandardInfo>(data.model->getNrObservations());
997 explorer.resumeExploration(info, {}, {}, rewardModelName, storm::OptionalRef<NoAbstraction>{});
998 EXPECT_GT(info.matrix.rows(), 0ul);
999 EXPECT_EQ(info.actionRewards.size(), info.matrix.rows());
1000 };
1001
1002 selectRewardModel();
1003 auto ordinary = explorer.template initializeExploration<StandardInfo>(data.model->getNrObservations());
1004 explorer.resumeExploration(ordinary, {}, {}, storm::NullRef, storm::OptionalRef<NoAbstraction>{});
1005 EXPECT_GT(ordinary.matrix.rows(), 0ul);
1006 EXPECT_TRUE(ordinary.actionRewards.empty());
1007
1008 selectRewardModel();
1009 auto clipping = explorer.template initializeExploration<ClippingInfo>(data.model->getNrObservations());
1010 explorer.resumeClippingExploration(clipping, {}, {}, storm::NullRef, storm::OptionalRef<NoAbstraction>{});
1011 EXPECT_GT(clipping.matrix.rows(), 0ul);
1012 EXPECT_TRUE(clipping.actionRewards.empty());
1013
1014 selectRewardModel();
1016 splitter.setRewardModel();
1017 auto rewardAware = explorer.template initializeExploration<RewardAwareInfo>(data.model->getNrObservations());
1018 explorer.resumeRewardAwareExploration(rewardAware, {}, {}, splitter, storm::OptionalRef<NoAbstraction>{});
1019 EXPECT_GT(rewardAware.matrix.rows(), 0ul);
1020 EXPECT_TRUE(rewardAware.actionRewards.empty());
1021 ASSERT_FALSE(rewardAware.matrix.transitions.empty());
1022 for (auto const& transition : rewardAware.matrix.transitions) {
1023 EXPECT_EQ(transition.data.size(), 1ul);
1024 }
1025}
1026
1027TYPED_TEST(BeliefBasedModelCheckerTest, reward_aware_resume_preserves_observation_indices) {
1030 using BeliefMDPValueType = typename TestFixture::BeliefMDPValueType;
1035
1036 // Different state/action rewards ensure that new reward vectors are encountered after resuming.
1037 auto data = this->buildPrism(STORM_TEST_RESOURCES_DIR "/pomdp/simple.prism", "Rmax=? [F s>4 ]", "slippery=0");
1038 Explorer explorer(*data.model);
1039 Splitter uninterruptedSplitter(*data.model);
1040 uninterruptedSplitter.setRewardModel();
1041 auto uninterrupted = explorer.template initializeExploration<Info>(data.model->getNrObservations(), storm::pomdp::beliefs::ExplorationQueueOrder::FIFO);
1042 explorer.resumeRewardAwareExploration(uninterrupted, {}, {}, uninterruptedSplitter, storm::OptionalRef<NoAbstraction>{});
1043
1044 Splitter resumedSplitter(*data.model);
1045 resumedSplitter.setRewardModel();
1046 auto resumed = explorer.template initializeExploration<Info>(data.model->getNrObservations(), storm::pomdp::beliefs::ExplorationQueueOrder::FIFO);
1047 for (uint64_t steps = 0; resumed.queue.hasNext() && steps < 100; ++steps) {
1048 auto const limit = resumed.exploredBeliefs.size() + 1;
1049 explorer.resumeRewardAwareExploration(
1050 resumed, {}, [&]() { return resumed.exploredBeliefs.size() >= limit; }, resumedSplitter, storm::OptionalRef<NoAbstraction>{});
1051 }
1052 ASSERT_FALSE(resumed.queue.hasNext());
1053 ASSERT_EQ(resumed.discoveredBeliefs.getNumberOfBeliefIds(), uninterrupted.discoveredBeliefs.getNumberOfBeliefIds());
1054 for (uint64_t id = 0; id < uninterrupted.discoveredBeliefs.getNumberOfBeliefIds(); ++id) {
1055 EXPECT_TRUE(resumed.discoveredBeliefs.containsBelief(uninterrupted.discoveredBeliefs.getBeliefFromId(id)));
1056 }
1057 ASSERT_EQ(resumed.matrix.transitions.size(), uninterrupted.matrix.transitions.size());
1058 for (uint64_t i = 0; i < uninterrupted.matrix.transitions.size(); ++i) {
1059 auto const& expected = uninterrupted.matrix.transitions[i];
1060 auto const& actual = resumed.matrix.transitions[i];
1061 EXPECT_EQ(actual.probability, expected.probability);
1062 EXPECT_EQ(actual.data, expected.data);
1063 EXPECT_EQ(resumed.discoveredBeliefs.getBeliefFromId(actual.targetBelief), uninterrupted.discoveredBeliefs.getBeliefFromId(expected.targetBelief));
1064 }
1065}
1066
1067TYPED_TEST(BeliefBasedModelCheckerTest, reward_bounded_requires_explicit_reward_model_name) {
1068 auto const programFile = STORM_TEST_RESOURCES_DIR "/pomdp/simple_unit_rewards.prism";
1069 auto data = this->buildPrism(programFile, "Pmax=? [ true Urew{\"rew\"}<=3 \"goal\" ]", "slippery=0");
1070 auto program = storm::api::parseProgram(programFile).preprocess("slippery=0");
1071 auto unnamedFormula = storm::api::parsePropertiesForPrismProgram("Pmax=? [ true Urew<=3 \"goal\" ]", program).front().getRawFormula();
1072
1073 STORM_SILENT_EXPECT_THROW(storm::pomdp::analysis::getFormulaInformation(*data.model, *unnamedFormula), storm::exceptions::NotSupportedException);
1074
1075 auto unnamedProperty = *data.propertyInfo;
1076 unnamedProperty.rewardBounds.front().rewardModelName.clear();
1078 typename TestFixture::BeliefMDPValueType>
1079 checker(*data.model);
1082 STORM_SILENT_EXPECT_THROW(checker.checkRewardAwareUnfold(this->env(), unnamedProperty, options, bounds, {"rew"}), storm::exceptions::NotSupportedException);
1083}
1084
1085TYPED_TEST(BeliefBasedModelCheckerTest, reward_bounded_requires_matching_reward_model_selection) {
1088 using BeliefMDPValueType = typename TestFixture::BeliefMDPValueType;
1089 auto data = this->buildPrism(STORM_TEST_RESOURCES_DIR "/pomdp/simple_unit_rewards.prism", "Pmax=? [ true Urew{\"rew\"}<=3 \"goal\" ]", "slippery=0");
1093
1094 STORM_SILENT_EXPECT_THROW(checker.checkRewardAwareUnfold(this->env(), *data.propertyInfo, options, bounds, {}),
1095 storm::exceptions::IllegalArgumentException);
1096 STORM_SILENT_EXPECT_THROW(checker.checkRewardAwareDiscretize(this->env(), *data.propertyInfo, options, 10, false, bounds, {}),
1097 storm::exceptions::IllegalArgumentException);
1098 STORM_SILENT_EXPECT_THROW(checker.checkRewardAwareUnfold(this->env(), *data.propertyInfo, options, bounds, {"other"}),
1099 storm::exceptions::IllegalArgumentException);
1100 STORM_SILENT_EXPECT_THROW(checker.checkRewardAwareDiscretize(this->env(), *data.propertyInfo, options, 10, false, bounds, {"other", "rew"}),
1101 storm::exceptions::IllegalArgumentException);
1102
1104 STORM_SILENT_EXPECT_THROW(splitter.setRewardModels({}), storm::exceptions::IllegalArgumentException);
1105}
1106
1107TYPED_TEST(BeliefBasedModelCheckerTest, reward_bounded_allows_additional_observed_reward_model) {
1108 using POMDPValueType = typename TestFixture::POMDPValueType;
1109 using BeliefMDPValueType = typename TestFixture::BeliefMDPValueType;
1111 auto data = this->buildPrism(STORM_TEST_RESOURCES_DIR "/pomdp/simple_unit_rewards.prism", "Pmax=? [ true Urew{\"rew\"}<=3 \"goal\" ]", "slippery=0");
1112 auto extraRewardModel = data.model->getRewardModel("rew");
1113 data.model->addRewardModel("extra", extraRewardModel);
1114
1116 bounds.preprocessingBounds.emplace();
1117 bounds.preprocessingBounds->lower.emplace_back(data.model->getNumberOfStates(), storm::utility::zero<POMDPValueType>());
1118 bounds.preprocessingBounds->upper.emplace_back(data.model->getNumberOfStates(), storm::utility::one<POMDPValueType>());
1121
1122 auto const expected = this->template parseNumber<BeliefMDPValueType>("7/10");
1123 for (bool const discretize : {false, true}) {
1124 auto const result = discretize ? checker.checkRewardAwareDiscretize(this->env(), *data.propertyInfo, options, 10, false, bounds, {"rew", "extra"})
1125 : checker.checkRewardAwareUnfold(this->env(), *data.propertyInfo, options, bounds, {"rew", "extra"});
1126 EXPECT_TRUE(result.completedExploration);
1127 EXPECT_LE(storm::utility::abs(result.value - expected), this->template modelcheckingPrecision<BeliefMDPValueType>());
1128 }
1129}
1130
1131TYPED_TEST(BeliefBasedModelCheckerTest, reward_bounded_simple_early_frontier_uses_cutoff) {
1133 typedef typename TestFixture::POMDPValueType POMDPValueType;
1134 typedef typename TestFixture::BeliefValueType BeliefValueType;
1135 typedef typename TestFixture::BeliefMDPValueType BeliefMDPValueType;
1136
1137 auto check = [this](std::string const& formula, std::string const& frontierValue, std::string const& completeValue) {
1138 SCOPED_TRACE(formula);
1139 auto data = this->buildPrism(STORM_TEST_RESOURCES_DIR "/pomdp/simple_unit_rewards.prism", formula, "slippery=0");
1142 precomputedBeliefBounds.preprocessingBounds.emplace();
1143 precomputedBeliefBounds.preprocessingBounds->lower.emplace_back(data.model->getNumberOfStates(), storm::utility::zero<POMDPValueType>());
1144 precomputedBeliefBounds.preprocessingBounds->upper.emplace_back(data.model->getNumberOfStates(), storm::utility::one<POMDPValueType>());
1145
1147 options.buildChoiceLabeling = false;
1149 options.maxExplorationSize = 1;
1150
1151 std::vector<std::string> rewardModelNames;
1152 for (auto const& rewardBound : data.propertyInfo->rewardBounds) {
1153 rewardModelNames.push_back(rewardBound.rewardModelName);
1154 }
1155 for (bool const discretize : {false, true}) {
1156 SCOPED_TRACE(discretize ? "discretize" : "unfold");
1157 if (discretize) {
1159 checker.checkRewardAwareDiscretize(this->env(), *data.propertyInfo, options, 10, false, precomputedBeliefBounds, rewardModelNames),
1160 storm::exceptions::NotSupportedException);
1161 options.maxExplorationSize.reset();
1162 }
1163 auto const result =
1164 discretize ? checker.checkRewardAwareDiscretize(this->env(), *data.propertyInfo, options, 10, false, precomputedBeliefBounds, rewardModelNames)
1165 : checker.checkRewardAwareUnfold(this->env(), *data.propertyInfo, options, precomputedBeliefBounds, rewardModelNames);
1166 EXPECT_EQ(result.completedExploration, discretize);
1167 if (discretize) {
1168 EXPECT_GT(result.statistics.exploredBeliefs, 1);
1169 } else {
1170 EXPECT_EQ(result.statistics.exploredBeliefs, 1);
1171 EXPECT_GT(result.statistics.discoveredBeliefs, result.statistics.exploredBeliefs);
1172 }
1173 auto const expected = this->template parseNumber<BeliefMDPValueType>(discretize ? completeValue : frontierValue);
1174 EXPECT_LE(storm::utility::abs(result.value - expected), this->template modelcheckingPrecision<BeliefMDPValueType>());
1175 }
1176 };
1177
1178 // With only the initial belief explored, minimization uses the upper cut-off and maximization the lower cut-off.
1179 check("Pmin=? [ true Urew{\"rew\"}<=3 \"goal\" ]", "1", "3/10");
1180 check("Pmax=? [ true Urew{\"rew\"}<=3 \"goal\" ]", "0", "7/10");
1181 // The first transition still costs one reward: even the upper cut-off must fail a zero reward budget.
1182 check("Pmin=? [ true Urew{\"rew\"}<=0 \"goal\" ]", "0", "0");
1183 check("Pmax=? [ true Urew{\"rew\"}<=0 \"goal\" ]", "0", "0");
1184}
1185
1186TYPED_TEST(BeliefBasedModelCheckerTest, reward_bounded_hidden_transition_costs) {
1187 using POMDPValueType = typename TestFixture::POMDPValueType;
1188 using BeliefValueType = typename TestFixture::BeliefValueType;
1189 using BeliefMDPValueType = typename TestFixture::BeliefMDPValueType;
1191
1192 auto check = [this](std::string const& formula, std::string const& expectedValue, bool stopAtTargetFrontier, std::string const& completedValue = "") {
1193 SCOPED_TRACE(formula);
1194 auto data = this->buildPrism(STORM_TEST_RESOURCES_DIR "/pomdp/hidden_transition_rewards.prism", formula);
1197 bounds.preprocessingBounds.emplace();
1198 bounds.preprocessingBounds->lower.emplace_back(data.model->getNumberOfStates(), storm::utility::zero<POMDPValueType>());
1199 bounds.preprocessingBounds->upper.emplace_back(data.model->getNumberOfStates(), storm::utility::one<POMDPValueType>());
1200
1203 if (stopAtTargetFrontier) {
1204 // Explore the draw and finish actions, leaving the target beliefs on the frontier.
1205 options.maxExplorationSize = 2;
1206 }
1207
1208 for (bool const discretize : {false, true}) {
1209 SCOPED_TRACE(discretize ? "discretize" : "unfold");
1210 if (stopAtTargetFrontier && discretize) {
1211 STORM_SILENT_EXPECT_THROW(checker.checkRewardAwareDiscretize(this->env(), *data.propertyInfo, options, 2, false, bounds, {"cost"}),
1212 storm::exceptions::NotSupportedException);
1213 options.maxExplorationSize.reset();
1214 }
1215 auto const result = discretize ? checker.checkRewardAwareDiscretize(this->env(), *data.propertyInfo, options, 2, false, bounds, {"cost"})
1216 : checker.checkRewardAwareUnfold(this->env(), *data.propertyInfo, options, bounds, {"cost"});
1217 EXPECT_EQ(result.completedExploration, discretize || !stopAtTargetFrontier);
1218 if (stopAtTargetFrontier && !discretize) {
1219 EXPECT_EQ(result.statistics.exploredBeliefs, 2);
1220 EXPECT_GT(result.statistics.discoveredBeliefs, result.statistics.exploredBeliefs);
1221 }
1222 auto const expected = this->template parseNumber<BeliefMDPValueType>(discretize && !completedValue.empty() ? completedValue : expectedValue);
1223 EXPECT_LE(storm::utility::abs(result.value - expected), this->template modelcheckingPrecision<BeliefMDPValueType>())
1224 << "actual: " << result.value << ", expected: " << expected;
1225 }
1226 };
1227
1228 // The hidden states have the same observation, but finishing costs one or two rewards respectively.
1229 check("Pmin=? [ true Urew{\"cost\"}<=1 \"goal\" ]", "1/2", false);
1230 check("Pmax=? [ true Urew{\"cost\"}<=1 \"goal\" ]", "1/2", false);
1231 check("Pmin=? [ true Urew{\"cost\"}<=2 \"goal\" ]", "1", false);
1232 check("Pmax=? [ true Urew{\"cost\"}<=2 \"goal\" ]", "1", false);
1233 // A target reached at cost one may keep accumulating reward before satisfying a lower bound.
1234 check("Pmin=? [ true Urew{\"cost\"}>=2 \"goal\" ]", "1", false);
1235 check("Pmax=? [ true Urew{\"cost\"}>=2 \"goal\" ]", "1", false);
1236 // Target beliefs on the frontier still count as targets; the incoming edge cost decides which half succeeds.
1237 check("Pmin=? [ true Urew{\"cost\"}<=1 \"goal\" ]", "1/2", true);
1238 check("Pmax=? [ true Urew{\"cost\"}<=1 \"goal\" ]", "1/2", true);
1239 // The optimistic Pmin sink collects missing reward; Pmax only counts reward already accumulated at the frontier.
1240 check("Pmin=? [ true Urew{\"cost\"}>=2 \"goal\" ]", "1", true);
1241 check("Pmax=? [ true Urew{\"cost\"}>=2 \"goal\" ]", "1/2", true, "1");
1242}
1243
1244TYPED_TEST(BeliefBasedModelCheckerTest, reward_bounded_min_lower_bound_frontier_collects_reward) {
1245 using POMDPValueType = typename TestFixture::POMDPValueType;
1246 using BeliefMDPValueType = typename TestFixture::BeliefMDPValueType;
1248
1249 auto data = this->buildPrism(STORM_TEST_RESOURCES_DIR "/pomdp/hidden_transition_rewards.prism", "Pmin=? [ true Urew{\"cost\"}>=2 \"goal\" ]");
1250 auto extraRewardModel = data.model->getRewardModel("cost");
1251 data.model->addRewardModel("extra", extraRewardModel);
1254 bounds.preprocessingBounds.emplace();
1255 bounds.preprocessingBounds->lower.emplace_back(data.model->getNumberOfStates(), storm::utility::zero<POMDPValueType>());
1256 bounds.preprocessingBounds->upper.emplace_back(data.model->getNumberOfStates(), storm::utility::one<POMDPValueType>());
1257
1258 std::vector<storm::pomdp::beliefs::PropertyInformation> properties{*data.propertyInfo};
1259 auto strictProperty = *data.propertyInfo;
1260 strictProperty.rewardBounds.front().lowerBound = storm::logic::TimeBound(true, data.propertyInfo->rewardBounds.front().lowerBound->getBound());
1261 properties.push_back(strictProperty);
1262 auto zeroBoundData = this->buildPrism(STORM_TEST_RESOURCES_DIR "/pomdp/hidden_transition_rewards.prism", "Pmin=? [ true Urew{\"cost\"}>=0 \"goal\" ]");
1263 properties.push_back(*zeroBoundData.propertyInfo);
1264 auto multidimensionalProperty = *data.propertyInfo;
1265 auto extraLowerBound = strictProperty.rewardBounds.front();
1266 extraLowerBound.rewardModelName = "extra";
1267 multidimensionalProperty.rewardBounds.push_back(extraLowerBound);
1268 properties.push_back(multidimensionalProperty);
1269
1272 for (uint64_t const sizeLimit : {1, 2}) {
1273 SCOPED_TRACE(sizeLimit);
1274 // Size one leaves non-target beliefs; size two leaves target beliefs whose cost-one branch still needs reward.
1275 options.maxExplorationSize = sizeLimit;
1276 for (std::size_t i = 0; i < properties.size(); ++i) {
1277 SCOPED_TRACE(i);
1278 std::vector<std::string> rewardModelNames;
1279 for (auto const& rewardBound : properties[i].rewardBounds) {
1280 rewardModelNames.push_back(rewardBound.rewardModelName);
1281 }
1282 auto const result = checker.checkRewardAwareUnfold(this->env(), properties[i], options, bounds, rewardModelNames);
1283 EXPECT_FALSE(result.completedExploration);
1284 EXPECT_EQ(result.statistics.exploredBeliefs, sizeLimit);
1286 this->template modelcheckingPrecision<BeliefMDPValueType>());
1287 }
1288 }
1289
1290 // The first dimension has only an upper bound and must earn no reward in the synthetic sink.
1291 auto upperBoundData = this->buildPrism(STORM_TEST_RESOURCES_DIR "/pomdp/hidden_transition_rewards.prism", "Pmin=? [ true Urew{\"cost\"}<=1 \"goal\" ]");
1292 auto upperOnlyBound = upperBoundData.propertyInfo->rewardBounds.front();
1293 upperOnlyBound.rewardModelName = "extra";
1294 auto mixedProperty = strictProperty;
1295 mixedProperty.rewardBounds.insert(mixedProperty.rewardBounds.begin(), upperOnlyBound);
1296 options.maxExplorationSize = 2;
1297 auto const mixedResult = checker.checkRewardAwareUnfold(this->env(), mixedProperty, options, bounds, {"extra", "cost"});
1298 EXPECT_FALSE(mixedResult.completedExploration);
1299 auto const mixedExpected = this->template parseNumber<BeliefMDPValueType>("1/2");
1300 EXPECT_LE(storm::utility::abs(mixedResult.value - mixedExpected), this->template modelcheckingPrecision<BeliefMDPValueType>());
1301
1302 // Limits that leave no frontier must still allow the exact value, rather than rejecting the options themselves.
1303 for (auto const criterion :
1305 SCOPED_TRACE(criterion);
1306 options.maxExplorationSize.reset();
1307 options.maxExplorationTime.reset();
1309 options.maxExplorationSize = 1000;
1310 }
1312 options.maxExplorationTime = 3600;
1313 }
1314 auto const result = checker.checkRewardAwareUnfold(this->env(), *data.propertyInfo, options, bounds, {"cost"});
1315 EXPECT_TRUE(result.completedExploration);
1316 EXPECT_LE(storm::utility::abs(result.value - storm::utility::one<BeliefMDPValueType>()), this->template modelcheckingPrecision<BeliefMDPValueType>());
1317 }
1318}
1319
1320TYPED_TEST(BeliefBasedModelCheckerTest, reward_bounded_min_rejects_two_sided_dimensions) {
1321 using POMDPValueType = typename TestFixture::POMDPValueType;
1322 using BeliefMDPValueType = typename TestFixture::BeliefMDPValueType;
1324
1325 auto data = this->buildPrism(STORM_TEST_RESOURCES_DIR "/pomdp/hidden_transition_rewards.prism", "Pmin=? [ true Urew{\"cost\"}>=2 \"goal\" ]");
1326 auto extraRewardModel = data.model->getRewardModel("cost");
1327 data.model->addRewardModel("extra", extraRewardModel);
1329
1330 auto intervalProperty = *data.propertyInfo;
1331 intervalProperty.rewardBounds.front().upperBound = intervalProperty.rewardBounds.front().lowerBound;
1332 auto zeroBoundData = this->buildPrism(STORM_TEST_RESOURCES_DIR "/pomdp/hidden_transition_rewards.prism", "Pmin=? [ true Urew{\"cost\"}>=0 \"goal\" ]");
1333 auto zeroIntervalProperty = intervalProperty;
1334 zeroIntervalProperty.rewardBounds.front().lowerBound = zeroBoundData.propertyInfo->rewardBounds.front().lowerBound;
1335 auto multidimensionalProperty = *data.propertyInfo;
1336 auto extraIntervalBound = intervalProperty.rewardBounds.front();
1337 extraIntervalBound.rewardModelName = "extra";
1338 multidimensionalProperty.rewardBounds.push_back(extraIntervalBound);
1339
1340 // No preprocessing bounds: validation must reject the property before exploration accesses them.
1343 for (auto const& property : {intervalProperty, zeroIntervalProperty, multidimensionalProperty}) {
1344 std::vector<std::string> rewardModelNames;
1345 for (auto const& rewardBound : property.rewardBounds) {
1346 rewardModelNames.push_back(rewardBound.rewardModelName);
1347 }
1348 for (bool const limited : {false, true}) {
1349 SCOPED_TRACE(limited);
1350 options.maxExplorationSize = limited ? std::make_optional<uint64_t>(2) : std::nullopt;
1351 STORM_SILENT_EXPECT_THROW(checker.checkRewardAwareUnfold(this->env(), property, options, bounds, rewardModelNames),
1352 storm::exceptions::NotSupportedException);
1353 STORM_SILENT_EXPECT_THROW(checker.checkRewardAwareDiscretize(this->env(), property, options, 2, false, bounds, rewardModelNames),
1354 storm::exceptions::NotSupportedException);
1355 }
1356 }
1357
1358 // Pmax still reaches the underlying engine, whose existing interval restriction is unchanged.
1359 intervalProperty.dir = storm::OptimizationDirection::Maximize;
1360 options.maxExplorationSize.reset();
1361 bounds.preprocessingBounds.emplace();
1362 bounds.preprocessingBounds->lower.emplace_back(data.model->getNumberOfStates(), storm::utility::zero<POMDPValueType>());
1363 bounds.preprocessingBounds->upper.emplace_back(data.model->getNumberOfStates(), storm::utility::one<POMDPValueType>());
1364 for (bool const discretize : {false, true}) {
1365 SCOPED_TRACE(discretize);
1366 auto check = [&]() {
1367 try {
1368 if (discretize) {
1369 checker.checkRewardAwareDiscretize(this->env(), intervalProperty, options, 2, false, bounds, {"cost"});
1370 } else {
1371 checker.checkRewardAwareUnfold(this->env(), intervalProperty, options, bounds, {"cost"});
1372 }
1373 } catch (storm::exceptions::NotSupportedException const& exception) {
1374 EXPECT_NE(std::string(exception.what()).find("Bounded until formulas are only supported by this method"), std::string::npos);
1375 throw;
1376 }
1377 };
1378 STORM_SILENT_EXPECT_THROW(check(), storm::exceptions::NotSupportedException);
1379 }
1380}
1381
1382#if defined STORM_HAVE_LP_SOLVER
1383TYPED_TEST(BeliefBasedModelCheckerTest, clip_simple_Pmax) {
1385 typedef typename TestFixture::POMDPValueType POMDPValueType;
1386 typedef typename TestFixture::BeliefValueType BeliefValueType;
1387 typedef typename TestFixture::BeliefMDPValueType BeliefMDPValueType;
1388
1389 auto data = this->buildPrism(STORM_TEST_RESOURCES_DIR "/pomdp/simple.prism", "Pmax=? [F \"goal\" ]", "slippery=0");
1392
1394 precomputedBeliefBounds.preprocessingBounds = preprocessChecker.getValueBounds(this->env(), *data.formula);
1395
1397 options.buildChoiceLabeling = false;
1399 options.useClipping = true;
1400 options.clippingResolutions = std::vector<uint64_t>(data.model->getNrObservations(), 2);
1401
1402 typename TestFixture::ExtendedBeliefMDPValueType overResultValue;
1403 typename TestFixture::ExtendedBeliefMDPValueType underResultValue;
1404
1405 auto expected = this->template parseNumber<BeliefMDPValueType>("7/10");
1406
1407 auto overCheckResult = checker.checkDiscretize(this->env(), *data.propertyInfo, options, this->overApproxResolution(), true, precomputedBeliefBounds);
1408 overResultValue = overCheckResult.value;
1409 EXPECT_LE(overResultValue, expected + this->template modelcheckingPrecision<BeliefMDPValueType>());
1410
1411 options.maxExplorationSize = data.model->getNumberOfStates() * data.model->getMaxNrStatesWithSameObservation();
1412 auto underCheckResult = checker.checkUnfold(this->env(), *data.propertyInfo, options, precomputedBeliefBounds);
1413 underResultValue = underCheckResult.value;
1414 EXPECT_LE(underResultValue, expected + this->template modelcheckingPrecision<BeliefMDPValueType>());
1415 EXPECT_LE(storm::utility::abs(overResultValue - underResultValue), this->precision())
1416 << "Result [" << underResultValue << ", " << overResultValue
1417 << "] is not precise enough. If (only) this fails, the result bounds are still correct, but they might be unexpectedly imprecise.\n";
1418}
1419
1420TYPED_TEST(BeliefBasedModelCheckerTest, clip_simple_Pmin) {
1422 typedef typename TestFixture::POMDPValueType POMDPValueType;
1423 typedef typename TestFixture::BeliefValueType BeliefValueType;
1424 typedef typename TestFixture::BeliefMDPValueType BeliefMDPValueType;
1425
1426 auto data = this->buildPrism(STORM_TEST_RESOURCES_DIR "/pomdp/simple.prism", "Pmin=? [F \"goal\" ]", "slippery=0");
1427 storm::pomdp::beliefs::BeliefBasedModelChecker<storm::models::sparse::Pomdp<POMDPValueType>, BeliefValueType, BeliefMDPValueType> checker(*data.model);
1429
1431 precomputedBeliefBounds.preprocessingBounds = preprocessChecker.getValueBounds(this->env(), *data.formula);
1432
1434 options.buildChoiceLabeling = false;
1436 options.useClipping = true;
1437 options.clippingResolutions = std::vector<uint64_t>(data.model->getNrObservations(), 2);
1438
1439 typename TestFixture::ExtendedBeliefMDPValueType overResultValue;
1440 typename TestFixture::ExtendedBeliefMDPValueType underResultValue;
1441
1442 BeliefMDPValueType expected = this->template parseNumber<BeliefMDPValueType>("3/10");
1443
1444 auto overCheckResult = checker.checkDiscretize(this->env(), *data.propertyInfo, options, this->overApproxResolution(), true, precomputedBeliefBounds);
1445 overResultValue = overCheckResult.value;
1446 EXPECT_GE(overResultValue, expected - this->template modelcheckingPrecision<BeliefMDPValueType>());
1447
1448 options.maxExplorationSize = data.model->getNumberOfStates() * data.model->getMaxNrStatesWithSameObservation();
1449 auto underCheckResult = checker.checkUnfold(this->env(), *data.propertyInfo, options, precomputedBeliefBounds);
1450 underResultValue = underCheckResult.value;
1451 EXPECT_GE(underResultValue, expected - this->template modelcheckingPrecision<BeliefMDPValueType>());
1452 EXPECT_LE(storm::utility::abs(overResultValue - underResultValue), this->precision())
1453 << "Result [" << overResultValue << ", " << underResultValue
1454 << "] is not precise enough. If (only) this fails, the result bounds are still correct, but they might be unexpectedly imprecise.\n";
1455}
1456
1457TYPED_TEST(BeliefBasedModelCheckerTest, clip_simple_Pmin_early_clipping_is_sound) {
1459 typedef typename TestFixture::POMDPValueType POMDPValueType;
1460 typedef typename TestFixture::BeliefValueType BeliefValueType;
1461 typedef typename TestFixture::BeliefMDPValueType BeliefMDPValueType;
1462
1463 auto data = this->buildPrism(STORM_TEST_RESOURCES_DIR "/pomdp/simple.prism", "Pmin=? [F \"goal\" ]", "slippery=0");
1466
1468 precomputedBeliefBounds.preprocessingBounds = preprocessChecker.getValueBounds(this->env(), *data.formula);
1469
1472 options.maxExplorationSize = 1;
1473 options.useClipping = true;
1474 options.clippingResolutions = std::vector<uint64_t>(data.model->getNrObservations(), 2);
1475
1476 auto underResult = checker.checkUnfold(this->env(), *data.propertyInfo, options, precomputedBeliefBounds);
1477 auto expected = this->template parseNumber<BeliefMDPValueType>("3/10");
1478 EXPECT_GE(underResult.value, expected - this->template modelcheckingPrecision<BeliefMDPValueType>());
1479}
1480
1481TYPED_TEST(BeliefBasedModelCheckerTest, clip_simple_slippery_Pmax) {
1483 typedef typename TestFixture::POMDPValueType POMDPValueType;
1484 typedef typename TestFixture::BeliefValueType BeliefValueType;
1485 typedef typename TestFixture::BeliefMDPValueType BeliefMDPValueType;
1486
1487 auto data = this->buildPrism(STORM_TEST_RESOURCES_DIR "/pomdp/simple.prism", "Pmax=? [F \"goal\" ]", "slippery=0.4");
1488 storm::pomdp::beliefs::BeliefBasedModelChecker<storm::models::sparse::Pomdp<POMDPValueType>, BeliefValueType, BeliefMDPValueType> checker(*data.model);
1490
1492 precomputedBeliefBounds.preprocessingBounds = preprocessChecker.getValueBounds(this->env(), *data.formula);
1493
1495 options.buildChoiceLabeling = false;
1497 options.useClipping = true;
1498 options.clippingResolutions = std::vector<uint64_t>(data.model->getNrObservations(), 2);
1499
1500 typename TestFixture::ExtendedBeliefMDPValueType underResultValue;
1501
1502 BeliefMDPValueType expected = this->template parseNumber<BeliefMDPValueType>("7/10");
1503
1504 options.maxExplorationSize = data.model->getNumberOfStates() * data.model->getMaxNrStatesWithSameObservation();
1505
1506 auto underCheckResult = checker.checkUnfold(this->env(), *data.propertyInfo, options, precomputedBeliefBounds);
1507 underResultValue = underCheckResult.value;
1508 EXPECT_LE(underResultValue, expected + this->template modelcheckingPrecision<BeliefMDPValueType>());
1509}
1510
1511TYPED_TEST(BeliefBasedModelCheckerTest, clip_simple_slippery_Pmin) {
1513 typedef typename TestFixture::POMDPValueType POMDPValueType;
1514 typedef typename TestFixture::BeliefValueType BeliefValueType;
1515 typedef typename TestFixture::BeliefMDPValueType BeliefMDPValueType;
1516
1517 auto data = this->buildPrism(STORM_TEST_RESOURCES_DIR "/pomdp/simple.prism", "Pmin=? [F \"goal\" ]", "slippery=0.4");
1518 storm::pomdp::beliefs::BeliefBasedModelChecker<storm::models::sparse::Pomdp<POMDPValueType>, BeliefValueType, BeliefMDPValueType> checker(*data.model);
1520
1522 precomputedBeliefBounds.preprocessingBounds = preprocessChecker.getValueBounds(this->env(), *data.formula);
1524 options.buildChoiceLabeling = false;
1526 options.useClipping = true;
1527 options.clippingResolutions = std::vector<uint64_t>(data.model->getNrObservations(), 2);
1528
1529 typename TestFixture::ExtendedBeliefMDPValueType overResultValue;
1530 typename TestFixture::ExtendedBeliefMDPValueType underResultValue;
1531
1532 POMDPValueType expected = this->template parseNumber<BeliefMDPValueType>("3/10");
1533 auto overCheckResult = checker.checkDiscretize(this->env(), *data.propertyInfo, options, this->overApproxResolution(), true, precomputedBeliefBounds);
1534 overResultValue = overCheckResult.value;
1535
1536 options.maxExplorationSize = data.model->getNumberOfStates() * data.model->getMaxNrStatesWithSameObservation();
1537 auto underCheckResult = checker.checkUnfold(this->env(), *data.propertyInfo, options, precomputedBeliefBounds);
1538 underResultValue = underCheckResult.value;
1539 if (this->isExact()) {
1540 // This model's value can only be approximated arbitrarily close but never reached
1541 // Exact arithmetics will thus not reach the value with absoulute precision either.
1542 POMDPValueType approxPrecision = storm::utility::convertNumber<POMDPValueType>(1e-5);
1543 EXPECT_GE(underResultValue, expected - approxPrecision);
1544 EXPECT_LE(overResultValue, expected + approxPrecision);
1545 } else {
1546 EXPECT_GE(underResultValue, expected - this->template modelcheckingPrecision<BeliefMDPValueType>());
1547 EXPECT_LE(overResultValue, expected + this->template modelcheckingPrecision<BeliefMDPValueType>());
1548 }
1549 EXPECT_LE(storm::utility::abs(overResultValue - underResultValue), this->precision())
1550 << "Result [" << underResultValue << ", " << overResultValue
1551 << "] is not precise enough. If (only) this fails, the result bounds are still correct, but they might be unexpectedly imprecise.\n";
1552}
1553
1554TYPED_TEST(BeliefBasedModelCheckerTest, clip_simple_Rmax) {
1556 typedef typename TestFixture::POMDPValueType POMDPValueType;
1557 typedef typename TestFixture::BeliefValueType BeliefValueType;
1558 typedef typename TestFixture::BeliefMDPValueType BeliefMDPValueType;
1559
1560 auto data = this->buildPrism(STORM_TEST_RESOURCES_DIR "/pomdp/simple.prism", "Rmax=? [F s>4 ]", "slippery=0");
1561 storm::pomdp::beliefs::BeliefBasedModelChecker<storm::models::sparse::Pomdp<POMDPValueType>, BeliefValueType, BeliefMDPValueType> checker(*data.model);
1563
1565 precomputedBeliefBounds.preprocessingBounds = preprocessChecker.getValueBounds(this->env(), *data.formula);
1566 precomputedBeliefBounds.extremeBounds = preprocessChecker.getExtremeValueBound(this->env(), *data.formula);
1567
1569 options.buildChoiceLabeling = false;
1571 options.useClipping = true;
1572 options.clippingResolutions = std::vector<uint64_t>(data.model->getNrObservations(), 2);
1573
1574 typename TestFixture::ExtendedBeliefMDPValueType underResultValue;
1575
1576 BeliefMDPValueType expected = this->template parseNumber<BeliefMDPValueType>("29/50");
1577
1578 options.maxExplorationSize = data.model->getNumberOfStates() * data.model->getMaxNrStatesWithSameObservation();
1579 auto underCheckResult = checker.checkUnfold(this->env(), *data.propertyInfo, options, precomputedBeliefBounds);
1580 underResultValue = underCheckResult.value;
1581 EXPECT_LE(underResultValue, expected + this->template modelcheckingPrecision<BeliefMDPValueType>());
1582}
1583
1584TYPED_TEST(BeliefBasedModelCheckerTest, clip_simple_Rmin) {
1586 typedef typename TestFixture::POMDPValueType POMDPValueType;
1587 typedef typename TestFixture::BeliefValueType BeliefValueType;
1588 typedef typename TestFixture::BeliefMDPValueType BeliefMDPValueType;
1589
1590 auto data = this->buildPrism(STORM_TEST_RESOURCES_DIR "/pomdp/simple.prism", "Rmin=? [F s>4 ]", "slippery=0");
1591 storm::pomdp::beliefs::BeliefBasedModelChecker<storm::models::sparse::Pomdp<POMDPValueType>, BeliefValueType, BeliefMDPValueType> checker(*data.model);
1593
1595 precomputedBeliefBounds.preprocessingBounds = preprocessChecker.getValueBounds(this->env(), *data.formula);
1596 precomputedBeliefBounds.extremeBounds = preprocessChecker.getExtremeValueBound(this->env(), *data.formula);
1597
1599 options.buildChoiceLabeling = false;
1601 options.useClipping = true;
1602 options.clippingResolutions = std::vector<uint64_t>(data.model->getNrObservations(), 2);
1603
1604 typename TestFixture::ExtendedBeliefMDPValueType underResultValue;
1605
1606 BeliefMDPValueType expected = this->template parseNumber<BeliefMDPValueType>("19/50");
1607
1608 options.maxExplorationSize = data.model->getNumberOfStates() * data.model->getMaxNrStatesWithSameObservation();
1609 auto underCheckResult = checker.checkUnfold(this->env(), *data.propertyInfo, options, precomputedBeliefBounds);
1610 underResultValue = underCheckResult.value;
1611 EXPECT_GE(underResultValue, expected - this->template modelcheckingPrecision<BeliefMDPValueType>());
1612}
1613
1614TYPED_TEST(BeliefBasedModelCheckerTest, clip_simple_slippery_Rmax) {
1616 typedef typename TestFixture::POMDPValueType POMDPValueType;
1617 typedef typename TestFixture::BeliefValueType BeliefValueType;
1618 typedef typename TestFixture::BeliefMDPValueType BeliefMDPValueType;
1619
1620 auto data = this->buildPrism(STORM_TEST_RESOURCES_DIR "/pomdp/simple.prism", "Rmax=? [F s>4 ]", "slippery=0.4");
1621 storm::pomdp::beliefs::BeliefBasedModelChecker<storm::models::sparse::Pomdp<POMDPValueType>, BeliefValueType, BeliefMDPValueType> checker(*data.model);
1623
1625 precomputedBeliefBounds.preprocessingBounds = preprocessChecker.getValueBounds(this->env(), *data.formula);
1626 precomputedBeliefBounds.extremeBounds = preprocessChecker.getExtremeValueBound(this->env(), *data.formula);
1627
1629 options.buildChoiceLabeling = false;
1631 options.useClipping = true;
1632 options.clippingResolutions = std::vector<uint64_t>(data.model->getNrObservations(), 2);
1633
1634 typename TestFixture::ExtendedBeliefMDPValueType underResultValue;
1635
1636 BeliefMDPValueType expected = this->template parseNumber<BeliefMDPValueType>("29/30");
1637
1638 options.maxExplorationSize = data.model->getNumberOfStates() * data.model->getMaxNrStatesWithSameObservation();
1639 auto underCheckResult = checker.checkUnfold(this->env(), *data.propertyInfo, options, precomputedBeliefBounds);
1640 underResultValue = underCheckResult.value;
1641 EXPECT_LE(underResultValue, expected + this->template modelcheckingPrecision<BeliefMDPValueType>());
1642}
1643
1644TYPED_TEST(BeliefBasedModelCheckerTest, clip_simple_slippery_Rmin) {
1646 typedef typename TestFixture::POMDPValueType POMDPValueType;
1647 typedef typename TestFixture::BeliefValueType BeliefValueType;
1648 typedef typename TestFixture::BeliefMDPValueType BeliefMDPValueType;
1649
1650 auto data = this->buildPrism(STORM_TEST_RESOURCES_DIR "/pomdp/simple.prism", "Rmin=? [F s>4 ]", "slippery=0.4");
1651 storm::pomdp::beliefs::BeliefBasedModelChecker<storm::models::sparse::Pomdp<POMDPValueType>, BeliefValueType, BeliefMDPValueType> checker(*data.model);
1653
1655 precomputedBeliefBounds.preprocessingBounds = preprocessChecker.getValueBounds(this->env(), *data.formula);
1656 precomputedBeliefBounds.extremeBounds = preprocessChecker.getExtremeValueBound(this->env(), *data.formula);
1657
1659 options.buildChoiceLabeling = false;
1661 options.useClipping = true;
1662 options.clippingResolutions = std::vector<uint64_t>(data.model->getNrObservations(), 2);
1663
1664 typename TestFixture::ExtendedBeliefMDPValueType underResultValue;
1665
1666 BeliefMDPValueType expected = this->template parseNumber<BeliefMDPValueType>("19/30");
1667
1668 options.maxExplorationSize = data.model->getNumberOfStates() * data.model->getMaxNrStatesWithSameObservation();
1669 auto underCheckResult = checker.checkUnfold(this->env(), *data.propertyInfo, options, precomputedBeliefBounds);
1670 underResultValue = underCheckResult.value;
1671 EXPECT_GE(underResultValue, expected - this->template modelcheckingPrecision<BeliefMDPValueType>());
1672}
1673
1674TYPED_TEST(BeliefBasedModelCheckerTest, clip_maze2_Rmin) {
1676 typedef typename TestFixture::POMDPValueType POMDPValueType;
1677 typedef typename TestFixture::BeliefValueType BeliefValueType;
1678 typedef typename TestFixture::BeliefMDPValueType BeliefMDPValueType;
1679
1680 auto data = this->buildPrism(STORM_TEST_RESOURCES_DIR "/pomdp/maze2.prism", "Rmin=? [F \"goal\"]", "sl=0");
1681 storm::pomdp::beliefs::BeliefBasedModelChecker<storm::models::sparse::Pomdp<POMDPValueType>, BeliefValueType, BeliefMDPValueType> checker(*data.model);
1683
1685 precomputedBeliefBounds.preprocessingBounds = preprocessChecker.getValueBounds(this->env(), *data.formula);
1686 precomputedBeliefBounds.extremeBounds = preprocessChecker.getExtremeValueBound(this->env(), *data.formula);
1687
1689 options.buildChoiceLabeling = false;
1691 options.useClipping = true;
1692 options.clippingResolutions = std::vector<uint64_t>(data.model->getNrObservations(), 2);
1693
1694 typename TestFixture::ExtendedBeliefMDPValueType underResultValue;
1695
1696 BeliefMDPValueType expected = this->template parseNumber<BeliefMDPValueType>("74/91");
1697 options.maxExplorationSize = data.model->getNumberOfStates() * data.model->getMaxNrStatesWithSameObservation();
1698 auto underCheckResult = checker.checkUnfold(this->env(), *data.propertyInfo, options, precomputedBeliefBounds);
1699 underResultValue = underCheckResult.value;
1700 EXPECT_GE(underResultValue, expected - this->template modelcheckingPrecision<BeliefMDPValueType>());
1701}
1702
1703TYPED_TEST(BeliefBasedModelCheckerTest, clip_maze2_Rmax) {
1705 typedef typename TestFixture::POMDPValueType POMDPValueType;
1706 typedef typename TestFixture::BeliefValueType BeliefValueType;
1707 typedef typename TestFixture::BeliefMDPValueType BeliefMDPValueType;
1708
1709 auto data = this->buildPrism(STORM_TEST_RESOURCES_DIR "/pomdp/maze2.prism", "Rmax=? [F \"goal\"]", "sl=0");
1710 storm::pomdp::beliefs::BeliefBasedModelChecker<storm::models::sparse::Pomdp<POMDPValueType>, BeliefValueType, BeliefMDPValueType> checker(*data.model);
1712
1714 precomputedBeliefBounds.preprocessingBounds = preprocessChecker.getValueBounds(this->env(), *data.formula);
1715 precomputedBeliefBounds.extremeBounds = preprocessChecker.getExtremeValueBound(this->env(), *data.formula);
1716
1718 options.buildChoiceLabeling = false;
1720 options.useClipping = true;
1721 options.clippingResolutions = std::vector<uint64_t>(data.model->getNrObservations(), 2);
1722
1723 typename TestFixture::ExtendedBeliefMDPValueType underResultValue;
1724
1725 options.maxExplorationSize = data.model->getNumberOfStates() * data.model->getMaxNrStatesWithSameObservation();
1726 auto underCheckResult = checker.checkUnfold(this->env(), *data.propertyInfo, options, precomputedBeliefBounds);
1727 underResultValue = underCheckResult.value;
1728 EXPECT_EQ(storm::utility::positiveInfinity<BeliefMDPValueType>(), underResultValue);
1729}
1730
1731TYPED_TEST(BeliefBasedModelCheckerTest, clip_maze2_slippery_Rmin) {
1733 typedef typename TestFixture::POMDPValueType POMDPValueType;
1734 typedef typename TestFixture::BeliefValueType BeliefValueType;
1735 typedef typename TestFixture::BeliefMDPValueType BeliefMDPValueType;
1736
1737 auto data = this->buildPrism(STORM_TEST_RESOURCES_DIR "/pomdp/maze2.prism", "Rmin=? [F \"goal\"]", "sl=0.075");
1738 storm::pomdp::beliefs::BeliefBasedModelChecker<storm::models::sparse::Pomdp<POMDPValueType>, BeliefValueType, BeliefMDPValueType> checker(*data.model);
1740
1742 precomputedBeliefBounds.preprocessingBounds = preprocessChecker.getValueBounds(this->env(), *data.formula);
1743 precomputedBeliefBounds.extremeBounds = preprocessChecker.getExtremeValueBound(this->env(), *data.formula);
1744
1746 options.buildChoiceLabeling = false;
1748 options.useClipping = true;
1749 options.clippingResolutions = std::vector<uint64_t>(data.model->getNrObservations(), 2);
1750
1751 typename TestFixture::ExtendedBeliefMDPValueType underResultValue;
1752
1753 BeliefMDPValueType expected = this->template parseNumber<BeliefMDPValueType>("80/91");
1754
1755 options.maxExplorationSize = data.model->getNumberOfStates() * data.model->getMaxNrStatesWithSameObservation();
1756 auto underCheckResult = checker.checkUnfold(this->env(), *data.propertyInfo, options, precomputedBeliefBounds);
1757 underResultValue = underCheckResult.value;
1758 EXPECT_GE(underResultValue, expected - this->template modelcheckingPrecision<BeliefMDPValueType>());
1759}
1760
1761TYPED_TEST(BeliefBasedModelCheckerTest, clip_maze2_slippery_Rmax) {
1763 typedef typename TestFixture::POMDPValueType POMDPValueType;
1764 typedef typename TestFixture::BeliefValueType BeliefValueType;
1765 typedef typename TestFixture::BeliefMDPValueType BeliefMDPValueType;
1766
1767 auto data = this->buildPrism(STORM_TEST_RESOURCES_DIR "/pomdp/maze2.prism", "Rmax=? [F \"goal\"]", "sl=0.075");
1768 storm::pomdp::beliefs::BeliefBasedModelChecker<storm::models::sparse::Pomdp<POMDPValueType>, BeliefValueType, BeliefMDPValueType> checker(*data.model);
1770
1772 precomputedBeliefBounds.preprocessingBounds = preprocessChecker.getValueBounds(this->env(), *data.formula);
1773 precomputedBeliefBounds.extremeBounds = preprocessChecker.getExtremeValueBound(this->env(), *data.formula);
1774
1776 options.buildChoiceLabeling = false;
1778 options.useClipping = true;
1779 options.clippingResolutions = std::vector<uint64_t>(data.model->getNrObservations(), 2);
1780
1781 typename TestFixture::ExtendedBeliefMDPValueType underResultValue;
1782
1783 options.maxExplorationSize = data.model->getNumberOfStates() * data.model->getMaxNrStatesWithSameObservation();
1784 auto underCheckResult = checker.checkUnfold(this->env(), *data.propertyInfo, options, precomputedBeliefBounds);
1785 underResultValue = underCheckResult.value;
1786 EXPECT_EQ(storm::utility::positiveInfinity<BeliefMDPValueType>(), underResultValue);
1787}
1788
1789TYPED_TEST(BeliefBasedModelCheckerTest, clip_refuel_Pmax) {
1791 typedef typename TestFixture::POMDPValueType POMDPValueType;
1792 typedef typename TestFixture::BeliefValueType BeliefValueType;
1793 typedef typename TestFixture::BeliefMDPValueType BeliefMDPValueType;
1794
1795 auto data = this->buildPrism(STORM_TEST_RESOURCES_DIR "/pomdp/refuel.prism", "Pmax=?[\"notbad\" U \"goal\"]", "N=4");
1796 storm::pomdp::beliefs::BeliefBasedModelChecker<storm::models::sparse::Pomdp<POMDPValueType>, BeliefValueType, BeliefMDPValueType> checker(*data.model);
1798
1800 precomputedBeliefBounds.preprocessingBounds = preprocessChecker.getValueBounds(this->env(), *data.formula);
1801
1803 options.buildChoiceLabeling = false;
1805 options.useClipping = true;
1806 options.clippingResolutions = std::vector<uint64_t>(data.model->getNrObservations(), 2);
1807
1808 typename TestFixture::ExtendedBeliefMDPValueType underResultValue;
1809
1810 BeliefMDPValueType expected = this->template parseNumber<BeliefMDPValueType>("38/155");
1811
1812 options.maxExplorationSize = data.model->getNumberOfStates() * data.model->getMaxNrStatesWithSameObservation();
1813 auto underCheckResult = checker.checkUnfold(this->env(), *data.propertyInfo, options, precomputedBeliefBounds);
1814 underResultValue = underCheckResult.value;
1815 EXPECT_LE(underResultValue, expected + this->template modelcheckingPrecision<BeliefMDPValueType>());
1816}
1817
1818TYPED_TEST(BeliefBasedModelCheckerTest, clip_refuel_Pmin) {
1820 typedef typename TestFixture::POMDPValueType POMDPValueType;
1821 typedef typename TestFixture::BeliefValueType BeliefValueType;
1822 typedef typename TestFixture::BeliefMDPValueType BeliefMDPValueType;
1823
1824 auto data = this->buildPrism(STORM_TEST_RESOURCES_DIR "/pomdp/refuel.prism", "Pmin=?[\"notbad\" U \"goal\"]", "N=4");
1825 storm::pomdp::beliefs::BeliefBasedModelChecker<storm::models::sparse::Pomdp<POMDPValueType>, BeliefValueType, BeliefMDPValueType> checker(*data.model);
1827
1829 precomputedBeliefBounds.preprocessingBounds = preprocessChecker.getValueBounds(this->env(), *data.formula);
1830
1832 options.buildChoiceLabeling = false;
1834 options.useClipping = true;
1835 options.clippingResolutions = std::vector<uint64_t>(data.model->getNrObservations(), 2);
1836
1837 typename TestFixture::ExtendedBeliefMDPValueType underResultValue;
1838
1839 BeliefMDPValueType expected = this->template parseNumber<BeliefMDPValueType>("0");
1840
1841 options.maxExplorationSize = data.model->getNumberOfStates() * data.model->getMaxNrStatesWithSameObservation();
1842 auto underCheckResult = checker.checkUnfold(this->env(), *data.propertyInfo, options, precomputedBeliefBounds);
1843 underResultValue = underCheckResult.value;
1844 EXPECT_GE(underResultValue, expected - this->template modelcheckingPrecision<BeliefMDPValueType>());
1845}
1846
1847#endif // defined STORM_HAVE_LP_SOLVER
1848
1849} // namespace
SolverEnvironment & solver()
void setPrecision(storm::RationalNumber value)
void setMethod(storm::solver::MinMaxMethod value, bool isSetFromDefault=false)
Helper class that optionally holds a reference to an object of type T.
Definition OptionalRef.h:48
MinMaxSolverEnvironment & minMax()
This class represents a partially observable Markov decision process.
Definition Pomdp.h:13
Builds and checks a finite belief MDP approximation of a POMDP.
Class to perform belief exploration.
Represents a belief of a Pomdp, i.e.
Definition Belief.h:21
Abstracts a given belief by splitting it into sub-beliefs based on the (state-action) reward of the P...
std::shared_ptr< storm::models::sparse::Pomdp< ValueType > > transform(storm::models::sparse::Pomdp< ValueType > const &pomdp, storm::storage::BitVector &prob0States, storm::storage::BitVector &prob1States)
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
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:39
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
SFTBDDChecker::ValueType ValueType
helper::MDPSparseModelCheckingHelperReturnType< ValueType > check(Environment const &env, SparseModelType const &model, CheckTask< storm::logic::MultiObjectiveFormula, ValueType > const &checkTask, CheckFormulaCallback const &formulaChecker)
check a lexicographic LTL-formula
NumberType parseNumber(std::string const &value)
Parse number from string.
FormulaInformation getFormulaInformation(PomdpType const &pomdp, storm::logic::ProbabilityOperatorFormula const &formula)
constexpr NoAbstractionType NoAbstraction
ExplorationInformation< BeliefMdpValueType, BeliefType, std::optional< BeliefMdpValueType >, std::optional< BeliefMdpValueType > > ClippingExplorationInformation
Exploration information that stores optional clipping probability and reward-adjustment metadata.
ExplorationInformation< BeliefMdpValueType, BeliefType > StandardExplorationInformation
Exploration information without transition metadata.
ExplorationInformation< BeliefMdpValueType, BeliefType, std::vector< BeliefMdpValueType > > RewardAwareExplorationInformation
Exploration information that stores a reward vector on each transition.
bool constexpr maximize(OptimizationDirection d)
storm::storage::BitVector getReachableStates(storm::storage::SparseMatrix< T > const &transitionMatrix, storm::storage::BitVector const &initialStates, storm::storage::BitVector const &constraintStates, storm::storage::BitVector const &targetStates, bool useStepBound, uint_fast64_t maximalSteps, boost::optional< storm::storage::BitVector > const &choiceFilter)
Performs a forward depth-first search through the underlying graph structure to identify the states t...
Definition graph.cpp:42
ExtendedValueType< ValueType > positiveInfinity()
ValueType abs(ValueType const &number)
ValueType zero()
Definition constants.cpp:26
std::conditional_t< storm::NumberTraits< ValueType >::HasInfinity, ValueType, ExtendedNumber< ValueType > > ExtendedValueType
The type to use for values that may be infinite.
ValueType one()
Definition constants.cpp:21
TargetType convertNumber(SourceType const &number)
constexpr NullRefType NullRef
Definition OptionalRef.h:31
TYPED_TEST(GraphTestAR, SymbolicProb01StochasticGameDieSmall)
Definition GraphTest.cpp:64
TYPED_TEST_SUITE(GraphTestAR, TestingTypes,)
::testing::Types< Cudd, Sylvan > TestingTypes
Definition GraphTest.cpp:61
#define STORM_SILENT_EXPECT_THROW(statement, expected_exception)
Definition storm_gtest.h:21
bool cutZeroGap
Cut non-target beliefs only when their preprocessing lower and upper bounds have exactly zero gap.
Auxiliary struct used to identify cases where no abstraction of beliefs shall be applied.
Struct for storing precomputed values bounding the actual values on the POMDP.
std::optional< ExtremePOMDPValueBound< POMDPValueType > > extremeBounds
std::optional< PreprocessingPomdpValueBounds< POMDPValueType > > preprocessingBounds
std::unordered_map< std::string, RewardModelType > rewardModels
storm::storage::SparseMatrix< ValueType > transitionMatrix
std::optional< storm::storage::sparse::Valuations > observationValuations
std::optional< storm::models::sparse::ChoiceLabeling > choiceLabeling
storm::models::sparse::StateLabeling stateLabeling
std::optional< std::vector< uint32_t > > observabilityClasses